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

/wa-apps/site/lib/classes/siteThemes.class.php

http://github.com/webasyst/webasyst-framework
PHP | 334 lines | 266 code | 35 blank | 33 comment | 49 complexity | 08e56a3b79fec1e8076411077384640a MD5 | raw file
Possible License(s): LGPL-3.0, CC-BY-3.0, BSD-3-Clause, MIT
  1. <?php
  2. class siteThemes extends waTheme
  3. {
  4. public static function load($themes, $domains = null, $app_id = null)
  5. {
  6. foreach($themes as $theme_id => &$theme) {
  7. self::prepare($theme);
  8. $theme['used'] = self::getRoutingRules($domains, $app_id, $theme_id);
  9. unset($theme);
  10. }
  11. return $themes;
  12. }
  13. public static function sort($themes)
  14. {
  15. uasort($themes, array(__CLASS__,'sortThemesHandler'));
  16. return $themes;
  17. }
  18. private static function getRoutingRules($domains,$app_id,$theme_id)
  19. {
  20. static $themes;
  21. if (!is_array($themes)) {
  22. $themes = array();
  23. $theme_types = array('desktop'=>'theme','mobile'=>'theme_mobile');
  24. $routing = wa()->getRouting();
  25. foreach( (array)$domains as $domain) {
  26. $routing_params = array('domain' => $domain);//,'app'=>$app_id);
  27. $rules = $routing->getRoutes($domain);
  28. foreach ($rules as $route_id => $rule) {
  29. if (isset($rule['app'])) {
  30. foreach($theme_types as $type=>$source) {
  31. $id = isset($rule[$source])?$rule[$source]:'default';
  32. $app = $rule['app'];
  33. if (!isset($themes[$app])) {
  34. $themes[$app] = array();
  35. }
  36. if (!isset($themes[$app][$id])) {
  37. $themes[$app][$id] = array();
  38. }
  39. $themes[$app][$id][] = array(
  40. 'domain' => $domain,
  41. 'url' => $rule['url'],
  42. 'type' => $type,
  43. 'preview' => $routing->getUrlByRoute($rule, $domain)
  44. );
  45. }
  46. }
  47. }
  48. }
  49. }
  50. return isset($themes[$app_id][$theme_id])?$themes[$app_id][$theme_id]:false;
  51. }
  52. private static function sortThemesHandler($theme1, $theme2)
  53. {
  54. return min(1,max(-1,$theme2['mtime'] - $theme1['mtime']));
  55. }
  56. /**
  57. *
  58. * @param $slug
  59. * @return siteThemes
  60. */
  61. public static function getInstance($slug)
  62. {
  63. $slug = urldecode($slug);
  64. if(preg_match('@^/?([a-z_0-9]+)/themes/([a-zA-Z_0-9\-]+)/?$@',$slug,$matches)){
  65. return new self($matches[2],$matches[1]);
  66. }else {
  67. throw new waException(_w('Invalid theme slug').$slug);
  68. }
  69. }
  70. private static function prepare(&$theme)
  71. {
  72. static $root;
  73. if (!$root) {
  74. $root = wa()->getConfig()->getRootPath();
  75. }
  76. $theme['name'];
  77. if(!isset($theme['preview'])) {
  78. $theme['preview'] = false;
  79. }
  80. if(!isset($theme['used'])) {
  81. $theme['used'] = false;
  82. }
  83. if ($theme['path_custom'] && (strpos($theme['path_custom'],$root) === 0)) {
  84. $theme['custom'] = str_replace($root,'',$theme['path_custom']);
  85. $theme['custom'] = preg_replace(array('@[\\\\/]+@','@^/@'),array('/',''),$theme['custom']);
  86. }
  87. if ($theme['path_original'] && (strpos($theme['path_original'],$root) === 0)) {
  88. $theme['original'] = str_replace($root,'',$theme['path_original']);
  89. $theme['original'] = preg_replace(array('@[\\\\/]+@','@^/@'),array('/',''),$theme['original']);
  90. }
  91. if (isset($theme['app'])) {
  92. //$theme['app_id'] = $theme['app'];
  93. $theme['slug'] = $theme['app'].'/themes/'.$theme['id'];
  94. }
  95. }
  96. public function getInfo($domains = null)
  97. {
  98. //$info = array_merge($this->info,get_object_vars($this));
  99. self::prepare($this);
  100. $this->extra_info['used'] = self::getRoutingRules($domains, $this->app, $this->id);
  101. return $this;
  102. }
  103. public static function exists($id, $app = true, $force = false)
  104. {
  105. self::verify($id);
  106. $app = ($app === true) ? wa()->getApp() : $app;
  107. $path_custom = wa()->getDataPath('themes', true, $app).'/'.$id;
  108. $path_original = wa()->getAppPath('themes/', $app).$id;
  109. if (!file_exists($path_custom) || (!$force && !file_exists($path_custom.'/'.self::PATH))) {
  110. $path_custom = false;
  111. }
  112. if (!file_exists($path_original) || (!$force && !file_exists($path_original.'/'.self::PATH))) {
  113. $path_original = false;
  114. }
  115. return ($path_custom || $path_original)?true:false;
  116. }
  117. public function check()
  118. {
  119. if(!$this->path) {
  120. throw new waException(sprintf(_w("Theme %s not found"),$this->id));
  121. }
  122. if(!file_exists($this->path) || !file_exists($this->path.'/'.self::PATH)) {
  123. self::throwThemeException('MISSING_THEME_XML',$this->id);
  124. }
  125. //TODO check files of theme
  126. }
  127. /**
  128. *
  129. * @return siteThemes
  130. */
  131. public function brush()
  132. {
  133. //TODO check theme type
  134. waFiles::delete($this->path_custom,false);
  135. $this->flush();
  136. $instance = new self($this->id,$this->app);
  137. return $instance;
  138. }
  139. public function purge()
  140. {
  141. //TODO check theme type
  142. waFiles::delete($this->path_custom);
  143. //waFiles::delete($this->original);
  144. $this->flush();
  145. }
  146. public function duplicate()
  147. {
  148. $numerator = 0;
  149. do {
  150. $id = $this->id.++$numerator;
  151. if($numerator>1000){
  152. break;
  153. }
  154. } while($available = self::exists($id,$this->app,true));
  155. if($available) {
  156. throw new waException(_w("Duplicate theme failed"));
  157. }
  158. $names = $this->getName(true);
  159. foreach($names as &$name) {
  160. $name .= ' '.$numerator;
  161. }
  162. unset($name);
  163. return $this->copy($this->id.$numerator,array('name'=>$names));
  164. }
  165. /**
  166. *
  167. * Extract theme from archive
  168. * @throws Exception
  169. * @param string $source_path archive path
  170. *
  171. * @return siteThemes
  172. */
  173. public static function extract($source_path)
  174. {
  175. $autoload = waAutoload::getInstance();
  176. $autoload->add('Archive_Tar','wa-installer/lib/vendors/PEAR/Tar.php');
  177. $autoload->add('PEAR','wa-installer/lib/vendors/PEAR/PEAR.php');
  178. if (class_exists('Archive_Tar')) {
  179. try {
  180. $tar_object= new Archive_Tar($source_path,true);
  181. $files = $tar_object->listContent();
  182. if(!$files) {
  183. self::throwArchiveException('INVALID_OR_EMPTY_ARCHIVE');
  184. }
  185. //search theme info
  186. $theme_check_files = array(self::PATH,);
  187. $theme_files_map = array();
  188. $info = false;
  189. $pattern = "/(\/|^)".wa_make_pattern(self::PATH)."$/";
  190. foreach($files as $file) {
  191. if (preg_match($pattern,$file['filename'])) {
  192. $info = $tar_object->extractInString($file['filename']);
  193. break;
  194. }
  195. }
  196. if(!$info) {
  197. self::throwThemeException('MISSING_THEME_XML');
  198. }
  199. $xml = @simplexml_load_string($info);
  200. $app_id = (string)$xml['app'];
  201. $id = (string)$xml['id'];
  202. if (!$app_id) {
  203. self::throwThemeException('MISSING_APP_ID');
  204. } elseif(!$id) {
  205. self::throwThemeException('MISSING_THEME_ID');
  206. } else {
  207. if($app_info = wa()->getAppInfo($app_id)) {
  208. //TODO check theme support
  209. } else {
  210. $message = sprintf(_w('Theme “%s” is for app “%s”, which is not installed in your Webasyst. Install the app, and upload theme once again.'),$id,$app_id);
  211. throw new waException($message);
  212. }
  213. }
  214. $wa_path = "wa-apps/{$app_id}/themes/{$id}";
  215. $wa_pattern = wa_make_pattern($wa_path);
  216. $file = reset($files);
  217. if(preg_match("@^{$wa_pattern}(/|$)@",$file['filename'])) {
  218. $extract_path = $wa_path;
  219. $extract_pattern = $wa_pattern;
  220. } else {
  221. $extract_path = $id;
  222. $extract_pattern = wa_make_pattern($id);
  223. if(!preg_match("@^{$extract_pattern}(/|$)@",$file['filename'])) {
  224. $extract_path = '';
  225. $extract_pattern = false;
  226. }
  227. }
  228. foreach($files as $file) {
  229. if($extract_pattern && !preg_match("@^{$extract_pattern}(/|$)@",$file['filename'])) {
  230. self::throwThemeException('UNEXPECTED_FILE_PATH',"{$file['filename']}. Expect files in [{$extract_path}] directory");
  231. } elseif(preg_match('@\.(php\d*|pl)@', $file['filename'],$matches)) {
  232. self::throwThemeException('UNEXPECTED_FILE_TYPE',$file['filename']);
  233. }
  234. }
  235. self::verify($id);
  236. self::protect($app_id);
  237. $target_path = wa()->getDataPath("themes/{$id}",true,$app_id,false);
  238. waFiles::delete($target_path);
  239. if ($extract_path && !$tar_object->extractModify($target_path, $extract_path)) {
  240. self::throwArchiveException('INTERNAL_ARCHIVE_ERROR');
  241. } elseif(!$tar_object->extract($target_path)) {
  242. self::throwArchiveException('INTERNAL_ARCHIVE_ERROR');
  243. }
  244. } catch (Exception $ex) {
  245. if(isset($target_path) && $target_path) {
  246. waFiles::delete($target_path, true);
  247. }
  248. throw $ex;
  249. }
  250. } else {
  251. self::throwArchiveException('UNSUPPORTED_ARCHIVE_TYPE');
  252. }
  253. $instance = new self($id,$app_id);
  254. $instance->check();
  255. return $instance;
  256. }
  257. private static function throwThemeException($code, $details = '')
  258. {
  259. $link = sprintf(_w('http://www.webasyst.com/framework/docs/site/themes/#%s'),$code);
  260. $message = $code.($details?", {$details}":'');
  261. throw new waException(sprintf(_w("Invalid theme archive structure (%s). <a href=\"%s\" target=\"_blank\">See help</a> for details"),$code,$link));
  262. }
  263. private static function throwArchiveException($code, $details = '')
  264. {
  265. $link = sprintf(_w('http://www.webasyst.com/framework/docs/site/themes/#%s'),$code);
  266. $message = $code.($details?", {$details}":'');
  267. throw new waException(sprintf(_w("Failed to extract files from theme archive (%s). <a href=\"%s\" target=\"_blank\">See help</a> for details"),$code,$link));
  268. }
  269. /**
  270. *
  271. * Compress theme into archive file
  272. * @param string $path target archive path
  273. * @param string $name archive filename
  274. * @return string arcive path
  275. */
  276. public function compress($path, $name = null)
  277. {
  278. if(!$name) {
  279. $name = "webasyst.{$this->app}.theme.{$this->id}.tar.gz";
  280. }
  281. $target_file = "{$path}/{$this->app}/{$name}";
  282. $autoload = waAutoload::getInstance();
  283. $autoload->add('Archive_Tar','wa-installer/lib/vendors/PEAR/Tar.php');
  284. $autoload->add('PEAR','wa-installer/lib/vendors/PEAR/PEAR.php');
  285. if(file_exists($this->path) && class_exists('Archive_Tar',true)) {
  286. waFiles::create($target_file);
  287. $tar_object = new Archive_Tar( $target_file, true );
  288. $tar_object->setIgnoreRegexp('@(\.(php\d?|svn|git|fw_|files\.md5$))@');
  289. $path = getcwd();
  290. chdir(dirname($this->path));
  291. if (!$tar_object->create('./'. basename($this->path) )) {
  292. waFiles::delete($target_file);
  293. }
  294. chdir($path);
  295. }
  296. return $target_file;
  297. }
  298. }
  299. //EOF