def install_page(content): """ Generate a themed installation page. Note: this function is not themeable. @param content The page content to show. """ drupal_set_header('Content-Type: text/html; charset=utf-8') # Assign content. variables['content'] = content # Delay setting the message variable so it can be processed below. variables['show_messages'] = False # The maintenance preprocess function is recycled here. template_preprocess_maintenance_page(variables) # Special handling of error messages messages = drupal_set_message() if (php.isset(messages, 'error')): title = (st('The following errors must be resolved before you can ' + \ 'continue the installation process') if \ (php.count(messages['error']) > 1) else \ st('The following error must be resolved before you can ' + \ 'continue the installation process')) variables['messages'] += '<h3>' + title + ':</h3>' variables['messages'] += theme('status_messages', 'error') variables['content'] += '<p>' + st('Please check the error ' + \ 'messages and <a href="not url">try again</a>.', \ {'not url' : request_uri()}) + '</p>' # Special handling of warning messages if (php.isset(messages, 'warning')): title = (st('The following installation warnings should be ' + \ 'carefully reviewed') if \ (php.count(messages['warning']) > 1) else \ st('The following installation warning should be carefully reviewed')) variables['messages'] += '<h4>' + title + ':</h4>' variables['messages'] += theme('status_messages', 'warning') # Special handling of status messages if (php.isset(messages, 'status')): title = (st('The following installation warnings should be ' + \ 'carefully reviewed, but in most cases may be safely ignored') if \ (php.count(messages['status']) > 1) else st('The following ' + \ 'installation warning should be carefully reviewed, but in ' + \ 'most cases may be safely ignored')) variables['messages'] += '<h4>' + title + ':</h4>' variables['messages'] += theme('status_messages', 'status') # This was called as a theme hook (not template), so we need to # fix path_to_theme() for the template, to point at the actual # theme rather than system plugin as owner of the hook. lib_appglobals.theme_path = 'themes/garland' return theme_render_template('themes/garland/maintenance-page.tpl.py', \ variables)
def create_table_sql(name, table): """ Generate SQL to create a new table from a Drupal schema definition. @param name The name of the table to create. @param table A Schema API table definition array. @return An array of SQL statements to create the table. """ if (php.empty(table['mysql_suffix'])): table['mysql_suffix'] = "/*not 40100 DEFAULT CHARACTER SET UTF8 */" sql = "CREATE TABLE {" + name + "} (\n" # Add the SQL statement for each field. for field_name, field in table['fields'].items(): sql += _db_create_field_sql(field_name, _db_process_field(field)) + ", \n" # Process keys & indexes. keys = _db_create_keys_sql(table) if (php.count(keys)): sql += php.implode(", \n", keys) + ", \n" # Remove the last comma and space. sql = php.substr(sql, 0, -3) + "\n) " sql += table['mysql_suffix'] return array(sql)
def update_page(content, show_messages = True): """ Generate a themed update page. Note: this function is not themeable. @param content The page content to show. @param show_messages Whether to output status and error messages. False can be useful to postpone the messages to a subsequent page. """ # Set required headers. drupal_set_header('Content-Type: text/html; charset=utf-8') # Assign content and show message flag. variables['content'] = content variables['show_messages'] = show_messages # The maintenance preprocess function is recycled here. template_preprocess_maintenance_page(variables) # Special handling of warning messages. messages = drupal_set_message() if (php.isset(messages['warning'])): title = ('The following update warnings should be carefully ' + \ 'reviewed before continuing' if \ (php.count(messages['warning']) > 1) else \ 'The following update warning should be carefully ' + \ 'reviewed before continuing') variables['messages'] += '<h4>' + title + ':</h4>' variables['messages'] += theme('status_messages', 'warning') # This was called as a theme hook (not template), so we need to # fix path_to_theme() for the template, to point at the actual # theme rather than system plugin as owner of the hook. lib_appglobals.theme_path = 'themes/garland' return theme_render_template('themes/garland/maintenance-page.tpl.php', \ variables)
def disable(plugin_list_): """ Disable a given set of plugins. @param plugin_list An array of plugin names. """ invoke_plugins = [] for plugin_ in plugin_list_: if (plugin_exists(plugin_)): # Check if node_access table needs rebuilding. if (not node_access_needs_rebuild() and plugin_hook(plugin_, \ 'node_grants')): node_access_needs_rebuild(True) plugin_load_install(plugin_) plugin_invoke(plugin_, 'disable') db_query(\ "UPDATE {system} SET status = %d " + \ "WHERE type = '%s' AND name = '%s'", 0, 'plugin', plugin_) invoke_plugins.append(plugin) if (not php.empty(invoke_plugins)): # Refresh the plugin list to exclude the disabled plugins. plugin_list(True, False) # Force to regenerate the stored list of hook implementations. registry_rebuild() # If there remains no more node_access plugin, rebuilding will be # straightforward, we can do it right now. if (node_access_needs_rebuild() and \ php.count(plugin_implements('node_grants')) == 0): node_access_rebuild()
def update_page(content, show_messages=True): """ Generate a themed update page. Note: this function is not themeable. @param content The page content to show. @param show_messages Whether to output status and error messages. False can be useful to postpone the messages to a subsequent page. """ # Set required headers. drupal_set_header('Content-Type: text/html; charset=utf-8') # Assign content and show message flag. variables['content'] = content variables['show_messages'] = show_messages # The maintenance preprocess function is recycled here. template_preprocess_maintenance_page(variables) # Special handling of warning messages. messages = drupal_set_message() if (php.isset(messages['warning'])): title = ('The following update warnings should be carefully ' + \ 'reviewed before continuing' if \ (php.count(messages['warning']) > 1) else \ 'The following update warning should be carefully ' + \ 'reviewed before continuing') variables['messages'] += '<h4>' + title + ':</h4>' variables['messages'] += theme('status_messages', 'warning') # This was called as a theme hook (not template), so we need to # fix path_to_theme() for the template, to point at the actual # theme rather than system plugin as owner of the hook. lib_appglobals.theme_path = 'themes/garland' return theme_render_template('themes/garland/maintenance-page.tpl.php', \ variables)
def create_table_sql(name, table): """ Generate SQL to create a new table from a Drupal schema definition. @param name The name of the table to create. @param table A Schema API table definition array. @return An array of SQL statements to create the table. """ if (php.empty(table['mysql_suffix'])): table['mysql_suffix'] = "/*not 40100 DEFAULT CHARACTER SET UTF8 */" sql = "CREATE TABLE {" + name + "} (\n" # Add the SQL statement for each field. for field_name,field in table['fields'].items(): sql += _db_create_field_sql(field_name, _db_process_field(field)) + ", \n" # Process keys & indexes. keys = _db_create_keys_sql(table) if (php.count(keys)): sql += php.implode(", \n", keys) + ", \n" # Remove the last comma and space. sql = php.substr(sql, 0, -3) + "\n) " sql += table['mysql_suffix'] return array(sql)
def placeholders(arguments, type = 'int'): """ Generate placeholders for an array of query arguments of a single type. Given a Schema API field type, return correct %-placeholders to embed in a query @param arguments An array with at least one element. @param type The Schema API type of a field (e.g. 'int', 'text', or 'varchar'). """ placeholder = db_type_placeholder(type); return php.implode(',', php.array_fill(0, php.count(arguments), \ placeholder));
def add_field(ret, table, field, spec, keys_new=[]): """ Add a new field to a table. @param ret Array to which query results will be added. @param table Name of the table to be altered. @param field Name of the field to be added. @param spec The field specification array, as taken from a schema definition. The specification may also contain the key 'initial', the newly created field will be set to the value of the key in all rows. This is most useful for creating NOT None columns with no default value in existing tables. @param keys_new Optional keys and indexes specification to be created on the table along with adding the field. The format is the same as a table specification but without the 'fields' element. If you are adding a type 'serial' field, you MUST specify at least one key or index including it in this array. @see db_change_field for more explanation why. """ php.Reference.check(ret) fixNone = False if (not php.empty(spec['not None']) and not php.isset(spec, 'default')): fixNone = True spec['not None'] = False query = 'ALTER TABLE {' + table + '} ADD ' query += _db_create_field_sql(field, _db_process_field(spec)) if (php.count(keys_new)): query += ', ADD ' + php.implode(', ADD ', _db_create_keys_sql(keys_new)) ret.append(update_sql(query)) if (php.isset(spec, 'initial')): # All this because update_sql does not support %-placeholders. sql = 'UPDATE {' + table + '} SET ' + field + ' = ' + \ db_type_placeholder(spec['type']) result = db_query(sql, spec['initial']) ret.append( {'success' : result != False, \ 'query' : check_plain(sql + ' (' + spec['initial'] + ')')}) if (fixNone): spec['not None'] = True db_change_field(ret, table, field, field, spec)
def add_field(ret, table, field, spec, keys_new = []): """ Add a new field to a table. @param ret Array to which query results will be added. @param table Name of the table to be altered. @param field Name of the field to be added. @param spec The field specification array, as taken from a schema definition. The specification may also contain the key 'initial', the newly created field will be set to the value of the key in all rows. This is most useful for creating NOT None columns with no default value in existing tables. @param keys_new Optional keys and indexes specification to be created on the table along with adding the field. The format is the same as a table specification but without the 'fields' element. If you are adding a type 'serial' field, you MUST specify at least one key or index including it in this array. @see db_change_field for more explanation why. """ php.Reference.check(ret) fixNone = False if (not php.empty(spec['not None']) and not php.isset(spec, 'default')): fixNone = True spec['not None'] = False query = 'ALTER TABLE {' + table + '} ADD ' query += _db_create_field_sql(field, _db_process_field(spec)) if (php.count(keys_new)): query += ', ADD ' + php.implode(', ADD ', _db_create_keys_sql(keys_new)) ret.append( update_sql(query) ) if (php.isset(spec, 'initial')): # All this because update_sql does not support %-placeholders. sql = 'UPDATE {' + table + '} SET ' + field + ' = ' + \ db_type_placeholder(spec['type']) result = db_query(sql, spec['initial']) ret.append( {'success' : result != False, \ 'query' : check_plain(sql + ' (' + spec['initial'] + ')')}) if (fixNone): spec['not None'] = True db_change_field(ret, table, field, field, spec)
def download(): """ Call plugins that implement hook_file_download() to find out if a file is accessible and what headers it should be transferred with + If a plugin returns -1 drupal_access_denied() will be returned + If one or more plugins returned headers the download will start with the returned headers + If no plugins respond drupal_not_found() will be returned. """ # Merge remainder of arguments from php.GET['q'], into relative file path. args = func_get_args() filepath = php.implode('/', args) # Maintain compatibility with old ?file=paths saved in node bodies. if (php.isset(php.GET, 'file')): filepath = php.GET['file'] if (php.file_exists(file_create_path(filepath))): headers = plugin_invoke_all('file_download', filepath) if (php.in_array(-1, headers)): return drupal_access_denied() if (php.count(headers)): file_transfer(filepath, headers) return drupal_not_found()
def page_get_cache(): """ Retrieve the current page from the cache. Note: we do not serve cached pages when status messages are waiting (from a redirected form submission which was completed). @param status_only When set to TRUE, retrieve the status of the page cache only (whether it was started in this request or not). """ cache = None if ( not lib_appglobals.user.uid and (php.SERVER["REQUEST_METHOD"] == "GET" or php.SERVER["REQUEST_METHOD"] == "HEAD") and php.count(drupal_set_message()) == 0 ): cache = lib_cache.get(lib_appglobals.base_root + request_uri(), "cache_page") if php.empty(cache): ob_start() return cache
def page_get_cache(): """ Retrieve the current page from the cache. Note: we do not serve cached pages when status messages are waiting (from a redirected form submission which was completed). @param status_only When set to TRUE, retrieve the status of the page cache only (whether it was started in this request or not). """ cache = None if (not lib_appglobals.user.uid and \ (php.SERVER['REQUEST_METHOD'] == 'GET' or \ php.SERVER['REQUEST_METHOD'] == 'HEAD') and \ php.count(drupal_set_message()) == 0): cache = lib_cache.get(lib_appglobals.base_root + request_uri(), \ 'cache_page') if (php.empty(cache)): ob_start() return cache
def from_browser(): """ Identify language from the Accept-language HTTP php.header we got. """ # Specified by the user via the browser's Accept Language setting # Samples: "hu, en-us;q=0.66, en;q=0.33", "hu,en-us;q=0.5" browser_langs = [] if (php.isset(php.SERVER, 'HTTP_ACCEPT_LANGUAGE')): browser_accept = php.explode(",", php.SERVER['HTTP_ACCEPT_LANGUAGE']) for i in range(php.count(browser_accept)): # The language part is either a code or a code with a quality. # We cannot do anything with a * code, so it is skipped. # If the quality is missing, it is assumed to be 1 according to the RFC. if (php.preg_match("not ([a-z-]+)(;q=([0-9\\.]+))?not ", \ php.trim(browser_accept[i]), found)): browser_langs[found[1]] = (float(found[3]) if \ php.isset(found, 3) else 1.0) # Order the codes by quality arsort(browser_langs) # Try to find the first preferred language we have languages = language_list('enabled') for langcode, q in browser_langs.items(): if (php.isset(languages['1'], langcode)): return languages['1'][langcode]
def from_browser(): """ Identify language from the Accept-language HTTP php.header we got. """ # Specified by the user via the browser's Accept Language setting # Samples: "hu, en-us;q=0.66, en;q=0.33", "hu,en-us;q=0.5" browser_langs = [] if (php.isset(php.SERVER, 'HTTP_ACCEPT_LANGUAGE')): browser_accept = php.explode(",", php.SERVER['HTTP_ACCEPT_LANGUAGE']) for i in range(php.count(browser_accept)): # The language part is either a code or a code with a quality. # We cannot do anything with a * code, so it is skipped. # If the quality is missing, it is assumed to be 1 according to the RFC. if (php.preg_match("not ([a-z-]+)(;q=([0-9\\.]+))?not ", \ php.trim(browser_accept[i]), found)): browser_langs[found[1]] = (float(found[3]) if \ php.isset(found, 3) else 1.0) # Order the codes by quality arsort(browser_langs) # Try to find the first preferred language we have languages = language_list('enabled') for langcode,q in browser_langs.items(): if (php.isset(languages['1'], langcode)): return languages['1'][langcode]
def save_upload(source, validators = {}, dest = False, \ replace = FILE_EXISTS_RENAME): """ Saves a file upload to a new location + The source file is validated as a proper upload and handled as such. The file will be added to the files table as a temporary file. Temporary files are periodically cleaned + To make the file permanent file call file_set_status() to change its status. @param source A string specifying the name of the upload field to save. @param validators An optional, associative array of callback functions used to validate the file + The keys are function names and the values arrays of callback parameters which will be passed in after the user and file objects + The functions should return an array of error messages, an empty array indicates that the file passed validation. The functions will be called in the order specified. @param dest A string containing the directory source should be copied to + If this is not provided or is not writable, the temporary directory will be used. @param replace A boolean indicating whether an existing file of the same name in the destination directory should overwritten + A False value will generate a new, unique filename in the destination directory. @return An object containing the file information, or False in the event of an error. """ php.static(file_save_upload, 'upload_cache', {}) # Add in our check of the the file name length. validators['file_validate_name_length'] = {} # Return cached objects without processing since the file will have # already been processed and the paths in FILES will be invalid. if (php.isset(file_save_upload.uploadcache, source)): return file_save_upload.uploadcache[source] # If a file was uploaded, process it. if (php.isset(p.FILES, 'files') and p.FILES['files']['name'][source] and \ php.is_uploaded_file(p.FILES['files']['tmp_name'][source])): # Check for file upload errors and return False if a # lower level system error occurred. # @see http://php.net/manual/en/features.file-upload.errors.php if p.FILES['files']['error'][source] == UPLOAD_ERR_OK: pass elif p.FILES['files']['error'][source] == UPLOAD_ERR_INI_SIZE or \ p.FILES['files']['error'][source] == UPLOAD_ERR_FORM_SIZE: drupal_set_message(t(\ 'The file %file could not be saved, because it exceeds %maxsize, ' + \ 'the maximum allowed size for uploads.', \ {'%file' : source, '%maxsize' : \ format_size(file_upload_max_size())}), 'error') return False elif p.FILES['files']['error'][source] == UPLOAD_ERR_PARTIAL or \ p.FILES['files']['error'][source] == UPLOAD_ERR_NO_FILE: drupal_set_message(t('The file %file could not be saved, ' + \ 'because the upload did not complete.', {'%file' : source}), 'error') return False # Unknown error else: drupal_set_message(t('The file %file could not be saved. ' + \ 'An unknown error has occurred.', {'%file' : source}), 'error') return False # Build the list of non-munged extensions. # @todo: this should not be here + we need to figure out the right place. extensions = '' for rid,name in lib_appglobals.user.roles.items(): extensions += ' ' + variable_get("upload_extensions_rid", variable_get('upload_extensions_default', \ 'jpg jpeg gif png txt html doc xls pdf ppt pps odt ods odp')) # Begin building file object. file = php.stdClass() file.filename = file_munge_filename(php.trim(\ basename(p.FILES['files']['name'][source]), '.'), extensions) file.filepath = p.FILES['files']['tmp_name'][source] file.filemime = p.FILES['files']['type'][source] # Rename potentially executable files, to help prevent exploits. if (php.preg_match('/\.(php|pl|py|cgi|asp|js)$/i', file.filename) and \ (php.substr(file.filename, -4) != '.txt')): file.filemime = 'text/plain' file.filepath += '.txt' file.filename += '.txt' # If the destination is not provided, or is not writable, then use the # temporary directory. if (php.empty(dest) or file_check_path(dest) == False): dest = file_directory_temp() file.source = source file.destination = file_destination(file_create_path(dest + '/' + \ file.filename), replace) file.filesize = FILES['files']['size'][source] # Call the validation functions. errors = {} for function,args in validators.items(): array_unshift(args, file) errors = php.array_merge(errors, function(*args)) # Check for validation errors. if (not php.empty(errors)): message = t('The selected file %name could not be uploaded.', \ {'%name' : file.filename}) if (php.count(errors) > 1): message += '<ul><li>' + php.implode('</li><li>', errors) + '</li></ul>' else: message += ' ' + php.array_pop(errors) form_set_error(source, message) return False # Move uploaded files from PHP's upload_tmp_dir to # Drupal's temporary directory. # This overcomes open_basedir restrictions for future file operations. file.filepath = file.destination if (not move_uploaded_file(p.FILES['files']['tmp_name'][source], \ file.filepath)): form_set_error(source, t('File upload error. ' + \ 'Could not move uploaded file.')) watchdog('file', 'Upload error + Could not move uploaded file ' + \ '%file to destination %destination.', \ {'%file' : file.filename, '%destination' : file.filepath}) return False # If we made it this far it's safe to record this file in the database. file.uid = lib_appglobals.user.uid file.status = FILE_STATUS_TEMPORARY file.timestamp = time() drupal_write_record('files', file) # Add file to the cache. file_save_upload.upload_cache[source] = file return file return False
def change_field(ret, table, field, field_new, spec, keys_new=[]): """ Change a field definition. IMPORTANT NOTE: To maintain database portability, you have to explicitly recreate all indices and primary keys that are using the changed field. That means that you have to drop all affected keys and indexes with db_drop_{primary_key,unique_key,index}() before calling db_change_field(). To recreate the keys and indices, pass the key definitions as the optional keys_new argument directly to db_change_field(). For example, suppose you have: @code schema['foo'] = array( 'fields' : array( 'bar' : array('type' : 'int', 'not None' : True) ), 'primary key' : array('bar') ) @endcode and you want to change foo.bar to be type serial, leaving it as the primary key. The correct sequence is: @code db_drop_primary_key(ret, 'foo') db_change_field(ret, 'foo', 'bar', 'bar', array('type' : 'serial', 'not None' : True), array('primary key' : array('bar'))) @endcode The reasons for this are due to the different database engines: On PostgreSQL, changing a field definition involves adding a new field and dropping an old one which* causes any indices, primary keys and sequences (from serial-type fields) that use the changed field to be dropped. On MySQL, all type 'serial' fields must be part of at least one key or index as soon as they are created. You cannot use db_add_{primary_key,unique_key,index}() for this purpose because the ALTER TABLE command will fail to add the column without a key or index specification. The solution is to use the optional keys_new argument to create the key or index at the same time as field. You could use db_add_{primary_key,unique_key,index}() in all cases unless you are converting a field to be type serial. You can use the keys_new argument in all cases. @param ret Array to which query results will be added. @param table Name of the table. @param field Name of the field to change. @param field_new New name for the field (set to the same as field if you don't want to change the name). @param spec The field specification for the new field. @param keys_new Optional keys and indexes specification to be created on the table along with changing the field. The format is the same as a table specification but without the 'fields' element. """ php.Reference.check(ret) sql = 'ALTER TABLE {' + table + '} CHANGE ' + field + ' ' + \ _db_create_field_sql(field_new, _db_process_field(spec)) if (php.count(keys_new) > 0): sql += ', ADD ' + php.implode(', ADD ', _db_create_keys_sql(keys_new)) ret.append(update_sql(sql))
def conf_init(): """ Loads the configuration and sets the base URL, cookie domain, and session name correctly. """ # These will come from settings # db_url, db_prefix, cookie_domain, conf, installed_profile, update_free_access if (lib_appglobals.base_url != None): # Parse fixed base URL from settings.php. parts = php.parse_url(lib_appglobals.base_url) if (not php.isset(parts, 'path')): parts['path'] = '' lib_appglobals.base_path = parts['path'] + '/' # Build base_root (everything until first slash after "scheme://"). lib_appglobals.base_root = \ php.substr(lib_appglobals.base_url, 0, \ php.strlen(lib_appglobals.base_url) - \ php.strlen(parts['path'])) else: # Create base URL lib_appglobals.base_root = \ ('https' if (php.isset(php.SERVER, 'HTTPS') and \ php.SERVER['HTTPS'] == 'on') else 'http') # As php.SERVER['HTTP_HOST'] is user input, ensure it only contains # characters allowed in hostnames. lib_appglobals.base_root += '://' + \ php.preg_replace('/[^a-z0-9-:._]/i', '', \ php.SERVER['HTTP_HOST']) lib_appglobals.base_url = lib_appglobals.base_root # php.SERVER['SCRIPT_NAME'] can, in contrast to php.SERVER['PHP_SELF'], not # be modified by a visitor. dir = php.trim(php.dirname(php.SERVER['SCRIPT_NAME']), '\,/') if (len(dir) > 0): lib_appglobals.base_path = "/dir" lib_appglobals.base_url += lib_appglobals.base_path lib_appglobals.base_path += '/' else: lib_appglobals.base_path = '/' if (settings.cookie_domain != None): # If the user specifies the cookie domain, also use it for session name. session_name_ = settings.cookie_domain else: # Otherwise use base_url as session name, without the protocol # to use the same session identifiers across http and https. session_name_ = php.explode('://', lib_appglobals.base_url, 2)[1] # We escape the hostname because it can be modified by a visitor. if (not php.empty(php.SERVER['HTTP_HOST'])): settings.cookie_domain = check_plain(php.SERVER['HTTP_HOST']) # To prevent session cookies from being hijacked, a user can configure the # SSL version of their website to only transfer session cookies via SSL by # using PHP's session.cookie_secure setting. The browser will then use two # separate session cookies for the HTTPS and HTTP versions of the site. So we # must use different session identifiers for HTTPS and HTTP to prevent a # cookie collision. if (php.ini_get('session.cookie_secure')): session_name_ += 'SSL' # Strip leading periods, www., and port numbers from cookie domain. settings.cookie_domain = php.ltrim(settings.cookie_domain, '.') if (php.strpos(settings.cookie_domain, 'www.') == 0): settings.cookie_domain = php.substr(settings.cookie_domain, 4) settings.cookie_domain = php.explode(':', settings.cookie_domain) settings.cookie_domain = '.' + settings.cookie_domain[0] # Per RFC 2109, cookie domains must contain at least one dot other than the # first. For hosts such as 'localhost' or IP Addresses we don't set a # cookie domain. if (php.count(php.explode('.', settings.cookie_domain)) > 2 and not \ php.is_numeric(php.str_replace('.', '', settings.cookie_domain))): php.ini_set('session.cookie_domain', settings.cookie_domain) #print session_name; lib_session.name('SESS' + php.md5(session_name_))
def change_field(ret, table, field, field_new, spec, keys_new = []): """ Change a field definition. IMPORTANT NOTE: To maintain database portability, you have to explicitly recreate all indices and primary keys that are using the changed field. That means that you have to drop all affected keys and indexes with db_drop_{primary_key,unique_key,index}() before calling db_change_field(). To recreate the keys and indices, pass the key definitions as the optional keys_new argument directly to db_change_field(). For example, suppose you have: @code schema['foo'] = array( 'fields' : array( 'bar' : array('type' : 'int', 'not None' : True) ), 'primary key' : array('bar') ) @endcode and you want to change foo.bar to be type serial, leaving it as the primary key. The correct sequence is: @code db_drop_primary_key(ret, 'foo') db_change_field(ret, 'foo', 'bar', 'bar', array('type' : 'serial', 'not None' : True), array('primary key' : array('bar'))) @endcode The reasons for this are due to the different database engines: On PostgreSQL, changing a field definition involves adding a new field and dropping an old one which* causes any indices, primary keys and sequences (from serial-type fields) that use the changed field to be dropped. On MySQL, all type 'serial' fields must be part of at least one key or index as soon as they are created. You cannot use db_add_{primary_key,unique_key,index}() for this purpose because the ALTER TABLE command will fail to add the column without a key or index specification. The solution is to use the optional keys_new argument to create the key or index at the same time as field. You could use db_add_{primary_key,unique_key,index}() in all cases unless you are converting a field to be type serial. You can use the keys_new argument in all cases. @param ret Array to which query results will be added. @param table Name of the table. @param field Name of the field to change. @param field_new New name for the field (set to the same as field if you don't want to change the name). @param spec The field specification for the new field. @param keys_new Optional keys and indexes specification to be created on the table along with changing the field. The format is the same as a table specification but without the 'fields' element. """ php.Reference.check(ret) sql = 'ALTER TABLE {' + table + '} CHANGE ' + field + ' ' + \ _db_create_field_sql(field_new, _db_process_field(spec)) if (php.count(keys_new) > 0): sql += ', ADD ' + php.implode(', ADD ', _db_create_keys_sql(keys_new)) ret.append( update_sql(sql) )
def conf_init(): """ Loads the configuration and sets the base URL, cookie domain, and session name correctly. """ # These will come from settings # db_url, db_prefix, cookie_domain, conf, installed_profile, update_free_access if lib_appglobals.base_url != None: # Parse fixed base URL from settings.php. parts = php.parse_url(lib_appglobals.base_url) if not php.isset(parts, "path"): parts["path"] = "" lib_appglobals.base_path = parts["path"] + "/" # Build base_root (everything until first slash after "scheme://"). lib_appglobals.base_root = php.substr( lib_appglobals.base_url, 0, php.strlen(lib_appglobals.base_url) - php.strlen(parts["path"]) ) else: # Create base URL lib_appglobals.base_root = ( "https" if (php.isset(php.SERVER, "HTTPS") and php.SERVER["HTTPS"] == "on") else "http" ) # As php.SERVER['HTTP_HOST'] is user input, ensure it only contains # characters allowed in hostnames. lib_appglobals.base_root += "://" + php.preg_replace("/[^a-z0-9-:._]/i", "", php.SERVER["HTTP_HOST"]) lib_appglobals.base_url = lib_appglobals.base_root # php.SERVER['SCRIPT_NAME'] can, in contrast to php.SERVER['PHP_SELF'], not # be modified by a visitor. dir = php.trim(php.dirname(php.SERVER["SCRIPT_NAME"]), "\,/") if len(dir) > 0: lib_appglobals.base_path = "/dir" lib_appglobals.base_url += lib_appglobals.base_path lib_appglobals.base_path += "/" else: lib_appglobals.base_path = "/" if settings.cookie_domain != None: # If the user specifies the cookie domain, also use it for session name. session_name_ = settings.cookie_domain else: # Otherwise use base_url as session name, without the protocol # to use the same session identifiers across http and https. session_name_ = php.explode("://", lib_appglobals.base_url, 2)[1] # We escape the hostname because it can be modified by a visitor. if not php.empty(php.SERVER["HTTP_HOST"]): settings.cookie_domain = check_plain(php.SERVER["HTTP_HOST"]) # To prevent session cookies from being hijacked, a user can configure the # SSL version of their website to only transfer session cookies via SSL by # using PHP's session.cookie_secure setting. The browser will then use two # separate session cookies for the HTTPS and HTTP versions of the site. So we # must use different session identifiers for HTTPS and HTTP to prevent a # cookie collision. if php.ini_get("session.cookie_secure"): session_name_ += "SSL" # Strip leading periods, www., and port numbers from cookie domain. settings.cookie_domain = php.ltrim(settings.cookie_domain, ".") if php.strpos(settings.cookie_domain, "www.") == 0: settings.cookie_domain = php.substr(settings.cookie_domain, 4) settings.cookie_domain = php.explode(":", settings.cookie_domain) settings.cookie_domain = "." + settings.cookie_domain[0] # Per RFC 2109, cookie domains must contain at least one dot other than the # first. For hosts such as 'localhost' or IP Addresses we don't set a # cookie domain. if php.count(php.explode(".", settings.cookie_domain)) > 2 and not php.is_numeric( php.str_replace(".", "", settings.cookie_domain) ): php.ini_set("session.cookie_domain", settings.cookie_domain) # print session_name; lib_session.name("SESS" + php.md5(session_name_))
def save_upload(source, validators = {}, dest = False, \ replace = FILE_EXISTS_RENAME): """ Saves a file upload to a new location + The source file is validated as a proper upload and handled as such. The file will be added to the files table as a temporary file. Temporary files are periodically cleaned + To make the file permanent file call file_set_status() to change its status. @param source A string specifying the name of the upload field to save. @param validators An optional, associative array of callback functions used to validate the file + The keys are function names and the values arrays of callback parameters which will be passed in after the user and file objects + The functions should return an array of error messages, an empty array indicates that the file passed validation. The functions will be called in the order specified. @param dest A string containing the directory source should be copied to + If this is not provided or is not writable, the temporary directory will be used. @param replace A boolean indicating whether an existing file of the same name in the destination directory should overwritten + A False value will generate a new, unique filename in the destination directory. @return An object containing the file information, or False in the event of an error. """ php.static(file_save_upload, 'upload_cache', {}) # Add in our check of the the file name length. validators['file_validate_name_length'] = {} # Return cached objects without processing since the file will have # already been processed and the paths in FILES will be invalid. if (php.isset(file_save_upload.uploadcache, source)): return file_save_upload.uploadcache[source] # If a file was uploaded, process it. if (php.isset(p.FILES, 'files') and p.FILES['files']['name'][source] and \ php.is_uploaded_file(p.FILES['files']['tmp_name'][source])): # Check for file upload errors and return False if a # lower level system error occurred. # @see http://php.net/manual/en/features.file-upload.errors.php if p.FILES['files']['error'][source] == UPLOAD_ERR_OK: pass elif p.FILES['files']['error'][source] == UPLOAD_ERR_INI_SIZE or \ p.FILES['files']['error'][source] == UPLOAD_ERR_FORM_SIZE: drupal_set_message(t(\ 'The file %file could not be saved, because it exceeds %maxsize, ' + \ 'the maximum allowed size for uploads.', \ {'%file' : source, '%maxsize' : \ format_size(file_upload_max_size())}), 'error') return False elif p.FILES['files']['error'][source] == UPLOAD_ERR_PARTIAL or \ p.FILES['files']['error'][source] == UPLOAD_ERR_NO_FILE: drupal_set_message(t('The file %file could not be saved, ' + \ 'because the upload did not complete.', {'%file' : source}), 'error') return False # Unknown error else: drupal_set_message(t('The file %file could not be saved. ' + \ 'An unknown error has occurred.', {'%file' : source}), 'error') return False # Build the list of non-munged extensions. # @todo: this should not be here + we need to figure out the right place. extensions = '' for rid, name in lib_appglobals.user.roles.items(): extensions += ' ' + variable_get("upload_extensions_rid", variable_get('upload_extensions_default', \ 'jpg jpeg gif png txt html doc xls pdf ppt pps odt ods odp')) # Begin building file object. file = php.stdClass() file.filename = file_munge_filename(php.trim(\ basename(p.FILES['files']['name'][source]), '.'), extensions) file.filepath = p.FILES['files']['tmp_name'][source] file.filemime = p.FILES['files']['type'][source] # Rename potentially executable files, to help prevent exploits. if (php.preg_match('/\.(php|pl|py|cgi|asp|js)$/i', file.filename) and \ (php.substr(file.filename, -4) != '.txt')): file.filemime = 'text/plain' file.filepath += '.txt' file.filename += '.txt' # If the destination is not provided, or is not writable, then use the # temporary directory. if (php.empty(dest) or file_check_path(dest) == False): dest = file_directory_temp() file.source = source file.destination = file_destination(file_create_path(dest + '/' + \ file.filename), replace) file.filesize = FILES['files']['size'][source] # Call the validation functions. errors = {} for function, args in validators.items(): array_unshift(args, file) errors = php.array_merge(errors, function(*args)) # Check for validation errors. if (not php.empty(errors)): message = t('The selected file %name could not be uploaded.', \ {'%name' : file.filename}) if (php.count(errors) > 1): message += '<ul><li>' + php.implode('</li><li>', errors) + '</li></ul>' else: message += ' ' + php.array_pop(errors) form_set_error(source, message) return False # Move uploaded files from PHP's upload_tmp_dir to # Drupal's temporary directory. # This overcomes open_basedir restrictions for future file operations. file.filepath = file.destination if (not move_uploaded_file(p.FILES['files']['tmp_name'][source], \ file.filepath)): form_set_error(source, t('File upload error. ' + \ 'Could not move uploaded file.')) watchdog('file', 'Upload error + Could not move uploaded file ' + \ '%file to destination %destination.', \ {'%file' : file.filename, '%destination' : file.filepath}) return False # If we made it this far it's safe to record this file in the database. file.uid = lib_appglobals.user.uid file.status = FILE_STATUS_TEMPORARY file.timestamp = time() drupal_write_record('files', file) # Add file to the cache. file_save_upload.upload_cache[source] = file return file return False