Пример #1
0
    def set(self, instance, value, **kw):
        """
        Set Analyses to an AR

        :param instance: Analysis Request
        :param value: Single AS UID or a list of dictionaries containing AS UIDs
        :param kw: Additional keyword parameters passed to the field
        """

        if not isinstance(value, (list, tuple)):
            value = [value]

        uids = []
        for item in value:
            uid = None
            if isinstance(item, dict):
                uid = item.get("uid")
            if api.is_uid(value):
                uid = item
            if uid is None:
                logger.warn("Could extract UID of value")
                continue
            uids.append(uid)

        analyses = map(api.get_object_by_uid, uids)
        self._set(instance, analyses, **kw)
Пример #2
0
def get_object_by_record(record):
    """Find an object by a given record

    Inspects request the record to locate an object

    :param record: A dictionary representation of an object
    :type record: dict
    :returns: Found Object or None
    :rtype: object
    """

    # nothing to do here
    if not record:
        return None

    if record.get("uid"):
        return get_object_by_uid(record["uid"])
    if record.get("path"):
        return get_object_by_path(record["path"])
    if record.get("parent_path") and record.get("id"):
        path = "/".join([record["parent_path"], record["id"]])
        return get_object_by_path(path)

    logger.warn("get_object_by_record::No object found! record='%r'" % record)
    return None
Пример #3
0
    def _set(self, instance, value, **kw):
        """Set the value of the field
        """
        logger.debug("DexterityFieldManager::set: value=%r" % value)

        # Check if the field is read only
        if self.field.readonly:
            raise Unauthorized("Field is read only")

        fieldname = self.get_field_name()
        # id fields take only strings
        if fieldname == "id":
            value = str(value)

        try:
            # Validate
            self.field.validate(value)

            # TODO: Check security on the field level
            return self.field.set(instance, value)
        except WrongType:
            logger.warn("WrongType: Field={} Value={}".format(
                self.field, value))
        except:  # noqa
            logger.warn("Unknown Exception: Field={} Value={}".format(
                self.field, value))
Пример #4
0
    def get_keyword_query(self, **kw):
        """Generates a query from the given keywords.
        Only known indexes make it into the generated query.

        :returns: Catalog query
        :rtype: dict
        """
        query = dict()

        # Only known indexes get observed
        indexes = self.catalog.get_indexes()

        # Handle additional keyword parameters
        for k, v in kw.iteritems():
            # handle uid in keywords
            if k.lower() == "uid":
                k = "UID"
            # handle portal_type in keywords
            if k.lower() == "portal_type":
                if v:
                    v = _.to_list(v)
            if k not in indexes:
                logger.warn("Skipping unknown keyword parameter '%s=%s'" %
                            (k, v))
                continue
            if v is None:
                logger.warn("Skip None value in kw parameter '%s=%s'" % (k, v))
                continue
            logger.debug("Adding '%s=%s' to query" % (k, v))
            query[k] = v

        return query
Пример #5
0
def get_sort_on(allowed_indexes=None):
    """ returns the 'sort_on' from the request
    """
    sort_on = get("sort_on")
    if allowed_indexes and sort_on not in allowed_indexes:
        logger.warn("Index '{}' is not in allowed_indexes".format(sort_on))
        return None
    return sort_on
Пример #6
0
def disable_csrf_protection():
    """ disables the CSRF protection
        https://pypi.python.org/pypi/plone.protect
    """
    if not HAS_PLONE_PROTECT:
        logger.warn(
            "Can not disable CSRF protection – please install plone.protect")
        return False
    request = get_request()
    interface.alsoProvides(request, IDisableCSRFProtection)
    return True
Пример #7
0
    def set(self, instance, value, **kw):
        """Converts the value into a DateTime object before setting.
        """
        try:
            value = DateTime(value)
        except SyntaxError:
            logger.warn(
                "Value '{}' is not a valid DateTime string".format(value))
            return False

        self._set(instance, value, **kw)
Пример #8
0
    def json_data(self, instance, default=None):
        """Get a JSON compatible value
        """
        value = self.get(instance)

        out = []
        for rel in value:
            if rel.isBroken():
                logger.warn("Skipping broken relation {}".format(repr(rel)))
                continue
            obj = rel.to_object
            out.append(api.get_url_info(obj))
        return out
Пример #9
0
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
    :raises:
        APIError,
        :class:`~plone.jsonapi.routes.exceptions.APIError`
    """

    # ensure we have a full content object
    content = get_object(content)

    # Look for an update-specific adapter for this object
    adapter = queryAdapter(content, IUpdate)
    if adapter:
        # Use the adapter to update the object
        logger.info("Delegating 'update' operation of '{}'".format(
            api.get_path(content)))
        adapter.update_object(**record)

    else:
        # Fall-back to default update machinery
        # get the proper data manager
        dm = IDataManager(content)

        if dm is None:
            fail(400, "Update for this object is not allowed")

        # Bail-out non-update-able fields
        purged_records = copy.deepcopy(record)
        map(lambda key: purged_records.pop(key, None), SKIP_UPDATE_FIELDS)

        # Iterate through record items
        for k, v in purged_records.items():
            try:
                success = dm.set(k, v, **record)
            except Unauthorized:
                fail(401, "Not allowed to set the field '%s'" % k)
            except ValueError, exc:
                fail(400, str(exc))

            if not success:
                logger.warn("update_object_with_data::skipping key=%r", k)
                continue

            logger.debug("update_object_with_data::field %r updated", k)
Пример #10
0
def create_object(container, portal_type, **data):
    """Creates an object slug

    :returns: The new created content object
    :rtype: object
    """

    if "id" in data:
        # always omit the id as senaite LIMS generates a proper one
        id = data.pop("id")
        logger.warn("Passed in ID '{}' omitted! Senaite LIMS "
                    "generates a proper ID for you".format(id))

    try:
        # Is there any adapter registered to handle the creation of this type?
        adapter = queryAdapter(container, ICreate, name=portal_type)
        if adapter and adapter.is_creation_delegated():
            logger.info("Delegating 'create' operation of '{}' in '{}'".format(
                portal_type, api.get_path(container)))
            return adapter.create_object(**data)

        # Special case for ARs
        # => return immediately w/o update
        if portal_type == "AnalysisRequest":
            obj = create_analysisrequest(container, **data)
            # Omit values which are already set through the helper
            data = u.omit(data, "SampleType", "Analyses")
            # Set the container as the client, as the AR lives in it
            data["Client"] = container
            return obj
        # Standard content creation
        else:
            # we want just a minimun viable object and set the data later
            obj = api.create(container, portal_type)
            # obj = api.create(container, portal_type, **data)
    except Unauthorized:
        fail(401, "You are not allowed to create this content")

    # Update the object with the given data, but omit the id
    try:
        update_object_with_data(obj, data)
    except APIError:
        # Failure in creation process, delete the invalid object
        # NOTE: We bypass the permission checks
        container._delObject(obj.id)
        # reraise the error
        raise

    return obj
Пример #11
0
def url_for(endpoint, default=DEFAULT_ENDPOINT, **values):
    """Looks up the API URL for the given endpoint

    :param endpoint: The name of the registered route (aka endpoint)
    :type endpoint: string
    :returns: External URL for this endpoint
    :rtype: string/None
    """

    try:
        return router.url_for(endpoint, force_external=True, values=values)
    except Exception:
        logger.warn("Could not build API URL for endpoint '%s'. "
                    "No route provider registered?" % endpoint)
        # build generic API URL
        return router.url_for(default, force_external=True, values=values)
Пример #12
0
def resource_to_portal_type(resource):
    """Converts a resource to a portal type

    :param resource: Resource name as it is used in the content route
    :type name: string
    :returns: Portal type name
    :rtype: string
    """
    if resource is None:
        return None

    resource_mapping = get_resource_mapping()
    portal_type = resource_mapping.get(resource.lower())

    if portal_type is None:
        logger.warn("Could not map the resource '{}' "
                    "to any known portal type".format(resource))

    return portal_type
Пример #13
0
def get_user_info(user):
    """Get the user information
    """
    user = api.get_user(user)
    current = api.get_current_user()

    if api.is_anonymous():
        return {
            "username": current.getUserName(),
            "authenticated": False,
            "roles": current.getRoles(),
            "api_url": api.url_for("senaite.jsonapi.v1.users",
                                   username="******"),
        }

    # nothing to do
    if user is None:
        logger.warn("No user found for {}".format(user))
        return None

    # plone user
    pu = user.getUser()

    info = {
        "username": user.getUserName(),
        "roles": user.getRoles(),
        "groups": pu.getGroups(),
        "authenticated": current == user,
        "api_url": api.url_for("senaite.jsonapi.v1.users",
                               username=user.getId()),
    }

    for k, v in api.get_user_properties(user).items():
        if api.is_date(v):
            v = api.to_iso_date(v)
        if not api.is_json_serializable(v):
            logger.warn(
                "User property '{}' is not JSON serializable".format(k))
            continue
        info[k] = v

    return info
Пример #14
0
def create_object(container, portal_type, **data):
    """Creates an object slug

    :returns: The new created content object
    :rtype: object
    """

    if "id" in data:
        # always omit the id as senaite LIMS generates a proper one
        id = data.pop("id")
        logger.warn("Passed in ID '{}' omitted! Senaite LIMS "
                    "generates a proper ID for you".format(id))

    try:
        # Special case for ARs
        # => return immediately w/o update
        if portal_type == "AnalysisRequest":
            obj = create_analysisrequest(container, **data)
            # Omit values which are already set through the helper
            data = u.omit(data, "SampleType", "Analyses")
            # Set the container as the client, as the AR lives in it
            data["Client"] = container
        # Standard content creation
        else:
            # we want just a minimun viable object and set the data later
            obj = api.create(container, portal_type)
            # obj = api.create(container, portal_type, **data)
    except Unauthorized:
        fail(401, "You are not allowed to create this content")

    # Update the object with the given data, but omit the id
    try:
        update_object_with_data(obj, data)
    except APIError:

        # Failure in creation process, delete the invalid object
        container.manage_delObjects(obj.id)
        # reraise the error
        raise

    return obj
Пример #15
0
def to_json_value(obj, fieldname, value=_marker, default=None):
    """JSON save value encoding

    :param obj: Content object
    :type obj: ATContentType/DexterityContentType
    :param fieldname: Schema name of the field
    :type fieldname: str/unicode
    :param value: The field value
    :type value: depends on the field type
    :returns: JSON encoded field value
    :rtype: field dependent
    """

    # This function bridges the value of the field to a probably more complex
    # JSON structure to return to the client.

    # extract the value from the object if omitted
    if value is _marker:
        value = IDataManager(obj).json_data(fieldname)

    # convert objects
    if isinstance(value, ImplicitAcquisitionWrapper):
        return get_url_info(value)

    # check if the value is callable
    if callable(value):
        value = value()

    # convert dates
    if is_date(value):
        return to_iso_date(value)

    # check if the value is JSON serializable
    if not is_json_serializable(value):
        logger.warn("Output {} is not JSON serializable".format(repr(value)))
        return default

    return value
Пример #16
0
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
    :raises:
        APIError,
        :class:`~plone.jsonapi.routes.exceptions.APIError`
    """

    # ensure we have a full content object
    content = get_object(content)

    # get the proper data manager
    dm = IDataManager(content)

    if dm is None:
        fail(400, "Update for this object is not allowed")

    # Iterate through record items
    for k, v in record.items():
        try:
            success = dm.set(k, v, **record)
        except Unauthorized:
            fail(401, "Not allowed to set the field '%s'" % k)
        except ValueError, exc:
            fail(400, str(exc))

        if not success:
            logger.warn("update_object_with_data::skipping key=%r", k)
            continue

        logger.debug("update_object_with_data::field %r updated", k)
Пример #17
0
def get_workflow_info(brain_or_object, endpoint=None):
    """Generate workflow information of the assigned workflows

    :param brain_or_object: A single catalog brain or content object
    :type brain_or_object: ATContentType/DexterityContentType/CatalogBrain
    :param endpoint: The named URL endpoint for the root of the items
    :type endpoint: str/unicode
    :returns: Workflows info
    :rtype: dict
    """

    # ensure we have a full content object
    obj = get_object(brain_or_object)

    # get the portal workflow tool
    wf_tool = get_tool("portal_workflow")

    # the assigned workflows of this object
    workflows = wf_tool.getWorkflowsFor(obj)

    # no worfkflows assigned -> return
    if not workflows:
        return []

    def to_transition_info(transition):
        """ return the transition information
        """
        return {
            "title": transition["title"],
            "value": transition["id"],
            "display": transition["description"],
            "url": transition["url"],
        }

    def to_review_history_info(review_history):
        """ return the transition information
        """
        converted = DT2dt(review_history.get('time')).\
            strftime("%Y-%m-%d %H:%M:%S")
        review_history['time'] = converted
        return review_history

    out = []

    for workflow in workflows:

        # get the status info of the current state (dictionary)
        info = wf_tool.getStatusOf(workflow.getId(), obj)
        if info is None:
            continue

        # get the current review_status
        review_state = info.get("review_state", None)
        inactive_state = info.get("inactive_state", None)
        cancellation_state = info.get("cancellation_state", None)
        worksheetanalysis_review_state = info.get(
            "worksheetanalysis_review_state", None)

        state = review_state or \
            inactive_state or \
            cancellation_state or \
            worksheetanalysis_review_state

        if state is None:
            logger.warn("No state variable found for {} -> {}".format(
                repr(obj), info))
            continue

        # get the wf status object
        status_info = workflow.states[state]

        # get the title of the current status
        status = status_info.title

        # get the transition informations
        transitions = map(to_transition_info, wf_tool.getTransitionsFor(obj))

        # get the review history
        rh = map(to_review_history_info,
                 workflow.getInfoFor(obj, 'review_history', ''))

        out.append({
            "workflow": workflow.getId(),
            "status": status,
            "review_state": state,
            "transitions": transitions,
            "review_history": rh,
        })

    return {"workflow_info": out}
Пример #18
0
def get_info(brain_or_object, endpoint=None, complete=False):
    """Extract the data from the catalog brain or object

    :param brain_or_object: A single catalog brain or content object
    :type brain_or_object: ATContentType/DexterityContentType/CatalogBrain
    :param endpoint: The named URL endpoint for the root of the items
    :type endpoint: str/unicode
    :param complete: Flag to wake up the object and fetch all data
    :type complete: bool
    :returns: Data mapping for the object/catalog brain
    :rtype: dict
    """

    # also extract the brain data for objects
    if not is_brain(brain_or_object):
        brain_or_object = get_brain(brain_or_object)
        if brain_or_object is None:
            logger.warn(
                "Couldn't find/fetch brain of {}".format(brain_or_object))
            return {}
        complete = True

    # When querying uid catalog we have to be sure that we skip the objects
    # used to relate two or more objects
    if is_relationship_object(brain_or_object):
        logger.warn("Skipping relationship object {}".format(
            repr(brain_or_object)))
        return {}

    # extract the data from the initial object with the proper adapter
    info = IInfo(brain_or_object).to_dict()

    # update with url info (always included)
    url_info = get_url_info(brain_or_object, endpoint)
    info.update(url_info)

    # include the parent url info
    parent = get_parent_info(brain_or_object)
    info.update(parent)

    # add the complete data of the object if requested
    # -> requires to wake up the object if it is a catalog brain
    if complete:
        # ensure we have a full content object
        obj = api.get_object(brain_or_object)
        # get the compatible adapter
        adapter = IInfo(obj)
        # update the data set with the complete information
        info.update(adapter.to_dict())

        # update the data set with the workflow information
        # -> only possible if `?complete=yes&workflow=yes`
        if req.get_workflow(False):
            info.update(get_workflow_info(obj))

        # # add sharing data if the user requested it
        # # -> only possible if `?complete=yes`
        # if req.get_sharing(False):
        #     sharing = get_sharing_info(obj)
        #     info.update({"sharing": sharing})

    return info
Пример #19
0
 def set(self, name, value, **kw):
     """Setter is not used for catalog brains
     """
     logger.warn("Setting is not allowed on catalog brains")
Пример #20
0
 def set(self, instance, value, **kw):
     """Not applicable for Computed Fields
     """
     logger.warn("Setting is not allowed for computed fields")