Compare commits

...

21 Commits

Author SHA1 Message Date
b969854592 Prevent deleted users from logging in 2020-07-24 12:45:06 +02:00
4e21b41ba6 On node save, detect changes in the download attr
If a change is detected, mark the previous file as _deleted. This
does not delete any file on the system or database document.
2019-08-28 21:41:05 +02:00
db9cb09c68 Further tweaks to notifications layout 2019-07-02 14:23:50 +02:00
d424febfeb Improve comments parsing
As part of #108 - dillo_post now becomes post, and the title of a
post, or the parsed content of a comment are displayed.
2019-06-27 01:22:45 +02:00
defa5abd18 PEP8 formatting 2019-06-27 01:17:15 +02:00
26858f01b7 Update package-lock.json 2019-05-16 19:23:10 +02:00
3bb35d0ab8 Merge branch 'master' into dillo 2019-04-24 22:24:23 +02:00
38e4c7c937 Merge branch 'master' into dillo
# Conflicts:
#	pillar/api/nodes/__init__.py
2019-04-20 22:26:51 +02:00
312b0a276a Merge branch 'master' into dillo 2019-04-08 23:24:56 +02:00
32361a0e70 Merge branch 'master' into dillo 2019-04-01 18:53:28 +02:00
b26402412b UI: Vertically center badges under comment avatar. 2019-03-21 01:04:21 +01:00
d5f2996704 Remove package-lock.json 2019-03-20 14:19:36 +01:00
d1143bad3e Merge branch 'master' into dillo 2019-03-12 20:27:54 +01:00
c64e24d80d Merge branch 'master' into dillo 2019-03-12 14:28:00 +01:00
446d31d807 Merge branch 'master' into dillo 2019-03-11 19:24:01 +01:00
145d512aa7 UI: Fix emojis margin-top on node description utility. 2019-03-11 03:13:01 +01:00
bf63148852 CSS: Remove primary buttons gradient.
Doesn't always look nice, fallback to default bootstrap primary color instead.
2019-03-11 01:32:17 +01:00
812d911195 Merge branch 'master' into dillo 2019-02-20 23:26:04 +01:00
f0031d44b2 Merge branch 'master' into dillo 2019-02-03 15:51:22 +01:00
5660f4b606 Turn log warning message into debug 2019-02-03 15:50:48 +01:00
6b6a5310f8 Temp fixes for Dillo integration 2019-02-01 19:49:58 +01:00
8 changed files with 12839 additions and 12798 deletions

132
package-lock.json generated
View File

@ -4058,14 +4058,14 @@
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
}, },
"fsevents": { "fsevents": {
"version": "1.2.4", "version": "1.2.9",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz",
"integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==", "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==",
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
"nan": "^2.9.2", "nan": "^2.12.1",
"node-pre-gyp": "^0.10.0" "node-pre-gyp": "^0.12.0"
}, },
"dependencies": { "dependencies": {
"abbrev": { "abbrev": {
@ -4087,7 +4087,7 @@
"optional": true "optional": true
}, },
"are-we-there-yet": { "are-we-there-yet": {
"version": "1.1.4", "version": "1.1.5",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true, "optional": true,
@ -4113,7 +4113,7 @@
} }
}, },
"chownr": { "chownr": {
"version": "1.0.1", "version": "1.1.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true "optional": true
@ -4143,16 +4143,16 @@
"optional": true "optional": true
}, },
"debug": { "debug": {
"version": "2.6.9", "version": "4.1.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
"ms": "2.0.0" "ms": "^2.1.1"
} }
}, },
"deep-extend": { "deep-extend": {
"version": "0.5.1", "version": "0.6.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true "optional": true
@ -4201,7 +4201,7 @@
} }
}, },
"glob": { "glob": {
"version": "7.1.2", "version": "7.1.3",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true, "optional": true,
@ -4221,12 +4221,12 @@
"optional": true "optional": true
}, },
"iconv-lite": { "iconv-lite": {
"version": "0.4.21", "version": "0.4.24",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
"safer-buffer": "^2.1.0" "safer-buffer": ">= 2.1.2 < 3"
} }
}, },
"ignore-walk": { "ignore-walk": {
@ -4291,17 +4291,17 @@
"optional": true "optional": true
}, },
"minipass": { "minipass": {
"version": "2.2.4", "version": "2.3.5",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
"safe-buffer": "^5.1.1", "safe-buffer": "^5.1.2",
"yallist": "^3.0.0" "yallist": "^3.0.0"
} }
}, },
"minizlib": { "minizlib": {
"version": "1.1.0", "version": "1.2.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true, "optional": true,
@ -4319,35 +4319,35 @@
} }
}, },
"ms": { "ms": {
"version": "2.0.0", "version": "2.1.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true "optional": true
}, },
"needle": { "needle": {
"version": "2.2.0", "version": "2.3.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
"debug": "^2.1.2", "debug": "^4.1.0",
"iconv-lite": "^0.4.4", "iconv-lite": "^0.4.4",
"sax": "^1.2.4" "sax": "^1.2.4"
} }
}, },
"node-pre-gyp": { "node-pre-gyp": {
"version": "0.10.0", "version": "0.12.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
"detect-libc": "^1.0.2", "detect-libc": "^1.0.2",
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"needle": "^2.2.0", "needle": "^2.2.1",
"nopt": "^4.0.1", "nopt": "^4.0.1",
"npm-packlist": "^1.1.6", "npm-packlist": "^1.1.6",
"npmlog": "^4.0.2", "npmlog": "^4.0.2",
"rc": "^1.1.7", "rc": "^1.2.7",
"rimraf": "^2.6.1", "rimraf": "^2.6.1",
"semver": "^5.3.0", "semver": "^5.3.0",
"tar": "^4" "tar": "^4"
@ -4364,13 +4364,13 @@
} }
}, },
"npm-bundled": { "npm-bundled": {
"version": "1.0.3", "version": "1.0.6",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true "optional": true
}, },
"npm-packlist": { "npm-packlist": {
"version": "1.1.10", "version": "1.4.1",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true, "optional": true,
@ -4447,12 +4447,12 @@
"optional": true "optional": true
}, },
"rc": { "rc": {
"version": "1.2.7", "version": "1.2.8",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
"deep-extend": "^0.5.1", "deep-extend": "^0.6.0",
"ini": "~1.3.0", "ini": "~1.3.0",
"minimist": "^1.2.0", "minimist": "^1.2.0",
"strip-json-comments": "~2.0.1" "strip-json-comments": "~2.0.1"
@ -4482,16 +4482,16 @@
} }
}, },
"rimraf": { "rimraf": {
"version": "2.6.2", "version": "2.6.3",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
"glob": "^7.0.5" "glob": "^7.1.3"
} }
}, },
"safe-buffer": { "safe-buffer": {
"version": "5.1.1", "version": "5.1.2",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true "optional": true
@ -4509,7 +4509,7 @@
"optional": true "optional": true
}, },
"semver": { "semver": {
"version": "5.5.0", "version": "5.7.0",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true "optional": true
@ -4562,17 +4562,17 @@
"optional": true "optional": true
}, },
"tar": { "tar": {
"version": "4.4.1", "version": "4.4.8",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
"chownr": "^1.0.1", "chownr": "^1.1.1",
"fs-minipass": "^1.2.5", "fs-minipass": "^1.2.5",
"minipass": "^2.2.4", "minipass": "^2.3.4",
"minizlib": "^1.1.0", "minizlib": "^1.1.1",
"mkdirp": "^0.5.0", "mkdirp": "^0.5.0",
"safe-buffer": "^5.1.1", "safe-buffer": "^5.1.2",
"yallist": "^3.0.2" "yallist": "^3.0.2"
} }
}, },
@ -4583,12 +4583,12 @@
"optional": true "optional": true
}, },
"wide-align": { "wide-align": {
"version": "1.1.2", "version": "1.1.3",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
"string-width": "^1.0.2" "string-width": "^1.0.2 || 2"
} }
}, },
"wrappy": { "wrappy": {
@ -4598,7 +4598,7 @@
"optional": true "optional": true
}, },
"yallist": { "yallist": {
"version": "3.0.2", "version": "3.0.3",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true "optional": true
@ -4606,9 +4606,9 @@
} }
}, },
"fstream": { "fstream": {
"version": "1.0.11", "version": "1.0.12",
"resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz",
"integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==",
"dev": true, "dev": true,
"requires": { "requires": {
"graceful-fs": "^4.1.2", "graceful-fs": "^4.1.2",
@ -5528,9 +5528,9 @@
} }
}, },
"chalk": { "chalk": {
"version": "2.4.1", "version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"ansi-styles": "^3.2.1", "ansi-styles": "^3.2.1",
@ -5538,12 +5538,6 @@
"supports-color": "^5.3.0" "supports-color": "^5.3.0"
} }
}, },
"replace-ext": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz",
"integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=",
"dev": true
},
"strip-ansi": { "strip-ansi": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
@ -8502,12 +8496,6 @@
"integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=", "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=",
"dev": true "dev": true
}, },
"lodash.mergewith": {
"version": "4.6.1",
"resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz",
"integrity": "sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ==",
"dev": true
},
"lodash.sortby": { "lodash.sortby": {
"version": "4.7.0", "version": "4.7.0",
"resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
@ -8943,9 +8931,9 @@
"integrity": "sha512-j4rEyZKCRinGaSiBxPx9YD9B782TMPHPOlKyaMY07vIGTNYg4ouCEBvL6zX9Hh1k1fKZ5ZF3S7c+XVk6PB+Igw==" "integrity": "sha512-j4rEyZKCRinGaSiBxPx9YD9B782TMPHPOlKyaMY07vIGTNYg4ouCEBvL6zX9Hh1k1fKZ5ZF3S7c+XVk6PB+Igw=="
}, },
"nan": { "nan": {
"version": "2.11.0", "version": "2.13.2",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.11.0.tgz", "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz",
"integrity": "sha512-F4miItu2rGnV2ySkXOQoA8FKz/SR2Q2sWP0sbTxNxz/tuokeC8WxOhPMcwi0qIyGtVn/rrSeLbvVkznqCdwYnw==", "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==",
"dev": true "dev": true
}, },
"nanomatch": { "nanomatch": {
@ -9057,9 +9045,9 @@
} }
}, },
"node-sass": { "node-sass": {
"version": "4.11.0", "version": "4.12.0",
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.11.0.tgz", "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.12.0.tgz",
"integrity": "sha512-bHUdHTphgQJZaF1LASx0kAviPH7sGlcyNhWade4eVIpFp6tsn7SV8xNMTbsQFpEV9VXpnwTTnNYlfsZXgGgmkA==", "integrity": "sha512-A1Iv4oN+Iel6EPv77/HddXErL2a+gZ4uBeZUy+a8O35CFYTXhgA8MgLCWBtwpGZdCvTvQ9d+bQxX/QC36GDPpQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"async-foreach": "^0.1.3", "async-foreach": "^0.1.3",
@ -9069,12 +9057,10 @@
"get-stdin": "^4.0.1", "get-stdin": "^4.0.1",
"glob": "^7.0.3", "glob": "^7.0.3",
"in-publish": "^2.0.0", "in-publish": "^2.0.0",
"lodash.assign": "^4.2.0", "lodash": "^4.17.11",
"lodash.clonedeep": "^4.3.2",
"lodash.mergewith": "^4.6.0",
"meow": "^3.7.0", "meow": "^3.7.0",
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"nan": "^2.10.0", "nan": "^2.13.2",
"node-gyp": "^3.8.0", "node-gyp": "^3.8.0",
"npmlog": "^4.0.0", "npmlog": "^4.0.0",
"request": "^2.88.0", "request": "^2.88.0",
@ -10509,6 +10495,12 @@
"is-finite": "^1.0.0" "is-finite": "^1.0.0"
} }
}, },
"replace-ext": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz",
"integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=",
"dev": true
},
"replace-homedir": { "replace-homedir": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-1.0.0.tgz", "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-1.0.0.tgz",
@ -11626,13 +11618,13 @@
} }
}, },
"tar": { "tar": {
"version": "2.2.1", "version": "2.2.2",
"resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz",
"integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==",
"dev": true, "dev": true,
"requires": { "requires": {
"block-stream": "*", "block-stream": "*",
"fstream": "^1.0.2", "fstream": "^1.0.12",
"inherits": "2" "inherits": "2"
} }
}, },

View File

@ -1,4 +1,5 @@
import logging import logging
from html.parser import HTMLParser
from flask import request, current_app from flask import request, current_app
from pillar.api.utils import gravatar from pillar.api.utils import gravatar
@ -7,6 +8,15 @@ from pillar.auth import current_user
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class CommentHTMLParser(HTMLParser):
def __init__(self):
HTMLParser.__init__(self)
self.data = []
def handle_data(self, data):
self.data.append(data)
def notification_parse(notification): def notification_parse(notification):
activities_collection = current_app.data.driver.db['activities'] activities_collection = current_app.data.driver.db['activities']
activities_subscriptions_collection = \ activities_subscriptions_collection = \
@ -30,9 +40,14 @@ def notification_parse(notification):
object_type = 'comment' object_type = 'comment'
object_name = '' object_name = ''
object_id = activity['object'] object_id = activity['object']
context_object_type = node['parent']['node_type']
# If node_type is 'dillo_post', just call it 'post'
node_type = 'post' if context_object_type.endswith('_post') else \
context_object_type
if node['parent']['user'] == current_user.user_id: if node['parent']['user'] == current_user.user_id:
owner = "your {0}".format(node['parent']['node_type']) owner = f"your {node_type}"
else: else:
parent_comment_user = users_collection.find_one( parent_comment_user = users_collection.find_one(
{'_id': node['parent']['user']}) {'_id': node['parent']['user']})
@ -40,10 +55,22 @@ def notification_parse(notification):
user_name = 'their' user_name = 'their'
else: else:
user_name = "{0}'s".format(parent_comment_user['username']) user_name = "{0}'s".format(parent_comment_user['username'])
owner = "{0} {1}".format(user_name, node['parent']['node_type'])
context_object_type = node['parent']['node_type'] owner = f"{user_name} {node_type}"
context_object_name = owner
context_object_name = f"{node['parent']['name'][:50]}..."
if context_object_type == 'comment':
# Parse the comment content, which might be HTML and extract
# some text from it.
parser = CommentHTMLParser()
# Trim the comment content to 50 chars, the parser will handle it
parser.feed(node['properties']['content'][:50])
try:
comment_content = parser.data[0]
except KeyError:
comment_content = '...'
# Trim the parsed text down to 15 charss
context_object_name = f"{comment_content[:50]}..."
context_object_id = activity['context_object'] context_object_id = activity['context_object']
if activity['verb'] == 'replied': if activity['verb'] == 'replied':
action = 'replied to' action = 'replied to'
@ -52,13 +79,15 @@ def notification_parse(notification):
else: else:
action = activity['verb'] action = activity['verb']
action = f'{action} {owner}'
lookup = { lookup = {
'user': current_user.user_id, 'user': current_user.user_id,
'context_object_type': 'node', 'context_object_type': 'node',
'context_object': context_object_id, 'context_object': context_object_id,
} }
subscription = activities_subscriptions_collection.find_one(lookup) subscription = activities_subscriptions_collection.find_one(lookup)
if subscription and subscription['notifications']['web'] == True: if subscription and subscription['notifications']['web'] is True:
is_subscribed = True is_subscribed = True
else: else:
is_subscribed = False is_subscribed = False

View File

@ -13,7 +13,8 @@ from pillar.web.utils import pretty_date
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
blueprint = Blueprint('nodes_api', __name__) blueprint = Blueprint('nodes_api', __name__)
ROLES_FOR_SHARING = ROLES_FOR_COMMENTING = {'subscriber', 'demo'} # TODO(fsiddi) Propose changes to make commenting roles a configuration value.
ROLES_FOR_SHARING = ROLES_FOR_COMMENTING = set()
@blueprint.route('/<node_id>/share', methods=['GET', 'POST']) @blueprint.route('/<node_id>/share', methods=['GET', 'POST'])

View File

@ -69,6 +69,22 @@ def before_replacing_node(item, original):
check_permissions('nodes', original, 'PUT') check_permissions('nodes', original, 'PUT')
update_file_name(item) update_file_name(item)
# XXX Dillo specific feature (for Graphicall)
if 'download' in original['properties']:
# Check if the file referenced in the download property was updated.
# If so, mark the old file as deleted. A cronjob will take care of
# removing the actual file based on the _delete status of file docs.
original_file_id = original['properties']['download']
new_file_id = item['properties']['download']
if original_file_id == new_file_id:
return
# Mark the original file as _deleted
files = current_app.data.driver.db['files']
files.update_one({'_id': original_file_id}, {'$set': {'_deleted': True}})
log.info('Marking file %s as _deleted' % original_file_id)
def after_replacing_node(item, original): def after_replacing_node(item, original):
"""Push an update to the Algolia index when a node item is updated. If the """Push an update to the Algolia index when a node item is updated. If the

View File

@ -53,7 +53,7 @@ def find_for_comment(project, node):
'_deleted': {'$ne': True} '_deleted': {'$ne': True}
}}, api=api) }}, api=api)
except ResourceNotFound: except ResourceNotFound:
log.warning( log.debug(
'url_for_node(node_id=%r): Unable to find parent node %r', 'url_for_node(node_id=%r): Unable to find parent node %r',
node['_id'], parent.parent) node['_id'], parent.parent)
raise ValueError('Unable to find parent node %r' % parent.parent) raise ValueError('Unable to find parent node %r' % parent.parent)

View File

@ -50,6 +50,7 @@ def iter_node_properties(node_type):
@functools.lru_cache(maxsize=1) @functools.lru_cache(maxsize=1)
def tag_choices() -> typing.List[typing.Tuple[str, str]]: def tag_choices() -> typing.List[typing.Tuple[str, str]]:
"""Return (value, label) tuples for the NODE_TAGS config setting.""" """Return (value, label) tuples for the NODE_TAGS config setting."""
#TODO(fsiddi) consider allowing tags based on custom_properties in the project.
tags = current_app.config.get('NODE_TAGS') or [] tags = current_app.config.get('NODE_TAGS') or []
return [(tag, tag.title()) for tag in tags] # (value, label) tuples return [(tag, tag.title()) for tag in tags] # (value, label) tuples
@ -70,9 +71,7 @@ def add_form_properties(form_class, node_type):
# Recursive call if detects a dict # Recursive call if detects a dict
field_type = schema_prop['type'] field_type = schema_prop['type']
if prop_name == 'tags' and field_type == 'list': if field_type == 'dict':
field = SelectMultipleField(choices=tag_choices())
elif field_type == 'dict':
assert prop_name == 'attachments' assert prop_name == 'attachments'
field = attachments.attachment_form_group_create(schema_prop) field = attachments.attachment_form_group_create(schema_prop)
elif field_type == 'list': elif field_type == 'list':

View File

@ -72,6 +72,9 @@ def oauth_callback(provider):
# Find or create user # Find or create user
user_info = {'id': oauth_user.id, 'email': oauth_user.email, 'full_name': ''} user_info = {'id': oauth_user.id, 'email': oauth_user.email, 'full_name': ''}
db_user = find_user_in_db(user_info, provider=provider) db_user = find_user_in_db(user_info, provider=provider)
if '_deleted' in db_user and db_user['_deleted'] is True:
log.debug('User has been deleted and will not be logge in')
return redirect(next_after_login)
db_id, status = upsert_user(db_user) db_id, status = upsert_user(db_user)
# TODO(Sybren): If the user doesn't have any badges, but the access token # TODO(Sybren): If the user doesn't have any badges, but the access token

View File

@ -137,6 +137,7 @@
& .nc-text & .nc-text
width: 90% width: 90%
white-space: normal
& .nc-date & .nc-date
display: block display: block