Hawk - PHP documentation
  • Namespace
  • Class
  • Tree

Namespaces

  • Hawk
    • View
      • Plugins

Classes

  • Hawk\App
  • Hawk\ButtonInput
  • Hawk\Cache
  • Hawk\CheckboxInput
  • Hawk\ColorInput
  • Hawk\Conf
  • Hawk\Controller
  • Hawk\Crypto
  • Hawk\DatabaseSessionHandler
  • Hawk\DatetimeInput
  • Hawk\DB
  • Hawk\DBExample
  • Hawk\DeleteInput
  • Hawk\Dialogbox
  • Hawk\EmailInput
  • Hawk\ErrorHandler
  • Hawk\Event
  • Hawk\FileInput
  • Hawk\FileSystem
  • Hawk\FloatInput
  • Hawk\Form
  • Hawk\FormFieldset
  • Hawk\FormInput
  • Hawk\GenericModel
  • Hawk\GifImage
  • Hawk\HawkApi
  • Hawk\HawkUpdater
  • Hawk\HiddenInput
  • Hawk\HtmlInput
  • Hawk\HTTPRequest
  • Hawk\Icon
  • Hawk\Image
  • Hawk\IntegerInput
  • Hawk\ItemList
  • Hawk\ItemListField
  • Hawk\JpegImage
  • Hawk\Lang
  • Hawk\Language
  • Hawk\LeftSidebarTab
  • Hawk\Less
  • Hawk\Logger
  • Hawk\Mail
  • Hawk\MenuItem
  • Hawk\Model
  • Hawk\NoSidebarTab
  • Hawk\NumberInput
  • Hawk\ObjectInput
  • Hawk\Option
  • Hawk\Panel
  • Hawk\PasswordInput
  • Hawk\Permission
  • Hawk\Plugin
  • Hawk\PluginInstaller
  • Hawk\PngImage
  • Hawk\ProfileQuestion
  • Hawk\ProfileQuestionValue
  • Hawk\RadioInput
  • Hawk\Request
  • Hawk\Response
  • Hawk\RightSidebarTab
  • Hawk\Role
  • Hawk\RolePermission
  • Hawk\Route
  • Hawk\Router
  • Hawk\SelectInput
  • Hawk\Session
  • Hawk\Singleton
  • Hawk\SubmitInput
  • Hawk\Tabs
  • Hawk\TextareaInput
  • Hawk\TextInput
  • Hawk\Theme
  • Hawk\TimeInput
  • Hawk\Upload
  • Hawk\User
  • Hawk\View
  • Hawk\View\Plugins\Accordion
  • Hawk\View\Plugins\Button
  • Hawk\View\Plugins\Form
  • Hawk\View\Plugins\Icon
  • Hawk\View\Plugins\Import
  • Hawk\View\Plugins\Panel
  • Hawk\View\Plugins\Tabs
  • Hawk\View\Plugins\Text
  • Hawk\View\Plugins\Uri
  • Hawk\View\Plugins\Widget
  • Hawk\ViewPlugin
  • Hawk\Widget
  • Hawk\WysiwygInput

Traits

  • Hawk\Utils

Exceptions

  • Hawk\AppStopException
  • Hawk\DBExampleException
  • Hawk\DBException
  • Hawk\FileSystemException
  • Hawk\HawkApiException
  • Hawk\ImageException
  • Hawk\MailException
  • Hawk\UploadException
  • Hawk\ViewException
  1 <?php
  2 /**
  3  * Plugin.php
  4  *
  5  * @author  Elvyrra SAS
  6  * @license http://rem.mit-license.org/ MIT
  7  */
  8 
  9 namespace Hawk;
 10 
 11 /**
 12  * This class describes the behavior of the application plugins
 13  *
 14  * @package Core\Plugin
 15  */
 16 class Plugin{
 17     /**
 18      * The table in the database where the plugins data are registered
 19      */
 20     const TABLE = 'Plugin';
 21 
 22     /**
 23      * The basename of the file containing the plugin definition
 24      */
 25     const MANIFEST_BASENAME = 'manifest.json';
 26 
 27     /**
 28      * The pattern for a plugin name
 29      */
 30     const NAME_PATTERN = '[a-zA-Z0-9\-_.]+';
 31 
 32     /**
 33      * The plugin name
 34      *
 35      * @var strign
 36      */
 37     private $name,
 38 
 39     /**
 40      * The plugin definition, described in the file manifest.json, at the root directory of the plugin
 41      *
 42      * @var array
 43      */
 44     $definition = array(),
 45 
 46 
 47     /**
 48      * The plugin optionsn defined in the table Options
 49      *
 50      * @var array
 51      */
 52     $options = array();
 53 
 54     /**
 55      * The root directory of the plugin
 56      *
 57      * @var string
 58      */
 59     private $rootDir,
 60 
 61     /**
 62      * Defines if the plugin can be removed or uninstalled
 63      *
 64      * @var boolean
 65      */
 66     $removable = true,
 67 
 68     /**
 69      * Defines the active/inactive state of the plugin
 70      *
 71      * @var boolean
 72      */
 73     $active;
 74 
 75 
 76     /**
 77      * The application main plugins, not removable or editable, used for the application core
 78      *
 79      * @var array
 80      */
 81     public static $mainPlugins = array('main', 'install', 'admin');
 82 
 83 
 84     /**
 85      * The plugin instances
 86      *
 87      * @var array
 88      */
 89     private static $instances = array();
 90 
 91 
 92     /**
 93      * Forbidden plugin names
 94      *
 95      * @var array
 96      */
 97     public static $forbiddenNames = array('custom');
 98 
 99 
100     /**
101      * Cache array containing the plugins instances references for files.
102      * This is used to increase performances when calling Plugin::current() or Plugin::getFilePlugin($file)
103      *
104      * @var array
105      */
106     private static $filePlugins = array();
107 
108 
109     /**
110      * Constructor
111      *
112      * @param string $name The plugin name, corresponding to the directory name
113      */
114     private function __construct($name){
115         $this->name = $name;
116         $this->rootDir = ($this->isMainPlugin() ? MAIN_PLUGINS_DIR : PLUGINS_DIR) . $this->name . '/';
117 
118         if(!is_dir($this->rootDir)) {
119             throw new \Exception('The plugin does not exists');
120         }
121 
122         if(!$this->isMainPlugin()) {
123             if(!is_file($this->rootDir . self::MANIFEST_BASENAME)) {
124                 throw new \Exception('The plugin must have a file manifest.json');
125             }
126             $this->definition = json_decode(file_get_contents($this->rootDir . self::MANIFEST_BASENAME), true);
127         }
128         else{
129             $this->active = true;
130             $this->removable = false;
131             $this->definition = array(
132             'title' => Lang::get($this->name . '.plugin-name'),
133             );
134         }
135     }
136 
137 
138     /**
139      * Get a plugin instance from it name
140      *
141      * @param string $name The plugin name to instance
142      *
143      * @return Plugin The instance of the wanted plugin
144      */
145     public static function get($name){
146         try{
147             if(!isset(self::$instances[$name])) {
148                 self::$instances[$name] = new self($name);
149             }
150 
151             return self::$instances[$name];
152         }
153         catch(\Exception $e){
154             return null;
155         }
156     }
157 
158 
159     /**
160      * Get the plugin containing the file where this function is called
161      *
162      * @return Plugin The current plugin
163      */
164     public static function current(){
165         $callingFile = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1)[0]['file'];
166 
167         return self::getFilePlugin($callingFile);
168     }
169 
170 
171     /**
172      * Get the plugin contaning a given filename
173      *
174      * @param string $file The filename to get the containing plugin of
175      *
176      * @return Plugin The found plugin
177      */
178     public static function getFilePlugin($file){
179         // Search plugin in cache
180         if(isset(self::$filePlugins[$file])) {
181             return self::$filePlugins[$file];
182         }
183 
184         if(strpos($file, PLUGINS_DIR) !== false) {
185             $dir = str_replace(PLUGINS_DIR, '', $file);
186         }
187         elseif(strpos($file, MAIN_PLUGINS_DIR) !== false) {
188             $dir = str_replace(MAIN_PLUGINS_DIR, '', $file);
189         }
190         else{
191             return null;
192         }
193         list($name) = explode(DIRECTORY_SEPARATOR, $dir);
194 
195         // instanciate the plugin
196         $plugin = self::get($name);
197 
198         // Register the pluygin in the memory cache
199         self::$filePlugins[$file] = &$plugin;
200 
201         // return the plugin instance
202         return $plugin;
203     }
204 
205 
206     /**
207       * Get all the plugins
208      *
209       * @param bool $includeMain If true, include main plugins to the returned list
210       * @param bool $loadConf    If set to true, load the plugins conf in the database
211       *
212       * @return array The list of plugin instances
213       */
214     public static function getAll($includeMain = true, $loadConf = false){
215         $plugins = array();
216         $dirs = $includeMain ? array(MAIN_PLUGINS_DIR, PLUGINS_DIR) : array(PLUGINS_DIR);
217 
218         if($loadConf && App::conf()->has('db')) {
219             $configs = App::db()->select(
220                 array(
221                 'from' => DB::getFullTablename(self::TABLE),
222                 'index' => 'name',
223                 'return' => DB::RETURN_OBJECT
224                 )
225             );
226         }
227         else{
228             $configs = array();
229         }
230 
231         foreach($dirs as $dir){
232             foreach(glob($dir . '*', GLOB_ONLYDIR) as $dir){
233                 $name = basename($dir);
234                 $config = isset($configs[$name]) ? $configs[$name] : null;
235 
236                 $plugin = self::get($name);
237                 if(!$plugin->isMainPlugin()) {
238                     $plugin->active = isset($config->active) ? $config->active : false;
239                 }
240                 $plugins[$name] = $plugin;
241             }
242         }
243 
244         return $plugins;
245     }
246 
247 
248     /**
249      * Get all the active plugins
250      *
251      * @param bool $includeMain If set to true, include main plugins in the returned array
252      *
253      * @return array The list of plugin instances
254      */
255     public static function getActivePlugins($includeMain = true){
256         $plugins = self::getAll($includeMain, true);
257 
258         return array_filter(
259             $plugins, function ($plugin) {
260                 return $plugin->active;
261             }
262         );
263     }
264 
265 
266     /**
267      * Get the main plugins
268      *
269      * @return array The list of plugin instances
270      */
271     public static function getMainPlugins(){
272         return array_map(
273             function ($name) {
274                 return new self($name);
275             }, self::$mainPlugins
276         );
277     }
278 
279 
280 
281     /**
282      * Get the plugin name
283      *
284      * @return string The plugin name
285      */
286     public function getName(){
287         return $this->name;
288     }
289 
290 
291     /**
292      * Check if the plugin is a main plugin
293      *
294      * @return boolean True if the plugin is a main plugin (main, install or admin), else False
295      */
296     public function isMainPlugin(){
297         return in_array($this->name, self::$mainPlugins);
298     }
299 
300     /**
301      * Get the plugin options
302      *
303      * @return array The plugin options, where keys are the options names, and values, the values for each option
304      */
305     public function getOptions(){
306         if(!isset($this->options)) {
307             $this->options = Option::getPluginOptions($this->name);
308         }
309 
310         return $this->options;
311     }
312 
313 
314     /**
315      * Get the plugin data from the manifest
316      *
317      * @param string $prop If set, the method will return the value of the definition property $prop.
318      *                      If not set, it will return the whole definition array
319      *
320      * @return mixed The plugin definition property if $prop is set,
321      *                   or the whole plugin definition if $prop is not set
322      */
323     public function getDefinition($prop = null){
324         if($prop) {
325             return isset($this->definition[$prop]) ? $this->definition[$prop]: null;
326         }
327         return $this->definition;
328     }
329 
330 
331     /**
332      * Return the root directory of the plugin
333      *
334      * @return string the root directory of the plugin
335      */
336     public function getRootDir(){
337         return $this->rootDir;
338     }
339 
340 
341     /**
342      * Returns the start file of the plugin.
343      * The start file is the file start.php, at the root of the plugin directory,
344      * that defines the routes, widgets, and event listenter of the plugin.
345      *
346      * @return string The file path of the plugin start file
347      */
348     public function getStartFile(){
349         return $this->getRootDir() . 'start.php';
350     }
351 
352 
353     /**
354      * Returns the directory of the plugin containing the controllers
355      *
356      * @return string The directory containing controllers classes of the plugin
357      */
358     public function getControllersDir(){
359         return $this->getRootDir() . 'controllers/';
360     }
361 
362 
363     /**
364      * Return the directory containing the plugin language files
365      *
366      * @return string The directory contaning the plugin language files
367      */
368     public function getLangDir(){
369         return $this->getRootDir() . 'lang/';
370     }
371 
372     /**
373      * Return the directory containing the plugin models
374      *
375      * @return string The directory containing models classes of the plugin
376      */
377     public function getModelsDir(){
378         return $this->getRootDir() . 'models/';
379     }
380 
381 
382     /**
383      * Return the directory containing the plugin widgets
384      *
385      * @return string The directory containing the widgets classes of the plugin
386      */
387     public function getWidgetsDir(){
388         return $this->getRootDir() . 'widgets/';
389     }
390 
391 
392     /**
393      * Return the directory containing the plugin views
394      *
395      * @return string The directory contaning the plugin views
396      */
397     public function getViewsDir(){
398         return $this->getRootDir() . 'views/';
399     }
400 
401 
402     /**
403      * Return the full path of a view in the plugin
404      *
405      * @param string $view The basename of the view file to get in the plugin
406      *
407      * @return string The full path of the view file
408      */
409     public function getView($view){
410         // Check if the view is overriden in the current theme
411         $file= Theme::getSelected()->getView('plugins/' . $this->name . '/' . $view);
412         if(is_file($file)) {
413             // The view is overriden in the theme
414             return $file;
415         }
416 
417         // The view is not overriden in the view
418         return $this->getViewsDir() . $view;
419     }
420 
421 
422     /**
423      * Return the directory containing the plugin static files (js, css, images)
424      *
425      * @return string The directory path
426      */
427     public function getStaticDir(){
428         return $this->getRootDir() . 'static/';
429     }
430 
431     /**
432      * Return the directory containing the plugin public static files (acessible by HTTP requests)
433      *
434      * @return string The directory path
435      */
436     public function getPublicStaticDir(){
437         return STATIC_PLUGINS_DIR . $this->name . '/';
438     }
439 
440 
441     /**
442      * Return the url of a static file in the plugin, or the directory containing static files if $basename is empty
443      *
444      * @param string $basename The file to get the URL of
445      *
446      * @return string The URL
447      */
448     public function getStaticUrl($basename = ''){
449         $baseUrl = PLUGINS_ROOT_URL . $this->name . '/';
450         if(empty($basename)) {
451             return $baseUrl;
452         }
453         else{
454             $privateFilename = $this->getStaticDir() . $basename;
455             $publicFilename = $this->getPublicStaticDir() . $basename;
456 
457             if(is_file($privateFilename) && (!is_file($publicFilename) || filemtime($privateFilename) > filemtime($publicFilename))) {
458                 if(!is_dir(dirname($publicFilename))) {
459                     mkdir(dirname($publicFilename), 0755, true);
460                 }
461 
462                 copy($privateFilename, $publicFilename);
463             }
464 
465             return $baseUrl . $basename . '?' . filemtime($publicFilename);
466         }
467     }
468 
469 
470 
471 
472 
473     /**
474      * Return the directory containing the plugin JavaScript files
475      *
476      * @return string The directory path
477      */
478     public function getJsDir(){
479         return $this->getStaticDir() . 'js/';
480     }
481 
482 
483     /**
484      * Return the directory containing the plugin public JavaScript files (accessible by HTTP requests)
485      *
486      * @return string The directory path
487      */
488     public function getPublicJsDir(){
489         return $this->getPublicStaticDir() . 'js/';
490     }
491 
492     /**
493      * Return the URL of a public javascript file,
494      * or the URL of the directory containing public javascript files if $basename is empty
495      *
496      * @param string $basename The Javascript file basename
497      *
498      * @return string The URL
499      */
500     public function getJsUrl($basename = ''){
501         if(empty($basename)) {
502             return $this->getStaticUrl() . 'js/';
503         }
504         else{
505             return $this->getStaticUrl('js/' . $basename);
506         }
507     }
508 
509 
510 
511 
512 
513 
514     /**
515      * Return the directory containing the plugin CSS files
516      *
517      * @return string The directory contaning the plugin CSS files
518      */
519     public function getLessDir(){
520         return $this->getStaticDir() . 'less/';
521     }
522 
523 
524     /**
525      * Return the directory containing the plugin public CSS files (accessible by HTTP requests)
526      *
527      * @return string The directory path
528      */
529     public function getPublicCssDir(){
530         return $this->getPublicStaticDir() . 'css/';
531     }
532 
533 
534     /**
535      * Return the URL of a public CSS file,
536      * or the URL of the directory containing public CSS files if $basename is empty
537      *
538      * @param string $basename The Less file basename
539      *
540      * @return string The URL
541      */
542     public function getCssUrl($basename = ""){
543         $cssUrl = $this->getStaticUrl() . 'css/';
544         if(empty($basename)) {
545             return $cssUrl;
546         }
547         else{
548             $privateFilename = $this->getLessDir() . $basename;
549             $cssBasename = preg_replace('/\.less$/', '.css', $basename);
550             $publicFilename = $this->getPublicCssDir() . $cssBasename;
551 
552             if(is_file($privateFilename)) {
553 
554                 Event::on(
555                     'built-less', function (Event $event) use ($privateFilename) {
556                         if($event->getData('source') === $privateFilename) {
557                             // Copy all static files except less and JS
558                             foreach(glob($this->getStaticDir() . '*') as $elt){
559                                 if(! in_array(basename($elt), array('less', 'js'))) {
560                                     App::fs()->copy($elt, $this->getPublicStaticDir());
561                                 }
562                             }
563                         }
564                     }
565                 );
566 
567                 Less::compile($privateFilename, $publicFilename);
568             }
569 
570             return $cssUrl . $cssBasename . '?' . filemtime($publicFilename);
571         }
572     }
573 
574 
575 
576 
577     /**
578      * Return the directory containing the plugin files due to user actions
579      *
580      * @return string The directory contaning the user files of the plugin
581      */
582     public function getUserfilesDir(){
583         return USERFILES_PLUGINS_DIR . $this->name . '/';
584     }
585 
586 
587     /**
588      * Return the directory containing the public (accessible by HTTP requests) plugin files due to user actions
589      *
590      * @return string The directory contaning the user files of the plugin
591      */
592     public function getPublicUserfilesDir(){
593         return $this->getPublicStaticDir() . 'userfiles/';
594     }
595 
596 
597     /**
598      * Return the URL of a static userfile,
599      * or the URL of the directory contaning the userfiles, if $basename is empty
600      *
601      * @param string $basename The basename of the file to get the access URL
602      *
603      * @return string The URL
604      */
605     public function getUserfilesUrl($basename = ''){
606         $baseUrl = $this->getStaticUrl() . 'userfiles/';
607         if(empty($basename)) {
608             return $baseUrl;
609         }
610         else{
611             return $baseUrl . $basename . '?' . filemtime($this->getPublicUserfilesDir() . $basename);
612         }
613     }
614 
615 
616     /**
617      * Check if the plugin is installed. The plugin is installed if it appears in the database
618      *
619      * @return boolean True if the plugin is installed, False else
620      */
621     public function isInstalled(){
622         return (bool) App::db()->count(DB::getFullTablename(self::TABLE), 'name = :name', array('name' => $this->name));
623     }
624 
625 
626     /**
627      * Get a plugin namespace by it name
628      *
629      * @param string $name The plugin name
630      */
631     public static function getNamespaceByName($name){
632         $namespace = preg_replace_callback(
633             '/(^|\W|_)(\w?)/', function ($m) {
634                 return strtoupper($m[2]);
635             }, $name
636         );
637 
638         return 'Hawk\\Plugins\\' . $namespace;
639     }
640 
641 
642     /**
643      * Get the namespace used for all files in the plugin. The namespace is generated from the plugin name
644      *
645      * @return string The plugin namespace
646      */
647     public function getNamespace(){
648         return self::getNamespaceByName($this->name);
649     }
650 
651     /**
652      * Instance the plugin installer
653      *
654      * @return PluginInstaller The instance of the plugin installer
655      */
656     public function getInstallerInstance(){
657         if(isset($this->manager)) {
658             return $this->manager;
659         }
660 
661         $class = '\\' . $this->getNamespace() . '\\Installer';
662         if(!empty($class)) {
663             $this->manager = new $class($this);
664             return $this->manager;
665         }
666         else{
667             return null;
668         }
669     }
670 
671     /**
672      * Install the plugin
673      */
674     public function install(){
675         App::db()->insert(
676             DB::getFullTablename(self::TABLE), array(
677             'name' => $this->name,
678             'active' => 0
679             ), 'IGNORE'
680         );
681 
682         try{
683             $this->getInstallerInstance()->install();
684             App::logger()->notice('The plugin ' . $this->name . ' has been installed');
685         }
686         catch(\Exception $e){
687             App::db()->delete(DB::getFullTablename(self::TABLE), new DBExample(array('name' => $this->name)));
688 
689             App::logger()->error('En error occured while installing plugin ' . $this->name . ' : ' . $e->getMessage());
690             throw $e;
691         }
692     }
693 
694 
695     /**
696      * Uninstall the plugin
697      */
698     public function uninstall(){
699         App::db()->delete(DB::getFullTablename(self::TABLE), new DBExample(array('name' => $this->name)));
700 
701         try{
702             $this->getInstallerInstance()->uninstall();
703             App::logger()->notice('The plugin ' . $this->name . ' has been uninstalled');
704         }
705         catch(\Exception $e){
706             App::db()->insert(
707                 DB::getFullTablename(self::TABLE), array(
708                 'name' => $this->name,
709                 'active' => 0
710                 ), 'IGNORE'
711             );
712 
713             App::logger()->error('En error occured while uninstalling plugin ' . $this->name . ' : ' . $e->getMessage());
714             throw $e;
715         }
716     }
717 
718     /**
719      * Check if the plugin is active
720      *
721      * @return boolean True if the plugin is active, False else
722      */
723     public function isActive(){
724         return $this->active;
725     }
726 
727 
728     /**
729      * Activate the plugin in the database
730      */
731     public function activate(){
732         // Activate the plugin
733         $this->active = 1;
734         App::db()->update(DB::getFullTablename(self::TABLE), new DBExample(array('name' => $this->name)), array('active' => 1));
735 
736         try{
737             $this->getInstallerInstance()->activate();
738             App::logger()->notice('The plugin ' . $this->name . ' has been activated');
739         }
740         catch(\Exception $e){
741             App::db()->update(DB::getFullTablename(self::TABLE), new DBExample(array('name' => $this->name)), array('active' => 0));
742 
743             App::logger()->error('En error occured while activating plugin ' . $this->name . ' : ' . $e->getMessage());
744             throw $e;
745         }
746     }
747 
748 
749     /**
750      * Deactive the plugin
751      */
752     public function deactivate(){
753         // Deactivate the plugin
754         $this->active = 0;
755         App::db()->update(DB::getFullTablename(self::TABLE), new DBExample(array('name' => $this->name)), array('active' => 0));
756 
757         try{
758             $this->getInstallerInstance()->deactivate();
759             App::logger()->notice('The plugin ' . $this->name . ' has been deactivated');
760         }
761         catch(\Exception $e){
762             App::db()->update(DB::getFullTablename(self::TABLE), new DBExample(array('name' => $this->name)), array('active' => 1));
763 
764             App::logger()->error('En error occured while deactivating plugin ' . $this->name . ' : ' . $e->getMessage());
765             throw $e;
766         }
767     }
768 
769 
770     /**
771      * Update the plugin to a given version
772      *
773      * @param string $version The version to update the plugin
774      */
775     public function update($version){
776         $updater = $this->getInstallerInstance();
777 
778         $method = 'v' . str_replace('.', '_', $version);
779         if(method_exists($updater, $method)) {
780             $updater->$method();
781         }
782     }
783 
784     /**
785      * Compelete deletion of plugin
786      */
787     public function delete(){
788         if($this->removable) {
789             $directory = $this->getRootDir();
790 
791             App::fs()->remove($directory);
792         }
793     }
794 }
795 
Hawk - PHP documentation API documentation generated by ApiGen