/[drupal]/contributions/modules/drake/drake.module
ViewVC logotype

Contents of /contributions/modules/drake/drake.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.1 - (show annotations) (download) (as text)
Sun Feb 25 18:10:10 2007 UTC (2 years, 9 months ago) by mgiglesias
Branch: MAIN
CVS Tags: HEAD
File MIME type: text/x-php
Initial Release for Drupal 5.0
1 <?php
2
3 define ('DRAKE_DRUPAL_PATH', dirname(dirname(dirname(__FILE__))));
4 define ('DRAKE_CONFIGURATION_FILE', dirname(__FILE__) . DIRECTORY_SEPARATOR . 'drake.ini');
5
6 require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'cake_embedded_dispatcher.class.php');
7 require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'drake.class.php');
8
9 /**
10 * Implementation of hook_help().
11 */
12 function drake_help($section)
13 {
14 switch($section)
15 {
16 case 'admin/modules#description':
17 return t('Lets you run your CakePHP applications inside Drupal.');
18 }
19 }
20
21 /**
22 * Implementation of hook_menu().
23 */
24 function drake_menu($may_cache)
25 {
26 $items = array();
27
28 if ($may_cache)
29 {
30 $items[] = array(
31 'path' => 'drake',
32 'title' => t('Drake Dispatcher'),
33 'callback' => 'drake_dispatcher',
34 'access' => user_access('access content'),
35 'type' => MENU_CALLBACK
36 );
37
38 $items[] = array(
39 'path' => 'admin/build/drake',
40 'title' => t('Drake'),
41 'description' => t('Get a Drake URL for a CakePHP action and view Drake\'s documentation.'),
42 'callback' => 'drake_admin_documentation_page',
43 'access' => user_access('administer site configuration'),
44 'type' => MENU_NORMAL_ITEM
45 );
46
47 $items[] = array(
48 'path' => 'admin/build/drake/documentation',
49 'title' => t('Documentation'),
50 'description' => t('View Drake\'s documentation.'),
51 'callback' => 'drake_admin_documentation_page',
52 'access' => user_access('administer site configuration'),
53 'type' => MENU_DEFAULT_LOCAL_TASK
54 );
55
56 $items[] = array(
57 'path' => 'admin/build/drake/get',
58 'title' => t('Get Drake URL'),
59 'description' => t('Get a Drake URL for a CakePHP action.'),
60 'callback' => 'drake_admin_geturl_page',
61 'access' => user_access('administer site configuration'),
62 'type' => MENU_LOCAL_TASK
63 );
64 }
65
66 return $items;
67 }
68
69 /**
70 * Displays Drake's documentation
71 */
72 function drake_admin_documentation_page()
73 {
74 $output = '';
75
76 $output .= '
77 <p>
78 Drake is a Drupal component that allows <a href="http://www.cakephp.org">CakePHP</a> applications to be hosted within a Drupal website. With Drake you have a bridge to your application that requires no modifications on your CakePHP code. Your application can run with or without Drake.
79 </p>
80
81 <h3>Documentation</h3>
82
83 <p>
84 Drake\'s documentation is hosted at <a href="http://dev.sypad.com/projects/drake">http://dev.sypad.com/projects/drake</a>.
85 </p>
86
87 <h3>Quick Setup</h3>
88
89 <p>
90 Assuming you have your CakePHP application up and running, take the following steps to test your Drake install:
91 </p>
92
93 <ol>
94 <li>Edit the file <code>' . DRAKE_CONFIGURATION_FILE . '</code> and modify its settings as instructed.</li>
95 <li>Go to ' . l(url('drake'), 'drake') . ' and you should see the default page of your CakePHP application inside Drupal.</li>
96 <li>Read the <a href="http://dev.sypad.com/projects/drake">documentation</a> for notes and tips on how to take better advantage of Drake.</li>
97 </ol>
98
99 <h3>Run Drake</h3>
100
101 <p>
102 Load your default CakePHP application (once Drake has been set up) default page with the following URL: ' . l(url('drake'), 'drake') . '
103 </p>
104
105 <p>
106 If you want to get the Drake URL to load a specific application/action use the handy Drake form at ' . l(url('admin/build/drake/get'), 'admin/build/drake/get') . '
107 </p>
108
109 <h3>Credits</h3>
110
111 <p>
112 This project is maintained and developed by <a href="http://www.marianoiglesias.com.ar">Mariano Iglesias</a>. Further credits go to Felix Geisendoerfer for his initial work on the idea.
113 </p>
114 ';
115
116 return $output;
117 }
118
119 /**
120 * Displays Drake's Get URL form
121 */
122 function drake_admin_geturl_page()
123 {
124 $output = '';
125
126 $output .= t('In the following form specify the action you wish to run and on which CakePHP application to get a valid Drake URL.');
127 $output .= drupal_get_form('drake_admin_geturl_form');
128
129 return $output;
130 }
131
132 /**
133 * Sets up the display of Drake's Get URL form
134 */
135 function theme_drake_admin_geturl_form()
136 {
137 $output = '';
138
139 $output .= drupal_render($form['application']);
140 $output .= drupal_render($form['url']);
141 $output .= drupal_render($form);
142
143 return $output;
144 }
145
146 /**
147 * Builds Drake's Get URL form elements
148 */
149 function drake_admin_geturl_form()
150 {
151 $settings = _drake_parseConfiguration(null, false);
152
153 $applications = array();
154
155 $applications[''] = t('-- CakePHP application --');
156
157 foreach($settings['applications'] as $application)
158 {
159 $applications[$application['id']] = $application['name'] . ($application['default'] ? ' (Default)' : '');
160 }
161
162 $form = array();
163
164 $form['drake_get_url']['application'] = array(
165 '#type' => 'select',
166 '#title' => t('CakePHP application'),
167 '#default_value' => (isset($settings['application']) ? $settings['application']['id'] : ''),
168 '#options' => $applications,
169 '#description' => t('Select to which CakePHP application the following URL applies.'),
170 );
171
172 $form['drake_get_url']['url'] = array(
173 '#type' => 'textfield',
174 '#title' => t('CakePHP URL'),
175 '#default_value' => '',
176 '#size' => 60,
177 '#maxlength' => 64,
178 '#description' => t('Enter a valid URL for your CakePHP application. E.g.: /posts/view/4'),
179 );
180
181 $form['drake_get_url']['submit'] = array(
182 '#type' => 'submit',
183 '#value' => t('Get Drake URL'),
184 );
185
186 return $form;
187 }
188
189 /**
190 * Validates the submission of Drake's Get URL form
191 *
192 * @parameter string $form_id Drupal's Form ID
193 * @parameter array $form_values Set of submitted values
194 */
195 function drake_admin_geturl_form_validate($form_id, $form_values)
196 {
197 if (trim($form_values['application']) == '')
198 {
199 form_set_error('', t('You must select a CakePHP application'));
200 }
201 }
202
203 /**
204 * Handles the submission of Drake's Get URL form
205 *
206 * @parameter string $form_id Drupal's Form ID
207 * @parameter array $form_values Set of submitted values
208 */
209 function drake_admin_geturl_form_submit($form_id, $form_values)
210 {
211 $settings = _drake_parseConfiguration(null, false);
212
213 if (isset($settings['application']) && strcmp($settings['application']['id'], $form_values['application']) == 0)
214 {
215 $form_values['application'] = null;
216 }
217
218 // Set up Drake callable
219
220 $drakeCallable =& Drake::getInstance();
221
222 $drakeCallable->setDrupalUrl('drake');
223
224 // Generate URL
225
226 $url = $drakeCallable->getUrl(!empty($form_values['url']) ? $form_values['url'] : null, $form_values['application']);
227
228 $query = null;
229
230 if (strpos($url, '?') !== false)
231 {
232 $query = substr($url, strpos($url, '?') + 1);
233 $url = substr($url, 0, strpos($url, '?'));
234 }
235
236 $url = url($url, $query, null, true);
237
238 // Show URL
239
240 drupal_set_message(t('Your Drake URL is ') . '<a href="' . $url . '">' . $url . '</a>');
241
242 // Leave Drake callable with correct URL
243
244 $drakeCallable->setDrupalUrl(url('drake'));
245 }
246
247 /**
248 * Dispatches a CakePHP action through drake.
249 *
250 * @param string $application The CakePHP application identifier or null for default.
251 */
252 function drake_dispatcher($application = null)
253 {
254 $settings = _drake_parseConfiguration($application);
255
256 $parameters = array();
257
258 $parameters['cake_path'] = $settings['application']['path'];
259 $parameters['cake_url'] = $settings['application']['url'];
260
261 if (isset($settings['application']['send']))
262 {
263 $parameters['send'] = $settings['application']['send'];
264 }
265
266 $parameters['drupal_url'] = url('drake' . (isset($application) ? '/' . $application : ''));
267 $parameters['clean'] = (isset($_REQUEST['task']) && strcasecmp($_REQUEST['task'], 'clean') == 0 ? true : false);
268
269 if (isset($_REQUEST['run']))
270 {
271 $parameters['url'] = $_REQUEST['run'];
272 }
273
274 // Startup
275
276 _drake_dispatcher_initialize();
277
278 // So CakePHP knows about Drake
279
280 define('DRAKE', true);
281
282 // Set up Drake callable
283
284 $drakeCallable =& Drake::getInstance();
285
286 $drakeCallable->setDrupalUrl(url('drake'));
287
288 if ($drakeCallable->isDrake() && isset($application))
289 {
290 $drakeCallable->setApplication($application);
291 }
292
293 // Set up CakePHP dispatcher
294
295 $cakeDispatcher =& new CakeEmbeddedDispatcher();
296
297 $cakeDispatcher->setRestoreSession(false);
298 $cakeDispatcher->setCakePath($parameters['cake_path']);
299 $cakeDispatcher->setCakeUrlBase($parameters['cake_url']);
300 $cakeDispatcher->setComponent($parameters['drupal_url'] . (strpos($parameters['drupal_url'], '?') !== false ? '&' : '?') . 'run=$CAKE_ACTION');
301 $cakeDispatcher->setCleanOutput((isset($parameters['clean']) ? $parameters['clean'] : false));
302 $cakeDispatcher->setCleanOutputParameter('task', 'clean');
303 $cakeDispatcher->setIgnoreParameters(array('q', 'task'));
304
305 if (isset($parameters['send']))
306 {
307 $cakeDispatcher->setCakeUrlAddParameters($parameters['send']);
308 }
309
310 // Execute CakePHP action
311
312 $content = $cakeDispatcher->get($parameters['url']);
313
314 // Finalize
315
316 _drake_dispatcher_finalize();
317
318 // If clean output (i.e: AJAX) send contents and exit
319
320 if (isset($parameters['clean']) && $parameters['clean'])
321 {
322 echo $content['body'];
323 exit;
324 }
325
326 // No title, leave Drupal's default
327
328 drupal_set_title('');
329
330 // Prepare all HEAD tags
331
332 if (!isset($content['head']['custom']))
333 {
334 $content['head']['custom'] = array();
335 }
336
337 $elements = array ('script', 'stylesheets', 'meta', 'http-equiv');
338
339 foreach($elements as $element)
340 {
341 if (isset($content['head'][$element]))
342 {
343 foreach($content['head'][$element] as $innerElement)
344 {
345 $content['head']['custom'][] = $innerElement['tag'];
346 }
347 }
348 }
349
350 // Set elements for HEAD tag
351
352 foreach($content['head']['custom'] as $element)
353 {
354 drupal_set_html_head($element);
355 }
356
357 // Send output through theme function
358
359 $output = theme('drake_dispatcher_output', $content['body']);
360
361 $output = '
362 <script type="text/javascript">
363 <!--
364 if (window && window.jQuery && jQuery && jQuery.noConflict)
365 {
366 jQuery.noConflict();
367 }
368 // -->
369 </script>' . "\n" . $output;
370
371 return $output;
372 }
373
374 /**
375 * Themalize output before sending to Drupal
376 *
377 * @param string $text Output to send
378 */
379 function theme_drake_dispatcher_output($text)
380 {
381 return $text;
382 }
383
384 /**
385 * Startup a CakePHP request by closing the current session.
386 */
387 function _drake_dispatcher_initialize()
388 {
389 // Backup the session
390
391 if (isset($_SESSION))
392 {
393 $GLOBALS['Drake']['backSession'] = array (
394 'id' => session_id()
395 );
396
397 // Change the session ID on database so Drupal can't delete the record when destroying the session
398
399 db_query("UPDATE {sessions} SET sid = '%s' WHERE sid = '%s'", 'drake_' . $GLOBALS['Drake']['backSession']['id'], $GLOBALS['Drake']['backSession']['id']);
400
401 // Destroy it
402
403 session_destroy();
404 }
405
406 // Set standard session module so CakePHP can safely change it
407
408 session_module_name('files');
409 }
410
411 /**
412 * Finalize a CakePHP request by restoring the database and the session previously closed.
413 */
414 function _drake_dispatcher_finalize()
415 {
416 // Restore DB connection (force new connection)
417
418 db_set_active('drake');
419
420 // Initialize session management
421
422 drupal_bootstrap(DRUPAL_BOOTSTRAP_SESSION);
423
424 if (isset($GLOBALS['Drake']['backSession']))
425 {
426 // Restore session by retrieving record we backed up
427
428 db_query("UPDATE {sessions} SET sid = '%s' WHERE sid = '%s'", session_id(), 'drake_' . $GLOBALS['Drake']['backSession']['id']);
429 }
430 }
431
432 /**
433 * Renders a Drake fatal error.
434 *
435 * @param string $message Message to render
436 */
437
438 function _drake_raiseError($message)
439 {
440 $message = '<strong>Drake ERROR</strong>: ' . $message;
441
442 die($message);
443
444 return false;
445 }
446
447 /**
448 * Parse Drake configuration
449 */
450 function _drake_parseConfiguration($currentApplicationId = null, $checkSettings = true)
451 {
452 $configFile = DRAKE_CONFIGURATION_FILE;
453
454 if (!@file_exists($configFile))
455 {
456 return _drake_raiseError('Configuration file not present! Looked for ' . $configFile);
457 }
458
459 $elements = parse_ini_file($configFile, true);
460
461 if (!isset($elements['settings']) || !is_array($elements['settings']))
462 {
463 return _drake_raiseError('Configuration file has no settings section');
464 }
465
466 $settings = $elements['settings'];
467
468 unset($elements['settings']);
469
470 if (count($elements) == 0)
471 {
472 return _drake_raiseError('There are no applications defined on the configuration file');
473 }
474
475 if ($checkSettings && !empty($currentApplicationId) && (!isset($settings['default']) || !is_string($settings['default'])))
476 {
477 return _drake_raiseError('No default application is set on the configuration file');
478 }
479
480 // Build available applications
481
482 $applications = array();
483
484 foreach($elements as $application => $applicationSettings)
485 {
486 $applications[$application] = $applicationSettings;
487
488 $applications[$application]['id'] = $application;
489
490 $applications[$application]['default'] = ($settings['default'] == $applications[$application]['id']);
491
492 if (!isset($applications[$application]['name']))
493 {
494 $applications[$application]['name'] = $application;
495 }
496
497 foreach($applications[$application] as $element => $value)
498 {
499 if (is_string($value) && in_array($element, array('path', 'url')))
500 {
501 $applications[$application][$element] = trim($value);
502 }
503 }
504
505 if (isset($applications[$application]['path']))
506 {
507 if ($applications[$application]['path'][0] !== '/' && $applications[$application]['path'][0] !== '\\')
508 {
509 $applications[$application]['path'] = _drake_canonicalizePath(DRAKE_DRUPAL_PATH . DIRECTORY_SEPARATOR . $applications[$application]['path']);
510 }
511
512 if (DIRECTORY_SEPARATOR == '/')
513 {
514 $applications[$application]['path'] = str_replace('\\', DIRECTORY_SEPARATOR, $applications[$application]['path']);
515 }
516 else
517 {
518 $applications[$application]['path'] = str_replace('/', DIRECTORY_SEPARATOR, $applications[$application]['path']);
519 }
520 }
521 }
522
523 if (isset($currentApplicationId))
524 {
525 $currentApplication = $currentApplicationId;
526 }
527 else
528 {
529 $currentApplication = $settings['default'];
530 }
531
532 if ($checkSettings && !isset($applications[$currentApplication]))
533 {
534 return _drake_raiseError('The application named <code>' . $currentApplication . '</code> is not set on the configuration file');
535 }
536
537 if (isset($applications[$currentApplication]))
538 {
539 // Set current application
540
541 $application = $applications[$currentApplication];
542
543 if ($checkSettings && (!isset($application['path']) || empty($application['path'])))
544 {
545 return _drake_raiseError('There\'s no <code>path</code> specified in application <code>' . $application['id'] . '</code>');
546 }
547
548 // Check current application path
549
550 if ($checkSettings && (@!is_readable($application['path']) || @!is_dir($application['path'])))
551 {
552 return _drake_raiseError('The path <code>' . $application['path'] . '</code> specified in application <code>' . $application['id'] . '</code> is not accessible');
553 }
554 else if ($checkSettings && @!file_exists($application['path'] . DIRECTORY_SEPARATOR . 'index.php'))
555 {
556 return _drake_raiseError('The path <code>' . $application['path'] . '</code> specified in application <code>' . $application['id'] . '</code> doesn\'t seem to host a CakePHP application');
557 }
558
559 if ($checkSettings && (!isset($application['url']) || empty($application['url'])))
560 {
561 return _drake_raiseError('There\'s no <code>url</code> specified in application <code>' . $application['id'] . '</code>');
562 }
563 }
564 else
565 {
566 $application = null;
567 }
568
569 $result = array(
570 'application' => $application,
571 'applications' => $applications
572 );
573
574 if (!is_array($result['application']) || empty($result['application']))
575 {
576 unset($result['application']);
577 }
578
579 return $result;
580 }
581
582 /**
583 * Get the canonicalized version of a path
584 *
585 * @param string $path Path
586 *
587 * @return string Canonicalized path
588 */
589 function _drake_canonicalizePath($path)
590 {
591 // Converts all "\" to "/", and erases blank spaces at the beginning and the ending of the string
592
593 $path = trim(preg_replace("/\\\\/", "/", (string) $path));
594
595 // Checks if last parameter is a directory with no slashs ("/") in the end. To be considered a dir,
596 // it can't end on "dot something", or can't have a querystring ("dot something ? querystring")
597
598 if (!preg_match("/(\.\w{1,4})$/", $path) && !preg_match("/\?[^\\/]+$/", $path) && !preg_match("/\\/$/", $path))
599 {
600 $path .= '/';
601 }
602
603 // Breaks the original string in to parts: "root" and "dir".
604 // "root" can be "C:/" (Windows), "/" (Linux) or "http://www.something.com/" (URLs). This will be the start of output string.
605 // "dir" can be "Windows/System", "root/html/examples/", "includes/classes/class.validator.php", etc.
606
607 preg_match_all("/^(\\/|\w:\\/|(http|ftp)s?:\\/\\/[^\\/]+\\/)?(.*)$/i", $path, $matches, PREG_SET_ORDER);
608
609 $path_root = $matches[0][1];
610 $path_dir = $matches[0][3];
611
612 // If "dir" part has one or more slashes at the beginning, erases all.
613 // Then if it has one or more slashes in sequence, replaces for only 1.
614
615 $path_dir = preg_replace( array("/^\\/+/", "/\\/+/"), array("", "/"), $path_dir );
616
617 // Breaks "dir" part on each slash
618
619 $path_parts = explode("/", $path_dir);
620
621 // Creates a new array with the right path. Each element is a new dir (or file in the ending, if exists) in sequence.
622
623 for ($i = $j = 0, $real_path_parts = array(); $i < count($path_parts); $i++)
624 {
625 if ($path_parts[$i] == '.')
626 {
627 continue;
628 }
629 else if ($path_parts[$i] == '..')
630 {
631 if ( (isset($real_path_parts[$j-1]) && $real_path_parts[$j-1] != '..') || ($path_root != "") )
632 {
633 array_pop($real_path_parts);
634 $j--;
635 continue;
636 }
637 }
638
639 array_push($real_path_parts, $path_parts[$i]);
640
641 $j++;
642 }
643
644 // Remove tailing slash
645
646 $path = $path_root . implode('/', $real_path_parts);
647
648 if ($path[strlen($path) - 1] == '/')
649 {
650 $path = substr($path, 0, strlen($path) - 1);
651 }
652
653 return $path;
654 }

  ViewVC Help
Powered by ViewVC 1.1.2