66from django import forms
77from django .core .urlresolvers import reverse
88from django .contrib .admin .util import quote
9- from django .utils .translation import ugettext_lazy as _ , ugettext
9+ from django .utils .translation import ugettext_lazy as _ , ugettext , ungettext
10+ from django .utils import timezone
11+ from django .contrib import messages
12+ from django .db .models .signals import post_save
13+ from django .db .models import Min , Q
1014
1115from mezzanine .pages .admin import PageAdmin
1216try :
1721from mezzanine .core .models import (CONTENT_STATUS_PUBLISHED ,
1822 CONTENT_STATUS_DRAFT )
1923
20- from widgy .forms import WidgyFormMixin
24+ from widgy .forms import WidgyFormMixin , VersionedWidgyWidget
2125from widgy .contrib .widgy_mezzanine import get_widgypage_model
22- from widgy .contrib .widgy_mezzanine .views import ClonePageView
23- from widgy .utils import fancy_import , format_html
26+ from widgy .contrib .widgy_mezzanine .views import ClonePageView , UnpublishView
27+ from widgy .utils import format_html
28+ from widgy .db .fields import get_site
2429
2530
2631WidgyPage = get_widgypage_model ()
2732
2833
34+ class PageVersionedWidgyWidget (VersionedWidgyWidget ):
35+ template_name = 'widgy/widgy_mezzanine/versioned_widgy_field.html'
36+
37+
2938class WidgyPageAdminForm (WidgyFormMixin , PageAdminForm ):
3039 class Meta :
3140 model = WidgyPage
41+ widgets = {
42+ 'root_node' : PageVersionedWidgyWidget ,
43+ }
3244
3345 def __init__ (self , * args , ** kwargs ):
3446 super (WidgyPageAdminForm , self ).__init__ (* args , ** kwargs )
@@ -38,26 +50,133 @@ def __init__(self, *args, **kwargs):
3850 self .fields ['expiry_date' ].help_text = _ (
3951 "If you enter a date here, the page will not be viewable after this time"
4052 )
41- self .fields ['status' ].initial = CONTENT_STATUS_DRAFT
53+ if self .instance .pk is not None :
54+ self .instance .status = CONTENT_STATUS_DRAFT
4255
43- def clean_status (self ):
44- status = self .cleaned_data .get ('status' )
45- if (status == CONTENT_STATUS_PUBLISHED and (not self .instance .root_node or
46- not self .instance .root_node .head )):
47- raise forms .ValidationError (_ ('You must commit before you can publish' ))
48- return status
4956
57+ # the status of a page before it's created, on the add page
58+ CONTENT_STATUS_EMBRYO = 0
5059
5160class WidgyPageAdmin (PageAdmin ):
52- change_form_template = 'widgy/page_builder /widgypage_change_form.html'
61+ change_form_template = 'widgy/widgy_mezzanine /widgypage_change_form.html'
5362 form = WidgyPageAdminForm
63+ readonly_fields = ['status' ]
64+
65+ unreviewed_buttons = {
66+ CONTENT_STATUS_EMBRYO : [('_continue' , _ ('Save' ))],
67+ CONTENT_STATUS_DRAFT : [('_continue' , _ ('Save as Draft' )),
68+ ('_save_and_commit' , _ ('Publish' ))],
69+ CONTENT_STATUS_PUBLISHED : [('_save_and_commit' , _ ('Publish Changes' ))],
70+ }
71+ reviewed_buttons = {
72+ (CONTENT_STATUS_EMBRYO , False ) : [('_continue' , _ ('Save' ))],
73+ (CONTENT_STATUS_EMBRYO , True ) : [('_continue' , _ ('Save' ))],
74+ (CONTENT_STATUS_DRAFT , False ) : [('_continue' , _ ('Save as Draft' )),
75+ ('_save_and_commit' , _ ('Submit for Review' ))],
76+ (CONTENT_STATUS_DRAFT , True ) : [('_continue' , _ ('Save as Draft' )),
77+ ('_save_and_commit' , _ ('Submit for Review' )),
78+ ('_save_and_approve' , _ ('Publish' ))],
79+ (CONTENT_STATUS_PUBLISHED , False ) : [('_save_and_commit' , _ ('Submit for Review' ))],
80+ (CONTENT_STATUS_PUBLISHED , True ) : [('_save_and_commit' , _ ('Submit for Review' )),
81+ ('_save_and_approve' , _ ('Publish Changes' ))],
82+ }
5483
5584 def get_urls (self ):
5685 clone_view = ClonePageView .as_view (has_permission = self .has_add_permission )
86+ unpublish_view = UnpublishView .as_view (has_change_permission = self .has_change_permission )
5787 return [
5888 url ('^(.+)/clone/$' , self .admin_site .admin_view (clone_view )),
89+ url ('^(.+)/unpublish/$' , self .admin_site .admin_view (unpublish_view )),
5990 ] + super (WidgyPageAdmin , self ).get_urls ()
6091
92+ def _save_and_commit (self , request , obj ):
93+ site = self .get_site ()
94+ commit_model = site .get_version_tracker_model ().commit_model
95+ if not site .has_add_permission (request , commit_model ):
96+ messages .error (request , _ ("You don't have permission to commit." ))
97+ else :
98+ if obj .root_node .has_changes ():
99+ obj .root_node .commit (user = request .user )
100+ elif self .has_review_queue :
101+ messages .warning (request , _ ("There was nothing to submit for review." ))
102+
103+ if not self .has_review_queue :
104+ obj .status = CONTENT_STATUS_PUBLISHED
105+ # else:
106+ # If we are reviewed, we'll have to wait for approval.
107+ # Handled by the publish_page_on_approve signal.
108+
109+ def _save_and_approve (self , request , obj ):
110+ site = self .get_site ()
111+ commit_model = site .get_version_tracker_model ().commit_model
112+ if not site .has_add_permission (request , commit_model ) or \
113+ not site .has_change_permission (request , commit_model ):
114+ messages .error (request , _ ("You don't have permission to approve commits." ))
115+ else :
116+ if obj .root_node .has_changes ():
117+ obj .root_node .commit (request .user )
118+ # If we had changes, `head` is the same commit we just created.
119+ # If we didn't need to create a commit, we want to publish the
120+ # most recent one instead.
121+ obj .root_node .head .reviewedversioncommit .approve (request .user )
122+ obj .root_node .head .reviewedversioncommit .save ()
123+ obj .status = CONTENT_STATUS_PUBLISHED
124+
125+ def save_model (self , request , obj , form , change ):
126+ if '_save_and_commit' in request .POST :
127+ self ._save_and_commit (request , obj )
128+ elif '_save_and_approve' in request .POST and self .has_review_queue :
129+ self ._save_and_approve (request , obj )
130+ request .POST ['_continue' ] = True
131+ super (WidgyPageAdmin , self ).save_model (request , obj , form , change )
132+
133+ def render_change_form (self , request , context , add = False , change = False , form_url = '' , obj = None , * args , ** kwargs ):
134+ if not add :
135+ unapproved = 0
136+ future = 0
137+ for commit in obj .root_node .get_history_list ():
138+ if obj .root_node .commit_is_ready (commit ):
139+ # got to the currently-published commit
140+ break
141+ if self .has_review_queue and not commit .reviewedversioncommit .is_approved :
142+ unapproved += 1
143+ if not commit .is_published :
144+ future += 1
145+ if unapproved :
146+ messages .warning (request , ungettext (
147+ "There is one unreviewed commit for this page." ,
148+ "There are {count} unreviewed commits for this page." ,
149+ unapproved
150+ ).format (count = unapproved ))
151+ if future :
152+ messages .warning (request , ungettext (
153+ "There is one future-scheduled commit." ,
154+ "There are {count} future-scheduled commits." ,
155+ future
156+ ).format (count = future ))
157+
158+ site = self .get_site ()
159+ if add :
160+ status = CONTENT_STATUS_EMBRYO
161+ else :
162+ status = obj .status
163+ if self .has_review_queue :
164+ commit_model = site .get_version_tracker_model ().commit_model
165+ can_approve = site .has_change_permission (request , commit_model )
166+ context ['save_buttons' ] = self .reviewed_buttons [(status , can_approve )]
167+ else :
168+ context ['save_buttons' ] = self .unreviewed_buttons [status ]
169+ if not add :
170+ context ['history_url' ] = site .reverse (site .history_view , kwargs = {'pk' : obj .pk })
171+ return super (WidgyPageAdmin , self ).render_change_form (request , context , add , change , form_url , obj , * args , ** kwargs )
172+
173+ @property
174+ def has_review_queue (self ):
175+ return isinstance (self .get_site (), ReviewedWidgySite )
176+
177+ def get_site (self ):
178+ return get_site (settings .WIDGY_MEZZANINE_SITE )
179+
61180
62181class UndeleteField (forms .ModelChoiceField ):
63182 widget = forms .RadioSelect
@@ -138,12 +257,55 @@ def __init__(self, *args, **kwargs):
138257admin .site .register (WidgyPage , WidgyPageAdmin )
139258admin .site .register (UndeletePage , UndeletePageAdmin )
140259
260+
261+ def publish_page_on_approve (sender , instance , created , ** kwargs ):
262+ site = get_site (settings .WIDGY_MEZZANINE_SITE )
263+
264+ pages = WidgyPage .objects .filter (
265+ root_node = instance .tracker ,
266+ )
267+ if instance .is_approved :
268+ pages = pages .filter (
269+ Q (publish_date__gte = instance .publish_at ) |
270+ Q (status = CONTENT_STATUS_DRAFT )
271+ ).update (
272+ status = CONTENT_STATUS_PUBLISHED ,
273+ publish_date = instance .publish_at ,
274+ )
275+ elif not site .get_version_tracker_model ().objects .filter (pk = instance .tracker .pk ).published ().exists ():
276+ # unaproving a commit, and there are no other currently published commits
277+ CommitModel = site .get_version_tracker_model ().commit_model
278+ beginning_of_validity = CommitModel .objects .approved ().filter (
279+ tracker_id = instance .tracker .pk ,
280+ publish_at__gt = timezone .now (),
281+ ).aggregate (min = Min ('publish_at' ))['min' ]
282+ if beginning_of_validity is not None :
283+ # There's a scheduled commit, move publish_date of the page forward
284+ # up to the publish_at of the commit.
285+ pages .update (
286+ publish_date = beginning_of_validity ,
287+ status = CONTENT_STATUS_PUBLISHED ,
288+ )
289+ else :
290+ # no other published commits at all, page needs to be unpublished
291+ pages .update (
292+ status = CONTENT_STATUS_DRAFT ,
293+ )
294+
141295if 'widgy.contrib.review_queue' in settings .INSTALLED_APPS :
142296 from widgy .contrib .review_queue .admin import VersionCommitAdminBase
143297 from widgy .contrib .review_queue .models import ReviewedVersionCommit
298+ from widgy .contrib .review_queue .site import ReviewedWidgySite
144299
145300 class VersionCommitAdmin (VersionCommitAdminBase ):
146301 def get_site (self ):
147- return fancy_import (settings .WIDGY_MEZZANINE_SITE )
302+ return get_site (settings .WIDGY_MEZZANINE_SITE )
148303
149304 admin .site .register (ReviewedVersionCommit , VersionCommitAdmin )
305+
306+ site = get_site (settings .WIDGY_MEZZANINE_SITE )
307+
308+ if isinstance (site , ReviewedWidgySite ):
309+ # In the tests, review_queue is installed but a ReviewedWidgySite might
310+ # not be in use.
311+ post_save .connect (publish_page_on_approve , sender = site .get_version_tracker_model ().commit_model )
0 commit comments