-
Notifications
You must be signed in to change notification settings - Fork 0
/
Folder.php
304 lines (279 loc) · 11.7 KB
/
Folder.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
<?php
namespace Sledgehammer\Mvc;
use Exception;
use Sledgehammer\Core\Base;
use Sledgehammer\Core\Url;
use Sledgehammer\Mvc\Component\HttpError;
use Sledgehammer\Mvc\Document\Redirect;
/**
* Superclasse van de Virtuele mappen.
* Door Folder creer je eenvoudig virtuele mappen en virtuele bestanden.
* Hierdoor heb je vrijheid in de paden die je gebruikt om de pagina's aan te duiden. I.p.v. "page.php?id=17" maak je "pages/introductie.html"
* tevens kun je viruele mappen nesten (Een virtuele map in een virtuele map) hierdoor kan een een hele map hergebuiken en parameterizeren.
*/
abstract class Folder extends Base implements Controller
{
/**
* Diepte van deze virtual folder.
* Als deze VirtualFolder de inhoud van de map "http://domain/folder/" afhandeld, dan $depth == 1.
* Een VirtualFolder van "http://domain/folder/subfolder/" heeft $depth == 2.
*
* @var int
*/
protected $depth;
/**
* Het aantal niveau's(submappen) dat door deze VirtualFolder wordt afgehandeld.
* Deze variabele wordt gebruikt om de $depth van de submap uit te rekenen.
* Dit is handig als je een andere VirtualFolder wilt gebruiken terwijl je zelf al meerdere submappen gebruikt.
* Als je je de $depth_increment op 0 zet, dan wordt de andere VirtualFolder niet als subfolder,maar als de dezelfde folder gebruikt.
*
* @var int
*/
protected $depthIncrement = 1;
/**
* automatisch gegenereerde array die bepaald of een methode via een url aangeroepen.
*
* @var array
*/
protected $publicMethods;
/**
* Bepaald of deze VirtualFolder bestandsnamen zonder extenties accepteerd.
* Als deze niet geaccepteerd worden(false), zal de bestandsnaam (via een redirect) omgezet worden naar een mapnaam.
*
* @var bool
*/
protected $handle_filenames_without_extension = false;
/**
* Deze virtuele map is een submap van ...
*
* @var Folder
*/
private $parent;
/**
* The current active VirtualFolder (Is used to detect the parent).
*
* @var Folder
*/
public static $current;
/**
* Constructor.
*/
public function __construct()
{
$methods = \Sledgehammer\get_public_methods($this);
foreach ($methods as $index => $method) {
if (substr($method, 0, 1) == '_') {
unset($methods[$index]); // Functies die beginnen met een "_" uit de publicMethods halen
}
}
$this->publicMethods = array_diff($methods, ['execute', 'getPath', 'file', 'folder', 'onFileNotFound', 'onFolderNotFound', 'getDocument']); // Prevent API methods from being called via a public url.
}
/**
* Aan de hand van de url de betreffende action functie aanroepen.
* Valt terug op file() en folder() functies, als de geen action functie gevonden wordt.
*
* @return Component
*/
public function generateContent()
{
$this->initDepth();
$url = Url::getCurrentURL();
$folders = $url->getFolders();
$filename = $url->getFilename();
$folder_count = count($folders);
if ($folder_count == $this->depth) {
$extension = \Sledgehammer\file_extension($filename, $file);
if ($extension === null && $this->handle_filenames_without_extension == false) { // Ongeldige bestandsnaam? (geen extentie)
error_log('filename without extension, redirecting to "'.$filename.'/"', E_NOTICE);
return new Redirect($filename.'/'); // Redirect naar dezelfde url, maar dan als mapnaam
}
if ($this->publicMethods === null) {
notice('Check if the parent::__construct() is called in '.get_class($this).'__construct()');
}
$function = str_replace('-', '_', $file);
if (substr($function, -7) != '_folder' && in_array($function, $this->publicMethods)) {
return $this->$function($extension); // Roept bijvoorbeeld de $this->index('html') functie aan.
}
return $this->file($filename);
}
if ($folder_count > $this->depth) {
if ($folder_count != ($this->depth + 1)) {
$filename = false; // Deze submap heeft nog 1 of meer submappen.
}
$folder = $folders[$this->depth];
$function = str_replace('-', '_', $folder).'_folder';
if (in_array($function, $this->publicMethods)) {
return $this->$function($filename); // Roept bijvoorbeeld de $this->fotos_folder('index.html') functie aan.
}
return $this->folder($folder, $filename);
}
warning('Not enough (virtual) subfolders in URI', 'VirtualFolder depth('.$this->depth.') exceeds maximum('.count($folders).')');
return $this->onFolderNotFound(); // @todo eigen event?
}
/**
* Het pad opvragen van deze VirtualFolder.
*
* @param bool $includeSubfolders De actieve submap(pen) aan het path toevoegen (mappen die door deze VirtualFolder worden afgehandeld)
*/
public function getPath($includeSubfolders = false)
{
$this->initDepth();
$folders = Url::getCurrentURL()->getFolders();
$path = '/';
for ($i = 0; $i < $this->depth; ++$i) {
$path .= $folders[$i].'/';
}
if ($includeSubfolders) {
for ($i = $this->depth; $i < ($this->depth + $this->depthIncrement); ++$i) {
if (empty($folders[$i])) {
break;
}
$path .= $folders[$i].'/';
}
}
return $path;
}
/**
* Een bestand(snaam) afhandelen.
*
* @param string $filename De bestandsnaam die in deze virtuele map word opgevraagd
*
* @return Component
*/
public function file($filename)
{
if ($filename == 'index.html') {
return new HttpError(403, ['notice' => [
'No index() method configured for '.get_class($this),
'override VirtualFolder->index() or VirtualFolder->file() in "'.get_class($this).'"',
]]);
}
return $this->onFileNotFound();
}
/**
* Een submap afhandelen.
*
* @param string $folder De submap die in deze virtuele map opgevraagd
* @param string|false $file Als er geen submap volgd, dan wordt $file de bestandsnaam binnen de map. Mocht je aan de hand van de mapnaam een nieuwe VirtualFolder starten, dan wordt de $file ook door de handle_file() afgehandeld.
*
* @return Component
*/
public function folder($folder)
{
return $this->onFolderNotFound();
}
/**
* Event dat getriggert wordt als een (virtuele) bestand niet gevonden wordt.
* Geeft deze of een parent van deze virtualfolder de mogenlijkheid om een custom actie uit te voeren.
*
* @return HttpError
*/
protected function onFileNotFound()
{
if ($this->parent !== null) {
return $this->parent->onFileNotFound();
}
$relativePath = substr(rawurldecode(Url::getCurrentURL()->path), strlen(\Sledgehammer\WEBPATH) - 1); // Relative path vanaf de WEBROOT
return new HttpError(404, ['notice' => 'HTTP[404] File "'.$relativePath.'" not found']);
}
/**
* Event/Action voor het afhandelen van niet bestaande (virtuele) mappen.
* Geeft deze of een parent van deze virtualfolder de mogenlijkheid om een custom actie uit te voeren.
*
* @return HttpError
*/
protected function onFolderNotFound()
{
if ($this->parent !== null) {
return $this->parent->onFolderNotFound();
}
$url = Url::getCurrentURL();
$relativePath = substr(rawurldecode($url->path), strlen(\Sledgehammer\WEBPATH) - 1); // Relative path vanaf de WEBROOT
$isFolder = (substr($relativePath, -1) == '/'); // Gaat de request om een folder?
if ($isFolder) {
$folder = $relativePath;
} else {
$folder = dirname($relativePath).'/';
}
// $publicFolder = array(APP_DIR.'/public'.$folder);
// $folders = explode('/', substr($folder, 1, -1));
// if (count($folders) != 0) {
// $module = $folders[0];
// $modules = Framework::getModules();
// if (isset($modules[$module])) {
// $publicFolder[] = $modules[$module]['path'].'public'.substr($folder, strlen($module) + 1);
// }
// }
// Zoek door de public mappen en kijk of de map/bestand bestaat.
$foundPublicFolder = false;
// foreach ($publicFolder as $folder) {
// if (is_dir($folder)) {
// $foundPublicFolder = $folder;
// if ($isFolder) {
// error_log('HTTP[403] Directory listing for "'.$url.'" not allowed');
//
// return new HttpError(403);
// }
// }
// $path = $isFolder ? $folder : $folder.basename($relativePath);
// if (file_exists($path)) {
// if (is_readable($path)) {
// return new HttpError(500, array('warning' => 'render_public_folder.php should have renderd the file: "'.$path.'"'));
// } else {
// return new HttpError(403, array('warning' => 'Permission denied for file "'.basename($path).'" in "'.dirname($path).'/"'));
// }
// }
// }
if ($foundPublicFolder) {
return new HttpError(404, ['notice' => [
'HTTP[404] File "'.basename($relativePath).'" not found in "'.dirname($relativePath).'/"',
'VirtualFolder "'.get_class(self::$current).'" doesn\'t handle the "'.basename(self::$current->getPath(true)).'" folder',
]]);
}
// Gaat om een bestand in een virtualfolder
return new HttpError(404, ['notice' => 'HTTP[404] VirtualFolder "'.get_class(self::$current).'" has no "'.basename(self::$current->getPath(true)).'" folder']);
}
/**
* De VirtualFolder van een bepaalde class opvragen die zich hoger in de hierarchie bevind.
*
* @return Folder
*/
public function &getParentByClass($class)
{
if (strtolower(get_class($this)) == strtolower($class)) { // Is dit de gespecifeerde virtualfolder?
return $this;
}
if ($this->parent === null) { // Is de virtualfolder niet gevonden in de hierarchie?
$message = ($class === null) ? 'VirtualFolder "'.get_class($this).'" has no parent' : 'VirtualFolder \''.$class.'\' is niet actief';
throw new Exception($message);
}
return $this->parent->getParentByClass($class);
}
/**
* Mits de $this->depth niet is ingesteld zal de waarde van $this->depth berekent worden.
* Hoe diep de handler genest is wordt aan de hand van de Parent->depth berekend.
*
* @return int
*/
private function initDepth()
{
if ($this->depth !== null) { // Is de diepte reeds ingesteld?
return;
}
if (isset(self::$current) == false) { // Gaat het om de eerste VirtualFolder (Website)
if (($this instanceof Website) == false) {
notice('VirtualFolder outside a Website object?');
}
self::$current = &$this; // De globale pointer laten verwijzen naar deze 'virtuele map'
if (defined('Sledgehammer\WEBPATH')) {
$this->depth = preg_match_all('/[^\/]+\//', \Sledgehammer\WEBPATH, $match);
} else {
$this->depth = 0;
}
return;
}
$this->parent = &self::$current;
self::$current = &$this; // De globale pointer laten verwijzen naar deze 'virtuele map'
$this->depth = $this->parent->depth + $this->parent->depthIncrement;
}
}