__filename__ = "roles.py" __author__ = "Bob Mottram" __license__ = "AGPL3+" __version__ = "1.2.0" __maintainer__ = "Bob Mottram" __email__ = "bob@libreserver.org" __status__ = "Production" __module_group__ = "Profile Metadata" import os from utils import loadJson from utils import saveJson from utils import getStatusNumber from utils import removeDomainPort from utils import acctDir def _clearRoleStatus(baseDir: str, role: str) -> None: """Removes role status from all accounts This could be slow if there are many users, but only happens rarely when roles are appointed or removed """ directory = os.fsencode(baseDir + '/accounts/') for f in os.scandir(directory): f = f.name filename = os.fsdecode(f) if '@' not in filename: continue if not filename.endswith(".json"): continue filename = os.path.join(baseDir + '/accounts/', filename) if '"' + role + '"' not in open(filename).read(): continue actorJson = loadJson(filename) if not actorJson: continue rolesList = getActorRolesList(actorJson) if role in rolesList: rolesList.remove(role) setRolesFromList(actorJson, rolesList) saveJson(actorJson, filename) def clearEditorStatus(baseDir: str) -> None: """Removes editor status from all accounts This could be slow if there are many users, but only happens rarely when editors are appointed or removed """ _clearRoleStatus(baseDir, 'editor') def clearCounselorStatus(baseDir: str) -> None: """Removes counselor status from all accounts This could be slow if there are many users, but only happens rarely when counselors are appointed or removed """ _clearRoleStatus(baseDir, 'editor') def clearArtistStatus(baseDir: str) -> None: """Removes artist status from all accounts This could be slow if there are many users, but only happens rarely when artists are appointed or removed """ _clearRoleStatus(baseDir, 'artist') def clearModeratorStatus(baseDir: str) -> None: """Removes moderator status from all accounts This could be slow if there are many users, but only happens rarely when moderators are appointed or removed """ _clearRoleStatus(baseDir, 'moderator') def _addRole(baseDir: str, nickname: str, domain: str, roleFilename: str) -> None: """Adds a role nickname to the file. This is a file containing the nicknames of accounts having this role """ domain = removeDomainPort(domain) roleFile = baseDir + '/accounts/' + roleFilename if os.path.isfile(roleFile): # is this nickname already in the file? with open(roleFile, 'r') as f: lines = f.readlines() for roleNickname in lines: roleNickname = roleNickname.strip('\n').strip('\r') if roleNickname == nickname: return lines.append(nickname) with open(roleFile, 'w+') as f: for roleNickname in lines: roleNickname = roleNickname.strip('\n').strip('\r') if len(roleNickname) < 2: continue if os.path.isdir(baseDir + '/accounts/' + roleNickname + '@' + domain): f.write(roleNickname + '\n') else: with open(roleFile, 'w+') as f: accountDir = acctDir(baseDir, nickname, domain) if os.path.isdir(accountDir): f.write(nickname + '\n') def _removeRole(baseDir: str, nickname: str, roleFilename: str) -> None: """Removes a role nickname from the file. This is a file containing the nicknames of accounts having this role """ roleFile = baseDir + '/accounts/' + roleFilename if not os.path.isfile(roleFile): return with open(roleFile, 'r') as f: lines = f.readlines() with open(roleFile, 'w+') as f: for roleNickname in lines: roleNickname = roleNickname.strip('\n').strip('\r') if len(roleNickname) > 1 and roleNickname != nickname: f.write(roleNickname + '\n') def _setActorRole(actorJson: {}, roleName: str) -> bool: """Sets a role for an actor """ if not actorJson.get('hasOccupation'): return False if not isinstance(actorJson['hasOccupation'], list): return False # occupation category from www.onetonline.org category = None if 'admin' in roleName: category = '15-1299.01' elif 'moderator' in roleName: category = '11-9199.02' elif 'editor' in roleName: category = '27-3041.00' elif 'counselor' in roleName: category = '23-1022.00' elif 'artist' in roleName: category = '27-1024.00' if not category: return False for index in range(len(actorJson['hasOccupation'])): occupationItem = actorJson['hasOccupation'][index] if not isinstance(occupationItem, dict): continue if not occupationItem.get('@type'): continue if occupationItem['@type'] != 'Role': continue if occupationItem['hasOccupation']['name'] == roleName: return True statusNumber, published = getStatusNumber() newRole = { "@type": "Role", "hasOccupation": { "@type": "Occupation", "name": roleName, "description": "Fediverse instance role", "occupationLocation": { "@type": "City", "url": "Fediverse" }, "occupationalCategory": { "@type": "CategoryCode", "inCodeSet": { "@type": "CategoryCodeSet", "name": "O*Net-SOC", "dateModified": "2019", "url": "https://www.onetonline.org/" }, "codeValue": category, "url": "https://www.onetonline.org/link/summary/" + category } }, "startDate": published } actorJson['hasOccupation'].append(newRole) return True def setRolesFromList(actorJson: {}, rolesList: []) -> None: """Sets roles from a list """ # clear Roles from the occupation list emptyRolesList = [] for occupationItem in actorJson['hasOccupation']: if not isinstance(occupationItem, dict): continue if not occupationItem.get('@type'): continue if occupationItem['@type'] == 'Role': continue emptyRolesList.append(occupationItem) actorJson['hasOccupation'] = emptyRolesList # create the new list for roleName in rolesList: _setActorRole(actorJson, roleName) def getActorRolesList(actorJson: {}) -> []: """Gets a list of role names from an actor """ if not actorJson.get('hasOccupation'): return [] if not isinstance(actorJson['hasOccupation'], list): return [] rolesList = [] for occupationItem in actorJson['hasOccupation']: if not isinstance(occupationItem, dict): continue if not occupationItem.get('@type'): continue if occupationItem['@type'] != 'Role': continue roleName = occupationItem['hasOccupation']['name'] if roleName not in rolesList: rolesList.append(roleName) return rolesList def setRole(baseDir: str, nickname: str, domain: str, role: str) -> bool: """Set a person's role Setting the role to an empty string or None will remove it """ # avoid giant strings if len(role) > 128: return False actorFilename = acctDir(baseDir, nickname, domain) + '.json' if not os.path.isfile(actorFilename): return False roleFiles = { "moderator": "moderators.txt", "editor": "editors.txt", "counselor": "counselors.txt", "artist": "artists.txt" } actorJson = loadJson(actorFilename) if actorJson: if not actorJson.get('hasOccupation'): return False rolesList = getActorRolesList(actorJson) actorChanged = False if role: # add the role if roleFiles.get(role): _addRole(baseDir, nickname, domain, roleFiles[role]) if role not in rolesList: rolesList.append(role) rolesList.sort() setRolesFromList(actorJson, rolesList) actorChanged = True else: # remove the role if roleFiles.get(role): _removeRole(baseDir, nickname, roleFiles[role]) if role in rolesList: rolesList.remove(role) setRolesFromList(actorJson, rolesList) actorChanged = True if actorChanged: saveJson(actorJson, actorFilename) return True def actorHasRole(actorJson: {}, roleName: str) -> bool: """Returns true if the given actor has the given role """ rolesList = getActorRolesList(actorJson) return roleName in rolesList