mirror of https://gitlab.com/bashrc2/epicyon
Spaces
parent
b4783c510d
commit
80f43a728a
|
@ -38,7 +38,7 @@ def _createAcceptReject(baseDir: str, federationList: [],
|
||||||
newAccept = {
|
newAccept = {
|
||||||
"@context": "https://www.w3.org/ns/activitystreams",
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
'type': acceptType,
|
'type': acceptType,
|
||||||
'actor': httpPrefix+'://' + domain + '/users/' + nickname,
|
'actor': httpPrefix + '://' + domain + '/users/' + nickname,
|
||||||
'to': [toUrl],
|
'to': [toUrl],
|
||||||
'cc': [],
|
'cc': [],
|
||||||
'object': objectJson
|
'object': objectJson
|
||||||
|
@ -120,9 +120,9 @@ def _acceptFollow(baseDir: str, domain: str, messageJson: {},
|
||||||
print('DEBUG: unrecognized actor ' + thisActor)
|
print('DEBUG: unrecognized actor ' + thisActor)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
if not '/' + acceptedDomain+'/users/' + nickname in thisActor:
|
if not '/' + acceptedDomain + '/users/' + nickname in thisActor:
|
||||||
if debug:
|
if debug:
|
||||||
print('Expected: /' + acceptedDomain+'/users/' + nickname)
|
print('Expected: /' + acceptedDomain + '/users/' + nickname)
|
||||||
print('Actual: ' + thisActor)
|
print('Actual: ' + thisActor)
|
||||||
print('DEBUG: unrecognized actor ' + thisActor)
|
print('DEBUG: unrecognized actor ' + thisActor)
|
||||||
return
|
return
|
||||||
|
@ -134,7 +134,7 @@ def _acceptFollow(baseDir: str, domain: str, messageJson: {},
|
||||||
return
|
return
|
||||||
followedDomainFull = followedDomain
|
followedDomainFull = followedDomain
|
||||||
if port:
|
if port:
|
||||||
followedDomainFull = followedDomain+':' + str(port)
|
followedDomainFull = followedDomain + ':' + str(port)
|
||||||
followedNickname = getNicknameFromActor(followedActor)
|
followedNickname = getNicknameFromActor(followedActor)
|
||||||
if not followedNickname:
|
if not followedNickname:
|
||||||
print('DEBUG: no nickname found within Follow activity object ' +
|
print('DEBUG: no nickname found within Follow activity object ' +
|
||||||
|
@ -168,7 +168,7 @@ def _acceptFollow(baseDir: str, domain: str, messageJson: {},
|
||||||
else:
|
else:
|
||||||
if debug:
|
if debug:
|
||||||
print('DEBUG: Unable to create follow - ' +
|
print('DEBUG: Unable to create follow - ' +
|
||||||
nickname + '@' + acceptedDomain+' -> ' +
|
nickname + '@' + acceptedDomain + ' -> ' +
|
||||||
followedNickname + '@' + followedDomain)
|
followedNickname + '@' + followedDomain)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -198,9 +198,10 @@ def sendAnnounceViaServer(baseDir: str, session,
|
||||||
statusNumber, published = getStatusNumber()
|
statusNumber, published = getStatusNumber()
|
||||||
newAnnounceId = httpPrefix + '://' + fromDomainFull + '/users/' + \
|
newAnnounceId = httpPrefix + '://' + fromDomainFull + '/users/' + \
|
||||||
fromNickname + '/statuses/' + statusNumber
|
fromNickname + '/statuses/' + statusNumber
|
||||||
|
actorStr = httpPrefix + '://' + fromDomainFull + '/users/' + fromNickname
|
||||||
newAnnounceJson = {
|
newAnnounceJson = {
|
||||||
"@context": "https://www.w3.org/ns/activitystreams",
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
'actor': httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname,
|
'actor': actorStr,
|
||||||
'atomUri': newAnnounceId,
|
'atomUri': newAnnounceId,
|
||||||
'cc': [ccUrl],
|
'cc': [ccUrl],
|
||||||
'id': newAnnounceId + '/activity',
|
'id': newAnnounceId + '/activity',
|
||||||
|
|
2
auth.py
2
auth.py
|
@ -134,7 +134,7 @@ def authorizeBasic(baseDir: str, path: str, authHeader: str,
|
||||||
providedPassword = plain.split(':')[1]
|
providedPassword = plain.split(':')[1]
|
||||||
passfile = open(passwordFile, "r")
|
passfile = open(passwordFile, "r")
|
||||||
for line in passfile:
|
for line in passfile:
|
||||||
if line.startswith(nickname+':'):
|
if line.startswith(nickname + ':'):
|
||||||
storedPassword = \
|
storedPassword = \
|
||||||
line.split(':')[1].replace('\n', '').replace('\r', '')
|
line.split(':')[1].replace('\n', '').replace('\r', '')
|
||||||
success = _verifyPassword(storedPassword, providedPassword)
|
success = _verifyPassword(storedPassword, providedPassword)
|
||||||
|
|
|
@ -95,8 +95,8 @@ def sendAvailabilityViaServer(baseDir: str, session,
|
||||||
|
|
||||||
newAvailabilityJson = {
|
newAvailabilityJson = {
|
||||||
'type': 'Availability',
|
'type': 'Availability',
|
||||||
'actor': httpPrefix+'://'+domainFull+'/users/'+nickname,
|
'actor': httpPrefix + '://' + domainFull + '/users/' + nickname,
|
||||||
'object': '"'+status+'"',
|
'object': '"' + status + '"',
|
||||||
'to': [toUrl],
|
'to': [toUrl],
|
||||||
'cc': [ccUrl]
|
'cc': [ccUrl]
|
||||||
}
|
}
|
||||||
|
|
|
@ -243,7 +243,7 @@ def bookmark(recentPostsCache: {},
|
||||||
newBookmarkJson = {
|
newBookmarkJson = {
|
||||||
"@context": "https://www.w3.org/ns/activitystreams",
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
'type': 'Bookmark',
|
'type': 'Bookmark',
|
||||||
'actor': httpPrefix+'://'+fullDomain+'/users/'+nickname,
|
'actor': httpPrefix + '://' + fullDomain + '/users/' + nickname,
|
||||||
'object': objectUrl
|
'object': objectUrl
|
||||||
}
|
}
|
||||||
if ccList:
|
if ccList:
|
||||||
|
@ -302,10 +302,10 @@ def undoBookmark(recentPostsCache: {},
|
||||||
newUndoBookmarkJson = {
|
newUndoBookmarkJson = {
|
||||||
"@context": "https://www.w3.org/ns/activitystreams",
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
'type': 'Undo',
|
'type': 'Undo',
|
||||||
'actor': httpPrefix+'://'+fullDomain+'/users/'+nickname,
|
'actor': httpPrefix + '://' + fullDomain + '/users/' + nickname,
|
||||||
'object': {
|
'object': {
|
||||||
'type': 'Bookmark',
|
'type': 'Bookmark',
|
||||||
'actor': httpPrefix+'://'+fullDomain+'/users/'+nickname,
|
'actor': httpPrefix + '://' + fullDomain + '/users/' + nickname,
|
||||||
'object': objectUrl
|
'object': objectUrl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
6
cache.py
6
cache.py
|
@ -20,7 +20,7 @@ def _removePersonFromCache(baseDir: str, personUrl: str,
|
||||||
"""Removes an actor from the cache
|
"""Removes an actor from the cache
|
||||||
"""
|
"""
|
||||||
cacheFilename = baseDir + '/cache/actors/' + \
|
cacheFilename = baseDir + '/cache/actors/' + \
|
||||||
personUrl.replace('/', '#')+'.json'
|
personUrl.replace('/', '#') + '.json'
|
||||||
if os.path.isfile(cacheFilename):
|
if os.path.isfile(cacheFilename):
|
||||||
try:
|
try:
|
||||||
os.remove(cacheFilename)
|
os.remove(cacheFilename)
|
||||||
|
@ -70,7 +70,7 @@ def storePersonInCache(baseDir: str, personUrl: str,
|
||||||
return
|
return
|
||||||
if os.path.isdir(baseDir + '/cache/actors'):
|
if os.path.isdir(baseDir + '/cache/actors'):
|
||||||
cacheFilename = baseDir + '/cache/actors/' + \
|
cacheFilename = baseDir + '/cache/actors/' + \
|
||||||
personUrl.replace('/', '#')+'.json'
|
personUrl.replace('/', '#') + '.json'
|
||||||
if not os.path.isfile(cacheFilename):
|
if not os.path.isfile(cacheFilename):
|
||||||
saveJson(personJson, cacheFilename)
|
saveJson(personJson, cacheFilename)
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ def getPersonFromCache(baseDir: str, personUrl: str, personCache: {},
|
||||||
if not personCache.get(personUrl):
|
if not personCache.get(personUrl):
|
||||||
# does the person exist as a cached file?
|
# does the person exist as a cached file?
|
||||||
cacheFilename = baseDir + '/cache/actors/' + \
|
cacheFilename = baseDir + '/cache/actors/' + \
|
||||||
personUrl.replace('/', '#')+'.json'
|
personUrl.replace('/', '#') + '.json'
|
||||||
actorFilename = getFileCaseInsensitive(cacheFilename)
|
actorFilename = getFileCaseInsensitive(cacheFilename)
|
||||||
if actorFilename:
|
if actorFilename:
|
||||||
personJson = loadJson(actorFilename)
|
personJson = loadJson(actorFilename)
|
||||||
|
|
|
@ -306,7 +306,7 @@ def _addMusicTag(content: str, tag: str) -> str:
|
||||||
musicSites = ('soundcloud.com', 'bandcamp.com')
|
musicSites = ('soundcloud.com', 'bandcamp.com')
|
||||||
musicSiteFound = False
|
musicSiteFound = False
|
||||||
for site in musicSites:
|
for site in musicSites:
|
||||||
if site+'/' in content:
|
if site + '/' in content:
|
||||||
musicSiteFound = True
|
musicSiteFound = True
|
||||||
break
|
break
|
||||||
if not musicSiteFound:
|
if not musicSiteFound:
|
||||||
|
@ -458,7 +458,7 @@ def _addEmoji(baseDir: str, wordStr: str,
|
||||||
'type': 'Image',
|
'type': 'Image',
|
||||||
'url': emojiUrl
|
'url': emojiUrl
|
||||||
},
|
},
|
||||||
'name': ':'+emoji+':',
|
'name': ':' + emoji + ':',
|
||||||
"updated": fileLastModified(emojiFilename),
|
"updated": fileLastModified(emojiFilename),
|
||||||
"id": emojiUrl.replace('.png', ''),
|
"id": emojiUrl.replace('.png', ''),
|
||||||
'type': 'Emoji'
|
'type': 'Emoji'
|
||||||
|
@ -820,7 +820,7 @@ def addHtmlTags(baseDir: str, httpPrefix: str,
|
||||||
continue
|
continue
|
||||||
elif ':' in wordStr:
|
elif ':' in wordStr:
|
||||||
wordStr2 = wordStr.split(':')[1]
|
wordStr2 = wordStr.split(':')[1]
|
||||||
# print('TAG: emoji located - '+wordStr)
|
# print('TAG: emoji located - ' + wordStr)
|
||||||
if not emojiDict:
|
if not emojiDict:
|
||||||
# emoji.json is generated so that it can be customized and
|
# emoji.json is generated so that it can be customized and
|
||||||
# the changes will be retained even if default_emoji.json
|
# the changes will be retained even if default_emoji.json
|
||||||
|
@ -830,7 +830,7 @@ def addHtmlTags(baseDir: str, httpPrefix: str,
|
||||||
baseDir + '/emoji/emoji.json')
|
baseDir + '/emoji/emoji.json')
|
||||||
emojiDict = loadJson(baseDir + '/emoji/emoji.json')
|
emojiDict = loadJson(baseDir + '/emoji/emoji.json')
|
||||||
|
|
||||||
# print('TAG: looking up emoji for :'+wordStr2+':')
|
# print('TAG: looking up emoji for :' + wordStr2 + ':')
|
||||||
_addEmoji(baseDir, ':' + wordStr2 + ':', httpPrefix,
|
_addEmoji(baseDir, ':' + wordStr2 + ':', httpPrefix,
|
||||||
originalDomain, replaceEmoji, hashtags,
|
originalDomain, replaceEmoji, hashtags,
|
||||||
emojiDict)
|
emojiDict)
|
||||||
|
|
|
@ -1092,7 +1092,7 @@ if args.message:
|
||||||
toDomain = 'public'
|
toDomain = 'public'
|
||||||
toPort = port
|
toPort = port
|
||||||
|
|
||||||
# ccUrl=httpPrefix+'://'+domain+'/users/'+nickname+'/followers'
|
# ccUrl = httpPrefix + '://' + domain + '/users/' + nickname + '/followers'
|
||||||
ccUrl = None
|
ccUrl = None
|
||||||
sendMessage = args.message
|
sendMessage = args.message
|
||||||
followersOnly = args.followersonly
|
followersOnly = args.followersonly
|
||||||
|
|
2
inbox.py
2
inbox.py
|
@ -506,7 +506,7 @@ def _inboxPostRecipientsAdd(baseDir: str, httpPrefix: str, toList: [],
|
||||||
if domainMatch in recipient:
|
if domainMatch in recipient:
|
||||||
# get the handle for the local account
|
# get the handle for the local account
|
||||||
nickname = recipient.split(domainMatch)[1]
|
nickname = recipient.split(domainMatch)[1]
|
||||||
handle = nickname+'@'+domain
|
handle = nickname + '@' + domain
|
||||||
if os.path.isdir(baseDir + '/accounts/' + handle):
|
if os.path.isdir(baseDir + '/accounts/' + handle):
|
||||||
recipientsDict[handle] = None
|
recipientsDict[handle] = None
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -131,7 +131,7 @@ def setProfileImage(baseDir: str, httpPrefix: str, nickname: str, domain: str,
|
||||||
personJson[iconFilenameBase]['mediaType'] = mediaType
|
personJson[iconFilenameBase]['mediaType'] = mediaType
|
||||||
personJson[iconFilenameBase]['url'] = \
|
personJson[iconFilenameBase]['url'] = \
|
||||||
httpPrefix + '://' + fullDomain + '/users/' + \
|
httpPrefix + '://' + fullDomain + '/users/' + \
|
||||||
nickname + '/'+iconFilename
|
nickname + '/' + iconFilename
|
||||||
saveJson(personJson, personFilename)
|
saveJson(personJson, personFilename)
|
||||||
|
|
||||||
cmd = \
|
cmd = \
|
||||||
|
|
12
posts.py
12
posts.py
|
@ -1060,7 +1060,7 @@ def _createPostBase(baseDir: str, nickname: str, domain: str, port: int,
|
||||||
httpPrefix + '://' + domain + '/users/' + nickname + \
|
httpPrefix + '://' + domain + '/users/' + nickname + \
|
||||||
'/statuses/' + statusNumber + '/replies'
|
'/statuses/' + statusNumber + '/replies'
|
||||||
newPostUrl = \
|
newPostUrl = \
|
||||||
httpPrefix + '://' + domain + '/@' + nickname + '/'+statusNumber
|
httpPrefix + '://' + domain + '/@' + nickname + '/' + statusNumber
|
||||||
newPostAttributedTo = \
|
newPostAttributedTo = \
|
||||||
httpPrefix + '://' + domain + '/users/' + nickname
|
httpPrefix + '://' + domain + '/users/' + nickname
|
||||||
newPost = {
|
newPost = {
|
||||||
|
@ -1120,7 +1120,7 @@ def _createPostBase(baseDir: str, nickname: str, domain: str, port: int,
|
||||||
httpPrefix + '://' + domain + '/users/' + nickname + \
|
httpPrefix + '://' + domain + '/users/' + nickname + \
|
||||||
'/statuses/' + statusNumber + '/replies'
|
'/statuses/' + statusNumber + '/replies'
|
||||||
newPostUrl = \
|
newPostUrl = \
|
||||||
httpPrefix + '://' + domain + '/@' + nickname+'/' + statusNumber
|
httpPrefix + '://' + domain + '/@' + nickname + '/' + statusNumber
|
||||||
newPost = {
|
newPost = {
|
||||||
"@context": postContext,
|
"@context": postContext,
|
||||||
'id': newPostId,
|
'id': newPostId,
|
||||||
|
@ -2896,7 +2896,7 @@ def createModeration(baseDir: str, nickname: str, domain: str, port: int,
|
||||||
pageNumber = 1
|
pageNumber = 1
|
||||||
|
|
||||||
pageStr = '?page=' + str(pageNumber)
|
pageStr = '?page=' + str(pageNumber)
|
||||||
boxUrl = httpPrefix+'://'+domain+'/users/'+nickname+'/'+boxname
|
boxUrl = httpPrefix + '://' + domain + '/users/' + nickname + '/' + boxname
|
||||||
boxHeader = {
|
boxHeader = {
|
||||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||||
'first': boxUrl + '?page=true',
|
'first': boxUrl + '?page=true',
|
||||||
|
@ -3849,8 +3849,8 @@ def populateRepliesJson(baseDir: str, nickname: str, domain: str,
|
||||||
searchFilename = \
|
searchFilename = \
|
||||||
baseDir + \
|
baseDir + \
|
||||||
'/accounts/' + nickname + '@' + \
|
'/accounts/' + nickname + '@' + \
|
||||||
domain+'/' + \
|
domain + '/' + \
|
||||||
boxname+'/' + \
|
boxname + '/' + \
|
||||||
messageId2.replace('/', '#') + '.json'
|
messageId2.replace('/', '#') + '.json'
|
||||||
if os.path.isfile(searchFilename):
|
if os.path.isfile(searchFilename):
|
||||||
if authorized or \
|
if authorized or \
|
||||||
|
@ -3877,7 +3877,7 @@ def populateRepliesJson(baseDir: str, nickname: str, domain: str,
|
||||||
searchFilename = \
|
searchFilename = \
|
||||||
baseDir + \
|
baseDir + \
|
||||||
'/accounts/inbox@' + \
|
'/accounts/inbox@' + \
|
||||||
domain+'/inbox/' + \
|
domain + '/inbox/' + \
|
||||||
messageId2.replace('/', '#') + '.json'
|
messageId2.replace('/', '#') + '.json'
|
||||||
if os.path.isfile(searchFilename):
|
if os.path.isfile(searchFilename):
|
||||||
if authorized or \
|
if authorized or \
|
||||||
|
|
4
tests.py
4
tests.py
|
@ -2045,7 +2045,7 @@ def _testAddEmoji():
|
||||||
tags.append(tag)
|
tags.append(tag)
|
||||||
content = contentModified
|
content = contentModified
|
||||||
contentModified = replaceEmojiFromTags(content, tags, 'content')
|
contentModified = replaceEmojiFromTags(content, tags, 'content')
|
||||||
# print('contentModified: '+contentModified)
|
# print('contentModified: ' + contentModified)
|
||||||
assert contentModified == '<p>Emoji 🍋 🍓 🍌</p>'
|
assert contentModified == '<p>Emoji 🍋 🍓 🍌</p>'
|
||||||
|
|
||||||
os.chdir(baseDirOriginal)
|
os.chdir(baseDirOriginal)
|
||||||
|
@ -2124,7 +2124,7 @@ def _testRecentPostsCache():
|
||||||
htmlStr = '<html></html>'
|
htmlStr = '<html></html>'
|
||||||
for i in range(5):
|
for i in range(5):
|
||||||
postJsonObject = {
|
postJsonObject = {
|
||||||
"id": "https://somesite.whatever/users/someuser/statuses/"+str(i)
|
"id": "https://somesite.whatever/users/someuser/statuses/" + str(i)
|
||||||
}
|
}
|
||||||
updateRecentPostsCache(recentPostsCache, maxRecentPosts,
|
updateRecentPostsCache(recentPostsCache, maxRecentPosts,
|
||||||
postJsonObject, htmlStr)
|
postJsonObject, htmlStr)
|
||||||
|
|
6
utils.py
6
utils.py
|
@ -1563,10 +1563,10 @@ def getCachedPostFilename(baseDir: str, nickname: str, domain: str,
|
||||||
"""
|
"""
|
||||||
cachedPostDir = getCachedPostDirectory(baseDir, nickname, domain)
|
cachedPostDir = getCachedPostDirectory(baseDir, nickname, domain)
|
||||||
if not os.path.isdir(cachedPostDir):
|
if not os.path.isdir(cachedPostDir):
|
||||||
# print('ERROR: invalid html cache directory '+cachedPostDir)
|
# print('ERROR: invalid html cache directory ' + cachedPostDir)
|
||||||
return None
|
return None
|
||||||
if '@' not in cachedPostDir:
|
if '@' not in cachedPostDir:
|
||||||
# print('ERROR: invalid html cache directory '+cachedPostDir)
|
# print('ERROR: invalid html cache directory ' + cachedPostDir)
|
||||||
return None
|
return None
|
||||||
cachedPostId = removeIdEnding(postJsonObject['id'])
|
cachedPostId = removeIdEnding(postJsonObject['id'])
|
||||||
cachedPostFilename = cachedPostDir + '/' + cachedPostId.replace('/', '#')
|
cachedPostFilename = cachedPostDir + '/' + cachedPostId.replace('/', '#')
|
||||||
|
@ -1866,7 +1866,7 @@ def undoLikesCollectionEntry(recentPostsCache: {},
|
||||||
if not postJsonObject.get('object'):
|
if not postJsonObject.get('object'):
|
||||||
if debug:
|
if debug:
|
||||||
pprint(postJsonObject)
|
pprint(postJsonObject)
|
||||||
print('DEBUG: post '+objectUrl+' has no object')
|
print('DEBUG: post ' + objectUrl + ' has no object')
|
||||||
return
|
return
|
||||||
if not isinstance(postJsonObject['object'], dict):
|
if not isinstance(postJsonObject['object'], dict):
|
||||||
return
|
return
|
||||||
|
|
|
@ -116,7 +116,7 @@ def headerButtonsTimeline(defaultTimeline: str,
|
||||||
tlStr += \
|
tlStr += \
|
||||||
'<a href="' + usersPath + \
|
'<a href="' + usersPath + \
|
||||||
'/inbox" tabindex="-1"><button class="' + \
|
'/inbox" tabindex="-1"><button class="' + \
|
||||||
inboxButton+'"><span>' + translate['Inbox'] + \
|
inboxButton + '"><span>' + translate['Inbox'] + \
|
||||||
'</span></button></a>'
|
'</span></button></a>'
|
||||||
|
|
||||||
if not featuresHeader:
|
if not featuresHeader:
|
||||||
|
|
|
@ -534,7 +534,7 @@ def htmlProfile(rssIconAtTop: bool,
|
||||||
if xmppAddress:
|
if xmppAddress:
|
||||||
donateSection += \
|
donateSection += \
|
||||||
'<p>' + translate['XMPP'] + ': <a href="xmpp:' + \
|
'<p>' + translate['XMPP'] + ': <a href="xmpp:' + \
|
||||||
xmppAddress + '">'+xmppAddress + '</a></p>\n'
|
xmppAddress + '">' + xmppAddress + '</a></p>\n'
|
||||||
if matrixAddress:
|
if matrixAddress:
|
||||||
donateSection += \
|
donateSection += \
|
||||||
'<p>' + translate['Matrix'] + ': ' + matrixAddress + '</p>\n'
|
'<p>' + translate['Matrix'] + ': ' + matrixAddress + '</p>\n'
|
||||||
|
@ -1675,7 +1675,7 @@ def htmlEditProfile(cssCache: {}, translate: {}, baseDir: str, path: str,
|
||||||
translate['Matrix'] + '</label><br>\n'
|
translate['Matrix'] + '</label><br>\n'
|
||||||
editProfileForm += \
|
editProfileForm += \
|
||||||
' <input type="text" name="matrixAddress" value="' + \
|
' <input type="text" name="matrixAddress" value="' + \
|
||||||
matrixAddress+'">\n'
|
matrixAddress + '">\n'
|
||||||
|
|
||||||
editProfileForm += '<label class="labels">SSB</label><br>\n'
|
editProfileForm += '<label class="labels">SSB</label><br>\n'
|
||||||
editProfileForm += \
|
editProfileForm += \
|
||||||
|
|
Loading…
Reference in New Issue