def reply(self): data = json_body(self.request) type_ = data.get('@type', 'SchemaFormData') id_ = data.get('id', None) title = data.get( 'title', 'Data at %s' % datetime.datetime.now().strftime('%Y-%m-%d %H%M%S')) # import pdb;pdb.set_trace() schema_form_data = data.get('schema_form_data', {}) if not type_: raise BadRequest("Property '@type' is required") # Disable CSRF protection if 'IDisableCSRFProtection' in dir(plone.protect.interfaces): alsoProvides(self.request, plone.protect.interfaces.IDisableCSRFProtection) obj = create(self.context, type_, id_=id_, title=title) if isinstance(obj, dict) and 'error' in obj: self.request.response.setStatus(400) return obj obj.schema_form_data = schema_form_data # Update fields # deserializer = queryMultiAdapter((obj, self.request), # IDeserializeFromJson) # if deserializer is None: # self.request.response.setStatus(501) # return dict(error=dict( # message='Cannot deserialize type {}'.format(obj.portal_type))) # try: # deserializer(validate_all=True) # except DeserializationError as e: # self.request.response.setStatus(400) # return dict(error=dict( # type='DeserializationError', # message=str(e))) # Rename if generated id if not id_: rename(obj) self.request.response.setStatus(201) self.request.response.setHeader('Location', obj.absolute_url()) # serializer = queryMultiAdapter( # (obj, self.request), # ISerializeToJson # ) # serialized_obj = serializer() # HypermediaBatch can't determine the correct canonical URL for # objects that have just been created via POST - so we make sure # to set it here # serialized_obj['@id'] = obj.absolute_url() success = {'message': 'successfully added data'} return success
def MOVE(self, REQUEST, RESPONSE): """Move a resource to a new location. Though we may later try to make a move appear seamless across namespaces (e.g. from Zope to Apache), MOVE is currently only supported within the Zope namespace.""" self.dav__init(REQUEST, RESPONSE) self.dav__validate(self, 'DELETE', REQUEST) if not hasattr(aq_base(self), 'cb_isMoveable') or \ not self.cb_isMoveable(): raise MethodNotAllowed('This object may not be moved.') dest = REQUEST.get_header('Destination', '') try: path = REQUEST.physicalPathFromURL(dest) except ValueError: raise BadRequest('No destination given') flag = REQUEST.get_header('Overwrite', 'F') flag = flag.upper() name = path.pop() parent_path = '/'.join(path) try: parent = self.restrictedTraverse(path) except ValueError: raise Conflict('Attempt to move to an unknown namespace.') except 'Not Found': raise Conflict('The resource %s must exist.' % parent_path) except Exception: raise if hasattr(parent, '__null_resource__'): raise Conflict('The resource %s must exist.' % parent_path) existing = hasattr(aq_base(parent), name) if existing and flag == 'F': raise PreconditionFailed('Resource %s exists.' % dest) try: parent._checkId(name, allow_dup=1) except Exception: raise Forbidden(sys.exc_info()[1]) try: parent._verifyObjectPaste(self) except Unauthorized: raise except Exception: raise Forbidden(sys.exc_info()[1]) # Now check locks. Since we're affecting the resource that we're # moving as well as the destination, we have to check both. ifhdr = REQUEST.get_header('If', '') if existing: # The destination itself exists, so we need to check its locks destob = aq_base(parent)._getOb(name) if IWriteLock.providedBy(destob) and destob.wl_isLocked(): if ifhdr: itrue = destob.dav__simpleifhandler( REQUEST, RESPONSE, 'MOVE', url=dest, refresh=1) if not itrue: raise PreconditionFailed else: raise Locked('Destination is locked.') elif IWriteLock.providedBy(parent) and parent.wl_isLocked(): # There's no existing object in the destination folder, so # we need to check the folders locks since we're changing its # member list if ifhdr: itrue = parent.dav__simpleifhandler(REQUEST, RESPONSE, 'MOVE', col=1, url=dest, refresh=1) if not itrue: raise PreconditionFailed('Condition failed.') else: raise Locked('Destination is locked.') if wl_isLocked(self): # Lastly, we check ourselves if ifhdr: itrue = self.dav__simpleifhandler(REQUEST, RESPONSE, 'MOVE', refresh=1) if not itrue: raise PreconditionFailed('Condition failed.') else: raise Locked('Source is locked and no condition was passed in') orig_container = aq_parent(aq_inner(self)) orig_id = self.getId() self._notifyOfCopyTo(parent, op=1) notify(ObjectWillBeMovedEvent(self, orig_container, orig_id, parent, name)) # try to make ownership explicit so that it gets carried # along to the new location if needed. self.manage_changeOwnershipType(explicit=1) ob = self._getCopy(parent) ob._setId(name) orig_container._delObject(orig_id, suppress_events=True) if existing: object = getattr(parent, name) self.dav__validate(object, 'DELETE', REQUEST) parent._delObject(name) parent._setObject(name, ob, set_owner=0, suppress_events=True) ob = parent._getOb(name) notify(ObjectMovedEvent(ob, orig_container, orig_id, parent, name)) notifyContainerModified(orig_container) if aq_base(orig_container) is not aq_base(parent): notifyContainerModified(parent) ob._postCopy(parent, op=1) # try to make ownership implicit if possible ob.manage_changeOwnershipType(explicit=0) RESPONSE.setStatus(existing and 204 or 201) if not existing: RESPONSE.setHeader('Location', dest) RESPONSE.setBody('') return RESPONSE
def update(self, context, request): """/@@API/update: Update existing object values Required parameters: - obj_path: path to the object, relative to plone site root. - fields: json value, dict: key:value = fieldname:value. { runtime: Function running time. error: true or string(message) if error. false if no error. success: true or string(message) if success. false if no success. <fieldname>: <current value> ... } So. >>> portal = layer['portal'] >>> portal_url = portal.absolute_url() >>> from plone.app.testing import SITE_OWNER_NAME >>> from plone.app.testing import SITE_OWNER_PASSWORD Update a client's existing address: >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD) >>> browser.open(portal_url+"/@@API/update?", "&".join([ ... "obj_path=/clients/client-1", ... "title=Test", ... "PostalAddress={'address': '1 Wendy Way', 'city': 'Johannesburg', 'zip': '9000', 'state': 'Gauteng', 'district': '', 'country':'South Africa'}" ... ])) >>> browser.contents '{..."success": true...}' quickly check that it saved: >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD) >>> browser.open(portal_url+"/@@API/read?", "&".join([ ... "id=client-1", ... "include_fields=PostalAddress", ... ])) >>> browser.contents '{...1 Wendy Way...}' Try the same with a nonsense fieldname: >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD) >>> browser.open(portal_url+"/@@API/update?", "&".join([ ... "obj_path=/clients/client-1", ... "Thing=Fish", ... ])) >>> browser.contents '{...The following request fields were not used: ...Thing...}' Setting the value of a RefereceField to "" or None (null) should not cause an error; setting an empty value should clear the field >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD) >>> browser.open(portal_url+"/@@API/update?", "&".join([ ... "obj_path=/clients/client-1", ... 'DefaultCategories=', ... ])) >>> browser.contents '{..."success": true...}' """ savepoint = transaction.savepoint() self.context = context self.request = request self.unused = [x for x in self.request.form.keys()] self.used("form.submitted") self.used("__ac_name") self.used("__ac_password") ret = { "url": router.url_for("update", force_external=True), "success": False, "error": True, } # always require obj_path self.require("obj_path") obj_path = self.request['obj_path'] self.used("obj_path") site_path = request['PATH_INFO'].replace("/@@API/update", "") obj = context.restrictedTraverse(str(site_path + obj_path)) try: fields = set_fields_from_request(obj, request) for field in fields: self.used(field) except: savepoint.rollback() raise ret['success'] = True ret['error'] = False if self.unused: raise BadRequest( "The following request fields were not used: %s. Request aborted." % self.unused) return ret
def checkValidId(self, id, allow_dup=0): # If allow_dup is false, an error will be raised if an object # with the given id already exists. If allow_dup is true, # only check that the id string contains no illegal chars; # check_valid_id() will be called again later with allow_dup # set to false before the object is added. if not id or not isinstance(id, str): if isinstance(id, text_type): id = escape(id, True) raise BadRequest('Empty or invalid id specified', id) if bad_id(id) is not None: raise BadRequest( ('The id "%s" contains characters ' 'illegal in URLs.' % escape(id, True))) if id in ('.', '..'): raise BadRequest( 'The id "%s" is invalid because it is not traversable.' % id) if id.startswith('_'): raise BadRequest( 'The id "%s" is invalid because it ' 'begins with an underscore.' % id) if id.startswith('aq_'): raise BadRequest( 'The id "%s" is invalid because it begins with "aq_".' % id) if id.endswith('__'): raise BadRequest( 'The id "%s" is invalid because it ' 'ends with two underscores.' % id) if id.startswith('@@') or id.startswith('++'): raise BadRequest( 'The id "%s" is invalid because it starts with characters ' 'reserved for Zope views lookup.' % id) if not allow_dup: obj = getattr(self, id, None) if obj is not None: # An object by the given id exists either in this # ObjectManager or in the acquisition path. flags = getattr(obj, '__replaceable__', NOT_REPLACEABLE) if hasattr(aq_base(self), id): # The object is located in this ObjectManager. if not flags & REPLACEABLE: raise BadRequest( 'The id "%s" is invalid - it is already in use.' % id) # else the object is replaceable even if the UNIQUE # flag is set. elif flags & UNIQUE: raise BadRequest('The id "%s" is reserved.' % id) if id == 'REQUEST': raise BadRequest('REQUEST is a reserved name.') if '/' in id: raise BadRequest( 'The id "%s" contains characters illegal in URLs.' % id)
def reply(self): data = json_body(self.request) type_ = data.get("@type", None) id_ = data.get("id", None) title = data.get("title", None) translation_of = data.get("translation_of", None) language = data.get("language", None) if not type_: raise BadRequest("Property '@type' is required") # Disable CSRF protection if "IDisableCSRFProtection" in dir(plone.protect.interfaces): alsoProvides(self.request, plone.protect.interfaces.IDisableCSRFProtection) try: obj = create(self.context, type_, id_=id_, title=title) except Unauthorized as exc: self.request.response.setStatus(403) return dict(error=dict(type="Forbidden", message=str(exc))) except BadRequest as exc: self.request.response.setStatus(400) return dict(error=dict(type="Bad Request", message=str(exc))) # Acquisition wrap temporarily to satisfy things like vocabularies # depending on tools temporarily_wrapped = False if IAcquirer.providedBy(obj) and not safe_hasattr(obj, "aq_base"): obj = obj.__of__(self.context) temporarily_wrapped = True # Update fields deserializer = queryMultiAdapter((obj, self.request), IDeserializeFromJson) if deserializer is None: self.request.response.setStatus(501) return dict(error=dict( message="Cannot deserialize type {}".format(obj.portal_type))) try: deserializer(validate_all=True, create=True) except DeserializationError as e: self.request.response.setStatus(400) return dict( error=dict(type="DeserializationError", message=str(e))) if temporarily_wrapped: obj = aq_base(obj) if not getattr(deserializer, "notifies_create", False): notify(ObjectCreatedEvent(obj)) obj = add(self.context, obj, rename=not bool(id_)) # Link translation given the translation_of property if PAM_INSTALLED: from plone.app.multilingual.interfaces import ( IPloneAppMultilingualInstalled, ) # noqa from plone.app.multilingual.interfaces import ITranslationManager if (IPloneAppMultilingualInstalled.providedBy(self.request) and translation_of and language): source = self.get_object(translation_of) if source: manager = ITranslationManager(source) manager.register_translation(language, obj) self.request.response.setStatus(201) self.request.response.setHeader("Location", obj.absolute_url()) serializer = queryMultiAdapter((obj, self.request), ISerializeToJson) serialized_obj = serializer() # HypermediaBatch can't determine the correct canonical URL for # objects that have just been created via POST - so we make sure # to set it here serialized_obj["@id"] = obj.absolute_url() return serialized_obj
def _create_ar(self, context, request): """Creates AnalysisRequest object, with supporting Sample, Partition and Analysis objects. The client is retrieved from the obj_path key in the request. Required request parameters: - Contact: One client contact Fullname. The contact must exist in the specified client. The first Contact with the specified value in it's Fullname field will be used. - SampleType_<index> - Must be an existing sample type. Optional request parameters: - CCContacts: A list of contact Fullnames, which will be copied on all messages related to this AR and it's sample or results. - CCEmails: A list of email addresses to include as above. - Sample_id: Create a secondary AR with an existing sample. If unspecified, a new sample is created. - Specification: a lookup to set Analysis specs default values for all analyses - Analysis_Specification: specs (or overrides) per analysis, using a special lookup format. &Analysis_Specification:list=<Keyword>:min:max:error&... """ wftool = getToolByName(context, 'portal_workflow') bc = getToolByName(context, 'bika_catalog') bsc = getToolByName(context, 'bika_setup_catalog') pc = getToolByName(context, 'portal_catalog') ret = { "url": router.url_for("create", force_external=True), "success": True, "error": False, } SamplingWorkflowEnabled = context.bika_setup.getSamplingWorkflowEnabled( ) for field in [ 'Client', 'SampleType', 'Contact', 'SamplingDate', 'Services' ]: self.require(field) self.used(field) try: client = resolve_request_lookup(context, request, 'Client')[0].getObject() except IndexError: raise Exception("Client not found") secondary = False sample = None # Sample_id if 'Sample' in request: # Secondary AR try: sample = resolve_request_lookup(context, request, 'Sample')[0].getObject() except IndexError: raise Exception("Sample not found") secondary = True else: # Primary AR sample = _createObjectByType("Sample", client, tmpID()) sample.unmarkCreationFlag() fields = set_fields_from_request(sample, request) for field in fields: self.used(field) sample._renameAfterCreation() sample.setSampleID(sample.getId()) sample.setSamplingWorkflowEnabled(SamplingWorkflowEnabled) event.notify(ObjectInitializedEvent(sample)) sample.at_post_create_script() ret['sample_id'] = sample.getId() parts = [{ 'services': [], 'container': [], 'preservation': '', 'separate': False }] specs = self.get_specs_from_request() ar = _createObjectByType("AnalysisRequest", client, tmpID()) ar.unmarkCreationFlag() fields = set_fields_from_request(ar, request) for field in fields: self.used(field) ar.setSample(sample) ar._renameAfterCreation() ret['ar_id'] = ar.getId() brains = resolve_request_lookup(context, request, 'Services') service_uids = [p.UID for p in brains] # If there is a profile, add its services' UIDs brains = resolve_request_lookup(context, request, 'Profiles') profiles_uids = [p.UID for p in brains] profiles_uids = ','.join(profiles_uids) profiles_dict = {'Profiles': profiles_uids} service_uids = get_services_uids(context=context, analyses_serv=service_uids, values=profiles_dict) ar.setAnalyses(service_uids, specs=specs) new_analyses = ar.getAnalyses(full_objects=True) ar.reindexObject() event.notify(ObjectInitializedEvent(ar)) ar.at_post_create_script() # Create sample partitions parts_and_services = {} for _i in range(len(parts)): p = parts[_i] part_prefix = sample.getId() + "-P" if '%s%s' % (part_prefix, _i + 1) in sample.objectIds(): parts[_i]['object'] = sample['%s%s' % (part_prefix, _i + 1)] parts_and_services['%s%s' % (part_prefix, _i + 1)] = p['services'] part = parts[_i]['object'] else: part = _createObjectByType("SamplePartition", sample, tmpID()) parts[_i]['object'] = part container = None preservation = p['preservation'] parts[_i]['prepreserved'] = False part.edit( Container=container, Preservation=preservation, ) part.processForm() parts_and_services[part.id] = p['services'] # Add analyses to sample partitions # XXX jsonapi create AR: right now, all new analyses are linked to the first samplepartition if new_analyses: analyses = list(part.getAnalyses()) analyses.extend(new_analyses) for analysis in new_analyses: analysis.setSamplePartition(part) part.setAnalyses(analyses) action = 'no_sampling_workflow' if SamplingWorkflowEnabled: action = 'sampling_workflow' wftool.doActionFor(ar, action) if secondary: # If secondary AR, then we need to manually transition the AR (and its # children) to fit with the Sample Partition's current state sampleactions = getReviewHistoryActionsList(sample) doActionsFor(ar, sampleactions) else: # If Preservation is required for some partitions, # and the SamplingWorkflow is disabled, we need # to transition to to_be_preserved manually. if not SamplingWorkflowEnabled: to_be_preserved = [] sample_due = [] lowest_state = 'sample_due' for p in sample.objectValues('SamplePartition'): if p.getPreservation(): lowest_state = 'to_be_preserved' to_be_preserved.append(p) else: sample_due.append(p) for p in to_be_preserved: doActionFor(p, 'to_be_preserved') for p in sample_due: doActionFor(p, 'sample_due') doActionFor(sample, lowest_state) # Transition pre-preserved partitions for p in parts: if 'prepreserved' in p and p['prepreserved']: part = p['object'] state = workflow.getInfoFor(part, 'review_state') if state == 'to_be_preserved': doActionFor(part, 'preserve') if self.unused: raise BadRequest( "The following request fields were not used: %s. Request aborted." % self.unused) return ret
def __call__(self, validate_all=False, data=None, create=False): # noqa: ignore=C901 if data is None: data = json_body(self.request) modified = {} schema_data = {} errors = [] for schema in iterSchemata(self.context): write_permissions = mergedTaggedValueDict(schema, WRITE_PERMISSIONS_KEY) for name, field in getFields(schema).items(): field_data = schema_data.setdefault(schema, {}) if field.readonly: continue if name in data: dm = queryMultiAdapter((self.context, field), IDataManager) if not dm.canWrite(): continue if not self.check_permission(write_permissions.get(name)): continue # set the field to missing_value if we receive null if data[name] is None: if not field.required: dm.set(field.missing_value) else: errors.append({ "field": field.__name__, "message": ( "{} is a required field.".format( field.__name__), "Setting it to null is not allowed.", ), }) continue # Deserialize to field value deserializer = queryMultiAdapter( (field, self.context, self.request), IFieldDeserializer) if deserializer is None: continue try: value = deserializer(data[name]) except ValueError as e: errors.append({ "message": str(e), "field": name, "error": e }) except ValidationError as e: errors.append({ "message": e.doc(), "field": name, "error": e }) else: field_data[name] = value if value != dm.get(): dm.set(value) # Collect the names of the modified fields # Use prefixed name because z3c.form does so prefixed_name = schema.__name__ + "." + name modified.setdefault(schema, []).append(prefixed_name) elif validate_all: # Never validate the changeNote of p.a.versioningbehavior # The Versionable adapter always returns an empty string # which is the wrong type. Should be unicode and should be # fixed in p.a.versioningbehavior if name == "changeNote": continue dm = queryMultiAdapter((self.context, field), IDataManager) bound = field.bind(self.context) try: bound.validate(dm.get()) except ValidationError as e: errors.append({ "message": e.doc(), "field": name, "error": e }) # Validate schemata for schema, field_data in schema_data.items(): validator = queryMultiAdapter( (self.context, self.request, None, schema, None), IManagerValidator) for error in validator.validate(field_data): errors.append({"error": error, "message": str(error)}) if errors: raise BadRequest(errors) # We'll set the layout after the validation and and even if there # are no other changes. if "layout" in data: layout = data["layout"] self.context.setLayout(layout) # OrderingMixin self.handle_ordering(data) if modified and not create: descriptions = [] for interface, names in modified.items(): descriptions.append(Attributes(interface, *names)) notify(ObjectModifiedEvent(self.context, *descriptions)) return self.context
def f(): raise BadRequest("argh")
def lookup_object(dataself, data): oguid = Oguid.parse(data.get('oguid')) obj = oguid.resolve_object() if not obj: raise BadRequest('Invalid oguid, could not be resolved.') return obj
def badRequestError(self, v=''): """Raise an error indicating something wrong with the request""" raise BadRequest(v)
def reply(self): data = json_body(self.request) type_ = data.get('@type', None) id_ = data.get('id', None) title = data.get('title', None) if not type_: raise BadRequest("Property '@type' is required") # Disable CSRF protection if 'IDisableCSRFProtection' in dir(plone.protect.interfaces): alsoProvides(self.request, plone.protect.interfaces.IDisableCSRFProtection) # Generate a temporary id if the id is not given if not id_: now = DateTime() new_id = '{}.{}.{}{:04d}'.format(type_.lower().replace(' ', '_'), now.strftime('%Y-%m-%d'), str(now.millis())[7:], randint(0, 9999)) else: new_id = id_ # Create object try: new_id = self.context.invokeFactory(type_, new_id, title=title) except BadRequest as e: self.request.response.setStatus(400) return dict(error=dict(type='DeserializationError', message=str(e.message))) except ValueError as e: self.request.response.setStatus(400) return dict(error=dict(type='DeserializationError', message=str(e.message))) # Update fields obj = self.context[new_id] deserializer = queryMultiAdapter((obj, self.request), IDeserializeFromJson) if deserializer is None: self.request.response.setStatus(501) return dict(error=dict( message='Cannot deserialize type {}'.format(obj.portal_type))) try: deserializer(validate_all=True) except DeserializationError as e: self.request.response.setStatus(400) return dict( error=dict(type='DeserializationError', message=str(e))) # Rename if generated id if not id_: self.rename_object(obj) self.request.response.setStatus(201) self.request.response.setHeader('Location', obj.absolute_url()) serializer = queryMultiAdapter((obj, self.request), ISerializeToJson) serialized_obj = serializer() # HypermediaBatch can't determine the correct canonical URL for # objects that have just been created via POST - so we make sure # to set it here serialized_obj['@id'] = obj.absolute_url() return serialized_obj
def __call__(self): state = self.request.form.get('state') messages = IStatusMessage(self.request) translator = self.context.translate navigation_root = api.portal.get_navigation_root(context=self.context) if 'error' in self.request.form: messages = IStatusMessage(self.request) reason = self.request.form.get('error') # noinspection PyArgumentList messages.add(translator( _(u'message_%s_failed' % state, mapping={u'reason': reason}), ), type='error') self.request.response.redirect(navigation_root.absolute_url() + '/@@id4me') return if 'code' not in self.request.form: raise BadRequest('no code given') code = self.request.form.get('code') if state == 'login': user = self.auth_util.verify_user_login(code) if user: acl_users = getToolByName(self.context, 'acl_users') # noinspection PyProtectedMember acl_users.session._setupSession(user.getId(), self.request.response) # noinspection PyArgumentList messages.add(translator(_(u'message_login_successful')), type='info') self.request.response.redirect(navigation_root.absolute_url()) else: # noinspection PyArgumentList messages.add(translator(_(u'message_no_user_connected')), type='error') self.request.response.redirect(navigation_root.absolute_url() + '/@@id4me') elif state == 'register': if not api.portal.get_registry_record( name='plone.enable_self_reg'): raise Forbidden() user = self.auth_util.register_user(code) acl_users = getToolByName(self.context, 'acl_users') # noinspection PyProtectedMember acl_users.session._setupSession(user.getId(), self.request.response) # noinspection PyArgumentList messages.add(translator( _(u'message_account_created', mapping={u'user_id': user.getId()})), type='info') self.request.response.redirect(navigation_root.absolute_url()) elif state == 'connect': if api.user.is_anonymous(): raise Forbidden('No user logged in') user = api.user.get_current() self.auth_util.connect_user_login(user=user, code=code) # noinspection PyArgumentList messages.add( translator( _( u'message_login_connected', # ToDo: get Identity Agent information mapping={u'agent': 'NOT_FOUND'})), type='info') self.request.response.redirect(navigation_root.absolute_url()) else: # ToDo: handle Case raise BadRequest('no state given')
def reply(self): portal = getSite() data = json_body(self.request) groupname = data.get("groupname", None) if not groupname: raise BadRequest("Property 'groupname' is required") email = data.get("email", None) title = data.get("title", None) description = data.get("description", None) roles = data.get("roles", None) groups = data.get("groups", None) users = data.get("users", []) properties = { "title": title, "description": description, "email": email } gtool = getToolByName(self.context, "portal_groups") regtool = getToolByName(self.context, "portal_registration") if not regtool.isMemberIdAllowed(groupname): raise BadRequest("The group name you entered is not valid.") already_exists = gtool.getGroupById(groupname) if already_exists: raise BadRequest("The group name you entered already exists.") # Disable CSRF protection if "IDisableCSRFProtection" in dir(plone.protect.interfaces): alsoProvides(self.request, plone.protect.interfaces.IDisableCSRFProtection) success = gtool.addGroup( groupname, roles, groups, properties=properties, title=title, description=description, ) if not success: raise BadRequest( "Error occurred, could not add group {}.".format(groupname)) # Add members group = gtool.getGroupById(groupname) for userid in users: group.addMember(userid) self.request.response.setStatus(201) # Note: to please Zope 4.5.2+ we make sure the header is a string, # and not unicode on Python 2. if six.PY2 and not isinstance(groupname, str): groupname = groupname.encode("utf-8") self.request.response.setHeader( "Location", portal.absolute_url() + "/@groups/" + groupname) serializer = queryMultiAdapter((group, self.request), ISerializeToJson) return serializer()
def _initProperties(self, node): obj = self.context if node.hasAttribute('i18n:domain'): i18n_domain = str(node.getAttribute('i18n:domain')) obj._updateProperty('i18n_domain', i18n_domain) for child in node.childNodes: if child.nodeName != 'property': continue prop_id = str(child.getAttribute('name')) prop_map = obj.propdict().get(prop_id, None) if prop_map is None: if child.hasAttribute('type'): val = str(child.getAttribute('select_variable')) prop_type = str(child.getAttribute('type')) obj._setProperty(prop_id, val, prop_type) prop_map = obj.propdict().get(prop_id, None) else: raise ValueError("undefined property '%s'" % prop_id) if not 'w' in prop_map.get('mode', 'wd'): raise BadRequest('%s cannot be changed' % prop_id) elements = [] remove_elements = [] for sub in child.childNodes: if sub.nodeName == 'element': if len(sub.childNodes) > 0: value = sub.childNodes[0].nodeValue.strip() if isinstance(value, unicode): value = value.encode(self._encoding) if self._convertToBoolean( sub.getAttribute('remove') or 'False'): remove_elements.append(value) if value in elements: elements.remove(value) else: elements.append(value) if value in remove_elements: remove_elements.remove(value) if elements or prop_map.get('type') == 'multiple selection': prop_value = tuple(elements) or () elif prop_map.get('type') == 'boolean': prop_value = self._convertToBoolean(self._getNodeText(child)) else: # if we pass a *string* to _updateProperty, all other values # are converted to the right type prop_value = self._getNodeText(child).encode(self._encoding) if not self._convertToBoolean( child.getAttribute('purge') or 'True'): # If the purge attribute is False, merge sequences prop = obj.getProperty(prop_id) if isinstance(prop, (tuple, list)): prop_value = (tuple([ p for p in prop if p not in prop_value and p not in remove_elements ]) + tuple(prop_value)) obj._updateProperty(prop_id, prop_value)
def update_many(self, context, request): """/@@API/update_many: Update existing object values This is a wrapper around the update method, allowing multiple updates to be combined into a single request. required parameters: - input_values: A json-encoded dictionary. Each key is an obj_path, and each value is a dictionary containing key/value pairs to be set on the object. Return value: { runtime: Function running time. error: true or string(message) if error. false if no error. success: true or string(message) if success. false if no success. updates: return values from update } """ self.context = context self.request = request self.unused = [x for x in self.request.form.keys()] self.used("form.submitted") self.used("__ac_name") self.used("__ac_password") ret = { "url": router.url_for("update_many", force_external=True), "success": False, "error": True, "updates": [], } input_values = json.loads(request.get('input_values', '[]')) if not input_values: raise BadRequest("missing input_values") site_path = request['PATH_INFO'].replace("/@@API/update_many", "") for obj_path, i in input_values.items(): savepoint = transaction.savepoint() if not obj_path.startswith("/"): obj_path = "/" + obj_path if obj_path.startswith(site_path): obj_path = obj_path[len(site_path):] obj = context.restrictedTraverse(str(site_path + obj_path)) this_ret = { "url": router.url_for("update_many", force_external=True), "success": False, "error": True, } try: fields = set_fields_from_request(obj, i) if not fields: this_ret['success'] = False this_ret['error'] = True else: this_ret['success'] = True this_ret['error'] = False except: savepoint.rollback() raise ret['updates'].append(this_ret) ret['success'] = True ret['error'] = False return ret
def validate_data(self, data): if not data.get('oguid'): raise BadRequest('Missing parameter oguid') return data
def update(self, context, request): """/@@API/update: Update existing object values Required parameters: - obj_path: path to the object, relative to plone site root. - fields: json value, dict: key:value = fieldname:value. { runtime: Function running time. error: true or string(message) if error. false if no error. success: true or string(message) if success. false if no success. <fieldname>: <current value> ... } So. >>> portal = layer['portal'] >>> portal_url = portal.absolute_url() >>> from plone.app.testing import SITE_OWNER_NAME >>> from plone.app.testing import SITE_OWNER_PASSWORD Update a client's existing address: >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD) >>> browser.open(portal_url+"/@@API/update?", "&".join([ ... "obj_path=/clients/client-1", ... "title=Test", ... "PostalAddress={'address': '1 Wendy Way', 'city': 'Johannesburg', 'zip': '9000', 'state': 'Gauteng', 'district': '', 'country':'South Africa'}" ... ])) >>> browser.contents '{..."success": true...}' quickly check that it saved: >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD) >>> browser.open(portal_url+"/@@API/read?", "&".join([ ... "id=client-1", ... "include_fields=PostalAddress", ... ])) >>> browser.contents '{...1 Wendy Way...}' Try the same with a nonsense fieldname: >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD) >>> browser.open(portal_url+"/@@API/update?", "&".join([ ... "obj_path=/clients/client-1", ... "Thing=Fish", ... ])) >>> browser.contents '{...The following request fields were not used: ...Thing...}' Setting the value of a RefereceField to "" or None (null) should not cause an error; setting an empty value should clear the field >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD) >>> browser.open(portal_url+"/@@API/update?", "&".join([ ... "obj_path=/clients/client-1", ... 'DefaultCategories=', ... ])) >>> browser.contents '{..."success": true...}' """ savepoint = transaction.savepoint() self.context = context self.request = request self.unused = [x for x in self.request.form.keys()] self.used("form.submitted") self.used("__ac_name") self.used("__ac_password") ret = { "url": router.url_for("update", force_external=True), "success": False, "error": True, } obj = None # Find out if the object can be retrieved via UID or via # traversing. if self.request.get('obj_uid', ''): uc = getToolByName(self.context, 'uid_catalog') brain = uc(UID=self.request.get('obj_uid', '')) obj = brain[0].getObject() if brain else None if self.request.get('obj_path', '') and not obj: obj_path = self.request['obj_path'] site_path = context.portal_url.getPortalObject().getPhysicalPath() if site_path and isinstance(site_path, basestring): site_path = site_path if site_path.startswith( '/') else '/' + site_path obj = context.restrictedTraverse(site_path + obj_path) elif site_path and len(site_path) > 1: site_path = site_path[1] site_path = site_path if site_path.startswith( '/') else '/' + site_path obj = context.restrictedTraverse(site_path + obj_path) if obj: self.used('obj_uid') self.used('obj_path') else: ret['success'] = False ret['error'] = True return ret try: fields = set_fields_from_request(obj, request) if not fields: return ret for field in fields: self.used(field) except: savepoint.rollback() raise ret['success'] = True ret['error'] = False if self.unused: raise BadRequest( "The following request fields were not used: %s. Request aborted." % self.unused) return ret
def get_userid(self): if len(self.params) != 1: raise BadRequest("Must supply user ID as URL path parameters.") return self.params[0]
def create(self, context, request): """/@@API/create: Create new object. Required parameters: - obj_type = portal_type of new object. - obj_path = path of new object, from plone site root. - Not required for obj_type=AnalysisRequest Optionally: - obj_id = ID of new object. All other parameters in the request are matched against the object's Schema. If a matching field is found in the schema, then the value is taken from the request and sent to the field's mutator. Reference fields may have their target value(s) specified with a delimited string query syntax, containing the portal_catalog search: <FieldName>=index1:value1|index2:value2 eg to set the Client of a batch: ...@@API/update?obj_path=<path>... ...&Client=title:<client_title>&... And, to set a multi-valued reference, these both work: ...@@API/update?obj_path=<path>... ...&InheritedObjects:list=title:AR1... ...&InheritedObjects:list=title:AR2... ...@@API/update?obj_path=<path>... ...&InheritedObjects[]=title:AR1... ...&InheritedObjects[]=title:AR2... The Analysis_Specification parameter is special, it mimics the format of the python dictionaries, and only service Keyword can be used to reference services. Even if the keyword is not actively required, it must be supplied: <service_keyword>:min:max:error tolerance The function returns a dictionary as a json string: { runtime: Function running time. error: true or string(message) if error. false if no error. success: true or string(message) if success. false if no success. } >>> portal = layer['portal'] >>> portal_url = portal.absolute_url() >>> from plone.app.testing import SITE_OWNER_NAME >>> from plone.app.testing import SITE_OWNER_PASSWORD Simple AR creation, no obj_path parameter is required: >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD) >>> browser.open(portal_url+"/@@API/create", "&".join([ ... "obj_type=AnalysisRequest", ... "Client=portal_type:Client|id:client-1", ... "SampleType=portal_type:SampleType|title:Apple Pulp", ... "Contact=portal_type:Contact|getFullname:Rita Mohale", ... "Services:list=portal_type:AnalysisService|title:Calcium", ... "Services:list=portal_type:AnalysisService|title:Copper", ... "Services:list=portal_type:AnalysisService|title:Magnesium", ... "SamplingDate=2013-09-29", ... "Specification=portal_type:AnalysisSpec|title:Apple Pulp", ... ])) >>> browser.contents '{..."success": true...}' If some parameters are specified and are not located as existing fields or properties of the created instance, the create should fail: >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD) >>> browser.open(portal_url+"/@@API/create?", "&".join([ ... "obj_type=Batch", ... "obj_path=/batches", ... "title=Test", ... "Thing=Fish" ... ])) >>> browser.contents '{...The following request fields were not used: ...Thing...}' Now we test that the AR create also fails if some fields are spelled wrong >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD) >>> browser.open(portal_url+"/@@API/create", "&".join([ ... "obj_type=AnalysisRequest", ... "thing=Fish", ... "Client=portal_type:Client|id:client-1", ... "SampleType=portal_type:SampleType|title:Apple Pulp", ... "Contact=portal_type:Contact|getFullname:Rita Mohale", ... "Services:list=portal_type:AnalysisService|title:Calcium", ... "Services:list=portal_type:AnalysisService|title:Copper", ... "Services:list=portal_type:AnalysisService|title:Magnesium", ... "SamplingDate=2013-09-29" ... ])) >>> browser.contents '{...The following request fields were not used: ...thing...}' """ savepoint = transaction.savepoint() self.context = context self.request = request self.unused = [x for x in self.request.form.keys()] self.used("form.submitted") self.used("__ac_name") self.used("__ac_password") # always require obj_type self.require("obj_type") obj_type = self.request['obj_type'] self.used("obj_type") # AnalysisRequest shortcut: creates Sample, Partition, AR, Analyses. if obj_type == "AnalysisRequest": try: return self._create_ar(context, request) except: savepoint.rollback() raise # Other object types require explicit path as their parent self.require("obj_path") obj_path = self.request['obj_path'] if not obj_path.startswith("/"): obj_path = "/" + obj_path self.used("obj_path") site_path = request['PATH_INFO'].replace("/@@API/create", "") parent = context.restrictedTraverse(str(site_path + obj_path)) # normal permissions still apply for this user if not getSecurityManager().checkPermission(AccessJSONAPI, parent): msg = "You don't have the '{0}' permission on {1}".format( AccessJSONAPI, parent.absolute_url()) raise Unauthorized(msg) obj_id = request.get("obj_id", "") _renameAfterCreation = False if not obj_id: _renameAfterCreation = True obj_id = tmpID() self.used(obj_id) ret = { "url": router.url_for("create", force_external=True), "success": True, "error": False, } try: obj = _createObjectByType(obj_type, parent, obj_id) obj.unmarkCreationFlag() if _renameAfterCreation: renameAfterCreation(obj) ret['obj_id'] = obj.getId() ret['obj_uid'] = obj.UID() used_fields = set_fields_from_request(obj, request) for field in used_fields: self.used(field) obj.reindexObject() obj.aq_parent.reindexObject() event.notify(ObjectInitializedEvent(obj)) obj.at_post_create_script() except: savepoint.rollback() raise if self.unused: raise BadRequest( "The following request fields were not used: %s. Request aborted." % self.unused) return ret
def _delUsers(self, names, REQUEST=None): if not names: raise BadRequest('No users specified') self._doDelUsers(names) if REQUEST: return self._mainUser(self, REQUEST)
def reply(self): data = json_body(self.request) type_ = data.get('@type', None) id_ = data.get('id', None) title = data.get('title', None) if not type_: raise BadRequest("Property '@type' is required") # Disable CSRF protection if 'IDisableCSRFProtection' in dir(plone.protect.interfaces): alsoProvides(self.request, plone.protect.interfaces.IDisableCSRFProtection) try: obj = create(self.context, type_, id_=id_, title=title) except Unauthorized as exc: self.request.response.setStatus(403) return dict(error=dict( type='Forbidden', message=str(exc))) except BadRequest as exc: self.request.response.setStatus(400) return dict(error=dict( type='Bad Request', message=str(exc))) # Acquisition wrap temporarily to satisfy things like vocabularies # depending on tools temporarily_wrapped = False if IAcquirer.providedBy(obj) and not safe_hasattr(obj, 'aq_base'): obj = obj.__of__(self.context) temporarily_wrapped = True # Update fields deserializer = queryMultiAdapter((obj, self.request), IDeserializeFromJson) if deserializer is None: self.request.response.setStatus(501) return dict(error=dict( message='Cannot deserialize type {}'.format(obj.portal_type))) try: deserializer(validate_all=True, create=True) except DeserializationError as e: self.request.response.setStatus(400) return dict(error=dict( type='DeserializationError', message=str(e))) if temporarily_wrapped: obj = aq_base(obj) if not getattr(deserializer, 'notifies_create', False): notify(ObjectCreatedEvent(obj)) obj = add(self.context, obj, rename=not bool(id_)) self.request.response.setStatus(201) self.request.response.setHeader('Location', obj.absolute_url()) serializer = queryMultiAdapter( (obj, self.request), ISerializeToJson ) serialized_obj = serializer() # HypermediaBatch can't determine the correct canonical URL for # objects that have just been created via POST - so we make sure # to set it here serialized_obj['@id'] = obj.absolute_url() return serialized_obj
def calculate_partitions(self, context, request): """Calculate the size and type of sample partitions required to perform analysis services on a certain sample type. Required fields - services: A list of Analysis Services - sampletype: The sample type in use for this sample Example usage: @@API/calculate_partitions ?sampletype=portal_type:SampleType,title=Cattle Feed &services:list=portal_type:AnalysisService,title:Ash &services:list=portal_type:AnalysisService,title:Moisture """ ret = { "url": router.url_for("calculate_partitions", force_external=True), "success": False, "error": True, } parts = [] uc = getToolByName(context, 'uid_catalog') services = [] _services = request.get('services', '').split(",") if not _services: raise BadRequest("services are not present in request") for uid in _services: if not uid: continue try: services.append(uc(UID=uid)[0].getObject()) except: raise BadRequest("analysis service uid %s is invalid" % uid) _sampletype = request.get('sampletype', '') if not _sampletype: raise BadRequest("sampletype is not present in the request") try: sampletype = uc(UID=_sampletype)[0].getObject() except: raise BadRequest("sample type %s is invalid" % _sampletype) for service in services: partsetup = [ ps for ps in service.getPartitionSetup() if ps['sampletype'] == sampletype.UID() ] if partsetup: # User values set for each SampleType on each service separate = bool(partsetup[0]['separate']) containers = [ uc(UID=uid)[0].getObject() for uid in partsetup[0]['container'] ] preservations = [ uc(UID=uid)[0].getObject() for uid in partsetup[0]['preservation'] ] minvol = mg(partsetup[0]['vol']) else: # read default values from service separate = service.getSeparate() containers = service.getContainer() if not containers: containers = [] if containers and not type(containers) in (list, tuple): containers = [ containers, ] preservations = service.getPreservation() if not preservations: preservations = [] if preservations and not type(preservations) in (list, tuple): preservations = [ preservations, ] minvol = mg(sampletype.getMinimumVolume()) # ContainerTypes may also be selected in the UI. # we want a list of actual containers which this analysis can use _containers = [] for c in containers: if c.portal_type == 'Container': _containers.append(c) else: _containers.extend(c.getContainers()) containers = _containers containers.sort( lambda a, b: cmp(mg(a.getCapacity()), mg(b.getCapacity()))) if separate: parts.append({ 'services': [ service, ], 'separate': True, 'container': containers, 'preservation': preservations, 'minvol': minvol, }) else: # find partition this analysis can use or create new entry. for x, part in enumerate(parts): if part['separate']: continue # our container must match this part. _containers = [] if part['container']: _containers = [ c for c in part['container'] if c in containers ] if not _containers: continue # our preservation must match this part _preservations = [] if part['preservation']: _preservations = [ p for p in part['preservation'] if p in preservations ] if not _preservations: continue # filter containers on capacoty _required_vol = part['minvol'] + minvol if _containers: _containers = [ c for c in _containers if mg(c.getCapacity()) > _required_vol ] if not _containers: continue # all the conditions passed: # this partition can host our analysis part['minvol'] = _required_vol part['services'].append(service) part['container'] = _containers part['preservation'] = _preservations parts[x] = part break # no partition found to share this analysis: create new. else: parts.append({ 'services': [ service, ], 'separate': False, 'container': containers, 'preservation': preservations, 'minvol': minvol }) # Convert objects to UIDs for x, part in enumerate(parts): parts[x]['service_titles'] = [s.Title() for s in part['services']] parts[x]['services'] = [s.UID() for s in part['services']] parts[x]['container_titles'] = [ c.Title() for c in part['container'] ] parts[x]['container'] = [c.UID() for c in part['container']] parts[x]['preservation_titles'] = [ p.Title() for p in part['preservation'] ] parts[x]['preservation'] = [p.UID() for p in part['preservation']] parts[x]['minvol'] = str(part['minvol']) ret['success'] = True ret['error'] = False ret['parts'] = parts return ret
def apply(self, obj, url=None, depth=0, result=None, top=1): if result is None: result = StringIO() depth = self.depth url = urlfix(self.request['URL'], 'PROPFIND') url = urlbase(url) result.write('<?xml version="1.0" encoding="utf-8"?>\n' '<d:multistatus xmlns:d="DAV:">\n') iscol = isDavCollection(obj) if iscol and url[-1] != '/': url = url + '/' result.write('<d:response>\n<d:href>%s</d:href>\n' % safe_quote(url)) if hasattr(aq_base(obj), 'propertysheets'): propsets = obj.propertysheets.values() obsheets = obj.propertysheets else: davprops = DAVProps(obj) propsets = (davprops, ) obsheets = {'DAV:': davprops} if self.allprop: stats = [] for ps in propsets: if hasattr(aq_base(ps), 'dav__allprop'): stats.append(ps.dav__allprop()) stats = ''.join(stats) or '<d:status>200 OK</d:status>\n' result.write(stats) elif self.propname: stats = [] for ps in propsets: if hasattr(aq_base(ps), 'dav__propnames'): stats.append(ps.dav__propnames()) stats = ''.join(stats) or '<d:status>200 OK</d:status>\n' result.write(stats) elif self.propnames: rdict = {} for name, ns in self.propnames: ps = obsheets.get(ns, None) if ps is not None and hasattr(aq_base(ps), 'dav__propstat'): ps.dav__propstat(name, rdict) else: prop = '<n:%s xmlns:n="%s"/>' % (name, ns) code = '404 Not Found' if code not in rdict: rdict[code] = [prop] else: rdict[code].append(prop) keys = rdict.keys() keys.sort() for key in keys: result.write('<d:propstat>\n' ' <d:prop>\n') map(result.write, rdict[key]) result.write(' </d:prop>\n' ' <d:status>HTTP/1.1 %s</d:status>\n' '</d:propstat>\n' % key) else: raise BadRequest('Invalid request') result.write('</d:response>\n') if depth in ('1', 'infinity') and iscol: for ob in obj.listDAVObjects(): if hasattr(ob, "meta_type"): if ob.meta_type == "Broken Because Product is Gone": continue dflag = hasattr(ob, '_p_changed') and (ob._p_changed is None) if hasattr(ob, '__locknull_resource__'): # Do nothing, a null resource shouldn't show up to DAV if dflag: ob._p_deactivate() elif hasattr(ob, '__dav_resource__'): uri = urljoin(url, absattr(ob.getId())) depth = depth == 'infinity' and depth or 0 self.apply(ob, uri, depth, result, top=0) if dflag: ob._p_deactivate() if not top: return result result.write('</d:multistatus>') return result.getvalue()
def _checkId(self, id, allow_dup=0): wf_def = aq_parent(aq_inner(self)) if id == wf_def.state_var: raise BadRequest('"%s" is used for keeping state.' % id) return ContainerTab._checkId(self, id, allow_dup)
def getQuestionData(self, dbQn, dbLec): """Fetch dict for question, obsfucating the answer""" out = None def getQuestionDict(plonePath): if '?' in plonePath: (plonePath, querystring) = plonePath.split('?', 1) querystring = urlparse.parse_qs(querystring) else: querystring = {} try: #NB: Unrestricted so we can see this even when direct access is banned dataView = self.portalObject().unrestrictedTraverse( str(plonePath) + '/@@data') except KeyError: raise NotFound(self, str(plonePath), self.request) return dataView.asDict(querystring) # Is the student requesting a particular question they've done before? if not out and dbQn.qnType == 'tw_questiontemplate' and 'question_id' in self.request.form and 'author_qn' not in self.request.form: student = self.getCurrentStudent() ugQn = (Session.query(db.UserGeneratedQuestion).filter( db.UserGeneratedQuestion.ugQuestionGuid == self.request.form['question_id']).filter( db.UserGeneratedQuestion.questionId == dbQn.questionId).filter( db.UserGeneratedQuestion.studentId != student.studentId ).first()) if ugQn is not None: # Found one, should return it out = self.ugQuestionToJson(ugQn) else: raise NotFound(self, self.request.form['question_id'], self.request) # If a questiontemplate, might want a student to evaluate a question if not out and dbQn.qnType == 'tw_questiontemplate': student = self.getCurrentStudent() # Fetch value of required settings settings = getStudentSettings(dbLec, student) setting_values = dict( prob_template_eval=float( settings.get('prob_template_eval', 0.8)), cap_template_qns=int(settings.get('cap_template_qns', 5)), cap_template_qn_reviews=int( settings.get('cap_template_qn_reviews', 10)), cap_template_qn_nonsense=int( settings.get('cap_template_qn_nonsense', 10)), ) # Should the user be reviewing a question? if self.request.form.get('author_qn', False): reviewQuestion = False elif (random.random() <= setting_values['prob_template_eval']): reviewQuestion = True elif setting_values['cap_template_qns'] > 0: reviewQuestion = (Session.query( db.UserGeneratedQuestion).filter( db.UserGeneratedQuestion.questionId == dbQn.questionId ).filter( db.UserGeneratedQuestion.studentId == student.studentId ).count()) >= setting_values['cap_template_qns'] else: reviewQuestion = False if reviewQuestion: # Generate query of all irrelevant questions; either the student already reviewed it ugAnswerQuery = (Session.query( db.UserGeneratedAnswer.ugQuestionGuid).filter( db.UserGeneratedAnswer.studentId == student.studentId)) # ...or that question has reached it's review cap ugAnswerQuery = ugAnswerQuery.union( Session.query(db.UserGeneratedAnswer.ugQuestionGuid). group_by(db.UserGeneratedAnswer.ugQuestionGuid).having( or_( func.count(db.UserGeneratedAnswer.ugAnswerId) >= setting_values['cap_template_qn_reviews'], and_( func.count(db.UserGeneratedAnswer.ugAnswerId) >= setting_values['cap_template_qn_nonsense'], func.sum(db.UserGeneratedAnswer.questionRating) < 0)))) ugAnswerQuery = aliased(db.UserGeneratedAnswer, ugAnswerQuery.subquery()) ugQn = (Session.query( db.UserGeneratedQuestion).outerjoin(ugAnswerQuery).filter( ugAnswerQuery.ugQuestionGuid == None).filter( db.UserGeneratedQuestion.questionId == dbQn.questionId).filter( db.UserGeneratedQuestion.studentId != student.studentId).filter( db.UserGeneratedQuestion.superseded == None).order_by(func.random()).first()) if ugQn is not None: # Found one, should return it out = self.ugQuestionToJson(ugQn) else: # Don't fall back to writing questions raise BadRequest("No questions for student to review") else: # Author a question out = getQuestionDict(dbQn.plonePath) qnUri = self.request.getURL() if '?' in qnUri: qnUri = qnUri.split('?')[0] out['uri'] = '%s?author_qn=yes' % self.request.getURL() if 'question_id' in self.request.form: # Trying to rewrite a question, so add in what was written before ugQn = (Session.query(db.UserGeneratedQuestion).filter( db.UserGeneratedQuestion.ugQuestionGuid == self.request.form['question_id']).filter( db.UserGeneratedQuestion.questionId == dbQn.questionId).filter( db.UserGeneratedQuestion.studentId == student.studentId).filter( db.UserGeneratedQuestion.superseded == None).first()) if ugQn is not None: # Found one, add it to question out['uri'] += '&question_id=%s' % ugQn.ugQuestionGuid out['student_answer'] = dict( text=ugQn.text, choices=[ dict( answer=getattr(ugQn, 'choice_%d_answer' % i, None), correct=getattr(ugQn, 'choice_%d_correct' % i, None), ) for i in range(0, 10) if getattr(ugQn, 'choice_%d_correct' % i, None) is not None ], explanation=ugQn.explanation) else: raise NotFound(self, self.request.form['question_id'], self.request) # No custom techniques, fetch question @@data if not out: out = getQuestionDict(dbQn.plonePath) # Obsfucate answer if 'answer' in out: out['answer'] = base64.b64encode(json.dumps(out['answer'])) return out
def COPY(self, REQUEST, RESPONSE): """Create a duplicate of the source resource whose state and behavior match that of the source resource as closely as possible. Though we may later try to make a copy appear seamless across namespaces (e.g. from Zope to Apache), COPY is currently only supported within the Zope namespace.""" self.dav__init(REQUEST, RESPONSE) if not hasattr(aq_base(self), 'cb_isCopyable') or \ not self.cb_isCopyable(): raise MethodNotAllowed('This object may not be copied.') depth = REQUEST.get_header('Depth', 'infinity') if depth not in ('0', 'infinity'): raise BadRequest('Invalid Depth header.') dest = REQUEST.get_header('Destination', '') while dest and dest[-1] == '/': dest = dest[:-1] if not dest: raise BadRequest('Invalid Destination header.') try: path = REQUEST.physicalPathFromURL(dest) except ValueError: raise BadRequest('Invalid Destination header') name = path.pop() oflag = REQUEST.get_header('Overwrite', 'F').upper() if oflag not in ('T', 'F'): raise BadRequest('Invalid Overwrite header.') try: parent = self.restrictedTraverse(path) except ValueError: raise Conflict('Attempt to copy to an unknown namespace.') except NotFound: raise Conflict('Object ancestors must already exist.') except Exception: raise if hasattr(parent, '__null_resource__'): raise Conflict('Object ancestors must already exist.') existing = hasattr(aq_base(parent), name) if existing and oflag == 'F': raise PreconditionFailed('Destination resource exists.') try: parent._checkId(name, allow_dup=1) except Exception: raise Forbidden(sys.exc_info()[1]) try: parent._verifyObjectPaste(self) except Unauthorized: raise except Exception: raise Forbidden(sys.exc_info()[1]) # Now check locks. The If header on a copy only cares about the # lock on the destination, so we need to check out the destinations # lock status. ifhdr = REQUEST.get_header('If', '') if existing: # The destination itself exists, so we need to check its locks destob = aq_base(parent)._getOb(name) if IWriteLock.providedBy(destob) and destob.wl_isLocked(): if ifhdr: itrue = destob.dav__simpleifhandler( REQUEST, RESPONSE, 'COPY', refresh=1) if not itrue: raise PreconditionFailed() else: raise Locked('Destination is locked.') elif IWriteLock.providedBy(parent) and parent.wl_isLocked(): if ifhdr: parent.dav__simpleifhandler(REQUEST, RESPONSE, 'COPY', refresh=1) else: raise Locked('Destination is locked.') self._notifyOfCopyTo(parent, op=0) ob = self._getCopy(parent) ob._setId(name) if depth == '0' and isDavCollection(ob): for id in ob.objectIds(): ob._delObject(id) notify(ObjectCopiedEvent(ob, self)) if existing: object = getattr(parent, name) self.dav__validate(object, 'DELETE', REQUEST) parent._delObject(name) parent._setObject(name, ob) ob = parent._getOb(name) ob._postCopy(parent, op=0) compatibilityCall('manage_afterClone', ob, ob) notify(ObjectClonedEvent(ob)) # We remove any locks from the copied object because webdav clients # don't track the lock status and the lock token for copied resources ob.wl_clearLocks() RESPONSE.setStatus(existing and 204 or 201) if not existing: RESPONSE.setHeader('Location', dest) RESPONSE.setBody('') return RESPONSE