Use db sessions everywere #750
8
go.mod
8
go.mod
|
@ -18,7 +18,7 @@ module code.vikunja.io/api
|
|||
|
||||
require (
|
||||
4d63.com/tz v1.2.0
|
||||
code.vikunja.io/web v0.0.0-20200809154828-8767618f181f
|
||||
code.vikunja.io/web v0.0.0-20201223143420-588abb73703a
|
||||
dmitri.shuralyov.com/go/generated v0.0.0-20170818220700-b1254a446363 // indirect
|
||||
gitea.com/xorm/xorm-redis-cache v0.2.0
|
||||
github.com/adlio/trello v1.8.0
|
||||
|
@ -41,6 +41,7 @@ require (
|
|||
github.com/go-sql-driver/mysql v1.5.0
|
||||
github.com/go-testfixtures/testfixtures/v3 v3.4.1
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
||||
github.com/golang/snappy v0.0.2 // indirect
|
||||
github.com/gordonklaus/ineffassign v0.0.0-20201107091007-3b93a8888063
|
||||
github.com/iancoleman/strcase v0.1.2
|
||||
github.com/imdario/mergo v0.3.11
|
||||
|
@ -52,6 +53,7 @@ require (
|
|||
github.com/lib/pq v1.9.0
|
||||
github.com/magefile/mage v1.10.0
|
||||
github.com/mailru/easyjson v0.7.6 // indirect
|
||||
github.com/mattn/go-colorable v0.1.8 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.5
|
||||
github.com/mitchellh/mapstructure v1.3.2 // indirect
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
|
@ -76,8 +78,10 @@ require (
|
|||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
|
||||
golang.org/x/image v0.0.0-20201208152932-35266b937fa6
|
||||
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5
|
||||
golang.org/x/net v0.0.0-20201216054612-986b41b23924 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
|
||||
golang.org/x/sys v0.0.0-20201223074533-0d417f636930 // indirect
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||
|
@ -91,7 +95,7 @@ require (
|
|||
src.techknowlogick.com/xormigrate v1.4.0
|
||||
xorm.io/builder v0.3.7
|
||||
xorm.io/core v0.7.3
|
||||
xorm.io/xorm v1.0.2
|
||||
xorm.io/xorm v1.0.5
|
||||
)
|
||||
|
||||
replace (
|
||||
|
|
25
go.sum
25
go.sum
|
@ -38,8 +38,12 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
|
|||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
code.vikunja.io/web v0.0.0-20200809154828-8767618f181f h1:Zgtk9lbJkGbKjdTC78mg/c2uNkesxDJs1YUIL9zGvco=
|
||||
code.vikunja.io/web v0.0.0-20200809154828-8767618f181f/go.mod h1:vDWiCtftF6LNCCrem7mjstPWMgzLUvMW/L4YwIQ1Voo=
|
||||
code.vikunja.io/web v0.0.0-20201218134444-505d0e77fac7 h1:iS3TFA+y1If6DEbqzad5Ge7TI1NxZr9BevC/dU4ygEo=
|
||||
code.vikunja.io/web v0.0.0-20201218134444-505d0e77fac7/go.mod h1:vDWiCtftF6LNCCrem7mjstPWMgzLUvMW/L4YwIQ1Voo=
|
||||
code.vikunja.io/web v0.0.0-20201222144643-6fa2fb587215 h1:O5zMWgcnVDVLaQUawgdsv/jX/4SUUAvSedvRR+5+x2o=
|
||||
code.vikunja.io/web v0.0.0-20201222144643-6fa2fb587215/go.mod h1:OgFO06HN1KpA4S7Dw/QAIeygiUPSeGJJn1ykz/sjZdU=
|
||||
code.vikunja.io/web v0.0.0-20201223143420-588abb73703a h1:LaWCucY5Pp30EIMgGOvdVFNss5OhIAwrAO8PuFVRUfw=
|
||||
code.vikunja.io/web v0.0.0-20201223143420-588abb73703a/go.mod h1:OgFO06HN1KpA4S7Dw/QAIeygiUPSeGJJn1ykz/sjZdU=
|
||||
dmitri.shuralyov.com/go/generated v0.0.0-20170818220700-b1254a446363 h1:o4lAkfETerCnr1kF9/qwkwjICnU+YLHNDCM8h2xj7as=
|
||||
dmitri.shuralyov.com/go/generated v0.0.0-20170818220700-b1254a446363/go.mod h1:WG7q7swWsS2f9PYpt5DoEP/EBYWx8We5UoRltn9vJl8=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
|
@ -152,6 +156,7 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||
github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4 h1:YcpmyvADGYw5LqMnHqSkyIELsHCGF6PkrmM31V8rF7o=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20200910202707-1e08a3fab204 h1:tI48fqaIkxxYuIylVv1tdDfBp6836GKSfmmzgSyP1CY=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20200910202707-1e08a3fab204/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4=
|
||||
|
@ -293,6 +298,8 @@ github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pO
|
|||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw=
|
||||
github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
|
@ -491,6 +498,7 @@ github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
|||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=
|
||||
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg=
|
||||
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8=
|
||||
|
@ -516,6 +524,8 @@ github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+v
|
|||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw=
|
||||
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
|
@ -852,8 +862,6 @@ golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt
|
|||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM=
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620 h1:3wPMTskHO3+O6jqTEXyFcsnuxMQOqYSaHsDxcbUXpqA=
|
||||
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
|
@ -944,6 +952,8 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2l
|
|||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb h1:eBmm0M9fYhWpKZLjQUUKka/LtIxf46G4fxeEz5KJr9U=
|
||||
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201216054612-986b41b23924 h1:QsnDpLLOKwHBBDa8nDws4DYNc/ryVW2vCpxCs09d4PY=
|
||||
golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
|
||||
|
@ -1028,8 +1038,13 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuF
|
|||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e h1:AyodaIpKjppX+cBfTASF2E1US3H2JFBj920Ot3rtDjs=
|
||||
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201221093633-bc327ba9c2f0 h1:n+DPcgTwkgWzIFpLmoimYR2K2b0Ga5+Os4kayIN0vGo=
|
||||
golang.org/x/sys v0.0.0-20201221093633-bc327ba9c2f0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201223074533-0d417f636930 h1:vRgIt+nup/B/BwIS0g2oC0haq0iqbV3ZA+u6+0TlNCo=
|
||||
golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM=
|
||||
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=
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M=
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
@ -1284,3 +1299,5 @@ xorm.io/xorm v1.0.1 h1:/lITxpJtkZauNpdzj+L9CN/3OQxZaABrbergMcJu+Cw=
|
|||
xorm.io/xorm v1.0.1/go.mod h1:o4vnEsQ5V2F1/WK6w4XTwmiWJeGj82tqjAnHe44wVHY=
|
||||
xorm.io/xorm v1.0.2 h1:kZlCh9rqd1AzGwWitcrEEqHE1h1eaZE/ujU5/2tWEtg=
|
||||
xorm.io/xorm v1.0.2/go.mod h1:o4vnEsQ5V2F1/WK6w4XTwmiWJeGj82tqjAnHe44wVHY=
|
||||
xorm.io/xorm v1.0.5 h1:LRr5PfOUb4ODPR63YwbowkNDwcolT2LnkwP/TUaMaB0=
|
||||
xorm.io/xorm v1.0.5/go.mod h1:uF9EtbhODq5kNWxMbnBEj8hRRZnlcNSz2t2N7HW/+A4=
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
"code.vikunja.io/api/pkg/initialize"
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
|
@ -31,6 +32,7 @@ import (
|
|||
"github.com/olekukonko/tablewriter"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/term"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -91,13 +93,13 @@ func getPasswordFromFlagOrInput() (pw string) {
|
|||
return
|
||||
}
|
||||
|
||||
func getUserFromArg(arg string) *user.User {
|
||||
func getUserFromArg(s *xorm.Session, arg string) *user.User {
|
||||
id, err := strconv.ParseInt(arg, 10, 64)
|
||||
if err != nil {
|
||||
log.Fatalf("Invalid user id: %s", err)
|
||||
}
|
||||
|
||||
u, err := user.GetUserByID(id)
|
||||
u, err := user.GetUserByID(s, id)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not get user: %s", err)
|
||||
}
|
||||
|
@ -116,8 +118,16 @@ var userListCmd = &cobra.Command{
|
|||
initialize.FullInit()
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
users, err := user.ListUsers("")
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
users, err := user.ListUsers(s, "")
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
log.Fatalf("Error getting users: %s", err)
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
log.Fatalf("Error getting users: %s", err)
|
||||
}
|
||||
|
||||
|
@ -153,21 +163,30 @@ var userCreateCmd = &cobra.Command{
|
|||
initialize.FullInit()
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
u := &user.User{
|
||||
Username: userFlagUsername,
|
||||
Email: userFlagEmail,
|
||||
Password: getPasswordFromFlagOrInput(),
|
||||
}
|
||||
newUser, err := user.CreateUser(u)
|
||||
newUser, err := user.CreateUser(s, u)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
log.Fatalf("Error creating new user: %s", err)
|
||||
}
|
||||
|
||||
err = models.CreateNewNamespaceForUser(newUser)
|
||||
err = models.CreateNewNamespaceForUser(s, newUser)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
log.Fatalf("Error creating new namespace for user: %s", err)
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
log.Fatalf("Error saving everything: %s", err)
|
||||
}
|
||||
|
||||
fmt.Printf("\nUser was created successfully.\n")
|
||||
},
|
||||
}
|
||||
|
@ -180,7 +199,10 @@ var userUpdateCmd = &cobra.Command{
|
|||
initialize.FullInit()
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
u := getUserFromArg(args[0])
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
u := getUserFromArg(s, args[0])
|
||||
|
||||
if userFlagUsername != "" {
|
||||
u.Username = userFlagUsername
|
||||
|
@ -192,11 +214,16 @@ var userUpdateCmd = &cobra.Command{
|
|||
u.AvatarProvider = userFlagAvatar
|
||||
}
|
||||
|
||||
_, err := user.UpdateUser(u)
|
||||
_, err := user.UpdateUser(s, u)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
log.Fatalf("Error updating the user: %s", err)
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
log.Fatalf("Error saving everything: %s", err)
|
||||
}
|
||||
|
||||
fmt.Println("User updated successfully.")
|
||||
},
|
||||
}
|
||||
|
@ -209,22 +236,31 @@ var userResetPasswordCmd = &cobra.Command{
|
|||
},
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
u := getUserFromArg(args[0])
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
u := getUserFromArg(s, args[0])
|
||||
|
||||
// By default we reset as usual, only with specific flag directly.
|
||||
if userFlagResetPasswordDirectly {
|
||||
err := user.UpdateUserPassword(u, getPasswordFromFlagOrInput())
|
||||
err := user.UpdateUserPassword(s, u, getPasswordFromFlagOrInput())
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
log.Fatalf("Could not update user password: %s", err)
|
||||
}
|
||||
fmt.Println("Password updated successfully.")
|
||||
} else {
|
||||
err := user.RequestUserPasswordResetToken(u)
|
||||
err := user.RequestUserPasswordResetToken(s, u)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
log.Fatalf("Could not send password reset email: %s", err)
|
||||
}
|
||||
fmt.Println("Password reset email sent successfully.")
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
log.Fatalf("Could not send password reset email: %s", err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -236,7 +272,10 @@ var userChangeEnabledCmd = &cobra.Command{
|
|||
},
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
u := getUserFromArg(args[0])
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
u := getUserFromArg(s, args[0])
|
||||
|
||||
if userFlagEnableUser {
|
||||
u.IsActive = true
|
||||
|
@ -245,11 +284,16 @@ var userChangeEnabledCmd = &cobra.Command{
|
|||
} else {
|
||||
u.IsActive = !u.IsActive
|
||||
}
|
||||
_, err := user.UpdateUser(u)
|
||||
_, err := user.UpdateUser(s, u)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
log.Fatalf("Could not enable the user")
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
log.Fatalf("Error saving everything: %s", err)
|
||||
}
|
||||
|
||||
fmt.Printf("User status successfully changed, user is now active: %t.\n", u.IsActive)
|
||||
},
|
||||
}
|
||||
|
|
11
pkg/db/db.go
11
pkg/db/db.go
|
@ -31,6 +31,7 @@ import (
|
|||
"xorm.io/core"
|
||||
"xorm.io/xorm"
|
||||
"xorm.io/xorm/caches"
|
||||
"xorm.io/xorm/schemas"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql" // Because.
|
||||
_ "github.com/lib/pq" // Because.
|
||||
|
@ -211,3 +212,13 @@ func WipeEverything() error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewSession creates a new xorm session
|
||||
func NewSession() *xorm.Session {
|
||||
return x.NewSession()
|
||||
}
|
||||
|
||||
// Type returns the db type of the currently configured db
|
||||
func Type() schemas.DBType {
|
||||
return x.Dialect().URI().DBType
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"time"
|
||||
|
||||
"code.vikunja.io/api/pkg/config"
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
"code.vikunja.io/web"
|
||||
"github.com/c2h5oh/datasize"
|
||||
"github.com/spf13/afero"
|
||||
|
@ -93,27 +94,44 @@ func CreateWithMime(f io.Reader, realname string, realsize uint64, a web.Auth, m
|
|||
Mime: mime,
|
||||
}
|
||||
|
||||
_, err = x.Insert(file)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
_, err = s.Insert(file)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return
|
||||
}
|
||||
|
||||
// Save the file to storage with its new ID as path
|
||||
err = file.Save(f)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Delete removes a file from the DB and the file system
|
||||
func (f *File) Delete() (err error) {
|
||||
deleted, err := x.Where("id = ?", f.ID).Delete(f)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
deleted, err := s.Where("id = ?", f.ID).Delete(f)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return err
|
||||
}
|
||||
if deleted == 0 {
|
||||
_ = s.Rollback()
|
||||
return ErrFileDoesNotExist{FileID: f.ID}
|
||||
}
|
||||
|
||||
err = afs.Remove(f.getFileName())
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ package models
|
|||
import (
|
||||
"code.vikunja.io/web"
|
||||
"github.com/imdario/mergo"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// BulkTask is the definition of a bulk update task
|
||||
|
@ -29,9 +30,9 @@ type BulkTask struct {
|
|||
Task
|
||||
}
|
||||
|
||||
func (bt *BulkTask) checkIfTasksAreOnTheSameList() (err error) {
|
||||
func (bt *BulkTask) checkIfTasksAreOnTheSameList(s *xorm.Session) (err error) {
|
||||
// Get the tasks
|
||||
err = bt.GetTasksByIDs()
|
||||
err = bt.GetTasksByIDs(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -52,16 +53,16 @@ func (bt *BulkTask) checkIfTasksAreOnTheSameList() (err error) {
|
|||
}
|
||||
|
||||
// CanUpdate checks if a user is allowed to update a task
|
||||
func (bt *BulkTask) CanUpdate(a web.Auth) (bool, error) {
|
||||
func (bt *BulkTask) CanUpdate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
|
||||
err := bt.checkIfTasksAreOnTheSameList()
|
||||
err := bt.checkIfTasksAreOnTheSameList(s)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// A user can update an task if he has write acces to its list
|
||||
l := &List{ID: bt.Tasks[0].ListID}
|
||||
return l.CanWrite(a)
|
||||
return l.CanWrite(s, a)
|
||||
}
|
||||
|
||||
// Update updates a bunch of tasks at once
|
||||
|
@ -77,23 +78,14 @@ func (bt *BulkTask) CanUpdate(a web.Auth) (bool, error) {
|
|||
// @Failure 403 {object} web.HTTPError "The user does not have access to the task (aka its list)"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/bulk [post]
|
||||
func (bt *BulkTask) Update() (err error) {
|
||||
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
|
||||
err = sess.Begin()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
func (bt *BulkTask) Update(s *xorm.Session) (err error) {
|
||||
for _, oldtask := range bt.Tasks {
|
||||
|
||||
// When a repeating task is marked as done, we update all deadlines and reminders and set it as undone
|
||||
updateDone(oldtask, &bt.Task)
|
||||
|
||||
// Update the assignees
|
||||
if err := oldtask.updateTaskAssignees(sess, bt.Assignees); err != nil {
|
||||
if err := oldtask.updateTaskAssignees(s, bt.Assignees); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -109,7 +101,7 @@ func (bt *BulkTask) Update() (err error) {
|
|||
oldtask.Done = false
|
||||
}
|
||||
|
||||
_, err = sess.ID(oldtask.ID).
|
||||
_, err = s.ID(oldtask.ID).
|
||||
Cols("title",
|
||||
"description",
|
||||
"done",
|
||||
|
@ -121,15 +113,9 @@ func (bt *BulkTask) Update() (err error) {
|
|||
"end_date").
|
||||
Update(oldtask)
|
||||
if err != nil {
|
||||
_ = sess.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = sess.Commit()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
|
|
@ -57,18 +57,22 @@ func TestBulkTask_Update(t *testing.T) {
|
|||
t.Run(tt.name, func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
|
||||
s := db.NewSession()
|
||||
|
||||
bt := &BulkTask{
|
||||
IDs: tt.fields.IDs,
|
||||
Tasks: tt.fields.Tasks,
|
||||
Task: tt.fields.Task,
|
||||
}
|
||||
allowed, _ := bt.CanUpdate(tt.fields.User)
|
||||
allowed, _ := bt.CanUpdate(s, tt.fields.User)
|
||||
if !allowed != tt.wantForbidden {
|
||||
t.Errorf("BulkTask.Update() want forbidden, got %v, want %v", allowed, tt.wantForbidden)
|
||||
}
|
||||
if err := bt.Update(); (err != nil) != tt.wantErr {
|
||||
if err := bt.Update(s); (err != nil) != tt.wantErr {
|
||||
t.Errorf("BulkTask.Update() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
|
||||
s.Close()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,14 +97,14 @@ func getDefaultBucket(s *xorm.Session, listID int64) (bucket *Bucket, err error)
|
|||
// @Success 200 {array} models.Bucket "The buckets with their tasks"
|
||||
// @Failure 500 {object} models.Message "Internal server error"
|
||||
// @Router /lists/{id}/buckets [get]
|
||||
func (b *Bucket) ReadAll(auth web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
||||
func (b *Bucket) ReadAll(s *xorm.Session, auth web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
||||
|
||||
// Note: I'm ignoring pagination for now since I've yet to figure out a way on how to make it work
|
||||
// I'll probably just don't do it and instead make individual tasks archivable.
|
||||
|
||||
// Get all buckets for this list
|
||||
buckets := []*Bucket{}
|
||||
err = x.Where("list_id = ?", b.ListID).Find(&buckets)
|
||||
err = s.Where("list_id = ?", b.ListID).Find(&buckets)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ func (b *Bucket) ReadAll(auth web.Auth, search string, page int, perPage int) (r
|
|||
|
||||
// Get all users
|
||||
users := make(map[int64]*user.User)
|
||||
err = x.In("id", userIDs).Find(&users)
|
||||
err = s.In("id", userIDs).Find(&users)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -132,7 +132,7 @@ func (b *Bucket) ReadAll(auth web.Auth, search string, page int, perPage int) (r
|
|||
b.TaskCollection.ListID = b.ListID
|
||||
b.TaskCollection.OrderBy = []string{string(orderAscending)}
|
||||
b.TaskCollection.SortBy = []string{taskPropertyPosition}
|
||||
ts, _, _, err := b.TaskCollection.ReadAll(auth, "", -1, 0)
|
||||
ts, _, _, err := b.TaskCollection.ReadAll(s, auth, "", -1, 0)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -168,10 +168,10 @@ func (b *Bucket) ReadAll(auth web.Auth, search string, page int, perPage int) (r
|
|||
// @Failure 404 {object} web.HTTPError "The list does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{id}/buckets [put]
|
||||
func (b *Bucket) Create(a web.Auth) (err error) {
|
||||
func (b *Bucket) Create(s *xorm.Session, a web.Auth) (err error) {
|
||||
b.CreatedByID = a.GetID()
|
||||
|
||||
_, err = x.Insert(b)
|
||||
_, err = s.Insert(b)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -190,8 +190,8 @@ func (b *Bucket) Create(a web.Auth) (err error) {
|
|||
// @Failure 404 {object} web.HTTPError "The bucket does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{listID}/buckets/{bucketID} [post]
|
||||
func (b *Bucket) Update() (err error) {
|
||||
_, err = x.Where("id = ?", b.ID).Update(b)
|
||||
func (b *Bucket) Update(s *xorm.Session) (err error) {
|
||||
_, err = s.Where("id = ?", b.ID).Update(b)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -208,14 +208,11 @@ func (b *Bucket) Update() (err error) {
|
|||
// @Failure 404 {object} web.HTTPError "The bucket does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{listID}/buckets/{bucketID} [delete]
|
||||
func (b *Bucket) Delete() (err error) {
|
||||
|
||||
s := x.NewSession()
|
||||
func (b *Bucket) Delete(s *xorm.Session) (err error) {
|
||||
|
||||
// Prevent removing the last bucket
|
||||
total, err := s.Where("list_id = ?", b.ListID).Count(&Bucket{})
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return
|
||||
}
|
||||
if total <= 1 {
|
||||
|
@ -228,23 +225,19 @@ func (b *Bucket) Delete() (err error) {
|
|||
// Remove the bucket itself
|
||||
_, err = s.Where("id = ?", b.ID).Delete(&Bucket{})
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return
|
||||
}
|
||||
|
||||
// Get the default bucket
|
||||
defaultBucket, err := getDefaultBucket(s, b.ListID)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return
|
||||
}
|
||||
|
||||
// Remove all associations of tasks to that bucket
|
||||
_, err = s.Where("bucket_id = ?", b.ID).Cols("bucket_id").Update(&Task{BucketID: defaultBucket.ID})
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return
|
||||
}
|
||||
|
||||
return s.Commit()
|
||||
_, err = s.
|
||||
Where("bucket_id = ?", b.ID).
|
||||
Cols("bucket_id").
|
||||
Update(&Task{BucketID: defaultBucket.ID})
|
||||
return
|
||||
}
|
||||
|
|
|
@ -16,30 +16,33 @@
|
|||
|
||||
package models
|
||||
|
||||
import "code.vikunja.io/web"
|
||||
import (
|
||||
"code.vikunja.io/web"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// CanCreate checks if a user can create a new bucket
|
||||
func (b *Bucket) CanCreate(a web.Auth) (bool, error) {
|
||||
func (b *Bucket) CanCreate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
l := &List{ID: b.ListID}
|
||||
return l.CanWrite(a)
|
||||
return l.CanWrite(s, a)
|
||||
}
|
||||
|
||||
// CanUpdate checks if a user can update an existing bucket
|
||||
func (b *Bucket) CanUpdate(a web.Auth) (bool, error) {
|
||||
return b.canDoBucket(a)
|
||||
func (b *Bucket) CanUpdate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
return b.canDoBucket(s, a)
|
||||
}
|
||||
|
||||
// CanDelete checks if a user can delete an existing bucket
|
||||
func (b *Bucket) CanDelete(a web.Auth) (bool, error) {
|
||||
return b.canDoBucket(a)
|
||||
func (b *Bucket) CanDelete(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
return b.canDoBucket(s, a)
|
||||
}
|
||||
|
||||
// canDoBucket checks if the bucket exists and if the user has the right to act on it
|
||||
func (b *Bucket) canDoBucket(a web.Auth) (bool, error) {
|
||||
bb, err := getBucketByID(x.NewSession(), b.ID)
|
||||
func (b *Bucket) canDoBucket(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
bb, err := getBucketByID(s, b.ID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
l := &List{ID: bb.ListID}
|
||||
return l.CanWrite(a)
|
||||
return l.CanWrite(s, a)
|
||||
}
|
||||
|
|
|
@ -27,10 +27,12 @@ import (
|
|||
func TestBucket_ReadAll(t *testing.T) {
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
testuser := &user.User{ID: 1}
|
||||
b := &Bucket{ListID: 1}
|
||||
bucketsInterface, _, _, err := b.ReadAll(testuser, "", 0, 0)
|
||||
bucketsInterface, _, _, err := b.ReadAll(s, testuser, "", 0, 0)
|
||||
assert.NoError(t, err)
|
||||
|
||||
buckets, is := bucketsInterface.([]*Bucket)
|
||||
|
@ -66,6 +68,8 @@ func TestBucket_ReadAll(t *testing.T) {
|
|||
})
|
||||
t.Run("filtered", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
testuser := &user.User{ID: 1}
|
||||
b := &Bucket{
|
||||
|
@ -76,7 +80,7 @@ func TestBucket_ReadAll(t *testing.T) {
|
|||
FilterValue: []string{"done"},
|
||||
},
|
||||
}
|
||||
bucketsInterface, _, _, err := b.ReadAll(testuser, "", 0, 0)
|
||||
bucketsInterface, _, _, err := b.ReadAll(s, testuser, "", 0, 0)
|
||||
assert.NoError(t, err)
|
||||
|
||||
buckets := bucketsInterface.([]*Bucket)
|
||||
|
@ -88,16 +92,21 @@ func TestBucket_ReadAll(t *testing.T) {
|
|||
func TestBucket_Delete(t *testing.T) {
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
b := &Bucket{
|
||||
ID: 2, // The second bucket only has 3 tasks
|
||||
ListID: 1,
|
||||
}
|
||||
err := b.Delete()
|
||||
err := b.Delete(s)
|
||||
assert.NoError(t, err)
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Assert all tasks have been moved to bucket 1 as that one is the first
|
||||
tasks := []*Task{}
|
||||
err = x.Where("bucket_id = ?", 1).Find(&tasks)
|
||||
err = s.Where("bucket_id = ?", 1).Find(&tasks)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, tasks, 15)
|
||||
db.AssertMissing(t, "buckets", map[string]interface{}{
|
||||
|
@ -107,13 +116,19 @@ func TestBucket_Delete(t *testing.T) {
|
|||
})
|
||||
t.Run("last bucket in list", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
b := &Bucket{
|
||||
ID: 34,
|
||||
ListID: 18,
|
||||
}
|
||||
err := b.Delete()
|
||||
err := b.Delete(s)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrCannotRemoveLastBucket(err))
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
|
||||
db.AssertExists(t, "buckets", map[string]interface{}{
|
||||
"id": 34,
|
||||
"list_id": 18,
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
"code.vikunja.io/web"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// Label represents a label
|
||||
|
@ -64,7 +65,7 @@ func (Label) TableName() string {
|
|||
// @Failure 400 {object} web.HTTPError "Invalid label object provided."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /labels [put]
|
||||
func (l *Label) Create(a web.Auth) (err error) {
|
||||
func (l *Label) Create(s *xorm.Session, a web.Auth) (err error) {
|
||||
u, err := user.GetFromAuth(a)
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -73,7 +74,7 @@ func (l *Label) Create(a web.Auth) (err error) {
|
|||
l.CreatedBy = u
|
||||
l.CreatedByID = u.ID
|
||||
|
||||
_, err = x.Insert(l)
|
||||
_, err = s.Insert(l)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -92,8 +93,8 @@ func (l *Label) Create(a web.Auth) (err error) {
|
|||
// @Failure 404 {object} web.HTTPError "Label not found."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /labels/{id} [put]
|
||||
func (l *Label) Update() (err error) {
|
||||
_, err = x.
|
||||
func (l *Label) Update(s *xorm.Session) (err error) {
|
||||
_, err = s.
|
||||
ID(l.ID).
|
||||
Cols(
|
||||
"title",
|
||||
|
@ -105,7 +106,7 @@ func (l *Label) Update() (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
err = l.ReadOne()
|
||||
err = l.ReadOne(s)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -122,8 +123,8 @@ func (l *Label) Update() (err error) {
|
|||
// @Failure 404 {object} web.HTTPError "Label not found."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /labels/{id} [delete]
|
||||
func (l *Label) Delete() (err error) {
|
||||
_, err = x.ID(l.ID).Delete(&Label{})
|
||||
func (l *Label) Delete(s *xorm.Session) (err error) {
|
||||
_, err = s.ID(l.ID).Delete(&Label{})
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -140,7 +141,7 @@ func (l *Label) Delete() (err error) {
|
|||
// @Success 200 {array} models.Label "The labels"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /labels [get]
|
||||
func (l *Label) ReadAll(a web.Auth, search string, page int, perPage int) (ls interface{}, resultCount int, numberOfEntries int64, err error) {
|
||||
func (l *Label) ReadAll(s *xorm.Session, a web.Auth, search string, page int, perPage int) (ls interface{}, resultCount int, numberOfEntries int64, err error) {
|
||||
if _, is := a.(*LinkSharing); is {
|
||||
return nil, 0, 0, ErrGenericForbidden{}
|
||||
}
|
||||
|
@ -148,12 +149,12 @@ func (l *Label) ReadAll(a web.Auth, search string, page int, perPage int) (ls in
|
|||
u := &user.User{ID: a.GetID()}
|
||||
|
||||
// Get all tasks
|
||||
taskIDs, err := getUserTaskIDs(u)
|
||||
taskIDs, err := getUserTaskIDs(s, u)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
return getLabelsByTaskIDs(&LabelByTaskIDsOptions{
|
||||
return getLabelsByTaskIDs(s, &LabelByTaskIDsOptions{
|
||||
Search: search,
|
||||
User: u,
|
||||
TaskIDs: taskIDs,
|
||||
|
@ -177,25 +178,25 @@ func (l *Label) ReadAll(a web.Auth, search string, page int, perPage int) (ls in
|
|||
// @Failure 404 {object} web.HTTPError "Label not found"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /labels/{id} [get]
|
||||
func (l *Label) ReadOne() (err error) {
|
||||
label, err := getLabelByIDSimple(l.ID)
|
||||
func (l *Label) ReadOne(s *xorm.Session) (err error) {
|
||||
label, err := getLabelByIDSimple(s, l.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*l = *label
|
||||
|
||||
user, err := user.GetUserByID(l.CreatedByID)
|
||||
u, err := user.GetUserByID(s, l.CreatedByID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
l.CreatedBy = user
|
||||
l.CreatedBy = u
|
||||
return
|
||||
}
|
||||
|
||||
func getLabelByIDSimple(labelID int64) (*Label, error) {
|
||||
func getLabelByIDSimple(s *xorm.Session, labelID int64) (*Label, error) {
|
||||
label := Label{}
|
||||
exists, err := x.ID(labelID).Get(&label)
|
||||
exists, err := s.ID(labelID).Get(&label)
|
||||
if err != nil {
|
||||
return &label, err
|
||||
}
|
||||
|
@ -207,18 +208,21 @@ func getLabelByIDSimple(labelID int64) (*Label, error) {
|
|||
}
|
||||
|
||||
// Helper method to get all task ids a user has
|
||||
func getUserTaskIDs(u *user.User) (taskIDs []int64, err error) {
|
||||
func getUserTaskIDs(s *xorm.Session, u *user.User) (taskIDs []int64, err error) {
|
||||
|
||||
// Get all lists
|
||||
lists, _, _, err := getRawListsForUser(&listOptions{
|
||||
user: u,
|
||||
page: -1,
|
||||
})
|
||||
lists, _, _, err := getRawListsForUser(
|
||||
s,
|
||||
&listOptions{
|
||||
user: u,
|
||||
page: -1,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tasks, _, _, err := getRawTasksForLists(lists, u, &taskOptions{
|
||||
tasks, _, _, err := getRawTasksForLists(s, lists, u, &taskOptions{
|
||||
page: -1,
|
||||
perPage: 0,
|
||||
})
|
||||
|
|
|
@ -20,26 +20,27 @@ import (
|
|||
"code.vikunja.io/api/pkg/user"
|
||||
"code.vikunja.io/web"
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// CanUpdate checks if a user can update a label
|
||||
func (l *Label) CanUpdate(a web.Auth) (bool, error) {
|
||||
return l.isLabelOwner(a) // Only owners should be allowed to update a label
|
||||
func (l *Label) CanUpdate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
return l.isLabelOwner(s, a) // Only owners should be allowed to update a label
|
||||
}
|
||||
|
||||
// CanDelete checks if a user can delete a label
|
||||
func (l *Label) CanDelete(a web.Auth) (bool, error) {
|
||||
return l.isLabelOwner(a) // Only owners should be allowed to delete a label
|
||||
func (l *Label) CanDelete(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
return l.isLabelOwner(s, a) // Only owners should be allowed to delete a label
|
||||
}
|
||||
|
||||
// CanRead checks if a user can read a label
|
||||
func (l *Label) CanRead(a web.Auth) (bool, int, error) {
|
||||
return l.hasAccessToLabel(a)
|
||||
func (l *Label) CanRead(s *xorm.Session, a web.Auth) (bool, int, error) {
|
||||
return l.hasAccessToLabel(s, a)
|
||||
}
|
||||
|
||||
// CanCreate checks if the user can create a label
|
||||
// Currently a dummy.
|
||||
func (l *Label) CanCreate(a web.Auth) (bool, error) {
|
||||
func (l *Label) CanCreate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
if _, is := a.(*LinkSharing); is {
|
||||
return false, nil
|
||||
}
|
||||
|
@ -47,13 +48,13 @@ func (l *Label) CanCreate(a web.Auth) (bool, error) {
|
|||
return true, nil
|
||||
}
|
||||
|
||||
func (l *Label) isLabelOwner(a web.Auth) (bool, error) {
|
||||
func (l *Label) isLabelOwner(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
|
||||
if _, is := a.(*LinkSharing); is {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
lorig, err := getLabelByIDSimple(l.ID)
|
||||
lorig, err := getLabelByIDSimple(s, l.ID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -61,19 +62,19 @@ func (l *Label) isLabelOwner(a web.Auth) (bool, error) {
|
|||
}
|
||||
|
||||
// Helper method to check if a user can see a specific label
|
||||
func (l *Label) hasAccessToLabel(a web.Auth) (has bool, maxRight int, err error) {
|
||||
func (l *Label) hasAccessToLabel(s *xorm.Session, a web.Auth) (has bool, maxRight int, err error) {
|
||||
|
||||
// TODO: add an extra check for link share handling
|
||||
|
||||
// Get all tasks
|
||||
taskIDs, err := getUserTaskIDs(&user.User{ID: a.GetID()})
|
||||
taskIDs, err := getUserTaskIDs(s, &user.User{ID: a.GetID()})
|
||||
if err != nil {
|
||||
return false, 0, err
|
||||
}
|
||||
|
||||
// Get all labels associated with these tasks
|
||||
ll := &LabelTask{}
|
||||
has, err = x.Table("labels").
|
||||
has, err = s.Table("labels").
|
||||
Select("label_task.*").
|
||||
Join("LEFT", "label_task", "label_task.label_id = labels.id").
|
||||
Where("label_task.label_id is not null OR labels.created_by_id = ?", a.GetID()).
|
||||
|
@ -87,7 +88,7 @@ func (l *Label) hasAccessToLabel(a web.Auth) (has bool, maxRight int, err error)
|
|||
// Since the right depends on the task the label is associated with, we need to check that too.
|
||||
if ll.TaskID > 0 {
|
||||
t := &Task{ID: ll.TaskID}
|
||||
_, maxRight, err = t.CanRead(a)
|
||||
_, maxRight, err = t.CanRead(s, a)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -22,10 +22,10 @@ import (
|
|||
"time"
|
||||
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
"code.vikunja.io/web"
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// LabelTask represents a relation between a label and a task
|
||||
|
@ -61,8 +61,8 @@ func (LabelTask) TableName() string {
|
|||
// @Failure 404 {object} web.HTTPError "Label not found."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{task}/labels/{label} [delete]
|
||||
func (lt *LabelTask) Delete() (err error) {
|
||||
_, err = x.Delete(&LabelTask{LabelID: lt.LabelID, TaskID: lt.TaskID})
|
||||
func (lt *LabelTask) Delete(s *xorm.Session) (err error) {
|
||||
_, err = s.Delete(&LabelTask{LabelID: lt.LabelID, TaskID: lt.TaskID})
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -81,9 +81,9 @@ func (lt *LabelTask) Delete() (err error) {
|
|||
// @Failure 404 {object} web.HTTPError "The label does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{task}/labels [put]
|
||||
func (lt *LabelTask) Create(a web.Auth) (err error) {
|
||||
func (lt *LabelTask) Create(s *xorm.Session, a web.Auth) (err error) {
|
||||
// Check if the label is already added
|
||||
exists, err := x.Exist(&LabelTask{LabelID: lt.LabelID, TaskID: lt.TaskID})
|
||||
exists, err := s.Exist(&LabelTask{LabelID: lt.LabelID, TaskID: lt.TaskID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -92,12 +92,12 @@ func (lt *LabelTask) Create(a web.Auth) (err error) {
|
|||
}
|
||||
|
||||
// Insert it
|
||||
_, err = x.Insert(lt)
|
||||
_, err = s.Insert(lt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = updateListByTaskID(lt.TaskID)
|
||||
err = updateListByTaskID(s, lt.TaskID)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -115,10 +115,10 @@ func (lt *LabelTask) Create(a web.Auth) (err error) {
|
|||
// @Success 200 {array} models.Label "The labels"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{task}/labels [get]
|
||||
func (lt *LabelTask) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
||||
func (lt *LabelTask) ReadAll(s *xorm.Session, a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
||||
// Check if the user has the right to see the task
|
||||
task := Task{ID: lt.TaskID}
|
||||
canRead, _, err := task.CanRead(a)
|
||||
canRead, _, err := task.CanRead(s, a)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ func (lt *LabelTask) ReadAll(a web.Auth, search string, page int, perPage int) (
|
|||
return nil, 0, 0, ErrNoRightToSeeTask{lt.TaskID, a.GetID()}
|
||||
}
|
||||
|
||||
return getLabelsByTaskIDs(&LabelByTaskIDsOptions{
|
||||
return getLabelsByTaskIDs(s, &LabelByTaskIDsOptions{
|
||||
User: &user.User{ID: a.GetID()},
|
||||
Search: search,
|
||||
Page: page,
|
||||
|
@ -153,7 +153,7 @@ type LabelByTaskIDsOptions struct {
|
|||
|
||||
// Helper function to get all labels for a set of tasks
|
||||
// Used when getting all labels for one task as well when getting all lables
|
||||
func getLabelsByTaskIDs(opts *LabelByTaskIDsOptions) (ls []*labelWithTaskID, resultCount int, totalEntries int64, err error) {
|
||||
func getLabelsByTaskIDs(s *xorm.Session, opts *LabelByTaskIDsOptions) (ls []*labelWithTaskID, resultCount int, totalEntries int64, err error) {
|
||||
// We still need the task ID when we want to get all labels for a task, but because of this, we get the same label
|
||||
// multiple times when it is associated to more than one task.
|
||||
// Because of this whole thing, we need this extra switch here to only group by Task IDs if needed.
|
||||
|
@ -194,7 +194,7 @@ func getLabelsByTaskIDs(opts *LabelByTaskIDsOptions) (ls []*labelWithTaskID, res
|
|||
|
||||
limit, start := getLimitFromPageIndex(opts.Page, opts.PerPage)
|
||||
|
||||
query := x.Table("labels").
|
||||
query := s.Table("labels").
|
||||
Select(selectStmt).
|
||||
Join("LEFT", "label_task", "label_task.label_id = labels.id").
|
||||
Where(cond).
|
||||
|
@ -214,7 +214,7 @@ func getLabelsByTaskIDs(opts *LabelByTaskIDsOptions) (ls []*labelWithTaskID, res
|
|||
userids = append(userids, l.CreatedByID)
|
||||
}
|
||||
users := make(map[int64]*user.User)
|
||||
err = x.In("id", userids).Find(&users)
|
||||
err = s.In("id", userids).Find(&users)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
@ -230,7 +230,7 @@ func getLabelsByTaskIDs(opts *LabelByTaskIDsOptions) (ls []*labelWithTaskID, res
|
|||
}
|
||||
|
||||
// Get the total number of entries
|
||||
totalEntries, err = x.Table("labels").
|
||||
totalEntries, err = s.Table("labels").
|
||||
Select("count(DISTINCT labels.id)").
|
||||
Join("LEFT", "label_task", "label_task.label_id = labels.id").
|
||||
Where(cond).
|
||||
|
@ -244,11 +244,11 @@ func getLabelsByTaskIDs(opts *LabelByTaskIDsOptions) (ls []*labelWithTaskID, res
|
|||
}
|
||||
|
||||
// Create or update a bunch of task labels
|
||||
func (t *Task) updateTaskLabels(creator web.Auth, labels []*Label) (err error) {
|
||||
func (t *Task) updateTaskLabels(s *xorm.Session, creator web.Auth, labels []*Label) (err error) {
|
||||
|
||||
// If we don't have any new labels, delete everything right away. Saves us some hassle.
|
||||
if len(labels) == 0 && len(t.Labels) > 0 {
|
||||
_, err = x.Where("task_id = ?", t.ID).
|
||||
_, err = s.Where("task_id = ?", t.ID).
|
||||
Delete(LabelTask{})
|
||||
return err
|
||||
}
|
||||
|
@ -289,7 +289,7 @@ func (t *Task) updateTaskLabels(creator web.Auth, labels []*Label) (err error) {
|
|||
|
||||
// Delete all labels not passed
|
||||
if len(labelsToDelete) > 0 {
|
||||
_, err = x.In("label_id", labelsToDelete).
|
||||
_, err = s.In("label_id", labelsToDelete).
|
||||
And("task_id = ?", t.ID).
|
||||
Delete(LabelTask{})
|
||||
if err != nil {
|
||||
|
@ -306,13 +306,13 @@ func (t *Task) updateTaskLabels(creator web.Auth, labels []*Label) (err error) {
|
|||
}
|
||||
|
||||
// Add the new label
|
||||
label, err := getLabelByIDSimple(l.ID)
|
||||
label, err := getLabelByIDSimple(s, l.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if the user has the rights to see the label he is about to add
|
||||
hasAccessToLabel, _, err := label.hasAccessToLabel(creator)
|
||||
hasAccessToLabel, _, err := label.hasAccessToLabel(s, creator)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -322,14 +322,14 @@ func (t *Task) updateTaskLabels(creator web.Auth, labels []*Label) (err error) {
|
|||
}
|
||||
|
||||
// Insert it
|
||||
_, err = x.Insert(&LabelTask{LabelID: l.ID, TaskID: t.ID})
|
||||
_, err = s.Insert(&LabelTask{LabelID: l.ID, TaskID: t.ID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.Labels = append(t.Labels, label)
|
||||
}
|
||||
|
||||
err = updateListLastUpdated(&List{ID: t.ListID})
|
||||
err = updateListLastUpdated(s, &List{ID: t.ListID})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -356,12 +356,12 @@ type LabelTaskBulk struct {
|
|||
// @Failure 400 {object} web.HTTPError "Invalid label object provided."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{taskID}/labels/bulk [post]
|
||||
func (ltb *LabelTaskBulk) Create(a web.Auth) (err error) {
|
||||
task, err := GetTaskByIDSimple(ltb.TaskID)
|
||||
func (ltb *LabelTaskBulk) Create(s *xorm.Session, a web.Auth) (err error) {
|
||||
task, err := GetTaskByIDSimple(s, ltb.TaskID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
labels, _, _, err := getLabelsByTaskIDs(&LabelByTaskIDsOptions{
|
||||
labels, _, _, err := getLabelsByTaskIDs(s, &LabelByTaskIDsOptions{
|
||||
TaskIDs: []int64{ltb.TaskID},
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -370,5 +370,5 @@ func (ltb *LabelTaskBulk) Create(a web.Auth) (err error) {
|
|||
for _, l := range labels {
|
||||
task.Labels = append(task.Labels, &l.Label)
|
||||
}
|
||||
return task.updateTaskLabels(a, ltb.Labels)
|
||||
return task.updateTaskLabels(s, a, ltb.Labels)
|
||||
}
|
||||
|
|
|
@ -18,21 +18,22 @@ package models
|
|||
|
||||
import (
|
||||
"code.vikunja.io/web"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// CanCreate checks if a user can add a label to a task
|
||||
func (lt *LabelTask) CanCreate(a web.Auth) (bool, error) {
|
||||
label, err := getLabelByIDSimple(lt.LabelID)
|
||||
func (lt *LabelTask) CanCreate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
label, err := getLabelByIDSimple(s, lt.LabelID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
hasAccessTolabel, _, err := label.hasAccessToLabel(a)
|
||||
hasAccessTolabel, _, err := label.hasAccessToLabel(s, a)
|
||||
if err != nil || !hasAccessTolabel { // If the user doesn't have access to the label, we can error out here
|
||||
return false, err
|
||||
}
|
||||
|
||||
canDoLabelTask, err := canDoLabelTask(lt.TaskID, a)
|
||||
canDoLabelTask, err := canDoLabelTask(s, lt.TaskID, a)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -41,8 +42,8 @@ func (lt *LabelTask) CanCreate(a web.Auth) (bool, error) {
|
|||
}
|
||||
|
||||
// CanDelete checks if a user can delete a label from a task
|
||||
func (lt *LabelTask) CanDelete(a web.Auth) (bool, error) {
|
||||
canDoLabelTask, err := canDoLabelTask(lt.TaskID, a)
|
||||
func (lt *LabelTask) CanDelete(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
canDoLabelTask, err := canDoLabelTask(s, lt.TaskID, a)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -52,7 +53,7 @@ func (lt *LabelTask) CanDelete(a web.Auth) (bool, error) {
|
|||
|
||||
// We don't care here if the label exists or not. The only relevant thing here is if the relation already exists,
|
||||
// throw an error.
|
||||
exists, err := x.Exist(&LabelTask{LabelID: lt.LabelID, TaskID: lt.TaskID})
|
||||
exists, err := s.Exist(&LabelTask{LabelID: lt.LabelID, TaskID: lt.TaskID})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -60,18 +61,18 @@ func (lt *LabelTask) CanDelete(a web.Auth) (bool, error) {
|
|||
}
|
||||
|
||||
// CanCreate determines if a user can update a labeltask
|
||||
func (ltb *LabelTaskBulk) CanCreate(a web.Auth) (bool, error) {
|
||||
return canDoLabelTask(ltb.TaskID, a)
|
||||
func (ltb *LabelTaskBulk) CanCreate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
return canDoLabelTask(s, ltb.TaskID, a)
|
||||
}
|
||||
|
||||
// Helper function to check if a user can write to a task
|
||||
// + is able to see the label
|
||||
// always the same check for either deleting or adding a label to a task
|
||||
func canDoLabelTask(taskID int64, a web.Auth) (bool, error) {
|
||||
func canDoLabelTask(s *xorm.Session, taskID int64, a web.Auth) (bool, error) {
|
||||
// A user can add a label to a task if he can write to the task
|
||||
task, err := GetTaskByIDSimple(taskID)
|
||||
task, err := GetTaskByIDSimple(s, taskID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return task.CanUpdate(a)
|
||||
return task.CanUpdate(s, a)
|
||||
}
|
||||
|
|
|
@ -91,6 +91,7 @@ func TestLabelTask_ReadAll(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
|
||||
l := &LabelTask{
|
||||
ID: tt.fields.ID,
|
||||
|
@ -100,7 +101,7 @@ func TestLabelTask_ReadAll(t *testing.T) {
|
|||
CRUDable: tt.fields.CRUDable,
|
||||
Rights: tt.fields.Rights,
|
||||
}
|
||||
gotLabels, _, _, err := l.ReadAll(tt.args.a, tt.args.search, tt.args.page, 0)
|
||||
gotLabels, _, _, err := l.ReadAll(s, tt.args.a, tt.args.search, tt.args.page, 0)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("LabelTask.ReadAll() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
|
@ -111,6 +112,8 @@ func TestLabelTask_ReadAll(t *testing.T) {
|
|||
if diff, equal := messagediff.PrettyDiff(gotLabels, tt.wantLabels); !equal {
|
||||
t.Errorf("LabelTask.ReadAll() = %v, want %v, diff: %v", l, tt.wantLabels, diff)
|
||||
}
|
||||
|
||||
s.Close()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -186,6 +189,8 @@ func TestLabelTask_Create(t *testing.T) {
|
|||
t.Run(tt.name, func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
|
||||
s := db.NewSession()
|
||||
|
||||
l := &LabelTask{
|
||||
ID: tt.fields.ID,
|
||||
TaskID: tt.fields.TaskID,
|
||||
|
@ -194,11 +199,11 @@ func TestLabelTask_Create(t *testing.T) {
|
|||
CRUDable: tt.fields.CRUDable,
|
||||
Rights: tt.fields.Rights,
|
||||
}
|
||||
allowed, _ := l.CanCreate(tt.args.a)
|
||||
allowed, _ := l.CanCreate(s, tt.args.a)
|
||||
if !allowed && !tt.wantForbidden {
|
||||
t.Errorf("LabelTask.CanCreate() forbidden, want %v", tt.wantForbidden)
|
||||
}
|
||||
err := l.Create(tt.args.a)
|
||||
err := l.Create(s, tt.args.a)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("LabelTask.Create() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
|
@ -212,6 +217,7 @@ func TestLabelTask_Create(t *testing.T) {
|
|||
"label_id": l.LabelID,
|
||||
}, false)
|
||||
}
|
||||
s.Close()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -282,6 +288,8 @@ func TestLabelTask_Delete(t *testing.T) {
|
|||
t.Run(tt.name, func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
|
||||
s := db.NewSession()
|
||||
|
||||
l := &LabelTask{
|
||||
ID: tt.fields.ID,
|
||||
TaskID: tt.fields.TaskID,
|
||||
|
@ -290,11 +298,11 @@ func TestLabelTask_Delete(t *testing.T) {
|
|||
CRUDable: tt.fields.CRUDable,
|
||||
Rights: tt.fields.Rights,
|
||||
}
|
||||
allowed, _ := l.CanDelete(tt.auth)
|
||||
allowed, _ := l.CanDelete(s, tt.auth)
|
||||
if !allowed && !tt.wantForbidden {
|
||||
t.Errorf("LabelTask.CanDelete() forbidden, want %v", tt.wantForbidden)
|
||||
}
|
||||
err := l.Delete()
|
||||
err := l.Delete(s)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("LabelTask.Delete() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
|
@ -307,6 +315,7 @@ func TestLabelTask_Delete(t *testing.T) {
|
|||
"task_id": l.TaskID,
|
||||
})
|
||||
}
|
||||
s.Close()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -133,7 +133,8 @@ func TestLabel_ReadAll(t *testing.T) {
|
|||
Rights: tt.fields.Rights,
|
||||
}
|
||||
db.LoadAndAssertFixtures(t)
|
||||
gotLs, _, _, err := l.ReadAll(tt.args.a, tt.args.search, tt.args.page, 0)
|
||||
s := db.NewSession()
|
||||
gotLs, _, _, err := l.ReadAll(s, tt.args.a, tt.args.search, tt.args.page, 0)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Label.ReadAll() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
|
@ -141,6 +142,7 @@ func TestLabel_ReadAll(t *testing.T) {
|
|||
if diff, equal := messagediff.PrettyDiff(gotLs, tt.wantLs); !equal {
|
||||
t.Errorf("Label.ReadAll() = %v, want %v, diff: %v", gotLs, tt.wantLs, diff)
|
||||
}
|
||||
s.Close()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -249,11 +251,13 @@ func TestLabel_ReadOne(t *testing.T) {
|
|||
Rights: tt.fields.Rights,
|
||||
}
|
||||
|
||||
allowed, _, _ := l.CanRead(tt.auth)
|
||||
s := db.NewSession()
|
||||
|
||||
allowed, _, _ := l.CanRead(s, tt.auth)
|
||||
if !allowed && !tt.wantForbidden {
|
||||
t.Errorf("Label.CanRead() forbidden, want %v", tt.wantForbidden)
|
||||
}
|
||||
err := l.ReadOne()
|
||||
err := l.ReadOne(s)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Label.ReadOne() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
|
@ -263,6 +267,8 @@ func TestLabel_ReadOne(t *testing.T) {
|
|||
if diff, equal := messagediff.PrettyDiff(l, tt.want); !equal && !tt.wantErr && !tt.wantForbidden {
|
||||
t.Errorf("Label.ReadAll() = %v, want %v, diff: %v", l, tt.want, diff)
|
||||
}
|
||||
|
||||
s.Close()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -316,11 +322,12 @@ func TestLabel_Create(t *testing.T) {
|
|||
CRUDable: tt.fields.CRUDable,
|
||||
Rights: tt.fields.Rights,
|
||||
}
|
||||
allowed, _ := l.CanCreate(tt.args.a)
|
||||
s := db.NewSession()
|
||||
allowed, _ := l.CanCreate(s, tt.args.a)
|
||||
if !allowed && !tt.wantForbidden {
|
||||
t.Errorf("Label.CanCreate() forbidden, want %v", tt.wantForbidden)
|
||||
}
|
||||
if err := l.Create(tt.args.a); (err != nil) != tt.wantErr {
|
||||
if err := l.Create(s, tt.args.a); (err != nil) != tt.wantErr {
|
||||
t.Errorf("Label.Create() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
if !tt.wantErr {
|
||||
|
@ -331,6 +338,7 @@ func TestLabel_Create(t *testing.T) {
|
|||
"hex_color": l.HexColor,
|
||||
}, false)
|
||||
}
|
||||
_ = s.Close()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -406,11 +414,12 @@ func TestLabel_Update(t *testing.T) {
|
|||
CRUDable: tt.fields.CRUDable,
|
||||
Rights: tt.fields.Rights,
|
||||
}
|
||||
allowed, _ := l.CanUpdate(tt.auth)
|
||||
s := db.NewSession()
|
||||
allowed, _ := l.CanUpdate(s, tt.auth)
|
||||
if !allowed && !tt.wantForbidden {
|
||||
t.Errorf("Label.CanUpdate() forbidden, want %v", tt.wantForbidden)
|
||||
}
|
||||
if err := l.Update(); (err != nil) != tt.wantErr {
|
||||
if err := l.Update(s); (err != nil) != tt.wantErr {
|
||||
t.Errorf("Label.Update() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
if !tt.wantErr && !tt.wantForbidden {
|
||||
|
@ -419,6 +428,7 @@ func TestLabel_Update(t *testing.T) {
|
|||
"title": tt.fields.Title,
|
||||
}, false)
|
||||
}
|
||||
_ = s.Close()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -490,11 +500,12 @@ func TestLabel_Delete(t *testing.T) {
|
|||
CRUDable: tt.fields.CRUDable,
|
||||
Rights: tt.fields.Rights,
|
||||
}
|
||||
allowed, _ := l.CanDelete(tt.auth)
|
||||
s := db.NewSession()
|
||||
allowed, _ := l.CanDelete(s, tt.auth)
|
||||
if !allowed && !tt.wantForbidden {
|
||||
t.Errorf("Label.CanDelete() forbidden, want %v", tt.wantForbidden)
|
||||
}
|
||||
if err := l.Delete(); (err != nil) != tt.wantErr {
|
||||
if err := l.Delete(s); (err != nil) != tt.wantErr {
|
||||
t.Errorf("Label.Delete() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
if !tt.wantErr && !tt.wantForbidden {
|
||||
|
@ -502,6 +513,7 @@ func TestLabel_Delete(t *testing.T) {
|
|||
"id": l.ID,
|
||||
})
|
||||
}
|
||||
_ = s.Close()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
"code.vikunja.io/api/pkg/utils"
|
||||
"code.vikunja.io/web"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// SharingType holds the sharing type
|
||||
|
@ -99,7 +100,7 @@ func GetLinkShareFromClaims(claims jwt.MapClaims) (share *LinkSharing, err error
|
|||
// @Failure 404 {object} web.HTTPError "The list does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{list}/shares [put]
|
||||
func (share *LinkSharing) Create(a web.Auth) (err error) {
|
||||
func (share *LinkSharing) Create(s *xorm.Session, a web.Auth) (err error) {
|
||||
|
||||
err = share.Right.isValid()
|
||||
if err != nil {
|
||||
|
@ -108,7 +109,7 @@ func (share *LinkSharing) Create(a web.Auth) (err error) {
|
|||
|
||||
share.SharedByID = a.GetID()
|
||||
share.Hash = utils.MakeRandomString(40)
|
||||
_, err = x.Insert(share)
|
||||
_, err = s.Insert(share)
|
||||
share.SharedBy, _ = user.GetFromAuth(a)
|
||||
return
|
||||
}
|
||||
|
@ -127,8 +128,8 @@ func (share *LinkSharing) Create(a web.Auth) (err error) {
|
|||
// @Failure 404 {object} web.HTTPError "Share Link not found."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{list}/shares/{share} [get]
|
||||
func (share *LinkSharing) ReadOne() (err error) {
|
||||
exists, err := x.Where("id = ?", share.ID).Get(share)
|
||||
func (share *LinkSharing) ReadOne(s *xorm.Session) (err error) {
|
||||
exists, err := s.Where("id = ?", share.ID).Get(share)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -152,9 +153,9 @@ func (share *LinkSharing) ReadOne() (err error) {
|
|||
// @Success 200 {array} models.LinkSharing "The share links"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{list}/shares [get]
|
||||
func (share *LinkSharing) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, totalItems int64, err error) {
|
||||
func (share *LinkSharing) ReadAll(s *xorm.Session, a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, totalItems int64, err error) {
|
||||
list := &List{ID: share.ListID}
|
||||
can, _, err := list.CanRead(a)
|
||||
can, _, err := list.CanRead(s, a)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
@ -165,7 +166,7 @@ func (share *LinkSharing) ReadAll(a web.Auth, search string, page int, perPage i
|
|||
limit, start := getLimitFromPageIndex(page, perPage)
|
||||
|
||||
var shares []*LinkSharing
|
||||
query := x.
|
||||
query := s.
|
||||
Where("list_id = ? AND hash LIKE ?", share.ListID, "%"+search+"%")
|
||||
if limit > 0 {
|
||||
query = query.Limit(limit, start)
|
||||
|
@ -182,7 +183,7 @@ func (share *LinkSharing) ReadAll(a web.Auth, search string, page int, perPage i
|
|||
}
|
||||
|
||||
users := make(map[int64]*user.User)
|
||||
err = x.In("id", userIDs).Find(&users)
|
||||
err = s.In("id", userIDs).Find(&users)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
@ -192,7 +193,7 @@ func (share *LinkSharing) ReadAll(a web.Auth, search string, page int, perPage i
|
|||
}
|
||||
|
||||
// Total count
|
||||
totalItems, err = x.
|
||||
totalItems, err = s.
|
||||
Where("list_id = ? AND hash LIKE ?", share.ListID, "%"+search+"%").
|
||||
Count(&LinkSharing{})
|
||||
if err != nil {
|
||||
|
@ -216,15 +217,15 @@ func (share *LinkSharing) ReadAll(a web.Auth, search string, page int, perPage i
|
|||
// @Failure 404 {object} web.HTTPError "Share Link not found."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{list}/shares/{share} [delete]
|
||||
func (share *LinkSharing) Delete() (err error) {
|
||||
_, err = x.Where("id = ?", share.ID).Delete(share)
|
||||
func (share *LinkSharing) Delete(s *xorm.Session) (err error) {
|
||||
_, err = s.Where("id = ?", share.ID).Delete(share)
|
||||
return
|
||||
}
|
||||
|
||||
// GetLinkShareByHash returns a link share by hash
|
||||
func GetLinkShareByHash(hash string) (share *LinkSharing, err error) {
|
||||
func GetLinkShareByHash(s *xorm.Session, hash string) (share *LinkSharing, err error) {
|
||||
share = &LinkSharing{}
|
||||
has, err := x.Where("hash = ?", hash).Get(share)
|
||||
has, err := s.Where("hash = ?", hash).Get(share)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -235,13 +236,12 @@ func GetLinkShareByHash(hash string) (share *LinkSharing, err error) {
|
|||
}
|
||||
|
||||
// GetListByShareHash returns a link share by its hash
|
||||
func GetListByShareHash(hash string) (list *List, err error) {
|
||||
share, err := GetLinkShareByHash(hash)
|
||||
func GetListByShareHash(s *xorm.Session, hash string) (list *List, err error) {
|
||||
share, err := GetLinkShareByHash(s, hash)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
list = &List{ID: share.ListID}
|
||||
err = list.GetSimpleByID()
|
||||
list, err = GetListSimpleByID(s, share.ListID)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -16,53 +16,55 @@
|
|||
|
||||
package models
|
||||
|
||||
import "code.vikunja.io/web"
|
||||
import (
|
||||
"code.vikunja.io/web"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// CanRead implements the read right check for a link share
|
||||
func (share *LinkSharing) CanRead(a web.Auth) (bool, int, error) {
|
||||
func (share *LinkSharing) CanRead(s *xorm.Session, a web.Auth) (bool, int, error) {
|
||||
// Don't allow creating link shares if the user itself authenticated with a link share
|
||||
if _, is := a.(*LinkSharing); is {
|
||||
return false, 0, nil
|
||||
}
|
||||
|
||||
l, err := GetListByShareHash(share.Hash)
|
||||
l, err := GetListByShareHash(s, share.Hash)
|
||||
if err != nil {
|
||||
return false, 0, err
|
||||
}
|
||||
return l.CanRead(a)
|
||||
return l.CanRead(s, a)
|
||||
}
|
||||
|
||||
// CanDelete implements the delete right check for a link share
|
||||
func (share *LinkSharing) CanDelete(a web.Auth) (bool, error) {
|
||||
return share.canDoLinkShare(a)
|
||||
func (share *LinkSharing) CanDelete(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
return share.canDoLinkShare(s, a)
|
||||
}
|
||||
|
||||
// CanUpdate implements the update right check for a link share
|
||||
func (share *LinkSharing) CanUpdate(a web.Auth) (bool, error) {
|
||||
return share.canDoLinkShare(a)
|
||||
func (share *LinkSharing) CanUpdate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
return share.canDoLinkShare(s, a)
|
||||
}
|
||||
|
||||
// CanCreate implements the create right check for a link share
|
||||
func (share *LinkSharing) CanCreate(a web.Auth) (bool, error) {
|
||||
return share.canDoLinkShare(a)
|
||||
func (share *LinkSharing) CanCreate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
return share.canDoLinkShare(s, a)
|
||||
}
|
||||
|
||||
func (share *LinkSharing) canDoLinkShare(a web.Auth) (bool, error) {
|
||||
func (share *LinkSharing) canDoLinkShare(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
// Don't allow creating link shares if the user itself authenticated with a link share
|
||||
if _, is := a.(*LinkSharing); is {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
l := &List{ID: share.ListID}
|
||||
err := l.GetSimpleByID()
|
||||
l, err := GetListSimpleByID(s, share.ListID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Check if the user is admin when the link right is admin
|
||||
if share.Right == RightAdmin {
|
||||
return l.IsAdmin(a)
|
||||
return l.IsAdmin(s, a)
|
||||
}
|
||||
|
||||
return l.CanWrite(a)
|
||||
return l.CanWrite(s, a)
|
||||
}
|
||||
|
|
|
@ -96,9 +96,9 @@ var FavoritesPseudoList = List{
|
|||
}
|
||||
|
||||
// GetListsByNamespaceID gets all lists in a namespace
|
||||
func GetListsByNamespaceID(nID int64, doer *user.User) (lists []*List, err error) {
|
||||
func GetListsByNamespaceID(s *xorm.Session, nID int64, doer *user.User) (lists []*List, err error) {
|
||||
if nID == -1 {
|
||||
err = x.Select("l.*").
|
||||
err = s.Select("l.*").
|
||||
Table("list").
|
||||
Join("LEFT", []string{"team_list", "tl"}, "l.id = tl.list_id").
|
||||
Join("LEFT", []string{"team_members", "tm"}, "tm.team_id = tl.team_id").
|
||||
|
@ -111,7 +111,7 @@ func GetListsByNamespaceID(nID int64, doer *user.User) (lists []*List, err error
|
|||
GroupBy("l.id").
|
||||
Find(&lists)
|
||||
} else {
|
||||
err = x.Select("l.*").
|
||||
err = s.Select("l.*").
|
||||
Alias("l").
|
||||
Join("LEFT", []string{"namespaces", "n"}, "l.namespace_id = n.id").
|
||||
Where("l.is_archived = false").
|
||||
|
@ -124,7 +124,7 @@ func GetListsByNamespaceID(nID int64, doer *user.User) (lists []*List, err error
|
|||
}
|
||||
|
||||
// get more list details
|
||||
err = AddListDetails(lists)
|
||||
err = addListDetails(s, lists)
|
||||
return lists, err
|
||||
}
|
||||
|
||||
|
@ -143,33 +143,34 @@ func GetListsByNamespaceID(nID int64, doer *user.User) (lists []*List, err error
|
|||
// @Failure 403 {object} web.HTTPError "The user does not have access to the list"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists [get]
|
||||
func (l *List) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, totalItems int64, err error) {
|
||||
func (l *List) ReadAll(s *xorm.Session, a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, totalItems int64, err error) {
|
||||
// Check if we're dealing with a share auth
|
||||
shareAuth, ok := a.(*LinkSharing)
|
||||
if ok {
|
||||
list := &List{ID: shareAuth.ListID}
|
||||
err := list.GetSimpleByID()
|
||||
list, err := GetListSimpleByID(s, shareAuth.ListID)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
lists := []*List{list}
|
||||
err = AddListDetails(lists)
|
||||
err = addListDetails(s, lists)
|
||||
return lists, 0, 0, err
|
||||
}
|
||||
|
||||
lists, resultCount, totalItems, err := getRawListsForUser(&listOptions{
|
||||
search: search,
|
||||
user: &user.User{ID: a.GetID()},
|
||||
page: page,
|
||||
perPage: perPage,
|
||||
isArchived: l.IsArchived,
|
||||
})
|
||||
lists, resultCount, totalItems, err := getRawListsForUser(
|
||||
s,
|
||||
&listOptions{
|
||||
search: search,
|
||||
user: &user.User{ID: a.GetID()},
|
||||
page: page,
|
||||
perPage: perPage,
|
||||
isArchived: l.IsArchived,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
// Add more list details
|
||||
err = AddListDetails(lists)
|
||||
err = addListDetails(s, lists)
|
||||
return lists, resultCount, totalItems, err
|
||||
}
|
||||
|
||||
|
@ -185,7 +186,7 @@ func (l *List) ReadAll(a web.Auth, search string, page int, perPage int) (result
|
|||
// @Failure 403 {object} web.HTTPError "The user does not have access to the list"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{id} [get]
|
||||
func (l *List) ReadOne() (err error) {
|
||||
func (l *List) ReadOne(s *xorm.Session) (err error) {
|
||||
|
||||
if l.ID == FavoritesPseudoList.ID {
|
||||
// Already "built" the list in CanRead
|
||||
|
@ -194,7 +195,7 @@ func (l *List) ReadOne() (err error) {
|
|||
|
||||
// Check for saved filters
|
||||
if getSavedFilterIDFromListID(l.ID) > 0 {
|
||||
sf, err := getSavedFilterSimpleByID(getSavedFilterIDFromListID(l.ID))
|
||||
sf, err := getSavedFilterSimpleByID(s, getSavedFilterIDFromListID(l.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -206,13 +207,13 @@ func (l *List) ReadOne() (err error) {
|
|||
}
|
||||
|
||||
// Get list owner
|
||||
l.Owner, err = user.GetUserByID(l.OwnerID)
|
||||
l.Owner, err = user.GetUserByID(s, l.OwnerID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Check if the namespace is archived and set the namespace to archived if it is not already archived individually.
|
||||
if !l.IsArchived {
|
||||
err = l.CheckIsArchived()
|
||||
err = l.CheckIsArchived(s)
|
||||
if err != nil {
|
||||
if !IsErrNamespaceIsArchived(err) && !IsErrListIsArchived(err) {
|
||||
return
|
||||
|
@ -224,7 +225,7 @@ func (l *List) ReadOne() (err error) {
|
|||
// Get any background information if there is one set
|
||||
if l.BackgroundFileID != 0 {
|
||||
// Unsplash image
|
||||
l.BackgroundInformation, err = GetUnsplashPhotoByFileID(l.BackgroundFileID)
|
||||
l.BackgroundInformation, err = GetUnsplashPhotoByFileID(s, l.BackgroundFileID)
|
||||
if err != nil && !files.IsErrFileIsNotUnsplashFile(err) {
|
||||
return
|
||||
}
|
||||
|
@ -237,44 +238,33 @@ func (l *List) ReadOne() (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
// GetSimpleByID gets a list with only the basic items, aka no tasks or user objects. Returns an error if the list does not exist.
|
||||
func (l *List) GetSimpleByID() (err error) {
|
||||
s := x.NewSession()
|
||||
err = l.getSimpleByID(s)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// GetListSimpleByID gets a list with only the basic items, aka no tasks or user objects. Returns an error if the list does not exist.
|
||||
func GetListSimpleByID(s *xorm.Session, listID int64) (list *List, err error) {
|
||||
|
||||
func (l *List) getSimpleByID(s *xorm.Session) (err error) {
|
||||
if l.ID < 1 {
|
||||
return ErrListDoesNotExist{ID: l.ID}
|
||||
list = &List{}
|
||||
|
||||
if listID < 1 {
|
||||
return nil, ErrListDoesNotExist{ID: listID}
|
||||
}
|
||||
|
||||
// We need to re-init our list object, because otherwise xorm creates a "where for every item in that list object,
|
||||
// leading to not finding anything if the id is good, but for example the title is different.
|
||||
id := l.ID
|
||||
*l = List{}
|
||||
exists, err := s.Where("id = ?", id).Get(l)
|
||||
exists, err := s.Where("id = ?", listID).Get(list)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !exists {
|
||||
return ErrListDoesNotExist{ID: l.ID}
|
||||
return nil, ErrListDoesNotExist{ID: listID}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetListSimplByTaskID gets a list by a task id
|
||||
func GetListSimplByTaskID(taskID int64) (l *List, err error) {
|
||||
func GetListSimplByTaskID(s *xorm.Session, taskID int64) (l *List, err error) {
|
||||
// We need to re-init our list object, because otherwise xorm creates a "where for every item in that list object,
|
||||
// leading to not finding anything if the id is good, but for example the title is different.
|
||||
var list List
|
||||
exists, err := x.
|
||||
exists, err := s.
|
||||
Select("list.*").
|
||||
Table(List{}).
|
||||
Join("INNER", "tasks", "list.id = tasks.list_id").
|
||||
|
@ -292,9 +282,9 @@ func GetListSimplByTaskID(taskID int64) (l *List, err error) {
|
|||
}
|
||||
|
||||
// GetListsByIDs returns a map of lists from a slice with list ids
|
||||
func GetListsByIDs(listIDs []int64) (lists map[int64]*List, err error) {
|
||||
func GetListsByIDs(s *xorm.Session, listIDs []int64) (lists map[int64]*List, err error) {
|
||||
lists = make(map[int64]*List, len(listIDs))
|
||||
err = x.In("id", listIDs).Find(&lists)
|
||||
err = s.In("id", listIDs).Find(&lists)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -307,8 +297,8 @@ type listOptions struct {
|
|||
}
|
||||
|
||||
// Gets the lists only, without any tasks or so
|
||||
func getRawListsForUser(opts *listOptions) (lists []*List, resultCount int, totalItems int64, err error) {
|
||||
fullUser, err := user.GetUserByID(opts.user.ID)
|
||||
func getRawListsForUser(s *xorm.Session, opts *listOptions) (lists []*List, resultCount int, totalItems int64, err error) {
|
||||
fullUser, err := user.GetUserByID(s, opts.user.ID)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
@ -344,7 +334,7 @@ func getRawListsForUser(opts *listOptions) (lists []*List, resultCount int, tota
|
|||
|
||||
// Gets all Lists where the user is either owner or in a team which has access to the list
|
||||
// Or in a team which has namespace read access
|
||||
query := x.Select("l.*").
|
||||
query := s.Select("l.*").
|
||||
Table("list").
|
||||
Alias("l").
|
||||
Join("INNER", []string{"namespaces", "n"}, "l.namespace_id = n.id").
|
||||
|
@ -372,7 +362,7 @@ func getRawListsForUser(opts *listOptions) (lists []*List, resultCount int, tota
|
|||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
totalItems, err = x.
|
||||
totalItems, err = s.
|
||||
Table("list").
|
||||
Alias("l").
|
||||
Join("INNER", []string{"namespaces", "n"}, "l.namespace_id = n.id").
|
||||
|
@ -396,8 +386,8 @@ func getRawListsForUser(opts *listOptions) (lists []*List, resultCount int, tota
|
|||
return lists, len(lists), totalItems, err
|
||||
}
|
||||
|
||||
// AddListDetails adds owner user objects and list tasks to all lists in the slice
|
||||
func AddListDetails(lists []*List) (err error) {
|
||||
// addListDetails adds owner user objects and list tasks to all lists in the slice
|
||||
func addListDetails(s *xorm.Session, lists []*List) (err error) {
|
||||
var ownerIDs []int64
|
||||
for _, l := range lists {
|
||||
ownerIDs = append(ownerIDs, l.OwnerID)
|
||||
|
@ -405,7 +395,7 @@ func AddListDetails(lists []*List) (err error) {
|
|||
|
||||
// Get all list owners
|
||||
owners := map[int64]*user.User{}
|
||||
err = x.In("id", ownerIDs).Find(&owners)
|
||||
err = s.In("id", ownerIDs).Find(&owners)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -423,7 +413,7 @@ func AddListDetails(lists []*List) (err error) {
|
|||
|
||||
// Unsplash background file info
|
||||
us := []*UnsplashPhoto{}
|
||||
err = x.In("file_id", fileIDs).Find(&us)
|
||||
err = s.In("file_id", fileIDs).Find(&us)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -450,15 +440,15 @@ type NamespaceList struct {
|
|||
}
|
||||
|
||||
// CheckIsArchived returns an ErrListIsArchived or ErrNamespaceIsArchived if the list or its namespace is archived.
|
||||
func (l *List) CheckIsArchived() (err error) {
|
||||
func (l *List) CheckIsArchived(s *xorm.Session) (err error) {
|
||||
// When creating a new list, we check if the namespace is archived
|
||||
if l.ID == 0 {
|
||||
n := &Namespace{ID: l.NamespaceID}
|
||||
return n.CheckIsArchived()
|
||||
return n.CheckIsArchived(s)
|
||||
}
|
||||
|
||||
nl := &NamespaceList{}
|
||||
exists, err := x.
|
||||
exists, err := s.
|
||||
Table("list").
|
||||
Join("LEFT", "namespaces", "list.namespace_id = namespaces.id").
|
||||
Where("list.id = ? AND (list.is_archived = true OR namespaces.is_archived = true)", l.ID).
|
||||
|
@ -476,11 +466,11 @@ func (l *List) CheckIsArchived() (err error) {
|
|||
}
|
||||
|
||||
// CreateOrUpdateList updates a list or creates it if it doesn't exist
|
||||
func CreateOrUpdateList(list *List) (err error) {
|
||||
func CreateOrUpdateList(s *xorm.Session, list *List) (err error) {
|
||||
|
||||
// Check if the namespace exists
|
||||
if list.NamespaceID != 0 && list.NamespaceID != FavoritesPseudoNamespace.ID {
|
||||
_, err = GetNamespaceByID(list.NamespaceID)
|
||||
_, err = GetNamespaceByID(s, list.NamespaceID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -488,7 +478,7 @@ func CreateOrUpdateList(list *List) (err error) {
|
|||
|
||||
// Check if the identifier is unique and not empty
|
||||
if list.Identifier != "" {
|
||||
exists, err := x.
|
||||
exists, err := s.
|
||||
Where("identifier = ?", list.Identifier).
|
||||
And("id != ?", list.ID).
|
||||
Exist(&List{})
|
||||
|
@ -501,7 +491,7 @@ func CreateOrUpdateList(list *List) (err error) {
|
|||
}
|
||||
|
||||
if list.ID == 0 {
|
||||
_, err = x.Insert(list)
|
||||
_, err = s.Insert(list)
|
||||
metrics.UpdateCount(1, metrics.ListCountKey)
|
||||
} else {
|
||||
// We need to specify the cols we want to update here to be able to un-archive lists
|
||||
|
@ -516,7 +506,7 @@ func CreateOrUpdateList(list *List) (err error) {
|
|||
colsToUpdate = append(colsToUpdate, "description")
|
||||
}
|
||||
|
||||
_, err = x.
|
||||
_, err = s.
|
||||
ID(list.ID).
|
||||
Cols(colsToUpdate...).
|
||||
Update(list)
|
||||
|
@ -526,12 +516,13 @@ func CreateOrUpdateList(list *List) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
err = list.GetSimpleByID()
|
||||
l, err := GetListSimpleByID(s, list.ID)
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
err = list.ReadOne()
|
||||
*list = *l
|
||||
err = list.ReadOne(s)
|
||||
return
|
||||
|
||||
}
|
||||
|
@ -550,33 +541,23 @@ func CreateOrUpdateList(list *List) (err error) {
|
|||
// @Failure 403 {object} web.HTTPError "The user does not have access to the list"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{id} [post]
|
||||
func (l *List) Update() (err error) {
|
||||
return CreateOrUpdateList(l)
|
||||
func (l *List) Update(s *xorm.Session) (err error) {
|
||||
return CreateOrUpdateList(s, l)
|
||||
}
|
||||
|
||||
func updateListLastUpdated(list *List) (err error) {
|
||||
s := x.NewSession()
|
||||
err = updateListLastUpdatedS(s, list)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateListLastUpdatedS(s *xorm.Session, list *List) error {
|
||||
func updateListLastUpdated(s *xorm.Session, list *List) error {
|
||||
_, err := s.ID(list.ID).Cols("updated").Update(list)
|
||||
return err
|
||||
}
|
||||
|
||||
func updateListByTaskID(taskID int64) (err error) {
|
||||
func updateListByTaskID(s *xorm.Session, taskID int64) (err error) {
|
||||
// need to get the task to update the list last updated timestamp
|
||||
task, err := GetTaskByIDSimple(taskID)
|
||||
task, err := GetTaskByIDSimple(s, taskID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return updateListLastUpdated(&List{ID: task.ListID})
|
||||
return updateListLastUpdated(s, &List{ID: task.ListID})
|
||||
}
|
||||
|
||||
// Create implements the create method of CRUDable
|
||||
|
@ -593,8 +574,8 @@ func updateListByTaskID(taskID int64) (err error) {
|
|||
// @Failure 403 {object} web.HTTPError "The user does not have access to the list"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /namespaces/{namespaceID}/lists [put]
|
||||
func (l *List) Create(a web.Auth) (err error) {
|
||||
err = l.CheckIsArchived()
|
||||
func (l *List) Create(s *xorm.Session, a web.Auth) (err error) {
|
||||
err = l.CheckIsArchived(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -608,7 +589,7 @@ func (l *List) Create(a web.Auth) (err error) {
|
|||
l.Owner = doer
|
||||
l.ID = 0 // Otherwise only the first time a new list would be created
|
||||
|
||||
err = CreateOrUpdateList(l)
|
||||
err = CreateOrUpdateList(s, l)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -618,7 +599,7 @@ func (l *List) Create(a web.Auth) (err error) {
|
|||
ListID: l.ID,
|
||||
Title: "New Bucket",
|
||||
}
|
||||
return b.Create(a)
|
||||
return b.Create(s, a)
|
||||
}
|
||||
|
||||
// Delete implements the delete method of CRUDable
|
||||
|
@ -633,27 +614,27 @@ func (l *List) Create(a web.Auth) (err error) {
|
|||
// @Failure 403 {object} web.HTTPError "The user does not have access to the list"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{id} [delete]
|
||||
func (l *List) Delete() (err error) {
|
||||
func (l *List) Delete(s *xorm.Session) (err error) {
|
||||
|
||||
// Delete the list
|
||||
_, err = x.ID(l.ID).Delete(&List{})
|
||||
_, err = s.ID(l.ID).Delete(&List{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
metrics.UpdateCount(-1, metrics.ListCountKey)
|
||||
|
||||
// Delete all todotasks on that list
|
||||
_, err = x.Where("list_id = ?", l.ID).Delete(&Task{})
|
||||
_, err = s.Where("list_id = ?", l.ID).Delete(&Task{})
|
||||
return
|
||||
}
|
||||
|
||||
// SetListBackground sets a background file as list background in the db
|
||||
func SetListBackground(listID int64, background *files.File) (err error) {
|
||||
func SetListBackground(s *xorm.Session, listID int64, background *files.File) (err error) {
|
||||
l := &List{
|
||||
ID: listID,
|
||||
BackgroundFileID: background.ID,
|
||||
}
|
||||
_, err = x.
|
||||
_, err = s.
|
||||
Where("id = ?", l.ID).
|
||||
Cols("background_file_id").
|
||||
Update(l)
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"code.vikunja.io/api/pkg/log"
|
||||
"code.vikunja.io/api/pkg/utils"
|
||||
"code.vikunja.io/web"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// ListDuplicate holds everything needed to duplicate a list
|
||||
|
@ -38,17 +39,17 @@ type ListDuplicate struct {
|
|||
}
|
||||
|
||||
// CanCreate checks if a user has the right to duplicate a list
|
||||
func (ld *ListDuplicate) CanCreate(a web.Auth) (canCreate bool, err error) {
|
||||
func (ld *ListDuplicate) CanCreate(s *xorm.Session, a web.Auth) (canCreate bool, err error) {
|
||||
// List Exists + user has read access to list
|
||||
ld.List = &List{ID: ld.ListID}
|
||||
canRead, _, err := ld.List.CanRead(a)
|
||||
canRead, _, err := ld.List.CanRead(s, a)
|
||||
if err != nil || !canRead {
|
||||
return canRead, err
|
||||
}
|
||||
|
||||
// Namespace exists + user has write access to is (-> can create new lists)
|
||||
ld.List.NamespaceID = ld.NamespaceID
|
||||
return ld.List.CanCreate(a)
|
||||
return ld.List.CanCreate(s, a)
|
||||
}
|
||||
|
||||
// Create duplicates a list
|
||||
|
@ -66,7 +67,7 @@ func (ld *ListDuplicate) CanCreate(a web.Auth) (canCreate bool, err error) {
|
|||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{listID}/duplicate [put]
|
||||
//nolint:gocyclo
|
||||
func (ld *ListDuplicate) Create(a web.Auth) (err error) {
|
||||
func (ld *ListDuplicate) Create(s *xorm.Session, a web.Auth) (err error) {
|
||||
|
||||
log.Debugf("Duplicating list %d", ld.ListID)
|
||||
|
||||
|
@ -74,7 +75,7 @@ func (ld *ListDuplicate) Create(a web.Auth) (err error) {
|
|||
ld.List.Identifier = "" // Reset the identifier to trigger regenerating a new one
|
||||
// Set the owner to the current user
|
||||
ld.List.OwnerID = a.GetID()
|
||||
if err := CreateOrUpdateList(ld.List); err != nil {
|
||||
if err := CreateOrUpdateList(s, ld.List); err != nil {
|
||||
// If there is no available unique list identifier, just reset it.
|
||||
if IsErrListIdentifierIsNotUnique(err) {
|
||||
ld.List.Identifier = ""
|
||||
|
@ -90,7 +91,7 @@ func (ld *ListDuplicate) Create(a web.Auth) (err error) {
|
|||
// Used to map the newly created tasks to their new buckets
|
||||
bucketMap := make(map[int64]int64)
|
||||
buckets := []*Bucket{}
|
||||
err = x.Where("list_id = ?", ld.ListID).Find(&buckets)
|
||||
err = s.Where("list_id = ?", ld.ListID).Find(&buckets)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -98,7 +99,7 @@ func (ld *ListDuplicate) Create(a web.Auth) (err error) {
|
|||
oldID := b.ID
|
||||
b.ID = 0
|
||||
b.ListID = ld.List.ID
|
||||
if err := b.Create(a); err != nil {
|
||||
if err := b.Create(s, a); err != nil {
|
||||
return err
|
||||
}
|
||||
bucketMap[oldID] = b.ID
|
||||
|
@ -107,7 +108,7 @@ func (ld *ListDuplicate) Create(a web.Auth) (err error) {
|
|||
log.Debugf("Duplicated all buckets from list %d into %d", ld.ListID, ld.List.ID)
|
||||
|
||||
// Get all tasks + all task details
|
||||
tasks, _, _, err := getTasksForLists([]*List{{ID: ld.ListID}}, a, &taskOptions{})
|
||||
tasks, _, _, err := getTasksForLists(s, []*List{{ID: ld.ListID}}, a, &taskOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -123,10 +124,8 @@ func (ld *ListDuplicate) Create(a web.Auth) (err error) {
|
|||
t.ListID = ld.List.ID
|
||||
t.BucketID = bucketMap[t.BucketID]
|
||||
t.UID = ""
|
||||
s := x.NewSession()
|
||||
err := createTask(s, t, a, false)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return err
|
||||
}
|
||||
taskMap[oldID] = t.ID
|
||||
|
@ -138,7 +137,7 @@ func (ld *ListDuplicate) Create(a web.Auth) (err error) {
|
|||
// Save all attachments
|
||||
// We also duplicate all underlying files since they could be modified in one list which would result in
|
||||
// file changes in the other list which is not something we want.
|
||||
attachments, err := getTaskAttachmentsByTaskIDs(oldTaskIDs)
|
||||
attachments, err := getTaskAttachmentsByTaskIDs(s, oldTaskIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -164,7 +163,7 @@ func (ld *ListDuplicate) Create(a web.Auth) (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
err := attachment.NewAttachment(attachment.File.File, attachment.File.Name, attachment.File.Size, a)
|
||||
err := attachment.NewAttachment(s, attachment.File.File, attachment.File.Name, attachment.File.Size, a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -180,7 +179,7 @@ func (ld *ListDuplicate) Create(a web.Auth) (err error) {
|
|||
|
||||
// Copy label tasks (not the labels)
|
||||
labelTasks := []*LabelTask{}
|
||||
err = x.In("task_id", oldTaskIDs).Find(&labelTasks)
|
||||
err = s.In("task_id", oldTaskIDs).Find(&labelTasks)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -188,7 +187,7 @@ func (ld *ListDuplicate) Create(a web.Auth) (err error) {
|
|||
for _, lt := range labelTasks {
|
||||
lt.ID = 0
|
||||
lt.TaskID = taskMap[lt.TaskID]
|
||||
if _, err := x.Insert(lt); err != nil {
|
||||
if _, err := s.Insert(lt); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -198,7 +197,7 @@ func (ld *ListDuplicate) Create(a web.Auth) (err error) {
|
|||
// Assignees
|
||||
// Only copy those assignees who have access to the task
|
||||
assignees := []*TaskAssginee{}
|
||||
err = x.In("task_id", oldTaskIDs).Find(&assignees)
|
||||
err = s.In("task_id", oldTaskIDs).Find(&assignees)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -207,7 +206,7 @@ func (ld *ListDuplicate) Create(a web.Auth) (err error) {
|
|||
ID: taskMap[a.TaskID],
|
||||
ListID: ld.List.ID,
|
||||
}
|
||||
if err := t.addNewAssigneeByID(a.UserID, ld.List); err != nil {
|
||||
if err := t.addNewAssigneeByID(s, a.UserID, ld.List); err != nil {
|
||||
if IsErrUserDoesNotHaveAccessToList(err) {
|
||||
continue
|
||||
}
|
||||
|
@ -219,14 +218,14 @@ func (ld *ListDuplicate) Create(a web.Auth) (err error) {
|
|||
|
||||
// Comments
|
||||
comments := []*TaskComment{}
|
||||
err = x.In("task_id", oldTaskIDs).Find(&comments)
|
||||
err = s.In("task_id", oldTaskIDs).Find(&comments)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, c := range comments {
|
||||
c.ID = 0
|
||||
c.TaskID = taskMap[c.TaskID]
|
||||
if _, err := x.Insert(c); err != nil {
|
||||
if _, err := s.Insert(c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -237,7 +236,7 @@ func (ld *ListDuplicate) Create(a web.Auth) (err error) {
|
|||
// Low-Effort: Only copy those relations which are between tasks in the same list
|
||||
// because we can do that without a lot of hassle
|
||||
relations := []*TaskRelation{}
|
||||
err = x.In("task_id", oldTaskIDs).Find(&relations)
|
||||
err = s.In("task_id", oldTaskIDs).Find(&relations)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -249,7 +248,7 @@ func (ld *ListDuplicate) Create(a web.Auth) (err error) {
|
|||
r.ID = 0
|
||||
r.OtherTaskID = otherTaskID
|
||||
r.TaskID = taskMap[r.TaskID]
|
||||
if _, err := x.Insert(r); err != nil {
|
||||
if _, err := s.Insert(r); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -276,19 +275,19 @@ func (ld *ListDuplicate) Create(a web.Auth) (err error) {
|
|||
}
|
||||
|
||||
// Get unsplash info if applicable
|
||||
up, err := GetUnsplashPhotoByFileID(ld.List.BackgroundFileID)
|
||||
up, err := GetUnsplashPhotoByFileID(s, ld.List.BackgroundFileID)
|
||||
if err != nil && files.IsErrFileIsNotUnsplashFile(err) {
|
||||
return err
|
||||
}
|
||||
if up != nil {
|
||||
up.ID = 0
|
||||
up.FileID = file.ID
|
||||
if err := up.Save(); err != nil {
|
||||
if err := up.Save(s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := SetListBackground(ld.List.ID, file); err != nil {
|
||||
if err := SetListBackground(s, ld.List.ID, file); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -298,14 +297,14 @@ func (ld *ListDuplicate) Create(a web.Auth) (err error) {
|
|||
// Rights / Shares
|
||||
// To keep it simple(r) we will only copy rights which are directly used with the list, no namespace changes.
|
||||
users := []*ListUser{}
|
||||
err = x.Where("list_id = ?", ld.ListID).Find(&users)
|
||||
err = s.Where("list_id = ?", ld.ListID).Find(&users)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, u := range users {
|
||||
u.ID = 0
|
||||
u.ListID = ld.List.ID
|
||||
if _, err := x.Insert(u); err != nil {
|
||||
if _, err := s.Insert(u); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -313,21 +312,21 @@ func (ld *ListDuplicate) Create(a web.Auth) (err error) {
|
|||
log.Debugf("Duplicated user shares from list %d into %d", ld.ListID, ld.List.ID)
|
||||
|
||||
teams := []*TeamList{}
|
||||
err = x.Where("list_id = ?", ld.ListID).Find(&teams)
|
||||
err = s.Where("list_id = ?", ld.ListID).Find(&teams)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, t := range teams {
|
||||
t.ID = 0
|
||||
t.ListID = ld.List.ID
|
||||
if _, err := x.Insert(t); err != nil {
|
||||
if _, err := s.Insert(t); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Generate new link shares if any are available
|
||||
linkShares := []*LinkSharing{}
|
||||
err = x.Where("list_id = ?", ld.ListID).Find(&linkShares)
|
||||
err = s.Where("list_id = ?", ld.ListID).Find(&linkShares)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -335,7 +334,7 @@ func (ld *ListDuplicate) Create(a web.Auth) (err error) {
|
|||
share.ID = 0
|
||||
share.ListID = ld.List.ID
|
||||
share.Hash = utils.MakeRandomString(40)
|
||||
if _, err := x.Insert(share); err != nil {
|
||||
if _, err := s.Insert(share); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,8 @@ func TestListDuplicate(t *testing.T) {
|
|||
|
||||
db.LoadAndAssertFixtures(t)
|
||||
files.InitTestFileFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
u := &user.User{
|
||||
ID: 1,
|
||||
|
@ -38,10 +40,10 @@ func TestListDuplicate(t *testing.T) {
|
|||
ListID: 1,
|
||||
NamespaceID: 1,
|
||||
}
|
||||
can, err := l.CanCreate(u)
|
||||
can, err := l.CanCreate(s, u)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, can)
|
||||
err = l.Create(u)
|
||||
err = l.Create(s, u)
|
||||
assert.NoError(t, err)
|
||||
// To make this test 100% useful, it would need to assert a lot more stuff, but it is good enough for now.
|
||||
// Also, we're lacking utility functions to do all needed assertions.
|
||||
|
|
|
@ -20,10 +20,11 @@ import (
|
|||
"code.vikunja.io/api/pkg/user"
|
||||
"code.vikunja.io/web"
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// CanWrite return whether the user can write on that list or not
|
||||
func (l *List) CanWrite(a web.Auth) (bool, error) {
|
||||
func (l *List) CanWrite(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
|
||||
// The favorite list can't be edited
|
||||
if l.ID == FavoritesPseudoList.ID {
|
||||
|
@ -31,15 +32,14 @@ func (l *List) CanWrite(a web.Auth) (bool, error) {
|
|||
}
|
||||
|
||||
// Get the list and check the right
|
||||
originalList := &List{ID: l.ID}
|
||||
err := originalList.GetSimpleByID()
|
||||
originalList, err := GetListSimpleByID(s, l.ID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// We put the result of the is archived check in a separate variable to be able to return it later without
|
||||
// needing to recheck it again
|
||||
errIsArchived := originalList.CheckIsArchived()
|
||||
errIsArchived := originalList.CheckIsArchived(s)
|
||||
|
||||
var canWrite bool
|
||||
|
||||
|
@ -59,7 +59,7 @@ func (l *List) CanWrite(a web.Auth) (bool, error) {
|
|||
return canWrite, errIsArchived
|
||||
}
|
||||
|
||||
canWrite, _, err = originalList.checkRight(a, RightWrite, RightAdmin)
|
||||
canWrite, _, err = originalList.checkRight(s, a, RightWrite, RightAdmin)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ func (l *List) CanWrite(a web.Auth) (bool, error) {
|
|||
}
|
||||
|
||||
// CanRead checks if a user has read access to a list
|
||||
func (l *List) CanRead(a web.Auth) (bool, int, error) {
|
||||
func (l *List) CanRead(s *xorm.Session, a web.Auth) (bool, int, error) {
|
||||
|
||||
// The favorite list needs a special treatment
|
||||
if l.ID == FavoritesPseudoList.ID {
|
||||
|
@ -84,14 +84,18 @@ func (l *List) CanRead(a web.Auth) (bool, int, error) {
|
|||
// Saved Filter Lists need a special case
|
||||
if getSavedFilterIDFromListID(l.ID) > 0 {
|
||||
sf := &SavedFilter{ID: getSavedFilterIDFromListID(l.ID)}
|
||||
return sf.CanRead(a)
|
||||
return sf.CanRead(s, a)
|
||||
}
|
||||
|
||||
// Check if the user is either owner or can read
|
||||
if err := l.GetSimpleByID(); err != nil {
|
||||
var err error
|
||||
originalList, err := GetListSimpleByID(s, l.ID)
|
||||
if err != nil {
|
||||
return false, 0, err
|
||||
}
|
||||
|
||||
*l = *originalList
|
||||
|
||||
// Check if we're dealing with a share auth
|
||||
shareAuth, ok := a.(*LinkSharing)
|
||||
if ok {
|
||||
|
@ -102,16 +106,16 @@ func (l *List) CanRead(a web.Auth) (bool, int, error) {
|
|||
if l.isOwner(&user.User{ID: a.GetID()}) {
|
||||
return true, int(RightAdmin), nil
|
||||
}
|
||||
return l.checkRight(a, RightRead, RightWrite, RightAdmin)
|
||||
return l.checkRight(s, a, RightRead, RightWrite, RightAdmin)
|
||||
}
|
||||
|
||||
// CanUpdate checks if the user can update a list
|
||||
func (l *List) CanUpdate(a web.Auth) (canUpdate bool, err error) {
|
||||
func (l *List) CanUpdate(s *xorm.Session, a web.Auth) (canUpdate bool, err error) {
|
||||
// The favorite list can't be edited
|
||||
if l.ID == FavoritesPseudoList.ID {
|
||||
return false, nil
|
||||
}
|
||||
canUpdate, err = l.CanWrite(a)
|
||||
canUpdate, err = l.CanWrite(s, a)
|
||||
// If the list is archived and the user tries to un-archive it, let the request through
|
||||
if IsErrListIsArchived(err) && !l.IsArchived {
|
||||
err = nil
|
||||
|
@ -120,26 +124,25 @@ func (l *List) CanUpdate(a web.Auth) (canUpdate bool, err error) {
|
|||
}
|
||||
|
||||
// CanDelete checks if the user can delete a list
|
||||
func (l *List) CanDelete(a web.Auth) (bool, error) {
|
||||
return l.IsAdmin(a)
|
||||
func (l *List) CanDelete(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
return l.IsAdmin(s, a)
|
||||
}
|
||||
|
||||
// CanCreate checks if the user can create a list
|
||||
func (l *List) CanCreate(a web.Auth) (bool, error) {
|
||||
func (l *List) CanCreate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
// A user can create a list if they have write access to the namespace
|
||||
n := &Namespace{ID: l.NamespaceID}
|
||||
return n.CanWrite(a)
|
||||
return n.CanWrite(s, a)
|
||||
}
|
||||
|
||||
// IsAdmin returns whether the user has admin rights on the list or not
|
||||
func (l *List) IsAdmin(a web.Auth) (bool, error) {
|
||||
func (l *List) IsAdmin(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
// The favorite list can't be edited
|
||||
if l.ID == FavoritesPseudoList.ID {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
originalList := &List{ID: l.ID}
|
||||
err := originalList.GetSimpleByID()
|
||||
originalList, err := GetListSimpleByID(s, l.ID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -156,7 +159,7 @@ func (l *List) IsAdmin(a web.Auth) (bool, error) {
|
|||
if originalList.isOwner(&user.User{ID: a.GetID()}) {
|
||||
return true, nil
|
||||
}
|
||||
is, _, err := originalList.checkRight(a, RightAdmin)
|
||||
is, _, err := originalList.checkRight(s, a, RightAdmin)
|
||||
return is, err
|
||||
}
|
||||
|
||||
|
@ -166,7 +169,7 @@ func (l *List) isOwner(u *user.User) bool {
|
|||
}
|
||||
|
||||
// Checks n different rights for any given user
|
||||
func (l *List) checkRight(a web.Auth, rights ...Right) (bool, int, error) {
|
||||
func (l *List) checkRight(s *xorm.Session, a web.Auth, rights ...Right) (bool, int, error) {
|
||||
|
||||
/*
|
||||
The following loop creates an sql condition like this one:
|
||||
|
@ -218,7 +221,7 @@ func (l *List) checkRight(a web.Auth, rights ...Right) (bool, int, error) {
|
|||
|
||||
r := &allListRights{}
|
||||
var maxRight = 0
|
||||
exists, err := x.
|
||||
exists, err := s.
|
||||
Table("list").
|
||||
Alias("l").
|
||||
// User stuff
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"time"
|
||||
|
||||
"code.vikunja.io/web"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// TeamList defines the relation between a team and a list
|
||||
|
@ -68,7 +69,7 @@ type TeamWithRight struct {
|
|||
// @Failure 403 {object} web.HTTPError "The user does not have access to the list"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{id}/teams [put]
|
||||
func (tl *TeamList) Create(a web.Auth) (err error) {
|
||||
func (tl *TeamList) Create(s *xorm.Session, a web.Auth) (err error) {
|
||||
|
||||
// Check if the rights are valid
|
||||
if err = tl.Right.isValid(); err != nil {
|
||||
|
@ -76,19 +77,19 @@ func (tl *TeamList) Create(a web.Auth) (err error) {
|
|||
}
|
||||
|
||||
// Check if the team exists
|
||||
_, err = GetTeamByID(tl.TeamID)
|
||||
_, err = GetTeamByID(s, tl.TeamID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the list exists
|
||||
l := &List{ID: tl.ListID}
|
||||
if err := l.GetSimpleByID(); err != nil {
|
||||
l, err := GetListSimpleByID(s, tl.ListID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if the team is already on the list
|
||||
exists, err := x.Where("team_id = ?", tl.TeamID).
|
||||
exists, err := s.Where("team_id = ?", tl.TeamID).
|
||||
And("list_id = ?", tl.ListID).
|
||||
Get(&TeamList{})
|
||||
if err != nil {
|
||||
|
@ -99,12 +100,12 @@ func (tl *TeamList) Create(a web.Auth) (err error) {
|
|||
}
|
||||
|
||||
// Insert the new team
|
||||
_, err = x.Insert(tl)
|
||||
_, err = s.Insert(tl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = updateListLastUpdated(l)
|
||||
err = updateListLastUpdated(s, l)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -121,16 +122,17 @@ func (tl *TeamList) Create(a web.Auth) (err error) {
|
|||
// @Failure 404 {object} web.HTTPError "Team or list does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{listID}/teams/{teamID} [delete]
|
||||
func (tl *TeamList) Delete() (err error) {
|
||||
func (tl *TeamList) Delete(s *xorm.Session) (err error) {
|
||||
|
||||
// Check if the team exists
|
||||
_, err = GetTeamByID(tl.TeamID)
|
||||
_, err = GetTeamByID(s, tl.TeamID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the team has access to the list
|
||||
has, err := x.Where("team_id = ? AND list_id = ?", tl.TeamID, tl.ListID).
|
||||
has, err := s.
|
||||
Where("team_id = ? AND list_id = ?", tl.TeamID, tl.ListID).
|
||||
Get(&TeamList{})
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -140,14 +142,14 @@ func (tl *TeamList) Delete() (err error) {
|
|||
}
|
||||
|
||||
// Delete the relation
|
||||
_, err = x.Where("team_id = ?", tl.TeamID).
|
||||
_, err = s.Where("team_id = ?", tl.TeamID).
|
||||
And("list_id = ?", tl.ListID).
|
||||
Delete(TeamList{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = updateListLastUpdated(&List{ID: tl.ListID})
|
||||
err = updateListLastUpdated(s, &List{ID: tl.ListID})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -166,10 +168,10 @@ func (tl *TeamList) Delete() (err error) {
|
|||
// @Failure 403 {object} web.HTTPError "No right to see the list."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{id}/teams [get]
|
||||
func (tl *TeamList) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, totalItems int64, err error) {
|
||||
func (tl *TeamList) ReadAll(s *xorm.Session, a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, totalItems int64, err error) {
|
||||
// Check if the user can read the namespace
|
||||
l := &List{ID: tl.ListID}
|
||||
canRead, _, err := l.CanRead(a)
|
||||
canRead, _, err := l.CanRead(s, a)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
@ -181,7 +183,7 @@ func (tl *TeamList) ReadAll(a web.Auth, search string, page int, perPage int) (r
|
|||
|
||||
// Get the teams
|
||||
all := []*TeamWithRight{}
|
||||
query := x.
|
||||
query := s.
|
||||
Table("teams").
|
||||
Join("INNER", "team_list", "team_id = teams.id").
|
||||
Where("team_list.list_id = ?", tl.ListID).
|
||||
|
@ -199,12 +201,12 @@ func (tl *TeamList) ReadAll(a web.Auth, search string, page int, perPage int) (r
|
|||
teams = append(teams, &t.Team)
|
||||
}
|
||||
|
||||
err = addMoreInfoToTeams(teams)
|
||||
err = addMoreInfoToTeams(s, teams)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
totalItems, err = x.
|
||||
totalItems, err = s.
|
||||
Table("teams").
|
||||
Join("INNER", "team_list", "team_id = teams.id").
|
||||
Where("team_list.list_id = ?", tl.ListID).
|
||||
|
@ -232,14 +234,14 @@ func (tl *TeamList) ReadAll(a web.Auth, search string, page int, perPage int) (r
|
|||
// @Failure 404 {object} web.HTTPError "Team or list does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{listID}/teams/{teamID} [post]
|
||||
func (tl *TeamList) Update() (err error) {
|
||||
func (tl *TeamList) Update(s *xorm.Session) (err error) {
|
||||
|
||||
// Check if the right is valid
|
||||
if err := tl.Right.isValid(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = x.
|
||||
_, err = s.
|
||||
Where("list_id = ? AND team_id = ?", tl.ListID, tl.TeamID).
|
||||
Cols("right").
|
||||
Update(tl)
|
||||
|
@ -247,6 +249,6 @@ func (tl *TeamList) Update() (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
err = updateListLastUpdated(&List{ID: tl.ListID})
|
||||
err = updateListLastUpdated(s, &List{ID: tl.ListID})
|
||||
return
|
||||
}
|
||||
|
|
|
@ -18,29 +18,30 @@ package models
|
|||
|
||||
import (
|
||||
"code.vikunja.io/web"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// CanCreate checks if the user can create a team <-> list relation
|
||||
func (tl *TeamList) CanCreate(a web.Auth) (bool, error) {
|
||||
return tl.canDoTeamList(a)
|
||||
func (tl *TeamList) CanCreate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
return tl.canDoTeamList(s, a)
|
||||
}
|
||||
|
||||
// CanDelete checks if the user can delete a team <-> list relation
|
||||
func (tl *TeamList) CanDelete(a web.Auth) (bool, error) {
|
||||
return tl.canDoTeamList(a)
|
||||
func (tl *TeamList) CanDelete(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
return tl.canDoTeamList(s, a)
|
||||
}
|
||||
|
||||
// CanUpdate checks if the user can update a team <-> list relation
|
||||
func (tl *TeamList) CanUpdate(a web.Auth) (bool, error) {
|
||||
return tl.canDoTeamList(a)
|
||||
func (tl *TeamList) CanUpdate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
return tl.canDoTeamList(s, a)
|
||||
}
|
||||
|
||||
func (tl *TeamList) canDoTeamList(a web.Auth) (bool, error) {
|
||||
func (tl *TeamList) canDoTeamList(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
// Link shares aren't allowed to do anything
|
||||
if _, is := a.(*LinkSharing); is {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
l := List{ID: tl.ListID}
|
||||
return l.IsAdmin(a)
|
||||
return l.IsAdmin(s, a)
|
||||
}
|
||||
|
|
|
@ -37,20 +37,24 @@ func TestTeamList_ReadAll(t *testing.T) {
|
|||
ListID: 3,
|
||||
}
|
||||
db.LoadAndAssertFixtures(t)
|
||||
teams, _, _, err := tl.ReadAll(u, "", 1, 50)
|
||||
s := db.NewSession()
|
||||
teams, _, _, err := tl.ReadAll(s, u, "", 1, 50)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, reflect.TypeOf(teams).Kind(), reflect.Slice)
|
||||
s := reflect.ValueOf(teams)
|
||||
assert.Equal(t, s.Len(), 1)
|
||||
ts := reflect.ValueOf(teams)
|
||||
assert.Equal(t, ts.Len(), 1)
|
||||
_ = s.Close()
|
||||
})
|
||||
t.Run("nonexistant list", func(t *testing.T) {
|
||||
tl := TeamList{
|
||||
ListID: 99999,
|
||||
}
|
||||
db.LoadAndAssertFixtures(t)
|
||||
_, _, _, err := tl.ReadAll(u, "", 1, 50)
|
||||
s := db.NewSession()
|
||||
_, _, _, err := tl.ReadAll(s, u, "", 1, 50)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrListDoesNotExist(err))
|
||||
_ = s.Close()
|
||||
})
|
||||
t.Run("namespace owner", func(t *testing.T) {
|
||||
tl := TeamList{
|
||||
|
@ -59,8 +63,10 @@ func TestTeamList_ReadAll(t *testing.T) {
|
|||
Right: RightAdmin,
|
||||
}
|
||||
db.LoadAndAssertFixtures(t)
|
||||
_, _, _, err := tl.ReadAll(u, "", 1, 50)
|
||||
s := db.NewSession()
|
||||
_, _, _, err := tl.ReadAll(s, u, "", 1, 50)
|
||||
assert.NoError(t, err)
|
||||
_ = s.Close()
|
||||
})
|
||||
t.Run("no access", func(t *testing.T) {
|
||||
tl := TeamList{
|
||||
|
@ -69,9 +75,11 @@ func TestTeamList_ReadAll(t *testing.T) {
|
|||
Right: RightAdmin,
|
||||
}
|
||||
db.LoadAndAssertFixtures(t)
|
||||
_, _, _, err := tl.ReadAll(u, "", 1, 50)
|
||||
s := db.NewSession()
|
||||
_, _, _, err := tl.ReadAll(s, u, "", 1, 50)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrNeedToHaveListReadAccess(err))
|
||||
_ = s.Close()
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -79,14 +87,17 @@ func TestTeamList_Create(t *testing.T) {
|
|||
u := &user.User{ID: 1}
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
tl := TeamList{
|
||||
TeamID: 1,
|
||||
ListID: 1,
|
||||
Right: RightAdmin,
|
||||
}
|
||||
allowed, _ := tl.CanCreate(u)
|
||||
allowed, _ := tl.CanCreate(s, u)
|
||||
assert.True(t, allowed)
|
||||
err := tl.Create(u)
|
||||
err := tl.Create(s, u)
|
||||
assert.NoError(t, err)
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
db.AssertExists(t, "team_list", map[string]interface{}{
|
||||
"team_id": 1,
|
||||
|
@ -96,56 +107,67 @@ func TestTeamList_Create(t *testing.T) {
|
|||
})
|
||||
t.Run("team already has access", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
tl := TeamList{
|
||||
TeamID: 1,
|
||||
ListID: 3,
|
||||
Right: RightAdmin,
|
||||
}
|
||||
err := tl.Create(u)
|
||||
err := tl.Create(s, u)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTeamAlreadyHasAccess(err))
|
||||
_ = s.Close()
|
||||
})
|
||||
t.Run("wrong rights", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
tl := TeamList{
|
||||
TeamID: 1,
|
||||
ListID: 1,
|
||||
Right: RightUnknown,
|
||||
}
|
||||
err := tl.Create(u)
|
||||
err := tl.Create(s, u)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrInvalidRight(err))
|
||||
_ = s.Close()
|
||||
})
|
||||
t.Run("nonexistant team", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
tl := TeamList{
|
||||
TeamID: 9999,
|
||||
ListID: 1,
|
||||
}
|
||||
err := tl.Create(u)
|
||||
err := tl.Create(s, u)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTeamDoesNotExist(err))
|
||||
_ = s.Close()
|
||||
})
|
||||
t.Run("nonexistant list", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
tl := TeamList{
|
||||
TeamID: 1,
|
||||
ListID: 9999,
|
||||
}
|
||||
err := tl.Create(u)
|
||||
err := tl.Create(s, u)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrListDoesNotExist(err))
|
||||
_ = s.Close()
|
||||
})
|
||||
}
|
||||
|
||||
func TestTeamList_Delete(t *testing.T) {
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
tl := TeamList{
|
||||
TeamID: 1,
|
||||
ListID: 3,
|
||||
}
|
||||
err := tl.Delete()
|
||||
err := tl.Delete(s)
|
||||
assert.NoError(t, err)
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
db.AssertMissing(t, "team_list", map[string]interface{}{
|
||||
"team_id": 1,
|
||||
|
@ -154,23 +176,27 @@ func TestTeamList_Delete(t *testing.T) {
|
|||
})
|
||||
t.Run("nonexistant team", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
tl := TeamList{
|
||||
TeamID: 9999,
|
||||
ListID: 1,
|
||||
}
|
||||
err := tl.Delete()
|
||||
err := tl.Delete(s)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTeamDoesNotExist(err))
|
||||
_ = s.Close()
|
||||
})
|
||||
t.Run("nonexistant list", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
tl := TeamList{
|
||||
TeamID: 1,
|
||||
ListID: 9999,
|
||||
}
|
||||
err := tl.Delete()
|
||||
err := tl.Delete(s)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTeamDoesNotHaveAccessToList(err))
|
||||
_ = s.Close()
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -229,6 +255,7 @@ func TestTeamList_Update(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
|
||||
tl := &TeamList{
|
||||
ID: tt.fields.ID,
|
||||
|
@ -240,13 +267,15 @@ func TestTeamList_Update(t *testing.T) {
|
|||
CRUDable: tt.fields.CRUDable,
|
||||
Rights: tt.fields.Rights,
|
||||
}
|
||||
err := tl.Update()
|
||||
err := tl.Update(s)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("TeamList.Update() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
if (err != nil) && tt.wantErr && !tt.errType(err) {
|
||||
t.Errorf("TeamList.Update() Wrong error type! Error = %v, want = %v", err, runtime.FuncForPC(reflect.ValueOf(tt.errType).Pointer()).Name())
|
||||
}
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
if !tt.wantErr {
|
||||
db.AssertExists(t, "team_list", map[string]interface{}{
|
||||
"list_id": tt.fields.ListID,
|
||||
|
|
|
@ -35,12 +35,15 @@ func TestList_CreateOrUpdate(t *testing.T) {
|
|||
t.Run("create", func(t *testing.T) {
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
list := List{
|
||||
Title: "test",
|
||||
Description: "Lorem Ipsum",
|
||||
NamespaceID: 1,
|
||||
}
|
||||
err := list.Create(usr)
|
||||
err := list.Create(s, usr)
|
||||
assert.NoError(t, err)
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
db.AssertExists(t, "list", map[string]interface{}{
|
||||
"id": list.ID,
|
||||
|
@ -51,49 +54,56 @@ func TestList_CreateOrUpdate(t *testing.T) {
|
|||
})
|
||||
t.Run("nonexistant namespace", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
list := List{
|
||||
Title: "test",
|
||||
Description: "Lorem Ipsum",
|
||||
NamespaceID: 999999,
|
||||
}
|
||||
|
||||
err := list.Create(usr)
|
||||
err := list.Create(s, usr)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrNamespaceDoesNotExist(err))
|
||||
_ = s.Close()
|
||||
})
|
||||
t.Run("nonexistant owner", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
usr := &user.User{ID: 9482385}
|
||||
list := List{
|
||||
Title: "test",
|
||||
Description: "Lorem Ipsum",
|
||||
NamespaceID: 1,
|
||||
}
|
||||
err := list.Create(usr)
|
||||
err := list.Create(s, usr)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, user.IsErrUserDoesNotExist(err))
|
||||
_ = s.Close()
|
||||
})
|
||||
t.Run("existing identifier", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
list := List{
|
||||
Title: "test",
|
||||
Description: "Lorem Ipsum",
|
||||
Identifier: "test1",
|
||||
NamespaceID: 1,
|
||||
}
|
||||
|
||||
err := list.Create(usr)
|
||||
err := list.Create(s, usr)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrListIdentifierIsNotUnique(err))
|
||||
_ = s.Close()
|
||||
})
|
||||
t.Run("non ascii characters", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
list := List{
|
||||
Title: "приффки фсем",
|
||||
Description: "Lorem Ipsum",
|
||||
NamespaceID: 1,
|
||||
}
|
||||
err := list.Create(usr)
|
||||
err := list.Create(s, usr)
|
||||
assert.NoError(t, err)
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
db.AssertExists(t, "list", map[string]interface{}{
|
||||
"id": list.ID,
|
||||
|
@ -107,6 +117,7 @@ func TestList_CreateOrUpdate(t *testing.T) {
|
|||
t.Run("update", func(t *testing.T) {
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
list := List{
|
||||
ID: 1,
|
||||
Title: "test",
|
||||
|
@ -114,7 +125,9 @@ func TestList_CreateOrUpdate(t *testing.T) {
|
|||
NamespaceID: 1,
|
||||
}
|
||||
list.Description = "Lorem Ipsum dolor sit amet."
|
||||
err := list.Update()
|
||||
err := list.Update(s)
|
||||
assert.NoError(t, err)
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
db.AssertExists(t, "list", map[string]interface{}{
|
||||
"id": list.ID,
|
||||
|
@ -125,37 +138,43 @@ func TestList_CreateOrUpdate(t *testing.T) {
|
|||
})
|
||||
t.Run("nonexistant", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
list := List{
|
||||
ID: 99999999,
|
||||
Title: "test",
|
||||
}
|
||||
err := list.Update()
|
||||
err := list.Update(s)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrListDoesNotExist(err))
|
||||
_ = s.Close()
|
||||
|
||||
})
|
||||
t.Run("existing identifier", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
list := List{
|
||||
Title: "test",
|
||||
Description: "Lorem Ipsum",
|
||||
Identifier: "test1",
|
||||
NamespaceID: 1,
|
||||
}
|
||||
|
||||
err := list.Create(usr)
|
||||
err := list.Create(s, usr)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrListIdentifierIsNotUnique(err))
|
||||
_ = s.Close()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_Delete(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
list := List{
|
||||
ID: 1,
|
||||
}
|
||||
err := list.Delete()
|
||||
err := list.Delete(s)
|
||||
assert.NoError(t, err)
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
db.AssertMissing(t, "list", map[string]interface{}{
|
||||
"id": 1,
|
||||
|
@ -165,30 +184,34 @@ func TestList_Delete(t *testing.T) {
|
|||
func TestList_ReadAll(t *testing.T) {
|
||||
t.Run("all in namespace", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
// Get all lists for our namespace
|
||||
lists, err := GetListsByNamespaceID(1, &user.User{})
|
||||
lists, err := GetListsByNamespaceID(s, 1, &user.User{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, len(lists), 2)
|
||||
_ = s.Close()
|
||||
})
|
||||
t.Run("all lists for user", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
|
||||
s := db.NewSession()
|
||||
u := &user.User{ID: 1}
|
||||
list := List{}
|
||||
lists3, _, _, err := list.ReadAll(u, "", 1, 50)
|
||||
lists3, _, _, err := list.ReadAll(s, u, "", 1, 50)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, reflect.TypeOf(lists3).Kind(), reflect.Slice)
|
||||
s := reflect.ValueOf(lists3)
|
||||
assert.Equal(t, 16, s.Len())
|
||||
ls := reflect.ValueOf(lists3)
|
||||
assert.Equal(t, 16, ls.Len())
|
||||
_ = s.Close()
|
||||
})
|
||||
t.Run("lists for nonexistant user", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
|
||||
s := db.NewSession()
|
||||
usr := &user.User{ID: 999999}
|
||||
list := List{}
|
||||
_, _, _, err := list.ReadAll(usr, "", 1, 50)
|
||||
_, _, _, err := list.ReadAll(s, usr, "", 1, 50)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, user.IsErrUserDoesNotExist(err))
|
||||
_ = s.Close()
|
||||
})
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
"code.vikunja.io/web"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// ListUser represents a list <-> user relation
|
||||
|
@ -71,7 +72,7 @@ type UserWithRight struct {
|
|||
// @Failure 403 {object} web.HTTPError "The user does not have access to the list"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{id}/users [put]
|
||||
func (lu *ListUser) Create(a web.Auth) (err error) {
|
||||
func (lu *ListUser) Create(s *xorm.Session, a web.Auth) (err error) {
|
||||
|
||||
// Check if the right is valid
|
||||
if err := lu.Right.isValid(); err != nil {
|
||||
|
@ -79,17 +80,17 @@ func (lu *ListUser) Create(a web.Auth) (err error) {
|
|||
}
|
||||
|
||||
// Check if the list exists
|
||||
l := &List{ID: lu.ListID}
|
||||
if err = l.GetSimpleByID(); err != nil {
|
||||
l, err := GetListSimpleByID(s, lu.ListID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the user exists
|
||||
user, err := user.GetUserByUsername(lu.Username)
|
||||
u, err := user.GetUserByUsername(s, lu.Username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lu.UserID = user.ID
|
||||
lu.UserID = u.ID
|
||||
|
||||
// Check if the user already has access or is owner of that list
|
||||
// We explicitly DONT check for teams here
|
||||
|
@ -97,7 +98,7 @@ func (lu *ListUser) Create(a web.Auth) (err error) {
|
|||
return ErrUserAlreadyHasAccess{UserID: lu.UserID, ListID: lu.ListID}
|
||||
}
|
||||
|
||||
exist, err := x.Where("list_id = ? AND user_id = ?", lu.ListID, lu.UserID).Get(&ListUser{})
|
||||
exist, err := s.Where("list_id = ? AND user_id = ?", lu.ListID, lu.UserID).Get(&ListUser{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -106,12 +107,12 @@ func (lu *ListUser) Create(a web.Auth) (err error) {
|
|||
}
|
||||
|
||||
// Insert user <-> list relation
|
||||
_, err = x.Insert(lu)
|
||||
_, err = s.Insert(lu)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = updateListLastUpdated(l)
|
||||
err = updateListLastUpdated(s, l)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -128,17 +129,18 @@ func (lu *ListUser) Create(a web.Auth) (err error) {
|
|||
// @Failure 404 {object} web.HTTPError "user or list does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{listID}/users/{userID} [delete]
|
||||
func (lu *ListUser) Delete() (err error) {
|
||||
func (lu *ListUser) Delete(s *xorm.Session) (err error) {
|
||||
|
||||
// Check if the user exists
|
||||
user, err := user.GetUserByUsername(lu.Username)
|
||||
u, err := user.GetUserByUsername(s, lu.Username)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
lu.UserID = user.ID
|
||||
lu.UserID = u.ID
|
||||
|
||||
// Check if the user has access to the list
|
||||
has, err := x.Where("user_id = ? AND list_id = ?", lu.UserID, lu.ListID).
|
||||
has, err := s.
|
||||
Where("user_id = ? AND list_id = ?", lu.UserID, lu.ListID).
|
||||
Get(&ListUser{})
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -147,13 +149,14 @@ func (lu *ListUser) Delete() (err error) {
|
|||
return ErrUserDoesNotHaveAccessToList{ListID: lu.ListID, UserID: lu.UserID}
|
||||
}
|
||||
|
||||
_, err = x.Where("user_id = ? AND list_id = ?", lu.UserID, lu.ListID).
|
||||
_, err = s.
|
||||
Where("user_id = ? AND list_id = ?", lu.UserID, lu.ListID).
|
||||
Delete(&ListUser{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = updateListLastUpdated(&List{ID: lu.ListID})
|
||||
err = updateListLastUpdated(s, &List{ID: lu.ListID})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -172,10 +175,10 @@ func (lu *ListUser) Delete() (err error) {
|
|||
// @Failure 403 {object} web.HTTPError "No right to see the list."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{id}/users [get]
|
||||
func (lu *ListUser) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
||||
func (lu *ListUser) ReadAll(s *xorm.Session, a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
||||
// Check if the user has access to the list
|
||||
l := &List{ID: lu.ListID}
|
||||
canRead, _, err := l.CanRead(a)
|
||||
canRead, _, err := l.CanRead(s, a)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
@ -187,7 +190,7 @@ func (lu *ListUser) ReadAll(a web.Auth, search string, page int, perPage int) (r
|
|||
|
||||
// Get all users
|
||||
all := []*UserWithRight{}
|
||||
query := x.
|
||||
query := s.
|
||||
Join("INNER", "users_list", "user_id = users.id").
|
||||
Where("users_list.list_id = ?", lu.ListID).
|
||||
Where("users.username LIKE ?", "%"+search+"%")
|
||||
|
@ -204,7 +207,7 @@ func (lu *ListUser) ReadAll(a web.Auth, search string, page int, perPage int) (r
|
|||
u.Email = ""
|
||||
}
|
||||
|
||||
numberOfTotalItems, err = x.
|
||||
numberOfTotalItems, err = s.
|
||||
Join("INNER", "users_list", "user_id = users.id").
|
||||
Where("users_list.list_id = ?", lu.ListID).
|
||||
Where("users.username LIKE ?", "%"+search+"%").
|
||||
|
@ -228,7 +231,7 @@ func (lu *ListUser) ReadAll(a web.Auth, search string, page int, perPage int) (r
|
|||
// @Failure 404 {object} web.HTTPError "User or list does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{listID}/users/{userID} [post]
|
||||
func (lu *ListUser) Update() (err error) {
|
||||
func (lu *ListUser) Update(s *xorm.Session) (err error) {
|
||||
|
||||
// Check if the right is valid
|
||||
if err := lu.Right.isValid(); err != nil {
|
||||
|
@ -236,13 +239,13 @@ func (lu *ListUser) Update() (err error) {
|
|||
}
|
||||
|
||||
// Check if the user exists
|
||||
u, err := user.GetUserByUsername(lu.Username)
|
||||
u, err := user.GetUserByUsername(s, lu.Username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lu.UserID = u.ID
|
||||
|
||||
_, err = x.
|
||||
_, err = s.
|
||||
Where("list_id = ? AND user_id = ?", lu.ListID, lu.UserID).
|
||||
Cols("right").
|
||||
Update(lu)
|
||||
|
@ -250,6 +253,6 @@ func (lu *ListUser) Update() (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
err = updateListLastUpdated(&List{ID: lu.ListID})
|
||||
err = updateListLastUpdated(s, &List{ID: lu.ListID})
|
||||
return
|
||||
}
|
||||
|
|
|
@ -18,24 +18,25 @@ package models
|
|||
|
||||
import (
|
||||
"code.vikunja.io/web"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// CanCreate checks if the user can create a new user <-> list relation
|
||||
func (lu *ListUser) CanCreate(a web.Auth) (bool, error) {
|
||||
return lu.canDoListUser(a)
|
||||
func (lu *ListUser) CanCreate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
return lu.canDoListUser(s, a)
|
||||
}
|
||||
|
||||
// CanDelete checks if the user can delete a user <-> list relation
|
||||
func (lu *ListUser) CanDelete(a web.Auth) (bool, error) {
|
||||
return lu.canDoListUser(a)
|
||||
func (lu *ListUser) CanDelete(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
return lu.canDoListUser(s, a)
|
||||
}
|
||||
|
||||
// CanUpdate checks if the user can update a user <-> list relation
|
||||
func (lu *ListUser) CanUpdate(a web.Auth) (bool, error) {
|
||||
return lu.canDoListUser(a)
|
||||
func (lu *ListUser) CanUpdate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
return lu.canDoListUser(s, a)
|
||||
}
|
||||
|
||||
func (lu *ListUser) canDoListUser(a web.Auth) (bool, error) {
|
||||
func (lu *ListUser) canDoListUser(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
// Link shares aren't allowed to do anything
|
||||
if _, is := a.(*LinkSharing); is {
|
||||
return false, nil
|
||||
|
@ -43,5 +44,5 @@ func (lu *ListUser) canDoListUser(a web.Auth) (bool, error) {
|
|||
|
||||
// Get the list and check if the user has write access on it
|
||||
l := List{ID: lu.ListID}
|
||||
return l.IsAdmin(a)
|
||||
return l.IsAdmin(s, a)
|
||||
}
|
||||
|
|
|
@ -80,6 +80,7 @@ func TestListUser_CanDoSomething(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
|
||||
lu := &ListUser{
|
||||
ID: tt.fields.ID,
|
||||
|
@ -91,15 +92,16 @@ func TestListUser_CanDoSomething(t *testing.T) {
|
|||
CRUDable: tt.fields.CRUDable,
|
||||
Rights: tt.fields.Rights,
|
||||
}
|
||||
if got, _ := lu.CanCreate(tt.args.a); got != tt.want["CanCreate"] {
|
||||
if got, _ := lu.CanCreate(s, tt.args.a); got != tt.want["CanCreate"] {
|
||||
t.Errorf("ListUser.CanCreate() = %v, want %v", got, tt.want["CanCreate"])
|
||||
}
|
||||
if got, _ := lu.CanDelete(tt.args.a); got != tt.want["CanDelete"] {
|
||||
if got, _ := lu.CanDelete(s, tt.args.a); got != tt.want["CanDelete"] {
|
||||
t.Errorf("ListUser.CanDelete() = %v, want %v", got, tt.want["CanDelete"])
|
||||
}
|
||||
if got, _ := lu.CanUpdate(tt.args.a); got != tt.want["CanUpdate"] {
|
||||
if got, _ := lu.CanUpdate(s, tt.args.a); got != tt.want["CanUpdate"] {
|
||||
t.Errorf("ListUser.CanUpdate() = %v, want %v", got, tt.want["CanUpdate"])
|
||||
}
|
||||
_ = s.Close()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,9 +24,9 @@ import (
|
|||
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
"gopkg.in/d4l3k/messagediff.v1"
|
||||
|
||||
"code.vikunja.io/web"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/d4l3k/messagediff.v1"
|
||||
)
|
||||
|
||||
func TestListUser_Create(t *testing.T) {
|
||||
|
@ -108,6 +108,7 @@ func TestListUser_Create(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
|
||||
ul := &ListUser{
|
||||
ID: tt.fields.ID,
|
||||
|
@ -120,13 +121,17 @@ func TestListUser_Create(t *testing.T) {
|
|||
CRUDable: tt.fields.CRUDable,
|
||||
Rights: tt.fields.Rights,
|
||||
}
|
||||
err := ul.Create(tt.args.a)
|
||||
err := ul.Create(s, tt.args.a)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ListUser.Create() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
if (err != nil) && tt.wantErr && !tt.errType(err) {
|
||||
t.Errorf("ListUser.Create() Wrong error type! Error = %v, want = %v", err, runtime.FuncForPC(reflect.ValueOf(tt.errType).Pointer()).Name())
|
||||
}
|
||||
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
|
||||
if !tt.wantErr {
|
||||
db.AssertExists(t, "users_list", map[string]interface{}{
|
||||
"user_id": ul.UserID,
|
||||
|
@ -212,6 +217,7 @@ func TestListUser_ReadAll(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
|
||||
ul := &ListUser{
|
||||
ID: tt.fields.ID,
|
||||
|
@ -223,7 +229,7 @@ func TestListUser_ReadAll(t *testing.T) {
|
|||
CRUDable: tt.fields.CRUDable,
|
||||
Rights: tt.fields.Rights,
|
||||
}
|
||||
got, _, _, err := ul.ReadAll(tt.args.a, tt.args.search, tt.args.page, 50)
|
||||
got, _, _, err := ul.ReadAll(s, tt.args.a, tt.args.search, tt.args.page, 50)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ListUser.ReadAll() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
|
@ -233,6 +239,7 @@ func TestListUser_ReadAll(t *testing.T) {
|
|||
if diff, equal := messagediff.PrettyDiff(got, tt.want); !equal {
|
||||
t.Errorf("ListUser.ReadAll() = %v, want %v, diff: %v", got, tt.want, diff)
|
||||
}
|
||||
_ = s.Close()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -292,6 +299,7 @@ func TestListUser_Update(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
|
||||
lu := &ListUser{
|
||||
ID: tt.fields.ID,
|
||||
|
@ -303,13 +311,17 @@ func TestListUser_Update(t *testing.T) {
|
|||
CRUDable: tt.fields.CRUDable,
|
||||
Rights: tt.fields.Rights,
|
||||
}
|
||||
err := lu.Update()
|
||||
err := lu.Update(s)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ListUser.Update() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
if (err != nil) && tt.wantErr && !tt.errType(err) {
|
||||
t.Errorf("ListUser.Update() Wrong error type! Error = %v, want = %v", err, runtime.FuncForPC(reflect.ValueOf(tt.errType).Pointer()).Name())
|
||||
}
|
||||
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
|
||||
if !tt.wantErr {
|
||||
db.AssertExists(t, "users_list", map[string]interface{}{
|
||||
"list_id": tt.fields.ListID,
|
||||
|
@ -369,6 +381,7 @@ func TestListUser_Delete(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
|
||||
lu := &ListUser{
|
||||
ID: tt.fields.ID,
|
||||
|
@ -380,13 +393,17 @@ func TestListUser_Delete(t *testing.T) {
|
|||
CRUDable: tt.fields.CRUDable,
|
||||
Rights: tt.fields.Rights,
|
||||
}
|
||||
err := lu.Delete()
|
||||
err := lu.Delete(s)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ListUser.Delete() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
if (err != nil) && tt.wantErr && !tt.errType(err) {
|
||||
t.Errorf("ListUser.Delete() Wrong error type! Error = %v, want = %v", err, runtime.FuncForPC(reflect.ValueOf(tt.errType).Pointer()).Name())
|
||||
}
|
||||
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
|
||||
if !tt.wantErr {
|
||||
db.AssertMissing(t, "users_list", map[string]interface{}{
|
||||
"user_id": tt.fields.UserID,
|
||||
|
|
|
@ -23,12 +23,11 @@ import (
|
|||
"time"
|
||||
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
|
||||
"code.vikunja.io/api/pkg/metrics"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
"code.vikunja.io/web"
|
||||
"github.com/imdario/mergo"
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// Namespace holds informations about a namespace
|
||||
|
@ -95,55 +94,48 @@ func (Namespace) TableName() string {
|
|||
}
|
||||
|
||||
// GetSimpleByID gets a namespace without things like the owner, it more or less only checks if it exists.
|
||||
func (n *Namespace) GetSimpleByID() (err error) {
|
||||
if n.ID == 0 {
|
||||
return ErrNamespaceDoesNotExist{ID: n.ID}
|
||||
func getNamespaceSimpleByID(s *xorm.Session, id int64) (namespace *Namespace, err error) {
|
||||
if id == 0 {
|
||||
return nil, ErrNamespaceDoesNotExist{ID: id}
|
||||
}
|
||||
|
||||
// Get the namesapce with shared lists
|
||||
if n.ID == -1 {
|
||||
*n = SharedListsPseudoNamespace
|
||||
return
|
||||
if id == -1 {
|
||||
return &SharedListsPseudoNamespace, nil
|
||||
}
|
||||
|
||||
if n.ID == FavoritesPseudoNamespace.ID {
|
||||
*n = FavoritesPseudoNamespace
|
||||
return
|
||||
if id == FavoritesPseudoNamespace.ID {
|
||||
return &FavoritesPseudoNamespace, nil
|
||||
}
|
||||
|
||||
namespaceFromDB := &Namespace{}
|
||||
exists, err := x.Where("id = ?", n.ID).Get(namespaceFromDB)
|
||||
namespace = &Namespace{}
|
||||
|
||||
exists, err := s.Where("id = ?", id).Get(namespace)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !exists {
|
||||
return ErrNamespaceDoesNotExist{ID: n.ID}
|
||||
return nil, ErrNamespaceDoesNotExist{ID: id}
|
||||
}
|
||||
// We don't want to override the provided user struct because this would break updating, so we have to merge it
|
||||
if err := mergo.Merge(namespaceFromDB, n, mergo.WithOverride); err != nil {
|
||||
return err
|
||||
}
|
||||
*n = *namespaceFromDB
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetNamespaceByID returns a namespace object by its ID
|
||||
func GetNamespaceByID(id int64) (namespace Namespace, err error) {
|
||||
namespace = Namespace{ID: id}
|
||||
err = namespace.GetSimpleByID()
|
||||
func GetNamespaceByID(s *xorm.Session, id int64) (namespace *Namespace, err error) {
|
||||
namespace, err = getNamespaceSimpleByID(s, id)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Get the namespace Owner
|
||||
namespace.Owner, err = user.GetUserByID(namespace.OwnerID)
|
||||
namespace.Owner, err = user.GetUserByID(s, namespace.OwnerID)
|
||||
return
|
||||
}
|
||||
|
||||
// CheckIsArchived returns an ErrNamespaceIsArchived if the namepace is archived.
|
||||
func (n *Namespace) CheckIsArchived() error {
|
||||
exists, err := x.
|
||||
func (n *Namespace) CheckIsArchived(s *xorm.Session) error {
|
||||
exists, err := s.
|
||||
Where("id = ? AND is_archived = true", n.ID).
|
||||
Exist(&Namespace{})
|
||||
if err != nil {
|
||||
|
@ -167,8 +159,12 @@ func (n *Namespace) CheckIsArchived() error {
|
|||
// @Failure 403 {object} web.HTTPError "The user does not have access to that namespace."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /namespaces/{id} [get]
|
||||
func (n *Namespace) ReadOne() (err error) {
|
||||
*n, err = GetNamespaceByID(n.ID)
|
||||
func (n *Namespace) ReadOne(s *xorm.Session) (err error) {
|
||||
nn, err := GetNamespaceByID(s, n.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*n = *nn
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -207,7 +203,7 @@ func makeNamespaceSliceFromMap(namespaces map[int64]*NamespaceWithLists, userMap
|
|||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /namespaces [get]
|
||||
//nolint:gocyclo
|
||||
func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
||||
func (n *Namespace) ReadAll(s *xorm.Session, a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
||||
if _, is := a.(*LinkSharing); is {
|
||||
return nil, 0, 0, ErrGenericForbidden{}
|
||||
}
|
||||
|
@ -249,7 +245,7 @@ func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (r
|
|||
}
|
||||
|
||||
limit, start := getLimitFromPageIndex(page, perPage)
|
||||
query := x.Select("namespaces.*").
|
||||
query := s.Select("namespaces.*").
|
||||
Table("namespaces").
|
||||
Join("LEFT", "team_namespaces", "namespaces.id = team_namespaces.namespace_id").
|
||||
Join("LEFT", "team_members", "team_members.team_id = team_namespaces.team_id").
|
||||
|
@ -268,7 +264,7 @@ func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (r
|
|||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
numberOfTotalItems, err = x.
|
||||
numberOfTotalItems, err = s.
|
||||
Table("namespaces").
|
||||
Join("LEFT", "team_namespaces", "namespaces.id = team_namespaces.namespace_id").
|
||||
Join("LEFT", "team_members", "team_members.team_id = team_namespaces.team_id").
|
||||
|
@ -294,7 +290,7 @@ func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (r
|
|||
|
||||
// Get all owners
|
||||
userMap := make(map[int64]*user.User)
|
||||
err = x.In("id", userIDs).Find(&userMap)
|
||||
err = s.In("id", userIDs).Find(&userMap)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
@ -306,7 +302,7 @@ func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (r
|
|||
|
||||
// Get all lists
|
||||
lists := []*List{}
|
||||
listQuery := x.
|
||||
listQuery := s.
|
||||
In("namespace_id", namespaceids)
|
||||
|
||||
if !n.IsArchived {
|
||||
|
@ -330,7 +326,7 @@ func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (r
|
|||
|
||||
// Get all lists individually shared with our user (not via a namespace)
|
||||
individualLists := []*List{}
|
||||
iListQuery := x.Select("l.*").
|
||||
iListQuery := s.Select("l.*").
|
||||
Table("list").
|
||||
Alias("l").
|
||||
Join("LEFT", []string{"team_list", "tl"}, "l.id = tl.list_id").
|
||||
|
@ -360,7 +356,7 @@ func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (r
|
|||
}
|
||||
|
||||
// More details for the lists
|
||||
err = AddListDetails(lists)
|
||||
err = addListDetails(s, lists)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
@ -386,7 +382,7 @@ func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (r
|
|||
|
||||
// Check if we have any favorites or favorited lists and remove the favorites namespace from the list if not
|
||||
var favoriteCount int64
|
||||
favoriteCount, err = x.
|
||||
favoriteCount, err = s.
|
||||
Join("INNER", "list", "tasks.list_id = list.id").
|
||||
Join("INNER", "namespaces", "list.namespace_id = namespaces.id").
|
||||
Where(builder.And(builder.Eq{"tasks.is_favorite": true}, builder.In("namespaces.id", namespaceids))).
|
||||
|
@ -413,7 +409,7 @@ func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (r
|
|||
/////////////////
|
||||
// Saved Filters
|
||||
|
||||
savedFilters, err := getSavedFiltersForUser(a)
|
||||
savedFilters, err := getSavedFiltersForUser(s, a)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
@ -457,7 +453,7 @@ func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (r
|
|||
// @Failure 403 {object} web.HTTPError "The user does not have access to the namespace"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /namespaces [put]
|
||||
func (n *Namespace) Create(a web.Auth) (err error) {
|
||||
func (n *Namespace) Create(s *xorm.Session, a web.Auth) (err error) {
|
||||
// Check if we have at least a name
|
||||
if n.Title == "" {
|
||||
return ErrNamespaceNameCannotBeEmpty{NamespaceID: 0, UserID: a.GetID()}
|
||||
|
@ -465,14 +461,14 @@ func (n *Namespace) Create(a web.Auth) (err error) {
|
|||
n.ID = 0 // This would otherwise prevent the creation of new lists after one was created
|
||||
|
||||
// Check if the User exists
|
||||
n.Owner, err = user.GetUserByID(a.GetID())
|
||||
n.Owner, err = user.GetUserByID(s, a.GetID())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n.OwnerID = n.Owner.ID
|
||||
|
||||
// Insert
|
||||
if _, err = x.Insert(n); err != nil {
|
||||
if _, err = s.Insert(n); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -482,12 +478,12 @@ func (n *Namespace) Create(a web.Auth) (err error) {
|
|||
|
||||
// CreateNewNamespaceForUser creates a new namespace for a user. To prevent import cycles, we can't do that
|
||||
// directly in the user.Create function.
|
||||
func CreateNewNamespaceForUser(user *user.User) (err error) {
|
||||
func CreateNewNamespaceForUser(s *xorm.Session, user *user.User) (err error) {
|
||||
newN := &Namespace{
|
||||
Title: user.Username,
|
||||
Description: user.Username + "'s namespace.",
|
||||
}
|
||||
return newN.Create(user)
|
||||
return newN.Create(s, user)
|
||||
}
|
||||
|
||||
// Delete deletes a namespace
|
||||
|
@ -502,22 +498,22 @@ func CreateNewNamespaceForUser(user *user.User) (err error) {
|
|||
// @Failure 403 {object} web.HTTPError "The user does not have access to the namespace"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /namespaces/{id} [delete]
|
||||
func (n *Namespace) Delete() (err error) {
|
||||
func (n *Namespace) Delete(s *xorm.Session) (err error) {
|
||||
|
||||
// Check if the namespace exists
|
||||
_, err = GetNamespaceByID(n.ID)
|
||||
_, err = GetNamespaceByID(s, n.ID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Delete the namespace
|
||||
_, err = x.ID(n.ID).Delete(&Namespace{})
|
||||
_, err = s.ID(n.ID).Delete(&Namespace{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Delete all lists with their tasks
|
||||
lists, err := GetListsByNamespaceID(n.ID, &user.User{})
|
||||
lists, err := GetListsByNamespaceID(s, n.ID, &user.User{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -530,13 +526,13 @@ func (n *Namespace) Delete() (err error) {
|
|||
}
|
||||
|
||||
// Delete tasks
|
||||
_, err = x.In("list_id", listIDs).Delete(&Task{})
|
||||
_, err = s.In("list_id", listIDs).Delete(&Task{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Delete the lists
|
||||
_, err = x.In("id", listIDs).Delete(&List{})
|
||||
_, err = s.In("id", listIDs).Delete(&List{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -560,14 +556,14 @@ func (n *Namespace) Delete() (err error) {
|
|||
// @Failure 403 {object} web.HTTPError "The user does not have access to the namespace"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /namespace/{id} [post]
|
||||
func (n *Namespace) Update() (err error) {
|
||||
func (n *Namespace) Update(s *xorm.Session) (err error) {
|
||||
// Check if we have at least a name
|
||||
if n.Title == "" {
|
||||
return ErrNamespaceNameCannotBeEmpty{NamespaceID: n.ID}
|
||||
}
|
||||
|
||||
// Check if the namespace exists
|
||||
currentNamespace, err := GetNamespaceByID(n.ID)
|
||||
currentNamespace, err := GetNamespaceByID(s, n.ID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -581,7 +577,7 @@ func (n *Namespace) Update() (err error) {
|
|||
if n.Owner != nil {
|
||||
n.OwnerID = n.Owner.ID
|
||||
if currentNamespace.OwnerID != n.OwnerID {
|
||||
n.Owner, err = user.GetUserByID(n.OwnerID)
|
||||
n.Owner, err = user.GetUserByID(s, n.OwnerID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -599,7 +595,7 @@ func (n *Namespace) Update() (err error) {
|
|||
}
|
||||
|
||||
// Do the actual update
|
||||
_, err = x.
|
||||
_, err = s.
|
||||
ID(currentNamespace.ID).
|
||||
Cols(colsToUpdate...).
|
||||
Update(n)
|
||||
|
|
|
@ -19,37 +19,38 @@ package models
|
|||
import (
|
||||
"code.vikunja.io/web"
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// CanWrite checks if a user has write access to a namespace
|
||||
func (n *Namespace) CanWrite(a web.Auth) (bool, error) {
|
||||
can, _, err := n.checkRight(a, RightWrite, RightAdmin)
|
||||
func (n *Namespace) CanWrite(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
can, _, err := n.checkRight(s, a, RightWrite, RightAdmin)
|
||||
return can, err
|
||||
}
|
||||
|
||||
// IsAdmin returns true or false if the user is admin on that namespace or not
|
||||
func (n *Namespace) IsAdmin(a web.Auth) (bool, error) {
|
||||
is, _, err := n.checkRight(a, RightAdmin)
|
||||
func (n *Namespace) IsAdmin(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
is, _, err := n.checkRight(s, a, RightAdmin)
|
||||
return is, err
|
||||
}
|
||||
|
||||
// CanRead checks if a user has read access to that namespace
|
||||
func (n *Namespace) CanRead(a web.Auth) (bool, int, error) {
|
||||
return n.checkRight(a, RightRead, RightWrite, RightAdmin)
|
||||
func (n *Namespace) CanRead(s *xorm.Session, a web.Auth) (bool, int, error) {
|
||||
return n.checkRight(s, a, RightRead, RightWrite, RightAdmin)
|
||||
}
|
||||
|
||||
// CanUpdate checks if the user can update the namespace
|
||||
func (n *Namespace) CanUpdate(a web.Auth) (bool, error) {
|
||||
return n.IsAdmin(a)
|
||||
func (n *Namespace) CanUpdate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
return n.IsAdmin(s, a)
|
||||
}
|
||||
|
||||
// CanDelete checks if the user can delete a namespace
|
||||
func (n *Namespace) CanDelete(a web.Auth) (bool, error) {
|
||||
return n.IsAdmin(a)
|
||||
func (n *Namespace) CanDelete(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
return n.IsAdmin(s, a)
|
||||
}
|
||||
|
||||
// CanCreate checks if the user can create a new namespace
|
||||
func (n *Namespace) CanCreate(a web.Auth) (bool, error) {
|
||||
func (n *Namespace) CanCreate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
if _, is := a.(*LinkSharing); is {
|
||||
return false, nil
|
||||
}
|
||||
|
@ -58,7 +59,7 @@ func (n *Namespace) CanCreate(a web.Auth) (bool, error) {
|
|||
return true, nil
|
||||
}
|
||||
|
||||
func (n *Namespace) checkRight(a web.Auth, rights ...Right) (bool, int, error) {
|
||||
func (n *Namespace) checkRight(s *xorm.Session, a web.Auth, rights ...Right) (bool, int, error) {
|
||||
|
||||
// If the auth is a link share, don't do anything
|
||||
if _, is := a.(*LinkSharing); is {
|
||||
|
@ -66,13 +67,12 @@ func (n *Namespace) checkRight(a web.Auth, rights ...Right) (bool, int, error) {
|
|||
}
|
||||
|
||||
// Get the namespace and check the right
|
||||
nn := &Namespace{ID: n.ID}
|
||||
err := nn.GetSimpleByID()
|
||||
nn, err := getNamespaceSimpleByID(s, n.ID)
|
||||
if err != nil {
|
||||
return false, 0, err
|
||||
}
|
||||
|
||||
if a.GetID() == n.OwnerID {
|
||||
if a.GetID() == nn.OwnerID {
|
||||
return true, int(RightAdmin), nil
|
||||
}
|
||||
|
||||
|
@ -113,7 +113,8 @@ func (n *Namespace) checkRight(a web.Auth, rights ...Right) (bool, int, error) {
|
|||
|
||||
var maxRights = 0
|
||||
r := &allRights{}
|
||||
exists, err := x.Select("*").
|
||||
exists, err := s.
|
||||
Select("*").
|
||||
Table("namespaces").
|
||||
// User stuff
|
||||
Join("LEFT", "users_namespace", "users_namespace.namespace_id = namespaces.id").
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"time"
|
||||
|
||||
"code.vikunja.io/web"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// TeamNamespace defines the relationship between a Team and a Namespace
|
||||
|
@ -62,7 +63,7 @@ func (TeamNamespace) TableName() string {
|
|||
// @Failure 403 {object} web.HTTPError "The team does not have access to the namespace"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /namespaces/{id}/teams [put]
|
||||
func (tn *TeamNamespace) Create(a web.Auth) (err error) {
|
||||
func (tn *TeamNamespace) Create(s *xorm.Session, a web.Auth) (err error) {
|
||||
|
||||
// Check if the rights are valid
|
||||
if err = tn.Right.isValid(); err != nil {
|
||||
|
@ -70,19 +71,20 @@ func (tn *TeamNamespace) Create(a web.Auth) (err error) {
|
|||
}
|
||||
|
||||
// Check if the team exists
|
||||
_, err = GetTeamByID(tn.TeamID)
|
||||
_, err = GetTeamByID(s, tn.TeamID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the namespace exists
|
||||
_, err = GetNamespaceByID(tn.NamespaceID)
|
||||
_, err = GetNamespaceByID(s, tn.NamespaceID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the team already has access to the namespace
|
||||
exists, err := x.Where("team_id = ?", tn.TeamID).
|
||||
exists, err := s.
|
||||
Where("team_id = ?", tn.TeamID).
|
||||
And("namespace_id = ?", tn.NamespaceID).
|
||||
Get(&TeamNamespace{})
|
||||
if err != nil {
|
||||
|
@ -93,7 +95,7 @@ func (tn *TeamNamespace) Create(a web.Auth) (err error) {
|
|||
}
|
||||
|
||||
// Insert the new team
|
||||
_, err = x.Insert(tn)
|
||||
_, err = s.Insert(tn)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -110,16 +112,17 @@ func (tn *TeamNamespace) Create(a web.Auth) (err error) {
|
|||
// @Failure 404 {object} web.HTTPError "team or namespace does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /namespaces/{namespaceID}/teams/{teamID} [delete]
|
||||
func (tn *TeamNamespace) Delete() (err error) {
|
||||
func (tn *TeamNamespace) Delete(s *xorm.Session) (err error) {
|
||||
|
||||
// Check if the team exists
|
||||
_, err = GetTeamByID(tn.TeamID)
|
||||
_, err = GetTeamByID(s, tn.TeamID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the team has access to the namespace
|
||||
has, err := x.Where("team_id = ? AND namespace_id = ?", tn.TeamID, tn.NamespaceID).
|
||||
has, err := s.
|
||||
Where("team_id = ? AND namespace_id = ?", tn.TeamID, tn.NamespaceID).
|
||||
Get(&TeamNamespace{})
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -129,7 +132,8 @@ func (tn *TeamNamespace) Delete() (err error) {
|
|||
}
|
||||
|
||||
// Delete the relation
|
||||
_, err = x.Where("team_id = ?", tn.TeamID).
|
||||
_, err = s.
|
||||
Where("team_id = ?", tn.TeamID).
|
||||
And("namespace_id = ?", tn.NamespaceID).
|
||||
Delete(TeamNamespace{})
|
||||
|
||||
|
@ -151,10 +155,10 @@ func (tn *TeamNamespace) Delete() (err error) {
|
|||
// @Failure 403 {object} web.HTTPError "No right to see the namespace."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /namespaces/{id}/teams [get]
|
||||
func (tn *TeamNamespace) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
||||
func (tn *TeamNamespace) ReadAll(s *xorm.Session, a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
||||
// Check if the user can read the namespace
|
||||
n := Namespace{ID: tn.NamespaceID}
|
||||
canRead, _, err := n.CanRead(a)
|
||||
canRead, _, err := n.CanRead(s, a)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
@ -167,7 +171,8 @@ func (tn *TeamNamespace) ReadAll(a web.Auth, search string, page int, perPage in
|
|||
|
||||
limit, start := getLimitFromPageIndex(page, perPage)
|
||||
|
||||
query := x.Table("teams").
|
||||
query := s.
|
||||
Table("teams").
|
||||
Join("INNER", "team_namespaces", "team_id = teams.id").
|
||||
Where("team_namespaces.namespace_id = ?", tn.NamespaceID).
|
||||
Where("teams.name LIKE ?", "%"+search+"%")
|
||||
|
@ -184,12 +189,13 @@ func (tn *TeamNamespace) ReadAll(a web.Auth, search string, page int, perPage in
|
|||
teams = append(teams, &t.Team)
|
||||
}
|
||||
|
||||
err = addMoreInfoToTeams(teams)
|
||||
err = addMoreInfoToTeams(s, teams)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
numberOfTotalItems, err = x.Table("teams").
|
||||
numberOfTotalItems, err = s.
|
||||
Table("teams").
|
||||
Join("INNER", "team_namespaces", "team_id = teams.id").
|
||||
Where("team_namespaces.namespace_id = ?", tn.NamespaceID).
|
||||
Where("teams.name LIKE ?", "%"+search+"%").
|
||||
|
@ -213,14 +219,14 @@ func (tn *TeamNamespace) ReadAll(a web.Auth, search string, page int, perPage in
|
|||
// @Failure 404 {object} web.HTTPError "Team or namespace does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /namespaces/{namespaceID}/teams/{teamID} [post]
|
||||
func (tn *TeamNamespace) Update() (err error) {
|
||||
func (tn *TeamNamespace) Update(s *xorm.Session) (err error) {
|
||||
|
||||
// Check if the right is valid
|
||||
if err := tn.Right.isValid(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = x.
|
||||
_, err = s.
|
||||
Where("namespace_id = ? AND team_id = ?", tn.NamespaceID, tn.TeamID).
|
||||
Cols("right").
|
||||
Update(tn)
|
||||
|
|
|
@ -18,22 +18,23 @@ package models
|
|||
|
||||
import (
|
||||
"code.vikunja.io/web"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// CanCreate checks if one can create a new team <-> namespace relation
|
||||
func (tn *TeamNamespace) CanCreate(a web.Auth) (bool, error) {
|
||||
func (tn *TeamNamespace) CanCreate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
n := &Namespace{ID: tn.NamespaceID}
|
||||
return n.IsAdmin(a)
|
||||
return n.IsAdmin(s, a)
|
||||
}
|
||||
|
||||
// CanDelete checks if a user can remove a team from a namespace. Only namespace admins can do that.
|
||||
func (tn *TeamNamespace) CanDelete(a web.Auth) (bool, error) {
|
||||
func (tn *TeamNamespace) CanDelete(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
n := &Namespace{ID: tn.NamespaceID}
|
||||
return n.IsAdmin(a)
|
||||
return n.IsAdmin(s, a)
|
||||
}
|
||||
|
||||
// CanUpdate checks if a user can update a team from a Only namespace admins can do that.
|
||||
func (tn *TeamNamespace) CanUpdate(a web.Auth) (bool, error) {
|
||||
func (tn *TeamNamespace) CanUpdate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
n := &Namespace{ID: tn.NamespaceID}
|
||||
return n.IsAdmin(a)
|
||||
return n.IsAdmin(s, a)
|
||||
}
|
||||
|
|
|
@ -80,6 +80,7 @@ func TestTeamNamespace_CanDoSomething(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
|
||||
tn := &TeamNamespace{
|
||||
ID: tt.fields.ID,
|
||||
|
@ -91,15 +92,16 @@ func TestTeamNamespace_CanDoSomething(t *testing.T) {
|
|||
CRUDable: tt.fields.CRUDable,
|
||||
Rights: tt.fields.Rights,
|
||||
}
|
||||
if got, _ := tn.CanCreate(tt.args.a); got != tt.want["CanCreate"] {
|
||||
if got, _ := tn.CanCreate(s, tt.args.a); got != tt.want["CanCreate"] {
|
||||
t.Errorf("TeamNamespace.CanCreate() = %v, want %v", got, tt.want["CanCreate"])
|
||||
}
|
||||
if got, _ := tn.CanDelete(tt.args.a); got != tt.want["CanDelete"] {
|
||||
if got, _ := tn.CanDelete(s, tt.args.a); got != tt.want["CanDelete"] {
|
||||
t.Errorf("TeamNamespace.CanDelete() = %v, want %v", got, tt.want["CanDelete"])
|
||||
}
|
||||
if got, _ := tn.CanUpdate(tt.args.a); got != tt.want["CanUpdate"] {
|
||||
if got, _ := tn.CanUpdate(s, tt.args.a); got != tt.want["CanUpdate"] {
|
||||
t.Errorf("TeamNamespace.CanUpdate() = %v, want %v", got, tt.want["CanUpdate"])
|
||||
}
|
||||
_ = s.Close()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,29 +36,35 @@ func TestTeamNamespace_ReadAll(t *testing.T) {
|
|||
NamespaceID: 3,
|
||||
}
|
||||
db.LoadAndAssertFixtures(t)
|
||||
teams, _, _, err := tn.ReadAll(u, "", 1, 50)
|
||||
s := db.NewSession()
|
||||
teams, _, _, err := tn.ReadAll(s, u, "", 1, 50)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, reflect.TypeOf(teams).Kind(), reflect.Slice)
|
||||
s := reflect.ValueOf(teams)
|
||||
assert.Equal(t, s.Len(), 2)
|
||||
ts := reflect.ValueOf(teams)
|
||||
assert.Equal(t, ts.Len(), 2)
|
||||
_ = s.Close()
|
||||
})
|
||||
t.Run("nonexistant namespace", func(t *testing.T) {
|
||||
tn := TeamNamespace{
|
||||
NamespaceID: 9999,
|
||||
}
|
||||
db.LoadAndAssertFixtures(t)
|
||||
_, _, _, err := tn.ReadAll(u, "", 1, 50)
|
||||
s := db.NewSession()
|
||||
_, _, _, err := tn.ReadAll(s, u, "", 1, 50)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrNamespaceDoesNotExist(err))
|
||||
_ = s.Close()
|
||||
})
|
||||
t.Run("no right for namespace", func(t *testing.T) {
|
||||
tn := TeamNamespace{
|
||||
NamespaceID: 17,
|
||||
}
|
||||
db.LoadAndAssertFixtures(t)
|
||||
_, _, _, err := tn.ReadAll(u, "", 1, 50)
|
||||
s := db.NewSession()
|
||||
_, _, _, err := tn.ReadAll(s, u, "", 1, 50)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrNeedToHaveNamespaceReadAccess(err))
|
||||
_ = s.Close()
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -72,10 +78,15 @@ func TestTeamNamespace_Create(t *testing.T) {
|
|||
Right: RightAdmin,
|
||||
}
|
||||
db.LoadAndAssertFixtures(t)
|
||||
allowed, _ := tn.CanCreate(u)
|
||||
s := db.NewSession()
|
||||
allowed, _ := tn.CanCreate(s, u)
|
||||
assert.True(t, allowed)
|
||||
err := tn.Create(u)
|
||||
err := tn.Create(s, u)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
|
||||
db.AssertExists(t, "team_namespaces", map[string]interface{}{
|
||||
"team_id": 1,
|
||||
"namespace_id": 1,
|
||||
|
@ -89,9 +100,11 @@ func TestTeamNamespace_Create(t *testing.T) {
|
|||
Right: RightRead,
|
||||
}
|
||||
db.LoadAndAssertFixtures(t)
|
||||
err := tn.Create(u)
|
||||
s := db.NewSession()
|
||||
err := tn.Create(s, u)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTeamAlreadyHasAccess(err))
|
||||
_ = s.Close()
|
||||
})
|
||||
t.Run("invalid team right", func(t *testing.T) {
|
||||
tn := TeamNamespace{
|
||||
|
@ -100,9 +113,11 @@ func TestTeamNamespace_Create(t *testing.T) {
|
|||
Right: RightUnknown,
|
||||
}
|
||||
db.LoadAndAssertFixtures(t)
|
||||
err := tn.Create(u)
|
||||
s := db.NewSession()
|
||||
err := tn.Create(s, u)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrInvalidRight(err))
|
||||
_ = s.Close()
|
||||
})
|
||||
t.Run("nonexistant team", func(t *testing.T) {
|
||||
tn := TeamNamespace{
|
||||
|
@ -110,9 +125,11 @@ func TestTeamNamespace_Create(t *testing.T) {
|
|||
NamespaceID: 1,
|
||||
}
|
||||
db.LoadAndAssertFixtures(t)
|
||||
err := tn.Create(u)
|
||||
s := db.NewSession()
|
||||
err := tn.Create(s, u)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTeamDoesNotExist(err))
|
||||
_ = s.Close()
|
||||
})
|
||||
t.Run("nonexistant namespace", func(t *testing.T) {
|
||||
tn := TeamNamespace{
|
||||
|
@ -120,9 +137,11 @@ func TestTeamNamespace_Create(t *testing.T) {
|
|||
NamespaceID: 9999,
|
||||
}
|
||||
db.LoadAndAssertFixtures(t)
|
||||
err := tn.Create(u)
|
||||
s := db.NewSession()
|
||||
err := tn.Create(s, u)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrNamespaceDoesNotExist(err))
|
||||
_ = s.Close()
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -135,10 +154,14 @@ func TestTeamNamespace_Delete(t *testing.T) {
|
|||
NamespaceID: 9,
|
||||
}
|
||||
db.LoadAndAssertFixtures(t)
|
||||
allowed, _ := tn.CanDelete(u)
|
||||
s := db.NewSession()
|
||||
allowed, _ := tn.CanDelete(s, u)
|
||||
assert.True(t, allowed)
|
||||
err := tn.Delete()
|
||||
err := tn.Delete(s)
|
||||
assert.NoError(t, err)
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
|
||||
db.AssertMissing(t, "team_namespaces", map[string]interface{}{
|
||||
"team_id": 7,
|
||||
"namespace_id": 9,
|
||||
|
@ -150,9 +173,11 @@ func TestTeamNamespace_Delete(t *testing.T) {
|
|||
NamespaceID: 3,
|
||||
}
|
||||
db.LoadAndAssertFixtures(t)
|
||||
err := tn.Delete()
|
||||
s := db.NewSession()
|
||||
err := tn.Delete(s)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTeamDoesNotExist(err))
|
||||
_ = s.Close()
|
||||
})
|
||||
t.Run("nonexistant namespace", func(t *testing.T) {
|
||||
tn := TeamNamespace{
|
||||
|
@ -160,9 +185,11 @@ func TestTeamNamespace_Delete(t *testing.T) {
|
|||
NamespaceID: 9999,
|
||||
}
|
||||
db.LoadAndAssertFixtures(t)
|
||||
err := tn.Delete()
|
||||
s := db.NewSession()
|
||||
err := tn.Delete(s)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTeamDoesNotHaveAccessToNamespace(err))
|
||||
_ = s.Close()
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -221,6 +248,7 @@ func TestTeamNamespace_Update(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
|
||||
tl := &TeamNamespace{
|
||||
ID: tt.fields.ID,
|
||||
|
@ -232,13 +260,17 @@ func TestTeamNamespace_Update(t *testing.T) {
|
|||
CRUDable: tt.fields.CRUDable,
|
||||
Rights: tt.fields.Rights,
|
||||
}
|
||||
err := tl.Update()
|
||||
err := tl.Update(s)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("TeamNamespace.Update() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
if (err != nil) && tt.wantErr && !tt.errType(err) {
|
||||
t.Errorf("TeamNamespace.Update() Wrong error type! Error = %v, want = %v", err, runtime.FuncForPC(reflect.ValueOf(tt.errType).Pointer()).Name())
|
||||
}
|
||||
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
|
||||
if !tt.wantErr {
|
||||
db.AssertExists(t, "team_namespaces", map[string]interface{}{
|
||||
"team_id": tt.fields.TeamID,
|
||||
|
|
|
@ -36,8 +36,12 @@ func TestNamespace_Create(t *testing.T) {
|
|||
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
err := dummynamespace.Create(user1)
|
||||
s := db.NewSession()
|
||||
err := dummynamespace.Create(s, user1)
|
||||
assert.NoError(t, err)
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
|
||||
db.AssertExists(t, "namespaces", map[string]interface{}{
|
||||
"title": "Test",
|
||||
"description": "Lorem Ipsum",
|
||||
|
@ -45,18 +49,22 @@ func TestNamespace_Create(t *testing.T) {
|
|||
})
|
||||
t.Run("no title", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
n2 := Namespace{}
|
||||
err := n2.Create(user1)
|
||||
err := n2.Create(s, user1)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrNamespaceNameCannotBeEmpty(err))
|
||||
_ = s.Close()
|
||||
})
|
||||
t.Run("nonexistant user", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
nUser := &user.User{ID: 9482385}
|
||||
dnsp2 := dummynamespace
|
||||
err := dnsp2.Create(nUser)
|
||||
err := dnsp2.Create(s, nUser)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, user.IsErrUserDoesNotExist(err))
|
||||
_ = s.Close()
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -64,28 +72,36 @@ func TestNamespace_ReadOne(t *testing.T) {
|
|||
t.Run("normal", func(t *testing.T) {
|
||||
n := &Namespace{ID: 1}
|
||||
db.LoadAndAssertFixtures(t)
|
||||
err := n.ReadOne()
|
||||
s := db.NewSession()
|
||||
err := n.ReadOne(s)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, n.Title, "testnamespace")
|
||||
_ = s.Close()
|
||||
})
|
||||
t.Run("nonexistant", func(t *testing.T) {
|
||||
n := &Namespace{ID: 99999}
|
||||
db.LoadAndAssertFixtures(t)
|
||||
err := n.ReadOne()
|
||||
s := db.NewSession()
|
||||
err := n.ReadOne(s)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrNamespaceDoesNotExist(err))
|
||||
_ = s.Close()
|
||||
})
|
||||
}
|
||||
|
||||
func TestNamespace_Update(t *testing.T) {
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
n := &Namespace{
|
||||
ID: 1,
|
||||
Title: "Lorem Ipsum",
|
||||
}
|
||||
err := n.Update()
|
||||
err := n.Update(s)
|
||||
assert.NoError(t, err)
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
|
||||
db.AssertExists(t, "namespaces", map[string]interface{}{
|
||||
"id": 1,
|
||||
"title": "Lorem Ipsum",
|
||||
|
@ -93,56 +109,68 @@ func TestNamespace_Update(t *testing.T) {
|
|||
})
|
||||
t.Run("nonexisting", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
n := &Namespace{
|
||||
ID: 99999,
|
||||
Title: "Lorem Ipsum",
|
||||
}
|
||||
err := n.Update()
|
||||
err := n.Update(s)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrNamespaceDoesNotExist(err))
|
||||
_ = s.Close()
|
||||
})
|
||||
t.Run("nonexisting owner", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
n := &Namespace{
|
||||
ID: 1,
|
||||
Title: "Lorem Ipsum",
|
||||
Owner: &user.User{ID: 99999},
|
||||
}
|
||||
err := n.Update()
|
||||
err := n.Update(s)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, user.IsErrUserDoesNotExist(err))
|
||||
_ = s.Close()
|
||||
})
|
||||
t.Run("no title", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
n := &Namespace{
|
||||
ID: 1,
|
||||
}
|
||||
err := n.Update()
|
||||
err := n.Update(s)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrNamespaceNameCannotBeEmpty(err))
|
||||
_ = s.Close()
|
||||
})
|
||||
}
|
||||
|
||||
func TestNamespace_Delete(t *testing.T) {
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
n := &Namespace{
|
||||
ID: 1,
|
||||
}
|
||||
err := n.Delete()
|
||||
err := n.Delete(s)
|
||||
assert.NoError(t, err)
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
|
||||
db.AssertMissing(t, "namespaces", map[string]interface{}{
|
||||
"id": 1,
|
||||
})
|
||||
})
|
||||
t.Run("nonexisting", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
n := &Namespace{
|
||||
ID: 9999,
|
||||
}
|
||||
err := n.Delete()
|
||||
err := n.Delete(s)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrNamespaceDoesNotExist(err))
|
||||
_ = s.Close()
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -152,9 +180,12 @@ func TestNamespace_ReadAll(t *testing.T) {
|
|||
user11 := &user.User{ID: 11}
|
||||
user12 := &user.User{ID: 12}
|
||||
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
n := &Namespace{}
|
||||
nn, _, _, err := n.ReadAll(user1, "", 1, -1)
|
||||
nn, _, _, err := n.ReadAll(s, user1, "", 1, -1)
|
||||
assert.NoError(t, err)
|
||||
namespaces := nn.([]*NamespaceWithLists)
|
||||
assert.NotNil(t, namespaces)
|
||||
|
@ -174,7 +205,7 @@ func TestNamespace_ReadAll(t *testing.T) {
|
|||
n := &Namespace{
|
||||
NamespacesOnly: true,
|
||||
}
|
||||
nn, _, _, err := n.ReadAll(user1, "", 1, -1)
|
||||
nn, _, _, err := n.ReadAll(s, user1, "", 1, -1)
|
||||
assert.NoError(t, err)
|
||||
namespaces := nn.([]*NamespaceWithLists)
|
||||
assert.NotNil(t, namespaces)
|
||||
|
@ -188,7 +219,7 @@ func TestNamespace_ReadAll(t *testing.T) {
|
|||
n := &Namespace{
|
||||
NamespacesOnly: true,
|
||||
}
|
||||
nn, _, _, err := n.ReadAll(user7, "13,14", 1, -1)
|
||||
nn, _, _, err := n.ReadAll(s, user7, "13,14", 1, -1)
|
||||
assert.NoError(t, err)
|
||||
namespaces := nn.([]*NamespaceWithLists)
|
||||
assert.NotNil(t, namespaces)
|
||||
|
@ -200,7 +231,7 @@ func TestNamespace_ReadAll(t *testing.T) {
|
|||
n := &Namespace{
|
||||
NamespacesOnly: true,
|
||||
}
|
||||
nn, _, _, err := n.ReadAll(user1, "1,w", 1, -1)
|
||||
nn, _, _, err := n.ReadAll(s, user1, "1,w", 1, -1)
|
||||
assert.NoError(t, err)
|
||||
namespaces := nn.([]*NamespaceWithLists)
|
||||
assert.NotNil(t, namespaces)
|
||||
|
@ -211,7 +242,7 @@ func TestNamespace_ReadAll(t *testing.T) {
|
|||
n := &Namespace{
|
||||
IsArchived: true,
|
||||
}
|
||||
nn, _, _, err := n.ReadAll(user1, "", 1, -1)
|
||||
nn, _, _, err := n.ReadAll(s, user1, "", 1, -1)
|
||||
namespaces := nn.([]*NamespaceWithLists)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, namespaces)
|
||||
|
@ -222,7 +253,7 @@ func TestNamespace_ReadAll(t *testing.T) {
|
|||
})
|
||||
t.Run("no favorites", func(t *testing.T) {
|
||||
n := &Namespace{}
|
||||
nn, _, _, err := n.ReadAll(user11, "", 1, -1)
|
||||
nn, _, _, err := n.ReadAll(s, user11, "", 1, -1)
|
||||
namespaces := nn.([]*NamespaceWithLists)
|
||||
assert.NoError(t, err)
|
||||
// Assert the first namespace is not the favorites namespace
|
||||
|
@ -230,7 +261,7 @@ func TestNamespace_ReadAll(t *testing.T) {
|
|||
})
|
||||
t.Run("no favorite tasks but namespace", func(t *testing.T) {
|
||||
n := &Namespace{}
|
||||
nn, _, _, err := n.ReadAll(user12, "", 1, -1)
|
||||
nn, _, _, err := n.ReadAll(s, user12, "", 1, -1)
|
||||
namespaces := nn.([]*NamespaceWithLists)
|
||||
assert.NoError(t, err)
|
||||
// Assert the first namespace is the favorites namespace and contains lists
|
||||
|
@ -239,7 +270,7 @@ func TestNamespace_ReadAll(t *testing.T) {
|
|||
})
|
||||
t.Run("no saved filters", func(t *testing.T) {
|
||||
n := &Namespace{}
|
||||
nn, _, _, err := n.ReadAll(user11, "", 1, -1)
|
||||
nn, _, _, err := n.ReadAll(s, user11, "", 1, -1)
|
||||
namespaces := nn.([]*NamespaceWithLists)
|
||||
assert.NoError(t, err)
|
||||
// Assert the first namespace is not the favorites namespace
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
|
||||
user2 "code.vikunja.io/api/pkg/user"
|
||||
"code.vikunja.io/web"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// NamespaceUser represents a namespace <-> user relation
|
||||
|
@ -64,7 +65,7 @@ func (NamespaceUser) TableName() string {
|
|||
// @Failure 403 {object} web.HTTPError "The user does not have access to the namespace"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /namespaces/{id}/users [put]
|
||||
func (nu *NamespaceUser) Create(a web.Auth) (err error) {
|
||||
func (nu *NamespaceUser) Create(s *xorm.Session, a web.Auth) (err error) {
|
||||
// Reset the id
|
||||
nu.ID = 0
|
||||
|
||||
|
@ -74,13 +75,13 @@ func (nu *NamespaceUser) Create(a web.Auth) (err error) {
|
|||
}
|
||||
|
||||
// Check if the namespace exists
|
||||
l, err := GetNamespaceByID(nu.NamespaceID)
|
||||
l, err := GetNamespaceByID(s, nu.NamespaceID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the user exists
|
||||
user, err := user2.GetUserByUsername(nu.Username)
|
||||
user, err := user2.GetUserByUsername(s, nu.Username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -92,7 +93,9 @@ func (nu *NamespaceUser) Create(a web.Auth) (err error) {
|
|||
return ErrUserAlreadyHasNamespaceAccess{UserID: nu.UserID, NamespaceID: nu.NamespaceID}
|
||||
}
|
||||
|
||||
exist, err := x.Where("namespace_id = ? AND user_id = ?", nu.NamespaceID, nu.UserID).Get(&NamespaceUser{})
|
||||
exist, err := s.
|
||||
Where("namespace_id = ? AND user_id = ?", nu.NamespaceID, nu.UserID).
|
||||
Get(&NamespaceUser{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -101,7 +104,7 @@ func (nu *NamespaceUser) Create(a web.Auth) (err error) {
|
|||
}
|
||||
|
||||
// Insert user <-> namespace relation
|
||||
_, err = x.Insert(nu)
|
||||
_, err = s.Insert(nu)
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -119,17 +122,18 @@ func (nu *NamespaceUser) Create(a web.Auth) (err error) {
|
|||
// @Failure 404 {object} web.HTTPError "user or namespace does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /namespaces/{namespaceID}/users/{userID} [delete]
|
||||
func (nu *NamespaceUser) Delete() (err error) {
|
||||
func (nu *NamespaceUser) Delete(s *xorm.Session) (err error) {
|
||||
|
||||
// Check if the user exists
|
||||
user, err := user2.GetUserByUsername(nu.Username)
|
||||
user, err := user2.GetUserByUsername(s, nu.Username)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
nu.UserID = user.ID
|
||||
|
||||
// Check if the user has access to the namespace
|
||||
has, err := x.Where("user_id = ? AND namespace_id = ?", nu.UserID, nu.NamespaceID).
|
||||
has, err := s.
|
||||
Where("user_id = ? AND namespace_id = ?", nu.UserID, nu.NamespaceID).
|
||||
Get(&NamespaceUser{})
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -138,7 +142,8 @@ func (nu *NamespaceUser) Delete() (err error) {
|
|||
return ErrUserDoesNotHaveAccessToNamespace{NamespaceID: nu.NamespaceID, UserID: nu.UserID}
|
||||
}
|
||||
|
||||
_, err = x.Where("user_id = ? AND namespace_id = ?", nu.UserID, nu.NamespaceID).
|
||||
_, err = s.
|
||||
Where("user_id = ? AND namespace_id = ?", nu.UserID, nu.NamespaceID).
|
||||
Delete(&NamespaceUser{})
|
||||
return
|
||||
}
|
||||
|
@ -158,10 +163,10 @@ func (nu *NamespaceUser) Delete() (err error) {
|
|||
// @Failure 403 {object} web.HTTPError "No right to see the namespace."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /namespaces/{id}/users [get]
|
||||
func (nu *NamespaceUser) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
||||
func (nu *NamespaceUser) ReadAll(s *xorm.Session, a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
||||
// Check if the user has access to the namespace
|
||||
l := Namespace{ID: nu.NamespaceID}
|
||||
canRead, _, err := l.CanRead(a)
|
||||
canRead, _, err := l.CanRead(s, a)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
@ -174,7 +179,7 @@ func (nu *NamespaceUser) ReadAll(a web.Auth, search string, page int, perPage in
|
|||
|
||||
limit, start := getLimitFromPageIndex(page, perPage)
|
||||
|
||||
query := x.
|
||||
query := s.
|
||||
Join("INNER", "users_namespace", "user_id = users.id").
|
||||
Where("users_namespace.namespace_id = ?", nu.NamespaceID).
|
||||
Where("users.username LIKE ?", "%"+search+"%")
|
||||
|
@ -191,7 +196,7 @@ func (nu *NamespaceUser) ReadAll(a web.Auth, search string, page int, perPage in
|
|||
u.Email = ""
|
||||
}
|
||||
|
||||
numberOfTotalItems, err = x.
|
||||
numberOfTotalItems, err = s.
|
||||
Join("INNER", "users_namespace", "user_id = users.id").
|
||||
Where("users_namespace.namespace_id = ?", nu.NamespaceID).
|
||||
Where("users.username LIKE ?", "%"+search+"%").
|
||||
|
@ -215,7 +220,7 @@ func (nu *NamespaceUser) ReadAll(a web.Auth, search string, page int, perPage in
|
|||
// @Failure 404 {object} web.HTTPError "User or namespace does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /namespaces/{namespaceID}/users/{userID} [post]
|
||||
func (nu *NamespaceUser) Update() (err error) {
|
||||
func (nu *NamespaceUser) Update(s *xorm.Session) (err error) {
|
||||
|
||||
// Check if the right is valid
|
||||
if err := nu.Right.isValid(); err != nil {
|
||||
|
@ -223,13 +228,13 @@ func (nu *NamespaceUser) Update() (err error) {
|
|||
}
|
||||
|
||||
// Check if the user exists
|
||||
user, err := user2.GetUserByUsername(nu.Username)
|
||||
user, err := user2.GetUserByUsername(s, nu.Username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nu.UserID = user.ID
|
||||
|
||||
_, err = x.
|
||||
_, err = s.
|
||||
Where("namespace_id = ? AND user_id = ?", nu.NamespaceID, nu.UserID).
|
||||
Cols("right").
|
||||
Update(nu)
|
||||
|
|
|
@ -18,24 +18,25 @@ package models
|
|||
|
||||
import (
|
||||
"code.vikunja.io/web"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// CanCreate checks if the user can create a new user <-> namespace relation
|
||||
func (nu *NamespaceUser) CanCreate(a web.Auth) (bool, error) {
|
||||
return nu.canDoNamespaceUser(a)
|
||||
func (nu *NamespaceUser) CanCreate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
return nu.canDoNamespaceUser(s, a)
|
||||
}
|
||||
|
||||
// CanDelete checks if the user can delete a user <-> namespace relation
|
||||
func (nu *NamespaceUser) CanDelete(a web.Auth) (bool, error) {
|
||||
return nu.canDoNamespaceUser(a)
|
||||
func (nu *NamespaceUser) CanDelete(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
return nu.canDoNamespaceUser(s, a)
|
||||
}
|
||||
|
||||
// CanUpdate checks if the user can update a user <-> namespace relation
|
||||
func (nu *NamespaceUser) CanUpdate(a web.Auth) (bool, error) {
|
||||
return nu.canDoNamespaceUser(a)
|
||||
func (nu *NamespaceUser) CanUpdate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
return nu.canDoNamespaceUser(s, a)
|
||||
}
|
||||
|
||||
func (nu *NamespaceUser) canDoNamespaceUser(a web.Auth) (bool, error) {
|
||||
func (nu *NamespaceUser) canDoNamespaceUser(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
n := &Namespace{ID: nu.NamespaceID}
|
||||
return n.IsAdmin(a)
|
||||
return n.IsAdmin(s, a)
|
||||
}
|
||||
|
|
|
@ -80,6 +80,8 @@ func TestNamespaceUser_CanDoSomething(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
nu := &NamespaceUser{
|
||||
ID: tt.fields.ID,
|
||||
|
@ -91,13 +93,13 @@ func TestNamespaceUser_CanDoSomething(t *testing.T) {
|
|||
CRUDable: tt.fields.CRUDable,
|
||||
Rights: tt.fields.Rights,
|
||||
}
|
||||
if got, _ := nu.CanCreate(tt.args.a); got != tt.want["CanCreate"] {
|
||||
if got, _ := nu.CanCreate(s, tt.args.a); got != tt.want["CanCreate"] {
|
||||
t.Errorf("NamespaceUser.CanCreate() = %v, want %v", got, tt.want["CanCreate"])
|
||||
}
|
||||
if got, _ := nu.CanDelete(tt.args.a); got != tt.want["CanDelete"] {
|
||||
if got, _ := nu.CanDelete(s, tt.args.a); got != tt.want["CanDelete"] {
|
||||
t.Errorf("NamespaceUser.CanDelete() = %v, want %v", got, tt.want["CanDelete"])
|
||||
}
|
||||
if got, _ := nu.CanUpdate(tt.args.a); got != tt.want["CanUpdate"] {
|
||||
if got, _ := nu.CanUpdate(s, tt.args.a); got != tt.want["CanUpdate"] {
|
||||
t.Errorf("NamespaceUser.CanUpdate() = %v, want %v", got, tt.want["CanUpdate"])
|
||||
}
|
||||
})
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
"code.vikunja.io/api/pkg/db"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
"code.vikunja.io/web"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/d4l3k/messagediff.v1"
|
||||
)
|
||||
|
||||
|
@ -108,6 +109,7 @@ func TestNamespaceUser_Create(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
|
||||
un := &NamespaceUser{
|
||||
ID: tt.fields.ID,
|
||||
|
@ -119,13 +121,16 @@ func TestNamespaceUser_Create(t *testing.T) {
|
|||
CRUDable: tt.fields.CRUDable,
|
||||
Rights: tt.fields.Rights,
|
||||
}
|
||||
err := un.Create(tt.args.a)
|
||||
err := un.Create(s, tt.args.a)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("NamespaceUser.Create() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
if (err != nil) && tt.wantErr && !tt.errType(err) {
|
||||
t.Errorf("NamespaceUser.Create() Wrong error type! Error = %v, want = %v", err, runtime.FuncForPC(reflect.ValueOf(tt.errType).Pointer()).Name())
|
||||
}
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
|
||||
if !tt.wantErr {
|
||||
db.AssertExists(t, "users_namespace", map[string]interface{}{
|
||||
"user_id": tt.fields.UserID,
|
||||
|
@ -211,6 +216,8 @@ func TestNamespaceUser_ReadAll(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
un := &NamespaceUser{
|
||||
ID: tt.fields.ID,
|
||||
|
@ -222,7 +229,7 @@ func TestNamespaceUser_ReadAll(t *testing.T) {
|
|||
CRUDable: tt.fields.CRUDable,
|
||||
Rights: tt.fields.Rights,
|
||||
}
|
||||
got, _, _, err := un.ReadAll(tt.args.a, tt.args.search, tt.args.page, 50)
|
||||
got, _, _, err := un.ReadAll(s, tt.args.a, tt.args.search, tt.args.page, 50)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("NamespaceUser.ReadAll() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
|
@ -296,6 +303,7 @@ func TestNamespaceUser_Update(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
|
||||
nu := &NamespaceUser{
|
||||
ID: tt.fields.ID,
|
||||
|
@ -307,13 +315,16 @@ func TestNamespaceUser_Update(t *testing.T) {
|
|||
CRUDable: tt.fields.CRUDable,
|
||||
Rights: tt.fields.Rights,
|
||||
}
|
||||
err := nu.Update()
|
||||
err := nu.Update(s)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("NamespaceUser.Update() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
if (err != nil) && tt.wantErr && !tt.errType(err) {
|
||||
t.Errorf("NamespaceUser.Update() Wrong error type! Error = %v, want = %v", err, runtime.FuncForPC(reflect.ValueOf(tt.errType).Pointer()).Name())
|
||||
}
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
|
||||
if !tt.wantErr {
|
||||
db.AssertExists(t, "users_namespace", map[string]interface{}{
|
||||
"user_id": tt.fields.UserID,
|
||||
|
@ -373,6 +384,7 @@ func TestNamespaceUser_Delete(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
|
||||
nu := &NamespaceUser{
|
||||
ID: tt.fields.ID,
|
||||
|
@ -384,13 +396,16 @@ func TestNamespaceUser_Delete(t *testing.T) {
|
|||
CRUDable: tt.fields.CRUDable,
|
||||
Rights: tt.fields.Rights,
|
||||
}
|
||||
err := nu.Delete()
|
||||
err := nu.Delete(s)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("NamespaceUser.Delete() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
if (err != nil) && tt.wantErr && !tt.errType(err) {
|
||||
t.Errorf("NamespaceUser.Delete() Wrong error type! Error = %v, want = %v", err, runtime.FuncForPC(reflect.ValueOf(tt.errType).Pointer()).Name())
|
||||
}
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
|
||||
if !tt.wantErr {
|
||||
db.AssertMissing(t, "users_namespace", map[string]interface{}{
|
||||
"user_id": tt.fields.UserID,
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
"code.vikunja.io/web"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// SavedFilter represents a saved bunch of filters
|
||||
|
@ -48,14 +49,14 @@ type SavedFilter struct {
|
|||
}
|
||||
|
||||
// TableName returns a better table name for saved filters
|
||||
func (s *SavedFilter) TableName() string {
|
||||
func (sf *SavedFilter) TableName() string {
|
||||
return "saved_filters"
|
||||
}
|
||||
|
||||
func (s *SavedFilter) getTaskCollection() *TaskCollection {
|
||||
func (sf *SavedFilter) getTaskCollection() *TaskCollection {
|
||||
// We're resetting the listID to return tasks from all lists
|
||||
s.Filters.ListID = 0
|
||||
return s.Filters
|
||||
sf.Filters.ListID = 0
|
||||
return sf.Filters
|
||||
}
|
||||
|
||||
// Returns the saved filter ID from a list ID. Will not check if the filter actually exists.
|
||||
|
@ -79,13 +80,13 @@ func getListIDFromSavedFilterID(filterID int64) (listID int64) {
|
|||
return
|
||||
}
|
||||
|
||||
func getSavedFiltersForUser(auth web.Auth) (filters []*SavedFilter, err error) {
|
||||
func getSavedFiltersForUser(s *xorm.Session, auth web.Auth) (filters []*SavedFilter, err error) {
|
||||
// Link shares can't view or modify saved filters, therefore we can error out right away
|
||||
if _, is := auth.(*LinkSharing); is {
|
||||
return nil, ErrSavedFilterNotAvailableForLinkShare{LinkShareID: auth.GetID()}
|
||||
}
|
||||
|
||||
err = x.Where("owner_id = ?", auth.GetID()).Find(&filters)
|
||||
err = s.Where("owner_id = ?", auth.GetID()).Find(&filters)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -100,17 +101,17 @@ func getSavedFiltersForUser(auth web.Auth) (filters []*SavedFilter, err error) {
|
|||
// @Failure 403 {object} web.HTTPError "The user does not have access to that saved filter."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /filters [put]
|
||||
func (s *SavedFilter) Create(auth web.Auth) error {
|
||||
s.OwnerID = auth.GetID()
|
||||
_, err := x.Insert(s)
|
||||
func (sf *SavedFilter) Create(s *xorm.Session, auth web.Auth) error {
|
||||
sf.OwnerID = auth.GetID()
|
||||
_, err := s.Insert(sf)
|
||||
return err
|
||||
}
|
||||
|
||||
func getSavedFilterSimpleByID(id int64) (s *SavedFilter, err error) {
|
||||
s = &SavedFilter{}
|
||||
exists, err := x.
|
||||
func getSavedFilterSimpleByID(s *xorm.Session, id int64) (sf *SavedFilter, err error) {
|
||||
sf = &SavedFilter{}
|
||||
exists, err := s.
|
||||
Where("id = ?", id).
|
||||
Get(s)
|
||||
Get(sf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -132,10 +133,10 @@ func getSavedFilterSimpleByID(id int64) (s *SavedFilter, err error) {
|
|||
// @Failure 403 {object} web.HTTPError "The user does not have access to that saved filter."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /filters/{id} [get]
|
||||
func (s *SavedFilter) ReadOne() error {
|
||||
func (sf *SavedFilter) ReadOne(s *xorm.Session) error {
|
||||
// s already contains almost the full saved filter from the rights check, we only need to add the user
|
||||
u, err := user.GetUserByID(s.OwnerID)
|
||||
s.Owner = u
|
||||
u, err := user.GetUserByID(s, sf.OwnerID)
|
||||
sf.Owner = u
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -152,15 +153,15 @@ func (s *SavedFilter) ReadOne() error {
|
|||
// @Failure 404 {object} web.HTTPError "The saved filter does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /filters/{id} [post]
|
||||
func (s *SavedFilter) Update() error {
|
||||
_, err := x.
|
||||
Where("id = ?", s.ID).
|
||||
func (sf *SavedFilter) Update(s *xorm.Session) error {
|
||||
_, err := s.
|
||||
Where("id = ?", sf.ID).
|
||||
Cols(
|
||||
"title",
|
||||
"description",
|
||||
"filters",
|
||||
).
|
||||
Update(s)
|
||||
Update(sf)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -177,7 +178,9 @@ func (s *SavedFilter) Update() error {
|
|||
// @Failure 404 {object} web.HTTPError "The saved filter does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /filters/{id} [delete]
|
||||
func (s *SavedFilter) Delete() error {
|
||||
_, err := x.Where("id = ?", s.ID).Delete(s)
|
||||
func (sf *SavedFilter) Delete(s *xorm.Session) error {
|
||||
_, err := s.
|
||||
Where("id = ?", sf.ID).
|
||||
Delete(sf)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -16,28 +16,31 @@
|
|||
|
||||
package models
|
||||
|
||||
import "code.vikunja.io/web"
|
||||
import (
|
||||
"code.vikunja.io/web"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// CanRead checks if a user has the right to read a saved filter
|
||||
func (s *SavedFilter) CanRead(auth web.Auth) (bool, int, error) {
|
||||
can, err := s.canDoFilter(auth)
|
||||
func (sf *SavedFilter) CanRead(s *xorm.Session, auth web.Auth) (bool, int, error) {
|
||||
can, err := sf.canDoFilter(s, auth)
|
||||
return can, int(RightAdmin), err
|
||||
}
|
||||
|
||||
// CanDelete checks if a user has the right to delete a saved filter
|
||||
func (s *SavedFilter) CanDelete(auth web.Auth) (bool, error) {
|
||||
return s.canDoFilter(auth)
|
||||
func (sf *SavedFilter) CanDelete(s *xorm.Session, auth web.Auth) (bool, error) {
|
||||
return sf.canDoFilter(s, auth)
|
||||
}
|
||||
|
||||
// CanUpdate checks if a user has the right to update a saved filter
|
||||
func (s *SavedFilter) CanUpdate(auth web.Auth) (bool, error) {
|
||||
func (sf *SavedFilter) CanUpdate(s *xorm.Session, auth web.Auth) (bool, error) {
|
||||
// A normal check would replace the passed struct which in our case would override the values we want to update.
|
||||
sf := &SavedFilter{ID: s.ID}
|
||||
return sf.canDoFilter(auth)
|
||||
sff := &SavedFilter{ID: sf.ID}
|
||||
return sff.canDoFilter(s, auth)
|
||||
}
|
||||
|
||||
// CanCreate checks if a user has the right to update a saved filter
|
||||
func (s *SavedFilter) CanCreate(auth web.Auth) (bool, error) {
|
||||
func (sf *SavedFilter) CanCreate(s *xorm.Session, auth web.Auth) (bool, error) {
|
||||
if _, is := auth.(*LinkSharing); is {
|
||||
return false, nil
|
||||
}
|
||||
|
@ -46,23 +49,23 @@ func (s *SavedFilter) CanCreate(auth web.Auth) (bool, error) {
|
|||
}
|
||||
|
||||
// Helper function to check saved filter rights sind they all have the same logic
|
||||
func (s *SavedFilter) canDoFilter(auth web.Auth) (can bool, err error) {
|
||||
func (sf *SavedFilter) canDoFilter(s *xorm.Session, auth web.Auth) (can bool, err error) {
|
||||
// Link shares can't view or modify saved filters, therefore we can error out right away
|
||||
if _, is := auth.(*LinkSharing); is {
|
||||
return false, ErrSavedFilterNotAvailableForLinkShare{LinkShareID: auth.GetID(), SavedFilterID: s.ID}
|
||||
return false, ErrSavedFilterNotAvailableForLinkShare{LinkShareID: auth.GetID(), SavedFilterID: sf.ID}
|
||||
}
|
||||
|
||||
sf, err := getSavedFilterSimpleByID(s.ID)
|
||||
sff, err := getSavedFilterSimpleByID(s, sf.ID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Only owners are allowed to do something with a saved filter
|
||||
if sf.OwnerID != auth.GetID() {
|
||||
if sff.OwnerID != auth.GetID() {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
*s = *sf
|
||||
*sf = *sff
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
|
|
@ -45,6 +45,9 @@ func TestSavedFilter_getFilterIDFromListID(t *testing.T) {
|
|||
|
||||
func TestSavedFilter_Create(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
sf := &SavedFilter{
|
||||
Title: "test",
|
||||
Description: "Lorem Ipsum dolor sit amet",
|
||||
|
@ -52,9 +55,11 @@ func TestSavedFilter_Create(t *testing.T) {
|
|||
}
|
||||
|
||||
u := &user.User{ID: 1}
|
||||
err := sf.Create(u)
|
||||
err := sf.Create(s, u)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, u.ID, sf.OwnerID)
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
vals := map[string]interface{}{
|
||||
"title": "'test'",
|
||||
"description": "'Lorem Ipsum dolor sit amet'",
|
||||
|
@ -62,7 +67,7 @@ func TestSavedFilter_Create(t *testing.T) {
|
|||
"owner_id": 1,
|
||||
}
|
||||
// Postgres can't compare json values directly, see https://dba.stackexchange.com/a/106290/210721
|
||||
if x.Dialect().URI().DBType == schemas.POSTGRES {
|
||||
if db.Type() == schemas.POSTGRES {
|
||||
vals["filters::jsonb"] = vals["filters"].(string) + "::jsonb"
|
||||
delete(vals, "filters")
|
||||
}
|
||||
|
@ -72,26 +77,34 @@ func TestSavedFilter_Create(t *testing.T) {
|
|||
func TestSavedFilter_ReadOne(t *testing.T) {
|
||||
user1 := &user.User{ID: 1}
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
sf := &SavedFilter{
|
||||
ID: 1,
|
||||
}
|
||||
// canRead pre-populates the struct
|
||||
_, _, err := sf.CanRead(user1)
|
||||
_, _, err := sf.CanRead(s, user1)
|
||||
assert.NoError(t, err)
|
||||
err = sf.ReadOne()
|
||||
err = sf.ReadOne(s)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, sf.Owner)
|
||||
}
|
||||
|
||||
func TestSavedFilter_Update(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{},
|
||||
}
|
||||
err := sf.Update()
|
||||
err := sf.Update(s)
|
||||
assert.NoError(t, err)
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
db.AssertExists(t, "saved_filters", map[string]interface{}{
|
||||
"id": 1,
|
||||
|
@ -102,10 +115,15 @@ func TestSavedFilter_Update(t *testing.T) {
|
|||
|
||||
func TestSavedFilter_Delete(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
sf := &SavedFilter{
|
||||
ID: 1,
|
||||
}
|
||||
err := sf.Delete()
|
||||
err := sf.Delete(s)
|
||||
assert.NoError(t, err)
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
db.AssertMissing(t, "saved_filters", map[string]interface{}{
|
||||
"id": 1,
|
||||
|
@ -120,50 +138,65 @@ func TestSavedFilter_Rights(t *testing.T) {
|
|||
t.Run("create", func(t *testing.T) {
|
||||
// Should always be true
|
||||
db.LoadAndAssertFixtures(t)
|
||||
can, err := (&SavedFilter{}).CanCreate(user1)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
can, err := (&SavedFilter{}).CanCreate(s, user1)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, can)
|
||||
})
|
||||
t.Run("read", func(t *testing.T) {
|
||||
t.Run("owner", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
sf := &SavedFilter{
|
||||
ID: 1,
|
||||
Title: "Lorem",
|
||||
}
|
||||
can, max, err := sf.CanRead(user1)
|
||||
can, max, err := sf.CanRead(s, user1)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int(RightAdmin), max)
|
||||
assert.True(t, can)
|
||||
})
|
||||
t.Run("not owner", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
sf := &SavedFilter{
|
||||
ID: 1,
|
||||
Title: "Lorem",
|
||||
}
|
||||
can, _, err := sf.CanRead(user2)
|
||||
can, _, err := sf.CanRead(s, user2)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, can)
|
||||
})
|
||||
t.Run("nonexisting", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
sf := &SavedFilter{
|
||||
ID: 9999,
|
||||
Title: "Lorem",
|
||||
}
|
||||
can, _, err := sf.CanRead(user1)
|
||||
can, _, err := sf.CanRead(s, user1)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrSavedFilterDoesNotExist(err))
|
||||
assert.False(t, can)
|
||||
})
|
||||
t.Run("link share", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
sf := &SavedFilter{
|
||||
ID: 1,
|
||||
Title: "Lorem",
|
||||
}
|
||||
can, _, err := sf.CanRead(ls)
|
||||
can, _, err := sf.CanRead(s, ls)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrSavedFilterNotAvailableForLinkShare(err))
|
||||
assert.False(t, can)
|
||||
|
@ -172,42 +205,54 @@ func TestSavedFilter_Rights(t *testing.T) {
|
|||
t.Run("update", func(t *testing.T) {
|
||||
t.Run("owner", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
sf := &SavedFilter{
|
||||
ID: 1,
|
||||
Title: "Lorem",
|
||||
}
|
||||
can, err := sf.CanUpdate(user1)
|
||||
can, err := sf.CanUpdate(s, user1)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, can)
|
||||
})
|
||||
t.Run("not owner", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
sf := &SavedFilter{
|
||||
ID: 1,
|
||||
Title: "Lorem",
|
||||
}
|
||||
can, err := sf.CanUpdate(user2)
|
||||
can, err := sf.CanUpdate(s, user2)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, can)
|
||||
})
|
||||
t.Run("nonexisting", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
sf := &SavedFilter{
|
||||
ID: 9999,
|
||||
Title: "Lorem",
|
||||
}
|
||||
can, err := sf.CanUpdate(user1)
|
||||
can, err := sf.CanUpdate(s, user1)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrSavedFilterDoesNotExist(err))
|
||||
assert.False(t, can)
|
||||
})
|
||||
t.Run("link share", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
sf := &SavedFilter{
|
||||
ID: 1,
|
||||
Title: "Lorem",
|
||||
}
|
||||
can, err := sf.CanUpdate(ls)
|
||||
can, err := sf.CanUpdate(s, ls)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrSavedFilterNotAvailableForLinkShare(err))
|
||||
assert.False(t, can)
|
||||
|
@ -216,40 +261,52 @@ func TestSavedFilter_Rights(t *testing.T) {
|
|||
t.Run("delete", func(t *testing.T) {
|
||||
t.Run("owner", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
sf := &SavedFilter{
|
||||
ID: 1,
|
||||
}
|
||||
can, err := sf.CanDelete(user1)
|
||||
can, err := sf.CanDelete(s, user1)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, can)
|
||||
})
|
||||
t.Run("not owner", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
sf := &SavedFilter{
|
||||
ID: 1,
|
||||
}
|
||||
can, err := sf.CanDelete(user2)
|
||||
can, err := sf.CanDelete(s, user2)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, can)
|
||||
})
|
||||
t.Run("nonexisting", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
sf := &SavedFilter{
|
||||
ID: 9999,
|
||||
Title: "Lorem",
|
||||
}
|
||||
can, err := sf.CanDelete(user1)
|
||||
can, err := sf.CanDelete(s, user1)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrSavedFilterDoesNotExist(err))
|
||||
assert.False(t, can)
|
||||
})
|
||||
t.Run("link share", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
sf := &SavedFilter{
|
||||
ID: 1,
|
||||
Title: "Lorem",
|
||||
}
|
||||
can, err := sf.CanDelete(ls)
|
||||
can, err := sf.CanDelete(s, ls)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrSavedFilterNotAvailableForLinkShare(err))
|
||||
assert.False(t, can)
|
||||
|
|
|
@ -46,9 +46,9 @@ type TaskAssigneeWithUser struct {
|
|||
user.User `xorm:"extends"`
|
||||
}
|
||||
|
||||
func getRawTaskAssigneesForTasks(taskIDs []int64) (taskAssignees []*TaskAssigneeWithUser, err error) {
|
||||
func getRawTaskAssigneesForTasks(s *xorm.Session, taskIDs []int64) (taskAssignees []*TaskAssigneeWithUser, err error) {
|
||||
taskAssignees = []*TaskAssigneeWithUser{}
|
||||
err = x.Table("task_assignees").
|
||||
err = s.Table("task_assignees").
|
||||
Select("task_id, users.*").
|
||||
In("task_id", taskIDs).
|
||||
Join("INNER", "users", "task_assignees.user_id = users.id").
|
||||
|
@ -60,7 +60,7 @@ func getRawTaskAssigneesForTasks(taskIDs []int64) (taskAssignees []*TaskAssignee
|
|||
func (t *Task) updateTaskAssignees(s *xorm.Session, assignees []*user.User) (err error) {
|
||||
|
||||
// Load the current assignees
|
||||
currentAssignees, err := getRawTaskAssigneesForTasks([]int64{t.ID})
|
||||
currentAssignees, err := getRawTaskAssigneesForTasks(s, []int64{t.ID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -118,8 +118,7 @@ func (t *Task) updateTaskAssignees(s *xorm.Session, assignees []*user.User) (err
|
|||
}
|
||||
|
||||
// Get the list to perform later checks
|
||||
list := List{ID: t.ListID}
|
||||
err = list.GetSimpleByID()
|
||||
list, err := GetListSimpleByID(s, t.ListID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -133,7 +132,7 @@ func (t *Task) updateTaskAssignees(s *xorm.Session, assignees []*user.User) (err
|
|||
}
|
||||
|
||||
// Add the new assignee
|
||||
err = t.addNewAssigneeByID(u.ID, &list)
|
||||
err = t.addNewAssigneeByID(s, u.ID, list)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -141,7 +140,7 @@ func (t *Task) updateTaskAssignees(s *xorm.Session, assignees []*user.User) (err
|
|||
|
||||
t.setTaskAssignees(assignees)
|
||||
|
||||
err = updateListLastUpdated(&List{ID: t.ListID})
|
||||
err = updateListLastUpdated(s, &List{ID: t.ListID})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -167,13 +166,13 @@ func (t *Task) setTaskAssignees(assignees []*user.User) {
|
|||
// @Failure 403 {object} web.HTTPError "Not allowed to delete the assignee."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{taskID}/assignees/{userID} [delete]
|
||||
func (la *TaskAssginee) Delete() (err error) {
|
||||
_, err = x.Delete(&TaskAssginee{TaskID: la.TaskID, UserID: la.UserID})
|
||||
func (la *TaskAssginee) Delete(s *xorm.Session) (err error) {
|
||||
_, err = s.Delete(&TaskAssginee{TaskID: la.TaskID, UserID: la.UserID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = updateListByTaskID(la.TaskID)
|
||||
err = updateListByTaskID(s, la.TaskID)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -190,25 +189,25 @@ func (la *TaskAssginee) Delete() (err error) {
|
|||
// @Failure 400 {object} web.HTTPError "Invalid assignee object provided."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{taskID}/assignees [put]
|
||||
func (la *TaskAssginee) Create(a web.Auth) (err error) {
|
||||
func (la *TaskAssginee) Create(s *xorm.Session, a web.Auth) (err error) {
|
||||
|
||||
// Get the list to perform later checks
|
||||
list, err := GetListSimplByTaskID(la.TaskID)
|
||||
list, err := GetListSimplByTaskID(s, la.TaskID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
task := &Task{ID: la.TaskID}
|
||||
return task.addNewAssigneeByID(la.UserID, list)
|
||||
return task.addNewAssigneeByID(s, la.UserID, list)
|
||||
}
|
||||
|
||||
func (t *Task) addNewAssigneeByID(newAssigneeID int64, list *List) (err error) {
|
||||
func (t *Task) addNewAssigneeByID(s *xorm.Session, newAssigneeID int64, list *List) (err error) {
|
||||
// Check if the user exists and has access to the list
|
||||
newAssignee, err := user.GetUserByID(newAssigneeID)
|
||||
newAssignee, err := user.GetUserByID(s, newAssigneeID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
canRead, _, err := list.CanRead(newAssignee)
|
||||
canRead, _, err := list.CanRead(s, newAssignee)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -216,7 +215,7 @@ func (t *Task) addNewAssigneeByID(newAssigneeID int64, list *List) (err error) {
|
|||
return ErrUserDoesNotHaveAccessToList{list.ID, newAssigneeID}
|
||||
}
|
||||
|
||||
_, err = x.Insert(TaskAssginee{
|
||||
_, err = s.Insert(TaskAssginee{
|
||||
TaskID: t.ID,
|
||||
UserID: newAssigneeID,
|
||||
})
|
||||
|
@ -224,7 +223,7 @@ func (t *Task) addNewAssigneeByID(newAssigneeID int64, list *List) (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
err = updateListLastUpdated(&List{ID: t.ListID})
|
||||
err = updateListLastUpdated(s, &List{ID: t.ListID})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -242,13 +241,13 @@ func (t *Task) addNewAssigneeByID(newAssigneeID int64, list *List) (err error) {
|
|||
// @Success 200 {array} user.User "The assignees"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{taskID}/assignees [get]
|
||||
func (la *TaskAssginee) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
||||
task, err := GetListSimplByTaskID(la.TaskID)
|
||||
func (la *TaskAssginee) ReadAll(s *xorm.Session, a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
||||
task, err := GetListSimplByTaskID(s, la.TaskID)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
can, _, err := task.CanRead(a)
|
||||
can, _, err := task.CanRead(s, a)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
@ -258,7 +257,7 @@ func (la *TaskAssginee) ReadAll(a web.Auth, search string, page int, perPage int
|
|||
limit, start := getLimitFromPageIndex(page, perPage)
|
||||
|
||||
var taskAssignees []*user.User
|
||||
query := x.Table("task_assignees").
|
||||
query := s.Table("task_assignees").
|
||||
Select("users.*").
|
||||
Join("INNER", "users", "task_assignees.user_id = users.id").
|
||||
Where("task_id = ? AND users.username LIKE ?", la.TaskID, "%"+search+"%")
|
||||
|
@ -270,7 +269,7 @@ func (la *TaskAssginee) ReadAll(a web.Auth, search string, page int, perPage int
|
|||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
numberOfTotalItems, err = x.Table("task_assignees").
|
||||
numberOfTotalItems, err = s.Table("task_assignees").
|
||||
Select("users.*").
|
||||
Join("INNER", "users", "task_assignees.user_id = users.id").
|
||||
Where("task_id = ? AND users.username LIKE ?", la.TaskID, "%"+search+"%").
|
||||
|
@ -301,14 +300,12 @@ type BulkAssignees struct {
|
|||
// @Failure 400 {object} web.HTTPError "Invalid assignee object provided."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{taskID}/assignees/bulk [post]
|
||||
func (ba *BulkAssignees) Create(a web.Auth) (err error) {
|
||||
s := x.NewSession()
|
||||
|
||||
task, err := GetTaskByIDSimple(ba.TaskID)
|
||||
func (ba *BulkAssignees) Create(s *xorm.Session, a web.Auth) (err error) {
|
||||
task, err := GetTaskByIDSimple(s, ba.TaskID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
assignees, err := getRawTaskAssigneesForTasks([]int64{task.ID})
|
||||
assignees, err := getRawTaskAssigneesForTasks(s, []int64{task.ID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -317,10 +314,5 @@ func (ba *BulkAssignees) Create(a web.Auth) (err error) {
|
|||
}
|
||||
|
||||
err = task.updateTaskAssignees(s, ba.Assignees)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
return s.Commit()
|
||||
return
|
||||
}
|
||||
|
|
|
@ -18,28 +18,29 @@ package models
|
|||
|
||||
import (
|
||||
"code.vikunja.io/web"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// CanCreate checks if a user can add a new assignee
|
||||
func (la *TaskAssginee) CanCreate(a web.Auth) (bool, error) {
|
||||
return canDoTaskAssingee(la.TaskID, a)
|
||||
func (la *TaskAssginee) CanCreate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
return canDoTaskAssingee(s, la.TaskID, a)
|
||||
}
|
||||
|
||||
// CanCreate checks if a user can add a new assignee
|
||||
func (ba *BulkAssignees) CanCreate(a web.Auth) (bool, error) {
|
||||
return canDoTaskAssingee(ba.TaskID, a)
|
||||
func (ba *BulkAssignees) CanCreate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
return canDoTaskAssingee(s, ba.TaskID, a)
|
||||
}
|
||||
|
||||
// CanDelete checks if a user can delete an assignee
|
||||
func (la *TaskAssginee) CanDelete(a web.Auth) (bool, error) {
|
||||
return canDoTaskAssingee(la.TaskID, a)
|
||||
func (la *TaskAssginee) CanDelete(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
return canDoTaskAssingee(s, la.TaskID, a)
|
||||
}
|
||||
|
||||
func canDoTaskAssingee(taskID int64, a web.Auth) (bool, error) {
|
||||
func canDoTaskAssingee(s *xorm.Session, taskID int64, a web.Auth) (bool, error) {
|
||||
// Check if the current user can edit the list
|
||||
list, err := GetListSimplByTaskID(taskID)
|
||||
list, err := GetListSimplByTaskID(s, taskID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return list.CanUpdate(a)
|
||||
return list.CanUpdate(s, a)
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
"code.vikunja.io/api/pkg/files"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
"code.vikunja.io/web"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// TaskAttachment is the definition of a task attachment
|
||||
|
@ -49,7 +50,7 @@ func (TaskAttachment) TableName() string {
|
|||
|
||||
// NewAttachment creates a new task attachment
|
||||
// Note: I'm not sure if only accepting an io.ReadCloser and not an afero.File or os.File instead is a good way of doing things.
|
||||
func (ta *TaskAttachment) NewAttachment(f io.ReadCloser, realname string, realsize uint64, a web.Auth) error {
|
||||
func (ta *TaskAttachment) NewAttachment(s *xorm.Session, f io.ReadCloser, realname string, realsize uint64, a web.Auth) error {
|
||||
|
||||
// Store the file
|
||||
file, err := files.Create(f, realname, realsize, a)
|
||||
|
@ -64,7 +65,7 @@ func (ta *TaskAttachment) NewAttachment(f io.ReadCloser, realname string, realsi
|
|||
// Add an entry to the db
|
||||
ta.FileID = file.ID
|
||||
ta.CreatedByID = a.GetID()
|
||||
_, err = x.Insert(ta)
|
||||
_, err = s.Insert(ta)
|
||||
if err != nil {
|
||||
// remove the uploaded file if adding it to the db fails
|
||||
if err2 := file.Delete(); err2 != nil {
|
||||
|
@ -77,8 +78,8 @@ func (ta *TaskAttachment) NewAttachment(f io.ReadCloser, realname string, realsi
|
|||
}
|
||||
|
||||
// ReadOne returns a task attachment
|
||||
func (ta *TaskAttachment) ReadOne() (err error) {
|
||||
exists, err := x.Where("id = ?", ta.ID).Get(ta)
|
||||
func (ta *TaskAttachment) ReadOne(s *xorm.Session) (err error) {
|
||||
exists, err := s.Where("id = ?", ta.ID).Get(ta)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -110,12 +111,12 @@ func (ta *TaskAttachment) ReadOne() (err error) {
|
|||
// @Failure 404 {object} models.Message "The task does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{id}/attachments [get]
|
||||
func (ta *TaskAttachment) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
||||
func (ta *TaskAttachment) ReadAll(s *xorm.Session, a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
||||
attachments := []*TaskAttachment{}
|
||||
|
||||
limit, start := getLimitFromPageIndex(page, perPage)
|
||||
|
||||
query := x.
|
||||
query := s.
|
||||
Where("task_id = ?", ta.TaskID)
|
||||
if limit > 0 {
|
||||
query = query.Limit(limit, start)
|
||||
|
@ -133,13 +134,13 @@ func (ta *TaskAttachment) ReadAll(a web.Auth, search string, page int, perPage i
|
|||
}
|
||||
|
||||
fs := make(map[int64]*files.File)
|
||||
err = x.In("id", fileIDs).Find(&fs)
|
||||
err = s.In("id", fileIDs).Find(&fs)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
us := make(map[int64]*user.User)
|
||||
err = x.In("id", userIDs).Find(&us)
|
||||
err = s.In("id", userIDs).Find(&us)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
@ -153,7 +154,7 @@ func (ta *TaskAttachment) ReadAll(a web.Auth, search string, page int, perPage i
|
|||
r.CreatedBy = us[r.CreatedByID]
|
||||
}
|
||||
|
||||
numberOfTotalItems, err = x.
|
||||
numberOfTotalItems, err = s.
|
||||
Where("task_id = ?", ta.TaskID).
|
||||
Count(&TaskAttachment{})
|
||||
return attachments, len(attachments), numberOfTotalItems, err
|
||||
|
@ -173,15 +174,17 @@ func (ta *TaskAttachment) ReadAll(a web.Auth, search string, page int, perPage i
|
|||
// @Failure 404 {object} models.Message "The task does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{id}/attachments/{attachmentID} [delete]
|
||||
func (ta *TaskAttachment) Delete() error {
|
||||
func (ta *TaskAttachment) Delete(s *xorm.Session) error {
|
||||
// Load the attachment
|
||||
err := ta.ReadOne()
|
||||
err := ta.ReadOne(s)
|
||||
if err != nil && !files.IsErrFileDoesNotExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete it
|
||||
_, err = x.Where("task_id = ? AND id = ?", ta.TaskID, ta.ID).Delete(ta)
|
||||
_, err = s.
|
||||
Where("task_id = ? AND id = ?", ta.TaskID, ta.ID).
|
||||
Delete(ta)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -195,9 +198,9 @@ func (ta *TaskAttachment) Delete() error {
|
|||
return err
|
||||
}
|
||||
|
||||
func getTaskAttachmentsByTaskIDs(taskIDs []int64) (attachments []*TaskAttachment, err error) {
|
||||
func getTaskAttachmentsByTaskIDs(s *xorm.Session, taskIDs []int64) (attachments []*TaskAttachment, err error) {
|
||||
attachments = []*TaskAttachment{}
|
||||
err = x.
|
||||
err = s.
|
||||
In("task_id", taskIDs).
|
||||
Find(&attachments)
|
||||
if err != nil {
|
||||
|
@ -213,13 +216,13 @@ func getTaskAttachmentsByTaskIDs(taskIDs []int64) (attachments []*TaskAttachment
|
|||
|
||||
// Get all files
|
||||
fs := make(map[int64]*files.File)
|
||||
err = x.In("id", fileIDs).Find(&fs)
|
||||
err = s.In("id", fileIDs).Find(&fs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
users := make(map[int64]*user.User)
|
||||
err = x.In("id", userIDs).Find(&users)
|
||||
err = s.In("id", userIDs).Find(&users)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -16,25 +16,28 @@
|
|||
|
||||
package models
|
||||
|
||||
import "code.vikunja.io/web"
|
||||
import (
|
||||
"code.vikunja.io/web"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// CanRead checks if the user can see an attachment
|
||||
func (ta *TaskAttachment) CanRead(a web.Auth) (bool, int, error) {
|
||||
func (ta *TaskAttachment) CanRead(s *xorm.Session, a web.Auth) (bool, int, error) {
|
||||
t := &Task{ID: ta.TaskID}
|
||||
return t.CanRead(a)
|
||||
return t.CanRead(s, a)
|
||||
}
|
||||
|
||||
// CanDelete checks if the user can delete an attachment
|
||||
func (ta *TaskAttachment) CanDelete(a web.Auth) (bool, error) {
|
||||
func (ta *TaskAttachment) CanDelete(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
t := &Task{ID: ta.TaskID}
|
||||
return t.CanWrite(a)
|
||||
return t.CanWrite(s, a)
|
||||
}
|
||||
|
||||
// CanCreate checks if the user can create an attachment
|
||||
func (ta *TaskAttachment) CanCreate(a web.Auth) (bool, error) {
|
||||
t, err := GetTaskByIDSimple(ta.TaskID)
|
||||
func (ta *TaskAttachment) CanCreate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
t, err := GetTaskByIDSimple(s, ta.TaskID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return t.CanCreate(a)
|
||||
return t.CanCreate(s, a)
|
||||
}
|
||||
|
|
|
@ -33,11 +33,14 @@ import (
|
|||
func TestTaskAttachment_ReadOne(t *testing.T) {
|
||||
t.Run("Normal File", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
files.InitTestFileFixtures(t)
|
||||
ta := &TaskAttachment{
|
||||
ID: 1,
|
||||
}
|
||||
err := ta.ReadOne()
|
||||
err := ta.ReadOne(s)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, ta.File)
|
||||
assert.True(t, ta.File.ID == ta.FileID && ta.FileID != 0)
|
||||
|
@ -54,21 +57,27 @@ func TestTaskAttachment_ReadOne(t *testing.T) {
|
|||
})
|
||||
t.Run("Nonexisting Attachment", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
files.InitTestFileFixtures(t)
|
||||
ta := &TaskAttachment{
|
||||
ID: 9999,
|
||||
}
|
||||
err := ta.ReadOne()
|
||||
err := ta.ReadOne(s)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTaskAttachmentDoesNotExist(err))
|
||||
})
|
||||
t.Run("Existing Attachment, Nonexisting File", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
files.InitTestFileFixtures(t)
|
||||
ta := &TaskAttachment{
|
||||
ID: 2,
|
||||
}
|
||||
err := ta.ReadOne()
|
||||
err := ta.ReadOne(s)
|
||||
assert.Error(t, err)
|
||||
assert.EqualError(t, err, "file 9999 does not exist")
|
||||
})
|
||||
|
@ -94,6 +103,9 @@ func (t *testfile) Close() error {
|
|||
|
||||
func TestTaskAttachment_NewAttachment(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
files.InitTestFileFixtures(t)
|
||||
// Assert the file is being stored correctly
|
||||
ta := TaskAttachment{
|
||||
|
@ -104,7 +116,7 @@ func TestTaskAttachment_NewAttachment(t *testing.T) {
|
|||
}
|
||||
testuser := &user.User{ID: 1}
|
||||
|
||||
err := ta.NewAttachment(tf, "testfile", 100, testuser)
|
||||
err := ta.NewAttachment(s, tf, "testfile", 100, testuser)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEqual(t, 0, ta.FileID)
|
||||
_, err = files.FileStat("files/" + strconv.FormatInt(ta.FileID, 10))
|
||||
|
@ -125,9 +137,12 @@ func TestTaskAttachment_NewAttachment(t *testing.T) {
|
|||
|
||||
func TestTaskAttachment_ReadAll(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
files.InitTestFileFixtures(t)
|
||||
ta := &TaskAttachment{TaskID: 1}
|
||||
as, _, _, err := ta.ReadAll(&user.User{ID: 1}, "", 0, 50)
|
||||
as, _, _, err := ta.ReadAll(s, &user.User{ID: 1}, "", 0, 50)
|
||||
attachments, _ := as.([]*TaskAttachment)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, attachments, 2)
|
||||
|
@ -136,10 +151,13 @@ func TestTaskAttachment_ReadAll(t *testing.T) {
|
|||
|
||||
func TestTaskAttachment_Delete(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
files.InitTestFileFixtures(t)
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
ta := &TaskAttachment{ID: 1}
|
||||
err := ta.Delete()
|
||||
err := ta.Delete(s)
|
||||
assert.NoError(t, err)
|
||||
// Check if the file itself was deleted
|
||||
_, err = files.FileStat("/1") // The new file has the id 2 since it's the second attachment
|
||||
|
@ -148,14 +166,14 @@ func TestTaskAttachment_Delete(t *testing.T) {
|
|||
t.Run("Nonexisting", func(t *testing.T) {
|
||||
files.InitTestFileFixtures(t)
|
||||
ta := &TaskAttachment{ID: 9999}
|
||||
err := ta.Delete()
|
||||
err := ta.Delete(s)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTaskAttachmentDoesNotExist(err))
|
||||
})
|
||||
t.Run("Existing attachment, nonexisting file", func(t *testing.T) {
|
||||
files.InitTestFileFixtures(t)
|
||||
ta := &TaskAttachment{ID: 2}
|
||||
err := ta.Delete()
|
||||
err := ta.Delete(s)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
@ -165,15 +183,21 @@ func TestTaskAttachment_Rights(t *testing.T) {
|
|||
t.Run("Can Read", func(t *testing.T) {
|
||||
t.Run("Allowed", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
ta := &TaskAttachment{TaskID: 1}
|
||||
can, _, err := ta.CanRead(u)
|
||||
can, _, err := ta.CanRead(s, u)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, can)
|
||||
})
|
||||
t.Run("Forbidden", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
ta := &TaskAttachment{TaskID: 14}
|
||||
can, _, err := ta.CanRead(u)
|
||||
can, _, err := ta.CanRead(s, u)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, can)
|
||||
})
|
||||
|
@ -181,22 +205,31 @@ func TestTaskAttachment_Rights(t *testing.T) {
|
|||
t.Run("Can Delete", func(t *testing.T) {
|
||||
t.Run("Allowed", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
ta := &TaskAttachment{TaskID: 1}
|
||||
can, err := ta.CanDelete(u)
|
||||
can, err := ta.CanDelete(s, u)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, can)
|
||||
})
|
||||
t.Run("Forbidden, no access", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
ta := &TaskAttachment{TaskID: 14}
|
||||
can, err := ta.CanDelete(u)
|
||||
can, err := ta.CanDelete(s, u)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, can)
|
||||
})
|
||||
t.Run("Forbidden, shared read only", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
ta := &TaskAttachment{TaskID: 15}
|
||||
can, err := ta.CanDelete(u)
|
||||
can, err := ta.CanDelete(s, u)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, can)
|
||||
})
|
||||
|
@ -204,22 +237,31 @@ func TestTaskAttachment_Rights(t *testing.T) {
|
|||
t.Run("Can Create", func(t *testing.T) {
|
||||
t.Run("Allowed", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
ta := &TaskAttachment{TaskID: 1}
|
||||
can, err := ta.CanCreate(u)
|
||||
can, err := ta.CanCreate(s, u)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, can)
|
||||
})
|
||||
t.Run("Forbidden, no access", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
ta := &TaskAttachment{TaskID: 14}
|
||||
can, err := ta.CanCreate(u)
|
||||
can, err := ta.CanCreate(s, u)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, can)
|
||||
})
|
||||
t.Run("Forbidden, shared read only", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
ta := &TaskAttachment{TaskID: 15}
|
||||
can, err := ta.CanCreate(u)
|
||||
can, err := ta.CanCreate(s, u)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, can)
|
||||
})
|
||||
|
|
|
@ -20,6 +20,7 @@ package models
|
|||
import (
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
"code.vikunja.io/web"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// TaskCollection is a struct used to hold filter details and not clutter the Task struct with information not related to actual tasks.
|
||||
|
@ -100,17 +101,17 @@ func validateTaskField(fieldName string) error {
|
|||
// @Success 200 {array} models.Task "The tasks"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{listID}/tasks [get]
|
||||
func (tf *TaskCollection) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, totalItems int64, err error) {
|
||||
func (tf *TaskCollection) ReadAll(s *xorm.Session, a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, totalItems int64, err error) {
|
||||
|
||||
// If the list id is < -1 this means we're dealing with a saved filter - in that case we get and populate the filter
|
||||
// -1 is the favorites list which works as intended
|
||||
if tf.ListID < -1 {
|
||||
s, err := getSavedFilterSimpleByID(getSavedFilterIDFromListID(tf.ListID))
|
||||
sf, err := getSavedFilterSimpleByID(s, getSavedFilterIDFromListID(tf.ListID))
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
return s.getTaskCollection().ReadAll(a, search, page, perPage)
|
||||
return sf.getTaskCollection().ReadAll(s, a, search, page, perPage)
|
||||
}
|
||||
|
||||
if len(tf.SortByArr) > 0 {
|
||||
|
@ -156,28 +157,30 @@ func (tf *TaskCollection) ReadAll(a web.Auth, search string, page int, perPage i
|
|||
|
||||
shareAuth, is := a.(*LinkSharing)
|
||||
if is {
|
||||
list := &List{ID: shareAuth.ListID}
|
||||
err := list.GetSimpleByID()
|
||||
list, err := GetListSimpleByID(s, shareAuth.ListID)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
return getTasksForLists([]*List{list}, a, taskopts)
|
||||
return getTasksForLists(s, []*List{list}, a, taskopts)
|
||||
}
|
||||
|
||||
// If the list ID is not set, we get all tasks for the user.
|
||||
// This allows to use this function in Task.ReadAll with a possibility to deprecate the latter at some point.
|
||||
if tf.ListID == 0 {
|
||||
tf.Lists, _, _, err = getRawListsForUser(&listOptions{
|
||||
user: &user.User{ID: a.GetID()},
|
||||
page: -1,
|
||||
})
|
||||
tf.Lists, _, _, err = getRawListsForUser(
|
||||
s,
|
||||
&listOptions{
|
||||
user: &user.User{ID: a.GetID()},
|
||||
page: -1,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
} else {
|
||||
// Check the list exists and the user has acess on it
|
||||
list := &List{ID: tf.ListID}
|
||||
canRead, _, err := list.CanRead(a)
|
||||
canRead, _, err := list.CanRead(s, a)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
@ -187,5 +190,5 @@ func (tf *TaskCollection) ReadAll(a web.Auth, search string, page int, perPage i
|
|||
tf.Lists = []*List{{ID: tf.ListID}}
|
||||
}
|
||||
|
||||
return getTasksForLists(tf.Lists, a, taskopts)
|
||||
return getTasksForLists(s, tf.Lists, a, taskopts)
|
||||
}
|
||||
|
|
|
@ -986,6 +986,8 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
lt := &TaskCollection{
|
||||
ListID: tt.fields.ListID,
|
||||
|
@ -1000,7 +1002,7 @@ func TestTaskCollection_ReadAll(t *testing.T) {
|
|||
CRUDable: tt.fields.CRUDable,
|
||||
Rights: tt.fields.Rights,
|
||||
}
|
||||
got, _, _, err := lt.ReadAll(tt.args.a, tt.args.search, tt.args.page, 50)
|
||||
got, _, _, err := lt.ReadAll(s, tt.args.a, tt.args.search, tt.args.page, 50)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Test %s, Task.ReadAll() error = %v, wantErr %v", tt.name, err, tt.wantErr)
|
||||
return
|
||||
|
|
|
@ -17,28 +17,31 @@
|
|||
|
||||
package models
|
||||
|
||||
import "code.vikunja.io/web"
|
||||
import (
|
||||
"code.vikunja.io/web"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// CanRead checks if a user can read a comment
|
||||
func (tc *TaskComment) CanRead(a web.Auth) (bool, int, error) {
|
||||
func (tc *TaskComment) CanRead(s *xorm.Session, a web.Auth) (bool, int, error) {
|
||||
t := Task{ID: tc.TaskID}
|
||||
return t.CanRead(a)
|
||||
return t.CanRead(s, a)
|
||||
}
|
||||
|
||||
// CanDelete checks if a user can delete a comment
|
||||
func (tc *TaskComment) CanDelete(a web.Auth) (bool, error) {
|
||||
func (tc *TaskComment) CanDelete(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
t := Task{ID: tc.TaskID}
|
||||
return t.CanWrite(a)
|
||||
return t.CanWrite(s, a)
|
||||
}
|
||||
|
||||
// CanUpdate checks if a user can update a comment
|
||||
func (tc *TaskComment) CanUpdate(a web.Auth) (bool, error) {
|
||||
func (tc *TaskComment) CanUpdate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
t := Task{ID: tc.TaskID}
|
||||
return t.CanWrite(a)
|
||||
return t.CanWrite(s, a)
|
||||
}
|
||||
|
||||
// CanCreate checks if a user can create a new comment
|
||||
func (tc *TaskComment) CanCreate(a web.Auth) (bool, error) {
|
||||
func (tc *TaskComment) CanCreate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
t := Task{ID: tc.TaskID}
|
||||
return t.CanWrite(a)
|
||||
return t.CanWrite(s, a)
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ package models
|
|||
import (
|
||||
"time"
|
||||
|
||||
"xorm.io/xorm"
|
||||
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
"code.vikunja.io/web"
|
||||
)
|
||||
|
@ -57,19 +59,19 @@ func (tc *TaskComment) TableName() string {
|
|||
// @Failure 400 {object} web.HTTPError "Invalid task comment object provided."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{taskID}/comments [put]
|
||||
func (tc *TaskComment) Create(a web.Auth) (err error) {
|
||||
func (tc *TaskComment) Create(s *xorm.Session, a web.Auth) (err error) {
|
||||
// Check if the task exists
|
||||
_, err = GetTaskSimple(&Task{ID: tc.TaskID})
|
||||
_, err = GetTaskSimple(s, &Task{ID: tc.TaskID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tc.AuthorID = a.GetID()
|
||||
_, err = x.Insert(tc)
|
||||
_, err = s.Insert(tc)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tc.Author, err = user.GetUserByID(a.GetID())
|
||||
tc.Author, err = user.GetUserByID(s, a.GetID())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -87,8 +89,11 @@ func (tc *TaskComment) Create(a web.Auth) (err error) {
|
|||
// @Failure 404 {object} web.HTTPError "The task comment was not found."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{taskID}/comments/{commentID} [delete]
|
||||
func (tc *TaskComment) Delete() error {
|
||||
deleted, err := x.ID(tc.ID).NoAutoCondition().Delete(tc)
|
||||
func (tc *TaskComment) Delete(s *xorm.Session) error {
|
||||
deleted, err := s.
|
||||
ID(tc.ID).
|
||||
NoAutoCondition().
|
||||
Delete(tc)
|
||||
if deleted == 0 {
|
||||
return ErrTaskCommentDoesNotExist{ID: tc.ID}
|
||||
}
|
||||
|
@ -109,8 +114,11 @@ func (tc *TaskComment) Delete() error {
|
|||
// @Failure 404 {object} web.HTTPError "The task comment was not found."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{taskID}/comments/{commentID} [post]
|
||||
func (tc *TaskComment) Update() error {
|
||||
updated, err := x.ID(tc.ID).Cols("comment").Update(tc)
|
||||
func (tc *TaskComment) Update(s *xorm.Session) error {
|
||||
updated, err := s.
|
||||
ID(tc.ID).
|
||||
Cols("comment").
|
||||
Update(tc)
|
||||
if updated == 0 {
|
||||
return ErrTaskCommentDoesNotExist{ID: tc.ID}
|
||||
}
|
||||
|
@ -131,8 +139,8 @@ func (tc *TaskComment) Update() error {
|
|||
// @Failure 404 {object} web.HTTPError "The task comment was not found."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{taskID}/comments/{commentID} [get]
|
||||
func (tc *TaskComment) ReadOne() (err error) {
|
||||
exists, err := x.Get(tc)
|
||||
func (tc *TaskComment) ReadOne(s *xorm.Session) (err error) {
|
||||
exists, err := s.Get(tc)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -145,7 +153,7 @@ func (tc *TaskComment) ReadOne() (err error) {
|
|||
|
||||
// Get the author
|
||||
author := &user.User{}
|
||||
_, err = x.
|
||||
_, err = s.
|
||||
Where("id = ?", tc.AuthorID).
|
||||
Get(author)
|
||||
tc.Author = author
|
||||
|
@ -163,10 +171,10 @@ func (tc *TaskComment) ReadOne() (err error) {
|
|||
// @Success 200 {array} models.TaskComment "The array with all task comments"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{taskID}/comments [get]
|
||||
func (tc *TaskComment) ReadAll(auth web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
||||
func (tc *TaskComment) ReadAll(s *xorm.Session, auth web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
||||
|
||||
// Check if the user has access to the task
|
||||
canRead, _, err := tc.CanRead(auth)
|
||||
canRead, _, err := tc.CanRead(s, auth)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
@ -184,7 +192,7 @@ func (tc *TaskComment) ReadAll(auth web.Auth, search string, page int, perPage i
|
|||
limit, start := getLimitFromPageIndex(page, perPage)
|
||||
|
||||
comments := []*TaskComment{}
|
||||
query := x.
|
||||
query := s.
|
||||
Where("task_id = ? AND comment like ?", tc.TaskID, "%"+search+"%").
|
||||
Join("LEFT", "users", "users.id = task_comments.author_id")
|
||||
if limit > 0 {
|
||||
|
@ -197,7 +205,7 @@ func (tc *TaskComment) ReadAll(auth web.Auth, search string, page int, perPage i
|
|||
|
||||
// Get all authors
|
||||
authors := make(map[int64]*user.User)
|
||||
err = x.
|
||||
err = s.
|
||||
Select("users.*").
|
||||
Table("task_comments").
|
||||
Where("task_id = ? AND comment like ?", tc.TaskID, "%"+search+"%").
|
||||
|
@ -211,7 +219,7 @@ func (tc *TaskComment) ReadAll(auth web.Auth, search string, page int, perPage i
|
|||
comment.Author = authors[comment.AuthorID]
|
||||
}
|
||||
|
||||
numberOfTotalItems, err = x.
|
||||
numberOfTotalItems, err = s.
|
||||
Where("task_id = ? AND comment like ?", tc.TaskID, "%"+search+"%").
|
||||
Count(&TaskCommentWithAuthor{})
|
||||
return comments, len(comments), numberOfTotalItems, err
|
||||
|
|
|
@ -28,14 +28,20 @@ func TestTaskComment_Create(t *testing.T) {
|
|||
u := &user.User{ID: 1}
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
tc := &TaskComment{
|
||||
Comment: "test",
|
||||
TaskID: 1,
|
||||
}
|
||||
err := tc.Create(u)
|
||||
err := tc.Create(s, u)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "test", tc.Comment)
|
||||
assert.Equal(t, int64(1), tc.Author.ID)
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
|
||||
db.AssertExists(t, "task_comments", map[string]interface{}{
|
||||
"id": tc.ID,
|
||||
"author_id": u.ID,
|
||||
|
@ -45,11 +51,14 @@ func TestTaskComment_Create(t *testing.T) {
|
|||
})
|
||||
t.Run("nonexisting task", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
tc := &TaskComment{
|
||||
Comment: "test",
|
||||
TaskID: 99999,
|
||||
}
|
||||
err := tc.Create(u)
|
||||
err := tc.Create(s, u)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTaskDoesNotExist(err))
|
||||
})
|
||||
|
@ -58,17 +67,26 @@ func TestTaskComment_Create(t *testing.T) {
|
|||
func TestTaskComment_Delete(t *testing.T) {
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
tc := &TaskComment{ID: 1}
|
||||
err := tc.Delete()
|
||||
err := tc.Delete(s)
|
||||
assert.NoError(t, err)
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
|
||||
db.AssertMissing(t, "task_comments", map[string]interface{}{
|
||||
"id": 1,
|
||||
})
|
||||
})
|
||||
t.Run("nonexisting comment", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
tc := &TaskComment{ID: 9999}
|
||||
err := tc.Delete()
|
||||
err := tc.Delete(s)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTaskCommentDoesNotExist(err))
|
||||
})
|
||||
|
@ -77,12 +95,18 @@ func TestTaskComment_Delete(t *testing.T) {
|
|||
func TestTaskComment_Update(t *testing.T) {
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
tc := &TaskComment{
|
||||
ID: 1,
|
||||
Comment: "testing",
|
||||
}
|
||||
err := tc.Update()
|
||||
err := tc.Update(s)
|
||||
assert.NoError(t, err)
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
|
||||
db.AssertExists(t, "task_comments", map[string]interface{}{
|
||||
"id": 1,
|
||||
"comment": "testing",
|
||||
|
@ -90,10 +114,13 @@ func TestTaskComment_Update(t *testing.T) {
|
|||
})
|
||||
t.Run("nonexisting comment", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
tc := &TaskComment{
|
||||
ID: 9999,
|
||||
}
|
||||
err := tc.Update()
|
||||
err := tc.Update(s)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTaskCommentDoesNotExist(err))
|
||||
})
|
||||
|
@ -102,16 +129,22 @@ func TestTaskComment_Update(t *testing.T) {
|
|||
func TestTaskComment_ReadOne(t *testing.T) {
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
tc := &TaskComment{ID: 1}
|
||||
err := tc.ReadOne()
|
||||
err := tc.ReadOne(s)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "Lorem Ipsum Dolor Sit Amet", tc.Comment)
|
||||
assert.NotEmpty(t, tc.Author.ID)
|
||||
})
|
||||
t.Run("nonexisting", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
tc := &TaskComment{ID: 9999}
|
||||
err := tc.ReadOne()
|
||||
err := tc.ReadOne(s)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTaskCommentDoesNotExist(err))
|
||||
})
|
||||
|
@ -120,9 +153,12 @@ func TestTaskComment_ReadOne(t *testing.T) {
|
|||
func TestTaskComment_ReadAll(t *testing.T) {
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
tc := &TaskComment{TaskID: 1}
|
||||
u := &user.User{ID: 1}
|
||||
result, resultCount, total, err := tc.ReadAll(u, "", 0, -1)
|
||||
result, resultCount, total, err := tc.ReadAll(s, u, "", 0, -1)
|
||||
resultComment := result.([]*TaskComment)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, resultCount)
|
||||
|
@ -133,9 +169,12 @@ func TestTaskComment_ReadAll(t *testing.T) {
|
|||
})
|
||||
t.Run("no access to task", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
tc := &TaskComment{TaskID: 14}
|
||||
u := &user.User{ID: 1}
|
||||
_, _, _, err := tc.ReadAll(u, "", 0, -1)
|
||||
_, _, _, err := tc.ReadAll(s, u, "", 0, -1)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrGenericForbidden(err))
|
||||
})
|
||||
|
|
|
@ -20,6 +20,8 @@ package models
|
|||
import (
|
||||
"time"
|
||||
|
||||
"xorm.io/xorm"
|
||||
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
"code.vikunja.io/web"
|
||||
)
|
||||
|
@ -117,7 +119,7 @@ type RelatedTaskMap map[RelationKind][]*Task
|
|||
// @Failure 400 {object} web.HTTPError "Invalid task relation object provided."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{taskID}/relations [put]
|
||||
func (rel *TaskRelation) Create(a web.Auth) error {
|
||||
func (rel *TaskRelation) Create(s *xorm.Session, a web.Auth) error {
|
||||
|
||||
// Check if both tasks are the same
|
||||
if rel.TaskID == rel.OtherTaskID {
|
||||
|
@ -128,7 +130,7 @@ func (rel *TaskRelation) Create(a web.Auth) error {
|
|||
}
|
||||
|
||||
// Check if the relation already exists, in one form or the other.
|
||||
exists, err := x.
|
||||
exists, err := s.
|
||||
Where("(task_id = ? AND other_task_id = ? AND relation_kind = ?) OR (task_id = ? AND other_task_id = ? AND relation_kind = ?)",
|
||||
rel.TaskID, rel.OtherTaskID, rel.RelationKind, rel.TaskID, rel.OtherTaskID, rel.RelationKind).
|
||||
Exist(rel)
|
||||
|
@ -180,7 +182,7 @@ func (rel *TaskRelation) Create(a web.Auth) error {
|
|||
}
|
||||
|
||||
// Finally insert everything
|
||||
_, err = x.Insert(&[]*TaskRelation{
|
||||
_, err = s.Insert(&[]*TaskRelation{
|
||||
rel,
|
||||
otherRelation,
|
||||
})
|
||||
|
@ -200,9 +202,9 @@ func (rel *TaskRelation) Create(a web.Auth) error {
|
|||
// @Failure 404 {object} web.HTTPError "The task relation was not found."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{taskID}/relations [delete]
|
||||
func (rel *TaskRelation) Delete() error {
|
||||
func (rel *TaskRelation) Delete(s *xorm.Session) error {
|
||||
// Check if the relation exists
|
||||
exists, err := x.
|
||||
exists, err := s.
|
||||
Cols("task_id", "other_task_id", "relation_kind").
|
||||
Get(rel)
|
||||
if err != nil {
|
||||
|
@ -216,6 +218,6 @@ func (rel *TaskRelation) Delete() error {
|
|||
}
|
||||
}
|
||||
|
||||
_, err = x.Delete(rel)
|
||||
_, err = s.Delete(rel)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -17,17 +17,20 @@
|
|||
|
||||
package models
|
||||
|
||||
import "code.vikunja.io/web"
|
||||
import (
|
||||
"code.vikunja.io/web"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// CanDelete checks if a user can delete a task relation
|
||||
func (rel *TaskRelation) CanDelete(a web.Auth) (bool, error) {
|
||||
func (rel *TaskRelation) CanDelete(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
// A user can delete a relation if it can update the base task
|
||||
baseTask := &Task{ID: rel.TaskID}
|
||||
return baseTask.CanUpdate(a)
|
||||
return baseTask.CanUpdate(s, a)
|
||||
}
|
||||
|
||||
// CanCreate checks if a user can create a new relation between two relations
|
||||
func (rel *TaskRelation) CanCreate(a web.Auth) (bool, error) {
|
||||
func (rel *TaskRelation) CanCreate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
// Check if the relation kind is valid
|
||||
if !rel.RelationKind.isValid() {
|
||||
return false, ErrInvalidRelationKind{Kind: rel.RelationKind}
|
||||
|
@ -35,14 +38,14 @@ func (rel *TaskRelation) CanCreate(a web.Auth) (bool, error) {
|
|||
|
||||
// Needs have write access to the base task and at least read access to the other task
|
||||
baseTask := &Task{ID: rel.TaskID}
|
||||
has, err := baseTask.CanUpdate(a)
|
||||
has, err := baseTask.CanUpdate(s, a)
|
||||
if err != nil || !has {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// We explicitly don't check if the two tasks are on the same list.
|
||||
otherTask := &Task{ID: rel.OtherTaskID}
|
||||
has, _, err = otherTask.CanRead(a)
|
||||
has, _, err = otherTask.CanRead(s, a)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
|
|
@ -28,13 +28,17 @@ import (
|
|||
func TestTaskRelation_Create(t *testing.T) {
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
rel := TaskRelation{
|
||||
TaskID: 1,
|
||||
OtherTaskID: 2,
|
||||
RelationKind: RelationKindSubtask,
|
||||
}
|
||||
err := rel.Create(&user.User{ID: 1})
|
||||
err := rel.Create(s, &user.User{ID: 1})
|
||||
assert.NoError(t, err)
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
db.AssertExists(t, "task_relations", map[string]interface{}{
|
||||
"task_id": 1,
|
||||
|
@ -45,13 +49,17 @@ func TestTaskRelation_Create(t *testing.T) {
|
|||
})
|
||||
t.Run("Two Tasks In Different Lists", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
rel := TaskRelation{
|
||||
TaskID: 1,
|
||||
OtherTaskID: 13,
|
||||
RelationKind: RelationKindSubtask,
|
||||
}
|
||||
err := rel.Create(&user.User{ID: 1})
|
||||
err := rel.Create(s, &user.User{ID: 1})
|
||||
assert.NoError(t, err)
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
db.AssertExists(t, "task_relations", map[string]interface{}{
|
||||
"task_id": 1,
|
||||
|
@ -62,24 +70,28 @@ func TestTaskRelation_Create(t *testing.T) {
|
|||
})
|
||||
t.Run("Already Existing", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
rel := TaskRelation{
|
||||
TaskID: 1,
|
||||
OtherTaskID: 29,
|
||||
RelationKind: RelationKindSubtask,
|
||||
}
|
||||
err := rel.Create(&user.User{ID: 1})
|
||||
err := rel.Create(s, &user.User{ID: 1})
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrRelationAlreadyExists(err))
|
||||
})
|
||||
t.Run("Same Task", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
rel := TaskRelation{
|
||||
TaskID: 1,
|
||||
OtherTaskID: 1,
|
||||
}
|
||||
err := rel.Create(&user.User{ID: 1})
|
||||
err := rel.Create(s, &user.User{ID: 1})
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrRelationTasksCannotBeTheSame(err))
|
||||
})
|
||||
|
@ -88,13 +100,17 @@ func TestTaskRelation_Create(t *testing.T) {
|
|||
func TestTaskRelation_Delete(t *testing.T) {
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
rel := TaskRelation{
|
||||
TaskID: 1,
|
||||
OtherTaskID: 29,
|
||||
RelationKind: RelationKindSubtask,
|
||||
}
|
||||
err := rel.Delete()
|
||||
err := rel.Delete(s)
|
||||
assert.NoError(t, err)
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
db.AssertMissing(t, "task_relations", map[string]interface{}{
|
||||
"task_id": 1,
|
||||
|
@ -104,13 +120,15 @@ func TestTaskRelation_Delete(t *testing.T) {
|
|||
})
|
||||
t.Run("Not existing", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
rel := TaskRelation{
|
||||
TaskID: 9999,
|
||||
OtherTaskID: 3,
|
||||
RelationKind: RelationKindSubtask,
|
||||
}
|
||||
err := rel.Delete()
|
||||
err := rel.Delete(s)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrRelationDoesNotExist(err))
|
||||
})
|
||||
|
@ -119,86 +137,100 @@ func TestTaskRelation_Delete(t *testing.T) {
|
|||
func TestTaskRelation_CanCreate(t *testing.T) {
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
rel := TaskRelation{
|
||||
TaskID: 1,
|
||||
OtherTaskID: 2,
|
||||
RelationKind: RelationKindSubtask,
|
||||
}
|
||||
can, err := rel.CanCreate(&user.User{ID: 1})
|
||||
can, err := rel.CanCreate(s, &user.User{ID: 1})
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, can)
|
||||
})
|
||||
t.Run("Two tasks on different lists", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
rel := TaskRelation{
|
||||
TaskID: 1,
|
||||
OtherTaskID: 13,
|
||||
RelationKind: RelationKindSubtask,
|
||||
}
|
||||
can, err := rel.CanCreate(&user.User{ID: 1})
|
||||
can, err := rel.CanCreate(s, &user.User{ID: 1})
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, can)
|
||||
})
|
||||
t.Run("No update rights on base task", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
rel := TaskRelation{
|
||||
TaskID: 14,
|
||||
OtherTaskID: 1,
|
||||
RelationKind: RelationKindSubtask,
|
||||
}
|
||||
can, err := rel.CanCreate(&user.User{ID: 1})
|
||||
can, err := rel.CanCreate(s, &user.User{ID: 1})
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, can)
|
||||
})
|
||||
t.Run("No update rights on base task, but read rights", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
rel := TaskRelation{
|
||||
TaskID: 15,
|
||||
OtherTaskID: 1,
|
||||
RelationKind: RelationKindSubtask,
|
||||
}
|
||||
can, err := rel.CanCreate(&user.User{ID: 1})
|
||||
can, err := rel.CanCreate(s, &user.User{ID: 1})
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, can)
|
||||
})
|
||||
t.Run("No read rights on other task", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
rel := TaskRelation{
|
||||
TaskID: 1,
|
||||
OtherTaskID: 14,
|
||||
RelationKind: RelationKindSubtask,
|
||||
}
|
||||
can, err := rel.CanCreate(&user.User{ID: 1})
|
||||
can, err := rel.CanCreate(s, &user.User{ID: 1})
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, can)
|
||||
})
|
||||
t.Run("Nonexisting base task", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
rel := TaskRelation{
|
||||
TaskID: 999999,
|
||||
OtherTaskID: 1,
|
||||
RelationKind: RelationKindSubtask,
|
||||
}
|
||||
can, err := rel.CanCreate(&user.User{ID: 1})
|
||||
can, err := rel.CanCreate(s, &user.User{ID: 1})
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTaskDoesNotExist(err))
|
||||
assert.False(t, can)
|
||||
})
|
||||
t.Run("Nonexisting other task", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
rel := TaskRelation{
|
||||
TaskID: 1,
|
||||
OtherTaskID: 999999,
|
||||
RelationKind: RelationKindSubtask,
|
||||
}
|
||||
can, err := rel.CanCreate(&user.User{ID: 1})
|
||||
can, err := rel.CanCreate(s, &user.User{ID: 1})
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTaskDoesNotExist(err))
|
||||
assert.False(t, can)
|
||||
|
|
|
@ -19,6 +19,9 @@ package models
|
|||
import (
|
||||
"time"
|
||||
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
"xorm.io/xorm"
|
||||
|
||||
"code.vikunja.io/api/pkg/config"
|
||||
"code.vikunja.io/api/pkg/cron"
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
|
@ -44,10 +47,10 @@ type taskUser struct {
|
|||
User *user.User `xorm:"extends"`
|
||||
}
|
||||
|
||||
func getTaskUsersForTasks(taskIDs []int64) (taskUsers []*taskUser, err error) {
|
||||
func getTaskUsersForTasks(s *xorm.Session, taskIDs []int64) (taskUsers []*taskUser, err error) {
|
||||
// Get all creators of tasks
|
||||
creators := make(map[int64]*user.User, len(taskIDs))
|
||||
err = x.
|
||||
err = s.
|
||||
Select("users.id, users.username, users.email, users.name").
|
||||
Join("LEFT", "tasks", "tasks.created_by_id = users.id").
|
||||
In("tasks.id", taskIDs).
|
||||
|
@ -58,13 +61,13 @@ func getTaskUsersForTasks(taskIDs []int64) (taskUsers []*taskUser, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
assignees, err := getRawTaskAssigneesForTasks(taskIDs)
|
||||
assignees, err := getRawTaskAssigneesForTasks(s, taskIDs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
taskMap := make(map[int64]*Task, len(taskIDs))
|
||||
err = x.In("id", taskIDs).Find(&taskMap)
|
||||
err = s.In("id", taskIDs).Find(&taskMap)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -106,6 +109,8 @@ func RegisterReminderCron() {
|
|||
|
||||
log.Debugf("[Task Reminder Cron] Timezone is %s", tz)
|
||||
|
||||
s := db.NewSession()
|
||||
|
||||
err := cron.Schedule("* * * * *", func() {
|
||||
// By default, time.Now() includes nanoseconds which we don't save. That results in getting the wrong dates,
|
||||
// so we make sure the time we use to get the reminders don't contain nanoseconds.
|
||||
|
@ -116,7 +121,7 @@ func RegisterReminderCron() {
|
|||
log.Debugf("[Task Reminder Cron] Looking for reminders between %s and %s to send...", now, nextMinute)
|
||||
|
||||
reminders := []*TaskReminder{}
|
||||
err := x.
|
||||
err := s.
|
||||
Where("reminder >= ? and reminder < ?", now.Format(dbFormat), nextMinute.Format(dbFormat)).
|
||||
Find(&reminders)
|
||||
if err != nil {
|
||||
|
@ -136,7 +141,7 @@ func RegisterReminderCron() {
|
|||
taskIDs = append(taskIDs, r.TaskID)
|
||||
}
|
||||
|
||||
users, err := getTaskUsersForTasks(taskIDs)
|
||||
users, err := getTaskUsersForTasks(s, taskIDs)
|
||||
if err != nil {
|
||||
log.Errorf("[Task Reminder Cron] Could not get task users to send them reminders: %s", err)
|
||||
return
|
||||
|
|
|
@ -22,6 +22,8 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
|
||||
"code.vikunja.io/api/pkg/config"
|
||||
"code.vikunja.io/api/pkg/metrics"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
|
@ -153,7 +155,7 @@ type taskOptions struct {
|
|||
// @Success 200 {array} models.Task "The tasks"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/all [get]
|
||||
func (t *Task) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, totalItems int64, err error) {
|
||||
func (t *Task) ReadAll(s *xorm.Session, a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, totalItems int64, err error) {
|
||||
return nil, 0, 0, nil
|
||||
}
|
||||
|
||||
|
@ -209,7 +211,7 @@ func getFilterCondForSeparateTable(table string, concat taskFilterConcatinator,
|
|||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
func getRawTasksForLists(lists []*List, a web.Auth, opts *taskOptions) (tasks []*Task, resultCount int, totalItems int64, err error) {
|
||||
func getRawTasksForLists(s *xorm.Session, lists []*List, a web.Auth, opts *taskOptions) (tasks []*Task, resultCount int, totalItems int64, err error) {
|
||||
|
||||
// If the user does not have any lists, don't try to get any tasks
|
||||
if len(lists) == 0 {
|
||||
|
@ -253,7 +255,7 @@ func getRawTasksForLists(lists []*List, a web.Auth, opts *taskOptions) (tasks []
|
|||
|
||||
// Postgres sorts by default entries with null values after ones with values.
|
||||
// To make that consistent with the sort order we have and other dbms, we're adding a separate clause here.
|
||||
if x.Dialect().URI().DBType == schemas.POSTGRES {
|
||||
if db.Type() == schemas.POSTGRES {
|
||||
if param.orderBy == orderAscending {
|
||||
orderby += " NULLS FIRST"
|
||||
}
|
||||
|
@ -324,9 +326,7 @@ func getRawTasksForLists(lists []*List, a web.Auth, opts *taskOptions) (tasks []
|
|||
}
|
||||
|
||||
// Then return all tasks for that lists
|
||||
query := x.NewSession().
|
||||
OrderBy(orderby)
|
||||
queryCount := x.NewSession()
|
||||
var where builder.Cond
|
||||
|
||||
if len(opts.search) > 0 {
|
||||
// Postgres' is case sensitive by default.
|
||||
|
@ -335,11 +335,9 @@ func getRawTasksForLists(lists []*List, a web.Auth, opts *taskOptions) (tasks []
|
|||
// See https://stackoverflow.com/q/7005302/10924593
|
||||
// Seems okay to use that now, we may need to find a better solution overall in the future.
|
||||
if config.DatabaseType.GetString() == "postgres" {
|
||||
query = query.Where("title ILIKE ?", "%"+opts.search+"%")
|
||||
queryCount = queryCount.Where("title ILIKE ?", "%"+opts.search+"%")
|
||||
where = builder.Expr("title ILIKE ?", "%"+opts.search+"%")
|
||||
} else {
|
||||
query = query.Where("title LIKE ?", "%"+opts.search+"%")
|
||||
queryCount = queryCount.Where("title LIKE ?", "%"+opts.search+"%")
|
||||
where = &builder.Like{"title", "%" + opts.search + "%"}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -352,10 +350,13 @@ func getRawTasksForLists(lists []*List, a web.Auth, opts *taskOptions) (tasks []
|
|||
|
||||
if hasFavoriteLists {
|
||||
// Make sure users can only see their favorites
|
||||
userLists, _, _, err := getRawListsForUser(&listOptions{
|
||||
user: &user.User{ID: a.GetID()},
|
||||
page: -1,
|
||||
})
|
||||
userLists, _, _, err := getRawListsForUser(
|
||||
s,
|
||||
&listOptions{
|
||||
user: &user.User{ID: a.GetID()},
|
||||
page: -1,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
@ -399,32 +400,31 @@ func getRawTasksForLists(lists []*List, a web.Auth, opts *taskOptions) (tasks []
|
|||
filters = append(filters, cond)
|
||||
}
|
||||
|
||||
query = query.Where(listCond)
|
||||
queryCount = queryCount.Where(listCond)
|
||||
|
||||
var filterCond builder.Cond
|
||||
if len(filters) > 0 {
|
||||
if opts.filterConcat == filterConcatOr {
|
||||
query = query.Where(builder.Or(filters...))
|
||||
queryCount = queryCount.Where(builder.Or(filters...))
|
||||
filterCond = builder.Or(filters...)
|
||||
}
|
||||
if opts.filterConcat == filterConcatAnd {
|
||||
query = query.Where(builder.And(filters...))
|
||||
queryCount = queryCount.Where(builder.And(filters...))
|
||||
filterCond = builder.And(filters...)
|
||||
}
|
||||
}
|
||||
|
||||
limit, start := getLimitFromPageIndex(opts.page, opts.perPage)
|
||||
cond := builder.And(listCond, where, filterCond)
|
||||
|
||||
query := s.Where(cond)
|
||||
if limit > 0 {
|
||||
query = query.Limit(limit, start)
|
||||
}
|
||||
|
||||
tasks = []*Task{}
|
||||
err = query.Find(&tasks)
|
||||
err = query.OrderBy(orderby).Find(&tasks)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
queryCount := s.Where(cond)
|
||||
totalItems, err = queryCount.
|
||||
Count(&Task{})
|
||||
if err != nil {
|
||||
|
@ -434,9 +434,9 @@ func getRawTasksForLists(lists []*List, a web.Auth, opts *taskOptions) (tasks []
|
|||
return tasks, len(tasks), totalItems, nil
|
||||
}
|
||||
|
||||
func getTasksForLists(lists []*List, a web.Auth, opts *taskOptions) (tasks []*Task, resultCount int, totalItems int64, err error) {
|
||||
func getTasksForLists(s *xorm.Session, lists []*List, a web.Auth, opts *taskOptions) (tasks []*Task, resultCount int, totalItems int64, err error) {
|
||||
|
||||
tasks, resultCount, totalItems, err = getRawTasksForLists(lists, a, opts)
|
||||
tasks, resultCount, totalItems, err = getRawTasksForLists(s, lists, a, opts)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
@ -446,7 +446,7 @@ func getTasksForLists(lists []*List, a web.Auth, opts *taskOptions) (tasks []*Ta
|
|||
taskMap[t.ID] = t
|
||||
}
|
||||
|
||||
err = addMoreInfoToTasks(taskMap)
|
||||
err = addMoreInfoToTasks(s, taskMap)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
@ -455,18 +455,18 @@ func getTasksForLists(lists []*List, a web.Auth, opts *taskOptions) (tasks []*Ta
|
|||
}
|
||||
|
||||
// GetTaskByIDSimple returns a raw task without extra data by the task ID
|
||||
func GetTaskByIDSimple(taskID int64) (task Task, err error) {
|
||||
func GetTaskByIDSimple(s *xorm.Session, taskID int64) (task Task, err error) {
|
||||
if taskID < 1 {
|
||||
return Task{}, ErrTaskDoesNotExist{taskID}
|
||||
}
|
||||
|
||||
return GetTaskSimple(&Task{ID: taskID})
|
||||
return GetTaskSimple(s, &Task{ID: taskID})
|
||||
}
|
||||
|
||||
// GetTaskSimple returns a raw task without extra data
|
||||
func GetTaskSimple(t *Task) (task Task, err error) {
|
||||
func GetTaskSimple(s *xorm.Session, t *Task) (task Task, err error) {
|
||||
task = *t
|
||||
exists, err := x.Get(&task)
|
||||
exists, err := s.Get(&task)
|
||||
if err != nil {
|
||||
return Task{}, err
|
||||
}
|
||||
|
@ -478,14 +478,14 @@ func GetTaskSimple(t *Task) (task Task, err error) {
|
|||
}
|
||||
|
||||
// GetTasksByIDs returns all tasks for a list of ids
|
||||
func (bt *BulkTask) GetTasksByIDs() (err error) {
|
||||
func (bt *BulkTask) GetTasksByIDs(s *xorm.Session) (err error) {
|
||||
for _, id := range bt.IDs {
|
||||
if id < 1 {
|
||||
return ErrTaskDoesNotExist{id}
|
||||
}
|
||||
}
|
||||
|
||||
err = x.In("id", bt.IDs).Find(&bt.Tasks)
|
||||
err = s.In("id", bt.IDs).Find(&bt.Tasks)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -494,9 +494,9 @@ func (bt *BulkTask) GetTasksByIDs() (err error) {
|
|||
}
|
||||
|
||||
// GetTasksByUIDs gets all tasks from a bunch of uids
|
||||
func GetTasksByUIDs(uids []string) (tasks []*Task, err error) {
|
||||
func GetTasksByUIDs(s *xorm.Session, uids []string) (tasks []*Task, err error) {
|
||||
tasks = []*Task{}
|
||||
err = x.In("uid", uids).Find(&tasks)
|
||||
err = s.In("uid", uids).Find(&tasks)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -506,13 +506,13 @@ func GetTasksByUIDs(uids []string) (tasks []*Task, err error) {
|
|||
taskMap[t.ID] = t
|
||||
}
|
||||
|
||||
err = addMoreInfoToTasks(taskMap)
|
||||
err = addMoreInfoToTasks(s, taskMap)
|
||||
return
|
||||
}
|
||||
|
||||
func getRemindersForTasks(taskIDs []int64) (reminders []*TaskReminder, err error) {
|
||||
func getRemindersForTasks(s *xorm.Session, taskIDs []int64) (reminders []*TaskReminder, err error) {
|
||||
reminders = []*TaskReminder{}
|
||||
err = x.In("task_id", taskIDs).Find(&reminders)
|
||||
err = s.In("task_id", taskIDs).Find(&reminders)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -521,8 +521,8 @@ func (t *Task) setIdentifier(list *List) {
|
|||
}
|
||||
|
||||
// Get all assignees
|
||||
func addAssigneesToTasks(taskIDs []int64, taskMap map[int64]*Task) (err error) {
|
||||
taskAssignees, err := getRawTaskAssigneesForTasks(taskIDs)
|
||||
func addAssigneesToTasks(s *xorm.Session, taskIDs []int64, taskMap map[int64]*Task) (err error) {
|
||||
taskAssignees, err := getRawTaskAssigneesForTasks(s, taskIDs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -538,8 +538,8 @@ func addAssigneesToTasks(taskIDs []int64, taskMap map[int64]*Task) (err error) {
|
|||
}
|
||||
|
||||
// Get all labels for all the tasks
|
||||
func addLabelsToTasks(taskIDs []int64, taskMap map[int64]*Task) (err error) {
|
||||
labels, _, _, err := getLabelsByTaskIDs(&LabelByTaskIDsOptions{
|
||||
func addLabelsToTasks(s *xorm.Session, taskIDs []int64, taskMap map[int64]*Task) (err error) {
|
||||
labels, _, _, err := getLabelsByTaskIDs(s, &LabelByTaskIDsOptions{
|
||||
TaskIDs: taskIDs,
|
||||
Page: -1,
|
||||
})
|
||||
|
@ -556,8 +556,8 @@ func addLabelsToTasks(taskIDs []int64, taskMap map[int64]*Task) (err error) {
|
|||
}
|
||||
|
||||
// Get task attachments
|
||||
func addAttachmentsToTasks(taskIDs []int64, taskMap map[int64]*Task) (err error) {
|
||||
attachments, err := getTaskAttachmentsByTaskIDs(taskIDs)
|
||||
func addAttachmentsToTasks(s *xorm.Session, taskIDs []int64, taskMap map[int64]*Task) (err error) {
|
||||
attachments, err := getTaskAttachmentsByTaskIDs(s, taskIDs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -568,11 +568,11 @@ func addAttachmentsToTasks(taskIDs []int64, taskMap map[int64]*Task) (err error)
|
|||
return
|
||||
}
|
||||
|
||||
func getTaskReminderMap(taskIDs []int64) (taskReminders map[int64][]time.Time, err error) {
|
||||
func getTaskReminderMap(s *xorm.Session, taskIDs []int64) (taskReminders map[int64][]time.Time, err error) {
|
||||
taskReminders = make(map[int64][]time.Time)
|
||||
|
||||
// Get all reminders and put them in a map to have it easier later
|
||||
reminders, err := getRemindersForTasks(taskIDs)
|
||||
reminders, err := getRemindersForTasks(s, taskIDs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -584,9 +584,9 @@ func getTaskReminderMap(taskIDs []int64) (taskReminders map[int64][]time.Time, e
|
|||
return
|
||||
}
|
||||
|
||||
func addRelatedTasksToTasks(taskIDs []int64, taskMap map[int64]*Task) (err error) {
|
||||
func addRelatedTasksToTasks(s *xorm.Session, taskIDs []int64, taskMap map[int64]*Task) (err error) {
|
||||
relatedTasks := []*TaskRelation{}
|
||||
err = x.In("task_id", taskIDs).Find(&relatedTasks)
|
||||
err = s.In("task_id", taskIDs).Find(&relatedTasks)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -597,7 +597,7 @@ func addRelatedTasksToTasks(taskIDs []int64, taskMap map[int64]*Task) (err error
|
|||
relatedTaskIDs = append(relatedTaskIDs, rt.OtherTaskID)
|
||||
}
|
||||
fullRelatedTasks := make(map[int64]*Task)
|
||||
err = x.In("id", relatedTaskIDs).Find(&fullRelatedTasks)
|
||||
err = s.In("id", relatedTaskIDs).Find(&fullRelatedTasks)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -614,7 +614,7 @@ func addRelatedTasksToTasks(taskIDs []int64, taskMap map[int64]*Task) (err error
|
|||
|
||||
// This function takes a map with pointers and returns a slice with pointers to tasks
|
||||
// It adds more stuff like assignees/labels/etc to a bunch of tasks
|
||||
func addMoreInfoToTasks(taskMap map[int64]*Task) (err error) {
|
||||
func addMoreInfoToTasks(s *xorm.Session, taskMap map[int64]*Task) (err error) {
|
||||
|
||||
// No need to iterate over users and stuff if the list doesn't have tasks
|
||||
if len(taskMap) == 0 {
|
||||
|
@ -631,33 +631,33 @@ func addMoreInfoToTasks(taskMap map[int64]*Task) (err error) {
|
|||
listIDs = append(listIDs, i.ListID)
|
||||
}
|
||||
|
||||
err = addAssigneesToTasks(taskIDs, taskMap)
|
||||
err = addAssigneesToTasks(s, taskIDs, taskMap)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = addLabelsToTasks(taskIDs, taskMap)
|
||||
err = addLabelsToTasks(s, taskIDs, taskMap)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = addAttachmentsToTasks(taskIDs, taskMap)
|
||||
err = addAttachmentsToTasks(s, taskIDs, taskMap)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
users, err := user.GetUsersByIDs(userIDs)
|
||||
users, err := user.GetUsersByIDs(s, userIDs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
taskReminders, err := getTaskReminderMap(taskIDs)
|
||||
taskReminders, err := getTaskReminderMap(s, taskIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get all identifiers
|
||||
lists, err := GetListsByIDs(listIDs)
|
||||
lists, err := GetListsByIDs(s, listIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -679,7 +679,7 @@ func addMoreInfoToTasks(taskMap map[int64]*Task) (err error) {
|
|||
}
|
||||
|
||||
// Get all related tasks
|
||||
err = addRelatedTasksToTasks(taskIDs, taskMap)
|
||||
err = addRelatedTasksToTasks(s, taskIDs, taskMap)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -739,14 +739,8 @@ func checkBucketLimit(s *xorm.Session, t *Task, bucket *Bucket) (err error) {
|
|||
// @Failure 403 {object} web.HTTPError "The user does not have access to the list"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{id} [put]
|
||||
func (t *Task) Create(a web.Auth) (err error) {
|
||||
s := x.NewSession()
|
||||
err = createTask(s, t, a, true)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return err
|
||||
}
|
||||
return s.Commit()
|
||||
func (t *Task) Create(s *xorm.Session, a web.Auth) (err error) {
|
||||
return createTask(s, t, a, true)
|
||||
}
|
||||
|
||||
func createTask(s *xorm.Session, t *Task, a web.Auth, updateAssignees bool) (err error) {
|
||||
|
@ -759,16 +753,16 @@ func createTask(s *xorm.Session, t *Task, a web.Auth, updateAssignees bool) (err
|
|||
}
|
||||
|
||||
// Check if the list exists
|
||||
l := &List{ID: t.ListID}
|
||||
if err = l.getSimpleByID(s); err != nil {
|
||||
return
|
||||
l, err := GetListSimpleByID(s, t.ListID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, is := a.(*LinkSharing); is {
|
||||
// A negative user id indicates user share links
|
||||
t.CreatedByID = a.GetID() * -1
|
||||
} else {
|
||||
u, err := user.GetUserByID(a.GetID())
|
||||
u, err := user.GetUserByID(s, a.GetID())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -834,7 +828,7 @@ func createTask(s *xorm.Session, t *Task, a web.Auth, updateAssignees bool) (err
|
|||
|
||||
t.setIdentifier(l)
|
||||
|
||||
err = updateListLastUpdatedS(s, &List{ID: t.ListID})
|
||||
err = updateListLastUpdated(s, &List{ID: t.ListID})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -853,21 +847,17 @@ func createTask(s *xorm.Session, t *Task, a web.Auth, updateAssignees bool) (err
|
|||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{id} [post]
|
||||
//nolint:gocyclo
|
||||
func (t *Task) Update() (err error) {
|
||||
|
||||
s := x.NewSession()
|
||||
func (t *Task) Update(s *xorm.Session) (err error) {
|
||||
|
||||
// Check if the task exists and get the old values
|
||||
ot, err := GetTaskByIDSimple(t.ID)
|
||||
ot, err := GetTaskByIDSimple(s, t.ID)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return
|
||||
}
|
||||
|
||||
// Get the reminders
|
||||
reminders, err := getRemindersForTasks([]int64{t.ID})
|
||||
reminders, err := getRemindersForTasks(s, []int64{t.ID})
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -881,20 +871,17 @@ func (t *Task) Update() (err error) {
|
|||
|
||||
// Update the assignees
|
||||
if err := ot.updateTaskAssignees(s, t.Assignees); err != nil {
|
||||
_ = s.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
// Update the reminders
|
||||
if err := ot.updateReminders(s, t.Reminders); err != nil {
|
||||
_ = s.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
// If there is a bucket set, make sure they belong to the same list as the task
|
||||
err = checkBucketAndTaskBelongToSameList(s, &ot, t.BucketID)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -923,7 +910,6 @@ func (t *Task) Update() (err error) {
|
|||
if t.BucketID == 0 || (t.ListID != 0 && ot.ListID != t.ListID) {
|
||||
bucket, err = getDefaultBucket(s, t.ListID)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return err
|
||||
}
|
||||
t.BucketID = bucket.ID
|
||||
|
@ -934,7 +920,6 @@ func (t *Task) Update() (err error) {
|
|||
latestTask := &Task{}
|
||||
_, err = s.Where("list_id = ?", t.ListID).OrderBy("id desc").Get(latestTask)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -946,7 +931,6 @@ func (t *Task) Update() (err error) {
|
|||
// Only check the bucket limit if the task is being moved between buckets, allow reordering the task within a bucket
|
||||
if t.BucketID != ot.BucketID {
|
||||
if err := checkBucketLimit(s, t, bucket); err != nil {
|
||||
_ = s.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -972,7 +956,6 @@ func (t *Task) Update() (err error) {
|
|||
// Which is why we merge the actual task struct with the one we got from the db
|
||||
// The user struct overrides values in the actual one.
|
||||
if err := mergo.Merge(&ot, t, mergo.WithOverride); err != nil {
|
||||
_ = s.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -1034,7 +1017,6 @@ func (t *Task) Update() (err error) {
|
|||
Update(ot)
|
||||
*t = ot
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return err
|
||||
}
|
||||
// Get the task updated timestamp in a new struct - if we'd just try to put it into t which we already have, it
|
||||
|
@ -1042,17 +1024,11 @@ func (t *Task) Update() (err error) {
|
|||
nt := &Task{}
|
||||
_, err = s.ID(t.ID).Get(nt)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return err
|
||||
}
|
||||
t.Updated = nt.Updated
|
||||
|
||||
err = updateListLastUpdatedS(s, &List{ID: t.ListID})
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return err
|
||||
}
|
||||
return s.Commit()
|
||||
return updateListLastUpdated(s, &List{ID: t.ListID})
|
||||
}
|
||||
|
||||
// This helper function updates the reminders, doneAt, start and end dates of the *old* task
|
||||
|
@ -1174,7 +1150,7 @@ func (t *Task) updateReminders(s *xorm.Session, reminders []time.Time) (err erro
|
|||
t.Reminders = nil
|
||||
}
|
||||
|
||||
err = updateListLastUpdatedS(s, &List{ID: t.ListID})
|
||||
err = updateListLastUpdated(s, &List{ID: t.ListID})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1190,20 +1166,20 @@ func (t *Task) updateReminders(s *xorm.Session, reminders []time.Time) (err erro
|
|||
// @Failure 403 {object} web.HTTPError "The user does not have access to the list"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{id} [delete]
|
||||
func (t *Task) Delete() (err error) {
|
||||
func (t *Task) Delete(s *xorm.Session) (err error) {
|
||||
|
||||
if _, err = x.ID(t.ID).Delete(Task{}); err != nil {
|
||||
if _, err = s.ID(t.ID).Delete(Task{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete assignees
|
||||
if _, err = x.Where("task_id = ?", t.ID).Delete(TaskAssginee{}); err != nil {
|
||||
if _, err = s.Where("task_id = ?", t.ID).Delete(TaskAssginee{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
metrics.UpdateCount(-1, metrics.TaskCountKey)
|
||||
|
||||
err = updateListLastUpdated(&List{ID: t.ListID})
|
||||
err = updateListLastUpdated(s, &List{ID: t.ListID})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1219,16 +1195,16 @@ func (t *Task) Delete() (err error) {
|
|||
// @Failure 404 {object} models.Message "Task not found"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /tasks/{ID} [get]
|
||||
func (t *Task) ReadOne() (err error) {
|
||||
func (t *Task) ReadOne(s *xorm.Session) (err error) {
|
||||
|
||||
taskMap := make(map[int64]*Task, 1)
|
||||
taskMap[t.ID] = &Task{}
|
||||
*taskMap[t.ID], err = GetTaskByIDSimple(t.ID)
|
||||
*taskMap[t.ID], err = GetTaskByIDSimple(s, t.ID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = addMoreInfoToTasks(taskMap)
|
||||
err = addMoreInfoToTasks(s, taskMap)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -18,47 +18,48 @@ package models
|
|||
|
||||
import (
|
||||
"code.vikunja.io/web"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// CanDelete checks if the user can delete an task
|
||||
func (t *Task) CanDelete(a web.Auth) (bool, error) {
|
||||
return t.canDoTask(a)
|
||||
func (t *Task) CanDelete(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
return t.canDoTask(s, a)
|
||||
}
|
||||
|
||||
// CanUpdate determines if a user has the right to update a list task
|
||||
func (t *Task) CanUpdate(a web.Auth) (bool, error) {
|
||||
return t.canDoTask(a)
|
||||
func (t *Task) CanUpdate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
return t.canDoTask(s, a)
|
||||
}
|
||||
|
||||
// CanCreate determines if a user has the right to create a list task
|
||||
func (t *Task) CanCreate(a web.Auth) (bool, error) {
|
||||
func (t *Task) CanCreate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
// A user can do a task if he has write acces to its list
|
||||
l := &List{ID: t.ListID}
|
||||
return l.CanWrite(a)
|
||||
return l.CanWrite(s, a)
|
||||
}
|
||||
|
||||
// CanRead determines if a user can read a task
|
||||
func (t *Task) CanRead(a web.Auth) (canRead bool, maxRight int, err error) {
|
||||
func (t *Task) CanRead(s *xorm.Session, a web.Auth) (canRead bool, maxRight int, err error) {
|
||||
// Get the task, error out if it doesn't exist
|
||||
*t, err = GetTaskByIDSimple(t.ID)
|
||||
*t, err = GetTaskByIDSimple(s, t.ID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// A user can read a task if it has access to the list
|
||||
l := &List{ID: t.ListID}
|
||||
return l.CanRead(a)
|
||||
return l.CanRead(s, a)
|
||||
}
|
||||
|
||||
// CanWrite checks if a user has write access to a task
|
||||
func (t *Task) CanWrite(a web.Auth) (canWrite bool, err error) {
|
||||
return t.canDoTask(a)
|
||||
func (t *Task) CanWrite(s *xorm.Session, a web.Auth) (canWrite bool, err error) {
|
||||
return t.canDoTask(s, a)
|
||||
}
|
||||
|
||||
// Helper function to check if a user can do stuff on a list task
|
||||
func (t *Task) canDoTask(a web.Auth) (bool, error) {
|
||||
func (t *Task) canDoTask(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
// Get the task
|
||||
ot, err := GetTaskByIDSimple(t.ID)
|
||||
ot, err := GetTaskByIDSimple(s, t.ID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -66,7 +67,7 @@ func (t *Task) canDoTask(a web.Auth) (bool, error) {
|
|||
// Check if we're moving the task into a different list to check if the user has sufficient rights for that on the new list
|
||||
if t.ListID != 0 && t.ListID != ot.ListID {
|
||||
newList := &List{ID: t.ListID}
|
||||
can, err := newList.CanWrite(a)
|
||||
can, err := newList.CanWrite(s, a)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -77,5 +78,5 @@ func (t *Task) canDoTask(a web.Auth) (bool, error) {
|
|||
|
||||
// A user can do a task if it has write acces to its list
|
||||
l := &List{ID: ot.ListID}
|
||||
return l.CanWrite(a)
|
||||
return l.CanWrite(s, a)
|
||||
}
|
||||
|
|
|
@ -36,12 +36,15 @@ func TestTask_Create(t *testing.T) {
|
|||
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
task := &Task{
|
||||
Title: "Lorem",
|
||||
Description: "Lorem Ipsum Dolor",
|
||||
ListID: 1,
|
||||
}
|
||||
err := task.Create(usr)
|
||||
err := task.Create(s, usr)
|
||||
assert.NoError(t, err)
|
||||
// Assert getting a uid
|
||||
assert.NotEmpty(t, task.UID)
|
||||
|
@ -50,6 +53,9 @@ func TestTask_Create(t *testing.T) {
|
|||
assert.Equal(t, int64(18), task.Index)
|
||||
// Assert moving it into the default bucket
|
||||
assert.Equal(t, int64(1), task.BucketID)
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
|
||||
db.AssertExists(t, "tasks", map[string]interface{}{
|
||||
"id": task.ID,
|
||||
"title": "Lorem",
|
||||
|
@ -62,47 +68,59 @@ func TestTask_Create(t *testing.T) {
|
|||
})
|
||||
t.Run("empty title", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
task := &Task{
|
||||
Title: "",
|
||||
Description: "Lorem Ipsum Dolor",
|
||||
ListID: 1,
|
||||
}
|
||||
err := task.Create(usr)
|
||||
err := task.Create(s, usr)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTaskCannotBeEmpty(err))
|
||||
})
|
||||
t.Run("nonexistant list", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
task := &Task{
|
||||
Title: "Test",
|
||||
Description: "Lorem Ipsum Dolor",
|
||||
ListID: 9999999,
|
||||
}
|
||||
err := task.Create(usr)
|
||||
err := task.Create(s, usr)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrListDoesNotExist(err))
|
||||
})
|
||||
t.Run("noneixtant user", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
nUser := &user.User{ID: 99999999}
|
||||
task := &Task{
|
||||
Title: "Test",
|
||||
Description: "Lorem Ipsum Dolor",
|
||||
ListID: 1,
|
||||
}
|
||||
err := task.Create(nUser)
|
||||
err := task.Create(s, nUser)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, user.IsErrUserDoesNotExist(err))
|
||||
})
|
||||
t.Run("full bucket", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
task := &Task{
|
||||
Title: "Lorem",
|
||||
Description: "Lorem Ipsum Dolor",
|
||||
ListID: 1,
|
||||
BucketID: 2, // Bucket 2 already has 3 tasks and a limit of 3
|
||||
}
|
||||
err := task.Create(usr)
|
||||
err := task.Create(s, usr)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrBucketLimitExceeded(err))
|
||||
})
|
||||
|
@ -111,14 +129,20 @@ func TestTask_Create(t *testing.T) {
|
|||
func TestTask_Update(t *testing.T) {
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
task := &Task{
|
||||
ID: 1,
|
||||
Title: "test10000",
|
||||
Description: "Lorem Ipsum Dolor",
|
||||
ListID: 1,
|
||||
}
|
||||
err := task.Update()
|
||||
err := task.Update(s)
|
||||
assert.NoError(t, err)
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
|
||||
db.AssertExists(t, "tasks", map[string]interface{}{
|
||||
"id": 1,
|
||||
"title": "test10000",
|
||||
|
@ -128,18 +152,24 @@ func TestTask_Update(t *testing.T) {
|
|||
})
|
||||
t.Run("nonexistant task", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
task := &Task{
|
||||
ID: 9999999,
|
||||
Title: "test10000",
|
||||
Description: "Lorem Ipsum Dolor",
|
||||
ListID: 1,
|
||||
}
|
||||
err := task.Update()
|
||||
err := task.Update(s)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTaskDoesNotExist(err))
|
||||
})
|
||||
t.Run("full bucket", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
task := &Task{
|
||||
ID: 1,
|
||||
Title: "test10000",
|
||||
|
@ -147,12 +177,15 @@ func TestTask_Update(t *testing.T) {
|
|||
ListID: 1,
|
||||
BucketID: 2, // Bucket 2 already has 3 tasks and a limit of 3
|
||||
}
|
||||
err := task.Update()
|
||||
err := task.Update(s)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrBucketLimitExceeded(err))
|
||||
})
|
||||
t.Run("full bucket but not changing the bucket", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
task := &Task{
|
||||
ID: 4,
|
||||
Title: "test10000",
|
||||
|
@ -161,7 +194,7 @@ func TestTask_Update(t *testing.T) {
|
|||
ListID: 1,
|
||||
BucketID: 2, // Bucket 2 already has 3 tasks and a limit of 3
|
||||
}
|
||||
err := task.Update()
|
||||
err := task.Update(s)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
@ -169,11 +202,17 @@ func TestTask_Update(t *testing.T) {
|
|||
func TestTask_Delete(t *testing.T) {
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
task := &Task{
|
||||
ID: 1,
|
||||
}
|
||||
err := task.Delete()
|
||||
err := task.Delete(s)
|
||||
assert.NoError(t, err)
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
|
||||
db.AssertMissing(t, "tasks", map[string]interface{}{
|
||||
"id": 1,
|
||||
})
|
||||
|
@ -183,6 +222,9 @@ func TestTask_Delete(t *testing.T) {
|
|||
func TestUpdateDone(t *testing.T) {
|
||||
t.Run("marking a task as done", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
oldTask := &Task{Done: false}
|
||||
newTask := &Task{Done: true}
|
||||
updateDone(oldTask, newTask)
|
||||
|
@ -190,6 +232,9 @@ func TestUpdateDone(t *testing.T) {
|
|||
})
|
||||
t.Run("unmarking a task as done", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
oldTask := &Task{Done: true}
|
||||
newTask := &Task{Done: false}
|
||||
updateDone(oldTask, newTask)
|
||||
|
@ -397,15 +442,21 @@ func TestUpdateDone(t *testing.T) {
|
|||
func TestTask_ReadOne(t *testing.T) {
|
||||
t.Run("default", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
task := &Task{ID: 1}
|
||||
err := task.ReadOne()
|
||||
err := task.ReadOne(s)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "task #1", task.Title)
|
||||
})
|
||||
t.Run("nonexisting", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
task := &Task{ID: 99999}
|
||||
err := task.ReadOne()
|
||||
err := task.ReadOne(s)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTaskDoesNotExist(err))
|
||||
})
|
||||
|
|
|
@ -19,6 +19,7 @@ package models
|
|||
import (
|
||||
user2 "code.vikunja.io/api/pkg/user"
|
||||
"code.vikunja.io/web"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// Create implements the create method to assign a user to a team
|
||||
|
@ -35,23 +36,24 @@ import (
|
|||
// @Failure 403 {object} web.HTTPError "The user does not have access to the team"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /teams/{id}/members [put]
|
||||
func (tm *TeamMember) Create(a web.Auth) (err error) {
|
||||
func (tm *TeamMember) Create(s *xorm.Session, a web.Auth) (err error) {
|
||||
|
||||
// Check if the team extst
|
||||
_, err = GetTeamByID(tm.TeamID)
|
||||
_, err = GetTeamByID(s, tm.TeamID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the user exists
|
||||
user, err := user2.GetUserByUsername(tm.Username)
|
||||
user, err := user2.GetUserByUsername(s, tm.Username)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tm.UserID = user.ID
|
||||
|
||||
// Check if that user is already part of the team
|
||||
exists, err := x.Where("team_id = ? AND user_id = ?", tm.TeamID, tm.UserID).
|
||||
exists, err := s.
|
||||
Where("team_id = ? AND user_id = ?", tm.TeamID, tm.UserID).
|
||||
Get(&TeamMember{})
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -61,7 +63,7 @@ func (tm *TeamMember) Create(a web.Auth) (err error) {
|
|||
}
|
||||
|
||||
// Insert the user
|
||||
_, err = x.Insert(tm)
|
||||
_, err = s.Insert(tm)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -76,9 +78,9 @@ func (tm *TeamMember) Create(a web.Auth) (err error) {
|
|||
// @Success 200 {object} models.Message "The user was successfully removed from the team."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /teams/{id}/members/{userID} [delete]
|
||||
func (tm *TeamMember) Delete() (err error) {
|
||||
func (tm *TeamMember) Delete(s *xorm.Session) (err error) {
|
||||
|
||||
total, err := x.Where("team_id = ?", tm.TeamID).Count(&TeamMember{})
|
||||
total, err := s.Where("team_id = ?", tm.TeamID).Count(&TeamMember{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -87,13 +89,13 @@ func (tm *TeamMember) Delete() (err error) {
|
|||
}
|
||||
|
||||
// Find the numeric user id
|
||||
user, err := user2.GetUserByUsername(tm.Username)
|
||||
user, err := user2.GetUserByUsername(s, tm.Username)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tm.UserID = user.ID
|
||||
|
||||
_, err = x.Where("team_id = ? AND user_id = ?", tm.TeamID, tm.UserID).Delete(&TeamMember{})
|
||||
_, err = s.Where("team_id = ? AND user_id = ?", tm.TeamID, tm.UserID).Delete(&TeamMember{})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -108,9 +110,9 @@ func (tm *TeamMember) Delete() (err error) {
|
|||
// @Success 200 {object} models.Message "The member right was successfully changed."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /teams/{id}/members/{userID}/admin [post]
|
||||
func (tm *TeamMember) Update() (err error) {
|
||||
func (tm *TeamMember) Update(s *xorm.Session) (err error) {
|
||||
// Find the numeric user id
|
||||
user, err := user2.GetUserByUsername(tm.Username)
|
||||
user, err := user2.GetUserByUsername(s, tm.Username)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -118,7 +120,7 @@ func (tm *TeamMember) Update() (err error) {
|
|||
|
||||
// Get the full member object and change the admin right
|
||||
ttm := &TeamMember{}
|
||||
_, err = x.
|
||||
_, err = s.
|
||||
Where("team_id = ? AND user_id = ?", tm.TeamID, tm.UserID).
|
||||
Get(ttm)
|
||||
if err != nil {
|
||||
|
@ -127,7 +129,7 @@ func (tm *TeamMember) Update() (err error) {
|
|||
ttm.Admin = !ttm.Admin
|
||||
|
||||
// Do the update
|
||||
_, err = x.
|
||||
_, err = s.
|
||||
Where("team_id = ? AND user_id = ?", tm.TeamID, tm.UserID).
|
||||
Cols("admin").
|
||||
Update(ttm)
|
||||
|
|
|
@ -18,32 +18,34 @@ package models
|
|||
|
||||
import (
|
||||
"code.vikunja.io/web"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// CanCreate checks if the user can add a new tem member
|
||||
func (tm *TeamMember) CanCreate(a web.Auth) (bool, error) {
|
||||
return tm.IsAdmin(a)
|
||||
func (tm *TeamMember) CanCreate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
return tm.IsAdmin(s, a)
|
||||
}
|
||||
|
||||
// CanDelete checks if the user can delete a new team member
|
||||
func (tm *TeamMember) CanDelete(a web.Auth) (bool, error) {
|
||||
return tm.IsAdmin(a)
|
||||
func (tm *TeamMember) CanDelete(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
return tm.IsAdmin(s, a)
|
||||
}
|
||||
|
||||
// CanUpdate checks if the user can modify a team member's right
|
||||
func (tm *TeamMember) CanUpdate(a web.Auth) (bool, error) {
|
||||
return tm.IsAdmin(a)
|
||||
func (tm *TeamMember) CanUpdate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
return tm.IsAdmin(s, a)
|
||||
}
|
||||
|
||||
// IsAdmin checks if the user is team admin
|
||||
func (tm *TeamMember) IsAdmin(a web.Auth) (bool, error) {
|
||||
func (tm *TeamMember) IsAdmin(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
// Don't allow anything if we're dealing with a list share here
|
||||
if _, is := a.(*LinkSharing); is {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// A user can add a member to a team if he is admin of that team
|
||||
exists, err := x.Where("user_id = ? AND team_id = ? AND admin = ?", a.GetID(), tm.TeamID, true).
|
||||
exists, err := s.
|
||||
Where("user_id = ? AND team_id = ? AND admin = ?", a.GetID(), tm.TeamID, true).
|
||||
Get(&TeamMember{})
|
||||
return exists, err
|
||||
}
|
||||
|
|
|
@ -32,12 +32,18 @@ func TestTeamMember_Create(t *testing.T) {
|
|||
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
tm := &TeamMember{
|
||||
TeamID: 1,
|
||||
Username: "user3",
|
||||
}
|
||||
err := tm.Create(doer)
|
||||
err := tm.Create(s, doer)
|
||||
assert.NoError(t, err)
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
|
||||
db.AssertExists(t, "team_members", map[string]interface{}{
|
||||
"id": tm.ID,
|
||||
"team_id": 1,
|
||||
|
@ -46,31 +52,40 @@ func TestTeamMember_Create(t *testing.T) {
|
|||
})
|
||||
t.Run("already existing", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
tm := &TeamMember{
|
||||
TeamID: 1,
|
||||
Username: "user1",
|
||||
}
|
||||
err := tm.Create(doer)
|
||||
err := tm.Create(s, doer)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrUserIsMemberOfTeam(err))
|
||||
})
|
||||
t.Run("nonexisting user", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
tm := &TeamMember{
|
||||
TeamID: 1,
|
||||
Username: "nonexistinguser",
|
||||
}
|
||||
err := tm.Create(doer)
|
||||
err := tm.Create(s, doer)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, user.IsErrUserDoesNotExist(err))
|
||||
})
|
||||
t.Run("nonexisting team", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
tm := &TeamMember{
|
||||
TeamID: 9999999,
|
||||
Username: "user1",
|
||||
}
|
||||
err := tm.Create(doer)
|
||||
err := tm.Create(s, doer)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTeamDoesNotExist(err))
|
||||
})
|
||||
|
@ -79,12 +94,18 @@ func TestTeamMember_Create(t *testing.T) {
|
|||
func TestTeamMember_Delete(t *testing.T) {
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
tm := &TeamMember{
|
||||
TeamID: 1,
|
||||
Username: "user1",
|
||||
}
|
||||
err := tm.Delete()
|
||||
err := tm.Delete(s)
|
||||
assert.NoError(t, err)
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
|
||||
db.AssertMissing(t, "team_members", map[string]interface{}{
|
||||
"team_id": 1,
|
||||
"user_id": 1,
|
||||
|
@ -95,14 +116,20 @@ func TestTeamMember_Delete(t *testing.T) {
|
|||
func TestTeamMember_Update(t *testing.T) {
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
tm := &TeamMember{
|
||||
TeamID: 1,
|
||||
Username: "user1",
|
||||
Admin: true,
|
||||
}
|
||||
err := tm.Update()
|
||||
err := tm.Update(s)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, tm.Admin) // Since this endpoint toggles the right, we should get a false for admin back.
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
|
||||
db.AssertExists(t, "team_members", map[string]interface{}{
|
||||
"team_id": 1,
|
||||
"user_id": 1,
|
||||
|
@ -113,14 +140,20 @@ func TestTeamMember_Update(t *testing.T) {
|
|||
// should ignore what was passed.
|
||||
t.Run("explicitly false in payload", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
tm := &TeamMember{
|
||||
TeamID: 1,
|
||||
Username: "user1",
|
||||
Admin: true,
|
||||
}
|
||||
err := tm.Update()
|
||||
err := tm.Update(s)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, tm.Admin)
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
|
||||
db.AssertExists(t, "team_members", map[string]interface{}{
|
||||
"team_id": 1,
|
||||
"user_id": 1,
|
||||
|
|
|
@ -19,6 +19,8 @@ package models
|
|||
import (
|
||||
"time"
|
||||
|
||||
"xorm.io/xorm"
|
||||
|
||||
"code.vikunja.io/api/pkg/metrics"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
"code.vikunja.io/web"
|
||||
|
@ -54,10 +56,6 @@ func (Team) TableName() string {
|
|||
return "teams"
|
||||
}
|
||||
|
||||
// AfterLoad gets the created by user object
|
||||
func (t *Team) AfterLoad() {
|
||||
}
|
||||
|
||||
// TeamMember defines the relationship between a user and a team
|
||||
type TeamMember struct {
|
||||
// The unique, numeric id of this team member relation.
|
||||
|
@ -92,14 +90,14 @@ type TeamUser struct {
|
|||
}
|
||||
|
||||
// GetTeamByID gets a team by its ID
|
||||
func GetTeamByID(id int64) (team *Team, err error) {
|
||||
func GetTeamByID(s *xorm.Session, id int64) (team *Team, err error) {
|
||||
if id < 1 {
|
||||
return team, ErrTeamDoesNotExist{id}
|
||||
}
|
||||
|
||||
t := Team{}
|
||||
|
||||
exists, err := x.
|
||||
exists, err := s.
|
||||
Where("id = ?", id).
|
||||
Get(&t)
|
||||
if err != nil {
|
||||
|
@ -110,7 +108,7 @@ func GetTeamByID(id int64) (team *Team, err error) {
|
|||
}
|
||||
|
||||
teamSlice := []*Team{&t}
|
||||
err = addMoreInfoToTeams(teamSlice)
|
||||
err = addMoreInfoToTeams(s, teamSlice)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -120,7 +118,7 @@ func GetTeamByID(id int64) (team *Team, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func addMoreInfoToTeams(teams []*Team) (err error) {
|
||||
func addMoreInfoToTeams(s *xorm.Session, teams []*Team) (err error) {
|
||||
// Put the teams in a map to make assigning more info to it more efficient
|
||||
teamMap := make(map[int64]*Team, len(teams))
|
||||
var teamIDs []int64
|
||||
|
@ -133,7 +131,8 @@ func addMoreInfoToTeams(teams []*Team) (err error) {
|
|||
|
||||
// Get all owners and team members
|
||||
users := make(map[int64]*TeamUser)
|
||||
err = x.Select("*").
|
||||
err = s.
|
||||
Select("*").
|
||||
Table("users").
|
||||
Join("LEFT", "team_members", "team_members.user_id = users.id").
|
||||
Join("LEFT", "teams", "team_members.team_id = teams.id").
|
||||
|
@ -178,8 +177,8 @@ func addMoreInfoToTeams(teams []*Team) (err error) {
|
|||
// @Failure 403 {object} web.HTTPError "The user does not have access to the team"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /teams/{id} [get]
|
||||
func (t *Team) ReadOne() (err error) {
|
||||
team, err := GetTeamByID(t.ID)
|
||||
func (t *Team) ReadOne(s *xorm.Session) (err error) {
|
||||
team, err := GetTeamByID(s, t.ID)
|
||||
if team != nil {
|
||||
*t = *team
|
||||
}
|
||||
|
@ -199,7 +198,7 @@ func (t *Team) ReadOne() (err error) {
|
|||
// @Success 200 {array} models.Team "The teams."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /teams [get]
|
||||
func (t *Team) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
||||
func (t *Team) ReadAll(s *xorm.Session, a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
||||
if _, is := a.(*LinkSharing); is {
|
||||
return nil, 0, 0, ErrGenericForbidden{}
|
||||
}
|
||||
|
@ -207,7 +206,7 @@ func (t *Team) ReadAll(a web.Auth, search string, page int, perPage int) (result
|
|||
limit, start := getLimitFromPageIndex(page, perPage)
|
||||
|
||||
all := []*Team{}
|
||||
query := x.Select("teams.*").
|
||||
query := s.Select("teams.*").
|
||||
Table("teams").
|
||||
Join("INNER", "team_members", "team_members.team_id = teams.id").
|
||||
Where("team_members.user_id = ?", a.GetID()).
|
||||
|
@ -220,12 +219,12 @@ func (t *Team) ReadAll(a web.Auth, search string, page int, perPage int) (result
|
|||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
err = addMoreInfoToTeams(all)
|
||||
err = addMoreInfoToTeams(s, all)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
numberOfTotalItems, err = x.
|
||||
numberOfTotalItems, err = s.
|
||||
Table("teams").
|
||||
Join("INNER", "team_members", "team_members.team_id = teams.id").
|
||||
Where("team_members.user_id = ?", a.GetID()).
|
||||
|
@ -246,7 +245,7 @@ func (t *Team) ReadAll(a web.Auth, search string, page int, perPage int) (result
|
|||
// @Failure 400 {object} web.HTTPError "Invalid team object provided."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /teams [put]
|
||||
func (t *Team) Create(a web.Auth) (err error) {
|
||||
func (t *Team) Create(s *xorm.Session, a web.Auth) (err error) {
|
||||
doer, err := user.GetFromAuth(a)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -260,14 +259,14 @@ func (t *Team) Create(a web.Auth) (err error) {
|
|||
t.CreatedByID = doer.ID
|
||||
t.CreatedBy = doer
|
||||
|
||||
_, err = x.Insert(t)
|
||||
_, err = s.Insert(t)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Insert the current user as member and admin
|
||||
tm := TeamMember{TeamID: t.ID, Username: doer.Username, Admin: true}
|
||||
if err = tm.Create(doer); err != nil {
|
||||
if err = tm.Create(s, doer); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -286,28 +285,28 @@ func (t *Team) Create(a web.Auth) (err error) {
|
|||
// @Failure 400 {object} web.HTTPError "Invalid team object provided."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /teams/{id} [delete]
|
||||
func (t *Team) Delete() (err error) {
|
||||
func (t *Team) Delete(s *xorm.Session) (err error) {
|
||||
|
||||
// Delete the team
|
||||
_, err = x.ID(t.ID).Delete(&Team{})
|
||||
_, err = s.ID(t.ID).Delete(&Team{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Delete team members
|
||||
_, err = x.Where("team_id = ?", t.ID).Delete(&TeamMember{})
|
||||
_, err = s.Where("team_id = ?", t.ID).Delete(&TeamMember{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Delete team <-> namespace relations
|
||||
_, err = x.Where("team_id = ?", t.ID).Delete(&TeamNamespace{})
|
||||
_, err = s.Where("team_id = ?", t.ID).Delete(&TeamNamespace{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Delete team <-> lists relations
|
||||
_, err = x.Where("team_id = ?", t.ID).Delete(&TeamList{})
|
||||
_, err = s.Where("team_id = ?", t.ID).Delete(&TeamList{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -329,25 +328,25 @@ func (t *Team) Delete() (err error) {
|
|||
// @Failure 400 {object} web.HTTPError "Invalid team object provided."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /teams/{id} [post]
|
||||
func (t *Team) Update() (err error) {
|
||||
func (t *Team) Update(s *xorm.Session) (err error) {
|
||||
// Check if we have a name
|
||||
if t.Name == "" {
|
||||
return ErrTeamNameCannotBeEmpty{}
|
||||
}
|
||||
|
||||
// Check if the team exists
|
||||
_, err = GetTeamByID(t.ID)
|
||||
_, err = GetTeamByID(s, t.ID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = x.ID(t.ID).Update(t)
|
||||
_, err = s.ID(t.ID).Update(t)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Get the newly updated team
|
||||
team, err := GetTeamByID(t.ID)
|
||||
team, err := GetTeamByID(s, t.ID)
|
||||
if team != nil {
|
||||
*t = *team
|
||||
}
|
||||
|
|
|
@ -18,10 +18,11 @@ package models
|
|||
|
||||
import (
|
||||
"code.vikunja.io/web"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// CanCreate checks if the user can create a new team
|
||||
func (t *Team) CanCreate(a web.Auth) (bool, error) {
|
||||
func (t *Team) CanCreate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
if _, is := a.(*LinkSharing); is {
|
||||
return false, nil
|
||||
}
|
||||
|
@ -31,39 +32,40 @@ func (t *Team) CanCreate(a web.Auth) (bool, error) {
|
|||
}
|
||||
|
||||
// CanUpdate checks if the user can update a team
|
||||
func (t *Team) CanUpdate(a web.Auth) (bool, error) {
|
||||
return t.IsAdmin(a)
|
||||
func (t *Team) CanUpdate(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
return t.IsAdmin(s, a)
|
||||
}
|
||||
|
||||
// CanDelete checks if a user can delete a team
|
||||
func (t *Team) CanDelete(a web.Auth) (bool, error) {
|
||||
return t.IsAdmin(a)
|
||||
func (t *Team) CanDelete(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
return t.IsAdmin(s, a)
|
||||
}
|
||||
|
||||
// IsAdmin returns true when the user is admin of a team
|
||||
func (t *Team) IsAdmin(a web.Auth) (bool, error) {
|
||||
func (t *Team) IsAdmin(s *xorm.Session, a web.Auth) (bool, error) {
|
||||
// Don't do anything if we're deadling with a link share auth here
|
||||
if _, is := a.(*LinkSharing); is {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Check if the team exists to be able to return a proper error message if not
|
||||
_, err := GetTeamByID(t.ID)
|
||||
_, err := GetTeamByID(s, t.ID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return x.Where("team_id = ?", t.ID).
|
||||
return s.Where("team_id = ?", t.ID).
|
||||
And("user_id = ?", a.GetID()).
|
||||
And("admin = ?", true).
|
||||
Get(&TeamMember{})
|
||||
}
|
||||
|
||||
// CanRead returns true if the user has read access to the team
|
||||
func (t *Team) CanRead(a web.Auth) (bool, int, error) {
|
||||
func (t *Team) CanRead(s *xorm.Session, a web.Auth) (bool, int, error) {
|
||||
// Check if the user is in the team
|
||||
tm := &TeamMember{}
|
||||
can, err := x.Where("team_id = ?", t.ID).
|
||||
can, err := s.
|
||||
Where("team_id = ?", t.ID).
|
||||
And("user_id = ?", a.GetID()).
|
||||
Get(tm)
|
||||
|
||||
|
|
|
@ -82,6 +82,8 @@ func TestTeam_CanDoSomething(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
tm := &Team{
|
||||
ID: tt.fields.ID,
|
||||
|
@ -96,19 +98,19 @@ func TestTeam_CanDoSomething(t *testing.T) {
|
|||
Rights: tt.fields.Rights,
|
||||
}
|
||||
|
||||
if got, _ := tm.CanCreate(tt.args.a); got != tt.want["CanCreate"] { // CanCreate is currently always true
|
||||
if got, _ := tm.CanCreate(s, tt.args.a); got != tt.want["CanCreate"] { // CanCreate is currently always true
|
||||
t.Errorf("Team.CanCreate() = %v, want %v", got, tt.want["CanCreate"])
|
||||
}
|
||||
if got, _ := tm.CanDelete(tt.args.a); got != tt.want["CanDelete"] {
|
||||
if got, _ := tm.CanDelete(s, tt.args.a); got != tt.want["CanDelete"] {
|
||||
t.Errorf("Team.CanDelete() = %v, want %v", got, tt.want["CanDelete"])
|
||||
}
|
||||
if got, _ := tm.CanUpdate(tt.args.a); got != tt.want["CanUpdate"] {
|
||||
if got, _ := tm.CanUpdate(s, tt.args.a); got != tt.want["CanUpdate"] {
|
||||
t.Errorf("Team.CanUpdate() = %v, want %v", got, tt.want["CanUpdate"])
|
||||
}
|
||||
if got, _, _ := tm.CanRead(tt.args.a); got != tt.want["CanRead"] {
|
||||
if got, _, _ := tm.CanRead(s, tt.args.a); got != tt.want["CanRead"] {
|
||||
t.Errorf("Team.CanRead() = %v, want %v", got, tt.want["CanRead"])
|
||||
}
|
||||
if got, _ := tm.IsAdmin(tt.args.a); got != tt.want["IsAdmin"] {
|
||||
if got, _ := tm.IsAdmin(s, tt.args.a); got != tt.want["IsAdmin"] {
|
||||
t.Errorf("Team.IsAdmin() = %v, want %v", got, tt.want["IsAdmin"])
|
||||
}
|
||||
})
|
||||
|
|
|
@ -32,11 +32,16 @@ func TestTeam_Create(t *testing.T) {
|
|||
}
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
team := &Team{
|
||||
Name: "Testteam293",
|
||||
Description: "Lorem Ispum",
|
||||
}
|
||||
err := team.Create(doer)
|
||||
err := team.Create(s, doer)
|
||||
assert.NoError(t, err)
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
db.AssertExists(t, "teams", map[string]interface{}{
|
||||
"id": team.ID,
|
||||
|
@ -46,8 +51,11 @@ func TestTeam_Create(t *testing.T) {
|
|||
})
|
||||
t.Run("empty name", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
team := &Team{}
|
||||
err := team.Create(doer)
|
||||
err := team.Create(s, doer)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTeamNameCannotBeEmpty(err))
|
||||
})
|
||||
|
@ -56,8 +64,11 @@ func TestTeam_Create(t *testing.T) {
|
|||
func TestTeam_ReadOne(t *testing.T) {
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
team := &Team{ID: 1}
|
||||
err := team.ReadOne()
|
||||
err := team.ReadOne(s)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "testteam1", team.Name)
|
||||
assert.Equal(t, "Lorem Ipsum", team.Description)
|
||||
|
@ -66,15 +77,21 @@ func TestTeam_ReadOne(t *testing.T) {
|
|||
})
|
||||
t.Run("invalid id", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
team := &Team{ID: -1}
|
||||
err := team.ReadOne()
|
||||
err := team.ReadOne(s)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTeamDoesNotExist(err))
|
||||
})
|
||||
t.Run("nonexisting", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
team := &Team{ID: 99999}
|
||||
err := team.ReadOne()
|
||||
err := team.ReadOne(s)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTeamDoesNotExist(err))
|
||||
})
|
||||
|
@ -83,23 +100,31 @@ func TestTeam_ReadOne(t *testing.T) {
|
|||
func TestTeam_ReadAll(t *testing.T) {
|
||||
doer := &user.User{ID: 1}
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
team := &Team{}
|
||||
ts, _, _, err := team.ReadAll(doer, "", 1, 50)
|
||||
teams, _, _, err := team.ReadAll(s, doer, "", 1, 50)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, reflect.TypeOf(ts).Kind(), reflect.Slice)
|
||||
s := reflect.ValueOf(ts)
|
||||
assert.Equal(t, 8, s.Len())
|
||||
assert.Equal(t, reflect.TypeOf(teams).Kind(), reflect.Slice)
|
||||
ts := reflect.ValueOf(teams)
|
||||
assert.Equal(t, 8, ts.Len())
|
||||
})
|
||||
}
|
||||
|
||||
func TestTeam_Update(t *testing.T) {
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
team := &Team{
|
||||
ID: 1,
|
||||
Name: "SomethingNew",
|
||||
}
|
||||
err := team.Update()
|
||||
err := team.Update(s)
|
||||
assert.NoError(t, err)
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
db.AssertExists(t, "teams", map[string]interface{}{
|
||||
"id": team.ID,
|
||||
|
@ -108,21 +133,27 @@ func TestTeam_Update(t *testing.T) {
|
|||
})
|
||||
t.Run("empty name", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
team := &Team{
|
||||
ID: 1,
|
||||
Name: "",
|
||||
}
|
||||
err := team.Update()
|
||||
err := team.Update(s)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTeamNameCannotBeEmpty(err))
|
||||
})
|
||||
t.Run("nonexisting", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
team := &Team{
|
||||
ID: 9999,
|
||||
Name: "SomethingNew",
|
||||
}
|
||||
err := team.Update()
|
||||
err := team.Update(s)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTeamDoesNotExist(err))
|
||||
})
|
||||
|
@ -131,10 +162,15 @@ func TestTeam_Update(t *testing.T) {
|
|||
func TestTeam_Delete(t *testing.T) {
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
team := &Team{
|
||||
ID: 1,
|
||||
}
|
||||
err := team.Delete()
|
||||
err := team.Delete(s)
|
||||
assert.NoError(t, err)
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
db.AssertMissing(t, "teams", map[string]interface{}{
|
||||
"id": 1,
|
||||
|
|
|
@ -16,7 +16,10 @@
|
|||
|
||||
package models
|
||||
|
||||
import "code.vikunja.io/api/pkg/files"
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/files"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// Unsplash requires us to do pingbacks to their site and also name the image author.
|
||||
// To do this properly, we need to save these information somewhere.
|
||||
|
@ -36,15 +39,15 @@ func (u *UnsplashPhoto) TableName() string {
|
|||
}
|
||||
|
||||
// Save persists an unsplash photo to the db
|
||||
func (u *UnsplashPhoto) Save() error {
|
||||
_, err := x.Insert(u)
|
||||
func (u *UnsplashPhoto) Save(s *xorm.Session) error {
|
||||
_, err := s.Insert(u)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetUnsplashPhotoByFileID returns an unsplash photo by its saved file id
|
||||
func GetUnsplashPhotoByFileID(fileID int64) (u *UnsplashPhoto, err error) {
|
||||
func GetUnsplashPhotoByFileID(s *xorm.Session, fileID int64) (u *UnsplashPhoto, err error) {
|
||||
u = &UnsplashPhoto{}
|
||||
exists, err := x.Where("file_id = ?", fileID).Get(u)
|
||||
exists, err := s.Where("file_id = ?", fileID).Get(u)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -55,10 +58,10 @@ func GetUnsplashPhotoByFileID(fileID int64) (u *UnsplashPhoto, err error) {
|
|||
}
|
||||
|
||||
// RemoveUnsplashPhoto removes an unsplash photo from the db
|
||||
func RemoveUnsplashPhoto(fileID int64) (err error) {
|
||||
func RemoveUnsplashPhoto(s *xorm.Session, fileID int64) (err error) {
|
||||
// This is intentionally "fire and forget" which is why we don't check if we have an
|
||||
// unsplash entry for that file at all. If there is one, it will be deleted.
|
||||
// We do this to keep the function simple.
|
||||
_, err = x.Where("file_id = ?", fileID).Delete(&UnsplashPhoto{})
|
||||
_, err = s.Where("file_id = ?", fileID).Delete(&UnsplashPhoto{})
|
||||
return
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ package models
|
|||
import (
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// ListUIDs hold all kinds of user IDs from accounts who have somehow access to a list
|
||||
|
@ -33,11 +34,11 @@ type ListUIDs struct {
|
|||
}
|
||||
|
||||
// ListUsersFromList returns a list with all users who have access to a list, regardless of the method which gave them access
|
||||
func ListUsersFromList(l *List, search string) (users []*user.User, err error) {
|
||||
func ListUsersFromList(s *xorm.Session, l *List, search string) (users []*user.User, err error) {
|
||||
|
||||
userids := []*ListUIDs{}
|
||||
|
||||
err = x.
|
||||
err = s.
|
||||
Select(`l.owner_id as listOwner,
|
||||
un.user_id as unID,
|
||||
ul.user_id as ulID,
|
||||
|
@ -97,7 +98,7 @@ func ListUsersFromList(l *List, search string) (users []*user.User, err error) {
|
|||
}
|
||||
|
||||
// Get all users
|
||||
err = x.
|
||||
err = s.
|
||||
Table("users").
|
||||
Select("*").
|
||||
In("id", uids).
|
||||
|
|
|
@ -201,8 +201,10 @@ func TestListUsersFromList(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
gotUsers, err := ListUsersFromList(tt.args.l, tt.args.search)
|
||||
gotUsers, err := ListUsersFromList(s, tt.args.l, tt.args.search)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ListUsersFromList() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
|
|
|
@ -23,6 +23,9 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
"xorm.io/xorm"
|
||||
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/api/pkg/modules/auth"
|
||||
|
@ -130,8 +133,17 @@ func HandleCallback(c echo.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
// Check if we have seen this user before
|
||||
u, err := getOrCreateUser(cl, idToken.Issuer, idToken.Subject)
|
||||
u, err := getOrCreateUser(s, cl, idToken.Issuer, idToken.Subject)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
err = s.Commit()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -140,9 +152,9 @@ func HandleCallback(c echo.Context) error {
|
|||
return auth.NewUserAuthTokenResponse(u, c)
|
||||
}
|
||||
|
||||
func getOrCreateUser(cl *claims, issuer, subject string) (u *user.User, err error) {
|
||||
func getOrCreateUser(s *xorm.Session, cl *claims, issuer, subject string) (u *user.User, err error) {
|
||||
// Check if the user exists for that issuer and subject
|
||||
u, err = user.GetUserWithEmail(&user.User{
|
||||
u, err = user.GetUserWithEmail(s, &user.User{
|
||||
Issuer: issuer,
|
||||
Subject: subject,
|
||||
})
|
||||
|
@ -165,7 +177,7 @@ func getOrCreateUser(cl *claims, issuer, subject string) (u *user.User, err erro
|
|||
uu.Username = petname.Generate(3, "-")
|
||||
}
|
||||
|
||||
u, err = user.CreateUser(uu)
|
||||
u, err = user.CreateUser(s, uu)
|
||||
if err != nil && !user.IsErrUsernameExists(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -173,14 +185,14 @@ func getOrCreateUser(cl *claims, issuer, subject string) (u *user.User, err erro
|
|||
// If their preferred username is already taken, create some random one from the email and subject
|
||||
if user.IsErrUsernameExists(err) {
|
||||
uu.Username = petname.Generate(3, "-")
|
||||
u, err = user.CreateUser(uu)
|
||||
u, err = user.CreateUser(s, uu)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// And create its namespace
|
||||
err = models.CreateNewNamespaceForUser(u)
|
||||
err = models.CreateNewNamespaceForUser(s, u)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -196,7 +208,7 @@ func getOrCreateUser(cl *claims, issuer, subject string) (u *user.User, err erro
|
|||
if cl.Name != u.Name {
|
||||
u.Name = cl.Name
|
||||
}
|
||||
u, err = user.UpdateUser(&user.User{
|
||||
u, err = user.UpdateUser(s, &user.User{
|
||||
ID: u.ID,
|
||||
Email: u.Email,
|
||||
Name: u.Name,
|
||||
|
|
|
@ -26,12 +26,18 @@ import (
|
|||
func TestGetOrCreateUser(t *testing.T) {
|
||||
t.Run("new user", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
cl := &claims{
|
||||
Email: "test@example.com",
|
||||
PreferredUsername: "someUserWhoDoesNotExistYet",
|
||||
}
|
||||
u, err := getOrCreateUser(cl, "https://some.issuer", "12345")
|
||||
u, err := getOrCreateUser(s, cl, "https://some.issuer", "12345")
|
||||
assert.NoError(t, err)
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
|
||||
db.AssertExists(t, "users", map[string]interface{}{
|
||||
"id": u.ID,
|
||||
"email": cl.Email,
|
||||
|
@ -40,13 +46,19 @@ func TestGetOrCreateUser(t *testing.T) {
|
|||
})
|
||||
t.Run("new user, no username provided", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
cl := &claims{
|
||||
Email: "test@example.com",
|
||||
PreferredUsername: "",
|
||||
}
|
||||
u, err := getOrCreateUser(cl, "https://some.issuer", "12345")
|
||||
u, err := getOrCreateUser(s, cl, "https://some.issuer", "12345")
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, u.Username)
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
|
||||
db.AssertExists(t, "users", map[string]interface{}{
|
||||
"id": u.ID,
|
||||
"email": cl.Email,
|
||||
|
@ -54,19 +66,28 @@ func TestGetOrCreateUser(t *testing.T) {
|
|||
})
|
||||
t.Run("new user, no email address", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
cl := &claims{
|
||||
Email: "",
|
||||
}
|
||||
_, err := getOrCreateUser(cl, "https://some.issuer", "12345")
|
||||
_, err := getOrCreateUser(s, cl, "https://some.issuer", "12345")
|
||||
assert.Error(t, err)
|
||||
})
|
||||
t.Run("existing user, different email address", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
cl := &claims{
|
||||
Email: "other-email-address@some.service.com",
|
||||
}
|
||||
u, err := getOrCreateUser(cl, "https://some.service.com", "12345")
|
||||
u, err := getOrCreateUser(s, cl, "https://some.service.com", "12345")
|
||||
assert.NoError(t, err)
|
||||
err = s.Commit()
|
||||
assert.NoError(t, err)
|
||||
|
||||
db.AssertExists(t, "users", map[string]interface{}{
|
||||
"id": u.ID,
|
||||
"email": cl.Email,
|
||||
|
|
|
@ -19,6 +19,7 @@ package background
|
|||
import (
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/web"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// Image represents an image which can be used as a list background
|
||||
|
@ -33,7 +34,7 @@ type Image struct {
|
|||
// Provider represents something that is able to get a list of images and set one of them as background
|
||||
type Provider interface {
|
||||
// Search is used to either return a pre-defined list of Image or let the user search for an image
|
||||
Search(search string, page int64) (result []*Image, err error)
|
||||
Search(s *xorm.Session, search string, page int64) (result []*Image, err error)
|
||||
// Set sets an image which was most likely previously obtained by Search as list background
|
||||
Set(image *Image, list *models.List, auth web.Auth) (err error)
|
||||
Set(s *xorm.Session, image *Image, list *models.List, auth web.Auth) (err error)
|
||||
}
|
||||
|
|
|
@ -22,6 +22,9 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
"xorm.io/xorm"
|
||||
|
||||
"code.vikunja.io/api/pkg/files"
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
|
@ -59,8 +62,17 @@ func (bp *BackgroundProvider) SearchBackgrounds(c echo.Context) error {
|
|||
}
|
||||
}
|
||||
|
||||
result, err := p.Search(search, page)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
result, err := p.Search(s, search, page)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "An error occurred: "+err.Error())
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
_ = s.Rollback()
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "An error occurred: "+err.Error())
|
||||
}
|
||||
|
||||
|
@ -68,7 +80,7 @@ func (bp *BackgroundProvider) SearchBackgrounds(c echo.Context) error {
|
|||
}
|
||||
|
||||
// This function does all kinds of preparations for setting and uploading a background
|
||||
func (bp *BackgroundProvider) setBackgroundPreparations(c echo.Context) (list *models.List, auth web.Auth, err error) {
|
||||
func (bp *BackgroundProvider) setBackgroundPreparations(s *xorm.Session, c echo.Context) (list *models.List, auth web.Auth, err error) {
|
||||
auth, err = auth2.GetAuthFromClaims(c)
|
||||
if err != nil {
|
||||
return nil, nil, echo.NewHTTPError(http.StatusBadRequest, "Invalid auth token: "+err.Error())
|
||||
|
@ -81,7 +93,7 @@ func (bp *BackgroundProvider) setBackgroundPreparations(c echo.Context) (list *m
|
|||
|
||||
// Check if the user has the right to change the list background
|
||||
list = &models.List{ID: listID}
|
||||
can, err := list.CanUpdate(auth)
|
||||
can, err := list.CanUpdate(s, auth)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -90,14 +102,18 @@ func (bp *BackgroundProvider) setBackgroundPreparations(c echo.Context) (list *m
|
|||
return list, auth, models.ErrGenericForbidden{}
|
||||
}
|
||||
// Load the list
|
||||
err = list.GetSimpleByID()
|
||||
list, err = models.GetListSimpleByID(s, list.ID)
|
||||
return
|
||||
}
|
||||
|
||||
// SetBackground sets an Image as list background
|
||||
func (bp *BackgroundProvider) SetBackground(c echo.Context) error {
|
||||
list, auth, err := bp.setBackgroundPreparations(c)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
list, auth, err := bp.setBackgroundPreparations(s, c)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
|
@ -106,11 +122,13 @@ func (bp *BackgroundProvider) SetBackground(c echo.Context) error {
|
|||
image := &background.Image{}
|
||||
err = c.Bind(image)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "No or invalid model provided: "+err.Error())
|
||||
}
|
||||
|
||||
err = p.Set(image, list, auth)
|
||||
err = p.Set(s, image, list, auth)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
return c.JSON(http.StatusOK, list)
|
||||
|
@ -118,8 +136,12 @@ func (bp *BackgroundProvider) SetBackground(c echo.Context) error {
|
|||
|
||||
// UploadBackground uploads a background and passes the id of the uploaded file as an Image to the Set function of the BackgroundProvider.
|
||||
func (bp *BackgroundProvider) UploadBackground(c echo.Context) error {
|
||||
list, auth, err := bp.setBackgroundPreparations(c)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
list, auth, err := bp.setBackgroundPreparations(s, c)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
|
@ -128,10 +150,12 @@ func (bp *BackgroundProvider) UploadBackground(c echo.Context) error {
|
|||
// Get + upload the image
|
||||
file, err := c.FormFile("background")
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return err
|
||||
}
|
||||
src, err := file.Open()
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return err
|
||||
}
|
||||
defer src.Close()
|
||||
|
@ -139,9 +163,11 @@ func (bp *BackgroundProvider) UploadBackground(c echo.Context) error {
|
|||
// Validate we're dealing with an image
|
||||
mime, err := mimetype.DetectReader(src)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
if !strings.HasPrefix(mime.String(), "image") {
|
||||
_ = s.Rollback()
|
||||
return c.JSON(http.StatusBadRequest, models.Message{Message: "Uploaded file is no image."})
|
||||
}
|
||||
_, _ = src.Seek(0, io.SeekStart)
|
||||
|
@ -149,6 +175,7 @@ func (bp *BackgroundProvider) UploadBackground(c echo.Context) error {
|
|||
// Save the file
|
||||
f, err := files.CreateWithMime(src, file.Filename, uint64(file.Size), auth, mime.String())
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
if files.IsErrFileIsTooLarge(err) {
|
||||
return echo.ErrBadRequest
|
||||
}
|
||||
|
@ -158,10 +185,17 @@ func (bp *BackgroundProvider) UploadBackground(c echo.Context) error {
|
|||
|
||||
image := &background.Image{ID: strconv.FormatInt(f.ID, 10)}
|
||||
|
||||
err = p.Set(image, list, auth)
|
||||
err = p.Set(s, image, list, auth)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, list)
|
||||
}
|
||||
|
||||
|
@ -190,17 +224,23 @@ func GetListBackground(c echo.Context) error {
|
|||
return echo.NewHTTPError(http.StatusBadRequest, "Invalid list ID: "+err.Error())
|
||||
}
|
||||
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
// Check if a background for this list exists + Rights
|
||||
list := &models.List{ID: listID}
|
||||
can, _, err := list.CanRead(auth)
|
||||
can, _, err := list.CanRead(s, auth)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
if !can {
|
||||
_ = s.Rollback()
|
||||
log.Infof("Tried to get list background of list %d while not having the rights for it (User: %v)", listID, auth)
|
||||
return echo.NewHTTPError(http.StatusForbidden)
|
||||
}
|
||||
if list.BackgroundFileID == 0 {
|
||||
_ = s.Rollback()
|
||||
return echo.NotFoundHandler(c)
|
||||
}
|
||||
|
||||
|
@ -209,13 +249,19 @@ func GetListBackground(c echo.Context) error {
|
|||
ID: list.BackgroundFileID,
|
||||
}
|
||||
if err := bgFile.LoadFileByID(); err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
// Unsplash requires pingbacks as per their api usage guidelines.
|
||||
// To do this in a privacy-preserving manner, we do the ping from inside of Vikunja to not expose any user details.
|
||||
// FIXME: This should use an event once we have events
|
||||
unsplash.Pingback(bgFile)
|
||||
unsplash.Pingback(s, bgFile)
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
// Serve the file
|
||||
return c.Stream(http.StatusOK, "image/jpg", bgFile.File)
|
||||
|
|
|
@ -26,6 +26,8 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"xorm.io/xorm"
|
||||
|
||||
"code.vikunja.io/api/pkg/config"
|
||||
"code.vikunja.io/api/pkg/files"
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
|
@ -150,7 +152,7 @@ func getUnsplashPhotoInfoByID(photoID string) (photo *Photo, err error) {
|
|||
// @Success 200 {array} background.Image "An array with photos"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /backgrounds/unsplash/search [get]
|
||||
func (p *Provider) Search(search string, page int64) (result []*background.Image, err error) {
|
||||
func (p *Provider) Search(s *xorm.Session, search string, page int64) (result []*background.Image, err error) {
|
||||
|
||||
// If we don't have a search query, return results from the unsplash featured collection
|
||||
if search == "" {
|
||||
|
@ -243,7 +245,7 @@ func (p *Provider) Search(search string, page int64) (result []*background.Image
|
|||
// @Failure 403 {object} web.HTTPError "The user does not have access to the list"
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{id}/backgrounds/unsplash [post]
|
||||
func (p *Provider) Set(image *background.Image, list *models.List, auth web.Auth) (err error) {
|
||||
func (p *Provider) Set(s *xorm.Session, image *background.Image, list *models.List, auth web.Auth) (err error) {
|
||||
|
||||
// Find the photo
|
||||
photo, err := getUnsplashPhotoInfoByID(image.ID)
|
||||
|
@ -292,7 +294,7 @@ func (p *Provider) Set(image *background.Image, list *models.List, auth web.Auth
|
|||
return err
|
||||
}
|
||||
|
||||
if err := models.RemoveUnsplashPhoto(list.BackgroundFileID); err != nil {
|
||||
if err := models.RemoveUnsplashPhoto(s, list.BackgroundFileID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -304,7 +306,7 @@ func (p *Provider) Set(image *background.Image, list *models.List, auth web.Auth
|
|||
Author: photo.User.Username,
|
||||
AuthorName: photo.User.Name,
|
||||
}
|
||||
err = unsplashPhoto.Save()
|
||||
err = unsplashPhoto.Save(s)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -315,13 +317,13 @@ func (p *Provider) Set(image *background.Image, list *models.List, auth web.Auth
|
|||
list.BackgroundInformation = unsplashPhoto
|
||||
|
||||
// Set it as the list background
|
||||
return models.SetListBackground(list.ID, file)
|
||||
return models.SetListBackground(s, list.ID, file)
|
||||
}
|
||||
|
||||
// Pingback pings the unsplash api if an unsplash photo has been accessed.
|
||||
func Pingback(f *files.File) {
|
||||
func Pingback(s *xorm.Session, f *files.File) {
|
||||
// Check if the file is actually downloaded from unsplash
|
||||
unsplashPhoto, err := models.GetUnsplashPhotoByFileID(f.ID)
|
||||
unsplashPhoto, err := models.GetUnsplashPhotoByFileID(s, f.ID)
|
||||
if err != nil {
|
||||
if files.IsErrFileIsNotUnsplashFile(err) {
|
||||
return
|
||||
|
|
|
@ -19,6 +19,8 @@ package upload
|
|||
import (
|
||||
"strconv"
|
||||
|
||||
"xorm.io/xorm"
|
||||
|
||||
"code.vikunja.io/api/pkg/files"
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/api/pkg/modules/background"
|
||||
|
@ -30,7 +32,7 @@ type Provider struct {
|
|||
}
|
||||
|
||||
// Search is only used to implement the interface
|
||||
func (p *Provider) Search(search string, page int64) (result []*background.Image, err error) {
|
||||
func (p *Provider) Search(s *xorm.Session, search string, page int64) (result []*background.Image, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -50,7 +52,7 @@ func (p *Provider) Search(search string, page int64) (result []*background.Image
|
|||
// @Failure 404 {object} models.Message "The list does not exist."
|
||||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /lists/{id}/backgrounds/upload [put]
|
||||
func (p *Provider) Set(image *background.Image, list *models.List, auth web.Auth) (err error) {
|
||||
func (p *Provider) Set(s *xorm.Session, image *background.Image, list *models.List, auth web.Auth) (err error) {
|
||||
// Remove the old background if one exists
|
||||
if list.BackgroundFileID != 0 {
|
||||
file := files.File{ID: list.BackgroundFileID}
|
||||
|
@ -67,5 +69,5 @@ func (p *Provider) Set(image *background.Image, list *models.List, auth web.Auth
|
|||
|
||||
list.BackgroundInformation = &models.ListBackgroundType{Type: models.ListBackgroundUpload}
|
||||
|
||||
return models.SetListBackground(list.ID, file)
|
||||
return models.SetListBackground(s, list.ID, file)
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ import (
|
|||
"bytes"
|
||||
"io/ioutil"
|
||||
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
|
||||
"code.vikunja.io/api/pkg/files"
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
|
@ -34,10 +36,14 @@ func InsertFromStructure(str []*models.NamespaceWithLists, user *user.User) (err
|
|||
|
||||
labels := make(map[string]*models.Label)
|
||||
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
// Create all namespaces
|
||||
for _, n := range str {
|
||||
err = n.Create(user)
|
||||
err = n.Create(s, user)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -54,8 +60,9 @@ func InsertFromStructure(str []*models.NamespaceWithLists, user *user.User) (err
|
|||
needsDefaultBucket := false
|
||||
|
||||
l.NamespaceID = n.ID
|
||||
err = l.Create(user)
|
||||
err = l.Create(s, user)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -67,11 +74,13 @@ func InsertFromStructure(str []*models.NamespaceWithLists, user *user.User) (err
|
|||
|
||||
file, err := files.Create(backgroundFile, "", uint64(backgroundFile.Len()), user)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
err = models.SetListBackground(l.ID, file)
|
||||
err = models.SetListBackground(s, l.ID, file)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -87,8 +96,9 @@ func InsertFromStructure(str []*models.NamespaceWithLists, user *user.User) (err
|
|||
oldID := bucket.ID
|
||||
bucket.ID = 0 // We want a new id
|
||||
bucket.ListID = l.ID
|
||||
err = bucket.Create(user)
|
||||
err = bucket.Create(s, user)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return
|
||||
}
|
||||
buckets[oldID] = bucket
|
||||
|
@ -111,8 +121,9 @@ func InsertFromStructure(str []*models.NamespaceWithLists, user *user.User) (err
|
|||
}
|
||||
|
||||
t.ListID = l.ID
|
||||
err = t.Create(user)
|
||||
err = t.Create(s, user)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -132,8 +143,9 @@ func InsertFromStructure(str []*models.NamespaceWithLists, user *user.User) (err
|
|||
// First create the related tasks if they do not exist
|
||||
if rt.ID == 0 {
|
||||
rt.ListID = t.ListID
|
||||
err = rt.Create(user)
|
||||
err = rt.Create(s, user)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return
|
||||
}
|
||||
log.Debugf("[creating structure] Created related task %d", rt.ID)
|
||||
|
@ -145,8 +157,9 @@ func InsertFromStructure(str []*models.NamespaceWithLists, user *user.User) (err
|
|||
OtherTaskID: rt.ID,
|
||||
RelationKind: kind,
|
||||
}
|
||||
err = taskRel.Create(user)
|
||||
err = taskRel.Create(s, user)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -164,8 +177,9 @@ func InsertFromStructure(str []*models.NamespaceWithLists, user *user.User) (err
|
|||
if len(a.File.FileContent) > 0 {
|
||||
a.TaskID = t.ID
|
||||
fr := ioutil.NopCloser(bytes.NewReader(a.File.FileContent))
|
||||
err = a.NewAttachment(fr, a.File.Name, a.File.Size, user)
|
||||
err = a.NewAttachment(s, fr, a.File.Name, a.File.Size, user)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return
|
||||
}
|
||||
log.Debugf("[creating structure] Created new attachment %d", a.ID)
|
||||
|
@ -180,8 +194,9 @@ func InsertFromStructure(str []*models.NamespaceWithLists, user *user.User) (err
|
|||
var exists bool
|
||||
lb, exists = labels[label.Title+label.HexColor]
|
||||
if !exists {
|
||||
err = label.Create(user)
|
||||
err = label.Create(s, user)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return err
|
||||
}
|
||||
log.Debugf("[creating structure] Created new label %d", label.ID)
|
||||
|
@ -193,8 +208,9 @@ func InsertFromStructure(str []*models.NamespaceWithLists, user *user.User) (err
|
|||
LabelID: lb.ID,
|
||||
TaskID: t.ID,
|
||||
}
|
||||
err = lt.Create(user)
|
||||
err = lt.Create(s, user)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return err
|
||||
}
|
||||
log.Debugf("[creating structure] Associated task %d with label %d", t.ID, lb.ID)
|
||||
|
@ -204,13 +220,15 @@ func InsertFromStructure(str []*models.NamespaceWithLists, user *user.User) (err
|
|||
// All tasks brought their own bucket with them, therefore the newly created default bucket is just extra space
|
||||
if !needsDefaultBucket {
|
||||
b := &models.Bucket{ListID: l.ID}
|
||||
bucketsIn, _, _, err := b.ReadAll(user, "", 1, 1)
|
||||
bucketsIn, _, _, err := b.ReadAll(s, user, "", 1, 1)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return err
|
||||
}
|
||||
buckets := bucketsIn.([]*models.Bucket)
|
||||
err = buckets[0].Delete()
|
||||
err = buckets[0].Delete(s)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -222,5 +240,5 @@ func InsertFromStructure(str []*models.NamespaceWithLists, user *user.User) (err
|
|||
|
||||
log.Debugf("[creating structure] Done inserting new task structure")
|
||||
|
||||
return nil
|
||||
return s.Commit()
|
||||
}
|
||||
|
|
|
@ -19,20 +19,10 @@ package migration
|
|||
import (
|
||||
"code.vikunja.io/api/pkg/config"
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
var x *xorm.Engine
|
||||
|
||||
// InitDB sets up the database connection to use in this module
|
||||
func InitDB() (err error) {
|
||||
x, err = db.CreateDBEngine()
|
||||
if err != nil {
|
||||
log.Criticalf("Could not connect to db: %v", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Cache
|
||||
if config.CacheEnabled.GetBool() && config.CacheType.GetString() == "redis" {
|
||||
db.RegisterTableStructsForCache(GetTables())
|
||||
|
|
|
@ -19,6 +19,7 @@ package migration
|
|||
import (
|
||||
"time"
|
||||
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
)
|
||||
|
||||
|
@ -37,17 +38,26 @@ func (s *Status) TableName() string {
|
|||
|
||||
// SetMigrationStatus sets the migration status for a user
|
||||
func SetMigrationStatus(m Migrator, u *user.User) (err error) {
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
status := &Status{
|
||||
UserID: u.ID,
|
||||
MigratorName: m.Name(),
|
||||
}
|
||||
_, err = x.Insert(status)
|
||||
_, err = s.Insert(status)
|
||||
return
|
||||
}
|
||||
|
||||
// GetMigrationStatus returns the migration status for a migration and a user
|
||||
func GetMigrationStatus(m Migrator, u *user.User) (status *Status, err error) {
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
status = &Status{}
|
||||
_, err = x.Where("user_id = ? and migrator_name = ?", u.ID, m.Name()).Desc("id").Get(status)
|
||||
_, err = s.
|
||||
Where("user_id = ? and migrator_name = ?", u.ID, m.Name()).
|
||||
Desc("id").
|
||||
Get(status)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package v1
|
||||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
"code.vikunja.io/api/pkg/files"
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
|
@ -56,8 +57,11 @@ func GetAvatar(c echo.Context) error {
|
|||
// Get the username
|
||||
username := c.Param("username")
|
||||
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
// Get the user
|
||||
u, err := user.GetUserWithEmail(&user.User{Username: username})
|
||||
u, err := user.GetUserWithEmail(s, &user.User{Username: username})
|
||||
if err != nil {
|
||||
log.Errorf("Error getting user for avatar: %v", err)
|
||||
return handler.HandleHTTPError(err, c)
|
||||
|
@ -113,22 +117,28 @@ func GetAvatar(c echo.Context) error {
|
|||
// @Router /user/settings/avatar/upload [put]
|
||||
func UploadAvatar(c echo.Context) (err error) {
|
||||
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
uc, err := user.GetCurrentUser(c)
|
||||
if err != nil {
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
u, err := user.GetUserByID(uc.ID)
|
||||
u, err := user.GetUserByID(s, uc.ID)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
// Get + upload the image
|
||||
file, err := c.FormFile("avatar")
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return err
|
||||
}
|
||||
src, err := file.Open()
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return err
|
||||
}
|
||||
defer src.Close()
|
||||
|
@ -136,6 +146,7 @@ func UploadAvatar(c echo.Context) (err error) {
|
|||
// Validate we're dealing with an image
|
||||
mime, err := mimetype.DetectReader(src)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
if !strings.HasPrefix(mime.String(), "image") {
|
||||
|
@ -148,6 +159,7 @@ func UploadAvatar(c echo.Context) (err error) {
|
|||
f := &files.File{ID: u.AvatarFileID}
|
||||
if err := f.Delete(); err != nil {
|
||||
if !files.IsErrFileDoesNotExist(err) {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
}
|
||||
|
@ -157,11 +169,13 @@ func UploadAvatar(c echo.Context) (err error) {
|
|||
// Resize the new file to a max height of 1024
|
||||
img, _, err := image.Decode(src)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
resizedImg := imaging.Resize(img, 0, 1024, imaging.Lanczos)
|
||||
buf := &bytes.Buffer{}
|
||||
if err := png.Encode(buf, resizedImg); err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
|
@ -170,6 +184,7 @@ func UploadAvatar(c echo.Context) (err error) {
|
|||
// Save the file
|
||||
f, err := files.CreateWithMime(buf, file.Filename, uint64(file.Size), u, "image/png")
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
if files.IsErrFileIsTooLarge(err) {
|
||||
return echo.ErrBadRequest
|
||||
}
|
||||
|
@ -180,7 +195,13 @@ func UploadAvatar(c echo.Context) (err error) {
|
|||
u.AvatarFileID = f.ID
|
||||
u.AvatarProvider = "upload"
|
||||
|
||||
if _, err := user.UpdateUser(u); err != nil {
|
||||
if _, err := user.UpdateUser(s, u); err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@ package v1
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/api/pkg/modules/auth"
|
||||
"code.vikunja.io/web/handler"
|
||||
|
@ -45,8 +47,18 @@ type LinkShareToken struct {
|
|||
// @Router /shares/{share}/auth [post]
|
||||
func AuthenticateLinkShare(c echo.Context) error {
|
||||
hash := c.Param("share")
|
||||
share, err := models.GetLinkShareByHash(hash)
|
||||
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
share, err := models.GetLinkShareByHash(s, hash)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,9 @@ import (
|
|||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
"xorm.io/xorm"
|
||||
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
"code.vikunja.io/web/handler"
|
||||
|
@ -41,8 +44,11 @@ import (
|
|||
// @Failure 500 {object} models.Message "Internal error"
|
||||
// @Router /namespaces/{id}/lists [get]
|
||||
func GetListsByNamespaceID(c echo.Context) error {
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
// Get our namespace
|
||||
namespace, err := getNamespace(c)
|
||||
namespace, err := getNamespace(s, c)
|
||||
if err != nil {
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
@ -53,14 +59,14 @@ func GetListsByNamespaceID(c echo.Context) error {
|
|||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
lists, err := models.GetListsByNamespaceID(namespace.ID, doer)
|
||||
lists, err := models.GetListsByNamespaceID(s, namespace.ID, doer)
|
||||
if err != nil {
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
return c.JSON(http.StatusOK, lists)
|
||||
}
|
||||
|
||||
func getNamespace(c echo.Context) (namespace *models.Namespace, err error) {
|
||||
func getNamespace(s *xorm.Session, c echo.Context) (namespace *models.Namespace, err error) {
|
||||
// Check if we have our ID
|
||||
id := c.Param("namespace")
|
||||
// Make int
|
||||
|
@ -75,12 +81,12 @@ func getNamespace(c echo.Context) (namespace *models.Namespace, err error) {
|
|||
}
|
||||
|
||||
// Check if the user has acces to that namespace
|
||||
user, err := user.GetCurrentUser(c)
|
||||
u, err := user.GetCurrentUser(c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
namespace = &models.Namespace{ID: namespaceID}
|
||||
canRead, _, err := namespace.CanRead(user)
|
||||
canRead, _, err := namespace.CanRead(s, u)
|
||||
if err != nil {
|
||||
return namespace, err
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ package v1
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/api/pkg/modules/auth"
|
||||
user2 "code.vikunja.io/api/pkg/user"
|
||||
|
@ -45,27 +47,38 @@ func Login(c echo.Context) error {
|
|||
return c.JSON(http.StatusBadRequest, models.Message{Message: "Please provide a username and password."})
|
||||
}
|
||||
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
// Check user
|
||||
user, err := user2.CheckUserCredentials(&u)
|
||||
user, err := user2.CheckUserCredentials(s, &u)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
totpEnabled, err := user2.TOTPEnabledForUser(user)
|
||||
totpEnabled, err := user2.TOTPEnabledForUser(s, user)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
if totpEnabled {
|
||||
_, err = user2.ValidateTOTPPasscode(&user2.TOTPPasscode{
|
||||
_, err = user2.ValidateTOTPPasscode(s, &user2.TOTPPasscode{
|
||||
User: user,
|
||||
Passcode: u.TOTPPasscode,
|
||||
})
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
// Create token
|
||||
return auth.NewUserAuthTokenResponse(user, c)
|
||||
}
|
||||
|
@ -82,18 +95,23 @@ func Login(c echo.Context) error {
|
|||
// @Router /user/token [post]
|
||||
func RenewToken(c echo.Context) (err error) {
|
||||
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
jwtinf := c.Get("user").(*jwt.Token)
|
||||
claims := jwtinf.Claims.(jwt.MapClaims)
|
||||
typ := int(claims["type"].(float64))
|
||||
if typ == auth.AuthTypeLinkShare {
|
||||
share := &models.LinkSharing{}
|
||||
share.ID = int64(claims["id"].(float64))
|
||||
err := share.ReadOne()
|
||||
err := share.ReadOne(s)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
t, err := auth.NewLinkShareJWTAuthtoken(share)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
return c.JSON(http.StatusOK, auth.Token{Token: t})
|
||||
|
@ -101,11 +119,18 @@ func RenewToken(c echo.Context) (err error) {
|
|||
|
||||
u, err := user2.GetUserFromClaims(claims)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
user, err := user2.GetUserWithEmail(&user2.User{ID: u.ID})
|
||||
user, err := user2.GetUserWithEmail(s, &user2.User{ID: u.ID})
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@ package v1
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
auth2 "code.vikunja.io/api/pkg/modules/auth"
|
||||
"code.vikunja.io/web/handler"
|
||||
|
@ -52,8 +54,12 @@ func UploadTaskAttachment(c echo.Context) error {
|
|||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
can, err := taskAttachment.CanCreate(auth)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
can, err := taskAttachment.CanCreate(s, auth)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
if !can {
|
||||
|
@ -63,6 +69,7 @@ func UploadTaskAttachment(c echo.Context) error {
|
|||
// Multipart form
|
||||
form, err := c.MultipartForm()
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
|
@ -85,7 +92,7 @@ func UploadTaskAttachment(c echo.Context) error {
|
|||
}
|
||||
defer f.Close()
|
||||
|
||||
err = ta.NewAttachment(f, file.Filename, uint64(file.Size), auth)
|
||||
err = ta.NewAttachment(s, f, file.Filename, uint64(file.Size), auth)
|
||||
if err != nil {
|
||||
r.Errors = append(r.Errors, handler.HandleHTTPError(err, c))
|
||||
continue
|
||||
|
@ -93,6 +100,11 @@ func UploadTaskAttachment(c echo.Context) error {
|
|||
r.Success = append(r.Success, ta)
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, r)
|
||||
}
|
||||
|
||||
|
@ -121,8 +133,13 @@ func GetTaskAttachment(c echo.Context) error {
|
|||
if err != nil {
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
can, _, err := taskAttachment.CanRead(auth)
|
||||
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
can, _, err := taskAttachment.CanRead(s, auth)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
if !can {
|
||||
|
@ -130,14 +147,21 @@ func GetTaskAttachment(c echo.Context) error {
|
|||
}
|
||||
|
||||
// Get the attachment incl file
|
||||
err = taskAttachment.ReadOne()
|
||||
err = taskAttachment.ReadOne(s)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
// Open an send the file to the client
|
||||
err = taskAttachment.File.LoadFileByID()
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@ package v1
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
"code.vikunja.io/web/handler"
|
||||
|
@ -43,8 +45,17 @@ func UserConfirmEmail(c echo.Context) error {
|
|||
return echo.NewHTTPError(http.StatusBadRequest, "No token provided.")
|
||||
}
|
||||
|
||||
err := user.ConfirmEmail(&emailConfirm)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
err := user.ConfirmEmail(s, &emailConfirm)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ import (
|
|||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
auth2 "code.vikunja.io/api/pkg/modules/auth"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
|
@ -40,9 +42,19 @@ import (
|
|||
// @Failure 500 {object} models.Message "Internal server error."
|
||||
// @Router /users [get]
|
||||
func UserList(c echo.Context) error {
|
||||
s := c.QueryParam("s")
|
||||
users, err := user.ListUsers(s)
|
||||
search := c.QueryParam("s")
|
||||
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
users, err := user.ListUsers(s, search)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
|
@ -80,17 +92,27 @@ func ListUsersForList(c echo.Context) error {
|
|||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
canRead, _, err := list.CanRead(auth)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
canRead, _, err := list.CanRead(s, auth)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
if !canRead {
|
||||
return echo.ErrForbidden
|
||||
}
|
||||
|
||||
s := c.QueryParam("s")
|
||||
users, err := models.ListUsersFromList(&list, s)
|
||||
search := c.QueryParam("s")
|
||||
users, err := models.ListUsersFromList(s, &list, search)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@ package v1
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
"code.vikunja.io/web/handler"
|
||||
|
@ -43,8 +45,17 @@ func UserResetPassword(c echo.Context) error {
|
|||
return echo.NewHTTPError(http.StatusBadRequest, "No password provided.")
|
||||
}
|
||||
|
||||
err := user.ResetPassword(&pwReset)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
err := user.ResetPassword(s, &pwReset)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
|
@ -73,8 +84,17 @@ func UserRequestResetPasswordToken(c echo.Context) error {
|
|||
return echo.NewHTTPError(http.StatusBadRequest, err)
|
||||
}
|
||||
|
||||
err := user.RequestUserPasswordResetTokenByEmail(&pwTokenReset)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
err := user.RequestUserPasswordResetTokenByEmail(s, &pwTokenReset)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@ package v1
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
|
||||
"code.vikunja.io/api/pkg/config"
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
|
@ -50,15 +52,25 @@ func RegisterUser(c echo.Context) error {
|
|||
return c.JSON(http.StatusBadRequest, models.Message{Message: "No or invalid user model provided."})
|
||||
}
|
||||
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
// Insert the user
|
||||
newUser, err := user.CreateUser(datUser.APIFormat())
|
||||
newUser, err := user.CreateUser(s, datUser.APIFormat())
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
// Add its namespace
|
||||
err = models.CreateNewNamespaceForUser(newUser)
|
||||
err = models.CreateNewNamespaceForUser(s, newUser)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@ package v1
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
user2 "code.vikunja.io/api/pkg/user"
|
||||
"code.vikunja.io/web/handler"
|
||||
|
@ -57,8 +59,17 @@ func GetUserAvatarProvider(c echo.Context) error {
|
|||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
user, err := user2.GetUserWithEmail(&user2.User{ID: u.ID})
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
user, err := user2.GetUserWithEmail(s, &user2.User{ID: u.ID})
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
|
@ -91,15 +102,25 @@ func ChangeUserAvatarProvider(c echo.Context) error {
|
|||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
user, err := user2.GetUserWithEmail(&user2.User{ID: u.ID})
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
user, err := user2.GetUserWithEmail(s, &user2.User{ID: u.ID})
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
user.AvatarProvider = uap.AvatarProvider
|
||||
|
||||
_, err = user2.UpdateUser(user)
|
||||
_, err = user2.UpdateUser(s, user)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
|
@ -129,16 +150,26 @@ func UpdateGeneralUserSettings(c echo.Context) error {
|
|||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
user, err := user2.GetUserWithEmail(&user2.User{ID: u.ID})
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
user, err := user2.GetUserWithEmail(s, &user2.User{ID: u.ID})
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
user.Name = us.Name
|
||||
user.EmailRemindersEnabled = us.EmailRemindersEnabled
|
||||
|
||||
_, err = user2.UpdateUser(user)
|
||||
_, err = user2.UpdateUser(s, user)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@ package v1
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
|
||||
user2 "code.vikunja.io/api/pkg/user"
|
||||
"code.vikunja.io/web/handler"
|
||||
"github.com/labstack/echo/v4"
|
||||
|
@ -41,8 +43,17 @@ func UserShow(c echo.Context) error {
|
|||
return echo.NewHTTPError(http.StatusInternalServerError, "Error getting current user.")
|
||||
}
|
||||
|
||||
user, err := user2.GetUserByID(userInfos.ID)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
user, err := user2.GetUserByID(s, userInfos.ID)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,8 @@ import (
|
|||
"image/jpeg"
|
||||
"net/http"
|
||||
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
|
@ -47,8 +49,17 @@ func UserTOTPEnroll(c echo.Context) error {
|
|||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
t, err := user.EnrollTOTP(u)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
t, err := user.EnrollTOTP(s, u)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
|
@ -86,8 +97,17 @@ func UserTOTPEnable(c echo.Context) error {
|
|||
return echo.NewHTTPError(http.StatusBadRequest, "Invalid model provided.")
|
||||
}
|
||||
|
||||
err = user.EnableTOTP(passcode)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
err = user.EnableTOTP(s, passcode)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
|
@ -122,18 +142,29 @@ func UserTOTPDisable(c echo.Context) error {
|
|||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
u, err = user.GetUserByID(u.ID)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
u, err = user.GetUserByID(s, u.ID)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
err = user.CheckUserPassword(u, login.Password)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
err = user.DisableTOTP(u)
|
||||
err = user.DisableTOTP(s, u)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
|
@ -156,14 +187,24 @@ func UserTOTPQrCode(c echo.Context) error {
|
|||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
qrcode, err := user.GetTOTPQrCodeForUser(u)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
qrcode, err := user.GetTOTPQrCodeForUser(s, u)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
buff := &bytes.Buffer{}
|
||||
err = jpeg.Encode(buff, qrcode, nil)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
|
@ -186,8 +227,17 @@ func UserTOTP(c echo.Context) error {
|
|||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
t, err := user.GetTOTPForUser(u)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
t, err := user.GetTOTPForUser(s, u)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
|
@ -56,16 +58,26 @@ func UpdateUserEmail(c echo.Context) (err error) {
|
|||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
emailUpdate.User, err = user.CheckUserCredentials(&user.Login{
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
emailUpdate.User, err = user.CheckUserCredentials(s, &user.Login{
|
||||
Username: emailUpdate.User.Username,
|
||||
Password: emailUpdate.Password,
|
||||
})
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
err = user.UpdateEmail(emailUpdate)
|
||||
err = user.UpdateEmail(s, emailUpdate)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@ package v1
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
"code.vikunja.io/web/handler"
|
||||
|
@ -61,13 +63,23 @@ func UserChangePassword(c echo.Context) error {
|
|||
return handler.HandleHTTPError(user.ErrEmptyOldPassword{}, c)
|
||||
}
|
||||
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
// Check the current password
|
||||
if _, err = user.CheckUserCredentials(&user.Login{Username: doer.Username, Password: newPW.OldPassword}); err != nil {
|
||||
if _, err = user.CheckUserCredentials(s, &user.Login{Username: doer.Username, Password: newPW.OldPassword}); err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
// Update the password
|
||||
if err = user.UpdateUserPassword(doer, newPW.NewPassword); err != nil {
|
||||
if err = user.UpdateUserPassword(s, doer, newPW.NewPassword); err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,8 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
user2 "code.vikunja.io/api/pkg/user"
|
||||
|
@ -90,9 +92,16 @@ func (vcls *VikunjaCaldavListStorage) GetResources(rpath string, withChildren bo
|
|||
return []data.Resource{r}, nil
|
||||
}
|
||||
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
// Otherwise get all lists
|
||||
thelists, _, _, err := vcls.list.ReadAll(vcls.user, "", -1, 50)
|
||||
thelists, _, _, err := vcls.list.ReadAll(s, vcls.user, "", -1, 50)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
if err := s.Commit(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lists := thelists.([]*models.List)
|
||||
|
@ -125,10 +134,17 @@ func (vcls *VikunjaCaldavListStorage) GetResourcesByList(rpaths []string) ([]dat
|
|||
uids = append(uids, string(uid[:endlen]))
|
||||
}
|
||||
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
// GetTasksByUIDs...
|
||||
// Parse these into ressources...
|
||||
tasks, err := models.GetTasksByUIDs(uids)
|
||||
tasks, err := models.GetTasksByUIDs(s, uids)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
if err := s.Commit(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -187,15 +203,22 @@ func (vcls *VikunjaCaldavListStorage) GetResource(rpath string) (*data.Resource,
|
|||
|
||||
// If the task is not nil, we need to get the task and not the list
|
||||
if vcls.task != nil {
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
// save and override the updated unix date to not break any later etag checks
|
||||
updated := vcls.task.Updated
|
||||
task, err := models.GetTaskSimple(&models.Task{ID: vcls.task.ID, UID: vcls.task.UID})
|
||||
task, err := models.GetTaskSimple(s, &models.Task{ID: vcls.task.ID, UID: vcls.task.UID})
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
if models.IsErrTaskDoesNotExist(err) {
|
||||
return nil, false, errs.ResourceNotFoundError
|
||||
}
|
||||
return nil, false, err
|
||||
}
|
||||
if err := s.Commit(); err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
vcls.task = &task
|
||||
if updated.Unix() > 0 {
|
||||
|
@ -230,6 +253,9 @@ func (vcls *VikunjaCaldavListStorage) GetShallowResource(rpath string) (*data.Re
|
|||
// CreateResource creates a new resource
|
||||
func (vcls *VikunjaCaldavListStorage) CreateResource(rpath, content string) (*data.Resource, error) {
|
||||
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
vTask, err := parseTaskFromVTODO(content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -238,7 +264,7 @@ func (vcls *VikunjaCaldavListStorage) CreateResource(rpath, content string) (*da
|
|||
vTask.ListID = vcls.list.ID
|
||||
|
||||
// Check the rights
|
||||
canCreate, err := vTask.CanCreate(vcls.user)
|
||||
canCreate, err := vTask.CanCreate(s, vcls.user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -247,8 +273,13 @@ func (vcls *VikunjaCaldavListStorage) CreateResource(rpath, content string) (*da
|
|||
}
|
||||
|
||||
// Create the task
|
||||
err = vTask.Create(vcls.user)
|
||||
err = vTask.Create(s, vcls.user)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -272,18 +303,28 @@ func (vcls *VikunjaCaldavListStorage) UpdateResource(rpath, content string) (*da
|
|||
// At this point, we already have the right task in vcls.task, so we can use that ID directly
|
||||
vTask.ID = vcls.task.ID
|
||||
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
// Check the rights
|
||||
canUpdate, err := vTask.CanUpdate(vcls.user)
|
||||
canUpdate, err := vTask.CanUpdate(s, vcls.user)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
if !canUpdate {
|
||||
_ = s.Rollback()
|
||||
return nil, errs.ForbiddenError
|
||||
}
|
||||
|
||||
// Update the task
|
||||
err = vTask.Update()
|
||||
err = vTask.Update(s)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -299,9 +340,13 @@ func (vcls *VikunjaCaldavListStorage) UpdateResource(rpath, content string) (*da
|
|||
// DeleteResource deletes a resource
|
||||
func (vcls *VikunjaCaldavListStorage) DeleteResource(rpath string) error {
|
||||
if vcls.task != nil {
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
// Check the rights
|
||||
canDelete, err := vcls.task.CanDelete(vcls.user)
|
||||
canDelete, err := vcls.task.CanDelete(s, vcls.user)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return err
|
||||
}
|
||||
if !canDelete {
|
||||
|
@ -309,7 +354,13 @@ func (vcls *VikunjaCaldavListStorage) DeleteResource(rpath string) error {
|
|||
}
|
||||
|
||||
// Delete it
|
||||
return vcls.task.Delete()
|
||||
err = vcls.task.Delete(s)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
return s.Commit()
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -385,16 +436,22 @@ func (vlra *VikunjaListResourceAdapter) GetModTime() time.Time {
|
|||
}
|
||||
|
||||
func (vcls *VikunjaCaldavListStorage) getListRessource(isCollection bool) (rr VikunjaListResourceAdapter, err error) {
|
||||
can, _, err := vcls.list.CanRead(vcls.user)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
can, _, err := vcls.list.CanRead(s, vcls.user)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return
|
||||
}
|
||||
if !can {
|
||||
_ = s.Rollback()
|
||||
log.Errorf("User %v tried to access a caldav resource (List %v) which they are not allowed to access", vcls.user.Username, vcls.list.ID)
|
||||
return rr, models.ErrUserDoesNotHaveAccessToList{ListID: vcls.list.ID}
|
||||
}
|
||||
err = vcls.list.ReadOne()
|
||||
err = vcls.list.ReadOne(s)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -403,8 +460,9 @@ func (vcls *VikunjaCaldavListStorage) getListRessource(isCollection bool) (rr Vi
|
|||
tk := models.TaskCollection{
|
||||
ListID: vcls.list.ID,
|
||||
}
|
||||
iface, _, _, err := tk.ReadAll(vcls.user, "", 1, 1000)
|
||||
iface, _, _, err := tk.ReadAll(s, vcls.user, "", 1, 1000)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return rr, err
|
||||
}
|
||||
tasks, ok := iface.([]*models.Task)
|
||||
|
@ -416,6 +474,10 @@ func (vcls *VikunjaCaldavListStorage) getListRessource(isCollection bool) (rr Vi
|
|||
vcls.list.Tasks = tasks
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
return rr, err
|
||||
}
|
||||
|
||||
rr = VikunjaListResourceAdapter{
|
||||
list: vcls.list,
|
||||
listTasks: listTasks,
|
||||
|
|
|
@ -50,11 +50,8 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
microsofttodo "code.vikunja.io/api/pkg/modules/migration/microsoft-todo"
|
||||
|
||||
"code.vikunja.io/api/pkg/modules/migration/trello"
|
||||
|
||||
"code.vikunja.io/api/pkg/config"
|
||||
"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"
|
||||
|
@ -65,7 +62,9 @@ import (
|
|||
"code.vikunja.io/api/pkg/modules/background/upload"
|
||||
"code.vikunja.io/api/pkg/modules/migration"
|
||||
migrationHandler "code.vikunja.io/api/pkg/modules/migration/handler"
|
||||
microsofttodo "code.vikunja.io/api/pkg/modules/migration/microsoft-todo"
|
||||
"code.vikunja.io/api/pkg/modules/migration/todoist"
|
||||
"code.vikunja.io/api/pkg/modules/migration/trello"
|
||||
"code.vikunja.io/api/pkg/modules/migration/wunderlist"
|
||||
apiv1 "code.vikunja.io/api/pkg/routes/api/v1"
|
||||
"code.vikunja.io/api/pkg/routes/caldav"
|
||||
|
@ -175,6 +174,7 @@ func NewEcho() *echo.Echo {
|
|||
})
|
||||
handler.SetLoggingProvider(log.GetLogger())
|
||||
handler.SetMaxItemsPerPage(config.ServiceMaxItemsPerPage.GetInt())
|
||||
handler.SetSessionFactory(db.NewSession)
|
||||
|
||||
return e
|
||||
}
|
||||
|
@ -601,11 +601,19 @@ func caldavBasicAuth(username, password string, c echo.Context) (bool, error) {
|
|||
Username: username,
|
||||
Password: password,
|
||||
}
|
||||
u, err := user.CheckUserCredentials(creds)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
u, err := user.CheckUserCredentials(s, creds)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
log.Errorf("Error during basic auth for caldav: %v", err)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if err := s.Commit(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Save the user in echo context for later use
|
||||
c.Set("userBasicAuth", u)
|
||||
return true, nil
|
||||
|
|
|
@ -20,20 +20,10 @@ package user
|
|||
import (
|
||||
"code.vikunja.io/api/pkg/config"
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
var x *xorm.Engine
|
||||
|
||||
// InitDB sets up the database connection to use in this module
|
||||
func InitDB() (err error) {
|
||||
x, err = db.CreateDBEngine()
|
||||
if err != nil {
|
||||
log.Criticalf("Could not connect to db: %v", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Cache
|
||||
if config.CacheEnabled.GetBool() && config.CacheType.GetString() == "redis" {
|
||||
db.RegisterTableStructsForCache(GetTables())
|
||||
|
|
|
@ -24,8 +24,7 @@ import (
|
|||
|
||||
// InitTests handles the actual bootstrapping of the test env
|
||||
func InitTests() {
|
||||
var err error
|
||||
x, err = db.CreateTestEngine()
|
||||
x, err := db.CreateTestEngine()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ package user
|
|||
import (
|
||||
"image"
|
||||
|
||||
"xorm.io/xorm"
|
||||
|
||||
"code.vikunja.io/api/pkg/config"
|
||||
"github.com/pquerna/otp"
|
||||
"github.com/pquerna/otp/totp"
|
||||
|
@ -47,19 +49,19 @@ type TOTPPasscode struct {
|
|||
}
|
||||
|
||||
// TOTPEnabledForUser checks if totp is enabled for a user - not if it is activated, use GetTOTPForUser to check that.
|
||||
func TOTPEnabledForUser(user *User) (bool, error) {
|
||||
func TOTPEnabledForUser(s *xorm.Session, user *User) (bool, error) {
|
||||
if !config.ServiceEnableTotp.GetBool() {
|
||||
return false, nil
|
||||
}
|
||||
t := &TOTP{}
|
||||
_, err := x.Where("user_id = ?", user.ID).Get(t)
|
||||
_, err := s.Where("user_id = ?", user.ID).Get(t)
|
||||
return t.Enabled, err
|
||||
}
|
||||
|
||||
// GetTOTPForUser returns the current state of totp settings for the user.
|
||||
func GetTOTPForUser(user *User) (t *TOTP, err error) {
|
||||
func GetTOTPForUser(s *xorm.Session, user *User) (t *TOTP, err error) {
|
||||
t = &TOTP{}
|
||||
exists, err := x.Where("user_id = ?", user.ID).Get(t)
|
||||
exists, err := s.Where("user_id = ?", user.ID).Get(t)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -71,8 +73,8 @@ func GetTOTPForUser(user *User) (t *TOTP, err error) {
|
|||
}
|
||||
|
||||
// EnrollTOTP creates a new TOTP entry for the user - it does not enable it yet.
|
||||
func EnrollTOTP(user *User) (t *TOTP, err error) {
|
||||
isEnrolled, err := x.Where("user_id = ?", user.ID).Exist(&TOTP{})
|
||||
func EnrollTOTP(s *xorm.Session, user *User) (t *TOTP, err error) {
|
||||
isEnrolled, err := s.Where("user_id = ?", user.ID).Exist(&TOTP{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -94,18 +96,18 @@ func EnrollTOTP(user *User) (t *TOTP, err error) {
|
|||
Enabled: false,
|
||||
URL: key.URL(),
|
||||
}
|
||||
_, err = x.Insert(t)
|
||||
_, err = s.Insert(t)
|
||||
return
|
||||
}
|
||||
|
||||
// EnableTOTP enables totp for a user. The provided passcode is used to verify the user has a working totp setup.
|
||||
func EnableTOTP(passcode *TOTPPasscode) (err error) {
|
||||
t, err := ValidateTOTPPasscode(passcode)
|
||||
func EnableTOTP(s *xorm.Session, passcode *TOTPPasscode) (err error) {
|
||||
t, err := ValidateTOTPPasscode(s, passcode)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = x.
|
||||
_, err = s.
|
||||
Where("id = ?", t.ID).
|
||||
Cols("enabled").
|
||||
Update(&TOTP{Enabled: true})
|
||||
|
@ -113,14 +115,16 @@ func EnableTOTP(passcode *TOTPPasscode) (err error) {
|
|||
}
|
||||
|
||||
// DisableTOTP removes all totp settings for a user.
|
||||
func DisableTOTP(user *User) (err error) {
|
||||
_, err = x.Where("user_id = ?", user.ID).Delete(&TOTP{})
|
||||
func DisableTOTP(s *xorm.Session, user *User) (err error) {
|
||||
_, err = s.
|
||||
Where("user_id = ?", user.ID).
|
||||
Delete(&TOTP{})
|
||||
return
|
||||
}
|
||||
|
||||
// ValidateTOTPPasscode validated totp codes of users.
|
||||
func ValidateTOTPPasscode(passcode *TOTPPasscode) (t *TOTP, err error) {
|
||||
t, err = GetTOTPForUser(passcode.User)
|
||||
func ValidateTOTPPasscode(s *xorm.Session, passcode *TOTPPasscode) (t *TOTP, err error) {
|
||||
t, err = GetTOTPForUser(s, passcode.User)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -133,8 +137,8 @@ func ValidateTOTPPasscode(passcode *TOTPPasscode) (t *TOTP, err error) {
|
|||
}
|
||||
|
||||
// GetTOTPQrCodeForUser returns a qrcode for a user's totp setting
|
||||
func GetTOTPQrCodeForUser(user *User) (qrcode image.Image, err error) {
|
||||
t, err := GetTOTPForUser(user)
|
||||
func GetTOTPQrCodeForUser(s *xorm.Session, user *User) (qrcode image.Image, err error) {
|
||||
t, err := GetTOTPForUser(s, user)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"code.vikunja.io/api/pkg/config"
|
||||
"code.vikunja.io/api/pkg/mail"
|
||||
"code.vikunja.io/api/pkg/utils"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// EmailUpdate is the data structure to update a user's email address
|
||||
|
@ -32,11 +33,11 @@ type EmailUpdate struct {
|
|||
}
|
||||
|
||||
// UpdateEmail lets a user update their email address
|
||||
func UpdateEmail(update *EmailUpdate) (err error) {
|
||||
func UpdateEmail(s *xorm.Session, update *EmailUpdate) (err error) {
|
||||
|
||||
// Check the email is not already used
|
||||
user := &User{}
|
||||
has, err := x.Where("email = ?", update.NewEmail).Get(user)
|
||||
has, err := s.Where("email = ?", update.NewEmail).Get(user)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -46,7 +47,7 @@ func UpdateEmail(update *EmailUpdate) (err error) {
|
|||
}
|
||||
|
||||
// Set the user as unconfirmed and the new email address
|
||||
update.User, err = GetUserWithEmail(&User{ID: update.User.ID})
|
||||
update.User, err = GetUserWithEmail(s, &User{ID: update.User.ID})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -54,7 +55,7 @@ func UpdateEmail(update *EmailUpdate) (err error) {
|
|||
update.User.IsActive = false
|
||||
update.User.Email = update.NewEmail
|
||||
update.User.EmailConfirmToken = utils.MakeRandomString(64)
|
||||
_, err = x.
|
||||
_, err = s.
|
||||
Where("id = ?", update.User.ID).
|
||||
Cols("email", "is_active", "email_confirm_token").
|
||||
Update(update.User)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue