def copy_items(portal_type=None, request=None, uid=None, endpoint=None): """ copy items """ # try to find the requested objects objects = find_objects(uid=uid) # No objects could be found, bail out if not objects: raise APIError(404, "No Objects could be found") # We support only to copy a single object if len(objects) > 1: raise APIError(400, "Can only copy one object at a time") # We don't want to copy the portal object if filter(lambda o: is_root(o), objects): raise APIError(400, "Can not copy the portal object") # cut the object obj = objects[0] obj.aq_parent.manage_copyObjects(obj.getId(), REQUEST=request) request.response.setHeader("Content-Type", "application/json") info = IInfo(obj)() return [info]
def update_object_with_data(content, record): """ update the content with the values from records """ dm = IDataManager(content, None) if dm is None: raise APIError(400, "Update on this object is not allowed") for k, v in record.items(): try: success = dm.set(k, v) except Unauthorized: raise APIError(401, "You are not allowed to set the field '%s'" % k) if not success: logger.info("update_object_with_data::skipping key=%r", k) continue logger.info("update_object_with_data::field %r updated with value=%r", k, v) # do a wf transition if record.get("transition", None): t = record.get("transition") logger.info(">>> Do Transition '%s' for Object %s", t, content.getId()) do_action_for(content, t) # reindex the object content.reindexObject() return content
def delete_items(portal_type=None, request=None, uid=None, endpoint=None): """ delete items 1. If the uid is given, we can ignore the request body and delete the object with the given uid (if the uid was valid). 2. If no uid is given, the user wants to delete more than one item. => go through each item and extract the uid. Delete it afterwards. // we should do this kind of transaction base. So if we can not get an // object for an uid, no item will be deleted. 3. we could check if the portal_type matches, just to be sure the user wants to delete the right content. """ # try to find the requested objects objects = find_objects(uid=uid) # We don't want to delete the portal object if filter(lambda o: is_root(o), objects): raise APIError(400, "Can not delete the portal object") results = [] for obj in objects: info = IInfo(obj)() info["deleted"] = delete_object(obj) results.append(info) if not results: raise APIError(404, "No Objects could be found") return results
def find_target_container(record): """Locates a target container for the given record :param record: The dictionary representation of a content object :type record: dict :returns: folder which contains the object :rtype: object """ parent_uid = record.get("parent_uid") parent_path = record.get("parent_path") target = None # Try to find the target object if parent_uid: target = get_object_by_uid(parent_uid) elif parent_path: target = get_object_by_path(parent_path) # Issue 18 if target is None: target = mkdir(parent_path) else: raise APIError(404, "No target UID/PATH information found") if not target: raise APIError(404, "No target container found") return target
def paste_items(portal_type=None, request=None, uid=None, endpoint=None): """ paste items """ # try to find the requested objects objects = find_objects(uid=uid) # No objects could be found, bail out if not objects: raise APIError(404, "No Objects could be found") # check if the cookie is there cookie = req.get_cookie("__cp") if cookie is None: raise APIError(400, "No data found to paste") # We support only to copy a single object if len(objects) > 1: raise APIError(400, "Can only paste to one location") # cut the object obj = objects[0] # paste the object results = obj.manage_pasteObjects(cookie) out = [] for result in results: new_id = result.get("new_id") pasted = obj.get(new_id) if pasted: out.append(IInfo(pasted)()) return out
def login(context, request): """ Login Route Login route to authenticate a user against Plone. """ # extract the data __ac_name = request.form.get("__ac_name", None) __ac_password = request.form.get("__ac_password", None) logger.info("*** LOGIN %s ***" % __ac_name) if __ac_name is None: raise APIError(400, "Username is missing") if __ac_password is None: raise APIError(400, "Password is missing") acl_users = ploneapi.portal.get_tool("acl_users") # XXX hard coded acl_users.credentials_cookie_auth.login() # XXX amin user won't be logged in if I use this approach # acl_users.login() # response = request.response # acl_users.updateCredentials(request, response, __ac_name, __ac_password) if ploneapi.user.is_anonymous(): raise APIError(401, "Invalid Credentials") # return the JSON in the same format like the user route return get_user(context, request, username=__ac_name)
def delete_object(obj): """ delete the object """ # we do not want to delete the site root! if is_root(obj): raise APIError(401, "Removing the Portal is not allowed") try: success = ploneapi.content.delete(obj) == None and True or False except Unauthorized: raise APIError( 401, "You are not allowed to delete the object '%s'" % obj.getId())
def update_object_with_data(content, record): """Update the content with the record data :param content: A single folderish catalog brain or content object :type content: ATContentType/DexterityContentType/CatalogBrain :param record: The data to update :type record: dict :returns: The updated content object :rtype: object """ # ensure we have a full content object content = get_object(content) # get the proper data manager dm = IDataManager(content) if dm is None: raise APIError(400, "Update on this object is not allowed") # Iterate through record items for k, v in record.items(): try: success = dm.set(k, v, **record) except Unauthorized: raise APIError(401, "Not allowed to set the field '%s'" % k) if not success: logger.warn("update_object_with_data::skipping key=%r", k) continue logger.debug("update_object_with_data::field %r updated", k) # do a wf transition if record.get("transition", None): t = record.get("transition") logger.debug(">>> Do Transition '%s' for Object %s", t, content.getId()) do_action_for(content, t) # do a sharing update if record.get("sharing", None): s = record.get("sharing") logger.debug(">>> Update sharing to %r for Object %s", s, content.getId()) update_sharing_for(content, s) # reindex the object content.reindexObject() return content
def delete_object(brain_or_object): """Delete the given object :param brain_or_object: A single catalog brain or content object :type brain_or_object: ATContentType/DexterityContentType/CatalogBrain :returns: Nothing :rtype: None """ obj = get_object(brain_or_object) # we do not want to delete the site root! if is_root(obj): raise APIError(401, "Removing the Portal is not allowed") try: return ploneapi.content.delete(obj) is None and True or False except Unauthorized: raise APIError(401, "Not allowed to delete object '%s'" % obj.getId())
def mkdir(path): """Crate a folder structure by a given path Behavior is similar to `mkdir -p` :param path: The physical path of the folder :type path: string :returns: folder located at the path :rtype: object """ container = get_portal() segments = path.split("/") curpath = None for n, segment in enumerate(segments): # skip the first element if not segment: continue curpath = "/".join(segments[:n + 1]) obj = get_object_by_path(curpath) if obj: container = obj continue if not is_folderish(container): raise APIError(400, "Object at %s is not a folder" % curpath) # create the folder on the go container = ploneapi.content.create(container, type="Folder", id=segment) return container
def create_items(portal_type=None, request=None, uid=None, endpoint=None): """ create items 1. If the uid is given, get the object and create the content in there (assumed that it is folderish) 2. If the uid is 0, the target folder is assumed the portal. 3. If there is no uid given, the payload is checked for either a key - `parent_uid` specifies the *uid* of the target folder - `parent_path` specifies the *physical path* of the target folder """ # destination where to create the content dest = uid and get_object_by_uid(uid) or None # extract the data from the request records = req.get_request_data() results = [] for record in records: if dest is None: # find the container for content creation dest = find_target_container(record) if portal_type is None: portal_type = record.get("portal_type", None) id = record.get("id", None) title = record.get("title", None) obj = create_object_in_container(dest, portal_type, id=id, title=title) # update the object update_object_with_data(obj, record) results.append(obj) if not results: raise APIError(400, "No Objects could be created") return make_items_for(results, endpoint=endpoint)
def mkdir(path): """ creates a folder structure by a given path """ container = get_portal() segments = path.split("/") curpath = None for n, segment in enumerate(segments): # skip the first element if not segment: continue curpath = "/".join(segments[:n + 1]) obj = get_object_by_path(curpath) if obj: container = obj continue if not is_folderish(container): raise APIError(400, "Object at %s is not a folder" % curpath) # create the folder on the go container = ploneapi.content.create(container, type="Folder", title=segment, save_id=True) return container
def get_object_by_uid(uid): """ Fetches an object by uid """ # nothing to do here if uid is None: return None # define uid 0 as the portal object if str(uid).lower() in PORTAL_IDS: return get_portal() # we try to find the object with both catalogs pc = get_portal_catalog() rc = get_portal_reference_catalog() # try to find the object with the reference catalog first obj = rc.lookupObject(uid) if obj: return obj # try to find the object with the portal catalog res = pc(dict(UID=uid)) if len(res) > 1: raise APIError(400, "More than one object found for UID %s" % uid) if not res: return None return get_object(res[0])
def create_object(**kw): defaults = {"save_id": True} defaults.update(kw) try: return ploneapi.content.create(**defaults) except Unauthorized: raise APIError(401, "You are not allowed to create this content")
def get(context, request, resource=None, uid=None): """Get Plone contents, e.g. <Plonesite>/@@API/plone/api/1.0/folder -> returns all folders <Plonesite>/@@API/plone/api/1.0/folder/4711 -> returns the folder with UID 4711 """ # We have a UID, return the record if uid and not resource: return api.get_record(uid) # we have a UID as resource, return the record if api.is_uid(resource): return api.get_record(resource) # BBB if resource == "get": logger.warn( "The /get route is obsolete and will be removed in 1.0.0. Please use /<UID> instead" ) return api.get_record(uid) portal_type = api.resource_to_portal_type(resource) if portal_type is None: raise APIError(404, "Not Found") return api.get_batched(portal_type=portal_type, uid=uid, endpoint="plone.jsonapi.routes.get")
def get_object_by_path(path): """Find an object by a given physical path :param path: The physical path of the object to find :type path: string :returns: Found Object or None :rtype: object """ # nothing to do here if not path: return None pc = get_portal_catalog() portal = get_portal() portal_path = get_path(portal) if not path.startswith(portal_path): raise APIError(404, "Not a physical path inside the portal") if path == portal_path: return portal res = pc(path=dict(query=path, depth=0)) if not res: return None return get_object(res[0])
def get_schema(self): """ get the schema """ try: return self.context.Schema() except AttributeError: raise APIError(400, "Can not get Schema of %r" % self.context)
def get_request_data(): """ extract and convert the json data from the request returns a list of dictionaries """ request = get_request() data = request.get("BODY", "{}") if not is_json_deserializable(data): from plone.jsonapi.routes.exceptions import APIError raise APIError(400, "Request Data is not JSON deserializable – Check JSON Syntax!") return _.convert(json.loads(data), _.to_list)
def create_object_in_container(container, portal_type, id=None, title=None): """ creates an object with the given data in the container """ if not is_root(container) and portal_type not in get_locally_allowed_types( container): raise APIError( 500, "Creation of this portal type is not allowed in this context.") return create_object(container=container, id=id, title=title, type=portal_type)
def create_object(**kw): """Creates an object :returns: The new created content object :rtype: object """ defaults = {"save_id": True} defaults.update(kw) try: return ploneapi.content.create(**defaults) except Unauthorized: raise APIError(401, "You are not allowed to create this content")
def get_record(uid=None): """ returns a single record """ obj = None if uid is not None: obj = get_object_by_uid(uid) else: form = req.get_form() obj = get_object_by_record(form) if obj is None: raise APIError(404, "No object found") items = make_items_for([obj]) return _.first(items)
def find_target_container(record): """ find the target container for this record """ parent_uid = record.get("parent_uid") parent_path = record.get("parent_path") target = None # Try to find the target object if parent_uid: target = get_object_by_uid(parent_uid) elif parent_path: target = get_object_by_path(parent_path) # Issue 18 if target is None: target = mkdir(parent_path) else: raise APIError(404, "No target UID/PATH information found") if not target: raise APIError(404, "No target container found") return target
def get_record(uid=None): """ returns a single record """ obj = None if uid is not None: obj = get_object_by_uid(uid) else: form = req.get_form() obj = get_object_by_record(form) if obj is None: raise APIError(404, "No object found") complete = req.get_complete(default=_marker) if complete is _marker: complete = True items = make_items_for([obj], complete=complete) return _.first(items)
def create_object_in_container(container, portal_type, id=None, title=None): """Creates a content object in the specified container :param container: A single folderish catalog brain or content object :type container: ATContentType/DexterityContentType/CatalogBrain :param portal_type: The portal type to create :type portal_type: string :returns: The new content object :rtype: object """ container = get_object(container) allowed_types = get_locally_allowed_types(container) if not is_root(container) and portal_type not in allowed_types: raise APIError( 500, "Creation of this portal type" "is not allowed in this context.") return create_object(container=container, id=id, title=title, type=portal_type)
def delete_items(portal_type=None, request=None, uid=None, endpoint=None): """ delete items 1. If the uid is given, we can ignore the request body and delete the object with the given uid (if the uid was valid). 2. If no uid is given, the user wants to delete more than one item. => go through each item and extract the uid. Delete it afterwards. // we should do this kind of transaction base. So if we can not get an // object for an uid, no item will be deleted. 3. we could check if the portal_type matches, just to be sure the user wants to delete the right content. """ # the data to update records = req.get_request_data() # we have an uid -> try to get an object for it obj = get_object_by_uid(uid) if obj: info = IInfo(obj)() info["deleted"] = delete_object(obj) return [info] # no uid -> go through the record items results = [] for record in records: obj = get_object_by_record(record) # no object found for this record if obj is None: continue info = IInfo(obj)() info["deleted"] = delete_object(obj) results.append(info) if not results: raise APIError(400, "No Objects could be deleted") return results
def get_object_by_path(path): """ fetch the object by physical path """ # nothing to do here if not path: return None pc = get_portal_catalog() portal = get_portal() portal_path = get_path(portal) if not path.startswith(portal_path): raise APIError(404, "Not a physical path inside the portal") if path == portal_path: return portal res = pc(path=dict(query=path, depth=0)) if not res: return None return get_object(res[0])
def update_items(portal_type=None, request=None, uid=None, endpoint=None): """ update items 1. If the uid is given, the user wants to update the object with the data given in request body 2. If no uid is given, the user wants to update a bunch of objects. -> each record contains either an UID, path or parent_path + id """ # the data to update records = req.get_request_data() # we have an uid -> try to get an object for it obj = get_object_by_uid(uid) if obj: record = records[0] # ignore other records if we got an uid obj = update_object_with_data(obj, record) return make_items_for([obj], endpoint=endpoint) # no uid -> go through the record items results = [] for record in records: obj = get_object_by_record(record) # no object found for this record if obj is None: continue # update the object with the given record data obj = update_object_with_data(obj, record) results.append(obj) if not results: raise APIError(400, "No Objects could be updated") return make_items_for(results, endpoint=endpoint)
def fail(status, msg): """API Error """ if msg is None: msg = "Reason not given." raise APIError(status, "{}".format(msg))