diff --git a/README_roadmap.md b/README_roadmap.md index 62b2ba3ba..bfc0ddc4f 100644 --- a/README_roadmap.md +++ b/README_roadmap.md @@ -1,22 +1,24 @@ -# Roadman +# Roadmap ## UX - * Change animation on buttons (themeable?) + * Minimize button shows different icons or highlighting + * Layout of buttons on person options screen -## Teams +## Groups - * Test groups + * Unit test for group creation * Groups can be defined as having particular roles/skills - * Templates for different group organizations -## Events +## Questions - * Events timeline - * Events appear on calendar - * Check compatibility with Mobilizon + * Still not implemented ideally + * Instance-only questions + * Active polls screen? + * Questions more integrated into overall organization ## Code - * Modularize daemon - * Move modules out of the daemon - * Make comment notes linking daemon functions to webinterface \ No newline at end of file + * More unit test coverage + * Break up large functions into smaller ones + * Architecture diagrams + * Code documentation? diff --git a/cwtch.py b/cwtch.py new file mode 100644 index 000000000..9619067f1 --- /dev/null +++ b/cwtch.py @@ -0,0 +1,92 @@ +__filename__ = "cwtch.py" +__author__ = "Bob Mottram" +__license__ = "AGPL3+" +__version__ = "1.2.0" +__maintainer__ = "Bob Mottram" +__email__ = "bob@freedombone.net" +__status__ = "Production" +__module_group__ = "Profile Metadata" + +import re + + +def getCwtchAddress(actorJson: {}) -> str: + """Returns cwtch address for the given actor + """ + if not actorJson.get('attachment'): + return '' + for propertyValue in actorJson['attachment']: + if not propertyValue.get('name'): + continue + if not propertyValue['name'].lower().startswith('cwtch'): + continue + if not propertyValue.get('type'): + continue + if not propertyValue.get('value'): + continue + if propertyValue['type'] != 'PropertyValue': + continue + propertyValue['value'] = propertyValue['value'].strip() + if len(propertyValue['value']) < 2: + continue + if '"' in propertyValue['value']: + continue + if ' ' in propertyValue['value']: + continue + if ',' in propertyValue['value']: + continue + if '.' in propertyValue['value']: + continue + return propertyValue['value'] + return '' + + +def setCwtchAddress(actorJson: {}, cwtchAddress: str) -> None: + """Sets an cwtch address for the given actor + """ + notCwtchAddress = False + + if len(cwtchAddress) < 56: + notCwtchAddress = True + if cwtchAddress != cwtchAddress.lower(): + notCwtchAddress = True + if not re.match("^[a-z0-9]*$", cwtchAddress): + notCwtchAddress = True + + if not actorJson.get('attachment'): + actorJson['attachment'] = [] + + # remove any existing value + propertyFound = None + for propertyValue in actorJson['attachment']: + if not propertyValue.get('name'): + continue + if not propertyValue.get('type'): + continue + if not propertyValue['name'].lower().startswith('cwtch'): + continue + propertyFound = propertyValue + break + if propertyFound: + actorJson['attachment'].remove(propertyFound) + if notCwtchAddress: + return + + for propertyValue in actorJson['attachment']: + if not propertyValue.get('name'): + continue + if not propertyValue.get('type'): + continue + if not propertyValue['name'].lower().startswith('cwtch'): + continue + if propertyValue['type'] != 'PropertyValue': + continue + propertyValue['value'] = cwtchAddress + return + + newCwtchAddress = { + "name": "Cwtch", + "type": "PropertyValue", + "value": cwtchAddress + } + actorJson['attachment'].append(newCwtchAddress) diff --git a/daemon.py b/daemon.py index 69795fab2..b3904bf63 100644 --- a/daemon.py +++ b/daemon.py @@ -44,6 +44,8 @@ from briar import getBriarAddress from briar import setBriarAddress from jami import getJamiAddress from jami import setJamiAddress +from cwtch import getCwtchAddress +from cwtch import setCwtchAddress from matrix import getMatrixAddress from matrix import setMatrixAddress from donate import getDonationUrl @@ -4517,6 +4519,18 @@ class PubServer(BaseHTTPRequestHandler): setJamiAddress(actorJson, '') actorChanged = True + # change cwtch address + currentCwtchAddress = getCwtchAddress(actorJson) + if fields.get('cwtchAddress'): + if fields['cwtchAddress'] != currentCwtchAddress: + setCwtchAddress(actorJson, + fields['cwtchAddress']) + actorChanged = True + else: + if currentCwtchAddress: + setCwtchAddress(actorJson, '') + actorChanged = True + # change PGP public key currentPGPpubKey = getPGPpubKey(actorJson) if fields.get('pgp'): @@ -5811,6 +5825,7 @@ class PubServer(BaseHTTPRequestHandler): toxAddress = None briarAddress = None jamiAddress = None + cwtchAddress = None ssbAddress = None emailAddress = None lockedAccount = False @@ -5832,6 +5847,7 @@ class PubServer(BaseHTTPRequestHandler): toxAddress = getToxAddress(actorJson) briarAddress = getBriarAddress(actorJson) jamiAddress = getJamiAddress(actorJson) + cwtchAddress = getCwtchAddress(actorJson) emailAddress = getEmailAddress(actorJson) PGPpubKey = getPGPpubKey(actorJson) PGPfingerprint = getPGPfingerprint(actorJson) @@ -5866,7 +5882,7 @@ class PubServer(BaseHTTPRequestHandler): xmppAddress, matrixAddress, ssbAddress, blogAddress, toxAddress, briarAddress, - jamiAddress, + jamiAddress, cwtchAddress, PGPpubKey, PGPfingerprint, emailAddress, self.server.dormantMonths, diff --git a/posts.py b/posts.py index 230b7d72f..22bb0cf4a 100644 --- a/posts.py +++ b/posts.py @@ -852,6 +852,30 @@ def _addAutoCW(baseDir: str, nickname: str, domain: str, return newSubject +def _createPostCWFromReply(baseDir: str, nickname: str, domain: str, + inReplyTo: str, + sensitive: bool, summary: str) -> (bool, str): + """If this is a reply and the original post has a CW + then use the same CW + """ + if inReplyTo and not sensitive: + # locate the post which this is a reply to and check if + # it has a content warning. If it does then reproduce + # the same warning + replyPostFilename = \ + locatePost(baseDir, nickname, domain, inReplyTo) + if replyPostFilename: + replyToJson = loadJson(replyPostFilename) + if replyToJson: + if replyToJson.get('object'): + if replyToJson['object'].get('sensitive'): + if replyToJson['object']['sensitive']: + sensitive = True + if replyToJson['object'].get('summary'): + summary = replyToJson['object']['summary'] + return sensitive, summary + + def _createPostBase(baseDir: str, nickname: str, domain: str, port: int, toUrl: str, ccUrl: str, httpPrefix: str, content: str, followersOnly: bool, saveToFile: bool, @@ -952,21 +976,9 @@ def _createPostBase(baseDir: str, nickname: str, domain: str, port: int, _updateHashtagsIndex(baseDir, tag, newPostId) # print('Content tags: ' + str(tags)) - if inReplyTo and not sensitive: - # locate the post which this is a reply to and check if - # it has a content warning. If it does then reproduce - # the same warning - replyPostFilename = \ - locatePost(baseDir, nickname, domain, inReplyTo) - if replyPostFilename: - replyToJson = loadJson(replyPostFilename) - if replyToJson: - if replyToJson.get('object'): - if replyToJson['object'].get('sensitive'): - if replyToJson['object']['sensitive']: - sensitive = True - if replyToJson['object'].get('summary'): - summary = replyToJson['object']['summary'] + sensitive, summary = \ + _createPostCWFromReply(baseDir, nickname, domain, + inReplyTo, sensitive, summary) # get the ending date and time endDateStr = None diff --git a/shares.py b/shares.py index 2a2261c7c..b9fac2e41 100644 --- a/shares.py +++ b/shares.py @@ -72,6 +72,27 @@ def removeShare(baseDir: str, nickname: str, domain: str, '" does not exist in ' + sharesFilename) +def _addShareDurationSec(duration: str, published: str) -> int: + """Returns the duration for the shared item in seconds + """ + if ' ' not in duration: + return 0 + durationList = duration.split(' ') + if not durationList[0].isdigit(): + return 0 + if 'hour' in durationList[1]: + return published + (int(durationList[0]) * 60 * 60) + if 'day' in durationList[1]: + return published + (int(durationList[0]) * 60 * 60 * 24) + if 'week' in durationList[1]: + return published + (int(durationList[0]) * 60 * 60 * 24 * 7) + if 'month' in durationList[1]: + return published + (int(durationList[0]) * 60 * 60 * 24 * 30) + if 'year' in durationList[1]: + return published + (int(durationList[0]) * 60 * 60 * 24 * 365) + return 0 + + def addShare(baseDir: str, httpPrefix: str, nickname: str, domain: str, port: int, displayName: str, summary: str, imageFilename: str, @@ -86,24 +107,8 @@ def addShare(baseDir: str, sharesJson = loadJson(sharesFilename) duration = duration.lower() - durationSec = 0 published = int(time.time()) - if ' ' in duration: - durationList = duration.split(' ') - if durationList[0].isdigit(): - if 'hour' in durationList[1]: - durationSec = published + (int(durationList[0]) * 60 * 60) - if 'day' in durationList[1]: - durationSec = published + (int(durationList[0]) * 60 * 60 * 24) - if 'week' in durationList[1]: - durationSec = \ - published + (int(durationList[0]) * 60 * 60 * 24 * 7) - if 'month' in durationList[1]: - durationSec = \ - published + (int(durationList[0]) * 60 * 60 * 24 * 30) - if 'year' in durationList[1]: - durationSec = \ - published + (int(durationList[0]) * 60 * 60 * 24 * 365) + durationSec = _addShareDurationSec(duration, published) itemID = getValidSharedItemID(displayName) diff --git a/tests.py b/tests.py index 72b6cb741..cefa769d7 100644 --- a/tests.py +++ b/tests.py @@ -2981,6 +2981,7 @@ def _testFunctions(): functionProperties = {} modules = {} modGroups = {} + methodLOC = [] for subdir, dirs, files in os.walk('.'): for sourceFile in files: @@ -2997,6 +2998,9 @@ def _testFunctions(): with open(sourceFile, "r") as f: lines = f.readlines() modules[modName]['lines'] = lines + lineCount = 0 + prevLine = 'start' + methodName = '' for line in lines: if '__module_group__' in line: if '=' in line: @@ -3010,7 +3014,28 @@ def _testFunctions(): if modName not in modGroups[groupName]: modGroups[groupName].append(modName) if not line.strip().startswith('def '): + if lineCount > 0: + lineCount += 1 + # add LOC count for this function + if len(prevLine.strip()) == 0 and \ + len(line.strip()) == 0 and \ + lineCount > 2: + lineCount -= 2 + if lineCount > 80: + locStr = str(lineCount) + ';' + methodName + if lineCount < 1000: + locStr = '0' + locStr + if lineCount < 100: + locStr = '0' + locStr + if lineCount < 10: + locStr = '0' + locStr + if locStr not in methodLOC: + methodLOC.append(locStr) + lineCount = 0 + prevLine = line continue + prevLine = line + lineCount = 1 methodName = line.split('def ', 1)[1].split('(')[0] methodArgs = \ sourceStr.split('def ' + methodName + '(')[1] @@ -3027,8 +3052,26 @@ def _testFunctions(): "module": modName, "calledInModule": [] } + # LOC count for the last function + if lineCount > 2: + lineCount -= 2 + if lineCount > 80: + locStr = str(lineCount) + ';' + methodName + if lineCount < 1000: + locStr = '0' + locStr + if lineCount < 100: + locStr = '0' + locStr + if lineCount < 10: + locStr = '0' + locStr + if locStr not in methodLOC: + methodLOC.append(locStr) break + print('LOC counts:') + methodLOC.sort() + for locStr in methodLOC: + print(locStr.split(';')[0] + ' ' + locStr.split(';')[1]) + excludeFuncArgs = [ 'pyjsonld' ] diff --git a/webapp_person_options.py b/webapp_person_options.py index 026182d8f..35ac8708e 100644 --- a/webapp_person_options.py +++ b/webapp_person_options.py @@ -45,6 +45,7 @@ def htmlPersonOptions(defaultTimeline: str, toxAddress: str, briarAddress: str, jamiAddress: str, + cwtchAddress: str, PGPpubKey: str, PGPfingerprint: str, emailAddress: str, @@ -214,6 +215,9 @@ def htmlPersonOptions(defaultTimeline: str, if jamiAddress: optionsStr += \ '
Jami: ' + removeHtml(jamiAddress) + '
\n' + if cwtchAddress: + optionsStr += \ + 'Cwtch: ' + removeHtml(cwtchAddress) + '
\n' if PGPfingerprint: optionsStr += 'PGP: ' + \
removeHtml(PGPfingerprint).replace('\n', '
') + '
Jami:
\n' + if cwtchAddress: + donateSection += \ + 'Cwtch:
\n' if PGPfingerprint: donateSection += \ 'PGP: ' + \
@@ -1063,6 +1069,7 @@ def htmlEditProfile(cssCache: {}, translate: {}, baseDir: str, path: str,
toxAddress = getToxAddress(actorJson)
briarAddress = getBriarAddress(actorJson)
jamiAddress = getJamiAddress(actorJson)
+ cwtchAddress = getCwtchAddress(actorJson)
emailAddress = getEmailAddress(actorJson)
PGPpubKey = getPGPpubKey(actorJson)
PGPfingerprint = getPGPfingerprint(actorJson)
@@ -1698,6 +1705,11 @@ def htmlEditProfile(cssCache: {}, translate: {}, baseDir: str, path: str,
' \n'
+ editProfileForm += '
\n'
+ editProfileForm += \
+ ' \n'
+
editProfileForm += \
'
\n'