def process_redirects():
    # We create the Redirects here.  We don't try and port over the
    # version information for the formerly-page-text-based redirects.
    global redirects

    from pages.models import Page, slugify
    from redirects.models import Redirect

    u = get_robot_user()

    for from_pagename, to_pagename in redirects:
        try:
            to_page = Page.objects.get(slug=slugify(to_pagename))
        except Page.DoesNotExist:
            print "Error creating redirect: %s --> %s" % (
                from_pagename, to_pagename)
            print "  (page %s does not exist)" % to_pagename
            continue

        if slugify(from_pagename) == to_page.slug:
            continue
        if not Redirect.objects.filter(source=slugify(from_pagename)):
            r = Redirect(source=slugify(from_pagename), destination=to_page)
            r.save(user=u, comment="Automated edit. Creating redirect.")
            print "Redirect %s --> %s created" % (from_pagename, to_pagename)
def import_redirect(from_pagename):
    # We create the Redirects here.  We don't try and port over the
    # version information for the formerly-page-text-based redirects.
    to_pagename = parse_redirect(from_pagename)
    if to_pagename is None:
        print "Error creating redirect: %s has no link" % from_pagename
        return
    to_pagename = fix_pagename(to_pagename)

    from pages.models import Page, slugify
    from redirects.models import Redirect

    u = get_robot_user()

    try:
        to_page = Page.objects.get(slug=slugify(to_pagename))
    except Page.DoesNotExist:
        print "Error creating redirect: %s --> %s" % (
            from_pagename.encode('utf-8'), to_pagename.encode('utf-8'))
        print "  (page %s does not exist)" % to_pagename.encode('utf-8')
        return

    if slugify(from_pagename) == to_page.slug:
        return
    if not Redirect.objects.filter(source=slugify(from_pagename)):
        r = Redirect(source=slugify(from_pagename), destination=to_page)
        try:
            r.save(user=u, comment="Automated edit. Creating redirect.")
        except IntegrityError:
            connection.close()
        print "Redirect %s --> %s created" % (from_pagename.encode('utf-8'), to_pagename.encode('utf-8'))
def process_redirects():
    # We create the Redirects here.  We don't try and port over the
    # version information for the formerly-page-text-based redirects.
    global redirects

    from pages.models import Page, slugify
    from redirects.models import Redirect

    u = get_robot_user()

    for from_pagename, to_pagename in redirects:
        try:
            to_page = Page.objects.get(slug=slugify(to_pagename))
        except Page.DoesNotExist:
            print "Error creating redirect: %s --> %s" % (from_pagename,
                                                          to_pagename)
            print "  (page %s does not exist)" % to_pagename
            continue

        if slugify(from_pagename) == to_page.slug:
            continue
        if not Redirect.objects.filter(source=slugify(from_pagename)):
            r = Redirect(source=slugify(from_pagename), destination=to_page)
            r.save(user=u, comment="Automated edit. Creating redirect.")
            print "Redirect %s --> %s created" % (from_pagename, to_pagename)
Exemple #4
0
    def test_move_redirects(self):
        p = Page(region=self.sf)
        p.content = "<p>A page.</p>"
        p.name = "Page content"
        p.save()

        redir = Redirect(
            source="short name",
            destination=p,
            region=self.sf)
        redir.save()

        move_to_region(self.oak, pages=[p], redirects=[redir])

        # Redirect should be in Oak
        self.assertEqual(Redirect.objects.filter(region=self.oak).count(), 1)
Exemple #5
0
    def test_move_with_fks(self):
        ###########################################################
        # Moving should carry along files and FK'ed items that
        # point to it.
        ###########################################################

        p = Page(region=self.sf)
        p.content = "<p>A page with files and a map in SF.</p>"
        p.name = "Page With FKs"
        p.save()
        # Create a file that points at the page.
        pf = PageFile(file=ContentFile("foo"), name="file.txt", slug=p.slug, region=self.sf)
        pf.save()
        # Create a redirect that points at the page.
        redirect = Redirect(source="foobar", destination=p, region=self.sf)
        redirect.save()
        # Create a map that points at the page.
        points = GEOSGeometry("""MULTIPOINT (-122.4378964233400069 37.7971758820830033, -122.3929211425700032 37.7688207875790027, -122.3908612060599950 37.7883584775320003, -122.4056240844700056 37.8013807351830025, -122.4148937988299934 37.8002956347170027, -122.4183270263600036 37.8051784612779969)""")
        map = MapData(points=points, page=p, region=self.sf)
        map.save()
        # Add tags to page
        tagset = PageTagSet(page=p, region=self.sf)
        tagset.save()
        tag = Tag(name="tag1", region=self.sf)
        tag.save()
        tagset.tags.add(tag)

        move_to_region(self.oak, pages=[p])

        # Check to see that the related objects were moved.
        p = Page.objects.get(name="Page With FKs", region=self.oak)
        self.assertEqual(len(MapData.objects.filter(page=p, region=self.oak)), 1)
        self.assertEqual(len(p.pagetagset.tags.all()), 1)
        self.assertEqual(len(Redirect.objects.filter(destination=p, region=self.oak)), 1)
        self.assertEqual(len(PageFile.objects.filter(slug=p.slug, region=self.oak)), 1)

        # Check to see that version history was moved as well
        self.assertEquals(p.versions.all().count(), 1)
        self.assertEqual(len(MapData.versions.filter(page=p, region=self.oak)), 1)
        for pts in p.pagetagset.tags.all():
            self.assertEqual(pts.versions.all().count(), 1)
        self.assertEqual(len(Redirect.versions.filter(destination=p, region=self.oak)), 1)
        self.assertEqual(len(PageFile.versions.filter(slug=p.slug, region=self.oak)), 1)

        # ..and that the page is no longer in the SF region
        self.assertFalse(Page.objects.filter(region=self.sf, name="Page With FKs").exists())
Exemple #6
0
    def test_move_redirect_destination(self):
        # Destination page should be transparently moved if not specified
        p = Page(region=self.sf)
        p.content = "<p>A page.</p>"
        p.name = "Page content not moved directly"
        p.save()

        redir = Redirect(
            source="short name",
            destination=p,
            region=self.sf)
        redir.save()

        move_to_region(self.oak, redirects=[redir])

        # Redirect should be in Oak
        self.assertEqual(Redirect.objects.filter(region=self.oak).count(), 1)
        # ..and page
        self.assertTrue(Page.objects.filter(slug='page content not moved directly', region=self.oak).exists())
Exemple #7
0
    def rename_to(self, pagename):
        """
        Renames the page to `pagename`.  Moves related objects around
        accordingly.
        """
        def _get_slug_lookup(unique_together, obj, new_p):
            d = {}
            for field in unique_together:
                d[field] = getattr(obj, field)
            d['slug'] = new_p.slug
            return d

        from redirects.models import Redirect
        from redirects.exceptions import RedirectToSelf

        if Page.objects.filter(slug=slugify(pagename)):
            if slugify(pagename) == self.slug:
                # The slug is the same but we're changing the name.
                old_name = self.name
                self.name = pagename
                self.save(comment='Renamed from "%s"' % old_name)
                return
            else:
                raise exceptions.PageExistsError(
                    "The page '%s' already exists!" % pagename)

        # Copy the current page into the new page, zeroing out the
        # primary key and setting a new name and slug.
        new_p = copy(self)
        new_p.pk = None
        new_p.name = pagename
        new_p.slug = slugify(pagename)
        new_p.save(comment='Renamed from "%s"' % self.name)

        # Get all related objects before the original page is deleted.
        related_objs = []
        for r in self._meta.get_all_related_objects():
            try:
                rel_obj = getattr(self, r.get_accessor_name())
            except:
                continue  # No object for this relation.

            # Is this a related /set/, e.g. redirect_set?
            if isinstance(rel_obj, models.Manager):
                # list() freezes the QuerySet, which we don't want to be
                # fetched /after/ we delete the page.
                related_objs.append(
                    (r.get_accessor_name(), list(rel_obj.all())))
            else:
                related_objs.append((r.get_accessor_name(), rel_obj))

        # Create a redirect from the starting pagename to the new pagename.
        redirect = Redirect(source=self.slug, destination=new_p)
        # Creating the redirect causes the starting page to be deleted.
        redirect.save()

        # Point each related object to the new page and save the object with a
        # 'was renamed' comment.
        for attname, rel_obj in related_objs:
            if isinstance(rel_obj, list):
                for obj in rel_obj:
                    obj.pk = None  # Reset the primary key before saving.
                    try:
                        getattr(new_p, attname).add(obj)
                        obj.save(comment="Parent page renamed")
                    except RedirectToSelf, s:
                        # We don't want to create a redirect to ourself.
                        # This happens during a rename -> rename-back
                        # cycle.
                        continue
            else:
                # This is an easy way to set obj to point to new_p.
                setattr(new_p, attname, rel_obj)
                rel_obj.pk = None  # Reset the primary key before saving.
                rel_obj.save(comment="Parent page renamed")
Exemple #8
0
    def test_page_rename(self):
        p = Page()
        p.content = "<p>The page content.</p>"
        p.name = "Original page"
        p.save()

        p.rename_to("New page")

        # Renamed-to page should exist.
        new_p = Page.objects.get(name="New page")
        # new_p should have the same content.
        self.assertEqual(new_p.content, p.content)

        # "Original page" should no longer exist.
        pgs = Page.objects.filter(name="Original page")
        self.assertEqual(len(pgs), 0)
        # and a redirect from "original page" to "New page" should exist.
        Redirect.objects.filter(source="original page", destination=new_p)

        ###########################################################
        # Renaming to a page that already exists should raise an
        # exception and not affect the original page.
        ###########################################################
        p = Page()
        p.content = "<p>Hello, world.</p>"
        p.name = "Page A"
        p.save()

        self.assertRaises(exceptions.PageExistsError, p.rename_to, "New page")
        # p should be unaffected.  No redirect should be created.
        p = Page.objects.get(name="Page A")
        self.assertEqual(p.content, "<p>Hello, world.</p>")
        self.assertEqual(len(Redirect.objects.filter(source="page a")), 0)

        ###########################################################
        # Renaming should carry along files and FK'ed items that
        # point to it.
        ###########################################################
        p = Page()
        p.content = "<p>A page with files and a map.</p>"
        p.name = "Page With FKs"
        p.save()
        # Create a file that points at the page.
        pf = PageFile(file=ContentFile("foo"), name="file.txt", slug=p.slug)
        pf.save()
        # Create a redirect that points at the page.
        redirect = Redirect(source="foobar", destination=p)
        redirect.save()
        # Create a map that points at the page.
        points = GEOSGeometry("""MULTIPOINT (-122.4378964233400069 37.7971758820830033, -122.3929211425700032 37.7688207875790027, -122.3908612060599950 37.7883584775320003, -122.4056240844700056 37.8013807351830025, -122.4148937988299934 37.8002956347170027, -122.4183270263600036 37.8051784612779969)""")
        map = MapData(points=points, page=p)
        map.save()

        p.rename_to("New Page With FKs")

        new_p = Page.objects.get(name="New Page With FKs")
        self.assertEqual(len(MapData.objects.filter(page=new_p)), 1)
        # Two redirects: one we created explicitly and one that was
        # created during rename_to()
        self.assertEqual(len(Redirect.objects.filter(destination=new_p)), 2)
        self.assertEqual(len(PageFile.objects.filter(slug=new_p.slug)), 1)

        # Renaming should keep slugs pointed at old page /and/ copy
        # them to the new page.
        self.assertEqual(len(PageFile.objects.filter(slug=p.slug)), 1)

        ###########################################################
        # Renaming with multiple files.
        ###########################################################
        p = Page()
        p.content = "<p>A new page with multiple files.</p>"
        p.name = "Page with multiple files"
        p.save()
        # Create a file that points at the page.
        pf = PageFile(file=ContentFile("foo"), name="file.txt", slug=p.slug)
        pf.save()
        pf = PageFile(file=ContentFile("foo2"), name="file2.txt", slug=p.slug)
        pf.save()
        pf = PageFile(file=ContentFile("foo3"), name="file3.txt", slug=p.slug)
        pf.save()
        p.rename_to("A page with multiple files 2")

        p = Page.objects.get(name="A page with multiple files 2")
        self.assertEqual(len(PageFile.objects.filter(slug=p.slug)), 3)

        ###########################################################
        # Reverting a renamed page should be possible and should
        # restore files and FK'ed items that were pointed at the
        # original page.  The renamed-to page should still exist
        # after the revert and should still have its own files and
        # FK'ed items pointed at it.
        ###########################################################
        p = Page(name="Page With FKs", slug="page with fks")
        # get the version right before it was deleted
        v_before_deleted = len(p.versions.all()) - 1
        p_h = p.versions.as_of(version=v_before_deleted)
        p_h.revert_to()
        p = Page.objects.get(name="Page With FKs")
        self.assertEqual(len(MapData.objects.filter(page=p)), 1)
        self.assertEqual(len(PageFile.objects.filter(slug=p.slug)), 1)

        p2 = Page.objects.get(name="New Page With FKs")
        self.assertEqual(len(MapData.objects.filter(page=p2)), 1)
        self.assertEqual(len(PageFile.objects.filter(slug=p2.slug)), 1)

        self.assertEqual(len(Redirect.objects.filter(destination=p2)), 1)

        ###########################################################
        # Renaming a page and then renaming it back.
        ###########################################################
        # 1. Simple case
        p = Page(name="Page X", content="<p>Foobar</p>")
        p.save()
        p.rename_to("Page Y")
        self.assertEqual(len(Page.objects.filter(name="Page X")), 0)
        self.assertEqual(len(Page.objects.filter(name="Page Y")), 1)

        p_new = Page.objects.get(name="Page Y")
        p_new.rename_to("Page X")
        self.assertEqual(len(Page.objects.filter(name="Page X")), 1)
        self.assertEqual(len(Page.objects.filter(name="Page Y")), 0)

        # 2. If we have FKs pointed at the page this shouldn't be
        # totally f****d.
        p = Page(name="Page X2", content="<p>Foo X</p>")
        p.save()
        points = GEOSGeometry("""MULTIPOINT (-122.4378964233400069 37.7971758820830033, -122.3929211425700032 37.7688207875790027, -122.3908612060599950 37.7883584775320003, -122.4056240844700056 37.8013807351830025, -122.4148937988299934 37.8002956347170027, -122.4183270263600036 37.8051784612779969)""")
        map = MapData(points=points, page=p)
        map.save()
        # Create a file that points at the page.
        pf = PageFile(file=ContentFile("foooo"), name="file_foo.txt", slug=p.slug)
        pf.save()

        p.rename_to("Page Y2")
        p_new = Page.objects.get(name="Page Y2")
        # FK points at the page we renamed to.
        self.assertEqual(len(MapData.objects.filter(page=p_new)), 1)
        self.assertEqual(len(PageFile.objects.filter(slug=p_new.slug)), 1)

        # Now rename it back.
        p_new.rename_to("Page X2")
        p = Page.objects.get(name="Page X2")
        # After rename-back-to, FK points to the renamed-back-to page.
        self.assertEqual(len(MapData.objects.filter(page=p)), 1)
        self.assertEqual(len(PageFile.objects.filter(slug=p.slug)), 1)

        ###########################################################
        # Renaming a page but keeping the same slug
        ###########################################################
        p = Page(name="Foo A", content="<p>Foo A</p>")
        p.save()
        p.rename_to("FOO A")

        # Name has changed.
        self.assertEqual(len(Page.objects.filter(name="FOO A")), 1)
        # Has the same history, with a new entry for the name change.
        p = Page.objects.get(name="FOO A")
        p1, p0 = p.versions.all()
        self.assertEqual(p1.name, 'FOO A')
        self.assertEqual(p0.name, 'Foo A')
        self.assertEqual(p0.content, p1.content)

        ###########################################################
        # Renaming a page twice (A -> B -> C) and then revert A to
        # an existing state.
        ###########################################################
        p = Page(name="Bar A", content="<p>Bar A</p>")
        p.save()
        p.rename_to("Bar B")
        p = Page.objects.get(name="Bar B")
        p.rename_to("Bar C")

        p = Page(name="Bar A", slug="bar a")
        p_h = p.versions.as_of(version=1)
        p_h.revert_to()

        ###########################################################
        # Renaming a page back and forth and reverting.
        ###########################################################
        p = Page(name="Zoo A", content="<p>Zoo A</p>")
        p.save()
        p.rename_to("Zoo B")
        p = Page.objects.get(name="Zoo B")
        p.rename_to("Zoo A")
        p = Page.objects.get(name="Zoo A")
        p.rename_to("Zoo B")

        p = Page(name="Zoo A", slug="zoo a")
        p_h = p.versions.as_of(version=1)
        p_h.revert_to()

        ###########################################################
        # page A, rename to B, then create new A, rename B to C,
        # rename C to B, then revert C to first version
        ###########################################################
        p = Page(name="Mike A", content="<p>A</p>")
        p.save()
        p.rename_to("Mike B")
        new_a = Page(name="Mike A", content="<p>A new</p>")
        new_a.save()
        p = Page.objects.get(name="Mike B")
        p.rename_to("Mike C")
        p = Page.objects.get(name="Mike C")
        p.rename_to("Mike B")

        p_c = Page(name="Mike C", slug="mike c")
        p_h = p_c.versions.as_of(version=1)
        p_h.revert_to()
Exemple #9
0
def get_posts(item, uri_parser, user):
    """
    If the given Article has already been created, skip it.
    If not, create Article object and Redirect object.
    """
    alreadyThere = False
    link = unicode(item.find('link').contents[0])
    slug = uri_parser.parse(link).path.strip('/')

    for article in Article.objects.all():
        if article.slug == slug[:100]:
            alreadyThere = True
            break
    
    if not alreadyThere:
        title = unicode(item.find('title').contents[0])
        post_id = item.find('wp:post_id').string
        post_id = int(post_id)
        body = unicode(item.find('content:encoded').contents[0])
        body = replace_short_code(body)
        try:
            # There may not be a file associated with a post.
            # If so, catch that error.
            fgroup = AssociatedFile.objects.filter(post_id=post_id)
            for f in fgroup:
                body = correct_media_file_path(body, f.file)

        except:
            pass

        post_date = unicode(item.find('wp:post_date').contents[0])
        post_dt = datetime.strptime(post_date, '%Y-%m-%d %H:%M:%S')
        
        tags_raw = item.findAll('category', domain="post_tag")
        tags_list = []
    
        if tags_raw:
            for tag in tags_raw:
                if len(','.join(tags_list)) + len(tag.string) <= 255:
                    tags_list.append(tag.string[:50])
    
        article = {
            'headline': title,
            'guid': str(uuid.uuid1()),
            'slug': slug[:100],
            'body': body,
            'tags': ','.join(tags_list),
            'timezone': 'US/Central',
            'syndicate': True,
            'featured': False,
            'release_dt': post_dt,
            'creator': user,
            'creator_username': user.username,
            'allow_anonymous_view': True,
            'allow_user_view': False,
            'allow_member_view': False,
            'allow_anonymous_edit': False,
            'allow_user_edit': False,
            'allow_member_edit': False,
            'owner': user,
            'owner_username': user.username,
            'status': True,
            'status_detail': 'active'
        }

        redirect = {
            'from_url': slug[:100],
            'to_url': os.path.join('articles', slug[:100])
        }

        a = Article(**article)
        a.save()

        r = Redirect(**redirect)
        r.save()
Exemple #10
0
    def rename_to(self, pagename):
        """
        Renames the page to `pagename`.  Moves related objects around
        accordingly.
        """
        def _get_slug_lookup(unique_together, obj, new_p):
            d = {}
            for field in unique_together:
                d[field] = getattr(obj, field)
            d['slug'] = new_p.slug
            return d

        from redirects.models import Redirect
        from redirects.exceptions import RedirectToSelf

        if Page.objects.filter(slug=slugify(pagename)):
            if slugify(pagename) == self.slug:
                # The slug is the same but we're changing the name.
                old_name = self.name
                self.name = pagename
                self.save(comment='Renamed from "%s"' % old_name)
                return
            else:
                raise exceptions.PageExistsError(
                    "The page '%s' already exists!" % pagename)

        # Copy the current page into the new page, zeroing out the
        # primary key and setting a new name and slug.
        new_p = copy(self)
        new_p.pk = None
        new_p.name = pagename
        new_p.slug = slugify(pagename)
        new_p.save(comment='Renamed from "%s"' % self.name)

        # Get all related objects before the original page is deleted.
        related_objs = []
        for r in self._meta.get_all_related_objects():
            try:
                rel_obj = getattr(self, r.get_accessor_name())
            except:
                continue  # No object for this relation.

            # Is this a related /set/, e.g. redirect_set?
            if isinstance(rel_obj, models.Manager):
                # list() freezes the QuerySet, which we don't want to be
                # fetched /after/ we delete the page.
                related_objs.append(
                    (r.get_accessor_name(), list(rel_obj.all())))
            else:
                related_objs.append((r.get_accessor_name(), rel_obj))

        # Create a redirect from the starting pagename to the new pagename.
        redirect = Redirect(source=self.slug, destination=new_p)
        # Creating the redirect causes the starting page to be deleted.
        redirect.save()

        # Point each related object to the new page and save the object with a
        # 'was renamed' comment.
        for attname, rel_obj in related_objs:
            if isinstance(rel_obj, list):
                for obj in rel_obj:
                    obj.pk = None  # Reset the primary key before saving.
                    try:
                        getattr(new_p, attname).add(obj)
                        obj.save(comment="Parent page renamed")
                    except RedirectToSelf, s:
                        # We don't want to create a redirect to ourself.
                        # This happens during a rename -> rename-back
                        # cycle.
                        continue
            else:
                # This is an easy way to set obj to point to new_p.
                setattr(new_p, attname, rel_obj)
                rel_obj.pk = None  # Reset the primary key before saving.
                rel_obj.save(comment="Parent page renamed")
    def rename_to(self, pagename):
        """
        Renames the page to `pagename`.  Moves related objects around
        accordingly.
        """
        def _get_slug_lookup(unique_together, obj, new_p):
            d = {}
            for field in unique_together:
                d[field] = getattr(obj, field)
            d['slug'] = new_p.slug
            return d

        def _already_exists(obj):
            M = obj.__class__
            unique_vals = unique_lookup_values_for(obj)
            if not unique_vals:
                return False
            return M.objects.filter(**unique_vals).exists()

        from redirects.models import Redirect
        from redirects.exceptions import RedirectToSelf

        if Page(slug=slugify(pagename), region=self.region).exists():
            if slugify(pagename) == self.slug:
                # The slug is the same but we're changing the name.
                old_name = self.name
                self.name = pagename
                self.save(comment=_('Renamed from "%s"') % old_name)
                return
            else:
                raise exceptions.PageExistsError(
                    _("The page '%s' already exists!") % pagename)

        # Copy the current page into the new page, zeroing out the
        # primary key and setting a new name and slug.
        new_p = copy(self)
        new_p.pk = None
        new_p.name = pagename
        new_p.slug = slugify(pagename)
        new_p._in_rename = True
        new_p.save(comment=_('Renamed from "%s"') % self.name)

        # Get all related objects before the original page is deleted.
        related_objs = self._get_related_objs()

        # Cache all ManyToMany values on related objects so we can restore them
        # later--otherwise they will be lost when page is deleted.
        for attname, rel_obj_list in related_objs:
            if not isinstance(rel_obj_list, list):
                rel_obj_list = [rel_obj_list]
            for rel_obj in rel_obj_list:
                rel_obj._m2m_values = dict(
                    (f.attname, list(getattr(rel_obj, f.attname).all()))
                    for f in rel_obj._meta.many_to_many)

        # Create a redirect from the starting pagename to the new pagename.
        redirect = Redirect(source=self.slug, destination=new_p, region=self.region)
        # Creating the redirect causes the starting page to be deleted.
        redirect.save()

        # Point each related object to the new page and save the object with a
        # 'was renamed' comment.
        for attname, rel_obj in related_objs:
            if isinstance(rel_obj, list):
                for obj in rel_obj:
                    obj.pk = None  # Reset the primary key before saving.
                    try:
                        getattr(new_p, attname).add(obj)
                        if _already_exists(obj):
                            continue
                        if is_versioned(obj):
                            obj.save(comment=_("Parent page renamed"))
                        else:
                            obj.save()
                        # Restore any m2m fields now that we have a new pk
                        for name, value in obj._m2m_values.items():
                            setattr(obj, name, value)
                    except RedirectToSelf, s:
                        # We don't want to create a redirect to ourself.
                        # This happens during a rename -> rename-back
                        # cycle.
                        continue
            else:
                # This is an easy way to set obj to point to new_p.
                setattr(new_p, attname, rel_obj)
                rel_obj.pk = None  # Reset the primary key before saving.
                if _already_exists(rel_obj):
                    continue

                if is_versioned(rel_obj):
                    rel_obj.save(comment=_("Parent page renamed"))
                else:
                    rel_obj.save()
                # Restore any m2m fields now that we have a new pk
                for name, value in rel_obj._m2m_values.items():
                    setattr(rel_obj, name, value)
Exemple #12
0
    def test_page_rename(self):
        p = Page()
        p.content = "<p>The page content.</p>"
        p.name = "Original page"
        p.save()

        p.rename_to("New page")

        # Renamed-to page should exist.
        new_p = Page.objects.get(name="New page")
        # new_p should have the same content.
        self.assertEqual(new_p.content, p.content)

        # "Original page" should no longer exist.
        pgs = Page.objects.filter(name="Original page")
        self.assertEqual(len(pgs), 0)
        # and a redirect from "original page" to "New page" should exist.
        Redirect.objects.filter(source="original page", destination=new_p)

        ###########################################################
        # Renaming to a page that already exists should raise an
        # exception and not affect the original page.
        ###########################################################
        p = Page()
        p.content = "<p>Hello, world.</p>"
        p.name = "Page A"
        p.save()

        self.assertRaises(exceptions.PageExistsError, p.rename_to, "New page")
        # p should be unaffected.  No redirect should be created.
        p = Page.objects.get(name="Page A")
        self.assertEqual(p.content, "<p>Hello, world.</p>")
        self.assertEqual(len(Redirect.objects.filter(source="page a")), 0)

        ###########################################################
        # Renaming should carry along files and FK'ed items that
        # point to it.
        ###########################################################
        p = Page()
        p.content = "<p>A page with files and tags</p>"
        p.name = "Page With FKs"
        p.save()
        # Create a file that points at the page.
        pf = PageFile(file=ContentFile("foo"), name="file.txt", slug=p.slug)
        pf.save()
        # Create a redirect that points at the page.
        redirect = Redirect(source="foobar", destination=p)
        redirect.save()
        # Add tags to page
        tagset = PageTagSet(page=p)
        tagset.save()
        tag = Tag(name="tag1")
        tag.save()
        tagset.tags.add(tag)

        p.rename_to("New Page With FKs")

        new_p = Page.objects.get(name="New Page With FKs")
        self.assertEqual(len(new_p.pagetagset.tags.all()), 1)
        # Two redirects: one we created explicitly and one that was
        # created during rename_to()
        self.assertEqual(len(Redirect.objects.filter(destination=new_p)), 2)
        self.assertEqual(len(PageFile.objects.filter(slug=new_p.slug)), 1)

        # Renaming should keep slugs pointed at old page /and/ copy
        # them to the new page.
        self.assertEqual(len(PageFile.objects.filter(slug=p.slug)), 1)

        ###########################################################
        # Renaming with multiple files.
        ###########################################################
        p = Page()
        p.content = "<p>A new page with multiple files.</p>"
        p.name = "Page with multiple files"
        p.save()
        # Create a file that points at the page.
        pf = PageFile(file=ContentFile("foo"), name="file.txt", slug=p.slug)
        pf.save()
        pf = PageFile(file=ContentFile("foo2"), name="file2.txt", slug=p.slug)
        pf.save()
        pf = PageFile(file=ContentFile("foo3"), name="file3.txt", slug=p.slug)
        pf.save()
        p.rename_to("A page with multiple files 2")

        p = Page.objects.get(name="A page with multiple files 2")
        self.assertEqual(len(PageFile.objects.filter(slug=p.slug)), 3)

        ###########################################################
        # Reverting a renamed page should be possible and should
        # restore files and FK'ed items that were pointed at the
        # original page.  The renamed-to page should still exist
        # after the revert and should still have its own files and
        # FK'ed items pointed at it.
        ###########################################################
        p = Page(name="Page With FKs", slug="page with fks")
        # get the version right before it was deleted
        v_before_deleted = len(p.versions.all()) - 1
        p_h = p.versions.as_of(version=v_before_deleted)
        p_h.revert_to()
        p = Page.objects.get(name="Page With FKs")
        self.assertEqual(len(PageFile.objects.filter(slug=p.slug)), 1)

        p2 = Page.objects.get(name="New Page With FKs")
        self.assertEqual(len(PageFile.objects.filter(slug=p2.slug)), 1)

        self.assertEqual(len(Redirect.objects.filter(destination=p2)), 1)

        ###########################################################
        # Renaming a page and then renaming it back.
        ###########################################################
        # 1. Simple case
        p = Page(name="Page X", content="<p>Foobar</p>")
        p.save()
        p.rename_to("Page Y")
        self.assertEqual(len(Page.objects.filter(name="Page X")), 0)
        self.assertEqual(len(Page.objects.filter(name="Page Y")), 1)

        p_new = Page.objects.get(name="Page Y")
        p_new.rename_to("Page X")
        self.assertEqual(len(Page.objects.filter(name="Page X")), 1)
        self.assertEqual(len(Page.objects.filter(name="Page Y")), 0)

        # 2. If we have FKs pointed at the page this shouldn't be
        # totally f****d.
        p = Page(name="Page X2", content="<p>Foo X</p>")
        p.save()
        # Create a file that points at the page.
        pf = PageFile(file=ContentFile("foooo"), name="file_foo.txt", slug=p.slug)
        pf.save()

        p.rename_to("Page Y2")
        p_new = Page.objects.get(name="Page Y2")
        # FK points at the page we renamed to.
        self.assertEqual(len(PageFile.objects.filter(slug=p_new.slug)), 1)

        # Now rename it back.
        p_new.rename_to("Page X2")
        p = Page.objects.get(name="Page X2")
        # After rename-back-to, FK points to the renamed-back-to page.
        self.assertEqual(len(PageFile.objects.filter(slug=p.slug)), 1)

        ###########################################################
        # Renaming a page but keeping the same slug
        ###########################################################
        p = Page(name="Foo A", content="<p>Foo A</p>")
        p.save()
        p.rename_to("FOO A")

        # Name has changed.
        self.assertEqual(len(Page.objects.filter(name="FOO A")), 1)
        # Has the same history, with a new entry for the name change.
        p = Page.objects.get(name="FOO A")
        p1, p0 = p.versions.all()
        self.assertEqual(p1.name, 'FOO A')
        self.assertEqual(p0.name, 'Foo A')
        self.assertEqual(p0.content, p1.content)

        ###########################################################
        # Renaming a page twice (A -> B -> C) and then revert A to
        # an existing state.
        ###########################################################
        p = Page(name="Bar A", content="<p>Bar A</p>")
        p.save()
        p.rename_to("Bar B")
        p = Page.objects.get(name="Bar B")
        p.rename_to("Bar C")

        p = Page(name="Bar A", slug="bar a")
        p_h = p.versions.as_of(version=1)
        p_h.revert_to()

        ###########################################################
        # Renaming a page back and forth and reverting.
        ###########################################################
        p = Page(name="Zoo A", content="<p>Zoo A</p>")
        p.save()
        p.rename_to("Zoo B")
        p = Page.objects.get(name="Zoo B")
        p.rename_to("Zoo A")
        p = Page.objects.get(name="Zoo A")
        p.rename_to("Zoo B")

        p = Page(name="Zoo A", slug="zoo a")
        p_h = p.versions.as_of(version=1)
        p_h.revert_to()

        ###########################################################
        # page A, rename to B, then create new A, rename B to C,
        # rename C to B, then revert C to first version
        ###########################################################
        p = Page(name="Mike A", content="<p>A</p>")
        p.save()
        p.rename_to("Mike B")
        new_a = Page(name="Mike A", content="<p>A new</p>")
        new_a.save()
        p = Page.objects.get(name="Mike B")
        p.rename_to("Mike C")
        p = Page.objects.get(name="Mike C")
        p.rename_to("Mike B")

        p_c = Page(name="Mike C", slug="mike c")
        p_h = p_c.versions.as_of(version=1)
        p_h.revert_to()
    def rename_to(self, pagename):
        """
        Renames the page to `pagename`.  Moves related objects around
        accordingly.
        """
        def _get_slug_lookup(unique_together, obj, new_p):
            d = {}
            for field in unique_together:
                d[field] = getattr(obj, field)
            d['slug'] = new_p.slug
            return d

        def _already_exists(obj):
            M = obj.__class__
            unique_vals = unique_lookup_values_for(obj)
            if not unique_vals:
                return False
            return M.objects.filter(**unique_vals).exists()

        from redirects.models import Redirect
        from redirects.exceptions import RedirectToSelf

        if Page(slug=slugify(pagename), region=self.region).exists():
            if slugify(pagename) == self.slug:
                # The slug is the same but we're changing the name.
                old_name = self.name
                self.name = pagename
                self.save(comment=_('Renamed from "%s"') % old_name)
                return
            else:
                raise exceptions.PageExistsError(
                    _("The page '%s' already exists!") % pagename)

        # Copy the current page into the new page, zeroing out the
        # primary key and setting a new name and slug.
        new_p = copy(self)
        new_p.pk = None
        new_p.name = pagename
        new_p.slug = slugify(pagename)
        new_p._in_rename = True
        new_p.save(comment=_('Renamed from "%s"') % self.name)

        # Get all related objects before the original page is deleted.
        related_objs = self._get_related_objs()

        # Cache all ManyToMany values on related objects so we can restore them
        # later--otherwise they will be lost when page is deleted.
        for attname, rel_obj_list in related_objs:
            if not isinstance(rel_obj_list, list):
                rel_obj_list = [rel_obj_list]
            for rel_obj in rel_obj_list:
                rel_obj._m2m_values = dict(
                    (f.attname, list(getattr(rel_obj, f.attname).all()))
                    for f in rel_obj._meta.many_to_many)

        # Create a redirect from the starting pagename to the new pagename.
        redirect = Redirect(source=self.slug,
                            destination=new_p,
                            region=self.region)
        # Creating the redirect causes the starting page to be deleted.
        redirect.save()

        # Point each related object to the new page and save the object with a
        # 'was renamed' comment.
        for attname, rel_obj in related_objs:
            if isinstance(rel_obj, list):
                for obj in rel_obj:
                    obj.pk = None  # Reset the primary key before saving.
                    try:
                        getattr(new_p, attname).add(obj)
                        if _already_exists(obj):
                            continue
                        if is_versioned(obj):
                            obj.save(comment=_("Parent page renamed"))
                        else:
                            obj.save()
                        # Restore any m2m fields now that we have a new pk
                        for name, value in obj._m2m_values.items():
                            setattr(obj, name, value)
                    except RedirectToSelf, s:
                        # We don't want to create a redirect to ourself.
                        # This happens during a rename -> rename-back
                        # cycle.
                        continue
            else:
                # This is an easy way to set obj to point to new_p.
                setattr(new_p, attname, rel_obj)
                rel_obj.pk = None  # Reset the primary key before saving.
                if _already_exists(rel_obj):
                    continue

                if is_versioned(rel_obj):
                    rel_obj.save(comment=_("Parent page renamed"))
                else:
                    rel_obj.save()
                # Restore any m2m fields now that we have a new pk
                for name, value in rel_obj._m2m_values.items():
                    setattr(rel_obj, name, value)