plugin.class.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677
  1. <?php
  2. /* For licensing terms, see /license.txt */
  3. /**
  4. * Class Plugin
  5. * Base class for plugins
  6. *
  7. * This class has to be extended by every plugin. It defines basic methods
  8. * to install/uninstall and get information about a plugin
  9. *
  10. * @author Julio Montoya <gugli100@gmail.com>
  11. * @author Yannick Warnier <ywarnier@beeznest.org>
  12. * @author Laurent Opprecht <laurent@opprecht.info>
  13. * @copyright 2012 University of Geneva
  14. * @license GNU General Public License - http://www.gnu.org/copyleft/gpl.html
  15. *
  16. */
  17. class Plugin
  18. {
  19. protected $version = '';
  20. protected $author = '';
  21. protected $fields = array();
  22. private $settings = null;
  23. // Translation strings.
  24. private $strings = null;
  25. public $isCoursePlugin = false;
  26. public $isMailPlugin = false;
  27. /**
  28. * When creating a new course, these settings are added to the course, in
  29. * the course_info/infocours.php
  30. * To show the plugin course icons you need to add these icons:
  31. * main/img/icons/22/plugin_name.png
  32. * main/img/icons/64/plugin_name.png
  33. * main/img/icons/64/plugin_name_na.png
  34. * @example
  35. * $course_settings = array(
  36. array('name' => 'big_blue_button_welcome_message', 'type' => 'text'),
  37. array('name' => 'big_blue_button_record_and_store', 'type' => 'checkbox')
  38. );
  39. */
  40. public $course_settings = array();
  41. /**
  42. * This indicates whether changing the setting should execute the callback
  43. * function.
  44. */
  45. public $course_settings_callback = false;
  46. /**
  47. * Default constructor for the plugin class. By default, it only sets
  48. * a few attributes of the object
  49. * @param string $version of this plugin
  50. * @param string $author of this plugin
  51. * @param array $settings settings to be proposed to configure the plugin
  52. */
  53. protected function __construct($version, $author, $settings = array())
  54. {
  55. $this->version = $version;
  56. $this->author = $author;
  57. $this->fields = $settings;
  58. global $language_files;
  59. $language_files[] = 'plugin_' . $this->get_name();
  60. }
  61. /**
  62. * Gets an array of information about this plugin (name, version, ...)
  63. * @return array Array of information elements about this plugin
  64. */
  65. public function get_info()
  66. {
  67. $result = array();
  68. $result['title'] = $this->get_title();
  69. $result['comment'] = $this->get_comment();
  70. $result['version'] = $this->get_version();
  71. $result['author'] = $this->get_author();
  72. $result['plugin_class'] = get_class($this);
  73. $result['is_course_plugin'] = $this->isCoursePlugin;
  74. $result['is_mail_plugin'] = $this->isMailPlugin;
  75. if ($form = $this->get_settings_form()) {
  76. $result['settings_form'] = $form;
  77. foreach ($this->fields as $name => $type) {
  78. $value = $this->get($name);
  79. if (is_array($type)) {
  80. $value = $type['options'];
  81. }
  82. $result[$name] = $value;
  83. }
  84. }
  85. return $result;
  86. }
  87. /**
  88. * Returns the "system" name of the plugin in lowercase letters
  89. * @return string
  90. */
  91. public function get_name()
  92. {
  93. $result = get_class($this);
  94. $result = str_replace('Plugin', '', $result);
  95. $result = strtolower($result);
  96. return $result;
  97. }
  98. /**
  99. * Returns the title of the plugin
  100. * @return string
  101. */
  102. public function get_title()
  103. {
  104. return $this->get_lang('plugin_title');
  105. }
  106. /**
  107. * Returns the description of the plugin
  108. * @return string
  109. */
  110. public function get_comment()
  111. {
  112. return $this->get_lang('plugin_comment');
  113. }
  114. /**
  115. * Returns the version of the plugin
  116. * @return string
  117. */
  118. public function get_version()
  119. {
  120. return $this->version;
  121. }
  122. /**
  123. * Returns the author of the plugin
  124. * @return string
  125. */
  126. public function get_author()
  127. {
  128. return $this->author;
  129. }
  130. /**
  131. * Returns the contents of the CSS defined by the plugin
  132. * @return array
  133. */
  134. public function get_css()
  135. {
  136. $name = $this->get_name();
  137. $path = api_get_path(SYS_PLUGIN_PATH)."$name/resources/$name.css";
  138. if (!is_readable($path)) {
  139. return '';
  140. }
  141. $css = array();
  142. $css[] = file_get_contents($path);
  143. $result = implode($css);
  144. return $result;
  145. }
  146. /**
  147. * Returns an HTML form (generated by FormValidator) of the plugin settings
  148. * @return string FormValidator-generated form
  149. */
  150. public function get_settings_form()
  151. {
  152. $result = new FormValidator($this->get_name());
  153. $defaults = array();
  154. $checkboxGroup = array();
  155. $checkboxCollection = array();
  156. if ($checkboxNames = array_keys($this->fields, 'checkbox')) {
  157. $pluginInfoCollection = api_get_settings('Plugins');
  158. foreach ($pluginInfoCollection as $pluginInfo) {
  159. if (array_search($pluginInfo['title'], $checkboxNames) !== false) {
  160. $checkboxCollection[$pluginInfo['title']] = $pluginInfo;
  161. }
  162. }
  163. }
  164. foreach ($this->fields as $name => $type) {
  165. $value = $this->get($name);
  166. $defaults[$name] = $value;
  167. $type = isset($type) ? $type : 'text';
  168. $help = null;
  169. if ($this->get_lang_plugin_exists($name.'_help')) {
  170. $help = $this->get_lang($name.'_help');
  171. if ($name === "show_main_menu_tab") {
  172. $pluginName = strtolower(str_replace('Plugin', '', get_class($this)));
  173. $pluginUrl = api_get_path(WEB_PATH)."plugin/$pluginName/index.php";
  174. $pluginUrl = "<a href=$pluginUrl>$pluginUrl</a>";
  175. $help = sprintf($help, $pluginUrl);
  176. }
  177. }
  178. switch ($type) {
  179. case 'html':
  180. $result->addElement('html', $this->get_lang($name));
  181. break;
  182. case 'wysiwyg':
  183. $result->add_html_editor($name, $this->get_lang($name));
  184. break;
  185. case 'text':
  186. $result->addElement($type, $name, array($this->get_lang($name), $help));
  187. break;
  188. case 'boolean':
  189. $group = array();
  190. $group[] = $result->createElement('radio', $name, '', get_lang('Yes'), 'true');
  191. $group[] = $result->createElement('radio', $name, '', get_lang('No'), 'false');
  192. $result->addGroup($group, null, array($this->get_lang($name), $help));
  193. break;
  194. case 'checkbox':
  195. $selectedValue = null;
  196. if (isset($checkboxCollection[$name])) {
  197. if ($checkboxCollection[$name]['selected_value'] === 'true') {
  198. $selectedValue = 'checked';
  199. }
  200. }
  201. $element = $result->createElement(
  202. $type,
  203. $name,
  204. '',
  205. $this->get_lang($name),
  206. $selectedValue
  207. );
  208. $element->_attributes['value'] = 'true';
  209. $checkboxGroup[] = $element;
  210. break;
  211. }
  212. }
  213. if (!empty($checkboxGroup)) {
  214. $result->addGroup($checkboxGroup, null, array($this->get_lang('sms_types'), $help));
  215. }
  216. $result->setDefaults($defaults);
  217. $result->addElement('style_submit_button', 'submit_button', $this->get_lang('Save'));
  218. return $result;
  219. }
  220. /**
  221. * Returns the value of a given plugin global setting
  222. * @param string $name of the plugin
  223. *
  224. * @return string Value of the plugin
  225. */
  226. public function get($name)
  227. {
  228. $settings = $this->get_settings();
  229. foreach ($settings as $setting) {
  230. if ($setting['variable'] == ($this->get_name() . '_' . $name)) {
  231. return $setting['selected_value'];
  232. }
  233. }
  234. return false;
  235. }
  236. /**
  237. * Returns an array with the global settings for this plugin
  238. * @return array Plugin settings as an array
  239. */
  240. public function get_settings()
  241. {
  242. if (is_null($this->settings)) {
  243. $settings = api_get_settings_params(
  244. array(
  245. "subkey = ? AND category = ? AND type = ? " => array($this->get_name(), 'Plugins', 'setting')
  246. )
  247. );
  248. $this->settings = $settings;
  249. }
  250. return $this->settings;
  251. }
  252. /**
  253. * Tells whether language variables are defined for this plugin or not
  254. * @param string $name System name of the plugin
  255. *
  256. * @return bool True if the plugin has language variables defined, false otherwise
  257. */
  258. public function get_lang_plugin_exists($name)
  259. {
  260. return isset($this->strings[$name]);
  261. }
  262. /**
  263. * Hook for the get_lang() function to check for plugin-defined language terms
  264. * @param string $name of the language variable we are looking for
  265. *
  266. * @return string The translated language term of the plugin
  267. */
  268. public function get_lang($name)
  269. {
  270. // Check whether the language strings for the plugin have already been
  271. // loaded. If so, no need to load them again.
  272. if (is_null($this->strings)) {
  273. global $language_interface;
  274. $root = api_get_path(SYS_PLUGIN_PATH);
  275. $plugin_name = $this->get_name();
  276. //1. Loading english if exists
  277. $english_path = $root.$plugin_name."/lang/english.php";
  278. if (is_readable($english_path)) {
  279. include $english_path;
  280. $this->strings = $strings;
  281. }
  282. $path = $root.$plugin_name."/lang/$language_interface.php";
  283. //2. Loading the system language
  284. if (is_readable($path)) {
  285. include $path;
  286. if (!empty($strings)) {
  287. foreach ($strings as $key => $string) {
  288. $this->strings[$key] = $string;
  289. }
  290. }
  291. }
  292. }
  293. if (isset($this->strings[$name])) {
  294. return $this->strings[$name];
  295. }
  296. return get_lang($name);
  297. }
  298. /**
  299. * Caller for the install_course_fields() function
  300. * @param int $courseId
  301. * @param boolean $addToolLink Whether to add a tool link on the course homepage
  302. *
  303. * @return void
  304. */
  305. public function course_install($courseId, $addToolLink = true)
  306. {
  307. $this->install_course_fields($courseId, $addToolLink);
  308. }
  309. /**
  310. * Add course settings and, if not asked otherwise, add a tool link on the course homepage
  311. * @param int $courseId Course integer ID
  312. * @param boolean $add_tool_link Whether to add a tool link or not
  313. * (some tools might just offer a configuration section and act on the backend)
  314. * @return boolean False on error, null otherwise
  315. */
  316. public function install_course_fields($courseId, $add_tool_link = true)
  317. {
  318. $plugin_name = $this->get_name();
  319. $t_course = Database::get_course_table(TABLE_COURSE_SETTING);
  320. $courseId = intval($courseId);
  321. if (empty($courseId)) {
  322. return false;
  323. }
  324. // Adding course settings.
  325. if (!empty($this->course_settings)) {
  326. foreach ($this->course_settings as $setting) {
  327. $variable = Database::escape_string($setting['name']);
  328. $value ='';
  329. if (isset($setting['init_value'])) {
  330. $value = Database::escape_string($setting['init_value']);
  331. }
  332. $type = 'textfield';
  333. if (isset($setting['type'])) {
  334. $type = Database::escape_string($setting['type']);
  335. }
  336. if (isset($setting['group'])) {
  337. $group = Database::escape_string($setting['group']);
  338. $sql = "SELECT value FROM $t_course
  339. WHERE c_id = $courseId AND variable = '$group' AND subkey = '$variable' ";
  340. $result = Database::query($sql);
  341. if (!Database::num_rows($result)) {
  342. $sql = "INSERT INTO $t_course (c_id, variable, subkey, value, category, type) VALUES
  343. ($courseId, '$group', '$variable', '$value', 'plugins', '$type')";
  344. Database::query($sql);
  345. }
  346. } else {
  347. $sql = "SELECT value FROM $t_course
  348. WHERE c_id = $courseId AND variable = '$variable' ";
  349. $result = Database::query($sql);
  350. if (!Database::num_rows($result)) {
  351. $sql = "INSERT INTO $t_course (c_id, variable, value, category, subkey, type) VALUES
  352. ($courseId, '$variable','$value', 'plugins', '$plugin_name', '$type')";
  353. Database::query($sql);
  354. }
  355. }
  356. }
  357. }
  358. // Stop here if we don't want a tool link on the course homepage
  359. if (!$add_tool_link) {
  360. return true;
  361. }
  362. //Add an icon in the table tool list
  363. $t_tool = Database::get_course_table(TABLE_TOOL_LIST);
  364. $sql = "SELECT name FROM $t_tool
  365. WHERE c_id = $courseId AND name = '$plugin_name' ";
  366. $result = Database::query($sql);
  367. if (!Database::num_rows($result)) {
  368. $tool_link = "$plugin_name/start.php";
  369. $visibility = string2binary(api_get_setting('course_create_active_tools', $plugin_name));
  370. $sql = "INSERT INTO $t_tool VALUES
  371. ($courseId, NULL, '$plugin_name', '$tool_link', '$plugin_name.png',' ".$visibility."','0', 'squaregrey.gif','NO','_self','plugin','0')";
  372. Database::query($sql);
  373. }
  374. }
  375. /**
  376. * Delete the fields added to the course settings page and the link to the
  377. * tool on the course's homepage
  378. * @param int $courseId
  379. * @return void
  380. */
  381. public function uninstall_course_fields($courseId)
  382. {
  383. $courseId = intval($courseId);
  384. if (empty($courseId)) {
  385. return false;
  386. }
  387. $plugin_name = $this->get_name();
  388. $t_course = Database::get_course_table(TABLE_COURSE_SETTING);
  389. $t_tool = Database::get_course_table(TABLE_TOOL_LIST);
  390. if (!empty($this->course_settings)) {
  391. foreach ($this->course_settings as $setting) {
  392. $variable = Database::escape_string($setting['name']);
  393. if (!empty($setting['group'])) {
  394. $variable = Database::escape_string($setting['group']);
  395. }
  396. if (empty($variable)) {
  397. continue;
  398. }
  399. $sql = "DELETE FROM $t_course
  400. WHERE c_id = $courseId AND variable = '$variable'";
  401. Database::query($sql);
  402. }
  403. }
  404. $plugin_name = Database::escape_string($plugin_name);
  405. $sql = "DELETE FROM $t_tool
  406. WHERE c_id = $courseId AND name = '$plugin_name'";
  407. Database::query($sql);
  408. }
  409. /**
  410. * Install the course fields and tool link of this plugin in all courses
  411. * @param boolean Whether we want to add a plugin link on the course homepage
  412. *
  413. * @return void
  414. */
  415. public function install_course_fields_in_all_courses($add_tool_link = true)
  416. {
  417. // Update existing courses to add plugin settings
  418. $t_courses = Database::get_main_table(TABLE_MAIN_COURSE);
  419. $sql = "SELECT id FROM $t_courses ORDER BY id";
  420. $res = Database::query($sql);
  421. while ($row = Database::fetch_assoc($res)) {
  422. $this->install_course_fields($row['id'], $add_tool_link);
  423. }
  424. }
  425. /**
  426. * Uninstall the plugin settings fields from all courses
  427. * @return void
  428. */
  429. public function uninstall_course_fields_in_all_courses()
  430. {
  431. // Update existing courses to add conference settings
  432. $t_courses = Database::get_main_table(TABLE_MAIN_COURSE);
  433. $sql = "SELECT id FROM $t_courses ORDER BY id";
  434. $res = Database::query($sql);
  435. while ($row = Database::fetch_assoc($res)) {
  436. $this->uninstall_course_fields($row['id']);
  437. }
  438. }
  439. /**
  440. * @return array
  441. */
  442. public function getCourseSettings()
  443. {
  444. $settings = array();
  445. if (is_array($this->course_settings)) {
  446. foreach ($this->course_settings as $item) {
  447. if (isset($item['group'])) {
  448. if (!in_array($item['group'], $settings)) {
  449. $settings[] = $item['group'];
  450. }
  451. } else {
  452. $settings[] = $item['name'];
  453. }
  454. }
  455. }
  456. return $settings;
  457. }
  458. /**
  459. * Method to be extended when changing the setting in the course
  460. * configuration should trigger the use of a callback method
  461. * @param array $values sent back from the course configuration script
  462. *
  463. * @return void
  464. */
  465. public function course_settings_updated($values = array())
  466. {
  467. }
  468. /**
  469. * Add a tab to platform
  470. * @param string $tabName
  471. * @param string $url
  472. *
  473. * @return boolean
  474. */
  475. public function addTab($tabName, $url)
  476. {
  477. $sql = "SELECT * FROM settings_current
  478. WHERE
  479. variable = 'show_tabs' AND
  480. subkey like 'custom_tab_%'";
  481. $result = Database::query($sql);
  482. $customTabsNum = Database::num_rows($result);
  483. $tabNum = $customTabsNum + 1;
  484. //Avoid Tab Name Spaces
  485. $tabNameNoSpaces = preg_replace('/\s+/', '', $tabName);
  486. $subkeytext = "Tabs" . $tabNameNoSpaces;
  487. //Check if it is already added
  488. $checkCondition = array(
  489. 'where' =>
  490. array(
  491. "variable = 'show_tabs' AND subkeytext = ?" => array(
  492. $subkeytext
  493. )
  494. )
  495. );
  496. $checkDuplicate = Database::select('*', 'settings_current', $checkCondition);
  497. if (!empty($checkDuplicate)) {
  498. return false;
  499. }
  500. //End Check
  501. $subkey = 'custom_tab_' . $tabNum;
  502. $attributes = array(
  503. 'variable' => 'show_tabs',
  504. 'subkey' => $subkey,
  505. 'type' => 'checkbox',
  506. 'category' => 'Platform',
  507. 'selected_value' => 'true',
  508. 'title' => $tabName,
  509. 'comment' => $url,
  510. 'subkeytext' => $subkeytext,
  511. 'access_url' => 1,
  512. 'access_url_changeable' => 0,
  513. 'access_url_locked' => 0
  514. );
  515. $resp = Database::insert('settings_current', $attributes);
  516. //Save the id
  517. $settings = $this->get_settings();
  518. $setData = array (
  519. 'comment' => $subkey
  520. );
  521. $whereCondition = array(
  522. 'id = ?' => key($settings)
  523. );
  524. Database::update('settings_current', $setData, $whereCondition);
  525. return $resp;
  526. }
  527. /**
  528. * Delete a tab to chamilo's platform
  529. * @param string $key
  530. * @return boolean $resp Transaction response
  531. */
  532. public function deleteTab($key)
  533. {
  534. $sql = "SELECT *
  535. FROM settings_current
  536. WHERE variable = 'show_tabs'
  537. AND subkey <> '$key'
  538. AND subkey like 'custom_tab_%'
  539. ";
  540. $resp = $result = Database::query($sql);
  541. $customTabsNum = Database::num_rows($result);
  542. if (!empty($key)) {
  543. $whereCondition = array(
  544. 'variable = ? AND subkey = ?' => array('show_tabs', $key)
  545. );
  546. $resp = Database::delete('settings_current', $whereCondition);
  547. //if there is more than one tab
  548. //re enumerate them
  549. if (!empty($customTabsNum) && $customTabsNum > 0) {
  550. $tabs = Database::store_result($result, 'ASSOC');
  551. $i = 1;
  552. foreach ($tabs as $row) {
  553. $attributes = array(
  554. 'subkey' => 'custom_tab_' . $i
  555. );
  556. $this->updateTab($row['subkey'], $attributes);
  557. $i++;
  558. }
  559. }
  560. }
  561. return $resp;
  562. }
  563. /**
  564. * Update the tabs attributes
  565. * @param string $key
  566. * @param array $attributes
  567. *
  568. * @return boolean
  569. */
  570. public function updateTab($key, $attributes)
  571. {
  572. $whereCondition = array(
  573. 'variable = ? AND subkey = ?' => array('show_tabs', $key)
  574. );
  575. $resp = Database::update('settings_current', $attributes, $whereCondition);
  576. return $resp;
  577. }
  578. /**
  579. * This method shows or hides plugin's tab
  580. * @param boolean Shows or hides the main menu plugin tab
  581. * @param string Plugin starter file path
  582. */
  583. public function manageTab($showTab, $filePath = 'index.php')
  584. {
  585. $langString = str_replace('Plugin', '', get_class($this));
  586. $pluginName = strtolower($langString);
  587. $pluginUrl = 'plugin/'.$pluginName.'/'.$filePath;
  588. if ($showTab === 'true') {
  589. $tabAdded = $this->addTab($langString, $pluginUrl);
  590. if ($tabAdded) {
  591. // The page must be refreshed to show the recently created tab
  592. echo "<script>location.href = '".Security::remove_XSS($_SERVER['REQUEST_URI'])."';</script>";
  593. }
  594. } else {
  595. $settingsCurrentTable = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
  596. $conditions = array(
  597. 'where' => array(
  598. "variable = 'show_tabs' AND title = ? AND comment = ? " => array(
  599. $langString,
  600. $pluginUrl
  601. )
  602. )
  603. );
  604. $result = Database::select('subkey', $settingsCurrentTable, $conditions);
  605. if (!empty($result)) {
  606. $this->deleteTab($result[0]['subkey']);
  607. }
  608. }
  609. }
  610. }