forked from vikunja/vikunja
Compare commits
10 Commits
f6204c307e
...
43dff328e5
Author | SHA1 | Date |
---|---|---|
Daniel Herrmann | 43dff328e5 | |
Daniel Herrmann | b0f5779dab | |
Daniel Herrmann | c44d9ef732 | |
Daniel Herrmann | 19aeedc568 | |
Daniel Herrmann | 2253e1b5e4 | |
kolaente | e65c3ffe6b | |
renovate | 9ad7bc5932 | |
kolaente | 2101f37aa2 | |
renovate | e8d77e61e4 | |
renovate | 7e69c7cbe0 |
19
.drone.yml
19
.drone.yml
|
@ -3,6 +3,11 @@ kind: pipeline
|
|||
type: docker
|
||||
name: build-and-test
|
||||
|
||||
trigger:
|
||||
event:
|
||||
exclude:
|
||||
- cron
|
||||
|
||||
workspace:
|
||||
base: /go
|
||||
path: src/code.vikunja.io/api
|
||||
|
@ -528,6 +533,9 @@ trigger:
|
|||
ref:
|
||||
- refs/heads/main
|
||||
- "refs/tags/**"
|
||||
event:
|
||||
exclude:
|
||||
- cron
|
||||
|
||||
steps:
|
||||
# Needed to get the versions right as they depend on tags
|
||||
|
@ -808,6 +816,9 @@ trigger:
|
|||
ref:
|
||||
- refs/heads/main
|
||||
- "refs/tags/**"
|
||||
event:
|
||||
exclude:
|
||||
- cron
|
||||
|
||||
steps:
|
||||
- name: fetch-tags
|
||||
|
@ -1145,6 +1156,9 @@ trigger:
|
|||
ref:
|
||||
- refs/heads/main
|
||||
- "refs/tags/**"
|
||||
event:
|
||||
exclude:
|
||||
- cron
|
||||
|
||||
steps:
|
||||
- name: fetch-tags
|
||||
|
@ -1360,6 +1374,9 @@ trigger:
|
|||
ref:
|
||||
- refs/heads/main
|
||||
- "refs/tags/**"
|
||||
event:
|
||||
exclude:
|
||||
- cron
|
||||
|
||||
depends_on:
|
||||
- build-and-test
|
||||
|
@ -1384,6 +1401,6 @@ steps:
|
|||
- failure
|
||||
---
|
||||
kind: signature
|
||||
hmac: 008b86263a8d03806da907c128a837a380901f1a2190a658c22d4e06cadc1b64
|
||||
hmac: a569410ea13ad83c15c7606ed44b17b6bac0eb66d668344dfbf008c9448b4af5
|
||||
|
||||
...
|
||||
|
|
|
@ -62,6 +62,9 @@ service:
|
|||
allowiconchanges: true
|
||||
# Allow using a custom logo via external URL.
|
||||
customlogourl: ''
|
||||
# Enables the public team feature. If enabled, it is possible to configure teams to be public, which makes them
|
||||
# discoverable when sharing a project, therefore not only showing teams the user is member of.
|
||||
enablepublicteams: false
|
||||
|
||||
sentry:
|
||||
# If set to true, enables anonymous error tracking of api errors via Sentry. This allows us to gather more
|
||||
|
|
|
@ -346,6 +346,17 @@ Full path: `service.customlogourl`
|
|||
Environment path: `VIKUNJA_SERVICE_CUSTOMLOGOURL`
|
||||
|
||||
|
||||
### enablepublicteams
|
||||
|
||||
discoverable when sharing a project, therefore not only showing teams the user is member of.
|
||||
|
||||
Default: `false`
|
||||
|
||||
Full path: `service.enablepublicteams`
|
||||
|
||||
Environment path: `VIKUNJA_SERVICE_ENABLEPUBLICTEAMS`
|
||||
|
||||
|
||||
---
|
||||
|
||||
## sentry
|
||||
|
|
|
@ -95,7 +95,7 @@ It depends on the provider being used as well as the preferences of the administ
|
|||
Typically you'd want to request an additional scope (e.g. `vikunja_scope`) which then triggers the identity provider to add the claim.
|
||||
If the `vikunja_groups` is part of the **ID token**, Vikunja will start the procedure and import teams and team memberships.
|
||||
|
||||
The claim structure expexted by Vikunja is as follows:
|
||||
The minimal claim structure expected by Vikunja is as follows:
|
||||
|
||||
```json
|
||||
{
|
||||
|
@ -112,6 +112,21 @@ The claim structure expexted by Vikunja is as follows:
|
|||
}
|
||||
```
|
||||
|
||||
It also also possible to pass the description and isPublic flag as optional parameter. If not present, the description will be empty and project visibility defaults to false.
|
||||
|
||||
```json
|
||||
{
|
||||
"vikunja_groups": [
|
||||
{
|
||||
"name": "team 3",
|
||||
"oidcID": 33349,
|
||||
"description": "My Team Description",
|
||||
"isPublic": true
|
||||
},
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
For each team, you need to define a team `name` and an `oidcID`, where the `oidcID` can be any string with a length of less than 250 characters.
|
||||
The `oidcID` is used to uniquely identify the team, so please make sure to keep this unique.
|
||||
|
||||
|
|
|
@ -146,9 +146,9 @@
|
|||
"@types/sortablejs": "1.15.8",
|
||||
"@typescript-eslint/eslint-plugin": "7.1.1",
|
||||
"@typescript-eslint/parser": "7.1.1",
|
||||
"@vitejs/plugin-legacy": "5.3.1",
|
||||
"@vitejs/plugin-legacy": "5.3.2",
|
||||
"@vitejs/plugin-vue": "5.0.4",
|
||||
"@vue/eslint-config-typescript": "12.0.0",
|
||||
"@vue/eslint-config-typescript": "13.0.0",
|
||||
"@vue/test-utils": "2.4.4",
|
||||
"@vue/tsconfig": "0.5.1",
|
||||
"autoprefixer": "10.4.18",
|
||||
|
@ -160,7 +160,7 @@
|
|||
"esbuild": "0.20.1",
|
||||
"eslint": "8.57.0",
|
||||
"eslint-plugin-vue": "9.22.0",
|
||||
"happy-dom": "13.6.2",
|
||||
"happy-dom": "13.7.0",
|
||||
"histoire": "0.17.9",
|
||||
"postcss": "8.4.35",
|
||||
"postcss-easing-gradients": "3.0.1",
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -920,7 +920,9 @@
|
|||
"description": "Description",
|
||||
"descriptionPlaceholder": "Describe the team here, hit '/' for more options…",
|
||||
"admin": "Admin",
|
||||
"member": "Member"
|
||||
"member": "Member",
|
||||
"isPublic": "Visibility",
|
||||
"isPublicDescription": "Make team publicly discoverable"
|
||||
}
|
||||
},
|
||||
"keyboardShortcuts": {
|
||||
|
|
|
@ -10,6 +10,7 @@ export interface ITeam extends IAbstract {
|
|||
members: ITeamMember[]
|
||||
right: Right
|
||||
oidcId: string
|
||||
isPublic: boolean
|
||||
|
||||
createdBy: IUser
|
||||
created: Date
|
||||
|
|
|
@ -14,6 +14,7 @@ export default class TeamModel extends AbstractModel<ITeam> implements ITeam {
|
|||
members: ITeamMember[] = []
|
||||
right: Right = RIGHTS.READ
|
||||
oidcId = ''
|
||||
isPublic: boolean = false
|
||||
|
||||
createdBy: IUser = {} // FIXME: seems wrong
|
||||
created: Date = null
|
||||
|
|
|
@ -37,6 +37,7 @@ export interface ConfigState {
|
|||
providers: IProvider[],
|
||||
},
|
||||
},
|
||||
publicTeamsEnabled: boolean,
|
||||
}
|
||||
|
||||
export const useConfigStore = defineStore('config', () => {
|
||||
|
@ -70,6 +71,7 @@ export const useConfigStore = defineStore('config', () => {
|
|||
providers: [],
|
||||
},
|
||||
},
|
||||
publicTeamsEnabled: false,
|
||||
})
|
||||
|
||||
const migratorsEnabled = computed(() => state.availableMigrators?.length > 0)
|
||||
|
|
|
@ -33,6 +33,27 @@
|
|||
>
|
||||
{{ $t('team.attributes.nameRequired') }}
|
||||
</p>
|
||||
<div
|
||||
v-if="configStore.publicTeamsEnabled"
|
||||
class="field"
|
||||
>
|
||||
<label
|
||||
class="label"
|
||||
for="teamIsPublic"
|
||||
>{{ $t('team.attributes.isPublic') }}</label>
|
||||
<div
|
||||
class="control is-expanded"
|
||||
:class="{ 'is-loading': teamService.loading }"
|
||||
>
|
||||
<Fancycheckbox
|
||||
v-model="team.isPublic"
|
||||
:disabled="teamMemberService.loading || undefined"
|
||||
:class="{ 'disabled': teamService.loading }"
|
||||
>
|
||||
{{ $t('team.attributes.isPublicDescription') }}
|
||||
</Fancycheckbox>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label
|
||||
class="label"
|
||||
|
@ -241,6 +262,7 @@ import {useI18n} from 'vue-i18n'
|
|||
import {useRoute, useRouter} from 'vue-router'
|
||||
|
||||
import Editor from '@/components/input/AsyncEditor'
|
||||
import Fancycheckbox from '@/components/input/fancycheckbox.vue'
|
||||
import Multiselect from '@/components/input/multiselect.vue'
|
||||
import User from '@/components/misc/user.vue'
|
||||
|
||||
|
@ -253,12 +275,14 @@ import {RIGHTS as Rights} from '@/constants/rights'
|
|||
import {useTitle} from '@/composables/useTitle'
|
||||
import {success} from '@/message'
|
||||
import {useAuthStore} from '@/stores/auth'
|
||||
import {useConfigStore} from '@/stores/config'
|
||||
|
||||
import type {ITeam} from '@/modelTypes/ITeam'
|
||||
import type {IUser} from '@/modelTypes/IUser'
|
||||
import type {ITeamMember} from '@/modelTypes/ITeamMember'
|
||||
|
||||
const authStore = useAuthStore()
|
||||
const configStore = useConfigStore()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const {t} = useI18n({useScope: 'global'})
|
||||
|
@ -305,6 +329,8 @@ async function save() {
|
|||
}
|
||||
showErrorTeamnameRequired.value = false
|
||||
|
||||
console.log('team.value', team.value)
|
||||
|
||||
team.value = await teamService.value.update(team.value)
|
||||
success({message: t('team.edit.success')})
|
||||
}
|
||||
|
|
|
@ -25,6 +25,26 @@
|
|||
>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="configStore.publicTeamsEnabled"
|
||||
class="field"
|
||||
>
|
||||
<label
|
||||
class="label"
|
||||
for="teamIsPublic"
|
||||
>{{ $t('team.attributes.isPublic') }}</label>
|
||||
<div
|
||||
class="control is-expanded"
|
||||
:class="{ 'is-loading': teamService.loading }"
|
||||
>
|
||||
<Fancycheckbox
|
||||
v-model="team.isPublic"
|
||||
:class="{ 'disabled': teamService.loading }"
|
||||
>
|
||||
{{ $t('team.attributes.isPublicDescription') }}
|
||||
</Fancycheckbox>
|
||||
</div>
|
||||
</div>
|
||||
<p
|
||||
v-if="showError && team.name === ''"
|
||||
class="help is-danger"
|
||||
|
@ -46,11 +66,14 @@ import TeamModel from '@/models/team'
|
|||
import TeamService from '@/services/team'
|
||||
|
||||
import CreateEdit from '@/components/misc/create-edit.vue'
|
||||
import Fancycheckbox from '@/components/input/fancycheckbox.vue'
|
||||
|
||||
import {useTitle} from '@/composables/useTitle'
|
||||
import {useRouter} from 'vue-router'
|
||||
import {success} from '@/message'
|
||||
|
||||
import {useConfigStore} from '@/stores/config'
|
||||
|
||||
const {t} = useI18n()
|
||||
const title = computed(() => t('team.create.title'))
|
||||
useTitle(title)
|
||||
|
@ -60,6 +83,8 @@ const teamService = shallowReactive(new TeamService())
|
|||
const team = reactive(new TeamModel())
|
||||
const showError = ref(false)
|
||||
|
||||
const configStore = useConfigStore()
|
||||
|
||||
async function newTeam() {
|
||||
if (team.name === '') {
|
||||
showError.value = true
|
||||
|
|
3
go.mod
3
go.mod
|
@ -32,7 +32,7 @@ require (
|
|||
github.com/dustinkirkland/golang-petname v0.0.0-20231002161417-6a283f1aaaf2
|
||||
github.com/gabriel-vasile/mimetype v1.4.3
|
||||
github.com/getsentry/sentry-go v0.27.0
|
||||
github.com/go-sql-driver/mysql v1.7.1
|
||||
github.com/go-sql-driver/mysql v1.8.0
|
||||
github.com/go-testfixtures/testfixtures/v3 v3.10.0
|
||||
github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||
|
@ -83,6 +83,7 @@ require (
|
|||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/ClickHouse/ch-go v0.58.2 // indirect
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.18.0 // indirect
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
|
|
4
go.sum
4
go.sum
|
@ -2,6 +2,8 @@ code.vikunja.io/web v0.0.0-20210706160506-d85def955bd3 h1:MXl7Ff9a/ndTpuEmQKIGhq
|
|||
code.vikunja.io/web v0.0.0-20210706160506-d85def955bd3/go.mod h1:OgFO06HN1KpA4S7Dw/QAIeygiUPSeGJJn1ykz/sjZdU=
|
||||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
|
||||
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
|
||||
gitee.com/travelliu/dm v1.8.11192/go.mod h1:DHTzyhCrM843x9VdKVbZ+GKXGRbKM2sJ4LxihRxShkE=
|
||||
|
@ -150,6 +152,8 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB
|
|||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
||||
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/go-sql-driver/mysql v1.8.0 h1:UtktXaU2Nb64z/pLiGIxY4431SJ4/dR5cjMmlVHgnT4=
|
||||
github.com/go-sql-driver/mysql v1.8.0/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/go-testfixtures/testfixtures/v3 v3.10.0 h1:BrBwN7AuC+74g5qtk9D59TLGOaEa8Bw1WmIsf+SyzWc=
|
||||
|
|
|
@ -64,6 +64,7 @@ const (
|
|||
ServiceMaxAvatarSize Key = `service.maxavatarsize`
|
||||
ServiceAllowIconChanges Key = `service.allowiconchanges`
|
||||
ServiceCustomLogoURL Key = `service.customlogourl`
|
||||
ServiceEnablePublicTeams Key = `service.enablepublicteams`
|
||||
|
||||
SentryEnabled Key = `sentry.enabled`
|
||||
SentryDsn Key = `sentry.dsn`
|
||||
|
@ -312,6 +313,7 @@ func InitDefaultConfig() {
|
|||
ServiceMaxAvatarSize.setDefault(1024)
|
||||
ServiceDemoMode.setDefault(false)
|
||||
ServiceAllowIconChanges.setDefault(true)
|
||||
ServiceEnablePublicTeams.setDefault(false)
|
||||
|
||||
// Sentry
|
||||
SentryDsn.setDefault("https://440eedc957d545a795c17bbaf477497c@o1047380.ingest.sentry.io/4504254983634944")
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
// 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 teams20240309111148 struct {
|
||||
IsPublic bool `xorm:"not null default 0" json:"is_public"`
|
||||
}
|
||||
|
||||
func (teams20240309111148) TableName() string {
|
||||
return "teams"
|
||||
}
|
||||
|
||||
func init() {
|
||||
migrations = append(migrations, &xormigrate.Migration{
|
||||
ID: "20240309111148",
|
||||
Description: "Add IsPublic field to teams table to control discoverability of teams.",
|
||||
Migrate: func(tx *xorm.Engine) error {
|
||||
return tx.Sync2(teams20240309111148{})
|
||||
},
|
||||
Rollback: func(tx *xorm.Engine) error {
|
||||
return nil
|
||||
},
|
||||
})
|
||||
}
|
|
@ -53,6 +53,9 @@ type Team struct {
|
|||
// A timestamp when this relation was last updated. You cannot change this value.
|
||||
Updated time.Time `xorm:"updated" json:"updated"`
|
||||
|
||||
// Defines wether the team should be publicly discoverable when sharing a project
|
||||
IsPublic bool `xorm:"not null default 0" json:"is_public"`
|
||||
|
||||
web.CRUDable `xorm:"-" json:"-"`
|
||||
web.Rights `xorm:"-" json:"-"`
|
||||
}
|
||||
|
@ -100,6 +103,7 @@ type OIDCTeam struct {
|
|||
Name string
|
||||
OidcID string
|
||||
Description string
|
||||
IsPublic bool
|
||||
}
|
||||
|
||||
// GetTeamByID gets a team by its ID
|
||||
|
@ -398,7 +402,7 @@ func (t *Team) Update(s *xorm.Session, _ web.Auth) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
_, err = s.ID(t.ID).Update(t)
|
||||
_, err = s.ID(t.ID).UseBool("is_public").Update(t)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -298,14 +298,27 @@ func getTeamDataFromToken(groups []map[string]interface{}, provider *Provider) (
|
|||
var name string
|
||||
var description string
|
||||
var oidcID string
|
||||
var IsPublic bool
|
||||
|
||||
// Read name
|
||||
_, exists := team["name"]
|
||||
if exists {
|
||||
name = team["name"].(string)
|
||||
}
|
||||
|
||||
// Read description
|
||||
_, exists = team["description"]
|
||||
if exists {
|
||||
description = team["description"].(string)
|
||||
}
|
||||
|
||||
// Read isPublic flag
|
||||
_, exists = team["isPublic"]
|
||||
if exists {
|
||||
IsPublic = team["isPublic"].(bool)
|
||||
}
|
||||
|
||||
// Read oidcID
|
||||
_, exists = team["oidcID"]
|
||||
if exists {
|
||||
switch t := team["oidcID"].(type) {
|
||||
|
@ -324,7 +337,7 @@ func getTeamDataFromToken(groups []map[string]interface{}, provider *Provider) (
|
|||
errs = append(errs, &user.ErrOpenIDCustomScopeMalformed{})
|
||||
continue
|
||||
}
|
||||
teamData = append(teamData, &models.OIDCTeam{Name: name, OidcID: oidcID, Description: description})
|
||||
teamData = append(teamData, &models.OIDCTeam{Name: name, OidcID: oidcID, Description: description, IsPublic: IsPublic})
|
||||
}
|
||||
return teamData, errs
|
||||
}
|
||||
|
@ -339,6 +352,7 @@ func CreateOIDCTeam(s *xorm.Session, teamData *models.OIDCTeam, u *user.User, is
|
|||
Description: teamData.Description,
|
||||
OidcID: teamData.OidcID,
|
||||
Issuer: issuer,
|
||||
IsPublic: teamData.IsPublic,
|
||||
}
|
||||
err = team.CreateNewTeam(s, u, false)
|
||||
return team, err
|
||||
|
@ -363,12 +377,24 @@ func GetOrCreateTeamsByOIDC(s *xorm.Session, teamData []*models.OIDCTeam, u *use
|
|||
continue
|
||||
}
|
||||
|
||||
// Compare the name and update if it changed
|
||||
if team.Name != getOIDCTeamName(oidcTeam.Name) {
|
||||
team.Name = getOIDCTeamName(oidcTeam.Name)
|
||||
err = team.Update(s, u)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Compare the description and update if it changed
|
||||
if team.Description != oidcTeam.Description {
|
||||
team.Description = oidcTeam.Description
|
||||
}
|
||||
|
||||
// Compare the isPublic flag and update if it changed
|
||||
if team.IsPublic != oidcTeam.IsPublic {
|
||||
team.IsPublic = oidcTeam.IsPublic
|
||||
}
|
||||
|
||||
err = team.Update(s, u)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debugf("Team with oidc_id %v and name %v already exists.", team.OidcID, team.Name)
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
package trello
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"code.vikunja.io/api/pkg/config"
|
||||
"code.vikunja.io/api/pkg/files"
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
|
@ -24,6 +26,7 @@ import (
|
|||
"code.vikunja.io/api/pkg/modules/migration"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
"github.com/adlio/trello"
|
||||
"github.com/yuin/goldmark"
|
||||
)
|
||||
|
||||
// Migration represents the trello migration struct
|
||||
|
@ -160,6 +163,16 @@ func getTrelloData(token string) (trelloData []*trello.Board, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func convertMarkdownToHTML(input string) (output string, err error) {
|
||||
var buf bytes.Buffer
|
||||
err = goldmark.Convert([]byte(input), &buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
//#nosec - we are not responsible to escape this as we don't know the context where it is used
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
// Converts all previously obtained data from trello into the vikunja format.
|
||||
// `trelloData` should contain all boards with their projects and cards respectively.
|
||||
func convertTrelloDataToVikunja(trelloData []*trello.Board, token string) (fullVikunjaHierachie []*models.ProjectWithTasksAndBuckets, err error) {
|
||||
|
@ -220,11 +233,15 @@ func convertTrelloDataToVikunja(trelloData []*trello.Board, token string) (fullV
|
|||
// The usual stuff: Title, description, position, bucket id
|
||||
task := &models.Task{
|
||||
Title: card.Name,
|
||||
Description: card.Desc,
|
||||
KanbanPosition: card.Pos,
|
||||
BucketID: bucketID,
|
||||
}
|
||||
|
||||
task.Description, err = convertMarkdownToHTML(card.Desc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if card.Due != nil {
|
||||
task.DueDate = *card.Due
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ func TestConvertTrelloToVikunja(t *testing.T) {
|
|||
Cards: []*trello.Card{
|
||||
{
|
||||
Name: "Test Card 1",
|
||||
Desc: "Card Description",
|
||||
Desc: "Card Description **bold**",
|
||||
Pos: 123,
|
||||
Due: &time1,
|
||||
Labels: []*trello.Label{
|
||||
|
@ -218,7 +218,7 @@ func TestConvertTrelloToVikunja(t *testing.T) {
|
|||
{
|
||||
Task: models.Task{
|
||||
Title: "Test Card 1",
|
||||
Description: "Card Description",
|
||||
Description: "<p>Card Description <strong>bold</strong></p>\n",
|
||||
BucketID: 1,
|
||||
KanbanPosition: 123,
|
||||
DueDate: time1,
|
||||
|
|
|
@ -51,6 +51,7 @@ type vikunjaInfos struct {
|
|||
TaskCommentsEnabled bool `json:"task_comments_enabled"`
|
||||
DemoModeEnabled bool `json:"demo_mode_enabled"`
|
||||
WebhooksEnabled bool `json:"webhooks_enabled"`
|
||||
PublicTeamsEnabled bool `json:"public_teams_enabled"`
|
||||
}
|
||||
|
||||
type authInfo struct {
|
||||
|
@ -95,6 +96,7 @@ func Info(c echo.Context) error {
|
|||
TaskCommentsEnabled: config.ServiceEnableTaskComments.GetBool(),
|
||||
DemoModeEnabled: config.ServiceDemoMode.GetBool(),
|
||||
WebhooksEnabled: config.WebhooksEnabled.GetBool(),
|
||||
PublicTeamsEnabled: config.ServiceEnablePublicTeams.GetBool(),
|
||||
AvailableMigrators: []string{
|
||||
(&vikunja_file.FileMigrator{}).Name(),
|
||||
(&ticktick.Migrator{}).Name(),
|
||||
|
|
|
@ -8287,6 +8287,10 @@ const docTemplate = `{
|
|||
"description": "The unique, numeric id of this team.",
|
||||
"type": "integer"
|
||||
},
|
||||
"is_public": {
|
||||
"description": "Defines wether the team should be publicly discoverable when sharing a project",
|
||||
"type": "boolean"
|
||||
},
|
||||
"members": {
|
||||
"description": "An array of all members in this team.",
|
||||
"type": "array",
|
||||
|
@ -8422,6 +8426,10 @@ const docTemplate = `{
|
|||
"description": "The unique, numeric id of this team.",
|
||||
"type": "integer"
|
||||
},
|
||||
"is_public": {
|
||||
"description": "Defines wether the team should be publicly discoverable when sharing a project",
|
||||
"type": "boolean"
|
||||
},
|
||||
"members": {
|
||||
"description": "An array of all members in this team.",
|
||||
"type": "array",
|
||||
|
@ -8944,6 +8952,9 @@ const docTemplate = `{
|
|||
"motd": {
|
||||
"type": "string"
|
||||
},
|
||||
"public_teams_enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"registration_enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
|
|
@ -8279,6 +8279,10 @@
|
|||
"description": "The unique, numeric id of this team.",
|
||||
"type": "integer"
|
||||
},
|
||||
"is_public": {
|
||||
"description": "Defines wether the team should be publicly discoverable when sharing a project",
|
||||
"type": "boolean"
|
||||
},
|
||||
"members": {
|
||||
"description": "An array of all members in this team.",
|
||||
"type": "array",
|
||||
|
@ -8414,6 +8418,10 @@
|
|||
"description": "The unique, numeric id of this team.",
|
||||
"type": "integer"
|
||||
},
|
||||
"is_public": {
|
||||
"description": "Defines wether the team should be publicly discoverable when sharing a project",
|
||||
"type": "boolean"
|
||||
},
|
||||
"members": {
|
||||
"description": "An array of all members in this team.",
|
||||
"type": "array",
|
||||
|
@ -8936,6 +8944,9 @@
|
|||
"motd": {
|
||||
"type": "string"
|
||||
},
|
||||
"public_teams_enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"registration_enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
|
|
@ -894,6 +894,10 @@ definitions:
|
|||
id:
|
||||
description: The unique, numeric id of this team.
|
||||
type: integer
|
||||
is_public:
|
||||
description: Defines wether the team should be publicly discoverable when
|
||||
sharing a project
|
||||
type: boolean
|
||||
members:
|
||||
description: An array of all members in this team.
|
||||
items:
|
||||
|
@ -1001,6 +1005,10 @@ definitions:
|
|||
id:
|
||||
description: The unique, numeric id of this team.
|
||||
type: integer
|
||||
is_public:
|
||||
description: Defines wether the team should be publicly discoverable when
|
||||
sharing a project
|
||||
type: boolean
|
||||
members:
|
||||
description: An array of all members in this team.
|
||||
items:
|
||||
|
@ -1386,6 +1394,8 @@ definitions:
|
|||
type: string
|
||||
motd:
|
||||
type: string
|
||||
public_teams_enabled:
|
||||
type: boolean
|
||||
registration_enabled:
|
||||
type: boolean
|
||||
task_attachments_enabled:
|
||||
|
|
Loading…
Reference in New Issue