PageRenderTime 61ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/backup/cc/cc_lib/cc_utils.php

https://bitbucket.org/kudutest1/moodlegit
PHP | 470 lines | 272 code | 46 blank | 152 comment | 23 complexity | d9b6c339e9dc2e3a24b90f7872ebffb2 MD5 | raw file
  1. <?php
  2. // This file is part of Moodle - http://moodle.org/
  3. //
  4. // Moodle is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // Moodle is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
  16. /**
  17. * @package backup-convert
  18. * @subpackage cc-library
  19. * @copyright 2011 Darko Miletic <dmiletic@moodlerooms.com>
  20. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  21. */
  22. require_once 'xmlbase.php';
  23. /**
  24. *
  25. * Various helper utils
  26. * @author Darko Miletic dmiletic@moodlerooms.com
  27. *
  28. */
  29. abstract class cc_helpers {
  30. /**
  31. *
  32. * Checks extension of the supplied filename
  33. * @param string $filename
  34. */
  35. public static function is_html($filename) {
  36. $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
  37. return in_array($extension, array('htm', 'html'));
  38. }
  39. /**
  40. *
  41. * Generates unique identifier
  42. * @param string $prefix
  43. * @param string $suffix
  44. * @return string
  45. */
  46. public static function uuidgen($prefix = '', $suffix = '', $uppercase = true) {
  47. $uuid = trim(sprintf('%s%04x%04x%s', $prefix, mt_rand(0, 65535), mt_rand(0, 65535), $suffix));
  48. $result = $uppercase ? strtoupper($uuid) : strtolower($uuid) ;
  49. return $result;
  50. }
  51. /**
  52. *
  53. * Creates new folder with random name
  54. * @param string $where
  55. * @param string $prefix
  56. * @param string $suffix
  57. * @return mixed - directory short name or false in case of faliure
  58. */
  59. public static function randomdir($where, $prefix = '', $suffix = '') {
  60. $dirname = false;
  61. $randomname = self::uuidgen($prefix, $suffix, false);
  62. $newdirname = $where.DIRECTORY_SEPARATOR.$randomname;
  63. if (mkdir($newdirname)) {
  64. chmod($newdirname, 0755);
  65. $dirname = $randomname;
  66. }
  67. return $dirname;
  68. }
  69. public static function build_query($attributes, $search) {
  70. $result = '';
  71. foreach ($attributes as $attribute) {
  72. if ($result != '') {
  73. $result .= ' | ';
  74. }
  75. $result .= "//*[starts-with(@{$attribute},'{$search}')]/@{$attribute}";
  76. }
  77. return $result;
  78. }
  79. public static function process_embedded_files(&$doc, $attributes, $search, $customslash = null) {
  80. $result = array();
  81. $query = self::build_query($attributes, $search);
  82. $list = $doc->nodeList($query);
  83. foreach ($list as $filelink) {
  84. $rvalue = str_replace($search, '', $filelink->nodeValue);
  85. if (!empty($customslash)) {
  86. $rvalue = str_replace($customslash, '/', $rvalue);
  87. }
  88. $result[] = rawurldecode($rvalue);
  89. }
  90. return $result;
  91. }
  92. /**
  93. *
  94. * Get list of embedded files
  95. * @param string $html
  96. * @return multitype:mixed
  97. */
  98. public static function embedded_files($html) {
  99. $result = array();
  100. $doc = new XMLGenericDocument();
  101. $doc->doc->validateOnParse = false;
  102. $doc->doc->strictErrorChecking = false;
  103. if (!empty($html) && $doc->loadHTML($html)) {
  104. $attributes = array('src', 'href');
  105. $result1 = self::process_embedded_files($doc, $attributes, '@@PLUGINFILE@@');
  106. $result2 = self::process_embedded_files($doc, $attributes, '$@FILEPHP@$', '$@SLASH@$');
  107. $result = array_merge($result1, $result2);
  108. }
  109. return $result;
  110. }
  111. public static function embedded_mapping($packageroot, $contextid = null) {
  112. $main_file = $packageroot . DIRECTORY_SEPARATOR . 'files.xml';
  113. $mfile = new XMLGenericDocument();
  114. if (!$mfile->load($main_file)) {
  115. return false;
  116. }
  117. $query = "/files/file[filename!='.']";
  118. if (!empty($contextid)) {
  119. $query .= "[contextid='{$contextid}']";
  120. }
  121. $files = $mfile->nodeList($query);
  122. $depfiles = array();
  123. foreach ($files as $node) {
  124. $mainfile = intval($mfile->nodeValue('sortorder', $node));
  125. $filename = $mfile->nodeValue('filename', $node);
  126. $filepath = $mfile->nodeValue('filepath', $node);
  127. $source = $mfile->nodeValue('source', $node);
  128. $author = $mfile->nodeValue('author', $node);
  129. $license = $mfile->nodeValue('license', $node);
  130. $hashedname = $mfile->nodeValue('contenthash', $node);
  131. $hashpart = substr($hashedname, 0, 2);
  132. $location = 'files'.DIRECTORY_SEPARATOR.$hashpart.DIRECTORY_SEPARATOR.$hashedname;
  133. $type = $mfile->nodeValue('mimetype', $node);
  134. $depfiles[$filepath.$filename] = array( $location,
  135. ($mainfile == 1),
  136. strtolower(str_replace(' ', '_',$filename)),
  137. $type,
  138. $source,
  139. $author,
  140. $license,
  141. strtolower(str_replace(' ', '_',$filepath)));
  142. }
  143. return $depfiles;
  144. }
  145. public static function add_files(cc_i_manifest &$manifest, $packageroot, $outdir, $allinone = true) {
  146. if (pkg_static_resources::instance()->finished) {
  147. return;
  148. }
  149. $files = cc_helpers::embedded_mapping($packageroot);
  150. $rdir = $allinone ? new cc_resource_location($outdir) : null;
  151. foreach ($files as $virtual => $values) {
  152. $clean_filename = $values[2];
  153. if (!$allinone) {
  154. $rdir = new cc_resource_location($outdir);
  155. }
  156. $rtp = $rdir->fullpath().$values[7].$clean_filename;
  157. //Are there any relative virtual directories?
  158. //let us try to recreate them
  159. $justdir = $rdir->fullpath(false).$values[7];
  160. if (!file_exists($justdir)) {
  161. if (!mkdir($justdir, 0777, true)) {
  162. throw new RuntimeException('Unable to create directories!');
  163. }
  164. }
  165. $source = $packageroot.DIRECTORY_SEPARATOR.$values[0];
  166. if (!copy($source, $rtp)) {
  167. throw new RuntimeException('Unable to copy files!');
  168. }
  169. $resource = new cc_resource($rdir->rootdir(),
  170. $values[7].$clean_filename,
  171. $rdir->dirname(false));
  172. $res = $manifest->add_resource($resource, null, cc_version11::webcontent);
  173. pkg_static_resources::instance()->add($virtual,
  174. $res[0],
  175. $rdir->dirname(false).$values[7].$clean_filename,
  176. $values[1],
  177. $resource);
  178. }
  179. pkg_static_resources::instance()->finished = true;
  180. }
  181. /**
  182. *
  183. * Excerpt from IMS CC 1.1 overview :
  184. * No spaces in filenames, directory and file references should
  185. * employ all lowercase or all uppercase - no mixed case
  186. *
  187. * @param cc_i_manifest $manifest
  188. * @param string $packageroot
  189. * @param integer $contextid
  190. * @param string $outdir
  191. * @param boolean $allinone
  192. * @throws RuntimeException
  193. */
  194. public static function handle_static_content(cc_i_manifest &$manifest, $packageroot, $contextid, $outdir, $allinone = true){
  195. cc_helpers::add_files($manifest, $packageroot, $outdir, $allinone);
  196. return pkg_static_resources::instance()->get_values();
  197. }
  198. public static function handle_resource_content(cc_i_manifest &$manifest, $packageroot, $contextid, $outdir, $allinone = true){
  199. $result = array();
  200. cc_helpers::add_files($manifest, $packageroot, $outdir, $allinone);
  201. $files = cc_helpers::embedded_mapping($packageroot, $contextid);
  202. //$rdir = $allinone ? new cc_resource_location($outdir) : null;
  203. $rootnode = null;
  204. $rootvals = null;
  205. $depfiles = array();
  206. $depres = array();
  207. $flocation = null;
  208. foreach ($files as $virtual => $values) {
  209. $clean_filename = $values[2];
  210. $vals = pkg_static_resources::instance()->get_identifier($virtual);
  211. $resource = $vals[3];
  212. $identifier = $resource->identifier;
  213. $flocation = $vals[1];
  214. if ($values[1]) {
  215. $rootnode = $resource;
  216. $rootvals = $flocation;
  217. continue;
  218. }
  219. $depres[] = $identifier;
  220. $depfiles[] = $vals[1];
  221. $result[$virtual] = array($identifier, $flocation, false);
  222. }
  223. if (!empty($rootnode)) {
  224. $rootnode->files = array_merge($rootnode->files, $depfiles);
  225. $result[$virtual] = array($rootnode->identifier, $rootvals, true);
  226. }
  227. return $result;
  228. }
  229. public static function process_linked_files($content, cc_i_manifest &$manifest, $packageroot, $contextid, $outdir, $webcontent = false) {
  230. /**
  231. - detect all embedded files
  232. - locate their physical counterparts in moodle 2 backup
  233. - copy all files in the cc package stripping any spaces and using inly lowercase letters
  234. - add those files as resources of the type webcontent to the manifest
  235. - replace the links to the resourcse using $IMS-CC-FILEBASE$ and their new locations
  236. - cc_resource has array of files and array of dependencies
  237. - most likely we would need to add all files as independent resources and than
  238. attach them all as dependencies to the forum tag
  239. */
  240. $lfiles = self::embedded_files($content);
  241. $text = $content;
  242. $deps = array();
  243. if (!empty($lfiles)) {
  244. $files = self::handle_static_content($manifest,
  245. $packageroot,
  246. $contextid,
  247. $outdir);
  248. $replaceprefix = $webcontent ? '' : '$IMS-CC-FILEBASE$';
  249. foreach ($lfiles as $lfile) {
  250. if (array_key_exists($lfile, $files)) {
  251. $filename = str_replace('%2F', '/',rawurlencode($lfile));
  252. $content = str_replace('@@PLUGINFILE@@'.$filename,
  253. $replaceprefix.'../'.$files[$lfile][1],
  254. $content);
  255. //for the legacy stuff
  256. $content = str_replace('$@FILEPHP@$'.str_replace('/','$@SLASH@$',$filename),
  257. $replaceprefix.'../'.$files[$lfile][1],
  258. $content);
  259. $deps[] = $files[$lfile][0];
  260. }
  261. }
  262. $text = $content;
  263. }
  264. return array($text, $deps);
  265. }
  266. public static function relative_location($originpath, $linkingpath) {
  267. return false;
  268. }
  269. }
  270. final class cc_resource_location {
  271. /**
  272. *
  273. * Root directory
  274. * @var string
  275. */
  276. private $rootdir = null;
  277. /**
  278. *
  279. * new directory
  280. * @var string
  281. */
  282. private $dir = null;
  283. /**
  284. *
  285. * Full precalculated path
  286. * @var string
  287. */
  288. private $fullpath = null;
  289. /**
  290. *
  291. * ctor
  292. * @param string $rootdir - path to the containing directory
  293. * @throws InvalidArgumentException
  294. * @throws RuntimeException
  295. */
  296. public function __construct($rootdir) {
  297. $rdir = realpath($rootdir);
  298. if (empty($rdir)) {
  299. throw new InvalidArgumentException('Invalid path!');
  300. }
  301. $dir = cc_helpers::randomdir($rdir, 'i_');
  302. if ($dir === false) {
  303. throw new RuntimeException('Unable to create directory!');
  304. }
  305. $this->rootdir = $rdir;
  306. $this->dir = $dir;
  307. $this->fullpath = $rdir.DIRECTORY_SEPARATOR.$dir;
  308. }
  309. /**
  310. *
  311. * Newly created directory
  312. * @return string
  313. */
  314. public function dirname($endseparator=false) {
  315. return $this->dir.($endseparator ? '/' : '');
  316. }
  317. /**
  318. *
  319. * Full path to the new directory
  320. * @return string
  321. */
  322. public function fullpath($endseparator=false) {
  323. return $this->fullpath.($endseparator ? DIRECTORY_SEPARATOR : '');
  324. }
  325. /**
  326. * Returns containing dir
  327. * @return string
  328. */
  329. public function rootdir($endseparator=false) {
  330. return $this->rootdir.($endseparator ? DIRECTORY_SEPARATOR : '');
  331. }
  332. }
  333. class pkg_static_resources {
  334. /**
  335. * @var array
  336. */
  337. private $values = array();
  338. /**
  339. * @var boolean
  340. */
  341. public $finished = false;
  342. /**
  343. * @var pkg_static_resources
  344. */
  345. private static $instance = null;
  346. private function __clone() {}
  347. private function __construct() {}
  348. /**
  349. * @return pkg_static_resources
  350. */
  351. public static function instance() {
  352. if (empty(self::$instance)) {
  353. $c = __CLASS__;
  354. self::$instance = new $c();
  355. }
  356. return self::$instance;
  357. }
  358. /**
  359. *
  360. * add new element
  361. * @param string $identifier
  362. * @param string $file
  363. * @param boolean $main
  364. */
  365. public function add($key, $identifier, $file, $main, $node = null) {
  366. $this->values[$key] = array($identifier, $file, $main, $node);
  367. }
  368. /**
  369. * @return array
  370. */
  371. public function get_values() {
  372. return $this->values;
  373. }
  374. public function get_identifier($location) {
  375. $result = false;
  376. if (array_key_exists($location, $this->values)) {
  377. $result = $this->values[$location];
  378. }
  379. return $result;
  380. }
  381. public function reset() {
  382. $this->values = array();
  383. $this->finished = false ;
  384. }
  385. }
  386. class pkg_resource_dependencies {
  387. /**
  388. * @var array
  389. */
  390. private $values = array();
  391. /**
  392. * @var pkg_resource_dependencies
  393. */
  394. private static $instance = null;
  395. private function __clone() {
  396. }
  397. private function __construct() {
  398. }
  399. /**
  400. * @return pkg_resource_dependencies
  401. */
  402. public static function instance() {
  403. if (empty(self::$instance)) {
  404. $c = __CLASS__;
  405. self::$instance = new $c();
  406. }
  407. return self::$instance;
  408. }
  409. /**
  410. * @param array $deps
  411. */
  412. public function add(array $deps) {
  413. $this->values = array_merge($this->values, $deps);
  414. }
  415. public function reset() {
  416. $this->values = array();
  417. }
  418. /**
  419. * @return array
  420. */
  421. public function get_deps() {
  422. return $this->values;
  423. }
  424. }