Friendica Communications Platform (please note that this is a clone of the repository at github, issues are handled there) https://friendi.ca
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

583 řádky
14KB

  1. <?php
  2. /**
  3. * @file include/plugin.php
  4. *
  5. * @brief Some functions to handle addons and themes.
  6. */
  7. /**
  8. * @brief uninstalls an addon.
  9. *
  10. * @param string $plugin name of the addon
  11. * @return boolean
  12. */
  13. if (! function_exists('uninstall_plugin')){
  14. function uninstall_plugin($plugin){
  15. logger("Addons: uninstalling " . $plugin);
  16. q("DELETE FROM `addon` WHERE `name` = '%s' ",
  17. dbesc($plugin)
  18. );
  19. @include_once('addon/' . $plugin . '/' . $plugin . '.php');
  20. if(function_exists($plugin . '_uninstall')) {
  21. $func = $plugin . '_uninstall';
  22. $func();
  23. }
  24. }}
  25. /**
  26. * @brief installs an addon.
  27. *
  28. * @param string $plugin name of the addon
  29. * @return bool
  30. */
  31. if (! function_exists('install_plugin')){
  32. function install_plugin($plugin) {
  33. // silently fail if plugin was removed
  34. if(! file_exists('addon/' . $plugin . '/' . $plugin . '.php'))
  35. return false;
  36. logger("Addons: installing " . $plugin);
  37. $t = @filemtime('addon/' . $plugin . '/' . $plugin . '.php');
  38. @include_once('addon/' . $plugin . '/' . $plugin . '.php');
  39. if(function_exists($plugin . '_install')) {
  40. $func = $plugin . '_install';
  41. $func();
  42. $plugin_admin = (function_exists($plugin."_plugin_admin")?1:0);
  43. $r = q("INSERT INTO `addon` (`name`, `installed`, `timestamp`, `plugin_admin`) VALUES ( '%s', 1, %d , %d ) ",
  44. dbesc($plugin),
  45. intval($t),
  46. $plugin_admin
  47. );
  48. // we can add the following with the previous SQL
  49. // once most site tables have been updated.
  50. // This way the system won't fall over dead during the update.
  51. if(file_exists('addon/' . $plugin . '/.hidden')) {
  52. q("UPDATE `addon` SET `hidden` = 1 WHERE `name` = '%s'",
  53. dbesc($plugin)
  54. );
  55. }
  56. return true;
  57. }
  58. else {
  59. logger("Addons: FAILED installing " . $plugin);
  60. return false;
  61. }
  62. }}
  63. // reload all updated plugins
  64. if(! function_exists('reload_plugins')) {
  65. function reload_plugins() {
  66. $plugins = get_config('system','addon');
  67. if(strlen($plugins)) {
  68. $r = q("SELECT * FROM `addon` WHERE `installed` = 1");
  69. if (dbm::is_result($r))
  70. $installed = $r;
  71. else
  72. $installed = array();
  73. $parr = explode(',',$plugins);
  74. if(count($parr)) {
  75. foreach($parr as $pl) {
  76. $pl = trim($pl);
  77. $fname = 'addon/' . $pl . '/' . $pl . '.php';
  78. if(file_exists($fname)) {
  79. $t = @filemtime($fname);
  80. foreach($installed as $i) {
  81. if(($i['name'] == $pl) && ($i['timestamp'] != $t)) {
  82. logger('Reloading plugin: ' . $i['name']);
  83. @include_once($fname);
  84. if(function_exists($pl . '_uninstall')) {
  85. $func = $pl . '_uninstall';
  86. $func();
  87. }
  88. if(function_exists($pl . '_install')) {
  89. $func = $pl . '_install';
  90. $func();
  91. }
  92. q("UPDATE `addon` SET `timestamp` = %d WHERE `id` = %d",
  93. intval($t),
  94. intval($i['id'])
  95. );
  96. }
  97. }
  98. }
  99. }
  100. }
  101. }
  102. }}
  103. /**
  104. * @brief check if addon is enabled
  105. *
  106. * @param string $plugin
  107. * @return boolean
  108. */
  109. function plugin_enabled($plugin) {
  110. $r = q("SELECT * FROM `addon` WHERE `installed` = 1 AND `name` = '%s'", $plugin);
  111. return ((dbm::is_result($r)) && (count($r) > 0));
  112. }
  113. /**
  114. * @brief registers a hook.
  115. *
  116. * @param string $hook the name of the hook
  117. * @param string $file the name of the file that hooks into
  118. * @param string $function the name of the function that the hook will call
  119. * @param int $priority A priority (defaults to 0)
  120. * @return mixed|bool
  121. */
  122. if(! function_exists('register_hook')) {
  123. function register_hook($hook,$file,$function,$priority=0) {
  124. $r = q("SELECT * FROM `hook` WHERE `hook` = '%s' AND `file` = '%s' AND `function` = '%s' LIMIT 1",
  125. dbesc($hook),
  126. dbesc($file),
  127. dbesc($function)
  128. );
  129. if (dbm::is_result($r))
  130. return true;
  131. $r = q("INSERT INTO `hook` (`hook`, `file`, `function`, `priority`) VALUES ( '%s', '%s', '%s', '%s' ) ",
  132. dbesc($hook),
  133. dbesc($file),
  134. dbesc($function),
  135. dbesc($priority)
  136. );
  137. return $r;
  138. }}
  139. /**
  140. * @brief unregisters a hook.
  141. *
  142. * @param string $hook the name of the hook
  143. * @param string $file the name of the file that hooks into
  144. * @param string $function the name of the function that the hook called
  145. * @return array
  146. */
  147. if(! function_exists('unregister_hook')) {
  148. function unregister_hook($hook,$file,$function) {
  149. $r = q("DELETE FROM `hook` WHERE `hook` = '%s' AND `file` = '%s' AND `function` = '%s'",
  150. dbesc($hook),
  151. dbesc($file),
  152. dbesc($function)
  153. );
  154. return $r;
  155. }}
  156. if(! function_exists('load_hooks')) {
  157. function load_hooks() {
  158. $a = get_app();
  159. $a->hooks = array();
  160. $r = q("SELECT * FROM `hook` WHERE 1 ORDER BY `priority` DESC, `file`");
  161. if (dbm::is_result($r)) {
  162. foreach ($r as $rr) {
  163. if(! array_key_exists($rr['hook'],$a->hooks))
  164. $a->hooks[$rr['hook']] = array();
  165. $a->hooks[$rr['hook']][] = array($rr['file'],$rr['function']);
  166. }
  167. }
  168. }}
  169. /**
  170. * @brief Calls a hook.
  171. *
  172. * Use this function when you want to be able to allow a hook to manipulate
  173. * the provided data.
  174. *
  175. * @param string $name of the hook to call
  176. * @param string|array &$data to transmit to the callback handler
  177. */
  178. function call_hooks($name, &$data = null) {
  179. $stamp1 = microtime(true);
  180. $a = get_app();
  181. if (is_array($a->hooks) && array_key_exists($name, $a->hooks))
  182. foreach ($a->hooks[$name] as $hook)
  183. call_single_hook($a, $name, $hook, $data);
  184. }
  185. /**
  186. * @brief Calls a single hook.
  187. *
  188. * @param string $name of the hook to call
  189. * @param array $hook Hook data
  190. * @param string|array &$data to transmit to the callback handler
  191. */
  192. function call_single_hook($a, $name, $hook, &$data = null) {
  193. // Don't run a theme's hook if the user isn't using the theme
  194. if (strpos($hook[0], 'view/theme/') !== false && strpos($hook[0], 'view/theme/'.current_theme()) === false)
  195. return;
  196. @include_once($hook[0]);
  197. if (function_exists($hook[1])) {
  198. $func = $hook[1];
  199. $func($a, $data);
  200. } else {
  201. // remove orphan hooks
  202. q("DELETE FROM `hook` WHERE `hook` = '%s' AND `file` = '%s' AND `function` = '%s'",
  203. dbesc($name),
  204. dbesc($hook[0]),
  205. dbesc($hook[1])
  206. );
  207. }
  208. }
  209. //check if an app_menu hook exist for plugin $name.
  210. //Return true if the plugin is an app
  211. if(! function_exists('plugin_is_app')) {
  212. function plugin_is_app($name) {
  213. $a = get_app();
  214. if(is_array($a->hooks) && (array_key_exists('app_menu',$a->hooks))) {
  215. foreach($a->hooks['app_menu'] as $hook) {
  216. if($hook[0] == 'addon/'.$name.'/'.$name.'.php')
  217. return true;
  218. }
  219. }
  220. return false;
  221. }}
  222. /**
  223. * @brief Parse plugin comment in search of plugin infos.
  224. *
  225. * like
  226. * \code
  227. *...* Name: Plugin
  228. * * Description: A plugin which plugs in
  229. * . * Version: 1.2.3
  230. * * Author: John <profile url>
  231. * * Author: Jane <email>
  232. * *
  233. * *\endcode
  234. * @param string $plugin the name of the plugin
  235. * @return array with the plugin information
  236. */
  237. if (! function_exists('get_plugin_info')){
  238. function get_plugin_info($plugin){
  239. $a = get_app();
  240. $info=Array(
  241. 'name' => $plugin,
  242. 'description' => "",
  243. 'author' => array(),
  244. 'version' => "",
  245. 'status' => ""
  246. );
  247. if (!is_file("addon/$plugin/$plugin.php")) return $info;
  248. $stamp1 = microtime(true);
  249. $f = file_get_contents("addon/$plugin/$plugin.php");
  250. $a->save_timestamp($stamp1, "file");
  251. $r = preg_match("|/\*.*\*/|msU", $f, $m);
  252. if ($r){
  253. $ll = explode("\n", $m[0]);
  254. foreach( $ll as $l ) {
  255. $l = trim($l,"\t\n\r */");
  256. if ($l!=""){
  257. list($k,$v) = array_map("trim", explode(":",$l,2));
  258. $k= strtolower($k);
  259. if ($k=="author"){
  260. $r=preg_match("|([^<]+)<([^>]+)>|", $v, $m);
  261. if ($r) {
  262. $info['author'][] = array('name'=>$m[1], 'link'=>$m[2]);
  263. } else {
  264. $info['author'][] = array('name'=>$v);
  265. }
  266. } else {
  267. if (array_key_exists($k,$info)){
  268. $info[$k]=$v;
  269. }
  270. }
  271. }
  272. }
  273. }
  274. return $info;
  275. }}
  276. /**
  277. * @brief Parse theme comment in search of theme infos.
  278. *
  279. * like
  280. * \code
  281. * ..* Name: My Theme
  282. * * Description: My Cool Theme
  283. * . * Version: 1.2.3
  284. * * Author: John <profile url>
  285. * * Maintainer: Jane <profile url>
  286. * *
  287. * \endcode
  288. * @param string $theme the name of the theme
  289. * @return array
  290. */
  291. if (! function_exists('get_theme_info')){
  292. function get_theme_info($theme){
  293. $info=Array(
  294. 'name' => $theme,
  295. 'description' => "",
  296. 'author' => array(),
  297. 'maintainer' => array(),
  298. 'version' => "",
  299. 'credits' => "",
  300. 'experimental' => false,
  301. 'unsupported' => false
  302. );
  303. if(file_exists("view/theme/$theme/experimental"))
  304. $info['experimental'] = true;
  305. if(file_exists("view/theme/$theme/unsupported"))
  306. $info['unsupported'] = true;
  307. if (!is_file("view/theme/$theme/theme.php")) return $info;
  308. $a = get_app();
  309. $stamp1 = microtime(true);
  310. $f = file_get_contents("view/theme/$theme/theme.php");
  311. $a->save_timestamp($stamp1, "file");
  312. $r = preg_match("|/\*.*\*/|msU", $f, $m);
  313. if ($r){
  314. $ll = explode("\n", $m[0]);
  315. foreach( $ll as $l ) {
  316. $l = trim($l,"\t\n\r */");
  317. if ($l!=""){
  318. list($k,$v) = array_map("trim", explode(":",$l,2));
  319. $k= strtolower($k);
  320. if ($k=="author"){
  321. $r=preg_match("|([^<]+)<([^>]+)>|", $v, $m);
  322. if ($r) {
  323. $info['author'][] = array('name'=>$m[1], 'link'=>$m[2]);
  324. } else {
  325. $info['author'][] = array('name'=>$v);
  326. }
  327. }
  328. elseif ($k=="maintainer"){
  329. $r=preg_match("|([^<]+)<([^>]+)>|", $v, $m);
  330. if ($r) {
  331. $info['maintainer'][] = array('name'=>$m[1], 'link'=>$m[2]);
  332. } else {
  333. $info['maintainer'][] = array('name'=>$v);
  334. }
  335. } else {
  336. if (array_key_exists($k,$info)){
  337. $info[$k]=$v;
  338. }
  339. }
  340. }
  341. }
  342. }
  343. return $info;
  344. }}
  345. /**
  346. * @brief Returns the theme's screenshot.
  347. *
  348. * The screenshot is expected as view/theme/$theme/screenshot.[png|jpg].
  349. *
  350. * @param sring $theme The name of the theme
  351. * @return string
  352. */
  353. function get_theme_screenshot($theme) {
  354. $exts = array('.png','.jpg');
  355. foreach($exts as $ext) {
  356. if (file_exists('view/theme/' . $theme . '/screenshot' . $ext)) {
  357. return(App::get_baseurl() . '/view/theme/' . $theme . '/screenshot' . $ext);
  358. }
  359. }
  360. return(App::get_baseurl() . '/images/blank.png');
  361. }
  362. // install and uninstall theme
  363. if (! function_exists('uninstall_theme')){
  364. function uninstall_theme($theme){
  365. logger("Addons: uninstalling theme " . $theme);
  366. include_once("view/theme/$theme/theme.php");
  367. if (function_exists("{$theme}_uninstall")) {
  368. $func = "{$theme}_uninstall";
  369. $func();
  370. }
  371. }}
  372. if (! function_exists('install_theme')){
  373. function install_theme($theme) {
  374. // silently fail if theme was removed
  375. if (! file_exists("view/theme/$theme/theme.php")) {
  376. return false;
  377. }
  378. logger("Addons: installing theme $theme");
  379. include_once("view/theme/$theme/theme.php");
  380. if (function_exists("{$theme}_install")) {
  381. $func = "{$theme}_install";
  382. $func();
  383. return true;
  384. } else {
  385. logger("Addons: FAILED installing theme $theme");
  386. return false;
  387. }
  388. }}
  389. // check service_class restrictions. If there are no service_classes defined, everything is allowed.
  390. // if $usage is supplied, we check against a maximum count and return true if the current usage is
  391. // less than the subscriber plan allows. Otherwise we return boolean true or false if the property
  392. // is allowed (or not) in this subscriber plan. An unset property for this service plan means
  393. // the property is allowed, so it is only necessary to provide negative properties for each plan,
  394. // or what the subscriber is not allowed to do.
  395. function service_class_allows($uid,$property,$usage = false) {
  396. if ($uid == local_user()) {
  397. $service_class = $a->user['service_class'];
  398. } else {
  399. $r = q("SELECT `service_class` FROM `user` WHERE `uid` = %d LIMIT 1",
  400. intval($uid)
  401. );
  402. if (dbm::is_result($r)) {
  403. $service_class = $r[0]['service_class'];
  404. }
  405. }
  406. if (! x($service_class)) {
  407. // everything is allowed
  408. return true;
  409. }
  410. $arr = get_config('service_class',$service_class);
  411. if (! is_array($arr) || (! count($arr))) {
  412. return true;
  413. }
  414. if ($usage === false) {
  415. return ((x($arr[$property])) ? (bool) $arr['property'] : true);
  416. } else {
  417. if (! array_key_exists($property,$arr)) {
  418. return true;
  419. }
  420. return (((intval($usage)) < intval($arr[$property])) ? true : false);
  421. }
  422. }
  423. function service_class_fetch($uid,$property) {
  424. if ($uid == local_user()) {
  425. $service_class = $a->user['service_class'];
  426. } else {
  427. $r = q("SELECT `service_class` FROM `user` WHERE `uid` = %d LIMIT 1",
  428. intval($uid)
  429. );
  430. if (dbm::is_result($r)) {
  431. $service_class = $r[0]['service_class'];
  432. }
  433. }
  434. if(! x($service_class))
  435. return false; // everything is allowed
  436. $arr = get_config('service_class',$service_class);
  437. if(! is_array($arr) || (! count($arr)))
  438. return false;
  439. return((array_key_exists($property,$arr)) ? $arr[$property] : false);
  440. }
  441. function upgrade_link($bbcode = false) {
  442. $l = get_config('service_class','upgrade_link');
  443. if(! $l)
  444. return '';
  445. if($bbcode)
  446. $t = sprintf('[url=%s]' . t('Click here to upgrade.') . '[/url]', $l);
  447. else
  448. $t = sprintf('<a href="%s">' . t('Click here to upgrade.') . '</div>', $l);
  449. return $t;
  450. }
  451. function upgrade_message($bbcode = false) {
  452. $x = upgrade_link($bbcode);
  453. return t('This action exceeds the limits set by your subscription plan.') . (($x) ? ' ' . $x : '') ;
  454. }
  455. function upgrade_bool_message($bbcode = false) {
  456. $x = upgrade_link($bbcode);
  457. return t('This action is not available under your subscription plan.') . (($x) ? ' ' . $x : '') ;
  458. }
  459. /**
  460. * @brief Get the full path to relevant theme files by filename
  461. *
  462. * This function search in the theme directory (and if not present in global theme directory)
  463. * if there is a directory with the file extension and for a file with the given
  464. * filename.
  465. *
  466. * @param string $file Filename
  467. * @param string $root Full root path
  468. * @return string Path to the file or empty string if the file isn't found
  469. */
  470. function theme_include($file, $root = '') {
  471. // Make sure $root ends with a slash / if it's not blank
  472. if($root !== '' && $root[strlen($root)-1] !== '/')
  473. $root = $root . '/';
  474. $theme_info = $a->theme_info;
  475. if(is_array($theme_info) AND array_key_exists('extends',$theme_info))
  476. $parent = $theme_info['extends'];
  477. else
  478. $parent = 'NOPATH';
  479. $theme = current_theme();
  480. $thname = $theme;
  481. $ext = substr($file,strrpos($file,'.')+1);
  482. $paths = array(
  483. "{$root}view/theme/$thname/$ext/$file",
  484. "{$root}view/theme/$parent/$ext/$file",
  485. "{$root}view/$ext/$file",
  486. );
  487. foreach($paths as $p) {
  488. // strpos() is faster than strstr when checking if one string is in another (http://php.net/manual/en/function.strstr.php)
  489. if(strpos($p,'NOPATH') !== false)
  490. continue;
  491. if(file_exists($p))
  492. return $p;
  493. }
  494. return '';
  495. }