Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
remap_manager.inc
1 <?php
17 require_once SQ_INCLUDE_PATH.'/asset.inc';
18 
30 class Remap_Manager extends Asset
31 {
32 
33 
40  function Remap_Manager($assetid=0)
41  {
42  $this->Asset($assetid);
43 
44  }//end constructor
45 
46 
58  public function create(Array &$link)
59  {
60  require_once SQ_CORE_PACKAGE_PATH.'/system/system_asset_fns.inc';
61  if (!system_asset_fns_create_pre_check($this)) {
62  return FALSE;
63  }
64  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
65  $GLOBALS['SQ_SYSTEM']->doTransaction('BEGIN');
66 
67  if ($linkid = parent::create($link)) {
68  if (!system_asset_fns_create_cleanup($this)) {
69  $linkid = FALSE;
70  }
71  }
72 
73  if ($linkid) {
74  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
75  } else {
76  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
77  }
78 
79  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
80  return $linkid;
81 
82  }//end create()
83 
84 
94  protected function _getName($short_name=FALSE)
95  {
96  return $GLOBALS['SQ_SYSTEM']->am->getTypeInfo($this->type(), 'name');
97 
98  }//end _getName()
99 
100 
108  public function _getAllowedLinks()
109  {
110  $page_links = parent::_getAllowedLinks();
111  // added for root node
112  $page_links[SQ_LINK_NOTICE]['asset'] = Array('card' => 'M', 'exclusive' => FALSE);
113  return $page_links;
114 
115  }//end _getAllowedLinks()
116 
117 
124  public function canDelete()
125  {
126  return FALSE;
127 
128  }//end canDelete()
129 
130 
137  public function canClone()
138  {
139  return FALSE;
140 
141  }//end canClone()
142 
143 
152  public function describeLink($linkid)
153  {
154  $link = $GLOBALS['SQ_SYSTEM']->am->getLinkById($linkid);
155  switch (strtolower($link['value'])) {
156  case '404' :
157  return translate('404_remap_description');
158  break;
159  default :
160  return parent::describeLink($linkid);
161  break;
162  }
163 
164  }//end describeLink()
165 
166 
179  public function addRemapURL($old_url, $new_url, $expires=NULL, $never_delete=NULL, $auto=TRUE)
180  {
181  // old url and new url must be strings, makes not sense otherwise
182  if (!is_string($old_url) || !is_string($new_url)) {
183  trigger_localised_error('CORE0093', E_USER_WARNING);
184  return 0;
185  }
186 
187  $old_url = strip_url($old_url);
188  $new_url = strip_url($new_url);
189 
190  // there is no use remapping a URL to itself
191  if (strcmp($old_url, $new_url) == 0) return 1;
192 
193  // can't remap to a blank URL
194  if ($new_url === '') {
195  trigger_localised_error('CORE0271', E_USER_WARNING);
196  return 0;
197  }
198 
199  $db = MatrixDAL::getDb();
200 
201  $sql = 'SELECT remap_url
202  FROM '.SQ_TABLE_RUNNING_PREFIX.'ast_lookup_remap';
203  $where = 'url = :url';
204  $where = $GLOBALS['SQ_SYSTEM']->constructRollbackWhereClause($where);
205 
206  try {
207  $query = MatrixDAL::preparePdoQuery($sql.$where);
208  MatrixDAL::bindValueToPdo($query, 'url', $old_url);
209  $remap_urls = MatrixDAL::executePdoAssoc($query, 0);
210  } catch (Exception $e) {
211  throw new Exception('Unable to get remap URLs for URL: '.$old_url.' due to database error: '.$e->getMessage());
212  }
213 
214  if (in_array($new_url, $remap_urls)) return 1;
215 
216  if (count($remap_urls) > 0) {
217  trigger_localised_error('CORE0024', E_USER_WARNING, $old_url);
218  return -1;
219  }
220 
221  if ($never_delete === NULL){
222  $never_delete = ($this->attr('never_delete_remap_default') == FALSE) ? 0 : 1;
223  }
224 
225  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
226  $db = MatrixDAL::getDb();
227  $GLOBALS['SQ_SYSTEM']->doTransaction('BEGIN');
228 
229  // check if the new URL is already remapping to something else, and delete it
230  $sql = 'DELETE FROM '.SQ_TABLE_RUNNING_PREFIX.'ast_lookup_remap';
231  $where = ' WHERE url = :url';
232 
233  try {
234  $query = MatrixDAL::preparePdoQuery($sql.$where);
235  MatrixDAL::bindValueToPdo($query, 'url', $new_url);
236  MatrixDAL::execPdoQuery($query);
237  } catch (Exception $e) {
238  throw new Exception('Unable to delete old remap URLs for URL: '.$new_url.' due to database error: '.$e->getMessage());
239  }
240 
241  try {
242  $bind_vars = Array (
243  'url' => $old_url,
244  'remap_url' => $new_url,
245  'expires' => (is_null($expires)) ? $expires : ts_iso8601($expires),
246  'never_delete' => $never_delete,
247  'auto_remap' => ($auto === TRUE) ? '1' : (($auto === FALSE) ? '0' : NULL),
248  );
249  $type_codes = MatrixDAL::executeQuery('core', 'insertRemap', $bind_vars);
250  } catch (Exception $e) {
251  throw new Exception('Unable to insert new remap from: '.$old_url.' to: '.$new_url.' due to database error: '. $e->getMessage());
252  }
253 
254  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
255  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
256 
257  return 1;
258 
259  }//end addRemapURL()
260 
261 
270  public function getRemapURLs(Asset $asset)
271  {
272  $null = NULL;
273  if (is_null($asset)) return $null;
274 
275  $urls = $asset->getURLs();
276  $remaps = Array();
277 
278  if (!empty($urls)) {
279 
280  $bind_vars = Array();
281 
282  // This sub-query gets all URLs for this asset that have HTTP enabled
283  // and adds http protocol to them to create a usable URL - then same
284  // or similar for HTTPS enabled.
285  $sql = 'SELECT url, remap_url, expires, never_delete, auto_remap
286  FROM '.SQ_TABLE_RUNNING_PREFIX.'ast_lookup_remap';
287  $union_select = '(SELECT
288  (\'http://\' || url)
289  FROM sq_ast_lookup
290  '.$GLOBALS['SQ_SYSTEM']->constructRollbackWhereClause('http = \'1\'
291  AND assetid = :assetid').')
292  UNION
293  (SELECT
294  (\'https://\' || url)
295  FROM sq_ast_lookup
296  '.$GLOBALS['SQ_SYSTEM']->constructRollbackWhereClause('https = \'1\'
297  AND assetid = :assetid_1').')';
298 
299  $bind_vars['assetid'] = $asset->id;
300  $bind_vars['assetid_1'] = $asset->id;
301  $where = 'remap_url IN ('.$union_select.')';
302  $where = $GLOBALS['SQ_SYSTEM']->constructRollbackWhereClause($where);
303 
304  try {
305  $query = MatrixDAL::preparePdoQuery($sql.$where);
306  foreach ($bind_vars as $bind_var => $bind_value) {
307  MatrixDAL::bindValueToPdo($query, $bind_var, $bind_value);
308  }
309  $remaps = MatrixDAL::executePdoAssoc($query);
310  } catch (Exception $e) {
311  throw new Exception('Unable to get remap URLs for this asset: '.$asset->id.' due to database error: '.$e->getMessage());
312  }
313 
314  }//end if
315 
316  return $remaps;
317 
318 
319  }//end getRemapURLs()
320 
321 
332  public function remapAllURLs($assetid, $new_url, $expires=NULL)
333  {
334  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
335 
336  try {
337  $bind_vars = Array('assetid' => $assetid);
338  $urls = MatrixDAL::executeAll('core', 'getLookupUrlsByAssetId', $bind_vars);
339  } catch (DALException $e) {
340  throw new Exception('Unable to remap all URLs; could not get existing lookups for asset ID #'.$assetid.' due to database error: '.$e->getMessage);
341  }
342 
343  $GLOBALS['SQ_SYSTEM']->doTransaction('BEGIN');
344 
345  $protocols = Array ( 'https' => FALSE, 'http' => FALSE );
346  $never_delete = ($this->attr('never_delete_remap_default') == FALSE) ? 0 : 1;
347 
348  foreach ($urls as $url_data) {
349  if ($url_data['http']) {
350  $retval = @$this->addRemapURL('http://'.$url_data['url'], $new_url, $expires, $never_delete, FALSE);
351  if ($retval === 0) {
352  trigger_localised_error('CORE0039', E_USER_WARNING, $assetid, $new_url);
353  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
354  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
355  return FALSE;
356  }
357  $protocols['http'] = TRUE;
358  }
359  if ($url_data['https']) {
360  $retval = @$this->addRemapURL('https://'.$url_data['url'], $new_url, $expires, $never_delete, FALSE);
361  if ($retval === 0) {
362  trigger_localised_error('CORE0039', E_USER_WARNING, $assetid, $new_url);
363  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
364  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
365  return FALSE;
366  }
367  $protocols['https'] = TRUE;
368  }
369  }
370 
371  //Feature #4759: Asset ID to URL remaps.
372  foreach ($protocols as $protocol => $enabled){
373  if ($enabled){
374  //try deleting first as we want to be able to change remap entries for the assetid.
375  try {
376  $bind_vars = Array('urls' => Array($protocol.'://?a='.$assetid));
377  MatrixDAL::executeQuery('remap_manager', 'deleteRemapsByURL', $bind_vars);
378  } catch (DALException $e) {
379  throw new Exception('Unable to delete assetid remaps due to database error: '.$e->getMessage());
380  }
381  $retval = @$this->addRemapURL($protocol.'://?a='.$assetid, $new_url, $expires, $never_delete, FALSE);
382  if ($retval !== 1) {
383  trigger_localised_error('CORE0040', E_USER_WARNING, $assetid, $new_url);
384  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
385  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
386  return FALSE;
387  }
388  }
389  }
390 
391  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
392  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
393  return TRUE;
394 
395  }//end remapAllURLs()
396 
405  public function loadRemapFromCurrentURL()
406  {
407  $protocol = current_protocol();
408 
409  // URL without parameters
410  $url = strip_url(current_url(FALSE, TRUE));
411 
412  // This URL is verbatim
413  $url_with_params = strip_url($_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']);
414 
415  // Try verbatim URL first
416  if($this->loadRemapFromURL($protocol, $url_with_params))
417  return TRUE;
418 
419  if(!$this->attr("preserve_url_parameters")) {
420  // If the urls are the same with and without params, there's no
421  // need to check anything else.
422  if ($url === $url_with_params) {
423  return FALSE;
424  }
425 
426  // Try URL without paramters
427  return($this->loadRemapFromURL($protocol, $url));
428  }
429 
430  // Try URL, and preserve all parameters when redirecting
431  $parameters = '';
432  $pos = strpos($url_with_params, '?');
433  if($pos !== FALSE) {
434  $parameters = substr($url_with_params, $pos + 1);
435  }
436 
437  // Try URL and preserve all paramters
438  if (!empty($parameters)) {
439  if($this->loadRemapFromURL($protocol, $url, $parameters)) {
440  return TRUE;
441  }
442  }
443 
444  return FALSE;
445  }
446 
458  public function loadRemapFromURL($protocol=NULL, $url=NULL, $parameters='')
459  {
460  if (is_null($protocol)) $protocol = current_protocol();
461  if (is_null($url)) {
462  $url = strip_url(current_url(FALSE, TRUE));
463  }
464 
465  $url = $protocol.'://'.$url;
466  $time = ts_iso8601(time());
467 
468  // there is URL parameters to pass
469  if(!empty($parameters)) {
470  // Try URL + ?, if match is found, this URL is marked as an exception that no need of preserving parameters
471  try {
472 
473  $bind_vars = Array(
474  'url' => $url,
475  'url_with_trailing_question_mark' => $url.'?',
476  'time' => $time,
477  );
478  $results = MatrixDAL::executeAssoc('remap_manager', 'getCurrentRemapURLTrailingQuestionMark', $bind_vars);
479  } catch (DALException $e) {
480  throw new Exception('Unable to get current remap URL for "'.$url.'" due to database error: '.$e->getMessage());
481  }
482 
483  if(empty($results)) return FALSE;
484 
485  $remap_url = $results[0]['remap_url'];
486 
487  // if this query returns us a plain url (not with trailing '?'), we should pass URL parameters to remapped url
488  if($results[0]['url'] === $url) {
489  // preserve parameters to remapped url
490  if(strpos($remap_url, '?') !== FALSE) {
491  $remap_url = $remap_url.'&'.$parameters;
492  }
493  else {
494  $remap_url = $remap_url.'?'.$parameters;
495  }
496  }
497  }
498  else {
499  // no URL parameters to pass
500  try {
501 
502  $bind_vars = Array(
503  'url' => $url,
504  'time' => $time,
505  );
506  $remap_url = MatrixDAL::executeOne('remap_manager', 'getCurrentRemapURL', $bind_vars);
507  } catch (DALException $e) {
508  throw new Exception('Unable to get current remap URL for "'.$url.'" due to database error: '.$e->getMessage());
509  }
510  }
511 
512  // if we have a remap url, display it
513  if (!empty($remap_url)) {
514 
515  header('HTTP/1.1 301 Moved Permanently');
516  $cm = $GLOBALS['SQ_SYSTEM']->am->getSystemAsset('cache_manager');
517  // Send Cacheable Header based on cache manager default setting
518  if (SQ_CONF_SEND_CACHEABLE_HEADER && $cm->cacheableHeadersEnabledForCurrentProtocol()) {
519  header('Pragma: cache');
520 
521  $browser_cache_expiry = $cm->attr('browser_cache_expiry');
522  if (empty($browser_cache_expiry)) {
523  $browser_cache_expiry = $cm->attr('expiry');
524  }
525 
526  header('Cache-Control: max-age='.$browser_cache_expiry.', '.$cm->cacheControlLevel());
527  header('Expires: '.gmdate('D, d M Y H:i:s', time() + $browser_cache_expiry).' GMT');
528  }
529  header('Location: '.$remap_url);
530 
531  return TRUE;
532  }
533 
534  return FALSE;
535 
536  }//end loadRemapFromURL()
537 
547  public function getRemapFromURL($protocol=NULL, $url=NULL)
548  {
549  if (is_null($protocol)) $protocol = current_protocol();
550  if (is_null($url)) {
551  $url = strip_url(current_url(FALSE, TRUE));
552  }
553 
554  $url = $protocol.'://'.$url;
555 
556  try {
557  $time = ts_iso8601(time());
558  $bind_vars = Array(
559  'url' => $url,
560  'time' => $time,
561  );
562  $remap_url = MatrixDAL::executeOne('remap_manager', 'getCurrentRemapURL', $bind_vars);
563  } catch (DALException $e) {
564  throw new Exception('Unable to get current remap URL for "'.$url.'" due to database error: '.$e->getMessage());
565  }
566 
567  // if remap is found, keeping finding its remap until the final remap
568  if (!empty($remap_url)) {
569  $protocol = parse_url($remap_url, PHP_URL_SCHEME);
570  $search = '#'.$protocol.'\\://#';
571  $url = preg_replace($search, '', $remap_url);
572 
573  // find the final remap url recursively
574  $final_url = $this->getRemapFromURL($protocol, $url);
575  if ($final_url !== FALSE) $remap_url = $final_url;
576  return $remap_url;
577  }
578  return FALSE;
579 
580  }//end getRemapFromURL()
581 
589  public function getPageNotFoundAsset()
590  {
591  // searching for the root asset
592  $link_to_404 = $GLOBALS['SQ_SYSTEM']->am->getLink($this->id, SQ_LINK_NOTICE, '', FALSE, '404');
593 
594  if (empty($link_to_404)) {
595  $asset = NULL;
596  } else {
597  $asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($link_to_404['minorid']);
598  }
599 
600  return $asset;
601 
602  }//end getPageNotFoundAsset()
603 
604 
615  public function paintPageNotFoundRemap()
616  {
617  $asset = $this->getPageNotFoundAsset();
618  if (is_null($asset)) return FALSE;
619 
620  // send a 404 header
621  header('HTTP/1.0 404 Not Found');
622 
623  $url = strip_url($this->getURL(), TRUE);
624  $asset_url = strip_url($asset->getURL(NULL, TRUE), TRUE);
625 
626  global $ASSET_LINEAGE;
627  $ASSET_LINEAGE = $GLOBALS['SQ_SYSTEM']->am->getLineageFromUrl(NULL, $asset_url);
628 
629  $design = $this->getDesign($asset, $url);
630  if (!is_null($design)) {
631  $design->paint($asset);
632  return TRUE;
633  }
634  // get the first design for this asset
635  $design = $this->getDesign($asset);
636  if (!is_null($design)) {
637  $design->paint($asset);
638  return TRUE;
639  }
640  // give up, and just paint the asset
641  // without a design
642  $asset->printFrontend();
643 
644  return TRUE;
645 
646  }//end paintPageNotFoundRemap()
647 
648 
661  public function getDesign(Asset $asset, $url=NULL)
662  {
663  $null = NULL;
664  if (is_null($asset)) return $null;
665 
666  $bind_vars = Array();
667 
668  // get original design or inherited design if no override design
669  $sql = 'SELECT lv.value as designid, a.type_code
670  FROM '.SQ_TABLE_RUNNING_PREFIX.'ast_lookup l
671  INNER JOIN '.SQ_TABLE_RUNNING_PREFIX.'ast_lookup_value lv ON ((l.url = lv.url) OR (l.url || \'/\' = lv.url))
672  INNER JOIN '.SQ_TABLE_RUNNING_PREFIX.'ast a ON lv.value = a.assetid
673  ';
674  $where = 'l.assetid = :assetid AND lv.name = :lookup_name';
675 
676  $bind_vars['assetid'] = $asset->id;
677  $bind_vars['lookup_name'] = 'design::system::frontend';
678 
679  if (!is_null($url)) {
680  $where .= ' AND lv.url LIKE :url';
681  $bind_vars['url'] = $url.'%';
682  }
683 
684  $order_by = ' ORDER BY depth DESC';
685  $where = $GLOBALS['SQ_SYSTEM']->constructRollbackWhereClause($where, 'lv');
686  $where = $GLOBALS['SQ_SYSTEM']->constructRollbackWhereClause($where, 'l');
687  $where = $GLOBALS['SQ_SYSTEM']->constructRollbackWhereClause($where, 'a');
688  $sql .= $where.$order_by;
689  $sql = db_extras_modify_limit_clause($sql, MatrixDAL::getDbType(), 1);
690 
691  try {
692  $query = MatrixDAL::preparePdoQuery($sql);
693  foreach ($bind_vars as $bind_var => $bind_value) {
694  MatrixDAL::bindValueToPdo($query, $bind_var, $bind_value);
695  }
696  $result = MatrixDAL::executePdoAll($query);
697  } catch (DALException $e) {
698  throw new Exception('Unable to select design of remapped asset "'.$asset->name.'" (#'.$asset->id.') due to database error: '.$e->getMessage());
699  }
700 
701  if ($result) {
702  return $GLOBALS['SQ_SYSTEM']->am->getAsset($result[0]['designid'], $result[0]['type_code']);
703  }
704  return $null;
705 
706  }//end getDesign()
707 
708 
709 }//end class
710 
711 ?>