Base application
[infodrom.org/touren.infodrom.org] / core / application.class.php
1 <?php
2
3 class Application
4 {
5     protected static $instance = NULL;
6
7     public static function get()
8     {
9         if (is_null(static::$instance))
10             static::$instance = new Application();
11
12         return static::$instance;
13     }
14
15     public static function url($controller=false, $action=false, Tour $tour=NULL)
16     {
17         $app = Application::get();
18
19         if ($controller === false)
20             return $app->getBaseURL();
21
22         if ($action === false)
23             return $app->getBaseURL() . $controller . '/index';
24
25         if (is_null($tour))
26             return $app->getBaseURL() . $controller . '/' . $action;
27
28         return $app->getBaseURL() . $controller . '/' . $tour->get('key') . '/' . $action;
29     }
30
31     protected static $debug_file = false;
32     public static function debug($values)
33     {
34         if (!defined('DEBUG') || DEBUG !== true) return error_log('return');
35
36         if (!static::$debug_file) {
37             $dir = dirname(__DIR__) . '/tmp';
38             if (!is_dir($dir)) return;
39
40             static::$debug_file = fopen($dir . '/debug.log', 'a');
41         }
42
43         if (!static::$debug_file) return;
44
45         if (is_string($values))
46             $values = trim($values);
47         else
48             $values = var_export($values, true);
49
50         fputs(static::$debug_file, sprintf("%s %s - %s\n", date('Y-m-d H:i:s'), $_SESSION['email'], $values));
51     }
52
53     protected $_javascript = NULL;
54     protected $_styles = NULL;
55     protected $_request = NULL;
56     protected $_response = NULL;
57     protected $_base = NULL;
58     protected $_is_admin = NULL;
59
60     protected $_controller;
61     protected $_action;
62     protected $tour;
63
64     protected $db;
65
66     private function __construct()
67     {
68         global $db;
69
70         $this->initSession();
71
72         $this->_request = new Request();
73         $this->_response = new Response();
74
75         $dsn = sprintf('%s:host=%s;dbname=%s', DBDRIVER, DBHOST, DBNAME);
76         $db = new Database(DBDRIVER, DBHOST, DBNAME, DBUSER, DBPASS);
77         if (defined('MAIL_ERROR')) $db->setErrorMail(MAIL_ERROR);
78         $this->db = $db;
79
80         $this->setExceptionHandler();
81     }
82
83     public function getBaseDir()
84     {
85         return dirname(__DIR__) . '/';
86     }
87
88     public function getBaseURL()
89     {
90         if (is_null($this->_base)) {
91             if (strpos($this->getBaseDir(), $this->_request->getServer('DOCUMENT_ROOT')) !== 0)
92                 throw new Exception("Local directory doesn't match DOCUMENT_ROOT");
93
94             $this->_base = substr($this->getBaseDir(), strlen($this->_request->getServer('DOCUMENT_ROOT')));
95         }
96
97         return $this->_base;
98     }
99
100     protected function initSession()
101     {
102         if (!defined('SESSION_PATH')) define('SESSION_PATH', '/');
103         if (!defined('SESSION_LIFETIME')) define('SESSION_LIFETIME', 60*60*56);
104
105         session_name(SESSION_NAME);
106         session_set_cookie_params(SESSION_LIFETIME, SESSION_PATH);
107         session_start();
108         session_regenerate_id();
109     }
110
111     protected function setExceptionHandler()
112     {
113         set_exception_handler(function(Throwable $e){
114                 Application::debug($e->getTraceAsString());
115                 $app = Application::get();
116
117                 if ($app->getRequest()->isAjax()) {
118                     header('Content-type: application/json; charset="UTF-8"');
119                     echo json_encode(['status' => false,
120                                       'error' =>  $e->getMessage()]);
121                     exit();
122                 }
123
124                 $content = Template::render('page/error', ['title' => 'Es ist ein Problem aufgetreten',
125                                                            'text' => $e->getMessage()]);
126
127                 header('Content-type: text/html; charset="UTF-8"');
128                 echo Template::render('page/main', ['title' => TITLE,
129                                                     'pagetitle' => TITLE,
130                                                     'navigation' => '',
131                                                     'favicon' => FAVICON,
132                                                     'content' => $content,
133                                                     'styles' => Styles::instance()->toString(),
134                                                     'javascript' => JavaScript::instance()->toString()]);
135                 exit();
136             });
137     }
138
139     protected function addBaseFiles()
140     {
141         $this->addJavascript('lib/jquery-2.1.0.min.js');
142         $this->addJavascript('lib/bootstrap-4.3.1-dist/js/bootstrap.js');
143         $this->addJavascript('touren.js');
144         $this->addJavascriptCode(sprintf("var tour_base_url = '%s';", $this->getBaseURL()));
145         $this->addStyle('lib/bootstrap-4.3.1-dist/css/bootstrap.css');
146         $this->addStyle('touren.css');
147     }
148
149     public function addJavascript($path)
150     {
151         if (is_null($this->_javascript))
152             $this->_javascript = JavaScript::instance();
153
154         $this->_javascript->file($path);
155     }
156
157     public function addJavascriptCode($code)
158     {
159         if (is_null($this->_javascript))
160             $this->_javascript = JavaScript::instance();
161
162         $this->_javascript->add($code);
163     }
164
165     public function addStyle($path)
166     {
167         if (is_null($this->_styles))
168             $this->_styles = Styles::instance();
169
170         $this->_styles->file($path);
171     }
172
173     public function getRequest()
174     {
175         return $this->_request;
176     }
177
178     public function getResponse()
179     {
180         return $this->_response;
181     }
182
183     public function isAdmin()
184     {
185         if (is_null($this->_is_admin)) {
186             $this->_is_admin = $_SESSION['userid'] == 1;
187         }
188
189         return $this->_is_admin;
190     }
191
192     protected function route()
193     {
194         $controller_name = $this->_request->getControllerName() or 'index';
195         $this->_action = $this->_request->getActionName() or 'index';
196         $tour_name = $this->_request->getTourName() or NULL;
197
198         if (empty($controller_name)) $controller_name = 'index';
199         if (empty($this->_action))     $this->_action = 'index';
200
201         $controller_name = ucfirst($controller_name) . 'Controller';
202         $this->_controller = new $controller_name();
203
204         if ((empty($_SESSION) || empty($_SESSION['userid']))
205             && !in_array($this->_action, $this->_controller->allowUnauthenticated())) {
206             $this->_response->setLocation(sprintf('%sindex/login', $this->getBaseURL()));
207             return;
208         }
209
210         $this->_controller->setDatabase($this->db);
211         $this->_controller->setApplication($this);
212
213         if ($this->_request->isAjax())
214             $method = 'ajax' . ucfirst($this->_action);
215         else
216             $method = $this->_action . 'Action';
217
218         if (!method_exists($this->_controller, $method))
219             throw new Exception("Entry point for {$this->_action} does not exist");
220
221         if (!is_null($tour_name)) {
222             $this->tour = new Tour($tour_name, 'key');
223             if ($this->tour->id()) {
224                 $this->_controller->setTour($this->tour);
225                 $this->addJavascriptCode(sprintf("var tour_key = '%s';", $this->tour->get('key')));
226             }
227         }
228
229         if ($this->_request->isAjax()) {
230             $this->getResponse()->setType('application/json');
231             $this->_controller->$method($this->_request, $this->_response, $this->_request->getPost());
232         } else {
233             $this->_controller->$method($this->_request, $this->_response);
234         }
235     }
236
237     public function process()
238     {
239         if (!$this->_request->isAjax())
240             $this->addBaseFiles();
241
242         $this->route();
243
244         if ($this->getResponse()->getLocation()) {
245             header(sprintf('Location: %s', $this->getResponse()->getLocation()));
246             exit();
247         }
248
249         if ($this->getResponse()->getType() == 'application/json') {
250             $data = $this->getResponse()->getData();
251             if ($data === false) $data = ['status' => false];
252             if ($data === true)  $data = ['status' => true];
253             if (!array_key_exists('status', $data))
254                 $data['status'] = true;
255
256             header('Content-type: application/json; charset="UTF-8"');
257             echo json_encode($data);
258             exit();
259         }
260
261         if ($this->getResponse()->getType() != 'text/html') {
262             header(sprintf('Content-type: %s; charset="UTF-8"'), $this->getResponse()->getType());
263             echo $this->getResponse()->getData();
264             exit();
265         }
266
267         $nav = $this->_controller->getNavigation();
268         if (is_null($nav) || $nav === false || !count($nav))
269             $navigation = '';
270         else
271             $navigation = Template::render('page/navigation', ['list' => $nav,
272                                                                'tour' => $this->tour]);
273
274         header('Content-type: text/html; charset="UTF-8"');
275         echo Template::render('page/main', ['title' => TITLE,
276                                             'pagetitle' => TITLE,
277                                             'navigation' => $navigation,
278                                             'favicon' => FAVICON,
279                                             'content' => $this->getResponse()->getData(),
280                                             'styles' => !is_null($this->_styles) ? $this->_styles->toString() : '',
281                                             'javascript' => !is_null($this->_javascript) ? $this->_javascript->toString() : '']);
282
283         exit();
284     }
285 }