1
2
3
4 """
5 Common functions that many Drupy plugins will need to reference.
6
7 @package includes
8 @see <a href='http://drupy.net'>Drupy Homepage</a>
9 @see <a href='http://drupal.org'>Drupal Homepage</a>
10 @note Drupy is a port of the Drupal project.
11 @note This file was ported from Drupal's includes/common.inc
12 @author Brendon Crawford
13 @copyright 2008 Brendon Crawford
14 @contact message144 at users dot sourceforge dot net
15 @created 2008-01-10
16 @version 0.1
17 @note License:
18
19 This program is free software; you can redistribute it and/or
20 modify it under the terms of the GNU General Public License
21 as published by the Free Software Foundation; either version 2
22 of the License, or (at your option) any later version.
23
24 This program is distributed in the hope that it will be useful,
25 but WITHOUT ANY WARRANTY; without even the implied warranty of
26 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 GNU General Public License for more details.
28
29 You should have received a copy of the GNU General Public License
30 along with this program; if not, write to:
31
32 The Free Software Foundation, Inc.,
33 51 Franklin Street, Fifth Floor,
34 Boston, MA 02110-1301,
35 USA
36 """
37
38 __version__ = "$Revision: 1 $"
39
40
41
42
43
44
45
46
47
48 import urllib2
49 from lib.drupy import DrupyPHP as php
50 from sites.default import settings
51 import bootstrap as lib_bootstrap
52 import theme as lib_theme
53
54 import plugin as lib_plugin
55 import menu as lib_menu
56
57 import file as lib_file
58 import unicode as lib_unicode
59
60
61
62
63
64
65
66
67
68 SAVED_NEW = 1;
69
70
71
72 SAVED_UPDATED = 2;
73
74
75
76 SAVED_DELETED = 3;
77
78
79
80 -def drupal_set_content(region = None, data = None):
81 """
82 Set content for a specified region.
83
84 @param region
85 Page region the content is assigned to.
86 @param data
87 Content to be set.
88 """
89 php.static(drupal_set_content, 'content', {})
90 if (not php.is_null(region) and not php.is_null(data)):
91 drupal_set_content.content[region].append( data );
92 return drupal_set_content.content;
93
94
95 -def drupal_get_content(region = None, delimiter = ' '):
96 """
97 Get assigned content.
98
99 @param region
100 A specified region to fetch content for. If None, all regions will be
101 returned.
102 @param delimiter
103 Content to be inserted between exploded array elements.
104 """
105 content = drupal_set_content();
106 if (region != None):
107 if (php.isset(content, region) and php.is_array(content, region)):
108 return php.implode(delimiter, content[region]);
109 else:
110 for region in php.array_keys(content):
111 if (php.is_array(content[region])):
112 content[region] = php.implode(delimiter, content[region]);
113 return content;
114
115
116
130
131
140
141
142
153
154
156 """
157 Retrieve output to be displayed in the head tag of the HTML page.
158 """
159 output = '<meta http-equiv="Content-Type" ' + \
160 'content="text/html; charset=utf-8" />\n';
161 return output + drupal_set_html_head();
162
163
165 """
166 Reset the static variable which holds the aliases mapped for this request.
167 """
168 drupal_lookup_path('wipe');
169
170
171
173 """
174 Set an HTTP response php.header for the current page.
175
176 Note: When sending a Content-Type php.header, always
177 include a 'charset' type,
178 too. This is necessary to avoid security bugs (e.g. UTF-7 XSS).
179 """
180
181
182
183
184 php.static(drupal_set_header, 'stored_headers', [])
185 if (php.strlen(header_) > 0):
186 php.header(header_);
187 drupal_set_header.stored_headers.append(header_);
188 return php.implode("\n", drupal_set_header.stored_headers);
189
190
192 """
193 Get the HTTP response headers for the current page.
194 """
195 return drupal_set_header();
196
197
218
219
221 """
222 Get the feed URLs for the current page.
223
224 @param delimiter
225 A delimiter to split feeds by.
226 """
227 feeds = drupal_add_feed();
228 return php.implode(feeds, delimiter);
229
230
231
232
233
234
236 """
237 Functions to properly handle HTTP responses.
238
239 Parse an array into a valid urlencoded query string.
240
241 @param query
242 The array to be processed e.g. php.GET.
243 @param exclude
244 The array filled with keys to be excluded. Use parent[child] to exclude
245 nested items.
246 @param parent
247 Should not be passed, only used in recursive calls.
248 @return
249 An urlencoded string which can be appended to/as the URL query string.
250 """
251 params = [];
252 for key,value in query.items():
253 key = drupal_urlencode(key);
254 if (parent):
255 key = parent + '[' + key + ']';
256 if (php.in_array(key, exclude)):
257 continue;
258 if (php.is_array(value)):
259 params.append( drupal_query_string_encode(value, exclude, key) );
260 else:
261 params.append( key + '=' + drupal_urlencode(value) );
262 return php.implode('&', params);
263
264
266 """
267 Prepare a destination query string for use in combination
268 with drupal_goto().
269
270 Used to direct the user back to the referring page after completing a form.
271 By default the current URL is returned. If a destination exists in the
272 previous request, that destination is returned. As such, a destination can
273 persist across multiple pages.
274
275 @see drupal_goto()
276 """
277 if (php.isset(php.REQUEST, 'destination')):
278 return 'destination=' + urlencode(php.REQUEST['destination']);
279 else:
280
281 path = (php.GET.__getitem__('q') if php.isset(php.GET, 'q') else '');
282 query = drupal_query_string_encode(php.GET, ['q']);
283 if (query != ''):
284 path += '?' + query;
285 return 'destination=' + urlencode(path);
286
287
288
289 -def drupal_goto(path = '', query = None, fragment = None, \
290 http_response_code = 302):
291 """
292 Send the user to a different Drupal page.
293
294 This issues an on-site HTTP redirect. The function makes sure the redirected
295 URL is formatted correctly.
296
297 Usually the redirected URL is constructed from this function's input
298 parameters. However you may override that behavior by setting a
299 <em>destination</em> in either the php.REQUEST-array (i.e. by using
300 the query string of an URI) or the php.REQUEST['edit']-array (i.e. by
301 using a hidden form field). This is used to direct the user back to
302 the proper page after completing a form. For example, after editing
303 a post on the 'admin/content/node'-page or after having logged on using the
304 'user login'-block in a sidebar. The function drupal_get_destination()
305 can be used to help set the destination URL.
306
307 Drupal will ensure that messages set by drupal_set_message() and other
308 session data are written to the database before the user is redirected.
309
310 This function ends the request; use it rather than a print theme('page')
311 statement in your menu callback.
312
313 @param path
314 A Drupal path or a full URL.
315 @param query
316 A query string component, if any.
317 @param fragment
318 A destination fragment identifier (named anchor).
319 @param http_response_code
320 Valid values for an actual "goto" as per RFC 2616 section 10.3 are:
321 - 301 Moved Permanently (the recommended value for most redirects)
322 - 302 Found (default in Drupal and PHP, sometimes used for spamming search
323 engines)
324 - 303 See Other
325 - 304 Not Modified
326 - 305 Use Proxy
327 - 307 Temporary Redirect (alternative to "503 Site Down for Maintenance")
328 Note: Other values are defined by RFC 2616, but are rarely used and poorly
329 supported.
330 @see drupal_get_destination()
331 """
332 if (php.isset(php.REQUEST, 'destination')):
333 urlP = php.parse_url(php.urldecode(php.REQUEST['destination']));
334 elif (php.isset(php.REQUEST['edit'], 'destination')):
335 urlP = php.parse_url(php.urldecode(php.REQUEST['edit']['destination']));
336 url = url(path, {'query' : urlP['query'], 'fragment' : urlP['fragment'], \
337 'absolute' : True});
338
339 url = php.str_replace(["\n", "\r"], '', url);
340
341
342 if (not php.defined(locals(), 'MAINTENANCE_MODE', True) or \
343 MAINTENANCE_MODE != 'update'):
344 plugin_invoke_all('exit', url);
345
346
347 session_write_close();
348 php.header('Location: '. url, True, http_response_code);
349
350
351
352
353 exit();
354
355
356
358 """
359 Generates a site off-line message.
360 """
361 drupal_maintenance_theme();
362 drupal_set_header('HTTP/1.1 503 Service unavailable');
363 drupal_set_title(t('Site off-line'));
364 print theme(
365 'maintenance_page',
366 filter_xss_admin(
367 variable_get(
368 'site_offline_message',
369 t(
370 '@site is currently under maintenance. ' + \
371 'We should be back shortly. Thank you for your patience.', \
372 {'@site' : variable_get('site_name', 'Drupal')}
373 )
374 )
375 )
376 );
377
378
379
402
403
404
425
426
427
430 """
431 Perform an HTTP request.
432
433 This is a flexible and powerful HTTP client implementation.
434 Correctly handles
435 php.GET, php.POST, PUT or any other HTTP requests. Handles redirects.
436
437 @note DRUPY:
438 This function has been modified to use urllib2.
439 Although it does not act exactly as before, it is
440 still good enough to get the job done.
441 Return object should look like this:
442 result:
443 Str error
444 Int code
445 Str request
446 Dict headers
447 Int redirect_code
448 Str redirect_url
449 @param url
450 A string containing a fully qualified URI.
451 @param headers
452 An array containing an HTTP php.header : value pair.
453 @param method
454 A string defining the HTTP request to use.
455 @param data
456 A string containing data to include in the request.
457 @param retry
458 An integer representing how many times to retry the request in case of a
459 redirect.
460 @return
461 An object containing the HTTP request headers, response code, headers,
462 data and redirect status.
463 """
464 headers['User-Agent'] = 'Drupy (+http://drupy.sourceforge.net/)';
465 req = urllib2.Request(url, data, headers);
466 res = urllib2.urlopen(req);
467 result = php.object_({
468 'error' : res.msg,
469 'code' : res.code,
470 'request' : 'NOT-AVAILABLE',
471 'headers' : headers,
472 'data' : res.read()
473 });
474 return result;
475
476
477
478
479
480
481
482
485 """
486 Log errors as defined by administrator.
487
488 Error levels:
489 - 0 = Log errors to database.
490 - 1 = Log errors to database and to screen.
491 """
492 if (errno > 0):
493
494
495 err = {
496 'errType' : errType,
497 'message' : message,
498 'filename' : filename,
499 'line' : line
500 };
501 entry = '%(errType)s : %(message)s in %(filename)s on line %(line)s' % err
502
503 if (lib_bootstrap.variable_get('error_level', 1) == 1 or \
504 strstr(php.SERVER['SCRIPT_NAME'], 'update.py')):
505 lib_bootstrap.drupal_set_message(entry, 'error');
506 lib_bootstrap.watchdog('php', \
507 '%(message)s in %(filename)s on line %(line)s.' % err, \
508 lib_bootstrap.WATCHDOG_ERROR);
509
510
511
513 """
514 Not needed
515 """
516 pass;
517
518
520 """
521 Helper function to strip slashes from FILES skipping over the tmp_name keys
522 since PHP generates single backslashes for file paths on Windows systems.
523
524 tmp_name does not have backslashes added see
525 http://php.net/manual/en/features.file-upload.php#42280
526 """
527 pass;
528
529
531 """
532 Fix double-escaping problems caused by "magic quotes"
533 in some PHP installations.
534 """
535 pass;
536
537
538 -def t(string, args={}, langcode=None):
539 """
540 Translate strings to the page language or a given language.
541
542 All human-readable text that will be displayed somewhere
543 within a page should
544 be run through the t() function.
545
546 Examples:
547 @code
548 if (!info or !info['extension']) {
549 form_set_error('picture_upload', \
550 t('The uploaded file was not an image.'));
551 }
552
553 form['submit'] = array(
554 '#type' : 'submit',
555 '#value' : t('Log in'),
556 );
557 @endcode
558
559 Any text within t() can be extracted by translators and changed into
560 the equivalent text in their native language.
561
562 Special variables called "placeholders" are used to signal dynamic
563 information in a string which should not be translated. Placeholders
564 can also be used for text that may change from time to time
565 (such as link paths) to be changed without requiring
566 updates to translations.
567
568 For example:
569 @code
570 output = t('There are currently %members and %visitors online.', array(
571 '%members' : format_plural(%(total_users)s, '1 user', '@count users'),
572 '%visitors' : format_plural(%(guests)s.count, \
573 '1 guest', '@count guests')));
574 @endcode
575
576 There are three styles of placeholders:
577 - !variable, which indicates that the text should be inserted as-is. This is
578 useful for inserting variables into things like e-mail.
579 @code
580 message[] = t("If you don't want to receive such e-mails, " + \
581 "you can change your settings at !url.", \
582 array('!url' : url("user/%(account)s.uid", \
583 array('absolute' : True))));
584 @endcode
585
586 - @variable, which indicates that the text should be
587 run through check_plain,
588 to escape HTML characters. Use this for any output that's displayed within
589 a Drupal page.
590 @code
591 drupal_set_title(title = t("@name's blog", \
592 array('@name' : account.name)));
593 @endcode
594
595 - %variable, which indicates that the string should be HTML escaped and
596 highlighted with theme_placeholder() which shows up by default as
597 <em>emphasized</em>.
598 @code
599 message = t('%name-from sent %name-to an e-mail.', \
600 array('%name-from' : %(user)s.name, '%name-to' : account.name));
601 @endcode
602
603 When using t(), try to put entire sentences and strings in one t() call.
604 This makes it easier for translators, as it provides
605 context as to what each
606 word refers to. HTML markup within translation strings i
607 s allowed, but should
608 be avoided if possible. The exception are embedded links; link titles add a
609 context for translators, so should be kept in the main string.
610
611 Here is an example of incorrect usage of t():
612 @code
613 output += t('<p>Go to the @contact-page.</p>', \
614 array('@contact-page' : l(t('contact page'), 'contact')));
615 @endcode
616
617 Here is an example of t() used correctly:
618 @code
619 output += '<p>'. t(\
620 'Go to the <a href="@contact-page">contact page</a>.', \
621 array('@contact-page' : url('contact'))) .'</p>';
622 @endcode
623
624 Also avoid escaping quotation marks wherever possible.
625
626 Incorrect:
627 @code
628 output += t('Don\'t click me.');
629 @endcode
630
631 Correct:
632 @code
633 output += t("Don't click me.");
634 @endcode
635
636 @param string
637 A string containing the English string to translate.
638 @param args
639 An associative array of replacements to make after translation. Incidences
640 of any key in this array are replaced with the corresponding value.
641 Based on the first character of the key, the value
642 is escaped and/or themed:
643 - !variable: inserted as is
644 - @variable: escape plain text to HTML (check_plain)
645 - %variable: escape text and theme as a placeholder for user-submitted
646 content (check_plain + theme_placeholder)
647 @param langcode
648 Optional language code to translate to a language other than what is used
649 to display the page.
650 @return
651 The translated string.
652 """
653 php.static(t, 'custom_strings', {})
654 langcode = (langcode if (langcode != None) else \
655 lib_appglobals.language.language);
656
657
658
659
660 if (not php.isset(t.custom_strings, langcode)):
661 t.custom_strings[langcode] = \
662 variable_get('locale_custom_strings_' + langcode, {});
663
664 if (php.isset(t.custom_strings[langcode], string)):
665 string = t.custom_strings[langcode][string];
666
667 elif (php.function_exists('locale') and langcode != 'en'):
668 string = locale(string, langcode);
669 if (php.empty(args)):
670 return string;
671 else:
672
673 for key,value in args.items():
674 if key[0] == '@':
675
676 args[key] = check_plain(value);
677 if key[0] == '!':
678 pass;
679 elif key[0] == '%' or True:
680
681 args[key] = lib_theme.theme('placeholder', value);
682 return php.strtr(string, args);
683
684
685
686
687
688
689
690
691
692
694 """
695 Verify the syntax of the given e-mail address.
696
697 Empty e-mail addresses are allowed. See RFC 2822 for details.
698
699 @param mail
700 A string containing an e-mail address.
701 @return
702 True if the address is in a valid format.
703 """
704 items = {
705 user : '[a-zA-Z0-9_\-\.\+\^!#\$%&*+\/\=\?\`\|\{\}~\']+',
706 domain : '(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.?)+',
707 ipv4 : '[0-9]{1,3}(\.[0-9]{1,3}){3}',
708 ipv6 : '[0-9a-fA-F]{1,4}(\:[0-9a-fA-F]{1,4}){7}'
709 };
710 mail = php.Reference();
711 cnt = php.preg_match(\
712 "/^%(user)s@(%(domain)s|(\[(%(ipv4)s|%(ipv6)s)\]))$/" % items, mail);
713 return (cnt > 0);
714
715
716
718 """
719 Verify the syntax of the given URL.
720
721 This function should only be used on actual URLs. It should not be used for
722 Drupal menu paths, which can contain arbitrary characters.
723
724 @param url
725 The URL to verify.
726 @param absolute
727 Whether the URL is absolute (beginning with a scheme such as "http:").
728 @return
729 True if the URL is in a valid format.
730 """
731 allowed_characters = '[a-z0-9\/:_\-_\.\?\$,;~=#&%\+]';
732 if (absolute):
733 url = php.Reference();
734 return (php.preg_match("/^(http|https|ftp):\/\/" + \
735 allowed_characters + "+$/i", url) > 0);
736 else:
737 url = php.Reference();
738 return (php.preg_match("/^" + allowed_characters + "+$/i", url) > 0);
739
740
741
742
743
744
746 """
747 Register an event for the current visitor (hostname/IP) to
748 the flood control mechanism.
749
750 @param name
751 The name of an event.
752 """
753 db_query("INSERT INTO {flood} (event, hostname, timestamp) " + \
754 "VALUES ('%s', '%s', %d)", name, ip_address(), php.time_());
755
756
757
759 """
760 Check if the current visitor (hostname/IP) is allowed to
761 proceed with the specified event.
762
763 The user is allowed to proceed if he did not trigger the
764 specified event more than threshold times per hour.
765
766 @param name
767 The name of the event.
768 @param number
769 The maximum number of the specified event per hour (per visitor).
770 @return
771 True if the user did not exceed the hourly threshold. False otherwise.
772 """
773 number = db_result(db_query("SELECT COUNT(*) FROM {flood} " + \
774 "WHERE event = '%s' AND hostname = '%s' AND timestamp > %d", \
775 name, ip_address(), drupy_time() - 3600));
776 return (number < threshold);
777
778
779
782
783
784
786 """
787 Prepare a URL for use in an HTML attribute. Strips harmful protocols.
788 """
789 return filter_xss_bad_protocol(uri, False);
790
791
792
793
794
795
796
797
820
821
835
836
870
871
872
943
944
945
947 """
948 Parse a given byte count.
949
950 @param size
951 A size expressed as a number of bytes with optional SI size and unit
952 suffix (e.g. 2, 3K, 5MB, 10G).
953 @return
954 An integer representation of the size.
955 """
956 suffixes = {
957 '' : 1,
958 'k' : 1024,
959 'm' : 1048576,
960 'g' : 1073741824,
961 };
962 match = []
963 if (php.preg_match('/([0-9]+)\s*(k|m|g)?(b?(ytes?)?)/i', size, match) > 0):
964 return match[1] * suffixes[drupal_strtolower(match[2])];
965
966
967
992
993
1027
1028
1029
1101
1102
1103
1104
1105
1106
1107
1108 -def url(path = None, options = {}):
1109 """
1110 Generate a URL from a Drupal menu path.
1111 Will also pass-through existing URLs.
1112
1113 @param path
1114 The Drupal path being linked to, such as "admin/content/node", or an
1115 existing URL like "http://drupal.org/". The special path
1116 '<front>' may also be given and will generate the site's base URL.
1117 @param options
1118 An associative array of additional options, with the following keys:
1119 'query'
1120 A query string to append to the link, or an array of query key/value
1121 properties.
1122 'fragment'
1123 A fragment identifier (or named anchor) to append to the link.
1124 Do not include the '#' character.
1125 'absolute' (default False)
1126 Whether to force the output to be an absolute link (beginning with
1127 http:). Useful for links that will be displayed outside the site, such
1128 as in an RSS feed.
1129 'alias' (default False)
1130 Whether the given path is an alias already.
1131 'external'
1132 Whether the given path is an external URL.
1133 'language'
1134 An optional language object. Used to build the URL to link to and
1135 look up the proper alias for the link.
1136 'base_url'
1137 Only used internally, to modify the base URL when a language dependent
1138 URL requires so.
1139 'prefix'
1140 Only used internally, to modify the path when a language dependent URL
1141 requires so.
1142 @return
1143 A string containing a URL to the given path.
1144
1145 When creating links in plugins, consider whether l() could be a better
1146 alternative than url().
1147 """
1148 php.static(url, 'script')
1149 php.static(url, 'clean_url')
1150
1151 options = php.array_merge(options, {
1152 'fragment' : '',
1153 'query' : '',
1154 'absolute' : False,
1155 'alias' : False,
1156 'prefix' : ''
1157 });
1158 if (not php.isset(options, 'external')):
1159
1160
1161
1162 colonpos = php.strpos(path, ':');
1163 options['external'] = (colonpos != False and \
1164 php.preg_match('![/?#]!', php.substr(path, 0, colonpos) == 0) and \
1165 filter_xss_bad_protocol(path, False) == check_plain(path));
1166
1167 if (php.function_exists('language_url_rewrite')):
1168 language_url_rewrite(path, options);
1169 if (not php.empty(options['fragment'])):
1170 options['fragment'] = '#' + options['fragment'];
1171 if (php.is_array(options['query'])):
1172 options['query'] = drupal_query_string_encode(options['query']);
1173 if (not php.empty(options['external'])):
1174
1175 if (php.strpos(path, '#') != False):
1176 p1_ = php.explode('#', path, 2);
1177 (path, old_fragment)
1178 if php.isset(p1_, 0):
1179 path = p1_[0];
1180 if php.isset(p1_, 1):
1181 old_fragment = p1_[1];
1182 else:
1183 old_fragment = None;
1184 if (old_fragment != None and php.empty(options['fragment'])):
1185 options['fragment'] = '#' + old_fragment;
1186
1187 if (not php.empty(options['query'])):
1188 path += ('&' if (php.strpos(path, '?') != False) else '?') + \
1189 options['query'];
1190
1191 return path + options['fragment'];
1192 if (url.script == None):
1193
1194
1195
1196 url.script = ('index.php' if \
1197 (php.strpos(php.SERVER['php.SERVER_SOFTWARE'], 'Apache') == \
1198 False) else '');
1199
1200 if (url.clean_url == None):
1201 url.clean_url = drupy_bool(variable_get('clean_url', '0'));
1202 if (not php.isset(options, 'base_url')):
1203
1204 options['base_url'] = settings.base_url;
1205
1206 original_path = path;
1207
1208 if (path == '<front>'):
1209 path = '';
1210 elif (not php.empty(path) and not options['alias']):
1211 path = drupal_get_path_alias(path, (options['language'].language if \
1212 php.isset(options, 'language') else ''));
1213 if (php.function_exists('custom_url_rewrite_outbound')):
1214
1215 custom_url_rewrite_outbound(path, options, original_path);
1216 base = ((options['base_url'] + '/') if \
1217 options['absolute'] else base_path());
1218 prefix = (php.rtrim(options['prefix'], '/') if php.empty(path) else \
1219 options['prefix']);
1220 path = drupal_urlencode(prefix . path);
1221 if (clean_url):
1222
1223 if (options['query']):
1224 return base + path + '?' + options['query'] + options['fragment'];
1225 else:
1226 return base + path + options['fragment'];
1227 else:
1228
1229 variables = [];
1230 if (not php.empty(path)):
1231 variables.append( 'q=' + path );
1232 if (not php.empty(options['query'])):
1233 variables.append( options['query'] );
1234 query = php.implode('&', variables);
1235 if (len(query) > 0):
1236 return base + url.script + '?' + query + options['fragment'];
1237 else:
1238 return base + options['fragment'];
1239
1240
1241
1243 """
1244 Format an attribute string to insert in a tag.
1245
1246 @param attributes
1247 An associative array of HTML attributes.
1248 @return
1249 An HTML string ready for insertion in a tag.
1250 """
1251 if (php.is_array(attributes)):
1252 t = '';
1253 for key,value in attributes.items():
1254 t += ' %s=%s' % (key, check_plain(value));
1255 return t;
1256
1257
1258 -def l(text, path, options = {}):
1259 """
1260 Format an internal Drupal link.
1261
1262 This function correctly handles aliased paths, and allows
1263 themes to highlight
1264 links to the current page correctly, so all internal links output by plugins
1265 should be generated by this function if possible.
1266
1267 @param text
1268 The text to be enclosed with the anchor tag.
1269 @param path
1270 The Drupal path being linked to, such as "admin/content/node". Can be an
1271 external or internal URL.
1272 - If you provide the full URL, it will be considered an external URL.
1273 - If you provide only the path (e.g. "admin/content/node"), it is
1274 considered an internal link. In this case, it must be a system URL
1275 as the url() function will generate the alias.
1276 - If you provide '<front>', it generates a link to the site's
1277 base URL (again via the url() function).
1278 - If you provide a path, and 'alias' is set to True (see below), it is
1279 used as is.
1280 @param options
1281 An associative array of additional options, with the following keys:
1282 'attributes'
1283 An associative array of HTML attributes to apply to the anchor tag.
1284 'query'
1285 A query string to append to the link, or an array of query key/value
1286 properties.
1287 'fragment'
1288 A fragment identifier (named anchor) to append to the link.
1289 Do not include the '#' character.
1290 'absolute' (default False)
1291 Whether to force the output to be an absolute link (beginning with
1292 http:). Useful for links that will be displayed outside the site, such
1293 as in an RSS feed.
1294 'html' (default False)
1295 Whether the title is HTML, or just plain-text. For example for making
1296 an image a link, this must be set to True, or else you will see the
1297 escaped HTML.
1298 'alias' (default False)
1299 Whether the given path is an alias already.
1300 @return
1301 an HTML string containing a link to the given path.
1302 """
1303
1304 options = php.array_merge(options, {
1305 'attributes' : {},
1306 'html' : False,
1307 });
1308
1309 if ((path == php.GET['q']) or (path == '<front>' and \
1310 drupal_is_front_page())):
1311 if (php.isset(options['attributes']['class'])):
1312 options['attributes']['class'] += ' active';
1313 else:
1314 options['attributes']['class'] = 'active';
1315
1316
1317
1318
1319 if (php.isset(options['attributes'], 'title') and \
1320 php.strpos(options['attributes']['title'], '<') != False):
1321 options['attributes']['title'] = strip_tags(options['attributes']['title'])
1322 return '<a href="' + check_url(url(path, options)) + '"' + \
1323 drupal_attributes(options['attributes']) + '>' + \
1324 (text if options['html'] else check_plain(text)) + '</a>';
1325
1326
1327
1340
1341
1342
1344 """
1345 Form an associative array from a linear array.
1346
1347 This function walks through the provided array and constructs an associative
1348 array out of it. The keys of the resulting array will be the values of the
1349 input array. The values will be the same as the keys unless a function is
1350 specified, in which case the output of the function is used for the values
1351 instead.
1352
1353 @param array
1354 A linear array.
1355 @param function
1356 A name of a function to apply to all values before output.
1357 @result
1358 An associative array.
1359 """
1360 if (function != None):
1361 result = {};
1362 for key,value in array_.items():
1363 result[value] = value;
1364 return result;
1365 elif (php.function_exists(function)):
1366 result = {};
1367 for key,value in array_.items():
1368 result[value] = function(value);
1369 return result;
1370
1371
1372
1374 """
1375 Evaluate a string of PHP code.
1376
1377 This is a wrapper around PHP's eval(). It uses output
1378 buffering to capture both
1379 returned and printed text. Unlike eval(), we require code to
1380 be surrounded by
1381 <?php ?> tags; in other words, we evaluate the code as if it
1382 were a stand-alone
1383 PHP file.
1384
1385 Using this wrapper also ensures that the PHP code which is
1386 evaluated can not
1387 overwrite any variables in the calling code, unlike a regular eval() call.
1388
1389 @param code
1390 The code to evaluate.
1391 @return
1392 A string containing the printed output of the code,
1393 followed by the returned
1394 output of the code.
1395 """
1396
1397 old_theme_path = lib_appglobals.theme_path;
1398
1399
1400
1401 if (not php.isset(locals(), lib_appglobals.theme_info, True)):
1402 lib_appglobals.theme_path = \
1403 drupal_get_path('theme', settings.conf['theme_default']);
1404 else:
1405 lib_appglobals.theme_path = php.dirname(lib_appglobals.theme_info.filename);
1406 ob_start();
1407 exec(code);
1408 output = ob_get_clean();
1409
1410 lib_appglobals.theme_path = old_theme_path;
1411 return output;
1412
1413
1414
1416 """
1417 Returns the path to a system item (plugin, theme, etc.).
1418
1419 @param type
1420 The type of the item (i.e. theme, theme_engine, plugin).
1421 @param name
1422 The name of the item for which the path is requested.
1423
1424 @return
1425 The path to the requested item.
1426 """
1427 return lib_bootstrap.drupal_get_filename(type_, name)
1428
1429
1430
1432 """
1433 Returns the base URL path of the Drupal installation.
1434 At the very least, this will always default to /.
1435 """
1436 return lib_appglobals.base_path
1437
1438
1444
1445
1446 -def drupal_add_css(path = None, type = 'plugin', media = 'all', \
1447 preprocess = True):
1448 """
1449 Adds a CSS file to the stylesheet queue.
1450
1451 @param path
1452 (optional) The path to the CSS file relative to the base_path(), e.g.,
1453 /plugins/devel/devel.css.
1454
1455 Modules should always prefix the names of their CSS files with the plugin
1456 name, for example: system-menus.css rather than simply menus.css. Themes
1457 can override plugin-supplied CSS files based on their filenames, and this
1458 prefixing helps prevent confusing name collisions for theme developers.
1459 See drupal_get_css where the overrides are performed.
1460
1461 If the direction of the current language is right-to-left (Hebrew,
1462 Arabic, etc.), the function will also look for an RTL CSS file and append
1463 it to the list. The name of this file should have an '-rtl.css' suffix.
1464 For example a CSS file called 'name.css' will have a 'name-rtl.css'
1465 file added to the list, if exists in the same directory. This CSS file
1466 should contain overrides for properties which should be reversed or
1467 otherwise different in a right-to-left display.
1468 @param type
1469 (optional) The type of stylesheet that is being added. Types are: plugin
1470 or theme.
1471 @param media
1472 (optional) The media type for the stylesheet, e.g., all, print, screen.
1473 @param preprocess
1474 (optional) Should this CSS file be aggregated and compressed if this
1475 feature has been turned on under the performance section?
1476
1477 What does this actually mean?
1478 CSS preprocessing is the process of aggregating a bunch of separate CSS
1479 files into one file that is then compressed by removing all extraneous
1480 white space.
1481
1482 The reason for merging the CSS files is outlined quite thoroughly here:
1483 http://www.die.net/musings/page_load_time/
1484 "Load fewer external objects. Due to request overhead, one bigger file
1485 just loads faster than two smaller ones half its size."
1486
1487 However, you should *not* preprocess every file as this can lead to
1488 redundant caches. You should set preprocess = False when:
1489
1490 - Your styles are only used rarely on the site. This could be a special
1491 admin page, the homepage, or a handful of pages that
1492 does not represent
1493 the majority of the pages on your site.
1494
1495 Typical candidates for caching are for example styles for nodes across
1496 the site, or used in the theme.
1497 @return
1498 An array of CSS files.
1499 """
1500 php.static(drupal_add_css, 'css', {})
1501
1502
1503
1504 if (path != None):
1505
1506
1507 if (not php.isset(drupal_add_css.css, media)):
1508 drupal_add_css.css[media] = {'plugin' : {}, 'theme' : {}};
1509 drupal_add_css.css[media][type][path] = preprocess;
1510
1511 if (php.defined('LANGUAGE_RTL') and \
1512 lib_appglobals.language.direction == LANGUAGE_RTL):
1513 rtl_path = php.str_replace('.css', '-rtl.css', path);
1514 if (php.file_exists(rtl_path)):
1515 drupal_add_css.css[media][type][rtl_path] = preprocess;
1516 return drupal_add_css.css;
1517
1518
1519
1521 """
1522 Returns a themed representation of all stylesheets that
1523 should be attached to the page.
1524
1525 It loads the CSS in order, with 'plugin' first, then 'theme' afterwards.
1526 This ensures proper cascading of styles so themes can easily override
1527 plugin styles through CSS selectors.
1528
1529 Themes may replace plugin-defined CSS files by adding a stylesheet with the
1530 same filename. For example, themes/garland/system-menus.css would replace
1531 plugins/system/system-menus.css. This allows themes to override complete
1532 CSS files, rather than specific selectors, when necessary.
1533
1534 If the original CSS file is being overridden by a theme, the theme is
1535 responsible for supplying an accompanying RTL CSS file to replace the
1536 plugin's.
1537
1538 @param css
1539 (optional) An array of CSS files. If no array is provided, the default
1540 stylesheets array is used instead.
1541 @return
1542 A string of XHTML CSS tags.
1543 """
1544 output = '';
1545 if (css == None):
1546 css = drupal_add_css();
1547 no_plugin_preprocess = '';
1548 no_theme_preprocess = '';
1549 preprocess_css = ((variable_get('preprocess_css', False) and \
1550 (not php.defined('MAINTENANCE_MODE') or MAINTENANCE_MODE != 'update')));
1551 directory = file_directory_path();
1552 is_writable = (php.is_dir(directory) and php.is_writable(directory) and \
1553 (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == \
1554 FILE_DOWNLOADS_PUBLIC));
1555
1556
1557
1558
1559 query_string = '?' + php.substr(\
1560 variable_get('css_js_query_string', '0'), 0, 1);
1561 for media,types in css.items():
1562
1563
1564
1565 for type_,files in types.items():
1566 if (type_ == 'plugin'):
1567
1568 theme_styles = [];
1569 for theme_style in php.array_keys(css[media]['theme']):
1570 theme_styles.append( basename(theme_style) );
1571 for file_,preprocess in types[type_].items():
1572
1573
1574
1575
1576 if (type_ == 'plugin' and php.in_array(\
1577 php.str_replace('-rtl.css', '.css', \
1578 basename(file)), theme_styles)):
1579
1580
1581 del(types[type_][file]);
1582 continue;
1583
1584 if (lib_file.exists(file)):
1585 if (not preprocess and not (is_writable and preprocess_css)):
1586
1587
1588
1589 if (not preprocess and type_ == 'module'):
1590 no_module_preprocess += \
1591 '<link type="text/css" rel="stylesheet" media="' + media + \
1592 '" href="' + base_path() + file + query_string + '" />' + "\n"
1593
1594
1595
1596 elif (not preprocess and type_ == 'theme'):
1597 no_theme_preprocess += \
1598 '<link type="text/css" rel="stylesheet" media="' + \
1599 media + '" href="' + base_path() + file + \
1600 query_string + '" />' + "\n";
1601 else:
1602 output += '<link type="text/css" rel="stylesheet" media="' + \
1603 media + '" href="' + base_path() + file + query_string + \
1604 '" />' + "\n";
1605 if (is_writable and preprocess_css):
1606 filename = php.md5(php.serialize(types) + query_string) + '.css';
1607 preprocess_file = drupal_build_css_cache(types, filename);
1608 output += '<link type="text/css" rel="stylesheet" media="' + media + \
1609 '" href="' + base_path() + preprocess_file + '" />' + "\n";
1610 return no_plugin_preprocess . output . no_theme_preprocess;
1611
1612
1613
1615 """
1616 Aggregate and optimize CSS files, putting them in the files directory.
1617
1618 @param types
1619 An array of types of CSS files (e.g., screen, print) to aggregate and
1620 compress into one file.
1621 @param filename
1622 The name of the aggregate CSS file.
1623 @return
1624 The name of the CSS file.
1625 """
1626 data = '';
1627
1628 csspath = file_create_path('css');
1629 file_check_directory(csspath, FILE_CREATE_DIRECTORY);
1630 if (not php.file_exists(csspath + '/' + filename)):
1631
1632 for type_ in types:
1633 for file_,cache in type_.items():
1634 if (not php.empty(cache)):
1635 contents = drupal_load_stylesheet(file_, True);
1636
1637 base = base_path() + php.dirname(file_) + '/';
1638 _drupal_build_css_path(None, base);
1639
1640
1641 data += php.preg_replace_callback(\
1642 '/url\([\'"]?(?![a-z]+:|\/+)([^\'")]+)[\'"]?\)/i', \
1643 '_drupal_build_css_path', contents);
1644
1645
1646
1647 regexp = '/@import[^;]+;/i';
1648 matches = []
1649 php.preg_match_all(regexp, data, matches);
1650 data = php.preg_replace(regexp, '', data);
1651 data = php.implode('', matches[0]) . data;
1652
1653 file_save_data(data, csspath + '/' + filename, FILE_EXISTS_REPLACE);
1654 return csspath + '/' + filename;
1655
1656
1657
1673
1674
1675
1677 """
1678 Loads the stylesheet and resolves all @import commands.
1679
1680 Loads a stylesheet and replaces @import commands with the contents of the
1681 imported file. Use this instead of file_get_contents when processing
1682 stylesheets.
1683
1684 The returned contents are compressed removing white space and comments only
1685 when CSS aggregation is enabled. This optimization will not apply for
1686 color.plugin enabled themes with CSS aggregation turned off.
1687
1688 @param file
1689 Name of the stylesheet to be processed.
1690 @param optimize
1691 Defines if CSS contents should be compressed or not.
1692 @return
1693 Contents of the stylesheet including the imported stylesheets.
1694 """
1695 php.static(drupal_load_stylesheet, 'optimize_', optimize)
1696
1697
1698 contents = '';
1699 if (php.file_exists(file)):
1700
1701 contents = file_get_contents(file);
1702
1703 cwd = getcwd();
1704 chdir(php.dirname(file));
1705
1706
1707 contents = php.preg_replace_callback(\
1708 '/@import\s*(?:url\()?[\'"]?(?![a-z]+:)([^\'"\()]+)[\'"]?\)?;/', \
1709 '_drupal_load_stylesheet', contents);
1710
1711
1712 contents = php.preg_replace('/^@charset\s+[\'"](\S*)\b[\'"];/i', '', \
1713 contents);
1714 if (not php.empty(drupal_load_stylesheet.optimize_)):
1715
1716 contents = php.preg_replace(
1717 '<' +
1718
1719
1720 "\s*([@{}:;,]|\)\s|\s\()\s* |" + \
1721
1722 '/\*([^*\\\\]|\*(?!/))+\*/ |' + \
1723
1724 '[\n\r]' + \
1725 '>x', \
1726 '\1', \
1727 contents \
1728 );
1729
1730 chdir(cwd);
1731 return contents;
1732
1733
1734
1736 """
1737 Loads stylesheets recursively and returns contents with corrected paths.
1738
1739 This function is used for recursive loading of stylesheets and
1740 returns the stylesheet content with all url() paths corrected.
1741 """
1742 filename = matches[1];
1743
1744 file = drupal_load_stylesheet(filename);
1745
1746 return php.preg_replace('/url\(([\'"]?)(?![a-z]+:)([^\'")]+)[\'"]?\)?;/i', \
1747 'url(\1' + php.dirname(filename) + '/', file);
1748
1749
1751 """
1752 Delete all cached CSS files.
1753 """
1754 file_scan_directory(file_create_path('css'), '.*', ['.', '..', 'CVS'], \
1755 'file_delete', True);
1756
1757
1758 -def drupal_add_js(data = None, type_ = 'plugin', scope = 'php.header', \
1759 defer = False, cache = True, preprocess = True):
1760 """
1761 Add a JavaScript file, setting or inline code to the page.
1762
1763 The behavior of this function depends on the parameters it is called with.
1764 Generally, it handles the addition of JavaScript to the page, either as
1765 reference to an existing file or as inline code.
1766 The following actions can be
1767 performed using this function:
1768
1769 - Add a file ('core', 'plugin' and 'theme'):
1770 Adds a reference to a JavaScript file to the page. JavaScript files
1771 are placed in a certain order, from 'core' first, to 'plugin' and finally
1772 'theme' so that files, that are added later, can override previously added
1773 files with ease.
1774
1775 - Add inline JavaScript code ('inline'):
1776 Executes a piece of JavaScript code on the current page by
1777 placing the code
1778 directly in the page. This can, for example, be useful
1779 to tell the user that
1780 a new message arrived, by opening a pop up, alert box etc.
1781
1782 - Add settings ('setting'):
1783 Adds a setting to Drupal's global storage of JavaScript settings. Per-page
1784 settings are required by some plugins to function properly. The settings
1785 will be accessible at Drupal.settings.
1786
1787 @param data
1788 (optional) If given, the value depends on the type parameter:
1789 - 'core', 'plugin' or 'theme': Path to the file relative to base_path().
1790 - 'inline': The JavaScript code that should be placed in the given scope.
1791 - 'setting': An array with configuration options as associative array. The
1792 array is directly placed in Drupal.settings.
1793 You might want to wrap your
1794 actual configuration settings in another variable to
1795 prevent the pollution
1796 of the Drupal.settings namespace.
1797 @param type
1798 (optional) The type of JavaScript that should be added to
1799 the page. Allowed
1800 values are 'core', 'plugin', 'theme', 'inline' and 'setting'. You
1801 can, however, specify any value. It is treated as a reference
1802 to a JavaScript
1803 file. Defaults to 'plugin'.
1804 @param scope
1805 (optional) The location in which you want to place the script. Possible
1806 values are 'php.header' and 'footer' by default. If your theme implements
1807 different locations, however, you can also use these.
1808 @param defer
1809 (optional) If set to True, the defer attribute is set on the <script> tag.
1810 Defaults to False. This parameter is not used with type == 'setting'.
1811 @param cache
1812 (optional) If set to False, the JavaScript file is loaded
1813 anew on every page
1814 call, that means, it is not cached. Defaults to True. Used only when type
1815 references a JavaScript file.
1816 @param preprocess
1817 (optional) Should this JS file be aggregated if this
1818 feature has been turned on under the performance section?
1819 @return
1820 If the first parameter is None, the JavaScript array that
1821 has been built so
1822 far for scope is returned. If the first three parameters are None,
1823 an array with all scopes is returned.
1824 """
1825 php.static(drupal_add_js, 'javascript', {})
1826 if (data != None):
1827
1828
1829 if (php.empty(drupal_add_js.javascript)):
1830 javascript['php.header'] = {
1831 'core' : {
1832 'misc/jquery.js' : {'cache' : True, 'defer' : False, \
1833 'preprocess' : True},
1834 'misc/drupal.js' : {'cache' : True, 'defer' : False, \
1835 'preprocess' : True}
1836 },
1837 'plugin' : {},
1838 'theme' : {},
1839 'setting' : [
1840 {'basePath' : base_path()}
1841 ],
1842 'inline' : {},
1843 };
1844 if (not php.empty(scope) and not \
1845 php.isset(drupal_add_js.javascript, scope)):
1846 drupal_add_js.javascript[scope] = {'core' : {}, 'plugin' : {}, \
1847 'theme' : {}, 'setting' : {}, 'inline' : {}};
1848 if (not php.empty(type) and not php.empty(scope) and not \
1849 php.isset(drupal_add_js.javascript[scope], type_)):
1850 drupal_add_js.javascript[scope][type_] = [];
1851 if type == 'setting':
1852 drupal_add_js.javascript[scope][type_].append(data);
1853 elif type == 'inline':
1854 drupal_add_js.javascript[scope][type_].append({'code' : data, \
1855 'defer' : defer});
1856 else:
1857
1858 drupal_add_js.javascript[scope][type_][data] = {'cache' : \
1859 cache, 'defer' : defer, 'preprocess' : (False if not \
1860 cache else preprocess)};
1861 if (not php.empty(scope)):
1862 if (php.isset(drupal_add_js.javascript, scope)):
1863 return drupal_add_js.javascript[scope];
1864 else:
1865 return {};
1866 else:
1867 return drupal_add_js.javascript;
1868
1869
1870
1871
1873 """
1874 Returns a themed presentation of all JavaScript code for the current page.
1875
1876 References to JavaScript files are placed in a certain order: first, all
1877 'core' files, then all 'plugin' and finally all 'theme' JavaScript files
1878 are added to the page. Then, all settings are output, followed by 'inline'
1879 JavaScript code. If running update.php, all preprocessing is disabled.
1880
1881 @parameter scope
1882 (optional) The scope for which the JavaScript rules should be returned.
1883 Defaults to 'php.header'.
1884 @parameter javascript
1885 (optional) An array with all JavaScript code. Defaults to the default
1886 JavaScript array for the given scope.
1887 @return
1888 All JavaScript code segments and includes for the scope as HTML tags.
1889 """
1890 if ((not php.defined('MAINTENANCE_MODE') or \
1891 MAINTENANCE_MODE != 'update') and \
1892 php.function_exists('locale_update_js_files')):
1893 locale_update_js_files();
1894 if (javascript == None):
1895 javascript = drupal_add_js(None, None, scope);
1896 if (php.empty(javascript)):
1897 return '';
1898 output = '';
1899 preprocessed = '';
1900 no_preprocess = {'core' : '', 'plugin' : '', 'theme' : ''};
1901 files = {};
1902 preprocess_js = (variable_get('preprocess_js', False) and \
1903 (not php.defined('MAINTENANCE_MODE') or MAINTENANCE_MODE != 'update'));
1904 directory = file_directory_path();
1905 is_writable_ = php.is_dir(directory) and php.is_writable(directory) and \
1906 (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == \
1907 FILE_DOWNLOADS_PUBLIC);
1908
1909
1910
1911
1912
1913
1914 query_string = '?' + \
1915 php.substr(variable_get('css_js_query_string', '0'), 0, 1);
1916
1917
1918
1919 embed_prefix = "\n<!--//--><![CDATA[//><!--\n"
1920 embed_suffix = "\n//--><!]]>\n"
1921 for type_,data in javascript.items():
1922 if (php.empty(data)):
1923 continue;
1924 if type_ == 'setting':
1925 output += '<script type="text/javascript">' + embed_prefix + \
1926 'jQuery.extend(Drupal.settings, ' + \
1927 drupal_to_js(\
1928 php.call_user_func_array('array_merge_recursive', data)) + \
1929 ");" + embed_suffix + "</script>\n";
1930 elif type_ == 'inline':
1931 for infoKey_,info in data.items():
1932 output += '<script type="text/javascript"' + \
1933 (' defer="defer"' if info['defer'] else '') + '>' + \
1934 embed_prefix + info['code'] + embed_suffix + "</script>\n";
1935 else:
1936
1937
1938
1939 for path,info in data.items():
1940 if (not info['preprocess'] or not is_writable_ or not preprocess_js):
1941 no_preprocess[type] += '<script type="text/javascript"' + \
1942 (' defer="defer"' if info['defer'] else '') + ' src="' + \
1943 base_path() + path + (query_string if info['cache'] else \
1944 '?' + drupy_time()) + "\"></script>\n";
1945 else:
1946 files[path] = info;
1947
1948 if (is_writable and preprocess_js and php.count(files) > 0):
1949 filename = php.md5(php.serialize(files) + query_string) + '.js'
1950 preprocess_file = drupal_build_js_cache(files, filename)
1951 preprocessed += '<script type="text/javascript" src="' + base_path() + \
1952 preprocess_file + '"></script>' + "\n"
1953
1954
1955
1956
1957 output = preprocessed + php.implode('', no_preprocess) + output;
1958 return output;
1959
1960
1961
1962 -def drupal_add_tabledrag(table_id, action, relationship, group, \
1963 subgroup = None, source = None, hidden = True, limit = 0):
1964 """
1965 Assist in adding the tableDrag JavaScript behavior to a themed table.
1966
1967 Draggable tables should be used wherever an outline
1968 or list of sortable items
1969 needs to be arranged by an end-user. Draggable tables are very flexible and
1970 can manipulate the value of form elements placed within individual columns.
1971
1972 To set up a table to use drag and drop in place of weight select-lists or
1973 in place of a form that contains parent relationships, the form must be
1974 themed into a table. The table must have an id attribute set. If using
1975 theme_table(), the id may be set as such:
1976 @code
1977 output = theme('table', %(php.header)s, rows, array('id' : \
1978 'my-plugin-table'));
1979 return output;
1980 @endcode
1981
1982 In the theme function for the form, a special class must be added to each
1983 form element within the same column, "grouping" them together.
1984
1985 In a situation where a single weight column is
1986 being sorted in the table, the
1987 classes could be added like this (in the theme function):
1988 @code
1989 form['my_elements'][%(delta)s]['weight']['#attributes']['class'] = \
1990 "my-elements-weight";
1991 @endcode
1992
1993 Each row of the table must also have a class of "draggable"
1994 in order to enable the
1995 drag handles:
1996 @code
1997 row = array(...);
1998 rows[] = array(
1999 'data' : row,
2000 'class' : 'draggable',
2001 );
2002 @endcode
2003
2004 When tree relationships are present, the two additional classes
2005 'tabledrag-leaf' and 'tabledrag-root' can be used to refine the behavior:
2006 - Rows with the 'tabledrag-leaf' class cannot have child rows.
2007 - Rows with the 'tabledrag-root' class cannot be nested under a parent row.
2008
2009 Calling drupal_add_tabledrag() would then be written as such:
2010 @code
2011 drupal_add_tabledrag('my-plugin-table', 'order', 'sibling', \
2012 'my-elements-weight');
2013 @endcode
2014
2015 In a more complex case where there are several groups in one column (such as
2016 the block regions on the admin/build/block page), a separate subgroup class
2017 must also be added to differentiate the groups.
2018 @code
2019 form['my_elements'][%(region)s][delta]['weight']['#attributes']['class'] = \
2020 "my-elements-weight my-elements-weight-". region;
2021 @endcode
2022
2023 group is still 'my-element-weight', and the additional subgroup variable
2024 will be passed in as 'my-elements-weight-'. region. This also means that
2025 you'll need to call drupal_add_tabledrag() once for every region added.
2026
2027 @code
2028 foreach (regions as region) {
2029 drupal_add_tabledrag('my-plugin-table', 'order', 'sibling', \
2030 'my-elements-weight', 'my-elements-weight-'. region);
2031 }
2032 @endcode
2033
2034 In a situation where tree relationships are present, adding multiple
2035 subgroups is not necessary, because the table will contain indentations that
2036 provide enough information about the sibling and parent relationships.
2037 See theme_menu_overview_form() for an example creating a table containing
2038 parent relationships.
2039
2040 Please note that this function should be called from the theme layer,
2041 such as
2042 in a .tpl.php file, theme_ function, or in a template_preprocess function,
2043 not in a form declartion. Though the same JavaScript could be added to the
2044 page using drupal_add_js() directly, this function helps keep template files
2045 clean and readable. It also prevents tabledrag.js from being added twice
2046 accidentally.
2047
2048 @param table_id
2049 String containing the target table's id attribute. If the table does not
2050 have an id, one will need to be set, such as <table id="my-plugin-table">.
2051 @param action
2052 String describing the action to be done on the form item. Either 'match'
2053 'depth', or 'order'. Match is typically used for parent relationships.
2054 Order is typically used to set weights on other form elements with
2055 the same
2056 group. Depth updates the target element with the current indentation.
2057 @param relationship
2058 String describing where the action variable should be performed. Either
2059 'parent', 'sibling', 'group', or 'self'. Parent will only look for fields
2060 up the tree. Sibling will look for fields in the same group in rows above
2061 and below it. Self affects the dragged row itself. Group affects the
2062 dragged row, plus any children below it (the entire dragged group).
2063 @param group
2064 A class name applied on all related form elements for this action.
2065 @param subgroup
2066 (optional) If the group has several subgroups within it,
2067 this string should
2068 contain the class name identifying fields in the same subgroup.
2069 @param source
2070 (optional) If the action is 'match', this string should contain the class
2071 name identifying what field will be used as the source value when matching
2072 the value in subgroup.
2073 @param hidden
2074 (optional) The column containing the field elements may be entirely hidden
2075 from view dynamically when the JavaScript is loaded. Set to False if the
2076 column should not be hidden.
2077 @param limit
2078 (optional) Limit the maximum amount of parenting in this table.
2079 @see block-admin-display-form.tpl.php
2080 @see theme_menu_overview_form()
2081 """
2082 php.static(drupal_add_tabledrag, 'js_added', False)
2083 if (not drupal_add_tabledrag.js_added):
2084 drupal_add_js('misc/tabledrag.js', 'core');
2085 drupal_add_tabledrag.js_added = True;
2086
2087 target = (subgroup if (subgroup != None) else group);
2088 source = (source if source != None else target);
2089 settings['tableDrag'][table_id][group].append({
2090 'target' : target,
2091 'source' : source,
2092 'relationship' : relationship,
2093 'action' : action,
2094 'hidden' : hidden,
2095 'limit' : limit,
2096 });
2097 drupal_add_js(settings, 'setting');
2098
2099
2100
2102 """
2103 Aggregate JS files, putting them in the files directory.
2104
2105 @param files
2106 An array of JS files to aggregate and compress into one file.
2107 @param filename
2108 The name of the aggregate JS file.
2109 @return
2110 The name of the JS file.
2111 """
2112 contents = '';
2113
2114 jspath = file_create_path('js');
2115 file_check_directory(jspath, FILE_CREATE_DIRECTORY);
2116 if (not php.file_exists(jspath + '/' + filename)):
2117
2118 for path,info in files.items():
2119 if (not php.empty(info['preprocess'])):
2120
2121
2122 contents += file_get_contents(path) + ';';
2123
2124 file_save_data(contents, jspath + '/' + filename, FILE_EXISTS_REPLACE);
2125 return jspath + '/' + filename;
2126
2127
2129 """
2130 Delete all cached JS files.
2131 """
2132 file_scan_directory(file_create_path('js'), '.*', array('.', '..', 'CVS'), \
2133 'file_delete', True);
2134 variable_set('javascript_parsed', array());
2135
2136
2137
2139 """
2140 Converts a PHP variable into its Javascript equivalent.
2141
2142 We use HTML-safe strings, i.e. with <, > and & escaped.
2143 """
2144
2145
2146 return php.str_replace(array("<", ">", "&"), \
2147 array('\x3c', '\x3e', '\x26'), json_encode(var));
2148
2149
2150
2152 """
2153 Return data in JSON format.
2154
2155 This function should be used for JavaScript callback functions returning
2156 data in JSON format. It sets the php.header for JavaScript output.
2157
2158 @param var
2159 (optional) If set, the variable will be converted to JSON and output.
2160 """
2161
2162 drupal_set_header('Content-Type: text/javascript; charset=utf-8');
2163 if (var != None):
2164 print drupal_to_js(var);
2165
2166
2167
2169 """
2170 Wrapper around urlencode() which avoids Apache quirks.
2171
2172 Should be used when placing arbitrary data in an URL. Note that Drupal paths
2173 are urlencoded() when passed through url() and do not require urlencoding()
2174 of individual components.
2175
2176 Notes:
2177 - For esthetic reasons, we do not escape slashes.
2178 This also avoids a 'feature'
2179 in Apache where it 404s on any path containing '%2F'.
2180 - mod_rewrite unescapes %-encoded ampersands, hashes, and slashes when clean
2181 URLs are used, which are interpreted as delimiters by PHP. These
2182 characters are double escaped so PHP will still see the encoded version.
2183 - With clean URLs, Apache changes '//' to '/', so every second slash is
2184 double escaped.
2185
2186 @param text
2187 String to encode
2188 """
2189 if (variable_get('clean_url', '0')):
2190 return php.str_replace(
2191 ['%2F', '%26', '%23', '//'],
2192 ['/', '%2526', '%2523', '/%252F'],
2193 rawurlencode(text)
2194 );
2195 else:
2196 return php.str_replace('%2F', '/', rawurlencode(text));
2197
2198
2199
2201 """
2202 Returns a string of highly randomized bytes (over the full 8-bit range).
2203
2204 This function is better than simply
2205 calling php.mt_rand() or any other built-in
2206 PHP function because it can return a long string of bytes (compared to < 4
2207 bytes normally from php.mt_rand()) and uses the
2208 best available pseudo-random source.
2209
2210 @param $count
2211 The number of characters (bytes) to return in the string.
2212 """
2213
2214 php.static(drupal_random_bytes, 'random_state', getmypid())
2215 output = '';
2216
2217
2218 fh = fopen('/dev/urandom', 'rb')
2219 if (fh != False):
2220 output = fread(fh, count_);
2221 fclose(fh);
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231 while (php.strlen(output) < count_):
2232 drupal_random_bytes.random_state = php.md5(php.microtime() + \
2233 php.mt_rand() + drupal_random_bytes.random_state);
2234 output += php.md5(php.mt_rand() + drupal_random_bytes.random_state, True);
2235 return php.substr(output, 0, count);
2236
2237
2239 """
2240 Ensure the private key variable used to generate tokens is set.
2241
2242 @return
2243 The private key.
2244 """
2245 key = variable_get('drupal_private_key', 0);
2246 if (not key):
2247 key = php.md5(drupal_random_bytes(64));
2248 variable_set('drupal_private_key', key);
2249 return key;
2250
2251
2252
2254 """
2255 Generate a token based on value, the current user session and private key.
2256
2257 @param value
2258 An additional value to base the token on.
2259 """
2260 private_key = drupal_get_private_key();
2261 return php.md5(session_id() + value + private_key);
2262
2263
2264
2266 """
2267 Validate a token based on value, the current user session and private key.
2268
2269 @param token
2270 The token to be validated.
2271 @param value
2272 An additional value to base the token on.
2273 @param skip_anonymous
2274 Set to true to skip token validation for anonymous users.
2275 @return
2276 True for a valid token, false for an invalid token. When skip_anonymous
2277 is true, the return value will always be true for anonymous users.
2278 """
2279 return ((skip_anonymous and lib_appglobals.user.uid == 0) or \
2280 (token == php.md5(session_id() + value + \
2281 variable_get('drupal_private_key', ''))));
2282
2283
2284
2286 """
2287 Performs one or more XML-RPC request(s).
2288
2289 @param url
2290 An absolute URL of the XML-RPC endpoint.
2291 Example:
2292 http://www.example.com/xmlrpc.php
2293 @param ...
2294 For one request:
2295 The method name followed by a variable number
2296 of arguments to the method.
2297 For multiple requests (system.multicall):
2298 An array of call arrays. Each call array follows the
2299 pattern of the single
2300 request: method name followed by the arguments to the method.
2301 @return
2302 For one request:
2303 Either the return value of the method on success, or False.
2304 If False is returned, see xmlrpc_errno() and xmlrpc_error_msg().
2305 For multiple requests:
2306 An array of results. Each result will either be the result
2307 returned by the method called, or an xmlrpc_error object if the call
2308 failed. See xmlrpc_error().
2309 """
2310 php.require_once('./includes/xmlrpc.inc');
2311 args = func_get_args();
2312 return php.call_user_func_array('_xmlrpc', args);
2313
2314
2315
2316
2333
2334
2335
2337 """
2338 Store the current page in the cache.
2339
2340 We try to store a gzipped version of the cache. This requires the
2341 PHP zlib extension (http://php.net/manual/en/ref.zlib.php).
2342 Presence of the extension is checked by testing for the function
2343 gzencode. There are two compression algorithms: gzip and deflate.
2344 The majority of all modern browsers support gzip or both of them.
2345 We thus only deal with the gzip variant and unzip the cache in case
2346 the browser does not accept gzip encoding.
2347
2348 @see drupal_page_header
2349 """
2350 if (not lib_appglobals.user.uid and (php.SERVER['REQUEST_METHOD'] == 'GET' or \
2351 php.SERVER['REQUEST_METHOD'] == 'HEAD') and \
2352 php.count(drupal_get_messages(None, False)) == 0):
2353
2354 data = ob_get_contents();
2355 if (not php.empty(data = ob_get_contents())):
2356 cache = True;
2357 if (variable_get('page_compression', True) and \
2358 php.function_exists('gzencode')):
2359
2360
2361 if (zlib_get_coding_type() == 'deflate'):
2362 cache = False;
2363 elif (zlib_get_coding_type() == False):
2364 data = php.gzencode(data, 9, FORCE_GZIP);
2365
2366
2367 ob_end_flush();
2368 if (cache and data):
2369 cache_set(lib_appglobals.base_root + request_uri(), \
2370 data, 'cache_page', \
2371 CACHE_TEMPORARY, drupal_get_headers());
2372
2373
2374
2376 """
2377 Executes a cron run when called
2378 @return
2379 Returns True if ran successfully
2380 """
2381
2382 semaphore = variable_get('cron_semaphore', False);
2383 if (semaphore):
2384 if (drupy_time() - semaphore > 3600):
2385
2386
2387 watchdog('cron', \
2388 'Cron has been running for more than an hour ' + \
2389 'and is most likely stuck.', {}, WATCHDOG_ERROR);
2390
2391 variable_del('cron_semaphore');
2392 else:
2393
2394 watchdog('cron', \
2395 'Attempting to re-run cron while it is already running.', [], \
2396 WATCHDOG_WARNING);
2397 else:
2398
2399 register_shutdown_function('drupal_cron_cleanup');
2400
2401 variable_set('cron_semaphore', drupy_time());
2402
2403 plugin_invoke_all('cron');
2404
2405 variable_set('cron_last', drupy_time());
2406 watchdog('cron', 'Cron run completed.', [], WATCHDOG_NOTICE);
2407
2408 variable_del('cron_semaphore');
2409
2410 return True;
2411
2412
2413
2415 """
2416 Shutdown function for cron cleanup.
2417 """
2418
2419 if (variable_get('cron_semaphore', False)):
2420 watchdog('cron', 'Cron run exceeded the time limit and was aborted.', \
2421 [], WATCHDOG_WARNING);
2422
2423 variable_del('cron_semaphore');
2424
2425
2426
2428 """
2429 Return an array of system file objects.
2430
2431 Returns an array of file objects of the given type from the site-wide
2432 directory (i.e. plugins/), the all-sites directory (i.e.
2433 sites/all/plugins/), the profiles directory, and site-specific directory
2434 (i.e. sites/somesite/plugins/). The returned array will be keyed using the
2435 key specified (name, basename, filename). Using name or basename will cause
2436 site-specific files to be prioritized over similar files in the default
2437 directories. That is, if a file with the same name appears in both the
2438 site-wide directory and site-specific directory, only the site-specific
2439 version will be included.
2440
2441 @param mask
2442 The regular expression of the files to find.
2443 @param directory
2444 The subdirectory name in which the files are found. For example,
2445 'plugins' will search in both plugins/ and
2446 sites/somesite/plugins/.
2447 @param key
2448 The key to be passed to file_scan_directory().
2449 @param min_depth
2450 Minimum depth of directories to return files from.
2451
2452 @return
2453 An array of file objects of the specified type.
2454 """
2455 config = lib_bootstrap.conf_path();
2456
2457
2458
2459
2460
2461
2462
2463
2464 if (lib_appglobals.profile is None):
2465 lib_appglobals.profile = \
2466 lib_bootstrap.variable_get('install_profile', 'default');
2467 searchdir = [directory];
2468 files = {};
2469
2470 searchdir.append( 'sites/all/%s' % directory );
2471
2472
2473
2474
2475 if (php.file_exists("profiles/%s/%s" % \
2476 (lib_appglobals.profile, directory) )):
2477 searchdir.append( "profiles/%s/%s" % (lib_appglobals.profile, directory) )
2478 if (php.file_exists("%s/%s" % (config, directory))):
2479 searchdir.append( "%s/%s" % (config, directory) );
2480
2481 for dir_ in searchdir:
2482 files = php.array_merge(files, lib_file.scan_directory(dir_, mask, \
2483 ('.', '..', 'CVS'), 0, True, key, min_depth));
2484 return files;
2485
2486
2487
2489 """
2490 This dispatch function hands off structured Drupal arrays to type-specific
2491 *_alter implementations. It ensures a consistent interface for all altering
2492 operations.
2493
2494 @param type
2495 The data type of the structured array. 'form', 'links',
2496 'node_content', and so on are several examples.
2497 @param data
2498 The structured array to be altered.
2499 @param ...
2500 Any additional params will be passed on to the called
2501 hook_type_alter functions.
2502 """
2503 php.Reference.check(data);
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516 if (php.is_array(data) and php.isset(data['__drupal_alter_by_ref'])):
2517 by_ref_parameters = data['__drupal_alter_by_ref'];
2518 del(data['__drupal_alter_by_ref']);
2519 else:
2520 by_ref_parameters = None;
2521
2522
2523 args = [data];
2524 if (by_ref_parameters != None):
2525 args = php.array_merge(args, by_ref_parameters);
2526
2527
2528 additional_args = additional_args_;
2529 php.array_shift(additional_args);
2530 php.array_shift(additional_args);
2531 args = tuple(php.array_merge(args, additional_args));
2532 for plugin in plugin_implements(type_ + '_alter'):
2533 function = plugin + '_' + type_ + '_alter';
2534 function( *args );
2535
2536
2537
2539 """
2540 Renders HTML given a structured array tree.
2541
2542 Recursively iterates over each of the array elements, generating HTML code.
2543 This function is usually called from within a another function, like
2544 drupal_get_form() or node_view().
2545
2546 @param elements
2547 The structured array describing the data to be rendered.
2548 @return
2549 The rendered HTML.
2550 """
2551 php.Reference.check(elements);
2552 if (php.empty(elements) or (php.isset(elements, '#access') and not \
2553 elements['#access'])):
2554 return None;
2555
2556
2557 if (not php.isset(elements, '#defaults_loaded') or not \
2558 elements['#defaults_loaded']):
2559 info = _element_info(elements['#type']);
2560 if ((not php.empty(elements['#type'])) and info):
2561 for ik,iv in info.items():
2562 elements[ik] = iv;
2563
2564
2565
2566 if (php.isset(elements, '#pre_render')):
2567 for function in elements['#pre_render']:
2568 if (drupal_function_exists(function)):
2569 for ek,ev in function(elements).items():
2570 elements[ek] = ev
2571 content = '';
2572
2573
2574 if (not php.isset(elements, '#sorted')):
2575 php.uasort(elements, element_sort);
2576 elements['#title'] = None
2577 elements['#description'] = None
2578 if (not php.isset(elements['#children'])):
2579 children = element_children(elements);
2580
2581 if (php.isset(elements, '#theme') and \
2582 php.empty(elements['#theme_used'])):
2583 elements['#theme_used'] = True;
2584
2585 if (php.empty(children)):
2586 elements['#printed'] = True;
2587 else:
2588 elements['#markup'] = '';
2589 content = theme(elements['#theme'], elements);
2590
2591 if (php.empty(content)):
2592 for key in children:
2593 content += drupal_render(elements[key]);
2594 if (not php.empty(content)):
2595 elements['#children'] = content;
2596
2597 if (not php.isset(elements, '#printed')):
2598 content = theme((elements['#type'] if not \
2599 php.empty(elements['#type']) else 'markup'), elements);
2600 elements['#printed'] = True;
2601 if (not php.empty(content)):
2602
2603
2604
2605 if (php.isset(elements, '#post_render')):
2606 for function in elements['#post_render']:
2607 if (drupal_function_exists(function)):
2608 content = function(content, elements);
2609 prefix = (elements['#prefix'] if \
2610 php.isset(elements, '#prefix') else '');
2611 suffix = (elements['#suffix'] if \
2612 php.isset(elements, '#suffix') else '');
2613 return prefix + content + suffix;
2614
2615
2616
2618 """
2619 Function used by uasort to sort structured arrays by weight.
2620 """
2621 a_weight = (a['#weight'] if \
2622 (php.is_array(a) and php.isset(a['#weight'])) else 0);
2623 b_weight = (b['#weight'] if \
2624 (php.is_array(b) and php.isset(b['#weight'])) else 0);
2625 if (a_weight == b_weight):
2626 return 0;
2627 return (-1 if (a_weight < b_weight) else 1);
2628
2629
2630
2632 """
2633 Check if the key is a property.
2634 """
2635 return (key[0] == '#');
2636
2637
2638
2640 """
2641 Get properties of a structured array element. Properties begin with '#'.
2642 """
2643 return php.array_filter(php.array_keys(element), 'element_property');
2644
2645
2646
2648 """
2649 Check if the key is a child.
2650 """
2651 return (not php.isset(key, 0) or key[0] != '#');
2652
2653
2654
2656 """
2657 Get keys of a structured array tree element
2658 that are not properties (i.e., do not begin with '#').
2659 """
2660 return php.array_filter(php.array_keys(element), 'element_child');
2661
2662
2663
2665 """
2666 Provide theme registration for themes across .inc files.
2667 """
2668 return {
2669
2670 'placeholder' : {
2671 'arguments' : {'text' : None}
2672 },
2673 'page' : {
2674 'arguments' : {'content' : None, 'show_blocks' : True, \
2675 'show_messages' : True},
2676 'template' : 'page',
2677 },
2678 'maintenance_page' : {
2679 'arguments' : {'content' : None, 'show_blocks' : True, \
2680 'show_messages' : True},
2681 'template' : 'maintenance-page',
2682 },
2683 'update_page' : {
2684 'arguments' : {'content' : None, 'show_messages' : True}
2685 },
2686 'install_page' : {
2687 'arguments' : {'content' : None}
2688 },
2689 'task_list' : {
2690 'arguments' : {'items' : None, 'active' : None}
2691 },
2692 'status_messages' : {
2693 'arguments' : {'display' : None}
2694 },
2695 'links' : {
2696 'arguments' : {'links' : None, 'attributes' : {'class' : 'links'}}
2697 },
2698 'image' : {
2699 'arguments' : {'path' : None, 'alt' : '', 'title' : '', \
2700 'attributes' : None, 'getsize' : True}
2701 },
2702 'breadcrumb' : {
2703 'arguments' : {'breadcrumb' : None}
2704 },
2705 'help' : {
2706 'arguments' : {}
2707 },
2708 'submenu' : {
2709 'arguments' : {'links' : None}
2710 },
2711 'table' : {
2712 'arguments' : {'php.header' : None, 'rows' : None, \
2713 'attributes' : {}, 'caption' : None}
2714 },
2715 'table_select_header_cell' : {
2716 'arguments' : {}
2717 },
2718 'tablesort_indicator' : {
2719 'arguments' : {'style' : None}
2720 },
2721 'box' : {
2722 'arguments' : {'title' : None, 'content' : None, 'region' : 'main'},
2723 'template' : 'box',
2724 },
2725 'block' : {
2726 'arguments' : {'block' : None},
2727 'template' : 'block',
2728 },
2729 'mark' : {
2730 'arguments' : {'type' : MARK_NEW}
2731 },
2732 'item_list' : {
2733 'arguments' : {'items' : {}, 'title' : None, \
2734 'type' : 'ul', 'attributes' : None}
2735 },
2736 'more_help_link' : {
2737 'arguments' : {'url' : None}
2738 },
2739 'xml_icon' : {
2740 'arguments' : {'url' : None}
2741 },
2742 'feed_icon' : {
2743 'arguments' : {'url' : None, 'title' : None}
2744 },
2745 'more_link' : {
2746 'arguments' : {'url' : None, 'title' : None}
2747 },
2748 'closure' : {
2749 'arguments' : {'main' : 0}
2750 },
2751 'blocks' : {
2752 'arguments' : {'region' : None}
2753 },
2754 'username' : {
2755 'arguments' : {'object' : None}
2756 },
2757 'progress_bar' : {
2758 'arguments' : {'percent' : None, 'message' : None}
2759 },
2760 'indentation' : {
2761 'arguments' : {'size' : 1}
2762 },
2763
2764 'pager' : {
2765 'arguments' : {'tags' : {}, 'limit' : 10, 'element' : 0, \
2766 'parameters' : {}}
2767 },
2768 'pager_first' : {
2769 'arguments' : {'text' : None, 'limit' : None, 'element' : 0, \
2770 'parameters' : {}}
2771 },
2772 'pager_previous' : {
2773 'arguments' : {'text' : None, 'limit' : None, 'element' : 0, \
2774 'interval' : 1, 'parameters' : {}}
2775 },
2776 'pager_next' : {
2777 'arguments' : {'text' : None, 'limit' : None, 'element' : 0, \
2778 'interval' : 1, 'parameters' : {}}
2779 },
2780 'pager_last' : {
2781 'arguments' : {'text' : None, 'limit' : None, 'element' : 0, \
2782 'parameters' : {}}
2783 },
2784 'pager_link' : {
2785 'arguments' : {'text' : None, 'page_new' : None, 'element' : None, \
2786 'parameters' : {}, 'attributes' : {}}
2787 },
2788
2789 'locale_admin_manage_screen' : {
2790 'arguments' : {'form' : None}
2791 },
2792
2793 'menu_item_link' : {
2794 'arguments' : {'item' : None}
2795 },
2796 'menu_tree' : {
2797 'arguments' : {'tree' : None}
2798 },
2799 'menu_item' : {
2800 'arguments' : {'link' : None, 'has_children' : None, 'menu' : ''}
2801 },
2802 'menu_local_task' : {
2803 'arguments' : {'link' : None, 'active' : False}
2804 },
2805 'menu_local_tasks' : {
2806 'arguments' : {}
2807 },
2808
2809 'select' : {
2810 'arguments' : {'element' : None}
2811 },
2812 'fieldset' : {
2813 'arguments' : {'element' : None}
2814 },
2815 'radio' : {
2816 'arguments' : {'element' : None}
2817 },
2818 'radios' : {
2819 'arguments' : {'element' : None}
2820 },
2821 'password_confirm' : {
2822 'arguments' : {'element' : None}
2823 },
2824 'date' : {
2825 'arguments' : {'element' : None}
2826 },
2827 'item' : {
2828 'arguments' : {'element' : None}
2829 },
2830 'checkbox' : {
2831 'arguments' : {'element' : None}
2832 },
2833 'checkboxes' : {
2834 'arguments' : {'element' : None}
2835 },
2836 'submit' : {
2837 'arguments' : {'element' : None}
2838 },
2839 'button' : {
2840 'arguments' : {'element' : None}
2841 },
2842 'image_button' : {
2843 'arguments' : {'element' : None}
2844 },
2845 'hidden' : {
2846 'arguments' : {'element' : None}
2847 },
2848 'token' : {
2849 'arguments' : {'element' : None}
2850 },
2851 'textfield' : {
2852 'arguments' : {'element' : None}
2853 },
2854 'form' : {
2855 'arguments' : {'element' : None}
2856 },
2857 'textarea' : {
2858 'arguments' : {'element' : None}
2859 },
2860 'markup' : {
2861 'arguments' : {'element' : None}
2862 },
2863 'password' : {
2864 'arguments' : {'element' : None}
2865 },
2866 'file' : {
2867 'arguments' : {'element' : None}
2868 },
2869 'form_element' : {
2870 'arguments' : {'element' : None, 'value' : None}
2871 },
2872 };
2873
2874
2875
2876
2877
2878
2879
2881 """
2882 Get the schema definition of a table, or the whole database schema.
2883
2884 The returned schema will include any modifications made by any
2885 plugin that implements hook_schema_alter().
2886
2887 @param table
2888 The name of the table. If not given, the schema of all tables is returned.
2889 @param rebuild
2890 If true, the schema will be rebuilt instead of retrieved from the cache.
2891 """
2892 php.static(drupal_get_schema, 'schema', [])
2893 if (php.empty(drupal_get_schema.schema) or rebuild):
2894
2895 cached = cache_get('schema')
2896 if (not rebuild and cached):
2897 drupal_get_schema.schema = cached.data;
2898
2899 else:
2900 drupal_get_schema.schema = [];
2901
2902 plugin_load_all_includes('install');
2903
2904 for plugin in plugin_implements('schema'):
2905 current = plugin_invoke(plugin, 'schema');
2906 _drupal_initialize_schema(plugin, current);
2907 drupal_get_schema.schema = \
2908 php.array_merge(drupal_get_schema.schema, current);
2909 drupal_alter('schema', drupal_get_schema.schema);
2910 cache_set('schema', drupal_get_schema.schema);
2911 if (table != None):
2912 return drupal_get_schema.schema;
2913 elif (php.isset(drupal_get_schema.schema, table)):
2914 return drupal_get_schema.schema[table];
2915 else:
2916 return False;
2917
2918
2919
2921 """
2922 Create all tables that a plugin defines in its hook_schema().
2923
2924 Note: This function does not pass the plugin's schema through
2925 hook_schema_alter(). The plugin's tables will be created exactly as the
2926 plugin defines them.
2927
2928 @param plugin
2929 The plugin for which the tables will be created.
2930 @return
2931 An array of arrays with the following key/value pairs:
2932 success: a boolean indicating whether the query succeeded
2933 query: the SQL query(s) executed, passed through check_plain()
2934 """
2935 schema = drupal_get_schema_unprocessed(plugin);
2936 _drupal_initialize_schema(plugin, schema);
2937 ret = array();
2938 for name,table in schema.items():
2939 db_create_table(ret, name, table);
2940 return ret;
2941
2942
2943
2945 """
2946 Remove all tables that a plugin defines in its hook_schema().
2947
2948 Note: This function does not pass the plugin's schema through
2949 hook_schema_alter(). The plugin's tables will be created exactly as the
2950 plugin defines them.
2951
2952 @param plugin
2953 The plugin for which the tables will be removed.
2954 @return
2955 An array of arrays with the following key/value pairs:
2956 success: a boolean indicating whether the query succeeded
2957 query: the SQL query(s) executed, passed through check_plain()
2958 """
2959 schema = drupal_get_schema_unprocessed(plugin);
2960 _drupal_initialize_schema(plugin, schema);
2961 ret = [];
2962 for table in schema:
2963 db_drop_table(ret, table['name']);
2964 return ret;
2965
2966
2967
2969 """
2970 Returns the unprocessed and unaltered version of a plugin's schema.
2971
2972 Use this function only if you explicitly need the original
2973 specification of a schema, as it was defined in a plugin's
2974 hook_schema(). No additional default values will be set,
2975 hook_schema_alter() is not invoked and these unprocessed
2976 definitions won't be cached.
2977
2978 This function can be used to retrieve a schema specification in
2979 hook_schema(), so it allows you to derive your tables from existing
2980 specifications.
2981
2982 It is also used by drupal_install_schema() and
2983 drupal_uninstall_schema() to ensure that a plugin's tables are
2984 created exactly as specified without any changes introduced by a
2985 plugin that implements hook_schema_alter().
2986
2987 @param plugin
2988 The plugin to which the table belongs.
2989 @param table
2990 The name of the table. If not given, the plugin's complete schema
2991 is returned.
2992 """
2993
2994 plugin_load_install(plugin_);
2995 schema = plugin_invoke(plugin_, 'schema');
2996 if (not php.is_null(table) and php.isset(schema, table)):
2997 return schema[table];
2998 else:
2999 return schema;
3000
3001
3003 """
3004 Fill in required default values for
3005 table definitions returned by hook_schema().
3006
3007 @param plugin
3008 The plugin for which hook_schema() was invoked.
3009 @param &schema
3010 The schema definition array as it was returned by the plugin's
3011 hook_schema().
3012
3013 Drupy(BC): schema is a reference
3014 """
3015 php.Reference.check(schema);
3016
3017 for name,table in schema.items():
3018 if (php.empty(table['plugin'])):
3019 schema[name]['plugin'] = plugin;
3020 if (not php.isset(table, 'name')):
3021 schema[name]['name'] = name;
3022
3023
3024
3026 """
3027 Retrieve a list of fields from a table schema.
3028 The list is suitable for use in a SQL query.
3029
3030 @param table
3031 The name of the table from which to retrieve fields.
3032 @param
3033 An optional prefix to to all fields.
3034
3035 @return An array of fields.
3036 """
3037 schema = drupal_get_schema(table);
3038 fields = php.array_keys(schema['fields']);
3039 if (prefix != None):
3040 columns = [];
3041 for field in fields:
3042 columns.append( "%(prefix)s.field" % {'prefix':prefix} );
3043 return columns;
3044 else:
3045 return fields;
3046
3047
3048
3050 """
3051 Save a record to the database based upon the schema.
3052
3053 Default values are filled in for missing items, and 'serial'
3054 (auto increment) types are filled in with IDs.
3055
3056 @param table
3057 The name of the table; this must exist in schema API.
3058 @param &object
3059 The object to write. This is a reference, as defaults according to
3060 the schema may be filled in on the object, as well as ID on the serial
3061 type(s). Both array an object types may be passed.
3062 @param update
3063 If this is an update, specify the primary keys' field names. It is the
3064 caller's responsibility to know if a record for this object already
3065 exists in the database. If there is only 1 key,
3066 you may pass a simple string.
3067 @return
3068 Failure to write a record will return False. Otherwise SAVED_NEW or
3069 SAVED_UPDATED is returned depending on the operation performed. The
3070 object parameter contains values for any serial fields defined by
3071 the table. For example, object.nid will be populated after inserting
3072 a new node.
3073 """
3074 php.Reference.check(object_);
3075
3076 if (p.is_string(update)):
3077 update = [update];
3078 schema = drupal_get_schema(table);
3079 if php.empty(schema):
3080 return False
3081
3082 if (php.is_array(object_)):
3083 tmp = php.object_(object_);
3084 array_ = True;
3085 else:
3086 tmp = php.clone(object_)
3087 array_ = False;
3088 fields = defs = values = serials = placeholders = [];
3089
3090
3091 for field,info in schema['fields'].items():
3092
3093 if (info['type'] == 'serial' and php.count(update)):
3094 continue;
3095
3096 if (not php.isset(tmp, field) and not \
3097 php.count(update) and php.isset(info, 'default')):
3098 setattr(tmp, field, info['default']);
3099
3100 if (info['type'] == 'serial'):
3101 serials.append( field );
3102
3103 delattr(tmp, field);
3104
3105 if (php.isset(tmp, field)):
3106 fields.append( field );
3107 placeholders.append( db_type_placeholder(info['type']) );
3108 if (php.empty(info['serialize'])):
3109 values.append( getattr( tmp, field ) );
3110 elif (not php.empty(getattr( tmp, field ))):
3111 values.append( php.serialize( getattr(tmp, field) ) );
3112 else:
3113 values.append( '' );
3114 if (php.empty(fields)):
3115
3116
3117 if (array_):
3118 php.array_merge(object_, php.array_(tmp), True)
3119 else:
3120 php.object_merge(object_, tmp, True)
3121 return;
3122
3123 query = '';
3124 if (php.count(update) == 0):
3125 query = "INSERT INTO {" + table + "} (" + \
3126 php.implode(', ', fields) + ') VALUES (' + \
3127 php.implode(', ', placeholders) + ')';
3128 return_ = SAVED_NEW;
3129 else:
3130 query = '';
3131 for id,field in fields.items():
3132 if (not php.empty(query)):
3133 query += ', ';
3134 query += field + ' = ' + placeholders[id];
3135 for key in update:
3136 conditions.append( key + " = " + \
3137 db_type_placeholder(schema['fields'][key]['type']) );
3138 values.append( tmp.key );
3139 query = "UPDATE {" + table + "} SET query WHERE " + \
3140 php.implode(' AND ', conditions);
3141 return_ = SAVED_UPDATED;
3142
3143 if (db_query(query, values)):
3144 if (not php.empty(serials)):
3145
3146 for field in serials:
3147 setattr( tmp, field, db_last_insert_id(table, field) );
3148 else:
3149 return_ = False
3150
3151 if (array_):
3152 php.array_merge(object_, php.array_(tmp), True)
3153 else:
3154 php.object_merge(object_, tmp, True)
3155 return return_;
3156
3157
3158
3160 """
3161 Parse Drupal info file format.
3162
3163 Files should use an ini-like format to specify values.
3164 White-space generally doesn't matter, except inside values.
3165 e.g.
3166
3167 @verbatim
3168 key = value
3169 key = "value"
3170 key = 'value'
3171 key = "multi-line
3172
3173 value"
3174 key = 'multi-line
3175
3176 value'
3177 key
3178 =
3179 'value'
3180 @endverbatim
3181
3182 Arrays are created using a php.GET-like syntax:
3183
3184 @verbatim
3185 key[] = "numeric array"
3186 key[index] = "associative array"
3187 key[index][] = "nested numeric array"
3188 key[index][index] = "nested associative array"
3189 @endverbatim
3190
3191 PHP constants are substituted in, but only when used as the entire value:
3192
3193 Comments should start with a semi-colon at the beginning of a line.
3194
3195 This function is NOT for placing arbitrary plugin-specific settings. Use
3196 variable_get() and variable_set() for that.
3197
3198 Information stored in the plugin.info file:
3199 - name: The real name of the plugin for display purposes.
3200 - description: A brief description of the plugin.
3201 - dependencies: An array of shortnames of other
3202 plugins this plugin depends on.
3203 - package: The name of the package of plugins this plugin belongs to.
3204
3205 Example of .info file:
3206 @verbatim
3207 name = Forum
3208 description = Enables threaded discussions about general topics.
3209 dependencies[] = taxonomy
3210 dependencies[] = comment
3211 package = Core - optional
3212 version = VERSION
3213 @endverbatim
3214
3215 @param filename
3216 The file we are parsing. Accepts file with relative or absolute path.
3217 @return
3218 The info array.
3219 """
3220 return DrupyHelper.get_import(filename).__all__;
3221
3222
3223 """
3224 Severity levels, as defined in RFC 3164:
3225 http://www.ietf.org/rfc/rfc3164.txt.
3226
3227 @return
3228 Array of the possible severity levels for log messages.
3229
3230 @see watchdog()
3231 """
3233 """
3234 Parse Drupal info file format.
3235
3236 Files should use an ini-like format to specify values.
3237 White-space generally doesn't matter, except inside values.
3238 e.g.
3239
3240 @verbatim
3241 key = value
3242 key = "value"
3243 key = 'value'
3244 key = "multi-line
3245
3246 value"
3247 key = 'multi-line
3248
3249 value'
3250 key
3251 =
3252 'value'
3253 @endverbatim
3254
3255 Arrays are created using a php.GET-like syntax:
3256
3257 @verbatim
3258 key[] = "numeric array"
3259 key[index] = "associative array"
3260 key[index][] = "nested numeric array"
3261 key[index][index] = "nested associative array"
3262 @endverbatim
3263
3264 PHP constants are substituted in, but only when used as the entire value:
3265
3266 Comments should start with a semi-colon at the beginning of a line.
3267
3268 This function is NOT for placing arbitrary plugin-specific settings. Use
3269 variable_get() and variable_set() for that.
3270
3271 Information stored in the plugin.info file:
3272 - name: The real name of the plugin for display purposes.
3273 - description: A brief description of the plugin.
3274 - dependencies: An array of shortnames of
3275 other plugins this plugin depends on.
3276 - package: The name of the package of plugins this plugin belongs to.
3277
3278 Example of .info file:
3279 @verbatim
3280 name = Forum
3281 description = Enables threaded discussions about general topics.
3282 dependencies[] = taxonomy
3283 dependencies[] = comment
3284 package = Core - optional
3285 version = VERSION
3286 @endverbatim
3287
3288 @param filename
3289 The file we are parsing. Accepts file with relative or absolute path.
3290 @return
3291 The info array.
3292 """
3293 return {
3294 WATCHDOG_EMERG : t('emergency'),
3295 WATCHDOG_ALERT : t('alert'),
3296 WATCHDOG_CRITICAL : t('critical'),
3297 WATCHDOG_ERROR : t('error'),
3298 WATCHDOG_WARNING : t('warning'),
3299 WATCHDOG_NOTICE : t('notice'),
3300 WATCHDOG_INFO : t('info'),
3301 WATCHDOG_DEBUG : t('debug')
3302 };
3303
3304
3305
3326
3327
3328
3340
3341
3342
3366
3367
3368
3370 """
3371 Helper function to change query-strings on css/js files.
3372
3373 Changes the character added to all css/js files as dummy query-string,
3374 so that all browsers are forced to reload fresh files. We keep
3375 20 characters history (FIFO) to avoid repeats, but only the first
3376 (newest) character is actually used on urls, to keep them short.
3377 This is also called from update.php.
3378 """
3379 string_history = variable_get('css_js_query_string', '00000000000000000000');
3380 new_character = string_history[0];
3381 characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
3382 while (php.strpos(string_history, new_character) != False):
3383 new_character = characters[php.mt_rand(0, php.strlen(characters) - 1)];
3384 variable_set('css_js_query_string', new_character + \
3385 php.substr(string_history, 0, 19));
3386