Ejemplo n.º 1
0
    def _copy_page(self,
                   page,
                   to=None,
                   update_attrs=None,
                   exclude_fields=None,
                   _mpnode_attrs=None):
        specific_page = page.specific
        exclude_fields = (specific_page.default_exclude_fields_in_copy +
                          specific_page.exclude_fields_in_copy +
                          (exclude_fields or []))
        if self.keep_live:
            base_update_attrs = {
                "alias_of": None,
            }
        else:
            base_update_attrs = {
                "live": False,
                "has_unpublished_changes": True,
                "live_revision": None,
                "first_published_at": None,
                "last_published_at": None,
                "alias_of": None,
            }

        if self.user:
            base_update_attrs["owner"] = self.user

        # When we're not copying for translation, we should give the translation_key a new value
        if self.reset_translation_key:
            base_update_attrs["translation_key"] = uuid.uuid4()

        if update_attrs:
            base_update_attrs.update(update_attrs)

        page_copy, child_object_map = _copy(specific_page,
                                            exclude_fields=exclude_fields,
                                            update_attrs=base_update_attrs)
        # Save copied child objects and run process_child_object on them if we need to
        for (child_relation, old_pk), child_object in child_object_map.items():

            if self.process_child_object:
                self.process_child_object(specific_page, page_copy,
                                          child_relation, child_object)

            if self.reset_translation_key and isinstance(
                    child_object, TranslatableMixin):
                child_object.translation_key = self.generate_translation_key(
                    child_object.translation_key)

        # Save the new page
        if _mpnode_attrs:
            # We've got a tree position already reserved. Perform a quick save
            page_copy.path = _mpnode_attrs[0]
            page_copy.depth = _mpnode_attrs[1]
            page_copy.save(clean=False)

        else:
            if to:
                page_copy = to.add_child(instance=page_copy)
            else:
                page_copy = page.add_sibling(instance=page_copy)

            _mpnode_attrs = (page_copy.path, page_copy.depth)

        _copy_m2m_relations(
            specific_page,
            page_copy,
            exclude_fields=exclude_fields,
            update_attrs=base_update_attrs,
        )

        # Copy revisions
        if self.copy_revisions:
            for revision in page.revisions.all():
                revision.pk = None
                revision.submitted_for_moderation = False
                revision.approved_go_live_at = None
                revision.page = page_copy

                # Update ID fields in content
                revision_content = revision.content
                revision_content["pk"] = page_copy.pk

                for child_relation in get_all_child_relations(specific_page):
                    accessor_name = child_relation.get_accessor_name()
                    try:
                        child_objects = revision_content[accessor_name]
                    except KeyError:
                        # KeyErrors are possible if the revision was created
                        # before this child relation was added to the database
                        continue

                    for child_object in child_objects:
                        child_object[child_relation.field.name] = page_copy.pk
                        # Remap primary key to copied versions
                        # If the primary key is not recognised (eg, the child object has been deleted from the database)
                        # set the primary key to None
                        copied_child_object = child_object_map.get(
                            (child_relation, child_object["pk"]))
                        child_object["pk"] = (copied_child_object.pk
                                              if copied_child_object else None)
                        if (self.reset_translation_key
                                and "translation_key" in child_object):
                            child_object[
                                "translation_key"] = self.generate_translation_key(
                                    child_object["translation_key"])

                revision.content = revision_content

                # Save
                revision.save()

        # Create a new revision
        # This code serves a few purposes:
        # * It makes sure update_attrs gets applied to the latest revision
        # * It bumps the last_revision_created_at value so the new page gets ordered as if it was just created
        # * It sets the user of the new revision so it's possible to see who copied the page by looking at its history
        latest_revision = page_copy.get_latest_revision_as_page()

        if update_attrs:
            for field, value in update_attrs.items():
                setattr(latest_revision, field, value)

        latest_revision_as_page_revision = latest_revision.save_revision(
            user=self.user, changed=False, clean=False)
        if self.keep_live:
            page_copy.live_revision = latest_revision_as_page_revision
            page_copy.last_published_at = latest_revision_as_page_revision.created_at
            page_copy.first_published_at = latest_revision_as_page_revision.created_at
            page_copy.save(clean=False)

        if page_copy.live:
            page_published.send(
                sender=page_copy.specific_class,
                instance=page_copy,
                revision=latest_revision_as_page_revision,
            )

        # Log
        if self.log_action:
            parent = specific_page.get_parent()
            log(
                instance=page_copy,
                action=self.log_action,
                user=self.user,
                data={
                    "page": {
                        "id": page_copy.id,
                        "title": page_copy.get_admin_display_title(),
                        "locale": {
                            "id": page_copy.locale_id,
                            "language_code": page_copy.locale.language_code,
                        },
                    },
                    "source": {
                        "id":
                        parent.id,
                        "title":
                        parent.specific_deferred.get_admin_display_title(),
                    } if parent else None,
                    "destination": {
                        "id": to.id,
                        "title":
                        to.specific_deferred.get_admin_display_title(),
                    } if to else None,
                    "keep_live": page_copy.live and self.keep_live,
                    "source_locale": {
                        "id": page.locale_id,
                        "language_code": page.locale.language_code,
                    },
                },
            )
            if page_copy.live and self.keep_live:
                # Log the publish if the use chose to keep the copied page live
                log(
                    instance=page_copy,
                    action="wagtail.publish",
                    user=self.user,
                    revision=latest_revision_as_page_revision,
                )
        logger.info('Page copied: "%s" id=%d from=%d', page_copy.title,
                    page_copy.id, page.id)

        # Copy child pages
        from wagtail.models import Page

        if self.recursive:
            numchild = 0

            for child_page in page.get_children().specific():
                newdepth = _mpnode_attrs[1] + 1
                child_mpnode_attrs = (
                    Page._get_path(_mpnode_attrs[0], newdepth, numchild),
                    newdepth,
                )
                numchild += 1
                self._copy_page(child_page,
                                to=page_copy,
                                _mpnode_attrs=child_mpnode_attrs)

            if numchild > 0:
                page_copy.numchild = numchild
                page_copy.save(clean=False, update_fields=["numchild"])

        return page_copy
Ejemplo n.º 2
0
    def _create_alias(
        self,
        page,
        *,
        recursive,
        parent,
        update_slug,
        update_locale,
        user,
        log_action,
        reset_translation_key,
        _mpnode_attrs,
    ):

        specific_page = page.specific

        # FIXME: Switch to the same fields that are excluded from copy
        # We can't do this right now because we can't exclude fields from with_content_json
        # which we use for updating aliases
        exclude_fields = [
            "id",
            "path",
            "depth",
            "numchild",
            "url_path",
            "path",
            "index_entries",
            "postgres_index_entries",
        ]

        update_attrs = {
            "alias_of": page,
            # Aliases don't have revisions so the draft title should always match the live title
            "draft_title": page.title,
            # Likewise, an alias page can't have unpublished changes if it's live
            "has_unpublished_changes": not page.live,
        }

        if update_slug:
            update_attrs["slug"] = update_slug

        if update_locale:
            update_attrs["locale"] = update_locale

        if user:
            update_attrs["owner"] = user

        # When we're not copying for translation, we should give the translation_key a new value
        if reset_translation_key:
            update_attrs["translation_key"] = uuid.uuid4()

        alias, child_object_map = _copy(specific_page,
                                        update_attrs=update_attrs,
                                        exclude_fields=exclude_fields)

        # Update any translatable child objects
        for (child_relation, old_pk), child_object in child_object_map.items():
            if isinstance(child_object, TranslatableMixin):
                if update_locale:
                    child_object.locale = update_locale

                # When we're not copying for translation,
                # we should give the translation_key a new value for each child object as well.
                if reset_translation_key:
                    child_object.translation_key = uuid.uuid4()

        # Save the new page
        if _mpnode_attrs:
            # We've got a tree position already reserved. Perform a quick save.
            alias.path = _mpnode_attrs[0]
            alias.depth = _mpnode_attrs[1]
            alias.save(clean=False)

        else:
            if parent:
                alias = parent.add_child(instance=alias)
            else:
                alias = page.add_sibling(instance=alias)

            _mpnode_attrs = (alias.path, alias.depth)

        _copy_m2m_relations(specific_page,
                            alias,
                            exclude_fields=exclude_fields)

        # Log
        if log_action:
            source_parent = specific_page.get_parent()
            log(
                instance=alias,
                action=log_action,
                user=user,
                data={
                    "page": {
                        "id": alias.id,
                        "title": alias.get_admin_display_title()
                    },
                    "source": {
                        "id":
                        source_parent.id,
                        "title":
                        source_parent.specific_deferred.
                        get_admin_display_title(),
                    } if source_parent else None,
                    "destination": {
                        "id":
                        parent.id,
                        "title":
                        parent.specific_deferred.get_admin_display_title(),
                    } if parent else None,
                },
            )
            if alias.live:
                # Log the publish
                log(
                    instance=alias,
                    action="wagtail.publish",
                    user=user,
                )

        logger.info('Page alias created: "%s" id=%d from=%d', alias.title,
                    alias.id, page.id)

        # Copy child pages
        if recursive:
            from wagtail.models import Page

            numchild = 0

            for child_page in page.get_children().specific().iterator():
                newdepth = _mpnode_attrs[1] + 1
                child_mpnode_attrs = (
                    Page._get_path(_mpnode_attrs[0], newdepth, numchild),
                    newdepth,
                )
                numchild += 1
                self._create_alias(
                    child_page,
                    recursive=True,
                    parent=alias,
                    update_slug=None,
                    update_locale=update_locale,
                    user=user,
                    log_action=log_action,
                    reset_translation_key=reset_translation_key,
                    _mpnode_attrs=child_mpnode_attrs,
                )

            if numchild > 0:
                alias.numchild = numchild
                alias.save(clean=False, update_fields=["numchild"])

        return alias