-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPageContentFlow.module
444 lines (334 loc) · 12.9 KB
/
PageContentFlow.module
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
<?php
/**
* Adds method hook to Page objects for doing stuff when cfStatus changes
* Page::cfChangedStatus($oldStatus, $newStatus) see bottom of this file to see description
* about parameters
*/
class PageContentFlow extends WireData implements Module{
protected $id;
protected $page;
protected $currentUser;
protected $isPost = false;
protected $cfRootPage;
protected $cfMainModule;
protected $stop = false;
protected $pageStatusBeforeSave;
protected $usersToNotify;
public static function getModuleInfo() {
return array(
'title' => 'Content Flow Tab',
'version' => 1,
'summary' => 'Adds a Publish Workflow Tab on pages.',
'singular' => true,
'autoload' => true,
'requires' => array('ProcessContentFlow')
);
}
public function init(){
if(isset($_POST['id'])) $this->id = (int) $_POST['id'];
else if(isset($_GET['id'])) $this->id = (int) $_GET['id'];
if(!$this->id){
$this->stop = true;
return;
}
$this->usersToNotify = new PageArray();
$this->currentUser = $this->user;
$this->page = $this->pages->get($this->id);
if($this->page instanceof NullPage){ $this->stop = true; return; }
// determine if we're going to be dealing with a save/post request
$this->isPost = ($this->input->post->id > 0 && (((int) $this->input->post->id) === $this->page->id)) || $this->config->ajax && (count($_POST) || isset($_SERVER['HTTP_X_FIELDNAME']));
/** Add our hooks **/
$this->addHook('Page::cfChangedStatus', $this, 'hookCFChangedStatus');
wire()->pages->addHookAfter('saveReady', $this, 'hookGetCFStatusBeforeSave');
$this->addHookAfter('Fieldtype::savePageField', $this, 'hookCheckChangeStatus');
}
public function ready(){
if($this->stop) return;
$this->cfMainModule = $this->modules->get('ProcessContentFlow');
$this->cfRootPage = $this->pages->get($this->cfMainModule->root_page_id);
if($this->cfRootPage instanceOf NullPage){
$this->error('Could not load Content Flow base page.');
$this->stop = true;
return;
}
//don't run code below if we have a post request
if($this->isPost) return;
//check if this page should have a workflow tab
$templatesWithWorkflow = $this->cfMainModule->getTemplatesWithWorkflow();
if(count($templatesWithWorkflow)){
foreach($templatesWithWorkflow as $temp){
if($temp->id == $this->page->template->id){
$this->addHookAfter('ProcessPageEdit::buildForm', $this, 'hookRemoveCFFieldsFromEditPage');
$this->addHookAfter('ProcessPageEdit::buildForm', $this, 'hookPageBuildForm');
$this->addHookAfter('ProcessPageEdit::buildForm', $this, 'hookEditFormDescription');
break;
}
}
}
}
protected function ___buildFormWorkFlow(){
$modules = $this->modules;
$page = $this->page;
$wrapper = new InputfieldWrapper();
$wrapper->attr('id', __CLASS__ . 'WorkFlow');
$wrapper->attr('title', $this->_('Publishing Workflow'));
//add cf_status field
$field = $page->template->fields->getField('cf_status');
$inputfield = $field->getInputfield($page);
if(!$inputfield){
$this->error('Could not display Workflow Status field');
} else {
$inputfield->value = $page->get($field->name);
$wrapper->append($inputfield);
}
return $wrapper;
}
protected function ___buildFormNotifications(){
$page = $this->page;
$modules = wire('modules');
$wrapper = new InputfieldWrapper();
$wrapper->attr('id', __CLASS__ . 'Notifications');
$wrapper->attr('title', $this->_('Notifications'));
$fieldset = $modules->get('InputfieldFieldset');
$fieldset->label = $this->_('Notifications');
$fieldset->description = $this->_('Select the users and roles (user groups) that should receive notifications when the workflow status of this page is updated.');
//add cf_users field
$field = $page->template->fields->getField('cf_users');
$origInputfield = $field->getInputfield($page);
if(!$origInputfield){
$this->error('Could not display Notify Users field');
} else {
/**
* We build our own inputfield because we want to show the user's
* display_name if it exists
*/
$inputfield = $modules->get('InputfieldCheckboxes');
$inputfield->attr('name', $origInputfield->attr('name'));
$inputfield->attr('id', $origInputfield->attr('id'));
$inputfield->label = $origInputfield->label;
$inputfield->description = $origInputfield->description;
$inputfield->columnWidth = 50;
$origInputfield->value = $page->get($field->name);
$value = $origInputfield->value;
if($value instanceof Page) $inputfield->value = $value->id; //derefAsPage
if($value instanceof PageArray) foreach($value as $v) $inputfield->value = $v->id; //derefAsPageArray
$children = $origInputfield->getSelectablePages($page);
if($children) foreach($children as $child){
$label = $this->getUserLabelFieldName($origInputfield->labelFieldName, $child);
$inputfield->addOption($child->id, $label);
}
$fieldset->append($inputfield);
}
//add roles field
$field = $page->template->fields->getField('cf_roles');
$inputfield = $field->getInputfield($page);
if(!$inputfield){
$this->error('Could not display Notify Roles field');
} else {
$inputfield->columnWidth = 50;
$inputfield->value = $page->get($field->name);
$fieldset->append($inputfield);
}
$wrapper->append($fieldset);
return $wrapper;
}
/**
* Returns label field to be used when building own multi select fieldpage
*/
public function ___getUserLabelFieldName($labelFieldName = '', WireData $page){
$label = $labelFieldName ? $page->get($labelFieldName) : $page->name;
if($page->display_name)
$label = $page->display_name . " ($label) {$page->id}";
return $label;
}
/**
* ======================================
* HOOKS GO BELOW
* ======================================
*/
/**
* @Hooks to ProcessPageEdit::buildForm
*/
public function hookPageBuildForm(HookEvent $event){
$form = $event->return;
//get the 'Content' tab input field wrapper so we can append our tab after this
$contentTab = $form->children('id=ProcessPageEditContent')->first();
$workflow = $this->buildFormWorkFlow();
$notifications = $this->buildFormNotifications();
if($contentTab->id){
$event->return = $form->insertAfter($workflow, $contentTab);
$event->return = $form->insertAfter($notifications, $workflow);
} else {
$event->return = $form->append($workflow);
$event->return = $form->append($notifications);
}
}
/**
* Removes workflow fields from being returned when a page calls getInputfields() as in
* ProcessPageEdit::buildForm() because we don't want workflow fields to show up the normal
* place. We want to show workflow fields under the 'Workflow' tab
*
* NOTE: If this hook is being run then it means that the current page has workflow enabled
*
* @Hooks to ProcessPageEdit::buildForm
* @param HookEvent $event
*/
public function hookRemoveCFFieldsFromEditPage(HookEvent $event){
$form = $event->return;
$workflowFieldNames = $this->cfMainModule->getWorkflowFields();
$fieldsToFilter = $form->find('name='.implode('|',$workflowFieldNames));
if(!count($fieldsToFilter)) return;
foreach($fieldsToFilter as $f){
$f->parent->remove($f);
}
$event->return = $form;
}
/**
* Add our status text to the form description (if the page is unpublished)
*
* NOTE: If this hook is triggered then it means the current page has workflow enabled
*
* @hooks to ProcessPageEdit::buildForm
* @param HookEvent $event
*/
public function hookEditFormDescription(HookEvent $event){
$form = $event->return;
if($this->page->is(Page::statusUnpublished) && $this->page->cf_status){
//append our status text to the form description
if($form->description) $form->description .= sprintf($this->_(' [status: "%1$s"]'), $this->page->cf_status->get('title|name'));
}
$event->return = $form; //not really needed since $form is an object and is copied by reference
}
/**
* Get status before page is saved
*/
public function hookGetCFStatusBeforeSave(HookEvent $event){
$page = $event->arguments('page');
$statusFieldName = 'cf_status';
if(!$page->fields->hasField($statusFieldName)) return;
//we'll need the clone later
$clone = clone $page;
// get the old value of the field
//$page->uncache();
$clone->uncache();
$this->pageStatusBeforeSave = $clone->$statusFieldName;
}
/**
* check if status has changed and trigger Page::cfChangedStatus hook
*
* hooks into Fieldtype::savePageField
*/
protected function hookCheckChangeStatus(HookEvent $event){
$page = $event->arguments('page');
$field = $event->arguments('field');
$statusFieldName = 'cf_status';
//page field did not save to database
if(!$event->return) return;
//field is cf_status field
if($field->name != $statusFieldName) return;
$savedStatus = $page->get($statusFieldName);
//don't do anything if cf_status hasn't changed
if($this->pageStatusBeforeSave->id == $savedStatus->id) return;
//trigger status change.
//NOTE: Still not sure which one to keep.
$page->cfChangedStatus($this->pageStatusBeforeSave, $savedStatus);
$this->changedStatus($this->pageStatusBeforeSave, $savedStatus, $page);
}
/**
* hook Page::cfChangeStatus
*/
protected function hookCFChangedStatus(HookEvent $event){
$old = $event->arguments(0);
$new = $event->arguments(1);
//$this->message('Publishing status has changed from ' . $old->title . ' to ' . $new->title);
$notice = new NoticeMessage('Publishing status has changed from ' . $old->title . ' to ' . $new->title);
$notice->class = $this->className();
wire('notices')->prepend($notice);
$event->return = true;
}
/**
* =============================
* CF Method Hooks and friends go below
* =============================
*/
/**
* Hook called a page has changed publishing status
* !Note: technically, this hook is called after the cf_status field (of the page) has changed status
*
* @param $old previous status (contentflow-status type page) the page was set to
* @param $new new status (contentflow-status type page) the page was set to
* @param Page $page the page that changed status
*/
public function ___changedStatus($old, $new, Page $page){
//TODO: should we notify users/roles in old status?
$cfUsers = $this->buildListUsersToNotify($new, $page);
}
/**
* Returns PageArray of user objects
*
* Makes a list (PageArray) of users that should be notified based of the passed status.
* Users are selected from the cf_users and roles fields.
*
* @param Page $status
* @param Page $page current page being saved
* @return PageArray
*/
protected function ___buildListUsersToNotify(Page $status, Page $page){
/**
* Get users/roles that should be notified as specified in the status
*/
$userList = new PageArray();
$rolesList = new PageArray();
//specific users that should be notified
$cfUsers = $status->cf_users;
if(count($cfUsers))
$userList->import($cfUsers);
$pageUsers = $page->cf_users;
if(count($pageUsers))
$userList->import($pageUsers); //PW will take care not to include duplicates
//get users from selected roles that should be notified
$cfRoles = $status->cf_roles;
$pageRoles = $page->cf_roles;
if(count($cfRoles))
$rolesList->import($cfRoles);
if(count($pageRoles))
$rolesList->import($pageRoles);
//Now get the users from the rolesList
if(count($rolesList)){
foreach($this->users as $user){
//do not include users who have been selected already
if($userList->has($user)) continue;
foreach($rolesList as $r){
if($user->roles->has($r)){
$userList->add($user);
continue; //user is already in the list
}
}
}
}
if(count($cfRoles)){
foreach($this->users as $user){
//do not include users who have been selected already
if($cfUsers->has($user)) continue;
foreach($cfRoles as $r){
if($user->roles->has($r))
$userList->add($user);
}
}
}
$this->usersToNotify->import($userList); //save the list
return $userList;
}
/**
* Returns PageArray of users to notify
*/
public function ___getListUsersToNotify(){
return $this->usersToNotify;
}
}
/**
* Description of Page::cfChangedStatus()
*
* both $oldStatus and $newStatus are just Page objects under /admin/Content Flow/Status
Page::cfChangedStatus($oldStatus, $newStatus)
*/