def page_get(userauth, prjname): pmd = config.cache.get_pmd(prjname) if pmd is None: abort(404, "Project does not exist") return pdata = pmd.pdata if pdata.projtype.has_grflangid: return template('upload_lang', userauth=userauth, pmd=pmd) else: return template('upload_lang_select', userauth=userauth, pmd=pmd, lnginfos=sorted(pdata.get_all_languages(), lambda l: l.isocode))
def language(userauth, lngname): """ Get an overview of the state of the given language in every project. @param lngname: Name of the language (isocode). @type lngname: C{str} """ lnginfo = language_info.isocode.get(lngname) if lnginfo is None: abort(404, "Language is unknown") return prjdata = [] for pmd in config.cache.projects.values(): lstate = pmd.overview.get(lngname) if lstate is not None: prjdata.append((pmd, True, lstate)) else: lstate = [0 for i in range(data.MAX_STATE)] lstate[data.MISSING] = pmd.blang_count prjdata.append((pmd, False, lstate)) prjdata.sort(key=lambda p: p[0].human_name.lower()) return template('language', userauth=userauth, lnginfo=lnginfo, prjdata=prjdata)
def page_post(userauth): prjname = request.forms.name.lower().strip() acceptance = utils.verify_name(prjname, "Project identifier", True) if acceptance is not None: redirect('/newproject', message=acceptance) return if prjname in config.cache.projects: redirect('/newproject', message="Project \"{}\" already exists".format(prjname)) return ptype_name = request.forms.projtype_select projtype = None for prjtp in project_type.project_types.values(): if prjtp.human_name == ptype_name and prjtp.name in config.cfg.project_types: projtype = prjtp break if projtype is None: redirect('/newproject', message='No known project type provided') return return template('createproject_form', userauth=userauth, projtype_name=projtype.name, prjname=prjname)
def page_get(userauth): all_ptype_names = [] for projtype in project_type.project_types.values(): if projtype.name in config.cfg.project_types: all_ptype_names.append(projtype.human_name) all_ptype_names.sort() return template("newproject_form", userauth=userauth, all_ptype_names=all_ptype_names)
def userprofile(userauth): """ Get an overview of the (write) access rights of a user in every project. """ prjdata = [] languages = {} is_owner = False for pmd in config.cache.projects.values(): owner = userauth.may_read("projsettings", pmd.name, "-") is_owner |= owner langs = {} for lang in language_info.all_languages: lngname = lang.isocode lstate = pmd.overview.get(lngname) exist = False translator = False if lstate is not None: exist = True translator = userauth.may_read("string", pmd.name, lngname) else: lstate = [ 0 for i in range(data.MAX_STATE) ] lstate[data.MISSING] = pmd.blang_count translator = userauth.may_read("makelanguage", pmd.name, lngname) langs[lngname] = (exist, lstate, translator) if translator and not owner: # Collect languages with translator access, but do not spam language list due to owner access languages[lngname] = lang prjdata.append((pmd, owner, langs)) prjdata.sort(key = lambda p: p[0].human_name.lower()) return template('userprofile', userauth = userauth, prjdata = prjdata, is_owner = is_owner, lnginfos = sorted(languages.values(), key=lambda x:x.isocode))
def new_language_get(userauth, prjname): """ Form to add another language to the project. """ pmd = config.cache.get_pmd(prjname) if pmd is None: abort(404, "Project does not exist") return pdata = pmd.pdata base_langs = [] translations = [] can_be_added = [] for lang in pdata.get_all_languages(): if lang.isocode == pdata.base_language: base_langs.append(lang) continue if lang.isocode in pdata.languages: translations.append(lang) continue can_be_added.append(lang) translations.sort(key=lambda x: x.isocode) can_be_added.sort(key=lambda x: x.isocode) return template( "newlanguage", userauth=userauth, pmd=pmd, base_langs=base_langs, translations=translations, can_be_added=can_be_added, )
def project(userauth, prjname): pmd = config.cache.get_pmd(prjname) if pmd is None: abort(404, "Project does not exist") return pdata = pmd.pdata base_lng = pdata.get_base_language() transl = [] bcounts = None if base_lng is not None: for lname, lng in pdata.languages.items(): if lng is base_lng: continue transl.append((lng, pmd.overview.get(lname))) transl.sort(key=lambda x: x[0].name) bcounts = pmd.overview.get(base_lng.name) return template('project', userauth=userauth, pmd=pmd, transl=transl, base_lng=base_lng, bcounts=bcounts)
def project_get(userauth, prjname): pmd = config.cache.get_pmd(prjname) if pmd is None: abort(404, "Project does not exist") return return template("projsettings", userauth=userauth, pmd=pmd)
def project(userauth, prjname, lngname): pmd = config.cache.get_pmd(prjname) if pmd is None: abort(404, "Project does not exist") return pdata = pmd.pdata lng = pdata.languages.get(lngname) if lng is None: abort(404, "Language does not exist in the project") return assert pdata.projtype.allow_case or lng.case == [""] blng = pdata.get_base_language( ) # As above we established there is at least one language, this should work. stored = [[] for i in range(data.MAX_STATE)] sdict = pdata.statistics sdict = sdict.get(lngname) # Statistics dict for the queried language. if sdict is None: abort(404, "Missing language statistics") return for sname, bchgs in blng.changes.items(): cstates = sdict[sname] state = max(s[1] for s in cstates) if state != data.MISSING_OK: bchg = data.get_newest_change(bchgs, "") sdd = StringDisplayData(sname, bchg.base_text) chgs = lng.changes.get(sname) if chgs is not None: cases = data.get_all_newest_changes(chgs, lng.case) for case, cstate in cstates: chg = cases[case] if chg is not None: if lng is blng: text = chg.base_text.text else: text = chg.new_text.text if text == "" and case != "": # Suppress empty non-default case from translations. continue cdd = CaseDisplayData(case, data.STATE_MAP[cstate].name, text) sdd.cases.append(cdd) stored[state].append(sdd) for strs in stored: strs.sort() return template("translation", userauth=userauth, pmd=pmd, is_blng=(lng == blng), lng=lng, stored=stored)
def page_get_subdir(userauth, prjname, lngname): pmd = config.cache.get_pmd(prjname) if pmd is None: abort(404, "Project does not exist") return linfo = language_info.isocode.get(lngname) if linfo is None: abort(404, "Language is unknown") return return template("upload_lang_subdir", userauth=userauth, pmd=pmd, lnginfo=linfo)
def login(userauth): req_redirect = request.query.get("redirect") req_login = request.query.get("login") if userauth.is_auth: login_success(req_redirect) elif users.oauth_redirect: session, url = users.oauth_redirect(req_redirect, req_login) start_session(session) redirect(url) elif users.get_authentication: return utils.template("login", userauth=userauth, req_login=req_login, req_redirect=req_redirect) else: abort(500, "No authentication method") return
def new_language_post(userauth, prjname): """ Construct the requested language. """ pmd = config.cache.get_pmd(prjname) if pmd is None: abort(404, "Project does not exist") return new_iso = request.forms.language_select lng_def = get_language(new_iso) if lng_def is None: msg = "No language found that can be created" abort(404, msg) return return template("makelanguage", userauth=userauth, pmd=pmd, lnginfo=lng_def)
def login(userauth): if not users.get_authentication: abort(500, "No authentication method") return req_redirect = request.forms.get("redirect") req_login = request.forms.get('login') req_password = request.forms.get('password') if req_login and req_password: userauth = users.get_authentication(req_login, req_password) if userauth.is_auth: start_session(userauth) login_success(req_redirect) return return utils.template('login', userauth=userauth, req_login=req_login, req_redirect=req_redirect, message='Try harder!', message_class='error')
def delete_form(userauth, prjname, lngname): pmd = config.cache.get_pmd(prjname) if pmd is None: abort(404, "Project does not exist") return if pmd.storage_type == config.STORAGE_SEPARATE_LANGUAGES: abort( 404, "Cannot delete a language, ask the system administrator to remove the language file" ) return pdata = pmd.pdata lng = pdata.languages.get(lngname) if lng is None: abort(404, "Language does not exist in the project") return if pdata.base_language == lngname: abort(404, "Cannot delete base language!") return return template("delete_translation", userauth=userauth, pmd=pmd, lng=lng)
def login_post(userauth): if not users.get_authentication: abort(500, "No authentication method") return req_redirect = request.forms.get("redirect") req_login = request.forms.get("login") req_password = request.forms.get("password") if req_login and req_password: userauth = users.get_authentication(req_login, req_password) if userauth.is_auth: start_session(userauth) login_success(req_redirect) return return utils.template( "login", userauth=userauth, req_login=req_login, req_redirect=req_redirect, message="Try harder!", message_class="error", )
def handle_upload(userauth, pmd, projname, langfile, override, is_base, lng_data): """ Process the upload. @param userauth: User authentication. @type userauth: L{UserAuthentication} @param pmd: Project meta data. @type pmd: L{ProjectMetaData} @param projname: Project name. @type projname: C{str} @param langfile: Language file to load if available. @type langfile: C{file} or C{None} @param override: Override existing text. @type override: C{bool} @param lng_data: Used language, if provided. @type lng_data: L{LanguageData} or C{None} """ pdata = pmd.pdata # Missing language file in the upload. if not langfile or not langfile.file: abort(404, "Missing language file") return base_language = pdata.get_base_language() # Cannot download a translation without base language. if not is_base and base_language is None: abort(404, "Project has no base language") return # Parse language file, and report any errors. ng_data = language_file.load_language_file(pdata.projtype, langfile.file, config.cfg.language_file_size, lng_data) if len(ng_data.errors) > 0: return template('upload_errors', userauth=userauth, pmd=pmd, errors=ng_data.errors) # Is the language allowed? if not pdata.projtype.allow_language(ng_data.language_data): abort( 404, "Language \"{}\" may not be uploaded".format( ng_data.language_data.isocode)) return stamp = data.make_stamp() lng = pdata.languages.get(ng_data.language_data.isocode) if lng is None: # New language being added. result = add_new_language(ng_data, pdata, is_base) if not result[0]: abort(404, result[0]) return lng = result[1] if is_base and base_language is None: base_language = lng if is_base: if base_language is not None and base_language != lng: abort(404, "Cannot change a translation to a base language") return # Add strings as changes. for sv in ng_data.strings: sv.text = language_file.sanitize_text(sv.text) chgs = base_language.changes.get(sv.name) chg = get_blng_change(sv, base_language) if chg is None: # New change. base_text = data.Text(sv.text, sv.case, stamp) chg = data.Change(sv.name, sv.case, base_text, None, stamp, userauth.name, True) if chgs is None: chgs = [chg] base_language.changes[sv.name] = chgs else: chgs.append(chg) else: # Only way to update a base language is by upload, no need for override check. chg.stamp = stamp chg.user = userauth.name for c in chgs: c.last_upload = (c == chg) # Update language properties as well. copy_lng_properties(pdata.projtype, ng_data, base_language) pdata.skeleton = ng_data.skeleton # Use the new skeleton file. pdata.flush_related_cache() # Drop the related strings cache. pdata.set_modified() # Push the new set of string-names to all languages (this includes the base language). str_names = set(sv.name for sv in ng_data.strings) for lang in pdata.languages.values(): lng_modified = False not_seen = str_names.copy() for sn in list(lang.changes.keys()): not_seen.discard(sn) if sn in str_names: continue # Name is kept. del lang.changes[sn] # Old string, delete lng_modified = True for sn in not_seen: # Missing translation are not saved, so no set_modified here. lang.changes[sn] = [] if lng_modified: lang.set_modified() else: # Not a base language -> it is a translation. if base_language is not None and base_language == lng: abort(404, "Cannot change a base language to a translation") return for sv in ng_data.strings: sv.text = language_file.sanitize_text(sv.text) # Find base language string for 'sv'. bchgs = base_language.changes.get(sv.name) if bchgs is None: continue # Translation has a string not in the base language bchg = data.get_newest_change(bchgs, '') if bchg is None: continue # Nothing to base against. base_text = bchg.base_text chgs = lng.changes.get(sv.name) chg = get_lng_change(sv, lng, base_text) if chg is None: # It's a new text or new case. lng_text = data.Text(sv.text, sv.case, stamp) chg = data.Change(sv.name, sv.case, base_text, lng_text, stamp, userauth.name, True) if chgs is None: lng.changes[sv.name] = [chg] else: for c in chgs: c.last_upload = False chgs.append(chg) elif override: # Override existing entry. chg.stamp = stamp chg.user = userauth.name for c in chgs: c.last_upload = (c == chg) # Update language properties as well. copy_lng_properties(pdata.projtype, ng_data, lng) lng.set_modified() config.cache.save_pmd(pmd) if is_base: pmd.create_statistics(None) # Update all languages. else: pmd.create_statistics(lng) message = "Successfully uploaded language '" + lng.name + "' " + utils.get_datetime_now_formatted( ) redirect("/project/<prjname>", prjname=projname, message=message)
def root(userauth): return template("root", userauth=userauth)
def root(userauth): # projs: C{list} of L{ProjectMetaData} projs = sorted(config.cache.projects.values(), key=lambda p: p.human_name.lower()) return template("projects", userauth=userauth, projects=projs)
def output_string_edit_page(userauth, bchg, binfo, lng, pmd, lngname, sname, states=None, messages=[]): """ Construct a page for editing a string. @param bchg: Last version of the base language. @type bchg: L{Change} @param binfo: Information about the state of the L{bchg} string. @type binfo: L{StringInfo} @param lng: Language being translated. @type lng: L{Language} @param pmd: Project Meta Data. @type pmd: L{ProjectMetaData} @param lngname: Language name. @type lngname: C{str} @param sname: Name of the string. @type sname: C{str} @param states: Changes, state and errors for (some of) the cases, omission of a case means the function should derive it from the language. @type states: C{dict} of C{str} to tuple (L{Change}, C{int}, C{list} of L{ErrorMessage}), use C{None} to derive all. @return: Either an error, or an instantiated template. @rtype: C{None} or C{str} """ if states is None: states = {} pdata = pmd.pdata projtype = pdata.projtype # Mapping of case to list of related strings. related_cases = dict((case, []) for case in lng.case) for rel_sname in pdata.get_related_strings(sname): rel_chgs = lng.changes.get(rel_sname) if rel_chgs is not None: rel_chgs = data.get_all_newest_changes(rel_chgs, lng.case) for case, chg in rel_chgs.items(): if chg is not None and chg.new_text is not None: rc = related_cases.get(case) if rc is not None: rc.append(RelatedString(rel_sname, chg.new_text)) case_chgs = data.get_all_changes(lng.changes.get(sname), lng.case, None) now = data.make_stamp() transl_cases = [] for case in lng.case: tranls = [] cchgs = [(cchg, True) for cchg in case_chgs[case] ] # Tuples (case-change, 'saved translation') chg_err_state = states.get(case) if chg_err_state is not None: cchgs.append((chg_err_state[0], False)) if len(cchgs) == 0: # No changes for this case, make a dummy one to display the base data. tra = Translation(bchg, None, now, False) if case == "": tra.errors = [ language_file.ErrorMessage(language_file.ERROR, None, "String is missing") ] tra.state = data.STATE_MAP[data.MISSING].name else: tra.errors = [] tra.state = data.STATE_MAP[data.MISSING_OK].name tranls.append(tra) else: # Changes do exist, add them (in reverse chronological order). cchgs.reverse() for idx, (lchg, saved) in enumerate(cchgs): tra = Translation(bchg, lchg, now, saved) if idx == 0: # Newest string, add errors if chg_err_state is not None: state, errors = chg_err_state[1], chg_err_state[2] else: state, errors = data.get_string_status( projtype, lchg, case, lng, bchg.base_text, binfo) tra.errors = errors tra.state = data.STATE_MAP[state].name else: # For older translations, the errors and state are never displayed. tra.errors = [] tra.state = data.STATE_MAP[data.MISSING_OK].name tranls.append(tra) if projtype.allow_case or case == "": transl_cases.append( TransLationCase(case, tranls, related_cases[case])) related_languages = [] if lng.name[:3] != pdata.base_language[:3]: for n, l in pdata.languages.items(): if n[:3] != lng.name[:3] or n == lng.name: continue related = data.get_newest_change(lng.changes.get(sname), "") if related is not None: related_languages.append((l, related)) related_languages.sort(key=lambda x: x[0].name) return template( "string_form", userauth=userauth, pmd=pmd, lng=lng, sname=sname, plurals=language_info.all_plurals[lng.plural].description, genders=lng.gender, cases=lng.case, related_languages=related_languages, tcs=transl_cases, messages=messages, ) @route("/string/<prjname>/<lngname>/<sname>", method="GET") @protected(["string", "prjname", "lngname"]) def str_form(userauth, prjname, lngname, sname): parms = check_page_parameters(prjname, lngname, sname) if parms is None: return pmd, bchg, lng, binfo = parms return output_string_edit_page(userauth, bchg, binfo, lng, pmd, lngname, sname, None) @route("/string/<prjname>/<lngname>/<sname>", method="POST") @protected(["string", "prjname", "lngname"]) def str_post(userauth, prjname, lngname, sname): parms = check_page_parameters(prjname, lngname, sname) if parms is None: return pmd, bchg, lng, binfo = parms request.forms.recode_unicode = False # Allow Unicode input request_forms = request.forms.decode() # Convert dict to Unicode. base_str = request_forms.get( "base") # Base text translated against in the form. if base_str is None or base_str != bchg.base_text.text: abort( 404, "Base language has been changed, please translate the newer version instead" ) return # Get changes against bchg case_chgs = data.get_all_changes(lng.changes.get(sname), lng.case, bchg) projtype = pmd.pdata.projtype stamp = None # Assigned a stamp object when a change is made in the translation. # Collected output data new_changes = [] new_state_errors = {} for case in lng.case: trl_str = request_forms.get("text_" + case) # Translation text in the form. if trl_str is None: continue # It's missing from the form data. trl_str = language_file.sanitize_text(trl_str) if case == "" and trl_str == "" and bchg.base_text != "": # Empty base case for a non-empty string, was the "allow empty base translation" flag set? if request_forms.get("allow_empty_default") != "on": if stamp is None: stamp = data.make_stamp() txt = data.Text(trl_str, case, stamp) tchg = data.Change(sname, case, bchg.base_text, txt, stamp, userauth.name) error = language_file.ErrorMessage( language_file.ERROR, None, "Empty default case is not allowed (enable by setting 'Allow empty input').", ) new_state_errors[case] = (tchg, data.INVALID, [error]) continue # Check whether there is a match with a change in the translation. trl_chg = None for cchg in case_chgs[case]: if cchg.new_text.text == trl_str: trl_chg = cchg break if trl_chg is None: if case != "" and trl_str == "" and len(case_chgs[case]) == 0: continue # Skip adding empty non-base cases if there are no strings. # A new translation against bchg! if stamp is None: stamp = data.make_stamp() txt = data.Text(trl_str, case, stamp) tchg = data.Change(sname, case, bchg.base_text, txt, stamp, userauth.name) state, errors = data.get_string_status(projtype, tchg, case, lng, bchg.base_text, binfo) if state == data.MISSING or state == data.INVALID: new_state_errors[case] = (tchg, state, errors) else: new_changes.append(tchg) continue
def languages(userauth): """ Get an overview of used languages over all projects. """ languages = sorted(language_info.all_languages, key=lambda l: l.isocode) return template('languages', userauth=userauth, lnginfos=languages)
def root(userauth): return template('root', userauth=userauth)