def update_notification(store, request, language=GLSetting.memory_copy.default_language): try: notif = store.find(Notification).one() except Exception as excep: log.err("Database error or application error: %s" % excep ) raise excep fill_localized_keys(request, Notification.localized_strings, language) if request['security'] in Notification._security_types: notif.security = request['security'] else: log.err("Invalid request: Security option not recognized") log.debug("Invalid Security value: %s" % request['security']) raise errors.InvalidInputFormat("Security selection not recognized") try: notif.update(request) except DatabaseError as dberror: log.err("Unable to update Notification: %s" % dberror) raise errors.InvalidInputFormat(dberror) if request['disable'] != GLSetting.notification_temporary_disable: log.msg("Switching notification mode: was %s and now is %s" % ("DISABLE" if GLSetting.notification_temporary_disable else "ENABLE", "DISABLE" if request['disable'] else "ENABLE") ) GLSetting.notification_temporary_disable = request['disable'] return admin_serialize_notification(notif, language)
def db_create_user(store, request, language): fill_localized_keys(request, models.User.localized_keys, language) user = models.User({ 'username': request['username'], 'role': request['role'], 'state': u'enabled', 'deletable': request['deletable'], 'name': request['name'], 'description': request['description'], 'public_name': request['public_name'] if request['public_name'] != '' else request['name'], 'language': language, 'password_change_needed': request['password_change_needed'], 'mail_address': request['mail_address'] }) if request['username'] == '': user.username = user.id password = request['password'] if len(password) == 0: password = GLSettings.memory_copy.default_password user.salt = security.generateRandomSalt() user.password = security.hash_password(password, user.salt) # The various options related in manage PGP keys are used here. parse_pgp_options(user, request) store.add(user) return user
def update_receiver(store, receiver_id, request, language): """ Updates the specified receiver with the details. raises :class:`globaleaks.errors.ReceiverIdNotFound` if the receiver does not exist. """ receiver = models.Receiver.get(store, receiver_id) if not receiver: raise errors.ReceiverIdNotFound fill_localized_keys(request, models.Receiver.localized_keys, language) contexts = request.get('contexts', []) receiver.contexts.clear() for context_id in contexts: context = models.Context.get(store, context_id) if not context: raise errors.ContextIdNotFound receiver.contexts.add(context) receiver.update(request) return admin_serialize_receiver(receiver, language)
def db_create_step(store, context_id, steps, language): """ Add a new step to the store, then return the new serialized object. """ context = models.Context.get(store, context_id) if context is None: raise errors.ContextIdNotFound n = 1 for step in steps: step['context_id'] = context_id step['number'] = n fill_localized_keys(step, models.Step.localized_strings, language) s = models.Step.new(store, step) for f in step['children']: field = models.Field.get(store, f['id']) if not field: log.err("Creation error: unexistent field can't be associated") raise errors.FieldIdNotFound # remove current step/field fieldgroup/field association a_s, a_f = get_field_association(store, field.id) if a_s != s.id: disassociate_field(store, field.id) s.children.add(field) n += 1
def db_create_field(store, field, language): """ Create and add a new field to the store, then return the new serialized object. :param store: the store on which perform queries. :param field: the field definition dict :param language: the language of the field definition dict :return: a serialization of the object """ _, step, fieldgroup = field_integrity_check(store, field) fill_localized_keys(field, models.Field.localized_strings, language) if field['template_id'] == '': field['template_id'] = None f = models.Field.new(store, field) if field['template_id'] is None: db_update_fieldattrs(store, f.id, field['attrs'], language) db_update_fieldoptions(store, f.id, field['options'], language) for child in field['children']: db_update_field(store, child['id'], child, language) associate_field(store, f, step, fieldgroup) return f
def update_step(store, step_id, request, language): """ Update the specified step with the details. raises :class:`globaleaks.errors.StepIdNotFound` if the step does not exist. :param store: the store on which perform queries. :param step_id: the step_id of the step to update :param request: the step definition dict :param language: the language of the step definition dict :return: a serialization of the object """ step = models.Step.get(store, step_id) try: if not step: raise errors.StepIdNotFound fill_localized_keys(request, models.Step.localized_strings, language) step.update(request) for child in request['children']: db_update_field(store, child['id'], child, language) except Exception as dberror: log.err('Unable to update step: {e}'.format(e=dberror)) raise errors.InvalidInputFormat(dberror) return anon_serialize_step(store, step, language)
def db_create_field(store, request, language): """ Add a new field to the store, then return the new serialized object. :param: store: the store reference :param: request: the field definition dict :param: language: the language of the field definition dict :return: a serialization of the object """ is_template, step_id, fieldgroup_id = field_integrity_check(request) # XXX you probably want to split this if else statement into two functions # that are called create_field_from_template and create_field if not 'template_id' in request: fill_localized_keys(request, models.Field.localized_strings, language) field = models.Field.new(store, request) db_update_options(store, field.id, request['options'], language) else: template = store.find(models.Field, models.Field.id == request['template_id']).one() if not template: raise errors.InvalidInputFormat("The specified template id %s does not exist" % request.get('template_id')) field = template.copy(store, is_template) associate_field(store, field, step_id, fieldgroup_id) return anon_serialize_field(store, field, language)
def db_create_step(store, context, steps, language): """ Add the specified steps :param store: the store on which perform queries. :param context: the context on which register specified steps. :param steps: a dictionary containing the new steps. :param language: the language of the specified steps. """ n = 1 for step in steps: step['context_id'] = context.id step['number'] = n fill_localized_keys(step, models.Step.localized_strings, language) s = models.Step.new(store, step) for f in step['children']: field = models.Field.get(store, f['id']) if not field: log.err("Creation error: unexistent field can't be associated") raise errors.FieldIdNotFound # remove current step/field fieldgroup/field association a_s, _ = get_field_association(store, field.id) if a_s != s.id: disassociate_field(store, field.id) s.children.add(field) n += 1
def db_admin_update_user(store, user_id, request, language): """ Updates the specified user. raises: globaleaks.errors.ReceiverIdNotFound` if the receiver does not exist. """ user = models.User.get(store, user_id) if not user: raise errors.UserIdNotFound fill_localized_keys(request, models.User.localized_keys, language) user.name = request['name'] user.description = request['description'] user.state = request['state'] user.password_change_needed = request['password_change_needed'] user.mail_address = request['mail_address'] user.language = request.get('language', GLSettings.memory_copy.default_language) user.timezone = request.get('timezone', GLSettings.memory_copy.default_timezone) password = request['password'] if len(password): security.check_password_format(password) user.password = security.hash_password(password, user.salt) user.password_change_date = datetime_now() # The various options related in manage PGP keys are used here. parse_pgp_options(user, request) return user
def db_update_step(store, step_id, step, language): fill_localized_keys(step, models.Step.localized_strings, language) s = store.find(models.Step, models.Step.id == step_id).one() s.update(step) s.children.clear() for child in step['children']: child['step_id'] = s.id child['fieldgroup_id'] = '' db_update_field(store, child['id'], child, language) # remove current step/field fieldgroup/field association a_s, _ = get_field_association(store, child['id']) if a_s is None: s.children.add(f) elif a_s != s.id: disassociate_field(store, f.id) s.children.add(f) else: # the else condition means a_s == s.id; already associated! pass return s.id
def db_create_receiver(store, request, language): """ Creates a new receiver Returns: (dict) the receiver descriptor """ user = db_create_user(store, request, language) fill_localized_keys(request, models.Receiver.localized_keys, language) receiver = models.Receiver(request) # set receiver.id user.id receiver.id = user.id store.add(receiver) contexts = request.get('contexts', []) for context_id in contexts: context = models.Context.get(store, context_id) if not context: raise errors.ContextIdNotFound context.receivers.add(receiver) log.debug("Created new receiver") return receiver
def db_update_node(store, request, wizard_done, language): """ Update and serialize the node infos :param store: the store on which perform queries. :param language: the language in which to localize data :return: a dictionary representing the serialization of the node """ node = store.find(models.Node).one() fill_localized_keys(request, models.Node.localized_strings, language) admin = store.find(models.User, (models.User.username == unicode('admin'))).one() admin.mail_address = request['email'] admin.language = request['admin_language'] admin.timezone = request['admin_timezone'] password = request.get('password', None) old_password = request.get('old_password', None) if password and old_password and len(password) and len(old_password): admin.password = security.change_password(admin.password, old_password, password, admin.salt) # verify that the languages enabled are valid 'code' in the languages supported node.languages_enabled = [] for lang_code in request['languages_enabled']: if lang_code in LANGUAGES_SUPPORTED_CODES: node.languages_enabled.append(lang_code) else: raise errors.InvalidInputFormat("Invalid lang code enabled: %s" % lang_code) if not len(node.languages_enabled): raise errors.InvalidInputFormat("Missing enabled languages") # enforcing of default_language usage (need to be set, need to be _enabled) if request['default_language']: if request['default_language'] not in node.languages_enabled: raise errors.InvalidInputFormat("Invalid lang code as default") node.default_language = request['default_language'] else: node.default_language = node.languages_enabled[0] log.err("Default language not set!? fallback on %s" % node.default_language) if wizard_done: node.wizard_done = True try: node.update(request) except DatabaseError as dberror: log.err("Unable to update node: %s" % dberror) raise errors.InvalidInputFormat(dberror) db_update_memory_variables(store) return db_admin_serialize_node(store, language)
def db_create_field(store, field_dict, language): """ Create and add a new field to the store, then return the new serialized object. :param store: the store on which perform queries. :param field_dict: the field definition dict :param language: the language of the field definition dict :return: a serialization of the object """ fill_localized_keys(field_dict, models.Field.localized_keys, language) field = models.Field(field_dict) if field_dict["template_id"] != "": field.template_id = field_dict["template_id"] if field_dict["step_id"] != "": field.step_id = field_dict["step_id"] if field_dict["fieldgroup_id"] != "": ancestors = set(fieldtree_ancestors(store, field_dict["fieldgroup_id"])) if field.id == field_dict["fieldgroup_id"] or field.id in ancestors: raise errors.InvalidInputFormat("Provided field association would cause recursion loop") field.fieldgroup_id = field_dict["fieldgroup_id"] store.add(field) if field.template: # special handling of the whistleblower_identity field if field.template.key == "whistleblower_identity": if field.step: if ( store.find( models.Field, models.Field.key == u"whistleblower_identity", models.Field.step_id == models.Step.id, models.Step.questionnaire_id == models.Questionnaire.id, models.Questionnaire.id == field.step.questionnaire_id, ).count() == 0 ): field.step.questionnaire.enable_whistleblower_identity = True else: raise errors.InvalidInputFormat("Whistleblower identity field already present") else: raise errors.InvalidInputFormat("Cannot associate whistleblower identity field to a fieldgroup") else: db_update_fieldattrs(store, field.id, field_dict["attrs"], language) db_update_fieldoptions(store, field.id, field_dict["options"], language) if field.instance != "reference": for c in field_dict["children"]: c["fieldgroup_id"] = field.id db_create_field(store, c, language) return field
def update_receiver(store, receiver_id, request, language=GLSetting.memory_copy.default_language): """ Updates the specified receiver with the details. raises :class:`globaleaks.errors.ReceiverIdNotFound` if the receiver does not exist. """ receiver = models.Receiver.get(store, receiver_id) if not receiver: raise errors.ReceiverIdNotFound fill_localized_keys(request, models.Receiver.localized_strings, language) mail_address = request['mail_address'] homonymous = store.find(models.User, models.User.username == mail_address).one() if homonymous and homonymous.id != receiver.user_id: log.err("Update error: already present receiver with the requested username: %s" % mail_address) raise errors.ExpectedUniqueField('mail_address', mail_address) receiver.mail_address = mail_address # the email address it's also the username, stored in User receiver.user.username = mail_address receiver.user.state = request['state'] # The various options related in manage GPG keys are used here. gpg_options_parse(receiver, request) receiver.user.language = request.get('language', GLSetting.memory_copy.default_language) receiver.user.timezone = request.get('timezone', GLSetting.memory_copy.default_timezone) password = request['password'] if len(password): security.check_password_format(password) receiver.user.password = security.hash_password(password, receiver.user.salt) receiver.user.password_change_date = datetime_now() contexts = request.get('contexts', []) for context in receiver.contexts: receiver.contexts.remove(context) for context_id in contexts: context = models.Context.get(store, context_id) if not context: log.err("Update error: unexistent context can't be associated") raise errors.ContextIdNotFound receiver.contexts.add(context) receiver.last_update = datetime_now() try: receiver.update(request) except DatabaseError as dberror: log.err("Unable to update receiver %s: %s" % (receiver.name, dberror)) raise errors.InvalidInputFormat(dberror) return admin_serialize_receiver(receiver, language)
def update_context(store, context_id, request, language): """ Updates the specified context. If the key receivers is specified we remove the current receivers of the Context and reset set it to the new specified ones. If no such context exists raises :class:`globaleaks.errors.ContextIdNotFound`. Args: context_id: request: (dict) the request to use to set the attributes of the Context Returns: (dict) the serialized object updated """ context = store.find(models.Context, models.Context.id == context_id).one() if not context: raise errors.ContextIdNotFound receivers = request.get('receivers', []) steps = request.get('steps', []) fill_localized_keys(request, models.Context.localized_strings, language) for receiver in context.receivers: context.receivers.remove(receiver) for receiver_id in receivers: receiver = store.find(models.Receiver, models.Receiver.id == receiver_id).one() if not receiver: log.err("Update error: unexistent receiver can't be associated") raise errors.ReceiverIdNotFound context.receivers.add(receiver) context.tip_timetolive = acquire_context_timetolive(int(request['tip_timetolive'])) if request['select_all_receivers']: if request['maximum_selectable_receivers']: log.debug("Resetting maximum_selectable_receivers (%d) because 'select_all_receivers' is True" % request['maximum_selectable_receivers']) request['maximum_selectable_receivers'] = 0 context.last_update = datetime_now() try: context.update(request) except DatabaseError as dberror: log.err("Unable to update context %s: %s" % (context.name, dberror)) raise errors.InvalidInputFormat(dberror) if request['reset_steps']: db_update_steps(store, context, [], language) db_setup_default_steps(store, context) else: db_update_steps(store, context, steps, language) return admin_serialize_context(store, context, language)
def create_dummy_field(self, store, **custom_attrs): field = self.get_dummy_field() fill_localized_keys(field, models.Field.localized_keys, 'en') field.update(custom_attrs) return models.Field.new(store, field).id
def update_field(store, field_id, request, language): """ Update the specified field with the details. raises :class:`globaleaks.errors.FieldIdNotFound` if the field does not exist. :param store: the store on which perform queries. :param: field_id: the field_id of the field to update :param: request: the field definition dict :param: language: the language of the field definition dict :return: a serialization of the object """ errmsg = 'Invalid or not existent field ids in request.' is_template, step_id, fieldgroup_id = field_integrity_check(request) field = models.Field.get(store, field_id) try: if not field: raise errors.InvalidInputFormat(errmsg) fill_localized_keys(request, models.Field.localized_strings, language) field.update(request) # children handling: # - old children are cleared # - new provided childrens are evaluated and added children = request['children'] if children and field.type != 'fieldgroup': raise errors.InvalidInputFormat("children can be associated only to fields of type fieldgroup") ancestors = set(fieldtree_ancestors(store, field.id)) field.children.clear() for c in children: child = models.Field.get(store, c['id']) # check child do exists and graph is not recursive if not child or child.id == field.id or child.id in ancestors: raise errors.InvalidInputFormat(errmsg) # remove current step/field fieldgroup/field association disassociate_field(store, child.id) field.children.add(child) db_update_options(store, field.id, request['options'], language) # remove current step/field fieldgroup/field association disassociate_field(store, field_id) associate_field(store, field, step_id, fieldgroup_id) except Exception as dberror: log.err('Unable to update field: {e}'.format(e=dberror)) raise errors.InvalidInputFormat(dberror) return anon_serialize_field(store, field, language)
def migrate_ArchivedSchema(self): # Marking to avoid count check for ArchivedSchema self.fail_on_count_mismatch["ArchivedSchema"] = False old_node = self.store_old.find(self.model_from["Node"]).one() old_objs = self.store_old.find( self.model_from["ArchivedSchema"], self.model_from["ArchivedSchema"].language == old_node.default_language ) new_obj_model = self.model_to["ArchivedSchema"] def fill_field_localized_keys_recursively(f, language): for k in ["label", "description", "hint", "multi_entry_hint"]: try: f[k] = {language: f.get(k, "")} except Exception: f[k] = {language: ""} f["attrs"] = {} for o in f.get("options", []): try: if "label" in o: o["label"] = {language: o.get("label", "")} elif "attrs" in o: if "name" in o["attrs"]: o["label"] = {language: o["attrs"].get("name", "")} del o["attrs"] except Exception: pass for c in f.get("children", []): fill_field_localized_keys_recursively(c, language) for old_obj in old_objs: new_obj = self.store_new.find( new_obj_model, And(new_obj_model.hash == old_obj.hash, new_obj_model.type == old_obj.type) ).one() if not new_obj: new_obj = new_obj_model() for _, v in new_obj._storm_columns.iteritems(): if v.name == "schema": if not isinstance(new_obj.schema, dict): new_obj.value = {} for step in old_obj.schema: fill_localized_keys(step, models.Step.localized_keys, old_node.default_language) for c in step.get("children", []): fill_field_localized_keys_recursively(c, old_node.default_language) new_obj.schema = old_obj.schema continue setattr(new_obj, v.name, getattr(old_obj, v.name)) self.store_new.add(new_obj)
def db_create_receiver(store, request, language): """ Creates a new receiver. Returns: (dict) the configured receiver """ fill_localized_keys(request, models.Receiver.localized_strings, language) password = request['password'] if len(password) and password != GLSettings.default_password: security.check_password_format(password) else: password = GLSettings.default_password receiver_salt = security.get_salt(rstr.xeger('[A-Za-z0-9]{56}')) receiver_password = security.hash_password(password, receiver_salt) # ping_mail_address is duplicated at creation time from mail_address request.update({'ping_mail_address': request['mail_address']}) receiver = models.Receiver(request) receiver_user_dict = { 'username': uuid4(), 'password': receiver_password, 'salt': receiver_salt, 'role': u'receiver', 'state': u'enabled', 'language': u'en', 'timezone': 0, 'password_change_needed': True, 'mail_address': request['mail_address'] } receiver_user = models.User(receiver_user_dict) # The various options related in manage PGP keys are used here. pgp_options_parse(receiver, request) # Set receiver.id = receiver.user.username = receiver.user.id receiver.id = receiver_user.username = receiver_user.id store.add(receiver_user) store.add(receiver) create_random_receiver_portrait(receiver.id) contexts = request.get('contexts', []) for context_id in contexts: context = models.Context.get(store, context_id) if not context: log.err("Creation error: invalid Context can't be associated") raise errors.ContextIdNotFound context.receivers.add(receiver) log.debug("Created receiver %s" % receiver.user.username) return admin_serialize_receiver(receiver, language)
def db_create_context(store, request, language): """ Creates a new context from the request of a client. We associate to the context the list of receivers and if the receiver is not valid we raise a ReceiverIdNotFound exception. Args: (dict) the request containing the keys to set on the model. Returns: (dict) representing the configured context """ receivers = request.get('receivers', []) steps = request.get('steps', []) fill_localized_keys(request, models.Context.localized_strings, language) context = models.Context(request) # Integrity checks related on name (need to exists, need to be unique) # are performed only using the default language at the moment (XXX) try: context_name = request['name'][language] except Exception as excep: raise errors.InvalidInputFormat("language %s do not provide name: %s" % (language, excep) ) if len(context_name) < 1: log.err("Invalid request: name is an empty string") raise errors.InvalidInputFormat("Context name is missing (1 char required)") if request['select_all_receivers']: if request['maximum_selectable_receivers']: log.debug("Resetting maximum_selectable_receivers (%d) because 'select_all_receivers' is True" % request['maximum_selectable_receivers']) request['maximum_selectable_receivers'] = 0 # tip_timetolive to be converted in seconds since hours and days context.tip_timetolive = acquire_context_timetolive(int(request['tip_timetolive'])) c = store.add(context) for receiver_id in receivers: receiver = models.Receiver.get(store, receiver_id) if not receiver: log.err("Creation error: unexistent context can't be associated") raise errors.ReceiverIdNotFound c.receivers.add(receiver) if steps: db_create_steps(store, c.id, steps, language) else: db_setup_default_steps(store, c.id) log.debug("Created context %s (using %s)" % (context_name, language) ) return admin_serialize_context(store, context, language)
def migrate_ArchivedSchema(self): # Marking to avoid count check for ArchivedSchema self.fail_on_count_mismatch["ArchivedSchema"] = False old_node = self.store_old.find(self.model_from['Node']).one() old_objs = self.store_old.find(self.model_from['ArchivedSchema'], self.model_from['ArchivedSchema'].language == old_node.default_language) new_obj_model = self.model_to['ArchivedSchema'] def fill_field_localized_keys_recursively(f, language): for k in ['label', 'description', 'hint', 'multi_entry_hint']: try: f[k] = {language: f.get(k, '')} except Exception: f[k] = {language: ''} f['attrs'] = {} for o in f.get('options', []): try: if 'label' in o: o['label'] = {language: o.get('label', '')} elif 'attrs' in o: if 'name' in o['attrs']: o['label'] = {language: o['attrs'].get('name', '')} del o['attrs'] except Exception: pass for c in f.get('children', []): fill_field_localized_keys_recursively(c, language) for old_obj in old_objs: new_obj = self.store_new.find(new_obj_model, And(new_obj_model.hash == old_obj.hash, new_obj_model.type == old_obj.type)).one() if not new_obj: new_obj = new_obj_model() for _, v in new_obj._storm_columns.iteritems(): if v.name == 'schema': if not isinstance(new_obj.schema, dict): new_obj.value = {} for step in old_obj.schema: fill_localized_keys(step, models.Step.localized_keys, old_node.default_language) for c in step.get('children', []): fill_field_localized_keys_recursively(c, old_node.default_language) new_obj.schema = old_obj.schema continue setattr(new_obj, v.name, getattr(old_obj, v.name)) self.store_new.add(new_obj)
def create_dummy_field(self, store, **custom_attrs): field = get_dummy_field() fill_localized_keys(field, models.Field.localized_keys, 'en') field.update(custom_attrs) f = models.Field(field) store.add(f) return f.id
def fill_context_request(request, language): fill_localized_keys(request, models.Context.localized_keys, language) request['tip_timetolive'] = acquire_context_timetolive(int(request['tip_timetolive'])) if request['select_all_receivers']: if request['maximum_selectable_receivers']: log.debug("Resetting maximum_selectable_receivers (%d) because 'select_all_receivers' is True" % request['maximum_selectable_receivers']) request['maximum_selectable_receivers'] = 0 return request
def db_update_fieldattrs(store, field_id, field_attrs, language): """ """ attrs_ids = [] for name, value in field_attrs.iteritems(): value['name'] = name if value['type'] == u'localized': fill_localized_keys(value, ['value'], language) attrs_ids.append(db_update_fieldattr(store, field_id, value)) store.find(models.FieldAttr, And(models.FieldAttr.field_id == field_id, Not(In(models.FieldAttr.id, attrs_ids)))).remove()
def db_update_node(store, request, wizard_done, language): """ Update and serialize the node infos :param store: the store on which perform queries. :param language: the language in which to localize data :return: a dictionary representing the serialization of the node """ node = store.find(models.Node).one() fill_localized_keys(request, models.Node.localized_keys, language) # verify that the languages enabled are valid 'code' in the languages supported node.languages_enabled = [] for lang_code in request['languages_enabled']: if lang_code in LANGUAGES_SUPPORTED_CODES: node.languages_enabled.append(lang_code) else: raise errors.InvalidInputFormat("Invalid lang code enabled: %s" % lang_code) if not len(node.languages_enabled): raise errors.InvalidInputFormat("Missing enabled languages") # enforcing of default_language usage (need to be set, need to be _enabled) if request['default_language']: if request['default_language'] not in node.languages_enabled: raise errors.InvalidInputFormat("Invalid lang code as default") node.default_language = request['default_language'] else: node.default_language = node.languages_enabled[0] log.err("Default language not set!? fallback on %s" % node.default_language) node.basic_auth = request['basic_auth'] if request['basic_auth'] and request['basic_auth_username'] != '' and request['basic_auth_password'] != '': node.basic_auth = True node.basic_auth_username = request['basic_auth_username'] node.basic_auth_password = request['basic_auth_password'] else: node.basic_auth = False if wizard_done: node.wizard_done = True node.update(request) db_refresh_memory_variables(store) return db_admin_serialize_node(store, language)
def update_receiver(store, receiver_id, request, language): """ Updates the specified receiver with the details. raises :class:`globaleaks.errors.ReceiverIdNotFound` if the receiver does not exist. """ receiver = models.Receiver.get(store, receiver_id) if not receiver: raise errors.ReceiverIdNotFound fill_localized_keys(request, models.Receiver.localized_strings, language) receiver.user.state = request['state'] receiver.user.password_change_needed = request['password_change_needed'] # The various options related in manage PGP keys are used here. pgp_options_parse(receiver, request) receiver.user.language = request.get('language', GLSetting.memory_copy.language) receiver.user.timezone = request.get('timezone', GLSetting.memory_copy.default_timezone) password = request['password'] if len(password): security.check_password_format(password) receiver.user.password = security.hash_password(password, receiver.user.salt) receiver.user.password_change_date = datetime_now() contexts = request.get('contexts', []) for context in receiver.contexts: receiver.contexts.remove(context) for context_id in contexts: context = models.Context.get(store, context_id) if not context: raise errors.ContextIdNotFound receiver.contexts.add(context) receiver.last_update = datetime_now() try: receiver.update(request) except DatabaseError as dberror: log.err("Unable to update receiver %s: %s" % (receiver.name, dberror)) raise errors.InvalidInputFormat(dberror) return admin_serialize_receiver(receiver, language)
def db_create_field(store, field_dict, language): """ Create and add a new field to the store, then return the new serialized object. :param store: the store on which perform queries. :param field_dict: the field definition dict :param language: the language of the field definition dict :return: a serialization of the object """ fill_localized_keys(field_dict, models.Field.localized_keys, language) field = models.Field(field_dict) if field_dict['template_id'] != '': field.template_id = field_dict['template_id'] if field_dict['step_id'] != '': field.step_id = field_dict['step_id'] if field_dict['fieldgroup_id'] != '': ancestors = set(fieldtree_ancestors(store, field_dict['fieldgroup_id'])) if field.id == field_dict['fieldgroup_id'] or field.id in ancestors: raise errors.InvalidInputFormat("Provided field association would cause recursion loop") field.fieldgroup_id = field_dict['fieldgroup_id'] store.add(field) if field.template: # special handling of the whistleblower_identity field if field.template.key == 'whistleblower_identity': if field.step: if not field.step.questionnaire.enable_whistleblower_identity: field.step.questionnaire.enable_whistleblower_identity = True else: raise errors.InvalidInputFormat("Whistleblower identity field already present") else: raise errors.InvalidInputFormat("Cannot associate whistleblower identity field to a fieldgroup") else: db_update_fieldattrs(store, field.id, field_dict['attrs'], language) db_update_fieldoptions(store, field.id, field_dict['options'], language) for c in field_dict['children']: c['fieldgroup_id'] = field.id db_create_field(store, c, language) return field
def update_notification(store, request, language): notif = store.find(Notification).one() fill_localized_keys(request, Notification.localized_strings, language) if request['reset_templates']: appdata_dict = load_appdata() for k in appdata_dict['templates']: request[k] = appdata_dict['templates'][k] notif.update(request) db_update_memory_variables(store) return admin_serialize_notification(notif, language)
def db_create_step(store, step, language): """ Create the specified step :param store: the store on which perform queries. :param language: the language of the specified steps. """ fill_localized_keys(step, models.Step.localized_keys, language) s = models.Step.new(store, step) for c in step['children']: c['step_id'] = s.id db_create_field(store, c, language) return s
def db_update_fieldattr(store, field_id, attr_name, attr_dict, language): attr = store.find(models.FieldAttr, And(models.FieldAttr.field_id == field_id, models.FieldAttr.name == attr_name)).one() if not attr: attr = models.FieldAttr() attr_dict['name'] = attr_name attr_dict['field_id'] = field_id if attr_dict['type'] == 'bool': attr_dict['value'] = 'True' if attr_dict['value'] == True else 'False' elif attr_dict['type'] == u'localized': fill_localized_keys(attr_dict, ['value'], language) attr.update(attr_dict) store.add(attr) return attr.id
def db_update_field(store, field_id, field_dict, language): field = models.Field.get(store, field_id) if not field: raise errors.FieldIdNotFound # To be uncommented upon completion of fields implementaion # if not field.editable: # raise errors.FieldNotEditable try: # make not possible to change field type field_dict['type'] = field.type if field_dict['instance'] != 'reference': fill_localized_keys(field_dict, models.Field.localized_keys, language) db_update_fieldattrs(store, field.id, field_dict['attrs'], language) db_update_fieldoptions(store, field.id, field_dict['options'], language) # full update field.update(field_dict) else: # partial update partial_update = { 'x': field_dict['x'], 'y': field_dict['y'], 'width': field_dict['width'], 'multi_entry': field_dict['multi_entry'] } field.update(partial_update) except Exception as dberror: log.err('Unable to update field: {e}'.format(e=dberror)) raise errors.InvalidInputFormat(dberror) return field
def db_update_fieldattr(session, tid, field_id, attr_name, attr_dict, language): attr_dict['name'] = attr_name attr_dict['field_id'] = field_id attr_dict['tid'] = tid if attr_dict['type'] == 'bool': attr_dict['value'] = 'True' if attr_dict['value'] else 'False' elif attr_dict['type'] == u'localized': fill_localized_keys(attr_dict, ['value'], language) a = session.query(models.FieldAttr).filter( models.FieldAttr.field_id == field_id, models.FieldAttr.name == attr_name, models.FieldAttr.field_id == models.Field.id, models.Field.tid == tid).one_or_none() if not a: a = models.db_forge_obj(session, models.FieldAttr, attr_dict) else: a.update(attr_dict) return a.id
def db_create_receiver_user(store, request, language): """ Creates a new receiver Returns: (dict) the receiver descriptor """ fill_localized_keys(request, models.Receiver.localized_keys, language) user = db_create_user(store, request, language) receiver = models.db_forge_obj(store, models.Receiver, request) # set receiver.id user.id receiver.id = user.id contexts = request.get('contexts', []) db_associate_context_receivers(store, receiver, contexts) log.debug("Created new receiver") return receiver, user
def db_update_step(session, tid, step_id, request, language): """ Update the specified step with the details. :param session: the session on which perform queries. :param step_id: the step_id of the step to update :param request: the step definition dict :param language: the language of the step definition dict :return: a serialization of the object """ step = models.db_get(session, models.Step, models.Step.id == step_id, models.Questionnaire.id == models.Step.questionnaire_id, models.Questionnaire.tid == tid) fill_localized_keys(request, models.Step.localized_keys, language) step.update(request) for child in request['children']: db_update_field(session, tid, child['id'], child, language) return step
def db_admin_update_user(store, user_id, request, language): """ Updates the specified user. raises: globaleaks.errors.ReceiverIdNotFound` if the receiver does not exist. """ user = models.User.get(store, user_id) if not user: raise errors.UserIdNotFound fill_localized_keys(request, models.User.localized_keys, language) user.update(request) password = request['password'] if len(password) > 0: user.password = security.hash_password(password, user.salt) user.password_change_date = datetime_now() # The various options related in manage PGP keys are used here. parse_pgp_options(user, request) return user
def db_create_user(store, request, language): fill_localized_keys(request, models.User.localized_keys, language) password = request['password'] if len(password) and password != GLSettings.default_password: security.check_password_format(password) else: password = GLSettings.default_password password_salt = security.get_salt(rstr.xeger('[A-Za-z0-9]{56}')) password_hash = security.hash_password(password, password_salt) user = models.User({ 'username': request['username'], 'password': password_hash, 'salt': password_salt, 'role': request['role'], 'state': u'enabled', 'deletable': request['deletable'], 'name': request['name'], 'description': request['description'], 'language': u'en', 'timezone': 0, 'password_change_needed': True, 'mail_address': request['mail_address'] }) if request['username'] == '': user.username = user.id # The various options related in manage PGP keys are used here. parse_pgp_options(user, request) create_user_picture(user.id) store.add(user) return user
def db_update_fieldoption(session, tid, field_id, fieldoption_id, option_dict, language, idx): option_dict['tid'] = tid option_dict['field_id'] = field_id fill_localized_keys(option_dict, models.FieldOption.localized_keys, language) o = None if fieldoption_id is not None: o = session.query(models.FieldOption).filter( models.FieldOption.id == fieldoption_id, models.FieldOption.field_id == models.Field.id, models.Field.tid == tid).one_or_none() if o is None: o = models.db_forge_obj(session, models.FieldOption, option_dict) else: o.update(option_dict) o.presentation_order = idx return o.id
def db_admin_update_user(session, state, tid, user_id, request, language): """ Updates the specified user. """ fill_localized_keys(request, models.User.localized_keys, language) user = models.db_get(session, models.User, models.User.tid == tid, models.User.id == user_id) user.update(request) password = request['password'] if password: user.password = security.hash_password(password, user.salt) user.password_change_date = datetime_now() # The various options related in manage PGP keys are used here. parse_pgp_options(state, user, request) if user.role == 'admin': db_refresh_memory_variables(session, [tid]) return user
def db_admin_update_user(store, user_id, request, language): """ Updates the specified user. raises: globaleaks.errors.UserIdNotFound` if the user does not exist. """ fill_localized_keys(request, models.User.localized_keys, language) user = models.db_get(store, models.User, id=user_id) user.update(request) password = request['password'] if password: user.password = security.hash_password(password, user.salt) user.password_change_date = datetime_now() # The various options related in manage PGP keys are used here. parse_pgp_options(user, request) if user.role == 'admin': db_refresh_exception_delivery_list(store) return user
def db_create_field(store, field_dict, language): """ Create and add a new field to the store, then return the new serialized object. :param store: the store on which perform queries. :param field: the field definition dict :param language: the language of the field definition dict :return: a serialization of the object """ _, template, step, fieldgroup = field_integrity_check(store, field_dict) fill_localized_keys(field_dict, models.Field.localized_keys, language) field = models.Field.new(store, field_dict) associate_field(store, field, template, step, fieldgroup) if field.template: # special handling of the whistleblower_identity field if field.template.key == 'whistleblower_identity': if field.step: if not field.step.questionnaire.enable_whistleblower_identity: field.step.questionnaire.enable_whistleblower_identity = True else: raise errors.InvalidInputFormat("Whistleblower identity field already present") else: raise errors.InvalidInputFormat("Cannot associate whistleblower identity field to a fieldgroup") else: db_update_fieldattrs(store, field.id, field_dict['attrs'], language) db_update_fieldoptions(store, field.id, field_dict['options'], language) for c in field_dict['children']: c['fieldgroup_id'] = field.id db_create_field(store, c, language) return field
def db_update_step(store, step_id, request, language): """ Update the specified step with the details. raises :class:`globaleaks.errors.StepIdNotFound` if the step does not exist. :param store: the store on which perform queries. :param step_id: the step_id of the step to update :param request: the step definition dict :param language: the language of the step definition dict :return: a serialization of the object """ step = models.Step.get(store, step_id) if not step: raise errors.StepIdNotFound fill_localized_keys(request, models.Step.localized_keys, language) step.update(request) for child in request['children']: db_update_field(store, child['id'], child, language) return step
def db_update_field(store, field_id, field_dict, language): field = models.db_get(store, models.Field, id=field_id) # make not possible to change field type field_dict['type'] = field.type if field_dict['instance'] != 'reference': fill_localized_keys(field_dict, models.Field.localized_keys, language) db_update_fieldattrs(store, field, field_dict['attrs'], language) db_update_fieldoptions(store, field, field_dict['options'], language) # full update field.update(field_dict) else: # partial update field.update({ 'x': field_dict['x'], 'y': field_dict['y'], 'width': field_dict['width'], 'multi_entry': field_dict['multi_entry'] }) return field
def db_update_fieldoption(store, fieldoption_id, option, language): fill_localized_keys(option, models.FieldOption.localized_keys, language) if fieldoption_id is not None: o = store.find(models.FieldOption, models.FieldOption.id == fieldoption_id).one() else: o = None if o is None: o = models.FieldOption() store.add(o) o.update(option) for activated_field in option['activated_fields']: o.activated_fields.add( store.find(models.Field, models.Field.id == activated_field)) for activated_step in option['activated_steps']: o.activated_steps.add( store.find(models.Step, models.Step.id == activated_step)) return o.id
def migrate_ArchivedSchema(self): # Marking to avoid count check for ArchivedSchema self.fail_on_count_mismatch["ArchivedSchema"] = False old_node = self.store_old.find(self.model_from['Node']).one() old_objs = self.store_old.find( self.model_from['ArchivedSchema'], self.model_from['ArchivedSchema'].language == old_node.default_language) new_obj_model = self.model_to['ArchivedSchema'] def fill_field_localized_keys_recursively(f, language): for k in ['label', 'description', 'hint', 'multi_entry_hint']: try: f[k] = {language: f.get(k, '')} except Exception: f[k] = {language: ''} f['attrs'] = {} for o in f.get('options', []): try: if 'label' in o: o['label'] = {language: o.get('label', '')} elif 'attrs' in o: if 'name' in o['attrs']: o['label'] = {language: o['attrs'].get('name', '')} del o['attrs'] except Exception: pass for c in f.get('children', []): fill_field_localized_keys_recursively(c, language) for old_obj in old_objs: new_obj = self.store_new.find( new_obj_model, And(new_obj_model.hash == old_obj.hash, new_obj_model.type == old_obj.type)).one() if not new_obj: new_obj = new_obj_model() for _, v in new_obj._storm_columns.iteritems(): if v.name == 'schema': if not isinstance(new_obj.schema, dict): new_obj.value = {} for step in old_obj.schema: fill_localized_keys(step, models.Step.localized_keys, old_node.default_language) for c in step.get('children', []): fill_field_localized_keys_recursively( c, old_node.default_language) new_obj.schema = old_obj.schema continue setattr(new_obj, v.name, getattr(old_obj, v.name)) self.store_new.add(new_obj)
def db_create_field(session, tid, field_dict, language): """ Create and add a new field to the session, then return the new serialized object. :param session: the session on which perform queries. :param field_dict: the field definition dict :param language: the language of the field definition dict :return: a serialization of the object """ field_dict['tid'] = tid fill_localized_keys(field_dict, models.Field.localized_keys, language) check_field_association(session, tid, field_dict) if field_dict.get('template_id', '') != '': if field_dict['template_id'] == 'whistleblower_identity': if field_dict.get('step_id', '') == '': raise errors.InputValidationError( "Cannot associate whistleblower identity field to a fieldgroup" ) q_id = session.query(models.Questionnaire.id) \ .filter(models.Questionnaire.id == models.Step.questionnaire_id, models.Step.id == field_dict['step_id']) field = session.query(models.Field) \ .filter(models.Field.template_id == u'whistleblower_identity', models.Field.step_id == models.Step.id, models.Step.questionnaire_id.in_(q_id.subquery())).one_or_none() if field is not None: raise errors.InputValidationError( "Whistleblower identity field already present") field = models.db_forge_obj(session, models.Field, field_dict) template = session.query(models.Field).filter( models.Field.id == field_dict['template_id']).one() field.label = template.label field.hint = template.hint field.description = template.description field_attrs = read_json_file(Settings.field_attrs_file) attrs = field_attrs.get(field.template_id, {}) db_add_field_attrs(session, field.id, attrs) else: field = models.db_forge_obj(session, models.Field, field_dict) attrs = field_dict.get('attrs', []) options = field_dict.get('options', []) db_update_fieldattrs(session, tid, field.id, attrs, language) db_update_fieldoptions(session, tid, field.id, options, language) if field.instance != 'reference': for c in field_dict.get('children', []): c['tid'] = field.tid c['fieldgroup_id'] = field.id db_create_field(session, tid, c, language) return field
def db_update_questionnaire(session, questionnaire, request, language): fill_localized_keys(request, models.Questionnaire.localized_keys, language) questionnaire.update(request) return questionnaire
def fill_questionnaire_request(request, language): fill_localized_keys(request, models.Questionnaire.localized_keys, language) return request
def db_update_node(store, request, wizard_done, language): """ Update and serialize the node infos :param store: the store on which perform queries. :param language: the language in which to localize data :return: a dictionary representing the serialization of the node """ node = store.find(models.Node).one() fill_localized_keys(request, models.Node.localized_strings, language) admin = store.find(models.User, (models.User.username == unicode('admin'))).one() admin.language = request.get('admin_language', GLSetting.memory_copy.language) admin.timezone = request.get('admin_timezone', GLSetting.memory_copy.default_timezone) password = request.get('password', None) old_password = request.get('old_password', None) if password and old_password and len(password) and len(old_password): admin.password = security.change_password(admin.password, old_password, password, admin.salt) # verify that the languages enabled are valid 'code' in the languages supported node.languages_enabled = [] for lang_code in request['languages_enabled']: if lang_code in LANGUAGES_SUPPORTED_CODES: node.languages_enabled.append(lang_code) else: raise errors.InvalidInputFormat("Invalid lang code enabled: %s" % lang_code) if not len(node.languages_enabled): raise errors.InvalidInputFormat("Missing enabled languages") # enforcing of default_language usage (need to be set, need to be _enabled) if request['default_language']: if request['default_language'] not in LANGUAGES_SUPPORTED_CODES: raise errors.InvalidInputFormat("Invalid lang code as default") if request['default_language'] not in node.languages_enabled: raise errors.InvalidInputFormat("Invalid lang code as default") node.default_language = request['default_language'] else: node.default_language = node.languages_enabled[0] log.err("Default language not set!? fallback on %s" % node.default_language) if wizard_done: node.wizard_done = True # since change of regexp format to XXXX-XXXX-XXXX-XXXX # we removed the possibility to customize the receipt from the GLCllient request['receipt_regexp'] = GLSetting.defaults.receipt_regexp try: node.update(request) except DatabaseError as dberror: log.err("Unable to update Node: %s" % dberror) raise errors.InvalidInputFormat(dberror) node.last_update = datetime_now() db_import_memory_variables(store) return db_admin_serialize_node(store, language)
def db_update_field(store, field_id, field, language): _, step, fieldgroup = field_integrity_check(store, field) fill_localized_keys(field, models.Field.localized_strings, language) if field['template_id'] == '': field['template_id'] = None f = models.Field.get(store, field_id) if not f: raise errors.FieldIdNotFound try: # make not possible to change field type field['type'] = f.type if field['template_id'] is None: # children handling: # - old children are cleared # - new provided childrens are evaluated and added children = field['children'] if len(children) and f.type != 'fieldgroup': raise errors.InvalidInputFormat( "children can be associated only to fields of type fieldgroup" ) ancestors = set(fieldtree_ancestors(store, f.id)) f.children.clear() for child in children: if child['id'] == f.id or child['id'] in ancestors: raise errors.FieldIdNotFound c = db_update_field(store, child['id'], child, language) # remove current step/field fieldgroup/field association disassociate_field(store, c.id) f.children.add(c) db_update_fieldattrs(store, f.id, field['attrs'], language) db_update_fieldoptions(store, f.id, field['options'], language) # full update f.update(field) else: # partial update partial_update = { 'x': field['x'], 'y': field['y'], 'width': field['width'], 'stats_enabled': field['stats_enabled'], 'multi_entry': field['multi_entry'], 'required': field['required'] } f.update(partial_update) # remove current step/field fieldgroup/field association disassociate_field(store, field_id) associate_field(store, f, step, fieldgroup) except Exception as dberror: log.err('Unable to update field: {e}'.format(e=dberror)) raise errors.InvalidInputFormat(dberror) return f
def db_update_field(store, field_id, field_dict, language): field = models.Field.get(store, field_id) if not field: raise errors.FieldIdNotFound # To be uncommented upon completion of fields implementaion # if not field.editable: # raise errors.FieldNotEditable _, template, step, fieldgroup = field_integrity_check(store, field_dict) try: # make not possible to change field type field_dict['type'] = field.type if field_dict['instance'] != 'reference': fill_localized_keys(field_dict, models.Field.localized_keys, language) # children handling: # - old children are cleared # - new provided childrens are evaluated and added children = field_dict['children'] if len(children) and field.type != 'fieldgroup': raise errors.InvalidInputFormat( "children can be associated only to fields of type fieldgroup" ) ancestors = set(fieldtree_ancestors(store, field.id)) field.children.clear() for child in children: if child['id'] == field.id or child['id'] in ancestors: raise errors.FieldIdNotFound c = db_update_field(store, child['id'], child, language) # remove current step/field fieldgroup/field association disassociate_field(c) field.children.add(c) db_update_fieldattrs(store, field.id, field_dict['attrs'], language) db_update_fieldoptions(store, field.id, field_dict['options'], language) # full update field.update(field_dict) else: # partial update partial_update = { 'x': field_dict['x'], 'y': field_dict['y'], 'width': field_dict['width'], 'multi_entry': field_dict['multi_entry'] } field.update(partial_update) # remove current step/field fieldgroup/field association disassociate_field(field) associate_field(store, field, template, step, fieldgroup) except Exception as dberror: log.err('Unable to update field: {e}'.format(e=dberror)) raise errors.InvalidInputFormat(dberror) return field
def db_create_field(store, field_dict, language): """ Create and add a new field to the store, then return the new serialized object. :param store: the store on which perform queries. :param field_dict: the field definition dict :param language: the language of the field definition dict :return: a serialization of the object """ fill_localized_keys(field_dict, models.Field.localized_keys, language) field = models.Field(field_dict) if field_dict['template_id'] != '': field.template_id = field_dict['template_id'] if field_dict['step_id'] != '': field.step_id = field_dict['step_id'] if field_dict['fieldgroup_id'] != '': ancestors = set(fieldtree_ancestors(store, field_dict['fieldgroup_id'])) if field.id == field_dict['fieldgroup_id'] or field.id in ancestors: raise errors.InvalidInputFormat( "Provided field association would cause recursion loop") field.fieldgroup_id = field_dict['fieldgroup_id'] store.add(field) if field.template: # special handling of the whistleblower_identity field if field.template.key == 'whistleblower_identity': if field.step: if store.find( models.Field, models.Field.key == u'whistleblower_identity', models.Field.step_id == models.Step.id, models.Step.questionnaire_id == models.Questionnaire.id, models.Questionnaire.id == field.step.questionnaire_id).count() == 0: field.step.questionnaire.enable_whistleblower_identity = True else: raise errors.InvalidInputFormat( "Whistleblower identity field already present") else: raise errors.InvalidInputFormat( "Cannot associate whistleblower identity field to a fieldgroup" ) else: db_update_fieldattrs(store, field.id, field_dict['attrs'], language) db_update_fieldoptions(store, field.id, field_dict['options'], language) if field.instance != 'reference': for c in field_dict['children']: c['fieldgroup_id'] = field.id db_create_field(store, c, language) return field
def db_create_context(store, request, language): """ Creates a new context from the request of a client. We associate to the context the list of receivers and if the receiver is not valid we raise a ReceiverIdNotFound exception. Args: (dict) the request containing the keys to set on the model. Returns: (dict) representing the configured context """ receivers = request.get('receivers', []) steps = request.get('steps', []) fill_localized_keys(request, models.Context.localized_strings, language) context = models.Context(request) # Integrity checks related on name (need to exists, need to be unique) # are performed only using the default language at the moment (XXX) try: context_name = request['name'][language] except Exception as excep: raise errors.InvalidInputFormat("language %s do not provide name: %s" % (language, excep) ) if len(context_name) < 1: log.err("Invalid request: name is an empty string") raise errors.InvalidInputFormat("Context name is missing (1 char required)") if request['select_all_receivers']: if request['maximum_selectable_receivers']: log.debug("Resetting maximum_selectable_receivers (%d) because 'select_all_receivers' is True" % request['maximum_selectable_receivers']) request['maximum_selectable_receivers'] = 0 # tip_timetolive and submission_timetolive need to be converted in seconds since hours and days (context.submission_timetolive, context.tip_timetolive) = acquire_context_timetolive(request) c = store.add(context) for receiver_id in receivers: receiver = models.Receiver.get(store, receiver_id) if not receiver: log.err("Creation error: unexistent context can't be associated") raise errors.ReceiverIdNotFound c.receivers.add(receiver) if steps: db_create_step(store, context.id, steps, language) else: appdata = store.find(models.ApplicationData).one() steps = copy.deepcopy(appdata.fields) n_s = 1 for step in steps: for f_child in step['children']: if not field_is_present(store, f_child): f_child['is_template'] = False for step in steps: f_children = copy.deepcopy(step['children']) del step['children'] s = models.db_forge_obj(store, models.Step, step) for f_child in f_children: o_children = copy.deepcopy(f_child['options']) del f_child['options'] # FIXME currently default updata do not handle fieldgroups # all this block must be redesigned in order to be called recursively del f_child['children'] f = models.db_forge_obj(store, models.Field, f_child) n_o = 1 for o_child in o_children: o = models.db_forge_obj(store, models.FieldOption, o_child) o.field_id = f.id o.number = n_o f.options.add(o) n_o += 1 f.step_id = s.id s.children.add(f) s.context_id = context.id s.number = n_s context.steps.add(s) n_s += 1 log.debug("Created context %s (using %s)" % (context_name, language) ) return admin_serialize_context(store, context, language)
def db_update_steps(store, context_id, steps, language): """ Update steps :param store: the store on which perform queries. :param context_id: the id of the context on which register specified steps. :param steps: a dictionary containing the steps to be updated. :param language: the language of the specified steps. """ context = models.Context.get(store, context_id) if context is None: raise errors.ContextIdNotFound old_steps = store.find(models.Step, models.Step.context_id == context_id) indexed_old_steps = {} for o in old_steps: indexed_old_steps[o.id] = o new_steps = [] n = 1 for step in steps: step['context_id'] = context_id step['number'] = n fill_localized_keys(step, models.Step.localized_strings, language) # check for reuse (needed to keep translations) if 'id' in step and step['id'] in indexed_old_steps: s = indexed_old_steps[step['id']] for field in s.children: s.children.remove(field) s.update(step) new_steps.append(indexed_old_steps[step['id']]) del indexed_old_steps[step['id']] else: new_steps.append(models.Step(step)) i = 1 for f in step['children']: field = models.Field.get(store, f['id']) i += 1 field.y = i if not field: log.err("Creation error: unexistent field can't be associated") raise errors.FieldIdNotFound # remove current step/field fieldgroup/field association a_s, _ = get_field_association(store, field.id) if a_s is None: s.children.add(field) elif a_s != s.id: disassociate_field(store, field.id) s.children.add(field) else: # the else condition means a_s == s.id; already associated! pass n += 1 for o_id in indexed_old_steps: store.remove(indexed_old_steps[o_id]) for n in new_steps: store.add(n)