def _copy_page(self, item):
     """ Create a draft copy of a published item to edit."""
     if not item.is_published:
         return None
     new_item = deepcopy(item)
     new_item.id = None
     new_item.status = get_unpublished_status_name()
     new_item.copy_of = item
     new_item.title += _(" (draft copy)")
     new_item.slug += "-draft-copy"
     # Position the new item as the next neighbor of
     # the original
     new_item.insert_at(item, position='right')
     new_item.save()
     for obj in introspect.get_referencing_objects(new_item):
         if not obj.__class__ is new_item.__class__:
             copy_method = self._get_copy_method_name(obj)
             obj_copy = getattr(self, copy_method, self._copy_object)(obj)
             for data in introspect.get_referencing_models(
                 new_item.__class__
             ):
                 if data['model'] is obj_copy.__class__:
                     for m2m_field_name in data['m2m_field_names']:
                         getattr(obj_copy, m2m_field_name).add(new_item)
                     for field_name in data['field_names']:
                         setattr(obj_copy, field_name, new_item)
             obj_copy.save()
     return new_item
Beispiel #2
0
 def _merge_item(self, original, draft_copy):
     """ Delete original, clean up and publish copy."""
     # Handle FK and M2M references.
     refs = filter(
         lambda obj: obj != draft_copy,
         get_referencing_objects(original)
     )
     for ref in refs:
         field_names = lookup_referencing_object_relationships(original, ref)
         for field_name in field_names:
             fld_class = ref._meta.get_field(field_name).__class__
             if issubclass(fld_class, models.fields.related.ManyToManyField):
                 getattr(ref, field_name).remove(original)
                 getattr(ref, field_name).add(draft_copy)
             else:
                 setattr(ref, field_name, draft_copy)
                 ref.save()
     # Handle generic references.
     for ref in get_generic_referencing_objects(original):
         generic_fk_field = [f for f in ref._meta.virtual_fields \
             if isinstance(f, generic.GenericForeignKey)
         ][0].name
         setattr(ref, generic_fk_field, draft_copy)
         ref.save()
     # Overwrite the old object.
     if self.slug:
         setattr(original, self.slug_field, original.slug + "-merge")
     original.save()
     if self.slug:
         import re
         slug = re.sub(
             "-draft-copy$", "", getattr(draft_copy, self.slug_field)
         )
         setattr(draft_copy, self.slug_field, slug)
     draft_copy.copy_of = None
     draft_copy.save()
     original.delete()
     draft_copy.publish()
     return draft_copy
    def merge_view(self, request, object_id, extra_context=None):
        """
        The 'merge' admin view for this model. Allows a user to merge a draft
        copy back over the original.
        """
        opts = self.model._meta
        app_label = opts.app_label

        obj = self.get_object(request, unquote(object_id))

        # For our purposes, permission to merge is equivalent to
        # has_change_permisison and has_delete_permission.
        if not self.has_change_permission(request, obj) \
            or not self.has_delete_permission(request, obj):
            raise PermissionDenied

        if obj is None:
            raise Http404(_(
                '%(name)s object with primary key %(key)r does not exist.') %
                {
                    'name': force_unicode(opts.verbose_name),
                    'key': escape(object_id)
                }
            )

        if not obj.is_draft_copy:
            return HttpResponseBadRequest(_(
                'The %s object could not be merged because it is not a'
                'draft copy. There is nothing to merge it into.'
            ) % force_unicode(opts.verbose_name))

        # Populate deleted_objects, a data structure of all related objects
        # that will also be deleted when this copy is deleted.
        all_objects = introspect.get_referencing_objects(obj.copy_of)
        all_objects.insert(0, obj.copy_of)
        using = router.db_for_write(self.model)
        (deleted_objects, perms_needed, protected) = get_deleted_objects(
            all_objects, opts, request.user, self.admin_site, using
        )
        # Flatten nested list:
        deleted_objects = map(
            lambda i: hasattr(i, '__iter__') and i or [i],
            deleted_objects
        )
        deleted_objects = chain(*deleted_objects)
        deleted_objects = list(deleted_objects)
        # ``get_deleted_objects`` is zealous and will add the draft copy to
        # the list of things to be deleted. This needs to be removed.
        obj_url = reverse("admin:pagemanager_page_change", args=(obj.pk,))
        deleted_objects = filter(
            lambda link: obj_url not in link,
            deleted_objects
        )
        # Filter out child pages: these will be preserved too.
        for child in obj.copy_of.children.all():
            child_url = reverse("admin:pagemanager_page_change", args=(child.pk,))
            deleted_objects = filter(
                lambda link: child_url not in link,
                deleted_objects
            )
        # Populate replacing_objects, a data structure of all related objects
        # that will be replacing the originals.
        replacing_objects = introspect.get_referencing_objects(obj)
        replacing_objects.insert(0, obj)
        (replacing_objects, perms_needed, protected) = get_deleted_objects(
            replacing_objects, opts, request.user, self.admin_site, using
        )
        # Flatten nested list:
        replacing_objects = map(
            lambda i: hasattr(i, '__iter__') and i or [i],
            replacing_objects
        )
        replacing_objects = chain(*replacing_objects)
        replacing_objects = list(replacing_objects)

        if request.POST:  # The user has already confirmed the merge.
            if perms_needed:
                raise PermissionDenied
            obj_display = force_unicode(obj) + " merged."
            self.log_change(request, obj, obj_display)

            original = obj.copy_of
            original_pk = original.pk
            original_layout_pk = original.page_layout.pk
            self._merge_item(original, obj)
            # Look up admin log entries for the old object and reassign them
            # to the new object.
            page_ctype = ContentType.objects.get_for_model(original)
            layout_ctype = ContentType.objects.get_for_model(original.page_layout)
            LogEntry.objects.filter(
                content_type=page_ctype,
                object_id=original_pk
            ).update(object_id=obj.pk)
            LogEntry.objects.filter(
                content_type=layout_ctype,
                object_id=original_layout_pk
            ).update(object_id=obj.page_layout.pk)
            self.message_user(
                request,
                _('The %(name)s "%(obj)s" was merged successfully.') % {
                    'name': force_unicode(opts.verbose_name),
                    'obj': force_unicode(obj_display)
                }
            )
            redirect_url = reverse("admin:pagemanager_page_change",
                args=(original.pk,)
            )
            return HttpResponseRedirect(redirect_url)

        context = {
            "title": _("Are you sure?"),
            "object_name": force_unicode(opts.verbose_name),
            "object": obj,
            "escaped_original": force_unicode(obj.copy_of),
            "deleted_objects": deleted_objects,
            "replacing_objects": replacing_objects,
            "perms_lacking": perms_needed,
            "opts": opts,
            "root_path": self.admin_site.root_path,
            "app_label": app_label,
        }
        context.update(extra_context or {})
        context_instance = template.RequestContext(
            request,
            current_app=self.admin_site.name
        )
        return render_to_response(self.merge_form_template, context,
            context_instance=context_instance
        )
Beispiel #4
0
    def merge_view(self, request, object_id, extra_context=None):
        """
        The 'merge' admin view for this model. Allows a user to merge a copy 
        back over the original.
        """
        opts = self.model._meta
        app_label = opts.app_label

        if not self.draft_copy_allowed:
            return HttpResponseBadRequest("Draft copy not allowed for %s." % 
                force_unicode(opts.verbose_name_plural)
            )

        obj = self.get_object(request, unquote(object_id))

        # For our purposes, permission to merge is equivalent to 
        # has_change_permisison and has_delete_permission.
        if not self.has_change_permission(request, obj) \
            or not self.has_delete_permission(request, obj) :
            raise PermissionDenied
        if obj is None:
            raise Http404(_(
                '%(name)s object with primary key %(key)r does not exist.') %   
                {
                    'name': force_unicode(opts.verbose_name), 
                    'key': escape(object_id)
                }
            )
        if not obj.is_draft_copy():
            return HttpResponseBadRequest(_(
                'The %s object could not be merged because it is not a '
                'draft copy. There is nothing to merge it into.'
            ) % force_unicode(opts.verbose_name))

        # Populate deleted_objects, a data structure of all related objects
        # that will also be deleted when this copy is deleted.
        generic_refs = get_generic_referencing_objects(obj.copy_of)
        direct_refs = get_referencing_objects(obj.copy_of)
        direct_refs = filter(lambda o: obj != o, direct_refs)
        object_refs = [(unicode(o), o._meta.verbose_name) for o in \
            chain(direct_refs, generic_refs)
        ]
        perms_needed = False
        if request.POST: # The user has already confirmed the merge.
            if perms_needed:
                raise PermissionDenied            
            original = obj.copy_of
            original_pk = original.pk
            self._merge_item(original, obj)
            # Look up admin log entries for the old object and reassign them
            # to the new object.
            ctype = ContentType.objects.get_for_model(original)
            LogEntry.objects.filter(
                content_type=ctype,
                object_id=original_pk
            ).update(object_id=obj.pk)
            message = 'Merged %s "%s".' % (
                force_unicode(obj._meta.verbose_name),
                force_unicode(obj)
            )
            self.log_change(request, obj, message)
            self.message_user(
                request, 
                _('The %(name)s "%(obj)s" was merged successfully.') % {
                    'name': force_unicode(opts.verbose_name), 
                    'obj': force_unicode(obj)
                }
            )

            return HttpResponseRedirect("../../")

        context = {
            "title": _("Are you sure?"),
            "object_name": force_unicode(opts.verbose_name),
            "object": obj,
            "escaped_original": force_unicode(obj.copy_of), 
            "referencing_objects": object_refs,
            "opts": opts,
            "root_path": self.admin_site.root_path,
            "app_label": app_label,
        }
        context.update(extra_context or {})
        context_instance = template.RequestContext(
            request, 
            current_app=self.admin_site.name
        )
        return render_to_response(self.merge_form_template, context, 
            context_instance=context_instance
        )
Beispiel #5
0
    def copy_view(self, request, object_id, extra_context=None):
        """
        Create a draft copy of the item after user has confirmed. 
        """
        opts = self.model._meta
        app_label = opts.app_label

        if not self.draft_copy_allowed:
            return HttpResponseBadRequest("Draft copy not allowed for %s." % 
                force_unicode(opts.verbose_name_plural)
            )

        obj = self.get_object(request, unquote(object_id))
        object_refs = None

        # For our purposes, permission to copy is equivalent to 
        # has_add_permisison.
        if not self.has_add_permission(request):
            raise PermissionDenied

        if obj is None:
            raise Http404(_(
                '%(name)s object with primary key %(key)r does not exist.') %   
                {
                    'name': force_unicode(opts.verbose_name), 
                    'key': escape(object_id)
                }
            )
    
        if request.POST: # The user has already confirmed the copy.
            if obj.is_draft_copy():
                self.message_user(
                    request, 
                    _('You cannot copy a draft copy.')
                )            
                return HttpResponseRedirect(request.path)
            if obj.get_draft_copy():
                self.message_user(
                    request, 
                    _('A draft copy already exists.')
                )
                return HttpResponseRedirect(request.path)
            copy = self._copy_item(obj)
            original_message = 'Created a draft copy for %s "%s".' % (
                force_unicode(obj._meta.verbose_name),
                force_unicode(obj)
            )
            copy_message = 'Copied from %s "%s".' % (
                force_unicode(obj._meta.verbose_name),
                force_unicode(obj)
            )
            self.log_change(request, obj, original_message)
            self.log_change(request, copy, copy_message)            
            self.message_user(
                request, 
                _('The %(name)s "%(obj)s" was copied successfully.') % {
                    'name': force_unicode(opts.verbose_name), 
                    'obj': force_unicode(obj)
                }
            )
            
            url = reverse(
                "admin:%s_%s_change" % (
                    app_label, 
                    self.model._meta.module_name
                ),
                args=(copy.id,)
            )
            return HttpResponseRedirect(url)

        if self.model.objects.filter(copy_of=obj).exists():
            draft_already_exists = True
            title = _("Draft Copy Exists")
            edit_copy_url = reverse(
                "admin:%s_%s_change" % (
                    app_label, 
                    self.model._meta.module_name
                ),
                args=(self.model.objects.filter(copy_of=obj)[0].id,)
            )
            
        else:
            draft_already_exists = False
            title = _("Are you sure?")
            edit_copy_url = None
            generic_refs = get_generic_referencing_objects(obj)
            direct_refs = get_referencing_objects(obj)
            object_refs = [(unicode(o), o._meta.verbose_name) for o in \
                chain(direct_refs, generic_refs)
            ]
            
        context = {
            "title": title,
            "object_name": force_unicode(opts.verbose_name),
            "object": obj,
            "referencing_objects": object_refs,
            "opts": opts,
            "root_path": self.admin_site.root_path,
            "app_label": app_label,
            'draft_already_exists': draft_already_exists,
            'edit_copy_url': edit_copy_url
        }
        context.update(extra_context or {})
        context_instance = template.RequestContext(
            request, 
            current_app=self.admin_site.name
        )
        return render_to_response(self.copy_form_template, context, 
            context_instance=context_instance
        )    
Beispiel #6
0
    def merge_view(self, request, object_id, extra_context=None):
        """
        The 'merge' admin view for this model. Allows a user to merge a draft
        copy back over the original.
        """
        opts = self.model._meta
        app_label = opts.app_label

        obj = self.get_object(request, unquote(object_id))

        # For our purposes, permission to merge is equivalent to
        # has_change_permisison and has_delete_permission.
        if not self.has_change_permission(request, obj) \
            or not self.has_delete_permission(request, obj):
            raise PermissionDenied

        if obj is None:
            raise Http404(
                _('%(name)s object with primary key %(key)r does not exist.') %
                {
                    'name': force_unicode(opts.verbose_name),
                    'key': escape(object_id)
                })

        if not obj.is_draft_copy:
            return HttpResponseBadRequest(
                _('The %s object could not be merged because it is not a'
                  'draft copy. There is nothing to merge it into.') %
                force_unicode(opts.verbose_name))

        # Populate deleted_objects, a data structure of all related objects
        # that will also be deleted when this copy is deleted.
        all_objects = introspect.get_referencing_objects(obj.copy_of)
        all_objects.insert(0, obj.copy_of)
        using = router.db_for_write(self.model)
        (deleted_objects, perms_needed,
         protected) = get_deleted_objects(all_objects, opts, request.user,
                                          self.admin_site, using)
        # Flatten nested list:
        deleted_objects = map(lambda i: hasattr(i, '__iter__') and i or [i],
                              deleted_objects)
        deleted_objects = chain(*deleted_objects)
        deleted_objects = list(deleted_objects)
        # ``get_deleted_objects`` is zealous and will add the draft copy to
        # the list of things to be deleted. This needs to be removed.
        obj_url = reverse("admin:pagemanager_page_change", args=(obj.pk, ))
        deleted_objects = filter(lambda link: obj_url not in link,
                                 deleted_objects)
        # Filter out child pages: these will be preserved too.
        for child in obj.copy_of.children.all():
            child_url = reverse("admin:pagemanager_page_change",
                                args=(child.pk, ))
            deleted_objects = filter(lambda link: child_url not in link,
                                     deleted_objects)
        # Populate replacing_objects, a data structure of all related objects
        # that will be replacing the originals.
        replacing_objects = introspect.get_referencing_objects(obj)
        replacing_objects.insert(0, obj)
        (replacing_objects, perms_needed,
         protected) = get_deleted_objects(replacing_objects, opts,
                                          request.user, self.admin_site, using)
        # Flatten nested list:
        replacing_objects = map(lambda i: hasattr(i, '__iter__') and i or [i],
                                replacing_objects)
        replacing_objects = chain(*replacing_objects)
        replacing_objects = list(replacing_objects)

        if request.POST:  # The user has already confirmed the merge.
            if perms_needed:
                raise PermissionDenied
            obj_display = force_unicode(obj) + " merged."
            self.log_change(request, obj, obj_display)

            original = obj.copy_of
            original_pk = original.pk
            original_layout_pk = original.page_layout.pk
            self._merge_item(original, obj)
            # Look up admin log entries for the old object and reassign them
            # to the new object.
            page_ctype = ContentType.objects.get_for_model(original)
            layout_ctype = ContentType.objects.get_for_model(
                original.page_layout)
            LogEntry.objects.filter(
                content_type=page_ctype,
                object_id=original_pk).update(object_id=obj.pk)
            LogEntry.objects.filter(content_type=layout_ctype,
                                    object_id=original_layout_pk).update(
                                        object_id=obj.page_layout.pk)
            self.message_user(
                request,
                _('The %(name)s "%(obj)s" was merged successfully.') % {
                    'name': force_unicode(opts.verbose_name),
                    'obj': force_unicode(obj_display)
                })
            redirect_url = reverse("admin:pagemanager_page_change",
                                   args=(original.pk, ))
            return HttpResponseRedirect(redirect_url)

        context = {
            "title": _("Are you sure?"),
            "object_name": force_unicode(opts.verbose_name),
            "object": obj,
            "escaped_original": force_unicode(obj.copy_of),
            "deleted_objects": deleted_objects,
            "replacing_objects": replacing_objects,
            "perms_lacking": perms_needed,
            "opts": opts,
            "root_path": self.admin_site.root_path,
            "app_label": app_label,
        }
        context.update(extra_context or {})
        context_instance = template.RequestContext(
            request, current_app=self.admin_site.name)
        return render_to_response(self.merge_form_template,
                                  context,
                                  context_instance=context_instance)