Compare commits

..
Author SHA1 Message Date
Dominik Pschenitschni b7665b95cd wip: move to bulma 1.0 2024-11-02 19:32:04 +01:00
60 changed files with 1489 additions and 1808 deletions
+10 -8
View File
@@ -626,7 +626,7 @@ steps:
depends_on: [ generate-config-yaml ]
- name: before-static-build
image: ghcr.io/techknowlogick/xgo:go-1.23.x
image: techknowlogick/xgo:go-1.22.x
pull: always
commands:
- export PATH=$PATH:$GOPATH/bin
@@ -635,7 +635,7 @@ steps:
depends_on: [ fetch-tags, mage ]
- name: static-build-windows
image: ghcr.io/techknowlogick/xgo:go-1.23.x
image: techknowlogick/xgo:go-1.22.x
pull: always
environment:
# This path does not exist. However, when we set the gopath to /go, the build fails. Not sure why.
@@ -651,7 +651,7 @@ steps:
- frontend-build
- name: static-build-linux
image: ghcr.io/techknowlogick/xgo:go-1.23.x
image: techknowlogick/xgo:go-1.22.x
pull: always
environment:
# This path does not exist. However, when we set the gopath to /go, the build fails. Not sure why.
@@ -667,7 +667,7 @@ steps:
- frontend-build
- name: static-build-darwin
image: ghcr.io/techknowlogick/xgo:go-1.23.x
image: techknowlogick/xgo:go-1.22.x
pull: always
environment:
# This path does not exist. However, when we set the gopath to /go, the build fails. Not sure why.
@@ -693,7 +693,7 @@ steps:
- ./mage-static release:compress
- name: after-build-static
image: ghcr.io/techknowlogick/xgo:go-1.23.x
image: techknowlogick/xgo:go-1.22.x
pull: always
depends_on:
- after-build-compress
@@ -884,6 +884,7 @@ steps:
repo: vikunja/vikunja
tags: unstable
platforms:
- linux/386
- linux/amd64
- linux/arm/v6
- linux/arm/v7
@@ -915,6 +916,7 @@ steps:
from_secret: docker_password
repo: vikunja/vikunja
platforms:
- linux/386
- linux/amd64
- linux/arm/v6
- linux/arm/v7
@@ -1148,7 +1150,7 @@ steps:
# - '.cache'
- name: build
image: electronuserland/builder:wine
image: electronuserland/builder:wine-mono
pull: true
environment:
PNPM_CACHE_FOLDER: .cache/pnpm
@@ -1230,7 +1232,7 @@ steps:
# - '.cache'
- name: build
image: electronuserland/builder:wine
image: electronuserland/builder:wine-mono
pull: true
environment:
PNPM_CACHE_FOLDER: .cache/pnpm
@@ -1398,6 +1400,6 @@ steps:
- failure
---
kind: signature
hmac: 7fc1cb2b0cfdc84d7eda25ce75dd56446fc01060c03af89c91043f095e92a2b3
hmac: 75759b487ff5f815f3ca3d77e824f7d1d70a9c07d03967f62a00217f421a81d0
...
+1 -1
View File
@@ -13,7 +13,7 @@ RUN corepack enable && \
pnpm install && \
pnpm run build
FROM --platform=$BUILDPLATFORM ghcr.io/techknowlogick/xgo:go-1.23.x AS apibuilder
FROM --platform=$BUILDPLATFORM techknowlogick/xgo:go-1.22.x AS apibuilder
RUN go install github.com/magefile/mage@latest && \
mv /go/bin/mage /usr/local/go/bin
+1 -1
View File
@@ -17,7 +17,6 @@ body = """
{% raw %} {% endraw %}- **BREAKING**: {{commit.breaking_description}}
{%- endif -%}
{%- endfor -%}
{% raw %}\n{% endraw %}\
{%- for commit in commits %}
{%- if commit.scope -%}
{% else -%}
@@ -57,3 +56,4 @@ commit_parsers = [
{ body = ".*security", group = "Security"},
{ message = ".*", group = "Other", default_scope = "other"}, # Everything that's not a conventional commit goes into the "Other" category
]
+1 -2
View File
@@ -665,10 +665,9 @@
},
{
"key": "providers",
"comment": "A list of enabled providers. You can freely choose the `<provider key>`. Note that you must add at least one key to a config file if you want to read values from an environment variable as the provider won't be known to Vikunja otherwise.",
"comment": "A list of enabled providers. This expects an array, which means you cannot use enviroment variables.",
"children": [
{
"key": "<provider key>",
"children": [
{
"key": "name",
+1 -1
View File
@@ -51,7 +51,7 @@
}
},
"devDependencies": {
"electron": "33.2.0",
"electron": "33.0.2",
"electron-builder": "25.1.8"
},
"dependencies": {
+5 -5
View File
@@ -16,8 +16,8 @@ importers:
version: 4.21.1
devDependencies:
electron:
specifier: 33.2.0
version: 33.2.0
specifier: 33.0.2
version: 33.0.2
electron-builder:
specifier: 25.1.8
version: 25.1.8(electron-builder-squirrel-windows@24.13.3)
@@ -575,8 +575,8 @@ packages:
electron-publish@25.1.7:
resolution: {integrity: sha512-+jbTkR9m39eDBMP4gfbqglDd6UvBC7RLh5Y0MhFSsc6UkGHj9Vj9TWobxevHYMMqmoujL11ZLjfPpMX+Pt6YEg==}
electron@33.2.0:
resolution: {integrity: sha512-PVw1ICAQDPsnnsmpNFX/b1i/49h67pbSPxuIENd9K9WpGO1tsRaQt+K2bmXqTuoMJsbzIc75Ce8zqtuwBPqawA==}
electron@33.0.2:
resolution: {integrity: sha512-C2WksfP0COsMHbYXSJG68j6S3TjuGDrw/YT42B526yXalIlNQZ2GeAYKryg6AEMkIp3p8TUfDRD0+HyiyCt/nw==}
engines: {node: '>= 12.20.55'}
hasBin: true
@@ -2373,7 +2373,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
electron@33.2.0:
electron@33.0.2:
dependencies:
'@electron/get': 2.0.3
'@types/node': 20.12.7
-1
View File
@@ -27,7 +27,6 @@ in {
go = {
enable = true;
enableHardeningWorkaround = true;
};
};
+27 -26
View File
@@ -13,7 +13,7 @@
},
"homepage": "https://vikunja.io/",
"funding": "https://opencollective.com/vikunja",
"packageManager": "pnpm@9.13.2",
"packageManager": "pnpm@9.12.3",
"keywords": [
"todo",
"productivity",
@@ -56,10 +56,10 @@
"@fortawesome/vue-fontawesome": "3.0.8",
"@github/hotkey": "3.1.1",
"@infectoone/vue-ganttastic": "2.3.2",
"@intlify/unplugin-vue-i18n": "6.0.0",
"@intlify/unplugin-vue-i18n": "5.2.0",
"@kyvg/vue3-notification": "3.4.1",
"@sentry/tracing": "7.114.0",
"@sentry/vue": "8.38.0",
"@sentry/vue": "8.36.0",
"@tiptap/core": "2.9.1",
"@tiptap/extension-blockquote": "2.9.1",
"@tiptap/extension-bold": "2.9.1",
@@ -98,11 +98,11 @@
"@vueuse/router": "11.2.0",
"axios": "1.7.7",
"blurhash": "2.0.5",
"bulma-css-variables": "0.9.33",
"bulma": "^1.0.2",
"change-case": "5.4.4",
"date-fns": "4.1.0",
"dayjs": "1.11.13",
"dompurify": "3.2.0",
"dompurify": "3.1.7",
"fast-deep-equal": "3.1.3",
"flatpickr": "4.6.13",
"flexsearch": "0.7.43",
@@ -110,13 +110,13 @@
"is-touch-device": "1.0.1",
"klona": "2.0.6",
"lowlight": "2.9.0",
"pinia": "2.2.6",
"pinia": "2.2.5",
"register-service-worker": "1.7.2",
"sortablejs": "1.15.3",
"tailwindcss": "3.4.15",
"tailwindcss": "3.4.14",
"tippy.js": "6.3.7",
"ufo": "1.5.4",
"vue": "3.5.13",
"vue": "3.5.12",
"vue-advanced-cropper": "2.8.9",
"vue-flatpickr-component": "11.0.5",
"vue-i18n": "10.0.4",
@@ -129,48 +129,49 @@
"@4tw/cypress-drag-drop": "2.2.5",
"@cypress/vite-dev-server": "5.2.0",
"@cypress/vue": "6.0.1",
"@faker-js/faker": "9.2.0",
"@faker-js/faker": "9.1.0",
"@histoire/plugin-screenshot": "0.17.17",
"@histoire/plugin-vue": "0.17.17",
"@tsconfig/node22": "22.0.0",
"@tsconfig/node20": "20.1.4",
"@types/codemirror": "5.60.15",
"@types/dompurify": "3.0.5",
"@types/flexsearch": "0.7.6",
"@types/is-touch-device": "1.0.3",
"@types/lodash.clonedeep": "4.5.9",
"@types/node": "22.9.0",
"@types/node": "22.8.6",
"@types/sortablejs": "1.15.8",
"@typescript-eslint/eslint-plugin": "8.14.0",
"@typescript-eslint/parser": "8.14.0",
"@typescript-eslint/eslint-plugin": "8.12.2",
"@typescript-eslint/parser": "8.12.2",
"@vitejs/plugin-legacy": "5.4.3",
"@vitejs/plugin-vue": "5.2.0",
"@vitejs/plugin-vue": "5.1.4",
"@vue/eslint-config-typescript": "14.1.3",
"@vue/test-utils": "2.4.6",
"@vue/tsconfig": "0.6.0",
"@vue/tsconfig": "0.5.1",
"autoprefixer": "10.4.20",
"browserslist": "4.24.2",
"caniuse-lite": "1.0.30001680",
"caniuse-lite": "1.0.30001676",
"csstype": "3.1.3",
"cypress": "13.15.2",
"cypress": "13.15.1",
"esbuild": "0.24.0",
"eslint": "9.14.0",
"eslint-plugin-vue": "9.31.0",
"happy-dom": "15.11.6",
"eslint-plugin-vue": "9.30.0",
"happy-dom": "15.8.0",
"histoire": "0.17.17",
"postcss": "8.4.49",
"postcss": "8.4.47",
"postcss-easing-gradients": "3.0.1",
"postcss-preset-env": "10.1.0",
"rollup": "4.26.0",
"postcss-easings": "4.0.0",
"postcss-preset-env": "10.0.9",
"rollup": "4.24.3",
"rollup-plugin-visualizer": "5.12.0",
"sass-embedded": "1.81.0",
"sass-embedded": "^1.80.6",
"start-server-and-test": "2.0.8",
"typescript": "5.6.3",
"unplugin-inject-preload": "3.0.0",
"vite": "5.4.11",
"vite-plugin-pwa": "0.21.0",
"vite": "5.4.10",
"vite-plugin-pwa": "0.20.5",
"vite-plugin-sentry": "1.4.0",
"vite-svg-loader": "5.1.0",
"vitest": "2.1.5",
"vitest": "2.1.4",
"vue-tsc": "2.1.10",
"wait-on": "8.0.1",
"workbox-cli": "7.3.0"
+1046 -1059
View File
File diff suppressed because it is too large Load Diff
@@ -8,7 +8,7 @@
>
<input
type="checkbox"
class="is-sr-only"
class="tw-sr-only"
:checked="modelValue"
:disabled="disabled || undefined"
@change="(event) => emit('update:modelValue', (event.target as HTMLInputElement).checked)"
+3 -11
View File
@@ -51,10 +51,7 @@ const props = withDefaults(defineProps<{
const wrapper = ref<HTMLElement | null>(null)
const computedBackgroundColor = computed(() => {
if (wrapper.value === null) {
return props.backgroundColor || '#fff'
}
return props.backgroundColor || getInheritedBackgroundColor(wrapper.value)
return props.backgroundColor || wrapper.value === null ? '#fff' : getInheritedBackgroundColor(wrapper.value)
})
/**
@@ -134,16 +131,11 @@ function removeHeight(el: HTMLElement) {
<style lang="scss" scoped>
$transition-time: 300ms;
// https://easings.net/#easeInQuint
$ease-in-quint: cubic-bezier(0.64, 0, 0.78, 0);
// https://easings.net/#easeInOutQuint
$ease-in-out-quint: cubic-bezier(0.83, 0, 0.17, 1);
.expandable-slide-enter-active,
.expandable-slide-leave-active {
transition:
opacity $transition-time $ease-in-quint,
height $transition-time $ease-in-out-quint;
opacity $transition-time ease-in-quint,
height $transition-time ease-in-out-quint;
overflow: hidden;
}
@@ -221,6 +221,8 @@ const buttonText = computed<string>(() => {
</script>
<style lang="scss" scoped>
@use "bulma/sass/utilities/css-variables.scss" as cv;
.datepicker-with-range-container {
position: relative;
}
@@ -231,7 +233,7 @@ const buttonText = computed<string>(() => {
border-radius: $radius;
border: 1px solid var(--grey-200);
background-color: var(--white);
box-shadow: $shadow;
box-shadow: cv.getVar("shadow");
&.is-open {
width: 500px;
@@ -168,6 +168,8 @@ const customRangeActive = computed<boolean>(() => {
</script>
<style lang="scss" scoped>
@use "bulma/sass/utilities/css-variables.scss" as cv;
.datepicker-with-range-container {
position: relative;
}
@@ -178,7 +180,7 @@ const customRangeActive = computed<boolean>(() => {
border-radius: $radius;
border: 1px solid var(--grey-200);
background-color: var(--white);
box-shadow: $shadow;
box-shadow: cv.getVar("shadow");
&.is-open {
width: 500px;
@@ -130,6 +130,8 @@ function reset() {
</script>
<style lang="scss" scoped>
@use "bulma/sass/utilities/css-variables.scss" as cv;
.color-picker-container {
display: flex;
justify-content: center;
@@ -156,7 +158,7 @@ function reset() {
overflow: hidden;
border-radius: 100%;
border: $BORDER_WIDTH solid var(--grey-300);
box-shadow: $shadow;
box-shadow: cv.getVar("shadow");
& > * {
grid-row: 1;
+3 -1
View File
@@ -120,6 +120,8 @@ function close() {
</script>
<style lang="scss" scoped>
@use "bulma/sass/utilities/css-variables.scss" as cv;
.datepicker {
input.input {
display: none;
@@ -132,7 +134,7 @@ function close() {
width: 320px;
background: var(--white);
border-radius: $radius;
box-shadow: $shadow;
box-shadow: cv.getVar("shadow");
@media screen and (max-width: ($tablet)) {
width: calc(100vw - 5rem);
+3 -1
View File
@@ -24,6 +24,8 @@ withDefaults(defineProps<{
</script>
<style lang="scss" scoped>
@use 'sass:list';
.progress-bar {
--progress-height: #{$size-normal};
--progress-bar-background-color: var(--border-light, #{$border-light});
@@ -73,7 +75,7 @@ withDefaults(defineProps<{
// Colors
@each $name, $pair in $colors {
$color: nth($pair, 1);
$color: list.nth($pair, 1);
&.is-#{$name} {
--progress-value-background-color: var(--#{$name}, #{$color});
@@ -65,11 +65,7 @@ import {useRoute} from 'vue-router'
import type {TaskFilterParams} from '@/services/taskCollection'
import {useLabelStore} from '@/stores/labels'
import {useProjectStore} from '@/stores/projects'
import {
hasFilterQuery,
transformFilterStringForApi,
transformFilterStringFromApi,
} from '@/helpers/filters'
import {FILTER_OPERATORS, transformFilterStringForApi, transformFilterStringFromApi} from '@/helpers/filters'
import FilterInputDocs from '@/components/project/partials/FilterInputDocs.vue'
const props = withDefaults(defineProps<{
@@ -165,7 +161,8 @@ function change(event: 'blur' | 'modelValue' | 'always') {
let s = ''
// When the filter does not contain any filter tokens, assume a simple search and redirect the input
if (!hasFilterQuery(filter)) {
const hasFilterQueries = FILTER_OPERATORS.find(o => filter.includes(o)) || false
if (!hasFilterQueries) {
s = filter
}
@@ -58,7 +58,7 @@ import type {IProject} from '@/modelTypes/IProject'
import BaseButton from '@/components/base/BaseButton.vue'
import {useProjectBackground} from '@/composables/useProjectBackground'
import {useProjectBackground} from './useProjectBackground'
import {useProjectStore} from '@/stores/projects'
import {getProjectTitle} from '@/helpers/getProjectTitle'
@@ -1,10 +1,9 @@
<script setup lang="ts">
import type {IProjectView} from '@/modelTypes/IProjectView'
import type {IFilter} from '@/modelTypes/ISavedFilter'
import XButton from '@/components/input/Button.vue'
import FilterInput from '@/components/project/partials/FilterInput.vue'
import {onBeforeMount, ref} from 'vue'
import {hasFilterQuery, transformFilterStringForApi, transformFilterStringFromApi} from '@/helpers/filters'
import {ref, onBeforeMount} from 'vue'
import {transformFilterStringForApi, transformFilterStringFromApi} from '@/helpers/filters'
import {useLabelStore} from '@/stores/labels'
import {useProjectStore} from '@/stores/projects'
import FilterInputDocs from '@/components/project/partials/FilterInputDocs.vue'
@@ -29,24 +28,18 @@ const labelStore = useLabelStore()
const projectStore = useProjectStore()
onBeforeMount(() => {
const transformFilterToString = (filter: IFilter): string => {
if (filter.s !== '') {
return filter.s
}
return transformFilterStringFromApi(
filter.filter,
labelId => labelStore.getLabelById(labelId)?.title || null,
projectId => projectStore.projects[projectId]?.title || null,
)
}
const transform = (filterString: string) => transformFilterStringFromApi(
filterString,
labelId => labelStore.getLabelById(labelId)?.title || null,
projectId => projectStore.projects[projectId]?.title || null,
)
const transformed = {
...props.modelValue,
filter: transformFilterToString(props.modelValue.filter),
filter: transform(props.modelValue.filter),
bucketConfiguration: props.modelValue.bucketConfiguration.map(bc => ({
title: bc.title,
filter: transformFilterToString(bc.filter),
filter: transform(bc.filter),
})),
}
@@ -56,31 +49,21 @@ onBeforeMount(() => {
})
function save() {
const transformFilterForApi = (filterQuery: string): IFilter => {
const filterString = transformFilterStringForApi(
filterQuery,
labelTitle => labelStore.getLabelByExactTitle(labelTitle)?.id || null,
projectTitle => {
const found = projectStore.findProjectByExactname(projectTitle)
return found?.id || null
},
)
const filter: IFilter = {}
if (hasFilterQuery(filterString)) {
filter.filter = filterString
} else {
filter.s = filterString
}
return filter
}
const transformFilter = (filterQuery: string) => transformFilterStringForApi(
filterQuery,
labelTitle => labelStore.getLabelByExactTitle(labelTitle)?.id || null,
projectTitle => {
const found = projectStore.findProjectByExactname(projectTitle)
return found?.id || null
},
)
emit('update:modelValue', {
...view.value,
filter: transformFilterForApi(view.value?.filter || ''),
filter: transformFilter(view.value?.filter),
bucketConfiguration: view.value?.bucketConfiguration.map(bc => ({
title: bc.title,
filter: transformFilterForApi(bc.filter || ''),
filter: transformFilter(bc.filter),
})),
})
}
@@ -248,7 +231,7 @@ function handleBubbleSave() {
<XButton
variant="secondary"
icon="plus"
@click="() => view.bucketConfiguration.push({title: '', filter: {filter: ''}})"
@click="() => view.bucketConfiguration.push({title: '', filter: ''})"
>
{{ $t('project.kanban.addBucket') }}
</XButton>
-4
View File
@@ -60,10 +60,6 @@ export const FILTER_JOIN_OPERATOR = [
export const FILTER_OPERATORS_REGEX = '(&lt;|&gt;|&lt;=|&gt;=|=|!=|not in|in)'
export function hasFilterQuery(filter: string): boolean {
return FILTER_OPERATORS.find(o => filter.includes(o)) || false
}
export function getFilterFieldRegexPattern(field: string): RegExp {
return new RegExp('(' + field + '\\s*' + FILTER_OPERATORS_REGEX + '\\s*)([\'"]?)([^\'"&|()<]+\\1?)?', 'ig')
}
+74 -74
View File
@@ -615,8 +615,8 @@
"quote": "Quote",
"quoteTooltip": "Capture a quote.",
"bulletList": "Bullet list",
"bulletListTooltip": "Sukurti paprastą sąrašą.",
"unorderedList": "Nerikiuojamas sąrašas",
"bulletListTooltip": "Create a simple bullet list.",
"unorderedList": "Unordered list",
"orderedList": "Ordered list",
"orderedListTooltip": "Create a list with numbering.",
"cleanBlock": "Clean Block",
@@ -626,31 +626,31 @@
"horizontalRule": "Horizontal Rule",
"horizontalRuleTooltip": "Divide a section.",
"sideBySide": "Vienas šalia kito",
"guide": "Vadovas",
"guide": "Guide",
"text": "Text",
"textTooltip": "Pradėkite rašyti paprastą tekstą.",
"taskList": "Užduočių sąrašas",
"taskListTooltip": "Sekti užduotis su veiklų sąrašu.",
"textTooltip": "Just start typing with plain text.",
"taskList": "Task list",
"taskListTooltip": "Track tasks with a to-do list.",
"undo": "Undo",
"redo": "Grąžinti",
"placeholder": "Rašykite tekstą arba spauskite '/' norėdami matyti daugiau pasirinkčių…",
"redo": "Redo",
"placeholder": "Type some text or hit '/' to see more options…",
"table": {
"title": "Table",
"insert": "Insert table",
"addColumnBefore": "Add column before",
"addColumnAfter": "Add column after",
"deleteColumn": "Delete column",
"addRowBefore": "Pridėti eilutę prieš tai",
"addRowAfter": "Pridėti eilutę po",
"addRowBefore": "Add row before",
"addRowAfter": "Add row after",
"deleteRow": "Ištrinti eilutę",
"deleteTable": "Ištrinti lentelę",
"mergeCells": "Sujungti langelius",
"splitCell": "Skelti langelį",
"deleteTable": "Delete table",
"mergeCells": "Merge cells",
"splitCell": "Split cell",
"toggleHeaderColumn": "Toggle header column",
"toggleHeaderRow": "Toggle header row",
"toggleHeaderCell": "Toggle header cell",
"mergeOrSplit": "Sujungti arba padalinti",
"fixTables": "Pataisyti lenteles"
"mergeOrSplit": "Merge or split",
"fixTables": "Fix tables"
}
},
"multiselect": {
@@ -658,15 +658,15 @@
"selectPlaceholder": "Click or press enter to select"
},
"datepickerRange": {
"to": "Į",
"to": "To",
"from": "From",
"fromto": "{from}{to}",
"date": "Data",
"fromto": "{from} to {to}",
"date": "Date",
"ranges": {
"today": "Today",
"thisWeek": "Šią savaitę",
"thisWeek": "This Week",
"restOfThisWeek": "The Rest of This Week",
"nextWeek": "Kitą savaitę",
"nextWeek": "Next Week",
"next7Days": "Next 7 Days",
"lastWeek": "Praeitą savaitę",
"thisMonth": "Šį mėnesį",
@@ -693,7 +693,7 @@
"startOfThisMonth": "Start of this month",
"endOfThisMonth": "End of this month",
"startOfNextMonth": "Start of next month",
"endOfNextMonth": "Kito mėnesio pabaiga",
"endOfNextMonth": "End of next month",
"in30Days": "In 30 days",
"startOfThisYear": "Beginning of this year",
"endOfThisYear": "End of this year"
@@ -706,7 +706,7 @@
"intro": "Specify relative dates which are resolved on the fly by Vikunja when applying the filter.",
"expression": "Each Date Math expression starts with an anchor date, which can either be {0}, or a date string ending with {1}. This anchor date can optionally be followed by one or more maths expressions.",
"similar": "These expressions are similar to the ones provided by {0} and {1}.",
"add1Day": "Pridėti vieną dieną",
"add1Day": "Add one day",
"minus1Day": "Subtract one day",
"roundDay": "Round down to the nearest day",
"supportedUnits": "Supported time units",
@@ -716,17 +716,17 @@
"minutes": "Minutes",
"hours": "Hours",
"days": "Days",
"weeks": "Savaitės",
"months": "Mėnesiai",
"years": "Metai"
"weeks": "Weeks",
"months": "Months",
"years": "Years"
},
"examples": {
"now": "Dabar",
"in24h": "24-ių valandų bėgyje",
"today": "Šiandien 00:00",
"now": "Right now",
"in24h": "In 24h",
"today": "Today at 00:00",
"beginningOfThisWeek": "The beginning of this week at 00:00",
"endOfThisWeek": "The end of this week",
"in30Days": "Už 30 dienų",
"in30Days": "In 30 days",
"datePlusMonth": "{0} plus one month at 00:00 of that day"
}
}
@@ -743,13 +743,13 @@
"undo": "Undo",
"openDetail": "Open task detail view",
"checklistTotal": "{checked} of {total} tasks",
"checklistAllDone": "{total} užduotys",
"checklistAllDone": "{total} tasks",
"show": {
"titleCurrent": "Dabartinės Užduotys",
"titleDates": "Užduotys nuo {from} iki {to}",
"noDates": "Rodyti užduotis be datų",
"overdue": "Rodyti neišpildytas užduotis",
"fromuntil": "Užduotys nuo {from} iki {until}",
"titleCurrent": "Current Tasks",
"titleDates": "Tasks from {from} until {to}",
"noDates": "Show tasks without dates",
"overdue": "Show overdue tasks",
"fromuntil": "Tasks from {from} until {until}",
"select": "Select a date range",
"noTasks": "Nothing to do — Have a nice day!"
},
@@ -823,55 +823,55 @@
"notSubscribedTask": "You are not subscribed to this task and won't receive notifications for changes.",
"subscribe": "Subscribe",
"unsubscribe": "Unsubscribe",
"subscribeSuccessProject": "Užsiprenumeravai projekto naujienas",
"unsubscribeSuccessProject": "Projekto naujienų atsisakyta",
"subscribeSuccessTask": "Užsiprenumeravai užduoties naujienas",
"subscribeSuccessProject": "You are now subscribed to this project",
"unsubscribeSuccessProject": "You are now unsubscribed to this project",
"subscribeSuccessTask": "You are now subscribed to this task",
"unsubscribeSuccessTask": "Užsiprenumeravai užduoties naujienas"
},
"attachment": {
"title": "Attachments",
"createdBy": "sukurta {0} {1}",
"downloadTooltip": "Atsiųsti šį prisegtuką",
"createdBy": "created {0} by {1}",
"downloadTooltip": "Download this attachment",
"upload": "Upload attachment",
"drop": "Drop files here to upload",
"delete": "Delete attachment",
"deleteTooltip": "Pašalinti šį priedą",
"deleteTooltip": "Delete this attachment",
"deleteText1": "Ar esi tikra -s, kad nori ištrinti šį prisegtuką {filename}?",
"copyUrl": "Copy URL",
"copyUrlTooltip": "Nukopijuoti nuorodą šio prisegtuko, kaip paprastą tekstą",
"copyUrlTooltip": "Copy the url of this attachment for usage in text",
"setAsCover": "Make cover",
"unsetAsCover": "Pašalinti viršelį",
"successfullyChangedCoverImage": "Viršelio paveikslėlis buvo sėkmingai pakeistas.",
"usedAsCover": "Viršelio nuotrauka"
"unsetAsCover": "Remove cover",
"successfullyChangedCoverImage": "The cover image was successfully changed.",
"usedAsCover": "Cover image"
},
"comment": {
"title": "Comments",
"loading": "Loading comments…",
"edited": "pataisyta {date}",
"creating": "Kraunami komentarai…",
"placeholder": "Pridėk komentarą, spausk '/' dėl daugiau pasirinkčių…",
"edited": "edited {date}",
"creating": "Creating comment…",
"placeholder": "Add your comment, hit '/' for more options…",
"comment": "Comment",
"delete": "Delete this comment",
"deleteText1": "Ar tikrai nori ištrinti šį komentarą?",
"deleteSuccess": "Komentaras panaikintas sėkmingai.",
"addedSuccess": "Komentaras sėkmigai pridėtas.",
"permalink": "Nukopijuoti komentaro nuorodą"
"deleteText1": "Are you sure you want to delete this comment?",
"deleteSuccess": "The comment was deleted successfully.",
"addedSuccess": "The comment was added successfully.",
"permalink": "Copy permalink to this comment"
},
"deferDueDate": {
"title": "Defer due date",
"1day": "1 diena",
"3days": "3 dienos",
"1week": "1 savaitė"
"1day": "1 day",
"3days": "3 days",
"1week": "1 week"
},
"description": {
"placeholder": "Įvesti aprašą, spausti '/' norint gauti daugiau pasirinkčių…",
"empty": "Aprašo kol kas nėra."
},
"assignee": {
"placeholder": "Rašykite norėdami priskirti naudotoją…",
"selectPlaceholder": "Priskirti šį naudotoją",
"assignSuccess": "Naudotojas buvo sėmingai priskirtas.",
"unassignSuccess": "Naudotojas buvo sėkmingai atšauktas."
"placeholder": "Type to assign a user…",
"selectPlaceholder": "Assign this user",
"assignSuccess": "The user has been assigned successfully.",
"unassignSuccess": "The user has been unassigned successfully."
},
"label": {
"placeholder": "Rašykite noredami pridėti etiketę…",
@@ -898,15 +898,15 @@
"add": "Add a New Task Relation",
"new": "New Task Relation",
"searchPlaceholder": "Type search for a task to add as related…",
"createPlaceholder": "Pridėti kaip susijusią užduotį",
"createPlaceholder": "Add this as related task",
"differentProject": "This task belongs to a different project.",
"noneYet": "Kol kas užduoties ryšio nėra.",
"noneYet": "No task relations yet.",
"delete": "Delete Task Relation",
"deleteText1": "Are you sure you want to delete this task relation?",
"select": "Pasirinkite ryšio tipą",
"select": "Select a relation kind",
"taskRequired": "Please select a task or enter a new task title.",
"kinds": {
"subtask": "Sub užduotis | Sub užduotys",
"subtask": "Subtask | Subtasks",
"parenttask": "Parent Task | Parent Tasks",
"related": "Related Task | Related Tasks",
"duplicateof": "Duplicate Of | Duplicates Of",
@@ -944,7 +944,7 @@
"weeks": "Weeks",
"months": "Months",
"years": "Years",
"invalidAmount": "Prašome įvesti daugiau nei 0."
"invalidAmount": "Please enter more than 0."
},
"quickAddMagic": {
"hint": "Use magic prefixes to define due dates, assignees and other task properties.",
@@ -965,7 +965,7 @@
"dateAndTime": "Date and time",
"date": "Any date will be used as the due date of the new task. You can use dates in any of these formats:",
"dateWeekday": "any weekday, will use the next date with that date",
"dateCurrentYear": "naudoti šiuos metus",
"dateCurrentYear": "will use the current year",
"dateNth": "will use the {day}th of the current month",
"dateTime": "Combine any of the date formats with \"{time}\" (or {timePM}) to set a time.",
"repeats": "Repeating tasks",
@@ -982,7 +982,7 @@
"edit": {
"title": "Edit Team \"{team}\"",
"members": "Team Members",
"search": "Rašykite čia norėdami ieškoti naudotojo…",
"search": "Type to search a user…",
"addUser": "Add to team",
"makeMember": "Make Member",
"makeAdmin": "Make Admin",
@@ -990,7 +990,7 @@
"userAddedSuccess": "The team member was successfully added.",
"madeMember": "The team member was successfully made member.",
"madeAdmin": "The team member was successfully made admin.",
"mustSelectUser": "Prašome pasirinkti vartotoją.",
"mustSelectUser": "Please select a user.",
"delete": {
"header": "Delete the team",
"text1": "Are you sure you want to delete this team and all of its members?",
@@ -1001,13 +1001,13 @@
"header": "Remove a user from the team",
"text1": "Are you sure you want to remove this user from the team?",
"text2": "They will lose access to all projects this team has access to. This CANNOT BE UNDONE!",
"success": "Naudotojas buvo sėkmingai ištrintas iš komandos."
"success": "The user was successfully deleted from the team."
},
"leave": {
"title": "Leave team",
"text1": "Are you sure you want to leave this team?",
"text2": "You will lose access to all projects this team has access to. If you change your mind you'll need a team admin to add you again.",
"success": "Jūs sėkmingai palikote komandą."
"success": "You have successfully left the team."
}
},
"attributes": {
@@ -1016,7 +1016,7 @@
"nameRequired": "Prašome nurodyti vardą.",
"description": "Aprašymas",
"descriptionPlaceholder": "Aprašykite komandą čia, spauskite '/' dėl daugiau opcijų…",
"admin": "Administravimas",
"admin": "Admin",
"member": "Member",
"isPublic": "Public Team",
"isPublicDescription": "Padaryk komandą viešai randamą. Kai įgalinta, bet kas gali pasidalinti projektu su komanda, net nebūdama -s tiesioginiu komandos nariu."
@@ -1041,7 +1041,7 @@
"related": "Modify related tasks of this task",
"color": "Change the color of this task",
"move": "Move this task to another project",
"reminder": "Valdyti šios užduoties priminimus",
"reminder": "Manage reminders of this task",
"description": "Praskleisti redagavimą užduoties apraše",
"delete": "Delete this task",
"priority": "Change the priority of this task",
@@ -1105,7 +1105,7 @@
},
"quickActions": {
"commands": "Commands",
"placeholder": "Rašykite komandą arba ieškokite…",
"placeholder": "Type a command or search…",
"hint": "Gai naudoti {project} sutrumpinti projektų paiešką. Sujungti {project} ar {label} (etiketes) paieškos užklausoje norint ieškoti užduoties su tomis etiketėmis ar tame projekte. Naudok {assignee} norėdama(-s) ieškoti komandu.",
"tasks": "Tasks",
"projects": "Projects",
@@ -1165,7 +1165,7 @@
"4002": "The task does not exist.",
"4003": "All bulk editing tasks must belong to the same project.",
"4004": "Need at least one task when bulk editing tasks.",
"4005": "Neturi prieigos prie šios užduoties.",
"4005": "You do not have the right to see the task.",
"4006": "You can't set a parent task as the task itself.",
"4007": "You can't create a task relation with an invalid kind of relation.",
"4008": "You can't create a task relation which already exists.",
@@ -1186,7 +1186,7 @@
"6005": "The user is already a member of that team.",
"6006": "Cannot delete the last team member.",
"6007": "The team does not have access to the project to perform that action.",
"7002": "Naudotojas jau turi prieigą prie šio projekto.",
"7002": "The user already has access to that project.",
"7003": "You do not have access to that project.",
"8001": "Ši etiketė jau yra prie šios užduoties.",
"8002": "Etiketė neegzistuoja.",
+2 -3
View File
@@ -1,6 +1,5 @@
import type {IAbstract} from './IAbstract'
import type {IProject} from '@/modelTypes/IProject'
import type {IFilters} from '@/modelTypes/ISavedFilter'
export const PROJECT_VIEW_KINDS = {
LIST: 'list',
@@ -21,7 +20,7 @@ export type ProjectViewBucketConfigurationMode = typeof PROJECT_VIEW_BUCKET_CONF
export interface IProjectViewBucketConfiguration {
title: string
filter: IFilters
filter: string
}
export interface IProjectView extends IAbstract {
@@ -30,7 +29,7 @@ export interface IProjectView extends IAbstract {
projectId: IProject['id']
viewKind: ProjectViewKind
filter: IFilters
filter: string
position: number
bucketConfigurationMode: ProjectViewBucketConfigurationMode
+2 -2
View File
@@ -2,7 +2,7 @@ import type {IAbstract} from './IAbstract'
import type {IUser} from './IUser'
// FIXME: what makes this different from TaskFilterParams?
export interface IFilters {
interface Filters {
sort_by: ('start_date' | 'done' | 'id' | 'position')[],
order_by: ('asc' | 'desc')[],
filter: string,
@@ -14,7 +14,7 @@ export interface ISavedFilter extends IAbstract {
id: number
title: string
description: string
filters: IFilters
filters: Filters
owner: IUser
created: Date
+1 -7
View File
@@ -7,13 +7,7 @@ export default class ProjectViewModel extends AbstractModel<IProjectView> implem
projectId = 0
viewKind: ProjectViewKind = 'list'
filter: IProjectView['filters'] = {
sort_by: ['done', 'id'],
order_by: ['asc', 'desc'],
filter: 'done = false',
filter_include_nulls: true,
s: '',
}
filter = ''
position = 0
bucketConfiguration = []
+18 -3
View File
@@ -1,3 +1,4 @@
@use "sass:math";
//
// IMPORTANT NOTE:
//
@@ -10,10 +11,17 @@
// are defined here.
//
$family-sans-serif: 'Open Sans', Helvetica, Arial, sans-serif;
// the default values get overwritten by the definitions above
@import "bulma-css-variables/sass/utilities/_all";
// utilities
@forward "bulma/sass/utilities/initial-variables.scss" with (
$family-sans-serif: "'Open Sans', Helvetica, Arial, sans-serif"
);
@forward "bulma/sass/utilities/functions";
@forward "bulma/sass/utilities/derived-variables";
@forward "bulma/sass/utilities/controls";
@use "bulma/sass/utilities/initial-variables.scss" as *;
// since $tablet is defined by bulma we can just define it after importing the utilities
$mobile: math.div($tablet, 2);
@@ -39,3 +47,10 @@ $vikunja-nav-color: var(--grey-700);
$vikunja-nav-selected-width: 0.4rem;
$close-button-min-space: 84px;
// re-added from bulma-css-variables
$item-padding: 0.375rem 1rem;
// 1152px container + 4rem
$widescreen: 1152px + (2 * $gap);
$border-light: $grey-lightest;
+4 -4
View File
@@ -1,4 +1,4 @@
@import "tooltip";
@import "labels";
@import "task";
@import "tasks";
@forward "tooltip";
@forward "labels";
@forward "task";
@forward "tasks";
@@ -1,3 +1,5 @@
@use "@/styles/common-imports" as *;
// FIXME: These classes are used all over.
// very hard to untangle
// they have many overwrites at different positions
@@ -1,2 +1,2 @@
@import "colors";
@import "shadows";
@forward "colors";
@forward "shadows";
+76 -70
View File
@@ -1,11 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@import "fonts";
@import "transitions";
// ###################
// START BULMA IMPORTS
// ###################
@@ -14,79 +6,93 @@
// with the expeption of the bulma utilities – They are
// imported globally in common-imports.scss
// imports from "bulma-css-variables/sass/base/_all";
@import "bulma-css-variables/sass/base/minireset";
@import "bulma-css-variables/sass/base/generic";
@import "bulma-css-variables/sass/base/animations";
// themes
@forward "bulma/sass/themes";
// base
@forward "bulma/sass/base/minireset";
@forward "bulma/sass/base/generic";
@forward "bulma/sass/base/animations";
// imports from "bulma-css-variables/sass/elements/_all";
@import "bulma-css-variables/sass/elements/box";
@import "bulma-css-variables/sass/elements/button";
@import "bulma-css-variables/sass/elements/container";
@import "bulma-css-variables/sass/elements/content";
@import "bulma-css-variables/sass/elements/icon";
@import "bulma-css-variables/sass/elements/image";
//@import "bulma-css-variables/sass/elements/notification"; // not used
// @import "bulma-css-variables/sass/elements/progress"; // not used
@import "bulma-css-variables/sass/elements/table";
@import "bulma-css-variables/sass/elements/tag";
@import "bulma-css-variables/sass/elements/title";
// elements
@forward "bulma/sass/elements/block";
@forward "bulma/sass/elements/box";
@forward "bulma/sass/elements/button";
@forward "bulma/sass/elements/content";
@forward "bulma/sass/elements/delete";
@forward "bulma/sass/elements/icon";
@forward "bulma/sass/elements/image";
@forward "bulma/sass/elements/loader";
// @forward "bulma/sass/elements/notification"; // not used
// @forward "bulma/sass/elements/progress"; // not used
@forward "bulma/sass/elements/table";
@forward "bulma/sass/elements/tag";
@forward "bulma/sass/elements/title";
@import "bulma-css-variables/sass/elements/other";
// form
@forward "bulma/sass/form/shared";
@forward "bulma/sass/form/input-textarea";
@forward "bulma/sass/form/checkbox-radio";
@forward "bulma/sass/form/select";
@forward "bulma/sass/form/file";
@forward "bulma/sass/form/tools";
// components
// @forward "bulma/sass/components/breadcrumb"; // not used
@forward "bulma/sass/components/card";
// @forward "bulma/sass/components/dropdown"; // not used
@forward "bulma/sass/components/menu";
// @forward "bulma/sass/components/message"; // not used
@forward "bulma/sass/components/modal";
@forward "bulma/sass/components/navbar";
@forward "bulma/sass/components/pagination";
// @forward "bulma/sass/components/panel"; // not used
// @forward "bulma/sass/components/tabs"; // not used
// imports from "bulma-css-variables/sass/form/_all";
@import "bulma-css-variables/sass/form/shared";
@import "bulma-css-variables/sass/form/input-textarea";
@import "bulma-css-variables/sass/form/checkbox-radio";
@import "bulma-css-variables/sass/form/select";
@import "bulma-css-variables/sass/form/file";
@import "bulma-css-variables/sass/form/tools";
// grid
@forward "bulma/sass/grid/columns";
// @forward "bulma/sass/grid/grid"; // probably not used, because new
// layout
@forward "bulma/sass/layout/container";
// @forward "bulma/sass/layout/footer"; // not used
// @forward "bulma/sass/layout/hero"; // not used
// @forward "bulma/sass/layout/level"; // not used
@forward "bulma/sass/layout/media";
// @forward "bulma/sass/layout/section"; // not used
// imports from "bulma-css-variables/sass/components/_all";
// @import "bulma-css-variables/sass/components/breadcrumb"; // not used
@import "bulma-css-variables/sass/components/card";
// @import "bulma-css-variables/sass/components/dropdown"; // moved to component
// @import "bulma-css-variables/sass/components/level"; // not used
@import "bulma-css-variables/sass/components/media";
@import "bulma-css-variables/sass/components/menu";
//@import "bulma-css-variables/sass/components/message"; // not used
@import "bulma-css-variables/sass/components/modal";
@import "bulma-css-variables/sass/components/navbar";
@import "bulma-css-variables/sass/components/pagination";
// @import "bulma-css-variables/sass/components/panel"; // not used
// @import "bulma-css-variables/sass/components/tabs"; // not used
// base/skeleton
@forward "bulma/sass/base/skeleton";
// imports from "bulma-css-variables/sass/grid/_all";
@import "bulma-css-variables/sass/grid/columns";
// @import "bulma-css-variables/sass/grid/tiles"; // not used
// imports from "bulma-css-variables/sass/helpers/_all";
@import "bulma-css-variables/sass/helpers/color";
@import "bulma-css-variables/sass/helpers/flexbox";
@import "bulma-css-variables/sass/helpers/float";
// @import "bulma-css-variables/sass/helpers/other"; // not used
// @import "bulma-css-variables/sass/helpers/overflow";
// @import "bulma-css-variables/sass/helpers/position";
@import "bulma-css-variables/sass/helpers/spacing";
@import "bulma-css-variables/sass/helpers/typography";
@import "bulma-css-variables/sass/helpers/visibility";
// imports from "bulma-css-variables/sass/layout/_all";
// @import "bulma-css-variables/sass/layout/hero"; // not used
// @import "bulma-css-variables/sass/layout/section"; // not used
// @import "bulma-css-variables/sass/layout/footer"; // not used
// helpers
// @forward "bulma/sass/helpers/aspect-ratio"; // probably not used, because new
// @forward "bulma/sass/helpers/border"; // probably not used, because new
@forward "bulma/sass/helpers/color";
@forward "bulma/sass/helpers/flexbox";
@forward "bulma/sass/helpers/float";
// @forward "bulma/sass/helpers/gap"; // probably not used, because new
// @forward "bulma/sass/helpers/overflow"; // not used
// @forward "bulma/sass/helpers/position"; // not used
@forward "bulma/sass/helpers/spacing";
@forward "bulma/sass/helpers/typography";
@forward "bulma/sass/helpers/visibility";
// @forward "bulma/sass/helpers/other"; // not used
// #################
// END BULMA IMPORTS
// #################
@import "theme";
@import "components";
@import "custom-properties";
@use "fonts";
@use "transitions";
@use "theme";
@use "components";
@use "custom-properties";
@tailwind base;
@tailwind components;
@tailwind utilities;
+10 -10
View File
@@ -1,12 +1,12 @@
@import "scrollbars";
@forward "scrollbars";
@import "theme";
@forward "theme";
@import "background";
@import "content";
@import "form";
@import "link-share";
@import "loading";
@import "flatpickr";
@import 'helpers';
@import 'navigation';
@forward "background";
@forward "content";
@forward "form";
@forward "link-share";
@forward "loading";
@forward "flatpickr";
@forward 'helpers';
@forward 'navigation';
@@ -1,3 +1,5 @@
@use "@/styles/common-imports" as *;
.app-container.has-background,
.link-share-container.has-background {
position: relative;
+2
View File
@@ -1,3 +1,5 @@
@use "@/styles/common-imports" as *;
.content h3 {
.icon,
.is-small {
+7 -4
View File
@@ -1,3 +1,6 @@
@use 'sass:list';
@use "@/styles/common-imports" as *;
.field.has-addons .button {
height: 2.5rem;
margin-left: 0 !important;
@@ -20,8 +23,8 @@
}
@each $name, $pair in $colors {
$color: nth($pair, 1);
$color-invert: nth($pair, 2);
$color: list.nth($pair, 1);
$color-invert: list.nth($pair, 2);
&.is-#{$name} {
&.is-active,
@@ -85,8 +88,8 @@
padding: .3rem;
&:focus {
background: $input-background-color;
border-color: $input-focus-border-color;
background: $scheme-main;
border-color: $link;
}
&.disabled {
@@ -1,3 +1,5 @@
@use "@/styles/common-imports" as *;
.field.has-addons.no-input-mobile {
.control:first-child {
width: 100%;
+2
View File
@@ -1,3 +1,5 @@
@use "bulma/sass/utilities/mixins.scss" as *;
// FIXME: move to Loading.vue
.loader-container.is-loading {
position: relative;
@@ -1,3 +1,5 @@
@use "@/styles/common-imports" as *;
// these are general menu styles
// should be in own components
.menu {
@@ -1,3 +1,5 @@
@use "@/styles/common-imports" as *;
$scrollbar-height: 8px;
$scrollbar-track-color: var(--grey-200);
$scrollbar-thumb-color: var(--grey-300);
+2
View File
@@ -1,3 +1,5 @@
@use "@/styles/common-imports" as *;
*,
*:hover,
*:active,
+2
View File
@@ -1,3 +1,5 @@
@use "@/styles/common-imports" as *;
.fade-enter-active,
.fade-leave-active {
transition: opacity $transition-duration;
+1 -1
View File
@@ -1,6 +1,6 @@
{
"extends": [
"@tsconfig/node22/tsconfig.json",
"@tsconfig/node20/tsconfig.json",
"@vue/tsconfig/tsconfig.json"
],
"include": ["vite.config.*", "vitest.config.*", "cypress.config.*"],
+8 -7
View File
@@ -14,6 +14,7 @@ import {visualizer} from 'rollup-plugin-visualizer'
import viteSentry, {type ViteSentryPluginOptions} from 'vite-plugin-sentry'
import svgLoader from 'vite-svg-loader'
import postcssPresetEnv from 'postcss-preset-env'
import postcssEasings from 'postcss-easings'
import postcssEasingGradients from 'postcss-easing-gradients'
import tailwindcss from 'tailwindcss'
@@ -21,7 +22,7 @@ const pathSrc = fileURLToPath(new URL('./src', import.meta.url)).replaceAll('\\'
// the @use rules have to be the first in the compiled stylesheets
const PREFIXED_SCSS_STYLES = `@use "sass:math";
@import "${pathSrc}/styles/common-imports.scss";`
@use "${pathSrc}/styles/common-imports.scss" as *;`
const isModernBuild = Boolean(process.env.BUILD_MODERN_ONLY)
const legacy = isModernBuild
@@ -92,20 +93,20 @@ export default defineConfig(({mode}) => {
},
css: {
preprocessorOptions: {
sass: {
api: 'modern-compiler',
quietDeps: true, // silence deprecation warnings
},
// sass: {
// api: 'modern-compiler',
// },
scss: {
api: 'modern-compiler',
additionalData: PREFIXED_SCSS_STYLES,
charset: false, // fixes "@charset" must be the first rule in the file" warnings,
quietDeps: true, // silence deprecation warnings
quietDeps: true, // silence bulmas deprecation warnings
},
},
postcss: {
plugins: [
tailwindcss(),
postcssEasings(),
postcssEasingGradients(),
postcssPresetEnv(),
],
@@ -221,7 +222,7 @@ export default defineConfig(({mode}) => {
build: {
target: 'esnext',
// required for sentry debugging: tells vite to create source maps
sourcemap: Boolean(env.SENTRY_AUTH_TOKEN),
sourcemap: true,
rollupOptions: {
plugins: [
visualizer({
+11 -11
View File
@@ -66,18 +66,19 @@ require (
github.com/tkuchiki/go-timezone v0.2.3
github.com/typesense/typesense-go/v2 v2.0.0
github.com/ulule/limiter/v3 v3.11.2
github.com/wneessen/go-mail v0.5.2
github.com/wneessen/go-mail v0.5.1
github.com/yuin/goldmark v1.7.8
golang.org/x/crypto v0.29.0
golang.org/x/image v0.22.0
golang.org/x/oauth2 v0.24.0
golang.org/x/sync v0.9.0
golang.org/x/sys v0.27.0
golang.org/x/term v0.26.0
golang.org/x/text v0.20.0
golang.org/x/crypto v0.28.0
golang.org/x/image v0.21.0
golang.org/x/oauth2 v0.23.0
golang.org/x/sync v0.8.0
golang.org/x/sys v0.26.0
golang.org/x/term v0.25.0
golang.org/x/text v0.19.0
gopkg.in/d4l3k/messagediff.v1 v1.2.1
gopkg.in/yaml.v3 v3.0.1
mvdan.cc/xurls/v2 v2.5.0
src.techknowlogick.com/xgo v1.8.1-0.20241105013731-313dedef864f
src.techknowlogick.com/xgo v1.7.1-0.20240403232151-e01c4fbef884
src.techknowlogick.com/xormigrate v1.7.1
xorm.io/builder v0.3.13
xorm.io/xorm v1.3.9
@@ -171,7 +172,6 @@ require (
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)
@@ -179,4 +179,4 @@ replace github.com/samedi/caldav-go => github.com/kolaente/caldav-go v3.0.1-0.20
go 1.22
toolchain go1.23.3
toolchain go1.23.2
+42 -18
View File
@@ -19,6 +19,10 @@ github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
github.com/ThreeDotsLabs/watermill v1.3.7 h1:NV0PSTmuACVEOV4dMxRnmGXrmbz8U83LENOvpHekN7o=
github.com/ThreeDotsLabs/watermill v1.3.7/go.mod h1:lBnrLbxOjeMRgcJbv+UiZr8Ylz8RkJ4m6i/VN/Nk+to=
github.com/ThreeDotsLabs/watermill v1.4.0 h1:c8T4QHY/MuxSXYQ1Cxn93cCZB5lkGgqhYA6L2jh2ghA=
github.com/ThreeDotsLabs/watermill v1.4.0/go.mod h1:lBnrLbxOjeMRgcJbv+UiZr8Ylz8RkJ4m6i/VN/Nk+to=
github.com/ThreeDotsLabs/watermill v1.4.1 h1:gjP6yZH+otMPjV0KsV07pl9TeMm9UQV/gqiuiuG5Drs=
github.com/ThreeDotsLabs/watermill v1.4.1/go.mod h1:lBnrLbxOjeMRgcJbv+UiZr8Ylz8RkJ4m6i/VN/Nk+to=
github.com/adlio/trello v1.12.0 h1:JqOE2GFHQ9YtEviRRRSnicSxPbt4WFOxhqXzjMOw8lw=
@@ -91,10 +95,14 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4=
github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4=
github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc=
github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc=
github.com/ganigeorgiev/fexpr v0.4.1 h1:hpUgbUEEWIZhSDBtf4M9aUNfQQ0BZkGRaMePy7Gcx5k=
github.com/ganigeorgiev/fexpr v0.4.1/go.mod h1:RyGiGqmeXhEQ6+mlGdnUleLHgtzzu/VGO2WtJkF5drE=
github.com/getsentry/sentry-go v0.29.0 h1:YtWluuCFg9OfcqnaujpY918N/AhCCwarIDWOYSBAjCA=
github.com/getsentry/sentry-go v0.29.0/go.mod h1:jhPesDAL0Q0W2+2YEuVOvdWmVtdsr1+jtBrlDEVWwLY=
github.com/getsentry/sentry-go v0.29.1 h1:DyZuChN8Hz3ARxGVV8ePaNXh1dQ7d76AiB117xcREwA=
github.com/getsentry/sentry-go v0.29.1/go.mod h1:x3AtIzN01d6SiWkderzaH28Tm0lgkafpJ5Bm3li39O0=
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
@@ -329,6 +337,8 @@ github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZ
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/go-sqlite3 v1.14.23 h1:gbShiuAP1W5j9UOksQ06aiiqPMxYecovVGwmTxWtuw0=
github.com/mattn/go-sqlite3 v1.14.23/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
@@ -385,6 +395,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI=
github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
@@ -393,6 +405,10 @@ github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4=
github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA=
github.com/redis/go-redis/v9 v9.6.2 h1:w0uvkRbc9KpgD98zcvo5IrVUsn0lXpRMuhNgiHDJzdk=
github.com/redis/go-redis/v9 v9.6.2/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA=
github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
@@ -462,6 +478,8 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg=
github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk=
github.com/swaggo/swag v1.16.4 h1:clWJtd9LStiG3VeijiCfOVODP6VpHtKdQy9ELFG3s1A=
github.com/swaggo/swag v1.16.4/go.mod h1:VBsHJRsDvfYvqoiMKnsdwhNV9LEMHgEDZcyVYX0sxPg=
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
@@ -481,10 +499,10 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/wneessen/go-mail v0.4.4 h1:rI8wJzPYymUpUth87vFV3k313bmnid4v+FwhBAYYLFM=
github.com/wneessen/go-mail v0.4.4/go.mod h1:zxOlafWCP/r6FEhAaRgH4IC1vg2YXxO0Nar9u0IScZ8=
github.com/wneessen/go-mail v0.5.1 h1:3XIiVt4N3oZzHmACyLsp1OTq5/yQuSZWtHliPMD3KsI=
github.com/wneessen/go-mail v0.5.1/go.mod h1:kRroJvEq2hOSEPFRiKjN7Csrz0G1w+RpiGR3b6yo+Ck=
github.com/wneessen/go-mail v0.5.2 h1:MZKwgHJoRboLJ+EHMLuHpZc95wo+u1xViL/4XSswDT8=
github.com/wneessen/go-mail v0.5.2/go.mod h1:kRroJvEq2hOSEPFRiKjN7Csrz0G1w+RpiGR3b6yo+Ck=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
@@ -494,6 +512,14 @@ github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7Jul
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg=
github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yuin/goldmark v1.7.5 h1:ekTA+pa72CEE23b+Cr+9Z7Xq8/rpKdRMqqyauGO5RNE=
github.com/yuin/goldmark v1.7.5/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yuin/goldmark v1.7.6 h1:cZgJxVh5mL5cu8KOnwxvFJy5TFB0BHUskZZyq7TYbDg=
github.com/yuin/goldmark v1.7.6/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yuin/goldmark v1.7.7 h1:5m9rrB1sW3JUMToKFQfb+FGt1U7r57IHu5GrYrG2nqU=
github.com/yuin/goldmark v1.7.7/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
@@ -534,17 +560,17 @@ golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.20.0 h1:7cVCUjQwfL18gyBJOmYvptfSHS8Fb3YUDtfLIZ7Nbpw=
golang.org/x/image v0.20.0/go.mod h1:0a88To4CYVBAHp5FXJm8o7QbUl37Vd85ply1vyD8auM=
golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s=
golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78=
golang.org/x/image v0.22.0 h1:UtK5yLUzilVrkjMAZAZ34DXGpASN8i8pj8g+O+yd10g=
golang.org/x/image v0.22.0/go.mod h1:9hPFhljd4zZ1GNSIZJ49sqbp45GKK9t6w+iXvGqZUz4=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
@@ -575,12 +601,12 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -593,8 +619,6 @@ golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -626,10 +650,10 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -639,10 +663,10 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -654,10 +678,10 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
@@ -768,8 +792,8 @@ mvdan.cc/xurls/v2 v2.5.0 h1:lyBNOm8Wo71UknhUs4QTFUNNMyxy2JEIaKKo0RWOh+8=
mvdan.cc/xurls/v2 v2.5.0/go.mod h1:yQgaGQ1rFtJUzkmKiHYSSfuQxqfYmd//X6PxvholpeE=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
src.techknowlogick.com/xgo v1.8.1-0.20241105013731-313dedef864f h1:Dy7qQ31o3z4EV+0ISDH1IldkPxzubOAvV7DW+9HnbNg=
src.techknowlogick.com/xgo v1.8.1-0.20241105013731-313dedef864f/go.mod h1:31CE1YKtDOrKTk9PSnjTpe6YbO6W/0LTYZ1VskL09oU=
src.techknowlogick.com/xgo v1.7.1-0.20240403232151-e01c4fbef884 h1:Ttvt8FCpUXfC8r3+LgSPrBUIr/JkHmYQtvmOwEET8qE=
src.techknowlogick.com/xgo v1.7.1-0.20240403232151-e01c4fbef884/go.mod h1:31CE1YKtDOrKTk9PSnjTpe6YbO6W/0LTYZ1VskL09oU=
src.techknowlogick.com/xormigrate v1.7.1 h1:RKGLLUAqJ+zO8iZ7eOc7oLH7f0cs2gfXSZSvBRBHnlY=
src.techknowlogick.com/xormigrate v1.7.1/go.mod h1:YGNBdj8prENlySwIKmfoEXp7ILGjAltyKFXD0qLgD7U=
xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
+1 -13
View File
@@ -559,19 +559,7 @@ func (Release) Windows() error {
// Builds binaries for linux
func (Release) Linux() error {
targets := []string{
"linux/amd64",
"linux/arm-5",
"linux/arm-6",
"linux/arm-7",
"linux/arm64",
"linux/mips",
"linux/mipsle",
"linux/mips64",
"linux/mips64le",
"linux/riscv64",
}
return runXgo(strings.Join(targets, ","))
return runXgo("linux/*")
}
// Builds binaries for darwin
-39
View File
@@ -421,43 +421,6 @@ func InitDefaultConfig() {
AutoTLSRenewBefore.setDefault("720h") // 30days in hours
}
func GetConfigValueFromFile(configKey string) string {
if !strings.HasSuffix(configKey, ".file") {
configKey += ".file"
}
var valuePath = viper.GetString(configKey)
if valuePath == "" {
return ""
}
if !strings.HasPrefix(valuePath, "/") {
valuePath = path.Join(ServiceRootpath.GetString(), valuePath)
}
contents, err := os.ReadFile(valuePath)
if err == nil {
return strings.Trim(string(contents), "\n")
}
log.Fatalf("Failed to read the config file at %s for key %s: %v", valuePath, configKey, err)
return ""
}
func readConfigValuesFromFiles() {
keys := viper.AllKeys()
for _, key := range keys {
if strings.Contains(key, "auth.openid.providers") {
// Setting openid provider values will remove everything but the value from file
continue
}
// Env is evaluated manually at runtime, so we need to check this for each key
value := GetConfigValueFromFile(key)
if value != "" {
viper.Set(strings.TrimSuffix(key, ".file"), value)
}
}
}
// InitConfig initializes the config, sets defaults etc.
func InitConfig() {
@@ -502,8 +465,6 @@ func InitConfig() {
log.Info("No config file found, using default or config from environment variables.")
}
readConfigValuesFromFiles()
if RateLimitStore.GetString() == "keyvalue" {
RateLimitStore.Set(KeyvalueType.GetString())
}
-83
View File
@@ -1,83 +0,0 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-present Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public Licensee as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public Licensee for more details.
//
// You should have received a copy of the GNU Affero General Public Licensee
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package migration
import (
"src.techknowlogick.com/xormigrate"
"xorm.io/xorm"
)
type taskCollection20241118123644 struct {
Filter string `query:"filter" json:"filter"`
}
type projectViews20241118123644New struct {
ID int64 `xorm:"autoincr not null unique pk" json:"id" param:"view"`
Filter *taskCollection20241118123644 `xorm:"json null default null" query:"filter" json:"filter"`
}
func (*projectViews20241118123644New) TableName() string {
return "project_views"
}
type projectViews20241118123644 struct {
ID int64 `xorm:"autoincr not null unique pk" json:"id" param:"view"`
Filter string `xorm:"json null default null" query:"filter" json:"filter"`
}
func (*projectViews20241118123644) TableName() string {
return "project_views"
}
func init() {
migrations = append(migrations, &xormigrate.Migration{
ID: "20241118123644",
Description: "change filter format",
Migrate: func(tx *xorm.Engine) (err error) {
oldViews := []*projectViews20241118123644{}
err = tx.Where("filter != '' AND filter IS NOT NULL").Find(&oldViews)
if err != nil {
return
}
err = tx.Sync(projectViews20241118123644New{})
if err != nil {
return
}
for _, view := range oldViews {
newView := &projectViews20241118123644New{
ID: view.ID,
Filter: &taskCollection20241118123644{
Filter: view.Filter,
},
}
_, err = tx.Where("id = ?", view.ID).Update(newView)
if err != nil {
return
}
}
return
},
Rollback: func(tx *xorm.Engine) error {
return nil
},
})
}
-95
View File
@@ -1,95 +0,0 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-present Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public Licensee as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public Licensee for more details.
//
// You should have received a copy of the GNU Affero General Public Licensee
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package migration
import (
"src.techknowlogick.com/xormigrate"
"xorm.io/xorm"
)
type projectView20241119115012BucketConfiguration struct {
Title string `json:"title"`
Filter string `json:"filter"`
}
type projectView20241119115012 struct {
ID int64 `xorm:"autoincr not null unique pk" json:"id" param:"view"`
BucketConfiguration []*projectView20241119115012BucketConfiguration `xorm:"json" json:"bucket_configuration"`
}
func (projectView20241119115012) TableName() string {
return "project_views"
}
type projectView20241119115012BucketConfigurationNew struct {
Title string `json:"title"`
Filter *taskCollection20241118123644 `json:"filter"`
}
type projectView20241119115012New struct {
ID int64 `xorm:"autoincr not null unique pk" json:"id" param:"view"`
BucketConfiguration []*projectView20241119115012BucketConfigurationNew `xorm:"json" json:"bucket_configuration"`
}
func (projectView20241119115012New) TableName() string {
return "project_views"
}
func init() {
migrations = append(migrations, &xormigrate.Migration{
ID: "20241119115012",
Description: "change bucket filter format",
Migrate: func(tx *xorm.Engine) (err error) {
oldViews := []*projectView20241119115012{}
err = tx.Where("bucket_configuration_mode = 2").Find(&oldViews)
if err != nil {
return
}
err = tx.Sync(projectView20241119115012New{})
if err != nil {
return
}
for _, view := range oldViews {
newView := &projectView20241119115012New{
ID: view.ID,
}
for _, configuration := range view.BucketConfiguration {
newView.BucketConfiguration = append(newView.BucketConfiguration, &projectView20241119115012BucketConfigurationNew{
Title: configuration.Title,
Filter: &taskCollection20241118123644{
Filter: configuration.Filter,
},
})
}
_, err = tx.Where("id = ?", view.ID).Update(newView)
if err != nil {
return
}
}
return
},
Rollback: func(tx *xorm.Engine) error {
return nil
},
})
}
+1 -8
View File
@@ -230,14 +230,7 @@ func GetTasksInBucketsForView(s *xorm.Session, view *ProjectView, projects []*Pr
var bucketFilter = taskPropertyBucketID + " = " + strconv.FormatInt(id, 10)
if view.BucketConfigurationMode == BucketConfigurationModeFilter {
bucketFilter = ""
if view.BucketConfiguration[id].Filter.Filter != "" {
bucketFilter = "(" + view.BucketConfiguration[id].Filter.Filter + ")"
}
if view.BucketConfiguration[id].Filter.Search != "" {
opts.search = view.BucketConfiguration[id].Filter.Search
}
bucketFilter = "(" + view.BucketConfiguration[id].Filter + ")"
}
var filterString string
+1 -1
View File
@@ -136,7 +136,7 @@ var FavoritesPseudoProject = Project{
Title: "List",
ViewKind: ProjectViewKindList,
Position: 100,
Filter: &TaskCollection{Filter: "done = false"},
Filter: "done = false",
},
{
ID: -2,
+4 -32
View File
@@ -22,7 +22,6 @@ import (
"time"
"code.vikunja.io/api/pkg/web"
"xorm.io/xorm"
)
@@ -116,8 +115,8 @@ func (p *BucketConfigurationModeKind) UnmarshalJSON(bytes []byte) error {
}
type ProjectViewBucketConfiguration struct {
Title string `json:"title"`
Filter *TaskCollection `json:"filter"`
Title string `json:"title"`
Filter string `json:"filter"`
}
type ProjectView struct {
@@ -131,7 +130,7 @@ type ProjectView struct {
ViewKind ProjectViewKind `xorm:"not null" json:"view_kind"`
// The filter query to match tasks by. Check out https://vikunja.io/docs/filters for a full explanation.
Filter *TaskCollection `xorm:"json null default null" query:"filter" json:"filter"`
Filter string `xorm:"text null default null" query:"filter" json:"filter"`
// The position of this view in the list. The list of all views will be sorted by this parameter.
Position float64 `xorm:"double null" json:"position"`
@@ -274,24 +273,6 @@ func (pv *ProjectView) Create(s *xorm.Session, a web.Auth) (err error) {
}
func createProjectView(s *xorm.Session, p *ProjectView, a web.Auth, createBacklogBucket bool, addExistingTasksToView bool) (err error) {
if p.Filter != nil && p.Filter.Filter != "" {
_, err = getTaskFiltersFromFilterString(p.Filter.Filter, p.Filter.FilterTimezone)
if err != nil {
return
}
}
if p.BucketConfigurationMode == BucketConfigurationModeFilter {
for _, configuration := range p.BucketConfiguration {
if configuration.Filter != nil && configuration.Filter.Filter != "" {
_, err = getTaskFiltersFromFilterString(configuration.Filter.Filter, configuration.Filter.FilterTimezone)
if err != nil {
return
}
}
}
}
p.ID = 0
_, err = s.Insert(p)
if err != nil {
@@ -367,13 +348,6 @@ func addTasksToView(s *xorm.Session, a web.Auth, pv *ProjectView, b *Bucket) (er
// @Failure 500 {object} models.Message "Internal error"
// @Router /projects/{project}/views/{id} [post]
func (pv *ProjectView) Update(s *xorm.Session, _ web.Auth) (err error) {
if pv.Filter != nil && pv.Filter.Filter != "" {
_, err = getTaskFiltersFromFilterString(pv.Filter.Filter, pv.Filter.FilterTimezone)
if err != nil {
return
}
}
// Check if the project view exists
_, err = GetProjectViewByIDAndProject(s, pv.ID, pv.ProjectID)
if err != nil {
@@ -454,9 +428,7 @@ func CreateDefaultViewsForProject(s *xorm.Session, project *Project, a web.Auth,
Position: 100,
}
if createDefaultListFilter {
list.Filter = &TaskCollection{
Filter: "done = false",
}
list.Filter = "done = false"
}
err = createProjectView(s, list, a, createBacklogBucket, true)
if err != nil {
-10
View File
@@ -123,11 +123,6 @@ func (sf *SavedFilter) toProject() *Project {
// @Failure 500 {object} models.Message "Internal error"
// @Router /filters [put]
func (sf *SavedFilter) Create(s *xorm.Session, auth web.Auth) (err error) {
_, err = getTaskFiltersFromFilterString(sf.Filters.Filter, sf.Filters.FilterTimezone)
if err != nil {
return
}
sf.OwnerID = auth.GetID()
sf.ID = 0
_, err = s.Insert(sf)
@@ -195,11 +190,6 @@ func (sf *SavedFilter) Update(s *xorm.Session, _ web.Auth) error {
sf.Filters = origFilter.Filters
}
_, err = getTaskFiltersFromFilterString(sf.Filters.Filter, sf.Filters.FilterTimezone)
if err != nil {
return err
}
_, err = s.
Where("id = ?", sf.ID).
Cols(
+26 -64
View File
@@ -46,56 +46,34 @@ func TestSavedFilter_getFilterIDFromProjectID(t *testing.T) {
}
func TestSavedFilter_Create(t *testing.T) {
t.Run("empty filter", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
s := db.NewSession()
defer s.Close()
db.LoadAndAssertFixtures(t)
s := db.NewSession()
defer s.Close()
sf := &SavedFilter{
Title: "test",
Description: "Lorem Ipsum dolor sit amet",
Filters: &TaskCollection{}, // Empty filter
}
sf := &SavedFilter{
Title: "test",
Description: "Lorem Ipsum dolor sit amet",
Filters: &TaskCollection{}, // Empty filter
}
u := &user.User{ID: 1}
err := sf.Create(s, u)
require.NoError(t, err)
assert.Equal(t, u.ID, sf.OwnerID)
err = s.Commit()
require.NoError(t, err)
vals := map[string]interface{}{
"title": "'test'",
"description": "'Lorem Ipsum dolor sit amet'",
"filters": `'{"s":"","sort_by":null,"order_by":null,"filter":"","filter_include_nulls":false}'`,
"owner_id": 1,
}
// Postgres can't compare json values directly, see https://dba.stackexchange.com/a/106290/210721
if db.Type() == schemas.POSTGRES {
vals["filters::jsonb"] = vals["filters"].(string) + "::jsonb"
delete(vals, "filters")
}
db.AssertExists(t, "saved_filters", vals, true)
})
t.Run("invalid filter string", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
s := db.NewSession()
defer s.Close()
sf := &SavedFilter{
Title: "test",
Description: "Lorem Ipsum dolor sit amet",
Filters: &TaskCollection{
Filter: "foo = value",
},
}
u := &user.User{ID: 1}
err := sf.Create(s, u)
require.Error(t, err)
db.AssertMissing(t, "saved_filters", map[string]interface{}{
"title": "test",
})
})
u := &user.User{ID: 1}
err := sf.Create(s, u)
require.NoError(t, err)
assert.Equal(t, u.ID, sf.OwnerID)
err = s.Commit()
require.NoError(t, err)
vals := map[string]interface{}{
"title": "'test'",
"description": "'Lorem Ipsum dolor sit amet'",
"filters": "'{\"sort_by\":null,\"order_by\":null,\"filter\":\"\",\"filter_include_nulls\":false}'",
"owner_id": 1,
}
// Postgres can't compare json values directly, see https://dba.stackexchange.com/a/106290/210721
if db.Type() == schemas.POSTGRES {
vals["filters::jsonb"] = vals["filters"].(string) + "::jsonb"
delete(vals, "filters")
}
db.AssertExists(t, "saved_filters", vals, true)
}
func TestSavedFilter_ReadOne(t *testing.T) {
@@ -156,22 +134,6 @@ func TestSavedFilter_Update(t *testing.T) {
"is_favorite": true,
}, false)
})
t.Run("invalid filter string", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
s := db.NewSession()
defer s.Close()
sf := &SavedFilter{
ID: 1,
Title: "NewTitle",
Description: "", // Explicitly reset the description
Filters: &TaskCollection{
Filter: "foo = bar",
},
}
err := sf.Update(s, &user.User{ID: 1})
require.Error(t, err)
})
}
func TestSavedFilter_Delete(t *testing.T) {
+11 -30
View File
@@ -32,8 +32,6 @@ type TaskCollection struct {
ProjectID int64 `param:"project" json:"-"`
ProjectViewID int64 `param:"view" json:"-"`
Search string `query:"s" json:"s"`
// The query parameter to sort by. This is for ex. done, priority, etc.
SortBy []string `query:"sort_by" json:"sort_by"`
SortByArr []string `query:"sort_by[]" json:"-"`
@@ -183,10 +181,6 @@ func getRelevantProjectsFromCollection(s *xorm.Session, a web.Auth, tf *TaskColl
}
func getFilterValueForBucketFilter(filter string, view *ProjectView) (newFilter string, err error) {
if view.BucketConfigurationMode != BucketConfigurationModeFilter {
return filter, nil
}
re := regexp.MustCompile(`bucket_id\s*=\s*(\d+)`)
match := re.FindStringSubmatch(filter)
@@ -201,7 +195,7 @@ func getFilterValueForBucketFilter(filter string, view *ProjectView) (newFilter
for id, bucket := range view.BucketConfiguration {
if id == bucketID {
return re.ReplaceAllString(filter, `(`+bucket.Filter.Filter+`)`), nil
return re.ReplaceAllString(filter, `(`+bucket.Filter+`)`), nil
}
}
@@ -283,34 +277,21 @@ func (tf *TaskCollection) ReadAll(s *xorm.Session, a web.Auth, search string, pa
return nil, 0, 0, err
}
if view.Filter != nil {
if view.Filter.Filter != "" {
if tf.Filter != "" {
tf.Filter = "(" + tf.Filter + ") && (" + view.Filter.Filter + ")"
} else {
tf.Filter = view.Filter.Filter
}
tf.FilterIncludeNulls = view.Filter.FilterIncludeNulls
}
if view.Filter.FilterTimezone != "" {
tf.FilterTimezone = view.Filter.FilterTimezone
}
if view.Filter.FilterIncludeNulls {
tf.FilterIncludeNulls = view.Filter.FilterIncludeNulls
}
if view.Filter.Search != "" {
search = view.Filter.Search
if view.Filter != "" {
if tf.Filter != "" {
tf.Filter = "(" + tf.Filter + ") && (" + view.Filter + ")"
} else {
tf.Filter = view.Filter
}
}
if strings.Contains(tf.Filter, taskPropertyBucketID) {
filteringForBucket = true
tf.Filter, err = getFilterValueForBucketFilter(tf.Filter, view)
if err != nil {
return nil, 0, 0, err
if view.BucketConfigurationMode == BucketConfigurationModeFilter {
tf.Filter, err = getFilterValueForBucketFilter(tf.Filter, view)
if err != nil {
return nil, 0, 0, err
}
}
}
}
+1 -1
View File
@@ -59,7 +59,7 @@ func getOrderByDBStatement(opts *taskSearchOptions) (orderby string, err error)
prefix = "task_positions."
}
if param.sortBy == taskPropertyID || param.sortBy == taskPropertyCreated || param.sortBy == taskPropertyUpdated {
if param.sortBy == taskPropertyID {
prefix = "tasks."
}
+2 -1
View File
@@ -21,8 +21,9 @@ import (
"net/http"
"time"
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/db"
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/api/pkg/web"
+3 -3
View File
@@ -17,13 +17,13 @@
package openid
import (
"code.vikunja.io/api/pkg/cron"
"code.vikunja.io/api/pkg/db"
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/models"
"xorm.io/builder"
"xorm.io/xorm"
"code.vikunja.io/api/pkg/cron"
"code.vikunja.io/api/pkg/db"
)
func RemoveEmptySSOTeams(s *xorm.Session) (err error) {
+4 -3
View File
@@ -24,14 +24,14 @@ import (
"strconv"
"strings"
"code.vikunja.io/api/pkg/web/handler"
"code.vikunja.io/api/pkg/db"
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/modules/auth"
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/api/pkg/utils"
"code.vikunja.io/api/pkg/web/handler"
"github.com/coreos/go-oidc/v3/oidc"
petname "github.com/dustinkirkland/golang-petname"
"github.com/labstack/echo/v4"
@@ -100,6 +100,7 @@ func HandleCallback(c echo.Context) error {
provider, err := GetProvider(providerKey)
log.Debugf("Provider: %v", provider)
if err != nil {
log.Error(err)
return handler.HandleHTTPError(err)
}
if provider == nil {
@@ -113,6 +114,7 @@ func HandleCallback(c echo.Context) error {
if err != nil {
var rerr *oauth2.RetrieveError
if errors.As(err, &rerr) {
log.Error(err)
details := make(map[string]interface{})
if err := json.Unmarshal(rerr.Body, &details); err != nil {
@@ -120,7 +122,6 @@ func HandleCallback(c echo.Context) error {
return handler.HandleHTTPError(err)
}
log.Error(err)
return c.JSON(http.StatusBadRequest, map[string]interface{}{
"message": "Could not authenticate against third party.",
"details": details,
+24 -36
View File
@@ -17,12 +17,14 @@
package openid
import (
"regexp"
"strconv"
"strings"
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/modules/keyvalue"
"github.com/coreos/go-oidc/v3/oidc"
"golang.org/x/oauth2"
)
@@ -41,18 +43,14 @@ func GetAllProviders() (providers []*Provider, err error) {
return nil, nil
}
rawProvider, is := rawProviders.(map[string]interface{})
if !is {
log.Criticalf("It looks like your openid configuration is in the wrong format. Please check the docs for the correct format.")
return
}
rawProvider := rawProviders.([]interface{})
for key, p := range rawProvider {
for _, p := range rawProvider {
var pi map[string]interface{}
var is bool
pi, is = p.(map[string]interface{})
// JSON config is a map[string]interface{}, other providers are not. Under the hood they are all strings so
// it is safe to cast.
// it is save to cast.
if !is {
pis := p.(map[interface{}]interface{})
pi = make(map[string]interface{}, len(pis))
@@ -61,21 +59,21 @@ func GetAllProviders() (providers []*Provider, err error) {
}
}
provider, err := getProviderFromMap(pi, key)
provider, err := getProviderFromMap(pi)
if err != nil {
log.Errorf("Error while getting openid provider %s: %s", key, err)
continue
}
if provider == nil {
log.Errorf("Could not openid provider %s, please check your config", key)
if provider != nil {
log.Errorf("Error while getting openid provider %s: %s", provider.Name, err)
continue
}
log.Errorf("Error while getting openid provider: %s", err)
continue
}
providers = append(providers, provider)
err = keyvalue.Put("openid_provider_"+key, provider)
k := getKeyFromName(pi["name"].(string))
err = keyvalue.Put("openid_provider_"+k, provider)
if err != nil {
return nil, err
}
@@ -109,29 +107,19 @@ func GetProvider(key string) (provider *Provider, err error) {
return
}
func getProviderFromMap(pi map[string]interface{}, key string) (provider *Provider, err error) {
for _, configKey := range []string{
// Values from environment variables are evaluated at runtime, hence we need to check them explicitly
// through viper to make sure we catch all of them.
"name",
"logouturl",
"scope",
"authurl",
"clientsecret",
"clientid",
} {
valueFromFile := config.GetConfigValueFromFile("auth.openid.providers." + key + "." + configKey)
if valueFromFile != "" {
pi[configKey] = valueFromFile
}
}
func getKeyFromName(name string) string {
reg := regexp.MustCompile("[^a-z0-9]+")
return reg.ReplaceAllString(strings.ToLower(name), "")
}
func getProviderFromMap(pi map[string]interface{}) (provider *Provider, err error) {
name, is := pi["name"].(string)
if !is {
return nil, nil
}
k := getKeyFromName(name)
logoutURL, ok := pi["logouturl"].(string)
if !ok {
logoutURL = ""
@@ -142,8 +130,8 @@ func getProviderFromMap(pi map[string]interface{}, key string) (provider *Provid
scope = "openid profile email"
}
provider = &Provider{
Name: name,
Key: key,
Name: pi["name"].(string),
Key: k,
AuthURL: pi["authurl"].(string),
OriginalAuthURL: pi["authurl"].(string),
ClientSecret: pi["clientsecret"].(string),
-4
View File
@@ -17,7 +17,6 @@
package v1
import (
"errors"
"net/http"
"strings"
@@ -73,9 +72,6 @@ func UploadTaskAttachment(c echo.Context) error {
form, err := c.MultipartForm()
if err != nil {
_ = s.Rollback()
if errors.Is(err, http.ErrNotMultipart) {
return echo.NewHTTPError(http.StatusBadRequest, "No multipart form provided")
}
return handler.HandleHTTPError(err)
}