PageRenderTime 55ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/panel/app/src/panel.php

https://gitlab.com/gricelya/rental
PHP | 559 lines | 356 code | 139 blank | 64 comment | 60 complexity | eb05b4df98e80727426b2d5ee6db5d9f MD5 | raw file
  1. <?php
  2. namespace Kirby;
  3. use A;
  4. use C;
  5. use Collection;
  6. use Detect;
  7. use Dir;
  8. use ErrorController;
  9. use Exception;
  10. use F;
  11. use Header;
  12. use Kirby;
  13. use L;
  14. use Obj;
  15. use R;
  16. use Response;
  17. use Router;
  18. use Server;
  19. use S;
  20. use Str;
  21. use Toolkit;
  22. use Tpl;
  23. use Url;
  24. use Kirby\Panel\Installer;
  25. use Kirby\Panel\Form;
  26. use Kirby\Panel\Models\Site;
  27. use Kirby\Panel\Translation;
  28. use Kirby\Panel\Models\User\Blueprint as UserBlueprint;
  29. use Kirby\Panel\Models\Page\Blueprint as PageBlueprint;
  30. class Panel {
  31. static public $version = '2.3.1';
  32. // minimal requirements
  33. static public $requires = array(
  34. 'php' => '5.4.0',
  35. 'toolkit' => '2.2.2',
  36. 'kirby' => '2.2.1'
  37. );
  38. static public $instance;
  39. public $kirby;
  40. public $site;
  41. public $path;
  42. public $roots;
  43. public $routes = array();
  44. public $router = null;
  45. public $route = null;
  46. public $translation = null;
  47. public $translations = null;
  48. public $csrf = null;
  49. static public function instance() {
  50. return static::$instance;
  51. }
  52. static public function version() {
  53. return static::$version;
  54. }
  55. public function defaults() {
  56. return array(
  57. 'panel.language' => 'en',
  58. 'panel.stylesheet' => null,
  59. 'panel.kirbytext' => true,
  60. 'panel.session.timeout' => 1440,
  61. 'panel.session.lifetime' => 0,
  62. 'panel.info.license' => true,
  63. 'panel.info.versions' => true,
  64. 'panel.widgets' => array(
  65. 'pages' => true,
  66. 'site' => true,
  67. 'account' => true,
  68. 'history' => true
  69. ),
  70. );
  71. }
  72. public function __construct($kirby, $root) {
  73. // check requirements
  74. $this->requirements();
  75. // store the instance as a singleton
  76. static::$instance = $this;
  77. $this->kirby = $kirby;
  78. $this->roots = new \Kirby\Panel\Roots($this, $root);
  79. $this->urls = new \Kirby\Panel\Urls($this, $root);
  80. // add the panel default options
  81. $this->kirby->options = array_merge($this->defaults(), $this->kirby->options);
  82. // setup the blueprints roots
  83. UserBlueprint::$root = $this->kirby->roots()->blueprints() . DS . 'users';
  84. PageBlueprint::$root = $this->kirby->roots()->blueprints();
  85. // load the site object
  86. $this->site = $this->site();
  87. // setup the session
  88. $this->session();
  89. // setup the multilang site stuff
  90. $this->multilang();
  91. // load all Kirby extensions (methods, tags, smartypants)
  92. $this->kirby->extensions();
  93. $this->kirby->plugins();
  94. // setup the form plugin
  95. form::$root = array(
  96. 'default' => $this->roots->fields,
  97. 'custom' => $this->kirby->roots()->fields()
  98. );
  99. // force ssl if set in config
  100. if($this->kirby->option('ssl') and !r::secure()) {
  101. // rebuild the current url with https
  102. go(url::build(array('scheme' => 'https')));
  103. }
  104. // load all available routes
  105. $this->routes = array_merge($this->routes, require($this->roots->config . DS . 'routes.php'));
  106. // start the router
  107. $this->router = new Router($this->routes);
  108. // register router filters
  109. $this->router->filter('auth', function() use($kirby) {
  110. try {
  111. $user = panel()->user();
  112. } catch(Exception $e) {
  113. panel()->redirect('login');
  114. }
  115. });
  116. // check for a completed installation
  117. $this->router->filter('isInstalled', function() use($kirby) {
  118. $installer = new Installer();
  119. if(!$installer->isCompleted()) {
  120. panel()->redirect('install');
  121. }
  122. });
  123. // check for valid csrf tokens. Can be used for get requests
  124. // since all post requests are blocked anyway
  125. $this->router->filter('csrf', function() {
  126. panel()->csrfCheck();
  127. });
  128. // csrf protection for every post request
  129. if(r::is('post')) {
  130. $this->csrfCheck();
  131. }
  132. }
  133. public function session() {
  134. // setup the session
  135. s::$timeout = $this->kirby->option('panel.session.timeout', 120);
  136. s::$cookie['lifetime'] = $this->kirby->option('panel.session.lifetime', 0);
  137. // start the session
  138. s::start();
  139. }
  140. public function requirements() {
  141. if(!version_compare(PHP_VERSION, static::$requires['php'], '>=')) {
  142. throw new Exception('Your PHP version is too old. Please upgrade to ' . static::$requires['php'] . ' or newer.');
  143. }
  144. if(!detect::mbstring()) {
  145. throw new Exception('The mbstring extension must be installed');
  146. }
  147. if(!version_compare(toolkit::version(), static::$requires['toolkit'], '>=')) {
  148. throw new Exception('Your Toolkit version is too old. Please upgrade to ' . static::$requires['toolkit'] . ' or newer.');
  149. }
  150. if(!version_compare(kirby::version(), static::$requires['kirby'], '>=')) {
  151. throw new Exception('Your Kirby version is too old. Please upgrade to ' . static::$requires['kirby'] . ' or newer.');
  152. }
  153. }
  154. public function csrf() {
  155. if(!is_null($this->csrf)) return $this->csrf;
  156. // see if there's a token in the session
  157. $token = s::get('csrf');
  158. // create a new csrf token if not available yet
  159. if(str::length($token) !== 32) {
  160. $token = str::random(32);
  161. }
  162. // store the new token in the session
  163. s::set('csrf', $token);
  164. // create a new csrf token
  165. return $this->csrf = $token;
  166. }
  167. public function csrfCheck() {
  168. $csrf = get('csrf');
  169. if(empty($csrf) or $csrf !== s::get('csrf')) {
  170. try {
  171. $this->user()->logout();
  172. } catch(Exception $e) {}
  173. $this->redirect('login');
  174. }
  175. }
  176. public function kirby() {
  177. return $this->kirby;
  178. }
  179. public function site() {
  180. // return the site object if it has already been stored
  181. if(!is_null($this->site)) return $this->site;
  182. // load the original site first to load all branch files
  183. $this->kirby->site();
  184. // create a new panel site object
  185. return $this->site = new Site($this->kirby);
  186. }
  187. public function multilang() {
  188. if(!$this->site->multilang()) {
  189. $language = null;
  190. } else if($language = get('language') or $language = s::get('lang')) {
  191. // $language is already set
  192. } else {
  193. $language = null;
  194. }
  195. // set the path and lang for the original site object
  196. $this->kirby->site()->visit('/', $language);
  197. // set the path and lang for the panel site object
  198. $this->site->visit('/', $language);
  199. // store the language code
  200. if($this->site->multilang()) {
  201. s::set('lang', $this->site->language()->code());
  202. }
  203. }
  204. public function page($id) {
  205. if($page = (empty($id) or $id == '/') ? $this->site() : $this->site()->find($id)) {
  206. return $page;
  207. } else {
  208. throw new Exception(l('pages.error.missing'));
  209. }
  210. }
  211. public function roots() {
  212. return $this->roots;
  213. }
  214. public function routes($routes = null) {
  215. if(is_null($routes)) return $this->routes;
  216. return $this->routes = array_merge($this->routes, (array)$routes);
  217. }
  218. public function urls() {
  219. return $this->urls;
  220. }
  221. public function form($id, $data = array(), $submit = null) {
  222. if(file_exists($id)) {
  223. $file = $id;
  224. } else {
  225. $file = $this->roots->forms . DS . $id . '.php';
  226. }
  227. if(!file_exists($file)) {
  228. throw new Exception(l('form.error.missing'));
  229. }
  230. $callback = require($file);
  231. if(!is_callable($callback)) {
  232. throw new Exception(l('form.construct.error.invalid'));
  233. }
  234. $form = call($callback, $data);
  235. if(is_callable($submit)) {
  236. $form->on('submit', $submit);
  237. }
  238. return $form;
  239. }
  240. public function translations() {
  241. if(!is_null($this->translations)) return $this->translations;
  242. $this->translations = new Collection;
  243. foreach(dir::read($this->roots()->translations()) as $dir) {
  244. // filter out everything but directories
  245. if(!is_dir($this->roots()->translations() . DS . $dir)) continue;
  246. // create the translation object
  247. $translation = new Translation($this, $dir);
  248. $this->translations->append($translation->code(), $translation);
  249. }
  250. return $this->translations;
  251. }
  252. public function translation() {
  253. if(!is_null($this->translation)) return $this->translation;
  254. // get the default language code from the options
  255. $lang = $this->kirby()->option('panel.language', 'en');
  256. $user = $this->site()->user();
  257. if($user && $user->language()) {
  258. $lang = $user->language();
  259. }
  260. return $this->translation = new Translation($this, $lang);
  261. }
  262. public function language() {
  263. return $this->translation();
  264. }
  265. public function direction() {
  266. return $this->translation()->direction();
  267. }
  268. public function launch($path = null) {
  269. // set the timezone for all date functions
  270. date_default_timezone_set($this->kirby->options['timezone']);
  271. // load the current translation
  272. $this->translation()->load();
  273. $this->path = $this->kirby->path();
  274. $this->route = $this->router->run($this->path);
  275. // set the current url
  276. $this->urls->current = rtrim($this->urls->index() . '/' . $this->path, '/');
  277. ob_start();
  278. try {
  279. // react on invalid routes
  280. if(!$this->route) {
  281. throw new Exception(l('routes.error.invalid'));
  282. }
  283. if(is_callable($this->route->action())) {
  284. $response = call($this->route->action(), $this->route->arguments());
  285. } else {
  286. $response = $this->response();
  287. }
  288. } catch(Exception $e) {
  289. require_once($this->roots->controllers . DS . 'error.php');
  290. $controller = new ErrorController();
  291. $response = $controller->index($e->getMessage(), $e);
  292. }
  293. // check for a valid response object
  294. if(is_a($response, 'Response')) {
  295. echo $response;
  296. } else {
  297. echo new Response($response);
  298. }
  299. ob_end_flush();
  300. }
  301. public function response() {
  302. // let's find the controller and controller action
  303. $controllerParts = str::split($this->route->action(), '::');
  304. $controllerUri = $controllerParts[0];
  305. $controllerAction = $controllerParts[1];
  306. $controllerFile = $this->roots->controllers . DS . strtolower(str_replace('Controller', '', $controllerUri)) . '.php';
  307. $controllerName = basename($controllerUri);
  308. // react on missing controllers
  309. if(!file_exists($controllerFile)) {
  310. throw new Exception(l('controller.error.invalid'));
  311. }
  312. // load the controller
  313. require_once($controllerFile);
  314. // check for the called action
  315. if(!method_exists($controllerName, $controllerAction)) {
  316. throw new Exception(l('controller.error.action'));
  317. }
  318. // run the controller
  319. $controller = new $controllerName;
  320. // call the action and pass all arguments from the router
  321. return call(array($controller, $controllerAction), $this->route->arguments());
  322. }
  323. public function license() {
  324. $key = c::get('license');
  325. $type = 'trial';
  326. /**
  327. * Hey stranger,
  328. *
  329. * So this is the mysterious place where the panel checks for
  330. * valid licenses. As you can see, this is not reporting
  331. * back to any server and the license keys are rather simple to
  332. * hack. If you really feel like removing the warning in the panel
  333. * or tricking Kirby into believing you bought a valid license even
  334. * if you didn't, go for it! But remember that literally thousands of
  335. * hours of work have gone into Kirby in order to make your
  336. * life as a developer, designer, publisher, etc. easier. If this
  337. * doesn't mean anything to you, you are probably a lost case anyway.
  338. *
  339. * Have a great day!
  340. *
  341. * Bastian
  342. */
  343. if(str::startsWith($key, 'K2-PRO') and str::length($key) == 39) {
  344. $type = 'Kirby 2 Professional';
  345. } else if(str::startsWith($key, 'K2-PERSONAL') and str::length($key) == 44) {
  346. $type = 'Kirby 2 Personal';
  347. } else if(str::startsWith($key, 'MD-') and str::length($key) == 35) {
  348. $type = 'Kirby 1';
  349. } else if(str::startsWith($key, 'BETA') and str::length($key) == 9) {
  350. $type = 'Kirby 1';
  351. } else if(str::length($key) == 32) {
  352. $type = 'Kirby 1';
  353. } else {
  354. $key = null;
  355. }
  356. return new Obj(array(
  357. 'key' => $key,
  358. 'local' => $this->isLocal(),
  359. 'type' => $type,
  360. ));
  361. }
  362. public function isLocal() {
  363. $localhosts = array('::1', '127.0.0.1', '0.0.0.0');
  364. return (in_array(server::get('SERVER_ADDR'), $localhosts) || server::get('SERVER_NAME') == 'localhost');
  365. }
  366. public function notify($text) {
  367. s::set('message', array(
  368. 'type' => 'notification',
  369. 'text' => $text,
  370. ));
  371. }
  372. public function alert($text) {
  373. s::set('message', array(
  374. 'type' => 'error',
  375. 'text' => $text,
  376. ));
  377. }
  378. public function redirect($obj = '/', $action = false, $force = false) {
  379. if($force === false and $redirect = get('_redirect')) {
  380. $url = purl($redirect);
  381. } else {
  382. $url = purl($obj, $action);
  383. }
  384. if(r::ajax()) {
  385. $user = $this->site()->user();
  386. die(response::json(array(
  387. 'direction' => $this->direction(),
  388. 'user' => $user ? $user->username() : false,
  389. 'url' => $url
  390. )));
  391. } else {
  392. go($url);
  393. }
  394. }
  395. public function users() {
  396. return $this->site()->users();
  397. }
  398. public function user($username = null) {
  399. if($user = $this->site()->user($username)) {
  400. return $user;
  401. } else {
  402. throw new Exception(l('users.error.missing'));
  403. }
  404. }
  405. public static function fatal($e, $root) {
  406. $message = $e->getMessage() ? $e->getMessage() : 'Error without a useful message :(';
  407. $where = implode('<br>', [
  408. '',
  409. '',
  410. '<b>It happened here:</b>',
  411. 'File: <i>' . str_replace($root, '/panel', $e->getFile()) . '</i>',
  412. 'Line: <i>' . $e->getLine() . '</i>'
  413. ]);
  414. // load the fatal screen
  415. return tpl::load($root . DS . 'app' . DS . 'layouts' . DS . 'fatal.php', [
  416. 'css' => url::index() . '/assets/css/panel.css',
  417. 'content' => $message . $where
  418. ]);
  419. }
  420. }