List Backgrounds #568

Merged
konrad merged 27 commits from list-backgrounds into master 2020-05-26 20:07:57 +00:00
18 changed files with 720 additions and 57 deletions

View File

@ -178,3 +178,19 @@ avatar:
provider: gravatar
# When using gravatar, this is the duration in seconds until a cached gravatar user avatar expires
gravatarexpiration: 3600
backgrounds:
# Whether to enable backgrounds for lists at all.
enabled: false
providers:
unsplash:
# Whether to enable setting backgrounds from unsplash as list backgrounds
enabled: false
# You need to create an application for your installation at https://unsplash.com/oauth/applications/new
# and set the access token below.
accesstoken:
# The unsplash application id is only used for pingback and required as per their api guidelines.
# You can find the Application ID in the dashboard for your API application. It should be a numeric ID.
# It will only show in the UI if your application has been approved for Enterprise usage, therefore if
# youre in Demo mode, you can also find the ID in the URL at the end: https://unsplash.com/oauth/applications/:application_id
applicationid:

View File

@ -221,4 +221,20 @@ avatar:
provider: gravatar
# When using gravatar, this is the duration in seconds until a cached gravatar user avatar expires
gravatarexpiration: 3600
backgrounds:
# Whether to enable backgrounds for lists at all.
enabled: false
providers:
unsplash:
# Whether to enable setting backgrounds from unsplash as list backgrounds
enabled: false
# You need to create an application for your installation at https://unsplash.com/oauth/applications/new
# and set the access token below.
accesstoken:
# The unsplash application id is only used for pingback and required as per their api guidelines.
# You can find the Application ID in the dashboard for your API application. It should be a numeric ID.
# It will only show in the UI if your application has been approved for Enterprise usage, therefore if
# youre in Demo mode, you can also find the ID in the URL at the end: https://unsplash.com/oauth/applications/:application_id
applicationid:
{{< /highlight >}}

1
go.mod
View File

@ -47,7 +47,6 @@ require (
github.com/lib/pq v1.5.2
github.com/mailru/easyjson v0.7.0 // indirect
github.com/mattn/go-sqlite3 v2.0.3+incompatible
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/olekukonko/tablewriter v0.0.4
github.com/onsi/ginkgo v1.12.0 // indirect

45
go.sum
View File

@ -160,10 +160,6 @@ github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-testfixtures/testfixtures/v3 v3.1.1 h1:SBIfzULODQQ7JV6AD931MvAz8pnkk6QCfMIcoOBDaXQ=
github.com/go-testfixtures/testfixtures/v3 v3.1.1/go.mod h1:RZctY24ixituGC73XlAV1gkCwYMVwiSwPm26MNlQIhE=
github.com/go-testfixtures/testfixtures/v3 v3.1.2 h1:sACIJoknTtWHbz5wahA6f50IGsz7oBXS+jgm0eKl4eI=
github.com/go-testfixtures/testfixtures/v3 v3.1.2/go.mod h1:RZctY24ixituGC73XlAV1gkCwYMVwiSwPm26MNlQIhE=
github.com/go-testfixtures/testfixtures/v3 v3.2.0 h1:FGAW3z5UzmrZGjR/dZp1u3Tbld0SDmirLO4RrR5++7Q=
github.com/go-testfixtures/testfixtures/v3 v3.2.0/go.mod h1:RZctY24ixituGC73XlAV1gkCwYMVwiSwPm26MNlQIhE=
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
@ -184,8 +180,6 @@ github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
@ -300,12 +294,6 @@ github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
github.com/lib/pq v1.0.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.4.0 h1:TmtCFbH+Aw0AixwyttznSMQDgbR5Yed/Gg6S8Funrhc=
github.com/lib/pq v1.4.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.5.0 h1:Hq6pEflc2Q3hP5iEH3Q6XopXrJXxjhwbvMpj9eZnpp0=
github.com/lib/pq v1.5.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.5.1 h1:Jn6HYxiYrtQ92CopqJLvfPCJUrrruw1+1cn0jM9dKrI=
github.com/lib/pq v1.5.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.5.2 h1:yTSXVswvWUOQ3k1sd7vJfDrbSl8lKuscqFJRqjC0ifw=
github.com/lib/pq v1.5.2/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
@ -357,8 +345,6 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
@ -403,8 +389,6 @@ github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829 h1:D+Ci
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.5.1 h1:bdHYieyGlH+6OLEk2YQha8THib30KP0/yD0YH9m6xcA=
github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_golang v1.6.0 h1:YVPodQOcK15POxhgARIvnDRVpLcuK8mglnMrWfyrw6A=
github.com/prometheus/client_golang v1.6.0/go.mod h1:ZLOG9ck3JLRdB5MgO8f+lLTe83AXG6ro35rLTxvnIl4=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
@ -429,8 +413,6 @@ github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.0.11 h1:DhHlBtkHWPYi8O2y31JkK0TF+DGM+51OopZjH/Ia5qI=
github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
@ -462,8 +444,6 @@ github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.7 h1:FfTH+vuMXOas8jmfb5/M7dzEYx7LpcLb7a0LPe34uOU=
github.com/spf13/cobra v0.0.7/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
@ -475,8 +455,6 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/spf13/viper v1.6.3 h1:pDDu1OyEDTKzpJwdq4TiuLyMsUgRa/BT5cn5O62NoHs=
github.com/spf13/viper v1.6.3/go.mod h1:jUMtyi0/lB5yZH/FjyGAoH7IMNrIhlBf6pXZmbMDvzw=
github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@ -544,22 +522,6 @@ golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4 h1:ydJNl0ENAG67pFbB+9tfhi
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200406173513-056763e48d71 h1:DOmugCavvUtnUD114C1Wh+UgTgQZ4pMLzXxi1pSt+/Y=
golang.org/x/crypto v0.0.0-20200406173513-056763e48d71/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 h1:bXoxMPcSLOq08zI3/c5dEBT6lE4eh+jOh886GHrn6V8=
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200420192832-a76a400e3025 h1:U821GPBRQKVHpr6Os5UDBgF2FveIjlZYbz+e2n5wmmQ=
golang.org/x/crypto v0.0.0-20200420192832-a76a400e3025/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200420201142-3c4aac89819a h1:y6sBfNd1b9Wy08a6K1Z1DZc4aXABUN5TKjkYhz7UKmo=
golang.org/x/crypto v0.0.0-20200420201142-3c4aac89819a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200422194213-44a606286825 h1:dSChiwOTvzwbHFTMq2l6uRardHH7/E6SqEkqccinS/o=
golang.org/x/crypto v0.0.0-20200422194213-44a606286825/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5 h1:Q7tZBpemrlsc2I7IyODzhtallWRSm4Q0d09pL6XbQtU=
golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200427165652-729f1e841bcc h1:ZGI/fILM2+ueot/UixBSoj9188jCAxVHEZEGhqq67I4=
golang.org/x/crypto v0.0.0-20200427165652-729f1e841bcc/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 h1:IaQbIIB2X/Mp/DKctl6ROxz1KyMlKp4uyvL6+kQ7C88=
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -617,6 +579,7 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
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=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ=
@ -656,13 +619,10 @@ golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200409092240-59c9f1ba88fa h1:mQTN3ECqfsViCNBgq+A40vdwhkGykrrQlYe3mPj6BoU=
golang.org/x/sys v0.0.0-20200409092240-59c9f1ba88fa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f h1:gWF768j/LaZugp8dyS4UwsslYCYz9XgFxvlgsn0n9H8=
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
@ -718,6 +678,7 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.0 h1:Tfd7cKwKbFRsI8RMAD3oqqw7JPFRrvFlOsfbgVkjOOw=
google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
@ -781,8 +742,6 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
src.techknowlogick.com/xgo v0.0.0-20200408234745-bb0faa361273 h1:dE6ry9rVwDn3soD4wPCXqEG60AZTuhniZzHdnj3c+74=
src.techknowlogick.com/xgo v0.0.0-20200408234745-bb0faa361273/go.mod h1:31CE1YKtDOrKTk9PSnjTpe6YbO6W/0LTYZ1VskL09oU=
src.techknowlogick.com/xgo v0.0.0-20200514233805-209a5cf70012 h1:k1/qGRpsaGka4IHT2UIUdPqM6+oo3O7+8S3ACN2kJZQ=
src.techknowlogick.com/xgo v0.0.0-20200514233805-209a5cf70012/go.mod h1:31CE1YKtDOrKTk9PSnjTpe6YbO6W/0LTYZ1VskL09oU=
src.techknowlogick.com/xormigrate v1.2.0 h1:bq9JaI48bxB+OddMghicjmV7sGmBUogJq4HmTN0DOcw=

View File

@ -112,6 +112,11 @@ const (
AvatarProvider Key = `avatar.provider`
AvatarGravaterExpiration Key = `avatar.gravatarexpiration`
BackgroundsEnabled Key = `backgrounds.enabled`
BackgroundsUnsplashEnabled Key = `backgrounds.providers.unsplash.enabled`
BackgroundsUnsplashAccessToken Key = `backgrounds.providers.unsplash.accesstoken`
BackgroundsUnsplashApplicationID Key = `backgrounds.providers.unsplash.applicationid`
)
// GetString returns a string config value
@ -243,6 +248,9 @@ func InitDefaultConfig() {
// Avatar
AvatarProvider.setDefault("gravatar")
AvatarGravaterExpiration.setDefault(3600)
// List Backgrounds
BackgroundsEnabled.setDefault(false)
BackgroundsUnsplashEnabled.setDefault(false)
}
// InitConfig initializes the config, sets defaults etc.

View File

@ -50,3 +50,20 @@ func IsErrFileIsTooLarge(err error) bool {
_, ok := err.(ErrFileIsTooLarge)
return ok
}
// ErrFileIsNotUnsplashFile defines an error where a file is not downloaded from unsplash.
// Used in cases whenever unsplash information about a file is requested, but the file was not downloaded from unsplash.
type ErrFileIsNotUnsplashFile struct {
FileID int64
}
// Error is the error implementation of ErrFileIsNotUnsplashFile
func (err ErrFileIsNotUnsplashFile) Error() string {
return fmt.Sprintf("file was not downloaded from unsplash [FileID: %d]", err.FileID)
}
//IsErrFileIsNotUnsplashFile checks if an error is ErrFileIsNotUnsplashFile
func IsErrFileIsNotUnsplashFile(err error) bool {
_, ok := err.(ErrFileIsNotUnsplashFile)
return ok
}

View File

@ -0,0 +1,47 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package migration
import (
"src.techknowlogick.com/xormigrate"
"xorm.io/xorm"
)
type unsplashPhoto20200524221534 struct {
ID int64 `xorm:"autoincr not null unique pk"`
FileID int64 `xorm:"not null"`
UnsplashID string `xorm:"varchar(50)"`
Author string `xorm:"text"`
AuthorName string `xorm:"text"`
}
func (u unsplashPhoto20200524221534) TableName() string {
return "unsplash_photos"
}
func init() {
migrations = append(migrations, &xormigrate.Migration{
ID: "20200524221534",
Description: "Create unsplash photo table",
Migrate: func(tx *xorm.Engine) error {
return tx.Sync2(unsplashPhoto20200524221534{})
},
Rollback: func(tx *xorm.Engine) error {
return tx.DropTables(unsplashPhoto20200524221534{})
},
})
}

View File

@ -0,0 +1,43 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package migration
import (
"src.techknowlogick.com/xormigrate"
"xorm.io/xorm"
)
type list20200524224611 struct {
BackgroundFileID int64 `xorm:"null" json:"-"`
}
func (s list20200524224611) TableName() string {
return "list"
}
func init() {
migrations = append(migrations, &xormigrate.Migration{
ID: "20200524224611",
Description: "Add background file id property to list",
Migrate: func(tx *xorm.Engine) error {
return tx.Sync2(list20200524224611{})
},
Rollback: func(tx *xorm.Engine) error {
return tx.DropTables(list20200524224611{})
},
})
}

View File

@ -17,6 +17,7 @@
package models
import (
"code.vikunja.io/api/pkg/files"
"code.vikunja.io/api/pkg/metrics"
"code.vikunja.io/api/pkg/timeutil"
"code.vikunja.io/api/pkg/user"
@ -51,6 +52,11 @@ type List struct {
// Whether or not a list is archived.
IsArchived bool `xorm:"not null default false" json:"is_archived" query:"is_archived"`
// The id of the file this list has set as background
BackgroundFileID int64 `xorm:"null" json:"-"`
// Holds extra information about the background set since some background providers require attribution or similar. If not null, the background can be accessed at /lists/{listID}/background
BackgroundInformation interface{} `xorm:"-" json:"background_information"`
// A timestamp when this list was created. You cannot change this value.
Created timeutil.TimeStamp `xorm:"created not null" json:"created"`
// A timestamp when this list was last updated. You cannot change this value.
@ -166,6 +172,16 @@ func (l *List) ReadOne() (err error) {
l.IsArchived = true
}
}
// Get any background information if there is one set
if l.BackgroundFileID != 0 {
// Currently unsplash only
l.BackgroundInformation, err = GetUnsplashPhotoByFileID(l.BackgroundFileID)
if err != nil && !files.IsErrFileIsNotUnsplashFile(err) {
return
}
}
return nil
}
@ -560,3 +576,16 @@ func (l *List) Delete() (err error) {
_, err = x.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) {
l := &List{
ID: listID,
BackgroundFileID: background.ID,
}
_, err = x.
Where("id = ?", l.ID).
Cols("background_file_id").
Update(l)
return
}

64
pkg/models/unsplash.go Normal file
View File

@ -0,0 +1,64 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package models
import "code.vikunja.io/api/pkg/files"
// 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.
// UnsplashPhoto is an unsplash photo in the db
type UnsplashPhoto struct {
ID int64 `xorm:"autoincr not null unique pk" json:"id,omitempty"`
FileID int64 `xorm:"not null" json:"-"`
UnsplashID string `xorm:"varchar(50)" json:"unsplash_id"`
Author string `xorm:"text" json:"author"`
AuthorName string `xorm:"text" json:"author_name"`
}
// TableName contains the table name for an unsplash photo
func (u *UnsplashPhoto) TableName() string {
return "unsplash_photos"
}
// Save persists an unsplash photo to the db
func (u *UnsplashPhoto) Save() error {
_, err := x.Insert(u)
return err
}
// GetUnsplashPhotoByFileID returns an unsplash photo by its saved file id
func GetUnsplashPhotoByFileID(fileID int64) (u *UnsplashPhoto, err error) {
u = &UnsplashPhoto{}
exists, err := x.Where("file_id = ?", fileID).Get(u)
if err != nil {
return
}
if !exists {
return nil, files.ErrFileIsNotUnsplashFile{FileID: fileID}
}
return
}
// RemoveUnsplashPhoto removes an unsplash photo from the db
func RemoveUnsplashPhoto(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{})
return
}

View File

@ -0,0 +1,39 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package background
import (
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/web"
)
// Image represents an image which can be used as a list background
type Image struct {
ID string `json:"id"`
URL string `json:"url"`
Thumb string `json:"thumb"`
// This can be used to supply extra information from an image provider to clients
Info interface{} `json:"info,omitempty"`
}
// 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)
// 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)
}

View File

@ -0,0 +1,157 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package handler
import (
"code.vikunja.io/api/pkg/files"
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/modules/background"
"code.vikunja.io/api/pkg/modules/background/unsplash"
v1 "code.vikunja.io/api/pkg/routes/api/v1"
"code.vikunja.io/web/handler"
"github.com/labstack/echo/v4"
"net/http"
"strconv"
)
// BackgroundProvider represents a thing which holds a background provider
// Lets us get a new fresh provider every time we need one.
type BackgroundProvider struct {
Provider func() background.Provider
}
// SearchBackgrounds is the web handler to search for backgrounds
func (bp *BackgroundProvider) SearchBackgrounds(c echo.Context) error {
p := bp.Provider()
err := c.Bind(p)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "No or invalid model provided: "+err.Error())
}
search := c.QueryParam("s")
var page int64 = 1
pg := c.QueryParam("p")
if pg != "" {
page, err = strconv.ParseInt(pg, 10, 64)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid page number: "+err.Error())
}
}
result, err := p.Search(search, page)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "An error occurred: "+err.Error())
}
return c.JSON(http.StatusOK, result)
}
// SetBackground sets an Image as list background
func (bp *BackgroundProvider) SetBackground(c echo.Context) error {
auth, err := v1.GetAuthFromClaims(c)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid auth token: "+err.Error())
}
p := bp.Provider()
listID, err := strconv.ParseInt(c.Param("list"), 10, 64)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid list ID: "+err.Error())
}
// Check if the user has the right to change the list background
list := &models.List{ID: listID}
can, err := list.CanUpdate(auth)
if err != nil {
return handler.HandleHTTPError(err, c)
}
if !can {
log.Infof("Tried to update list background of list %d while not having the rights for it (User: %v)", listID, auth)
return echo.NewHTTPError(http.StatusForbidden)
}
image := &background.Image{}
err = c.Bind(image)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "No or invalid model provided: "+err.Error())
}
err = p.Set(image, list, auth)
if err != nil {
return handler.HandleHTTPError(err, c)
}
return c.JSON(http.StatusOK, list)
}
// GetListBackground serves a previously set background from a list
// It has no knowledge of the provider that was responsible for setting the background.
// @Summary Get the list background
// @Description Get the list background of a specific list. **Returns json on error.**
// @tags list
// @Produce octet-stream
// @Param id path int true "List ID"
// @Security JWTKeyAuth
// @Success 200 {} string "The list background file."
// @Failure 403 {object} models.Message "No access to this list."
// @Failure 404 {object} models.Message "The list does not exist."
// @Failure 500 {object} models.Message "Internal error"
// @Router /lists/{id}/background [get]
func GetListBackground(c echo.Context) error {
auth, err := v1.GetAuthFromClaims(c)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid auth token: "+err.Error())
}
listID, err := strconv.ParseInt(c.Param("list"), 10, 64)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid list ID: "+err.Error())
}
// Check if a background for this list exists + Rights
list := &models.List{ID: listID}
can, err := list.CanRead(auth)
if err != nil {
return handler.HandleHTTPError(err, c)
}
if !can {
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 {
return echo.NotFoundHandler(c)
}
// Get the file
bgFile := &files.File{
ID: list.BackgroundFileID,
}
if err := bgFile.LoadFileByID(); err != nil {
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)
// Serve the file
return c.Stream(http.StatusOK, "image/jpg", bgFile.File)
}

View File

@ -0,0 +1,241 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package unsplash
import (
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/files"
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/modules/background"
"code.vikunja.io/web"
"encoding/json"
"net/http"
"strconv"
)
// Provider represents an unsplash image provider
type Provider struct {
}
// SearchResult is a search result from unsplash's api
type SearchResult struct {
Total int `json:"total"`
TotalPages int `json:"total_pages"`
Results []*Photo `json:"results"`
}
// Photo represents an unpslash photo as returned by their api
type Photo struct {
ID string `json:"id"`
CreatedAt string `json:"created_at"`
Width int `json:"width"`
Height int `json:"height"`
Color string `json:"color"`
Description string `json:"description"`
User struct {
Username string `json:"username"`
Name string `json:"name"`
} `json:"user"`
Urls struct {
Raw string `json:"raw"`
Full string `json:"full"`
Regular string `json:"regular"`
Small string `json:"small"`
Thumb string `json:"thumb"`
} `json:"urls"`
Links struct {
Self string `json:"self"`
HTML string `json:"html"`
Download string `json:"download"`
} `json:"links"`
}
// Very simple caching method - pretty much only used to retain information when saving an image
// FIXME: Should use a proper cache
var photos map[string]*Photo
func init() {
photos = make(map[string]*Photo)
}
func doGet(url string, result interface{}) (err error) {
req, err := http.NewRequest("GET", "https://api.unsplash.com/"+url, nil)
if err != nil {
return
}
req.Header.Add("Authorization", "Client-ID "+config.BackgroundsUnsplashAccessToken.GetString())
hc := http.Client{}
resp, err := hc.Do(req)
if err != nil {
return
}
err = json.NewDecoder(resp.Body).Decode(result)
return
}
// Search is the implementation to search on unsplash
// @Summary Search for a background from unsplash
// @Description Search for a list background from unsplash
// @tags list
// @Produce json
// @Security JWTKeyAuth
// @Param s query string false "Search backgrounds from unsplash with this search term."
// @Param p query int false "The page number. Used for pagination. If not provided, the first page of results is returned."
// @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) {
// If we don't have a search query, return results from the unsplash featured collection
if search == "" {
collectionResult := []*Photo{}
err = doGet("collections/317099/photos?page="+strconv.FormatInt(page, 10)+"&per_page=25&order_by=latest", &collectionResult)
if err != nil {
return
}
result = []*background.Image{}
for _, p := range collectionResult {
result = append(result, &background.Image{
ID: p.ID,
URL: p.Urls.Raw,
Thumb: p.Urls.Thumb,
Info: &models.UnsplashPhoto{
UnsplashID: p.ID,
Author: p.User.Username,
AuthorName: p.User.Name,
},
})
photos[p.ID] = p
}
return
}
searchResult := &SearchResult{}
err = doGet("search/photos?query="+search+"&page="+strconv.FormatInt(page, 10)+"&per_page=25", &searchResult)
if err != nil {
return
}
result = []*background.Image{}
for _, p := range searchResult.Results {
result = append(result, &background.Image{
ID: p.ID,
URL: p.Urls.Raw,
Thumb: p.Urls.Thumb,
})
photos[p.ID] = p
}
return
}
// Set sets an unsplash photo as list background
// @Summary Set an unsplash photo as list background
// @Description Sets a photo from unsplash as list background.1
// @tags list
// @Accept json
// @Produce json
// @Security JWTKeyAuth
// @Param id path int true "List ID"
// @Param list body background.Image true "The image you want to set as background"
// @Success 200 {object} models.List "The background has been successfully set."
// @Failure 400 {object} code.vikunja.io/web.HTTPError "Invalid image object provided."
// @Failure 403 {object} code.vikunja.io/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) {
// Find the photo
var photo *Photo
var exists bool
photo, exists = photos[image.ID]
if !exists {
log.Debugf("Image information for unsplash photo %s not cached, requesting from unsplash...", image.ID)
photo = &Photo{}
err = doGet("photos/"+image.ID, photo)
if err != nil {
return
}
}
// Download the photo from unsplash
// The parameters crop the image to a max width of 2560 and a max height of 2048 to save bandwidth and storage.
resp, err := http.Get(photo.Urls.Raw + "&w=2560&h=2048&q=90")
if err != nil {
return
}
defer resp.Body.Close()
log.Debugf("Downloaded Unsplash Photo %s", image.ID)
// Save it as a file in vikunja
file, err := files.Create(resp.Body, "", 0, auth)
if err != nil {
return
}
// Remove the old background if one exists
if list.BackgroundFileID != 0 {
file := files.File{ID: list.BackgroundFileID}
if err := file.Delete(); err != nil {
return err
}
if err := models.RemoveUnsplashPhoto(list.BackgroundFileID); err != nil {
return err
}
}
// Save the relation that we got it from unsplash
unsplashPhoto := &models.UnsplashPhoto{
FileID: file.ID,
UnsplashID: image.ID,
Author: photo.User.Username,
AuthorName: photo.User.Name,
}
err = unsplashPhoto.Save()
if err != nil {
return
}
log.Debugf("Saved unsplash photo %s as file %d with new entry %d", image.ID, file.ID, unsplashPhoto.ID)
// Set the file in the list
list.BackgroundFileID = file.ID
list.BackgroundInformation = unsplashPhoto
// Set it as the list background
return models.SetListBackground(list.ID, file)
}
// Pingback pings the unsplash api if an unsplash photo has been accessed.
func Pingback(f *files.File) {
// Check if the file is actually downloaded from unsplash
unsplashPhoto, err := models.GetUnsplashPhotoByFileID(f.ID)
if err != nil {
log.Errorf("Unsplash Pingback: %s", err.Error())
}
// Do the ping
if _, err := http.Get("https://views.unsplash.com/v?app_id=" + config.BackgroundsUnsplashApplicationID.GetString() + "&photo_id=" + unsplashPhoto.UnsplashID); err != nil {
log.Errorf("Unsplash Pingback Failed: %s", err.Error())
}
log.Debugf("Pinged unsplash for photo %s", unsplashPhoto.UnsplashID)
}

File diff suppressed because one or more lines are too long

View File

@ -26,14 +26,15 @@ import (
)
type vikunjaInfos struct {
Version string `json:"version"`
FrontendURL string `json:"frontend_url"`
Motd string `json:"motd"`
LinkSharingEnabled bool `json:"link_sharing_enabled"`
MaxFileSize string `json:"max_file_size"`
RegistrationEnabled bool `json:"registration_enabled"`
AvailableMigrators []string `json:"available_migrators"`
TaskAttachmentsEnabled bool `json:"task_attachments_enabled"`
Version string `json:"version"`
FrontendURL string `json:"frontend_url"`
Motd string `json:"motd"`
LinkSharingEnabled bool `json:"link_sharing_enabled"`
MaxFileSize string `json:"max_file_size"`
RegistrationEnabled bool `json:"registration_enabled"`
AvailableMigrators []string `json:"available_migrators"`
TaskAttachmentsEnabled bool `json:"task_attachments_enabled"`
EnabledBackgroundProviders []string `json:"enabled_background_providers"`
}
// Info is the handler to get infos about this vikunja instance
@ -44,7 +45,7 @@ type vikunjaInfos struct {
// @Success 200 {object} v1.vikunjaInfos
// @Router /info [get]
func Info(c echo.Context) error {
infos := vikunjaInfos{
info := vikunjaInfos{
Version: version.Version,
FrontendURL: config.ServiceFrontendurl.GetString(),
Motd: config.ServiceMotd.GetString(),
@ -57,12 +58,18 @@ func Info(c echo.Context) error {
// Migrators
if config.MigrationWunderlistEnable.GetBool() {
m := &wunderlist.Migration{}
infos.AvailableMigrators = append(infos.AvailableMigrators, m.Name())
info.AvailableMigrators = append(info.AvailableMigrators, m.Name())
}
if config.MigrationTodoistEnable.GetBool() {
m := &todoist.Migration{}
infos.AvailableMigrators = append(infos.AvailableMigrators, m.Name())
info.AvailableMigrators = append(info.AvailableMigrators, m.Name())
}
return c.JSON(http.StatusOK, infos)
if config.BackgroundsEnabled.GetBool() {
if config.BackgroundsUnsplashEnabled.GetBool() {
info.EnabledBackgroundProviders = append(info.EnabledBackgroundProviders, "unsplash")
}
}
return c.JSON(http.StatusOK, info)
}

View File

@ -47,6 +47,9 @@ import (
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/modules/background"
backgroundHandler "code.vikunja.io/api/pkg/modules/background/handler"
"code.vikunja.io/api/pkg/modules/background/unsplash"
"code.vikunja.io/api/pkg/modules/migration"
migrationHandler "code.vikunja.io/api/pkg/modules/migration/handler"
"code.vikunja.io/api/pkg/modules/migration/todoist"
@ -444,6 +447,20 @@ func registerAPIRoutes(a *echo.Group) {
}
todoistMigrationHandler.RegisterRoutes(m)
}
// List Backgrounds
if config.BackgroundsEnabled.GetBool() {
a.GET("/lists/:list/background", backgroundHandler.GetListBackground)
if config.BackgroundsUnsplashEnabled.GetBool() {
unsplashBackgroundProvider := &backgroundHandler.BackgroundProvider{
Provider: func() background.Provider {
return &unsplash.Provider{}
},
}
a.GET("/backgrounds/unsplash/search", unsplashBackgroundProvider.SearchBackgrounds)
a.POST("/lists/:list/backgrounds/unsplash", unsplashBackgroundProvider.SetBackground)
}
}
}
func registerCalDavRoutes(c *echo.Group) {

View File

@ -1,3 +1,5 @@
module github.com/hashicorp/hcl
require github.com/davecgh/go-spew v1.1.1
go 1.13

View File

@ -1,3 +1,5 @@
module github.com/spf13/afero
require golang.org/x/text v0.3.0
go 1.13