· 9 years ago · Jan 10, 2017, 05:46 AM
1<?php
2/**
3 * <pre>
4 * Invision Power Services
5 * IP.Board v3.4.6
6 * Hooks Management
7 * Last Updated: $Date: 2013-05-20 16:38:23 -0400 (Mon, 20 May 2013) $
8 * </pre>
9 *
10 * @author $Author: bfarber $
11 * @copyright (c) 2001 - 2009 Invision Power Services, Inc.
12 * @license http://www.invisionpower.com/company/standards.php#license
13 * @package IP.Board
14 * @subpackage Core
15 * @link http://www.invisionpower.com
16 * @since 3.0.0
17 * @version $Revision: 12261 $
18 */
19
20if ( ! defined( 'IN_ACP' ) )
21{
22 print "<h1>Incorrect access</h1>You cannot access this file directly. If you have recently upgraded, make sure you upgraded 'admin.php'.";
23 exit();
24}
25
26class admin_core_applications_hooks extends ipsCommand
27{
28 /**
29 * Skin object
30 *
31 * @var object Skin templates
32 */
33 protected $html;
34
35 /**
36 * Hooks library
37 *
38 * @var object Hooks library
39 */
40 protected $hooksFunctions;
41
42 /**
43 * Existing hooks
44 *
45 * @var array Existing hooks
46 */
47 protected $hooks;
48
49 /**
50 * Array of all the cache versions
51 * for the required applications
52 *
53 * @var $cachedVersions
54 */
55 protected $cachedVersions = array();
56
57 /**
58 * Main class entry point
59 *
60 * @param object ipsRegistry reference
61 * @return @e void [Outputs to screen]
62 */
63 public function doExecute( ipsRegistry $registry )
64 {
65 //-----------------------------------------
66 // Load skin
67 //-----------------------------------------
68
69 $this->html = $this->registry->output->loadTemplate('cp_skin_applications');
70
71 //-----------------------------------------
72 // Load hooks library
73 //-----------------------------------------
74
75 $classToLoad = IPSLib::loadLibrary( IPSLib::getAppDir('core') . '/sources/classes/hooksFunctions.php', 'hooksFunctions' );
76 $this->hooksFunctions = new $classToLoad( $registry );
77
78 // lang already loaded by hookFunctions
79 //$this->registry->class_localization->loadLanguageFile( array( 'admin_applications' ), 'core' );
80
81 if( defined('NO_WRITE_HOOKS') )
82 {
83 $this->registry->output->showError( $this->lang->words['no_write_hooks'] );
84 }
85
86 //-----------------------------------------
87 // Set up stuff
88 //-----------------------------------------
89
90 $this->form_code = $this->html->form_code = 'module=applications&section=hooks&';
91 $this->form_code_js = $this->html->form_code_js = 'module=applications§ion=hooks&';
92
93 //-----------------------------------------
94 // What to do...
95 //-----------------------------------------
96
97 switch( $this->request['do'] )
98 {
99 case 'reenable_all_hooks':
100 $this->registry->getClass('class_permissions')->checkPermissionAutoMsg( 'hooks_manage' );
101 $this->_reenableAllHooks();
102 break;
103 case 'disable_all_hooks':
104 $this->registry->getClass('class_permissions')->checkPermissionAutoMsg( 'hooks_manage' );
105 $this->_disableAllHooks();
106 break;
107
108 break;
109 case 'disable_hook':
110 $this->registry->getClass('class_permissions')->checkPermissionAutoMsg( 'hooks_manage' );
111 $this->_disableHook();
112 break;
113 case 'enable_hook':
114 $this->registry->getClass('class_permissions')->checkPermissionAutoMsg( 'hooks_manage' );
115 $this->_enableHook();
116 break;
117
118 case 'uninstall_hook':
119 $this->registry->getClass('class_permissions')->checkPermissionAutoMsg( 'hooks_delete' );
120 $this->_uninstallHook();
121 break;
122 case 'install_hook':
123 $this->registry->getClass('class_permissions')->checkPermissionAutoMsg( 'hooks_manage' );
124 $this->_installHook();
125 break;
126
127 case 'reorder':
128 $this->registry->getClass('class_permissions')->checkPermissionAutoMsg( 'hooks_manage' );
129 $this->_reorder();
130 break;
131
132 case 'view_details':
133 $this->registry->getClass('class_permissions')->checkPermissionAutoMsg( 'hooks_manage' );
134 $this->_viewDetails();
135 break;
136
137 case 'check_requirements':
138 $this->registry->getClass('class_permissions')->checkPermissionAutoMsg( 'hooks_manage' );
139 $this->_checkRequirements();
140 break;
141
142 case 'export_hook':
143 $this->registry->getClass('class_permissions')->checkPermissionAutoMsg( 'hooks_manage' );
144 $this->_exportHook();
145 break;
146
147 case 'do_export_hook':
148 $this->registry->getClass('class_permissions')->checkPermissionAutoMsg( 'hooks_manage' );
149 $this->_doExportHook();
150 break;
151
152 case 'create_hook':
153 $this->registry->getClass('class_permissions')->checkPermissionAutoMsg( 'hooks_manage' );
154 $this->_hookForm( 'add' );
155 break;
156
157 case 'edit_hook':
158 $this->registry->getClass('class_permissions')->checkPermissionAutoMsg( 'hooks_manage' );
159 $this->_hookForm( 'edit' );
160 break;
161
162 case 'do_create_hook':
163 $this->registry->getClass('class_permissions')->checkPermissionAutoMsg( 'hooks_manage' );
164 $this->_hookSave( 'add' );
165 break;
166
167 case 'do_edit_hook':
168 $this->registry->getClass('class_permissions')->checkPermissionAutoMsg( 'hooks_manage' );
169 $this->_hookSave( 'edit' );
170 break;
171
172 case 'removeDeadCaches':
173 $this->registry->getClass('class_permissions')->checkPermissionAutoMsg( 'hooks_manage' );
174 $this->removeDeadCaches();
175 break;
176
177 case 'reimport_apps':
178 $this->registry->getClass('class_permissions')->checkPermissionAutoMsg( 'hooks_manage' );
179 $this->reimportAppHooks();
180 break;
181
182 case 'hooks_overview':
183 default:
184 $this->registry->getClass('class_permissions')->checkPermissionAutoMsg( 'hooks_manage' );
185 $this->request['do'] = 'hooks_overview';
186 $this->_hooksOverview();
187 break;
188 }
189
190 //-----------------------------------------
191 // Pass to CP output hander
192 //-----------------------------------------
193
194 $this->registry->getClass('output')->html_main .= $this->registry->getClass('output')->global_template->global_frame_wrapper();
195 $this->registry->getClass('output')->sendOutput();
196 }
197
198 /**
199 * Reimport all hooks for all installed applications
200 *
201 * @return @e void
202 */
203 protected function reimportAppHooks()
204 {
205 $stats = array( 'inserted' => 0, 'updated' => 0, 'skipped' => 0 );
206
207 foreach( ipsRegistry::$applications as $app )
208 {
209 $_stats = $this->installAppHooks( $app['app_directory'] );
210
211 $stats['inserted'] = $stats['inserted'] + $_stats['inserted'];
212 $stats['updated'] = $stats['updated'] + $_stats['updated'];
213 $stats['skipped'] = $stats['skipped'] + $_stats['skipped'];
214 }
215
216 /* Rebuild cache */
217 $this->rebuildHooksCache();
218
219 /* Rebuild global caches */
220 try
221 {
222 IPSLib::cacheGlobalCaches();
223 }
224 catch( Exception $e ){}
225
226 /* Flag skins for recache */
227 require_once( IPS_ROOT_PATH . 'sources/classes/skins/skinFunctions.php' );/*noLibHook*/
228 require_once( IPS_ROOT_PATH . 'sources/classes/skins/skinCaching.php' );/*noLibHook*/
229 $skinCaching = new skinCaching( $this->registry );
230 $skinCaching->flagSetForRecache();
231
232 /* Redirect */
233 $this->registry->output->global_message = sprintf( $this->lang->words['app_hooks_rebuilt'], $stats['inserted'], $stats['updated'], $stats['skipped'] );
234 $this->registry->output->silentRedirectWithMessage( $this->settings['base_url'] . $this->form_code . '&do=hooks_overview' );
235 }
236
237 /**
238 * Rebuild skins following hook import that included a template
239 *
240 * @return @e void
241 */
242 protected function removeDeadCaches()
243 {
244 $messages = array();
245 $keep = array();
246 $unlink = array();
247
248 /* Grab all current hooks caches */
249 $this->DB->build( array( 'select' => '*',
250 'from' => 'core_hooks_files' ) );
251
252 $this->DB->execute();
253
254 while( $row = $this->DB->fetch() )
255 {
256 $keep[] = $row['hook_file_stored'];
257 }
258
259 try
260 {
261 foreach( new DirectoryIterator( IPS_HOOKS_PATH ) as $file )
262 {
263 if ( ! $file->isDot() )
264 {
265 $_name = $file->getFileName();
266
267 if ( preg_match( "#_[a-z0-9]{32}\.php$#", $_name ) )
268 {
269 if ( ! in_array( $_name, $keep ) )
270 {
271 $unlink[] = $_name;
272 }
273 }
274 }
275 }
276 } catch ( Exception $e ) { print $e->getMessage(); }
277
278 /* Anything to unlink? */
279 if ( count( $unlink ) )
280 {
281 foreach( $unlink as $file )
282 {
283 @unlink( IPS_HOOKS_PATH . $file );
284 }
285 }
286
287 /* Print message */
288 $this->registry->output->global_message = sprintf( $this->lang->words['hook_removed_files'], count( $unlink ) ) . '<br />' . implode( '<br />', $unlink );
289 $this->_hooksOverview();
290 }
291
292 /**
293 * Reorder hooks
294 *
295 * @return @e void [Outputs to screen]
296 */
297 protected function _reorder()
298 {
299 //-----------------------------------------
300 // INIT
301 //-----------------------------------------
302
303 $classToLoad = IPSLib::loadLibrary( IPS_KERNEL_PATH . 'classAjax.php', 'classAjax' );
304 $ajax = new $classToLoad();
305
306 //-----------------------------------------
307 // Checks...
308 //-----------------------------------------
309
310 if( $this->registry->adminFunctions->checkSecurityKey( $this->request['md5check'], true ) === false )
311 {
312 $ajax->returnString( $this->lang->words['postform_badmd5'] );
313 }
314
315 //-----------------------------------------
316 // Save new position
317 //-----------------------------------------
318
319 $position = 1;
320
321 if( is_array($this->request['hooks']) AND count($this->request['hooks']) )
322 {
323 /* Reset all hooks to 0 temporarily */
324 $this->DB->update( 'core_hooks', array( 'hook_position' => 0 ) );
325
326 foreach( $this->request['hooks'] as $this_id )
327 {
328 $this->DB->update( 'core_hooks', array( 'hook_position' => $position ), 'hook_id=' . $this_id );
329
330 $position++;
331 }
332 }
333
334 $this->rebuildHooksCache();
335
336 $ajax->returnString('OK');
337 }
338
339 /**
340 * Install a new hook
341 *
342 * @return void
343 */
344 protected function _installHook()
345 {
346 //-----------------------------------------
347 // Get uploaded schtuff
348 //-----------------------------------------
349
350 $tmp_name = $_FILES['FILE_UPLOAD']['name'];
351 $tmp_name = preg_replace( "#\.gz$#", "", $tmp_name );
352
353 $content = ipsRegistry::getClass('adminFunctions')->importXml( $tmp_name );
354
355 if( ! $content )
356 {
357 $this->registry->output->showError( $this->lang->words['h_noxml'], 1110 );
358 }
359
360 $this->installHook( $content, TRUE, FALSE );
361
362 /* Rebuild cache */
363 $this->rebuildHooksCache();
364
365 /* Rebuild global caches */
366 try
367 {
368 IPSLib::cacheGlobalCaches();
369 }
370 catch( Exception $e ){}
371
372 /* Flag skins for recache */
373 require_once( IPS_ROOT_PATH . 'sources/classes/skins/skinFunctions.php' );/*noLibHook*/
374 require_once( IPS_ROOT_PATH . 'sources/classes/skins/skinCaching.php' );/*noLibHook*/
375 $skinCaching = new skinCaching( $this->registry );
376 $skinCaching->flagSetForRecache();
377
378 /* Redirect */
379 $this->registry->output->silentRedirectWithMessage( $this->settings['base_url'] . $this->form_code . '&do=hooks_overview' );
380 }
381
382 /**
383 * Function mostly used in installer/upgrader
384 * Inserts new hooks
385 *
386 * @param string Application to install hooks for
387 */
388 public function installAppHooks( $app )
389 {
390 static $hooks = array();
391
392 $msgs = array( 'inserted' => 0, 'updated' => 0, 'skipped' => 0 );
393 $xmlData = array();
394 $path = IPSLib::getAppDir( $app ) . '/xml';
395
396 if ( is_file( $path . '/hooks.xml' ) AND is_dir( $path . '/hooks' ) )
397 {
398 if( !count($hooks) )
399 {
400 /* Fetch current hooks */
401 $this->DB->build( array( 'select' => '*',
402 'from' => 'core_hooks' ) );
403 $this->DB->execute();
404
405 while( $row = $this->DB->fetch() )
406 {
407 if ( $row['hook_key'] )
408 {
409 $hooks[ $row['hook_key'] ] = $row;
410 }
411 }
412 }
413
414 /* Alright. We're in. Read the XML file */
415 require_once( IPS_KERNEL_PATH . 'classXML.php' );/*noLibHook*/
416 $xml = new classXML( IPS_DOC_CHAR_SET );
417
418 /* Grab wrapper file */
419 $xml->load( $path . '/hooks.xml' );
420
421 foreach( $xml->fetchElements('hook') as $data )
422 {
423 $xmlData[] = $xml->fetchElementsFromRecord( $data );
424 }
425
426 /* Examine XML */
427 if ( is_array( $xmlData ) AND count( $xmlData ) )
428 {
429 foreach( $xmlData as $x )
430 {
431 if ( is_file( $path . '/hooks/' . $x['file'] ) )
432 {
433 $xml->load( $path . '/hooks/' . $x['file'] );
434
435 foreach( $xml->fetchElements('config') as $data )
436 {
437 $config = $xml->fetchElementsFromRecord( $data );
438 }
439
440 if ( ! isset( $hooks[ $config['hook_key'] ] ) )
441 {
442 /* Add it */
443 $msgs['inserted']++;
444 $this->installHook( file_get_contents( $path . '/hooks/' . $x['file'] ), FALSE, FALSE, $x['enabled'] );
445 }
446 else
447 {
448 $this->installHook( file_get_contents( $path . '/hooks/' . $x['file'] ), FALSE, FALSE, $x['enabled'] );
449 $msgs['updated']++;
450 }
451 }
452 }
453 }
454 }
455
456 return $msgs;
457 }
458
459 /**
460 * Public install hook so we can use it in the installer and elsewhere
461 *
462 * @param string XML data
463 * @param boolean Add message to output->global_message
464 * @param boolean Allow skins to recache
465 * @param int Install enabled
466 * @return @e void
467 */
468 public function installHook( $content, $addMessage=FALSE, $allowSkinRecache=TRUE, $enabled=1 )
469 {
470 //-----------------------------------------
471 // Hooks directory writable?
472 //-----------------------------------------
473
474 if( !is_writable( IPS_HOOKS_PATH ) )
475 {
476 if( !$addMessage )
477 {
478 return false;
479 }
480
481 $this->registry->output->showError( $this->lang->words['h_dir_notwritable'], 111159 );
482 }
483
484 //-----------------------------------------
485 // Got our hooks?
486 //-----------------------------------------
487
488 if( !is_array($this->hooks) OR !count($this->hooks) )
489 {
490 $this->DB->build( array( 'select' => '*', 'from' => 'core_hooks' ) );
491 $this->DB->execute();
492
493 while( $r = $this->DB->fetch() )
494 {
495 $this->hooks[ $r['hook_key'] ] = $r;
496 }
497 }
498
499 //-----------------------------------------
500 // Get xml mah-do-dah
501 //-----------------------------------------
502
503 require_once( IPS_KERNEL_PATH . 'classXML.php' );/*noLibHook*/
504 $xml = new classXML( IPS_DOC_CHAR_SET );
505
506 //-----------------------------------------
507 // Unpack the datafile
508 //-----------------------------------------
509
510 $xml->loadXML( $content );
511
512 foreach( $xml->fetchElements('config') as $data )
513 {
514 $config = $xml->fetchElementsFromRecord( $data );
515
516 if( !count($config) )
517 {
518 $this->registry->output->showError( $this->lang->words['h_xmlwrong'], 1111 );
519 }
520 }
521
522 //-----------------------------------------
523 // Temp
524 //-----------------------------------------
525
526 $tempExtraData = unserialize( $config['hook_extra_data'] );
527
528 //-----------------------------------------
529 // Set config
530 //-----------------------------------------
531
532 $config = array('hook_name' => $config['hook_name'],
533 'hook_desc' => $config['hook_desc'],
534 'hook_author' => $config['hook_author'],
535 'hook_email' => $config['hook_email'],
536 'hook_website' => $config['hook_website'],
537 'hook_update_check' => $config['hook_update_check'],
538 'hook_requirements' => $config['hook_requirements'],
539 'hook_version_human' => $config['hook_version_human'],
540 'hook_version_long' => $config['hook_version_long'],
541 'hook_key' => $config['hook_key'],
542 'hook_global_caches' => $config['hook_global_caches'],
543 'hook_enabled' => $enabled,
544 'hook_updated' => IPS_UNIX_TIME_NOW,
545 );
546
547 $extra_data = array();
548
549 //-----------------------------------------
550 // Set files
551 //-----------------------------------------
552
553 $files = array();
554
555 foreach( $xml->fetchElements('hookfiles') as $node )
556 {
557 foreach( $xml->fetchElements('file', $node) as $_file )
558 {
559 $file = $xml->fetchElementsFromRecord( $_file );
560
561 if( $file['hook_type'] )
562 {
563 $files[] = array(
564 'hook_file_real' => $file['hook_file_real'],
565 'hook_type' => $file['hook_type'],
566 'hook_classname' => $file['hook_classname'],
567 'hook_data' => $file['hook_data'],
568 'hooks_source' => $file['hooks_source'],
569 );
570 }
571 }
572 }
573
574 //-----------------------------------------
575 // Set the custom script
576 //-----------------------------------------
577
578 $custom = array();
579 $customClass = null;
580
581 foreach( $xml->fetchElements('hookextras_custom') as $node )
582 {
583 foreach( $xml->fetchElements('file', $node) as $_file )
584 {
585 $file = $xml->fetchElementsFromRecord( $_file );
586
587 if( count($file) )
588 {
589 $custom = array( 'filename' => $file['filename'],
590 'source' => $file['source'],
591 );
592 }
593 }
594 }
595
596 /* Got any custom file to initialize? */
597 if ( $custom['filename'] && $custom['source'] )
598 {
599 # Our dirty trick to avoid saving the file on disk just yet...
600 $_custom = preg_replace( '#^(\s*)<\?(php)+#i', '', $custom['source'] );
601 eval($_custom);
602
603 $classname = str_replace( '.php', '', $custom['filename'] );
604
605 if( class_exists( $classname ) )
606 {
607 $customClass = new $classname( $this->registry );
608
609 if( method_exists( $customClass, 'pre_install' ) )
610 {
611 $customClass->pre_install();
612 }
613 }
614 }
615
616 //-----------------------------------------
617 // Set the settings
618 //-----------------------------------------
619
620 $settings = array();
621 $settingGroups = array();
622
623 foreach( $xml->fetchElements('hookextras_settings') as $node )
624 {
625 foreach( $xml->fetchElements('setting', $node) as $_setting )
626 {
627 $setting = $xml->fetchElementsFromRecord( $_setting );
628
629 if( $setting['conf_is_title'] == 1)
630 {
631 $settingGroups[] = array(
632 'conf_title_title' => $setting['conf_title_title'],
633 'conf_title_desc' => $setting['conf_title_desc'],
634 'conf_title_noshow' => $setting['conf_title_noshow'],
635 'conf_title_keyword' => $setting['conf_title_keyword'],
636 'conf_title_app' => $setting['conf_title_app'],
637 'conf_title_tab' => $setting['conf_title_tab'],
638 );
639 }
640 else
641 {
642 $settings[] = array(
643 'conf_title' => $setting['conf_title'],
644 'conf_description' => $setting['conf_description'],
645 'conf_group' => $setting['conf_group'],
646 'conf_type' => $setting['conf_type'],
647 'conf_key' => $setting['conf_key'],
648 'conf_default' => $setting['conf_default'],
649 'conf_extra' => $setting['conf_extra'],
650 'conf_evalphp' => $setting['conf_evalphp'],
651 'conf_protected' => $setting['conf_protected'],
652 'conf_position' => $setting['conf_position'],
653 'conf_start_group' => $setting['conf_start_group'],
654 'conf_add_cache' => $setting['conf_add_cache'],
655 'conf_title_keyword' => $setting['conf_title_keyword'],
656 );
657 }
658 }
659 }
660
661 //-----------------------------------------
662 // Set the lang bits
663 //-----------------------------------------
664
665 $language = array();
666
667 foreach( $xml->fetchElements('hookextras_language') as $node )
668 {
669 foreach( $xml->fetchElements('language', $node) as $_langbit )
670 {
671 $langbit = $xml->fetchElementsFromRecord( $_langbit );
672
673 $language[] = array(
674 'word_app' => $langbit['word_app'],
675 'word_pack' => $langbit['word_pack'],
676 'word_key' => $langbit['word_key'],
677 'word_default' => $langbit['word_default'],
678 'word_custom' => $langbit['word_custom'],
679 'word_js' => intval($langbit['word_js']),
680 );
681 }
682 }
683
684 //-----------------------------------------
685 // Set the modules
686 //-----------------------------------------
687
688 $modules = array();
689
690 foreach( $xml->fetchElements('hookextras_modules') as $node )
691 {
692 foreach( $xml->fetchElements('module', $node) as $_module )
693 {
694 $module = $xml->fetchElementsFromRecord( $_module );
695 $modules[] = array(
696 'sys_module_title' => $module['sys_module_title'],
697 'sys_module_application' => $module['sys_module_application'],
698 'sys_module_key' => $module['sys_module_key'],
699 'sys_module_description' => $module['sys_module_description'],
700 'sys_module_version' => $module['sys_module_version'],
701 'sys_module_protected' => $module['sys_module_protected'],
702 'sys_module_visible' => $module['sys_module_visible'],
703 'sys_module_position' => $module['sys_module_position'],
704 'sys_module_admin' => $module['sys_module_admin'],
705 );
706 }
707 }
708
709 //-----------------------------------------
710 // Set the help files
711 //-----------------------------------------
712
713 $help = array();
714
715 foreach( $xml->fetchElements('hookextras_help') as $node )
716 {
717 foreach( $xml->fetchElements('help', $node) as $_helpfile )
718 {
719 $helpfile = $xml->fetchElementsFromRecord( $_helpfile );
720 $help[] = array(
721 'title' => $helpfile['title'],
722 'text' => $helpfile['text'],
723 'description' => $helpfile['description'],
724 'position' => $helpfile['position'],
725 );
726 }
727 }
728
729 //-----------------------------------------
730 // Set the templates
731 //-----------------------------------------
732
733 $templates = array();
734
735 foreach( $xml->fetchElements('hookextras_templates') as $node )
736 {
737 foreach( $xml->fetchElements('templates', $node) as $_template )
738 {
739 $template = $xml->fetchElementsFromRecord( $_template );
740 $templates[] = array(
741 'template_set_id' => 0,
742 'template_group' => $template['template_group'],
743 'template_content' => $template['template_content'],
744 'template_name' => $template['template_name'],
745 'template_data' => $template['template_data'],
746 'template_updated' => $template['template_updated'],
747 'template_removable' => $template['template_removable'],
748 'template_added_to' => intval($template['template_added_to']),
749 'template_user_added' => 1,
750 'template_user_edited' => 0,
751 'template_master_key' => $template['template_master_key'] ? $template['template_master_key'] : 'root',
752 );
753 }
754 }
755
756 //-----------------------------------------
757 // Set the CSS
758 //-----------------------------------------
759
760 $css = array();
761
762 foreach( $xml->fetchElements('hookextras_css') as $node )
763 {
764 foreach( $xml->fetchElements('css', $node) as $_css )
765 {
766 $_css = $xml->fetchElementsFromRecord( $_css );
767 $css[] = array(
768 'css_set_id' => 0,
769 'css_group' => $_css['css_group'],
770 'css_content' => $_css['css_content'],
771 'css_position' => $_css['css_position'],
772 'css_added_to' => 1,
773 'css_app' => $_css['css_app'],
774 'css_app_hide' => $_css['css_app_hide'],
775 'css_attributes' => $_css['css_attributes'],
776 'css_removed' => $_css['css_removed'],
777 'css_modules' => $_css['css_modules'],
778 'css_master_key' => $_css['css_master_key'],
779 );
780 }
781 }
782
783 //-----------------------------------------
784 // Set the Replacements
785 //-----------------------------------------
786
787 $replacements = array();
788
789 foreach( $xml->fetchElements('hookextras_replacements') as $node )
790 {
791 foreach( $xml->fetchElements('replacements', $node) as $_r )
792 {
793 $_r = $xml->fetchElementsFromRecord( $_r );
794 $replacements[] = array(
795 'replacement_key' => $_r['replacement_key'],
796 'replacement_content' => $_r['replacement_content'],
797 );
798 }
799 }
800
801 //-----------------------------------------
802 // Set the tasks
803 //-----------------------------------------
804
805 $tasks = array();
806
807 foreach( $xml->fetchElements('hookextras_tasks') as $node )
808 {
809 foreach( $xml->fetchElements('tasks', $node) as $_task )
810 {
811 $task = $xml->fetchElementsFromRecord( $_task );
812 $tasks[] = array(
813 'task_title' => $task['task_title'],
814 'task_file' => $task['task_file'],
815 'task_week_day' => $task['task_week_day'],
816 'task_month_day' => $task['task_month_day'],
817 'task_hour' => $task['task_hour'],
818 'task_minute' => $task['task_minute'],
819 'task_cronkey' => $task['task_cronkey'],
820 'task_log' => $task['task_log'],
821 'task_description' => $task['task_description'],
822 'task_enabled' => $task['task_enabled'],
823 'task_key' => $task['task_key'],
824 'task_safemode' => $task['task_safemode'],
825 'task_locked' => $task['task_locked'],
826 'task_application' => $task['task_application'],
827 );
828 }
829 }
830
831 //-----------------------------------------
832 // Set the database changes
833 //-----------------------------------------
834
835 $database = array(
836 'create' => array(),
837 'alter' => array(),
838 'update' => array(),
839 'insert' => array(),
840 );
841
842 foreach( $xml->fetchElements('hookextras_database_create') as $node )
843 {
844 foreach( $xml->fetchElements('create', $node) as $_table )
845 {
846 $table = $xml->fetchElementsFromRecord( $_table );
847 $database['create'][] = array(
848 'name' => $table['name'],
849 'fields' => $table['fields'],
850 'tabletype' => $table['tabletype'],
851 );
852 }
853 }
854
855 foreach( $xml->fetchElements('hookextras_database_alter') as $node )
856 {
857 foreach( $xml->fetchElements('alter', $node) as $_table )
858 {
859 $table = $xml->fetchElementsFromRecord( $_table );
860 $database['alter'][] = array(
861 'altertype' => $table['altertype'],
862 'table' => $table['table'],
863 'field' => $table['field'],
864 'newfield' => $table['newfield'],
865 'fieldtype' => $table['fieldtype'],
866 'default' => $table['default'],
867 );
868 }
869 }
870
871 foreach( $xml->fetchElements('hookextras_database_update') as $node )
872 {
873 foreach( $xml->fetchElements('update', $node) as $_table )
874 {
875 $table = $xml->fetchElementsFromRecord( $_table );
876 $database['update'][] = array(
877 'table' => $table['table'],
878 'field' => $table['field'],
879 'newvalue' => $table['newvalue'],
880 'oldvalue' => $table['oldvalue'],
881 'where' => $table['where'],
882 );
883 }
884 }
885
886 foreach( $xml->fetchElements('hookextras_database_insert') as $node )
887 {
888 foreach( $xml->fetchElements('insert', $node) as $_table )
889 {
890 $table = $xml->fetchElementsFromRecord( $_table );
891 $database['insert'][] = array(
892 'table' => $table['table'],
893 'updates' => $table['updates'],
894 'fordelete' => $table['fordelete'],
895 );
896 }
897 }
898
899 //-----------------------------------------
900 // Set some vars for display tallies
901 //-----------------------------------------
902
903 $filesInserted = 0;
904 $settingGroupsInserted = 0;
905 $settingsInserted = 0;
906 $settingsUpdated = 0;
907 $languageInserted = 0;
908 $languageUpdated = 0;
909 $modulesInserted = 0;
910 $modulesUpdated = 0;
911 $helpInserted = 0;
912 $helpUpdated = 0;
913 $templatesInserted = 0;
914 $templatesUpdated = 0;
915 $templateHooks = false;
916 $tasksInserted = 0;
917 $tasksUpdated = 0;
918 $createQueries = 0;
919 $alterQueries = 0;
920 $updateQueries = 0;
921 $insertQueries = 0;
922 $cssInserted = 0;
923 $cssUpdated = 0;
924 $replacementsInserted = 0;
925 $replacementsUpdated = 0;
926
927 //-----------------------------------------
928 // Insert/update DB records
929 //-----------------------------------------
930
931 if( $this->hooks[ $config['hook_key'] ]['hook_id'] )
932 {
933 //-----------------------------------------
934 // Don't change enabled/disabled status
935 //-----------------------------------------
936
937 unset( $config['hook_enabled'] );
938
939 $this->DB->update( 'core_hooks', $config, 'hook_id=' . $this->hooks[ $config['hook_key'] ]['hook_id'] );
940
941 $hook_id = $this->hooks[ $config['hook_key'] ]['hook_id'];
942
943 $extra_data = unserialize( $this->hooks[ $config['hook_key'] ]['hook_extra_data'] );
944
945 /* Reset DB */
946 $extra_data['database'] = array();
947 }
948 else
949 {
950 $config['hook_installed'] = IPS_UNIX_TIME_NOW;
951 $this->hook[ $config['hook_key'] ] = $config;
952
953 $this->DB->insert( 'core_hooks', $config );
954
955 $hook_id = $this->DB->getInsertId();
956
957 $this->hook[ $config['hook_key'] ]['hook_id'] = $hook_id;
958
959 $extra_data['display'] = $tempExtraData['display'];
960 }
961
962 if( count($files) )
963 {
964 //-----------------------------------------
965 // If we are updating, remove old files
966 //-----------------------------------------
967
968 if( $this->hooks[ $config['hook_key'] ]['hook_id'] )
969 {
970 $this->DB->build( array( 'select' => 'hook_file_id, hook_file_stored', 'from' => 'core_hooks_files', 'where' => 'hook_hook_id=' . $this->hooks[ $config['hook_key'] ]['hook_id'] ) );
971 $outer = $this->DB->execute();
972
973 while( $r = $this->DB->fetch($outer) )
974 {
975 @unlink( IPS_HOOKS_PATH . $r['hook_file_stored'] );
976 $this->DB->delete( 'core_hooks_files', 'hook_file_id=' . $r['hook_file_id'] );
977 }
978 }
979
980 foreach( $files as $file )
981 {
982 //-----------------------------------------
983 // Store new files
984 //-----------------------------------------
985
986 $filename = $file['hook_classname'] . '_' . md5( uniqid( microtime(), true ) ) . '.php';
987
988 file_put_contents( IPS_HOOKS_PATH . $filename, $file['hooks_source'] );
989 chmod( IPS_HOOKS_PATH . $filename, IPS_FILE_PERMISSION );
990
991 $file['hook_file_stored'] = $filename;
992 $file['hook_hook_id'] = $hook_id;
993
994 $this->DB->insert( 'core_hooks_files', $file );
995 $filesInserted++;
996
997 /* Need to recache skins? */
998 if ( $file['hook_type'] == 'templateHooks' )
999 {
1000 $templateHooks = true;
1001 }
1002 }
1003 }
1004
1005 //-----------------------------------------
1006 // Put custom install/uninstall file
1007 //-----------------------------------------
1008
1009 if( is_object($customClass) )
1010 {
1011 $extra_data['display']['custom'] = $custom['filename'];
1012
1013 file_put_contents( IPS_HOOKS_PATH . 'install_' . $custom['filename'], $custom['source'] );
1014 chmod( IPS_HOOKS_PATH . 'install_' . $custom['filename'], IPS_FILE_PERMISSION );
1015 }
1016
1017 //-----------------------------------------
1018 // (1) Settings
1019 //-----------------------------------------
1020
1021 if( count($settingGroups) OR count($settings) )
1022 {
1023 $setting_groups = array();
1024
1025 $this->DB->build( array( 'select' => '*', 'from' => 'core_sys_settings_titles' ) );
1026 $this->DB->execute();
1027
1028 while( $r = $this->DB->fetch() )
1029 {
1030 $setting_groups[ $r['conf_title_id'] ] = $r;
1031 $setting_groups_by_key[ $r['conf_title_keyword'] ] = $r;
1032 }
1033
1034 //-----------------------------------------
1035 // Get current settings.
1036 //-----------------------------------------
1037
1038 $this->DB->build( array( 'select' => 'conf_id, conf_key', 'from' => 'core_sys_conf_settings' ) );
1039 $this->DB->execute();
1040
1041 while ( $r = $this->DB->fetch() )
1042 {
1043 $cur_settings[ $r['conf_key'] ] = $r['conf_id'];
1044 }
1045 }
1046
1047 if( count($settingGroups) )
1048 {
1049 $need_to_update = array();
1050
1051 foreach( $settingGroups as $data )
1052 {
1053 if ( $data['conf_title_title'] AND $data['conf_title_keyword'] )
1054 {
1055 //-----------------------------------------
1056 // Get ID based on key
1057 //-----------------------------------------
1058
1059 $conf_id = $setting_groups_by_key[ $data['conf_title_keyword'] ]['conf_title_id'];
1060
1061 $save = array( 'conf_title_title' => $data['conf_title_title'],
1062 'conf_title_desc' => $data['conf_title_desc'],
1063 'conf_title_keyword' => $data['conf_title_keyword'],
1064 'conf_title_app' => $data['conf_title_app'],
1065 'conf_title_tab' => $data['conf_title_tab'],
1066 'conf_title_noshow' => $data['conf_title_noshow'] );
1067
1068 //-----------------------------------------
1069 // Not got a row, insert first!
1070 //-----------------------------------------
1071
1072 if ( ! $conf_id )
1073 {
1074 $this->DB->insert( 'core_sys_settings_titles', $save );
1075 $conf_id = $this->DB->getInsertId();
1076 $settingGroupsInserted++;
1077 $extra_data['settingGroups'][] = $save['conf_title_keyword'];
1078 }
1079 else
1080 {
1081 //-----------------------------------------
1082 // Update...
1083 //-----------------------------------------
1084
1085 $this->DB->update( 'core_sys_settings_titles', $save, 'conf_title_id='.$conf_id );
1086 }
1087
1088 //-----------------------------------------
1089 // Update settings cache
1090 //-----------------------------------------
1091
1092 $save['conf_title_id'] = $conf_id;
1093 $setting_groups_by_key[ $save['conf_title_keyword'] ] = $save;
1094 $setting_groups[ $save['conf_title_id'] ] = $save;
1095
1096 //-----------------------------------------
1097 // Set need update...
1098 //-----------------------------------------
1099
1100 $need_update[] = $conf_id;
1101 }
1102 }
1103 }
1104
1105 if( count($settings) )
1106 {
1107 foreach( $settings as $idx => $data )
1108 {
1109 $data['conf_group'] = $setting_groups_by_key[ $data['conf_title_keyword'] ]['conf_title_id'];
1110
1111 //-----------------------------------------
1112 // Remove from array
1113 //-----------------------------------------
1114
1115 unset( $data['conf_title_keyword'] );
1116
1117 if ( $cur_settings[ $data['conf_key'] ] )
1118 {
1119 $this->DB->update( 'core_sys_conf_settings', $data, 'conf_id='.$cur_settings[ $data['conf_key'] ] );
1120 $settingsUpdated++;
1121 }
1122 else
1123 {
1124 $this->DB->insert( 'core_sys_conf_settings', $data );
1125 $settingsInserted++;
1126 $extra_data['settings'][] = $data['conf_key'];
1127 }
1128 }
1129 }
1130
1131 //-----------------------------------------
1132 // Update group counts...
1133 //-----------------------------------------
1134
1135 if ( count( $need_update ) )
1136 {
1137 foreach( $need_update as $i => $idx )
1138 {
1139 $conf = $this->DB->buildAndFetch( array( 'select' => 'count(*) as count', 'from' => 'core_sys_conf_settings', 'where' => 'conf_group=' . $idx ) );
1140
1141 $count = intval($conf['count']);
1142
1143 $this->DB->update( 'core_sys_settings_titles', array( 'conf_title_count' => $count ), 'conf_title_id='.$idx );
1144 }
1145 }
1146
1147 if( count($settingGroups) OR count($settings) )
1148 {
1149 $this->cache->rebuildCache( 'settings', 'global' );
1150 }
1151
1152 //-----------------------------------------
1153 // (2) Languages
1154 //-----------------------------------------
1155
1156 if( count($language) )
1157 {
1158 $langPacks = array();
1159
1160 $this->DB->build( array( 'select' => 'lang_id', 'from' => 'core_sys_lang' ) );
1161 $this->DB->execute();
1162
1163 while( $r = $this->DB->fetch() )
1164 {
1165 $langPacks[] = $r['lang_id'];
1166 }
1167
1168 foreach( $language as $langbit )
1169 {
1170 foreach( $langPacks as $lang_id )
1171 {
1172
1173 $langbit['lang_id'] = $lang_id;
1174
1175 // See if it exists
1176 $cnt = $this->DB->buildAndFetch( array( 'select' => 'word_id', 'from' => 'core_sys_lang_words', 'where' => "lang_id={$lang_id} AND word_app='{$langbit['word_app']}' AND word_key='{$langbit['word_key']}' AND word_pack='{$langbit['word_pack']}'" ) );
1177
1178 if( $cnt['word_id'] )
1179 {
1180 $this->DB->update( 'core_sys_lang_words', $langbit, 'word_id=' . $cnt['word_id'] );
1181 $languageUpdated++;
1182 }
1183 else
1184 {
1185 $this->DB->insert( 'core_sys_lang_words', $langbit );
1186 $languageInserted++;
1187 $word_id = $this->DB->getInsertId();
1188 }
1189
1190 $extra_data['language'][ $langbit['word_app'] . "_" . $langbit['word_pack'] ][] = $langbit['word_key'];
1191 }
1192
1193 }
1194
1195 $classToLoad = IPSLib::loadActionOverloader( IPSLib::getAppDir('core') . '/modules_admin/languages/manage_languages.php', 'admin_core_languages_manage_languages' );
1196 $langLib = new $classToLoad();
1197 $langLib->makeRegistryShortcuts( $this->registry );
1198
1199 foreach( $langPacks as $langId )
1200 {
1201 $langLib->cacheToDisk( $langId );
1202 }
1203
1204 }
1205
1206 //-----------------------------------------
1207 // (3) Modules
1208 //-----------------------------------------
1209
1210 if( count($modules) )
1211 {
1212 //-----------------------------------------
1213 // Get current modules
1214 //-----------------------------------------
1215 $cur_modules = array();
1216 $this->DB->build( array( 'select' => '*',
1217 'from' => 'core_sys_module',
1218 'order' => 'sys_module_id' ) );
1219
1220 $this->DB->execute();
1221
1222 while ( $r = $this->DB->fetch() )
1223 {
1224 $cur_modules[ $r['sys_module_application'] ][ $r['sys_module_key'] ] = $r['sys_module_id'];
1225 }
1226
1227 foreach( $modules as $module )
1228 {
1229 //-----------------------------------------
1230 // Insert or update?
1231 //-----------------------------------------
1232
1233 if ( $cur_modules[ $module['sys_module_application'] ][ $module['sys_module_key'] ] )
1234 {
1235 $this->DB->update( 'core_sys_module', $module, "sys_module_id=" . $cur_modules[ $module['sys_module_application'] ][ $module['sys_module_key'] ] );
1236 $modulesUpdated++;
1237 }
1238 else
1239 {
1240 $this->DB->insert( 'core_sys_module', $module );
1241 $modulesInserted++;
1242
1243 $extra_data['modules'][] = ( $module['sys_module_admin'] ? 'admin' : 'public' ) . '-' . $module['sys_module_application'] . '-' . $module['sys_module_key'];
1244 }
1245 }
1246
1247 $classToLoad = IPSLib::loadActionOverloader( IPSLib::getAppDir('core') . '/modules_admin/applications/applications.php', 'admin_core_applications_applications' );
1248 $moduleLib = new $classToLoad();
1249 $moduleLib->makeRegistryShortcuts( $this->registry );
1250 $moduleLib->moduleRecache();
1251 $moduleLib->applicationsMenuDataRecache();
1252 }
1253
1254 //-----------------------------------------
1255 // (4) Help Files
1256 //-----------------------------------------
1257
1258 if( count($help) )
1259 {
1260 $keys = array();
1261
1262 $this->DB->build( array( 'select' => 'title', 'from' => 'faq' ) );
1263 $this->DB->execute();
1264
1265 while( $r = $this->DB->fetch() )
1266 {
1267 $keys[] = $r['title'];
1268 }
1269
1270 foreach( $help as $entry )
1271 {
1272 if( in_array( $entry['title'], $keys ) )
1273 {
1274 $this->DB->update( 'faq', $entry, "title='{$entry['title']}'" );
1275 $helpUpdated++;
1276 }
1277 else
1278 {
1279 $this->DB->insert( 'faq', $entry );
1280 $helpInserted++;
1281 $help_id = $this->DB->getInsertId();
1282 $extra_data['help'][] = $entry['title'];
1283 }
1284 }
1285 }
1286
1287 //-----------------------------------------
1288 // (6) Templates
1289 //-----------------------------------------
1290
1291 if( count($templates) )
1292 {
1293 $bits = array();
1294
1295 /* Root will always be updated */
1296 $this->DB->build( array( 'select' => 'template_name,template_group,template_master_key', 'from' => 'skin_templates' ) );
1297 $this->DB->execute();
1298
1299 while( $r = $this->DB->fetch() )
1300 {
1301 $bits[ $r['template_master_key'] ][ $r['template_group'] ][] = $r['template_name'];
1302 }
1303
1304 foreach( $templates as $template )
1305 {
1306 //-----------------------------------------
1307 // If template is not in root set, add it to
1308 // prevent fatal error selecting other skins
1309 //-----------------------------------------
1310
1311 if ( $template['template_master_key'] != 'root' && ( !isset($bits['root'][ $template['template_group'] ]) OR !in_array( $template['template_name'], $bits['root'][ $template['template_group'] ] ) ) )
1312 {
1313 $this->DB->insert( 'skin_templates', array_merge( $template, array( 'template_set_id' => 0, 'template_master_key' => 'root', 'template_content' => '' ) ) );
1314
1315 $extra_data['templates'][ $template['template_group'] ][ $template['template_name'] ] = $template['template_name'];
1316 $bits['root'][ $template['template_group'] ][] = $template['template_name'];
1317 }
1318
1319 //-----------------------------------------
1320 // Ignore unknown skin sets
1321 //-----------------------------------------
1322
1323 if ( !isset($bits[ $template['template_master_key'] ]) )
1324 {
1325 continue;
1326 }
1327
1328 if( isset($bits[ $template['template_master_key'] ][ $template['template_group'] ]) AND in_array( $template['template_name'], $bits[ $template['template_master_key'] ][ $template['template_group'] ] ) )
1329 {
1330 $template['template_updated'] = IPS_UNIX_TIME_NOW;
1331
1332 $this->DB->update( 'skin_templates', $template, "template_master_key='{$template['template_master_key']}' AND template_group='{$template['template_group']}' AND template_name='{$template['template_name']}'" );
1333 $templatesUpdated++;
1334 }
1335 else
1336 {
1337 $this->DB->insert( 'skin_templates', $template );
1338 $templatesInserted++;
1339
1340 $extra_data['templates'][ $template['template_group'] ][ $template['template_name'] ] = $template['template_name'];
1341 $bits[ $template['template_master_key'] ][ $template['template_group'] ][] = $template['template_name'];
1342 }
1343 }
1344 }
1345
1346 //-----------------------------------------
1347 // CSS
1348 //-----------------------------------------
1349
1350 if( count( $css ) )
1351 {
1352 $bits = array();
1353
1354 $this->DB->build( array( 'select' => 'css_master_key, css_group', 'from' => 'skin_css' ) );
1355 $this->DB->execute();
1356
1357 while( $r = $this->DB->fetch() )
1358 {
1359 $bits[ $r['css_master_key'] ][ $r['css_group'] ] = $r['css_group'];
1360 }
1361
1362 foreach( $css as $entry )
1363 {
1364 //-----------------------------------------
1365 // Ignore unknown skin sets
1366 //-----------------------------------------
1367
1368 if ( !isset( $bits[ $entry['css_master_key'] ] ) )
1369 {
1370 continue;
1371 }
1372
1373 if( in_array( $entry['css_group'], $bits[ $entry['css_master_key'] ] ) )
1374 {
1375 $entry['css_updated'] = IPS_UNIX_TIME_NOW;
1376
1377 $this->DB->update( 'skin_css', $entry, "css_master_key='{$entry['css_master_key']}' AND css_group='{$entry['css_group']}'" );
1378 $cssUpdated++;
1379 }
1380 else
1381 {
1382 $this->DB->insert( 'skin_css', $entry );
1383 $cssInserted++;
1384
1385 $extra_data['css'][ $entry['css_group'] ] = $entry['css_group'];
1386 $bits[ $entry['css_master_key'] ][ $entry['css_group'] ] = $entry['css_group'];
1387 }
1388 }
1389 }
1390
1391 //-----------------------------------------
1392 // Skin Replacements
1393 //-----------------------------------------
1394
1395 if( count( $replacements ) )
1396 {
1397 $bits = array();
1398
1399 $this->DB->build( array( 'select' => 'replacement_master_key, replacement_key', 'from' => 'skin_replacements' ) );
1400 $this->DB->execute();
1401
1402 while( $r = $this->DB->fetch() )
1403 {
1404 $bits[ $r['replacement_master_key'] ][ $r['replacement_key'] ] = $r['replacement_key'];
1405 }
1406
1407 foreach( $replacements as $entry )
1408 {
1409 if ( !isset( $bits[ $entry['replacement_master_key'] ] ) )
1410 {
1411 $entry['replacement_master_key'] = 'root';
1412 }
1413
1414 if( in_array( $entry['replacement_key'], $bits[ $entry['replacement_master_key'] ] ) )
1415 {
1416 $this->DB->update( 'skin_replacements', $entry, "replacement_master_key='{$entry['replacement_master_key']}' AND replacement_key='{$entry['replacement_key']}'" );
1417 $replacementsUpdated++;
1418 }
1419 else
1420 {
1421 $this->DB->insert( 'skin_replacements', $entry );
1422 $replacementsInserted++;
1423
1424 $extra_data['replacements'][ $entry['replacement_key'] ] = $entry['replacement_key'];
1425 }
1426 }
1427 }
1428
1429 //-----------------------------------------
1430 // (7) Tasks
1431 //-----------------------------------------
1432
1433 if( count($tasks) )
1434 {
1435 $keys = array();
1436
1437 $this->DB->build( array( 'select' => 'task_key', 'from' => 'task_manager' ) );
1438 $this->DB->execute();
1439
1440 while( $r = $this->DB->fetch() )
1441 {
1442 $keys[] = $r['task_key'];
1443 }
1444
1445 foreach( $tasks as $entry )
1446 {
1447 if( in_array( $entry['task_key'], $keys ) )
1448 {
1449 $this->DB->update( 'task_manager', $entry, "task_key='{$entry['task_key']}'" );
1450 $tasksUpdated++;
1451 }
1452 else
1453 {
1454 $this->DB->insert( 'task_manager', $entry );
1455 $tasksInserted++;
1456 $task_id = $this->DB->getInsertId();
1457 $extra_data['tasks'][] = $entry['task_key'];
1458 }
1459 }
1460 }
1461
1462 //-----------------------------------------
1463 // (8) Create new tables
1464 //-----------------------------------------
1465
1466 if( count($database['create']) )
1467 {
1468 foreach($database['create'] as $create )
1469 {
1470 $query = "CREATE TABLE {$this->settings['sql_tbl_prefix']}{$create['name']} (
1471 {$create['fields']}
1472 )";
1473
1474 if( $create['tabletype'] )
1475 {
1476 $query .= " ENGINE=" . $create['tabletype'];
1477 }
1478 else if( $this->settings['sql_driver'] == 'mysql' AND $this->settings['mysql_tbl_type'] )
1479 {
1480 $query .= " ENGINE=" . $this->settings['mysql_tbl_type'];
1481 }
1482
1483 //-----------------------------------------
1484 // Fix prefix
1485 //-----------------------------------------
1486
1487 $this->DB->return_die = true;
1488 $this->DB->query( $query );
1489 $this->DB->return_die = false;
1490 $createQueries++;
1491
1492 $extra_data['database']['create'][] = $create;
1493 }
1494 }
1495
1496 //-----------------------------------------
1497 // (9) Alter tables
1498 //-----------------------------------------
1499
1500 if( count($database['alter']) )
1501 {
1502 foreach( $database['alter'] as $alter )
1503 {
1504 $this->DB->return_die = true;
1505
1506 $alter['default'] = ( !isset($alter['default']) || $alter['default'] === "" ) ? NULL : $alter['default'];
1507
1508 switch( $alter['altertype'] )
1509 {
1510 case 'remove':
1511 $this->DB->dropField( $alter['table'], $alter['field'] );
1512 break;
1513
1514 case 'add':
1515 $this->DB->addField( $alter['table'], $alter['field'], $alter['fieldtype'], $alter['default'] );
1516 break;
1517
1518 case 'change':
1519 $this->DB->changeField( $alter['table'], $alter['field'], $alter['newfield'], $alter['fieldtype'], $alter['default'] );
1520 break;
1521 }
1522
1523 $this->DB->return_die = false;
1524 $alterQueries++;
1525 $extra_data['database']['alter'][] = $alter;
1526 }
1527 }
1528
1529 //-----------------------------------------
1530 // (10) Run update queries
1531 //-----------------------------------------
1532
1533 if( count($database['update']) )
1534 {
1535 foreach( $database['update'] as $update )
1536 {
1537 $this->DB->return_die = true;
1538 $this->DB->update( $update['table'], array( $update['field'] => $update['newvalue'] ), html_entity_decode( $update['where'], ENT_QUOTES ) );
1539 $this->DB->return_die = false;
1540 $updateQueries++;
1541 $extra_data['database']['update'][] = $update;
1542 }
1543 }
1544
1545 //-----------------------------------------
1546 // (11) Run insert queries
1547 //-----------------------------------------
1548
1549 // if( !$this->hooks[ $config['hook_key'] ]['hook_id'] )
1550 // {
1551 if( count($database['insert']) )
1552 {
1553 foreach( $database['insert'] as $insert )
1554 {
1555 $fields = array();
1556 $content = explode( ',', $insert['updates'] );
1557
1558 foreach( $content as $value )
1559 {
1560 list( $field, $toInsert ) = explode( '=', $value );
1561
1562 $fields[ trim($field) ] = str_replace( '~C~', ',', $toInsert );
1563 }
1564
1565 $this->DB->return_die = true;
1566 $this->DB->insert( $insert['table'], $fields );
1567 $this->DB->return_die = false;
1568 $insertQueries++;
1569 $extra_data['database']['insert'][] = $insert;
1570 }
1571 }
1572 // }
1573
1574 /* Got an install function to run too? */
1575 if( is_object($customClass) )
1576 {
1577 if( method_exists( $customClass, 'install' ) )
1578 {
1579 $customClass->install();
1580 }
1581 }
1582
1583 if( count($extra_data) )
1584 {
1585 $this->DB->update( 'core_hooks', array( 'hook_extra_data' => serialize( $extra_data ) ), 'hook_id=' . $hook_id );
1586 }
1587
1588 //print_r($config);
1589 //print_r($files);
1590 //print_r($custom);
1591 //print_r($settingGroups);
1592 //print_r($settings);
1593 //print_r($language);
1594 //print_r($modules);
1595 //print_r($templates);
1596 //print_r($tasks);
1597 //print_r($help);
1598 //print_r($database);
1599
1600 if ( $addMessage )
1601 {
1602 $_tempMsg = <<<EOF
1603 <strong>{$this->lang->words['h_newhookin']}</strong>
1604 <ul>
1605 <li>{$filesInserted} {$this->lang->words['h_filesin']}</li>
1606 <li>{$settingGroupsInserted} {$this->lang->words['h_settinggin']}</li>
1607 <li>{$settingsInserted} {$this->lang->words['h_settingin']}</li>
1608 <li>{$settingsUpdated} {$this->lang->words['h_settingup']}</li>
1609 <li>{$languageInserted} {$this->lang->words['h_langbitin']}</li>
1610 <li>{$languageUpdated} {$this->lang->words['h_langbitup']}</li>
1611 <li>{$modulesInserted} {$this->lang->words['h_modin']}</li>
1612 <li>{$modulesUpdated} {$this->lang->words['h_modup']}</li>
1613 <li>{$helpInserted} {$this->lang->words['h_helpin']}</li>
1614 <li>{$helpUpdated} {$this->lang->words['h_helpup']}</li>
1615 <li>{$templatesInserted} {$this->lang->words['h_tempin']}</li>
1616 <li>{$templatesUpdated} {$this->lang->words['h_tempup']}</li>
1617 <li>{$cssInserted} {$this->lang->words['h_cssin']}</li>
1618 <li>{$cssUpdated} {$this->lang->words['h_cssup']}</li>
1619 <li>{$replacementsInserted} {$this->lang->words['h_replacementsin']}</li>
1620 <li>{$replacementsUpdated} {$this->lang->words['h_replacementsup']}</li>
1621 <li>{$tasksInserted} {$this->lang->words['h_taskin']}</li>
1622 <li>{$tasksUpdated} {$this->lang->words['h_taskup']}</li>
1623 <li>{$createQueries} {$this->lang->words['h_dbcreated']}</li>
1624 <li>{$alterQueries} {$this->lang->words['h_dbaltered']}</li>
1625 <li>{$updateQueries} {$this->lang->words['h_updateran']}</li>
1626 <li>{$insertQueries} {$this->lang->words['h_insertran']}</li>
1627 </ul>
1628EOF;
1629
1630 $this->registry->output->setMessage( $_tempMsg, 1 );
1631 }
1632
1633 //-----------------------------------------
1634 // Got some skin recaching to do...
1635 //-----------------------------------------
1636
1637 if( $allowSkinRecache === TRUE AND ( $templatesInserted OR $templatesUpdated OR $templateHooks OR $cssUpdated OR $cssInserted ) )
1638 {
1639 /* Rebuild cache */
1640 $this->rebuildHooksCache();
1641
1642 /* Flag skins for recache */
1643 require_once( IPS_ROOT_PATH . 'sources/classes/skins/skinFunctions.php' );/*noLibHook*/
1644 require_once( IPS_ROOT_PATH . 'sources/classes/skins/skinCaching.php' );/*noLibHook*/
1645 $skinCaching = new skinCaching( $this->registry );
1646 $skinCaching->flagSetForRecache();
1647 }
1648 }
1649
1650 /**
1651 * Show the form to export a hook
1652 *
1653 * @return void
1654 */
1655 protected function _exportHook()
1656 {
1657 /* Get the hook */
1658 $id = intval($this->request['id']);
1659
1660 if( !$id )
1661 {
1662 $this->registry->output->showError( $this->lang->words['h_noexport'], 1112 );
1663 }
1664
1665 $hookData = $this->DB->buildAndFetch( array( 'select' => '*', 'from' => 'core_hooks', 'where' => 'hook_id=' . $id ) );
1666
1667 if( !$hookData['hook_id'] )
1668 {
1669 $this->registry->output->showError( $this->lang->words['h_noexport'], 1113 );
1670 }
1671
1672 /* Set defaults */
1673 $hookData['hook_extra_data'] = unserialize( $hookData['hook_extra_data'] );
1674
1675 /* Output */
1676 $this->registry->output->extra_nav[] = array( $this->settings['base_url'] . $this->form_code . 'do=hooks_overview', $this->lang->words['menu__manage_hooks'] );
1677 $this->registry->output->html .= $this->html->hooksExport( $hookData );
1678 }
1679
1680 /**
1681 * Actually export the damn hook already
1682 * Sorry, it has been a long day...
1683 *
1684 * @return @e void
1685 */
1686 protected function _doExportHook()
1687 {
1688 //-----------------------------------------
1689 // Get hook
1690 //-----------------------------------------
1691
1692 $id = intval($this->request['id']);
1693
1694 if( !$id )
1695 {
1696 $this->registry->output->showError( $this->lang->words['h_noexport'], 1114 );
1697 }
1698
1699 $hookData = $this->DB->buildAndFetch( array( 'select' => '*', 'from' => 'core_hooks', 'where' => 'hook_id=' . $id ) );
1700
1701 if( !$hookData['hook_id'] )
1702 {
1703 $this->registry->output->showError( $this->lang->words['h_noexport'], 1115 );
1704 }
1705
1706 $extra_data = unserialize( $hookData['hook_extra_data'] );
1707
1708 //-----------------------------------------
1709 // Get hook files
1710 //-----------------------------------------
1711
1712 $files = array();
1713 $index = 1;
1714
1715 $this->DB->build( array( 'select' => '*', 'from' => 'core_hooks_files', 'where' => 'hook_hook_id=' . $id, 'order' => 'hook_file_id ASC' ) );
1716 $this->DB->execute();
1717
1718 while( $r = $this->DB->fetch() )
1719 {
1720 $files[ $index ] = $r;
1721 $index++;
1722 }
1723
1724 //-----------------------------------------
1725 // Get XML class
1726 //-----------------------------------------
1727
1728 require_once( IPS_KERNEL_PATH.'classXML.php' );/*noLibHook*/
1729
1730 $xml = new classXML( IPS_DOC_CHAR_SET );
1731
1732 $xml->newXMLDocument();
1733 $xml->addElement( 'hookexport' );
1734
1735 //-----------------------------------------
1736 // Put hook data in export
1737 //-----------------------------------------
1738
1739 $xml->addElement( 'hookdata', 'hookexport' );
1740 $content = array();
1741
1742 foreach( $hookData as $k => $v )
1743 {
1744 if( in_array( $k, array( 'hook_id', 'hook_enabled', 'hook_installed', 'hook_updated', 'hook_position' ) ) )
1745 {
1746 continue;
1747 }
1748
1749 $content[ $k ] = $v;
1750 }
1751
1752 $xml->addElementAsRecord( 'hookdata', 'config', $content );
1753
1754 //-----------------------------------------
1755 // Put hook files in export
1756 //-----------------------------------------
1757
1758 $xml->addElement( 'hookfiles', 'hookexport' );
1759
1760 foreach( $files as $index => $r )
1761 {
1762 $content = array();
1763
1764 foreach( $r as $k => $v )
1765 {
1766 if( in_array( $k, array( 'hook_file_id', 'hook_hook_id', 'hook_file_stored', 'hooks_source' ) ) )
1767 {
1768 continue;
1769 }
1770
1771 $content[ $k ] = $v;
1772 }
1773
1774 $source = is_file( IPS_HOOKS_PATH . $r['hook_file_stored'] ) ? file_get_contents( IPS_HOOKS_PATH . $r['hook_file_stored'] ) : '';
1775
1776 if ( !empty($source) && in_array( $r['hook_type'], array( 'skinHooks', 'commandHooks', 'libraryHooks' ) ) )
1777 {
1778 $source = $this->_cleanSource( $source );
1779 }
1780
1781 $content['hooks_source'] = $source;
1782
1783 $xml->addElementAsRecord( 'hookfiles', 'file', $content );
1784 }
1785
1786 //-----------------------------------------
1787 // Custom install/uninstall script?
1788 //-----------------------------------------
1789
1790 if( $extra_data['custom'] )
1791 {
1792 $content = array();
1793 $xml->addElement( 'hookextras_custom', 'hookexport' );
1794
1795 $content['filename'] = $extra_data['custom'];
1796 $content['source'] = is_file( IPS_HOOKS_PATH . 'install_' . $extra_data['custom'] ) ? file_get_contents( IPS_HOOKS_PATH . 'install_' . $extra_data['custom'] ) : '';
1797
1798 $xml->addElementAsRecord( 'hookextras_custom', 'file', $content );
1799 }
1800
1801 //-----------------------------------------
1802 // Settings or setting groups?
1803 //-----------------------------------------
1804
1805 $entry = array();
1806 $_groups = array();
1807 $_settings = array();
1808 $titles = array();
1809 $content = array();
1810
1811 $xml->addElement( 'hookextras_settings', 'hookexport' );
1812
1813 # Store group ids and setting ids for entire setting groups
1814 if( is_array($extra_data['settingGroups']) AND count($extra_data['settingGroups']) )
1815 {
1816 $_groups = $extra_data['settingGroups'];
1817 $_groupIds = array();
1818
1819 $this->DB->build( array( 'select' => 'conf_title_id', 'from' => 'core_sys_settings_titles', 'where' => "conf_title_keyword IN('" . implode( "','", $_groups ) . "')" ) );
1820 $this->DB->execute();
1821
1822 while( $rr = $this->DB->fetch() )
1823 {
1824 $_groupIds[] = $rr['conf_title_id'];
1825 }
1826
1827 if( count($_groupIds) )
1828 {
1829 $this->DB->build( array( 'select' => 'conf_key', 'from' => 'core_sys_conf_settings', 'where' => 'conf_group IN(' . implode( ',', $_groupIds ) . ')' ) );
1830 $this->DB->execute();
1831
1832 while( $setting = $this->DB->fetch() )
1833 {
1834 $_settings[] = $setting['conf_key'];
1835 }
1836 }
1837 }
1838
1839 # Store group ids and setting ids for indvidual settings
1840 if( is_array($extra_data['settings']) AND count($extra_data['settings']) )
1841 {
1842 foreach( $extra_data['settings'] as $_aSetting )
1843 {
1844 $_settings[] = $_aSetting;
1845 }
1846
1847 $this->DB->build( array( 'select' => 't.conf_title_keyword',
1848 'from' => array( 'core_sys_settings_titles' => 't' ),
1849 'where' => "c.conf_key IN('" . implode( "','", $extra_data['settings'] ) . "')",
1850 'add_join' => array(
1851 array(
1852 'from' => array( 'core_sys_conf_settings' => 'c' ),
1853 'where' => 'c.conf_group=t.conf_title_id',
1854 'type' => 'left',
1855 )
1856 )
1857 ) );
1858 $this->DB->execute();
1859
1860 while( $group = $this->DB->fetch() )
1861 {
1862 $_groups[] = $group['conf_title_keyword'];
1863 }
1864 }
1865
1866 if( count($_groups) )
1867 {
1868 # Now get the group data for the XML file
1869 $this->DB->build( array( 'select' => '*', 'from' => 'core_sys_settings_titles', 'where' => "conf_title_keyword IN('" . implode( "','", $_groups ) . "')" ) );
1870 $this->DB->execute();
1871
1872 while( $r = $this->DB->fetch() )
1873 {
1874 $content = array();
1875
1876 $titles[ $r['conf_title_id'] ] = $r['conf_title_keyword'];
1877
1878 $content['conf_is_title'] = 1;
1879
1880 foreach( $r as $k => $v )
1881 {
1882 if( in_array( $k, array( 'conf_title_tab', 'conf_title_keyword', 'conf_title_title', 'conf_title_desc', 'conf_title_app', 'conf_title_noshow' ) ) )
1883 {
1884 $content[ $k ] = $v;
1885 }
1886 }
1887
1888 $xml->addElementAsRecord( 'hookextras_settings', 'setting', $content );
1889 }
1890 }
1891
1892 if( count($_settings) )
1893 {
1894 # Now get the group data for the XML file
1895 $this->DB->build( array( 'select' => '*', 'from' => 'core_sys_conf_settings', 'where' => "conf_key IN('" . implode( "','", $_settings ) . "')" ) );
1896 $this->DB->execute();
1897
1898 while( $r = $this->DB->fetch() )
1899 {
1900 $r['conf_value'] = '';
1901 $r['conf_title_keyword'] = $titles[ $r['conf_group'] ];
1902 $r['conf_is_title'] = 0;
1903
1904 $xml->addElementAsRecord( 'hookextras_settings', 'setting', $r );
1905 }
1906 }
1907
1908 //-----------------------------------------
1909 // Language strings/files
1910 //-----------------------------------------
1911
1912 $entry = array();
1913
1914 $xml->addElement( 'hookextras_language', 'hookexport' );
1915
1916 if( is_array($extra_data['language']) AND count($extra_data['language']) )
1917 {
1918 foreach( $extra_data['language'] as $file => $strings )
1919 {
1920 $bits = explode( '_', $file );
1921 $app = $bits[0];
1922 $pack = str_replace( $app.'_', '', $file );
1923
1924 $this->DB->build( array( 'select' => '*', 'from' => 'core_sys_lang_words', 'where' => "word_app='{$app}' AND word_pack='{$pack}' AND word_key IN('" . implode( "','", $strings ) . "')" ) );
1925 $this->DB->execute();
1926
1927 while( $r = $this->DB->fetch() )
1928 {
1929 $content = array();
1930
1931 foreach( $r as $k => $v )
1932 {
1933 if( !in_array( $k, array( 'word_app', 'word_pack', 'word_key', 'word_default' ) ) )
1934 {
1935 continue;
1936 }
1937
1938 $content[ $k ] = $v;
1939 }
1940
1941 $xml->addElementAsRecord( 'hookextras_language', 'language', $content );
1942 }
1943 }
1944 }
1945
1946 //-----------------------------------------
1947 // Modules
1948 //-----------------------------------------
1949
1950 $xml->addElement( 'hookextras_modules', 'hookexport' );
1951
1952 if( is_array($extra_data['modules']) AND count($extra_data['modules']) )
1953 {
1954 foreach ( $extra_data['modules'] as $module )
1955 {
1956 list( $_side, $_app, $_module ) = explode( '-', $module );
1957 $_is_admin = ( $_side == 'admin' ) ? 1 : 0;
1958
1959 $this->DB->build( array( 'select' => '*', 'from' => 'core_sys_module', 'where' => "sys_module_application='{$_app}' AND sys_module_key='{$_module}' AND sys_module_admin={$_is_admin}" ) );
1960 $this->DB->execute();
1961
1962 while( $r = $this->DB->fetch() )
1963 {
1964 unset($r['sys_module_id']);
1965
1966 $xml->addElementAsRecord( 'hookextras_modules', 'module', $r );
1967 }
1968 }
1969 }
1970
1971 //-----------------------------------------
1972 // Help files
1973 //-----------------------------------------
1974
1975 $xml->addElement( 'hookextras_help', 'hookexport' );
1976
1977 if( is_array($extra_data['help']) AND count($extra_data['help']) )
1978 {
1979 $this->DB->build( array( 'select' => '*', 'from' => 'faq', 'where' => "title IN('" . implode( "','", $extra_data['help'] ) . "')" ) );
1980 $this->DB->execute();
1981
1982 while( $r = $this->DB->fetch() )
1983 {
1984 unset($r['id']);
1985
1986 $xml->addElementAsRecord( 'hookextras_help', 'help', $r );
1987 }
1988 }
1989
1990 //-----------------------------------------
1991 // Skin templates
1992 //-----------------------------------------
1993
1994 $remapData = $this->registry->output->buildRemapData( true );
1995
1996 $xml->addElement( 'hookextras_templates', 'hookexport' );
1997
1998 if( is_array($extra_data['templates']) AND count($extra_data['templates']) )
1999 {
2000 foreach( $extra_data['templates'] as $file => $templates )
2001 {
2002 $this->DB->build( array( 'select' => '*', 'from' => 'skin_templates', 'where' => "template_set_id IN ('" . implode( "','", $remapData['export'] ) . "') AND template_master_key != '' AND template_group='{$file}' AND template_name IN('" . implode( "','", $templates ) . "')" ) );
2003 $this->DB->execute();
2004
2005 while( $r = $this->DB->fetch() )
2006 {
2007 unset($r['template_id']);
2008 unset($r['template_set_id']);
2009
2010 $xml->addElementAsRecord( 'hookextras_templates', 'templates', $r );
2011 }
2012 }
2013 }
2014
2015 //-----------------------------------------
2016 // CSS
2017 //-----------------------------------------
2018
2019 $xml->addElement( 'hookextras_css', 'hookexport' );
2020
2021 if( is_array( $extra_data['css'] ) AND count ( $extra_data['css'] ) )
2022 {
2023 $this->DB->build( array( 'select' => '*', 'from' => 'skin_css', 'where' => "css_set_id IN ('" . implode( "','", $remapData['export'] ) . "') AND css_master_key != '' AND css_group IN('" . implode( "','", $extra_data['css'] ) . "')" ) );
2024 $this->DB->execute();
2025
2026 while( $r = $this->DB->fetch() )
2027 {
2028 unset($r['css_id']);
2029 unset($r['css_set_id']);
2030
2031 $xml->addElementAsRecord( 'hookextras_css', 'css', $r );
2032 }
2033 }
2034
2035 //-----------------------------------------
2036 // Replacements
2037 //-----------------------------------------
2038
2039 $xml->addElement( 'hookextras_replacements', 'hookexport' );
2040
2041 if( is_array( $extra_data['replacements'] ) AND count ( $extra_data['replacements'] ) )
2042 {
2043 $this->DB->build( array( 'select' => '*', 'from' => 'skin_replacements', 'where' => "replacement_key IN('" . implode( "','", $extra_data['replacements'] ) . "')", 'group' => 'replacement_key' ) );
2044
2045 $this->DB->execute();
2046
2047 while( $r = $this->DB->fetch() )
2048 {
2049 unset($r['replacement_id']);
2050 unset($r['replacement_set_id']);
2051
2052 $xml->addElementAsRecord( 'hookextras_replacements', 'replacements', $r );
2053 }
2054 }
2055
2056 //-----------------------------------------
2057 // Tasks
2058 //-----------------------------------------
2059
2060 $xml->addElement( 'hookextras_tasks', 'hookexport' );
2061
2062 if( is_array($extra_data['tasks']) AND count($extra_data['tasks']) )
2063 {
2064 $this->DB->build( array( 'select' => '*', 'from' => 'task_manager', 'where' => "task_key IN('" . implode( "','", $extra_data['tasks'] ) . "')" ) );
2065 $this->DB->execute();
2066
2067 while( $r = $this->DB->fetch() )
2068 {
2069 unset($r['task_id']);
2070 unset($r['task_next_run']);
2071
2072 $xml->addElementAsRecord( 'hookextras_tasks', 'tasks', $r );
2073 }
2074 }
2075
2076 //-----------------------------------------
2077 // Database changes
2078 //-----------------------------------------
2079
2080 $xml->addElement( 'hookextras_database_create', 'hookexport' );
2081
2082 if( is_array($extra_data['database']['create']) AND count($extra_data['database']['create']) )
2083 {
2084 foreach( $extra_data['database']['create'] as $create_query )
2085 {
2086 $xml->addElementAsRecord( 'hookextras_database_create', 'create', $create_query );
2087 }
2088 }
2089
2090 $xml->addElement( 'hookextras_database_alter', 'hookexport' );
2091
2092 if( is_array($extra_data['database']['alter']) AND count($extra_data['database']['alter']) )
2093 {
2094 foreach( $extra_data['database']['alter'] as $alter_query )
2095 {
2096 $xml->addElementAsRecord( 'hookextras_database_alter', 'alter', $alter_query );
2097 }
2098 }
2099
2100 $xml->addElement( 'hookextras_database_update', 'hookexport' );
2101
2102 if( is_array($extra_data['database']['update']) AND count($extra_data['database']['update']) )
2103 {
2104 foreach( $extra_data['database']['update'] as $update_query )
2105 {
2106 $xml->addElementAsRecord( 'hookextras_database_update', 'update', $update_query );
2107 }
2108 }
2109
2110 $xml->addElement( 'hookextras_database_insert', 'hookexport' );
2111
2112 if( is_array($extra_data['database']['insert']) AND count($extra_data['database']['insert']) )
2113 {
2114 foreach( $extra_data['database']['insert'] as $insert_query )
2115 {
2116 $xml->addElementAsRecord( 'hookextras_database_insert', 'insert', $insert_query );
2117 }
2118 }
2119
2120 //-----------------------------------------
2121 // Print to browser
2122 //-----------------------------------------
2123
2124 $this->registry->output->showDownload( $xml->fetchDocument(), $hookData['hook_key'] . '.xml', '', 0 );
2125 }
2126
2127 /**
2128 * Clean source code for export..
2129 *
2130 * @param string Hook source code
2131 * @return string "Cleaned" source code
2132 */
2133 protected function _cleanSource( $source )
2134 {
2135 $source = preg_replace( "/class\s+(\S+)\s+extends\s+(\S+)/i", "class \\1 extends (~extends~)", $source );
2136
2137 return $source;
2138 }
2139
2140 /**
2141 * Fix hook positions
2142 *
2143 * @return @e void
2144 */
2145 protected function _fixPositions()
2146 {
2147 /* Init vars */
2148 $new_order = 0;
2149 $usedActions = array();
2150
2151 $this->DB->build( array( 'select' => 'hook_id, hook_position', 'from' => 'core_hooks', 'where' => 'hook_enabled=1', 'order' => 'hook_position ASC' ) );
2152 $qid = $this->DB->execute();
2153
2154 while ( $hook = $this->DB->fetch( $qid ) )
2155 {
2156 $new_order++;
2157 $this->DB->update( 'core_hooks', array ( 'hook_position' => $new_order ), "hook_id={$hook['hook_id']}" );
2158
2159 $this->DB->build( array( 'select' => '*', 'from' => 'core_hooks_files', 'where' => "hook_type IN ('commandHooks', 'skinHooks', 'libraryHooks' ) AND hook_hook_id=" . $hook['hook_id'] ) );
2160 $this->DB->execute();
2161
2162 while( $file = $this->DB->fetch() )
2163 {
2164 if( $file['hooks_source'] )
2165 {
2166 $source = $this->_cleanSource( $file['hooks_source'] );
2167 }
2168 else
2169 {
2170 $source = is_file( IPS_HOOKS_PATH . $file['hook_file_stored'] ) ? file_get_contents( IPS_HOOKS_PATH . $file['hook_file_stored'] ) : '';
2171
2172 if( $source )
2173 {
2174 $source = $this->_cleanSource( $source );
2175 }
2176 }
2177
2178 if( $source )
2179 {
2180 $hook_data = unserialize( $file['hook_data'] );
2181 $overload = $hook_data['classToOverload'];
2182 $newClass = $overload;
2183
2184 $app = ( $file['hook_type'] == 'libraryHooks' ) ? trim($hook_data['libApplication']) : '~_NO_APP_~';
2185
2186 if( isset( $usedActions[ $app ][ $overload ] ) )
2187 {
2188 $newClass = $usedActions[ $app ][ $overload ];
2189 }
2190 else if( $file['hook_type'] == 'skinHooks' )
2191 {
2192 $newClass .= "(~id~)";
2193 }
2194
2195 $source = str_replace( "(~extends~)", $newClass, $source );
2196
2197 file_put_contents( IPS_HOOKS_PATH . $file['hook_file_stored'], $source );
2198
2199 $usedActions[ $app ][ $hook_data['classToOverload'] ] = $file['hook_classname'];
2200 }
2201 }
2202 }
2203 }
2204
2205 /**
2206 * Check if there is an update for a hook
2207 *
2208 * @param string URL to check
2209 * @param string Long version number
2210 * @return bool
2211 */
2212 public function _updateAvailable( $url, $version )
2213 {
2214 if( empty($url) || $version === '' )
2215 {
2216 return false;
2217 }
2218
2219 $classToLoad = IPSLib::loadLibrary( IPS_KERNEL_PATH . '/classFileManagement.php', 'classFileManagement' );
2220 $checker = new $classToLoad();
2221
2222 /* Timeout to prevent page from taking too long */
2223 $checker->timeout = 5;
2224
2225 /* Setup url and check */
2226 $url = str_replace( 'php&', 'php?', $url );
2227 $url .= '&boardVersion=' . IPB_LONG_VERSION . '&version=' . $version;
2228
2229 $return = $checker->getFileContents( $url );
2230
2231 /* Return the content (if valid) or return 'no update' */
2232 $return = ( strpos( $return, '0' ) === 0 || strpos( $return, '1' ) === 0 ) ? explode( '|', $return ) : array( 0 );
2233
2234 /* Replace {IPSID} id necessary */
2235 if ( strpos( $return[1], '{IPSID}' ) )
2236 {
2237 if ( ipsRegistry::$settings['ipb_reg_number'] )
2238 {
2239 $exploded = explode( '-', ipsRegistry::$settings['ipb_reg_number'] );
2240 $return[1] = str_replace( '{IPSID}', $exploded[3], $return[1] );
2241 }
2242 else
2243 {
2244 $return[1] = "https://www.invisionpower.com/clients";
2245 }
2246 }
2247
2248 return $return;
2249 }
2250
2251 /**
2252 * Sorts hooks by their position
2253 * (this is used to avoid a mysql filesort)
2254 *
2255 * @param array $a First hook data
2256 * @param array $b Second hook data
2257 * @return @e integer
2258 */
2259 protected function sortHooksByPosition( $a, $b )
2260 {
2261 if ( $a['hook_position'] == $b['hook_position'] )
2262 {
2263 return 0;
2264 }
2265
2266 return ( $a['hook_position'] < $b['hook_position'] ) ? -1 : 1;
2267 }
2268
2269 /**
2270 * Hooks overview
2271 *
2272 * @return @e void [Outputs to screen]
2273 */
2274 public function _hooksOverview()
2275 {
2276 /* INI */
2277 $hooksFound = array( 'installed' => array(), 'uninstalled' => array() );
2278 $hookwarnings = 0;
2279 $hookUpdates = 0;
2280 $checkUpdates = !empty( $this->request['checkUpdates'] ) ? true : false;
2281 $message = '';
2282 $confGroupIdsToLoad = array();
2283 $confGroupKeysToLoad= array();
2284
2285 /* Get current hooks */
2286 $this->DB->build( array( 'select' => '*', 'from' => 'core_hooks' ) );
2287 $outer = $this->DB->execute();
2288
2289 while( $r = $this->DB->fetch($outer) )
2290 {
2291 /* Format some dates */
2292 $r['_updated'] = $this->registry->getClass('class_localization')->formatTime( $r['hook_updated'] );
2293 $r['_installed'] = $this->registry->getClass('class_localization')->formatTime( $r['hook_installed'] );
2294
2295 /* Got updates? */
2296 if ( $r['hook_update_check'] && $checkUpdates === true )
2297 {
2298 $r['hook_update_available'] = $this->_updateAvailable( $r['hook_update_check'], $r['hook_version_long'] );
2299
2300 if ( $r['hook_update_available'][0] )
2301 {
2302 $hookUpdates++;
2303 }
2304 }
2305 else
2306 {
2307 $r['hook_update_available'] = array( 0 );
2308 }
2309
2310 /* Got some setting groups to take care of? */
2311 $r['_has_setting_links'] = null;
2312 $r['_hook_extra_data'] = IPSLib::isSerialized($r['hook_extra_data']) ? unserialize($r['hook_extra_data']) : array();
2313
2314 if ( is_array($r['_hook_extra_data']['settingGroups']) && count($r['_hook_extra_data']['settingGroups']) )
2315 {
2316 $r['_has_setting_links'] = array();
2317
2318 foreach( $r['_hook_extra_data']['settingGroups'] as $_group )
2319 {
2320 $confGroupKeysToLoad[ $_group ] = $_group;
2321 }
2322 }
2323
2324 if ( is_array($r['_hook_extra_data']['settings']) && count($r['_hook_extra_data']['settings']) )
2325 {
2326 $r['_has_setting_links'] = array();
2327
2328 $this->DB->build( array( 'select' => 'conf_group, conf_key', 'from' => 'core_sys_conf_settings', 'where' => "conf_key IN ('" . implode( "','", $r['_hook_extra_data']['settings'] ) . "')" ) );
2329 $this->DB->execute();
2330
2331 while( $hsg = $this->DB->fetch() )
2332 {
2333 $confGroupIdsToLoad[ $hsg['conf_key'] ] = $hsg['conf_group'];
2334 }
2335 }
2336
2337 /* Check requirements */
2338 $r['_require_errors'] = $this->checkHookRequirements( $r );
2339
2340 /* Store */
2341 if( $r['hook_enabled'] )
2342 {
2343 if ( count($r['_require_errors']) )
2344 {
2345 $hookwarnings++;
2346 }
2347
2348 $hooksFound['installed'][ $r['hook_id'] ] = $r;
2349 }
2350 else
2351 {
2352 $hooksFound['uninstalled'][ $r['hook_id'] ] = $r;
2353 }
2354 }
2355
2356 /* Got groups to load? */
2357 $where = array();
2358
2359 if ( count($confGroupKeysToLoad) )
2360 {
2361 $where[] = "conf_title_keyword IN ('" . implode( "','", $confGroupKeysToLoad ) . "')";
2362 }
2363
2364 if ( count($confGroupIdsToLoad) )
2365 {
2366 $where[] = "conf_title_id IN (" . implode( ",", $confGroupIdsToLoad ) . ")";
2367 }
2368
2369 if ( count($where) )
2370 {
2371 $confGroupsLoaded = array();
2372
2373 $this->DB->build( array( 'select' => '*', 'from' => 'core_sys_settings_titles', 'where' => implode( ' OR ', $where ) ) );
2374 $this->DB->execute();
2375
2376 while( $gr = $this->DB->fetch() )
2377 {
2378 if ( empty($gr['conf_title_noshow']) )
2379 {
2380 $confGroupsLoaded['ids'][ $gr['conf_title_id'] ] = $gr;
2381 $confGroupsLoaded['keys'][ $gr['conf_title_keyword'] ] = & $confGroupsLoaded['ids'][ $gr['conf_title_id'] ];
2382 }
2383 }
2384
2385 /* Merge back the data */
2386 foreach( $hooksFound as $rk => $rt )
2387 {
2388 foreach( $rt as $sk => $st )
2389 {
2390 if ( is_array($st['_has_setting_links']) )
2391 {
2392 if ( is_array($st['_hook_extra_data']['settingGroups']) && count($st['_hook_extra_data']['settingGroups']) )
2393 {
2394 foreach( $st['_hook_extra_data']['settingGroups'] as $_group )
2395 {
2396 if ( isset($confGroupsLoaded['keys'][ $_group ]) )
2397 {
2398 $hooksFound[ $rk ][ $sk ]['_has_setting_links'][ $confGroupsLoaded['keys'][ $_group ]['conf_title_id'] ] = $confGroupsLoaded['keys'][ $_group ]['conf_title_title'];
2399 }
2400 }
2401 }
2402
2403 if ( is_array($st['_hook_extra_data']['settings']) && count($st['_hook_extra_data']['settings']) )
2404 {
2405 foreach( $st['_hook_extra_data']['settings'] as $_id )
2406 {
2407 if ( isset($confGroupsLoaded['ids'][ $confGroupIdsToLoad[ $_id ] ]) )
2408 {
2409 $hooksFound[ $rk ][ $sk ]['_has_setting_links'][ $confGroupIdsToLoad[ $_id ] ] = $confGroupsLoaded['ids'][ $confGroupIdsToLoad[ $_id ] ]['conf_title_title'];
2410 }
2411 }
2412 }
2413 }
2414 }
2415 }
2416 }
2417
2418 /* Got updates to show? */
2419 if ( $checkUpdates === true )
2420 {
2421 $message = ( $hookUpdates == 1 ) ? $this->lang->words['updates_string_single'] : sprintf( $this->lang->words['updates_string_more'], $hookUpdates );
2422 }
2423
2424 if ( count($hooksFound['installed']) )
2425 {
2426 uasort( $hooksFound['installed'], array( $this, 'sortHooksByPosition' ) );
2427 }
2428
2429 /* Output */
2430 $this->registry->output->html .= $this->html->hooksOverview( $hooksFound, $hookwarnings, $message );
2431 }
2432
2433 /**
2434 * Disables all hook
2435 *
2436 * @return @e void [Outputs to screen]
2437 */
2438 protected function _disableAllHooks()
2439 {
2440 /* INIT */
2441 $activeHookIds = array();
2442
2443 /* Query Enabled Hooks */
2444 $this->DB->build( array( 'select' => 'hook_id', 'from' => 'core_hooks', 'where' => "hook_enabled=1" ) );
2445 $this->DB->execute();
2446
2447 while( $r = $this->DB->fetch() )
2448 {
2449 $activeHookIds[] = $r['hook_id'];
2450 }
2451
2452 /* Make sure there were active hooks */
2453 if( ! count( $activeHookIds ) )
2454 {
2455 $this->registry->output->global_message = $this->lang->words['hook_disable_all_none'];
2456 $this->_hooksOverview();
2457 return;
2458 }
2459
2460 /* Disable hooks */
2461 $this->DB->update( 'core_hooks', array( 'hook_enabled' => 0 ), 'hook_id IN( ' . implode( ',', $activeHookIds ) . ' ) ' );
2462
2463 /* Save to cache */
2464 $this->cache->setCache( 'disabledHooksCache', $activeHookIds, array( 'array' => 1 ) );
2465
2466 /* Rebuild cache */
2467 $this->rebuildHooksCache();
2468
2469 /* All Done */
2470 $this->registry->output->global_message = $this->lang->words['hook_disabled_all'];
2471 $this->registry->output->silentRedirectWithMessage( $this->settings['base_url'] . $this->form_code . '&do=hooks_overview' );
2472 }
2473
2474 /**
2475 * Re-enable all hook
2476 *
2477 * @return @e void [Outputs to screen]
2478 */
2479 protected function _reenableAllHooks()
2480 {
2481 /* INIT */
2482 $activeHookIds = $this->cache->getCache( 'disabledHooksCache' );
2483
2484 /* Make sure there were active hooks */
2485 if( ! count( $activeHookIds ) )
2486 {
2487 $this->registry->output->global_message = $this->lang->words['hook_enable_all_none'];
2488 $this->_hooksOverview();
2489 return;
2490 }
2491
2492 /* Disable hooks */
2493 $this->DB->update( 'core_hooks', array( 'hook_enabled' => 1 ), 'hook_id IN( ' . implode( ',', $activeHookIds ) . ' ) ' );
2494
2495 /* Save to cache */
2496 $this->cache->setCache( 'disabledHooksCache', array(), array( 'array' => 1 ) );
2497
2498 /* Rebuild cache */
2499 $this->rebuildHooksCache();
2500
2501 /* Flag skins for recache */
2502 require_once( IPS_ROOT_PATH . 'sources/classes/skins/skinFunctions.php' );/*noLibHook*/
2503 require_once( IPS_ROOT_PATH . 'sources/classes/skins/skinCaching.php' );/*noLibHook*/
2504 $skinCaching = new skinCaching( $this->registry );
2505
2506 /* Flag all for recache */
2507 $skinCaching->flagSetForRecache();
2508
2509 /* All Done */
2510 $this->registry->output->global_message = $this->lang->words['hook_reenable_all'];
2511 $this->registry->output->silentRedirectWithMessage( $this->settings['base_url'] . $this->form_code . '&do=hooks_overview' );
2512 }
2513
2514 /**
2515 * Disables a hook
2516 *
2517 * @return @e void [Outputs to screen]
2518 */
2519 protected function _disableHook()
2520 {
2521 /* INI */
2522 $hook_id = intval( $this->request['id'] );
2523
2524 if( !$hook_id )
2525 {
2526 $this->registry->output->showError( $this->lang->words['h_noedit'], '1118.D' );
2527 }
2528
2529 $hookData = $this->DB->buildAndFetch( array( 'select' => '*', 'from' => 'core_hooks', 'where' => 'hook_id=' . $hook_id ) );
2530
2531 if( !$hookData['hook_id'] )
2532 {
2533 $this->registry->output->showError( $this->lang->words['h_noedit'], '1119.D' );
2534 }
2535
2536 /* Do update */
2537 $this->DB->update( 'core_hooks', array( 'hook_enabled' => 0, 'hook_position' => 0 ), "hook_id={$hookData['hook_id']}" );
2538
2539 /* Rebuild cache */
2540 $this->rebuildHooksCache();
2541
2542 /* Rebuild global caches */
2543 try
2544 {
2545 IPSLib::cacheGlobalCaches();
2546 }
2547 catch( Exception $e ){}
2548
2549 /* Flag skins for recache */
2550 require_once( IPS_ROOT_PATH . 'sources/classes/skins/skinFunctions.php' );/*noLibHook*/
2551 require_once( IPS_ROOT_PATH . 'sources/classes/skins/skinCaching.php' );/*noLibHook*/
2552 $skinCaching = new skinCaching( $this->registry );
2553 $skinCaching->flagSetForRecache();
2554
2555 /* Redirect */
2556 $this->registry->output->setMessage( $this->lang->words['h_hasbeendisabled'] );
2557 $this->registry->output->silentRedirectWithMessage( $this->settings['base_url'] . $this->form_code . '&do=hooks_overview' );
2558 }
2559
2560 /**
2561 * Enables a hook
2562 *
2563 * @return @e void [Outputs to screen]
2564 */
2565 protected function _enableHook()
2566 {
2567 /* INI */
2568 $hook_id = intval($this->request['id']);
2569
2570 if( !$hook_id )
2571 {
2572 $this->registry->output->showError( $this->lang->words['h_noedit'], '1118.E' );
2573 }
2574
2575 $hookData = $this->DB->buildAndFetch( array( 'select' => '*', 'from' => 'core_hooks', 'where' => 'hook_id=' . $hook_id ) );
2576
2577 if( !$hookData['hook_id'] )
2578 {
2579 $this->registry->output->showError( $this->lang->words['h_noedit'], '1119.E' );
2580 }
2581
2582 /* Check for requirements */
2583 if ( empty($this->request['skipRequirements']) )
2584 {
2585 $errors = $this->checkHookRequirements( $hookData );
2586
2587 if ( count($errors) )
2588 {
2589 $this->registry->output->silentRedirectWithMessage( $this->settings['base_url'] . $this->form_code . '&do=check_requirements&fromInstall=1&id='.$hook_id );
2590 }
2591 }
2592
2593 /* Do update */
2594 $this->DB->update( 'core_hooks', array( 'hook_enabled' => 1 ), "hook_id={$hookData['hook_id']}" );
2595
2596 /* Rebuild cache */
2597 $this->rebuildHooksCache();
2598
2599 /* Rebuild global caches */
2600 try
2601 {
2602 IPSLib::cacheGlobalCaches();
2603 }
2604 catch( Exception $e ){}
2605
2606 /* Flag skins for recache */
2607 require_once( IPS_ROOT_PATH . 'sources/classes/skins/skinFunctions.php' );/*noLibHook*/
2608 require_once( IPS_ROOT_PATH . 'sources/classes/skins/skinCaching.php' );/*noLibHook*/
2609 $skinCaching = new skinCaching( $this->registry );
2610 $skinCaching->flagSetForRecache();
2611
2612 /* Redirect */
2613 $this->registry->output->global_message = $this->lang->words['h_hasbeenenabled'];
2614 $this->registry->output->silentRedirectWithMessage( $this->settings['base_url'] . $this->form_code . '&do=hooks_overview' );
2615 }
2616
2617 /**
2618 * Uninstall a hook
2619 *
2620 * @return @e void [Outputs to screen]
2621 */
2622 protected function _uninstallHook()
2623 {
2624 /* Init vars */
2625 $hook_id = intval( $this->request['id'] );
2626
2627 $hook = $this->DB->buildAndFetch( array( 'select' => '*', 'from' => 'core_hooks', 'where' => 'hook_id=' . $hook_id ) );
2628 $extra_data = unserialize( $hook['hook_extra_data'] );
2629
2630 if ( ! $hook['hook_id'] )
2631 {
2632 $this->registry->output->global_message = $this->lang->words['h_hasbeenun'];
2633 $this->registry->output->silentRedirectWithMessage( $this->settings['base_url'] . $this->form_code . '&do=hooks_overview' );
2634 }
2635
2636 /* Got any custom file to initialize? */
2637 $_customFile = '';
2638 $customClass = null;
2639
2640 if ( $extra_data['display']['custom'] )
2641 {
2642 $_customFile = IPS_HOOKS_PATH . 'install_' . $extra_data['display']['custom'];
2643
2644 if( is_file( $_customFile ) )
2645 {
2646 require_once( $_customFile );/*noLibHook*/
2647
2648 $classname = str_replace( '.php', '', $extra_data['display']['custom'] );
2649
2650 if( class_exists( $classname ) )
2651 {
2652 $customClass = new $classname( $this->registry );
2653
2654 if( method_exists( $customClass, 'pre_uninstall' ) )
2655 {
2656 $customClass->pre_uninstall();
2657 }
2658 }
2659 }
2660 }
2661
2662 /* Get associated files */
2663 $this->DB->build( array( 'select' => 'hook_file_stored', 'from' => 'core_hooks_files', 'where' => 'hook_hook_id=' . $hook_id ) );
2664 $this->DB->execute();
2665
2666 while( $r = $this->DB->fetch() )
2667 {
2668 @unlink( IPS_HOOKS_PATH . $r['hook_file_stored'] );
2669 }
2670
2671 /* Delete hook file entries */
2672 $this->DB->delete( 'core_hooks_files', "hook_hook_id={$hook_id}" );
2673
2674 //-----------------------------------------
2675 // Settings or setting groups?
2676 //-----------------------------------------
2677
2678 if( count($extra_data['settingGroups']) )
2679 {
2680 $this->DB->delete( 'core_sys_settings_titles', "conf_title_keyword IN('" . implode( "','", $extra_data['settingGroups'] ) . "')" );
2681 }
2682
2683 if( count($extra_data['settings']) )
2684 {
2685 $this->DB->delete( 'core_sys_conf_settings', "conf_key IN('" . implode( "','", $extra_data['settings'] ) . "')" );
2686 }
2687
2688 $this->cache->rebuildCache( 'settings', 'global' );
2689
2690 //-----------------------------------------
2691 // Language strings/files
2692 //-----------------------------------------
2693
2694 if( count($extra_data['language']) )
2695 {
2696 foreach( $extra_data['language'] as $file => $bit )
2697 {
2698 $this->DB->delete( 'core_sys_lang_words', "word_pack='{$file}' AND word_key IN('" . implode( "','", $bit ) . "')" );
2699 }
2700
2701 $langPacks = array();
2702
2703 $this->DB->build( array( 'select' => 'lang_id', 'from' => 'core_sys_lang' ) );
2704 $this->DB->execute();
2705
2706 while( $r = $this->DB->fetch() )
2707 {
2708 $langPacks[] = $r['lang_id'];
2709 }
2710
2711 $classToLoad = IPSLib::loadActionOverloader( IPSLib::getAppDir('core') . '/modules_admin/languages/manage_languages.php', 'admin_core_languages_manage_languages' );
2712 $langLib = new $classToLoad();
2713 $langLib->makeRegistryShortcuts( $this->registry );
2714
2715 foreach( $langPacks as $langId )
2716 {
2717 $langLib->cacheToDisk( $langId );
2718 }
2719 }
2720
2721 //-----------------------------------------
2722 // Modules
2723 //-----------------------------------------
2724
2725 if( count($extra_data['modules']) )
2726 {
2727 foreach( $extra_data['modules'] as $module )
2728 {
2729 list( $_side, $_app, $_module ) = explode( '-', $module );
2730 $_is_admin = ( $_side == 'admin' ) ? 1 : 0;
2731
2732 $this->DB->delete( 'core_sys_module', "sys_module_application='{$_app}' AND sys_module_key='{$_module}' AND sys_module_admin={$_is_admin}" );
2733 }
2734
2735 $this->cache->rebuildCache( 'module_cache', 'global' );
2736 $this->cache->rebuildCache( 'app_menu_cache', 'global' );
2737 }
2738
2739 //-----------------------------------------
2740 // Help files
2741 //-----------------------------------------
2742
2743 if( count($extra_data['help']) )
2744 {
2745 $this->DB->delete( 'faq', "title IN('" . implode( "','", $extra_data['help'] ) . "')" );
2746 }
2747
2748 //-----------------------------------------
2749 // Skin templates
2750 //-----------------------------------------
2751
2752 if( count($extra_data['templates']) )
2753 {
2754 foreach( $extra_data['templates'] as $group => $bits )
2755 {
2756 $this->DB->delete( 'skin_templates', "template_group='{$group}' AND template_name IN('" . implode( "','", $bits ) . "')" );
2757 }
2758
2759 require_once( IPS_ROOT_PATH . 'sources/classes/skins/skinFunctions.php' );/*noLibHook*/
2760 require_once( IPS_ROOT_PATH . 'sources/classes/skins/skinCaching.php' );/*noLibHook*/
2761 $skinCaching = new skinCaching( $this->registry );
2762 $skinCaching->rebuildPHPTemplates( 0 );
2763 }
2764
2765 //-----------------------------------------
2766 // CSS
2767 //-----------------------------------------
2768
2769 if( count( $extra_data['css'] ) )
2770 {
2771 $this->DB->delete( 'skin_css', "css_group IN('" . implode( "','", $extra_data['css'] ) . "')" );
2772
2773 if( ! $skinCaching )
2774 {
2775 require_once( IPS_ROOT_PATH . 'sources/classes/skins/skinFunctions.php' );/*noLibHook*/
2776 require_once( IPS_ROOT_PATH . 'sources/classes/skins/skinCaching.php' );/*noLibHook*/
2777 $skinCaching = new skinCaching( $this->registry );
2778 }
2779
2780 $skinCaching->rebuildCSSCache( 0 );
2781 }
2782
2783 //-----------------------------------------
2784 // Tasks
2785 //-----------------------------------------
2786
2787 if( count($extra_data['tasks']) )
2788 {
2789 $this->DB->delete( 'task_manager', "task_key IN('" . implode( "','", $extra_data['tasks'] ) . "')" );
2790 }
2791
2792 //-----------------------------------------
2793 // Database changes
2794 //-----------------------------------------
2795
2796 if( is_array($extra_data['database']['create']) AND count($extra_data['database']['create']) )
2797 {
2798 foreach( $extra_data['database']['create'] as $create_query )
2799 {
2800 $this->DB->return_die = true;
2801 $this->DB->dropTable( $create_query['name'] );
2802 $this->DB->return_die = false;
2803 }
2804 }
2805
2806 if( is_array($extra_data['database']['alter']) AND count($extra_data['database']['alter']) )
2807 {
2808 foreach( $extra_data['database']['alter'] as $alter_query )
2809 {
2810 $this->DB->return_die = true;
2811
2812 if( $alter_query['altertype'] == 'add' )
2813 {
2814 if( $this->DB->checkForField( $alter_query['field'], $alter_query['table'] ) )
2815 {
2816 $this->DB->dropField( $alter_query['table'], $alter_query['field'] );
2817 }
2818 }
2819 else if( $alter_query['altertype'] == 'change' )
2820 {
2821 if( $this->DB->checkForField( $alter_query['newfield'], $alter_query['table'] ) )
2822 {
2823 $this->DB->changeField( $alter_query['table'] , $alter_query['newfield'], $alter_query['field'], $alter_query['fieldtype'], $alter_query['default'] );
2824 }
2825 }
2826
2827 $this->DB->return_die = false;
2828 }
2829 }
2830
2831 if( is_array($extra_data['database']['update']) AND count($extra_data['database']['update']) )
2832 {
2833 foreach( $extra_data['database']['update'] as $update_query )
2834 {
2835 $this->DB->return_die = true;
2836 $this->DB->update( $update_query['table'], array( $update_query['field'] => $update_query['oldvalue'] ), $update_query['where'] );
2837 $this->DB->return_die = false;
2838 }
2839 }
2840
2841 if( is_array($extra_data['database']['insert']) AND count($extra_data['database']['insert']) )
2842 {
2843 foreach( $extra_data['database']['insert'] as $insert_query )
2844 {
2845 if( $insert_query['fordelete'] )
2846 {
2847 $this->DB->return_die = true;
2848 $this->DB->delete( $insert_query['table'], $insert_query['fordelete'] );
2849 $this->DB->return_die = false;
2850 }
2851 }
2852 }
2853
2854 //-----------------------------------------
2855 // Custom install/uninstall script?
2856 //-----------------------------------------
2857
2858 if ( $_customFile )
2859 {
2860 if( is_object($customClass) && method_exists( $customClass, 'uninstall' ) )
2861 {
2862 $customClass->uninstall();
2863 }
2864
2865 @unlink( $_customFile );
2866 }
2867
2868 /* Delete main hook entry */
2869 $this->DB->delete( 'core_hooks', "hook_id={$hook_id}" );
2870
2871 $this->rebuildHooksCache();
2872
2873 /* Done */
2874 $this->registry->output->global_message = $this->lang->words['h_hasbeenun'];
2875 $this->registry->output->silentRedirectWithMessage( $this->settings['base_url'] . $this->form_code . '&do=hooks_overview' );
2876 }
2877
2878 /**
2879 * Hook add/edit form
2880 * This dynamic form allows users to associate multiple files with a single hook.
2881 *
2882 * @param string [add|edit]
2883 * @return @e void [Outputs to screen]
2884 */
2885 protected function _hookForm( $type='add' )
2886 {
2887 /* Init vars */
2888 $form = array();
2889 $files = array();
2890 $hookData = array();
2891 $requirements = array();
2892 $entryPoints = array( 'foreach' => array( array( 'outer.pre', $this->lang->words['h_outerpre'] ),
2893 array( 'inner.pre', $this->lang->words['h_innerpre'] ),
2894 array( 'inner.post', $this->lang->words['h_innerpost'] ),
2895 array( 'outer.post', $this->lang->words['h_outerpost'] )
2896 ),
2897 'if' => array( array( 'pre.startif', $this->lang->words['h_prestartif'] ),
2898 array( 'post.startif', $this->lang->words['h_poststartif'] ),
2899 array( 'pre.else', $this->lang->words['h_preelse']),
2900 array( 'post.else', $this->lang->words['h_postelse'] ),
2901 array( 'pre.endif', $this->lang->words['h_preendif'] ),
2902 array( 'post.endif', $this->lang->words['h_postendif'] )
2903 )
2904 );
2905
2906 /* Edit time? */
2907 if( $type == 'edit' )
2908 {
2909 $id = intval($this->request['id']);
2910
2911 if( !$id )
2912 {
2913 $this->registry->output->showError( $this->lang->words['h_noedit'], 1116 );
2914 }
2915
2916 $hookData = $this->DB->buildAndFetch( array( 'select' => '*', 'from' => 'core_hooks', 'where' => 'hook_id=' . $id ) );
2917
2918 if( !$hookData['hook_id'] )
2919 {
2920 $this->registry->output->showError( $this->lang->words['h_noedit'], 1117 );
2921 }
2922
2923 /* Sort out extra stuff and requirements */
2924 $hookData['hook_extra_data'] = unserialize( $hookData['hook_extra_data'] );
2925 $hookData['hook_requirements'] = unserialize( $hookData['hook_requirements'] );
2926
2927 /* Old data? - @todo: we should remove that around 3.3(4?) */
2928 if ( ! isset($hookData['hook_requirements']['required_applications']['core']) && isset($hookData['hook_requirements']['hook_ipb_version_min']) && ( $hookData['hook_requirements']['hook_ipb_version_min'] > 0 || $hookData['hook_requirements']['hook_ipb_version_max'] > 0 ) )
2929 {
2930 $requirements['core'] = array( 'min_version' => intval($hookData['hook_requirements']['hook_ipb_version_min']), 'max_version' => intval($hookData['hook_requirements']['hook_ipb_version_max']) );
2931 }
2932
2933 if ( is_array($hookData['hook_requirements']['required_applications']) && count($hookData['hook_requirements']['required_applications']) )
2934 {
2935 /* Get the setup class */
2936 require_once( IPS_ROOT_PATH . 'setup/sources/base/setup.php' );/*noLibHook*/
2937
2938 foreach( $hookData['hook_requirements']['required_applications'] as $appKey => $versionData )
2939 {
2940 /* Fetch and check versions */
2941 if ( !isset($this->cachedVersions[ $appKey ]) )
2942 {
2943 $this->cachedVersions[ $appKey ] = IPSSetUp::fetchXmlAppVersions( $appKey );
2944 }
2945
2946 $_versions = $this->cachedVersions[ $appKey ];
2947
2948 krsort($_versions);
2949
2950 /* Setup our default 'no version' value */
2951 $versions = array( array( 0, $this->lang->words['h_any_version'] ) );
2952
2953 foreach( $_versions as $long => $human )
2954 {
2955 if ( $long < 30000 && in_array( $appKey, array( 'core', 'forums', 'members' ) ) )
2956 {
2957 continue;
2958 }
2959
2960 $versions[] = array( $long, $human );
2961 }
2962
2963 $versionData['_versions'] = $versions;
2964 $requirements[ $appKey ] = $versionData;
2965 }
2966 }
2967
2968 /* Sort out hook files */
2969 $index = 1;
2970 $skinGroups = $this->hooksFunctions->getSkinGroups();
2971
2972 $this->DB->build( array( 'select' => '*', 'from' => 'core_hooks_files', 'where' => 'hook_hook_id=' . $id ) );
2973 $outer = $this->DB->execute();
2974
2975 /* Get them outside the while cycle to prevent warnings */
2976 $_dataHooks = IPSLib::getDataHookLocations();
2977
2978 while( $r = $this->DB->fetch($outer) )
2979 {
2980 $r['hook_data'] = unserialize( $r['hook_data'] );
2981
2982 if( $r['hook_type'] == 'templateHooks' )
2983 {
2984 $templates = $this->hooksFunctions->getSkinMethods( $r['hook_data']['skinGroup'] );
2985 $hookIds = $this->hooksFunctions->getHookIds( $r['hook_data']['skinFunction'], $r['hook_data']['type'] );
2986
2987 $r['_skinDropdown'] = $this->registry->output->formDropdown( "skinGroup[{$index}]", $skinGroups, $r['hook_data']['skinGroup'], "skinGroup[{$index}]", "onchange='getTemplatesForAdd({$index});'" );
2988 $r['_templateDropdown'] = $this->registry->output->formDropdown( "skinFunction[{$index}]", $templates, $r['hook_data']['skinFunction'], "skinFunction[{$index}]", "onchange='getTypeOfHook({$index});'" );
2989 $r['_hookTypeDropdown'] = $this->registry->output->formDropdown( "type[{$index}]", array( array( 0, $this->lang->words['a_selectone'] ), array( 'foreach', $this->lang->words['hook_foreach_loop'] ), array( 'if', $this->lang->words['hook_if_statement'] ) ), $r['hook_data']['type'], "type[{$index}]", "onchange='getHookIds({$index});'" );
2990 $r['_hookIdsDropdown'] = $this->registry->output->formDropdown( "id[{$index}]", $hookIds, $r['hook_data']['id'], "id[{$index}]", "onchange='getHookEntryPoints({$index});'" );
2991 $r['_hookEPDropdown'] = $this->registry->output->formDropdown( "position[{$index}]", $r['hook_data']['type'] == 'foreach' ? $entryPoints['foreach'] : $entryPoints['if'], $r['hook_data']['position'] );
2992 }
2993
2994 if( $r['hook_type'] == 'dataHooks' )
2995 {
2996 $r['_dataLocationDropdown'] = $this->registry->output->formDropdown( "dataLocation[{$index}]", $_dataHooks, $r['hook_data']['dataLocation'] );
2997 }
2998
2999 $files[ $index ] = $r;
3000 $index++;
3001 }
3002
3003 $action = 'do_edit_hook';
3004 }
3005 else
3006 {
3007 $action = 'do_create_hook';
3008 }
3009
3010 /* Info */
3011 foreach( array( 'hook_name', 'hook_desc', 'hook_key', 'hook_version_human', 'hook_version_long', 'hook_author', 'hook_email', 'hook_website', 'hook_update_check' ) as $_hook_key )
3012 {
3013 $form[ $_hook_key ] = $this->registry->output->formSimpleInput( $_hook_key , isset($this->request[ $_hook_key ]) ? $this->request[ $_hook_key ] : $hookData[ $_hook_key ], 60 );
3014 }
3015
3016 /* Requirements */
3017 foreach( array( 'hook_php_version_min', 'hook_php_version_max' ) as $_version_key )
3018 {
3019 $form[ $_version_key ] = $this->registry->output->formSimpleInput( $_version_key , isset($this->request[ $_version_key ]) ? $this->request[ $_version_key ] : $hookData['hook_requirements'][ $_version_key ], 20 );
3020 }
3021
3022 /* Setup the global caches DD */
3023 $_caches = array();
3024
3025 $this->DB->build( array( 'select' => 'cs_key',
3026 'from' => 'cache_store',
3027 'order' => 'cs_key ASC'
3028 ) );
3029 $this->DB->execute();
3030
3031 while( $gc = $this->DB->fetch() )
3032 {
3033 $_caches[] = array( $gc['cs_key'], $gc['cs_key'] );
3034 }
3035
3036 sort( $_caches );
3037
3038 $form['hook_global_caches'] = $this->registry->output->formMultiDropdown( 'hook_global_caches[]', $_caches, isset($_POST['hook_global_caches']) ? $_POST['hook_global_caches'] : explode(',',$hookData['hook_global_caches']), 15 );
3039
3040 /* Get some data for the javascript */
3041 $form['jsDataTypes'] = IPSText::br2nl( $this->registry->output->formDropdown( "type[#{index}]", array( array( 0, $this->lang->words['a_selectone'] ), array( 'foreach', $this->lang->words['hook_foreach_loop'] ), array( 'if', $this->lang->words['hook_if_statement'] ) ), '', "type[#{index}]", "onchange='getHookIds(#{index});'" ) );
3042 $form['jsDataPoints'] = IPSText::br2nl( $this->registry->output->formDropdown( "position[#{index}]", $r['hook_data']['type'] == 'foreach' ? $entryPoints['foreach'] : $entryPoints['if'] ) );
3043
3044 /* Output */
3045 $this->registry->output->extra_nav[] = array( $this->settings['base_url'] . $this->form_code . 'do=hooks_overview', $this->lang->words['menu__manage_hooks'] );
3046 $this->registry->output->html .= $this->html->hookForm( $form, $action, $hookData, $files, $requirements );
3047 }
3048
3049 /**
3050 * Hook add/edit save
3051 * Save the new (or updated) hook record
3052 *
3053 * @param string [add|edit]
3054 * @return @e void [Outputs to screen]
3055 */
3056 protected function _hookSave( $type='add' )
3057 {
3058 /* Init vars */
3059 $hookData = array();
3060 $newFiles = array();
3061 $requireApp = array();
3062 $_hookFiles = array();
3063 $extraKey = '';
3064
3065 /* Get data if we are editing */
3066 if( $type == 'edit' )
3067 {
3068 $id = intval($this->request['hook_id']);
3069
3070 if( !$id )
3071 {
3072 $this->registry->output->showError( $this->lang->words['h_noedit'], 1118 );
3073 }
3074
3075 $hookData = $this->DB->buildAndFetch( array( 'select' => '*', 'from' => 'core_hooks', 'where' => 'hook_id=' . $id ) );
3076
3077 if( !$hookData['hook_id'] )
3078 {
3079 $this->registry->output->showError( $this->lang->words['h_noedit'], 1119 );
3080 }
3081
3082 $extraKey = ' AND hook_id != ' . $hookData['hook_id'];
3083 }
3084
3085 /* Error Checking */
3086 if( ! $this->request['hook_name'] )
3087 {
3088 $errors[] = $this->lang->words['hook_form_no_title'];
3089 }
3090
3091 if( empty( $this->request['hook_key'] ) )
3092 {
3093 $errors[] = $this->lang->words['hook_form_no_key'];
3094 }
3095 else
3096 {
3097 $keyCheck = $this->DB->buildAndFetch( array( 'select' => 'count(*) as found', 'from' => 'core_hooks', 'where' => "hook_key='" . $this->DB->addSlashes( $this->request['hook_key'] ) . "'" . $extraKey ) );
3098
3099 if ( $keyCheck['found'] > 0 )
3100 {
3101 $errors[] = $this->lang->words['hook_form_dupe_key'];
3102 }
3103 }
3104
3105 /* Got any errors? */
3106 if ( is_array( $errors ) && count( $errors ) )
3107 {
3108 $this->registry->output->global_error = implode( '<br />', $errors );
3109 $this->_hookForm();
3110 return;
3111 }
3112
3113 /* Not IN_DEV? */
3114 if ( ! IN_DEV )
3115 {
3116 $this->DB->build( array( 'select' => '*', 'from' => 'core_hooks_files', 'where' => 'hook_hook_id=' . intval($this->request['hook_id']) ) );
3117 $this->DB->execute();
3118
3119 while( $_r = $this->DB->fetch() )
3120 {
3121 $_hookFiles[ $_r['hook_classname'] ] = $_r['hook_file_stored'];
3122 }
3123 }
3124
3125 /* Check for requirements */
3126 if ( is_array( $this->request['requireApp'] ) AND count( $this->request['requireApp'] ) )
3127 {
3128 foreach( $this->request['requireApp'] as $_index => $app_key )
3129 {
3130 if ( $app_key )
3131 {
3132 $minVersion = intval($this->request['minVersion'][ $_index ]);
3133 $maxVersion = intval($this->request['maxVersion'][ $_index ]);
3134
3135 $requireApp[ $app_key ] = array( 'app_name' => ipsRegistry::$applications[ $app_key ]['app_title'],
3136 'min_version' => $minVersion,
3137 'max_version' => $maxVersion
3138 );
3139 }
3140 }
3141 }
3142
3143 /* Check for files */
3144 if ( is_array( $this->request['file'] ) AND count( $this->request['file'] ) )
3145 {
3146 foreach( $this->request['file'] as $index => $file )
3147 {
3148 if ( $file )
3149 {
3150 $newFiles[ $index ] = array('hook_file_real' => $file,
3151 'hook_type' => $this->request['hook_type'][ $index ],
3152 'hook_classname' => $this->request['hook_classname'][ $index ],
3153 'hook_data' => serialize( array('dataLocation' => trim( $this->request['dataLocation'][ $index ] ),
3154 'libApplication' => trim( $this->request['libApplication'][ $index ] ),
3155 'classToOverload' => trim($this->request['classToOverload'][ $index ]),
3156 'skinGroup' => $this->request['skinGroup'][ $index ],
3157 'skinFunction' => $this->request['skinFunction'][ $index ],
3158 'type' => $this->request['type'][ $index ],
3159 'id' => $this->request['id'][ $index ],
3160 'position' => $this->request['position'][ $index ],
3161 )
3162 )
3163 );
3164
3165 /**
3166 * @link http://community.invisionpower.com/tracker/issue-22084-hook-edit-does-not-preserve-correct-name/
3167 * We don't want to reset the stored name if you are not in developer mode
3168 */
3169 if ( IN_DEV )
3170 {
3171 $newFiles[ $index ]['hook_file_stored'] = $file; // During import this is a random name, but for devs it's actual file
3172 }
3173 else
3174 {
3175 $newFiles[ $index ]['hook_file_stored'] = $_hookFiles[ $this->request['hook_classname'][ $index ] ] ? $_hookFiles[ $this->request['hook_classname'][ $index ] ] : $file;
3176 }
3177 }
3178 }
3179 }
3180
3181 /* Get position */
3182 if ( $type == 'add' )
3183 {
3184 $position = $this->DB->buildAndFetch( array( 'select' => 'MAX(hook_position) as newPos', 'from' => 'core_hooks' ) );
3185
3186 $position['newPos'] = intval($position['newPos']) + 1;
3187 }
3188 else
3189 {
3190 $position['newPos'] = $hookData['hook_position'];
3191 }
3192
3193 $mainHookRecord = array('hook_name' => trim($this->request['hook_name']),
3194 'hook_key' => substr( trim($this->request['hook_key']), 0, 32 ),
3195 'hook_global_caches' => ( is_array($this->request['hook_global_caches']) && count($this->request['hook_global_caches']) ) ? implode(',', $this->request['hook_global_caches']) : '',
3196 'hook_desc' => trim($this->request['hook_desc']),
3197 'hook_version_human' => trim($this->request['hook_version_human']),
3198 'hook_version_long' => trim($this->request['hook_version_long']),
3199 'hook_author' => trim($this->request['hook_author']),
3200 'hook_email' => trim($this->request['hook_email']),
3201 'hook_website' => trim($this->request['hook_website']),
3202 'hook_update_check' => trim($this->request['hook_update_check']),
3203 'hook_enabled' => $type == 'add' ? 1 : $hookData['hook_enabled'],
3204 'hook_installed' => $type == 'add' ? IPS_UNIX_TIME_NOW : $hookData['hook_installed'],
3205 'hook_updated' => $type == 'add' ? 0 : IPS_UNIX_TIME_NOW,
3206 'hook_position' => $position['newPos'],
3207 'hook_requirements' => serialize( array('required_applications' => $requireApp,
3208 'hook_php_version_min' => trim($this->request['hook_php_version_min']),
3209 'hook_php_version_max' => trim($this->request['hook_php_version_max'])
3210 )
3211 )
3212 );
3213
3214 if( $type == 'edit' )
3215 {
3216 $this->DB->update( 'core_hooks', $mainHookRecord, 'hook_id=' . $hookData['hook_id'] );
3217
3218 $this->DB->delete( 'core_hooks_files', 'hook_hook_id=' . $hookData['hook_id'] );
3219 }
3220 else
3221 {
3222 $this->DB->insert( 'core_hooks', $mainHookRecord );
3223
3224 $hookData['hook_id'] = $this->DB->getInsertId();
3225 }
3226
3227 foreach( $newFiles as $index => $toInsert )
3228 {
3229 $toInsert['hook_hook_id'] = $hookData['hook_id'];
3230
3231 $this->DB->insert( 'core_hooks_files', $toInsert );
3232 }
3233
3234 /* Rebuild cache */
3235 $this->rebuildHooksCache();
3236
3237 /* Rebuild global caches */
3238 try
3239 {
3240 IPSLib::cacheGlobalCaches();
3241 }
3242 catch( Exception $e ){}
3243
3244 /* Flag skins for recache */
3245 require_once( IPS_ROOT_PATH . 'sources/classes/skins/skinFunctions.php' );/*noLibHook*/
3246 require_once( IPS_ROOT_PATH . 'sources/classes/skins/skinCaching.php' );/*noLibHook*/
3247 $skinCaching = new skinCaching( $this->registry );
3248 $skinCaching->flagSetForRecache();
3249
3250 /* Redirect */
3251 $this->registry->output->setMessage( $this->lang->words['h_saved'] );
3252 $this->registry->output->silentRedirectWithMessage( $this->settings['base_url'] . $this->form_code . '&do=hooks_overview' );
3253 }
3254
3255 /**
3256 * View details about a hook
3257 *
3258 * @return @e void [Outputs to screen]
3259 */
3260 protected function _viewDetails()
3261 {
3262 $id = intval($this->request['id']);
3263
3264 if( !$id )
3265 {
3266 $this->registry->output->showError( $this->lang->words['h_nodetails'], 11110 );
3267 }
3268
3269 $hookData = $this->DB->buildAndFetch( array( 'select' => '*', 'from' => 'core_hooks', 'where' => 'hook_id=' . $id ) );
3270
3271 if( !$hookData['hook_id'] )
3272 {
3273 $this->registry->output->showError( $this->lang->words['h_nodetails'], 11111 );
3274 }
3275
3276 /* Extra data */
3277 $hookData['_updated'] = $this->registry->getClass('class_localization')->formatTime( $hookData['hook_updated'] );
3278 $hookData['hook_extra_data'] = unserialize( $hookData['hook_extra_data'] );
3279
3280 /* Meets requirements? */
3281 $hookData['_require_errors'] = $this->checkHookRequirements( $hookData );
3282
3283 /* Got updates? */
3284 if ( $hookData['hook_update_check'] )
3285 {
3286 $hookData['hook_update_available'] = $this->_updateAvailable( $hookData['hook_update_check'], $hookData['hook_version_long'] );
3287 }
3288 else
3289 {
3290 $hookData['hook_update_available'] = array( 0 );
3291 }
3292
3293 /* Get files */
3294 $files = array();
3295 $index = 1;
3296
3297 $this->DB->build( array( 'select' => '*', 'from' => 'core_hooks_files', 'where' => 'hook_hook_id=' . $id ) );
3298 $this->DB->execute();
3299
3300 while( $r = $this->DB->fetch() )
3301 {
3302 $r['hook_data'] = unserialize( $r['hook_data'] );
3303 $files[ $index ] = $r;
3304 $index++;
3305 }
3306
3307 /* Output */
3308 $this->registry->output->extra_nav[] = array( $this->settings['base_url'] . $this->form_code . 'do=hooks_overview', $this->lang->words['menu__manage_hooks'] );
3309 $this->registry->output->html .= $this->html->hookDetails( $hookData, $files );
3310 }
3311
3312 /**
3313 * Check if the hook meets all the requirements
3314 *
3315 * @param mixed $hook Can be an hook ID or an array of the hook data
3316 * @return @e array Errors found
3317 */
3318 public function checkHookRequirements( $hook )
3319 {
3320 /* Init vars */
3321 $hookData = array();
3322 $reqsData = array();
3323 $errors = array();
3324
3325 /* We already have some data? */
3326 if ( is_array($hook) && count($hook) )
3327 {
3328 $hookData = $hook;
3329 }
3330 elseif ( is_int($hook) )
3331 {
3332 $hookData = $this->DB->buildAndFetch( array( 'select' => '*', 'from' => 'core_hooks', 'where' => 'hook_id=' . $hook ) );
3333 }
3334
3335 /* Requirements are still serialized? */
3336 if ( IPSLib::isSerialized($hookData['hook_requirements']) )
3337 {
3338 $hookData['hook_requirements'] = unserialize($hookData['hook_requirements']);
3339 }
3340
3341 /* Make our var shorter... */
3342 $reqsData = &$hookData['hook_requirements'];
3343
3344 /* Old data? - @todo: remove this check around 3.3(4?) */
3345 if ( ! isset($reqsData['required_applications']['core']) && isset($reqsData['hook_ipb_version_min']) && ( $reqsData['hook_ipb_version_min'] > 0 || $reqsData['hook_ipb_version_max'] > 0 ) )
3346 {
3347 $reqsData['hook_ipb_version_min'] = ( $reqsData['hook_ipb_version_min'] < 30000 ) ? 30000 : $reqsData['hook_ipb_version_min'];
3348
3349 $reqsData['required_applications']['core'] = array( 'min_version' => intval($reqsData['hook_ipb_version_min']), 'max_version' => intval($reqsData['hook_ipb_version_max']) );
3350 }
3351
3352 //-----------------------------------------
3353 // Let's start checking requirements
3354 //-----------------------------------------
3355
3356 /* PHP */
3357 if( $reqsData['hook_php_version_min'] OR $reqsData['hook_php_version_max'] )
3358 {
3359 if( $reqsData['hook_php_version_min'] AND version_compare( PHP_VERSION, $reqsData['hook_php_version_min'], '<' ) == true )
3360 {
3361 $errors['php_min'] = sprintf( $this->lang->words['h_phpold'],$reqsData['hook_php_version_min'] );
3362 }
3363
3364 if( $reqsData['hook_php_version_max'] AND version_compare( PHP_VERSION, $reqsData['hook_php_version_max'], '>' ) == true )
3365 {
3366 $errors['php_max'] = sprintf( $this->lang->words['h_phpnew'], $reqsData['hook_php_version_max'] );
3367 }
3368 }
3369
3370 /* Additional applications */
3371 if ( is_array($reqsData['required_applications']) && count($reqsData['required_applications']) )
3372 {
3373 /* Get the setup class */
3374 require_once( IPS_ROOT_PATH . 'setup/sources/base/setup.php' );/*noLibHook*/
3375
3376 /* Loop through all apps */
3377 foreach( $reqsData['required_applications'] as $appKey => $appData )
3378 {
3379 /* Versions file doesn't exist? */
3380 if ( !is_file( IPSLib::getAppDir( $appKey ) . '/xml/versions.xml' ) )
3381 {
3382 $errors[ $appKey.'_app' ] = sprintf( $this->lang->words['hook_require_appnotfound'], $appData['app_name'] );
3383 }
3384 /* App not installed/enabled? */
3385 elseif ( !IPSLib::appIsInstalled( $appKey ) )
3386 {
3387 $errors[ $appKey.'_app' ] = sprintf( $this->lang->words['hook_require_appdisabled'], ipsRegistry::$applications[ $appKey ]['app_title'] );
3388 }
3389 elseif ( $appData['min_version'] OR $appData['max_version'] )
3390 {
3391 /* Fetch and check versions */
3392 if ( !isset($this->cachedVersions[ $appKey ]) )
3393 {
3394 $this->cachedVersions[ $appKey ] = IPSSetUp::fetchXmlAppVersions( $appKey );
3395 }
3396
3397 $versions = $this->cachedVersions[ $appKey ];
3398
3399 if ( is_array($versions) && count($versions) )
3400 {
3401 if ( !isset($this->cachedUpgradeInfo[ $appKey ]) )
3402 {
3403 $_key = in_array( $appKey, array( 'forums', 'members' ) ) ? 'core' : $appKey;
3404 $this->cachedUpgradeInfo[ $_key ] = $this->DB->buildAndFetch( array( 'select' => '*', 'from' => 'upgrade_history', 'where' => "upgrade_app='{$_key}'", 'order' => 'upgrade_version_id DESC', 'limit' => array( 1 ) ) );
3405
3406 /* Extra caching for the three core apps */
3407 if( in_array( $appKey, array( 'core', 'forums', 'members' ) ) )
3408 {
3409 $this->cachedUpgradeInfo['core'] = $this->cachedUpgradeInfo[ $_key ];
3410 $this->cachedUpgradeInfo['forums'] = $this->cachedUpgradeInfo[ $_key ];
3411 $this->cachedUpgradeInfo['members'] = $this->cachedUpgradeInfo[ $_key ];
3412 }
3413 }
3414
3415 /* Do we meet tha requirements? */
3416 if ( $appData['min_version'] AND $this->cachedUpgradeInfo[ $appKey ]['upgrade_version_id'] < $appData['min_version'] )
3417 {
3418 $errors[ $appKey.'_min' ] = sprintf( $this->lang->words['hook_require_tooold'], isset($versions[ $appData['min_version'] ]) ? $versions[ $appData['min_version'] ] : $appData['min_version'] );
3419 }
3420
3421 if ( $appData['max_version'] AND $this->cachedUpgradeInfo[ $appKey ]['upgrade_version_id'] > $appData['max_version'] )
3422 {
3423 $errors[ $appKey.'_max' ] = sprintf( $this->lang->words['hook_require_toonew'], isset($versions[ $appData['max_version'] ]) ? $versions[ $appData['max_version'] ] : $appData['max_version'] );
3424 }
3425 }
3426 }
3427 }
3428 }
3429
3430 return $errors;
3431 }
3432
3433 /**
3434 * Check if you meet hook requirements
3435 *
3436 * @return @e void [Outputs to screen]
3437 */
3438 protected function _checkRequirements()
3439 {
3440 $id = intval($this->request['id']);
3441
3442 if( !$id )
3443 {
3444 $this->registry->output->showError( $this->lang->words['h_norequirements'], 11112 );
3445 }
3446
3447 $hookData = $this->DB->buildAndFetch( array( 'select' => '*', 'from' => 'core_hooks', 'where' => 'hook_id=' . $id ) );
3448
3449 if( !$hookData['hook_id'] )
3450 {
3451 $this->registry->output->showError( $this->lang->words['h_norequirements'], 11113 );
3452 }
3453
3454 /* Get requirements */
3455 $hookData['hook_requirements'] = unserialize($hookData['hook_requirements']);
3456
3457 /* Check requirements */
3458 $errors = $this->checkHookRequirements( $hookData );
3459
3460 /* Output */
3461 $this->registry->output->extra_nav[] = array( $this->settings['base_url'] . $this->form_code . 'do=hooks_overview', $this->lang->words['menu__manage_hooks'] );
3462 $this->registry->output->html .= $this->html->hookRequirements( $hookData, $errors, $this->cachedVersions );
3463 }
3464
3465 /**
3466 * Rebuild hooks cache
3467 *
3468 * @return @e void
3469 */
3470 public function rebuildHooksCache()
3471 {
3472 /* INI */
3473 $cache = array( 'commandHooks' => array(), 'skinHooks' => array(), 'templateHooks' => array(), 'dataHooks' => array(), 'libraryHooks' => array() );
3474
3475 /* First fix positions */
3476 $this->_fixPositions();
3477
3478 /* Get current hooks */
3479 $this->DB->build( array( 'select' => 'f.*',
3480 'from' => array( 'core_hooks_files' => 'f' ),
3481 'where' => 'c.hook_enabled=1',
3482 'order' => 'c.hook_position ASC',
3483 'add_join' => array( array( 'select' => 'c.hook_id',
3484 'from' => array( 'core_hooks' => 'c' ),
3485 'where' => 'c.hook_id=f.hook_hook_id',
3486 'type' => 'left' ) )
3487 ) );
3488 $this->DB->execute();
3489
3490 while( $r = $this->DB->fetch() )
3491 {
3492 /* Got no main hook id? Weird but skip.. */
3493 if ( empty($r['hook_id']) )
3494 {
3495 continue;
3496 }
3497
3498 /* INIT */
3499 $_cache = array();
3500 $data = unserialize( $r['hook_data'] );
3501
3502 /* General Stuff */
3503 $_cache = array( 'filename' => $r['hook_file_stored'],
3504 'className' => $r['hook_classname']
3505 );
3506
3507 /* Hook type specific stuff */
3508 if( $r['hook_type'] == 'templateHooks' )
3509 {
3510 $_cache['type'] = $data['type'];
3511 $_cache['skinFunction'] = $data['skinFunction'];
3512 $_cache['id'] = $data['id'];
3513 $_cache['position'] = $data['position'];
3514 }
3515 else if( /*$r['hook_type'] == 'dataHooks' OR*/ $r['hook_type'] == 'libraryHooks' )
3516 {
3517 $_cache['classToOverload'] = $data['classToOverload'];
3518 }
3519
3520 /* Add to array */
3521 if( $r['hook_type'] == 'templateHooks' )
3522 {
3523 $cache[ $r['hook_type'] ][ $data['skinGroup'] ][] = $_cache;
3524 }
3525 else if( $r['hook_type'] == 'dataHooks' )
3526 {
3527 $cache[ $r['hook_type'] ][ $data['dataLocation'] ][] = $_cache;
3528 }
3529 else if( $r['hook_type'] == 'libraryHooks' )
3530 {
3531 $cache[ $r['hook_type'] ][ $data['libApplication'] ][ $data['classToOverload'] ][] = $_cache;
3532 }
3533 else
3534 {
3535 $cache[ $r['hook_type'] ][ $data['classToOverload'] ][] = $_cache;
3536 }
3537 }
3538
3539 /* Update the cache */
3540 $this->cache->setCache( 'hooks', $cache, array( 'array' => 1 ) );
3541 }
3542}