Пример #1
0
 def has_permission(self, permission, obj):
     """
     Return true if authenticated user has been granted the given permission on obj.
     """
     # Check roles, strongest first to optimize caching.
     try:
         p_template = obj.model_class.permission_templates.get(obj.permissions)
     except KeyError:
         # XXX Perm template is invalid or incomplete... Should do something here...
         # But if we're on the system account, let's pass
         if issubclass(self.model_class, SystemAccount):
             return True
         log.warning("Invalid permission template: '%s' on %s" % (obj.permissions, obj.model_class, ))
     if self.has_role(p_template[permission], obj):
         return True
     
     # Didn't find, we disallow
     return False
Пример #2
0
    def has_permission(self, permission, obj):
        """
        Return true if authenticated user has been granted the given permission on obj.
        """
        # Check roles, strongest first to optimize caching.
        try:
            p_template = obj.model_class.permission_templates.get(
                obj.permissions)
        except KeyError:
            # XXX Perm template is invalid or incomplete... Should do something here...
            # But if we're on the system account, let's pass
            if issubclass(self.model_class, SystemAccount):
                return True
            log.warning("Invalid permission template: '%s' on %s" % (
                obj.permissions,
                obj.model_class,
            ))
        if self.has_role(p_template[permission], obj):
            return True

        # Didn't find, we disallow
        return False
Пример #3
0
    def save(self, *args, **kw):
        """
        Set various object attributes
        """
        import account
        import community

        auth = Twistable.objects._getAuthenticatedAccount()

        # Check if we're saving a real object and not a generic Content one (which is prohibited).
        # This must be a programming error, then.
        if self.__class__.__name__ == Twistable.__name__:
            raise ValidationError(
                "You cannot save a raw content object. Use a derived class instead."
            )

        # Set information used to retreive the actual subobject
        self.model_name = self._meta.object_name
        self.app_label = self._meta.app_label

        # Set owner, publisher upon object creation. Publisher is NEVER set as None by default.
        if self.id is None:
            # If self.owner is already set, ensure it's done by SystemAccount
            if self.owner_id:
                if not isinstance(auth, account.SystemAccount):
                    raise PermissionDenied(
                        "You're not allowed to set the content owner by yourself."
                    )
            else:
                self.owner = self.getDefaultOwner()
            if not self.publisher_id:
                self.publisher = self.getDefaultPublisher()
            else:
                if not self.publisher.can_publish:
                    raise PermissionDenied(
                        "You're not allowed to publish on %s" % self.publisher)
        else:
            # XXX TODO: Check that nobody sets /unsets the owner or the publisher of an object
            # raise PermissionDenied("You're not allowed to set the content owner by yourself.")
            if not self.can_edit:
                raise PermissionDenied(
                    "You're not allowed to edit this content.")

        # Set created_by and modified_by fields
        if self.id is None:
            self.created_by = auth
        self.modified_by = auth

        # Check if publisher is set. Only GlobalCommunity may have its publisher to None to make a site visible on the internet.
        if not self.publisher_id:
            if not self.__class__._ALLOW_NO_PUBLISHER:
                raise ValueError(
                    "Only the Global Community can have no publisher, not %s" %
                    self)

        # Set permissions; we will apply them last to ensure we have an id.
        # We also ensure that the right permissions are set on the right object
        if not self.permissions:
            perm_template = self.model_class.permission_templates
            if not perm_template:
                raise ValueError(
                    "permission_templates not defined on class %s" %
                    self.__class__.__name__)
            self.permissions = perm_template.get_default()
        tpl = [
            t for t in self.permission_templates.permissions()
            if t["id"] == self.permissions
        ]
        if not tpl:
            # Didn't find? We restore default setting. XXX Should log/alert something here!
            tpl = [
                t for t in self.permission_templates.permissions() if t["id"]
                == self.model_class.permission_templates.get_default()
            ]
            log.warning("Restoring default permissions. Problem here.")
            log.warning(
                "Unable to find %s permission template %s in %s" %
                (self, self.permissions, self.permission_templates.perm_dict))
        if tpl[0].get("disabled_for_community") and issubclass(
                self.publisher.model_class, community.Community):
            raise ValueError(
                "Invalid permission setting %s for this object (%s/%s)" %
                (tpl, self, self.title_or_description))
        elif tpl[0].get("disabled_for_useraccount") and issubclass(
                self.publisher.model_class, account.UserAccount):
            raise ValueError(
                "Invalid permission setting %s for this object (%s/%s)" %
                (tpl, self, self.title_or_description))
        for perm, role in tpl[0].items():
            if perm.startswith("can_"):
                if callable(role):
                    role = role(self)
                setattr(self, "_p_%s" % perm, role)

        # Check if we're creating or not
        created = not self.id

        # Generate slug (or not !)
        if not self.slug and self.__class__._FORCE_SLUG_CREATION:
            if self.title:
                self.slug = slugify(self.title)
            elif self.description:
                self.slug = slugify(self.description)
            else:
                self.slug = slugify(self.model_name)
            self.slug = self.slug[:40]
        if created and self.__class__._FORCE_SLUG_CREATION:
            while Twistable.objects.__booster__.filter(
                    slug=self.slug).exists():
                match = re.search("_(?P<num>[0-9]+)$", self.slug)
                if match:
                    root = self.slug[:match.start()]
                    num = int(match.groupdict()['num']) + 1
                else:
                    root = self.slug
                    num = 1
                self.slug = "%s_%i" % (
                    root,
                    num,
                )

        # Perform a full_clean on the model just to be sure it validates correctly
        self.full_clean()

        # Save and update access network information
        ret = super(Twistable, self).save(*args, **kw)
        self._update_access_network()

        # Send TN's post-save signal
        twistable_post_save.send(sender=self.__class__,
                                 instance=self,
                                 created=created)
        return ret
Пример #4
0
    def get_query_set(
        self,
        __account__=None,
        request=None,
    ):
        """
        Return a queryset of 100%-authorized objects. All (should) have the can_list perm to True.
        This is in fact a kind of 'has_permission(can_list)' method!
        
        This method IS very slow. But you can speed things up if you pass either 'request' or '__account__' along the lines.
        Be aware, however, that in this case you loose the 'safety belt' provided by the security model.
        """
        # Check for anonymous query
        import community, account, community
        __account__ = self._getAuthenticatedAccount(__account__, request)
        base_query_set = super(TwistableManager, self).get_query_set()

        # System account: return all objects without asking any question. And with all permissions set.
        if __account__.id == account.SystemAccount.SYSTEMACCOUNT_ID:
            return base_query_set

        # XXX TODO: Make a special query for admin members? Or at least mgrs of the global community?
        # XXX Make this more efficient?
        # XXX Or, better, check if current user is manager of the owner ?
        if __account__.id > 0:
            managed_accounts = [
                __account__.id,
            ]
        else:
            managed_accounts = []

        # XXX This try/except is there so that things don't get stucked during boostrap
        try:
            if __account__.is_admin:
                return base_query_set.filter(_p_can_list__lte=roles.manager, )
        except DatabaseError:
            log.warning(
                "DB error while checking AdminCommunity. This is NORMAL during syncdb or bootstrap."
            )
            return base_query_set

        # Regular check. Works for anonymous as well...
        # network_ids = __account__.network_ids
        if not __account__.is_anonymous:
            qs = base_query_set.filter(
                Q(
                    owner__id=__account__.id,
                    _p_can_list=roles.owner,
                ) | Q(
                    owner__id=__account__.id,
                    _p_can_list=roles.manager,
                ) | Q(
                    _access_network__targeted_network__target=__account__,
                    _p_can_list=roles.network,
                ) | Q(
                    _access_network__targeted_network__target=__account__,
                    _p_can_list=roles.public,
                ) | Q(
                    # Anonymous stuff
                    _access_network__isnull=True,
                    _p_can_list=roles.public,
                ))
        else:
            # Anon query. Easy: We just return public stuff.
            # Warning: nested query is surely inefficient...
            free_access_network = Twistable.objects.__booster__.filter(
                _access_network__isnull=True,
                _p_can_list=roles.public,
            )
            qs = base_query_set.filter(
                Q(
                    # Strictly anonymous stuff
                    _access_network__isnull=True,
                    _p_can_list=roles.public,
                ) | Q(
                    # Incidently anonymous stuff (public stuff published by an anon account)
                    _access_network__isnull=False,
                    _access_network__id__in=free_access_network,
                    _p_can_list=roles.public,
                ))
        return qs
Пример #5
0
    def __call__(self, sender, **kwargs):
        """
        Generate the message itself.
        XXX TODO: Handle translation correctly (not from the request only)
        """
        # Fake-Login with SystemAccount so that everybody can be notified,
        # even users this current user can't list.
        from twistranet.twistapp.models import SystemAccount, Account, UserAccount, Community, Twistable
        __account__ = SystemAccount.get()
        from_email = settings.SERVER_EMAIL
        host = settings.EMAIL_HOST
        cache_mimeimages = {}
        if not host:
            # If host is disabled (EMAIL_HOST is None), skip that
            return
        
        # Handle recipients emails
        recipients = kwargs.get(self.recipient_arg, None)
        if not recipients:
            raise ValueError("Recipient must be provided as a '%s' parameter" % self.recipient_arg)
        to_list = []
        if not isinstance(recipients, (list, tuple, QuerySet, )):
            recipients = (recipients, )
        for recipient in recipients:
            if isinstance(recipient, Twistable):
                recipient = recipient.object
            if isinstance(recipient, UserAccount):
                to = recipient.email
                if not to:
                    log.warning("Can't send email for '%s': %s doesn't have an email registered." % (sender, recipient, ))
                    return
                to_list.append(to)
            elif isinstance(recipient, Community):
                if self.managers_only:
                    members = recipient.managers
                else:
                    members = recipient.members
                # XXX Suboptimal for very large communities
                to_list.extend([ member.email for member in members if member.email ])
            elif type(recipient) in (str, unicode, ):
                to_list.append(recipient)        # XXX Todo: check the '@'
            else:
                raise ValueError("Invalid recipient: %s (%s)" % (recipient, type(recipient), ))
                
        # Fetch templates
        text_template = kwargs.get('text_template', self.text_template)
        html_template = kwargs.get('html_template', self.html_template)
                
        # Now generate template and send mail for each recipient
        # XXX TODO: See http://docs.djangoproject.com/en/1.2/topics/email/#sending-multiple-e-mails
        # for the good approach to use.
        for to in to_list:
            # Append domain (and site info) to kwargs
            d = kwargs.copy()
            domain = cache.get("twistranet_site_domain")
            d.update({
                "domain":       domain,
                "site_name":    utils.get_site_name(),
                "baseline":     utils.get_baseline(),
                "recipient":    to,     # A string
            })
        
            # Load both templates and render them with kwargs context
            text_tpl = get_template(text_template)
            c = Context(d)
            text_content = text_tpl.render(c).strip()
            if html_template:
                html_tpl = get_template(html_template)
                html_content = html_tpl.render(c)
            else:
                html_content = None
            
            # Fetch back subject from text template
            subject = self.subject
            if not subject:
                match = SUBJECT_REGEX.search(text_content)
                if match:
                    subject = match.groups()[0]
            if not subject:
                raise ValueError("No subject provided nor 'Subject:' first line in your text template")
            
            # Remove empty lines and "Subject:" line from text templates
            text_content = SUBJECT_REGEX.sub('', text_content)
            text_content = EMPTY_LINE_REGEX.sub('\n', text_content)
        
            # Prepare messages
            msg = EmailMultiAlternatives(subject, text_content, from_email, [ to ], )
            if html_content:
                if getattr(settings, 'SEND_EMAIL_IMAGES_AS_ATTACHMENTS', DEFAULT_SEND_EMAIL_IMAGES_AS_ATTACHMENTS):
                    # we replace img links by img Mime Images
                    mimeimages = []
                    def replace_img_url(match):
                        """Change src url by mimeurl
                           fill the mimeimages list
                        """
                        urlpath = str(match.group('urlpath'))
                        attribute = str(match.group('attribute'))

                        is_static = False
                        pathSplit = urlpath.split('/')
                        if 'static' in pathSplit:
                            filename = urlpath.split('/static/')[-1]
                            is_static = True
                        else:
                            # XXX TODO : need to be improved split with site path (for vhosts)
                            filename = urlpath.split('/')[-1]
                        nb = len(mimeimages)+1
                        mimeimages.append((filename, 'img%i'%nb, is_static))
                        mimeurl = "cid:img%i" %nb
                        return '%s="%s"' % (attribute,mimeurl)

                    img_url_expr = re.compile('(?P<attribute>src)\s*=\s*([\'\"])(%s)?(?P<urlpath>[^\"\']*)\\2' %domain, re.IGNORECASE)
                    html_content = img_url_expr.sub(replace_img_url, html_content)
                    msg.attach_alternative(html_content, "text/html")
                    if mimeimages:
                        msg.mixed_subtype = 'related'
                        for fkey, name, is_static in mimeimages:
                            if cache_mimeimages.has_key(fkey):
                                msgImage = cache_mimeimages[fkey]
                            else:
                                if is_static:
                                    f = open(path.join(settings.TWISTRANET_STATIC_PATH, fkey), 'rb')
                                else:
                                    f = open(path.join(settings.MEDIA_ROOT, fkey), 'rb')
                                cache_mimeimages[fkey] = msgImage = MIMEImage(f.read())
                                f.close()
                            msgImage.add_header('Content-ID', '<%s>' % name)
                            msgImage.add_header('Content-Disposition', 'inline')
                            msg.attach(msgImage)
                # just inline images
                else:
                    msg.attach_alternative(html_content, "text/html")
            # Send safely
            try:
                log.debug("Sending mail: '%s' from '%s' to '%s'" % (subject, from_email, to))
                msg.send()
            except:
                log.warning("Unable to send message to %s" % to)
                log.exception("Here's what we've got as an error.")
Пример #6
0
    def prepare_view(self):
        """
        Render the import form
        or do the import (from csv file posted).
        """

        if self.request.method == "POST" and \
           self.request.FILES.get("csv_file", None):
            csv_file = self.request.FILES.get("csv_file")
            reader = csv.reader(csv_file, dialect=CSVDialect)
            for line in reader:
                if not line:
                    continue
                # firstname;lastname;email
                firstname = line[0].decode('utf8')
                lastname = line[1].decode('utf8')
                email = line[2]
                username = email.split('@')[0]
                username = slugify(username).replace('_','-')
                if User.objects.filter(username = username).exists():
                    u = User.objects.get(username = username)
                    useraccount = UserAccount.objects.get(user = u)
                    log.info( "User account '%s' already exixts" %useraccount.title )
                else:
                    # create user
                    try:
                        __account__ = SystemAccount.get()
                        u = User.objects.create(
                            username = username,
                            first_name = firstname,
                            last_name = lastname,
                            email = email,
                            is_superuser = False,
                            is_active = True,
                        )
                        chars = string.ascii_letters + string.digits
                        random.seed = (os.urandom(1024))
                        password = ''.join(random.choice(chars) for i in range(6))
                        u.set_password(password)
                        u.save()
                        useraccount = UserAccount.objects.get(user = u)
                        useraccount.title = u"%s %s" % (firstname, lastname)
                        useraccount.save()
                        log.info( "User account '%s' for %s %s (%s) created !" %(username, firstname, lastname,  email))

                        # notify imported user (a mail is sent to prevent user)
                        h = "%s%s%s%s" % (settings.SECRET_KEY, email, password, time.strftime("%Y%m%d"))
                        h = hashlib.md5(h).hexdigest()
                        reset_link = reverse(ResetPassword.name, args = (h, urllib.quote_plus(email)))

                        user_imported.send(
                            sender = self.__class__,
                            target = useraccount,
                            reset_password_url = reset_link,
                        )
                        del __account__
                    except:
                        log.warning( "Impossible to create account '%s' for %s %s (%s)" %(username, firstname, lastname,  email))
                        continue

                community_title = line[3].decode('utf8')
                cid = slugify(community_title)
                if  Community.objects.filter(slug = cid).exists():
                    log.info( "Community %s already exists !" %community )
                else:
                    c  = Community.objects.create(
                        slug = cid,
                        title = community_title,
                        permissions = "workgroup"
                    )
                    c.save()

                com = Community.objects.get(slug= cid)
                com.join(account=useraccount)
                log.info( "user %s join the community %s !" %(useraccount.title, community_title) )

            messages.info( self.request, u"import finished",)
Пример #7
0
    def __call__(self, sender, **kwargs):
        """
        Generate the message itself.
        XXX TODO: Handle translation correctly (not from the request only)
        """
        # Fake-Login with SystemAccount so that everybody can be notified,
        # even users this current user can't list.
        from twistranet.twistapp.models import SystemAccount, Account, UserAccount, Community, Twistable
        __account__ = SystemAccount.get()
        from_email = settings.SERVER_EMAIL
        host = settings.EMAIL_HOST
        cache_mimeimages = {}
        if not host:
            # If host is disabled (EMAIL_HOST is None), skip that
            return

        # Handle recipients emails
        recipients = kwargs.get(self.recipient_arg, None)
        if not recipients:
            raise ValueError("Recipient must be provided as a '%s' parameter" %
                             self.recipient_arg)
        to_list = []
        if not isinstance(recipients, (
                list,
                tuple,
                QuerySet,
        )):
            recipients = (recipients, )
        for recipient in recipients:
            if isinstance(recipient, Twistable):
                recipient = recipient.object
            if isinstance(recipient, UserAccount):
                to = recipient.email
                if not to:
                    log.warning(
                        "Can't send email for '%s': %s doesn't have an email registered."
                        % (
                            sender,
                            recipient,
                        ))
                    return
                to_list.append(to)
            elif isinstance(recipient, Community):
                if self.managers_only:
                    members = recipient.managers
                else:
                    members = recipient.members
                # XXX Suboptimal for very large communities
                to_list.extend(
                    [member.email for member in members if member.email])
            elif type(recipient) in (
                    str,
                    unicode,
            ):
                to_list.append(recipient)  # XXX Todo: check the '@'
            else:
                raise ValueError("Invalid recipient: %s (%s)" % (
                    recipient,
                    type(recipient),
                ))

        # Fetch templates
        text_template = kwargs.get('text_template', self.text_template)
        html_template = kwargs.get('html_template', self.html_template)

        # Now generate template and send mail for each recipient
        # XXX TODO: See http://docs.djangoproject.com/en/1.2/topics/email/#sending-multiple-e-mails
        # for the good approach to use.
        for to in to_list:
            # Append domain (and site info) to kwargs
            d = kwargs.copy()
            domain = cache.get("twistranet_site_domain")
            d.update({
                "domain": domain,
                "site_name": utils.get_site_name(),
                "baseline": utils.get_baseline(),
                "recipient": to,  # A string
            })

            # Load both templates and render them with kwargs context
            text_tpl = get_template(text_template)
            c = Context(d)
            text_content = text_tpl.render(c).strip()
            if html_template:
                html_tpl = get_template(html_template)
                html_content = html_tpl.render(c)
            else:
                html_content = None

            # Fetch back subject from text template
            subject = self.subject
            if not subject:
                match = SUBJECT_REGEX.search(text_content)
                if match:
                    subject = match.groups()[0]
            if not subject:
                raise ValueError(
                    "No subject provided nor 'Subject:' first line in your text template"
                )

            # Remove empty lines and "Subject:" line from text templates
            text_content = SUBJECT_REGEX.sub('', text_content)
            text_content = EMPTY_LINE_REGEX.sub('\n', text_content)

            # Prepare messages
            msg = EmailMultiAlternatives(
                subject,
                text_content,
                from_email,
                [to],
            )

            if html_content:
                if getattr(settings, 'SEND_EMAIL_IMAGES_AS_ATTACHMENTS',
                           DEFAULT_SEND_EMAIL_IMAGES_AS_ATTACHMENTS):
                    # we replace img links by img Mime Images
                    mimeimages = []

                    def replace_img_url(match):
                        """Change src url by mimeurl
                           fill the mimeimages list
                        """
                        urlpath = str(match.group('urlpath'))
                        attribute = str(match.group('attribute'))

                        is_static = False
                        pathSplit = urlpath.split('/')
                        if 'static' in pathSplit:
                            filename = urlpath.split('/static/')[-1]
                            is_static = True
                        else:
                            # XXX TODO : need to be improved split with site path (for vhosts)
                            filename = urlpath.split('/')[-1]
                        nb = len(mimeimages) + 1
                        mimeimages.append((filename, 'img%i' % nb, is_static))
                        mimeurl = "cid:img%i" % nb
                        return '%s="%s"' % (attribute, mimeurl)

                    img_url_expr = re.compile(
                        '(?P<attribute>src)\s*=\s*([\'\"])(%s)?(?P<urlpath>[^\"\']*)\\2'
                        % domain, re.IGNORECASE)
                    html_content = img_url_expr.sub(replace_img_url,
                                                    html_content)
                    msg.attach_alternative(html_content, "text/html")
                    if mimeimages:
                        msg.mixed_subtype = 'related'
                        for fkey, name, is_static in mimeimages:
                            if cache_mimeimages.has_key(fkey):
                                msgImage = cache_mimeimages[fkey]
                            else:
                                if is_static:
                                    f = open(
                                        path.join(
                                            settings.TWISTRANET_STATIC_PATH,
                                            fkey), 'rb')
                                else:
                                    f = open(
                                        path.join(settings.MEDIA_ROOT, fkey),
                                        'rb')
                                cache_mimeimages[fkey] = msgImage = MIMEImage(
                                    f.read())
                                f.close()
                            msgImage.add_header('Content-ID', '<%s>' % name)
                            msgImage.add_header('Content-Disposition',
                                                'inline')
                            msg.attach(msgImage)
                # just inline images
                else:
                    msg.attach_alternative(html_content, "text/html")
            # Send safely
            try:
                log.debug("Sending mail: '%s' from '%s' to '%s'" %
                          (subject, from_email, to))
                msg.send()
            except:
                log.warning("Unable to send message to %s" % to)
                log.exception("Here's what we've got as an error.")
Пример #8
0
    def get_query_set(self, __account__ = None, request = None, ):
        """
        Return a queryset of 100%-authorized objects. All (should) have the can_list perm to True.
        This is in fact a kind of 'has_permission(can_list)' method!
        
        This method IS very slow. But you can speed things up if you pass either 'request' or '__account__' along the lines.
        Be aware, however, that in this case you loose the 'safety belt' provided by the security model.
        """
        # Check for anonymous query
        import community, account, community
        __account__ = self._getAuthenticatedAccount(__account__, request)
        base_query_set = super(TwistableManager, self).get_query_set()
            
        # System account: return all objects without asking any question. And with all permissions set.
        if __account__.id == account.SystemAccount.SYSTEMACCOUNT_ID:
            return base_query_set
            
        # XXX TODO: Make a special query for admin members? Or at least mgrs of the global community?
        # XXX Make this more efficient?
        # XXX Or, better, check if current user is manager of the owner ?
        if __account__.id > 0:
            managed_accounts = [__account__.id, ]
        else:
            managed_accounts = []
        
        # XXX This try/except is there so that things don't get stucked during boostrap
        try:
            if __account__.is_admin:
                return base_query_set.filter(
                    _p_can_list__lte = roles.manager,
                )
        except DatabaseError:
            log.warning("DB error while checking AdminCommunity. This is NORMAL during syncdb or bootstrap.")
            return base_query_set

        # Regular check. Works for anonymous as well...
        # network_ids = __account__.network_ids
        if not __account__.is_anonymous:
            qs = base_query_set.filter(
                Q(
                    owner__id = __account__.id,
                    _p_can_list = roles.owner,
                ) | Q(
                    owner__id = __account__.id,
                    _p_can_list = roles.manager,
                ) | Q(
                    _access_network__targeted_network__target = __account__,
                    _p_can_list = roles.network,
                ) | Q(
                    _access_network__targeted_network__target = __account__,
                    _p_can_list = roles.public,
                ) | Q(
                    # Anonymous stuff
                    _access_network__isnull = True,
                    _p_can_list = roles.public,
                )
            )
        else:
            # Anon query. Easy: We just return public stuff.
            # Warning: nested query is surely inefficient...
            free_access_network = Twistable.objects.__booster__.filter(
                _access_network__isnull = True,
                _p_can_list = roles.public,
            )
            qs = base_query_set.filter(
                Q(
                    # Strictly anonymous stuff
                    _access_network__isnull = True,
                    _p_can_list = roles.public,
                ) | Q(
                    # Incidently anonymous stuff (public stuff published by an anon account)
                    _access_network__isnull = False,
                    _access_network__id__in = free_access_network,
                    _p_can_list = roles.public,
                )
            )
        return qs
Пример #9
0
    def save(self, *args, **kw):
        """
        Set various object attributes
        """
        import account
        import community
        
        auth = Twistable.objects._getAuthenticatedAccount()

        # Check if we're saving a real object and not a generic Content one (which is prohibited).
        # This must be a programming error, then.
        if self.__class__.__name__ == Twistable.__name__:
            raise ValidationError("You cannot save a raw content object. Use a derived class instead.")
            
        # Set information used to retreive the actual subobject
        self.model_name = self._meta.object_name
        self.app_label = self._meta.app_label

        # Set owner, publisher upon object creation. Publisher is NEVER set as None by default.
        if self.id is None:
            # If self.owner is already set, ensure it's done by SystemAccount
            if self.owner_id:
                if not isinstance(auth, account.SystemAccount):
                    raise PermissionDenied("You're not allowed to set the content owner by yourself.")
            else:
                self.owner = self.getDefaultOwner()
            if not self.publisher_id:
                self.publisher = self.getDefaultPublisher()
            else:
                if not self.publisher.can_publish:
                    raise PermissionDenied("You're not allowed to publish on %s" % self.publisher)
        else:
            # XXX TODO: Check that nobody sets /unsets the owner or the publisher of an object
            # raise PermissionDenied("You're not allowed to set the content owner by yourself.")
            if not self.can_edit:
                raise PermissionDenied("You're not allowed to edit this content.")
            
        # Set created_by and modified_by fields
        if self.id is None:
            self.created_by = auth
        self.modified_by = auth
            
        # Check if publisher is set. Only GlobalCommunity may have its publisher to None to make a site visible on the internet.
        if not self.publisher_id:
            if not self.__class__._ALLOW_NO_PUBLISHER:
                raise ValueError("Only the Global Community can have no publisher, not %s" % self)
    
        # Set permissions; we will apply them last to ensure we have an id.
        # We also ensure that the right permissions are set on the right object
        if not self.permissions:
            perm_template = self.model_class.permission_templates
            if not perm_template:
                raise ValueError("permission_templates not defined on class %s" % self.__class__.__name__)
            self.permissions = perm_template.get_default()
        tpl = [ t for t in self.permission_templates.permissions() if t["id"] == self.permissions ]
        if not tpl:
            # Didn't find? We restore default setting. XXX Should log/alert something here!
            tpl = [ t for t in self.permission_templates.permissions() if t["id"] == self.model_class.permission_templates.get_default() ]
            log.warning("Restoring default permissions. Problem here.")
            log.warning("Unable to find %s permission template %s in %s" % (self, self.permissions, self.permission_templates.perm_dict))
        if tpl[0].get("disabled_for_community") and issubclass(self.publisher.model_class, community.Community):
            raise ValueError("Invalid permission setting %s for this object (%s/%s)" % (tpl, self, self.title_or_description))
        elif tpl[0].get("disabled_for_useraccount") and issubclass(self.publisher.model_class, account.UserAccount):
            raise ValueError("Invalid permission setting %s for this object (%s/%s)" % (tpl, self, self.title_or_description))
        for perm, role in tpl[0].items():
            if perm.startswith("can_"):
                if callable(role):
                    role = role(self)
                setattr(self, "_p_%s" % perm, role)

        # Check if we're creating or not
        created = not self.id
                
        # Generate slug (or not !)
        if not self.slug and self.__class__._FORCE_SLUG_CREATION:
            if self.title:
                self.slug = slugify(self.title)
            elif self.description:
                self.slug = slugify(self.description)
            else:
                self.slug = slugify(self.model_name)
            self.slug = self.slug[:40]
        if created and self.__class__._FORCE_SLUG_CREATION:
            while Twistable.objects.__booster__.filter(slug = self.slug).exists():
                match = re.search("_(?P<num>[0-9]+)$", self.slug)
                if match:
                    root = self.slug[:match.start()]
                    num = int(match.groupdict()['num']) + 1
                else:
                    root = self.slug
                    num = 1
                self.slug = "%s_%i" % (root, num, )
        
        # Perform a full_clean on the model just to be sure it validates correctly
        self.full_clean()
            
        # Save and update access network information
        ret = super(Twistable, self).save(*args, **kw)
        self._update_access_network()

        # Send TN's post-save signal
        twistable_post_save.send(sender = self.__class__, instance = self, created = created)
        return ret
Пример #10
0
 def __call__(self, sender, **kwargs):
     """
     Generate the message itself.
     XXX TODO: Handle translation correctly (not from the request only)
     """
     # Fake-Login with SystemAccount so that everybody can be notified,
     # even users this current user can't list.
     from twistranet.twistapp.models import SystemAccount, Account, UserAccount, Community, Twistable
     __account__ = SystemAccount.get()
     from_email = settings.SERVER_EMAIL
     host = settings.EMAIL_HOST
     if not host:
         # If host is disabled (EMAIL_HOST is None), skip that
         return
     
     # Handle recipients emails
     recipients = kwargs.get(self.recipient_arg, None)
     if not recipients:
         raise ValueError("Recipient must be provided as a '%s' parameter" % self.recipient_arg)
     to_list = []
     if not isinstance(recipients, (list, tuple, QuerySet, )):
         recipients = (recipients, )
     for recipient in recipients:
         if isinstance(recipient, Twistable):
             recipient = recipient.object
         if isinstance(recipient, UserAccount):
             to = recipient.email
             if not to:
                 log.warning("Can't send email for '%s': %s doesn't have an email registered." % (sender, recipient, ))
                 return
             to_list.append(to)
         elif isinstance(recipient, Community):
             if self.managers_only:
                 members = recipient.managers
             else:
                 members = recipient.members
             # XXX Suboptimal for very large communities
             to_list.extend([ member.email for member in members if member.email ])
         elif type(recipient) in (str, unicode, ):
             to_list.append(recipient)        # XXX Todo: check the '@'
         else:
             raise ValueError("Invalid recipient: %s (%s)" % (recipient, type(recipient), ))
             
     # Fetch templates
     text_template = kwargs.get('text_template', self.text_template)
     html_template = kwargs.get('html_template', self.html_template)
             
     # Now generate template and send mail for each recipient
     # XXX TODO: See http://docs.djangoproject.com/en/1.2/topics/email/#sending-multiple-e-mails
     # for the good approach to use.
     for to in to_list:
         # Append domain (and site info) to kwargs
         d = kwargs.copy()
         domain = cache.get("twistranet_site_domain")
         d.update({
             "domain":       domain,
             "site_name":    utils.get_site_name(),
             "baseline":     utils.get_baseline(),
             "recipient":    to,     # A string
         })
     
         # Load both templates and render them with kwargs context
         text_tpl = get_template(text_template)
         c = Context(d)
         text_content = text_tpl.render(c).strip()
         if html_template:
             html_tpl = get_template(html_template)
             html_content = html_tpl.render(c)
         else:
             html_content = None
         
         # Fetch back subject from text template
         subject = self.subject
         if not subject:
             match = SUBJECT_REGEX.search(text_content)
             if match:
                 subject = match.groups()[0]
         if not subject:
             raise ValueError("No subject provided nor 'Subject:' first line in your text template")
         
         # Remove empty lines and "Subject:" line from text templates
         text_content = SUBJECT_REGEX.sub('', text_content)
         text_content = EMPTY_LINE_REGEX.sub('\n', text_content)
     
         # Prepare messages
         msg = EmailMultiAlternatives(subject, text_content, from_email, [ to ], )
         if html_content:
             msg.attach_alternative(html_content, "text/html")
         
         # Send safely
         try:
             log.debug("Sending mail: '%s' from '%s' to '%s'" % (subject, from_email, to))
             msg.send()
         except:
             log.warning("Unable to send message to %s" % to)
             log.exception("Here's what we've got as an error.")