def renameAfterCreation(obj): # Can't rename without a subtransaction commit when using portal_factory transaction.savepoint(optimistic=True) # The id returned should be normalized already new_id = generateUniqueId(obj) obj.aq_inner.aq_parent.manage_renameObject(obj.id, new_id) return new_id
def remove(self, context, request): """/@@API/remove: Remove existing object Required parameters: - UID: UID for the object. { 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. } 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 >>> blah = portal.portal_catalog(Type = "Contact")[-1] >>> uid = blah.UID >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD) >>> browser.open(portal_url+"/@@API/remove?UID="+uid) >>> browser.contents '{..."success": true...}' """ savepoint = transaction.savepoint() uc = getToolByName(context, 'uid_catalog') _uid = request.get('UID', '') if not _uid: raise BadRequest("No UID specified in request") ret = { "url": router.url_for("remove", force_external=True), "success": True, "error": False, } data = uc(UID=_uid) if not data: raise BadRequest("No objects found") for proxy in data: try: parent = proxy.getObject().aq_parent parent.manage_delObjects([proxy.id]) except Exception as e: savepoint.rollback() msg = "Cannot delete '{0}' because ({1})".format( _uid, e.message) raise BadRequest(msg) return ret
def remove(self, context, request): """/@@API/remove: Remove existing object Required parameters: - UID: UID for the object. { 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. } 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 >>> blah = portal.portal_catalog(Type = "Contact")[-1] >>> uid = blah.UID >>> browser = layer['getBrowser'](portal, loggedIn=True, username=SITE_OWNER_NAME, password=SITE_OWNER_PASSWORD) >>> browser.open(portal_url+"/@@API/remove?UID="+uid) >>> browser.contents '{..."success": true...}' """ savepoint = transaction.savepoint() uc = getToolByName(context, 'uid_catalog') _uid = request.get('UID', '') if not _uid: raise BadRequest("No UID specified in request") ret = { "url": router.url_for("remove", force_external=True), "success": True, "error": False, } data = uc(UID=_uid) if not data: raise BadRequest("No objects found") for proxy in data: try: parent = proxy.getObject().aq_parent parent.manage_delObjects([proxy.id]) except Exception as e: savepoint.rollback() msg = "Cannot delete '{0}' because ({1})".format(_uid, e.message) raise BadRequest(msg) return ret
def do_action_for(self, context, request): """/@@API/doActionFor: Perform workflow transition on values returned by jsonapi "read" function. Required parameters: - action: The workflow transition to apply to found objects. Parameters used to locate objects are the same as used for the "read" method. """ savepoint = transaction.savepoint() workflow = getToolByName(context, 'portal_workflow') uc = getToolByName(context, 'uid_catalog') action = request.get('action', '') if not action: raise BadRequest("No action specified in request") ret = { "url": router.url_for("doActionFor", force_external=True), "success": True, "error": False, } data = read(context, request) objects = data.get('objects', []) if len(objects) == 0: raise BadRequest("No matching objects found") for obj_dict in objects: try: obj = uc(UID=obj_dict['UID'])[0].getObject() workflow.doActionFor(obj, action) obj.reindexObject() except Exception as e: savepoint.rollback() msg = "Cannot execute '{0}' on {1} ({2})".format( action, obj, e.message) msg = msg.replace("${action_id}", action) raise BadRequest(msg) return ret
def do_action_for_many(self, context, request): """/@@API/doActionFor: Perform workflow transition on a list of objects. required parameters: - obj_paths: a json encoded list of objects to transition. - action: the id of the transition """ savepoint = transaction.savepoint() workflow = getToolByName(context, 'portal_workflow') site_path = request['PATH_INFO'].replace("/@@API/doActionFor_many", "") obj_paths = json.loads(request.get('f', '[]')) action = request.get('action', '') if not action: raise BadRequest("No action specified in request") ret = { "url": router.url_for("doActionFor_many", force_external=True), "success": True, "error": False, } for obj_path in obj_paths: if not obj_path.startswith("/"): obj_path = "/" + obj_path obj = context.restrictedTraverse(str(site_path + obj_path)) if obj_path.startswith(site_path): obj_path = obj_path[len(site_path):] try: workflow.doActionFor(obj, action) obj.reindexObject() except Exception as e: savepoint.rollback() msg = "Cannot execute '{0}' on {1} ({2})".format( action, obj, e.message) msg = msg.replace("${action_id}", action) raise BadRequest(msg) return ret
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 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: set_fields_from_request(obj, i) except: savepoint.rollback() raise this_ret['success'] = True this_ret['error'] = False ret['updates'].append(this_ret) ret['success'] = True ret['error'] = False return ret
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 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() 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 __call__(self): form = self.request.form portal = getSite() workbook = None if 'setupexisting' in form and 'existing' in form and form['existing']: fn = form['existing'].split(":") self.dataset_project = fn[0] self.dataset_name = fn[1] path = 'setupdata/%s/%s.xlsx' % \ (self.dataset_name, self.dataset_name) filename = resource_filename(self.dataset_project, path) try: workbook = load_workbook( filename=filename) # , use_iterators=True) except AttributeError: print "" print traceback.format_exc() print "Error while loading ", path elif 'setupfile' in form and 'file' in form and form[ 'file'] and 'projectname' in form and form['projectname']: self.dataset_project = form['projectname'] tmp = tempfile.mktemp() file_content = form['file'].read() open(tmp, 'wb').write(file_content) workbook = load_workbook(filename=tmp) # , use_iterators=True) self.dataset_name = 'uploaded' assert (workbook is not None) adapters = [[name, adapter] for name, adapter in list( getAdapters((self.context, ), ISetupDataImporter))] for sheetname in workbook.get_sheet_names(): transaction.savepoint() ad_name = sheetname.replace(" ", "_") if ad_name in [a[0] for a in adapters]: adapter = [a[1] for a in adapters if a[0] == ad_name][0] adapter(self, workbook, self.dataset_project, self.dataset_name) adapters = [a for a in adapters if a[0] != ad_name] for name, adapter in adapters: transaction.savepoint() adapter(self, workbook, self.dataset_project, self.dataset_name) check = len(self.deferred) while len(self.deferred) > 0: new = self.solve_deferred() logger.info("solved %s of %s deferred references" % (check - new, check)) if new == check: raise Exception("%s unsolved deferred references: %s" % (len(self.deferred), self.deferred)) check = new logger.info("Rebuilding bika_setup_catalog") bsc = getToolByName(self.context, 'bika_setup_catalog') bsc.clearFindAndRebuild() logger.info("Rebuilding bika_catalog") bc = getToolByName(self.context, 'bika_catalog') bc.clearFindAndRebuild() logger.info("Rebuilding bika_analysis_catalog") bac = getToolByName(self.context, 'bika_analysis_catalog') bac.clearFindAndRebuild() message = PMF("Changes saved.") self.context.plone_utils.addPortalMessage(message) self.request.RESPONSE.redirect(portal.absolute_url())
def __call__(self): form = self.request.form portal = getSite() workbook = None if 'setupexisting' in form and 'existing' in form and form['existing']: fn = form['existing'].split(":") self.dataset_project = fn[0] self.dataset_name = fn[1] path = 'setupdata/%s/%s.xlsx' % \ (self.dataset_name, self.dataset_name) filename = resource_filename(self.dataset_project, path) try: workbook = load_workbook(filename=filename) # , use_iterators=True) except AttributeError: print "" print traceback.format_exc() print "Error while loading ", path elif 'setupfile' in form and 'file' in form and form['file'] and 'projectname' in form and form['projectname']: self.dataset_project = form['projectname'] tmp = tempfile.mktemp() file_content = form['file'].read() open(tmp, 'wb').write(file_content) workbook = load_workbook(filename=tmp) # , use_iterators=True) self.dataset_name = 'uploaded' assert(workbook is not None) adapters = [[name, adapter] for name, adapter in list(getAdapters((self.context, ), ISetupDataImporter))] for sheetname in workbook.get_sheet_names(): transaction.savepoint() ad_name = sheetname.replace(" ", "_") if ad_name in [a[0] for a in adapters]: adapter = [a[1] for a in adapters if a[0] == ad_name][0] adapter(self, workbook, self.dataset_project, self.dataset_name) adapters = [a for a in adapters if a[0] != ad_name] for name, adapter in adapters: transaction.savepoint() adapter(self, workbook, self.dataset_project, self.dataset_name) check = len(self.deferred) while len(self.deferred) > 0: new = self.solve_deferred() logger.info("solved %s of %s deferred references" % ( check - new, check)) if new == check: raise Exception("%s unsolved deferred references: %s" % ( len(self.deferred), self.deferred)) check = new logger.info("Rebuilding bika_setup_catalog") bsc = getToolByName(self.context, 'bika_setup_catalog') bsc.clearFindAndRebuild() logger.info("Rebuilding bika_catalog") bc = getToolByName(self.context, 'bika_catalog') bc.clearFindAndRebuild() logger.info("Rebuilding bika_analysis_catalog") bac = getToolByName(self.context, 'bika_analysis_catalog') bac.clearFindAndRebuild() message = PMF("Changes saved.") self.context.plone_utils.addPortalMessage(message) self.request.RESPONSE.redirect(portal.absolute_url())