def rest_create(request, callback=None): """Create a new item of type clazz. The item will be initialised with the data provided in the submitted POST request. The submitted data will be validated before the item is actually saved. If the submission fails the item is not saved in the database. In all cases the item is returned as JSON object with the item and updated values back to the client. The JSON Response will include further details on the reason why the validation failed. :clazz: Class of item to create :request: Current request :returns: JSON object. """ clazz = request.context.__model__ # Create a new item. factory = clazz.get_item_factory() item = factory.create(request.user) # Initialise the create form for the item to be able to validate the # submitted data. form = Form(get_form_config(item, 'create'), item, request.db, translate=request.translate, csrf_token=request.session.get_csrf_token()) if form.validate(request.params): sitem = form.save() return JSONResponse(True, sitem) else: # Validation fails! return item return JSONResponse(False, sitem)
def test_get_form_config(self): from ringo.model.modul import ModulItem from ringo.lib.form import get_form_config factory = ModulItem.get_item_factory() item = factory.load(1) result = get_form_config(item, 'create') self.assertEqual(result.id, 'create')
def get_blobform_config(request, item, formname): """Helper function used in the create_ method to setup the create forms for blogform items. To create a new blogform item the creation is done in three steps: 1. Stage 1: The user selects a form from a list 2. Stage 2: The create dialog is rendered with the selected form 3. Stage 3: The item is validated and saved. :request: current request :item: item to build the form :formname: name of the form in the formconfig :returns: formconfig, item used to build a form. """ # First check if the fid parameter is provided fid = request.params.get('fid') or item.fid blobform = request.params.get('blobforms') if fid: log.debug("Stage 3: User has submitted data to create a new item") setattr(item, 'fid', fid) formfactory = BlobformForm.get_item_factory() formconfig = Config(parse(formfactory.load(fid).definition)) return item, formconfig.get_form(formname) elif blobform: log.debug("Stage 2: User has selected a blobform %s " % blobform) setattr(item, 'fid', blobform) formfactory = BlobformForm.get_item_factory() formconfig = Config(parse(formfactory.load(blobform).definition)) return item, formconfig.get_form(formname) else: log.debug("Stage 1: User is selecting a blobform") modul = get_item_modul(request, item) formconfig = get_form_config(modul, "blobform") return modul, formconfig
def anonymize_values(self, values, form="create"): """Will return a anonymized version of the values dictionary :values: Dictionary with values of the item. :returns: Dictionary with anonymized values of the item. """ formconfig = get_form_config(self, form) anon_values = {} fields = formconfig.get_fields() for key in fields: value = values.get(key) if not value: continue field = fields[key] if "anonymize" not in field.tags: anon_values[key] = values[key] continue # Special handling of datatypes: if field.type == "date": anon_values[key] = None else: anon_values[key] = None # Finally remove some global values for key in ["uuid"]: anon_values[key] = None return anon_values
def removeaccount(request): """Method to remove the useraccout by the user.""" # Check authentification # The view is only available for authenticated users and callable # if the user is not the admin unser (id=1) id = request.matchdict.get('id') if not request.user or id == '1': raise HTTPUnauthorized clazz = User _ = request.translate # Load the item return 400 if the item can not be found. factory = clazz.get_item_factory() try: item = factory.load(id, request.db) # Check authorisation if item.id != request.user.id: raise HTTPForbidden() except sa.orm.exc.NoResultFound: raise HTTPBadRequest() form = Form(get_form_config(item, 'removeaccount'), item, request.db, translate=_, renderers={}, change_page_callback={ 'url': 'set_current_form_page', 'item': clazz.__tablename__, 'itemid': id }, request=request, csrf_token=request.session.get_csrf_token(), dependencies=create_dependencies(request)) if request.POST: mapping = {'item': item} if form.validate(request.params): # Delete the account and redirect the user to a result page request.db.delete(item) headers = forget(request) target_url = request.route_path('users-accountremoved') return HTTPFound(location=target_url, headers=headers) else: msg = _('Deleting the account of ' '"${item}" failed.', mapping=mapping) log.info(msg) request.session.flash(msg, 'error') rvalue = {} rvalue['clazz'] = clazz rvalue['item'] = item rvalue['form'] = form.render(page=get_current_form_page(clazz, request)) return rvalue
def get_item_form(name, request, renderers=None, validators=None, values=None): """Will return a form for the given item :name: Name of the form :request: Current request :renderers: Dictionary of external renderers which should be used for renderering some form elements. :validators: List of external formbar validators which should be added to the form for validation :values: Dictionary with external values to prefill the form or add addional values for rule evaluation. :returns: Form """ if renderers is None: renderers = {} if validators is None: validators = [] if values is None: values = {} item = get_item_from_request(request) renderers = add_renderers(renderers) clazz = request.context.__model__ name = request.session.get("%s.form" % clazz) or name # handle blobforms if isinstance(item, Blobform): # TODO: Why not use the get_form_config method here. This can # handle Blobforms and usual form configs. () <2014-08-26 22:21> item, formconfig = get_blobform_config(request, item, name) else: formconfig = get_form_config(item, name) form = Form(formconfig, item, request.db, translate=request.translate, renderers=renderers, change_page_callback={ 'url': 'set_current_form_page', 'item': clazz.__tablename__, 'itemid': item.id }, request=request, csrf_token=request.session.get_csrf_token(), eval_url=get_eval_url(), url_prefix=get_app_url(request), locale=locale_negotiator(request), values=values, timezone=get_timezone(request), dependencies=create_dependencies(request)) # Add validators for validator in validators: form.add_validator(validator) return form
def removeaccount(request): """Method to remove the useraccout by the user.""" # Check authentification # The view is only available for authenticated users and callable # if the user is not the admin unser (id=1) id = request.matchdict.get('id') if not request.user or id == '1': raise HTTPUnauthorized clazz = User _ = request.translate handle_history(request) handle_params(request) # Load the item return 400 if the item can not be found. factory = clazz.get_item_factory() try: item = factory.load(id, request.db) # Check authorisation if item.id != request.user.id: raise HTTPForbidden() except sa.orm.exc.NoResultFound: raise HTTPBadRequest() form = Form(get_form_config(item, 'removeaccount'), item, request.db, translate=_, renderers={}, change_page_callback={'url': 'set_current_form_page', 'item': clazz.__tablename__, 'itemid': id}, request=request, csrf_token=request.session.get_csrf_token()) if request.POST: mapping = {'item': item} if form.validate(request.params): # Delete the account and redirect the user to a result page request.db.delete(item) headers = forget(request) target_url = request.route_path('users-accountremoved') return HTTPFound(location=target_url, headers=headers) else: msg = _('Deleting the account of ' '"${item}" failed.', mapping=mapping) log.info(msg) request.session.flash(msg, 'error') rvalue = {} rvalue['clazz'] = clazz rvalue['item'] = item rvalue['form'] = form.render(page=get_current_form_page(clazz, request)) return rvalue
def get_item_form(name, request, renderers=None, validators=None, values=None): """Will return a form for the given item :name: Name of the form :request: Current request :renderers: Dictionary of external renderers which should be used for renderering some form elements. :validators: List of external formbar validators which should be added to the form for validation :values: Dictionary with external values to prefill the form or add addional values for rule evaluation. :returns: Form """ if renderers is None: renderers = {} if validators is None: validators = [] if values is None: values = {} item = get_item_from_request(request) renderers = add_renderers(renderers) clazz = request.context.__model__ name = request.session.get("%s.form" % clazz) or name ## handle blobforms if isinstance(item, Blobform): # TODO: Why not use the get_form_config method here. This can # handle Blobforms and usual form configs. () <2014-08-26 22:21> item, formconfig = get_blobform_config(request, item, name) else: formconfig = get_form_config(item, name) form = Form(formconfig, item, request.db, translate=request.translate, renderers=renderers, change_page_callback={'url': 'set_current_form_page', 'item': clazz.__tablename__, 'itemid': item.id}, request=request, csrf_token=request.session.get_csrf_token(), eval_url=get_eval_url(), url_prefix=get_app_url(request), locale=locale_negotiator(request), values=values) # Add validators for validator in validators: form.add_validator(validator) return form
def changepassword(request): """Method to change the users password by the user. The user user musst provide his old and the new pasword. Users are only allowed to change their own password.""" # Check authentification # As this view has now security configured it is # generally callable by all users. For this reason we first check if # the user is authenticated. If the user is not authenticated the # raise an 401 (unauthorized) exception. if not request.user: raise HTTPUnauthorized clazz = User _ = request.translate rvalue = {} # Load the item return 400 if the item can not be found. id = request.matchdict.get('id') factory = clazz.get_item_factory() try: item = factory.load(id, request.db) # Check authorisation # User are only allowed to set their own password. if item.id != request.user.id: raise HTTPForbidden() except sa.orm.exc.NoResultFound: raise HTTPBadRequest() form = Form(get_form_config(item, 'changepassword'), item, request.db, translate=_, renderers={}, change_page_callback={'url': 'set_current_form_page', 'item': clazz.__tablename__, 'itemid': id}, request=request, csrf_token=request.session.get_csrf_token()) if request.POST: mapping = {'item': item} # Do extra validation which is not handled by formbar. # Is the provided old password correct? validator = Validator('oldpassword', _('The given password is not correct'), check_password) pw_len_validator = Validator('password', _('Password must be at least 12 ' 'characters long.'), password_minlength_validator) pw_nonchar_validator = Validator('password', _('Password must contain at least 2 ' 'non-letters.'), password_nonletter_validator) form.add_validator(validator) form.add_validator(pw_len_validator) form.add_validator(pw_nonchar_validator) if form.validate(request.params): form.save() # Actually save the password. This is not done in the form # as the password needs to be encrypted. encrypt_password_callback(request, item) msg = _('Changed password for "${item}" successfull.', mapping=mapping) log.info(msg) request.session.flash(msg, 'success') route_name = get_action_routename(item, 'changepassword') url = request.route_path(route_name, id=item.id) # Invalidate cache invalidate_cache() return HTTPFound(location=url) else: msg = _('Error on changing the password for ' '"${item}".', mapping=mapping) log.info(msg) request.session.flash(msg, 'error') rvalue['clazz'] = clazz rvalue['item'] = item rvalue['form'] = form.render(page=get_current_form_page(clazz, request)) return rvalue
def get_value(self, name, form_id="read", expand=False): """Return the value of the given attribute of the item. Unlike accessing the value directly this function this function optionally supports the expansion of the value before returning it. On default no expansion is done. Expansion is relevant for values which are saved as integer values in the database but have another literal meaning. This is typically true for values in selection lists. E.g the literal values for 'Yes' and 'No' are saves as value '1' and '0' in the database. To expand the value the method will try to get the literal value from the value in the database by looking in the form identified by the `form_id` attribute. :name: Name of the attribute with the value :form_id: ID of the form which will be used for expansion :expand: Expand the value before returning it :returns: Value of the named attribute """ try: raw_value = getattr(self, name) except: # This error is only acceptable for blobforms as attributes # can be added and removed by the user on the fly. So there # is a good chance that older items do not have this attribute. if hasattr(self, 'id'): log.error("Attribute '%s' not found in '%s'; id:%s" % (name, repr(self), self.id)) else: log.error("Attribute '%s' not found in '%s'" % (name, repr(self))) raw_value = None if expand: # In case the fieldname is dotted and refers to values in # related items then we need some special logic. elements = name.split(".") if len(elements) == 2: obj = getattr(self, elements[0]) name = elements[1] elif len(elements) > 2: obj = getattr(self, ".".join(elements[0:-1])) name = elements[-1] else: obj = self # Expanding the value means to get the "literal" value for the # given value from the form. form_config = get_form_config(obj, form_id) try: field_config = form_config.get_field(name) options = [] for option in field_config.options: # Handle "list" values "{"",1,2}" if str(raw_value).startswith("{") and str(raw_value).endswith("}"): for value in raw_value.strip("}").strip("{").split(","): if str(value) == str(option[1]): options.append(option[0]) elif str(raw_value) == str(option[1]): options.append(option[0]) break # If we can not match a value we return the raw value. # This can also happen if the user tries to expand value # which do not have options. if len(options) > 0: return ", ".join(options) return raw_value except KeyError: # If the field/value which should to be expanded is not # included in the form the form library will raise a # KeyError exception. However this is not a big deal as # we still have the raw value and only the expandation # fails. So silently ignore this one. The exception is # already logged in the form library. pass return raw_value
def get_value(self, name, form_id="read", expand=False, strict=True): """Return the value of the given attribute of the item. Unlike accessing the value directly this function this function optionally supports the expansion of the value before returning it. On default no expansion is done. Expansion is relevant for values which are saved as integer values in the database but have another literal meaning. This is typically true for values in selection lists. E.g the literal values for 'Yes' and 'No' are saves as value '1' and '0' in the database. To expand the value the method will try to get the literal value from the value in the database by looking in the form identified by the `form_id` attribute. Strict mode means that in case the given attribute `name` can not be accesses a error log message will be triggered. However there are some cases where the attribute can not be accesses for some known reasons and therefor the error should not be logged but passed silently. As this is highly situation depended you can call this method with `stric=False` to prevent logging. Here are two examples where you migh want to disable logging: * Overviews. In case you want to display related items like 'country.code' but the item does not have a related country (yet). * For blobforms as attributes can be added and removed by the user on the fly. So there is a good chance that older items do not have this attribute. :name: Name of the attribute with the value :form_id: ID of the form which will be used for expansion :expand: Expand the value before returning it :strict: Log error if the value can not be accessed. Defaults to True. :returns: Value of the named attribute """ try: raw_value = getattr(self, name) except AttributeError: if not strict: pass elif hasattr(self, 'id'): log.error("Attribute '%s' not found in '%s'; id:%s" % (name, repr(self), self.id)) else: log.error("Attribute '%s' not found in '%s'" % (name, repr(self))) raw_value = None if expand: # In case the fieldname is dotted and refers to values in # related items then we need some special logic. elements = name.split(".") if len(elements) == 2: obj = getattr(self, elements[0]) name = elements[1] elif len(elements) > 2: obj = getattr(self, ".".join(elements[0:-1])) name = elements[-1] else: obj = self # Expanding the value means to get the "literal" value for the # given value from the form. form_config = get_form_config(obj, form_id) try: field_config = form_config.get_field(name) options = [] for option in field_config.options: # Handle "list" values "{"",1,2}" if (str(raw_value).startswith("{") and str(raw_value).endswith("}")): for value in ( raw_value.strip("}").strip("{").split(",")): if str(value) == str(option[1]): options.append(option[0]) elif str(raw_value) == str(option[1]): options.append(option[0]) break # If we can not match a value we return the raw value. # This can also happen if the user tries to expand value # which do not have options. if len(options) > 0: return ", ".join(options) return raw_value except KeyError: # If the field/value which should to be expanded is not # included in the form the form library will raise a # KeyError exception. However this is not a big deal as # we still have the raw value and only the expandation # fails. So silently ignore this one. The exception is # already logged in the form library. pass return raw_value
def changepassword(request): """Method to change the users password by the user. The user user musst provide his old and the new pasword. Users are only allowed to change their own password.""" # Check authentification # As this view has now security configured it is # generally callable by all users. For this reason we first check if # the user is authenticated. If the user is not authenticated the # raise an 401 (unauthorized) exception. if not request.user: raise HTTPUnauthorized clazz = User handle_history(request) handle_params(request) _ = request.translate rvalue = {} # Load the item return 400 if the item can not be found. id = request.matchdict.get('id') factory = clazz.get_item_factory() try: item = factory.load(id, request.db) # Check authorisation # User are only allowed to set their own password. if item.id != request.user.id: raise HTTPForbidden() except sa.orm.exc.NoResultFound: raise HTTPBadRequest() form = Form(get_form_config(item, 'changepassword'), item, request.db, translate=_, renderers={}, change_page_callback={'url': 'set_current_form_page', 'item': clazz.__tablename__, 'itemid': id}, request=request, csrf_token=request.session.get_csrf_token()) if request.POST: mapping = {'item': item} # Do extra validation which is not handled by formbar. # Is the provided old password correct? validator = Validator('oldpassword', _('The given password is not correct'), check_password) pw_len_validator = Validator('password', _('Password must be at least 12 ' 'characters long.'), password_minlength_validator) pw_nonchar_validator = Validator('password', _('Password must contain at least 2 ' 'non-letters.'), password_nonletter_validator) form.add_validator(validator) form.add_validator(pw_len_validator) form.add_validator(pw_nonchar_validator) if form.validate(request.params): form.save() # Actually save the password. This is not done in the form # as the password needs to be encrypted. encrypt_password_callback(request, item) msg = _('Changed password for "${item}" successfull.', mapping=mapping) log.info(msg) request.session.flash(msg, 'success') route_name = get_action_routename(item, 'changepassword') url = request.route_path(route_name, id=item.id) # Invalidate cache invalidate_cache() return HTTPFound(location=url) else: msg = _('Error on changing the password for ' '"${item}".', mapping=mapping) log.info(msg) request.session.flash(msg, 'error') rvalue['clazz'] = clazz rvalue['item'] = item rvalue['form'] = form.render(page=get_current_form_page(clazz, request)) return rvalue