From 5e1e0b1d5f2f38726474c3793c7f414d9565f3a3 Mon Sep 17 00:00:00 2001 From: webchick Date: Mon, 5 Sep 2011 11:35:44 -0700 Subject: [PATCH 01/80] Issue #787876 by zero2one, avior: Fixed Edit 'My Account' fills the first password field. --- modules/user/user.module | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/user/user.module b/modules/user/user.module index db0591fd7b1c..1355159a90c3 100644 --- a/modules/user/user.module +++ b/modules/user/user.module @@ -1065,6 +1065,7 @@ function user_account_form(&$form, &$form_state) { '#access' => !empty($protected_values), '#description' => $current_pass_description, '#weight' => -5, + '#attributes' => array('autocomplete' => 'off'), ); $form['#validate'][] = 'user_validate_current_pass'; } From 54c1c7fb0757d4a3eb08ac1f4ae429538a1d09a4 Mon Sep 17 00:00:00 2001 From: webchick Date: Mon, 5 Sep 2011 11:43:38 -0700 Subject: [PATCH 02/80] Issue #937380 by David_Rothstein, swentel: Fixed errors when submitting the shortcut set configuration page after deleting all shortcuts. --- modules/shortcut/shortcut.admin.inc | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/modules/shortcut/shortcut.admin.inc b/modules/shortcut/shortcut.admin.inc index 9735d376079a..4c8bf53afedf 100644 --- a/modules/shortcut/shortcut.admin.inc +++ b/modules/shortcut/shortcut.admin.inc @@ -264,6 +264,7 @@ function shortcut_set_add_form_submit($form, &$form_state) { * @see shortcut_set_customize_submit() */ function shortcut_set_customize($form, &$form_state, $shortcut_set) { + $form['#shortcut_set_name'] = $shortcut_set->set_name; $form['shortcuts'] = array( '#tree' => TRUE, '#weight' => -20, @@ -299,7 +300,10 @@ function shortcut_set_customize($form, &$form_state, $shortcut_set) { 'js' => array(drupal_get_path('module', 'shortcut') . '/shortcut.admin.js'), ); - $form['actions'] = array('#type' => 'actions'); + $form['actions'] = array( + '#type' => 'actions', + '#access' => !empty($shortcut_set->links), + ); $form['actions']['submit'] = array( '#type' => 'submit', '#value' => t('Save changes'), @@ -336,9 +340,15 @@ function shortcut_set_customize_submit($form, &$form_state) { function theme_shortcut_set_customize($variables) { $form = $variables['form']; $map = array('disabled' => t('Disabled'), 'enabled' => t('Enabled')); + $shortcuts_by_status = array( + 'enabled' => element_children($form['shortcuts']['enabled']), + 'disabled' => element_children($form['shortcuts']['disabled']), + ); + // Do not add any rows to the table if there are no shortcuts to display. + $statuses = empty($shortcuts_by_status['enabled']) && empty($shortcuts_by_status['disabled']) ? array() : array_keys($shortcuts_by_status); $rows = array(); - foreach (array('enabled', 'disabled') as $status) { + foreach ($statuses as $status) { drupal_add_tabledrag('shortcuts', 'match', 'sibling', 'shortcut-status-select'); drupal_add_tabledrag('shortcuts', 'order', 'sibling', 'shortcut-weight'); $rows[] = array( @@ -349,7 +359,7 @@ function theme_shortcut_set_customize($variables) { 'class' => array('shortcut-status', 'shortcut-status-' . $status), ); - foreach (element_children($form['shortcuts'][$status]) as $key) { + foreach ($shortcuts_by_status[$status] as $key) { $shortcut = &$form['shortcuts'][$status][$key]; $row = array(); $row[] = drupal_render($shortcut['name']); @@ -373,7 +383,7 @@ function theme_shortcut_set_customize($variables) { 'class' => array('shortcut-slot-empty'), ); } - $count_shortcuts = count(element_children($form['shortcuts'][$status])); + $count_shortcuts = count($shortcuts_by_status[$status]); if (!empty($count_shortcuts)) { for ($i = 0; $i < min($count_shortcuts, shortcut_max_slots()); $i++) { $rows['empty-' . $i]['class'][] = 'shortcut-slot-hidden'; @@ -383,7 +393,7 @@ function theme_shortcut_set_customize($variables) { } $header = array(t('Name'), t('Weight'), t('Status'), array('data' => t('Operations'), 'colspan' => 2)); - $output = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'shortcuts'))); + $output = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'shortcuts'), 'empty' => t('No shortcuts available. Add a shortcut.', array('@link' => url('admin/config/user-interface/shortcut/' . $form['#shortcut_set_name'] . '/add-link'))))); $output .= drupal_render($form['actions']); $output = drupal_render_children($form) . $output; return $output; From 446031031f09b088b3e4cbf1731f8ea1745761e9 Mon Sep 17 00:00:00 2001 From: webchick Date: Mon, 5 Sep 2011 11:59:30 -0700 Subject: [PATCH 03/80] Issue #1069778 by msonnabaum: Fixed Conflicting messages from user_pass_submit(). --- modules/user/user.pages.inc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/user/user.pages.inc b/modules/user/user.pages.inc index 09bf33b6bfd7..02870e9e65a8 100644 --- a/modules/user/user.pages.inc +++ b/modules/user/user.pages.inc @@ -76,9 +76,11 @@ function user_pass_submit($form, &$form_state) { $account = $form_state['values']['account']; // Mail one time login URL and instructions using current language. - _user_mail_notify('password_reset', $account, $language); - watchdog('user', 'Password reset instructions mailed to %name at %email.', array('%name' => $account->name, '%email' => $account->mail)); - drupal_set_message(t('Further instructions have been sent to your e-mail address.')); + $mail = _user_mail_notify('password_reset', $account, $language); + if (!empty($mail)) { + watchdog('user', 'Password reset instructions mailed to %name at %email.', array('%name' => $account->name, '%email' => $account->mail)); + drupal_set_message(t('Further instructions have been sent to your e-mail address.')); + } $form_state['redirect'] = 'user'; return; From 7379f95ee590c79b4838f7896fd46faf700c7b08 Mon Sep 17 00:00:00 2001 From: webchick Date: Mon, 5 Sep 2011 12:05:36 -0700 Subject: [PATCH 04/80] Issue #575810 by wojtha, Heine, vzima: Fixed OpenID discovery spec violation - follow redirects. --- modules/openid/openid.api.php | 10 ++- modules/openid/openid.module | 107 ++++++++++++++++++------ modules/openid/openid.test | 57 +++++++++++++ modules/openid/tests/openid_test.module | 35 ++++++++ 4 files changed, 180 insertions(+), 29 deletions(-) diff --git a/modules/openid/openid.api.php b/modules/openid/openid.api.php index 11faa71efa89..5e3d15d94cbe 100644 --- a/modules/openid/openid.api.php +++ b/modules/openid/openid.api.php @@ -49,8 +49,13 @@ function hook_openid_response($response, $account) { * Allow modules to declare OpenID discovery methods. * * The discovery function callbacks will be called in turn with an unique - * parameter, the claimed identifier. They have to return an array of services, - * in the same form returned by openid_discover(). + * parameter, the claimed identifier. They have to return an associative array + * with array of services and claimed identifier in the same form as returned by + * openid_discover(). The resulting array must contain following keys: + * - 'services' (required) an array of discovered services (including OpenID + * version, endpoint URI, etc). + * - 'claimed_id' (optional) new claimed identifer, found by following HTTP + * redirects during the services discovery. * * The first discovery method that succeed (return at least one services) will * stop the discovery process. @@ -58,6 +63,7 @@ function hook_openid_response($response, $account) { * @return * An associative array which keys are the name of the discovery methods and * values are function callbacks. + * * @see hook_openid_discovery_method_info_alter() */ function hook_openid_discovery_method_info() { diff --git a/modules/openid/openid.module b/modules/openid/openid.module index 6d4b1d7ff78f..bb6ad712bebb 100644 --- a/modules/openid/openid.module +++ b/modules/openid/openid.module @@ -256,16 +256,25 @@ function openid_login_validate($form, &$form_state) { function openid_begin($claimed_id, $return_to = '', $form_values = array()) { module_load_include('inc', 'openid'); + $service = NULL; $claimed_id = openid_normalize($claimed_id); + $discovery = openid_discovery($claimed_id); - $services = openid_discovery($claimed_id); - $service = _openid_select_service($services); + if (!empty($discovery['services'])) { + $service = _openid_select_service($discovery['services']); + } - if (!$service) { + // Quit if the discovery result was empty or if we can't select any service. + if (!$discovery || !$service) { form_set_error('openid_identifier', t('Sorry, that is not a valid OpenID. Ensure you have spelled your ID correctly.')); return; } + // Set claimed id from discovery. + if (!empty($discovery['claimed_id'])) { + $claimed_id = $discovery['claimed_id']; + } + // Store discovered information in the users' session so we don't have to rediscover. $_SESSION['openid']['service'] = $service; // Store the claimed id @@ -352,11 +361,13 @@ function openid_complete($response = array()) { // identififer to make sure that the provider is authorized to // respond on behalf of this. if ($response_claimed_id != $claimed_id) { - $services = openid_discovery($response_claimed_id); - $uris = array(); - foreach ($services as $discovered_service) { - if (in_array('http://specs.openid.net/auth/2.0/server', $discovered_service['types']) || in_array('http://specs.openid.net/auth/2.0/signon', $discovered_service['types'])) { - $uris[] = $discovered_service['uri']; + $discovery = openid_discovery($response['openid.claimed_id']); + if ($discovery && !empty($discovery['services'])) { + $uris = array(); + foreach ($discovery['services'] as $discovered_service) { + if (in_array('http://specs.openid.net/auth/2.0/server', $discovered_service['types']) || in_array('http://specs.openid.net/auth/2.0/signon', $discovered_service['types'])) { + $uris[] = $discovered_service['uri']; + } } } if (!in_array($service['uri'], $uris)) { @@ -378,10 +389,21 @@ function openid_complete($response = array()) { /** * Perform discovery on a claimed ID to determine the OpenID provider endpoint. * - * @param $claimed_id The OpenID URL to perform discovery on. + * Discovery methods are provided by the hook_openid_discovery_method_info and + * could be further altered using the hook_openid_discovery_method_info_alter. * - * @return Array of services discovered (including OpenID version, endpoint - * URI, etc). + * @param $claimed_id + * The OpenID URL to perform discovery on. + * + * @return + * The resulting discovery array from the first successful discovery method, + * which must contain following keys: + * - 'services' (required) an array of discovered services (including OpenID + * version, endpoint URI, etc). + * - 'claimed_id' (optional) new claimed identifer, found by following HTTP + * redirects during the services discovery. + * If all the discovery method fails or if no appropriate discovery method is + * found, FALSE is returned. */ function openid_discovery($claimed_id) { module_load_include('inc', 'openid'); @@ -389,15 +411,15 @@ function openid_discovery($claimed_id) { $methods = module_invoke_all('openid_discovery_method_info'); drupal_alter('openid_discovery_method_info', $methods); - // Execute each method in turn. + // Execute each method in turn and return first successful discovery. foreach ($methods as $method) { - $discovered_services = $method($claimed_id); - if (!empty($discovered_services)) { - return $discovered_services; + $discovery = $method($claimed_id); + if (!empty($discovery)) { + return $discovery; } } - return array(); + return FALSE; } /** @@ -421,24 +443,33 @@ function openid_openid_discovery_method_info() { * * @see http://openid.net/specs/openid-authentication-2_0.html#discovery * @see hook_openid_discovery_method_info() + * @see openid_discovery() + * + * @return + * An array of discovered services and claimed identifier or NULL. See + * openid_discovery() for more specific information. */ function _openid_xri_discovery($claimed_id) { if (_openid_is_xri($claimed_id)) { // Resolve XRI using a proxy resolver (Extensible Resource Identifier (XRI) // Resolution Version 2.0, section 11.2 and 14.3). $xrds_url = variable_get('xri_proxy_resolver', 'http://xri.net/') . rawurlencode($claimed_id) . '?_xrd_r=application/xrds+xml'; - $services = _openid_xrds_discovery($xrds_url); - foreach ($services as $i => &$service) { - $status = $service['xrd']->children(OPENID_NS_XRD)->Status; - if ($status && $status->attributes()->cid == 'verified') { - $service['claimed_id'] = openid_normalize((string)$service['xrd']->children(OPENID_NS_XRD)->CanonicalID); + $discovery = _openid_xrds_discovery($xrds_url); + if (!empty($discovery['services']) && is_array($discovery['services'])) { + foreach ($discovery['services'] as $i => &$service) { + $status = $service['xrd']->children(OPENID_NS_XRD)->Status; + if ($status && $status->attributes()->cid == 'verified') { + $service['claimed_id'] = openid_normalize((string)$service['xrd']->children(OPENID_NS_XRD)->CanonicalID); + } + else { + // Ignore service if the Canonical ID could not be verified. + unset($discovery['services'][$i]); + } } - else { - // Ignore service if CanonicalID could not be verified. - unset($services[$i]); + if (!empty($discovery['services'])) { + return $discovery; } } - return $services; } } @@ -447,6 +478,11 @@ function _openid_xri_discovery($claimed_id) { * * @see http://openid.net/specs/openid-authentication-2_0.html#discovery * @see hook_openid_discovery_method_info() + * @see openid_discovery() + * + * @return + * An array of discovered services and claimed identifier or NULL. See + * openid_discovery() for more specific information. */ function _openid_xrds_discovery($claimed_id) { $services = array(); @@ -458,7 +494,18 @@ function _openid_xrds_discovery($claimed_id) { $headers = array('Accept' => 'application/xrds+xml'); $result = drupal_http_request($xrds_url, array('headers' => $headers)); - if (!isset($result->error)) { + // Check for HTTP error and make sure, that we reach the target. If the + // maximum allowed redirects are exhausted, final destination URL isn't + // reached, but drupal_http_request() doesn't return any error. + // @todo Remove the check for 200 HTTP result code after the following issue + // will be fixed: http://drupal.org/node/1096890. + if (!isset($result->error) && $result->code == 200) { + + // Replace the user-entered claimed_id if we received a redirect. + if (!empty($result->redirect_url)) { + $claimed_id = openid_normalize($result->redirect_url); + } + if (isset($result->headers['content-type']) && preg_match("/application\/xrds\+xml/", $result->headers['content-type'])) { // Parse XML document to find URL $services = _openid_xrds_parse($result->data); @@ -504,7 +551,13 @@ function _openid_xrds_discovery($claimed_id) { } } } - return $services; + + if (!empty($services)) { + return array( + 'services' => $services, + 'claimed_id' => $claimed_id, + ); + } } /** diff --git a/modules/openid/openid.test b/modules/openid/openid.test index 09632ba1417c..6e2528e66af2 100644 --- a/modules/openid/openid.test +++ b/modules/openid/openid.test @@ -124,6 +124,28 @@ class OpenIDFunctionalTestCase extends OpenIDWebTestCase { // OpenID Authentication 2.0, section 7.3.3: $this->addIdentity(url('openid-test/html/openid2', array('absolute' => TRUE)), 2, 'http://example.com/html-openid2'); + + // OpenID Authentication 2.0, section 7.2.4: + // URL Identifiers MUST then be further normalized by both (1) following + // redirects when retrieving their content and finally (2) applying the + // rules in Section 6 of RFC3986 to the final destination URL. This final + // URL MUST be noted by the Relying Party as the Claimed Identifier and be + // used when requesting authentication. + + // Single redirect. + $identity = $expected_claimed_id = url('openid-test/redirected/yadis/xrds/1', array('absolute' => TRUE)); + $this->addRedirectedIdentity($identity, 2, 'http://example.com/xrds', $expected_claimed_id, 0); + + // Exact 3 redirects (default value for the 'max_redirects' option in + // drupal_http_request()). + $identity = $expected_claimed_id = url('openid-test/redirected/yadis/xrds/2', array('absolute' => TRUE)); + $this->addRedirectedIdentity($identity, 2, 'http://example.com/xrds', $expected_claimed_id, 2); + + // Fails because there are more than 3 redirects (default value for the + // 'max_redirects' option in drupal_http_request()). + $identity = url('openid-test/redirected/yadis/xrds/3', array('absolute' => TRUE)); + $expected_claimed_id = FALSE; + $this->addRedirectedIdentity($identity, 2, 'http://example.com/xrds', $expected_claimed_id, 3); } /** @@ -279,6 +301,41 @@ class OpenIDFunctionalTestCase extends OpenIDWebTestCase { $this->assertRaw(t('Successfully added %identity', array('%identity' => $claimed_id)), t('Identity %identity was added.', array('%identity' => $identity))); } + /** + * Add OpenID identity, changed by the following redirects, to user's profile. + * + * According to OpenID Authentication 2.0, section 7.2.4, URL Identifiers MUST + * be further normalized by following redirects when retrieving their content + * and this final URL MUST be noted by the Relying Party as the Claimed + * Identifier and be used when requesting authentication. + * + * @param $identity + * The User-supplied Identifier. + * @param $version + * The protocol version used by the service. + * @param $local_id + * The expected OP-Local Identifier found during discovery. + * @param $claimed_id + * The expected Claimed Identifier returned by the OpenID Provider, or FALSE + * if the discovery is expected to fail. + * @param $redirects + * The number of redirects. + */ + function addRedirectedIdentity($identity, $version = 2, $local_id = 'http://example.com/xrds', $claimed_id = NULL, $redirects = 0) { + // Set the final destination URL which is the same as the Claimed + // Identifier, we insert the same identifier also to the provider response, + // but provider could futher change the Claimed ID actually (e.g. it could + // add unique fragment). + variable_set('openid_test_redirect_url', $identity); + variable_set('openid_test_response', array('openid.claimed_id' => $identity)); + + $this->addIdentity(url('openid-test/redirect/' . $redirects, array('absolute' => TRUE)), $version, $local_id, $claimed_id); + + // Clean up. + variable_del('openid_test_redirect_url'); + variable_del('openid_test_response'); + } + /** * Tests that openid.signed is verified. */ diff --git a/modules/openid/tests/openid_test.module b/modules/openid/tests/openid_test.module index bad1184a311a..629dcd3356a5 100644 --- a/modules/openid/tests/openid_test.module +++ b/modules/openid/tests/openid_test.module @@ -60,6 +60,19 @@ function openid_test_menu() { 'access callback' => TRUE, 'type' => MENU_CALLBACK, ); + $items['openid-test/redirect'] = array( + 'title' => 'OpenID Provider Redirection Point', + 'page callback' => 'openid_test_redirect', + 'access callback' => TRUE, + 'type' => MENU_CALLBACK, + ); + $items['openid-test/redirected/%/%'] = array( + 'title' => 'OpenID Provider Final URL', + 'page callback' => 'openid_test_redirected_method', + 'page arguments' => array(2, 3), + 'access callback' => TRUE, + 'type' => MENU_CALLBACK, + ); return $items; } @@ -212,6 +225,28 @@ function openid_test_endpoint() { } } +/** + * Menu callback; redirect during Normalization/Discovery. + */ +function openid_test_redirect($count = 0) { + if ($count == 0) { + $url = variable_get('openid_test_redirect_url', ''); + } + else { + $url = url('openid-test/redirect/' . --$count, array('absolute' => TRUE)); + } + $http_response_code = variable_get('openid_test_redirect_http_reponse_code', 301); + header('Location: ' . $url, TRUE, $http_response_code); + exit(); +} + +/** + * Menu callback; respond with appropriate callback. + */ +function openid_test_redirected_method($method1, $method2) { + return call_user_func('openid_test_' . $method1 . '_' . $method2); +} + /** * OpenID endpoint; handle "associate" requests (see OpenID Authentication 2.0, * section 8). From 75380e5f327eb57af19754efc25798d5ee98af97 Mon Sep 17 00:00:00 2001 From: webchick Date: Mon, 5 Sep 2011 12:08:11 -0700 Subject: [PATCH 05/80] Issue #1118016 by dereine, pwolanin: Fixed conditional visibility of a managed_file() using #states attribute does not work. --- includes/form.inc | 12 +++++++++++- modules/file/file.module | 11 ++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/includes/form.inc b/includes/form.inc index 40363d6a738e..442016af0268 100644 --- a/includes/form.inc +++ b/includes/form.inc @@ -2796,7 +2796,17 @@ function password_confirm_validate($element, &$element_state) { */ function theme_date($variables) { $element = $variables['element']; - return '
' . drupal_render_children($element) . '
'; + + $attributes = array(); + if (isset($element['#id'])) { + $attributes['id'] = $element['#id']; + } + if (!empty($element['#attributes']['class'])) { + $attributes['class'] = (array) $element['#attributes']['class']; + } + $attributes['class'][] = 'container-inline'; + + return '' . drupal_render_children($element) . ''; } /** diff --git a/modules/file/file.module b/modules/file/file.module index 83de0f622aee..4fc9985014e7 100644 --- a/modules/file/file.module +++ b/modules/file/file.module @@ -643,9 +643,18 @@ function file_managed_file_save_upload($element) { function theme_file_managed_file($variables) { $element = $variables['element']; + $attributes = array(); + if (isset($element['#id'])) { + $attributes['id'] = $element['#id']; + } + if (!empty($element['#attributes']['class'])) { + $attributes['class'] = (array) $element['#attributes']['class']; + } + $attributes['class'][] = 'form-managed-file'; + // This wrapper is required to apply JS behaviors and CSS styling. $output = ''; - $output .= '
'; + $output .= ''; $output .= drupal_render_children($element); $output .= '
'; return $output; From 0a3c0284e8c09a2e95b4068f6d277ea8c774de9d Mon Sep 17 00:00:00 2001 From: webchick Date: Mon, 5 Sep 2011 12:32:55 -0700 Subject: [PATCH 06/80] Issue #799932 by David_Rothstein, Stefan Freudenberg: Fixed Simpletest HTTP authentication credentials should use the 'password' form element. --- modules/simpletest/simpletest.pages.inc | 34 ++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/modules/simpletest/simpletest.pages.inc b/modules/simpletest/simpletest.pages.inc index a39e8b792b49..d2d4a91bc6c7 100644 --- a/modules/simpletest/simpletest.pages.inc +++ b/modules/simpletest/simpletest.pages.inc @@ -428,6 +428,9 @@ function simpletest_result_status_image($status) { /** * Provides settings form for SimpleTest variables. + * + * @ingroup forms + * @see simpletest_settings_form_validate() */ function simpletest_settings_form($form, &$form_state) { $form['general'] = array( @@ -467,16 +470,41 @@ function simpletest_settings_form($form, &$form_state) { ), '#default_value' => variable_get('simpletest_httpauth_method', CURLAUTH_BASIC), ); + $username = variable_get('simpletest_httpauth_username'); + $password = variable_get('simpletest_httpauth_password'); $form['httpauth']['simpletest_httpauth_username'] = array( '#type' => 'textfield', '#title' => t('Username'), - '#default_value' => variable_get('simpletest_httpauth_username', ''), + '#default_value' => $username, ); + if ($username && $password) { + $form['httpauth']['simpletest_httpauth_username']['#description'] = t('Leave this blank to delete both the existing username and password.'); + } $form['httpauth']['simpletest_httpauth_password'] = array( - '#type' => 'textfield', + '#type' => 'password', '#title' => t('Password'), - '#default_value' => variable_get('simpletest_httpauth_password', ''), ); + if ($password) { + $form['httpauth']['simpletest_httpauth_password']['#description'] = t('To change the password, enter the new password here.'); + } return system_settings_form($form); } + +/** + * Validation handler for simpletest_settings_form(). + */ +function simpletest_settings_form_validate($form, &$form_state) { + // If a username was provided but a password wasn't, preserve the existing + // password. + if (!empty($form_state['values']['simpletest_httpauth_username']) && empty($form_state['values']['simpletest_httpauth_password'])) { + $form_state['values']['simpletest_httpauth_password'] = variable_get('simpletest_httpauth_password', ''); + } + + // If a password was provided but a username wasn't, the credentials are + // incorrect, so throw an error. + if (empty($form_state['values']['simpletest_httpauth_username']) && !empty($form_state['values']['simpletest_httpauth_password'])) { + form_set_error('simpletest_httpauth_username', t('HTTP authentication credentials must include a username in addition to a password.')); + } +} + From bbceb3e853c247dfa50b202ad7f2f753ffc71dfd Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Wed, 7 Sep 2011 06:27:05 -0400 Subject: [PATCH 07/80] - Patch #1267220 by dixon_: dixon_ as Comment maintainer in MAINTAINERS.txt. --- MAINTAINERS.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS.txt b/MAINTAINERS.txt index 346cd4e28df5..20463808dc29 100644 --- a/MAINTAINERS.txt +++ b/MAINTAINERS.txt @@ -158,7 +158,7 @@ Color module - ? Comment module -- ? +- Dick Olsson 'dixon_' Contact module - Dave Reid 'davereid' From a3769a4e786beec8b795ecbed95576dbf0080dce Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Wed, 7 Sep 2011 06:30:25 -0400 Subject: [PATCH 08/80] - Patch #1266322 by webchick: return feed ID after saving. --- modules/aggregator/aggregator.module | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/aggregator/aggregator.module b/modules/aggregator/aggregator.module index eafb61ed3083..f247d26a33f1 100644 --- a/modules/aggregator/aggregator.module +++ b/modules/aggregator/aggregator.module @@ -478,6 +478,8 @@ function aggregator_save_category($edit) { * * @param $edit * An associative array describing the feed to be added/edited/deleted. + * @return + * The ID of the feed. */ function aggregator_save_feed($edit) { if (!empty($edit['fid'])) { @@ -544,6 +546,8 @@ function aggregator_save_feed($edit) { } } } + + return $edit['fid']; } /** From 4e8ceed648fbfbb2948a7de44f47bb719419c27a Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Wed, 7 Sep 2011 06:32:17 -0400 Subject: [PATCH 09/80] - Patch #1265294 by sven.lauer: minor improvement of the documentation page for t(). --- includes/bootstrap.inc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index 606156946e4c..1f29c246dafd 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -1391,10 +1391,11 @@ function drupal_unpack($obj, $field = 'data') { * The t() function serves two purposes. First, at run-time it translates * user-visible text into the appropriate language. Second, various mechanisms * that figure out what text needs to be translated work off t() -- the text - * inside t() calls is added to the database of strings to be translated. So, - * to enable a fully-translatable site, it is important that all human-readable - * text that will be displayed on the site or sent to a user is passed through - * the t() function, or a related function. See the + * inside t() calls is added to the database of strings to be translated. + * These strings are expected to be in English, so the first argument should + * always be in English. To enable a fully-translatable site, it is important + * that all human-readable text that will be displayed on the site or sent to + * a user is passed through the t() function, or a related function. See the * @link http://drupal.org/node/322729 Localization API @endlink pages for * more information, including recommendations on how to break up or not * break up strings for translation. From 06aebd83b7302bd6e3f63f5c645615aa0851fc3b Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Wed, 7 Sep 2011 06:38:50 -0400 Subject: [PATCH 10/80] - Patch #1265586 by James_Stallings, edmund.kwok: remove bitter tasting 'bile' in core documentation ;). --- includes/common.inc | 2 +- modules/update/update.test | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/common.inc b/includes/common.inc index f54f29a7c39f..e75aa81a1e53 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -6164,7 +6164,7 @@ function element_children(&$elements, $sort = FALSE) { } /** - * Return the visibile children of an element. + * Returns the visible children of an element. * * @param $elements * The parent element. diff --git a/modules/update/update.test b/modules/update/update.test index 4fb8630d76c9..2688bb3aac99 100644 --- a/modules/update/update.test +++ b/modules/update/update.test @@ -13,7 +13,7 @@ * dummy .info file data (specified via hook_system_info_alter() in the * update_test helper module) describing what's currently installed. Each * test case defines a set of projects to install, their current state (via - * the 'update_test_system_info' variable) and the desired availabile update + * the 'update_test_system_info' variable) and the desired available update * data (via the 'update_test_xml_map' variable), and then performs a series * of assertions that the report matches our expectations given the specific * initial state and availability scenario. From 1cd3e54a2efb1318d7dcbf7a906ec08e322d8624 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Wed, 7 Sep 2011 06:44:59 -0400 Subject: [PATCH 11/80] - Patch #1267092 by xjm: some comment cleanup in block.test. --- modules/block/block.test | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/block/block.test b/modules/block/block.test index 03f3048b4eb6..21666871987b 100644 --- a/modules/block/block.test +++ b/modules/block/block.test @@ -83,7 +83,7 @@ class BlockTestCase extends DrupalWebTestCase { $this->assertTrue(array_key_exists('subject', $data) && empty($data['subject']), t('block_block_view() provides an empty block subject, since custom blocks do not have default titles.')); $this->assertEqual(check_markup($custom_block['body[value]'], $format), $data['content'], t('block_block_view() provides correct block content.')); - // Check if the block can be moved to all availble regions. + // Check whether the block can be moved to all available regions. $custom_block['module'] = 'block'; $custom_block['delta'] = $bid; foreach ($this->regions as $region) { @@ -307,7 +307,7 @@ class BlockTestCase extends DrupalWebTestCase { // Check to see if the block was created by checking that it's in the database. $this->assertNotNull($bid, t('Block found in database')); - // Check if the block can be moved to all availble regions. + // Check whether the block can be moved to all available regions. foreach ($this->regions as $region) { $this->moveBlockToRegion($block, $region); } @@ -321,7 +321,7 @@ class BlockTestCase extends DrupalWebTestCase { $this->assertText(t('The block settings have been updated.'), t('Block successfully move to disabled region.')); $this->assertNoText(t($block['title']), t('Block no longer appears on page.')); - // Confirm that the regions xpath is not availble + // Confirm that the region's xpath is not available. $xpath = $this->buildXPathQuery('//div[@id=:id]/*', array(':id' => 'block-block-' . $bid)); $this->assertNoFieldByXPath($xpath, FALSE, t('Custom block found in no regions.')); From 409390637ecc029e27988c6da63a72a49dff7949 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Wed, 7 Sep 2011 06:49:13 -0400 Subject: [PATCH 12/80] - Patch #1097958 by Devin Carlson, dixon_: change class comment_forbidden() to comment-forbidden. --- modules/comment/comment.module | 8 ++++---- themes/bartik/css/print.css | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/comment/comment.module b/modules/comment/comment.module index 8e0c7d930394..37a208f6e97d 100644 --- a/modules/comment/comment.module +++ b/modules/comment/comment.module @@ -655,7 +655,7 @@ function comment_node_view($node, $view_mode) { ); } else { - $links['comment_forbidden'] = array( + $links['comment-forbidden'] = array( 'title' => theme('comment_post_forbidden', array('node' => $node)), 'html' => TRUE, ); @@ -685,7 +685,7 @@ function comment_node_view($node, $view_mode) { } } else { - $links['comment_forbidden'] = array( + $links['comment-forbidden'] = array( 'title' => theme('comment_post_forbidden', array('node' => $node)), 'html' => TRUE, ); @@ -1066,8 +1066,8 @@ function comment_links($comment, $node) { ); } else { - $links['comment_forbidden']['title'] = theme('comment_post_forbidden', array('node' => $node)); - $links['comment_forbidden']['html'] = TRUE; + $links['comment-forbidden']['title'] = theme('comment_post_forbidden', array('node' => $node)); + $links['comment-forbidden']['html'] = TRUE; } } return $links; diff --git a/themes/bartik/css/print.css b/themes/bartik/css/print.css index 61ca3fa7d333..fbe386a43cc8 100644 --- a/themes/bartik/css/print.css +++ b/themes/bartik/css/print.css @@ -41,6 +41,6 @@ body { #comments .title, #comments form, -.comment_forbidden { +.comment-forbidden { display: none; } From a120045c9f048004760d2c42f3b7eb6d66f63b6c Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Wed, 7 Sep 2011 14:38:31 -0400 Subject: [PATCH 13/80] - Patch #81461 by catch, pillarsdotnet, beejeebus: clean up the cache API (stop overloading function arguments, remove procedural wrappers). --- includes/bootstrap.inc | 34 ++-- includes/cache-install.inc | 10 + includes/cache.inc | 210 +++++++++++++------- modules/simpletest/drupal_web_test_case.php | 8 +- modules/simpletest/tests/cache.test | 110 +++++----- 5 files changed, 222 insertions(+), 150 deletions(-) diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index 1f29c246dafd..2d6366739ee9 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -28,7 +28,7 @@ define('DRUPAL_MINIMUM_PHP_MEMORY_LIMIT', '32M'); /** * Indicates that the item should never be removed unless explicitly selected. * - * The item may be removed using cache_clear_all() with a cache ID. + * The item may be removed using cache()->delete() with a cache ID. */ define('CACHE_PERMANENT', 0); @@ -283,12 +283,12 @@ define('DRUPAL_PHP_FUNCTION_PATTERN', '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*' abstract class DrupalCacheArray implements ArrayAccess { /** - * A cid to pass to cache_set() and cache_get(). + * A cid to pass to cache()->set() and cache()->get(). */ private $cid; /** - * A bin to pass to cache_set() and cache_get(). + * A bin to pass to cache()->set() and cache()->get(). */ private $bin; @@ -314,7 +314,7 @@ abstract class DrupalCacheArray implements ArrayAccess { $this->cid = $cid; $this->bin = $bin; - if ($cached = cache_get($this->cid, $this->bin)) { + if ($cached = cache($bin)->get($this->cid)) { $this->storage = $cached->data; } } @@ -391,10 +391,10 @@ abstract class DrupalCacheArray implements ArrayAccess { // To implement locking for cache misses, override __construct(). $lock_name = $cid . ':' . $bin; if (!$lock || lock_acquire($lock_name)) { - if ($cached = cache_get($cid, $bin)) { + if ($cached = cache($bin)->get($cid)) { $data = $cached->data + $data; } - cache_set($cid, $data, $bin); + cache($bin)->set($cid, $data); if ($lock) { lock_release($lock_name); } @@ -899,7 +899,7 @@ function drupal_get_filename($type, $name, $filename = NULL) { function variable_initialize($conf = array()) { // NOTE: caching the variables improves performance by 20% when serving // cached pages. - if ($cached = cache_get('variables', 'cache_bootstrap')) { + if ($cached = cache('bootstrap')->get('variables')) { $variables = $cached->data; } else { @@ -914,7 +914,7 @@ function variable_initialize($conf = array()) { else { // Proceed with variable rebuild. $variables = array_map('unserialize', db_query('SELECT name, value FROM {variable}')->fetchAllKeyed()); - cache_set('variables', $variables, 'cache_bootstrap'); + cache('bootstrap')->set('variables', $variables); lock_release($name); } } @@ -971,7 +971,7 @@ function variable_set($name, $value) { db_merge('variable')->key(array('name' => $name))->fields(array('value' => serialize($value)))->execute(); - cache_clear_all('variables', 'cache_bootstrap'); + cache('bootstrap')->delete('variables'); $conf[$name] = $value; } @@ -995,7 +995,7 @@ function variable_del($name) { db_delete('variable') ->condition('name', $name) ->execute(); - cache_clear_all('variables', 'cache_bootstrap'); + cache('bootstrap')->delete('variables'); unset($conf[$name]); } @@ -1024,7 +1024,7 @@ function drupal_page_get_cache($check_only = FALSE) { } if (drupal_page_is_cacheable()) { - $cache = cache_get($base_root . request_uri(), 'cache_page'); + $cache = cache('page')->get($base_root . request_uri()); if ($cache !== FALSE) { $cache_hit = TRUE; } @@ -2791,7 +2791,7 @@ function drupal_get_complete_schema($rebuild = FALSE) { if (empty($schema) || $rebuild) { // Try to load the schema from cache. - if (!$rebuild && $cached = cache_get('schema')) { + if (!$rebuild && $cached = cache()->get('schema')) { $schema = $cached->data; } // Otherwise, rebuild the schema cache. @@ -2817,7 +2817,7 @@ function drupal_get_complete_schema($rebuild = FALSE) { // That would break modules which use $schema further down the line. $current = (array) module_invoke($module, 'schema'); // Set 'module' and 'name' keys for each table, and remove descriptions, - // as they needlessly slow down cache_get() for every single request. + // as they needlessly slow down cache()->get() for every single request. _drupal_schema_initialize($current, $module); $schema = array_merge($schema, $current); } @@ -2826,10 +2826,10 @@ function drupal_get_complete_schema($rebuild = FALSE) { // If the schema is empty, avoid saving it: some database engines require // the schema to perform queries, and this could lead to infinite loops. if (!empty($schema) && (drupal_get_bootstrap_phase() == DRUPAL_BOOTSTRAP_FULL)) { - cache_set('schema', $schema); + cache()->set('schema', $schema); } if ($rebuild) { - cache_clear_all('schema:', 'cache', TRUE); + cache()->deletePrefix('schema:'); } } } @@ -2900,7 +2900,7 @@ function _registry_check_code($type, $name = NULL) { if (!isset($lookup_cache)) { $lookup_cache = array(); - if ($cache = cache_get('lookup_cache', 'cache_bootstrap')) { + if ($cache = cache('bootstrap')->get('lookup_cache')) { $lookup_cache = $cache->data; } } @@ -2917,7 +2917,7 @@ function _registry_check_code($type, $name = NULL) { // changes to the lookup cache for this request. if ($type == REGISTRY_WRITE_LOOKUP_CACHE) { if ($cache_update_needed) { - cache_set('lookup_cache', $lookup_cache, 'cache_bootstrap'); + cache('bootstrap')->set('lookup_cache', $lookup_cache); } return; } diff --git a/includes/cache-install.inc b/includes/cache-install.inc index d9bb0f92ea5b..8bcf8b7b1c17 100644 --- a/includes/cache-install.inc +++ b/includes/cache-install.inc @@ -26,6 +26,16 @@ class DrupalFakeCache extends DrupalDatabaseCache implements DrupalCacheInterfac function set($cid, $data, $expire = CACHE_PERMANENT) { } + function deletePrefix($cid) { + try { + if (class_exists('Database')) { + parent::deletePrefix($cid); + } + } + catch (Exception $e) { + } + } + function clear($cid = NULL, $wildcard = FALSE) { // If there is a database cache, attempt to clear it whenever possible. The // reason for doing this is that the database cache can accumulate data diff --git a/includes/cache.inc b/includes/cache.inc index 8666874ac625..9b60a7e08fbb 100644 --- a/includes/cache.inc +++ b/includes/cache.inc @@ -1,7 +1,7 @@ get($cid); + return cache($bin)->get($cid); } /** @@ -61,7 +66,7 @@ function cache_get($cid, $bin = 'cache') { * An array of the items successfully returned from cache indexed by cid. */ function cache_get_multiple(array &$cids, $bin = 'cache') { - return _cache_get_object($bin)->getMultiple($cids); + return cache($bin)->getMultiple($cids); } /** @@ -134,7 +139,7 @@ function cache_get_multiple(array &$cids, $bin = 'cache') { * the given time, after which it behaves like CACHE_TEMPORARY. */ function cache_set($cid, $data, $bin = 'cache', $expire = CACHE_PERMANENT) { - return _cache_get_object($bin)->set($cid, $data, $expire); + return cache($bin)->set($cid, $data, $expire); } /** @@ -161,12 +166,12 @@ function cache_clear_all($cid = NULL, $bin = NULL, $wildcard = FALSE) { // Clear the block cache first, so stale data will // not end up in the page cache. if (module_exists('block')) { - cache_clear_all(NULL, 'cache_block'); + cache('block')->expire(); } - cache_clear_all(NULL, 'cache_page'); + cache('page')->expire(); return; } - return _cache_get_object($bin)->clear($cid, $wildcard); + return cache($bin)->clear($cid, $wildcard); } /** @@ -181,7 +186,7 @@ function cache_clear_all($cid = NULL, $bin = NULL, $wildcard = FALSE) { * TRUE if the cache bin specified is empty. */ function cache_is_empty($bin) { - return _cache_get_object($bin)->isEmpty(); + return cache($bin)->isEmpty(); } /** @@ -218,7 +223,7 @@ function cache_is_empty($bin) { * cache_get($cid, 'custom_bin'); * @endcode * - * @see _cache_get_object() + * @see cache() * @see DrupalDatabaseCache */ interface DrupalCacheInterface { @@ -274,6 +279,44 @@ interface DrupalCacheInterface { */ function set($cid, $data, $expire = CACHE_PERMANENT); + /** + * Delete an item from the cache. + * + * @param $cid + * The cache ID to delete. + */ + function delete($cid); + + /** + * Delete multiple items from the cache. + * + * @param $cids + * An array of $cids to delete. + */ + function deleteMultiple(Array $cids); + + /** + * Delete items from the cache using a wildcard prefix. + * + * @param $prefix + * A wildcard prefix. + */ + function deletePrefix($prefix); + + /** + * Flush all cache items in a bin. + */ + function flush(); + + /** + * Expire temporary items from cache. + */ + function expire(); + + /** + * Perform garbage collection on a cache bin. + */ + function garbageCollection(); /** * Expire data from the cache. If called without arguments, expirable @@ -286,6 +329,9 @@ interface DrupalCacheInterface { * If set to TRUE, the $cid is treated as a substring * to match rather than a complete ID. The match is a right hand * match. If '*' is given as $cid, the bin $bin will be emptied. + * + * @todo: this method is deprecated, as it's functionality is covered by + * more targetted methods in the interface. */ function clear($cid = NULL, $wildcard = FALSE); @@ -311,6 +357,11 @@ class DrupalDatabaseCache implements DrupalCacheInterface { protected $bin; function __construct($bin) { + // All cache tables should be prefixed with 'cache_', apart from the + // default 'cache' bin, which would look silly. + if ($bin != 'cache') { + $bin = 'cache_' . $bin; + } $this->bin = $bin; } @@ -350,28 +401,6 @@ class DrupalDatabaseCache implements DrupalCacheInterface { } } - /** - * Garbage collection for get() and getMultiple(). - * - * @param $bin - * The bin being requested. - */ - protected function garbageCollection() { - global $user; - - // Garbage collection necessary when enforcing a minimum cache lifetime. - $cache_flush = variable_get('cache_flush_' . $this->bin, 0); - if ($cache_flush && ($cache_flush + variable_get('cache_lifetime', 0) <= REQUEST_TIME)) { - // Reset the variable immediately to prevent a meltdown in heavy load situations. - variable_set('cache_flush_' . $this->bin, 0); - // Time to flush old cache data - db_delete($this->bin) - ->condition('expire', CACHE_PERMANENT, '<>') - ->condition('expire', $cache_flush, '<=') - ->execute(); - } - } - /** * Prepare a cached item. * @@ -434,64 +463,101 @@ class DrupalDatabaseCache implements DrupalCacheInterface { } } - function clear($cid = NULL, $wildcard = FALSE) { - global $user; + function delete($cid) { + db_delete($this->bin) + ->condition('cid', $cid) + ->execute(); + } - if (empty($cid)) { - if (variable_get('cache_lifetime', 0)) { - // We store the time in the current user's $user->cache variable which - // will be saved into the sessions bin by _drupal_session_write(). We then - // simulate that the cache was flushed for this user by not returning - // cached data that was cached before the timestamp. - $user->cache = REQUEST_TIME; + function deleteMultiple(Array $cids) { + // Delete in chunks when a large array is passed. + do { + db_delete($this->bin) + ->condition('cid', array_splice($cids, 0, 1000), 'IN') + ->execute(); + } + while (count($cids)); + } - $cache_flush = variable_get('cache_flush_' . $this->bin, 0); - if ($cache_flush == 0) { - // This is the first request to clear the cache, start a timer. - variable_set('cache_flush_' . $this->bin, REQUEST_TIME); - } - elseif (REQUEST_TIME > ($cache_flush + variable_get('cache_lifetime', 0))) { - // Clear the cache for everyone, cache_lifetime seconds have - // passed since the first request to clear the cache. - db_delete($this->bin) - ->condition('expire', CACHE_PERMANENT, '<>') - ->condition('expire', REQUEST_TIME, '<') - ->execute(); - variable_set('cache_flush_' . $this->bin, 0); - } + function deletePrefix($prefix) { + db_delete($this->bin) + ->condition('cid', db_like($prefix) . '%', 'LIKE') + ->execute(); + } + + function flush() { + db_truncate($this->bin)->execute(); + } + + function expire() { + if (variable_get('cache_lifetime', 0)) { + // We store the time in the current user's $user->cache variable which + // will be saved into the sessions bin by _drupal_session_write(). We then + // simulate that the cache was flushed for this user by not returning + // cached data that was cached before the timestamp. + $GLOBALS['user']->cache = REQUEST_TIME; + + $cache_flush = variable_get('cache_flush_' . $this->bin, 0); + if ($cache_flush == 0) { + // This is the first request to clear the cache, start a timer. + variable_set('cache_flush_' . $this->bin, REQUEST_TIME); } - else { - // No minimum cache lifetime, flush all temporary cache entries now. + elseif (REQUEST_TIME > ($cache_flush + variable_get('cache_lifetime', 0))) { + // Clear the cache for everyone, cache_lifetime seconds have + // passed since the first request to clear the cache. db_delete($this->bin) ->condition('expire', CACHE_PERMANENT, '<>') ->condition('expire', REQUEST_TIME, '<') ->execute(); + variable_set('cache_flush_' . $this->bin, 0); } } + else { + // No minimum cache lifetime, flush all temporary cache entries now. + db_delete($this->bin) + ->condition('expire', CACHE_PERMANENT, '<>') + ->condition('expire', REQUEST_TIME, '<') + ->execute(); + } + } + + function garbageCollection() { + global $user; + + // When cache lifetime is in force, avoid running garbage collection too + // often since this will remove temporary cache items indiscriminately. + $cache_flush = variable_get('cache_flush_' . $this->bin, 0); + if ($cache_flush && ($cache_flush + variable_get('cache_lifetime', 0) <= REQUEST_TIME)) { + // Reset the variable immediately to prevent a meltdown in heavy load situations. + variable_set('cache_flush_' . $this->bin, 0); + // Time to flush old cache data + db_delete($this->bin) + ->condition('expire', CACHE_PERMANENT, '<>') + ->condition('expire', $cache_flush, '<=') + ->execute(); + } + } + + function clear($cid = NULL, $wildcard = FALSE) { + global $user; + + if (empty($cid)) { + $this->expire(); + } else { if ($wildcard) { if ($cid == '*') { - db_truncate($this->bin)->execute(); + $this->flush(); } else { - db_delete($this->bin) - ->condition('cid', db_like($cid) . '%', 'LIKE') - ->execute(); + $this->deletePrefix($cid); } } elseif (is_array($cid)) { - // Delete in chunks when a large array is passed. - do { - db_delete($this->bin) - ->condition('cid', array_splice($cid, 0, 1000), 'IN') - ->execute(); - } - while (count($cid)); + $this->deleteMultiple($cid); } else { - db_delete($this->bin) - ->condition('cid', $cid) - ->execute(); + $this->delete($cid); } } } diff --git a/modules/simpletest/drupal_web_test_case.php b/modules/simpletest/drupal_web_test_case.php index 5c39cfc11cd5..b2a7bf1d3433 100644 --- a/modules/simpletest/drupal_web_test_case.php +++ b/modules/simpletest/drupal_web_test_case.php @@ -1238,6 +1238,10 @@ class DrupalWebTestCase extends DrupalTestCase { ->condition('test_id', $this->testId) ->execute(); + // Reset all statics and variables to perform tests in a clean environment. + $conf = array(); + drupal_static_reset(); + // Clone the current connection and replace the current prefix. $connection_info = Database::getConnectionInfo('default'); Database::renameConnection('default', 'simpletest_original_default'); @@ -1285,10 +1289,6 @@ class DrupalWebTestCase extends DrupalTestCase { ini_set('log_errors', 1); ini_set('error_log', $public_files_directory . '/error.log'); - // Reset all statics and variables to perform tests in a clean environment. - $conf = array(); - drupal_static_reset(); - // Set the test information for use in other parts of Drupal. $test_info = &$GLOBALS['drupal_test_info']; $test_info['test_run_id'] = $this->databasePrefix; diff --git a/modules/simpletest/tests/cache.test b/modules/simpletest/tests/cache.test index 954f575961dc..1235269ccad4 100644 --- a/modules/simpletest/tests/cache.test +++ b/modules/simpletest/tests/cache.test @@ -1,7 +1,7 @@ default_bin; } - $cache = cache_get($cid, $bin); + $cached = cache($bin)->get($cid); - return isset($cache->data) && $cache->data == $var; + return isset($cached->data) && $cached->data == $var; } /** @@ -71,8 +71,8 @@ class CacheTestCase extends DrupalWebTestCase { $cid = $this->default_cid; } - $cache = cache_get($cid, $bin); - $this->assertFalse($cache, $message); + $cached = cache($bin)->get($cid); + $this->assertFalse($cached, $message); } /** @@ -85,7 +85,7 @@ class CacheTestCase extends DrupalWebTestCase { $bin = $this->default_bin; } - cache_clear_all(NULL, $bin); + cache($bin)->expire(); } /** @@ -146,23 +146,24 @@ class CacheSavingCase extends CacheTestCase { $test_object->test2 = 100; $test_object->test3 = array('drupal1', 'drupal2' => 'drupal3', 'drupal4' => array('drupal5', 'drupal6')); - cache_set('test_object', $test_object, 'cache'); - $cache = cache_get('test_object', 'cache'); - $this->assertTrue(isset($cache->data) && $cache->data == $test_object, t('Object is saved and restored properly.')); + + cache()->set('test_object', $test_object); + $cached = cache()->get('test_object'); + $this->assertTrue(isset($cached->data) && $cached->data == $test_object, t('Object is saved and restored properly.')); } /* * Check or a variable is stored and restored properly. **/ function checkVariable($var) { - cache_set('test_var', $var, 'cache'); - $cache = cache_get('test_var', 'cache'); - $this->assertTrue(isset($cache->data) && $cache->data === $var, t('@type is saved and restored properly.', array('@type' => ucfirst(gettype($var))))); + cache()->set('test_var', $var); + $cached = cache()->get('test_var'); + $this->assertTrue(isset($cached->data) && $cached->data === $var, t('@type is saved and restored properly.', array('@type' => ucfirst(gettype($var))))); } } /** - * Test cache_get_multiple(). + * Test getMultiple(). */ class CacheGetMultipleUnitTest extends CacheTestCase { @@ -175,33 +176,34 @@ class CacheGetMultipleUnitTest extends CacheTestCase { } function setUp() { - $this->default_bin = 'cache_page'; + $this->default_bin = 'page'; parent::setUp(); } /** - * Test cache_get_multiple(). + * Test getMultiple(). */ function testCacheMultiple() { $item1 = $this->randomName(10); $item2 = $this->randomName(10); - cache_set('item1', $item1, $this->default_bin); - cache_set('item2', $item2, $this->default_bin); + $cache = cache($this->default_bin); + $cache->set('item1', $item1); + $cache->set('item2', $item2); $this->assertTrue($this->checkCacheExists('item1', $item1), t('Item 1 is cached.')); $this->assertTrue($this->checkCacheExists('item2', $item2), t('Item 2 is cached.')); - // Fetch both records from the database with cache_get_multiple(). + // Fetch both records from the database with getMultiple(). $item_ids = array('item1', 'item2'); - $items = cache_get_multiple($item_ids, $this->default_bin); + $items = $cache->getMultiple($item_ids); $this->assertEqual($items['item1']->data, $item1, t('Item was returned from cache successfully.')); $this->assertEqual($items['item2']->data, $item2, t('Item was returned from cache successfully.')); // Remove one item from the cache. - cache_clear_all('item2', $this->default_bin); + $cache->delete('item2'); - // Confirm that only one item is returned by cache_get_multiple(). + // Confirm that only one item is returned by getMultiple(). $item_ids = array('item1', 'item2'); - $items = cache_get_multiple($item_ids, $this->default_bin); + $items = $cache->getMultiple($item_ids); $this->assertEqual($items['item1']->data, $item1, t('Item was returned from cache successfully.')); $this->assertFalse(isset($items['item2']), t('Item was not returned from the cache.')); $this->assertTrue(count($items) == 1, t('Only valid cache entries returned.')); @@ -221,7 +223,7 @@ class CacheClearCase extends CacheTestCase { } function setUp() { - $this->default_bin = 'cache_page'; + $this->default_bin = 'page'; $this->default_value = $this->randomName(10); parent::setUp(); @@ -231,44 +233,36 @@ class CacheClearCase extends CacheTestCase { * Test clearing using a cid. */ function testClearCid() { - cache_set('test_cid_clear', $this->default_value, $this->default_bin); + $cache = cache($this->default_bin); + $cache->set('test_cid_clear', $this->default_value); $this->assertCacheExists(t('Cache was set for clearing cid.'), $this->default_value, 'test_cid_clear'); - cache_clear_all('test_cid_clear', $this->default_bin); + $cache->delete('test_cid_clear'); $this->assertCacheRemoved(t('Cache was removed after clearing cid.'), 'test_cid_clear'); - - cache_set('test_cid_clear1', $this->default_value, $this->default_bin); - cache_set('test_cid_clear2', $this->default_value, $this->default_bin); - $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value) - && $this->checkCacheExists('test_cid_clear2', $this->default_value), - t('Two caches were created for checking cid "*" with wildcard false.')); - cache_clear_all('*', $this->default_bin); - $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value) - && $this->checkCacheExists('test_cid_clear2', $this->default_value), - t('Two caches still exists after clearing cid "*" with wildcard false.')); } /** * Test clearing using wildcard. */ function testClearWildcard() { - cache_set('test_cid_clear1', $this->default_value, $this->default_bin); - cache_set('test_cid_clear2', $this->default_value, $this->default_bin); + $cache = cache($this->default_bin); + $cache->set('test_cid_clear1', $this->default_value); + $cache->set('test_cid_clear2', $this->default_value); $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value) && $this->checkCacheExists('test_cid_clear2', $this->default_value), t('Two caches were created for checking cid "*" with wildcard true.')); - cache_clear_all('*', $this->default_bin, TRUE); + $cache->flush(); $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value) || $this->checkCacheExists('test_cid_clear2', $this->default_value), t('Two caches removed after clearing cid "*" with wildcard true.')); - cache_set('test_cid_clear1', $this->default_value, $this->default_bin); - cache_set('test_cid_clear2', $this->default_value, $this->default_bin); + $cache->set('test_cid_clear1', $this->default_value); + $cache->set('test_cid_clear2', $this->default_value); $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value) && $this->checkCacheExists('test_cid_clear2', $this->default_value), t('Two caches were created for checking cid substring with wildcard true.')); - cache_clear_all('test_', $this->default_bin, TRUE); + $cache->deletePrefix('test_'); $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value) || $this->checkCacheExists('test_cid_clear2', $this->default_value), t('Two caches removed after clearing cid substring with wildcard true.')); @@ -279,16 +273,17 @@ class CacheClearCase extends CacheTestCase { */ function testClearArray() { // Create three cache entries. - cache_set('test_cid_clear1', $this->default_value, $this->default_bin); - cache_set('test_cid_clear2', $this->default_value, $this->default_bin); - cache_set('test_cid_clear3', $this->default_value, $this->default_bin); + $cache = cache($this->default_bin); + $cache->set('test_cid_clear1', $this->default_value); + $cache->set('test_cid_clear2', $this->default_value); + $cache->set('test_cid_clear3', $this->default_value); $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value) && $this->checkCacheExists('test_cid_clear2', $this->default_value) && $this->checkCacheExists('test_cid_clear3', $this->default_value), t('Three cache entries were created.')); // Clear two entries using an array. - cache_clear_all(array('test_cid_clear1', 'test_cid_clear2'), $this->default_bin); + $cache->deleteMultiple(array('test_cid_clear1', 'test_cid_clear2')); $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value) || $this->checkCacheExists('test_cid_clear2', $this->default_value), t('Two cache entries removed after clearing with an array.')); @@ -299,12 +294,12 @@ class CacheClearCase extends CacheTestCase { // Set the cache clear threshold to 2 to confirm that the full bin is cleared // when the threshold is exceeded. variable_set('cache_clear_threshold', 2); - cache_set('test_cid_clear1', $this->default_value, $this->default_bin); - cache_set('test_cid_clear2', $this->default_value, $this->default_bin); + $cache->set('test_cid_clear1', $this->default_value); + $cache->set('test_cid_clear2', $this->default_value); $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value) && $this->checkCacheExists('test_cid_clear2', $this->default_value), t('Two cache entries were created.')); - cache_clear_all(array('test_cid_clear1', 'test_cid_clear2', 'test_cid_clear3'), $this->default_bin); + $cache->deleteMultiple(array('test_cid_clear1', 'test_cid_clear2', 'test_cid_clear3')); $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value) || $this->checkCacheExists('test_cid_clear2', $this->default_value) || $this->checkCacheExists('test_cid_clear3', $this->default_value), @@ -316,7 +311,7 @@ class CacheClearCase extends CacheTestCase { */ function testFlushAllCaches() { // Create cache entries for each flushed cache bin. - $bins = array('cache', 'cache_filter', 'cache_page', 'cache_boostrap', 'cache_path'); + $bins = array('cache', 'filter', 'page', 'bootstrap', 'path'); $bins = array_merge(module_invoke_all('flush_caches'), $bins); foreach ($bins as $id => $bin) { $id = 'test_cid_clear' . $id; @@ -334,7 +329,7 @@ class CacheClearCase extends CacheTestCase { } /** - * Test cache_is_empty() function. + * Test isEmpty() method. */ class CacheIsEmptyCase extends CacheTestCase { public static function getInfo() { @@ -346,7 +341,7 @@ class CacheIsEmptyCase extends CacheTestCase { } function setUp() { - $this->default_bin = 'cache_page'; + $this->default_bin = 'page'; $this->default_value = $this->randomName(10); parent::setUp(); @@ -357,15 +352,16 @@ class CacheIsEmptyCase extends CacheTestCase { */ function testIsEmpty() { // Clear the cache bin. - cache_clear_all('*', $this->default_bin); - $this->assertTrue(cache_is_empty($this->default_bin), t('The cache bin is empty')); + $cache = cache($this->default_bin); + $cache->flush(); + $this->assertTrue($cache->isEmpty(), t('The cache bin is empty')); // Add some data to the cache bin. - cache_set($this->default_cid, $this->default_value, $this->default_bin); + $cache->set($this->default_cid, $this->default_value); $this->assertCacheExists(t('Cache was set.'), $this->default_value, $this->default_cid); - $this->assertFalse(cache_is_empty($this->default_bin), t('The cache bin is not empty')); + $this->assertFalse($cache->isEmpty(), t('The cache bin is not empty')); // Remove the cached data. - cache_clear_all($this->default_cid, $this->default_bin); + $cache->delete($this->default_cid); $this->assertCacheRemoved(t('Cache was removed.'), $this->default_cid); - $this->assertTrue(cache_is_empty($this->default_bin), t('The cache bin is empty')); + $this->assertTrue($cache->isEmpty(), t('The cache bin is empty')); } } From 1a1da09dbafccd235f1c2e41287e96ea0c8364d5 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Wed, 7 Sep 2011 14:57:25 -0400 Subject: [PATCH 14/80] - Patch #1260528 by tarmstrong, svendecabooter, podarok: introduce API to delete languages. --- modules/field/field.multilingual.inc | 4 +-- modules/locale/locale.admin.inc | 33 +++++--------------- modules/locale/locale.api.php | 27 +++++++++-------- modules/locale/locale.module | 45 ++++++++++++++++++++++++++++ modules/node/node.module | 11 +++++++ 5 files changed, 79 insertions(+), 41 deletions(-) diff --git a/modules/field/field.multilingual.inc b/modules/field/field.multilingual.inc index 5373d9708284..e0cfec76c0f2 100644 --- a/modules/field/field.multilingual.inc +++ b/modules/field/field.multilingual.inc @@ -60,9 +60,9 @@ */ /** - * Implements hook_multilingual_settings_changed(). + * Implements hook_locale_language_delete(). */ -function field_multilingual_settings_changed() { +function field_locale_language_delete() { field_info_cache_clear(); } diff --git a/modules/locale/locale.admin.inc b/modules/locale/locale.admin.inc index 79ec3a1866da..f3c1ae5f1dc9 100644 --- a/modules/locale/locale.admin.inc +++ b/modules/locale/locale.admin.inc @@ -461,37 +461,18 @@ function locale_languages_delete_form($form, &$form_state, $langcode) { * Process language deletion submissions. */ function locale_languages_delete_form_submit($form, &$form_state) { + $langcode = $form_state['values']['langcode']; $languages = language_list(); - if (isset($languages[$form_state['values']['langcode']])) { - // Remove translations first. - db_delete('locales_target') - ->condition('language', $form_state['values']['langcode']) - ->execute(); - cache_clear_all('locale:' . $form_state['values']['langcode'], 'cache'); - // With no translations, this removes existing JavaScript translations file. - _locale_rebuild_js($form_state['values']['langcode']); - // Remove the language. - db_delete('languages') - ->condition('language', $form_state['values']['langcode']) - ->execute(); - db_update('node') - ->fields(array('language' => '')) - ->condition('language', $form_state['values']['langcode']) - ->execute(); - if ($languages[$form_state['values']['langcode']]->enabled) { - variable_set('language_count', variable_get('language_count', 1) - 1); - } - module_invoke_all('multilingual_settings_changed'); - $variables = array('%locale' => $languages[$form_state['values']['langcode']]->name); + $language = $languages[$langcode]; + + $success = locale_language_delete($langcode); + + if ($success) { + $variables = array('%locale' => $language->name); drupal_set_message(t('The language %locale has been removed.', $variables)); - watchdog('locale', 'The language %locale has been removed.', $variables); } - // Changing the language settings impacts the interface: - cache_clear_all('*', 'cache_page', TRUE); - $form_state['redirect'] = 'admin/config/regional/language'; - return; } /** diff --git a/modules/locale/locale.api.php b/modules/locale/locale.api.php index e327401a6e67..96f5b778b835 100644 --- a/modules/locale/locale.api.php +++ b/modules/locale/locale.api.php @@ -154,19 +154,6 @@ function hook_language_negotiation_info_alter(array &$language_providers) { } } -/** - * Allow modules to react to language settings changes. - * - * Every module needing to act when the number of enabled languages changes - * should implement this. This is an "internal" hook and should not be invoked - * elsewhere. The typical implementation would trigger some kind of rebuilding, - * this way system components could properly react to the change of the enabled - * languages number. - */ -function hook_multilingual_settings_changed() { - field_info_cache_clear(); -} - /** * Perform alterations on the language fallback candidates. * @@ -178,6 +165,20 @@ function hook_language_fallback_candidates_alter(array &$fallback_candidates) { $fallback_candidates = array_reverse($fallback_candidates); } +/** + * Allow modules to react before the deletion of a language. + * + * @param $language + * The language object of the language that is about to be deleted. + */ +function hook_locale_language_delete($language) { + // On nodes with this language, unset the language + db_update('node') + ->fields(array('language' => '')) + ->condition('language', $language->language) + ->execute(); +} + /** * @} End of "addtogroup hooks". */ diff --git a/modules/locale/locale.module b/modules/locale/locale.module index 0c238be47bc3..ba66999e146c 100644 --- a/modules/locale/locale.module +++ b/modules/locale/locale.module @@ -778,6 +778,51 @@ function locale_language_list($field = 'name', $all = FALSE) { return $list; } +/** + * Delete a language. + * + * @param $langcode + * Language code of the language to be deleted. + * @return + * TRUE if language is successfully deleted. Otherwise FALSE. + */ +function locale_language_delete($langcode) { + $languages = language_list(); + if (isset($languages[$langcode])) { + $language = $languages[$langcode]; + + module_invoke_all('locale_language_delete', $language); + + // Remove translations first. + db_delete('locales_target') + ->condition('language', $language->language) + ->execute(); + + // Remove the language. + db_delete('languages') + ->condition('language', $language->language) + ->execute(); + + if ($language->enabled) { + variable_set('language_count', variable_get('language_count', 1) - 1); + } + + drupal_static_reset('language_list'); + _locale_invalidate_js($language->language); + + // Changing the language settings impacts the interface: + cache_clear_all('*', 'cache_page', TRUE); + + // Clearing all locale cache from database + cache_clear_all('locale:' . $language->language, 'cache'); + + $variables = array('%locale' => $language->name); + watchdog('locale', 'The language %locale has been removed.', $variables); + return TRUE; + } + return FALSE; +} + /** * Implements hook_modules_installed(). */ diff --git a/modules/node/node.module b/modules/node/node.module index 20815df85d3f..13b8e4d60b57 100644 --- a/modules/node/node.module +++ b/modules/node/node.module @@ -3948,3 +3948,14 @@ function node_file_download_access($field, $entity_type, $entity) { return node_access('view', $entity); } } + +/** + * Implements hook_locale_language_delete(). + */ +function node_locale_language_delete($language) { + // On nodes with this language, unset the language + db_update('node') + ->fields(array('language' => '')) + ->condition('language', $language->language) + ->execute(); +} From d06e11ce6a9ed9982969670e1c1f70c785fd5205 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Wed, 7 Sep 2011 16:56:17 -0400 Subject: [PATCH 15/80] - Patch #1250800 by attiks, dereine: fixed language domain should work regardless of ports or protocols. --- includes/locale.inc | 14 ++++++++++---- modules/locale/locale.admin.inc | 2 +- modules/locale/locale.test | 4 ++-- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/includes/locale.inc b/includes/locale.inc index f667d30b9c81..b7d9672dfc66 100644 --- a/includes/locale.inc +++ b/includes/locale.inc @@ -207,10 +207,16 @@ function locale_language_from_url($languages) { case LOCALE_LANGUAGE_NEGOTIATION_URL_DOMAIN: foreach ($languages as $language) { - $host = parse_url($language->domain, PHP_URL_HOST); - if ($host && ($_SERVER['HTTP_HOST'] == $host)) { - $language_url = $language->language; - break; + // Skip check if the language doesn't have a domain. + if ($language->domain) { + // Only compare the domains not the protocols or ports. + // Remove protocol and add http:// so parse_url works + $host = 'http://' . str_replace(array('http://', 'https://'), '', $language->domain); + $host = parse_url($host, PHP_URL_HOST); + if ($_SERVER['HTTP_HOST'] == $host) { + $language_url = $language->language; + break; + } } } break; diff --git a/modules/locale/locale.admin.inc b/modules/locale/locale.admin.inc index f3c1ae5f1dc9..d9044c6ee9cc 100644 --- a/modules/locale/locale.admin.inc +++ b/modules/locale/locale.admin.inc @@ -307,7 +307,7 @@ function _locale_languages_common_controls(&$form, $language = NULL) { '#title' => t('Language domain'), '#maxlength' => 128, '#default_value' => @$language->domain, - '#description' => t('URL including protocol to use for this language, if your Detection and selection settings use URL domains. For the default language, this value may be left blank. Modifying this value may break existing URLs. Use with caution in a production environment. Example: Specifying "http://example.de" or "http://de.example.com" as language domains for German results in URLs like "http://example.de/contact" and "http://de.example.com/contact", respectively.'), + '#description' => t('The domain name to use for this language if URL domains are used for Detection and selection. Leave blank for the default language. Changing this value may break existing URLs. Example: Specifying "de.example.com" as language domain for German will result in an URL like "http://de.example.com/contact".'), ); $form['direction'] = array('#type' => 'radios', '#title' => t('Direction'), diff --git a/modules/locale/locale.test b/modules/locale/locale.test index 6dad7e08bec4..425f68cfa79a 100644 --- a/modules/locale/locale.test +++ b/modules/locale/locale.test @@ -1900,8 +1900,8 @@ class LocaleUILanguageNegotiationTest extends DrupalWebTestCase { $this->assertResponse(404, "Unknown language path prefix should return 404"); // Setup for domain negotiation, first configure the language to have domain - // URL. - $edit = array('prefix' => '', 'domain' => "http://$language_domain"); + // URL. We use https and a port to make sure that only the domain name is used. + $edit = array('prefix' => '', 'domain' => "https://$language_domain:99"); $this->drupalPost("admin/config/regional/language/edit/$language", $edit, t('Save language')); // Set the site to use domain language negotiation. From dc29547ab34e78b1e346af723b2b78401d652ca7 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Wed, 7 Sep 2011 22:00:12 -0400 Subject: [PATCH 16/80] =?UTF-8?q?-=20Patch=20#1265700=20by=20G=C3=A1bor=20?= =?UTF-8?q?Hojtsy:=20Little=20modernization=20of=20language=5Fdefault().?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- includes/bootstrap.inc | 26 ++++++++++++++++++++------ includes/locale.inc | 2 +- modules/field/field.multilingual.inc | 2 +- modules/locale/locale.admin.inc | 6 +++--- modules/path/path.test | 2 +- 5 files changed, 26 insertions(+), 12 deletions(-) diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index 2d6366739ee9..285652960394 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -2559,14 +2559,28 @@ function language_list($field = 'language') { } /** - * Default language used on the site + * Default language used on the site. * - * @param $property - * Optional property of the language object to return + * @return + * A language object. */ -function language_default($property = NULL) { - $language = variable_get('language_default', (object) array('language' => 'en', 'name' => 'English', 'native' => 'English', 'direction' => 0, 'enabled' => 1, 'plurals' => 0, 'formula' => '', 'domain' => '', 'prefix' => '', 'weight' => 0, 'javascript' => '')); - return $property ? $language->$property : $language; +function language_default() { + return variable_get( + 'language_default', + (object) array( + 'language' => 'en', + 'name' => 'English', + 'native' => 'English', + 'direction' => 0, + 'enabled' => 1, + 'plurals' => 0, + 'formula' => '', + 'domain' => '', + 'prefix' => '', + 'weight' => 0, + 'javascript' => '' + ) + ); } /** diff --git a/includes/locale.inc b/includes/locale.inc index b7d9672dfc66..fabde9025af8 100644 --- a/includes/locale.inc +++ b/includes/locale.inc @@ -709,7 +709,7 @@ function _locale_rebuild_js($langcode = NULL) { // Update the default language variable if the default language has been altered. // This is necessary to keep the variable consistent with the database // version of the language and to prevent checking against an outdated hash. - $default_langcode = language_default('language'); + $default_langcode = language_default()->language; if ($default_langcode == $language->language) { $default = db_query("SELECT * FROM {languages} WHERE language = :language", array(':language' => $default_langcode))->fetchObject(); variable_set('language_default', $default); diff --git a/modules/field/field.multilingual.inc b/modules/field/field.multilingual.inc index e0cfec76c0f2..d9227455cca5 100644 --- a/modules/field/field.multilingual.inc +++ b/modules/field/field.multilingual.inc @@ -228,7 +228,7 @@ function field_valid_language($langcode, $default = TRUE) { return $langcode; } global $language_content; - return $default ? language_default('language') : $language_content->language; + return $default ? language_default()->language : $language_content->language; } /** diff --git a/modules/locale/locale.admin.inc b/modules/locale/locale.admin.inc index d9044c6ee9cc..3e8f707004f2 100644 --- a/modules/locale/locale.admin.inc +++ b/modules/locale/locale.admin.inc @@ -52,7 +52,7 @@ function locale_languages_overview_form() { '#title' => t('Default language'), '#title_display' => 'invisible', '#options' => $options, - '#default_value' => language_default('language'), + '#default_value' => language_default()->language, ); $form['actions'] = array('#type' => 'actions'); $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save configuration')); @@ -392,7 +392,7 @@ function locale_languages_edit_form_validate($form, &$form_state) { if (!empty($form_state['values']['domain']) && $duplicate = db_query("SELECT language FROM {languages} WHERE domain = :domain AND language <> :language", array(':domain' => $form_state['values']['domain'], ':language' => $form_state['values']['langcode']))->fetchField()) { form_set_error('domain', t('The domain (%domain) is already tied to a language (%language).', array('%domain' => $form_state['values']['domain'], '%language' => $duplicate->language))); } - if (empty($form_state['values']['prefix']) && language_default('language') != $form_state['values']['langcode'] && empty($form_state['values']['domain'])) { + if (empty($form_state['values']['prefix']) && language_default()->language != $form_state['values']['langcode'] && empty($form_state['values']['domain'])) { form_set_error('prefix', t('Only the default language can have both the domain and prefix empty.')); } if (!empty($form_state['values']['prefix']) && $duplicate = db_query("SELECT language FROM {languages} WHERE prefix = :prefix AND language <> :language", array(':prefix' => $form_state['values']['prefix'], ':language' => $form_state['values']['langcode']))->fetchField()) { @@ -439,7 +439,7 @@ function locale_languages_delete_form($form, &$form_state, $langcode) { drupal_goto('admin/config/regional/language'); } - if (language_default('language') == $langcode) { + if (language_default()->language == $langcode) { drupal_set_message(t('The default language cannot be deleted.')); drupal_goto('admin/config/regional/language'); } diff --git a/modules/path/path.test b/modules/path/path.test index f42ec81be08c..9e501782b4cd 100644 --- a/modules/path/path.test +++ b/modules/path/path.test @@ -477,7 +477,7 @@ class PathMonolingualTestCase extends DrupalWebTestCase { // Verify that French is the only language. $this->assertFalse(drupal_multilingual(), t('Site is mono-lingual')); - $this->assertEqual(language_default('language'), 'fr', t('French is the default language')); + $this->assertEqual(language_default()->language, 'fr', t('French is the default language')); // Set language detection to URL. $edit = array('language[enabled][locale-url]' => TRUE); From 577520743c351d7b83fad00e0aacf48623fd84da Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Wed, 7 Sep 2011 22:03:31 -0400 Subject: [PATCH 17/80] - Patch #848546 by mlncn: throbber versus progress bar help text incorrect. --- modules/file/file.field.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/file/file.field.inc b/modules/file/file.field.inc index 2af3cb620be7..c73430c64c05 100644 --- a/modules/file/file.field.inc +++ b/modules/file/file.field.inc @@ -428,7 +428,7 @@ function file_field_widget_settings_form($field, $instance) { 'bar' => t('Bar with progress meter'), ), '#default_value' => $settings['progress_indicator'], - '#description' => t('The throbber display does not show the status of uploads but takes up space. The progress bar is helpful for monitoring progress on large uploads.'), + '#description' => t('The throbber display does not show the status of uploads but takes up less space. The progress bar is helpful for monitoring progress on large uploads.'), '#weight' => 16, '#access' => file_progress_implementation(), ); From 9864e17bc748025dfa721fbdf33a2cac7052b948 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Thu, 8 Sep 2011 09:33:22 -0400 Subject: [PATCH 18/80] - Patch #1244116 by brianV, catch, xjm, CrashTest_ | jenpasch: Fixed Feed items with title longer than 255 characters fail to insert into aggregator_item(). --- modules/aggregator/aggregator.install | 5 ++--- modules/aggregator/tests/aggregator_test_rss091.xml | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/modules/aggregator/aggregator.install b/modules/aggregator/aggregator.install index eecd14fb27f1..a6d576a7536c 100644 --- a/modules/aggregator/aggregator.install +++ b/modules/aggregator/aggregator.install @@ -226,10 +226,9 @@ function aggregator_schema() { 'description' => 'The {aggregator_feed}.fid to which this item belongs.', ), 'title' => array( - 'type' => 'varchar', - 'length' => 255, + 'type' => 'text', + 'size' => 'normal', 'not null' => TRUE, - 'default' => '', 'description' => 'Title of the feed item.', ), 'link' => array( diff --git a/modules/aggregator/tests/aggregator_test_rss091.xml b/modules/aggregator/tests/aggregator_test_rss091.xml index 1fd5320d3e98..f39a2732c034 100644 --- a/modules/aggregator/tests/aggregator_test_rss091.xml +++ b/modules/aggregator/tests/aggregator_test_rss091.xml @@ -22,7 +22,7 @@ First example feed item description. - Second example feed item title + Second example feed item title. This title is extremely long so that it exceeds the 255 character limit for titles in feed item storage. In fact it's so long that this sentence isn't long enough so I'm rambling a bit to make it longer, nearly there now. Ah now it's long enough so I'll shut up. http://example.com/example-turns-two Second example feed item description. From da131e2993d40bae321a250d316256ea61daa1a4 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Thu, 8 Sep 2011 14:29:58 -0400 Subject: [PATCH 19/80] =?UTF-8?q?-=20Patch=20#1231402=20by=20G=C3=A1bor=20?= =?UTF-8?q?Hojtsy:=20Drupal=20does=20not=20use=20ISO=20language=20codes,?= =?UTF-8?q?=20iso.inc=20is=20misleading.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- includes/install.core.inc | 8 +- includes/iso.inc | 482 -------------------------------- includes/locale.inc | 12 +- modules/locale/locale.admin.inc | 10 +- modules/locale/locale.bulk.inc | 4 +- modules/system/system.api.php | 2 +- 6 files changed, 18 insertions(+), 500 deletions(-) delete mode 100644 includes/iso.inc diff --git a/includes/install.core.inc b/includes/install.core.inc index 9364bcc039bd..1040bf3e550d 100644 --- a/includes/install.core.inc +++ b/includes/install.core.inc @@ -1234,8 +1234,8 @@ function install_select_locale(&$install_state) { * Form API array definition for language selection. */ function install_select_locale_form($form, &$form_state, $locales, $profilename) { - include_once DRUPAL_ROOT . '/includes/iso.inc'; - $languages = _locale_get_predefined_list(); + include_once DRUPAL_ROOT . '/includes/standard.inc'; + $languages = standard_language_list(); foreach ($locales as $locale) { $name = $locale->langcode; if (isset($languages[$name])) { @@ -1376,8 +1376,8 @@ function install_import_locales(&$install_state) { include_once drupal_get_path('module', 'locale') . '/locale.bulk.inc'; $install_locale = $install_state['parameters']['locale']; - include_once DRUPAL_ROOT . '/includes/iso.inc'; - $predefined = _locale_get_predefined_list(); + include_once DRUPAL_ROOT . '/includes/standard.inc'; + $predefined = standard_language_list(); if (!isset($predefined[$install_locale])) { // Drupal does not know about this language, so we prefill its values with // our best guess. The user will be able to edit afterwards. diff --git a/includes/iso.inc b/includes/iso.inc deleted file mode 100644 index dabbefdd5b6f..000000000000 --- a/includes/iso.inc +++ /dev/null @@ -1,482 +0,0 @@ - country name pairs. - * - * Get an array of all country code => country name pairs as laid out - * in ISO 3166-1 alpha-2. - * Grabbed from location project (http://drupal.org/project/location). - * @return - * An array of all country code => country name pairs. - */ -function _country_get_predefined_list() { - static $countries; - - if (isset($countries)) { - return $countries; - } - $t = get_t(); - - $countries = array( - 'AD' => $t('Andorra'), - 'AE' => $t('United Arab Emirates'), - 'AF' => $t('Afghanistan'), - 'AG' => $t('Antigua and Barbuda'), - 'AI' => $t('Anguilla'), - 'AL' => $t('Albania'), - 'AM' => $t('Armenia'), - 'AN' => $t('Netherlands Antilles'), - 'AO' => $t('Angola'), - 'AQ' => $t('Antarctica'), - 'AR' => $t('Argentina'), - 'AS' => $t('American Samoa'), - 'AT' => $t('Austria'), - 'AU' => $t('Australia'), - 'AW' => $t('Aruba'), - 'AX' => $t('Aland Islands'), - 'AZ' => $t('Azerbaijan'), - 'BA' => $t('Bosnia and Herzegovina'), - 'BB' => $t('Barbados'), - 'BD' => $t('Bangladesh'), - 'BE' => $t('Belgium'), - 'BF' => $t('Burkina Faso'), - 'BG' => $t('Bulgaria'), - 'BH' => $t('Bahrain'), - 'BI' => $t('Burundi'), - 'BJ' => $t('Benin'), - 'BL' => $t('Saint Barthélemy'), - 'BM' => $t('Bermuda'), - 'BN' => $t('Brunei'), - 'BO' => $t('Bolivia'), - 'BR' => $t('Brazil'), - 'BS' => $t('Bahamas'), - 'BT' => $t('Bhutan'), - 'BV' => $t('Bouvet Island'), - 'BW' => $t('Botswana'), - 'BY' => $t('Belarus'), - 'BZ' => $t('Belize'), - 'CA' => $t('Canada'), - 'CC' => $t('Cocos (Keeling) Islands'), - 'CD' => $t('Congo (Kinshasa)'), - 'CF' => $t('Central African Republic'), - 'CG' => $t('Congo (Brazzaville)'), - 'CH' => $t('Switzerland'), - 'CI' => $t('Ivory Coast'), - 'CK' => $t('Cook Islands'), - 'CL' => $t('Chile'), - 'CM' => $t('Cameroon'), - 'CN' => $t('China'), - 'CO' => $t('Colombia'), - 'CR' => $t('Costa Rica'), - 'CU' => $t('Cuba'), - 'CV' => $t('Cape Verde'), - 'CX' => $t('Christmas Island'), - 'CY' => $t('Cyprus'), - 'CZ' => $t('Czech Republic'), - 'DE' => $t('Germany'), - 'DJ' => $t('Djibouti'), - 'DK' => $t('Denmark'), - 'DM' => $t('Dominica'), - 'DO' => $t('Dominican Republic'), - 'DZ' => $t('Algeria'), - 'EC' => $t('Ecuador'), - 'EE' => $t('Estonia'), - 'EG' => $t('Egypt'), - 'EH' => $t('Western Sahara'), - 'ER' => $t('Eritrea'), - 'ES' => $t('Spain'), - 'ET' => $t('Ethiopia'), - 'FI' => $t('Finland'), - 'FJ' => $t('Fiji'), - 'FK' => $t('Falkland Islands'), - 'FM' => $t('Micronesia'), - 'FO' => $t('Faroe Islands'), - 'FR' => $t('France'), - 'GA' => $t('Gabon'), - 'GB' => $t('United Kingdom'), - 'GD' => $t('Grenada'), - 'GE' => $t('Georgia'), - 'GF' => $t('French Guiana'), - 'GG' => $t('Guernsey'), - 'GH' => $t('Ghana'), - 'GI' => $t('Gibraltar'), - 'GL' => $t('Greenland'), - 'GM' => $t('Gambia'), - 'GN' => $t('Guinea'), - 'GP' => $t('Guadeloupe'), - 'GQ' => $t('Equatorial Guinea'), - 'GR' => $t('Greece'), - 'GS' => $t('South Georgia and the South Sandwich Islands'), - 'GT' => $t('Guatemala'), - 'GU' => $t('Guam'), - 'GW' => $t('Guinea-Bissau'), - 'GY' => $t('Guyana'), - 'HK' => $t('Hong Kong S.A.R., China'), - 'HM' => $t('Heard Island and McDonald Islands'), - 'HN' => $t('Honduras'), - 'HR' => $t('Croatia'), - 'HT' => $t('Haiti'), - 'HU' => $t('Hungary'), - 'ID' => $t('Indonesia'), - 'IE' => $t('Ireland'), - 'IL' => $t('Israel'), - 'IM' => $t('Isle of Man'), - 'IN' => $t('India'), - 'IO' => $t('British Indian Ocean Territory'), - 'IQ' => $t('Iraq'), - 'IR' => $t('Iran'), - 'IS' => $t('Iceland'), - 'IT' => $t('Italy'), - 'JE' => $t('Jersey'), - 'JM' => $t('Jamaica'), - 'JO' => $t('Jordan'), - 'JP' => $t('Japan'), - 'KE' => $t('Kenya'), - 'KG' => $t('Kyrgyzstan'), - 'KH' => $t('Cambodia'), - 'KI' => $t('Kiribati'), - 'KM' => $t('Comoros'), - 'KN' => $t('Saint Kitts and Nevis'), - 'KP' => $t('North Korea'), - 'KR' => $t('South Korea'), - 'KW' => $t('Kuwait'), - 'KY' => $t('Cayman Islands'), - 'KZ' => $t('Kazakhstan'), - 'LA' => $t('Laos'), - 'LB' => $t('Lebanon'), - 'LC' => $t('Saint Lucia'), - 'LI' => $t('Liechtenstein'), - 'LK' => $t('Sri Lanka'), - 'LR' => $t('Liberia'), - 'LS' => $t('Lesotho'), - 'LT' => $t('Lithuania'), - 'LU' => $t('Luxembourg'), - 'LV' => $t('Latvia'), - 'LY' => $t('Libya'), - 'MA' => $t('Morocco'), - 'MC' => $t('Monaco'), - 'MD' => $t('Moldova'), - 'ME' => $t('Montenegro'), - 'MF' => $t('Saint Martin (French part)'), - 'MG' => $t('Madagascar'), - 'MH' => $t('Marshall Islands'), - 'MK' => $t('Macedonia'), - 'ML' => $t('Mali'), - 'MM' => $t('Myanmar'), - 'MN' => $t('Mongolia'), - 'MO' => $t('Macao S.A.R., China'), - 'MP' => $t('Northern Mariana Islands'), - 'MQ' => $t('Martinique'), - 'MR' => $t('Mauritania'), - 'MS' => $t('Montserrat'), - 'MT' => $t('Malta'), - 'MU' => $t('Mauritius'), - 'MV' => $t('Maldives'), - 'MW' => $t('Malawi'), - 'MX' => $t('Mexico'), - 'MY' => $t('Malaysia'), - 'MZ' => $t('Mozambique'), - 'NA' => $t('Namibia'), - 'NC' => $t('New Caledonia'), - 'NE' => $t('Niger'), - 'NF' => $t('Norfolk Island'), - 'NG' => $t('Nigeria'), - 'NI' => $t('Nicaragua'), - 'NL' => $t('Netherlands'), - 'NO' => $t('Norway'), - 'NP' => $t('Nepal'), - 'NR' => $t('Nauru'), - 'NU' => $t('Niue'), - 'NZ' => $t('New Zealand'), - 'OM' => $t('Oman'), - 'PA' => $t('Panama'), - 'PE' => $t('Peru'), - 'PF' => $t('French Polynesia'), - 'PG' => $t('Papua New Guinea'), - 'PH' => $t('Philippines'), - 'PK' => $t('Pakistan'), - 'PL' => $t('Poland'), - 'PM' => $t('Saint Pierre and Miquelon'), - 'PN' => $t('Pitcairn'), - 'PR' => $t('Puerto Rico'), - 'PS' => $t('Palestinian Territory'), - 'PT' => $t('Portugal'), - 'PW' => $t('Palau'), - 'PY' => $t('Paraguay'), - 'QA' => $t('Qatar'), - 'RE' => $t('Reunion'), - 'RO' => $t('Romania'), - 'RS' => $t('Serbia'), - 'RU' => $t('Russia'), - 'RW' => $t('Rwanda'), - 'SA' => $t('Saudi Arabia'), - 'SB' => $t('Solomon Islands'), - 'SC' => $t('Seychelles'), - 'SD' => $t('Sudan'), - 'SE' => $t('Sweden'), - 'SG' => $t('Singapore'), - 'SH' => $t('Saint Helena'), - 'SI' => $t('Slovenia'), - 'SJ' => $t('Svalbard and Jan Mayen'), - 'SK' => $t('Slovakia'), - 'SL' => $t('Sierra Leone'), - 'SM' => $t('San Marino'), - 'SN' => $t('Senegal'), - 'SO' => $t('Somalia'), - 'SR' => $t('Suriname'), - 'ST' => $t('Sao Tome and Principe'), - 'SV' => $t('El Salvador'), - 'SY' => $t('Syria'), - 'SZ' => $t('Swaziland'), - 'TC' => $t('Turks and Caicos Islands'), - 'TD' => $t('Chad'), - 'TF' => $t('French Southern Territories'), - 'TG' => $t('Togo'), - 'TH' => $t('Thailand'), - 'TJ' => $t('Tajikistan'), - 'TK' => $t('Tokelau'), - 'TL' => $t('Timor-Leste'), - 'TM' => $t('Turkmenistan'), - 'TN' => $t('Tunisia'), - 'TO' => $t('Tonga'), - 'TR' => $t('Turkey'), - 'TT' => $t('Trinidad and Tobago'), - 'TV' => $t('Tuvalu'), - 'TW' => $t('Taiwan'), - 'TZ' => $t('Tanzania'), - 'UA' => $t('Ukraine'), - 'UG' => $t('Uganda'), - 'UM' => $t('United States Minor Outlying Islands'), - 'US' => $t('United States'), - 'UY' => $t('Uruguay'), - 'UZ' => $t('Uzbekistan'), - 'VA' => $t('Vatican'), - 'VC' => $t('Saint Vincent and the Grenadines'), - 'VE' => $t('Venezuela'), - 'VG' => $t('British Virgin Islands'), - 'VI' => $t('U.S. Virgin Islands'), - 'VN' => $t('Vietnam'), - 'VU' => $t('Vanuatu'), - 'WF' => $t('Wallis and Futuna'), - 'WS' => $t('Samoa'), - 'YE' => $t('Yemen'), - 'YT' => $t('Mayotte'), - 'ZA' => $t('South Africa'), - 'ZM' => $t('Zambia'), - 'ZW' => $t('Zimbabwe'), - ); - - // Sort the list. - natcasesort($countries); - - return $countries; -} - -/** - * @ingroup locale-api-predefined List of predefined languages - * @{ - */ - -/** - * Some of the common languages with their English and native names - * - * Based on ISO 639 and http://people.w3.org/rishida/names/languages.html - */ -function _locale_get_predefined_list() { - return array( - 'aa' => array('Afar'), - 'ab' => array('Abkhazian', 'аҧсуа бызшәа'), - 'ae' => array('Avestan'), - 'af' => array('Afrikaans'), - 'ak' => array('Akan'), - 'am' => array('Amharic', 'አማርኛ'), - 'ar' => array('Arabic', /* Left-to-right marker "‭" */ 'العربية', LANGUAGE_RTL), - 'as' => array('Assamese'), - 'ast' => array('Asturian'), - 'av' => array('Avar'), - 'ay' => array('Aymara'), - 'az' => array('Azerbaijani', 'azərbaycan'), - 'ba' => array('Bashkir'), - 'be' => array('Belarusian', 'Беларуская'), - 'bg' => array('Bulgarian', 'Български'), - 'bh' => array('Bihari'), - 'bi' => array('Bislama'), - 'bm' => array('Bambara', 'Bamanankan'), - 'bn' => array('Bengali'), - 'bo' => array('Tibetan'), - 'br' => array('Breton'), - 'bs' => array('Bosnian', 'Bosanski'), - 'ca' => array('Catalan', 'Català'), - 'ce' => array('Chechen'), - 'ch' => array('Chamorro'), - 'co' => array('Corsican'), - 'cr' => array('Cree'), - 'cs' => array('Czech', 'Čeština'), - 'cu' => array('Old Slavonic'), - 'cv' => array('Chuvash'), - 'cy' => array('Welsh', 'Cymraeg'), - 'da' => array('Danish', 'Dansk'), - 'de' => array('German', 'Deutsch'), - 'dv' => array('Maldivian'), - 'dz' => array('Bhutani'), - 'ee' => array('Ewe', 'Ɛʋɛ'), - 'el' => array('Greek', 'Ελληνικά'), - 'en' => array('English'), - 'en-gb' => array('English, British'), - 'eo' => array('Esperanto'), - 'es' => array('Spanish', 'Español'), - 'et' => array('Estonian', 'Eesti'), - 'eu' => array('Basque', 'Euskera'), - 'fa' => array('Persian', /* Left-to-right marker "‭" */ 'فارسی', LANGUAGE_RTL), - 'ff' => array('Fulah', 'Fulfulde'), - 'fi' => array('Finnish', 'Suomi'), - 'fil' => array('Filipino'), - 'fj' => array('Fiji'), - 'fo' => array('Faeroese'), - 'fr' => array('French', 'Français'), - 'fy' => array('Frisian', 'Frysk'), - 'ga' => array('Irish', 'Gaeilge'), - 'gd' => array('Scots Gaelic'), - 'gl' => array('Galician', 'Galego'), - 'gn' => array('Guarani'), - 'gsw-berne' => array('Swiss German'), - 'gu' => array('Gujarati'), - 'gv' => array('Manx'), - 'ha' => array('Hausa'), - 'he' => array('Hebrew', /* Left-to-right marker "‭" */ 'עברית', LANGUAGE_RTL), - 'hi' => array('Hindi', 'हिन्दी'), - 'ho' => array('Hiri Motu'), - 'hr' => array('Croatian', 'Hrvatski'), - 'ht' => array('Haitian Creole'), - 'hu' => array('Hungarian', 'Magyar'), - 'hy' => array('Armenian', 'Հայերեն'), - 'hz' => array('Herero'), - 'ia' => array('Interlingua'), - 'id' => array('Indonesian', 'Bahasa Indonesia'), - 'ie' => array('Interlingue'), - 'ig' => array('Igbo'), - 'ik' => array('Inupiak'), - 'is' => array('Icelandic', 'Íslenska'), - 'it' => array('Italian', 'Italiano'), - 'iu' => array('Inuktitut'), - 'ja' => array('Japanese', '日本語'), - 'jv' => array('Javanese'), - 'ka' => array('Georgian'), - 'kg' => array('Kongo'), - 'ki' => array('Kikuyu'), - 'kj' => array('Kwanyama'), - 'kk' => array('Kazakh', 'Қазақ'), - 'kl' => array('Greenlandic'), - 'km' => array('Cambodian'), - 'kn' => array('Kannada', 'ಕನ್ನಡ'), - 'ko' => array('Korean', '한국어'), - 'kr' => array('Kanuri'), - 'ks' => array('Kashmiri'), - 'ku' => array('Kurdish', 'Kurdî'), - 'kv' => array('Komi'), - 'kw' => array('Cornish'), - 'ky' => array('Kyrgyz', 'Кыргызча'), - 'la' => array('Latin', 'Latina'), - 'lb' => array('Luxembourgish'), - 'lg' => array('Luganda'), - 'ln' => array('Lingala'), - 'lo' => array('Laothian'), - 'lt' => array('Lithuanian', 'Lietuvių'), - 'lv' => array('Latvian', 'Latviešu'), - 'mg' => array('Malagasy'), - 'mh' => array('Marshallese'), - 'mi' => array('Māori'), - 'mk' => array('Macedonian', 'Македонски'), - 'ml' => array('Malayalam', 'മലയാളം'), - 'mn' => array('Mongolian'), - 'mo' => array('Moldavian'), - 'mr' => array('Marathi'), - 'ms' => array('Malay', 'Bahasa Melayu'), - 'mt' => array('Maltese', 'Malti'), - 'my' => array('Burmese'), - 'na' => array('Nauru'), - 'nd' => array('North Ndebele'), - 'ne' => array('Nepali'), - 'ng' => array('Ndonga'), - 'nl' => array('Dutch', 'Nederlands'), - 'nb' => array('Norwegian Bokmål', 'Bokmål'), - 'nn' => array('Norwegian Nynorsk', 'Nynorsk'), - 'nr' => array('South Ndebele'), - 'nv' => array('Navajo'), - 'ny' => array('Chichewa'), - 'oc' => array('Occitan'), - 'om' => array('Oromo'), - 'or' => array('Oriya'), - 'os' => array('Ossetian'), - 'pa' => array('Punjabi'), - 'pi' => array('Pali'), - 'pl' => array('Polish', 'Polski'), - 'ps' => array('Pashto', /* Left-to-right marker "‭" */ 'پښتو', LANGUAGE_RTL), - 'pt' => array('Portuguese, International'), - 'pt-pt' => array('Portuguese, Portugal', 'Português'), - 'pt-br' => array('Portuguese, Brazil', 'Português'), - 'qu' => array('Quechua'), - 'rm' => array('Rhaeto-Romance'), - 'rn' => array('Kirundi'), - 'ro' => array('Romanian', 'Română'), - 'ru' => array('Russian', 'Русский'), - 'rw' => array('Kinyarwanda'), - 'sa' => array('Sanskrit'), - 'sc' => array('Sardinian'), - 'sco' => array('Scots'), - 'sd' => array('Sindhi'), - 'se' => array('Northern Sami'), - 'sg' => array('Sango'), - 'sh' => array('Serbo-Croatian'), - 'si' => array('Sinhala', 'සිංහල'), - 'sk' => array('Slovak', 'Slovenčina'), - 'sl' => array('Slovenian', 'Slovenščina'), - 'sm' => array('Samoan'), - 'sn' => array('Shona'), - 'so' => array('Somali'), - 'sq' => array('Albanian', 'Shqip'), - 'sr' => array('Serbian', 'Српски'), - 'ss' => array('Siswati'), - 'st' => array('Sesotho'), - 'su' => array('Sudanese'), - 'sv' => array('Swedish', 'Svenska'), - 'sw' => array('Swahili', 'Kiswahili'), - 'ta' => array('Tamil', 'தமிழ்'), - 'te' => array('Telugu', 'తెలుగు'), - 'tg' => array('Tajik'), - 'th' => array('Thai', 'ภาษาไทย'), - 'ti' => array('Tigrinya'), - 'tk' => array('Turkmen'), - 'tl' => array('Tagalog'), - 'tn' => array('Setswana'), - 'to' => array('Tonga'), - 'tr' => array('Turkish', 'Türkçe'), - 'ts' => array('Tsonga'), - 'tt' => array('Tatar', 'Tatarça'), - 'tw' => array('Twi'), - 'ty' => array('Tahitian'), - 'ug' => array('Uighur'), - 'uk' => array('Ukrainian', 'Українська'), - 'ur' => array('Urdu', /* Left-to-right marker "‭" */ 'اردو', LANGUAGE_RTL), - 'uz' => array('Uzbek', "o'zbek"), - 've' => array('Venda'), - 'vi' => array('Vietnamese', 'Tiếng Việt'), - 'wo' => array('Wolof'), - 'xh' => array('Xhosa', 'isiXhosa'), - 'xx-lolspeak' => array('Lolspeak'), - 'yi' => array('Yiddish'), - 'yo' => array('Yoruba', 'Yorùbá'), - 'za' => array('Zhuang'), - 'zh-hans' => array('Chinese, Simplified', '简体中文'), - 'zh-hant' => array('Chinese, Traditional', '繁體中文'), - 'zu' => array('Zulu', 'isiZulu'), - ); -} -/** - * @} End of "locale-api-languages-predefined" - */ diff --git a/includes/locale.inc b/includes/locale.inc index fabde9025af8..199edd1918e0 100644 --- a/includes/locale.inc +++ b/includes/locale.inc @@ -466,8 +466,8 @@ function locale_add_language($langcode, $name = NULL, $native = NULL, $direction // If name was not set, we add a predefined language. if (!isset($name)) { - include_once DRUPAL_ROOT . '/includes/iso.inc'; - $predefined = _locale_get_predefined_list(); + include_once DRUPAL_ROOT . '/includes/standard.inc'; + $predefined = standard_language_list(); $name = $predefined[$langcode][0]; $native = isset($predefined[$langcode][1]) ? $predefined[$langcode][1] : $predefined[$langcode][0]; $direction = isset($predefined[$langcode][2]) ? $predefined[$langcode][2] : LANGUAGE_LTR; @@ -750,9 +750,9 @@ function _locale_rebuild_js($langcode = NULL) { * Prepares the language code list for a select form item with only the unsupported ones */ function _locale_prepare_predefined_list() { - include_once DRUPAL_ROOT . '/includes/iso.inc'; + include_once DRUPAL_ROOT . '/includes/standard.inc'; $languages = language_list(); - $predefined = _locale_get_predefined_list(); + $predefined = standard_language_list(); foreach ($predefined as $key => $value) { if (isset($languages[$key])) { unset($predefined[$key]); @@ -782,8 +782,8 @@ function _locale_prepare_predefined_list() { * An array of all country code => country name pairs. */ function country_get_list() { - include_once DRUPAL_ROOT . '/includes/iso.inc'; - $countries = _country_get_predefined_list(); + include_once DRUPAL_ROOT . '/includes/standard.inc'; + $countries = standard_country_list(); // Allow other modules to modify the country list. drupal_alter('countries', $countries); return $countries; diff --git a/modules/locale/locale.admin.inc b/modules/locale/locale.admin.inc index 3e8f707004f2..ecf9f1f607f1 100644 --- a/modules/locale/locale.admin.inc +++ b/modules/locale/locale.admin.inc @@ -280,7 +280,7 @@ function _locale_languages_common_controls(&$form, $language = NULL) { '#required' => TRUE, '#default_value' => @$language->language, '#disabled' => (isset($language->language)), - '#description' => t('RFC 4646 compliant language identifier. Language codes typically use a country code, and optionally, a script or regional variant name. Examples: "en", "en-US" and "zh-Hant".', array('@rfc4646' => 'http://www.ietf.org/rfc/rfc4646.txt')), + '#description' => t('Use language codes as defined by the W3C for interoperability. Examples: "en", "en-gb" and "zh-hant".', array('@w3ctags' => 'http://www.w3.org/International/articles/language-tags/')), ); } $form['name'] = array('#type' => 'textfield', @@ -331,8 +331,8 @@ function locale_languages_predefined_form_validate($form, &$form_state) { if (!isset($form_state['values']['name'])) { // Predefined language selection. - include_once DRUPAL_ROOT . '/includes/iso.inc'; - $predefined = _locale_get_predefined_list(); + include_once DRUPAL_ROOT . '/includes/standard.inc'; + $predefined = standard_language_list(); if (!isset($predefined[$langcode])) { form_set_error('langcode', t('Invalid language code.')); } @@ -355,8 +355,8 @@ function locale_languages_predefined_form_submit($form, &$form_state) { } else { // Predefined language selection. - include_once DRUPAL_ROOT . '/includes/iso.inc'; - $predefined = _locale_get_predefined_list(); + include_once DRUPAL_ROOT . '/includes/standard.inc'; + $predefined = standard_language_list(); locale_add_language($langcode); drupal_set_message(t('The language %language has been created and can now be used. More information is available on the help screen.', array('%language' => t($predefined[$langcode][0]), '@locale-help' => url('admin/help/locale')))); } diff --git a/modules/locale/locale.bulk.inc b/modules/locale/locale.bulk.inc index 8d6ed8f3c173..d27c80a5ad2d 100644 --- a/modules/locale/locale.bulk.inc +++ b/modules/locale/locale.bulk.inc @@ -68,8 +68,8 @@ function locale_translate_import_form_submit($form, &$form_state) { $languages = language_list('language'); $langcode = $form_state['values']['langcode']; if (!isset($languages[$langcode])) { - include_once DRUPAL_ROOT . '/includes/iso.inc'; - $predefined = _locale_get_predefined_list(); + include_once DRUPAL_ROOT . '/includes/standard.inc'; + $predefined = standard_language_list(); locale_add_language($langcode); drupal_set_message(t('The language %language has been created.', array('%language' => t($predefined[$langcode][0])))); } diff --git a/modules/system/system.api.php b/modules/system/system.api.php index bcdd923220e8..fc7b73783b18 100644 --- a/modules/system/system.api.php +++ b/modules/system/system.api.php @@ -4383,7 +4383,7 @@ function hook_updater_info_alter(&$updaters) { * The associative array of countries keyed by ISO 3166-1 country code. * * @see country_get_list() - * @see _country_get_predefined_list() + * @see standard_country_list() */ function hook_countries_alter(&$countries) { // Elbonia is now independent, so add it to the country list. From 94b0b58b3543bc0fd116271bd12a04ca70fb1da4 Mon Sep 17 00:00:00 2001 From: webchick Date: Thu, 8 Sep 2011 16:46:04 -0700 Subject: [PATCH 20/80] Committing missing file from #1231402 to un-break testbot. --- includes/standard.inc | 482 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 482 insertions(+) create mode 100644 includes/standard.inc diff --git a/includes/standard.inc b/includes/standard.inc new file mode 100644 index 000000000000..f8dcbf6cb9dc --- /dev/null +++ b/includes/standard.inc @@ -0,0 +1,482 @@ + country name pairs. + * + * Get an array of all country code => country name pairs as laid out + * in ISO 3166-1 alpha-2. Originally from the location project + * (http://drupal.org/project/location). + * + * @return + * An array of country code => country name pairs. + */ +function standard_country_list() { + static $countries; + + if (isset($countries)) { + return $countries; + } + $t = get_t(); + + $countries = array( + 'AD' => $t('Andorra'), + 'AE' => $t('United Arab Emirates'), + 'AF' => $t('Afghanistan'), + 'AG' => $t('Antigua and Barbuda'), + 'AI' => $t('Anguilla'), + 'AL' => $t('Albania'), + 'AM' => $t('Armenia'), + 'AN' => $t('Netherlands Antilles'), + 'AO' => $t('Angola'), + 'AQ' => $t('Antarctica'), + 'AR' => $t('Argentina'), + 'AS' => $t('American Samoa'), + 'AT' => $t('Austria'), + 'AU' => $t('Australia'), + 'AW' => $t('Aruba'), + 'AX' => $t('Aland Islands'), + 'AZ' => $t('Azerbaijan'), + 'BA' => $t('Bosnia and Herzegovina'), + 'BB' => $t('Barbados'), + 'BD' => $t('Bangladesh'), + 'BE' => $t('Belgium'), + 'BF' => $t('Burkina Faso'), + 'BG' => $t('Bulgaria'), + 'BH' => $t('Bahrain'), + 'BI' => $t('Burundi'), + 'BJ' => $t('Benin'), + 'BL' => $t('Saint Barthélemy'), + 'BM' => $t('Bermuda'), + 'BN' => $t('Brunei'), + 'BO' => $t('Bolivia'), + 'BR' => $t('Brazil'), + 'BS' => $t('Bahamas'), + 'BT' => $t('Bhutan'), + 'BV' => $t('Bouvet Island'), + 'BW' => $t('Botswana'), + 'BY' => $t('Belarus'), + 'BZ' => $t('Belize'), + 'CA' => $t('Canada'), + 'CC' => $t('Cocos (Keeling) Islands'), + 'CD' => $t('Congo (Kinshasa)'), + 'CF' => $t('Central African Republic'), + 'CG' => $t('Congo (Brazzaville)'), + 'CH' => $t('Switzerland'), + 'CI' => $t('Ivory Coast'), + 'CK' => $t('Cook Islands'), + 'CL' => $t('Chile'), + 'CM' => $t('Cameroon'), + 'CN' => $t('China'), + 'CO' => $t('Colombia'), + 'CR' => $t('Costa Rica'), + 'CU' => $t('Cuba'), + 'CV' => $t('Cape Verde'), + 'CX' => $t('Christmas Island'), + 'CY' => $t('Cyprus'), + 'CZ' => $t('Czech Republic'), + 'DE' => $t('Germany'), + 'DJ' => $t('Djibouti'), + 'DK' => $t('Denmark'), + 'DM' => $t('Dominica'), + 'DO' => $t('Dominican Republic'), + 'DZ' => $t('Algeria'), + 'EC' => $t('Ecuador'), + 'EE' => $t('Estonia'), + 'EG' => $t('Egypt'), + 'EH' => $t('Western Sahara'), + 'ER' => $t('Eritrea'), + 'ES' => $t('Spain'), + 'ET' => $t('Ethiopia'), + 'FI' => $t('Finland'), + 'FJ' => $t('Fiji'), + 'FK' => $t('Falkland Islands'), + 'FM' => $t('Micronesia'), + 'FO' => $t('Faroe Islands'), + 'FR' => $t('France'), + 'GA' => $t('Gabon'), + 'GB' => $t('United Kingdom'), + 'GD' => $t('Grenada'), + 'GE' => $t('Georgia'), + 'GF' => $t('French Guiana'), + 'GG' => $t('Guernsey'), + 'GH' => $t('Ghana'), + 'GI' => $t('Gibraltar'), + 'GL' => $t('Greenland'), + 'GM' => $t('Gambia'), + 'GN' => $t('Guinea'), + 'GP' => $t('Guadeloupe'), + 'GQ' => $t('Equatorial Guinea'), + 'GR' => $t('Greece'), + 'GS' => $t('South Georgia and the South Sandwich Islands'), + 'GT' => $t('Guatemala'), + 'GU' => $t('Guam'), + 'GW' => $t('Guinea-Bissau'), + 'GY' => $t('Guyana'), + 'HK' => $t('Hong Kong S.A.R., China'), + 'HM' => $t('Heard Island and McDonald Islands'), + 'HN' => $t('Honduras'), + 'HR' => $t('Croatia'), + 'HT' => $t('Haiti'), + 'HU' => $t('Hungary'), + 'ID' => $t('Indonesia'), + 'IE' => $t('Ireland'), + 'IL' => $t('Israel'), + 'IM' => $t('Isle of Man'), + 'IN' => $t('India'), + 'IO' => $t('British Indian Ocean Territory'), + 'IQ' => $t('Iraq'), + 'IR' => $t('Iran'), + 'IS' => $t('Iceland'), + 'IT' => $t('Italy'), + 'JE' => $t('Jersey'), + 'JM' => $t('Jamaica'), + 'JO' => $t('Jordan'), + 'JP' => $t('Japan'), + 'KE' => $t('Kenya'), + 'KG' => $t('Kyrgyzstan'), + 'KH' => $t('Cambodia'), + 'KI' => $t('Kiribati'), + 'KM' => $t('Comoros'), + 'KN' => $t('Saint Kitts and Nevis'), + 'KP' => $t('North Korea'), + 'KR' => $t('South Korea'), + 'KW' => $t('Kuwait'), + 'KY' => $t('Cayman Islands'), + 'KZ' => $t('Kazakhstan'), + 'LA' => $t('Laos'), + 'LB' => $t('Lebanon'), + 'LC' => $t('Saint Lucia'), + 'LI' => $t('Liechtenstein'), + 'LK' => $t('Sri Lanka'), + 'LR' => $t('Liberia'), + 'LS' => $t('Lesotho'), + 'LT' => $t('Lithuania'), + 'LU' => $t('Luxembourg'), + 'LV' => $t('Latvia'), + 'LY' => $t('Libya'), + 'MA' => $t('Morocco'), + 'MC' => $t('Monaco'), + 'MD' => $t('Moldova'), + 'ME' => $t('Montenegro'), + 'MF' => $t('Saint Martin (French part)'), + 'MG' => $t('Madagascar'), + 'MH' => $t('Marshall Islands'), + 'MK' => $t('Macedonia'), + 'ML' => $t('Mali'), + 'MM' => $t('Myanmar'), + 'MN' => $t('Mongolia'), + 'MO' => $t('Macao S.A.R., China'), + 'MP' => $t('Northern Mariana Islands'), + 'MQ' => $t('Martinique'), + 'MR' => $t('Mauritania'), + 'MS' => $t('Montserrat'), + 'MT' => $t('Malta'), + 'MU' => $t('Mauritius'), + 'MV' => $t('Maldives'), + 'MW' => $t('Malawi'), + 'MX' => $t('Mexico'), + 'MY' => $t('Malaysia'), + 'MZ' => $t('Mozambique'), + 'NA' => $t('Namibia'), + 'NC' => $t('New Caledonia'), + 'NE' => $t('Niger'), + 'NF' => $t('Norfolk Island'), + 'NG' => $t('Nigeria'), + 'NI' => $t('Nicaragua'), + 'NL' => $t('Netherlands'), + 'NO' => $t('Norway'), + 'NP' => $t('Nepal'), + 'NR' => $t('Nauru'), + 'NU' => $t('Niue'), + 'NZ' => $t('New Zealand'), + 'OM' => $t('Oman'), + 'PA' => $t('Panama'), + 'PE' => $t('Peru'), + 'PF' => $t('French Polynesia'), + 'PG' => $t('Papua New Guinea'), + 'PH' => $t('Philippines'), + 'PK' => $t('Pakistan'), + 'PL' => $t('Poland'), + 'PM' => $t('Saint Pierre and Miquelon'), + 'PN' => $t('Pitcairn'), + 'PR' => $t('Puerto Rico'), + 'PS' => $t('Palestinian Territory'), + 'PT' => $t('Portugal'), + 'PW' => $t('Palau'), + 'PY' => $t('Paraguay'), + 'QA' => $t('Qatar'), + 'RE' => $t('Reunion'), + 'RO' => $t('Romania'), + 'RS' => $t('Serbia'), + 'RU' => $t('Russia'), + 'RW' => $t('Rwanda'), + 'SA' => $t('Saudi Arabia'), + 'SB' => $t('Solomon Islands'), + 'SC' => $t('Seychelles'), + 'SD' => $t('Sudan'), + 'SE' => $t('Sweden'), + 'SG' => $t('Singapore'), + 'SH' => $t('Saint Helena'), + 'SI' => $t('Slovenia'), + 'SJ' => $t('Svalbard and Jan Mayen'), + 'SK' => $t('Slovakia'), + 'SL' => $t('Sierra Leone'), + 'SM' => $t('San Marino'), + 'SN' => $t('Senegal'), + 'SO' => $t('Somalia'), + 'SR' => $t('Suriname'), + 'ST' => $t('Sao Tome and Principe'), + 'SV' => $t('El Salvador'), + 'SY' => $t('Syria'), + 'SZ' => $t('Swaziland'), + 'TC' => $t('Turks and Caicos Islands'), + 'TD' => $t('Chad'), + 'TF' => $t('French Southern Territories'), + 'TG' => $t('Togo'), + 'TH' => $t('Thailand'), + 'TJ' => $t('Tajikistan'), + 'TK' => $t('Tokelau'), + 'TL' => $t('Timor-Leste'), + 'TM' => $t('Turkmenistan'), + 'TN' => $t('Tunisia'), + 'TO' => $t('Tonga'), + 'TR' => $t('Turkey'), + 'TT' => $t('Trinidad and Tobago'), + 'TV' => $t('Tuvalu'), + 'TW' => $t('Taiwan'), + 'TZ' => $t('Tanzania'), + 'UA' => $t('Ukraine'), + 'UG' => $t('Uganda'), + 'UM' => $t('United States Minor Outlying Islands'), + 'US' => $t('United States'), + 'UY' => $t('Uruguay'), + 'UZ' => $t('Uzbekistan'), + 'VA' => $t('Vatican'), + 'VC' => $t('Saint Vincent and the Grenadines'), + 'VE' => $t('Venezuela'), + 'VG' => $t('British Virgin Islands'), + 'VI' => $t('U.S. Virgin Islands'), + 'VN' => $t('Vietnam'), + 'VU' => $t('Vanuatu'), + 'WF' => $t('Wallis and Futuna'), + 'WS' => $t('Samoa'), + 'YE' => $t('Yemen'), + 'YT' => $t('Mayotte'), + 'ZA' => $t('South Africa'), + 'ZM' => $t('Zambia'), + 'ZW' => $t('Zimbabwe'), + ); + + // Sort the list. + natcasesort($countries); + + return $countries; +} + +/** + * Some common languages with their English and native names. + * + * Language codes are defined by the W3C language tags document for + * interoperability. Language codes typically have a language and optionally, + * a script or regional variant name. See + * http://www.w3.org/International/articles/language-tags/ for more information. + * + * @return + * An array of language code to language name information. + * Language name information itself is an array of English and native names. + */ +function standard_language_list() { + return array( + 'aa' => array('Afar'), + 'ab' => array('Abkhazian', 'аҧсуа бызшәа'), + 'ae' => array('Avestan'), + 'af' => array('Afrikaans'), + 'ak' => array('Akan'), + 'am' => array('Amharic', 'አማርኛ'), + 'ar' => array('Arabic', /* Left-to-right marker "‭" */ 'العربية', LANGUAGE_RTL), + 'as' => array('Assamese'), + 'ast' => array('Asturian'), + 'av' => array('Avar'), + 'ay' => array('Aymara'), + 'az' => array('Azerbaijani', 'azərbaycan'), + 'ba' => array('Bashkir'), + 'be' => array('Belarusian', 'Беларуская'), + 'bg' => array('Bulgarian', 'Български'), + 'bh' => array('Bihari'), + 'bi' => array('Bislama'), + 'bm' => array('Bambara', 'Bamanankan'), + 'bn' => array('Bengali'), + 'bo' => array('Tibetan'), + 'br' => array('Breton'), + 'bs' => array('Bosnian', 'Bosanski'), + 'ca' => array('Catalan', 'Català'), + 'ce' => array('Chechen'), + 'ch' => array('Chamorro'), + 'co' => array('Corsican'), + 'cr' => array('Cree'), + 'cs' => array('Czech', 'Čeština'), + 'cu' => array('Old Slavonic'), + 'cv' => array('Chuvash'), + 'cy' => array('Welsh', 'Cymraeg'), + 'da' => array('Danish', 'Dansk'), + 'de' => array('German', 'Deutsch'), + 'dv' => array('Maldivian'), + 'dz' => array('Bhutani'), + 'ee' => array('Ewe', 'Ɛʋɛ'), + 'el' => array('Greek', 'Ελληνικά'), + 'en' => array('English'), + 'en-gb' => array('English, British'), + 'eo' => array('Esperanto'), + 'es' => array('Spanish', 'Español'), + 'et' => array('Estonian', 'Eesti'), + 'eu' => array('Basque', 'Euskera'), + 'fa' => array('Persian', /* Left-to-right marker "‭" */ 'فارسی', LANGUAGE_RTL), + 'ff' => array('Fulah', 'Fulfulde'), + 'fi' => array('Finnish', 'Suomi'), + 'fil' => array('Filipino'), + 'fj' => array('Fiji'), + 'fo' => array('Faeroese'), + 'fr' => array('French', 'Français'), + 'fy' => array('Frisian', 'Frysk'), + 'ga' => array('Irish', 'Gaeilge'), + 'gd' => array('Scots Gaelic'), + 'gl' => array('Galician', 'Galego'), + 'gn' => array('Guarani'), + 'gsw-berne' => array('Swiss German'), + 'gu' => array('Gujarati'), + 'gv' => array('Manx'), + 'ha' => array('Hausa'), + 'he' => array('Hebrew', /* Left-to-right marker "‭" */ 'עברית', LANGUAGE_RTL), + 'hi' => array('Hindi', 'हिन्दी'), + 'ho' => array('Hiri Motu'), + 'hr' => array('Croatian', 'Hrvatski'), + 'ht' => array('Haitian Creole'), + 'hu' => array('Hungarian', 'Magyar'), + 'hy' => array('Armenian', 'Հայերեն'), + 'hz' => array('Herero'), + 'ia' => array('Interlingua'), + 'id' => array('Indonesian', 'Bahasa Indonesia'), + 'ie' => array('Interlingue'), + 'ig' => array('Igbo'), + 'ik' => array('Inupiak'), + 'is' => array('Icelandic', 'Íslenska'), + 'it' => array('Italian', 'Italiano'), + 'iu' => array('Inuktitut'), + 'ja' => array('Japanese', '日本語'), + 'jv' => array('Javanese'), + 'ka' => array('Georgian'), + 'kg' => array('Kongo'), + 'ki' => array('Kikuyu'), + 'kj' => array('Kwanyama'), + 'kk' => array('Kazakh', 'Қазақ'), + 'kl' => array('Greenlandic'), + 'km' => array('Cambodian'), + 'kn' => array('Kannada', 'ಕನ್ನಡ'), + 'ko' => array('Korean', '한국어'), + 'kr' => array('Kanuri'), + 'ks' => array('Kashmiri'), + 'ku' => array('Kurdish', 'Kurdî'), + 'kv' => array('Komi'), + 'kw' => array('Cornish'), + 'ky' => array('Kyrgyz', 'Кыргызча'), + 'la' => array('Latin', 'Latina'), + 'lb' => array('Luxembourgish'), + 'lg' => array('Luganda'), + 'ln' => array('Lingala'), + 'lo' => array('Laothian'), + 'lt' => array('Lithuanian', 'Lietuvių'), + 'lv' => array('Latvian', 'Latviešu'), + 'mg' => array('Malagasy'), + 'mh' => array('Marshallese'), + 'mi' => array('Māori'), + 'mk' => array('Macedonian', 'Македонски'), + 'ml' => array('Malayalam', 'മലയാളം'), + 'mn' => array('Mongolian'), + 'mo' => array('Moldavian'), + 'mr' => array('Marathi'), + 'ms' => array('Malay', 'Bahasa Melayu'), + 'mt' => array('Maltese', 'Malti'), + 'my' => array('Burmese'), + 'na' => array('Nauru'), + 'nd' => array('North Ndebele'), + 'ne' => array('Nepali'), + 'ng' => array('Ndonga'), + 'nl' => array('Dutch', 'Nederlands'), + 'nb' => array('Norwegian Bokmål', 'Bokmål'), + 'nn' => array('Norwegian Nynorsk', 'Nynorsk'), + 'nr' => array('South Ndebele'), + 'nv' => array('Navajo'), + 'ny' => array('Chichewa'), + 'oc' => array('Occitan'), + 'om' => array('Oromo'), + 'or' => array('Oriya'), + 'os' => array('Ossetian'), + 'pa' => array('Punjabi'), + 'pi' => array('Pali'), + 'pl' => array('Polish', 'Polski'), + 'ps' => array('Pashto', /* Left-to-right marker "‭" */ 'پښتو', LANGUAGE_RTL), + 'pt' => array('Portuguese, International'), + 'pt-pt' => array('Portuguese, Portugal', 'Português'), + 'pt-br' => array('Portuguese, Brazil', 'Português'), + 'qu' => array('Quechua'), + 'rm' => array('Rhaeto-Romance'), + 'rn' => array('Kirundi'), + 'ro' => array('Romanian', 'Română'), + 'ru' => array('Russian', 'Русский'), + 'rw' => array('Kinyarwanda'), + 'sa' => array('Sanskrit'), + 'sc' => array('Sardinian'), + 'sco' => array('Scots'), + 'sd' => array('Sindhi'), + 'se' => array('Northern Sami'), + 'sg' => array('Sango'), + 'sh' => array('Serbo-Croatian'), + 'si' => array('Sinhala', 'සිංහල'), + 'sk' => array('Slovak', 'Slovenčina'), + 'sl' => array('Slovenian', 'Slovenščina'), + 'sm' => array('Samoan'), + 'sn' => array('Shona'), + 'so' => array('Somali'), + 'sq' => array('Albanian', 'Shqip'), + 'sr' => array('Serbian', 'Српски'), + 'ss' => array('Siswati'), + 'st' => array('Sesotho'), + 'su' => array('Sudanese'), + 'sv' => array('Swedish', 'Svenska'), + 'sw' => array('Swahili', 'Kiswahili'), + 'ta' => array('Tamil', 'தமிழ்'), + 'te' => array('Telugu', 'తెలుగు'), + 'tg' => array('Tajik'), + 'th' => array('Thai', 'ภาษาไทย'), + 'ti' => array('Tigrinya'), + 'tk' => array('Turkmen'), + 'tl' => array('Tagalog'), + 'tn' => array('Setswana'), + 'to' => array('Tonga'), + 'tr' => array('Turkish', 'Türkçe'), + 'ts' => array('Tsonga'), + 'tt' => array('Tatar', 'Tatarça'), + 'tw' => array('Twi'), + 'ty' => array('Tahitian'), + 'ug' => array('Uighur'), + 'uk' => array('Ukrainian', 'Українська'), + 'ur' => array('Urdu', /* Left-to-right marker "‭" */ 'اردو', LANGUAGE_RTL), + 'uz' => array('Uzbek', "o'zbek"), + 've' => array('Venda'), + 'vi' => array('Vietnamese', 'Tiếng Việt'), + 'wo' => array('Wolof'), + 'xh' => array('Xhosa', 'isiXhosa'), + 'xx-lolspeak' => array('Lolspeak'), + 'yi' => array('Yiddish'), + 'yo' => array('Yoruba', 'Yorùbá'), + 'za' => array('Zhuang'), + 'zh-hans' => array('Chinese, Simplified', '简体中文'), + 'zh-hant' => array('Chinese, Traditional', '繁體中文'), + 'zu' => array('Zulu', 'isiZulu'), + ); +} From f40a6931fd3ff714584f95b5814d3cfeb843103e Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Sat, 10 Sep 2011 10:19:08 -0400 Subject: [PATCH 21/80] - Patch #565288 by bfroehle, anarcat, p.brouwers, aspilicious: cannot install Drupal when Drupal.org is 503 (at Drupalcon!). --- modules/update/tests/update_test.module | 14 ++++++++++++++ modules/update/update.fetch.inc | 2 +- modules/update/update.test | 14 ++++++++++++-- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/modules/update/tests/update_test.module b/modules/update/tests/update_test.module index 4e32d336afea..4acb6ef837e6 100644 --- a/modules/update/tests/update_test.module +++ b/modules/update/tests/update_test.module @@ -12,6 +12,12 @@ function update_test_menu() { 'access callback' => TRUE, 'type' => MENU_CALLBACK, ); + $items['503-error'] = array( + 'title' => t('503 Service unavailable'), + 'page callback' => 'update_callback_service_unavailable', + 'access callback' => TRUE, + 'type' => MENU_CALLBACK, + ); return $items; } @@ -148,3 +154,11 @@ class UpdateTestFileTransfer { return $form; } } + +/** + * Return an Error 503 (Service unavailable) page. + */ +function update_callback_service_unavailable() { + drupal_add_http_header('Status', '503 Service unavailable'); + print "503 Service Temporarily Unavailable"; +} diff --git a/modules/update/update.fetch.inc b/modules/update/update.fetch.inc index ff69cbb114d6..7ac0dbefbc2b 100644 --- a/modules/update/update.fetch.inc +++ b/modules/update/update.fetch.inc @@ -143,7 +143,7 @@ function _update_process_fetch_task($project) { if (empty($fail[$fetch_url_base]) || $fail[$fetch_url_base] < $max_fetch_attempts) { $xml = drupal_http_request($url); - if (isset($xml->data)) { + if (!isset($xml->error) && isset($xml->data)) { $data = $xml->data; } } diff --git a/modules/update/update.test b/modules/update/update.test index 2688bb3aac99..a657f91de0da 100644 --- a/modules/update/update.test +++ b/modules/update/update.test @@ -32,9 +32,9 @@ class UpdateTestHelper extends DrupalWebTestCase { * * @see update_test_mock_page() */ - protected function refreshUpdateStatus($xml_map) { + protected function refreshUpdateStatus($xml_map, $url = 'update-test') { // Tell update module to fetch from the URL provided by update_test module. - variable_set('update_fetch_url', url('update-test', array('absolute' => TRUE))); + variable_set('update_fetch_url', url($url, array('absolute' => TRUE))); // Save the map for update_test_mock_page() to use. variable_set('update_test_xml_map', $xml_map); // Manually check the update status. @@ -215,6 +215,16 @@ class UpdateCoreTestCase extends UpdateTestHelper { $this->assertNoText(t('There is a security update available for your version of Drupal.')); } + /** + * Tests the update module when the update server returns 503 (Service unavailable) errors. + */ + function testServiceUnavailable() { + $this->refreshUpdateStatus(array(), '503-error'); + // Ensure that no "Warning: SimpleXMLElement..." parse errors are found. + $this->assertNoText('SimpleXMLElement'); + $this->assertUniqueText(t('Failed to get available update data for one project.')); + } + protected function setSystemInfo7_0() { $setting = array( '#all' => array( From 1ce52d92fa19def95e89af856b7c29646dfba537 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Sat, 10 Sep 2011 10:21:02 -0400 Subject: [PATCH 22/80] - Patch #1212018 by Liam Morland: form required marker in field.form.inc should use theme system. --- modules/field/field.form.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/field/field.form.inc b/modules/field/field.form.inc index 79fc14a20e87..be3685d875ee 100644 --- a/modules/field/field.form.inc +++ b/modules/field/field.form.inc @@ -273,7 +273,7 @@ function theme_field_multiple_value_form($variables) { if ($element['#cardinality'] > 1 || $element['#cardinality'] == FIELD_CARDINALITY_UNLIMITED) { $table_id = drupal_html_id($element['#field_name'] . '_values'); $order_class = $element['#field_name'] . '-delta-order'; - $required = !empty($element['#required']) ? '*' : ''; + $required = !empty($element['#required']) ? theme('form_required_marker', $variables) : ''; $header = array( array( From 990ef700593f2d18d04115054f8ef48095ec31e8 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Sat, 10 Sep 2011 11:23:23 -0400 Subject: [PATCH 23/80] - Patch #1273444 by Xano: Missing operator docs for EntityFieldQuery's propertyCondition() and addFieldCondition(). --- includes/entity.inc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/includes/entity.inc b/includes/entity.inc index 31679fdccb9a..99baf4992b47 100644 --- a/includes/entity.inc +++ b/includes/entity.inc @@ -613,6 +613,8 @@ class EntityFieldQuery { * literals of the same type as the column. * - 'BETWEEN': This operator expects $value to be an array of two literals * of the same type as the column. + * The operator can be omitted, and will default to 'IN' if the value is an + * array, or to '=' otherwise. * * @return EntityFieldQuery * The called object. @@ -729,6 +731,8 @@ class EntityFieldQuery { * literals of the same type as the column. * - 'BETWEEN': This operator expects $value to be an array of two literals * of the same type as the column. + * The operator can be omitted, and will default to 'IN' if the value is an + * array, or to '=' otherwise. * @param $delta_group * An arbitrary identifier: conditions in the same group must have the same * $delta_group. For example, let's presume a multivalue field which has From 14f6a17ef22f7b8a882d6e9100211a6b0eeaed95 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Sat, 10 Sep 2011 11:26:40 -0400 Subject: [PATCH 24/80] - Patch #881376 by marcvangend | Lenz Grimmer: Fixed 'Run the clean URL test' UX is broken. --- modules/system/system.admin.inc | 68 ++++++++++++++++++++++++++------- 1 file changed, 54 insertions(+), 14 deletions(-) diff --git a/modules/system/system.admin.inc b/modules/system/system.admin.inc index 9dffd5999b19..baac6f71ff27 100644 --- a/modules/system/system.admin.inc +++ b/modules/system/system.admin.inc @@ -2182,24 +2182,45 @@ function system_site_maintenance_mode() { * @see system_settings_form() */ function system_clean_url_settings($form, &$form_state) { - global $base_url; - - // When accessing this form using a non-clean URL, allow a re-check to make - // sure clean URLs can be disabled at all times. $available = FALSE; - if (strpos(request_uri(), '?q=') === FALSE || !empty($_SESSION['clean_url'])) { + $conflict = FALSE; + + // If the request URI is a clean URL, clean URLs must be available. + // Otherwise, run a test. + if (strpos(request_uri(), '?q=') === FALSE && strpos(request_uri(), '&q=') === FALSE) { $available = TRUE; } else { - $request = drupal_http_request($base_url . '/admin/config/search/clean-urls/check'); + $request = drupal_http_request($GLOBALS['base_url'] . '/admin/config/search/clean-urls/check'); + // If the request returns HTTP 200, clean URLs are available. if (isset($request->code) && $request->code == 200) { $available = TRUE; + // If the user started the clean URL test, provide explicit feedback. + if (isset($form_state['input']['clean_url_test_execute'])) { + drupal_set_message(t('The clean URL test passed.')); + } + } + else { + // If the test failed while clean URLs are enabled, make sure clean URLs + // can be disabled. + if (variable_get('clean_url', 0)) { + $conflict = TRUE; + // Warn the user of a conflicting situation, unless after processing + // a submitted form. + if (!isset($form_state['input']['op'])) { + drupal_set_message(t('Clean URLs are enabled, but the clean URL test failed. Uncheck the box below to disable clean URLs.'), 'warning'); + } + } + // If the user started the clean URL test, provide explicit feedback. + elseif (isset($form_state['input']['clean_url_test_execute'])) { + drupal_set_message(t('The clean URL test failed.'), 'warning'); + } } } - if ($available) { - $_SESSION['clean_url'] = TRUE; - + // Show the enable/disable form if clean URLs are available or if the user + // must be able to resolve a conflicting setting. + if ($available || $conflict) { $form['clean_url'] = array( '#type' => 'checkbox', '#title' => t('Enable clean URLs'), @@ -2207,18 +2228,37 @@ function system_clean_url_settings($form, &$form_state) { '#description' => t('Use URLs like example.com/user instead of example.com/?q=user.'), ); $form = system_settings_form($form); + if ($conflict) { + // $form_state['redirect'] needs to be set to the non-clean URL, + // otherwise the setting is not saved. + $form_state['redirect'] = url('', array('query' => array('q' => '/admin/config/search/clean-urls'))); + } } + // Show the clean URLs test form. else { drupal_add_js(drupal_get_path('module', 'system') . '/system.js'); - $form_state['redirect'] = $base_url . '/admin/config/search/clean-urls'; + $form_state['redirect'] = url('admin/config/search/clean-urls'); $form['clean_url_description'] = array( '#type' => 'markup', - '#markup' => '

' . t('Use URLs like example.com/user instead of example.com/?q=user.') . ' ' . t('If you are directed to a Page not found (404) error after testing for clean URLs, see the online handbook.', array('@handbook' => 'http://drupal.org/node/15365')) . '

', + '#markup' => '

' . t('Use URLs like example.com/user instead of example.com/?q=user.'), ); - $form['clean_url_test'] = array( - '#type' => 'submit', - '#value' => t('Run the clean URL test'), + // Explain why the user is seeing this page and tell him what to expect + // after clicking the 'Run the clean URL test' button. + $form['clean_url_test_result'] = array( + '#type' => 'markup', + '#markup' => '

' . t('Clean URLs cannot be enabled. If you are directed to this page or to a Page not found (404) error after testing for clean URLs, see the online handbook.', array('@handbook' => 'http://drupal.org/node/15365')) . '

', + ); + $form['actions'] = array( + '#type' => 'actions', + 'clean_url_test' => array( + '#type' => 'submit', + '#value' => t('Run the clean URL test'), + ), + ); + $form['clean_url_test_execute'] = array( + '#type' => 'hidden', + '#value' => 1, ); } From e72649440fbf9ca8822a431797d0689a3d0db514 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Sat, 10 Sep 2011 11:32:08 -0400 Subject: [PATCH 25/80] - Patch #1216972 by aspilicious: clean up the CSS for Color module. --- modules/color/color-rtl.css | 44 -------------------- modules/color/color.css | 81 ------------------------------------- modules/color/color.module | 2 +- 3 files changed, 1 insertion(+), 126 deletions(-) delete mode 100644 modules/color/color-rtl.css delete mode 100644 modules/color/color.css diff --git a/modules/color/color-rtl.css b/modules/color/color-rtl.css deleted file mode 100644 index bfbcd499ff8f..000000000000 --- a/modules/color/color-rtl.css +++ /dev/null @@ -1,44 +0,0 @@ - -#placeholder { - left: 0; - right: auto; -} - -/* Palette */ -.color-form .form-item { - padding-left: 0; - padding-right: 1em; -} -.color-form label { - float: right; - clear: right; -} -.color-form .form-text, -.color-form .form-select { - float: right; -} -.color-form .form-text { - margin-right: 0; - margin-left: 5px; -} -#palette .hook { - float: right; -} -#palette .down, -#palette .up, -#palette .both { - background: url(images/hook-rtl.png) no-repeat 0 0; -} -#palette .up { - background-position: 0 -27px; -} -#palette .both { - background-position: 0 -54px; -} -#palette .lock { - float: right; - right: -10px; -} -html.js #preview { - float: right; -} diff --git a/modules/color/color.css b/modules/color/color.css deleted file mode 100644 index e513dadf5408..000000000000 --- a/modules/color/color.css +++ /dev/null @@ -1,81 +0,0 @@ - -/* Farbtastic placement */ -.color-form { - max-width: 50em; - position: relative; -} -#placeholder { - position: absolute; - top: 0; - right: 0; /* LTR */ -} - -/* Palette */ -.color-form .form-item { - height: 2em; - line-height: 2em; - padding-left: 1em; /* LTR */ - margin: 0.5em 0; -} -.color-form label { - float: left; /* LTR */ - clear: left; /* LTR */ - width: 10em; -} -.color-form .form-text, -.color-form .form-select { - float: left; /* LTR */ -} -.color-form .form-text { - text-align: center; - margin-right: 5px; /* LTR */ - cursor: pointer; -} - -#palette .hook { - float: left; /* LTR */ - margin-top: 3px; - width: 16px; - height: 16px; -} -#palette .down, -#palette .up, -#palette .both { - background: url(images/hook.png) no-repeat 100% 0; /* LTR */ -} -#palette .up { - background-position: 100% -27px; /* LTR */ -} -#palette .both { - background-position: 100% -54px; /* LTR */ -} - -#palette .lock { - float: left; /* LTR */ - position: relative; - top: -1.4em; - left: -10px; /* LTR */ - width: 20px; - height: 25px; - background: url(images/lock.png) no-repeat 50% 2px; - cursor: pointer; -} -#palette .unlocked { - background-position: 50% -22px; -} -#palette .form-item { - width: 20em; -} -#palette .item-selected { - background: #eee; -} - -/* Preview */ -#preview { - display: none; -} -html.js #preview { - display: block; - position: relative; - float: left; /* LTR */ -} diff --git a/modules/color/color.module b/modules/color/color.module index 09eb82b23b84..7665631ed4e8 100644 --- a/modules/color/color.module +++ b/modules/color/color.module @@ -189,7 +189,7 @@ function color_scheme_form($complete_form, &$form_state, $theme) { ), // Add custom CSS. 'css' => array( - $base . '/color.css' => array(), + $base . '/color.admin.css' => array(), ), // Add custom JavaScript. 'js' => array( From 9aac55b4a7c3b739a7dc4fa99e85892c0c8a47db Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Sat, 10 Sep 2011 11:33:20 -0400 Subject: [PATCH 26/80] - Patch #1216948 by tars16, Aron Novak: clean up the CSS for Aggregator module. --- modules/aggregator/aggregator-rtl.css | 4 -- modules/aggregator/aggregator-wrapper.tpl.php | 2 +- modules/aggregator/aggregator.css | 38 ------------------- modules/aggregator/aggregator.info | 2 +- themes/garland/style.css | 10 ++--- 5 files changed, 7 insertions(+), 49 deletions(-) delete mode 100644 modules/aggregator/aggregator-rtl.css delete mode 100644 modules/aggregator/aggregator.css diff --git a/modules/aggregator/aggregator-rtl.css b/modules/aggregator/aggregator-rtl.css deleted file mode 100644 index ea59ca3a19f1..000000000000 --- a/modules/aggregator/aggregator-rtl.css +++ /dev/null @@ -1,4 +0,0 @@ - -#aggregator .feed-source .feed-icon { - float: left; -} diff --git a/modules/aggregator/aggregator-wrapper.tpl.php b/modules/aggregator/aggregator-wrapper.tpl.php index 80b903271d4f..0c2f774f55e5 100644 --- a/modules/aggregator/aggregator-wrapper.tpl.php +++ b/modules/aggregator/aggregator-wrapper.tpl.php @@ -12,7 +12,7 @@ * @see template_preprocess_aggregator_wrapper() */ ?> -
+
diff --git a/modules/aggregator/aggregator.css b/modules/aggregator/aggregator.css deleted file mode 100644 index 13c58ffe70ee..000000000000 --- a/modules/aggregator/aggregator.css +++ /dev/null @@ -1,38 +0,0 @@ - -#aggregator .feed-source .feed-title { - margin-top: 0; -} -#aggregator .feed-source .feed-image img { - margin-bottom: 0.75em; -} -#aggregator .feed-source .feed-icon { - float: right; /* LTR */ - display: block; -} -#aggregator .feed-item { - margin-bottom: 1.5em; -} -#aggregator .feed-item-title { - margin-bottom: 0; - font-size: 1.3em; -} -#aggregator .feed-item-meta, -#aggregator .feed-item-body { - margin-bottom: 0.5em; -} -#aggregator .feed-item-categories { - font-size: 0.9em; -} -#aggregator td { - vertical-align: bottom; -} -#aggregator td.categorize-item { - white-space: nowrap; -} -#aggregator .categorize-item .news-item .body { - margin-top: 0; -} -#aggregator .categorize-item h3 { - margin-bottom: 1em; - margin-top: 0; -} diff --git a/modules/aggregator/aggregator.info b/modules/aggregator/aggregator.info index f147740d3e23..4d57ba853f58 100644 --- a/modules/aggregator/aggregator.info +++ b/modules/aggregator/aggregator.info @@ -5,4 +5,4 @@ version = VERSION core = 8.x files[] = aggregator.test configure = admin/config/services/aggregator/settings -stylesheets[all][] = aggregator.css +stylesheets[all][] = aggregator.theme.css diff --git a/themes/garland/style.css b/themes/garland/style.css index 4a5961c61996..079f8a36e821 100644 --- a/themes/garland/style.css +++ b/themes/garland/style.css @@ -868,22 +868,22 @@ ul.inline li { /** * Aggregator.module */ -#aggregator { +.aggregator { margin-top: 1em; } -#aggregator .feed-item-title { +.aggregator .feed-item-title { font-size: 160%; line-height: 130%; } -#aggregator .feed-item { +.aggregator .feed-item { border-bottom: 1px solid #e9eff3; margin: -1.5em -31px 1.75em; padding: 1.5em 31px; } -#aggregator .feed-item-categories { +.aggregator .feed-item-categories { font-size: 0.92em; } -#aggregator .feed-item-meta { +.aggregator .feed-item-meta { font-size: 0.92em; color: #898989; } From 8bdea8d9f034f091a844110d4e9c1339d7d79d7c Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Sat, 10 Sep 2011 11:37:08 -0400 Subject: [PATCH 27/80] - Patch #1263882 by cweagans, TR: Code style: authorize.php. --- authorize.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/authorize.php b/authorize.php index cd3df50e6538..26319b8e7128 100644 --- a/authorize.php +++ b/authorize.php @@ -35,7 +35,7 @@ define('DRUPAL_ROOT', getcwd()); define('MAINTENANCE_MODE', 'update'); /** - * Render a 403 access denied page for authorize.php + * Renders a 403 access denied page for authorize.php. */ function authorize_access_denied_page() { drupal_add_http_header('Status', '403 Forbidden'); @@ -45,7 +45,7 @@ function authorize_access_denied_page() { } /** - * Determine if the current user is allowed to run authorize.php. + * Determines if the current user is allowed to run authorize.php. * * The killswitch in settings.php overrides all else, otherwise, the user must * have access to the 'administer software updates' permission. @@ -145,7 +145,7 @@ if (authorize_access_allowed()) { l(t('Front page'), ''), )); } - + $output .= theme('item_list', array('items' => $links, 'title' => t('Next steps'))); } // If a batch is running, let it run. From f214431458d1621b3d28068444bd9ff946dc5450 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Sat, 10 Sep 2011 11:39:03 -0400 Subject: [PATCH 28/80] - Patch #1263902 by cweagans: Code style: filetransfer.inc. --- includes/filetransfer/filetransfer.inc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/includes/filetransfer/filetransfer.inc b/includes/filetransfer/filetransfer.inc index 63e41230bf35..aa7ebe470e2e 100644 --- a/includes/filetransfer/filetransfer.inc +++ b/includes/filetransfer/filetransfer.inc @@ -1,6 +1,7 @@ isFile($check . '/' . basename(__FILE__))) { // Remove the trailing slash. - return substr($chroot,0,-1); + return substr($chroot, 0, -1); } $chroot .= array_shift($parts) . '/'; } From 9177c2a11c60b00dc5a468d2e4ef949f88c2bfad Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Sun, 11 Sep 2011 12:14:18 -0400 Subject: [PATCH 29/80] - Patch #1272686 by Paul Simard, bfroehle, beejeebus: convert existing cache_* calls to the new cache()-> API. --- includes/common.inc | 18 ++++++------ includes/form.inc | 12 ++++---- includes/gettext.inc | 2 +- includes/menu.inc | 20 ++++++------- includes/module.inc | 23 ++++++++------- includes/path.inc | 4 +-- includes/registry.inc | 4 +-- includes/theme.inc | 6 ++-- modules/block/block.module | 4 +-- modules/book/book.module | 10 +++---- modules/field/field.attach.inc | 6 ++-- modules/field/field.info.inc | 12 ++++---- modules/field/field.module | 2 +- modules/field/tests/field.test | 28 +++++++++---------- modules/filter/filter.module | 20 ++++++------- modules/image/image.api.php | 2 +- modules/image/image.module | 16 +++++------ modules/locale/locale.admin.inc | 2 +- modules/locale/locale.module | 12 ++++---- modules/locale/locale.pages.inc | 4 +-- modules/menu/menu.module | 4 +-- modules/node/node.module | 6 ++-- modules/path/path.test | 8 +++--- modules/simpletest/drupal_web_test_case.php | 2 +- modules/simpletest/simpletest.module | 6 ++-- modules/simpletest/tests/bootstrap.test | 2 +- modules/simpletest/tests/cache.test | 8 +++--- modules/simpletest/tests/module.test | 10 +++---- modules/simpletest/tests/upgrade/upgrade.test | 4 +-- modules/system/system.admin.inc | 2 +- modules/system/system.module | 2 +- update.php | 2 +- 32 files changed, 131 insertions(+), 132 deletions(-) diff --git a/includes/common.inc b/includes/common.inc index e75aa81a1e53..bb4d8e7321bd 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -4992,7 +4992,7 @@ function drupal_page_set_cache() { if (variable_get('page_compression', TRUE) && extension_loaded('zlib')) { $cache->data['body'] = gzencode($cache->data['body'], 9, FORCE_GZIP); } - cache_set($cache->cid, $cache->data, 'cache_page', $cache->expire); + cache('page')->set($cache->cid, $cache->data, $cache->expire); } return $cache; } @@ -5813,7 +5813,7 @@ function drupal_render_cache_get($elements) { } $bin = isset($elements['#cache']['bin']) ? $elements['#cache']['bin'] : 'cache'; - if (!empty($cid) && $cache = cache_get($cid, $bin)) { + if (!empty($cid) && $cache = cache($bin)->get($cid)) { // Add additional libraries, JavaScript, CSS and other data attached // to this element. if (isset($cache->data['#attached'])) { @@ -5858,7 +5858,7 @@ function drupal_render_cache_set(&$markup, $elements) { } $bin = isset($elements['#cache']['bin']) ? $elements['#cache']['bin'] : 'cache'; $expire = isset($elements['#cache']['expire']) ? $elements['#cache']['expire'] : CACHE_PERMANENT; - cache_set($cid, $data, $bin, $expire); + cache($bin)->set($cid, $data, $expire); } /** @@ -7149,7 +7149,7 @@ function drupal_flush_all_caches() { $core = array('cache', 'cache_path', 'cache_filter', 'cache_bootstrap', 'cache_page'); $cache_tables = array_merge(module_invoke_all('flush_caches'), $core); foreach ($cache_tables as $table) { - cache_clear_all('*', $table, TRUE); + cache($table)->flush(); } // Rebuild the bootstrap module list. We do this here so that developers @@ -7299,7 +7299,7 @@ function entity_get_info($entity_type = NULL) { $langcode = $language->language; if (empty($entity_info)) { - if ($cache = cache_get("entity_info:$langcode")) { + if ($cache = cache()->get("entity_info:$langcode")) { $entity_info = $cache->data; } else { @@ -7342,7 +7342,7 @@ function entity_get_info($entity_type = NULL) { } // Let other modules alter the entity info. drupal_alter('entity_info', $entity_info); - cache_set("entity_info:$langcode", $entity_info); + cache()->set("entity_info:$langcode", $entity_info); } } @@ -7360,7 +7360,7 @@ function entity_get_info($entity_type = NULL) { function entity_info_cache_clear() { drupal_static_reset('entity_get_info'); // Clear all languages. - cache_clear_all('entity_info:', 'cache', TRUE); + cache()->deletePrefix('entity_info:'); } /** @@ -7738,13 +7738,13 @@ function archiver_get_info() { $archiver_info = &drupal_static(__FUNCTION__, array()); if (empty($archiver_info)) { - $cache = cache_get('archiver_info'); + $cache = cache()->get('archiver_info'); if ($cache === FALSE) { // Rebuild the cache and save it. $archiver_info = module_invoke_all('archiver_info'); drupal_alter('archiver_info', $archiver_info); uasort($archiver_info, 'drupal_sort_weight'); - cache_set('archiver_info', $archiver_info); + cache()->set('archiver_info', $archiver_info); } else { $archiver_info = $cache->data; diff --git a/includes/form.inc b/includes/form.inc index 442016af0268..cfbea38645b3 100644 --- a/includes/form.inc +++ b/includes/form.inc @@ -472,12 +472,12 @@ function drupal_rebuild_form($form_id, &$form_state, $old_form = NULL) { * Fetch a form from cache. */ function form_get_cache($form_build_id, &$form_state) { - if ($cached = cache_get('form_' . $form_build_id, 'cache_form')) { + if ($cached = cache('form')->get('form_' . $form_build_id)) { $form = $cached->data; global $user; if ((isset($form['#cache_token']) && drupal_valid_token($form['#cache_token'])) || (!isset($form['#cache_token']) && !$user->uid)) { - if ($cached = cache_get('form_state_' . $form_build_id, 'cache_form')) { + if ($cached = cache('form')->get('form_state_' . $form_build_id)) { // Re-populate $form_state for subsequent rebuilds. $form_state = $cached->data + $form_state; @@ -511,12 +511,12 @@ function form_set_cache($form_build_id, $form, $form_state) { if ($GLOBALS['user']->uid) { $form['#cache_token'] = drupal_get_token(); } - cache_set('form_' . $form_build_id, $form, 'cache_form', REQUEST_TIME + $expire); + cache('form')->set('form_' . $form_build_id, $form, REQUEST_TIME + $expire); } // Cache form state. if ($data = array_diff_key($form_state, array_flip(form_state_keys_no_cache()))) { - cache_set('form_state_' . $form_build_id, $data, 'cache_form', REQUEST_TIME + $expire); + cache('form')->set('form_state_' . $form_build_id, $data, REQUEST_TIME + $expire); } } @@ -835,8 +835,8 @@ function drupal_process_form($form_id, &$form, &$form_state) { // here, as we've finished with them. The in-memory copies are still // here, though. if (!variable_get('cache', 0) && !empty($form_state['values']['form_build_id'])) { - cache_clear_all('form_' . $form_state['values']['form_build_id'], 'cache_form'); - cache_clear_all('form_state_' . $form_state['values']['form_build_id'], 'cache_form'); + cache('form')->delete('form_' . $form_state['values']['form_build_id']); + cache('form')->delete('form_state_' . $form_state['values']['form_build_id']); } // If batches were set in the submit handlers, we process them now, diff --git a/includes/gettext.inc b/includes/gettext.inc index 39a6fbe9ec3a..fa9952b74f90 100644 --- a/includes/gettext.inc +++ b/includes/gettext.inc @@ -53,7 +53,7 @@ function _locale_import_po($file, $langcode, $mode) { // Clear cache and force refresh of JavaScript translations. _locale_invalidate_js($langcode); - cache_clear_all('locale:', 'cache', TRUE); + cache()->deletePrefix('locale:'); // Rebuild the menu, strings may have changed. menu_rebuild(); diff --git a/includes/menu.inc b/includes/menu.inc index 3e775db7ba93..0e3f5b689877 100644 --- a/includes/menu.inc +++ b/includes/menu.inc @@ -441,14 +441,14 @@ function menu_get_item($path = NULL, $router_item = NULL) { // Since there is no limit to the length of $path, use a hash to keep it // short yet unique. $cid = 'menu_item:' . hash('sha256', $path); - if ($cached = cache_get($cid, 'cache_menu')) { + if ($cached = cache('menu')->get($cid)) { $router_item = $cached->data; } else { $parts = array_slice($original_map, 0, MENU_MAX_PARTS); $ancestors = menu_get_ancestors($parts); $router_item = db_query_range('SELECT * FROM {menu_router} WHERE path IN (:ancestors) ORDER BY fit DESC', 0, 1, array(':ancestors' => $ancestors))->fetchAssoc(); - cache_set($cid, $router_item, 'cache_menu'); + cache('menu')->set($cid, $router_item); } if ($router_item) { // Allow modules to alter the router item before it is translated and @@ -1095,7 +1095,7 @@ function menu_tree_all_data($menu_name, $link = NULL, $max_depth = NULL) { if (!isset($tree[$cid])) { // If the static variable doesn't have the data, check {cache_menu}. - $cache = cache_get($cid, 'cache_menu'); + $cache = cache('menu')->get($cid); if ($cache && isset($cache->data)) { // If the cache entry exists, it contains the parameters for // menu_build_tree(). @@ -1122,7 +1122,7 @@ function menu_tree_all_data($menu_name, $link = NULL, $max_depth = NULL) { } // Cache the tree building parameters using the page-specific cid. - cache_set($cid, $tree_parameters, 'cache_menu'); + cache('menu')->set($cid, $tree_parameters); } // Build the tree using the parameters; the resulting tree will be cached @@ -1179,7 +1179,7 @@ function menu_tree_page_data($menu_name, $max_depth = NULL, $only_active_trail = if (!isset($tree[$cid])) { // If the static variable doesn't have the data, check {cache_menu}. - $cache = cache_get($cid, 'cache_menu'); + $cache = cache('menu')->get($cid); if ($cache && isset($cache->data)) { // If the cache entry exists, it contains the parameters for // menu_build_tree(). @@ -1251,7 +1251,7 @@ function menu_tree_page_data($menu_name, $max_depth = NULL, $only_active_trail = $tree_parameters['active_trail'] = $active_trail; } // Cache the tree building parameters using the page-specific cid. - cache_set($cid, $tree_parameters, 'cache_menu'); + cache('menu')->set($cid, $tree_parameters); } // Build the tree using the parameters; the resulting tree will be cached @@ -1317,7 +1317,7 @@ function _menu_build_tree($menu_name, array $parameters = array()) { // If we do not have this tree in the static cache, check {cache_menu}. if (!isset($trees[$tree_cid])) { - $cache = cache_get($tree_cid, 'cache_menu'); + $cache = cache('menu')->get($tree_cid); if ($cache && isset($cache->data)) { $trees[$tree_cid] = $cache->data; } @@ -1378,7 +1378,7 @@ function _menu_build_tree($menu_name, array $parameters = array()) { menu_tree_collect_node_links($data['tree'], $data['node_links']); // Cache the data, if it is not already in the cache. - cache_set($tree_cid, $data, 'cache_menu'); + cache('menu')->set($tree_cid, $data); $trees[$tree_cid] = $data; } @@ -2512,7 +2512,7 @@ function menu_cache_clear($menu_name = 'navigation') { $cache_cleared = &drupal_static(__FUNCTION__, array()); if (empty($cache_cleared[$menu_name])) { - cache_clear_all('links:' . $menu_name . ':', 'cache_menu', TRUE); + cache('menu')->deletePrefix('links:' . $menu_name . ':'); $cache_cleared[$menu_name] = 1; } elseif ($cache_cleared[$menu_name] == 1) { @@ -2529,7 +2529,7 @@ function menu_cache_clear($menu_name = 'navigation') { * might have been made to the router items or menu links. */ function menu_cache_clear_all() { - cache_clear_all('*', 'cache_menu', TRUE); + cache('menu')->flush(); menu_reset_static_cache(); } diff --git a/includes/module.inc b/includes/module.inc index 66c77f57789e..cc3aa8eecb14 100644 --- a/includes/module.inc +++ b/includes/module.inc @@ -129,12 +129,12 @@ function system_list($type) { if (isset($lists['bootstrap'])) { return $lists['bootstrap']; } - if ($cached = cache_get('bootstrap_modules', 'cache_bootstrap')) { + if ($cached = cache('bootstrap')->get('bootstrap_modules')) { $bootstrap_list = $cached->data; } else { $bootstrap_list = db_query("SELECT name, filename FROM {system} WHERE status = 1 AND bootstrap = 1 AND type = 'module' ORDER BY weight ASC, name ASC")->fetchAllAssoc('name'); - cache_set('bootstrap_modules', $bootstrap_list, 'cache_bootstrap'); + cache('bootstrap')->set('bootstrap_modules', $bootstrap_list); } // To avoid a separate database lookup for the filepath, prime the // drupal_get_filename() static cache for bootstrap modules only. @@ -148,7 +148,7 @@ function system_list($type) { } // Otherwise build the list for enabled modules and themes. elseif (!isset($lists['module_enabled'])) { - if ($cached = cache_get('system_list', 'cache_bootstrap')) { + if ($cached = cache('bootstrap')->get('system_list')) { $lists = $cached->data; } else { @@ -178,7 +178,7 @@ function system_list($type) { $lists['filepaths'][] = array('type' => $record->type, 'name' => $record->name, 'filepath' => $record->filename); } } - cache_set('system_list', $lists, 'cache_bootstrap'); + cache('bootstrap')->set('system_list', $lists); } // To avoid a separate database lookup for the filepath, prime the // drupal_get_filename() static cache with all enabled modules and themes. @@ -197,8 +197,7 @@ function system_list_reset() { drupal_static_reset('system_list'); drupal_static_reset('system_rebuild_module_data'); drupal_static_reset('list_themes'); - cache_clear_all('bootstrap_modules', 'cache_bootstrap'); - cache_clear_all('system_list', 'cache_bootstrap'); + cache('bootstrap')->deleteMultiple(array('bootstrap_modules', 'system_list')); } /** @@ -648,16 +647,16 @@ function module_implements($hook, $sort = FALSE, $reset = FALSE) { // per request. if ($reset) { $implementations = array(); - cache_set('module_implements', array(), 'cache_bootstrap'); + cache('bootstrap')->set('module_implements', array()); drupal_static_reset('module_hook_info'); drupal_static_reset('drupal_alter'); - cache_clear_all('hook_info', 'cache_bootstrap'); + cache('bootstrap')->delete('hook_info'); return; } // Fetch implementations from cache. if (empty($implementations)) { - $implementations = cache_get('module_implements', 'cache_bootstrap'); + $implementations = cache('bootstrap')->get('module_implements'); if ($implementations === FALSE) { $implementations = array(); } @@ -726,7 +725,7 @@ function module_hook_info() { if (!isset($hook_info)) { $hook_info = array(); - $cache = cache_get('hook_info', 'cache_bootstrap'); + $cache = cache('bootstrap')->get('hook_info'); if ($cache === FALSE) { // Rebuild the cache and save it. // We can't use module_invoke_all() here or it would cause an infinite @@ -747,7 +746,7 @@ function module_hook_info() { $function($hook_info); } } - cache_set('hook_info', $hook_info, 'cache_bootstrap'); + cache('bootstrap')->set('hook_info', $hook_info); } else { $hook_info = $cache->data; @@ -769,7 +768,7 @@ function module_implements_write_cache() { // optimized as tightly, and not doing so keeps the cache entry smaller. if (isset($implementations['#write_cache']) && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD')) { unset($implementations['#write_cache']); - cache_set('module_implements', $implementations, 'cache_bootstrap'); + cache('bootstrap')->set('module_implements', $implementations); } } diff --git a/includes/path.inc b/includes/path.inc index db605370cd49..630b34c4ce08 100644 --- a/includes/path.inc +++ b/includes/path.inc @@ -90,7 +90,7 @@ function drupal_lookup_path($action, $path = '', $path_language = NULL) { $cache['map'][$path_language] = array(); // Load system paths from cache. $cid = current_path(); - if ($cached = cache_get($cid, 'cache_path')) { + if ($cached = cache('path')->get($cid)) { $cache['system_paths'] = $cached->data; // Now fetch the aliases corresponding to these system paths. $args = array( @@ -212,7 +212,7 @@ function drupal_cache_system_paths() { if ($paths = current($cache['map'])) { $data = array_keys($paths); $expire = REQUEST_TIME + (60 * 60 * 24); - cache_set($cid, $data, 'cache_path', $expire); + cache('path')->set($cid, $data, $expire); } } } diff --git a/includes/registry.inc b/includes/registry.inc index 3fb14fb31b7f..38bcd166c8de 100644 --- a/includes/registry.inc +++ b/includes/registry.inc @@ -83,7 +83,7 @@ function _registry_update() { $unchanged_resources = array(); $lookup_cache = array(); - if ($cache = cache_get('lookup_cache', 'cache_bootstrap')) { + if ($cache = cache('bootstrap')->get('lookup_cache')) { $lookup_cache = $cache->data; } foreach ($lookup_cache as $key => $file) { @@ -105,7 +105,7 @@ function _registry_update() { // We have some unchanged resources, warm up the cache - no need to pay // for looking them up again. if (count($unchanged_resources) > 0) { - cache_set('lookup_cache', $unchanged_resources, 'cache_bootstrap'); + cache('bootstrap')->set('lookup_cache', $unchanged_resources); } } diff --git a/includes/theme.inc b/includes/theme.inc index 6c2b6406903a..3287073f380d 100644 --- a/includes/theme.inc +++ b/includes/theme.inc @@ -280,7 +280,7 @@ function _theme_registry_callback($callback = NULL, array $arguments = array()) */ function _theme_load_registry($theme, $base_theme = NULL, $theme_engine = NULL) { // Check the theme registry cache; if it exists, use it. - $cache = cache_get("theme_registry:$theme->name", 'cache'); + $cache = cache()->get("theme_registry:$theme->name"); if (isset($cache->data)) { $registry = $cache->data; } @@ -300,7 +300,7 @@ function _theme_load_registry($theme, $base_theme = NULL, $theme_engine = NULL) * Write the theme_registry cache into the database. */ function _theme_save_registry($theme, $registry) { - cache_set("theme_registry:$theme->name", $registry); + cache()->set("theme_registry:$theme->name", $registry); } /** @@ -309,7 +309,7 @@ function _theme_save_registry($theme, $registry) { * to add more theme hooks. */ function drupal_theme_rebuild() { - cache_clear_all('theme_registry', 'cache', TRUE); + cache()->deletePrefix('theme_registry'); } /** diff --git a/modules/block/block.module b/modules/block/block.module index 40fc6462e9c2..93c34ae4321c 100644 --- a/modules/block/block.module +++ b/modules/block/block.module @@ -827,7 +827,7 @@ function _block_render_blocks($region_blocks) { // with node_access modules. We also preserve the submission of forms in // blocks, by fetching from cache only if the request method is 'GET' // (or 'HEAD'). - if (!count(module_implements('node_grants')) && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD') && ($cid = _block_get_cache_id($block)) && ($cache = cache_get($cid, 'cache_block'))) { + if (!count(module_implements('node_grants')) && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD') && ($cid = _block_get_cache_id($block)) && ($cache = cache('block')->get($cid))) { $array = $cache->data; } else { @@ -838,7 +838,7 @@ function _block_render_blocks($region_blocks) { drupal_alter(array('block_view', "block_view_{$block->module}_{$block->delta}"), $array, $block); if (isset($cid)) { - cache_set($cid, $array, 'cache_block', CACHE_TEMPORARY); + cache('block')->set($cid, $array, CACHE_TEMPORARY); } } diff --git a/modules/book/book.module b/modules/book/book.module index beb17214c9fa..6e74d3297efc 100644 --- a/modules/book/book.module +++ b/modules/book/book.module @@ -1262,12 +1262,12 @@ function book_menu_subtree_data($link) { $cid = 'links:' . $link['menu_name'] . ':subtree-cid:' . $link['mlid']; if (!isset($tree[$cid])) { - $cache = cache_get($cid, 'cache_menu'); + $cache = cache('menu')->get($cid); if ($cache && isset($cache->data)) { // If the cache entry exists, it will just be the cid for the actual data. // This avoids duplication of large amounts of data. - $cache = cache_get($cache->data, 'cache_menu'); + $cache = cache('menu')->get($cache->data); if ($cache && isset($cache->data)) { $data = $cache->data; @@ -1300,11 +1300,11 @@ function book_menu_subtree_data($link) { $tree_cid = 'links:' . $item['menu_name'] . ':subtree-data:' . hash('sha256', serialize($data)); // Cache the data, if it is not already in the cache. - if (!cache_get($tree_cid, 'cache_menu')) { - cache_set($tree_cid, $data, 'cache_menu'); + if (!cache('menu')->get($tree_cid)) { + cache('menu')->set($tree_cid, $data); } // Cache the cid of the (shared) data using the menu and item-specific cid. - cache_set($cid, $tree_cid, 'cache_menu'); + cache('menu')->set($cid, $tree_cid); } // Check access for the current user to each item in the tree. menu_tree_check_access($data['tree'], $data['node_links']); diff --git a/modules/field/field.attach.inc b/modules/field/field.attach.inc index 2419201dea79..c643e4016628 100644 --- a/modules/field/field.attach.inc +++ b/modules/field/field.attach.inc @@ -710,7 +710,7 @@ function field_attach_load($entity_type, $entities, $age = FIELD_LOAD_CURRENT, $ $data[$instance['field_name']] = $queried_entities[$id]->{$instance['field_name']}; } $cid = "field:$entity_type:$id"; - cache_set($cid, $data, 'cache_field'); + cache('field')->set($cid, $data); } } } @@ -984,7 +984,7 @@ function field_attach_update($entity_type, $entity) { $entity_info = entity_get_info($entity_type); if ($entity_info['field cache']) { - cache_clear_all("field:$entity_type:$id", 'cache_field'); + cache('field')->delete("field:$entity_type:$id"); } } @@ -1021,7 +1021,7 @@ function field_attach_delete($entity_type, $entity) { $entity_info = entity_get_info($entity_type); if ($entity_info['field cache']) { - cache_clear_all("field:$entity_type:$id", 'cache_field'); + cache('field')->delete("field:$entity_type:$id"); } } diff --git a/modules/field/field.info.inc b/modules/field/field.info.inc index 6b172dd3417b..cc8dac5d13fc 100644 --- a/modules/field/field.info.inc +++ b/modules/field/field.info.inc @@ -76,12 +76,12 @@ function _field_info_collate_types($reset = FALSE) { if ($reset) { $info = NULL; // Clear all languages. - cache_clear_all('field_info_types:', 'cache_field', TRUE); + cache('field')->deletePrefix('field_info_types:'); return; } if (!isset($info)) { - if ($cached = cache_get("field_info_types:$langcode", 'cache_field')) { + if ($cached = cache('field')->get("field_info_types:$langcode")) { $info = $cached->data; } else { @@ -149,7 +149,7 @@ function _field_info_collate_types($reset = FALSE) { } drupal_alter('field_storage_info', $info['storage types']); - cache_set("field_info_types:$langcode", $info, 'cache_field'); + cache('field')->set("field_info_types:$langcode", $info); } } @@ -181,12 +181,12 @@ function _field_info_collate_fields($reset = FALSE) { if ($reset) { $info = NULL; - cache_clear_all('field_info_fields', 'cache_field'); + cache('field')->delete('field_info_fields'); return; } if (!isset($info)) { - if ($cached = cache_get('field_info_fields', 'cache_field')) { + if ($cached = cache('field')->get('field_info_fields')) { $info = $cached->data; } else { @@ -238,7 +238,7 @@ function _field_info_collate_fields($reset = FALSE) { } } - cache_set('field_info_fields', $info, 'cache_field'); + cache('field')->set('field_info_fields', $info); } } diff --git a/modules/field/field.module b/modules/field/field.module index 30166bdeecfd..9683c02b691c 100644 --- a/modules/field/field.module +++ b/modules/field/field.module @@ -772,7 +772,7 @@ function _field_extra_fields_pre_render($elements) { * Clear the field info and field data caches. */ function field_cache_clear() { - cache_clear_all('*', 'cache_field', TRUE); + cache('field')->flush(); field_info_cache_clear(); } diff --git a/modules/field/tests/field.test b/modules/field/tests/field.test index 669fc37cf441..b361637146e9 100644 --- a/modules/field/tests/field.test +++ b/modules/field/tests/field.test @@ -824,18 +824,18 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase { $cid = "field:$entity_type:{$entity_init->ftid}"; // Check that no initial cache entry is present. - $this->assertFalse(cache_get($cid, 'cache_field'), t('Non-cached: no initial cache entry')); + $this->assertFalse(cache('field')->get($cid), t('Non-cached: no initial cache entry')); // Save, and check that no cache entry is present. $entity = clone($entity_init); $entity->{$this->field_name}[$langcode] = $values; field_attach_insert($entity_type, $entity); - $this->assertFalse(cache_get($cid, 'cache_field'), t('Non-cached: no cache entry on insert')); + $this->assertFalse(cache('field')->get($cid), t('Non-cached: no cache entry on insert')); // Load, and check that no cache entry is present. $entity = clone($entity_init); field_attach_load($entity_type, array($entity->ftid => $entity)); - $this->assertFalse(cache_get($cid, 'cache_field'), t('Non-cached: no cache entry on load')); + $this->assertFalse(cache('field')->get($cid), t('Non-cached: no cache entry on load')); // Cacheable entity type. @@ -846,24 +846,24 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase { field_create_instance($instance); // Check that no initial cache entry is present. - $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no initial cache entry')); + $this->assertFalse(cache('field')->get($cid), t('Cached: no initial cache entry')); // Save, and check that no cache entry is present. $entity = clone($entity_init); $entity->{$this->field_name}[$langcode] = $values; field_attach_insert($entity_type, $entity); - $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no cache entry on insert')); + $this->assertFalse(cache('field')->get($cid), t('Cached: no cache entry on insert')); // Load a single field, and check that no cache entry is present. $entity = clone($entity_init); field_attach_load($entity_type, array($entity->ftid => $entity), FIELD_LOAD_CURRENT, array('field_id' => $this->field_id)); - $cache = cache_get($cid, 'cache_field'); - $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no cache entry on loading a single field')); + $cache = cache('field')->get($cid); + $this->assertFalse(cache('field')->get($cid), t('Cached: no cache entry on loading a single field')); // Load, and check that a cache entry is present with the expected values. $entity = clone($entity_init); field_attach_load($entity_type, array($entity->ftid => $entity)); - $cache = cache_get($cid, 'cache_field'); + $cache = cache('field')->get($cid); $this->assertEqual($cache->data[$this->field_name][$langcode], $values, t('Cached: correct cache entry on load')); // Update with different values, and check that the cache entry is wiped. @@ -871,12 +871,12 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase { $entity = clone($entity_init); $entity->{$this->field_name}[$langcode] = $values; field_attach_update($entity_type, $entity); - $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no cache entry on update')); + $this->assertFalse(cache('field')->get($cid), t('Cached: no cache entry on update')); // Load, and check that a cache entry is present with the expected values. $entity = clone($entity_init); field_attach_load($entity_type, array($entity->ftid => $entity)); - $cache = cache_get($cid, 'cache_field'); + $cache = cache('field')->get($cid); $this->assertEqual($cache->data[$this->field_name][$langcode], $values, t('Cached: correct cache entry on load')); // Create a new revision, and check that the cache entry is wiped. @@ -885,18 +885,18 @@ class FieldAttachOtherTestCase extends FieldAttachTestCase { $entity = clone($entity_init); $entity->{$this->field_name}[$langcode] = $values; field_attach_update($entity_type, $entity); - $cache = cache_get($cid, 'cache_field'); - $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no cache entry on new revision creation')); + $cache = cache('field')->get($cid); + $this->assertFalse(cache('field')->get($cid), t('Cached: no cache entry on new revision creation')); // Load, and check that a cache entry is present with the expected values. $entity = clone($entity_init); field_attach_load($entity_type, array($entity->ftid => $entity)); - $cache = cache_get($cid, 'cache_field'); + $cache = cache('field')->get($cid); $this->assertEqual($cache->data[$this->field_name][$langcode], $values, t('Cached: correct cache entry on load')); // Delete, and check that the cache entry is wiped. field_attach_delete($entity_type, $entity); - $this->assertFalse(cache_get($cid, 'cache_field'), t('Cached: no cache entry after delete')); + $this->assertFalse(cache('field')->get($cid), t('Cached: no cache entry after delete')); } /** diff --git a/modules/filter/filter.module b/modules/filter/filter.module index 9e1481207cdc..a3f787e6a856 100644 --- a/modules/filter/filter.module +++ b/modules/filter/filter.module @@ -260,7 +260,7 @@ function filter_format_save($format) { $return = SAVED_UPDATED; // Clear the filter cache whenever a text format is updated. - cache_clear_all($format->format . ':', 'cache_filter', TRUE); + cache('filter')->deletePrefix($format->format . ':'); } filter_formats_reset(); @@ -290,7 +290,7 @@ function filter_format_disable($format) { // Clear the filter cache whenever a text format is disabled. filter_formats_reset(); - cache_clear_all($format->format . ':', 'cache_filter', TRUE); + cache('filter')->deletePrefix($format->format . ':'); } /** @@ -395,7 +395,7 @@ function filter_formats($account = NULL) { // All available formats are cached for performance. if (!isset($formats['all'])) { - if ($cache = cache_get("filter_formats:{$language->language}")) { + if ($cache = cache()->get("filter_formats:{$language->language}")) { $formats['all'] = $cache->data; } else { @@ -407,7 +407,7 @@ function filter_formats($account = NULL) { ->execute() ->fetchAllAssoc('format'); - cache_set("filter_formats:{$language->language}", $formats['all']); + cache()->set("filter_formats:{$language->language}", $formats['all']); } } @@ -430,8 +430,8 @@ function filter_formats($account = NULL) { * @see filter_formats() */ function filter_formats_reset() { - cache_clear_all('filter_formats', 'cache', TRUE); - cache_clear_all('filter_list_format', 'cache', TRUE); + cache()->deletePrefix('filter_formats'); + cache()->deletePrefix('filter_list_format'); drupal_static_reset('filter_list_format'); drupal_static_reset('filter_formats'); } @@ -657,7 +657,7 @@ function filter_list_format($format_id) { $filter_info = filter_get_filters(); if (!isset($filters['all'])) { - if ($cache = cache_get('filter_list_format')) { + if ($cache = cache()->get('filter_list_format')) { $filters['all'] = $cache->data; } else { @@ -665,7 +665,7 @@ function filter_list_format($format_id) { foreach ($result as $record) { $filters['all'][$record->format][$record->name] = $record; } - cache_set('filter_list_format', $filters['all']); + cache()->set('filter_list_format', $filters['all']); } } @@ -731,7 +731,7 @@ function check_markup($text, $format_id = NULL, $langcode = '', $cache = FALSE) $cache_id = ''; if ($cache) { $cache_id = $format->format . ':' . $langcode . ':' . hash('sha256', $text); - if ($cached = cache_get($cache_id, 'cache_filter')) { + if ($cached = cache('filter')->get($cache_id)) { return $cached->data; } } @@ -765,7 +765,7 @@ function check_markup($text, $format_id = NULL, $langcode = '', $cache = FALSE) // automatically flushed when the text format is updated. // @see filter_format_save() if ($cache) { - cache_set($cache_id, $text, 'cache_filter'); + cache('filter')->set($cache_id, $text); } return $text; diff --git a/modules/image/image.api.php b/modules/image/image.api.php index acb3f9c1983f..52490c3eb747 100644 --- a/modules/image/image.api.php +++ b/modules/image/image.api.php @@ -110,7 +110,7 @@ function hook_image_style_delete($style) { */ function hook_image_style_flush($style) { // Empty cached data that contains information about the style. - cache_clear_all('*', 'cache_mymodule', TRUE); + cache('mymodule')->flush(); } /** diff --git a/modules/image/image.module b/modules/image/image.module index 008a36513a94..1f4207374038 100644 --- a/modules/image/image.module +++ b/modules/image/image.module @@ -492,7 +492,7 @@ function image_styles() { // Grab from cache or build the array. if (!isset($styles)) { - if ($cache = cache_get('image_styles', 'cache')) { + if ($cache = cache()->get('image_styles')) { $styles = $cache->data; } else { @@ -534,7 +534,7 @@ function image_styles() { } drupal_alter('image_styles', $styles); - cache_set('image_styles', $styles); + cache()->set('image_styles', $styles); } } @@ -827,8 +827,8 @@ function image_style_flush($style) { module_invoke_all('image_style_flush', $style); // Clear image style and effect caches. - cache_clear_all('image_styles', 'cache'); - cache_clear_all('image_effects:', 'cache', TRUE); + cache()->delete('image_styles'); + cache()->deletePrefix('image_effects:'); drupal_static_reset('image_styles'); drupal_static_reset('image_effects'); @@ -838,9 +838,9 @@ function image_style_flush($style) { // Clear page caches when flushing. if (module_exists('block')) { - cache_clear_all('*', 'cache_block', TRUE); + cache('block')->flush(); } - cache_clear_all('*', 'cache_page', TRUE); + cache('page')->flush(); } /** @@ -952,7 +952,7 @@ function image_effect_definitions() { $effects = &drupal_static(__FUNCTION__); if (!isset($effects)) { - if ($cache = cache_get("image_effects:$langcode") && !empty($cache->data)) { + if ($cache = cache()->get("image_effects:$langcode") && !empty($cache->data)) { $effects = $cache->data; } else { @@ -969,7 +969,7 @@ function image_effect_definitions() { } uasort($effects, '_image_effect_definitions_sort'); drupal_alter('image_effect_info', $effects); - cache_set("image_effects:$langcode", $effects); + cache()->set("image_effects:$langcode", $effects); } } diff --git a/modules/locale/locale.admin.inc b/modules/locale/locale.admin.inc index ecf9f1f607f1..cd2edf5bd1a5 100644 --- a/modules/locale/locale.admin.inc +++ b/modules/locale/locale.admin.inc @@ -168,7 +168,7 @@ function locale_languages_overview_form_submit($form, &$form_state) { drupal_set_message(t('Configuration saved.')); // Changing the language settings impacts the interface. - cache_clear_all('*', 'cache_page', TRUE); + cache('page')->flush(); module_invoke_all('multilingual_settings_changed'); $form_state['redirect'] = 'admin/config/regional/language'; diff --git a/modules/locale/locale.module b/modules/locale/locale.module index ba66999e146c..4a15ed990b0f 100644 --- a/modules/locale/locale.module +++ b/modules/locale/locale.module @@ -638,7 +638,7 @@ function locale($string = NULL, $context = NULL, $langcode = NULL) { // perspective that is a really bad idea, so we have no user // interface for this. Be careful when turning this option off! if (variable_get('locale_cache_strings', 1) == 1) { - if ($cache = cache_get('locale:' . $langcode, 'cache')) { + if ($cache = cache()->get('locale:' . $langcode)) { $locale_t[$langcode] = $cache->data; } elseif (lock_acquire('locale_cache_' . $langcode)) { @@ -649,7 +649,7 @@ function locale($string = NULL, $context = NULL, $langcode = NULL) { foreach ($result as $data) { $locale_t[$langcode][$data->context][$data->source] = (empty($data->translation) ? TRUE : $data->translation); } - cache_set('locale:' . $langcode, $locale_t[$langcode]); + cache()->set('locale:' . $langcode, $locale_t[$langcode]); lock_release('locale_cache_' . $langcode); } } @@ -677,7 +677,7 @@ function locale($string = NULL, $context = NULL, $langcode = NULL) { ->fields(array('version' => VERSION)) ->condition('lid', $translation->lid) ->execute(); - cache_clear_all('locale:', 'cache', TRUE); + cache()->deletePrefix('locale:'); } } else { @@ -692,7 +692,7 @@ function locale($string = NULL, $context = NULL, $langcode = NULL) { ->execute(); $locale_t[$langcode][$context][$string] = TRUE; // Clear locale cache so this string can be added in a later request. - cache_clear_all('locale:', 'cache', TRUE); + cache()->deletePrefix('locale:'); } } @@ -811,10 +811,10 @@ function locale_language_delete($langcode) { _locale_invalidate_js($language->language); // Changing the language settings impacts the interface: - cache_clear_all('*', 'cache_page', TRUE); + cache('page')->flush(); // Clearing all locale cache from database - cache_clear_all('locale:' . $language->language, 'cache'); + cache()->delete('locale:' . $language->language); $variables = array('%locale' => $language->name); watchdog('locale', 'The language %locale has been removed.', $variables); diff --git a/modules/locale/locale.pages.inc b/modules/locale/locale.pages.inc index 45816b905611..3e6590efe59b 100644 --- a/modules/locale/locale.pages.inc +++ b/modules/locale/locale.pages.inc @@ -410,7 +410,7 @@ function locale_translate_edit_form_submit($form, &$form_state) { // Clear locale cache. _locale_invalidate_js(); - cache_clear_all('locale:', 'cache', TRUE); + cache()->deletePrefix('locale:'); $form_state['redirect'] = 'admin/config/regional/translate/translate'; return; @@ -450,7 +450,7 @@ function locale_translate_delete_form_submit($form, &$form_state) { ->execute(); // Force JavaScript translation file recreation for all languages. _locale_invalidate_js(); - cache_clear_all('locale:', 'cache', TRUE); + cache()->deletePrefix('locale:'); drupal_set_message(t('The string has been removed.')); $form_state['redirect'] = 'admin/config/regional/translate/translate'; } diff --git a/modules/menu/menu.module b/modules/menu/menu.module index 254079700c9e..fa5837311268 100644 --- a/modules/menu/menu.module +++ b/modules/menu/menu.module @@ -230,12 +230,12 @@ function menu_load($menu_name) { function menu_load_all() { $custom_menus = &drupal_static(__FUNCTION__); if (!isset($custom_menus)) { - if ($cached = cache_get('menu_custom', 'cache_menu')) { + if ($cached = cache('menu')->get('menu_custom')) { $custom_menus = $cached->data; } else { $custom_menus = db_query('SELECT * FROM {menu_custom}')->fetchAllAssoc('menu_name', PDO::FETCH_ASSOC); - cache_set('menu_custom', $custom_menus, 'cache_menu'); + cache('menu')->set('menu_custom', $custom_menus); } } return $custom_menus; diff --git a/modules/node/node.module b/modules/node/node.module index 13b8e4d60b57..92c679538d44 100644 --- a/modules/node/node.module +++ b/modules/node/node.module @@ -675,7 +675,7 @@ function _node_types_build($rebuild = FALSE) { if (isset($_node_types)) { return $_node_types; } - if ($cache = cache_get($cid)) { + if ($cache = cache()->get($cid)) { $_node_types = $cache->data; return $_node_types; } @@ -737,7 +737,7 @@ function _node_types_build($rebuild = FALSE) { asort($_node_types->names); - cache_set($cid, $_node_types); + cache()->set($cid, $_node_types); return $_node_types; } @@ -746,7 +746,7 @@ function _node_types_build($rebuild = FALSE) { * Clears the node type cache. */ function node_type_cache_reset() { - cache_clear_all('node_types:', 'cache', TRUE); + cache()->deletePrefix('node_types:'); drupal_static_reset('_node_types_build'); } diff --git a/modules/path/path.test b/modules/path/path.test index 9e501782b4cd..241f5325dc2e 100644 --- a/modules/path/path.test +++ b/modules/path/path.test @@ -37,14 +37,14 @@ class PathTestCase extends DrupalWebTestCase { // Visit the system path for the node and confirm a cache entry is // created. - cache_clear_all('*', 'cache_path', TRUE); + cache('path')->flush(); $this->drupalGet($edit['source']); - $this->assertTrue(cache_get($edit['source'], 'cache_path'), t('Cache entry was created.')); + $this->assertTrue(cache('path')->get($edit['source']), t('Cache entry was created.')); // Visit the alias for the node and confirm a cache entry is created. - cache_clear_all('*', 'cache_path', TRUE); + cache('path')->flush(); $this->drupalGet($edit['alias']); - $this->assertTrue(cache_get($edit['source'], 'cache_path'), t('Cache entry was created.')); + $this->assertTrue(cache('path')->get($edit['source']), t('Cache entry was created.')); } /** diff --git a/modules/simpletest/drupal_web_test_case.php b/modules/simpletest/drupal_web_test_case.php index b2a7bf1d3433..3ce06d55fd6f 100644 --- a/modules/simpletest/drupal_web_test_case.php +++ b/modules/simpletest/drupal_web_test_case.php @@ -1435,7 +1435,7 @@ class DrupalWebTestCase extends DrupalTestCase { */ protected function refreshVariables() { global $conf; - cache_clear_all('variables', 'cache_bootstrap'); + cache('bootstrap')->delete('variables'); $conf = variable_initialize(); } diff --git a/modules/simpletest/simpletest.module b/modules/simpletest/simpletest.module index ede9ac6ec760..80a6bc531838 100644 --- a/modules/simpletest/simpletest.module +++ b/modules/simpletest/simpletest.module @@ -311,7 +311,7 @@ function simpletest_test_get_all() { if (!$groups) { // Load test information from cache if available, otherwise retrieve the // information from each tests getInfo() method. - if ($cache = cache_get('simpletest', 'cache')) { + if ($cache = cache()->get('simpletest')) { $groups = $cache->data; } else { @@ -347,7 +347,7 @@ function simpletest_test_get_all() { // Allow modules extending core tests to disable originals. drupal_alter('simpletest', $groups); - cache_set('simpletest', $groups); + cache()->set('simpletest', $groups); } } return $groups; @@ -421,7 +421,7 @@ function simpletest_clean_environment() { // Detect test classes that have been added, renamed or deleted. registry_rebuild(); - cache_clear_all('simpletest', 'cache'); + cache()->delete('simpletest'); } /** diff --git a/modules/simpletest/tests/bootstrap.test b/modules/simpletest/tests/bootstrap.test index 14c16a95b6d0..d1e96623aae5 100644 --- a/modules/simpletest/tests/bootstrap.test +++ b/modules/simpletest/tests/bootstrap.test @@ -306,7 +306,7 @@ class HookBootExitTestCase extends DrupalWebTestCase { // Boot and exit should not fire since the page is cached. variable_set('page_cache_invoke_hooks', FALSE); - $this->assertTrue(cache_get(url('', array('absolute' => TRUE)), 'cache_page'), t('Page has been cached.')); + $this->assertTrue(cache('page')->get(url('', array('absolute' => TRUE))), t('Page has been cached.')); $this->drupalGet(''); $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_boot'))->fetchField(), $calls, t('hook_boot not called with agressive cache and a cached page.')); $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_exit'))->fetchField(), $calls, t('hook_exit not called with agressive cache and a cached page.')); diff --git a/modules/simpletest/tests/cache.test b/modules/simpletest/tests/cache.test index 1235269ccad4..1f3240d50971 100644 --- a/modules/simpletest/tests/cache.test +++ b/modules/simpletest/tests/cache.test @@ -314,16 +314,16 @@ class CacheClearCase extends CacheTestCase { $bins = array('cache', 'filter', 'page', 'bootstrap', 'path'); $bins = array_merge(module_invoke_all('flush_caches'), $bins); foreach ($bins as $id => $bin) { - $id = 'test_cid_clear' . $id; - cache_set($id, $this->default_value, $bin); + $cid = 'test_cid_clear' . $id; + cache($bin)->set($cid, $this->default_value); } // Remove all caches then make sure that they are cleared. drupal_flush_all_caches(); foreach ($bins as $id => $bin) { - $id = 'test_cid_clear' . $id; - $this->assertFalse($this->checkCacheExists($id, $this->default_value, $bin), t('All cache entries removed from @bin.', array('@bin' => $bin))); + $cid = 'test_cid_clear' . $id; + $this->assertFalse($this->checkCacheExists($cid, $this->default_value, $bin), t('All cache entries removed from @bin.', array('@bin' => $bin))); } } } diff --git a/modules/simpletest/tests/module.test b/modules/simpletest/tests/module.test index f55c08af4c97..c9601c9b9ab7 100644 --- a/modules/simpletest/tests/module.test +++ b/modules/simpletest/tests/module.test @@ -86,17 +86,17 @@ class ModuleUnitTest extends DrupalWebTestCase { */ function testModuleImplements() { // Clear the cache. - cache_clear_all('module_implements', 'cache_bootstrap'); - $this->assertFalse(cache_get('module_implements', 'cache_bootstrap'), t('The module implements cache is empty.')); + cache('bootstrap')->delete('module_implements'); + $this->assertFalse(cache('bootstrap')->get('module_implements'), t('The module implements cache is empty.')); $this->drupalGet(''); - $this->assertTrue(cache_get('module_implements', 'cache_bootstrap'), t('The module implements cache is populated after requesting a page.')); + $this->assertTrue(cache('bootstrap')->get('module_implements'), t('The module implements cache is populated after requesting a page.')); // Test again with an authenticated user. $this->user = $this->drupalCreateUser(); $this->drupalLogin($this->user); - cache_clear_all('module_implements', 'cache_bootstrap'); + cache('bootstrap')->delete('module_implements'); $this->drupalGet(''); - $this->assertTrue(cache_get('module_implements', 'cache_bootstrap'), t('The module implements cache is populated after requesting a page.')); + $this->assertTrue(cache('bootstrap')->get('module_implements'), t('The module implements cache is populated after requesting a page.')); // Make sure group include files are detected properly even when the file is // already loaded when the cache is rebuilt. diff --git a/modules/simpletest/tests/upgrade/upgrade.test b/modules/simpletest/tests/upgrade/upgrade.test index 1ef8525cbbcb..9326f2561709 100644 --- a/modules/simpletest/tests/upgrade/upgrade.test +++ b/modules/simpletest/tests/upgrade/upgrade.test @@ -204,8 +204,8 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase { ->execute(); try { - cache_clear_all('variables', 'cache'); - cache_clear_all('variables', 'cache_bootstrap'); + cache()->delete('variables'); + cache('bootstrap')->delete('variables'); } // Since cache_bootstrap won't exist in a Drupal 6 site, ignore the // exception if the above fails. diff --git a/modules/system/system.admin.inc b/modules/system/system.admin.inc index baac6f71ff27..6824f19d792b 100644 --- a/modules/system/system.admin.inc +++ b/modules/system/system.admin.inc @@ -1734,7 +1734,7 @@ function system_clear_cache_submit($form, &$form_state) { * @ingroup forms */ function system_clear_page_cache_submit($form, &$form_state) { - cache_clear_all('*', 'cache_page', TRUE); + cache('page')->flush(); } /** diff --git a/modules/system/system.module b/modules/system/system.module index 3737af1bc0f8..cfab0a98288c 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -3007,7 +3007,7 @@ function system_cron() { $core = array('cache', 'cache_path', 'cache_filter', 'cache_page', 'cache_form', 'cache_menu'); $cache_tables = array_merge(module_invoke_all('flush_caches'), $core); foreach ($cache_tables as $table) { - cache_clear_all(NULL, $table); + cache($table)->expire(); } // Cleanup the batch table and the queue for failed batches. diff --git a/update.php b/update.php index 105bc9d498b3..b7594366b3db 100644 --- a/update.php +++ b/update.php @@ -231,7 +231,7 @@ function update_info_page() { _drupal_flush_css_js(); // Flush the cache of all data for the update status module. if (db_table_exists('cache_update')) { - cache_clear_all('*', 'cache_update', TRUE); + cache('update')->flush(); } update_task_list('info'); From 168c1364ab4a122126e1152e4be3761449c45f24 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Sun, 11 Sep 2011 12:16:15 -0400 Subject: [PATCH 30/80] - Patch #1269178 by catch: add catch as cache system co-maintainer. --- MAINTAINERS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS.txt b/MAINTAINERS.txt index 20463808dc29..a54b5215f810 100644 --- a/MAINTAINERS.txt +++ b/MAINTAINERS.txt @@ -32,6 +32,7 @@ Batch system Cache system - Damien Tournoud 'DamZ' +- Nathaniel Catchpole 'catch' Cron system - Károly Négyesi 'chx' From eeac53e45f99b51f4826703dab59c35fa4d7aab6 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Sun, 11 Sep 2011 13:00:50 -0400 Subject: [PATCH 31/80] - Patch #1266694 by Damien Tournoud: Fixed DatabaseStatementPrefetch doesn't implement fetchColumn(). --- includes/database/prefetch.inc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/includes/database/prefetch.inc b/includes/database/prefetch.inc index f378d35559ef..4f2b19d1f3d1 100644 --- a/includes/database/prefetch.inc +++ b/includes/database/prefetch.inc @@ -370,7 +370,7 @@ class DatabaseStatementPrefetch implements Iterator, DatabaseStatementInterface } } - public function fetchField($index = 0) { + public function fetchColumn($index = 0) { if (isset($this->currentRow) && isset($this->columnNames[$index])) { // We grab the value directly from $this->data, and format it. $return = $this->currentRow[$this->columnNames[$index]]; @@ -382,6 +382,10 @@ class DatabaseStatementPrefetch implements Iterator, DatabaseStatementInterface } } + public function fetchField($index = 0) { + return $this->fetchColumn($index); + } + public function fetchObject($class_name = NULL, $constructor_args = array()) { if (isset($this->currentRow)) { if (!isset($class_name)) { From 5b98347d00bdd0895e630a4e5ea347b197aacb78 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Sun, 11 Sep 2011 15:40:17 -0400 Subject: [PATCH 32/80] - Patch #160163 by c960657, Pancho, danillonunes: login with OpenID link has mis-encoding of #. --- modules/openid/openid.js | 4 ++-- modules/openid/openid.module | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/openid/openid.js b/modules/openid/openid.js index 1f2042746892..fdc97fa06be3 100644 --- a/modules/openid/openid.js +++ b/modules/openid/openid.js @@ -11,10 +11,10 @@ Drupal.behaviors.openid = { if (cookie) { $('#edit-openid-identifier').val(cookie); } - if ($('#edit-openid-identifier').val()) { + if ($('#edit-openid-identifier').val() || location.hash == '#openid-login') { $('#edit-openid-identifier').addClass('openid-processed'); loginElements.hide(); - // Use .css('display', 'block') instead of .show() to Konqueror friendly. + // Use .css('display', 'block') instead of .show() to be Konqueror friendly. openidElements.css('display', 'block'); } } diff --git a/modules/openid/openid.module b/modules/openid/openid.module index bb6ad712bebb..2bf891ab69ac 100644 --- a/modules/openid/openid.module +++ b/modules/openid/openid.module @@ -146,11 +146,11 @@ function _openid_user_login_form_alter(&$form, &$form_state) { $items = array(); $items[] = array( - 'data' => l(t('Log in using OpenID'), '#'), + 'data' => l(t('Log in using OpenID'), '#openid-login', array('external' => TRUE)), 'class' => array('openid-link'), ); $items[] = array( - 'data' => l(t('Cancel OpenID login'), '#'), + 'data' => l(t('Cancel OpenID login'), '#', array('external' => TRUE)), 'class' => array('user-link'), ); From a625ce4cab7dac1d9dc7fb96ce64055a9a9409c0 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Sun, 11 Sep 2011 16:07:49 -0400 Subject: [PATCH 33/80] - Patch #240828 by sun, xjm, David_Rothstein, yoroy: 'This form is outdated. Reload the page and try again' is incorrect and confuses users. --- includes/form.inc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/includes/form.inc b/includes/form.inc index cfbea38645b3..decca0037535 100644 --- a/includes/form.inc +++ b/includes/form.inc @@ -1082,8 +1082,12 @@ function drupal_validate_form($form_id, &$form, &$form_state) { // matches the current user's session. if (isset($form['#token'])) { if (!drupal_valid_token($form_state['values']['form_token'], $form['#token'])) { + $path = current_path(); + $query = drupal_get_query_parameters(); + $url = url($path, array('query' => $query)); + // Setting this error will cause the form to fail validation. - form_set_error('form_token', t('This form is outdated. Reload the page and try again. Contact the site administrator if the problem persists.')); + form_set_error('form_token', t('The form has become outdated. Copy any unsaved work in the form below and then reload this page.', array('@link' => $url))); } } From e9565463f31a677a308818b3c5d6268975674ffd Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Sun, 11 Sep 2011 16:59:52 -0400 Subject: [PATCH 34/80] - Patch #1237290 by e-anima, dereine, xjm: menu path length fix. --- modules/menu/menu.admin.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/menu/menu.admin.inc b/modules/menu/menu.admin.inc index cb33cbb85664..91229b497418 100644 --- a/modules/menu/menu.admin.inc +++ b/modules/menu/menu.admin.inc @@ -286,6 +286,7 @@ function menu_edit_item($form, &$form_state, $type, $item, $menu) { $form['link_path'] = array( '#type' => 'textfield', '#title' => t('Path'), + '#maxlength' => 255, '#default_value' => $path, '#description' => t('The path for this menu link. This can be an internal Drupal path such as %add-node or an external URL such as %drupal. Enter %front to link to the front page.', array('%front' => '', '%add-node' => 'node/add', '%drupal' => 'http://drupal.org')), '#required' => TRUE, From 74b78a54f57e357345e9ae6261a8dff5685fc953 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Sun, 11 Sep 2011 17:06:35 -0400 Subject: [PATCH 35/80] - Patch #1164808 by mrfelton: block title field was overlooked. --- modules/block/block.admin.inc | 2 +- modules/block/block.module | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/block/block.admin.inc b/modules/block/block.admin.inc index ba31b9c5b6f4..f58e3739d2a4 100644 --- a/modules/block/block.admin.inc +++ b/modules/block/block.admin.inc @@ -276,7 +276,7 @@ function block_admin_configure($form, &$form_state, $module, $delta) { '#maxlength' => 64, '#description' => $block->module == 'block' ? t('The title of the block as shown to the user.') : t('Override the default title for the block. Use !placeholder to display no title, or leave blank to use the default block title.', array('!placeholder' => '<none>')), '#default_value' => isset($block->title) ? $block->title : '', - '#weight' => -18, + '#weight' => -19, ); // Module-specific block configuration. diff --git a/modules/block/block.module b/modules/block/block.module index 93c34ae4321c..7f9379f30a39 100644 --- a/modules/block/block.module +++ b/modules/block/block.module @@ -500,7 +500,7 @@ function block_custom_block_form($edit = array()) { '#maxlength' => 64, '#description' => t('A brief description of your block. Used on the Blocks administration page.', array('@overview' => url('admin/structure/block'))), '#required' => TRUE, - '#weight' => -19, + '#weight' => -18, ); $form['body_field']['#weight'] = -17; $form['body_field']['body'] = array( From aa46bb2df54aa9c19dcc10988513c80ac473d583 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Sun, 11 Sep 2011 17:09:32 -0400 Subject: [PATCH 36/80] - Patch #897650 by Garrett Albright, Damien Tournoud: DatabaseSchema_sqlite()::findTables() shouldn't find indexes. --- includes/database/sqlite/schema.inc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/includes/database/sqlite/schema.inc b/includes/database/sqlite/schema.inc index 3c8cd3f554a1..c5882f12715d 100644 --- a/includes/database/sqlite/schema.inc +++ b/includes/database/sqlite/schema.inc @@ -672,9 +672,10 @@ class DatabaseSchema_sqlite extends DatabaseSchema { // Don't add the prefix, $table_expression already includes the prefix. $info = $this->getPrefixInfo($table_expression, FALSE); - // Can't use query placeholders because the query would have to be - // :prefixsqlite_master, which does not work. - $result = db_query("SELECT name FROM " . $info['schema'] . ".sqlite_master WHERE name LIKE :table_name", array( + // Can't use query placeholders for the schema because the query would have + // to be :prefixsqlite_master, which does not work. + $result = db_query("SELECT name FROM " . $info['schema'] . ".sqlite_master WHERE type = :type AND name LIKE :table_name", array( + ':type' => 'table', ':table_name' => $info['table'], )); return $result->fetchAllKeyed(0, 0); From 1d3b16fcaec37c5eeb39e3d34af4da168a8db510 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Sun, 11 Sep 2011 17:44:28 -0400 Subject: [PATCH 37/80] =?UTF-8?q?-=20Patch=20#1257638=20by=20michaellenaha?= =?UTF-8?q?n,=20B=C3=A8s:=20hook=5Fmenu()=20example=20should=20be=20change?= =?UTF-8?q?d=20to=20not=20suggest=20admin/config/foo=20as=20path.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/system/system.api.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/modules/system/system.api.php b/modules/system/system.api.php index fc7b73783b18..44c4ab1d6e27 100644 --- a/modules/system/system.api.php +++ b/modules/system/system.api.php @@ -1053,24 +1053,24 @@ function hook_menu_get_item_alter(&$router_item, $path, $original_map) { * MENU_LOCAL_TASK. Example: * @code * // Make "Foo settings" appear on the admin Config page - * $items['admin/config/foo'] = array( + * $items['admin/config/system/foo'] = array( * 'title' => 'Foo settings', * 'type' => MENU_NORMAL_ITEM, * // Page callback, etc. need to be added here. * ); - * // Make "Global settings" the main tab on the "Foo settings" page - * $items['admin/config/foo/global'] = array( - * 'title' => 'Global settings', + * // Make "Tab 1" the main tab on the "Foo settings" page + * $items['admin/config/system/foo/tab1'] = array( + * 'title' => 'Tab 1', * 'type' => MENU_DEFAULT_LOCAL_TASK, * // Access callback, page callback, and theme callback will be inherited - * // from 'admin/config/foo', if not specified here to override. + * // from 'admin/config/system/foo', if not specified here to override. * ); - * // Make an additional tab called "Node settings" on "Foo settings" - * $items['admin/config/foo/node'] = array( - * 'title' => 'Node settings', + * // Make an additional tab called "Tab 2" on "Foo settings" + * $items['admin/config/system/foo/tab2'] = array( + * 'title' => 'Tab 2', * 'type' => MENU_LOCAL_TASK, * // Page callback and theme callback will be inherited from - * // 'admin/config/foo', if not specified here to override. + * // 'admin/config/system/foo', if not specified here to override. * // Need to add access callback or access arguments. * ); * @endcode From 7c5d1bd139fbcdaadce0315034996319717ccb35 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Sun, 11 Sep 2011 17:46:15 -0400 Subject: [PATCH 38/80] =?UTF-8?q?-=20Patch=20#1260162=20by=20michaellenaha?= =?UTF-8?q?n,=20B=C3=A8s:=20hook=5Ffile=5Finsert()=20doc=20has=20no=20func?= =?UTF-8?q?tion=20body.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/system/system.api.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/modules/system/system.api.php b/modules/system/system.api.php index 44c4ab1d6e27..52350f9b9339 100644 --- a/modules/system/system.api.php +++ b/modules/system/system.api.php @@ -2627,17 +2627,21 @@ function hook_file_presave($file) { /** * Respond to a file being added. * - * This hook is called before a file has been added to the database. The hook + * This hook is called after a file has been added to the database. The hook * doesn't distinguish between files created as a result of a copy or those * created by an upload. * * @param $file - * The file that is about to be saved. + * The file that has been added. * * @see file_save() */ function hook_file_insert($file) { - + // Add a message to the log, if the file is a jpg + $validate = file_validate_extensions($file, 'jpg'); + if (empty($validate)) { + watchdog('file', 'A jpg has been added.'); + } } /** From aafb25c2b7b567ee8274756323002c9e3da46c32 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Sun, 11 Sep 2011 17:47:49 -0400 Subject: [PATCH 39/80] - Patch #1024328 by pillarsdotnet, droplet: re-use message-16-error.png icon for dblog severity levels higher than LOG_ERR. --- modules/dblog/dblog.css | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/dblog/dblog.css b/modules/dblog/dblog.css index ff310e3baa20..88f4ba01b8ad 100644 --- a/modules/dblog/dblog.css +++ b/modules/dblog/dblog.css @@ -1,4 +1,3 @@ - .form-item-type, .form-item-severity { float: left; /* LTR */ @@ -52,7 +51,9 @@ table#admin-dblog td.icon { table#admin-dblog tr.dblog-warning td.icon { background-image: url(../../misc/message-16-warning.png); } -table#admin-dblog tr.dblog-error td.icon { +table#admin-dblog tr.dblog-error td.icon, +table#admin-dblog tr.dblog-critical td.icon, +table#admin-dblog tr.dblog-alert td.icon, +table#admin-dblog tr.dblog-emerg td.icon { background-image: url(../../misc/message-16-error.png); } - From 2a881d81859b80a502337d7fab2a32b47a2215a2 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Sun, 11 Sep 2011 18:43:38 -0400 Subject: [PATCH 40/80] - Patch #1185842 by Dave Reid: the [node:author] token does not use format_username(). --- modules/node/node.test | 1 + modules/node/node.tokens.inc | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/node/node.test b/modules/node/node.test index d004684c5b63..5de608171205 100644 --- a/modules/node/node.test +++ b/modules/node/node.test @@ -2261,6 +2261,7 @@ class NodeTokenReplaceTestCase extends DrupalWebTestCase { $tests['[node:language]'] = check_plain($node->language); $tests['[node:url]'] = url('node/' . $node->nid, $url_options); $tests['[node:edit-url]'] = url('node/' . $node->nid . '/edit', $url_options); + $tests['[node:author]'] = check_plain(format_username($account)); $tests['[node:author:uid]'] = $node->uid; $tests['[node:author:name]'] = check_plain(format_username($account)); $tests['[node:created:since]'] = format_interval(REQUEST_TIME - $node->created, 2, $language->language); diff --git a/modules/node/node.tokens.inc b/modules/node/node.tokens.inc index 80dbda517a01..b6bafc639077 100644 --- a/modules/node/node.tokens.inc +++ b/modules/node/node.tokens.inc @@ -157,8 +157,9 @@ function node_tokens($type, $tokens, array $data = array(), array $options = arr // Default values for the chained tokens handled below. case 'author': - $name = ($node->uid == 0) ? variable_get('anonymous', t('Anonymous')) : $node->name; - $replacements[$original] = $sanitize ? filter_xss($name) : $name; + $account = user_load($node->uid); + $name = format_username($account); + $replacements[$original] = $sanitize ? check_plain($name) : $name; break; case 'created': From 2e9e46a3a6185d43e6053869d363d4bd42f6ac1d Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Sun, 11 Sep 2011 20:08:54 -0400 Subject: [PATCH 41/80] - Patch #308865 by cosmicdreams, casey, Rob Loach, tim.plunkett: drop IE6 support in Drupal core. --- misc/tabledrag.js | 10 ----- misc/vertical-tabs.css | 9 +---- modules/color/color.js | 8 ---- modules/overlay/overlay-child.css | 9 ----- modules/overlay/overlay-parent.js | 2 +- modules/system/system.admin.css | 1 - modules/system/system.base.css | 6 +-- modules/toolbar/toolbar-rtl.css | 4 -- modules/toolbar/toolbar.css | 15 ------- themes/bartik/css/ie6.css | 18 --------- themes/bartik/template.php | 1 - themes/garland/fix-ie-rtl.css | 61 ----------------------------- themes/garland/fix-ie.css | 65 ------------------------------- themes/garland/template.php | 2 - themes/seven/ie6.css | 17 -------- themes/seven/reset.css | 7 ---- themes/seven/template.php | 2 - themes/seven/vertical-tabs.css | 4 -- themes/stark/layout.css | 4 +- 19 files changed, 5 insertions(+), 240 deletions(-) delete mode 100644 themes/bartik/css/ie6.css delete mode 100644 themes/garland/fix-ie-rtl.css delete mode 100644 themes/garland/fix-ie.css delete mode 100644 themes/seven/ie6.css diff --git a/misc/tabledrag.js b/misc/tabledrag.js index 3b56e2345c65..b9b5822c8128 100644 --- a/misc/tabledrag.js +++ b/misc/tabledrag.js @@ -301,11 +301,6 @@ Drupal.tableDrag.prototype.makeDraggable = function (item) { $(self.oldRowElement).removeClass('drag-previous'); } - // Hack for IE6 that flickers uncontrollably if select lists are moved. - if (navigator.userAgent.indexOf('MSIE 6.') != -1) { - $('select', this.table).css('display', 'none'); - } - // Hack for Konqueror, prevent the blur handler from firing. // Konqueror always gives links focus, even after returning false on mousedown. self.safeBlur = false; @@ -559,11 +554,6 @@ Drupal.tableDrag.prototype.dropRow = function (event, self) { self.dragObject = null; $('body').removeClass('drag'); clearInterval(self.scrollInterval); - - // Hack for IE6 that flickers uncontrollably if select lists are moved. - if (navigator.userAgent.indexOf('MSIE 6.') != -1) { - $('select', this.table).css('display', 'block'); - } } }; diff --git a/misc/vertical-tabs.css b/misc/vertical-tabs.css index 10e81539176c..505ba1e58e29 100644 --- a/misc/vertical-tabs.css +++ b/misc/vertical-tabs.css @@ -2,15 +2,14 @@ div.vertical-tabs { margin: 1em 0 1em 15em; /* LTR */ border: 1px solid #ccc; - position: relative; /* IE6/7 */ + position: relative; /* IE7 */ } .vertical-tabs ul.vertical-tabs-list { width: 15em; list-style: none; - list-style-image: none; /* IE6 */ + list-style-image: none; /* IE7 */ border-top: 1px solid #ccc; padding: 0; - position: relative; /* IE6 */ margin: -1px 0 -1px -15em; /* LTR */ float: left; /* LTR */ } @@ -71,7 +70,3 @@ div.vertical-tabs { -webkit-box-sizing: border-box; box-sizing: border-box; } -* html .vertical-tabs .form-type-textfield, -* html .vertical-tabs .form-textarea-wrapper { - width: 95%; /* IE6 */ -} diff --git a/modules/color/color.js b/modules/color/color.js index 43099adcabf8..3e53ce115a78 100644 --- a/modules/color/color.js +++ b/modules/color/color.js @@ -43,14 +43,6 @@ Drupal.behaviors.color = { } } - // Fix preview background in IE6. - if (navigator.appVersion.match(/MSIE [0-6]\./)) { - var e = $('#preview #img')[0]; - var image = e.currentStyle.backgroundImage; - e.style.backgroundImage = 'none'; - e.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=crop, src='" + image.substring(5, image.length - 2) + "')"; - } - // Set up colorScheme selector. $('#edit-scheme', form).change(function () { var schemes = settings.color.schemes, colorScheme = this.options[this.selectedIndex].value; diff --git a/modules/overlay/overlay-child.css b/modules/overlay/overlay-child.css index d31952e594d5..f3532159bfcc 100644 --- a/modules/overlay/overlay-child.css +++ b/modules/overlay/overlay-child.css @@ -133,15 +133,6 @@ html.js body { padding-top: 0.9em; } -/** - * IE6 shows elements with position:fixed as position:static so replace - * it with position:absolute; - */ -* html #overlay-close, -* html #overlay-close:hover { - position: absolute; -} - /** * Disable message. */ diff --git a/modules/overlay/overlay-parent.js b/modules/overlay/overlay-parent.js index f9789f14aff1..135320fe603f 100644 --- a/modules/overlay/overlay-parent.js +++ b/modules/overlay/overlay-parent.js @@ -429,7 +429,7 @@ Drupal.overlay.eventhandlerAlterDisplacedElements = function (event) { var documentHeight = this.iframeWindow.document.body.clientHeight; var documentWidth = this.iframeWindow.document.body.clientWidth; // IE6 doesn't support maxWidth, use width instead. - var maxWidthName = (typeof document.body.style.maxWidth == 'string') ? 'maxWidth' : 'width'; + var maxWidthName = 'maxWidth'; if (Drupal.overlay.leftSidedScrollbarOffset === undefined && $(document.documentElement).attr('dir') === 'rtl') { // We can't use element.clientLeft to detect whether scrollbars are placed diff --git a/modules/system/system.admin.css b/modules/system/system.admin.css index 43340b58d3fd..7299484c38d8 100644 --- a/modules/system/system.admin.css +++ b/modules/system/system.admin.css @@ -243,7 +243,6 @@ table.screenshot { .exposed-filters .filters { float: left; /* LTR */ margin-right: 1em; /* LTR */ - width: 25em; /* IE6 */ } .exposed-filters .form-item { margin: 0 0 0.1em 0; diff --git a/modules/system/system.base.css b/modules/system/system.base.css index a6748de42ebd..4ce63cf99955 100644 --- a/modules/system/system.base.css +++ b/modules/system/system.base.css @@ -227,7 +227,7 @@ html.js .js-hide { */ .element-invisible { position: absolute !important; - clip: rect(1px 1px 1px 1px); /* IE6, IE7 */ + clip: rect(1px 1px 1px 1px); /* IE7 */ clip: rect(1px, 1px, 1px, 1px); } @@ -253,10 +253,6 @@ html.js .js-hide { clear: both; visibility: hidden; } -/* IE6 */ -* html .clearfix { - height: 1%; -} /* IE7 */ *:first-child + html .clearfix { min-height: 1%; diff --git a/modules/toolbar/toolbar-rtl.css b/modules/toolbar/toolbar-rtl.css index acbc98f82873..e12154724a70 100644 --- a/modules/toolbar/toolbar-rtl.css +++ b/modules/toolbar/toolbar-rtl.css @@ -35,7 +35,3 @@ left: 10px; right: auto; } -* html #toolbar { - left: 0; - padding-left: 0; -} diff --git a/modules/toolbar/toolbar.css b/modules/toolbar/toolbar.css index cbf3c14c8fe3..4b62cded060e 100644 --- a/modules/toolbar/toolbar.css +++ b/modules/toolbar/toolbar.css @@ -133,18 +133,3 @@ body.toolbar-drawer { position: relative; padding: 0 10px; } - -/** - * IE 6 Fix. - * - * IE 6 shows elements with position:fixed as position:static so we replace - * it with position:absolute; toolbar needs its z-index to stay above overlay. - */ -* html #toolbar { - left: -20px; - margin: 0; - padding-right: 0; - position: absolute; - right: 0; - width: 100%; -} diff --git a/themes/bartik/css/ie6.css b/themes/bartik/css/ie6.css deleted file mode 100644 index 435fab02bd8d..000000000000 --- a/themes/bartik/css/ie6.css +++ /dev/null @@ -1,18 +0,0 @@ - -#content { - overflow: hidden; -} -.form-item-search-block-form { - width: 50%; -} -.tabs ul.primary, -.region-header .block-menu li a, -.comment-form .form-item { - zoom: 1; -} -#block-search-form .form-item-search-block-form input { - width: 67%; -} -.node-teaser { - border-bottom: 1px solid #d3d7d9; -} diff --git a/themes/bartik/template.php b/themes/bartik/template.php index d523a0abab57..8d2b8c4e316c 100644 --- a/themes/bartik/template.php +++ b/themes/bartik/template.php @@ -23,7 +23,6 @@ function bartik_preprocess_html(&$variables) { // Add conditional stylesheets for IE drupal_add_css(path_to_theme() . '/css/ie.css', array('group' => CSS_THEME, 'browsers' => array('IE' => 'lte IE 7', '!IE' => FALSE), 'preprocess' => FALSE)); - drupal_add_css(path_to_theme() . '/css/ie6.css', array('group' => CSS_THEME, 'browsers' => array('IE' => 'IE 6', '!IE' => FALSE), 'preprocess' => FALSE)); } /** diff --git a/themes/garland/fix-ie-rtl.css b/themes/garland/fix-ie-rtl.css deleted file mode 100644 index fb1901b37eba..000000000000 --- a/themes/garland/fix-ie-rtl.css +++ /dev/null @@ -1,61 +0,0 @@ - -body { - /* Center layout */ - text-align: center; -} - -#squeeze { - zoom: 1; - direction: ltr; -} - -#squeeze .left-corner { - direction: rtl -} - -#header-region, -#wrapper #container { - /* Reset text alignment */ - text-align: right; -} - -#wrapper #container #center { - /* Reduce amount of damage done by extremely wide content */ - overflow: hidden; -} - -#wrapper #container #center .right-corner .left-corner { - /* Because of the lack of min-height, we use height as an alternative */ - height: 400px; -} - -fieldset { - /* Don't draw backgrounds on fieldsets in IE, as they look really bad. */ - background: none; -} - -/* Prevent fieldsets from shifting when changing collapsed state. */ -html.js fieldset.collapsible { - top: -1em; -} - -html.js fieldset.collapsed { - top: 0; - margin-bottom: 1em; -} - -tr.menu-disabled { - /* Use filter to emulate CSS3 opacity */ - filter: alpha(opacity=50); -} - -#header-region { - /* Because of the lack of min-height, we use height as an alternative */ - height: 1em; -} - -#attach-hide label, -#uploadprogress div.message { - /* Fading elements in IE causes the text to bleed unless they have a background. */ - background-color: #ffffff; -} diff --git a/themes/garland/fix-ie.css b/themes/garland/fix-ie.css deleted file mode 100644 index 1a1c131bc21f..000000000000 --- a/themes/garland/fix-ie.css +++ /dev/null @@ -1,65 +0,0 @@ - -body { - /* Center layout */ - text-align: center; -} - -#header-region, -#wrapper #container { - /* Reset text alignment */ - text-align: left; /* LTR */ -} - -#wrapper #container #center { - /* Reduce amount of damage done by extremely wide content */ - overflow: hidden; -} - -#wrapper #container #center .right-corner .left-corner { - /* Because of the lack of min-height, we use height as an alternative */ - height: 400px; -} - -fieldset { - /* Don't draw backgrounds on fieldsets in IE, as they look really bad. */ - background: none; -} - -div.vertical-tabs ul.vertical-tabs-list li.first { - background-image: none; -} - -ul.primary { - /* Fix missing top margin */ - position: relative; /* LTR */ -/* top: 0.5em; */ -} - -/* Prevent fieldsets from shifting when changing collapsed state. */ -html.js fieldset.collapsible { - top: -1em; -} -html.js fieldset.collapsed { - top: 0; - margin-bottom: 1em; -} - -tr.menu-disabled { - /* Use filter to emulate CSS3 opacity */ - filter: alpha(opacity=50); -} - -#header-region { - /* Because of the lack of min-height, we use height as an alternative */ - height: 1em; -} - -tr.taxonomy-term-preview { - filter: alpha(opacity=50); -} - -#attach-hide label, -#uploadprogress div.message { - /* Fading elements in IE causes the text to bleed unless they have a background. */ - background-color: #ffffff; -} diff --git a/themes/garland/template.php b/themes/garland/template.php index 1b144f1210b9..fc09571435f3 100644 --- a/themes/garland/template.php +++ b/themes/garland/template.php @@ -40,8 +40,6 @@ function garland_preprocess_html(&$vars) { if (theme_get_setting('garland_width') == 'fluid') { $vars['classes_array'][] = 'fluid-width'; } - // Add conditional CSS for IE6. - drupal_add_css(path_to_theme() . '/fix-ie.css', array('group' => CSS_THEME, 'browsers' => array('IE' => 'lt IE 7', '!IE' => FALSE), 'preprocess' => FALSE)); } /** diff --git a/themes/seven/ie6.css b/themes/seven/ie6.css deleted file mode 100644 index 7d1c320b84bc..000000000000 --- a/themes/seven/ie6.css +++ /dev/null @@ -1,17 +0,0 @@ - -ul.menu li, -ul.menu li a, -ul.links li, -ul.links li a, -.action-links, -#page { - height: 1%; -} -#block-system-main ul.admin-list li a { - height: 1px; - position: relative; - display: block; -} -#block-system-main ul.admin-list li div.description a { - display: inline; -} diff --git a/themes/seven/reset.css b/themes/seven/reset.css index 0bdc29aef80d..78602dea037e 100644 --- a/themes/seven/reset.css +++ b/themes/seven/reset.css @@ -205,13 +205,6 @@ ul.inline:after { display: none; clear: none; } -/* IE6 */ -* html .form-item, -* html ul.links, -* html div.admin-panel .body, -* html .clearfix { - height: 1%; -} /* IE7 */ *:first-child + html .form-item, *:first-child + html ul.links, diff --git a/themes/seven/template.php b/themes/seven/template.php index 437e9a79f006..b78f5ab5f834 100644 --- a/themes/seven/template.php +++ b/themes/seven/template.php @@ -20,8 +20,6 @@ function seven_preprocess_html(&$vars) { drupal_add_css(path_to_theme() . '/ie.css', array('group' => CSS_THEME, 'browsers' => array('IE' => 'lte IE 8', '!IE' => FALSE), 'weight' => 999, 'preprocess' => FALSE)); // Add conditional CSS for IE7 and below. drupal_add_css(path_to_theme() . '/ie7.css', array('group' => CSS_THEME, 'browsers' => array('IE' => 'lte IE 7', '!IE' => FALSE), 'weight' => 999, 'preprocess' => FALSE)); - // Add conditional CSS for IE6. - drupal_add_css(path_to_theme() . '/ie6.css', array('group' => CSS_THEME, 'browsers' => array('IE' => 'lte IE 6', '!IE' => FALSE), 'weight' => 999, 'preprocess' => FALSE)); } /** diff --git a/themes/seven/vertical-tabs.css b/themes/seven/vertical-tabs.css index 4f6622251c73..f06aade8135b 100644 --- a/themes/seven/vertical-tabs.css +++ b/themes/seven/vertical-tabs.css @@ -87,7 +87,3 @@ div.vertical-tabs .vertical-tabs-panes legend { -webkit-box-sizing: border-box; box-sizing: border-box; } -* html .vertical-tabs .form-type-textfield, -* html .vertical-tabs .form-textarea-wrapper { - width: 95%; /* IE6 */ -} diff --git a/themes/stark/layout.css b/themes/stark/layout.css index 43bb93f88856..7e49d74a8f7c 100644 --- a/themes/stark/layout.css +++ b/themes/stark/layout.css @@ -10,9 +10,7 @@ * This layout method works reasonably well, but shouldn't be used on a * production site because it can break. For example, if an over-large image * (one that is wider than 20% of the viewport) is in the left sidebar, the - * image will overlap with the #content to the right. The exception to this - * is IE6 which will just hide the navigation block completely in these - * instances due to a positioning bug. + * image will overlap with the #content to the right. */ #content, From 849860fb523f7d32a085997da929bc5061c37bac Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Sun, 11 Sep 2011 20:19:05 -0400 Subject: [PATCH 42/80] - Patch #262498 by EugenMayer, DjebbZ: error in content administration using taxonomy filter. --- modules/node/node.admin.inc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/modules/node/node.admin.inc b/modules/node/node.admin.inc index 43e0ecd2823a..a6abb0b70670 100644 --- a/modules/node/node.admin.inc +++ b/modules/node/node.admin.inc @@ -121,10 +121,6 @@ function node_build_filter_query(SelectQueryInterface $query) { foreach ($filter_data as $index => $filter) { list($key, $value) = $filter; switch ($key) { - case 'term': - $alias = $query->join('taxonomy_index', 'ti', "n.nid = %alias.nid"); - $query->condition($alias . '.tid', $value); - break; case 'status': // Note: no exploitable hole as $key/$value have already been checked when submitted list($key, $value) = explode('-', $value, 2); From 245db071cc5dabfa59f6f58f1b4768497b7f5eff Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Sun, 11 Sep 2011 20:26:41 -0400 Subject: [PATCH 43/80] - Patch #1275808 by bfroehle: use new cache bin naming in hook_flush_cache(). --- includes/common.inc | 8 ++++---- modules/block/block.module | 2 +- modules/field/field.module | 2 +- modules/image/image.module | 2 +- modules/system/system.api.php | 8 ++++---- modules/system/system.module | 8 ++++---- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/includes/common.inc b/includes/common.inc index bb4d8e7321bd..72fcf76a8046 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -7146,10 +7146,10 @@ function drupal_flush_all_caches() { // Don't clear cache_form - in-progress form submissions may break. // Ordered so clearing the page cache will always be the last action. - $core = array('cache', 'cache_path', 'cache_filter', 'cache_bootstrap', 'cache_page'); - $cache_tables = array_merge(module_invoke_all('flush_caches'), $core); - foreach ($cache_tables as $table) { - cache($table)->flush(); + $core = array('cache', 'path', 'filter', 'bootstrap', 'page'); + $cache_bins = array_merge(module_invoke_all('flush_caches'), $core); + foreach ($cache_bins as $bin) { + cache($bin)->flush(); } // Rebuild the bootstrap module list. We do this here so that developers diff --git a/modules/block/block.module b/modules/block/block.module index 7f9379f30a39..86e1ca734b78 100644 --- a/modules/block/block.module +++ b/modules/block/block.module @@ -912,7 +912,7 @@ function block_flush_caches() { _block_rehash($theme->name); } - return array('cache_block'); + return array('block'); } /** diff --git a/modules/field/field.module b/modules/field/field.module index 9683c02b691c..8969547715dd 100644 --- a/modules/field/field.module +++ b/modules/field/field.module @@ -421,7 +421,7 @@ function field_system_info_alter(&$info, $file, $type) { */ function field_flush_caches() { field_sync_field_status(); - return array('cache_field'); + return array('field'); } /** diff --git a/modules/image/image.module b/modules/image/image.module index 1f4207374038..af683d6ba80f 100644 --- a/modules/image/image.module +++ b/modules/image/image.module @@ -262,7 +262,7 @@ function image_system_file_system_settings_submit($form, &$form_state) { * Implements hook_flush_caches(). */ function image_flush_caches() { - return array('cache_image'); + return array('image'); } /** diff --git a/modules/system/system.api.php b/modules/system/system.api.php index 52350f9b9339..ec7f6b8ba892 100644 --- a/modules/system/system.api.php +++ b/modules/system/system.api.php @@ -2377,17 +2377,17 @@ function hook_mail($key, &$message, $params) { /** * Add a list of cache tables to be cleared. * - * This hook allows your module to add cache table names to the list of cache - * tables that will be cleared by the Clear button on the Performance page or + * This hook allows your module to add cache bins to the list of cache bins + * that will be cleared by the Clear button on the Performance page or * whenever drupal_flush_all_caches is invoked. * * @return - * An array of cache table names. + * An array of cache bins. * * @see drupal_flush_all_caches() */ function hook_flush_caches() { - return array('cache_example'); + return array('example'); } /** diff --git a/modules/system/system.module b/modules/system/system.module index cfab0a98288c..16ed0b50cecb 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -3004,10 +3004,10 @@ function system_cron() { } } - $core = array('cache', 'cache_path', 'cache_filter', 'cache_page', 'cache_form', 'cache_menu'); - $cache_tables = array_merge(module_invoke_all('flush_caches'), $core); - foreach ($cache_tables as $table) { - cache($table)->expire(); + $core = array('cache', 'path', 'filter', 'page', 'form', 'menu'); + $cache_bins = array_merge(module_invoke_all('flush_caches'), $core); + foreach ($cache_bins as $bin) { + cache($bin)->expire(); } // Cleanup the batch table and the queue for failed batches. From d2779809b2d348040415c21696b5d196649e691f Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Sun, 11 Sep 2011 20:29:15 -0400 Subject: [PATCH 44/80] - Patch #999040 by Niklas Fiekas: corrected alternative control structure syntax in theme templates. --- modules/aggregator/aggregator-item.tpl.php | 6 +++--- modules/aggregator/aggregator-summary-item.tpl.php | 4 ++-- modules/book/book-all-books-block.tpl.php | 8 ++++---- modules/book/book-export-html.tpl.php | 2 +- modules/book/book-navigation.tpl.php | 6 +++--- modules/field/theme/field.tpl.php | 4 ++-- modules/search/search-block-form.tpl.php | 2 +- modules/search/search-result.tpl.php | 6 +++--- modules/search/search-results.tpl.php | 2 +- modules/user/user-profile-category.tpl.php | 2 +- themes/garland/comment.tpl.php | 2 +- 11 files changed, 22 insertions(+), 22 deletions(-) diff --git a/modules/aggregator/aggregator-item.tpl.php b/modules/aggregator/aggregator-item.tpl.php index c5dd70c67b5d..e9ad1e0d7560 100644 --- a/modules/aggregator/aggregator-item.tpl.php +++ b/modules/aggregator/aggregator-item.tpl.php @@ -24,19 +24,19 @@
- + -
- +
- +
:
diff --git a/modules/aggregator/aggregator-summary-item.tpl.php b/modules/aggregator/aggregator-summary-item.tpl.php index 1c8299938be0..fcd57c7a4629 100644 --- a/modules/aggregator/aggregator-summary-item.tpl.php +++ b/modules/aggregator/aggregator-summary-item.tpl.php @@ -18,6 +18,6 @@ -, - +, + diff --git a/modules/book/book-all-books-block.tpl.php b/modules/book/book-all-books-block.tpl.php index d22ff2ccc972..626a5f26482a 100644 --- a/modules/book/book-all-books-block.tpl.php +++ b/modules/book/book-all-books-block.tpl.php @@ -11,8 +11,8 @@ * render() on each to print it as an unordered list. */ ?> - $menu) : ?> -
- -
+ $menu): ?> +
+ +
diff --git a/modules/book/book-export-html.tpl.php b/modules/book/book-export-html.tpl.php index 180f3ae7783f..4b25a766e18c 100644 --- a/modules/book/book-export-html.tpl.php +++ b/modules/book/book-export-html.tpl.php @@ -40,7 +40,7 @@ */ $div_close = ''; ?> - +
'; ?> diff --git a/modules/book/book-navigation.tpl.php b/modules/book/book-navigation.tpl.php index e5883dc56ec8..5d8e9aa7fcb6 100644 --- a/modules/book/book-navigation.tpl.php +++ b/modules/book/book-navigation.tpl.php @@ -35,13 +35,13 @@ diff --git a/modules/field/theme/field.tpl.php b/modules/field/theme/field.tpl.php index e4cd85cd025b..9e76e3b9c12f 100644 --- a/modules/field/theme/field.tpl.php +++ b/modules/field/theme/field.tpl.php @@ -49,11 +49,11 @@ After copying this file to your theme's folder and customizing it, remove this HTML comment. -->
> - +
>
> - $item) : ?> + $item): ?>
>
diff --git a/modules/search/search-block-form.tpl.php b/modules/search/search-block-form.tpl.php index 78447463c6f9..da58403c2c47 100644 --- a/modules/search/search-block-form.tpl.php +++ b/modules/search/search-block-form.tpl.php @@ -30,7 +30,7 @@ */ ?>
- subject)) : ?> + subject)): ?>

diff --git a/modules/search/search-result.tpl.php b/modules/search/search-result.tpl.php index db9f2202fc23..949452ac34cb 100644 --- a/modules/search/search-result.tpl.php +++ b/modules/search/search-result.tpl.php @@ -45,7 +45,7 @@ * for its existence before printing. The default keys of 'type', 'user' and * 'date' always exist for node searches. Modules may provide other data. * @code - * + * * * * @@ -69,10 +69,10 @@
- +

>

- +

diff --git a/modules/search/search-results.tpl.php b/modules/search/search-results.tpl.php index 4de724bec196..e35be1edcfaa 100644 --- a/modules/search/search-results.tpl.php +++ b/modules/search/search-results.tpl.php @@ -21,7 +21,7 @@ * @see template_preprocess_search_results() */ ?> - +

    diff --git a/modules/user/user-profile-category.tpl.php b/modules/user/user-profile-category.tpl.php index 3e295826c495..8b6cd9991a6b 100644 --- a/modules/user/user-profile-category.tpl.php +++ b/modules/user/user-profile-category.tpl.php @@ -25,7 +25,7 @@ */ ?>
    - +

    diff --git a/themes/garland/comment.tpl.php b/themes/garland/comment.tpl.php index 900afc106535..952cc0170325 100644 --- a/themes/garland/comment.tpl.php +++ b/themes/garland/comment.tpl.php @@ -6,7 +6,7 @@ - + From 78d166a509391fe86b0eefd30141d0f869efd4fe Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Tue, 13 Sep 2011 22:29:05 -0400 Subject: [PATCH 45/80] - Patch #1216972 by Jacine: added missing CSS files. --- modules/color/color.admin-rtl.css | 44 +++++++++++++++++ modules/color/color.admin.css | 81 +++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 modules/color/color.admin-rtl.css create mode 100644 modules/color/color.admin.css diff --git a/modules/color/color.admin-rtl.css b/modules/color/color.admin-rtl.css new file mode 100644 index 000000000000..bfbcd499ff8f --- /dev/null +++ b/modules/color/color.admin-rtl.css @@ -0,0 +1,44 @@ + +#placeholder { + left: 0; + right: auto; +} + +/* Palette */ +.color-form .form-item { + padding-left: 0; + padding-right: 1em; +} +.color-form label { + float: right; + clear: right; +} +.color-form .form-text, +.color-form .form-select { + float: right; +} +.color-form .form-text { + margin-right: 0; + margin-left: 5px; +} +#palette .hook { + float: right; +} +#palette .down, +#palette .up, +#palette .both { + background: url(images/hook-rtl.png) no-repeat 0 0; +} +#palette .up { + background-position: 0 -27px; +} +#palette .both { + background-position: 0 -54px; +} +#palette .lock { + float: right; + right: -10px; +} +html.js #preview { + float: right; +} diff --git a/modules/color/color.admin.css b/modules/color/color.admin.css new file mode 100644 index 000000000000..e513dadf5408 --- /dev/null +++ b/modules/color/color.admin.css @@ -0,0 +1,81 @@ + +/* Farbtastic placement */ +.color-form { + max-width: 50em; + position: relative; +} +#placeholder { + position: absolute; + top: 0; + right: 0; /* LTR */ +} + +/* Palette */ +.color-form .form-item { + height: 2em; + line-height: 2em; + padding-left: 1em; /* LTR */ + margin: 0.5em 0; +} +.color-form label { + float: left; /* LTR */ + clear: left; /* LTR */ + width: 10em; +} +.color-form .form-text, +.color-form .form-select { + float: left; /* LTR */ +} +.color-form .form-text { + text-align: center; + margin-right: 5px; /* LTR */ + cursor: pointer; +} + +#palette .hook { + float: left; /* LTR */ + margin-top: 3px; + width: 16px; + height: 16px; +} +#palette .down, +#palette .up, +#palette .both { + background: url(images/hook.png) no-repeat 100% 0; /* LTR */ +} +#palette .up { + background-position: 100% -27px; /* LTR */ +} +#palette .both { + background-position: 100% -54px; /* LTR */ +} + +#palette .lock { + float: left; /* LTR */ + position: relative; + top: -1.4em; + left: -10px; /* LTR */ + width: 20px; + height: 25px; + background: url(images/lock.png) no-repeat 50% 2px; + cursor: pointer; +} +#palette .unlocked { + background-position: 50% -22px; +} +#palette .form-item { + width: 20em; +} +#palette .item-selected { + background: #eee; +} + +/* Preview */ +#preview { + display: none; +} +html.js #preview { + display: block; + position: relative; + float: left; /* LTR */ +} From 7c77288a11d9a58037e0703db84d5810fd2ce335 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Tue, 13 Sep 2011 22:32:37 -0400 Subject: [PATCH 46/80] - Patch #1276746 by jessebeach: the title of the ui.mouse library in system.module is incorrectly set to 'jQuery UI: Droppable' instead of 'jQuery UI: Mouse'. --- modules/system/system.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/system/system.module b/modules/system/system.module index 16ed0b50cecb..d7dc69cc4c54 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -1359,7 +1359,7 @@ function system_library() { ), ); $libraries['ui.mouse'] = array( - 'title' => 'jQuery UI: Droppable', + 'title' => 'jQuery UI: Mouse', 'website' => 'http://docs.jquery.com/UI/Mouse', 'version' => '1.8.7', 'js' => array( From 3b1d2f387ffdde5419fae3fca60314c990d56acd Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Wed, 14 Sep 2011 16:24:19 -0400 Subject: [PATCH 47/80] - Patch #76824 by geerlingguy, xjm, droplet, kbahey: Drupal should not handle 404 for certain files. Oh yeah. --- .htaccess | 6 ----- includes/bootstrap.inc | 28 +++++++++++++++++++++++ includes/common.inc | 5 ++++- modules/locale/locale.test | 5 ++--- sites/default/default.settings.php | 36 ++++++++++++++++++++++++++++++ 5 files changed, 70 insertions(+), 10 deletions(-) diff --git a/.htaccess b/.htaccess index 9494b53b42d7..d2b9ae67457b 100644 --- a/.htaccess +++ b/.htaccess @@ -16,12 +16,6 @@ Options +FollowSymLinks # Make Drupal handle any 404 errors. ErrorDocument 404 /index.php -# Force simple error message for requests for non-existent favicon.ico. - - # There is no end quote below, for compatibility with Apache 1.3. - ErrorDocument 404 "The requested file favicon.ico was not found. - - # Set the default handler. DirectoryIndex index.php index.html index.htm diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index 285652960394..01860c50cf76 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -2417,6 +2417,34 @@ function drupal_maintenance_theme() { _drupal_maintenance_theme(); } +/** + * Returns a simple 404 Not Found page. + * + * If fast 404 pages are enabled, and this is a matching page then print a + * simple 404 page and exit. + * + * This function is called from drupal_deliver_html_page() at the time when a + * a normal 404 page is generated, but it can also optionally be called directly + * from settings.php to prevent a Drupal bootstrap on these pages. See + * documentation in settings.php for the benefits and drawbacks of using this. + * + * Paths to dynamically-generated content, such as image styles, should also be + * accounted for in this function. + */ +function drupal_fast_404() { + $exclude_paths = variable_get('404_fast_paths_exclude', FALSE); + if ($exclude_paths && !preg_match($exclude_paths, $_GET['q'])) { + $fast_paths = variable_get('404_fast_paths', FALSE); + if ($fast_paths && preg_match($fast_paths, $_GET['q'])) { + drupal_add_http_header('Status', '404 Not Found'); + $fast_404_html = variable_get('404_fast_html', '404 Not Found

    Not Found

    The requested URL "@path" was not found on this server.

    '); + // Replace @path in the variable with the page path. + print strtr($fast_404_html, array('@path' => check_plain(request_uri()))); + exit; + } + } +} + /** * Return TRUE if a Drupal installation is currently being attempted. */ diff --git a/includes/common.inc b/includes/common.inc index 72fcf76a8046..6db2e64cad59 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -2476,6 +2476,9 @@ function drupal_deliver_html_page($page_callback_result) { watchdog('page not found', check_plain($_GET['q']), NULL, WATCHDOG_WARNING); + // Check for and return a fast 404 page if configured. + drupal_fast_404(); + // Keep old path for reference, and to allow forms to redirect to it. if (!isset($_GET['destination'])) { $_GET['destination'] = $_GET['q']; @@ -2492,7 +2495,7 @@ function drupal_deliver_html_page($page_callback_result) { if (empty($return) || $return == MENU_NOT_FOUND || $return == MENU_ACCESS_DENIED) { // Standard 404 handler. drupal_set_title(t('Page not found')); - $return = t('The requested page could not be found.'); + $return = t('The requested page "@path" could not be found.', array('@path' => request_uri())); } drupal_set_page_content($return); diff --git a/modules/locale/locale.test b/modules/locale/locale.test index 425f68cfa79a..90e313df7409 100644 --- a/modules/locale/locale.test +++ b/modules/locale/locale.test @@ -290,9 +290,8 @@ class LocaleTranslationFunctionalTest extends DrupalWebTestCase { $this->assertRaw(t('The language %locale has been removed.', array('%locale' => $name)), t('The test language has been removed.')); // Reload to remove $name. $this->drupalGet($path); - $this->assertNoText($langcode, t('Language code not found.')); - $this->assertNoText($name, t('Name not found.')); - $this->assertNoText($native, t('Native not found.')); + // Verify that language is no longer found. + $this->assertResponse(404, t('Language no longer found.')); $this->drupalLogout(); // Delete the string. diff --git a/sites/default/default.settings.php b/sites/default/default.settings.php index 0472f02b1c3b..710cab3589d8 100644 --- a/sites/default/default.settings.php +++ b/sites/default/default.settings.php @@ -427,6 +427,42 @@ ini_set('session.cookie_lifetime', 2000000); # 'a.b.c.d', # ); +/** + * Fast 404 pages: + * + * Drupal can generate fully themed 404 pages. However, some of these responses + * are for images or other resource files that are not displayed to the user. + * This can waste bandwidth, and also generate server load. + * + * The options below return a simple, fast 404 page for URLs matching a + * specific pattern: + * - 404_fast_paths_exclude: A regular expression to match paths to exclude, + * such as images generated by image styles, or dynamically-resized images. + * If you need to add more paths, you can add '|path' to the expression. + * - 404_fast_paths: A regular expression to match paths that should return a + * simple 404 page, rather than the fully themed 404 page. If you don't have + * any aliases ending in htm or html you can add '|s?html?' to the expression. + * - 404_fast_html: The html to return for simple 404 pages. + * + * Add leading hash signs if you would like to disable this functionality. + */ +$conf['404_fast_paths_exclude'] = '/\/(?:styles)\//'; +$conf['404_fast_paths'] = '/\.(?:txt|png|gif|jpe?g|css|js|ico|swf|flv|cgi|bat|pl|dll|exe|asp)$/i'; +$conf['404_fast_html'] = '404 Not Found

    Not Found

    The requested URL "@path" was not found on this server.

    '; + +/** + * By default, fast 404s are returned as part of the normal page request + * process, which will properly serve valid pages that happen to match and will + * also log actual 404s to the Drupal log. Alternatively you can choose to + * return a 404 now by uncommenting the following line. This will reduce server + * load, but will cause even valid pages that happen to match the pattern to + * return 404s, rather than the actual page. It will also prevent the Drupal + * system log entry. Ensure you understand the effects of this before enabling. + * + * To enable this functionality, remove the leading hash sign below. + */ +# drupal_fast_404(); + /** * Authorized file system operations: * From adec17e04f53de1db6ade290c9baf5648d246c33 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Wed, 14 Sep 2011 22:02:44 -0400 Subject: [PATCH 48/80] - Patch #1278182 by CashWilliams: user-profile.tpl.php should use not in example. --- modules/user/user-profile.tpl.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/user/user-profile.tpl.php b/modules/user/user-profile.tpl.php index 50e611117363..b6094294b1dc 100644 --- a/modules/user/user-profile.tpl.php +++ b/modules/user/user-profile.tpl.php @@ -18,11 +18,11 @@ * Available variables: * - $user_profile: An array of profile items. Use render() to print them. * - Field variables: for each field instance attached to the user a - * corresponding variable is defined; e.g., $user->field_example has a + * corresponding variable is defined; e.g., $account->field_example has a * variable $field_example defined. When needing to access a field's raw * values, developers/themers are strongly encouraged to use these * variables. Otherwise they will have to explicitly specify the desired - * field language, e.g. $user->field_example['en'], thus overriding any + * field language, e.g. $account->field_example['en'], thus overriding any * language negotiation rule that was previously applied. * * @see user-profile-category.tpl.php From 4d30df83fa43be903d5e03681812623979dc1d2a Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Wed, 14 Sep 2011 22:11:13 -0400 Subject: [PATCH 49/80] - Patch #1275214 by CashWilliams: block_load() should not refer to . --- modules/block/block.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/block/block.module b/modules/block/block.module index 86e1ca734b78..d1c803979557 100644 --- a/modules/block/block.module +++ b/modules/block/block.module @@ -668,7 +668,7 @@ function block_list($region) { * Name of the module that implements the block to load. * @param $delta * Unique ID of the block within the context of $module. Pass NULL to return - * an empty $block object for $module. + * an empty block object for $module. * * @return * A block object. From 39c4a7a51478adcab9c0d7b87245b1761ee3aeec Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Wed, 14 Sep 2011 22:22:59 -0400 Subject: [PATCH 50/80] - Patch #1275684 by catch: image module creates a cache bin then never uses it. --- modules/image/image.install | 3 --- modules/image/image.module | 7 ------- 2 files changed, 10 deletions(-) diff --git a/modules/image/image.install b/modules/image/image.install index 6f37ad00ed9e..911a445152fd 100644 --- a/modules/image/image.install +++ b/modules/image/image.install @@ -28,9 +28,6 @@ function image_uninstall() { function image_schema() { $schema = array(); - $schema['cache_image'] = drupal_get_schema_unprocessed('system', 'cache'); - $schema['cache_image']['description'] = 'Cache table used to store information about image manipulations that are in-progress.'; - $schema['image_styles'] = array( 'description' => 'Stores configuration options for image styles.', 'fields' => array( diff --git a/modules/image/image.module b/modules/image/image.module index af683d6ba80f..9e1c57ead92d 100644 --- a/modules/image/image.module +++ b/modules/image/image.module @@ -258,13 +258,6 @@ function image_system_file_system_settings_submit($form, &$form_state) { } } -/** - * Implements hook_flush_caches(). - */ -function image_flush_caches() { - return array('image'); -} - /** * Implements hook_file_download(). * From bb131220b5ecd767e806ce02df474f1685efb945 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Wed, 14 Sep 2011 22:46:57 -0400 Subject: [PATCH 51/80] - Patch #1273032 by michaellenahan: conf_path() has formatting issue with numbered list. --- includes/bootstrap.inc | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index 01860c50cf76..7d000343a588 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -489,22 +489,22 @@ function timer_stop($name) { * With a site located at http://www.example.com:8080/mysite/test/, the file, * settings.php, is searched for in the following directories: * - * 1. $confdir/8080.www.example.com.mysite.test - * 2. $confdir/www.example.com.mysite.test - * 3. $confdir/example.com.mysite.test - * 4. $confdir/com.mysite.test + * - $confdir/8080.www.example.com.mysite.test + * - $confdir/www.example.com.mysite.test + * - $confdir/example.com.mysite.test + * - $confdir/com.mysite.test * - * 5. $confdir/8080.www.example.com.mysite - * 6. $confdir/www.example.com.mysite - * 7. $confdir/example.com.mysite - * 8. $confdir/com.mysite + * - $confdir/8080.www.example.com.mysite + * - $confdir/www.example.com.mysite + * - $confdir/example.com.mysite + * - $confdir/com.mysite * - * 9. $confdir/8080.www.example.com - * 10. $confdir/www.example.com - * 11. $confdir/example.com - * 12. $confdir/com + * - $confdir/8080.www.example.com + * - $confdir/www.example.com + * - $confdir/example.com + * - $confdir/com * - * 13. $confdir/default + * - $confdir/default * * If a file named sites.php is present in the $confdir, it will be loaded * prior to scanning for directories. It should define an associative array From 81aaf70447c5c61afe98b2368266b6156bae3dcc Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Wed, 14 Sep 2011 22:56:07 -0400 Subject: [PATCH 52/80] - Patch #1263912 by TR: run-tests.sh fails to print out list of tests to be run. --- scripts/run-tests.sh | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/scripts/run-tests.sh b/scripts/run-tests.sh index dc4f2600fd38..63c02af73c5a 100755 --- a/scripts/run-tests.sh +++ b/scripts/run-tests.sh @@ -67,6 +67,8 @@ if ($args['list']) { exit; } +$test_list = simpletest_script_get_test_list(); + // Try to allocate unlimited time to run the tests. drupal_set_time_limit(0); @@ -485,12 +487,13 @@ function simpletest_script_reporter_init() { echo "\n"; } - echo "Test run started: " . format_date($_SERVER['REQUEST_TIME'], 'long') . "\n"; + echo "Test run started:\n"; + echo " " . format_date($_SERVER['REQUEST_TIME'], 'long') . "\n"; timer_start('run-tests'); echo "\n"; - echo "Test summary:\n"; - echo "-------------\n"; + echo "Test summary\n"; + echo "------------\n"; echo "\n"; } @@ -571,7 +574,7 @@ function simpletest_script_reporter_timer_stop() { echo "\n"; $end = timer_stop('run-tests'); echo "Test run duration: " . format_interval($end['time'] / 1000); - echo "\n"; + echo "\n\n"; } /** From d36af64d46f56f9c738a3e4c1837a1b4d593d305 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Wed, 14 Sep 2011 22:57:55 -0400 Subject: [PATCH 53/80] - Patch #1216948 by Jacine: added missing CSS files. --- modules/aggregator/aggregator.theme-rtl.css | 3 +++ modules/aggregator/aggregator.theme.css | 4 ++++ 2 files changed, 7 insertions(+) create mode 100644 modules/aggregator/aggregator.theme-rtl.css create mode 100644 modules/aggregator/aggregator.theme.css diff --git a/modules/aggregator/aggregator.theme-rtl.css b/modules/aggregator/aggregator.theme-rtl.css new file mode 100644 index 000000000000..f02ae91b2dfb --- /dev/null +++ b/modules/aggregator/aggregator.theme-rtl.css @@ -0,0 +1,3 @@ +.aggregator .feed-icon { + float: left; +} diff --git a/modules/aggregator/aggregator.theme.css b/modules/aggregator/aggregator.theme.css new file mode 100644 index 000000000000..e2182acf7c72 --- /dev/null +++ b/modules/aggregator/aggregator.theme.css @@ -0,0 +1,4 @@ +.aggregator .feed-icon { + float: right; /* LTR */ + display: block; +} From a1c091cef3f14885ec55a3ca187a32681e279413 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Fri, 16 Sep 2011 11:51:45 -0400 Subject: [PATCH 54/80] - Patch #1279888 by 10oclock: missing table prefix on query in node_feed(). --- modules/node/node.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/node/node.module b/modules/node/node.module index 92c679538d44..58604264e97e 100644 --- a/modules/node/node.module +++ b/modules/node/node.module @@ -2452,7 +2452,7 @@ function node_feed($nids = FALSE, $channel = array()) { $nids = db_select('node', 'n') ->fields('n', array('nid', 'created')) ->condition('n.promote', 1) - ->condition('status', 1) + ->condition('n.status', 1) ->orderBy('n.created', 'DESC') ->range(0, variable_get('feed_default_items', 10)) ->addTag('node_access') From 75e28b464712cd2f0a8177b91e3728ea556c4e48 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Fri, 16 Sep 2011 14:58:27 -0400 Subject: [PATCH 55/80] - Patch #1281436 by dixon_: double spaces in comment.module. --- modules/comment/comment.module | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/comment/comment.module b/modules/comment/comment.module index 37a208f6e97d..b52204ab3aaf 100644 --- a/modules/comment/comment.module +++ b/modules/comment/comment.module @@ -92,7 +92,7 @@ function comment_help($path, $arg) { * Implements hook_entity_info(). */ function comment_entity_info() { - $return = array( + $return = array( 'comment' => array( 'label' => t('Comment'), 'base table' => 'comment', @@ -542,7 +542,7 @@ function comment_new_page_count($num_comments, $new_replies, $node) { elseif ($flat) { // Flat comments. $count = $num_comments - $new_replies; - $pageno = $count / $comments_per_page; + $pageno = $count / $comments_per_page; } else { // Threaded comments: we build a query with a subquery to find the first @@ -575,7 +575,7 @@ function comment_new_page_count($num_comments, $new_replies, $node) { ':thread' => $first_thread, ))->fetchField(); - $pageno = $count / $comments_per_page; + $pageno = $count / $comments_per_page; } if ($pageno >= 1) { @@ -1439,7 +1439,7 @@ function comment_save($comment) { $transaction = db_transaction(); try { - $defaults = array( + $defaults = array( 'mail' => '', 'homepage' => '', 'name' => '', @@ -1711,7 +1711,7 @@ function comment_num_new($nid, $timestamp = 0) { $timestamp = ($timestamp > NODE_NEW_LIMIT ? $timestamp : NODE_NEW_LIMIT); // Use the timestamp to retrieve the number of new comments. - return db_query('SELECT COUNT(cid) FROM {comment} WHERE nid = :nid AND created > :timestamp AND status = :status', array( + return db_query('SELECT COUNT(cid) FROM {comment} WHERE nid = :nid AND created > :timestamp AND status = :status', array( ':nid' => $nid, ':timestamp' => $timestamp, ':status' => COMMENT_PUBLISHED, @@ -2279,10 +2279,10 @@ function template_preprocess_comment(&$variables) { // Set status to a string representation of comment->status. if (isset($comment->in_preview)) { - $variables['status'] = 'comment-preview'; + $variables['status'] = 'comment-preview'; } else { - $variables['status'] = ($comment->status == COMMENT_NOT_PUBLISHED) ? 'comment-unpublished' : 'comment-published'; + $variables['status'] = ($comment->status == COMMENT_NOT_PUBLISHED) ? 'comment-unpublished' : 'comment-published'; } // Gather comment classes. if ($comment->uid === 0) { From 79858cb7f83f81fb707cefd4e82d2b958098aa8d Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Fri, 16 Sep 2011 15:03:08 -0400 Subject: [PATCH 56/80] - Patch #1280718 by theborg: white space in forum code. --- modules/forum/forum.admin.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/forum/forum.admin.inc b/modules/forum/forum.admin.inc index 1e6b36551591..49c71d90a0bf 100644 --- a/modules/forum/forum.admin.inc +++ b/modules/forum/forum.admin.inc @@ -55,7 +55,7 @@ function forum_form_forum($form, &$form_state, $edit = array()) { $form['vid'] = array('#type' => 'hidden', '#value' => variable_get('forum_nav_vocabulary', '')); $form['actions'] = array('#type' => 'actions'); - $form['actions']['submit' ] = array('#type' => 'submit', '#value' => t('Save')); + $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save')); if ($edit['tid']) { $form['actions']['delete'] = array('#type' => 'submit', '#value' => t('Delete')); $form['tid'] = array('#type' => 'hidden', '#value' => $edit['tid']); From c4f2229e6bb3d8f86e96e3e114713edf80c2c395 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Fri, 16 Sep 2011 15:08:57 -0400 Subject: [PATCH 57/80] - Patch #1057242 by drunken monkey: entity_uri() should not use ->uri (was ->uri doesn't match the contract for uri callbacks). --- includes/common.inc | 58 +++++++++++++++++---------------------------- 1 file changed, 22 insertions(+), 36 deletions(-) diff --git a/includes/common.inc b/includes/common.inc index 6db2e64cad59..0c6c9eb2be8f 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -7572,44 +7572,30 @@ function entity_prepare_view($entity_type, $entities, $langcode = NULL) { * uri of its own. */ function entity_uri($entity_type, $entity) { - // This check enables the URI of an entity to be easily overridden from what - // the callback for the entity type or bundle would return, and it helps - // minimize performance overhead when entity_uri() is called multiple times - // for the same entity. - if (!isset($entity->uri)) { - $info = entity_get_info($entity_type); - list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity); + $info = entity_get_info($entity_type); + list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity); - // A bundle-specific callback takes precedence over the generic one for the - // entity type. - if (isset($info['bundles'][$bundle]['uri callback'])) { - $uri_callback = $info['bundles'][$bundle]['uri callback']; - } - elseif (isset($info['uri callback'])) { - $uri_callback = $info['uri callback']; - } - else { - $uri_callback = NULL; - } - - // Invoke the callback to get the URI. If there is no callback, set the - // entity's 'uri' property to FALSE to indicate that it is known to not have - // a URI. - if (isset($uri_callback) && function_exists($uri_callback)) { - $entity->uri = $uri_callback($entity); - if (!isset($entity->uri['options'])) { - $entity->uri['options'] = array(); - } - // Pass the entity data to url() so that alter functions do not need to - // lookup this entity again. - $entity->uri['options']['entity_type'] = $entity_type; - $entity->uri['options']['entity'] = $entity; - } - else { - $entity->uri = FALSE; - } + // A bundle-specific callback takes precedence over the generic one for the + // entity type. + if (isset($info['bundles'][$bundle]['uri callback'])) { + $uri_callback = $info['bundles'][$bundle]['uri callback']; + } + elseif (isset($info['uri callback'])) { + $uri_callback = $info['uri callback']; + } + else { + return NULL; + } + + // Invoke the callback to get the URI. If there is no callback, return NULL. + if (isset($uri_callback) && function_exists($uri_callback)) { + $uri = $uri_callback($entity); + // Pass the entity data to url() so that alter functions do not need to + // lookup this entity again. + $uri['options']['entity_type'] = $entity_type; + $uri['options']['entity'] = $entity; + return $uri; } - return $entity->uri ? $entity->uri : NULL; } /** From 670cf5cd7be416c08c46a1a90096c766f8a4a2cb Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Fri, 16 Sep 2011 17:39:51 -0400 Subject: [PATCH 58/80] - Patch #1262064 by michaellenahan: default.settings.php has formatting issue with numbered list. --- sites/default/default.settings.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/sites/default/default.settings.php b/sites/default/default.settings.php index 710cab3589d8..866e170cce49 100644 --- a/sites/default/default.settings.php +++ b/sites/default/default.settings.php @@ -22,19 +22,19 @@ * http://www.drupal.org/mysite/test/, the 'settings.php' * is searched in the following directories: * - * 1. sites/www.drupal.org.mysite.test - * 2. sites/drupal.org.mysite.test - * 3. sites/org.mysite.test + * - sites/www.drupal.org.mysite.test + * - sites/drupal.org.mysite.test + * - sites/org.mysite.test * - * 4. sites/www.drupal.org.mysite - * 5. sites/drupal.org.mysite - * 6. sites/org.mysite + * - sites/www.drupal.org.mysite + * - sites/drupal.org.mysite + * - sites/org.mysite * - * 7. sites/www.drupal.org - * 8. sites/drupal.org - * 9. sites/org + * - sites/www.drupal.org + * - sites/drupal.org + * - sites/org * - * 10. sites/default + * - sites/default * * If you are installing on a non-standard port number, prefix the * hostname with that number. For example, From 068424e21dbd2f87ef2868c5b1f69bea01cf477d Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Fri, 16 Sep 2011 17:45:27 -0400 Subject: [PATCH 59/80] - Patch #1182290 by BTMash, chx, matason, catch, mdm, tstoeckler: add boilerplate upgrade path tests. --- includes/utility.inc | 7 +++++ modules/simpletest/simpletest.info | 3 ++ modules/simpletest/tests/upgrade/upgrade.test | 29 +++++++++++++++---- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/includes/utility.inc b/includes/utility.inc index 254313f7338c..7466102e2a7a 100644 --- a/includes/utility.inc +++ b/includes/utility.inc @@ -46,6 +46,13 @@ function drupal_var_export($var, $prefix = '') { $output = "'" . $var . "'"; } } + else if (is_object($var) && get_class($var) === 'stdClass') { + // var_export() will export stdClass objects using an undefined + // magic method __set_state() leaving the export broken. This + // workaround avoids this by casting the object as an array for + // export and casting it back to an object when evaluated. + $output .= '(object) ' . drupal_var_export((array) $var, $prefix); + } else { $output = var_export($var, TRUE); } diff --git a/modules/simpletest/simpletest.info b/modules/simpletest/simpletest.info index 54b020d5c573..ba817daa5c2b 100644 --- a/modules/simpletest/simpletest.info +++ b/modules/simpletest/simpletest.info @@ -37,3 +37,6 @@ files[] = tests/theme.test files[] = tests/unicode.test files[] = tests/update.test files[] = tests/xmlrpc.test +files[] = tests/upgrade/upgrade.test +files[] = tests/upgrade/upgrade_bare.test +files[] = tests/upgrade/upgrade_filled.test diff --git a/modules/simpletest/tests/upgrade/upgrade.test b/modules/simpletest/tests/upgrade/upgrade.test index 9326f2561709..e2420b00d05e 100644 --- a/modules/simpletest/tests/upgrade/upgrade.test +++ b/modules/simpletest/tests/upgrade/upgrade.test @@ -33,6 +33,14 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase { protected function setUp() { global $user, $language, $conf; + // We are going to set a missing zlib requirement property for usage during + // the performUpgrade() and tearDown() calls. Also set that the tests failed. + if (!function_exists('gzopen')) { + $this->missing_zlib_requirement = TRUE; + parent::setUp(); + return; + } + // Load the Update API. require_once DRUPAL_ROOT . '/includes/update.inc'; @@ -92,7 +100,11 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase { $conf = array(); // Load the database from the portable PHP dump. + // The files can be gzipped. foreach ($this->databaseDumpFiles as $file) { + if (substr($file, -3) == '.gz') { + $file = "compress.zlib://$file"; + } require $file; } @@ -111,17 +123,12 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase { // Generate and set a D6-compatible session cookie. $this->curlInitialize(); $sid = drupal_hash_base64(uniqid(mt_rand(), TRUE) . drupal_random_bytes(55)); - $session_name = update_get_d6_session_name(); - curl_setopt($this->curlHandle, CURLOPT_COOKIE, rawurlencode($session_name) . '=' . rawurlencode($sid)); + curl_setopt($this->curlHandle, CURLOPT_COOKIE, rawurlencode(session_name()) . '=' . rawurlencode($sid)); // Force our way into the session of the child site. drupal_save_session(TRUE); - // A session cannot be written without the ssid column which is missing on - // Drupal 6 sites. - db_add_field('sessions', 'ssid', array('description' => "Secure session ID. The value is generated by Drupal's session handlers.", 'type' => 'varchar', 'length' => 128, 'not null' => TRUE, 'default' => '')); _drupal_session_write($sid, ''); // Remove the temporarily added ssid column. - db_drop_field('sessions', 'ssid'); drupal_save_session(FALSE); // Restore necessary variables. @@ -137,6 +144,11 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase { protected function tearDown() { global $user, $language; + if (!empty($this->missing_zlib_requirement)) { + parent::tearDown(); + return; + } + // In case a fatal error occured that was not in the test process read the // log to pick up any fatal errors. simpletest_log_read($this->testId, $this->databasePrefix, get_class($this), TRUE); @@ -233,6 +245,11 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase { protected function performUpgrade($register_errors = TRUE) { $update_url = $GLOBALS['base_url'] . '/update.php'; + if (!empty($this->missing_zlib_requirement)) { + $this->fail(t('Missing zlib requirement for upgrade tests.')); + return FALSE; + } + // Load the first update screen. $this->drupalGet($update_url, array('external' => TRUE)); if (!$this->assertResponse(200)) { From 403850336ce17b079c8f4bdc301b33ccc0195243 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Fri, 16 Sep 2011 17:46:10 -0400 Subject: [PATCH 60/80] - Patch #1182290 by BTMash, chx, matason, catch, mdm, tstoeckler: add boilerplate upgrade path tests. --- .../upgrade/drupal-7.bare.database.php.gz | Bin 0 -> 59294 bytes .../upgrade/drupal-7.filled.database.php.gz | Bin 0 -> 77880 bytes .../tests/upgrade/upgrade_bare.test | 31 ++ .../tests/upgrade/upgrade_filled.test | 31 ++ scripts/dump-database-d7.sh | 90 +++++ scripts/generate-d7-content.sh | 308 ++++++++++++++++++ 6 files changed, 460 insertions(+) create mode 100644 modules/simpletest/tests/upgrade/drupal-7.bare.database.php.gz create mode 100644 modules/simpletest/tests/upgrade/drupal-7.filled.database.php.gz create mode 100644 modules/simpletest/tests/upgrade/upgrade_bare.test create mode 100644 modules/simpletest/tests/upgrade/upgrade_filled.test create mode 100644 scripts/dump-database-d7.sh create mode 100644 scripts/generate-d7-content.sh diff --git a/modules/simpletest/tests/upgrade/drupal-7.bare.database.php.gz b/modules/simpletest/tests/upgrade/drupal-7.bare.database.php.gz new file mode 100644 index 0000000000000000000000000000000000000000..4eddff6ac194ce66f2f73ff0a34ca495e54d5417 GIT binary patch literal 59294 zcmV)bK&ihUiwFok8D&lY17vb_aA9mMH!fmfa%C=LVRT_)VRL0JaADF+vDSI&pzjdA`}QxC8iO`fU3J@M}PNct;_@x zND!|QRB6W^cL@@5k@;J%xpL)S|9^+w;ZJ|M`@jG1KPmsW^8aito8TYsE!!rhVmW@G z+qNE9uA{hH=svkM=WqO-61c7nZ}i-~qC5DGypCVvi;=R=REy~fi zoCo^muq$}!t>HQWaRPWbz7F62M)@&Nc0ds}-MY5ze#Qs)u6eYHANEMhdwf8-RW`P3 zeByr_uI+l!-}^nhIr;_tdJh(4_g=jYZ`s6o2zKl)u;|%QIhTh zSVeA-2jW7CWsja_HTFd2a_W0I=cqM`LI7?~F! zfEl_+$NJ+)&?1)1cK#4Ww;I^kTh}9i4td`cy^@hZ$i}SV#{*-y_Hpk>{1^dE^pEg~ zZ!)BdMt{tpg2^8;3N`t&jI_}MV^@ZaHdWAFn}-7T6LD&zYc?s}kF_U$pzjZjIW~MF z8_1d->HD`oeJJ6Qo<{2?fmX{gL5gPQGIx~e^+Jrv9ZQa)JdB}?Zv$g#QGH@c8p^8E zXgVP)O=1Un?e43`YBHgs9ciE|8h<|US8yy2($rUp2{s8V=PA|sXAsl7pksaPkq5MN zE;ho((GT4Hg+}<%@~w?!TR}z&orwT+A&TLJQR33lBCvu>2n+1N(;eT|V>=;BoS_@L zV&ZCyvsz-DtE!wL+bq~i#XFT!5h+|~3k0JO%}|u5+Hk$%bSGRiMZY;CN58rBkQ0@o z8M&5ADMo%V6>nviWzp7fG{r83F%UjP{0Na?@nb>>LH`yQmQ5<FYtBJDqRqT@|g<#k=C7Qy^vA}pG zeztHSX0unf>ghqyB!fT~v@(MLe#5@WAl$0iQSdF3z$?~U%kwWk7US$##DN)MBs^k7 zQzwjtPae7Hd1@$B2_T|1(shis3y#GkI~K8TCX9vcKD5po{X)gGw3u-8N9#cs;!e+V zKxU-}PfZ3y#w}ad4Wdxq<%VKjdMGeAp=DIWue{(0ESf3N@-nl>I&|3# z1&zj_7a#~Zw5GObra1e80##y9N(aFqpWDSiY&vj7arv=mXjYaVj@x2esy727u`Lq` z45!V@JY_K|g^|$u?r03k41vU?l$zK6lVe(j9*|)TK`9tWjL9l4Mz05;%=gP|N1ig7 z#R0$J4{I1o;h?1)`pasPrd&7 zG9O>oc61%va-N}L)FcLDMf<2F6WVex`R9>b91>-tk@@CQ&WH>L6cUTEhP5#GVJIJg zEC{%j9|f65)rVmyA9pMW#10+|tyqqOAB8py$H5Om8+PN+2Vq>+Y|OEohGaFw<58Gc z%<`DSW%Kq^)@o-)Zd|iC(WW5T<#c+XQYs6@a<*D~xBUa1^!`B#L`*2rSxfGENY0bPD5wTa$3m)f+7 zGyIDXGZ~%gq)1;C&rUpCGBt9jrbMiY+rSfZl{23_4e<0#8s{yCkTMCdU`Jv1%{N`Z zJ@VyXdBq~Gc``X0|E{-?F~)*#ttq9sKgaUMXrbwUvv3k zU3w?Wl`F1XF)*%>YW@W5i&?{U10Q_IQhRc*>~Up}fw4z{(h#v<$Rm^+uKaQ3k7vjq z9NRMX2yYd$hD~$Que@>Pjc3Rkc91R2kOY1)chEfFD{owRkzK{p+lx4Z)+)WS z#+5amA!~4K%Qz#vRm2)BW#P&hSI&5LoKd7Sc&1JfXB@6NLbrX?Kl>$*Yb5>b+&!U|dFx{*>C6U*9-DNgHabD5SlLB~q)>>j-oy}mBmZ)&aPD5T?s{UcwO~F8=ikUICp4?p zY){MDb`8B;x_!e8 z8jrf|IuGL0^x{v^MPc3wiZR5m+Y{3|?lUj^S-4#Fm9$_%=8YfXtI`)1?)((SwbT|C zbK8zPn{T6seDeH6evcWSPHWt>XcuS*mh5-%8GD`hu4d9ea%yAZu1pF<-y#wu$c7UF zAwqb#qgw#ttN(MmzcrN~?k(Y^Y1S;q)p0jkwwwK(dG~*m+m<^TXFD?Q_8oLIkqjJk zWVYVXRPse{_HWw%qFMeNJ=|M*i#^TrC#mJ07WuQ(a!<=_Pd_XFR@$GYunvvb)4!FU zyXZ+K92ESj)_ao3hj8>-o+nizA;RHnyDOQD;N#bJR}v}l-D@RRl>Xg z$n!?MNrVOTRv^?H^(GM*IF9*}y-{xxq48e%_v<`fZqk<~6&vrn=}RU!UMuhOBuAsZ zB%(t=qm0iQbte%Y0vucKP9j1CJhtAQM2tj$Y`rsy905dj)0aewM2Kv?Gl>xS_Z$9Y z|7aQh5)oq456k6jyP~@<7}pXR0-Y6!kp`Vfga~v7vz9MN8uTU+BjPy9Fw&qmi4enrmQU_*PZ}2K2z}zNEJ7c}=acg-#OTFyK*cht8ML1E7`j;qG7zrYiv&PY^%dNZgYC#g2 zOfK)$|HEGMd(y!v(^S%(Xn2l<5u_LN``P_}`}eALe*>Qh<4CFUZ@$&<)%*Xp?nmGH z_w)N3J`6YCer0XNalZL>Lz^w`Z+sGfpn9N>kM95bD}9!ehSw(8*90DiK5pO<6>euf z=qB%7v-=x15`~XKD>eu>-&*%-NU)d<#6ONUWu`n{(eTO?Xk*_5kBzYh+~~dzTk)}= zi8!*iBz}DT%D$h=QhBe@ASWNp!dsltj4F%fRSQ~;}P z+wNy#uGugOlg2Tlj_z+BT|?g-Z4d-Ej9YN@I35nVM|A~0LNOuZG=}gz*6KAE0hX2_U;15u2l9UNm5N>2Te1id3z0 zXEPJ9O5rcM$luT)(o`cdBS@B_5KY|dbgU*Xa+CgO@q>kMORZQ+gR9L_gPRDhyyc8T*xu zqqoj~@b`~<9^sWR-~{B%?+4q}O+|MUN(i#rgR7uQQ-Yo4E4;Hee4Nw5D>c%Ka7Bs6 zbWLb!1TNEx*gyT^J1Zh-${T{bB#sYMF;Rh0a_2#Lu(ry-4-fD^@(?PIz5WGj(G$jz zTvDw?*FGl1D8Fer@|#Os{R-IO50R!sM#lZx;pO;M#ULs+%;2r`)7Q}eNbOj- z2pakUsP)&DV_Jp|Lm0w*=ee5=p+D6nHYAT0`OMz9_Jb$J5KC-%+;1G+vTw3hg_^c1 zrkt`0vZ#!bjbEGdsnLi;l+)LG93G?ab|^HMUaa;x_3VQhq$3G*^Mjmrx~f67R>e>F zS}2K1EL)(6;J(`F1&cPK@y(;*mk-BZE*u_DLiZ=UDtOpAo?GrZUG&yPX|KjhJ*N-o zZD(MS<5)&TVQ0w$(aWssNJf?R73OK3r?>MSb6&JMlwH6P7%0Wh2@Iqmd3uehSUP42 zIVsPYuQQzx7wM5G$M)jg()^oK`>hx);@!-w7glsTfx>v%Kat@UgR!W*TKe7TQYpoi z)^c)E(w4uVmvhQ*3GI?G*Q*}C>hY@{KaU<4IqzKhJkjS>(_S@gDNS1rHL_OKG;c|C zHqyX(eRQXZQ>&UMHF6?+cU8z&g?v@W=TXQ49sx6Dt5O`alhs%#L@RbN&0b=|oQ_IXb0&R5%@Pqx8zDY}XsP<}Z& z^~iNe`f3GSt$?#z0Rvl+zN*IlsmALfbv5O9Vxd}8@Tb*b)l<=Hi;H8Y;|?y`mbS$(69ERVC8H&G|N{8M2!$pZZaG5?%Z`@#zSCB%rhO!+FR z^j<76u*{<5qxj6p8CQIJfs4nuF~i2}YNon9y)G+fFML+wkioc=y+EvJk;RjiSF`wFF7@lA5B7USuT=JEt;!L?luzP2@N{K`lHi3SUSI zn@#R-{;K~?`77DOfBe96od@{CumS@AZ1A|9KaMW_VYnv#bF|?l;^PWV_cKM91&0+1o#X(V5Y*(6oLFS&czFdATi{crOLk{U zudA`%TQ8CB%fa$=Jj&t1g0|Gjq||1X5Xuk}ks-`rtb=7M#z@rBlV%(V15+qlbCy{c z>*TDk>?};zDYZScQr1f?1UQ}da270|hFH8@PQUpy+Tdh zJ(_H{;1ZR}Lbk}c@~$lZg6qvyZNNnL$;G02%+ayW5T#{SJG8psbm~r)#3X4R*`v)bn_JLqV_Mf?5#gM*LK>61`3q78 zPW5>jfbpEk!GrhH*h#=5?P!ZPWo~w|Lq1{jPjPheT*@TGUhTnkyp2hWXaCjD=v2mc z(j))sk{;Gw<#Z`V_N5PNto z9iByrSFzn|_z@6CfsS=}F!iK`f;W=aQ zxxRvDkNFBHAw@U$mIKxt`Ua0g33UIHUz*VP`!a<7lDd^kXxuQLPz#SoGhtScVjO7& zHvoVI>c3HbqAhffz^7mnn5v-Y)wn3Lhl>U{Ur)*gX4u`sq*BC}|8mT4mE-PrxKn_; zoAA4_UH%t)#HPoc@~^?}UcpxyG|vtsv9Ek4wvGSd?N>j!4~lyPiG)wTxhWG3p&vv$ z8;E4z+vhS34yStR(nY{2p(n>)K#4^=o&Zb4yEGMaB>Jt-UCP)krT>-a@BEqKLOduY zgPn|7o<-^}@A!KV)2ZI^P*~5=?MwB))BUxvvuxA5uBV!nHpu7lgh+^ea-s(UpX8 zAnO)9RVR>HjIz#D;crd&do27t5&oVEf6s)!=fd9$;qPS?!#_O_V&kHjC>w~f6Oqu< zi;|)TD-A-idnIB}QP=VGrcjpoSkXJ7ze86*B%c1wjb-|GWV*6DX{;pil-R(+Y$5um zV3{zQc}fIFNQjOQ86he{M1*Jvkr1LF5)L(^RFQukrf5WNbeLC|bZuYJ&{KAF)Av$t z;H$Sn#43y~iGEm|omhY(!Gj5@OSl!`#f+^Veq6!;4_k{#DNAwQ#3xgRW%w+&_Fl!4 znYcSn{+uL#PLn@p$)EG&&qea*GVxQ?5Cw@v#o@3R3R*cR(D;@d3GuBo5NM>0hs2~X zpgEvZOihMY-h8{cSHJBH7Ne4|xDB&`D+5CtA~4bs;usm@a>(FyQ1AYn1CP}GG)CUB z+sc7>V2{Af-pY%*uoyotSWL>ng5^}+1=hjO0Y;{U1R4j|CraF+5h~P5s3Y`%u zyihSMhYCzp%9Rx2%`_IyE)}|-|LOCZ|5=6k*G;ns0nzQOU1`!)IFe&c3|UFch6smz z2V5pc%<&jY)y3p#%2(B)&{!pZ@!I9Q2HX=#5Nw^ z-+Ul8F#-jq8UeSp9*!PSp4_8{i@AH^zq$GL>%}F{?JR>etrXT$4$OSuSX)%V8k^2S zoQX}cCJ94BzGqt&X1E_Rbd(8ZtFYa1og%Bg5UT~O#lV5JtO%@FG36sGZX`_L8vwpt1Xg3rta@HuI}X-*wR*Mn0%MKT~`)O?r;8O zA=FU%gplrpGH^VUj46lHjp_BpfQVYi z5E0$%VUW4@lB7DwFmr+0$i$h8)CO1lg^DB5CXI`q#hY~lJ+u3p@4w*o7`8EHdvxf= zB;B@xr(W13w^~aIXf_CR zK-_~}ynG=xN#o*I;a**b2!60Fg>)}kh%^7S;<+Hgm?wlu7M71E^Yp_Tx}CHnd4?Wp zRx8DtJ#uippjfXZ+NAOJn|QNne24e+T4962p+-CpcYpRF+T2!jXW+qRZIs4Xfq!+- zegCtY8%LD1$ztpepfjM+=48uf>~lq9+;5EJ3nHyjxY-Z9qY)gvV$GLol?KX(6l}#QENsRxtq?>=66uWEyg!l`O*y^%Za20!q5Eo9jAh+B3^V)-oaEaUn$h zj7emk`_ z`_kZ;4jmksQ1U5o5Yx(AVURUS1LOPXP9I@W*Xl^aV(2kotz-;1Jb(KR&)?3qI;5?# z7Ab3_V58nDmD)DPAOph%0LcV~Psxo(i+nyQH{wQVc+enQdlqSDX$);ikfFzNwQMYR zAw`I|E^x9M8rQD^ufSnJW9hvD-~+HqoJ)rph(-M zq4IYcAh`Oy<*lk5mky^sfchM%9&pSO}!M8a3obJoP8j&pd6Ia1s z`S6cPHh3LZ3*2BwJO#6tacOFWvALU$^j-ah&{{xEhvvpTm1hO}z_TIYxfG~u<@?US zEm(FAKqYN5S?cuO02j(jhUrrF(0J&v+i@m#yDX4nTq@ip44UHoGRd|%1q7NcW=@y) zH$PcE+bV-D9Lv=SH_gz?SjUxLP*`|Kx0OGR|SZuotd^%%yBarcDNikxU@F$fIO}sqBu_Ow$~k zU|sf|>sii2W2yClZ*~@osI!yk@G@1lQD>nW(U0|d$xg9ah)j8}(fkevk(uauN$-^= zViaOh+BOfALX(<+TxwC`WGY$2at!-uA_jRWO@$qE_*&U|E|#pqXTT2JyKJvnXdH%n z`TLV+(v>ee8ysx2g-J>phv7h#H4c;QGw_Y}!9oMOxBkXv@)ca}`n>o5n+5H5g}B4N91JCV3>bdngk~kv7kQCDov&I8qn) zH^RHXzH{w<6Q=~1po~}O>)N#zLKNN>`3$h-y2P-kZrchT+#NhIm+v{n@zcANCW8y)PcnggxqeDlhrEjsbFz}8&^A*OpfT>$ zlZ7OE;hEiLP4HTXOVyE_3@#Es$wcDA9qmQc1x~{)+psj&da4cUNu*S2=wxtV_(>)V zUnaAw^xp8oE#i&E(G;4zw=lGhC9zn*lXXZ>Z}oJ82H!th!7k3z=|}9SHK#3|H8vSs z+Qm->MrFz(6~`@`d3Azwg>qLkzNfKe0zW?*>upRV_yI~YQzM?TZz zsd|CamQb2rz?t+kdR_q2O!T~j{41e>B`rHB(xOeol$}JKmR`V2w)wafakv;9&T1v` z3-xu%*?XzxS;(at)WqFvg7J9VUSv50AxC^FAGbtD!R#xWC&j0(Dhfw|ELvHW77*%( zCD@f6*wg5JK0c?}=zW>UoVl^x;?oZ#Ewhzf3|!QK`=BO$EW6tbos%)IBt#x*vTcBS z2zL4-0fme`*-y%b5C=PaAblwr-4YN}QMzbahXcgSnn4ML%r<)Hd7A_Z+GerH?o#{B z)}4nV(Af-)w6ZjRYn;ksyM}IC|Do$9EFIj_9p9$E`=PtLNllC9w`YhfIEpN@Xi>O| z<&j6LNMp;JWpS9eea0zdyj0De4Co4;7mH$Y-y^RWv)Zfwfw&J#d40QZ-en9#kk z$-d)V$fX^PSz9h|+S3@^(#N#U(uX)(C-k<^GJ{j$UR_l^zXxnO06S3D^mWnhm&B1Z z{m|7N#Q~>-TNattT^0!|{}e(muX$dDphgIs&7+Mjf&TCS%J{Wnz+gSN9`|e6f{0=3 zzK=Sa2}pO#Bxj%YVRf_|G9BD<$TVA}zKr^25#U>xC5sdv6<6>?3Zyq5-2*(uV#~Z8 zuL6G4RI`uqCk+wGJ5c^I8IApK`k}JBQrLrALz!mRP|AQ4x7FP`Zg9JGk4`ys${vxz zlWy6Ah6-!g8a`(cKIJ_iKqt=~po3dP32p0&34ce&a=^i`i^JDBt<$Rg_vGz9=Kr;2 z45kwRqv0P8VCpP(&;uE0irrQ}am0PW6B*oK@<6IhUDsrB57N=K`N|t{b{IXPMMI8> zUV35sb+re7Hn@e3S+#|Zf-;nu7P1GES80khV?Z4{ zwP2P#DOP^mS$tG|iORa^W@*$3xB93Q+j8VmX3}hMVf|UQ4$w(-7_RMlr_muaOQS=$ z^^(vrxTyLp8&xlXj??vQvkkKd>3Ny|t}rsV;PxyV+%AC;FeSbO#}kRK(zaQoB=5f@ zunaEHJkJK2Pl6>?7_JB_sitYD6qlHL&Pa*IeK<(T7TZ6Kl#`n}(#3pyh5(qeEzxMu%|gC81+*1#{-v;BPr}to=XiHH?)P?VUM^lR)ooAEAOTgsB4*XQBEIe}d-al(-3@+?C zuMu{IsSQYp$eP$r0*~M&9DA1r$lxNc^BR#?{Lg84#I4fci0`~4XbdjwIC;^eERR$i>otFfS z!9`3LH6o_CH{&!k;#O&B#CKj2G_0+{JKuIQSS`sA%o4@j#4r>=!Yaki=@sf*7^mJ0XaQabZCLQOj?*G}Jpz_uJ>(vs|T59q0e#rj(w+dyKvSlF`vQg)e%HjhUINQ}#W z(S2igx>=m4ncF^tLeo?Rw-v1xW->?8r!;dkBRvL9CJsF=5??~iI6Skm0%%h0WuW0j z-so|dFAqCufur>wJDK?Oq^Oo~@+hVX&`CCzA!lgop{*b$eST6j9X&=&CSW}+hnUDE ztRQeQkI0bo=o-l* z+-V2Nec{fqiSFDUJ-cL88tFr6=)A-G%J0AZ)cg>m?;U+( z69xP;)AH$`M)dMCx@~1anJpF4irbc2!;NOV$Dqu_*((GP3@;4HmWm$4%`-rZ?$m|g zD2f~CN0>l8z;hSyV356zHT)b_)=&34KGd=-{v>rJJBb_2`wzfO2tPb_smv)_cSy`_ zVC_p4m&|)A4ae``Hao~8YEl=MiBm5@G#+gI3FRRml*vU??5h{plvSB*1)QlgxoGBl z49FHWTG|uwC86*gW*-Q%SbN zs&G5Q6qG2JSz^n=jX+jpwJ& z7|PaRu8bku-JGZjb?UJUg6O^CCH7t*NphBMH&E(-IA0xQe+1{mNEN~*88 zPUzS60>0-m@cO@8p^h@^6;;uTwVglZ=F^sYOpf0~lez%L;L6T*eF9F2W$YLt!&jq= z0ti+tdWf5{(;wk)cHRuYnso_A0&o4S!-84#_J6fR9c7kFCPk)Yc;*o9TW6T`ioNOl z2hxcBX<%F7Ye6P`LeV5+L-v0$Lmg#SGb-g1ovfLV|G=;?(^?$lR31@0BYj3qb_t?l zUPu+-WS_ZWCo$+TcKW|SppLSe3o2xxRlrTEy$mxn5v|wJj*gI%&P9u7rN_t#2SdSXQO0Gg^W4WWIAS+x9G{)vnZ;AZ6GGJ~9!~#(2ji$iX$!sR zHt_;q;pW&I?mUx)GYZ7!$>E;&ywc;Pa zE70i!(B*4fY}GfqPhKkoce=nofzgkHNxNEf9jBJkV%YVWHL`mNDzb)<^-+~QGLNQN z{>ru6EXc7O9x|HQVt(1aCtyM-PJndD^2u zr#lg;$oEO0y!-Hh{{25c0Gb{1vtCwNzr)SW5$F0Lv(9W*H?sk+jSqvUi^E-yYc9mvq(&A5FvRH_)T$Hg#3Hn#fj- zEK=~P49}wPd`+)9$Nh%T>H=_k@1rOLAqmJvh5mU*pbfhKp4ZMj+9s|h(r)~_faquk z+-;AxeT#LPdZFLD(eCM!Yhx(&8SdF_DpeA)lSwxv^Rbf;WgyO*6(saj3`t}o5+T@M z`B4=OOvqOL<2o!on^x`co_Z23=xYCfng;9u7JbyrqEA(Gl$vf;?NQ;O3}D@+xfyug zH->cCiOa89;svk8at1@QY_i2|OF^sChUNg4k<`pGQkA8EQgXBKnKTMN^gjKuCG?zJ z4p0ZMkfdf7k}7A?`WO%|#LtPRvIvT9^$|WT>A+LTv+4jAlR|r-);3PGdu}a8pMgM-@vB&gg(HKO1272>ND}JDR)k`o4Pi5d8-+IAfQX9b1jMjX+3W%j( z4c_939mi!JP2=bz+*gGAFBwb&SO(ED%OK3y;(rEwPVfP2I znsTPK6m0-&DOz?dWmJHqFh~FB@SPi#GW&C$%3_H(d-e!Q=lF+XA46yW>l0dbeWI!j z$7otpdkoV7>zQ)}Z^=N6HdU~xf=#bhUUDk?47Y57tcsfhitE7SKad7d3vlTsK+zIf zPVNWL2CxXAW%rUakxu?`(_ACd&M6i{!b!=>RH@TX!XXWj5|8?DYEwmp;j^)e3XzHvdaU@Ni|exbUjMX?e5va7 z$8*#x;ABq_9u;Y`+)5TF3ul*jQdU2kwvl3~bVU~J{s!NqD-QY03kcA+_9K&5>jaJ` zt69V|-zzvuPA#r#i`OaP{^w#4V#4^46k#m{RW{D!+3FR9B!?Q;P=zJMS?*(U7)1g6 z{-P;cuk1|faok5RWy9h{!OZj#Mzruqi30&nJ~y-z0lKR`k&b7M2M`h;XF}r3A{0pv zWaC!>BW1)=@w1uQ?9o!7?kKwXh;as!pUl=@jCEtjpE{QQP1XFPLlgjd zv~{h?2_jwX3tDz#ew{c$B*sRc02?hKG;kBk6xu)i;7hHvf6`Bkrl`kqZ;}nh*9l#n zEWL5^xnY$nywrluq>mBwNgEOLI>j#kJXrk5cHf-IjGy!|fkCp~g z=@Z*Dw)2`+1Tc;sttu$23(|u3VYPsMEE0ATls~;lIO$_hev%2w*9fPw_m=@@!Gqbl zf$V%r*0dQ&)#w}%gd%?>?sc>rer7}6^Hw_(quqD}rvkDO9D0S(OvryxqCz0ALBaE~ z{7RKpik}@`<$%_LYbQ>rSuE3d>SKU^k_qrv_b^Wv4>OoRQ;6=_7S_My zkF@}z(U~w*ZtvMNiJ@rt$Laa#+19fn2xV3a%0meLCSKkO!HM?S62f>7i}_zsG#(y` zGBN$~D0;MV3-}kdmLSN=KVsrp17^}sz!cd@nE<<*vp4#5>P&?lV?__kw*1icL$ZZh zS@=GgeG^Y#uP~Jfva5y1Y3GN_>me%j=oL?;Bku9k^$Jhfl=_m}GSboWns`b+dc{-e z0DL@ky~0x_1Rq6u>%`#eflBuL6{CwYRB6~_mVlyK!9pSbkBIwrss~tqF9qoE^>5r56PXR*`0zMS-#0tKd~o4Xkt% z)s8QK(24d^?09>HC*v#ZIXnMktbPKiUOm-u3Q6&-3}WEVbi*o~mXMPh79ilwAiHmDtzKcl20?TdIL9=Yd0_T%F#NkLM)e{P=5l#NKm< z*NL4$(Imy_>f@@wEVC+5V>@IOU===^1FC{za-~BW^ZT3dZ4Q57FHUdl28}28JkcCYL1gct6N=@5fsk1kt*@>i7 zG=sf@l_Z8=!U)S8rEzCkzP_tj>sjX&aFq9;v{GwJcKNWdblNbFrma_4%Ea1h80J-h zlzA){Mpu(OF-d(k$+NNcdL}tN)2^mLEjR7I`oOG6*XHNcmprbt{0q8I&mY#R!O8smcrts1ux#0$x(KV3)KmC$ zj+=1>Rt~WGSi5H-$=GkUi?S5Ls=g)%YQk>EM$W@zp4nGvayM6r1ga@^*Hf?JG8-!+ z5M36^lCnWfwyQS;vg{>AFmAkeeKIW%;u_o9pB02Nd)g!LTo%HTrH-c3_++j?wUn_mlI@ll&ETu?>LEI< zAHrTpYc%va64?e?t1m6bK)EPfqwPU$!IjOr-xRW`*GQ#qYq%XR?{DZEbw?pzEFX_J z3*r0O3U-!r76{kRj<^tnBk)`n!r7IcGCLVdV6TD!T=i`^mT;?(j#nzvpx%1!zUNYT z-VaE}=sGi~GiyXuvNU+WYOh<)ZJZokE$v5+)1Tp$Rr9Um=FcZPBbuKcXJ;%kt3S0c zlze|XAWFXT<)Uaj02E~wcWR(0U3{kv6#bHV=gUP=UrH<$p{UWe4-NzjinOfHeur0H z^_eg~m&Q;*nXYAKtwwI<`$YGQT@FUXo7r=T<9m&k?#{JxrvCGfj)(R5L!r527+p)N)IyJwR*^iM|QZ_$gUw2V*|BHF*f2BGWF5p zy$>zoVljAJWCQa{xd|djgxoNuq|M;uMiw0t5$8u4Hc1|Ql;5($^WgBSJmcdi#0 z#~?2oQr;|MawKmL112SfNUFeRJi`5!A^Bd6Hj;#zr5ItJh(``EyZ&9FL7Lw*bF{d7 zP}qItu=8}k;W^{u@%_!eEb>{|yMCY?+{4k_m`iB%4E)t*oQmkxZmX3zd7yFAQA69 zE0m-#qNO3y(*tvhAd*ehKM4`{k$9)J7iAl!L6W%nqEIrpQ*yOYHr@UtO6bLt5Q$r* z@e$v7QQ#O{k$Y{FO_@Il4$s`4#zxdA4UOo=i$cb1c*w|xWltibgm08-m&S+RR@{DQ z5Sb4TBH8HdX^8L`NePf7T4rF9xcj1jvKSsvGU3{3iL)=bN9&US)7^?UDi=K>Ul~-A zjcUeSW^=LnmPc&zi0Ax7FUQ>eFbo55QQTlhJjDXwe8Chx-fJ%+GqRPZTIQl#_L39<3H=E}h}OqJ{B%_ZgF!fOQ2adeWoIYn}-r{`Qli+m<|HeG{W^CBSbb;BUnMxT0I6 zX7KafPIn%N^5h;tQd$9d8A2yTpdpiqPQY0P3W75YJt&0F?=?4%d5=zt*it9JmluoI)6j@aLMOqeTZEVx7E~Vl*zAVx*k^jnas?uq*xzFVtHQSO0 z*iEGMVnVBjCG0gdvo_FBXqDgRU+x`w1<2d=0?&oCONp&M6iCsuOjN(J*eZV z&0-AV8wvvrXP04pk27d}jJj)*sCy$BCY&2pSBMF(7~;`X{gGhfULY(Rao38H!%E4Y z7HoUTi%uB$1G37_gSfOri|4v~#D$ZBD|8_vi>S+l<>gwFZuQMyc23@wMXZd<`+?6{ zXyjJQVxwGniFgDDRbJv0EJ_v|i6lsHszg?zO&TEYEvNSg2_5np2pMDnl2rLT5geJQ zxf~pS-Fh&kf6IY|90@^w6> z-Yla;#G5q}I2X(No8Nt+{FUtg#yj)>dIvvf)-hH+2YKlDR$x7nP#%F9{fS^nXaBWw z@QClzq-D(JQ{b76?LH_6*Y7y_vpz=4RVzqIsCb5li_uJ!`~*f1V3?l*Xxt`^&cExv zWhn9e205PdIs6P!x{2N^0F`Ba2JqoVh|<@Jf5eP=9}C{Tdb@#7P>#?&KG|&w=-HvU z90W8AJiX|!pbEQy0@t-SdWoItiH2#ECT%O;>1{^|Xvs0chu0(I!{B42XP6@AxDaf>zo#4cZ^sO?m zotE7bQ4cq>6mI6j6L#~=5@At&T>Wq$x`*2aOCcw(c@cQ1fO-DdnWv%C)1pvC(8(5s zDkE|j9i=3W*fg`bOSsoZph(tqgGT2 zpk&&mA#&_FM7na(qVXOOM6!EYW)*z2QpQW+GpEg8j8P8HmZ`Nj_@w_7&t$!IOdZi0 zw~I@0cPQ@e?xnaDcZWi8%HFuU7k8()yKJ1|?(Xik@sD%Pyfn z*7JKtD~CO4eM${comxs|nsQRi#0@rk8KV2o`lK475N)%>z9?ITx5}a9xEI9V;S0sm zpmeqSkO;C7O=1Q*?l~kF*?b=Zacg!61dG6V-Bzp^sJF<|mKRzD_WIYpQn~UHvP81x zVycxp3>95C<;Iy`vsNUEpPLPRIQFDL4n*TWm;^lP5j5g3&zj*Zd;E)ONE3B9RSQz@ z?Nm83ru4Fs=I?l4weM7=nCnXLe&DIAa_(;D&QG9ZZq6Ui~nKTTe4DCp)jBUC8zj=UFytS(YiHEq797 zocv@BaVAeKrjyonG0Tn!7kEmy)&C>Zl$Ez*aO-SO|o)9=DJYkIcQDpcHxiZlwUvR#WcvuMVRYUZ=MGi9ss70 z)qAsPyB7d6*p1dnS*FX#sgwB5`AAdcNIS94@J=^O)1sV|?2n06QBMFjZxe1OgjUn5 zIzD+R;gEppc7FdK8?nzJooWvIoRA03u z4zY!;Aale?e&)?a8CLxQgb$Ptp4j1$O8b^@7wMw&*gR zuf_LJrppLkoMAwpae(9jtc~B>!?vWaQxb}?0kT$SSSKrW?c=u=Qw@&Al1Yuf*TtEE zn;t$By)B|~r_y{rX^Qd=u+F5{M0%D9TKj%Vb~7?_iqC;i$KIi9qjurkw8BAO45@Y_r;?s$sd4zPnMor?W=BvOnV zcO-F{=>rM~94AF8V`@71Wn3P-^;2lv>RSrccOx7<@1Bo1o=p8^K+E4%e>DMx*H{@-wZmOPfIXF6}xg`i8exYx$40T*sF5 ziX{nztrvl@Mc-?*pGFT)`E41VeU=+U`>=&^3d%iifPv0w#f_(7o%aIdN^y#~9uEnK z&4!6LGY+yD)|b*Aft6hfI(D2o$|pwx5&9P6+;k5N?|!-w8L(k#DAj!~F0 z^R!$7@J}ZBB=1>yRBzXS)=?&sziCB*|1#|ku(;H!gIU|lmKTJA0nd|BZDVZ`dVk9= zIk7uVP6QiZn@1~X|EApsHEk_~xM`gfX)#uNvl&{pijOn#Hoy)JE^Hk08di2Cv-6e5 zir)m|vw?h{4pv3qhSw@kV#NyF{NPdFgSX?yKgt*7qSOs%5jU|7`cWmr5b(A+vb0uC zDK89ntn6m=c$`SVoGY$`2EW(DoE+C79K2r7A1@08x1n&oNqi@H&vLYy$}>*Sr`b)f z_Au&d*T>7%*Ml!n)*3H_U9Tm4^{VKU6_z|KC;a5m87o2xQ01wLNulW7^ZwGU9~I|JQD`Q2wNz$^j7VLGCyDtm zndcq$zw8a~jBmOePc589Bw4WC}X! zlVIE9koXs-5&DO`yr?`7j5$>+2P;#&^IOp$>`|On(4E{T>%M>iQD^DyY#Ha~Q@!!g z>?ZoW09rhF|o~`B7MOgqnJ(Szc!0uHYo7qOl);yp@r3P7;Q^!b9Adojd z8V!hHyuC-Y5^tdYTAGqp4$s%3M=mN6L&)n;j0(UuRcINBr+G-`W;PsFO(96t4w^Uh}CA5E+8(qDL*_pybT%>*(y%F`00y@WTS9Hd2uhOi3jl5o6eiF-;dm&|7 zS`5FmhQGKKYKdJ9_H;%~7~*Gm%?1!&!%mBii2L$y3k%RzF_S^h_YF0INhJFkzqhBV z^i&zK8(_@#199VUMa_)EaAomsxicBFZMO*4KEz_1AQ};@+cH}%M<@1kNze*mI*Z+f zjvk)B?jD54H@03%tUXu|Y}^uPXO#h2`{-nr z1DVnXC3O>U!B1a2zM{&MJ@O)+(h~dS$-Sb5BX&?}MKuyv0q|-ij)W-#r?eQ~{qYwT zIP&Md*%*Jz$DOdj4{lihWt?59TzNYpK;OyeI4*jL=XcocWp&!+>r&loLB_i6xMezt zA|b-{Q{wqal#Ngik4WLdp||NXiTnGH(a>?H?2`1I!(+Xq&J>9Q zkz3u>Bhl|1cPLFc$GdCztAdCRmOLvl;lTm4*XOWzq#s~7*Su}+&c7QU(w%zbdg)XPkxWq5* z8s?GI(PwkZ3S11%w z2we!%t3!O>om~7bU#rtx{Q%x8T9QO*kNo;ybL^f8zcGdRQiM~jF(1Ce_px56+TAAY z>~HBPiIoJZP1@vYMcSaJd%ejGCB*WRqfFy;xr}cVB0B zHNe&G*Q~_kn;eKyFyDD5V_AHR@shwhRQAWvKS($`2ze9@QP5A62ko-}eKI~8fXg4& z?ad0aG~4@sYaM}14REU1(?pHYEIYXnIau!|eo4F8&LLkgw}d zEBfE^UQ8E^OFG%~=aC!nkBzW>%uBK_rp-Y^mfPyDLoY}0G1a9n?a}3)G<-R@r*JD? zc!nD90V$x??Qbe;>63QfQ?4LDw~FAbYtH0?z!e}B`9QDacBKQIQT{<#6xl>N*bn5= z+06%MTP(Yn3Qa1nH5d#(`Npf=_G*?!K3K%(Ve)n3F)C}z`$Rj7Y3@lO>-wOUCU*_@ zG>mFir7Ssb8?L~*9nJpeFEonY+cbwtneJoy-+_u3b}f!8Kc!1$@IL{T)Gu=!!rEMv zqz+0?V%K)yu{VOnyzrN=Gt+j{X)84shiAJ^q{nL9hI&D-vi_xkvcnf%7Q0YCL($>C z3C2avfZsC$Z@cedqHq^`W_edK5b;P90;eG*XQQr5k+=e`jF2#02$xqdRqFi5A-@vs z3KiCp<53e5-F}DiwJ>m2Td>1|ShSDI57Is;EVQ`R;!)TReH!ZPDb8T&MndmWOX|Y9 zAJXW=En76*cwKRpV%fwpn`K?fpP%6`26inMb~z+FCo&4y_~N~W7`9ozA$^k(SqwxS zyGWIEX*2cNB}0E_bZO6J2YD)iY+aQ=NFvu{*R*oPtT@%38V@ppHTG#HI0E{DNKD`V zLotN!{-jAg`vNh!B0{e~HB%FSc0tgv5x2!@t;|LZ5|i(!ZH!GuoX8-^<&Kbe>4+hc z9!_YE=)a+Y#@g;uXk&c}4&W^;Y#W=9BqjK!&O(>cog&VVS^s`)#~N07KLVVH-T_qS4k5=&fy6Rxp$U`k zjJKpE))hjShnZcwI*Vt1TWH(xazaDP*d#KWQ2F6I0zsa!)i}CD&}{o4C4g_K_jgyO zHaA#}E|mehzJESC`8Kd$qsLH~x%b1{32tfDb-CHdoV4Y-K=#gyZ z6YX!BnLjPFwuEbQe>$tl2uf>>I2+Tjv#z3$cNtI|*XXweqnv@}7WCOWyV}Bam5&9t zeO!@BnA@Z`vEK4E4wMdrKw<8VS38xrYv2SfJ^tH(&APA z?b5D25nIl?xT6{R#YmddM6O-n+`7}@lLGZ=piFM&2pbYL65B0z956I;gc%Q z9vYeNyXre6Z{i?}&_6Eu>XUor~yhz|ev9GL1ZaNZx++P^e0-XD(%3 zHLuug7aUr`K{dVad{UN7(}$T`JyJm5+d2tWsDu9Hc1+j#10<6Lcn;(pWCUTmPX>Oe zc7NX7s7OuF3)XbzGPI_Z;T?dKG4`({=H}`RIU7s?Aj2lrMYw)xf-($a^{X>n@0Y*e zKT`Y@4VaZrDMirac)PU zkUw8bg3ZLYdy&JAB#yoyKobx(T}$_ngoV=BFjcl>fkyD38SRg&>%PrJtUfcQ)q-{w z-sMbfH-;+W6$J*ZP>813atG~|0zV}c+Z)u;?h#Yytg zGUm#I6g%aJ=F1y2n8oGHwMN-7bQbv4$yOF9`hpkSU_PolZVP9d<(Si{02I1Bvh|Sl z^IE}!&kPoov>RjR_c2AMHa+0`)A8j>r^0&z?cG{gVB=u9&hFpI#on&+9x0!p>wFHM zKrnT0PThC5L5>~V!zg(7ba^i2fxv4{(OxTt>BsN+y=G&!B?s*PaA0L%qjk9apC%{; zf?HMf70|Y?+Y}orm?J$4ar4+W`b;{e-{{g+xk}OwyvhT)_PoYNTMzd2Z=IEb9fu;J zetQ~DuVvF5xzZE8;5;ibuIFWB)>?M1WVxLw8CnKOKsl|tW4ThXi(CDSpe`b#1~G6d z#D^C~=Tt^VjB0^qS&N%W0*v&nt0xBYRY(79h+}{wP(N6Ol+ShW-pw^DcgT1TyK1_vo$zEqL;2YB9uzo+5s%1BR(IvWr&41lbSWe}+k z#Qkmrf0LBA=#x$fSI@DiFtvU zMA#=G)mu8JXRITTr68?ld}d#2B^4`e{)>iG$Ve)_8J&zG>8VnZa=ejtM2Y?0s-YCB zu1u;j&Em4gzT>G{>l;sZ0cnK#;i|SC*qMHdEpY$oOIR!AU5ztIs89S?^@K^cobk4k z)?zAZFSwgis6DZbPnSU(#pl^&`s6hj{-kSiRDx{rsXQ^}Y%6D;5+9;-__b-V=p9VD z9CW@`*^9sTA7mvNG1d{Hu_$rr6f9bKy}q7v%O6|tyB8Q(kSyFX2v70vAwdvJf3Q@f zs1e`}o}NEzP$lJ6YTq4nynyi;I}~5GHjfErVJFRU{yC|$f7cpIQa6?kike05YJkE* ztY6ZM`kAO^naPKnRW>GFzgAyvW#Bx0TtH>Uyr@UL=&&$7{vc2Ks1f?(;o3!&b(SQ! zXGx;GvMcFvfy;9MT2XmhP^YnylI3XB$t%#=^?8z1Sah)3D7}X&w*NQUi>+~mvVZMZ zMVMyXogt`#%>Ns;qk-Oqn>6VW%zq5jFB{*13p~RQ%v5M-N9HnsNV5AR<6b zWwgwM*uv{dl}|Z(6}N?w|5rYy@uCe(G9df(o`>`|)uDyr!S1yuxeoSlg#{Wg@lA@%HzLAiI$(*02 zj2PaAbq2-b$LNkJT7Lhz;%b=;2Kg%tmX35%dk+rV@YBSg=p@+A<1-P5Y6Hm&x-uT`&!y#!5&Tj>hB zC}xp_Wrt?PUU9W@_sUC9U^mk%Aq&M{)dw4{pg9xM>X=Q!ALH(;5G~e~u^QzVW!F7f zx+64P+S4Uvd7unv1D+SYvN^C)v@yw5(rD~`l?HTYF5EyfIX5F#@In;7Xbb@sNk(FY z+pklmjOoKs_c+}-O9p91aGgX*ibP@=B{((6v!iwg!4yg@tZ*^R^2k%fT5LKa{Nqlg2*ul}}e1`LCZVf`;>^L%e*g+-^;1Af(%~>gnpuY| z#T8p}DiOOkspmR3Kc3Y?|0(Q@eW|z;*1zDV99Y;v2O^$>Ok-x`j%(;@u^=9Cka&aO zcbR)1%-gBIK|uK;3Hb~E%kfqsCM1*LRgRWOkglzRj?tmkoG)%o+lX|zDhta@>>LhB z@c!xW#KR*%kcW>t4L{Hpb%8}hJ5pPXTW@S06yuOkkqLP{99rEWgl(uKPG9K{-!qk{ zHr2BiIbh13S^XJuns5#oSzQ}%sRI+}=x3Nm;fAN^M$VQLthG$vbYx~jXfAk?z>E$l z*`*&hXSb7m8GWQ80EM37N+Q-h2wjp67qsdQP8T4?@2oML{r%ncpqHSobfJ43p)LfK zN{3#qx%FJaEGs?En- zD39sS{;wX1*#af{AG1AdplB0rts)rDg@V5N8uOq?{YZY!EO>F|qhZj@UFh))c)LP% zW~N{Z>K3YuT}b!!Xc!VzEdcbUs?ph#z3&ao^4qgyBhgq5KQp~@R^F6TIkIn24AK7S zlHil_ZJ5w=CuXR;T@nc32xhg1GS}23=!#UrL(gS~>Jp1kLlV)UVGf8C_{J!0CAPV7(e>ZUhc*5T)FR?m9~2xVQ|+_zk=`f7Xsh z1*RmnMxxdr$)*p`kbalGE^3Xs}w#n>C0v$&gz`ab$ws? z09KPtv~Mkt_x&{8`5s}yexCm!|7!q(US~l|N+vjaI9sZ(`>Mi^`)SH5E#Atx&wXy z9W^tu4^}DYMcOEzE**A@G9NzOi9S5%@qVd%DH?jfbpB-3$D_|=+}!5E5{dW!gQ{~0 zGo5JC%+Cr`{K;W2EHekHlha7c^yPhDxnmrPW(`mg2v18Lu4{Ln$b6!Y@=D5=^;@JL zVr0h%NZ~uICTySM?yj>Nl>8&9&G|291+O&pd@Ha)US4m%ckWc^pSqM?J2R%3V{yr0=f zKUr8nK?^?{B;ACK$$IC)a@tf<>s-utdZTjmu-CA%rUv*FMju!WZ}r%8lqm%vnnvVI zYxV@V)EEt)MIDZN{Rw-@N3^i}H)L~!{9Qpd4_X(+-fv9C8mGOd-cUIPhp(AaGkKAv zV|+jHdUNckf22&%_VON&|^V5vwlZY^QU@IP9&B@Gkf}%$K#6pA_98RGC4=twWrXx`hOBRuoC|5431*Ud14-YO?~mq zd{It~?(ZjiNngYmzk?R>`j|8%y`2c_q2iIme@1(a`(yh$v$#e6DzZRx~=yy3FL$oS?N|80um{Y4Po2VhIC3yGcie*)kPS zar?Bj*>zg6Zsy2`OX}9E@KCae3q01LVDF=qH za~0Y|LxF0kYRQc!+uJ7+eD;>p*D6|w?zbXkG+iKe2p5OBYk|sx(xj1$G zL*s3vy3z~rC?at%E>;I*z17_4W~#WiXLO5uRR+cAP0H`>EC2j0Tu4?ibB)`aOdu@; z`L0td3mY6?8NQ^2if+Z2(ZL(;p zl8CwF@%2Y1$i#uPS5{?4cBG2z&RawAd*e;_SI;?;Kq;wNzQ1IwK`m1`CE78vUAe1c zG3a)j$GpVquO-~?t+xo&PXkx+xYrmTH%m504s04CdZoq;@S6i^HZzqybA)7m-X?dH z<&SMkOlg?4#iYx02^nuQIAjKk530&XknHTdSM_3zKTrVJ_uPEBl|?Y$R4>bKoYYcKm4nhEF)mBv$20-w$C9lp>e(nKl;$~>D>`ij^1IIHeKxMP9TwPX zhAAQKJMm6t*9I%@mK(FG$(3?9vGyprWo3WS=++0ziW0|nCF@jSQfCz|gg4i5BrAox zamv+i?qa1gbP2ZqV!V`o@+06Yudr5r z-}e2|w=dj_?2eCY(Qp|{o%TJse6Ie&Q*X)^i{q0dITEH0%%Mq$HHI`f2yR$4^^I)A zfm;Mm#1Z0##1gBqfB348xPXIG9vn&2EzQ8-8dfp9U-|A z$8S3{N@Rupwj@I4#H>V1SOeaZNt?@W-@_bAF_L$=Pv_Q`bFBsoiL+sj{*45 zHEYhKRwu+@VaFW;MzOkx&k8@e;;8?)f8+CzRN2kR$X502Ah5MDJ^zskY7v!j{ z2*xjwh9d)M$NWpcA|(mJH3fN22{gsr{ypn!b}`2T;Yp zwaCK+QY2IxCL+JCb#cpnid*qyp}8o1dMmbHN;$L-D`-eLOo zi=tiXXpYmJolYptGW9tR~HC}*k+=BRScm^tWeLXIa zNh4%3&UTeF5Wb#Z2N#hRq+_oGWDK`}_BAlAds|X2@*k>TRUFHwbrUjiQ}&EW-tDi` z^~(z~p<2Hv4qCyb%PYgMslDXhj_A*YU!P&#jU-=_yWho^CW&+~|x|B`llS`YfN z+7$SkK`!6Ei_q^GtW3(foO1jp}8{edYGrtQd^AyxfQIQz1ZXU(*)KLovv z11^wpH{gYhno<5AKj}$L&&ioPK#cTkYIK&Y?tO`)D`vOHH#V_0Ge^ zM^9ZFpGXgLO8S+8QTlpIzCYpQTqB%^c^^U25EYhg;Z@W^Xl2x#@kC&twLuJ!CE&f_ zw1cDV5WUjGt(Qj&lx>-)M_fBmBW;b1D+_UI#sVGibNc-AKT0uj3MSRIACRxS84elb z0CUggcf}=QVxG@GK^3bZGtJy!!m9I!o;vTIE1Y66KZoZNoGxeZ12xKtG_LYfU7t{q7QL4$_>212ZeH*EFPr+{s?Sh)BUy`~sFJD2vC4#!4B-Mla~1kz zD0sS!;^pQasuel(QGYaJ%syXJ^~_T*ev<{%a@WWf<`RlW{Mv2ZG|_iwPmYvogk*AX znz=SIK4``@a&Ca}x5ZN9gVxYR@7Cvqi2e6MIKV4@e;7Sj!#4nMFjvJahP8}+EMDE$ z5$p1%yxEYD?WK`gXCpPMDV-mzcr-VdbR+Ek9jdUe6Z)V4Z8%ep>xeBR3ckpf6b+RR5J_X*XgU$%OCnN7V*# z*o-IfY&~TaT^C(MFh4qbW|xxrfFliFC&Ab|zHgtj@|Nvy)1}HbKG?Nl ztG_pocMx5GCe<*#onQ+QJyCHRXWzc*?29}!3q7n^@~x-CvHQ`l0@Z-iM8>grcHNoe7pGiF!DNRXUoh3w@p-BI6h?29cyLCBiU*sS5OuF?} zB64Q-(?f1U_OzXOIp)fe76Rz4jYW(wR!$PGyI2^nptR|@Q5(CAI})p8#iO81US;vM zD+bO4!11Y{ctu$fou7z(!lT{L>wiy!@t)AXPk;`gxDnJ{Jr0}{7zJ0n9&$SA_+Ad} zD68xEJ{O)I*q`{fgc{Dp7g)&*=93ty&@JhpIZ-_aDZt0aEI3wE#O^F19lddTw;DMO z<R#)2QwlQwH_h=9hGj!H^NBkkJ**DP)=0+QiEW572exmATdU{YW_4iBPi#kGZM$wfy+lMIP@^vb>84?(+8bV4?+ab z*YxZjL*0|jc!*=yu-;oCD6QoHOJR?GEgt0vWxL)~>C`VCnKX@I>7<$KC_{G^sz3eg z)R}YD103oS?EVTRmfKA9NjB4Bx$9a|$gM^0P&C}au;fFtS-(GxH+sfx|Ecz#3i7H% zpAGnc5Z|$kTDHtOj#t z#=g)^XoPJn9c3&X{zRui=Q2)kWYEzt>)I zN4pRW{@a3>urP)zNlU2nL?7RYpSNoYWO>_CilY1&;-^1Fk}vU<($Fbv{VtK;p3dc; zjpqO4DI04IlyCIJOTI)`(nb>>mE&mI-)=GBIu%=eNzXNVx=G1 z4`wf};oTt3o8!XS_$z1V^}3hHkK{A(Z#G5co3lXi*r!YhzeEWbD#Fi~ZgOA2_r){$ zh3m56xWYC*6sW|_U5g^T(1*ReryTu13>TUvMtLnU0iM%a(VAENmNk{sO^!_kqJ&~( z*G{J`J?9I;0<3*)@T7e-U%VP6Ky4`popz=(58gMNaHdP~Ngjtqg zVR{IA^baCXO%f;Og6L3 z7Src^UN?%&;lk%kAllhffLG|G&QK1g6nRc2I>w=ljO;ofdCOjY?H1=S4?5!#lC;bs zCl%I7e!0MYSs=Aju1UIj>_zM8pfmd&E}cX%+NZ0MEXotr_UFC&&k?II%{A*WYujE% z_raKMurON8^63PILg53!p~ABm2dycI)Uz8gK(1}P{xk92;2>*!M(F>h3xOe-XZM4p z*+{I4?x%I4owSs~M9EbrpA~W*mub52a~)A-%Ah(KLGGbohTt?5^OBGVqGQVH7D=$K z-cWITIzs3+fpq{{IKlZoZk_X($~-i}VGV|;zPz0`u#2aNeX1KOuTd(Zju=RFys#_mSfQ^SDZknudULx$i$4jxp_Mt75S6O(A`0OJ)qBYn^EgRbDK$qpf za2k_XSGLz3(g7YLdPCIgeHd2(y8BExkH%EEyy3-aSEi@b*?5BORbvTvDP987?IE=3 zSj3H5wAV>8a1K(FD#V|^Pm@OM?pYuGh`G(Y_UJ7ySD3#NIvSwomcxYBWXD+E;yQx7 z{*5REi*6!`9`b$6iEV*420)HMEkFq8@!Nt(vL$KiF1~QRX9$fUPn+pFA^{rdu`L8-HL(7c2iW0?4ET1$`fP#I|My$F zs>im0Hh-SYV67z3*1m&%3lOXMfRkv3TB!Td3(LpqHEQ4ZeoO{ajY$Z5<2mGp`ezIs z_J|G^+0}oOUVdaEHBHQi>P!X%c%(v)oBasjAr7oac_+eb#5-X#T0o9~CM}Yih`Ln= zumX{;c|d?-m@BcYNIPiW+p%v7XR2nNj2XI!fwY|AA7E{6I1yPWclHHKq>hU|ZY2{w z^5h}IK}V73)xK}NAYyzi$B87p1&v~TMHensL+{b$;hHa&3)g=Um)uv)_{5mGPS9w% z#czeY-+vl)FQ2RH<6uk3Q5dpd!0qZT`oXeS8h+N;wR>vD)GLChM;u%Y8_ti@EMFu( z495fSXBTxFHyEOyXl*w}iTIl2HY}@iDdvnGYRmggRf5SyZKe*)NK|~)_qw}njFpL1 za~WLyqv%rV%}Jv=_@ptF7nB?&?U%J?^VPKC0H`sGN`ax%ZWJX~$lAOk>*UbQ_w4uF z*iyynUiIbMogF#%w)ynLWEnm^Hf@p!_QH8jcw% zKVa*S^frxJp=YTQ9J4S1H99>-Vu4kSiiI`W40Tp<{-;-p93}T@D_Q&$1yRH-DZ27L zxaMX@@5MT8t04_iH;=Aj^0Y=ZkqKD-omiQJ4dY_8@HxSV4j?I)K6)lNhtlzsj*1A6 zPf~ah-Cp=n{F7W(#|R$c5o;MP!WweafCTW$3sbn%@UMB~5f8}@toCjg`feHP9hPEK z$y)#F$-Ow~P-UdhlD+r?_dIB0bp)|pkuCWnzST#^R)^!SBu|Pr6CpR78IZV^peXxI z%aTr6l|}TEpt8KtNumB9K^3*PjQz`eb7dEUze=(t?^PGk(F6+p6QROyE7d0yz+Knv zWQZN$+$K3=pF&lN?QL(siJ`ow#d`p;C8*|dOqjY$i;8pnG>kpnXBGSdVfH6TM){3AM%`M&;%VulAC`xn*R! z?VOMOo%?!qeVCId+;I)a&0BIO6QNV6>QWVHi%@i(RQ-Kn6?IfML&GJC8Ilajw*1!X z?vYFIACwBzrRgPs<&p?G8JVD5{itnQhV9vYzld2z{J$vGnc4qPD!|I3=+&dq*i;)5G@XlCSU;`2jcjMd6ur@n1Yx*alLv>Q-Ix8uZaRAFxGLAHlO zTtw)#mzyK-p2XXNj~)fLdDX_DTBj$;5=3>_c%Pp{-epBq5sn7&e7_rHP8ps_-tC57 z4F&WDr=d&-PeG!?aqw8}Mh0M^qbbfv={lYIB(Yy=t#uh$D3;(fUukh*`40SgP3kY( zSt?#s$P4e1TRsZsbz$r@JND3J z^Lg`_Z9|D^K<{XiFop1ue75=PiscKYFisO%|AnCr8v=KW-efxe~txlkwJyzwy zuqknABX`vKmBDYK^;il#X_s`H3C`xC?S0#}9`5z6bbChzeL+L)n$_EgwFj=tT=`|d z2AhL!>BGj9!m99&4rKhoS@dukXNM$@5O5#;StD#Mdo`Cb97RA~U5NgT=1C*XqO4Ta zJIQ?4%C>Mv7pJ^dB3c<1)G}q96)jz!OnVzZbA?0lZUo3CY_R(f^se_^ySmBSuUsw% z0=ZeM4=fCBofDe0_hxDZDASZ60Ra+1Y-F zreYy0ouG#TrVxJSgt%>5Sys8qAywz5zNX1RfPz)?4tyjrolnh}>l{@vWZ%ZcDMTA(I{1EHroeVp}U)!eHd zCh2G$mU^)DUxUZcZvR*Foo#JelXIPI$-geLkbeKpIpRplnA|1JDzUtcbWGYy*goODAGc*-u=H`u zW%2(u0!&zzdc<{KE3udPVKE4{XM>tcU9}D;kRUhdy$I9p4T#uzy%S{3;K9+ z;}U9^4h&jC<6%0q_)C26f*bN2S}D7eLC*VQy&%!6+pa{5B92kH zZVcx;LZZ#g*@?0|kt?FscvrQ+tC%bGa{kNK1@+Mh&y5O;^MdbZhMD9751Xr2@YwN_ zu>=g((c${G+O8>dvxJV&)$5M#W)fPJJ_Hbp{eKv*#vZL_^!bZPvnkO_b(`)d%53i@V zNcq3aO1xsM{IBjN*e5!iN{GsoQObD**GO)h0!_ILbm?5PTw5GbM)$qNglex0JGoyl zD*V|nisC~nk2WVMe^Y0%1Mg0DGTFow)dXC*(OLf1D`kV`;t_S-{?YA5TF2AHxj7vi zds%cKJZ?)D=4Tm79?#{0`U$rb4QyiEM`gzQ4o)P3$oi%kFRWTlAmr26Ch zGcwccR8frk!*mW7|3j|2ev9c@IYq`rCOpJ#n~)Uj?$q&{esDethiY#PRY>54oZTO~ z=~wX~#?1ewSJlZNKnf5dg%!W1VrvDceckOqLTw>k{3~r%7$6F)Oh#!oehpLL$XT=x zf1jv&{TIu-ZAidvqv;VO&{6nQ)b*5A9+|5~NTPv6+o_o;EAGsrz;mF(k+A-=QKx6qyaYI|pO$vk#ggqcBxz#4k5kkk)fA zZDpHOwAqE7Qh?L&wEVKFBV7me!t)We-bV}qrB0n}F;?}-Dr;tfn7$0kZAleibL0); z(E{&LeHKim1M4@({zZ4fNJ-wwj4}a-#Aomp^kuF};x^6rzYX>NA205;2N@0T$EcPV!Fr0{aAQ6v1FnQb-I(0lgVQE} zeV~d0hNy)>05^YEICGuorJHUkcI0-@&_y`%S1QNGsZkdUTtwzfOkA!!wK2GIZ42wR zzou7BbrpXktV-{*s3aKd%+pY9=u2FHDow_V8cfJ?Ok*6T{+DXjU-5r%^G-`>`c$+r zW+p&I2M@U)uOmTo6MB55R%-3nB48IHnC?1e<3pTA%jr5b%~_9xe;ztNURy8E#m~?E z2S?SL=A+uXBfO#VdYG`e23>o-%mEqRe3wzhv8yv2Rm!Gmm%hk1&MIg8iuYzu9rCE5 zI~Rvr)a2PUB^`)b-~Q6cbfpdx0ZDYxyi$a_>!GIlB=iy;p;g8TT4!mj{JVimHP#&? zdb(5bnQT7m`T;?Fq}1_#GjywhZ#B6AG4oP`LnPhOjpn6H(O7|`?3gE0G%(vJrSTRFa>8BFEQI-;VIUT^F~?wDYI$Z)zJZ8N_NL}%=Kspx<+A- zziUSJly_6wpbefQ|C={Y@C+LO06ZYmMh)?Dmb2gWQl$azk7sZ^cugEUaxR1TFEHSf zmqz&!)$tiCClN#)h+aIeGio%hQ$@15uqF1{5+S{Tpv8N%EO;j?$ z1^Grw5?RS$HBzEn+M%rDqsfPzkYIRuqfB?WbZ#wby^CcR#b|C95vt1lksoGN^32l) zu}C)R4)fomZ2#b_%7$a$ZxdMbHI8o_Y6pOdyDjivXXqW1)HmV!HYF$AM@=p~hV7wu z?Xvg29d6&83EP4Ga5f*Nj5-n_JH%c(2o3g41~>MxdQ4=#t%jln{vQCdKuo`9=zbXF zKG8j6x3(OUe0g4+t!no-;@3SV9?yQ;bLRFB^h*^1qXTC9%cjbv*xn=lv-8HW)@b7M zo)zO~Kkfx^z6jF+v;JlGsD`W`5FMD{dGY-0{)Tw#V`+37(q`FjzV8GJNgx2!Xo0C$Hk*vf`lRe#w_2 z60Z9ryATew_C;PL!(}BynVpa?MJ!x*K30?of$o3m#mP!@;27FS2^q)Ju`_xEPa)Iz zPrzjRHGPc9iH+?VpJ>JVA`Ys0Qbz77uU3&Iasi)#^T&9025kA3IA4;@A^)cajE@IX z56GQIbhBXJVO=!Sm#>-hDTnKL7~47`PrJWK(ZPSxZ?1FLQMadtWoJapC&sHv;3}-u z#Sz}IXUE5a{qu&;UrBs#^uXA)4r#Rke7U@(gtabYa~CJoW}p;?vD6wwJ>abqV?$f? z4U6?9ZCG~V^!Y;Sx4)e8XzOWYI&cyT&4sXp%Yk@%-L{-hCFC!DQZ)m*imgxq#;G}w zR5(t3*7K`gPz9-X`r+WYkEaWGoEfpplzUhnwTTaE7qhu8UqgE!XEwwm4l~z>h}e@! z9^pXnf8S8grmFBQTasolh~|Z3!gbD52E|#>o#VZwrEf{m{7jZHPi~+~X?xB5rL9qS{ zW~e_$@pM;&qylU`a$mhg`H| zDJSM5briVMBlLEhA=$)U#<$(+|JDmO*`H4UPXqx>R|1RVFH*in%GbvOl3fJglhHN$ z9)2$c;!4XI6+oOC`3|FP^y4Da50RPFmWDAAv6Be3OmZ8 zkL|O+R2sGA*A6ZR-Wis`MJmg{w^p5k@XO?d>$?nOX>xccpj-yz&tyU2iJ>iD}Z7?K^ggyiSFa?lEnJ@RPz6oTgiGdtfX?750fTmr?>Ys2Ce z7e{l>0z+jq|CXGp0Ym!aT5mg$F26xt4R?hk0WJV!Om~lr8!WBWK)n{jlkuNmzO=sq zEJJZ|oir2Zd%)*Brz~ zZa^F)PIXf6z-7#*zcNuxCOVb3(PtJaZ+iJVx6s;3G2^Dsyb#Y=FD**xG9l<@|ww~BleE~fG z?i1y&WdAq*CH`OU;D-WUixOCSa_bWZk^V@cDiOBziC~(a|5`a`bDe+ zV`F@3nO8DTZghWB(d%lXQdO2hmzHHIoDZdO)^j{h06ysO{JkPfb;$}&FTkwSc zoaUnm$G5dd5YfjPCVDj4tt(QI*zA2Go?P*W!|^B&)D6>+PEUmZ?0*o4czQq-`qc_; zcX6CwFpk0sk9Ja%qX{PI5(g%c#@7oV*~dZ#nUNLO$$xnJgOdEPmr>JzU-9p7YRgJm zHjtFBdxcS2Y{PUvJEsc5h#4IR&w3Qos4u(HksaH_X)ZQVlHd1|Wn@JiKYkWEeq0Tw z>Ld@SllFjmRqoY*3;#Q~p6<1O2E%!!=koq0`jmdj&(sKuXN81!l(dX}sbIABvxWVbT~!e!3`yK=UG9n`Oyi1{>63N{nMR(3|zzAABy+9*2HDk84clA ztg~OyZv+<%zvwCnH_=Z@)UXu>}#g{I=CiMKCVKo*bni;#rRHMCXFhI&eC>FV7* zDJd(Rp~MMe?7o8c3&5TjgbD?eqG1@qo{?6WpRVKuwP$G z+@(@Jho~xsWH*)Dt37;0C%X(xGhO(KUB*+V&QH6FcGrjG*O?jDOW zJ?VF{bgUD$LvI|*5-HO(?G67^Nm!Olmg7}i=*YET51=*DUg4qmm0g{D*46e=ar6Zk z|NK-E#4okjG#j@&Lw5{Puy3y$@UmXpgKw=$b37$8BqluKk{#0CeVysb7*EWVn3hj3 zpN9MMC>hg4M)MK8<5LY63dQzVj(p5?{006yZCVg7-omP}n239=w3+ij5eeO8xb=gn#u|ck8DM%=y~rk{Ed9?`VC*i8c=#>}QA5%NACmL>4(}VT6)2U6>b|89#7AD8CU{h&fsTac3PHl)sTbU{V4s+N~rf;5oZY4N!`ug#Dm{1Pcn8@a@e{ey9W7D9#s=!9rdA zhVVODJe?xAFROi>0k}L$eG=fJP>HoXjjr2`>3=5;R`1RvW+xF^r-Gba(=Uz_^V$E# z{C^dTuxPn!cMXfM$mb>@j^SQ^1o7ppSMr4z$AJ9hn{WU0Cv&rA2PCXvG;Q*iSFBo- z5~F`>5!+;65?#dkv&{6h#OFcJmd;WK=$)hETMrIl4}JXY_wS=SjteUCXxphf->`pP zJ)Y>u;xegTFLVZI-5z>llQCOcsq*7c{rJU9>C_u}sNp&ah6mxJf- z-JlqHZA`vpe0u7*li-x3>{Nhxy5rk=wv zzbA19tBgD}p%_ago5p~0?c?5&=o^7bfiU?5>c?Q`dd2-8qw<+2`wgd1n6+fTY-A3e zwZ}3tFfvkOFmB0A(0ClU!LRR=*S^WMKO+ylxf$A}aDBL7-!NwO{(_d-18`!%iqW2X z2vEFQ|KS}3mh&VfjU5)=Q{woM_q`&nJePCmCy-cDg6i8XF+iizV^~j1&~gGSlmP;P zG67abEXA=wlujU=EC&Q>kQ7Vb-2RP8BOyi z;_p}{ywYtu(>1-JN+?LYGmTGrqg>cv&3yxE8(C;e^aGdAPA2GQr^UD6jK`WUpb&_9A>z_R2(s}DPAyb6k08H65T4uLhn9sFO@|BuqMWAeL1VdvBzlMvc%z;2VD{u_9IMh$33N2r`yvn zdtzG0eWl^u5xZFToQDeCU!PX@jqRqwC=y#Ku=NeGd&>;Q(GT3>5M_O>bZUTrLo5(f zU73BSy0=~9)5SVBEgz@lQr)|r^^m)Wbq;g*v;`5ZZB@{g{25tTklvC71*3l0%jjQJ zM~L0hzePDqFHl*>{)*%c{VB3Oy}ZYJ&Ncb_PKCHuee?)<;r!4CzjI|coYTc0?X5cNpH@`8$OeWHnp z{%hsn;a(w+*aYbC6?kR?4v}(j{lI@CjAsm$loXG0B~xUY+ps;)3%1w^e z5#q^;JPe_P0~OxOen0tgu)MCl?K32Eoz`F>tm4+W&bhuX%A|=DQ{vl74lBQrp{yJnyyEgR2g`Zr-1StoMOpUR938WP+f|{b@Z&GWOciXXcwpoJfXPhH zeqw&pCY5-z*r*b^eI>rGSbfGSPngf7l_L&6?%`mCTGaa+O3a#`?Zqp!!1WQHsRw=} z7I5(sgItN3EpVW2-hbyBG^w4wU<$8A zt$Y)evB{@uL5bqczp~!Bpuu+I6N5OXkDK(!LQWF<%;+6oSs$Q%!cWCf{^FbTDLjYh zhLI6w?1xS8_S*8@#cZU8sU|MAZ_%E6LsQAYM87 z%E?zw?v#@Yokq6v6VGTYu`YjESFVvsy=>SyZf5KH`_H$Oa^ho!O??;hCIu%j9UIMa zD|fE@DUaxgBK~dBZz1AY(O-;NC*v-JccfXFQP?&18X|OMg0K;}Iq8>i?33n!en%Y8 z6(5Noyo+MG@uV3bZ~Rn`^oIP{NDy0+@SHdp>D`|viNr!>Ka)WJs6X_VNMJ48H2foo zd2g#5849iBjU<)PVeUGA4d3OOBCr{0A9b(cuoY9+#_;`r)}ik-uA7fzpm#vt-}S)Q z{Y>_*_w>JbP^uh_5%@OvhBq{#)h7NZVLc-W!|GJ7e8he3wWZ1i-^2c!BU9+T21LHo zja{GV`{CoyKmAx=cXUr69vhAu?LHhI9-sp(Qt`WE1s|i%dPZ#@2lAb3yIyV541AjX zH-|KOuP$lSZ+pwik2Ii>z9#%PmrfzKxh|#2@_YTH@o?~5XjY3!&POKYnD@N&Oz)|E z#{we1klGBMW>`~GT&1lsppga1F;Xp>$ymZe3qUM<}a>En8(_4 zLVwjCh}U--F>9K9?m3meefUt@T00+=AKg#H`Hg^`Y1FsX&q1!|I=>yA-Ud;=)BWAX z)jgAH_WIVG8NMg}D~HA9e#W>GS6z_VCb*So-|Ah=s1Kpb5{j z7j#PQcOC{o0k|Iy#Gq@zeTVK(?n490Z1ycY3F3NBSt$Xvn>JIA)$c|RxHMt`fJ@qG=9H7L`T{8mdJ(+ zqvs_SoZ~GCj9n8};;-nG&Ow+WA9epz10ioYexTcSSCo~H!=H_!mb6XhWVWMiIw!L& z9_f-$+=Z<7*%=ln#GscIyw`!6-a8cn+24OT*p^`hjC`<`-`ckOc~GD53*7w0b$+K6 zVuwVxi|ezGLn8T~$Kapae|jeIdQ3D;>}|LJ+IdX(@8HP=1{%G+to?jxNhJF zo_^?vtcGrpf$wOav?%C_udhC6T~WpO^?N6P z>)nF%;6{o!Ca@kOUx3=*h;88>tzl7}0BJ*^**Rh{q1g3w@2T&s^*b$!-oSlfRn@y9 ztm#BE>RR0?^Sy(-^Bt?N)7vZ=W@XUy$C_S2`-VlDrUt0%inyP38&8dF6!7VawqHFL zWI$k%`Z-T@%itZNjsKm4JbJGlt#o?d1_9WB{|t7d{*Hk2()u9)as5!ApU+Ee@5%i8 z(f-7o|K5}O&O6rMnshEALERi|{pszmKlYx~>XI`_A)_sCQ$SPd-h*`S{Sf}>Y~2A6 z3I4rfkU{W||IbI_+4`Zcae7MRhd=Cg)KKCV242@bdvEqi(RhpP8hlpo!SRuocW$&f z@La_H{^QTO0~5kELNj{TM=ScH5D7=C1(M^2Cm$PX|NP#j`I5aRm#-`Jn#NXkI-Z44H2k)8;_`So;7=!hwCfba zB);3h`x*wr&rWm$Z|m%|zW?}ZQ!0a2^FLC=eg7rELm`@vWp`B4h53f$+4zpG2>kHj zhlb?&eyh3qKpZeGt1;C;PzH=gaG@(#Oa`Q8@f=3uyWf8QuID83%pZ*K>5Y{58E9oh z#&s%sOzID=+hCXdd1(B_9mK4EAjZ+Nf<{i|8ORKaY4nps+jY&Jr!&9zTvp$ux9I^` za;P?Oc(4)uPYz-6XOcDf$FAMun6s<%G`PtalQ`ZM&n4EEm~ zGU>erRDQqR5+mp{g{{Qf&O~P4>1!Mt>Yh%zKcLc2AM%Y+V{~(LKLG7E9-X zh!zI#-Np}rtd=&ac1mJ9n^Zd`vA5f2esmsD=KM^&2hudHuhTX5AAfyons|05EgWV! z{jC%KljR3(ES-nHY>o4Rp4~x??ELh3Z%Jg-5Za`eH`R}wEOOstS9S&qfC9loia~ciTGwa8E&Ou{e ziEFeN*DY;roMmtZ3i~>G$hQ1|g`Sbt;53Dpu)NI8hNR)4j!o2YwM#c@PR9g zo)Y*$f9$KZ{1Fe%?lW!w`@@I6lGjE8ZO0@6A-|%d4t})LYbtw58rurh*OiiA+@9u# z{Ng%1oDm<+S(&_UqZ!BjhInl*-Z=D){GU81(wp@t=7B4=Rio+t$MBcSUK;+JLpHx# zk8aGb`rd4isn#8TBWb_5!F%`U^fzFQL<;A7^>7HojNaR*7y{nJO% zpu|SUB=(Q2-YpEr!2(>0Y3oFeZpe(zuIo3Mbnlkg=5YGNCPvUYp}(TXxpQr^apApA zNA%d=b@t!PZFoL*5O~(+7&Iu?=9ydkUk;gYr#_8MiuQCz);~x9|NR9n`3FE)A4l$I z{cr#^OpkiB8ZC@;P9p~Pdq^Gpzy7oCeLB{UFmy-UM^86C5wFM0OMo)+h>l2qr64=P zt!DY~PO9mDAi8Jl-kOhCvdm<^Fn6c=(9uWvKY2q(Z#E&c**f}pXykhCoqNQ?tsfqm z;PIRU_nt~;YdwT3iao~Y_xlYo`%K>#-RS#d;|hRx=s%SQ+&4k}EZmU-{*2fEvHgF^ z(`U?ri=4#jiI3+Yv~g(cJY@c?2MsC@pNGacy|7lM$eDFaO2Ss=2l6X`ppv9;sq+oy8pdVz5aezKef zdAvS1ZF0`@QZx{Y0FH2CMPiWvZ~6 z0L}J*b&gR+<1qla0rp#uX>1t9J`0UMg8BC4*WIC?Vw01&b!kFI&xaBPJl`(yUbpRy z9wyQDj?y02*I)$F-P%2`a!9voe*oL1x=IZ2Q6C;{Q@4cqXU+Dr1^g}1A2}lz-fl+g zPvk*wP-shWQoj8X1S=ORFUe19%_( zPu`eDD{FNq1R3Z-)+S{l?y=#D6weBb<#v(5?vtKxevXS&A$?FZ~4ti z8FoyeWdz42o@J-h{YD-v-1FHes?{-xuHCRWbocc6y*CaT6|C%-G#EL($2{`U+LQm! zb(*GybWCI$(bR59q;t)-K=hNqfAKT?-W@%U;6b9k|L*7q?!MM$+q2MmL9tD~5aSq- zzkKuUpZ;WS)&?~K*EGZEFRwhNSDx0`^{dxP{26o!#Ha6yE~ZHoZX`bG5y7J~qUbG~ z=Cjh9+H@LScHBU5jZ9FFnZK7?alhJIlG~w^fay%N179(vwo&h!E zDGbG!XqusIG}SO?WLlY)Fh5yF%*I?yjm^}YZ-}8SM>DdRZeY&l>S8`oN3+F9U5+*| zL1Z!>Z%t#nT}~$3`D8OEsxeb%W>sP~xYt%j$abumQ*EEX z+--rZv8Hb~qwQup8!t><*J0ocGS@YIY*ZcS-lk?T>OkGUpFf=nVQ73a% zGiHXut?8e;AmMY9_Wpc8mR)n$93E3`HlNe)twpaok znH+mekro@@xPy=l%20(e?zv*~thfHVS5+Kd-lZM>~a zh`Zmrj$G2sU~hm0CF@RFUeFhM~0zpfu-l84K&bz&xvW6WUP|8IvUMXpno#j zY%5bzPd3Z%to0fTQCWU^Ja+RWVOQ?gjjw+5icoa>Vfpk%9R+X+k?SnW#F*!s!?P&iITpvD)A?Mz>2+6E?V4jN)HgYFHO z>CtR!%*SKUE|aOTKPrLUIWQ}zJ9E2LH#3l9b8}pqFt%V_gfh_LTQDm?ER3fMpaXbqW^U(z z^eG|Q2vn;%nV3^UBV;l~#sK}d7@L#%e7cy=N&UHGq!plS3W|R*GNzji2u~G6=XN_A zfsHhV2?MlG!F-$RQy89!sV_`G+yXS?VmX?E>H~pSS^pNV@(Uvd5C`qQ0P%%58Z8$9 zeHFx$HUU$?9L*Q{bTrlH)A0x-un8i}1oLDJ5*RFm*|f3+ncm8ri!yaG(LjRfo9PG` zU<&3OmM){F?9j|2jXg>k3iFaB?ETNWC7M7$O=Q%2{=(3ZMm&4uy|rkp>+eU z2uvFK3gFglCV&-?U}!pQLG=LRft54YMj+h55(ZkQ6BE5ku+hjIOy&BR;I@8b6Ww5NG=V=rGmMOhW>oiPPUIsAW2TZ3 zxC_R}SZd(y0-{0egSePa&BcO@mY@yM_cb>f=<*Sm&EVgdNbh_*oy;fo&D5~yLN53> zAgxC5?F0-|9gHma2?infRN#QlG_YeqLd-#)kn~JYov6Tw;1PlXGRbtNg4Yg%rEWl|0gJ*kf-cm6pp9;tW3axqT6GhZ&=dWn zAq;a&reIfry+oD(Az*iXyCpLqMu$H&4eY$x9GoC-Qqaf?7$;;c4a`-T^4UZOZwK6abGfK2 zGV!uh>24Rx#Rlx&$$SQK2v9njnL1ib(C1>Z0LulVUUM))buigAgXrM!Z0BIiEEX`G zwdmMedx9}a>Btx}w2f5ID7vv+UR0c0w$;%BLX&9dE0 z=irxtN*p2UjcY+EqRDm*U8+gu;GdhI+jKC@w;0=)j6gWg!H@$34kk?lBSl+)x*dU` zWRA?O0j#Kk&j_y0agEJgF!-U{#e~ejK%DB8gN9qz+e?kg1&Cyofd6YQCZ<8mDSSAfWdoXHqAfr!6ID|u z<_w$`lMof1dYHnc0orUkQY#w(51xLI4hZ-~=!JlXtEt2!2I}g@K~w{zL8a*P4N;d9V@Yc8RcHbkt|yPGXn;|ZEWrHIR0E6*V4;~g-GWn#VT+Nr z9UEYGfc}_ofZXK>?t+DF%)x&LE4MOw3uBh1zMSbx(2j<=0KaHE0W)Y}jKSPiH)M&^ zHUp1jse>K?gus}MKxvN9f?Sa5CYj~CQYAhIFA(rL9)k`TfjpkiwKKc5lW*Re8-TG3W z==1puW=sd8l>pmJ!6d_YDtOjgv-0{3J#tP$2gVFEvYdjewFT310pAK9GKOwPI+!%T z-!KUVnQg%dLTgN0Oy?%p>tHp{MgXI#bR@U@31<2n`2)li*y^(x$cg22f=f}vFieau zlf`UA7l**=0fT+K0RWH5Sk*_uL_u(EEjX&U+@*?Ac-{ zkpfE(?CvqzzhHJw!2mP?Cg2VbFpa?kpMhI2Rcj1NAk}}%rDlTdW^T+GnSrN>*4sh_ z=L|i{4VX&v=@ytu9qG$4SfW!fvu0!T!WUpY)mZ!pSGDEgFYtTjb9HKf2R0uqL9}e< z<`%uO<-{Csr-nWP@1KBgGFi;P;RI)HJXwG!a$y2V%ZX-=z=|E26A*47 zNwld6Y)#Nu0+SnP*^D>fQjbSV@MX4O^lr!kq|X?}($p&ZvXc5Tf-VH@kAa5KQX8Qw zWQ+`8+{tXa-RP68F#%e@3J19drsfi~B+RD@p0BZ;f^iF~sxmRS307*RfCp(#C+Zev zXgr^ijRE?33leQJAq35n$z}}Z35q)~V2vr7O<>fhz}-5STGdB|60(oW@JVH_s*c8B zCQd*IgV#ouz{&;~(4d62GY~=>44%&P#bPp1r(iY!$kDR|Z4FawRCjFyOc;}z`zn0! z!kmG>W1vYn(!pBLpffNbm+cr4tz{ngdhuLDhC36GphS?Gj4D&lPDm&VI zYmh8{tb%8V(M2-B@HLvxPAtFkrx#0y7Vk zc4dE+Q6lFbb32)i4DiaP<0be!6W}IuL11!ba|6tpE%20q>w2cC&89GLGh?RDz#-ZK zy5WhEq=cD;@{^tg0|HC~;1A3^GC|T#!51BmwrH{dBZFtO08Kfag1K*iKLUp4n1Ba2 z22*f4*TBv!D}Y0C4A*u&_FpMEQ^U}7u)EC-sA62?1RE3VdthN5-82IF2gK}Vu7h{I zSfHnE82W6n02fo$HMKf9^f;XG-^8XIVT`~ho&pVE&;Ul}GXrJ}^d}gW3qTMeY6LLS zU}(S^Gc+(Z5o1%GY!~1`ZOR77^N5m-lLqz^NZBP$2B_{CX1>fOU|dc&Iv7@4u(5Du zT_+oDGy#U6Zon$V$RS(Pln^#l8_l$^yB^wf9?N?SK3xKuXQ z!FL6BcBxJ`6GNYXz)`o$Eio381*m(_q(GwqVgRTCm$xE0tfUD2?;AY`boqkp44k6v zNCl%0U-edw= z6JR{=#Yi zYd>&3aQ(7Zq*J@q7hAB{jrnprQI{iq26mCI&rCo)3?UE-q7rnTu3^#@2vYFb35b2u zFaT_z>lzVlgAa6vRIxv$1VVq9@Cwto#5gf#-+(Ten+wn}p!$ezf}k5s!A#qtJ*BA_ zVV+I&%|bUvn^7|Y9eQF3|3^N4<_P8%CU;E0XP$sX4FW?4F``Wf7%t!&0cpm@92wxX z%y8*+i|k{9M+I)WFKhyThY94e)$jzZT2*^j~h@mL4{at7}1R?}b%$EFVM z{%8s=EokZOd^%r(=9p=iGg~$=RlXdMpbyMBn7rl)td}YJa3)xObFg55nK4BNM9EeI z0SrouOcxVv0@wsey#*x9mvgZBYLUj^#`su@H&E2zPHWoO080l{qXCZHW^0V*+I#~R z_;d=YVgxP|h#s(;7q}EKvSIu!gr72nR6&Ljy)1C~r^> zAj!<_g3Kr2C`>_FZl|DA4Wwu^1HCW-dP`@2$3)pA8Yo*XIDj6a&cS`zz_5Ua3-;d} zLA9m9LMMvr*Y46oI6GEz1QtoM@`X1jH%WnkvZL zC9ZZE(AfqF+m7|c3>{AlYk>uCEO4oJfw%(l0ES>MRwQUg>;wIeTr6zCtAp7w(6a?o zcnV&D3Qp8U)u!9+d;+5aj@CwB%xOv$ILRAOqMMnfViy0h>gdqr6uzh;oh%i@6CmPe zy1JOpP$*4jGeez_F=*=j|^v1kI4DoQAL*#vGT zpt_f;F$O171*3B^pXf7GMZkAkaK3@3Ow5BJ1S}fhuPwnP!qI36BUh1_xWcxfTo{;t4VFCSgu{$zbI@%VuOA!W{D5<(nX0CtU|3GJ1?~4%*%RvAu-ug zZiPVGbU`$ofJ_5_8^e(L0s(=Z<_5F@C@z8WCE1^}j8@I1%pRBT{Uzy{rt<#=XdmJ%56!208j3Kprpq!~=5Q+4Hp zN%g%x2kj18Uf0QJqJpvjOKzzG9k@YgHlBj-GGgm;1cV~0Hx-~ZAB`sPC=)D`3&p^YIZv=yDB9w zH&nEh32fcPWVsy^bo!_dkAYOs4=`6FeS&cmOheIdf04cbFL^Yb&M*ozo2lSUl#TL~ zDVHhm9WF${A}E}cH`cBdK))n-1!1>Ff*;NtjlG9`=o6jK<11!1zt7^6SPj&sa4)l9J3 zVLY{F(QL zd4Bm?x$f0LdL!S>LF3L%5DBBDF;y`u$^z(6=uD!GfWxp|f=dn>MA23*-aYzC)nXRWJGIO^kHHR?g0XI}ne$$?G{3J8 zQGuv7SU&;z+I%n2RG0g~$F9l#ArS&n>kv?)+(r=r<#{$aUZ=UZaBx#^N)WSkJ%E@a z7e$mqC1VvyP};(ehstp64xtC`%IfmWdUyL7{|64b7{_oGB+1Qp5gA zr-#8Sd^yp7mH8rmr+EkT+_UWa`B{YZUJk>pU?g6_Ty!1&p^;Q#u6M!S&i2J_mlk&2 zjn%0R8jV%X%MY#}bV$4Z$vT9uSM&oIkdL}#3YvD)(TtA^Cqz)ma7)1?RLKZNm4F)c z)xr#&>Yx$2O1)fhABp#9kWf14ql&pRzr~ zHyXX)w*sPAjv}NsD*NaR*!D3@`}4@b^<+41r;osO?F~1260eVMGio2lsU6LN?^)@=_P-KanFv&u9R<X5zJnwDgg!t2d@;7e4pso=L-=>|nQ z@Zi>ZI5IP+%c=L>ZSYz5h{6KMox&`6+FU}D#CMwaf_n=({f?t&E4ppzzN{Nu4sWFs zUn(3Mu?y8h%8$Q-nQeNYdOH}V%@gj|q7p@6`n_ejV$dTA@ZDIuzU2WjcDe%^!~N{g z@sDGS?e#zda*FN+mSK~QTm;?;dQaW<&>bfjw9K&e9}@^9V52EPKQ^(UH zbkyP0@LmwZXZalLsQb_H$fK2GLA3FDkhpsv9XGWD_YEiBIvta^?=1_3$d$iAU&4@ny#H^fI>}FLP6R;-{o?M-dl*)1ad3zq#0B!%zZw zSbn7T>`zA(hbx_z@B(n}WBshcezvx&=OO?7+n+vUPa4Tfhk)T+=X_QSJrR86b0Sjk zlYCE{9g%@5Z&H*d5h5W$Xi`4JM9UH=iujI#T!36d9^}kb%z>zz*4(du(F)*%2yia= z<%Z7AlrFE9WXh);&sNn7d}vNHy?g+j=#@E1Mui9$!CMUTzzxt3=)0m}5!H63a4CsX zap3uI=8pMZKkQJSlo;@?57;^};?y3$VE^RH!K$Ws%kZ}J;IBN+%o_rBy(GE(MkVxB zK50w5%UWYn{GSEiv8W5XvhPhTfLAbj;LT)tJs=jrY5_jIT) z&Xbqc@TWxhMS1Qt=8vW1dBJBSfKf&E24|MLCuTzk!6iARGRLMyqz#4*!JelHxB}_N z>Cq>Yz=gYS7O`up0o~7z9P?-TdN90#qXfjQ+O5%{yDNr^dAV9pF?lZ`~Fgf%-?T=c)!Od)8QKQKlUj-XgkUXuM8*Li8)KUtCc#d)WB{ zg-SK|;nJpeyk4EOlGydr$HfblQb-VIefp;`Dyb1kW13zQ#)RRZTB!^D;lU#hxS3t} zGf{;@*i;@h5XcwaCVomRUqrD&_p)b%KzLQcvadp;FufsOMExuMULD{6w|_smzd>qm zzV+|b(f!Tah+cX{y}vPuZ+I46o4`xcdEAKjivQ%8e8+sapXD#VefaQN`R%*+pgtW_ zc^kNU1q(>|-@L&W!1D*)_@qD3KB4<}VAk*({4?;^`2J@8X2F{nya~Pdg_3c9L-#S= zd!Ab}$=fMxh zfo*Tu5fX%*QhGpqML*R^qJP7u509Z$F?49duV2~Nc<#T65#WO}7<}(|&~5wy-#|AG zprap7mO54^c>IMrqgOPXtOM3Sa}0|O&FKEWzfv*}y8|D#=^O3MgugMu)Xldzf?w-1 z0xLrqUs&UXlRUAGYsK`5F|-t8Xk(Ev_)Z~(esT_%OknOl91pl(Fl-LiyApr7;t`v2 z4Za0nCSeWu^^x7I%pK7C&N2Z9H{WjV)o;%XERi=^yXH_63%kov6>k2khb6TtmgXZ` zLMnx(+i3|n!T9_giOs=|IvJr*bOO({eVP>ZX!*D^cyqLD6X3x2KPo!QskP|#VW9eG zAfP^5!S0RncizM!%EkpVLvgo}fc9vaL}&_DlnXK%Tj{SA3d#o{;x#tbmCs~DJLp_$ z!{^2o`=tU~iv%|NOt{RjaC2^Kw}oUTFX`V!qB3sKe<_KBOJS$5d6a@1XYyCieYD^@ zZO6fUm47Eu^FaTT50r=gK}1bKWI_@0kcSNzxbGc$kdv|px6QhxTh{T@WcX-R2ra1$ zdWc2P(4>m%exm=$!w1~zA3o9^2;u_>&B4bEX5p=EyPsJt1FV>!g!}x!(Qv&7-NBt~ zp=lNBJIg^O=N<#wayr*$Y$}U%*<5fwkUlndHQbE_?~3~wC|a{{7^-g}9-SkHG`)9L zgRiF4n)E0$n(lV3poN^s1H%-eoj=ryCHqf+)C)rd-xPm7zv6TW! z1fCWFkG@l!GMs*uzgIjs>V)?YN?ap3W@SPZy8l#;_>Xa?4ZuQ^6BX*MCDX z4ah|Fg$1?Zh@qk4; zo(YK3<2rvlOz31bm_Fjw0AQvYEI!R$D2y?t+6B6g;Y5&;aK-q9Es$Q^-i!NGG3W># zJh?|!Sm{k6x+-B*E-@#FMDc7!Dl-esj42N@%BAZxl&&#)qS`2pxuPp6DxL_LgYZUy zAvwC$E}SHkoZn$#cpa28(EPyTezL_Cn2Vp>2L?xv0LDyQh{UC2g*;NZ2r?4EZt@qN z!!t4m9Y4xDH#2TY@Ac8$PWwbKFz=Dp0*{E^SKj{mBLYP?O@*Z&P}Ry00|!0)iLi=& z6lc_X>Ig?;(GV2pAaZkN;q~~SRlKM1d+E~>PYXkJ&fh2=JxU*2+~0WQkE7)gGmM^> z^^xG4Xe5GD68cHY*iYhzJEj};)>Eo}dTRmmi0#hGNLtMWorqID{I8#s2qRLi8bjL; z&;q!Hek&EdJo1W9cOZ#RDf=xG0$+1vwx&rqAm$zk8w>!U^r&8XNUt1+G^?zK(330_ z{bc1XBF@knCR@v)V%iTlV&|)}6>hHAz6d0c{HHs&Sj{T#GfQ%<0Dc&)2?M zgDr{5_0fv{D8$*(>b;g;gc$q%$2@ewSpJULj>!d?1iJboDnSk0M03-3wQCdlpIi*t zXAH*G#1EBGc3$jFo5fmI$I`w;P_R7$%`^^F+hho^iK$()>%~eh*{uJnZ^=+q6w#QD zg(c;k>wXH|0;;wY`go`ej}G-MbQ@Jvd#HPkp$)8g&Hu?qQ*h2;#(bpK^Z;fQML}*n zV=@jK_+m!(0W4IVSGh(=nU)XsZkxc#4O;MlFS7G9=>dz>j?plakflKQH|NG>3n+BG z2@v2wc%dkL3pg)Pe>PWruAyCe32{wD{|cxwoz-VZ=$o|^z8_OtHbalI_Tg-K1! zW_2Ch-lEX&?^uY!RMBpRn(RVPq-6iifl7F0W$%(*CRyS4cfN^JVDY zC7hW|yZ)I0vhBx|6^i^B%p6GgGYXALqbk=~vViE`u-A3$-^k;B6V3^exuSeA^bnuz z8ajB`?4P{k0D80k$eUq0Dw3K5I#q@7dTW}#0#f1Oh?v^xj%g#r5~!jpZA93p0%SWr z7uBHR`Q8;VNEo=$OMS(K1{gEc9p!uH0XK7pOz8GqNcjN0Cyrm9 z62W@%SR|5_rC zDxQfj$h72FD}^Z~u^^gdQ6Uzz;QdOfO2nWUYE=pP1x#tvV};n!=|!45-K_uWTjM+t zYGp=zak;v&RF~7!<>R9&ueIvxf$S8zh5;+w4I;v-5Kp6gJY5SPHU`t0d@%?e>{GCc zTlODdS_)th>4i|6mVjFFO;^yJ1f2`rTPFV}H$$3)=lb;uR+4=Ce*&pLewQb!7TF3p zw)?<}V@4a=;U0!-*qh!IK_u5wtQJ+_V?Wd-7LFh$xY&;(Jt(y3AEGjXN(~{Un8*G8 zNjb2Ha$W`m=JxUSVM~OXDaM-Hl}LrlFtZR`9R!lkD579Az)uVR&BGPD+p;HHPynI9 zHV#H=o|?lgkIXe?q2cf7tfbG2j|0QJ=pi4S_FUM(>$Za)?C5`TAcMDhmn9&Hv;7Km z=72?$s5Flbh4TAvKb@nNDjnbaINBsre;*HNp|f&!jJ1HM-2wlXho@crq?5B5NjQwZ z{_v}^Ez0DZ#^GDC-w;q5rh*R{=%V~8-s zEGeb>E2A~fjE~RspvwtVpW#|PTa)J>dX{35;gFXrGL0JcK-@z-SC@}kG>4yOZt;IP zu!B3jGvd-p7e?l{@80L2O~tsx_l{>le_U>cOIWnH$({r{t+9jQ3KH<#5B+ek@i-=$ z%JdiV=HZRPY%7I054b_-wQvTF{|#vBAV6;v4+McBa7@Jtc%?~v#m+jg>Q{OYc-H0^ zbPsP_<$_rqsy;_lvY-e2RTS|EZ}(3yW&-~+iqkT2|M5Z^j7>d#4Zxm+5qF5#KRF0u zH_w_CQh$rk2TE*#W)15z$0e9Qk*CkDXL1212Ti*R@i1?E#L1U{9+DIm+KZ~-vD^82 zH@#0Nn#k&h$&I4!Li2GNn~zC=4Aq3>U%><>`>_sP%Qag4h0sqn?Y|(!^q~)28;AZg zQl95-%L@*0eV&x3=p7~Ac|B064)ngj`_^>VbTRItcKrt(YHytN5I0C`4&ZXimq`J4 zh1Usq@x#WWKk^WjIIO%!&Pp0bXxVXEbKoOncScd?J>9Do<#tkUnU-Eeffar`D|z3! z-wgMo$;eMupQRKye#&jMtP)M<&ACC^0wn2H7bHsfGY^(ggZ@jnq}(}G#6VZJ9y|z^ zF8CL*n_(^Bb>gd+-?DmPtPQJlv^Kn>xyF53$+jxpBQ;Jn{^Llzr?`!ppq6-E zbZ`~i)Dj~k1k>Q&IPr44NB7}uiSYa+`8=Ac(Yc_c;pRN?R^d!k zxVK2$+v}Dc-&-c{#YyY{lu=@082iR|Rqpq&y9eF((dUZqC&x)x)`NTF#Iv}o2jCJ< zfHq0=%ve6YJ53Yy@L_orQA7-pN07~N%(Kv3303MACT4G z@-vo@?4td{IYj_}uc9V6AM|L8(h@%Ded{oia68)*jM7@w4I zvyVK1XUd&+1XuhtNNW4Km9iGFPC;rIU|Mo4eax3oIa@Cnh$7F2j?7g=RYVf?mWrSh zH|Rf>u<-5Qk>03qZJy4XN)eTO_6nw|U}7$E5t@V+{ex3_jSqqIjuKp0L|I-mfIrv) zkR*$3TF%i`;)L)OHx#C&CXg+_QREyv3+ux17i((r#q@6U`dP`>3r~H$c=4BWbI027 z;4dsVmRl_|r*;3hCSx6r6I`Rb1LBn*DDip0%o!^XN4x|B!E?VI9el?--6!|=I23sC z`*7=<|CevBBDx|7lYvS(^b&3Q4>()}iH_N$4L}*SH)8W3mPr zcF+TB!^;GfYn2DeGSBuOtazv(-{-`Scjcd=#Gg`F?JcK75kny>peY-vXf5a}F6QCTLSkFo_`I-vL6R|< zvr&x0MB4!XP>TTg?zi8+Q+}XFq7((ZJ7KZt%6pQA1GC6PyY#72WSYq)Hs^l#|Lpj}ME=Q+E)NmnY}W*wtrO=UfJ| zfE0=@p+*0JrY^$Mox2~$~=cpz8jPO$(IYRh^mJu zUPvF)^?kLlOh=|+zwmVv<7-6$wxVOcgkQf2q$?^ls8$?nD-mq;;$j3BnQb2I^gz)= zpjp1+xOieKe2Q%oQ~A6jjsl8tt6MfZLu)M#(qCTjrmwzvvtrO&g}97f z4p)fI6@%xB;#0WsWyLVK;uu}UAYFwhjhZl=t{6~PF;Z6?sw)AjD-PCG0<9|%t}Ddr zih*>+5F7oXm|cmWUB#$faoFCp2wSZzY_$QyRyzUP<)RX7aXHj=2*Q_5GA%oI3dtPE za%{w#IgymCxLC!9#;(FsV^_Sg(SR7E2jJ&h1dCuVHt84O^W5DJBtBXr&k&HWwDNCN zSP`(R9Z(NbEGa^ys}Nr~&HIlvT)6tcY(`-PwEB8OxKQe+-epNE!?g*%!gYV#hP-{Y`cPp68N8xcRyWYKb=e2OCxn z_{d2vu`n@#5e`=-!i5OBEWs8i(2}GFHdu~A@pDO0hgA*umknCH(TWX)kc%4hA8$Ca zACcZVDRcC>Ye1(Fnph1FmeHo>04mXD0GI*<{Z2`)p)il}gI72;gOgSQPTCbtS2&e} z)3^kj##cC9;Zy)8&tB`N(uk6c1^_4+y5B>Zi%%ED^F$Os0V|*3DtbP%-g?chEsrl& z@~jA4LW=<)3&!|=|NPVKZ@<1PN-LVnQ?CoU|%C2Y9)JS%4IE=An;%N%1;va}% zZTayFSD|(Sbbtihie`v^=Eb?91_J;aI(ZSjd1?`+?Yyp8!zU@BnfAS-&aI=4?lN@sUfs&f1GSv`pj;VMA zWtbm^9LOVGY6W@5hoWoHsWh~TJw<>U9&{-D=|6?R`P$H_eDdggG_P5@XBGzLbHFo& zwm;yG0I;$i2+)k%mGKO>=nyQr?SvLWjE^IAy{<}n&k_fAusNVdggmAW?3hNkX}Mf} ziE zxm_)CL*W9!9M5;AOBi8ED#=kd(|8pjTHE4WGZt>;S_JVCt}p{f1J7~13xt8{U{xXu zP$_@m6p=3nTX*!xvLE9n6=Iw8^ozgv-{3;d(I?8S;uE6$RabT%+5V54JN88$p=bQv zZSfKR=5Op#ES&JyJN<7r)%n78?Tzk*|K@Q8yL&#SsCjA~dNGXD9EMmf6Xo{#cd`5| ze}~6IAe#j4;nv2Y$>}4kX}UCMmpwC9=-Cj;)^qp#h&w*2Wu5vGEH-#w3Z67R5ik5V z4`A$W|BYEh7^Afy02GN_Y{^eR0>c*1M-0zNq!A%JiR4WgnaOkWB6GCE<2%E7YRCNT zOr7%@>+rGRl5KpYaN#!9tbd5hqzlj~W~liXQ(sshO}=@ES^)e_!Z=*F`Bz3<{P$^? z^$%g01J;)@!UV$eJg#|klu`sHdaQc*D9uHIOYw=>VDCH}0Y4>#9Uz09`p~(on^9R8 z&12u?o%Qg5T9nH=`m0#Z#ZCH$Kv@+0Bcr%*bM06v*TN@bIaVaHO2@FDt3Ae%#QrUY zPydlKkARbB@sDDAj2{5P<<%BFGu&fg5HHZd>q^8NUn~C6*eSZtc4s)`b0qF8kbE9N zLDzXWk{V=eqVnC{vBcet$7EoE8Wy|?k4Mn;wUB#|cSu6ARsTVY5stTS-zukb6mm9N z@*IVIyiv1Es=^DGdMHnyLa!d$hsV| zPNQmAh$@w(*D9J`cwP%f6F*b32twyv{u(tnB#7gO9p#+o0;nT2%6G*0#8O!FN8PgN zanI~y!PduUzM+E)KnyUS?Wb^awvvV#3;fe<5x(+h>58s=CL1L>g!xSo%yK=2Y6%L7 zGeHY8V#3uT{+&ZU^$~;Rp&W!i*QZL0B^&i$8sa9{-~GhNDf>DVeek>Ehp$4h9I)t# zs`)@VYc{~VUN*pl;U`ax!aV&(O&I^${}q7!JO!;Yztj;VWQ^v4^1D~0aXUE>H3rQG5sFOK?O~u>>!QEPuCg=?Am4E+7X_6T_b~ z_G(%nFg)`fZewOhc*;He!~}-$KzqQgbKGx;7oA!!X6Nuj>L(}wf?aejePV|%KaU=| zH8(WBlOH~h32X$j-l8MtF*$7qn#wo^(Mj{rTM#B|@bl%702p^$e~ONkU!d$Dwz)o9 z(Q)z9M<&W?YjdRQ z)M(JK=JQ4 z?0yR}qpXRw$mWv%oqkrHj$Gj0ovls2;K??xX)ZD_D)kiDMrbNUz}a}Bs#9JOxOU#$ z$#dFYN$#He!!Ns^^E~C6roBy@WsC3qR*vzB>W`^)CK}Bjj5qfst5m zEzgfGeqa}s_~ImP34W{VnBmp1Y4U1-P=7LZ>%<#A@~gsor$XC3NGCfA07i|4%VVLn zt;h66ZKI1gD8S@|e;8opxOYPP?LEE2s8Eu(_(SY#;#*ipgFnS?iI2s71v|?#rG3#m z^1in2(F=CczSu3{v03;O|M5Qhf#VzL?Dbx^ST`uQ!>h_)@bWh|p`4Fe^O+6l5+2}p zz6rlGe#UZeh4V40G9o+_-ufoAspC*UcpB^|*3oNTvlJ5?X*c&bqvi^8hUXul4v4rd z)(%Ogp;RqH^^InX`{2gK>Tb6Bqh+8}WhPW4r-K zq^v`8NALs_V%drkr4zFFD;uWp!*GMGO-mW-w)mB*^qpTTnnw>hbmExdzfuE<-#d$i z)L9TJZ6U!l{)2s2j)|n=wYSRtXb09U6M>;=w6j3gV+?f@Z@vL9D{wJ7Wo@4nW*g8r zgAaxjwBC^5GvFMA{ugLTk6N&gmI-E-8KB-k>*c&A7L|lc-YT+BCJ}#z$Pn(!%$y~^ zQ1FJ7c_Wj^s6~16H64;LmV$%Sy*N%u zP0v!mhZz21ss(ID-_O}>m2eGLNj(ssOd)!QE3WHSDgSWA72_)DAx1rNVG#X(C;X-- zOHkJ^`uRihC-W7fpYP+J^b-YtF~0ai>f(gEF^K^Vwb^Tihh zdm$d0q%>V|LtvG$1XkP>SY@Sbd}@ir8d!02U?ma2oV$|`Mi#+}yOOI&udY&xbwxF6 z;%pJFTUW7ijbu%zURPYdt^@_U%4*n^pkh~29lH{g>?&W&u3|O2iuLSDP_(O*rd@@q zb|vWA6<4+?=1gka6Q`8S*&9*kt`bUjmDakewAz&*B~rX{Ohl?TXX-=cD>7WDek1sQ z<(((3!d1%YOz=eHcBa{YDf3mKHeaRGdE(7q%bwoCXNd2i#9F5GitwjY!N<3UtL0zC zT0U799KMa|*BrkTi=?vR;o=D!v`B_f8a&sJ%$AQYmyacsN80Fqf=HyFD*|a%d6MZ0&9od~9l@SEZ^=+VXj6Iht1=?=n*GGYd7| zY%HopzOD&S@^LS?E<8^)BCGpE-|QbZY*6w`shnry+EqVY_0x;cPm{DcD3^z2Rh7gx zW%}x>!_K6`LJ=yxcv6Ff4Tr73(hG;kW}*TMzcNpKX%h7X?B^>|=0K%}-dwEK^0gN- zU{ZZ*(Qd@lYp=@gs_d@Hu3cr9cE+TF^Q!C4t?S4aVjKhVmv6rP)1S=EnpK8c)3lAh z#79$Fj!C|}daZ~m5i9sou^(MT)n=LLYYAm$IczgCy?k_h>%k%HiI2bi{(W=@kA-xO zww=AhSIp>*5xe>g`{z|~!M^dNhEGrByH9lT{_Rg63VXp0tp9}efaN^BO7_m{dDZN` z)Hi;^RXyN9} zGnX<%IU60?BBqLFFJ<1BpiLkrB0NGO3`cQ)!lAD)a(K{qx!yyJCr0&~%&71kuZgZQ z3j-%Yjq82s*~n?FQ@^4)x;&nLZlJyv`qS7b!11PH%x5@eu6jO>H`@ z*!!#aJz?q32vFI95e(}>8gZX)DY+C&ppErVi4?5opa<=blt5qDmEfLqDhLmrwFip_ z#f}vFB?Z)@Yv`M!4R0zbE`$e4$%~LZ=pXs=B3G_Q%nx>%ghcVJ;n>GxtM5rlutWQG z4%Z}JC3!UGv2@EC@=p}OWn|#aA|Ar^sSQEcrF=T*Sikr(-5293mok2%qMFanQfhl3S=XqLhR@6ijQ$W@Z1g`xqJFCs*yn412qkNSfzA%K5!crPsmUj2@-Y`Bd% z#}A&1naLiw*I4Dxb#OC|W#i#M?g0#N3&JDJ7 zzl_>BoJ;Na?$I+YDJ|HLT(1A{OpDqis9F;WPK&ZbDN#m;aUFilT8wH9T(WmDr4sv+tjA;e!LM@xJ<5`wRUp^M*iVhA%40+v#i9?1Xe!~gZ2x!C@% z|MOw=!++lX&;NYvkrWyW>VAQKBo4=ad5OuJ++;_}@9 zEPCHnX+uiS!<+Mitz3>!cd4rCKkgjm7g^7!mOWXeeAGE=5fb6HsJahxRhyrX7s|7% zsR&Sx@7e)NB3}ZO-M&Nt$(geIa=}!?@oFlf4I>Hz2F-Aq4?~|09;}0vxc5JoV|(>% zJ*ThZ$sxYEntx3W*H53HmdYYBzkONykID@#%ODP}GM41GQm`Z>&ek?&ep)g4q+2U~nE`!08Vo;7CI} z0*Pcz)Yx#g-}^b~nhzJV#ltY(?T5&JrU^ou$oBn}?YuRtgJv}<)4|>cDmNS%ld$uE zQmheqqX9apf)sQ@jBWlVYl-P#9fDg#7bs6ML3Hikz+q?Z`(6~Ku%>Eg8K;~IvlC_G z5)9qQSlQlmq^L;n_NIoVBe7ikjT9u2izj1@%nDYz1YRG|K6RNd$oDm36mg> zWV0yvEI*QJ5do_`k7iSh3syeW77275c%0!l&;{jm=y5kL&YCgsdmOrHO=(&!B#1c%@roWTk1?(S~EZ3wQx5+Dig?rs5sOORlL27+6F;O@cguF2jx z=j``A&;6e7x&N$LQ)^9kb#+yBtzXX!SvVr15TT_q44j3FyOSxHnTN&777VtuV6iZD zGc_}HwPbO!ae~>u_l{rVZs0+DJW}cG{}|;Wq^3t7r$2H3>$at>PRRU=P^HUqYG{P2 zDs{pDi+qAySy_$1-QpI^kFQ!kL<8l2G#M#l8af3o^YId+KioVD2xYcktsVy4SFt8M z7T!HxHUu4$EiXg`G4%@Hyj|gye)UDBcad?~XDfb%uDr39XK?8}0p|Lx3;!F?jLd_9 z*K?8Ehk*;+JIamt6~^f8kAhTz$p=}9KauFCubo=zBficO3b)j2JKlq6oQx2bwwn=p zc~?C({jSjb*$(GG0$%FJ1_`x}WMl*BYbu=zj!KsVIp#2|eO28@0p)iDbSmGeeD{{z zNiMPdkALc0BV3>0$GS(8slHCkTuUR%>rk3;Zd*U9T5nfT>>Ig+p7OkghQ@Y$g&m;xDLr{rG(q+P=B~w9uIe#`&@Cy)kGwi{k-{Zm#V1$*tjh;oy5%- zHA^ zD?WACN1N;)jUMMHX8GwagtMXp~*7eVcJE8&ZGXrw>t@^gik zd=6O$Y7|J17<-A(U7FTkhbDE@tKeq#GT7=_?`5^mE_4vfZ1kDncGn25-_EwOjb_P6 zN?bLg81;EdWzz;U?D;HCB!7v@W;|fIiTH{qYVxi~J+CS|s5r=(NZjOu%KZCyJ126b zgFdR7urmD+EPVU$Eflhv{PTtoao!<+SdB~@S0hbzErZL+c6yq42?TMeOW(RjUaC)+ z$-^G!&uNr|-1gh{C`4th8mmJ0n9DTZh}g|@GxzI#U{vNWC7E4Gz{k9C|XJ;&WlusFb2vwd4X`b6HQ_#+ck^}X=vNkebMR)5Jl78%h{pQiQ7wn|zU4vrB<(12=hae%O&DwOlQmo= zZg&dHTAjw-4YBX?&)z<*Lk@!R=*}G_|54VfcI<%C$=L8_;p{p(#F!*}l1(Bq_`|Tp zZb|j=SfA{9D<_T}5_ufK4!0C>Uv&z8A7%Zwl4_?PeVyJCOqxtY;i35_WB#0U9TV>8 z{WT|n89!9|C8iG!n@0l^oQIa&gL)GC9O<+pKRnr@MU4pil}IAV7NVGF>{bi0f(OE7d&31~#_f7lE|Dl6Rui z1}!iV)Ph7ROl^XLHpA9BdAnvX;w2>VO>HWLRug~W6(1R|&~eU?RipbA=I3xtQ4CFj za!Vb>eKi)xg!nH`knyD}Bu&AkWM~EA3o;pZXPC|(BsCl@>RU9~C?{a0LLswa1}S$h zIWI-0d4Hrui>FG_bOG-Krg_ak81Kb6`v#)Bo`9CW5G9`6!b3ATv(3~v^Fqm}I)#RneXH^J?5T=7R)Z+1 zEnIFmE$Xii*2g4Z^YKKp_F5J9z4kd4--z|uC#)GMOWlD#Ff|}PcTW-foRm%Ekk{C! z&wX%%`pDliY@IBu-e^2Es?2X_In-aeNNLvi<5^Z}dDwNtI1KA(0pDxB;0*ad6pu;& z_QxU=8;NX{1|8Pb8&Ld*wNW1wy?$L$`*4gxi8V6Rt<#6_v=@`EQX9N7XcbLe-~n9Q zReKr{F!7N^tRY3SnVF$l%c=D%lTo5d;;UB@-1G5_a}qUuJ+t{%V>#6(LsY@U)B&;& zP2vw9KN~?_IBN*m^HPJ1PT#LbGOnY6=#s4ZpLApi$YxcoqYV)2^bx!JXeBHe_YoFo zms+3=5b5+2VKxl$QoSxSk9=t<=JI|$qI@l)MT%;0vUDvX-jIXZx>az5*T|T|Db{0v zFpk5R#QN#f#$J~}R}eh4qwG?X_|nHtBM7ma0la4qapVn$^w4P(X!G17dbs0jyVd7_ zrF-Jk#agR3`1HOm_kpfstIki6o;&$kuvY48Pk}S}l5c(EDc@f0sl0?J&a?&7-@dZd zi=9YTfz|16h(5IL&~#QB@XY!FN11m0(_H724T0R}@Qkf~21)!p+{i^1`R!GMVS9Fo z%j_I~6cU+q6<-#HLF;n!Rh0RL0G(;9an4bclkr2Zi)|L(G0U&-4GmlzaXVHnCfDU< zQie%c%4OmXQ)j^h8P3K}%Ij{t&AE;(}? zALjxjolye2`4P6>|cg8}9C8AooTgiN9h``_=B51K# z;Z}OhsGgaV)V)tgcw8kuT!aR{)VrJ>64QPd$aL#V*LrNd>$0E|_YOXUr(K~csr|FZ z7Kp1dvf;Dtt?BTb9jVY}{2uo86At0j>0i?=MmcP{VQ!Kf7qk*OBDSh7cIHL(ipbP; z-%0Z_69ELci|MfsvVS44}JD({5&MuSFub%95|kWQp- z`g@6EA|mpYcBJ6Zc|DdXtVucMZdp#gDW2pjVxtAGY zDUm1<9(6;XUtcbKtO-CxeZ<0Cecr{uw_s~~s4?gsiuyKsDm3;586zg;F4~C(ropn<$zlKCl@q2!>ZRJ{Adr@3t=y8ae zUi#d>P3Nae!MF^X*@2vI!k*mM?|8V8<$I!3tm=I)TlWZ~)hG%vr3D^dT&hJ2bh-cf z;6mOc zuh0Ntf#rngEMbHMmU#Vb&pEHeZ;rd?(UyeQ;Ykdp_%^f{j>=YThF^wcP-ta8!Wz*{_9ZBB&>Idu5(weN##&x=nbQ^-#?Kd2)$uG9jt31{l&hI0mpFG!$UO$<; z4|t>=l{V_0*}O7v^&)9^hKnxwZb-gev}-hUo9;wyM;7v>ORVX7uw7>jePT`&(|LVb zHL*{J)7KK#55@LfpW7XL>zE($GHxWiTo3&lh0VU~fcL*-YU=LF2gt3-n7Y!l#=6v# zh8bz@`s;m$?Ke`YdTxNHo%x$BIAe)Qk~0|fjB6U$0Ik58Ay~2p^az;_m^W*+Z`?2f zpRbcPyqKrLAXUA2obgB zr1sczyeO-Flqbye2f9sBeivH%`LVc&n+X{fW?CF0!loHE!+rYVcpHW>FZ~z@8h1S| z;%2a!TQ+k=MjAeVIG#us(>xPNBsFNB|5!JVI&3wx2`+Nb8nB9(MTKbBq{@Q1>(m?V zMyzkN<9#6?M_++yvO{~U;cDkv_|m^MwpegTgP)l*&M8uJ6Z`NjaBkK1daztaU}ksD z$_rzMjy|)hwaAx-6E=8_A2&*HLKso8&JlYB-ac2XkX4{Fubc9qCuAP7*;S1IS|L=T zmL^UcWl*OWyaKlw$qAeHSifwD8_SfiuH0h4yllG`B>(tmHL7`BU_{o?BzyN8`=YwIMWYD#dL4ivW!CH zfHvW+L~Ts$Yib`$o5#{nrifz-`PZ#SY_DS(@>qZ#@k}B1Km%2wJ@yi;VEu46O!s;% z%PvF?tN&1RYZ;s5kLy2WLx;&I!);= zneL#KD$o@+)4wfmj2$}j%w$%dI@)GeJAQy_I0YDm1$S(UgN?DhhNk3#9lLihVb#i5 zVk<3H6&>*Mem1B&-A<+g-8g|!zie)u3-1clT|@O@vunYR z>ja|%><#kBLTd`)z6hpB{x!@fq$e0Y%`0V!R9j_*A{79&eGWFRmz1is)b$prp%ddVuu1g z!%r~56D(3bF4l+rHSf4zGS-Ru!{)AJ!m{U_Rq*HaGGVE_+jD@^ZALdeI335v)BLoz z@WG*k^(Q)$wKG1Edjp*fBF-~Qn#>PRd0M}6lhXH?)IHCx#aR^?1s)OABNW&k*n6(> zB2tO6-t`0)F3Eg&n)tjqnEvVz@s$QDJS#G!B3k*qHB5CF9G&Kmw$p)gCKDt>8efkO z3D%eUod?H1*u37P>S+x!TV}xF3%hl#>7~5rvct^?XI^w_p^_oGr~NGclAva}Wyqf~ z8go$3VVXL*}#5_i#ROqUK$9IoP2ReJt{e zfRz+s?F>%@i`{}6)UC2gi*a4)I)XS>GA5kF^V;bt`D|Pk%^vlpxjoRmFneI7WPCCc_>^^4rqz}mfbd>}sDaUt6|OW!3M-U#Xx3iqiwQn73~iDui^N>!spJRAzDQgmKc zoi-ucEi6`l{yiDtmBIdlMt#MgO`rXXixoW4qBk&lC~r~?X`daQpVBdD{P4uNn2>36 z(?fo8b1DlC_X{j**Gk=gIvkV*hSw}^#aH!ldJ603&D794LI*4~sr!+Gz@;el;#L89 zqT0Nic0uw5v&N4tvt^1G;p=KD+z}=5OKg<&K4PjS#wRUO53Maf$`skR&q_e_zjhN` zx3y9wSrK;?;A-{5Bg}w>Cv_cXOHCy%Oshz*_mdjyL>Ro7pRI+qeXMzDWg|m8(~#h< zTw38E=V%<)u4P^!c?m1ph#hmy$mBO|%Q(*@86i1Ajp7XNqQOZHpi82(DSe9-o7##& zYBbOXpU8rh5{;2=HA~7U;DeFh?3l5zINi%5V`Fk+gP%GKA|aIx%RYxYKO67=G+p$f zx?u|PaoCQUyP_KXEVH znfF+6VWm%{K&%M!jpM85)YPJdsm@4pSWN=X;e{QSe#KzsIK0{?!O7;2#w5r*Pk-@- ztl@gyVW1bwSHt0ZTGvDVN~+3xnHK4^E^{uq{IZJpAsHUkDC<57w4;FA`!5AF;u2Ra z+d@^{kKaG_6vO>`rE~r@YD9)M`6SgN7N$CSv`XLKKk43w6Uig|C_B3S-Ydc<<62n? zW7Tm*V*7Z!ruYI^8?-R?z$S$Uen>O&afd~ z@TjIujhVU{>~zQJm9D}hC`ZhGm*qdYt5A&4bdIiqyyngyof`aq`h?P^hzKO*GWIqy-+^v ztyCO%qGK1EpjtkvCdtwzV>7-zvl+~}lZMry)mbr^++<#rXkbZ8Y?%*Eh^$fL9+aM= zgT85<)io24H8S94w`bBXN+5>OTPXhdGs5nZ9WG=yBe%Nj&yD~76f3s}n|5!$IJa`4 z8n3xl)l2PU)JQF*e>M5%POs|f@M?w^xHZlUkzIu4XUOL6-@fZk{Z9=NBJqj85R|W~ zQ4kl8(`8h*l)Thlo48BSEqDD(#Xs!<`mXgtSf*M8^=$LD??xxukS2MT#Mf)Jq)y7&YUhRZkbm%hV``9nw{AgH*hE8i*nf`k5O-!~& zL!t1YV>OxB;C>H(6EW->*zS8jg>Rz!rDBJIN_64tEZ??**Au?!Y4#`P_AMXa2v7{} zrga<>CZAJWUag}5@qHU?7c@NyzP9n&1fI88;%N~`$$~)RC3bu3r(M`{jW9i7!94*x%bEH&4|+Xb{h}f)SE71qbsgZ)^gEle=uK zMYMBDE!;Sk3oK-l-%jI&TP@Q(w(@&(8&f1=Y&UC{;E-pnH4QUuh{gMl2W>=>&OM_Vi(uQChL9u?_BE8ye<=@q(i9bF^b$GDb>exZ7h1kf`+i^l zn~bv+U9W%x9^(z;)0Xe&LH3yw7;Es8kKJ=OZ}(_+=w;SonsY5eLZ+^}!bl#FhTknS z;9E4455aq9z^9rCzUsmYGT;iH%A~(7SaCB6XS^=95Z`fpVyEX+F$q(t2-h@BbS*Gn zafhbMTDzNn0HEV<+O<+0`{z~T#^}xS*4jr!;eM|KZ(J&p?b{6Z`i%D2mOF|1{lObg zyl_!op&7gIxamFYeov|N|GgWBkV3Ie+D|!dg*NQFruEec33aA!i1C&9sLTwfhcyC$ zMfperar2A8bJfZZ<`o~b4C!=an)!{5Nf+{p4=+3zt@l_jjb&K$$+mz$D@RD?I zx2afd8k*XniKi?d&APg@Im{Id*{Jg5V`BHF*%u5b$e20amua|&i=K-5@B8pn&OvBb5E$0yUeeL6WX6y2%(4G?uI z>taXMtdyOE%gnz@B<`hh_>4^AAZwp?tb3#E9-d1}syx0zO{z>ns+@vW*eB%gsCG7Rc-6Avl3Gyvgv z%Tt4JxbAgUg9jp3NsJZZ6C@yLe$S4pT-4>R7*Zaja&QlCQKu48e$Sn**#sCsF$6_tsG-zp zojz85rDP{tRbMMpIbABafgq8+$W{w5eOg{OaJusXI8EpO3Rqptrm~3m2D*o!6$P0$LF2}ajlyGM46blj!2%9BIyg3PWZEl45Ag@&)uhrk9@gu1PMzfj%jE1le6&Q`P zA}|_fMY|gi&O!OaZOzB;)A{NU&iOvTmTYX532c!hfQLKyh5!Z(dJY(HuXGFWa8GW) z!*6_>;~=wagUQ?tBNFX()$X6Q#-@jm>Vq%kK?zVZ;^=^ItR!}V=CXjF|lN>Ttfl19!o&04Iip>y?XhaHhtxN}W?Jf=tF6=NyOK_$t zwCiAKZ~?83&+_L&gA1uQkrSd!2klxM8eG_AVGYL0B~y4qPoSOB?F6rG2X}$#pb<7T zhNOj%J%5YRZ*vq}cF#==(%g(U=g;k$s;R&Z6Qqg54oiDrbQZV7%vIS}G&cn%6X=dm z1L$t>Ke_{^AMh$bGa8_~Fh<*mOjTc7sAhmyfi!U)+A>t_y{e#t2Q1+1E^f-50M%o6 zJGks`r}h+|@=uS%W;h#Q%T!&lo0~?G%EB5zl2Uq15A5%y>fx4UdS6Vgq_RgG=&}D z-XF$|0q@DV{vFT@7O<6p76uIIe@8uGvDdNths$CmU0HO|^rjSx^t5gu%o#JzdMyIx zdwQuC5*XT3EDYK*S!r)4rs3n;)K|C-KI!&r^8Su;-l8tR8l_I`xRvG%hJPHA!4U9< zf-Xy8J}U-8Aj>L+P^p9_Aj>E{29KBRy3I3D{$k|+kOm}J4Ywx1GZ%vZchEoprk@+} z8xZ9yiqI&J0OrHtBjBVFndi{?__YDd$Cl~u`B+GG6O+SFa1(QO7jv*4hovD^B_&mz z`dQliB{1w_q&V%U##u;>jAY^@7}(Kz4f2wvF4kb*(OGrLi*=Hc+P;qMot*25W-7{} z^|z>Y=Qzm^uZRvMGXj$;-ff%h8Li6T8_Zydv=H1T@?t@zOM7BpHoPcQ@49JHFrMkn_HOXUNWsl<+Y982MAmO}l%V!^&q{k}1kH>oC@WUL;iB8fRuVMXIfbMp31 z2RecVI_6D3g5hQ1Ct3v&l{qWTL0zBGs}aH>N?qTrh(i0Aql{`M++NQS-5a56K>SV~ z5SMBs9~H1M3-owSr?h;!9wC~T7xcA`ES1PQ`ozZksv8i$C%yv246K0I_GJiE9Ma`E zJ^Mb4R>dIPn|RLiD<>AW_k7O=PtjZ9kuyl$q2xyEd6;>bff6lbh_@)h{4~E33sk`oF@Uynnkz59U#gWkX}@57hN z`TrDKDSJR}n*_4Pe_%*nXq`lC@R=QcnGB zv#+Fw!m0e}l^qIalz_FOLZ{0{?NS0#xO5S^Po<58{(92irNi&4A=csAxYBE1CGB&d z1u^m(vTQ?Q=-@pH{hdeVpoIhVg8JN_#KYSg zR5j5g;?O1T0`ohMOp-wJ$Vvt@kI=Hv&J}5%0NG{tz?W?Nb3Xci+O#SK` z{z3tc|5-1$L(sBs^|!g7fVh?jDo%I?6>pC^w$Do6U4O%0OW~DKNOcg+Sc|BRByi1O zYgjRh;(%~x4LU<+pJ^yqe^RMaTOORRVxOnWk2K7W{2KiGY%wpA>#vkFJqBqxVh`mW zCnI5-m(}i18k4?@5FLZKje~HK%Eyk_Wt+0Zt4IdS2s>6~@rFb(WNK2RCE48~4H}bd z*U2SjCOt61LM4i_Y@*!Q&SzZ;!Yi!fL8gkNfr3cpyC6o;r)UA&_CzhiLIJs!_@FlF|~-JKj5;*%SY==#V-qn9UuO zh&|nxSvNcKyT1Z~&|(3k6xEja0)f(WfzoG!T%P!3PAHUxVG#tjsU zTT>{2BtjJ! zLf7L5d#}b=3TbBXT6n+b1jN5SpY>1-$yW(F_n-c<0mMXDD@c+>Twrg?*q!Dv!Q6h% zuwLvi#i6s)V9E*Xd~fks+n=!@oK)=>n@Cbc)>3DHSeH$Oj-9z|7`RU*6e?cv%Tl$_ zXH5g{%fk4E9R}uLZ{lyb)u5##n9D-_29yL7Hccah6YR@|*AY9Z-wnt5IWKKj6GFqJ zi;Bf`WMhPp?8^_-X+I4FbESTUaG0W{s6yY=zeckEaxyQ$q3Nm8Cw(}bAWfuzq87aa&0QM#l-jpMlctx$!mGZv9 z3k$-j+7H;xkwDd^iTg zYEPl!AXGq1^kM}NU&aFB24}L$29AjXJj1OBTgmXf47SCDukIr$kX@*eC-1}f_OL^K zfM?i|>GNaQkq0=Ckre5mXLHQRdT?23Ur$5@1b2}62D5phBk zL4M+6EktOnK~LTJ7>tdVm)|EG(!cs0FTOZCO^6HWX>|y(yRL9HTf>oMw*)93v<{T!>~Mn zX2RxSA&_1t1qi7{XXUb+nb;1%ECF05oDUZCn8;uo0V z$&>oVOc?LEXu5OZs=F^H8!uk;d`mQ_7`oS=OXuaQq1#D%h@w&Tp&_JxsAR4$e32Bt zTwssfUg8^id#2y4Wp$d?N?SEx|NBL0Pd{{ta>m=+0kzd~7||?@-2N7agoFZTqoGn) zT-{hLYFVG3{eN@17KpyNRzcJtq}&E)M7Kh|YV1SwZpr^>IlLrF^*(8)$lHNE%w>Vo zOti#hNBj2#0ogRNW#f*;MM# zU$*;gLhcHC<~)A!a|7e_&H4elLA!;C1tcZ3ZvOucK}|{j4L7y*%6E^QGeiD88w2(e zEz^{hRGJ+d^|r?8QAMBFBKDa6D}wBC@A;eL+PiC6R_u27xhMTk^Edr5KY!Y3hN+=7 z{eLxmu9CP!0T@x*W-ZR*}wGh89^PH-6 zOK&~nq~wjiUaC%Af^^eUNRyL6-p!GwZ~wJ{(~Cd-+TN!h@jLfR5A++XEYYZXr3lyw zA(F4JW>OOF?~o^s(K7m&Ho?TDo_{yPi@^phPJ2T!f@uforEX8dkN#x}kR3wPLEU+Z1$ z>7i7OU5?5u`wL17jrbPlA)GXxT2U=jS%Sw{G|KJot-0CX%&E(Bys*^N@#7TDRv4?R zC1d>h3%c7=eoIRu-GW*NO?E8TTT=tICzPeWqNDZfg@MOd1e;zjIh?&o3WYvL;~_#C zuqtL7!`bQVY#3hFSa6!2-2LPF)!38Hw9o zScyn4tz}^Jb=}%`oG@I*&4g5v8SZ8H6R?&)kc~`Z7_!Rg^AUX*M#J4>`LuvGy?YEg z#`OPUShk_goV=@+WEXZ6h+q)mFx8^Fm|CMkq66Ysh2X~VWqmQS^=pM8;gT~^j z@DSy}F9!E|cpJ{<&C4jK+9^d+x;OXbb;%NYu&G>akwni4qT45*6yTB92ddS>410X$ zM;EDcvGt`_Fh>i9vR;Bcn6%39f3x0(q*&3MvE7twNO|DNDnX^&w`oXlWpt(O{s{W< zh<2s}k4s?!W1%6vj6QvNifU!L%i@0B>jrqu#e;+C$!U|rwl3p4%I@<9pL6PRR$zB+ zOvd=T@0_3CZKG3tg{&*heeUy&x7O(1@UoUG;?CVMPT-pQeozi977!-2HH zZ`y01ocY-s-gK`NyPJZ-ZZq6zg##bN=&WEFoBK@~s8vh=5~WD00bOcS0W@SV10_*6 zMNksO2au?SC3+|tQUOpzbpRCel4?RxL~dDLMPs=9!m3^@3&T!c91DX#3&U{8xRkrv zuq?ioIX>{caey=i7Pf^$Nu!}Az~&C}kY~QLurQn!$g(gXKhs&w8>9@fN&8>8*C6)q zhqB+yH?`&6KXLEvKXLDS!++x5oPXlp_dNf^y{dm6*nT77xgxB8;@+pv|A~9CJ^zV& zyZ(uLL4yG9bxoxMVQ*OEk#tD%4TrF+9p6~0rrMn$2VFvma~Cs z?2Rt2I)ywEZ}kF6KICWE@-1Z(UbX$-@t}Ni2RaqlUQi%J7z_X*Y!oOEl7s@GPGu+% zN{0d=nD+n>GP;J&0@fw~gl5bDAY`QJ>ec9`CWoKsrgq|{X3CPlQWkC|-BLpIrR0wq zMw-wk=se&F8jP>i^xnf+Efyr@qNet;2@78fk8mD%cAg5ca80Oyy2V)}vkrm15d!MQoKlZv$&+;EPj}R>^$^&8t z@H?n@yc-a|H);aJeyo7lSu(WB@0*7!;gmBxRIcy?60N_*mclbZMdJ#dX3KdZ`blmfMVag48XNWH&6lBQd0(8OHCOt zYZ&~rZHS(Q%^rZSiBNzFa^fSQ&R_~85wI=yO&4HL3S_{b=WDJ2@vKb;FtAV;l%sP) z3EK=MfF@d&%WU=z^7OmD6o$*UloW)A6^A3c=DMjF;c$#gP0dPaeJBCA>nq!%JSca? z*8;ffml9xd!^6V_>Ept~-gPE6mDKcqpMnzfJScR<#|2bqDFIZNmV!nW3j;K=)POmy z2nV1m*hmwqLLL;l;^RV9DEUhThibu?|ED06$exuO>H;7U&@For&~3v1>UIy%kO=5j zXPn?m2^7jmK_L-K8NjE$luRGL{vF~wd8<2lU50cT)aUsgt5}jX-95_4?NVlcUl}tHh;ro#AjDirX3uR4F zb<@5=cX$TKfzxzd=nfBs9W>NEpgTOX&>bGQmr%DDgzoSpzciU=Fw(rioq%Y{UwCu? z;P;v~({cP7GI8CIDL1nqML}ic7-{4<^ z1NQ5?Oo!d)K3I_-W9@gecLFA8!%N{}$@CQ4KHT-2?eee7;2CsKh_uY|qg{{nPMh~f zvosuv`@2=2g&eiS;si7oV<}=^nT^F!s+IxSJ$&qBN1{Js90pi`$4FOw4d)1Rnt|D}Y_F71t zD)zFT`EKQwfW2ZlAynLn2oqyd`qJUucW&7j@M&HB*Y$q? zSoyyG=c$@`ss?h`>RgES9sp?7*(C#-#wjKoBQCI_D8Rw}0%cvMlV6;sX zo)Ba2>7jPGS&jWI0F#D8x32uwf9d>xEuz9p`5w?CkI;?1fvEa4OleMXqD1y@O-%|pYAB7e)ffMDnbB^75(yO800sR zZh#f7bh3EE3#TLa_fFOVbSKLd)bwYk`g10bx?Yra{>gP3)J;#~4s0p@mD{RApf?0} z{e45kMC=v+4FH>&s1b629qJk-s)ao zqmSoqrUlMPA$K$3<`f#-#KmCkwbISQC#v(s_S3$}N;R8FCilG#eY@cX)po3}t3I7l zMt*e0V0x9-CmT;A%{3sVet-21Z$hvGDV~NFF?F*uRA!o7EFUR;3MY8&6mNpM8n{r@ z|0-Rw*Rf^RO7$e2wSiCiZZq8B9cIV1S^B$ffIV+69|K}S2B_Ev87ltT35cy60Wstn zjpUvP98b(ULa?wc(nn}ce6z)C4mMvSv5s(f)#?Vhk&ZgA_(aJ<)yM8K%;AD6NJrI& z9}*Ek!V^g{^Yiy;^kt&<+J~rJ6a$>r``Pq(1SF$QT%SlqO$=nKeyo?i9U@=#k$>+> z9d79sPNl~b9)cn}{rr#?AjC}ASriHpyIVn$jQ|h+CEJT?q_JSdLTjX$FG|w;Y{CW! zV8zn7P*!|90>CY0I{){>eUK=!oEdq)e;>f()bMm%&&Q@*KV+K&-je%IEq;|0=3pV#W zC?&21cy;1P5yeGbsO(HnA%zBr2vCB545~yZ1gZoll(6Liz>OAtM{E|blBOiYDD|6Po_HfEu*?Q`dR~R2as$7Rk z31P+^7!QR2DkyV8k$D~znPW!+$UM^qip;^QvO0yewjYVaD95>+oppC4i7BJ`C1#{i zTb?DKtpPgm^<#bnX9htDHVG5xQ$Yr+3kAX8|9QJX(*-1X#pkjbB zirOBK0F)7Xi~PPbHZ#a#asMYt&nVJ5i6ZEcKO=}x@;RE1xZ9Lw!oYZ;a(HE$xps}4 z(kHjt99|2(H~?S2nV_LV*K1jZGHfloEZ%*kWZ)7Z>q@9Z(s_gT>SrFKcdIeBf#IO% zsoGdup*;`5NXSw}Y;sT{8aSkl8|9~G~v*NHbphnA(eO~;DEt{sM+R^y8K;+=s@Dmx(c0Mm2~Yla&gj2 zYVo829{|Os-u1?_Bn?Ocr)e1g#x5#FhZ1hyEr4*pWC95HvvUB@a>sN5t3stclyKu| zt4`0H)GbvOtjY0b6|9BkuOR{MlLd7jFS}i@vOn%42z8&eP{4hnKv4G)gu0I))P16g zQ7I>5qm}j)bd*H76lR(b0&3h!pfvl7iT4P;f(!*fvzrwXwtTDI0#Bi0dO)nv$1;j! z-m(|76~T6{dj`<#^fRv!0u3EOfM&ORhy=uuY*5H;&<2Pnej-7|RAF1dmKCydeP-(G zsukc1D{Dr8!FROLfbX$z#sSYUDMj}>!+OgE8&+SE*yIWG6WHXV*yMkT5$3njSy)8W z*hbVmQvvpH0>}BKp>}1H2ljCK=muKmD5OKPvFW5!l^=r5yt!^no?-5~XtwA=PNBO6 zn^3b>Q)IcZMdlt;N7$CU1vaaMPNBZu1neGYJhG^C&9;a&11p?fKeQ^ZM;>J1%_6B2 zB9OT%n(Aq*>v(nl&c}gx1ZMLxwNyZ&)P4Df8QMH$_p+GZ}OHF<4+j-(?B;pZz? zQkWkhJ)l0gr~a>=ZegJ`?w(Z$=V|hiU|YkY3cLMuT9xo`yDZM&8igm5+dz~?W0qB( zN6r)^I}jNu`Ejl`B6(%E!sXuH;3ugEXN`hcd{|6%I#?p>$7mfB@6Ye)ewg4@R|?y{ z&V2&?ON+2}Ed5FBXxUyLr;CBS=>RSQ2)05jLL{4&o-y6!9CYeYFa!; zmlh)NIb9JYSx(>Zp$}-IGR%JE=WW;Nv0BPAAfpO1_|5#@+9T>q`4u`Y{*aBLz%82} zB`4o%14#l~g%hHzE`|+GtlXUCiG`c9(~|eAaA?SCrGA_a)Zj z?IBY|Y+<~%C8353`7UftgGy(o_f%L$g){mOjUv`u=8PS6o8e6#X%?c(+S|v6f!5?5 zDmTy+By;lbUlv9JKe{U}!Qg@Zpz!+sn@MB`eSj3gRUd}nSDfEU0(Lp$eL2R3$6QT7raiui@31bC4 zzo;Ds-Lo`T-7~hs*=}Ua?Y;{(VtlotL1#&(RAoe(3K_eV^?G zY_Z6Xkl8#psPQ+6+MSw_U$Q38=l85jxJ<7~{pS6seuXGPN=!_7{hSxx=@|6;KNofn znLS20hl}xYBgM>79GD;5jfLv<8fV`K>0du#k-=wx(SE(A!-v&1FsqG~jg&~oharJL zCf-%HQ5oEH9VbI^P8lrA!iyQ_?@{ABZX7_y#URr9cn!J<$6q%x1~+PVm9U6PWRL2Q zk!mzPFIaxWZKShRxJ9RiQ)L|@ow1CAa}ra{kKPuKdF$*253+?REu{Do+feTI@zP@< z<@DO(4fLOBM&5B_t5;w}Nm{e)*nCVWAf7}mvUk$!A@JPYPozE2fYV?!VC-U|*wd8M z%>rk%J^To+*SBnQ6aN}}jW6AKYWcBn#YOzgvMw=tn2q=sBPFF0io!|EuopaK&P=pT z_{E!-p?9=RpT~5)bs9$5mVRg>4EqWbGwni1A{^dZSjRh?q8KPCGD)DM#}J4uSiUa} zL~j1!WL!QuH&2}&^XBenv!G;#)to91w_<~wY+h+o!fOfPczjqsyeizM&KSh~Zxw#< zrH~MW;1Y?Bjfy(IeDi7Lj%JoVyB&@{t8tSs_&#@FKX&1I6Yr_VSDHk5+jYF^7)PIr zojm_E0*T%%3!S$L%dMzi-9`pGgBH$a2fk7|+Dt@RT6tUqjRnVw?2MJN)7L(K;|?=% z!hK-Ie_Q1bk+z(VBfHi)%6@*Lw08U8^C5Hc%BuGwu@+;q2?c|=&$_n}Zk_RM`IX8c zh$ip@I;6u$$Qp$1;r+DdH1H)+vOhyOMt!9P3 z7GU-86q9TbD3mAoxuh^R=77HwL>)~zxxHsx@~cLT5aC$K$pFFyj6(m~A__Z}S=APc z>HgdCM@rW;tIeGa#4jeab#vkau7-i!n z^paXXzgJWDVs>jG40(U(#R%U`U5XF9&$9n-=Q*k?dQ{ZG@;<1ZG3Y3D{Z-$ z=ZCPLu%2{znKPVxosS9QG*@E}nBf=mQMV%3I?sReJj{{Xc>O1}=uJ`0xWF(}g*%RZ)3e{0m=I`#LQ`g=kBy`=tL zQGc(gzc+0Z|NK1AH`mQXQ%97O@q}IuO41f=sS_$}Yv6-QKAlc)4rK*mY3qdij!gl& z^W^VHStfrcs%zMi$4VAYnGGzVE=2yMG?TE;QA$LFkQf~?GGbK3h=|b;BOyjXA{;t; zsS^7zD@09V zQN24Xg@V=$3dFxvj)b%+4+P?Ai(F!*Fd#Xg3!It^ue|wccdvfM3>LG7u(*x0fm;JZ zn<6ms9^w=kdNX90?g5LKyT4SxBiEkC$TwlLazY;HBhV$HM4Rf%W_^u5136 zE#}{_tU3fF%|*S^B2hS0p-oJAkyuU<4%G(6Dh~&>6b{6QxHC>CWO&BWL=~_ozpVg| zTzi?Wsm?&z3k>o!;mr?VDk^esh|Bt%2CW!n^Hct2!pC?lM$67G&; zimdKJtP`--69?9&C9u+ksTx`7JtfepT-h4SE-eG9=zEbIBX*V;04uYxJ3(wS35ZoX zU*cgn<=rtQ-j_QMJ(6jyfLO^r1&ERSBISra)p}*x=~1_1*;UN|l5@f&BP=#TYFxDi zUu~TfvJC%e=Nev>f?ax79wy&ro4d-Q`TfnmYz#Fled0+AwWy>6VklDTVi;+Z%++MU zR`3Sbfen{;sKr@&Lmpy3BrP4$HDBM~{D$@jCfh_L>y*?Oe3R(wL_>Q+z!#^Eih#9Z z(4iaBw5cPl;9agm8i`5QLXEm3kF_7uCJ`69+xQ%S-k8uQfr)BmsNh+yQHC7%u`)E7 zzab9@oQrJ_5tkHP^_fo`4-I|F@pR+#`g%Ylca$L_X%x`O+&LtvPSVX>vuqUn%yr8~ zwD8w19GQFaxcHfF?CR)Q-rs!p8E=mXH^%JGN3t==aO~h&2%Y3s8)yMqP6A!kK@7Fv zwyu#lRZ%Ruv4E%N0S)i5e18ok%_<9q$xE76ZsiklPfmUKg1#k>i(jd>t_BhO2)7iH zy=XDc{5Oi{f(Q#OA)I7k`}o`+F|486`9P9q>aJ$Bm9N=DC)W$=*R{+&dA$8bH}>`K zL|=NNgo6@64S62k{V9UCc_{DB#GTFYscenxtCQjTpWI46V#z&4jQs)q42ZWmd*>C~ zT*(-Vjge|Wq_uK4`+;{hgR@t^^5yQz1La+|xySlPg}zAnTK`QF0>ir};3pmj_zwz9 zAtcl7K&~Hc>W*ivmE)PzK&XWrbw?g<@6#p`1<|e6RTSkhF%1`SwKb--Oh5)b#Cg7$ zhMc*pEW-@1SJBM_poERy+|@|at}GW>E4YmHT8R7wCy{yX$Ex+J;=Ynhv$Rn_c&HsE zNDdv^G#tn^@F;kYFTo=`LH8m$=-Ud>bE-|m1*WUBFAt8z)WK13C0_yuvh1n|L)lGv zV0@RfhzN_WT1OrhQ}+RD4Sm4z@z-}S3x`Urqx7z#MjGCv;G2Y9R66S%lXMK%0Hoj; zz9ct3+UV0oxsl$KhX--84X2SZOJf*Hf=u0)s}+5@>r#Z!ae=$A_FDx^D(@^aVAB1# zBI2Zo61@>hE_nQtL7xmv^jRJzS|eB69dT{d0;TlcJXHQpJY+?n+@wCY2g>R8KzZFB zLbuxkIYnv%Kfg&f$$WY;g3$SvW}lONIk-k-nEZ*O;IDl6kA-&lI*t~&!4vWnoV|=g zQxDiTcf)AC>mCU01k}81P8U?3)$9W=x`bDyKn)|`cP8F~fzaMvg^N;Vz@l)bE$c-dj;XpkFX^vAMtb$@asoGT4L*b9LfPGvqR^ z;~HI1Lidp2D1V&M83_?fA4p#1f}F|ZP6v*~%;H8nChn4*p0${)yuWIzAX1EvhV9^* zq3OPdC_dE>v-*8z;kC`D=(P%nS7nDtMeHLkl#QX=bfDgKW0d;Kw(^?6IAgzPAz+f;H}xLi}J+iomcMR((Xej44I8sV)FM=g^Lq~C>uJ3QCcY%KAI%4F44`x&3 zH8?@z4>ffh3-O0~w{A`LL*b5>k@}GLwr&l;Og&hI`i_}v@02@ar# z8?^u|-dHGIkv4U83SdV&e#;AR2h1nu8J`zC;|&8k=~nZL@tpM5BD#*}eqo&T67T;F zx9^c%zBs6u$gwwJQV;qJQWQ(MXdf^Zg@73tp6E~4do+&Ng-(uqCdX3=fzyFdnh)U2 z1@)fSz_j2!Zy^5~tiUCAGAPob&FPe#Oq~`XU{<#Ico1>8o*d3PCGk`Bb(-mWx!a47 z%iYiscgs2U$K&lq_7P#oA)m;{JuT8nAam8p?fg}6ckiRB~1GfPeyuD`QT>WZ|59yk;jKvzGP ziVjBaPgpR8+f~9(ze(OrKJ@Ope~KZW7`a*&lZ!Jf3UP*pfvtEos)b?Y{Y5a+tzB2k zC2i5hd9u-VsRKargiL#Hn_UmMVpRlXAS~u$UW>_vG#15>MiV|6r5fqpUVWF>XX#sad>Ed1>94Pt@CmSfAK<&X$vl zEG)a!Ec)bnANG!0C6j9IEub#b+9NY5t~3*WE=(nMJ=<`SYa?W|HHRveT z({AZR25JsoIt z@TXopTP=529vW%OOM=GaoT=*$&eV89FQOxJV;&%x#+QVV$+=Y59bBpnwj3Ak%Ok|E z-f(=?0GXV}blt&Y+GuNV$(?zS1a-TDuNo$k^O&wXcuaA6M+1l?cNO4~w7eu}OwMDv z?%*-SdowOWBfTpRjkM(@K|@eqY4vWNb+PzF`EH(gCAh;SUh7Y0s=jqvM>KWb-lOHV z_pEa@oCGy-Yfw^fn>IvA&E~J_Tc@=PY%q{a{dmcB!FxJyTsZwMu2EjQ)`@6c)iOU=!}cX`ax6z%tL>iZI28aKC}7uVAu{mt zBgbLBJnZBJjwU{K3jXPNT`l44QA{nMlfAtRIa7;=4uY8c`ANxi2#lD5!+OySF^Ngo zLf{l0DMQXj*EDu#$M7cQ$Q}eg`GS+lvlb4TZwlxK1u~f()W?L?Xo6NYtpe*>=TR0F*FLuc`@*bqO7l4?wbcNt7 z@f#Rtm_Q@I=PuxbK@JA4;dczn`WZgsLoJ))ucWRd6TiVqd;k_)`0=qzO-|9e!=1Se z>_emCl2uRT;rJ~yhX;8iw{*p2=G03NjVH%=#_|vl8p=hB{+1BfEUPj(2sm?Ta>>jK z49;$4oXKH+cYmpcWa+&HAff;}=P4XShHSxO-`bpgG(5++P^kXmb9vzYgKm7e7Hv8| zB4emnhq*O|9Cvr2D%7RN3J4-?^&xg4kW_M(E*mI`@6K0eMSp(tG!6T7OM-qd-A$Iz zs;BZm`teuAuwYt!U&-FsaV^!>iJ3~?2g_#q2|zEJO^NYjtFE?X4d? zJIyLdS3On0B)&tT@8}4|)ta_V!xx6fAehEc*^v_v!jua@r~MzCI|P3l*1rJ;_y_5# zueit9t`!2lV<~vzhbz=sAzslIy|}jXhsu1~W{;KQH*-r@0AuIMlk55jj1t>?3K1E< z8k;D9V8tefxG7J@NBBEDZw6o;eiD>~=9T0 zThrAK8Ht_Dpr_aoKR}?)ikk~sWTCadP450O%#cJhp}if9kdx0vOJ_x3MMqWyueo?b?mM3FT=Clb8>+gqlCC90P)I?4-qG!Gau{umc8F24`=UM2pgwlbBq5* z@y}ot7~}!?7129^n21{3)X);WC*+Y7kq+oBJeo)EulXA~Z#`7Yvi`^!(53+}0;FOH8%Ou} ze82*o>_j9Y-$#M+&AWHx@BiZ+p!sNhvaR5Wcx3=9;oiikV`2cDk!Pj|@Cig})MBKj z4&YI+1(12LeIHNhWxcodnG#6HclDFS^ZT1`5#IKVeS_mVx2?aEgo3+p6QTR^y z^v8kFb1@vCP9Tt^76M7FGiiP75U<70g{O)LN}5E3&p>P7rQ}(40>PwMALz7=6YD*< z_A%U*X1ChXS^j7NN7#xe+|}9fNYcKH<6~r=-~ozjm&LiXZxkHxF~DAlwYj|qhB|Ri zhGorC8|LKtL1@E-tt9M4{=RPCV}7$}3L=q#-hb1I9~fTy5)9N+1$d`TFIY@!69~;{ z{kN-tSPHgaEne7h+~mX5&+R{gg;Q1UV znLE0!uiA?C0^-e_acMOBcsd6-3KE9$Y+nh_9z+&hVAOCN_Y<;|AKw4`lM)z@{#)g1 z$9Kc)zJhSfkZdN{M>!t1C(r$8M|()&bzgaWbn?Ni@E5=f0HAsqc^v}m==SHi{{)(rHSc-1DdIaTj{Nu4tAvA&b zgjS4CwAJClK5dCUhSLGNsdF`NDL{OVT5b-guLD#4KpsQ` zz=e&2Vj#3!+z+5lAPArp_mcFHPX2k#k-Vn|4Tgxal-hN7Q#*lYOS27vn3RSN)9#duoN~F zXO)kY!zgj!i;JdWy|R&(M_oiP72V=>&dmG~Mx^jag98CBJ~y=!0oYZa$on((3An`d zf=hf;hoaI0*`lj}ktSlP{@Frp_9PT&92LX*i2V#!bTXT`7-OTyUpkj9olKz+R;;Ad zA~7|5abquL$uk8+#ZxfU$w_0Gf^4!=^$B>v^@10?gX&##G;^b;rgchl*(x`d>b(*X zLIuxwtJ+*G#ZcKpW%wET*uP1MqC&MUT@<%>9gYfa?@fa`!NsFL+GJo|db&`1e}Fu! zg<5@dhyp-QwyyO#L4?)5pbZ=K>%s{lDKyw14@*ms)B6qMevbk-&0q zUUbHHaa~<3y>aomX_YJF70{WB=s}+k;X&_G?D8*@#ZTGpn~~J`xriS0dC`O3F(%kl z64gH17)*^%Y}3M>*Q6qVdG<(EK`Jgt3f{-n0>lUsvT@2^4ie5qbjr^QPWcY*)bRaH zz*+NPv2I{^K4o}oKai@)IU*Q}(JS#@NBbC^+0ga8Rc2z8jYV+DA)CNKD2x_d{_6%6 z0;3ufQC^ncs?tjRv(u{_Fqm`g!YMWDO&(7oI{4=W2Y-7D^K$WkOOL(G>H(K+%L%ym z=LPqE2LN5%R(Ypv(DQyz+WN=n~eMV~6MaTYJM92PO0LOm&t%z-3BljXU6N|}g3M>{Eg&06T#5LMp z*77ZKe_126iBc`6W`o(HSS_c#?8DKu(A{5P3H@k?wK4y4XCVg#Rk*rve;z^Kk~Rey zo@3+sm(gPbfM_-nhMMg?TVyem48NYA55cyc)j_BbEocrQ_#1gu5rPZ%7fT4^HeAeK zqDY?}iV8mc<|z7TR~GQEy}JZKcJ&bxR}GlCn1HF%lL`)YwPJ7d<V(2mF{QrYwv4>@yd$2n zk4ii>cEG1oClsCvF8IXL+r79F4c+?@U4R(z90TVo$*IEaS(~mVghW!*~u*N4hqynk(Py^fL?A@L{(5t zThxZGydnwb$6w9qC!?^{x>@~b1sb^&r4!#c6fsh;Dnts}R5rA+pz4CAk_T(BG?cqxQWjzX zP^+AT5>c^V6+;1P9iAd3>V&+5?T0CZo{Q^KG+HnlK3#zSrTu_XU`s0cs|lD&-_tG;Stt9Ml4jJ{_~ zm^C#}b#5>dF+Q*^#s`*lYi)H-eZ%8Qo41gb@UPEf0Y&&J9G zM0}xKQZ}g1cJ-b>E_+EGjC=20pD&t&xWl>AcQ0PkRC$z7Q@6b@Y|S7jp&Ly`(fG zH<%nQLKuHpO5sLQcr65ki#r)sb@7!;fndB}y=N?$enr`)_t060_IqUZgRd4<4>3sn zknn}1MuX6v$RW_$KD3+yg)3Z>?LkAqHJo+7FJyDCDV4f|;a1<=-;g&NM+JSheSE}O z4Bt<7@MIrHfpGWi2u=`Az{3~9#Yj(+os12z*Fphq`?d;Acu+{Ek;*)%_nvzYTq>`` zfOLwkkwIOE5w*$Eh#Xc2!#>`o$4c%| z`-cHh_MI;mMfwC#R0!^LKvBN<&JZa2CHKykiy~1SJ)ghr6 z>e!U0#D~*;hY$3r1q5|RNyg!(Ho!jC5qDi;ZFop&lD}RjJ2DdPmvvA@JUZ z0dcXOJT8ik`AxYBLP>;5H)hE_lam`obTq8ocu{JkgFP91XtNSecqu%hqMrVf>jmaH zsLF;czpbEhWSge}lZHYhSKu=pVXtxeo~H9BwG5@=_`bzjQVQ17MwyDsCOv zlqYmks>`KCjJ{{`3Qp%GY@J7kpl9mViKk+^fB9*sq0D(g>%WKI|&>--`S4Afel? z3MDy=XnBYTdSGrHM2e~U7a`()MBb(CMa3KQAjveoD3na@lw56AOt-&?5_0h(MAEzR z_()q`6gVbVg;#akNBLQxeD6$T*B&WeX!nEuS6E%}{O&)FMXl|V0zlk#Z0ha(fd;hG&pT+oP zi^}f#(L-Qzne!H=^DQza3l8fRQuLxlS9N>AiTK-3is9Jh9_yPFeGe4;9w_`hgaCXX zTcnoo^P4B*_=uEe_Y9KK4$v!K!Qa3=@Ri+j_%SRj@sZ4LA229yID_(bF^;56W?W=c z7QEO=w;RH;=~=aE6U(S zFOn|}WfvFDbV7wfS;14DXrV1EkUI|%&Ao#ql6|z0UE;vq3U$NF0n|n7Ae}p*09A0R z_Yzx;HdtPGxV*mffGH_sDflDFDFG|kXllV7-&&cvSQ)zZnX;nL;)eS-2%LW!tCY5nGo(kb_rI*m$Pq!n>C#5V2rQYxKezDO&0{#(nbR;SHYK38?r z>_8SE8&4}Yp(S7mdrd9G26_su=KK7cy`!oC`LI^tSV-d~wnQk9qG<)MervJS{5Y7Z zr%Ls7DX_`}Rx)2A2`sJPhVLn`nie%{@`iFF5CsY$O<47+M33hrsFK;`i?I@?dJp#k zex{#~fqp}+qhWLzCc2+N6VdCgm3ZBI$uR2NsIEecdc_ovrV>Yj=|Ui^=yBKTlEWIw zp8<6Hk{4Z2@F%2|jf1$nL`&zIJ>ud?!4bNck!jQw-16nCCfVv+9d^z(mwBwr*873S zEHrYf6|vE*yhJ*JlPWK92^I~DjU*BzxKtu5b59;1-`hvw5fU5Z9S}0f0wlTec{Df* zUh{Hr{B`fal>V&(7Uf9z!EI%AwLpM6{OZwk{#R3+|C)WcTEUrJUScF(748f#uuZ2* z4Jp5^u!zu&9TPa$oBNyJeWd&q9sU-5%>U~h{2*D!xav8`!=rBp_D2-UBT%D1AY9Vf zf1{i{Ai*Z_M>4t#?C=X=C-aYuXca)4-}BH+eUE4izx1cVh~AX)qr+Y6m8|pZV1F^O7Egct_5xBy?N077&ej< zI3h?0E9|xFnx3#*6+(n{^>K~U2^k*VHrNO`RkzoHhX|PK$IdDb9YKpi z)j_9N6sn2H3H#AV;?TDiHg{2NA_Ao(PCSs1nO=dYRUwAgP{=gEO2yqT6)pw2GQC<+ zs}8lI8UUqmUmhZ7!6CxRL`%kdLJ%qLX<4?g(Hbc)wa;8uf3c5pdb&)fy}=jlr*ytV z##C`bODjw@5J7d1T)~3H3OvgRfTy^hr4@KCZiT9TU|Rd2#zW0&0;nnOO=*RiM)ij3 zA1YQHswp)j1cFMueykw0o&bc3&T6gM2}gnP**&_4XPjbY`=?8lRxdwTe%zXpxkp4m zX$3rWO1I6Qb+pKzb)@AaQaX1c0cu@vB(EEkA2=cR@Q!;Tvp{d|??joNW zz^TELFB?#;g_DTeOV-8hC966-)u};u7TOzUr|F5cmtIgz_NN*}T=dFnTy)Cw@2^Uw z^U4=p#jPdr65T#JC$Gs_oW#RC)c@^g6#!sq#f{N#|@wiaN`!!KN8 z&xB4b$ z(aj3H^H7mkE{f0+NZ%5o*web`Q17E}U4&TU=S$CjkM6&Ec=cDd)`OyZU$<2{gM4vRtnIX} ztBZ2UdTZ;vYVeYm>t5(@1*Y@M7iGojPW@2UMr*(HhC(p|Xz5VQV46zR5D?RZfLPH3 z-w$G*-_hStctIXuzrA}WF0P6WEN-|G!uP%@@xAxLRahl+d-+_24sWVB_)YM07IbkS zCxknGQ%p-A1acnoduwJTuc`nYeT#^wn~Q_Gg%GGKCa(8GU4V>(N*H4?~bmE_0F39Z2AeRo)qKW-cC z-f91$Vu??=$cGmhmFIhihfVKr6v=!6wYm>$*eW<2;z#kYU z>8JK(GWn;mr&i75E}d_lo?3lY@cR#hy6#Cp)o-du209#MGEg(fi4YvnXNBN^8t>%n z2DyGAr}DSR(9=b9>3MXCkioCdiot1$&7<+4bm)|J0zJW2g?2bY78JRG@U%vk8i!ZF**7OkE zv<>iqgI|Vt4CnvV8zseQRMQE+DY=M07(;}*2$#vclCOTmyO*LfHc69%j*rnZ+G3Gh zKDvQ&#Bl@M@cy^oze`%;02X;&^q$w@e_lDRVQu&4qnU!Isv|qc-MzZ>x%j<-yEVK= z7H>986Zrs0!*O$Xz<`R{X!%mOw}TQ1 zTP5Z*KeG3}NvpH*YKCY*`=3iqNwM;pm+_`(Sv_$_MmZ zuFt2pRvBNE!Ajc;U|)QNO$z(yBQMAr6R&Uo7TOS$@tb@tugH^Y1tpwZ%`4FK#Z)MT zgl5#x^iuA&qB2J=$FRw&KjccRjD+3H0wgUlZi$@fUph&tTq(DgUX@Q6X*vTywqZ-f42B=SSMUF8-_O4C@3qzaO?Z<+ zx@7x-XW);KYOwyzSAUjGI)&UVd2CBC3EIAiJtlD(*%Ct`SEg6xwaCcJxQzsG&fMpe9IhsBcok-dQ!QqXA<5sW2tu_E|3nNcM zxU5Mm_`X99G6TgpT1uGZfA)~_?4ILUds~KY2knC*#&4J7fjpP?Jz21CGjt3C_f-|# z^!7C*ik)wT+?(+4cBzv>4b{#y2Nx#Orzsf zK$<3FVu+@@PUmYJC7wIr8O;B{NVWhC2xt<{+=gN2X3$OtvIM=BcRm@;_7Mf!z3Uz7 zkgL&VXs#a-x#S%;U)lF6+_t#CAyb<$4+Frf<$$lyy4z{yKZZ0ZNAyV$`0Md`6CD{q zTU_FRa9X4Pyp3kJD-Tdd!B1AA4{vD-gYTb`>EQ@zbzUXYFIb(;5Fcm7j3V=MS!a{N ze2aA;bw{fAHyQ0G9)PJz{CAlN2kDB&-ly&zM2Ts}=u*OnYyeNor%MW2R(T z7*jEd`*yd3dA2_c&MVuj^lrg5|GZxVo&)O}$MF4|dlfGG_!1rt9PGKlkdKv`xc||A zU^MnFzF~fU6W_bMzX=v(!^GI|mD+x>Jz9=RobH#$DrNBHmQ=>)W#9K0q|f4>de zqmO`=5}bYf1Nc$J|J}fs0rl7~eu}Qo$bX?1z6qZ&w!oD>j3=S#{^nMV-W9@veg+;1 zX@hZE;}OYH`1gcD7_S=&uH;!5y|0zMgFf3}L%dNuWV(*)r3Qua$qt?rP&1yP?GexK zTl^dduM*y9Ux`kZ)9CyeMo&mp42n9Z-K*I#U9e^18 zQ^!B?pqRdY=NPcK1aIL_GAQf(L>KQ-_3%mZk>tTu*@Kx|;xCfH@aw-w-}5KI%gM8k z0htcc`=}4(YJDH5TmJl$jAba}e#lP{=y^V+ZKY60LkY0$;Ef!MJ2sBPw(^rsz)on8sD0|8ephDn@`)!8{S7(srN3Z zdk?l{IVirI+)Q28sk=cGCH+nAO)la=0X|0&VSttMfpCTj65xB%681vLHboDG)TB*- zf%vJgiGD2oR`6tdR_V7$OZm6xrS$f@o9&j zfqTAzK~;8T)9!vhxweJ*FOtn_d&X#4AUl{6wsY zAMl@CFEG!+He5-!I0rnHt|1qmJon$A=|;qcidZDYu@wg5{SdMfyie8D`C45q)n#;h zx)Km>OV@}j(~w?mV#N(c(QzN$*ieT)?2V7MiKVK2uhFZReXmE03_M_v_IWh2M;BBD zqM4p-k`a#^7tugP+3Nz^``gp;Fhff{_8ks#(aVF7y$E|CW-Ni$vEh z0cAL_6#xv!xC&|Ekkb+^9P>m1{~3NV>dH3U1Q4xmVUIFmIqFJY7XKLzQ%snDKAjx0 z=>n_(ib}Qgop}9_%WLiOTF;w*4(ySuw#35er*zSF=^TqH+ic$5Hp>~xEseClQ+9)p zCYpH8=)~rFG=SNU*3^uyl;0u(;{TSJU2t>MI;Pz<8`JIr6918sSYmjo_}bUX;p_zV zZKznq2G^4f8l-6)l^sg`Y8I&M3INaEKP#a~ByI&C%rm%qhk{SAdV(tX8Scald*^Ih zV7yrY)<&_=9@lUqDA`1JC1QDAfC<%JCfzsDg_<|yqYQ@1~WTZvK5WHvK5I zj7&zIA;r+#sS1OAPXa_an@~u@s9b&4ljX5UVW`RF^lGg_ua==07DnAF&sf)G(hY8m zaS?A~SzhTHJavupU8*ZdtE;7&85kb_?68}Rn)#6EnGdO=`H*Ou50R?*knkF&A^ex< znh)W+{&17ry&&&|_M{04+KD5(2AHqfIA<{e_LM7$lX7&)ui-%PE z%(bbwS6K}-Bor>+s>yppz~r9Ak4A(WSO)*Mg#332`6_;p!f5!J86M*IPqrULFf4R{ znZ^ny@|ACi{CVXk*^ocH|M@570B<~>NTlMMckh%?!oF4ByNUxiJ1S4^CmitDoo&Yo zQ;K0|qu1kyut4`Lspjv=+A zJS1xhz6*FEy#R~#!%Z4;uRw3+hA?mJA=Z@qTt3uCb~gkHxu7@i1glvY=k5EVzkyLzrUe=<6ehxj^yVL*`Hyo zBl-D#`je1I#XHiAKjbdX$yc0H^5cj2$C|(;R3MTIu_-_eWUAPv7b$Zf9h$7N^bqO! z4+Ta4A=308ib~W&r0YMFDf4#X8 zexOw8he(&sFD)5mIuQoINr`wR3Uz|!NTq(r=+qB+rTQVSRhJ+o(W}cbk!aQxQy=Tr zG{eQ(bproCqmO5ezK2}UB*PO)*d)&ep|1W=tE@ldwDruJBYmB*biFJX|NQf2bZs2> z^$N7{RHc;Kcsp$zujgB8-_>M1zj_)}#x&5BP=A(rg0U|ny<<#&FwQwG;Ii$X~Zh!rEnik6aKM`+Fi7?+icj zX4K7T;r3{&L#fMI=Hz4>Z&R2;tgols14PX$w4tFJC~TB(ha z-eDo|6$AdCdY4rr1|gj8}1i6~!`N<)Tg$#x#{)EL9U{S-y$yy&5espV^8D zra6ixxJ*aXN_0e6<9wx5+F;EQJ3Lc9tyU57Dwow!Yjt+1QA&+cYLvrilzD$;sTh?C zr6>wzDlfN9Ws=HgBjq!M#o$_|5H+5cg&NP%&uc32{5v&)6UMxH z5%m4*I>VZMf^}zjuyZ>FE23y(M+s%-fr9ck;dNZag+HJLuhSpay}2qVV}d4!f-|G ziifc6M#_d^eb6A6$=;cS2qhx7&FOIJ#RDpaWsbYB^FmbYUuTMY0Ojx>Ce=N{S4TXB zjrxx^`oz?l?a46NS;}PBd=8XiqgXtXR_w>y&LWZ&`*=DB%I+C^oIPK${bh+~nEBYKJx}BajtK!B5J8q2Uj^f8^9xNM; z!m-c|#)B!R3eGB*&wXUbGTZ27Q6J)$wqPP^7Kidt&2o!b)Va_*)tZZzm1kEkS#r_k zqa@Q+PS{0GY}PTX;Y%Kix`wp2@heeZwszDyV{5gx?pog0mL(Jq^gV^|V%0WATp-GE ze3LSC#Agq|kMR?0Ww6jke|V zp67bP`onG#A2Qy0<52xJ90cPRIkNn>%D)WHcF)NLa%hF;o&%~ZQ#g$ZuCfMC2Cib+ z`#r*At5{@{IsuUXM)A*h0}y!tKKVwm3>a|3M{g8#Fl-0@6PRyr8ZA}hIjQN1$GKvl z2BcXGFN|l`3D4q>ZK>hFrB~S|V-3N$h54hH_vf@tjPQNcI``iA5Eeco6YlQ!j(rTb z&{57pL4+u^5N+uEyr7$}<=hIK0Se9gl!~0aN&>h34<_96hPr} zLdxPkp=SjKN(o_vEaB9)aAxcSVD2{Dm?HvD=^36Py^HD&*KrfMZ%gjmrtbM+){HUv zq&OQCqx?t>hOnHHX6b|-1x7G}_?%3@gGH1u5Bc_gNWu*$WY)jWVI4d|y$@y%*4|Ez z@jO)04S8|+?~wc=NIbM7&vaUWX7@KVufYIB`&c*!gDTml7sug0;+($z> zqEGb1H)I@*7vlg-P=Fd_tu2x=ZObh3!YrI5iRG7g&t-UCP`tPCD!5OhKShs0IEvIp zGCW@GQgyT{MjSF#=OO?yVJh4p2-j7|sYoYbQESh0+!iai5;WqtDrUP%p30LtMP{OB zmSUyv;IiP1lxmAp2oGNce zW`<<(5m{WD7nGI~E434+FJ_f~qs_RSo&i?TdYuHzr$%g^>Gz&{mAY`ara2BmU;F_LL@!6N_bQ|iTyU@l#9DoSaFJn05i=zK&^eQ+O;ibl|I*S6esB> z$^wAmz~4xN;I5*mlN0e1;Y^wjwh$}R=vgY8Bugb^oM5YaH-`zCPakM=yY|OpDVgY4 zn2=1OaPG2T{E@^Q@XX`1dZRA++Uz`(3cr7Yn-Z+K6K|NM@E%|3K9=nTj&#s%-=J7XlQu_twX?wP}5$VYu z|4fbIdB~Jhar^jZR6ISx4fkBIa+d7Q!atKLze)xd6E1RmG3hIYL-NPqymc2rWpEzb zCQv5u#hU;_EvpINC8rBe&@KT!jr0)vxDr3b-O2F|FM)lRm9=flycr$XH!F{Ou<^Gv z=7e-L4z?#QY?G&pVZ}#7Oq-vEO+SNcRXpR*J z;NfrciC+_4K!spdEZ-%o3crj_l2O021c=d(P|lS-my$njvjHs0uY>Vx(N2wr`o;0= z_qaivQ0er2J;1%6Yzy#sOQ@2pJ1pxis8I^Xd5g#S#qsU`5ZaX#W0Gk*G416w_8RzS z;5;AkeXs9?SUc#Y-BEu0RWU5f!$sYiq_~*!VH^|SvsSvefvc*FZ`w_c0DT9w zLkD+?zxy!b6Z{Y2yp>*z4wlIY^tvj$Vu=*Aoe=Kji#o9NO(56afXy0TDj%Uvc$hfQgwxLyH9?w9Sp}Evdwb{n136dF2%HMqsTad zBn^QtbMVr_dXd`KP}{5QRK%2!P=(3aQtmU^S5r-7nBVC}3^_QDaT++WXf&dNok-_Z ztN2;WmCX>~#DopTNI0tnwxGphwmOInwL2o6EFogk(F}Z!%RsIoFy8!t*^4k@_SMfH zPsJFxQOXB%BEcryVZEFTVr6vrX;jvpX}o8?fIz?vts`yUfemr|w`lVBBxj8HyIU^b z$aksFwGA%C0g(rnYYy7S>GA0ThIq|%@cOhAC=0^$Gpwy*ub(YBfm%}9UM@_|u>GX+ z8vm+Zq!Un%*SI{aq}|aeG0W)QT#k|a_b}3(7Jn44Bkk#MUzZ&tRUWX?C+h{$1j?Z- zgfxEGfP{a5o?M>KReTs6dtruuJs>C6`Ni9$OrJ&kBCP_f1XFi5$vhzoHo!z{X5F0@ zd6Ux&wiF#QRb(cDW^p%3?t1qPC}jAj$$0uD|5AMy%%cgFvIiXpa}|VuBu2iU8($ZopW?Y!wax>frTb@myuTL?*_@+C(Vd-U+nH?iZ6SN>Gf% z4aHF^kgwvD9p(Nlf<&A};w{Sq6WDsgB~urJ`W88Ld;{KxpWuB!KlF_e>2t*?*#4pa zA}aAhme0xjd(f#kc}96U?YCbH2jkQlg=D}jz$scWx>^^*swM-mqZC)8g81(tb;|9I z>VpHPe(=~J2Ou7~E*dRRPX9MpEILIp{ELmOllPXAdsxX3QhLb9TWH1sUCU>1weHj3 z%2S7P7?Ri9Y5-vuOsH?%bNAs_ph z=%R~Vr&PRe8eUN8HDVE`t^GMPDppocZQ$hu3pqW0(*a^1H7b@jFm?hs6OK4*k_b6r z=VrBV;mHpu9RtDM;~DT)bKE9SA#4l(@0;mMKV(%b#23OUDq_Yswm~)@Eh^N|bM<`? zLIQx3-gOZ}CvnaFfbR}Njxi#(Jd-EaNxm_>P_y)vX>_X$()K93K%pepxJpy>wgvS z=!t|7wP3;Dd1jlQmfy%%7gb)+6Tqvq2{NVa5HAKA=6^`~7>^u8IoxjHlXvh?X@xxg zj3(kn0l63CYU~n`a$_At?Q>ie{A^V$fp!8+!9yv(VIVRJ=}~qY@gmCtt48lPZ?1>g zs0!!_jpH?_=sLUf!gc#bOT``sXW#QZ=rB%PIkSPVoWkqxJd)8JjFgFpmS8nIm(t@{BM zYwst9pmzL*3&i_(fCybouLictrn}79B4eQ#FOLq`CfY*Ncs6~Uts+1kkxf`m$7^6- zn_n{a@~fRjqed*jmp*#p_wOPf6I4kUfj6(_IFuQ&k&e-+f|CN$kxx@547|FK7Y^w)v3T?7 z@C~zP9FyrPy-CM=4>k9*EWix4&XiNmp9V;WmN-V6#KB>1etROQA~R2QfjR^X zGKHz}%Ei!iHl<`ZQxpA^o~ydK!L;|a?x99zteC(ny;K*k*Oe`ZbnFvuN5?!-4C?8vtI zGsaBZ%f@46?>;OO+Dp`I#A#XdzdtULNRbJ@^uFL)={bg=&LhTtTHm71om;7y~nt$4)G@O z#0s*kOa^y$0_&huv)(TGVT8UOicYV`?Ya8*Mt8B~&|g1OIvc7~@#{~)y)H9Ae#^-B zl6z2@a7x%f^#uWH}4I1(o+X z8pmEQvIvpCAKpA5KH`f{)a}NvcO(cMCRy)b0k-8_VBhD(L?3)dB6=!6gxzhCm6m^9 zce24nUP>o`kHlKc_po=EI-yN0x{g7!o|KatLyh{rpbVu=8;0F}>3xO<77w#n7Tp(f zxn76LT(cyv`;^5*BJi|+EzuIW?4{6CA{a$jBuKA-r6UEt%UqR=I;6$9Hf4KD8kJzd zSZM`)^EtHr{Z?8xlMo76Cx;rUt71?h!qx=hI- zUcyz|pb0y_%qSa#?--65MXEnFO)oPM)tWo~JYPxfi@;O9YInrWEDnGX34syq40l;w zBKg822~D*hwgpU+uM~=>!8aBT*_{m7H}i1R`~cPDL2dmh z>41>kuN8>hNjF<>K7TZLY`N&A_1({8X{1GPmQh?pLsbMmEF}!@MVvXIKzL1l6Gczh zU5K7dVhl*9@j4emTl;SDh8||8L~Z91wUpSZ5wt@0sl@Bi>wP2_YaG&uUnJ|TaElH^ zGrE?M=gwyI{8L;9%$8to0;%f^T6T9zD{+;2fCk<+dG#o4%Mq09;`rYrmBg-ZTC|Rz zDXafZ8KOx;3Dd4VEjsocw)v_Tw8ik>QUDi&(2ItjmIMsi+joN4mxmvqVjpe*rdNSq zNZ<`NtH`JQR;50(E$Ad6odeZ3C>`4--Kyra2^lTjb`KgrQWo)_hnLShpn!&rhFBdpl z0geK;L$=PSWXLH@qF5>W=rg{9bTZJSjJyY|9zW%fJF1}$$3n4mUgi^c3IFSr`jNjq z`p~P%090Flo)oUDV=QDlT%!|@6xTskmoZT7)i`@%cEI8QODO~wBA(&|Hm#W7;=6{c z@Vhm;cjbDpR>R2)qSb3gHfo#jm*?%8FuipgTFNWd2XN8rr&Qq62bv(+MtT}7x0x5V;{P7M zHzU1(tW_}~$kG1V_CeIcRoGFuqcPtR7;}ccz5&LQ2~6D;)ZM-kU96^%8aoUPb{J+k zmL=oCO*^CW=hvj<^~`|oe=vNzUVF``^(v2wc|{}L1{?em_3zfN4m|WN3-uJP!$z=N zHtI8u^41O;d+R{&%jvHFx)WRZoLc<(cEG4uSP27l z9_8}o7L@Mm9Y&UV8!5ne`h}kofBR#~n+!^^p?j*!+ii_Om&Cvty z{6g?r5}*hd@;3btiw@v1Y#P!MQfQyBg<;7$^0ks&1di}=@mIf-s{Oq{ZQMGqjw&#q zXg%(w3#L9j^9l{}Ar^8dSnd|ucl=iF)euZ(PptRP_##vFBCiz&;QJT>;V&Rl^|zMPf+qQ;K-i=c zOa;B*ri};a^MQ*ufQo`lSqe-Iz$kA(x#n3 zHz;IOX5-0rexemxs{w^=Mbz&Xhb=234ZH<)x<@5PujD9Aw3;(qdKr6`h-NMMT%aD$%Hye`pexTd) zvAMGo3vn%*E5_Dqy6a`7Ujuwf=&cEn9m(s)z`b8Kfc)oQ>m8Cq1huFDjDy_lhc>^j z8lOiA9xWd%9K1s&RTMhD%T1Ac3TbqroN7mQ24!pA)#%n2T+tX@(1k-H83bha%o#B@ zQ3yfa{Jp)(A$w2cGnPWb3}LBaqNOvl+}sbY?n-bwQyG->Z;YzK@D3Gz3gqX~-7eD` zVGdVV6WdaF4bF`*T&~=hTRRWOfQ>E>?r!Ky99QU_{9j9udFrB%-In@yZ(hlfVPr2y zS;N1-G?`#@QsuiR)AZeZkPs<}XcZIhhdAL`$V*6PZiG~x;i_@TNS_^@p=noc^OA%8 zY;n>1)WE$=6Xrs|pTz#0-(o4#n(dw!hNZ=`2tH>3esb8e#sA9R|2)PelZ75Cc`rxj zc*ZIebFbkQVb3cCkK$wa$VPzz8HJ85%H(u8$qm@C&1g0 z9)uB3GrXuA%?ootKA2(vD15Le6v z9{DxdX~da#*e*Z$r(Hd|w?Xs^!xwXUL}S4rhM;!f*B$ogTJfQz+^7j@;PC5f>EZ8c z#7%*)cd~s>CtVQ_-;(k*6Jx(^`KP&BY@kK$BG(Yv^Un(N$L-Mt-ui;Dd0eZ%U`O2k z%yVyIHt8mz&6}oJPgQU=n`tuO!`JM8j&-qSatUB&{t#Ay5j*g&@DVKv^(x#wYaaE- z3ccfi94pY)P5_3kqWq4A?D+&df)WZrDN;p1AU#2Pu|J6~1lijya@ zux366;<=e7+5V*J=ck*6Kuy?sh|h?Wlx$rkNFF%->j*+BaNRRT!rCo&)7b0N>red% z;s*QzLekoq)LrwJ3FDDt-VOkrPs@lY-#IkT}h{&wv61NSKJsbdK@R#2BWP8W1ec}Us&c_Pp? zd3Q&mto=Uk_oG>QVesPCQ>0nrO_RQ21Yh;}M#PGlu4iILA9H?jwm}`96HHn{gabSL zV9zv(nE?%az;$%y^TD`ZZav*SxBok66;?@>17 zr#aFP^c|^#@OkOYP7t#h-;Ifldr3&p#!*W$U=J)#@%X$^%k?IDTGG$6_>PJ4?E<6& zN@nnQjjWZkHE4pJdIMnq1$~0F@@v-7$Du;_8j_xm2Ph`65u;WA+2Es>VgIqJjxlU4!m8|YIne((}Nq>SiIz1{sC(2vI*TUp7So&e?@F0lV$tok~1 zZa83mjg)=X2pT!!5=&py=l_#|td? z!OwF_=Uq_e+W}T;u;H|%tO_Yk=hpB-Vm=8T2@$TczB6)y=(#=coT4gjMDLFJWZ`7mV! zG2;q=w(h?Xs|}BhNYACGPYNQO8NyqkU5me|JP>p+v#0~fJOg%qMqqh_keJ?}Ia$Xcw+;*5S9hi3Jb=BB2pIj%X}; z%G5WD@~eP+-ytr6f|h+Jq_AB-SfNGVmf1JT*R?C>_Au8xpf9ETtU^=Ds%*kPD3=~@ zmz=EooivrwmXQKik{I1Fi>ST}Y_n>bH5p13lvBJknauUa>$7Vy&b(f1((*fd?OzGm zqsvHQekc3F_C!EX$;GEYRT92G0sL4u;>Z<}^~eEYrT!9pwfPa^(WC_bz*SW$QJ%MZ z45^{#Fxo=AFj7Pn$r*Q&7JTt1*TqM!tZ2o*E!q|#1xX5|O$mWj5OXFjmivDH)OWKX6qFT$%e1YLx;P=l>dOi%#;xSed56?V10-87sB_Fjnoy z0)>`8j1>!Rf6*EKJS#5$(&0ahm3UZ);>mv)t1ia>#aOleFUIQb>%SQ*{{LaD+Dz&H zL<>#{cHdE}rRwZ4?06hp0FT36`+WT@tuSXu_rpJrw>XFsD{PXUtM~B+{>7{==3pc; zVn0OD*H0gb7!gk&bdCO@tR5eu_6g%t3qF=)vpE~Tci>oM_-xyII-ni{|C_RUqyGD%;5Y%)~7>8pEO*I>vfI0OtT)XGM9=ynZ7h6g6LZch> z07x|l$7)7Z2iTQ{*&mo1qRJso!c)lMC!decB}g$3R76M&C)Z2VRm__)i(8cihp}@h zpxovW+Svk!@k|}G6$$i=KktlNPqTR#F$bCR@fG|Q<$=v^pOC|2=o37>d3f@CCc{r~ zG%H2>72ENLRL4$wECzQU*8(s_)=HFS5cC&wY#Dooc44HiqiH?Gyg=a=5Ax!Bh-i-w zS-CV^T=2JS@jj0r`_RebX!}R(`2<{D|3|F(WLRaG z({YHXnRjmEaY+992k+SwTv7NvP!SuCV#PFE+0{oZ#SC2H(?_gA4zB*^tDk%>E&&XC zPotsYsUZ3%a!F(ANNv}$|1!%Otix3~* zk=)tLk2v<)d(bjO-g}&wfxq7bs=~iw7v>mkq8lBD=O2Ggbnj;}`kZVxQ^-$u%fwTu z4bQ?7;N{S#$^Y)UVw8mCgr}&;7#O7j=MV(GHBA1E_(VO!HhA++D;BM^@79q#_P|pR zJ0-vU_4kid3fWNW10oJeWxxIqt76hXm2DD^2G(P=r$Yyky2|mv=+JVl4e7d33**P2 zgB)u~&_==JrTlZfBx~#$D8trne222mO6`V|n>EN3hb}qhh|~Qsn$jN1ROhxp4oYlg zdVgsq$^eJa5MwzOe`)_HiY8cA(rK;)^GHGwQ<4t#f+RC@Sxx*8ca@}wRRL*H34f%} zhrB{jDUr2oUSO!L3gJ2A`(2Vv3Tf5)oz=Ozp z^uu2*;h3zgMgQYHa#qDU=&7+M8&byIn# zB2!U5Im*-?@>C~ogJPsOmjW^uV^Ycn$(_AzQ=@%WW}VQ5}Y;-v0Lh*Abv*ZNFvL2NcnVw5!%`z5IB*t3f);oEYg0K zEX-TxRy3$15?k8bVu5~I#1_q30Ug@QmI=%ohBthmxYd%3Da;#zvQwnm4OKzvobqeU zb`Sc(M;q&u!(1|}2c@VJgo*07d5x8vUN&n9NaE0_ZrSszk@yj)(rlol5vali)Dfs} z^`?1L>SPh90UxA=ZVWptCIK?&g11-_2wm& z>*W7+d7|>~aIM(fM1nBUZErd*QWlk%QzsZf{1|UfS=C zQCmoj;c(GpYod__X@Wbs_u4ut%W`Ov#YkP_YEq3C!Z9j{teHcYu>(pui^PD1;Zv+p zhS8d_TC-ZZ%6wd$WEN4M?M_QQwy2v*E~ROp1B-$>ZY_cgK!mt=W8geNYlK%=QkvT1MAUtX%3 zE(Zs>jw=&y<~OO?LT1`&Z7#MH)AM)( zn&$2jGcFJq7oXzg-H805I$%&7x?_YC@RB%13Cbwezt^1!2N#|q7$A%5DT$Y|eMj{m z`^OX;bw_rD{0x{G=(8O1?ydH>3i}gsq)%`Jx66m}newV2d%ldJzo(|5Zz9?+ssdKX z`U_sZoJ(6wh_(ZmGS=O=RFQ|0zg)jX-g!bdb9dRzzc&0_0ej*;7BUD0=^r=jgd&Cn z3cL~nGj1-3aa5TdWBbV(nO>HJP_8FCFh7uU8c#?gS2YKc)*pcSabI3%MH}P=ro#IX zj!&t(UyUWXDhBTSe(aSBBPc~5@FmST4!Yz$ey_ULq&MYzCx&WWLHDhC<7Q*mhIVor zo7S06PN=|STp+@zz?k-~SQ9uHNari}NcvjLj}^Hdb5zcYMxRa67pfjb7^ex&^%s>v z_!&y}FNL&rbqbglM9h#ZM#_8MVRlns6af|N(k(ww!8AX$xC#%-&{5?HO<=DRW^%FO zbsG3+6)@`}(E_ZHe%eXPB%Ae{x|8YOw{&(UPffK6coR@+$xqLm9)?u(OyAetW`fAihJc<+N{q40MRZeEDbpyJH zDA=&c^C?cY(60{?pqEw?fdK81CGrB7lDCQf@~E^tY%%n&lLu~45K1YbVhJ^Z{+3*% zNSzxI*_~nyHv>MMd;BptuIka#&kdU(B--#paltJ{5`NbR5r>K#p?fueKQ@D3=-qI7 zL<#Byww>7s(BobcX&g$6JJ5iVY@Kyxko!4%oK8Op7U$J2FYz@BAGdVFtt6 zB-@bH(UDG1?vlk_bP7g^V}T6lmgiZ#knG0K;T?XXI;xNVo4-d{!gzs2X>!>hz{9Vu zy&gY0LYJY9)_3xNVA7RRfr;-<#RwYFBE|!gZTZP^B#C6aQ&$fn(=bt=WHEo&P_c`U z-J-6jpPYqJcw8G)7O8olQGyDi0RPoMH@P!{mjKej8tDaT6xWswZ(q=~zuJr|li^C1 z@M_k66Jb7hVno&+zfA1G9^FT7?1jr7xg6DR8gN(Rcg@NrnaT9~4`9?uH=F~MlXXi^ zrZW5pp`|ifQm?|m@q9JG8N(u_BmQU&r7aMd98{9nk zeoCXnEXiYrBH{Z)A6}BU9ew7@Dpxu{DR4DFw>@<~K?CGTAc$CLVMCv~GKPBP23co7 zpp*TnCGyS~LE(^a@=zi_+vE}7g1?qLm83Wo6_h^ydeN)^U8%WIzL45$-BYoxG~@#+ z&d#w5t)!wEJg>MWLs(H{YIY>Lwo__mApAhFiPl9zmn)vnruOt!lD~eqfBtD~sL7iU zM8vgSz`l-zcf&9Jg9Ix8L>mYxd1K9_NmW?`6zO|ZnfnMt^lyPvWqDT#vpGI>ruC~= z{s2(uhK?avsERxs$oiWQc}`hTNvnQyX1wn;P7;ezK&K(srDg1@HZ z6Tv_~C_5!J)Z1ISEP_|~E4NUDYU#IGvfn$sr&B|aZ|&J#sHmHyArNi}oogRR&85~c zU4D_cTYD@)FYDPXQedQCsL^pX^Ovh1+3kDf(;H!}hM4q(L5EnZ!Ep5q#siJ$nZMTG zPMa!!uuVpfQn?g@H8!sTed@1jXQ;4qEeq6nwY^*ugB~WEvH<4JJ)M@>`!qz&Dj0lP zZ{(7WKkon))!oPP+{;jb6?oeJwm$Hbqj38>m0@(@Hg*1-bg{Dtvfm5p!FFPIu8&P9 zKOylb5B}5Jh>62~iGZX2@o$Z|aO1jbop}d4?MCHvK-(w<-k3AciMcpVy&MSt!c?X_ z4LCX4Z>x1na*X2)aV|Z^*6nQyzxE=$xD2*5(MVxTj-pO@d2Q$#5zVN^SWJ!(G!$x9 zH}O3D!dcVSwEH4R*tW5RB5<>YJ%w>N{3DTss4Q9~*HM@p0kLRQ5uu*UKz3>YvqMZg z(vc{~hZ9@xf%Sl*w)<(phU0a^2`#P_$Ivtp0S47-Nsi-vl>CH|Am}!0C(%gEc$5&a zusrm=@cO-A<7;~U8wQZ--rY`xUGpN)bVE(Hg`}UmXr#b$dnurFa%dg<#t8~{+ z(G6yYcMkqCem%#2%NbTBmXRl%yPE0WBTd4Lhgv8HXki`znKA>8{gpE=G3JwR01Unm z)e$Et&#d@rD}#TL8mvqoR*J5pJvYCMqQfH6IxmIMjkdH7vI~ z-C@nU%o=LFweUU0?pZ=KD%Yb*60^pX-UQE$+vE=L{@Kp2f{jJ*!6(LakrcCC2Zr zJNk}|`=#hv#1I0vUu7rm`_}w1ZPmFe3I%TCi4U?pZKRbK2knl}8pkSa(sAcd5%Rj=FjS^{g=o!5YInmF$uMe`4E$L{a9jVB zs>Yp@;de+Gh+fV9t(=L#o`bHV1gHXWy&r8moUFx!y63Yke7^#}!$C-w0~*q}4%8jj zNsNuDIjRgwy5yZ(7+fa@osW6boK}bL5*j>o70*J$VfX5Z|5%|j%(?C!R|+eKX0a61 z8aM&r1~yO^gZq0_M^c^xq&=#77I3(YjjYTzlko3bIa(v%d6pgfPaBm95Kdv?3lb9? z77?r9Ts?XXv%AMdFbd-U?)_R@ukg^W)I$U~!H1oe!Fn@F&y3{4H0b<-etNsi`*?nO z$ac!gmd*z&vh*6FNH;qmEu5XK#0cZECi6yeN>I6;#$Lfz7T-?cR~$PP1GtR!U*|-= zE1^IpU%ftg_HR`K-3VR(c9U652C}QL6^4R85zfwQiGu&xJ-*l)#BEwh z;X?*r7lfRbcY?z$P(dI8nK$1fRx;~?41E1Gbs7+8lA$BGSV4V;Qj9+^poNbH>%34k z-fE?b5*=f{Q`=!a#)vV*>;G<4->&=m!xkr&sBO=skgRagtHwSL9dr-~jK7AkK?F<^ zS*CcBCXS!RPE(8kQE-xnz@Q+naX}^#in69{9=U#82jso0wa?Jnnj*EkQ6|kNsphul zo*Ye8g|&@A#tQJ71dL1rItl*zlC#{-m+=Qsq%Z`7?-JNa98Dypk{dq0hQpo?Jeuoa z?g;{T1jxa)TkhkCh20JanY1PnoU(HwH z*WB0@LYIhX70`JTayI_A-1)=}W^er`rl8Lp#pUo2{F~d6B3dKHKdz00GGRHp(*g#W z-HgCseLJHaopm1kSjZJZm26~KZ4h?wTuWFizT(3;PnaSbjGNtn12TOr2y?BuR#KrxY^HCMIN@5H-3L?=%C zgzfj-xgl;K_PBPV4vnWki2elF zW8(RavhV+48YVefjazDZ897G06n)tMzpKzAF`( z0c`vDV`G98?y^Ett%A|(m><{OVr7R~T)V%Y?(>dAHE?Dv(=S^@N_7BzU_&bFKNtcs zxnAVN%v|GMpkls%V|m}&`TzQZRPp_YUP(KO*nJ*vA&Fc$2c-VTBa&lQ^@KBIQRLgK zNLyHjLCkAg7@eJo3qP_sq8~A`>$VI<8$;c`;XNZ+c?CXcoq^2Cf5!&+w&GFh)6};5 z6A#_GaVL?2UOX2Ay*WW}%d|I;nL7dePG{dh{cnV~{JZ)R@n_i06RXx&=ls zqKuKIg9-vf8|ty%4ExFY0FhhhH=~!~KAkMLh=_!==*Ks#)-kSAHmJ8w^Qp-=?E=zg zyd4dFG+pBmq{uz^&J#dDDCaWcT)~XBRy$hS)Hd437tgxz4A1_& z!s&eH8bWwrFvH|ieqm>?7ss<;iT1DX>m?#@)Naa*NPy^_Mn)%?_!KUbz6!I48)UFe z4e^O5cc)MhYE<98DOKLOb7Yp^Q{+yE0F`TQZUCTtx!7|JV17Xd+ZCN0f05t|QN5;N z>b+IQwhE0+tfoK;zx#e2;Do>>L)*IYH{0l+kR`&B!f=|(+Ec55w0((?0{>^PbRFri zn|`&Y*mKfENTX)LG^l79p%@yqo4!{fMO-cyhTG;WNh*N!JvYvr#2wWsFaTKpqS^y2 z_R^N!er{Sy>Sd+br4bunU&NTmrblI|V(S63$`~-4?<>y$t+2|-K*q>K$vfarSbt3* zbfRHJSXEKAS+3R5?=}A;@vzqCtoKaXK-g)yXf`KJLz!&i$)D{nVR~UX0$aWOX636u;^yqd83Mef zz-G;i-KAL^1K8Mc(k>~(Z;~4A#ODG(Tx%K2AT_^z!&4P#G+@|P8V-fg2Jiq-83nE4 zO6EmekualbgGpOvnxKJy}8b}G4su9%LSft z%FH$SJE~V-D;EM`5%lI}6hC6myNX6i-N6XbGQ^o181qN?2C$s+Nk5e3aU}%t>(Xm0 zAcv+lyp+w2W{d+v;#kF%nSosr>GGK3WBnq|D@6^-0s+UyP>oca&lg84^<$5vp_8gx zma=LY^{U%H(R>hF2Pz+e-sSF5AO2rJ6^n_xUia54u8+fCAgWIpXI*YPVa{2l`7-h< zkn8$;*C{A|PK7bts{KratBu4>ud?aiG`!v6y^=lr9fpR$5Vy7THm4fYjm$wgl zDkRCis`M`)JFc(TDk$z4ynVKtBdw-Kvo}LWsnJ)rue;grijl^M6&U%I04OPKST(O> zUW%4q(y(z(TE6)n)%1WK`Uu+Jp_Nvg5LUM@dn zyiWl)2WL_o2_2onVD@Z8Zyo)0k_ zsS?aYCs?>)f1NCiK;hOCrY&Al8iI;$;mp%bcyqjpFOBf**GmPbIQR06jEJhAs!zo9 zxF>zK8(`titg2tE12x&e$zPq_xmnenzJ5Anv|Td|yugP!0Bm{j@>*7Sm zT=eYYz}8_IKPM%e;B4mNxC=;jTY0-0f5h#^7aCIZ{?n%ybR*6an_38!ii#KtgvM1T zYO0lZ!Qr=vz4|DlegwM57&jh9iW zonjT6SU=fUkEF)eXE;O5x2M)q+B}@2r$JjFHyU{5zW=`LB0q~6W$(3TW8b*BTx+VBe6`U==$gc-eEwrtB$2Fl7CUV&@dCgDzG zEoK=s14aoNLq{6=C=}VUGmdyG;1yh}?+(}&85$u+5-J5ITsbkR1eFfTsviAN35s7d zPevR{lT5LrN!7xYZ(UhbBg%&rbgV(uBU1Mr5^!hoPz$8~WE(Q8j~t}wL?2J36uj!Q z&Dswl=iDO3o!g`xPEj6IsRK%JbMJ)DKq; zGcm<8)iaf?|D7;UYa*#KIY+)AWVXRG3li$SR}E%~ZzeJ*d&ZEMJVWX=+UA~D4u~=` z#WynyQ#s)pwr@z6>0^<v#DE$2=3(79Dj!hT!8>cN6C;X zO-cm$Dwq29)f`xZN8{|(T-a6t%0)J+L0kXv3$Sfki^qfiPbU)Wk~7(TkAgf)cpwTb zi@O0rI@o*XXl+MG7fg<(`&DY=`!)B}I)jLgPOSp8i}YxN6=vn&5o@j-DF7EY4wCKW zHIIvjD}xeqN-N0Li4E#||E+nui}p9`+tn)Ldx%{cmXw7$N@4W&sIC0>RkL#LS@k{^ zdNP}oNhoKrk-CWy?TokHn^o#$pehtgI~7qm%3!^px#jPxXSrMd)aWqKuH|5l*5Vf4 zT1@dBv95+|RIVM;+MZ5DOCbB1Sv;~@mGWVSTYM2K2V(hGbvu?0xW% zC*1&njanl{XA%p@1pkZ%>b<{F-Tfxq>8j%k!-QUdnp zXG>N6X5{1O?FOCkbl%fnOBZ|Y{!P%rz|B(u-#L?S(l=gR+#HDxXKuBs=CP=CHmJCD z9e$_k*XmsGHw>a;o6;>iql@QJ;IT&HjsWq~nX6WDkzDHm1p=3O*kmhL^ZF?ADq{Ki z407wrtwrP*Xl5Fw2H76MKzS7cRZUGJZzew1lq7vN%ImRbo$sH%dg(FsJ7>5) z-E8@~X0*W*jKkwdUc#>Qg znHI*~Hx`E%idW6N!n3kv>J}o%o!!}nq-%;-&mNIEy7m^&+;XHxy4S-V(^t}0YgRf@ zhrGLvn<$?CGS?W@>D0JYMSQwXS?FD?SZGro5m_IqS(4gKWM zKXw2;!Jc~Qi`LL8v0kO_mwQ3{d5VJAzWExAJry?r98`bZ!o@tT1BKZ(*+2}%zIL)e z91|<0R<>BQ=9cU0o^-4^S@BnOLE`T7$#4g0yhfhX zeBQ2Hch9{<2#T*fl*4AM~57ahx|lW zB}p``B{4i0lb)y6n0@fj@%NwsjyrMf&yN%7Q?e3mQ6H_NI%1fUo+r!rf5P8Nq)|+n z-}4KCcZ)8L+jUF!lrtb^qHbqvxNhSdZ?&uEZb>DsOog2fh0&Zx{O$G>Gn=MA;kMsy z<3SI`irKMoGNWb=kiyw_(NCNQDAnhj5y6Qm*=lhmodrS@s?91{+;g#a^GGCjiBM(> zO4%rLk#~3Zc>j(RvoB_UVhfme0ASWaYsrgU`z#y)M(q5%y>4D666j?f5(0PoCa1mY`%e6fi#aW9Itpg7i%FoZI$f`sa z-7rzs_P65#8S4T z+mkiBYHcc1`j)RU%5O;X!!O53qV!LkgJ&8YZe2>dup)a5@pm3B6nqOropLOC(k^_d z-aSp3J7q@8c1&5MX`y{Q+-!8NrQ(~C=Et5}qU!{rYbSNHKYOca2X?%m){6iDVe9y0 z)iSxSTaUr4aa$~LWuR(iI3#lclb8_VsdF0}kc-<5ICH_f0LV2D_tCQGS}Z#zcB`5)KCz^7z2H}J zsOIS*Jl-kKSRh@YjS(G5Yy2Ac`&a_WzCUFD(D>jw*d{}Kecp%PdLLqqxTg4LZOr8X zsVtmF?*587^kah;zDGHXuv_Oa&z%zZiDN5Z8(oM9Tj)a|d4a&iZQ0v{PMY-nWpwjF zvB^o{X+0}O8LpvXC`Et2ja~r?n*ux4{#UQzu?Y`jk6Ti>b)`yq=}dRNj!KVbu>=lt zjv93c&X>w?SNZn$%Evug|xyVXw{@ z>y~S!)s+l6UKd(PepAz#4t0xk4@IA=7Rfp|M|d|Kzh%nfGk+hF0o+}DN1VcByo_jceG9n8oY z#&O6C>Skw?mrGY>*=?&J>8~Fw0NXn0fl!$@_ySyVW4b9j1G)y%0ijIy`N!?0y~;LK zZ-2UZ37mDpbia;YIC$yNd=^NBo|g4&lLS!4mFEg&SfMyssRH(=$yrs1baa#r8HP18 zmbI$S93iAxFD_A16ExBV*pDcPp(!25i|+oAMQ~4c;lsqlEQ#WcO|GfW(Ws3BTX1YF zK7p8&@#s*!U``;W8P|0tD*Wb_`N+L&F(VsyXXA!p_#p;Oyp8vQ)R2dpo|=7Sm#BLY zeDeLLkJdQp%BSqoS)mAm)tJrr!P>mGq*{qC%^-CV^B7`_g*h|PDa9YXgezs>0EHb= zw!aS=j)6`4Tz3IBh&k5mW+bDSJkLV&@ZNJhP!LgH_o#!)VII*q(2!(K2!9(BmixHB zA^{J&Zpo}k{hBUfM&A(zJ6l*M`Qa`t1)5Wy<{YO{9T!5i?@_CfIaX^dt_gqSP?nEf znEEeYI`2g8xGkR}^mOQs*Vn?j+@F!KKRy6HLjMl{e?Wl0PN`;nOu)CpZ+J^QT0`QG zbWE@=VYfPwEAOAs0dI8^e4Oxqm;r|M{mMyRRMWJNO-!{Kd6UXt4_3(O->6p z61!Afq~X)0!?{( z-=e*JMAmnoO>~+>pSOo+{|~1}H_7v3b@uewue0R&u{sN%vV%T6AHINTcfa+p6BK~^ z?u1Md3l<%^KP3mMbm^JnKC?U#+q8qQ`=BjFBpXcD8`+s^C@&xWQN$l+z0A=pe{W}=;-95Q@|8w|nl?Qj1oW$C?Yhj-0&mhU( zQ51F_yaT*w_`I%fk%I#Ce_=Ol4+IYH#!b65*a|y*C%yB@a7Yf-*SNWT7k923_4{(8e$ZI;D&*Jij)5IKcgRvm`iICHhVNj$XFVsERX4vkKJ=Q| zTNauy>Df=NX*ll!4;hF50iHW`nOvVdzWo$F?+@R}Up@OHPAC+;o97+qyQ259U0!hc zx?7VT+-u=YJJ^kpIrR27Vt; zdM?O-z((D3p2(KLJB+sI?+WCRwr+hT)BAN0fDZW2;0bl#5io9F-vuD9AG`DOxV;s= zn}0t$A420__^y8Aox5*M8mp7A+?*Wa`RiYQ6uztNC1YjxZt7iqNmW+q0egQHzALJwTV!%~<01coMxN#x1O9JKM6*QS62H zUxs5_Mi^rsqMrs)gv(`cuf95n*0_4$z(I*PaH{33(^<4e9sSh^`q%>__42pqbZ+r` zDt-?mR_dcAjMUTrv8_iG2;I+cX|nL2-#H{-Qt0kRaV4SqILJ=NQ96nhzkL_KJiZ)6 zPiKA%YZRni{N@SX*V7q3dZ&9>Tch{&`}e>0?PV}%{ztBJ-+d17p%DGYl3g!JV7_Pf z9Q+Pe9enrhho0T@{Xui}p>x2vVU4LCf-(qs1P8h*#bkgw7S9R$_|0#>e=B$wqs$-d z;WK(I;v@7m>||P}Lg23c;JQ6_*^kr5U))K|`a5KvJv-=SRF2TixR{2RU36U65efJ+W_?i@bYi2PI0vGg>r*4+YG^Bel(Yn?U!kKd2@lnb(MzYCt> zgPrTokmEAKe=E94+IsZzyZs)Sfyg}^#NRejH%0GXZ{HC6euf-_-IiY;t=~OIP-GtW zE^)XRp5G&jg}ERig~5f}_(64RppB|byEx1yRi<4OcKgha$B$U%{EWOu)HkhvD1`Fi?PtGDxY z>{y4|?B%dzl#Kr~I-YTj&CU}Iiv5}VQ!%Nyse9Li>{cupMOvYMCU5u_ozT%jN3*-) z%~}|S9gVC)aStE~yD510qD>V&dsn-uqJJm;(3^0G+TH#C}I|9Q8j> zN&Z;t9^+&0$UhamO^?NE+t**vr^qc%&aenernc@M({lsTg3W&?)mZxMs$n>gNiUk8{#a35qc=<=wY^v4p5877Rf<_|Et!s-gY5d=cZbz-%`i=9ezO#B{sQ$P>JT{8Q0A+|<8&JtEj_JNUDE zbR8Ra(h;()O|0RR31m;4hTOvI7<**KkG874h*;Y_OOze9#+K7DO{#3jqD@E6YAX+L!2(da)_Lq{6> zbhKX_eVjf<>)bc)86R%__}B-JV|G~hULN=MV;oTw7^B}EcE}RBf1eFg^vT8%0PiGz zFHd;i1hKR5jufzGJn_f&|3uH9a28x*B(_g{9H*nbU1Q^P^JgRIQF(ZrKBnn~oias6 z>LiKQkm2mL3u(m8_SlI%N-weV8<%+XY)j-WzDJ)$1!X%wWO?5Z@pDz~4Cvj*-;86w zeS4#ff}%- z(lhPjV8_u9-^(Z03(RxylYQ)w$LmLLN&cu}=%~40U*GjfH5s?7!guo=Snj7|ye*`o z;>+402+;r8z(VI}^hp#A>039z8>r5nZ+}GJ+8(g03z8HEMB$J25&&OjUdnPJ zr=z_)9}b51{1ZAp_K3!_%Tx(#0`%Jh#vG$zAI|~E4ba~N?qg3c_9%V)5!AQOzdoJB z6q`{O_by4u5PT@HfRDCId~Z0;&WMv}h3l!n^)8RsJQ*j!ueY}7@D-R-w` zVEo~oP*)<=nvrmEG{==}VMATtB%-#ts@o+D3O^gm|%y=FA z{p7fYCFUsl$otwf@m2wnrVv|$?Nf5k@Fwz4RWJRng)m4Tdqw^j_w5`!9gNf0zHdJs zdu?Q3-cQ$b4<`rpIEI967sEQZqJr=noFdVEmp|jqb@!R29jo${_3RohRVgauaH$$( z{qxf?5`zMFQ_!yc#!S?%=GGyFC+YHWqEK8=n`wczXc+Y1quNLDjx=zpH z(Cqu?3$GmXDp<+5YcO(#z&!HaKA`{CJ@!otVcf|fys2!vNai}+0wHDx{}r9#_s!Y! z5I#uM_rE#&fqUq**>;q^aunO>GcwNs`ttQxUwmQhwkA;mwp%U52wt~_t?r(eBM zQZh&g#3%1cF6O&PHDpNoh{0ooogz&R$!8^v+G3GhKDvQ&JUb3)c>mk)--Ru&!~eW` zGW@5`-&xKZeTMw=>g3rtzNLIX&(##Cw^w<1(z(lxE6l`Kc<^nqJvcw?uW$brj`o-I z@Kaw6N1_UF70xpwOm|P6XUTBB(viAbZ+0uo(&o#>V!qLr>PB7c_vT`@TdBIS-s{F< zx!xJZYNqdJ`W(&IJ8iyMt@dl(T(|DZzm3mj{V6)?M&Gx(fQeRt!VZgwdXk}=IZnhqk zup_zDQ3|+ooSd)jm+E@4f}vTWxjJ8|nz@{>cC)2n?yLne=W`wAQZ;t_*=B|SusdtN z+^8_Kt-Dh!wL*=JfzW=VTFBIm<$44C*ll29HkP3;m#f8czS!(nGng9~r`-;)wSfs* zz`W|a+1#>PcgDo!Vs1f%1cu|3bx5-`O`mOabFZ)FGYc@XnlCNY)aUEvY_;6X4Pdr~ zIkSw7y5C!xzJ@-|H=Bh4W3ySHP8}-i$U@UMJ4;>7RvO@BX6!6$p>LMfa*uYFKHni= zd&^py2=KJlmW#b^f;0k7+Ue`Prte#K#629`W4WYT!VK(ZrarR}0CKj{H)x?*Gt*S} zz|yPP4sOtZ&yi(XNLSHHoz0di^nX6v?OXSxrtEauDUcSkH4MIKqWNCkYfEcqEYNzh z+M9qLYh}!LfReqc?dLFUXtUbv_nNUZH)~a2>5En8PC4#lS%2mTbI4|6|5+;)oKUJnX$C=y}mX#3)R?|AkTqWVY##Rdv&)2IkvL& z&K(N_)^#X@ReTR>1&9TGv4(yCuPv?p3Xr})NSndZYR%`?!qgC&FEC@k`nT4t`D(RT zuU4r0T!vk%LD>Qp{`JgU>~f; zYynFj2)x$oZ+(=X8Zm%4to~~dUl>QT%^IMuf_T#Apek6i)!JCh7RG9!&p-lOAi^wA zPjryLpdl<5tu@H}R_02Ssq?u863o~wX21XoQ0G8BSb{`QK}x8g%&v@$wFia(=<9G3 ztjB=26&A#>d@i-V9n0-rrbY~bW;@px02^J?5J)P})iyKJSTA9bg8zdBNC$NRI;Lu% z+U=1AEV%@svD1*Y>&}-a5A9KfdbhS^AWmS} zMvJ*_Sn3>`2s3rLn?b{-J5N>uYhkX;&2pyAHoG-IY&Az4SYZrMa)B8)2<_&JnK@g}QnbvdArS{qe45KApm^KFJP1<_4*48@< z763y>YZKJ=IX11<2;?c~!QH!538wv0(@hYTD{a4C?N-Z`3c5G2n7W4l197!BX0XzL zCIfoSd=1(l$O=<65ExNAZL{w#u%g78TI~i*5tuY=D}Y(In*&xrf??HR4@(a)9%wl$ zZ3e;}G-2rLVs2rp5_B}Q0#&&?rlP*pNK<_cN}siZDcZoGSR1fW?(LuGjpSXwF`&_u@B;6wXoJ}G~2*x zh;3hMr@>l21GO3K8w>Y$wO`Cv^X_VDTy&ul{5z0VGoT%Tf@*-01wTO{1e*#B(4_`? zEJ%nI$TKw4u~%WaU+n<&%iTtUg<)fK*HtUZL1eYojisrUwg)>y2kmA#(-Djxh}QLb zX;}b4ut(S09%RJ^#0l6Ldw`-o*Fmhzy9m^Bn-tcYIk5g(HPpEZj0hGXEI<}oELE`D zVX)L42sL0)m_}F&HR#aJuq++4uf5h@MMdO9zfu>5rK1JtRiH1S4L}Ik-PrHZ5;|kR zpPB}G-f{&-5SY)aHH^vH#9|g~FnD0SQ(O1M^IWgg!KCd#|Avv@f%>Gc7a)x0>)C!| zsGzm0Drlv!BClbbFl%X`uELZr=LT3iVBTAsb!(A{FPAR5`}JnM1HE^?T7nz`l+Kox zfi))h=6b#c%>{eCR-l9$pt5TwGQi;3uRxhuuVFen_2Xb45cW})j*Pj)x{(SiilJ@b zn{(hI1M5Y)zC^%GmS(KLdH@v|)RZM?PhdEMSTuL*9dvMKcInZD;tG`Q04vr;2P+1w z1$8q6T}fT4`vsPQptq>I#Tx00#Y_X+0ko%$1`^1?%kvzlnl1F!0LpeQDD|>{Hj8%q zInZy-VaQfQDuWfZG{Ay4*7|M<`X^d}_}QqSv+Q?^71(94B+fAF>7AgI^vQ8eqjV)% zfqibl+Gc=azQ?|e`3!{f3KTg|;9$};P*SutEVnaIl&qPxH-QyZuo=PB*~~Zndgc42 z${c(OX)s1}6NW~I`O;QcEzv<~SufWn(pT^w(2ZBH-Wt{%mSC_$_v<-Yf`Yg(T00H* zu6HO^D%T*ARRs30wVqohvKBx%pk)Uu$6Q;3Tt=#<&aEXFEfzv5HtJytHzurR`#zpQKpwAF+6q+b*$fPjoodYIV3ApxzE`o8y&*DL+XIAQ zK=zu|+MacylBT7w1vqXN=F&o-*kBja9#A|3VFR)x3$#T7!Ap@9U0f5zamZ1T)nq@BW(rs*vxv^R;Va5zlS`o0#0#q{W zPX)_*Z?%rkkR#_x`hk508rm$t)Y^k;xdvLnLdLG!nE@&d@Hb3?iI#gXg0MEGtrsf` z^mWjhmotD-+kRxX{84K93iAhuE6~-KOOO+r#TFWg!X)RliNbcY=${GM&PTzw{yVzTc^=h*NSnM{~QJ|>{1k9qNJq`lk1roF@ zFa*Fj*MZe$8$c9l%`_*&_i^>)QXLOWTrBcuI#%ew7~X&hfIU*6>4Dy@WBnJ@&IKrd z7Qh6|0R*ZssNhR53l?gJK?zFNzs*LoKzFlt))FnjQpDQZS_R__Ta-Icl~#*AFqJwp zHacjc3sAF`I<~^spgna6e#BAja`zY5J*$Bu21!&fSMRduF=nm7j`3|1T304tlI zK*J)mUxE|kZ zS}b6K<|Zh%yB!V^V)vtgOalfCtw3{FuJt`ynV>f;H-KQ6-=*2w(2lkSmHCfVune(x z5zVpt8c+ccnXi_!^&Tt_bz^Mypk&Qqx!i-@v{=D9fp&Ny1%(fg-de>i?3$yx0si-gSYp_JXGEuFi3X9Sl)D{4&ZsO$#tKhJ->ua#8cPfsNELuz9b6`7F&MF*> zv@{bWEYhw066gPbF%KV9fqB51-h-`%18`uAEjF-HYp`O#Bdg^e!HS9?_ z|5*F^VrGI>w$L|V^UQ&ptTlqkS*}b_YxclXCXVY_mbP5Lz%9+Cu>^x?59o#`8g?bl zEG$3inNlD?H30s=nMW2#+6CC6`fQI?7GPwsjMlJHE*7Bfn_!QCqNyXW;B-(07b^|) z%%%c3?vCj?t{47W+MR`IY6j@t)()0p9OMKY6ZCsvVFR0K2-Y7Ev%8f6*7bUgEp5{@ zmg_Z`n5v#ZD3^MR9GomcFBSd?_9&P(!WZ(HJjs94VWd+u0Zje?TjUe=GhF)q}|e3 zZLGDqpRYDJL<9Eqe1{ku^EE8@u#!R_O%Man8!&lWb|)+;LjHYg1c6Z=kX?dNw4bS< z^Z~4Z>({dlD5J2LFR?V%XDcvd_E?>tFXrY<-2=*&CeF{X&kje(^P85Rh-*#gwGJ=Ui* z6?>SMb7QwQtl4hXuY*T&VoCI$^6|4~Ft;$dIs%({4jMHG3w&cbmDieq_Oie>oCTWS3N#pCW}Kn} zqGYdu0ER^iE!J~w4%h@qy$2+$HY?EiI(03CTgb13(cJpm$qX{hVf5(}lpa%rk_q{C{x#A=zg*)4z-*E188v8B0~<9NbyGskL3TTa8y z*w75c>1IC?ZshWlb!)CtM zTTfKkS@e`sX%D*Q3KlGD14gz1LT3fS&oH#rY(Lj$u*Rt-!j55cwt*?s^!Z%d=%7q2 zmix_o)zsRHJ5xO6mU6T$)<`oD$TYCGu^ZA@V?ba_a|f#dEL;e0qyS^bfYohjEMS^| z`)A9==KpK&OPk!bv9-V7U%~3SRgSA-x43BQ%uKzGm$5T;;_;Hiw^XH~BuZjTi3&-z z-JbG)e{ld11W5n{D2Y~Q`h(jd0pehJ&VmErU;sP84Xe_07ZvB#N7G+t=FE`=g&hF> zC7OAq)h6S8$j-gxN- zFzLf{cyOd5{eZ@*=X>Bsfo&)boGUz-(uE7bBLusz_a`QXpH8CNIERXLo|JMJ=iU<7?^4@Wh{Db*$5dy z_Q>lmX5;w?2}oo;_QvkeAA_zNj$AaYLY3_yso1l@4xWwCq}dsaW@uj6A3CNgtr9ZI z7Y-kq?e-niRkPs$7IeK}1dHQiuOEzteb~Z?SP%_X;DYxD*l`GZO`Qd@+EITxhYg?D zdYlC_WXB>&0DiccJsZr>Ouaus)Ak;my>fhKJ_|g1gjk~IE@!a#iljQycd)JVu%T0U zGXp0;oCaGZK|DIhlpKLEVk2d{L^Jp~G8vH#1WT~X`ONlu4r0q8(lP^ZI|kYdhOvKq8?xp7(9ol{EKVr|&MtXikk(FS4tV>PORqF>qyXFmgQ`)zWnQ zR8e(#wKpCk)S~*h0c__Oz-qC-9Qh8M=NZk=Z{PNjrd~Kp&kvRh4@B(b>5Sn1a;?Q@&Dza@fDISl9s1kEDDVZaoJ__c;p$`WR z`=+20$JXHtZO=otBbwxnouNDSyaiH#NKzp&iS)}1w&pBgV=j_Vv(XsMLtF=$zp&6h z@1wrcKF*^$)6LBxv6+uX39<8F=)+_ZYy{|rrLt4-t4BY{mxA-9}i(u*RBYJYbC?=z!jGoiw%W4;HvwMbh=+#ch@3$dRqHsXI zJ`j@rVaM0CkJ`hC7CA$>c_OZLwz!6CIx1vJ8Ks4Ae(v*rG3=3-^%R!S;WW*H|4 zVq38&!W=5-D<(m4j-lyPlj<6#Lc#AG4ruS+sqA7=s)a94HnaNwu$PrNV2xD zJ6&Hj5jd!?B!%zdG&^D4(~sdUFW&NZ$bh`|1w(KsnG=n;s~iwcC8Hz@HlYf7kVOJI zh}IECsENQq=+Y|Oim!v@It)IYkjm$5??+Yy(UL`dmFGB=7TeQMC&DKkL<4#&@^9)C ziiCs8Ps1#*!mY(^ZL~J*4%p*k7}HCFU7Uz;d>k5C97i*1^puJ(5)LXKG`W?}kn2>a zqHjrp$`eF^M(H|!FBK&4{w@Ip{QejOPAIZ?B1E#_laBNzBO5DPbo^Yg#q!Y3?S<@2qQvUvM3UrA|G!!)HC}cpS zBOY9at35G;PC4{xyv#oNNnjBN@=0MTV27OLCs{a;f)h;hEEZ%z_ihY}0o7{w!ryGdcp7>QH&)=8mtELWqXc*J!@c<|}( ziKxI&(4Jyq!B#_$V|)A?(holGLNmpy!`s4(zhs;#>jLs#v${0F z2z|ykjd8$|LhsLgfTqh@+N^o(kF%}5xhIOQ*6#||(CQY;B1yElGAAXTjTT*Uk40-~ zm_+8w%FX?~?_?))lZ|rV7G_4~#B>_u#yU>3gGA@yRmj3kkY@g7r$4AI&7b*N-Oj^l zP#7zz%%XVKta&98+T6rSQ!TqnISMST+*&as?#s5A9OG#!vf3KvWw?K**tx5ml@+V* zWbhS}vAh{v&71bD%Y-T08zWIG)RQ~ab(96!^dRnZor9S(JLi)Wtb;W116#SIJz9VV zXDe2z8xd3*Cyt0FmxUzM%st)7Rw&v^Ls6Mu4qOH7$&ke>sanKj0Wd|D|3o5$5wje7z~?(U8V`B z0y&S{qZ^dKHzt3W$L>%K7=PM|G2c<^k>Qo?1t31E-O4WQJ+*y|FU^`>Bi3j#lUopb zU<+*LbmK=+rB6?P0vt=`WqgE*K>I7#rK$nTo+(6H6ln*7H&(9#8sC?iz@AFy#VxCL zhMj((RI1XCwN3A0y}E2Ak@wBl#dDTYULnf*_$SvZl^&^L8o%TEgy6s|)P=uVCBX^~ zvvWU7Q8>6mizx8GZDo;*Y9 zbGxt}W%0&>08;7whfo0ee&^3W`YTKmk$;TL8k#_x0Z;DL#qi;XW|`A0_~mC@jjIcM zj_KkD)R3$HgjYSNE_%z22m~Uz_~C1MHHH$%Mt?#SH4AUFxFoMHQ#jiieUSKKkG5B$KTq+;F3P2b4$k=XE9sjg{v()Y@ZkV;ezM>B5j>za z;DZA&(a#6Vc5M%?zpw}RjRS+VgBWPrc}TjZclDpIxSG55j&@uBq4UtAB{EE1`~W@p zwLK${FqFlMDZFUpiD?ll=A0PAsW67)@{B=G3R$?x5-#!3*uCBF;Jjd-9a-;I(Q+#Z zBFr^V3WAw{HPF*Ll4#5wi26EQARJu$FuSsU=onZ$Z<2h6LyZsYE=E;;_^%z7Y*Q=^ zdpLwt1x*k78gN4W^CMJNf*srIfl%CLNgSnkQ`mKw!pY#-K8zL!4)pw^<&!n+loeS~F_O`c%m3cOpj@F!d=Hs@>r*hp z1WqosQFDuk{X&85Fo8{)iCz;dTy%}?Vkz%i`6!t0h+Tn*hY_-pVFPvC(=EQp$=aZ4TlfGUS%;>H@NrZj zG{!P$RfwS5CRO72BmN`92TD3Wd{k#3fDcqr2|fmB6dpxU{E5UefQSiJe$Ed~G~#6C zZ{Z|cZd&E~F5H5Wi}zU+Zcnbw$WW?u*^rxjK>f(rwU};z#PK(;@Ya*HC35OPfo>KEH@WnI^UZCWmWaa6RslJ)@CnUL zEZTT{g6;Hz6{DDpg&f6heGWV?ntkgeaURZ$`>6mT3w_p+Nba>$_4a^Zk&opjQ~P~x zCR~mG24Wf^6VR6j)G9oNu@yuCK8cJ)__3Wdw|=fHy1q}0tV$@nGXhps#-Uz$l2=v& zDBB%yi1Ld#f7DI5tsOAEhPN4lnLj7~X*z`hA7dq7=BMCJL^2Y+nSX>FP+vIS3+Gh9 z>4*s2#CsB0>7f!`Mi>=KOb?LA-_3|+rqaylQ#Yeny7qJFTA+u;Mmd-(P9;V88zChK zKgcm8Mz?*6NfJoTClDCkN6H!1c}4wxqQw<77BAx!fnyRNj4fax5|)y!;2O(CBqIUr z3tC_oGo!_=_8-aa%`9@_v~!$pSLZ~iW1c}>6*D57ZasSa0)XN#78colfK@BK3>@j< zj{%9;hjE5yPbb2W%Nv6H7_e#1Ab($6vlV340=FP4aotxR@u=%N?tk&UU6`etWBedH8&tGLqABL5Fe5+kd~b7)FF# z<)XZAQ4Yi{xUE?9QqL=VeF90`r0kbO2z-^uY{wCBK*2o%Hn;@{#aH#>OM1mP#JkGQ z5PE`zqL-m`5pe+4aIg%wSWKrGg&Yo~`_Oui3<$7CtzXD}yWBDXiBtpzD|~iLaV`R* zQwL+11Bu=REQco+rsX@A54S5i1YuEk)%rF|KYuUNm_SmsxDETt1iOB~62+hL8;5jf zNP4T_f4v27)eYZbERn>so5@#28ybtBaZM#4az=%4Lrn^ja9)ycXQM!&?%b8@w?#Tt zZ(|pOiKd=Cnox)TlB*ISoJLLoC#L?sw@?k{A7N`4nMC`GFt%p#R^&ZM07zE zH^@;!;yHoPVY>8Ip%6M2|B>L2MSAJVP}9VpV8_Ngymlv^74YB-H@hg03wVz5Y)qCR zzx@<|bY<{)7x`PC@o_Joj6t`3m)?iT6)a>uKx`dYfAg(%5-k6ILHa+vB+g8FWeEXa zdhriZ0$5#tyY&BY(GtX*yFq2#6&cRox&w!D;T3u(xAEMu)W+kyO6@H?@m$r;=c>57 zv5)G8IDx*Nt5J9)i8K`xi2Fp}u?%n;y&SG9`4KkHkdU;DU|K zxoh7jz<;C|iaPYkwZspWQIX6A=L`yE>qCU!fYY;Q1e$)~ua1J-0Gpz=Q}SM|^vc=J zU-gX%s#K1RzRRsFujBY*K0Cl_%fg@M%5cBMQw%=>6?Ht+{S`eN5c-<_Cr4A(rB7qn zvs+FC1{g(|)UDyUd4`nVn7u<5s!xMm1EdyVip<_)0!Qk*pdGI=_iMWZMwNr3`G!KW z6C%Cn8ka{vVVvwYB7_^x>nC&u@|7-1uxG z>DQC*FCar?bVP2+_lL#;?l`BCuiSDcmwB9=_nPfGh<5mo1kaFk8%(|DsGhQE0_w3> z{uXZSCHq{6)Pzl~@fJ?GSh)Qs7NTgVFqzxB=LKI(N&ZTpl3!hUc6(mDy6}6kQLN5; zobV&Jb#DA^xZBfPTHuD+&OC7*!IENzfUrSEMJnywY6OQ(un2uJmJf)eJ6IR%yXz!d zX3o-sM?g=eH2Am1oTirV{Hy`dVPwn^s%Z`EBPdO4EbOBOjqa1J1N(OG7T1Yso_JFCizg$)y1rej-gy~_bQT` z#GLS>%e>~da1urFOZU9s2j#ApBFFjT83vUATD2_VInrej(pm(}n;^N{`=SFU2vLjw zNbpl!gnnMT_^QxBKVPA$%CxWY+%vnE79fQ0>fZX-;VQpX5cSRJXE&IPe8%0Y&$u_V zxwlMy!8UjNN4B+!+-=vL6H*g!)oBp=`7^4j3~)vKh};8-kTyf`+Rrv&M$gtUywS7K zFtJ7{F}cC?R7U`u(yppvBywNVn7%dgRtj7~c+Z7_OK3#t3SvwYpt-FvCU6Ug(#Dqz zk&Dz-n!B3q{MENZc_P=!r1I+Eb*@mC=zr4v(m{U9-&w&* zu>1ZORMi(xWU^{hkCzk0D-tS`9dAcz;I1JhJybzNYAKp|RcPPe)|D2DAR@TPgGvu_ zInEDJH9=+Pkg^v0&F0415s$KLBLr;mqxtik0JR10Yd)?-Qm)1fa&Vo%k=&t(LcM|J zY5D&$T#?ix&tw(h5DqxhL8>ZKb8rBXOihVv_z51B>h1A`W0;0Oq`h;zDeSG+Z96|( z<3AF}K+@T577+N^UZZ+Sz~Ts0T7tX6djIBS7m-zQ|H}PnUM(6wT-kz0rE`oO0a5E6 z{FC8n-G1*$XGRiqkLEB%Nw?u{7mz?KGi1C&-+}Rc zYOsy)px#iJ<<5Lop!sjJM4FK+n2%L!PY!CF08lcx>sBy?on<$58fLBsBX zcvplvgnts)p_H>T;;Kp)^~{?m&m?HG!7uT2n}moSm7Dnz77lf?2{Io? z+yQq*kkP##{%#k+QSx^Bc2F-C7;?cO# z9uGce_{yX_>>i7WN0Q}ww|ZQN4W#+iay>>aHy`_j`B(jKK{B0VOduVTpvmY5FOc zk-a=~h^C97LE}J=^0*rG!U$G^`vV_!!~eto%#u8+6G+m`&zLzIUiGPwLeuh*W9fs^ zw{ss2B1;?oK(>g{w0N_Lw=8i)TSOA`$iUN5tH(c(=wT!e33xP}C;P<|&S{nN+W3r= zTgm+AK1gngJa(zJL6Wd*tWcs;>LJdVhSJ+H zUG78v&?8D5mP+MrNJVL*QrgB3qatlwPAfLCBcSw314G!iNVVy_hotWOG=(`=kzVd6 zZdnhdxutL6R1bnnF#wKNqD)x6NbQ#poCbJ{X^}iAVKKv#LnL~O48u}K;sE1C;;2+O z;_-R0l^P|FOEuf?KvoybQ|__Pn<&}Mi&V^=gW_8>zKbCLzsLq1=X$bi-^CLs^Za4 zkA5eM*idH#ocTv9KkOskz|*Ibb^urLHImfP-BHR`z-ofj?Eur5V(Fb+LP?HZ5D>AM z5AK;e5oL%ZJX=yhsmO5tSc3SsKSF(3{92jL>kAQ8{`L+`reGpdWEGmY9Onn8>KY#a z=P|CZGM2J9XaN221V9ojwmFiEtoQ+;D{df6wFZzZz+vR1ziCzpXNv3((mryiD*L*mK~L1=#}!EKj2^$gk8F6ZblaOb>78xX%8#n z*C+RvPGLC=^~n^;u$`ZUGa4pHFP99II^Ui@SmBC6dd`Vn_DY*#6-`k_dy|AHA}Azr zG(|nd)`IRN=24&>ZuP)j%Vz}d+Sj)L;A0uU`NrHq{G1@3uS~+~rwXLCeCH2G@Pt3b z)wXc8l{X)X4TKzc0ar|iXPaUFWv%0nl5DZgpv!syjzcMjU#g1gaSxKhFgFnoy$NZ8j#$U0~h3c46{2h zwE`3D6INk~rQZT5p$>y-9u7gTKK(8>(eZa|u6Jjcw?Gq4P~xe`aSITIp!2FYO(~o3 ziBDtFpLDt44pHq8MFZ)>cKy^=hUutfJTH7*6!@B8fK70pkI~Z`uIiSJ4XO#n+Jpxi zemUx)m&7*D)_!LBInct?+Qx8kD{4v<1q4X4vQXq9gp*rEP zI-y{ll+Zfi;W`1lP6$XR1hMQ1V|K!Wc2b~rLScJxMA$mIuyt-RY@G(!jz>nYh2_wA z8vuOCAmgxeO-QCd7GtA0%n4Pgi;GEdckCp;Ja$4O8{HztI0Nw0BZ4Z}^I6Ud@UrRd z+n~5wLuLqwS6XRXEfNIm&JJjYDY7Youa^>ElI-V?HCVWMOKe7N1=M`KAzvs>D`kbk z$Z*F5pK{$Fjv-I+H8tB+b-DkxuI^K?HN)|CAP_M2wwSe;`SwmZ-4QQHjIJ)~1Mt^e zlE@EVtDsiObN*n1$N}$Yl{FS7Jk-Pa%0#{pftMx70tHx-_`-&8YhnC+OHl_=4QR`5 zwRpo38&L2XMMLpc?fgsJHE@}7oGkFx zNOEyF>f>+$w<*|Gx(nvvl3u@XC)651hmZhA(dPV5nV*YgxCLN?Ngk`OOf90}dl|3N z{BG`|ff#b^mZX!%#DxH)Meh?uE{clVtA@45vsG^7F1p6#5fFHjM)8XNC-<&yW-TKv$1OxC^5j@KImZh=G<|>;wlwEUqJslT(HC9+WQZ zAY*{92)V9Y*wJUnI9x7G!Xa%3Ea}gEun+Py9MaZfX_?Oo#KCOYqQFBkgJG%t0@wJv z9M^E05!n*3$TfG(eN|9hO}8x;BsdA~9^Bo6`^MefHFyYa3Blbqx{xcx9+L?d|y_t8a2llJ-cf?te(AkX*EhvBh+5wy`5fW84chrrzOeYOV*j2 z&on!2_nUw89$8|Y-6`C>AYw;OxW_Nwq(wlXwZCd@FiN6?n8K$$j{fGKE5a zeTl6@@w7)`OccEwNt*~^cFo@*2VuC=B;HM#O}-})#^l<&C5&w_89c`z zs=$G?+&~BPkL#C5I5if?tTeu*)Pe_iBGxIuGrV#C>{yF8xk`{J)uYmy`yr=unU(;9 zL9c))$T3luq>}1oWobJnAoC6ln-{@W-cW2g=Y~(l@Vu4KdM~niQRHX~&dl(a)w?Rn zR<*CeC~K9#z;8`Oc{;y-Ee!n8JH`&+4kML7Ywsl;*u*|3*J~NbspB z^^Lc?9og4o}DlT|8_4dvtSoMoo3=$xFY7kX7p z>DPE)+=riddLvWT64xI!oKZvmRED`%ULvvZ3zCWWS@O7O#b-Aa8Q}8%@>R|uMSa~# ztO_e%a4ZfkSEyVHE+-FsI>*pk!_d3DH-y(qF>Hd@iz657keqFj)aC*^;}_KP9+BcI zzwRsn zQVbKfE?X=!!QbAkp2+NGp8k6&VUQ8V`1g065lnZgwFr$wOX;RPre}+Xo4ggqR*F^J zJw_%Sr-cwmqf9bmJ3K1u0C+3Z$kS6%iXKdT;x&)mVstkc^6Vp*1lAp&>O7F>Tsg9H z4)IllF#Qyk(hU^9zAIrjFxZx1Em}EmS+H6UtzvHQl0OUDu*;ubUnDjmDXy8&9b2Eg zB3ZvvZ~6eZFfw_%U|l(y>A4#V8DAh%mA>&cvjiRKeJ57(^3Ff(0-2xjQ$T;*9egHC zGg!?<)A@%?q#H5f$@p{F(s2M6Dvp=0NT+<{s@7>R%)sm)zlTY26F(Las@C)kC*T!^ z-+Vt_*DjyerSKm$18QZSHJd-(zEUj1d=9@IcJtv%*Qj43R#?U07O-yqlh@7J-lRol6(CewNQR|-z~2t5F}Rh6;|K2T6zfnIW9AHx*tOu?tTqZ{sG%E zqVg*b*oEO4n@_QrTGlnphVV`9Mo8A_d8%b^sZa4o=$YUQA#}uix}M+Q;hR=P=X@Aw zR;f_y$cmy^vEzC_d)ZMhi>r|>`H{^_BDrv2-)yK}nK38=u)U$51q|$AuLx(%<$G)u zHDjb4(MEO=Fi~asBZJP!yG4RKDU>3sC`QQBO?$<1r*f$YJ|N>0@5NQgYt9&n!tndz z+31bjkZv*W=s1M6B3I+H&s?@Wy)D91W4qttI&qWRdE_lriHoM+&*wHoD5WnRW|v^>ei0SlzOR<)xn+`S0XMU!iE1 zZkBRa<2qC7Wm(_J=jCEnL)d#zb-RSDirv2Y{Cd=&q0tqFPC9%uB8mshR-=Saat7j@ zeKy2}-C|SAu(!Nb7rG8bQpCuSpZ=Zx=AG^5tKd#6gY60WB_C|18VytYO`gf+2zS2` z7zijdWAlpXK(43`-m?W1PK3F|KY9#N8_Oj~4{(>%=tv9@x2H#LEN$Yd`M@$selkE~ zemi4|tw^@ZD#v&LsM419D6x}L1~8cw^BzH5H-wf~Mw7BP*L^HbkdctO}V zU+TnxJZZZdhLgnQG`JiOKR3h1=BOTZ$DF+T(_zyLy|ytNV*33{J{WmX$PiJYfzR6t zh*f^hZwX zHUAiLxtIP5&7b6=pGK^`f8r9Jswf?)n;P?=-lI@lbx>*9YtUV( ztHfpQ8JcYAuiMa%d%|N?ytbZ5a3_)g5TF^fkD??NjeiFY?e0(i=MC>HM+r zn+bQFI;68;mV{UDks(W2L8C`r5A!{~=2=}2%8X~y(37hK?pBeoq~-fEcGK~=790Pc z2tcCR{=FSpP94j*@o%!rQqCQ^ZT`l-2WE^Du_D6b|ka{v*&9JqD(=OzM1Ekj_Us~ z4?BczXfezvADlS~JDfu)F{SrJ7uwML11((9XD8+iH2f)2txJ*^2k=V^mDw)JSK8BO zSLUc%8JLaLHaazzTgoXQ5< zwfM`F1QmsU7UyX3QVjVQOz$Rr6J$$y(z?aL7nHa{qYdN!<i5OY z*?OsyRHwo&2#YDD7i*7gu}hq@8F^#U*HYwL+iCzatHR`n%7VuPi?Be0TDesTq_1C$ zz;%zm{ZsafjuO>MLdeRC{^j7(u6RY{8RAdb2$?(W(#0@=SABhqQwSS?Q17=AsE@e0 zO~2?U6Ip>F`^|03X^jkxTdwQiL4b^)G?@Y@E7ZaJ;Q%1^tg}yFWsmV~C%+PmGhZxa zvT{%z>!Hgk5za=;qYFIuVyZsurmQBc>blS#nogruHHmswR~#V8n>OXuDdlOofkR}5 zs6^(-l)wL6^5-OZC8t)XvK9j93k$gf%z0KEg^d67&waDD^12cDV#AS@IH0Q0=Cswi z88PJAiY&VJxVouDC3W`8JhvTT29FhdsxZX7oY*sRcO8%w0de}+;d8tDtdR=WBC{}ku@xhFkCR{%XWXHs4hy%DwoHcH#CJP1Z6{ony4|qo zlMD6>3(XV#BX{UhOz}lZ3mN~7*`mrHfhyk?5%c#>J?f(B)ZEe1c!RSK1k1?a@WP+0 zMLh7yNvl5vzUgnQC^lRucy57v7{}tHen=|EwLaorlPvR9%8K;+lAh37;il(oGK4I;^Rh@|RtZ4-{6j~*2 zUV(jGAdSLqg{Ny0T6b@S=Y=+(O5jDkfBfDeJ!_eQ%@z8enpP;d1e@DxMDXW7cmG#2 z2b=pD3jVjoNa+u%5hnt+WYZmC$D+z*0{S=M&4^XS@A5^>&Ldvwl_ z*}uvChuQKk^X@Nm5&1Z1TzOf$!p_62`u`QWG6glM?d|26&6L6Cx`ZbEudn|Montbw&#RgQ{ zG$f{0X9|y@YF}oejPG|1xg&D{>Y5a98zGJ{?{Wgx;vd=zvYF4D?6&bK7Y<2#!i%+L zIZ#E9q;Hta??$^*)U;CFYR~JoZr|&4X1~W@*FnX0l0-R4blsxo!)jKEdZsGj>|OH| zzO{KoPN4zUQmm8R!a~ZRX9|yz-O0X4i);e9IjJmOXt2a{*mzDK3tXO+$x`CCX3N4H z6r@o^Q~b3@7pOug$a_`sq^k$0ztN<}@jFPVRp5~C2EjOYb?T*kwff@O5)@-yHA|o= zHr%)g(xN~R3B2uJ`+R$J-IeRgw%)pn!_OO$zPTT2%3)-aRXUBQ-5EhAps(!Ci0l46 ztBCn$p1kv?B*O~2mV&nnbjs?froU;bTI7M+No@+@hFfZXfgSG*%O;? zbP{W?5lCsKa~u?0bEScAHpz^h;Cw{*&T9sbATv}|FHF9UyZ1+hjq1GCXH&~nW-DyO z(QXHS<d{5;flEo+aQs(X=JwZ6*@xE#Z9UQbg`9U+yY?2)v`*eSS5HKTGJ-T;|Zk4VS z>REi_ml~mQ$vfYNHme#T-^1P8R(McfO7zwXwJH1uT2h2vdRr{8VM9~-&C;^(#6KZY z?S(cj{8|>lnnQ9Qc$5DMo1O0KkpN~4t4F_ZL4~=MB$tK!cq27<2plh40woFQ(5KR0 zg#5Ij2%#Kk#7D^};%l_3?DEk9DPYF7den&cA6Czk=M^(%i8W99BOlQayIp#0q5k-V ztAn=Wnl9tNPHv}%dSuR{_UyMS-2YhrZvg3EATkKjVyj+96tO!@cgU$kR7`ndBr@Dn zp@+U(85_ZgMl?Ie9`awv7!>*MA=IDBN?Ch~%mg=$|1l12Auq{?4#Ppz=f4{I=&>yh zd4B=BW<qRrK*I^j0(%zFBxFtq!6nzQU7BG4enU)p%#KeVXND&rSt z_K(bt7G?AQj(B^CjjvC`3f}Hm_J2AfGOA3h1^H=L<0NVHcTx=Dfxw*&tqR0`|4W&F zMicAyzmVmp>0e>6cBsY4rR|3v%bu9n<~eA0GH2E^6s0|(Q(&>VYTAD*h=wzt z{rS^(r?ryhnD)ygQ-|v7+w|?3R$CBHAxcBY^jyf?ZPDAs$)a(cgcO%RUTfh^Vj2!=wCBVrv!$)5e+yM z)(9OjK71*uQxXx}^cIBlhq$?Vv|Us2AMy9JQwNmP(J&AoU(!X|+1+c@LUXTbtLhho_ak+^QZY3ZWB^Lj~XlM^`uV(V`5SmJn(aUkYH; zal(a5Vb9f(L#(wJI4otx2)3D85L4d|Abx4V?48O)3}B$0d&`@tlsu+7M2BNYH$fxe z`nyWI=V3xD08wK)Gq+^8SKyLse#W}}Ql~5ru71#0e{||h6rEYFv36dc_B&Dz6mb-@$&`t40*9o1q#=FYw!xaW{ZK~i*YU(&`#E6ZS zi+92&dzH_A9e8>N^v)oNEeQU)+Ef2P$!SYFRiUbWlGhHjtZQCz^}fO*pDY%@v{GiV zJfwb*qAf^Qk({+#p7KSSNu)FqGNs^hNYqrT`9K+!qB5e(g!E1OCZ7xlV0zRTZ%zSC zVv3h9E7??#FzO2g`CWPH3#8w~80axRWAzTv+m)wKOEX2P#sveIBBz4gm=rv6#G(aZ|;ePzwA%Qa?S)m1Jxk3^Zpz4T8&;#Z^k%<`^f#{Yh$9USR8QtP|ArX|85N66O z0w(>&_?YjMpBzdhHn6am+NPu(wD1yQV(<)x>1tQXoSbpBK9Im!{$e;UXkZ*3=roxJAp$96@1Co+D_sE~d#07F&8!eWn<4z#*cV+;3m+=~II0l+uY2e z1<`5JeUl+4mC?oVzU+%v!>!J7YnK;BnfRsOEWkko)q9KEWkmBk`CGBssJ4SY$^gi> zxZ)6oZ&!+yY~CO~J3L1)XRt-)A&yQ0jsE=5!MWDf;`p2@bE#KK3S+F=Z+^BA#VodW zoxuJj=6UP1MlRO~__$?wlW+p_8EG!HJp(LHS}9W63AHsZ42r`qs0Ck8)O6%M{$L;> zVUhZy7D%F~sYp*B3mBim6coCMdoWWj>fkgS1HEC?yijzSG_5%8@`?q?NT|phGgJjx zv@$e_7|Uu=(GAp7zJjLIQwBi^iHy-9Yl&bb_MB9(5@$?`X8X%J7Nyy+)RbM>kfjEz zD2?SK@KaQ}t7Z2=uO$dCY0}RvL0fFVGI3FoMEM&w+A*x973YvP8;;G@%`Z&OD1)g9z&6?af=NfcT zJ}Btm0z@x@Q=StRS{5aiqs?4&6DF}$@|j*joursp)US4#*LQ8?i^1@V=!Gag3yC>( zPIY<7ASJHdnN4T5P2Kix50_+WUDbJ>DaX0OCq6mmW=oS6lN#?1nHcyNSWHmp?Ox`= zu0_84x~Wwv8WhMM!!1ee0gli`m));(edqtR>6qhCp%EU%^*pAD%&eWq#d4qriF-LB zl6~=Khzg#;_laETbEQd{iacyfK4YNvg5_Mbi(+2`x42H?HiJGRQfH->8tzQj{;c_o zbK@5~zAVm<*|FuU-8~8E!M@tO+~5IM3AkHE+G*-v=dH6{hg90cO*S#Q<_*vGcMY~x z`m6MYScr~bEc<(1wP)oK#bSlwIR8t<`1K8`aQ^$m0e$rV)`ubO4`rCR@81to(*AVL z!Xy0ElCKxXFxZgr+1j+dzbWmr8ZQNy?Oy!p{&_eJB)Wd|v&QfRtm<%23_#hbQUC|5 zeK0uMS>5ckmN+Au^OTlHH_{c_u9mwI`T?7MH1PW6u~P-FaHmtO?O5dXV!wp#^kx3# z;&HcRw9_l@Aueanr81X}yw3#q#Z@#$Y`n7nBhIsd}_a?y1&k`&HfYHH&}di8FkL#M`UVedvme^cNq-0e{1+GEG>*TkZ~!W8vw4RDpPw9FFLJl& zJ3Bg1rq0r<+hQ(-XFah1^AwCb*{bE%7WUVuSTu-o$bvhLN3x<+g?0?E%S{I zUV!00YDL~003g(X?d6#jtq-zC-j+7Jy^^#>-VwLftd?BlYe8e3l(8&Qc-a&H1T>&vpp_3g&>{>9+Q z4CAu!%Jb~w`P6#PMUx5e#~isfUs?~JN}z#=U*Z!(J@o@=%)7tm=WuXd~5gA4Go>0$?D)xaT(MndrJ}vIQ*Ez-WE{ zl^}!~@?KF8;nH}WXmv3u!9sU`JnySQ6=GTFDgxgI%eggO>rE1tcRXSwY5k4I-65v) zkHn5{RK0>pPIo7+I*!|GA*%!F6y$h9pXEIIzLz~HRRR^7o_AClWVejkxq=-A`B?0idj5IjPJ(}p+ic@dq>WTe)f3O<|(muxPnoj;wY$a>y8`h_T zAIB?>GX{N*-2}hX8@-W(Ihg1IWHW3AIpoou`aSw)e}DQ_1=)MdmAEaLV-r1H$wd-t zS8Bf=AD`sgQYlw=O z+~Ie1cx~0VV01DoWF#?d0`wc;ffIjj>I-0d%@93>-bMK8*V{da4E23=%5=n>~uO-ygB&V`Lq2^Hi~Fe z6aG6)Ve?E3%b(Y-t}{ld>pQ=;GA$CvD5@fQ7{8(t>{?;Cb3SKtZhMg~8n8M~UasPO~8JS@nSWHRZxOX0qMN5@`XlT?H2$?Bs?lK>Q2y;r(V1r+Lp4B1R zNG!Mv$sd4^6M}$i1WH7}s%_0?mN?o)oIdE-TlYHH95^=jvyBx+;CzQLIqFZpt1lON z9+xyul(@!Zg 'Bare upgrade test', + 'description' => 'Bare upgrade test.', + 'group' => 'Upgrade path', + ); + } + + public function setUp() { + // Path to the database dump. + $this->databaseDumpFiles = array( + drupal_get_path('module', 'simpletest') . '/tests/upgrade/drupal-7.bare.database.php.gz', + ); + parent::setUp(); + } + + /** + * Test a successful upgrade. + */ + public function testBareUpgrade() { + $this->assertTrue($this->performUpgrade(), t('The upgrade was completed successfully.')); + } +} diff --git a/modules/simpletest/tests/upgrade/upgrade_filled.test b/modules/simpletest/tests/upgrade/upgrade_filled.test new file mode 100644 index 000000000000..9b17bda4caa4 --- /dev/null +++ b/modules/simpletest/tests/upgrade/upgrade_filled.test @@ -0,0 +1,31 @@ + 'Filled upgrade test', + 'description' => 'Filled upgrade test.', + 'group' => 'Upgrade path', + ); + } + + public function setUp() { + // Path to the database dump. + $this->databaseDumpFiles = array( + drupal_get_path('module', 'simpletest') . '/tests/upgrade/drupal-7.filled.database.php.gz', + ); + parent::setUp(); + } + + /** + * Test a successful upgrade. + */ + public function testFilledUpgrade() { + $this->assertTrue($this->performUpgrade(), t('The upgrade was completed successfully.')); + } +} diff --git a/scripts/dump-database-d7.sh b/scripts/dump-database-d7.sh new file mode 100644 index 000000000000..f78f998e50c5 --- /dev/null +++ b/scripts/dump-database-d7.sh @@ -0,0 +1,90 @@ +#!/usr/bin/env php + $data) { + // Remove descriptions to save time and code. + unset($data['description']); + foreach ($data['fields'] as &$field) { + unset($field['description']); + } + + // Dump the table structure. + $output .= "db_create_table('" . $table . "', " . drupal_var_export($data) . ");\n"; + + // Don't output values for those tables. + if (substr($table, 0, 5) == 'cache' || $table == 'sessions' || $table == 'watchdog') { + $output .= "\n"; + continue; + } + + // Prepare the export of values. + $result = db_query('SELECT * FROM {'. $table .'}', array(), array('fetch' => PDO::FETCH_ASSOC)); + $insert = ''; + foreach ($result as $record) { + $insert .= '->values('. drupal_var_export($record) .")\n"; + } + + // Dump the values if there are some. + if ($insert) { + $output .= "db_insert('". $table . "')->fields(". drupal_var_export(array_keys($data['fields'])) .")\n"; + $output .= $insert; + $output .= "->execute();\n"; + } + + $output .= "\n"; +} + +print $output; diff --git a/scripts/generate-d7-content.sh b/scripts/generate-d7-content.sh new file mode 100644 index 000000000000..07b715e922b4 --- /dev/null +++ b/scripts/generate-d7-content.sh @@ -0,0 +1,308 @@ +#!/usr/bin/env php +fields(array('uid', 'name', 'pass', 'mail', 'status', 'created', 'access')); +for ($i = 0; $i < 6; $i++) { + $name = "test user $i"; + $pass = md5("test PassW0rd $i !(.)"); + $mail = "test$i@example.com"; + $now = mktime(0, 0, 0, 1, $i + 1, 2010); + $query->values(array(db_next_id(), $name, user_hash_password($pass), $mail, 1, $now, $now)); +} +$query->execute(); + +// Create vocabularies and terms + +$terms = array(); + +// All possible combinations of these vocabulary properties. +$hierarchy = array(0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2); +$multiple = array(0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1); +$required = array(0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1); + +$voc_id = 0; +$term_id = 0; +for ($i = 0; $i < 24; $i++) { + $vocabulary = new stdClass; + ++$voc_id; + $vocabulary->name = "vocabulary $voc_id (i=$i)"; + $vocabulary->machine_name = 'vocabulary_' . $voc_id . '_' . $i; + $vocabulary->description = "description of ". $vocabulary->name; + $vocabulary->multiple = $multiple[$i % 12]; + $vocabulary->required = $required[$i % 12]; + $vocabulary->relations = 1; + $vocabulary->hierarchy = $hierarchy[$i % 12]; + $vocabulary->weight = $i; + taxonomy_vocabulary_save($vocabulary); + $field = array( + 'field_name' => 'taxonomy_'. $vocabulary->machine_name, + 'module' => 'taxonomy', + 'type' => 'taxonomy_term_reference', + 'cardinality' => $vocabulary->multiple || $vocabulary->tags ? FIELD_CARDINALITY_UNLIMITED : 1, + 'settings' => array( + 'required' => $vocabulary->required ? TRUE : FALSE, + 'allowed_values' => array( + array( + 'vocabulary' => $vocabulary->machine_name, + 'parent' => 0, + ), + ), + ), + ); + field_create_field($field); + $node_types = $i > 11 ? array('page') : array_keys(node_type_get_types()); + foreach ($node_types as $bundle) { + $instance = array( + 'label' => $vocabulary->name, + 'field_name' => $field['field_name'], + 'bundle' => $bundle, + 'entity_type' => 'node', + 'settings' => array(), + 'description' => $vocabulary->help, + 'required' => $vocabulary->required, + 'widget' => array(), + 'display' => array( + 'default' => array( + 'type' => 'taxonomy_term_reference_link', + 'weight' => 10, + ), + 'teaser' => array( + 'type' => 'taxonomy_term_reference_link', + 'weight' => 10, + ), + ), + ); + if ($vocabulary->tags) { + $instance['widget'] = array( + 'type' => 'taxonomy_autocomplete', + 'module' => 'taxonomy', + 'settings' => array( + 'size' => 60, + 'autocomplete_path' => 'taxonomy/autocomplete', + ), + ); + } + else { + $instance['widget'] = array( + 'type' => 'select', + 'module' => 'options', + 'settings' => array(), + ); + } + field_create_instance($instance); + } + $parents = array(); + // Vocabularies without hierarcy get one term, single parent vocabularies get + // one parent and one child term. Multiple parent vocabularies get three + // terms: t0, t1, t2 where t0 is a parent of both t1 and t2. + for ($j = 0; $j < $vocabulary->hierarchy + 1; $j++) { + $term = new stdClass; + $term->vocabulary_machine_name = $vocabulary->machine_name; + // For multiple parent vocabularies, omit the t0-t1 relation, otherwise + // every parent in the vocabulary is a parent. + $term->parent = $vocabulary->hierarchy == 2 && i == 1 ? array() : $parents; + ++$term_id; + $term->name = "term $term_id of vocabulary $voc_id (j=$j)"; + $term->description = 'description of ' . $term->name; + $term->format = 'filtered_html'; + $term->weight = $i * 3 + $j; + taxonomy_term_save($term); + $terms[] = $term->tid; + $term_vocabs[$term->tid] = 'taxonomy_' . $vocabulary->machine_name; + $parents[] = $term->tid; + } +} +$node_id = 0; +$revision_id = 0; +module_load_include('inc', 'node', 'node.pages'); +for ($i = 0; $i < 24; $i++) { + $uid = intval($i / 8) + 3; + $user = user_load($uid); + $node = new stdClass(); + $node->uid = $uid; + $node->type = $i < 12 ? 'page' : 'story'; + $node->sticky = 0; + ++$node_id; + ++$revision_id; + $node->title = "node title $node_id rev $revision_id (i=$i)"; + $node->language = LANGUAGE_NONE; + $body_text = str_repeat("node body ($node->type) - $i", 100); + $node->body[$node->language][0]['value'] = $body_text; + $node->body[$node->language][0]['summary'] = text_summary($body_text); + $node->body[$node->language][0]['format'] = 'filtered_html'; + $node->status = intval($i / 4) % 2; + $node->revision = $i < 12; + $node->promote = $i % 2; + $node->created = $now + $i * 86400; + $node->log = "added $i node"; + // Make every term association different a little. For nodes with revisions, + // make the initial revision have a different set of terms than the + // newest revision. + $items = array(); + if ($node->revision) { + $node_terms = array($terms[$i], $terms[47-$i]); + } + else { + $node_terms = $terms; + unset($node_terms[$i], $node_terms[47 - $i]); + } + foreach ($node_terms as $tid) { + $field_name = $term_vocabs[$tid]; + $node->{$field_name}[LANGUAGE_NONE][] = array('tid' => $tid); + } + $node->path = array('alias' => "content/$node->created"); + node_save($node); + if ($node->revision) { + $user = user_load($uid + 3); + ++$revision_id; + $node->title .= " rev2 $revision_id"; + $body_text = str_repeat("node revision body ($node->type) - $i", 100); + $node->body[$node->language][0]['value'] = $body_text; + $node->body[$node->language][0]['summary'] = text_summary($body_text); + $node->body[$node->language][0]['format'] = 'filtered_html'; + $node->log = "added $i revision"; + $node_terms = $terms; + unset($node_terms[$i], $node_terms[47 - $i]); + foreach ($node_terms as $tid) { + $field_name = $term_vocabs[$tid]; + $node->{$field_name}[LANGUAGE_NONE][] = array('tid' => $tid); + } + node_save($node); + } +} + +// Create poll content +for ($i = 0; $i < 12; $i++) { + $uid = intval($i / 4) + 3; + $user = user_load($uid); + $node = new stdClass(); + $node->uid = $uid; + $node->type = 'poll'; + $node->sticky = 0; + $node->title = "poll title $i"; + $node->language = LANGUAGE_NONE; + $node->status = intval($i / 2) % 2; + $node->revision = 1; + $node->promote = $i % 2; + $node->created = REQUEST_TIME + $i * 43200; + $node->runtime = 0; + $node->active = 1; + $node->log = "added $i poll"; + $node->path = array('alias' => "content/poll/$i"); + + $nbchoices = ($i % 4) + 2; + for ($c = 0; $c < $nbchoices; $c++) { + $node->choice[] = array('chtext' => "Choice $c for poll $i", 'chvotes' => 0, 'weight' => 0); + } + node_save($node); + $path = array( + 'alias' => "content/poll/$i/results", + 'source' => "node/$node->nid/results", + ); + path_save($path); + + // Add some votes + $node = node_load($node->nid); + $choices = array_keys($node->choice); + $original_user = $GLOBALS['user']; + for ($v = 0; $v < ($i % 4); $v++) { + drupal_static_reset('ip_address'); + $_SERVER['REMOTE_ADDR'] = "127.0.$v.1"; + $GLOBALS['user'] = drupal_anonymous_user();// We should have already allowed anon to vote. + $c = $v % $nbchoices; + $form_state = array(); + $form_state['values']['choice'] = $choices[$c]; + $form_state['values']['op'] = t('Vote'); + drupal_form_submit('poll_view_voting', $form_state, $node); + } +} + +$uid = 6; +$node_type = 'broken'; +$user = user_load($uid); +$node = new stdClass(); +$node->uid = $uid; +$node->type = 'article'; +$body_text = str_repeat("node body ($node_type) - 37", 100); +$node->sticky = 0; +$node->title = "node title 24"; +$node->language = LANGUAGE_NONE; +$node->body[$node->language][0]['value'] = $body_text; +$node->body[$node->language][0]['summary'] = text_summary($body_text); +$node->body[$node->language][0]['format'] = 'filtered_html'; +$node->status = 1; +$node->revision = 0; +$node->promote = 0; +$node->created = 1263769200; +$node->log = "added a broken node"; +$node->path = array('alias' => "content/1263769200"); +node_save($node); +db_update('node') + ->fields(array( + 'type' => $node_type, + )) + ->condition('nid', $node->nid) + ->execute(); +db_update('field_data_body') + ->fields(array( + 'bundle' => $node_type, + )) + ->condition('entity_id', $node->nid) + ->condition('entity_type', 'node') + ->execute(); +db_update('field_revision_body') + ->fields(array( + 'bundle' => $node_type, + )) + ->condition('entity_id', $node->nid) + ->condition('entity_type', 'node') + ->execute(); +db_update('field_config_instance') + ->fields(array( + 'bundle' => $node_type, + )) + ->condition('bundle', 'article') + ->execute(); From cf9b0d63ed32b68a2a07fd03a77af0531c752db7 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Fri, 16 Sep 2011 17:53:59 -0400 Subject: [PATCH 61/80] - Patch #1273206 by droplet: access global when it essentially needed. --- modules/node/node.module | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/node/node.module b/modules/node/node.module index 58604264e97e..30e95591726f 100644 --- a/modules/node/node.module +++ b/modules/node/node.module @@ -2833,8 +2833,6 @@ function node_search_validate($form, &$form_state) { * TRUE if the operation may be performed, FALSE otherwise. */ function node_access($op, $node, $account = NULL) { - global $user; - $rights = &drupal_static(__FUNCTION__, array()); if (!$node || !in_array($op, array('view', 'update', 'delete', 'create'), TRUE)) { @@ -2844,7 +2842,7 @@ function node_access($op, $node, $account = NULL) { } // If no user object is supplied, the access check is for the current user. if (empty($account)) { - $account = $user; + $account = $GLOBALS['user']; } // $node may be either an object or a node type. Since node types cannot be From 996b5aca77344ec83f36e5800453706eb96b4f03 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Sat, 17 Sep 2011 07:23:13 -0400 Subject: [PATCH 62/80] - Patch #1078646 by Nneka, droplet, Vincent Giersch, larowlan: incorrect link to the author's profile in topic column if topic has replies. --- modules/forum/forum.module | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/forum/forum.module b/modules/forum/forum.module index 5bb43925bc55..0dcea5adaa6b 100644 --- a/modules/forum/forum.module +++ b/modules/forum/forum.module @@ -1127,7 +1127,6 @@ function template_preprocess_forum_topic_list(&$variables) { $variables['topics'][$id]->title = l($topic->title, "node/$topic->nid"); $variables['topics'][$id]->message = ''; } - $topic->uid = $topic->last_comment_uid ? $topic->last_comment_uid : $topic->uid; $variables['topics'][$id]->created = theme('forum_submitted', array('topic' => $topic)); $variables['topics'][$id]->last_reply = theme('forum_submitted', array('topic' => isset($topic->last_reply) ? $topic->last_reply : NULL)); From c8302d626540edc433a30f1a0f70270371307e68 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Sat, 17 Sep 2011 07:27:18 -0400 Subject: [PATCH 63/80] - Patch #646312 by mr.baileys: remove ->module. --- modules/simpletest/tests/path.test | 2 +- modules/taxonomy/taxonomy.admin.inc | 1 - modules/taxonomy/taxonomy.install | 14 +++++++------- modules/taxonomy/taxonomy.module | 4 ---- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/modules/simpletest/tests/path.test b/modules/simpletest/tests/path.test index 4701b5659b12..e1b320d270d1 100644 --- a/modules/simpletest/tests/path.test +++ b/modules/simpletest/tests/path.test @@ -180,7 +180,7 @@ class UrlAlterFunctionalTest extends DrupalWebTestCase { // level and for a specific existing forum. $this->assertUrlInboundAlter('community', 'forum'); $this->assertUrlOutboundAlter('forum', 'community'); - $forum_vid = db_query("SELECT vid FROM {taxonomy_vocabulary} WHERE module = 'forum'")->fetchField(); + $forum_vid = variable_get('forum_nav_vocabulary'); $tid = db_insert('taxonomy_term_data') ->fields(array( 'name' => $this->randomName(), diff --git a/modules/taxonomy/taxonomy.admin.inc b/modules/taxonomy/taxonomy.admin.inc index a236cfed1d82..2440a283d8bc 100644 --- a/modules/taxonomy/taxonomy.admin.inc +++ b/modules/taxonomy/taxonomy.admin.inc @@ -173,7 +173,6 @@ function taxonomy_form_vocabulary($form, &$form_state, $edit = array()) { if (isset($vocabulary->vid)) { $form['actions']['delete'] = array('#type' => 'submit', '#value' => t('Delete')); $form['vid'] = array('#type' => 'value', '#value' => $vocabulary->vid); - $form['module'] = array('#type' => 'value', '#value' => $vocabulary->module); } $form['#validate'][] = 'taxonomy_form_vocabulary_validate'; diff --git a/modules/taxonomy/taxonomy.install b/modules/taxonomy/taxonomy.install index b67f18b58956..3e07259f4118 100644 --- a/modules/taxonomy/taxonomy.install +++ b/modules/taxonomy/taxonomy.install @@ -150,13 +150,6 @@ function taxonomy_schema() { 'size' => 'tiny', 'description' => 'The type of hierarchy allowed within the vocabulary. (0 = disabled, 1 = single, 2 = multiple)', ), - 'module' => array( - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - 'description' => 'The module which created the vocabulary.', - ), 'weight' => array( 'type' => 'int', 'not null' => TRUE, @@ -246,3 +239,10 @@ function taxonomy_field_schema($field) { ), ); } + +/** + * Remove the {taxonomy_vocabulary}.module field. + */ +function taxonomy_update_8000() { + db_drop_field('taxonomy_vocabulary', 'module'); +} \ No newline at end of file diff --git a/modules/taxonomy/taxonomy.module b/modules/taxonomy/taxonomy.module index eb81870f9983..d7a5e9a1ffbe 100644 --- a/modules/taxonomy/taxonomy.module +++ b/modules/taxonomy/taxonomy.module @@ -417,10 +417,6 @@ function taxonomy_vocabulary_save($vocabulary) { $vocabulary->old_machine_name = $vocabulary->original->machine_name; } - if (!isset($vocabulary->module)) { - $vocabulary->module = 'taxonomy'; - } - module_invoke_all('taxonomy_vocabulary_presave', $vocabulary); module_invoke_all('entity_presave', $vocabulary, 'taxonomy_vocabulary'); From 6d1c15fc7c74b513de49c26d9257d8621bfb36cb Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Sat, 17 Sep 2011 14:10:41 -0400 Subject: [PATCH 64/80] - Patch #952772 by oriol_e9g, mdupont, bleen18, idflood: theme_html_tag() optional value not really optional. --- includes/theme.inc | 5 +++-- modules/simpletest/tests/theme.test | 26 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/includes/theme.inc b/includes/theme.inc index 3287073f380d..f592891ac143 100644 --- a/includes/theme.inc +++ b/includes/theme.inc @@ -1902,11 +1902,12 @@ function theme_feed_icon($variables) { */ function theme_html_tag($variables) { $element = $variables['element']; + $attributes = isset($element['#attributes']) ? drupal_attributes($element['#attributes']) : ''; if (!isset($element['#value'])) { - return '<' . $element['#tag'] . drupal_attributes($element['#attributes']) . " />\n"; + return '<' . $element['#tag'] . $attributes . " />\n"; } else { - $output = '<' . $element['#tag'] . drupal_attributes($element['#attributes']) . '>'; + $output = '<' . $element['#tag'] . $attributes . '>'; if (isset($element['#value_prefix'])) { $output .= $element['#value_prefix']; } diff --git a/modules/simpletest/tests/theme.test b/modules/simpletest/tests/theme.test index f1e1bd58b86a..53557e361a03 100644 --- a/modules/simpletest/tests/theme.test +++ b/modules/simpletest/tests/theme.test @@ -354,3 +354,29 @@ class ThemeFastTestCase extends DrupalWebTestCase { $this->assertText('registry not initialized', t('The registry was not initialized')); } } + +/** + * Unit tests for theme_html_tag(). + */ +class ThemeHtmlTag extends DrupalUnitTestCase { + public static function getInfo() { + return array( + 'name' => 'Theme HTML Tag', + 'description' => 'Tests theme_html_tag() built-in theme functions.', + 'group' => 'Theme', + ); + } + + /** + * Test function theme_html_tag() + */ + function testThemeHtmlTag() { + // Test auto-closure meta tag generation + $tag['element'] = array('#tag' => 'meta', '#attributes' => array('name' => 'description', 'content' => 'Drupal test')); + $this->assertEqual(''."\n", theme_html_tag($tag), t('Test auto-closure meta tag generation.')); + + // Test title tag generation + $tag['element'] = array('#tag' => 'title', '#value' => 'title test'); + $this->assertEqual('title test'."\n", theme_html_tag($tag), t('Test title tag generation.')); + } +} From a3bef9f39716ba7ca1e6bca126fb43310971f6b1 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Sat, 17 Sep 2011 20:02:22 -0400 Subject: [PATCH 65/80] - Patch #1153122 by Mac_Weber, tim.plunkett: Fixed padding to make Save button in admin page more visible. --- modules/overlay/overlay-child.css | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/overlay/overlay-child.css b/modules/overlay/overlay-child.css index f3532159bfcc..d047535415d7 100644 --- a/modules/overlay/overlay-child.css +++ b/modules/overlay/overlay-child.css @@ -17,6 +17,7 @@ html.js body { min-width: 700px; position: relative; padding: .2em; + padding-bottom: 2em; padding-right: 26px; /* LTR */ width: 88%; } From 8473b2ff9573259b13a07fcd17db34db018497f0 Mon Sep 17 00:00:00 2001 From: Bob Vincent Date: Wed, 14 Sep 2011 14:58:11 -0400 Subject: [PATCH 66/80] Issue #1170538: Rename core .gitignore file to example.gitignore. --- .gitignore => example.gitignore | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .gitignore => example.gitignore (100%) diff --git a/.gitignore b/example.gitignore similarity index 100% rename from .gitignore rename to example.gitignore From 8a2b590095479303d165e632b6c373e8c0d1bd0b Mon Sep 17 00:00:00 2001 From: Bob Vincent Date: Wed, 14 Sep 2011 14:58:33 -0400 Subject: [PATCH 67/80] Issue #1170538: Add explanatory comments to the example.gitignore file. --- example.gitignore | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/example.gitignore b/example.gitignore index 5023b0b32a5c..d25a2097603e 100644 --- a/example.gitignore +++ b/example.gitignore @@ -1,3 +1,12 @@ +# To use this file simply copy it to .gitignore, and it will cause files like +# your settings.php and user-uploaded files to be excluded from git source +# control. This is a common strategy to avoid accidentally including private +# information in public repositories and patch files. +# +# A .gitignore file was included in Drupal core for versions 7.2 through 7.8. +# As of Drupal 7.9, this is no longer the case, and any changes made to your +# .gitignore file will no longer be overwritten by core updates. + # Ignore configuration files that may contain sensitive information. sites/*/settings*.php @@ -5,3 +14,12 @@ sites/*/settings*.php sites/*/files sites/*/private +# If you prefer to store your .gitignore file in the sites/ folder, comment +# or delete the previous settings and uncomment the following ones, instead. + +# Ignore configuration files that may contain sensitive information. +# */settings*.php + +# Ignore paths that contain user-generated content. +# */files +# */private From 0449633be76d51f0686eb61f295af186f7f7d6fb Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Sun, 18 Sep 2011 20:07:11 -0400 Subject: [PATCH 68/80] - Patch #1280550 by loganfsmyth: language negotiation UX: Change wording and add link on 'detection and selection' page. --- includes/language.inc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/includes/language.inc b/includes/language.inc index b7057f2a138c..c729ec25a161 100644 --- a/includes/language.inc +++ b/includes/language.inc @@ -299,8 +299,9 @@ function language_negotiation_info() { $language_providers[LANGUAGE_NEGOTIATION_DEFAULT] = array( 'callbacks' => array('language' => 'language_from_default'), 'weight' => 10, - 'name' => t('Default'), + 'name' => t('Default language'), 'description' => t('Use the default site language (@language_name).', array('@language_name' => language_default()->native)), + 'config' => 'admin/config/regional/language', ); // Let other modules alter the list of language providers. From 778ea2ceebe118e7df21d48c15261cf378f93e66 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Sun, 18 Sep 2011 20:41:59 -0400 Subject: [PATCH 69/80] - Patch #1116416 by Kars-T, Coornail: use 'Header set' instead of 'Header append' in .htaccess to avoid double encoding. --- .htaccess | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.htaccess b/.htaccess index d2b9ae67457b..e59cd99a512e 100644 --- a/.htaccess +++ b/.htaccess @@ -123,7 +123,7 @@ DirectoryIndex index.php index.html index.htm # Serve correct encoding type. - Header append Content-Encoding gzip + Header set Content-Encoding gzip # Force proxies to cache gzipped & non-gzipped css/js files separately. Header append Vary Accept-Encoding From cb0670bda93ccd6c59cf8de13ba64cbdb3de1314 Mon Sep 17 00:00:00 2001 From: webchick Date: Tue, 20 Sep 2011 15:22:30 -0400 Subject: [PATCH 70/80] Issue #1059268 by Berdir: Fixed Files are lost when adding multiple files to multiple file fields at the same time. --- modules/file/file.field.inc | 69 ++++++++++-------- modules/file/tests/file.test | 137 +++++++++++++++++++++-------------- 2 files changed, 122 insertions(+), 84 deletions(-) diff --git a/modules/file/file.field.inc b/modules/file/file.field.inc index c73430c64c05..35696dda62c0 100644 --- a/modules/file/file.field.inc +++ b/modules/file/file.field.inc @@ -447,37 +447,18 @@ function file_field_widget_form(&$form, &$form_state, $field, $instance, $langco 'description' => '', ); - // Retrieve any values set in $form_state, as will be the case during Ajax - // rebuilds of this form. - if (isset($form_state['values'])) { - $path = array_merge($element['#field_parents'], array($field['field_name'], $langcode)); - $path_exists = FALSE; - $values = drupal_array_get_nested_value($form_state['values'], $path, $path_exists); - if ($path_exists) { - $items = $values; - drupal_array_set_nested_value($form_state['values'], $path, NULL); - } + // Load the items for form rebuilds from the field state as they might not be + // in $form_state['values'] because of validation limitations. Also, they are + // only passed in as $items when editing existing entities. + $field_state = field_form_get_state($element['#field_parents'], $field['field_name'], $langcode, $form_state); + if (isset($field_state['items'])) { + $items = $field_state['items']; } - foreach ($items as $delta => $item) { - $items[$delta] = array_merge($defaults, $items[$delta]); - // Remove any items from being displayed that are not needed. - if ($items[$delta]['fid'] == 0) { - unset($items[$delta]); - } - } - - // Re-index deltas after removing empty items. - $items = array_values($items); - - // Update order according to weight. - $items = _field_sort_items($field, $items); - // Essentially we use the managed_file type, extended with some enhancements. $element_info = element_info('managed_file'); $element += array( '#type' => 'managed_file', - '#default_value' => isset($items[$delta]) ? $items[$delta] : $defaults, '#upload_location' => file_field_widget_uri($field, $instance), '#upload_validators' => file_field_widget_upload_validators($field, $instance), '#value_callback' => 'file_field_widget_value', @@ -487,6 +468,8 @@ function file_field_widget_form(&$form, &$form_state, $field, $instance, $langco ); if ($field['cardinality'] == 1) { + // Set the default value. + $element['#default_value'] = !empty($items) ? $items[0] : $defaults; // If there's only one field, return it as delta 0. if (empty($element['#default_value']['fid'])) { $element['#description'] = theme('file_upload_help', array('description' => $element['#description'], 'upload_validators' => $element['#upload_validators'])); @@ -495,15 +478,15 @@ function file_field_widget_form(&$form, &$form_state, $field, $instance, $langco } else { // If there are multiple values, add an element for each existing one. - $delta = -1; - foreach ($items as $delta => $item) { + foreach ($items as $item) { $elements[$delta] = $element; $elements[$delta]['#default_value'] = $item; $elements[$delta]['#weight'] = $delta; + $delta++; } - // And then add one more empty row for new uploads. - $delta++; - if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED || $delta < $field['cardinality']) { + // And then add one more empty row for new uploads except when this is a + // programmed form as it is not necessary. + if (($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED || $delta < $field['cardinality']) && empty($form_state['programmed'])) { $elements[$delta] = $element; $elements[$delta]['#default_value'] = $defaults; $elements[$delta]['#weight'] = $delta; @@ -757,6 +740,32 @@ function file_field_widget_submit($form, &$form_state) { // so nothing is lost in doing this. $parents = array_slice($form_state['triggering_element']['#parents'], 0, -2); drupal_array_set_nested_value($form_state['input'], $parents, NULL); + + $button = $form_state['triggering_element']; + + // Go one level up in the form, to the widgets container. + $element = drupal_array_get_nested_value($form, array_slice($button['#array_parents'], 0, -1)); + $field_name = $element['#field_name']; + $langcode = $element['#language']; + $parents = $element['#field_parents']; + + $submitted_values = drupal_array_get_nested_value($form_state['values'], array_slice($button['#array_parents'], 0, -2)); + foreach ($submitted_values as $delta => $submitted_value) { + if (!$submitted_value['fid']) { + unset($submitted_values[$delta]); + } + } + + // Re-index deltas after removing empty items. + $submitted_values = array_values($submitted_values); + + // Update form_state values. + drupal_array_set_nested_value($form_state['values'], array_slice($button['#array_parents'], 0, -2), $submitted_values); + + // Update items. + $field_state = field_form_get_state($parents, $field_name, $langcode, $form_state); + $field_state['items'] = $submitted_values; + field_form_set_state($parents, $field_name, $langcode, $form_state, $field_state); } /** diff --git a/modules/file/tests/file.test b/modules/file/tests/file.test index 32de9dcabb97..5474774bbd4a 100644 --- a/modules/file/tests/file.test +++ b/modules/file/tests/file.test @@ -380,7 +380,7 @@ class FileFieldWidgetTestCase extends FileFieldTestCase { } /** - * Tests upload and remove buttons, with and without Ajax, for a multi-valued File field. + * Tests upload and remove buttons, with and without Ajax, for multiple multi-valued File field. */ function testMultiValuedWidget() { // Use 'page' instead of 'article', so that the 'article' image field does @@ -389,77 +389,106 @@ class FileFieldWidgetTestCase extends FileFieldTestCase { // using a custom node type. $type_name = 'page'; $field_name = strtolower($this->randomName()); + $field_name2 = strtolower($this->randomName()); $this->createFileField($field_name, $type_name, array('cardinality' => 3)); + $this->createFileField($field_name2, $type_name, array('cardinality' => 3)); + $field = field_info_field($field_name); $instance = field_info_instance('node', $field_name, $type_name); + $field2 = field_info_field($field_name2); + $instance2 = field_info_instance('node', $field_name2, $type_name); + $test_file = $this->getTestFile('text'); foreach (array('nojs', 'js') as $type) { - // Visit the node creation form, and upload 3 files. Since the field has - // cardinality of 3, ensure the "Upload" button is displayed until after - // the 3rd file, and after that, isn't displayed. + // Visit the node creation form, and upload 3 files for each field. Since + // the field has cardinality of 3, ensure the "Upload" button is displayed + // until after the 3rd file, and after that, isn't displayed. Because + // SimpleTest triggers the last button with a given name, so upload to the + // second field first. // @todo This is only testing a non-Ajax upload, because drupalPostAJAX() // does not yet emulate jQuery's file upload. + // $this->drupalGet("node/add/$type_name"); - for ($delta = 0; $delta < 3; $delta++) { - $edit = array('files[' . $field_name . '_' . LANGUAGE_NONE . '_' . $delta . ']' => drupal_realpath($test_file->uri)); - // If the Upload button doesn't exist, drupalPost() will automatically - // fail with an assertion message. - $this->drupalPost(NULL, $edit, t('Upload')); + foreach (array($field_name2, $field_name) as $each_field_name) { + for ($delta = 0; $delta < 3; $delta++) { + $edit = array('files[' . $each_field_name . '_' . LANGUAGE_NONE . '_' . $delta . ']' => drupal_realpath($test_file->uri)); + // If the Upload button doesn't exist, drupalPost() will automatically + // fail with an assertion message. + $this->drupalPost(NULL, $edit, t('Upload')); + } } - $this->assertNoFieldByXpath('//input[@type="submit"]', t('Upload'), t('After uploading 3 files, the "Upload" button is no longer displayed.')); + $this->assertNoFieldByXpath('//input[@type="submit"]', t('Upload'), t('After uploading 3 files for each field, the "Upload" button is no longer displayed.')); - // Test clicking each "Remove" button. For extra robustness, test them out - // of sequential order. They are 0-indexed, and get renumbered after each - // iteration, so array(1, 1, 0) means: - // - First remove the 2nd file. - // - Then remove what is then the 2nd file (was originally the 3rd file). - // - Then remove the first file. - $num_expected_remove_buttons = 3; - foreach (array(1, 1, 0) as $delta) { - // Ensure we have the expected number of Remove buttons, and that they - // are numbered sequentially. - $buttons = $this->xpath('//input[@type="submit" and @value="Remove"]'); - $this->assertTrue(is_array($buttons) && count($buttons) === $num_expected_remove_buttons, t('There are %n "Remove" buttons displayed (JSMode=%type).', array('%n' => $num_expected_remove_buttons, '%type' => $type))); - foreach ($buttons as $i => $button) { - $this->assertIdentical((string) $button['name'], $field_name . '_' . LANGUAGE_NONE . '_' . $i . '_remove_button'); - } + $num_expected_remove_buttons = 6; - // "Click" the remove button (emulating either a nojs or js submission). - $button_name = $field_name . '_' . LANGUAGE_NONE . '_' . $delta . '_remove_button'; - switch ($type) { - case 'nojs': - // drupalPost() takes a $submit parameter that is the value of the - // button whose click we want to emulate. Since we have multiple - // buttons with the value "Remove", and want to control which one we - // use, we change the value of the other ones to something else. - // Since non-clicked buttons aren't included in the submitted POST - // data, and since drupalPost() will result in $this being updated - // with a newly rebuilt form, this doesn't cause problems. - foreach ($buttons as $button) { - if ($button['name'] != $button_name) { - $button['value'] = 'DUMMY'; - } + foreach (array($field_name, $field_name2) as $current_field_name) { + // How many uploaded files for the current field are remaining. + $remaining = 3; + // Test clicking each "Remove" button. For extra robustness, test them out + // of sequential order. They are 0-indexed, and get renumbered after each + // iteration, so array(1, 1, 0) means: + // - First remove the 2nd file. + // - Then remove what is then the 2nd file (was originally the 3rd file). + // - Then remove the first file. + foreach (array(1,1,0) as $delta) { + // Ensure we have the expected number of Remove buttons, and that they + // are numbered sequentially. + $buttons = $this->xpath('//input[@type="submit" and @value="Remove"]'); + $this->assertTrue(is_array($buttons) && count($buttons) === $num_expected_remove_buttons, t('There are %n "Remove" buttons displayed (JSMode=%type).', array('%n' => $num_expected_remove_buttons, '%type' => $type))); + foreach ($buttons as $i => $button) { + $key = $i >= $remaining ? $i - $remaining : $i; + $check_field_name = $field_name2; + if ($current_field_name == $field_name && $i < $remaining) { + $check_field_name = $field_name; } - $this->drupalPost(NULL, array(), t('Remove')); - break; - case 'js': - // drupalPostAJAX() lets us target the button precisely, so we don't - // require the workaround used above for nojs. - $this->drupalPostAJAX(NULL, array(), array($button_name => t('Remove'))); - break; - } - $num_expected_remove_buttons--; - // Ensure we have a single Upload button, and that it is numbered - // sequentially after the Remove buttons. - $buttons = $this->xpath('//input[@type="submit" and @value="Upload"]'); - $this->assertTrue(is_array($buttons) && count($buttons) == 1 && ((string) $buttons[0]['name'] === ($field_name . '_' . LANGUAGE_NONE . '_' . $num_expected_remove_buttons . '_upload_button')), t('After removing a file, an "Upload" button is displayed (JSMode=%type).')); + $this->assertIdentical((string) $button['name'], $check_field_name . '_' . LANGUAGE_NONE . '_' . $key. '_remove_button'); + } + + // "Click" the remove button (emulating either a nojs or js submission). + $button_name = $current_field_name . '_' . LANGUAGE_NONE . '_' . $delta . '_remove_button'; + switch ($type) { + case 'nojs': + // drupalPost() takes a $submit parameter that is the value of the + // button whose click we want to emulate. Since we have multiple + // buttons with the value "Remove", and want to control which one we + // use, we change the value of the other ones to something else. + // Since non-clicked buttons aren't included in the submitted POST + // data, and since drupalPost() will result in $this being updated + // with a newly rebuilt form, this doesn't cause problems. + foreach ($buttons as $button) { + if ($button['name'] != $button_name) { + $button['value'] = 'DUMMY'; + } + } + $this->drupalPost(NULL, array(), t('Remove')); + break; + case 'js': + // drupalPostAJAX() lets us target the button precisely, so we don't + // require the workaround used above for nojs. + $this->drupalPostAJAX(NULL, array(), array($button_name => t('Remove'))); + break; + } + $num_expected_remove_buttons--; + $remaining--; + + // Ensure an "Upload" button for the current field is displayed with the + // correct name. + $upload_button_name = $current_field_name . '_' . LANGUAGE_NONE . '_' . $remaining . '_upload_button'; + $buttons = $this->xpath('//input[@type="submit" and @value="Upload" and @name=:name]', array(':name' => $upload_button_name)); + $this->assertTrue(is_array($buttons) && count($buttons) == 1, t('The upload button is displayed with the correct name (JSMode=%type).', array('%type' => $type))); + + // Ensure only at most one button per field is displayed. + $buttons = $this->xpath('//input[@type="submit" and @value="Upload"]'); + $expected = $current_field_name == $field_name ? 1 : 2; + $this->assertTrue(is_array($buttons) && count($buttons) == $expected, t('After removing a file, only one "Upload" button for each possible field is displayed (JSMode=%type).', array('%type' => $type))); + } } // Ensure the page now has no Remove buttons. - $this->assertNoFieldByXPath('//input[@type="submit"]', t('Remove'), t('After removing all files, there is no "Remove" button displayed.', array('%n' => $num_expected_remove_buttons, '%type' => $type))); + $this->assertNoFieldByXPath('//input[@type="submit"]', t('Remove'), t('After removing all files, there is no "Remove" button displayed (JSMode=%type).', array('%type' => $type))); // Save the node and ensure it does not have any files. $this->drupalPost(NULL, array('title' => $this->randomName()), t('Save')); From 75f9f5dc7c79fd6182e1e7841f8399a7c59c4ed0 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Wed, 21 Sep 2011 05:31:43 -0400 Subject: [PATCH 71/80] - Patch #1283244 by pillarsdotnet, TR: request_uri() documentation did not conform to standards. --- includes/bootstrap.inc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index 7d000343a588..7c6ca094a41e 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -1572,11 +1572,12 @@ function drupal_validate_utf8($text) { } /** - * Since $_SERVER['REQUEST_URI'] is only available on Apache, we - * generate an equivalent using other environment variables. + * Returns the equivalent of Apache's $_SERVER['REQUEST_URI'] variable. + * + * Because $_SERVER['REQUEST_URI'] is only available on Apache, we generate an + * equivalent using other environment variables. */ function request_uri() { - if (isset($_SERVER['REQUEST_URI'])) { $uri = $_SERVER['REQUEST_URI']; } From eb170dca1d330d545b247eae23e913003886d729 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Wed, 21 Sep 2011 05:32:00 -0400 Subject: [PATCH 72/80] - Patch #1266348 by kathyh, webchick: improved consistency of internal APIs --- modules/aggregator/aggregator.pages.inc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/aggregator/aggregator.pages.inc b/modules/aggregator/aggregator.pages.inc index 53ecb3684e61..3ca084af005e 100644 --- a/modules/aggregator/aggregator.pages.inc +++ b/modules/aggregator/aggregator.pages.inc @@ -14,7 +14,7 @@ function aggregator_page_last() { drupal_add_feed('aggregator/rss', variable_get('site_name', 'Drupal') . ' ' . t('aggregator')); - $items = aggregator_feed_items_load('sum'); + $items = aggregator_load_feed_items('sum'); return _aggregator_page_list($items, arg(1)); } @@ -33,8 +33,8 @@ function aggregator_page_source($feed) { $feed_source = theme('aggregator_feed_source', array('feed' => $feed)); // It is safe to include the fid in the query because it's loaded from the - // database by aggregator_feed_load. - $items = aggregator_feed_items_load('source', $feed); + // database by aggregator_feed_load(). + $items = aggregator_load_feed_items('source', $feed); return _aggregator_page_list($items, arg(3), $feed_source); } @@ -67,8 +67,8 @@ function aggregator_page_category($category) { drupal_add_feed('aggregator/rss/' . $category['cid'], variable_get('site_name', 'Drupal') . ' ' . t('aggregator - @title', array('@title' => $category['title']))); // It is safe to include the cid in the query because it's loaded from the - // database by aggregator_category_load. - $items = aggregator_feed_items_load('category', $category); + // database by aggregator_category_load(). + $items = aggregator_load_feed_items('category', $category); return _aggregator_page_list($items, arg(3)); } @@ -98,7 +98,7 @@ function aggregator_page_category_form($form, $form_state, $category) { * @return * An array of the feed items. */ -function aggregator_feed_items_load($type, $data = NULL) { +function aggregator_load_feed_items($type, $data = NULL) { $items = array(); $range_limit = 20; switch ($type) { From bb9430f513bd7815136b05ccaa3533c853fb77df Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Wed, 21 Sep 2011 05:35:29 -0400 Subject: [PATCH 73/80] - Patch #1282024 by Wim Leers: optimize favicon.ico by removing the embedded 32x32 version. --- misc/favicon.ico | Bin 5430 -> 1150 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/misc/favicon.ico b/misc/favicon.ico index bfa1c996f26cb47776b27da40f151c71e637b715..3417ec244e8dc4be38ac8a6da044533ac6790773 100644 GIT binary patch delta 89 zcmdm{^^e1Zfq@YS1q48}0z(E11A`a?1A_(-1BHMh5OE;IzyOpJ;s)V~j#3-rJ}^%X cVo{l#$HF7{vNA*d*Li$*~?6%-W& z1-TS(m19B0C^rZyMARsFAR-EalqeApP_EtW^t{c3WqG^H;j8MJc{APp^-TBsW*&eD zbYR2?pw=fn`u}WCb9TDBNqWeebw zG!NvTf2$s1cH~Ap=d%FQL+voiX)?a4F2VYmr?LpAA7}A#MA?BPeH$Q?=GUdXd1u?w zREPJo!?C07edkz4gyUu2K7Kt^^7dtE?wvZUBbUpu@}?MLou}dLONYou3pbyi=vB_! zpK}BmZ=d<+5G+ox$2U*1u`JCEuX-7BaV68*Laa#EeTli^?HI61zmd03bO0%Duc=Ml z3K+CfksHcYC2t>R53e-YDbHQp2j?lmw-2Kf6M~DC`sNc ztGsp;b3Mjz>(%5V)C%6X8-qS?&)TfOg#m5Nwb6)fD4(pYJ#ymo@=XeclaT2|ozg z2#qD|p6p+Gc^697LCXnC_(e!Z+gSY0@imn1>6-MU1^^Ai0V?zWa&!UQM8d-6a!7~i zwvvt16+n&(o-rhV-$)oAse7pCp||3(20DN`CAMojjPk7)U49@N>b_hlozI;c-J%zN8qqrH_g zev}{ILG@%rFM{73@z5o97ASLU_03Z~ft5zsIaZPvgP{&%JJ+kog$Vyi_E*wWbR`EC z=Tol-KH2ER*h9NAHtRFK?9Ic@;DQU@7`gp>E|$lC#|xSjUyqSdEq9?;dkn+qfBi;5O+y;tOvDth1l|yQMsZP|C6<}(xHJ&@L0M)Hu zChfp|CZcRr{4;ByP6$8ShB9+cWokJo&y;u(7(Rn;-tArYN!VF2w88y$jj*%j(e2d=Rp4fbBc0RmRnX zU#p5AZ{IWbyg$}vI1R?4=w)0!UKB)dcQF>1vW{|}eZ!19cwX|i_y~j6jO5-4ze5$D z_Ft}S{~`L9HvRPiHh)bIa5VoBQ2l?xu9xcG%CK5Li)Cm(KK8N6iNI4?w68Z1c&6TasPv?3`s!WAz;@ zJY_71&F(N!w$MhgpCVPQx$cXOc-w^Jmk(30`BgC%$J=$oPbJ)FJ7_1|SQ6d7`0a}> z)y22@V#jwa^go}qTTzRQTPVlLK^CCb7hnC=4YYu<e=x0Z6aWAK From bd0fec9a5df89543e5966a7034760631dcd4a244 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Wed, 21 Sep 2011 05:50:30 -0400 Subject: [PATCH 74/80] - Patch #1285098 by tim.plunkett: toolbar Drawer uses invalid Render API key. --- modules/toolbar/toolbar.module | 5 +++-- modules/toolbar/toolbar.tpl.php | 4 +--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/modules/toolbar/toolbar.module b/modules/toolbar/toolbar.module index 61ae648ad5f1..d25ca2d0d843 100644 --- a/modules/toolbar/toolbar.module +++ b/modules/toolbar/toolbar.module @@ -263,10 +263,11 @@ function toolbar_view() { 'toolbar-drawer', 'clearfix', ); - if(_toolbar_is_collapsed()) { + if (_toolbar_is_collapsed()) { $toolbar_drawer_classes[] = 'collapsed'; } - $build['toolbar_drawer_classes'] = implode(' ', $toolbar_drawer_classes); + $build['toolbar_drawer']['#type'] = 'container'; + $build['toolbar_drawer']['#attributes']['class'] = $toolbar_drawer_classes; return $build; } diff --git a/modules/toolbar/toolbar.tpl.php b/modules/toolbar/toolbar.tpl.php index 1df0cf03bf23..342fa603f1bc 100644 --- a/modules/toolbar/toolbar.tpl.php +++ b/modules/toolbar/toolbar.tpl.php @@ -31,7 +31,5 @@
-
- -
+
From 120dbd9760fdf0b432a6a6bd2e9827c7bc0be826 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Wed, 21 Sep 2011 05:58:59 -0400 Subject: [PATCH 75/80] - Patch #1122584 by 10oclock: use #attached for block_admin_display_form() instead of drupal_add_css(). --- modules/block/block.admin.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/block/block.admin.inc b/modules/block/block.admin.inc index f58e3739d2a4..7169190da8e7 100644 --- a/modules/block/block.admin.inc +++ b/modules/block/block.admin.inc @@ -77,7 +77,7 @@ function block_admin_display_prepare_blocks($theme) { */ function block_admin_display_form($form, &$form_state, $blocks, $theme, $block_regions = NULL) { - drupal_add_css(drupal_get_path('module', 'block') . '/block.admin.css'); + $form['#attached']['css'] = array(drupal_get_path('module', 'block') . '/block.admin.css'); // Get a list of block regions if one was not provided. if (!isset($block_regions)) { From 35c400c79f2d22fdff3c476d381d42ef66dad043 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Wed, 21 Sep 2011 06:01:44 -0400 Subject: [PATCH 76/80] - Patch #1284436 by bxtaylor: make comment text gender-neutral in system_clean_url_settings(). --- modules/system/system.admin.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/system/system.admin.inc b/modules/system/system.admin.inc index 6824f19d792b..d5d10b284865 100644 --- a/modules/system/system.admin.inc +++ b/modules/system/system.admin.inc @@ -2243,8 +2243,8 @@ function system_clean_url_settings($form, &$form_state) { '#type' => 'markup', '#markup' => '

' . t('Use URLs like example.com/user instead of example.com/?q=user.'), ); - // Explain why the user is seeing this page and tell him what to expect - // after clicking the 'Run the clean URL test' button. + // Explain why the user is seeing this page and what to expect after + // clicking the 'Run the clean URL test' button. $form['clean_url_test_result'] = array( '#type' => 'markup', '#markup' => '

' . t('Clean URLs cannot be enabled. If you are directed to this page or to a Page not found (404) error after testing for clean URLs, see the online handbook.', array('@handbook' => 'http://drupal.org/node/15365')) . '

', From 33fe4c1d4fae9be55a56ba5004baa0c41cf15a9d Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Wed, 21 Sep 2011 06:03:15 -0400 Subject: [PATCH 77/80] - Patch #1008250 by jhodgdon, bxtaylor: clean up hook_init() and hook_boot() doc. --- modules/system/system.api.php | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/modules/system/system.api.php b/modules/system/system.api.php index ec7f6b8ba892..5b15a80db76b 100644 --- a/modules/system/system.api.php +++ b/modules/system/system.api.php @@ -672,8 +672,8 @@ function hook_element_info_alter(&$type) { * page logging and specialized cleanup. This hook MUST NOT print anything. * * Only use this hook if your code must run even for cached page views. - * If you have code which must run once on all non cached pages, use - * hook_init instead. Thats the usual case. If you implement this hook + * If you have code which must run once on all non-cached pages, use + * hook_init() instead. That is the usual case. If you implement this hook * and see an error like 'Call to undefined function', it is likely that * you are depending on the presence of a module which has not been loaded yet. * It is not loaded because Drupal is still in bootstrap mode. @@ -1744,32 +1744,36 @@ function hook_forms($form_id, $args) { } /** - * Perform setup tasks. See also, hook_init. + * Perform setup tasks for all page requests. * * This hook is run at the beginning of the page request. It is typically - * used to set up global parameters which are needed later in the request. + * used to set up global parameters that are needed later in the request. * - * Only use this hook if your code must run even for cached page views.This hook - * is called before modules or most include files are loaded into memory. + * Only use this hook if your code must run even for cached page views. This + * hook is called before modules or most include files are loaded into memory. * It happens while Drupal is still in bootstrap mode. + * + * @see hook_init() */ function hook_boot() { - // we need user_access() in the shutdown function. make sure it gets loaded + // We need user_access() in the shutdown function. Make sure it gets loaded. drupal_load('module', 'user'); drupal_register_shutdown_function('devel_shutdown'); } /** - * Perform setup tasks. See also, hook_boot. + * Perform setup tasks for non-cached page requests. * * This hook is run at the beginning of the page request. It is typically - * used to set up global parameters which are needed later in the request. - * when this hook is called, all modules are already loaded in memory. + * used to set up global parameters that are needed later in the request. + * When this hook is called, all modules are already loaded in memory. * * This hook is not run on cached pages. * * To add CSS or JS that should be present on all pages, modules should not * implement this hook, but declare these files in their .info file. + * + * @see hook_boot() */ function hook_init() { // Since this file should only be loaded on the front page, it cannot be From 58a5b82f90d0d92bd58ca6eb18bd936b5b7eb083 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Wed, 21 Sep 2011 06:09:49 -0400 Subject: [PATCH 78/80] - Patch #1018602 by fago, catch, aspilicious: move entity system to a module. --- includes/common.inc | 407 ---------------- includes/install.core.inc | 5 +- includes/module.inc | 8 +- includes/update.inc | 30 ++ modules/comment/comment.info | 1 + modules/entity/entity.api.php | 414 ++++++++++++++++ modules/entity/entity.controller.inc | 390 ++++++++++++++++ modules/entity/entity.info | 8 + modules/entity/entity.module | 442 ++++++++++++++++++ .../entity/entity.query.inc | 393 +--------------- .../tests/entity_cache_test.info | 0 .../tests/entity_cache_test.module | 0 .../tests/entity_cache_test_dependency.info | 0 .../tests/entity_cache_test_dependency.module | 0 .../tests/entity_crud_hook_test.info | 0 .../tests/entity_crud_hook_test.module | 0 .../tests/entity_crud_hook_test.test | 0 .../tests/entity_query.test | 1 - modules/field/field.info | 1 + modules/node/node.info | 1 + modules/system/system.api.php | 434 +---------------- modules/system/system.install | 4 +- modules/taxonomy/taxonomy.info | 1 + modules/user/user.entity.inc | 52 +++ modules/user/user.info | 2 +- modules/user/user.module | 47 -- update.php | 1 - 27 files changed, 1381 insertions(+), 1261 deletions(-) create mode 100644 modules/entity/entity.api.php create mode 100644 modules/entity/entity.controller.inc create mode 100644 modules/entity/entity.info create mode 100644 modules/entity/entity.module rename includes/entity.inc => modules/entity/entity.query.inc (71%) rename modules/{simpletest => entity}/tests/entity_cache_test.info (100%) rename modules/{simpletest => entity}/tests/entity_cache_test.module (100%) rename modules/{simpletest => entity}/tests/entity_cache_test_dependency.info (100%) rename modules/{simpletest => entity}/tests/entity_cache_test_dependency.module (100%) rename modules/{simpletest => entity}/tests/entity_crud_hook_test.info (100%) rename modules/{simpletest => entity}/tests/entity_crud_hook_test.module (100%) rename modules/{simpletest => entity}/tests/entity_crud_hook_test.test (100%) rename modules/{simpletest => entity}/tests/entity_query.test (99%) create mode 100644 modules/user/user.entity.inc diff --git a/includes/common.inc b/includes/common.inc index 0c6c9eb2be8f..268e36bfbd2b 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -7277,413 +7277,6 @@ function drupal_check_incompatibility($v, $current_version) { } } -/** - * Get the entity info array of an entity type. - * - * @see hook_entity_info() - * @see hook_entity_info_alter() - * - * @param $entity_type - * The entity type, e.g. node, for which the info shall be returned, or NULL - * to return an array with info about all types. - */ -function entity_get_info($entity_type = NULL) { - global $language; - - // Use the advanced drupal_static() pattern, since this is called very often. - static $drupal_static_fast; - if (!isset($drupal_static_fast)) { - $drupal_static_fast['entity_info'] = &drupal_static(__FUNCTION__); - } - $entity_info = &$drupal_static_fast['entity_info']; - - // hook_entity_info() includes translated strings, so each language is cached - // separately. - $langcode = $language->language; - - if (empty($entity_info)) { - if ($cache = cache()->get("entity_info:$langcode")) { - $entity_info = $cache->data; - } - else { - $entity_info = module_invoke_all('entity_info'); - // Merge in default values. - foreach ($entity_info as $name => $data) { - $entity_info[$name] += array( - 'fieldable' => FALSE, - 'controller class' => 'DrupalDefaultEntityController', - 'static cache' => TRUE, - 'field cache' => TRUE, - 'load hook' => $name . '_load', - 'bundles' => array(), - 'view modes' => array(), - 'entity keys' => array(), - 'translation' => array(), - ); - $entity_info[$name]['entity keys'] += array( - 'revision' => '', - 'bundle' => '', - ); - foreach ($entity_info[$name]['view modes'] as $view_mode => $view_mode_info) { - $entity_info[$name]['view modes'][$view_mode] += array( - 'custom settings' => FALSE, - ); - } - // If no bundle key is provided, assume a single bundle, named after - // the entity type. - if (empty($entity_info[$name]['entity keys']['bundle']) && empty($entity_info[$name]['bundles'])) { - $entity_info[$name]['bundles'] = array($name => array('label' => $entity_info[$name]['label'])); - } - // Prepare entity schema fields SQL info for - // DrupalEntityControllerInterface::buildQuery(). - if (isset($entity_info[$name]['base table'])) { - $entity_info[$name]['schema_fields_sql']['base table'] = drupal_schema_fields_sql($entity_info[$name]['base table']); - if (isset($entity_info[$name]['revision table'])) { - $entity_info[$name]['schema_fields_sql']['revision table'] = drupal_schema_fields_sql($entity_info[$name]['revision table']); - } - } - } - // Let other modules alter the entity info. - drupal_alter('entity_info', $entity_info); - cache()->set("entity_info:$langcode", $entity_info); - } - } - - if (empty($entity_type)) { - return $entity_info; - } - elseif (isset($entity_info[$entity_type])) { - return $entity_info[$entity_type]; - } -} - -/** - * Resets the cached information about entity types. - */ -function entity_info_cache_clear() { - drupal_static_reset('entity_get_info'); - // Clear all languages. - cache()->deletePrefix('entity_info:'); -} - -/** - * Helper function to extract id, vid, and bundle name from an entity. - * - * @param $entity_type - * The entity type; e.g. 'node' or 'user'. - * @param $entity - * The entity from which to extract values. - * @return - * A numerically indexed array (not a hash table) containing these - * elements: - * 0: primary id of the entity - * 1: revision id of the entity, or NULL if $entity_type is not versioned - * 2: bundle name of the entity - */ -function entity_extract_ids($entity_type, $entity) { - $info = entity_get_info($entity_type); - - // Objects being created might not have id/vid yet. - $id = isset($entity->{$info['entity keys']['id']}) ? $entity->{$info['entity keys']['id']} : NULL; - $vid = ($info['entity keys']['revision'] && isset($entity->{$info['entity keys']['revision']})) ? $entity->{$info['entity keys']['revision']} : NULL; - - if (!empty($info['entity keys']['bundle'])) { - // Explicitly fail for malformed entities missing the bundle property. - if (!isset($entity->{$info['entity keys']['bundle']}) || $entity->{$info['entity keys']['bundle']} === '') { - throw new EntityMalformedException(t('Missing bundle property on entity of type @entity_type.', array('@entity_type' => $entity_type))); - } - $bundle = $entity->{$info['entity keys']['bundle']}; - } - else { - // The entity type provides no bundle key: assume a single bundle, named - // after the entity type. - $bundle = $entity_type; - } - - return array($id, $vid, $bundle); -} - -/** - * Helper function to assemble an object structure with initial ids. - * - * This function can be seen as reciprocal to entity_extract_ids(). - * - * @param $entity_type - * The entity type; e.g. 'node' or 'user'. - * @param $ids - * A numerically indexed array, as returned by entity_extract_ids(), - * containing these elements: - * 0: primary id of the entity - * 1: revision id of the entity, or NULL if $entity_type is not versioned - * 2: bundle name of the entity, or NULL if $entity_type has no bundles - * @return - * An entity structure, initialized with the ids provided. - */ -function entity_create_stub_entity($entity_type, $ids) { - $entity = new stdClass(); - $info = entity_get_info($entity_type); - $entity->{$info['entity keys']['id']} = $ids[0]; - if (!empty($info['entity keys']['revision']) && isset($ids[1])) { - $entity->{$info['entity keys']['revision']} = $ids[1]; - } - if (!empty($info['entity keys']['bundle']) && isset($ids[2])) { - $entity->{$info['entity keys']['bundle']} = $ids[2]; - } - return $entity; -} - -/** - * Load entities from the database. - * - * The entities are stored in a static memory cache, and will not require - * database access if loaded again during the same page request. - * - * The actual loading is done through a class that has to implement the - * DrupalEntityControllerInterface interface. By default, - * DrupalDefaultEntityController is used. Entity types can specify that a - * different class should be used by setting the 'controller class' key in - * hook_entity_info(). These classes can either implement the - * DrupalEntityControllerInterface interface, or, most commonly, extend the - * DrupalDefaultEntityController class. See node_entity_info() and the - * NodeController in node.module as an example. - * - * @see hook_entity_info() - * @see DrupalEntityControllerInterface - * @see DrupalDefaultEntityController - * @see EntityFieldQuery - * - * @param $entity_type - * The entity type to load, e.g. node or user. - * @param $ids - * An array of entity IDs, or FALSE to load all entities. - * @param $conditions - * (deprecated) An associative array of conditions on the base table, where - * the keys are the database fields and the values are the values those - * fields must have. Instead, it is preferable to use EntityFieldQuery to - * retrieve a list of entity IDs loadable by this function. - * @param $reset - * Whether to reset the internal cache for the requested entity type. - * - * @return - * An array of entity objects indexed by their ids. When no results are - * found, an empty array is returned. - * - * @todo Remove $conditions in Drupal 8. - */ -function entity_load($entity_type, $ids = FALSE, $conditions = array(), $reset = FALSE) { - if ($reset) { - entity_get_controller($entity_type)->resetCache(); - } - return entity_get_controller($entity_type)->load($ids, $conditions); -} - -/** - * Loads the unchanged, i.e. not modified, entity from the database. - * - * Unlike entity_load() this function ensures the entity is directly loaded from - * the database, thus bypassing any static cache. In particular, this function - * is useful to determine changes by comparing the entity being saved to the - * stored entity. - * - * @param $entity_type - * The entity type to load, e.g. node or user. - * @param $id - * The id of the entity to load. - * - * @return - * The unchanged entity, or FALSE if the entity cannot be loaded. - */ -function entity_load_unchanged($entity_type, $id) { - entity_get_controller($entity_type)->resetCache(array($id)); - $result = entity_get_controller($entity_type)->load(array($id)); - return reset($result); -} - -/** - * Get the entity controller class for an entity type. - */ -function entity_get_controller($entity_type) { - $controllers = &drupal_static(__FUNCTION__, array()); - if (!isset($controllers[$entity_type])) { - $type_info = entity_get_info($entity_type); - $class = $type_info['controller class']; - $controllers[$entity_type] = new $class($entity_type); - } - return $controllers[$entity_type]; -} - -/** - * Invoke hook_entity_prepare_view(). - * - * If adding a new entity similar to nodes, comments or users, you should - * invoke this function during the ENTITY_build_content() or - * ENTITY_view_multiple() phases of rendering to allow other modules to alter - * the objects during this phase. This is needed for situations where - * information needs to be loaded outside of ENTITY_load() - particularly - * when loading entities into one another - i.e. a user object into a node, due - * to the potential for unwanted side-effects such as caching and infinite - * recursion. By convention, entity_prepare_view() is called after - * field_attach_prepare_view() to allow entity level hooks to act on content - * loaded by field API. - * @see hook_entity_prepare_view() - * - * @param $entity_type - * The type of entity, i.e. 'node', 'user'. - * @param $entities - * The entity objects which are being prepared for view, keyed by object ID. - * @param $langcode - * (optional) A language code to be used for rendering. Defaults to the global - * content language of the current request. - */ -function entity_prepare_view($entity_type, $entities, $langcode = NULL) { - if (!isset($langcode)) { - $langcode = $GLOBALS['language_content']->language; - } - - // To ensure hooks are only run once per entity, check for an - // entity_view_prepared flag and only process items without it. - // @todo: resolve this more generally for both entity and field level hooks. - $prepare = array(); - foreach ($entities as $id => $entity) { - if (empty($entity->entity_view_prepared)) { - // Add this entity to the items to be prepared. - $prepare[$id] = $entity; - - // Mark this item as prepared. - $entity->entity_view_prepared = TRUE; - } - } - - if (!empty($prepare)) { - module_invoke_all('entity_prepare_view', $prepare, $entity_type, $langcode); - } -} - -/** - * Returns the uri elements of an entity. - * - * @param $entity_type - * The entity type; e.g. 'node' or 'user'. - * @param $entity - * The entity for which to generate a path. - * @return - * An array containing the 'path' and 'options' keys used to build the uri of - * the entity, and matching the signature of url(). NULL if the entity has no - * uri of its own. - */ -function entity_uri($entity_type, $entity) { - $info = entity_get_info($entity_type); - list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity); - - // A bundle-specific callback takes precedence over the generic one for the - // entity type. - if (isset($info['bundles'][$bundle]['uri callback'])) { - $uri_callback = $info['bundles'][$bundle]['uri callback']; - } - elseif (isset($info['uri callback'])) { - $uri_callback = $info['uri callback']; - } - else { - return NULL; - } - - // Invoke the callback to get the URI. If there is no callback, return NULL. - if (isset($uri_callback) && function_exists($uri_callback)) { - $uri = $uri_callback($entity); - // Pass the entity data to url() so that alter functions do not need to - // lookup this entity again. - $uri['options']['entity_type'] = $entity_type; - $uri['options']['entity'] = $entity; - return $uri; - } -} - -/** - * Returns the label of an entity. - * - * See the 'label callback' component of the hook_entity_info() return value - * for more information. - * - * @param $entity_type - * The entity type; e.g., 'node' or 'user'. - * @param $entity - * The entity for which to generate the label. - * - * @return - * The entity label, or FALSE if not found. - */ -function entity_label($entity_type, $entity) { - $label = FALSE; - $info = entity_get_info($entity_type); - if (isset($info['label callback']) && function_exists($info['label callback'])) { - $label = $info['label callback']($entity_type, $entity); - } - elseif (!empty($info['entity keys']['label']) && isset($entity->{$info['entity keys']['label']})) { - $label = $entity->{$info['entity keys']['label']}; - } - - return $label; -} - -/** - * Helper function for attaching field API validation to entity forms. - */ -function entity_form_field_validate($entity_type, $form, &$form_state) { - // All field attach API functions act on an entity object, but during form - // validation, we don't have one. $form_state contains the entity as it was - // prior to processing the current form submission, and we must not update it - // until we have fully validated the submitted input. Therefore, for - // validation, act on a pseudo entity created out of the form values. - $pseudo_entity = (object) $form_state['values']; - field_attach_form_validate($entity_type, $pseudo_entity, $form, $form_state); -} - -/** - * Helper function for copying submitted values to entity properties for simple entity forms. - * - * During the submission handling of an entity form's "Save", "Preview", and - * possibly other buttons, the form state's entity needs to be updated with the - * submitted form values. Each entity form implements its own builder function - * for doing this, appropriate for the particular entity and form, whereas - * modules may specify additional builder functions in $form['#entity_builders'] - * for copying the form values of added form elements to entity properties. - * Many of the main entity builder functions can call this helper function to - * re-use its logic of copying $form_state['values'][PROPERTY] values to - * $entity->PROPERTY for all entries in $form_state['values'] that are not field - * data, and calling field_attach_submit() to copy field data. Apart from that - * this helper invokes any additional builder functions that have been specified - * in $form['#entity_builders']. - * - * For some entity forms (e.g., forms with complex non-field data and forms that - * simultaneously edit multiple entities), this behavior may be inappropriate, - * so the builder function for such forms needs to implement the required - * functionality instead of calling this function. - */ -function entity_form_submit_build_entity($entity_type, $entity, $form, &$form_state) { - $info = entity_get_info($entity_type); - list(, , $bundle) = entity_extract_ids($entity_type, $entity); - - // Copy top-level form values that are not for fields to entity properties, - // without changing existing entity properties that are not being edited by - // this form. Copying field values must be done using field_attach_submit(). - $values_excluding_fields = $info['fieldable'] ? array_diff_key($form_state['values'], field_info_instances($entity_type, $bundle)) : $form_state['values']; - foreach ($values_excluding_fields as $key => $value) { - $entity->$key = $value; - } - - // Invoke all specified builders for copying form values to entity properties. - if (isset($form['#entity_builders'])) { - foreach ($form['#entity_builders'] as $function) { - $function($entity_type, $entity, $form, $form_state); - } - } - - // Copy field values to the entity. - if ($info['fieldable']) { - field_attach_submit($entity_type, $entity, $form, $form_state); - } -} - /** * Performs one or more XML-RPC request(s). * diff --git a/includes/install.core.inc b/includes/install.core.inc index 1040bf3e550d..3791d71b56ce 100644 --- a/includes/install.core.inc +++ b/includes/install.core.inc @@ -253,12 +253,13 @@ function install_begin_request(&$install_state) { // Set up $language, so t() caller functions will still work. drupal_language_initialize(); - include_once DRUPAL_ROOT . '/includes/entity.inc'; require_once DRUPAL_ROOT . '/includes/ajax.inc'; $module_list['system']['filename'] = 'modules/system/system.module'; - $module_list['user']['filename'] = 'modules/user/user.module'; + $module_list['entity']['filename'] = 'modules/entity/entity.module'; + $module_list['user']['filename'] = 'modules/user/user.module'; module_list(TRUE, FALSE, FALSE, $module_list); drupal_load('module', 'system'); + drupal_load('module', 'entity'); drupal_load('module', 'user'); // Load the cache infrastructure using a "fake" cache implementation that diff --git a/includes/module.inc b/includes/module.inc index cc3aa8eecb14..77fc98ac87e2 100644 --- a/includes/module.inc +++ b/includes/module.inc @@ -424,8 +424,9 @@ function module_enable($module_list, $enable_dependencies = TRUE) { registry_update(); // Refresh the schema to include it. drupal_get_schema(NULL, TRUE); - // Clear entity cache. - entity_info_cache_clear(); + + // Allow modules to react prior to the installation of a module. + module_invoke_all('modules_preinstall', array($module)); // Now install the module if necessary. if (drupal_get_installed_schema_version($module, TRUE) == SCHEMA_UNINSTALLED) { @@ -450,6 +451,9 @@ function module_enable($module_list, $enable_dependencies = TRUE) { watchdog('system', '%module module installed.', array('%module' => $module), WATCHDOG_INFO); } + // Allow modules to react prior to the enabling of a module. + module_invoke_all('modules_preenable', array($module)); + // Enable the module. module_invoke($module, 'enable'); diff --git a/includes/update.inc b/includes/update.inc index cbee34e0dc0e..416d2033a09e 100644 --- a/includes/update.inc +++ b/includes/update.inc @@ -82,6 +82,7 @@ function update_prepare_d8_bootstrap() { // Allow the database system to work even if the registry has not been // created yet. include_once DRUPAL_ROOT . '/includes/install.inc'; + include_once DRUPAL_ROOT . '/modules/entity/entity.controller.inc'; drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE); // If the site has not updated to Drupal 8 yet, check to make sure that it is @@ -120,6 +121,35 @@ function update_fix_d8_requirements() { } } +/** + * Helper function to install a new module in Drupal 8 via hook_update_N(). + */ +function update_module_enable(array $modules) { + foreach ($modules as $module) { + // Check for initial schema and install it. The schema version of a newly + // installed module is always 0. Using 8000 here would be inconsistent + // since $module_update_8000() may involve a schema change, and we want + // to install the schema as it was before any updates were added. + $function = $module . '_schema_0'; + if (function_exists($function)) { + $schema = $function(); + foreach ($schema as $table => $spec) { + db_create_table($table, $spec); + } + } + // Change the schema version from SCHEMA_UNINSTALLED to 0, so any module + // updates since the module's inception are executed in a core upgrade. + db_update('system') + ->condition('type', 'module') + ->condition('name', $module) + ->fields(array('schema_version' => 0)) + ->execute(); + + system_list_reset(); + // @todo: figure out what to do about hook_install() and hook_enable(). + } +} + /** * Perform one update and store the results for display on finished page. * diff --git a/modules/comment/comment.info b/modules/comment/comment.info index a5837af83ab1..949ffc2a830b 100644 --- a/modules/comment/comment.info +++ b/modules/comment/comment.info @@ -4,6 +4,7 @@ package = Core version = VERSION core = 8.x dependencies[] = text +dependencies[] = entity files[] = comment.module files[] = comment.test configure = admin/content/comment diff --git a/modules/entity/entity.api.php b/modules/entity/entity.api.php new file mode 100644 index 000000000000..9b19477163bf --- /dev/null +++ b/modules/entity/entity.api.php @@ -0,0 +1,414 @@ +subject, then + * 'subject' should be specified here. If complex logic is required to + * build the label, a 'label callback' should be defined instead (see + * the 'label callback' section above for details). + * - bundle keys: An array describing how the Field API can extract the + * information it needs from the bundle objects for this type (e.g + * $vocabulary objects for terms; not applicable for nodes). This entry can + * be omitted if this type's bundles do not exist as standalone objects. + * Elements: + * - bundle: The name of the property that contains the name of the bundle + * object. + * - bundles: An array describing all bundles for this object type. Keys are + * bundles machine names, as found in the objects' 'bundle' property + * (defined in the 'entity keys' entry above). Elements: + * - label: The human-readable name of the bundle. + * - uri callback: Same as the 'uri callback' key documented above for the + * entity type, but for the bundle only. When determining the URI of an + * entity, if a 'uri callback' is defined for both the entity type and + * the bundle, the one for the bundle is used. + * - admin: An array of information that allows Field UI pages to attach + * themselves to the existing administration pages for the bundle. + * Elements: + * - path: the path of the bundle's main administration page, as defined + * in hook_menu(). If the path includes a placeholder for the bundle, + * the 'bundle argument', 'bundle helper' and 'real path' keys below + * are required. + * - bundle argument: The position of the placeholder in 'path', if any. + * - real path: The actual path (no placeholder) of the bundle's main + * administration page. This will be used to generate links. + * - access callback: As in hook_menu(). 'user_access' will be assumed if + * no value is provided. + * - access arguments: As in hook_menu(). + * - view modes: An array describing the view modes for the entity type. View + * modes let entities be displayed differently depending on the context. + * For instance, a node can be displayed differently on its own page + * ('full' mode), on the home page or taxonomy listings ('teaser' mode), or + * in an RSS feed ('rss' mode). Modules taking part in the display of the + * entity (notably the Field API) can adjust their behavior depending on + * the requested view mode. An additional 'default' view mode is available + * for all entity types. This view mode is not intended for actual entity + * display, but holds default display settings. For each available view + * mode, administrators can configure whether it should use its own set of + * field display settings, or just replicate the settings of the 'default' + * view mode, thus reducing the amount of display configurations to keep + * track of. Keys of the array are view mode names. Each view mode is + * described by an array with the following key/value pairs: + * - label: The human-readable name of the view mode + * - custom settings: A boolean specifying whether the view mode should by + * default use its own custom field display settings. If FALSE, entities + * displayed in this view mode will reuse the 'default' display settings + * by default (e.g. right after the module exposing the view mode is + * enabled), but administrators can later use the Field UI to apply custom + * display settings specific to the view mode. + * + * @see entity_load() + * @see hook_entity_info_alter() + */ +function hook_entity_info() { + $return = array( + 'node' => array( + 'label' => t('Node'), + 'controller class' => 'NodeController', + 'base table' => 'node', + 'revision table' => 'node_revision', + 'uri callback' => 'node_uri', + 'fieldable' => TRUE, + 'translation' => array( + 'locale' => TRUE, + ), + 'entity keys' => array( + 'id' => 'nid', + 'revision' => 'vid', + 'bundle' => 'type', + ), + 'bundle keys' => array( + 'bundle' => 'type', + ), + 'bundles' => array(), + 'view modes' => array( + 'full' => array( + 'label' => t('Full content'), + 'custom settings' => FALSE, + ), + 'teaser' => array( + 'label' => t('Teaser'), + 'custom settings' => TRUE, + ), + 'rss' => array( + 'label' => t('RSS'), + 'custom settings' => FALSE, + ), + ), + ), + ); + + // Search integration is provided by node.module, so search-related + // view modes for nodes are defined here and not in search.module. + if (module_exists('search')) { + $return['node']['view modes'] += array( + 'search_index' => array( + 'label' => t('Search index'), + 'custom settings' => FALSE, + ), + 'search_result' => array( + 'label' => t('Search result'), + 'custom settings' => FALSE, + ), + ); + } + + // Bundles must provide a human readable name so we can create help and error + // messages, and the path to attach Field admin pages to. + foreach (node_type_get_names() as $type => $name) { + $return['node']['bundles'][$type] = array( + 'label' => $name, + 'admin' => array( + 'path' => 'admin/structure/types/manage/%node_type', + 'real path' => 'admin/structure/types/manage/' . str_replace('_', '-', $type), + 'bundle argument' => 4, + 'access arguments' => array('administer content types'), + ), + ); + } + + return $return; +} + +/** + * Alter the entity info. + * + * Modules may implement this hook to alter the information that defines an + * entity. All properties that are available in hook_entity_info() can be + * altered here. + * + * @param $entity_info + * The entity info array, keyed by entity name. + * + * @see hook_entity_info() + */ +function hook_entity_info_alter(&$entity_info) { + // Set the controller class for nodes to an alternate implementation of the + // DrupalEntityController interface. + $entity_info['node']['controller class'] = 'MyCustomNodeController'; +} + +/** + * Act on entities when loaded. + * + * This is a generic load hook called for all entity types loaded via the + * entity API. + * + * @param $entities + * The entities keyed by entity ID. + * @param $type + * The type of entities being loaded (i.e. node, user, comment). + */ +function hook_entity_load($entities, $type) { + foreach ($entities as $entity) { + $entity->foo = mymodule_add_something($entity, $type); + } +} + +/** + * Act on an entity before it is about to be created or updated. + * + * @param $entity + * The entity object. + * @param $type + * The type of entity being saved (i.e. node, user, comment). + */ +function hook_entity_presave($entity, $type) { + $entity->changed = REQUEST_TIME; +} + +/** + * Act on entities when inserted. + * + * @param $entity + * The entity object. + * @param $type + * The type of entity being inserted (i.e. node, user, comment). + */ +function hook_entity_insert($entity, $type) { + // Insert the new entity into a fictional table of all entities. + $info = entity_get_info($type); + list($id) = entity_extract_ids($type, $entity); + db_insert('example_entity') + ->fields(array( + 'type' => $type, + 'id' => $id, + 'created' => REQUEST_TIME, + 'updated' => REQUEST_TIME, + )) + ->execute(); +} + +/** + * Act on entities when updated. + * + * @param $entity + * The entity object. + * @param $type + * The type of entity being updated (i.e. node, user, comment). + */ +function hook_entity_update($entity, $type) { + // Update the entity's entry in a fictional table of all entities. + $info = entity_get_info($type); + list($id) = entity_extract_ids($type, $entity); + db_update('example_entity') + ->fields(array( + 'updated' => REQUEST_TIME, + )) + ->condition('type', $type) + ->condition('id', $id) + ->execute(); +} + +/** + * Act on entities when deleted. + * + * @param $entity + * The entity object. + * @param $type + * The type of entity being deleted (i.e. node, user, comment). + */ +function hook_entity_delete($entity, $type) { + // Delete the entity's entry from a fictional table of all entities. + $info = entity_get_info($type); + list($id) = entity_extract_ids($type, $entity); + db_delete('example_entity') + ->condition('type', $type) + ->condition('id', $id) + ->execute(); +} + +/** + * Alter or execute an EntityFieldQuery. + * + * @param EntityFieldQuery $query + * An EntityFieldQuery. One of the most important properties to be changed is + * EntityFieldQuery::executeCallback. If this is set to an existing function, + * this function will get the query as its single argument and its result + * will be the returned as the result of EntityFieldQuery::execute(). This can + * be used to change the behavior of EntityFieldQuery entirely. For example, + * the default implementation can only deal with one field storage engine, but + * it is possible to write a module that can query across field storage + * engines. Also, the default implementation presumes entities are stored in + * SQL, but the execute callback could instead query any other entity storage, + * local or remote. + * + * Note the $query->altered attribute which is TRUE in case the query has + * already been altered once. This happens with cloned queries. + * If there is a pager, then such a cloned query will be executed to count + * all elements. This query can be detected by checking for + * ($query->pager && $query->count), allowing the driver to return 0 from + * the count query and disable the pager. + */ +function hook_entity_query_alter($query) { + $query->executeCallback = 'my_module_query_callback'; +} + +/** + * Act on entities being assembled before rendering. + * + * @param $entity + * The entity object. + * @param $type + * The type of entity being rendered (i.e. node, user, comment). + * @param $view_mode + * The view mode the entity is rendered in. + * @param $langcode + * The language code used for rendering. + * + * The module may add elements to $entity->content prior to rendering. The + * structure of $entity->content is a renderable array as expected by + * drupal_render(). + * + * @see hook_entity_view_alter() + * @see hook_comment_view() + * @see hook_node_view() + * @see hook_user_view() + */ +function hook_entity_view($entity, $type, $view_mode, $langcode) { + $entity->content['my_additional_field'] = array( + '#markup' => $additional_field, + '#weight' => 10, + '#theme' => 'mymodule_my_additional_field', + ); +} + +/** + * Alter the results of ENTITY_view(). + * + * This hook is called after the content has been assembled in a structured + * array and may be used for doing processing which requires that the complete + * entity content structure has been built. + * + * If a module wishes to act on the rendered HTML of the entity rather than the + * structured content array, it may use this hook to add a #post_render + * callback. Alternatively, it could also implement hook_preprocess_ENTITY(). + * See drupal_render() and theme() for details. + * + * @param $build + * A renderable array representing the entity content. + * @param $type + * The type of entity being rendered (i.e. node, user, comment). + * + * @see hook_entity_view() + * @see hook_comment_view_alter() + * @see hook_node_view_alter() + * @see hook_taxonomy_term_view_alter() + * @see hook_user_view_alter() + */ +function hook_entity_view_alter(&$build, $type) { + if ($build['#view_mode'] == 'full' && isset($build['an_additional_field'])) { + // Change its weight. + $build['an_additional_field']['#weight'] = -10; + + // Add a #post_render callback to act on the rendered HTML of the entity. + $build['#post_render'][] = 'my_module_node_post_render'; + } +} + +/** + * Act on entities as they are being prepared for view. + * + * Allows you to operate on multiple entities as they are being prepared for + * view. Only use this if attaching the data during the entity_load() phase + * is not appropriate, for example when attaching other 'entity' style objects. + * + * @param $entities + * The entities keyed by entity ID. + * @param $type + * The type of entities being loaded (i.e. node, user, comment). + */ +function hook_entity_prepare_view($entities, $type) { + // Load a specific node into the user object for later theming. + if ($type == 'user') { + $nodes = mymodule_get_user_nodes(array_keys($entities)); + foreach ($entities as $uid => $entity) { + $entity->user_node = $nodes[$uid]; + } + } +} diff --git a/modules/entity/entity.controller.inc b/modules/entity/entity.controller.inc new file mode 100644 index 000000000000..8327bc6d0682 --- /dev/null +++ b/modules/entity/entity.controller.inc @@ -0,0 +1,390 @@ + $value. + * + * @return + * An array of entity objects indexed by their ids. + */ + public function load($ids = array(), $conditions = array()); +} + +/** + * Default implementation of DrupalEntityControllerInterface. + * + * This class can be used as-is by most simple entity types. Entity types + * requiring special handling can extend the class. + */ +class DrupalDefaultEntityController implements DrupalEntityControllerInterface { + + /** + * Static cache of entities. + * + * @var array + */ + protected $entityCache; + + /** + * Entity type for this controller instance. + * + * @var string + */ + protected $entityType; + + /** + * Array of information about the entity. + * + * @var array + * + * @see entity_get_info() + */ + protected $entityInfo; + + /** + * Additional arguments to pass to hook_TYPE_load(). + * + * Set before calling DrupalDefaultEntityController::attachLoad(). + * + * @var array + */ + protected $hookLoadArguments; + + /** + * Name of the entity's ID field in the entity database table. + * + * @var string + */ + protected $idKey; + + /** + * Name of entity's revision database table field, if it supports revisions. + * + * Has the value FALSE if this entity does not use revisions. + * + * @var string + */ + protected $revisionKey; + + /** + * The table that stores revisions, if the entity supports revisions. + * + * @var string + */ + protected $revisionTable; + + /** + * Whether this entity type should use the static cache. + * + * Set by entity info. + * + * @var boolean + */ + protected $cache; + + /** + * Constructor: sets basic variables. + */ + public function __construct($entityType) { + $this->entityType = $entityType; + $this->entityInfo = entity_get_info($entityType); + $this->entityCache = array(); + $this->hookLoadArguments = array(); + $this->idKey = $this->entityInfo['entity keys']['id']; + + // Check if the entity type supports revisions. + if (!empty($this->entityInfo['entity keys']['revision'])) { + $this->revisionKey = $this->entityInfo['entity keys']['revision']; + $this->revisionTable = $this->entityInfo['revision table']; + } + else { + $this->revisionKey = FALSE; + } + + // Check if the entity type supports static caching of loaded entities. + $this->cache = !empty($this->entityInfo['static cache']); + } + + /** + * Implements DrupalEntityControllerInterface::resetCache(). + */ + public function resetCache(array $ids = NULL) { + if (isset($ids)) { + foreach ($ids as $id) { + unset($this->entityCache[$id]); + } + } + else { + $this->entityCache = array(); + } + } + + /** + * Implements DrupalEntityControllerInterface::load(). + */ + public function load($ids = array(), $conditions = array()) { + $entities = array(); + + // Revisions are not statically cached, and require a different query to + // other conditions, so separate the revision id into its own variable. + if ($this->revisionKey && isset($conditions[$this->revisionKey])) { + $revision_id = $conditions[$this->revisionKey]; + unset($conditions[$this->revisionKey]); + } + else { + $revision_id = FALSE; + } + + // Create a new variable which is either a prepared version of the $ids + // array for later comparison with the entity cache, or FALSE if no $ids + // were passed. The $ids array is reduced as items are loaded from cache, + // and we need to know if it's empty for this reason to avoid querying the + // database when all requested entities are loaded from cache. + $passed_ids = !empty($ids) ? array_flip($ids) : FALSE; + // Try to load entities from the static cache, if the entity type supports + // static caching. + if ($this->cache && !$revision_id) { + $entities += $this->cacheGet($ids, $conditions); + // If any entities were loaded, remove them from the ids still to load. + if ($passed_ids) { + $ids = array_keys(array_diff_key($passed_ids, $entities)); + } + } + + // Load any remaining entities from the database. This is the case if $ids + // is set to FALSE (so we load all entities), if there are any ids left to + // load, if loading a revision, or if $conditions was passed without $ids. + if ($ids === FALSE || $ids || $revision_id || ($conditions && !$passed_ids)) { + // Build the query. + $query = $this->buildQuery($ids, $conditions, $revision_id); + $queried_entities = $query + ->execute() + ->fetchAllAssoc($this->idKey); + } + + // Pass all entities loaded from the database through $this->attachLoad(), + // which attaches fields (if supported by the entity type) and calls the + // entity type specific load callback, for example hook_node_load(). + if (!empty($queried_entities)) { + $this->attachLoad($queried_entities, $revision_id); + $entities += $queried_entities; + } + + if ($this->cache) { + // Add entities to the cache if we are not loading a revision. + if (!empty($queried_entities) && !$revision_id) { + $this->cacheSet($queried_entities); + } + } + + // Ensure that the returned array is ordered the same as the original + // $ids array if this was passed in and remove any invalid ids. + if ($passed_ids) { + // Remove any invalid ids from the array. + $passed_ids = array_intersect_key($passed_ids, $entities); + foreach ($entities as $entity) { + $passed_ids[$entity->{$this->idKey}] = $entity; + } + $entities = $passed_ids; + } + + return $entities; + } + + /** + * Builds the query to load the entity. + * + * This has full revision support. For entities requiring special queries, + * the class can be extended, and the default query can be constructed by + * calling parent::buildQuery(). This is usually necessary when the object + * being loaded needs to be augmented with additional data from another + * table, such as loading node type into comments or vocabulary machine name + * into terms, however it can also support $conditions on different tables. + * See CommentController::buildQuery() or TaxonomyTermController::buildQuery() + * for examples. + * + * @param $ids + * An array of entity IDs, or FALSE to load all entities. + * @param $conditions + * An array of conditions in the form 'field' => $value. + * @param $revision_id + * The ID of the revision to load, or FALSE if this query is asking for the + * most current revision(s). + * + * @return SelectQuery + * A SelectQuery object for loading the entity. + */ + protected function buildQuery($ids, $conditions = array(), $revision_id = FALSE) { + $query = db_select($this->entityInfo['base table'], 'base'); + + $query->addTag($this->entityType . '_load_multiple'); + + if ($revision_id) { + $query->join($this->revisionTable, 'revision', "revision.{$this->idKey} = base.{$this->idKey} AND revision.{$this->revisionKey} = :revisionId", array(':revisionId' => $revision_id)); + } + elseif ($this->revisionKey) { + $query->join($this->revisionTable, 'revision', "revision.{$this->revisionKey} = base.{$this->revisionKey}"); + } + + // Add fields from the {entity} table. + $entity_fields = $this->entityInfo['schema_fields_sql']['base table']; + + if ($this->revisionKey) { + // Add all fields from the {entity_revision} table. + $entity_revision_fields = drupal_map_assoc($this->entityInfo['schema_fields_sql']['revision table']); + // The id field is provided by entity, so remove it. + unset($entity_revision_fields[$this->idKey]); + + // Remove all fields from the base table that are also fields by the same + // name in the revision table. + $entity_field_keys = array_flip($entity_fields); + foreach ($entity_revision_fields as $key => $name) { + if (isset($entity_field_keys[$name])) { + unset($entity_fields[$entity_field_keys[$name]]); + } + } + $query->fields('revision', $entity_revision_fields); + } + + $query->fields('base', $entity_fields); + + if ($ids) { + $query->condition("base.{$this->idKey}", $ids, 'IN'); + } + if ($conditions) { + foreach ($conditions as $field => $value) { + $query->condition('base.' . $field, $value); + } + } + return $query; + } + + /** + * Attaches data to entities upon loading. + * + * This will attach fields, if the entity is fieldable. It calls + * hook_entity_load() for modules which need to add data to all entities. + * It also calls hook_TYPE_load() on the loaded entities. For example + * hook_node_load() or hook_user_load(). If your hook_TYPE_load() + * expects special parameters apart from the queried entities, you can set + * $this->hookLoadArguments prior to calling the method. + * See NodeController::attachLoad() for an example. + * + * @param $queried_entities + * Associative array of query results, keyed on the entity ID. + * @param $revision_id + * ID of the revision that was loaded, or FALSE if teh most current revision + * was loaded. + */ + protected function attachLoad(&$queried_entities, $revision_id = FALSE) { + // Attach fields. + if ($this->entityInfo['fieldable']) { + if ($revision_id) { + field_attach_load_revision($this->entityType, $queried_entities); + } + else { + field_attach_load($this->entityType, $queried_entities); + } + } + + // Call hook_entity_load(). + foreach (module_implements('entity_load') as $module) { + $function = $module . '_entity_load'; + $function($queried_entities, $this->entityType); + } + // Call hook_TYPE_load(). The first argument for hook_TYPE_load() are + // always the queried entities, followed by additional arguments set in + // $this->hookLoadArguments. + $args = array_merge(array($queried_entities), $this->hookLoadArguments); + foreach (module_implements($this->entityInfo['load hook']) as $module) { + call_user_func_array($module . '_' . $this->entityInfo['load hook'], $args); + } + } + + /** + * Gets entities from the static cache. + * + * @param $ids + * If not empty, return entities that match these IDs. + * @param $conditions + * If set, return entities that match all of these conditions. + * + * @return + * Array of entities from the entity cache. + */ + protected function cacheGet($ids, $conditions = array()) { + $entities = array(); + // Load any available entities from the internal cache. + if (!empty($this->entityCache)) { + if ($ids) { + $entities += array_intersect_key($this->entityCache, array_flip($ids)); + } + // If loading entities only by conditions, fetch all available entities + // from the cache. Entities which don't match are removed later. + elseif ($conditions) { + $entities = $this->entityCache; + } + } + + // Exclude any entities loaded from cache if they don't match $conditions. + // This ensures the same behavior whether loading from memory or database. + if ($conditions) { + foreach ($entities as $entity) { + $entity_values = (array) $entity; + if (array_diff_assoc($conditions, $entity_values)) { + unset($entities[$entity->{$this->idKey}]); + } + } + } + return $entities; + } + + /** + * Stores entities in the static entity cache. + * + * @param $entities + * Entities to store in the cache. + */ + protected function cacheSet($entities) { + $this->entityCache += $entities; + } +} diff --git a/modules/entity/entity.info b/modules/entity/entity.info new file mode 100644 index 000000000000..31eb720d0a7c --- /dev/null +++ b/modules/entity/entity.info @@ -0,0 +1,8 @@ +name = Entity +description = API for managing entities like nodes and users. +package = Core +version = VERSION +core = 8.x +required = TRUE +files[] = entity.query.inc +files[] = entity.controller.inc diff --git a/modules/entity/entity.module b/modules/entity/entity.module new file mode 100644 index 000000000000..02611e3c4910 --- /dev/null +++ b/modules/entity/entity.module @@ -0,0 +1,442 @@ +' . t('About') . ''; + $output .= '

' . t('The Entity module provides an API for managing entities like nodes and users, i.e. an API for loading and identifying entities. For more information, see the online handbook entry for Entity module', array('!url' => 'http://drupal.org/handbook/modules/entity')) . '

'; + return $output; + } +} + +/** + * Implements hook_modules_preenable(). + */ +function entity_modules_preenable() { + entity_info_cache_clear(); +} + +/** + * Implements hook_modules_disabled(). + */ +function entity_modules_disabled() { + entity_info_cache_clear(); +} + +/** + * Gets the entity info array of an entity type. + * + * @see hook_entity_info() + * @see hook_entity_info_alter() + * + * @param $entity_type + * The entity type, e.g. node, for which the info shall be returned, or NULL + * to return an array with info about all types. + */ +function entity_get_info($entity_type = NULL) { + global $language; + + // Use the advanced drupal_static() pattern, since this is called very often. + static $drupal_static_fast; + if (!isset($drupal_static_fast)) { + $drupal_static_fast['entity_info'] = &drupal_static(__FUNCTION__); + } + $entity_info = &$drupal_static_fast['entity_info']; + + // hook_entity_info() includes translated strings, so each language is cached + // separately. + $langcode = $language->language; + + if (empty($entity_info)) { + if ($cache = cache()->get("entity_info:$langcode")) { + $entity_info = $cache->data; + } + else { + $entity_info = module_invoke_all('entity_info'); + // Merge in default values. + foreach ($entity_info as $name => $data) { + $entity_info[$name] += array( + 'fieldable' => FALSE, + 'controller class' => 'DrupalDefaultEntityController', + 'static cache' => TRUE, + 'field cache' => TRUE, + 'load hook' => $name . '_load', + 'bundles' => array(), + 'view modes' => array(), + 'entity keys' => array(), + 'translation' => array(), + ); + $entity_info[$name]['entity keys'] += array( + 'revision' => '', + 'bundle' => '', + ); + foreach ($entity_info[$name]['view modes'] as $view_mode => $view_mode_info) { + $entity_info[$name]['view modes'][$view_mode] += array( + 'custom settings' => FALSE, + ); + } + // If no bundle key is provided, assume a single bundle, named after + // the entity type. + if (empty($entity_info[$name]['entity keys']['bundle']) && empty($entity_info[$name]['bundles'])) { + $entity_info[$name]['bundles'] = array($name => array('label' => $entity_info[$name]['label'])); + } + // Prepare entity schema fields SQL info for + // DrupalEntityControllerInterface::buildQuery(). + if (isset($entity_info[$name]['base table'])) { + $entity_info[$name]['schema_fields_sql']['base table'] = drupal_schema_fields_sql($entity_info[$name]['base table']); + if (isset($entity_info[$name]['revision table'])) { + $entity_info[$name]['schema_fields_sql']['revision table'] = drupal_schema_fields_sql($entity_info[$name]['revision table']); + } + } + } + // Let other modules alter the entity info. + drupal_alter('entity_info', $entity_info); + cache()->set("entity_info:$langcode", $entity_info); + } + } + + if (empty($entity_type)) { + return $entity_info; + } + elseif (isset($entity_info[$entity_type])) { + return $entity_info[$entity_type]; + } +} + +/** + * Resets the cached information about entity types. + */ +function entity_info_cache_clear() { + drupal_static_reset('entity_get_info'); + // Clear all languages. + cache()->deletePrefix('entity_info:'); +} + +/** + * Helper function to extract id, vid, and bundle name from an entity. + * + * @param $entity_type + * The entity type; e.g. 'node' or 'user'. + * @param $entity + * The entity from which to extract values. + * @return + * A numerically indexed array (not a hash table) containing these + * elements: + * 0: primary id of the entity + * 1: revision id of the entity, or NULL if $entity_type is not versioned + * 2: bundle name of the entity + */ +function entity_extract_ids($entity_type, $entity) { + $info = entity_get_info($entity_type); + + // Objects being created might not have id/vid yet. + $id = isset($entity->{$info['entity keys']['id']}) ? $entity->{$info['entity keys']['id']} : NULL; + $vid = ($info['entity keys']['revision'] && isset($entity->{$info['entity keys']['revision']})) ? $entity->{$info['entity keys']['revision']} : NULL; + + if (!empty($info['entity keys']['bundle'])) { + // Explicitly fail for malformed entities missing the bundle property. + if (!isset($entity->{$info['entity keys']['bundle']}) || $entity->{$info['entity keys']['bundle']} === '') { + throw new EntityMalformedException(t('Missing bundle property on entity of type @entity_type.', array('@entity_type' => $entity_type))); + } + $bundle = $entity->{$info['entity keys']['bundle']}; + } + else { + // The entity type provides no bundle key: assume a single bundle, named + // after the entity type. + $bundle = $entity_type; + } + + return array($id, $vid, $bundle); +} + +/** + * Helper function to assemble an object structure with initial ids. + * + * This function can be seen as reciprocal to entity_extract_ids(). + * + * @param $entity_type + * The entity type; e.g. 'node' or 'user'. + * @param $ids + * A numerically indexed array, as returned by entity_extract_ids(), + * containing these elements: + * 0: primary id of the entity + * 1: revision id of the entity, or NULL if $entity_type is not versioned + * 2: bundle name of the entity, or NULL if $entity_type has no bundles + * + * @return + * An entity structure, initialized with the ids provided. + */ +function entity_create_stub_entity($entity_type, $ids) { + $entity = new stdClass(); + $info = entity_get_info($entity_type); + $entity->{$info['entity keys']['id']} = $ids[0]; + if (!empty($info['entity keys']['revision']) && isset($ids[1])) { + $entity->{$info['entity keys']['revision']} = $ids[1]; + } + if (!empty($info['entity keys']['bundle']) && isset($ids[2])) { + $entity->{$info['entity keys']['bundle']} = $ids[2]; + } + return $entity; +} + +/** + * Loads entities from the database. + * + * This function should be used whenever you need to load more than one entity + * from the database. The entities are loaded into memory and will not require + * database access if loaded again during the same page request. + * + * The actual loading is done through a class that has to implement the + * DrupalEntityControllerInterface interface. By default, + * DrupalDefaultEntityController is used. Entity types can specify that a + * different class should be used by setting the 'controller class' key in + * hook_entity_info(). These classes can either implement the + * DrupalEntityControllerInterface interface, or, most commonly, extend the + * DrupalDefaultEntityController class. See node_entity_info() and the + * NodeController in node.module as an example. + * + * @see hook_entity_info() + * @see DrupalEntityControllerInterface + * @see DrupalDefaultEntityController + * @see EntityFieldQuery + * + * @param $entity_type + * The entity type to load, e.g. node or user. + * @param $ids + * An array of entity IDs, or FALSE to load all entities. + * @param $conditions + * (deprecated) An associative array of conditions on the base table, where + * the keys are the database fields and the values are the values those + * fields must have. Instead, it is preferable to use EntityFieldQuery to + * retrieve a list of entity IDs loadable by this function. + * @param $reset + * Whether to reset the internal cache for the requested entity type. + * + * @return + * An array of entity objects indexed by their ids. + * + * @todo Remove $conditions in Drupal 8. + */ +function entity_load($entity_type, $ids = FALSE, $conditions = array(), $reset = FALSE) { + if ($reset) { + entity_get_controller($entity_type)->resetCache(); + } + return entity_get_controller($entity_type)->load($ids, $conditions); +} + +/** + * Loads the unchanged, i.e. not modified, entity from the database. + * + * Unlike entity_load() this function ensures the entity is directly loaded from + * the database, thus bypassing any static cache. In particular, this function + * is useful to determine changes by comparing the entity being saved to the + * stored entity. + * + * @param $entity_type + * The entity type to load, e.g. node or user. + * @param $id + * The id of the entity to load. + * + * @return + * The unchanged entity, or FALSE if the entity cannot be loaded. + */ +function entity_load_unchanged($entity_type, $id) { + entity_get_controller($entity_type)->resetCache(array($id)); + $result = entity_get_controller($entity_type)->load(array($id)); + return reset($result); +} + +/** + * Gets the entity controller class for an entity type. + */ +function entity_get_controller($entity_type) { + $controllers = &drupal_static(__FUNCTION__, array()); + if (!isset($controllers[$entity_type])) { + $type_info = entity_get_info($entity_type); + $class = $type_info['controller class']; + $controllers[$entity_type] = new $class($entity_type); + } + return $controllers[$entity_type]; +} + +/** + * Invokes hook_entity_prepare_view(). + * + * If adding a new entity similar to nodes, comments or users, you should + * invoke this function during the ENTITY_build_content() or + * ENTITY_view_multiple() phases of rendering to allow other modules to alter + * the objects during this phase. This is needed for situations where + * information needs to be loaded outside of ENTITY_load() - particularly + * when loading entities into one another - i.e. a user object into a node, due + * to the potential for unwanted side-effects such as caching and infinite + * recursion. By convention, entity_prepare_view() is called after + * field_attach_prepare_view() to allow entity level hooks to act on content + * loaded by field API. + * + * @see hook_entity_prepare_view() + * + * @param $entity_type + * The type of entity, i.e. 'node', 'user'. + * @param $entities + * The entity objects which are being prepared for view, keyed by object ID. + */ +function entity_prepare_view($entity_type, $entities) { + // To ensure hooks are only run once per entity, check for an + // entity_view_prepared flag and only process items without it. + // @todo: resolve this more generally for both entity and field level hooks. + $prepare = array(); + foreach ($entities as $id => $entity) { + if (empty($entity->entity_view_prepared)) { + // Add this entity to the items to be prepared. + $prepare[$id] = $entity; + + // Mark this item as prepared. + $entity->entity_view_prepared = TRUE; + } + } + + if (!empty($prepare)) { + module_invoke_all('entity_prepare_view', $prepare, $entity_type); + } +} + +/** + * Returns the uri elements of an entity. + * + * @param $entity_type + * The entity type; e.g. 'node' or 'user'. + * @param $entity + * The entity for which to generate a path. + * + * @return + * An array containing the 'path' and 'options' keys used to build the uri of + * the entity, and matching the signature of url(). NULL if the entity has no + * uri of its own. + */ +function entity_uri($entity_type, $entity) { + $info = entity_get_info($entity_type); + list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity); + + // A bundle-specific callback takes precedence over the generic one for the + // entity type. + if (isset($info['bundles'][$bundle]['uri callback'])) { + $uri_callback = $info['bundles'][$bundle]['uri callback']; + } + elseif (isset($info['uri callback'])) { + $uri_callback = $info['uri callback']; + } + else { + return NULL; + } + + // Invoke the callback to get the URI. If there is no callback, return NULL. + if (isset($uri_callback) && function_exists($uri_callback)) { + $uri = $uri_callback($entity); + // Pass the entity data to url() so that alter functions do not need to + // lookup this entity again. + $uri['options']['entity_type'] = $entity_type; + $uri['options']['entity'] = $entity; + return $uri; + } +} + +/** + * Returns the label of an entity. + * + * See the 'label callback' component of the hook_entity_info() return value + * for more information. + * + * @param $entity_type + * The entity type; e.g., 'node' or 'user'. + * @param $entity + * The entity for which to generate the label. + * + * @return + * The entity label, or FALSE if not found. + */ +function entity_label($entity_type, $entity) { + $label = FALSE; + $info = entity_get_info($entity_type); + if (isset($info['label callback']) && function_exists($info['label callback'])) { + $label = $info['label callback']($entity_type, $entity); + } + elseif (!empty($info['entity keys']['label']) && isset($entity->{$info['entity keys']['label']})) { + $label = $entity->{$info['entity keys']['label']}; + } + + return $label; +} + +/** + * Helper function for attaching field API validation to entity forms. + */ +function entity_form_field_validate($entity_type, $form, &$form_state) { + // All field attach API functions act on an entity object, but during form + // validation, we don't have one. $form_state contains the entity as it was + // prior to processing the current form submission, and we must not update it + // until we have fully validated the submitted input. Therefore, for + // validation, act on a pseudo entity created out of the form values. + $pseudo_entity = (object) $form_state['values']; + field_attach_form_validate($entity_type, $pseudo_entity, $form, $form_state); +} + +/** + * Helper function for copying submitted values to entity properties for simple entity forms. + * + * During the submission handling of an entity form's "Save", "Preview", and + * possibly other buttons, the form state's entity needs to be updated with the + * submitted form values. Each entity form implements its own builder function + * for doing this, appropriate for the particular entity and form, whereas + * modules may specify additional builder functions in $form['#entity_builders'] + * for copying the form values of added form elements to entity properties. + * Many of the main entity builder functions can call this helper function to + * re-use its logic of copying $form_state['values'][PROPERTY] values to + * $entity->PROPERTY for all entries in $form_state['values'] that are not field + * data, and calling field_attach_submit() to copy field data. Apart from that + * this helper invokes any additional builder functions that have been specified + * in $form['#entity_builders']. + * + * For some entity forms (e.g., forms with complex non-field data and forms that + * simultaneously edit multiple entities), this behavior may be inappropriate, + * so the builder function for such forms needs to implement the required + * functionality instead of calling this function. + */ +function entity_form_submit_build_entity($entity_type, $entity, $form, &$form_state) { + $info = entity_get_info($entity_type); + list(, , $bundle) = entity_extract_ids($entity_type, $entity); + + // Copy top-level form values that are not for fields to entity properties, + // without changing existing entity properties that are not being edited by + // this form. Copying field values must be done using field_attach_submit(). + $values_excluding_fields = $info['fieldable'] ? array_diff_key($form_state['values'], field_info_instances($entity_type, $bundle)) : $form_state['values']; + foreach ($values_excluding_fields as $key => $value) { + $entity->$key = $value; + } + + // Invoke all specified builders for copying form values to entity properties. + if (isset($form['#entity_builders'])) { + foreach ($form['#entity_builders'] as $function) { + $function($entity_type, $entity, $form, $form_state); + } + } + + // Copy field values to the entity. + if ($info['fieldable']) { + field_attach_submit($entity_type, $entity, $form, $form_state); + } +} + +/** + * Exception thrown when a malformed entity is passed. + */ +class EntityMalformedException extends Exception { } + diff --git a/includes/entity.inc b/modules/entity/entity.query.inc similarity index 71% rename from includes/entity.inc rename to modules/entity/entity.query.inc index 99baf4992b47..b4e91a2f9f40 100644 --- a/includes/entity.inc +++ b/modules/entity/entity.query.inc @@ -1,388 +1,9 @@ $value. - * - * @return - * An array of entity objects indexed by their ids. When no results are - * found, an empty array is returned. - */ - public function load($ids = array(), $conditions = array()); -} - -/** - * Default implementation of DrupalEntityControllerInterface. - * - * This class can be used as-is by most simple entity types. Entity types - * requiring special handling can extend the class. - */ -class DrupalDefaultEntityController implements DrupalEntityControllerInterface { - - /** - * Static cache of entities. - * - * @var array - */ - protected $entityCache; - - /** - * Entity type for this controller instance. - * - * @var string - */ - protected $entityType; - - /** - * Array of information about the entity. - * - * @var array - * - * @see entity_get_info() - */ - protected $entityInfo; - - /** - * Additional arguments to pass to hook_TYPE_load(). - * - * Set before calling DrupalDefaultEntityController::attachLoad(). - * - * @var array - */ - protected $hookLoadArguments; - - /** - * Name of the entity's ID field in the entity database table. - * - * @var string - */ - protected $idKey; - - /** - * Name of entity's revision database table field, if it supports revisions. - * - * Has the value FALSE if this entity does not use revisions. - * - * @var string - */ - protected $revisionKey; - - /** - * The table that stores revisions, if the entity supports revisions. - * - * @var string - */ - protected $revisionTable; - - /** - * Whether this entity type should use the static cache. - * - * Set by entity info. - * - * @var boolean - */ - protected $cache; - - /** - * Constructor: sets basic variables. - */ - public function __construct($entityType) { - $this->entityType = $entityType; - $this->entityInfo = entity_get_info($entityType); - $this->entityCache = array(); - $this->hookLoadArguments = array(); - $this->idKey = $this->entityInfo['entity keys']['id']; - - // Check if the entity type supports revisions. - if (!empty($this->entityInfo['entity keys']['revision'])) { - $this->revisionKey = $this->entityInfo['entity keys']['revision']; - $this->revisionTable = $this->entityInfo['revision table']; - } - else { - $this->revisionKey = FALSE; - } - - // Check if the entity type supports static caching of loaded entities. - $this->cache = !empty($this->entityInfo['static cache']); - } - - /** - * Implements DrupalEntityControllerInterface::resetCache(). - */ - public function resetCache(array $ids = NULL) { - if (isset($ids)) { - foreach ($ids as $id) { - unset($this->entityCache[$id]); - } - } - else { - $this->entityCache = array(); - } - } - - /** - * Implements DrupalEntityControllerInterface::load(). - */ - public function load($ids = array(), $conditions = array()) { - $entities = array(); - - // Revisions are not statically cached, and require a different query to - // other conditions, so separate the revision id into its own variable. - if ($this->revisionKey && isset($conditions[$this->revisionKey])) { - $revision_id = $conditions[$this->revisionKey]; - unset($conditions[$this->revisionKey]); - } - else { - $revision_id = FALSE; - } - - // Create a new variable which is either a prepared version of the $ids - // array for later comparison with the entity cache, or FALSE if no $ids - // were passed. The $ids array is reduced as items are loaded from cache, - // and we need to know if it's empty for this reason to avoid querying the - // database when all requested entities are loaded from cache. - $passed_ids = !empty($ids) ? array_flip($ids) : FALSE; - // Try to load entities from the static cache, if the entity type supports - // static caching. - if ($this->cache && !$revision_id) { - $entities += $this->cacheGet($ids, $conditions); - // If any entities were loaded, remove them from the ids still to load. - if ($passed_ids) { - $ids = array_keys(array_diff_key($passed_ids, $entities)); - } - } - - // Load any remaining entities from the database. This is the case if $ids - // is set to FALSE (so we load all entities), if there are any ids left to - // load, if loading a revision, or if $conditions was passed without $ids. - if ($ids === FALSE || $ids || $revision_id || ($conditions && !$passed_ids)) { - // Build the query. - $query = $this->buildQuery($ids, $conditions, $revision_id); - $queried_entities = $query - ->execute() - ->fetchAllAssoc($this->idKey); - } - - // Pass all entities loaded from the database through $this->attachLoad(), - // which attaches fields (if supported by the entity type) and calls the - // entity type specific load callback, for example hook_node_load(). - if (!empty($queried_entities)) { - $this->attachLoad($queried_entities, $revision_id); - $entities += $queried_entities; - } - - if ($this->cache) { - // Add entities to the cache if we are not loading a revision. - if (!empty($queried_entities) && !$revision_id) { - $this->cacheSet($queried_entities); - } - } - - // Ensure that the returned array is ordered the same as the original - // $ids array if this was passed in and remove any invalid ids. - if ($passed_ids) { - // Remove any invalid ids from the array. - $passed_ids = array_intersect_key($passed_ids, $entities); - foreach ($entities as $entity) { - $passed_ids[$entity->{$this->idKey}] = $entity; - } - $entities = $passed_ids; - } - - return $entities; - } - - /** - * Builds the query to load the entity. - * - * This has full revision support. For entities requiring special queries, - * the class can be extended, and the default query can be constructed by - * calling parent::buildQuery(). This is usually necessary when the object - * being loaded needs to be augmented with additional data from another - * table, such as loading node type into comments or vocabulary machine name - * into terms, however it can also support $conditions on different tables. - * See CommentController::buildQuery() or TaxonomyTermController::buildQuery() - * for examples. - * - * @param $ids - * An array of entity IDs, or FALSE to load all entities. - * @param $conditions - * An array of conditions in the form 'field' => $value. - * @param $revision_id - * The ID of the revision to load, or FALSE if this query is asking for the - * most current revision(s). - * - * @return SelectQuery - * A SelectQuery object for loading the entity. - */ - protected function buildQuery($ids, $conditions = array(), $revision_id = FALSE) { - $query = db_select($this->entityInfo['base table'], 'base'); - - $query->addTag($this->entityType . '_load_multiple'); - - if ($revision_id) { - $query->join($this->revisionTable, 'revision', "revision.{$this->idKey} = base.{$this->idKey} AND revision.{$this->revisionKey} = :revisionId", array(':revisionId' => $revision_id)); - } - elseif ($this->revisionKey) { - $query->join($this->revisionTable, 'revision', "revision.{$this->revisionKey} = base.{$this->revisionKey}"); - } - - // Add fields from the {entity} table. - $entity_fields = $this->entityInfo['schema_fields_sql']['base table']; - - if ($this->revisionKey) { - // Add all fields from the {entity_revision} table. - $entity_revision_fields = drupal_map_assoc($this->entityInfo['schema_fields_sql']['revision table']); - // The id field is provided by entity, so remove it. - unset($entity_revision_fields[$this->idKey]); - - // Remove all fields from the base table that are also fields by the same - // name in the revision table. - $entity_field_keys = array_flip($entity_fields); - foreach ($entity_revision_fields as $key => $name) { - if (isset($entity_field_keys[$name])) { - unset($entity_fields[$entity_field_keys[$name]]); - } - } - $query->fields('revision', $entity_revision_fields); - } - - $query->fields('base', $entity_fields); - - if ($ids) { - $query->condition("base.{$this->idKey}", $ids, 'IN'); - } - if ($conditions) { - foreach ($conditions as $field => $value) { - $query->condition('base.' . $field, $value); - } - } - return $query; - } - - /** - * Attaches data to entities upon loading. - * This will attach fields, if the entity is fieldable. It calls - * hook_entity_load() for modules which need to add data to all entities. - * It also calls hook_TYPE_load() on the loaded entities. For example - * hook_node_load() or hook_user_load(). If your hook_TYPE_load() - * expects special parameters apart from the queried entities, you can set - * $this->hookLoadArguments prior to calling the method. - * See NodeController::attachLoad() for an example. - * - * @param $queried_entities - * Associative array of query results, keyed on the entity ID. - * @param $revision_id - * ID of the revision that was loaded, or FALSE if teh most current revision - * was loaded. - */ - protected function attachLoad(&$queried_entities, $revision_id = FALSE) { - // Attach fields. - if ($this->entityInfo['fieldable']) { - if ($revision_id) { - field_attach_load_revision($this->entityType, $queried_entities); - } - else { - field_attach_load($this->entityType, $queried_entities); - } - } - - // Call hook_entity_load(). - foreach (module_implements('entity_load') as $module) { - $function = $module . '_entity_load'; - $function($queried_entities, $this->entityType); - } - // Call hook_TYPE_load(). The first argument for hook_TYPE_load() are - // always the queried entities, followed by additional arguments set in - // $this->hookLoadArguments. - $args = array_merge(array($queried_entities), $this->hookLoadArguments); - foreach (module_implements($this->entityInfo['load hook']) as $module) { - call_user_func_array($module . '_' . $this->entityInfo['load hook'], $args); - } - } - - /** - * Gets entities from the static cache. - * - * @param $ids - * If not empty, return entities that match these IDs. - * @param $conditions - * If set, return entities that match all of these conditions. - * - * @return - * Array of entities from the entity cache. - */ - protected function cacheGet($ids, $conditions = array()) { - $entities = array(); - // Load any available entities from the internal cache. - if (!empty($this->entityCache)) { - if ($ids) { - $entities += array_intersect_key($this->entityCache, array_flip($ids)); - } - // If loading entities only by conditions, fetch all available entities - // from the cache. Entities which don't match are removed later. - elseif ($conditions) { - $entities = $this->entityCache; - } - } - - // Exclude any entities loaded from cache if they don't match $conditions. - // This ensures the same behavior whether loading from memory or database. - if ($conditions) { - foreach ($entities as $entity) { - $entity_values = (array) $entity; - if (array_diff_assoc($conditions, $entity_values)) { - unset($entities[$entity->{$this->idKey}]); - } - } - } - return $entities; - } - - /** - * Stores entities in the static entity cache. - * - * @param $entities - * Entities to store in the cache. - */ - protected function cacheSet($entities) { - $this->entityCache += $entities; - } -} /** * Exception thrown by EntityFieldQuery() on unsupported query syntax. @@ -938,7 +559,7 @@ class EntityFieldQuery { } /** - * Enable a pager for the query. + * Enables a pager for the query. * * @param $limit * An integer specifying the number of elements per page. If passed a false @@ -966,7 +587,7 @@ class EntityFieldQuery { } /** - * Enable sortable tables for this query. + * Enables sortable tables for this query. * * @param $headers * An EFQ Header array based on which the order clause is added to the query. @@ -1243,7 +864,7 @@ class EntityFieldQuery { } /** - * Get the total number of results and initialize a pager for the query. + * Gets the total number of results and initialize a pager for the query. * * This query can be detected by checking for ($this->pager && $this->count), * which allows a driver to return 0 from the count query and disable @@ -1330,7 +951,3 @@ class EntityFieldQuery { } -/** - * Exception thrown when a malformed entity is passed. - */ -class EntityMalformedException extends Exception { } diff --git a/modules/simpletest/tests/entity_cache_test.info b/modules/entity/tests/entity_cache_test.info similarity index 100% rename from modules/simpletest/tests/entity_cache_test.info rename to modules/entity/tests/entity_cache_test.info diff --git a/modules/simpletest/tests/entity_cache_test.module b/modules/entity/tests/entity_cache_test.module similarity index 100% rename from modules/simpletest/tests/entity_cache_test.module rename to modules/entity/tests/entity_cache_test.module diff --git a/modules/simpletest/tests/entity_cache_test_dependency.info b/modules/entity/tests/entity_cache_test_dependency.info similarity index 100% rename from modules/simpletest/tests/entity_cache_test_dependency.info rename to modules/entity/tests/entity_cache_test_dependency.info diff --git a/modules/simpletest/tests/entity_cache_test_dependency.module b/modules/entity/tests/entity_cache_test_dependency.module similarity index 100% rename from modules/simpletest/tests/entity_cache_test_dependency.module rename to modules/entity/tests/entity_cache_test_dependency.module diff --git a/modules/simpletest/tests/entity_crud_hook_test.info b/modules/entity/tests/entity_crud_hook_test.info similarity index 100% rename from modules/simpletest/tests/entity_crud_hook_test.info rename to modules/entity/tests/entity_crud_hook_test.info diff --git a/modules/simpletest/tests/entity_crud_hook_test.module b/modules/entity/tests/entity_crud_hook_test.module similarity index 100% rename from modules/simpletest/tests/entity_crud_hook_test.module rename to modules/entity/tests/entity_crud_hook_test.module diff --git a/modules/simpletest/tests/entity_crud_hook_test.test b/modules/entity/tests/entity_crud_hook_test.test similarity index 100% rename from modules/simpletest/tests/entity_crud_hook_test.test rename to modules/entity/tests/entity_crud_hook_test.test diff --git a/modules/simpletest/tests/entity_query.test b/modules/entity/tests/entity_query.test similarity index 99% rename from modules/simpletest/tests/entity_query.test rename to modules/entity/tests/entity_query.test index fb95518d168e..e540a90d99f8 100644 --- a/modules/simpletest/tests/entity_query.test +++ b/modules/entity/tests/entity_query.test @@ -1,6 +1,5 @@ subject, then - * 'subject' should be specified here. If complex logic is required to - * build the label, a 'label callback' should be defined instead (see - * the 'label callback' section above for details). - * - bundle keys: An array describing how the Field API can extract the - * information it needs from the bundle objects for this type. This entry - * is required if the 'path' provided in the 'bundles'/'admin' section - * identifies the bundle using a named menu placeholder whose loader - * callback returns an object (e.g., $vocabulary for taxonomy terms, or - * $node_type for nodes). If the path does not include the bundle, or the - * bundle is just a string rather than an automatically loaded object, then - * this can be omitted. Elements: - * - bundle: The name of the property of the bundle object that contains - * the name of the bundle object. - * - bundles: An array describing all bundles for this object type. Keys are - * bundles machine names, as found in the objects' 'bundle' property - * (defined in the 'entity keys' entry above). Elements: - * - label: The human-readable name of the bundle. - * - uri callback: Same as the 'uri callback' key documented above for the - * entity type, but for the bundle only. When determining the URI of an - * entity, if a 'uri callback' is defined for both the entity type and - * the bundle, the one for the bundle is used. - * - admin: An array of information that allows Field UI pages to attach - * themselves to the existing administration pages for the bundle. - * Elements: - * - path: the path of the bundle's main administration page, as defined - * in hook_menu(). If the path includes a placeholder for the bundle, - * the 'bundle argument' and 'real path' keys below are required. - * - bundle argument: The position of the bundle placeholder in 'path', if - * any. - * - real path: The actual path (no placeholder) of the bundle's main - * administration page. This will be used to generate links. - * - access callback: As in hook_menu(). 'user_access' will be assumed if - * no value is provided. - * - access arguments: As in hook_menu(). - * - view modes: An array describing the view modes for the entity type. View - * modes let entities be displayed differently depending on the context. - * For instance, a node can be displayed differently on its own page - * ('full' mode), on the home page or taxonomy listings ('teaser' mode), or - * in an RSS feed ('rss' mode). Modules taking part in the display of the - * entity (notably the Field API) can adjust their behavior depending on - * the requested view mode. An additional 'default' view mode is available - * for all entity types. This view mode is not intended for actual entity - * display, but holds default display settings. For each available view - * mode, administrators can configure whether it should use its own set of - * field display settings, or just replicate the settings of the 'default' - * view mode, thus reducing the amount of display configurations to keep - * track of. Keys of the array are view mode names. Each view mode is - * described by an array with the following key/value pairs: - * - label: The human-readable name of the view mode - * - custom settings: A boolean specifying whether the view mode should by - * default use its own custom field display settings. If FALSE, entities - * displayed in this view mode will reuse the 'default' display settings - * by default (e.g. right after the module exposing the view mode is - * enabled), but administrators can later use the Field UI to apply custom - * display settings specific to the view mode. - * - * @see entity_load() - * @see hook_entity_info_alter() - */ -function hook_entity_info() { - $return = array( - 'node' => array( - 'label' => t('Node'), - 'controller class' => 'NodeController', - 'base table' => 'node', - 'revision table' => 'node_revision', - 'uri callback' => 'node_uri', - 'fieldable' => TRUE, - 'translation' => array( - 'locale' => TRUE, - ), - 'entity keys' => array( - 'id' => 'nid', - 'revision' => 'vid', - 'bundle' => 'type', - ), - 'bundle keys' => array( - 'bundle' => 'type', - ), - 'bundles' => array(), - 'view modes' => array( - 'full' => array( - 'label' => t('Full content'), - 'custom settings' => FALSE, - ), - 'teaser' => array( - 'label' => t('Teaser'), - 'custom settings' => TRUE, - ), - 'rss' => array( - 'label' => t('RSS'), - 'custom settings' => FALSE, - ), - ), - ), - ); - - // Search integration is provided by node.module, so search-related - // view modes for nodes are defined here and not in search.module. - if (module_exists('search')) { - $return['node']['view modes'] += array( - 'search_index' => array( - 'label' => t('Search index'), - 'custom settings' => FALSE, - ), - 'search_result' => array( - 'label' => t('Search result'), - 'custom settings' => FALSE, - ), - ); - } - - // Bundles must provide a human readable name so we can create help and error - // messages, and the path to attach Field admin pages to. - foreach (node_type_get_names() as $type => $name) { - $return['node']['bundles'][$type] = array( - 'label' => $name, - 'admin' => array( - 'path' => 'admin/structure/types/manage/%node_type', - 'real path' => 'admin/structure/types/manage/' . str_replace('_', '-', $type), - 'bundle argument' => 4, - 'access arguments' => array('administer content types'), - ), - ); - } - - return $return; -} - -/** - * Alter the entity info. - * - * Modules may implement this hook to alter the information that defines an - * entity. All properties that are available in hook_entity_info() can be - * altered here. - * - * @param $entity_info - * The entity info array, keyed by entity name. - * - * @see hook_entity_info() - */ -function hook_entity_info_alter(&$entity_info) { - // Set the controller class for nodes to an alternate implementation of the - // DrupalEntityController interface. - $entity_info['node']['controller class'] = 'MyCustomNodeController'; -} - -/** - * Act on entities when loaded. - * - * This is a generic load hook called for all entity types loaded via the - * entity API. - * - * @param $entities - * The entities keyed by entity ID. - * @param $type - * The type of entities being loaded (i.e. node, user, comment). - */ -function hook_entity_load($entities, $type) { - foreach ($entities as $entity) { - $entity->foo = mymodule_add_something($entity, $type); - } -} - -/** - * Act on an entity before it is about to be created or updated. - * - * @param $entity - * The entity object. - * @param $type - * The type of entity being saved (i.e. node, user, comment). - */ -function hook_entity_presave($entity, $type) { - $entity->changed = REQUEST_TIME; -} - -/** - * Act on entities when inserted. - * - * @param $entity - * The entity object. - * @param $type - * The type of entity being inserted (i.e. node, user, comment). - */ -function hook_entity_insert($entity, $type) { - // Insert the new entity into a fictional table of all entities. - $info = entity_get_info($type); - list($id) = entity_extract_ids($type, $entity); - db_insert('example_entity') - ->fields(array( - 'type' => $type, - 'id' => $id, - 'created' => REQUEST_TIME, - 'updated' => REQUEST_TIME, - )) - ->execute(); -} - -/** - * Act on entities when updated. - * - * @param $entity - * The entity object. - * @param $type - * The type of entity being updated (i.e. node, user, comment). - */ -function hook_entity_update($entity, $type) { - // Update the entity's entry in a fictional table of all entities. - $info = entity_get_info($type); - list($id) = entity_extract_ids($type, $entity); - db_update('example_entity') - ->fields(array( - 'updated' => REQUEST_TIME, - )) - ->condition('type', $type) - ->condition('id', $id) - ->execute(); -} - -/** - * Act on entities when deleted. - * - * @param $entity - * The entity object. - * @param $type - * The type of entity being deleted (i.e. node, user, comment). - */ -function hook_entity_delete($entity, $type) { - // Delete the entity's entry from a fictional table of all entities. - $info = entity_get_info($type); - list($id) = entity_extract_ids($type, $entity); - db_delete('example_entity') - ->condition('type', $type) - ->condition('id', $id) - ->execute(); -} - -/** - * Alter or execute an EntityFieldQuery. - * - * @param EntityFieldQuery $query - * An EntityFieldQuery. One of the most important properties to be changed is - * EntityFieldQuery::executeCallback. If this is set to an existing function, - * this function will get the query as its single argument and its result - * will be the returned as the result of EntityFieldQuery::execute(). This can - * be used to change the behavior of EntityFieldQuery entirely. For example, - * the default implementation can only deal with one field storage engine, but - * it is possible to write a module that can query across field storage - * engines. Also, the default implementation presumes entities are stored in - * SQL, but the execute callback could instead query any other entity storage, - * local or remote. - * - * Note the $query->altered attribute which is TRUE in case the query has - * already been altered once. This happens with cloned queries. - * If there is a pager, then such a cloned query will be executed to count - * all elements. This query can be detected by checking for - * ($query->pager && $query->count), allowing the driver to return 0 from - * the count query and disable the pager. - */ -function hook_entity_query_alter($query) { - $query->executeCallback = 'my_module_query_callback'; -} - -/** - * Act on entities being assembled before rendering. - * - * @param $entity - * The entity object. - * @param $type - * The type of entity being rendered (i.e. node, user, comment). - * @param $view_mode - * The view mode the entity is rendered in. - * @param $langcode - * The language code used for rendering. - * - * The module may add elements to $entity->content prior to rendering. The - * structure of $entity->content is a renderable array as expected by - * drupal_render(). - * - * @see hook_entity_view_alter() - * @see hook_comment_view() - * @see hook_node_view() - * @see hook_user_view() - */ -function hook_entity_view($entity, $type, $view_mode, $langcode) { - $entity->content['my_additional_field'] = array( - '#markup' => $additional_field, - '#weight' => 10, - '#theme' => 'mymodule_my_additional_field', - ); -} - -/** - * Alter the results of ENTITY_view(). - * - * This hook is called after the content has been assembled in a structured - * array and may be used for doing processing which requires that the complete - * entity content structure has been built. - * - * If a module wishes to act on the rendered HTML of the entity rather than the - * structured content array, it may use this hook to add a #post_render - * callback. Alternatively, it could also implement hook_preprocess_ENTITY(). - * See drupal_render() and theme() for details. - * - * @param $build - * A renderable array representing the entity content. - * @param $type - * The type of entity being rendered (i.e. node, user, comment). - * - * @see hook_entity_view() - * @see hook_comment_view_alter() - * @see hook_node_view_alter() - * @see hook_taxonomy_term_view_alter() - * @see hook_user_view_alter() - */ -function hook_entity_view_alter(&$build, $type) { - if ($build['#view_mode'] == 'full' && isset($build['an_additional_field'])) { - // Change its weight. - $build['an_additional_field']['#weight'] = -10; - - // Add a #post_render callback to act on the rendered HTML of the entity. - $build['#post_render'][] = 'my_module_node_post_render'; - } -} - /** * Define administrative paths. * @@ -491,30 +105,6 @@ function hook_admin_paths_alter(&$paths) { $paths['node/add/forum'] = FALSE; } -/** - * Act on entities as they are being prepared for view. - * - * Allows you to operate on multiple entities as they are being prepared for - * view. Only use this if attaching the data during the entity_load() phase - * is not appropriate, for example when attaching other 'entity' style objects. - * - * @param $entities - * The entities keyed by entity ID. - * @param $type - * The type of entities being loaded (i.e. node, user, comment). - * @param $langcode - * The language to display the entity in. - */ -function hook_entity_prepare_view($entities, $type, $langcode) { - // Load a specific node into the user object for later theming. - if ($type == 'user') { - $nodes = mymodule_get_user_nodes(array_keys($entities)); - foreach ($entities as $uid => $entity) { - $entity->user_node = $nodes[$uid]; - } - } -} - /** * Perform periodic actions. * @@ -2394,6 +1984,30 @@ function hook_flush_caches() { return array('example'); } +/** + * Perform necessary actions before modules are installed. + * + * This function allows all modules to react prior to a module being installed. + * + * @param $modules + * An array of modules about to be installed. + */ +function hook_modules_preinstall($modules) { + mymodule_cache_clear(); +} + +/** + * Perform necessary actions before modules are enabled. + * + * This function allows all modules to react prior to a module being enabled. + * + * @param $module + * An array of modules about to be enabled. + */ +function hook_modules_preenable($modules) { + mymodule_cache_clear(); +} + /** * Perform necessary actions after modules are installed. * diff --git a/modules/system/system.install b/modules/system/system.install index 6d80c43a0057..5c4250aec7bd 100644 --- a/modules/system/system.install +++ b/modules/system/system.install @@ -1623,10 +1623,10 @@ function system_update_last_removed() { */ /** - * Placeholder update to set the schema version to 8000. + * Enable entity module. */ function system_update_8000() { - // Fill in the first update to Drupal 8 when needed. + update_module_enable(array('entity')); } /** diff --git a/modules/taxonomy/taxonomy.info b/modules/taxonomy/taxonomy.info index cba3869820ff..6a13f81db0af 100644 --- a/modules/taxonomy/taxonomy.info +++ b/modules/taxonomy/taxonomy.info @@ -4,6 +4,7 @@ package = Core version = VERSION core = 8.x dependencies[] = options +dependencies[] = entity files[] = taxonomy.module files[] = taxonomy.test configure = admin/structure/taxonomy diff --git a/modules/user/user.entity.inc b/modules/user/user.entity.inc new file mode 100644 index 000000000000..5549c7707188 --- /dev/null +++ b/modules/user/user.entity.inc @@ -0,0 +1,52 @@ + $record) { + $picture_fids[] = $record->picture; + $queried_users[$key]->data = unserialize($record->data); + $queried_users[$key]->roles = array(); + if ($record->uid) { + $queried_users[$record->uid]->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user'; + } + else { + $queried_users[$record->uid]->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user'; + } + } + + // Add any additional roles from the database. + $result = db_query('SELECT r.rid, r.name, ur.uid FROM {role} r INNER JOIN {users_roles} ur ON ur.rid = r.rid WHERE ur.uid IN (:uids)', array(':uids' => array_keys($queried_users))); + foreach ($result as $record) { + $queried_users[$record->uid]->roles[$record->rid] = $record->name; + } + + // Add the full file objects for user pictures if enabled. + if (!empty($picture_fids) && variable_get('user_pictures', 1) == 1) { + $pictures = file_load_multiple($picture_fids); + foreach ($queried_users as $account) { + if (!empty($account->picture) && isset($pictures[$account->picture])) { + $account->picture = $pictures[$account->picture]; + } + else { + $account->picture = NULL; + } + } + } + // Call the default attachLoad() method. This will add fields and call + // hook_user_load(). + parent::attachLoad($queried_users, $revision_id); + } +} diff --git a/modules/user/user.info b/modules/user/user.info index a4d18d634085..d887352760e4 100644 --- a/modules/user/user.info +++ b/modules/user/user.info @@ -3,7 +3,7 @@ description = Manages the user registration and login system. package = Core version = VERSION core = 8.x -files[] = user.module +files[] = user.entity.inc files[] = user.test required = TRUE configure = admin/config/people diff --git a/modules/user/user.module b/modules/user/user.module index 1355159a90c3..47942584e9a8 100644 --- a/modules/user/user.module +++ b/modules/user/user.module @@ -287,53 +287,6 @@ function user_load_multiple($uids = array(), $conditions = array(), $reset = FAL return entity_load('user', $uids, $conditions, $reset); } -/** - * Controller class for users. - * - * This extends the DrupalDefaultEntityController class, adding required - * special handling for user objects. - */ -class UserController extends DrupalDefaultEntityController { - - function attachLoad(&$queried_users, $revision_id = FALSE) { - // Build an array of user picture IDs so that these can be fetched later. - $picture_fids = array(); - foreach ($queried_users as $key => $record) { - $picture_fids[] = $record->picture; - $queried_users[$key]->data = unserialize($record->data); - $queried_users[$key]->roles = array(); - if ($record->uid) { - $queried_users[$record->uid]->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user'; - } - else { - $queried_users[$record->uid]->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user'; - } - } - - // Add any additional roles from the database. - $result = db_query('SELECT r.rid, r.name, ur.uid FROM {role} r INNER JOIN {users_roles} ur ON ur.rid = r.rid WHERE ur.uid IN (:uids)', array(':uids' => array_keys($queried_users))); - foreach ($result as $record) { - $queried_users[$record->uid]->roles[$record->rid] = $record->name; - } - - // Add the full file objects for user pictures if enabled. - if (!empty($picture_fids) && variable_get('user_pictures', 1) == 1) { - $pictures = file_load_multiple($picture_fids); - foreach ($queried_users as $account) { - if (!empty($account->picture) && isset($pictures[$account->picture])) { - $account->picture = $pictures[$account->picture]; - } - else { - $account->picture = NULL; - } - } - } - // Call the default attachLoad() method. This will add fields and call - // hook_user_load(). - parent::attachLoad($queried_users, $revision_id); - } -} - /** * Loads a user object. * diff --git a/update.php b/update.php index b7594366b3db..e1ad8c0e760e 100644 --- a/update.php +++ b/update.php @@ -345,7 +345,6 @@ require_once DRUPAL_ROOT . '/includes/bootstrap.inc'; require_once DRUPAL_ROOT . '/includes/update.inc'; require_once DRUPAL_ROOT . '/includes/common.inc'; require_once DRUPAL_ROOT . '/includes/file.inc'; -require_once DRUPAL_ROOT . '/includes/entity.inc'; require_once DRUPAL_ROOT . '/includes/unicode.inc'; update_prepare_d8_bootstrap(); From bb01b39ac3def0b73604fb03b240b112096f2c50 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Wed, 21 Sep 2011 06:17:58 -0400 Subject: [PATCH 79/80] - Patch #1276042 by chx: preg is used on html. --- modules/filter/filter.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/filter/filter.module b/modules/filter/filter.module index a3f787e6a856..a84d6f712714 100644 --- a/modules/filter/filter.module +++ b/modules/filter/filter.module @@ -1079,7 +1079,7 @@ function filter_dom_serialize($dom_document) { foreach ($body_node->childNodes as $child_node) { $body_content .= $dom_document->saveXML($child_node); } - return preg_replace('|<([^> ]*)/>|i', '<$1 />', $body_content); + return $body_content; } /** From 2032c2809d411911ee4836193ebe5e3d6436a023 Mon Sep 17 00:00:00 2001 From: Dries Buytaert Date: Wed, 21 Sep 2011 08:44:37 -0400 Subject: [PATCH 80/80] =?UTF-8?q?-=20Patch=20#1236680=20by=20franz,=20G?= =?UTF-8?q?=C3=A1bor=20Hojtsy:=20move=20path=20language=20settings=20from?= =?UTF-8?q?=20Locale=20to=20Path=20module.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/locale/locale.module | 14 -------------- modules/path/path.admin.inc | 22 +++++++++++++++++----- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/modules/locale/locale.module b/modules/locale/locale.module index 4a15ed990b0f..9af706c34bdb 100644 --- a/modules/locale/locale.module +++ b/modules/locale/locale.module @@ -293,20 +293,6 @@ function locale_language_selector_form(&$form, &$form_state, $user) { ); } -/** - * Implements hook_form_FORM_ID_alter(). - */ -function locale_form_path_admin_form_alter(&$form, &$form_state) { - $form['language'] = array( - '#type' => 'select', - '#title' => t('Language'), - '#options' => array(LANGUAGE_NONE => t('All languages')) + locale_language_list('name'), - '#default_value' => $form['language']['#value'], - '#weight' => -10, - '#description' => t('A path alias set for a specific language will always be used when displaying this page in that language, and takes precedence over path aliases set for All languages.'), - ); -} - /** * Implements hook_form_FORM_ID_alter(). */ diff --git a/modules/path/path.admin.inc b/modules/path/path.admin.inc index f10142b56393..c8a69639aa68 100644 --- a/modules/path/path.admin.inc +++ b/modules/path/path.admin.inc @@ -130,11 +130,23 @@ function path_admin_form($form, &$form_state, $path = array('source' => '', 'ali '#required' => TRUE, ); - // This will be a hidden value unless locale module is enabled. - $form['language'] = array( - '#type' => 'value', - '#value' => $path['language'] - ); + // A hidden value unless locale module is enabled. + if (module_exists('locale')) { + $form['language'] = array( + '#type' => 'select', + '#title' => t('Language'), + '#options' => array(LANGUAGE_NONE => t('All languages')) + locale_language_list('name'), + '#default_value' => $path['language'], + '#weight' => -10, + '#description' => t('A path alias set for a specific language will always be used when displaying this page in that language, and takes precedence over path aliases set for All languages.'), + ); + } + else { + $form['language'] = array( + '#type' => 'value', + '#value' => $path['language'] + ); + } $form['actions'] = array('#type' => 'actions'); $form['actions']['submit'] = array(