Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
hipo_job_purge_trash.inc
1 <?php
17 require_once SQ_SYSTEM_ROOT.'/core/hipo/hipo_job.inc';
18 
33 {
34 
35 
41  function HIPO_Job_Purge_Trash($code_name='')
42  {
43  // we'll do our own transactions thanks
44  $this->uses_trans = FALSE;
45 
46  $this->HIPO_Job($code_name);
47 
48  }//end constructor
49 
50 
60  public static function paintConfig(&$o, $class, $write_access)
61  {
62  // metadata regeneration threshhold HIPO config entry
63  $o->openField(translate('purge_trash_threshold'));
64 
65  if ($write_access) {
66  text_box($class.'[SQ_HIPO_PURGE_TRASH_THRESHOLD]', SQ_HIPO_PURGE_TRASH_THRESHOLD, 5);
67  } else {
68  echo SQ_HIPO_PURGE_TRASH_THRESHOLD;
69  }
70  echo ' '.translate('assets');
71 
72  $o->closeField();
73 
74  }//end paintConfig()
75 
76 
83  public static function getConfigVars()
84  {
85  return Array(
86  'SQ_HIPO_PURGE_TRASH_THRESHOLD' => Array('editable' => 1, 'default' => 1),
87  );
88 
89  }//end getConfigVars()
90 
91 
98  function getHipoName()
99  {
100  return translate('hipo_name_purge_trash');
101 
102  }//end getHipoName()
103 
104 
113  {
114  return Array(
115  Array(
116  'name' => translate('hipo_purging_trash_determining'),
117  'function_call' => Array(
118  'process_function' => 'buildListToDelete',
119  ),
120  'running_mode' => 'server',
121  'auto_step' => TRUE,
122  'allow_cancel' => TRUE,
123  'percent_done' => 0,
124  'complete' => FALSE,
125  'message' => '',
126  ),
127  Array(
128  'name' => translate('hipo_purging_trash_deleting'),
129  'function_call' => Array(
130  'process_function' => 'purgeAsset',
131  ),
132  'running_mode' => 'server',
133  'auto_step' => TRUE,
134  'allow_cancel' => TRUE,
135  'percent_done' => 0,
136  'complete' => FALSE,
137  'message' => '',
138  ),
139  Array(
140  'name' => translate('hipo_purging_trash_cleaning'),
141  'function_call' => Array(
142  'process_function' => 'deleteRemainingTrashLinks',
143  ),
144  'running_mode' => 'server',
145  'auto_step' => TRUE,
146  'allow_cancel' => TRUE,
147  'percent_done' => 0,
148  'complete' => FALSE,
149  'message' => '',
150  ),
151  );
152 
153  }//end getInitialStepData()
154 
155 
166  {
167  if (SQ_HIPO_PURGE_TRASH_THRESHOLD == 0) return 0;
168  if (!isset($this->_running_vars['to_check'])) {
169  return 0;
170  }
171  return ((count($this->_running_vars['to_check']) / SQ_HIPO_PURGE_TRASH_THRESHOLD) * 100);
172 
173  }//end getThresholdPercentageRequired()
174 
175 
182  function prepare()
183  {
184  $trash = $GLOBALS['SQ_SYSTEM']->am->getSystemAsset('trash_folder');
185  $this->_running_vars['to_delete'] = Array();
186  $this->_running_vars['trash_id'] = $trash->id;
187  $asset_tree_ids = $GLOBALS['SQ_SYSTEM']->am->getAssetTreeids($trash->id);
188  $this->_running_vars['trash_treeid'] = reset($asset_tree_ids);
189 
190  if (!empty($this->_running_vars['purge_root_linkid'])) {
191 
192  $purge_root_link = $GLOBALS['SQ_SYSTEM']->am->getLinkById($this->_running_vars['purge_root_linkid']);
193  $this->_running_vars['purge_root_id'] = $purge_root_link['minorid'];
194  $purge_root = $GLOBALS['SQ_SYSTEM']->am->getAsset($this->_running_vars['purge_root_id']);
195 
196  // is the root in the trash?
197  if (!$GLOBALS['SQ_SYSTEM']->am->assetInTrash($purge_root->id)) {
198  trigger_localised_error('HIPO0083', E_USER_WARNING);
199  return FALSE;
200  }
201  $purge_root_children = $GLOBALS['SQ_SYSTEM']->am->getChildren($purge_root->id);
202  // also delete the purge root
203  $purge_root_children[$purge_root->id][0]['type_code'] = $purge_root->type();
204  $link_tree_ids = $GLOBALS['SQ_SYSTEM']->am->getLinkTreeId($this->_running_vars['purge_root_linkid']);
205  $root_tree_id = reset($link_tree_ids);
206  $this->_running_vars['purge_root_treeid'] = $root_tree_id[0][0];
207  } else {
208  $purge_root = $trash;
209  $purge_root_children = $GLOBALS['SQ_SYSTEM']->am->getChildren($purge_root->id);
210  $this->_running_vars['purge_root_treeid'] = $this->_running_vars['trash_treeid'];
211  }
212 
213  $db = MatrixDAL::getDb();
214  try {
215  $bind_vars['treeid'] = $this->_running_vars['purge_root_treeid'].'%';
216  $this->_running_vars['delete_order'] = MatrixDAL::executeAssoc('core', 'getPurgeTrashHipoDeleteOrder', 0, $bind_vars);
217  } catch (Exception $e) {
218  throw new Exception('Failed to get delete order for purge trash HIPO: '.$e->getMessage());
219  }
220 
221  // Sort the 'to_check' assetid list by asset tree level. The parent assets will come on top of the array.
222  $ordered_to_check_list = Array();
223  foreach ($this->_running_vars['delete_order'] as $assetid) {
224  if (isset($purge_root_children[$assetid])) {
225  $ordered_to_check_list[$assetid] = $purge_root_children[$assetid];
226  }
227  }
228  $this->_running_vars['to_check'] = $ordered_to_check_list;
229  $this->_running_vars['total_assets'] = count($this->_running_vars['to_check']);
230 
231  return parent::prepare();
232 
233  }//end prepare()
234 
235 
242  function freestyle()
243  {
244  while (!empty($this->_running_vars['to_check'])) {
245  if (!$this->buildListToDelete($this->_steps[0], $this->getCodeName())) {
246  return FALSE;
247  }
248  }
249 
250  while (!empty($this->_running_vars['to_delete'])) {
251  if (!$this->purgeAsset($this->_steps[1], $this->getCodeName())) {
252  return FALSE;
253  }
254  }
255 
256  if (!$this->deleteRemainingTrashLinks($this->_steps[2], $this->getCodeName())) {
257  return FALSE;
258  }
259 
260  return TRUE;
261 
262  }//end freestyle()
263 
264 
274  function buildListToDelete(&$step_data, $prefix)
275  {
276  $GLOBALS['SQ_PURGING_TRASH'] = TRUE;
277  if (!empty($this->_running_vars['to_check'])) {
278  reset($this->_running_vars['to_check']);
279  list($assetid, $type_code) = each($this->_running_vars['to_check']);
280  unset($this->_running_vars['to_check'][$assetid]);
281 
282  if (strpos($assetid, ':') === FALSE) {
283  $db = MatrixDAL::getDb();
284  try {
285  $bind_vars = Array(
286  'treeid' => $this->_running_vars['purge_root_treeid'].'%',
287  'assetid' => $assetid,
288  );
289  $num_external_links = MatrixDAL::executeOne('core', 'getPurgeTrashHipoLinkCount', $bind_vars);
290  } catch (Exception $e) {
291  throw new Exception('Failed to get asset external link count for purge trash HIPO: '.$e->getMessage());
292  $num_external_links = -1;
293  }
294 
295  if ($num_external_links == 0) {
296  $this->_running_vars['to_delete'][$assetid] = $type_code;
297  } else if ($num_external_links > 0) {
298  // This asset has links outside the Trash, therefore all of its children have external links as well.
299  // We remove its children's assetids from the 'to_check' list so that we do not have to check them later.
300  $children = $GLOBALS['SQ_SYSTEM']->am->getChildren($assetid);
301  foreach ($children as $child_assetid => $type_code) {
302  if (isset($this->_running_vars['to_check'][$child_assetid])) {
303  unset($this->_running_vars['to_check'][$child_assetid]);
304  }
305  }
306  }
307  }
308  }
309 
310  if (empty($this->_running_vars['to_check'])) {
311  $GLOBALS['SQ_PURGING_TRASH'] = FALSE;
312  $step_data['percent_done'] = 100;
313  $step_data['complete'] = TRUE;
314  $this->_running_vars['total_to_delete'] = count($this->_running_vars['to_delete']);
315  } else {
316  $step_data['percent_done'] = (int)((($this->_running_vars['total_assets'] - count($this->_running_vars['to_check'])) / $this->_running_vars['total_assets']) * 100);
317  $step_data['complete'] = FALSE;
318  }
319 
320  return TRUE;
321 
322  }//end buildListToDelete()
323 
324 
334  function purgeAsset(&$step_data, $prefix)
335  {
336  $GLOBALS['SQ_PURGING_TRASH'] = TRUE;
337  if (!empty($this->_running_vars['to_delete'])) {
338 
339  $assetid = NULL;
340  while (!isset($this->_running_vars['to_delete'][$assetid])) {
341  $assetid = array_pop($this->_running_vars['delete_order']);
342  if (is_null($assetid)) break;
343  }
344  if (!is_null($assetid)) {
345  $asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($assetid, $this->_running_vars['to_delete'][$assetid][0]['type_code']);
346  unset($this->_running_vars['to_delete'][$assetid]);
347 
348  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
349  $GLOBALS['SQ_SYSTEM']->doTransaction('BEGIN');
350 
351  // we have an asset ready to delete, but we need to check with
352  // the asset manager that we are allowed to delete it
353  if (!$GLOBALS['SQ_SYSTEM']->am->canPurgeAsset($asset)) {
354  trigger_localised_error('HIPO0004', E_USER_WARNING, $asset->name, $asset->id);
355  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
356  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
357  return FALSE;
358  }
359 
360  if (!$GLOBALS['SQ_SYSTEM']->am->acquireLock($this->_running_vars['trash_id'], 'all') || !$GLOBALS['SQ_SYSTEM']->am->acquireLock($asset->id, 'all', $this->_running_vars['trash_id'])) {
361  trigger_localised_error('HIPO0005', E_USER_WARNING, $asset->name, $asset->id);
362  $GLOBALS['SQ_SYSTEM']->am->releaseLock($this->_running_vars['trash_id'], 'all');
363  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
364  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
365  return FALSE;
366  }
367 
368  // Get all the shadow asset links in which this asset is the major party and delete it
369  $shadow_links = $GLOBALS['SQ_SYSTEM']->am->getShadowLinkByAsset($asset->id, NULL, NULL, NULL, 'major', TRUE);
370  foreach($shadow_links as $shadow_link) {
371  $GLOBALS['SQ_SYSTEM']->am->deleteShadowAssetLink($shadow_link['linkid']);
372  }
373 
374  // because we delete the deepest assets first, it's safe to assume
375  // that anything linked under this asset is also linked outside the
376  // trash (or it would already have been deleted itself), so we will
377  // now delete all links in which this asset is the major party
378  $links = $GLOBALS['SQ_SYSTEM']->am->getLinks($asset->id, SQ_SC_LINK_ALL, '', TRUE);
379  foreach ($links as $link) {
380  $GLOBALS['SQ_SYSTEM']->am->deleteAssetLink($link['linkid'], FALSE);
381  }
382 
383  // the delete function will remove all links in which the asset is the minor party
384  if (!$asset->delete(FALSE, FALSE)) {
385  trigger_localised_error('HIPO0004', E_USER_WARNING, $asset->name, $asset->id);
386  $GLOBALS['SQ_SYSTEM']->am->releaseLock($asset->id, 'all');
387  $GLOBALS['SQ_SYSTEM']->am->releaseLock($this->_running_vars['trash_id'], 'all');
388  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
389  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
390  return FALSE;
391  }
392 
393  $GLOBALS['SQ_SYSTEM']->am->releaseLock($this->_running_vars['trash_id'], 'all');
394 
395  $step_data['message'] = translate('hipo_deleting', htmlentities($asset->name, ENT_COMPAT, SQ_CONF_DEFAULT_CHARACTER_SET), $assetid);
396  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($asset);
397 
398  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
399  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
400  }//end if
401  }//end if
402 
403  if (empty($this->_running_vars['to_delete'])) {
404  $step_data['percent_done'] = 100;
405  $step_data['complete'] = TRUE;
406  } else {
407  $step_data['percent_done'] = (int)((($this->_running_vars['total_to_delete'] - count($this->_running_vars['to_delete'])) / $this->_running_vars['total_to_delete']) * 100);
408  $step_data['complete'] = FALSE;
409  }
410 
411  $GLOBALS['SQ_PURGING_TRASH'] = FALSE;
412  return TRUE;
413 
414  }//end purgeAsset()
415 
416 
428  function deleteRemainingTrashLinks(&$step_data, $prefix)
429  {
430  $GLOBALS['SQ_PURGING_TRASH'] = TRUE;
431  $links = Array();
432  if (empty($this->_running_vars['purge_root_id'])) {
433  $links = $GLOBALS['SQ_SYSTEM']->am->getLinks($this->_running_vars['trash_id'], SQ_SC_LINK_ALL);
434  } else {
435  // if the root node has not been deleted, the selected purge root link should be.
436  $links[] = $GLOBALS['SQ_SYSTEM']->am->getLinkById($this->_running_vars['purge_root_linkid'], $this->_running_vars['purge_root_id'], 'minor');
437  }
438 
439  // We reverse the array to start deleting the assets with the highest sort-order.
440  // This is *much* faster because we don't have to update the sort order
441  // of all the other assets under the trash each time
442  foreach (array_reverse($links) as $link) {
443  // Extra check to make sure the link is under trash exclusively before deleting the link itself
444  $link_info = $GLOBALS['SQ_SYSTEM']->am->getLinkById($link['linkid']);
445  if (!is_null($link_info['minorid'])) {
446  $under_trash = $GLOBALS['SQ_SYSTEM']->am->getLinkByAsset($this->_running_vars['trash_id'], $link_info['minorid']);
447  if ($GLOBALS['SQ_SYSTEM']->am->assetInTrash($link_info['minorid'], TRUE) || !empty($under_trash)) {
448  // we know it has other links so no new trash link will be created
449  $GLOBALS['SQ_SYSTEM']->am->deleteAssetLink($link['linkid'], FALSE);
450  }//end if
451  } else {
452  $GLOBALS['SQ_SYSTEM']->am->deleteAssetLink($link['linkid'], FALSE);
453  }//end if
454  }//end foreach
455  $step_data['percent_done'] = 100;
456  $step_data['complete'] = TRUE;
457  $GLOBALS['SQ_PURGING_TRASH'] = FALSE;
458  return TRUE;
459 
460  }//end deleteRemainingTrashLinks()
461 
462 
463 }//end class
464 ?>