PageRenderTime 47ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/integrator/integrator.php

https://gitlab.com/ElvisAns/tiki
PHP | 382 lines | 274 code | 19 blank | 89 comment | 71 complexity | b14bf9d95d3d9a20488fea5368e666ac MD5 | raw file
  1. <?php
  2. // (c) Copyright by authors of the Tiki Wiki CMS Groupware Project
  3. //
  4. // All Rights Reserved. See copyright.txt for details and a complete list of authors.
  5. // Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
  6. // $Id$
  7. /** \file
  8. * \brief Tiki integrator support class
  9. */
  10. if (basename($_SERVER['SCRIPT_NAME']) === basename(__FILE__)) {
  11. die('This script may only be included.');
  12. }
  13. include_once('lib/tikilib.php');
  14. class TikiIntegrator
  15. {
  16. public $c_rep; //!< cached value for repository data
  17. /// Repository management
  18. //\{
  19. /// List all
  20. public function list_repositories($visible_only)
  21. {
  22. global $tikilib;
  23. $values = [];
  24. $cond = '';
  25. if ($visible_only == true) {
  26. $cond = "where `visibility`=?";
  27. $values[] = 'y';
  28. }
  29. $query = "select * from `tiki_integrator_reps` " . $cond . " order by `name`";
  30. $result = $tikilib->query($query, $values);
  31. $ret = [];
  32. while ($res = $result->fetchRow(DB_FETCHMODE_ASSOC)) {
  33. $ret[] = $res;
  34. }
  35. return $ret;
  36. }
  37. /// Add/Update
  38. public function add_replace_repository($repID, $name, $path, $start, $css, $vis, $cacheable, $exp, $descr)
  39. {
  40. global $tikilib;
  41. $parms = [$name, $path, $start, $css, $vis, $cacheable, $exp, $descr];
  42. if (strlen($repID) == 0 || $repID == 0) {
  43. $query = "insert into `tiki_integrator_reps` (`name`,`path`,`start_page`,`css_file`, `visibility`,`cacheable`,`expiration`,`description`) values(?,?,?,?,?,?,?,?)";
  44. } else {
  45. $query = "update `tiki_integrator_reps` set `name`=?,`path`=?,`start_page`=?,`css_file`=?,`visibility`=?,`cacheable`=?,`expiration`=?,`description`=? where `repID`=?";
  46. $parms[] = (int) $repID;
  47. }
  48. $result = $tikilib->query($query, $parms);
  49. // Invalidate cached repository if needed
  50. if (isset($this->c_rep["repID"]) && ($this->c_rep["repID"] == $repID)) {
  51. unset($this->c_rep);
  52. }
  53. }
  54. /// Get one entry by ID
  55. public function get_repository($repID)
  56. {
  57. global $tikilib;
  58. // Check if we already cache requested repository info
  59. if (isset($this->c_rep["repID"]) && ($this->c_rep["repID"] == $repID)) {
  60. return $this->c_rep;
  61. } else { // Need to select it...
  62. $query = "select * from `tiki_integrator_reps` where `repID`=?";
  63. $result = $tikilib->query($query, [$repID]);
  64. if (! $result->numRows()) {
  65. return false;
  66. }
  67. $res = $result->fetchRow(DB_FETCHMODE_ASSOC);
  68. $c_rep = $res;
  69. }
  70. return $res;
  71. }
  72. /// Remove repository and all rules configured for it
  73. public function remove_repository($repID)
  74. {
  75. global $tikilib;
  76. $query = "delete from `tiki_integrator_rules` where `repID`=?";
  77. $result = $tikilib->query($query, [$repID]);
  78. $query = "delete from `tiki_integrator_reps` where `repID`=?";
  79. $result = $tikilib->query($query, [$repID]);
  80. // Check if we remove cached repository
  81. if (isset($this->c_rep["repID"]) && ($this->c_rep["repID"] == $repID)) {
  82. unset($this->c_rep);
  83. }
  84. // Clear cached pages for this repository
  85. $this->clear_cache($repID);
  86. }
  87. //\}
  88. /// Rules management
  89. //\{
  90. /// List rules for given repository
  91. public function list_rules($repID)
  92. {
  93. global $tikilib;
  94. $query = "select * from `tiki_integrator_rules` where `repID`=? order by `ord`";
  95. $result = $tikilib->query($query, [$repID]);
  96. $ret = [];
  97. while ($res = $result->fetchRow(DB_FETCHMODE_ASSOC)) {
  98. $ret[] = $res;
  99. }
  100. return $ret;
  101. }
  102. /// Add or update rule for repository
  103. public function add_replace_rule($repID, $ruleID, $ord, $srch, $repl, $type, $case, $rxmod, $en, $descr)
  104. {
  105. global $tikilib;
  106. if ($ord == 0) {
  107. $query = "select max(`ord`) from `tiki_integrator_rules` where `repID`=?";
  108. $ord = $tikilib->getOne($query, [$repID]) + 1;
  109. }
  110. if (strlen($ruleID) == 0 || $ruleID == 0) {
  111. $query = "insert into `tiki_integrator_rules`
  112. (`repID`,`ord`,`srch`,`repl`,`type`,`casesense`,`rxmod`,`enabled`,`description`)
  113. values(?,?,?,?,?,?,?,?,?)";
  114. $qparms = [$repID, $ord, $srch, $repl, $type, $case, $rxmod, $en, $descr];
  115. } else {
  116. $query = "update `tiki_integrator_rules`
  117. set `repID`=?,`ord`=?,`srch`=?,`repl`=?,`type`=?,`casesense`=?,
  118. `rxmod`=?,`enabled`=?,`description`=? where `ruleID`=?";
  119. $qparms = [$repID, $ord, $srch, $repl, $type, $case, $rxmod, $en, $descr,(int) $ruleID];
  120. }
  121. $result = $tikilib->query($query, $qparms);
  122. // Clear cached pages for this repository
  123. $this->clear_cache($repID);
  124. }
  125. /// Get one entry by ID
  126. public function get_rule($ruleID)
  127. {
  128. global $tikilib;
  129. $query = "select * from `tiki_integrator_rules` where `ruleID`=?";
  130. $result = $tikilib->query($query, [$ruleID]);
  131. if (! $result->numRows()) {
  132. return false;
  133. }
  134. $res = $result->fetchRow(DB_FETCHMODE_ASSOC);
  135. return $res;
  136. }
  137. /// Remove rule
  138. public function remove_rule($ruleID)
  139. {
  140. global $tikilib;
  141. // Clear cached pages for this repository
  142. $rule = $this->get_rule($ruleID);
  143. $this->clear_cache($rule["repID"]);
  144. // Remove rule
  145. $query = "delete from `tiki_integrator_rules` where `ruleID`=?";
  146. $result = $tikilib->query($query, [$ruleID]);
  147. }
  148. /// Apply rule to string
  149. public function apply_rule(&$rep, &$rule, $data)
  150. {
  151. // Is there something to search? If no or rule disabled return original data
  152. if ((strlen($rule["srch"]) == 0) || ($rule["enabled"] != 'y')) {
  153. return $data;
  154. }
  155. // Prepare replace string (subst {path})
  156. $repl = str_replace('{path}', $rep["path"], $rule["repl"]);
  157. $repl = str_replace('{repID}', $rep["repID"], $repl);
  158. //
  159. $d = $data;
  160. if ($rule["type"] == 'y') {
  161. // regex rule. Do replace 'till we have smth to replace (if 'g' modifier present)...
  162. $g = ! (strpos($rule["rxmod"], 'g') === false);
  163. $mod = str_replace('g', '', $rule["rxmod"]);
  164. do {
  165. $tmp = $d;
  166. $d = preg_replace('_' . $rule["srch"] . '_' . $mod, $repl, $tmp);
  167. } while ((strcmp($d, $tmp) != 0) && ($g == true));
  168. unset($tmp);
  169. } else {
  170. // simple str_replace rule
  171. if ($rule["casesense"] == 'y') {
  172. $d = str_replace($rule['srch'], $repl, $d);
  173. } else { // \todo Hmmm... where is str_ireplace() ???
  174. $d = str_replace($rule["srch"], $repl, $d);
  175. }
  176. }
  177. return $d;
  178. }
  179. /// Apply all rules in defined order and returns a filtered text
  180. public function apply_all_rules($repID, $data)
  181. {
  182. $rules = $this->list_rules($repID);
  183. // Get repository configuration data
  184. $rep = $this->get_repository($repID);
  185. if (is_array($rules)) {
  186. foreach ($rules as $rule) {
  187. $data = $this->apply_rule($rep, $rule, $data);
  188. }
  189. }
  190. return $data;
  191. }
  192. //\}
  193. /// Build full path to file inside given repository
  194. public function get_rep_file($rep, $file = '')
  195. {
  196. // Is repository path absolute? (start from www root ('/'))
  197. $p = '';
  198. if (
  199. (substr($rep["path"], 0, 7) == 'http://')
  200. || (substr($rep["path"], 0, 8) == 'https://')
  201. ) {
  202. // It is remote repository -- just copy configured path
  203. $p = $rep["path"];
  204. } elseif (substr($rep["path"], 0, 1) == '/') {
  205. // Absolute path: prepend web server root
  206. $p = $_SERVER['DOCUMENT_ROOT'] . $rep["path"];
  207. } else { // Relative Tiki base path: get tiki root and append repository path
  208. // note: little hack here -- assume that __this__ file placed exactly
  209. // at 2nd dir level in Tiki base dir.
  210. $p = dirname(dirname(__DIR__)) . '/' . $rep["path"];
  211. }
  212. return $p . '/' . ((strlen($file) > 0) ? $file : $rep["start_page"]);
  213. }
  214. /// Return CSS file for given repository
  215. public function get_rep_css($repID)
  216. {
  217. global $style;
  218. global $style_base;
  219. // Return if no CSS file defined for repository
  220. $rep = $this->get_repository($repID);
  221. if (! isset($rep["css_file"]) || strlen($rep["css_file"]) == 0) {
  222. return '';
  223. }
  224. $tiki_root = $_SERVER['DOCUMENT_ROOT'] . dirname($_SERVER['SCRIPT_NAME']);
  225. // Fill array of dirs to scan (local filesystem, and web based)
  226. $dirs = [];
  227. $dirs[] = ['fs' => $tiki_root . "/styles/" . $style_base, 'rel' => "styles/" . $style_base];
  228. $dirs[] = ['fs' => $tiki_root . "/styles/integrator", 'rel' => "styles/integrator"];
  229. $dirs[] = ['fs' => $tiki_root . "/" . $rep['path'], 'rel' => "/" . $rep['path']];
  230. // Fill array of files to search
  231. $ts = preg_replace('|\.css|', '', $style); // Tiki style w/o '.css' extension
  232. $is = preg_replace('|\.css|', '', $rep["css_file"]);
  233. $files = [];
  234. $files[] = $ts . '-' . $rep["css_file"]; // matrix-doxygen.css
  235. $files[] = $ts . '.' . $rep["css_file"]; // matrix.doxygen.css
  236. $files[] = $ts . '_' . $rep["css_file"]; // matrix_doxygen.css
  237. $files[] = $is . '-' . $style; // doxygen-matrix.css
  238. $files[] = $is . '.' . $style; // doxygen.matrix.css
  239. $files[] = $is . '_' . $style; // doxygen_matrix.css
  240. $files[] = $rep["css_file"]; // doxygen.css
  241. // Make full list of files to search (combine all dirs with all files)
  242. $candidates = [];
  243. foreach ($dirs as $dir) {
  244. foreach ($files as $file) {
  245. $candidates[] = ['fs' => $dir['fs'] . '/' . $file, 'rel' => $dir['rel'] . '/' . $file];
  246. }
  247. }
  248. // Search for CSS file
  249. foreach ($candidates as $candidate) {
  250. if (file_exists($candidate['fs'])) {
  251. return $candidate['rel'];
  252. }
  253. }
  254. // Nothing found...
  255. return '';
  256. }
  257. /**
  258. * \brief Copy rules from one repository to another
  259. *
  260. * Variant #1 (stupid but working):
  261. * a) get rules list for repository 1
  262. * b) fix repID for all elements of list
  263. * c) insert new rules for repository 2
  264. *
  265. * Variant #2 (better but need smth special):
  266. * a) create temporary type=hash table ... (stored in memory and
  267. * auto deleted on connection close -- is all DBs support this?)
  268. * a.1) create table name like original_table_name;
  269. * work for MySQL > 4.1 (smth else?)
  270. * b) select into it rules of rep 1
  271. * c) fix it
  272. * d) copy to main rules table
  273. *
  274. */
  275. public function copy_rules($srcID, $dstID)
  276. {
  277. $rules = $this->list_rules($srcID);
  278. foreach ($rules as $rule) {
  279. $this->add_replace_rule(
  280. $dstID,
  281. 0,
  282. $rule["ord"],
  283. $rule["srch"],
  284. $rule["repl"],
  285. $rule["type"],
  286. $rule["casesense"],
  287. $rule["rxmod"],
  288. $rule["enabled"],
  289. $rule["description"]
  290. );
  291. }
  292. }
  293. /**
  294. * \brief Filter file
  295. * Returns ready for integration data from given file. Cache used if neccessary.
  296. * \param $repID -- repository ID to get file from
  297. * \param $file -- file name to return data from
  298. * \param $use_cache -- use or not cache :)
  299. * \param $url -- URL to associate with cached data
  300. *
  301. * \note File is not checked for existence...
  302. */
  303. public function get_file($repID, $file, $use_cache = 'y', $url = '')
  304. {
  305. global $tikilib;
  306. $data = '';
  307. // Try to get data from cache
  308. $cacheId = 0;
  309. if ($use_cache == 'y' && $url != '' && $tikilib->is_cached($url)) {
  310. $data = $tikilib->get_cache($cacheId = $tikilib->get_cache_id($url));
  311. }
  312. $rep = $this->get_repository($repID);
  313. // If smth found in cache return it... else try to get it by usual way.
  314. if (
  315. $data != '' && isset($data["data"]) && ($data["data"] != '')
  316. && ($rep["expiration"] > 0 ? (time() - $data["refresh"]) < $rep["expiration"] : true)
  317. ) {
  318. return $data["data"];
  319. }
  320. // Get file content to string
  321. if (preg_match('#^https?://#', $file)) {
  322. $data = $tikilib->httprequest($file);
  323. } else {
  324. $data = @file_get_contents($file);
  325. }
  326. if ($lastError = error_get_last()) {
  327. $data .= "ERROR: " . $lastError['message'];
  328. } else {
  329. // Now we need to hack this file by applying all configured rules...
  330. $data = $this->apply_all_rules($repID, $data);
  331. // Add result to cache (remove prev if needed)
  332. if ($cacheId != 0) {
  333. $tikilib->remove_cache($cacheId);
  334. }
  335. $tikilib->cache_url($url, $data);
  336. }
  337. return $data;
  338. }
  339. /// Clear cache for given repository
  340. public function clear_cache($repID)
  341. {
  342. global $tikilib;
  343. // Delete all cached URLs with word 'integrator' in a script
  344. // name and 'repID' parameter equal to function arg...
  345. $query = "delete from `tiki_link_cache` where `url` like ?";
  346. $result = $tikilib->query(
  347. $query,
  348. [$tikilib->httpPrefix() . "/%integrator%.php?%repID=" . $repID . "%"]
  349. );
  350. }
  351. /// Clear cache of given file for given repository
  352. public function clear_cached_file($repID, $file)
  353. {
  354. global $tikilib;
  355. // Delete all cached URLs with word 'integrator' in a script
  356. // name and 'repID' parameter equal to function arg...
  357. $query = "delete from `tiki_link_cache` where `url` like ?";
  358. $result = $tikilib->query(
  359. $query,
  360. [$tikilib->httpPrefix() . "/%integrator%.php?repID=" . $repID . (strlen($file) > 0 ? "&file=" . $file : '')]
  361. );
  362. }
  363. }