Example #1
0
 def init_for_outgoing_email(self, agent):
     """
     This method sets up the Viewer to render the body of an outgoing email
     to the specified `agent`. There is no request associated with this
     viewer. Viewers initialized with this function should not be used to
     render ordinary actions (like item_show_html). Instead, they can be
     used to render emails that want to take advantage of the item_tags
     and existing infrastructure that requires a viewer.
     """
     self.request = None
     self.method = None
     self.cur_agent = agent
     self.cur_site = get_default_site()
     self.multi_agent_permission_cache = MultiAgentPermissionCache()
     self.permission_cache = self.multi_agent_permission_cache.get(self.cur_agent)
     self.format = 'html'
     self.noun = None
     self.item = None
     self.action = 'list'
     self.context = Context()
     self.context['action'] = self.action
     self.context['item'] = self.item
     self.context['specific_version'] = False
     self.context['viewer_name'] = self.viewer_name
     self.context['accepted_item_type'] = self.accepted_item_type
     self.context['accepted_item_type_name'] = self.accepted_item_type._meta.verbose_name
     self.context['accepted_item_type_name_plural'] = self.accepted_item_type._meta.verbose_name_plural
     self.context['full_path'] = '/'
     self.context['query_string'] = ''
     self.context['cur_agent'] = self.cur_agent
     self.context['cur_site'] = self.cur_site
     self.context['_viewer'] = self
     self.context['layout'] = 'blank.html'
Example #2
0
def handle_email(msg, email_username):
    multi_agent_permission_cache = MultiAgentPermissionCache()
    subject = get_subject(msg)
    if msg.is_multipart():
        body = msg.get_payload(0).get_payload()
    else:
        body = msg.get_payload()
    from_email = get_from_email(msg)
    try:
        email_contact_method = EmailContactMethod.objects.filter(email__iexact=from_email)[:1].get()
    except ObjectDoesNotExist:
        raise UserException('Error: Your comment could not be created because there is no agent with email address %s' % from_email)
    try:
        item = Item.item_for_notification_email_username(email_username)
    except ObjectDoesNotExist:
        raise UserException('Error: Your comment could not be created because there does is no item for email account %s' % email_username)
    permission_cache = multi_agent_permission_cache.get(email_contact_method.agent)
    if not permission_cache.agent_can('comment_on', item):
        display_name = item.display_name(permission_cache.agent_can('view Item.name', item))
        raise UserException('Error: Your comment could not be created because you do not have permission to comment on %s' % display_name)

    agent = email_contact_method.agent

    #TODO permissions to view Item.name: technically you could figure out the name of an item by commenting on it here (same issue in cms/views.py)
    if subject.lower().startswith('re: '):
        item_name = item.display_name()
        if item_name.lower().startswith('re: '):
            comment_name = item_name
        else:
            comment_name = 'Re: %s' % item_name
    else:
        comment_name = subject
    
    comment = TextComment(item=item, item_version_number=item.version_number, name=comment_name, body=body, from_contact_method=email_contact_method)
    comment.name = comment_name #TODO this is a hack due to multiple inheritance bug in Django. remove it when bug is fixed
    permissions = [OneToOnePermission(source=agent, ability='do_anything', is_allowed=True)]
    comment.save_versioned(action_agent=agent, initial_permissions=permissions)
Example #3
0
 def init_for_http(self, request, action, noun, format):
     """
     This method sets up the Viewer from an incoming HttpRequest. The
     `action`, `noun`, and `format` parameters are extracted from the URL
     (or the ViewerRequest). This method should only be called from
     cms/dispatcher.py.
     """
     self.context = Context()
     self.action = action or ('show' if noun else 'list')
     self.noun = noun
     self.format = format or 'html'
     self.request = request
     self.method = self.request.method
     self.cur_agent = get_logged_in_agent(request)
     self.cur_site = get_current_site(request)
     self.multi_agent_permission_cache = MultiAgentPermissionCache()
     self.permission_cache = self.multi_agent_permission_cache.get(self.cur_agent)
     if self.noun is None:
         self.item = None
     else:
         try:
             self.item = Item.objects.get(pk=self.noun)
             self.item = self.item.downcast()
             version_number = self.request.GET.get('version')
             if version_number is not None:
                 self.item.copy_fields_from_version(version_number)
             self.context['specific_version'] = ('version' in self.request.GET)
         except ObjectDoesNotExist:
             self.item = None
     self.context['action'] = self.action
     self.context['item'] = self.item
     self.context['viewer_name'] = self.viewer_name
     self.context['accepted_item_type'] = self.accepted_item_type
     self.context['accepted_item_type_name'] = self.accepted_item_type._meta.verbose_name
     self.context['accepted_item_type_name_plural'] = self.accepted_item_type._meta.verbose_name_plural
     self.context['full_path'] = self.request.get_full_path()
     self.context['query_string'] = self.request.META['QUERY_STRING']
     self.context['cur_agent'] = self.cur_agent
     self.context['cur_site'] = self.cur_site
     self.context['_viewer'] = self
     self.context['default_metadata_menu_option'] = self.default_metadata_menu_option()
     self._set_default_layout()
Example #4
0
class Viewer(object):
    """
    Superclass of all viewers. Implements all of the common functionality
    like dispatching and convenience methods, but does not define any views.
    
    Although most viewers will want to inherit from ItemViewer, since it
    defines the basic views (like list, show, edit), some viewers may want to
    inherit from this abstract Viewer so they can ensure no views will be
    inherited.
    
    Subclasses must define the following fields:
    * `accepted_item_type`: The item type that this viewer is defined over. For
      example, if accepted_item_type == Agent, this viewer can be used to view
      Agents and all subclasses of Agents (e.g., Person), but an error will be
      rendered if a user tries to view something else (e.g., a Document) with
      this viewer.
    * `viewer_name`: The name of the viewer, which shows up in the URL. This
      should only consist of lowercase letters, in accordance with the Deme URL
      scheme.
    
    There are two types of views subclasses can define: item-specific views,
    and type-wide views. Item-specific views expect an item in the URL (the noun),
    while type-wide views do not expect a particular item.
    1. To define an item-speicfic view, define a method with the name
      `item_method_format`, where `method` is the name of the view (which shows
      up in the URL as the action), and format is the output format (which shows
      up in the URL as the format). For example, the method item_edit_html(self)
      in an agent viewer represents the item-specific view with action="edit" and
      format="html", and would respond to the URL `/viewing/agent/123/edit.html`.
    2. To define a type-wide view, define a method with the name
      `type_method_format`. For example, the method type_new_html(self) in an
      agent viewer represents the type-wide view with action="new" and
      format="html", and would respond to the URL `/viewing/agent/new.html`.
    
    View methods take no parameters (except self), since all of the details of
    the request are defined as instance variables in the viewer before
    dispatch. They must return an HttpResponse. They should take advantage of
    self.context, which is defined before the view is called.
        
    The following instance variables are defined on the viewer:
    * self.item (the requested Item, or None if there is no noun)
    * self.context (the Context object used to render templates)
    * self.action (the action part of the URL)
    * self.noun (the noun part of the URL)
    * self.format (the format part of the URL)
    * self.request (the HttpRequest that initiated this viewer)
    * self.method (the HTTP method of the request, so that require_POST works)
    * self.cur_agent (the currently authenticated Agent or AnonymousAgent)
    * self.cur_site (the Site that is being used for this request)
    * self.multi_agent_permission_cache (a MultiAgentPermissionCache for
      permission queries)
    * self.permission_cache (a PermissionCache for permission queries on cur_agent)
    * self.item (the downcasted requested item if there's a noun in the request)
      - If there is a `version` parameter in the query string, the item's fields
        will be populated with data from the requested version
    
    The following context variables are defined:
    * self.context['action'] (the action part of the URL)
    * self.context['item'] (the requested Item, or None if there is no noun)
    * self.context['specific_version'] (True if the user requested a specific
      version of the item in the query string, otherwise False)
    * self.context['viewer_name'] (value of viewer.viewer_name)
    * self.context['accepted_item_type'] (value of viewer.accepted_item_type)
    * self.context['accepted_item_type_name'] (verbose name of accepted_item_type)
    * self.context['accepted_item_type_name_plural'] (verbose name plural of
      accepted_item_type)
    * self.context['full_path'] (the path to the current page)
    * self.context['query_string'] (the query string for the current page)
    * self.context['cur_agent'] (the currently authenticated Agent)
    * self.context['cur_site'] (the Site that is being used for this request)
    * self.context['_viewer'] (the viewer itself, only for custom tags/filters)
    * self.context['layout'] (the layout that the template should inherit from)

        
    """
    __metaclass__ = ViewerMetaClass

    ###########################################################################
    # Important functions (initializing and dispatching)
    ###########################################################################

    def __init__(self):
        # Nothing happens in the constructor. All of the initialization happens
        # in the init_for_* methods, based on how the viewer was loaded.
        pass

    def init_for_http(self, request, action, noun, format):
        """
        This method sets up the Viewer from an incoming HttpRequest. The
        `action`, `noun`, and `format` parameters are extracted from the URL
        (or the ViewerRequest). This method should only be called from
        cms/dispatcher.py.
        """
        self.context = Context()
        self.action = action or ('show' if noun else 'list')
        self.noun = noun
        self.format = format or 'html'
        self.request = request
        self.method = self.request.method
        self.cur_agent = get_logged_in_agent(request)
        self.cur_site = get_current_site(request)
        self.multi_agent_permission_cache = MultiAgentPermissionCache()
        self.permission_cache = self.multi_agent_permission_cache.get(self.cur_agent)
        if self.noun is None:
            self.item = None
        else:
            try:
                self.item = Item.objects.get(pk=self.noun)
                self.item = self.item.downcast()
                version_number = self.request.GET.get('version')
                if version_number is not None:
                    self.item.copy_fields_from_version(version_number)
                self.context['specific_version'] = ('version' in self.request.GET)
            except ObjectDoesNotExist:
                self.item = None
        self.context['action'] = self.action
        self.context['item'] = self.item
        self.context['viewer_name'] = self.viewer_name
        self.context['accepted_item_type'] = self.accepted_item_type
        self.context['accepted_item_type_name'] = self.accepted_item_type._meta.verbose_name
        self.context['accepted_item_type_name_plural'] = self.accepted_item_type._meta.verbose_name_plural
        self.context['full_path'] = self.request.get_full_path()
        self.context['query_string'] = self.request.META['QUERY_STRING']
        self.context['cur_agent'] = self.cur_agent
        self.context['cur_site'] = self.cur_site
        self.context['_viewer'] = self
        self.context['default_metadata_menu_option'] = self.default_metadata_menu_option()
        self._set_default_layout()

    def init_for_div(self, original_viewer, action, item, query_string):
        """
        This method sets up the Viewer to be embedded (probably in a <div>) in
        the specified original_viewer. The action, item, and query_string
        specify the details of what will be embedded in this viewer.
        """
        if item is None:
            path = reverse('item_type_url', kwargs={'viewer': self.viewer_name, 'action': action})
        else:
            path = reverse('item_url', kwargs={'viewer': self.viewer_name, 'action': action, 'noun': item.pk})
        self.request = VirtualRequest(original_viewer.request, path, query_string)
        self.method = self.request.method
        self.cur_agent = original_viewer.cur_agent
        self.cur_site = original_viewer.cur_site
        self.multi_agent_permission_cache = original_viewer.multi_agent_permission_cache
        self.permission_cache = original_viewer.permission_cache
        self.format = 'html'
        if item is None:
            self.noun = None
        else:
            self.noun = str(item.pk)
        self.item = item
        self.action = action
        self.context = Context()
        self.context['action'] = self.action
        self.context['item'] = self.item
        self.context['specific_version'] = False
        self.context['viewer_name'] = self.viewer_name
        self.context['accepted_item_type'] = self.accepted_item_type
        self.context['accepted_item_type_name'] = self.accepted_item_type._meta.verbose_name
        self.context['accepted_item_type_name_plural'] = self.accepted_item_type._meta.verbose_name_plural
        self.context['full_path'] = original_viewer.context['full_path'] 
        self.context['query_string'] = ''
        self.context['cur_agent'] = self.cur_agent
        self.context['cur_site'] = self.cur_site
        self.context['_viewer'] = self
        self.context['layout'] = 'blank.html'

    def init_for_outgoing_email(self, agent):
        """
        This method sets up the Viewer to render the body of an outgoing email
        to the specified `agent`. There is no request associated with this
        viewer. Viewers initialized with this function should not be used to
        render ordinary actions (like item_show_html). Instead, they can be
        used to render emails that want to take advantage of the item_tags
        and existing infrastructure that requires a viewer.
        """
        self.request = None
        self.method = None
        self.cur_agent = agent
        self.cur_site = get_default_site()
        self.multi_agent_permission_cache = MultiAgentPermissionCache()
        self.permission_cache = self.multi_agent_permission_cache.get(self.cur_agent)
        self.format = 'html'
        self.noun = None
        self.item = None
        self.action = 'list'
        self.context = Context()
        self.context['action'] = self.action
        self.context['item'] = self.item
        self.context['specific_version'] = False
        self.context['viewer_name'] = self.viewer_name
        self.context['accepted_item_type'] = self.accepted_item_type
        self.context['accepted_item_type_name'] = self.accepted_item_type._meta.verbose_name
        self.context['accepted_item_type_name_plural'] = self.accepted_item_type._meta.verbose_name_plural
        self.context['full_path'] = '/'
        self.context['query_string'] = ''
        self.context['cur_agent'] = self.cur_agent
        self.context['cur_site'] = self.cur_site
        self.context['_viewer'] = self
        self.context['layout'] = 'blank.html'

    def dispatch(self):
        """
        Perform the requested action and return an HttpResponse. In general,
        this should just be called from cms/dispatcher.py. Return None if there
        is no Python method that corresponds with the requested action.
        """
        # Make sure there isn't a cycle in the virtual request graph.
        if hasattr(self.request, 'has_virtual_request_cycle'):
            if self.request.has_virtual_request_cycle():
                return self.render_error(
                    'Cycle detected in embedded viewers',
                    'The embedded viewers cannot be rendered without an infinite loop.',
                    HttpResponseNotFound)

        # Get the Python method that corresponds to the requested action
        if self.noun is None:
            action_method_name = 'type_%s_%s' % (self.action, self.format)
        else:
            action_method_name = 'item_%s_%s' % (self.action, self.format)
        action_method = getattr(self, action_method_name, None)
        if not action_method:
            return None

        # Return an error if there was a noun in the request but we weren't
        # able to find the corresponding item, or if the corresponding item
        # is not an instance of accepted_item_type (with the exception of
        # the 'copy' action, which will allow any viewer to copy any item).
        if self.noun != None:
            if self.item is None:
                return self.render_item_not_found()
            if not isinstance(self.item, self.accepted_item_type) and self.action != 'copy':
                return self.render_item_not_found()

        # Perform the action
        try:
            response = action_method()
            return response
        except DemePermissionDenied, e:
            # If a DemePermissionDenied exception was raised while trying to
            # perform the action, render a friendly error page.
            from cms.templatetags.item_tags import get_item_link_tag
            ability_friendly_name = friendly_name_for_ability(e.ability) or e.ability
            # Explain what the user is trying to do
            if self.context.get('action_title'):
                msg = u'You do not have permission to perform the "%s" action' % self.context['action_title']
            else:
                msg = u'You do not have permission to perform the action'
            if self.item:
                msg += u' on %s' % get_item_link_tag(self.context, self.item)
            # Explain why the user cannot do it
            if e.item is None:
                msg += u' (you need the "%s" global ability)' % ability_friendly_name
            else:
                permission_item_text = 'it' if e.item == self.item else get_item_link_tag(self.context, e.item)
                if e.item.destroyed:
                    msg += u' (%s is destroyed)' % permission_item_text
                else:
                    msg += u' (you need the "%s" ability on %s)' % (ability_friendly_name, permission_item_text)
            return self.render_error('Permission Denied', msg)