From 9882d7fda8d912674843dab1c1bd259715efcb93 Mon Sep 17 00:00:00 2001 From: Ryan Cross Date: Sat, 16 Nov 2019 05:15:03 +0000 Subject: [PATCH] Fix issue where draft resurrect feature does not move most recent draft file from the archive directory back to the current draft directory. Fixes #2646. Commit ready for merge - Legacy-Id: 17027 --- ietf/doc/tests_draft.py | 69 +++++++++++++++++++++++------------------ ietf/doc/views_draft.py | 21 +++++++++++++ 2 files changed, 60 insertions(+), 30 deletions(-) diff --git a/ietf/doc/tests_draft.py b/ietf/doc/tests_draft.py index f604231ee..7ce34e7d0 100644 --- a/ietf/doc/tests_draft.py +++ b/ietf/doc/tests_draft.py @@ -16,6 +16,7 @@ from django.conf import settings import debug # pyflakes:ignore +from ietf.doc.expire import get_expired_drafts, send_expire_notice_for_draft, expire_draft from ietf.doc.factories import IndividualDraftFactory, WgDraftFactory, RgDraftFactory, DocEventFactory from ietf.doc.models import ( Document, DocReminder, DocEvent, ConsensusDocEvent, LastCallDocEvent, RelatedDocument, State, TelechatDocEvent, @@ -492,7 +493,33 @@ class EditInfoTests(TestCase): self.assertEqual(draft.latest_event(ConsensusDocEvent, type="changed_consensus").consensus, None) -class ResurrectTests(TestCase): +class DraftFileMixin(): + '''A mixin to setup temporary draft directories and files''' + def setUp(self): + self.saved_id_dir = settings.INTERNET_DRAFT_PATH + self.saved_archive_dir = settings.INTERNET_DRAFT_ARCHIVE_DIR + self.id_dir = self.tempdir('id') + self.archive_dir = self.tempdir('id-archive') + os.mkdir(os.path.join(self.archive_dir, "unknown_ids")) + os.mkdir(os.path.join(self.archive_dir, "deleted_tombstones")) + os.mkdir(os.path.join(self.archive_dir, "expired_without_tombstone")) + + settings.INTERNET_DRAFT_PATH = self.id_dir + settings.INTERNET_DRAFT_ARCHIVE_DIR = self.archive_dir + + def tearDown(self): + shutil.rmtree(self.id_dir) + shutil.rmtree(self.archive_dir) + settings.INTERNET_DRAFT_PATH = self.saved_id_dir + settings.INTERNET_DRAFT_ARCHIVE_DIR = self.saved_archive_dir + + def write_draft_file(self, name, size): + f = io.open(os.path.join(self.id_dir, name), 'w') + f.write("a" * size) + f.close() + + +class ResurrectTests(DraftFileMixin, TestCase): def test_request_resurrect(self): draft = WgDraftFactory(states=[('draft','expired')]) @@ -526,14 +553,17 @@ class ResurrectTests(TestCase): def test_resurrect(self): ad = Person.objects.get(name="AreaĆ° Irector") - draft = WgDraftFactory(ad=ad,states=[('draft','expired')]) + draft = WgDraftFactory(ad=ad,states=[('draft','active')]) DocEventFactory(doc=draft,type="requested_resurrect",by=ad) - url = urlreverse('ietf.doc.views_draft.resurrect', kwargs=dict(name=draft.name)) - - login_testing_unauthorized(self, "secretary", url) + # create file and expire draft + txt = "%s-%s.txt" % (draft.name, draft.rev) + self.write_draft_file(txt, 5000) + expire_draft(draft) # normal get + url = urlreverse('ietf.doc.views_draft.resurrect', kwargs=dict(name=draft.name)) + login_testing_unauthorized(self, "secretary", url) r = self.client.get(url) self.assertEqual(r.status_code, 200) q = PyQuery(r.content) @@ -556,31 +586,12 @@ class ResurrectTests(TestCase): self.assertTrue('iesg-secretary' in outbox[-1]['To']) self.assertTrue('aread' in outbox[-1]['To']) + # ensure file restored from archive directory + self.assertTrue(os.path.exists(os.path.join(self.id_dir, txt))) + self.assertTrue(not os.path.exists(os.path.join(self.archive_dir, txt))) -class ExpireIDsTests(TestCase): - def setUp(self): - self.saved_id_dir = settings.INTERNET_DRAFT_PATH - self.saved_archive_dir = settings.INTERNET_DRAFT_ARCHIVE_DIR - self.id_dir = self.tempdir('id') - self.archive_dir = self.tempdir('id-archive') - os.mkdir(os.path.join(self.archive_dir, "unknown_ids")) - os.mkdir(os.path.join(self.archive_dir, "deleted_tombstones")) - os.mkdir(os.path.join(self.archive_dir, "expired_without_tombstone")) - - settings.INTERNET_DRAFT_PATH = self.id_dir - settings.INTERNET_DRAFT_ARCHIVE_DIR = self.archive_dir - def tearDown(self): - shutil.rmtree(self.id_dir) - shutil.rmtree(self.archive_dir) - settings.INTERNET_DRAFT_PATH = self.saved_id_dir - settings.INTERNET_DRAFT_ARCHIVE_DIR = self.saved_archive_dir - - def write_draft_file(self, name, size): - f = io.open(os.path.join(self.id_dir, name), 'w') - f.write("a" * size) - f.close() - +class ExpireIDsTests(DraftFileMixin, TestCase): def test_in_draft_expire_freeze(self): from ietf.doc.expire import in_draft_expire_freeze @@ -623,8 +634,6 @@ class ExpireIDsTests(TestCase): self.assertTrue('aread@' in outbox[-1]['Cc']) def test_expire_drafts(self): - from ietf.doc.expire import get_expired_drafts, send_expire_notice_for_draft, expire_draft - mars = GroupFactory(type_id='wg',acronym='mars') ad_role = RoleFactory(group=mars, name_id='ad', person=Person.objects.get(user__username='ad')) draft = WgDraftFactory(name='draft-ietf-mars-test',group=mars) diff --git a/ietf/doc/views_draft.py b/ietf/doc/views_draft.py index f605bc135..7eaa32053 100644 --- a/ietf/doc/views_draft.py +++ b/ietf/doc/views_draft.py @@ -7,6 +7,9 @@ from __future__ import absolute_import, print_function, unicode_literals # changing state and metadata on Internet Drafts import datetime +import os +import glob +import shutil from django import forms from django.conf import settings @@ -47,6 +50,7 @@ from ietf.person.fields import SearchableEmailField from ietf.person.models import Person, Email from ietf.utils.mail import send_mail, send_mail_message, on_behalf_of from ietf.utils.textupload import get_cleaned_text_file_content +from ietf.utils import log from ietf.mailtrigger.utils import gather_address_lists class ChangeStateForm(forms.Form): @@ -835,6 +839,8 @@ def resurrect(request, name): doc.expires = datetime.datetime.now() + datetime.timedelta(settings.INTERNET_DRAFT_DAYS_TO_EXPIRE) doc.save_with_history(events) + restore_draft_file(request, doc) + return HttpResponseRedirect(doc.get_absolute_url()) return render(request, 'doc/draft/resurrect.html', @@ -842,6 +848,21 @@ def resurrect(request, name): resurrect_requested_by=resurrect_requested_by, back_url=doc.get_absolute_url())) + +def restore_draft_file(request, draft): + '''restore latest revision document file from archive''' + basename = '{}-{}'.format(draft.name, draft.rev) + files = glob.glob(os.path.join(settings.INTERNET_DRAFT_ARCHIVE_DIR, basename) + '.*') + log.log("Resurrecting %s. Moving files:" % draft.name) + for file in files: + try: + shutil.move(file, settings.INTERNET_DRAFT_PATH) + log.log(" Moved file %s to %s" % (file, settings.INTERNET_DRAFT_PATH)) + except shutil.Error as ex: + messages.warning(request, 'There was an error restoring the draft file: {} ({})'.format(file, ex)) + log.log(" Exception %s when attempting to move %s" % (ex, file)) + + class IESGNoteForm(forms.Form): note = forms.CharField(widget=forms.Textarea, label="IESG note", required=False, strip=False)