source: branches/MootoolsFileManager-Update/plugins/MootoolsFileManager/mootools-filemanager/Demos/FM-common.php @ 1302

Last change on this file since 1302 was 1302, checked in by gogo, 9 years ago

Updates to http://www.github.com/sleemanj/mootools-filemanager from GerHoblett?

http://www.github.com/GerHoblett/

Changes to said updates by gogo (sleemanj @ github)

Modifications to MootoolsFileManager? to work with those updates, some courtesy of GerHoblett?, some sleemanj

GerHoblett? provided a large diff which accomplished the goal in a quite different way. It has merit, however I have opted for a less-affecting path in so far as Xinha's "way" is concerned, namely, not splitting the config for a single plugin into several calls to backend config passing functions which seemed a little cumbersome.

Instead I take the option of using POST to send backend data around, at the minor expense of an extra round trip when displaying thumbnails (for each one). This could be reduced by checking for thumbnail existence and returning the thumbnail name directly in "onView" rather than the backend request to generate said thumbnail.

Still to do, is to make the preview pane thumbnail also work.


File size: 18.3 KB
Line 
1<?php
2
3if (!defined('FILEMANAGER_CODE')) { header('HTTP/1.0 403 Forbidden', true, 403); die('illegal entry point'); }
4
5
6/*
7 * Set to nonzero, e.g. 01, when your site uses mod_alias, mod_vhost_alias or any other form of path aliasing, where
8 * one or more of these assumptions do not apply any longer as they would for a 'regular' site:
9 *
10 * - SERVER['DOCUMENT_ROOT'] correctly points at the physical filesystem path equivalent of the '/' URI path (~ 'http://your-site.com/')
11 *
12 * - every subdirectory of SERVER['DOCUMENT_ROOT'] is also a subdirectory in URI space, i.e. URI path '/media/Files/'
13 *   (~ 'http://your-site.com/media/Files/') would point at the physical filesystem path SERVER['DOCUMENT_ROOT'].'/media/Files/'
14 *
15 * Edit the 'Aliases' sub-array below to mimic your local site setup; see the notes there for a few hints.
16 */
17if (!defined('SITE_USES_ALIASES')) define('SITE_USES_ALIASES', 0);
18
19
20
21if (!defined('DEVELOPMENT')) define('DEVELOPMENT', 01);   // set to 01 / 1 / nonzero value to enable logging of each incoming event request.
22
23
24
25if (!SITE_USES_ALIASES)
26{
27        require_once(str_replace('\\', '/', dirname(__FILE__)) . '/../Assets/Connector/FileManager.php');
28}
29else
30{
31        // you don't need the additional sophistication of this one when you don't need path mapping support
32        require_once(str_replace('\\', '/', dirname(__FILE__)) . '/../Assets/Connector/FMgr4Alias.php');
33}
34
35
36
37
38
39
40
41
42
43/**
44defines for dump_request_to_logfile():
45*/
46define('DUMP2LOG_SERVER_GLOBALS',  0x0001);
47define('DUMP2LOG_ENV_GLOBALS',     0x0002);
48define('DUMP2LOG_SESSION_GLOBALS', 0x0004);
49define('DUMP2LOG_POST_GLOBALS',    0x0008);
50define('DUMP2LOG_GET_GLOBALS',     0x0010);
51define('DUMP2LOG_REQUEST_GLOBALS', 0x0020);
52define('DUMP2LOG_FILES_GLOBALS',   0x0040);
53define('DUMP2LOG_COOKIE_GLOBALS',  0x0080);
54define('DUMP2LOG_STACKTRACE',      0x0400);
55
56define('DUMP2LOG_SORT',                            0x0100000);
57define('DUMP2LOG_FORMAT_AS_HTML',                  0x0400000);
58define('DUMP2LOG_WRITE_TO_FILE',                   0x0800000);
59define('DUMP2LOG_WRITE_TO_STDOUT',                 0x1000000);
60
61
62
63
64
65
66
67
68
69
70
71/*
72Derived from code by phella.net:
73
74  http://nl3.php.net/manual/en/function.var-dump.php
75*/
76function var_dump_ex($value, $level = 0, $sort_before_dump = 0, $show_whitespace = true)
77{
78        if ($level == -1)
79        {
80                $trans = array();
81                if ($show_whitespace)
82                {
83                        $trans[' '] = '&there4;';
84                        $trans["\t"] = '&rArr;';
85                        $trans["\n"] = '&para;';
86                        $trans["\r"] = '&lArr;';
87                        $trans["\0"] = '&oplus;';
88                }
89                return strtr(htmlspecialchars($value, ENT_COMPAT, 'UTF-8'), $trans);
90        }
91
92        $rv = '';
93        if ($level == 0)
94        {
95                $rv .= '<pre>';
96        }
97        $type = gettype($value);
98        $rv .= $type;
99
100        switch ($type)
101        {
102        case 'string':
103                $rv .= '(' . strlen($value) . ')';
104                $value = var_dump_ex($value, -1, $show_whitespace);
105                break;
106
107        case 'boolean':
108                $value = ($value ? 'true' : 'false');
109                break;
110
111        case 'object':
112                $props = get_object_vars($value);
113                if ($sort_before_dump > $level)
114                {
115                        ksort($props);
116                }
117                $rv .= '(' . count($props) . ') <u>' . get_class($value) . '</u>';
118                foreach($props as $key => $val)
119                {
120                        $rv .= "\n" . str_repeat("\t", $level + 1) . var_dump_ex($key, -1, 0, $show_whitespace) . ' => ';
121                        $rv .= var_dump_ex($value->{$key}, $level + 1, $sort_before_dump, $show_whitespace);
122                }
123                $value = '';
124                break;
125
126        case 'array':
127                if ($sort_before_dump > $level)
128                {
129                        $value = array_merge($value); // fastest way to clone the input array
130                        ksort($value);
131                }
132                $rv .= '(' . count($value) . ')';
133                foreach($value as $key => $val)
134                {
135                        $rv .= "\n" . str_repeat("\t", $level + 1) . var_dump_ex($key, -1, 0, $show_whitespace) . ' => ';
136                        $rv .= var_dump_ex($val, $level + 1, $sort_before_dump, $show_whitespace);
137                }
138                $value = '';
139                break;
140
141        default:
142                break;
143        }
144        $rv .= ' <b>' . $value . '</b>';
145        if ($level == 0)
146        {
147                $rv .= '</pre>';
148        }
149        return $rv;
150}
151
152
153
154
155/**
156 * Generate a dump of the optional $extra values and/or the global variables $ccms[], $cfg[] and the superglobals.
157 *
158 * @param array $filename_options (optional) specifies a few pieces of the filename which will be generated to write
159 *                                the dump to:
160 *
161 *                                'namebase': the leading part of the filename,
162 *                                'origin-section': follows the timestamp encoded in the filename,
163 *                                'extension': the desired filename extension (default: 'html' for HTML dumps, 'log' for plain dumps)
164 *
165 * @return the generated dump in the format and carrying the content as specified by the $dump_options.
166 */
167define('__DUMP2LOG_DEFAULT_OPTIONS', -1 ^ DUMP2LOG_WRITE_TO_STDOUT);
168function dump_request_to_logfile($extra = null, $dump_options = __DUMP2LOG_DEFAULT_OPTIONS, $filename_options = null)
169{
170        global $_SERVER;
171        global $_ENV;
172        global $_COOKIE;
173        global $_SESSION;
174        static $sequence_number;
175
176        if (!$sequence_number)
177        {
178                $sequence_number = 1;
179        }
180        else
181        {
182                $sequence_number++;
183        }
184
185        $sorting = ($dump_options & DUMP2LOG_SORT);
186        $show_WS = ($dump_options & DUMP2LOG_FORMAT_AS_HTML);
187
188        $rv = '<html><body>';
189
190        if (!empty($_SESSION['dbg_last_dump']) && ($dump_options & DUMP2LOG_FORMAT_AS_HTML))
191        {
192                $rv .= '<p><a href="' . $_SESSION['dbg_last_dump'] . '">Go to previous dump</a></p>' ."\n";
193        }
194
195        if (!empty($extra))
196        {
197                $rv .= '<h1>EXTRA</h1>';
198                $rv .= "<pre>";
199                $rv .= var_dump_ex($extra, 0, $sorting, $show_WS);
200                $rv .= "</pre>";
201        }
202
203        if ($dump_options & DUMP2LOG_ENV_GLOBALS)
204        {
205                $rv .= '<h1>$_ENV</h1>';
206                $rv .= "<pre>";
207                $rv .= var_dump_ex($_ENV, 0, $sorting, $show_WS);
208                $rv .= "</pre>";
209        }
210        if ($dump_options & DUMP2LOG_SESSION_GLOBALS)
211        {
212                $rv .= '<h1>$_SESSION</h1>';
213                $rv .= "<pre>";
214                $rv .= var_dump_ex($_SESSION, 0, $sorting, $show_WS);
215                $rv .= "</pre>";
216        }
217        if ($dump_options & DUMP2LOG_POST_GLOBALS)
218        {
219                $rv .= '<h1>$_POST</h1>';
220                $rv .= "<pre>";
221                $rv .= var_dump_ex($_POST, 0, $sorting, $show_WS);
222                $rv .= "</pre>";
223        }
224        if ($dump_options & DUMP2LOG_GET_GLOBALS)
225        {
226                $rv .= '<h1>$_GET</h1>';
227                $rv .= "<pre>";
228                $rv .= var_dump_ex($_GET, 0, $sorting, $show_WS);
229                $rv .= "</pre>";
230        }
231        if ($dump_options & DUMP2LOG_FILES_GLOBALS)
232        {
233                $rv .= '<h1>$_FILES</h1>';
234                $rv .= "<pre>";
235                $rv .= var_dump_ex($_FILES, 0, $sorting, $show_WS);
236                $rv .= "</pre>";
237        }
238        if ($dump_options & DUMP2LOG_COOKIE_GLOBALS)
239        {
240                $rv .= '<h1>$_COOKIE</h1>';
241                $rv .= "<pre>";
242                $rv .= var_dump_ex($_COOKIE, 0, $sorting, $show_WS);
243                $rv .= "</pre>";
244        }
245        if ($dump_options & DUMP2LOG_REQUEST_GLOBALS)
246        {
247                $rv .= '<h1>$_REQUEST</h1>';
248                $rv .= "<pre>";
249                $rv .= var_dump_ex($_REQUEST, 0, $sorting, $show_WS);
250                $rv .= "</pre>";
251        }
252
253        if ($dump_options & DUMP2LOG_SERVER_GLOBALS)
254        {
255                $rv .= '<h1>$_SERVER</h1>';
256                $rv .= "<pre>";
257                $rv .= var_dump_ex($_SERVER, 0, $sorting, $show_WS);
258                $rv .= "</pre>";
259        }
260
261        if ($dump_options & DUMP2LOG_STACKTRACE)
262        {
263                $st = debug_backtrace(false);
264                $rv .= '<h1>Stack Trace:</h1>';
265                $rv .= "<pre>";
266                $rv .= var_dump_ex($st, 0, 0, $show_WS);
267                $rv .= "</pre>";
268        }
269
270        $rv .= '</body></html>';
271
272        $tstamp = date('Y-m-d.His') . '.' . sprintf('%07d', fmod(microtime(true), 1) * 1E6);
273
274        $filename_options = array_merge(array(
275                        'namebase'       => 'LOG-',
276                        'origin-section' => substr($_SERVER['REQUEST_URI'], 0, -42),
277                        'extension'      => (($dump_options & DUMP2LOG_FORMAT_AS_HTML) ? 'html' : 'log')
278                ), (is_array($filename_options) ? $filename_options : array()));
279
280        $fname = $filename_options['namebase'] . $tstamp . '.' . sprintf('%03u', $sequence_number) . '-' . $filename_options['origin-section'];
281        $fname = substr(preg_replace('/[^A-Za-z0-9_.-]+/', '_', $fname), 0, 46) . '.' . substr(preg_replace('/[^A-Za-z0-9_.-]+/', '_', $filename_options['extension']), 0, 9);    // make suitable for filesystem
282        if (isset($_SESSION))
283        {
284                $_SESSION['dbg_last_dump'] = $fname;
285        }
286
287        if (!($dump_options & DUMP2LOG_FORMAT_AS_HTML))
288        {
289                $rv = preg_replace('/^.*?<body>(.+)<\/body>.*?$/sD', '\\1', $rv);
290
291                $trans['<h1>'] = "\n\n*** ";
292                $trans['</h1>'] = " ***\n";
293                $rv = strtr($rv, $trans);
294
295                $rv = html_entity_decode(strip_tags($rv), ENT_NOQUOTES, 'UTF-8');
296        }
297
298        if ($dump_options & DUMP2LOG_WRITE_TO_FILE)
299        {
300                $fname = str_replace('\\', '/', dirname(__FILE__)) . '/' . $fname;
301
302                if (@file_put_contents($fname, $rv) === false)
303                {
304                        throw new Exception('b0rk at ' . $fname);
305                }
306        }
307
308        if ($dump_options & DUMP2LOG_FORMAT_AS_HTML)
309        {
310                $rv = preg_replace('/^.*?<body>(.+)<\/body>.*?$/sD', '\\1', $rv);
311        }
312
313        if ($dump_options & DUMP2LOG_WRITE_TO_STDOUT)
314        {
315                echo $rv;
316        }
317
318        return array('filename' => $fname, 'content' => $rv);
319}
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335/**
336 * dumper useful in development
337 */
338function FM_vardumper($mgr = null, $action = null, $info = null, $extra = null)
339{
340        if (DEVELOPMENT)
341        {
342                if ($mgr)
343                        $settings = $mgr->getSettings();
344                else
345                        $settings = null;
346
347                //$mimetdefs = $mgr->getMimeTypeDefinitions();
348
349                // log request data:
350                $data = array(
351                                "FileManager::action" => $action,
352                                "FileManager::info" => $info,
353                                "FileManager::settings" => $settings
354                        );
355                if (!empty($extra))
356                {
357                        $data['extra'] = $extra;
358                }
359
360                dump_request_to_logfile($data, (0
361                                | DUMP2LOG_SERVER_GLOBALS
362                                | DUMP2LOG_ENV_GLOBALS
363                                | DUMP2LOG_SESSION_GLOBALS
364                                | DUMP2LOG_POST_GLOBALS
365                                | DUMP2LOG_GET_GLOBALS
366                                | DUMP2LOG_REQUEST_GLOBALS
367                                | DUMP2LOG_FILES_GLOBALS
368                                | DUMP2LOG_COOKIE_GLOBALS
369                                | DUMP2LOG_STACKTRACE
370                                | DUMP2LOG_SORT
371                                //| DUMP2LOG_FORMAT_AS_HTML
372                                | DUMP2LOG_WRITE_TO_FILE
373                                //| DUMP2LOG_WRITE_TO_STDOUT
374                        ), array(
375                                'origin-section' => basename($_SERVER['REQUEST_URI']) . '-' . $action
376                        ));
377        }
378}
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394/**
395 * Just a simple wrapper around the FileManager class constructor. Assumes a series of option defaults for the Demos,
396 * which you may override by providing your own in $options.
397 *
398 * Returns an instantiated FileManager instance, which you can use to process the incoming request.
399 */
400function mkNewFileManager($options = null)
401{
402        $Aliases = array();
403
404        if (SITE_USES_ALIASES)
405        {
406                //
407                // http://httpd.apache.org/docs/2.2/mod/mod_alias.html -- we emulate the Alias statement. Sort of.
408                //
409                // In principle each entry in this array should copy a Alias/VhostAlias/... web server configuration line.
410                //
411                // When filesystem paths are 'real time constructed', e.g. through complex regex manipulations, you will need
412                // to derive your own class from FileManagerWithAliasSupport or FileManager and implement/override
413                // the offending member functions in there, using the FileManagerWithAliasSupport implementation as a guide.
414                //
415                // NOTE that the above caveat applies to very complex rigs only, e.g. where a single URL points at different
416                //      physical locations, depending on who's logged in, or where the request is originating from.
417                //
418                //      As long as you can construct a static URI path to disk mapping, you are good to go using the Aliases[]
419                //      array below!
420                //
421                $Aliases = array(
422                                '/c/lib/includes/js/mootools-filemanager/Demos/Files/alias' => "D:/xxx",
423                                '/c/lib/includes/js/mootools-filemanager/Demos/Files/d' => "D:/xxx.tobesorted",
424                                '/c/lib/includes/js/mootools-filemanager/Demos/Files/u' => "D:/websites-uploadarea",
425
426                                '/c/lib/includes/js/mootools-filemanager/Demos/Files' => "D:/experiment"
427                        );
428        }
429
430        $options = array_merge(array(
431                        //'directory' => $fm_basedir . 'Files/',   // absolute paths: as the relative ones, they sit in URI space, i.e. assume DocumentRoot is root '/'
432
433                        'directory' => 'Files/',                   // relative paths: are relative to the URI request script path, i.e. dirname(__FILE__) or rather: $_SERVER['SCRIPT_NAME']
434                        'thumbnailPath' => 'Files/Thumbnails/',
435                        'assetBasePath' => '../Assets',
436                        'chmod' => 0777,
437                        //'maxUploadSize' => 1024 * 1024 * 5,
438                        //'upload' => false,
439                        //'destroy' => false,
440                        //'create' => false,
441                        //'move' => false,
442                        //'download' => false,
443                        //'filter' => 'image/',
444                        'allowExtChange' => true,                  // allow file name extensions to be changed; the default however is: NO (FALSE)
445                        'UploadIsAuthorized_cb' => 'FM_IsAuthorized',
446                        'DownloadIsAuthorized_cb' => 'FM_IsAuthorized',
447                        'CreateIsAuthorized_cb' => 'FM_IsAuthorized',
448                        'DestroyIsAuthorized_cb' => 'FM_IsAuthorized',
449                        'MoveIsAuthorized_cb' => 'FM_IsAuthorized',
450                        'ViewIsAuthorized_cb' => 'FM_IsAuthorized',
451                        'DetailIsAuthorized_cb' => 'FM_IsAuthorized',
452                        'ThumbnailIsAuthorized_cb' => 'FM_IsAuthorized',
453
454                        'Aliases' => $Aliases
455                ), (is_array($options) ? $options : array()));
456
457        if (SITE_USES_ALIASES)
458        {
459                $browser = new FileManagerWithAliasSupport($options);
460        }
461        else
462        {
463                $browser = new FileManager($options);
464        }
465        return $browser;
466}
467
468
469
470
471
472
473
474
475
476
477/**
478Start the session, whether the session ID is passed through the URL query section or as a cookie.
479*/
480function start_session_ex()
481{
482        /*
483         * Load session if not already done by CCMS!
484         */
485        if (empty($_SESSION))
486        {
487                $sesid = session_id();
488                $sesname = session_name();
489                // the SWF.Upload / FancyUpload FLASH components do pass along the cookies, but as extra URL query entities:
490                if (!empty($_POST[$sesname]))
491                {
492                        // legalize the sessionID; just a precaution for malicious intent
493                        $alt_sesid = preg_replace('/[^A-Za-z0-9]/', 'X', $_POST[$sesname]);
494
495                        /*
496                         * Before we set the sessionID, we'd better make darn sure it's a legitimate request instead of a hacker trying to get in:
497                         *
498                         * however, before we can access any $_SESSION[] variables do we have to load the session for the given ID.
499                         */
500                        session_id($alt_sesid);
501                        if (!session_start())
502                        {
503                                header('HTTP/1.0 403 Forbidden', true, 403);
504                                die('session_start_ex() failed');
505                        }
506
507                        /*
508                        check the 'secret' value to doublecheck the legality of the session: did it originate from one of the demo entry pages?
509                        */
510                        if (empty($_SESSION['FileManager']) || $_SESSION['FileManager'] !== 'DemoMagick')
511                        {
512                                //echo " :: illegal session override! IGNORED! \n";
513
514                                // do not nuke the first session; this might have been a interloper trying a attack... let it all run its natural course.
515                                if (0)
516                                {
517                                        if (ini_get('session.use_cookies'))
518                                        {
519                                                $params = session_get_cookie_params();
520                                                if (!empty($params['ccms_userID']))
521                                                {
522                                                        setcookie(session_name(), '', time() - 42000,
523                                                                $params['path'], $params['domain'],
524                                                                $params['secure'], $params['httponly']
525                                                        );
526                                                }
527                                        }
528
529                                        // Generate a new session_id
530                                        session_regenerate_id();
531
532                                        // Finally, destroy the session.
533                                        if(session_destroy())
534                                        {
535                                                header('HTTP/1.0 403 Forbidden', true, 403);
536                                                die('session_start_ex() failed');
537                                        }
538                                }
539                                session_id($sesid);
540                        }
541                }
542                else
543                {
544                        if (!session_start())
545                        {
546                                header('HTTP/1.0 403 Forbidden', true, 403);
547                                die('session_start_ex(ALT) failed');
548                        }
549                }
550        }
551}
552
553
554
555
556
557
558
559
560
561
562
563/*
564 * FileManager event callback: Please add your own authentication / authorization here.
565 *
566 * Note that this function serves as a custom callback for all FileManager
567 * authentication/authorization requests, but you may of course provide
568 * different functions for each of the FM callbacks.
569 *
570 * Return TRUE when the session/client is authorized to execute the action, FALSE
571 * otherwise.
572 *
573 * NOTE: the customer code in here may edit the $fileinfo items and have those edits picked up by FM.
574 *       E.g. changing the filename on write/move, fixing filename extensions based on file content sniffed mimetype, etc.
575 *
576 * See the Assets/Connector/FileManager.php file for extended info about this callback and the parameters passed into it.
577 *
578 *
579 * Notes:
580 *
581 *     Just for the sake of the demo, did we include exceptions being thrown in here; in a real situation you wouldn't
582 *     need those in the circumstances they are used right now. You /may/ use exceptions to signal other faults, though.
583 */
584function FM_IsAuthorized($mgr, $action, &$info)
585{
586        // Start session, if not already started
587        start_session_ex();
588
589        //$settings = $mgr->getSettings();
590        //$mimetdefs = $mgr->getMimeTypeDefinitions();
591
592        // log request data:
593        FM_vardumper($mgr, $action, $info);
594
595        // when the session, started in the demo entry pages, doesn't exist or is not valid, we do not allow ANYTHING any more:
596        if (empty($_SESSION))
597        {
598                throw new FileManagerException('authorized: The session is non-existent.');
599                return false;
600        }
601
602        if (empty($_SESSION['FileManager']) || $_SESSION['FileManager'] !== 'DemoMagick')
603        {
604                throw new FileManagerException('authorized: The session is illegal, as it does not the mandatory magic value set up by the demo entry pages.');
605                return false;
606        }
607
608
609        /*
610         * authenticate / authorize:
611         * this sample is a bogus authorization, but you can perform simple to highly
612         * sophisticated authentications / authorizations here, e.g. even ones which also check permissions
613         * related to what is being uploaded right now (different permissions required for file mimetypes,
614         * e.g. images: any authorized user; while other file types which are more susceptible to carrying
615         * illicit payloads requiring at least 'power/trusted user' permissions, ...)
616         */
617
618        switch ($action)
619        {
620        case 'upload':
621                /*
622                 * Note that the TinyMCE demo currently has this sestting set to 'NO' to simulate an UNauthorized user, for the sake of the demo.
623                 */
624                return ($_SESSION['UploadAuth'] == 'yes');
625
626        case 'download':
627                return true;
628
629        case 'create': // create directory
630        case 'destroy':
631        case 'move':  // move or copy!
632        case 'view':
633                return true;
634
635        case 'detail':
636                return true;
637
638        case 'thumbnail':
639                /*
640                For the demo, we deny generation of thumbnails for images in a certain size range: 500KB - 2MB, jpeg only.
641
642                To showcase the nasty/cool (depending on your disposition) things you can do in this callback, we
643                force the thumbnail to become a thumbnail of the 'nuke':
644                */
645                $fsize = filesize($info['file']);
646                if (DEVELOPMENT && $info['mime'] == 'image/jpeg' && $fsize >= 500 * 1024 && $fsize <= 2 * 1024 * 1024)
647                {
648                        // force the manager to fetch the 'nuke' icon:
649                        $info['filename'] = 'is.default-error';
650
651                        // and nuke the mimetype to make sure it does go for the icon, always:
652                        $info['mime'] = 'icon/icon';
653
654                        // and act as if we authorized the action. Meanwhile, we just nuked it.
655                }
656                FM_vardumper($mgr, $action, $info);
657                return true;
658
659        default:
660                // unknown operation. Internal server error.
661                return false;
662        }
663}
664
665
666
667
668
669
670
671// Do *NOT* add a <?php ?-> close tag here! Any whitespace after that makes PHP output both a Content-Type: test/html header AND the whitespace as content.
672// This BREAKS any operation (such as mootools-filemanager::event=thumbnail) which outputs BINARY DATA (in that particular case, PHP spits out an image)
673// The safest way to prevent ANY PHP file from producing undesirable [whitespace] output is to never add that ?-> close tag.
Note: See TracBrowser for help on using the repository browser.