-
Notifications
You must be signed in to change notification settings - Fork 0
/
work.controller.inc
503 lines (429 loc) · 15.6 KB
/
work.controller.inc
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
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
<?php
// $Id$
/**
* @file
* Provides a controller for loading, creating, and saving works.
*/
class workController extends DrupalDefaultEntityController implements EntityAPIControllerInterface {
/**
* Stores our transaction object, necessary for pessimistic locking to work.
*/
protected $controllerTransaction = NULL;
protected function buildQuery($ids, $conditions = array(), $revision_id = FALSE) {
return parent::buildQuery($ids, $conditions, $revision_id);
}
public function resetCache(array $ids = NULL) {
parent::resetCache($ids);
if (empty($this->entityCache) && !empty($this->controllerTransaction)) {
// If we don't have any entity in our local cache anymore, we commit the
// transaction so as to remove the locks we acquired.
// This will not commit the translation directly. Drupal will commit
// it as soon as possible given the state of the transaction stack.
unset($this->controllerTransaction);
}
}
/**
* Deletes multiple works by ID.
*
* @param $ids
* An array of work IDs to delete.
* @return
* TRUE on success, FALSE otherwise.
*/
public function delete($ids, DatabaseTransaction $transaction = NULL) {
$works = $ids ? $this->load($ids) : FALSE;
if (!$works) {
// Do nothing, incase invalid or no ids have been passed.
return;
}
if (!isset($transaction)) {
$transaction = db_transaction();
$started_transaction = TRUE;
}
try {
db_delete('work')
->condition('id', $ids, 'IN')
->execute();
db_delete('work_revision')
->condition('id', $ids, 'IN')
->execute();
// delete any related rows in the work_title table
db_delete('work_title')
->condition('id', $ids, 'IN')
->execute();
// delete any related rows in the work_description table
db_delete('work_description')
->condition('id', $ids, 'IN')
->execute();
foreach ($works as $work_id => $work) {
field_attach_delete('work', $work);
}
// Clear the page and block and work caches.
$this->resetCache();
// Ignore slave server temporarily.
db_ignore_slave();
// Invoke hook_ENTITY_view() to allow modules to add their additions.
// Invoke the hook. If rules is there, use the rule funtion so that a rules
// event is invoked too.
if (module_exists('rules')) {
rules_invoke_all($this->entityType . '_delete', $work);
}
else {
module_invoke_all($this->entityType . '_delete', $work);
}
// Invoke the more generic hook_entity_view() to allow the same.
module_invoke_all('entity_delete', $work, $this->entityType);
return TRUE;
}
catch (Exception $e) {
if (!empty($started_transaction)) {
$transaction->rollback();
watchdog_exception($this->entityType, $e);
}
throw $e;
}
}
/**
* (Internal use) Invokes a hook on behalf of the entity.
*
* For hooks that have a respective field API attacher like insert/update/..
* the attacher is called too.
*/
public function invoke($hook, $entity) {
if (!empty($this->entityInfo['fieldable']) && function_exists($function = 'field_attach_' . $hook)) {
$function($this->entityType, $entity);
}
// Invoke the hook. If rules is there, use the rule funtion so that a rules
// event is invoked too.
if (module_exists('rules')) {
rules_invoke_all($this->entityType . '_' . $hook, $entity);
}
else {
module_invoke_all($this->entityType . '_' . $hook, $entity);
}
// Invoke the respective entity level hook.
if ($hook == 'presave' || $hook == 'insert' || $hook == 'update' || $hook == 'delete') {
module_invoke_all('entity_' . $hook, $entity, $this->entityType);
}
}
/**
* Permanently saves the given entity.
*
* In case of failures, an exception is thrown.
*
* @param $entity
* The entity to save.
* @param $transaction
* An optional transaction object to pass thru. If passed the caller is
* responsible for rolling back the transaction if something goes wrong.
*
* @return
* SAVED_NEW or SAVED_UPDATED depending on the operation performed.
*/
public function save($work, DatabaseTransaction $transaction = NULL) {
if (!isset($transaction)) {
$transaction = db_transaction();
$started_transaction = TRUE;
}
try {
global $user;
// Determine if we will be inserting a new work.
$work->is_new = empty($work->id);
// Set the timestamp fields.
if (empty($work->created)) {
$work->created = REQUEST_TIME;
}
$work->changed = REQUEST_TIME;
$work->revision_timestamp = REQUEST_TIME;
$update_work = TRUE;
// Give modules the opportunity to prepare field data for saving.
field_attach_presave('work', $work);
// Set the enhanced flag on the work.
$work->enhanced = $this->checkEnhanced($work);
// When saving a new work revision, unset any existing $work->vid
// to ensure a new revision will actually be created and store the old
// revision ID in a separate property for work hook implementations.
if (!$work->is_new && !empty($work->revision) && $work->vid) {
$work->old_vid = $work->vid;
unset($work->vid);
}
// If this is a new work...
if ($work->is_new) {
// Save the new work.
$ret = drupal_write_record('work', $work);
// Save the alternative titles in the joined table.
foreach ($work->alternative_titles as $title) {
$title->id = $work->id;
drupal_write_record('work_title', $title);
}
// Save the descriptions
foreach ($work->descriptions as $description) {
$description->id = $work->id;
drupal_write_record('work_description', $description);
}
// Save the initial revision.
$this->saveRevision($work, $user->uid);
$op = 'insert';
}
else {
// Save the updated work.
$ret = drupal_write_record('work', $work, 'id');
// Save the alternative titles. Delete all the existing titles first
db_delete('work_title')
->condition('id', $work->id)
->execute();
// and save the new descriptions
foreach ($work->alternative_titles as $title) {
$title->id = $work->id;
drupal_write_record('work_title', $title);
}
// Save the work descriptions.
// Delete any existing descriptions first
db_delete('work_description')
->condition('id', $work->id)
->execute();
// and save the new descriptions
foreach ($work->descriptions as $description) {
$description->id = $work->id;
drupal_write_record('work_description', $description);
}
// If a new work revision was requested, save a new record for that;
// otherwise, update the work revision record that matches the value
// of $work->vid.
if (!empty($work->revision)) {
$this->saveRevision($work, $user->uid);
}
else {
$this->saveRevision($work, $user->uid, TRUE);
$update_work = FALSE;
}
$op = 'update';
}
// If the revision ID is new or updated, save it to the work.
if ($update_work) {
db_update('work')
->fields(array('vid' => $work->vid))
->condition('id', $work->id)
->execute();
}
// Save fields.
$function = 'field_attach_' . $op;
$function('work', $work);
module_invoke_all('entity_' . $op, $work, 'work');
// Clear internal properties.
unset($work->is_new);
// Ignore slave server temporarily to give time for the saved order to be
// propagated to the slave.
db_ignore_slave();
// return the value returned by drupal_write_record
return $ret;
}
catch (Exception $e) {
$transaction->rollback();
watchdog_exception('work', $e, NULL, WATCHDOG_ERROR);
return FALSE;
}
}
/**
* Saves an work revision with the uid of the current user.
*
* @param $work
* The fully loaded work object.
* @param $uid
* The user's uid for the current revision.
* @param $update
* TRUE or FALSE indicating whether or not the existing revision should be
* updated instead of a new one created.
*/
private function saveRevision($work, $uid, $update = FALSE) {
// Hold on to the work's original creator_uid but swap in the revision's
// creator_uid for the momentary write.
$work->uid = 1; // hard coded for import
$temp_uid = $work->uid;
$work->uid = $uid;
// Update the existing revision if specified.
if ($update) {
drupal_write_record('work_revision', $work, 'vid');
}
else {
// Otherwise insert a new revision. This will automatically update $work
// to include the vid.
drupal_write_record('work_revision', $work);
}
// Reset the order's creator_uid to the original value.
$work->uid = $temp_uid;
}
/**
* Create a default work.
*
* @param $type
* The machine-readable type of the work.
*
* @return
* An work object with all default fields initialized.
*/
public function create(array $values = array()) {
return (object) array(
'id' => '',
'type' => $values['type'],
'title' => '',
);
}
/**
* Implements EntityAPIControllerInterface.
*/
public function export($entity, $prefix = '') {
throw new Exception('Not implemented');
}
/**
* Implements EntityAPIControllerInterface.
*/
public function import($export) {
throw new Exception('Not implemented');
}
/**
* Builds a structured array representing the entity's content.
*
* The content built for the entity will vary depending on the $view_mode
* parameter.
*
* @param $entity
* An entity object.
* @param $view_mode
* View mode, e.g. 'full', 'teaser'...
* @param $langcode
* (optional) A language code to use for rendering. Defaults to the global
* content language of the current request.
* @return
* The renderable array.
*/
public function buildContent($entity, $view_mode = 'full', $langcode = NULL, $content = array()) {
// Remove previously built content, if exists.
$entity->content = $content;
$langcode = isset($langcode) ? $langcode : $GLOBALS['language_content']->language;
// Add in fields.
if (!empty($this->entityInfo['fieldable'])) {
$entity->content += field_attach_view($this->entityType, $entity, $view_mode, $langcode);
}
if (module_exists('rules')) {
// Invoke hook_ENTITY_view() to allow modules to add their additions.
rules_invoke_all($this->entityType . '_view', $entity, $view_mode, $langcode);
}
// Invoke the more generic hook_entity_view() to allow the same.
module_invoke_all('entity_view', $entity, $this->entityType, $view_mode, $langcode);
// Remove the build array information from the entity and return it.
$build = $entity->content;
unset($entity->content);
return $build;
}
/**
* Generate an array for rendering the given entities.
*
* @param $entities
* An array of entities to render.
* @param $view_mode
* View mode, e.g. 'full', 'teaser'...
* @param $langcode
* (optional) A language code to use for rendering. Defaults to the global
* content language of the current request.
* @param $page
* (optional) If set will control if the entity is rendered: if TRUE
* the entity will be rendered without its title, so that it can be embeded
* in another context. If FALSE the entity will be displayed with its title
* in a mode suitable for lists.
* If unset, the page mode will be enabled if the current path is the URI
* of the entity, as returned by entity_uri().
* This parameter is only supported for entities which controller is a
* EntityAPIControllerInterface.
* @return
* The renderable array.
*/
public function view($entities, $view_mode = '', $langcode = NULL, $page = NULL) {
// Ensure that the array of entities is keyed by entity_id.
$entities = entity_key_array_by_property($entities, 'id');
// If no view mode is specified, use the first one available..
if (!isset($this->entityInfo['view modes'][$view_mode])) {
reset($this->entityInfo['view modes']);
$view_mode = key($this->entityInfo['view modes']);
}
if (!empty($this->entityInfo['fieldable'])) {
field_attach_prepare_view($this->entityType, $entities, $view_mode);
}
entity_prepare_view($this->entityType, $entities);
$langcode = isset($langcode) ? $langcode : $GLOBALS['language_content']->language;
$view = array();
// Build the content array for each entity passed in.
foreach ($entities as $key => $entity) {
$build = entity_build_content($this->entityType, $entity, $view_mode, $langcode);
// Add default properties to the array to ensure the content is passed
// through the theme layer.
$build += array(
'#theme' => 'entity',
'#entity_type' => $this->entityType,
'#entity' => $entity,
'#view_mode' => $view_mode,
'#language' => $langcode,
'#page' => $page,
);
// Allow modules to modify the structured entity.
drupal_alter(array($this->entityType . '_view', 'entity_view'), $build, $this->entityType);
$view[$this->entityType][$key] = $build;
}
return $view;
}
public function load($ids = array(), $conditions = array()) {
// Use the inherited load method to load the field from the standard
// schema table.
$works = parent::load($ids, $conditions);
foreach ($works as &$work) {
// Alternative titles are stored in a seperate table. Load them here
// and add to the work object.
$alternative_titles = db_select('work_title', 'ewt')
->fields('ewt', array('type', 'title'))
->condition('id', $work->id, '=')
->execute()
->fetchAll();
$work->alternative_titles = $alternative_titles;
// Load the descriptions and add to the work object.
$descriptions = db_select('work_description', 'wd')
->fields('wd', array('type', 'body', 'date'))
->condition('id', $work->id, '=')
->execute()
->fetchAll();
/**
* CID descriptions have hard linefeeds - removed single occurances of
* them and replace multiple occurances with a paragraph break.
*/
foreach ($descriptions as &$description) {
// replace all linefeeds/carridge returns etc with <br /> tags
$linefeeds = array("\r\n", "\n", "\r");
$replace = '<br />';
$description->body = str_replace($linefeeds, $replace, $description->body);
// replace multiple occurances of </ br> with </p><p>
$description->body = preg_replace('/(\s*<br\s*\/>\s*){2,}/', '</p><p>', $description->body);
// replace single occurances fo </br> with ' '
$description->body = '<p>' . str_replace('<br />', ' ', $description->body) . '</p>';
}
$work->descriptions = $descriptions;
}
return $works;
}
/**
* A private function to check if a work has been enhanced in drupal.
*
* 'Enhanced' is currently defined as any work entity that has a value in any
* of the introduction,synopsis or one_liner fields.
*/
private function checkEnhanced($work) {
if (! empty($work->introduction['und'])) {
return 1;
}
if (! empty($work->synopsis['und'])) {
return 1;
}
if (! empty($work->one_liner['und'])) {
return 1;
}
return 0;
}
}