diff --git a/.gitignore b/.gitignore deleted file mode 100644 index baff153..0000000 --- a/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -# Ignore configuration files that may contain sensitive information. -sites/*/settings*.php - -# Ignore paths that contain user-generated content. -sites/*/files -sites/*/private diff --git a/.htaccess b/.htaccess deleted file mode 100644 index 7ccb6a2..0000000 --- a/.htaccess +++ /dev/null @@ -1,143 +0,0 @@ -# -# Apache/PHP/Drupal settings: -# - -# Protect files and directories from prying eyes. - - Order allow,deny - - -# Don't show directory listings for URLs which map to a directory. -Options -Indexes - -# Follow symbolic links in this directory. -Options +FollowSymLinks - -# Make Drupal handle any 404 errors. -ErrorDocument 404 /index.php - -# Set the default handler. -DirectoryIndex index.php index.html index.htm - -# Override PHP settings that cannot be changed at runtime. See -# sites/default/default.settings.php and drupal_environment_initialize() in -# includes/bootstrap.inc for settings that can be changed at runtime. - -# PHP 5, Apache 1 and 2. - - php_flag magic_quotes_gpc off - php_flag magic_quotes_sybase off - php_flag register_globals off - php_flag session.auto_start off - php_value mbstring.http_input pass - php_value mbstring.http_output pass - php_flag mbstring.encoding_translation off - - -# Requires mod_expires to be enabled. - - # Enable expirations. - ExpiresActive On - - # Cache all files for 2 weeks after access (A). - ExpiresDefault A1209600 - - - # Do not allow PHP scripts to be cached unless they explicitly send cache - # headers themselves. Otherwise all scripts would have to overwrite the - # headers set by mod_expires if they want another caching behavior. This may - # fail if an error occurs early in the bootstrap process, and it may cause - # problems if a non-Drupal PHP file is installed in a subdirectory. - ExpiresActive Off - - - -# Various rewrite rules. - - RewriteEngine on - - # Set "protossl" to "s" if we were accessed via https://. This is used later - # if you enable "www." stripping or enforcement, in order to ensure that - # you don't bounce between http and https. - RewriteRule ^ - [E=protossl] - RewriteCond %{HTTPS} on - RewriteRule ^ - [E=protossl:s] - - # Make sure Authorization HTTP header is available to PHP - # even when running as CGI or FastCGI. - RewriteRule ^ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] - - # Block access to "hidden" directories whose names begin with a period. This - # includes directories used by version control systems such as Subversion or - # Git to store control files. Files whose names begin with a period, as well - # as the control files used by CVS, are protected by the FilesMatch directive - # above. - # - # NOTE: This only works when mod_rewrite is loaded. Without mod_rewrite, it is - # not possible to block access to entire directories from .htaccess, because - # is not allowed here. - # - # If you do not have mod_rewrite installed, you should remove these - # directories from your webroot or otherwise protect them from being - # downloaded. - RewriteRule "(^|/)\." - [F] - - # If your site can be accessed both with and without the 'www.' prefix, you - # can use one of the following settings to redirect users to your preferred - # URL, either WITH or WITHOUT the 'www.' prefix. Choose ONLY one option: - # - # To redirect all users to access the site WITH the 'www.' prefix, - # (http://example.com/... will be redirected to http://www.example.com/...) - # uncomment the following: - # RewriteCond %{HTTP_HOST} . - # RewriteCond %{HTTP_HOST} !^www\. [NC] - # RewriteRule ^ http%{ENV:protossl}://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301] - # - # To redirect all users to access the site WITHOUT the 'www.' prefix, - # (http://www.example.com/... will be redirected to http://example.com/...) - # uncomment the following: - # RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] - # RewriteRule ^ http%{ENV:protossl}://%1%{REQUEST_URI} [L,R=301] - - # Modify the RewriteBase if you are using Drupal in a subdirectory or in a - # VirtualDocumentRoot and the rewrite rules are not working properly. - # For example if your site is at http://example.com/drupal uncomment and - # modify the following line: - # RewriteBase /drupal - # - # If your site is running in a VirtualDocumentRoot at http://example.com/, - # uncomment the following line: - # RewriteBase / - - # Pass all requests not referring directly to files in the filesystem to - # index.php. Clean URLs are handled in drupal_environment_initialize(). - RewriteCond %{REQUEST_FILENAME} !-f - RewriteCond %{REQUEST_FILENAME} !-d - RewriteCond %{REQUEST_URI} !=/favicon.ico - RewriteRule ^ index.php [L] - - # Rules to correctly serve gzip compressed CSS and JS files. - # Requires both mod_rewrite and mod_headers to be enabled. - - # Serve gzip compressed CSS files if they exist and the client accepts gzip. - RewriteCond %{HTTP:Accept-encoding} gzip - RewriteCond %{REQUEST_FILENAME}\.gz -s - RewriteRule ^(.*)\.css $1\.css\.gz [QSA] - - # Serve gzip compressed JS files if they exist and the client accepts gzip. - RewriteCond %{HTTP:Accept-encoding} gzip - RewriteCond %{REQUEST_FILENAME}\.gz -s - RewriteRule ^(.*)\.js $1\.js\.gz [QSA] - - # Serve correct content types, and prevent mod_deflate double gzip. - RewriteRule \.css\.gz$ - [T=text/css,E=no-gzip:1] - RewriteRule \.js\.gz$ - [T=text/javascript,E=no-gzip:1] - - - # Serve correct encoding type. - Header set Content-Encoding gzip - # Force proxies to cache gzipped & non-gzipped css/js files separately. - Header append Vary Accept-Encoding - - - diff --git a/CHANGELOG.txt b/CHANGELOG.txt deleted file mode 100644 index 93c4bd5..0000000 --- a/CHANGELOG.txt +++ /dev/null @@ -1,2033 +0,0 @@ - -Drupal 7.36, xxxx-xx-xx (development version) ------------------------ -- Added a 'file_public_schema' variable which allows modules that define - publicly-accessible streams in hook_stream_wrappers() to bypass file download - access checks when processing managed file upload fields. -- Fixed a bug that caused database query tags not to be added to search-related - database queries under many circumstances, and which prevented the - corresponding hook_query_TAG_alter() implementations from being called. -- Fixed the "for" attribute on managed file upload field labels to improve - accessibility (minor markup change). -- Added a 'javascript_always_use_jquery' variable which can be set to FALSE by - sites that may not need jQuery loaded on all pages, and a 'requires_jquery' - option to drupal_add_js() which modules can set to FALSE when adding - JavaScript files that have no dependency on jQuery (API addition: - https://www.drupal.org/node/2462717). -- Fixed incorrect foreign keys in the User module's role_permission and - users_roles database tables. -- Changed permission descriptions throughout Drupal core to consistently link - to relevant administrative pages, regardless of whether the user viewing the - Permissions page can view the page being linked to (minor UI change). -- Fixed the drupal_add_region_content() function so that it actually adds - content to the page. -- Added an 'image_suppress_itok_output' variable to allow sites already using - the existing 'image_allow_insecure_derivatives' variable to also prevent - security tokens from appearing in image derivative URLs. -- Fixed double-escaping of theme names in the Block module administrative - interface (minor string change). -- Added basic support for Xdebug when running automated tests. -- Fixed a bug which caused previewing a node to remove elements from the node - being edited. With this fix, calling node_preview() will no longer modify the - passed-in node object (minor API change). -- Added a user_has_role() function to check whether a user has a particular - role (API addition: https://www.drupal.org/node/2462411). -- Fixed installation failures when an opcode cache is enabled. -- Fixed a bug in the Drupal 6 to Drupal 7 upgrade path which caused private - files to be inaccessible. -- Fixed a bug in the Drupal 6 to Drupal 7 upgrade path which caused user - pictures to be lost. -- Fixed missing language code in hook_field_attach_view_alter() when it is - invoked from field_view_field(). -- Stopped sending ETag and Last-Modified headers for uncached page requests, - since they break caching for certain Varnish and Nginx configurations. -- Changed the Simpletest module to allow PSR-4 test classes to be used in - Drupal 7. -- Fixed a fatal error that occurred when using the Comment module's "Unpublish - comment containing keyword(s)" action. -- Changed the "lang" attribute on language links to "xml:lang" so it validates - as XHTML (minor markup change). -- Prevented the form API from allowing arrays to be submitted for various form - elements, such as textfields, textareas, and password fields (API change: - https://www.drupal.org/node/2462723). -- Fixed a bug in the Contact module which caused the global user object to have - the incorrect name and e-mail address during the remainder of the page - request after the contact form is submitted. -- Numerous small bug fixes. -- Numerous API documentation improvements. -- Additional automated test coverage. - -Drupal 7.35, 2015-03-18 ----------------------- -- Fixed security issues (multiple vulnerabilities). See SA-CORE-2015-001. - -Drupal 7.34, 2014-11-19 ----------------------- -- Fixed security issues (multiple vulnerabilities). See SA-CORE-2014-006. - -Drupal 7.33, 2014-11-07 ------------------------ -- Began storing the file modification time of each module and theme in the - {system} database table so that contributed modules can use it to identify - recently changed modules and themes (minor data structure change to the - return value of system_get_info() and other related functions). -- Added a "Did you mean?" feature to the run-tests.sh script for running - automated tests from the command line, to help developers who are attempting - to run a particular test class or group. -- Changed the date format used in various HTTP headers output by Drupal core - from RFC 1123 format to RFC 7231 format. -- Added a "block_cache_bypass_node_grants" variable to allow sites which have - node access modules enabled to use the block cache if desired (API addition). -- Made image derivative generation HTTP requests return a 404 error (rather - than a 500 error) when the source image does not exist. -- Fixed a bug which caused user pictures to be removed from the user object - after saving, and resulted in data loss if the user account was subsequently - re-saved. -- Fixed a bug in which field_has_data() did not return TRUE for fields that - only had data in older entity revisions, leading to loss of the field's data - when the field configuration was edited. -- Fixed a bug which caused the Ajax progress throbber to appear misaligned in - many situatons (minor styling change). -- Prevented the Bartik theme from lower-casing the "Permalink" link on - comments, for improved multilingual support (minor UI change). -- Added a "preferred_menu_links" tag to the database query that is used by - menu_link_get_preferred() to find the preferred menu link for a given path, - to make it easier to alter. -- Increased the maximum allowed length of block titles to 255 characters - (database schema change to the {block} table). -- Removed the Field module's field_modules_uninstalled() function, since it did - not do anything when it was invoked. -- Added a "theme_hook_original" variable to templates and theme functions and - an optional sitewide theme debug mode, to provide contextual information in - the page's HTML to theme developers. The theme debug mode is based on the one - used with Twig in Drupal 8 and can be accessed by setting the "theme_debug" - variable to TRUE (API addition). -- Added an entity_view_mode_prepare() API function to allow entity-defining - modules to properly invoke hook_entity_view_mode_alter(), and used it - throughout Drupal core to fix bugs with the invocation of that hook (API - change: https://www.drupal.org/node/2369141). -- Security improvement: Made the database API's orderBy() method sanitize the - sort direction ("ASC" or "DESC") for queries built with db_select(), so that - calling code does not have to. -- Changed the RDF module to consistently output RDF metadata for nodes and - comments near where the node is rendered in the HTML (minor markup and data - structure change). -- Added an HTML class to RDFa metatags throughout Drupal to prevent them from - accidentally affecting the site appearance (minor markup change). -- Fixed a bug in the Unicode requirements check which prevented installing - Drupal on PHP 5.6. -- Fixed a bug which caused drupal_get_bootstrap_phase() to abort the bootstrap - when called early in the page request. -- Renamed the "Search result" view mode to "Search result highlighting input" - to better reflect how it is used (UI change). -- Improved database queries generated by EntityFieldQuery in the case where - delta or language condition groups are used, to reduce the number of INNER - JOINs (this is a minor data structure change affecting code which implements - hook_query_alter() on these queries). -- Removed special-case behavior for file uploads which allowed user #1 to - bypass maximum file size and user quota limits. -- Numerous small bug fixes. -- Numerous API documentation improvements. -- Additional automated test coverage. - -Drupal 7.32, 2014-10-15 ----------------------- -- Fixed security issues (SQL injection). See SA-CORE-2014-005. - -Drupal 7.31, 2014-08-06 ----------------------- -- Fixed security issues (denial of service). See SA-CORE-2014-004. - -Drupal 7.30, 2014-07-24 ------------------------ -- Fixed a regression introduced in Drupal 7.29 that caused files or images - attached to taxonomy terms to be deleted when the taxonomy term was edited - and resaved (and other related bugs with contributed and custom modules). -- Added a warning on the permissions page to recommend restricting access to - the "View site reports" permission to trusted administrators. See - DRUPAL-PSA-2014-002. -- Numerous API documentation improvements. -- Additional automated test coverage. - -Drupal 7.29, 2014-07-16 ----------------------- -- Fixed security issues (multiple vulnerabilities). See SA-CORE-2014-003. - -Drupal 7.28, 2014-05-08 ------------------------ -- Fixed a regression introduced in Drupal 7.27 that caused JavaScript to break - on older browsers (such as Internet Explorer 8 and earlier) when Ajax was - used. -- Increased the timeout used by the Update Manager module when it fetches data - from drupal.org (from 5 seconds to 30 seconds), to work around a problem - which causes incomplete information about security updates to be presented to - site administrators. This fix may lead to a performance slowdown on the - Update Manager administration pages, when installing Drupal distributions, - and (for sites that use the automated cron feature) on occasional page loads - by site visitors. -- Fixed the behavior of the token system's "[node:summary]" token when the body - field does not have a manual summary. -- Changed the behavior of db_query_temporary() so that it works on SELECT - queries even when they have leading comments/whitespace. A side effect of - this fix is that db_query_temporary() will now fail with an error if it is - ever used on non-SELECT queries. -- Added a "node_admin_filter" tag to the database query used to build the list - of nodes on the content administration page, to make it easier to alter. -- Made the cron queue system log any exceptions that are thrown while an item - in the queue is being processed, rather than stopping the entire PHP request. -- Improved screen reader support by adding an aria-live HTML attribute to file - upload fields when there is an error uploading the file (minor markup - change). -- Made the pager on the Tracker module listing pages show the same number of - items as other pagers throughout Drupal core (minor UI change). -- Fixed a bug which caused caches not to be properly cleared when a file entity - was saved or deleted. -- Added several missing countries to the default list returned by - country_get_list() (string change). -- Replaced the term "weight" with "influence" in the content ranking settings - for search, and added help text for administrators (string change). -- Fixed untranslatable text strings in the administrative interface for the - "Crop" effect provided by the Image module (minor string change). -- Fixed a bug in the Taxonomy module update function introduced in Drupal 7.26 - that caused memory and CPU problems on sites with very large numbers of - unpublished nodes. -- Numerous small bug fixes. -- Numerous API documentation improvements. -- Additional automated test coverage. - -Drupal 7.27, 2014-04-16 ----------------------- -- Fixed security issues (information disclosure). See SA-CORE-2014-002. - -Drupal 7.26, 2014-01-15 ----------------------- -- Fixed security issues (multiple vulnerabilities). See SA-CORE-2014-001. - -Drupal 7.25, 2014-01-02 ------------------------ -- Fixed a bug in node_save() which prevented the saved node from being updated - in hook_node_insert() and other similar hooks. -- Added a meta tag to install.php to prevent it from being indexed by search - engines even when Drupal is installed in a subfolder (minor markup change). -- Fixed a bug in the database API that caused frequent deadlock errors when - running merge queries on some servers. -- Performance improvement: Prevented block rehashing from writing blocks to the - database on every cache clear and cron run when the blocks have not changed. - This fix results in an extra 'saved' key which is added and set to TRUE for - each block returned by _block_rehash() that actually is saved to the database - (data structure change). -- Added an optional 'skip on cron' parameter to hook_cron_queue_info() to allow - queues to avoid being automatically processed on cron runs (API addition). -- Fixed a bug which caused hook_block_view_MODULE_DELTA_alter() to never be - invoked if the block delta had a hyphen in it. To implement the hook when the - block delta has a hyphen, modules should now replace hyphens with underscores - when constructing the function name for the hook implementation. -- Fixed a bug which caused cached pages to sometimes be sent to the browser - with incorrect compression. The fix adds a new 'page_compressed' key to the - $cache->data array returned by drupal_page_get_cache() (minor data structure - change). -- Fixed broken tests on PHP 5.5. -- Made the File and Image modules more robust when saving entities that have - deleted files attached. The code in file_field_presave() will now remove the - record of the deleted file from the entity before saving (minor data - structure change). -- Standardized menu callback functions throughout Drupal core to return - MENU_NOT_FOUND and MENU_ACCESS_DENIED rather than printing their own "page - not found" or "access denied" pages (minor API change in the return value of - these functions under some circumstances). -- Fixed a bug in which caches were not properly cleared when a node was deleted - via the administrative interface. -- Changed the Bartik theme to render content contained in
,  and
-  similar tags in a larger font size, so it is easier to read.
-- Fixed a bug in the Search module that caused exceptions to be thrown during
-  searches if the server was not configured to represent decimal points as a
-  period.
-- Fixed a regression in the Image module that made image_style_url() not work
-  when a relative path (rather than a complete file URI) was passed to it.
-- Added an optional feature to the Statistics module to allow node views to be
-  tracked by Ajax requests rather than during the server-side generation of the
-  page. This allows the node counter to work on sites that use external page
-  caches (string change and new administrative option:
-  https://drupal.org/node/2164069).
-- Added a link to the drupal.org documentation page for cron to the Cron
-  settings page (string change).
-- Added a 'drupal_anonymous_user_object' variable to allow the anonymous user
-  object returned by drupal_anonymous_user() to be overridden with a classed
-  object (API addition).
-- Changed the database API to allow inserts based on a SELECT * query to work
-  correctly.
-- Changed the database schema of the {file_managed} table to allow Drupal to
-  manage files larger than 4 GB.
-- Changed the File module's hook_field_load() implementation to prevent file
-  entity properties which have the same name as file or image field properties
-  from overwriting the field properties (minor API change).
-- Numerous small bug fixes.
-- Numerous API documentation improvements.
-- Additional automated test coverage.
-
-Drupal 7.24, 2013-11-20
-----------------------
-- Fixed security issues (multiple vulnerabilities), see SA-CORE-2013-003.
-
-Drupal 7.23, 2013-08-07
------------------------
-- Fixed a fatal error on PostgreSQL databases when updating the Taxonomy module
-  from Drupal 6 to Drupal 7.
-- Fixed the default ordering of CSS files for sites using right-to-left
-  languages, to consistently place the right-to-left override file immediately
-  after the CSS it is overriding (API change: https://drupal.org/node/2058463).
-- Added a drupal_check_memory_limit() API function to allow the memory limit to
-  be checked consistently (API addition).
-- Changed the default web.config file for IIS servers to allow favicon.ico
-  files which are present in the filesystem to be accessed.
-- Fixed inconsistent support for the 'tel' protocol in Drupal's URL filtering
-  functions.
-- Performance improvement: Allowed all hooks to be included in the
-  module_implements() cache, even those that are only invoked on HTTP POST
-  requests.
-- Made the database system replace truncate queries with delete queries when
-  inside a transaction, to fix issues with PostgreSQL and other databases.
-- Fixed a bug which caused nested contextual links to display improperly.
-- Fixed a bug which prevented cached image derivatives from being flushed for
-  private files and other non-default file schemes.
-- Fixed drupal_render() to always return an empty string when there is no
-  output, rather than sometimes returning NULL (minor API change).
-- Added protection to cache_clear_all() to ensure that non-cache tables cannot
-  be truncated (API addition: a new isValidBin() method has been added to the
-  default database cache implementation).
-- Changed the default .htaccess file to support HTTP authorization in CGI
-  environments.
-- Changed the password reset form to pre-fill the username when requested via a
-  URL query parameter, and used this in the error message that appears after a
-  failed login attempt (minor data structure and behavior change).
-- Fixed broken support for foreign keys in the field API.
-- Fixed "No active batch" error when a user cancels their own account.
-- Added a description to the "access content overview" permission on the
-  permissions page (string change).
-- Added a drupal_array_diff_assoc_recursive() function to allow associative
-  arrays to be compared recursively (API addition).
-- Added human-readable labels to image styles, in addition to the existing
-  machine-readable name (API change: https://drupal.org/node/2058503).
-- Moved the drupal_get_hash_salt() function to bootstrap.inc and used it in
-  additional places in the code, for added security in the case where there is
-  no hash salt in settings.php.
-- Fixed a regression in Drupal 7.22 that caused internal server errors for
-  sites running on very old Apache 1.x web servers.
-- Numerous small bug fixes.
-- Numerous API documentation improvements.
-- Additional automated test coverage.
-
-Drupal 7.22, 2013-04-03
------------------------
-- Allowed the drupal_http_request() function to be overridden so that
-  additional HTTP request capabilities can be added by contributed modules.
-- Changed the Simpletest module to allow PSR-0 test classes to be used in
-  Drupal 7.
-- Removed an unnecessary "Content-Disposition" header from private file
-  downloads; it prevented many private files from being viewed inline in a web
-  browser.
-- Changed various field API functions to allow them to optionally act on a
-  single field within an entity (API addition: http://drupal.org/node/1825844).
-- Fixed a bug which prevented Drupal's file transfer functionality from working
-  on some PHP 5.4 systems.
-- Fixed incorrect log message when theme() is called for a theme hook that does
-  not exist (minor string change).
-- Fixed Drupal's token-replacement system to allow spaces in the token value.
-- Changed the default behavior after a user creates a node they do not have
-  access to view. The user will now be redirected to the front page rather than
-  an access denied page.
-- Fixed a bug which prevented empty HTTP headers (such as "0") from being set.
-  (Minor behavior change: Callers of drupal_add_http_header() must now set
-  FALSE explicitly to prevent a header from being sent at all; this was already
-  indicated in the function's documentation.)
-- Fixed OpenID errors when more than one module implements hook_openid(). The
-  behavior is now changed so that if more than one module tries to set the same
-  parameter, the last module's change takes effect.
-- Fixed a serious documentation bug: The $name variable in the
-  taxonomy-term.tpl.php theme template was incorrectly documented as being
-  sanitized when in fact it is not.
-- Fixed a bug which prevented Drupal 6 to Drupal 7 upgrades on sites which had
-  duplicate permission names in the User module's database tables.
-- Added an empty "datatype" attribute to taxonomy term and username links to
-  make the RDFa markup upward compatible with RDFa 1.1 (minor markup addition).
-- Fixed a bug which caused the denial-of-service protection added in Drupal
-  7.20 to break certain valid image URLs that had an extra slash in them.
-- Fixed a bug with update queries in the SQLite database driver that prevented
-  Drupal from being installed with SQLite on PHP 5.4.
-- Fixed enforced dependencies errors updating to recent versions of Drupal 7 on
-  certain non-MySQL databases.
-- Refactored the Field module's caching behavior to obtain large improvements
-  in memory usage for sites with many fields and instances (API addition:
-  http://drupal.org/node/1915646).
-- Fixed entity argument not being passed to implementations of
-  hook_file_download_access_alter(). The fix adds an additional context
-  parameter that can be passed when calling drupal_alter() for any hook (API
-  change: http://drupal.org/node/1882722).
-- Fixed broken support for translatable comment fields (API change:
-  http://drupal.org/node/1874724).
-- Added an assertThemeOutput() method to Simpletest to allow tests to check
-  that themed output matches an expected HTML string (API addition).
-- Added a link to "Install another module" after a module has been successfully
-  downloaded via the Update Manager (UI change).
-- Added an optional "exclusive" flag to installation profile .info files which
-  allows Drupal distributions to force a profile to be selected during
-  installation (API addition: http://drupal.org/node/1961012).
-- Fixed a bug which caused the database API to not properly close database
-  connections.
-- Added a link to the URL for running cron from outside the site to the Cron
-  settings page (UI change).
-- Fixed a bug which prevented image styles from being reverted on PHP 5.4.
-- Made the default .htaccess rules protocol sensitive to improve security for
-  sites which use HTTPS and redirect between "www" and non-"www" versions of
-  the page.
-- Numerous small bug fixes.
-- Numerous API documentation improvements.
-- Additional automated test coverage.
-
-Drupal 7.21, 2013-03-06
------------------------
-- Allowed sites using the 'image_allow_insecure_derivatives' variable to still
-  have partial protection from the security issues fixed in Drupal 7.20.
-
-Drupal 7.20, 2013-02-20
------------------------
-- Fixed security issues (denial of service). See SA-CORE-2013-002.
-
-Drupal 7.19, 2013-01-16
------------------------
-- Fixed security issues (multiple vulnerabilities). See SA-CORE-2013-001.
-
-Drupal 7.18, 2012-12-19
------------------------
-- Fixed security issues (multiple vulnerabilities). See SA-CORE-2012-004.
-
-Drupal 7.17, 2012-11-07
------------------------
-- Changed the default value of the '404_fast_html' variable to have a DOCTYPE
-  declaration.
-- Made it possible to use associative arrays for the 'items' variable in
-  theme_item_list().
-- Fixed a bug which prevented required form elements without a title from being
-  given an "error" class when the form fails validation.
-- Prevented duplicate HTML IDs from appearing when two forms are displayed on
-  the same page and one of them is submitted with invalid data (minor markup
-  change).
-- Fixed a bug which prevented Drupal 6 to Drupal 7 upgrades on sites which had
-  stale data in the Upload module's database tables.
-- Fixed a bug in the States API which prevented certain types of form elements
-  from being disabled when requested.
-- Allowed aggregator feed items with author names longer than 255 characters to
-  have a truncated version saved to the database (rather than causing a fatal
-  error).
-- Allowed aggregator feed items to have URLs longer than 255 characters
-  (schema change which results in several columns in the Aggregator module's
-  database tables changing from VARCHAR to TEXT fields).
-- Added hook_taxonomy_term_view() and standardized the process for rendering
-  taxonomy terms to invoke hook_entity_view() and otherwise make it consistent
-  with other entities (API change: http://drupal.org/node/1808870).
-- Added hook_entity_view_mode_alter() to allow modules to change entity view
-  modes on display (API addition: http://drupal.org/node/1833086).
-- Fixed a bug which made database queries running a "LIKE" query on blob fields
-  fail on PostgreSQL databases. This caused errors during the Drupal 6 to
-  Drupal 7 upgrade.
-- Changed the hook_menu() entry for Drupal's rss.xml page to prevent extra path
-  components from being accidentally passed to the page callback function (data
-  structure change).
-- Removed a non-standard "name" attribute from Drupal's default Content-Type
-  header for file downloads.
-- Fixed the theme settings form to properly clean up submitted values in
-  $form_state['values'] when the form is submitted (data structure change).
-- Fixed an inconsistency by removing the colon from the end of the label on
-  multi-valued form fields (minor string change).
-- Added support for 'weight' in hook_field_widget_info() to allow modules to
-  control the order in which widgets are displayed in the Field UI.
-- Updated various tables in the OpenID and Book modules to use the default
-  "empty table" text pattern (string change).
-- Added proxy server support to drupal_http_request().
-- Added "lang" attributes to language links, to better support screen readers.
-- Fixed double occurrence of a "ul" HTML tag on secondary local tasks in the
-  Seven theme (markup change).
-- Fixed bugs which caused taxonomy vocabulary and shortcut set titles to be
-  double-escaped. The fix replaces the taxonomy vocabulary overview page and
-  "Edit shortcuts" menu items' title callback entries in hook_menu() with new
-  functions that do not escape HTML characters (data structure change).
-- Modified the Update manager module to allow drupal.org to collect usage
-  statistics for individual modules and themes, rather than only for entire
-  projects.
-- Modified the node listing database query on Drupal's default front page to
-  add table aliases for better query altering (this is a data structure change
-  affecting code which implements hook_query_alter() on this query).
-- Improved the translatability of the "Field type(s) in use" message on the
-  modules page (admin-facing string change).
-- Fixed a regression which caused a "call to undefined function
-  drupal_find_base_themes()" fatal error under rare circumstances.
-- Numerous API documentation improvements.
-- Additional automated test coverage.
-
-Drupal 7.16, 2012-10-17
------------------------
-- Fixed security issues (Arbitrary PHP code execution and information
-  disclosure). See SA-CORE-2012-003.
-
-Drupal 7.15, 2012-08-01
------------------------
-- Introduced a 'user_password_reset_timeout' variable to allow the 24-hour
-  expiration for user password reset links to be adjusted (API addition).
-- Fixed database errors due to ambiguous column names that occurred when
-  EntityFieldQuery was used in certain situations.
-- Changed the drupal_array_get_nested_value() function to return a reference
-  (API addition).
-- Changed the System module's hook_block_info() implementation to assign the
-  "Main page content" and "System help" blocks to appropriate regions by
-  default and prevent error messages on the block administration page (data
-  structure change).
-- Fixed regression: Non-node entities couldn't be accessed with
-  EntityFieldQuery.
-- Fixed regression: Optional radio buttons with an empty, non-NULL default
-  value led to an illegal choice error when none were selected.
-- Reorganized the testing framework to split setUp() into specific sub-methods
-  and fix several regressions in the process.
-- Fixed bug which made it impossible to search for strings that have not been
-  translated into a particular language.
-- Renamed the "Field" column on the Manage Fields screen to "Field type", since
-  the former was confusing and inaccurate (UI change).
-- Performance improvement: Removed needless call to system_rebuild_module_data()
-  in field_sync_field_status(), greatly speeding up bulk module enable/disable.
-- Fixed bug which prevented notifications from being sent when core, module, and
-  theme updates are available.
-- Fixed bug which prevented sub-themes from inheriting the default values of
-  theme settings defined by the base theme.
-- Fixed bug which prevented the jQuery UI Datepicker from being localized.
-- Made Ajax alert dialogs respect error reporting settings.
-- Fixed bug which prevented image styles from being deleted on PHP 5.4.
-- Fixed bug: Language detection by domain only worked on port 80.
-- Fixed regression: The first plural index on a page was not calculated
-  correctly.
-- Introduced generic entity language support. Entities may now declare their
-  language property in hook_entity_info(), and modules working with entities
-  may access the language using entity_language() (API change:
-  http://drupal.org/node/1626346).
-- Added EntityFieldQuery support for taxonomy bundles.
-- Fixed issue where field form structure was incomplete if field_access()
-  returned FALSE. Instead of being incomplete, the form structure now has
-  #access set to FALSE and field form validation is skipped (data structure
-  change: http://drupal.org/node/1663020).
-- Fixed data loss issue due to field_has_data() returning inconsistent results.
-  The fix adds an optional DANGEROUS_ACCESS_CHECK_OPT_OUT tag to entity field
-  queries which field storage engines can respond to (API addition:
-  http://drupal.org/node/1597378).
-- Fixed notice: Undefined index: default_image in image_field_prepare_view()
-- Numerous API documentation improvements.
-- Additional automated test coverage.
-
-Drupal 7.14 2012-05-02
-----------------------
-- Fixed "integrity constraint" fatal errors when rebuilding registry.
-- Fixed custom logo and favicon functionality referencing incorrect paths.
-- Fixed DB Case Sensitivity: Allow BINARY attribute in MySQL.
-- Split field_bundle_settings out per bundle.
-- Improve UX for machine names for fields (UI change).
-- Fixed User pictures are not removed properly.
-- Fixed HTTPS sessions not working in all cases.
-- Fixed Regression: Required radios throw illegal choice error when none
-  selected.
-- Fixed allow autocompletion requests to include slashes.
-- Eliminate $user->cache and {session}.cache in favor of
-  $_SESSION['cache_expiration'][$bin] (Performance).
-- Fixed focus jumps to tab when pressing enter on a form element within tab.
-- Fixed race condition in locale() - duplicates in {locales_source}.
-- Fixed Missing "Default image" per field instance.
-- Quit clobbering people's work when they click the filter tips link
-- Form API #states: Fix conditionals to allow OR and XOR constructions.
-- Fixed Focus jumps to tab when pressing enter on a form element within tab.
-  (Accessibility)
-- Improved performance of node_access queries.
-- Fixed Fieldsets inside vertical tabs have no title and can't be collapsed.
-- Reduce size of cache_menu table (Performance).
-- Fixed unnecessary aggregation of CSS/JS (Performance).
-- Fixed taxonomy_autocomplete() produces SQL error for nonexistent field.
-- Fixed HTML filter is not run first by default, despite default weight.
-- Fixed Overlay does not work with prefixed URL paths.
-- Better debug info for field errors (string change).
-- Fixed Data corruption in comment IDs (results in broken threading on
-  PostgreSQL).
-- Fixed machine name not editable if every character is replaced.
-- Fixed user picture not appearing in comment preview (Markup change).
-- Added optional vid argument for taxonomy_get_term_by_name().
-- Fixed Invalid Unicode code range in PREG_CLASS_UNICODE_WORD_BOUNDARY fails
-  with PCRE 8.30.
-- Fixed {trigger_assignments()}.hook has only 32 characters, is too short.
-- Numerous fixes to run-tests.sh.
-- Fixed Tests in profiles/[name]/modules cannot be run and cannot use a
-  different profile for running tests.
-- Numerous JavaScript performance fixes.
-- Numerous documentation fixes.
-- Fixed All pager links have an 'active' CSS class.
-- Numerous upgrade path fixes; notably:
-  - system_update_7061() fails on inserting files with same name but different
-    case.
-  - system_update_7061() converts filepaths too aggressively.
-  - Trigger upgrade path: Node triggers removed when upgrading to 7-x from 6.25.
-
-Drupal 7.13 2012-05-02
-----------------------
-- Fixed security issues (Multiple vulnerabilities), see SA-CORE-2012-002.
-
-Drupal 7.12, 2012-02-01
-----------------------
-- Fixed bug preventing custom menus from receiving an active trail.
-- Fixed hook_field_delete() no longer invoked during field_purge_data().
-- Fixed bug causing entity info cache to not be cleared with the rest of caches.
-- Fixed file_unmanaged_copy() fails with Drupal 7.7+ and safe_mode() or
-  open_basedir().
-- Fixed Nested transactions throw exceptions when they got out of scope.
-- Fixed bugs with the Return-Path when sending mail on both Windows and
-  non-Windows systems.
-- Fixed bug with DrupalCacheArray property visibility preventing others from
-  extending it (API change: http://drupal.org/node/1422264).
-- Fixed bug with handling of non-ASCII characters in file names (API change:
-  http://drupal.org/node/1424840).
-- Reconciled field maximum length with database column size in image and
-  aggregator modules.
-- Fixes to various core JavaScript files to allow for minification and
-  aggregation.
-- Fixed Prevent tests from deleting main installation's tables when
-  parent::setUp() is not called.
-- Fixed several Poll module bugs.
-- Fixed several Shortcut module bugs.
-- Added new hook_system_theme_info() to provide ability for contributed modules
-  to test theme functionality.
-- Added ability to cancel mail sending from hook_mail_alter().
-- Added support for configurable PDO connection options, enabling master-master
-  database replication.
-- Numerous improvements to tests and test runner to pave the way for faster test
-  runs.
-- Expanded test coverage.
-- Numerous API documentation improvements.
-- Numerous performance improvements, including token replacement and render
-  cache.
-
-Drupal 7.11, 2012-02-01
-----------------------
-- Fixed security issues (Multiple vulnerabilities), see SA-CORE-2012-001.
-
-Drupal 7.10, 2011-12-05
-----------------------
-- Fixed Content-Language HTTP header to not cause issues with Drush 5.x.
-- Reduce memory usage of theme registry (performance).
-- Fixed PECL upload progress bar for FileField
-- Fixed running update.php doesn't always clear the cache.
-- Fixed PDO exceptions on long titles.
-- Fixed Overlay redirect does not include query string.
-- Fixed D6 modules satisfy D7 module dependencies.
-- Fixed the ordering of module hooks when using module_implements_alter().
-- Fixed "floating" submit buttons during AJAX requests.
-- Fixed timezone selected on install not propogating to admin account.
-- Added msgctx context to JS translation functions, for feature parity with t().
-- Profiles' .install files now available during hook_install_tasks().
-- Added test coverage of 7.0 -> 7.x upgrade path.
-- Numerous notice fixes.
-- Numerous documentation improvements.
-- Additional automated test coverage.
-
-Drupal 7.9, 2011-10-26
-----------------------
-- Critical fixes to OpenID to spec violations that could allow for
-  impersonation in certain scenarios. Existing OpenID users should see
-  http://drupal.org/node/1120290#comment-5092796 for more information on
-  transitioning.
-- Fixed files getting lost when adding multiple files to multiple file fields
-  at the same time.
-- Improved usability of the clean URL test screens.
-- Restored height/width attributes on images run through the theme system.
-- Fixed usability bug with first password field being pre-filled by certain
-  browser plugins.
-- Fixed file_usage_list() so that it can return more than one result.
-- Fixed bug preventing preview of private images on node form.
-- Fixed PDO error when inserting an aggregator title longer than 255 characters.
-- Spelled out what TRADITIONAL means in MySQL sql_mode.
-- Deprecated "!=" operator for DBTNG; should be "<>".
-- Added two new API functions (menu_tree_set_path()/menu_tree_get_path()) were
-  added in order to enable setting the active menu trail for dynamically
-  generated menu paths.
-- Added new "fast 404" capability in settings.php to bypass Drupal bootstrap
-  when serving 404 pages for certain file types.
-- Added format_string() function which can perform string munging ala the t()
-  function without the overhead of the translation system.
-- Numerous #states system fixes.
-- Numerous EntityFieldQuery, DBTNG, and SQLite fixes.
-- Numerous Shortcut module fixes.
-- Numerous language system fixes.
-- Numerous token fixes.
-- Numerous CSS fixes.
-- Numerous upgrade path fixes.
-- Numerous minor string fixes.
-- Numerous notice fixes.
-
-Drupal 7.8, 2011-08-31
-----------------------
-- Fixed critical upgrade path issue with multilingual sites, leading to lost
-  content.
-- Numerous fixes to upgrade path, preventing fatal errors due to incorrect
-  dependencies.
-- Fixed issue with saving files on hosts with open_basedir restrictions.
-- Fixed Update manger error when used with Overlay.
-- Fixed RTL support in Seven administration theme and Overlay.
-- Fixes to nested transaction support.
-- Introduced performance pattern to reduce Drupal core's RAM usage.
-- Added support for HTML 5 tags to filter_xss_admin().
-- Added exception handling to cron.
-- Added new hook hook_field_widget_form_alter() for contribtued modules.
-- element_validate_*() functions now available to contrib.
-- Added new maintainers for several subsystems.
-- Numerous testing system improvements.
-- Numerous markup and CSS fixes.
-- Numerous poll module fixes.
-- Numerous notice/warning fixes.
-- Numerous documentation fixes.
-- Numerous token fixes.
-
-Drupal 7.7, 2011-07-27
-----------------------
-- Fixed VERSION string.
-
-Drupal 7.6, 2011-07-27
-----------------------
-- Fixed support for remote streamwrappers.
-- AJAX now binds to 'click' instead of 'mousedown'.
-- 'Translatable' flag on fields created in UI now defaults to FALSE, to match those created via the API.
-- Performance enhancement to permissions page on large numbers of permissions.
-- More secure password generation.
-- Fix for temporary directory on Windows servers.
-- run-tests.sh now uses proc_open() instead of pcntl_fork() for better Windows support.
-- Numerous upgrade path fixes.
-- Numerous documentation fixes.
-- Numerous notice fixes.
-- Numerous fixes to improve PHP 5.4 support.
-- Numerous RTL improvements.
-
-Drupal 7.5, 2011-07-27
-----------------------
-- Fixed security issue (Access bypass), see SA-CORE-2011-003.
-
-Drupal 7.4, 2011-06-29
-----------------------
-- Rolled back patch that caused fatal errors in CTools, Feeds, and other modules using the class registry.
-- Fixed critical bug with saving default images.
-- Fixed fatal errors when uninstalling some modules.
-- Added workaround for MySQL transaction support breaking on DDL statments.
-- Improved page caching with external caching systems.
-- Fix to Batch API, which was terminating too early.
-- Numerous upgrade path fixes.
-- Performance fixes.
-- Additional test coverage.
-- Numerous documentation fixes.
-
-Drupal 7.3, 2011-06-29
-----------------------
-- Fixed security issue (Access bypass), see SA-CORE-2011-002.
-
-Drupal 7.2, 2011-05-25
-----------------------
-- Added a default .gitignore file.
-- Improved PostgreSQL and SQLite support.
-- Numerous critical performance improvements.
-- Numerous critical fixes to the upgrade path.
-- Numerous fixes to language and translation systems.
-- Numerous fixes to AJAX and #states systems.
-- Improvements to the locking system.
-- Numerous documentation fixes.
-- Numerous styling and theme system fixes.
-- Numerous fixes for schema mis-matches between Drupal 6 and 7.
-- Minor internal API clean-ups.
-
-Drupal 7.1, 2011-05-25
-----------------------
-- Fixed security issues (Cross site scripting, File access bypass), see SA-CORE-2011-001.
-
-Drupal 7.0, 2011-01-05 
-----------------------
-- Database:
-    * Fully rewritten database layer utilizing PHP 5's PDO abstraction layer.
-    * Drupal now requires MySQL >= 5.0.15 or PostgreSQL >= 8.3.
-    * Added query builders for INSERT, UPDATE, DELETE, MERGE, and SELECT queries.
-    * Support for master/slave replication, transactions, multi-insert queries,
-      and other features.
-    * Added support for the SQLite database engine.
-    * Default to InnoDB engine, rather than MyISAM, on MySQL when available.
-      This offers increased scalability and data integrity.
-- Security:
-    * Protected cron.php -- cron will only run if the proper key is provided.
-    * Implemented a pluggable password system and much stronger password hashes
-      that are compatible with the Portable PHP password hashing framework.
-    * Rate limited login attempts to prevent brute-force password guessing, and
-      improved the flood control API to allow variable time windows and
-      identifiers for limiting user access to resources.
-    * Transformed the "Update status" module into the "Update manager" which
-      can securely install or update modules and themes via a web interface.
-- Usability:
-    * Added contextual links (a.k.a. local tasks) to page elements, such as
-      blocks, nodes, or comments, which allows to perform the most common tasks
-      with a single click only.
-    * Improved installer requirements check.
-    * Improved support for integration of WYSIWYG editors.
-    * Implemented drag-and-drop positioning for input format listings.
-    * Implemented drag-and-drop positioning for language listing.
-    * Implemented drag-and-drop positioning for poll options.
-    * Provided descriptions and human-readable names for user permissions.
-    * Removed comment controls for users.
-    * Removed display order settings for comment module. Comment display
-      order can now be customized using the Views module.
-    * Removed the 'related terms' feature from taxonomy module since this can
-      now be achieved with Field API.
-    * Added additional features to the default installation profile, and
-      implemented a "slimmed down" profile designed for developers.
-    * Added a built-in, automated cron run feature, which is triggered by site
-      visitors.
-    * Added an administrator role which is assigned all permissions for
-      installed modules automatically.
-    * Image toolkits are now provided by modules (rather than requiring a
-      manual file copy to the includes directory).
-    * Added an edit tab to taxonomy term pages.
-    * Redesigned password strength validator.
-    * Redesigned the add content type screen.
-    * Highlight duplicate URL aliases.
-    * Renamed "input formats" to "text formats".
-    * Moved text format permissions to the main permissions page.
-    * Added configurable ability for users to cancel their own accounts.
-    * Added "vertical tabs", a reusable interface component that features
-      automatic summaries and increases usability.
-    * Replaced fieldsets on node edit and add pages with vertical tabs.
-- Performance:
-    * Improved performance on uncached page views by loading multiple core
-      objects in a single database query.
-    * Improved performance for logged-in users by reducing queries for path
-      alias lookups.
-    * Improved support for HTTP proxies (including reverse proxies), allowing
-      anonymous page views to be served entirely from the proxy.
-- Documentation:
-    * Hook API documentation now included in Drupal core.
-- News aggregator:
-    * Added OPML import functionality for RSS feeds.
-    * Optionally, RSS feeds may be configured to not automatically generate feed blocks.
-- Search:
-    * Added support for language-aware searches.
-- Aggregator:
-    * Introduced architecture that allows pluggable parsers and processors for
-      syndicating RSS and Atom feeds.
-    * Added options to suspend updating specific feeds and never discard feeds
-      items.
-- Testing:
-    * Added test framework and tests.
-- Improved time zone support:
-    * Drupal now uses PHP's time zone database when rendering dates in local
-      time. Site-wide and user-configured time zone offsets have been converted
-      to time zone names, e.g. Africa/Abidjan.
-    * In some cases the upgrade and install scripts do not choose the preferred
-      site default time zone. The automatically-selected time zone can be
-      corrected at admin/config/regional/settings.
-    * If your site is being upgraded from Drupal 6 and you do not have the
-      contributed date or event modules installed, user time zone settings will
-      fallback to the system time zone and will have to be reconfigured by each user.
-    * User-configured time zones now serve as the default time zone for PHP
-      date/time functions.
-- Filter system:
-    * Revamped the filter API and text format storage.
-    * Added support for default text formats to be assigned on a per-role basis.
-    * Refactored the HTML corrector to take advantage of PHP 5 features.
-- User system:
-    * Added clean API functions for creating, loading, updating, and deleting
-      user roles and permissions.
-    * Refactored the "access rules" component of user module: The user module
-      now provides a simple interface for blocking single IP addresses. The
-      previous functionality in the user module for restricting certain e-mail
-      addresses and usernames is now available as a contributed module. Further,
-      IP address range blocking is no longer supported and should be implemented
-      at the operating system level.
-    * Removed per-user themes: Contributed modules with similar functionality
-      are available.
-- OpenID:
-    * Added support for Gmail and Google Apps for Domain identifiers. Users can
-      now login with their user@example.com identifier when example.com is powered
-      by Google.
-    * Made the OpenID module more pluggable.
-- Added code registry:
-    * Using the registry, modules declare their includable files via their .info file,
-      allowing Drupal to lazy-load classes and interfaces as needed.
-- Theme system:
-    * Removed the Bluemarine, Chameleon and Pushbutton themes. These themes live
-      on as contributed themes (http://drupal.org/project/bluemarine,
-      http://drupal.org/project/chameleon and http://drupal.org/project/pushbutton).
-    * Added Stark theme to make analyzing Drupal's default HTML and CSS easier.
-    * Added Seven as the default administration theme.
-    * Variable preprocessing of theme hooks prior to template rendering now goes
-      through two phases: a 'preprocess' phase and a new 'process' phase. See
-      http://api.drupal.org/api/function/theme/7 for details.
-    * Theme hooks implemented as functions (rather than as templates) can now
-      also have preprocess (and process) functions. See
-      http://api.drupal.org/api/function/theme/7 for details.
-    * Added Bartik as the default theme.
-- File handling:
-    * Files are now first class Drupal objects with file_load(), file_save(),
-      and file_validate() functions and corresponding hooks.
-    * The file_move(), file_copy() and file_delete() functions now operate on
-      file objects and invoke file hooks so that modules are notified and can
-      respond to changes.
-    * For the occasions when only basic file manipulation are needed--such as
-      uploading a site logo--that don't require the overhead of databases and
-      hooks, the current unmanaged copy, move and delete operations have been
-      preserved but renamed to file_unmanaged_*().
-    * Rewrote file handling to use PHP stream wrappers to enable support for
-      both public and private files and to support pluggable storage mechanisms
-      and access to remote resources (e.g. S3 storage or Flickr photos).
-    * The mime_extension_mapping variable has been removed. Modules that need to
-      alter the default MIME type extension mappings should implement
-      hook_file_mimetype_mapping_alter().
-    * Added the hook_file_url_alter() hook, which makes it possible to serve
-      files from a CDN.
-    * Added a field specifically for uploading files, previously provided by
-      the contributed module FileField.
-- Image handling:
-    * Improved image handling, including better support for add-on image
-      libraries.
-    * Added API and interface for creating advanced image thumbnails.
-    * Inclusion of additional effects such as rotate and desaturate.
-    * Added a field specifically for uploading images, previously provided by
-      the contributed module ImageField.
-- Added aliased multi-site support:
-    * Added support for mapping domain names to sites directories.
-- Added RDF support:
-    * Modules can declare RDF namespaces which are serialized in the  tag
-      for RDFa support.
-    * Modules can specify how their data structure maps to RDF.
-    * Added support for RDFa export of nodes, comments, terms, users, etc. and
-      their fields.
-- Search engine optimization and web linking:
-    * Added a rel="canonical" link on node and comment pages to prevent
-      duplicate content indexing by search engines.
-    * Added a default rel="shortlink" link on node and comment pages that
-      advertises a short link as an alternative URL to third-party services.
-    * Meta information is now alterable by all modules before rendering.
-- Field API:
-    * Custom data fields may be attached to nodes, users, comments and taxonomy
-      terms.
-    * Node bodies and teasers are now Field API fields instead of
-      being a hard-coded property of node objects.
-    * In addition, any other object type may register with Field API
-      and allow custom data fields to be attached to itself.
-    * Provides most of the features of the former Content Construction
-      Kit (CCK) module.
-    * Taxonomy terms are now Field API fields that can be added to any fieldable
-      object.
-- Installer:
-    * Refactored the installer into an API that allows Drupal to be installed
-      via a command line script.
-- Page organization
-    * Made the help text area a full featured region with blocks.
-    * Site mission is replaced with the highlighted content block region and
-      separate RSS feed description settings.
-    * The footer message setting was removed in favor of custom blocks.
-    * Made the main page content a block which can be moved and ordered
-      with other blocks in the same region.
-    * Blocks can now return structured arrays for later rendering just
-      like page callbacks.
-- Translation system
-    * The translation system now supports message context (msgctxt).
-    * Added support for translatable fields to Field API.
-- JavaScript changes
-    * Upgraded the core JavaScript library to jQuery version 1.4.4.
-    * Upgraded the jQuery Forms library to 2.52.
-    * Added jQuery UI 1.8.7, which allows improvements to Drupal's user
-      experience.
-- Better module version support
-    * Modules now can specify which version of another module they depend on.
-- Removed modules from core
-    * The following modules have been removed from core, because contributed
-      modules with similar functionality are available:
-      * Blog API module
-      * Ping module
-      * Throttle module
-- Improved node access control system.
-    * All modules may now influence the access to a node at runtime, not just
-      the module that defined a node.
-    * Users may now be allowed to bypass node access restrictions without giving
-      them complete access to the site.
-    * Access control affects both published and unpublished nodes.
-    * Numerous other improvements to the node access system.
-- Actions system
-    * Simplified definitions of actions and triggers.
-    * Removed dependency on the combination of hooks and operations. Triggers
-      now directly map to module hooks.
-- Task handling
-    * Added a queue API to process many or long-running tasks.
-    * Added queue API support to cron API.
-    * Added a locking framework to coordinate long-running operations across
-      requests.
-
-Drupal 6.23-dev, xxxx-xx-xx (development release)
------------------------
-
-Drupal 6.22, 2011-05-25
------------------------
-- Made Drupal 6 work better with IIS and Internet Explorer.
-- Fixed .po file imports to work better with custom textgroups.
-- Improved code documentation at various places.
-- Fixed a variety of other bugs.
-
-Drupal 6.21, 2011-05-25
-----------------------
-- Fixed security issues (Cross site scripting), see SA-CORE-2011-001.
-
-Drupal 6.20, 2010-12-15
-----------------------
-- Fixed a variety of small bugs, improved code documentation.
-
-Drupal 6.19, 2010-08-11
-----------------------
-- Fixed a variety of small bugs, improved code documentation.
-
-Drupal 6.18, 2010-08-11
-----------------------
-- Fixed security issues (OpenID authentication bypass, File download access
-  bypass, Comment unpublishing bypass, Actions cross site scripting),
-  see SA-CORE-2010-002.
-
-Drupal 6.17, 2010-06-02
-----------------------
-- Improved PostgreSQL compatibility
-- Better PHP 5.3 and PHP 4 compatibility
-- Better browser compatibility of CSS and JS aggregation
-- Improved logging for login failures
-- Fixed an incompatibility with some contributed modules and the locking system
-- Fixed a variety of other bugs.
-
-Drupal 6.16, 2010-03-03
-----------------------
-- Fixed security issues (Installation cross site scripting, Open redirection,
-  Locale module cross site scripting, Blocked user session regeneration),
-  see SA-CORE-2010-001.
-- Better support for updated jQuery versions.
-- Reduced resource usage of update.module.
-- Fixed several issues relating to support of installation profiles and
-  distributions.
-- Added a locking framework to avoid data corruption on long operations.
-- Fixed a variety of other bugs.
-
-Drupal 6.15, 2009-12-16
-----------------------
-- Fixed security issues (Cross site scripting), see SA-CORE-2009-009.
-- Fixed a variety of other bugs.
-
-Drupal 6.14, 2009-09-16
-----------------------
-- Fixed security issues (OpenID association cross site request forgeries,
-  OpenID impersonation and File upload), see SA-CORE-2009-008.
-- Changed the system modules page to not run all cache rebuilds; use the
-  button on the performance settings page to achieve the same effect.
-- Added support for PHP 5.3.0 out of the box.
-- Fixed a variety of small bugs.
-
-Drupal 6.13, 2009-07-01
-----------------------
-- Fixed security issues (Cross site scripting, Input format access bypass and
-  Password leakage in URL), see SA-CORE-2009-007.
-- Fixed a variety of small bugs.
-
-Drupal 6.12, 2009-05-13
-----------------------
-- Fixed security issues (Cross site scripting), see SA-CORE-2009-006.
-- Fixed a variety of small bugs.
-
-Drupal 6.11, 2009-04-29
-----------------------
-- Fixed security issues (Cross site scripting and limited information
-  disclosure), see SA-CORE-2009-005
-- Fixed performance issues with the menu router cache, the update
-  status cache and improved cache invalidation
-- Fixed a variety of small bugs.
-
-Drupal 6.10, 2009-02-25
-----------------------
-- Fixed a security issue, (Local file inclusion on Windows),
-  see SA-CORE-2009-003
-- Fixed node_feed() so custom fields can show up in RSS feeds.
-- Improved PostgreSQL compatibility.
-- Fixed a variety of small bugs.
-
-Drupal 6.9, 2009-01-14
-----------------------
-- Fixed security issues, (Access Bypass, Validation Bypass and Hardening
-  against SQL injection), see SA-CORE-2009-001
-- Made HTTP request checking more robust and informative.
-- Fixed HTTP_HOST checking to work again with HTTP 1.0 clients and
-  basic shell scripts.
-- Removed t() calls from all schema documentation. Suggested best practice
-  changed for contributed modules, see http://drupal.org/node/322731.
-- Fixed a variety of small bugs.
-
-Drupal 6.8, 2008-12-11
-----------------------
-- Removed a previous change incompatible with PHP 5.1.x and lower.
-
-Drupal 6.7, 2008-12-10
-----------------------
-- Fixed security issues, (Cross site request forgery and Cross site scripting), see SA-2008-073
-- Updated robots.txt and .htaccess to match current file use.
-- Fixed a variety of small bugs.
-
-Drupal 6.6, 2008-10-22
-----------------------
-- Fixed security issues, (File inclusion, Cross site scripting), see SA-2008-067
-- Fixed a variety of small bugs.
-
-Drupal 6.5, 2008-10-08
-----------------------
-- Fixed security issues, (File upload access bypass, Access rules bypass,
-  BlogAPI access bypass), see SA-2008-060.
-- Fixed a variety of small bugs.
-
-Drupal 6.4, 2008-08-13
-----------------------
-- Fixed a security issue (Cross site scripting, Arbitrary file uploads via
-  BlogAPI, Cross site request forgeries and Various Upload module
-  vulnerabilities), see SA-2008-047.
-- Improved error messages during installation.
-- Fixed a bug that prevented AHAH handlers to be attached to radios widgets.
-- Fixed a variety of small bugs.
-
-Drupal 6.3, 2008-07-09
-----------------------
-- Fixed security issues, (Cross site scripting, cross site request forgery,
-  session fixation and SQL injection), see SA-2008-044.
-- Slightly modified installation process to prevent file ownership issues on
-  shared hosts.
-- Improved PostgreSQL compatibility (rewritten queries; custom blocks).
-- Upgraded to jQuery 1.2.6.
-- Performance improvements to search, menu handling and form API caches.
-- Fixed Views compatibility issues (Views for Drupal 6 requires Drupal 6.3+).
-- Fixed a variety of small bugs.
-
-Drupal 6.2, 2008-04-09
-----------------------
-- Fixed a variety of small bugs.
-- Fixed a security issue (Access bypasses), see SA-2008-026.
-
-Drupal 6.1, 2008-02-27
-----------------------
-- Fixed a variety of small bugs.
-- Fixed a security issue (Cross site scripting), see SA-2008-018.
-
-Drupal 6.0, 2008-02-13
-----------------------
-- New, faster and better menu system.
-- New watchdog as a hook functionality.
-   * New hook_watchdog that can be implemented by any module to route log
-     messages to various destinations.
-   * Expands the severity levels from 3 (Error, Warning, Notice) to the 8
-     levels defined in RFC 3164.
-   * The watchdog module is now called dblog, and is optional, but enabled by
-     default in the default installation profile.
-   * Extended the database log module so log messages can be filtered.
-   * Added syslog module: useful for monitoring large Drupal installations.
-- Added optional e-mail notifications when users are approved, blocked, or
-  deleted.
-- Drupal works with error reporting set to E_ALL.
-- Added scripts/drupal.sh to execute Drupal code from the command line. Useful
-  to use Drupal as a framework to build command-line tools.
-- Made signature support optional and made it possible to theme signatures.
-- Made it possible to filter the URL aliases on the URL alias administration
-  screen.
-- Language system improvements:
-    * Support for right to left languages.
-    * Language detection based on parts of the URL.
-    * Browser based language detection.
-    * Made it possible to specify a node's language.
-    * Support for translating posts on the site to different languages.
-    * Language dependent path aliases.
-    * Automatically import translations when adding a new language.
-    * JavaScript interface translation.
-    * Automatically import a module's translation upon enabling that module.
-- Moved "PHP input filter" to a standalone module so it can be deleted for
-  security reasons.
-- Usability:
-    * Improved handling of teasers in posts.
-    * Added sticky table headers.
-    * Check for clean URL support automatically with JavaScript.
-    * Removed default/settings.php. Instead the installer will create it from
-      default.settings.php.
-    * Made it possible to configure your own date formats.
-    * Remember anonymous comment posters.
-    * Only allow modules and themes to be enabled that have explicitly been
-      ported to the correct core API version.
-    * Can now specify the minimum PHP version required for a module within the
-      .info file.
-    * Drupal core no longer requires CREATE TEMPORARY TABLES or LOCK TABLES
-      database rights.
-    * Dynamically check password strength and confirmation.
-    * Refactored poll administration.
-    * Implemented drag-and-drop positioning for blocks, menu items, taxonomy
-      vocabularies and terms, forums, profile fields, and input format filters.
-- Theme system:
-    * Added .info files to themes and made it easier to specify regions and
-      features.
-    * Added theme registry: modules can directly provide .tpl.php files for
-      their themes without having to create theme_ functions.
-    * Used the Garland theme for the installation and maintenance pages.
-    * Added theme preprocess functions for themes that are templates.
-    * Added support for themeable functions in JavaScript.
-- Refactored update.php to a generic batch API to be able to run time-consuming
-  operations in multiple subsequent HTTP requests.
-- Installer:
-    * Themed the installer with the Garland theme.
-    * Added form to provide initial site information during installation.
-    * Added ability to provide extra installation steps programmatically.
-    * Made it possible to import interface translations during installation.
-- Added the HTML corrector filter:
-    * Fixes faulty and chopped off HTML in postings.
-    * Tags are now automatically closed at the end of the teaser.
-- Performance:
-    * Made it easier to conditionally load .include files and split up many core
-      modules.
-    * Added a JavaScript aggregator.
-    * Added block-level caching, improving performance for both authenticated
-      and anonymous users.
-    * Made Drupal work correctly when running behind a reverse proxy like
-      Squid or Pound.
-- File handling improvements:
-    * Entries in the files table are now keyed to a user instead of a node.
-    * Added reusable validation functions to check for uploaded file sizes,
-      extensions, and image resolution.
-    * Added ability to create and remove temporary files during a cron job.
-- Forum improvements:
-    * Any node type may now be posted in a forum.
-- Taxonomy improvements:
-    * Descriptions for terms are now shown on taxonomy/term pages as well
-      as RSS feeds.
-    * Added versioning support to categories by associating them with node
-      revisions.
-- Added support for OpenID.
-- Added support for triggering configurable actions.
-- Added the Update status module to automatically check for available updates
-  and warn sites if they are missing security updates or newer versions.
-  Sites deploying from CVS should use http://drupal.org/project/cvs_deploy.
-  Advanced settings provided by http://drupal.org/project/update_advanced.
-- Upgraded the core JavaScript library to jQuery version 1.2.3.
-- Added a new Schema API, which provides built-in support for core and
-  contributed modules to work with databases other than MySQL.
-- Removed drupal.module. The functionality lives on as the Site network
-  contributed module (http://drupal.org/project/site_network).
-- Removed old system updates. Updates from Drupal versions prior to 5.x will
-  require upgrading to 5.x before upgrading to 6.x.
-
-Drupal 5.23, 2010-08-11
------------------------
-- Fixed security issues (File download access bypass, Comment unpublishing
-  bypass), see SA-CORE-2010-002.
-
-Drupal 5.22, 2010-03-03
------------------------
-- Fixed security issues (Open redirection, Locale module cross site scripting,
-  Blocked user session regeneration), see SA-CORE-2010-001.
-
-Drupal 5.21, 2009-12-16
------------------------
-- Fixed a security issue (Cross site scripting), see SA-CORE-2009-009.
-- Fixed a variety of small bugs.
-
-Drupal 5.20, 2009-09-16
------------------------
-- Avoid security problems resulting from writing Drupal 6-style menu
-  declarations.
-- Fixed security issues (session fixation), see SA-CORE-2009-008.
-- Fixed a variety of small bugs.
-
-Drupal 5.19, 2009-07-01
------------------------
-- Fixed security issues (Cross site scripting and Password leakage in URL), see
-  SA-CORE-2009-007.          
-- Fixed a variety of small bugs.
-
-Drupal 5.18, 2009-05-13
------------------------
-- Fixed security issues (Cross site scripting), see SA-CORE-2009-006.
-- Fixed a variety of small bugs.
-
-Drupal 5.17, 2009-04-29
------------------------
-- Fixed security issues (Cross site scripting and limited information
-  disclosure) see SA-CORE-2009-005.
-- Fixed a variety of small bugs.
-
-Drupal 5.16, 2009-02-25
------------------------
-- Fixed a security issue, (Local file inclusion on Windows), see SA-CORE-2009-004.
-- Fixed a variety of small bugs.
-
-Drupal 5.15, 2009-01-14
------------------------
-- Fixed security issues, (Hardening against SQL injection), see
-  SA-CORE-2009-001
-- Fixed HTTP_HOST checking to work again with HTTP 1.0 clients and basic shell
-  scripts.
-- Fixed a variety of small bugs.
-
-Drupal 5.14, 2008-12-11
------------------------
-- removed a previous change incompatible with PHP 5.1.x and lower.
-
-Drupal 5.13, 2008-12-10
------------------------
-- fixed a variety of small bugs.
-- fixed security issues, (Cross site request forgery and Cross site scripting), see SA-2008-073
-- updated robots.txt and .htaccess to match current file use.
-
-Drupal 5.12, 2008-10-22
------------------------
-- fixed security issues, (File inclusion), see SA-2008-067
-
-Drupal 5.11, 2008-10-08
------------------------
-- fixed a variety of small bugs.
-- fixed security issues, (File upload access bypass, Access rules bypass,
-  BlogAPI access bypass, Node validation bypass), see SA-2008-060
-
-Drupal 5.10, 2008-08-13
------------------------
-- fixed a variety of small bugs.
-- fixed security issues, (Cross site scripting, Arbitrary file uploads via
-  BlogAPI and Cross site request forgery), see SA-2008-047
-
-Drupal 5.9, 2008-07-23
-----------------------
-- fixed a variety of small bugs.
-- fixed security issues, (Session fixation), see SA-2008-046
-
-Drupal 5.8, 2008-07-09
-----------------------
-- fixed a variety of small bugs.
-- fixed security issues, (Cross site scripting, cross site request forgery, and
-  session fixation), see SA-2008-044
-
-Drupal 5.7, 2008-01-28
-----------------------
-- fixed the input format configuration page.
-- fixed a variety of small bugs.
-
-Drupal 5.6, 2008-01-10
-----------------------
-- fixed a variety of small bugs.
-- fixed a security issue (Cross site request forgery), see SA-2008-005
-- fixed a security issue (Cross site scripting, UTF8), see SA-2008-006
-- fixed a security issue (Cross site scripting, register_globals), see SA-2008-007
-
-Drupal 5.5, 2007-12-06
-----------------------
-- fixed missing missing brackets in a query in the user module.
-- fixed taxonomy feed bug introduced by SA-2007-031
-
-Drupal 5.4, 2007-12-05
-----------------------
-- fixed a variety of small bugs.
-- fixed a security issue (SQL injection), see SA-2007-031
-
-Drupal 5.3, 2007-10-17
-----------------------
-- fixed a variety of small bugs.
-- fixed a security issue (HTTP response splitting), see SA-2007-024
-- fixed a security issue (Arbitrary code execution via installer), see SA-2007-025
-- fixed a security issue (Cross site scripting via uploads), see SA-2007-026
-- fixed a security issue (User deletion cross site request forgery), see SA-2007-029
-- fixed a security issue (API handling of unpublished comment), see SA-2007-030
-
-Drupal 5.2, 2007-07-26
-----------------------
-- changed hook_link() $teaser argument to match documentation.
-- fixed a variety of small bugs.
-- fixed a security issue (cross-site request forgery), see SA-2007-017
-- fixed a security issue (cross-site scripting), see SA-2007-018
-
-Drupal 5.1, 2007-01-29
-----------------------
-- fixed security issue (code execution), see SA-2007-005
-- fixed a variety of small bugs.
-
-Drupal 5.0, 2007-01-15
-----------------------
-- Completely retooled the administration page
-    * /Admin now contains an administration page which may be themed
-    * Reorganised administration menu items by task and by module
-    * Added a status report page with detailed PHP/MySQL/Drupal information
-- Added web-based installer which can:
-    * Check installation and run-time requirements
-    * Automatically generate the database configuration file
-    * Install pre-made installation profiles or distributions
-    * Import the database structure with automatic table prefixing
-    * Be localized
-- Added new default Garland theme
-- Added color module to change some themes' color schemes
-- Included the jQuery JavaScript library 1.0.4 and converted all core JavaScript to use it
-- Introduced the ability to alter mail sent from system
-- Module system:
-    * Added .info files for module meta-data
-    * Added support for module dependencies
-    * Improved module installation screen
-    * Moved core modules to their own directories
-    * Added support for module uninstalling
-- Added support for different cache backends
-- Added support for a generic "sites/all" directory.
-- Usability:
-    * Added support for auto-complete forms (AJAX) to user profiles.
-    * Made it possible to instantly assign roles to newly created user accounts.
-    * Improved configurability of the contact forms.
-    * Reorganized the settings pages.
-    * Made it easy to investigate popular search terms.
-    * Added a 'select all' checkbox and a range select feature to administration tables.
-    * Simplified the 'break' tag to split teasers from body.
-    * Use proper capitalization for titles, menu items and operations.
-- Integrated urlfilter.module into filter.module
-- Block system:
-    * Extended the block visibility settings with a role specific setting.
-    * Made it possible to customize all block titles.
-- Poll module:
-    * Optionally allow people to inspect all votes.
-    * Optionally allow people to cancel their vote.
-- Distributed authentication:
-    * Added default server option.
-- Added default robots.txt to control crawlers.
-- Database API:
-    * Added db_table_exists().
-- Blogapi module:
-    * 'Blogapi new' and 'blogapi edit' nodeapi operations.
-- User module:
-    * Added hook_profile_alter().
-    * E-mail verification is made optional.
-    * Added mass editing and filtering on admin/user/user.
-- PHP Template engine:
-    * Add the ability to look for a series of suggested templates.
-    * Look for page templates based upon the path.
-    * Look for block templates based upon the region, module, and delta.
-- Content system:
-    * Made it easier for node access modules to work well with each other.
-    * Added configurable content types.
-    * Changed node rendering to work with structured arrays.
-- Performance:
-    * Improved session handling: reduces database overhead.
-    * Improved access checking: reduces database overhead.
-    * Made it possible to do memcached based session management.
-    * Omit sidebars when serving a '404 - Page not found': saves CPU cycles and bandwidth.
-    * Added an 'aggressive' caching policy.
-    * Added a CSS aggregator and compressor (up to 40% faster page loads).
-- Removed the archive module.
-- Upgrade system:
-    * Created space for update branches.
-- Form API:
-    * Made it possible to programmatically submit forms.
-    * Improved api for multistep forms.
-- Theme system:
-    * Split up and removed drupal.css.
-    * Added nested lists generation.
-    * Added a self-clearing block class.
-
-Drupal 4.7.11, 2008-01-10
--------------------------
-- fixed a security issue (Cross site request forgery), see SA-2008-005
-- fixed a security issue (Cross site scripting, UTF8), see SA-2008-006
-- fixed a security issue (Cross site scripting, register_globals), see SA-2008-007
-
-Drupal 4.7.10, 2007-12-06
--------------------------
-- fixed taxonomy feed bug introduced by SA-2007-031
-
-Drupal 4.7.9, 2007-12-05
-------------------------
-- fixed a security issue (SQL injection), see SA-2007-031
-
-Drupal 4.7.8, 2007-10-17
-----------------------
-- fixed a security issue (HTTP response splitting), see SA-2007-024
-- fixed a security issue (Cross site scripting via uploads), see SA-2007-026
-- fixed a security issue (API handling of unpublished comment), see SA-2007-030
-
-Drupal 4.7.7, 2007-07-26
-------------------------
-- fixed security issue (XSS), see SA-2007-018
-
-Drupal 4.7.6, 2007-01-29
-------------------------
-- fixed security issue (code execution), see SA-2007-005
-
-Drupal 4.7.5, 2007-01-05
-------------------------
-- Fixed security issue (XSS), see SA-2007-001
-- Fixed security issue (DoS), see SA-2007-002
-
-Drupal 4.7.4, 2006-10-18
-------------------------
-- Fixed security issue (XSS), see SA-2006-024
-- Fixed security issue (CSRF), see SA-2006-025
-- Fixed security issue (Form action attribute injection), see SA-2006-026
-
-Drupal 4.7.3, 2006-08-02
-------------------------
-- Fixed security issue (XSS), see SA-2006-011
-
-Drupal 4.7.2, 2006-06-01
-------------------------
-- Fixed critical upload issue, see SA-2006-007
-- Fixed taxonomy XSS issue, see SA-2006-008
-- Fixed a variety of small bugs.
-
-Drupal 4.7.1, 2006-05-24
-------------------------
-- Fixed critical SQL issue, see SA-2006-005
-- Fixed a serious upgrade related bug.
-- Fixed a variety of small bugs.
-
-Drupal 4.7.0, 2006-05-01
-------------------------
-- Added free tagging support.
-- Added a site-wide contact form.
-- Theme system:
-    * Added the PHPTemplate theme engine and removed the Xtemplate engine.
-    * Converted the bluemarine theme from XTemplate to PHPTemplate.
-    * Converted the pushbutton theme from XTemplate to PHPTemplate.
-- Usability:
-    * Reworked the 'request new password' functionality.
-    * Reworked the node and comment edit forms.
-    * Made it easy to add nodes to the navigation menu.
-    * Added site 'offline for maintenance' feature.
-    * Added support for auto-complete forms (AJAX).
-    * Added support for collapsible page sections (JS).
-    * Added support for resizable text fields (JS).
-    * Improved file upload functionality (AJAX).
-    * Reorganized some settings pages.
-    * Added friendly database error screens.
-    * Improved styling of update.php.
-- Refactored the forms API.
-    * Made it possible to alter, extend or theme forms.
-- Comment system:
-    * Added support for "mass comment operations" to ease repetitive tasks.
-    * Comment moderation has been removed.
-- Node system:
-    * Reworked the revision functionality.
-    * Removed the bookmarklet code. Third-party modules can now handle
-      This.
-- Upgrade system:
-    * Allows contributed modules to plug into the upgrade system.
-- Profiles:
-    * Added a block to display author information along with posts.
-    * Added support for private profile fields.
-- Statistics module:
-    * Added the ability to track page generation times.
-    * Made it possible to block certain IPs/hostnames.
-- Block system:
-    * Added support for theme-specific block regions.
-- Syndication:
-    * Made the aggregator module parse Atom feeds.
-    * Made the aggregator generate RSS feeds.
-    * Added RSS feed settings.
-- XML-RPC:
-    * Replaced the XML-RPC library by a better one.
-- Performance:
-    * Added 'loose caching' option for high-traffic sites.
-    * Improved performance of path aliasing.
-    * Added the ability to track page generation times.
-- Internationalization:
-    * Improved Unicode string handling API.
-    * Added support for PHP's multibyte string module.
-- Added support for PHP5's 'mysqli' extension.
-- Search module:
-    * Made indexer smarter and more robust.
-    * Added advanced search operators (e.g. phrase, node type, ...).
-    * Added customizable result ranking.
-- PostgreSQL support:
-    * Removed dependency on PL/pgSQL procedural language.
-- Menu system:
-    * Added support for external URLs.
-- Queue module:
-    * Removed from core.
-- HTTP handling:
-    * Added support for a tolerant Base URL.
-    * Output URIs relative to the root, without a base tag.
-
-Drupal 4.6.11, 2007-01-05
--------------------------
-- Fixed security issue (XSS), see SA-2007-001
-- Fixed security issue (DoS), see SA-2007-002
-
-Drupal 4.6.10, 2006-10-18
-------------------------
-- Fixed security issue (XSS), see SA-2006-024
-- Fixed security issue (CSRF), see SA-2006-025
-- Fixed security issue (Form action attribute injection), see SA-2006-026
-
-Drupal 4.6.9, 2006-08-02
-------------------------
-- Fixed security issue (XSS), see SA-2006-011
-
-Drupal 4.6.8, 2006-06-01
-------------------------
-- Fixed critical upload issue, see SA-2006-007
-- Fixed taxonomy XSS issue, see SA-2006-008
-
-Drupal 4.6.7, 2006-05-24
-------------------------
-- Fixed critical SQL issue, see SA-2006-005
-
-Drupal 4.6.6, 2006-03-13
-------------------------
-- Fixed bugs, including 4 security vulnerabilities.
-
-Drupal 4.6.5, 2005-12-12
-------------------------
-- Fixed bugs: no critical bugs were identified.
-
-Drupal 4.6.4, 2005-11-30
-------------------------
-- Fixed bugs, including 3 security vulnerabilities.
-
-Drupal 4.6.3, 2005-08-15
-------------------------
-- Fixed bugs, including a critical "arbitrary PHP code execution" bug.
-
-Drupal 4.6.2, 2005-06-29
-------------------------
-- Fixed bugs, including two critical "arbitrary PHP code execution" bugs.
-
-Drupal 4.6.1, 2005-06-01
-------------------------
-- Fixed bugs, including a critical input validation bug.
-
-Drupal 4.6.0, 2005-04-15
-------------------------
-- PHP5 compliance
-- Search:
-    * Added UTF-8 support to make it work with all languages.
-    * Improved search indexing algorithm.
-    * Improved search output.
-    * Impose a throttle on indexing of large sites.
-    * Added search block.
-- Syndication:
-    * Made the ping module ping pingomatic.com which, in turn, will ping all the major ping services.
-    * Made Drupal generate RSS 2.0 feeds.
-    * Made RSS feeds extensible.
-    * Added categories to RSS feeds.
-    * Added enclosures to RSS feeds.
-- Flood control mechanism:
-    * Added a mechanism to throttle certain operations.
-- Usability:
-    * Refactored the block configuration pages.
-    * Refactored the statistics pages.
-    * Refactored the watchdog pages.
-    * Refactored the throttle module configuration.
-    * Refactored the access rules page.
-    * Refactored the content administration page.
-    * Introduced forum configuration pages.
-    * Added a 'add child page' link to book pages.
-- Contact module:
-    * Added a simple contact module that allows users to contact each other using e-mail.
-- Multi-site configuration:
-    * Made it possible to run multiple sites from a single code base.
-- Added an image API: enables better image handling.
-- Block system:
-    * Extended the block visibility settings.
-- Theme system:
-    * Added new theme functions.
-- Database backend:
-    * The PEAR database backend is no longer supported.
-- Performance:
-    * Improved performance of the forum topics block.
-    * Improved performance of the tracker module.
-    * Improved performance of the node pages.
-- Documentation:
-    * Improved and extended PHPDoc/Doxygen comments.
-
-Drupal 4.5.8, 2006-03-13
-------------------------
-- Fixed bugs, including 3 security vulnerabilities.
-
-Drupal 4.5.7, 2005-12-12
-------------------------
-- Fixed bugs: no critical bugs were identified.
-
-Drupal 4.5.6, 2005-11-30
-------------------------
-- Fixed bugs, including 3 security vulnerabilities.
-
-Drupal 4.5.5, 2005-08-15
-------------------------
-- Fixed bugs, including a critical "arbitrary PHP code execution" bug.
-
-Drupal 4.5.4, 2005-06-29
-------------------------
-- Fixed bugs, including two critical "arbitrary PHP code execution" bugs.
-
-Drupal 4.5.3, 2005-06-01
-------------------------
-- Fixed bugs, including a critical input validation bug.
-
-Drupal 4.5.2, 2005-01-15
-------------------------
-- Fixed bugs: a cross-site scripting (XSS) vulnerability has been fixed.
-
-Drupal 4.5.1, 2004-12-01
-------------------------
-- Fixed bugs: no critical bugs were identified.
-
-Drupal 4.5.0, 2004-10-18
-------------------------
-- Navigation:
-    * Made it possible to add, delete, rename and move menu items.
-    * Introduced tabs and subtabs for local tasks.
-    * Reorganized the navigation menus.
-- User management:
-    * Added support for multiple roles per user.
-    * Made it possible to add custom profile fields.
-    * Made it possible to browse user profiles by field.
-- Node system:
-    * Added support for node-level permissions.
-- Comment module:
-    * Made it possible to leave contact information without having to register.
-- Upload module:
-    * Added support for uploading documents (includes images).
-- Forum module:
-    * Added support for sticky forum topics.
-    * Made it possible to track forum topics.
-- Syndication:
-    * Added support for RSS ping-notifications of http://technorati.com/.
-    * Refactored the categorization of syndicated news items.
-    * Added an URL alias for 'rss.xml'.
-    * Improved date parsing.
-- Database backend:
-    * Added support for multiple database connections.
-    * The PostgreSQL backend does no longer require PEAR.
-- Theme system:
-    * Changed all GIFs to PNGs.
-    * Reorganised the handling of themes, template engines, templates and styles.
-    * Unified and extended the available theme settings.
-    * Added theme screenshots.
-- Blocks:
-    * Added 'recent comments' block.
-    * Added 'categories' block.
-- Blogger API:
-    * Added support for auto-discovery of blogger API via RSD.
-- Performance:
-    * Added support for sending gzip compressed pages.
-    * Improved performance of the forum module.
-- Accessibility:
-    * Improved the accessibility of the archive module's calendar.
-    * Improved form handling and error reporting.
-    * Added HTTP redirects to prevent submitting twice when refreshing right after a form submission.
-- Refactored 403 (forbidden) handling and added support for custom 403 pages.
-- Documentation:
-    * Added PHPDoc/Doxygen comments.
-- Filter system:
-    * Added support for using multiple input formats on the site
-    * Expanded the embedded PHP-code feature so it can be used everywhere
-    * Added support for role-dependent filtering, through input formats
-- UI translation:
-    * Managing translations is now completely done through the administration interface
-    * Added support for importing/exporting gettext .po files
-
-Drupal 4.4.3, 2005-06-01
-------------------------
-- Fixed bugs, including a critical input validation bug.
-
-Drupal 4.4.2, 2004-07-04
-------------------------
-- Fixed bugs: no critical bugs were identified.
-
-Drupal 4.4.1, 2004-05-01
-------------------------
-- Fixed bugs: no critical bugs were identified.
-
-Drupal 4.4.0, 2004-04-01
-------------------------
-- Added support for the MetaWeblog API and MovableType extensions.
-- Added a file API: enables better document management.
-- Improved the watchdog and search module to log search keys.
-- News aggregator:
-    * Added support for conditional GET.
-    * Added OPML feed subscription list.
-    * Added support for , , , ,  and .
-- Comment module:
-    * Made it possible to disable the "comment viewing controls".
-- Performance:
-    * Improved module loading when serving cached pages.
-    * Made it possible to automatically disable modules when under heavy load.
-    * Made it possible to automatically disable blocks when under heavy load.
-    * Improved performance and memory footprint of the locale module.
-- Theme system:
-    * Made all theme functions start with 'theme_'.
-    * Made all theme functions return their output.
-    * Migrated away from using the BaseTheme class.
-    * Added many new theme functions and refactored existing theme functions.
-    * Added avatar support to 'Xtemplate'.
-    * Replaced theme 'UnConeD' by 'Chameleon'.
-    * Replaced theme 'Marvin' by 'Pushbutton'.
-- Usability:
-    * Added breadcrumb navigation to all pages.
-    * Made it possible to add context-sensitive help to all pages.
-    * Replaced drop-down menus by radio buttons where appropriate.
-    * Removed the 'magic_quotes_gpc = 0' requirement.
-    * Added a 'book navigation' block.
-- Accessibility:
-    * Made themes degrade gracefully in absence of CSS.
-    * Grouped form elements using '
' and '' tags. - * Added '
'; ?> - * @endcode - * - * @see template_preprocess() - * @see template_preprocess_search_result() - * @see template_process() - * - * @ingroup themeable - */ -?> -
  • > - -

    > - -

    - -
    - -

    >

    - - -

    - -
    -
  • diff --git a/modules/search/search-results.tpl.php b/modules/search/search-results.tpl.php deleted file mode 100644 index aa9bf8d..0000000 --- a/modules/search/search-results.tpl.php +++ /dev/null @@ -1,35 +0,0 @@ - - -

    -
      - -
    - - -

    - - diff --git a/modules/search/search-rtl.css b/modules/search/search-rtl.css deleted file mode 100644 index da9e8d9..0000000 --- a/modules/search/search-rtl.css +++ /dev/null @@ -1,13 +0,0 @@ - -.search-advanced .criterion { - float: right; - margin-right: 0; - margin-left: 2em; -} -.search-advanced .action { - float: right; - clear: right; -} -.search-results .search-snippet-info { - padding-right: 1em; /* LTR */ -} \ No newline at end of file diff --git a/modules/search/search.admin.inc b/modules/search/search.admin.inc deleted file mode 100644 index a609485..0000000 --- a/modules/search/search.admin.inc +++ /dev/null @@ -1,186 +0,0 @@ - $info) { - $names[$module] = $system_info[$module]['name']; - } - asort($names, SORT_STRING); - return $names; -} - -/** - * Menu callback: displays the search module settings page. - * - * @ingroup forms - * - * @see search_admin_settings_validate() - * @see search_admin_settings_submit() - * @see search_admin_reindex_submit() - */ -function search_admin_settings($form) { - // Collect some stats - $remaining = 0; - $total = 0; - foreach (variable_get('search_active_modules', array('node', 'user')) as $module) { - if ($status = module_invoke($module, 'search_status')) { - $remaining += $status['remaining']; - $total += $status['total']; - } - } - - $count = format_plural($remaining, 'There is 1 item left to index.', 'There are @count items left to index.'); - $percentage = ((int)min(100, 100 * ($total - $remaining) / max(1, $total))) . '%'; - $status = '

    ' . t('%percentage of the site has been indexed.', array('%percentage' => $percentage)) . ' ' . $count . '

    '; - $form['status'] = array('#type' => 'fieldset', '#title' => t('Indexing status')); - $form['status']['status'] = array('#markup' => $status); - $form['status']['wipe'] = array('#type' => 'submit', '#value' => t('Re-index site'), '#submit' => array('search_admin_reindex_submit')); - - $items = drupal_map_assoc(array(10, 20, 50, 100, 200, 500)); - - // Indexing throttle: - $form['indexing_throttle'] = array( - '#type' => 'fieldset', - '#title' => t('Indexing throttle') - ); - $form['indexing_throttle']['search_cron_limit'] = array( - '#type' => 'select', - '#title' => t('Number of items to index per cron run'), - '#default_value' => variable_get('search_cron_limit', 100), - '#options' => $items, - '#description' => t('The maximum number of items indexed in each pass of a cron maintenance task. If necessary, reduce the number of items to prevent timeouts and memory errors while indexing.', array('@cron' => url('admin/reports/status'))) - ); - // Indexing settings: - $form['indexing_settings'] = array( - '#type' => 'fieldset', - '#title' => t('Indexing settings') - ); - $form['indexing_settings']['info'] = array( - '#markup' => t('

    Changing the settings below will cause the site index to be rebuilt. The search index is not cleared but systematically updated to reflect the new settings. Searching will continue to work but new content won\'t be indexed until all existing content has been re-indexed.

    The default settings should be appropriate for the majority of sites.

    ') - ); - $form['indexing_settings']['minimum_word_size'] = array( - '#type' => 'textfield', - '#title' => t('Minimum word length to index'), - '#default_value' => variable_get('minimum_word_size', 3), - '#size' => 5, - '#maxlength' => 3, - '#description' => t('The number of characters a word has to be to be indexed. A lower setting means better search result ranking, but also a larger database. Each search query must contain at least one keyword that is this size (or longer).'), - '#element_validate' => array('element_validate_integer_positive'), - ); - $form['indexing_settings']['overlap_cjk'] = array( - '#type' => 'checkbox', - '#title' => t('Simple CJK handling'), - '#default_value' => variable_get('overlap_cjk', TRUE), - '#description' => t('Whether to apply a simple Chinese/Japanese/Korean tokenizer based on overlapping sequences. Turn this off if you want to use an external preprocessor for this instead. Does not affect other languages.') - ); - - $form['active'] = array( - '#type' => 'fieldset', - '#title' => t('Active search modules') - ); - $module_options = _search_get_module_names(); - $form['active']['search_active_modules'] = array( - '#type' => 'checkboxes', - '#title' => t('Active modules'), - '#title_display' => 'invisible', - '#default_value' => variable_get('search_active_modules', array('node', 'user')), - '#options' => $module_options, - '#description' => t('Choose which search modules are active from the available modules.') - ); - $form['active']['search_default_module'] = array( - '#title' => t('Default search module'), - '#type' => 'radios', - '#default_value' => variable_get('search_default_module', 'node'), - '#options' => $module_options, - '#description' => t('Choose which search module is the default.') - ); - $form['#validate'][] = 'search_admin_settings_validate'; - $form['#submit'][] = 'search_admin_settings_submit'; - - // Per module settings - foreach (variable_get('search_active_modules', array('node', 'user')) as $module) { - $added_form = module_invoke($module, 'search_admin'); - if (is_array($added_form)) { - $form = array_merge($form, $added_form); - } - } - - return system_settings_form($form); -} - -/** - * Form validation handler for search_admin_settings(). - */ -function search_admin_settings_validate($form, &$form_state) { - // Check whether we selected a valid default. - if ($form_state['triggering_element']['#value'] != t('Reset to defaults')) { - $new_modules = array_filter($form_state['values']['search_active_modules']); - $default = $form_state['values']['search_default_module']; - if (!in_array($default, $new_modules, TRUE)) { - form_set_error('search_default_module', t('Your default search module is not selected as an active module.')); - } - } -} - -/** - * Form submission handler for search_admin_settings(). - */ -function search_admin_settings_submit($form, &$form_state) { - // If these settings change, the index needs to be rebuilt. - if ((variable_get('minimum_word_size', 3) != $form_state['values']['minimum_word_size']) || - (variable_get('overlap_cjk', TRUE) != $form_state['values']['overlap_cjk'])) { - drupal_set_message(t('The index will be rebuilt.')); - search_reindex(); - } - $current_modules = variable_get('search_active_modules', array('node', 'user')); - // Check whether we are resetting the values. - if ($form_state['triggering_element']['#value'] == t('Reset to defaults')) { - $new_modules = array('node', 'user'); - } - else { - $new_modules = array_filter($form_state['values']['search_active_modules']); - } - if (array_diff($current_modules, $new_modules)) { - drupal_set_message(t('The active search modules have been changed.')); - variable_set('menu_rebuild_needed', TRUE); - } -} - -/** - * Form submission handler for reindex button on search_admin_settings_form(). - */ -function search_admin_reindex_submit($form, &$form_state) { - // send the user to the confirmation page - $form_state['redirect'] = 'admin/config/search/settings/reindex'; -} diff --git a/modules/search/search.api.php b/modules/search/search.api.php deleted file mode 100644 index 62d53b8..0000000 --- a/modules/search/search.api.php +++ /dev/null @@ -1,376 +0,0 @@ - 'Content', - 'path' => 'node', - 'conditions_callback' => 'callback_search_conditions', - ); -} - -/** - * Define access to a custom search routine. - * - * This hook allows a module to define permissions for a search tab. - * - * @ingroup search - */ -function hook_search_access() { - return user_access('access content'); -} - -/** - * Take action when the search index is going to be rebuilt. - * - * Modules that use hook_update_index() should update their indexing - * bookkeeping so that it starts from scratch the next time - * hook_update_index() is called. - * - * @ingroup search - */ -function hook_search_reset() { - db_update('search_dataset') - ->fields(array('reindex' => REQUEST_TIME)) - ->condition('type', 'node') - ->execute(); -} - -/** - * Report the status of indexing. - * - * The core search module only invokes this hook on active modules. - * Implementing modules do not need to check whether they are active when - * calculating their return values. - * - * @return - * An associative array with the key-value pairs: - * - 'remaining': The number of items left to index. - * - 'total': The total number of items to index. - * - * @ingroup search - */ -function hook_search_status() { - $total = db_query('SELECT COUNT(*) FROM {node} WHERE status = 1')->fetchField(); - $remaining = db_query("SELECT COUNT(*) FROM {node} n LEFT JOIN {search_dataset} d ON d.type = 'node' AND d.sid = n.nid WHERE n.status = 1 AND d.sid IS NULL OR d.reindex <> 0")->fetchField(); - return array('remaining' => $remaining, 'total' => $total); -} - -/** - * Add elements to the search settings form. - * - * @return - * Form array for the Search settings page at admin/config/search/settings. - * - * @ingroup search - */ -function hook_search_admin() { - // Output form for defining rank factor weights. - $form['content_ranking'] = array( - '#type' => 'fieldset', - '#title' => t('Content ranking'), - ); - $form['content_ranking']['#theme'] = 'node_search_admin'; - $form['content_ranking']['info'] = array( - '#value' => '' . t('The following numbers control which properties the content search should favor when ordering the results. Higher numbers mean more influence, zero means the property is ignored. Changing these numbers does not require the search index to be rebuilt. Changes take effect immediately.') . '' - ); - - // Note: reversed to reflect that higher number = higher ranking. - $options = drupal_map_assoc(range(0, 10)); - foreach (module_invoke_all('ranking') as $var => $values) { - $form['content_ranking']['factors']['node_rank_' . $var] = array( - '#title' => $values['title'], - '#type' => 'select', - '#options' => $options, - '#default_value' => variable_get('node_rank_' . $var, 0), - ); - } - return $form; -} - -/** - * Execute a search for a set of key words. - * - * Use database API with the 'PagerDefault' query extension to perform your - * search. - * - * If your module uses hook_update_index() and search_index() to index its - * items, use table 'search_index' aliased to 'i' as the main table in your - * query, with the 'SearchQuery' extension. You can join to your module's table - * using the 'i.sid' field, which will contain the $sid values you provided to - * search_index(). Add the main keywords to the query by using method - * searchExpression(). The functions search_expression_extract() and - * search_expression_insert() may also be helpful for adding custom search - * parameters to the search expression. - * - * See node_search_execute() for an example of a module that uses the search - * index, and user_search_execute() for an example that doesn't use the search - * index. - * - * @param $keys - * The search keywords as entered by the user. - * @param $conditions - * An optional array of additional conditions, such as filters. - * - * @return - * An array of search results. To use the default search result - * display, each item should have the following keys': - * - 'link': Required. The URL of the found item. - * - 'type': The type of item (such as the content type). - * - 'title': Required. The name of the item. - * - 'user': The author of the item. - * - 'date': A timestamp when the item was last modified. - * - 'extra': An array of optional extra information items. - * - 'snippet': An excerpt or preview to show with the result (can be - * generated with search_excerpt()). - * - 'language': Language code for the item (usually two characters). - * - * @ingroup search - */ -function hook_search_execute($keys = NULL, $conditions = NULL) { - // Build matching conditions - $query = db_select('search_index', 'i', array('target' => 'slave'))->extend('SearchQuery')->extend('PagerDefault'); - $query->join('node', 'n', 'n.nid = i.sid'); - $query - ->condition('n.status', 1) - ->addTag('node_access') - ->searchExpression($keys, 'node'); - - // Insert special keywords. - $query->setOption('type', 'n.type'); - $query->setOption('language', 'n.language'); - if ($query->setOption('term', 'ti.tid')) { - $query->join('taxonomy_index', 'ti', 'n.nid = ti.nid'); - } - // Only continue if the first pass query matches. - if (!$query->executeFirstPass()) { - return array(); - } - - // Add the ranking expressions. - _node_rankings($query); - - // Load results. - $find = $query - ->limit(10) - ->execute(); - $results = array(); - foreach ($find as $item) { - // Build the node body. - $node = node_load($item->sid); - node_build_content($node, 'search_result'); - $node->body = drupal_render($node->content); - - // Fetch comments for snippet. - $node->rendered .= ' ' . module_invoke('comment', 'node_update_index', $node); - // Fetch terms for snippet. - $node->rendered .= ' ' . module_invoke('taxonomy', 'node_update_index', $node); - - $extra = module_invoke_all('node_search_result', $node); - - $results[] = array( - 'link' => url('node/' . $item->sid, array('absolute' => TRUE)), - 'type' => check_plain(node_type_get_name($node)), - 'title' => $node->title, - 'user' => theme('username', array('account' => $node)), - 'date' => $node->changed, - 'node' => $node, - 'extra' => $extra, - 'score' => $item->calculated_score, - 'snippet' => search_excerpt($keys, $node->body), - ); - } - return $results; -} - -/** - * Override the rendering of search results. - * - * A module that implements hook_search_info() to define a type of search may - * implement this hook in order to override the default theming of its search - * results, which is otherwise themed using theme('search_results'). - * - * Note that by default, theme('search_results') and theme('search_result') - * work together to create an ordered list (OL). So your hook_search_page() - * implementation should probably do this as well. - * - * @param $results - * An array of search results. - * - * @return - * A renderable array, which will render the formatted search results with a - * pager included. - * - * @see search-result.tpl.php - * @see search-results.tpl.php - */ -function hook_search_page($results) { - $output['prefix']['#markup'] = '
      '; - - foreach ($results as $entry) { - $output[] = array( - '#theme' => 'search_result', - '#result' => $entry, - '#module' => 'my_module_name', - ); - } - $output['suffix']['#markup'] = '
    ' . theme('pager'); - - return $output; -} - -/** - * Preprocess text for search. - * - * This hook is called to preprocess both the text added to the search index and - * the keywords users have submitted for searching. - * - * Possible uses: - * - Adding spaces between words of Chinese or Japanese text. - * - Stemming words down to their root words to allow matches between, for - * instance, walk, walked, walking, and walks in searching. - * - Expanding abbreviations and acronymns that occur in text. - * - * @param $text - * The text to preprocess. This is a single piece of plain text extracted - * from between two HTML tags or from the search query. It will not contain - * any HTML entities or HTML tags. - * - * @return - * The text after preprocessing. Note that if your module decides not to alter - * the text, it should return the original text. Also, after preprocessing, - * words in the text should be separated by a space. - * - * @ingroup search - */ -function hook_search_preprocess($text) { - // Do processing on $text - return $text; -} - -/** - * Update the search index for this module. - * - * This hook is called every cron run if search.module is enabled, your - * module has implemented hook_search_info(), and your module has been set as - * an active search module on the Search settings page - * (admin/config/search/settings). It allows your module to add items to the - * built-in search index using search_index(), or to add them to your module's - * own indexing mechanism. - * - * When implementing this hook, your module should index content items that - * were modified or added since the last run. PHP has a time limit - * for cron, though, so it is advisable to limit how many items you index - * per run using variable_get('search_cron_limit') (see example below). Also, - * since the cron run could time out and abort in the middle of your run, you - * should update your module's internal bookkeeping on when items have last - * been indexed as you go rather than waiting to the end of indexing. - * - * @ingroup search - */ -function hook_update_index() { - $limit = (int)variable_get('search_cron_limit', 100); - - $result = db_query_range("SELECT n.nid FROM {node} n LEFT JOIN {search_dataset} d ON d.type = 'node' AND d.sid = n.nid WHERE d.sid IS NULL OR d.reindex <> 0 ORDER BY d.reindex ASC, n.nid ASC", 0, $limit); - - foreach ($result as $node) { - $node = node_load($node->nid); - - // Save the changed time of the most recent indexed node, for the search - // results half-life calculation. - variable_set('node_cron_last', $node->changed); - - // Render the node. - node_build_content($node, 'search_index'); - $node->rendered = drupal_render($node->content); - - $text = '

    ' . check_plain($node->title) . '

    ' . $node->rendered; - - // Fetch extra data normally not visible - $extra = module_invoke_all('node_update_index', $node); - foreach ($extra as $t) { - $text .= $t; - } - - // Update index - search_index($node->nid, 'node', $text); - } -} -/** - * @} End of "addtogroup hooks". - */ - -/** - * Provide search query conditions. - * - * Callback for hook_search_info(). - * - * This callback is invoked by search_view() to get an array of additional - * search conditions to pass to search_data(). For example, a search module - * may get additional keywords, filters, or modifiers for the search from - * the query string. - * - * This example pulls additional search keywords out of the $_REQUEST variable, - * (i.e. from the query string of the request). The conditions may also be - * generated internally - for example based on a module's settings. - * - * @param $keys - * The search keywords string. - * - * @return - * An array of additional conditions, such as filters. - * - * @ingroup callbacks - * @ingroup search - */ -function callback_search_conditions($keys) { - $conditions = array(); - - if (!empty($_REQUEST['keys'])) { - $conditions['keys'] = $_REQUEST['keys']; - } - if (!empty($_REQUEST['sample_search_keys'])) { - $conditions['sample_search_keys'] = $_REQUEST['sample_search_keys']; - } - if ($force_keys = config('sample_search.settings')->get('force_keywords')) { - $conditions['sample_search_force_keywords'] = $force_keys; - } - return $conditions; -} diff --git a/modules/search/search.css b/modules/search/search.css deleted file mode 100644 index ff7230f..0000000 --- a/modules/search/search.css +++ /dev/null @@ -1,34 +0,0 @@ - -.search-form { - margin-bottom: 1em; -} -.search-form input { - margin-top: 0; - margin-bottom: 0; -} -.search-results { - list-style: none; -} -.search-results p { - margin-top: 0; -} -.search-results .title { - font-size: 1.2em; -} -.search-results li { - margin-bottom: 1em; -} -.search-results .search-snippet-info { - padding-left: 1em; /* LTR */ -} -.search-results .search-info { - font-size: 0.85em; -} -.search-advanced .criterion { - float: left; /* LTR */ - margin-right: 2em; /* LTR */ -} -.search-advanced .action { - float: left; /* LTR */ - clear: left; /* LTR */ -} diff --git a/modules/search/search.extender.inc b/modules/search/search.extender.inc deleted file mode 100644 index 72cea64..0000000 --- a/modules/search/search.extender.inc +++ /dev/null @@ -1,546 +0,0 @@ - array(), 'negative' => array()); - - /** - * Indicates whether the first pass query requires complex conditions (LIKE). - * - * @var boolean. - */ - protected $simple = TRUE; - - /** - * Conditions that are used for exact searches. - * - * This is always used for the second pass query but not for the first pass, - * unless $this->simple is FALSE. - * - * @var DatabaseCondition - */ - protected $conditions; - - /** - * Indicates how many matches for a search query are necessary. - * - * @var int - */ - protected $matches = 0; - - /** - * Array of search words. - * - * These words have to match against {search_index}.word. - * - * @var array - */ - protected $words = array(); - - /** - * Multiplier for the normalized search score. - * - * This value is calculated by the first pass query and multiplied with the - * actual score of a specific word to make sure that the resulting calculated - * score is between 0 and 1. - * - * @var float - */ - protected $normalize; - - /** - * Indicates whether the first pass query has been executed. - * - * @var boolean - */ - protected $executedFirstPass = FALSE; - - /** - * Stores score expressions. - * - * @var array - * - * @see addScore() - */ - protected $scores = array(); - - /** - * Stores arguments for score expressions. - * - * @var array - */ - protected $scoresArguments = array(); - - /** - * Stores multipliers for score expressions. - * - * @var array - */ - protected $multiply = array(); - - /** - * Whether or not search expressions were ignored. - * - * The maximum number of AND/OR combinations exceeded can be configured to - * avoid Denial-of-Service attacks. Expressions beyond the limit are ignored. - * - * @var boolean - */ - protected $expressionsIgnored = FALSE; - - /** - * Sets up the search query expression. - * - * @param $query - * A search query string, which can contain options. - * @param $module - * The search module. This maps to {search_index}.type in the database. - * - * @return - * The SearchQuery object. - */ - public function searchExpression($expression, $module) { - $this->searchExpression = $expression; - $this->type = $module; - - // Add a search_* tag. This needs to be added before any preExecute methods - // for decorated queries are called, as $this->prepared will be set to TRUE - // and tags added in the execute method will never get used. For example, - // if $query is extended by 'SearchQuery' then 'PagerDefault', the - // search-specific tag will be added too late (when preExecute() has - // already been called from the PagerDefault extender), and as a - // consequence will not be available to hook_query_alter() implementations, - // nor will the correct hook_query_TAG_alter() implementations get invoked. - // See node_search_execute(). - $this->addTag('search_' . $module); - - return $this; - } - - /** - * Applies a search option and removes it from the search query string. - * - * These options are in the form option:value,value2,value3. - * - * @param $option - * Name of the option. - * @param $column - * Name of the database column to which the value should be applied. - * - * @return - * TRUE if a value for that option was found, FALSE if not. - */ - public function setOption($option, $column) { - if ($values = search_expression_extract($this->searchExpression, $option)) { - $or = db_or(); - foreach (explode(',', $values) as $value) { - $or->condition($column, $value); - } - $this->condition($or); - $this->searchExpression = search_expression_insert($this->searchExpression, $option); - return TRUE; - } - return FALSE; - } - - /** - * Parses the search query into SQL conditions. - * - * We build two queries that match the dataset bodies. - */ - protected function parseSearchExpression() { - // Matchs words optionally prefixed by a dash. A word in this case is - // something between two spaces, optionally quoted. - preg_match_all('/ (-?)("[^"]+"|[^" ]+)/i', ' ' . $this->searchExpression , $keywords, PREG_SET_ORDER); - - if (count($keywords) == 0) { - return; - } - - // Classify tokens. - $or = FALSE; - $warning = ''; - $limit_combinations = variable_get('search_and_or_limit', 7); - // The first search expression does not count as AND. - $and_count = -1; - $or_count = 0; - foreach ($keywords as $match) { - if ($or_count && $and_count + $or_count >= $limit_combinations) { - // Ignore all further search expressions to prevent Denial-of-Service - // attacks using a high number of AND/OR combinations. - $this->expressionsIgnored = TRUE; - break; - } - $phrase = FALSE; - // Strip off phrase quotes. - if ($match[2]{0} == '"') { - $match[2] = substr($match[2], 1, -1); - $phrase = TRUE; - $this->simple = FALSE; - } - // Simplify keyword according to indexing rules and external - // preprocessors. Use same process as during search indexing, so it - // will match search index. - $words = search_simplify($match[2]); - // Re-explode in case simplification added more words, except when - // matching a phrase. - $words = $phrase ? array($words) : preg_split('/ /', $words, -1, PREG_SPLIT_NO_EMPTY); - // Negative matches. - if ($match[1] == '-') { - $this->keys['negative'] = array_merge($this->keys['negative'], $words); - } - // OR operator: instead of a single keyword, we store an array of all - // OR'd keywords. - elseif ($match[2] == 'OR' && count($this->keys['positive'])) { - $last = array_pop($this->keys['positive']); - // Starting a new OR? - if (!is_array($last)) { - $last = array($last); - } - $this->keys['positive'][] = $last; - $or = TRUE; - $or_count++; - continue; - } - // AND operator: implied, so just ignore it. - elseif ($match[2] == 'AND' || $match[2] == 'and') { - $warning = $match[2]; - continue; - } - - // Plain keyword. - else { - if ($match[2] == 'or') { - $warning = $match[2]; - } - if ($or) { - // Add to last element (which is an array). - $this->keys['positive'][count($this->keys['positive']) - 1] = array_merge($this->keys['positive'][count($this->keys['positive']) - 1], $words); - } - else { - $this->keys['positive'] = array_merge($this->keys['positive'], $words); - $and_count++; - } - } - $or = FALSE; - } - - // Convert keywords into SQL statements. - $this->conditions = db_and(); - $simple_and = FALSE; - $simple_or = FALSE; - // Positive matches. - foreach ($this->keys['positive'] as $key) { - // Group of ORed terms. - if (is_array($key) && count($key)) { - $simple_or = TRUE; - $any = FALSE; - $queryor = db_or(); - foreach ($key as $or) { - list($num_new_scores) = $this->parseWord($or); - $any |= $num_new_scores; - $queryor->condition('d.data', "% $or %", 'LIKE'); - } - if (count($queryor)) { - $this->conditions->condition($queryor); - // A group of OR keywords only needs to match once. - $this->matches += ($any > 0); - } - } - // Single ANDed term. - else { - $simple_and = TRUE; - list($num_new_scores, $num_valid_words) = $this->parseWord($key); - $this->conditions->condition('d.data', "% $key %", 'LIKE'); - if (!$num_valid_words) { - $this->simple = FALSE; - } - // Each AND keyword needs to match at least once. - $this->matches += $num_new_scores; - } - } - if ($simple_and && $simple_or) { - $this->simple = FALSE; - } - // Negative matches. - foreach ($this->keys['negative'] as $key) { - $this->conditions->condition('d.data', "% $key %", 'NOT LIKE'); - $this->simple = FALSE; - } - - if ($warning == 'or') { - drupal_set_message(t('Search for either of the two terms with uppercase OR. For example, cats OR dogs.')); - } - } - - /** - * Helper function for parseQuery(). - */ - protected function parseWord($word) { - $num_new_scores = 0; - $num_valid_words = 0; - // Determine the scorewords of this word/phrase. - $split = explode(' ', $word); - foreach ($split as $s) { - $num = is_numeric($s); - if ($num || drupal_strlen($s) >= variable_get('minimum_word_size', 3)) { - if (!isset($this->words[$s])) { - $this->words[$s] = $s; - $num_new_scores++; - } - $num_valid_words++; - } - } - // Return matching snippet and number of added words. - return array($num_new_scores, $num_valid_words); - } - - /** - * Executes the first pass query. - * - * This can either be done explicitly, so that additional scores and - * conditions can be applied to the second pass query, or implicitly by - * addScore() or execute(). - * - * @return - * TRUE if search items exist, FALSE if not. - */ - public function executeFirstPass() { - $this->parseSearchExpression(); - - if (count($this->words) == 0) { - form_set_error('keys', format_plural(variable_get('minimum_word_size', 3), 'You must include at least one positive keyword with 1 character or more.', 'You must include at least one positive keyword with @count characters or more.')); - return FALSE; - } - if ($this->expressionsIgnored) { - drupal_set_message(t('Your search used too many AND/OR expressions. Only the first @count terms were included in this search.', array('@count' => variable_get('search_and_or_limit', 7))), 'warning'); - } - $this->executedFirstPass = TRUE; - - if (!empty($this->words)) { - $or = db_or(); - foreach ($this->words as $word) { - $or->condition('i.word', $word); - } - $this->condition($or); - } - // Build query for keyword normalization. - $this->join('search_total', 't', 'i.word = t.word'); - $this - ->condition('i.type', $this->type) - ->groupBy('i.type') - ->groupBy('i.sid') - ->having('COUNT(*) >= :matches', array(':matches' => $this->matches)); - - // Clone the query object to do the firstPass query; - $first = clone $this->query; - - // For complex search queries, add the LIKE conditions to the first pass query. - if (!$this->simple) { - $first->join('search_dataset', 'd', 'i.sid = d.sid AND i.type = d.type'); - $first->condition($this->conditions); - } - - // Calculate maximum keyword relevance, to normalize it. - $first->addExpression('SUM(i.score * t.count)', 'calculated_score'); - $this->normalize = $first - ->range(0, 1) - ->orderBy('calculated_score', 'DESC') - ->execute() - ->fetchField(); - - if ($this->normalize) { - return TRUE; - } - return FALSE; - } - - /** - * Adds a custom score expression to the search query. - * - * Score expressions are used to order search results. If no calls to - * addScore() have taken place, a default keyword relevance score will be - * used. However, if at least one call to addScore() has taken place, the - * keyword relevance score is not automatically added. - * - * Also note that if you call orderBy() directly on the query, search scores - * will not automatically be used to order search results. Your orderBy() - * expression can reference 'calculated_score', which will be the total - * calculated score value. - * - * @param $score - * The score expression, which should evaluate to a number between 0 and 1. - * The string 'i.relevance' in a score expression will be replaced by a - * measure of keyword relevance between 0 and 1. - * @param $arguments - * Query arguments needed to provide values to the score expression. - * @param $multiply - * If set, the score is multiplied with this value. However, all scores - * with multipliers are then divided by the total of all multipliers, so - * that overall, the normalization is maintained. - * - * @return object - * The updated query object. - */ - public function addScore($score, $arguments = array(), $multiply = FALSE) { - if ($multiply) { - $i = count($this->multiply); - // Modify the score expression so it is multiplied by the multiplier, - // with a divisor to renormalize. - $score = "CAST(:multiply_$i AS DECIMAL) * COALESCE(( " . $score . "), 0) / CAST(:total_$i AS DECIMAL)"; - // Add an argument for the multiplier. The :total_$i argument is taken - // care of in the execute() method, which is when the total divisor is - // calculated. - $arguments[':multiply_' . $i] = $multiply; - $this->multiply[] = $multiply; - } - - $this->scores[] = $score; - $this->scoresArguments += $arguments; - - return $this; - } - - /** - * Executes the search. - * - * If not already done, this executes the first pass query. Then the complex - * conditions are applied to the query including score expressions and - * ordering. - * - * @return - * FALSE if the first pass query returned no results, and a database result - * set if there were results. - */ - public function execute() - { - if (!$this->executedFirstPass) { - $this->executeFirstPass(); - } - if (!$this->normalize) { - return new DatabaseStatementEmpty(); - } - - // Add conditions to query. - $this->join('search_dataset', 'd', 'i.sid = d.sid AND i.type = d.type'); - $this->condition($this->conditions); - - if (empty($this->scores)) { - // Add default score. - $this->addScore('i.relevance'); - } - - if (count($this->multiply)) { - // Re-normalize scores with multipliers by dividing by the total of all - // multipliers. The expressions were altered in addScore(), so here just - // add the arguments for the total. - $i = 0; - $sum = array_sum($this->multiply); - foreach ($this->multiply as $total) { - $this->scoresArguments[':total_' . $i] = $sum; - $i++; - } - } - - // Replace the pseudo-expression 'i.relevance' with a measure of keyword - // relevance in all score expressions, using string replacement. Careful - // though! If you just print out a float, some locales use ',' as the - // decimal separator in PHP, while SQL always uses '.'. So, make sure to - // set the number format correctly. - $relevance = number_format((1.0 / $this->normalize), 10, '.', ''); - $this->scores = str_replace('i.relevance', '(' . $relevance . ' * i.score * t.count)', $this->scores); - - // Add all scores together to form a query field. - $this->addExpression('SUM(' . implode(' + ', $this->scores) . ')', 'calculated_score', $this->scoresArguments); - - // If an order has not yet been set for this query, add a default order - // that sorts by the calculated sum of scores. - if (count($this->getOrderBy()) == 0) { - $this->orderBy('calculated_score', 'DESC'); - } - - // Add useful metadata. - $this - ->addMetaData('normalize', $this->normalize) - ->fields('i', array('type', 'sid')); - - return $this->query->execute(); - } - - /** - * Builds the default count query for SearchQuery. - * - * Since SearchQuery always uses GROUP BY, we can default to a subquery. We - * also add the same conditions as execute() because countQuery() is called - * first. - */ - public function countQuery() { - // Clone the inner query. - $inner = clone $this->query; - - // Add conditions to query. - $inner->join('search_dataset', 'd', 'i.sid = d.sid AND i.type = d.type'); - $inner->condition($this->conditions); - - // Remove existing fields and expressions, they are not needed for a count - // query. - $fields =& $inner->getFields(); - $fields = array(); - $expressions =& $inner->getExpressions(); - $expressions = array(); - - // Add the sid as the only field and count them as a subquery. - $count = db_select($inner->fields('i', array('sid')), NULL, array('target' => 'slave')); - - // Add the COUNT() expression. - $count->addExpression('COUNT(*)'); - - return $count; - } -} diff --git a/modules/search/search.info b/modules/search/search.info deleted file mode 100644 index 77cab05..0000000 --- a/modules/search/search.info +++ /dev/null @@ -1,9 +0,0 @@ -name = Search -description = Enables site-wide keyword searching. -package = Core -version = VERSION -core = 7.x -files[] = search.extender.inc -files[] = search.test -configure = admin/config/search/settings -stylesheets[all][] = search.css diff --git a/modules/search/search.install b/modules/search/search.install deleted file mode 100644 index f0113b3..0000000 --- a/modules/search/search.install +++ /dev/null @@ -1,182 +0,0 @@ - 'Stores items that will be searched.', - 'fields' => array( - 'sid' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'description' => 'Search item ID, e.g. node ID for nodes.', - ), - 'type' => array( - 'type' => 'varchar', - 'length' => 16, - 'not null' => TRUE, - 'description' => 'Type of item, e.g. node.', - ), - 'data' => array( - 'type' => 'text', - 'not null' => TRUE, - 'size' => 'big', - 'description' => 'List of space-separated words from the item.', - ), - 'reindex' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'description' => 'Set to force node reindexing.', - ), - ), - 'primary key' => array('sid', 'type'), - ); - - $schema['search_index'] = array( - 'description' => 'Stores the search index, associating words, items and scores.', - 'fields' => array( - 'word' => array( - 'type' => 'varchar', - 'length' => 50, - 'not null' => TRUE, - 'default' => '', - 'description' => 'The {search_total}.word that is associated with the search item.', - ), - 'sid' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'description' => 'The {search_dataset}.sid of the searchable item to which the word belongs.', - ), - 'type' => array( - 'type' => 'varchar', - 'length' => 16, - 'not null' => TRUE, - 'description' => 'The {search_dataset}.type of the searchable item to which the word belongs.', - ), - 'score' => array( - 'type' => 'float', - 'not null' => FALSE, - 'description' => 'The numeric score of the word, higher being more important.', - ), - ), - 'indexes' => array( - 'sid_type' => array('sid', 'type'), - ), - 'foreign keys' => array( - 'search_dataset' => array( - 'table' => 'search_dataset', - 'columns' => array( - 'sid' => 'sid', - 'type' => 'type', - ), - ), - ), - 'primary key' => array('word', 'sid', 'type'), - ); - - $schema['search_total'] = array( - 'description' => 'Stores search totals for words.', - 'fields' => array( - 'word' => array( - 'description' => 'Primary Key: Unique word in the search index.', - 'type' => 'varchar', - 'length' => 50, - 'not null' => TRUE, - 'default' => '', - ), - 'count' => array( - 'description' => "The count of the word in the index using Zipf's law to equalize the probability distribution.", - 'type' => 'float', - 'not null' => FALSE, - ), - ), - 'primary key' => array('word'), - ); - - $schema['search_node_links'] = array( - 'description' => 'Stores items (like nodes) that link to other nodes, used to improve search scores for nodes that are frequently linked to.', - 'fields' => array( - 'sid' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'description' => 'The {search_dataset}.sid of the searchable item containing the link to the node.', - ), - 'type' => array( - 'type' => 'varchar', - 'length' => 16, - 'not null' => TRUE, - 'default' => '', - 'description' => 'The {search_dataset}.type of the searchable item containing the link to the node.', - ), - 'nid' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'description' => 'The {node}.nid that this item links to.', - ), - 'caption' => array( - 'type' => 'text', - 'size' => 'big', - 'not null' => FALSE, - 'description' => 'The text used to link to the {node}.nid.', - ), - ), - 'primary key' => array('sid', 'type', 'nid'), - 'indexes' => array( - 'nid' => array('nid'), - ), - ); - - return $schema; -} - -/** - * Replace unique keys in 'search_dataset' and 'search_index' by primary keys. - */ -function search_update_7000() { - db_drop_unique_key('search_dataset', 'sid_type'); - $dataset_type_spec = array( - 'type' => 'varchar', - 'length' => 16, - 'not null' => TRUE, - 'description' => 'Type of item, e.g. node.', - ); - db_change_field('search_dataset', 'type', 'type', $dataset_type_spec); - db_add_primary_key('search_dataset', array('sid', 'type')); - - db_drop_index('search_index', 'word'); - db_drop_unique_key('search_index', 'word_sid_type'); - $index_type_spec = array( - 'type' => 'varchar', - 'length' => 16, - 'not null' => TRUE, - 'description' => 'The {search_dataset}.type of the searchable item to which the word belongs.', - ); - db_change_field('search_index', 'type', 'type', $index_type_spec); - db_add_primary_key('search_index', array('word', 'sid', 'type')); -} - diff --git a/modules/search/search.module b/modules/search/search.module deleted file mode 100644 index 7542f98..0000000 --- a/modules/search/search.module +++ /dev/null @@ -1,1356 +0,0 @@ -' . t('About') . ''; - $output .= '

    ' . t('The Search module provides the ability to index and search for content by exact keywords, and for users by username or e-mail. For more information, see the online handbook entry for Search module.', array('@search-module' => 'http://drupal.org/documentation/modules/search/', '@search' => url('search'))) . '

    '; - $output .= '

    ' . t('Uses') . '

    '; - $output .= '
    '; - $output .= '
    ' . t('Searching content and users') . '
    '; - $output .= '
    ' . t('Users with Use search permission can use the search block and Search page. Users with the View published content permission can search for content containing exact keywords. Users with the View user profiles permission can search for users containing the keyword anywhere in the user name, and users with the Administer users permission can search for users by email address. Additionally, users with Use advanced search permission can find content using more complex search methods and filtering by choosing the Advanced search option on the Search page.', array('@search' => url('search'))) . '
    '; - $output .= '
    ' . t('Indexing content with cron') . '
    '; - $output .= '
    ' . t('To provide keyword searching, the search engine maintains an index of words found in the content and its fields, along with text added to your content by other modules (such as comments from the core Comment module, and taxonomy terms from the core Taxonomy module). To build and maintain this index, a correctly configured cron maintenance task is required. Users with Administer search permission can further configure the cron settings on the Search settings page.', array('@cron' => 'http://drupal.org/cron', '@searchsettings' => url('admin/config/search/settings'))) . '
    '; - $output .= '
    ' . t('Content reindexing') . '
    '; - $output .= '
    ' . t('Content-related actions on your site (creating, editing, or deleting content and comments) automatically cause affected content items to be marked for indexing or reindexing at the next cron run. When content is marked for reindexing, the previous content remains in the index until cron runs, at which time it is replaced by the new content. Unlike content-related actions, actions related to the structure of your site do not cause affected content to be marked for reindexing. Examples of structure-related actions that affect content include deleting or editing taxonomy terms, enabling or disabling modules that add text to content (such as Taxonomy, Comment, and field-providing modules), and modifying the fields or display parameters of your content types. If you take one of these actions and you want to ensure that the search index is updated to reflect your changed site structure, you can mark all content for reindexing by clicking the "Re-index site" button on the Search settings page. If you have a lot of content on your site, it may take several cron runs for the content to be reindexed.', array('@searchsettings' => url('admin/config/search/settings'))) . '
    '; - $output .= '
    ' . t('Configuring search settings') . '
    '; - $output .= '
    ' . t('Indexing behavior can be adjusted using the Search settings page. Users with Administer search permission can control settings such as the Number of items to index per cron run, Indexing settings (word length), Active search modules, and Content ranking, which lets you adjust the priority in which indexed content is returned in results.', array('@searchsettings' => url('admin/config/search/settings'))) . '
    '; - $output .= '
    ' . t('Search block') . '
    '; - $output .= '
    ' . t('The Search module includes a default Search form block, which can be enabled and configured on the Blocks administration page. The block is available to users with the Search content permission.', array('@blocks' => url('admin/structure/block'))) . '
    '; - $output .= '
    ' . t('Extending Search module') . '
    '; - $output .= '
    ' . t('By default, the Search module only supports exact keyword matching in content searches. You can modify this behavior by installing a language-specific stemming module for your language (such as Porter Stemmer for American English), which allows words such as walk, walking, and walked to be matched in the Search module. Another approach is to use a third-party search technology with stemming or partial word matching features built in, such as Apache Solr or Sphinx. These and other search-related contributed modules can be downloaded by visiting Drupal.org.', array('@contrib-search' => 'http://drupal.org/project/modules?filters=tid%3A105')) . '
    '; - $output .= '
    '; - return $output; - case 'admin/config/search/settings': - return '

    ' . t('The search engine maintains an index of words found in your site\'s content. To build and maintain this index, a correctly configured cron maintenance task is required. Indexing behavior can be adjusted using the settings below.', array('@cron' => url('admin/reports/status'))) . '

    '; - case 'search#noresults': - return t('
      -
    • Check if your spelling is correct.
    • -
    • Remove quotes around phrases to search for each word individually. bike shed will often show more results than "bike shed".
    • -
    • Consider loosening your query with OR. bike OR shed will often show more results than bike shed.
    • -
    '); - } -} - -/** - * Implements hook_theme(). - */ -function search_theme() { - return array( - 'search_block_form' => array( - 'render element' => 'form', - 'template' => 'search-block-form', - ), - 'search_result' => array( - 'variables' => array('result' => NULL, 'module' => NULL), - 'file' => 'search.pages.inc', - 'template' => 'search-result', - ), - 'search_results' => array( - 'variables' => array('results' => NULL, 'module' => NULL), - 'file' => 'search.pages.inc', - 'template' => 'search-results', - ), - ); -} - -/** - * Implements hook_permission(). - */ -function search_permission() { - return array( - 'administer search' => array( - 'title' => t('Administer search'), - ), - 'search content' => array( - 'title' => t('Use search'), - ), - 'use advanced search' => array( - 'title' => t('Use advanced search'), - ), - ); -} - -/** - * Implements hook_block_info(). - */ -function search_block_info() { - $blocks['form']['info'] = t('Search form'); - // Not worth caching. - $blocks['form']['cache'] = DRUPAL_NO_CACHE; - $blocks['form']['properties']['administrative'] = TRUE; - return $blocks; -} - -/** - * Implements hook_block_view(). - */ -function search_block_view($delta = '') { - if (user_access('search content')) { - $block['content'] = drupal_get_form('search_block_form'); - return $block; - } -} - -/** - * Implements hook_menu(). - */ -function search_menu() { - $items['search'] = array( - 'title' => 'Search', - 'page callback' => 'search_view', - 'access callback' => 'search_is_active', - 'type' => MENU_SUGGESTED_ITEM, - 'file' => 'search.pages.inc', - ); - $items['admin/config/search/settings'] = array( - 'title' => 'Search settings', - 'description' => 'Configure relevance settings for search and other indexing options.', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('search_admin_settings'), - 'access arguments' => array('administer search'), - 'weight' => -10, - 'file' => 'search.admin.inc', - ); - $items['admin/config/search/settings/reindex'] = array( - 'title' => 'Clear index', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('search_reindex_confirm'), - 'access arguments' => array('administer search'), - 'type' => MENU_VISIBLE_IN_BREADCRUMB, - 'file' => 'search.admin.inc', - ); - - // Add paths for searching. We add each module search path twice: once without - // and once with %menu_tail appended. The reason for this is that we want to - // preserve keywords when switching tabs, and also to have search tabs - // highlighted properly. The only way to do that within the Drupal menu - // system appears to be having two sets of tabs. See discussion on issue - // http://drupal.org/node/245103 for details. - - drupal_static_reset('search_get_info'); - $default_info = search_get_default_module_info(); - if ($default_info) { - foreach (search_get_info() as $module => $search_info) { - $path = 'search/' . $search_info['path']; - $items[$path] = array( - 'title' => $search_info['title'], - 'page callback' => 'search_view', - 'page arguments' => array($module, ''), - 'access callback' => '_search_menu_access', - 'access arguments' => array($module), - 'type' => MENU_LOCAL_TASK, - 'file' => 'search.pages.inc', - 'weight' => $module == $default_info['module'] ? -10 : 0, - ); - $items["$path/%menu_tail"] = array( - 'title' => $search_info['title'], - 'load arguments' => array('%map', '%index'), - 'page callback' => 'search_view', - 'page arguments' => array($module, 2), - 'access callback' => '_search_menu_access', - 'access arguments' => array($module), - // The default local task points to its parent, but this item points to - // where it should so it should not be changed. - 'type' => MENU_LOCAL_TASK, - 'file' => 'search.pages.inc', - 'weight' => 0, - // These tabs are not subtabs. - 'tab_root' => 'search/' . $default_info['path'] . '/%', - // These tabs need to display at the same level. - 'tab_parent' => 'search/' . $default_info['path'], - ); - } - } - return $items; -} - -/** - * Determines access for the ?q=search path. - */ -function search_is_active() { - // This path cannot be accessed if there are no active modules. - return user_access('search content') && search_get_info(); -} - -/** - * Returns information about available search modules. - * - * @param $all - * If TRUE, information about all enabled modules implementing - * hook_search_info() will be returned. If FALSE (default), only modules that - * have been set to active on the search settings page will be returned. - * - * @return - * Array of hook_search_info() return values, keyed by module name. The - * 'title' and 'path' array elements will be set to defaults for each module - * if not supplied by hook_search_info(), and an additional array element of - * 'module' will be added (set to the module name). - */ -function search_get_info($all = FALSE) { - $search_hooks = &drupal_static(__FUNCTION__); - - if (!isset($search_hooks)) { - foreach (module_implements('search_info') as $module) { - $search_hooks[$module] = call_user_func($module . '_search_info'); - // Use module name as the default value. - $search_hooks[$module] += array('title' => $module, 'path' => $module); - // Include the module name itself in the array. - $search_hooks[$module]['module'] = $module; - } - } - - if ($all) { - return $search_hooks; - } - - $active = variable_get('search_active_modules', array('node', 'user')); - return array_intersect_key($search_hooks, array_flip($active)); -} - -/** - * Returns information about the default search module. - * - * @return - * The search_get_info() array element for the default search module, if any. - */ -function search_get_default_module_info() { - $info = search_get_info(); - $default = variable_get('search_default_module', 'node'); - if (isset($info[$default])) { - return $info[$default]; - } - // The variable setting does not match any active module, so just return - // the info for the first active module (if any). - return reset($info); -} - -/** - * Access callback for search tabs. - */ -function _search_menu_access($name) { - return user_access('search content') && (!function_exists($name . '_search_access') || module_invoke($name, 'search_access')); -} - -/** - * Clears a part of or the entire search index. - * - * @param $sid - * (optional) The ID of the item to remove from the search index. If - * specified, $module must also be given. Omit both $sid and $module to clear - * the entire search index. - * @param $module - * (optional) The machine-readable name of the module for the item to remove - * from the search index. - */ -function search_reindex($sid = NULL, $module = NULL, $reindex = FALSE) { - if ($module == NULL && $sid == NULL) { - module_invoke_all('search_reset'); - } - else { - db_delete('search_dataset') - ->condition('sid', $sid) - ->condition('type', $module) - ->execute(); - db_delete('search_index') - ->condition('sid', $sid) - ->condition('type', $module) - ->execute(); - // Don't remove links if re-indexing. - if (!$reindex) { - db_delete('search_node_links') - ->condition('sid', $sid) - ->condition('type', $module) - ->execute(); - } - } -} - -/** - * Marks a word as "dirty" (changed), or retrieves the list of dirty words. - * - * This is used during indexing (cron). Words that are dirty have outdated - * total counts in the search_total table, and need to be recounted. - */ -function search_dirty($word = NULL) { - $dirty = &drupal_static(__FUNCTION__, array()); - if ($word !== NULL) { - $dirty[$word] = TRUE; - } - else { - return $dirty; - } -} - -/** - * Implements hook_cron(). - * - * Fires hook_update_index() in all modules and cleans up dirty words. - * - * @see search_dirty() - */ -function search_cron() { - // We register a shutdown function to ensure that search_total is always up - // to date. - drupal_register_shutdown_function('search_update_totals'); - - foreach (variable_get('search_active_modules', array('node', 'user')) as $module) { - // Update word index - module_invoke($module, 'update_index'); - } -} - -/** - * Updates the {search_total} database table. - * - * This function is called on shutdown to ensure that {search_total} is always - * up to date (even if cron times out or otherwise fails). - */ -function search_update_totals() { - // Update word IDF (Inverse Document Frequency) counts for new/changed words. - foreach (search_dirty() as $word => $dummy) { - // Get total count - $total = db_query("SELECT SUM(score) FROM {search_index} WHERE word = :word", array(':word' => $word), array('target' => 'slave'))->fetchField(); - // Apply Zipf's law to equalize the probability distribution. - $total = log10(1 + 1/(max(1, $total))); - db_merge('search_total') - ->key(array('word' => $word)) - ->fields(array('count' => $total)) - ->execute(); - } - // Find words that were deleted from search_index, but are still in - // search_total. We use a LEFT JOIN between the two tables and keep only the - // rows which fail to join. - $result = db_query("SELECT t.word AS realword, i.word FROM {search_total} t LEFT JOIN {search_index} i ON t.word = i.word WHERE i.word IS NULL", array(), array('target' => 'slave')); - $or = db_or(); - foreach ($result as $word) { - $or->condition('word', $word->realword); - } - if (count($or) > 0) { - db_delete('search_total') - ->condition($or) - ->execute(); - } -} - -/** - * Simplifies a string according to indexing rules. - * - * @param $text - * Text to simplify. - * - * @return - * Simplified text. - * - * @see hook_search_preprocess() - */ -function search_simplify($text) { - // Decode entities to UTF-8 - $text = decode_entities($text); - - // Lowercase - $text = drupal_strtolower($text); - - // Call an external processor for word handling. - search_invoke_preprocess($text); - - // Simple CJK handling - if (variable_get('overlap_cjk', TRUE)) { - $text = preg_replace_callback('/[' . PREG_CLASS_CJK . ']+/u', 'search_expand_cjk', $text); - } - - // To improve searching for numerical data such as dates, IP addresses - // or version numbers, we consider a group of numerical characters - // separated only by punctuation characters to be one piece. - // This also means that searching for e.g. '20/03/1984' also returns - // results with '20-03-1984' in them. - // Readable regexp: ([number]+)[punctuation]+(?=[number]) - $text = preg_replace('/([' . PREG_CLASS_NUMBERS . ']+)[' . PREG_CLASS_PUNCTUATION . ']+(?=[' . PREG_CLASS_NUMBERS . '])/u', '\1', $text); - - // Multiple dot and dash groups are word boundaries and replaced with space. - // No need to use the unicode modifer here because 0-127 ASCII characters - // can't match higher UTF-8 characters as the leftmost bit of those are 1. - $text = preg_replace('/[.-]{2,}/', ' ', $text); - - // The dot, underscore and dash are simply removed. This allows meaningful - // search behavior with acronyms and URLs. See unicode note directly above. - $text = preg_replace('/[._-]+/', '', $text); - - // With the exception of the rules above, we consider all punctuation, - // marks, spacers, etc, to be a word boundary. - $text = preg_replace('/[' . PREG_CLASS_UNICODE_WORD_BOUNDARY . ']+/u', ' ', $text); - - // Truncate everything to 50 characters. - $words = explode(' ', $text); - array_walk($words, '_search_index_truncate'); - $text = implode(' ', $words); - - return $text; -} - -/** - * Splits CJK (Chinese, Japanese, Korean) text into tokens. - * - * The Search module matches exact words, where a word is defined to be a - * sequence of characters delimited by spaces or punctuation. CJK languages are - * written in long strings of characters, though, not split up into words. So - * in order to allow search matching, we split up CJK text into tokens - * consisting of consecutive, overlapping sequences of characters whose length - * is equal to the 'minimum_word_size' variable. This tokenizing is only done if - * the 'overlap_cjk' variable is TRUE. - * - * @param $matches - * This function is a callback for preg_replace_callback(), which is called - * from search_simplify(). So, $matches is an array of regular expression - * matches, which means that $matches[0] contains the matched text -- a string - * of CJK characters to tokenize. - * - * @return - * Tokenized text, starting and ending with a space character. - */ -function search_expand_cjk($matches) { - $min = variable_get('minimum_word_size', 3); - $str = $matches[0]; - $length = drupal_strlen($str); - // If the text is shorter than the minimum word size, don't tokenize it. - if ($length <= $min) { - return ' ' . $str . ' '; - } - $tokens = ' '; - // Build a FIFO queue of characters. - $chars = array(); - for ($i = 0; $i < $length; $i++) { - // Add the next character off the beginning of the string to the queue. - $current = drupal_substr($str, 0, 1); - $str = substr($str, strlen($current)); - $chars[] = $current; - if ($i >= $min - 1) { - // Make a token of $min characters, and add it to the token string. - $tokens .= implode('', $chars) . ' '; - // Shift out the first character in the queue. - array_shift($chars); - } - } - return $tokens; -} - -/** - * Simplifies and splits a string into tokens for indexing. - */ -function search_index_split($text) { - $last = &drupal_static(__FUNCTION__); - $lastsplit = &drupal_static(__FUNCTION__ . ':lastsplit'); - - if ($last == $text) { - return $lastsplit; - } - // Process words - $text = search_simplify($text); - $words = explode(' ', $text); - - // Save last keyword result - $last = $text; - $lastsplit = $words; - - return $words; -} - -/** - * Helper function for array_walk in search_index_split. - */ -function _search_index_truncate(&$text) { - if (is_numeric($text)) { - $text = ltrim($text, '0'); - } - $text = truncate_utf8($text, 50); -} - -/** - * Invokes hook_search_preprocess() in modules. - */ -function search_invoke_preprocess(&$text) { - foreach (module_implements('search_preprocess') as $module) { - $text = module_invoke($module, 'search_preprocess', $text); - } -} - -/** - * Update the full-text search index for a particular item. - * - * @param $sid - * An ID number identifying this particular item (e.g., node ID). - * @param $module - * The machine-readable name of the module that this item comes from (a module - * that implements hook_search_info()). - * @param $text - * The content of this item. Must be a piece of HTML or plain text. - * - * @ingroup search - */ -function search_index($sid, $module, $text) { - $minimum_word_size = variable_get('minimum_word_size', 3); - - // Link matching - global $base_url; - $node_regexp = '@href=[\'"]?(?:' . preg_quote($base_url, '@') . '/|' . preg_quote(base_path(), '@') . ')(?:\?q=)?/?((?![a-z]+:)[^\'">]+)[\'">]@i'; - - // Multipliers for scores of words inside certain HTML tags. The weights are stored - // in a variable so that modules can overwrite the default weights. - // Note: 'a' must be included for link ranking to work. - $tags = variable_get('search_tag_weights', array( - 'h1' => 25, - 'h2' => 18, - 'h3' => 15, - 'h4' => 12, - 'h5' => 9, - 'h6' => 6, - 'u' => 3, - 'b' => 3, - 'i' => 3, - 'strong' => 3, - 'em' => 3, - 'a' => 10)); - - // Strip off all ignored tags to speed up processing, but insert space before/after - // them to keep word boundaries. - $text = str_replace(array('<', '>'), array(' <', '> '), $text); - $text = strip_tags($text, '<' . implode('><', array_keys($tags)) . '>'); - - // Split HTML tags from plain text. - $split = preg_split('/\s*<([^>]+?)>\s*/', $text, -1, PREG_SPLIT_DELIM_CAPTURE); - // Note: PHP ensures the array consists of alternating delimiters and literals - // and begins and ends with a literal (inserting $null as required). - - $tag = FALSE; // Odd/even counter. Tag or no tag. - $link = FALSE; // State variable for link analyzer - $score = 1; // Starting score per word - $accum = ' '; // Accumulator for cleaned up data - $tagstack = array(); // Stack with open tags - $tagwords = 0; // Counter for consecutive words - $focus = 1; // Focus state - - $results = array(0 => array()); // Accumulator for words for index - - foreach ($split as $value) { - if ($tag) { - // Increase or decrease score per word based on tag - list($tagname) = explode(' ', $value, 2); - $tagname = drupal_strtolower($tagname); - // Closing or opening tag? - if ($tagname[0] == '/') { - $tagname = substr($tagname, 1); - // If we encounter unexpected tags, reset score to avoid incorrect boosting. - if (!count($tagstack) || $tagstack[0] != $tagname) { - $tagstack = array(); - $score = 1; - } - else { - // Remove from tag stack and decrement score - $score = max(1, $score - $tags[array_shift($tagstack)]); - } - if ($tagname == 'a') { - $link = FALSE; - } - } - else { - if (isset($tagstack[0]) && $tagstack[0] == $tagname) { - // None of the tags we look for make sense when nested identically. - // If they are, it's probably broken HTML. - $tagstack = array(); - $score = 1; - } - else { - // Add to open tag stack and increment score - array_unshift($tagstack, $tagname); - $score += $tags[$tagname]; - } - if ($tagname == 'a') { - // Check if link points to a node on this site - if (preg_match($node_regexp, $value, $match)) { - $path = drupal_get_normal_path($match[1]); - if (preg_match('!(?:node|book)/(?:view/)?([0-9]+)!i', $path, $match)) { - $linknid = $match[1]; - if ($linknid > 0) { - $node = db_query('SELECT title, nid, vid FROM {node} WHERE nid = :nid', array(':nid' => $linknid), array('target' => 'slave'))->fetchObject(); - $link = TRUE; - $linktitle = $node->title; - } - } - } - } - } - // A tag change occurred, reset counter. - $tagwords = 0; - } - else { - // Note: use of PREG_SPLIT_DELIM_CAPTURE above will introduce empty values - if ($value != '') { - if ($link) { - // Check to see if the node link text is its URL. If so, we use the target node title instead. - if (preg_match('!^https?://!i', $value)) { - $value = $linktitle; - } - } - $words = search_index_split($value); - foreach ($words as $word) { - // Add word to accumulator - $accum .= $word . ' '; - // Check wordlength - if (is_numeric($word) || drupal_strlen($word) >= $minimum_word_size) { - // Links score mainly for the target. - if ($link) { - if (!isset($results[$linknid])) { - $results[$linknid] = array(); - } - $results[$linknid][] = $word; - // Reduce score of the link caption in the source. - $focus *= 0.2; - } - // Fall-through - if (!isset($results[0][$word])) { - $results[0][$word] = 0; - } - $results[0][$word] += $score * $focus; - - // Focus is a decaying value in terms of the amount of unique words up to this point. - // From 100 words and more, it decays, to e.g. 0.5 at 500 words and 0.3 at 1000 words. - $focus = min(1, .01 + 3.5 / (2 + count($results[0]) * .015)); - } - $tagwords++; - // Too many words inside a single tag probably mean a tag was accidentally left open. - if (count($tagstack) && $tagwords >= 15) { - $tagstack = array(); - $score = 1; - } - } - } - } - $tag = !$tag; - } - - search_reindex($sid, $module, TRUE); - - // Insert cleaned up data into dataset - db_insert('search_dataset') - ->fields(array( - 'sid' => $sid, - 'type' => $module, - 'data' => $accum, - 'reindex' => 0, - )) - ->execute(); - - // Insert results into search index - foreach ($results[0] as $word => $score) { - // If a word already exists in the database, its score gets increased - // appropriately. If not, we create a new record with the appropriate - // starting score. - db_merge('search_index') - ->key(array( - 'word' => $word, - 'sid' => $sid, - 'type' => $module, - )) - ->fields(array('score' => $score)) - ->expression('score', 'score + :score', array(':score' => $score)) - ->execute(); - search_dirty($word); - } - unset($results[0]); - - // Get all previous links from this item. - $result = db_query("SELECT nid, caption FROM {search_node_links} WHERE sid = :sid AND type = :type", array( - ':sid' => $sid, - ':type' => $module - ), array('target' => 'slave')); - $links = array(); - foreach ($result as $link) { - $links[$link->nid] = $link->caption; - } - - // Now store links to nodes. - foreach ($results as $nid => $words) { - $caption = implode(' ', $words); - if (isset($links[$nid])) { - if ($links[$nid] != $caption) { - // Update the existing link and mark the node for reindexing. - db_update('search_node_links') - ->fields(array('caption' => $caption)) - ->condition('sid', $sid) - ->condition('type', $module) - ->condition('nid', $nid) - ->execute(); - search_touch_node($nid); - } - // Unset the link to mark it as processed. - unset($links[$nid]); - } - elseif ($sid != $nid || $module != 'node') { - // Insert the existing link and mark the node for reindexing, but don't - // reindex if this is a link in a node pointing to itself. - db_insert('search_node_links') - ->fields(array( - 'caption' => $caption, - 'sid' => $sid, - 'type' => $module, - 'nid' => $nid, - )) - ->execute(); - search_touch_node($nid); - } - } - // Any left-over links in $links no longer exist. Delete them and mark the nodes for reindexing. - foreach ($links as $nid => $caption) { - db_delete('search_node_links') - ->condition('sid', $sid) - ->condition('type', $module) - ->condition('nid', $nid) - ->execute(); - search_touch_node($nid); - } -} - -/** - * Changes a node's changed timestamp to 'now' to force reindexing. - * - * @param $nid - * The node ID of the node that needs reindexing. - */ -function search_touch_node($nid) { - db_update('search_dataset') - ->fields(array('reindex' => REQUEST_TIME)) - ->condition('type', 'node') - ->condition('sid', $nid) - ->execute(); -} - -/** - * Implements hook_node_update_index(). - */ -function search_node_update_index($node) { - // Transplant links to a node into the target node. - $result = db_query("SELECT caption FROM {search_node_links} WHERE nid = :nid", array(':nid' => $node->nid), array('target' => 'slave')); - $output = array(); - foreach ($result as $link) { - $output[] = $link->caption; - } - if (count($output)) { - return '(' . implode(', ', $output) . ')'; - } -} - -/** - * Implements hook_node_update(). - */ -function search_node_update($node) { - // Reindex the node when it is updated. The node is automatically indexed - // when it is added, simply by being added to the node table. - search_touch_node($node->nid); -} - -/** - * Implements hook_comment_insert(). - */ -function search_comment_insert($comment) { - // Reindex the node when comments are added. - search_touch_node($comment->nid); -} - -/** - * Implements hook_comment_update(). - */ -function search_comment_update($comment) { - // Reindex the node when comments are changed. - search_touch_node($comment->nid); -} - -/** - * Implements hook_comment_delete(). - */ -function search_comment_delete($comment) { - // Reindex the node when comments are deleted. - search_touch_node($comment->nid); -} - -/** - * Implements hook_comment_publish(). - */ -function search_comment_publish($comment) { - // Reindex the node when comments are published. - search_touch_node($comment->nid); -} - -/** - * Implements hook_comment_unpublish(). - */ -function search_comment_unpublish($comment) { - // Reindex the node when comments are unpublished. - search_touch_node($comment->nid); -} - -/** - * Extracts a module-specific search option from a search expression. - * - * Search options are added using search_expression_insert(), and retrieved - * using search_expression_extract(). They take the form option:value, and - * are added to the ordinary keywords in the search expression. - * - * @param $expression - * The search expression to extract from. - * @param $option - * The name of the option to retrieve from the search expression. - * - * @return - * The value previously stored in the search expression for option $option, - * if any. Trailing spaces in values will not be included. - */ -function search_expression_extract($expression, $option) { - if (preg_match('/(^| )' . $option . ':([^ ]*)( |$)/i', $expression, $matches)) { - return $matches[2]; - } -} - -/** - * Adds a module-specific search option to a search expression. - * - * Search options are added using search_expression_insert(), and retrieved - * using search_expression_extract(). They take the form option:value, and - * are added to the ordinary keywords in the search expression. - * - * @param $expression - * The search expression to add to. - * @param $option - * The name of the option to add to the search expression. - * @param $value - * The value to add for the option. If present, it will replace any previous - * value added for the option. Cannot contain any spaces or | characters, as - * these are used as delimiters. If you want to add a blank value $option: to - * the search expression, pass in an empty string or a string that is composed - * of only spaces. To clear a previously-stored option without adding a - * replacement, pass in NULL for $value or omit. - * - * @return - * $expression, with any previous value for this option removed, and a new - * $option:$value pair added if $value was provided. - */ -function search_expression_insert($expression, $option, $value = NULL) { - // Remove any previous values stored with $option. - $expression = trim(preg_replace('/(^| )' . $option . ':[^ ]*/i', '', $expression)); - - // Set new value, if provided. - if (isset($value)) { - $expression .= ' ' . $option . ':' . trim($value); - } - return $expression; -} - -/** - * @defgroup search Search interface - * @{ - * The Drupal search interface manages a global search mechanism. - * - * Modules may plug into this system to provide searches of different types of - * data. Most of the system is handled by search.module, so this must be enabled - * for all of the search features to work. - * - * There are three ways to interact with the search system: - * - Specifically for searching nodes, you can implement - * hook_node_update_index() and hook_node_search_result(). However, note that - * the search system already indexes all visible output of a node; i.e., - * everything displayed normally by hook_view() and hook_node_view(). This is - * usually sufficient. You should only use this mechanism if you want - * additional, non-visible data to be indexed. - * - Implement hook_search_info(). This will create a search tab for your module - * on the /search page with a simple keyword search form. You will also need - * to implement hook_search_execute() to perform the search. - * - Implement hook_update_index(). This allows your module to use Drupal's - * HTML indexing mechanism for searching full text efficiently. - * - * If your module needs to provide a more complicated search form, then you need - * to implement it yourself without hook_search_info(). In that case, you should - * define it as a local task (tab) under the /search page (e.g. /search/mymodule) - * so that users can easily find it. - */ - -/** - * Builds a search form. - * - * @param $action - * Form action. Defaults to "search/$path", where $path is the search path - * associated with the module in its hook_search_info(). This will be - * run through url(). - * @param $keys - * The search string entered by the user, containing keywords for the search. - * @param $module - * The search module to render the form for: a module that implements - * hook_search_info(). If not supplied, the default search module is used. - * @param $prompt - * Label for the keywords field. Defaults to t('Enter your keywords') if NULL. - * Supply '' to omit. - * - * @return - * A Form API array for the search form. - */ -function search_form($form, &$form_state, $action = '', $keys = '', $module = NULL, $prompt = NULL) { - $module_info = FALSE; - if (!$module) { - $module_info = search_get_default_module_info(); - } - else { - $info = search_get_info(); - $module_info = isset($info[$module]) ? $info[$module] : FALSE; - } - - // Sanity check. - if (!$module_info) { - form_set_error(NULL, t('Search is currently disabled.'), 'error'); - return $form; - } - - if (!$action) { - $action = 'search/' . $module_info['path']; - } - if (!isset($prompt)) { - $prompt = t('Enter your keywords'); - } - - $form['#action'] = url($action); - // Record the $action for later use in redirecting. - $form_state['action'] = $action; - $form['#attributes']['class'][] = 'search-form'; - $form['module'] = array('#type' => 'value', '#value' => $module); - $form['basic'] = array('#type' => 'container', '#attributes' => array('class' => array('container-inline'))); - $form['basic']['keys'] = array( - '#type' => 'textfield', - '#title' => $prompt, - '#default_value' => $keys, - '#size' => $prompt ? 40 : 20, - '#maxlength' => 255, - ); - // processed_keys is used to coordinate keyword passing between other forms - // that hook into the basic search form. - $form['basic']['processed_keys'] = array('#type' => 'value', '#value' => ''); - $form['basic']['submit'] = array('#type' => 'submit', '#value' => t('Search')); - - return $form; -} - -/** - * Form builder; Output a search form for the search block's search box. - * - * @ingroup forms - * @see search_box_form_submit() - * @see search-block-form.tpl.php - */ -function search_box($form, &$form_state, $form_id) { - $form[$form_id] = array( - '#type' => 'textfield', - '#title' => t('Search'), - '#title_display' => 'invisible', - '#size' => 15, - '#default_value' => '', - '#attributes' => array('title' => t('Enter the terms you wish to search for.')), - ); - $form['actions'] = array('#type' => 'actions'); - $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Search')); - $form['#submit'][] = 'search_box_form_submit'; - - return $form; -} - -/** - * Process a block search form submission. - */ -function search_box_form_submit($form, &$form_state) { - // The search form relies on control of the redirect destination for its - // functionality, so we override any static destination set in the request, - // for example by drupal_access_denied() or drupal_not_found() - // (see http://drupal.org/node/292565). - if (isset($_GET['destination'])) { - unset($_GET['destination']); - } - - // Check to see if the form was submitted empty. - // If it is empty, display an error message. - // (This method is used instead of setting #required to TRUE for this field - // because that results in a confusing error message. It would say a plain - // "field is required" because the search keywords field has no title. - // The error message would also complain about a missing #title field.) - if ($form_state['values']['search_block_form'] == '') { - form_set_error('keys', t('Please enter some keywords.')); - } - - $form_id = $form['form_id']['#value']; - $info = search_get_default_module_info(); - if ($info) { - $form_state['redirect'] = 'search/' . $info['path'] . '/' . trim($form_state['values'][$form_id]); - } - else { - form_set_error(NULL, t('Search is currently disabled.'), 'error'); - } -} - -/** - * Process variables for search-block-form.tpl.php. - * - * The $variables array contains the following arguments: - * - $form - * - * @see search-block-form.tpl.php - */ -function template_preprocess_search_block_form(&$variables) { - $variables['search'] = array(); - $hidden = array(); - // Provide variables named after form keys so themers can print each element independently. - foreach (element_children($variables['form']) as $key) { - $type = isset($variables['form'][$key]['#type']) ? $variables['form'][$key]['#type'] : ''; - if ($type == 'hidden' || $type == 'token') { - $hidden[] = drupal_render($variables['form'][$key]); - } - else { - $variables['search'][$key] = drupal_render($variables['form'][$key]); - } - } - // Hidden form elements have no value to themers. No need for separation. - $variables['search']['hidden'] = implode($hidden); - // Collect all form elements to make it easier to print the whole form. - $variables['search_form'] = implode($variables['search']); -} - -/** - * Performs a search by calling hook_search_execute(). - * - * @param $keys - * Keyword query to search on. - * @param $module - * Search module to search. - * @param $conditions - * Optional array of additional search conditions. - * - * @return - * Renderable array of search results. No return value if $keys are not - * supplied or if the given search module is not active. - */ -function search_data($keys, $module, $conditions = NULL) { - if (module_hook($module, 'search_execute')) { - $results = module_invoke($module, 'search_execute', $keys, $conditions); - if (module_hook($module, 'search_page')) { - return module_invoke($module, 'search_page', $results); - } - else { - return array( - '#theme' => 'search_results', - '#results' => $results, - '#module' => $module, - ); - } - } -} - -/** - * Returns snippets from a piece of text, with certain keywords highlighted. - * Used for formatting search results. - * - * @param $keys - * A string containing a search query. - * - * @param $text - * The text to extract fragments from. - * - * @return - * A string containing HTML for the excerpt. - */ -function search_excerpt($keys, $text) { - // We highlight around non-indexable or CJK characters. - $boundary = '(?:(?<=[' . PREG_CLASS_UNICODE_WORD_BOUNDARY . PREG_CLASS_CJK . '])|(?=[' . PREG_CLASS_UNICODE_WORD_BOUNDARY . PREG_CLASS_CJK . ']))'; - - // Extract positive keywords and phrases - preg_match_all('/ ("([^"]+)"|(?!OR)([^" ]+))/', ' ' . $keys, $matches); - $keys = array_merge($matches[2], $matches[3]); - - // Prepare text by stripping HTML tags and decoding HTML entities. - $text = strip_tags(str_replace(array('<', '>'), array(' <', '> '), $text)); - $text = decode_entities($text); - - // Slash-escape quotes in the search keyword string. - array_walk($keys, '_search_excerpt_replace'); - $workkeys = $keys; - - // Extract fragments around keywords. - // First we collect ranges of text around each keyword, starting/ending - // at spaces, trying to get to 256 characters. - // If the sum of all fragments is too short, we look for second occurrences. - $ranges = array(); - $included = array(); - $foundkeys = array(); - $length = 0; - while ($length < 256 && count($workkeys)) { - foreach ($workkeys as $k => $key) { - if (strlen($key) == 0) { - unset($workkeys[$k]); - unset($keys[$k]); - continue; - } - if ($length >= 256) { - break; - } - // Remember occurrence of key so we can skip over it if more occurrences - // are desired. - if (!isset($included[$key])) { - $included[$key] = 0; - } - // Locate a keyword (position $p, always >0 because $text starts with a - // space). First try bare keyword, but if that doesn't work, try to find a - // derived form from search_simplify(). - $p = 0; - if (preg_match('/' . $boundary . $key . $boundary . '/iu', $text, $match, PREG_OFFSET_CAPTURE, $included[$key])) { - $p = $match[0][1]; - } - else { - $info = search_simplify_excerpt_match($key, $text, $included[$key], $boundary); - if ($info['where']) { - $p = $info['where']; - if ($info['keyword']) { - $foundkeys[] = $info['keyword']; - } - } - } - // Now locate a space in front (position $q) and behind it (position $s), - // leaving about 60 characters extra before and after for context. - // Note that a space was added to the front and end of $text above. - if ($p) { - if (($q = strpos(' ' . $text, ' ', max(0, $p - 61))) !== FALSE) { - $end = substr($text . ' ', $p, 80); - if (($s = strrpos($end, ' ')) !== FALSE) { - // Account for the added spaces. - $q = max($q - 1, 0); - $s = min($s, strlen($end) - 1); - $ranges[$q] = $p + $s; - $length += $p + $s - $q; - $included[$key] = $p + 1; - } - else { - unset($workkeys[$k]); - } - } - else { - unset($workkeys[$k]); - } - } - else { - unset($workkeys[$k]); - } - } - } - - if (count($ranges) == 0) { - // We didn't find any keyword matches, so just return the first part of the - // text. We also need to re-encode any HTML special characters that we - // entity-decoded above. - return check_plain(truncate_utf8($text, 256, TRUE, TRUE)); - } - - // Sort the text ranges by starting position. - ksort($ranges); - - // Now we collapse overlapping text ranges into one. The sorting makes it O(n). - $newranges = array(); - foreach ($ranges as $from2 => $to2) { - if (!isset($from1)) { - $from1 = $from2; - $to1 = $to2; - continue; - } - if ($from2 <= $to1) { - $to1 = max($to1, $to2); - } - else { - $newranges[$from1] = $to1; - $from1 = $from2; - $to1 = $to2; - } - } - $newranges[$from1] = $to1; - - // Fetch text - $out = array(); - foreach ($newranges as $from => $to) { - $out[] = substr($text, $from, $to - $from); - } - - // Let translators have the ... separator text as one chunk. - $dots = explode('!excerpt', t('... !excerpt ... !excerpt ...')); - - $text = (isset($newranges[0]) ? '' : $dots[0]) . implode($dots[1], $out) . $dots[2]; - $text = check_plain($text); - - // Slash-escape quotes in keys found in a derived form and merge with original keys. - array_walk($foundkeys, '_search_excerpt_replace'); - $keys = array_merge($keys, $foundkeys); - - // Highlight keywords. Must be done at once to prevent conflicts ('strong' and ''). - $text = preg_replace('/' . $boundary . '(' . implode('|', $keys) . ')' . $boundary . '/iu', '\0', $text); - return $text; -} - -/** - * @} End of "defgroup search". - */ - -/** - * Helper function for array_walk() in search_excerpt(). - */ -function _search_excerpt_replace(&$text) { - $text = preg_quote($text, '/'); -} - -/** - * Find words in the original text that matched via search_simplify(). - * - * This is called in search_excerpt() if an exact match is not found in the - * text, so that we can find the derived form that matches. - * - * @param $key - * The keyword to find. - * @param $text - * The text to search for the keyword. - * @param $offset - * Offset position in $text to start searching at. - * @param $boundary - * Text to include in a regular expression that will match a word boundary. - * - * @return - * FALSE if no match is found. If a match is found, return an associative - * array with element 'where' giving the position of the match, and element - * 'keyword' giving the actual word found in the text at that position. - */ -function search_simplify_excerpt_match($key, $text, $offset, $boundary) { - $pos = NULL; - $simplified_key = search_simplify($key); - $simplified_text = search_simplify($text); - - // Return immediately if simplified key or text are empty. - if (!$simplified_key || !$simplified_text) { - return FALSE; - } - - // Check if we have a match after simplification in the text. - if (!preg_match('/' . $boundary . $simplified_key . $boundary . '/iu', $simplified_text, $match, PREG_OFFSET_CAPTURE, $offset)) { - return FALSE; - } - - // If we get here, we have a match. Now find the exact location of the match - // and the original text that matched. Start by splitting up the text by all - // potential starting points of the matching text and iterating through them. - $split = array_filter(preg_split('/' . $boundary . '/iu', $text, -1, PREG_SPLIT_OFFSET_CAPTURE), '_search_excerpt_match_filter'); - foreach ($split as $value) { - // Skip starting points before the offset. - if ($value[1] < $offset) { - continue; - } - - // Check a window of 80 characters after the starting point for a match, - // based on the size of the excerpt window. - $window = substr($text, $value[1], 80); - $simplified_window = search_simplify($window); - if (strpos($simplified_window, $simplified_key) === 0) { - // We have a match in this window. Store the position of the match. - $pos = $value[1]; - // Iterate through the text in the window until we find the full original - // matching text. - $length = strlen($window); - for ($i = 1; $i <= $length; $i++) { - $keyfound = substr($text, $value[1], $i); - if ($simplified_key == search_simplify($keyfound)) { - break; - } - } - break; - } - } - - return $pos ? array('where' => $pos, 'keyword' => $keyfound) : FALSE; -} - -/** - * Helper function for array_filter() in search_search_excerpt_match(). - */ -function _search_excerpt_match_filter($var) { - return strlen(trim($var[0])); -} - -/** - * Implements hook_forms(). - */ -function search_forms() { - $forms['search_block_form']= array( - 'callback' => 'search_box', - 'callback arguments' => array('search_block_form'), - ); - return $forms; -} - diff --git a/modules/search/search.pages.inc b/modules/search/search.pages.inc deleted file mode 100644 index 9dd00a6..0000000 --- a/modules/search/search.pages.inc +++ /dev/null @@ -1,158 +0,0 @@ - ''); - // Process the search form. Note that if there is $_POST data, - // search_form_submit() will cause a redirect to search/[module path]/[keys], - // which will get us back to this page callback. In other words, the search - // form submits with POST but redirects to GET. This way we can keep - // the search query URL clean as a whistle. - if (empty($_POST['form_id']) || $_POST['form_id'] != 'search_form') { - $conditions = NULL; - if (isset($info['conditions_callback']) && function_exists($info['conditions_callback'])) { - // Build an optional array of more search conditions. - $conditions = call_user_func($info['conditions_callback'], $keys); - } - // Only search if there are keywords or non-empty conditions. - if ($keys || !empty($conditions)) { - // Log the search keys. - watchdog('search', 'Searched %type for %keys.', array('%keys' => $keys, '%type' => $info['title']), WATCHDOG_NOTICE, l(t('results'), 'search/' . $info['path'] . '/' . $keys)); - - // Collect the search results. - $results = search_data($keys, $info['module'], $conditions); - } - } - // The form may be altered based on whether the search was run. - $build['search_form'] = drupal_get_form('search_form', NULL, $keys, $info['module']); - $build['search_results'] = $results; - - return $build; -} - -/** - * Process variables for search-results.tpl.php. - * - * The $variables array contains the following arguments: - * - $results: Search results array. - * - $module: Module the search results came from (module implementing - * hook_search_info()). - * - * @see search-results.tpl.php - */ -function template_preprocess_search_results(&$variables) { - $variables['search_results'] = ''; - if (!empty($variables['module'])) { - $variables['module'] = check_plain($variables['module']); - } - foreach ($variables['results'] as $result) { - $variables['search_results'] .= theme('search_result', array('result' => $result, 'module' => $variables['module'])); - } - $variables['pager'] = theme('pager', array('tags' => NULL)); - $variables['theme_hook_suggestions'][] = 'search_results__' . $variables['module']; -} - -/** - * Process variables for search-result.tpl.php. - * - * The $variables array contains the following arguments: - * - $result - * - $module - * - * @see search-result.tpl.php - */ -function template_preprocess_search_result(&$variables) { - global $language; - - $result = $variables['result']; - $variables['url'] = check_url($result['link']); - $variables['title'] = check_plain($result['title']); - if (isset($result['language']) && $result['language'] != $language->language && $result['language'] != LANGUAGE_NONE) { - $variables['title_attributes_array']['xml:lang'] = $result['language']; - $variables['content_attributes_array']['xml:lang'] = $result['language']; - } - - $info = array(); - if (!empty($result['module'])) { - $info['module'] = check_plain($result['module']); - } - if (!empty($result['user'])) { - $info['user'] = $result['user']; - } - if (!empty($result['date'])) { - $info['date'] = format_date($result['date'], 'short'); - } - if (isset($result['extra']) && is_array($result['extra'])) { - $info = array_merge($info, $result['extra']); - } - // Check for existence. User search does not include snippets. - $variables['snippet'] = isset($result['snippet']) ? $result['snippet'] : ''; - // Provide separated and grouped meta information.. - $variables['info_split'] = $info; - $variables['info'] = implode(' - ', $info); - $variables['theme_hook_suggestions'][] = 'search_result__' . $variables['module']; -} - -/** - * As the search form collates keys from other modules hooked in via - * hook_form_alter, the validation takes place in _submit. - * search_form_validate() is used solely to set the 'processed_keys' form - * value for the basic search form. - */ -function search_form_validate($form, &$form_state) { - form_set_value($form['basic']['processed_keys'], trim($form_state['values']['keys']), $form_state); -} - -/** - * Process a search form submission. - */ -function search_form_submit($form, &$form_state) { - $keys = $form_state['values']['processed_keys']; - if ($keys == '') { - form_set_error('keys', t('Please enter some keywords.')); - // Fall through to the form redirect. - } - - $form_state['redirect'] = $form_state['action'] . '/' . $keys; -} diff --git a/modules/search/search.test b/modules/search/search.test deleted file mode 100644 index 5f16db3..0000000 --- a/modules/search/search.test +++ /dev/null @@ -1,2132 +0,0 @@ - 'Search engine queries', - 'description' => 'Indexes content and queries it.', - 'group' => 'Search', - ); - } - - /** - * Implementation setUp(). - */ - function setUp() { - parent::setUp('search'); - } - - /** - * Test search indexing. - */ - function testMatching() { - $this->_setup(); - $this->_testQueries(); - } - - /** - * Set up a small index of items to test against. - */ - function _setup() { - variable_set('minimum_word_size', 3); - - for ($i = 1; $i <= 7; ++$i) { - search_index($i, SEARCH_TYPE, $this->getText($i)); - } - for ($i = 1; $i <= 5; ++$i) { - search_index($i + 7, SEARCH_TYPE_2, $this->getText2($i)); - } - // No getText builder function for Japanese text; just a simple array. - foreach (array( - 13 => '以呂波耳・ã»ã¸ã¨ã¡ã€‚リヌルヲ。', - 14 => 'ドルーパルãŒå¤§å¥½ãよï¼', - 15 => 'コーヒーã¨ã‚±ãƒ¼ã‚­', - ) as $i => $jpn) { - search_index($i, SEARCH_TYPE_JPN, $jpn); - } - search_update_totals(); - } - - /** - * _test_: Helper method for generating snippets of content. - * - * Generated items to test against: - * 1 ipsum - * 2 dolore sit - * 3 sit am ut - * 4 am ut enim am - * 5 ut enim am minim veniam - * 6 enim am minim veniam es cillum - * 7 am minim veniam es cillum dolore eu - */ - function getText($n) { - $words = explode(' ', "Ipsum dolore sit am. Ut enim am minim veniam. Es cillum dolore eu."); - return implode(' ', array_slice($words, $n - 1, $n)); - } - - /** - * _test2_: Helper method for generating snippets of content. - * - * Generated items to test against: - * 8 dear - * 9 king philip - * 10 philip came over - * 11 came over from germany - * 12 over from germany swimming - */ - function getText2($n) { - $words = explode(' ', "Dear King Philip came over from Germany swimming."); - return implode(' ', array_slice($words, $n - 1, $n)); - } - - /** - * Run predefine queries looking for indexed terms. - */ - function _testQueries() { - /* - Note: OR queries that include short words in OR groups are only accepted - if the ORed terms are ANDed with at least one long word in the rest of the query. - - e.g. enim dolore OR ut = enim (dolore OR ut) = (enim dolor) OR (enim ut) -> good - e.g. dolore OR ut = (dolore) OR (ut) -> bad - - This is a design limitation to avoid full table scans. - */ - $queries = array( - // Simple AND queries. - 'ipsum' => array(1), - 'enim' => array(4, 5, 6), - 'xxxxx' => array(), - 'enim minim' => array(5, 6), - 'enim xxxxx' => array(), - 'dolore eu' => array(7), - 'dolore xx' => array(), - 'ut minim' => array(5), - 'xx minim' => array(), - 'enim veniam am minim ut' => array(5), - // Simple OR queries. - 'dolore OR ipsum' => array(1, 2, 7), - 'dolore OR xxxxx' => array(2, 7), - 'dolore OR ipsum OR enim' => array(1, 2, 4, 5, 6, 7), - 'ipsum OR dolore sit OR cillum' => array(2, 7), - 'minim dolore OR ipsum' => array(7), - 'dolore OR ipsum veniam' => array(7), - 'minim dolore OR ipsum OR enim' => array(5, 6, 7), - 'dolore xx OR yy' => array(), - 'xxxxx dolore OR ipsum' => array(), - // Negative queries. - 'dolore -sit' => array(7), - 'dolore -eu' => array(2), - 'dolore -xxxxx' => array(2, 7), - 'dolore -xx' => array(2, 7), - // Phrase queries. - '"dolore sit"' => array(2), - '"sit dolore"' => array(), - '"am minim veniam es"' => array(6, 7), - '"minim am veniam es"' => array(), - // Mixed queries. - '"am minim veniam es" OR dolore' => array(2, 6, 7), - '"minim am veniam es" OR "dolore sit"' => array(2), - '"minim am veniam es" OR "sit dolore"' => array(), - '"am minim veniam es" -eu' => array(6), - '"am minim veniam" -"cillum dolore"' => array(5, 6), - '"am minim veniam" -"dolore cillum"' => array(5, 6, 7), - 'xxxxx "minim am veniam es" OR dolore' => array(), - 'xx "minim am veniam es" OR dolore' => array() - ); - foreach ($queries as $query => $results) { - $result = db_select('search_index', 'i') - ->extend('SearchQuery') - ->searchExpression($query, SEARCH_TYPE) - ->execute(); - - $set = $result ? $result->fetchAll() : array(); - $this->_testQueryMatching($query, $set, $results); - $this->_testQueryScores($query, $set, $results); - } - - // These queries are run against the second index type, SEARCH_TYPE_2. - $queries = array( - // Simple AND queries. - 'ipsum' => array(), - 'enim' => array(), - 'enim minim' => array(), - 'dear' => array(8), - 'germany' => array(11, 12), - ); - foreach ($queries as $query => $results) { - $result = db_select('search_index', 'i') - ->extend('SearchQuery') - ->searchExpression($query, SEARCH_TYPE_2) - ->execute(); - - $set = $result ? $result->fetchAll() : array(); - $this->_testQueryMatching($query, $set, $results); - $this->_testQueryScores($query, $set, $results); - } - - // These queries are run against the third index type, SEARCH_TYPE_JPN. - $queries = array( - // Simple AND queries. - '呂波耳' => array(13), - '以呂波耳' => array(13), - 'ã»ã¸ã¨ã€€ãƒŒãƒ«ãƒ²' => array(13), - 'ã¨ã¡ãƒª' => array(), - 'ドルーパル' => array(14), - 'パルãŒå¤§' => array(14), - 'コーヒー' => array(15), - 'ヒーキ' => array(), - ); - foreach ($queries as $query => $results) { - $result = db_select('search_index', 'i') - ->extend('SearchQuery') - ->searchExpression($query, SEARCH_TYPE_JPN) - ->execute(); - - $set = $result ? $result->fetchAll() : array(); - $this->_testQueryMatching($query, $set, $results); - $this->_testQueryScores($query, $set, $results); - } - } - - /** - * Test the matching abilities of the engine. - * - * Verify if a query produces the correct results. - */ - function _testQueryMatching($query, $set, $results) { - // Get result IDs. - $found = array(); - foreach ($set as $item) { - $found[] = $item->sid; - } - - // Compare $results and $found. - sort($found); - sort($results); - $this->assertEqual($found, $results, "Query matching '$query'"); - } - - /** - * Test the scoring abilities of the engine. - * - * Verify if a query produces normalized, monotonous scores. - */ - function _testQueryScores($query, $set, $results) { - // Get result scores. - $scores = array(); - foreach ($set as $item) { - $scores[] = $item->calculated_score; - } - - // Check order. - $sorted = $scores; - sort($sorted); - $this->assertEqual($scores, array_reverse($sorted), "Query order '$query'"); - - // Check range. - $this->assertEqual(!count($scores) || (min($scores) > 0.0 && max($scores) <= 1.0001), TRUE, "Query scoring '$query'"); - } -} - -/** - * Tests the bike shed text on no results page, and text on the search page. - */ -class SearchPageText extends DrupalWebTestCase { - protected $searching_user; - - public static function getInfo() { - return array( - 'name' => 'Search page text', - 'description' => 'Tests the bike shed text on the no results page, and various other text on search pages.', - 'group' => 'Search' - ); - } - - function setUp() { - parent::setUp('search'); - - // Create user. - $this->searching_user = $this->drupalCreateUser(array('search content', 'access user profiles')); - } - - /** - * Tests the failed search text, and various other text on the search page. - */ - function testSearchText() { - $this->drupalLogin($this->searching_user); - $this->drupalGet('search/node'); - $this->assertText(t('Enter your keywords')); - $this->assertText(t('Search')); - $title = t('Search') . ' | Drupal'; - $this->assertTitle($title, 'Search page title is correct'); - - $edit = array(); - $edit['keys'] = 'bike shed ' . $this->randomName(); - $this->drupalPost('search/node', $edit, t('Search')); - $this->assertText(t('Consider loosening your query with OR. bike OR shed will often show more results than bike shed.'), 'Help text is displayed when search returns no results.'); - $this->assertText(t('Search')); - $this->assertTitle($title, 'Search page title is correct'); - - $edit['keys'] = $this->searching_user->name; - $this->drupalPost('search/user', $edit, t('Search')); - $this->assertText(t('Search')); - $this->assertTitle($title, 'Search page title is correct'); - - // Test that search keywords containing slashes are correctly loaded - // from the path and displayed in the search form. - $arg = $this->randomName() . '/' . $this->randomName(); - $this->drupalGet('search/node/' . $arg); - $input = $this->xpath("//input[@id='edit-keys' and @value='{$arg}']"); - $this->assertFalse(empty($input), 'Search keys with a / are correctly set as the default value in the search box.'); - - // Test a search input exceeding the limit of AND/OR combinations to test - // the Denial-of-Service protection. - $limit = variable_get('search_and_or_limit', 7); - $keys = array(); - for ($i = 0; $i < $limit + 1; $i++) { - $keys[] = $this->randomName(3); - if ($i % 2 == 0) { - $keys[] = 'OR'; - } - } - $edit['keys'] = implode(' ', $keys); - $this->drupalPost('search/node', $edit, t('Search')); - $this->assertRaw(t('Your search used too many AND/OR expressions. Only the first @count terms were included in this search.', array('@count' => $limit))); - } -} - -/** - * Indexes content and tests the advanced search form. - */ -class SearchAdvancedSearchForm extends DrupalWebTestCase { - protected $node; - - public static function getInfo() { - return array( - 'name' => 'Advanced search form', - 'description' => 'Indexes content and tests the advanced search form.', - 'group' => 'Search', - ); - } - - function setUp() { - parent::setUp('search'); - // Create and login user. - $test_user = $this->drupalCreateUser(array('access content', 'search content', 'use advanced search', 'administer nodes')); - $this->drupalLogin($test_user); - - // Create initial node. - $node = $this->drupalCreateNode(); - $this->node = $this->drupalCreateNode(); - - // First update the index. This does the initial processing. - node_update_index(); - - // Then, run the shutdown function. Testing is a unique case where indexing - // and searching has to happen in the same request, so running the shutdown - // function manually is needed to finish the indexing process. - search_update_totals(); - } - - /** - * Test using the search form with GET and POST queries. - * Test using the advanced search form to limit search to nodes of type "Basic page". - */ - function testNodeType() { - $this->assertTrue($this->node->type == 'page', 'Node type is Basic page.'); - - // Assert that the dummy title doesn't equal the real title. - $dummy_title = 'Lorem ipsum'; - $this->assertNotEqual($dummy_title, $this->node->title, "Dummy title doesn't equal node title"); - - // Search for the dummy title with a GET query. - $this->drupalGet('search/node/' . $dummy_title); - $this->assertNoText($this->node->title, 'Basic page node is not found with dummy title.'); - - // Search for the title of the node with a GET query. - $this->drupalGet('search/node/' . $this->node->title); - $this->assertText($this->node->title, 'Basic page node is found with GET query.'); - - // Search for the title of the node with a POST query. - $edit = array('or' => $this->node->title); - $this->drupalPost('search/node', $edit, t('Advanced search')); - $this->assertText($this->node->title, 'Basic page node is found with POST query.'); - - // Advanced search type option. - $this->drupalPost('search/node', array_merge($edit, array('type[page]' => 'page')), t('Advanced search')); - $this->assertText($this->node->title, 'Basic page node is found with POST query and type:page.'); - - $this->drupalPost('search/node', array_merge($edit, array('type[article]' => 'article')), t('Advanced search')); - $this->assertText('bike shed', 'Article node is not found with POST query and type:article.'); - } -} - -/** - * Indexes content and tests ranking factors. - */ -class SearchRankingTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Search engine ranking', - 'description' => 'Indexes content and tests ranking factors.', - 'group' => 'Search', - ); - } - - /** - * Implementation setUp(). - */ - function setUp() { - parent::setUp('search', 'statistics', 'comment'); - } - - function testRankings() { - // Login with sufficient privileges. - $this->drupalLogin($this->drupalCreateUser(array('skip comment approval', 'create page content'))); - - // Build a list of the rankings to test. - $node_ranks = array('sticky', 'promote', 'relevance', 'recent', 'comments', 'views'); - - // Create nodes for testing. - foreach ($node_ranks as $node_rank) { - $settings = array( - 'type' => 'page', - 'title' => 'Drupal rocks', - 'body' => array(LANGUAGE_NONE => array(array('value' => "Drupal's search rocks"))), - ); - foreach (array(0, 1) as $num) { - if ($num == 1) { - switch ($node_rank) { - case 'sticky': - case 'promote': - $settings[$node_rank] = 1; - break; - case 'relevance': - $settings['body'][LANGUAGE_NONE][0]['value'] .= " really rocks"; - break; - case 'recent': - $settings['created'] = REQUEST_TIME + 3600; - break; - case 'comments': - $settings['comment'] = 2; - break; - } - } - $nodes[$node_rank][$num] = $this->drupalCreateNode($settings); - } - } - - // Update the search index. - module_invoke_all('update_index'); - search_update_totals(); - - // Refresh variables after the treatment. - $this->refreshVariables(); - - // Add a comment to one of the nodes. - $edit = array(); - $edit['subject'] = 'my comment title'; - $edit['comment_body[' . LANGUAGE_NONE . '][0][value]'] = 'some random comment'; - $this->drupalGet('comment/reply/' . $nodes['comments'][1]->nid); - $this->drupalPost(NULL, $edit, t('Preview')); - $this->drupalPost(NULL, $edit, t('Save')); - - // Enable counting of statistics. - variable_set('statistics_count_content_views', 1); - - // Then View one of the nodes a bunch of times. - for ($i = 0; $i < 5; $i ++) { - $this->drupalGet('node/' . $nodes['views'][1]->nid); - } - - // Test each of the possible rankings. - foreach ($node_ranks as $node_rank) { - // Disable all relevancy rankings except the one we are testing. - foreach ($node_ranks as $var) { - variable_set('node_rank_' . $var, $var == $node_rank ? 10 : 0); - } - - // Do the search and assert the results. - $set = node_search_execute('rocks'); - $this->assertEqual($set[0]['node']->nid, $nodes[$node_rank][1]->nid, 'Search ranking "' . $node_rank . '" order.'); - } - } - - /** - * Test rankings of HTML tags. - */ - function testHTMLRankings() { - // Login with sufficient privileges. - $this->drupalLogin($this->drupalCreateUser(array('create page content'))); - - // Test HTML tags with different weights. - $sorted_tags = array('h1', 'h2', 'h3', 'h4', 'a', 'h5', 'h6', 'notag'); - $shuffled_tags = $sorted_tags; - - // Shuffle tags to ensure HTML tags are ranked properly. - shuffle($shuffled_tags); - $settings = array( - 'type' => 'page', - 'title' => 'Simple node', - ); - foreach ($shuffled_tags as $tag) { - switch ($tag) { - case 'a': - $settings['body'] = array(LANGUAGE_NONE => array(array('value' => l('Drupal Rocks', 'node'), 'format' => 'full_html'))); - break; - case 'notag': - $settings['body'] = array(LANGUAGE_NONE => array(array('value' => 'Drupal Rocks'))); - break; - default: - $settings['body'] = array(LANGUAGE_NONE => array(array('value' => "<$tag>Drupal Rocks", 'format' => 'full_html'))); - break; - } - $nodes[$tag] = $this->drupalCreateNode($settings); - } - - // Update the search index. - module_invoke_all('update_index'); - search_update_totals(); - - // Refresh variables after the treatment. - $this->refreshVariables(); - - // Disable all other rankings. - $node_ranks = array('sticky', 'promote', 'recent', 'comments', 'views'); - foreach ($node_ranks as $node_rank) { - variable_set('node_rank_' . $node_rank, 0); - } - $set = node_search_execute('rocks'); - - // Test the ranking of each tag. - foreach ($sorted_tags as $tag_rank => $tag) { - // Assert the results. - if ($tag == 'notag') { - $this->assertEqual($set[$tag_rank]['node']->nid, $nodes[$tag]->nid, 'Search tag ranking for plain text order.'); - } else { - $this->assertEqual($set[$tag_rank]['node']->nid, $nodes[$tag]->nid, 'Search tag ranking for "<' . $sorted_tags[$tag_rank] . '>" order.'); - } - } - - // Test tags with the same weight against the sorted tags. - $unsorted_tags = array('u', 'b', 'i', 'strong', 'em'); - foreach ($unsorted_tags as $tag) { - $settings['body'] = array(LANGUAGE_NONE => array(array('value' => "<$tag>Drupal Rocks", 'format' => 'full_html'))); - $node = $this->drupalCreateNode($settings); - - // Update the search index. - module_invoke_all('update_index'); - search_update_totals(); - - // Refresh variables after the treatment. - $this->refreshVariables(); - - $set = node_search_execute('rocks'); - - // Ranking should always be second to last. - $set = array_slice($set, -2, 1); - - // Assert the results. - $this->assertEqual($set[0]['node']->nid, $node->nid, 'Search tag ranking for "<' . $tag . '>" order.'); - - // Delete node so it doesn't show up in subsequent search results. - node_delete($node->nid); - } - } - - /** - * Verifies that if we combine two rankings, search still works. - * - * See issue http://drupal.org/node/771596 - */ - function testDoubleRankings() { - // Login with sufficient privileges. - $this->drupalLogin($this->drupalCreateUser(array('skip comment approval', 'create page content'))); - - // See testRankings() above - build a node that will rank high for sticky. - $settings = array( - 'type' => 'page', - 'title' => 'Drupal rocks', - 'body' => array(LANGUAGE_NONE => array(array('value' => "Drupal's search rocks"))), - 'sticky' => 1, - ); - - $node = $this->drupalCreateNode($settings); - - // Update the search index. - module_invoke_all('update_index'); - search_update_totals(); - - // Refresh variables after the treatment. - $this->refreshVariables(); - - // Set up for ranking sticky and lots of comments; make sure others are - // disabled. - $node_ranks = array('sticky', 'promote', 'relevance', 'recent', 'comments', 'views'); - foreach ($node_ranks as $var) { - $value = ($var == 'sticky' || $var == 'comments') ? 10 : 0; - variable_set('node_rank_' . $var, $value); - } - - // Do the search and assert the results. - $set = node_search_execute('rocks'); - $this->assertEqual($set[0]['node']->nid, $node->nid, 'Search double ranking order.'); - } -} - -/** - * Tests the rendering of the search block. - */ -class SearchBlockTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Block availability', - 'description' => 'Check if the search form block is available.', - 'group' => 'Search', - ); - } - - function setUp() { - parent::setUp('search'); - - // Create and login user - $admin_user = $this->drupalCreateUser(array('administer blocks', 'search content')); - $this->drupalLogin($admin_user); - } - - function testSearchFormBlock() { - // Set block title to confirm that the interface is available. - $this->drupalPost('admin/structure/block/manage/search/form/configure', array('title' => $this->randomName(8)), t('Save block')); - $this->assertText(t('The block configuration has been saved.'), 'Block configuration set.'); - - // Set the block to a region to confirm block is available. - $edit = array(); - $edit['blocks[search_form][region]'] = 'footer'; - $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); - $this->assertText(t('The block settings have been updated.'), 'Block successfully move to footer region.'); - } - - /** - * Test that the search block form works correctly. - */ - function testBlock() { - // Enable the block, and place it in the 'content' region so that it isn't - // hidden on 404 pages. - $edit = array('blocks[search_form][region]' => 'content'); - $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); - - // Test a normal search via the block form, from the front page. - $terms = array('search_block_form' => 'test'); - $this->drupalPost('node', $terms, t('Search')); - $this->assertText('Your search yielded no results'); - - // Test a search from the block on a 404 page. - $this->drupalGet('foo'); - $this->assertResponse(404); - $this->drupalPost(NULL, $terms, t('Search')); - $this->assertResponse(200); - $this->assertText('Your search yielded no results'); - - // Test a search from the block when it doesn't appear on the search page. - $edit = array('pages' => 'search'); - $this->drupalPost('admin/structure/block/manage/search/form/configure', $edit, t('Save block')); - $this->drupalPost('node', $terms, t('Search')); - $this->assertText('Your search yielded no results'); - - // Confirm that the user is redirected to the search page. - $this->assertEqual( - $this->getUrl(), - url('search/node/' . $terms['search_block_form'], array('absolute' => TRUE)), - 'Redirected to correct url.' - ); - - // Test an empty search via the block form, from the front page. - $terms = array('search_block_form' => ''); - $this->drupalPost('node', $terms, t('Search')); - $this->assertText('Please enter some keywords'); - - // Confirm that the user is redirected to the search page, when form is submitted empty. - $this->assertEqual( - $this->getUrl(), - url('search/node/', array('absolute' => TRUE)), - 'Redirected to correct url.' - ); - } -} - -/** - * Tests that searching for a phrase gets the correct page count. - */ -class SearchExactTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Search engine phrase queries', - 'description' => 'Tests that searching for a phrase gets the correct page count.', - 'group' => 'Search', - ); - } - - function setUp() { - parent::setUp('search'); - } - - /** - * Tests that the correct number of pager links are found for both keywords and phrases. - */ - function testExactQuery() { - // Login with sufficient privileges. - $this->drupalLogin($this->drupalCreateUser(array('create page content', 'search content'))); - - $settings = array( - 'type' => 'page', - 'title' => 'Simple Node', - ); - // Create nodes with exact phrase. - for ($i = 0; $i <= 17; $i++) { - $settings['body'] = array(LANGUAGE_NONE => array(array('value' => 'love pizza'))); - $this->drupalCreateNode($settings); - } - // Create nodes containing keywords. - for ($i = 0; $i <= 17; $i++) { - $settings['body'] = array(LANGUAGE_NONE => array(array('value' => 'love cheesy pizza'))); - $this->drupalCreateNode($settings); - } - - // Update the search index. - module_invoke_all('update_index'); - search_update_totals(); - - // Refresh variables after the treatment. - $this->refreshVariables(); - - // Test that the correct number of pager links are found for keyword search. - $edit = array('keys' => 'love pizza'); - $this->drupalPost('search/node', $edit, t('Search')); - $this->assertLinkByHref('page=1', 0, '2nd page link is found for keyword search.'); - $this->assertLinkByHref('page=2', 0, '3rd page link is found for keyword search.'); - $this->assertLinkByHref('page=3', 0, '4th page link is found for keyword search.'); - $this->assertNoLinkByHref('page=4', '5th page link is not found for keyword search.'); - - // Test that the correct number of pager links are found for exact phrase search. - $edit = array('keys' => '"love pizza"'); - $this->drupalPost('search/node', $edit, t('Search')); - $this->assertLinkByHref('page=1', 0, '2nd page link is found for exact phrase search.'); - $this->assertNoLinkByHref('page=2', '3rd page link is not found for exact phrase search.'); - } -} - -/** - * Test integration searching comments. - */ -class SearchCommentTestCase extends DrupalWebTestCase { - protected $admin_user; - - public static function getInfo() { - return array( - 'name' => 'Comment Search tests', - 'description' => 'Test integration searching comments.', - 'group' => 'Search', - ); - } - - function setUp() { - parent::setUp('comment', 'search'); - - // Create and log in an administrative user having access to the Full HTML - // text format. - $full_html_format = filter_format_load('full_html'); - $permissions = array( - 'administer filters', - filter_permission_name($full_html_format), - 'administer permissions', - 'create page content', - 'skip comment approval', - 'access comments', - ); - $this->admin_user = $this->drupalCreateUser($permissions); - $this->drupalLogin($this->admin_user); - } - - /** - * Verify that comments are rendered using proper format in search results. - */ - function testSearchResultsComment() { - $comment_body = 'Test comment body'; - - variable_set('comment_preview_article', DRUPAL_OPTIONAL); - // Enable check_plain() for 'Filtered HTML' text format. - $filtered_html_format_id = 'filtered_html'; - $edit = array( - 'filters[filter_html_escape][status]' => TRUE, - ); - $this->drupalPost('admin/config/content/formats/' . $filtered_html_format_id, $edit, t('Save configuration')); - // Allow anonymous users to search content. - $edit = array( - DRUPAL_ANONYMOUS_RID . '[search content]' => 1, - DRUPAL_ANONYMOUS_RID . '[access comments]' => 1, - DRUPAL_ANONYMOUS_RID . '[post comments]' => 1, - ); - $this->drupalPost('admin/people/permissions', $edit, t('Save permissions')); - - // Create a node. - $node = $this->drupalCreateNode(array('type' => 'article')); - // Post a comment using 'Full HTML' text format. - $edit_comment = array(); - $edit_comment['subject'] = 'Test comment subject'; - $edit_comment['comment_body[' . LANGUAGE_NONE . '][0][value]'] = '

    ' . $comment_body . '

    '; - $full_html_format_id = 'full_html'; - $edit_comment['comment_body[' . LANGUAGE_NONE . '][0][format]'] = $full_html_format_id; - $this->drupalPost('comment/reply/' . $node->nid, $edit_comment, t('Save')); - - // Invoke search index update. - $this->drupalLogout(); - $this->cronRun(); - - // Search for the comment subject. - $edit = array( - 'search_block_form' => "'" . $edit_comment['subject'] . "'", - ); - $this->drupalPost('', $edit, t('Search')); - $this->assertText($node->title, 'Node found in search results.'); - $this->assertText($edit_comment['subject'], 'Comment subject found in search results.'); - - // Search for the comment body. - $edit = array( - 'search_block_form' => "'" . $comment_body . "'", - ); - $this->drupalPost('', $edit, t('Search')); - $this->assertText($node->title, 'Node found in search results.'); - - // Verify that comment is rendered using proper format. - $this->assertText($comment_body, 'Comment body text found in search results.'); - $this->assertNoRaw(t('n/a'), 'HTML in comment body is not hidden.'); - $this->assertNoRaw(check_plain($edit_comment['comment_body[' . LANGUAGE_NONE . '][0][value]']), 'HTML in comment body is not escaped.'); - - // Hide comments. - $this->drupalLogin($this->admin_user); - $node->comment = 0; - node_save($node); - - // Invoke search index update. - $this->drupalLogout(); - $this->cronRun(); - - // Search for $title. - $this->drupalPost('', $edit, t('Search')); - $this->assertNoText($comment_body, 'Comment body text not found in search results.'); - } - - /** - * Verify access rules for comment indexing with different permissions. - */ - function testSearchResultsCommentAccess() { - $comment_body = 'Test comment body'; - $this->comment_subject = 'Test comment subject'; - $this->admin_role = $this->admin_user->roles; - unset($this->admin_role[DRUPAL_AUTHENTICATED_RID]); - $this->admin_role = key($this->admin_role); - - // Create a node. - variable_set('comment_preview_article', DRUPAL_OPTIONAL); - $this->node = $this->drupalCreateNode(array('type' => 'article')); - - // Post a comment using 'Full HTML' text format. - $edit_comment = array(); - $edit_comment['subject'] = $this->comment_subject; - $edit_comment['comment_body[' . LANGUAGE_NONE . '][0][value]'] = '

    ' . $comment_body . '

    '; - $this->drupalPost('comment/reply/' . $this->node->nid, $edit_comment, t('Save')); - - $this->drupalLogout(); - $this->setRolePermissions(DRUPAL_ANONYMOUS_RID); - $this->checkCommentAccess('Anon user has search permission but no access comments permission, comments should not be indexed'); - - $this->setRolePermissions(DRUPAL_ANONYMOUS_RID, TRUE); - $this->checkCommentAccess('Anon user has search permission and access comments permission, comments should be indexed', TRUE); - - $this->drupalLogin($this->admin_user); - $this->drupalGet('admin/people/permissions'); - - // Disable search access for authenticated user to test admin user. - $this->setRolePermissions(DRUPAL_AUTHENTICATED_RID, FALSE, FALSE); - - $this->setRolePermissions($this->admin_role); - $this->checkCommentAccess('Admin user has search permission but no access comments permission, comments should not be indexed'); - - $this->setRolePermissions($this->admin_role, TRUE); - $this->checkCommentAccess('Admin user has search permission and access comments permission, comments should be indexed', TRUE); - - $this->setRolePermissions(DRUPAL_AUTHENTICATED_RID); - $this->checkCommentAccess('Authenticated user has search permission but no access comments permission, comments should not be indexed'); - - $this->setRolePermissions(DRUPAL_AUTHENTICATED_RID, TRUE); - $this->checkCommentAccess('Authenticated user has search permission and access comments permission, comments should be indexed', TRUE); - - // Verify that access comments permission is inherited from the - // authenticated role. - $this->setRolePermissions(DRUPAL_AUTHENTICATED_RID, TRUE, FALSE); - $this->setRolePermissions($this->admin_role); - $this->checkCommentAccess('Admin user has search permission and no access comments permission, but comments should be indexed because admin user inherits authenticated user\'s permission to access comments', TRUE); - - // Verify that search content permission is inherited from the authenticated - // role. - $this->setRolePermissions(DRUPAL_AUTHENTICATED_RID, TRUE, TRUE); - $this->setRolePermissions($this->admin_role, TRUE, FALSE); - $this->checkCommentAccess('Admin user has access comments permission and no search permission, but comments should be indexed because admin user inherits authenticated user\'s permission to search', TRUE); - - } - - /** - * Set permissions for role. - */ - function setRolePermissions($rid, $access_comments = FALSE, $search_content = TRUE) { - $permissions = array( - 'access comments' => $access_comments, - 'search content' => $search_content, - ); - user_role_change_permissions($rid, $permissions); - } - - /** - * Update search index and search for comment. - */ - function checkCommentAccess($message, $assume_access = FALSE) { - // Invoke search index update. - search_touch_node($this->node->nid); - $this->cronRun(); - - // Search for the comment subject. - $edit = array( - 'search_block_form' => "'" . $this->comment_subject . "'", - ); - $this->drupalPost('', $edit, t('Search')); - $method = $assume_access ? 'assertText' : 'assertNoText'; - $verb = $assume_access ? 'found' : 'not found'; - $this->{$method}($this->node->title, "Node $verb in search results: " . $message); - $this->{$method}($this->comment_subject, "Comment subject $verb in search results: " . $message); - } - - /** - * Verify that 'add new comment' does not appear in search results or index. - */ - function testAddNewComment() { - // Create a node with a short body. - $settings = array( - 'type' => 'article', - 'title' => 'short title', - 'body' => array(LANGUAGE_NONE => array(array('value' => 'short body text'))), - ); - - $user = $this->drupalCreateUser(array('search content', 'create article content', 'access content')); - $this->drupalLogin($user); - - $node = $this->drupalCreateNode($settings); - // Verify that if you view the node on its own page, 'add new comment' - // is there. - $this->drupalGet('node/' . $node->nid); - $this->assertText(t('Add new comment'), 'Add new comment appears on node page'); - - // Run cron to index this page. - $this->drupalLogout(); - $this->cronRun(); - - // Search for 'comment'. Should be no results. - $this->drupalLogin($user); - $this->drupalPost('search/node', array('keys' => 'comment'), t('Search')); - $this->assertText(t('Your search yielded no results'), 'No results searching for the word comment'); - - // Search for the node title. Should be found, and 'Add new comment' should - // not be part of the search snippet. - $this->drupalPost('search/node', array('keys' => 'short'), t('Search')); - $this->assertText($node->title, 'Search for keyword worked'); - $this->assertNoText(t('Add new comment'), 'Add new comment does not appear on search results page'); - } - -} - -/** - * Tests search_expression_insert() and search_expression_extract(). - * - * @see http://drupal.org/node/419388 (issue) - */ -class SearchExpressionInsertExtractTestCase extends DrupalUnitTestCase { - public static function getInfo() { - return array( - 'name' => 'Search expression insert/extract', - 'description' => 'Tests the functions search_expression_insert() and search_expression_extract()', - 'group' => 'Search', - ); - } - - function setUp() { - drupal_load('module', 'search'); - parent::setUp(); - } - - /** - * Tests search_expression_insert() and search_expression_extract(). - */ - function testInsertExtract() { - $base_expression = "mykeyword"; - // Build an array of option, value, what should be in the expression, what - // should be retrieved from expression. - $cases = array( - array('foo', 'bar', 'foo:bar', 'bar'), // Normal case. - array('foo', NULL, '', NULL), // Empty value: shouldn't insert. - array('foo', ' ', 'foo:', ''), // Space as value: should insert but retrieve empty string. - array('foo', '', 'foo:', ''), // Empty string as value: should insert but retrieve empty string. - array('foo', '0', 'foo:0', '0'), // String zero as value: should insert. - array('foo', 0, 'foo:0', '0'), // Numeric zero as value: should insert. - ); - - foreach ($cases as $index => $case) { - $after_insert = search_expression_insert($base_expression, $case[0], $case[1]); - if (empty($case[2])) { - $this->assertEqual($after_insert, $base_expression, "Empty insert does not change expression in case $index"); - } - else { - $this->assertEqual($after_insert, $base_expression . ' ' . $case[2], "Insert added correct expression for case $index"); - } - - $retrieved = search_expression_extract($after_insert, $case[0]); - if (!isset($case[3])) { - $this->assertFalse(isset($retrieved), "Empty retrieval results in unset value in case $index"); - } - else { - $this->assertEqual($retrieved, $case[3], "Value is retrieved for case $index"); - } - - $after_clear = search_expression_insert($after_insert, $case[0]); - $this->assertEqual(trim($after_clear), $base_expression, "After clearing, base expression is restored for case $index"); - - $cleared = search_expression_extract($after_clear, $case[0]); - $this->assertFalse(isset($cleared), "After clearing, value could not be retrieved for case $index"); - } - } -} - -/** - * Tests that comment count display toggles properly on comment status of node - * - * Issue 537278 - * - * - Nodes with comment status set to Open should always how comment counts - * - Nodes with comment status set to Closed should show comment counts - * only when there are comments - * - Nodes with comment status set to Hidden should never show comment counts - */ -class SearchCommentCountToggleTestCase extends DrupalWebTestCase { - protected $searching_user; - protected $searchable_nodes; - - public static function getInfo() { - return array( - 'name' => 'Comment count toggle', - 'description' => 'Verify that comment count display toggles properly on comment status of node.', - 'group' => 'Search', - ); - } - - function setUp() { - parent::setUp('search'); - - // Create searching user. - $this->searching_user = $this->drupalCreateUser(array('search content', 'access content', 'access comments', 'skip comment approval')); - - // Create initial nodes. - $node_params = array('type' => 'article', 'body' => array(LANGUAGE_NONE => array(array('value' => 'SearchCommentToggleTestCase')))); - - $this->searchable_nodes['1 comment'] = $this->drupalCreateNode($node_params); - $this->searchable_nodes['0 comments'] = $this->drupalCreateNode($node_params); - - // Login with sufficient privileges. - $this->drupalLogin($this->searching_user); - - // Create a comment array - $edit_comment = array(); - $edit_comment['subject'] = $this->randomName(); - $edit_comment['comment_body[' . LANGUAGE_NONE . '][0][value]'] = $this->randomName(); - $filtered_html_format_id = 'filtered_html'; - $edit_comment['comment_body[' . LANGUAGE_NONE . '][0][format]'] = $filtered_html_format_id; - - // Post comment to the test node with comment - $this->drupalPost('comment/reply/' . $this->searchable_nodes['1 comment']->nid, $edit_comment, t('Save')); - - // First update the index. This does the initial processing. - node_update_index(); - - // Then, run the shutdown function. Testing is a unique case where indexing - // and searching has to happen in the same request, so running the shutdown - // function manually is needed to finish the indexing process. - search_update_totals(); - } - - /** - * Verify that comment count display toggles properly on comment status of node - */ - function testSearchCommentCountToggle() { - // Search for the nodes by string in the node body. - $edit = array( - 'search_block_form' => "'SearchCommentToggleTestCase'", - ); - - // Test comment count display for nodes with comment status set to Open - $this->drupalPost('', $edit, t('Search')); - $this->assertText(t('0 comments'), 'Empty comment count displays for nodes with comment status set to Open'); - $this->assertText(t('1 comment'), 'Non-empty comment count displays for nodes with comment status set to Open'); - - // Test comment count display for nodes with comment status set to Closed - $this->searchable_nodes['0 comments']->comment = COMMENT_NODE_CLOSED; - node_save($this->searchable_nodes['0 comments']); - $this->searchable_nodes['1 comment']->comment = COMMENT_NODE_CLOSED; - node_save($this->searchable_nodes['1 comment']); - - $this->drupalPost('', $edit, t('Search')); - $this->assertNoText(t('0 comments'), 'Empty comment count does not display for nodes with comment status set to Closed'); - $this->assertText(t('1 comment'), 'Non-empty comment count displays for nodes with comment status set to Closed'); - - // Test comment count display for nodes with comment status set to Hidden - $this->searchable_nodes['0 comments']->comment = COMMENT_NODE_HIDDEN; - node_save($this->searchable_nodes['0 comments']); - $this->searchable_nodes['1 comment']->comment = COMMENT_NODE_HIDDEN; - node_save($this->searchable_nodes['1 comment']); - - $this->drupalPost('', $edit, t('Search')); - $this->assertNoText(t('0 comments'), 'Empty comment count does not display for nodes with comment status set to Hidden'); - $this->assertNoText(t('1 comment'), 'Non-empty comment count does not display for nodes with comment status set to Hidden'); - } -} - -/** - * Test search_simplify() on every Unicode character, and some other cases. - */ -class SearchSimplifyTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Search simplify', - 'description' => 'Check that the search_simply() function works as intended.', - 'group' => 'Search', - ); - } - - /** - * Tests that all Unicode characters simplify correctly. - */ - function testSearchSimplifyUnicode() { - // This test uses a file that was constructed so that the even lines are - // boundary characters, and the odd lines are valid word characters. (It - // was generated as a sequence of all the Unicode characters, and then the - // boundary chararacters (punctuation, spaces, etc.) were split off into - // their own lines). So the even-numbered lines should simplify to nothing, - // and the odd-numbered lines we need to split into shorter chunks and - // verify that simplification doesn't lose any characters. - $input = file_get_contents(DRUPAL_ROOT . '/modules/search/tests/UnicodeTest.txt'); - $basestrings = explode(chr(10), $input); - $strings = array(); - foreach ($basestrings as $key => $string) { - if ($key %2) { - // Even line - should simplify down to a space. - $simplified = search_simplify($string); - $this->assertIdentical($simplified, ' ', "Line $key is excluded from the index"); - } - else { - // Odd line, should be word characters. - // Split this into 30-character chunks, so we don't run into limits - // of truncation in search_simplify(). - $start = 0; - while ($start < drupal_strlen($string)) { - $newstr = drupal_substr($string, $start, 30); - // Special case: leading zeros are removed from numeric strings, - // and there's one string in this file that is numbers starting with - // zero, so prepend a 1 on that string. - if (preg_match('/^[0-9]+$/', $newstr)) { - $newstr = '1' . $newstr; - } - $strings[] = $newstr; - $start += 30; - } - } - } - foreach ($strings as $key => $string) { - $simplified = search_simplify($string); - $this->assertTrue(drupal_strlen($simplified) >= drupal_strlen($string), "Nothing is removed from string $key."); - } - - // Test the low-numbered ASCII control characters separately. They are not - // in the text file because they are problematic for diff, especially \0. - $string = ''; - for ($i = 0; $i < 32; $i++) { - $string .= chr($i); - } - $this->assertIdentical(' ', search_simplify($string), 'Search simplify works for ASCII control characters.'); - } - - /** - * Tests that search_simplify() does the right thing with punctuation. - */ - function testSearchSimplifyPunctuation() { - $cases = array( - array('20.03/94-28,876', '20039428876', 'Punctuation removed from numbers'), - array('great...drupal--module', 'great drupal module', 'Multiple dot and dashes are word boundaries'), - array('very_great-drupal.module', 'verygreatdrupalmodule', 'Single dot, dash, underscore are removed'), - array('regular,punctuation;word', 'regular punctuation word', 'Punctuation is a word boundary'), - ); - - foreach ($cases as $case) { - $out = trim(search_simplify($case[0])); - $this->assertEqual($out, $case[1], $case[2]); - } - } -} - - -/** - * Tests keywords and conditions. - */ -class SearchKeywordsConditions extends DrupalWebTestCase { - - public static function getInfo() { - return array( - 'name' => 'Keywords and conditions', - 'description' => 'Verify the search pulls in keywords and extra conditions.', - 'group' => 'Search', - ); - } - - function setUp() { - parent::setUp('search', 'search_extra_type'); - // Create searching user. - $this->searching_user = $this->drupalCreateUser(array('search content', 'access content', 'access comments', 'skip comment approval')); - // Login with sufficient privileges. - $this->drupalLogin($this->searching_user); - // Test with all search modules enabled. - variable_set('search_active_modules', array('node' => 'node', 'user' => 'user', 'search_extra_type' => 'search_extra_type')); - menu_rebuild(); - } - - /** - * Verify the kewords are captured and conditions respected. - */ - function testSearchKeyswordsConditions() { - // No keys, not conditions - no results. - $this->drupalGet('search/dummy_path'); - $this->assertNoText('Dummy search snippet to display'); - // With keys - get results. - $keys = 'bike shed ' . $this->randomName(); - $this->drupalGet("search/dummy_path/{$keys}"); - $this->assertText("Dummy search snippet to display. Keywords: {$keys}"); - $keys = 'blue drop ' . $this->randomName(); - $this->drupalGet("search/dummy_path", array('query' => array('keys' => $keys))); - $this->assertText("Dummy search snippet to display. Keywords: {$keys}"); - // Add some conditions and keys. - $keys = 'moving drop ' . $this->randomName(); - $this->drupalGet("search/dummy_path/bike", array('query' => array('search_conditions' => $keys))); - $this->assertText("Dummy search snippet to display."); - $this->assertRaw(print_r(array('search_conditions' => $keys), TRUE)); - // Add some conditions and no keys. - $keys = 'drop kick ' . $this->randomName(); - $this->drupalGet("search/dummy_path", array('query' => array('search_conditions' => $keys))); - $this->assertText("Dummy search snippet to display."); - $this->assertRaw(print_r(array('search_conditions' => $keys), TRUE)); - } -} - -/** - * Tests that numbers can be searched. - */ -class SearchNumbersTestCase extends DrupalWebTestCase { - protected $test_user; - protected $numbers; - protected $nodes; - - public static function getInfo() { - return array( - 'name' => 'Search numbers', - 'description' => 'Check that numbers can be searched', - 'group' => 'Search', - ); - } - - function setUp() { - parent::setUp('search'); - - $this->test_user = $this->drupalCreateUser(array('search content', 'access content', 'administer nodes', 'access site reports')); - $this->drupalLogin($this->test_user); - - // Create content with various numbers in it. - // Note: 50 characters is the current limit of the search index's word - // field. - $this->numbers = array( - 'ISBN' => '978-0446365383', - 'UPC' => '036000 291452', - 'EAN bar code' => '5901234123457', - 'negative' => '-123456.7890', - 'quoted negative' => '"-123456.7890"', - 'leading zero' => '0777777777', - 'tiny' => '111', - 'small' => '22222222222222', - 'medium' => '333333333333333333333333333', - 'large' => '444444444444444444444444444444444444444', - 'gigantic' => '5555555555555555555555555555555555555555555555555', - 'over fifty characters' => '666666666666666666666666666666666666666666666666666666666666', - 'date', '01/02/2009', - 'commas', '987,654,321', - ); - - foreach ($this->numbers as $doc => $num) { - $info = array( - 'body' => array(LANGUAGE_NONE => array(array('value' => $num))), - 'type' => 'page', - 'language' => LANGUAGE_NONE, - 'title' => $doc . ' number', - ); - $this->nodes[$doc] = $this->drupalCreateNode($info); - } - - // Run cron to ensure the content is indexed. - $this->cronRun(); - $this->drupalGet('admin/reports/dblog'); - $this->assertText(t('Cron run completed'), 'Log shows cron run completed'); - } - - /** - * Tests that all the numbers can be searched. - */ - function testNumberSearching() { - $types = array_keys($this->numbers); - - foreach ($types as $type) { - $number = $this->numbers[$type]; - // If the number is negative, remove the - sign, because - indicates - // "not keyword" when searching. - $number = ltrim($number, '-'); - $node = $this->nodes[$type]; - - // Verify that the node title does not appear on the search page - // with a dummy search. - $this->drupalPost('search/node', - array('keys' => 'foo'), - t('Search')); - $this->assertNoText($node->title, $type . ': node title not shown in dummy search'); - - // Verify that the node title does appear as a link on the search page - // when searching for the number. - $this->drupalPost('search/node', - array('keys' => $number), - t('Search')); - $this->assertText($node->title, format_string('%type: node title shown (search found the node) in search for number %number.', array('%type' => $type, '%number' => $number))); - } - } -} - -/** - * Tests that numbers can be searched, with more complex matching. - */ -class SearchNumberMatchingTestCase extends DrupalWebTestCase { - protected $test_user; - protected $numbers; - protected $nodes; - - public static function getInfo() { - return array( - 'name' => 'Search number matching', - 'description' => 'Check that numbers can be searched with more complex matching', - 'group' => 'Search', - ); - } - - function setUp() { - parent::setUp('search'); - - $this->test_user = $this->drupalCreateUser(array('search content', 'access content', 'administer nodes', 'access site reports')); - $this->drupalLogin($this->test_user); - - // Define a group of numbers that should all match each other -- - // numbers with internal punctuation should match each other, as well - // as numbers with and without leading zeros and leading/trailing - // . and -. - $this->numbers = array( - '123456789', - '12/34/56789', - '12.3456789', - '12-34-56789', - '123,456,789', - '-123456789', - '0123456789', - ); - - foreach ($this->numbers as $num) { - $info = array( - 'body' => array(LANGUAGE_NONE => array(array('value' => $num))), - 'type' => 'page', - 'language' => LANGUAGE_NONE, - ); - $this->nodes[] = $this->drupalCreateNode($info); - } - - // Run cron to ensure the content is indexed. - $this->cronRun(); - $this->drupalGet('admin/reports/dblog'); - $this->assertText(t('Cron run completed'), 'Log shows cron run completed'); - } - - /** - * Tests that all the numbers can be searched. - */ - function testNumberSearching() { - for ($i = 0; $i < count($this->numbers); $i++) { - $node = $this->nodes[$i]; - - // Verify that the node title does not appear on the search page - // with a dummy search. - $this->drupalPost('search/node', - array('keys' => 'foo'), - t('Search')); - $this->assertNoText($node->title, format_string('%number: node title not shown in dummy search', array('%number' => $i))); - - // Now verify that we can find node i by searching for any of the - // numbers. - for ($j = 0; $j < count($this->numbers); $j++) { - $number = $this->numbers[$j]; - // If the number is negative, remove the - sign, because - indicates - // "not keyword" when searching. - $number = ltrim($number, '-'); - - $this->drupalPost('search/node', - array('keys' => $number), - t('Search')); - $this->assertText($node->title, format_string('%i: node title shown (search found the node) in search for number %number', array('%i' => $i, '%number' => $number))); - } - } - - } -} - -/** - * Test config page. - */ -class SearchConfigSettingsForm extends DrupalWebTestCase { - public $search_user; - public $search_node; - - public static function getInfo() { - return array( - 'name' => 'Config settings form', - 'description' => 'Verify the search config settings form.', - 'group' => 'Search', - ); - } - - function setUp() { - parent::setUp('search', 'search_extra_type'); - - // Login as a user that can create and search content. - $this->search_user = $this->drupalCreateUser(array('search content', 'administer search', 'administer nodes', 'bypass node access', 'access user profiles', 'administer users', 'administer blocks')); - $this->drupalLogin($this->search_user); - - // Add a single piece of content and index it. - $node = $this->drupalCreateNode(); - $this->search_node = $node; - // Link the node to itself to test that it's only indexed once. The content - // also needs the word "pizza" so we can use it as the search keyword. - $langcode = LANGUAGE_NONE; - $body_key = "body[$langcode][0][value]"; - $edit[$body_key] = l($node->title, 'node/' . $node->nid) . ' pizza sandwich'; - $this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save')); - - node_update_index(); - search_update_totals(); - - // Enable the search block. - $edit = array(); - $edit['blocks[search_form][region]'] = 'content'; - $this->drupalPost('admin/structure/block', $edit, t('Save blocks')); - } - - /** - * Verify the search settings form. - */ - function testSearchSettingsPage() { - - // Test that the settings form displays the correct count of items left to index. - $this->drupalGet('admin/config/search/settings'); - $this->assertText(t('There are @count items left to index.', array('@count' => 0))); - - // Test the re-index button. - $this->drupalPost('admin/config/search/settings', array(), t('Re-index site')); - $this->assertText(t('Are you sure you want to re-index the site')); - $this->drupalPost('admin/config/search/settings/reindex', array(), t('Re-index site')); - $this->assertText(t('The index will be rebuilt')); - $this->drupalGet('admin/config/search/settings'); - $this->assertText(t('There is 1 item left to index.')); - - // Test that the form saves with the default values. - $this->drupalPost('admin/config/search/settings', array(), t('Save configuration')); - $this->assertText(t('The configuration options have been saved.'), 'Form saves with the default values.'); - - // Test that the form does not save with an invalid word length. - $edit = array( - 'minimum_word_size' => $this->randomName(3), - ); - $this->drupalPost('admin/config/search/settings', $edit, t('Save configuration')); - $this->assertNoText(t('The configuration options have been saved.'), 'Form does not save with an invalid word length.'); - } - - /** - * Verify that you can disable individual search modules. - */ - function testSearchModuleDisabling() { - // Array of search modules to test: 'path' is the search path, 'title' is - // the tab title, 'keys' are the keywords to search for, and 'text' is - // the text to assert is on the results page. - $module_info = array( - 'node' => array( - 'path' => 'node', - 'title' => 'Content', - 'keys' => 'pizza', - 'text' => $this->search_node->title, - ), - 'user' => array( - 'path' => 'user', - 'title' => 'User', - 'keys' => $this->search_user->name, - 'text' => $this->search_user->mail, - ), - 'search_extra_type' => array( - 'path' => 'dummy_path', - 'title' => 'Dummy search type', - 'keys' => 'foo', - 'text' => 'Dummy search snippet to display', - ), - ); - $modules = array_keys($module_info); - - // Test each module if it's enabled as the only search module. - foreach ($modules as $module) { - // Enable the one module and disable other ones. - $info = $module_info[$module]; - $edit = array(); - foreach ($modules as $other) { - $edit['search_active_modules[' . $other . ']'] = (($other == $module) ? $module : FALSE); - } - $edit['search_default_module'] = $module; - $this->drupalPost('admin/config/search/settings', $edit, t('Save configuration')); - - // Run a search from the correct search URL. - $this->drupalGet('search/' . $info['path'] . '/' . $info['keys']); - $this->assertNoText('no results', $info['title'] . ' search found results'); - $this->assertText($info['text'], 'Correct search text found'); - - // Verify that other module search tab titles are not visible. - foreach ($modules as $other) { - if ($other != $module) { - $title = $module_info[$other]['title']; - $this->assertNoText($title, $title . ' search tab is not shown'); - } - } - - // Run a search from the search block on the node page. Verify you get - // to this module's search results page. - $terms = array('search_block_form' => $info['keys']); - $this->drupalPost('node', $terms, t('Search')); - $this->assertEqual( - $this->getURL(), - url('search/' . $info['path'] . '/' . $info['keys'], array('absolute' => TRUE)), - 'Block redirected to right search page'); - - // Try an invalid search path. Should redirect to our active module. - $this->drupalGet('search/not_a_module_path'); - $this->assertEqual( - $this->getURL(), - url('search/' . $info['path'], array('absolute' => TRUE)), - 'Invalid search path redirected to default search page'); - } - - // Test with all search modules enabled. When you go to the search - // page or run search, all modules should be shown. - $edit = array(); - foreach ($modules as $module) { - $edit['search_active_modules[' . $module . ']'] = $module; - } - $edit['search_default_module'] = 'node'; - - $this->drupalPost('admin/config/search/settings', $edit, t('Save configuration')); - - foreach (array('search/node/pizza', 'search/node') as $path) { - $this->drupalGet($path); - foreach ($modules as $module) { - $title = $module_info[$module]['title']; - $this->assertText($title, format_string('%title search tab is shown', array('%title' => $title))); - } - } - } -} - -/** - * Tests the search_excerpt() function. - */ -class SearchExcerptTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Search excerpt extraction', - 'description' => 'Tests that the search_excerpt() function works.', - 'group' => 'Search', - ); - } - - function setUp() { - parent::setUp('search'); - } - - /** - * Tests search_excerpt() with several simulated search keywords. - * - * Passes keywords and a sample marked up string, "The quick - * brown fox jumps over the lazy dog", and compares it to the - * correctly marked up string. The correctly marked up string - * contains either highlighted keywords or the original marked - * up string if no keywords matched the string. - */ - function testSearchExcerpt() { - // Make some text with entities and tags. - $text = 'The quick brown fox & jumps

    over

    the lazy dog'; - // Note: The search_excerpt() function adds some extra spaces -- not - // important for HTML formatting. Remove these for comparison. - $expected = 'The quick brown fox & jumps over the lazy dog'; - $result = preg_replace('| +|', ' ', search_excerpt('nothing', $text)); - $this->assertEqual(preg_replace('| +|', ' ', $result), $expected, 'Entire string is returned when keyword is not found in short string'); - - $result = preg_replace('| +|', ' ', search_excerpt('fox', $text)); - $this->assertEqual($result, 'The quick brown fox & jumps over the lazy dog ...', 'Found keyword is highlighted'); - - $longtext = str_repeat($text . ' ', 10); - $result = preg_replace('| +|', ' ', search_excerpt('nothing', $longtext)); - $this->assertTrue(strpos($result, $expected) === 0, 'When keyword is not found in long string, return value starts as expected'); - - $entities = str_repeat('készítése ', 20); - $result = preg_replace('| +|', ' ', search_excerpt('nothing', $entities)); - $this->assertFalse(strpos($result, '&'), 'Entities are not present in excerpt'); - $this->assertTrue(strpos($result, 'í') > 0, 'Entities are converted in excerpt'); - - // The node body that will produce this rendered $text is: - // 123456789 HTMLTest +123456789+‘ +‘ +‘ +‘ +12345678    +‘ +‘ +‘ ‘ - $text = "

    123456789 HTMLTest +123456789+‘ +‘ +‘ +‘ +12345678    +‘ +‘ +‘ ‘

    \n
    "; - $result = search_excerpt('HTMLTest', $text); - $this->assertFalse(empty($result), 'Rendered Multi-byte HTML encodings are not corrupted in search excerpts'); - } - - /** - * Tests search_excerpt() with search keywords matching simplified words. - * - * Excerpting should handle keywords that are matched only after going through - * search_simplify(). This test passes keywords that match simplified words - * and compares them with strings that contain the original unsimplified word. - */ - function testSearchExcerptSimplified() { - $lorem1 = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam vitae arcu at leo cursus laoreet. Curabitur dui tortor, adipiscing malesuada tempor in, bibendum ac diam. Cras non tellus a libero pellentesque condimentum. What is a Drupalism? Suspendisse ac lacus libero. Ut non est vel nisl faucibus interdum nec sed leo. Pellentesque sem risus, vulputate eu semper eget, auctor in libero.'; - $lorem2 = 'Ut fermentum est vitae metus convallis scelerisque. Phasellus pellentesque rhoncus tellus, eu dignissim purus posuere id. Quisque eu fringilla ligula. Morbi ullamcorper, lorem et mattis egestas, tortor neque pretium velit, eget eleifend odio turpis eu purus. Donec vitae metus quis leo pretium tincidunt a pulvinar sem. Morbi adipiscing laoreet mauris vel placerat. Nullam elementum, nisl sit amet scelerisque malesuada, dolor nunc hendrerit quam, eu ultrices erat est in orci.'; - - // Make some text with some keywords that will get simplified. - $text = $lorem1 . ' Number: 123456.7890 Hyphenated: one-two abc,def ' . $lorem2; - // Note: The search_excerpt() function adds some extra spaces -- not - // important for HTML formatting. Remove these for comparison. - $result = preg_replace('| +|', ' ', search_excerpt('123456.7890', $text)); - $this->assertTrue(strpos($result, 'Number: 123456.7890') !== FALSE, 'Numeric keyword is highlighted with exact match'); - - $result = preg_replace('| +|', ' ', search_excerpt('1234567890', $text)); - $this->assertTrue(strpos($result, 'Number: 123456.7890') !== FALSE, 'Numeric keyword is highlighted with simplified match'); - - $result = preg_replace('| +|', ' ', search_excerpt('Number 1234567890', $text)); - $this->assertTrue(strpos($result, 'Number: 123456.7890') !== FALSE, 'Punctuated and numeric keyword is highlighted with simplified match'); - - $result = preg_replace('| +|', ' ', search_excerpt('"Number 1234567890"', $text)); - $this->assertTrue(strpos($result, 'Number: 123456.7890') !== FALSE, 'Phrase with punctuated and numeric keyword is highlighted with simplified match'); - - $result = preg_replace('| +|', ' ', search_excerpt('"Hyphenated onetwo"', $text)); - $this->assertTrue(strpos($result, 'Hyphenated: one-two') !== FALSE, 'Phrase with punctuated and hyphenated keyword is highlighted with simplified match'); - - $result = preg_replace('| +|', ' ', search_excerpt('"abc def"', $text)); - $this->assertTrue(strpos($result, 'abc,def') !== FALSE, 'Phrase with keyword simplified into two separate words is highlighted with simplified match'); - - // Test phrases with characters which are being truncated. - $result = preg_replace('| +|', ' ', search_excerpt('"ipsum _"', $text)); - $this->assertTrue(strpos($result, 'ipsum ') !== FALSE, 'Only valid part of the phrase is highlighted and invalid part containing "_" is ignored.'); - - $result = preg_replace('| +|', ' ', search_excerpt('"ipsum 0000"', $text)); - $this->assertTrue(strpos($result, 'ipsum ') !== FALSE, 'Only valid part of the phrase is highlighted and invalid part "0000" is ignored.'); - - // Test combination of the valid keyword and keyword containing only - // characters which are being truncated during simplification. - $result = preg_replace('| +|', ' ', search_excerpt('ipsum _', $text)); - $this->assertTrue(strpos($result, 'ipsum') !== FALSE, 'Only valid keyword is highlighted and invalid keyword "_" is ignored.'); - - $result = preg_replace('| +|', ' ', search_excerpt('ipsum 0000', $text)); - $this->assertTrue(strpos($result, 'ipsum') !== FALSE, 'Only valid keyword is highlighted and invalid keyword "0000" is ignored.'); - } -} - -/** - * Test the CJK tokenizer. - */ -class SearchTokenizerTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'CJK tokenizer', - 'description' => 'Check that CJK tokenizer works as intended.', - 'group' => 'Search', - ); - } - - function setUp() { - parent::setUp('search'); - } - - /** - * Verifies that strings of CJK characters are tokenized. - * - * The search_simplify() function does special things with numbers, symbols, - * and punctuation. So we only test that CJK characters that are not in these - * character classes are tokenized properly. See PREG_CLASS_CKJ for more - * information. - */ - function testTokenizer() { - // Set the minimum word size to 1 (to split all CJK characters) and make - // sure CJK tokenizing is turned on. - variable_set('minimum_word_size', 1); - variable_set('overlap_cjk', TRUE); - $this->refreshVariables(); - - // Create a string of CJK characters from various character ranges in - // the Unicode tables. - - // Beginnings of the character ranges. - $starts = array( - 'CJK unified' => 0x4e00, - 'CJK Ext A' => 0x3400, - 'CJK Compat' => 0xf900, - 'Hangul Jamo' => 0x1100, - 'Hangul Ext A' => 0xa960, - 'Hangul Ext B' => 0xd7b0, - 'Hangul Compat' => 0x3131, - 'Half non-punct 1' => 0xff21, - 'Half non-punct 2' => 0xff41, - 'Half non-punct 3' => 0xff66, - 'Hangul Syllables' => 0xac00, - 'Hiragana' => 0x3040, - 'Katakana' => 0x30a1, - 'Katakana Ext' => 0x31f0, - 'CJK Reserve 1' => 0x20000, - 'CJK Reserve 2' => 0x30000, - 'Bomofo' => 0x3100, - 'Bomofo Ext' => 0x31a0, - 'Lisu' => 0xa4d0, - 'Yi' => 0xa000, - ); - - // Ends of the character ranges. - $ends = array( - 'CJK unified' => 0x9fcf, - 'CJK Ext A' => 0x4dbf, - 'CJK Compat' => 0xfaff, - 'Hangul Jamo' => 0x11ff, - 'Hangul Ext A' => 0xa97f, - 'Hangul Ext B' => 0xd7ff, - 'Hangul Compat' => 0x318e, - 'Half non-punct 1' => 0xff3a, - 'Half non-punct 2' => 0xff5a, - 'Half non-punct 3' => 0xffdc, - 'Hangul Syllables' => 0xd7af, - 'Hiragana' => 0x309f, - 'Katakana' => 0x30ff, - 'Katakana Ext' => 0x31ff, - 'CJK Reserve 1' => 0x2fffd, - 'CJK Reserve 2' => 0x3fffd, - 'Bomofo' => 0x312f, - 'Bomofo Ext' => 0x31b7, - 'Lisu' => 0xa4fd, - 'Yi' => 0xa48f, - ); - - // Generate characters consisting of starts, midpoints, and ends. - $chars = array(); - $charcodes = array(); - foreach ($starts as $key => $value) { - $charcodes[] = $starts[$key]; - $chars[] = $this->code2utf($starts[$key]); - $mid = round(0.5 * ($starts[$key] + $ends[$key])); - $charcodes[] = $mid; - $chars[] = $this->code2utf($mid); - $charcodes[] = $ends[$key]; - $chars[] = $this->code2utf($ends[$key]); - } - - // Merge into a string and tokenize. - $string = implode('', $chars); - $out = trim(search_simplify($string)); - $expected = drupal_strtolower(implode(' ', $chars)); - - // Verify that the output matches what we expect. - $this->assertEqual($out, $expected, 'CJK tokenizer worked on all supplied CJK characters'); - } - - /** - * Verifies that strings of non-CJK characters are not tokenized. - * - * This is just a sanity check - it verifies that strings of letters are - * not tokenized. - */ - function testNoTokenizer() { - // Set the minimum word size to 1 (to split all CJK characters) and make - // sure CJK tokenizing is turned on. - variable_set('minimum_word_size', 1); - variable_set('overlap_cjk', TRUE); - $this->refreshVariables(); - - $letters = 'abcdefghijklmnopqrstuvwxyz'; - $out = trim(search_simplify($letters)); - - $this->assertEqual($letters, $out, 'Letters are not CJK tokenized'); - } - - /** - * Like PHP chr() function, but for unicode characters. - * - * chr() only works for ASCII characters up to character 255. This function - * converts a number to the corresponding unicode character. Adapted from - * functions supplied in comments on several functions on php.net. - */ - function code2utf($num) { - if ($num < 128) { - return chr($num); - } - - if ($num < 2048) { - return chr(($num >> 6) + 192) . chr(($num & 63) + 128); - } - - if ($num < 65536) { - return chr(($num >> 12) + 224) . chr((($num >> 6) & 63) + 128) . chr(($num & 63) + 128); - } - - if ($num < 2097152) { - return chr(($num >> 18) + 240) . chr((($num >> 12) & 63) + 128) . chr((($num >> 6) & 63) + 128) . chr(($num & 63) + 128); - } - - return ''; - } -} - -/** - * Tests that we can embed a form in search results and submit it. - */ -class SearchEmbedForm extends DrupalWebTestCase { - /** - * Node used for testing. - */ - public $node; - - /** - * Count of how many times the form has been submitted. - */ - public $submit_count = 0; - - public static function getInfo() { - return array( - 'name' => 'Embedded forms', - 'description' => 'Verifies that a form embedded in search results works', - 'group' => 'Search', - ); - } - - function setUp() { - parent::setUp('search', 'search_embedded_form'); - - // Create a user and a node, and update the search index. - $test_user = $this->drupalCreateUser(array('access content', 'search content', 'administer nodes')); - $this->drupalLogin($test_user); - - $this->node = $this->drupalCreateNode(); - - node_update_index(); - search_update_totals(); - - // Set up a dummy initial count of times the form has been submitted. - $this->submit_count = 12; - variable_set('search_embedded_form_submitted', $this->submit_count); - $this->refreshVariables(); - } - - /** - * Tests that the embedded form appears and can be submitted. - */ - function testEmbeddedForm() { - // First verify we can submit the form from the module's page. - $this->drupalPost('search_embedded_form', - array('name' => 'John'), - t('Send away')); - $this->assertText(t('Test form was submitted'), 'Form message appears'); - $count = variable_get('search_embedded_form_submitted', 0); - $this->assertEqual($this->submit_count + 1, $count, 'Form submission count is correct'); - $this->submit_count = $count; - - // Now verify that we can see and submit the form from the search results. - $this->drupalGet('search/node/' . $this->node->title); - $this->assertText(t('Your name'), 'Form is visible'); - $this->drupalPost('search/node/' . $this->node->title, - array('name' => 'John'), - t('Send away')); - $this->assertText(t('Test form was submitted'), 'Form message appears'); - $count = variable_get('search_embedded_form_submitted', 0); - $this->assertEqual($this->submit_count + 1, $count, 'Form submission count is correct'); - $this->submit_count = $count; - - // Now verify that if we submit the search form, it doesn't count as - // our form being submitted. - $this->drupalPost('search', - array('keys' => 'foo'), - t('Search')); - $this->assertNoText(t('Test form was submitted'), 'Form message does not appear'); - $count = variable_get('search_embedded_form_submitted', 0); - $this->assertEqual($this->submit_count, $count, 'Form submission count is correct'); - $this->submit_count = $count; - } -} - -/** - * Tests that hook_search_page runs. - */ -class SearchPageOverride extends DrupalWebTestCase { - public $search_user; - - public static function getInfo() { - return array( - 'name' => 'Search page override', - 'description' => 'Verify that hook_search_page can override search page display.', - 'group' => 'Search', - ); - } - - function setUp() { - parent::setUp('search', 'search_extra_type'); - - // Login as a user that can create and search content. - $this->search_user = $this->drupalCreateUser(array('search content', 'administer search')); - $this->drupalLogin($this->search_user); - - // Enable the extra type module for searching. - variable_set('search_active_modules', array('node' => 'node', 'user' => 'user', 'search_extra_type' => 'search_extra_type')); - menu_rebuild(); - } - - function testSearchPageHook() { - $keys = 'bike shed ' . $this->randomName(); - $this->drupalGet("search/dummy_path/{$keys}"); - $this->assertText('Dummy search snippet', 'Dummy search snippet is shown'); - $this->assertText('Test page text is here', 'Page override is working'); - } -} - -/** - * Test node search with multiple languages. - */ -class SearchLanguageTestCase extends DrupalWebTestCase { - public static function getInfo() { - return array( - 'name' => 'Search language selection', - 'description' => 'Tests advanced search with different languages enabled.', - 'group' => 'Search', - ); - } - - /** - * Implementation setUp(). - */ - function setUp() { - parent::setUp('search', 'locale'); - - // Create and login user. - $test_user = $this->drupalCreateUser(array('access content', 'search content', 'use advanced search', 'administer nodes', 'administer languages', 'access administration pages')); - $this->drupalLogin($test_user); - } - - function testLanguages() { - // Check that there are initially no languages displayed. - $this->drupalGet('search/node'); - $this->assertNoText(t('Languages'), 'No languages to choose from.'); - - // Add predefined language. - $edit = array('langcode' => 'fr'); - $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language')); - $this->assertText('fr', 'Language added successfully.'); - - // Now we should have languages displayed. - $this->drupalGet('search/node'); - $this->assertText(t('Languages'), 'Languages displayed to choose from.'); - $this->assertText(t('English'), 'English is a possible choice.'); - $this->assertText(t('French'), 'French is a possible choice.'); - - // Ensure selecting no language does not make the query different. - $this->drupalPost('search/node', array(), t('Advanced search')); - $this->assertEqual($this->getUrl(), url('search/node/', array('absolute' => TRUE)), 'Correct page redirection, no language filtering.'); - - // Pick French and ensure it is selected. - $edit = array('language[fr]' => TRUE); - $this->drupalPost('search/node', $edit, t('Advanced search')); - $this->assertFieldByXPath('//input[@name="keys"]', 'language:fr', 'Language filter added to query.'); - - // Change the default language and disable English. - $path = 'admin/config/regional/language'; - $this->drupalGet($path); - $this->assertFieldChecked('edit-site-default-en', 'English is the default language.'); - $edit = array('site_default' => 'fr'); - $this->drupalPost(NULL, $edit, t('Save configuration')); - $this->assertNoFieldChecked('edit-site-default-en', 'Default language updated.'); - $edit = array('enabled[en]' => FALSE); - $this->drupalPost('admin/config/regional/language', $edit, t('Save configuration')); - $this->assertNoFieldChecked('edit-enabled-en', 'Language disabled.'); - - // Check that there are again no languages displayed. - $this->drupalGet('search/node'); - $this->assertNoText(t('Languages'), 'No languages to choose from.'); - } -} - -/** - * Tests node search with node access control. - */ -class SearchNodeAccessTest extends DrupalWebTestCase { - public $test_user; - - public static function getInfo() { - return array( - 'name' => 'Search and node access', - 'description' => 'Tests search functionality with node access control.', - 'group' => 'Search', - ); - } - - function setUp() { - parent::setUp('search', 'node_access_test'); - node_access_rebuild(); - - // Create a test user and log in. - $this->test_user = $this->drupalCreateUser(array('access content', 'search content', 'use advanced search')); - $this->drupalLogin($this->test_user); - } - - /** - * Tests that search returns results with punctuation in the search phrase. - */ - function testPhraseSearchPunctuation() { - $node = $this->drupalCreateNode(array('body' => array(LANGUAGE_NONE => array(array('value' => "The bunny's ears were fuzzy."))))); - - // Update the search index. - module_invoke_all('update_index'); - search_update_totals(); - - // Refresh variables after the treatment. - $this->refreshVariables(); - - // Submit a phrase wrapped in double quotes to include the punctuation. - $edit = array('keys' => '"bunny\'s"'); - $this->drupalPost('search/node', $edit, t('Search')); - $this->assertText($node->title); - } -} - -/** - * Tests node search with query tags. - */ -class SearchNodeTagTest extends DrupalWebTestCase { - public $test_user; - - public static function getInfo() { - return array( - 'name' => 'Node search query tags', - 'description' => 'Tests Node search tags functionality.', - 'group' => 'Search', - ); - } - - function setUp() { - parent::setUp('search', 'search_node_tags'); - node_access_rebuild(); - - // Create a test user and log in. - $this->test_user = $this->drupalCreateUser(array('search content')); - $this->drupalLogin($this->test_user); - } - - /** - * Tests that the correct tags are available and hooks invoked. - */ - function testNodeSearchQueryTags() { - $this->drupalCreateNode(array('body' => array(LANGUAGE_NONE => array(array('value' => 'testing testing testing.'))))); - - // Update the search index. - module_invoke_all('update_index'); - search_update_totals(); - - $edit = array('keys' => 'testing'); - $this->drupalPost('search/node', $edit, t('Search')); - - $this->assertTrue(variable_get('search_node_tags_test_query_tag', FALSE), 'hook_query_alter() was invoked and the query contained the "search_node" tag.'); - $this->assertTrue(variable_get('search_node_tags_test_query_tag_hook', FALSE), 'hook_query_search_node_alter() was invoked.'); - } -} - -/** - * Tests searching with locale values set. - */ -class SearchSetLocaleTest extends DrupalWebTestCase { - - public static function getInfo() { - return array( - 'name' => 'Search with numeric locale set', - 'description' => 'Check that search works with numeric locale settings', - 'group' => 'Search', - ); - } - - function setUp() { - parent::setUp('search'); - - // Create a simple node so something will be put in the index. - $info = array( - 'body' => array(LANGUAGE_NONE => array(array('value' => 'Tapir'))), - ); - $this->drupalCreateNode($info); - - // Run cron to index. - $this->cronRun(); - } - - /** - * Verify that search works with a numeric locale set. - */ - public function testSearchWithNumericLocale() { - // French decimal point is comma. - setlocale(LC_NUMERIC, 'fr_FR'); - - // An exception will be thrown if a float in the wrong format occurs in the - // query to the database, so an assertion is not necessary here. - db_select('search_index', 'i') - ->extend('searchquery') - ->searchexpression('tapir', 'node') - ->execute(); - } -} diff --git a/modules/search/tests/UnicodeTest.txt b/modules/search/tests/UnicodeTest.txt deleted file mode 100644 index af8a65c..0000000 --- a/modules/search/tests/UnicodeTest.txt +++ /dev/null @@ -1,333 +0,0 @@ - - !"#$%&'()*+,-./ -0123456789 -:;<=>?@ -ABCDEFGHIJKLMNOPQRSTUVWXYZ -[\]^_` -abcdefghijklmnopqrstuvwxyz -{|}~€Â‚ƒ„…†‡ˆ‰Š‹ŒÂŽ‘’“”•–—˜™š›œÂžŸ ¡¢£¤¥¦§¨© -ª -«¬­®¯°± -²³ -´ -µ -¶·¸ -¹º -» -¼½¾ -¿ -ÀÃÂÃÄÅÆÇÈÉÊËÌÃÃŽÃÃÑÒÓÔÕÖ -× -ØÙÚÛÜÃÞßàáâãäåæçèéêëìíîïðñòóôõö -÷ -øùúûüýþÿĀÄĂ㥹ĆćĈĉĊċČÄÄŽÄÄđĒēĔĕĖėĘęĚěĜÄĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀÅłŃńŅņŇňʼnŊŋŌÅÅŽÅÅőŒœŔŕŖŗŘřŚśŜÅŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƀÆƂƃƄƅƆƇƈƉƊƋƌÆÆŽÆÆƑƒƓƔƕƖƗƘƙƚƛƜÆƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿǀÇǂǃDŽDždžLJLjljNJNjnjÇÇŽÇÇǑǒǓǔǕǖǗǘǙǚǛǜÇǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴǵǶǷǸǹǺǻǼǽǾǿȀÈȂȃȄȅȆȇȈȉȊȋȌÈÈŽÈÈȑȒȓȔȕȖȗȘșȚțȜÈȞȟȠȡȢȣȤȥȦȧȨȩȪȫȬȭȮȯȰȱȲȳȴȵȶȷȸȹȺȻȼȽȾȿɀÉɂɃɄɅɆɇɈɉɊɋɌÉÉŽÉÉɑɒɓɔɕɖɗɘəɚɛɜÉɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀÊʂʃʄʅʆʇʈʉʊʋʌÊÊŽÊÊʑʒʓʔʕʖʗʘʙʚʛʜÊÊžÊŸÊ Ê¡Ê¢Ê£Ê¤Ê¥Ê¦Ê§Ê¨Ê©ÊªÊ«Ê¬Ê­Ê®Ê¯Ê°Ê±Ê²Ê³Ê´ÊµÊ¶Ê·Ê¸Ê¹ÊºÊ»Ê¼Ê½Ê¾Ê¿Ë€Ë -˂˃˄˅ -ˆˇˈˉˊˋˌËËŽËËË‘ -˒˓˔˕˖˗˘˙˚˛˜Ë˞˟ -ˠˡˢˣˤ -˥˦˧˨˩˪˫ -ˬ -Ë­ -Ë® -˯˰˱˲˳˴˵˶˷˸˹˺˻˼˽˾˿ -Ì€Ì̂̃̄̅̆̇̈̉̊̋̌ÌÌŽÌÌ̛̖̗̘̙̜̑̒̓̔̕̚Ì̴̵̶̷̸̡̢̧̨̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼̽̾̿̀Í͇͈͉͂̓̈́͆͊͋͌ͅÍÍŽÍÍ͓͔͕͖͙͚͑͒͗͛͘͜Íͣͤͥͦͧͨͩͪͫͬͭͮͯ͟͢͞͠͡ͰͱͲͳʹ -͵ -Ͷͷͺͻͼͽ -;΄΅ -Ά -· -ΈΉΊΌΎÎÎΑΒΓΔΕΖΗΘΙΚΛΜÎΞΟΠΡΣΤΥΦΧΨΩΪΫάέήίΰαβγδεζηθικλμνξοπÏςστυφχψωϊϋόÏÏŽÏÏϑϒϓϔϕϖϗϘϙϚϛϜÏϞϟϠϡϢϣϤϥϦϧϨϩϪϫϬϭϮϯϰϱϲϳϴϵ -϶ -ϷϸϹϺϻϼϽϾϿЀÐЂЃЄЅІЇЈЉЊЋЌÐÐŽÐÐБВГДЕЖЗИЙКЛМÐОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрÑтуфхцчшщъыьÑÑŽÑÑёђѓєѕіїјљњћќÑÑžÑŸÑ Ñ¡Ñ¢Ñ£Ñ¤Ñ¥Ñ¦Ñ§Ñ¨Ñ©ÑªÑ«Ñ¬Ñ­Ñ®Ñ¯Ñ°Ñ±Ñ²Ñ³Ñ´ÑµÑ¶Ñ·Ñ¸Ñ¹ÑºÑ»Ñ¼Ñ½Ñ¾Ñ¿Ò€Ò -Ò‚ -ÒƒÒ„Ò…Ò†Ò‡ÒˆÒ‰ÒŠÒ‹ÒŒÒÒŽÒÒÒ‘Ò’Ò“Ò”Ò•Ò–Ò—Ò˜Ò™ÒšÒ›ÒœÒÒžÒŸÒ Ò¡Ò¢Ò£Ò¤Ò¥Ò¦Ò§Ò¨Ò©ÒªÒ«Ò¬Ò­Ò®Ò¯Ò°Ò±Ò²Ò³Ò´ÒµÒ¶Ò·Ò¸Ò¹ÒºÒ»Ò¼Ò½Ò¾Ò¿Ó€ÓÓ‚ÓƒÓ„Ó…Ó†Ó‡ÓˆÓ‰ÓŠÓ‹ÓŒÓÓŽÓÓÓ‘Ó’Ó“Ó”Ó•Ó–Ó—Ó˜Ó™ÓšÓ›ÓœÓÓžÓŸÓ Ó¡Ó¢Ó£Ó¤Ó¥Ó¦Ó§Ó¨Ó©ÓªÓ«Ó¬Ó­Ó®Ó¯Ó°Ó±Ó²Ó³Ó´ÓµÓ¶Ó·Ó¸Ó¹ÓºÓ»Ó¼Ó½Ó¾Ó¿Ô€ÔÔ‚ÔƒÔ„Ô…Ô†Ô‡ÔˆÔ‰ÔŠÔ‹ÔŒÔÔŽÔÔÔ‘Ô’Ô“Ô”Ô•Ô–Ô—Ô˜Ô™ÔšÔ›ÔœÔÔžÔŸÔ Ô¡Ô¢Ô£Ô¤Ô¥Ô±Ô²Ô³Ô´ÔµÔ¶Ô·Ô¸Ô¹ÔºÔ»Ô¼Ô½Ô¾Ô¿Õ€ÕÕ‚ÕƒÕ„Õ…Õ†Õ‡ÕˆÕ‰ÕŠÕ‹ÕŒÕÕŽÕÕÕ‘Õ’Õ“Õ”Õ•Õ–Õ™ -ÕšÕ›ÕœÕÕžÕŸ -Õ¡Õ¢Õ£Õ¤Õ¥Õ¦Õ§Õ¨Õ©ÕªÕ«Õ¬Õ­Õ®Õ¯Õ°Õ±Õ²Õ³Õ´ÕµÕ¶Õ·Õ¸Õ¹ÕºÕ»Õ¼Õ½Õ¾Õ¿Ö€ÖÖ‚ÖƒÖ„Ö…Ö†Ö‡ -Ö‰ÖŠ -Ö‘Ö’Ö“Ö”Ö•Ö–Ö—Ö˜Ö™ÖšÖ›ÖœÖÖžÖŸÖ Ö¡Ö¢Ö£Ö¤Ö¥Ö¦Ö§Ö¨Ö©ÖªÖ«Ö¬Ö­Ö®Ö¯Ö°Ö±Ö²Ö³Ö´ÖµÖ¶Ö·Ö¸Ö¹ÖºÖ»Ö¼Ö½ -Ö¾ -Ö¿ -×€ -×ׂ -׃ -ׅׄ -׆ -ׇ×בגדהוזחטיךכל×מןנסעףפץצקרשתװױײ -׳״؀Ø؂؃؆؇؈؉؊؋،ØØŽØ -Øؘؙؚؑؒؓؔؕؖؗ -؛؞؟ -ءآأؤإئابةتثجحخدذرزسشصضطظعغػؼؽؾؿـÙقكلمنهوىيًٌÙÙŽÙÙّْٕٖٜٓٔٗ٘ٙٚٛÙٞ٠١٢٣٤٥٦٧٨٩ -٪٫٬٭ -ٮٯٰٱٲٳٴٵٶٷٸٹٺٻټٽپٿڀÚÚ‚ÚƒÚ„Ú…Ú†Ú‡ÚˆÚ‰ÚŠÚ‹ÚŒÚÚŽÚÚÚ‘Ú’Ú“Ú”Ú•Ú–Ú—Ú˜Ú™ÚšÚ›ÚœÚÚžÚŸÚ Ú¡Ú¢Ú£Ú¤Ú¥Ú¦Ú§Ú¨Ú©ÚªÚ«Ú¬Ú­Ú®Ú¯Ú°Ú±Ú²Ú³Ú´ÚµÚ¶Ú·Ú¸Ú¹ÚºÚ»Ú¼Ú½Ú¾Ú¿Û€ÛÛ‚ÛƒÛ„Û…Û†Û‡ÛˆÛ‰ÛŠÛ‹ÛŒÛÛŽÛÛÛ‘Û’Û“ -Û” -Û•Û–Û—Û˜Û™ÛšÛ›Ûœ -Û -ÛžÛŸÛ Û¡Û¢Û£Û¤Û¥Û¦Û§Û¨ -Û© -ÛªÛ«Û¬Û­Û®Û¯Û°Û±Û²Û³Û´ÛµÛ¶Û·Û¸Û¹ÛºÛ»Û¼ -Û½Û¾ -Û¿ -Ü€Ü܂܃܄܅܆܇܈܉܊܋܌ÜÜ -ÜܑܒܓܔܕܖܗܘܙܚܛܜÜܞܟܠܡܢܣܤܥܦܧܨܩܪܫܬܭܮܯܱܴܷܸܹܻܼܾܰܲܳܵܶܺܽܿ݀Ý݂݄݆݈݃݅݇݉݊ÝÝŽÝÝݑݒݓݔݕݖݗݘݙݚݛݜÝݞݟݠݡݢݣݤݥݦݧݨݩݪݫݬݭݮݯݰݱݲݳݴݵݶݷݸݹݺݻݼݽݾݿހÞÞ‚ÞƒÞ„Þ…Þ†Þ‡ÞˆÞ‰ÞŠÞ‹ÞŒÞÞŽÞÞÞ‘Þ’Þ“Þ”Þ•Þ–Þ—Þ˜Þ™ÞšÞ›ÞœÞޞޟޠޡޢޣޤޥަާިީުޫެޭޮޯްޱ߀ß߂߃߄߅߆߇߈߉ߊߋߌßߎßßߑߒߓߔߕߖߗߘߙߚߛߜßߞߟߠߡߢߣߤߥߦߧߨߩߪ߲߫߬߭߮߯߰߱߳ߴߵ -߶߷߸߹ -ߺࠀà à ‚ࠃࠄࠅࠆࠇࠈࠉࠊࠋࠌà à Žà à à ‘ࠒࠓࠔࠕࠖࠗ࠘࠙ࠚࠛࠜà à žà Ÿà  à ¡à ¢à £à ¤à ¥à ¦à §à ¨à ©à ªà «à ¬à ­ -࠰࠱࠲࠳࠴࠵࠶࠷࠸࠹࠺࠻࠼࠽࠾ -ऀà¤à¤‚ःऄअआइईउऊऋऌà¤à¤Žà¤à¤à¤‘ऒओऔकखगघङचछजà¤à¤žà¤Ÿà¤ à¤¡à¤¢à¤£à¤¤à¤¥à¤¦à¤§à¤¨à¤©à¤ªà¤«à¤¬à¤­à¤®à¤¯à¤°à¤±à¤²à¤³à¤´à¤µà¤¶à¤·à¤¸à¤¹à¤¼à¤½à¤¾à¤¿à¥€à¥à¥‚ृॄॅॆेैॉॊोौà¥à¥Žà¥à¥‘॒॓॔ॕक़ख़ग़ज़ड़à¥à¥žà¥Ÿà¥ à¥¡à¥¢à¥£ -।॥ -०१२३४५६७८९ -॰ -ॱॲॹॺॻॼॽॾॿà¦à¦‚ঃঅআইঈউঊঋঌà¦à¦à¦“ঔকখগঘঙচছজà¦à¦žà¦Ÿà¦ à¦¡à¦¢à¦£à¦¤à¦¥à¦¦à¦§à¦¨à¦ªà¦«à¦¬à¦­à¦®à¦¯à¦°à¦²à¦¶à¦·à¦¸à¦¹à¦¼à¦½à¦¾à¦¿à§€à§à§‚ৃৄেৈোৌà§à§Žà§—ড়à§à§Ÿà§ à§¡à§¢à§£à§¦à§§à§¨à§©à§ªà§«à§¬à§­à§®à§¯à§°à§± -৲৳ -৴৵৶৷৸৹ -৺৻ -à¨à¨‚ਃਅਆਇਈਉਊà¨à¨à¨“ਔਕਖਗਘਙਚਛਜà¨à¨žà¨Ÿà¨ à¨¡à¨¢à¨£à¨¤à¨¥à¨¦à¨§à¨¨à¨ªà¨«à¨¬à¨­à¨®à¨¯à¨°à¨²à¨³à¨µà¨¶à¨¸à¨¹à¨¼à¨¾à¨¿à©€à©à©‚ੇੈੋੌà©à©‘ਖ਼ਗ਼ਜ਼ੜਫ਼੦੧੨੩੪੫੬੭੮੯ੰੱੲੳੴੵàªàª‚ઃઅઆઇઈઉઊઋઌàªàªàªàª‘ઓઔકખગઘઙચછજàªàªžàªŸàª àª¡àª¢àª£àª¤àª¥àª¦àª§àª¨àªªàª«àª¬àª­àª®àª¯àª°àª²àª³àªµàª¶àª·àª¸àª¹àª¼àª½àª¾àª¿à«€à«à«‚ૃૄૅેૈૉોૌà«à«à« à«¡à«¢à«£à«¦à«§à«¨à«©à«ªà««à«¬à«­à«®à«¯ -૱ -à¬à¬‚ଃଅଆଇଈଉଊଋଌà¬à¬à¬“ଔକଖଗଘଙଚଛଜà¬à¬žà¬Ÿà¬ à¬¡à¬¢à¬£à¬¤à¬¥à¬¦à¬§à¬¨à¬ªà¬«à¬¬à¬­à¬®à¬¯à¬°à¬²à¬³à¬µà¬¶à¬·à¬¸à¬¹à¬¼à¬½à¬¾à¬¿à­€à­à­‚ୃୄେୈୋୌà­à­–à­—à­œà­à­Ÿà­ à­¡à­¢à­£à­¦à­§à­¨à­©à­ªà­«à­¬à­­à­®à­¯ -à­° -ୱஂஃஅஆஇஈஉஊஎà®à®à®’ஓஔகஙசஜஞடணதநனபமயரறலளழவஶஷஸஹாிீà¯à¯‚ெேைொோௌà¯à¯à¯—௦௧௨௩௪௫௬௭௮௯௰௱௲ -௳௴௵௶௷௸௹௺ -à°à°‚ఃఅఆఇఈఉఊఋఌఎà°à°à°’ఓఔకఖగఘఙచఛజà°à°žà°Ÿà° à°¡à°¢à°£à°¤à°¥à°¦à°§à°¨à°ªà°«à°¬à°­à°®à°¯à°°à°±à°²à°³à°µà°¶à°·à°¸à°¹à°½à°¾à°¿à±€à±à±‚ృౄెేైొోౌà±à±•à±–ౘౙౠౡౢౣ౦౧౨౩౪౫౬౭౮౯౸౹౺౻౼౽౾ -౿ -ಂಃಅಆಇಈಉಊಋಌಎà²à²à²’ಓಔಕಖಗಘಙಚಛಜà²à²žà²Ÿà² à²¡à²¢à²£à²¤à²¥à²¦à²§à²¨à²ªà²«à²¬à²­à²®à²¯à²°à²±à²²à²³à²µà²¶à²·à²¸à²¹à²¼à²½à²¾à²¿à³€à³à³‚ೃೄೆೇೈೊೋೌà³à³•à³–ೞೠೡೢೣ೦೧೨೩೪೫೬೭೮೯ -ೱೲ -ംഃഅആഇഈഉഊഋഌഎà´à´à´’ഓഔകഖഗഘങചഛജà´à´žà´Ÿà´ à´¡à´¢à´£à´¤à´¥à´¦à´§à´¨à´ªà´«à´¬à´­à´®à´¯à´°à´±à´²à´³à´´à´µà´¶à´·à´¸à´¹à´½à´¾à´¿àµ€àµàµ‚ൃൄെേൈൊോൌàµàµ—ൠൡൢൣ൦൧൨൩൪൫൬൭൮൯൰൱൲൳൴൵ -൹ -ൺൻർൽൾൿංඃඅආඇඈඉඊඋඌà¶à¶Žà¶à¶à¶‘ඒඓඔඕඖකඛගà¶à¶žà¶Ÿà¶ à¶¡à¶¢à¶£à¶¤à¶¥à¶¦à¶§à¶¨à¶©à¶ªà¶«à¶¬à¶­à¶®à¶¯à¶°à¶±à¶³à¶´à¶µà¶¶à¶·à¶¸à¶¹à¶ºà¶»à¶½à·€à·à·‚සහළෆ්à·à·à·‘ිීුූෘෙේෛොà·à·žà·Ÿà·²à·³ -à·´ -à¸à¸‚ฃคฅฆงจฉชซฌà¸à¸Žà¸à¸à¸‘ฒณดตถทธนบปผà¸à¸žà¸Ÿà¸ à¸¡à¸¢à¸£à¸¤à¸¥à¸¦à¸§à¸¨à¸©à¸ªà¸«à¸¬à¸­à¸®à¸¯à¸°à¸±à¸²à¸³à¸´à¸µà¸¶à¸·à¸¸à¸¹à¸º -฿ -เà¹à¹‚ใไๅๆ็่้๊๋์à¹à¹Ž -๠-à¹à¹‘๒๓๔๕๖๗๘๙ -๚๛ -àºàº‚ຄງຈຊàºàº”ຕຖທນບປຜàºàºžàºŸàº¡àº¢àº£àº¥àº§àºªàº«àº­àº®àº¯àº°àº±àº²àº³àº´àºµàº¶àº·àº¸àº¹àº»àº¼àº½à»€à»à»‚ໃໄໆ່້໊໋໌à»à»à»‘໒໓໔໕໖໗໘໙ໜà»à¼€ -à¼à¼‚༃༄༅༆༇༈༉༊་༌à¼à¼Žà¼à¼à¼‘༒༓༔༕༖༗ -༘༙ -༚༛༜à¼à¼žà¼Ÿ -༠༡༢༣༤༥༦༧༨༩༪༫༬༭༮༯༰༱༲༳ -༴ -༵ -༶ -༷ -༸ -༹ -༺༻༼༽ -༾༿ཀà½à½‚གྷངཅཆཇཉཊཋཌà½à½Žà½à½à½‘དྷནཔཕབབྷམཙཚཛཛྷà½à½žà½Ÿà½ à½¡à½¢à½£à½¤à½¥à½¦à½§à½¨à½©à½ªà½«à½¬à½±à½²à½³à½´à½µà½¶à½·à½¸à½¹à½ºà½»à½¼à½½à½¾à½¿à¾€à¾à¾‚྄ྃ -྅ -྆྇ྈྉྊྋà¾à¾‘ྒྒྷྔྕྖྗྙྚྛྜà¾à¾žà¾Ÿà¾ à¾¡à¾¢à¾£à¾¤à¾¥à¾¦à¾§à¾¨à¾©à¾ªà¾«à¾¬à¾­à¾®à¾¯à¾°à¾±à¾²à¾³à¾´à¾µà¾¶à¾·à¾¸à¾¹à¾ºà¾»à¾¼ -྾྿࿀à¿à¿‚࿃࿄࿅ -࿆ -࿇࿈࿉࿊࿋࿌࿎à¿à¿à¿‘࿒࿓࿔࿕࿖࿗࿘ -ကá€á€‚ဃငစဆဇဈဉညဋဌá€á€Žá€á€á€‘ဒဓနပဖဗဘမယရလá€á€žá€Ÿá€ á€¡á€¢á€£á€¤á€¥á€¦á€§á€¨á€©á€ªá€«á€¬á€­á€®á€¯á€°á€±á€²á€³á€´á€µá€¶á€·á€¸á€¹á€ºá€»á€¼á€½á€¾á€¿á€áá‚áƒá„á…á†á‡áˆá‰ -áŠá‹áŒááŽá -áá‘á’á“á”á•á–á—á˜á™ášá›áœáážáŸá á¡á¢á£á¤á¥á¦á§á¨á©áªá«á¬á­á®á¯á°á±á²á³á´áµá¶á·á¸á¹áºá»á¼á½á¾á¿á‚€á‚ႂႃႄႅႆႇႈႉႊႋႌá‚á‚Žá‚á‚႑႒႓႔႕႖႗႘႙ႚႛႜႠ-á‚žá‚Ÿ -ႠႡႢႣႤႥႦႧႨႩႪႫႬႭႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀáƒáƒ‚ჃჄჅáƒáƒ‘გდევზთიკლმნáƒáƒžáƒŸáƒ áƒ¡áƒ¢áƒ£áƒ¤áƒ¥áƒ¦áƒ§áƒ¨áƒ©áƒªáƒ«áƒ¬áƒ­áƒ®áƒ¯áƒ°áƒ±áƒ²áƒ³áƒ´áƒµáƒ¶áƒ·áƒ¸áƒ¹áƒº -჻ -ჼᄀá„ᄂᄃᄄᄅᄆᄇᄈᄉᄊᄋᄌá„á„Žá„á„ᄑᄒᄓᄔᄕᄖᄗᄘᄙᄚᄛᄜá„ᄞᄟᄠᄡᄢᄣᄤᄥᄦᄧᄨᄩᄪᄫᄬᄭᄮᄯᄰᄱᄲᄳᄴᄵᄶᄷᄸᄹᄺᄻᄼᄽᄾᄿᅀá…ᅂᅃᅄᅅᅆᅇᅈᅉᅊᅋᅌá…á…Žá…á…ᅑᅒᅓᅔᅕᅖᅗᅘᅙᅚᅛᅜá…ᅞᅟᅠᅡᅢᅣᅤᅥᅦᅧᅨᅩᅪᅫᅬᅭᅮᅯᅰᅱᅲᅳᅴᅵᅶᅷᅸᅹᅺᅻᅼᅽᅾᅿᆀá†á†‚ᆃᆄᆅᆆᆇᆈᆉᆊᆋᆌá†á†Žá†á†á†‘ᆒᆓᆔᆕᆖᆗᆘᆙᆚᆛᆜá†á†žá†Ÿá† á†¡á†¢á†£á†¤á†¥á†¦á†§á†¨á†©á†ªá†«á†¬á†­á†®á†¯á†°á†±á†²á†³á†´á†µá†¶á†·á†¸á†¹á†ºá†»á†¼á†½á†¾á†¿á‡€á‡á‡‚ᇃᇄᇅᇆᇇᇈᇉᇊᇋᇌá‡á‡Žá‡á‡á‡‘ᇒᇓᇔᇕᇖᇗᇘᇙᇚᇛᇜá‡á‡žá‡Ÿá‡ á‡¡á‡¢á‡£á‡¤á‡¥á‡¦á‡§á‡¨á‡©á‡ªá‡«á‡¬á‡­á‡®á‡¯á‡°á‡±á‡²á‡³á‡´á‡µá‡¶á‡·á‡¸á‡¹á‡ºá‡»á‡¼á‡½á‡¾á‡¿áˆ€áˆáˆ‚ሃሄህሆሇለሉሊላሌáˆáˆŽáˆáˆáˆ‘ሒሓሔሕሖሗመሙሚማሜáˆáˆžáˆŸáˆ áˆ¡áˆ¢áˆ£áˆ¤áˆ¥áˆ¦áˆ§áˆ¨áˆ©áˆªáˆ«áˆ¬áˆ­áˆ®áˆ¯áˆ°áˆ±áˆ²áˆ³áˆ´áˆµáˆ¶áˆ·áˆ¸áˆ¹áˆºáˆ»áˆ¼áˆ½áˆ¾áˆ¿á‰€á‰á‰‚ቃቄቅቆቇቈቊቋቌá‰á‰á‰‘ቒቓቔቕቖቘቚቛቜá‰á‰ á‰¡á‰¢á‰£á‰¤á‰¥á‰¦á‰§á‰¨á‰©á‰ªá‰«á‰¬á‰­á‰®á‰¯á‰°á‰±á‰²á‰³á‰´á‰µá‰¶á‰·á‰¸á‰¹á‰ºá‰»á‰¼á‰½á‰¾á‰¿áŠ€áŠáŠ‚ኃኄኅኆኇኈኊኋኌáŠáŠáŠ‘ኒናኔንኖኗኘኙኚኛኜáŠáŠžáŠŸáŠ áŠ¡áŠ¢áŠ£áŠ¤áŠ¥áŠ¦áŠ§áŠ¨áŠ©áŠªáŠ«áŠ¬áŠ­áŠ®áŠ¯áŠ°áŠ²áŠ³áŠ´áŠµáŠ¸áŠ¹áŠºáŠ»áŠ¼áŠ½áŠ¾á‹€á‹‚ዃዄዅወዉዊዋዌá‹á‹Žá‹á‹á‹‘ዒዓዔዕዖዘዙዚዛዜá‹á‹žá‹Ÿá‹ á‹¡á‹¢á‹£á‹¤á‹¥á‹¦á‹§á‹¨á‹©á‹ªá‹«á‹¬á‹­á‹®á‹¯á‹°á‹±á‹²á‹³á‹´á‹µá‹¶á‹·á‹¸á‹¹á‹ºá‹»á‹¼á‹½á‹¾á‹¿áŒ€áŒáŒ‚ጃጄጅጆጇገጉጊጋጌáŒáŒŽáŒáŒáŒ’ጓጔጕጘጙጚጛጜáŒáŒžáŒŸáŒ áŒ¡áŒ¢áŒ£áŒ¤áŒ¥áŒ¦áŒ§áŒ¨áŒ©áŒªáŒ«áŒ¬áŒ­áŒ®áŒ¯áŒ°áŒ±áŒ²áŒ³áŒ´áŒµáŒ¶áŒ·áŒ¸áŒ¹áŒºáŒ»áŒ¼áŒ½áŒ¾áŒ¿á€áá‚áƒá„á…á†á‡áˆá‰áŠá‹áŒááŽááá‘á’á“á”á•á–á—á˜á™ášáŸ -á á¡á¢á£á¤á¥á¦á§á¨ -á©áªá«á¬á­á®á¯á°á±á²á³á´áµá¶á·á¸á¹áºá»á¼áŽ€áŽáŽ‚ᎃᎄᎅᎆᎇᎈᎉᎊᎋᎌáŽáŽŽáŽ -áŽáŽ‘᎒᎓᎔᎕᎖᎗᎘᎙ -ᎠᎡᎢᎣᎤᎥᎦᎧᎨᎩᎪᎫᎬᎭᎮᎯᎰᎱᎲᎳᎴᎵᎶᎷᎸᎹᎺᎻᎼᎽᎾᎿá€áá‚áƒá„á…á†á‡áˆá‰áŠá‹áŒááŽááá‘á’á“á”á•á–á—á˜á™ášá›áœáážáŸá á¡á¢á£á¤á¥á¦á§á¨á©áªá«á¬á­á®á¯á°á±á²á³á´ -ဠ-áá‚áƒá„á…á†á‡áˆá‰áŠá‹áŒááŽááá‘á’á“á”á•á–á—á˜á™ášá›áœáážáŸá á¡á¢á£á¤á¥á¦á§á¨á©áªá«á¬á­á®á¯á°á±á²á³á´áµá¶á·á¸á¹áºá»á¼á½á¾á¿á‘€á‘ᑂᑃᑄᑅᑆᑇᑈᑉᑊᑋᑌá‘á‘Žá‘á‘ᑑᑒᑓᑔᑕᑖᑗᑘᑙᑚᑛᑜá‘ᑞᑟᑠᑡᑢᑣᑤᑥᑦᑧᑨᑩᑪᑫᑬᑭᑮᑯᑰᑱᑲᑳᑴᑵᑶᑷᑸᑹᑺᑻᑼᑽᑾᑿᒀá’ᒂᒃᒄᒅᒆᒇᒈᒉᒊᒋᒌá’á’Žá’á’ᒑᒒᒓᒔᒕᒖᒗᒘᒙᒚᒛᒜá’ᒞᒟᒠᒡᒢᒣᒤᒥᒦᒧᒨᒩᒪᒫᒬᒭᒮᒯᒰᒱᒲᒳᒴᒵᒶᒷᒸᒹᒺᒻᒼᒽᒾᒿᓀá“ᓂᓃᓄᓅᓆᓇᓈᓉᓊᓋᓌá“á“Žá“á“ᓑᓒᓓᓔᓕᓖᓗᓘᓙᓚᓛᓜá“ᓞᓟᓠᓡᓢᓣᓤᓥᓦᓧᓨᓩᓪᓫᓬᓭᓮᓯᓰᓱᓲᓳᓴᓵᓶᓷᓸᓹᓺᓻᓼᓽᓾᓿᔀá”ᔂᔃᔄᔅᔆᔇᔈᔉᔊᔋᔌá”ᔎá”á”ᔑᔒᔓᔔᔕᔖᔗᔘᔙᔚᔛᔜá”ᔞᔟᔠᔡᔢᔣᔤᔥᔦᔧᔨᔩᔪᔫᔬᔭᔮᔯᔰᔱᔲᔳᔴᔵᔶᔷᔸᔹᔺᔻᔼᔽᔾᔿᕀá•á•‚ᕃᕄᕅᕆᕇᕈᕉᕊᕋᕌá•á•Žá•á•á•‘ᕒᕓᕔᕕᕖᕗᕘᕙᕚᕛᕜá•á•žá•Ÿá• á•¡á•¢á•£á•¤á•¥á•¦á•§á•¨á•©á•ªá•«á•¬á•­á•®á•¯á•°á•±á•²á•³á•´á•µá•¶á•·á•¸á•¹á•ºá•»á•¼á•½á•¾á•¿á–€á–ᖂᖃᖄᖅᖆᖇᖈᖉᖊᖋᖌá–á–Žá–á–ᖑᖒᖓᖔᖕᖖᖗᖘᖙᖚᖛᖜá–ᖞᖟᖠᖡᖢᖣᖤᖥᖦᖧᖨᖩᖪᖫᖬᖭᖮᖯᖰᖱᖲᖳᖴᖵᖶᖷᖸᖹᖺᖻᖼᖽᖾᖿᗀá—ᗂᗃᗄᗅᗆᗇᗈᗉᗊᗋᗌá—á—Žá—á—ᗑᗒᗓᗔᗕᗖᗗᗘᗙᗚᗛᗜá—ᗞᗟᗠᗡᗢᗣᗤᗥᗦᗧᗨᗩᗪᗫᗬᗭᗮᗯᗰᗱᗲᗳᗴᗵᗶᗷᗸᗹᗺᗻᗼᗽᗾᗿᘀá˜á˜‚ᘃᘄᘅᘆᘇᘈᘉᘊᘋᘌá˜á˜Žá˜á˜á˜‘ᘒᘓᘔᘕᘖᘗᘘᘙᘚᘛᘜá˜á˜žá˜Ÿá˜ á˜¡á˜¢á˜£á˜¤á˜¥á˜¦á˜§á˜¨á˜©á˜ªá˜«á˜¬á˜­á˜®á˜¯á˜°á˜±á˜²á˜³á˜´á˜µá˜¶á˜·á˜¸á˜¹á˜ºá˜»á˜¼á˜½á˜¾á˜¿á™€á™á™‚ᙃᙄᙅᙆᙇᙈᙉᙊᙋᙌá™á™Žá™á™á™‘ᙒᙓᙔᙕᙖᙗᙘᙙᙚᙛᙜá™á™žá™Ÿá™ á™¡á™¢á™£á™¤á™¥á™¦á™§á™¨á™©á™ªá™«á™¬ -á™­á™® -ᙯᙰᙱᙲᙳᙴᙵᙶᙷᙸᙹᙺᙻᙼᙽᙾᙿ -  -ášáš‚ᚃᚄᚅᚆᚇᚈᚉᚊᚋᚌášášŽášášáš‘ᚒᚓᚔᚕᚖᚗᚘᚙᚚ -᚛᚜ -ᚠᚡᚢᚣᚤᚥᚦᚧᚨᚩᚪᚫᚬᚭᚮᚯᚰᚱᚲᚳᚴᚵᚶᚷᚸᚹᚺᚻᚼᚽᚾᚿᛀá›á›‚ᛃᛄᛅᛆᛇᛈᛉᛊᛋᛌá›á›Žá›á›á›‘ᛒᛓᛔᛕᛖᛗᛘᛙᛚᛛᛜá›á›žá›Ÿá› á›¡á›¢á›£á›¤á›¥á›¦á›§á›¨á›©á›ª -᛫᛬᛭ -ᛮᛯᛰᜀáœáœ‚ᜃᜄᜅᜆᜇᜈᜉᜊᜋᜌᜎáœáœáœ‘ᜒᜓ᜔ᜠᜡᜢᜣᜤᜥᜦᜧᜨᜩᜪᜫᜬᜭᜮᜯᜰᜱᜲᜳ᜴ -᜵᜶ -á€áá‚áƒá„á…á†á‡áˆá‰áŠá‹áŒááŽááá‘á’á“á á¡á¢á£á¤á¥á¦á§á¨á©áªá«á¬á®á¯á°á²á³áž€ážáž‚ឃងចឆជឈញដឋឌážážŽážážáž‘ធនបផពភមយរលវážážžážŸáž áž¡áž¢áž£áž¤áž¥áž¦áž§áž¨áž©ážªáž«áž¬áž­áž®áž¯áž°áž±áž²áž³ -áž´ážµ -ាិីឹឺុូួើឿៀáŸáŸ‚ៃោៅំះៈ៉៊់៌áŸáŸŽáŸáŸáŸ‘្៓ -។៕៖ -ៗ -៘៙៚៛ -ៜáŸáŸ áŸ¡áŸ¢áŸ£áŸ¤áŸ¥áŸ¦áŸ§áŸ¨áŸ©áŸ°áŸ±áŸ²áŸ³áŸ´áŸµáŸ¶áŸ·áŸ¸áŸ¹ -á €á á ‚᠃᠄᠅᠆᠇᠈᠉᠊ -á ‹á Œá  -á Ž -á á ‘᠒᠓᠔᠕᠖᠗᠘᠙ᠠᠡᠢᠣᠤᠥᠦᠧᠨᠩᠪᠫᠬᠭᠮᠯᠰᠱᠲᠳᠴᠵᠶᠷᠸᠹᠺᠻᠼᠽᠾᠿᡀá¡á¡‚ᡃᡄᡅᡆᡇᡈᡉᡊᡋᡌá¡á¡Žá¡á¡á¡‘ᡒᡓᡔᡕᡖᡗᡘᡙᡚᡛᡜá¡á¡žá¡Ÿá¡ á¡¡á¡¢á¡£á¡¤á¡¥á¡¦á¡§á¡¨á¡©á¡ªá¡«á¡¬á¡­á¡®á¡¯á¡°á¡±á¡²á¡³á¡´á¡µá¡¶á¡·á¢€á¢á¢‚ᢃᢄᢅᢆᢇᢈᢉᢊᢋᢌá¢á¢Žá¢á¢á¢‘ᢒᢓᢔᢕᢖᢗᢘᢙᢚᢛᢜá¢á¢žá¢Ÿá¢ á¢¡á¢¢á¢£á¢¤á¢¥á¢¦á¢§á¢¨á¢©á¢ªá¢°á¢±á¢²á¢³á¢´á¢µá¢¶á¢·á¢¸á¢¹á¢ºá¢»á¢¼á¢½á¢¾á¢¿á£€á£á£‚ᣃᣄᣅᣆᣇᣈᣉᣊᣋᣌá£á£Žá£á£á£‘ᣒᣓᣔᣕᣖᣗᣘᣙᣚᣛᣜá£á£žá£Ÿá£ á£¡á£¢á££á£¤á£¥á£¦á£§á£¨á£©á£ªá£«á£¬á£­á£®á£¯á£°á£±á£²á£³á£´á£µá¤€á¤á¤‚ᤃᤄᤅᤆᤇᤈᤉᤊᤋᤌá¤á¤Žá¤á¤á¤‘ᤒᤓᤔᤕᤖᤗᤘᤙᤚᤛᤜᤠᤡᤢᤣᤤᤥᤦᤧᤨᤩᤪᤫᤰᤱᤲᤳᤴᤵᤶᤷᤸ᤻᤹᤺ -᥀᥄᥅ -᥆᥇᥈᥉᥊᥋᥌á¥á¥Žá¥á¥á¥‘ᥒᥓᥔᥕᥖᥗᥘᥙᥚᥛᥜá¥á¥žá¥Ÿá¥ á¥¡á¥¢á¥£á¥¤á¥¥á¥¦á¥§á¥¨á¥©á¥ªá¥«á¥¬á¥­á¥°á¥±á¥²á¥³á¥´á¦€á¦á¦‚ᦃᦄᦅᦆᦇᦈᦉᦊᦋᦌá¦á¦Žá¦á¦á¦‘ᦒᦓᦔᦕᦖᦗᦘᦙᦚᦛᦜá¦á¦žá¦Ÿá¦ á¦¡á¦¢á¦£á¦¤á¦¥á¦¦á¦§á¦¨á¦©á¦ªá¦«á¦°á¦±á¦²á¦³á¦´á¦µá¦¶á¦·á¦¸á¦¹á¦ºá¦»á¦¼á¦½á¦¾á¦¿á§€á§á§‚ᧃᧄᧅᧆᧇᧈᧉá§á§‘᧒᧓᧔᧕᧖᧗᧘᧙᧚ -᧞᧟᧠᧡᧢᧣᧤᧥᧦᧧᧨᧩᧪᧫᧬᧭᧮᧯᧰᧱᧲᧳᧴᧵᧶᧷᧸᧹᧺᧻᧼᧽᧾᧿ -ᨀá¨á¨‚ᨃᨄᨅᨆᨇᨈᨉᨊᨋᨌá¨á¨Žá¨á¨á¨‘ᨒᨓᨔᨕᨖᨘᨗᨙᨚᨛ -᨞᨟ -ᨠᨡᨢᨣᨤᨥᨦᨧᨨᨩᨪᨫᨬᨭᨮᨯᨰᨱᨲᨳᨴᨵᨶᨷᨸᨹᨺᨻᨼᨽᨾᨿᩀá©á©‚ᩃᩄᩅᩆᩇᩈᩉᩊᩋᩌá©á©Žá©á©á©‘ᩒᩓᩔᩕᩖᩗᩘᩙᩚᩛᩜá©á©žá© á©¡á©¢á©£á©¤á©¥á©¦á©§á©¨á©©á©ªá©«á©¬á©­á©®á©¯á©°á©±á©²á©³á©´á©µá©¶á©·á©¸á©¹á©ºá©»á©¼á©¿áª€áªáª‚᪃᪄᪅᪆᪇᪈᪉áªáª‘᪒᪓᪔᪕᪖᪗᪘᪙ -᪠᪡᪢᪣᪤᪥᪦ -ᪧ -᪨᪩᪪᪫᪬᪭ -ᬀá¬á¬‚ᬃᬄᬅᬆᬇᬈᬉᬊᬋᬌá¬á¬Žá¬á¬á¬‘ᬒᬓᬔᬕᬖᬗᬘᬙᬚᬛᬜá¬á¬žá¬Ÿá¬ á¬¡á¬¢á¬£á¬¤á¬¥á¬¦á¬§á¬¨á¬©á¬ªá¬«á¬¬á¬­á¬®á¬¯á¬°á¬±á¬²á¬³á¬´á¬µá¬¶á¬·á¬¸á¬¹á¬ºá¬»á¬¼á¬½á¬¾á¬¿á­€á­á­‚ᭃ᭄ᭅᭆᭇᭈᭉᭊᭋá­á­‘᭒᭓᭔᭕᭖᭗᭘᭙ -᭚᭛᭜á­á­žá­Ÿá­ á­¡á­¢á­£á­¤á­¥á­¦á­§á­¨á­©á­ª -᭬᭫᭭᭮᭯᭰᭱᭲᭳ -᭴᭵᭶᭷᭸᭹᭺᭻᭼ -ᮀá®á®‚ᮃᮄᮅᮆᮇᮈᮉᮊᮋᮌá®á®Žá®á®á®‘ᮒᮓᮔᮕᮖᮗᮘᮙᮚᮛᮜá®á®žá®Ÿá® á®¡á®¢á®£á®¤á®¥á®¦á®§á®¨á®©á®ªá®®á®¯á®°á®±á®²á®³á®´á®µá®¶á®·á®¸á®¹á°€á°á°‚ᰃᰄᰅᰆᰇᰈᰉᰊᰋᰌá°á°Žá°á°á°‘ᰒᰓᰔᰕᰖᰗᰘᰙᰚᰛᰜá°á°žá°Ÿá° á°¡á°¢á°£á°¤á°¥á°¦á°§á°¨á°©á°ªá°«á°¬á°­á°®á°¯á°°á°±á°²á°³á°´á°µá°¶á°· -᰻᰼᰽᰾᰿ -á±€á±á±‚᱃᱄᱅᱆᱇᱈᱉á±á±Žá±á±á±‘᱒᱓᱔᱕᱖᱗᱘᱙ᱚᱛᱜá±á±žá±Ÿá± á±¡á±¢á±£á±¤á±¥á±¦á±§á±¨á±©á±ªá±«á±¬á±­á±®á±¯á±°á±±á±²á±³á±´á±µá±¶á±·á±¸á±¹á±ºá±»á±¼á±½ -᱾᱿ -á³á³‘á³’ -᳓ -᳔᳕᳖᳗᳘᳙᳜᳚᳛á³á³žá³Ÿá³ á³¡á³¢á³£á³¤á³¥á³¦á³§á³¨á³©á³ªá³«á³¬á³­á³®á³¯á³°á³±á³²á´€á´á´‚ᴃᴄᴅᴆᴇᴈᴉᴊᴋᴌá´á´Žá´á´á´‘ᴒᴓᴔᴕᴖᴗᴘᴙᴚᴛᴜá´á´žá´Ÿá´ á´¡á´¢á´£á´¤á´¥á´¦á´§á´¨á´©á´ªá´«á´¬á´­á´®á´¯á´°á´±á´²á´³á´´á´µá´¶á´·á´¸á´¹á´ºá´»á´¼á´½á´¾á´¿áµ€áµáµ‚ᵃᵄᵅᵆᵇᵈᵉᵊᵋᵌáµáµŽáµáµáµ‘ᵒᵓᵔᵕᵖᵗᵘᵙᵚᵛᵜáµáµžáµŸáµ áµ¡áµ¢áµ£áµ¤áµ¥áµ¦áµ§áµ¨áµ©áµªáµ«áµ¬áµ­áµ®áµ¯áµ°áµ±áµ²áµ³áµ´áµµáµ¶áµ·áµ¸áµ¹áµºáµ»áµ¼áµ½áµ¾áµ¿á¶€á¶á¶‚ᶃᶄᶅᶆᶇᶈᶉᶊᶋᶌá¶á¶Žá¶á¶á¶‘ᶒᶓᶔᶕᶖᶗᶘᶙᶚᶛᶜá¶á¶žá¶Ÿá¶ á¶¡á¶¢á¶£á¶¤á¶¥á¶¦á¶§á¶¨á¶©á¶ªá¶«á¶¬á¶­á¶®á¶¯á¶°á¶±á¶²á¶³á¶´á¶µá¶¶á¶·á¶¸á¶¹á¶ºá¶»á¶¼á¶½á¶¾á¶¿á·€á·á·‚᷊᷃᷄᷅᷆᷇᷈᷉᷋᷌á·á·Žá·á·á·‘᷒ᷓᷔᷕᷖᷗᷘᷙᷚᷛᷜá·á·žá·Ÿá· á·¡á·¢á·£á·¤á·¥á·¦á·½á·¾á·¿á¸€á¸á¸‚ḃḄḅḆḇḈḉḊḋḌá¸á¸Žá¸á¸á¸‘ḒḓḔḕḖḗḘḙḚḛḜá¸á¸žá¸Ÿá¸ á¸¡á¸¢á¸£á¸¤á¸¥á¸¦á¸§á¸¨á¸©á¸ªá¸«á¸¬á¸­á¸®á¸¯á¸°á¸±á¸²á¸³á¸´á¸µá¸¶á¸·á¸¸á¸¹á¸ºá¸»á¸¼á¸½á¸¾á¸¿á¹€á¹á¹‚ṃṄṅṆṇṈṉṊṋṌá¹á¹Žá¹á¹á¹‘ṒṓṔṕṖṗṘṙṚṛṜá¹á¹žá¹Ÿá¹ á¹¡á¹¢á¹£á¹¤á¹¥á¹¦á¹§á¹¨á¹©á¹ªá¹«á¹¬á¹­á¹®á¹¯á¹°á¹±á¹²á¹³á¹´á¹µá¹¶á¹·á¹¸á¹¹á¹ºá¹»á¹¼á¹½á¹¾á¹¿áº€áºáº‚ẃẄẅẆẇẈẉẊẋẌáºáºŽáºáºáº‘ẒẓẔẕẖẗẘẙẚẛẜáºáºžáºŸáº áº¡áº¢áº£áº¤áº¥áº¦áº§áº¨áº©áºªáº«áº¬áº­áº®áº¯áº°áº±áº²áº³áº´áºµáº¶áº·áº¸áº¹áººáº»áº¼áº½áº¾áº¿á»€á»á»‚ểỄễỆệỈỉỊịỌá»á»Žá»á»á»‘ỒồỔổỖỗỘộỚớỜá»á»žá»Ÿá» á»¡á»¢á»£á»¤á»¥á»¦á»§á»¨á»©á»ªá»«á»¬á»­á»®á»¯á»°á»±á»²á»³á»´á»µá»¶á»·á»¸á»¹á»ºá»»á»¼á»½á»¾á»¿á¼€á¼á¼‚ἃἄἅἆἇἈἉἊἋἌá¼á¼Žá¼á¼á¼‘ἒἓἔἕἘἙἚἛἜá¼á¼ á¼¡á¼¢á¼£á¼¤á¼¥á¼¦á¼§á¼¨á¼©á¼ªá¼«á¼¬á¼­á¼®á¼¯á¼°á¼±á¼²á¼³á¼´á¼µá¼¶á¼·á¼¸á¼¹á¼ºá¼»á¼¼á¼½á¼¾á¼¿á½€á½á½‚ὃὄὅὈὉὊὋὌá½á½á½‘ὒὓὔὕὖὗὙὛá½á½Ÿá½ á½¡á½¢á½£á½¤á½¥á½¦á½§á½¨á½©á½ªá½«á½¬á½­á½®á½¯á½°á½±á½²á½³á½´á½µá½¶á½·á½¸á½¹á½ºá½»á½¼á½½á¾€á¾á¾‚ᾃᾄᾅᾆᾇᾈᾉᾊᾋᾌá¾á¾Žá¾á¾á¾‘ᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜá¾á¾žá¾Ÿá¾ á¾¡á¾¢á¾£á¾¤á¾¥á¾¦á¾§á¾¨á¾©á¾ªá¾«á¾¬á¾­á¾®á¾¯á¾°á¾±á¾²á¾³á¾´á¾¶á¾·á¾¸á¾¹á¾ºá¾»á¾¼ -á¾½ -á¾¾ -᾿῀ῠ-ῂῃῄῆῇῈΈῊΉῌ -á¿á¿Žá¿ -á¿á¿‘ῒΐῖῗῘῙῚΊ -á¿á¿žá¿Ÿ -ῠῡῢΰῤῥῦῧῨῩῪΎῬ -῭΅` -ῲῳῴῶῷῸΌῺΏῼ -´῾ â€â€‚        ​‌â€â€Žâ€â€â€‘‒–—―‖‗‘’‚‛“â€â€žâ€Ÿâ€ â€¡â€¢â€£â€¤â€¥â€¦â€§â€¨â€©â€ªâ€«â€¬â€­â€®â€¯â€°â€±â€²â€³â€´â€µâ€¶â€·â€¸â€¹â€ºâ€»â€¼â€½â€¾â€¿â€ââ‚âƒâ„â…â†â‡âˆâ‰âŠâ‹âŒââŽâââ‘â’â“â”â•â–â—â˜â™âšâ›âœââžâŸâ â¡â¢â£â¤âªâ«â¬â­â®â¯ -â°â±â´âµâ¶â·â¸â¹ -âºâ»â¼â½â¾ -â¿â‚€â‚₂₃₄₅₆₇₈₉ -â‚Šâ‚‹â‚Œâ‚â‚Ž -â‚â‚‘â‚’â‚“â‚” -₠₡₢₣₤₥₦₧₨₩₪₫€₭₮₯₰₱₲₳₴₵₶₷₸ -âƒâƒ‘⃒⃓⃘⃙⃚⃔⃕⃖⃗⃛⃜âƒâƒžâƒŸâƒ âƒ¡âƒ¢âƒ£âƒ¤âƒ¥âƒ¦âƒ§âƒ¨âƒ©âƒªâƒ«âƒ¬âƒ­âƒ®âƒ¯âƒ° -℀℠-â„‚ -℃℄℅℆ -ℇ -℈℉ -â„Šâ„‹â„Œâ„â„Žâ„â„â„‘â„’â„“ -â„” -â„• -№℗℘ -ℙℚℛℜ℠-℞℟℠℡™℣ -ℤ -â„¥ -Ω -℧ -ℨ -â„© -KÅℬℭ -â„® -ℯℰℱℲℳℴℵℶℷℸℹ -℺℻ -ℼℽℾℿ -â…€â…⅂⅃⅄ -ⅅⅆⅇⅈⅉ -â…Šâ…‹â…Œâ… -â…Ž -â… -â…⅑⅒⅓⅔⅕⅖⅗⅘⅙⅚⅛⅜â…⅞⅟ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿↀâ†â†‚Ↄↄↅↆↇↈ↉ -â†â†‘→↓↔↕↖↗↘↙↚↛↜â†â†žâ†Ÿâ† â†¡â†¢â†£â†¤â†¥â†¦â†§â†¨â†©â†ªâ†«â†¬â†­â†®â†¯â†°â†±â†²â†³â†´â†µâ†¶â†·â†¸â†¹â†ºâ†»â†¼â†½â†¾â†¿â‡€â‡â‡‚⇃⇄⇅⇆⇇⇈⇉⇊⇋⇌â‡â‡Žâ‡â‡â‡‘⇒⇓⇔⇕⇖⇗⇘⇙⇚⇛⇜â‡â‡žâ‡Ÿâ‡ â‡¡â‡¢â‡£â‡¤â‡¥â‡¦â‡§â‡¨â‡©â‡ªâ‡«â‡¬â‡­â‡®â‡¯â‡°â‡±â‡²â‡³â‡´â‡µâ‡¶â‡·â‡¸â‡¹â‡ºâ‡»â‡¼â‡½â‡¾â‡¿âˆ€âˆâˆ‚∃∄∅∆∇∈∉∊∋∌âˆâˆŽâˆâˆâˆ‘−∓∔∕∖∗∘∙√∛∜âˆâˆžâˆŸâˆ âˆ¡âˆ¢âˆ£âˆ¤âˆ¥âˆ¦âˆ§âˆ¨âˆ©âˆªâˆ«âˆ¬âˆ­âˆ®âˆ¯âˆ°âˆ±âˆ²âˆ³âˆ´âˆµâˆ¶âˆ·âˆ¸âˆ¹âˆºâˆ»âˆ¼âˆ½âˆ¾âˆ¿â‰€â‰â‰‚≃≄≅≆≇≈≉≊≋≌â‰â‰Žâ‰â‰â‰‘≒≓≔≕≖≗≘≙≚≛≜â‰â‰žâ‰Ÿâ‰ â‰¡â‰¢â‰£â‰¤â‰¥â‰¦â‰§â‰¨â‰©â‰ªâ‰«â‰¬â‰­â‰®â‰¯â‰°â‰±â‰²â‰³â‰´â‰µâ‰¶â‰·â‰¸â‰¹â‰ºâ‰»â‰¼â‰½â‰¾â‰¿âŠ€âŠâŠ‚⊃⊄⊅⊆⊇⊈⊉⊊⊋⊌âŠâŠŽâŠâŠâŠ‘⊒⊓⊔⊕⊖⊗⊘⊙⊚⊛⊜âŠâŠžâŠŸâŠ âŠ¡âŠ¢âŠ£âŠ¤âŠ¥âŠ¦âŠ§âŠ¨âŠ©âŠªâŠ«âŠ¬âŠ­âŠ®âŠ¯âŠ°âŠ±âŠ²âŠ³âŠ´âŠµâŠ¶âŠ·âŠ¸âŠ¹âŠºâŠ»âŠ¼âŠ½âŠ¾âŠ¿â‹€â‹â‹‚⋃⋄⋅⋆⋇⋈⋉⋊⋋⋌â‹â‹Žâ‹â‹â‹‘⋒⋓⋔⋕⋖⋗⋘⋙⋚⋛⋜â‹â‹žâ‹Ÿâ‹ â‹¡â‹¢â‹£â‹¤â‹¥â‹¦â‹§â‹¨â‹©â‹ªâ‹«â‹¬â‹­â‹®â‹¯â‹°â‹±â‹²â‹³â‹´â‹µâ‹¶â‹·â‹¸â‹¹â‹ºâ‹»â‹¼â‹½â‹¾â‹¿âŒ€âŒâŒ‚⌃⌄⌅⌆⌇⌈⌉⌊⌋⌌âŒâŒŽâŒâŒâŒ‘⌒⌓⌔⌕⌖⌗⌘⌙⌚⌛⌜âŒâŒžâŒŸâŒ âŒ¡âŒ¢âŒ£âŒ¤âŒ¥âŒ¦âŒ§âŒ¨âŒ©âŒªâŒ«âŒ¬âŒ­âŒ®âŒ¯âŒ°âŒ±âŒ²âŒ³âŒ´âŒµâŒ¶âŒ·âŒ¸âŒ¹âŒºâŒ»âŒ¼âŒ½âŒ¾âŒ¿â€ââ‚âƒâ„â…â†â‡âˆâ‰âŠâ‹âŒââŽâââ‘â’â“â”â•â–â—â˜â™âšâ›âœââžâŸâ â¡â¢â£â¤â¥â¦â§â¨â©âªâ«â¬â­â®â¯â°â±â²â³â´âµâ¶â·â¸â¹âºâ»â¼â½â¾â¿âŽ€âŽâŽ‚⎃⎄⎅⎆⎇⎈⎉⎊⎋⎌âŽâŽŽâŽâŽâŽ‘⎒⎓⎔⎕⎖⎗⎘⎙⎚⎛⎜âŽâŽžâŽŸâŽ âŽ¡âŽ¢âŽ£âŽ¤âŽ¥âŽ¦âŽ§âŽ¨âŽ©âŽªâŽ«âŽ¬âŽ­âŽ®âŽ¯âŽ°âŽ±âŽ²âŽ³âŽ´âŽµâŽ¶âŽ·âŽ¸âŽ¹âŽºâŽ»âŽ¼âŽ½âŽ¾âŽ¿â€ââ‚âƒâ„â…â†â‡âˆâ‰âŠâ‹âŒââŽâââ‘â’â“â”â•â–â—â˜â™âšâ›âœââžâŸâ â¡â¢â£â¤â¥â¦â§â¨â€ââ‚âƒâ„â…â†â‡âˆâ‰âŠâ‹âŒââŽâââ‘â’â“â”â•â–â—â˜â™âšâ›âœââžâŸâ â¡â¢â£â¤â¥â¦â‘€â‘⑂⑃⑄⑅⑆⑇⑈⑉⑊ -①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳⑴⑵⑶⑷⑸⑹⑺⑻⑼⑽⑾⑿⒀â’⒂⒃⒄⒅⒆⒇⒈⒉⒊⒋⒌â’â’Žâ’â’⒑⒒⒓⒔⒕⒖⒗⒘⒙⒚⒛ -â’œâ’⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀâ“ⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌâ“â“Žâ“â“ⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜâ“ⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ -⓪⓫⓬⓭⓮⓯⓰⓱⓲⓳⓴⓵⓶⓷⓸⓹⓺⓻⓼⓽⓾⓿ -─â”│┃┄┅┆┇┈┉┊┋┌â”┎â”â”┑┒┓└┕┖┗┘┙┚┛├â”┞┟┠┡┢┣┤┥┦┧┨┩┪┫┬┭┮┯┰┱┲┳┴┵┶┷┸┹┺┻┼┽┾┿╀â•â•‚╃╄╅╆╇╈╉╊╋╌â•â•Žâ•â•â•‘╒╓╔╕╖╗╘╙╚╛╜â•â•žâ•Ÿâ• â•¡â•¢â•£â•¤â•¥â•¦â•§â•¨â•©â•ªâ•«â•¬â•­â•®â•¯â•°â•±â•²â•³â•´â•µâ•¶â•·â•¸â•¹â•ºâ•»â•¼â•½â•¾â•¿â–€â–▂▃▄▅▆▇█▉▊▋▌â–â–Žâ–â–░▒▓▔▕▖▗▘▙▚▛▜â–▞▟■□▢▣▤▥▦▧▨▩▪▫▬▭▮▯▰▱▲△▴▵▶▷▸▹►▻▼▽▾▿◀â—◂◃◄◅◆◇◈◉◊○◌â—â—Žâ—â—◑◒◓◔◕◖◗◘◙◚◛◜â—◞◟◠◡◢◣◤◥◦◧◨◩◪◫◬◭◮◯◰◱◲◳◴◵◶◷◸◹◺◻◼◽◾◿☀â˜â˜‚☃☄★☆☇☈☉☊☋☌â˜â˜Žâ˜â˜â˜‘☒☓☔☕☖☗☘☙☚☛☜â˜â˜žâ˜Ÿâ˜ â˜¡â˜¢â˜£â˜¤â˜¥â˜¦â˜§â˜¨â˜©â˜ªâ˜«â˜¬â˜­â˜®â˜¯â˜°â˜±â˜²â˜³â˜´â˜µâ˜¶â˜·â˜¸â˜¹â˜ºâ˜»â˜¼â˜½â˜¾â˜¿â™€â™â™‚♃♄♅♆♇♈♉♊♋♌â™â™Žâ™â™â™‘♒♓♔♕♖♗♘♙♚♛♜â™â™žâ™Ÿâ™ â™¡â™¢â™£â™¤â™¥â™¦â™§â™¨â™©â™ªâ™«â™¬â™­â™®â™¯â™°â™±â™²â™³â™´â™µâ™¶â™·â™¸â™¹â™ºâ™»â™¼â™½â™¾â™¿âš€âšâš‚⚃⚄⚅⚆⚇⚈⚉⚊⚋⚌âšâšŽâšâšâš‘⚒⚓⚔⚕⚖⚗⚘⚙⚚⚛⚜âšâšžâšŸâš âš¡âš¢âš£âš¤âš¥âš¦âš§âš¨âš©âšªâš«âš¬âš­âš®âš¯âš°âš±âš²âš³âš´âšµâš¶âš·âš¸âš¹âšºâš»âš¼âš½âš¾âš¿â›€â›â›‚⛃⛄⛅⛆⛇⛈⛉⛊⛋⛌â›â›â›â›‘⛒⛓⛔⛕⛖⛗⛘⛙⛚⛛⛜â›â›žâ›Ÿâ› â›¡â›£â›¨â›©â›ªâ›«â›¬â›­â›®â›¯â›°â›±â›²â›³â›´â›µâ›¶â›·â›¸â›¹â›ºâ›»â›¼â›½â›¾â›¿âœâœ‚✃✄✆✇✈✉✌âœâœŽâœâœâœ‘✒✓✔✕✖✗✘✙✚✛✜âœâœžâœŸâœ âœ¡âœ¢âœ£âœ¤âœ¥âœ¦âœ§âœ©âœªâœ«âœ¬âœ­âœ®âœ¯âœ°âœ±âœ²âœ³âœ´âœµâœ¶âœ·âœ¸âœ¹âœºâœ»âœ¼âœ½âœ¾âœ¿â€ââ‚âƒâ„â…â†â‡âˆâ‰âŠâ‹ââââ‘â’â–â—â˜â™âšâ›âœââžâ¡â¢â£â¤â¥â¦â§â¨â©âªâ«â¬â­â®â¯â°â±â²â³â´âµ -â¶â·â¸â¹âºâ»â¼â½â¾â¿âž€âžâž‚➃➄➅➆➇➈➉➊➋➌âžâžŽâžâžâž‘âž’âž“ -➔➘➙➚➛➜âžâžžâžŸâž âž¡âž¢âž£âž¤âž¥âž¦âž§âž¨âž©âžªâž«âž¬âž­âž®âž¯âž±âž²âž³âž´âžµâž¶âž·âž¸âž¹âžºâž»âž¼âž½âž¾âŸ€âŸâŸ‚⟃⟄⟅⟆⟇⟈⟉⟊⟌âŸâŸ‘⟒⟓⟔⟕⟖⟗⟘⟙⟚⟛⟜âŸâŸžâŸŸâŸ âŸ¡âŸ¢âŸ£âŸ¤âŸ¥âŸ¦âŸ§âŸ¨âŸ©âŸªâŸ«âŸ¬âŸ­âŸ®âŸ¯âŸ°âŸ±âŸ²âŸ³âŸ´âŸµâŸ¶âŸ·âŸ¸âŸ¹âŸºâŸ»âŸ¼âŸ½âŸ¾âŸ¿â €â â ‚⠃⠄⠅⠆⠇⠈⠉⠊⠋⠌â â Žâ â â ‘⠒⠓⠔⠕⠖⠗⠘⠙⠚⠛⠜â â žâ Ÿâ  â ¡â ¢â £â ¤â ¥â ¦â §â ¨â ©â ªâ «â ¬â ­â ®â ¯â °â ±â ²â ³â ´â µâ ¶â ·â ¸â ¹â ºâ »â ¼â ½â ¾â ¿â¡€â¡â¡‚⡃⡄⡅⡆⡇⡈⡉⡊⡋⡌â¡â¡Žâ¡â¡â¡‘⡒⡓⡔⡕⡖⡗⡘⡙⡚⡛⡜â¡â¡žâ¡Ÿâ¡ â¡¡â¡¢â¡£â¡¤â¡¥â¡¦â¡§â¡¨â¡©â¡ªâ¡«â¡¬â¡­â¡®â¡¯â¡°â¡±â¡²â¡³â¡´â¡µâ¡¶â¡·â¡¸â¡¹â¡ºâ¡»â¡¼â¡½â¡¾â¡¿â¢€â¢â¢‚⢃⢄⢅⢆⢇⢈⢉⢊⢋⢌â¢â¢Žâ¢â¢â¢‘⢒⢓⢔⢕⢖⢗⢘⢙⢚⢛⢜â¢â¢žâ¢Ÿâ¢ â¢¡â¢¢â¢£â¢¤â¢¥â¢¦â¢§â¢¨â¢©â¢ªâ¢«â¢¬â¢­â¢®â¢¯â¢°â¢±â¢²â¢³â¢´â¢µâ¢¶â¢·â¢¸â¢¹â¢ºâ¢»â¢¼â¢½â¢¾â¢¿â£€â£â£‚⣃⣄⣅⣆⣇⣈⣉⣊⣋⣌â£â£Žâ£â£â£‘⣒⣓⣔⣕⣖⣗⣘⣙⣚⣛⣜â£â£žâ£Ÿâ£ â£¡â£¢â££â£¤â£¥â£¦â£§â£¨â£©â£ªâ£«â£¬â£­â£®â£¯â£°â£±â£²â£³â£´â£µâ£¶â£·â£¸â£¹â£ºâ£»â£¼â£½â£¾â£¿â¤€â¤â¤‚⤃⤄⤅⤆⤇⤈⤉⤊⤋⤌â¤â¤Žâ¤â¤â¤‘⤒⤓⤔⤕⤖⤗⤘⤙⤚⤛⤜â¤â¤žâ¤Ÿâ¤ â¤¡â¤¢â¤£â¤¤â¤¥â¤¦â¤§â¤¨â¤©â¤ªâ¤«â¤¬â¤­â¤®â¤¯â¤°â¤±â¤²â¤³â¤´â¤µâ¤¶â¤·â¤¸â¤¹â¤ºâ¤»â¤¼â¤½â¤¾â¤¿â¥€â¥â¥‚⥃⥄⥅⥆⥇⥈⥉⥊⥋⥌â¥â¥Žâ¥â¥â¥‘⥒⥓⥔⥕⥖⥗⥘⥙⥚⥛⥜â¥â¥žâ¥Ÿâ¥ â¥¡â¥¢â¥£â¥¤â¥¥â¥¦â¥§â¥¨â¥©â¥ªâ¥«â¥¬â¥­â¥®â¥¯â¥°â¥±â¥²â¥³â¥´â¥µâ¥¶â¥·â¥¸â¥¹â¥ºâ¥»â¥¼â¥½â¥¾â¥¿â¦€â¦â¦‚⦃⦄⦅⦆⦇⦈⦉⦊⦋⦌â¦â¦Žâ¦â¦â¦‘⦒⦓⦔⦕⦖⦗⦘⦙⦚⦛⦜â¦â¦žâ¦Ÿâ¦ â¦¡â¦¢â¦£â¦¤â¦¥â¦¦â¦§â¦¨â¦©â¦ªâ¦«â¦¬â¦­â¦®â¦¯â¦°â¦±â¦²â¦³â¦´â¦µâ¦¶â¦·â¦¸â¦¹â¦ºâ¦»â¦¼â¦½â¦¾â¦¿â§€â§â§‚⧃⧄⧅⧆⧇⧈⧉⧊⧋⧌â§â§Žâ§â§â§‘⧒⧓⧔⧕⧖⧗⧘⧙⧚⧛⧜â§â§žâ§Ÿâ§ â§¡â§¢â§£â§¤â§¥â§¦â§§â§¨â§©â§ªâ§«â§¬â§­â§®â§¯â§°â§±â§²â§³â§´â§µâ§¶â§·â§¸â§¹â§ºâ§»â§¼â§½â§¾â§¿â¨€â¨â¨‚⨃⨄⨅⨆⨇⨈⨉⨊⨋⨌â¨â¨Žâ¨â¨â¨‘⨒⨓⨔⨕⨖⨗⨘⨙⨚⨛⨜â¨â¨žâ¨Ÿâ¨ â¨¡â¨¢â¨£â¨¤â¨¥â¨¦â¨§â¨¨â¨©â¨ªâ¨«â¨¬â¨­â¨®â¨¯â¨°â¨±â¨²â¨³â¨´â¨µâ¨¶â¨·â¨¸â¨¹â¨ºâ¨»â¨¼â¨½â¨¾â¨¿â©€â©â©‚⩃⩄⩅⩆⩇⩈⩉⩊⩋⩌â©â©Žâ©â©â©‘⩒⩓⩔⩕⩖⩗⩘⩙⩚⩛⩜â©â©žâ©Ÿâ© â©¡â©¢â©£â©¤â©¥â©¦â©§â©¨â©©â©ªâ©«â©¬â©­â©®â©¯â©°â©±â©²â©³â©´â©µâ©¶â©·â©¸â©¹â©ºâ©»â©¼â©½â©¾â©¿âª€âªâª‚⪃⪄⪅⪆⪇⪈⪉⪊⪋⪌âªâªŽâªâªâª‘⪒⪓⪔⪕⪖⪗⪘⪙⪚⪛⪜âªâªžâªŸâª âª¡âª¢âª£âª¤âª¥âª¦âª§âª¨âª©âªªâª«âª¬âª­âª®âª¯âª°âª±âª²âª³âª´âªµâª¶âª·âª¸âª¹âªºâª»âª¼âª½âª¾âª¿â«€â«â«‚⫃⫄⫅⫆⫇⫈⫉⫊⫋⫌â«â«Žâ«â«â«‘⫒⫓⫔⫕⫖⫗⫘⫙⫚⫛⫝̸â«â«žâ«Ÿâ« â«¡â«¢â«£â«¤â«¥â«¦â«§â«¨â«©â«ªâ««â«¬â«­â«®â«¯â«°â«±â«²â«³â«´â«µâ«¶â«·â«¸â«¹â«ºâ«»â«¼â«½â«¾â«¿â¬€â¬â¬‚⬃⬄⬅⬆⬇⬈⬉⬊⬋⬌â¬â¬Žâ¬â¬â¬‘⬒⬓⬔⬕⬖⬗⬘⬙⬚⬛⬜â¬â¬žâ¬Ÿâ¬ â¬¡â¬¢â¬£â¬¤â¬¥â¬¦â¬§â¬¨â¬©â¬ªâ¬«â¬¬â¬­â¬®â¬¯â¬°â¬±â¬²â¬³â¬´â¬µâ¬¶â¬·â¬¸â¬¹â¬ºâ¬»â¬¼â¬½â¬¾â¬¿â­€â­â­‚⭃⭄⭅⭆⭇⭈⭉⭊⭋⭌â­â­‘⭒⭓⭔⭕⭖⭗⭘⭙ -â°€â°â°‚ⰃⰄⰅⰆⰇⰈⰉⰊⰋⰌâ°â°Žâ°â°â°‘ⰒⰓⰔⰕⰖⰗⰘⰙⰚⰛⰜâ°â°žâ°Ÿâ° â°¡â°¢â°£â°¤â°¥â°¦â°§â°¨â°©â°ªâ°«â°¬â°­â°®â°°â°±â°²â°³â°´â°µâ°¶â°·â°¸â°¹â°ºâ°»â°¼â°½â°¾â°¿â±€â±â±‚ⱃⱄⱅⱆⱇⱈⱉⱊⱋⱌâ±â±Žâ±â±â±‘ⱒⱓⱔⱕⱖⱗⱘⱙⱚⱛⱜâ±â±žâ± â±¡â±¢â±£â±¤â±¥â±¦â±§â±¨â±©â±ªâ±«â±¬â±­â±®â±¯â±°â±±â±²â±³â±´â±µâ±¶â±·â±¸â±¹â±ºâ±»â±¼â±½â±¾â±¿â²€â²â²‚ⲃⲄⲅⲆⲇⲈⲉⲊⲋⲌâ²â²Žâ²â²â²‘ⲒⲓⲔⲕⲖⲗⲘⲙⲚⲛⲜâ²â²žâ²Ÿâ² â²¡â²¢â²£â²¤â²¥â²¦â²§â²¨â²©â²ªâ²«â²¬â²­â²®â²¯â²°â²±â²²â²³â²´â²µâ²¶â²·â²¸â²¹â²ºâ²»â²¼â²½â²¾â²¿â³€â³â³‚ⳃⳄⳅⳆⳇⳈⳉⳊⳋⳌâ³â³Žâ³â³â³‘ⳒⳓⳔⳕⳖⳗⳘⳙⳚⳛⳜâ³â³žâ³Ÿâ³ â³¡â³¢â³£â³¤ -⳥⳦⳧⳨⳩⳪ -ⳫⳬⳭⳮ⳯⳰⳱ -⳹⳺⳻⳼ -â³½ -⳾⳿ -â´€â´â´‚ⴃⴄⴅⴆⴇⴈⴉⴊⴋⴌâ´â´Žâ´â´â´‘ⴒⴓⴔⴕⴖⴗⴘⴙⴚⴛⴜâ´â´žâ´Ÿâ´ â´¡â´¢â´£â´¤â´¥â´°â´±â´²â´³â´´â´µâ´¶â´·â´¸â´¹â´ºâ´»â´¼â´½â´¾â´¿âµ€âµâµ‚ⵃⵄⵅⵆⵇⵈⵉⵊⵋⵌâµâµŽâµâµâµ‘ⵒⵓⵔⵕⵖⵗⵘⵙⵚⵛⵜâµâµžâµŸâµ âµ¡âµ¢âµ£âµ¤âµ¥âµ¯â¶€â¶â¶‚ⶃⶄⶅⶆⶇⶈⶉⶊⶋⶌâ¶â¶Žâ¶â¶â¶‘ⶒⶓⶔⶕⶖⶠⶡⶢⶣⶤⶥⶦⶨⶩⶪⶫⶬⶭⶮⶰⶱⶲⶳⶴⶵⶶⶸⶹⶺⶻⶼⶽⶾⷀâ·â·‚ⷃⷄⷅⷆⷈⷉⷊⷋⷌâ·â·Žâ·â·‘ⷒⷓⷔⷕⷖⷘⷙⷚⷛⷜâ·â·žâ· â·¡â·¢â·£â·¤â·¥â·¦â·§â·¨â·©â·ªâ·«â·¬â·­â·®â·¯â·°â·±â·²â·³â·´â·µâ·¶â··â·¸â·¹â·ºâ·»â·¼â·½â·¾â·¿ -⸀â¸â¸‚⸃⸄⸅⸆⸇⸈⸉⸊⸋⸌â¸â¸Žâ¸â¸â¸‘⸒⸓⸔⸕⸖⸗⸘⸙⸚⸛⸜â¸â¸žâ¸Ÿâ¸ â¸¡â¸¢â¸£â¸¤â¸¥â¸¦â¸§â¸¨â¸©â¸ªâ¸«â¸¬â¸­â¸® -ⸯ -⸰⸱⺀âºâº‚⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌âºâºŽâºâºâº‘⺒⺓⺔⺕⺖⺗⺘⺙⺛⺜âºâºžâºŸâº âº¡âº¢âº£âº¤âº¥âº¦âº§âº¨âº©âºªâº«âº¬âº­âº®âº¯âº°âº±âº²âº³âº´âºµâº¶âº·âº¸âº¹âººâº»âº¼âº½âº¾âº¿â»€â»â»‚⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌â»â»Žâ»â»â»‘⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜â»â»žâ»Ÿâ» â»¡â»¢â»£â»¤â»¥â»¦â»§â»¨â»©â»ªâ»«â»¬â»­â»®â»¯â»°â»±â»²â»³â¼€â¼â¼‚⼃⼄⼅⼆⼇⼈⼉⼊⼋⼌â¼â¼Žâ¼â¼â¼‘⼒⼓⼔⼕⼖⼗⼘⼙⼚⼛⼜â¼â¼žâ¼Ÿâ¼ â¼¡â¼¢â¼£â¼¤â¼¥â¼¦â¼§â¼¨â¼©â¼ªâ¼«â¼¬â¼­â¼®â¼¯â¼°â¼±â¼²â¼³â¼´â¼µâ¼¶â¼·â¼¸â¼¹â¼ºâ¼»â¼¼â¼½â¼¾â¼¿â½€â½â½‚⽃⽄⽅⽆⽇⽈⽉⽊⽋⽌â½â½Žâ½â½â½‘⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜â½â½žâ½Ÿâ½ â½¡â½¢â½£â½¤â½¥â½¦â½§â½¨â½©â½ªâ½«â½¬â½­â½®â½¯â½°â½±â½²â½³â½´â½µâ½¶â½·â½¸â½¹â½ºâ½»â½¼â½½â½¾â½¿â¾€â¾â¾‚⾃⾄⾅⾆⾇⾈⾉⾊⾋⾌â¾â¾Žâ¾â¾â¾‘⾒⾓⾔⾕⾖⾗⾘⾙⾚⾛⾜â¾â¾žâ¾Ÿâ¾ â¾¡â¾¢â¾£â¾¤â¾¥â¾¦â¾§â¾¨â¾©â¾ªâ¾«â¾¬â¾­â¾®â¾¯â¾°â¾±â¾²â¾³â¾´â¾µâ¾¶â¾·â¾¸â¾¹â¾ºâ¾»â¾¼â¾½â¾¾â¾¿â¿€â¿â¿‚⿃⿄⿅⿆⿇⿈⿉⿊⿋⿌â¿â¿Žâ¿â¿â¿‘⿒⿓⿔⿕⿰⿱⿲⿳⿴⿵⿶⿷⿸⿹⿺⿻ ã€ã€‚〃〄 -々〆〇 -〈〉《》「ã€ã€Žã€ã€ã€‘〒〓〔〕〖〗〘〙〚〛〜ã€ã€žã€Ÿã€  -〡〢〣〤〥〦〧〨〩〪〭〮〯〫〬 -〰 -〱〲〳〴〵 -〶〷 -〸〹〺〻〼 -〽〾〿 -ãã‚ãƒã„ã…ã†ã‡ãˆã‰ãŠã‹ãŒããŽããã‘ã’ã“ã”ã•ã–ã—ã˜ã™ãšã›ãœããžãŸã ã¡ã¢ã£ã¤ã¥ã¦ã§ã¨ã©ãªã«ã¬ã­ã®ã¯ã°ã±ã²ã³ã´ãµã¶ã·ã¸ã¹ãºã»ã¼ã½ã¾ã¿ã‚€ã‚もゃやゅゆょよらりるれã‚ã‚Žã‚ã‚ゑをんゔゕゖ゙゚ -゛゜ -ã‚ã‚žã‚Ÿ -ã‚  -ァアィイゥウェエォオカガキギクグケゲコゴサザシジスズセゼソゾタダãƒãƒ‚ッツヅテデトドナニヌãƒãƒŽãƒãƒãƒ‘ヒビピフブプヘベペホボãƒãƒžãƒŸãƒ ãƒ¡ãƒ¢ãƒ£ãƒ¤ãƒ¥ãƒ¦ãƒ§ãƒ¨ãƒ©ãƒªãƒ«ãƒ¬ãƒ­ãƒ®ãƒ¯ãƒ°ãƒ±ãƒ²ãƒ³ãƒ´ãƒµãƒ¶ãƒ·ãƒ¸ãƒ¹ãƒº -・ -ーヽヾヿㄅㄆㄇㄈㄉㄊㄋㄌã„ã„Žã„ã„ㄑㄒㄓㄔㄕㄖㄗㄘㄙㄚㄛㄜã„ㄞㄟㄠㄡㄢㄣㄤㄥㄦㄧㄨㄩㄪㄫㄬㄭㄱㄲㄳㄴㄵㄶㄷㄸㄹㄺㄻㄼㄽㄾㄿㅀã…ㅂㅃㅄㅅㅆㅇㅈㅉㅊㅋㅌã…ã…Žã…ã…ㅑㅒㅓㅔㅕㅖㅗㅘㅙㅚㅛㅜã…ㅞㅟㅠㅡㅢㅣㅤㅥㅦㅧㅨㅩㅪㅫㅬㅭㅮㅯㅰㅱㅲㅳㅴㅵㅶㅷㅸㅹㅺㅻㅼㅽㅾㅿㆀã†ã†‚ㆃㆄㆅㆆㆇㆈㆉㆊㆋㆌã†ã†Ž -ã†ã†‘ -㆒㆓㆔㆕ -㆖㆗㆘㆙㆚㆛㆜ã†ã†žã†Ÿ -ㆠㆡㆢㆣㆤㆥㆦㆧㆨㆩㆪㆫㆬㆭㆮㆯㆰㆱㆲㆳㆴㆵㆶㆷ -㇀ã‡ã‡‚㇃㇄㇅㇆㇇㇈㇉㇊㇋㇌ã‡ã‡Žã‡ã‡ã‡‘㇒㇓㇔㇕㇖㇗㇘㇙㇚㇛㇜ã‡ã‡žã‡Ÿã‡ ã‡¡ã‡¢ã‡£ -ㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿ -㈀ãˆãˆ‚㈃㈄㈅㈆㈇㈈㈉㈊㈋㈌ãˆãˆŽãˆãˆãˆ‘㈒㈓㈔㈕㈖㈗㈘㈙㈚㈛㈜ãˆãˆž -㈠㈡㈢㈣㈤㈥㈦㈧㈨㈩ -㈪㈫㈬㈭㈮㈯㈰㈱㈲㈳㈴㈵㈶㈷㈸㈹㈺㈻㈼㈽㈾㈿㉀ã‰ã‰‚㉃㉄㉅㉆㉇㉈㉉㉊㉋㉌ã‰ã‰Žã‰ã‰ -㉑㉒㉓㉔㉕㉖㉗㉘㉙㉚㉛㉜ã‰ã‰žã‰Ÿ -㉠㉡㉢㉣㉤㉥㉦㉧㉨㉩㉪㉫㉬㉭㉮㉯㉰㉱㉲㉳㉴㉵㉶㉷㉸㉹㉺㉻㉼㉽㉾㉿ -㊀ãŠãŠ‚㊃㊄㊅㊆㊇㊈㊉ -㊊㊋㊌ãŠãŠŽãŠãŠãŠ‘㊒㊓㊔㊕㊖㊗㊘㊙㊚㊛㊜ãŠãŠžãŠŸãŠ ãŠ¡ãŠ¢ãŠ£ãŠ¤ãŠ¥ãŠ¦ãŠ§ãŠ¨ãŠ©ãŠªãŠ«ãŠ¬ãŠ­ãŠ®ãŠ¯ãŠ° -㊱㊲㊳㊴㊵㊶㊷㊸㊹㊺㊻㊼㊽㊾㊿ -ã‹€ã‹ã‹‚㋃㋄㋅㋆㋇㋈㋉㋊㋋㋌ã‹ã‹Žã‹ã‹ã‹‘㋒㋓㋔㋕㋖㋗㋘㋙㋚㋛㋜ã‹ã‹žã‹Ÿã‹ ã‹¡ã‹¢ã‹£ã‹¤ã‹¥ã‹¦ã‹§ã‹¨ã‹©ã‹ªã‹«ã‹¬ã‹­ã‹®ã‹¯ã‹°ã‹±ã‹²ã‹³ã‹´ã‹µã‹¶ã‹·ã‹¸ã‹¹ã‹ºã‹»ã‹¼ã‹½ã‹¾ãŒ€ãŒãŒ‚㌃㌄㌅㌆㌇㌈㌉㌊㌋㌌ãŒãŒŽãŒãŒãŒ‘㌒㌓㌔㌕㌖㌗㌘㌙㌚㌛㌜ãŒãŒžãŒŸãŒ ãŒ¡ãŒ¢ãŒ£ãŒ¤ãŒ¥ãŒ¦ãŒ§ãŒ¨ãŒ©ãŒªãŒ«ãŒ¬ãŒ­ãŒ®ãŒ¯ãŒ°ãŒ±ãŒ²ãŒ³ãŒ´ãŒµãŒ¶ãŒ·ãŒ¸ãŒ¹ãŒºãŒ»ãŒ¼ãŒ½ãŒ¾ãŒ¿ã€ãã‚ãƒã„ã…ã†ã‡ãˆã‰ãŠã‹ãŒããŽããã‘ã’ã“ã”ã•ã–ã—ã˜ã™ãšã›ãœããžãŸã ã¡ã¢ã£ã¤ã¥ã¦ã§ã¨ã©ãªã«ã¬ã­ã®ã¯ã°ã±ã²ã³ã´ãµã¶ã·ã¸ã¹ãºã»ã¼ã½ã¾ã¿ãŽ€ãŽãŽ‚㎃㎄㎅㎆㎇㎈㎉㎊㎋㎌ãŽãŽŽãŽãŽãŽ‘㎒㎓㎔㎕㎖㎗㎘㎙㎚㎛㎜ãŽãŽžãŽŸãŽ ãŽ¡ãŽ¢ãŽ£ãŽ¤ãŽ¥ãŽ¦ãŽ§ãŽ¨ãŽ©ãŽªãŽ«ãŽ¬ãŽ­ãŽ®ãŽ¯ãŽ°ãŽ±ãŽ²ãŽ³ãŽ´ãŽµãŽ¶ãŽ·ãŽ¸ãŽ¹ãŽºãŽ»ãŽ¼ãŽ½ãŽ¾ãŽ¿ã€ãã‚ãƒã„ã…ã†ã‡ãˆã‰ãŠã‹ãŒããŽããã‘ã’ã“ã”ã•ã–ã—ã˜ã™ãšã›ãœããžãŸã ã¡ã¢ã£ã¤ã¥ã¦ã§ã¨ã©ãªã«ã¬ã­ã®ã¯ã°ã±ã²ã³ã´ãµã¶ã·ã¸ã¹ãºã»ã¼ã½ã¾ã¿ -ã€ä¶µ -ä·€ä·ä·‚䷃䷄䷅䷆䷇䷈䷉䷊䷋䷌ä·ä·Žä·ä·ä·‘䷒䷓䷔䷕䷖䷗䷘䷙䷚䷛䷜ä·ä·žä·Ÿä· ä·¡ä·¢ä·£ä·¤ä·¥ä·¦ä·§ä·¨ä·©ä·ªä·«ä·¬ä·­ä·®ä·¯ä·°ä·±ä·²ä·³ä·´ä·µä·¶ä··ä·¸ä·¹ä·ºä·»ä·¼ä·½ä·¾ä·¿ -一鿋ꀀê€ê€‚ꀃꀄꀅꀆꀇꀈꀉꀊꀋꀌê€ê€Žê€ê€ê€‘ꀒꀓꀔꀕꀖꀗꀘꀙꀚꀛꀜê€ê€žê€Ÿê€ ê€¡ê€¢ê€£ê€¤ê€¥ê€¦ê€§ê€¨ê€©ê€ªê€«ê€¬ê€­ê€®ê€¯ê€°ê€±ê€²ê€³ê€´ê€µê€¶ê€·ê€¸ê€¹ê€ºê€»ê€¼ê€½ê€¾ê€¿ê€êê‚êƒê„ê…ê†ê‡êˆê‰êŠê‹êŒêêŽêêê‘ê’ê“ê”ê•ê–ê—ê˜ê™êšê›êœêêžêŸê ê¡ê¢ê£ê¤ê¥ê¦ê§ê¨ê©êªê«ê¬ê­ê®ê¯ê°ê±ê²ê³ê´êµê¶ê·ê¸ê¹êºê»ê¼ê½ê¾ê¿ê‚€ê‚ꂂꂃꂄꂅꂆꂇꂈꂉꂊꂋꂌê‚ê‚Žê‚ê‚ꂑꂒꂓꂔꂕꂖꂗꂘꂙꂚꂛꂜê‚ꂞꂟꂠꂡꂢꂣꂤꂥꂦꂧꂨꂩꂪꂫꂬꂭꂮꂯꂰꂱꂲꂳꂴꂵꂶꂷꂸꂹꂺꂻꂼꂽꂾꂿꃀêƒêƒ‚ꃃꃄꃅꃆꃇꃈꃉꃊꃋꃌêƒêƒŽêƒêƒêƒ‘ꃒꃓꃔꃕꃖꃗꃘꃙꃚꃛꃜêƒêƒžêƒŸêƒ êƒ¡êƒ¢êƒ£êƒ¤êƒ¥êƒ¦êƒ§êƒ¨êƒ©êƒªêƒ«êƒ¬êƒ­êƒ®êƒ¯êƒ°êƒ±êƒ²êƒ³êƒ´êƒµêƒ¶êƒ·êƒ¸êƒ¹êƒºêƒ»êƒ¼êƒ½êƒ¾êƒ¿ê„€ê„ꄂꄃꄄꄅꄆꄇꄈꄉꄊꄋꄌê„ê„Žê„ê„ꄑꄒꄓꄔꄕꄖꄗꄘꄙꄚꄛꄜê„ꄞꄟꄠꄡꄢꄣꄤꄥꄦꄧꄨꄩꄪꄫꄬꄭꄮꄯꄰꄱꄲꄳꄴꄵꄶꄷꄸꄹꄺꄻꄼꄽꄾꄿꅀê…ꅂꅃꅄꅅꅆꅇꅈꅉꅊꅋꅌê…ê…Žê…ê…ꅑꅒꅓꅔꅕꅖꅗꅘꅙꅚꅛꅜê…ꅞꅟꅠꅡꅢꅣꅤꅥꅦꅧꅨꅩꅪꅫꅬꅭꅮꅯꅰꅱꅲꅳꅴꅵꅶꅷꅸꅹꅺꅻꅼꅽꅾꅿꆀê†ê†‚ꆃꆄꆅꆆꆇꆈꆉꆊꆋꆌê†ê†Žê†ê†ê†‘ꆒꆓꆔꆕꆖꆗꆘꆙꆚꆛꆜê†ê†žê†Ÿê† ê†¡ê†¢ê†£ê†¤ê†¥ê†¦ê†§ê†¨ê†©ê†ªê†«ê†¬ê†­ê†®ê†¯ê†°ê†±ê†²ê†³ê†´ê†µê†¶ê†·ê†¸ê†¹ê†ºê†»ê†¼ê†½ê†¾ê†¿ê‡€ê‡ê‡‚ꇃꇄꇅꇆꇇꇈꇉꇊꇋꇌê‡ê‡Žê‡ê‡ê‡‘ꇒꇓꇔꇕꇖꇗꇘꇙꇚꇛꇜê‡ê‡žê‡Ÿê‡ ê‡¡ê‡¢ê‡£ê‡¤ê‡¥ê‡¦ê‡§ê‡¨ê‡©ê‡ªê‡«ê‡¬ê‡­ê‡®ê‡¯ê‡°ê‡±ê‡²ê‡³ê‡´ê‡µê‡¶ê‡·ê‡¸ê‡¹ê‡ºê‡»ê‡¼ê‡½ê‡¾ê‡¿êˆ€êˆêˆ‚ꈃꈄꈅꈆꈇꈈꈉꈊꈋꈌêˆêˆŽêˆêˆêˆ‘ꈒꈓꈔꈕꈖꈗꈘꈙꈚꈛꈜêˆêˆžêˆŸêˆ êˆ¡êˆ¢êˆ£êˆ¤êˆ¥êˆ¦êˆ§êˆ¨êˆ©êˆªêˆ«êˆ¬êˆ­êˆ®êˆ¯êˆ°êˆ±êˆ²êˆ³êˆ´êˆµêˆ¶êˆ·êˆ¸êˆ¹êˆºêˆ»êˆ¼êˆ½êˆ¾êˆ¿ê‰€ê‰ê‰‚ꉃꉄꉅꉆꉇꉈꉉꉊꉋꉌê‰ê‰Žê‰ê‰ê‰‘ꉒꉓꉔꉕꉖꉗꉘꉙꉚꉛꉜê‰ê‰žê‰Ÿê‰ ê‰¡ê‰¢ê‰£ê‰¤ê‰¥ê‰¦ê‰§ê‰¨ê‰©ê‰ªê‰«ê‰¬ê‰­ê‰®ê‰¯ê‰°ê‰±ê‰²ê‰³ê‰´ê‰µê‰¶ê‰·ê‰¸ê‰¹ê‰ºê‰»ê‰¼ê‰½ê‰¾ê‰¿êŠ€êŠêŠ‚ꊃꊄꊅꊆꊇꊈꊉꊊꊋꊌêŠêŠŽêŠêŠêŠ‘ꊒꊓꊔꊕꊖꊗꊘꊙꊚꊛꊜêŠêŠžêŠŸêŠ êŠ¡êŠ¢êŠ£êŠ¤êŠ¥êŠ¦êŠ§êŠ¨êŠ©êŠªêŠ«êŠ¬êŠ­êŠ®êŠ¯êŠ°êŠ±êŠ²êŠ³êŠ´êŠµêŠ¶êŠ·êŠ¸êŠ¹êŠºêŠ»êŠ¼êŠ½êŠ¾êŠ¿ê‹€ê‹ê‹‚ꋃꋄꋅꋆꋇꋈꋉꋊꋋꋌê‹ê‹Žê‹ê‹ê‹‘ꋒꋓꋔꋕꋖꋗꋘꋙꋚꋛꋜê‹ê‹žê‹Ÿê‹ ê‹¡ê‹¢ê‹£ê‹¤ê‹¥ê‹¦ê‹§ê‹¨ê‹©ê‹ªê‹«ê‹¬ê‹­ê‹®ê‹¯ê‹°ê‹±ê‹²ê‹³ê‹´ê‹µê‹¶ê‹·ê‹¸ê‹¹ê‹ºê‹»ê‹¼ê‹½ê‹¾ê‹¿êŒ€êŒêŒ‚ꌃꌄꌅꌆꌇꌈꌉꌊꌋꌌêŒêŒŽêŒêŒêŒ‘ꌒꌓꌔꌕꌖꌗꌘꌙꌚꌛꌜêŒêŒžêŒŸêŒ êŒ¡êŒ¢êŒ£êŒ¤êŒ¥êŒ¦êŒ§êŒ¨êŒ©êŒªêŒ«êŒ¬êŒ­êŒ®êŒ¯êŒ°êŒ±êŒ²êŒ³êŒ´êŒµêŒ¶êŒ·êŒ¸êŒ¹êŒºêŒ»êŒ¼êŒ½êŒ¾êŒ¿ê€êê‚êƒê„ê…ê†ê‡êˆê‰êŠê‹êŒêêŽêêê‘ê’ê“ê”ê•ê–ê—ê˜ê™êšê›êœêêžêŸê ê¡ê¢ê£ê¤ê¥ê¦ê§ê¨ê©êªê«ê¬ê­ê®ê¯ê°ê±ê²ê³ê´êµê¶ê·ê¸ê¹êºê»ê¼ê½ê¾ê¿êŽ€êŽêŽ‚ꎃꎄꎅꎆꎇꎈꎉꎊꎋꎌêŽêŽŽêŽêŽêŽ‘ꎒꎓꎔꎕꎖꎗꎘꎙꎚꎛꎜêŽêŽžêŽŸêŽ êŽ¡êŽ¢êŽ£êŽ¤êŽ¥êŽ¦êŽ§êŽ¨êŽ©êŽªêŽ«êŽ¬êŽ­êŽ®êŽ¯êŽ°êŽ±êŽ²êŽ³êŽ´êŽµêŽ¶êŽ·êŽ¸êŽ¹êŽºêŽ»êŽ¼êŽ½êŽ¾êŽ¿ê€êê‚êƒê„ê…ê†ê‡êˆê‰êŠê‹êŒêêŽêêê‘ê’ê“ê”ê•ê–ê—ê˜ê™êšê›êœêêžêŸê ê¡ê¢ê£ê¤ê¥ê¦ê§ê¨ê©êªê«ê¬ê­ê®ê¯ê°ê±ê²ê³ê´êµê¶ê·ê¸ê¹êºê»ê¼ê½ê¾ê¿ê€êê‚êƒê„ê…ê†ê‡êˆê‰êŠê‹êŒêêŽêêê‘ê’ê“ê”ê•ê–ê—ê˜ê™êšê›êœêêžêŸê ê¡ê¢ê£ê¤ê¥ê¦ê§ê¨ê©êªê«ê¬ê­ê®ê¯ê°ê±ê²ê³ê´êµê¶ê·ê¸ê¹êºê»ê¼ê½ê¾ê¿ê‘€ê‘ꑂꑃꑄꑅꑆꑇꑈꑉꑊꑋꑌê‘ê‘Žê‘ê‘ꑑꑒꑓꑔꑕꑖꑗꑘꑙꑚꑛꑜê‘ꑞꑟꑠꑡꑢꑣꑤꑥꑦꑧꑨꑩꑪꑫꑬꑭꑮꑯꑰꑱꑲꑳꑴꑵꑶꑷꑸꑹꑺꑻꑼꑽꑾꑿꒀê’ꒂꒃꒄꒅꒆꒇꒈꒉꒊꒋꒌ -ê’꒑꒒꒓꒔꒕꒖꒗꒘꒙꒚꒛꒜ê’꒞꒟꒠꒡꒢꒣꒤꒥꒦꒧꒨꒩꒪꒫꒬꒭꒮꒯꒰꒱꒲꒳꒴꒵꒶꒷꒸꒹꒺꒻꒼꒽꒾꒿꓀ê“꓂꓃꓄꓅꓆ -ê“ꓑꓒꓓꓔꓕꓖꓗꓘꓙꓚꓛꓜê“ꓞꓟꓠꓡꓢꓣꓤꓥꓦꓧꓨꓩꓪꓫꓬꓭꓮꓯꓰꓱꓲꓳꓴꓵꓶꓷꓸꓹꓺꓻꓼꓽ -꓾꓿ -ꔀê”ꔂꔃꔄꔅꔆꔇꔈꔉꔊꔋꔌê”ꔎê”ê”ꔑꔒꔓꔔꔕꔖꔗꔘꔙꔚꔛꔜê”ꔞꔟꔠꔡꔢꔣꔤꔥꔦꔧꔨꔩꔪꔫꔬꔭꔮꔯꔰꔱꔲꔳꔴꔵꔶꔷꔸꔹꔺꔻꔼꔽꔾꔿꕀê•ê•‚ꕃꕄꕅꕆꕇꕈꕉꕊꕋꕌê•ê•Žê•ê•ê•‘ꕒꕓꕔꕕꕖꕗꕘꕙꕚꕛꕜê•ê•žê•Ÿê• ê•¡ê•¢ê•£ê•¤ê•¥ê•¦ê•§ê•¨ê•©ê•ªê•«ê•¬ê•­ê•®ê•¯ê•°ê•±ê•²ê•³ê•´ê•µê•¶ê•·ê•¸ê•¹ê•ºê•»ê•¼ê•½ê•¾ê•¿ê–€ê–ꖂꖃꖄꖅꖆꖇꖈꖉꖊꖋꖌê–ê–Žê–ê–ꖑꖒꖓꖔꖕꖖꖗꖘꖙꖚꖛꖜê–ꖞꖟꖠꖡꖢꖣꖤꖥꖦꖧꖨꖩꖪꖫꖬꖭꖮꖯꖰꖱꖲꖳꖴꖵꖶꖷꖸꖹꖺꖻꖼꖽꖾꖿꗀê—ꗂꗃꗄꗅꗆꗇꗈꗉꗊꗋꗌê—ê—Žê—ê—ꗑꗒꗓꗔꗕꗖꗗꗘꗙꗚꗛꗜê—ꗞꗟꗠꗡꗢꗣꗤꗥꗦꗧꗨꗩꗪꗫꗬꗭꗮꗯꗰꗱꗲꗳꗴꗵꗶꗷꗸꗹꗺꗻꗼꗽꗾꗿꘀê˜ê˜‚ꘃꘄꘅꘆꘇꘈꘉꘊꘋꘌ -ê˜ê˜Žê˜ -ê˜ê˜‘ꘒꘓꘔꘕꘖꘗꘘꘙꘚꘛꘜê˜ê˜žê˜Ÿê˜ ê˜¡ê˜¢ê˜£ê˜¤ê˜¥ê˜¦ê˜§ê˜¨ê˜©ê˜ªê˜«ê™€ê™ê™‚ꙃꙄꙅꙆꙇꙈꙉꙊꙋꙌê™ê™Žê™ê™ê™‘ꙒꙓꙔꙕꙖꙗꙘꙙꙚꙛꙜê™ê™žê™Ÿê™¢ê™£ê™¤ê™¥ê™¦ê™§ê™¨ê™©ê™ªê™«ê™¬ê™­ê™®ê™¯ê™°ê™±ê™² -꙳ -꙼꙽ -꙾ -ꙿꚀêšêš‚ꚃꚄꚅꚆꚇꚈꚉꚊꚋꚌêšêšŽêšêšêš‘ꚒꚓꚔꚕꚖꚗꚠꚡꚢꚣꚤꚥꚦꚧꚨꚩꚪꚫꚬꚭꚮꚯꚰꚱꚲꚳꚴꚵꚶꚷꚸꚹꚺꚻꚼꚽꚾꚿꛀê›ê›‚ꛃꛄꛅꛆꛇꛈꛉꛊꛋꛌê›ê›Žê›ê›ê›‘ꛒꛓꛔꛕꛖꛗꛘꛙꛚꛛꛜê›ê›žê›Ÿê› ê›¡ê›¢ê›£ê›¤ê›¥ê›¦ê›§ê›¨ê›©ê›ªê›«ê›¬ê›­ê›®ê›¯ê›°ê›± -꛲꛳꛴꛵꛶꛷꜀êœêœ‚꜃꜄꜅꜆꜇꜈꜉꜊꜋꜌êœêœŽêœêœêœ‘꜒꜓꜔꜕꜖ -ꜗꜘꜙꜚꜛꜜêœêœžêœŸ -꜠꜡ -ꜢꜣꜤꜥꜦꜧꜨꜩꜪꜫꜬꜭꜮꜯꜰꜱꜲꜳꜴꜵꜶꜷꜸꜹꜺꜻꜼꜽꜾꜿê€êê‚êƒê„ê…ê†ê‡êˆê‰êŠê‹êŒêêŽêêê‘ê’ê“ê”ê•ê–ê—ê˜ê™êšê›êœêêžêŸê ê¡ê¢ê£ê¤ê¥ê¦ê§ê¨ê©êªê«ê¬ê­ê®ê¯ê°ê±ê²ê³ê´êµê¶ê·ê¸ê¹êºê»ê¼ê½ê¾ê¿êž€êžêž‚ꞃꞄꞅꞆꞇꞈ -꞉꞊ -Ꞌꞌꟻꟼꟽꟾꟿꠀê ê ‚ꠃꠄꠅ꠆ꠇꠈꠉꠊꠋꠌê ê Žê ê ê ‘ꠒꠓꠔꠕꠖꠗꠘꠙꠚꠛꠜê ê žê Ÿê  ê ¡ê ¢ê £ê ¤ê ¥ê ¦ê § -꠨꠩꠪꠫ -꠰꠱꠲꠳꠴꠵ -꠶꠷꠸꠹ -ê¡€ê¡ê¡‚ꡃꡄꡅꡆꡇꡈꡉꡊꡋꡌê¡ê¡Žê¡ê¡ê¡‘ꡒꡓꡔꡕꡖꡗꡘꡙꡚꡛꡜê¡ê¡žê¡Ÿê¡ ê¡¡ê¡¢ê¡£ê¡¤ê¡¥ê¡¦ê¡§ê¡¨ê¡©ê¡ªê¡«ê¡¬ê¡­ê¡®ê¡¯ê¡°ê¡±ê¡²ê¡³ -꡴꡵꡶꡷ -ꢀê¢ê¢‚ꢃꢄꢅꢆꢇꢈꢉꢊꢋꢌê¢ê¢Žê¢ê¢ê¢‘ꢒꢓꢔꢕꢖꢗꢘꢙꢚꢛꢜê¢ê¢žê¢Ÿê¢ ê¢¡ê¢¢ê¢£ê¢¤ê¢¥ê¢¦ê¢§ê¢¨ê¢©ê¢ªê¢«ê¢¬ê¢­ê¢®ê¢¯ê¢°ê¢±ê¢²ê¢³ê¢´ê¢µê¢¶ê¢·ê¢¸ê¢¹ê¢ºê¢»ê¢¼ê¢½ê¢¾ê¢¿ê£€ê£ê£‚ꣃ꣄ -ê£Žê£ -ê£ê£‘꣒꣓꣔꣕꣖꣗꣘꣙꣠꣡꣢꣣꣤꣥꣦꣧꣨꣩꣪꣫꣬꣭꣮꣯꣰꣱ꣲꣳꣴꣵꣶꣷ -꣸꣹꣺ -ꣻ꤀ê¤ê¤‚꤃꤄꤅꤆꤇꤈꤉ꤊꤋꤌê¤ê¤Žê¤ê¤ê¤‘ꤒꤓꤔꤕꤖꤗꤘꤙꤚꤛꤜê¤ê¤žê¤Ÿê¤ ê¤¡ê¤¢ê¤£ê¤¤ê¤¥ê¤¦ê¤§ê¤¨ê¤©ê¤ªê¤«ê¤¬ê¤­ -꤮꤯ -ꤰꤱꤲꤳꤴꤵꤶꤷꤸꤹꤺꤻꤼꤽꤾꤿꥀê¥ê¥‚ꥃꥄꥅꥆꥇꥈꥉꥊꥋꥌê¥ê¥Žê¥ê¥ê¥‘ꥒ꥓ -꥟ -ꥠꥡꥢꥣꥤꥥꥦꥧꥨꥩꥪꥫꥬꥭꥮꥯꥰꥱꥲꥳꥴꥵꥶꥷꥸꥹꥺꥻꥼꦀê¦ê¦‚ꦃꦄꦅꦆꦇꦈꦉꦊꦋꦌê¦ê¦Žê¦ê¦ê¦‘ꦒꦓꦔꦕꦖꦗꦘꦙꦚꦛꦜê¦ê¦žê¦Ÿê¦ ê¦¡ê¦¢ê¦£ê¦¤ê¦¥ê¦¦ê¦§ê¦¨ê¦©ê¦ªê¦«ê¦¬ê¦­ê¦®ê¦¯ê¦°ê¦±ê¦²ê¦³ê¦´ê¦µê¦¶ê¦·ê¦¸ê¦¹ê¦ºê¦»ê¦¼ê¦½ê¦¾ê¦¿ê§€ -ê§ê§‚ê§ƒê§„ê§…ê§†ê§‡ê§ˆê§‰ê§Šê§‹ê§Œê§ -ê§ê§ê§‘꧒꧓꧔꧕꧖꧗꧘꧙ -꧞꧟ -ꨀê¨ê¨‚ꨃꨄꨅꨆꨇꨈꨉꨊꨋꨌê¨ê¨Žê¨ê¨ê¨‘ꨒꨓꨔꨕꨖꨗꨘꨙꨚꨛꨜê¨ê¨žê¨Ÿê¨ ê¨¡ê¨¢ê¨£ê¨¤ê¨¥ê¨¦ê¨§ê¨¨ê¨©ê¨ªê¨«ê¨¬ê¨­ê¨®ê¨¯ê¨°ê¨±ê¨²ê¨³ê¨´ê¨µê¨¶ê©€ê©ê©‚ꩃꩄꩅꩆꩇꩈꩉꩊꩋꩌê©ê©ê©‘꩒꩓꩔꩕꩖꩗꩘꩙ -ê©œê©ê©žê©Ÿ -ꩠꩡꩢꩣꩤꩥꩦꩧꩨꩩꩪꩫꩬꩭꩮꩯꩰꩱꩲꩳꩴꩵꩶ -꩷꩸꩹ -ꩺꩻꪀêªêª‚ꪃꪄꪅꪆꪇꪈꪉꪊꪋꪌêªêªŽêªêªêª‘ꪒꪓꪔꪕꪖꪗꪘꪙꪚꪛꪜêªêªžêªŸêª êª¡êª¢êª£êª¤êª¥êª¦êª§êª¨êª©êªªêª«êª¬êª­êª®êª¯êª°êª±êª²êª³êª´êªµêª¶êª·êª¸êª¹êªºêª»êª¼êª½êª¾êª¿ê«€ê«ê«‚ê«›ê«œê« -ê«žê«Ÿ -ꯀê¯ê¯‚ꯃꯄꯅꯆꯇꯈꯉꯊꯋꯌê¯ê¯Žê¯ê¯ê¯‘ꯒꯓꯔꯕꯖꯗꯘꯙꯚꯛꯜê¯ê¯žê¯Ÿê¯ ê¯¡ê¯¢ê¯£ê¯¤ê¯¥ê¯¦ê¯§ê¯¨ê¯©ê¯ª -꯫ -꯬꯭꯰꯱꯲꯳꯴꯵꯶꯷꯸꯹가힣ힰힱힲힳힴힵힶힷힸힹힺힻힼힽힾힿퟀíŸíŸ‚ퟃퟄퟅퟆퟋퟌíŸíŸŽíŸíŸíŸ‘ퟒퟓퟔퟕퟖퟗퟘퟙퟚퟛퟜíŸíŸžíŸŸíŸ íŸ¡íŸ¢íŸ£íŸ¤íŸ¥íŸ¦íŸ§íŸ¨íŸ©íŸªíŸ«íŸ¬íŸ­íŸ®íŸ¯íŸ°íŸ±íŸ²íŸ³íŸ´íŸµíŸ¶íŸ·íŸ¸íŸ¹íŸºíŸ» - -豈ï¤ï¤‚賈滑串句龜龜契金喇奈ï¤ï¤Žï¤ï¤ï¤‘裸邏樂洛烙珞落酪駱亂卵ï¤ï¤žï¤Ÿï¤ ï¤¡ï¤¢ï¤£ï¤¤ï¤¥ï¤¦ï¤§ï¤¨ï¤©ï¤ªï¤«ï¤¬ï¤­ï¤®ï¤¯ï¤°ï¤±ï¤²ï¤³ï¤´ï¤µï¤¶ï¤·ï¤¸ï¤¹ï¤ºï¤»ï¤¼ï¤½ï¤¾ï¤¿ï¥€ï¥ï¥‚弄籠聾牢磊賂雷壘屢樓ï¥ï¥Žï¥ï¥ï¥‘勒肋凜凌稜綾菱陵讀拏樂ï¥ï¥žï¥Ÿï¥ ï¥¡ï¥¢ï¥£ï¥¤ï¥¥ï¥¦ï¥§ï¥¨ï¥©ï¥ªï¥«ï¥¬ï¥­ï¥®ï¥¯ï¥°ï¥±ï¥²ï¥³ï¥´ï¥µï¥¶ï¥·ï¥¸ï¥¹ï¥ºï¥»ï¥¼ï¥½ï¥¾ï¥¿ï¦€ï¦ï¦‚旅濾礪閭驪麗黎力曆歷ï¦ï¦Žï¦ï¦ï¦‘漣煉璉秊練聯輦蓮連鍊列ï¦ï¦žï¦Ÿï¦ ï¦¡ï¦¢ï¦£ï¦¤ï¦¥ï¦¦ï¦§ï¦¨ï¦©ï¦ªï¦«ï¦¬ï¦­ï¦®ï¦¯ï¦°ï¦±ï¦²ï¦³ï¦´ï¦µï¦¶ï¦·ï¦¸ï¦¹ï¦ºï¦»ï¦¼ï¦½ï¦¾ï¦¿ï§€ï§ï§‚遼龍暈阮劉杻柳流溜琉ï§ï§Žï§ï§ï§‘戮陸倫崙淪輪律慄栗率隆ï§ï§žï§Ÿï§ ï§¡ï§¢ï§£ï§¤ï§¥ï§¦ï§§ï§¨ï§©ï§ªï§«ï§¬ï§­ï§®ï§¯ï§°ï§±ï§²ï§³ï§´ï§µï§¶ï§·ï§¸ï§¹ï§ºï§»ï§¼ï§½ï§¾ï§¿ï¨€ï¨ï¨‚糖宅洞暴輻行降見廓兀ï¨ï¨Žï¨ï¨ï¨‘晴﨓﨔凞猪益礼神祥福靖ï¨ï¨žï¨Ÿï¨ ï¨¡ï¨¢ï¨£ï¨¤ï¨¥ï¨¦ï¨§ï¨¨ï¨©ï¨ªï¨«ï¨¬ï¨­ï¨°ï¨±ï¨²ï¨³ï¨´ï¨µï¨¶ï¨·ï¨¸ï¨¹ï¨ºï¨»ï¨¼ï¨½ï¨¾ï¨¿ï©€ï©ï©‚暑梅海渚漢煮爫琢碑社ï©ï©Žï©ï©ï©‘禍禎穀突節練縉繁署者臭ï©ï©žï©Ÿï© ï©¡ï©¢ï©£ï©¤ï©¥ï©¦ï©§ï©¨ï©©ï©ªï©«ï©¬ï©­ï©°ï©±ï©²ï©³ï©´ï©µï©¶ï©·ï©¸ï©¹ï©ºï©»ï©¼ï©½ï©¾ï©¿ïª€ïªïª‚廙彩徭惘慎愈憎慠懲戴ïªïªŽïªïªïª‘朗望杖歹殺流滛滋漢瀞煮ïªïªžïªŸïª ïª¡ïª¢ïª£ïª¤ïª¥ïª¦ïª§ïª¨ïª©ïªªïª«ïª¬ïª­ïª®ïª¯ïª°ïª±ïª²ïª³ïª´ïªµïª¶ïª·ïª¸ïª¹ïªºïª»ïª¼ïª½ïª¾ïª¿ï«€ï«ï«‚遲醙鉶陼難靖韛響頋頻ï«ï«Žï«ï«ï«‘㮝䀘䀹𥉉𥳐𧻓齃龎ffï¬ï¬‚ffifflſtstﬓﬔﬕﬖﬗï¬ï¬žï¬Ÿï¬ ï¬¡ï¬¢ï¬£ï¬¤ï¬¥ï¬¦ï¬§ï¬¨ -﬩ -שׁשׂשּׁשּׂאַאָאּבּגּדּהּוּזּטּיּךּכּלּמּנּï­ï­ƒï­„צּקּרּשּתּוֹבֿï­ï­Žï­ï­ï­‘ﭒﭓﭔﭕﭖﭗﭘﭙﭚﭛﭜï­ï­žï­Ÿï­ ï­¡ï­¢ï­£ï­¤ï­¥ï­¦ï­§ï­¨ï­©ï­ªï­«ï­¬ï­­ï­®ï­¯ï­°ï­±ï­²ï­³ï­´ï­µï­¶ï­·ï­¸ï­¹ï­ºï­»ï­¼ï­½ï­¾ï­¿ï®€ï®ï®‚ﮃﮄﮅﮆﮇﮈﮉﮊﮋﮌï®ï®Žï®ï®ï®‘ﮒﮓﮔﮕﮖﮗﮘﮙﮚﮛﮜï®ï®žï®Ÿï® ï®¡ï®¢ï®£ï®¤ï®¥ï®¦ï®§ï®¨ï®©ï®ªï®«ï®¬ï®­ï®®ï®¯ï®°ï®±ï¯“ﯔﯕﯖﯗﯘﯙﯚﯛﯜï¯ï¯žï¯Ÿï¯ ï¯¡ï¯¢ï¯£ï¯¤ï¯¥ï¯¦ï¯§ï¯¨ï¯©ï¯ªï¯«ï¯¬ï¯­ï¯®ï¯¯ï¯°ï¯±ï¯²ï¯³ï¯´ï¯µï¯¶ï¯·ï¯¸ï¯¹ï¯ºï¯»ï¯¼ï¯½ï¯¾ï¯¿ï°€ï°ï°‚ﰃﰄﰅﰆﰇﰈﰉﰊﰋﰌï°ï°Žï°ï°ï°‘ﰒﰓﰔﰕﰖﰗﰘﰙﰚﰛﰜï°ï°žï°Ÿï° ï°¡ï°¢ï°£ï°¤ï°¥ï°¦ï°§ï°¨ï°©ï°ªï°«ï°¬ï°­ï°®ï°¯ï°°ï°±ï°²ï°³ï°´ï°µï°¶ï°·ï°¸ï°¹ï°ºï°»ï°¼ï°½ï°¾ï°¿ï±€ï±ï±‚ﱃﱄﱅﱆﱇﱈﱉﱊﱋﱌï±ï±Žï±ï±ï±‘ﱒﱓﱔﱕﱖﱗﱘﱙﱚﱛﱜï±ï±žï±Ÿï± ï±¡ï±¢ï±£ï±¤ï±¥ï±¦ï±§ï±¨ï±©ï±ªï±«ï±¬ï±­ï±®ï±¯ï±°ï±±ï±²ï±³ï±´ï±µï±¶ï±·ï±¸ï±¹ï±ºï±»ï±¼ï±½ï±¾ï±¿ï²€ï²ï²‚ﲃﲄﲅﲆﲇﲈﲉﲊﲋﲌï²ï²Žï²ï²ï²‘ﲒﲓﲔﲕﲖﲗﲘﲙﲚﲛﲜï²ï²žï²Ÿï² ï²¡ï²¢ï²£ï²¤ï²¥ï²¦ï²§ï²¨ï²©ï²ªï²«ï²¬ï²­ï²®ï²¯ï²°ï²±ï²²ï²³ï²´ï²µï²¶ï²·ï²¸ï²¹ï²ºï²»ï²¼ï²½ï²¾ï²¿ï³€ï³ï³‚ﳃﳄﳅﳆﳇﳈﳉﳊﳋﳌï³ï³Žï³ï³ï³‘ﳒﳓﳔﳕﳖﳗﳘﳙﳚﳛﳜï³ï³žï³Ÿï³ ï³¡ï³¢ï³£ï³¤ï³¥ï³¦ï³§ï³¨ï³©ï³ªï³«ï³¬ï³­ï³®ï³¯ï³°ï³±ï³²ï³³ï³´ï³µï³¶ï³·ï³¸ï³¹ï³ºï³»ï³¼ï³½ï³¾ï³¿ï´€ï´ï´‚ﴃﴄﴅﴆﴇﴈﴉﴊﴋﴌï´ï´Žï´ï´ï´‘ﴒﴓﴔﴕﴖﴗﴘﴙﴚﴛﴜï´ï´žï´Ÿï´ ï´¡ï´¢ï´£ï´¤ï´¥ï´¦ï´§ï´¨ï´©ï´ªï´«ï´¬ï´­ï´®ï´¯ï´°ï´±ï´²ï´³ï´´ï´µï´¶ï´·ï´¸ï´¹ï´ºï´»ï´¼ï´½ -﴾﴿ -ïµïµ‘ﵒﵓﵔﵕﵖﵗﵘﵙﵚﵛﵜïµïµžïµŸïµ ïµ¡ïµ¢ïµ£ïµ¤ïµ¥ïµ¦ïµ§ïµ¨ïµ©ïµªïµ«ïµ¬ïµ­ïµ®ïµ¯ïµ°ïµ±ïµ²ïµ³ïµ´ïµµïµ¶ïµ·ïµ¸ïµ¹ïµºïµ»ïµ¼ïµ½ïµ¾ïµ¿ï¶€ï¶ï¶‚ﶃﶄﶅﶆﶇﶈﶉﶊﶋﶌï¶ï¶Žï¶ï¶’ﶓﶔﶕﶖﶗﶘﶙﶚﶛﶜï¶ï¶žï¶Ÿï¶ ï¶¡ï¶¢ï¶£ï¶¤ï¶¥ï¶¦ï¶§ï¶¨ï¶©ï¶ªï¶«ï¶¬ï¶­ï¶®ï¶¯ï¶°ï¶±ï¶²ï¶³ï¶´ï¶µï¶¶ï¶·ï¶¸ï¶¹ï¶ºï¶»ï¶¼ï¶½ï¶¾ï¶¿ï·€ï·ï·‚ﷃﷄﷅﷆﷇﷰﷱﷲﷳﷴﷵﷶﷷﷸﷹﷺﷻ -﷼﷽ -︀ï¸ï¸‚︃︄︅︆︇︈︉︊︋︌ï¸ï¸Žï¸ -ï¸ï¸‘︒︓︔︕︖︗︘︙ -︠︡︢︣︤︥︦ -︰︱︲︳︴︵︶︷︸︹︺︻︼︽︾︿﹀ï¹ï¹‚﹃﹄﹅﹆﹇﹈﹉﹊﹋﹌ï¹ï¹Žï¹ï¹ï¹‘﹒﹔﹕﹖﹗﹘﹙﹚﹛﹜ï¹ï¹žï¹Ÿï¹ ï¹¡ï¹¢ï¹£ï¹¤ï¹¥ï¹¦ï¹¨ï¹©ï¹ªï¹« -ﹰﹱﹲﹳﹴﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀïºïº‚ﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌïºïºŽïºïºïº‘ﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜïºïºžïºŸïº ïº¡ïº¢ïº£ïº¤ïº¥ïº¦ïº§ïº¨ïº©ïºªïº«ïº¬ïº­ïº®ïº¯ïº°ïº±ïº²ïº³ïº´ïºµïº¶ïº·ïº¸ïº¹ïººïº»ïº¼ïº½ïº¾ïº¿ï»€ï»ï»‚ﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌï»ï»Žï»ï»ï»‘ﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜï»ï»žï»Ÿï» ï»¡ï»¢ï»£ï»¤ï»¥ï»¦ï»§ï»¨ï»©ï»ªï»«ï»¬ï»­ï»®ï»¯ï»°ï»±ï»²ï»³ï»´ï»µï»¶ï»·ï»¸ï»¹ï»ºï»»ï»¼ -ï¼ï¼‚#$%&'()*+,ï¼ï¼Žï¼ -ï¼ï¼‘23456789 -:;<ï¼ï¼žï¼Ÿï¼  -ABCDEFGHIJKLMNOPQRSTUVWXYZ -[\]^_` -ï½ï½‚cdefghijklï½ï½Žï½ï½ï½‘rstuvwxyz -{|ï½ï½žï½Ÿï½ ï½¡ï½¢ï½£ï½¤ï½¥ -ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタï¾ï¾‚テトナニヌネノハヒフï¾ï¾Žï¾ï¾ï¾‘メモヤユヨラリルレロワï¾ï¾žï¾Ÿï¾ ï¾¡ï¾¢ï¾£ï¾¤ï¾¥ï¾¦ï¾§ï¾¨ï¾©ï¾ªï¾«ï¾¬ï¾­ï¾®ï¾¯ï¾°ï¾±ï¾²ï¾³ï¾´ï¾µï¾¶ï¾·ï¾¸ï¾¹ï¾ºï¾»ï¾¼ï¾½ï¾¾ï¿‚ᅢᅣᅤᅥᅦᅧᅨᅩï¿ï¿Žï¿ï¿’ᅮᅯᅰᅱᅲᅳᅴᅵ -¢£¬ ̄¦¥₩│←↑→↓■○� -ð€€ \ No newline at end of file diff --git a/modules/search/tests/search_embedded_form.info b/modules/search/tests/search_embedded_form.info deleted file mode 100644 index 02e4c1f..0000000 --- a/modules/search/tests/search_embedded_form.info +++ /dev/null @@ -1,6 +0,0 @@ -name = "Search embedded form" -description = "Support module for search module testing of embedded forms." -package = Testing -version = VERSION -core = 7.x -hidden = TRUE diff --git a/modules/search/tests/search_embedded_form.module b/modules/search/tests/search_embedded_form.module deleted file mode 100644 index 4845796..0000000 --- a/modules/search/tests/search_embedded_form.module +++ /dev/null @@ -1,70 +0,0 @@ - 'Search_Embed_Form', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('search_embedded_form_form'), - 'access arguments' => array('search content'), - 'type' => MENU_CALLBACK, - ); - - return $items; -} - -/** - * Builds a form for embedding in search results for testing. - * - * @see search_embedded_form_form_submit(). - */ -function search_embedded_form_form($form, &$form_state) { - $count = variable_get('search_embedded_form_submitted', 0); - - $form['name'] = array( - '#type' => 'textfield', - '#title' => t('Your name'), - '#maxlength' => 255, - '#default_value' => '', - '#required' => TRUE, - '#description' => t('Times form has been submitted: %count', array('%count' => $count)), - ); - - $form['actions'] = array('#type' => 'actions'); - $form['actions']['submit'] = array( - '#type' => 'submit', - '#value' => t('Send away'), - ); - - $form['#submit'][] = 'search_embedded_form_form_submit'; - - return $form; -} - -/** - * Submit handler for search_embedded_form_form(). - */ -function search_embedded_form_form_submit($form, &$form_state) { - $count = variable_get('search_embedded_form_submitted', 0) + 1; - variable_set('search_embedded_form_submitted', $count); - drupal_set_message(t('Test form was submitted')); -} - -/** - * Adds the test form to search results. - */ -function search_embedded_form_preprocess_search_result(&$variables) { - $form = drupal_get_form('search_embedded_form_form'); - $variables['snippet'] .= drupal_render($form); -} diff --git a/modules/search/tests/search_extra_type.info b/modules/search/tests/search_extra_type.info deleted file mode 100644 index 5c3293c..0000000 --- a/modules/search/tests/search_extra_type.info +++ /dev/null @@ -1,6 +0,0 @@ -name = "Test search type" -description = "Support module for search module testing." -package = Testing -version = VERSION -core = 7.x -hidden = TRUE diff --git a/modules/search/tests/search_extra_type.module b/modules/search/tests/search_extra_type.module deleted file mode 100644 index 80c050c..0000000 --- a/modules/search/tests/search_extra_type.module +++ /dev/null @@ -1,69 +0,0 @@ - 'Dummy search type', - 'path' => 'dummy_path', - 'conditions_callback' => 'search_extra_type_conditions', - ); -} - -/** - * Test conditions callback for hook_search_info(). - */ -function search_extra_type_conditions() { - $conditions = array(); - - if (!empty($_REQUEST['search_conditions'])) { - $conditions['search_conditions'] = $_REQUEST['search_conditions']; - } - return $conditions; -} - -/** - * Implements hook_search_execute(). - * - * This is a dummy search, so when search "executes", we just return a dummy - * result containing the keywords and a list of conditions. - */ -function search_extra_type_search_execute($keys = NULL, $conditions = NULL) { - if (!$keys) { - $keys = ''; - } - return array( - array( - 'link' => url('node'), - 'type' => 'Dummy result type', - 'title' => 'Dummy title', - 'snippet' => "Dummy search snippet to display. Keywords: {$keys}\n\nConditions: " . print_r($conditions, TRUE), - ), - ); -} - -/** - * Implements hook_search_page(). - * - * Adds some text to the search page so we can verify that it runs. - */ -function search_extra_type_search_page($results) { - $output['prefix']['#markup'] = '

    Test page text is here

      '; - - foreach ($results as $entry) { - $output[] = array( - '#theme' => 'search_result', - '#result' => $entry, - '#module' => 'search_extra_type', - ); - } - $output['suffix']['#markup'] = '
    ' . theme('pager'); - - return $output; -} diff --git a/modules/search/tests/search_node_tags.info b/modules/search/tests/search_node_tags.info deleted file mode 100644 index dfa7d53..0000000 --- a/modules/search/tests/search_node_tags.info +++ /dev/null @@ -1,6 +0,0 @@ -name = "Test search node tags" -description = "Support module for Node search tags testing." -package = Testing -version = VERSION -core = 7.x -hidden = TRUE diff --git a/modules/search/tests/search_node_tags.module b/modules/search/tests/search_node_tags.module deleted file mode 100644 index b66dd9e..0000000 --- a/modules/search/tests/search_node_tags.module +++ /dev/null @@ -1,23 +0,0 @@ -hasTag('search_node')) { - variable_set('search_node_tags_test_query_tag', TRUE); - } -} - -/** - * Implements hook_query_TAG_alter(). - */ -function search_node_tags_query_search_node_alter(QueryAlterableInterface $query) { - variable_set('search_node_tags_test_query_tag_hook', TRUE); -} diff --git a/modules/shortcut/shortcut-rtl.css b/modules/shortcut/shortcut-rtl.css deleted file mode 100644 index 5dec957..0000000 --- a/modules/shortcut/shortcut-rtl.css +++ /dev/null @@ -1,48 +0,0 @@ - -div#toolbar a#edit-shortcuts { - position: absolute; - left: 0; - top: 0; - padding: 5px 5px 5px 10px; -} -div#toolbar div.toolbar-shortcuts ul { - float: none; - margin-right: 5px; - margin-left: 10em; -} -div#toolbar div.toolbar-shortcuts ul li a { - margin-left: 5px; - margin-right: 0; - padding: 0 5px; -} -div#toolbar div.toolbar-shortcuts span.icon { - float: right; -} -div.add-or-remove-shortcuts a span.icon { - float: right; - margin-right: 8px; - margin-left: 0; -} -div.add-or-remove-shortcuts a span.text { - float: right; - padding-right: 10px; - padding-left: 0; -} -div.add-or-remove-shortcuts a:focus span.text, -div.add-or-remove-shortcuts a:hover span.text { - -moz-border-radius: 5px 0 0 5px; - -webkit-border-top-left-radius: 5px; - -webkit-border-bottom-left-radius: 5px; - border-radius: 5px 0 0 5px; - padding-left: 6px; -} -#shortcut-set-switch .form-item-new { - padding-right: 17px; - padding-left: 0; -} -div.add-shortcut a:hover span.icon { - background-position: 0 -24px; -} -div.remove-shortcut a:hover span.icon { - background-position: -12px -24px; -} diff --git a/modules/shortcut/shortcut.admin.css b/modules/shortcut/shortcut.admin.css deleted file mode 100644 index 8ca03be..0000000 --- a/modules/shortcut/shortcut.admin.css +++ /dev/null @@ -1,8 +0,0 @@ - -.shortcut-slot-hidden { - display: none; -} - -div.form-item-set div.form-item-new { - display: inline; -} diff --git a/modules/shortcut/shortcut.admin.inc b/modules/shortcut/shortcut.admin.inc deleted file mode 100644 index 2e8ddb4..0000000 --- a/modules/shortcut/shortcut.admin.inc +++ /dev/null @@ -1,788 +0,0 @@ - $set) { - $options[$name] = check_plain($set->title); - } - - // Only administrators can add shortcut sets. - $add_access = user_access('administer shortcuts'); - if ($add_access) { - $options['new'] = t('New set'); - } - - if (count($options) > 1) { - $form['account'] = array( - '#type' => 'value', - '#value' => $account, - ); - - $form['set'] = array( - '#type' => 'radios', - '#title' => $user->uid == $account->uid ? t('Choose a set of shortcuts to use') : t('Choose a set of shortcuts for this user'), - '#options' => $options, - '#default_value' => $current_set->set_name, - ); - - $form['new'] = array( - '#type' => 'textfield', - '#title' => t('Name'), - '#title_display' => 'invisible', - '#description' => t('The new set is created by copying items from your default shortcut set.'), - '#access' => $add_access, - ); - - if ($user->uid != $account->uid) { - $default_set = shortcut_default_set($account); - $form['new']['#description'] = t('The new set is created by copying items from the %default set.', array('%default' => $default_set->title)); - } - - $form['#attached'] = array( - 'css' => array(drupal_get_path('module', 'shortcut') . '/shortcut.admin.css'), - 'js' => array(drupal_get_path('module', 'shortcut') . '/shortcut.admin.js'), - ); - - $form['actions'] = array('#type' => 'actions'); - $form['actions']['submit'] = array( - '#type' => 'submit', - '#value' => t('Change set'), - ); - } - else { - // There is only 1 option, so output a message in the $form array. - $form['info'] = array( - '#markup' => '

    ' . t('You are currently using the %set-name shortcut set.', array('%set-name' => $current_set->title)) . '

    ', - ); - } - - return $form; -} - -/** - * Validation handler for shortcut_set_switch(). - */ -function shortcut_set_switch_validate($form, &$form_state) { - if ($form_state['values']['set'] == 'new') { - // Check to prevent creating a shortcut set with an empty title. - if (trim($form_state['values']['new']) == '') { - form_set_error('new', t('The new set name is required.')); - } - // Check to prevent a duplicate title. - if (shortcut_set_title_exists($form_state['values']['new'])) { - form_set_error('new', t('The shortcut set %name already exists. Choose another name.', array('%name' => $form_state['values']['new']))); - } - } -} - -/** - * Submit handler for shortcut_set_switch(). - */ -function shortcut_set_switch_submit($form, &$form_state) { - global $user; - $account = $form_state['values']['account']; - - if ($form_state['values']['set'] == 'new') { - // Save a new shortcut set with links copied from the user's default set. - $default_set = shortcut_default_set($account); - $set = (object) array( - 'title' => $form_state['values']['new'], - 'links' => menu_links_clone($default_set->links), - ); - shortcut_set_save($set); - $replacements = array( - '%user' => $account->name, - '%set_name' => $set->title, - '@switch-url' => url(current_path()), - ); - if ($account->uid == $user->uid) { - // Only administrators can create new shortcut sets, so we know they have - // access to switch back. - drupal_set_message(t('You are now using the new %set_name shortcut set. You can edit it from this page or switch back to a different one.', $replacements)); - } - else { - drupal_set_message(t('%user is now using a new shortcut set called %set_name. You can edit it from this page.', $replacements)); - } - $form_state['redirect'] = 'admin/config/user-interface/shortcut/' . $set->set_name; - } - else { - // Switch to a different shortcut set. - $set = shortcut_set_load($form_state['values']['set']); - $replacements = array( - '%user' => $account->name, - '%set_name' => $set->title, - ); - drupal_set_message($account->uid == $user->uid ? t('You are now using the %set_name shortcut set.', $replacements) : t('%user is now using the %set_name shortcut set.', $replacements)); - } - - // Assign the shortcut set to the provided user account. - shortcut_set_assign_user($set, $account); -} - -/** - * Menu page callback: builds the page for administering shortcut sets. - */ -function shortcut_set_admin() { - $shortcut_sets = shortcut_sets(); - $header = array(t('Name'), array('data' => t('Operations'), 'colspan' => 4)); - - $rows = array(); - foreach ($shortcut_sets as $set) { - $row = array( - check_plain($set->title), - l(t('list links'), "admin/config/user-interface/shortcut/$set->set_name"), - l(t('edit set name'), "admin/config/user-interface/shortcut/$set->set_name/edit"), - ); - if (shortcut_set_delete_access($set)) { - $row[] = l(t('delete set'), "admin/config/user-interface/shortcut/$set->set_name/delete"); - } - else { - $row[] = ''; - } - - $rows[] = $row; - } - - return theme('table', array('header' => $header, 'rows' => $rows)); -} - -/** - * Form callback: builds the form for adding a shortcut set. - * - * @param $form - * An associative array containing the structure of the form. - * @param $form_state - * An associative array containing the current state of the form. - * - * @return - * An array representing the form definition. - * - * @ingroup forms - * @see shortcut_set_add_form_validate() - * @see shortcut_set_add_form_submit() - */ -function shortcut_set_add_form($form, &$form_state) { - $form['new'] = array( - '#type' => 'textfield', - '#title' => t('Set name'), - '#description' => t('The new set is created by copying items from your default shortcut set.'), - '#required' => TRUE, - ); - - $form['actions'] = array('#type' => 'actions'); - $form['actions']['submit'] = array( - '#type' => 'submit', - '#value' => t('Create new set'), - ); - - return $form; -} - -/** - * Validation handler for shortcut_set_add_form(). - */ -function shortcut_set_add_form_validate($form, &$form_state) { - // Check to prevent a duplicate title. - if (shortcut_set_title_exists($form_state['values']['new'])) { - form_set_error('new', t('The shortcut set %name already exists. Choose another name.', array('%name' => $form_state['values']['new']))); - } -} - -/** - * Submit handler for shortcut_set_add_form(). - */ -function shortcut_set_add_form_submit($form, &$form_state) { - // Save a new shortcut set with links copied from the user's default set. - $default_set = shortcut_default_set(); - $set = (object) array( - 'title' => $form_state['values']['new'], - 'links' => menu_links_clone($default_set->links), - ); - shortcut_set_save($set); - drupal_set_message(t('The %set_name shortcut set has been created. You can edit it from this page.', array('%set_name' => $set->title))); - $form_state['redirect'] = 'admin/config/user-interface/shortcut/' . $set->set_name; -} - -/** - * Form callback: builds the form for customizing shortcut sets. - * - * @param $form - * An associative array containing the structure of the form. - * @param $form_state - * An associative array containing the current state of the form. - * @param $shortcut_set - * An object representing the shortcut set which is being edited. - * - * @return - * An array representing the form definition. - * - * @ingroup forms - * @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, - 'enabled' => array(), - 'disabled' => array(), - ); - - foreach ($shortcut_set->links as $link) { - $mlid = $link['mlid']; - $status = $link['hidden'] ? 'disabled' : 'enabled'; - $form['shortcuts'][$status][$mlid]['name']['#markup'] = l($link['link_title'], $link['link_path']); - $form['shortcuts'][$status][$mlid]['weight'] = array( - '#type' => 'weight', - '#title' => t('Weight'), - '#delta' => 50, - '#default_value' => $link['weight'], - '#attributes' => array('class' => array('shortcut-weight')), - ); - $form['shortcuts'][$status][$mlid]['status'] = array( - '#type' => 'select', - '#title' => t('Status'), - '#options' => array('disabled' => t('Disabled'), 'enabled' => t('Enabled')), - '#default_value' => $status, - '#attributes' => array('class' => array('shortcut-status-select')), - ); - - $form['shortcuts'][$status][$mlid]['edit']['#markup'] = l(t('edit'), 'admin/config/user-interface/shortcut/link/' . $mlid); - $form['shortcuts'][$status][$mlid]['delete']['#markup'] = l(t('delete'), 'admin/config/user-interface/shortcut/link/' . $mlid . '/delete'); - } - - $form['#attached'] = array( - 'css' => array(drupal_get_path('module', 'shortcut') . '/shortcut.admin.css'), - 'js' => array(drupal_get_path('module', 'shortcut') . '/shortcut.admin.js'), - ); - - $form['actions'] = array( - '#type' => 'actions', - '#access' => !empty($shortcut_set->links), - ); - $form['actions']['submit'] = array( - '#type' => 'submit', - '#value' => t('Save changes'), - ); - - return $form; -} - -/** - * Submit handler for shortcut_set_customize(). - */ -function shortcut_set_customize_submit($form, &$form_state) { - foreach ($form_state['values']['shortcuts'] as $group => $links) { - foreach ($links as $mlid => $data) { - $link = menu_link_load($mlid); - $link['hidden'] = $data['status'] == 'enabled' ? 0 : 1; - $link['weight'] = $data['weight']; - menu_link_save($link); - } - } - drupal_set_message(t('The shortcut set has been updated.')); -} - -/** - * Returns HTML for a shortcut set customization form. - * - * @param $variables - * An associative array containing: - * - form: A render element representing the form. - * - * @see shortcut_set_customize() - * @ingroup themeable - */ -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 ($statuses as $status) { - drupal_add_tabledrag('shortcuts', 'match', 'sibling', 'shortcut-status-select'); - drupal_add_tabledrag('shortcuts', 'order', 'sibling', 'shortcut-weight'); - $rows[] = array( - 'data' => array(array( - 'colspan' => 5, - 'data' => '' . $map[$status] . '', - )), - 'class' => array('shortcut-status', 'shortcut-status-' . $status), - ); - - foreach ($shortcuts_by_status[$status] as $key) { - $shortcut = &$form['shortcuts'][$status][$key]; - $row = array(); - $row[] = drupal_render($shortcut['name']); - $row[] = drupal_render($shortcut['weight']); - $row[] = drupal_render($shortcut['status']); - $row[] = drupal_render($shortcut['edit']); - $row[] = drupal_render($shortcut['delete']); - $rows[] = array( - 'data' => $row, - 'class' => array('draggable'), - ); - } - - if ($status == 'enabled') { - for ($i = 0; $i < shortcut_max_slots(); $i++) { - $rows['empty-' . $i] = array( - 'data' => array(array( - 'colspan' => 5, - 'data' => '' . t('Empty') . '', - )), - 'class' => array('shortcut-slot-empty'), - ); - } - $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'; - } - } - } - } - - $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'), '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; -} - -/** - * Form callback: builds the form for adding a new shortcut link. - * - * @param $form - * An associative array containing the structure of the form. - * @param $form_state - * An associative array containing the current state of the form. - * @param $shortcut_set - * An object representing the shortcut set to which the link will be added. - * - * @return - * An array representing the form definition. - * - * @ingroup forms - * @see shortcut_link_edit_validate() - * @see shortcut_link_add_submit() - */ -function shortcut_link_add($form, &$form_state, $shortcut_set) { - drupal_set_title(t('Add new shortcut')); - $form['shortcut_set'] = array( - '#type' => 'value', - '#value' => $shortcut_set, - ); - $form += _shortcut_link_form_elements(); - return $form; -} - -/** - * Form callback: builds the form for editing a shortcut link. - * - * @param $form - * An associative array containing the structure of the form. - * @param $form_state - * An associative array containing the current state of the form. - * @param $shortcut_link - * An array representing the link that is being edited. - * - * @return - * An array representing the form definition. - * - * @ingroup forms - * @see shortcut_link_edit_validate() - * @see shortcut_link_edit_submit() - */ -function shortcut_link_edit($form, &$form_state, $shortcut_link) { - drupal_set_title(t('Editing @shortcut', array('@shortcut' => $shortcut_link['link_title']))); - $form['original_shortcut_link'] = array( - '#type' => 'value', - '#value' => $shortcut_link, - ); - $form += _shortcut_link_form_elements($shortcut_link); - return $form; -} - -/** - * Helper function for building a form for adding or editing shortcut links. - * - * @param $shortcut_link - * (optional) An array representing the shortcut link that will be edited. If - * not provided, a new link will be created. - * - * @return - * An array of form elements. - */ -function _shortcut_link_form_elements($shortcut_link = NULL) { - if (!isset($shortcut_link)) { - $shortcut_link = array( - 'link_title' => '', - 'link_path' => '' - ); - } - else { - $shortcut_link['link_path'] = ($shortcut_link['link_path'] == '') ? '' : drupal_get_path_alias($shortcut_link['link_path']); - } - - $form['shortcut_link']['#tree'] = TRUE; - $form['shortcut_link']['link_title'] = array( - '#type' => 'textfield', - '#title' => t('Name'), - '#description' => t('The name of the shortcut.'), - '#size' => 40, - '#maxlength' => 255, - '#default_value' => $shortcut_link['link_title'], - '#required' => TRUE, - ); - - $form['shortcut_link']['link_path'] = array( - '#type' => 'textfield', - '#title' => t('Path'), - '#description' => t('The path to the shortcut.'), - '#size' => 40, - '#maxlength' => 255, - '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q='), - '#default_value' => $shortcut_link['link_path'], - ); - - $form['#validate'][] = 'shortcut_link_edit_validate'; - - $form['actions'] = array('#type' => 'actions'); - $form['actions']['submit'] = array( - '#type' => 'submit', - '#value' => t('Save'), - ); - - return $form; -} - -/** - * Validation handler for the shortcut link add and edit forms. - */ -function shortcut_link_edit_validate($form, &$form_state) { - if (!shortcut_valid_link($form_state['values']['shortcut_link']['link_path'])) { - form_set_error('shortcut_link][link_path', t('The link must correspond to a valid path on the site.')); - } -} - -/** - * Submit handler for shortcut_link_edit(). - */ -function shortcut_link_edit_submit($form, &$form_state) { - // Normalize the path in case it is an alias. - $shortcut_path = drupal_get_normal_path($form_state['values']['shortcut_link']['link_path']); - if (empty($shortcut_path)) { - $shortcut_path = ''; - } - $form_state['values']['shortcut_link']['link_path'] = $shortcut_path; - - $shortcut_link = array_merge($form_state['values']['original_shortcut_link'], $form_state['values']['shortcut_link']); - - menu_link_save($shortcut_link); - $form_state['redirect'] = 'admin/config/user-interface/shortcut/' . $shortcut_link['menu_name']; - drupal_set_message(t('The shortcut %link has been updated.', array('%link' => $shortcut_link['link_title']))); -} - -/** - * Submit handler for shortcut_link_add(). - */ -function shortcut_link_add_submit($form, &$form_state) { - // Add the shortcut link to the set. - $shortcut_set = $form_state['values']['shortcut_set']; - $shortcut_link = $form_state['values']['shortcut_link']; - $shortcut_link['menu_name'] = $shortcut_set->set_name; - shortcut_admin_add_link($shortcut_link, $shortcut_set, shortcut_max_slots()); - shortcut_set_save($shortcut_set); - $form_state['redirect'] = 'admin/config/user-interface/shortcut/' . $shortcut_link['menu_name']; - drupal_set_message(t('Added a shortcut for %title.', array('%title' => $shortcut_link['link_title']))); -} - -/** - * Adds a link to the end of a shortcut set, keeping within a prescribed limit. - * - * @param $link - * An array representing a shortcut link. - * @param $shortcut_set - * An object representing the shortcut set which the link will be added to. - * The links in the shortcut set will be re-weighted so that the new link is - * at the end, and some existing links may be disabled (if the $limit - * parameter is provided). - * @param $limit - * (optional) The maximum number of links that are allowed to be enabled for - * this shortcut set. If provided, existing links at the end of the list that - * exceed the limit will be automatically disabled. If not provided, no limit - * will be enforced. - */ -function shortcut_admin_add_link($shortcut_link, &$shortcut_set, $limit = NULL) { - if (isset($limit)) { - // Disable any existing links at the end of the list that would cause the - // limit to be exceeded. Take into account whether or not the new link will - // be enabled and count towards the total. - $number_enabled = !empty($shortcut_link['hidden']) ? 0 : 1; - foreach ($shortcut_set->links as &$link) { - if (!$link['hidden']) { - $number_enabled++; - if ($number_enabled > $limit) { - $link['hidden'] = 1; - } - } - } - } - - // Normalize the path in case it is an alias. - $shortcut_link['link_path'] = drupal_get_normal_path($shortcut_link['link_path']); - if (empty($shortcut_link['link_path'])) { - $shortcut_link['link_path'] = ''; - } - - // Add the link to the end of the list. - $shortcut_set->links[] = $shortcut_link; - shortcut_set_reset_link_weights($shortcut_set); -} - -/** - * Form callback: builds the form for editing the shortcut set name. - * - * @param $form - * An associative array containing the structure of the form. - * @param $form_state - * An associative array containing the current state of the form. - * @param object $shortcut_set - * An object representing the shortcut set, as returned from - * shortcut_set_load(). - * - * @return - * An array representing the form definition. - * - * @ingroup forms - * @see shortcut_set_edit_form_validate() - * @see shortcut_set_edit_form_submit() - */ -function shortcut_set_edit_form($form, &$form_state, $shortcut_set) { - $form['shortcut_set'] = array( - '#type' => 'value', - '#value' => $shortcut_set, - ); - $form['title'] = array( - '#type' => 'textfield', - '#title' => t('Set name'), - '#default_value' => $shortcut_set->title, - '#maxlength' => 255, - '#required' => TRUE, - '#weight' => -5, - ); - $form['actions'] = array('#type' => 'actions'); - $form['actions']['submit'] = array( - '#type' => 'submit', - '#value' => t('Save'), - '#weight' => 5, - ); - - return $form; -} - -/** - * Validation handler for shortcut_set_edit_form(). - */ -function shortcut_set_edit_form_validate($form, &$form_state) { - // Check to prevent a duplicate title, if the title was edited from its - // original value. - if ($form_state['values']['title'] != $form_state['values']['shortcut_set']->title && shortcut_set_title_exists($form_state['values']['title'])) { - form_set_error('title', t('The shortcut set %name already exists. Choose another name.', array('%name' => $form_state['values']['title']))); - } -} - -/** - * Submit handler for shortcut_set_edit_form(). - */ -function shortcut_set_edit_form_submit($form, &$form_state) { - $shortcut_set = $form_state['values']['shortcut_set']; - $shortcut_set->title = $form_state['values']['title']; - shortcut_set_save($shortcut_set); - drupal_set_message(t('Updated set name to %set-name.', array('%set-name' => $shortcut_set->title))); - $form_state['redirect'] = "admin/config/user-interface/shortcut/$shortcut_set->set_name"; -} - -/** - * Form callback: builds the confirmation form for deleting a shortcut set. - * - * @param $form - * An associative array containing the structure of the form. - * @param $form_state - * An associative array containing the current state of the form. - * @param object $shortcut_set - * An object representing the shortcut set, as returned from - * shortcut_set_load(). - * - * @return - * An array representing the form definition. - * - * @ingroup forms - * @see shortcut_set_delete_form_submit() - */ -function shortcut_set_delete_form($form, &$form_state, $shortcut_set) { - $form['shortcut_set'] = array( - '#type' => 'value', - '#value' => $shortcut_set->set_name, - ); - - // Find out how many users are directly assigned to this shortcut set, and - // make a message. - $number = db_query('SELECT COUNT(*) FROM {shortcut_set_users} WHERE set_name = :name', array(':name' => $shortcut_set->set_name))->fetchField(); - $info = ''; - if ($number) { - $info .= '

    ' . format_plural($number, - '1 user has chosen or been assigned to this shortcut set.', - '@count users have chosen or been assigned to this shortcut set.') . '

    '; - } - - // Also, if a module implements hook_shortcut_default_set(), it's possible - // that this set is being used as a default set. Add a message about that too. - if (count(module_implements('shortcut_default_set')) > 0) { - $info .= '

    ' . t('If you have chosen this shortcut set as the default for some or all users, they may also be affected by deleting it.') . '

    '; - } - - $form['info'] = array( - '#markup' => $info, - ); - - return confirm_form( - $form, - t('Are you sure you want to delete the shortcut set %title?', array('%title' => $shortcut_set->title)), - 'admin/config/user-interface/shortcut/' . $shortcut_set->set_name, - t('This action cannot be undone.'), - t('Delete'), - t('Cancel') - ); -} - -/** - * Submit handler for shortcut_set_delete_form(). - */ -function shortcut_set_delete_form_submit($form, &$form_state) { - $shortcut_set = shortcut_set_load($form_state['values']['shortcut_set']); - shortcut_set_delete($shortcut_set); - $form_state['redirect'] = 'admin/config/user-interface/shortcut'; - drupal_set_message(t('The shortcut set %title has been deleted.', array('%title' => $shortcut_set->title))); -} - -/** - * Form callback: builds the confirmation form for deleting a shortcut link. - * - * @param $form - * An associative array containing the structure of the form. - * @param $form_state - * An associative array containing the current state of the form. - * @param $shortcut_link - * An array representing the link that will be deleted. - * - * @return - * An array representing the form definition. - * - * @ingroup forms - * @see shortcut_link_delete_submit() - */ -function shortcut_link_delete($form, &$form_state, $shortcut_link) { - $form['shortcut_link'] = array( - '#type' => 'value', - '#value' => $shortcut_link, - ); - - return confirm_form( - $form, - t('Are you sure you want to delete the shortcut %title?', array('%title' => $shortcut_link['link_title'])), - 'admin/config/user-interface/shortcut/' . $shortcut_link['menu_name'], - t('This action cannot be undone.'), - t('Delete'), - t('Cancel') - ); -} - -/** - * Submit handler for shortcut_link_delete_submit(). - */ -function shortcut_link_delete_submit($form, &$form_state) { - $shortcut_link = $form_state['values']['shortcut_link']; - menu_link_delete($shortcut_link['mlid']); - $form_state['redirect'] = 'admin/config/user-interface/shortcut/' . $shortcut_link['menu_name']; - drupal_set_message(t('The shortcut %title has been deleted.', array('%title' => $shortcut_link['link_title']))); -} - -/** - * Menu page callback: creates a new link in the provided shortcut set. - * - * After completion, redirects the user back to where they came from. - * - * @param $shortcut_set - * Returned from shortcut_set_load(). - */ -function shortcut_link_add_inline($shortcut_set) { - if (isset($_REQUEST['token']) && drupal_valid_token($_REQUEST['token'], 'shortcut-add-link') && shortcut_valid_link($_GET['link'])) { - $item = menu_get_item($_GET['link']); - $title = ($item && $item['title']) ? $item['title'] : $_GET['name']; - $link = array( - 'link_title' => $title, - 'link_path' => $_GET['link'], - ); - shortcut_admin_add_link($link, $shortcut_set, shortcut_max_slots()); - if (shortcut_set_save($shortcut_set)) { - drupal_set_message(t('Added a shortcut for %title.', array('%title' => $link['link_title']))); - } - else { - drupal_set_message(t('Unable to add a shortcut for %title.', array('%title' => $link['link_title']))); - } - drupal_goto(); - } - - return MENU_ACCESS_DENIED; -} diff --git a/modules/shortcut/shortcut.admin.js b/modules/shortcut/shortcut.admin.js deleted file mode 100644 index 422cc4c..0000000 --- a/modules/shortcut/shortcut.admin.js +++ /dev/null @@ -1,115 +0,0 @@ -(function ($) { - -/** - * Handle the concept of a fixed number of slots. - * - * This behavior is dependent on the tableDrag behavior, since it uses the - * objects initialized in that behavior to update the row. - */ -Drupal.behaviors.shortcutDrag = { - attach: function (context, settings) { - if (Drupal.tableDrag) { - var table = $('table#shortcuts'), - visibleLength = 0, - slots = 0, - tableDrag = Drupal.tableDrag.shortcuts; - $('> tbody > tr, > tr', table) - .filter(':visible') - .filter(':odd').filter('.odd') - .removeClass('odd').addClass('even') - .end().end() - .filter(':even').filter('.even') - .removeClass('even').addClass('odd') - .end().end() - .end() - .filter('.shortcut-slot-empty').each(function(index) { - if ($(this).is(':visible')) { - visibleLength++; - } - slots++; - }); - - // Add a handler for when a row is swapped. - tableDrag.row.prototype.onSwap = function (swappedRow) { - var disabledIndex = $(table).find('tr').index($(table).find('tr.shortcut-status-disabled')) - slots - 2, - count = 0; - $(table).find('tr.shortcut-status-enabled').nextAll(':not(.shortcut-slot-empty)').each(function(index) { - if (index < disabledIndex) { - count++; - } - }); - var total = slots - count; - if (total == -1) { - var disabled = $(table).find('tr.shortcut-status-disabled'); - // To maintain the shortcut links limit, we need to move the last - // element from the enabled section to the disabled section. - var changedRow = disabled.prevAll(':not(.shortcut-slot-empty)').not($(this.element)).get(0); - disabled.after(changedRow); - if ($(changedRow).hasClass('draggable')) { - // The dropped element will automatically be marked as changed by - // the tableDrag system. However, the row that swapped with it - // has moved to the "disabled" section, so we need to force its - // status to be disabled and mark it also as changed. - var changedRowObject = new tableDrag.row(changedRow, 'mouse', false, 0, true); - changedRowObject.markChanged(); - tableDrag.rowStatusChange(changedRowObject); - } - } - else if (total != visibleLength) { - if (total > visibleLength) { - // Less slots on screen than needed. - $('.shortcut-slot-empty:hidden:last').show(); - visibleLength++; - } - else { - // More slots on screen than needed. - $('.shortcut-slot-empty:visible:last').hide(); - visibleLength--; - } - } - }; - - // Add a handler so when a row is dropped, update fields dropped into new regions. - tableDrag.onDrop = function () { - tableDrag.rowStatusChange(this.rowObject); - return true; - }; - - tableDrag.rowStatusChange = function (rowObject) { - // Use "status-message" row instead of "status" row because - // "status-{status_name}-message" is less prone to regexp match errors. - var statusRow = $(rowObject.element).prevAll('tr.shortcut-status').get(0); - var statusName = statusRow.className.replace(/([^ ]+[ ]+)*shortcut-status-([^ ]+)([ ]+[^ ]+)*/, '$2'); - var statusField = $('select.shortcut-status-select', rowObject.element); - statusField.val(statusName); - }; - - tableDrag.restripeTable = function () { - // :even and :odd are reversed because jQuery counts from 0 and - // we count from 1, so we're out of sync. - // Match immediate children of the parent element to allow nesting. - $('> tbody > tr:visible, > tr:visible', this.table) - .filter(':odd').filter('.odd') - .removeClass('odd').addClass('even') - .end().end() - .filter(':even').filter('.even') - .removeClass('even').addClass('odd'); - }; - } - } -}; - -/** - * Make it so when you enter text into the "New set" textfield, the - * corresponding radio button gets selected. - */ -Drupal.behaviors.newSet = { - attach: function (context, settings) { - var selectDefault = function() { - $(this).closest('form').find('.form-item-set .form-type-radio:last input').attr('checked', 'checked'); - }; - $('div.form-item-new input').focus(selectDefault).keyup(selectDefault); - } -}; - -})(jQuery); diff --git a/modules/shortcut/shortcut.api.php b/modules/shortcut/shortcut.api.php deleted file mode 100644 index 717a7c9..0000000 --- a/modules/shortcut/shortcut.api.php +++ /dev/null @@ -1,42 +0,0 @@ -roles)) { - return variable_get('mymodule_shortcut_admin_default_set'); - } -} - -/** - * @} End of "addtogroup hooks". - */ diff --git a/modules/shortcut/shortcut.css b/modules/shortcut/shortcut.css deleted file mode 100644 index 3afcb94..0000000 --- a/modules/shortcut/shortcut.css +++ /dev/null @@ -1,106 +0,0 @@ -div#toolbar a#edit-shortcuts { - float: right; - padding: 5px 10px 5px 5px; - line-height: 24px; - color: #fefefe; -} -div#toolbar a#edit-shortcuts:focus, -div#toolbar a#edit-shortcuts:hover, -div#toolbar a#edit-shortcuts.active { - color: #fff; - text-decoration: underline; -} - -div#toolbar div.toolbar-shortcuts ul { - padding: 5px 0 2px 0; - height: 28px; - line-height: 24px; - float: left; /* LTR */ - margin-left:5px; /* LTR */ -} - -div#toolbar div.toolbar-shortcuts ul li a { - padding: 0 5px 0 5px; - margin-right: 5px; /* LTR */ - -moz-border-radius: 5px; - -webkit-border-radius: 5px; - border-radius: 5px; -} - -div#toolbar div.toolbar-shortcuts ul li a:focus, -div#toolbar div.toolbar-shortcuts ul li a:hover, -div#toolbar div.toolbar-shortcuts ul li a.active:focus { - background: #555; -} - -div#toolbar div.toolbar-shortcuts ul li a.active:hover, -div#toolbar div.toolbar-shortcuts ul li a.active { - background: #000; -} - -div#toolbar div.toolbar-shortcuts span.icon { - float: left; /* LTR */ - background: #444; - width: 30px; - height: 30px; - margin-right: 5px; /* LTR */ - -moz-border-radius: 5px; - -webkit-border-radius: 5px; - border-radius: 5px; -} - -div.add-or-remove-shortcuts { - padding-top: 5px; -} - -div.add-or-remove-shortcuts a span.icon { - display: block; - width: 12px; - background: transparent url(shortcut.png) no-repeat scroll 0 0; - height: 12px; - float: left; - margin-left:8px; -} - -div.add-shortcut a:focus span.icon, -div.add-shortcut a:hover span.icon { - background-position: 0 -12px; -} -div.remove-shortcut a span.icon { - background-position: -12px 0; -} -div.remove-shortcut a:focus span.icon, -div.remove-shortcut a:hover span.icon { - background-position: -12px -12px; -} - -div.add-or-remove-shortcuts a span.text { - float: left; - padding-left:10px; - display: none; -} - -div.add-or-remove-shortcuts a:focus span.text, -div.add-or-remove-shortcuts a:hover span.text { - font-size: 10px; - line-height: 12px; - color: #fff; - background-color: #5f605b; - display: block; - padding-right: 6px; /* LTR */ - cursor: pointer; - -moz-border-radius: 0 5px 5px 0; /* LTR */ - -webkit-border-top-right-radius: 5px; /* LTR */ - -webkit-border-bottom-right-radius: 5px; /* LTR */ - border-radius: 0 5px 5px 0; /* LTR */ -} - -#shortcut-set-switch .form-type-radios { - padding-bottom: 0; - margin-bottom: 0; -} - -#shortcut-set-switch .form-item-new { - padding-top: 0; - padding-left: 17px; /* LTR */ -} diff --git a/modules/shortcut/shortcut.info b/modules/shortcut/shortcut.info deleted file mode 100644 index 8132b3c..0000000 --- a/modules/shortcut/shortcut.info +++ /dev/null @@ -1,7 +0,0 @@ -name = Shortcut -description = Allows users to manage customizable lists of shortcut links. -package = Core -version = VERSION -core = 7.x -files[] = shortcut.test -configure = admin/config/user-interface/shortcut diff --git a/modules/shortcut/shortcut.install b/modules/shortcut/shortcut.install deleted file mode 100644 index 60ee6be..0000000 --- a/modules/shortcut/shortcut.install +++ /dev/null @@ -1,115 +0,0 @@ -title = $t('Default'); - $shortcut_set->links = array( - array( - 'link_path' => 'node/add', - 'link_title' => $t('Add content'), - 'weight' => -20, - ), - array( - 'link_path' => 'admin/content', - 'link_title' => $t('Find content'), - 'weight' => -19, - ), - ); - // If Drupal is being installed, rebuild the menu before saving the shortcut - // set, to make sure the links defined above can be correctly saved. (During - // installation, the menu might not have been built at all yet, or it might - // have been built but without the node module's links in it.) - if (drupal_installation_attempted()) { - menu_rebuild(); - } - shortcut_set_save($shortcut_set); -} - -/** - * Implements hook_uninstall(). - */ -function shortcut_uninstall() { - drupal_load('module', 'shortcut'); - // Delete the menu links associated with each shortcut set. - foreach (shortcut_sets() as $shortcut_set) { - menu_delete_links($shortcut_set->set_name); - } -} - -/** - * Implements hook_schema(). - */ -function shortcut_schema() { - $schema['shortcut_set'] = array( - 'description' => 'Stores information about sets of shortcuts links.', - 'fields' => array( - 'set_name' => array( - 'type' => 'varchar', - 'length' => 32, - 'not null' => TRUE, - 'default' => '', - 'description' => "Primary Key: The {menu_links}.menu_name under which the set's links are stored.", - ), - 'title' => array( - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - 'description' => 'The title of the set.', - ), - ), - 'primary key' => array('set_name'), - 'foreign keys' => array( - 'menu_name' => array( - 'table' => 'menu_links', - 'columns' => array('set_name' => 'menu_name'), - ), - ), - ); - - $schema['shortcut_set_users'] = array( - 'description' => 'Maps users to shortcut sets.', - 'fields' => array( - 'uid' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - 'description' => 'The {users}.uid for this set.', - ), - 'set_name' => array( - 'type' => 'varchar', - 'length' => 32, - 'not null' => TRUE, - 'default' => '', - 'description' => "The {shortcut_set}.set_name that will be displayed for this user.", - ), - ), - 'primary key' => array('uid'), - 'indexes' => array( - 'set_name' => array('set_name'), - ), - 'foreign keys' => array( - 'set_user' => array( - 'table' => 'users', - 'columns' => array('uid' => 'uid'), - ), - 'set_name' => array( - 'table' => 'shortcut_set', - 'columns' => array('set_name' => 'set_name'), - ), - ), - ); - - return $schema; -} diff --git a/modules/shortcut/shortcut.module b/modules/shortcut/shortcut.module deleted file mode 100644 index 2f6db0a..0000000 --- a/modules/shortcut/shortcut.module +++ /dev/null @@ -1,767 +0,0 @@ -' . t('About') . ''; - $output .= '

    ' . t('The Shortcut module allows users to create sets of shortcut links to commonly-visited pages of the site. Shortcuts are contained within sets. Each user with Select any shortcut set permission can select a shortcut set created by anyone at the site. For more information, see the online handbook entry for Shortcut module.', array('@shortcut' => 'http://drupal.org/documentation/modules/shortcut/')) . '

    '; - $output .= '

    ' . t('Uses') . '

    '; - $output .= '
    ' . t('Administering shortcuts') . '
    '; - $output .= '
    ' . t('Users with the Administer shortcuts permission can manage shortcut sets and edit the shortcuts within sets from the Shortcuts administration page.', array('@shortcuts' => url('admin/config/user-interface/shortcut'))) . '
    '; - $output .= '
    ' . t('Choosing shortcut sets') . '
    '; - $output .= '
    ' . t('Users with permission to switch shortcut sets can choose a shortcut set to use from the Shortcuts tab of their user account page.') . '
    '; - $output .= '
    ' . t('Adding and removing shortcuts') . '
    '; - $output .= '
    ' . t('The Shortcut module creates an add/remove link for each page on your site; the link lets you add or remove the current page from the currently-enabled set of shortcuts (if your theme displays it and you have permission to edit your shortcut set). The core Seven administration theme displays this link next to the page title, as a small + or - sign. If you click on the + sign, you will add that page to your preferred set of shortcuts. If the page is already part of your shortcut set, the link will be a - sign, and will allow you to remove the current page from your shortcut set.') . '
    '; - $output .= '
    ' . t('Displaying shortcuts') . '
    '; - $output .= '
    ' . t('You can display your shortcuts by enabling the Shortcuts block on the Blocks administration page. Certain administrative modules also display your shortcuts; for example, the core Toolbar module displays them near the top of the page, along with an Edit shortcuts link.', array('@blocks' => url('admin/structure/block'), '@toolbar-help' => url('admin/help/toolbar'))) . '
    '; - $output .= '
    '; - return $output; - - case 'admin/config/user-interface/shortcut': - case 'admin/config/user-interface/shortcut/%': - if (user_access('switch shortcut sets')) { - $output = '

    ' . t('Define which shortcut set you are using on the Shortcuts tab of your account page.', array('@shortcut-link' => url("user/{$user->uid}/shortcuts"))) . '

    '; - return $output; - } - } -} - -/** - * Implements hook_permission(). - */ -function shortcut_permission() { - return array( - 'administer shortcuts' => array( - 'title' => t('Administer shortcuts'), - ), - 'customize shortcut links' => array( - 'title' => t('Edit current shortcut set'), - 'description' => t('Editing the current shortcut set will affect other users if that set has been assigned to or selected by other users. Granting "Select any shortcut set" permission along with this permission will grant permission to edit any shortcut set.'), - ), - 'switch shortcut sets' => array( - 'title' => t('Select any shortcut set'), - 'description' => t('From all shortcut sets, select one to be own active set. Without this permission, an administrator selects shortcut sets for users.'), - ), - ); -} - -/** - * Implements hook_menu(). - */ -function shortcut_menu() { - $items['admin/config/user-interface/shortcut'] = array( - 'title' => 'Shortcuts', - 'description' => 'Add and modify shortcut sets.', - 'page callback' => 'shortcut_set_admin', - 'access arguments' => array('administer shortcuts'), - 'file' => 'shortcut.admin.inc', - ); - $items['admin/config/user-interface/shortcut/add-set'] = array( - 'title' => 'Add shortcut set', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('shortcut_set_add_form'), - 'access arguments' => array('administer shortcuts'), - 'type' => MENU_LOCAL_ACTION, - 'file' => 'shortcut.admin.inc', - ); - $items['admin/config/user-interface/shortcut/%shortcut_set'] = array( - 'title' => 'Edit shortcuts', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('shortcut_set_customize', 4), - 'title callback' => 'shortcut_set_title_callback', - 'title arguments' => array(4), - 'access callback' => 'shortcut_set_edit_access', - 'access arguments' => array(4), - 'file' => 'shortcut.admin.inc', - ); - $items['admin/config/user-interface/shortcut/%shortcut_set/links'] = array( - 'title' => 'List links', - 'type' => MENU_DEFAULT_LOCAL_TASK, - ); - $items['admin/config/user-interface/shortcut/%shortcut_set/edit'] = array( - 'title' => 'Edit set name', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('shortcut_set_edit_form', 4), - 'access callback' => 'shortcut_set_edit_access', - 'access arguments' => array(4), - 'type' => MENU_LOCAL_TASK, - 'file' => 'shortcut.admin.inc', - 'weight' => 10, - ); - $items['admin/config/user-interface/shortcut/%shortcut_set/delete'] = array( - 'title' => 'Delete shortcut set', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('shortcut_set_delete_form', 4), - 'access callback' => 'shortcut_set_delete_access', - 'access arguments' => array(4), - 'file' => 'shortcut.admin.inc', - ); - $items['admin/config/user-interface/shortcut/%shortcut_set/add-link'] = array( - 'title' => 'Add shortcut', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('shortcut_link_add', 4), - 'access callback' => 'shortcut_set_edit_access', - 'access arguments' => array(4), - 'type' => MENU_LOCAL_ACTION, - 'file' => 'shortcut.admin.inc', - ); - $items['admin/config/user-interface/shortcut/%shortcut_set/add-link-inline'] = array( - 'title' => 'Add shortcut', - 'page callback' => 'shortcut_link_add_inline', - 'page arguments' => array(4), - 'access callback' => 'shortcut_set_edit_access', - 'access arguments' => array(4), - 'type' => MENU_CALLBACK, - 'file' => 'shortcut.admin.inc', - ); - $items['admin/config/user-interface/shortcut/link/%menu_link'] = array( - 'title' => 'Edit shortcut', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('shortcut_link_edit', 5), - 'access callback' => 'shortcut_link_access', - 'access arguments' => array(5), - 'file' => 'shortcut.admin.inc', - ); - $items['admin/config/user-interface/shortcut/link/%menu_link/delete'] = array( - 'title' => 'Delete shortcut', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('shortcut_link_delete', 5), - 'access callback' => 'shortcut_link_access', - 'access arguments' => array(5), - 'file' => 'shortcut.admin.inc', - ); - $items['user/%user/shortcuts'] = array( - 'title' => 'Shortcuts', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('shortcut_set_switch', 1), - 'access callback' => 'shortcut_set_switch_access', - 'access arguments' => array(1), - 'type' => MENU_LOCAL_TASK, - 'file' => 'shortcut.admin.inc', - ); - - return $items; -} - -/** - * Implements hook_admin_paths(). - */ -function shortcut_admin_paths() { - $paths = array( - 'user/*/shortcuts' => TRUE, - ); - return $paths; -} - -/** - * Implements hook_theme(). - */ -function shortcut_theme() { - return array( - 'shortcut_set_customize' => array( - 'render element' => 'form', - 'file' => 'shortcut.admin.inc', - ), - ); -} - -/** - * Implements hook_block_info(). - */ -function shortcut_block_info() { - $blocks['shortcuts']['info'] = t('Shortcuts'); - // Shortcut blocks can't be cached because each menu item can have a custom - // access callback. menu.inc manages its own caching. - $blocks['shortcuts']['cache'] = DRUPAL_NO_CACHE; - return $blocks; -} - -/** - * Implements hook_block_view(). - */ -function shortcut_block_view($delta = '') { - if ($delta == 'shortcuts') { - $shortcut_set = shortcut_current_displayed_set(); - $data['subject'] = t('@shortcut_set shortcuts', array('@shortcut_set' => $shortcut_set->title)); - $data['content'] = shortcut_renderable_links($shortcut_set); - return $data; - } -} - -/** - * Access callback for editing a shortcut set. - * - * @param object $shortcut_set - * (optional) The shortcut set to be edited. If not set, the current user's - * shortcut set will be used. - * - * @return - * TRUE if the current user has access to edit the shortcut set, FALSE - * otherwise. - */ -function shortcut_set_edit_access($shortcut_set = NULL) { - // Sufficiently-privileged users can edit their currently displayed shortcut - // set, but not other sets. Shortcut administrators can edit any set. - if (user_access('administer shortcuts')) { - return TRUE; - } - if (user_access('customize shortcut links')) { - return !isset($shortcut_set) || $shortcut_set == shortcut_current_displayed_set(); - } - return FALSE; -} - -/** - * Access callback for deleting a shortcut set. - * - * @param $shortcut_set - * The shortcut set to be deleted. - * - * @return - * TRUE if the current user has access to delete shortcut sets and this is - * not the site-wide default set; FALSE otherwise. - */ -function shortcut_set_delete_access($shortcut_set) { - // Only admins can delete sets. - if (!user_access('administer shortcuts')) { - return FALSE; - } - - // Never let the default shortcut set be deleted. - if ($shortcut_set->set_name == SHORTCUT_DEFAULT_SET_NAME) { - return FALSE; - } - - return TRUE; -} - -/** - * Access callback for switching the shortcut set assigned to a user account. - * - * @param object $account - * (optional) The user account whose shortcuts will be switched. If not set, - * permissions will be checked for switching the logged-in user's own - * shortcut set. - * - * @return - * TRUE if the current user has access to switch the shortcut set of the - * provided account, FALSE otherwise. - */ -function shortcut_set_switch_access($account = NULL) { - global $user; - - if (user_access('administer shortcuts')) { - // Administrators can switch anyone's shortcut set. - return TRUE; - } - - if (!user_access('switch shortcut sets')) { - // The user has no permission to switch anyone's shortcut set. - return FALSE; - } - - if (!isset($account) || $user->uid == $account->uid) { - // Users with the 'switch shortcut sets' permission can switch their own - // shortcuts sets. - return TRUE; - } - - return FALSE; -} - -/** - * Access callback for editing a link in a shortcut set. - */ -function shortcut_link_access($menu_link) { - // The link must belong to a shortcut set that the current user has access - // to edit. - if ($shortcut_set = shortcut_set_load($menu_link['menu_name'])) { - return shortcut_set_edit_access($shortcut_set); - } - return FALSE; -} - -/** - * Loads the data for a shortcut set. - * - * @param $set_name - * The name of the shortcut set to load. - * - * @return object - * If the shortcut set exists, an object containing the following properties: - * - 'set_name': The internal name of the shortcut set. - * - 'title': The title of the shortcut set. - * - 'links': An array of links associated with this shortcut set. - * If the shortcut set does not exist, the function returns FALSE. - */ -function shortcut_set_load($set_name) { - $set = db_select('shortcut_set', 'ss') - ->fields('ss') - ->condition('set_name', $set_name) - ->execute() - ->fetchObject(); - if (!$set) { - return FALSE; - } - $set->links = menu_load_links($set_name); - return $set; -} - -/** - * Saves a shortcut set. - * - * @param $shortcut_set - * An object containing the following properties: - * - 'title': The title of the shortcut set. - * - 'set_name': (optional) The internal name of the shortcut set. If - * omitted, a new shortcut set will be created, and the 'set_name' property - * will be added to the passed-in object. - * - 'links': (optional) An array of menu links to save for the shortcut set. - * Each link is an array containing at least the following keys (which will - * be expanded to fill in other default values after the shortcut set is - * saved): - * - 'link_path': The Drupal path or external path that the link points to. - * - 'link_title': The title of the link. - * Any other keys accepted by menu_link_save() may also be provided. - * - * @return - * A constant which is either SAVED_NEW or SAVED_UPDATED depending on whether - * a new set was created or an existing one was updated. - * - * @see menu_link_save() - */ -function shortcut_set_save(&$shortcut_set) { - // First save the shortcut set itself. - if (isset($shortcut_set->set_name)) { - $return = drupal_write_record('shortcut_set', $shortcut_set, 'set_name'); - } - else { - $shortcut_set->set_name = shortcut_set_get_unique_name(); - $return = drupal_write_record('shortcut_set', $shortcut_set); - } - // If links were provided for the set, save them. - if (isset($shortcut_set->links)) { - foreach ($shortcut_set->links as &$link) { - // Do not specifically associate these links with the shortcut module, - // since other modules may make them editable via the menu system. - // However, we do need to specify the correct menu name. - $link['menu_name'] = $shortcut_set->set_name; - $link['plid'] = 0; - menu_link_save($link); - } - // Make sure that we have a return value, since if the links were updated - // but the shortcut set was not, the call to drupal_write_record() above - // would not return an indication that anything had changed. - if (empty($return)) { - $return = SAVED_UPDATED; - } - } - return $return; -} - -/** - * Deletes a shortcut set. - * - * Note that the default set cannot be deleted. - * - * @param $shortcut_set - * An object representing the shortcut set to delete. - * - * @return - * TRUE if the set was deleted, FALSE otherwise. - */ -function shortcut_set_delete($shortcut_set) { - // Don't allow deletion of the system default shortcut set. - if ($shortcut_set->set_name == SHORTCUT_DEFAULT_SET_NAME) { - return FALSE; - } - - // First, delete any user assignments for this set, so that each of these - // users will go back to using whatever default set applies. - db_delete('shortcut_set_users') - ->condition('set_name', $shortcut_set->set_name) - ->execute(); - - // Next, delete the menu links for this set. - menu_delete_links($shortcut_set->set_name); - - // Finally, delete the set itself. - $deleted = db_delete('shortcut_set') - ->condition('set_name', $shortcut_set->set_name) - ->execute(); - - return (bool) $deleted; -} - -/** - * Resets the link weights in a shortcut set to match their current order. - * - * This function can be used, for example, when a new shortcut link is added to - * the set. If the link is added to the end of the array and this function is - * called, it will force that link to display at the end of the list. - * - * @param object $shortcut_set - * An object representing a shortcut set. The link weights of the passed-in - * object will be reset as described above. - */ -function shortcut_set_reset_link_weights(&$shortcut_set) { - $weight = -50; - foreach ($shortcut_set->links as &$link) { - $link['weight'] = $weight; - $weight++; - } -} - -/** - * Assigns a user to a particular shortcut set. - * - * @param $shortcut_set - * An object representing the shortcut set. - * @param $account - * A user account that will be assigned to use the set. - */ -function shortcut_set_assign_user($shortcut_set, $account) { - db_merge('shortcut_set_users') - ->key(array('uid' => $account->uid)) - ->fields(array('set_name' => $shortcut_set->set_name)) - ->execute(); - drupal_static_reset('shortcut_current_displayed_set'); -} - -/** - * Unassigns a user from any shortcut set they may have been assigned to. - * - * The user will go back to using whatever default set applies. - * - * @param $account - * A user account that will be removed from the shortcut set assignment. - * - * @return - * TRUE if the user was previously assigned to a shortcut set and has been - * successfully removed from it. FALSE if the user was already not assigned - * to any set. - */ -function shortcut_set_unassign_user($account) { - $deleted = db_delete('shortcut_set_users') - ->condition('uid', $account->uid) - ->execute(); - return (bool) $deleted; -} - -/** - * Returns the current displayed shortcut set for the provided user account. - * - * @param $account - * (optional) The user account whose shortcuts will be returned. Defaults to - * the currently logged-in user. - * - * @return - * An object representing the shortcut set that should be displayed to the - * current user. If the user does not have an explicit shortcut set defined, - * the default set is returned. - */ -function shortcut_current_displayed_set($account = NULL) { - $shortcut_sets = &drupal_static(__FUNCTION__, array()); - global $user; - if (!isset($account)) { - $account = $user; - } - // Try to return a shortcut set from the static cache. - if (isset($shortcut_sets[$account->uid])) { - return $shortcut_sets[$account->uid]; - } - // If none was found, try to find a shortcut set that is explicitly assigned - // to this user. - $query = db_select('shortcut_set', 's'); - $query->addField('s', 'set_name'); - $query->join('shortcut_set_users', 'u', 's.set_name = u.set_name'); - $query->condition('u.uid', $account->uid); - $shortcut_set_name = $query->execute()->fetchField(); - if ($shortcut_set_name) { - $shortcut_set = shortcut_set_load($shortcut_set_name); - } - // Otherwise, use the default set. - else { - $shortcut_set = shortcut_default_set($account); - } - - $shortcut_sets[$account->uid] = $shortcut_set; - return $shortcut_set; -} - -/** - * Returns the default shortcut set for a given user account. - * - * @param object $account - * (optional) The user account whose default shortcut set will be returned. - * If not provided, the function will return the currently logged-in user's - * default shortcut set. - * - * @return - * An object representing the default shortcut set. - */ -function shortcut_default_set($account = NULL) { - global $user; - if (!isset($account)) { - $account = $user; - } - - // Allow modules to return a default shortcut set name. Since we can only - // have one, we allow the last module which returns a valid result to take - // precedence. If no module returns a valid set, fall back on the site-wide - // default, which is the lowest-numbered shortcut set. - $suggestions = array_reverse(module_invoke_all('shortcut_default_set', $account)); - $suggestions[] = SHORTCUT_DEFAULT_SET_NAME; - foreach ($suggestions as $name) { - if ($shortcut_set = shortcut_set_load($name)) { - break; - } - } - - return $shortcut_set; -} - -/** - * Returns a unique, machine-readable shortcut set name. - */ -function shortcut_set_get_unique_name() { - // Shortcut sets are numbered sequentially, so we keep trying until we find - // one that is available. For better performance, we start with a number - // equal to one more than the current number of shortcut sets, so that if - // no shortcut sets have been deleted from the database, this will - // automatically give us the correct one. - $number = db_query("SELECT COUNT(*) FROM {shortcut_set}")->fetchField() + 1; - do { - $name = shortcut_set_name($number); - $number++; - } while ($shortcut_set = shortcut_set_load($name)); - return $name; -} - -/** - * Returns the name of a shortcut set, based on a provided number. - * - * All shortcut sets have names like "shortcut-set-N" so that they can be - * matched with a properly-namespaced entry in the {menu_links} table. - * - * @param $number - * A number representing the shortcut set whose name should be retrieved. - * - * @return - * A string representing the expected shortcut name. - */ -function shortcut_set_name($number) { - return "shortcut-set-$number"; -} - -/** - * Returns an array of all shortcut sets, keyed by the set name. - * - * @return - * An array of shortcut sets. Note that only the basic shortcut set - * properties (name and title) are returned by this function, not the list - * of menu links that belong to the set. - */ -function shortcut_sets() { - return db_select('shortcut_set', 'ss') - ->fields('ss') - ->execute() - ->fetchAllAssoc('set_name'); -} - -/** - * Check to see if a shortcut set with the given title already exists. - * - * @param $title - * Human-readable name of the shortcut set to check. - * - * @return - * TRUE if a shortcut set with that title exists; FALSE otherwise. - */ -function shortcut_set_title_exists($title) { - return (bool) db_query_range('SELECT 1 FROM {shortcut_set} WHERE title = :title', 0, 1, array(':title' => $title))->fetchField(); -} - -/** - * Determines if a path corresponds to a valid shortcut link. - * - * @param $path - * The path to the link. - * @return - * TRUE if the shortcut link is valid, FALSE otherwise. Valid links are ones - * that correspond to actual paths on the site. - * - * @see menu_edit_item_validate() - */ -function shortcut_valid_link($path) { - // Do not use URL aliases. - $normal_path = drupal_get_normal_path($path); - if ($path != $normal_path) { - $path = $normal_path; - } - // An empty path is valid too and will be converted to . - return (!url_is_external($path) && menu_get_item($path)) || empty($path) || $path == ''; -} - -/** - * Returns an array of shortcut links, suitable for rendering. - * - * @param $shortcut_set - * (optional) An object representing the set whose links will be displayed. - * If not provided, the user's current set will be displayed. - * @return - * An array of shortcut links, in the format returned by the menu system. - * - * @see menu_tree() - */ -function shortcut_renderable_links($shortcut_set = NULL) { - if (!isset($shortcut_set)) { - $shortcut_set = shortcut_current_displayed_set(); - } - return menu_tree($shortcut_set->set_name); -} - -/** - * Implements hook_preprocess_page(). - */ -function shortcut_preprocess_page(&$variables) { - // Only display the shortcut link if the user has the ability to edit - // shortcuts and if the page's actual content is being shown (for example, - // we do not want to display it on "access denied" or "page not found" - // pages). - if (shortcut_set_edit_access() && ($item = menu_get_item()) && $item['access']) { - $link = $_GET['q']; - $query_parameters = drupal_get_query_parameters(); - if (!empty($query_parameters)) { - $link .= '?' . drupal_http_build_query($query_parameters); - } - $query = array( - 'link' => $link, - 'name' => drupal_get_title(), - ); - $query += drupal_get_destination(); - - $shortcut_set = shortcut_current_displayed_set(); - - // Check if $link is already a shortcut and set $link_mode accordingly. - foreach ($shortcut_set->links as $shortcut) { - if ($link == $shortcut['link_path']) { - $mlid = $shortcut['mlid']; - break; - } - } - $link_mode = isset($mlid) ? "remove" : "add"; - - if ($link_mode == "add") { - $query['token'] = drupal_get_token('shortcut-add-link'); - $link_text = shortcut_set_switch_access() ? t('Add to %shortcut_set shortcuts', array('%shortcut_set' => $shortcut_set->title)) : t('Add to shortcuts'); - $link_path = 'admin/config/user-interface/shortcut/' . $shortcut_set->set_name . '/add-link-inline'; - } - else { - $query['mlid'] = $mlid; - $link_text = shortcut_set_switch_access() ? t('Remove from %shortcut_set shortcuts', array('%shortcut_set' => $shortcut_set->title)) : t('Remove from shortcuts'); - $link_path = 'admin/config/user-interface/shortcut/link/' . $mlid . '/delete'; - } - - if (theme_get_setting('shortcut_module_link')) { - $variables['title_suffix']['add_or_remove_shortcut'] = array( - '#attached' => array('css' => array(drupal_get_path('module', 'shortcut') . '/shortcut.css')), - '#prefix' => '', - ); - } - } -} - -/** - * Implements hook_page_alter(). - */ -function shortcut_page_alter(&$page) { - if (isset($page['page_top']['toolbar'])) { - // If the toolbar is available, add a pre-render function to display the - // current shortcuts in the toolbar drawer. - $page['page_top']['toolbar']['#pre_render'][] = 'shortcut_toolbar_pre_render'; - } -} - -/** - * Pre-render function for adding shortcuts to the toolbar drawer. - */ -function shortcut_toolbar_pre_render($toolbar) { - $links = shortcut_renderable_links(); - $links['#attached'] = array('css' => array(drupal_get_path('module', 'shortcut') . '/shortcut.css')); - $links['#prefix'] = '
    '; - $links['#suffix'] = '
    '; - $shortcut_set = shortcut_current_displayed_set(); - $configure_link = NULL; - if (shortcut_set_edit_access($shortcut_set)) { - $configure_link = array( - '#type' => 'link', - '#title' => t('Edit shortcuts'), - '#href' => 'admin/config/user-interface/shortcut/' . $shortcut_set->set_name, - '#options' => array('attributes' => array('id' => 'edit-shortcuts')), - ); - } - - $drawer = array( - 'shortcuts' => $links, - 'configure' => $configure_link, - ); - - $toolbar['toolbar_drawer'][] = $drawer; - return $toolbar; -} - -/** - * Returns the sanitized title of a shortcut set. - * - * Deprecated. This function was previously used as a menu item title callback - * but has been replaced by shortcut_set_title_callback() (which does not - * sanitize the title, since the menu system does that automatically). In - * Drupal 7, use that function for title callbacks, and call check_plain() - * directly if you need a sanitized title. In Drupal 8, this function will be - * restored as a title callback and therefore will no longer sanitize its - * output. - * - * @param $shortcut_set - * An object representing the shortcut set, as returned by - * shortcut_set_load(). - */ -function shortcut_set_title($shortcut_set) { - return check_plain($shortcut_set->title); -} - -/** - * Returns the title of a shortcut set. - * - * Title callback for the editing pages for shortcut sets. - * - * @param $shortcut_set - * An object representing the shortcut set, as returned by - * shortcut_set_load(). - */ -function shortcut_set_title_callback($shortcut_set) { - return $shortcut_set->title; -} diff --git a/modules/shortcut/shortcut.png b/modules/shortcut/shortcut.png deleted file mode 100644 index 2924557..0000000 Binary files a/modules/shortcut/shortcut.png and /dev/null differ diff --git a/modules/shortcut/shortcut.test b/modules/shortcut/shortcut.test deleted file mode 100644 index 2bd9605..0000000 --- a/modules/shortcut/shortcut.test +++ /dev/null @@ -1,371 +0,0 @@ -admin_user = $this->drupalCreateUser(array('access toolbar', 'administer shortcuts', 'view the administration theme', 'create article content', 'create page content', 'access content overview')); - $this->shortcut_user = $this->drupalCreateUser(array('customize shortcut links', 'switch shortcut sets')); - - // Create a node. - $this->node = $this->drupalCreateNode(array('type' => 'article')); - - // Log in as admin and grab the default shortcut set. - $this->drupalLogin($this->admin_user); - $this->set = shortcut_set_load(SHORTCUT_DEFAULT_SET_NAME); - shortcut_set_assign_user($this->set, $this->admin_user); - } - - /** - * Creates a generic shortcut set. - */ - function generateShortcutSet($title = '', $default_links = TRUE) { - $set = new stdClass(); - $set->title = empty($title) ? $this->randomName(10) : $title; - if ($default_links) { - $set->links = array(); - $set->links[] = $this->generateShortcutLink('node/add'); - $set->links[] = $this->generateShortcutLink('admin/content'); - } - shortcut_set_save($set); - - return $set; - } - - /** - * Creates a generic shortcut link. - */ - function generateShortcutLink($path, $title = '') { - $link = array( - 'link_path' => $path, - 'link_title' => !empty($title) ? $title : $this->randomName(10), - ); - - return $link; - } - - /** - * Extracts information from shortcut set links. - * - * @param object $set - * The shortcut set object to extract information from. - * @param string $key - * The array key indicating what information to extract from each link: - * - 'link_path': Extract link paths. - * - 'link_title': Extract link titles. - * - 'mlid': Extract the menu link item ID numbers. - * - * @return array - * Array of the requested information from each link. - */ - function getShortcutInformation($set, $key) { - $info = array(); - foreach ($set->links as $link) { - $info[] = $link[$key]; - } - return $info; - } -} - -/** - * Defines shortcut links test cases. - */ -class ShortcutLinksTestCase extends ShortcutTestCase { - - public static function getInfo() { - return array( - 'name' => 'Shortcut link functionality', - 'description' => 'Create, view, edit, delete, and change shortcut links.', - 'group' => 'Shortcut', - ); - } - - /** - * Tests that creating a shortcut works properly. - */ - function testShortcutLinkAdd() { - $set = $this->set; - - // Create an alias for the node so we can test aliases. - $path = array( - 'source' => 'node/' . $this->node->nid, - 'alias' => $this->randomName(8), - ); - path_save($path); - - // Create some paths to test. - $test_cases = array( - array('path' => ''), - array('path' => 'admin'), - array('path' => 'admin/config/system/site-information'), - array('path' => "node/{$this->node->nid}/edit"), - array('path' => $path['alias']), - ); - - // Check that each new shortcut links where it should. - foreach ($test_cases as $test) { - $title = $this->randomName(10); - $form_data = array( - 'shortcut_link[link_title]' => $title, - 'shortcut_link[link_path]' => $test['path'], - ); - $this->drupalPost('admin/config/user-interface/shortcut/' . $set->set_name . '/add-link', $form_data, t('Save')); - $this->assertResponse(200); - $saved_set = shortcut_set_load($set->set_name); - $paths = $this->getShortcutInformation($saved_set, 'link_path'); - $test_path = empty($test['path']) ? '' : $test['path']; - $this->assertTrue(in_array(drupal_get_normal_path($test_path), $paths), 'Shortcut created: '. $test['path']); - $this->assertLink($title, 0, 'Shortcut link found on the page.'); - } - } - - /** - * Tests that the "add to shortcut" link changes to "remove shortcut". - */ - function testShortcutQuickLink() { - $this->drupalGet($this->set->links[0]['link_path']); - $this->assertRaw(t('Remove from %title shortcuts', array('%title' => $this->set->title)), '"Add to shortcuts" link properly switched to "Remove from shortcuts".'); - } - - /** - * Tests that shortcut links can be renamed. - */ - function testShortcutLinkRename() { - $set = $this->set; - - // Attempt to rename shortcut link. - $new_link_name = $this->randomName(10); - - $this->drupalPost('admin/config/user-interface/shortcut/link/' . $set->links[0]['mlid'], array('shortcut_link[link_title]' => $new_link_name, 'shortcut_link[link_path]' => $set->links[0]['link_path']), t('Save')); - $saved_set = shortcut_set_load($set->set_name); - $titles = $this->getShortcutInformation($saved_set, 'link_title'); - $this->assertTrue(in_array($new_link_name, $titles), 'Shortcut renamed: ' . $new_link_name); - $this->assertLink($new_link_name, 0, 'Renamed shortcut link appears on the page.'); - } - - /** - * Tests that changing the path of a shortcut link works. - */ - function testShortcutLinkChangePath() { - $set = $this->set; - - // Tests changing a shortcut path. - $new_link_path = 'admin/config'; - - $this->drupalPost('admin/config/user-interface/shortcut/link/' . $set->links[0]['mlid'], array('shortcut_link[link_title]' => $set->links[0]['link_title'], 'shortcut_link[link_path]' => $new_link_path), t('Save')); - $saved_set = shortcut_set_load($set->set_name); - $paths = $this->getShortcutInformation($saved_set, 'link_path'); - $this->assertTrue(in_array($new_link_path, $paths), 'Shortcut path changed: ' . $new_link_path); - $this->assertLinkByHref($new_link_path, 0, 'Shortcut with new path appears on the page.'); - } - - /** - * Tests deleting a shortcut link. - */ - function testShortcutLinkDelete() { - $set = $this->set; - - $this->drupalPost('admin/config/user-interface/shortcut/link/' . $set->links[0]['mlid'] . '/delete', array(), 'Delete'); - $saved_set = shortcut_set_load($set->set_name); - $mlids = $this->getShortcutInformation($saved_set, 'mlid'); - $this->assertFalse(in_array($set->links[0]['mlid'], $mlids), 'Successfully deleted a shortcut.'); - } - - /** - * Tests that the add shortcut link is not displayed for 404/403 errors. - * - * Tests that the "Add to shortcuts" link is not displayed on a page not - * found or a page the user does not have access to. - */ - function testNoShortcutLink() { - // Change to a theme that displays shortcuts. - variable_set('theme_default', 'seven'); - - $this->drupalGet('page-that-does-not-exist'); - $this->assertNoRaw('add-shortcut', 'Add to shortcuts link was not shown on a page not found.'); - - // The user does not have access to this path. - $this->drupalGet('admin/modules'); - $this->assertNoRaw('add-shortcut', 'Add to shortcuts link was not shown on a page the user does not have access to.'); - - // Verify that the testing mechanism works by verifying the shortcut - // link appears on admin/content/node. - $this->drupalGet('admin/content/node'); - $this->assertRaw('add-shortcut', 'Add to shortcuts link was shown on a page the user does have access to.'); - } -} - -/** - * Defines shortcut set test cases. - */ -class ShortcutSetsTestCase extends ShortcutTestCase { - - public static function getInfo() { - return array( - 'name' => 'Shortcut set functionality', - 'description' => 'Create, view, edit, delete, and change shortcut sets.', - 'group' => 'Shortcut', - ); - } - - /** - * Tests creating a shortcut set. - */ - function testShortcutSetAdd() { - $new_set = $this->generateShortcutSet($this->randomName(10)); - $sets = shortcut_sets(); - $this->assertTrue(isset($sets[$new_set->set_name]), 'Successfully created a shortcut set.'); - $this->drupalGet('user/' . $this->admin_user->uid . '/shortcuts'); - $this->assertText($new_set->title, 'Generated shortcut set was listed as a choice on the user account page.'); - } - - /** - * Tests switching a user's own shortcut set. - */ - function testShortcutSetSwitchOwn() { - $new_set = $this->generateShortcutSet($this->randomName(10)); - - // Attempt to switch the default shortcut set to the newly created shortcut - // set. - $this->drupalPost('user/' . $this->admin_user->uid . '/shortcuts', array('set' => $new_set->set_name), t('Change set')); - $this->assertResponse(200); - $current_set = shortcut_current_displayed_set($this->admin_user); - $this->assertTrue($new_set->set_name == $current_set->set_name, 'Successfully switched own shortcut set.'); - } - - /** - * Tests switching another user's shortcut set. - */ - function testShortcutSetAssign() { - $new_set = $this->generateShortcutSet($this->randomName(10)); - - shortcut_set_assign_user($new_set, $this->shortcut_user); - $current_set = shortcut_current_displayed_set($this->shortcut_user); - $this->assertTrue($new_set->set_name == $current_set->set_name, "Successfully switched another user's shortcut set."); - } - - /** - * Tests switching a user's shortcut set and creating one at the same time. - */ - function testShortcutSetSwitchCreate() { - $edit = array( - 'set' => 'new', - 'new' => $this->randomName(10), - ); - $this->drupalPost('user/' . $this->admin_user->uid . '/shortcuts', $edit, t('Change set')); - $current_set = shortcut_current_displayed_set($this->admin_user); - $this->assertNotEqual($current_set->set_name, $this->set->set_name, 'A shortcut set can be switched to at the same time as it is created.'); - $this->assertEqual($current_set->title, $edit['new'], 'The new set is correctly assigned to the user.'); - } - - /** - * Tests switching a user's shortcut set without providing a new set name. - */ - function testShortcutSetSwitchNoSetName() { - $edit = array('set' => 'new'); - $this->drupalPost('user/' . $this->admin_user->uid . '/shortcuts', $edit, t('Change set')); - $this->assertText(t('The new set name is required.')); - $current_set = shortcut_current_displayed_set($this->admin_user); - $this->assertEqual($current_set->set_name, $this->set->set_name, 'Attempting to switch to a new shortcut set without providing a set name does not succeed.'); - } - - /** - * Tests that shortcut_set_save() correctly updates existing links. - */ - function testShortcutSetSave() { - $set = $this->set; - $old_mlids = $this->getShortcutInformation($set, 'mlid'); - - $set->links[] = $this->generateShortcutLink('admin', $this->randomName(10)); - shortcut_set_save($set); - $saved_set = shortcut_set_load($set->set_name); - - $new_mlids = $this->getShortcutInformation($saved_set, 'mlid'); - $this->assertTrue(count(array_intersect($old_mlids, $new_mlids)) == count($old_mlids), 'shortcut_set_save() did not inadvertently change existing mlids.'); - } - - /** - * Tests renaming a shortcut set. - */ - function testShortcutSetRename() { - $set = $this->set; - - $new_title = $this->randomName(10); - $this->drupalPost('admin/config/user-interface/shortcut/' . $set->set_name . '/edit', array('title' => $new_title), t('Save')); - $set = shortcut_set_load($set->set_name); - $this->assertTrue($set->title == $new_title, 'Shortcut set has been successfully renamed.'); - } - - /** - * Tests renaming a shortcut set to the same name as another set. - */ - function testShortcutSetRenameAlreadyExists() { - $set = $this->generateShortcutSet($this->randomName(10)); - $existing_title = $this->set->title; - $this->drupalPost('admin/config/user-interface/shortcut/' . $set->set_name . '/edit', array('title' => $existing_title), t('Save')); - $this->assertRaw(t('The shortcut set %name already exists. Choose another name.', array('%name' => $existing_title))); - $set = shortcut_set_load($set->set_name); - $this->assertNotEqual($set->title, $existing_title, format_string('The shortcut set %title cannot be renamed to %new-title because a shortcut set with that title already exists.', array('%title' => $set->title, '%new-title' => $existing_title))); - } - - /** - * Tests unassigning a shortcut set. - */ - function testShortcutSetUnassign() { - $new_set = $this->generateShortcutSet($this->randomName(10)); - - shortcut_set_assign_user($new_set, $this->shortcut_user); - shortcut_set_unassign_user($this->shortcut_user); - $current_set = shortcut_current_displayed_set($this->shortcut_user); - $default_set = shortcut_default_set($this->shortcut_user); - $this->assertTrue($current_set->set_name == $default_set->set_name, "Successfully unassigned another user's shortcut set."); - } - - /** - * Tests deleting a shortcut set. - */ - function testShortcutSetDelete() { - $new_set = $this->generateShortcutSet($this->randomName(10)); - - $this->drupalPost('admin/config/user-interface/shortcut/' . $new_set->set_name . '/delete', array(), t('Delete')); - $sets = shortcut_sets(); - $this->assertFalse(isset($sets[$new_set->set_name]), 'Successfully deleted a shortcut set.'); - } - - /** - * Tests deleting the default shortcut set. - */ - function testShortcutSetDeleteDefault() { - $this->drupalGet('admin/config/user-interface/shortcut/' . SHORTCUT_DEFAULT_SET_NAME . '/delete'); - $this->assertResponse(403); - } -} diff --git a/modules/simpletest/drupal_web_test_case.php b/modules/simpletest/drupal_web_test_case.php deleted file mode 100644 index 271efff..0000000 --- a/modules/simpletest/drupal_web_test_case.php +++ /dev/null @@ -1,3757 +0,0 @@ - 0, - '#fail' => 0, - '#exception' => 0, - '#debug' => 0, - ); - - /** - * Assertions thrown in that test case. - * - * @var Array - */ - protected $assertions = array(); - - /** - * This class is skipped when looking for the source of an assertion. - * - * When displaying which function an assert comes from, it's not too useful - * to see "drupalWebTestCase->drupalLogin()', we would like to see the test - * that called it. So we need to skip the classes defining these helper - * methods. - */ - protected $skipClasses = array(__CLASS__ => TRUE); - - /** - * Flag to indicate whether the test has been set up. - * - * The setUp() method isolates the test from the parent Drupal site by - * creating a random prefix for the database and setting up a clean file - * storage directory. The tearDown() method then cleans up this test - * environment. We must ensure that setUp() has been run. Otherwise, - * tearDown() will act on the parent Drupal site rather than the test - * environment, destroying live data. - */ - protected $setup = FALSE; - - protected $setupDatabasePrefix = FALSE; - - protected $setupEnvironment = FALSE; - - /** - * Constructor for DrupalTestCase. - * - * @param $test_id - * Tests with the same id are reported together. - */ - public function __construct($test_id = NULL) { - $this->testId = $test_id; - } - - /** - * Internal helper: stores the assert. - * - * @param $status - * Can be 'pass', 'fail', 'exception'. - * TRUE is a synonym for 'pass', FALSE for 'fail'. - * @param $message - * The message string. - * @param $group - * Which group this assert belongs to. - * @param $caller - * By default, the assert comes from a function whose name starts with - * 'test'. Instead, you can specify where this assert originates from - * by passing in an associative array as $caller. Key 'file' is - * the name of the source file, 'line' is the line number and 'function' - * is the caller function itself. - */ - protected function assert($status, $message = '', $group = 'Other', array $caller = NULL) { - // Convert boolean status to string status. - if (is_bool($status)) { - $status = $status ? 'pass' : 'fail'; - } - - // Increment summary result counter. - $this->results['#' . $status]++; - - // Get the function information about the call to the assertion method. - if (!$caller) { - $caller = $this->getAssertionCall(); - } - - // Creation assertion array that can be displayed while tests are running. - $this->assertions[] = $assertion = array( - 'test_id' => $this->testId, - 'test_class' => get_class($this), - 'status' => $status, - 'message' => $message, - 'message_group' => $group, - 'function' => $caller['function'], - 'line' => $caller['line'], - 'file' => $caller['file'], - ); - - // Store assertion for display after the test has completed. - self::getDatabaseConnection() - ->insert('simpletest') - ->fields($assertion) - ->execute(); - - // We do not use a ternary operator here to allow a breakpoint on - // test failure. - if ($status == 'pass') { - return TRUE; - } - else { - return FALSE; - } - } - - /** - * Returns the database connection to the site running Simpletest. - * - * @return DatabaseConnection - * The database connection to use for inserting assertions. - */ - public static function getDatabaseConnection() { - try { - $connection = Database::getConnection('default', 'simpletest_original_default'); - } - catch (DatabaseConnectionNotDefinedException $e) { - // If the test was not set up, the simpletest_original_default - // connection does not exist. - $connection = Database::getConnection('default', 'default'); - } - - return $connection; - } - - /** - * Store an assertion from outside the testing context. - * - * This is useful for inserting assertions that can only be recorded after - * the test case has been destroyed, such as PHP fatal errors. The caller - * information is not automatically gathered since the caller is most likely - * inserting the assertion on behalf of other code. In all other respects - * the method behaves just like DrupalTestCase::assert() in terms of storing - * the assertion. - * - * @return - * Message ID of the stored assertion. - * - * @see DrupalTestCase::assert() - * @see DrupalTestCase::deleteAssert() - */ - public static function insertAssert($test_id, $test_class, $status, $message = '', $group = 'Other', array $caller = array()) { - // Convert boolean status to string status. - if (is_bool($status)) { - $status = $status ? 'pass' : 'fail'; - } - - $caller += array( - 'function' => t('Unknown'), - 'line' => 0, - 'file' => t('Unknown'), - ); - - $assertion = array( - 'test_id' => $test_id, - 'test_class' => $test_class, - 'status' => $status, - 'message' => $message, - 'message_group' => $group, - 'function' => $caller['function'], - 'line' => $caller['line'], - 'file' => $caller['file'], - ); - - return self::getDatabaseConnection() - ->insert('simpletest') - ->fields($assertion) - ->execute(); - } - - /** - * Delete an assertion record by message ID. - * - * @param $message_id - * Message ID of the assertion to delete. - * @return - * TRUE if the assertion was deleted, FALSE otherwise. - * - * @see DrupalTestCase::insertAssert() - */ - public static function deleteAssert($message_id) { - return (bool) self::getDatabaseConnection() - ->delete('simpletest') - ->condition('message_id', $message_id) - ->execute(); - } - - /** - * Cycles through backtrace until the first non-assertion method is found. - * - * @return - * Array representing the true caller. - */ - protected function getAssertionCall() { - $backtrace = debug_backtrace(); - - // The first element is the call. The second element is the caller. - // We skip calls that occurred in one of the methods of our base classes - // or in an assertion function. - while (($caller = $backtrace[1]) && - ((isset($caller['class']) && isset($this->skipClasses[$caller['class']])) || - substr($caller['function'], 0, 6) == 'assert')) { - // We remove that call. - array_shift($backtrace); - } - - return _drupal_get_last_caller($backtrace); - } - - /** - * Check to see if a value is not false (not an empty string, 0, NULL, or FALSE). - * - * @param $value - * The value on which the assertion is to be done. - * @param $message - * The message to display along with the assertion. - * @param $group - * The type of assertion - examples are "Browser", "PHP". - * @return - * TRUE if the assertion succeeded, FALSE otherwise. - */ - protected function assertTrue($value, $message = '', $group = 'Other') { - return $this->assert((bool) $value, $message ? $message : t('Value @value is TRUE.', array('@value' => var_export($value, TRUE))), $group); - } - - /** - * Check to see if a value is false (an empty string, 0, NULL, or FALSE). - * - * @param $value - * The value on which the assertion is to be done. - * @param $message - * The message to display along with the assertion. - * @param $group - * The type of assertion - examples are "Browser", "PHP". - * @return - * TRUE if the assertion succeeded, FALSE otherwise. - */ - protected function assertFalse($value, $message = '', $group = 'Other') { - return $this->assert(!$value, $message ? $message : t('Value @value is FALSE.', array('@value' => var_export($value, TRUE))), $group); - } - - /** - * Check to see if a value is NULL. - * - * @param $value - * The value on which the assertion is to be done. - * @param $message - * The message to display along with the assertion. - * @param $group - * The type of assertion - examples are "Browser", "PHP". - * @return - * TRUE if the assertion succeeded, FALSE otherwise. - */ - protected function assertNull($value, $message = '', $group = 'Other') { - return $this->assert(!isset($value), $message ? $message : t('Value @value is NULL.', array('@value' => var_export($value, TRUE))), $group); - } - - /** - * Check to see if a value is not NULL. - * - * @param $value - * The value on which the assertion is to be done. - * @param $message - * The message to display along with the assertion. - * @param $group - * The type of assertion - examples are "Browser", "PHP". - * @return - * TRUE if the assertion succeeded, FALSE otherwise. - */ - protected function assertNotNull($value, $message = '', $group = 'Other') { - return $this->assert(isset($value), $message ? $message : t('Value @value is not NULL.', array('@value' => var_export($value, TRUE))), $group); - } - - /** - * Check to see if two values are equal. - * - * @param $first - * The first value to check. - * @param $second - * The second value to check. - * @param $message - * The message to display along with the assertion. - * @param $group - * The type of assertion - examples are "Browser", "PHP". - * @return - * TRUE if the assertion succeeded, FALSE otherwise. - */ - protected function assertEqual($first, $second, $message = '', $group = 'Other') { - return $this->assert($first == $second, $message ? $message : t('Value @first is equal to value @second.', array('@first' => var_export($first, TRUE), '@second' => var_export($second, TRUE))), $group); - } - - /** - * Check to see if two values are not equal. - * - * @param $first - * The first value to check. - * @param $second - * The second value to check. - * @param $message - * The message to display along with the assertion. - * @param $group - * The type of assertion - examples are "Browser", "PHP". - * @return - * TRUE if the assertion succeeded, FALSE otherwise. - */ - protected function assertNotEqual($first, $second, $message = '', $group = 'Other') { - return $this->assert($first != $second, $message ? $message : t('Value @first is not equal to value @second.', array('@first' => var_export($first, TRUE), '@second' => var_export($second, TRUE))), $group); - } - - /** - * Check to see if two values are identical. - * - * @param $first - * The first value to check. - * @param $second - * The second value to check. - * @param $message - * The message to display along with the assertion. - * @param $group - * The type of assertion - examples are "Browser", "PHP". - * @return - * TRUE if the assertion succeeded, FALSE otherwise. - */ - protected function assertIdentical($first, $second, $message = '', $group = 'Other') { - return $this->assert($first === $second, $message ? $message : t('Value @first is identical to value @second.', array('@first' => var_export($first, TRUE), '@second' => var_export($second, TRUE))), $group); - } - - /** - * Check to see if two values are not identical. - * - * @param $first - * The first value to check. - * @param $second - * The second value to check. - * @param $message - * The message to display along with the assertion. - * @param $group - * The type of assertion - examples are "Browser", "PHP". - * @return - * TRUE if the assertion succeeded, FALSE otherwise. - */ - protected function assertNotIdentical($first, $second, $message = '', $group = 'Other') { - return $this->assert($first !== $second, $message ? $message : t('Value @first is not identical to value @second.', array('@first' => var_export($first, TRUE), '@second' => var_export($second, TRUE))), $group); - } - - /** - * Fire an assertion that is always positive. - * - * @param $message - * The message to display along with the assertion. - * @param $group - * The type of assertion - examples are "Browser", "PHP". - * @return - * TRUE. - */ - protected function pass($message = NULL, $group = 'Other') { - return $this->assert(TRUE, $message, $group); - } - - /** - * Fire an assertion that is always negative. - * - * @param $message - * The message to display along with the assertion. - * @param $group - * The type of assertion - examples are "Browser", "PHP". - * @return - * FALSE. - */ - protected function fail($message = NULL, $group = 'Other') { - return $this->assert(FALSE, $message, $group); - } - - /** - * Fire an error assertion. - * - * @param $message - * The message to display along with the assertion. - * @param $group - * The type of assertion - examples are "Browser", "PHP". - * @param $caller - * The caller of the error. - * @return - * FALSE. - */ - protected function error($message = '', $group = 'Other', array $caller = NULL) { - if ($group == 'User notice') { - // Since 'User notice' is set by trigger_error() which is used for debug - // set the message to a status of 'debug'. - return $this->assert('debug', $message, 'Debug', $caller); - } - - return $this->assert('exception', $message, $group, $caller); - } - - /** - * Logs a verbose message in a text file. - * - * The link to the verbose message will be placed in the test results as a - * passing assertion with the text '[verbose message]'. - * - * @param $message - * The verbose message to be stored. - * - * @see simpletest_verbose() - */ - protected function verbose($message) { - if ($id = simpletest_verbose($message)) { - $class_safe = str_replace('\\', '_', get_class($this)); - $url = file_create_url($this->originalFileDirectory . '/simpletest/verbose/' . $class_safe . '-' . $id . '.html'); - $this->error(l(t('Verbose message'), $url, array('attributes' => array('target' => '_blank'))), 'User notice'); - } - } - - /** - * Run all tests in this class. - * - * Regardless of whether $methods are passed or not, only method names - * starting with "test" are executed. - * - * @param $methods - * (optional) A list of method names in the test case class to run; e.g., - * array('testFoo', 'testBar'). By default, all methods of the class are - * taken into account, but it can be useful to only run a few selected test - * methods during debugging. - */ - public function run(array $methods = array()) { - // Initialize verbose debugging. - $class = get_class($this); - simpletest_verbose(NULL, variable_get('file_public_path', conf_path() . '/files'), str_replace('\\', '_', $class)); - - // HTTP auth settings (:) for the simpletest browser - // when sending requests to the test site. - $this->httpauth_method = variable_get('simpletest_httpauth_method', CURLAUTH_BASIC); - $username = variable_get('simpletest_httpauth_username', NULL); - $password = variable_get('simpletest_httpauth_password', NULL); - if ($username && $password) { - $this->httpauth_credentials = $username . ':' . $password; - } - - set_error_handler(array($this, 'errorHandler')); - // Iterate through all the methods in this class, unless a specific list of - // methods to run was passed. - $class_methods = get_class_methods($class); - if ($methods) { - $class_methods = array_intersect($class_methods, $methods); - } - foreach ($class_methods as $method) { - // If the current method starts with "test", run it - it's a test. - if (strtolower(substr($method, 0, 4)) == 'test') { - // Insert a fail record. This will be deleted on completion to ensure - // that testing completed. - $method_info = new ReflectionMethod($class, $method); - $caller = array( - 'file' => $method_info->getFileName(), - 'line' => $method_info->getStartLine(), - 'function' => $class . '->' . $method . '()', - ); - $completion_check_id = DrupalTestCase::insertAssert($this->testId, $class, FALSE, t('The test did not complete due to a fatal error.'), 'Completion check', $caller); - $this->setUp(); - if ($this->setup) { - try { - $this->$method(); - // Finish up. - } - catch (Exception $e) { - $this->exceptionHandler($e); - } - $this->tearDown(); - } - else { - $this->fail(t("The test cannot be executed because it has not been set up properly.")); - } - // Remove the completion check record. - DrupalTestCase::deleteAssert($completion_check_id); - } - } - // Clear out the error messages and restore error handler. - drupal_get_messages(); - restore_error_handler(); - } - - /** - * Handle errors during test runs. - * - * Because this is registered in set_error_handler(), it has to be public. - * @see set_error_handler - */ - public function errorHandler($severity, $message, $file = NULL, $line = NULL) { - if ($severity & error_reporting()) { - $error_map = array( - E_STRICT => 'Run-time notice', - E_WARNING => 'Warning', - E_NOTICE => 'Notice', - E_CORE_ERROR => 'Core error', - E_CORE_WARNING => 'Core warning', - E_USER_ERROR => 'User error', - E_USER_WARNING => 'User warning', - E_USER_NOTICE => 'User notice', - E_RECOVERABLE_ERROR => 'Recoverable error', - ); - - // PHP 5.3 adds new error logging constants. Add these conditionally for - // backwards compatibility with PHP 5.2. - if (defined('E_DEPRECATED')) { - $error_map += array( - E_DEPRECATED => 'Deprecated', - E_USER_DEPRECATED => 'User deprecated', - ); - } - - $backtrace = debug_backtrace(); - $this->error($message, $error_map[$severity], _drupal_get_last_caller($backtrace)); - } - return TRUE; - } - - /** - * Handle exceptions. - * - * @see set_exception_handler - */ - protected function exceptionHandler($exception) { - $backtrace = $exception->getTrace(); - // Push on top of the backtrace the call that generated the exception. - array_unshift($backtrace, array( - 'line' => $exception->getLine(), - 'file' => $exception->getFile(), - )); - require_once DRUPAL_ROOT . '/includes/errors.inc'; - // The exception message is run through check_plain() by _drupal_decode_exception(). - $this->error(t('%type: !message in %function (line %line of %file).', _drupal_decode_exception($exception)), 'Uncaught exception', _drupal_get_last_caller($backtrace)); - } - - /** - * Generates a random string of ASCII characters of codes 32 to 126. - * - * The generated string includes alpha-numeric characters and common - * miscellaneous characters. Use this method when testing general input - * where the content is not restricted. - * - * Do not use this method when special characters are not possible (e.g., in - * machine or file names that have already been validated); instead, - * use DrupalWebTestCase::randomName(). - * - * @param $length - * Length of random string to generate. - * - * @return - * Randomly generated string. - * - * @see DrupalWebTestCase::randomName() - */ - public static function randomString($length = 8) { - $str = ''; - for ($i = 0; $i < $length; $i++) { - $str .= chr(mt_rand(32, 126)); - } - return $str; - } - - /** - * Generates a random string containing letters and numbers. - * - * The string will always start with a letter. The letters may be upper or - * lower case. This method is better for restricted inputs that do not - * accept certain characters. For example, when testing input fields that - * require machine readable values (i.e. without spaces and non-standard - * characters) this method is best. - * - * Do not use this method when testing unvalidated user input. Instead, use - * DrupalWebTestCase::randomString(). - * - * @param $length - * Length of random string to generate. - * - * @return - * Randomly generated string. - * - * @see DrupalWebTestCase::randomString() - */ - public static function randomName($length = 8) { - $values = array_merge(range(65, 90), range(97, 122), range(48, 57)); - $max = count($values) - 1; - $str = chr(mt_rand(97, 122)); - for ($i = 1; $i < $length; $i++) { - $str .= chr($values[mt_rand(0, $max)]); - } - return $str; - } - - /** - * Converts a list of possible parameters into a stack of permutations. - * - * Takes a list of parameters containing possible values, and converts all of - * them into a list of items containing every possible permutation. - * - * Example: - * @code - * $parameters = array( - * 'one' => array(0, 1), - * 'two' => array(2, 3), - * ); - * $permutations = DrupalTestCase::generatePermutations($parameters) - * // Result: - * $permutations == array( - * array('one' => 0, 'two' => 2), - * array('one' => 1, 'two' => 2), - * array('one' => 0, 'two' => 3), - * array('one' => 1, 'two' => 3), - * ) - * @endcode - * - * @param $parameters - * An associative array of parameters, keyed by parameter name, and whose - * values are arrays of parameter values. - * - * @return - * A list of permutations, which is an array of arrays. Each inner array - * contains the full list of parameters that have been passed, but with a - * single value only. - */ - public static function generatePermutations($parameters) { - $all_permutations = array(array()); - foreach ($parameters as $parameter => $values) { - $new_permutations = array(); - // Iterate over all values of the parameter. - foreach ($values as $value) { - // Iterate over all existing permutations. - foreach ($all_permutations as $permutation) { - // Add the new parameter value to existing permutations. - $new_permutations[] = $permutation + array($parameter => $value); - } - } - // Replace the old permutations with the new permutations. - $all_permutations = $new_permutations; - } - return $all_permutations; - } -} - -/** - * Test case for Drupal unit tests. - * - * These tests can not access the database nor files. Calling any Drupal - * function that needs the database will throw exceptions. These include - * watchdog(), module_implements(), module_invoke_all() etc. - */ -class DrupalUnitTestCase extends DrupalTestCase { - - /** - * Constructor for DrupalUnitTestCase. - */ - function __construct($test_id = NULL) { - parent::__construct($test_id); - $this->skipClasses[__CLASS__] = TRUE; - } - - /** - * Sets up unit test environment. - * - * Unlike DrupalWebTestCase::setUp(), DrupalUnitTestCase::setUp() does not - * install modules because tests are performed without accessing the database. - * Any required files must be explicitly included by the child class setUp() - * method. - */ - protected function setUp() { - global $conf; - - // Store necessary current values before switching to the test environment. - $this->originalFileDirectory = variable_get('file_public_path', conf_path() . '/files'); - - // Reset all statics so that test is performed with a clean environment. - drupal_static_reset(); - - // Generate temporary prefixed database to ensure that tests have a clean starting point. - $this->databasePrefix = Database::getConnection()->prefixTables('{simpletest' . mt_rand(1000, 1000000) . '}'); - - // Create test directory. - $public_files_directory = $this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10); - file_prepare_directory($public_files_directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); - $conf['file_public_path'] = $public_files_directory; - - // Clone the current connection and replace the current prefix. - $connection_info = Database::getConnectionInfo('default'); - Database::renameConnection('default', 'simpletest_original_default'); - foreach ($connection_info as $target => $value) { - $connection_info[$target]['prefix'] = array( - 'default' => $value['prefix']['default'] . $this->databasePrefix, - ); - } - Database::addConnectionInfo('default', 'default', $connection_info['default']); - - // Set user agent to be consistent with web test case. - $_SERVER['HTTP_USER_AGENT'] = $this->databasePrefix; - - // If locale is enabled then t() will try to access the database and - // subsequently will fail as the database is not accessible. - $module_list = module_list(); - if (isset($module_list['locale'])) { - // Transform the list into the format expected as input to module_list(). - foreach ($module_list as &$module) { - $module = array('filename' => drupal_get_filename('module', $module)); - } - $this->originalModuleList = $module_list; - unset($module_list['locale']); - module_list(TRUE, FALSE, FALSE, $module_list); - } - $this->setup = TRUE; - } - - protected function tearDown() { - global $conf; - - // Get back to the original connection. - Database::removeConnection('default'); - Database::renameConnection('simpletest_original_default', 'default'); - - $conf['file_public_path'] = $this->originalFileDirectory; - // Restore modules if necessary. - if (isset($this->originalModuleList)) { - module_list(TRUE, FALSE, FALSE, $this->originalModuleList); - } - } -} - -/** - * Test case for typical Drupal tests. - */ -class DrupalWebTestCase extends DrupalTestCase { - /** - * The profile to install as a basis for testing. - * - * @var string - */ - protected $profile = 'standard'; - - /** - * The URL currently loaded in the internal browser. - * - * @var string - */ - protected $url; - - /** - * The handle of the current cURL connection. - * - * @var resource - */ - protected $curlHandle; - - /** - * The headers of the page currently loaded in the internal browser. - * - * @var Array - */ - protected $headers; - - /** - * The content of the page currently loaded in the internal browser. - * - * @var string - */ - protected $content; - - /** - * The content of the page currently loaded in the internal browser (plain text version). - * - * @var string - */ - protected $plainTextContent; - - /** - * The value of the Drupal.settings JavaScript variable for the page currently loaded in the internal browser. - * - * @var Array - */ - protected $drupalSettings; - - /** - * The parsed version of the page. - * - * @var SimpleXMLElement - */ - protected $elements = NULL; - - /** - * The current user logged in using the internal browser. - * - * @var bool - */ - protected $loggedInUser = FALSE; - - /** - * The current cookie file used by cURL. - * - * We do not reuse the cookies in further runs, so we do not need a file - * but we still need cookie handling, so we set the jar to NULL. - */ - protected $cookieFile = NULL; - - /** - * Additional cURL options. - * - * DrupalWebTestCase itself never sets this but always obeys what is set. - */ - protected $additionalCurlOptions = array(); - - /** - * The original user, before it was changed to a clean uid = 1 for testing purposes. - * - * @var object - */ - protected $originalUser = NULL; - - /** - * The original shutdown handlers array, before it was cleaned for testing purposes. - * - * @var array - */ - protected $originalShutdownCallbacks = array(); - - /** - * HTTP authentication method - */ - protected $httpauth_method = CURLAUTH_BASIC; - - /** - * HTTP authentication credentials (:). - */ - protected $httpauth_credentials = NULL; - - /** - * The current session name, if available. - */ - protected $session_name = NULL; - - /** - * The current session ID, if available. - */ - protected $session_id = NULL; - - /** - * Whether the files were copied to the test files directory. - */ - protected $generatedTestFiles = FALSE; - - /** - * The number of redirects followed during the handling of a request. - */ - protected $redirect_count; - - /** - * Constructor for DrupalWebTestCase. - */ - function __construct($test_id = NULL) { - parent::__construct($test_id); - $this->skipClasses[__CLASS__] = TRUE; - } - - /** - * Get a node from the database based on its title. - * - * @param $title - * A node title, usually generated by $this->randomName(). - * @param $reset - * (optional) Whether to reset the internal node_load() cache. - * - * @return - * A node object matching $title. - */ - function drupalGetNodeByTitle($title, $reset = FALSE) { - $nodes = node_load_multiple(array(), array('title' => $title), $reset); - // Load the first node returned from the database. - $returned_node = reset($nodes); - return $returned_node; - } - - /** - * Creates a node based on default settings. - * - * @param $settings - * An associative array of settings to change from the defaults, keys are - * node properties, for example 'title' => 'Hello, world!'. - * @return - * Created node object. - */ - protected function drupalCreateNode($settings = array()) { - // Populate defaults array. - $settings += array( - 'body' => array(LANGUAGE_NONE => array(array())), - 'title' => $this->randomName(8), - 'comment' => 2, - 'changed' => REQUEST_TIME, - 'moderate' => 0, - 'promote' => 0, - 'revision' => 1, - 'log' => '', - 'status' => 1, - 'sticky' => 0, - 'type' => 'page', - 'revisions' => NULL, - 'language' => LANGUAGE_NONE, - ); - - // Use the original node's created time for existing nodes. - if (isset($settings['created']) && !isset($settings['date'])) { - $settings['date'] = format_date($settings['created'], 'custom', 'Y-m-d H:i:s O'); - } - - // If the node's user uid is not specified manually, use the currently - // logged in user if available, or else the user running the test. - if (!isset($settings['uid'])) { - if ($this->loggedInUser) { - $settings['uid'] = $this->loggedInUser->uid; - } - else { - global $user; - $settings['uid'] = $user->uid; - } - } - - // Merge body field value and format separately. - $body = array( - 'value' => $this->randomName(32), - 'format' => filter_default_format(), - ); - $settings['body'][$settings['language']][0] += $body; - - $node = (object) $settings; - node_save($node); - - // Small hack to link revisions to our test user. - db_update('node_revision') - ->fields(array('uid' => $node->uid)) - ->condition('vid', $node->vid) - ->execute(); - return $node; - } - - /** - * Creates a custom content type based on default settings. - * - * @param $settings - * An array of settings to change from the defaults. - * Example: 'type' => 'foo'. - * @return - * Created content type. - */ - protected function drupalCreateContentType($settings = array()) { - // Find a non-existent random type name. - do { - $name = strtolower($this->randomName(8)); - } while (node_type_get_type($name)); - - // Populate defaults array. - $defaults = array( - 'type' => $name, - 'name' => $name, - 'base' => 'node_content', - 'description' => '', - 'help' => '', - 'title_label' => 'Title', - 'body_label' => 'Body', - 'has_title' => 1, - 'has_body' => 1, - ); - // Imposed values for a custom type. - $forced = array( - 'orig_type' => '', - 'old_type' => '', - 'module' => 'node', - 'custom' => 1, - 'modified' => 1, - 'locked' => 0, - ); - $type = $forced + $settings + $defaults; - $type = (object) $type; - - $saved_type = node_type_save($type); - node_types_rebuild(); - menu_rebuild(); - node_add_body_field($type); - - $this->assertEqual($saved_type, SAVED_NEW, t('Created content type %type.', array('%type' => $type->type))); - - // Reset permissions so that permissions for this content type are available. - $this->checkPermissions(array(), TRUE); - - return $type; - } - - /** - * Get a list files that can be used in tests. - * - * @param $type - * File type, possible values: 'binary', 'html', 'image', 'javascript', 'php', 'sql', 'text'. - * @param $size - * File size in bytes to match. Please check the tests/files folder. - * @return - * List of files that match filter. - */ - protected function drupalGetTestFiles($type, $size = NULL) { - if (empty($this->generatedTestFiles)) { - // Generate binary test files. - $lines = array(64, 1024); - $count = 0; - foreach ($lines as $line) { - simpletest_generate_file('binary-' . $count++, 64, $line, 'binary'); - } - - // Generate text test files. - $lines = array(16, 256, 1024, 2048, 20480); - $count = 0; - foreach ($lines as $line) { - simpletest_generate_file('text-' . $count++, 64, $line); - } - - // Copy other test files from simpletest. - $original = drupal_get_path('module', 'simpletest') . '/files'; - $files = file_scan_directory($original, '/(html|image|javascript|php|sql)-.*/'); - foreach ($files as $file) { - file_unmanaged_copy($file->uri, variable_get('file_public_path', conf_path() . '/files')); - } - - $this->generatedTestFiles = TRUE; - } - - $files = array(); - // Make sure type is valid. - if (in_array($type, array('binary', 'html', 'image', 'javascript', 'php', 'sql', 'text'))) { - $files = file_scan_directory('public://', '/' . $type . '\-.*/'); - - // If size is set then remove any files that are not of that size. - if ($size !== NULL) { - foreach ($files as $file) { - $stats = stat($file->uri); - if ($stats['size'] != $size) { - unset($files[$file->uri]); - } - } - } - } - usort($files, array($this, 'drupalCompareFiles')); - return $files; - } - - /** - * Compare two files based on size and file name. - */ - protected function drupalCompareFiles($file1, $file2) { - $compare_size = filesize($file1->uri) - filesize($file2->uri); - if ($compare_size) { - // Sort by file size. - return $compare_size; - } - else { - // The files were the same size, so sort alphabetically. - return strnatcmp($file1->name, $file2->name); - } - } - - /** - * Create a user with a given set of permissions. - * - * @param array $permissions - * Array of permission names to assign to user. Note that the user always - * has the default permissions derived from the "authenticated users" role. - * - * @return object|false - * A fully loaded user object with pass_raw property, or FALSE if account - * creation fails. - */ - protected function drupalCreateUser(array $permissions = array()) { - // Create a role with the given permission set, if any. - $rid = FALSE; - if ($permissions) { - $rid = $this->drupalCreateRole($permissions); - if (!$rid) { - return FALSE; - } - } - - // Create a user assigned to that role. - $edit = array(); - $edit['name'] = $this->randomName(); - $edit['mail'] = $edit['name'] . '@example.com'; - $edit['pass'] = user_password(); - $edit['status'] = 1; - if ($rid) { - $edit['roles'] = array($rid => $rid); - } - - $account = user_save(drupal_anonymous_user(), $edit); - - $this->assertTrue(!empty($account->uid), t('User created with name %name and pass %pass', array('%name' => $edit['name'], '%pass' => $edit['pass'])), t('User login')); - if (empty($account->uid)) { - return FALSE; - } - - // Add the raw password so that we can log in as this user. - $account->pass_raw = $edit['pass']; - return $account; - } - - /** - * Creates a role with specified permissions. - * - * @param $permissions - * Array of permission names to assign to role. - * @param $name - * (optional) String for the name of the role. Defaults to a random string. - * @return - * Role ID of newly created role, or FALSE if role creation failed. - */ - protected function drupalCreateRole(array $permissions, $name = NULL) { - // Generate random name if it was not passed. - if (!$name) { - $name = $this->randomName(); - } - - // Check the all the permissions strings are valid. - if (!$this->checkPermissions($permissions)) { - return FALSE; - } - - // Create new role. - $role = new stdClass(); - $role->name = $name; - user_role_save($role); - user_role_grant_permissions($role->rid, $permissions); - - $this->assertTrue(isset($role->rid), t('Created role of name: @name, id: @rid', array('@name' => $name, '@rid' => (isset($role->rid) ? $role->rid : t('-n/a-')))), t('Role')); - if ($role && !empty($role->rid)) { - $count = db_query('SELECT COUNT(*) FROM {role_permission} WHERE rid = :rid', array(':rid' => $role->rid))->fetchField(); - $this->assertTrue($count == count($permissions), t('Created permissions: @perms', array('@perms' => implode(', ', $permissions))), t('Role')); - return $role->rid; - } - else { - return FALSE; - } - } - - /** - * Check to make sure that the array of permissions are valid. - * - * @param $permissions - * Permissions to check. - * @param $reset - * Reset cached available permissions. - * @return - * TRUE or FALSE depending on whether the permissions are valid. - */ - protected function checkPermissions(array $permissions, $reset = FALSE) { - $available = &drupal_static(__FUNCTION__); - - if (!isset($available) || $reset) { - $available = array_keys(module_invoke_all('permission')); - } - - $valid = TRUE; - foreach ($permissions as $permission) { - if (!in_array($permission, $available)) { - $this->fail(t('Invalid permission %permission.', array('%permission' => $permission)), t('Role')); - $valid = FALSE; - } - } - return $valid; - } - - /** - * Log in a user with the internal browser. - * - * If a user is already logged in, then the current user is logged out before - * logging in the specified user. - * - * Please note that neither the global $user nor the passed-in user object is - * populated with data of the logged in user. If you need full access to the - * user object after logging in, it must be updated manually. If you also need - * access to the plain-text password of the user (set by drupalCreateUser()), - * e.g. to log in the same user again, then it must be re-assigned manually. - * For example: - * @code - * // Create a user. - * $account = $this->drupalCreateUser(array()); - * $this->drupalLogin($account); - * // Load real user object. - * $pass_raw = $account->pass_raw; - * $account = user_load($account->uid); - * $account->pass_raw = $pass_raw; - * @endcode - * - * @param $account - * User object representing the user to log in. - * - * @see drupalCreateUser() - */ - protected function drupalLogin(stdClass $account) { - if ($this->loggedInUser) { - $this->drupalLogout(); - } - - $edit = array( - 'name' => $account->name, - 'pass' => $account->pass_raw - ); - $this->drupalPost('user', $edit, t('Log in')); - - // If a "log out" link appears on the page, it is almost certainly because - // the login was successful. - $pass = $this->assertLink(t('Log out'), 0, t('User %name successfully logged in.', array('%name' => $account->name)), t('User login')); - - if ($pass) { - $this->loggedInUser = $account; - } - } - - /** - * Generate a token for the currently logged in user. - */ - protected function drupalGetToken($value = '') { - $private_key = drupal_get_private_key(); - return drupal_hmac_base64($value, $this->session_id . $private_key); - } - - /* - * Logs a user out of the internal browser, then check the login page to confirm logout. - */ - protected function drupalLogout() { - // Make a request to the logout page, and redirect to the user page, the - // idea being if you were properly logged out you should be seeing a login - // screen. - $this->drupalGet('user/logout'); - $this->drupalGet('user'); - $pass = $this->assertField('name', t('Username field found.'), t('Logout')); - $pass = $pass && $this->assertField('pass', t('Password field found.'), t('Logout')); - - if ($pass) { - $this->loggedInUser = FALSE; - } - } - - /** - * Generates a database prefix for running tests. - * - * The generated database table prefix is used for the Drupal installation - * being performed for the test. It is also used as user agent HTTP header - * value by the cURL-based browser of DrupalWebTestCase, which is sent - * to the Drupal installation of the test. During early Drupal bootstrap, the - * user agent HTTP header is parsed, and if it matches, all database queries - * use the database table prefix that has been generated here. - * - * @see DrupalWebTestCase::curlInitialize() - * @see drupal_valid_test_ua() - * @see DrupalWebTestCase::setUp() - */ - protected function prepareDatabasePrefix() { - $this->databasePrefix = 'simpletest' . mt_rand(1000, 1000000); - - // As soon as the database prefix is set, the test might start to execute. - // All assertions as well as the SimpleTest batch operations are associated - // with the testId, so the database prefix has to be associated with it. - db_update('simpletest_test_id') - ->fields(array('last_prefix' => $this->databasePrefix)) - ->condition('test_id', $this->testId) - ->execute(); - } - - /** - * Changes the database connection to the prefixed one. - * - * @see DrupalWebTestCase::setUp() - */ - protected function changeDatabasePrefix() { - if (empty($this->databasePrefix)) { - $this->prepareDatabasePrefix(); - // If $this->prepareDatabasePrefix() failed to work, return without - // setting $this->setupDatabasePrefix to TRUE, so setUp() methods will - // know to bail out. - if (empty($this->databasePrefix)) { - return; - } - } - - // Clone the current connection and replace the current prefix. - $connection_info = Database::getConnectionInfo('default'); - Database::renameConnection('default', 'simpletest_original_default'); - foreach ($connection_info as $target => $value) { - $connection_info[$target]['prefix'] = array( - 'default' => $value['prefix']['default'] . $this->databasePrefix, - ); - } - Database::addConnectionInfo('default', 'default', $connection_info['default']); - - // Indicate the database prefix was set up correctly. - $this->setupDatabasePrefix = TRUE; - } - - /** - * Prepares the current environment for running the test. - * - * Backups various current environment variables and resets them, so they do - * not interfere with the Drupal site installation in which tests are executed - * and can be restored in tearDown(). - * - * Also sets up new resources for the testing environment, such as the public - * filesystem and configuration directories. - * - * @see DrupalWebTestCase::setUp() - * @see DrupalWebTestCase::tearDown() - */ - protected function prepareEnvironment() { - global $user, $language, $conf; - - // Store necessary current values before switching to prefixed database. - $this->originalLanguage = $language; - $this->originalLanguageDefault = variable_get('language_default'); - $this->originalFileDirectory = variable_get('file_public_path', conf_path() . '/files'); - $this->originalProfile = drupal_get_profile(); - $this->originalCleanUrl = variable_get('clean_url', 0); - $this->originalUser = $user; - - // Set to English to prevent exceptions from utf8_truncate() from t() - // during install if the current language is not 'en'. - // The following array/object conversion is copied from language_default(). - $language = (object) array('language' => 'en', 'name' => 'English', 'native' => 'English', 'direction' => 0, 'enabled' => 1, 'plurals' => 0, 'formula' => '', 'domain' => '', 'prefix' => '', 'weight' => 0, 'javascript' => ''); - - // Save and clean the shutdown callbacks array because it is static cached - // and will be changed by the test run. Otherwise it will contain callbacks - // from both environments and the testing environment will try to call the - // handlers defined by the original one. - $callbacks = &drupal_register_shutdown_function(); - $this->originalShutdownCallbacks = $callbacks; - $callbacks = array(); - - // Create test directory ahead of installation so fatal errors and debug - // information can be logged during installation process. - // Use temporary files directory with the same prefix as the database. - $this->public_files_directory = $this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10); - $this->private_files_directory = $this->public_files_directory . '/private'; - $this->temp_files_directory = $this->private_files_directory . '/temp'; - - // Create the directories - file_prepare_directory($this->public_files_directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); - file_prepare_directory($this->private_files_directory, FILE_CREATE_DIRECTORY); - file_prepare_directory($this->temp_files_directory, FILE_CREATE_DIRECTORY); - $this->generatedTestFiles = FALSE; - - // Log fatal errors. - ini_set('log_errors', 1); - ini_set('error_log', $this->public_files_directory . '/error.log'); - - // Set the test information for use in other parts of Drupal. - $test_info = &$GLOBALS['drupal_test_info']; - $test_info['test_run_id'] = $this->databasePrefix; - $test_info['in_child_site'] = FALSE; - - // Indicate the environment was set up correctly. - $this->setupEnvironment = TRUE; - } - - /** - * Sets up a Drupal site for running functional and integration tests. - * - * Generates a random database prefix and installs Drupal with the specified - * installation profile in DrupalWebTestCase::$profile into the - * prefixed database. Afterwards, installs any additional modules specified by - * the test. - * - * After installation all caches are flushed and several configuration values - * are reset to the values of the parent site executing the test, since the - * default values may be incompatible with the environment in which tests are - * being executed. - * - * @param ... - * List of modules to enable for the duration of the test. This can be - * either a single array or a variable number of string arguments. - * - * @see DrupalWebTestCase::prepareDatabasePrefix() - * @see DrupalWebTestCase::changeDatabasePrefix() - * @see DrupalWebTestCase::prepareEnvironment() - */ - protected function setUp() { - global $user, $language, $conf; - - // Create the database prefix for this test. - $this->prepareDatabasePrefix(); - - // Prepare the environment for running tests. - $this->prepareEnvironment(); - if (!$this->setupEnvironment) { - return FALSE; - } - - // Reset all statics and variables to perform tests in a clean environment. - $conf = array(); - drupal_static_reset(); - - // Change the database prefix. - // All static variables need to be reset before the database prefix is - // changed, since DrupalCacheArray implementations attempt to - // write back to persistent caches when they are destructed. - $this->changeDatabasePrefix(); - if (!$this->setupDatabasePrefix) { - return FALSE; - } - - // Preset the 'install_profile' system variable, so the first call into - // system_rebuild_module_data() (in drupal_install_system()) will register - // the test's profile as a module. Without this, the installation profile of - // the parent site (executing the test) is registered, and the test - // profile's hook_install() and other hook implementations are never invoked. - $conf['install_profile'] = $this->profile; - - // Perform the actual Drupal installation. - include_once DRUPAL_ROOT . '/includes/install.inc'; - drupal_install_system(); - - $this->preloadRegistry(); - - // Set path variables. - variable_set('file_public_path', $this->public_files_directory); - variable_set('file_private_path', $this->private_files_directory); - variable_set('file_temporary_path', $this->temp_files_directory); - - // Set the 'simpletest_parent_profile' variable to add the parent profile's - // search path to the child site's search paths. - // @see drupal_system_listing() - // @todo This may need to be primed like 'install_profile' above. - variable_set('simpletest_parent_profile', $this->originalProfile); - - // Include the testing profile. - variable_set('install_profile', $this->profile); - $profile_details = install_profile_info($this->profile, 'en'); - - // Install the modules specified by the testing profile. - module_enable($profile_details['dependencies'], FALSE); - - // Install modules needed for this test. This could have been passed in as - // either a single array argument or a variable number of string arguments. - // @todo Remove this compatibility layer in Drupal 8, and only accept - // $modules as a single array argument. - $modules = func_get_args(); - if (isset($modules[0]) && is_array($modules[0])) { - $modules = $modules[0]; - } - if ($modules) { - $success = module_enable($modules, TRUE); - $this->assertTrue($success, t('Enabled modules: %modules', array('%modules' => implode(', ', $modules)))); - } - - // Run the profile tasks. - $install_profile_module_exists = db_query("SELECT 1 FROM {system} WHERE type = 'module' AND name = :name", array( - ':name' => $this->profile, - ))->fetchField(); - if ($install_profile_module_exists) { - module_enable(array($this->profile), FALSE); - } - - // Reset/rebuild all data structures after enabling the modules. - $this->resetAll(); - - // Run cron once in that environment, as install.php does at the end of - // the installation process. - drupal_cron_run(); - - // Ensure that the session is not written to the new environment and replace - // the global $user session with uid 1 from the new test site. - drupal_save_session(FALSE); - // Login as uid 1. - $user = user_load(1); - - // Restore necessary variables. - variable_set('install_task', 'done'); - variable_set('clean_url', $this->originalCleanUrl); - variable_set('site_mail', 'simpletest@example.com'); - variable_set('date_default_timezone', date_default_timezone_get()); - - // Set up English language. - unset($conf['language_default']); - $language = language_default(); - - // Use the test mail class instead of the default mail handler class. - variable_set('mail_system', array('default-system' => 'TestingMailSystem')); - - drupal_set_time_limit($this->timeLimit); - $this->setup = TRUE; - } - - /** - * Preload the registry from the testing site. - * - * This method is called by DrupalWebTestCase::setUp(), and preloads the - * registry from the testing site to cut down on the time it takes to - * set up a clean environment for the current test run. - */ - protected function preloadRegistry() { - // Use two separate queries, each with their own connections: copy the - // {registry} and {registry_file} tables over from the parent installation - // to the child installation. - $original_connection = Database::getConnection('default', 'simpletest_original_default'); - $test_connection = Database::getConnection(); - - foreach (array('registry', 'registry_file') as $table) { - // Find the records from the parent database. - $source_query = $original_connection - ->select($table, array(), array('fetch' => PDO::FETCH_ASSOC)) - ->fields($table); - - $dest_query = $test_connection->insert($table); - - $first = TRUE; - foreach ($source_query->execute() as $row) { - if ($first) { - $dest_query->fields(array_keys($row)); - $first = FALSE; - } - // Insert the records into the child database. - $dest_query->values($row); - } - - $dest_query->execute(); - } - } - - /** - * Reset all data structures after having enabled new modules. - * - * This method is called by DrupalWebTestCase::setUp() after enabling - * the requested modules. It must be called again when additional modules - * are enabled later. - */ - protected function resetAll() { - // Reset all static variables. - drupal_static_reset(); - // Reset the list of enabled modules. - module_list(TRUE); - - // Reset cached schema for new database prefix. This must be done before - // drupal_flush_all_caches() so rebuilds can make use of the schema of - // modules enabled on the cURL side. - drupal_get_schema(NULL, TRUE); - - // Perform rebuilds and flush remaining caches. - drupal_flush_all_caches(); - - // Reload global $conf array and permissions. - $this->refreshVariables(); - $this->checkPermissions(array(), TRUE); - } - - /** - * Refresh the in-memory set of variables. Useful after a page request is made - * that changes a variable in a different thread. - * - * In other words calling a settings page with $this->drupalPost() with a changed - * value would update a variable to reflect that change, but in the thread that - * made the call (thread running the test) the changed variable would not be - * picked up. - * - * This method clears the variables cache and loads a fresh copy from the database - * to ensure that the most up-to-date set of variables is loaded. - */ - protected function refreshVariables() { - global $conf; - cache_clear_all('variables', 'cache_bootstrap'); - $conf = variable_initialize(); - } - - /** - * Delete created files and temporary files directory, delete the tables created by setUp(), - * and reset the database prefix. - */ - protected function tearDown() { - global $user, $language; - - // In case a fatal error occurred 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); - - $emailCount = count(variable_get('drupal_test_email_collector', array())); - if ($emailCount) { - $message = format_plural($emailCount, '1 e-mail was sent during this test.', '@count e-mails were sent during this test.'); - $this->pass($message, t('E-mail')); - } - - // Delete temporary files directory. - file_unmanaged_delete_recursive($this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10)); - - // Remove all prefixed tables. - $tables = db_find_tables($this->databasePrefix . '%'); - $connection_info = Database::getConnectionInfo('default'); - $tables = db_find_tables($connection_info['default']['prefix']['default'] . '%'); - if (empty($tables)) { - $this->fail('Failed to find test tables to drop.'); - } - $prefix_length = strlen($connection_info['default']['prefix']['default']); - foreach ($tables as $table) { - if (db_drop_table(substr($table, $prefix_length))) { - unset($tables[$table]); - } - } - if (!empty($tables)) { - $this->fail('Failed to drop all prefixed tables.'); - } - - // Get back to the original connection. - Database::removeConnection('default'); - Database::renameConnection('simpletest_original_default', 'default'); - - // Restore original shutdown callbacks array to prevent original - // environment of calling handlers from test run. - $callbacks = &drupal_register_shutdown_function(); - $callbacks = $this->originalShutdownCallbacks; - - // Return the user to the original one. - $user = $this->originalUser; - drupal_save_session(TRUE); - - // Ensure that internal logged in variable and cURL options are reset. - $this->loggedInUser = FALSE; - $this->additionalCurlOptions = array(); - - // Reload module list and implementations to ensure that test module hooks - // aren't called after tests. - module_list(TRUE); - module_implements('', FALSE, TRUE); - - // Reset the Field API. - field_cache_clear(); - - // Rebuild caches. - $this->refreshVariables(); - - // Reset public files directory. - $GLOBALS['conf']['file_public_path'] = $this->originalFileDirectory; - - // Reset language. - $language = $this->originalLanguage; - if ($this->originalLanguageDefault) { - $GLOBALS['conf']['language_default'] = $this->originalLanguageDefault; - } - - // Close the CURL handler. - $this->curlClose(); - } - - /** - * Initializes the cURL connection. - * - * If the simpletest_httpauth_credentials variable is set, this function will - * add HTTP authentication headers. This is necessary for testing sites that - * are protected by login credentials from public access. - * See the description of $curl_options for other options. - */ - protected function curlInitialize() { - global $base_url; - - if (!isset($this->curlHandle)) { - $this->curlHandle = curl_init(); - - // Some versions/configurations of cURL break on a NULL cookie jar, so - // supply a real file. - if (empty($this->cookieFile)) { - $this->cookieFile = $this->public_files_directory . '/cookie.jar'; - } - - $curl_options = array( - CURLOPT_COOKIEJAR => $this->cookieFile, - CURLOPT_URL => $base_url, - CURLOPT_FOLLOWLOCATION => FALSE, - CURLOPT_RETURNTRANSFER => TRUE, - CURLOPT_SSL_VERIFYPEER => FALSE, // Required to make the tests run on HTTPS. - CURLOPT_SSL_VERIFYHOST => FALSE, // Required to make the tests run on HTTPS. - CURLOPT_HEADERFUNCTION => array(&$this, 'curlHeaderCallback'), - CURLOPT_USERAGENT => $this->databasePrefix, - ); - if (isset($this->httpauth_credentials)) { - $curl_options[CURLOPT_HTTPAUTH] = $this->httpauth_method; - $curl_options[CURLOPT_USERPWD] = $this->httpauth_credentials; - } - // curl_setopt_array() returns FALSE if any of the specified options - // cannot be set, and stops processing any further options. - $result = curl_setopt_array($this->curlHandle, $this->additionalCurlOptions + $curl_options); - if (!$result) { - throw new Exception('One or more cURL options could not be set.'); - } - - // By default, the child session name should be the same as the parent. - $this->session_name = session_name(); - } - // We set the user agent header on each request so as to use the current - // time and a new uniqid. - if (preg_match('/simpletest\d+/', $this->databasePrefix, $matches)) { - curl_setopt($this->curlHandle, CURLOPT_USERAGENT, drupal_generate_test_ua($matches[0])); - } - } - - /** - * Initializes and executes a cURL request. - * - * @param $curl_options - * An associative array of cURL options to set, where the keys are constants - * defined by the cURL library. For a list of valid options, see - * http://www.php.net/manual/function.curl-setopt.php - * @param $redirect - * FALSE if this is an initial request, TRUE if this request is the result - * of a redirect. - * - * @return - * The content returned from the call to curl_exec(). - * - * @see curlInitialize() - */ - protected function curlExec($curl_options, $redirect = FALSE) { - $this->curlInitialize(); - - if (!empty($curl_options[CURLOPT_URL])) { - // Forward XDebug activation if present. - if (isset($_COOKIE['XDEBUG_SESSION'])) { - $options = drupal_parse_url($curl_options[CURLOPT_URL]); - $options += array('query' => array()); - $options['query'] += array('XDEBUG_SESSION_START' => $_COOKIE['XDEBUG_SESSION']); - $curl_options[CURLOPT_URL] = url($options['path'], $options); - } - - // cURL incorrectly handles URLs with a fragment by including the - // fragment in the request to the server, causing some web servers - // to reject the request citing "400 - Bad Request". To prevent - // this, we strip the fragment from the request. - // TODO: Remove this for Drupal 8, since fixed in curl 7.20.0. - if (strpos($curl_options[CURLOPT_URL], '#')) { - $original_url = $curl_options[CURLOPT_URL]; - $curl_options[CURLOPT_URL] = strtok($curl_options[CURLOPT_URL], '#'); - } - } - - $url = empty($curl_options[CURLOPT_URL]) ? curl_getinfo($this->curlHandle, CURLINFO_EFFECTIVE_URL) : $curl_options[CURLOPT_URL]; - - if (!empty($curl_options[CURLOPT_POST])) { - // This is a fix for the Curl library to prevent Expect: 100-continue - // headers in POST requests, that may cause unexpected HTTP response - // codes from some webservers (like lighttpd that returns a 417 error - // code). It is done by setting an empty "Expect" header field that is - // not overwritten by Curl. - $curl_options[CURLOPT_HTTPHEADER][] = 'Expect:'; - } - curl_setopt_array($this->curlHandle, $this->additionalCurlOptions + $curl_options); - - if (!$redirect) { - // Reset headers, the session ID and the redirect counter. - $this->session_id = NULL; - $this->headers = array(); - $this->redirect_count = 0; - } - - $content = curl_exec($this->curlHandle); - $status = curl_getinfo($this->curlHandle, CURLINFO_HTTP_CODE); - - // cURL incorrectly handles URLs with fragments, so instead of - // letting cURL handle redirects we take of them ourselves to - // to prevent fragments being sent to the web server as part - // of the request. - // TODO: Remove this for Drupal 8, since fixed in curl 7.20.0. - if (in_array($status, array(300, 301, 302, 303, 305, 307)) && $this->redirect_count < variable_get('simpletest_maximum_redirects', 5)) { - if ($this->drupalGetHeader('location')) { - $this->redirect_count++; - $curl_options = array(); - $curl_options[CURLOPT_URL] = $this->drupalGetHeader('location'); - $curl_options[CURLOPT_HTTPGET] = TRUE; - return $this->curlExec($curl_options, TRUE); - } - } - - $this->drupalSetContent($content, isset($original_url) ? $original_url : curl_getinfo($this->curlHandle, CURLINFO_EFFECTIVE_URL)); - $message_vars = array( - '!method' => !empty($curl_options[CURLOPT_NOBODY]) ? 'HEAD' : (empty($curl_options[CURLOPT_POSTFIELDS]) ? 'GET' : 'POST'), - '@url' => isset($original_url) ? $original_url : $url, - '@status' => $status, - '!length' => format_size(strlen($this->drupalGetContent())) - ); - $message = t('!method @url returned @status (!length).', $message_vars); - $this->assertTrue($this->drupalGetContent() !== FALSE, $message, t('Browser')); - return $this->drupalGetContent(); - } - - /** - * Reads headers and registers errors received from the tested site. - * - * @see _drupal_log_error(). - * - * @param $curlHandler - * The cURL handler. - * @param $header - * An header. - */ - protected function curlHeaderCallback($curlHandler, $header) { - // Header fields can be extended over multiple lines by preceding each - // extra line with at least one SP or HT. They should be joined on receive. - // Details are in RFC2616 section 4. - if ($header[0] == ' ' || $header[0] == "\t") { - // Normalize whitespace between chucks. - $this->headers[] = array_pop($this->headers) . ' ' . trim($header); - } - else { - $this->headers[] = $header; - } - - // Errors are being sent via X-Drupal-Assertion-* headers, - // generated by _drupal_log_error() in the exact form required - // by DrupalWebTestCase::error(). - if (preg_match('/^X-Drupal-Assertion-[0-9]+: (.*)$/', $header, $matches)) { - // Call DrupalWebTestCase::error() with the parameters from the header. - call_user_func_array(array(&$this, 'error'), unserialize(urldecode($matches[1]))); - } - - // Save cookies. - if (preg_match('/^Set-Cookie: ([^=]+)=(.+)/', $header, $matches)) { - $name = $matches[1]; - $parts = array_map('trim', explode(';', $matches[2])); - $value = array_shift($parts); - $this->cookies[$name] = array('value' => $value, 'secure' => in_array('secure', $parts)); - if ($name == $this->session_name) { - if ($value != 'deleted') { - $this->session_id = $value; - } - else { - $this->session_id = NULL; - } - } - } - - // This is required by cURL. - return strlen($header); - } - - /** - * Close the cURL handler and unset the handler. - */ - protected function curlClose() { - if (isset($this->curlHandle)) { - curl_close($this->curlHandle); - unset($this->curlHandle); - } - } - - /** - * Parse content returned from curlExec using DOM and SimpleXML. - * - * @return - * A SimpleXMLElement or FALSE on failure. - */ - protected function parse() { - if (!$this->elements) { - // DOM can load HTML soup. But, HTML soup can throw warnings, suppress - // them. - $htmlDom = new DOMDocument(); - @$htmlDom->loadHTML($this->drupalGetContent()); - if ($htmlDom) { - $this->pass(t('Valid HTML found on "@path"', array('@path' => $this->getUrl())), t('Browser')); - // It's much easier to work with simplexml than DOM, luckily enough - // we can just simply import our DOM tree. - $this->elements = simplexml_import_dom($htmlDom); - } - } - if (!$this->elements) { - $this->fail(t('Parsed page successfully.'), t('Browser')); - } - - return $this->elements; - } - - /** - * Retrieves a Drupal path or an absolute path. - * - * @param $path - * Drupal path or URL to load into internal browser - * @param $options - * Options to be forwarded to url(). - * @param $headers - * An array containing additional HTTP request headers, each formatted as - * "name: value". - * @return - * The retrieved HTML string, also available as $this->drupalGetContent() - */ - protected function drupalGet($path, array $options = array(), array $headers = array()) { - $options['absolute'] = TRUE; - - // We re-using a CURL connection here. If that connection still has certain - // options set, it might change the GET into a POST. Make sure we clear out - // previous options. - $out = $this->curlExec(array(CURLOPT_HTTPGET => TRUE, CURLOPT_URL => url($path, $options), CURLOPT_NOBODY => FALSE, CURLOPT_HTTPHEADER => $headers)); - $this->refreshVariables(); // Ensure that any changes to variables in the other thread are picked up. - - // Replace original page output with new output from redirected page(s). - if ($new = $this->checkForMetaRefresh()) { - $out = $new; - } - $this->verbose('GET request to: ' . $path . - '
    Ending URL: ' . $this->getUrl() . - '
    ' . $out); - return $out; - } - - /** - * Retrieve a Drupal path or an absolute path and JSON decode the result. - */ - protected function drupalGetAJAX($path, array $options = array(), array $headers = array()) { - return drupal_json_decode($this->drupalGet($path, $options, $headers)); - } - - /** - * Execute a POST request on a Drupal page. - * It will be done as usual POST request with SimpleBrowser. - * - * @param $path - * Location of the post form. Either a Drupal path or an absolute path or - * NULL to post to the current page. For multi-stage forms you can set the - * path to NULL and have it post to the last received page. Example: - * - * @code - * // First step in form. - * $edit = array(...); - * $this->drupalPost('some_url', $edit, t('Save')); - * - * // Second step in form. - * $edit = array(...); - * $this->drupalPost(NULL, $edit, t('Save')); - * @endcode - * @param $edit - * Field data in an associative array. Changes the current input fields - * (where possible) to the values indicated. A checkbox can be set to - * TRUE to be checked and FALSE to be unchecked. Note that when a form - * contains file upload fields, other fields cannot start with the '@' - * character. - * - * Multiple select fields can be set using name[] and setting each of the - * possible values. Example: - * @code - * $edit = array(); - * $edit['name[]'] = array('value1', 'value2'); - * @endcode - * @param $submit - * Value of the submit button whose click is to be emulated. For example, - * t('Save'). The processing of the request depends on this value. For - * example, a form may have one button with the value t('Save') and another - * button with the value t('Delete'), and execute different code depending - * on which one is clicked. - * - * This function can also be called to emulate an Ajax submission. In this - * case, this value needs to be an array with the following keys: - * - path: A path to submit the form values to for Ajax-specific processing, - * which is likely different than the $path parameter used for retrieving - * the initial form. Defaults to 'system/ajax'. - * - triggering_element: If the value for the 'path' key is 'system/ajax' or - * another generic Ajax processing path, this needs to be set to the name - * of the element. If the name doesn't identify the element uniquely, then - * this should instead be an array with a single key/value pair, - * corresponding to the element name and value. The callback for the - * generic Ajax processing path uses this to find the #ajax information - * for the element, including which specific callback to use for - * processing the request. - * - * This can also be set to NULL in order to emulate an Internet Explorer - * submission of a form with a single text field, and pressing ENTER in that - * textfield: under these conditions, no button information is added to the - * POST data. - * @param $options - * Options to be forwarded to url(). - * @param $headers - * An array containing additional HTTP request headers, each formatted as - * "name: value". - * @param $form_html_id - * (optional) HTML ID of the form to be submitted. On some pages - * there are many identical forms, so just using the value of the submit - * button is not enough. For example: 'trigger-node-presave-assign-form'. - * Note that this is not the Drupal $form_id, but rather the HTML ID of the - * form, which is typically the same thing but with hyphens replacing the - * underscores. - * @param $extra_post - * (optional) A string of additional data to append to the POST submission. - * This can be used to add POST data for which there are no HTML fields, as - * is done by drupalPostAJAX(). This string is literally appended to the - * POST data, so it must already be urlencoded and contain a leading "&" - * (e.g., "&extra_var1=hello+world&extra_var2=you%26me"). - */ - protected function drupalPost($path, $edit, $submit, array $options = array(), array $headers = array(), $form_html_id = NULL, $extra_post = NULL) { - $submit_matches = FALSE; - $ajax = is_array($submit); - if (isset($path)) { - $this->drupalGet($path, $options); - } - if ($this->parse()) { - $edit_save = $edit; - // Let's iterate over all the forms. - $xpath = "//form"; - if (!empty($form_html_id)) { - $xpath .= "[@id='" . $form_html_id . "']"; - } - $forms = $this->xpath($xpath); - foreach ($forms as $form) { - // We try to set the fields of this form as specified in $edit. - $edit = $edit_save; - $post = array(); - $upload = array(); - $submit_matches = $this->handleForm($post, $edit, $upload, $ajax ? NULL : $submit, $form); - $action = isset($form['action']) ? $this->getAbsoluteUrl((string) $form['action']) : $this->getUrl(); - if ($ajax) { - $action = $this->getAbsoluteUrl(!empty($submit['path']) ? $submit['path'] : 'system/ajax'); - // Ajax callbacks verify the triggering element if necessary, so while - // we may eventually want extra code that verifies it in the - // handleForm() function, it's not currently a requirement. - $submit_matches = TRUE; - } - - // We post only if we managed to handle every field in edit and the - // submit button matches. - if (!$edit && ($submit_matches || !isset($submit))) { - $post_array = $post; - if ($upload) { - // TODO: cURL handles file uploads for us, but the implementation - // is broken. This is a less than elegant workaround. Alternatives - // are being explored at #253506. - foreach ($upload as $key => $file) { - $file = drupal_realpath($file); - if ($file && is_file($file)) { - // Use the new CurlFile class for file uploads when using PHP - // 5.5 or higher. - if (class_exists('CurlFile')) { - $post[$key] = curl_file_create($file); - } - else { - $post[$key] = '@' . $file; - } - } - } - } - else { - foreach ($post as $key => $value) { - // Encode according to application/x-www-form-urlencoded - // Both names and values needs to be urlencoded, according to - // http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1 - $post[$key] = urlencode($key) . '=' . urlencode($value); - } - $post = implode('&', $post) . $extra_post; - } - $out = $this->curlExec(array(CURLOPT_URL => $action, CURLOPT_POST => TRUE, CURLOPT_POSTFIELDS => $post, CURLOPT_HTTPHEADER => $headers)); - // Ensure that any changes to variables in the other thread are picked up. - $this->refreshVariables(); - - // Replace original page output with new output from redirected page(s). - if ($new = $this->checkForMetaRefresh()) { - $out = $new; - } - $this->verbose('POST request to: ' . $path . - '
    Ending URL: ' . $this->getUrl() . - '
    Fields: ' . highlight_string('' . $out); - return $out; - } - } - // We have not found a form which contained all fields of $edit. - foreach ($edit as $name => $value) { - $this->fail(t('Failed to set field @name to @value', array('@name' => $name, '@value' => $value))); - } - if (!$ajax && isset($submit)) { - $this->assertTrue($submit_matches, t('Found the @submit button', array('@submit' => $submit))); - } - $this->fail(t('Found the requested form fields at @path', array('@path' => $path))); - } - } - - /** - * Execute an Ajax submission. - * - * This executes a POST as ajax.js does. It uses the returned JSON data, an - * array of commands, to update $this->content using equivalent DOM - * manipulation as is used by ajax.js. It also returns the array of commands. - * - * @param $path - * Location of the form containing the Ajax enabled element to test. Can be - * either a Drupal path or an absolute path or NULL to use the current page. - * @param $edit - * Field data in an associative array. Changes the current input fields - * (where possible) to the values indicated. - * @param $triggering_element - * The name of the form element that is responsible for triggering the Ajax - * functionality to test. May be a string or, if the triggering element is - * a button, an associative array where the key is the name of the button - * and the value is the button label. i.e.) array('op' => t('Refresh')). - * @param $ajax_path - * (optional) Override the path set by the Ajax settings of the triggering - * element. In the absence of both the triggering element's Ajax path and - * $ajax_path 'system/ajax' will be used. - * @param $options - * (optional) Options to be forwarded to url(). - * @param $headers - * (optional) An array containing additional HTTP request headers, each - * formatted as "name: value". Forwarded to drupalPost(). - * @param $form_html_id - * (optional) HTML ID of the form to be submitted, use when there is more - * than one identical form on the same page and the value of the triggering - * element is not enough to identify the form. Note this is not the Drupal - * ID of the form but rather the HTML ID of the form. - * @param $ajax_settings - * (optional) An array of Ajax settings which if specified will be used in - * place of the Ajax settings of the triggering element. - * - * @return - * An array of Ajax commands. - * - * @see drupalPost() - * @see ajax.js - */ - protected function drupalPostAJAX($path, $edit, $triggering_element, $ajax_path = NULL, array $options = array(), array $headers = array(), $form_html_id = NULL, $ajax_settings = NULL) { - // Get the content of the initial page prior to calling drupalPost(), since - // drupalPost() replaces $this->content. - if (isset($path)) { - $this->drupalGet($path, $options); - } - $content = $this->content; - $drupal_settings = $this->drupalSettings; - - // Get the Ajax settings bound to the triggering element. - if (!isset($ajax_settings)) { - if (is_array($triggering_element)) { - $xpath = '//*[@name="' . key($triggering_element) . '" and @value="' . current($triggering_element) . '"]'; - } - else { - $xpath = '//*[@name="' . $triggering_element . '"]'; - } - if (isset($form_html_id)) { - $xpath = '//form[@id="' . $form_html_id . '"]' . $xpath; - } - $element = $this->xpath($xpath); - $element_id = (string) $element[0]['id']; - $ajax_settings = $drupal_settings['ajax'][$element_id]; - } - - // Add extra information to the POST data as ajax.js does. - $extra_post = ''; - if (isset($ajax_settings['submit'])) { - foreach ($ajax_settings['submit'] as $key => $value) { - $extra_post .= '&' . urlencode($key) . '=' . urlencode($value); - } - } - foreach ($this->xpath('//*[@id]') as $element) { - $id = (string) $element['id']; - $extra_post .= '&' . urlencode('ajax_html_ids[]') . '=' . urlencode($id); - } - if (isset($drupal_settings['ajaxPageState'])) { - $extra_post .= '&' . urlencode('ajax_page_state[theme]') . '=' . urlencode($drupal_settings['ajaxPageState']['theme']); - $extra_post .= '&' . urlencode('ajax_page_state[theme_token]') . '=' . urlencode($drupal_settings['ajaxPageState']['theme_token']); - foreach ($drupal_settings['ajaxPageState']['css'] as $key => $value) { - $extra_post .= '&' . urlencode("ajax_page_state[css][$key]") . '=1'; - } - foreach ($drupal_settings['ajaxPageState']['js'] as $key => $value) { - $extra_post .= '&' . urlencode("ajax_page_state[js][$key]") . '=1'; - } - } - - // Unless a particular path is specified, use the one specified by the - // Ajax settings, or else 'system/ajax'. - if (!isset($ajax_path)) { - $ajax_path = isset($ajax_settings['url']) ? $ajax_settings['url'] : 'system/ajax'; - } - - // Submit the POST request. - $return = drupal_json_decode($this->drupalPost(NULL, $edit, array('path' => $ajax_path, 'triggering_element' => $triggering_element), $options, $headers, $form_html_id, $extra_post)); - - // Change the page content by applying the returned commands. - if (!empty($ajax_settings) && !empty($return)) { - // ajax.js applies some defaults to the settings object, so do the same - // for what's used by this function. - $ajax_settings += array( - 'method' => 'replaceWith', - ); - // DOM can load HTML soup. But, HTML soup can throw warnings, suppress - // them. - $dom = new DOMDocument(); - @$dom->loadHTML($content); - // XPath allows for finding wrapper nodes better than DOM does. - $xpath = new DOMXPath($dom); - foreach ($return as $command) { - switch ($command['command']) { - case 'settings': - $drupal_settings = drupal_array_merge_deep($drupal_settings, $command['settings']); - break; - - case 'insert': - $wrapperNode = NULL; - // When a command doesn't specify a selector, use the - // #ajax['wrapper'] which is always an HTML ID. - if (!isset($command['selector'])) { - $wrapperNode = $xpath->query('//*[@id="' . $ajax_settings['wrapper'] . '"]')->item(0); - } - // @todo Ajax commands can target any jQuery selector, but these are - // hard to fully emulate with XPath. For now, just handle 'head' - // and 'body', since these are used by ajax_render(). - elseif (in_array($command['selector'], array('head', 'body'))) { - $wrapperNode = $xpath->query('//' . $command['selector'])->item(0); - } - if ($wrapperNode) { - // ajax.js adds an enclosing DIV to work around a Safari bug. - $newDom = new DOMDocument(); - $newDom->loadHTML('
    ' . $command['data'] . '
    '); - $newNode = $dom->importNode($newDom->documentElement->firstChild->firstChild, TRUE); - $method = isset($command['method']) ? $command['method'] : $ajax_settings['method']; - // The "method" is a jQuery DOM manipulation function. Emulate - // each one using PHP's DOMNode API. - switch ($method) { - case 'replaceWith': - $wrapperNode->parentNode->replaceChild($newNode, $wrapperNode); - break; - case 'append': - $wrapperNode->appendChild($newNode); - break; - case 'prepend': - // If no firstChild, insertBefore() falls back to - // appendChild(). - $wrapperNode->insertBefore($newNode, $wrapperNode->firstChild); - break; - case 'before': - $wrapperNode->parentNode->insertBefore($newNode, $wrapperNode); - break; - case 'after': - // If no nextSibling, insertBefore() falls back to - // appendChild(). - $wrapperNode->parentNode->insertBefore($newNode, $wrapperNode->nextSibling); - break; - case 'html': - foreach ($wrapperNode->childNodes as $childNode) { - $wrapperNode->removeChild($childNode); - } - $wrapperNode->appendChild($newNode); - break; - } - } - break; - - case 'updateBuildId': - $buildId = $xpath->query('//input[@name="form_build_id" and @value="' . $command['old'] . '"]')->item(0); - if ($buildId) { - $buildId->setAttribute('value', $command['new']); - } - break; - - // @todo Add suitable implementations for these commands in order to - // have full test coverage of what ajax.js can do. - case 'remove': - break; - case 'changed': - break; - case 'css': - break; - case 'data': - break; - case 'restripe': - break; - case 'add_css': - break; - } - } - $content = $dom->saveHTML(); - } - $this->drupalSetContent($content); - $this->drupalSetSettings($drupal_settings); - - $verbose = 'AJAX POST request to: ' . $path; - $verbose .= '
    AJAX callback path: ' . $ajax_path; - $verbose .= '
    Ending URL: ' . $this->getUrl(); - $verbose .= '
    ' . $this->content; - - $this->verbose($verbose); - - return $return; - } - - /** - * Runs cron in the Drupal installed by Simpletest. - */ - protected function cronRun() { - $this->drupalGet($GLOBALS['base_url'] . '/cron.php', array('external' => TRUE, 'query' => array('cron_key' => variable_get('cron_key', 'drupal')))); - } - - /** - * Check for meta refresh tag and if found call drupalGet() recursively. This - * function looks for the http-equiv attribute to be set to "Refresh" - * and is case-sensitive. - * - * @return - * Either the new page content or FALSE. - */ - protected function checkForMetaRefresh() { - if (strpos($this->drupalGetContent(), 'parse()) { - $refresh = $this->xpath('//meta[@http-equiv="Refresh"]'); - if (!empty($refresh)) { - // Parse the content attribute of the meta tag for the format: - // "[delay]: URL=[page_to_redirect_to]". - if (preg_match('/\d+;\s*URL=(?P.*)/i', $refresh[0]['content'], $match)) { - return $this->drupalGet($this->getAbsoluteUrl(decode_entities($match['url']))); - } - } - } - return FALSE; - } - - /** - * Retrieves only the headers for a Drupal path or an absolute path. - * - * @param $path - * Drupal path or URL to load into internal browser - * @param $options - * Options to be forwarded to url(). - * @param $headers - * An array containing additional HTTP request headers, each formatted as - * "name: value". - * @return - * The retrieved headers, also available as $this->drupalGetContent() - */ - protected function drupalHead($path, array $options = array(), array $headers = array()) { - $options['absolute'] = TRUE; - $out = $this->curlExec(array(CURLOPT_NOBODY => TRUE, CURLOPT_URL => url($path, $options), CURLOPT_HTTPHEADER => $headers)); - $this->refreshVariables(); // Ensure that any changes to variables in the other thread are picked up. - return $out; - } - - /** - * Handle form input related to drupalPost(). Ensure that the specified fields - * exist and attempt to create POST data in the correct manner for the particular - * field type. - * - * @param $post - * Reference to array of post values. - * @param $edit - * Reference to array of edit values to be checked against the form. - * @param $submit - * Form submit button value. - * @param $form - * Array of form elements. - * @return - * Submit value matches a valid submit input in the form. - */ - protected function handleForm(&$post, &$edit, &$upload, $submit, $form) { - // Retrieve the form elements. - $elements = $form->xpath('.//input[not(@disabled)]|.//textarea[not(@disabled)]|.//select[not(@disabled)]'); - $submit_matches = FALSE; - foreach ($elements as $element) { - // SimpleXML objects need string casting all the time. - $name = (string) $element['name']; - // This can either be the type of or the name of the tag itself - // for +

    + + + + + + + + + +cap->manage_terms ) ) + wp_die( __( 'Cheatin’ uh?' ), 403 ); + +$wp_list_table = _get_list_table('WP_Terms_List_Table'); +$pagenum = $wp_list_table->get_pagenum(); + +$title = $tax->labels->name; + +if ( 'post' != $post_type ) { + $parent_file = ( 'attachment' == $post_type ) ? 'upload.php' : "edit.php?post_type=$post_type"; + $submenu_file = "edit-tags.php?taxonomy=$taxonomy&post_type=$post_type"; +} else if ( 'link_category' == $tax->name ) { + $parent_file = 'link-manager.php'; + $submenu_file = 'edit-tags.php?taxonomy=link_category'; +} else { + $parent_file = 'edit.php'; + $submenu_file = "edit-tags.php?taxonomy=$taxonomy"; +} + +add_screen_option( 'per_page', array( 'label' => $title, 'default' => 20, 'option' => 'edit_' . $tax->name . '_per_page' ) ); + +$location = false; + +switch ( $wp_list_table->current_action() ) { + +case 'add-tag': + + check_admin_referer( 'add-tag', '_wpnonce_add-tag' ); + + if ( !current_user_can( $tax->cap->edit_terms ) ) + wp_die( __( 'Cheatin’ uh?' ), 403 ); + + $ret = wp_insert_term( $_POST['tag-name'], $taxonomy, $_POST ); + $location = 'edit-tags.php?taxonomy=' . $taxonomy; + if ( 'post' != $post_type ) + $location .= '&post_type=' . $post_type; + + if ( $referer = wp_get_original_referer() ) { + if ( false !== strpos( $referer, 'edit-tags.php' ) ) + $location = $referer; + } + + if ( $ret && !is_wp_error( $ret ) ) + $location = add_query_arg( 'message', 1, $location ); + else + $location = add_query_arg( 'message', 4, $location ); + + break; + +case 'delete': + $location = 'edit-tags.php?taxonomy=' . $taxonomy; + if ( 'post' != $post_type ) + $location .= '&post_type=' . $post_type; + if ( $referer = wp_get_referer() ) { + if ( false !== strpos( $referer, 'edit-tags.php' ) ) + $location = $referer; + } + + if ( ! isset( $_REQUEST['tag_ID'] ) ) { + break; + } + + $tag_ID = (int) $_REQUEST['tag_ID']; + check_admin_referer( 'delete-tag_' . $tag_ID ); + + if ( !current_user_can( $tax->cap->delete_terms ) ) + wp_die( __( 'Cheatin’ uh?' ), 403 ); + + wp_delete_term( $tag_ID, $taxonomy ); + + $location = add_query_arg( 'message', 2, $location ); + + break; + +case 'bulk-delete': + check_admin_referer( 'bulk-tags' ); + + if ( !current_user_can( $tax->cap->delete_terms ) ) + wp_die( __( 'Cheatin’ uh?' ), 403 ); + + $tags = (array) $_REQUEST['delete_tags']; + foreach ( $tags as $tag_ID ) { + wp_delete_term( $tag_ID, $taxonomy ); + } + + $location = 'edit-tags.php?taxonomy=' . $taxonomy; + if ( 'post' != $post_type ) + $location .= '&post_type=' . $post_type; + if ( $referer = wp_get_referer() ) { + if ( false !== strpos( $referer, 'edit-tags.php' ) ) + $location = $referer; + } + + $location = add_query_arg( 'message', 6, $location ); + + break; + +case 'edit': + $title = $tax->labels->edit_item; + + $tag_ID = (int) $_REQUEST['tag_ID']; + + $tag = get_term( $tag_ID, $taxonomy, OBJECT, 'edit' ); + if ( ! $tag ) + wp_die( __( 'You attempted to edit an item that doesn’t exist. Perhaps it was deleted?' ) ); + require_once( ABSPATH . 'wp-admin/admin-header.php' ); + include( ABSPATH . 'wp-admin/edit-tag-form.php' ); + include( ABSPATH . 'wp-admin/admin-footer.php' ); + + exit; + +case 'editedtag': + $tag_ID = (int) $_POST['tag_ID']; + check_admin_referer( 'update-tag_' . $tag_ID ); + + if ( !current_user_can( $tax->cap->edit_terms ) ) + wp_die( __( 'Cheatin’ uh?' ), 403 ); + + $tag = get_term( $tag_ID, $taxonomy ); + if ( ! $tag ) + wp_die( __( 'You attempted to edit an item that doesn’t exist. Perhaps it was deleted?' ) ); + + $ret = wp_update_term( $tag_ID, $taxonomy, $_POST ); + + $location = 'edit-tags.php?taxonomy=' . $taxonomy; + if ( 'post' != $post_type ) + $location .= '&post_type=' . $post_type; + + if ( $referer = wp_get_original_referer() ) { + if ( false !== strpos( $referer, 'edit-tags.php' ) ) + $location = $referer; + } + + if ( $ret && !is_wp_error( $ret ) ) + $location = add_query_arg( 'message', 3, $location ); + else + $location = add_query_arg( 'message', 5, $location ); + break; +} + +if ( ! $location && ! empty( $_REQUEST['_wp_http_referer'] ) ) { + $location = remove_query_arg( array('_wp_http_referer', '_wpnonce'), wp_unslash($_SERVER['REQUEST_URI']) ); +} + +if ( $location ) { + if ( ! empty( $_REQUEST['paged'] ) ) { + $location = add_query_arg( 'paged', (int) $_REQUEST['paged'], $location ); + } + wp_redirect( $location ); + exit; +} + +$wp_list_table->prepare_items(); +$total_pages = $wp_list_table->get_pagination_arg( 'total_pages' ); + +if ( $pagenum > $total_pages && $total_pages > 0 ) { + wp_redirect( add_query_arg( 'paged', $total_pages ) ); + exit; +} + +wp_enqueue_script('admin-tags'); +if ( current_user_can($tax->cap->edit_terms) ) + wp_enqueue_script('inline-edit-tax'); + +if ( 'category' == $taxonomy || 'link_category' == $taxonomy || 'post_tag' == $taxonomy ) { + $help =''; + if ( 'category' == $taxonomy ) + $help = '

    ' . sprintf(__( 'You can use categories to define sections of your site and group related posts. The default category is “Uncategorized” until you change it in your writing settings.' ) , 'options-writing.php' ) . '

    '; + elseif ( 'link_category' == $taxonomy ) + $help = '

    ' . __( 'You can create groups of links by using Link Categories. Link Category names must be unique and Link Categories are separate from the categories you use for posts.' ) . '

    '; + else + $help = '

    ' . __( 'You can assign keywords to your posts using tags. Unlike categories, tags have no hierarchy, meaning there’s no relationship from one tag to another.' ) . '

    '; + + if ( 'link_category' == $taxonomy ) + $help .= '

    ' . __( 'You can delete Link Categories in the Bulk Action pull-down, but that action does not delete the links within the category. Instead, it moves them to the default Link Category.' ) . '

    '; + else + $help .='

    ' . __( 'What’s the difference between categories and tags? Normally, tags are ad-hoc keywords that identify important information in your post (names, subjects, etc) that may or may not recur in other posts, while categories are pre-determined sections. If you think of your site like a book, the categories are like the Table of Contents and the tags are like the terms in the index.' ) . '

    '; + + get_current_screen()->add_help_tab( array( + 'id' => 'overview', + 'title' => __('Overview'), + 'content' => $help, + ) ); + + if ( 'category' == $taxonomy || 'post_tag' == $taxonomy ) { + if ( 'category' == $taxonomy ) + $help = '

    ' . __( 'When adding a new category on this screen, you’ll fill in the following fields:' ) . '

    '; + else + $help = '

    ' . __( 'When adding a new tag on this screen, you’ll fill in the following fields:' ) . '

    '; + + $help .= '
      ' . + '
    • ' . __( 'Name - The name is how it appears on your site.' ) . '
    • '; + + if ( ! global_terms_enabled() ) + $help .= '
    • ' . __( 'Slug - The “slug” is the URL-friendly version of the name. It is usually all lowercase and contains only letters, numbers, and hyphens.' ) . '
    • '; + + if ( 'category' == $taxonomy ) + $help .= '
    • ' . __( 'Parent - Categories, unlike tags, can have a hierarchy. You might have a Jazz category, and under that have child categories for Bebop and Big Band. Totally optional. To create a subcategory, just choose another category from the Parent dropdown.' ) . '
    • '; + + $help .= '
    • ' . __( 'Description - The description is not prominent by default; however, some themes may display it.' ) . '
    • ' . + '
    ' . + '

    ' . __( 'You can change the display of this screen using the Screen Options tab to set how many items are displayed per screen and to display/hide columns in the table.' ) . '

    '; + + get_current_screen()->add_help_tab( array( + 'id' => 'adding-terms', + 'title' => 'category' == $taxonomy ? __( 'Adding Categories' ) : __( 'Adding Tags' ), + 'content' => $help, + ) ); + } + + $help = '

    ' . __( 'For more information:' ) . '

    '; + + if ( 'category' == $taxonomy ) + $help .= '

    ' . __( 'Documentation on Categories' ) . '

    '; + elseif ( 'link_category' == $taxonomy ) + $help .= '

    ' . __( 'Documentation on Link Categories' ) . '

    '; + else + $help .= '

    ' . __( 'Documentation on Tags' ) . '

    '; + + $help .= '

    ' . __('Support Forums') . '

    '; + + get_current_screen()->set_help_sidebar( $help ); + + unset( $help ); +} + +require_once( ABSPATH . 'wp-admin/admin-header.php' ); + +if ( !current_user_can($tax->cap->edit_terms) ) + wp_die( __('You are not allowed to edit this item.') ); + +$messages = array(); +$messages['_item'] = array( + 0 => '', // Unused. Messages start at index 1. + 1 => __( 'Item added.' ), + 2 => __( 'Item deleted.' ), + 3 => __( 'Item updated.' ), + 4 => __( 'Item not added.' ), + 5 => __( 'Item not updated.' ), + 6 => __( 'Items deleted.' ) +); +$messages['category'] = array( + 0 => '', // Unused. Messages start at index 1. + 1 => __( 'Category added.' ), + 2 => __( 'Category deleted.' ), + 3 => __( 'Category updated.' ), + 4 => __( 'Category not added.' ), + 5 => __( 'Category not updated.' ), + 6 => __( 'Categories deleted.' ) +); +$messages['post_tag'] = array( + 0 => '', // Unused. Messages start at index 1. + 1 => __( 'Tag added.' ), + 2 => __( 'Tag deleted.' ), + 3 => __( 'Tag updated.' ), + 4 => __( 'Tag not added.' ), + 5 => __( 'Tag not updated.' ), + 6 => __( 'Tags deleted.' ) +); + +/** + * Filter the messages displayed when a tag is updated. + * + * @since 3.7.0 + * + * @param array $messages The messages to be displayed. + */ +$messages = apply_filters( 'term_updated_messages', $messages ); + +$message = false; +if ( isset( $_REQUEST['message'] ) && ( $msg = (int) $_REQUEST['message'] ) ) { + if ( isset( $messages[ $taxonomy ][ $msg ] ) ) + $message = $messages[ $taxonomy ][ $msg ]; + elseif ( ! isset( $messages[ $taxonomy ] ) && isset( $messages['_item'][ $msg ] ) ) + $message = $messages['_item'][ $msg ]; +} + +?> + +
    +

    ' . __('Search results for “%s”') . '', esc_html( wp_unslash($_REQUEST['s']) ) ); ?> +

    + + +

    + +
    + +
    + + + +search_box( $tax->labels->search_items, 'tag' ); ?> + +
    +
    + +
    + +
    +
    +
    + + + +display(); ?> + +
    +
    + + +
    +

    + Note:
    Deleting a category does not delete the posts in that category. Instead, posts that were only assigned to the deleted category are set to the category %s.' ), apply_filters( 'the_category', get_cat_name( get_option( 'default_category') ) ) ); + ?> +

    + +

    category to tag converter.'), 'import.php') ?>

    + +
    + +
    +

    tag to category converter.'), 'import.php') ;?>

    +
    + + +
    +
    + +
    +
    + +labels->popular_items ) ) { + if ( current_user_can( $tax->cap->edit_terms ) ) + $tag_cloud = wp_tag_cloud( array( 'taxonomy' => $taxonomy, 'post_type' => $post_type, 'echo' => false, 'link' => 'edit' ) ); + else + $tag_cloud = wp_tag_cloud( array( 'taxonomy' => $taxonomy, 'echo' => false ) ); + + if ( $tag_cloud ) : + ?> +
    +

    labels->popular_items; ?>

    + +
    +cap->edit_terms) ) { + if ( 'category' == $taxonomy ) { + /** + * Fires before the Add Category form. + * + * @since 2.1.0 + * @deprecated 3.0.0 Use {$taxonomy}_pre_add_form instead. + * + * @param object $arg Optional arguments cast to an object. + */ + do_action( 'add_category_form_pre', (object) array( 'parent' => 0 ) ); + } elseif ( 'link_category' == $taxonomy ) { + /** + * Fires before the link category form. + * + * @since 2.3.0 + * @deprecated 3.0.0 Use {$taxonomy}_pre_add_form instead. + * + * @param object $arg Optional arguments cast to an object. + */ + do_action( 'add_link_category_form_pre', (object) array( 'parent' => 0 ) ); + } else { + /** + * Fires before the Add Tag form. + * + * @since 2.5.0 + * @deprecated 3.0.0 Use {$taxonomy}_pre_add_form instead. + * + * @param string $taxonomy The taxonomy slug. + */ + do_action( 'add_tag_form_pre', $taxonomy ); + } + + /** + * Fires before the Add Term form for all taxonomies. + * + * The dynamic portion of the hook name, `$taxonomy`, refers to the taxonomy slug. + * + * @since 3.0.0 + * + * @param string $taxonomy The taxonomy slug. + */ + do_action( "{$taxonomy}_pre_add_form", $taxonomy ); +?> + +
    +

    labels->add_new_item; ?>

    +
    > + + + + + + +
    + + +

    +
    + +
    + + +

    +
    + + +
    + + 0, + 'hide_if_empty' => false, + 'taxonomy' => $taxonomy, + 'name' => 'parent', + 'orderby' => 'name', + 'hierarchical' => true, + 'show_option_none' => __( 'None' ), + ); + + /** + * Filter the taxonomy parent drop-down on the Edit Term page. + * + * @since 3.7.0 + * + * @param array $dropdown_args { + * An array of taxonomy parent drop-down arguments. + * + * @type int|bool $hide_empty Whether to hide terms not attached to any posts. Default 0|false. + * @type bool $hide_if_empty Whether to hide the drop-down if no terms exist. Default false. + * @type string $taxonomy The taxonomy slug. + * @type string $name Value of the name attribute to use for the drop-down select element. + * Default 'parent'. + * @type string $orderby The field to order by. Default 'name'. + * @type bool $hierarchical Whether the taxonomy is hierarchical. Default true. + * @type string $show_option_none Label to display if there are no terms. Default 'None'. + * } + * @param string $taxonomy The taxonomy slug. + */ + $dropdown_args = apply_filters( 'taxonomy_parent_dropdown_args', $dropdown_args, $taxonomy ); + wp_dropdown_categories( $dropdown_args ); + ?> + +

    + +
    + +
    + + +

    +
    + +labels->add_new_item ); + +if ( 'category' == $taxonomy ) { + /** + * Fires at the end of the Edit Category form. + * + * @since 2.1.0 + * @deprecated 3.0.0 Use {$taxonomy}_add_form instead. + * + * @param object $arg Optional arguments cast to an object. + */ + do_action( 'edit_category_form', (object) array( 'parent' => 0 ) ); +} elseif ( 'link_category' == $taxonomy ) { + /** + * Fires at the end of the Edit Link form. + * + * @since 2.3.0 + * @deprecated 3.0.0 Use {$taxonomy}_add_form instead. + * + * @param object $arg Optional arguments cast to an object. + */ + do_action( 'edit_link_category_form', (object) array( 'parent' => 0 ) ); +} else { + /** + * Fires at the end of the Add Tag form. + * + * @since 2.7.0 + * @deprecated 3.0.0 Use {$taxonomy}_add_form instead. + * + * @param string $taxonomy The taxonomy slug. + */ + do_action( 'add_tag_form', $taxonomy ); +} + +/** + * Fires at the end of the Add Term form for all taxonomies. + * + * The dynamic portion of the hook name, `$taxonomy`, refers to the taxonomy slug. + * + * @since 3.0.0 + * + * @param string $taxonomy The taxonomy slug. + */ +do_action( "{$taxonomy}_add_form", $taxonomy ); +?> +
    + + +
    +
    + +
    +
    + + + +inline_edit(); + +include( ABSPATH . 'wp-admin/admin-footer.php' ); diff --git a/wp-admin/edit.php b/wp-admin/edit.php new file mode 100644 index 0000000..b5c4cde --- /dev/null +++ b/wp-admin/edit.php @@ -0,0 +1,334 @@ +cap->edit_posts ) ) + wp_die( __( 'Cheatin’ uh?' ), 403 ); + +$wp_list_table = _get_list_table('WP_Posts_List_Table'); +$pagenum = $wp_list_table->get_pagenum(); + +// Back-compat for viewing comments of an entry +foreach ( array( 'p', 'attachment_id', 'page_id' ) as $_redirect ) { + if ( ! empty( $_REQUEST[ $_redirect ] ) ) { + wp_redirect( admin_url( 'edit-comments.php?p=' . absint( $_REQUEST[ $_redirect ] ) ) ); + exit; + } +} +unset( $_redirect ); + +if ( 'post' != $post_type ) { + $parent_file = "edit.php?post_type=$post_type"; + $submenu_file = "edit.php?post_type=$post_type"; + $post_new_file = "post-new.php?post_type=$post_type"; +} else { + $parent_file = 'edit.php'; + $submenu_file = 'edit.php'; + $post_new_file = 'post-new.php'; +} + +$doaction = $wp_list_table->current_action(); + +if ( $doaction ) { + check_admin_referer('bulk-posts'); + + $sendback = remove_query_arg( array('trashed', 'untrashed', 'deleted', 'locked', 'ids'), wp_get_referer() ); + if ( ! $sendback ) + $sendback = admin_url( $parent_file ); + $sendback = add_query_arg( 'paged', $pagenum, $sendback ); + if ( strpos($sendback, 'post.php') !== false ) + $sendback = admin_url($post_new_file); + + if ( 'delete_all' == $doaction ) { + // Prepare for deletion of all posts with a specified post status (i.e. Empty trash). + $post_status = preg_replace('/[^a-z0-9_-]+/i', '', $_REQUEST['post_status']); + // Validate the post status exists. + if ( get_post_status_object( $post_status ) ) { + $post_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type=%s AND post_status = %s", $post_type, $post_status ) ); + } + $doaction = 'delete'; + } elseif ( isset( $_REQUEST['media'] ) ) { + $post_ids = $_REQUEST['media']; + } elseif ( isset( $_REQUEST['ids'] ) ) { + $post_ids = explode( ',', $_REQUEST['ids'] ); + } elseif ( !empty( $_REQUEST['post'] ) ) { + $post_ids = array_map('intval', $_REQUEST['post']); + } + + if ( !isset( $post_ids ) ) { + wp_redirect( $sendback ); + exit; + } + + switch ( $doaction ) { + case 'trash': + $trashed = $locked = 0; + + foreach( (array) $post_ids as $post_id ) { + if ( !current_user_can( 'delete_post', $post_id) ) + wp_die( __('You are not allowed to move this item to the Trash.') ); + + if ( wp_check_post_lock( $post_id ) ) { + $locked++; + continue; + } + + if ( !wp_trash_post($post_id) ) + wp_die( __('Error in moving to Trash.') ); + + $trashed++; + } + + $sendback = add_query_arg( array('trashed' => $trashed, 'ids' => join(',', $post_ids), 'locked' => $locked ), $sendback ); + break; + case 'untrash': + $untrashed = 0; + foreach( (array) $post_ids as $post_id ) { + if ( !current_user_can( 'delete_post', $post_id) ) + wp_die( __('You are not allowed to restore this item from the Trash.') ); + + if ( !wp_untrash_post($post_id) ) + wp_die( __('Error in restoring from Trash.') ); + + $untrashed++; + } + $sendback = add_query_arg('untrashed', $untrashed, $sendback); + break; + case 'delete': + $deleted = 0; + foreach( (array) $post_ids as $post_id ) { + $post_del = get_post($post_id); + + if ( !current_user_can( 'delete_post', $post_id ) ) + wp_die( __('You are not allowed to delete this item.') ); + + if ( $post_del->post_type == 'attachment' ) { + if ( ! wp_delete_attachment($post_id) ) + wp_die( __('Error in deleting.') ); + } else { + if ( !wp_delete_post($post_id) ) + wp_die( __('Error in deleting.') ); + } + $deleted++; + } + $sendback = add_query_arg('deleted', $deleted, $sendback); + break; + case 'edit': + if ( isset($_REQUEST['bulk_edit']) ) { + $done = bulk_edit_posts($_REQUEST); + + if ( is_array($done) ) { + $done['updated'] = count( $done['updated'] ); + $done['skipped'] = count( $done['skipped'] ); + $done['locked'] = count( $done['locked'] ); + $sendback = add_query_arg( $done, $sendback ); + } + } + break; + } + + $sendback = remove_query_arg( array('action', 'action2', 'tags_input', 'post_author', 'comment_status', 'ping_status', '_status', 'post', 'bulk_edit', 'post_view'), $sendback ); + + wp_redirect($sendback); + exit(); +} elseif ( ! empty($_REQUEST['_wp_http_referer']) ) { + wp_redirect( remove_query_arg( array('_wp_http_referer', '_wpnonce'), wp_unslash($_SERVER['REQUEST_URI']) ) ); + exit; +} + +$wp_list_table->prepare_items(); + +wp_enqueue_script('inline-edit-post'); +wp_enqueue_script('heartbeat'); + +$title = $post_type_object->labels->name; + +if ( 'post' == $post_type ) { + get_current_screen()->add_help_tab( array( + 'id' => 'overview', + 'title' => __('Overview'), + 'content' => + '

    ' . __('This screen provides access to all of your posts. You can customize the display of this screen to suit your workflow.') . '

    ' + ) ); + get_current_screen()->add_help_tab( array( + 'id' => 'screen-content', + 'title' => __('Screen Content'), + 'content' => + '

    ' . __('You can customize the display of this screen’s contents in a number of ways:') . '

    ' . + '
      ' . + '
    • ' . __('You can hide/display columns based on your needs and decide how many posts to list per screen using the Screen Options tab.') . '
    • ' . + '
    • ' . __('You can filter the list of posts by post status using the text links in the upper left to show All, Published, Draft, or Trashed posts. The default view is to show all posts.') . '
    • ' . + '
    • ' . __('You can view posts in a simple title list or with an excerpt. Choose the view you prefer by clicking on the icons at the top of the list on the right.') . '
    • ' . + '
    • ' . __('You can refine the list to show only posts in a specific category or from a specific month by using the dropdown menus above the posts list. Click the Filter button after making your selection. You also can refine the list by clicking on the post author, category or tag in the posts list.') . '
    • ' . + '
    ' + ) ); + get_current_screen()->add_help_tab( array( + 'id' => 'action-links', + 'title' => __('Available Actions'), + 'content' => + '

    ' . __('Hovering over a row in the posts list will display action links that allow you to manage your post. You can perform the following actions:') . '

    ' . + '
      ' . + '
    • ' . __('Edit takes you to the editing screen for that post. You can also reach that screen by clicking on the post title.') . '
    • ' . + '
    • ' . __('Quick Edit provides inline access to the metadata of your post, allowing you to update post details without leaving this screen.') . '
    • ' . + '
    • ' . __('Trash removes your post from this list and places it in the trash, from which you can permanently delete it.') . '
    • ' . + '
    • ' . __('Preview will show you what your draft post will look like if you publish it. View will take you to your live site to view the post. Which link is available depends on your post’s status.') . '
    • ' . + '
    ' + ) ); + get_current_screen()->add_help_tab( array( + 'id' => 'bulk-actions', + 'title' => __('Bulk Actions'), + 'content' => + '

    ' . __('You can also edit or move multiple posts to the trash at once. Select the posts you want to act on using the checkboxes, then select the action you want to take from the Bulk Actions menu and click Apply.') . '

    ' . + '

    ' . __('When using Bulk Edit, you can change the metadata (categories, author, etc.) for all selected posts at once. To remove a post from the grouping, just click the x next to its name in the Bulk Edit area that appears.') . '

    ' + ) ); + + get_current_screen()->set_help_sidebar( + '

    ' . __('For more information:') . '

    ' . + '

    ' . __('Documentation on Managing Posts') . '

    ' . + '

    ' . __('Support Forums') . '

    ' + ); + +} elseif ( 'page' == $post_type ) { + get_current_screen()->add_help_tab( array( + 'id' => 'overview', + 'title' => __('Overview'), + 'content' => + '

    ' . __('Pages are similar to posts in that they have a title, body text, and associated metadata, but they are different in that they are not part of the chronological blog stream, kind of like permanent posts. Pages are not categorized or tagged, but can have a hierarchy. You can nest pages under other pages by making one the “Parent” of the other, creating a group of pages.') . '

    ' + ) ); + get_current_screen()->add_help_tab( array( + 'id' => 'managing-pages', + 'title' => __('Managing Pages'), + 'content' => + '

    ' . __('Managing pages is very similar to managing posts, and the screens can be customized in the same way.') . '

    ' . + '

    ' . __('You can also perform the same types of actions, including narrowing the list by using the filters, acting on a page using the action links that appear when you hover over a row, or using the Bulk Actions menu to edit the metadata for multiple pages at once.') . '

    ' + ) ); + + get_current_screen()->set_help_sidebar( + '

    ' . __('For more information:') . '

    ' . + '

    ' . __('Documentation on Managing Pages') . '

    ' . + '

    ' . __('Support Forums') . '

    ' + ); +} + +add_screen_option( 'per_page', array( 'label' => $title, 'default' => 20, 'option' => 'edit_' . $post_type . '_per_page' ) ); + +$bulk_counts = array( + 'updated' => isset( $_REQUEST['updated'] ) ? absint( $_REQUEST['updated'] ) : 0, + 'locked' => isset( $_REQUEST['locked'] ) ? absint( $_REQUEST['locked'] ) : 0, + 'deleted' => isset( $_REQUEST['deleted'] ) ? absint( $_REQUEST['deleted'] ) : 0, + 'trashed' => isset( $_REQUEST['trashed'] ) ? absint( $_REQUEST['trashed'] ) : 0, + 'untrashed' => isset( $_REQUEST['untrashed'] ) ? absint( $_REQUEST['untrashed'] ) : 0, +); + +$bulk_messages = array(); +$bulk_messages['post'] = array( + 'updated' => _n( '%s post updated.', '%s posts updated.', $bulk_counts['updated'] ), + 'locked' => _n( '%s post not updated, somebody is editing it.', '%s posts not updated, somebody is editing them.', $bulk_counts['locked'] ), + 'deleted' => _n( '%s post permanently deleted.', '%s posts permanently deleted.', $bulk_counts['deleted'] ), + 'trashed' => _n( '%s post moved to the Trash.', '%s posts moved to the Trash.', $bulk_counts['trashed'] ), + 'untrashed' => _n( '%s post restored from the Trash.', '%s posts restored from the Trash.', $bulk_counts['untrashed'] ), +); +$bulk_messages['page'] = array( + 'updated' => _n( '%s page updated.', '%s pages updated.', $bulk_counts['updated'] ), + 'locked' => _n( '%s page not updated, somebody is editing it.', '%s pages not updated, somebody is editing them.', $bulk_counts['locked'] ), + 'deleted' => _n( '%s page permanently deleted.', '%s pages permanently deleted.', $bulk_counts['deleted'] ), + 'trashed' => _n( '%s page moved to the Trash.', '%s pages moved to the Trash.', $bulk_counts['trashed'] ), + 'untrashed' => _n( '%s page restored from the Trash.', '%s pages restored from the Trash.', $bulk_counts['untrashed'] ), +); + +/** + * Filter the bulk action updated messages. + * + * By default, custom post types use the messages for the 'post' post type. + * + * @since 3.7.0 + * + * @param array $bulk_messages Arrays of messages, each keyed by the corresponding post type. Messages are + * keyed with 'updated', 'locked', 'deleted', 'trashed', and 'untrashed'. + * @param array $bulk_counts Array of item counts for each message, used to build internationalized strings. + */ +$bulk_messages = apply_filters( 'bulk_post_updated_messages', $bulk_messages, $bulk_counts ); +$bulk_counts = array_filter( $bulk_counts ); + +require_once( ABSPATH . 'wp-admin/admin-header.php' ); +?> +
    +

    labels->name ); +if ( current_user_can( $post_type_object->cap->create_posts ) ) + echo ' ' . esc_html( $post_type_object->labels->add_new ) . ''; +if ( ! empty( $_REQUEST['s'] ) ) + printf( ' ' . __('Search results for “%s”') . '', get_search_query() ); +?>

    + + $count ) { + if ( isset( $bulk_messages[ $post_type ][ $message ] ) ) + $messages[] = sprintf( $bulk_messages[ $post_type ][ $message ], number_format_i18n( $count ) ); + elseif ( isset( $bulk_messages['post'][ $message ] ) ) + $messages[] = sprintf( $bulk_messages['post'][ $message ], number_format_i18n( $count ) ); + + if ( $message == 'trashed' && isset( $_REQUEST['ids'] ) ) { + $ids = preg_replace( '/[^0-9,]/', '', $_REQUEST['ids'] ); + $messages[] = '' . __('Undo') . ''; + } +} + +if ( $messages ) + echo '

    ' . join( ' ', $messages ) . '

    '; +unset( $messages ); + +$_SERVER['REQUEST_URI'] = remove_query_arg( array( 'locked', 'skipped', 'updated', 'deleted', 'trashed', 'untrashed' ), $_SERVER['REQUEST_URI'] ); +?> + +views(); ?> + +
    + +search_box( $post_type_object->labels->search_items, 'post' ); ?> + + + + + + + +display(); ?> + +
    + +has_items() ) + $wp_list_table->inline_edit(); +?> + +
    +
    +
    + + + +add_help_tab( array( + 'id' => 'overview', + 'title' => __('Overview'), + 'content' => '

    ' . __('You can export a file of your site’s content in order to import it into another installation or platform. The export file will be an XML file format called WXR. Posts, pages, comments, custom fields, categories, and tags can be included. You can choose for the WXR file to include only certain posts or pages by setting the dropdown filters to limit the export by category, author, date range by month, or publishing status.') . '

    ' . + '

    ' . __('Once generated, your WXR file can be imported by another WordPress site or by another blogging platform able to access this format.') . '

    ', +) ); + +get_current_screen()->set_help_sidebar( + '

    ' . __('For more information:') . '

    ' . + '

    ' . __('Documentation on Export') . '

    ' . + '

    ' . __('Support Forums') . '

    ' +); + +// If the 'download' URL parameter is set, a WXR export file is baked and returned. +if ( isset( $_GET['download'] ) ) { + $args = array(); + + if ( ! isset( $_GET['content'] ) || 'all' == $_GET['content'] ) { + $args['content'] = 'all'; + } else if ( 'posts' == $_GET['content'] ) { + $args['content'] = 'post'; + + if ( $_GET['cat'] ) + $args['category'] = (int) $_GET['cat']; + + if ( $_GET['post_author'] ) + $args['author'] = (int) $_GET['post_author']; + + if ( $_GET['post_start_date'] || $_GET['post_end_date'] ) { + $args['start_date'] = $_GET['post_start_date']; + $args['end_date'] = $_GET['post_end_date']; + } + + if ( $_GET['post_status'] ) + $args['status'] = $_GET['post_status']; + } else if ( 'pages' == $_GET['content'] ) { + $args['content'] = 'page'; + + if ( $_GET['page_author'] ) + $args['author'] = (int) $_GET['page_author']; + + if ( $_GET['page_start_date'] || $_GET['page_end_date'] ) { + $args['start_date'] = $_GET['page_start_date']; + $args['end_date'] = $_GET['page_end_date']; + } + + if ( $_GET['page_status'] ) + $args['status'] = $_GET['page_status']; + } else { + $args['content'] = $_GET['content']; + } + + /** + * Filter the export args. + * + * @since 3.5.0 + * + * @param array $args The arguments to send to the exporter. + */ + $args = apply_filters( 'export_args', $args ); + + export_wp( $args ); + die(); +} + +require_once( ABSPATH . 'wp-admin/admin-header.php' ); + +/** + * Create the date options fields for exporting a given post type. + * + * @global wpdb $wpdb WordPress database abstraction object. + * @global WP_Locale $wp_locale Date and Time Locale object. + * + * @since 3.1.0 + * + * @param string $post_type The post type. Default 'post'. + */ +function export_date_options( $post_type = 'post' ) { + global $wpdb, $wp_locale; + + $months = $wpdb->get_results( $wpdb->prepare( " + SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month + FROM $wpdb->posts + WHERE post_type = %s AND post_status != 'auto-draft' + ORDER BY post_date DESC + ", $post_type ) ); + + $month_count = count( $months ); + if ( !$month_count || ( 1 == $month_count && 0 == $months[0]->month ) ) + return; + + foreach ( $months as $date ) { + if ( 0 == $date->year ) + continue; + + $month = zeroise( $date->month, 2 ); + echo ''; + } +} +?> + +
    +

    + +

    +

    +

    + +

    +
    + +

    +

    + +

    +
      +
    • + + __('All') ) ); ?> +
    • +
    • + +get_col( "SELECT DISTINCT post_author FROM {$wpdb->posts} WHERE post_type = 'post'" ); + wp_dropdown_users( array( 'include' => $authors, 'name' => 'post_author', 'multi' => true, 'show_option_all' => __('All') ) ); +?> +
    • +
    • + + + +
    • +
    • + + +
    • +
    + +

    +
      +
    • + +get_col( "SELECT DISTINCT post_author FROM {$wpdb->posts} WHERE post_type = 'page'" ); + wp_dropdown_users( array( 'include' => $authors, 'name' => 'page_author', 'multi' => true, 'show_option_all' => __('All') ) ); +?> +
    • +
    • + + + +
    • +
    • + + +
    • +
    + + false, 'can_export' => true ), 'objects' ) as $post_type ) : ?> +

    + + + + + +
    +
    + + diff --git a/wp-admin/freedoms.php b/wp-admin/freedoms.php new file mode 100644 index 0000000..7d8dadb --- /dev/null +++ b/wp-admin/freedoms.php @@ -0,0 +1,57 @@ + +
    + +

    + +
    + +
    + + + +

    license, the GPL.' ), 'https://wordpress.org/about/license/' ); ?>

    + +
      +
    1. +
    2. +
    3. +
    4. +
    + +

    check out our trademark guidelines first.' ), 'http://wordpressfoundation.org/trademark-policy/' ); ?>

    + +

    plugins and themes there. If you get a plugin or theme from another source, make sure to ask them if it’s GPL first. If they don’t respect the WordPress license, we don’t recommend them.' ), $plugins_url, $themes_url, 'https://wordpress.org/about/license/' ); ?>

    + +

    Free Software Foundation.' ); ?>

    + +
    + diff --git a/wp-admin/images/align-center-2x.png b/wp-admin/images/align-center-2x.png new file mode 100644 index 0000000..0b62734 Binary files /dev/null and b/wp-admin/images/align-center-2x.png differ diff --git a/wp-admin/images/align-center.png b/wp-admin/images/align-center.png new file mode 100644 index 0000000..e7bc807 Binary files /dev/null and b/wp-admin/images/align-center.png differ diff --git a/wp-admin/images/align-left-2x.png b/wp-admin/images/align-left-2x.png new file mode 100644 index 0000000..1b2d428 Binary files /dev/null and b/wp-admin/images/align-left-2x.png differ diff --git a/wp-admin/images/align-left.png b/wp-admin/images/align-left.png new file mode 100644 index 0000000..b438f7e Binary files /dev/null and b/wp-admin/images/align-left.png differ diff --git a/wp-admin/images/align-none-2x.png b/wp-admin/images/align-none-2x.png new file mode 100644 index 0000000..a64a0be Binary files /dev/null and b/wp-admin/images/align-none-2x.png differ diff --git a/wp-admin/images/align-none.png b/wp-admin/images/align-none.png new file mode 100644 index 0000000..b72df64 Binary files /dev/null and b/wp-admin/images/align-none.png differ diff --git a/wp-admin/images/align-right-2x.png b/wp-admin/images/align-right-2x.png new file mode 100644 index 0000000..0131505 Binary files /dev/null and b/wp-admin/images/align-right-2x.png differ diff --git a/wp-admin/images/align-right.png b/wp-admin/images/align-right.png new file mode 100644 index 0000000..86a1b2e Binary files /dev/null and b/wp-admin/images/align-right.png differ diff --git a/wp-admin/images/arrows-2x.png b/wp-admin/images/arrows-2x.png new file mode 100644 index 0000000..0b0c53d Binary files /dev/null and b/wp-admin/images/arrows-2x.png differ diff --git a/wp-admin/images/arrows.png b/wp-admin/images/arrows.png new file mode 100644 index 0000000..9e4a96c Binary files /dev/null and b/wp-admin/images/arrows.png differ diff --git a/wp-admin/images/bubble_bg-2x.gif b/wp-admin/images/bubble_bg-2x.gif new file mode 100644 index 0000000..77ac312 Binary files /dev/null and b/wp-admin/images/bubble_bg-2x.gif differ diff --git a/wp-admin/images/bubble_bg.gif b/wp-admin/images/bubble_bg.gif new file mode 100644 index 0000000..f3bfdcd Binary files /dev/null and b/wp-admin/images/bubble_bg.gif differ diff --git a/wp-admin/images/comment-grey-bubble-2x.png b/wp-admin/images/comment-grey-bubble-2x.png new file mode 100644 index 0000000..0eec4a6 Binary files /dev/null and b/wp-admin/images/comment-grey-bubble-2x.png differ diff --git a/wp-admin/images/comment-grey-bubble.png b/wp-admin/images/comment-grey-bubble.png new file mode 100644 index 0000000..558ee8f Binary files /dev/null and b/wp-admin/images/comment-grey-bubble.png differ diff --git a/wp-admin/images/date-button-2x.gif b/wp-admin/images/date-button-2x.gif new file mode 100644 index 0000000..f658b51 Binary files /dev/null and b/wp-admin/images/date-button-2x.gif differ diff --git a/wp-admin/images/date-button.gif b/wp-admin/images/date-button.gif new file mode 100644 index 0000000..5080544 Binary files /dev/null and b/wp-admin/images/date-button.gif differ diff --git a/wp-admin/images/generic.png b/wp-admin/images/generic.png new file mode 100644 index 0000000..00575a0 Binary files /dev/null and b/wp-admin/images/generic.png differ diff --git a/wp-admin/images/icons32-2x.png b/wp-admin/images/icons32-2x.png new file mode 100644 index 0000000..b86b727 Binary files /dev/null and b/wp-admin/images/icons32-2x.png differ diff --git a/wp-admin/images/icons32-vs-2x.png b/wp-admin/images/icons32-vs-2x.png new file mode 100644 index 0000000..54e2fb2 Binary files /dev/null and b/wp-admin/images/icons32-vs-2x.png differ diff --git a/wp-admin/images/icons32-vs.png b/wp-admin/images/icons32-vs.png new file mode 100644 index 0000000..e46d6be Binary files /dev/null and b/wp-admin/images/icons32-vs.png differ diff --git a/wp-admin/images/icons32.png b/wp-admin/images/icons32.png new file mode 100644 index 0000000..e491b1a Binary files /dev/null and b/wp-admin/images/icons32.png differ diff --git a/wp-admin/images/imgedit-icons-2x.png b/wp-admin/images/imgedit-icons-2x.png new file mode 100644 index 0000000..98dd412 Binary files /dev/null and b/wp-admin/images/imgedit-icons-2x.png differ diff --git a/wp-admin/images/imgedit-icons.png b/wp-admin/images/imgedit-icons.png new file mode 100644 index 0000000..0d544ee Binary files /dev/null and b/wp-admin/images/imgedit-icons.png differ diff --git a/wp-admin/images/list-2x.png b/wp-admin/images/list-2x.png new file mode 100644 index 0000000..05c6eb3 Binary files /dev/null and b/wp-admin/images/list-2x.png differ diff --git a/wp-admin/images/list.png b/wp-admin/images/list.png new file mode 100644 index 0000000..85d1295 Binary files /dev/null and b/wp-admin/images/list.png differ diff --git a/wp-admin/images/loading.gif b/wp-admin/images/loading.gif new file mode 100644 index 0000000..1e9dea9 Binary files /dev/null and b/wp-admin/images/loading.gif differ diff --git a/wp-admin/images/marker.png b/wp-admin/images/marker.png new file mode 100644 index 0000000..30313b8 Binary files /dev/null and b/wp-admin/images/marker.png differ diff --git a/wp-admin/images/mask.png b/wp-admin/images/mask.png new file mode 100644 index 0000000..0fc9cbe Binary files /dev/null and b/wp-admin/images/mask.png differ diff --git a/wp-admin/images/media-button-2x.png b/wp-admin/images/media-button-2x.png new file mode 100644 index 0000000..b8f8ed4 Binary files /dev/null and b/wp-admin/images/media-button-2x.png differ diff --git a/wp-admin/images/media-button-image.gif b/wp-admin/images/media-button-image.gif new file mode 100644 index 0000000..56a9747 Binary files /dev/null and b/wp-admin/images/media-button-image.gif differ diff --git a/wp-admin/images/media-button-music.gif b/wp-admin/images/media-button-music.gif new file mode 100644 index 0000000..42e65b2 Binary files /dev/null and b/wp-admin/images/media-button-music.gif differ diff --git a/wp-admin/images/media-button-other.gif b/wp-admin/images/media-button-other.gif new file mode 100644 index 0000000..c7544ec Binary files /dev/null and b/wp-admin/images/media-button-other.gif differ diff --git a/wp-admin/images/media-button-video.gif b/wp-admin/images/media-button-video.gif new file mode 100644 index 0000000..405083b Binary files /dev/null and b/wp-admin/images/media-button-video.gif differ diff --git a/wp-admin/images/media-button.png b/wp-admin/images/media-button.png new file mode 100644 index 0000000..752ee45 Binary files /dev/null and b/wp-admin/images/media-button.png differ diff --git a/wp-admin/images/menu-2x.png b/wp-admin/images/menu-2x.png new file mode 100644 index 0000000..7131763 Binary files /dev/null and b/wp-admin/images/menu-2x.png differ diff --git a/wp-admin/images/menu-vs-2x.png b/wp-admin/images/menu-vs-2x.png new file mode 100644 index 0000000..99b3823 Binary files /dev/null and b/wp-admin/images/menu-vs-2x.png differ diff --git a/wp-admin/images/menu-vs.png b/wp-admin/images/menu-vs.png new file mode 100644 index 0000000..fe28108 Binary files /dev/null and b/wp-admin/images/menu-vs.png differ diff --git a/wp-admin/images/menu.png b/wp-admin/images/menu.png new file mode 100644 index 0000000..c1d15af Binary files /dev/null and b/wp-admin/images/menu.png differ diff --git a/wp-admin/images/no.png b/wp-admin/images/no.png new file mode 100644 index 0000000..59c35bd Binary files /dev/null and b/wp-admin/images/no.png differ diff --git a/wp-admin/images/post-formats-vs.png b/wp-admin/images/post-formats-vs.png new file mode 100644 index 0000000..d77f91c Binary files /dev/null and b/wp-admin/images/post-formats-vs.png differ diff --git a/wp-admin/images/post-formats.png b/wp-admin/images/post-formats.png new file mode 100644 index 0000000..cae309e Binary files /dev/null and b/wp-admin/images/post-formats.png differ diff --git a/wp-admin/images/post-formats32-vs.png b/wp-admin/images/post-formats32-vs.png new file mode 100644 index 0000000..f565340 Binary files /dev/null and b/wp-admin/images/post-formats32-vs.png differ diff --git a/wp-admin/images/post-formats32.png b/wp-admin/images/post-formats32.png new file mode 100644 index 0000000..69ec095 Binary files /dev/null and b/wp-admin/images/post-formats32.png differ diff --git a/wp-admin/images/resize-2x.gif b/wp-admin/images/resize-2x.gif new file mode 100644 index 0000000..315ea06 Binary files /dev/null and b/wp-admin/images/resize-2x.gif differ diff --git a/wp-admin/images/resize-rtl-2x.gif b/wp-admin/images/resize-rtl-2x.gif new file mode 100644 index 0000000..51edc79 Binary files /dev/null and b/wp-admin/images/resize-rtl-2x.gif differ diff --git a/wp-admin/images/resize-rtl.gif b/wp-admin/images/resize-rtl.gif new file mode 100644 index 0000000..61afefe Binary files /dev/null and b/wp-admin/images/resize-rtl.gif differ diff --git a/wp-admin/images/resize.gif b/wp-admin/images/resize.gif new file mode 100644 index 0000000..ca42227 Binary files /dev/null and b/wp-admin/images/resize.gif differ diff --git a/wp-admin/images/se.png b/wp-admin/images/se.png new file mode 100644 index 0000000..eb487b4 Binary files /dev/null and b/wp-admin/images/se.png differ diff --git a/wp-admin/images/sort-2x.gif b/wp-admin/images/sort-2x.gif new file mode 100644 index 0000000..60b6084 Binary files /dev/null and b/wp-admin/images/sort-2x.gif differ diff --git a/wp-admin/images/sort.gif b/wp-admin/images/sort.gif new file mode 100644 index 0000000..aa65db1 Binary files /dev/null and b/wp-admin/images/sort.gif differ diff --git a/wp-admin/images/spinner-2x.gif b/wp-admin/images/spinner-2x.gif new file mode 100644 index 0000000..a4e161e Binary files /dev/null and b/wp-admin/images/spinner-2x.gif differ diff --git a/wp-admin/images/spinner.gif b/wp-admin/images/spinner.gif new file mode 100644 index 0000000..209d10b Binary files /dev/null and b/wp-admin/images/spinner.gif differ diff --git a/wp-admin/images/stars-2x.png b/wp-admin/images/stars-2x.png new file mode 100644 index 0000000..15aa9de Binary files /dev/null and b/wp-admin/images/stars-2x.png differ diff --git a/wp-admin/images/stars.png b/wp-admin/images/stars.png new file mode 100644 index 0000000..c01ada1 Binary files /dev/null and b/wp-admin/images/stars.png differ diff --git a/wp-admin/images/w-logo-blue.png b/wp-admin/images/w-logo-blue.png new file mode 100644 index 0000000..11e550c Binary files /dev/null and b/wp-admin/images/w-logo-blue.png differ diff --git a/wp-admin/images/w-logo-white.png b/wp-admin/images/w-logo-white.png new file mode 100644 index 0000000..bb70f57 Binary files /dev/null and b/wp-admin/images/w-logo-white.png differ diff --git a/wp-admin/images/wheel.png b/wp-admin/images/wheel.png new file mode 100644 index 0000000..9b9fdf4 Binary files /dev/null and b/wp-admin/images/wheel.png differ diff --git a/wp-admin/images/wordpress-logo-white.svg b/wp-admin/images/wordpress-logo-white.svg new file mode 100644 index 0000000..c0d0ba9 --- /dev/null +++ b/wp-admin/images/wordpress-logo-white.svg @@ -0,0 +1 @@ + diff --git a/wp-admin/images/wordpress-logo.png b/wp-admin/images/wordpress-logo.png new file mode 100644 index 0000000..63b0379 Binary files /dev/null and b/wp-admin/images/wordpress-logo.png differ diff --git a/wp-admin/images/wordpress-logo.svg b/wp-admin/images/wordpress-logo.svg new file mode 100644 index 0000000..da5e5df --- /dev/null +++ b/wp-admin/images/wordpress-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/wp-admin/images/wpspin_light-2x.gif b/wp-admin/images/wpspin_light-2x.gif new file mode 100644 index 0000000..392e82f Binary files /dev/null and b/wp-admin/images/wpspin_light-2x.gif differ diff --git a/wp-admin/images/wpspin_light.gif b/wp-admin/images/wpspin_light.gif new file mode 100644 index 0000000..4c42dcb Binary files /dev/null and b/wp-admin/images/wpspin_light.gif differ diff --git a/wp-admin/images/xit-2x.gif b/wp-admin/images/xit-2x.gif new file mode 100644 index 0000000..3a72ee1 Binary files /dev/null and b/wp-admin/images/xit-2x.gif differ diff --git a/wp-admin/images/xit.gif b/wp-admin/images/xit.gif new file mode 100644 index 0000000..d288954 Binary files /dev/null and b/wp-admin/images/xit.gif differ diff --git a/wp-admin/images/yes.png b/wp-admin/images/yes.png new file mode 100644 index 0000000..fbb3983 Binary files /dev/null and b/wp-admin/images/yes.png differ diff --git a/wp-admin/import.php b/wp-admin/import.php new file mode 100644 index 0000000..e6f05e0 --- /dev/null +++ b/wp-admin/import.php @@ -0,0 +1,132 @@ +add_help_tab( array( + 'id' => 'overview', + 'title' => __('Overview'), + 'content' => '

    ' . __('This screen lists links to plugins to import data from blogging/content management platforms. Choose the platform you want to import from, and click Install Now when you are prompted in the popup window. If your platform is not listed, click the link to search the plugin directory for other importer plugins to see if there is one for your platform.') . '

    ' . + '

    ' . __('In previous versions of WordPress, all importers were built-in. They have been turned into plugins since most people only use them once or infrequently.') . '

    ', +) ); + +get_current_screen()->set_help_sidebar( + '

    ' . __('For more information:') . '

    ' . + '

    ' . __('Documentation on Import') . '

    ' . + '

    ' . __('Support Forums') . '

    ' +); + +if ( current_user_can( 'install_plugins' ) ) + $popular_importers = wp_get_popular_importers(); +else + $popular_importers = array(); + +// Detect and redirect invalid importers like 'movabletype', which is registered as 'mt' +if ( ! empty( $_GET['invalid'] ) && isset( $popular_importers[ $_GET['invalid'] ] ) ) { + $importer_id = $popular_importers[ $_GET['invalid'] ]['importer-id']; + if ( $importer_id != $_GET['invalid'] ) { // Prevent redirect loops. + wp_redirect( admin_url( 'admin.php?import=' . $importer_id ) ); + exit; + } + unset( $importer_id ); +} + +add_thickbox(); +wp_enqueue_script( 'plugin-install' ); + +require_once( ABSPATH . 'wp-admin/admin-header.php' ); +$parent_file = 'tools.php'; +?> + +
    +

    + +

    %s importer is invalid or is not installed.'), esc_html( $_GET['invalid'] ) ); ?>

    + +

    + + $pop_data ) { + if ( isset( $importers[ $pop_importer ] ) ) + continue; + if ( isset( $importers[ $pop_data['importer-id'] ] ) ) + continue; + $importers[ $pop_data['importer-id'] ] = array( $pop_data['name'], $pop_data['description'], 'install' => $pop_data['plugin-slug'] ); +} + +if ( empty( $importers ) ) { + echo '

    ' . __('No importers are available.') . '

    '; // TODO: make more helpful +} else { + uasort( $importers, '_usort_by_first_member' ); +?> + + + $data) { + $action = ''; + if ( isset( $data['install'] ) ) { + $plugin_slug = $data['install']; + if ( file_exists( WP_PLUGIN_DIR . '/' . $plugin_slug ) ) { + // Looks like Importer is installed, But not active + $plugins = get_plugins( '/' . $plugin_slug ); + if ( !empty($plugins) ) { + $keys = array_keys($plugins); + $plugin_file = $plugin_slug . '/' . $keys[0]; + $action = '' . $data[0] . ''; + } + } + if ( empty($action) ) { + if ( is_main_site() ) { + $action = '' . $data[0] . ''; + } else { + $action = $data[0]; + $data[1] = sprintf( __( 'This importer is not installed. Please install importers from the main site.' ), get_admin_url( $current_site->blog_id, 'import.php' ) ); + } + } + } else { + $action = "{$data[0]}"; + } + + $alt = $alt ? '' : ' class="alternate"'; + echo " + + + + "; + } +?> + +
    $action{$data[1]}
    +' . sprintf( __('If the importer you need is not listed, search the plugin directory to see if an importer is available.'), esc_url( network_admin_url( 'plugin-install.php?tab=search&type=tag&s=importer' ) ) ) . '

    '; +?> + +
    + +id and the JS global 'pagenow'. + if ( ! empty($_POST['screen_id']) ) + $screen_id = sanitize_key($_POST['screen_id']); + else + $screen_id = 'front'; + + if ( ! empty($_POST['data']) ) { + $data = wp_unslash( (array) $_POST['data'] ); + + /** + * Filter Heartbeat AJAX response in no-privilege environments. + * + * @since 3.6.0 + * + * @param array|object $response The no-priv Heartbeat response object or array. + * @param array $data An array of data passed via $_POST. + * @param string $screen_id The screen id. + */ + $response = apply_filters( 'heartbeat_nopriv_received', $response, $data, $screen_id ); + } + + /** + * Filter Heartbeat AJAX response when no data is passed. + * + * @since 3.6.0 + * + * @param array|object $response The Heartbeat response object or array. + * @param string $screen_id The screen id. + */ + $response = apply_filters( 'heartbeat_nopriv_send', $response, $screen_id ); + + /** + * Fires when Heartbeat ticks in no-privilege environments. + * + * Allows the transport to be easily replaced with long-polling. + * + * @since 3.6.0 + * + * @param array|object $response The no-priv Heartbeat response. + * @param string $screen_id The screen id. + */ + do_action( 'heartbeat_nopriv_tick', $response, $screen_id ); + + // Send the current time according to the server. + $response['server_time'] = time(); + + wp_send_json($response); +} + +// +// GET-based Ajax handlers. +// + +/** + * Ajax handler for fetching a list table. + * + * @since 3.1.0 + */ +function wp_ajax_fetch_list() { + global $wp_list_table; + + $list_class = $_GET['list_args']['class']; + check_ajax_referer( "fetch-list-$list_class", '_ajax_fetch_list_nonce' ); + + $wp_list_table = _get_list_table( $list_class, array( 'screen' => $_GET['list_args']['screen']['id'] ) ); + if ( ! $wp_list_table ) + wp_die( 0 ); + + if ( ! $wp_list_table->ajax_user_can() ) + wp_die( -1 ); + + $wp_list_table->ajax_response(); + + wp_die( 0 ); +} + +/** + * Ajax handler for tag search. + * + * @since 3.1.0 + */ +function wp_ajax_ajax_tag_search() { + if ( ! isset( $_GET['tax'] ) ) { + wp_die( 0 ); + } + + $taxonomy = sanitize_key( $_GET['tax'] ); + $tax = get_taxonomy( $taxonomy ); + if ( ! $tax ) { + wp_die( 0 ); + } + + if ( ! current_user_can( $tax->cap->assign_terms ) ) { + wp_die( -1 ); + } + + $s = wp_unslash( $_GET['q'] ); + + $comma = _x( ',', 'tag delimiter' ); + if ( ',' !== $comma ) + $s = str_replace( $comma, ',', $s ); + if ( false !== strpos( $s, ',' ) ) { + $s = explode( ',', $s ); + $s = $s[count( $s ) - 1]; + } + $s = trim( $s ); + + /** + * Filter the minimum number of characters required to fire a tag search via AJAX. + * + * @since 4.0.0 + * + * @param int $characters The minimum number of characters required. Default 2. + * @param object $tax The taxonomy object. + * @param string $s The search term. + */ + $term_search_min_chars = (int) apply_filters( 'term_search_min_chars', 2, $tax, $s ); + + /* + * Require $term_search_min_chars chars for matching (default: 2) + * ensure it's a non-negative, non-zero integer. + */ + if ( ( $term_search_min_chars == 0 ) || ( strlen( $s ) < $term_search_min_chars ) ){ + wp_die(); + } + + $results = get_terms( $taxonomy, array( 'name__like' => $s, 'fields' => 'names', 'hide_empty' => false ) ); + + echo join( $results, "\n" ); + wp_die(); +} + +/** + * Ajax handler for compression testing. + * + * @since 3.1.0 + */ +function wp_ajax_wp_compression_test() { + if ( !current_user_can( 'manage_options' ) ) + wp_die( -1 ); + + if ( ini_get('zlib.output_compression') || 'ob_gzhandler' == ini_get('output_handler') ) { + update_site_option('can_compress_scripts', 0); + wp_die( 0 ); + } + + if ( isset($_GET['test']) ) { + header( 'Expires: Wed, 11 Jan 1984 05:00:00 GMT' ); + header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s' ) . ' GMT' ); + header( 'Cache-Control: no-cache, must-revalidate, max-age=0' ); + header( 'Pragma: no-cache' ); + header('Content-Type: application/x-javascript; charset=UTF-8'); + $force_gzip = ( defined('ENFORCE_GZIP') && ENFORCE_GZIP ); + $test_str = '"wpCompressionTest Lorem ipsum dolor sit amet consectetuer mollis sapien urna ut a. Eu nonummy condimentum fringilla tempor pretium platea vel nibh netus Maecenas. Hac molestie amet justo quis pellentesque est ultrices interdum nibh Morbi. Cras mattis pretium Phasellus ante ipsum ipsum ut sociis Suspendisse Lorem. Ante et non molestie. Porta urna Vestibulum egestas id congue nibh eu risus gravida sit. Ac augue auctor Ut et non a elit massa id sodales. Elit eu Nulla at nibh adipiscing mattis lacus mauris at tempus. Netus nibh quis suscipit nec feugiat eget sed lorem et urna. Pellentesque lacus at ut massa consectetuer ligula ut auctor semper Pellentesque. Ut metus massa nibh quam Curabitur molestie nec mauris congue. Volutpat molestie elit justo facilisis neque ac risus Ut nascetur tristique. Vitae sit lorem tellus et quis Phasellus lacus tincidunt nunc Fusce. Pharetra wisi Suspendisse mus sagittis libero lacinia Integer consequat ac Phasellus. Et urna ac cursus tortor aliquam Aliquam amet tellus volutpat Vestibulum. Justo interdum condimentum In augue congue tellus sollicitudin Quisque quis nibh."'; + + if ( 1 == $_GET['test'] ) { + echo $test_str; + wp_die(); + } elseif ( 2 == $_GET['test'] ) { + if ( !isset($_SERVER['HTTP_ACCEPT_ENCODING']) ) + wp_die( -1 ); + if ( false !== stripos( $_SERVER['HTTP_ACCEPT_ENCODING'], 'deflate') && function_exists('gzdeflate') && ! $force_gzip ) { + header('Content-Encoding: deflate'); + $out = gzdeflate( $test_str, 1 ); + } elseif ( false !== stripos( $_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') && function_exists('gzencode') ) { + header('Content-Encoding: gzip'); + $out = gzencode( $test_str, 1 ); + } else { + wp_die( -1 ); + } + echo $out; + wp_die(); + } elseif ( 'no' == $_GET['test'] ) { + update_site_option('can_compress_scripts', 0); + } elseif ( 'yes' == $_GET['test'] ) { + update_site_option('can_compress_scripts', 1); + } + } + + wp_die( 0 ); +} + +/** + * Ajax handler for image editor previews. + * + * @since 3.1.0 + */ +function wp_ajax_imgedit_preview() { + $post_id = intval($_GET['postid']); + if ( empty($post_id) || !current_user_can('edit_post', $post_id) ) + wp_die( -1 ); + + check_ajax_referer( "image_editor-$post_id" ); + + include_once( ABSPATH . 'wp-admin/includes/image-edit.php' ); + if ( ! stream_preview_image($post_id) ) + wp_die( -1 ); + + wp_die(); +} + +/** + * Ajax handler for oEmbed caching. + * + * @since 3.1.0 + */ +function wp_ajax_oembed_cache() { + $GLOBALS['wp_embed']->cache_oembed( $_GET['post'] ); + wp_die( 0 ); +} + +/** + * Ajax handler for user autocomplete. + * + * @since 3.4.0 + */ +function wp_ajax_autocomplete_user() { + if ( ! is_multisite() || ! current_user_can( 'promote_users' ) || wp_is_large_network( 'users' ) ) + wp_die( -1 ); + + /** This filter is documented in wp-admin/user-new.php */ + if ( ! is_super_admin() && ! apply_filters( 'autocomplete_users_for_site_admins', false ) ) + wp_die( -1 ); + + $return = array(); + + // Check the type of request + // Current allowed values are `add` and `search` + if ( isset( $_REQUEST['autocomplete_type'] ) && 'search' === $_REQUEST['autocomplete_type'] ) { + $type = $_REQUEST['autocomplete_type']; + } else { + $type = 'add'; + } + + // Check the desired field for value + // Current allowed values are `user_email` and `user_login` + if ( isset( $_REQUEST['autocomplete_field'] ) && 'user_email' === $_REQUEST['autocomplete_field'] ) { + $field = $_REQUEST['autocomplete_field']; + } else { + $field = 'user_login'; + } + + // Exclude current users of this blog + if ( isset( $_REQUEST['site_id'] ) ) { + $id = absint( $_REQUEST['site_id'] ); + } else { + $id = get_current_blog_id(); + } + + $include_blog_users = ( $type == 'search' ? get_users( array( 'blog_id' => $id, 'fields' => 'ID' ) ) : array() ); + $exclude_blog_users = ( $type == 'add' ? get_users( array( 'blog_id' => $id, 'fields' => 'ID' ) ) : array() ); + + $users = get_users( array( + 'blog_id' => false, + 'search' => '*' . $_REQUEST['term'] . '*', + 'include' => $include_blog_users, + 'exclude' => $exclude_blog_users, + 'search_columns' => array( 'user_login', 'user_nicename', 'user_email' ), + ) ); + + foreach ( $users as $user ) { + $return[] = array( + /* translators: 1: user_login, 2: user_email */ + 'label' => sprintf( __( '%1$s (%2$s)' ), $user->user_login, $user->user_email ), + 'value' => $user->$field, + ); + } + + wp_die( wp_json_encode( $return ) ); +} + +/** + * Ajax handler for dashboard widgets. + * + * @since 3.4.0 + */ +function wp_ajax_dashboard_widgets() { + require_once ABSPATH . 'wp-admin/includes/dashboard.php'; + + $pagenow = $_GET['pagenow']; + if ( $pagenow === 'dashboard-user' || $pagenow === 'dashboard-network' || $pagenow === 'dashboard' ) { + set_current_screen( $pagenow ); + } + + switch ( $_GET['widget'] ) { + case 'dashboard_primary' : + wp_dashboard_primary(); + break; + } + wp_die(); +} + +/** + * Ajax handler for Customizer preview logged-in status. + * + * @since 3.4.0 + */ +function wp_ajax_logged_in() { + wp_die( 1 ); +} + +// +// Ajax helpers. +// + +/** + * Sends back current comment total and new page links if they need to be updated. + * + * Contrary to normal success AJAX response ("1"), die with time() on success. + * + * @since 2.7.0 + * + * @param int $comment_id + * @return die + */ +function _wp_ajax_delete_comment_response( $comment_id, $delta = -1 ) { + $total = isset( $_POST['_total'] ) ? (int) $_POST['_total'] : 0; + $per_page = isset( $_POST['_per_page'] ) ? (int) $_POST['_per_page'] : 0; + $page = isset( $_POST['_page'] ) ? (int) $_POST['_page'] : 0; + $url = isset( $_POST['_url'] ) ? esc_url_raw( $_POST['_url'] ) : ''; + + // JS didn't send us everything we need to know. Just die with success message + if ( !$total || !$per_page || !$page || !$url ) + wp_die( time() ); + + $total += $delta; + if ( $total < 0 ) + $total = 0; + + // Only do the expensive stuff on a page-break, and about 1 other time per page + if ( 0 == $total % $per_page || 1 == mt_rand( 1, $per_page ) ) { + $post_id = 0; + $status = 'total_comments'; // What type of comment count are we looking for? + $parsed = parse_url( $url ); + if ( isset( $parsed['query'] ) ) { + parse_str( $parsed['query'], $query_vars ); + if ( !empty( $query_vars['comment_status'] ) ) + $status = $query_vars['comment_status']; + if ( !empty( $query_vars['p'] ) ) + $post_id = (int) $query_vars['p']; + } + + $comment_count = wp_count_comments($post_id); + + // We're looking for a known type of comment count. + if ( isset( $comment_count->$status ) ) + $total = $comment_count->$status; + // Else use the decremented value from above. + } + + // The time since the last comment count. + $time = time(); + + $x = new WP_Ajax_Response( array( + 'what' => 'comment', + // Here for completeness - not used. + 'id' => $comment_id, + 'supplemental' => array( + 'total_items_i18n' => sprintf( _n( '1 item', '%s items', $total ), number_format_i18n( $total ) ), + 'total_pages' => ceil( $total / $per_page ), + 'total_pages_i18n' => number_format_i18n( ceil( $total / $per_page ) ), + 'total' => $total, + 'time' => $time + ) + ) ); + $x->send(); +} + +// +// POST-based Ajax handlers. +// + +/** + * Ajax handler for adding a hierarchical term. + * + * @since 3.1.0 + */ +function _wp_ajax_add_hierarchical_term() { + $action = $_POST['action']; + $taxonomy = get_taxonomy(substr($action, 4)); + check_ajax_referer( $action, '_ajax_nonce-add-' . $taxonomy->name ); + if ( !current_user_can( $taxonomy->cap->edit_terms ) ) + wp_die( -1 ); + $names = explode(',', $_POST['new'.$taxonomy->name]); + $parent = isset($_POST['new'.$taxonomy->name.'_parent']) ? (int) $_POST['new'.$taxonomy->name.'_parent'] : 0; + if ( 0 > $parent ) + $parent = 0; + if ( $taxonomy->name == 'category' ) + $post_category = isset($_POST['post_category']) ? (array) $_POST['post_category'] : array(); + else + $post_category = ( isset($_POST['tax_input']) && isset($_POST['tax_input'][$taxonomy->name]) ) ? (array) $_POST['tax_input'][$taxonomy->name] : array(); + $checked_categories = array_map( 'absint', (array) $post_category ); + $popular_ids = wp_popular_terms_checklist($taxonomy->name, 0, 10, false); + + foreach ( $names as $cat_name ) { + $cat_name = trim($cat_name); + $category_nicename = sanitize_title($cat_name); + if ( '' === $category_nicename ) + continue; + if ( !$cat_id = term_exists( $cat_name, $taxonomy->name, $parent ) ) + $cat_id = wp_insert_term( $cat_name, $taxonomy->name, array( 'parent' => $parent ) ); + if ( is_wp_error( $cat_id ) ) + continue; + else if ( is_array( $cat_id ) ) + $cat_id = $cat_id['term_id']; + $checked_categories[] = $cat_id; + if ( $parent ) // Do these all at once in a second + continue; + ob_start(); + wp_terms_checklist( 0, array( 'taxonomy' => $taxonomy->name, 'descendants_and_self' => $cat_id, 'selected_cats' => $checked_categories, 'popular_cats' => $popular_ids )); + $data = ob_get_contents(); + ob_end_clean(); + $add = array( + 'what' => $taxonomy->name, + 'id' => $cat_id, + 'data' => str_replace( array("\n", "\t"), '', $data), + 'position' => -1 + ); + } + + if ( $parent ) { // Foncy - replace the parent and all its children + $parent = get_term( $parent, $taxonomy->name ); + $term_id = $parent->term_id; + + while ( $parent->parent ) { // get the top parent + $parent = get_term( $parent->parent, $taxonomy->name ); + if ( is_wp_error( $parent ) ) + break; + $term_id = $parent->term_id; + } + + ob_start(); + wp_terms_checklist( 0, array('taxonomy' => $taxonomy->name, 'descendants_and_self' => $term_id, 'selected_cats' => $checked_categories, 'popular_cats' => $popular_ids)); + $data = ob_get_contents(); + ob_end_clean(); + $add = array( + 'what' => $taxonomy->name, + 'id' => $term_id, + 'data' => str_replace( array("\n", "\t"), '', $data), + 'position' => -1 + ); + } + + ob_start(); + wp_dropdown_categories( array( + 'taxonomy' => $taxonomy->name, 'hide_empty' => 0, 'name' => 'new'.$taxonomy->name.'_parent', 'orderby' => 'name', + 'hierarchical' => 1, 'show_option_none' => '— '.$taxonomy->labels->parent_item.' —' + ) ); + $sup = ob_get_contents(); + ob_end_clean(); + $add['supplemental'] = array( 'newcat_parent' => $sup ); + + $x = new WP_Ajax_Response( $add ); + $x->send(); +} + +/** + * Ajax handler for deleting a comment. + * + * @since 3.1.0 + */ +function wp_ajax_delete_comment() { + $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0; + + if ( !$comment = get_comment( $id ) ) + wp_die( time() ); + if ( ! current_user_can( 'edit_comment', $comment->comment_ID ) ) + wp_die( -1 ); + + check_ajax_referer( "delete-comment_$id" ); + $status = wp_get_comment_status( $comment->comment_ID ); + + $delta = -1; + if ( isset($_POST['trash']) && 1 == $_POST['trash'] ) { + if ( 'trash' == $status ) + wp_die( time() ); + $r = wp_trash_comment( $comment->comment_ID ); + } elseif ( isset($_POST['untrash']) && 1 == $_POST['untrash'] ) { + if ( 'trash' != $status ) + wp_die( time() ); + $r = wp_untrash_comment( $comment->comment_ID ); + if ( ! isset( $_POST['comment_status'] ) || $_POST['comment_status'] != 'trash' ) // undo trash, not in trash + $delta = 1; + } elseif ( isset($_POST['spam']) && 1 == $_POST['spam'] ) { + if ( 'spam' == $status ) + wp_die( time() ); + $r = wp_spam_comment( $comment->comment_ID ); + } elseif ( isset($_POST['unspam']) && 1 == $_POST['unspam'] ) { + if ( 'spam' != $status ) + wp_die( time() ); + $r = wp_unspam_comment( $comment->comment_ID ); + if ( ! isset( $_POST['comment_status'] ) || $_POST['comment_status'] != 'spam' ) // undo spam, not in spam + $delta = 1; + } elseif ( isset($_POST['delete']) && 1 == $_POST['delete'] ) { + $r = wp_delete_comment( $comment->comment_ID ); + } else { + wp_die( -1 ); + } + + if ( $r ) // Decide if we need to send back '1' or a more complicated response including page links and comment counts + _wp_ajax_delete_comment_response( $comment->comment_ID, $delta ); + wp_die( 0 ); +} + +/** + * Ajax handler for deleting a tag. + * + * @since 3.1.0 + */ +function wp_ajax_delete_tag() { + $tag_id = (int) $_POST['tag_ID']; + check_ajax_referer( "delete-tag_$tag_id" ); + + $taxonomy = !empty($_POST['taxonomy']) ? $_POST['taxonomy'] : 'post_tag'; + $tax = get_taxonomy($taxonomy); + + if ( !current_user_can( $tax->cap->delete_terms ) ) + wp_die( -1 ); + + $tag = get_term( $tag_id, $taxonomy ); + if ( !$tag || is_wp_error( $tag ) ) + wp_die( 1 ); + + if ( wp_delete_term($tag_id, $taxonomy)) + wp_die( 1 ); + else + wp_die( 0 ); +} + +/** + * Ajax handler for deleting a link. + * + * @since 3.1.0 + */ +function wp_ajax_delete_link() { + $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0; + + check_ajax_referer( "delete-bookmark_$id" ); + if ( !current_user_can( 'manage_links' ) ) + wp_die( -1 ); + + $link = get_bookmark( $id ); + if ( !$link || is_wp_error( $link ) ) + wp_die( 1 ); + + if ( wp_delete_link( $id ) ) + wp_die( 1 ); + else + wp_die( 0 ); +} + +/** + * Ajax handler for deleting meta. + * + * @since 3.1.0 + */ +function wp_ajax_delete_meta() { + $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0; + + check_ajax_referer( "delete-meta_$id" ); + if ( !$meta = get_metadata_by_mid( 'post', $id ) ) + wp_die( 1 ); + + if ( is_protected_meta( $meta->meta_key, 'post' ) || ! current_user_can( 'delete_post_meta', $meta->post_id, $meta->meta_key ) ) + wp_die( -1 ); + if ( delete_meta( $meta->meta_id ) ) + wp_die( 1 ); + wp_die( 0 ); +} + +/** + * Ajax handler for deleting a post. + * + * @since 3.1.0 + * + * @param string $action Action to perform. + */ +function wp_ajax_delete_post( $action ) { + if ( empty( $action ) ) + $action = 'delete-post'; + $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0; + + check_ajax_referer( "{$action}_$id" ); + if ( !current_user_can( 'delete_post', $id ) ) + wp_die( -1 ); + + if ( !get_post( $id ) ) + wp_die( 1 ); + + if ( wp_delete_post( $id ) ) + wp_die( 1 ); + else + wp_die( 0 ); +} + +/** + * Ajax handler for sending a post to the trash. + * + * @since 3.1.0 + * + * @param string $action Action to perform. + */ +function wp_ajax_trash_post( $action ) { + if ( empty( $action ) ) + $action = 'trash-post'; + $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0; + + check_ajax_referer( "{$action}_$id" ); + if ( !current_user_can( 'delete_post', $id ) ) + wp_die( -1 ); + + if ( !get_post( $id ) ) + wp_die( 1 ); + + if ( 'trash-post' == $action ) + $done = wp_trash_post( $id ); + else + $done = wp_untrash_post( $id ); + + if ( $done ) + wp_die( 1 ); + + wp_die( 0 ); +} + +/** + * Ajax handler to restore a post from the trash. + * + * @since 3.1.0 + * + * @param string $action Action to perform. + */ +function wp_ajax_untrash_post( $action ) { + if ( empty( $action ) ) + $action = 'untrash-post'; + wp_ajax_trash_post( $action ); +} + +function wp_ajax_delete_page( $action ) { + if ( empty( $action ) ) + $action = 'delete-page'; + $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0; + + check_ajax_referer( "{$action}_$id" ); + if ( !current_user_can( 'delete_page', $id ) ) + wp_die( -1 ); + + if ( ! get_post( $id ) ) + wp_die( 1 ); + + if ( wp_delete_post( $id ) ) + wp_die( 1 ); + else + wp_die( 0 ); +} + +/** + * Ajax handler to dim a comment. + * + * @since 3.1.0 + */ +function wp_ajax_dim_comment() { + $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0; + + if ( !$comment = get_comment( $id ) ) { + $x = new WP_Ajax_Response( array( + 'what' => 'comment', + 'id' => new WP_Error('invalid_comment', sprintf(__('Comment %d does not exist'), $id)) + ) ); + $x->send(); + } + + if ( ! current_user_can( 'edit_comment', $comment->comment_ID ) && ! current_user_can( 'moderate_comments' ) ) + wp_die( -1 ); + + $current = wp_get_comment_status( $comment->comment_ID ); + if ( isset( $_POST['new'] ) && $_POST['new'] == $current ) + wp_die( time() ); + + check_ajax_referer( "approve-comment_$id" ); + if ( in_array( $current, array( 'unapproved', 'spam' ) ) ) + $result = wp_set_comment_status( $comment->comment_ID, 'approve', true ); + else + $result = wp_set_comment_status( $comment->comment_ID, 'hold', true ); + + if ( is_wp_error($result) ) { + $x = new WP_Ajax_Response( array( + 'what' => 'comment', + 'id' => $result + ) ); + $x->send(); + } + + // Decide if we need to send back '1' or a more complicated response including page links and comment counts + _wp_ajax_delete_comment_response( $comment->comment_ID ); + wp_die( 0 ); +} + +/** + * Ajax handler for deleting a link category. + * + * @since 3.1.0 + * + * @param string $action Action to perform. + */ +function wp_ajax_add_link_category( $action ) { + if ( empty( $action ) ) + $action = 'add-link-category'; + check_ajax_referer( $action ); + if ( !current_user_can( 'manage_categories' ) ) + wp_die( -1 ); + $names = explode(',', wp_unslash( $_POST['newcat'] ) ); + $x = new WP_Ajax_Response(); + foreach ( $names as $cat_name ) { + $cat_name = trim($cat_name); + $slug = sanitize_title($cat_name); + if ( '' === $slug ) + continue; + if ( !$cat_id = term_exists( $cat_name, 'link_category' ) ) + $cat_id = wp_insert_term( $cat_name, 'link_category' ); + if ( is_wp_error( $cat_id ) ) + continue; + else if ( is_array( $cat_id ) ) + $cat_id = $cat_id['term_id']; + $cat_name = esc_html( $cat_name ); + $x->add( array( + 'what' => 'link-category', + 'id' => $cat_id, + 'data' => "", + 'position' => -1 + ) ); + } + $x->send(); +} + +/** + * Ajax handler to add a tag. + * + * @since 3.1.0 + */ +function wp_ajax_add_tag() { + global $wp_list_table; + + check_ajax_referer( 'add-tag', '_wpnonce_add-tag' ); + $taxonomy = !empty($_POST['taxonomy']) ? $_POST['taxonomy'] : 'post_tag'; + $tax = get_taxonomy($taxonomy); + + if ( !current_user_can( $tax->cap->edit_terms ) ) + wp_die( -1 ); + + $x = new WP_Ajax_Response(); + + $tag = wp_insert_term($_POST['tag-name'], $taxonomy, $_POST ); + + if ( !$tag || is_wp_error($tag) || (!$tag = get_term( $tag['term_id'], $taxonomy )) ) { + $message = __('An error has occurred. Please reload the page and try again.'); + if ( is_wp_error($tag) && $tag->get_error_message() ) + $message = $tag->get_error_message(); + + $x->add( array( + 'what' => 'taxonomy', + 'data' => new WP_Error('error', $message ) + ) ); + $x->send(); + } + + $wp_list_table = _get_list_table( 'WP_Terms_List_Table', array( 'screen' => $_POST['screen'] ) ); + + $level = 0; + if ( is_taxonomy_hierarchical($taxonomy) ) { + $level = count( get_ancestors( $tag->term_id, $taxonomy, 'taxonomy' ) ); + ob_start(); + $wp_list_table->single_row( $tag, $level ); + $noparents = ob_get_clean(); + } + + ob_start(); + $wp_list_table->single_row( $tag ); + $parents = ob_get_clean(); + + $x->add( array( + 'what' => 'taxonomy', + 'supplemental' => compact('parents', 'noparents') + ) ); + $x->add( array( + 'what' => 'term', + 'position' => $level, + 'supplemental' => (array) $tag + ) ); + $x->send(); +} + +/** + * Ajax handler for getting a tagcloud. + * + * @since 3.1.0 + */ +function wp_ajax_get_tagcloud() { + if ( ! isset( $_POST['tax'] ) ) { + wp_die( 0 ); + } + + $taxonomy = sanitize_key( $_POST['tax'] ); + $tax = get_taxonomy( $taxonomy ); + if ( ! $tax ) { + wp_die( 0 ); + } + + if ( ! current_user_can( $tax->cap->assign_terms ) ) { + wp_die( -1 ); + } + + $tags = get_terms( $taxonomy, array( 'number' => 45, 'orderby' => 'count', 'order' => 'DESC' ) ); + + if ( empty( $tags ) ) + wp_die( $tax->labels->not_found ); + + if ( is_wp_error( $tags ) ) + wp_die( $tags->get_error_message() ); + + foreach ( $tags as $key => $tag ) { + $tags[ $key ]->link = '#'; + $tags[ $key ]->id = $tag->term_id; + } + + // We need raw tag names here, so don't filter the output + $return = wp_generate_tag_cloud( $tags, array('filter' => 0) ); + + if ( empty($return) ) + wp_die( 0 ); + + echo $return; + + wp_die(); +} + +/** + * Ajax handler for getting comments. + * + * @since 3.1.0 + * + * @param string $action Action to perform. + */ +function wp_ajax_get_comments( $action ) { + global $wp_list_table, $post_id; + if ( empty( $action ) ) + $action = 'get-comments'; + + check_ajax_referer( $action ); + + if ( empty( $post_id ) && ! empty( $_REQUEST['p'] ) ) { + $id = absint( $_REQUEST['p'] ); + if ( ! empty( $id ) ) + $post_id = $id; + } + + if ( empty( $post_id ) ) + wp_die( -1 ); + + $wp_list_table = _get_list_table( 'WP_Post_Comments_List_Table', array( 'screen' => 'edit-comments' ) ); + + if ( ! current_user_can( 'edit_post', $post_id ) ) + wp_die( -1 ); + + $wp_list_table->prepare_items(); + + if ( !$wp_list_table->has_items() ) + wp_die( 1 ); + + $x = new WP_Ajax_Response(); + ob_start(); + foreach ( $wp_list_table->items as $comment ) { + if ( ! current_user_can( 'edit_comment', $comment->comment_ID ) ) + continue; + get_comment( $comment ); + $wp_list_table->single_row( $comment ); + } + $comment_list_item = ob_get_contents(); + ob_end_clean(); + + $x->add( array( + 'what' => 'comments', + 'data' => $comment_list_item + ) ); + $x->send(); +} + +/** + * Ajax handler for replying to a comment. + * + * @since 3.1.0 + * + * @param string $action Action to perform. + */ +function wp_ajax_replyto_comment( $action ) { + global $wp_list_table; + if ( empty( $action ) ) + $action = 'replyto-comment'; + + check_ajax_referer( $action, '_ajax_nonce-replyto-comment' ); + + $comment_post_ID = (int) $_POST['comment_post_ID']; + $post = get_post( $comment_post_ID ); + if ( ! $post ) + wp_die( -1 ); + + if ( !current_user_can( 'edit_post', $comment_post_ID ) ) + wp_die( -1 ); + + if ( empty( $post->post_status ) ) + wp_die( 1 ); + elseif ( in_array($post->post_status, array('draft', 'pending', 'trash') ) ) + wp_die( __('ERROR: you are replying to a comment on a draft post.') ); + + $user = wp_get_current_user(); + if ( $user->exists() ) { + $user_ID = $user->ID; + $comment_author = wp_slash( $user->display_name ); + $comment_author_email = wp_slash( $user->user_email ); + $comment_author_url = wp_slash( $user->user_url ); + $comment_content = trim( $_POST['content'] ); + $comment_type = isset( $_POST['comment_type'] ) ? trim( $_POST['comment_type'] ) : ''; + if ( current_user_can( 'unfiltered_html' ) ) { + if ( ! isset( $_POST['_wp_unfiltered_html_comment'] ) ) + $_POST['_wp_unfiltered_html_comment'] = ''; + + if ( wp_create_nonce( 'unfiltered-html-comment' ) != $_POST['_wp_unfiltered_html_comment'] ) { + kses_remove_filters(); // start with a clean slate + kses_init_filters(); // set up the filters + } + } + } else { + wp_die( __( 'Sorry, you must be logged in to reply to a comment.' ) ); + } + + if ( '' == $comment_content ) + wp_die( __( 'ERROR: please type a comment.' ) ); + + $comment_parent = 0; + if ( isset( $_POST['comment_ID'] ) ) + $comment_parent = absint( $_POST['comment_ID'] ); + $comment_auto_approved = false; + $commentdata = compact('comment_post_ID', 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_content', 'comment_type', 'comment_parent', 'user_ID'); + + // Automatically approve parent comment. + if ( !empty($_POST['approve_parent']) ) { + $parent = get_comment( $comment_parent ); + + if ( $parent && $parent->comment_approved === '0' && $parent->comment_post_ID == $comment_post_ID ) { + if ( wp_set_comment_status( $parent->comment_ID, 'approve' ) ) + $comment_auto_approved = true; + } + } + + $comment_id = wp_new_comment( $commentdata ); + $comment = get_comment($comment_id); + if ( ! $comment ) wp_die( 1 ); + + $position = ( isset($_POST['position']) && (int) $_POST['position'] ) ? (int) $_POST['position'] : '-1'; + + ob_start(); + if ( isset( $_REQUEST['mode'] ) && 'dashboard' == $_REQUEST['mode'] ) { + require_once( ABSPATH . 'wp-admin/includes/dashboard.php' ); + _wp_dashboard_recent_comments_row( $comment ); + } else { + if ( isset( $_REQUEST['mode'] ) && 'single' == $_REQUEST['mode'] ) { + $wp_list_table = _get_list_table('WP_Post_Comments_List_Table', array( 'screen' => 'edit-comments' ) ); + } else { + $wp_list_table = _get_list_table('WP_Comments_List_Table', array( 'screen' => 'edit-comments' ) ); + } + $wp_list_table->single_row( $comment ); + } + $comment_list_item = ob_get_clean(); + + $response = array( + 'what' => 'comment', + 'id' => $comment->comment_ID, + 'data' => $comment_list_item, + 'position' => $position + ); + + if ( $comment_auto_approved ) + $response['supplemental'] = array( 'parent_approved' => $parent->comment_ID ); + + $x = new WP_Ajax_Response(); + $x->add( $response ); + $x->send(); +} + +/** + * Ajax handler for editing a comment. + * + * @since 3.1.0 + */ +function wp_ajax_edit_comment() { + global $wp_list_table; + + check_ajax_referer( 'replyto-comment', '_ajax_nonce-replyto-comment' ); + + $comment_id = (int) $_POST['comment_ID']; + if ( ! current_user_can( 'edit_comment', $comment_id ) ) + wp_die( -1 ); + + if ( '' == $_POST['content'] ) + wp_die( __( 'ERROR: please type a comment.' ) ); + + if ( isset( $_POST['status'] ) ) + $_POST['comment_status'] = $_POST['status']; + edit_comment(); + + $position = ( isset($_POST['position']) && (int) $_POST['position']) ? (int) $_POST['position'] : '-1'; + $checkbox = ( isset($_POST['checkbox']) && true == $_POST['checkbox'] ) ? 1 : 0; + $wp_list_table = _get_list_table( $checkbox ? 'WP_Comments_List_Table' : 'WP_Post_Comments_List_Table', array( 'screen' => 'edit-comments' ) ); + + $comment = get_comment( $comment_id ); + if ( empty( $comment->comment_ID ) ) + wp_die( -1 ); + + ob_start(); + $wp_list_table->single_row( $comment ); + $comment_list_item = ob_get_clean(); + + $x = new WP_Ajax_Response(); + + $x->add( array( + 'what' => 'edit_comment', + 'id' => $comment->comment_ID, + 'data' => $comment_list_item, + 'position' => $position + )); + + $x->send(); +} + +/** + * Ajax handler for adding a menu item. + * + * @since 3.1.0 + */ +function wp_ajax_add_menu_item() { + check_ajax_referer( 'add-menu_item', 'menu-settings-column-nonce' ); + + if ( ! current_user_can( 'edit_theme_options' ) ) + wp_die( -1 ); + + require_once ABSPATH . 'wp-admin/includes/nav-menu.php'; + + // For performance reasons, we omit some object properties from the checklist. + // The following is a hacky way to restore them when adding non-custom items. + + $menu_items_data = array(); + foreach ( (array) $_POST['menu-item'] as $menu_item_data ) { + if ( + ! empty( $menu_item_data['menu-item-type'] ) && + 'custom' != $menu_item_data['menu-item-type'] && + ! empty( $menu_item_data['menu-item-object-id'] ) + ) { + switch( $menu_item_data['menu-item-type'] ) { + case 'post_type' : + $_object = get_post( $menu_item_data['menu-item-object-id'] ); + break; + + case 'taxonomy' : + $_object = get_term( $menu_item_data['menu-item-object-id'], $menu_item_data['menu-item-object'] ); + break; + } + + $_menu_items = array_map( 'wp_setup_nav_menu_item', array( $_object ) ); + $_menu_item = array_shift( $_menu_items ); + + // Restore the missing menu item properties + $menu_item_data['menu-item-description'] = $_menu_item->description; + } + + $menu_items_data[] = $menu_item_data; + } + + $item_ids = wp_save_nav_menu_items( 0, $menu_items_data ); + if ( is_wp_error( $item_ids ) ) + wp_die( 0 ); + + $menu_items = array(); + + foreach ( (array) $item_ids as $menu_item_id ) { + $menu_obj = get_post( $menu_item_id ); + if ( ! empty( $menu_obj->ID ) ) { + $menu_obj = wp_setup_nav_menu_item( $menu_obj ); + $menu_obj->label = $menu_obj->title; // don't show "(pending)" in ajax-added items + $menu_items[] = $menu_obj; + } + } + + /** This filter is documented in wp-admin/includes/nav-menu.php */ + $walker_class_name = apply_filters( 'wp_edit_nav_menu_walker', 'Walker_Nav_Menu_Edit', $_POST['menu'] ); + + if ( ! class_exists( $walker_class_name ) ) + wp_die( 0 ); + + if ( ! empty( $menu_items ) ) { + $args = array( + 'after' => '', + 'before' => '', + 'link_after' => '', + 'link_before' => '', + 'walker' => new $walker_class_name, + ); + echo walk_nav_menu_tree( $menu_items, 0, (object) $args ); + } + wp_die(); +} + +/** + * Ajax handler for adding meta. + * + * @since 3.1.0 + */ +function wp_ajax_add_meta() { + check_ajax_referer( 'add-meta', '_ajax_nonce-add-meta' ); + $c = 0; + $pid = (int) $_POST['post_id']; + $post = get_post( $pid ); + + if ( isset($_POST['metakeyselect']) || isset($_POST['metakeyinput']) ) { + if ( !current_user_can( 'edit_post', $pid ) ) + wp_die( -1 ); + if ( isset($_POST['metakeyselect']) && '#NONE#' == $_POST['metakeyselect'] && empty($_POST['metakeyinput']) ) + wp_die( 1 ); + + // If the post is an autodraft, save the post as a draft and then attempt to save the meta. + if ( $post->post_status == 'auto-draft' ) { + $save_POST = $_POST; // Backup $_POST + $_POST = array(); // Make it empty for edit_post() + $_POST['action'] = 'draft'; // Warning fix + $_POST['post_ID'] = $pid; + $_POST['post_type'] = $post->post_type; + $_POST['post_status'] = 'draft'; + $now = current_time('timestamp', 1); + $_POST['post_title'] = sprintf( __( 'Draft created on %1$s at %2$s' ), date( get_option( 'date_format' ), $now ), date( get_option( 'time_format' ), $now ) ); + + if ( $pid = edit_post() ) { + if ( is_wp_error( $pid ) ) { + $x = new WP_Ajax_Response( array( + 'what' => 'meta', + 'data' => $pid + ) ); + $x->send(); + } + $_POST = $save_POST; // Now we can restore original $_POST again + if ( !$mid = add_meta( $pid ) ) + wp_die( __( 'Please provide a custom field value.' ) ); + } else { + wp_die( 0 ); + } + } else if ( !$mid = add_meta( $pid ) ) { + wp_die( __( 'Please provide a custom field value.' ) ); + } + + $meta = get_metadata_by_mid( 'post', $mid ); + $pid = (int) $meta->post_id; + $meta = get_object_vars( $meta ); + $x = new WP_Ajax_Response( array( + 'what' => 'meta', + 'id' => $mid, + 'data' => _list_meta_row( $meta, $c ), + 'position' => 1, + 'supplemental' => array('postid' => $pid) + ) ); + } else { // Update? + $mid = (int) key( $_POST['meta'] ); + $key = wp_unslash( $_POST['meta'][$mid]['key'] ); + $value = wp_unslash( $_POST['meta'][$mid]['value'] ); + if ( '' == trim($key) ) + wp_die( __( 'Please provide a custom field name.' ) ); + if ( '' == trim($value) ) + wp_die( __( 'Please provide a custom field value.' ) ); + if ( ! $meta = get_metadata_by_mid( 'post', $mid ) ) + wp_die( 0 ); // if meta doesn't exist + if ( is_protected_meta( $meta->meta_key, 'post' ) || is_protected_meta( $key, 'post' ) || + ! current_user_can( 'edit_post_meta', $meta->post_id, $meta->meta_key ) || + ! current_user_can( 'edit_post_meta', $meta->post_id, $key ) ) + wp_die( -1 ); + if ( $meta->meta_value != $value || $meta->meta_key != $key ) { + if ( !$u = update_metadata_by_mid( 'post', $mid, $value, $key ) ) + wp_die( 0 ); // We know meta exists; we also know it's unchanged (or DB error, in which case there are bigger problems). + } + + $x = new WP_Ajax_Response( array( + 'what' => 'meta', + 'id' => $mid, 'old_id' => $mid, + 'data' => _list_meta_row( array( + 'meta_key' => $key, + 'meta_value' => $value, + 'meta_id' => $mid + ), $c ), + 'position' => 0, + 'supplemental' => array('postid' => $meta->post_id) + ) ); + } + $x->send(); +} + +/** + * Ajax handler for adding a user. + * + * @since 3.1.0 + * + * @param string $action Action to perform. + */ +function wp_ajax_add_user( $action ) { + global $wp_list_table; + if ( empty( $action ) ) + $action = 'add-user'; + + check_ajax_referer( $action ); + if ( ! current_user_can('create_users') ) + wp_die( -1 ); + if ( ! $user_id = edit_user() ) { + wp_die( 0 ); + } elseif ( is_wp_error( $user_id ) ) { + $x = new WP_Ajax_Response( array( + 'what' => 'user', + 'id' => $user_id + ) ); + $x->send(); + } + $user_object = get_userdata( $user_id ); + + $wp_list_table = _get_list_table('WP_Users_List_Table'); + + $role = current( $user_object->roles ); + + $x = new WP_Ajax_Response( array( + 'what' => 'user', + 'id' => $user_id, + 'data' => $wp_list_table->single_row( $user_object, '', $role ), + 'supplemental' => array( + 'show-link' => sprintf(__( 'User %s added' ), "user-$user_id", $user_object->user_login), + 'role' => $role, + ) + ) ); + $x->send(); +} + +/** + * Ajax handler for closed post boxes. + * + * @since 3.1.0 + */ +function wp_ajax_closed_postboxes() { + check_ajax_referer( 'closedpostboxes', 'closedpostboxesnonce' ); + $closed = isset( $_POST['closed'] ) ? explode( ',', $_POST['closed']) : array(); + $closed = array_filter($closed); + + $hidden = isset( $_POST['hidden'] ) ? explode( ',', $_POST['hidden']) : array(); + $hidden = array_filter($hidden); + + $page = isset( $_POST['page'] ) ? $_POST['page'] : ''; + + if ( $page != sanitize_key( $page ) ) + wp_die( 0 ); + + if ( ! $user = wp_get_current_user() ) + wp_die( -1 ); + + if ( is_array($closed) ) + update_user_option($user->ID, "closedpostboxes_$page", $closed, true); + + if ( is_array($hidden) ) { + $hidden = array_diff( $hidden, array('submitdiv', 'linksubmitdiv', 'manage-menu', 'create-menu') ); // postboxes that are always shown + update_user_option($user->ID, "metaboxhidden_$page", $hidden, true); + } + + wp_die( 1 ); +} + +/** + * Ajax handler for hidden columns. + * + * @since 3.1.0 + */ +function wp_ajax_hidden_columns() { + check_ajax_referer( 'screen-options-nonce', 'screenoptionnonce' ); + $hidden = explode( ',', isset( $_POST['hidden'] ) ? $_POST['hidden'] : '' ); + $page = isset( $_POST['page'] ) ? $_POST['page'] : ''; + + if ( $page != sanitize_key( $page ) ) + wp_die( 0 ); + + if ( ! $user = wp_get_current_user() ) + wp_die( -1 ); + + if ( is_array($hidden) ) + update_user_option($user->ID, "manage{$page}columnshidden", $hidden, true); + + wp_die( 1 ); +} + +/** + * Ajax handler for updating whether to display the welcome panel. + * + * @since 3.1.0 + */ +function wp_ajax_update_welcome_panel() { + check_ajax_referer( 'welcome-panel-nonce', 'welcomepanelnonce' ); + + if ( ! current_user_can( 'edit_theme_options' ) ) + wp_die( -1 ); + + update_user_meta( get_current_user_id(), 'show_welcome_panel', empty( $_POST['visible'] ) ? 0 : 1 ); + + wp_die( 1 ); +} + +/** + * Ajax handler for retrieving menu meta boxes. + * + * @since 3.1.0 + */ +function wp_ajax_menu_get_metabox() { + if ( ! current_user_can( 'edit_theme_options' ) ) + wp_die( -1 ); + + require_once ABSPATH . 'wp-admin/includes/nav-menu.php'; + + if ( isset( $_POST['item-type'] ) && 'post_type' == $_POST['item-type'] ) { + $type = 'posttype'; + $callback = 'wp_nav_menu_item_post_type_meta_box'; + $items = (array) get_post_types( array( 'show_in_nav_menus' => true ), 'object' ); + } elseif ( isset( $_POST['item-type'] ) && 'taxonomy' == $_POST['item-type'] ) { + $type = 'taxonomy'; + $callback = 'wp_nav_menu_item_taxonomy_meta_box'; + $items = (array) get_taxonomies( array( 'show_ui' => true ), 'object' ); + } + + if ( ! empty( $_POST['item-object'] ) && isset( $items[$_POST['item-object']] ) ) { + $menus_meta_box_object = $items[ $_POST['item-object'] ]; + + /** This filter is documented in wp-admin/includes/nav-menu.php */ + $item = apply_filters( 'nav_menu_meta_box_object', $menus_meta_box_object ); + ob_start(); + call_user_func_array($callback, array( + null, + array( + 'id' => 'add-' . $item->name, + 'title' => $item->labels->name, + 'callback' => $callback, + 'args' => $item, + ) + )); + + $markup = ob_get_clean(); + + echo wp_json_encode(array( + 'replace-id' => $type . '-' . $item->name, + 'markup' => $markup, + )); + } + + wp_die(); +} + +/** + * Ajax handler for internal linking. + * + * @since 3.1.0 + */ +function wp_ajax_wp_link_ajax() { + check_ajax_referer( 'internal-linking', '_ajax_linking_nonce' ); + + $args = array(); + + if ( isset( $_POST['search'] ) ) + $args['s'] = wp_unslash( $_POST['search'] ); + $args['pagenum'] = ! empty( $_POST['page'] ) ? absint( $_POST['page'] ) : 1; + + require(ABSPATH . WPINC . '/class-wp-editor.php'); + $results = _WP_Editors::wp_link_query( $args ); + + if ( ! isset( $results ) ) + wp_die( 0 ); + + echo wp_json_encode( $results ); + echo "\n"; + + wp_die(); +} + +/** + * Ajax handler for menu locations save. + * + * @since 3.1.0 + */ +function wp_ajax_menu_locations_save() { + if ( ! current_user_can( 'edit_theme_options' ) ) + wp_die( -1 ); + check_ajax_referer( 'add-menu_item', 'menu-settings-column-nonce' ); + if ( ! isset( $_POST['menu-locations'] ) ) + wp_die( 0 ); + set_theme_mod( 'nav_menu_locations', array_map( 'absint', $_POST['menu-locations'] ) ); + wp_die( 1 ); +} + +/** + * Ajax handler for saving the meta box order. + * + * @since 3.1.0 + */ +function wp_ajax_meta_box_order() { + check_ajax_referer( 'meta-box-order' ); + $order = isset( $_POST['order'] ) ? (array) $_POST['order'] : false; + $page_columns = isset( $_POST['page_columns'] ) ? $_POST['page_columns'] : 'auto'; + + if ( $page_columns != 'auto' ) + $page_columns = (int) $page_columns; + + $page = isset( $_POST['page'] ) ? $_POST['page'] : ''; + + if ( $page != sanitize_key( $page ) ) + wp_die( 0 ); + + if ( ! $user = wp_get_current_user() ) + wp_die( -1 ); + + if ( $order ) + update_user_option($user->ID, "meta-box-order_$page", $order, true); + + if ( $page_columns ) + update_user_option($user->ID, "screen_layout_$page", $page_columns, true); + + wp_die( 1 ); +} + +/** + * Ajax handler for menu quick searching. + * + * @since 3.1.0 + */ +function wp_ajax_menu_quick_search() { + if ( ! current_user_can( 'edit_theme_options' ) ) + wp_die( -1 ); + + require_once ABSPATH . 'wp-admin/includes/nav-menu.php'; + + _wp_ajax_menu_quick_search( $_POST ); + + wp_die(); +} + +/** + * Ajax handler to retrieve a permalink. + * + * @since 3.1.0 + */ +function wp_ajax_get_permalink() { + check_ajax_referer( 'getpermalink', 'getpermalinknonce' ); + $post_id = isset($_POST['post_id'])? intval($_POST['post_id']) : 0; + wp_die( add_query_arg( array( 'preview' => 'true' ), get_permalink( $post_id ) ) ); +} + +/** + * Ajax handler to retrieve a sample permalink. + * + * @since 3.1.0 + */ +function wp_ajax_sample_permalink() { + check_ajax_referer( 'samplepermalink', 'samplepermalinknonce' ); + $post_id = isset($_POST['post_id'])? intval($_POST['post_id']) : 0; + $title = isset($_POST['new_title'])? $_POST['new_title'] : ''; + $slug = isset($_POST['new_slug'])? $_POST['new_slug'] : null; + wp_die( get_sample_permalink_html( $post_id, $title, $slug ) ); +} + +/** + * Ajax handler for Quick Edit saving a post from a list table. + * + * @since 3.1.0 + */ +function wp_ajax_inline_save() { + global $wp_list_table; + + check_ajax_referer( 'inlineeditnonce', '_inline_edit' ); + + if ( ! isset($_POST['post_ID']) || ! ( $post_ID = (int) $_POST['post_ID'] ) ) + wp_die(); + + if ( 'page' == $_POST['post_type'] ) { + if ( ! current_user_can( 'edit_page', $post_ID ) ) + wp_die( __( 'You are not allowed to edit this page.' ) ); + } else { + if ( ! current_user_can( 'edit_post', $post_ID ) ) + wp_die( __( 'You are not allowed to edit this post.' ) ); + } + + if ( $last = wp_check_post_lock( $post_ID ) ) { + $last_user = get_userdata( $last ); + $last_user_name = $last_user ? $last_user->display_name : __( 'Someone' ); + printf( $_POST['post_type'] == 'page' ? __( 'Saving is disabled: %s is currently editing this page.' ) : __( 'Saving is disabled: %s is currently editing this post.' ), esc_html( $last_user_name ) ); + wp_die(); + } + + $data = &$_POST; + + $post = get_post( $post_ID, ARRAY_A ); + + // Since it's coming from the database. + $post = wp_slash($post); + + $data['content'] = $post['post_content']; + $data['excerpt'] = $post['post_excerpt']; + + // Rename. + $data['user_ID'] = get_current_user_id(); + + if ( isset($data['post_parent']) ) + $data['parent_id'] = $data['post_parent']; + + // Status. + if ( isset($data['keep_private']) && 'private' == $data['keep_private'] ) + $data['post_status'] = 'private'; + else + $data['post_status'] = $data['_status']; + + if ( empty($data['comment_status']) ) + $data['comment_status'] = 'closed'; + if ( empty($data['ping_status']) ) + $data['ping_status'] = 'closed'; + + // Hack: wp_unique_post_slug() doesn't work for drafts, so we will fake that our post is published. + if ( ! empty( $data['post_name'] ) && in_array( $post['post_status'], array( 'draft', 'pending' ) ) ) { + $post['post_status'] = 'publish'; + $data['post_name'] = wp_unique_post_slug( $data['post_name'], $post['ID'], $post['post_status'], $post['post_type'], $post['post_parent'] ); + } + + // Update the post. + edit_post(); + + $wp_list_table = _get_list_table( 'WP_Posts_List_Table', array( 'screen' => $_POST['screen'] ) ); + + $level = 0; + $request_post = array( get_post( $_POST['post_ID'] ) ); + $parent = $request_post[0]->post_parent; + + while ( $parent > 0 ) { + $parent_post = get_post( $parent ); + $parent = $parent_post->post_parent; + $level++; + } + + $wp_list_table->display_rows( array( get_post( $_POST['post_ID'] ) ), $level ); + + wp_die(); +} + +/** + * Ajax handler for quick edit saving for a term. + * + * @since 3.1.0 + */ +function wp_ajax_inline_save_tax() { + global $wp_list_table; + + check_ajax_referer( 'taxinlineeditnonce', '_inline_edit' ); + + $taxonomy = sanitize_key( $_POST['taxonomy'] ); + $tax = get_taxonomy( $taxonomy ); + if ( ! $tax ) + wp_die( 0 ); + + if ( ! current_user_can( $tax->cap->edit_terms ) ) + wp_die( -1 ); + + $wp_list_table = _get_list_table( 'WP_Terms_List_Table', array( 'screen' => 'edit-' . $taxonomy ) ); + + if ( ! isset($_POST['tax_ID']) || ! ( $id = (int) $_POST['tax_ID'] ) ) + wp_die( -1 ); + + $tag = get_term( $id, $taxonomy ); + $_POST['description'] = $tag->description; + + $updated = wp_update_term($id, $taxonomy, $_POST); + if ( $updated && !is_wp_error($updated) ) { + $tag = get_term( $updated['term_id'], $taxonomy ); + if ( !$tag || is_wp_error( $tag ) ) { + if ( is_wp_error($tag) && $tag->get_error_message() ) + wp_die( $tag->get_error_message() ); + wp_die( __( 'Item not updated.' ) ); + } + } else { + if ( is_wp_error($updated) && $updated->get_error_message() ) + wp_die( $updated->get_error_message() ); + wp_die( __( 'Item not updated.' ) ); + } + $level = 0; + $parent = $tag->parent; + while ( $parent > 0 ) { + $parent_tag = get_term( $parent, $taxonomy ); + $parent = $parent_tag->parent; + $level++; + } + $wp_list_table->single_row( $tag, $level ); + wp_die(); +} + +/** + * Ajax handler for querying posts for the Find Posts modal. + * + * @see window.findPosts + * + * @since 3.1.0 + */ +function wp_ajax_find_posts() { + check_ajax_referer( 'find-posts' ); + + $post_types = get_post_types( array( 'public' => true ), 'objects' ); + unset( $post_types['attachment'] ); + + $s = wp_unslash( $_POST['ps'] ); + $args = array( + 'post_type' => array_keys( $post_types ), + 'post_status' => 'any', + 'posts_per_page' => 50, + ); + if ( '' !== $s ) + $args['s'] = $s; + + $posts = get_posts( $args ); + + if ( ! $posts ) { + wp_send_json_error( __( 'No items found.' ) ); + } + + $html = ''; + $alt = ''; + foreach ( $posts as $post ) { + $title = trim( $post->post_title ) ? $post->post_title : __( '(no title)' ); + $alt = ( 'alternate' == $alt ) ? '' : 'alternate'; + + switch ( $post->post_status ) { + case 'publish' : + case 'private' : + $stat = __('Published'); + break; + case 'future' : + $stat = __('Scheduled'); + break; + case 'pending' : + $stat = __('Pending Review'); + break; + case 'draft' : + $stat = __('Draft'); + break; + } + + if ( '0000-00-00 00:00:00' == $post->post_date ) { + $time = ''; + } else { + /* translators: date format in table columns, see http://php.net/date */ + $time = mysql2date(__('Y/m/d'), $post->post_date); + } + + $html .= ''; + $html .= '' . "\n\n"; + } + + $html .= '

    '.__('Title').''.__('Type').''.__('Date').''.__('Status').'
    ' . esc_html( $post_types[$post->post_type]->labels->singular_name ) . ''.esc_html( $time ) . '' . esc_html( $stat ). '
    '; + + wp_send_json_success( $html ); +} + +/** + * Ajax handler for saving the widgets order. + * + * @since 3.1.0 + */ +function wp_ajax_widgets_order() { + check_ajax_referer( 'save-sidebar-widgets', 'savewidgets' ); + + if ( !current_user_can('edit_theme_options') ) + wp_die( -1 ); + + unset( $_POST['savewidgets'], $_POST['action'] ); + + // Save widgets order for all sidebars. + if ( is_array($_POST['sidebars']) ) { + $sidebars = array(); + foreach ( $_POST['sidebars'] as $key => $val ) { + $sb = array(); + if ( !empty($val) ) { + $val = explode(',', $val); + foreach ( $val as $k => $v ) { + if ( strpos($v, 'widget-') === false ) + continue; + + $sb[$k] = substr($v, strpos($v, '_') + 1); + } + } + $sidebars[$key] = $sb; + } + wp_set_sidebars_widgets($sidebars); + wp_die( 1 ); + } + + wp_die( -1 ); +} + +/** + * Ajax handler for saving a widget. + * + * @since 3.1.0 + */ +function wp_ajax_save_widget() { + global $wp_registered_widgets, $wp_registered_widget_controls, $wp_registered_widget_updates; + + check_ajax_referer( 'save-sidebar-widgets', 'savewidgets' ); + + if ( !current_user_can('edit_theme_options') || !isset($_POST['id_base']) ) + wp_die( -1 ); + + unset( $_POST['savewidgets'], $_POST['action'] ); + + /** + * Fires early when editing the widgets displayed in sidebars. + * + * @since 2.8.0 + */ + do_action( 'load-widgets.php' ); + + /** + * Fires early when editing the widgets displayed in sidebars. + * + * @since 2.8.0 + */ + do_action( 'widgets.php' ); + + /** This action is documented in wp-admin/widgets.php */ + do_action( 'sidebar_admin_setup' ); + + $id_base = $_POST['id_base']; + $widget_id = $_POST['widget-id']; + $sidebar_id = $_POST['sidebar']; + $multi_number = !empty($_POST['multi_number']) ? (int) $_POST['multi_number'] : 0; + $settings = isset($_POST['widget-' . $id_base]) && is_array($_POST['widget-' . $id_base]) ? $_POST['widget-' . $id_base] : false; + $error = '

    ' . __('An error has occurred. Please reload the page and try again.') . '

    '; + + $sidebars = wp_get_sidebars_widgets(); + $sidebar = isset($sidebars[$sidebar_id]) ? $sidebars[$sidebar_id] : array(); + + // Delete. + if ( isset($_POST['delete_widget']) && $_POST['delete_widget'] ) { + + if ( !isset($wp_registered_widgets[$widget_id]) ) + wp_die( $error ); + + $sidebar = array_diff( $sidebar, array($widget_id) ); + $_POST = array('sidebar' => $sidebar_id, 'widget-' . $id_base => array(), 'the-widget-id' => $widget_id, 'delete_widget' => '1'); + } elseif ( $settings && preg_match( '/__i__|%i%/', key($settings) ) ) { + if ( !$multi_number ) + wp_die( $error ); + + $_POST['widget-' . $id_base] = array( $multi_number => array_shift($settings) ); + $widget_id = $id_base . '-' . $multi_number; + $sidebar[] = $widget_id; + } + $_POST['widget-id'] = $sidebar; + + foreach ( (array) $wp_registered_widget_updates as $name => $control ) { + + if ( $name == $id_base ) { + if ( !is_callable( $control['callback'] ) ) + continue; + + ob_start(); + call_user_func_array( $control['callback'], $control['params'] ); + ob_end_clean(); + break; + } + } + + if ( isset($_POST['delete_widget']) && $_POST['delete_widget'] ) { + $sidebars[$sidebar_id] = $sidebar; + wp_set_sidebars_widgets($sidebars); + echo "deleted:$widget_id"; + wp_die(); + } + + if ( !empty($_POST['add_new']) ) + wp_die(); + + if ( $form = $wp_registered_widget_controls[$widget_id] ) + call_user_func_array( $form['callback'], $form['params'] ); + + wp_die(); +} + +/** + * Ajax handler for saving a widget. + * + * @since 3.9.0 + */ +function wp_ajax_update_widget() { + global $wp_customize; + $wp_customize->widgets->wp_ajax_update_widget(); +} + +/** + * Ajax handler for uploading attachments + * + * @since 3.3.0 + */ +function wp_ajax_upload_attachment() { + check_ajax_referer( 'media-form' ); + /* + * This function does not use wp_send_json_success() / wp_send_json_error() + * as the html4 Plupload handler requires a text/html content-type for older IE. + * See https://core.trac.wordpress.org/ticket/31037 + */ + + if ( ! current_user_can( 'upload_files' ) ) { + echo wp_json_encode( array( + 'success' => false, + 'data' => array( + 'message' => __( "You don't have permission to upload files." ), + 'filename' => $_FILES['async-upload']['name'], + ) + ) ); + + wp_die(); + } + + if ( isset( $_REQUEST['post_id'] ) ) { + $post_id = $_REQUEST['post_id']; + if ( ! current_user_can( 'edit_post', $post_id ) ) { + echo wp_json_encode( array( + 'success' => false, + 'data' => array( + 'message' => __( "You don't have permission to attach files to this post." ), + 'filename' => $_FILES['async-upload']['name'], + ) + ) ); + + wp_die(); + } + } else { + $post_id = null; + } + + $post_data = isset( $_REQUEST['post_data'] ) ? $_REQUEST['post_data'] : array(); + + // If the context is custom header or background, make sure the uploaded file is an image. + if ( isset( $post_data['context'] ) && in_array( $post_data['context'], array( 'custom-header', 'custom-background' ) ) ) { + $wp_filetype = wp_check_filetype_and_ext( $_FILES['async-upload']['tmp_name'], $_FILES['async-upload']['name'], false ); + if ( ! wp_match_mime_types( 'image', $wp_filetype['type'] ) ) { + echo wp_json_encode( array( + 'success' => false, + 'data' => array( + 'message' => __( 'The uploaded file is not a valid image. Please try again.' ), + 'filename' => $_FILES['async-upload']['name'], + ) + ) ); + + wp_die(); + } + } + + $attachment_id = media_handle_upload( 'async-upload', $post_id, $post_data ); + + if ( is_wp_error( $attachment_id ) ) { + echo wp_json_encode( array( + 'success' => false, + 'data' => array( + 'message' => $attachment_id->get_error_message(), + 'filename' => $_FILES['async-upload']['name'], + ) + ) ); + + wp_die(); + } + + if ( isset( $post_data['context'] ) && isset( $post_data['theme'] ) ) { + if ( 'custom-background' === $post_data['context'] ) + update_post_meta( $attachment_id, '_wp_attachment_is_custom_background', $post_data['theme'] ); + + if ( 'custom-header' === $post_data['context'] ) + update_post_meta( $attachment_id, '_wp_attachment_is_custom_header', $post_data['theme'] ); + } + + if ( ! $attachment = wp_prepare_attachment_for_js( $attachment_id ) ) + wp_die(); + + echo wp_json_encode( array( + 'success' => true, + 'data' => $attachment, + ) ); + + wp_die(); +} + +/** + * Ajax handler for image editing. + * + * @since 3.1.0 + */ +function wp_ajax_image_editor() { + $attachment_id = intval($_POST['postid']); + if ( empty($attachment_id) || !current_user_can('edit_post', $attachment_id) ) + wp_die( -1 ); + + check_ajax_referer( "image_editor-$attachment_id" ); + include_once( ABSPATH . 'wp-admin/includes/image-edit.php' ); + + $msg = false; + switch ( $_POST['do'] ) { + case 'save' : + $msg = wp_save_image($attachment_id); + $msg = wp_json_encode($msg); + wp_die( $msg ); + break; + case 'scale' : + $msg = wp_save_image($attachment_id); + break; + case 'restore' : + $msg = wp_restore_image($attachment_id); + break; + } + + wp_image_editor($attachment_id, $msg); + wp_die(); +} + +/** + * Ajax handler for setting the featured image. + * + * @since 3.1.0 + */ +function wp_ajax_set_post_thumbnail() { + $json = ! empty( $_REQUEST['json'] ); // New-style request + + $post_ID = intval( $_POST['post_id'] ); + if ( ! current_user_can( 'edit_post', $post_ID ) ) + wp_die( -1 ); + + $thumbnail_id = intval( $_POST['thumbnail_id'] ); + + if ( $json ) + check_ajax_referer( "update-post_$post_ID" ); + else + check_ajax_referer( "set_post_thumbnail-$post_ID" ); + + if ( $thumbnail_id == '-1' ) { + if ( delete_post_thumbnail( $post_ID ) ) { + $return = _wp_post_thumbnail_html( null, $post_ID ); + $json ? wp_send_json_success( $return ) : wp_die( $return ); + } else { + wp_die( 0 ); + } + } + + if ( set_post_thumbnail( $post_ID, $thumbnail_id ) ) { + $return = _wp_post_thumbnail_html( $thumbnail_id, $post_ID ); + $json ? wp_send_json_success( $return ) : wp_die( $return ); + } + + wp_die( 0 ); +} + +/** + * AJAX handler for setting the featured image for an attachment. + * + * @since 4.0.0 + * + * @see set_post_thumbnail() + */ +function wp_ajax_set_attachment_thumbnail() { + if ( empty( $_POST['urls'] ) || ! is_array( $_POST['urls'] ) ) { + wp_send_json_error(); + } + + $thumbnail_id = (int) $_POST['thumbnail_id']; + if ( empty( $thumbnail_id ) ) { + wp_send_json_error(); + } + + $post_ids = array(); + // For each URL, try to find its corresponding post ID. + foreach ( $_POST['urls'] as $url ) { + $post_id = attachment_url_to_postid( $url ); + if ( ! empty( $post_id ) ) { + $post_ids[] = $post_id; + } + } + + if ( empty( $post_ids ) ) { + wp_send_json_error(); + } + + $success = 0; + // For each found attachment, set its thumbnail. + foreach ( $post_ids as $post_id ) { + if ( ! current_user_can( 'edit_post', $post_id ) ) { + continue; + } + + if ( set_post_thumbnail( $post_id, $thumbnail_id ) ) { + $success++; + } + } + + if ( 0 === $success ) { + wp_send_json_error(); + } else { + wp_send_json_success(); + } + + wp_send_json_error(); +} + +/** + * Ajax handler for date formatting. + * + * @since 3.1.0 + */ +function wp_ajax_date_format() { + wp_die( date_i18n( sanitize_option( 'date_format', wp_unslash( $_POST['date'] ) ) ) ); +} + +/** + * Ajax handler for time formatting. + * + * @since 3.1.0 + */ +function wp_ajax_time_format() { + wp_die( date_i18n( sanitize_option( 'time_format', wp_unslash( $_POST['date'] ) ) ) ); +} + +/** + * Ajax handler for saving posts from the fullscreen editor. + * + * @since 3.1.0 + */ +function wp_ajax_wp_fullscreen_save_post() { + $post_id = isset( $_POST['post_ID'] ) ? (int) $_POST['post_ID'] : 0; + + $post = null; + + if ( $post_id ) + $post = get_post( $post_id ); + + check_ajax_referer('update-post_' . $post_id, '_wpnonce'); + + $post_id = edit_post(); + + if ( is_wp_error( $post_id ) ) { + wp_send_json_error(); + } + + if ( $post ) { + $last_date = mysql2date( get_option('date_format'), $post->post_modified ); + $last_time = mysql2date( get_option('time_format'), $post->post_modified ); + } else { + $last_date = date_i18n( get_option('date_format') ); + $last_time = date_i18n( get_option('time_format') ); + } + + if ( $last_id = get_post_meta( $post_id, '_edit_last', true ) ) { + $last_user = get_userdata( $last_id ); + $last_edited = sprintf( __('Last edited by %1$s on %2$s at %3$s'), esc_html( $last_user->display_name ), $last_date, $last_time ); + } else { + $last_edited = sprintf( __('Last edited on %1$s at %2$s'), $last_date, $last_time ); + } + + wp_send_json_success( array( 'last_edited' => $last_edited ) ); +} + +/** + * Ajax handler for removing a post lock. + * + * @since 3.1.0 + */ +function wp_ajax_wp_remove_post_lock() { + if ( empty( $_POST['post_ID'] ) || empty( $_POST['active_post_lock'] ) ) + wp_die( 0 ); + $post_id = (int) $_POST['post_ID']; + if ( ! $post = get_post( $post_id ) ) + wp_die( 0 ); + + check_ajax_referer( 'update-post_' . $post_id ); + + if ( ! current_user_can( 'edit_post', $post_id ) ) + wp_die( -1 ); + + $active_lock = array_map( 'absint', explode( ':', $_POST['active_post_lock'] ) ); + if ( $active_lock[1] != get_current_user_id() ) + wp_die( 0 ); + + /** + * Filter the post lock window duration. + * + * @since 3.3.0 + * + * @param int $interval The interval in seconds the post lock duration + * should last, plus 5 seconds. Default 150. + */ + $new_lock = ( time() - apply_filters( 'wp_check_post_lock_window', 150 ) + 5 ) . ':' . $active_lock[1]; + update_post_meta( $post_id, '_edit_lock', $new_lock, implode( ':', $active_lock ) ); + wp_die( 1 ); +} + +/** + * Ajax handler for dismissing a WordPress pointer. + * + * @since 3.1.0 + */ +function wp_ajax_dismiss_wp_pointer() { + $pointer = $_POST['pointer']; + if ( $pointer != sanitize_key( $pointer ) ) + wp_die( 0 ); + +// check_ajax_referer( 'dismiss-pointer_' . $pointer ); + + $dismissed = array_filter( explode( ',', (string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) ) ); + + if ( in_array( $pointer, $dismissed ) ) + wp_die( 0 ); + + $dismissed[] = $pointer; + $dismissed = implode( ',', $dismissed ); + + update_user_meta( get_current_user_id(), 'dismissed_wp_pointers', $dismissed ); + wp_die( 1 ); +} + +/** + * Ajax handler for getting an attachment. + * + * @since 3.5.0 + */ +function wp_ajax_get_attachment() { + if ( ! isset( $_REQUEST['id'] ) ) + wp_send_json_error(); + + if ( ! $id = absint( $_REQUEST['id'] ) ) + wp_send_json_error(); + + if ( ! $post = get_post( $id ) ) + wp_send_json_error(); + + if ( 'attachment' != $post->post_type ) + wp_send_json_error(); + + if ( ! current_user_can( 'upload_files' ) ) + wp_send_json_error(); + + if ( ! $attachment = wp_prepare_attachment_for_js( $id ) ) + wp_send_json_error(); + + wp_send_json_success( $attachment ); +} + +/** + * Ajax handler for querying attachments. + * + * @since 3.5.0 + */ +function wp_ajax_query_attachments() { + if ( ! current_user_can( 'upload_files' ) ) + wp_send_json_error(); + + $query = isset( $_REQUEST['query'] ) ? (array) $_REQUEST['query'] : array(); + $query = array_intersect_key( $query, array_flip( array( + 's', 'order', 'orderby', 'posts_per_page', 'paged', 'post_mime_type', + 'post_parent', 'post__in', 'post__not_in', 'year', 'monthnum' + ) ) ); + + $query['post_type'] = 'attachment'; + if ( MEDIA_TRASH + && ! empty( $_REQUEST['query']['post_status'] ) + && 'trash' === $_REQUEST['query']['post_status'] ) { + $query['post_status'] = 'trash'; + } else { + $query['post_status'] = 'inherit'; + } + + if ( current_user_can( get_post_type_object( 'attachment' )->cap->read_private_posts ) ) + $query['post_status'] .= ',private'; + + /** + * Filter the arguments passed to WP_Query during an AJAX + * call for querying attachments. + * + * @since 3.7.0 + * + * @see WP_Query::parse_query() + * + * @param array $query An array of query variables. + */ + $query = apply_filters( 'ajax_query_attachments_args', $query ); + $query = new WP_Query( $query ); + + $posts = array_map( 'wp_prepare_attachment_for_js', $query->posts ); + $posts = array_filter( $posts ); + + wp_send_json_success( $posts ); +} + +/** + * Ajax handler for updating attachment attributes. + * + * @since 3.5.0 + */ +function wp_ajax_save_attachment() { + if ( ! isset( $_REQUEST['id'] ) || ! isset( $_REQUEST['changes'] ) ) + wp_send_json_error(); + + if ( ! $id = absint( $_REQUEST['id'] ) ) + wp_send_json_error(); + + check_ajax_referer( 'update-post_' . $id, 'nonce' ); + + if ( ! current_user_can( 'edit_post', $id ) ) + wp_send_json_error(); + + $changes = $_REQUEST['changes']; + $post = get_post( $id, ARRAY_A ); + + if ( 'attachment' != $post['post_type'] ) + wp_send_json_error(); + + if ( isset( $changes['title'] ) ) + $post['post_title'] = $changes['title']; + + if ( isset( $changes['caption'] ) ) + $post['post_excerpt'] = $changes['caption']; + + if ( isset( $changes['description'] ) ) + $post['post_content'] = $changes['description']; + + if ( MEDIA_TRASH && isset( $changes['status'] ) ) + $post['post_status'] = $changes['status']; + + if ( isset( $changes['alt'] ) ) { + $alt = wp_unslash( $changes['alt'] ); + if ( $alt != get_post_meta( $id, '_wp_attachment_image_alt', true ) ) { + $alt = wp_strip_all_tags( $alt, true ); + update_post_meta( $id, '_wp_attachment_image_alt', wp_slash( $alt ) ); + } + } + + if ( 0 === strpos( $post['post_mime_type'], 'audio/' ) ) { + $changed = false; + $id3data = wp_get_attachment_metadata( $post['ID'] ); + if ( ! is_array( $id3data ) ) { + $changed = true; + $id3data = array(); + } + foreach ( wp_get_attachment_id3_keys( (object) $post, 'edit' ) as $key => $label ) { + if ( isset( $changes[ $key ] ) ) { + $changed = true; + $id3data[ $key ] = sanitize_text_field( wp_unslash( $changes[ $key ] ) ); + } + } + + if ( $changed ) { + wp_update_attachment_metadata( $id, $id3data ); + } + } + + if ( MEDIA_TRASH && isset( $changes['status'] ) && 'trash' === $changes['status'] ) { + wp_delete_post( $id ); + } else { + wp_update_post( $post ); + } + + wp_send_json_success(); +} + +/** + * Ajax handler for saving backwards compatible attachment attributes. + * + * @since 3.5.0 + */ +function wp_ajax_save_attachment_compat() { + if ( ! isset( $_REQUEST['id'] ) ) + wp_send_json_error(); + + if ( ! $id = absint( $_REQUEST['id'] ) ) + wp_send_json_error(); + + if ( empty( $_REQUEST['attachments'] ) || empty( $_REQUEST['attachments'][ $id ] ) ) + wp_send_json_error(); + $attachment_data = $_REQUEST['attachments'][ $id ]; + + check_ajax_referer( 'update-post_' . $id, 'nonce' ); + + if ( ! current_user_can( 'edit_post', $id ) ) + wp_send_json_error(); + + $post = get_post( $id, ARRAY_A ); + + if ( 'attachment' != $post['post_type'] ) + wp_send_json_error(); + + /** This filter is documented in wp-admin/includes/media.php */ + $post = apply_filters( 'attachment_fields_to_save', $post, $attachment_data ); + + if ( isset( $post['errors'] ) ) { + $errors = $post['errors']; // @todo return me and display me! + unset( $post['errors'] ); + } + + wp_update_post( $post ); + + foreach ( get_attachment_taxonomies( $post ) as $taxonomy ) { + if ( isset( $attachment_data[ $taxonomy ] ) ) + wp_set_object_terms( $id, array_map( 'trim', preg_split( '/,+/', $attachment_data[ $taxonomy ] ) ), $taxonomy, false ); + } + + if ( ! $attachment = wp_prepare_attachment_for_js( $id ) ) + wp_send_json_error(); + + wp_send_json_success( $attachment ); +} + +/** + * Ajax handler for saving the attachment order. + * + * @since 3.5.0 + */ +function wp_ajax_save_attachment_order() { + if ( ! isset( $_REQUEST['post_id'] ) ) + wp_send_json_error(); + + if ( ! $post_id = absint( $_REQUEST['post_id'] ) ) + wp_send_json_error(); + + if ( empty( $_REQUEST['attachments'] ) ) + wp_send_json_error(); + + check_ajax_referer( 'update-post_' . $post_id, 'nonce' ); + + $attachments = $_REQUEST['attachments']; + + if ( ! current_user_can( 'edit_post', $post_id ) ) + wp_send_json_error(); + + foreach ( $attachments as $attachment_id => $menu_order ) { + if ( ! current_user_can( 'edit_post', $attachment_id ) ) + continue; + if ( ! $attachment = get_post( $attachment_id ) ) + continue; + if ( 'attachment' != $attachment->post_type ) + continue; + + wp_update_post( array( 'ID' => $attachment_id, 'menu_order' => $menu_order ) ); + } + + wp_send_json_success(); +} + +/** + * Ajax handler for sending an attachment to the editor. + * + * Generates the HTML to send an attachment to the editor. + * Backwards compatible with the media_send_to_editor filter + * and the chain of filters that follow. + * + * @since 3.5.0 + */ +function wp_ajax_send_attachment_to_editor() { + check_ajax_referer( 'media-send-to-editor', 'nonce' ); + + $attachment = wp_unslash( $_POST['attachment'] ); + + $id = intval( $attachment['id'] ); + + if ( ! $post = get_post( $id ) ) + wp_send_json_error(); + + if ( 'attachment' != $post->post_type ) + wp_send_json_error(); + + if ( current_user_can( 'edit_post', $id ) ) { + // If this attachment is unattached, attach it. Primarily a back compat thing. + if ( 0 == $post->post_parent && $insert_into_post_id = intval( $_POST['post_id'] ) ) { + wp_update_post( array( 'ID' => $id, 'post_parent' => $insert_into_post_id ) ); + } + } + + $rel = $url = ''; + $html = isset( $attachment['post_title'] ) ? $attachment['post_title'] : ''; + if ( ! empty( $attachment['url'] ) ) { + $url = $attachment['url']; + if ( strpos( $url, 'attachment_id') || get_attachment_link( $id ) == $url ) + $rel = ' rel="attachment wp-att-' . $id . '"'; + $html = '' . $html . ''; + } + + remove_filter( 'media_send_to_editor', 'image_media_send_to_editor' ); + + if ( 'image' === substr( $post->post_mime_type, 0, 5 ) ) { + $align = isset( $attachment['align'] ) ? $attachment['align'] : 'none'; + $size = isset( $attachment['image-size'] ) ? $attachment['image-size'] : 'medium'; + $alt = isset( $attachment['image_alt'] ) ? $attachment['image_alt'] : ''; + $caption = isset( $attachment['post_excerpt'] ) ? $attachment['post_excerpt'] : ''; + $title = ''; // We no longer insert title tags into tags, as they are redundant. + $html = get_image_send_to_editor( $id, $caption, $title, $align, $url, (bool) $rel, $size, $alt ); + } elseif ( 'video' === substr( $post->post_mime_type, 0, 5 ) || 'audio' === substr( $post->post_mime_type, 0, 5 ) ) { + $html = stripslashes_deep( $_POST['html'] ); + } + + /** This filter is documented in wp-admin/includes/media.php */ + $html = apply_filters( 'media_send_to_editor', $html, $id, $attachment ); + + wp_send_json_success( $html ); +} + +/** + * Ajax handler for sending a link to the editor. + * + * Generates the HTML to send a non-image embed link to the editor. + * + * Backwards compatible with the following filters: + * - file_send_to_editor_url + * - audio_send_to_editor_url + * - video_send_to_editor_url + * + * @since 3.5.0 + */ +function wp_ajax_send_link_to_editor() { + global $post, $wp_embed; + + check_ajax_referer( 'media-send-to-editor', 'nonce' ); + + if ( ! $src = wp_unslash( $_POST['src'] ) ) + wp_send_json_error(); + + if ( ! strpos( $src, '://' ) ) + $src = 'http://' . $src; + + if ( ! $src = esc_url_raw( $src ) ) + wp_send_json_error(); + + if ( ! $title = trim( wp_unslash( $_POST['title'] ) ) ) + $title = wp_basename( $src ); + + $post = get_post( isset( $_POST['post_id'] ) ? $_POST['post_id'] : 0 ); + + // Ping WordPress for an embed. + $check_embed = $wp_embed->run_shortcode( '[embed]'. $src .'[/embed]' ); + + // Fallback that WordPress creates when no oEmbed was found. + $fallback = $wp_embed->maybe_make_link( $src ); + + if ( $check_embed !== $fallback ) { + // TinyMCE view for [embed] will parse this + $html = '[embed]' . $src . '[/embed]'; + } elseif ( $title ) { + $html = '' . $title . ''; + } else { + $html = ''; + } + + // Figure out what filter to run: + $type = 'file'; + if ( ( $ext = preg_replace( '/^.+?\.([^.]+)$/', '$1', $src ) ) && ( $ext_type = wp_ext2type( $ext ) ) + && ( 'audio' == $ext_type || 'video' == $ext_type ) ) + $type = $ext_type; + + /** This filter is documented in wp-admin/includes/media.php */ + $html = apply_filters( $type . '_send_to_editor_url', $html, $src, $title ); + + wp_send_json_success( $html ); +} + +/** + * Ajax handler for the Heartbeat API. + * + * Runs when the user is logged in. + * + * @since 3.6.0 + */ +function wp_ajax_heartbeat() { + if ( empty( $_POST['_nonce'] ) ) + wp_send_json_error(); + + $response = array(); + + if ( false === wp_verify_nonce( $_POST['_nonce'], 'heartbeat-nonce' ) ) { + // User is logged in but nonces have expired. + $response['nonces_expired'] = true; + wp_send_json($response); + } + + // screen_id is the same as $current_screen->id and the JS global 'pagenow'. + if ( ! empty($_POST['screen_id']) ) + $screen_id = sanitize_key($_POST['screen_id']); + else + $screen_id = 'front'; + + if ( ! empty($_POST['data']) ) { + $data = wp_unslash( (array) $_POST['data'] ); + + /** + * Filter the Heartbeat response received. + * + * @since 3.6.0 + * + * @param array|object $response The Heartbeat response object or array. + * @param array $data The $_POST data sent. + * @param string $screen_id The screen id. + */ + $response = apply_filters( 'heartbeat_received', $response, $data, $screen_id ); + } + + /** + * Filter the Heartbeat response sent. + * + * @since 3.6.0 + * + * @param array|object $response The Heartbeat response object or array. + * @param string $screen_id The screen id. + */ + $response = apply_filters( 'heartbeat_send', $response, $screen_id ); + + /** + * Fires when Heartbeat ticks in logged-in environments. + * + * Allows the transport to be easily replaced with long-polling. + * + * @since 3.6.0 + * + * @param array|object $response The Heartbeat response object or array. + * @param string $screen_id The screen id. + */ + do_action( 'heartbeat_tick', $response, $screen_id ); + + // Send the current time according to the server + $response['server_time'] = time(); + + wp_send_json($response); +} + +/** + * Ajax handler for getting revision diffs. + * + * @since 3.6.0 + */ +function wp_ajax_get_revision_diffs() { + require ABSPATH . 'wp-admin/includes/revision.php'; + + if ( ! $post = get_post( (int) $_REQUEST['post_id'] ) ) + wp_send_json_error(); + + if ( ! current_user_can( 'read_post', $post->ID ) ) + wp_send_json_error(); + + // Really just pre-loading the cache here. + if ( ! $revisions = wp_get_post_revisions( $post->ID, array( 'check_enabled' => false ) ) ) + wp_send_json_error(); + + $return = array(); + @set_time_limit( 0 ); + + foreach ( $_REQUEST['compare'] as $compare_key ) { + list( $compare_from, $compare_to ) = explode( ':', $compare_key ); // from:to + + $return[] = array( + 'id' => $compare_key, + 'fields' => wp_get_revision_ui_diff( $post, $compare_from, $compare_to ), + ); + } + wp_send_json_success( $return ); +} + +/** + * Ajax handler for auto-saving the selected color scheme for + * a user's own profile. + * + * @since 3.8.0 + */ +function wp_ajax_save_user_color_scheme() { + global $_wp_admin_css_colors; + + check_ajax_referer( 'save-color-scheme', 'nonce' ); + + $color_scheme = sanitize_key( $_POST['color_scheme'] ); + + if ( ! isset( $_wp_admin_css_colors[ $color_scheme ] ) ) { + wp_send_json_error(); + } + + update_user_meta( get_current_user_id(), 'admin_color', $color_scheme ); + wp_send_json_success(); +} + +/** + * Ajax handler for getting themes from themes_api(). + * + * @since 3.9.0 + */ +function wp_ajax_query_themes() { + global $themes_allowedtags, $theme_field_defaults; + + if ( ! current_user_can( 'install_themes' ) ) { + wp_send_json_error(); + } + + $args = wp_parse_args( wp_unslash( $_REQUEST['request'] ), array( + 'per_page' => 20, + 'fields' => $theme_field_defaults + ) ); + + $old_filter = isset( $args['browse'] ) ? $args['browse'] : 'search'; + + /** This filter is documented in wp-admin/includes/class-wp-theme-install-list-table.php */ + $args = apply_filters( 'install_themes_table_api_args_' . $old_filter, $args ); + + $api = themes_api( 'query_themes', $args ); + + if ( is_wp_error( $api ) ) { + wp_send_json_error(); + } + + $update_php = network_admin_url( 'update.php?action=install-theme' ); + foreach ( $api->themes as &$theme ) { + $theme->install_url = add_query_arg( array( + 'theme' => $theme->slug, + '_wpnonce' => wp_create_nonce( 'install-theme_' . $theme->slug ) + ), $update_php ); + + $theme->name = wp_kses( $theme->name, $themes_allowedtags ); + $theme->author = wp_kses( $theme->author, $themes_allowedtags ); + $theme->version = wp_kses( $theme->version, $themes_allowedtags ); + $theme->description = wp_kses( $theme->description, $themes_allowedtags ); + $theme->num_ratings = sprintf( _n( '(based on %s rating)', '(based on %s ratings)', $theme->num_ratings ), number_format_i18n( $theme->num_ratings ) ); + $theme->preview_url = set_url_scheme( $theme->preview_url ); + } + + wp_send_json_success( $api ); +} + +/** + * Apply [embed] AJAX handlers to a string. + * + * @since 4.0.0 + * + * @global WP_Post $post Global $post. + * @global WP_Embed $wp_embed Embed API instance. + */ +function wp_ajax_parse_embed() { + global $post, $wp_embed; + + if ( ! $post = get_post( (int) $_POST['post_ID'] ) ) { + wp_send_json_error(); + } + + if ( empty( $_POST['shortcode'] ) || ! current_user_can( 'edit_post', $post->ID ) ) { + wp_send_json_error(); + } + + $shortcode = wp_unslash( $_POST['shortcode'] ); + $url = str_replace( '[embed]', '', str_replace( '[/embed]', '', $shortcode ) ); + $parsed = false; + setup_postdata( $post ); + + $wp_embed->return_false_on_fail = true; + + if ( is_ssl() && preg_match( '%^\\[embed[^\\]]*\\]http://%i', $shortcode ) ) { + // Admin is ssl and the user pasted non-ssl URL. + // Check if the provider supports ssl embeds and use that for the preview. + $ssl_shortcode = preg_replace( '%^(\\[embed[^\\]]*\\])http://%i', '$1https://', $shortcode ); + $parsed = $wp_embed->run_shortcode( $ssl_shortcode ); + + if ( ! $parsed ) { + $no_ssl_support = true; + } + } + + if ( ! $parsed ) { + $parsed = $wp_embed->run_shortcode( $shortcode ); + } + + if ( ! $parsed ) { + wp_send_json_error( array( + 'type' => 'not-embeddable', + 'message' => sprintf( __( '%s failed to embed.' ), '' . esc_html( $url ) . '' ), + ) ); + } + + if ( has_shortcode( $parsed, 'audio' ) || has_shortcode( $parsed, 'video' ) ) { + $styles = ''; + $mce_styles = wpview_media_sandbox_styles(); + foreach ( $mce_styles as $style ) { + $styles .= sprintf( '', $style ); + } + + $html = do_shortcode( $parsed ); + + global $wp_scripts; + if ( ! empty( $wp_scripts ) ) { + $wp_scripts->done = array(); + } + ob_start(); + wp_print_scripts( 'wp-mediaelement' ); + $scripts = ob_get_clean(); + + $parsed = $styles . $html . $scripts; + } + + + if ( ! empty( $no_ssl_support ) || ( is_ssl() && ( preg_match( '%<(iframe|script|embed) [^>]*src="http://%', $parsed ) || + preg_match( '%]*href="http://%', $parsed ) ) ) ) { + // Admin is ssl and the embed is not. Iframes, scripts, and other "active content" will be blocked. + wp_send_json_error( array( + 'type' => 'not-ssl', + 'message' => __( 'This preview is unavailable in the editor.' ), + ) ); + } + + wp_send_json_success( array( + 'body' => $parsed + ) ); +} + +function wp_ajax_parse_media_shortcode() { + global $post, $wp_scripts; + + if ( ! $post = get_post( (int) $_POST['post_ID'] ) ) { + wp_send_json_error(); + } + + if ( empty( $_POST['shortcode'] ) || ! current_user_can( 'edit_post', $post->ID ) ) { + wp_send_json_error(); + } + + setup_postdata( $post ); + $shortcode = do_shortcode( wp_unslash( $_POST['shortcode'] ) ); + + if ( empty( $shortcode ) ) { + wp_send_json_error( array( + 'type' => 'no-items', + 'message' => __( 'No items found.' ), + ) ); + } + + $head = ''; + $styles = wpview_media_sandbox_styles(); + + foreach ( $styles as $style ) { + $head .= ''; + } + + if ( ! empty( $wp_scripts ) ) { + $wp_scripts->done = array(); + } + + ob_start(); + + echo $shortcode; + + if ( 'playlist' === $_REQUEST['type'] ) { + wp_underscore_playlist_templates(); + + wp_print_scripts( 'wp-playlist' ); + } else { + wp_print_scripts( 'wp-mediaelement' ); + } + + wp_send_json_success( array( + 'head' => $head, + 'body' => ob_get_clean() + ) ); +} + +/** + * AJAX handler for destroying multiple open sessions for a user. + * + * @since 4.1.0 + */ +function wp_ajax_destroy_sessions() { + + $user = get_userdata( (int) $_POST['user_id'] ); + if ( $user ) { + if ( ! current_user_can( 'edit_user', $user->ID ) ) { + $user = false; + } elseif ( ! wp_verify_nonce( $_POST['nonce'], 'update-user_' . $user->ID ) ) { + $user = false; + } + } + + if ( ! $user ) { + wp_send_json_error( array( + 'message' => __( 'Could not log out user sessions. Please try again.' ), + ) ); + } + + $sessions = WP_Session_Tokens::get_instance( $user->ID ); + + if ( $user->ID === get_current_user_id() ) { + $sessions->destroy_others( wp_get_session_token() ); + $message = __( 'You are now logged out everywhere else.' ); + } else { + $sessions->destroy_all(); + /* translators: 1: User's display name. */ + $message = sprintf( __( '%s has been logged out.' ), $user->display_name ); + } + + wp_send_json_success( array( 'message' => $message ) ); +} diff --git a/wp-admin/includes/bookmark.php b/wp-admin/includes/bookmark.php new file mode 100644 index 0000000..b73df8b --- /dev/null +++ b/wp-admin/includes/bookmark.php @@ -0,0 +1,305 @@ +link_url = esc_url( wp_unslash( $_GET['linkurl'] ) ); + else + $link->link_url = ''; + + if ( isset( $_GET['name'] ) ) + $link->link_name = esc_attr( wp_unslash( $_GET['name'] ) ); + else + $link->link_name = ''; + + $link->link_visible = 'Y'; + + return $link; +} + +/** + * Delete link specified from database. + * + * @since 2.0.0 + * + * @param int $link_id ID of the link to delete + * @return bool True + */ +function wp_delete_link( $link_id ) { + global $wpdb; + /** + * Fires before a link is deleted. + * + * @since 2.0.0 + * + * @param int $link_id ID of the link to delete. + */ + do_action( 'delete_link', $link_id ); + + wp_delete_object_term_relationships( $link_id, 'link_category' ); + + $wpdb->delete( $wpdb->links, array( 'link_id' => $link_id ) ); + /** + * Fires after a link has been deleted. + * + * @since 2.2.0 + * + * @param int $link_id ID of the deleted link. + */ + do_action( 'deleted_link', $link_id ); + + clean_bookmark_cache( $link_id ); + + return true; +} + +/** + * Retrieves the link categories associated with the link specified. + * + * @since 2.1.0 + * + * @param int $link_id Link ID to look up + * @return array The requested link's categories + */ +function wp_get_link_cats( $link_id = 0 ) { + + $cats = wp_get_object_terms( $link_id, 'link_category', array('fields' => 'ids') ); + + return array_unique( $cats ); +} + +/** + * Retrieve link data based on ID. + * + * @since 2.0.0 + * + * @param int $link_id ID of link to retrieve + * @return object Link for editing + */ +function get_link_to_edit( $link_id ) { + return get_bookmark( $link_id, OBJECT, 'edit' ); +} + +/** + * This function inserts/updates links into/in the database. + * + * @since 2.0.0 + * + * @param array $linkdata Elements that make up the link to insert. + * @param bool $wp_error Optional. If true return WP_Error object on failure. + * @return int|WP_Error Value 0 or WP_Error on failure. The link ID on success. + */ +function wp_insert_link( $linkdata, $wp_error = false ) { + global $wpdb; + + $defaults = array( 'link_id' => 0, 'link_name' => '', 'link_url' => '', 'link_rating' => 0 ); + + $args = wp_parse_args( $linkdata, $defaults ); + $r = wp_unslash( sanitize_bookmark( $args, 'db' ) ); + + $link_id = $r['link_id']; + $link_name = $r['link_name']; + $link_url = $r['link_url']; + + $update = false; + if ( ! empty( $link_id ) ) { + $update = true; + } + + if ( trim( $link_name ) == '' ) { + if ( trim( $link_url ) != '' ) { + $link_name = $link_url; + } else { + return 0; + } + } + + if ( trim( $link_url ) == '' ) { + return 0; + } + + $link_rating = ( ! empty( $r['link_rating'] ) ) ? $r['link_rating'] : 0; + $link_image = ( ! empty( $r['link_image'] ) ) ? $r['link_image'] : ''; + $link_target = ( ! empty( $r['link_target'] ) ) ? $r['link_target'] : ''; + $link_visible = ( ! empty( $r['link_visible'] ) ) ? $r['link_visible'] : 'Y'; + $link_owner = ( ! empty( $r['link_owner'] ) ) ? $r['link_owner'] : get_current_user_id(); + $link_notes = ( ! empty( $r['link_notes'] ) ) ? $r['link_notes'] : ''; + $link_description = ( ! empty( $r['link_description'] ) ) ? $r['link_description'] : ''; + $link_rss = ( ! empty( $r['link_rss'] ) ) ? $r['link_rss'] : ''; + $link_rel = ( ! empty( $r['link_rel'] ) ) ? $r['link_rel'] : ''; + $link_category = ( ! empty( $r['link_category'] ) ) ? $r['link_category'] : array(); + + // Make sure we set a valid category + if ( ! is_array( $link_category ) || 0 == count( $link_category ) ) { + $link_category = array( get_option( 'default_link_category' ) ); + } + + if ( $update ) { + if ( false === $wpdb->update( $wpdb->links, compact( 'link_url', 'link_name', 'link_image', 'link_target', 'link_description', 'link_visible', 'link_rating', 'link_rel', 'link_notes', 'link_rss' ), compact( 'link_id' ) ) ) { + if ( $wp_error ) { + return new WP_Error( 'db_update_error', __( 'Could not update link in the database' ), $wpdb->last_error ); + } else { + return 0; + } + } + } else { + if ( false === $wpdb->insert( $wpdb->links, compact( 'link_url', 'link_name', 'link_image', 'link_target', 'link_description', 'link_visible', 'link_owner', 'link_rating', 'link_rel', 'link_notes', 'link_rss' ) ) ) { + if ( $wp_error ) { + return new WP_Error( 'db_insert_error', __( 'Could not insert link into the database' ), $wpdb->last_error ); + } else { + return 0; + } + } + $link_id = (int) $wpdb->insert_id; + } + + wp_set_link_cats( $link_id, $link_category ); + + if ( $update ) { + /** + * Fires after a link was updated in the database. + * + * @since 2.0.0 + * + * @param int $link_id ID of the link that was updated. + */ + do_action( 'edit_link', $link_id ); + } else { + /** + * Fires after a link was added to the database. + * + * @since 2.0.0 + * + * @param int $link_id ID of the link that was added. + */ + do_action( 'add_link', $link_id ); + } + clean_bookmark_cache( $link_id ); + + return $link_id; +} + +/** + * Update link with the specified link categories. + * + * @since 2.1.0 + * + * @param int $link_id ID of link to update + * @param array $link_categories Array of categories to + */ +function wp_set_link_cats( $link_id = 0, $link_categories = array() ) { + // If $link_categories isn't already an array, make it one: + if ( !is_array( $link_categories ) || 0 == count( $link_categories ) ) + $link_categories = array( get_option( 'default_link_category' ) ); + + $link_categories = array_map( 'intval', $link_categories ); + $link_categories = array_unique( $link_categories ); + + wp_set_object_terms( $link_id, $link_categories, 'link_category' ); + + clean_bookmark_cache( $link_id ); +} + +/** + * Update a link in the database. + * + * @since 2.0.0 + * + * @param array $linkdata Link data to update. + * @return int|WP_Error Value 0 or WP_Error on failure. The updated link ID on success. + */ +function wp_update_link( $linkdata ) { + $link_id = (int) $linkdata['link_id']; + + $link = get_bookmark( $link_id, ARRAY_A ); + + // Escape data pulled from DB. + $link = wp_slash( $link ); + + // Passed link category list overwrites existing category list if not empty. + if ( isset( $linkdata['link_category'] ) && is_array( $linkdata['link_category'] ) + && 0 != count( $linkdata['link_category'] ) ) + $link_cats = $linkdata['link_category']; + else + $link_cats = $link['link_category']; + + // Merge old and new fields with new fields overwriting old ones. + $linkdata = array_merge( $link, $linkdata ); + $linkdata['link_category'] = $link_cats; + + return wp_insert_link( $linkdata ); +} + +/** + * @since 3.5.0 + * @access private + */ +function wp_link_manager_disabled_message() { + global $pagenow; + if ( 'link-manager.php' != $pagenow && 'link-add.php' != $pagenow && 'link.php' != $pagenow ) + return; + + add_filter( 'pre_option_link_manager_enabled', '__return_true', 100 ); + $really_can_manage_links = current_user_can( 'manage_links' ); + remove_filter( 'pre_option_link_manager_enabled', '__return_true', 100 ); + + if ( $really_can_manage_links && current_user_can( 'install_plugins' ) ) { + $link = network_admin_url( 'plugin-install.php?tab=search&s=Link+Manager' ); + wp_die( sprintf( __( 'If you are looking to use the link manager, please install the Link Manager plugin.' ), $link ) ); + } + + wp_die( __( 'You do not have sufficient permissions to edit the links for this site.' ) ); +} +add_action( 'admin_page_access_denied', 'wp_link_manager_disabled_message' ); diff --git a/wp-admin/includes/class-ftp-pure.php b/wp-admin/includes/class-ftp-pure.php new file mode 100644 index 0000000..bb7742a --- /dev/null +++ b/wp-admin/includes/class-ftp-pure.php @@ -0,0 +1,190 @@ +__construct($verb, $le); + } + + function __construct($verb=FALSE, $le=FALSE) { + parent::__construct(false, $verb, $le); + } + +// +// +// + + function _settimeout($sock) { + if(!@stream_set_timeout($sock, $this->_timeout)) { + $this->PushError('_settimeout','socket set send timeout'); + $this->_quit(); + return FALSE; + } + return TRUE; + } + + function _connect($host, $port) { + $this->SendMSG("Creating socket"); + $sock = @fsockopen($host, $port, $errno, $errstr, $this->_timeout); + if (!$sock) { + $this->PushError('_connect','socket connect failed', $errstr." (".$errno.")"); + return FALSE; + } + $this->_connected=true; + return $sock; + } + + function _readmsg($fnction="_readmsg"){ + if(!$this->_connected) { + $this->PushError($fnction, 'Connect first'); + return FALSE; + } + $result=true; + $this->_message=""; + $this->_code=0; + $go=true; + do { + $tmp=@fgets($this->_ftp_control_sock, 512); + if($tmp===false) { + $go=$result=false; + $this->PushError($fnction,'Read failed'); + } else { + $this->_message.=$tmp; + if(preg_match("/^([0-9]{3})(-(.*[".CRLF."]{1,2})+\\1)? [^".CRLF."]+[".CRLF."]{1,2}$/", $this->_message, $regs)) $go=false; + } + } while($go); + if($this->LocalEcho) echo "GET < ".rtrim($this->_message, CRLF).CRLF; + $this->_code=(int)$regs[1]; + return $result; + } + + function _exec($cmd, $fnction="_exec") { + if(!$this->_ready) { + $this->PushError($fnction,'Connect first'); + return FALSE; + } + if($this->LocalEcho) echo "PUT > ",$cmd,CRLF; + $status=@fputs($this->_ftp_control_sock, $cmd.CRLF); + if($status===false) { + $this->PushError($fnction,'socket write failed'); + return FALSE; + } + $this->_lastaction=time(); + if(!$this->_readmsg($fnction)) return FALSE; + return TRUE; + } + + function _data_prepare($mode=FTP_ASCII) { + if(!$this->_settype($mode)) return FALSE; + if($this->_passive) { + if(!$this->_exec("PASV", "pasv")) { + $this->_data_close(); + return FALSE; + } + if(!$this->_checkCode()) { + $this->_data_close(); + return FALSE; + } + $ip_port = explode(",", ereg_replace("^.+ \\(?([0-9]{1,3},[0-9]{1,3},[0-9]{1,3},[0-9]{1,3},[0-9]+,[0-9]+)\\)?.*".CRLF."$", "\\1", $this->_message)); + $this->_datahost=$ip_port[0].".".$ip_port[1].".".$ip_port[2].".".$ip_port[3]; + $this->_dataport=(((int)$ip_port[4])<<8) + ((int)$ip_port[5]); + $this->SendMSG("Connecting to ".$this->_datahost.":".$this->_dataport); + $this->_ftp_data_sock=@fsockopen($this->_datahost, $this->_dataport, $errno, $errstr, $this->_timeout); + if(!$this->_ftp_data_sock) { + $this->PushError("_data_prepare","fsockopen fails", $errstr." (".$errno.")"); + $this->_data_close(); + return FALSE; + } + else $this->_ftp_data_sock; + } else { + $this->SendMSG("Only passive connections available!"); + return FALSE; + } + return TRUE; + } + + function _data_read($mode=FTP_ASCII, $fp=NULL) { + if(is_resource($fp)) $out=0; + else $out=""; + if(!$this->_passive) { + $this->SendMSG("Only passive connections available!"); + return FALSE; + } + while (!feof($this->_ftp_data_sock)) { + $block=fread($this->_ftp_data_sock, $this->_ftp_buff_size); + if($mode!=FTP_BINARY) $block=preg_replace("/\r\n|\r|\n/", $this->_eol_code[$this->OS_local], $block); + if(is_resource($fp)) $out+=fwrite($fp, $block, strlen($block)); + else $out.=$block; + } + return $out; + } + + function _data_write($mode=FTP_ASCII, $fp=NULL) { + if(is_resource($fp)) $out=0; + else $out=""; + if(!$this->_passive) { + $this->SendMSG("Only passive connections available!"); + return FALSE; + } + if(is_resource($fp)) { + while(!feof($fp)) { + $block=fread($fp, $this->_ftp_buff_size); + if(!$this->_data_write_block($mode, $block)) return false; + } + } elseif(!$this->_data_write_block($mode, $fp)) return false; + return TRUE; + } + + function _data_write_block($mode, $block) { + if($mode!=FTP_BINARY) $block=preg_replace("/\r\n|\r|\n/", $this->_eol_code[$this->OS_remote], $block); + do { + if(($t=@fwrite($this->_ftp_data_sock, $block))===FALSE) { + $this->PushError("_data_write","Can't write to socket"); + return FALSE; + } + $block=substr($block, $t); + } while(!empty($block)); + return true; + } + + function _data_close() { + @fclose($this->_ftp_data_sock); + $this->SendMSG("Disconnected data from remote host"); + return TRUE; + } + + function _quit($force=FALSE) { + if($this->_connected or $force) { + @fclose($this->_ftp_control_sock); + $this->_connected=false; + $this->SendMSG("Socket closed"); + } + } +} + +?> diff --git a/wp-admin/includes/class-ftp-sockets.php b/wp-admin/includes/class-ftp-sockets.php new file mode 100644 index 0000000..f9ea6cd --- /dev/null +++ b/wp-admin/includes/class-ftp-sockets.php @@ -0,0 +1,250 @@ +__construct($verb, $le); + } + + function __construct($verb=FALSE, $le=FALSE) { + parent::__construct(true, $verb, $le); + } + +// +// +// + + function _settimeout($sock) { + if(!@socket_set_option($sock, SOL_SOCKET, SO_RCVTIMEO, array("sec"=>$this->_timeout, "usec"=>0))) { + $this->PushError('_connect','socket set receive timeout',socket_strerror(socket_last_error($sock))); + @socket_close($sock); + return FALSE; + } + if(!@socket_set_option($sock, SOL_SOCKET , SO_SNDTIMEO, array("sec"=>$this->_timeout, "usec"=>0))) { + $this->PushError('_connect','socket set send timeout',socket_strerror(socket_last_error($sock))); + @socket_close($sock); + return FALSE; + } + return true; + } + + function _connect($host, $port) { + $this->SendMSG("Creating socket"); + if(!($sock = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP))) { + $this->PushError('_connect','socket create failed',socket_strerror(socket_last_error($sock))); + return FALSE; + } + if(!$this->_settimeout($sock)) return FALSE; + $this->SendMSG("Connecting to \"".$host.":".$port."\""); + if (!($res = @socket_connect($sock, $host, $port))) { + $this->PushError('_connect','socket connect failed',socket_strerror(socket_last_error($sock))); + @socket_close($sock); + return FALSE; + } + $this->_connected=true; + return $sock; + } + + function _readmsg($fnction="_readmsg"){ + if(!$this->_connected) { + $this->PushError($fnction,'Connect first'); + return FALSE; + } + $result=true; + $this->_message=""; + $this->_code=0; + $go=true; + do { + $tmp=@socket_read($this->_ftp_control_sock, 4096, PHP_BINARY_READ); + if($tmp===false) { + $go=$result=false; + $this->PushError($fnction,'Read failed', socket_strerror(socket_last_error($this->_ftp_control_sock))); + } else { + $this->_message.=$tmp; + $go = !preg_match("/^([0-9]{3})(-.+\\1)? [^".CRLF."]+".CRLF."$/Us", $this->_message, $regs); + } + } while($go); + if($this->LocalEcho) echo "GET < ".rtrim($this->_message, CRLF).CRLF; + $this->_code=(int)$regs[1]; + return $result; + } + + function _exec($cmd, $fnction="_exec") { + if(!$this->_ready) { + $this->PushError($fnction,'Connect first'); + return FALSE; + } + if($this->LocalEcho) echo "PUT > ",$cmd,CRLF; + $status=@socket_write($this->_ftp_control_sock, $cmd.CRLF); + if($status===false) { + $this->PushError($fnction,'socket write failed', socket_strerror(socket_last_error($this->stream))); + return FALSE; + } + $this->_lastaction=time(); + if(!$this->_readmsg($fnction)) return FALSE; + return TRUE; + } + + function _data_prepare($mode=FTP_ASCII) { + if(!$this->_settype($mode)) return FALSE; + $this->SendMSG("Creating data socket"); + $this->_ftp_data_sock = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP); + if ($this->_ftp_data_sock < 0) { + $this->PushError('_data_prepare','socket create failed',socket_strerror(socket_last_error($this->_ftp_data_sock))); + return FALSE; + } + if(!$this->_settimeout($this->_ftp_data_sock)) { + $this->_data_close(); + return FALSE; + } + if($this->_passive) { + if(!$this->_exec("PASV", "pasv")) { + $this->_data_close(); + return FALSE; + } + if(!$this->_checkCode()) { + $this->_data_close(); + return FALSE; + } + $ip_port = explode(",", ereg_replace("^.+ \\(?([0-9]{1,3},[0-9]{1,3},[0-9]{1,3},[0-9]{1,3},[0-9]+,[0-9]+)\\)?.*".CRLF."$", "\\1", $this->_message)); + $this->_datahost=$ip_port[0].".".$ip_port[1].".".$ip_port[2].".".$ip_port[3]; + $this->_dataport=(((int)$ip_port[4])<<8) + ((int)$ip_port[5]); + $this->SendMSG("Connecting to ".$this->_datahost.":".$this->_dataport); + if(!@socket_connect($this->_ftp_data_sock, $this->_datahost, $this->_dataport)) { + $this->PushError("_data_prepare","socket_connect", socket_strerror(socket_last_error($this->_ftp_data_sock))); + $this->_data_close(); + return FALSE; + } + else $this->_ftp_temp_sock=$this->_ftp_data_sock; + } else { + if(!@socket_getsockname($this->_ftp_control_sock, $addr, $port)) { + $this->PushError("_data_prepare","can't get control socket information", socket_strerror(socket_last_error($this->_ftp_control_sock))); + $this->_data_close(); + return FALSE; + } + if(!@socket_bind($this->_ftp_data_sock,$addr)){ + $this->PushError("_data_prepare","can't bind data socket", socket_strerror(socket_last_error($this->_ftp_data_sock))); + $this->_data_close(); + return FALSE; + } + if(!@socket_listen($this->_ftp_data_sock)) { + $this->PushError("_data_prepare","can't listen data socket", socket_strerror(socket_last_error($this->_ftp_data_sock))); + $this->_data_close(); + return FALSE; + } + if(!@socket_getsockname($this->_ftp_data_sock, $this->_datahost, $this->_dataport)) { + $this->PushError("_data_prepare","can't get data socket information", socket_strerror(socket_last_error($this->_ftp_data_sock))); + $this->_data_close(); + return FALSE; + } + if(!$this->_exec('PORT '.str_replace('.',',',$this->_datahost.'.'.($this->_dataport>>8).'.'.($this->_dataport&0x00FF)), "_port")) { + $this->_data_close(); + return FALSE; + } + if(!$this->_checkCode()) { + $this->_data_close(); + return FALSE; + } + } + return TRUE; + } + + function _data_read($mode=FTP_ASCII, $fp=NULL) { + $NewLine=$this->_eol_code[$this->OS_local]; + if(is_resource($fp)) $out=0; + else $out=""; + if(!$this->_passive) { + $this->SendMSG("Connecting to ".$this->_datahost.":".$this->_dataport); + $this->_ftp_temp_sock=socket_accept($this->_ftp_data_sock); + if($this->_ftp_temp_sock===FALSE) { + $this->PushError("_data_read","socket_accept", socket_strerror(socket_last_error($this->_ftp_temp_sock))); + $this->_data_close(); + return FALSE; + } + } + + while(($block=@socket_read($this->_ftp_temp_sock, $this->_ftp_buff_size, PHP_BINARY_READ))!==false) { + if($block==="") break; + if($mode!=FTP_BINARY) $block=preg_replace("/\r\n|\r|\n/", $this->_eol_code[$this->OS_local], $block); + if(is_resource($fp)) $out+=fwrite($fp, $block, strlen($block)); + else $out.=$block; + } + return $out; + } + + function _data_write($mode=FTP_ASCII, $fp=NULL) { + $NewLine=$this->_eol_code[$this->OS_local]; + if(is_resource($fp)) $out=0; + else $out=""; + if(!$this->_passive) { + $this->SendMSG("Connecting to ".$this->_datahost.":".$this->_dataport); + $this->_ftp_temp_sock=socket_accept($this->_ftp_data_sock); + if($this->_ftp_temp_sock===FALSE) { + $this->PushError("_data_write","socket_accept", socket_strerror(socket_last_error($this->_ftp_temp_sock))); + $this->_data_close(); + return false; + } + } + if(is_resource($fp)) { + while(!feof($fp)) { + $block=fread($fp, $this->_ftp_buff_size); + if(!$this->_data_write_block($mode, $block)) return false; + } + } elseif(!$this->_data_write_block($mode, $fp)) return false; + return true; + } + + function _data_write_block($mode, $block) { + if($mode!=FTP_BINARY) $block=preg_replace("/\r\n|\r|\n/", $this->_eol_code[$this->OS_remote], $block); + do { + if(($t=@socket_write($this->_ftp_temp_sock, $block))===FALSE) { + $this->PushError("_data_write","socket_write", socket_strerror(socket_last_error($this->_ftp_temp_sock))); + $this->_data_close(); + return FALSE; + } + $block=substr($block, $t); + } while(!empty($block)); + return true; + } + + function _data_close() { + @socket_close($this->_ftp_temp_sock); + @socket_close($this->_ftp_data_sock); + $this->SendMSG("Disconnected data from remote host"); + return TRUE; + } + + function _quit() { + if($this->_connected) { + @socket_close($this->_ftp_control_sock); + $this->_connected=false; + $this->SendMSG("Socket closed"); + } + } +} +?> diff --git a/wp-admin/includes/class-ftp.php b/wp-admin/includes/class-ftp.php new file mode 100644 index 0000000..01e585b --- /dev/null +++ b/wp-admin/includes/class-ftp.php @@ -0,0 +1,906 @@ +__construct($port_mode); + } + + function __construct($port_mode=FALSE, $verb=FALSE, $le=FALSE) { + $this->LocalEcho=$le; + $this->Verbose=$verb; + $this->_lastaction=NULL; + $this->_error_array=array(); + $this->_eol_code=array(FTP_OS_Unix=>"\n", FTP_OS_Mac=>"\r", FTP_OS_Windows=>"\r\n"); + $this->AuthorizedTransferMode=array(FTP_AUTOASCII, FTP_ASCII, FTP_BINARY); + $this->OS_FullName=array(FTP_OS_Unix => 'UNIX', FTP_OS_Windows => 'WINDOWS', FTP_OS_Mac => 'MACOS'); + $this->AutoAsciiExt=array("ASP","BAT","C","CPP","CSS","CSV","JS","H","HTM","HTML","SHTML","INI","LOG","PHP3","PHTML","PL","PERL","SH","SQL","TXT"); + $this->_port_available=($port_mode==TRUE); + $this->SendMSG("Staring FTP client class".($this->_port_available?"":" without PORT mode support")); + $this->_connected=FALSE; + $this->_ready=FALSE; + $this->_can_restore=FALSE; + $this->_code=0; + $this->_message=""; + $this->_ftp_buff_size=4096; + $this->_curtype=NULL; + $this->SetUmask(0022); + $this->SetType(FTP_AUTOASCII); + $this->SetTimeout(30); + $this->Passive(!$this->_port_available); + $this->_login="anonymous"; + $this->_password="anon@ftp.com"; + $this->_features=array(); + $this->OS_local=FTP_OS_Unix; + $this->OS_remote=FTP_OS_Unix; + $this->features=array(); + if(strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') $this->OS_local=FTP_OS_Windows; + elseif(strtoupper(substr(PHP_OS, 0, 3)) === 'MAC') $this->OS_local=FTP_OS_Mac; + } + +// +// +// + + function parselisting($line) { + $is_windows = ($this->OS_remote == FTP_OS_Windows); + if ($is_windows && preg_match("/([0-9]{2})-([0-9]{2})-([0-9]{2}) +([0-9]{2}):([0-9]{2})(AM|PM) +([0-9]+|
    ) +(.+)/",$line,$lucifer)) { + $b = array(); + if ($lucifer[3]<70) { $lucifer[3]+=2000; } else { $lucifer[3]+=1900; } // 4digit year fix + $b['isdir'] = ($lucifer[7]==""); + if ( $b['isdir'] ) + $b['type'] = 'd'; + else + $b['type'] = 'f'; + $b['size'] = $lucifer[7]; + $b['month'] = $lucifer[1]; + $b['day'] = $lucifer[2]; + $b['year'] = $lucifer[3]; + $b['hour'] = $lucifer[4]; + $b['minute'] = $lucifer[5]; + $b['time'] = @mktime($lucifer[4]+(strcasecmp($lucifer[6],"PM")==0?12:0),$lucifer[5],0,$lucifer[1],$lucifer[2],$lucifer[3]); + $b['am/pm'] = $lucifer[6]; + $b['name'] = $lucifer[8]; + } else if (!$is_windows && $lucifer=preg_split("/[ ]/",$line,9,PREG_SPLIT_NO_EMPTY)) { + //echo $line."\n"; + $lcount=count($lucifer); + if ($lcount<8) return ''; + $b = array(); + $b['isdir'] = $lucifer[0]{0} === "d"; + $b['islink'] = $lucifer[0]{0} === "l"; + if ( $b['isdir'] ) + $b['type'] = 'd'; + elseif ( $b['islink'] ) + $b['type'] = 'l'; + else + $b['type'] = 'f'; + $b['perms'] = $lucifer[0]; + $b['number'] = $lucifer[1]; + $b['owner'] = $lucifer[2]; + $b['group'] = $lucifer[3]; + $b['size'] = $lucifer[4]; + if ($lcount==8) { + sscanf($lucifer[5],"%d-%d-%d",$b['year'],$b['month'],$b['day']); + sscanf($lucifer[6],"%d:%d",$b['hour'],$b['minute']); + $b['time'] = @mktime($b['hour'],$b['minute'],0,$b['month'],$b['day'],$b['year']); + $b['name'] = $lucifer[7]; + } else { + $b['month'] = $lucifer[5]; + $b['day'] = $lucifer[6]; + if (preg_match("/([0-9]{2}):([0-9]{2})/",$lucifer[7],$l2)) { + $b['year'] = date("Y"); + $b['hour'] = $l2[1]; + $b['minute'] = $l2[2]; + } else { + $b['year'] = $lucifer[7]; + $b['hour'] = 0; + $b['minute'] = 0; + } + $b['time'] = strtotime(sprintf("%d %s %d %02d:%02d",$b['day'],$b['month'],$b['year'],$b['hour'],$b['minute'])); + $b['name'] = $lucifer[8]; + } + } + + return $b; + } + + function SendMSG($message = "", $crlf=true) { + if ($this->Verbose) { + echo $message.($crlf?CRLF:""); + flush(); + } + return TRUE; + } + + function SetType($mode=FTP_AUTOASCII) { + if(!in_array($mode, $this->AuthorizedTransferMode)) { + $this->SendMSG("Wrong type"); + return FALSE; + } + $this->_type=$mode; + $this->SendMSG("Transfer type: ".($this->_type==FTP_BINARY?"binary":($this->_type==FTP_ASCII?"ASCII":"auto ASCII") ) ); + return TRUE; + } + + function _settype($mode=FTP_ASCII) { + if($this->_ready) { + if($mode==FTP_BINARY) { + if($this->_curtype!=FTP_BINARY) { + if(!$this->_exec("TYPE I", "SetType")) return FALSE; + $this->_curtype=FTP_BINARY; + } + } elseif($this->_curtype!=FTP_ASCII) { + if(!$this->_exec("TYPE A", "SetType")) return FALSE; + $this->_curtype=FTP_ASCII; + } + } else return FALSE; + return TRUE; + } + + function Passive($pasv=NULL) { + if(is_null($pasv)) $this->_passive=!$this->_passive; + else $this->_passive=$pasv; + if(!$this->_port_available and !$this->_passive) { + $this->SendMSG("Only passive connections available!"); + $this->_passive=TRUE; + return FALSE; + } + $this->SendMSG("Passive mode ".($this->_passive?"on":"off")); + return TRUE; + } + + function SetServer($host, $port=21, $reconnect=true) { + if(!is_long($port)) { + $this->verbose=true; + $this->SendMSG("Incorrect port syntax"); + return FALSE; + } else { + $ip=@gethostbyname($host); + $dns=@gethostbyaddr($host); + if(!$ip) $ip=$host; + if(!$dns) $dns=$host; + // Validate the IPAddress PHP4 returns -1 for invalid, PHP5 false + // -1 === "255.255.255.255" which is the broadcast address which is also going to be invalid + $ipaslong = ip2long($ip); + if ( ($ipaslong == false) || ($ipaslong === -1) ) { + $this->SendMSG("Wrong host name/address \"".$host."\""); + return FALSE; + } + $this->_host=$ip; + $this->_fullhost=$dns; + $this->_port=$port; + $this->_dataport=$port-1; + } + $this->SendMSG("Host \"".$this->_fullhost."(".$this->_host."):".$this->_port."\""); + if($reconnect){ + if($this->_connected) { + $this->SendMSG("Reconnecting"); + if(!$this->quit(FTP_FORCE)) return FALSE; + if(!$this->connect()) return FALSE; + } + } + return TRUE; + } + + function SetUmask($umask=0022) { + $this->_umask=$umask; + umask($this->_umask); + $this->SendMSG("UMASK 0".decoct($this->_umask)); + return TRUE; + } + + function SetTimeout($timeout=30) { + $this->_timeout=$timeout; + $this->SendMSG("Timeout ".$this->_timeout); + if($this->_connected) + if(!$this->_settimeout($this->_ftp_control_sock)) return FALSE; + return TRUE; + } + + function connect($server=NULL) { + if(!empty($server)) { + if(!$this->SetServer($server)) return false; + } + if($this->_ready) return true; + $this->SendMsg('Local OS : '.$this->OS_FullName[$this->OS_local]); + if(!($this->_ftp_control_sock = $this->_connect($this->_host, $this->_port))) { + $this->SendMSG("Error : Cannot connect to remote host \"".$this->_fullhost." :".$this->_port."\""); + return FALSE; + } + $this->SendMSG("Connected to remote host \"".$this->_fullhost.":".$this->_port."\". Waiting for greeting."); + do { + if(!$this->_readmsg()) return FALSE; + if(!$this->_checkCode()) return FALSE; + $this->_lastaction=time(); + } while($this->_code<200); + $this->_ready=true; + $syst=$this->systype(); + if(!$syst) $this->SendMSG("Can't detect remote OS"); + else { + if(preg_match("/win|dos|novell/i", $syst[0])) $this->OS_remote=FTP_OS_Windows; + elseif(preg_match("/os/i", $syst[0])) $this->OS_remote=FTP_OS_Mac; + elseif(preg_match("/(li|u)nix/i", $syst[0])) $this->OS_remote=FTP_OS_Unix; + else $this->OS_remote=FTP_OS_Mac; + $this->SendMSG("Remote OS: ".$this->OS_FullName[$this->OS_remote]); + } + if(!$this->features()) $this->SendMSG("Can't get features list. All supported - disabled"); + else $this->SendMSG("Supported features: ".implode(", ", array_keys($this->_features))); + return TRUE; + } + + function quit($force=false) { + if($this->_ready) { + if(!$this->_exec("QUIT") and !$force) return FALSE; + if(!$this->_checkCode() and !$force) return FALSE; + $this->_ready=false; + $this->SendMSG("Session finished"); + } + $this->_quit(); + return TRUE; + } + + function login($user=NULL, $pass=NULL) { + if(!is_null($user)) $this->_login=$user; + else $this->_login="anonymous"; + if(!is_null($pass)) $this->_password=$pass; + else $this->_password="anon@anon.com"; + if(!$this->_exec("USER ".$this->_login, "login")) return FALSE; + if(!$this->_checkCode()) return FALSE; + if($this->_code!=230) { + if(!$this->_exec((($this->_code==331)?"PASS ":"ACCT ").$this->_password, "login")) return FALSE; + if(!$this->_checkCode()) return FALSE; + } + $this->SendMSG("Authentication succeeded"); + if(empty($this->_features)) { + if(!$this->features()) $this->SendMSG("Can't get features list. All supported - disabled"); + else $this->SendMSG("Supported features: ".implode(", ", array_keys($this->_features))); + } + return TRUE; + } + + function pwd() { + if(!$this->_exec("PWD", "pwd")) return FALSE; + if(!$this->_checkCode()) return FALSE; + return ereg_replace("^[0-9]{3} \"(.+)\".+", "\\1", $this->_message); + } + + function cdup() { + if(!$this->_exec("CDUP", "cdup")) return FALSE; + if(!$this->_checkCode()) return FALSE; + return true; + } + + function chdir($pathname) { + if(!$this->_exec("CWD ".$pathname, "chdir")) return FALSE; + if(!$this->_checkCode()) return FALSE; + return TRUE; + } + + function rmdir($pathname) { + if(!$this->_exec("RMD ".$pathname, "rmdir")) return FALSE; + if(!$this->_checkCode()) return FALSE; + return TRUE; + } + + function mkdir($pathname) { + if(!$this->_exec("MKD ".$pathname, "mkdir")) return FALSE; + if(!$this->_checkCode()) return FALSE; + return TRUE; + } + + function rename($from, $to) { + if(!$this->_exec("RNFR ".$from, "rename")) return FALSE; + if(!$this->_checkCode()) return FALSE; + if($this->_code==350) { + if(!$this->_exec("RNTO ".$to, "rename")) return FALSE; + if(!$this->_checkCode()) return FALSE; + } else return FALSE; + return TRUE; + } + + function filesize($pathname) { + if(!isset($this->_features["SIZE"])) { + $this->PushError("filesize", "not supported by server"); + return FALSE; + } + if(!$this->_exec("SIZE ".$pathname, "filesize")) return FALSE; + if(!$this->_checkCode()) return FALSE; + return ereg_replace("^[0-9]{3} ([0-9]+)".CRLF, "\\1", $this->_message); + } + + function abort() { + if(!$this->_exec("ABOR", "abort")) return FALSE; + if(!$this->_checkCode()) { + if($this->_code!=426) return FALSE; + if(!$this->_readmsg("abort")) return FALSE; + if(!$this->_checkCode()) return FALSE; + } + return true; + } + + function mdtm($pathname) { + if(!isset($this->_features["MDTM"])) { + $this->PushError("mdtm", "not supported by server"); + return FALSE; + } + if(!$this->_exec("MDTM ".$pathname, "mdtm")) return FALSE; + if(!$this->_checkCode()) return FALSE; + $mdtm = ereg_replace("^[0-9]{3} ([0-9]+)".CRLF, "\\1", $this->_message); + $date = sscanf($mdtm, "%4d%2d%2d%2d%2d%2d"); + $timestamp = mktime($date[3], $date[4], $date[5], $date[1], $date[2], $date[0]); + return $timestamp; + } + + function systype() { + if(!$this->_exec("SYST", "systype")) return FALSE; + if(!$this->_checkCode()) return FALSE; + $DATA = explode(" ", $this->_message); + return array($DATA[1], $DATA[3]); + } + + function delete($pathname) { + if(!$this->_exec("DELE ".$pathname, "delete")) return FALSE; + if(!$this->_checkCode()) return FALSE; + return TRUE; + } + + function site($command, $fnction="site") { + if(!$this->_exec("SITE ".$command, $fnction)) return FALSE; + if(!$this->_checkCode()) return FALSE; + return TRUE; + } + + function chmod($pathname, $mode) { + if(!$this->site( sprintf('CHMOD %o %s', $mode, $pathname), "chmod")) return FALSE; + return TRUE; + } + + function restore($from) { + if(!isset($this->_features["REST"])) { + $this->PushError("restore", "not supported by server"); + return FALSE; + } + if($this->_curtype!=FTP_BINARY) { + $this->PushError("restore", "can't restore in ASCII mode"); + return FALSE; + } + if(!$this->_exec("REST ".$from, "resore")) return FALSE; + if(!$this->_checkCode()) return FALSE; + return TRUE; + } + + function features() { + if(!$this->_exec("FEAT", "features")) return FALSE; + if(!$this->_checkCode()) return FALSE; + $f=preg_split("/[".CRLF."]+/", preg_replace("/[0-9]{3}[ -].*[".CRLF."]+/", "", $this->_message), -1, PREG_SPLIT_NO_EMPTY); + $this->_features=array(); + foreach($f as $k=>$v) { + $v=explode(" ", trim($v)); + $this->_features[array_shift($v)]=$v; + } + return true; + } + + function rawlist($pathname="", $arg="") { + return $this->_list(($arg?" ".$arg:"").($pathname?" ".$pathname:""), "LIST", "rawlist"); + } + + function nlist($pathname="", $arg="") { + return $this->_list(($arg?" ".$arg:"").($pathname?" ".$pathname:""), "NLST", "nlist"); + } + + function is_exists($pathname) { + return $this->file_exists($pathname); + } + + function file_exists($pathname) { + $exists=true; + if(!$this->_exec("RNFR ".$pathname, "rename")) $exists=FALSE; + else { + if(!$this->_checkCode()) $exists=FALSE; + $this->abort(); + } + if($exists) $this->SendMSG("Remote file ".$pathname." exists"); + else $this->SendMSG("Remote file ".$pathname." does not exist"); + return $exists; + } + + function fget($fp, $remotefile,$rest=0) { + if($this->_can_restore and $rest!=0) fseek($fp, $rest); + $pi=pathinfo($remotefile); + if($this->_type==FTP_ASCII or ($this->_type==FTP_AUTOASCII and in_array(strtoupper($pi["extension"]), $this->AutoAsciiExt))) $mode=FTP_ASCII; + else $mode=FTP_BINARY; + if(!$this->_data_prepare($mode)) { + return FALSE; + } + if($this->_can_restore and $rest!=0) $this->restore($rest); + if(!$this->_exec("RETR ".$remotefile, "get")) { + $this->_data_close(); + return FALSE; + } + if(!$this->_checkCode()) { + $this->_data_close(); + return FALSE; + } + $out=$this->_data_read($mode, $fp); + $this->_data_close(); + if(!$this->_readmsg()) return FALSE; + if(!$this->_checkCode()) return FALSE; + return $out; + } + + function get($remotefile, $localfile=NULL, $rest=0) { + if(is_null($localfile)) $localfile=$remotefile; + if (@file_exists($localfile)) $this->SendMSG("Warning : local file will be overwritten"); + $fp = @fopen($localfile, "w"); + if (!$fp) { + $this->PushError("get","can't open local file", "Cannot create \"".$localfile."\""); + return FALSE; + } + if($this->_can_restore and $rest!=0) fseek($fp, $rest); + $pi=pathinfo($remotefile); + if($this->_type==FTP_ASCII or ($this->_type==FTP_AUTOASCII and in_array(strtoupper($pi["extension"]), $this->AutoAsciiExt))) $mode=FTP_ASCII; + else $mode=FTP_BINARY; + if(!$this->_data_prepare($mode)) { + fclose($fp); + return FALSE; + } + if($this->_can_restore and $rest!=0) $this->restore($rest); + if(!$this->_exec("RETR ".$remotefile, "get")) { + $this->_data_close(); + fclose($fp); + return FALSE; + } + if(!$this->_checkCode()) { + $this->_data_close(); + fclose($fp); + return FALSE; + } + $out=$this->_data_read($mode, $fp); + fclose($fp); + $this->_data_close(); + if(!$this->_readmsg()) return FALSE; + if(!$this->_checkCode()) return FALSE; + return $out; + } + + function fput($remotefile, $fp) { + if($this->_can_restore and $rest!=0) fseek($fp, $rest); + $pi=pathinfo($remotefile); + if($this->_type==FTP_ASCII or ($this->_type==FTP_AUTOASCII and in_array(strtoupper($pi["extension"]), $this->AutoAsciiExt))) $mode=FTP_ASCII; + else $mode=FTP_BINARY; + if(!$this->_data_prepare($mode)) { + return FALSE; + } + if($this->_can_restore and $rest!=0) $this->restore($rest); + if(!$this->_exec("STOR ".$remotefile, "put")) { + $this->_data_close(); + return FALSE; + } + if(!$this->_checkCode()) { + $this->_data_close(); + return FALSE; + } + $ret=$this->_data_write($mode, $fp); + $this->_data_close(); + if(!$this->_readmsg()) return FALSE; + if(!$this->_checkCode()) return FALSE; + return $ret; + } + + function put($localfile, $remotefile=NULL, $rest=0) { + if(is_null($remotefile)) $remotefile=$localfile; + if (!file_exists($localfile)) { + $this->PushError("put","can't open local file", "No such file or directory \"".$localfile."\""); + return FALSE; + } + $fp = @fopen($localfile, "r"); + + if (!$fp) { + $this->PushError("put","can't open local file", "Cannot read file \"".$localfile."\""); + return FALSE; + } + if($this->_can_restore and $rest!=0) fseek($fp, $rest); + $pi=pathinfo($localfile); + if($this->_type==FTP_ASCII or ($this->_type==FTP_AUTOASCII and in_array(strtoupper($pi["extension"]), $this->AutoAsciiExt))) $mode=FTP_ASCII; + else $mode=FTP_BINARY; + if(!$this->_data_prepare($mode)) { + fclose($fp); + return FALSE; + } + if($this->_can_restore and $rest!=0) $this->restore($rest); + if(!$this->_exec("STOR ".$remotefile, "put")) { + $this->_data_close(); + fclose($fp); + return FALSE; + } + if(!$this->_checkCode()) { + $this->_data_close(); + fclose($fp); + return FALSE; + } + $ret=$this->_data_write($mode, $fp); + fclose($fp); + $this->_data_close(); + if(!$this->_readmsg()) return FALSE; + if(!$this->_checkCode()) return FALSE; + return $ret; + } + + function mput($local=".", $remote=NULL, $continious=false) { + $local=realpath($local); + if(!@file_exists($local)) { + $this->PushError("mput","can't open local folder", "Cannot stat folder \"".$local."\""); + return FALSE; + } + if(!is_dir($local)) return $this->put($local, $remote); + if(empty($remote)) $remote="."; + elseif(!$this->file_exists($remote) and !$this->mkdir($remote)) return FALSE; + if($handle = opendir($local)) { + $list=array(); + while (false !== ($file = readdir($handle))) { + if ($file != "." && $file != "..") $list[]=$file; + } + closedir($handle); + } else { + $this->PushError("mput","can't open local folder", "Cannot read folder \"".$local."\""); + return FALSE; + } + if(empty($list)) return TRUE; + $ret=true; + foreach($list as $el) { + if(is_dir($local."/".$el)) $t=$this->mput($local."/".$el, $remote."/".$el); + else $t=$this->put($local."/".$el, $remote."/".$el); + if(!$t) { + $ret=FALSE; + if(!$continious) break; + } + } + return $ret; + + } + + function mget($remote, $local=".", $continious=false) { + $list=$this->rawlist($remote, "-lA"); + if($list===false) { + $this->PushError("mget","can't read remote folder list", "Can't read remote folder \"".$remote."\" contents"); + return FALSE; + } + if(empty($list)) return true; + if(!@file_exists($local)) { + if(!@mkdir($local)) { + $this->PushError("mget","can't create local folder", "Cannot create folder \"".$local."\""); + return FALSE; + } + } + foreach($list as $k=>$v) { + $list[$k]=$this->parselisting($v); + if($list[$k]["name"]=="." or $list[$k]["name"]=="..") unset($list[$k]); + } + $ret=true; + foreach($list as $el) { + if($el["type"]=="d") { + if(!$this->mget($remote."/".$el["name"], $local."/".$el["name"], $continious)) { + $this->PushError("mget", "can't copy folder", "Can't copy remote folder \"".$remote."/".$el["name"]."\" to local \"".$local."/".$el["name"]."\""); + $ret=false; + if(!$continious) break; + } + } else { + if(!$this->get($remote."/".$el["name"], $local."/".$el["name"])) { + $this->PushError("mget", "can't copy file", "Can't copy remote file \"".$remote."/".$el["name"]."\" to local \"".$local."/".$el["name"]."\""); + $ret=false; + if(!$continious) break; + } + } + @chmod($local."/".$el["name"], $el["perms"]); + $t=strtotime($el["date"]); + if($t!==-1 and $t!==false) @touch($local."/".$el["name"], $t); + } + return $ret; + } + + function mdel($remote, $continious=false) { + $list=$this->rawlist($remote, "-la"); + if($list===false) { + $this->PushError("mdel","can't read remote folder list", "Can't read remote folder \"".$remote."\" contents"); + return false; + } + + foreach($list as $k=>$v) { + $list[$k]=$this->parselisting($v); + if($list[$k]["name"]=="." or $list[$k]["name"]=="..") unset($list[$k]); + } + $ret=true; + + foreach($list as $el) { + if ( empty($el) ) + continue; + + if($el["type"]=="d") { + if(!$this->mdel($remote."/".$el["name"], $continious)) { + $ret=false; + if(!$continious) break; + } + } else { + if (!$this->delete($remote."/".$el["name"])) { + $this->PushError("mdel", "can't delete file", "Can't delete remote file \"".$remote."/".$el["name"]."\""); + $ret=false; + if(!$continious) break; + } + } + } + + if(!$this->rmdir($remote)) { + $this->PushError("mdel", "can't delete folder", "Can't delete remote folder \"".$remote."/".$el["name"]."\""); + $ret=false; + } + return $ret; + } + + function mmkdir($dir, $mode = 0777) { + if(empty($dir)) return FALSE; + if($this->is_exists($dir) or $dir == "/" ) return TRUE; + if(!$this->mmkdir(dirname($dir), $mode)) return false; + $r=$this->mkdir($dir, $mode); + $this->chmod($dir,$mode); + return $r; + } + + function glob($pattern, $handle=NULL) { + $path=$output=null; + if(PHP_OS=='WIN32') $slash='\\'; + else $slash='/'; + $lastpos=strrpos($pattern,$slash); + if(!($lastpos===false)) { + $path=substr($pattern,0,-$lastpos-1); + $pattern=substr($pattern,$lastpos); + } else $path=getcwd(); + if(is_array($handle) and !empty($handle)) { + while($dir=each($handle)) { + if($this->glob_pattern_match($pattern,$dir)) + $output[]=$dir; + } + } else { + $handle=@opendir($path); + if($handle===false) return false; + while($dir=readdir($handle)) { + if($this->glob_pattern_match($pattern,$dir)) + $output[]=$dir; + } + closedir($handle); + } + if(is_array($output)) return $output; + return false; + } + + function glob_pattern_match($pattern,$string) { + $out=null; + $chunks=explode(';',$pattern); + foreach($chunks as $pattern) { + $escape=array('$','^','.','{','}','(',')','[',']','|'); + while(strpos($pattern,'**')!==false) + $pattern=str_replace('**','*',$pattern); + foreach($escape as $probe) + $pattern=str_replace($probe,"\\$probe",$pattern); + $pattern=str_replace('?*','*', + str_replace('*?','*', + str_replace('*',".*", + str_replace('?','.{1,1}',$pattern)))); + $out[]=$pattern; + } + if(count($out)==1) return($this->glob_regexp("^$out[0]$",$string)); + else { + foreach($out as $tester) + if($this->my_regexp("^$tester$",$string)) return true; + } + return false; + } + + function glob_regexp($pattern,$probe) { + $sensitive=(PHP_OS!='WIN32'); + return ($sensitive? + ereg($pattern,$probe): + eregi($pattern,$probe) + ); + } + + function dirlist($remote) { + $list=$this->rawlist($remote, "-la"); + if($list===false) { + $this->PushError("dirlist","can't read remote folder list", "Can't read remote folder \"".$remote."\" contents"); + return false; + } + + $dirlist = array(); + foreach($list as $k=>$v) { + $entry=$this->parselisting($v); + if ( empty($entry) ) + continue; + + if($entry["name"]=="." or $entry["name"]=="..") + continue; + + $dirlist[$entry['name']] = $entry; + } + + return $dirlist; + } +// +// +// + function _checkCode() { + return ($this->_code<400 and $this->_code>0); + } + + function _list($arg="", $cmd="LIST", $fnction="_list") { + if(!$this->_data_prepare()) return false; + if(!$this->_exec($cmd.$arg, $fnction)) { + $this->_data_close(); + return FALSE; + } + if(!$this->_checkCode()) { + $this->_data_close(); + return FALSE; + } + $out=""; + if($this->_code<200) { + $out=$this->_data_read(); + $this->_data_close(); + if(!$this->_readmsg()) return FALSE; + if(!$this->_checkCode()) return FALSE; + if($out === FALSE ) return FALSE; + $out=preg_split("/[".CRLF."]+/", $out, -1, PREG_SPLIT_NO_EMPTY); +// $this->SendMSG(implode($this->_eol_code[$this->OS_local], $out)); + } + return $out; + } + +// +// +// +// Gnre une erreur pour traitement externe la classe + function PushError($fctname,$msg,$desc=false){ + $error=array(); + $error['time']=time(); + $error['fctname']=$fctname; + $error['msg']=$msg; + $error['desc']=$desc; + if($desc) $tmp=' ('.$desc.')'; else $tmp=''; + $this->SendMSG($fctname.': '.$msg.$tmp); + return(array_push($this->_error_array,$error)); + } + +// Rcupre une erreur externe + function PopError(){ + if(count($this->_error_array)) return(array_pop($this->_error_array)); + else return(false); + } +} + +$mod_sockets = extension_loaded( 'sockets' ); +if ( ! $mod_sockets && function_exists( 'dl' ) && is_callable( 'dl' ) ) { + $prefix = ( PHP_SHLIB_SUFFIX == 'dll' ) ? 'php_' : ''; + @dl( $prefix . 'sockets.' . PHP_SHLIB_SUFFIX ); + $mod_sockets = extension_loaded( 'sockets' ); +} + +require_once dirname( __FILE__ ) . "/class-ftp-" . ( $mod_sockets ? "sockets" : "pure" ) . ".php"; diff --git a/wp-admin/includes/class-pclzip.php b/wp-admin/includes/class-pclzip.php new file mode 100644 index 0000000..5e6a619 --- /dev/null +++ b/wp-admin/includes/class-pclzip.php @@ -0,0 +1,5687 @@ +zipname = $p_zipname; + $this->zip_fd = 0; + $this->magic_quotes_status = -1; + + // ----- Return + return; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : + // create($p_filelist, $p_add_dir="", $p_remove_dir="") + // create($p_filelist, $p_option, $p_option_value, ...) + // Description : + // This method supports two different synopsis. The first one is historical. + // This method creates a Zip Archive. The Zip file is created in the + // filesystem. The files and directories indicated in $p_filelist + // are added in the archive. See the parameters description for the + // supported format of $p_filelist. + // When a directory is in the list, the directory and its content is added + // in the archive. + // In this synopsis, the function takes an optional variable list of + // options. See bellow the supported options. + // Parameters : + // $p_filelist : An array containing file or directory names, or + // a string containing one filename or one directory name, or + // a string containing a list of filenames and/or directory + // names separated by spaces. + // $p_add_dir : A path to add before the real path of the archived file, + // in order to have it memorized in the archive. + // $p_remove_dir : A path to remove from the real path of the file to archive, + // in order to have a shorter path memorized in the archive. + // When $p_add_dir and $p_remove_dir are set, $p_remove_dir + // is removed first, before $p_add_dir is added. + // Options : + // PCLZIP_OPT_ADD_PATH : + // PCLZIP_OPT_REMOVE_PATH : + // PCLZIP_OPT_REMOVE_ALL_PATH : + // PCLZIP_OPT_COMMENT : + // PCLZIP_CB_PRE_ADD : + // PCLZIP_CB_POST_ADD : + // Return Values : + // 0 on failure, + // The list of the added files, with a status of the add action. + // (see PclZip::listContent() for list entry format) + // -------------------------------------------------------------------------------- + function create($p_filelist) + { + $v_result=1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Set default values + $v_options = array(); + $v_options[PCLZIP_OPT_NO_COMPRESSION] = FALSE; + + // ----- Look for variable options arguments + $v_size = func_num_args(); + + // ----- Look for arguments + if ($v_size > 1) { + // ----- Get the arguments + $v_arg_list = func_get_args(); + + // ----- Remove from the options list the first argument + array_shift($v_arg_list); + $v_size--; + + // ----- Look for first arg + if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { + + // ----- Parse the options + $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, + array (PCLZIP_OPT_REMOVE_PATH => 'optional', + PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', + PCLZIP_OPT_ADD_PATH => 'optional', + PCLZIP_CB_PRE_ADD => 'optional', + PCLZIP_CB_POST_ADD => 'optional', + PCLZIP_OPT_NO_COMPRESSION => 'optional', + PCLZIP_OPT_COMMENT => 'optional', + PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', + PCLZIP_OPT_TEMP_FILE_ON => 'optional', + PCLZIP_OPT_TEMP_FILE_OFF => 'optional' + //, PCLZIP_OPT_CRYPT => 'optional' + )); + if ($v_result != 1) { + return 0; + } + } + + // ----- Look for 2 args + // Here we need to support the first historic synopsis of the + // method. + else { + + // ----- Get the first argument + $v_options[PCLZIP_OPT_ADD_PATH] = $v_arg_list[0]; + + // ----- Look for the optional second argument + if ($v_size == 2) { + $v_options[PCLZIP_OPT_REMOVE_PATH] = $v_arg_list[1]; + } + else if ($v_size > 2) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, + "Invalid number / type of arguments"); + return 0; + } + } + } + + // ----- Look for default option values + $this->privOptionDefaultThreshold($v_options); + + // ----- Init + $v_string_list = array(); + $v_att_list = array(); + $v_filedescr_list = array(); + $p_result_list = array(); + + // ----- Look if the $p_filelist is really an array + if (is_array($p_filelist)) { + + // ----- Look if the first element is also an array + // This will mean that this is a file description entry + if (isset($p_filelist[0]) && is_array($p_filelist[0])) { + $v_att_list = $p_filelist; + } + + // ----- The list is a list of string names + else { + $v_string_list = $p_filelist; + } + } + + // ----- Look if the $p_filelist is a string + else if (is_string($p_filelist)) { + // ----- Create a list from the string + $v_string_list = explode(PCLZIP_SEPARATOR, $p_filelist); + } + + // ----- Invalid variable type for $p_filelist + else { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_filelist"); + return 0; + } + + // ----- Reformat the string list + if (sizeof($v_string_list) != 0) { + foreach ($v_string_list as $v_string) { + if ($v_string != '') { + $v_att_list[][PCLZIP_ATT_FILE_NAME] = $v_string; + } + else { + } + } + } + + // ----- For each file in the list check the attributes + $v_supported_attributes + = array ( PCLZIP_ATT_FILE_NAME => 'mandatory' + ,PCLZIP_ATT_FILE_NEW_SHORT_NAME => 'optional' + ,PCLZIP_ATT_FILE_NEW_FULL_NAME => 'optional' + ,PCLZIP_ATT_FILE_MTIME => 'optional' + ,PCLZIP_ATT_FILE_CONTENT => 'optional' + ,PCLZIP_ATT_FILE_COMMENT => 'optional' + ); + foreach ($v_att_list as $v_entry) { + $v_result = $this->privFileDescrParseAtt($v_entry, + $v_filedescr_list[], + $v_options, + $v_supported_attributes); + if ($v_result != 1) { + return 0; + } + } + + // ----- Expand the filelist (expand directories) + $v_result = $this->privFileDescrExpand($v_filedescr_list, $v_options); + if ($v_result != 1) { + return 0; + } + + // ----- Call the create fct + $v_result = $this->privCreate($v_filedescr_list, $p_result_list, $v_options); + if ($v_result != 1) { + return 0; + } + + // ----- Return + return $p_result_list; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : + // add($p_filelist, $p_add_dir="", $p_remove_dir="") + // add($p_filelist, $p_option, $p_option_value, ...) + // Description : + // This method supports two synopsis. The first one is historical. + // This methods add the list of files in an existing archive. + // If a file with the same name already exists, it is added at the end of the + // archive, the first one is still present. + // If the archive does not exist, it is created. + // Parameters : + // $p_filelist : An array containing file or directory names, or + // a string containing one filename or one directory name, or + // a string containing a list of filenames and/or directory + // names separated by spaces. + // $p_add_dir : A path to add before the real path of the archived file, + // in order to have it memorized in the archive. + // $p_remove_dir : A path to remove from the real path of the file to archive, + // in order to have a shorter path memorized in the archive. + // When $p_add_dir and $p_remove_dir are set, $p_remove_dir + // is removed first, before $p_add_dir is added. + // Options : + // PCLZIP_OPT_ADD_PATH : + // PCLZIP_OPT_REMOVE_PATH : + // PCLZIP_OPT_REMOVE_ALL_PATH : + // PCLZIP_OPT_COMMENT : + // PCLZIP_OPT_ADD_COMMENT : + // PCLZIP_OPT_PREPEND_COMMENT : + // PCLZIP_CB_PRE_ADD : + // PCLZIP_CB_POST_ADD : + // Return Values : + // 0 on failure, + // The list of the added files, with a status of the add action. + // (see PclZip::listContent() for list entry format) + // -------------------------------------------------------------------------------- + function add($p_filelist) + { + $v_result=1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Set default values + $v_options = array(); + $v_options[PCLZIP_OPT_NO_COMPRESSION] = FALSE; + + // ----- Look for variable options arguments + $v_size = func_num_args(); + + // ----- Look for arguments + if ($v_size > 1) { + // ----- Get the arguments + $v_arg_list = func_get_args(); + + // ----- Remove form the options list the first argument + array_shift($v_arg_list); + $v_size--; + + // ----- Look for first arg + if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { + + // ----- Parse the options + $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, + array (PCLZIP_OPT_REMOVE_PATH => 'optional', + PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', + PCLZIP_OPT_ADD_PATH => 'optional', + PCLZIP_CB_PRE_ADD => 'optional', + PCLZIP_CB_POST_ADD => 'optional', + PCLZIP_OPT_NO_COMPRESSION => 'optional', + PCLZIP_OPT_COMMENT => 'optional', + PCLZIP_OPT_ADD_COMMENT => 'optional', + PCLZIP_OPT_PREPEND_COMMENT => 'optional', + PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', + PCLZIP_OPT_TEMP_FILE_ON => 'optional', + PCLZIP_OPT_TEMP_FILE_OFF => 'optional' + //, PCLZIP_OPT_CRYPT => 'optional' + )); + if ($v_result != 1) { + return 0; + } + } + + // ----- Look for 2 args + // Here we need to support the first historic synopsis of the + // method. + else { + + // ----- Get the first argument + $v_options[PCLZIP_OPT_ADD_PATH] = $v_add_path = $v_arg_list[0]; + + // ----- Look for the optional second argument + if ($v_size == 2) { + $v_options[PCLZIP_OPT_REMOVE_PATH] = $v_arg_list[1]; + } + else if ($v_size > 2) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments"); + + // ----- Return + return 0; + } + } + } + + // ----- Look for default option values + $this->privOptionDefaultThreshold($v_options); + + // ----- Init + $v_string_list = array(); + $v_att_list = array(); + $v_filedescr_list = array(); + $p_result_list = array(); + + // ----- Look if the $p_filelist is really an array + if (is_array($p_filelist)) { + + // ----- Look if the first element is also an array + // This will mean that this is a file description entry + if (isset($p_filelist[0]) && is_array($p_filelist[0])) { + $v_att_list = $p_filelist; + } + + // ----- The list is a list of string names + else { + $v_string_list = $p_filelist; + } + } + + // ----- Look if the $p_filelist is a string + else if (is_string($p_filelist)) { + // ----- Create a list from the string + $v_string_list = explode(PCLZIP_SEPARATOR, $p_filelist); + } + + // ----- Invalid variable type for $p_filelist + else { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type '".gettype($p_filelist)."' for p_filelist"); + return 0; + } + + // ----- Reformat the string list + if (sizeof($v_string_list) != 0) { + foreach ($v_string_list as $v_string) { + $v_att_list[][PCLZIP_ATT_FILE_NAME] = $v_string; + } + } + + // ----- For each file in the list check the attributes + $v_supported_attributes + = array ( PCLZIP_ATT_FILE_NAME => 'mandatory' + ,PCLZIP_ATT_FILE_NEW_SHORT_NAME => 'optional' + ,PCLZIP_ATT_FILE_NEW_FULL_NAME => 'optional' + ,PCLZIP_ATT_FILE_MTIME => 'optional' + ,PCLZIP_ATT_FILE_CONTENT => 'optional' + ,PCLZIP_ATT_FILE_COMMENT => 'optional' + ); + foreach ($v_att_list as $v_entry) { + $v_result = $this->privFileDescrParseAtt($v_entry, + $v_filedescr_list[], + $v_options, + $v_supported_attributes); + if ($v_result != 1) { + return 0; + } + } + + // ----- Expand the filelist (expand directories) + $v_result = $this->privFileDescrExpand($v_filedescr_list, $v_options); + if ($v_result != 1) { + return 0; + } + + // ----- Call the create fct + $v_result = $this->privAdd($v_filedescr_list, $p_result_list, $v_options); + if ($v_result != 1) { + return 0; + } + + // ----- Return + return $p_result_list; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : listContent() + // Description : + // This public method, gives the list of the files and directories, with their + // properties. + // The properties of each entries in the list are (used also in other functions) : + // filename : Name of the file. For a create or add action it is the filename + // given by the user. For an extract function it is the filename + // of the extracted file. + // stored_filename : Name of the file / directory stored in the archive. + // size : Size of the stored file. + // compressed_size : Size of the file's data compressed in the archive + // (without the headers overhead) + // mtime : Last known modification date of the file (UNIX timestamp) + // comment : Comment associated with the file + // folder : true | false + // index : index of the file in the archive + // status : status of the action (depending of the action) : + // Values are : + // ok : OK ! + // filtered : the file / dir is not extracted (filtered by user) + // already_a_directory : the file can not be extracted because a + // directory with the same name already exists + // write_protected : the file can not be extracted because a file + // with the same name already exists and is + // write protected + // newer_exist : the file was not extracted because a newer file exists + // path_creation_fail : the file is not extracted because the folder + // does not exist and can not be created + // write_error : the file was not extracted because there was a + // error while writing the file + // read_error : the file was not extracted because there was a error + // while reading the file + // invalid_header : the file was not extracted because of an archive + // format error (bad file header) + // Note that each time a method can continue operating when there + // is an action error on a file, the error is only logged in the file status. + // Return Values : + // 0 on an unrecoverable failure, + // The list of the files in the archive. + // -------------------------------------------------------------------------------- + function listContent() + { + $v_result=1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Check archive + if (!$this->privCheckFormat()) { + return(0); + } + + // ----- Call the extracting fct + $p_list = array(); + if (($v_result = $this->privList($p_list)) != 1) + { + unset($p_list); + return(0); + } + + // ----- Return + return $p_list; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : + // extract($p_path="./", $p_remove_path="") + // extract([$p_option, $p_option_value, ...]) + // Description : + // This method supports two synopsis. The first one is historical. + // This method extract all the files / directories from the archive to the + // folder indicated in $p_path. + // If you want to ignore the 'root' part of path of the memorized files + // you can indicate this in the optional $p_remove_path parameter. + // By default, if a newer file with the same name already exists, the + // file is not extracted. + // + // If both PCLZIP_OPT_PATH and PCLZIP_OPT_ADD_PATH aoptions + // are used, the path indicated in PCLZIP_OPT_ADD_PATH is append + // at the end of the path value of PCLZIP_OPT_PATH. + // Parameters : + // $p_path : Path where the files and directories are to be extracted + // $p_remove_path : First part ('root' part) of the memorized path + // (if any similar) to remove while extracting. + // Options : + // PCLZIP_OPT_PATH : + // PCLZIP_OPT_ADD_PATH : + // PCLZIP_OPT_REMOVE_PATH : + // PCLZIP_OPT_REMOVE_ALL_PATH : + // PCLZIP_CB_PRE_EXTRACT : + // PCLZIP_CB_POST_EXTRACT : + // Return Values : + // 0 or a negative value on failure, + // The list of the extracted files, with a status of the action. + // (see PclZip::listContent() for list entry format) + // -------------------------------------------------------------------------------- + function extract() + { + $v_result=1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Check archive + if (!$this->privCheckFormat()) { + return(0); + } + + // ----- Set default values + $v_options = array(); +// $v_path = "./"; + $v_path = ''; + $v_remove_path = ""; + $v_remove_all_path = false; + + // ----- Look for variable options arguments + $v_size = func_num_args(); + + // ----- Default values for option + $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE; + + // ----- Look for arguments + if ($v_size > 0) { + // ----- Get the arguments + $v_arg_list = func_get_args(); + + // ----- Look for first arg + if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { + + // ----- Parse the options + $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, + array (PCLZIP_OPT_PATH => 'optional', + PCLZIP_OPT_REMOVE_PATH => 'optional', + PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', + PCLZIP_OPT_ADD_PATH => 'optional', + PCLZIP_CB_PRE_EXTRACT => 'optional', + PCLZIP_CB_POST_EXTRACT => 'optional', + PCLZIP_OPT_SET_CHMOD => 'optional', + PCLZIP_OPT_BY_NAME => 'optional', + PCLZIP_OPT_BY_EREG => 'optional', + PCLZIP_OPT_BY_PREG => 'optional', + PCLZIP_OPT_BY_INDEX => 'optional', + PCLZIP_OPT_EXTRACT_AS_STRING => 'optional', + PCLZIP_OPT_EXTRACT_IN_OUTPUT => 'optional', + PCLZIP_OPT_REPLACE_NEWER => 'optional' + ,PCLZIP_OPT_STOP_ON_ERROR => 'optional' + ,PCLZIP_OPT_EXTRACT_DIR_RESTRICTION => 'optional', + PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', + PCLZIP_OPT_TEMP_FILE_ON => 'optional', + PCLZIP_OPT_TEMP_FILE_OFF => 'optional' + )); + if ($v_result != 1) { + return 0; + } + + // ----- Set the arguments + if (isset($v_options[PCLZIP_OPT_PATH])) { + $v_path = $v_options[PCLZIP_OPT_PATH]; + } + if (isset($v_options[PCLZIP_OPT_REMOVE_PATH])) { + $v_remove_path = $v_options[PCLZIP_OPT_REMOVE_PATH]; + } + if (isset($v_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { + $v_remove_all_path = $v_options[PCLZIP_OPT_REMOVE_ALL_PATH]; + } + if (isset($v_options[PCLZIP_OPT_ADD_PATH])) { + // ----- Check for '/' in last path char + if ((strlen($v_path) > 0) && (substr($v_path, -1) != '/')) { + $v_path .= '/'; + } + $v_path .= $v_options[PCLZIP_OPT_ADD_PATH]; + } + } + + // ----- Look for 2 args + // Here we need to support the first historic synopsis of the + // method. + else { + + // ----- Get the first argument + $v_path = $v_arg_list[0]; + + // ----- Look for the optional second argument + if ($v_size == 2) { + $v_remove_path = $v_arg_list[1]; + } + else if ($v_size > 2) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments"); + + // ----- Return + return 0; + } + } + } + + // ----- Look for default option values + $this->privOptionDefaultThreshold($v_options); + + // ----- Trace + + // ----- Call the extracting fct + $p_list = array(); + $v_result = $this->privExtractByRule($p_list, $v_path, $v_remove_path, + $v_remove_all_path, $v_options); + if ($v_result < 1) { + unset($p_list); + return(0); + } + + // ----- Return + return $p_list; + } + // -------------------------------------------------------------------------------- + + + // -------------------------------------------------------------------------------- + // Function : + // extractByIndex($p_index, $p_path="./", $p_remove_path="") + // extractByIndex($p_index, [$p_option, $p_option_value, ...]) + // Description : + // This method supports two synopsis. The first one is historical. + // This method is doing a partial extract of the archive. + // The extracted files or folders are identified by their index in the + // archive (from 0 to n). + // Note that if the index identify a folder, only the folder entry is + // extracted, not all the files included in the archive. + // Parameters : + // $p_index : A single index (integer) or a string of indexes of files to + // extract. The form of the string is "0,4-6,8-12" with only numbers + // and '-' for range or ',' to separate ranges. No spaces or ';' + // are allowed. + // $p_path : Path where the files and directories are to be extracted + // $p_remove_path : First part ('root' part) of the memorized path + // (if any similar) to remove while extracting. + // Options : + // PCLZIP_OPT_PATH : + // PCLZIP_OPT_ADD_PATH : + // PCLZIP_OPT_REMOVE_PATH : + // PCLZIP_OPT_REMOVE_ALL_PATH : + // PCLZIP_OPT_EXTRACT_AS_STRING : The files are extracted as strings and + // not as files. + // The resulting content is in a new field 'content' in the file + // structure. + // This option must be used alone (any other options are ignored). + // PCLZIP_CB_PRE_EXTRACT : + // PCLZIP_CB_POST_EXTRACT : + // Return Values : + // 0 on failure, + // The list of the extracted files, with a status of the action. + // (see PclZip::listContent() for list entry format) + // -------------------------------------------------------------------------------- + //function extractByIndex($p_index, options...) + function extractByIndex($p_index) + { + $v_result=1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Check archive + if (!$this->privCheckFormat()) { + return(0); + } + + // ----- Set default values + $v_options = array(); +// $v_path = "./"; + $v_path = ''; + $v_remove_path = ""; + $v_remove_all_path = false; + + // ----- Look for variable options arguments + $v_size = func_num_args(); + + // ----- Default values for option + $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE; + + // ----- Look for arguments + if ($v_size > 1) { + // ----- Get the arguments + $v_arg_list = func_get_args(); + + // ----- Remove form the options list the first argument + array_shift($v_arg_list); + $v_size--; + + // ----- Look for first arg + if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { + + // ----- Parse the options + $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, + array (PCLZIP_OPT_PATH => 'optional', + PCLZIP_OPT_REMOVE_PATH => 'optional', + PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', + PCLZIP_OPT_EXTRACT_AS_STRING => 'optional', + PCLZIP_OPT_ADD_PATH => 'optional', + PCLZIP_CB_PRE_EXTRACT => 'optional', + PCLZIP_CB_POST_EXTRACT => 'optional', + PCLZIP_OPT_SET_CHMOD => 'optional', + PCLZIP_OPT_REPLACE_NEWER => 'optional' + ,PCLZIP_OPT_STOP_ON_ERROR => 'optional' + ,PCLZIP_OPT_EXTRACT_DIR_RESTRICTION => 'optional', + PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', + PCLZIP_OPT_TEMP_FILE_ON => 'optional', + PCLZIP_OPT_TEMP_FILE_OFF => 'optional' + )); + if ($v_result != 1) { + return 0; + } + + // ----- Set the arguments + if (isset($v_options[PCLZIP_OPT_PATH])) { + $v_path = $v_options[PCLZIP_OPT_PATH]; + } + if (isset($v_options[PCLZIP_OPT_REMOVE_PATH])) { + $v_remove_path = $v_options[PCLZIP_OPT_REMOVE_PATH]; + } + if (isset($v_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { + $v_remove_all_path = $v_options[PCLZIP_OPT_REMOVE_ALL_PATH]; + } + if (isset($v_options[PCLZIP_OPT_ADD_PATH])) { + // ----- Check for '/' in last path char + if ((strlen($v_path) > 0) && (substr($v_path, -1) != '/')) { + $v_path .= '/'; + } + $v_path .= $v_options[PCLZIP_OPT_ADD_PATH]; + } + if (!isset($v_options[PCLZIP_OPT_EXTRACT_AS_STRING])) { + $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE; + } + else { + } + } + + // ----- Look for 2 args + // Here we need to support the first historic synopsis of the + // method. + else { + + // ----- Get the first argument + $v_path = $v_arg_list[0]; + + // ----- Look for the optional second argument + if ($v_size == 2) { + $v_remove_path = $v_arg_list[1]; + } + else if ($v_size > 2) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments"); + + // ----- Return + return 0; + } + } + } + + // ----- Trace + + // ----- Trick + // Here I want to reuse extractByRule(), so I need to parse the $p_index + // with privParseOptions() + $v_arg_trick = array (PCLZIP_OPT_BY_INDEX, $p_index); + $v_options_trick = array(); + $v_result = $this->privParseOptions($v_arg_trick, sizeof($v_arg_trick), $v_options_trick, + array (PCLZIP_OPT_BY_INDEX => 'optional' )); + if ($v_result != 1) { + return 0; + } + $v_options[PCLZIP_OPT_BY_INDEX] = $v_options_trick[PCLZIP_OPT_BY_INDEX]; + + // ----- Look for default option values + $this->privOptionDefaultThreshold($v_options); + + // ----- Call the extracting fct + if (($v_result = $this->privExtractByRule($p_list, $v_path, $v_remove_path, $v_remove_all_path, $v_options)) < 1) { + return(0); + } + + // ----- Return + return $p_list; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : + // delete([$p_option, $p_option_value, ...]) + // Description : + // This method removes files from the archive. + // If no parameters are given, then all the archive is emptied. + // Parameters : + // None or optional arguments. + // Options : + // PCLZIP_OPT_BY_INDEX : + // PCLZIP_OPT_BY_NAME : + // PCLZIP_OPT_BY_EREG : + // PCLZIP_OPT_BY_PREG : + // Return Values : + // 0 on failure, + // The list of the files which are still present in the archive. + // (see PclZip::listContent() for list entry format) + // -------------------------------------------------------------------------------- + function delete() + { + $v_result=1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Check archive + if (!$this->privCheckFormat()) { + return(0); + } + + // ----- Set default values + $v_options = array(); + + // ----- Look for variable options arguments + $v_size = func_num_args(); + + // ----- Look for arguments + if ($v_size > 0) { + // ----- Get the arguments + $v_arg_list = func_get_args(); + + // ----- Parse the options + $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, + array (PCLZIP_OPT_BY_NAME => 'optional', + PCLZIP_OPT_BY_EREG => 'optional', + PCLZIP_OPT_BY_PREG => 'optional', + PCLZIP_OPT_BY_INDEX => 'optional' )); + if ($v_result != 1) { + return 0; + } + } + + // ----- Magic quotes trick + $this->privDisableMagicQuotes(); + + // ----- Call the delete fct + $v_list = array(); + if (($v_result = $this->privDeleteByRule($v_list, $v_options)) != 1) { + $this->privSwapBackMagicQuotes(); + unset($v_list); + return(0); + } + + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_list; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : deleteByIndex() + // Description : + // ***** Deprecated ***** + // delete(PCLZIP_OPT_BY_INDEX, $p_index) should be prefered. + // -------------------------------------------------------------------------------- + function deleteByIndex($p_index) + { + + $p_list = $this->delete(PCLZIP_OPT_BY_INDEX, $p_index); + + // ----- Return + return $p_list; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : properties() + // Description : + // This method gives the properties of the archive. + // The properties are : + // nb : Number of files in the archive + // comment : Comment associated with the archive file + // status : not_exist, ok + // Parameters : + // None + // Return Values : + // 0 on failure, + // An array with the archive properties. + // -------------------------------------------------------------------------------- + function properties() + { + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Magic quotes trick + $this->privDisableMagicQuotes(); + + // ----- Check archive + if (!$this->privCheckFormat()) { + $this->privSwapBackMagicQuotes(); + return(0); + } + + // ----- Default properties + $v_prop = array(); + $v_prop['comment'] = ''; + $v_prop['nb'] = 0; + $v_prop['status'] = 'not_exist'; + + // ----- Look if file exists + if (@is_file($this->zipname)) + { + // ----- Open the zip file + if (($this->zip_fd = @fopen($this->zipname, 'rb')) == 0) + { + $this->privSwapBackMagicQuotes(); + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in binary read mode'); + + // ----- Return + return 0; + } + + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) + { + $this->privSwapBackMagicQuotes(); + return 0; + } + + // ----- Close the zip file + $this->privCloseFd(); + + // ----- Set the user attributes + $v_prop['comment'] = $v_central_dir['comment']; + $v_prop['nb'] = $v_central_dir['entries']; + $v_prop['status'] = 'ok'; + } + + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_prop; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : duplicate() + // Description : + // This method creates an archive by copying the content of an other one. If + // the archive already exist, it is replaced by the new one without any warning. + // Parameters : + // $p_archive : The filename of a valid archive, or + // a valid PclZip object. + // Return Values : + // 1 on success. + // 0 or a negative value on error (error code). + // -------------------------------------------------------------------------------- + function duplicate($p_archive) + { + $v_result = 1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Look if the $p_archive is a PclZip object + if ((is_object($p_archive)) && (get_class($p_archive) == 'pclzip')) + { + + // ----- Duplicate the archive + $v_result = $this->privDuplicate($p_archive->zipname); + } + + // ----- Look if the $p_archive is a string (so a filename) + else if (is_string($p_archive)) + { + + // ----- Check that $p_archive is a valid zip file + // TBC : Should also check the archive format + if (!is_file($p_archive)) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_FILE, "No file with filename '".$p_archive."'"); + $v_result = PCLZIP_ERR_MISSING_FILE; + } + else { + // ----- Duplicate the archive + $v_result = $this->privDuplicate($p_archive); + } + } + + // ----- Invalid variable + else + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_archive_to_add"); + $v_result = PCLZIP_ERR_INVALID_PARAMETER; + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : merge() + // Description : + // This method merge the $p_archive_to_add archive at the end of the current + // one ($this). + // If the archive ($this) does not exist, the merge becomes a duplicate. + // If the $p_archive_to_add archive does not exist, the merge is a success. + // Parameters : + // $p_archive_to_add : It can be directly the filename of a valid zip archive, + // or a PclZip object archive. + // Return Values : + // 1 on success, + // 0 or negative values on error (see below). + // -------------------------------------------------------------------------------- + function merge($p_archive_to_add) + { + $v_result = 1; + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Check archive + if (!$this->privCheckFormat()) { + return(0); + } + + // ----- Look if the $p_archive_to_add is a PclZip object + if ((is_object($p_archive_to_add)) && (get_class($p_archive_to_add) == 'pclzip')) + { + + // ----- Merge the archive + $v_result = $this->privMerge($p_archive_to_add); + } + + // ----- Look if the $p_archive_to_add is a string (so a filename) + else if (is_string($p_archive_to_add)) + { + + // ----- Create a temporary archive + $v_object_archive = new PclZip($p_archive_to_add); + + // ----- Merge the archive + $v_result = $this->privMerge($v_object_archive); + } + + // ----- Invalid variable + else + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_archive_to_add"); + $v_result = PCLZIP_ERR_INVALID_PARAMETER; + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + + + // -------------------------------------------------------------------------------- + // Function : errorCode() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function errorCode() + { + if (PCLZIP_ERROR_EXTERNAL == 1) { + return(PclErrorCode()); + } + else { + return($this->error_code); + } + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : errorName() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function errorName($p_with_code=false) + { + $v_name = array ( PCLZIP_ERR_NO_ERROR => 'PCLZIP_ERR_NO_ERROR', + PCLZIP_ERR_WRITE_OPEN_FAIL => 'PCLZIP_ERR_WRITE_OPEN_FAIL', + PCLZIP_ERR_READ_OPEN_FAIL => 'PCLZIP_ERR_READ_OPEN_FAIL', + PCLZIP_ERR_INVALID_PARAMETER => 'PCLZIP_ERR_INVALID_PARAMETER', + PCLZIP_ERR_MISSING_FILE => 'PCLZIP_ERR_MISSING_FILE', + PCLZIP_ERR_FILENAME_TOO_LONG => 'PCLZIP_ERR_FILENAME_TOO_LONG', + PCLZIP_ERR_INVALID_ZIP => 'PCLZIP_ERR_INVALID_ZIP', + PCLZIP_ERR_BAD_EXTRACTED_FILE => 'PCLZIP_ERR_BAD_EXTRACTED_FILE', + PCLZIP_ERR_DIR_CREATE_FAIL => 'PCLZIP_ERR_DIR_CREATE_FAIL', + PCLZIP_ERR_BAD_EXTENSION => 'PCLZIP_ERR_BAD_EXTENSION', + PCLZIP_ERR_BAD_FORMAT => 'PCLZIP_ERR_BAD_FORMAT', + PCLZIP_ERR_DELETE_FILE_FAIL => 'PCLZIP_ERR_DELETE_FILE_FAIL', + PCLZIP_ERR_RENAME_FILE_FAIL => 'PCLZIP_ERR_RENAME_FILE_FAIL', + PCLZIP_ERR_BAD_CHECKSUM => 'PCLZIP_ERR_BAD_CHECKSUM', + PCLZIP_ERR_INVALID_ARCHIVE_ZIP => 'PCLZIP_ERR_INVALID_ARCHIVE_ZIP', + PCLZIP_ERR_MISSING_OPTION_VALUE => 'PCLZIP_ERR_MISSING_OPTION_VALUE', + PCLZIP_ERR_INVALID_OPTION_VALUE => 'PCLZIP_ERR_INVALID_OPTION_VALUE', + PCLZIP_ERR_UNSUPPORTED_COMPRESSION => 'PCLZIP_ERR_UNSUPPORTED_COMPRESSION', + PCLZIP_ERR_UNSUPPORTED_ENCRYPTION => 'PCLZIP_ERR_UNSUPPORTED_ENCRYPTION' + ,PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE => 'PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE' + ,PCLZIP_ERR_DIRECTORY_RESTRICTION => 'PCLZIP_ERR_DIRECTORY_RESTRICTION' + ); + + if (isset($v_name[$this->error_code])) { + $v_value = $v_name[$this->error_code]; + } + else { + $v_value = 'NoName'; + } + + if ($p_with_code) { + return($v_value.' ('.$this->error_code.')'); + } + else { + return($v_value); + } + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : errorInfo() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function errorInfo($p_full=false) + { + if (PCLZIP_ERROR_EXTERNAL == 1) { + return(PclErrorString()); + } + else { + if ($p_full) { + return($this->errorName(true)." : ".$this->error_string); + } + else { + return($this->error_string." [code ".$this->error_code."]"); + } + } + } + // -------------------------------------------------------------------------------- + + +// -------------------------------------------------------------------------------- +// ***** UNDER THIS LINE ARE DEFINED PRIVATE INTERNAL FUNCTIONS ***** +// ***** ***** +// ***** THESES FUNCTIONS MUST NOT BE USED DIRECTLY ***** +// -------------------------------------------------------------------------------- + + + + // -------------------------------------------------------------------------------- + // Function : privCheckFormat() + // Description : + // This method check that the archive exists and is a valid zip archive. + // Several level of check exists. (futur) + // Parameters : + // $p_level : Level of check. Default 0. + // 0 : Check the first bytes (magic codes) (default value)) + // 1 : 0 + Check the central directory (futur) + // 2 : 1 + Check each file header (futur) + // Return Values : + // true on success, + // false on error, the error code is set. + // -------------------------------------------------------------------------------- + function privCheckFormat($p_level=0) + { + $v_result = true; + + // ----- Reset the file system cache + clearstatcache(); + + // ----- Reset the error handler + $this->privErrorReset(); + + // ----- Look if the file exits + if (!is_file($this->zipname)) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_FILE, "Missing archive file '".$this->zipname."'"); + return(false); + } + + // ----- Check that the file is readeable + if (!is_readable($this->zipname)) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to read archive '".$this->zipname."'"); + return(false); + } + + // ----- Check the magic code + // TBC + + // ----- Check the central header + // TBC + + // ----- Check each file header + // TBC + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privParseOptions() + // Description : + // This internal methods reads the variable list of arguments ($p_options_list, + // $p_size) and generate an array with the options and values ($v_result_list). + // $v_requested_options contains the options that can be present and those that + // must be present. + // $v_requested_options is an array, with the option value as key, and 'optional', + // or 'mandatory' as value. + // Parameters : + // See above. + // Return Values : + // 1 on success. + // 0 on failure. + // -------------------------------------------------------------------------------- + function privParseOptions(&$p_options_list, $p_size, &$v_result_list, $v_requested_options=false) + { + $v_result=1; + + // ----- Read the options + $i=0; + while ($i<$p_size) { + + // ----- Check if the option is supported + if (!isset($v_requested_options[$p_options_list[$i]])) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid optional parameter '".$p_options_list[$i]."' for this method"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Look for next option + switch ($p_options_list[$i]) { + // ----- Look for options that request a path value + case PCLZIP_OPT_PATH : + case PCLZIP_OPT_REMOVE_PATH : + case PCLZIP_OPT_ADD_PATH : + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + $v_result_list[$p_options_list[$i]] = PclZipUtilTranslateWinPath($p_options_list[$i+1], FALSE); + $i++; + break; + + case PCLZIP_OPT_TEMP_FILE_THRESHOLD : + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + return PclZip::errorCode(); + } + + // ----- Check for incompatible options + if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_OFF])) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_OFF'"); + return PclZip::errorCode(); + } + + // ----- Check the value + $v_value = $p_options_list[$i+1]; + if ((!is_integer($v_value)) || ($v_value<0)) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Integer expected for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + return PclZip::errorCode(); + } + + // ----- Get the value (and convert it in bytes) + $v_result_list[$p_options_list[$i]] = $v_value*1048576; + $i++; + break; + + case PCLZIP_OPT_TEMP_FILE_ON : + // ----- Check for incompatible options + if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_OFF])) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_OFF'"); + return PclZip::errorCode(); + } + + $v_result_list[$p_options_list[$i]] = true; + break; + + case PCLZIP_OPT_TEMP_FILE_OFF : + // ----- Check for incompatible options + if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_ON])) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_ON'"); + return PclZip::errorCode(); + } + // ----- Check for incompatible options + if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_THRESHOLD])) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_THRESHOLD'"); + return PclZip::errorCode(); + } + + $v_result_list[$p_options_list[$i]] = true; + break; + + case PCLZIP_OPT_EXTRACT_DIR_RESTRICTION : + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + if ( is_string($p_options_list[$i+1]) + && ($p_options_list[$i+1] != '')) { + $v_result_list[$p_options_list[$i]] = PclZipUtilTranslateWinPath($p_options_list[$i+1], FALSE); + $i++; + } + else { + } + break; + + // ----- Look for options that request an array of string for value + case PCLZIP_OPT_BY_NAME : + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + if (is_string($p_options_list[$i+1])) { + $v_result_list[$p_options_list[$i]][0] = $p_options_list[$i+1]; + } + else if (is_array($p_options_list[$i+1])) { + $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1]; + } + else { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Wrong parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + $i++; + break; + + // ----- Look for options that request an EREG or PREG expression + case PCLZIP_OPT_BY_EREG : + // ereg() is deprecated starting with PHP 5.3. Move PCLZIP_OPT_BY_EREG + // to PCLZIP_OPT_BY_PREG + $p_options_list[$i] = PCLZIP_OPT_BY_PREG; + case PCLZIP_OPT_BY_PREG : + //case PCLZIP_OPT_CRYPT : + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + if (is_string($p_options_list[$i+1])) { + $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1]; + } + else { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Wrong parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + $i++; + break; + + // ----- Look for options that takes a string + case PCLZIP_OPT_COMMENT : + case PCLZIP_OPT_ADD_COMMENT : + case PCLZIP_OPT_PREPEND_COMMENT : + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, + "Missing parameter value for option '" + .PclZipUtilOptionText($p_options_list[$i]) + ."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + if (is_string($p_options_list[$i+1])) { + $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1]; + } + else { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, + "Wrong parameter value for option '" + .PclZipUtilOptionText($p_options_list[$i]) + ."'"); + + // ----- Return + return PclZip::errorCode(); + } + $i++; + break; + + // ----- Look for options that request an array of index + case PCLZIP_OPT_BY_INDEX : + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + $v_work_list = array(); + if (is_string($p_options_list[$i+1])) { + + // ----- Remove spaces + $p_options_list[$i+1] = strtr($p_options_list[$i+1], ' ', ''); + + // ----- Parse items + $v_work_list = explode(",", $p_options_list[$i+1]); + } + else if (is_integer($p_options_list[$i+1])) { + $v_work_list[0] = $p_options_list[$i+1].'-'.$p_options_list[$i+1]; + } + else if (is_array($p_options_list[$i+1])) { + $v_work_list = $p_options_list[$i+1]; + } + else { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Value must be integer, string or array for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Reduce the index list + // each index item in the list must be a couple with a start and + // an end value : [0,3], [5-5], [8-10], ... + // ----- Check the format of each item + $v_sort_flag=false; + $v_sort_value=0; + for ($j=0; $j= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1]; + $i++; + break; + + // ----- Look for options that request a call-back + case PCLZIP_CB_PRE_EXTRACT : + case PCLZIP_CB_POST_EXTRACT : + case PCLZIP_CB_PRE_ADD : + case PCLZIP_CB_POST_ADD : + /* for futur use + case PCLZIP_CB_PRE_DELETE : + case PCLZIP_CB_POST_DELETE : + case PCLZIP_CB_PRE_LIST : + case PCLZIP_CB_POST_LIST : + */ + // ----- Check the number of parameters + if (($i+1) >= $p_size) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Get the value + $v_function_name = $p_options_list[$i+1]; + + // ----- Check that the value is a valid existing function + if (!function_exists($v_function_name)) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Function '".$v_function_name."()' is not an existing function for option '".PclZipUtilOptionText($p_options_list[$i])."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Set the attribute + $v_result_list[$p_options_list[$i]] = $v_function_name; + $i++; + break; + + default : + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, + "Unknown parameter '" + .$p_options_list[$i]."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Next options + $i++; + } + + // ----- Look for mandatory options + if ($v_requested_options !== false) { + for ($key=reset($v_requested_options); $key=key($v_requested_options); $key=next($v_requested_options)) { + // ----- Look for mandatory option + if ($v_requested_options[$key] == 'mandatory') { + // ----- Look if present + if (!isset($v_result_list[$key])) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Missing mandatory parameter ".PclZipUtilOptionText($key)."(".$key.")"); + + // ----- Return + return PclZip::errorCode(); + } + } + } + } + + // ----- Look for default values + if (!isset($v_result_list[PCLZIP_OPT_TEMP_FILE_THRESHOLD])) { + + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privOptionDefaultThreshold() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privOptionDefaultThreshold(&$p_options) + { + $v_result=1; + + if (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]) + || isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) { + return $v_result; + } + + // ----- Get 'memory_limit' configuration value + $v_memory_limit = ini_get('memory_limit'); + $v_memory_limit = trim($v_memory_limit); + $last = strtolower(substr($v_memory_limit, -1)); + + if($last == 'g') + //$v_memory_limit = $v_memory_limit*1024*1024*1024; + $v_memory_limit = $v_memory_limit*1073741824; + if($last == 'm') + //$v_memory_limit = $v_memory_limit*1024*1024; + $v_memory_limit = $v_memory_limit*1048576; + if($last == 'k') + $v_memory_limit = $v_memory_limit*1024; + + $p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] = floor($v_memory_limit*PCLZIP_TEMPORARY_FILE_RATIO); + + + // ----- Sanity check : No threshold if value lower than 1M + if ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] < 1048576) { + unset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privFileDescrParseAtt() + // Description : + // Parameters : + // Return Values : + // 1 on success. + // 0 on failure. + // -------------------------------------------------------------------------------- + function privFileDescrParseAtt(&$p_file_list, &$p_filedescr, $v_options, $v_requested_options=false) + { + $v_result=1; + + // ----- For each file in the list check the attributes + foreach ($p_file_list as $v_key => $v_value) { + + // ----- Check if the option is supported + if (!isset($v_requested_options[$v_key])) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid file attribute '".$v_key."' for this file"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Look for attribute + switch ($v_key) { + case PCLZIP_ATT_FILE_NAME : + if (!is_string($v_value)) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'"); + return PclZip::errorCode(); + } + + $p_filedescr['filename'] = PclZipUtilPathReduction($v_value); + + if ($p_filedescr['filename'] == '') { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty filename for attribute '".PclZipUtilOptionText($v_key)."'"); + return PclZip::errorCode(); + } + + break; + + case PCLZIP_ATT_FILE_NEW_SHORT_NAME : + if (!is_string($v_value)) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'"); + return PclZip::errorCode(); + } + + $p_filedescr['new_short_name'] = PclZipUtilPathReduction($v_value); + + if ($p_filedescr['new_short_name'] == '') { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty short filename for attribute '".PclZipUtilOptionText($v_key)."'"); + return PclZip::errorCode(); + } + break; + + case PCLZIP_ATT_FILE_NEW_FULL_NAME : + if (!is_string($v_value)) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'"); + return PclZip::errorCode(); + } + + $p_filedescr['new_full_name'] = PclZipUtilPathReduction($v_value); + + if ($p_filedescr['new_full_name'] == '') { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty full filename for attribute '".PclZipUtilOptionText($v_key)."'"); + return PclZip::errorCode(); + } + break; + + // ----- Look for options that takes a string + case PCLZIP_ATT_FILE_COMMENT : + if (!is_string($v_value)) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'"); + return PclZip::errorCode(); + } + + $p_filedescr['comment'] = $v_value; + break; + + case PCLZIP_ATT_FILE_MTIME : + if (!is_integer($v_value)) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". Integer expected for attribute '".PclZipUtilOptionText($v_key)."'"); + return PclZip::errorCode(); + } + + $p_filedescr['mtime'] = $v_value; + break; + + case PCLZIP_ATT_FILE_CONTENT : + $p_filedescr['content'] = $v_value; + break; + + default : + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, + "Unknown parameter '".$v_key."'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Look for mandatory options + if ($v_requested_options !== false) { + for ($key=reset($v_requested_options); $key=key($v_requested_options); $key=next($v_requested_options)) { + // ----- Look for mandatory option + if ($v_requested_options[$key] == 'mandatory') { + // ----- Look if present + if (!isset($p_file_list[$key])) { + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Missing mandatory parameter ".PclZipUtilOptionText($key)."(".$key.")"); + return PclZip::errorCode(); + } + } + } + } + + // end foreach + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privFileDescrExpand() + // Description : + // This method look for each item of the list to see if its a file, a folder + // or a string to be added as file. For any other type of files (link, other) + // just ignore the item. + // Then prepare the information that will be stored for that file. + // When its a folder, expand the folder with all the files that are in that + // folder (recursively). + // Parameters : + // Return Values : + // 1 on success. + // 0 on failure. + // -------------------------------------------------------------------------------- + function privFileDescrExpand(&$p_filedescr_list, &$p_options) + { + $v_result=1; + + // ----- Create a result list + $v_result_list = array(); + + // ----- Look each entry + for ($i=0; $iprivCalculateStoredFilename($v_descr, $p_options); + + // ----- Add the descriptor in result list + $v_result_list[sizeof($v_result_list)] = $v_descr; + + // ----- Look for folder + if ($v_descr['type'] == 'folder') { + // ----- List of items in folder + $v_dirlist_descr = array(); + $v_dirlist_nb = 0; + if ($v_folder_handler = @opendir($v_descr['filename'])) { + while (($v_item_handler = @readdir($v_folder_handler)) !== false) { + + // ----- Skip '.' and '..' + if (($v_item_handler == '.') || ($v_item_handler == '..')) { + continue; + } + + // ----- Compose the full filename + $v_dirlist_descr[$v_dirlist_nb]['filename'] = $v_descr['filename'].'/'.$v_item_handler; + + // ----- Look for different stored filename + // Because the name of the folder was changed, the name of the + // files/sub-folders also change + if (($v_descr['stored_filename'] != $v_descr['filename']) + && (!isset($p_options[PCLZIP_OPT_REMOVE_ALL_PATH]))) { + if ($v_descr['stored_filename'] != '') { + $v_dirlist_descr[$v_dirlist_nb]['new_full_name'] = $v_descr['stored_filename'].'/'.$v_item_handler; + } + else { + $v_dirlist_descr[$v_dirlist_nb]['new_full_name'] = $v_item_handler; + } + } + + $v_dirlist_nb++; + } + + @closedir($v_folder_handler); + } + else { + // TBC : unable to open folder in read mode + } + + // ----- Expand each element of the list + if ($v_dirlist_nb != 0) { + // ----- Expand + if (($v_result = $this->privFileDescrExpand($v_dirlist_descr, $p_options)) != 1) { + return $v_result; + } + + // ----- Concat the resulting list + $v_result_list = array_merge($v_result_list, $v_dirlist_descr); + } + else { + } + + // ----- Free local array + unset($v_dirlist_descr); + } + } + + // ----- Get the result list + $p_filedescr_list = $v_result_list; + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privCreate() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privCreate($p_filedescr_list, &$p_result_list, &$p_options) + { + $v_result=1; + $v_list_detail = array(); + + // ----- Magic quotes trick + $this->privDisableMagicQuotes(); + + // ----- Open the file in write mode + if (($v_result = $this->privOpenFd('wb')) != 1) + { + // ----- Return + return $v_result; + } + + // ----- Add the list of files + $v_result = $this->privAddList($p_filedescr_list, $p_result_list, $p_options); + + // ----- Close + $this->privCloseFd(); + + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privAdd() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privAdd($p_filedescr_list, &$p_result_list, &$p_options) + { + $v_result=1; + $v_list_detail = array(); + + // ----- Look if the archive exists or is empty + if ((!is_file($this->zipname)) || (filesize($this->zipname) == 0)) + { + + // ----- Do a create + $v_result = $this->privCreate($p_filedescr_list, $p_result_list, $p_options); + + // ----- Return + return $v_result; + } + // ----- Magic quotes trick + $this->privDisableMagicQuotes(); + + // ----- Open the zip file + if (($v_result=$this->privOpenFd('rb')) != 1) + { + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_result; + } + + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) + { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + return $v_result; + } + + // ----- Go to beginning of File + @rewind($this->zip_fd); + + // ----- Creates a temporay file + $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp'; + + // ----- Open the temporary file in write mode + if (($v_zip_temp_fd = @fopen($v_zip_temp_name, 'wb')) == 0) + { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_zip_temp_name.'\' in binary write mode'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Copy the files from the archive to the temporary file + // TBC : Here I should better append the file and go back to erase the central dir + $v_size = $v_central_dir['offset']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = fread($this->zip_fd, $v_read_size); + @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Swap the file descriptor + // Here is a trick : I swap the temporary fd with the zip fd, in order to use + // the following methods on the temporary fil and not the real archive + $v_swap = $this->zip_fd; + $this->zip_fd = $v_zip_temp_fd; + $v_zip_temp_fd = $v_swap; + + // ----- Add the files + $v_header_list = array(); + if (($v_result = $this->privAddFileList($p_filedescr_list, $v_header_list, $p_options)) != 1) + { + fclose($v_zip_temp_fd); + $this->privCloseFd(); + @unlink($v_zip_temp_name); + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_result; + } + + // ----- Store the offset of the central dir + $v_offset = @ftell($this->zip_fd); + + // ----- Copy the block of file headers from the old archive + $v_size = $v_central_dir['size']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($v_zip_temp_fd, $v_read_size); + @fwrite($this->zip_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Create the Central Dir files header + for ($i=0, $v_count=0; $iprivWriteCentralFileHeader($v_header_list[$i])) != 1) { + fclose($v_zip_temp_fd); + $this->privCloseFd(); + @unlink($v_zip_temp_name); + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_result; + } + $v_count++; + } + + // ----- Transform the header to a 'usable' info + $this->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); + } + + // ----- Zip file comment + $v_comment = $v_central_dir['comment']; + if (isset($p_options[PCLZIP_OPT_COMMENT])) { + $v_comment = $p_options[PCLZIP_OPT_COMMENT]; + } + if (isset($p_options[PCLZIP_OPT_ADD_COMMENT])) { + $v_comment = $v_comment.$p_options[PCLZIP_OPT_ADD_COMMENT]; + } + if (isset($p_options[PCLZIP_OPT_PREPEND_COMMENT])) { + $v_comment = $p_options[PCLZIP_OPT_PREPEND_COMMENT].$v_comment; + } + + // ----- Calculate the size of the central header + $v_size = @ftell($this->zip_fd)-$v_offset; + + // ----- Create the central dir footer + if (($v_result = $this->privWriteCentralHeader($v_count+$v_central_dir['entries'], $v_size, $v_offset, $v_comment)) != 1) + { + // ----- Reset the file list + unset($v_header_list); + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_result; + } + + // ----- Swap back the file descriptor + $v_swap = $this->zip_fd; + $this->zip_fd = $v_zip_temp_fd; + $v_zip_temp_fd = $v_swap; + + // ----- Close + $this->privCloseFd(); + + // ----- Close the temporary file + @fclose($v_zip_temp_fd); + + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); + + // ----- Delete the zip file + // TBC : I should test the result ... + @unlink($this->zipname); + + // ----- Rename the temporary file + // TBC : I should test the result ... + //@rename($v_zip_temp_name, $this->zipname); + PclZipUtilRename($v_zip_temp_name, $this->zipname); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privOpenFd() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function privOpenFd($p_mode) + { + $v_result=1; + + // ----- Look if already open + if ($this->zip_fd != 0) + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Zip file \''.$this->zipname.'\' already open'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Open the zip file + if (($this->zip_fd = @fopen($this->zipname, $p_mode)) == 0) + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in '.$p_mode.' mode'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privCloseFd() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function privCloseFd() + { + $v_result=1; + + if ($this->zip_fd != 0) + @fclose($this->zip_fd); + $this->zip_fd = 0; + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privAddList() + // Description : + // $p_add_dir and $p_remove_dir will give the ability to memorize a path which is + // different from the real path of the file. This is usefull if you want to have PclTar + // running in any directory, and memorize relative path from an other directory. + // Parameters : + // $p_list : An array containing the file or directory names to add in the tar + // $p_result_list : list of added files with their properties (specially the status field) + // $p_add_dir : Path to add in the filename path archived + // $p_remove_dir : Path to remove in the filename path archived + // Return Values : + // -------------------------------------------------------------------------------- +// function privAddList($p_list, &$p_result_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, &$p_options) + function privAddList($p_filedescr_list, &$p_result_list, &$p_options) + { + $v_result=1; + + // ----- Add the files + $v_header_list = array(); + if (($v_result = $this->privAddFileList($p_filedescr_list, $v_header_list, $p_options)) != 1) + { + // ----- Return + return $v_result; + } + + // ----- Store the offset of the central dir + $v_offset = @ftell($this->zip_fd); + + // ----- Create the Central Dir files header + for ($i=0,$v_count=0; $iprivWriteCentralFileHeader($v_header_list[$i])) != 1) { + // ----- Return + return $v_result; + } + $v_count++; + } + + // ----- Transform the header to a 'usable' info + $this->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); + } + + // ----- Zip file comment + $v_comment = ''; + if (isset($p_options[PCLZIP_OPT_COMMENT])) { + $v_comment = $p_options[PCLZIP_OPT_COMMENT]; + } + + // ----- Calculate the size of the central header + $v_size = @ftell($this->zip_fd)-$v_offset; + + // ----- Create the central dir footer + if (($v_result = $this->privWriteCentralHeader($v_count, $v_size, $v_offset, $v_comment)) != 1) + { + // ----- Reset the file list + unset($v_header_list); + + // ----- Return + return $v_result; + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privAddFileList() + // Description : + // Parameters : + // $p_filedescr_list : An array containing the file description + // or directory names to add in the zip + // $p_result_list : list of added files with their properties (specially the status field) + // Return Values : + // -------------------------------------------------------------------------------- + function privAddFileList($p_filedescr_list, &$p_result_list, &$p_options) + { + $v_result=1; + $v_header = array(); + + // ----- Recuperate the current number of elt in list + $v_nb = sizeof($p_result_list); + + // ----- Loop on the files + for ($j=0; ($jprivAddFile($p_filedescr_list[$j], $v_header, + $p_options); + if ($v_result != 1) { + return $v_result; + } + + // ----- Store the file infos + $p_result_list[$v_nb++] = $v_header; + } + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privAddFile() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privAddFile($p_filedescr, &$p_header, &$p_options) + { + $v_result=1; + + // ----- Working variable + $p_filename = $p_filedescr['filename']; + + // TBC : Already done in the fileAtt check ... ? + if ($p_filename == "") { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid file list parameter (invalid or empty list)"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Look for a stored different filename + /* TBC : Removed + if (isset($p_filedescr['stored_filename'])) { + $v_stored_filename = $p_filedescr['stored_filename']; + } + else { + $v_stored_filename = $p_filedescr['stored_filename']; + } + */ + + // ----- Set the file properties + clearstatcache(); + $p_header['version'] = 20; + $p_header['version_extracted'] = 10; + $p_header['flag'] = 0; + $p_header['compression'] = 0; + $p_header['crc'] = 0; + $p_header['compressed_size'] = 0; + $p_header['filename_len'] = strlen($p_filename); + $p_header['extra_len'] = 0; + $p_header['disk'] = 0; + $p_header['internal'] = 0; + $p_header['offset'] = 0; + $p_header['filename'] = $p_filename; +// TBC : Removed $p_header['stored_filename'] = $v_stored_filename; + $p_header['stored_filename'] = $p_filedescr['stored_filename']; + $p_header['extra'] = ''; + $p_header['status'] = 'ok'; + $p_header['index'] = -1; + + // ----- Look for regular file + if ($p_filedescr['type']=='file') { + $p_header['external'] = 0x00000000; + $p_header['size'] = filesize($p_filename); + } + + // ----- Look for regular folder + else if ($p_filedescr['type']=='folder') { + $p_header['external'] = 0x00000010; + $p_header['mtime'] = filemtime($p_filename); + $p_header['size'] = filesize($p_filename); + } + + // ----- Look for virtual file + else if ($p_filedescr['type'] == 'virtual_file') { + $p_header['external'] = 0x00000000; + $p_header['size'] = strlen($p_filedescr['content']); + } + + + // ----- Look for filetime + if (isset($p_filedescr['mtime'])) { + $p_header['mtime'] = $p_filedescr['mtime']; + } + else if ($p_filedescr['type'] == 'virtual_file') { + $p_header['mtime'] = time(); + } + else { + $p_header['mtime'] = filemtime($p_filename); + } + + // ------ Look for file comment + if (isset($p_filedescr['comment'])) { + $p_header['comment_len'] = strlen($p_filedescr['comment']); + $p_header['comment'] = $p_filedescr['comment']; + } + else { + $p_header['comment_len'] = 0; + $p_header['comment'] = ''; + } + + // ----- Look for pre-add callback + if (isset($p_options[PCLZIP_CB_PRE_ADD])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_header, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. + $v_result = $p_options[PCLZIP_CB_PRE_ADD](PCLZIP_CB_PRE_ADD, $v_local_header); + if ($v_result == 0) { + // ----- Change the file status + $p_header['status'] = "skipped"; + $v_result = 1; + } + + // ----- Update the informations + // Only some fields can be modified + if ($p_header['stored_filename'] != $v_local_header['stored_filename']) { + $p_header['stored_filename'] = PclZipUtilPathReduction($v_local_header['stored_filename']); + } + } + + // ----- Look for empty stored filename + if ($p_header['stored_filename'] == "") { + $p_header['status'] = "filtered"; + } + + // ----- Check the path length + if (strlen($p_header['stored_filename']) > 0xFF) { + $p_header['status'] = 'filename_too_long'; + } + + // ----- Look if no error, or file not skipped + if ($p_header['status'] == 'ok') { + + // ----- Look for a file + if ($p_filedescr['type'] == 'file') { + // ----- Look for using temporary file to zip + if ( (!isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) + && (isset($p_options[PCLZIP_OPT_TEMP_FILE_ON]) + || (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]) + && ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] <= $p_header['size'])) ) ) { + $v_result = $this->privAddFileUsingTempFile($p_filedescr, $p_header, $p_options); + if ($v_result < PCLZIP_ERR_NO_ERROR) { + return $v_result; + } + } + + // ----- Use "in memory" zip algo + else { + + // ----- Open the source file + if (($v_file = @fopen($p_filename, "rb")) == 0) { + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to open file '$p_filename' in binary read mode"); + return PclZip::errorCode(); + } + + // ----- Read the file content + $v_content = @fread($v_file, $p_header['size']); + + // ----- Close the file + @fclose($v_file); + + // ----- Calculate the CRC + $p_header['crc'] = @crc32($v_content); + + // ----- Look for no compression + if ($p_options[PCLZIP_OPT_NO_COMPRESSION]) { + // ----- Set header parameters + $p_header['compressed_size'] = $p_header['size']; + $p_header['compression'] = 0; + } + + // ----- Look for normal compression + else { + // ----- Compress the content + $v_content = @gzdeflate($v_content); + + // ----- Set header parameters + $p_header['compressed_size'] = strlen($v_content); + $p_header['compression'] = 8; + } + + // ----- Call the header generation + if (($v_result = $this->privWriteFileHeader($p_header)) != 1) { + @fclose($v_file); + return $v_result; + } + + // ----- Write the compressed (or not) content + @fwrite($this->zip_fd, $v_content, $p_header['compressed_size']); + + } + + } + + // ----- Look for a virtual file (a file from string) + else if ($p_filedescr['type'] == 'virtual_file') { + + $v_content = $p_filedescr['content']; + + // ----- Calculate the CRC + $p_header['crc'] = @crc32($v_content); + + // ----- Look for no compression + if ($p_options[PCLZIP_OPT_NO_COMPRESSION]) { + // ----- Set header parameters + $p_header['compressed_size'] = $p_header['size']; + $p_header['compression'] = 0; + } + + // ----- Look for normal compression + else { + // ----- Compress the content + $v_content = @gzdeflate($v_content); + + // ----- Set header parameters + $p_header['compressed_size'] = strlen($v_content); + $p_header['compression'] = 8; + } + + // ----- Call the header generation + if (($v_result = $this->privWriteFileHeader($p_header)) != 1) { + @fclose($v_file); + return $v_result; + } + + // ----- Write the compressed (or not) content + @fwrite($this->zip_fd, $v_content, $p_header['compressed_size']); + } + + // ----- Look for a directory + else if ($p_filedescr['type'] == 'folder') { + // ----- Look for directory last '/' + if (@substr($p_header['stored_filename'], -1) != '/') { + $p_header['stored_filename'] .= '/'; + } + + // ----- Set the file properties + $p_header['size'] = 0; + //$p_header['external'] = 0x41FF0010; // Value for a folder : to be checked + $p_header['external'] = 0x00000010; // Value for a folder : to be checked + + // ----- Call the header generation + if (($v_result = $this->privWriteFileHeader($p_header)) != 1) + { + return $v_result; + } + } + } + + // ----- Look for post-add callback + if (isset($p_options[PCLZIP_CB_POST_ADD])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_header, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. + $v_result = $p_options[PCLZIP_CB_POST_ADD](PCLZIP_CB_POST_ADD, $v_local_header); + if ($v_result == 0) { + // ----- Ignored + $v_result = 1; + } + + // ----- Update the informations + // Nothing can be modified + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privAddFileUsingTempFile() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privAddFileUsingTempFile($p_filedescr, &$p_header, &$p_options) + { + $v_result=PCLZIP_ERR_NO_ERROR; + + // ----- Working variable + $p_filename = $p_filedescr['filename']; + + + // ----- Open the source file + if (($v_file = @fopen($p_filename, "rb")) == 0) { + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to open file '$p_filename' in binary read mode"); + return PclZip::errorCode(); + } + + // ----- Creates a compressed temporary file + $v_gzip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.gz'; + if (($v_file_compressed = @gzopen($v_gzip_temp_name, "wb")) == 0) { + fclose($v_file); + PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary write mode'); + return PclZip::errorCode(); + } + + // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks + $v_size = filesize($p_filename); + while ($v_size != 0) { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($v_file, $v_read_size); + //$v_binary_data = pack('a'.$v_read_size, $v_buffer); + @gzputs($v_file_compressed, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Close the file + @fclose($v_file); + @gzclose($v_file_compressed); + + // ----- Check the minimum file size + if (filesize($v_gzip_temp_name) < 18) { + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'gzip temporary file \''.$v_gzip_temp_name.'\' has invalid filesize - should be minimum 18 bytes'); + return PclZip::errorCode(); + } + + // ----- Extract the compressed attributes + if (($v_file_compressed = @fopen($v_gzip_temp_name, "rb")) == 0) { + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary read mode'); + return PclZip::errorCode(); + } + + // ----- Read the gzip file header + $v_binary_data = @fread($v_file_compressed, 10); + $v_data_header = unpack('a1id1/a1id2/a1cm/a1flag/Vmtime/a1xfl/a1os', $v_binary_data); + + // ----- Check some parameters + $v_data_header['os'] = bin2hex($v_data_header['os']); + + // ----- Read the gzip file footer + @fseek($v_file_compressed, filesize($v_gzip_temp_name)-8); + $v_binary_data = @fread($v_file_compressed, 8); + $v_data_footer = unpack('Vcrc/Vcompressed_size', $v_binary_data); + + // ----- Set the attributes + $p_header['compression'] = ord($v_data_header['cm']); + //$p_header['mtime'] = $v_data_header['mtime']; + $p_header['crc'] = $v_data_footer['crc']; + $p_header['compressed_size'] = filesize($v_gzip_temp_name)-18; + + // ----- Close the file + @fclose($v_file_compressed); + + // ----- Call the header generation + if (($v_result = $this->privWriteFileHeader($p_header)) != 1) { + return $v_result; + } + + // ----- Add the compressed data + if (($v_file_compressed = @fopen($v_gzip_temp_name, "rb")) == 0) + { + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary read mode'); + return PclZip::errorCode(); + } + + // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks + fseek($v_file_compressed, 10); + $v_size = $p_header['compressed_size']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($v_file_compressed, $v_read_size); + //$v_binary_data = pack('a'.$v_read_size, $v_buffer); + @fwrite($this->zip_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Close the file + @fclose($v_file_compressed); + + // ----- Unlink the temporary file + @unlink($v_gzip_temp_name); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privCalculateStoredFilename() + // Description : + // Based on file descriptor properties and global options, this method + // calculate the filename that will be stored in the archive. + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privCalculateStoredFilename(&$p_filedescr, &$p_options) + { + $v_result=1; + + // ----- Working variables + $p_filename = $p_filedescr['filename']; + if (isset($p_options[PCLZIP_OPT_ADD_PATH])) { + $p_add_dir = $p_options[PCLZIP_OPT_ADD_PATH]; + } + else { + $p_add_dir = ''; + } + if (isset($p_options[PCLZIP_OPT_REMOVE_PATH])) { + $p_remove_dir = $p_options[PCLZIP_OPT_REMOVE_PATH]; + } + else { + $p_remove_dir = ''; + } + if (isset($p_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { + $p_remove_all_dir = $p_options[PCLZIP_OPT_REMOVE_ALL_PATH]; + } + else { + $p_remove_all_dir = 0; + } + + + // ----- Look for full name change + if (isset($p_filedescr['new_full_name'])) { + // ----- Remove drive letter if any + $v_stored_filename = PclZipUtilTranslateWinPath($p_filedescr['new_full_name']); + } + + // ----- Look for path and/or short name change + else { + + // ----- Look for short name change + // Its when we cahnge just the filename but not the path + if (isset($p_filedescr['new_short_name'])) { + $v_path_info = pathinfo($p_filename); + $v_dir = ''; + if ($v_path_info['dirname'] != '') { + $v_dir = $v_path_info['dirname'].'/'; + } + $v_stored_filename = $v_dir.$p_filedescr['new_short_name']; + } + else { + // ----- Calculate the stored filename + $v_stored_filename = $p_filename; + } + + // ----- Look for all path to remove + if ($p_remove_all_dir) { + $v_stored_filename = basename($p_filename); + } + // ----- Look for partial path remove + else if ($p_remove_dir != "") { + if (substr($p_remove_dir, -1) != '/') + $p_remove_dir .= "/"; + + if ( (substr($p_filename, 0, 2) == "./") + || (substr($p_remove_dir, 0, 2) == "./")) { + + if ( (substr($p_filename, 0, 2) == "./") + && (substr($p_remove_dir, 0, 2) != "./")) { + $p_remove_dir = "./".$p_remove_dir; + } + if ( (substr($p_filename, 0, 2) != "./") + && (substr($p_remove_dir, 0, 2) == "./")) { + $p_remove_dir = substr($p_remove_dir, 2); + } + } + + $v_compare = PclZipUtilPathInclusion($p_remove_dir, + $v_stored_filename); + if ($v_compare > 0) { + if ($v_compare == 2) { + $v_stored_filename = ""; + } + else { + $v_stored_filename = substr($v_stored_filename, + strlen($p_remove_dir)); + } + } + } + + // ----- Remove drive letter if any + $v_stored_filename = PclZipUtilTranslateWinPath($v_stored_filename); + + // ----- Look for path to add + if ($p_add_dir != "") { + if (substr($p_add_dir, -1) == "/") + $v_stored_filename = $p_add_dir.$v_stored_filename; + else + $v_stored_filename = $p_add_dir."/".$v_stored_filename; + } + } + + // ----- Filename (reduce the path of stored name) + $v_stored_filename = PclZipUtilPathReduction($v_stored_filename); + $p_filedescr['stored_filename'] = $v_stored_filename; + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privWriteFileHeader() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privWriteFileHeader(&$p_header) + { + $v_result=1; + + // ----- Store the offset position of the file + $p_header['offset'] = ftell($this->zip_fd); + + // ----- Transform UNIX mtime to DOS format mdate/mtime + $v_date = getdate($p_header['mtime']); + $v_mtime = ($v_date['hours']<<11) + ($v_date['minutes']<<5) + $v_date['seconds']/2; + $v_mdate = (($v_date['year']-1980)<<9) + ($v_date['mon']<<5) + $v_date['mday']; + + // ----- Packed data + $v_binary_data = pack("VvvvvvVVVvv", 0x04034b50, + $p_header['version_extracted'], $p_header['flag'], + $p_header['compression'], $v_mtime, $v_mdate, + $p_header['crc'], $p_header['compressed_size'], + $p_header['size'], + strlen($p_header['stored_filename']), + $p_header['extra_len']); + + // ----- Write the first 148 bytes of the header in the archive + fputs($this->zip_fd, $v_binary_data, 30); + + // ----- Write the variable fields + if (strlen($p_header['stored_filename']) != 0) + { + fputs($this->zip_fd, $p_header['stored_filename'], strlen($p_header['stored_filename'])); + } + if ($p_header['extra_len'] != 0) + { + fputs($this->zip_fd, $p_header['extra'], $p_header['extra_len']); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privWriteCentralFileHeader() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privWriteCentralFileHeader(&$p_header) + { + $v_result=1; + + // TBC + //for(reset($p_header); $key = key($p_header); next($p_header)) { + //} + + // ----- Transform UNIX mtime to DOS format mdate/mtime + $v_date = getdate($p_header['mtime']); + $v_mtime = ($v_date['hours']<<11) + ($v_date['minutes']<<5) + $v_date['seconds']/2; + $v_mdate = (($v_date['year']-1980)<<9) + ($v_date['mon']<<5) + $v_date['mday']; + + + // ----- Packed data + $v_binary_data = pack("VvvvvvvVVVvvvvvVV", 0x02014b50, + $p_header['version'], $p_header['version_extracted'], + $p_header['flag'], $p_header['compression'], + $v_mtime, $v_mdate, $p_header['crc'], + $p_header['compressed_size'], $p_header['size'], + strlen($p_header['stored_filename']), + $p_header['extra_len'], $p_header['comment_len'], + $p_header['disk'], $p_header['internal'], + $p_header['external'], $p_header['offset']); + + // ----- Write the 42 bytes of the header in the zip file + fputs($this->zip_fd, $v_binary_data, 46); + + // ----- Write the variable fields + if (strlen($p_header['stored_filename']) != 0) + { + fputs($this->zip_fd, $p_header['stored_filename'], strlen($p_header['stored_filename'])); + } + if ($p_header['extra_len'] != 0) + { + fputs($this->zip_fd, $p_header['extra'], $p_header['extra_len']); + } + if ($p_header['comment_len'] != 0) + { + fputs($this->zip_fd, $p_header['comment'], $p_header['comment_len']); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privWriteCentralHeader() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privWriteCentralHeader($p_nb_entries, $p_size, $p_offset, $p_comment) + { + $v_result=1; + + // ----- Packed data + $v_binary_data = pack("VvvvvVVv", 0x06054b50, 0, 0, $p_nb_entries, + $p_nb_entries, $p_size, + $p_offset, strlen($p_comment)); + + // ----- Write the 22 bytes of the header in the zip file + fputs($this->zip_fd, $v_binary_data, 22); + + // ----- Write the variable fields + if (strlen($p_comment) != 0) + { + fputs($this->zip_fd, $p_comment, strlen($p_comment)); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privList() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privList(&$p_list) + { + $v_result=1; + + // ----- Magic quotes trick + $this->privDisableMagicQuotes(); + + // ----- Open the zip file + if (($this->zip_fd = @fopen($this->zipname, 'rb')) == 0) + { + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in binary read mode'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) + { + $this->privSwapBackMagicQuotes(); + return $v_result; + } + + // ----- Go to beginning of Central Dir + @rewind($this->zip_fd); + if (@fseek($this->zip_fd, $v_central_dir['offset'])) + { + $this->privSwapBackMagicQuotes(); + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read each entry + for ($i=0; $i<$v_central_dir['entries']; $i++) + { + // ----- Read the file header + if (($v_result = $this->privReadCentralFileHeader($v_header)) != 1) + { + $this->privSwapBackMagicQuotes(); + return $v_result; + } + $v_header['index'] = $i; + + // ----- Get the only interesting attributes + $this->privConvertHeader2FileInfo($v_header, $p_list[$i]); + unset($v_header); + } + + // ----- Close the zip file + $this->privCloseFd(); + + // ----- Magic quotes trick + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privConvertHeader2FileInfo() + // Description : + // This function takes the file informations from the central directory + // entries and extract the interesting parameters that will be given back. + // The resulting file infos are set in the array $p_info + // $p_info['filename'] : Filename with full path. Given by user (add), + // extracted in the filesystem (extract). + // $p_info['stored_filename'] : Stored filename in the archive. + // $p_info['size'] = Size of the file. + // $p_info['compressed_size'] = Compressed size of the file. + // $p_info['mtime'] = Last modification date of the file. + // $p_info['comment'] = Comment associated with the file. + // $p_info['folder'] = true/false : indicates if the entry is a folder or not. + // $p_info['status'] = status of the action on the file. + // $p_info['crc'] = CRC of the file content. + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privConvertHeader2FileInfo($p_header, &$p_info) + { + $v_result=1; + + // ----- Get the interesting attributes + $v_temp_path = PclZipUtilPathReduction($p_header['filename']); + $p_info['filename'] = $v_temp_path; + $v_temp_path = PclZipUtilPathReduction($p_header['stored_filename']); + $p_info['stored_filename'] = $v_temp_path; + $p_info['size'] = $p_header['size']; + $p_info['compressed_size'] = $p_header['compressed_size']; + $p_info['mtime'] = $p_header['mtime']; + $p_info['comment'] = $p_header['comment']; + $p_info['folder'] = (($p_header['external']&0x00000010)==0x00000010); + $p_info['index'] = $p_header['index']; + $p_info['status'] = $p_header['status']; + $p_info['crc'] = $p_header['crc']; + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privExtractByRule() + // Description : + // Extract a file or directory depending of rules (by index, by name, ...) + // Parameters : + // $p_file_list : An array where will be placed the properties of each + // extracted file + // $p_path : Path to add while writing the extracted files + // $p_remove_path : Path to remove (from the file memorized path) while writing the + // extracted files. If the path does not match the file path, + // the file is extracted with its memorized path. + // $p_remove_path does not apply to 'list' mode. + // $p_path and $p_remove_path are commulative. + // Return Values : + // 1 on success,0 or less on error (see error code list) + // -------------------------------------------------------------------------------- + function privExtractByRule(&$p_file_list, $p_path, $p_remove_path, $p_remove_all_path, &$p_options) + { + $v_result=1; + + // ----- Magic quotes trick + $this->privDisableMagicQuotes(); + + // ----- Check the path + if ( ($p_path == "") + || ( (substr($p_path, 0, 1) != "/") + && (substr($p_path, 0, 3) != "../") + && (substr($p_path,1,2)!=":/"))) + $p_path = "./".$p_path; + + // ----- Reduce the path last (and duplicated) '/' + if (($p_path != "./") && ($p_path != "/")) + { + // ----- Look for the path end '/' + while (substr($p_path, -1) == "/") + { + $p_path = substr($p_path, 0, strlen($p_path)-1); + } + } + + // ----- Look for path to remove format (should end by /) + if (($p_remove_path != "") && (substr($p_remove_path, -1) != '/')) + { + $p_remove_path .= '/'; + } + $p_remove_path_size = strlen($p_remove_path); + + // ----- Open the zip file + if (($v_result = $this->privOpenFd('rb')) != 1) + { + $this->privSwapBackMagicQuotes(); + return $v_result; + } + + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) + { + // ----- Close the zip file + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + return $v_result; + } + + // ----- Start at beginning of Central Dir + $v_pos_entry = $v_central_dir['offset']; + + // ----- Read each entry + $j_start = 0; + for ($i=0, $v_nb_extracted=0; $i<$v_central_dir['entries']; $i++) + { + + // ----- Read next Central dir entry + @rewind($this->zip_fd); + if (@fseek($this->zip_fd, $v_pos_entry)) + { + // ----- Close the zip file + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read the file header + $v_header = array(); + if (($v_result = $this->privReadCentralFileHeader($v_header)) != 1) + { + // ----- Close the zip file + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + return $v_result; + } + + // ----- Store the index + $v_header['index'] = $i; + + // ----- Store the file position + $v_pos_entry = ftell($this->zip_fd); + + // ----- Look for the specific extract rules + $v_extract = false; + + // ----- Look for extract by name rule + if ( (isset($p_options[PCLZIP_OPT_BY_NAME])) + && ($p_options[PCLZIP_OPT_BY_NAME] != 0)) { + + // ----- Look if the filename is in the list + for ($j=0; ($j strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) + && (substr($v_header['stored_filename'], 0, strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) == $p_options[PCLZIP_OPT_BY_NAME][$j])) { + $v_extract = true; + } + } + // ----- Look for a filename + elseif ($v_header['stored_filename'] == $p_options[PCLZIP_OPT_BY_NAME][$j]) { + $v_extract = true; + } + } + } + + // ----- Look for extract by ereg rule + // ereg() is deprecated with PHP 5.3 + /* + else if ( (isset($p_options[PCLZIP_OPT_BY_EREG])) + && ($p_options[PCLZIP_OPT_BY_EREG] != "")) { + + if (ereg($p_options[PCLZIP_OPT_BY_EREG], $v_header['stored_filename'])) { + $v_extract = true; + } + } + */ + + // ----- Look for extract by preg rule + else if ( (isset($p_options[PCLZIP_OPT_BY_PREG])) + && ($p_options[PCLZIP_OPT_BY_PREG] != "")) { + + if (preg_match($p_options[PCLZIP_OPT_BY_PREG], $v_header['stored_filename'])) { + $v_extract = true; + } + } + + // ----- Look for extract by index rule + else if ( (isset($p_options[PCLZIP_OPT_BY_INDEX])) + && ($p_options[PCLZIP_OPT_BY_INDEX] != 0)) { + + // ----- Look if the index is in the list + for ($j=$j_start; ($j=$p_options[PCLZIP_OPT_BY_INDEX][$j]['start']) && ($i<=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end'])) { + $v_extract = true; + } + if ($i>=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end']) { + $j_start = $j+1; + } + + if ($p_options[PCLZIP_OPT_BY_INDEX][$j]['start']>$i) { + break; + } + } + } + + // ----- Look for no rule, which means extract all the archive + else { + $v_extract = true; + } + + // ----- Check compression method + if ( ($v_extract) + && ( ($v_header['compression'] != 8) + && ($v_header['compression'] != 0))) { + $v_header['status'] = 'unsupported_compression'; + + // ----- Look for PCLZIP_OPT_STOP_ON_ERROR + if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) + && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { + + $this->privSwapBackMagicQuotes(); + + PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_COMPRESSION, + "Filename '".$v_header['stored_filename']."' is " + ."compressed by an unsupported compression " + ."method (".$v_header['compression'].") "); + + return PclZip::errorCode(); + } + } + + // ----- Check encrypted files + if (($v_extract) && (($v_header['flag'] & 1) == 1)) { + $v_header['status'] = 'unsupported_encryption'; + + // ----- Look for PCLZIP_OPT_STOP_ON_ERROR + if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) + && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { + + $this->privSwapBackMagicQuotes(); + + PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_ENCRYPTION, + "Unsupported encryption for " + ." filename '".$v_header['stored_filename'] + ."'"); + + return PclZip::errorCode(); + } + } + + // ----- Look for real extraction + if (($v_extract) && ($v_header['status'] != 'ok')) { + $v_result = $this->privConvertHeader2FileInfo($v_header, + $p_file_list[$v_nb_extracted++]); + if ($v_result != 1) { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + return $v_result; + } + + $v_extract = false; + } + + // ----- Look for real extraction + if ($v_extract) + { + + // ----- Go to the file position + @rewind($this->zip_fd); + if (@fseek($this->zip_fd, $v_header['offset'])) + { + // ----- Close the zip file + $this->privCloseFd(); + + $this->privSwapBackMagicQuotes(); + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Look for extraction as string + if ($p_options[PCLZIP_OPT_EXTRACT_AS_STRING]) { + + $v_string = ''; + + // ----- Extracting the file + $v_result1 = $this->privExtractFileAsString($v_header, $v_string, $p_options); + if ($v_result1 < 1) { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + return $v_result1; + } + + // ----- Get the only interesting attributes + if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted])) != 1) + { + // ----- Close the zip file + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + return $v_result; + } + + // ----- Set the file content + $p_file_list[$v_nb_extracted]['content'] = $v_string; + + // ----- Next extracted file + $v_nb_extracted++; + + // ----- Look for user callback abort + if ($v_result1 == 2) { + break; + } + } + // ----- Look for extraction in standard output + elseif ( (isset($p_options[PCLZIP_OPT_EXTRACT_IN_OUTPUT])) + && ($p_options[PCLZIP_OPT_EXTRACT_IN_OUTPUT])) { + // ----- Extracting the file in standard output + $v_result1 = $this->privExtractFileInOutput($v_header, $p_options); + if ($v_result1 < 1) { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + return $v_result1; + } + + // ----- Get the only interesting attributes + if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++])) != 1) { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + return $v_result; + } + + // ----- Look for user callback abort + if ($v_result1 == 2) { + break; + } + } + // ----- Look for normal extraction + else { + // ----- Extracting the file + $v_result1 = $this->privExtractFile($v_header, + $p_path, $p_remove_path, + $p_remove_all_path, + $p_options); + if ($v_result1 < 1) { + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + return $v_result1; + } + + // ----- Get the only interesting attributes + if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++])) != 1) + { + // ----- Close the zip file + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + return $v_result; + } + + // ----- Look for user callback abort + if ($v_result1 == 2) { + break; + } + } + } + } + + // ----- Close the zip file + $this->privCloseFd(); + $this->privSwapBackMagicQuotes(); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privExtractFile() + // Description : + // Parameters : + // Return Values : + // + // 1 : ... ? + // PCLZIP_ERR_USER_ABORTED(2) : User ask for extraction stop in callback + // -------------------------------------------------------------------------------- + function privExtractFile(&$p_entry, $p_path, $p_remove_path, $p_remove_all_path, &$p_options) + { + $v_result=1; + + // ----- Read the file header + if (($v_result = $this->privReadFileHeader($v_header)) != 1) + { + // ----- Return + return $v_result; + } + + + // ----- Check that the file header is coherent with $p_entry info + if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) { + // TBC + } + + // ----- Look for all path to remove + if ($p_remove_all_path == true) { + // ----- Look for folder entry that not need to be extracted + if (($p_entry['external']&0x00000010)==0x00000010) { + + $p_entry['status'] = "filtered"; + + return $v_result; + } + + // ----- Get the basename of the path + $p_entry['filename'] = basename($p_entry['filename']); + } + + // ----- Look for path to remove + else if ($p_remove_path != "") + { + if (PclZipUtilPathInclusion($p_remove_path, $p_entry['filename']) == 2) + { + + // ----- Change the file status + $p_entry['status'] = "filtered"; + + // ----- Return + return $v_result; + } + + $p_remove_path_size = strlen($p_remove_path); + if (substr($p_entry['filename'], 0, $p_remove_path_size) == $p_remove_path) + { + + // ----- Remove the path + $p_entry['filename'] = substr($p_entry['filename'], $p_remove_path_size); + + } + } + + // ----- Add the path + if ($p_path != '') { + $p_entry['filename'] = $p_path."/".$p_entry['filename']; + } + + // ----- Check a base_dir_restriction + if (isset($p_options[PCLZIP_OPT_EXTRACT_DIR_RESTRICTION])) { + $v_inclusion + = PclZipUtilPathInclusion($p_options[PCLZIP_OPT_EXTRACT_DIR_RESTRICTION], + $p_entry['filename']); + if ($v_inclusion == 0) { + + PclZip::privErrorLog(PCLZIP_ERR_DIRECTORY_RESTRICTION, + "Filename '".$p_entry['filename']."' is " + ."outside PCLZIP_OPT_EXTRACT_DIR_RESTRICTION"); + + return PclZip::errorCode(); + } + } + + // ----- Look for pre-extract callback + if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_entry, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. + $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header); + if ($v_result == 0) { + // ----- Change the file status + $p_entry['status'] = "skipped"; + $v_result = 1; + } + + // ----- Look for abort result + if ($v_result == 2) { + // ----- This status is internal and will be changed in 'skipped' + $p_entry['status'] = "aborted"; + $v_result = PCLZIP_ERR_USER_ABORTED; + } + + // ----- Update the informations + // Only some fields can be modified + $p_entry['filename'] = $v_local_header['filename']; + } + + + // ----- Look if extraction should be done + if ($p_entry['status'] == 'ok') { + + // ----- Look for specific actions while the file exist + if (file_exists($p_entry['filename'])) + { + + // ----- Look if file is a directory + if (is_dir($p_entry['filename'])) + { + + // ----- Change the file status + $p_entry['status'] = "already_a_directory"; + + // ----- Look for PCLZIP_OPT_STOP_ON_ERROR + // For historical reason first PclZip implementation does not stop + // when this kind of error occurs. + if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) + && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { + + PclZip::privErrorLog(PCLZIP_ERR_ALREADY_A_DIRECTORY, + "Filename '".$p_entry['filename']."' is " + ."already used by an existing directory"); + + return PclZip::errorCode(); + } + } + // ----- Look if file is write protected + else if (!is_writeable($p_entry['filename'])) + { + + // ----- Change the file status + $p_entry['status'] = "write_protected"; + + // ----- Look for PCLZIP_OPT_STOP_ON_ERROR + // For historical reason first PclZip implementation does not stop + // when this kind of error occurs. + if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) + && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { + + PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, + "Filename '".$p_entry['filename']."' exists " + ."and is write protected"); + + return PclZip::errorCode(); + } + } + + // ----- Look if the extracted file is older + else if (filemtime($p_entry['filename']) > $p_entry['mtime']) + { + // ----- Change the file status + if ( (isset($p_options[PCLZIP_OPT_REPLACE_NEWER])) + && ($p_options[PCLZIP_OPT_REPLACE_NEWER]===true)) { + } + else { + $p_entry['status'] = "newer_exist"; + + // ----- Look for PCLZIP_OPT_STOP_ON_ERROR + // For historical reason first PclZip implementation does not stop + // when this kind of error occurs. + if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) + && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { + + PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, + "Newer version of '".$p_entry['filename']."' exists " + ."and option PCLZIP_OPT_REPLACE_NEWER is not selected"); + + return PclZip::errorCode(); + } + } + } + else { + } + } + + // ----- Check the directory availability and create it if necessary + else { + if ((($p_entry['external']&0x00000010)==0x00000010) || (substr($p_entry['filename'], -1) == '/')) + $v_dir_to_check = $p_entry['filename']; + else if (!strstr($p_entry['filename'], "/")) + $v_dir_to_check = ""; + else + $v_dir_to_check = dirname($p_entry['filename']); + + if (($v_result = $this->privDirCheck($v_dir_to_check, (($p_entry['external']&0x00000010)==0x00000010))) != 1) { + + // ----- Change the file status + $p_entry['status'] = "path_creation_fail"; + + // ----- Return + //return $v_result; + $v_result = 1; + } + } + } + + // ----- Look if extraction should be done + if ($p_entry['status'] == 'ok') { + + // ----- Do the extraction (if not a folder) + if (!(($p_entry['external']&0x00000010)==0x00000010)) + { + // ----- Look for not compressed file + if ($p_entry['compression'] == 0) { + + // ----- Opening destination file + if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) + { + + // ----- Change the file status + $p_entry['status'] = "write_error"; + + // ----- Return + return $v_result; + } + + + // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks + $v_size = $p_entry['compressed_size']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($this->zip_fd, $v_read_size); + /* Try to speed up the code + $v_binary_data = pack('a'.$v_read_size, $v_buffer); + @fwrite($v_dest_file, $v_binary_data, $v_read_size); + */ + @fwrite($v_dest_file, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Closing the destination file + fclose($v_dest_file); + + // ----- Change the file mtime + touch($p_entry['filename'], $p_entry['mtime']); + + + } + else { + // ----- TBC + // Need to be finished + if (($p_entry['flag'] & 1) == 1) { + PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_ENCRYPTION, 'File \''.$p_entry['filename'].'\' is encrypted. Encrypted files are not supported.'); + return PclZip::errorCode(); + } + + + // ----- Look for using temporary file to unzip + if ( (!isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) + && (isset($p_options[PCLZIP_OPT_TEMP_FILE_ON]) + || (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]) + && ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] <= $p_entry['size'])) ) ) { + $v_result = $this->privExtractFileUsingTempFile($p_entry, $p_options); + if ($v_result < PCLZIP_ERR_NO_ERROR) { + return $v_result; + } + } + + // ----- Look for extract in memory + else { + + + // ----- Read the compressed file in a buffer (one shot) + $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']); + + // ----- Decompress the file + $v_file_content = @gzinflate($v_buffer); + unset($v_buffer); + if ($v_file_content === FALSE) { + + // ----- Change the file status + // TBC + $p_entry['status'] = "error"; + + return $v_result; + } + + // ----- Opening destination file + if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) { + + // ----- Change the file status + $p_entry['status'] = "write_error"; + + return $v_result; + } + + // ----- Write the uncompressed data + @fwrite($v_dest_file, $v_file_content, $p_entry['size']); + unset($v_file_content); + + // ----- Closing the destination file + @fclose($v_dest_file); + + } + + // ----- Change the file mtime + @touch($p_entry['filename'], $p_entry['mtime']); + } + + // ----- Look for chmod option + if (isset($p_options[PCLZIP_OPT_SET_CHMOD])) { + + // ----- Change the mode of the file + @chmod($p_entry['filename'], $p_options[PCLZIP_OPT_SET_CHMOD]); + } + + } + } + + // ----- Change abort status + if ($p_entry['status'] == "aborted") { + $p_entry['status'] = "skipped"; + } + + // ----- Look for post-extract callback + elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_entry, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. + $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header); + + // ----- Look for abort result + if ($v_result == 2) { + $v_result = PCLZIP_ERR_USER_ABORTED; + } + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privExtractFileUsingTempFile() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privExtractFileUsingTempFile(&$p_entry, &$p_options) + { + $v_result=1; + + // ----- Creates a temporary file + $v_gzip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.gz'; + if (($v_dest_file = @fopen($v_gzip_temp_name, "wb")) == 0) { + fclose($v_file); + PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary write mode'); + return PclZip::errorCode(); + } + + + // ----- Write gz file format header + $v_binary_data = pack('va1a1Va1a1', 0x8b1f, Chr($p_entry['compression']), Chr(0x00), time(), Chr(0x00), Chr(3)); + @fwrite($v_dest_file, $v_binary_data, 10); + + // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks + $v_size = $p_entry['compressed_size']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($this->zip_fd, $v_read_size); + //$v_binary_data = pack('a'.$v_read_size, $v_buffer); + @fwrite($v_dest_file, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Write gz file format footer + $v_binary_data = pack('VV', $p_entry['crc'], $p_entry['size']); + @fwrite($v_dest_file, $v_binary_data, 8); + + // ----- Close the temporary file + @fclose($v_dest_file); + + // ----- Opening destination file + if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) { + $p_entry['status'] = "write_error"; + return $v_result; + } + + // ----- Open the temporary gz file + if (($v_src_file = @gzopen($v_gzip_temp_name, 'rb')) == 0) { + @fclose($v_dest_file); + $p_entry['status'] = "read_error"; + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary read mode'); + return PclZip::errorCode(); + } + + + // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks + $v_size = $p_entry['size']; + while ($v_size != 0) { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @gzread($v_src_file, $v_read_size); + //$v_binary_data = pack('a'.$v_read_size, $v_buffer); + @fwrite($v_dest_file, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + @fclose($v_dest_file); + @gzclose($v_src_file); + + // ----- Delete the temporary file + @unlink($v_gzip_temp_name); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privExtractFileInOutput() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privExtractFileInOutput(&$p_entry, &$p_options) + { + $v_result=1; + + // ----- Read the file header + if (($v_result = $this->privReadFileHeader($v_header)) != 1) { + return $v_result; + } + + + // ----- Check that the file header is coherent with $p_entry info + if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) { + // TBC + } + + // ----- Look for pre-extract callback + if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_entry, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. +// eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);'); + $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header); + if ($v_result == 0) { + // ----- Change the file status + $p_entry['status'] = "skipped"; + $v_result = 1; + } + + // ----- Look for abort result + if ($v_result == 2) { + // ----- This status is internal and will be changed in 'skipped' + $p_entry['status'] = "aborted"; + $v_result = PCLZIP_ERR_USER_ABORTED; + } + + // ----- Update the informations + // Only some fields can be modified + $p_entry['filename'] = $v_local_header['filename']; + } + + // ----- Trace + + // ----- Look if extraction should be done + if ($p_entry['status'] == 'ok') { + + // ----- Do the extraction (if not a folder) + if (!(($p_entry['external']&0x00000010)==0x00000010)) { + // ----- Look for not compressed file + if ($p_entry['compressed_size'] == $p_entry['size']) { + + // ----- Read the file in a buffer (one shot) + $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']); + + // ----- Send the file to the output + echo $v_buffer; + unset($v_buffer); + } + else { + + // ----- Read the compressed file in a buffer (one shot) + $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']); + + // ----- Decompress the file + $v_file_content = gzinflate($v_buffer); + unset($v_buffer); + + // ----- Send the file to the output + echo $v_file_content; + unset($v_file_content); + } + } + } + + // ----- Change abort status + if ($p_entry['status'] == "aborted") { + $p_entry['status'] = "skipped"; + } + + // ----- Look for post-extract callback + elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_entry, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. + $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header); + + // ----- Look for abort result + if ($v_result == 2) { + $v_result = PCLZIP_ERR_USER_ABORTED; + } + } + + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privExtractFileAsString() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privExtractFileAsString(&$p_entry, &$p_string, &$p_options) + { + $v_result=1; + + // ----- Read the file header + $v_header = array(); + if (($v_result = $this->privReadFileHeader($v_header)) != 1) + { + // ----- Return + return $v_result; + } + + + // ----- Check that the file header is coherent with $p_entry info + if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) { + // TBC + } + + // ----- Look for pre-extract callback + if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_entry, $v_local_header); + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. + $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header); + if ($v_result == 0) { + // ----- Change the file status + $p_entry['status'] = "skipped"; + $v_result = 1; + } + + // ----- Look for abort result + if ($v_result == 2) { + // ----- This status is internal and will be changed in 'skipped' + $p_entry['status'] = "aborted"; + $v_result = PCLZIP_ERR_USER_ABORTED; + } + + // ----- Update the informations + // Only some fields can be modified + $p_entry['filename'] = $v_local_header['filename']; + } + + + // ----- Look if extraction should be done + if ($p_entry['status'] == 'ok') { + + // ----- Do the extraction (if not a folder) + if (!(($p_entry['external']&0x00000010)==0x00000010)) { + // ----- Look for not compressed file + // if ($p_entry['compressed_size'] == $p_entry['size']) + if ($p_entry['compression'] == 0) { + + // ----- Reading the file + $p_string = @fread($this->zip_fd, $p_entry['compressed_size']); + } + else { + + // ----- Reading the file + $v_data = @fread($this->zip_fd, $p_entry['compressed_size']); + + // ----- Decompress the file + if (($p_string = @gzinflate($v_data)) === FALSE) { + // TBC + } + } + + // ----- Trace + } + else { + // TBC : error : can not extract a folder in a string + } + + } + + // ----- Change abort status + if ($p_entry['status'] == "aborted") { + $p_entry['status'] = "skipped"; + } + + // ----- Look for post-extract callback + elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) { + + // ----- Generate a local information + $v_local_header = array(); + $this->privConvertHeader2FileInfo($p_entry, $v_local_header); + + // ----- Swap the content to header + $v_local_header['content'] = $p_string; + $p_string = ''; + + // ----- Call the callback + // Here I do not use call_user_func() because I need to send a reference to the + // header. + $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header); + + // ----- Swap back the content to header + $p_string = $v_local_header['content']; + unset($v_local_header['content']); + + // ----- Look for abort result + if ($v_result == 2) { + $v_result = PCLZIP_ERR_USER_ABORTED; + } + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privReadFileHeader() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privReadFileHeader(&$p_header) + { + $v_result=1; + + // ----- Read the 4 bytes signature + $v_binary_data = @fread($this->zip_fd, 4); + $v_data = unpack('Vid', $v_binary_data); + + // ----- Check signature + if ($v_data['id'] != 0x04034b50) + { + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Invalid archive structure'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read the first 42 bytes of the header + $v_binary_data = fread($this->zip_fd, 26); + + // ----- Look for invalid block size + if (strlen($v_binary_data) != 26) + { + $p_header['filename'] = ""; + $p_header['status'] = "invalid_header"; + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data)); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Extract the values + $v_data = unpack('vversion/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len', $v_binary_data); + + // ----- Get filename + $p_header['filename'] = fread($this->zip_fd, $v_data['filename_len']); + + // ----- Get extra_fields + if ($v_data['extra_len'] != 0) { + $p_header['extra'] = fread($this->zip_fd, $v_data['extra_len']); + } + else { + $p_header['extra'] = ''; + } + + // ----- Extract properties + $p_header['version_extracted'] = $v_data['version']; + $p_header['compression'] = $v_data['compression']; + $p_header['size'] = $v_data['size']; + $p_header['compressed_size'] = $v_data['compressed_size']; + $p_header['crc'] = $v_data['crc']; + $p_header['flag'] = $v_data['flag']; + $p_header['filename_len'] = $v_data['filename_len']; + + // ----- Recuperate date in UNIX format + $p_header['mdate'] = $v_data['mdate']; + $p_header['mtime'] = $v_data['mtime']; + if ($p_header['mdate'] && $p_header['mtime']) + { + // ----- Extract time + $v_hour = ($p_header['mtime'] & 0xF800) >> 11; + $v_minute = ($p_header['mtime'] & 0x07E0) >> 5; + $v_seconde = ($p_header['mtime'] & 0x001F)*2; + + // ----- Extract date + $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980; + $v_month = ($p_header['mdate'] & 0x01E0) >> 5; + $v_day = $p_header['mdate'] & 0x001F; + + // ----- Get UNIX date format + $p_header['mtime'] = @mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year); + + } + else + { + $p_header['mtime'] = time(); + } + + // TBC + //for(reset($v_data); $key = key($v_data); next($v_data)) { + //} + + // ----- Set the stored filename + $p_header['stored_filename'] = $p_header['filename']; + + // ----- Set the status field + $p_header['status'] = "ok"; + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privReadCentralFileHeader() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privReadCentralFileHeader(&$p_header) + { + $v_result=1; + + // ----- Read the 4 bytes signature + $v_binary_data = @fread($this->zip_fd, 4); + $v_data = unpack('Vid', $v_binary_data); + + // ----- Check signature + if ($v_data['id'] != 0x02014b50) + { + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Invalid archive structure'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read the first 42 bytes of the header + $v_binary_data = fread($this->zip_fd, 42); + + // ----- Look for invalid block size + if (strlen($v_binary_data) != 42) + { + $p_header['filename'] = ""; + $p_header['status'] = "invalid_header"; + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data)); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Extract the values + $p_header = unpack('vversion/vversion_extracted/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len/vcomment_len/vdisk/vinternal/Vexternal/Voffset', $v_binary_data); + + // ----- Get filename + if ($p_header['filename_len'] != 0) + $p_header['filename'] = fread($this->zip_fd, $p_header['filename_len']); + else + $p_header['filename'] = ''; + + // ----- Get extra + if ($p_header['extra_len'] != 0) + $p_header['extra'] = fread($this->zip_fd, $p_header['extra_len']); + else + $p_header['extra'] = ''; + + // ----- Get comment + if ($p_header['comment_len'] != 0) + $p_header['comment'] = fread($this->zip_fd, $p_header['comment_len']); + else + $p_header['comment'] = ''; + + // ----- Extract properties + + // ----- Recuperate date in UNIX format + //if ($p_header['mdate'] && $p_header['mtime']) + // TBC : bug : this was ignoring time with 0/0/0 + if (1) + { + // ----- Extract time + $v_hour = ($p_header['mtime'] & 0xF800) >> 11; + $v_minute = ($p_header['mtime'] & 0x07E0) >> 5; + $v_seconde = ($p_header['mtime'] & 0x001F)*2; + + // ----- Extract date + $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980; + $v_month = ($p_header['mdate'] & 0x01E0) >> 5; + $v_day = $p_header['mdate'] & 0x001F; + + // ----- Get UNIX date format + $p_header['mtime'] = @mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year); + + } + else + { + $p_header['mtime'] = time(); + } + + // ----- Set the stored filename + $p_header['stored_filename'] = $p_header['filename']; + + // ----- Set default status to ok + $p_header['status'] = 'ok'; + + // ----- Look if it is a directory + if (substr($p_header['filename'], -1) == '/') { + //$p_header['external'] = 0x41FF0010; + $p_header['external'] = 0x00000010; + } + + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privCheckFileHeaders() + // Description : + // Parameters : + // Return Values : + // 1 on success, + // 0 on error; + // -------------------------------------------------------------------------------- + function privCheckFileHeaders(&$p_local_header, &$p_central_header) + { + $v_result=1; + + // ----- Check the static values + // TBC + if ($p_local_header['filename'] != $p_central_header['filename']) { + } + if ($p_local_header['version_extracted'] != $p_central_header['version_extracted']) { + } + if ($p_local_header['flag'] != $p_central_header['flag']) { + } + if ($p_local_header['compression'] != $p_central_header['compression']) { + } + if ($p_local_header['mtime'] != $p_central_header['mtime']) { + } + if ($p_local_header['filename_len'] != $p_central_header['filename_len']) { + } + + // ----- Look for flag bit 3 + if (($p_local_header['flag'] & 8) == 8) { + $p_local_header['size'] = $p_central_header['size']; + $p_local_header['compressed_size'] = $p_central_header['compressed_size']; + $p_local_header['crc'] = $p_central_header['crc']; + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privReadEndCentralDir() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privReadEndCentralDir(&$p_central_dir) + { + $v_result=1; + + // ----- Go to the end of the zip file + $v_size = filesize($this->zipname); + @fseek($this->zip_fd, $v_size); + if (@ftell($this->zip_fd) != $v_size) + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to go to the end of the archive \''.$this->zipname.'\''); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- First try : look if this is an archive with no commentaries (most of the time) + // in this case the end of central dir is at 22 bytes of the file end + $v_found = 0; + if ($v_size > 26) { + @fseek($this->zip_fd, $v_size-22); + if (($v_pos = @ftell($this->zip_fd)) != ($v_size-22)) + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to seek back to the middle of the archive \''.$this->zipname.'\''); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read for bytes + $v_binary_data = @fread($this->zip_fd, 4); + $v_data = @unpack('Vid', $v_binary_data); + + // ----- Check signature + if ($v_data['id'] == 0x06054b50) { + $v_found = 1; + } + + $v_pos = ftell($this->zip_fd); + } + + // ----- Go back to the maximum possible size of the Central Dir End Record + if (!$v_found) { + $v_maximum_size = 65557; // 0xFFFF + 22; + if ($v_maximum_size > $v_size) + $v_maximum_size = $v_size; + @fseek($this->zip_fd, $v_size-$v_maximum_size); + if (@ftell($this->zip_fd) != ($v_size-$v_maximum_size)) + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to seek back to the middle of the archive \''.$this->zipname.'\''); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read byte per byte in order to find the signature + $v_pos = ftell($this->zip_fd); + $v_bytes = 0x00000000; + while ($v_pos < $v_size) + { + // ----- Read a byte + $v_byte = @fread($this->zip_fd, 1); + + // ----- Add the byte + //$v_bytes = ($v_bytes << 8) | Ord($v_byte); + // Note we mask the old value down such that once shifted we can never end up with more than a 32bit number + // Otherwise on systems where we have 64bit integers the check below for the magic number will fail. + $v_bytes = ( ($v_bytes & 0xFFFFFF) << 8) | Ord($v_byte); + + // ----- Compare the bytes + if ($v_bytes == 0x504b0506) + { + $v_pos++; + break; + } + + $v_pos++; + } + + // ----- Look if not found end of central dir + if ($v_pos == $v_size) + { + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Unable to find End of Central Dir Record signature"); + + // ----- Return + return PclZip::errorCode(); + } + } + + // ----- Read the first 18 bytes of the header + $v_binary_data = fread($this->zip_fd, 18); + + // ----- Look for invalid block size + if (strlen($v_binary_data) != 18) + { + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid End of Central Dir Record size : ".strlen($v_binary_data)); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Extract the values + $v_data = unpack('vdisk/vdisk_start/vdisk_entries/ventries/Vsize/Voffset/vcomment_size', $v_binary_data); + + // ----- Check the global size + if (($v_pos + $v_data['comment_size'] + 18) != $v_size) { + + // ----- Removed in release 2.2 see readme file + // The check of the file size is a little too strict. + // Some bugs where found when a zip is encrypted/decrypted with 'crypt'. + // While decrypted, zip has training 0 bytes + if (0) { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, + 'The central dir is not at the end of the archive.' + .' Some trailing bytes exists after the archive.'); + + // ----- Return + return PclZip::errorCode(); + } + } + + // ----- Get comment + if ($v_data['comment_size'] != 0) { + $p_central_dir['comment'] = fread($this->zip_fd, $v_data['comment_size']); + } + else + $p_central_dir['comment'] = ''; + + $p_central_dir['entries'] = $v_data['entries']; + $p_central_dir['disk_entries'] = $v_data['disk_entries']; + $p_central_dir['offset'] = $v_data['offset']; + $p_central_dir['size'] = $v_data['size']; + $p_central_dir['disk'] = $v_data['disk']; + $p_central_dir['disk_start'] = $v_data['disk_start']; + + // TBC + //for(reset($p_central_dir); $key = key($p_central_dir); next($p_central_dir)) { + //} + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privDeleteByRule() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privDeleteByRule(&$p_result_list, &$p_options) + { + $v_result=1; + $v_list_detail = array(); + + // ----- Open the zip file + if (($v_result=$this->privOpenFd('rb')) != 1) + { + // ----- Return + return $v_result; + } + + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) + { + $this->privCloseFd(); + return $v_result; + } + + // ----- Go to beginning of File + @rewind($this->zip_fd); + + // ----- Scan all the files + // ----- Start at beginning of Central Dir + $v_pos_entry = $v_central_dir['offset']; + @rewind($this->zip_fd); + if (@fseek($this->zip_fd, $v_pos_entry)) + { + // ----- Close the zip file + $this->privCloseFd(); + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read each entry + $v_header_list = array(); + $j_start = 0; + for ($i=0, $v_nb_extracted=0; $i<$v_central_dir['entries']; $i++) + { + + // ----- Read the file header + $v_header_list[$v_nb_extracted] = array(); + if (($v_result = $this->privReadCentralFileHeader($v_header_list[$v_nb_extracted])) != 1) + { + // ----- Close the zip file + $this->privCloseFd(); + + return $v_result; + } + + + // ----- Store the index + $v_header_list[$v_nb_extracted]['index'] = $i; + + // ----- Look for the specific extract rules + $v_found = false; + + // ----- Look for extract by name rule + if ( (isset($p_options[PCLZIP_OPT_BY_NAME])) + && ($p_options[PCLZIP_OPT_BY_NAME] != 0)) { + + // ----- Look if the filename is in the list + for ($j=0; ($j strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) + && (substr($v_header_list[$v_nb_extracted]['stored_filename'], 0, strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) == $p_options[PCLZIP_OPT_BY_NAME][$j])) { + $v_found = true; + } + elseif ( (($v_header_list[$v_nb_extracted]['external']&0x00000010)==0x00000010) /* Indicates a folder */ + && ($v_header_list[$v_nb_extracted]['stored_filename'].'/' == $p_options[PCLZIP_OPT_BY_NAME][$j])) { + $v_found = true; + } + } + // ----- Look for a filename + elseif ($v_header_list[$v_nb_extracted]['stored_filename'] == $p_options[PCLZIP_OPT_BY_NAME][$j]) { + $v_found = true; + } + } + } + + // ----- Look for extract by ereg rule + // ereg() is deprecated with PHP 5.3 + /* + else if ( (isset($p_options[PCLZIP_OPT_BY_EREG])) + && ($p_options[PCLZIP_OPT_BY_EREG] != "")) { + + if (ereg($p_options[PCLZIP_OPT_BY_EREG], $v_header_list[$v_nb_extracted]['stored_filename'])) { + $v_found = true; + } + } + */ + + // ----- Look for extract by preg rule + else if ( (isset($p_options[PCLZIP_OPT_BY_PREG])) + && ($p_options[PCLZIP_OPT_BY_PREG] != "")) { + + if (preg_match($p_options[PCLZIP_OPT_BY_PREG], $v_header_list[$v_nb_extracted]['stored_filename'])) { + $v_found = true; + } + } + + // ----- Look for extract by index rule + else if ( (isset($p_options[PCLZIP_OPT_BY_INDEX])) + && ($p_options[PCLZIP_OPT_BY_INDEX] != 0)) { + + // ----- Look if the index is in the list + for ($j=$j_start; ($j=$p_options[PCLZIP_OPT_BY_INDEX][$j]['start']) && ($i<=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end'])) { + $v_found = true; + } + if ($i>=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end']) { + $j_start = $j+1; + } + + if ($p_options[PCLZIP_OPT_BY_INDEX][$j]['start']>$i) { + break; + } + } + } + else { + $v_found = true; + } + + // ----- Look for deletion + if ($v_found) + { + unset($v_header_list[$v_nb_extracted]); + } + else + { + $v_nb_extracted++; + } + } + + // ----- Look if something need to be deleted + if ($v_nb_extracted > 0) { + + // ----- Creates a temporay file + $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp'; + + // ----- Creates a temporary zip archive + $v_temp_zip = new PclZip($v_zip_temp_name); + + // ----- Open the temporary zip file in write mode + if (($v_result = $v_temp_zip->privOpenFd('wb')) != 1) { + $this->privCloseFd(); + + // ----- Return + return $v_result; + } + + // ----- Look which file need to be kept + for ($i=0; $izip_fd); + if (@fseek($this->zip_fd, $v_header_list[$i]['offset'])) { + // ----- Close the zip file + $this->privCloseFd(); + $v_temp_zip->privCloseFd(); + @unlink($v_zip_temp_name); + + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Read the file header + $v_local_header = array(); + if (($v_result = $this->privReadFileHeader($v_local_header)) != 1) { + // ----- Close the zip file + $this->privCloseFd(); + $v_temp_zip->privCloseFd(); + @unlink($v_zip_temp_name); + + // ----- Return + return $v_result; + } + + // ----- Check that local file header is same as central file header + if ($this->privCheckFileHeaders($v_local_header, + $v_header_list[$i]) != 1) { + // TBC + } + unset($v_local_header); + + // ----- Write the file header + if (($v_result = $v_temp_zip->privWriteFileHeader($v_header_list[$i])) != 1) { + // ----- Close the zip file + $this->privCloseFd(); + $v_temp_zip->privCloseFd(); + @unlink($v_zip_temp_name); + + // ----- Return + return $v_result; + } + + // ----- Read/write the data block + if (($v_result = PclZipUtilCopyBlock($this->zip_fd, $v_temp_zip->zip_fd, $v_header_list[$i]['compressed_size'])) != 1) { + // ----- Close the zip file + $this->privCloseFd(); + $v_temp_zip->privCloseFd(); + @unlink($v_zip_temp_name); + + // ----- Return + return $v_result; + } + } + + // ----- Store the offset of the central dir + $v_offset = @ftell($v_temp_zip->zip_fd); + + // ----- Re-Create the Central Dir files header + for ($i=0; $iprivWriteCentralFileHeader($v_header_list[$i])) != 1) { + $v_temp_zip->privCloseFd(); + $this->privCloseFd(); + @unlink($v_zip_temp_name); + + // ----- Return + return $v_result; + } + + // ----- Transform the header to a 'usable' info + $v_temp_zip->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); + } + + + // ----- Zip file comment + $v_comment = ''; + if (isset($p_options[PCLZIP_OPT_COMMENT])) { + $v_comment = $p_options[PCLZIP_OPT_COMMENT]; + } + + // ----- Calculate the size of the central header + $v_size = @ftell($v_temp_zip->zip_fd)-$v_offset; + + // ----- Create the central dir footer + if (($v_result = $v_temp_zip->privWriteCentralHeader(sizeof($v_header_list), $v_size, $v_offset, $v_comment)) != 1) { + // ----- Reset the file list + unset($v_header_list); + $v_temp_zip->privCloseFd(); + $this->privCloseFd(); + @unlink($v_zip_temp_name); + + // ----- Return + return $v_result; + } + + // ----- Close + $v_temp_zip->privCloseFd(); + $this->privCloseFd(); + + // ----- Delete the zip file + // TBC : I should test the result ... + @unlink($this->zipname); + + // ----- Rename the temporary file + // TBC : I should test the result ... + //@rename($v_zip_temp_name, $this->zipname); + PclZipUtilRename($v_zip_temp_name, $this->zipname); + + // ----- Destroy the temporary archive + unset($v_temp_zip); + } + + // ----- Remove every files : reset the file + else if ($v_central_dir['entries'] != 0) { + $this->privCloseFd(); + + if (($v_result = $this->privOpenFd('wb')) != 1) { + return $v_result; + } + + if (($v_result = $this->privWriteCentralHeader(0, 0, 0, '')) != 1) { + return $v_result; + } + + $this->privCloseFd(); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privDirCheck() + // Description : + // Check if a directory exists, if not it creates it and all the parents directory + // which may be useful. + // Parameters : + // $p_dir : Directory path to check. + // Return Values : + // 1 : OK + // -1 : Unable to create directory + // -------------------------------------------------------------------------------- + function privDirCheck($p_dir, $p_is_dir=false) + { + $v_result = 1; + + + // ----- Remove the final '/' + if (($p_is_dir) && (substr($p_dir, -1)=='/')) + { + $p_dir = substr($p_dir, 0, strlen($p_dir)-1); + } + + // ----- Check the directory availability + if ((is_dir($p_dir)) || ($p_dir == "")) + { + return 1; + } + + // ----- Extract parent directory + $p_parent_dir = dirname($p_dir); + + // ----- Just a check + if ($p_parent_dir != $p_dir) + { + // ----- Look for parent directory + if ($p_parent_dir != "") + { + if (($v_result = $this->privDirCheck($p_parent_dir)) != 1) + { + return $v_result; + } + } + } + + // ----- Create the directory + if (!@mkdir($p_dir, 0777)) + { + // ----- Error log + PclZip::privErrorLog(PCLZIP_ERR_DIR_CREATE_FAIL, "Unable to create directory '$p_dir'"); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privMerge() + // Description : + // If $p_archive_to_add does not exist, the function exit with a success result. + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privMerge(&$p_archive_to_add) + { + $v_result=1; + + // ----- Look if the archive_to_add exists + if (!is_file($p_archive_to_add->zipname)) + { + + // ----- Nothing to merge, so merge is a success + $v_result = 1; + + // ----- Return + return $v_result; + } + + // ----- Look if the archive exists + if (!is_file($this->zipname)) + { + + // ----- Do a duplicate + $v_result = $this->privDuplicate($p_archive_to_add->zipname); + + // ----- Return + return $v_result; + } + + // ----- Open the zip file + if (($v_result=$this->privOpenFd('rb')) != 1) + { + // ----- Return + return $v_result; + } + + // ----- Read the central directory informations + $v_central_dir = array(); + if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) + { + $this->privCloseFd(); + return $v_result; + } + + // ----- Go to beginning of File + @rewind($this->zip_fd); + + // ----- Open the archive_to_add file + if (($v_result=$p_archive_to_add->privOpenFd('rb')) != 1) + { + $this->privCloseFd(); + + // ----- Return + return $v_result; + } + + // ----- Read the central directory informations + $v_central_dir_to_add = array(); + if (($v_result = $p_archive_to_add->privReadEndCentralDir($v_central_dir_to_add)) != 1) + { + $this->privCloseFd(); + $p_archive_to_add->privCloseFd(); + + return $v_result; + } + + // ----- Go to beginning of File + @rewind($p_archive_to_add->zip_fd); + + // ----- Creates a temporay file + $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp'; + + // ----- Open the temporary file in write mode + if (($v_zip_temp_fd = @fopen($v_zip_temp_name, 'wb')) == 0) + { + $this->privCloseFd(); + $p_archive_to_add->privCloseFd(); + + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_zip_temp_name.'\' in binary write mode'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Copy the files from the archive to the temporary file + // TBC : Here I should better append the file and go back to erase the central dir + $v_size = $v_central_dir['offset']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = fread($this->zip_fd, $v_read_size); + @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Copy the files from the archive_to_add into the temporary file + $v_size = $v_central_dir_to_add['offset']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = fread($p_archive_to_add->zip_fd, $v_read_size); + @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Store the offset of the central dir + $v_offset = @ftell($v_zip_temp_fd); + + // ----- Copy the block of file headers from the old archive + $v_size = $v_central_dir['size']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($this->zip_fd, $v_read_size); + @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Copy the block of file headers from the archive_to_add + $v_size = $v_central_dir_to_add['size']; + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($p_archive_to_add->zip_fd, $v_read_size); + @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Merge the file comments + $v_comment = $v_central_dir['comment'].' '.$v_central_dir_to_add['comment']; + + // ----- Calculate the size of the (new) central header + $v_size = @ftell($v_zip_temp_fd)-$v_offset; + + // ----- Swap the file descriptor + // Here is a trick : I swap the temporary fd with the zip fd, in order to use + // the following methods on the temporary fil and not the real archive fd + $v_swap = $this->zip_fd; + $this->zip_fd = $v_zip_temp_fd; + $v_zip_temp_fd = $v_swap; + + // ----- Create the central dir footer + if (($v_result = $this->privWriteCentralHeader($v_central_dir['entries']+$v_central_dir_to_add['entries'], $v_size, $v_offset, $v_comment)) != 1) + { + $this->privCloseFd(); + $p_archive_to_add->privCloseFd(); + @fclose($v_zip_temp_fd); + $this->zip_fd = null; + + // ----- Reset the file list + unset($v_header_list); + + // ----- Return + return $v_result; + } + + // ----- Swap back the file descriptor + $v_swap = $this->zip_fd; + $this->zip_fd = $v_zip_temp_fd; + $v_zip_temp_fd = $v_swap; + + // ----- Close + $this->privCloseFd(); + $p_archive_to_add->privCloseFd(); + + // ----- Close the temporary file + @fclose($v_zip_temp_fd); + + // ----- Delete the zip file + // TBC : I should test the result ... + @unlink($this->zipname); + + // ----- Rename the temporary file + // TBC : I should test the result ... + //@rename($v_zip_temp_name, $this->zipname); + PclZipUtilRename($v_zip_temp_name, $this->zipname); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privDuplicate() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privDuplicate($p_archive_filename) + { + $v_result=1; + + // ----- Look if the $p_archive_filename exists + if (!is_file($p_archive_filename)) + { + + // ----- Nothing to duplicate, so duplicate is a success. + $v_result = 1; + + // ----- Return + return $v_result; + } + + // ----- Open the zip file + if (($v_result=$this->privOpenFd('wb')) != 1) + { + // ----- Return + return $v_result; + } + + // ----- Open the temporary file in write mode + if (($v_zip_temp_fd = @fopen($p_archive_filename, 'rb')) == 0) + { + $this->privCloseFd(); + + PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive file \''.$p_archive_filename.'\' in binary write mode'); + + // ----- Return + return PclZip::errorCode(); + } + + // ----- Copy the files from the archive to the temporary file + // TBC : Here I should better append the file and go back to erase the central dir + $v_size = filesize($p_archive_filename); + while ($v_size != 0) + { + $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = fread($v_zip_temp_fd, $v_read_size); + @fwrite($this->zip_fd, $v_buffer, $v_read_size); + $v_size -= $v_read_size; + } + + // ----- Close + $this->privCloseFd(); + + // ----- Close the temporary file + @fclose($v_zip_temp_fd); + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privErrorLog() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function privErrorLog($p_error_code=0, $p_error_string='') + { + if (PCLZIP_ERROR_EXTERNAL == 1) { + PclError($p_error_code, $p_error_string); + } + else { + $this->error_code = $p_error_code; + $this->error_string = $p_error_string; + } + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privErrorReset() + // Description : + // Parameters : + // -------------------------------------------------------------------------------- + function privErrorReset() + { + if (PCLZIP_ERROR_EXTERNAL == 1) { + PclErrorReset(); + } + else { + $this->error_code = 0; + $this->error_string = ''; + } + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privDisableMagicQuotes() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privDisableMagicQuotes() + { + $v_result=1; + + // ----- Look if function exists + if ( (!function_exists("get_magic_quotes_runtime")) + || (!function_exists("set_magic_quotes_runtime"))) { + return $v_result; + } + + // ----- Look if already done + if ($this->magic_quotes_status != -1) { + return $v_result; + } + + // ----- Get and memorize the magic_quote value + $this->magic_quotes_status = @get_magic_quotes_runtime(); + + // ----- Disable magic_quotes + if ($this->magic_quotes_status == 1) { + @set_magic_quotes_runtime(0); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : privSwapBackMagicQuotes() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function privSwapBackMagicQuotes() + { + $v_result=1; + + // ----- Look if function exists + if ( (!function_exists("get_magic_quotes_runtime")) + || (!function_exists("set_magic_quotes_runtime"))) { + return $v_result; + } + + // ----- Look if something to do + if ($this->magic_quotes_status != -1) { + return $v_result; + } + + // ----- Swap back magic_quotes + if ($this->magic_quotes_status == 1) { + @set_magic_quotes_runtime($this->magic_quotes_status); + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + } + // End of class + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : PclZipUtilPathReduction() + // Description : + // Parameters : + // Return Values : + // -------------------------------------------------------------------------------- + function PclZipUtilPathReduction($p_dir) + { + $v_result = ""; + + // ----- Look for not empty path + if ($p_dir != "") { + // ----- Explode path by directory names + $v_list = explode("/", $p_dir); + + // ----- Study directories from last to first + $v_skip = 0; + for ($i=sizeof($v_list)-1; $i>=0; $i--) { + // ----- Look for current path + if ($v_list[$i] == ".") { + // ----- Ignore this directory + // Should be the first $i=0, but no check is done + } + else if ($v_list[$i] == "..") { + $v_skip++; + } + else if ($v_list[$i] == "") { + // ----- First '/' i.e. root slash + if ($i == 0) { + $v_result = "/".$v_result; + if ($v_skip > 0) { + // ----- It is an invalid path, so the path is not modified + // TBC + $v_result = $p_dir; + $v_skip = 0; + } + } + // ----- Last '/' i.e. indicates a directory + else if ($i == (sizeof($v_list)-1)) { + $v_result = $v_list[$i]; + } + // ----- Double '/' inside the path + else { + // ----- Ignore only the double '//' in path, + // but not the first and last '/' + } + } + else { + // ----- Look for item to skip + if ($v_skip > 0) { + $v_skip--; + } + else { + $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?"/".$v_result:""); + } + } + } + + // ----- Look for skip + if ($v_skip > 0) { + while ($v_skip > 0) { + $v_result = '../'.$v_result; + $v_skip--; + } + } + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : PclZipUtilPathInclusion() + // Description : + // This function indicates if the path $p_path is under the $p_dir tree. Or, + // said in an other way, if the file or sub-dir $p_path is inside the dir + // $p_dir. + // The function indicates also if the path is exactly the same as the dir. + // This function supports path with duplicated '/' like '//', but does not + // support '.' or '..' statements. + // Parameters : + // Return Values : + // 0 if $p_path is not inside directory $p_dir + // 1 if $p_path is inside directory $p_dir + // 2 if $p_path is exactly the same as $p_dir + // -------------------------------------------------------------------------------- + function PclZipUtilPathInclusion($p_dir, $p_path) + { + $v_result = 1; + + // ----- Look for path beginning by ./ + if ( ($p_dir == '.') + || ((strlen($p_dir) >=2) && (substr($p_dir, 0, 2) == './'))) { + $p_dir = PclZipUtilTranslateWinPath(getcwd(), FALSE).'/'.substr($p_dir, 1); + } + if ( ($p_path == '.') + || ((strlen($p_path) >=2) && (substr($p_path, 0, 2) == './'))) { + $p_path = PclZipUtilTranslateWinPath(getcwd(), FALSE).'/'.substr($p_path, 1); + } + + // ----- Explode dir and path by directory separator + $v_list_dir = explode("/", $p_dir); + $v_list_dir_size = sizeof($v_list_dir); + $v_list_path = explode("/", $p_path); + $v_list_path_size = sizeof($v_list_path); + + // ----- Study directories paths + $i = 0; + $j = 0; + while (($i < $v_list_dir_size) && ($j < $v_list_path_size) && ($v_result)) { + + // ----- Look for empty dir (path reduction) + if ($v_list_dir[$i] == '') { + $i++; + continue; + } + if ($v_list_path[$j] == '') { + $j++; + continue; + } + + // ----- Compare the items + if (($v_list_dir[$i] != $v_list_path[$j]) && ($v_list_dir[$i] != '') && ( $v_list_path[$j] != '')) { + $v_result = 0; + } + + // ----- Next items + $i++; + $j++; + } + + // ----- Look if everything seems to be the same + if ($v_result) { + // ----- Skip all the empty items + while (($j < $v_list_path_size) && ($v_list_path[$j] == '')) $j++; + while (($i < $v_list_dir_size) && ($v_list_dir[$i] == '')) $i++; + + if (($i >= $v_list_dir_size) && ($j >= $v_list_path_size)) { + // ----- There are exactly the same + $v_result = 2; + } + else if ($i < $v_list_dir_size) { + // ----- The path is shorter than the dir + $v_result = 0; + } + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : PclZipUtilCopyBlock() + // Description : + // Parameters : + // $p_mode : read/write compression mode + // 0 : src & dest normal + // 1 : src gzip, dest normal + // 2 : src normal, dest gzip + // 3 : src & dest gzip + // Return Values : + // -------------------------------------------------------------------------------- + function PclZipUtilCopyBlock($p_src, $p_dest, $p_size, $p_mode=0) + { + $v_result = 1; + + if ($p_mode==0) + { + while ($p_size != 0) + { + $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($p_src, $v_read_size); + @fwrite($p_dest, $v_buffer, $v_read_size); + $p_size -= $v_read_size; + } + } + else if ($p_mode==1) + { + while ($p_size != 0) + { + $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @gzread($p_src, $v_read_size); + @fwrite($p_dest, $v_buffer, $v_read_size); + $p_size -= $v_read_size; + } + } + else if ($p_mode==2) + { + while ($p_size != 0) + { + $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @fread($p_src, $v_read_size); + @gzwrite($p_dest, $v_buffer, $v_read_size); + $p_size -= $v_read_size; + } + } + else if ($p_mode==3) + { + while ($p_size != 0) + { + $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); + $v_buffer = @gzread($p_src, $v_read_size); + @gzwrite($p_dest, $v_buffer, $v_read_size); + $p_size -= $v_read_size; + } + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : PclZipUtilRename() + // Description : + // This function tries to do a simple rename() function. If it fails, it + // tries to copy the $p_src file in a new $p_dest file and then unlink the + // first one. + // Parameters : + // $p_src : Old filename + // $p_dest : New filename + // Return Values : + // 1 on success, 0 on failure. + // -------------------------------------------------------------------------------- + function PclZipUtilRename($p_src, $p_dest) + { + $v_result = 1; + + // ----- Try to rename the files + if (!@rename($p_src, $p_dest)) { + + // ----- Try to copy & unlink the src + if (!@copy($p_src, $p_dest)) { + $v_result = 0; + } + else if (!@unlink($p_src)) { + $v_result = 0; + } + } + + // ----- Return + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : PclZipUtilOptionText() + // Description : + // Translate option value in text. Mainly for debug purpose. + // Parameters : + // $p_option : the option value. + // Return Values : + // The option text value. + // -------------------------------------------------------------------------------- + function PclZipUtilOptionText($p_option) + { + + $v_list = get_defined_constants(); + for (reset($v_list); $v_key = key($v_list); next($v_list)) { + $v_prefix = substr($v_key, 0, 10); + if (( ($v_prefix == 'PCLZIP_OPT') + || ($v_prefix == 'PCLZIP_CB_') + || ($v_prefix == 'PCLZIP_ATT')) + && ($v_list[$v_key] == $p_option)) { + return $v_key; + } + } + + $v_result = 'Unknown'; + + return $v_result; + } + // -------------------------------------------------------------------------------- + + // -------------------------------------------------------------------------------- + // Function : PclZipUtilTranslateWinPath() + // Description : + // Translate windows path by replacing '\' by '/' and optionally removing + // drive letter. + // Parameters : + // $p_path : path to translate. + // $p_remove_disk_letter : true | false + // Return Values : + // The path translated. + // -------------------------------------------------------------------------------- + function PclZipUtilTranslateWinPath($p_path, $p_remove_disk_letter=true) + { + if (stristr(php_uname(), 'windows')) { + // ----- Look for potential disk letter + if (($p_remove_disk_letter) && (($v_position = strpos($p_path, ':')) != false)) { + $p_path = substr($p_path, $v_position+1); + } + // ----- Change potential windows directory separator + if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) { + $p_path = strtr($p_path, '\\', '/'); + } + } + return $p_path; + } + // -------------------------------------------------------------------------------- + + +?> diff --git a/wp-admin/includes/class-wp-comments-list-table.php b/wp-admin/includes/class-wp-comments-list-table.php new file mode 100644 index 0000000..3814f98 --- /dev/null +++ b/wp-admin/includes/class-wp-comments-list-table.php @@ -0,0 +1,638 @@ + 'comments', + 'singular' => 'comment', + 'ajax' => true, + 'screen' => isset( $args['screen'] ) ? $args['screen'] : null, + ) ); + } + + public function ajax_user_can() { + return current_user_can('edit_posts'); + } + + public function prepare_items() { + global $post_id, $comment_status, $search, $comment_type; + + $comment_status = isset( $_REQUEST['comment_status'] ) ? $_REQUEST['comment_status'] : 'all'; + if ( !in_array( $comment_status, array( 'all', 'moderated', 'approved', 'spam', 'trash' ) ) ) + $comment_status = 'all'; + + $comment_type = !empty( $_REQUEST['comment_type'] ) ? $_REQUEST['comment_type'] : ''; + + $search = ( isset( $_REQUEST['s'] ) ) ? $_REQUEST['s'] : ''; + + $post_type = ( isset( $_REQUEST['post_type'] ) ) ? sanitize_key( $_REQUEST['post_type'] ) : ''; + + $user_id = ( isset( $_REQUEST['user_id'] ) ) ? $_REQUEST['user_id'] : ''; + + $orderby = ( isset( $_REQUEST['orderby'] ) ) ? $_REQUEST['orderby'] : ''; + $order = ( isset( $_REQUEST['order'] ) ) ? $_REQUEST['order'] : ''; + + $comments_per_page = $this->get_per_page( $comment_status ); + + $doing_ajax = defined( 'DOING_AJAX' ) && DOING_AJAX; + + if ( isset( $_REQUEST['number'] ) ) { + $number = (int) $_REQUEST['number']; + } + else { + $number = $comments_per_page + min( 8, $comments_per_page ); // Grab a few extra + } + + $page = $this->get_pagenum(); + + if ( isset( $_REQUEST['start'] ) ) { + $start = $_REQUEST['start']; + } else { + $start = ( $page - 1 ) * $comments_per_page; + } + + if ( $doing_ajax && isset( $_REQUEST['offset'] ) ) { + $start += $_REQUEST['offset']; + } + + $status_map = array( + 'moderated' => 'hold', + 'approved' => 'approve', + 'all' => '', + ); + + $args = array( + 'status' => isset( $status_map[$comment_status] ) ? $status_map[$comment_status] : $comment_status, + 'search' => $search, + 'user_id' => $user_id, + 'offset' => $start, + 'number' => $number, + 'post_id' => $post_id, + 'type' => $comment_type, + 'orderby' => $orderby, + 'order' => $order, + 'post_type' => $post_type, + ); + + $_comments = get_comments( $args ); + + update_comment_cache( $_comments ); + + $this->items = array_slice( $_comments, 0, $comments_per_page ); + $this->extra_items = array_slice( $_comments, $comments_per_page ); + + $total_comments = get_comments( array_merge( $args, array('count' => true, 'offset' => 0, 'number' => 0) ) ); + + $_comment_post_ids = array(); + foreach ( $_comments as $_c ) { + $_comment_post_ids[] = $_c->comment_post_ID; + } + + $_comment_post_ids = array_unique( $_comment_post_ids ); + + $this->pending_count = get_pending_comments_num( $_comment_post_ids ); + + $this->set_pagination_args( array( + 'total_items' => $total_comments, + 'per_page' => $comments_per_page, + ) ); + } + + public function get_per_page( $comment_status = 'all' ) { + $comments_per_page = $this->get_items_per_page( 'edit_comments_per_page' ); + /** + * Filter the number of comments listed per page in the comments list table. + * + * @since 2.6.0 + * + * @param int $comments_per_page The number of comments to list per page. + * @param string $comment_status The comment status name. Default 'All'. + */ + $comments_per_page = apply_filters( 'comments_per_page', $comments_per_page, $comment_status ); + return $comments_per_page; + } + + public function no_items() { + global $comment_status; + + if ( 'moderated' == $comment_status ) + _e( 'No comments awaiting moderation.' ); + else + _e( 'No comments found.' ); + } + + protected function get_views() { + global $post_id, $comment_status, $comment_type; + + $status_links = array(); + $num_comments = ( $post_id ) ? wp_count_comments( $post_id ) : wp_count_comments(); + //, number_format_i18n($num_comments->moderated) ), "" . number_format_i18n($num_comments->moderated) . ""), + //, number_format_i18n($num_comments->spam) ), "" . number_format_i18n($num_comments->spam) . "") + $stati = array( + 'all' => _nx_noop('All', 'All', 'comments'), // singular not used + 'moderated' => _n_noop('Pending (%s)', 'Pending (%s)'), + 'approved' => _n_noop('Approved', 'Approved'), // singular not used + 'spam' => _n_noop('Spam (%s)', 'Spam (%s)'), + 'trash' => _n_noop('Trash (%s)', 'Trash (%s)') + ); + + if ( !EMPTY_TRASH_DAYS ) + unset($stati['trash']); + + $link = 'edit-comments.php'; + if ( !empty($comment_type) && 'all' != $comment_type ) + $link = add_query_arg( 'comment_type', $comment_type, $link ); + + foreach ( $stati as $status => $label ) { + $class = ( $status == $comment_status ) ? ' class="current"' : ''; + + if ( !isset( $num_comments->$status ) ) + $num_comments->$status = 10; + $link = add_query_arg( 'comment_status', $status, $link ); + if ( $post_id ) + $link = add_query_arg( 'p', absint( $post_id ), $link ); + /* + // I toyed with this, but decided against it. Leaving it in here in case anyone thinks it is a good idea. ~ Mark + if ( !empty( $_REQUEST['s'] ) ) + $link = add_query_arg( 's', esc_attr( wp_unslash( $_REQUEST['s'] ) ), $link ); + */ + $status_links[$status] = "" . sprintf( + translate_nooped_plural( $label, $num_comments->$status ), + number_format_i18n( $num_comments->$status ) + ) . ''; + } + + /** + * Filter the comment status links. + * + * @since 2.5.0 + * + * @param array $status_links An array of fully-formed status links. Default 'All'. + * Accepts 'All', 'Pending', 'Approved', 'Spam', and 'Trash'. + */ + $status_links = apply_filters( 'comment_status_links', $status_links ); + return $status_links; + } + + protected function get_bulk_actions() { + global $comment_status; + + $actions = array(); + if ( in_array( $comment_status, array( 'all', 'approved' ) ) ) + $actions['unapprove'] = __( 'Unapprove' ); + if ( in_array( $comment_status, array( 'all', 'moderated' ) ) ) + $actions['approve'] = __( 'Approve' ); + if ( in_array( $comment_status, array( 'all', 'moderated', 'approved', 'trash' ) ) ) + $actions['spam'] = _x( 'Mark as Spam', 'comment' ); + + if ( 'trash' == $comment_status ) + $actions['untrash'] = __( 'Restore' ); + elseif ( 'spam' == $comment_status ) + $actions['unspam'] = _x( 'Not Spam', 'comment' ); + + if ( in_array( $comment_status, array( 'trash', 'spam' ) ) || !EMPTY_TRASH_DAYS ) + $actions['delete'] = __( 'Delete Permanently' ); + else + $actions['trash'] = __( 'Move to Trash' ); + + return $actions; + } + + protected function extra_tablenav( $which ) { + global $comment_status, $comment_type; +?> +
    + + + + 'post-query-submit' ) ); + } + + if ( ( 'spam' == $comment_status || 'trash' == $comment_status ) && current_user_can( 'moderate_comments' ) ) { + wp_nonce_field( 'bulk-destroy', '_destroy_nonce' ); + $title = ( 'spam' == $comment_status ) ? esc_attr__( 'Empty Spam' ) : esc_attr__( 'Empty Trash' ); + submit_button( $title, 'apply', 'delete_all', false ); + } + /** + * Fires after the Filter submit button for comment types. + * + * @since 2.5.0 + * + * @param string $comment_status The comment status name. Default 'All'. + */ + do_action( 'manage_comments_nav', $comment_status ); + echo '
    '; + } + + public function current_action() { + if ( isset( $_REQUEST['delete_all'] ) || isset( $_REQUEST['delete_all2'] ) ) + return 'delete_all'; + + return parent::current_action(); + } + + public function get_columns() { + global $post_id; + + $columns = array(); + + if ( $this->checkbox ) + $columns['cb'] = ''; + + $columns['author'] = __( 'Author' ); + $columns['comment'] = _x( 'Comment', 'column name' ); + + if ( !$post_id ) + $columns['response'] = _x( 'In Response To', 'column name' ); + + return $columns; + } + + protected function get_sortable_columns() { + return array( + 'author' => 'comment_author', + 'response' => 'comment_post_ID' + ); + } + + public function display() { + wp_nonce_field( "fetch-list-" . get_class( $this ), '_ajax_fetch_list_nonce' ); + + $this->display_tablenav( 'top' ); + +?> + + + + print_column_headers(); ?> + + + + + + print_column_headers( false ); ?> + + + + + display_rows_or_placeholder(); ?> + + + + items = $this->extra_items; $this->display_rows(); ?> + +
    +display_tablenav( 'bottom' ); + } + + public function single_row( $a_comment ) { + global $post, $comment; + + $comment = $a_comment; + $the_comment_class = wp_get_comment_status( $comment->comment_ID ); + $the_comment_class = join( ' ', get_comment_class( $the_comment_class, $comment->comment_ID, $comment->comment_post_ID ) ); + + $post = get_post( $comment->comment_post_ID ); + + $this->user_can = current_user_can( 'edit_comment', $comment->comment_ID ); + + echo ""; + $this->single_row_columns( $comment ); + echo "\n"; + } + + public function column_cb( $comment ) { + if ( $this->user_can ) { ?> + + + user_can; + + $comment_url = esc_url( get_comment_link( $comment->comment_ID ) ); + $the_comment_status = wp_get_comment_status( $comment->comment_ID ); + + if ( $user_can ) { + $del_nonce = esc_html( '_wpnonce=' . wp_create_nonce( "delete-comment_$comment->comment_ID" ) ); + $approve_nonce = esc_html( '_wpnonce=' . wp_create_nonce( "approve-comment_$comment->comment_ID" ) ); + + $url = "comment.php?c=$comment->comment_ID"; + + $approve_url = esc_url( $url . "&action=approvecomment&$approve_nonce" ); + $unapprove_url = esc_url( $url . "&action=unapprovecomment&$approve_nonce" ); + $spam_url = esc_url( $url . "&action=spamcomment&$del_nonce" ); + $unspam_url = esc_url( $url . "&action=unspamcomment&$del_nonce" ); + $trash_url = esc_url( $url . "&action=trashcomment&$del_nonce" ); + $untrash_url = esc_url( $url . "&action=untrashcomment&$del_nonce" ); + $delete_url = esc_url( $url . "&action=deletecomment&$del_nonce" ); + } + + echo '
    '; + $this->column_author( $comment ); + echo '
    '; + + echo ''; + comment_text(); + if ( $user_can ) { ?> + + '', 'unapprove' => '', + 'reply' => '', + 'quickedit' => '', + 'edit' => '', + 'spam' => '', 'unspam' => '', + 'trash' => '', 'untrash' => '', 'delete' => '' + ); + + // Not looking at all comments. + if ( $comment_status && 'all' != $comment_status ) { + if ( 'approved' == $the_comment_status ) + $actions['unapprove'] = "" . __( 'Unapprove' ) . ''; + else if ( 'unapproved' == $the_comment_status ) + $actions['approve'] = "" . __( 'Approve' ) . ''; + } else { + $actions['approve'] = "" . __( 'Approve' ) . ''; + $actions['unapprove'] = "" . __( 'Unapprove' ) . ''; + } + + if ( 'spam' != $the_comment_status ) { + $actions['spam'] = "" . /* translators: mark as spam link */ _x( 'Spam', 'verb' ) . ''; + } elseif ( 'spam' == $the_comment_status ) { + $actions['unspam'] = "" . _x( 'Not Spam', 'comment' ) . ''; + } + + if ( 'trash' == $the_comment_status ) { + $actions['untrash'] = "" . __( 'Restore' ) . ''; + } + + if ( 'spam' == $the_comment_status || 'trash' == $the_comment_status || !EMPTY_TRASH_DAYS ) { + $actions['delete'] = "" . __( 'Delete Permanently' ) . ''; + } else { + $actions['trash'] = "" . _x( 'Trash', 'verb' ) . ''; + } + + if ( 'spam' != $the_comment_status && 'trash' != $the_comment_status ) { + $actions['edit'] = "". __( 'Edit' ) . ''; + + $format = '%s'; + + $actions['quickedit'] = sprintf( $format, $comment->comment_ID, $post->ID, 'edit', 'vim-q comment-inline', esc_attr__( 'Quick Edit' ), __( 'Quick Edit' ) ); + + $actions['reply'] = sprintf( $format, $comment->comment_ID, $post->ID, 'replyto', 'vim-r comment-inline', esc_attr__( 'Reply to this comment' ), __( 'Reply' ) ); + } + + /** This filter is documented in wp-admin/includes/dashboard.php */ + $actions = apply_filters( 'comment_row_actions', array_filter( $actions ), $comment ); + + $i = 0; + echo '
    '; + foreach ( $actions as $action => $link ) { + ++$i; + ( ( ( 'approve' == $action || 'unapprove' == $action ) && 2 === $i ) || 1 === $i ) ? $sep = '' : $sep = ' | '; + + // Reply and quickedit need a hide-if-no-js span when not added with ajax + if ( ( 'reply' == $action || 'quickedit' == $action ) && ! defined('DOING_AJAX') ) + $action .= ' hide-if-no-js'; + elseif ( ( $action == 'untrash' && $the_comment_status == 'trash' ) || ( $action == 'unspam' && $the_comment_status == 'spam' ) ) { + if ( '1' == get_comment_meta( $comment->comment_ID, '_wp_trash_meta_status', true ) ) + $action .= ' approve'; + else + $action .= ' unapprove'; + } + + echo "$sep$link"; + } + echo '
    '; + } + } + + public function column_author( $comment ) { + global $comment_status; + + $author_url = get_comment_author_url(); + if ( 'http://' == $author_url ) + $author_url = ''; + $author_url_display = preg_replace( '|http://(www\.)?|i', '', $author_url ); + if ( strlen( $author_url_display ) > 50 ) + $author_url_display = substr( $author_url_display, 0, 49 ) . '…'; + + echo ""; comment_author(); echo '
    '; + if ( !empty( $author_url ) ) + echo "$author_url_display
    "; + + if ( $this->user_can ) { + if ( !empty( $comment->comment_author_email ) ) { + comment_author_email_link(); + echo '
    '; + } + + $author_ip = get_comment_author_IP(); + if ( $author_ip ) { + $author_ip_url = add_query_arg( array( 's' => $author_ip, 'mode' => 'detail' ), 'edit-comments.php' ); + if ( 'spam' == $comment_status ) { + $author_ip_url = add_query_arg( 'comment_status', 'spam', $author_ip_url ); + } + printf( '%s', esc_url( $author_ip_url ), $author_ip ); + } + } + } + + public function column_date() { + return get_comment_date( __( 'Y/m/d \a\t g:ia' ) ); + } + + public function column_response() { + $post = get_post(); + + if ( isset( $this->pending_count[$post->ID] ) ) { + $pending_comments = $this->pending_count[$post->ID]; + } else { + $_pending_count_temp = get_pending_comments_num( array( $post->ID ) ); + $pending_comments = $this->pending_count[$post->ID] = $_pending_count_temp[$post->ID]; + } + + if ( current_user_can( 'edit_post', $post->ID ) ) { + $post_link = ""; + $post_link .= get_the_title( $post->ID ) . ''; + } else { + $post_link = get_the_title( $post->ID ); + } + + echo ''; + if ( 'attachment' == $post->post_type && ( $thumb = wp_get_attachment_image( $post->ID, array( 80, 60 ), true ) ) ) + echo $thumb; + } + + public function column_default( $comment, $column_name ) { + /** + * Fires when the default column output is displayed for a single row. + * + * @since 2.8.0 + * + * @param string $column_name The custom column's name. + * @param int $comment->comment_ID The custom column's unique ID number. + */ + do_action( 'manage_comments_custom_column', $column_name, $comment->comment_ID ); + } +} + +/** + * Post Comments List Table class. + * + * @package WordPress + * @subpackage List_Table + * @since 3.1.0 + * @access private + * + * @see WP_Comments_Table + */ +class WP_Post_Comments_List_Table extends WP_Comments_List_Table { + + protected function get_column_info() { + $this->_column_headers = array( + array( + 'author' => __( 'Author' ), + 'comment' => _x( 'Comment', 'column name' ), + ), + array(), + array(), + ); + + return $this->_column_headers; + } + + protected function get_table_classes() { + $classes = parent::get_table_classes(); + $classes[] = 'comments-box'; + return $classes; + } + + public function display( $output_empty = false ) { + $singular = $this->_args['singular']; + + wp_nonce_field( "fetch-list-" . get_class( $this ), '_ajax_fetch_list_nonce' ); +?> + + > + display_rows_or_placeholder(); + } ?> + + +$name; + } + + /** + * Make private properties settable for backwards compatibility. + * + * @since 4.0.0 + * @access public + * + * @param string $name Property to set. + * @param mixed $value Property value. + * @return mixed Newly-set property. + */ + public function __set( $name, $value ) { + return $this->$name = $value; + } + + /** + * Make private properties checkable for backwards compatibility. + * + * @since 4.0.0 + * @access public + * + * @param string $name Property to check if set. + * @return bool Whether the property is set. + */ + public function __isset( $name ) { + return isset( $this->$name ); + } + + /** + * Make private properties un-settable for backwards compatibility. + * + * @since 4.0.0 + * @access public + * + * @param string $name Property to unset. + */ + public function __unset( $name ) { + unset( $this->$name ); + } + + /** + * Return the path on the remote filesystem of ABSPATH. + * + * @access public + * @since 2.7.0 + * + * @return string The location of the remote path. + */ + public function abspath() { + $folder = $this->find_folder(ABSPATH); + // Perhaps the FTP folder is rooted at the WordPress install, Check for wp-includes folder in root, Could have some false positives, but rare. + if ( ! $folder && $this->is_dir( '/' . WPINC ) ) + $folder = '/'; + return $folder; + } + + /** + * Return the path on the remote filesystem of WP_CONTENT_DIR. + * + * @access public + * @since 2.7.0 + * + * @return string The location of the remote path. + */ + public function wp_content_dir() { + return $this->find_folder(WP_CONTENT_DIR); + } + + /** + * Return the path on the remote filesystem of WP_PLUGIN_DIR. + * + * @access public + * @since 2.7.0 + * + * @return string The location of the remote path. + */ + public function wp_plugins_dir() { + return $this->find_folder(WP_PLUGIN_DIR); + } + + /** + * Return the path on the remote filesystem of the Themes Directory. + * + * @access public + * @since 2.7.0 + * + * @param string $theme The Theme stylesheet or template for the directory. + * @return string The location of the remote path. + */ + public function wp_themes_dir( $theme = false ) { + $theme_root = get_theme_root( $theme ); + + // Account for relative theme roots + if ( '/themes' == $theme_root || ! is_dir( $theme_root ) ) + $theme_root = WP_CONTENT_DIR . $theme_root; + + return $this->find_folder( $theme_root ); + } + + /** + * Return the path on the remote filesystem of WP_LANG_DIR. + * + * @access public + * @since 3.2.0 + * + * @return string The location of the remote path. + */ + public function wp_lang_dir() { + return $this->find_folder(WP_LANG_DIR); + } + + /** + * Locate a folder on the remote filesystem. + * + * @access public + * @since 2.5.0 + * @deprecated 2.7.0 use WP_Filesystem::abspath() or WP_Filesystem::wp_*_dir() instead. + * @see WP_Filesystem::abspath() + * @see WP_Filesystem::wp_content_dir() + * @see WP_Filesystem::wp_plugins_dir() + * @see WP_Filesystem::wp_themes_dir() + * @see WP_Filesystem::wp_lang_dir() + * + * @param string $base The folder to start searching from. + * @param bool $echo True to display debug information. + * Default false. + * @return string The location of the remote path. + */ + public function find_base_dir( $base = '.', $echo = false ) { + _deprecated_function(__FUNCTION__, '2.7', 'WP_Filesystem::abspath() or WP_Filesystem::wp_*_dir()' ); + $this->verbose = $echo; + return $this->abspath(); + } + + /** + * Locate a folder on the remote filesystem. + * + * @access public + * @since 2.5.0 + * @deprecated 2.7.0 use WP_Filesystem::abspath() or WP_Filesystem::wp_*_dir() methods instead. + * @see WP_Filesystem::abspath() + * @see WP_Filesystem::wp_content_dir() + * @see WP_Filesystem::wp_plugins_dir() + * @see WP_Filesystem::wp_themes_dir() + * @see WP_Filesystem::wp_lang_dir() + * + * @param string $base The folder to start searching from. + * @param bool $echo True to display debug information. + * @return string The location of the remote path. + */ + public function get_base_dir( $base = '.', $echo = false ) { + _deprecated_function(__FUNCTION__, '2.7', 'WP_Filesystem::abspath() or WP_Filesystem::wp_*_dir()' ); + $this->verbose = $echo; + return $this->abspath(); + } + + /** + * Locate a folder on the remote filesystem. + * + * Assumes that on Windows systems, Stripping off the Drive + * letter is OK Sanitizes \\ to / in windows filepaths. + * + * @access public + * @since 2.7.0 + * + * @param string $folder the folder to locate. + * @return string The location of the remote path. + */ + public function find_folder( $folder ) { + + if ( isset( $this->cache[ $folder ] ) ) + return $this->cache[ $folder ]; + + if ( stripos($this->method, 'ftp') !== false ) { + $constant_overrides = array( + 'FTP_BASE' => ABSPATH, + 'FTP_CONTENT_DIR' => WP_CONTENT_DIR, + 'FTP_PLUGIN_DIR' => WP_PLUGIN_DIR, + 'FTP_LANG_DIR' => WP_LANG_DIR + ); + + // Direct matches ( folder = CONSTANT/ ) + foreach ( $constant_overrides as $constant => $dir ) { + if ( ! defined( $constant ) ) + continue; + if ( $folder === $dir ) + return trailingslashit( constant( $constant ) ); + } + + // Prefix Matches ( folder = CONSTANT/subdir ) + foreach ( $constant_overrides as $constant => $dir ) { + if ( ! defined( $constant ) ) + continue; + if ( 0 === stripos( $folder, $dir ) ) { // $folder starts with $dir + $potential_folder = preg_replace( '#^' . preg_quote( $dir, '#' ) . '/#i', trailingslashit( constant( $constant ) ), $folder ); + $potential_folder = trailingslashit( $potential_folder ); + + if ( $this->is_dir( $potential_folder ) ) { + $this->cache[ $folder ] = $potential_folder; + return $potential_folder; + } + } + } + } elseif ( 'direct' == $this->method ) { + $folder = str_replace('\\', '/', $folder); // Windows path sanitisation + return trailingslashit($folder); + } + + $folder = preg_replace('|^([a-z]{1}):|i', '', $folder); // Strip out windows drive letter if it's there. + $folder = str_replace('\\', '/', $folder); // Windows path sanitisation + + if ( isset($this->cache[ $folder ] ) ) + return $this->cache[ $folder ]; + + if ( $this->exists($folder) ) { // Folder exists at that absolute path. + $folder = trailingslashit($folder); + $this->cache[ $folder ] = $folder; + return $folder; + } + if ( $return = $this->search_for_folder($folder) ) + $this->cache[ $folder ] = $return; + return $return; + } + + /** + * Locate a folder on the remote filesystem. + * + * Expects Windows sanitized path. + * + * @access private + * @since 2.7.0 + * + * @param string $folder The folder to locate. + * @param string $base The folder to start searching from. + * @param bool $loop If the function has recursed, Internal use only. + * @return string The location of the remote path. + */ + public function search_for_folder( $folder, $base = '.', $loop = false ) { + if ( empty( $base ) || '.' == $base ) + $base = trailingslashit($this->cwd()); + + $folder = untrailingslashit($folder); + + if ( $this->verbose ) + printf( "\n" . __('Looking for %1$s in %2$s') . "
    \n", $folder, $base ); + + $folder_parts = explode('/', $folder); + $folder_part_keys = array_keys( $folder_parts ); + $last_index = array_pop( $folder_part_keys ); + $last_path = $folder_parts[ $last_index ]; + + $files = $this->dirlist( $base ); + + foreach ( $folder_parts as $index => $key ) { + if ( $index == $last_index ) + continue; // We want this to be caught by the next code block. + + /* + * Working from /home/ to /user/ to /wordpress/ see if that file exists within + * the current folder, If it's found, change into it and follow through looking + * for it. If it cant find WordPress down that route, it'll continue onto the next + * folder level, and see if that matches, and so on. If it reaches the end, and still + * cant find it, it'll return false for the entire function. + */ + if ( isset($files[ $key ]) ){ + + // Let's try that folder: + $newdir = trailingslashit(path_join($base, $key)); + if ( $this->verbose ) + printf( "\n" . __('Changing to %s') . "
    \n", $newdir ); + + // Only search for the remaining path tokens in the directory, not the full path again. + $newfolder = implode( '/', array_slice( $folder_parts, $index + 1 ) ); + if ( $ret = $this->search_for_folder( $newfolder, $newdir, $loop) ) + return $ret; + } + } + + // Only check this as a last resort, to prevent locating the incorrect install. All above procedures will fail quickly if this is the right branch to take. + if (isset( $files[ $last_path ] ) ) { + if ( $this->verbose ) + printf( "\n" . __('Found %s') . "
    \n", $base . $last_path ); + return trailingslashit($base . $last_path); + } + + // Prevent this function from looping again. + // No need to proceed if we've just searched in / + if ( $loop || '/' == $base ) + return false; + + // As an extra last resort, Change back to / if the folder wasn't found. + // This comes into effect when the CWD is /home/user/ but WP is at /var/www/.... + return $this->search_for_folder( $folder, '/', true ); + + } + + /** + * Return the *nix-style file permissions for a file. + * + * From the PHP documentation page for fileperms(). + * + * @link http://docs.php.net/fileperms + * + * @access public + * @since 2.5.0 + * + * @param string $file String filename. + * @return string The *nix-style representation of permissions. + */ + public function gethchmod( $file ){ + $perms = $this->getchmod($file); + if (($perms & 0xC000) == 0xC000) // Socket + $info = 's'; + elseif (($perms & 0xA000) == 0xA000) // Symbolic Link + $info = 'l'; + elseif (($perms & 0x8000) == 0x8000) // Regular + $info = '-'; + elseif (($perms & 0x6000) == 0x6000) // Block special + $info = 'b'; + elseif (($perms & 0x4000) == 0x4000) // Directory + $info = 'd'; + elseif (($perms & 0x2000) == 0x2000) // Character special + $info = 'c'; + elseif (($perms & 0x1000) == 0x1000) // FIFO pipe + $info = 'p'; + else // Unknown + $info = 'u'; + + // Owner + $info .= (($perms & 0x0100) ? 'r' : '-'); + $info .= (($perms & 0x0080) ? 'w' : '-'); + $info .= (($perms & 0x0040) ? + (($perms & 0x0800) ? 's' : 'x' ) : + (($perms & 0x0800) ? 'S' : '-')); + + // Group + $info .= (($perms & 0x0020) ? 'r' : '-'); + $info .= (($perms & 0x0010) ? 'w' : '-'); + $info .= (($perms & 0x0008) ? + (($perms & 0x0400) ? 's' : 'x' ) : + (($perms & 0x0400) ? 'S' : '-')); + + // World + $info .= (($perms & 0x0004) ? 'r' : '-'); + $info .= (($perms & 0x0002) ? 'w' : '-'); + $info .= (($perms & 0x0001) ? + (($perms & 0x0200) ? 't' : 'x' ) : + (($perms & 0x0200) ? 'T' : '-')); + return $info; + } + + /** + * Convert *nix-style file permissions to a octal number. + * + * Converts '-rw-r--r--' to 0644 + * From "info at rvgate dot nl"'s comment on the PHP documentation for chmod() + * + * @link http://docs.php.net/manual/en/function.chmod.php#49614 + * + * @access public + * @since 2.5.0 + * + * @param string $mode string The *nix-style file permission. + * @return int octal representation + */ + public function getnumchmodfromh( $mode ) { + $realmode = ''; + $legal = array('', 'w', 'r', 'x', '-'); + $attarray = preg_split('//', $mode); + + for ($i=0; $i < count($attarray); $i++) + if ($key = array_search($attarray[$i], $legal)) + $realmode .= $legal[$key]; + + $mode = str_pad($realmode, 10, '-', STR_PAD_LEFT); + $trans = array('-'=>'0', 'r'=>'4', 'w'=>'2', 'x'=>'1'); + $mode = strtr($mode,$trans); + + $newmode = $mode[0]; + $newmode .= $mode[1] + $mode[2] + $mode[3]; + $newmode .= $mode[4] + $mode[5] + $mode[6]; + $newmode .= $mode[7] + $mode[8] + $mode[9]; + return $newmode; + } + + /** + * Determine if the string provided contains binary characters. + * + * @access private + * @since 2.7.0 + * + * @param string $text String to test against. + * @return bool true if string is binary, false otherwise. + */ + public function is_binary( $text ) { + return (bool) preg_match( '|[^\x20-\x7E]|', $text ); // chr(32)..chr(127) + } + + /** + * Change the ownership of a file / folder. + * + * Default behavior is to do nothing, override this in your subclass, if desired. + * + * @since 2.5.0 + * + * @param string $file Path to the file. + * @param mixed $owner A user name or number. + * @param bool $recursive Optional. If set True changes file owner recursivly. Defaults to False. + * @return bool Returns true on success or false on failure. + */ + public function chown( $file, $owner, $recursive = false ) { + return false; + } + + /** + * Connect filesystem. + * + * @since 2.5.0 + * @abstract + * @return bool True on success or false on failure (always true for WP_Filesystem_Direct). + */ + public function connect() { + return true; + } + + /** + * Read entire file into a string. + * + * @since 2.5.0 + * @abstract + * @param string $file Name of the file to read. + * @return mixed|bool Returns the read data or false on failure. + */ + public function get_contents( $file ) { + return false; + } + + /** + * Read entire file into an array. + * + * @since 2.5.0 + * @abstract + * @param string $file Path to the file. + * @return array|bool the file contents in an array or false on failure. + */ + public function get_contents_array( $file ) { + return false; + } + + /** + * Write a string to a file. + * + * @since 2.5.0 + * @abstract + * @param string $file Remote path to the file where to write the data. + * @param string $contents The data to write. + * @param int $mode Optional. The file permissions as octal number, usually 0644. + * @return bool False on failure. + */ + public function put_contents( $file, $contents, $mode = false ) { + return false; + } + + /** + * Get the current working directory. + * + * @since 2.5.0 + * @abstract + * @return string|bool The current working directory on success, or false on failure. + */ + public function cwd() { + return false; + } + + /** + * Change current directory. + * + * @since 2.5.0 + * @abstract + * @param string $dir The new current directory. + * @return bool Returns true on success or false on failure. + */ + public function chdir( $dir ) { + return false; + } + + /** + * Change the file group. + * + * @since 2.5.0 + * @abstract + * @param string $file Path to the file. + * @param mixed $group A group name or number. + * @param bool $recursive Optional. If set True changes file group recursively. Defaults to False. + * @return bool Returns true on success or false on failure. + */ + public function chgrp( $file, $group, $recursive = false ) { + return false; + } + + /** + * Change filesystem permissions. + * + * @since 2.5.0 + * @abstract + * @param string $file Path to the file. + * @param int $mode Optional. The permissions as octal number, usually 0644 for files, 0755 for dirs. + * @param bool $recursive Optional. If set True changes file group recursively. Defaults to False. + * @return bool Returns true on success or false on failure. + */ + public function chmod( $file, $mode = false, $recursive = false ) { + return false; + } + + /** + * Get the file owner. + * + * @since 2.5.0 + * @abstract + * @param string $file Path to the file. + * @return string|bool Username of the user or false on error. + */ + public function owner( $file ) { + return false; + } + + /** + * Get the file's group. + * + * @since 2.5.0 + * @abstract + * @param string $file Path to the file. + * @return string|bool The group or false on error. + */ + public function group( $file ) { + return false; + } + + /** + * Copy a file. + * + * @since 2.5.0 + * @abstract + * @param string $source Path to the source file. + * @param string $destination Path to the destination file. + * @param bool $overwrite Optional. Whether to overwrite the destination file if it exists. + * Default false. + * @param int $mode Optional. The permissions as octal number, usually 0644 for files, 0755 for dirs. + * Default false. + * @return bool True if file copied successfully, False otherwise. + */ + public function copy( $source, $destination, $overwrite = false, $mode = false ) { + return false; + } + + /** + * Move a file. + * + * @since 2.5.0 + * @abstract + * @param string $source Path to the source file. + * @param string $destination Path to the destination file. + * @param bool $overwrite Optional. Whether to overwrite the destination file if it exists. + * Default false. + * @return bool True if file copied successfully, False otherwise. + */ + public function move( $source, $destination, $overwrite = false ) { + return false; + } + + /** + * Delete a file or directory. + * + * @since 2.5.0 + * @abstract + * @param string $file Path to the file. + * @param bool $recursive Optional. If set True changes file group recursively. Defaults to False. + * Default false. + * @param bool $type Type of resource. 'f' for file, 'd' for directory. + * Default false. + * @return bool True if the file or directory was deleted, false on failure. + */ + public function delete( $file, $recursive = false, $type = false ) { + return false; + } + + /** + * Check if a file or directory exists. + * + * @since 2.5.0 + * @abstract + * @param string $file Path to file/directory. + * @return bool Whether $file exists or not. + */ + public function exists( $file ) { + return false; + } + + /** + * Check if resource is a file. + * + * @since 2.5.0 + * @abstract + * @param string $file File path. + * @return bool Whether $file is a file. + */ + public function is_file( $file ) { + return false; + } + + /** + * Check if resource is a directory. + * + * @since 2.5.0 + * @abstract + * @param string $path Directory path. + * @return bool Whether $path is a directory. + */ + public function is_dir( $path ) { + return false; + } + + /** + * Check if a file is readable. + * + * @since 2.5.0 + * @abstract + * @param string $file Path to file. + * @return bool Whether $file is readable. + */ + public function is_readable( $file ) { + return false; + } + + /** + * Check if a file or directory is writable. + * + * @since 2.5.0 + * @abstract + * @return bool Whether $file is writable. + */ + public function is_writable( $file ) { + return false; + } + + /** + * Gets the file's last access time. + * + * @since 2.5.0 + * @abstract + * @param string $file Path to file. + * @return int|bool Unix timestamp representing last access time. + */ + public function atime( $file ) { + return false; + } + + /** + * Gets the file modification time. + * + * @since 2.5.0 + * @abstract + * @param string $file Path to file. + * @return int|bool Unix timestamp representing modification time. + */ + public function mtime( $file ) { + return false; + } + + /** + * Gets the file size (in bytes). + * + * @since 2.5.0 + * @abstract + * @param string $file Path to file. + * @return int|bool Size of the file in bytes. + */ + public function size( $file ) { + return false; + } + + /** + * Set the access and modification times of a file. + * + * Note: If $file doesn't exist, it will be created. + * + * @since 2.5.0 + * @abstract + * @param string $file Path to file. + * @param int $time Optional. Modified time to set for file. + * Default 0. + * @param int $atime Optional. Access time to set for file. + * Default 0. + * @return bool Whether operation was successful or not. + */ + public function touch( $file, $time = 0, $atime = 0 ) { + return false; + } + + /** + * Create a directory. + * + * @since 2.5.0 + * @abstract + * @param string $path Path for new directory. + * @param mixed $chmod Optional. The permissions as octal number, (or False to skip chmod) + * Default false. + * @param mixed $chown Optional. A user name or number (or False to skip chown) + * Default false. + * @param mixed $chgrp Optional. A group name or number (or False to skip chgrp). + * Default false. + * @return bool False if directory cannot be created, true otherwise. + */ + public function mkdir( $path, $chmod = false, $chown = false, $chgrp = false ) { + return false; + } + + /** + * Delete a directory. + * + * @since 2.5.0 + * @abstract + * @param string $path Path to directory. + * @param bool $recursive Optional. Whether to recursively remove files/directories. + * Default false. + * @return bool Whether directory is deleted successfully or not. + */ + public function rmdir( $path, $recursive = false ) { + return false; + } + + /** + * Get details for files in a directory or a specific file. + * + * @since 2.5.0 + * @abstract + * + * @param string $path Path to directory or file. + * @param bool $include_hidden Optional. Whether to include details of hidden ("." prefixed) files. + * Default true. + * @param bool $recursive Optional. Whether to recursively include file details in nested directories. + * Default false. + * @return array|bool { + * Array of files. False if unable to list directory contents. + * + * @type string $name Name of the file/directory. + * @type string $perms *nix representation of permissions. + * @type int $permsn Octal representation of permissions. + * @type string $owner Owner name or ID. + * @type int $size Size of file in bytes. + * @type int $lastmodunix Last modified unix timestamp. + * @type mixed $lastmod Last modified month (3 letter) and day (without leading 0). + * @type int $time Last modified time. + * @type string $type Type of resource. 'f' for file, 'd' for directory. + * @type mixed $files If a directory and $recursive is true, contains another array of files. + * } + */ + public function dirlist( $path, $include_hidden = true, $recursive = false ) { + return false; + } + +} // WP_Filesystem_Base diff --git a/wp-admin/includes/class-wp-filesystem-direct.php b/wp-admin/includes/class-wp-filesystem-direct.php new file mode 100644 index 0000000..1e4cb50 --- /dev/null +++ b/wp-admin/includes/class-wp-filesystem-direct.php @@ -0,0 +1,463 @@ +method = 'direct'; + $this->errors = new WP_Error(); + } + + /** + * Reads entire file into a string + * + * @param string $file Name of the file to read. + * @return string|bool The function returns the read data or false on failure. + */ + public function get_contents($file) { + return @file_get_contents($file); + } + + /** + * Reads entire file into an array + * + * @param string $file Path to the file. + * @return array|bool the file contents in an array or false on failure. + */ + public function get_contents_array($file) { + return @file($file); + } + + /** + * Write a string to a file + * + * @param string $file Remote path to the file where to write the data. + * @param string $contents The data to write. + * @param int $mode Optional. The file permissions as octal number, usually 0644. + * Default false. + * @return bool False upon failure, true otherwise. + */ + public function put_contents( $file, $contents, $mode = false ) { + $fp = @fopen( $file, 'wb' ); + if ( ! $fp ) + return false; + + mbstring_binary_safe_encoding(); + + $data_length = strlen( $contents ); + + $bytes_written = fwrite( $fp, $contents ); + + reset_mbstring_encoding(); + + fclose( $fp ); + + if ( $data_length !== $bytes_written ) + return false; + + $this->chmod( $file, $mode ); + + return true; + } + + /** + * Gets the current working directory + * + * @return string|bool the current working directory on success, or false on failure. + */ + public function cwd() { + return @getcwd(); + } + + /** + * Change directory + * + * @param string $dir The new current directory. + * @return bool Returns true on success or false on failure. + */ + public function chdir($dir) { + return @chdir($dir); + } + + /** + * Changes file group + * + * @param string $file Path to the file. + * @param mixed $group A group name or number. + * @param bool $recursive Optional. If set True changes file group recursively. Default false. + * @return bool Returns true on success or false on failure. + */ + public function chgrp($file, $group, $recursive = false) { + if ( ! $this->exists($file) ) + return false; + if ( ! $recursive ) + return @chgrp($file, $group); + if ( ! $this->is_dir($file) ) + return @chgrp($file, $group); + // Is a directory, and we want recursive + $file = trailingslashit($file); + $filelist = $this->dirlist($file); + foreach ($filelist as $filename) + $this->chgrp($file . $filename, $group, $recursive); + + return true; + } + + /** + * Changes filesystem permissions + * + * @param string $file Path to the file. + * @param int $mode Optional. The permissions as octal number, usually 0644 for files, + * 0755 for dirs. Default false. + * @param bool $recursive Optional. If set True changes file group recursively. Default false. + * @return bool Returns true on success or false on failure. + */ + public function chmod($file, $mode = false, $recursive = false) { + if ( ! $mode ) { + if ( $this->is_file($file) ) + $mode = FS_CHMOD_FILE; + elseif ( $this->is_dir($file) ) + $mode = FS_CHMOD_DIR; + else + return false; + } + + if ( ! $recursive || ! $this->is_dir($file) ) + return @chmod($file, $mode); + // Is a directory, and we want recursive + $file = trailingslashit($file); + $filelist = $this->dirlist($file); + foreach ( (array)$filelist as $filename => $filemeta) + $this->chmod($file . $filename, $mode, $recursive); + + return true; + } + + /** + * Changes file owner + * + * @param string $file Path to the file. + * @param mixed $owner A user name or number. + * @param bool $recursive Optional. If set True changes file owner recursively. + * Default false. + * @return bool Returns true on success or false on failure. + */ + public function chown($file, $owner, $recursive = false) { + if ( ! $this->exists($file) ) + return false; + if ( ! $recursive ) + return @chown($file, $owner); + if ( ! $this->is_dir($file) ) + return @chown($file, $owner); + // Is a directory, and we want recursive + $filelist = $this->dirlist($file); + foreach ($filelist as $filename) { + $this->chown($file . '/' . $filename, $owner, $recursive); + } + return true; + } + + /** + * Gets file owner + * + * @param string $file Path to the file. + * @return string|bool Username of the user or false on error. + */ + public function owner($file) { + $owneruid = @fileowner($file); + if ( ! $owneruid ) + return false; + if ( ! function_exists('posix_getpwuid') ) + return $owneruid; + $ownerarray = posix_getpwuid($owneruid); + return $ownerarray['name']; + } + + /** + * Gets file permissions + * + * FIXME does not handle errors in fileperms() + * + * @param string $file Path to the file. + * @return string Mode of the file (last 3 digits). + */ + public function getchmod($file) { + return substr( decoct( @fileperms( $file ) ), -3 ); + } + + /** + * @param string $file + * @return string|false + */ + public function group($file) { + $gid = @filegroup($file); + if ( ! $gid ) + return false; + if ( ! function_exists('posix_getgrgid') ) + return $gid; + $grouparray = posix_getgrgid($gid); + return $grouparray['name']; + } + + /** + * @param string $source + * @param string $destination + * @param bool $overwrite + * @param int $mode + * @return bool + */ + public function copy($source, $destination, $overwrite = false, $mode = false) { + if ( ! $overwrite && $this->exists($destination) ) + return false; + + $rtval = copy($source, $destination); + if ( $mode ) + $this->chmod($destination, $mode); + return $rtval; + } + + /** + * @param string $source + * @param string $destination + * @param bool $overwrite + * @return bool + */ + public function move($source, $destination, $overwrite = false) { + if ( ! $overwrite && $this->exists($destination) ) + return false; + + // Try using rename first. if that fails (for example, source is read only) try copy. + if ( @rename($source, $destination) ) + return true; + + if ( $this->copy($source, $destination, $overwrite) && $this->exists($destination) ) { + $this->delete($source); + return true; + } else { + return false; + } + } + + /** + * @param string $file + * @param bool $recursive + * @param string $type + * @return bool + */ + public function delete($file, $recursive = false, $type = false) { + if ( empty( $file ) ) // Some filesystems report this as /, which can cause non-expected recursive deletion of all files in the filesystem. + return false; + $file = str_replace( '\\', '/', $file ); // for win32, occasional problems deleting files otherwise + + if ( 'f' == $type || $this->is_file($file) ) + return @unlink($file); + if ( ! $recursive && $this->is_dir($file) ) + return @rmdir($file); + + // At this point it's a folder, and we're in recursive mode + $file = trailingslashit($file); + $filelist = $this->dirlist($file, true); + + $retval = true; + if ( is_array( $filelist ) ) { + foreach ( $filelist as $filename => $fileinfo ) { + if ( ! $this->delete($file . $filename, $recursive, $fileinfo['type']) ) + $retval = false; + } + } + + if ( file_exists($file) && ! @rmdir($file) ) + $retval = false; + + return $retval; + } + /** + * @param string $file + * @return bool + */ + public function exists($file) { + return @file_exists($file); + } + /** + * @param string $file + * @return bool + */ + public function is_file($file) { + return @is_file($file); + } + /** + * @param string $path + * @return bool + */ + public function is_dir($path) { + return @is_dir($path); + } + + /** + * @param string $file + * @return bool + */ + public function is_readable($file) { + return @is_readable($file); + } + + /** + * @param string $file + * @return bool + */ + public function is_writable($file) { + return @is_writable($file); + } + + /** + * @param string $file + * @return int + */ + public function atime($file) { + return @fileatime($file); + } + + /** + * @param string $file + * @return int + */ + public function mtime($file) { + return @filemtime($file); + } + + /** + * @param string $file + * @return int + */ + public function size($file) { + return @filesize($file); + } + + /** + * @param string $file + * @param int $time + * @param int $atime + * @return bool + */ + public function touch($file, $time = 0, $atime = 0) { + if ($time == 0) + $time = time(); + if ($atime == 0) + $atime = time(); + return @touch($file, $time, $atime); + } + + /** + * @param string $path + * @param mixed $chmod + * @param mixed $chown + * @param mixed $chgrp + * @return bool + */ + public function mkdir($path, $chmod = false, $chown = false, $chgrp = false) { + // Safe mode fails with a trailing slash under certain PHP versions. + $path = untrailingslashit($path); + if ( empty($path) ) + return false; + + if ( ! $chmod ) + $chmod = FS_CHMOD_DIR; + + if ( ! @mkdir($path) ) + return false; + $this->chmod($path, $chmod); + if ( $chown ) + $this->chown($path, $chown); + if ( $chgrp ) + $this->chgrp($path, $chgrp); + return true; + } + + /** + * @param string $path + * @param bool $recursive + * @return type + */ + public function rmdir($path, $recursive = false) { + return $this->delete($path, $recursive); + } + + /** + * @param string $path + * @param bool $include_hidden + * @param bool $recursive + * @return bool|array + */ + public function dirlist($path, $include_hidden = true, $recursive = false) { + if ( $this->is_file($path) ) { + $limit_file = basename($path); + $path = dirname($path); + } else { + $limit_file = false; + } + + if ( ! $this->is_dir($path) ) + return false; + + $dir = @dir($path); + if ( ! $dir ) + return false; + + $ret = array(); + + while (false !== ($entry = $dir->read()) ) { + $struc = array(); + $struc['name'] = $entry; + + if ( '.' == $struc['name'] || '..' == $struc['name'] ) + continue; + + if ( ! $include_hidden && '.' == $struc['name'][0] ) + continue; + + if ( $limit_file && $struc['name'] != $limit_file) + continue; + + $struc['perms'] = $this->gethchmod($path.'/'.$entry); + $struc['permsn'] = $this->getnumchmodfromh($struc['perms']); + $struc['number'] = false; + $struc['owner'] = $this->owner($path.'/'.$entry); + $struc['group'] = $this->group($path.'/'.$entry); + $struc['size'] = $this->size($path.'/'.$entry); + $struc['lastmodunix']= $this->mtime($path.'/'.$entry); + $struc['lastmod'] = date('M j',$struc['lastmodunix']); + $struc['time'] = date('h:i:s',$struc['lastmodunix']); + $struc['type'] = $this->is_dir($path.'/'.$entry) ? 'd' : 'f'; + + if ( 'd' == $struc['type'] ) { + if ( $recursive ) + $struc['files'] = $this->dirlist($path . '/' . $struc['name'], $include_hidden, $recursive); + else + $struc['files'] = array(); + } + + $ret[ $struc['name'] ] = $struc; + } + $dir->close(); + unset($dir); + return $ret; + } +} diff --git a/wp-admin/includes/class-wp-filesystem-ftpext.php b/wp-admin/includes/class-wp-filesystem-ftpext.php new file mode 100644 index 0000000..90e3de0 --- /dev/null +++ b/wp-admin/includes/class-wp-filesystem-ftpext.php @@ -0,0 +1,526 @@ +method = 'ftpext'; + $this->errors = new WP_Error(); + + // Check if possible to use ftp functions. + if ( ! extension_loaded('ftp') ) { + $this->errors->add('no_ftp_ext', __('The ftp PHP extension is not available')); + return false; + } + + // This Class uses the timeout on a per-connection basis, Others use it on a per-action basis. + + if ( ! defined('FS_TIMEOUT') ) + define('FS_TIMEOUT', 240); + + if ( empty($opt['port']) ) + $this->options['port'] = 21; + else + $this->options['port'] = $opt['port']; + + if ( empty($opt['hostname']) ) + $this->errors->add('empty_hostname', __('FTP hostname is required')); + else + $this->options['hostname'] = $opt['hostname']; + + if ( ! empty($opt['base']) ) + $this->wp_base = $opt['base']; + + // Check if the options provided are OK. + if ( empty($opt['username']) ) + $this->errors->add('empty_username', __('FTP username is required')); + else + $this->options['username'] = $opt['username']; + + if ( empty($opt['password']) ) + $this->errors->add('empty_password', __('FTP password is required')); + else + $this->options['password'] = $opt['password']; + + $this->options['ssl'] = false; + if ( isset($opt['connection_type']) && 'ftps' == $opt['connection_type'] ) + $this->options['ssl'] = true; + } + + public function connect() { + if ( isset($this->options['ssl']) && $this->options['ssl'] && function_exists('ftp_ssl_connect') ) + $this->link = @ftp_ssl_connect($this->options['hostname'], $this->options['port'], FS_CONNECT_TIMEOUT); + else + $this->link = @ftp_connect($this->options['hostname'], $this->options['port'], FS_CONNECT_TIMEOUT); + + if ( ! $this->link ) { + $this->errors->add('connect', sprintf(__('Failed to connect to FTP Server %1$s:%2$s'), $this->options['hostname'], $this->options['port'])); + return false; + } + + if ( ! @ftp_login($this->link,$this->options['username'], $this->options['password']) ) { + $this->errors->add('auth', sprintf(__('Username/Password incorrect for %s'), $this->options['username'])); + return false; + } + + // Set the Connection to use Passive FTP + @ftp_pasv( $this->link, true ); + if ( @ftp_get_option($this->link, FTP_TIMEOUT_SEC) < FS_TIMEOUT ) + @ftp_set_option($this->link, FTP_TIMEOUT_SEC, FS_TIMEOUT); + + return true; + } + + /** + * @param string $file + * @return bool|string + */ + public function get_contents( $file ) { + $tempfile = wp_tempnam($file); + $temp = fopen($tempfile, 'w+'); + + if ( ! $temp ) + return false; + + if ( ! @ftp_fget($this->link, $temp, $file, FTP_BINARY ) ) + return false; + + fseek( $temp, 0 ); // Skip back to the start of the file being written to + $contents = ''; + + while ( ! feof($temp) ) + $contents .= fread($temp, 8192); + + fclose($temp); + unlink($tempfile); + return $contents; + } + + /** + * @param string $file + * @return array + */ + public function get_contents_array($file) { + return explode("\n", $this->get_contents($file)); + } + + /** + * @param string $file + * @param string $contents + * @param bool|int $mode + * @return bool + */ + public function put_contents($file, $contents, $mode = false ) { + $tempfile = wp_tempnam($file); + $temp = fopen( $tempfile, 'wb+' ); + if ( ! $temp ) + return false; + + mbstring_binary_safe_encoding(); + + $data_length = strlen( $contents ); + $bytes_written = fwrite( $temp, $contents ); + + reset_mbstring_encoding(); + + if ( $data_length !== $bytes_written ) { + fclose( $temp ); + unlink( $tempfile ); + return false; + } + + fseek( $temp, 0 ); // Skip back to the start of the file being written to + + $ret = @ftp_fput( $this->link, $file, $temp, FTP_BINARY ); + + fclose($temp); + unlink($tempfile); + + $this->chmod($file, $mode); + + return $ret; + } + + /** + * @return string + */ + public function cwd() { + $cwd = @ftp_pwd($this->link); + if ( $cwd ) + $cwd = trailingslashit($cwd); + return $cwd; + } + + /** + * @param string $dir + * @return bool + */ + public function chdir($dir) { + return @ftp_chdir($this->link, $dir); + } + + /** + * @param string $file + * @param bool $group + * @param bool $recursive + */ + public function chgrp($file, $group, $recursive = false ) { + return false; + } + + /** + * @param string $file + * @param int $mode + * @param bool $recursive + * @return bool + */ + public function chmod($file, $mode = false, $recursive = false) { + if ( ! $mode ) { + if ( $this->is_file($file) ) + $mode = FS_CHMOD_FILE; + elseif ( $this->is_dir($file) ) + $mode = FS_CHMOD_DIR; + else + return false; + } + + // chmod any sub-objects if recursive. + if ( $recursive && $this->is_dir($file) ) { + $filelist = $this->dirlist($file); + foreach ( (array)$filelist as $filename => $filemeta ) + $this->chmod($file . '/' . $filename, $mode, $recursive); + } + + // chmod the file or directory + if ( ! function_exists('ftp_chmod') ) + return (bool)@ftp_site($this->link, sprintf('CHMOD %o %s', $mode, $file)); + return (bool)@ftp_chmod($this->link, $mode, $file); + } + + /** + * @param string $file + * @return string + */ + public function owner($file) { + $dir = $this->dirlist($file); + return $dir[$file]['owner']; + } + /** + * @param string $file + * @return string + */ + public function getchmod($file) { + $dir = $this->dirlist($file); + return $dir[$file]['permsn']; + } + /** + * @param string $file + * @return string + */ + public function group($file) { + $dir = $this->dirlist($file); + return $dir[$file]['group']; + } + + /** + * + * @param string $source + * @param string $destination + * @param bool $overwrite + * @param string|bool $mode + * @return bool + */ + public function copy($source, $destination, $overwrite = false, $mode = false) { + if ( ! $overwrite && $this->exists($destination) ) + return false; + $content = $this->get_contents($source); + if ( false === $content ) + return false; + return $this->put_contents($destination, $content, $mode); + } + /** + * @param string $source + * @param string $destination + * @param bool $overwrite + * @return bool + */ + public function move($source, $destination, $overwrite = false) { + return ftp_rename($this->link, $source, $destination); + } + /** + * @param string $file + * @param bool $recursive + * @param string $type + * @return bool + */ + public function delete($file, $recursive = false, $type = false) { + if ( empty($file) ) + return false; + if ( 'f' == $type || $this->is_file($file) ) + return @ftp_delete($this->link, $file); + if ( !$recursive ) + return @ftp_rmdir($this->link, $file); + + $filelist = $this->dirlist( trailingslashit($file) ); + if ( !empty($filelist) ) + foreach ( $filelist as $delete_file ) + $this->delete( trailingslashit($file) . $delete_file['name'], $recursive, $delete_file['type'] ); + return @ftp_rmdir($this->link, $file); + } + /** + * @param string $file + * @return bool + */ + public function exists($file) { + $list = @ftp_nlist($this->link, $file); + return !empty($list); //empty list = no file, so invert. + } + /** + * @param string $file + * @return bool + */ + public function is_file($file) { + return $this->exists($file) && !$this->is_dir($file); + } + /** + * @param string $path + * @return bool + */ + public function is_dir($path) { + $cwd = $this->cwd(); + $result = @ftp_chdir($this->link, trailingslashit($path) ); + if ( $result && $path == $this->cwd() || $this->cwd() != $cwd ) { + @ftp_chdir($this->link, $cwd); + return true; + } + return false; + } + + /** + * @param string $file + * @return bool + */ + public function is_readable($file) { + return true; + } + /** + * @param string $file + * @return bool + */ + public function is_writable($file) { + return true; + } + /** + * @param string $file + * @return bool + */ + public function atime($file) { + return false; + } + /** + * @param string $file + * @return int + */ + public function mtime($file) { + return ftp_mdtm($this->link, $file); + } + /** + * @param string $file + * @return int + */ + public function size($file) { + return ftp_size($this->link, $file); + } + /** + * @param string $file + * @return bool + */ + public function touch($file, $time = 0, $atime = 0) { + return false; + } + + /** + * @param string $path + * @param mixed $chmod + * @param mixed $chown + * @param mixed $chgrp + * @return bool + */ + public function mkdir($path, $chmod = false, $chown = false, $chgrp = false) { + $path = untrailingslashit($path); + if ( empty($path) ) + return false; + + if ( !@ftp_mkdir($this->link, $path) ) + return false; + $this->chmod($path, $chmod); + if ( $chown ) + $this->chown($path, $chown); + if ( $chgrp ) + $this->chgrp($path, $chgrp); + return true; + } + + /** + * @param string $path + * @param bool $recursive + * @return bool + */ + public function rmdir($path, $recursive = false) { + return $this->delete($path, $recursive); + } + + /** + * @staticvar bool $is_windows + * @param string $line + * @return string + */ + public function parselisting($line) { + static $is_windows; + if ( is_null($is_windows) ) + $is_windows = stripos( ftp_systype($this->link), 'win') !== false; + + if ( $is_windows && preg_match('/([0-9]{2})-([0-9]{2})-([0-9]{2}) +([0-9]{2}):([0-9]{2})(AM|PM) +([0-9]+|
    ) +(.+)/', $line, $lucifer) ) { + $b = array(); + if ( $lucifer[3] < 70 ) + $lucifer[3] +=2000; + else + $lucifer[3] += 1900; // 4digit year fix + $b['isdir'] = ( $lucifer[7] == ''); + if ( $b['isdir'] ) + $b['type'] = 'd'; + else + $b['type'] = 'f'; + $b['size'] = $lucifer[7]; + $b['month'] = $lucifer[1]; + $b['day'] = $lucifer[2]; + $b['year'] = $lucifer[3]; + $b['hour'] = $lucifer[4]; + $b['minute'] = $lucifer[5]; + $b['time'] = @mktime($lucifer[4] + (strcasecmp($lucifer[6], "PM") == 0 ? 12 : 0), $lucifer[5], 0, $lucifer[1], $lucifer[2], $lucifer[3]); + $b['am/pm'] = $lucifer[6]; + $b['name'] = $lucifer[8]; + } elseif ( !$is_windows && $lucifer = preg_split('/[ ]/', $line, 9, PREG_SPLIT_NO_EMPTY)) { + //echo $line."\n"; + $lcount = count($lucifer); + if ( $lcount < 8 ) + return ''; + $b = array(); + $b['isdir'] = $lucifer[0]{0} === 'd'; + $b['islink'] = $lucifer[0]{0} === 'l'; + if ( $b['isdir'] ) + $b['type'] = 'd'; + elseif ( $b['islink'] ) + $b['type'] = 'l'; + else + $b['type'] = 'f'; + $b['perms'] = $lucifer[0]; + $b['number'] = $lucifer[1]; + $b['owner'] = $lucifer[2]; + $b['group'] = $lucifer[3]; + $b['size'] = $lucifer[4]; + if ( $lcount == 8 ) { + sscanf($lucifer[5], '%d-%d-%d', $b['year'], $b['month'], $b['day']); + sscanf($lucifer[6], '%d:%d', $b['hour'], $b['minute']); + $b['time'] = @mktime($b['hour'], $b['minute'], 0, $b['month'], $b['day'], $b['year']); + $b['name'] = $lucifer[7]; + } else { + $b['month'] = $lucifer[5]; + $b['day'] = $lucifer[6]; + if ( preg_match('/([0-9]{2}):([0-9]{2})/', $lucifer[7], $l2) ) { + $b['year'] = date("Y"); + $b['hour'] = $l2[1]; + $b['minute'] = $l2[2]; + } else { + $b['year'] = $lucifer[7]; + $b['hour'] = 0; + $b['minute'] = 0; + } + $b['time'] = strtotime( sprintf('%d %s %d %02d:%02d', $b['day'], $b['month'], $b['year'], $b['hour'], $b['minute']) ); + $b['name'] = $lucifer[8]; + } + } + + // Replace symlinks formatted as "source -> target" with just the source name + if ( $b['islink'] ) + $b['name'] = preg_replace( '/(\s*->\s*.*)$/', '', $b['name'] ); + + return $b; + } + + /** + * @param string $path + * @param bool $include_hidden + * @param bool $recursive + * @return bool|array + */ + public function dirlist($path = '.', $include_hidden = true, $recursive = false) { + if ( $this->is_file($path) ) { + $limit_file = basename($path); + $path = dirname($path) . '/'; + } else { + $limit_file = false; + } + + $pwd = @ftp_pwd($this->link); + if ( ! @ftp_chdir($this->link, $path) ) // Cant change to folder = folder doesn't exist + return false; + $list = @ftp_rawlist($this->link, '-a', false); + @ftp_chdir($this->link, $pwd); + + if ( empty($list) ) // Empty array = non-existent folder (real folder will show . at least) + return false; + + $dirlist = array(); + foreach ( $list as $k => $v ) { + $entry = $this->parselisting($v); + if ( empty($entry) ) + continue; + + if ( '.' == $entry['name'] || '..' == $entry['name'] ) + continue; + + if ( ! $include_hidden && '.' == $entry['name'][0] ) + continue; + + if ( $limit_file && $entry['name'] != $limit_file) + continue; + + $dirlist[ $entry['name'] ] = $entry; + } + + $ret = array(); + foreach ( (array)$dirlist as $struc ) { + if ( 'd' == $struc['type'] ) { + if ( $recursive ) + $struc['files'] = $this->dirlist($path . '/' . $struc['name'], $include_hidden, $recursive); + else + $struc['files'] = array(); + } + + $ret[ $struc['name'] ] = $struc; + } + return $ret; + } + + public function __destruct() { + if ( $this->link ) + ftp_close($this->link); + } +} diff --git a/wp-admin/includes/class-wp-filesystem-ftpsockets.php b/wp-admin/includes/class-wp-filesystem-ftpsockets.php new file mode 100644 index 0000000..bb7e0c2 --- /dev/null +++ b/wp-admin/includes/class-wp-filesystem-ftpsockets.php @@ -0,0 +1,456 @@ +method = 'ftpsockets'; + $this->errors = new WP_Error(); + + // Check if possible to use ftp functions. + if ( ! @include_once( ABSPATH . 'wp-admin/includes/class-ftp.php' ) ) { + return false; + } + $this->ftp = new ftp(); + + if ( empty($opt['port']) ) + $this->options['port'] = 21; + else + $this->options['port'] = $opt['port']; + + if ( empty($opt['hostname']) ) + $this->errors->add('empty_hostname', __('FTP hostname is required')); + else + $this->options['hostname'] = $opt['hostname']; + + if ( ! empty($opt['base']) ) + $this->wp_base = $opt['base']; + + // Check if the options provided are OK. + if ( empty ($opt['username']) ) + $this->errors->add('empty_username', __('FTP username is required')); + else + $this->options['username'] = $opt['username']; + + if ( empty ($opt['password']) ) + $this->errors->add('empty_password', __('FTP password is required')); + else + $this->options['password'] = $opt['password']; + } + + public function connect() { + if ( ! $this->ftp ) + return false; + + $this->ftp->setTimeout(FS_CONNECT_TIMEOUT); + + if ( ! $this->ftp->SetServer($this->options['hostname'], $this->options['port']) ) { + $this->errors->add('connect', sprintf(__('Failed to connect to FTP Server %1$s:%2$s'), $this->options['hostname'], $this->options['port'])); + return false; + } + + if ( ! $this->ftp->connect() ) { + $this->errors->add('connect', sprintf(__('Failed to connect to FTP Server %1$s:%2$s'), $this->options['hostname'], $this->options['port'])); + return false; + } + + if ( ! $this->ftp->login($this->options['username'], $this->options['password']) ) { + $this->errors->add('auth', sprintf(__('Username/Password incorrect for %s'), $this->options['username'])); + return false; + } + + $this->ftp->SetType( FTP_BINARY ); + $this->ftp->Passive( true ); + $this->ftp->setTimeout( FS_TIMEOUT ); + return true; + } + + /** + * @param string $file + * @return bool|string + */ + public function get_contents( $file ) { + if ( ! $this->exists($file) ) + return false; + + $temp = wp_tempnam( $file ); + + if ( ! $temphandle = fopen($temp, 'w+') ) + return false; + + mbstring_binary_safe_encoding(); + + if ( ! $this->ftp->fget($temphandle, $file) ) { + fclose($temphandle); + unlink($temp); + + reset_mbstring_encoding(); + + return ''; // Blank document, File does exist, It's just blank. + } + + reset_mbstring_encoding(); + + fseek( $temphandle, 0 ); // Skip back to the start of the file being written to + $contents = ''; + + while ( ! feof($temphandle) ) + $contents .= fread($temphandle, 8192); + + fclose($temphandle); + unlink($temp); + return $contents; + } + /** + * @param string $file + * @return array + */ + public function get_contents_array($file) { + return explode("\n", $this->get_contents($file) ); + } + + /** + * @param string $file + * @param string $contents + * @param int|bool $mode + * @return bool + */ + public function put_contents($file, $contents, $mode = false ) { + $temp = wp_tempnam( $file ); + if ( ! $temphandle = @fopen($temp, 'w+') ) { + unlink($temp); + return false; + } + + // The FTP class uses string functions internally during file download/upload + mbstring_binary_safe_encoding(); + + $bytes_written = fwrite( $temphandle, $contents ); + if ( false === $bytes_written || $bytes_written != strlen( $contents ) ) { + fclose( $temphandle ); + unlink( $temp ); + + reset_mbstring_encoding(); + + return false; + } + + fseek( $temphandle, 0 ); // Skip back to the start of the file being written to + + $ret = $this->ftp->fput($file, $temphandle); + + reset_mbstring_encoding(); + + fclose($temphandle); + unlink($temp); + + $this->chmod($file, $mode); + + return $ret; + } + + public function cwd() { + $cwd = $this->ftp->pwd(); + if ( $cwd ) + $cwd = trailingslashit($cwd); + return $cwd; + } + + public function chdir($file) { + return $this->ftp->chdir($file); + } + + /** + * @param string $file + * @param bool $group + * @param bool $recursive + */ + public function chgrp($file, $group, $recursive = false ) { + return false; + } + + /** + * @param string $file + * @param int|bool $mode + * @param bool $recursive + * @return bool + */ + public function chmod($file, $mode = false, $recursive = false ) { + if ( ! $mode ) { + if ( $this->is_file($file) ) + $mode = FS_CHMOD_FILE; + elseif ( $this->is_dir($file) ) + $mode = FS_CHMOD_DIR; + else + return false; + } + + // chmod any sub-objects if recursive. + if ( $recursive && $this->is_dir($file) ) { + $filelist = $this->dirlist($file); + foreach ( (array)$filelist as $filename => $filemeta ) + $this->chmod($file . '/' . $filename, $mode, $recursive); + } + + // chmod the file or directory + return $this->ftp->chmod($file, $mode); + } + + /** + * @param string $file + * @return string + */ + public function owner($file) { + $dir = $this->dirlist($file); + return $dir[$file]['owner']; + } + /** + * @param string $file + * @return string + */ + public function getchmod($file) { + $dir = $this->dirlist($file); + return $dir[$file]['permsn']; + } + /** + * @param string $file + * @return string + */ + public function group($file) { + $dir = $this->dirlist($file); + return $dir[$file]['group']; + } + /** + * @param string $source + * @param string $destination + * @param bool $overwrite + * @param int|bool $mode + * @return bool + */ + public function copy($source, $destination, $overwrite = false, $mode = false) { + if ( ! $overwrite && $this->exists($destination) ) + return false; + + $content = $this->get_contents($source); + if ( false === $content ) + return false; + + return $this->put_contents($destination, $content, $mode); + } + /** + * @param string $source + * @param string $destination + * @param bool $overwrite + * @return bool + */ + public function move($source, $destination, $overwrite = false ) { + return $this->ftp->rename($source, $destination); + } + /** + * @param string $file + * @param bool $recursive + * @param string $type + * @return bool + */ + public function delete($file, $recursive = false, $type = false) { + if ( empty($file) ) + return false; + if ( 'f' == $type || $this->is_file($file) ) + return $this->ftp->delete($file); + if ( !$recursive ) + return $this->ftp->rmdir($file); + + return $this->ftp->mdel($file); + } + + /** + * @param string $file + * @return bool + */ + public function exists( $file ) { + $list = $this->ftp->nlist( $file ); + return !empty( $list ); //empty list = no file, so invert. + // Return $this->ftp->is_exists($file); has issues with ABOR+426 responses on the ncFTPd server. + } + + /** + * @param string $file + * @return bool + */ + public function is_file($file) { + if ( $this->is_dir($file) ) + return false; + if ( $this->exists($file) ) + return true; + return false; + } + + /** + * @param string $path + * @return bool + */ + public function is_dir($path) { + $cwd = $this->cwd(); + if ( $this->chdir($path) ) { + $this->chdir($cwd); + return true; + } + return false; + } + + /** + * @param string $file + * @return bool + */ + public function is_readable($file) { + return true; + } + + /** + * @param string $file + * @return bool + */ + public function is_writable($file) { + return true; + } + + /** + * @param string $file + * @return bool + */ + public function atime($file) { + return false; + } + + /** + * @param string $file + * @return int + */ + public function mtime($file) { + return $this->ftp->mdtm($file); + } + + /** + * @param string $file + * @return int + */ + public function size($file) { + return $this->ftp->filesize($file); + } + /** + * @param string $file + * @param int $time + * @param int $atime + * @return bool + */ + public function touch($file, $time = 0, $atime = 0 ) { + return false; + } + + /** + * @param string $path + * @param mixed $chmod + * @param mixed $chown + * @param mixed $chgrp + * @return bool + */ + public function mkdir($path, $chmod = false, $chown = false, $chgrp = false ) { + $path = untrailingslashit($path); + if ( empty($path) ) + return false; + + if ( ! $this->ftp->mkdir($path) ) + return false; + if ( ! $chmod ) + $chmod = FS_CHMOD_DIR; + $this->chmod($path, $chmod); + if ( $chown ) + $this->chown($path, $chown); + if ( $chgrp ) + $this->chgrp($path, $chgrp); + return true; + } + + /** + * @param sting $path + * @param bool $recursive + */ + public function rmdir($path, $recursive = false ) { + $this->delete($path, $recursive); + } + + /** + * @param string $path + * @param bool $include_hidden + * @param bool $recursive + * @return bool|array + */ + public function dirlist($path = '.', $include_hidden = true, $recursive = false ) { + if ( $this->is_file($path) ) { + $limit_file = basename($path); + $path = dirname($path) . '/'; + } else { + $limit_file = false; + } + + mbstring_binary_safe_encoding(); + + $list = $this->ftp->dirlist($path); + if ( empty( $list ) && ! $this->exists( $path ) ) { + + reset_mbstring_encoding(); + + return false; + } + + $ret = array(); + foreach ( $list as $struc ) { + + if ( '.' == $struc['name'] || '..' == $struc['name'] ) + continue; + + if ( ! $include_hidden && '.' == $struc['name'][0] ) + continue; + + if ( $limit_file && $struc['name'] != $limit_file ) + continue; + + if ( 'd' == $struc['type'] ) { + if ( $recursive ) + $struc['files'] = $this->dirlist($path . '/' . $struc['name'], $include_hidden, $recursive); + else + $struc['files'] = array(); + } + + // Replace symlinks formatted as "source -> target" with just the source name + if ( $struc['islink'] ) + $struc['name'] = preg_replace( '/(\s*->\s*.*)$/', '', $struc['name'] ); + + $ret[ $struc['name'] ] = $struc; + } + + reset_mbstring_encoding(); + + return $ret; + } + + public function __destruct() { + $this->ftp->quit(); + } +} diff --git a/wp-admin/includes/class-wp-filesystem-ssh2.php b/wp-admin/includes/class-wp-filesystem-ssh2.php new file mode 100644 index 0000000..dbfe707 --- /dev/null +++ b/wp-admin/includes/class-wp-filesystem-ssh2.php @@ -0,0 +1,505 @@ +method = 'ssh2'; + $this->errors = new WP_Error(); + + //Check if possible to use ssh2 functions. + if ( ! extension_loaded('ssh2') ) { + $this->errors->add('no_ssh2_ext', __('The ssh2 PHP extension is not available')); + return false; + } + if ( !function_exists('stream_get_contents') ) { + $this->errors->add('ssh2_php_requirement', __('The ssh2 PHP extension is available, however, we require the PHP5 function stream_get_contents()')); + return false; + } + + // Set defaults: + if ( empty($opt['port']) ) + $this->options['port'] = 22; + else + $this->options['port'] = $opt['port']; + + if ( empty($opt['hostname']) ) + $this->errors->add('empty_hostname', __('SSH2 hostname is required')); + else + $this->options['hostname'] = $opt['hostname']; + + if ( ! empty($opt['base']) ) + $this->wp_base = $opt['base']; + + // Check if the options provided are OK. + if ( !empty ($opt['public_key']) && !empty ($opt['private_key']) ) { + $this->options['public_key'] = $opt['public_key']; + $this->options['private_key'] = $opt['private_key']; + + $this->options['hostkey'] = array('hostkey' => 'ssh-rsa'); + + $this->keys = true; + } elseif ( empty ($opt['username']) ) { + $this->errors->add('empty_username', __('SSH2 username is required')); + } + + if ( !empty($opt['username']) ) + $this->options['username'] = $opt['username']; + + if ( empty ($opt['password']) ) { + // Password can be blank if we are using keys. + if ( !$this->keys ) + $this->errors->add('empty_password', __('SSH2 password is required')); + } else { + $this->options['password'] = $opt['password']; + } + + } + + public function connect() { + if ( ! $this->keys ) { + $this->link = @ssh2_connect($this->options['hostname'], $this->options['port']); + } else { + $this->link = @ssh2_connect($this->options['hostname'], $this->options['port'], $this->options['hostkey']); + } + + if ( ! $this->link ) { + $this->errors->add('connect', sprintf(__('Failed to connect to SSH2 Server %1$s:%2$s'), $this->options['hostname'], $this->options['port'])); + return false; + } + + if ( !$this->keys ) { + if ( ! @ssh2_auth_password($this->link, $this->options['username'], $this->options['password']) ) { + $this->errors->add('auth', sprintf(__('Username/Password incorrect for %s'), $this->options['username'])); + return false; + } + } else { + if ( ! @ssh2_auth_pubkey_file($this->link, $this->options['username'], $this->options['public_key'], $this->options['private_key'], $this->options['password'] ) ) { + $this->errors->add('auth', sprintf(__('Public and Private keys incorrect for %s'), $this->options['username'])); + return false; + } + } + + $this->sftp_link = ssh2_sftp($this->link); + + return true; + } + + /** + * @param string $command + * @param bool $returnbool + */ + public function run_command( $command, $returnbool = false) { + + if ( ! $this->link ) + return false; + + if ( ! ($stream = ssh2_exec($this->link, $command)) ) { + $this->errors->add('command', sprintf(__('Unable to perform command: %s'), $command)); + } else { + stream_set_blocking( $stream, true ); + stream_set_timeout( $stream, FS_TIMEOUT ); + $data = stream_get_contents( $stream ); + fclose( $stream ); + + if ( $returnbool ) + return ( $data === false ) ? false : '' != trim($data); + else + return $data; + } + return false; + } + + /** + * @param string $file + * @return string|false + */ + public function get_contents( $file ) { + $file = ltrim($file, '/'); + return file_get_contents('ssh2.sftp://' . $this->sftp_link . '/' . $file); + } + + /** + * @param string $file + * @return array + */ + public function get_contents_array($file) { + $file = ltrim($file, '/'); + return file('ssh2.sftp://' . $this->sftp_link . '/' . $file); + } + + /** + * @param string $file + * @param string $contents + * @param bool|int $mode + * @return bool + */ + public function put_contents($file, $contents, $mode = false ) { + $ret = file_put_contents( 'ssh2.sftp://' . $this->sftp_link . '/' . ltrim( $file, '/' ), $contents ); + + if ( $ret !== strlen( $contents ) ) + return false; + + $this->chmod($file, $mode); + + return true; + } + + public function cwd() { + $cwd = $this->run_command('pwd'); + if ( $cwd ) + $cwd = trailingslashit($cwd); + return $cwd; + } + + /** + * @param string $dir + * @return bool + */ + public function chdir($dir) { + return $this->run_command('cd ' . $dir, true); + } + + /** + * @param string $file + * @param string $group + * @param bool $recursive + */ + public function chgrp($file, $group, $recursive = false ) { + if ( ! $this->exists($file) ) + return false; + if ( ! $recursive || ! $this->is_dir($file) ) + return $this->run_command(sprintf('chgrp %s %s', escapeshellarg($group), escapeshellarg($file)), true); + return $this->run_command(sprintf('chgrp -R %s %s', escapeshellarg($group), escapeshellarg($file)), true); + } + + /** + * @param string $file + * @param int $mode + * @param bool $recursive + * @return bool + */ + public function chmod($file, $mode = false, $recursive = false) { + if ( ! $this->exists($file) ) + return false; + + if ( ! $mode ) { + if ( $this->is_file($file) ) + $mode = FS_CHMOD_FILE; + elseif ( $this->is_dir($file) ) + $mode = FS_CHMOD_DIR; + else + return false; + } + + if ( ! $recursive || ! $this->is_dir($file) ) + return $this->run_command(sprintf('chmod %o %s', $mode, escapeshellarg($file)), true); + return $this->run_command(sprintf('chmod -R %o %s', $mode, escapeshellarg($file)), true); + } + + /** + * Change the ownership of a file / folder. + * + * @since Unknown + * + * @param string $file Path to the file. + * @param string|int $owner A user name or number. + * @param bool $recursive Optional. If set True changes file owner recursivly. Defaults to False. + * @return bool|string Returns true on success or false on failure. + */ + public function chown( $file, $owner, $recursive = false ) { + if ( ! $this->exists($file) ) + return false; + if ( ! $recursive || ! $this->is_dir($file) ) + return $this->run_command(sprintf('chown %s %s', escapeshellarg($owner), escapeshellarg($file)), true); + return $this->run_command(sprintf('chown -R %s %s', escapeshellarg($owner), escapeshellarg($file)), true); + } + + /** + * @param string $file + * @return string|false + */ + public function owner($file) { + $owneruid = @fileowner('ssh2.sftp://' . $this->sftp_link . '/' . ltrim($file, '/')); + if ( ! $owneruid ) + return false; + if ( ! function_exists('posix_getpwuid') ) + return $owneruid; + $ownerarray = posix_getpwuid($owneruid); + return $ownerarray['name']; + } + /** + * @param string $file + * @return string + */ + public function getchmod($file) { + return substr( decoct( @fileperms( 'ssh2.sftp://' . $this->sftp_link . '/' . ltrim( $file, '/' ) ) ), -3 ); + } + + /** + * @param string $file + * @return string|false + */ + public function group($file) { + $gid = @filegroup('ssh2.sftp://' . $this->sftp_link . '/' . ltrim($file, '/')); + if ( ! $gid ) + return false; + if ( ! function_exists('posix_getgrgid') ) + return $gid; + $grouparray = posix_getgrgid($gid); + return $grouparray['name']; + } + + /** + * @param string $source + * @param string $destination + * @param bool $overwrite + * @param int|bool $mode + * @return bool + */ + public function copy($source, $destination, $overwrite = false, $mode = false) { + if ( ! $overwrite && $this->exists($destination) ) + return false; + $content = $this->get_contents($source); + if ( false === $content) + return false; + return $this->put_contents($destination, $content, $mode); + } + + /** + * @param string $source + * @param string $destination + * @param bool $overwrite + * @return bool + */ + public function move($source, $destination, $overwrite = false) { + return @ssh2_sftp_rename( $this->sftp_link, $source, $destination ); + } + + /** + * @param string $file + * @param bool $recursive + * @param string|bool $type + * @return bool + */ + public function delete($file, $recursive = false, $type = false) { + if ( 'f' == $type || $this->is_file($file) ) + return ssh2_sftp_unlink($this->sftp_link, $file); + if ( ! $recursive ) + return ssh2_sftp_rmdir($this->sftp_link, $file); + $filelist = $this->dirlist($file); + if ( is_array($filelist) ) { + foreach ( $filelist as $filename => $fileinfo) { + $this->delete($file . '/' . $filename, $recursive, $fileinfo['type']); + } + } + return ssh2_sftp_rmdir($this->sftp_link, $file); + } + + /** + * @param string $file + * @return bool + */ + public function exists($file) { + $file = ltrim($file, '/'); + return file_exists('ssh2.sftp://' . $this->sftp_link . '/' . $file); + } + /** + * @param string $file + * @return bool + */ + public function is_file($file) { + $file = ltrim($file, '/'); + return is_file('ssh2.sftp://' . $this->sftp_link . '/' . $file); + } + /** + * @param string $path + * @return bool + */ + public function is_dir($path) { + $path = ltrim($path, '/'); + return is_dir('ssh2.sftp://' . $this->sftp_link . '/' . $path); + } + /** + * @param string $file + * @return bool + */ + public function is_readable($file) { + $file = ltrim($file, '/'); + return is_readable('ssh2.sftp://' . $this->sftp_link . '/' . $file); + } + /** + * @param string $file + * @return bool + */ + public function is_writable($file) { + $file = ltrim($file, '/'); + return is_writable('ssh2.sftp://' . $this->sftp_link . '/' . $file); + } + /** + * @param string $file + * @return int + */ + public function atime($file) { + $file = ltrim($file, '/'); + return fileatime('ssh2.sftp://' . $this->sftp_link . '/' . $file); + } + + /** + * @param string $file + * @return int + */ + public function mtime($file) { + $file = ltrim($file, '/'); + return filemtime('ssh2.sftp://' . $this->sftp_link . '/' . $file); + } + + /** + * @param string $file + * @return int + */ + public function size($file) { + $file = ltrim($file, '/'); + return filesize('ssh2.sftp://' . $this->sftp_link . '/' . $file); + } + + /** + * @param string $file + * @param int $time + * @param int $atime + */ + public function touch($file, $time = 0, $atime = 0) { + //Not implemented. + } + + /** + * @param string $path + * @param mixed $chmod + * @param mixed $chown + * @param mixed $chgrp + * @return bool + */ + public function mkdir($path, $chmod = false, $chown = false, $chgrp = false) { + $path = untrailingslashit($path); + if ( empty($path) ) + return false; + + if ( ! $chmod ) + $chmod = FS_CHMOD_DIR; + if ( ! ssh2_sftp_mkdir($this->sftp_link, $path, $chmod, true) ) + return false; + if ( $chown ) + $this->chown($path, $chown); + if ( $chgrp ) + $this->chgrp($path, $chgrp); + return true; + } + + /** + * @param string $path + * @param bool $recursive + * @return bool + */ + public function rmdir($path, $recursive = false) { + return $this->delete($path, $recursive); + } + + /** + * @param string $path + * @param bool $include_hidden + * @param bool $recursive + * @return bool|array + */ + public function dirlist($path, $include_hidden = true, $recursive = false) { + if ( $this->is_file($path) ) { + $limit_file = basename($path); + $path = dirname($path); + } else { + $limit_file = false; + } + + if ( ! $this->is_dir($path) ) + return false; + + $ret = array(); + $dir = @dir('ssh2.sftp://' . $this->sftp_link .'/' . ltrim($path, '/') ); + + if ( ! $dir ) + return false; + + while (false !== ($entry = $dir->read()) ) { + $struc = array(); + $struc['name'] = $entry; + + if ( '.' == $struc['name'] || '..' == $struc['name'] ) + continue; //Do not care about these folders. + + if ( ! $include_hidden && '.' == $struc['name'][0] ) + continue; + + if ( $limit_file && $struc['name'] != $limit_file ) + continue; + + $struc['perms'] = $this->gethchmod($path.'/'.$entry); + $struc['permsn'] = $this->getnumchmodfromh($struc['perms']); + $struc['number'] = false; + $struc['owner'] = $this->owner($path.'/'.$entry); + $struc['group'] = $this->group($path.'/'.$entry); + $struc['size'] = $this->size($path.'/'.$entry); + $struc['lastmodunix']= $this->mtime($path.'/'.$entry); + $struc['lastmod'] = date('M j',$struc['lastmodunix']); + $struc['time'] = date('h:i:s',$struc['lastmodunix']); + $struc['type'] = $this->is_dir($path.'/'.$entry) ? 'd' : 'f'; + + if ( 'd' == $struc['type'] ) { + if ( $recursive ) + $struc['files'] = $this->dirlist($path . '/' . $struc['name'], $include_hidden, $recursive); + else + $struc['files'] = array(); + } + + $ret[ $struc['name'] ] = $struc; + } + $dir->close(); + unset($dir); + return $ret; + } +} diff --git a/wp-admin/includes/class-wp-importer.php b/wp-admin/includes/class-wp-importer.php new file mode 100644 index 0000000..b54573f --- /dev/null +++ b/wp-admin/includes/class-wp-importer.php @@ -0,0 +1,302 @@ +prepare( "SELECT post_id, meta_value FROM $wpdb->postmeta WHERE meta_key = '%s' LIMIT %d,%d", $meta_key, $offset, $limit ); + $results = $wpdb->get_results( $sql ); + + // Increment offset + $offset = ( $limit + $offset ); + + if ( !empty( $results ) ) { + foreach ( $results as $r ) { + // Set permalinks into array + $hashtable[$r->meta_value] = intval( $r->post_id ); + } + } + } while ( count( $results ) == $limit ); + + // Unset to save memory. + unset( $results, $r ); + + return $hashtable; + } + + /** + * Return count of imported permalinks from WordPress database + * + * @param string $bid + * @return int + */ + public function count_imported_posts( $importer_name, $bid ) { + global $wpdb; + + $count = 0; + + // Get count of permalinks + $meta_key = $importer_name . '_' . $bid . '_permalink'; + $sql = $wpdb->prepare( "SELECT COUNT( post_id ) AS cnt FROM $wpdb->postmeta WHERE meta_key = '%s'", $meta_key ); + + $result = $wpdb->get_results( $sql ); + + if ( !empty( $result ) ) + $count = intval( $result[0]->cnt ); + + // Unset to save memory. + unset( $results ); + + return $count; + } + + /** + * Set array with imported comments from WordPress database + * + * @param string $bid + * @return array + */ + public function get_imported_comments( $bid ) { + global $wpdb; + + $hashtable = array(); + + $limit = 100; + $offset = 0; + + // Grab all comments in chunks + do { + $sql = $wpdb->prepare( "SELECT comment_ID, comment_agent FROM $wpdb->comments LIMIT %d,%d", $offset, $limit ); + $results = $wpdb->get_results( $sql ); + + // Increment offset + $offset = ( $limit + $offset ); + + if ( !empty( $results ) ) { + foreach ( $results as $r ) { + // Explode comment_agent key + list ( $ca_bid, $source_comment_id ) = explode( '-', $r->comment_agent ); + $source_comment_id = intval( $source_comment_id ); + + // Check if this comment came from this blog + if ( $bid == $ca_bid ) { + $hashtable[$source_comment_id] = intval( $r->comment_ID ); + } + } + } + } while ( count( $results ) == $limit ); + + // Unset to save memory. + unset( $results, $r ); + + return $hashtable; + } + + public function set_blog( $blog_id ) { + if ( is_numeric( $blog_id ) ) { + $blog_id = (int) $blog_id; + } else { + $blog = 'http://' . preg_replace( '#^https?://#', '', $blog_id ); + if ( ( !$parsed = parse_url( $blog ) ) || empty( $parsed['host'] ) ) { + fwrite( STDERR, "Error: can not determine blog_id from $blog_id\n" ); + exit(); + } + if ( empty( $parsed['path'] ) ) + $parsed['path'] = '/'; + $blog = get_blog_details( array( 'domain' => $parsed['host'], 'path' => $parsed['path'] ) ); + if ( !$blog ) { + fwrite( STDERR, "Error: Could not find blog\n" ); + exit(); + } + $blog_id = (int) $blog->blog_id; + } + + if ( function_exists( 'is_multisite' ) ) { + if ( is_multisite() ) + switch_to_blog( $blog_id ); + } + + return $blog_id; + } + + public function set_user( $user_id ) { + if ( is_numeric( $user_id ) ) { + $user_id = (int) $user_id; + } else { + $user_id = (int) username_exists( $user_id ); + } + + if ( !$user_id || !wp_set_current_user( $user_id ) ) { + fwrite( STDERR, "Error: can not find user\n" ); + exit(); + } + + return $user_id; + } + + /** + * Sort by strlen, longest string first + * + * @param string $a + * @param string $b + * @return int + */ + public function cmpr_strlen( $a, $b ) { + return strlen( $b ) - strlen( $a ); + } + + /** + * GET URL + * + * @param string $url + * @param string $username + * @param string $password + * @param bool $head + * @return array + */ + public function get_page( $url, $username = '', $password = '', $head = false ) { + // Increase the timeout + add_filter( 'http_request_timeout', array( $this, 'bump_request_timeout' ) ); + + $headers = array(); + $args = array(); + if ( true === $head ) + $args['method'] = 'HEAD'; + if ( !empty( $username ) && !empty( $password ) ) + $headers['Authorization'] = 'Basic ' . base64_encode( "$username:$password" ); + + $args['headers'] = $headers; + + return wp_safe_remote_request( $url, $args ); + } + + /** + * Bump up the request timeout for http requests + * + * @param int $val + * @return int + */ + public function bump_request_timeout( $val ) { + return 60; + } + + /** + * Check if user has exceeded disk quota + * + * @return bool + */ + public function is_user_over_quota() { + if ( function_exists( 'upload_is_user_over_quota' ) ) { + if ( upload_is_user_over_quota( 1 ) ) { + echo "Sorry, you have used your upload quota.\n"; + return true; + } + } + + return false; + } + + /** + * Replace newlines, tabs, and multiple spaces with a single space + * + * @param string $string + * @return string + */ + public function min_whitespace( $string ) { + return preg_replace( '|[\r\n\t ]+|', ' ', $string ); + } + + /** + * Reset global variables that grow out of control during imports + * + * @return void + */ + public function stop_the_insanity() { + global $wpdb, $wp_actions; + // Or define( 'WP_IMPORTING', true ); + $wpdb->queries = array(); + // Reset $wp_actions to keep it from growing out of control + $wp_actions = array(); + } +} + +/** + * Returns value of command line params. + * Exits when a required param is not set. + * + * @param string $param + * @param bool $required + * @return mixed + */ +function get_cli_args( $param, $required = false ) { + $args = $_SERVER['argv']; + + $out = array(); + + $last_arg = null; + $return = null; + + $il = sizeof( $args ); + + for ( $i = 1, $il; $i < $il; $i++ ) { + if ( (bool) preg_match( "/^--(.+)/", $args[$i], $match ) ) { + $parts = explode( "=", $match[1] ); + $key = preg_replace( "/[^a-z0-9]+/", "", $parts[0] ); + + if ( isset( $parts[1] ) ) { + $out[$key] = $parts[1]; + } else { + $out[$key] = true; + } + + $last_arg = $key; + } else if ( (bool) preg_match( "/^-([a-zA-Z0-9]+)/", $args[$i], $match ) ) { + for ( $j = 0, $jl = strlen( $match[1] ); $j < $jl; $j++ ) { + $key = $match[1]{$j}; + $out[$key] = true; + } + + $last_arg = $key; + } else if ( $last_arg !== null ) { + $out[$last_arg] = $args[$i]; + } + } + + // Check array for specified param + if ( isset( $out[$param] ) ) { + // Set return value + $return = $out[$param]; + } + + // Check for missing required param + if ( !isset( $out[$param] ) && $required ) { + // Display message and exit + echo "\"$param\" parameter is required but was not specified\n"; + exit(); + } + + return $return; +} diff --git a/wp-admin/includes/class-wp-links-list-table.php b/wp-admin/includes/class-wp-links-list-table.php new file mode 100644 index 0000000..3a39063 --- /dev/null +++ b/wp-admin/includes/class-wp-links-list-table.php @@ -0,0 +1,211 @@ + 'bookmarks', + 'screen' => isset( $args['screen'] ) ? $args['screen'] : null, + ) ); + } + + public function ajax_user_can() { + return current_user_can( 'manage_links' ); + } + + public function prepare_items() { + global $cat_id, $s, $orderby, $order; + + wp_reset_vars( array( 'action', 'cat_id', 'link_id', 'orderby', 'order', 's' ) ); + + $args = array( 'hide_invisible' => 0, 'hide_empty' => 0 ); + + if ( 'all' != $cat_id ) + $args['category'] = $cat_id; + if ( !empty( $s ) ) + $args['search'] = $s; + if ( !empty( $orderby ) ) + $args['orderby'] = $orderby; + if ( !empty( $order ) ) + $args['order'] = $order; + + $this->items = get_bookmarks( $args ); + } + + public function no_items() { + _e( 'No links found.' ); + } + + protected function get_bulk_actions() { + $actions = array(); + $actions['delete'] = __( 'Delete' ); + + return $actions; + } + + protected function extra_tablenav( $which ) { + global $cat_id; + + if ( 'top' != $which ) + return; +?> +
    + $cat_id, + 'name' => 'cat_id', + 'taxonomy' => 'link_category', + 'show_option_all' => __( 'All categories' ), + 'hide_empty' => true, + 'hierarchical' => 1, + 'show_count' => 0, + 'orderby' => 'name', + ); + + echo ''; + wp_dropdown_categories( $dropdown_options ); + submit_button( __( 'Filter' ), 'button', 'filter_action', false, array( 'id' => 'post-query-submit' ) ); +?> +
    + '', + 'name' => _x( 'Name', 'link name' ), + 'url' => __( 'URL' ), + 'categories' => __( 'Categories' ), + 'rel' => __( 'Relationship' ), + 'visible' => __( 'Visible' ), + 'rating' => __( 'Rating' ) + ); + } + + protected function get_sortable_columns() { + return array( + 'name' => 'name', + 'url' => 'url', + 'visible' => 'visible', + 'rating' => 'rating' + ); + } + + public function display_rows() { + global $cat_id; + + $alt = 0; + + foreach ( $this->items as $link ) { + $link = sanitize_bookmark( $link ); + $link->link_name = esc_attr( $link->link_name ); + $link->link_category = wp_get_link_cats( $link->link_id ); + + $short_url = url_shorten( $link->link_url ); + + $visible = ( $link->link_visible == 'Y' ) ? __( 'Yes' ) : __( 'No' ); + $rating = $link->link_rating; + $style = ( $alt++ % 2 ) ? '' : ' class="alternate"'; + + $edit_link = get_edit_bookmark_link( $link ); +?> + > +get_column_info(); + + foreach ( $columns as $column_name => $column_display_name ) { + $class = "class='column-$column_name'"; + + $style = ''; + if ( in_array( $column_name, $hidden ) ) + $style = ' style="display:none;"'; + + $attributes = $class . $style; + + switch ( $column_name ) { + case 'cb': ?> + + + + + link_name ) ) . "'>$link->link_name
    "; + + $actions = array(); + $actions['edit'] = '' . __( 'Edit' ) . ''; + $actions['delete'] = "link_id ) . "' onclick=\"if ( confirm( '" . esc_js( sprintf( __( "You are about to delete this link '%s'\n 'Cancel' to stop, 'OK' to delete." ), $link->link_name ) ) . "' ) ) { return true;}return false;\">" . __( 'Delete' ) . ""; + echo $this->row_actions( $actions ); + + echo ''; + break; + case 'url': + echo "link_name ) )."'>$short_url"; + break; + case 'categories': + ?>>link_category as $category ) { + $cat = get_term( $category, 'link_category', OBJECT, 'display' ); + if ( is_wp_error( $cat ) ) + echo $cat->get_error_message(); + $cat_name = $cat->name; + if ( $cat_id != $category ) + $cat_name = "$cat_name"; + $cat_names[] = $cat_name; + } + echo implode( ', ', $cat_names ); + ?>>link_rel ) ? '
    ' : $link->link_rel; ?>>> + >link_id ); + ?> + + + '', + 'singular' => '', + 'ajax' => false, + 'screen' => null, + ) ); + + $this->screen = convert_to_screen( $args['screen'] ); + + add_filter( "manage_{$this->screen->id}_columns", array( $this, 'get_columns' ), 0 ); + + if ( !$args['plural'] ) + $args['plural'] = $this->screen->base; + + $args['plural'] = sanitize_key( $args['plural'] ); + $args['singular'] = sanitize_key( $args['singular'] ); + + $this->_args = $args; + + if ( $args['ajax'] ) { + // wp_enqueue_script( 'list-table' ); + add_action( 'admin_footer', array( $this, '_js_vars' ) ); + } + + if ( empty( $this->modes ) ) { + $this->modes = array( + 'list' => __( 'List View' ), + 'excerpt' => __( 'Excerpt View' ) + ); + } + } + + /** + * Make private properties readable for backwards compatibility. + * + * @since 4.0.0 + * @access public + * + * @param string $name Property to get. + * @return mixed Property. + */ + public function __get( $name ) { + return $this->$name; + } + + /** + * Make private properties settable for backwards compatibility. + * + * @since 4.0.0 + * @access public + * + * @param string $name Property to set. + * @param mixed $value Property value. + * @return mixed Newly-set property. + */ + public function __set( $name, $value ) { + return $this->$name = $value; + } + + /** + * Make private properties checkable for backwards compatibility. + * + * @since 4.0.0 + * @access public + * + * @param string $name Property to check if set. + * @return bool Whether the property is set. + */ + public function __isset( $name ) { + return isset( $this->$name ); + } + + /** + * Make private properties un-settable for backwards compatibility. + * + * @since 4.0.0 + * @access public + * + * @param string $name Property to unset. + */ + public function __unset( $name ) { + unset( $this->$name ); + } + + /** + * Make private/protected methods readable for backwards compatibility. + * + * @since 4.0.0 + * @access public + * + * @param callable $name Method to call. + * @param array $arguments Arguments to pass when calling. + * @return mixed|bool Return value of the callback, false otherwise. + */ + public function __call( $name, $arguments ) { + return call_user_func_array( array( $this, $name ), $arguments ); + } + + /** + * Checks the current user's permissions + * + * @since 3.1.0 + * @access public + * @abstract + */ + public function ajax_user_can() { + die( 'function WP_List_Table::ajax_user_can() must be over-ridden in a sub-class.' ); + } + + /** + * Prepares the list of items for displaying. + * @uses WP_List_Table::set_pagination_args() + * + * @since 3.1.0 + * @access public + * @abstract + */ + public function prepare_items() { + die( 'function WP_List_Table::prepare_items() must be over-ridden in a sub-class.' ); + } + + /** + * An internal method that sets all the necessary pagination arguments + * + * @param array $args An associative array with information about the pagination + * @access protected + */ + protected function set_pagination_args( $args ) { + $args = wp_parse_args( $args, array( + 'total_items' => 0, + 'total_pages' => 0, + 'per_page' => 0, + ) ); + + if ( !$args['total_pages'] && $args['per_page'] > 0 ) + $args['total_pages'] = ceil( $args['total_items'] / $args['per_page'] ); + + // Redirect if page number is invalid and headers are not already sent. + if ( ! headers_sent() && ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) && $args['total_pages'] > 0 && $this->get_pagenum() > $args['total_pages'] ) { + wp_redirect( add_query_arg( 'paged', $args['total_pages'] ) ); + exit; + } + + $this->_pagination_args = $args; + } + + /** + * Access the pagination args. + * + * @since 3.1.0 + * @access public + * + * @param string $key Pagination argument to retrieve. Common values include 'total_items', + * 'total_pages', 'per_page', or 'infinite_scroll'. + * @return int Number of items that correspond to the given pagination argument. + */ + public function get_pagination_arg( $key ) { + if ( 'page' == $key ) + return $this->get_pagenum(); + + if ( isset( $this->_pagination_args[$key] ) ) + return $this->_pagination_args[$key]; + } + + /** + * Whether the table has items to display or not + * + * @since 3.1.0 + * @access public + * + * @return bool + */ + public function has_items() { + return !empty( $this->items ); + } + + /** + * Message to be displayed when there are no items + * + * @since 3.1.0 + * @access public + */ + public function no_items() { + _e( 'No items found.' ); + } + + /** + * Display the search box. + * + * @since 3.1.0 + * @access public + * + * @param string $text The search button text + * @param string $input_id The search input id + */ + public function search_box( $text, $input_id ) { + if ( empty( $_REQUEST['s'] ) && !$this->has_items() ) + return; + + $input_id = $input_id . '-search-input'; + + if ( ! empty( $_REQUEST['orderby'] ) ) + echo ''; + if ( ! empty( $_REQUEST['order'] ) ) + echo ''; + if ( ! empty( $_REQUEST['post_mime_type'] ) ) + echo ''; + if ( ! empty( $_REQUEST['detached'] ) ) + echo ''; +?> + + link ) with the list + * of views available on this table. + * + * @since 3.1.0 + * @access protected + * + * @return array + */ + protected function get_views() { + return array(); + } + + /** + * Display the list of views available on this table. + * + * @since 3.1.0 + * @access public + */ + public function views() { + $views = $this->get_views(); + /** + * Filter the list of available list table views. + * + * The dynamic portion of the hook name, `$this->screen->id`, refers + * to the ID of the current screen, usually a string. + * + * @since 3.5.0 + * + * @param array $views An array of available list table views. + */ + $views = apply_filters( "views_{$this->screen->id}", $views ); + + if ( empty( $views ) ) + return; + + echo "
      \n"; + foreach ( $views as $class => $view ) { + $views[ $class ] = "\t
    • $view"; + } + echo implode( " |
    • \n", $views ) . "\n"; + echo "
    "; + } + + /** + * Get an associative array ( option_name => option_title ) with the list + * of bulk actions available on this table. + * + * @since 3.1.0 + * @access protected + * + * @return array + */ + protected function get_bulk_actions() { + return array(); + } + + /** + * Display the bulk actions dropdown. + * + * @since 3.1.0 + * @access protected + * + * @param string $which The location of the bulk actions: 'top' or 'bottom'. + * This is designated as optional for backwards-compatibility. + */ + protected function bulk_actions( $which = '' ) { + if ( is_null( $this->_actions ) ) { + $no_new_actions = $this->_actions = $this->get_bulk_actions(); + /** + * Filter the list table Bulk Actions drop-down. + * + * The dynamic portion of the hook name, `$this->screen->id`, refers + * to the ID of the current screen, usually a string. + * + * This filter can currently only be used to remove bulk actions. + * + * @since 3.5.0 + * + * @param array $actions An array of the available bulk actions. + */ + $this->_actions = apply_filters( "bulk_actions-{$this->screen->id}", $this->_actions ); + $this->_actions = array_intersect_assoc( $this->_actions, $no_new_actions ); + $two = ''; + } else { + $two = '2'; + } + + if ( empty( $this->_actions ) ) + return; + + echo ""; + echo "\n"; + + submit_button( __( 'Apply' ), 'action', false, false, array( 'id' => "doaction$two" ) ); + echo "\n"; + } + + /** + * Get the current action selected from the bulk actions dropdown. + * + * @since 3.1.0 + * @access public + * + * @return string|bool The action name or False if no action was selected + */ + public function current_action() { + if ( isset( $_REQUEST['filter_action'] ) && ! empty( $_REQUEST['filter_action'] ) ) + return false; + + if ( isset( $_REQUEST['action'] ) && -1 != $_REQUEST['action'] ) + return $_REQUEST['action']; + + if ( isset( $_REQUEST['action2'] ) && -1 != $_REQUEST['action2'] ) + return $_REQUEST['action2']; + + return false; + } + + /** + * Generate row actions div + * + * @since 3.1.0 + * @access protected + * + * @param array $actions The list of actions + * @param bool $always_visible Whether the actions should be always visible + * @return string + */ + protected function row_actions( $actions, $always_visible = false ) { + $action_count = count( $actions ); + $i = 0; + + if ( !$action_count ) + return ''; + + $out = '
    '; + foreach ( $actions as $action => $link ) { + ++$i; + ( $i == $action_count ) ? $sep = '' : $sep = ' | '; + $out .= "$link$sep"; + } + $out .= '
    '; + + return $out; + } + + /** + * Display a monthly dropdown for filtering items + * + * @since 3.1.0 + * @access protected + * + * @param string $post_type + */ + protected function months_dropdown( $post_type ) { + global $wpdb, $wp_locale; + + $months = $wpdb->get_results( $wpdb->prepare( " + SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month + FROM $wpdb->posts + WHERE post_type = %s + ORDER BY post_date DESC + ", $post_type ) ); + + /** + * Filter the 'Months' drop-down results. + * + * @since 3.7.0 + * + * @param object $months The months drop-down query results. + * @param string $post_type The post type. + */ + $months = apply_filters( 'months_dropdown_results', $months, $post_type ); + + $month_count = count( $months ); + + if ( !$month_count || ( 1 == $month_count && 0 == $months[0]->month ) ) + return; + + $m = isset( $_GET['m'] ) ? (int) $_GET['m'] : 0; +?> + + + + +
    +modes as $mode => $title ) { + $classes = array( 'view-' . $mode ); + if ( $current_mode == $mode ) + $classes[] = 'current'; + printf( + "%s\n", + esc_url( add_query_arg( 'mode', $mode ) ), + implode( ' ', $classes ), + $title + ); + } + ?> +
    +'; + + echo "" . number_format_i18n( get_comments_number() ) . ""; + + if ( $pending_comments ) + echo ''; + } + + /** + * Get the current page number + * + * @since 3.1.0 + * @access public + * + * @return int + */ + public function get_pagenum() { + $pagenum = isset( $_REQUEST['paged'] ) ? absint( $_REQUEST['paged'] ) : 0; + + if( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] ) + $pagenum = $this->_pagination_args['total_pages']; + + return max( 1, $pagenum ); + } + + /** + * Get number of items to display on a single page + * + * @since 3.1.0 + * @access protected + * + * @param string $option + * @param int $default + * @return int + */ + protected function get_items_per_page( $option, $default = 20 ) { + $per_page = (int) get_user_option( $option ); + if ( empty( $per_page ) || $per_page < 1 ) + $per_page = $default; + + /** + * Filter the number of items to be displayed on each page of the list table. + * + * The dynamic hook name, $option, refers to the `per_page` option depending + * on the type of list table in use. Possible values include: 'edit_comments_per_page', + * 'sites_network_per_page', 'site_themes_network_per_page', 'themes_network_per_page', + * 'users_network_per_page', 'edit_post_per_page', 'edit_page_per_page', + * 'edit_{$post_type}_per_page', etc. + * + * @since 2.9.0 + * + * @param int $per_page Number of items to be displayed. Default 20. + */ + return (int) apply_filters( $option, $per_page ); + } + + /** + * Display the pagination. + * + * @since 3.1.0 + * @access protected + * + * @param string $which + */ + protected function pagination( $which ) { + if ( empty( $this->_pagination_args ) ) { + return; + } + + $total_items = $this->_pagination_args['total_items']; + $total_pages = $this->_pagination_args['total_pages']; + $infinite_scroll = false; + if ( isset( $this->_pagination_args['infinite_scroll'] ) ) { + $infinite_scroll = $this->_pagination_args['infinite_scroll']; + } + + $output = '' . sprintf( _n( '1 item', '%s items', $total_items ), number_format_i18n( $total_items ) ) . ''; + + $current = $this->get_pagenum(); + + $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ); + + $current_url = remove_query_arg( array( 'hotkeys_highlight_last', 'hotkeys_highlight_first' ), $current_url ); + + $page_links = array(); + + $disable_first = $disable_last = ''; + if ( $current == 1 ) { + $disable_first = ' disabled'; + } + if ( $current == $total_pages ) { + $disable_last = ' disabled'; + } + $page_links[] = sprintf( "%s", + 'first-page' . $disable_first, + esc_attr__( 'Go to the first page' ), + esc_url( remove_query_arg( 'paged', $current_url ) ), + '«' + ); + + $page_links[] = sprintf( "%s", + 'prev-page' . $disable_first, + esc_attr__( 'Go to the previous page' ), + esc_url( add_query_arg( 'paged', max( 1, $current-1 ), $current_url ) ), + '‹' + ); + + if ( 'bottom' == $which ) { + $html_current_page = $current; + } else { + $html_current_page = sprintf( "%s", + '', + esc_attr__( 'Current page' ), + $current, + strlen( $total_pages ) + ); + } + $html_total_pages = sprintf( "%s", number_format_i18n( $total_pages ) ); + $page_links[] = '' . sprintf( _x( '%1$s of %2$s', 'paging' ), $html_current_page, $html_total_pages ) . ''; + + $page_links[] = sprintf( "%s", + 'next-page' . $disable_last, + esc_attr__( 'Go to the next page' ), + esc_url( add_query_arg( 'paged', min( $total_pages, $current+1 ), $current_url ) ), + '›' + ); + + $page_links[] = sprintf( "%s", + 'last-page' . $disable_last, + esc_attr__( 'Go to the last page' ), + esc_url( add_query_arg( 'paged', $total_pages, $current_url ) ), + '»' + ); + + $pagination_links_class = 'pagination-links'; + if ( ! empty( $infinite_scroll ) ) { + $pagination_links_class = ' hide-if-js'; + } + $output .= "\n" . join( "\n", $page_links ) . ''; + + if ( $total_pages ) { + $page_class = $total_pages < 2 ? ' one-page' : ''; + } else { + $page_class = ' no-pages'; + } + $this->_pagination = "
    $output
    "; + + echo $this->_pagination; + } + + /** + * Get a list of columns. The format is: + * 'internal-name' => 'Title' + * + * @since 3.1.0 + * @access public + * @abstract + * + * @return array + */ + public function get_columns() { + die( 'function WP_List_Table::get_columns() must be over-ridden in a sub-class.' ); + } + + /** + * Get a list of sortable columns. The format is: + * 'internal-name' => 'orderby' + * or + * 'internal-name' => array( 'orderby', true ) + * + * The second format will make the initial sorting order be descending + * + * @since 3.1.0 + * @access protected + * + * @return array + */ + protected function get_sortable_columns() { + return array(); + } + + /** + * Get a list of all, hidden and sortable columns, with filter applied + * + * @since 3.1.0 + * @access protected + * + * @return array + */ + protected function get_column_info() { + if ( isset( $this->_column_headers ) ) + return $this->_column_headers; + + $columns = get_column_headers( $this->screen ); + $hidden = get_hidden_columns( $this->screen ); + + $sortable_columns = $this->get_sortable_columns(); + /** + * Filter the list table sortable columns for a specific screen. + * + * The dynamic portion of the hook name, `$this->screen->id`, refers + * to the ID of the current screen, usually a string. + * + * @since 3.5.0 + * + * @param array $sortable_columns An array of sortable columns. + */ + $_sortable = apply_filters( "manage_{$this->screen->id}_sortable_columns", $sortable_columns ); + + $sortable = array(); + foreach ( $_sortable as $id => $data ) { + if ( empty( $data ) ) + continue; + + $data = (array) $data; + if ( !isset( $data[1] ) ) + $data[1] = false; + + $sortable[$id] = $data; + } + + $this->_column_headers = array( $columns, $hidden, $sortable ); + + return $this->_column_headers; + } + + /** + * Return number of visible columns + * + * @since 3.1.0 + * @access public + * + * @return int + */ + public function get_column_count() { + list ( $columns, $hidden ) = $this->get_column_info(); + $hidden = array_intersect( array_keys( $columns ), array_filter( $hidden ) ); + return count( $columns ) - count( $hidden ); + } + + /** + * Print column headers, accounting for hidden and sortable columns. + * + * @since 3.1.0 + * @access public + * + * @param bool $with_id Whether to set the id attribute or not + */ + public function print_column_headers( $with_id = true ) { + list( $columns, $hidden, $sortable ) = $this->get_column_info(); + + $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ); + $current_url = remove_query_arg( 'paged', $current_url ); + + if ( isset( $_GET['orderby'] ) ) + $current_orderby = $_GET['orderby']; + else + $current_orderby = ''; + + if ( isset( $_GET['order'] ) && 'desc' == $_GET['order'] ) + $current_order = 'desc'; + else + $current_order = 'asc'; + + if ( ! empty( $columns['cb'] ) ) { + static $cb_counter = 1; + $columns['cb'] = '' + . ''; + $cb_counter++; + } + + foreach ( $columns as $column_key => $column_display_name ) { + $class = array( 'manage-column', "column-$column_key" ); + + $style = ''; + if ( in_array( $column_key, $hidden ) ) + $style = 'display:none;'; + + $style = ' style="' . $style . '"'; + + if ( 'cb' == $column_key ) + $class[] = 'check-column'; + elseif ( in_array( $column_key, array( 'posts', 'comments', 'links' ) ) ) + $class[] = 'num'; + + if ( isset( $sortable[$column_key] ) ) { + list( $orderby, $desc_first ) = $sortable[$column_key]; + + if ( $current_orderby == $orderby ) { + $order = 'asc' == $current_order ? 'desc' : 'asc'; + $class[] = 'sorted'; + $class[] = $current_order; + } else { + $order = $desc_first ? 'desc' : 'asc'; + $class[] = 'sortable'; + $class[] = $desc_first ? 'asc' : 'desc'; + } + + $column_display_name = '' . $column_display_name . ''; + } + + $id = $with_id ? "id='$column_key'" : ''; + + if ( !empty( $class ) ) + $class = "class='" . join( ' ', $class ) . "'"; + + echo "$column_display_name"; + } + } + + /** + * Display the table + * + * @since 3.1.0 + * @access public + */ + public function display() { + $singular = $this->_args['singular']; + + $this->display_tablenav( 'top' ); + +?> + + + + print_column_headers(); ?> + + + + + + print_column_headers( false ); ?> + + + + > + display_rows_or_placeholder(); ?> + +
    +display_tablenav( 'bottom' ); + } + + /** + * Get a list of CSS classes for the list table table tag. + * + * @since 3.1.0 + * @access protected + * + * @return array List of CSS classes for the table tag. + */ + protected function get_table_classes() { + return array( 'widefat', 'fixed', $this->_args['plural'] ); + } + + /** + * Generate the table navigation above or below the table + * + * @since 3.1.0 + * @access protected + * @param string $which + */ + protected function display_tablenav( $which ) { + if ( 'top' == $which ) + wp_nonce_field( 'bulk-' . $this->_args['plural'] ); +?> +
    + +
    + bulk_actions( $which ); ?> +
    +extra_tablenav( $which ); + $this->pagination( $which ); +?> + +
    +
    +has_items() ) { + $this->display_rows(); + } else { + echo ''; + $this->no_items(); + echo ''; + } + } + + /** + * Generate the table rows + * + * @since 3.1.0 + * @access public + */ + public function display_rows() { + foreach ( $this->items as $item ) + $this->single_row( $item ); + } + + /** + * Generates content for a single row of the table + * + * @since 3.1.0 + * @access public + * + * @param object $item The current item + */ + public function single_row( $item ) { + static $row_class = ''; + $row_class = ( $row_class == '' ? ' class="alternate"' : '' ); + + echo ''; + $this->single_row_columns( $item ); + echo ''; + } + + /** + * Generates the columns for a single row of the table + * + * @since 3.1.0 + * @access protected + * + * @param object $item The current item + */ + protected function single_row_columns( $item ) { + list( $columns, $hidden ) = $this->get_column_info(); + + foreach ( $columns as $column_name => $column_display_name ) { + $class = "class='$column_name column-$column_name'"; + + $style = ''; + if ( in_array( $column_name, $hidden ) ) + $style = ' style="display:none;"'; + + $attributes = "$class$style"; + + if ( 'cb' == $column_name ) { + echo ''; + echo $this->column_cb( $item ); + echo ''; + } + elseif ( method_exists( $this, 'column_' . $column_name ) ) { + echo ""; + echo call_user_func( array( $this, 'column_' . $column_name ), $item ); + echo ""; + } + else { + echo ""; + echo $this->column_default( $item, $column_name ); + echo ""; + } + } + } + + /** + * Handle an incoming ajax request (called from admin-ajax.php) + * + * @since 3.1.0 + * @access public + */ + public function ajax_response() { + $this->prepare_items(); + + ob_start(); + if ( ! empty( $_REQUEST['no_placeholder'] ) ) { + $this->display_rows(); + } else { + $this->display_rows_or_placeholder(); + } + + $rows = ob_get_clean(); + + $response = array( 'rows' => $rows ); + + if ( isset( $this->_pagination_args['total_items'] ) ) { + $response['total_items_i18n'] = sprintf( + _n( '1 item', '%s items', $this->_pagination_args['total_items'] ), + number_format_i18n( $this->_pagination_args['total_items'] ) + ); + } + if ( isset( $this->_pagination_args['total_pages'] ) ) { + $response['total_pages'] = $this->_pagination_args['total_pages']; + $response['total_pages_i18n'] = number_format_i18n( $this->_pagination_args['total_pages'] ); + } + + die( wp_json_encode( $response ) ); + } + + /** + * Send required variables to JavaScript land + * + * @access public + */ + public function _js_vars() { + $args = array( + 'class' => get_class( $this ), + 'screen' => array( + 'id' => $this->screen->id, + 'base' => $this->screen->base, + ) + ); + + printf( "\n", wp_json_encode( $args ) ); + } +} diff --git a/wp-admin/includes/class-wp-media-list-table.php b/wp-admin/includes/class-wp-media-list-table.php new file mode 100644 index 0000000..377e3c0 --- /dev/null +++ b/wp-admin/includes/class-wp-media-list-table.php @@ -0,0 +1,555 @@ +detached = ( isset( $_REQUEST['attachment-filter'] ) && 'detached' === $_REQUEST['attachment-filter'] ); + + $this->modes = array( + 'list' => __( 'List View' ), + 'grid' => __( 'Grid View' ) + ); + + parent::__construct( array( + 'plural' => 'media', + 'screen' => isset( $args['screen'] ) ? $args['screen'] : null, + ) ); + } + + public function ajax_user_can() { + return current_user_can('upload_files'); + } + + public function prepare_items() { + global $wp_query, $post_mime_types, $avail_post_mime_types, $mode; + + list( $post_mime_types, $avail_post_mime_types ) = wp_edit_attachments_query( $_REQUEST ); + + $this->is_trash = isset( $_REQUEST['attachment-filter'] ) && 'trash' == $_REQUEST['attachment-filter']; + + $mode = empty( $_REQUEST['mode'] ) ? 'list' : $_REQUEST['mode']; + + $this->set_pagination_args( array( + 'total_items' => $wp_query->found_posts, + 'total_pages' => $wp_query->max_num_pages, + 'per_page' => $wp_query->query_vars['posts_per_page'], + ) ); + } + + protected function get_views() { + global $wpdb, $post_mime_types, $avail_post_mime_types; + + $type_links = array(); + $_num_posts = (array) wp_count_attachments(); + $_total_posts = array_sum($_num_posts) - $_num_posts['trash']; + $total_orphans = $wpdb->get_var( "SELECT COUNT( * ) FROM $wpdb->posts WHERE post_type = 'attachment' AND post_status != 'trash' AND post_parent < 1" ); + $matches = wp_match_mime_types(array_keys($post_mime_types), array_keys($_num_posts)); + foreach ( $matches as $type => $reals ) + foreach ( $reals as $real ) + $num_posts[$type] = ( isset( $num_posts[$type] ) ) ? $num_posts[$type] + $_num_posts[$real] : $_num_posts[$real]; + + $selected = empty( $_GET['attachment-filter'] ) ? ' selected="selected"' : ''; + $type_links['all'] = "'; + foreach ( $post_mime_types as $mime_type => $label ) { + if ( !wp_match_mime_types($mime_type, $avail_post_mime_types) ) + continue; + + $selected = ''; + if ( !empty( $_GET['attachment-filter'] ) && strpos( $_GET['attachment-filter'], 'post_mime_type:' ) === 0 && wp_match_mime_types( $mime_type, str_replace( 'post_mime_type:', '', $_GET['attachment-filter'] ) ) ) + $selected = ' selected="selected"'; + if ( !empty( $num_posts[$mime_type] ) ) + $type_links[$mime_type] = ''; + } + $type_links['detached'] = ''; + + if ( !empty($_num_posts['trash']) ) + $type_links['trash'] = ''; + + return $type_links; + } + + protected function get_bulk_actions() { + $actions = array(); + if ( MEDIA_TRASH ) { + if ( $this->is_trash ) { + $actions['untrash'] = __( 'Restore' ); + $actions['delete'] = __( 'Delete Permanently' ); + } else { + $actions['trash'] = __( 'Trash' ); + } + } else { + $actions['delete'] = __( 'Delete Permanently' ); + } + + if ( $this->detached ) + $actions['attach'] = __( 'Attach to a post' ); + + return $actions; + } + + /** + * @param string $which + */ + protected function extra_tablenav( $which ) { + if ( 'bar' !== $which ) { + return; + } +?> +
    +is_trash ) { + $this->months_dropdown( 'attachment' ); + } + + /** This action is documented in wp-admin/includes/class-wp-posts-list-table.php */ + do_action( 'restrict_manage_posts' ); + submit_button( __( 'Filter' ), 'button', 'filter_action', false, array( 'id' => 'post-query-submit' ) ); + } + + if ( $this->is_trash && current_user_can( 'edit_others_posts' ) ) { + submit_button( __( 'Empty Trash' ), 'apply', 'delete_all', false ); + } ?> +
    +get_views(); +?> +
    +
    + view_switcher( $mode ); ?> + + + +extra_tablenav( 'bar' ); + + /** This filter is documented in wp-admin/inclues/class-wp-list-table.php */ + $views = apply_filters( "views_{$this->screen->id}", array() ); + + // Back compat for pre-4.0 view links. + if ( ! empty( $views ) ) { + echo ''; + } +?> +
    + +
    + +
    +
    + '; + $posts_columns['icon'] = ''; + /* translators: column name */ + $posts_columns['title'] = _x( 'File', 'column name' ); + $posts_columns['author'] = __( 'Author' ); + + $taxonomies = get_taxonomies_for_attachments( 'objects' ); + $taxonomies = wp_filter_object_list( $taxonomies, array( 'show_admin_column' => true ), 'and', 'name' ); + + /** + * Filter the taxonomy columns for attachments in the Media list table. + * + * @since 3.5.0 + * + * @param array $taxonomies An array of registered taxonomies to show for attachments. + * @param string $post_type The post type. Default 'attachment'. + */ + $taxonomies = apply_filters( 'manage_taxonomies_for_attachment_columns', $taxonomies, 'attachment' ); + $taxonomies = array_filter( $taxonomies, 'taxonomy_exists' ); + + foreach ( $taxonomies as $taxonomy ) { + if ( 'category' == $taxonomy ) + $column_key = 'categories'; + elseif ( 'post_tag' == $taxonomy ) + $column_key = 'tags'; + else + $column_key = 'taxonomy-' . $taxonomy; + + $posts_columns[ $column_key ] = get_taxonomy( $taxonomy )->labels->name; + } + + /* translators: column name */ + if ( !$this->detached ) { + $posts_columns['parent'] = _x( 'Uploaded to', 'column name' ); + if ( post_type_supports( 'attachment', 'comments' ) ) + $posts_columns['comments'] = ''; + } + /* translators: column name */ + $posts_columns['date'] = _x( 'Date', 'column name' ); + /** + * Filter the Media list table columns. + * + * @since 2.5.0 + * + * @param array $posts_columns An array of columns displayed in the Media list table. + * @param bool $detached Whether the list table contains media not attached + * to any posts. Default true. + */ + $posts_columns = apply_filters( 'manage_media_columns', $posts_columns, $this->detached ); + + return $posts_columns; + } + + protected function get_sortable_columns() { + return array( + 'title' => 'title', + 'author' => 'author', + 'parent' => 'parent', + 'comments' => 'comment_count', + 'date' => array( 'date', true ), + ); + } + + public function display_rows() { + global $post; + + add_filter( 'the_title','esc_html' ); + $alt = ''; + + while ( have_posts() ) : the_post(); + $user_can_edit = current_user_can( 'edit_post', $post->ID ); + + if ( $this->is_trash && $post->post_status != 'trash' + || !$this->is_trash && $post->post_status == 'trash' ) + continue; + + $alt = ( 'alternate' == $alt ) ? '' : 'alternate'; + $post_owner = ( get_current_user_id() == $post->post_author ) ? 'self' : 'other'; + $att_title = _draft_or_post_title(); +?> + +get_column_info(); +foreach ( $columns as $column_name => $column_display_name ) { + $class = "class='$column_name column-$column_name'"; + + $style = ''; + if ( in_array( $column_name, $hidden ) ) + $style = ' style="display:none;"'; + + $attributes = $class . $style; + + switch ( $column_name ) { + + case 'cb': +?> + + + + + + +post_mime_type ); + $attributes = 'class="column-icon media-icon ' . $mime . '-icon"' . $style; +?> + >ID, array( 80, 60 ), true ) ) { + if ( $this->is_trash || ! $user_can_edit ) { + echo $thumb; + } else { +?> + + + + + + + + > + is_trash || ! $user_can_edit ) { + echo $att_title; + } else { ?> + + + +

    +ID ), $matches ) ) + echo esc_html( strtoupper( $matches[1] ) ); + else + echo strtoupper( str_replace( 'image/', '', get_post_mime_type() ) ); +?> +

    +row_actions( $this->_get_row_actions( $post, $att_title ) ); +?> + + + >%s', + esc_url( add_query_arg( array( 'author' => get_the_author_meta('ID') ), 'upload.php' ) ), + get_the_author() + ); + ?> + + >post_excerpt : ''; ?> +post_date ) { + $h_time = __( 'Unpublished' ); + } else { + $m_time = $post->post_date; + $time = get_post_time( 'G', true, $post, false ); + if ( ( abs( $t_diff = time() - $time ) ) < DAY_IN_SECONDS ) { + if ( $t_diff < 0 ) + $h_time = sprintf( __( '%s from now' ), human_time_diff( $time ) ); + else + $h_time = sprintf( __( '%s ago' ), human_time_diff( $time ) ); + } else { + $h_time = mysql2date( __( 'Y/m/d' ), $m_time ); + } + } +?> + > +post_parent > 0 ) + $parent = get_post( $post->post_parent ); + else + $parent = false; + + if ( $parent ) { + $title = _draft_or_post_title( $post->post_parent ); + $parent_type = get_post_type_object( $parent->post_type ); +?> + > + show_ui && current_user_can( 'edit_post', $post->post_parent ) ) { ?> + + , + + + + >
    + + + + + + > +
    +ID ); + + $this->comments_bubble( $post->ID, $pending_comments ); +?> +
    + +'; + if ( $terms = get_the_terms( $post->ID, $taxonomy ) ) { + $out = array(); + foreach ( $terms as $t ) { + $posts_in_term_qv = array(); + $posts_in_term_qv['taxonomy'] = $taxonomy; + $posts_in_term_qv['term'] = $t->slug; + + $out[] = sprintf( '%s', + esc_url( add_query_arg( $posts_in_term_qv, 'upload.php' ) ), + esc_html( sanitize_term_field( 'name', $t->name, $t->term_id, $taxonomy, 'display' ) ) + ); + } + /* translators: used between list items, there is a space after the comma */ + echo join( __( ', ' ), $out ); + } else { + echo '—'; + } + echo ''; + break; + } +?> + >ID ); + ?> + + +detached ) { + if ( current_user_can( 'edit_post', $post->ID ) ) + $actions['edit'] = '' . __( 'Edit' ) . ''; + if ( current_user_can( 'delete_post', $post->ID ) ) + if ( EMPTY_TRASH_DAYS && MEDIA_TRASH ) { + $actions['trash'] = "ID ) . "'>" . __( 'Trash' ) . ""; + } else { + $delete_ays = !MEDIA_TRASH ? " onclick='return showNotice.warn();'" : ''; + $actions['delete'] = "ID ) . "'>" . __( 'Delete Permanently' ) . ""; + } + $actions['view'] = '' . __( 'View' ) . ''; + if ( current_user_can( 'edit_post', $post->ID ) ) + $actions['attach'] = ''.__( 'Attach' ).''; + } + else { + if ( current_user_can( 'edit_post', $post->ID ) && !$this->is_trash ) + $actions['edit'] = '' . __( 'Edit' ) . ''; + if ( current_user_can( 'delete_post', $post->ID ) ) { + if ( $this->is_trash ) + $actions['untrash'] = "ID ) . "'>" . __( 'Restore' ) . ""; + elseif ( EMPTY_TRASH_DAYS && MEDIA_TRASH ) + $actions['trash'] = "ID ) . "'>" . __( 'Trash' ) . ""; + if ( $this->is_trash || !EMPTY_TRASH_DAYS || !MEDIA_TRASH ) { + $delete_ays = ( !$this->is_trash && !MEDIA_TRASH ) ? " onclick='return showNotice.warn();'" : ''; + $actions['delete'] = "ID ) . "'>" . __( 'Delete Permanently' ) . ""; + } + } + if ( !$this->is_trash ) { + $title =_draft_or_post_title( $post->post_parent ); + $actions['view'] = '' . __( 'View' ) . ''; + } + } + + /** + * Filter the action links for each attachment in the Media list table. + * + * @since 2.8.0 + * + * @param array $actions An array of action links for each attachment. + * Default 'Edit', 'Delete Permanently', 'View'. + * @param WP_Post $post WP_Post object for the current attachment. + * @param bool $detached Whether the list table contains media not attached + * to any posts. Default true. + */ + $actions = apply_filters( 'media_row_actions', $actions, $post, $this->detached ); + + return $actions; + } +} diff --git a/wp-admin/includes/class-wp-ms-sites-list-table.php b/wp-admin/includes/class-wp-ms-sites-list-table.php new file mode 100644 index 0000000..c4a99bf --- /dev/null +++ b/wp-admin/includes/class-wp-ms-sites-list-table.php @@ -0,0 +1,405 @@ + 'sites', + 'screen' => isset( $args['screen'] ) ? $args['screen'] : null, + ) ); + } + + public function ajax_user_can() { + return current_user_can( 'manage_sites' ); + } + + public function prepare_items() { + global $s, $mode, $wpdb; + + $current_site = get_current_site(); + + $mode = ( empty( $_REQUEST['mode'] ) ) ? 'list' : $_REQUEST['mode']; + + $per_page = $this->get_items_per_page( 'sites_network_per_page' ); + + $pagenum = $this->get_pagenum(); + + $s = isset( $_REQUEST['s'] ) ? wp_unslash( trim( $_REQUEST[ 's' ] ) ) : ''; + $wild = ''; + if ( false !== strpos($s, '*') ) { + $wild = '%'; + $s = trim($s, '*'); + } + + /* + * If the network is large and a search is not being performed, show only + * the latest blogs with no paging in order to avoid expensive count queries. + */ + if ( !$s && wp_is_large_network() ) { + if ( !isset($_REQUEST['orderby']) ) + $_GET['orderby'] = $_REQUEST['orderby'] = ''; + if ( !isset($_REQUEST['order']) ) + $_GET['order'] = $_REQUEST['order'] = 'DESC'; + } + + $query = "SELECT * FROM {$wpdb->blogs} WHERE site_id = '{$wpdb->siteid}' "; + + if ( empty($s) ) { + // Nothing to do. + } elseif ( preg_match( '/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/', $s ) || + preg_match( '/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.?$/', $s ) || + preg_match( '/^[0-9]{1,3}\.[0-9]{1,3}\.?$/', $s ) || + preg_match( '/^[0-9]{1,3}\.$/', $s ) ) { + // IPv4 address + $sql = $wpdb->prepare( "SELECT blog_id FROM {$wpdb->registration_log} WHERE {$wpdb->registration_log}.IP LIKE %s", $wpdb->esc_like( $s ) . $wild ); + $reg_blog_ids = $wpdb->get_col( $sql ); + + if ( !$reg_blog_ids ) + $reg_blog_ids = array( 0 ); + + $query = "SELECT * + FROM {$wpdb->blogs} + WHERE site_id = '{$wpdb->siteid}' + AND {$wpdb->blogs}.blog_id IN (" . implode( ', ', $reg_blog_ids ) . ")"; + } else { + if ( is_numeric($s) && empty( $wild ) ) { + $query .= $wpdb->prepare( " AND ( {$wpdb->blogs}.blog_id = %s )", $s ); + } elseif ( is_subdomain_install() ) { + $blog_s = str_replace( '.' . $current_site->domain, '', $s ); + $blog_s = $wpdb->esc_like( $blog_s ) . $wild . $wpdb->esc_like( '.' . $current_site->domain ); + $query .= $wpdb->prepare( " AND ( {$wpdb->blogs}.domain LIKE %s ) ", $blog_s ); + } else { + if ( $s != trim('/', $current_site->path) ) { + $blog_s = $wpdb->esc_like( $current_site->path . $s ) . $wild . $wpdb->esc_like( '/' ); + } else { + $blog_s = $wpdb->esc_like( $s ); + } + $query .= $wpdb->prepare( " AND ( {$wpdb->blogs}.path LIKE %s )", $blog_s ); + } + } + + $order_by = isset( $_REQUEST['orderby'] ) ? $_REQUEST['orderby'] : ''; + if ( $order_by == 'registered' ) { + $query .= ' ORDER BY registered '; + } elseif ( $order_by == 'lastupdated' ) { + $query .= ' ORDER BY last_updated '; + } elseif ( $order_by == 'blogname' ) { + if ( is_subdomain_install() ) + $query .= ' ORDER BY domain '; + else + $query .= ' ORDER BY path '; + } elseif ( $order_by == 'blog_id' ) { + $query .= ' ORDER BY blog_id '; + } else { + $order_by = null; + } + + if ( isset( $order_by ) ) { + $order = ( isset( $_REQUEST['order'] ) && 'DESC' == strtoupper( $_REQUEST['order'] ) ) ? "DESC" : "ASC"; + $query .= $order; + } + + // Don't do an unbounded count on large networks + if ( ! wp_is_large_network() ) + $total = $wpdb->get_var( str_replace( 'SELECT *', 'SELECT COUNT( blog_id )', $query ) ); + + $query .= " LIMIT " . intval( ( $pagenum - 1 ) * $per_page ) . ", " . intval( $per_page ); + $this->items = $wpdb->get_results( $query, ARRAY_A ); + + if ( wp_is_large_network() ) + $total = count($this->items); + + $this->set_pagination_args( array( + 'total_items' => $total, + 'per_page' => $per_page, + ) ); + } + + public function no_items() { + _e( 'No sites found.' ); + } + + protected function get_bulk_actions() { + $actions = array(); + if ( current_user_can( 'delete_sites' ) ) + $actions['delete'] = __( 'Delete' ); + $actions['spam'] = _x( 'Mark as Spam', 'site' ); + $actions['notspam'] = _x( 'Not Spam', 'site' ); + + return $actions; + } + + /** + * @param string $which + */ + protected function pagination( $which ) { + global $mode; + + parent::pagination( $which ); + + if ( 'top' == $which ) + $this->view_switcher( $mode ); + } + + public function get_columns() { + $blogname_columns = ( is_subdomain_install() ) ? __( 'Domain' ) : __( 'Path' ); + $sites_columns = array( + 'cb' => '', + 'blogname' => $blogname_columns, + 'lastupdated' => __( 'Last Updated' ), + 'registered' => _x( 'Registered', 'site' ), + 'users' => __( 'Users' ) + ); + + if ( has_filter( 'wpmublogsaction' ) ) + $sites_columns['plugins'] = __( 'Actions' ); + + /** + * Filter the displayed site columns in Sites list table. + * + * @since MU + * + * @param array $sites_columns An array of displayed site columns. Default 'cb', + * 'blogname', 'lastupdated', 'registered', 'users'. + */ + $sites_columns = apply_filters( 'wpmu_blogs_columns', $sites_columns ); + + return $sites_columns; + } + + protected function get_sortable_columns() { + return array( + 'blogname' => 'blogname', + 'lastupdated' => 'lastupdated', + 'registered' => 'blog_id', + ); + } + + public function display_rows() { + global $mode; + + $status_list = array( + 'archived' => array( 'site-archived', __( 'Archived' ) ), + 'spam' => array( 'site-spammed', _x( 'Spam', 'site' ) ), + 'deleted' => array( 'site-deleted', __( 'Deleted' ) ), + 'mature' => array( 'site-mature', __( 'Mature' ) ) + ); + + if ( 'list' == $mode ) { + $date = 'Y/m/d'; + } else { + $date = 'Y/m/d \<\b\r \/\> g:i:s a'; + } + + $class = ''; + foreach ( $this->items as $blog ) { + $class = ( 'alternate' == $class ) ? '' : 'alternate'; + reset( $status_list ); + + $blog_states = array(); + foreach ( $status_list as $status => $col ) { + if ( get_blog_status( $blog['blog_id'], $status ) == 1 ) { + $class = $col[0]; + $blog_states[] = $col[1]; + } + } + $blog_state = ''; + if ( ! empty( $blog_states ) ) { + $state_count = count( $blog_states ); + $i = 0; + $blog_state .= ' - '; + foreach ( $blog_states as $state ) { + ++$i; + ( $i == $state_count ) ? $sep = '' : $sep = ', '; + $blog_state .= "$state$sep"; + } + } + echo ""; + + $blogname = ( is_subdomain_install() ) ? str_replace( '.' . get_current_site()->domain, '', $blog['domain'] ) : $blog['path']; + + list( $columns, $hidden ) = $this->get_column_info(); + + foreach ( $columns as $column_name => $column_display_name ) { + $style = ''; + if ( in_array( $column_name, $hidden ) ) + $style = ' style="display:none;"'; + + switch ( $column_name ) { + case 'cb': ?> + + + + + + + + + + + "; ?> + + ' . sprintf( _x( '%1$s – %2$s', '%1$s: site name. %2$s: site tagline.' ), get_option( 'blogname' ), get_option( 'blogdescription ' ) ) . '

    '; + restore_current_blog(); + } + + // Preordered. + $actions = array( + 'edit' => '', 'backend' => '', + 'activate' => '', 'deactivate' => '', + 'archive' => '', 'unarchive' => '', + 'spam' => '', 'unspam' => '', + 'delete' => '', + 'visit' => '', + ); + + $actions['edit'] = '' . __( 'Edit' ) . ''; + $actions['backend'] = "" . __( 'Dashboard' ) . ''; + if ( get_current_site()->blog_id != $blog['blog_id'] ) { + if ( get_blog_status( $blog['blog_id'], 'deleted' ) == '1' ) + $actions['activate'] = '' . __( 'Activate' ) . ''; + else + $actions['deactivate'] = '' . __( 'Deactivate' ) . ''; + + if ( get_blog_status( $blog['blog_id'], 'archived' ) == '1' ) + $actions['unarchive'] = '' . __( 'Unarchive' ) . ''; + else + $actions['archive'] = '' . _x( 'Archive', 'verb; site' ) . ''; + + if ( get_blog_status( $blog['blog_id'], 'spam' ) == '1' ) + $actions['unspam'] = '' . _x( 'Not Spam', 'site' ) . ''; + else + $actions['spam'] = '' . _x( 'Spam', 'site' ) . ''; + + if ( current_user_can( 'delete_site', $blog['blog_id'] ) ) + $actions['delete'] = '' . __( 'Delete' ) . ''; + } + + $actions['visit'] = "" . __( 'Visit' ) . ''; + + /** + * Filter the action links displayed for each site in the Sites list table. + * + * The 'Edit', 'Dashboard', 'Delete', and 'Visit' links are displayed by + * default for each site. The site's status determines whether to show the + * 'Activate' or 'Deactivate' link, 'Unarchive' or 'Archive' links, and + * 'Not Spam' or 'Spam' link for each site. + * + * @since 3.1.0 + * + * @param array $actions An array of action links to be displayed. + * @param int $blog_id The site ID. + * @param string $blogname Site path, formatted depending on whether it is a sub-domain + * or subdirectory multisite install. + */ + $actions = apply_filters( 'manage_sites_action_links', array_filter( $actions ), $blog['blog_id'], $blogname ); + echo $this->row_actions( $actions ); + ?> + + "; + echo ( $blog['last_updated'] == '0000-00-00 00:00:00' ) ? __( 'Never' ) : mysql2date( $date, $blog['last_updated'] ); ?> + + "; + if ( $blog['registered'] == '0000-00-00 00:00:00' ) + echo '—'; + else + echo mysql2date( $date, $blog['registered'] ); + ?> + + "; + $blogusers = get_users( array( 'blog_id' => $blog['blog_id'], 'number' => 6) ); + if ( is_array( $blogusers ) ) { + $blogusers_warning = ''; + if ( count( $blogusers ) > 5 ) { + $blogusers = array_slice( $blogusers, 0, 5 ); + $blogusers_warning = __( 'Only showing first 5 users.' ) . ' ' . __( 'More' ) . ''; + } + foreach ( $blogusers as $user_object ) { + echo '' . esc_html( $user_object->user_login ) . ' '; + if ( 'list' != $mode ) + echo '( ' . $user_object->user_email . ' )'; + echo '
    '; + } + if ( $blogusers_warning != '' ) + echo '' . $blogusers_warning . '
    '; + } + ?> + + + "; + /** + * Fires inside the auxiliary 'Actions' column of the Sites list table. + * + * By default this column is hidden unless something is hooked to the action. + * + * @since MU + * + * @param int $blog_id The site ID. + */ + do_action( 'wpmublogsaction', $blog['blog_id'] ); ?> + + "; + /** + * Fires for each registered custom column in the Sites list table. + * + * @since 3.1.0 + * + * @param string $column_name The name of the column to display. + * @param int $blog_id The site ID. + */ + do_action( 'manage_sites_custom_column', $column_name, $blog['blog_id'] ); + echo ""; + break; + } + } + ?> + + 'themes', + 'screen' => isset( $args['screen'] ) ? $args['screen'] : null, + ) ); + + $status = isset( $_REQUEST['theme_status'] ) ? $_REQUEST['theme_status'] : 'all'; + if ( !in_array( $status, array( 'all', 'enabled', 'disabled', 'upgrade', 'search', 'broken' ) ) ) + $status = 'all'; + + $page = $this->get_pagenum(); + + $this->is_site_themes = ( 'site-themes-network' == $this->screen->id ) ? true : false; + + if ( $this->is_site_themes ) + $this->site_id = isset( $_REQUEST['id'] ) ? intval( $_REQUEST['id'] ) : 0; + } + + protected function get_table_classes() { + // todo: remove and add CSS for .themes + return array( 'widefat', 'plugins' ); + } + + public function ajax_user_can() { + if ( $this->is_site_themes ) + return current_user_can( 'manage_sites' ); + else + return current_user_can( 'manage_network_themes' ); + } + + public function prepare_items() { + global $status, $totals, $page, $orderby, $order, $s; + + wp_reset_vars( array( 'orderby', 'order', 's' ) ); + + $themes = array( + /** + * Filter the full array of WP_Theme objects to list in the Multisite + * themes list table. + * + * @since 3.1.0 + * + * @param array $all An array of WP_Theme objects to display in the list table. + */ + 'all' => apply_filters( 'all_themes', wp_get_themes() ), + 'search' => array(), + 'enabled' => array(), + 'disabled' => array(), + 'upgrade' => array(), + 'broken' => $this->is_site_themes ? array() : wp_get_themes( array( 'errors' => true ) ), + ); + + if ( $this->is_site_themes ) { + $themes_per_page = $this->get_items_per_page( 'site_themes_network_per_page' ); + $allowed_where = 'site'; + } else { + $themes_per_page = $this->get_items_per_page( 'themes_network_per_page' ); + $allowed_where = 'network'; + } + + $maybe_update = current_user_can( 'update_themes' ) && ! $this->is_site_themes && $current = get_site_transient( 'update_themes' ); + + foreach ( (array) $themes['all'] as $key => $theme ) { + if ( $this->is_site_themes && $theme->is_allowed( 'network' ) ) { + unset( $themes['all'][ $key ] ); + continue; + } + + if ( $maybe_update && isset( $current->response[ $key ] ) ) { + $themes['all'][ $key ]->update = true; + $themes['upgrade'][ $key ] = $themes['all'][ $key ]; + } + + $filter = $theme->is_allowed( $allowed_where, $this->site_id ) ? 'enabled' : 'disabled'; + $themes[ $filter ][ $key ] = $themes['all'][ $key ]; + } + + if ( $s ) { + $status = 'search'; + $themes['search'] = array_filter( array_merge( $themes['all'], $themes['broken'] ), array( $this, '_search_callback' ) ); + } + + $totals = array(); + foreach ( $themes as $type => $list ) + $totals[ $type ] = count( $list ); + + if ( empty( $themes[ $status ] ) && !in_array( $status, array( 'all', 'search' ) ) ) + $status = 'all'; + + $this->items = $themes[ $status ]; + WP_Theme::sort_by_name( $this->items ); + + $this->has_items = ! empty( $themes['all'] ); + $total_this_page = $totals[ $status ]; + + if ( $orderby ) { + $orderby = ucfirst( $orderby ); + $order = strtoupper( $order ); + + if ( $orderby == 'Name' ) { + if ( 'ASC' == $order ) + $this->items = array_reverse( $this->items ); + } else { + uasort( $this->items, array( $this, '_order_callback' ) ); + } + } + + $start = ( $page - 1 ) * $themes_per_page; + + if ( $total_this_page > $themes_per_page ) + $this->items = array_slice( $this->items, $start, $themes_per_page, true ); + + $this->set_pagination_args( array( + 'total_items' => $total_this_page, + 'per_page' => $themes_per_page, + ) ); + } + + /** + * @staticvar string $term + * @param WP_Theme $theme + * @return bool + */ + public function _search_callback( $theme ) { + static $term; + if ( is_null( $term ) ) + $term = wp_unslash( $_REQUEST['s'] ); + + foreach ( array( 'Name', 'Description', 'Author', 'Author', 'AuthorURI' ) as $field ) { + // Don't mark up; Do translate. + if ( false !== stripos( $theme->display( $field, false, true ), $term ) ) + return true; + } + + if ( false !== stripos( $theme->get_stylesheet(), $term ) ) + return true; + + if ( false !== stripos( $theme->get_template(), $term ) ) + return true; + + return false; + } + + // Not used by any core columns. + /** + * @global string $orderby + * @global string $order + * @param array $theme_a + * @param array $theme_b + * @return int + */ + public function _order_callback( $theme_a, $theme_b ) { + global $orderby, $order; + + $a = $theme_a[ $orderby ]; + $b = $theme_b[ $orderby ]; + + if ( $a == $b ) + return 0; + + if ( 'DESC' == $order ) + return ( $a < $b ) ? 1 : -1; + else + return ( $a < $b ) ? -1 : 1; + } + + public function no_items() { + if ( ! $this->has_items ) + _e( 'No themes found.' ); + else + _e( 'You do not appear to have any themes available at this time.' ); + } + + public function get_columns() { + global $status; + + return array( + 'cb' => '', + 'name' => __( 'Theme' ), + 'description' => __( 'Description' ), + ); + } + + protected function get_sortable_columns() { + return array( + 'name' => 'name', + ); + } + + protected function get_views() { + global $totals, $status; + + $status_links = array(); + foreach ( $totals as $type => $count ) { + if ( !$count ) + continue; + + switch ( $type ) { + case 'all': + $text = _nx( 'All (%s)', 'All (%s)', $count, 'themes' ); + break; + case 'enabled': + $text = _n( 'Enabled (%s)', 'Enabled (%s)', $count ); + break; + case 'disabled': + $text = _n( 'Disabled (%s)', 'Disabled (%s)', $count ); + break; + case 'upgrade': + $text = _n( 'Update Available (%s)', 'Update Available (%s)', $count ); + break; + case 'broken' : + $text = _n( 'Broken (%s)', 'Broken (%s)', $count ); + break; + } + + if ( $this->is_site_themes ) + $url = 'site-themes.php?id=' . $this->site_id; + else + $url = 'themes.php'; + + if ( 'search' != $type ) { + $status_links[$type] = sprintf( "%s", + esc_url( add_query_arg('theme_status', $type, $url) ), + ( $type == $status ) ? ' class="current"' : '', + sprintf( $text, number_format_i18n( $count ) ) + ); + } + } + + return $status_links; + } + + protected function get_bulk_actions() { + global $status; + + $actions = array(); + if ( 'enabled' != $status ) + $actions['enable-selected'] = $this->is_site_themes ? __( 'Enable' ) : __( 'Network Enable' ); + if ( 'disabled' != $status ) + $actions['disable-selected'] = $this->is_site_themes ? __( 'Disable' ) : __( 'Network Disable' ); + if ( ! $this->is_site_themes ) { + if ( current_user_can( 'update_themes' ) ) + $actions['update-selected'] = __( 'Update' ); + if ( current_user_can( 'delete_themes' ) ) + $actions['delete-selected'] = __( 'Delete' ); + } + return $actions; + } + + public function display_rows() { + foreach ( $this->items as $theme ) + $this->single_row( $theme ); + } + + /** + * @global string $status + * @global int $page + * @global string $s + * @global array $totals + * @param WP_Theme $theme + */ + public function single_row( $theme ) { + global $status, $page, $s, $totals; + + $context = $status; + + if ( $this->is_site_themes ) { + $url = "site-themes.php?id={$this->site_id}&"; + $allowed = $theme->is_allowed( 'site', $this->site_id ); + } else { + $url = 'themes.php?'; + $allowed = $theme->is_allowed( 'network' ); + } + + // Pre-order. + $actions = array( + 'enable' => '', + 'disable' => '', + 'edit' => '', + 'delete' => '' + ); + + $stylesheet = $theme->get_stylesheet(); + $theme_key = urlencode( $stylesheet ); + + if ( ! $allowed ) { + if ( ! $theme->errors() ) + $actions['enable'] = '' . ( $this->is_site_themes ? __( 'Enable' ) : __( 'Network Enable' ) ) . ''; + } else { + $actions['disable'] = '' . ( $this->is_site_themes ? __( 'Disable' ) : __( 'Network Disable' ) ) . ''; + } + + if ( current_user_can('edit_themes') ) + $actions['edit'] = '' . __('Edit') . ''; + + if ( ! $allowed && current_user_can( 'delete_themes' ) && ! $this->is_site_themes && $stylesheet != get_option( 'stylesheet' ) && $stylesheet != get_option( 'template' ) ) + $actions['delete'] = '' . __( 'Delete' ) . ''; + + /** + * Filter the action links displayed for each theme in the Multisite + * themes list table. + * + * The action links displayed are determined by the theme's status, and + * which Multisite themes list table is being displayed - the Network + * themes list table (themes.php), which displays all installed themes, + * or the Site themes list table (site-themes.php), which displays the + * non-network enabled themes when editing a site in the Network admin. + * + * The default action links for the Network themes list table include + * 'Network Enable', 'Network Disable', 'Edit', and 'Delete'. + * + * The default action links for the Site themes list table include + * 'Enable', 'Disable', and 'Edit'. + * + * @since 2.8.0 + * + * @param array $actions An array of action links. + * @param WP_Theme $theme The current WP_Theme object. + * @param string $context Status of the theme. + */ + $actions = apply_filters( 'theme_action_links', array_filter( $actions ), $theme, $context ); + + /** + * Filter the action links of a specific theme in the Multisite themes + * list table. + * + * The dynamic portion of the hook name, `$stylesheet`, refers to the + * directory name of the theme, which in most cases is synonymous + * with the template name. + * + * @since 3.1.0 + * + * @param array $actions An array of action links. + * @param WP_Theme $theme The current WP_Theme object. + * @param string $context Status of the theme. + */ + $actions = apply_filters( "theme_action_links_$stylesheet", $actions, $theme, $context ); + + $class = ! $allowed ? 'inactive' : 'active'; + $checkbox_id = "checkbox_" . md5( $theme->get('Name') ); + $checkbox = ""; + + $id = sanitize_html_class( $theme->get_stylesheet() ); + + if ( ! empty( $totals['upgrade'] ) && ! empty( $theme->update ) ) + $class .= ' update'; + + echo ""; + + list( $columns, $hidden ) = $this->get_column_info(); + + foreach ( $columns as $column_name => $column_display_name ) { + $style = ''; + if ( in_array( $column_name, $hidden ) ) + $style = ' style="display:none;"'; + + switch ( $column_name ) { + case 'cb': + echo "$checkbox"; + break; + case 'name': + echo "" . $theme->display('Name') . ""; + echo $this->row_actions( $actions, true ); + echo ""; + break; + case 'description': + echo ""; + if ( $theme->errors() ) { + $pre = $status == 'broken' ? __( 'Broken Theme:' ) . ' ' : ''; + echo '

    ' . $pre . $theme->errors()->get_error_message() . '

    '; + } + echo "

    " . $theme->display( 'Description' ) . "

    +
    "; + + $theme_meta = array(); + + if ( $theme->get('Version') ) + $theme_meta[] = sprintf( __( 'Version %s' ), $theme->display('Version') ); + + $theme_meta[] = sprintf( __( 'By %s' ), $theme->display('Author') ); + + if ( $theme->get('ThemeURI') ) + $theme_meta[] = '' . __( 'Visit Theme Site' ) . ''; + + /** + * Filter the array of row meta for each theme in the Multisite themes + * list table. + * + * @since 3.1.0 + * + * @param array $theme_meta An array of the theme's metadata, + * including the version, author, and + * theme URI. + * @param string $stylesheet Directory name of the theme. + * @param WP_Theme $theme WP_Theme object. + * @param string $status Status of the theme. + */ + $theme_meta = apply_filters( 'theme_row_meta', $theme_meta, $stylesheet, $theme, $status ); + echo implode( ' | ', $theme_meta ); + + echo "
    "; + break; + + default: + echo ""; + + /** + * Fires inside each custom column of the Multisite themes list table. + * + * @since 3.1.0 + * + * @param string $column_name Name of the column. + * @param string $stylesheet Directory name of the theme. + * @param WP_Theme $theme Current WP_Theme object. + */ + do_action( 'manage_themes_custom_column', $column_name, $stylesheet, $theme ); + echo ""; + } + } + + echo ""; + + if ( $this->is_site_themes ) + remove_action( "after_theme_row_$stylesheet", 'wp_theme_update_row' ); + + /** + * Fires after each row in the Multisite themes list table. + * + * @since 3.1.0 + * + * @param string $stylesheet Directory name of the theme. + * @param WP_Theme $theme Current WP_Theme object. + * @param string $status Status of the theme. + */ + do_action( 'after_theme_row', $stylesheet, $theme, $status ); + + /** + * Fires after each specific row in the Multisite themes list table. + * + * The dynamic portion of the hook name, `$stylesheet`, refers to the + * directory name of the theme, most often synonymous with the template + * name of the theme. + * + * @since 3.5.0 + * + * @param string $stylesheet Directory name of the theme. + * @param WP_Theme $theme Current WP_Theme object. + * @param string $status Status of the theme. + */ + do_action( "after_theme_row_$stylesheet", $stylesheet, $theme, $status ); + } +} diff --git a/wp-admin/includes/class-wp-ms-users-list-table.php b/wp-admin/includes/class-wp-ms-users-list-table.php new file mode 100644 index 0000000..3a8dd25 --- /dev/null +++ b/wp-admin/includes/class-wp-ms-users-list-table.php @@ -0,0 +1,307 @@ +get_items_per_page( 'users_network_per_page' ); + + $role = isset( $_REQUEST['role'] ) ? $_REQUEST['role'] : ''; + + $paged = $this->get_pagenum(); + + $args = array( + 'number' => $users_per_page, + 'offset' => ( $paged-1 ) * $users_per_page, + 'search' => $usersearch, + 'blog_id' => 0, + 'fields' => 'all_with_meta' + ); + + if ( wp_is_large_network( 'users' ) ) + $args['search'] = ltrim( $args['search'], '*' ); + + if ( $role == 'super' ) { + $logins = implode( "', '", get_super_admins() ); + $args['include'] = $wpdb->get_col( "SELECT ID FROM $wpdb->users WHERE user_login IN ('$logins')" ); + } + + /* + * If the network is large and a search is not being performed, + * show only the latest users with no paging in order to avoid + * expensive count queries. + */ + if ( !$usersearch && wp_is_large_network( 'users' ) ) { + if ( !isset($_REQUEST['orderby']) ) + $_GET['orderby'] = $_REQUEST['orderby'] = 'id'; + if ( !isset($_REQUEST['order']) ) + $_GET['order'] = $_REQUEST['order'] = 'DESC'; + $args['count_total'] = false; + } + + if ( isset( $_REQUEST['orderby'] ) ) + $args['orderby'] = $_REQUEST['orderby']; + + if ( isset( $_REQUEST['order'] ) ) + $args['order'] = $_REQUEST['order']; + + $mode = empty( $_REQUEST['mode'] ) ? 'list' : $_REQUEST['mode']; + + // Query the user IDs for this page + $wp_user_search = new WP_User_Query( $args ); + + $this->items = $wp_user_search->get_results(); + + $this->set_pagination_args( array( + 'total_items' => $wp_user_search->get_total(), + 'per_page' => $users_per_page, + ) ); + } + + protected function get_bulk_actions() { + $actions = array(); + if ( current_user_can( 'delete_users' ) ) + $actions['delete'] = __( 'Delete' ); + $actions['spam'] = _x( 'Mark as Spam', 'user' ); + $actions['notspam'] = _x( 'Not Spam', 'user' ); + + return $actions; + } + + public function no_items() { + _e( 'No users found.' ); + } + + protected function get_views() { + global $role; + + $total_users = get_user_count(); + $super_admins = get_super_admins(); + $total_admins = count( $super_admins ); + + $class = $role != 'super' ? ' class="current"' : ''; + $role_links = array(); + $role_links['all'] = "" . sprintf( _nx( 'All (%s)', 'All (%s)', $total_users, 'users' ), number_format_i18n( $total_users ) ) . ''; + $class = $role == 'super' ? ' class="current"' : ''; + $role_links['super'] = "" . sprintf( _n( 'Super Admin (%s)', 'Super Admins (%s)', $total_admins ), number_format_i18n( $total_admins ) ) . ''; + + return $role_links; + } + + /** + * @global string $mode + * @param string $which + */ + protected function pagination( $which ) { + global $mode; + + parent::pagination ( $which ); + + if ( 'top' == $which ) + $this->view_switcher( $mode ); + } + + public function get_columns() { + $users_columns = array( + 'cb' => '', + 'username' => __( 'Username' ), + 'name' => __( 'Name' ), + 'email' => __( 'E-mail' ), + 'registered' => _x( 'Registered', 'user' ), + 'blogs' => __( 'Sites' ) + ); + /** + * Filter the columns displayed in the Network Admin Users list table. + * + * @since MU + * + * @param array $users_columns An array of user columns. Default 'cb', 'username', + * 'name', 'email', 'registered', 'blogs'. + */ + $users_columns = apply_filters( 'wpmu_users_columns', $users_columns ); + + return $users_columns; + } + + protected function get_sortable_columns() { + return array( + 'username' => 'login', + 'name' => 'name', + 'email' => 'email', + 'registered' => 'id', + ); + } + + public function display_rows() { + global $mode; + + $alt = ''; + $super_admins = get_super_admins(); + foreach ( $this->items as $user ) { + $alt = ( 'alternate' == $alt ) ? '' : 'alternate'; + + $status_list = array( 'spam' => 'site-spammed', 'deleted' => 'site-deleted' ); + + foreach ( $status_list as $status => $col ) { + if ( $user->$status ) + $alt .= " $col"; + } + + ?> + + get_column_info(); + + foreach ( $columns as $column_name => $column_display_name ) : + $class = "class='$column_name column-$column_name'"; + + $style = ''; + if ( in_array( $column_name, $hidden ) ) + $style = ' style="display:none;"'; + + $attributes = "$class$style"; + + switch ( $column_name ) { + case 'cb': ?> + + + + + user_email, 32 ); + $edit_link = esc_url( add_query_arg( 'wp_http_referer', urlencode( wp_unslash( $_SERVER['REQUEST_URI'] ) ), get_edit_user_link( $user->ID ) ) ); + + echo ""; ?> + user_login; ?>user_login, $super_admins ) ) + echo ' - ' . __( 'Super Admin' ); + ?> +
    + ' . __( 'Edit' ) . ''; + + if ( current_user_can( 'delete_user', $user->ID ) && ! in_array( $user->user_login, $super_admins ) ) { + $actions['delete'] = '' . __( 'Delete' ) . ''; + } + + /** + * Filter the action links displayed under each user + * in the Network Admin Users list table. + * + * @since 3.2.0 + * + * @param array $actions An array of action links to be displayed. + * Default 'Edit', 'Delete'. + * @param WP_User $user WP_User object. + */ + $actions = apply_filters( 'ms_user_row_actions', $actions, $user ); + echo $this->row_actions( $actions ); + ?> + + $user->first_name $user->last_name"; + break; + + case 'email': + echo "$user->user_email"; + break; + + case 'registered': + if ( 'list' == $mode ) + $date = 'Y/m/d'; + else + $date = 'Y/m/d \<\b\r \/\> g:i:s a'; + + echo "" . mysql2date( $date, $user->user_registered ) . ""; + break; + + case 'blogs': + $blogs = get_blogs_of_user( $user->ID, true ); + echo ""; + if ( is_array( $blogs ) ) { + foreach ( (array) $blogs as $key => $val ) { + if ( !can_edit_network( $val->site_id ) ) + continue; + + $path = ( $val->path == '/' ) ? '' : $val->path; + echo ''; + echo '' . str_replace( '.' . get_current_site()->domain, '', $val->domain . $path ) . ''; + echo ' '; + $actions = array(); + $actions['edit'] = '' . __( 'Edit' ) . ''; + + $class = ''; + if ( get_blog_status( $val->userblog_id, 'spam' ) == 1 ) + $class .= 'site-spammed '; + if ( get_blog_status( $val->userblog_id, 'mature' ) == 1 ) + $class .= 'site-mature '; + if ( get_blog_status( $val->userblog_id, 'deleted' ) == 1 ) + $class .= 'site-deleted '; + if ( get_blog_status( $val->userblog_id, 'archived' ) == 1 ) + $class .= 'site-archived '; + + $actions['view'] = '' . __( 'View' ) . ''; + + /** + * Filter the action links displayed next the sites a user belongs to + * in the Network Admin Users list table. + * + * @since 3.1.0 + * + * @param array $actions An array of action links to be displayed. + * Default 'Edit', 'View'. + * @param int $userblog_id The site ID. + */ + $actions = apply_filters( 'ms_user_list_site_actions', $actions, $val->userblog_id ); + + $i=0; + $action_count = count( $actions ); + foreach ( $actions as $action => $link ) { + ++$i; + ( $i == $action_count ) ? $sep = '' : $sep = ' | '; + echo "$link$sep"; + } + echo '
    '; + } + } + ?> + + "; + /** This filter is documented in wp-admin/includes/class-wp-users-list-table.php */ + echo apply_filters( 'manage_users_custom_column', '', $column_name, $user->ID ); + echo ""; + break; + } + endforeach + ?> + + no_update ) ) { + foreach ( $plugin_info->no_update as $plugin ) { + $slugs[] = $plugin->slug; + } + } + + if ( isset( $plugin_info->response ) ) { + foreach ( $plugin_info->response as $plugin ) { + $slugs[] = $plugin->slug; + } + } + + return $slugs; + } + + public function prepare_items() { + include( ABSPATH . 'wp-admin/includes/plugin-install.php' ); + + global $tabs, $tab, $paged, $type, $term; + + wp_reset_vars( array( 'tab' ) ); + + $paged = $this->get_pagenum(); + + $per_page = 30; + + // These are the tabs which are shown on the page + $tabs = array(); + + if ( 'search' == $tab ) + $tabs['search'] = __( 'Search Results' ); + $tabs['featured'] = _x( 'Featured', 'Plugin Installer' ); + $tabs['popular'] = _x( 'Popular', 'Plugin Installer' ); + $tabs['recommended'] = _x( 'Recommended', 'Plugin Installer' ); + $tabs['favorites'] = _x( 'Favorites', 'Plugin Installer' ); + if ( $tab === 'beta' || false !== strpos( $GLOBALS['wp_version'], '-' ) ) { + $tabs['beta'] = _x( 'Beta Testing', 'Plugin Installer' ); + } + if ( current_user_can( 'upload_plugins' ) ) { + // No longer a real tab. Here for filter compatibility. + // Gets skipped in get_views(). + $tabs['upload'] = __( 'Upload Plugin' ); + } + + $nonmenu_tabs = array( 'plugin-information' ); // Valid actions to perform which do not have a Menu item. + + /** + * Filter the tabs shown on the Plugin Install screen. + * + * @since 2.7.0 + * + * @param array $tabs The tabs shown on the Plugin Install screen. Defaults are 'dashboard', 'search', + * 'upload', 'featured', 'popular', 'new', and 'favorites'. + */ + $tabs = apply_filters( 'install_plugins_tabs', $tabs ); + + /** + * Filter tabs not associated with a menu item on the Plugin Install screen. + * + * @since 2.7.0 + * + * @param array $nonmenu_tabs The tabs that don't have a Menu item on the Plugin Install screen. + */ + $nonmenu_tabs = apply_filters( 'install_plugins_nonmenu_tabs', $nonmenu_tabs ); + + // If a non-valid menu tab has been selected, And it's not a non-menu action. + if ( empty( $tab ) || ( !isset( $tabs[ $tab ] ) && !in_array( $tab, (array) $nonmenu_tabs ) ) ) + $tab = key( $tabs ); + + $args = array( + 'page' => $paged, + 'per_page' => $per_page, + 'fields' => array( 'last_updated' => true, 'downloaded' => true, 'icons' => true ), + // Send the locale and installed plugin slugs to the API so it can provide context-sensitive results. + 'locale' => get_locale(), + 'installed_plugins' => $this->get_installed_plugin_slugs(), + ); + + switch ( $tab ) { + case 'search': + $type = isset( $_REQUEST['type'] ) ? wp_unslash( $_REQUEST['type'] ) : 'term'; + $term = isset( $_REQUEST['s'] ) ? wp_unslash( $_REQUEST['s'] ) : ''; + + switch ( $type ) { + case 'tag': + $args['tag'] = sanitize_title_with_dashes( $term ); + break; + case 'term': + $args['search'] = $term; + break; + case 'author': + $args['author'] = $term; + break; + } + + break; + + case 'featured': + $args['fields']['group'] = true; + $this->orderby = 'group'; + // No break! + case 'popular': + case 'new': + case 'beta': + case 'recommended': + $args['browse'] = $tab; + break; + + case 'favorites': + $user = isset( $_GET['user'] ) ? wp_unslash( $_GET['user'] ) : get_user_option( 'wporg_favorites' ); + update_user_meta( get_current_user_id(), 'wporg_favorites', $user ); + if ( $user ) + $args['user'] = $user; + else + $args = false; + + add_action( 'install_plugins_favorites', 'install_plugins_favorites_form', 9, 0 ); + break; + + default: + $args = false; + break; + } + + /** + * Filter API request arguments for each Plugin Install screen tab. + * + * The dynamic portion of the hook name, `$tab`, refers to the plugin install tabs. + * Default tabs are 'dashboard', 'search', 'upload', 'featured', 'popular', 'new', + * and 'favorites'. + * + * @since 3.7.0 + * + * @param array|bool $args Plugin Install API arguments. + */ + $args = apply_filters( "install_plugins_table_api_args_$tab", $args ); + + if ( !$args ) + return; + + $api = plugins_api( 'query_plugins', $args ); + + if ( is_wp_error( $api ) ) { + $this->error = $api; + return; + } + + $this->items = $api->plugins; + + if ( $this->orderby ) { + uasort( $this->items, array( $this, 'order_callback' ) ); + } + + $this->set_pagination_args( array( + 'total_items' => $api->info['results'], + 'per_page' => $args['per_page'], + ) ); + + if ( isset( $api->info['groups'] ) ) { + $this->groups = $api->info['groups']; + } + } + + public function no_items() { + if ( isset( $this->error ) ) { + $message = $this->error->get_error_message() . '

    ' . __( 'Try again' ) . '

    '; + } else { + $message = __( 'No plugins match your request.' ); + } + echo '
    ' . $message . '
    '; + } + + protected function get_views() { + global $tabs, $tab; + + $display_tabs = array(); + foreach ( (array) $tabs as $action => $text ) { + $class = ( $action == $tab ) ? ' current' : ''; + $href = self_admin_url('plugin-install.php?tab=' . $action); + $display_tabs['plugin-install-'.$action] = "$text"; + } + // No longer a real tab. + unset( $display_tabs['plugin-install-upload'] ); + + return $display_tabs; + } + + /** + * Override parent views so we can use the filter bar display. + */ + public function views() { + $views = $this->get_views(); + + /** This filter is documented in wp-admin/inclues/class-wp-list-table.php */ + $views = apply_filters( "views_{$this->screen->id}", $views ); + +?> +
    + + + +
    +_args['singular']; + + $data_attr = ''; + + if ( $singular ) { + $data_attr = " data-wp-lists='list:$singular'"; + } + + $this->display_tablenav( 'top' ); + +?> +
    + +
    > + display_rows_or_placeholder(); ?> +
    +
    +display_tablenav( 'bottom' ); + } + + /** + * @param string $which + */ + protected function display_tablenav( $which ) { + if ( $GLOBALS['tab'] === 'featured' ) { + return; + } + + if ( 'top' == $which ) { + wp_referer_field(); + ?> +
    +
    + +
    + pagination( $which ); ?> +
    +
    + +
    + pagination( $which ); ?> +
    +
    + _args['plural'] ); + } + + public function get_columns() { + return array(); + } + + /** + * @param object $plugin_a + * @param object $plugin_b + * @return int + */ + private function order_callback( $plugin_a, $plugin_b ) { + $orderby = $this->orderby; + if ( ! isset( $plugin_a->$orderby, $plugin_b->$orderby ) ) { + return 0; + } + + $a = $plugin_a->$orderby; + $b = $plugin_b->$orderby; + + if ( $a == $b ) { + return 0; + } + + if ( 'DESC' == $this->order ) { + return ( $a < $b ) ? 1 : -1; + } else { + return ( $a < $b ) ? -1 : 1; + } + } + + public function display_rows() { + $plugins_allowedtags = array( + 'a' => array( 'href' => array(),'title' => array(), 'target' => array() ), + 'abbr' => array( 'title' => array() ),'acronym' => array( 'title' => array() ), + 'code' => array(), 'pre' => array(), 'em' => array(),'strong' => array(), + 'ul' => array(), 'ol' => array(), 'li' => array(), 'p' => array(), 'br' => array() + ); + + $plugins_group_titles = array( + 'Performance' => _x( 'Performance', 'Plugin installer group title' ), + 'Social' => _x( 'Social', 'Plugin installer group title' ), + 'Tools' => _x( 'Tools', 'Plugin installer group title' ), + ); + + $group = null; + + foreach ( (array) $this->items as $plugin ) { + if ( is_object( $plugin ) ) { + $plugin = (array) $plugin; + } + + // Display the group heading if there is one + if ( isset( $plugin['group'] ) && $plugin['group'] != $group ) { + if ( isset( $this->groups[ $plugin['group'] ] ) ) { + $group_name = $this->groups[ $plugin['group'] ]; + if ( isset( $plugins_group_titles[ $group_name ] ) ) { + $group_name = $plugins_group_titles[ $group_name ]; + } + } else { + $group_name = $plugin['group']; + } + + // Starting a new group, close off the divs of the last one + if ( ! empty( $group ) ) { + echo ''; + } + + echo '

    ' . esc_html( $group_name ) . '

    '; + // needs an extra wrapping div for nth-child selectors to work + echo '
    '; + + $group = $plugin['group']; + } + $title = wp_kses( $plugin['name'], $plugins_allowedtags ); + + // Remove any HTML from the description. + $description = strip_tags( $plugin['short_description'] ); + $version = wp_kses( $plugin['version'], $plugins_allowedtags ); + + $name = strip_tags( $title . ' ' . $version ); + + $author = wp_kses( $plugin['author'], $plugins_allowedtags ); + if ( ! empty( $author ) ) { + $author = ' ' . sprintf( __( 'By %s' ), $author ) . ''; + } + + $action_links = array(); + + if ( current_user_can( 'install_plugins' ) || current_user_can( 'update_plugins' ) ) { + $status = install_plugin_install_status( $plugin ); + + switch ( $status['status'] ) { + case 'install': + if ( $status['url'] ) { + /* translators: 1: Plugin name and version. */ + $action_links[] = '' . __( 'Install Now' ) . ''; + } + + break; + case 'update_available': + if ( $status['url'] ) { + /* translators: 1: Plugin name and version */ + $action_links[] = '' . __( 'Update Now' ) . ''; + } + + break; + case 'latest_installed': + case 'newer_installed': + $action_links[] = '' . _x( 'Installed', 'plugin' ) . ''; + break; + } + } + + $details_link = self_admin_url( 'plugin-install.php?tab=plugin-information&plugin=' . $plugin['slug'] . + '&TB_iframe=true&width=600&height=550' ); + + /* translators: 1: Plugin name and version. */ + $action_links[] = '' . __( 'More Details' ) . ''; + + if ( !empty( $plugin['icons']['svg'] ) ) { + $plugin_icon_url = $plugin['icons']['svg']; + } elseif ( !empty( $plugin['icons']['2x'] ) ) { + $plugin_icon_url = $plugin['icons']['2x']; + } elseif ( !empty( $plugin['icons']['1x'] ) ) { + $plugin_icon_url = $plugin['icons']['1x']; + } else { + $plugin_icon_url = $plugin['icons']['default']; + } + + /** + * Filter the install action links for a plugin. + * + * @since 2.7.0 + * + * @param array $action_links An array of plugin action hyperlinks. Defaults are links to Details and Install Now. + * @param array $plugin The plugin currently being listed. + */ + $action_links = apply_filters( 'plugin_install_action_links', $action_links, $plugin ); + ?> +
    +
    + +
    +

    +
    + +
    +

    +

    +
    +
    +
    +
    + $plugin['rating'], 'type' => 'percent', 'number' => $plugin['num_ratings'] ) ); ?> + () +
    +
    + + + +
    +
    + +
    +
    + ' ) ) { + echo '' . __( 'Untested with your version of WordPress' ) . ''; + } elseif ( ! empty( $plugin['requires'] ) && version_compare( substr( $GLOBALS['wp_version'], 0, strlen( $plugin['requires'] ) ), $plugin['requires'], '<' ) ) { + echo '' . __( 'Incompatible with your version of WordPress' ) . ''; + } else { + echo '' . __( 'Compatible with your version of WordPress' ) . ''; + } + ?> +
    +
    +
    +
    '; + } + } +} diff --git a/wp-admin/includes/class-wp-plugins-list-table.php b/wp-admin/includes/class-wp-plugins-list-table.php new file mode 100644 index 0000000..1ade73c --- /dev/null +++ b/wp-admin/includes/class-wp-plugins-list-table.php @@ -0,0 +1,634 @@ + 'plugins', + 'screen' => isset( $args['screen'] ) ? $args['screen'] : null, + ) ); + + $status = 'all'; + if ( isset( $_REQUEST['plugin_status'] ) && in_array( $_REQUEST['plugin_status'], array( 'active', 'inactive', 'recently_activated', 'upgrade', 'mustuse', 'dropins', 'search' ) ) ) + $status = $_REQUEST['plugin_status']; + + if ( isset($_REQUEST['s']) ) + $_SERVER['REQUEST_URI'] = add_query_arg('s', wp_unslash($_REQUEST['s']) ); + + $page = $this->get_pagenum(); + } + + protected function get_table_classes() { + return array( 'widefat', $this->_args['plural'] ); + } + + public function ajax_user_can() { + return current_user_can('activate_plugins'); + } + + public function prepare_items() { + global $status, $plugins, $totals, $page, $orderby, $order, $s; + + wp_reset_vars( array( 'orderby', 'order', 's' ) ); + + /** + * Filter the full array of plugins to list in the Plugins list table. + * + * @since 3.0.0 + * + * @see get_plugins() + * + * @param array $plugins An array of plugins to display in the list table. + */ + $plugins = array( + 'all' => apply_filters( 'all_plugins', get_plugins() ), + 'search' => array(), + 'active' => array(), + 'inactive' => array(), + 'recently_activated' => array(), + 'upgrade' => array(), + 'mustuse' => array(), + 'dropins' => array() + ); + + $screen = $this->screen; + + if ( ! is_multisite() || ( $screen->in_admin( 'network' ) && current_user_can( 'manage_network_plugins' ) ) ) { + + /** + * Filter whether to display the advanced plugins list table. + * + * There are two types of advanced plugins - must-use and drop-ins - + * which can be used in a single site or Multisite network. + * + * The $type parameter allows you to differentiate between the type of advanced + * plugins to filter the display of. Contexts include 'mustuse' and 'dropins'. + * + * @since 3.0.0 + * + * @param bool $show Whether to show the advanced plugins for the specified + * plugin type. Default true. + * @param string $type The plugin type. Accepts 'mustuse', 'dropins'. + */ + if ( apply_filters( 'show_advanced_plugins', true, 'mustuse' ) ) { + $plugins['mustuse'] = get_mu_plugins(); + } + + /** This action is documented in wp-admin/includes/class-wp-plugins-list-table.php */ + if ( apply_filters( 'show_advanced_plugins', true, 'dropins' ) ) + $plugins['dropins'] = get_dropins(); + + if ( current_user_can( 'update_plugins' ) ) { + $current = get_site_transient( 'update_plugins' ); + foreach ( (array) $plugins['all'] as $plugin_file => $plugin_data ) { + if ( isset( $current->response[ $plugin_file ] ) ) { + $plugins['all'][ $plugin_file ]['update'] = true; + $plugins['upgrade'][ $plugin_file ] = $plugins['all'][ $plugin_file ]; + } + } + } + } + + set_transient( 'plugin_slugs', array_keys( $plugins['all'] ), DAY_IN_SECONDS ); + + if ( ! $screen->in_admin( 'network' ) ) { + $recently_activated = get_option( 'recently_activated', array() ); + + foreach ( $recently_activated as $key => $time ) + if ( $time + WEEK_IN_SECONDS < time() ) + unset( $recently_activated[$key] ); + update_option( 'recently_activated', $recently_activated ); + } + + $plugin_info = get_site_transient( 'update_plugins' ); + + foreach ( (array) $plugins['all'] as $plugin_file => $plugin_data ) { + // Extra info if known. array_merge() ensures $plugin_data has precedence if keys collide. + if ( isset( $plugin_info->response[ $plugin_file ] ) ) { + $plugins['all'][ $plugin_file ] = $plugin_data = array_merge( (array) $plugin_info->response[ $plugin_file ], $plugin_data ); + } elseif ( isset( $plugin_info->no_update[ $plugin_file ] ) ) { + $plugins['all'][ $plugin_file ] = $plugin_data = array_merge( (array) $plugin_info->no_update[ $plugin_file ], $plugin_data ); + } + + // Filter into individual sections + if ( is_multisite() && ! $screen->in_admin( 'network' ) && is_network_only_plugin( $plugin_file ) && ! is_plugin_active( $plugin_file ) ) { + // On the non-network screen, filter out network-only plugins as long as they're not individually activated + unset( $plugins['all'][ $plugin_file ] ); + } elseif ( ! $screen->in_admin( 'network' ) && is_plugin_active_for_network( $plugin_file ) ) { + // On the non-network screen, filter out network activated plugins + unset( $plugins['all'][ $plugin_file ] ); + } elseif ( ( ! $screen->in_admin( 'network' ) && is_plugin_active( $plugin_file ) ) + || ( $screen->in_admin( 'network' ) && is_plugin_active_for_network( $plugin_file ) ) ) { + // On the non-network screen, populate the active list with plugins that are individually activated + // On the network-admin screen, populate the active list with plugins that are network activated + $plugins['active'][ $plugin_file ] = $plugin_data; + } else { + if ( ! $screen->in_admin( 'network' ) && isset( $recently_activated[ $plugin_file ] ) ) { + // On the non-network screen, populate the recently activated list with plugins that have been recently activated + $plugins['recently_activated'][ $plugin_file ] = $plugin_data; + } + // Populate the inactive list with plugins that aren't activated + $plugins['inactive'][ $plugin_file ] = $plugin_data; + } + } + + if ( $s ) { + $status = 'search'; + $plugins['search'] = array_filter( $plugins['all'], array( $this, '_search_callback' ) ); + } + + $totals = array(); + foreach ( $plugins as $type => $list ) + $totals[ $type ] = count( $list ); + + if ( empty( $plugins[ $status ] ) && !in_array( $status, array( 'all', 'search' ) ) ) + $status = 'all'; + + $this->items = array(); + foreach ( $plugins[ $status ] as $plugin_file => $plugin_data ) { + // Translate, Don't Apply Markup, Sanitize HTML + $this->items[$plugin_file] = _get_plugin_data_markup_translate( $plugin_file, $plugin_data, false, true ); + } + + $total_this_page = $totals[ $status ]; + + if ( $orderby ) { + $orderby = ucfirst( $orderby ); + $order = strtoupper( $order ); + + uasort( $this->items, array( $this, '_order_callback' ) ); + } + + $plugins_per_page = $this->get_items_per_page( str_replace( '-', '_', $screen->id . '_per_page' ), 999 ); + + $start = ( $page - 1 ) * $plugins_per_page; + + if ( $total_this_page > $plugins_per_page ) + $this->items = array_slice( $this->items, $start, $plugins_per_page ); + + $this->set_pagination_args( array( + 'total_items' => $total_this_page, + 'per_page' => $plugins_per_page, + ) ); + } + + /** + * @staticvar string $term + * @param array $plugin + * @return boolean + */ + public function _search_callback( $plugin ) { + static $term; + if ( is_null( $term ) ) + $term = wp_unslash( $_REQUEST['s'] ); + + foreach ( $plugin as $value ) { + if ( false !== stripos( strip_tags( $value ), $term ) ) { + return true; + } + } + + return false; + } + + /** + * @global string $orderby + * @global string $order + * @param array $plugin_a + * @param array $plugin_b + * @return int + */ + public function _order_callback( $plugin_a, $plugin_b ) { + global $orderby, $order; + + $a = $plugin_a[$orderby]; + $b = $plugin_b[$orderby]; + + if ( $a == $b ) + return 0; + + if ( 'DESC' == $order ) + return ( $a < $b ) ? 1 : -1; + else + return ( $a < $b ) ? -1 : 1; + } + + public function no_items() { + global $plugins; + + if ( !empty( $plugins['all'] ) ) + _e( 'No plugins found.' ); + else + _e( 'You do not appear to have any plugins available at this time.' ); + } + + public function get_columns() { + global $status; + + return array( + 'cb' => !in_array( $status, array( 'mustuse', 'dropins' ) ) ? '' : '', + 'name' => __( 'Plugin' ), + 'description' => __( 'Description' ), + ); + } + + protected function get_sortable_columns() { + return array(); + } + + protected function get_views() { + global $totals, $status; + + $status_links = array(); + foreach ( $totals as $type => $count ) { + if ( !$count ) + continue; + + switch ( $type ) { + case 'all': + $text = _nx( 'All (%s)', 'All (%s)', $count, 'plugins' ); + break; + case 'active': + $text = _n( 'Active (%s)', 'Active (%s)', $count ); + break; + case 'recently_activated': + $text = _n( 'Recently Active (%s)', 'Recently Active (%s)', $count ); + break; + case 'inactive': + $text = _n( 'Inactive (%s)', 'Inactive (%s)', $count ); + break; + case 'mustuse': + $text = _n( 'Must-Use (%s)', 'Must-Use (%s)', $count ); + break; + case 'dropins': + $text = _n( 'Drop-ins (%s)', 'Drop-ins (%s)', $count ); + break; + case 'upgrade': + $text = _n( 'Update Available (%s)', 'Update Available (%s)', $count ); + break; + } + + if ( 'search' != $type ) { + $status_links[$type] = sprintf( "%s", + add_query_arg('plugin_status', $type, 'plugins.php'), + ( $type == $status ) ? ' class="current"' : '', + sprintf( $text, number_format_i18n( $count ) ) + ); + } + } + + return $status_links; + } + + protected function get_bulk_actions() { + global $status; + + $actions = array(); + + if ( 'active' != $status ) + $actions['activate-selected'] = $this->screen->in_admin( 'network' ) ? __( 'Network Activate' ) : __( 'Activate' ); + + if ( 'inactive' != $status && 'recent' != $status ) + $actions['deactivate-selected'] = $this->screen->in_admin( 'network' ) ? __( 'Network Deactivate' ) : __( 'Deactivate' ); + + if ( !is_multisite() || $this->screen->in_admin( 'network' ) ) { + if ( current_user_can( 'update_plugins' ) ) + $actions['update-selected'] = __( 'Update' ); + if ( current_user_can( 'delete_plugins' ) && ( 'active' != $status ) ) + $actions['delete-selected'] = __( 'Delete' ); + } + + return $actions; + } + + /** + * @global string $status + * @param string $which + * @return null + */ + public function bulk_actions( $which = '' ) { + global $status; + + if ( in_array( $status, array( 'mustuse', 'dropins' ) ) ) + return; + + parent::bulk_actions( $which ); + } + + /** + * @global string $status + * @param string $which + * @return null + */ + protected function extra_tablenav( $which ) { + global $status; + + if ( ! in_array($status, array('recently_activated', 'mustuse', 'dropins') ) ) + return; + + echo '
    '; + + if ( ! $this->screen->in_admin( 'network' ) && 'recently_activated' == $status ) + submit_button( __( 'Clear List' ), 'button', 'clear-recent-list', false ); + elseif ( 'top' == $which && 'mustuse' == $status ) + echo '

    ' . sprintf( __( 'Files in the %s directory are executed automatically.' ), str_replace( ABSPATH, '/', WPMU_PLUGIN_DIR ) ) . '

    '; + elseif ( 'top' == $which && 'dropins' == $status ) + echo '

    ' . sprintf( __( 'Drop-ins are advanced plugins in the %s directory that replace WordPress functionality when present.' ), str_replace( ABSPATH, '', WP_CONTENT_DIR ) ) . '

    '; + + echo '
    '; + } + + public function current_action() { + if ( isset($_POST['clear-recent-list']) ) + return 'clear-recent-list'; + + return parent::current_action(); + } + + public function display_rows() { + global $status; + + if ( is_multisite() && ! $this->screen->in_admin( 'network' ) && in_array( $status, array( 'mustuse', 'dropins' ) ) ) + return; + + foreach ( $this->items as $plugin_file => $plugin_data ) + $this->single_row( array( $plugin_file, $plugin_data ) ); + } + + /** + * @global string $status + * @global int $page + * @global string $s + * @global array $totals + * @param array $item + */ + public function single_row( $item ) { + global $status, $page, $s, $totals; + + list( $plugin_file, $plugin_data ) = $item; + $context = $status; + $screen = $this->screen; + + // Pre-order. + $actions = array( + 'deactivate' => '', + 'activate' => '', + 'details' => '', + 'edit' => '', + 'delete' => '', + ); + + if ( 'mustuse' == $context ) { + $is_active = true; + } elseif ( 'dropins' == $context ) { + $dropins = _get_dropins(); + $plugin_name = $plugin_file; + if ( $plugin_file != $plugin_data['Name'] ) + $plugin_name .= '
    ' . $plugin_data['Name']; + if ( true === ( $dropins[ $plugin_file ][1] ) ) { // Doesn't require a constant + $is_active = true; + $description = '

    ' . $dropins[ $plugin_file ][0] . '

    '; + } elseif ( defined( $dropins[ $plugin_file ][1] ) && constant( $dropins[ $plugin_file ][1] ) ) { // Constant is true + $is_active = true; + $description = '

    ' . $dropins[ $plugin_file ][0] . '

    '; + } else { + $is_active = false; + $description = '

    ' . $dropins[ $plugin_file ][0] . ' ' . __('Inactive:') . ' ' . sprintf( __( 'Requires %s in wp-config.php.' ), "define('" . $dropins[ $plugin_file ][1] . "', true);" ) . '

    '; + } + if ( $plugin_data['Description'] ) + $description .= '

    ' . $plugin_data['Description'] . '

    '; + } else { + if ( $screen->in_admin( 'network' ) ) + $is_active = is_plugin_active_for_network( $plugin_file ); + else + $is_active = is_plugin_active( $plugin_file ); + + if ( $screen->in_admin( 'network' ) ) { + if ( $is_active ) { + if ( current_user_can( 'manage_network_plugins' ) ) + $actions['deactivate'] = '' . __('Network Deactivate') . ''; + } else { + if ( current_user_can( 'manage_network_plugins' ) ) + $actions['activate'] = '' . __('Network Activate') . ''; + if ( current_user_can( 'delete_plugins' ) && ! is_plugin_active( $plugin_file ) ) + $actions['delete'] = '' . __('Delete') . ''; + } + } else { + if ( $is_active ) { + $actions['deactivate'] = '' . __('Deactivate') . ''; + } else { + $actions['activate'] = '' . __('Activate') . ''; + + if ( ! is_multisite() && current_user_can('delete_plugins') ) + $actions['delete'] = '' . __('Delete') . ''; + } // end if $is_active + + } // end if $screen->in_admin( 'network' ) + + if ( ( ! is_multisite() || $screen->in_admin( 'network' ) ) && current_user_can('edit_plugins') && is_writable(WP_PLUGIN_DIR . '/' . $plugin_file) ) + $actions['edit'] = '' . __('Edit') . ''; + } // end if $context + + $prefix = $screen->in_admin( 'network' ) ? 'network_admin_' : ''; + + /** + * Filter the action links displayed for each plugin in the Plugins list table. + * + * The dynamic portion of the hook name, `$prefix`, refers to the context the + * action links are displayed in. The 'network_admin_' prefix is used if the + * current screen is the Network plugins list table. The prefix is empty ('') + * if the current screen is the site plugins list table. + * + * The default action links for the Network plugins list table include + * 'Network Activate', 'Network Deactivate', 'Edit', and 'Delete'. + * + * The default action links for the site plugins list table include + * 'Activate', 'Deactivate', and 'Edit', for a network site, and + * 'Activate', 'Deactivate', 'Edit', and 'Delete' for a single site. + * + * @since 2.5.0 + * + * @param array $actions An array of plugin action links. + * @param string $plugin_file Path to the plugin file. + * @param array $plugin_data An array of plugin data. + * @param string $context The plugin context. Defaults are 'All', 'Active', + * 'Inactive', 'Recently Activated', 'Upgrade', + * 'Must-Use', 'Drop-ins', 'Search'. + */ + $actions = apply_filters( $prefix . 'plugin_action_links', array_filter( $actions ), $plugin_file, $plugin_data, $context ); + + /** + * Filter the list of action links displayed for a specific plugin. + * + * The first dynamic portion of the hook name, $prefix, refers to the context + * the action links are displayed in. The 'network_admin_' prefix is used if the + * current screen is the Network plugins list table. The prefix is empty ('') + * if the current screen is the site plugins list table. + * + * The second dynamic portion of the hook name, $plugin_file, refers to the path + * to the plugin file, relative to the plugins directory. + * + * @since 2.7.0 + * + * @param array $actions An array of plugin action links. + * @param string $plugin_file Path to the plugin file. + * @param array $plugin_data An array of plugin data. + * @param string $context The plugin context. Defaults are 'All', 'Active', + * 'Inactive', 'Recently Activated', 'Upgrade', + * 'Must-Use', 'Drop-ins', 'Search'. + */ + $actions = apply_filters( $prefix . "plugin_action_links_$plugin_file", $actions, $plugin_file, $plugin_data, $context ); + + $class = $is_active ? 'active' : 'inactive'; + $checkbox_id = "checkbox_" . md5($plugin_data['Name']); + if ( in_array( $status, array( 'mustuse', 'dropins' ) ) ) { + $checkbox = ''; + } else { + $checkbox = "" + . ""; + } + if ( 'dropins' != $context ) { + $description = '

    ' . ( $plugin_data['Description'] ? $plugin_data['Description'] : ' ' ) . '

    '; + $plugin_name = $plugin_data['Name']; + } + + $id = sanitize_title( $plugin_name ); + if ( ! empty( $totals['upgrade'] ) && ! empty( $plugin_data['update'] ) ) + $class .= ' update'; + + echo ""; + + list( $columns, $hidden ) = $this->get_column_info(); + + foreach ( $columns as $column_name => $column_display_name ) { + $style = ''; + if ( in_array( $column_name, $hidden ) ) + $style = ' style="display:none;"'; + + switch ( $column_name ) { + case 'cb': + echo "$checkbox"; + break; + case 'name': + echo "$plugin_name"; + echo $this->row_actions( $actions, true ); + echo ""; + break; + case 'description': + echo " +
    $description
    +
    "; + + $plugin_meta = array(); + if ( !empty( $plugin_data['Version'] ) ) + $plugin_meta[] = sprintf( __( 'Version %s' ), $plugin_data['Version'] ); + if ( !empty( $plugin_data['Author'] ) ) { + $author = $plugin_data['Author']; + if ( !empty( $plugin_data['AuthorURI'] ) ) + $author = '' . $plugin_data['Author'] . ''; + $plugin_meta[] = sprintf( __( 'By %s' ), $author ); + } + + // Details link using API info, if available + if ( isset( $plugin_data['slug'] ) && current_user_can( 'install_plugins' ) ) { + $plugin_meta[] = sprintf( '%s', + esc_url( network_admin_url( 'plugin-install.php?tab=plugin-information&plugin=' . $plugin_data['slug'] . + '&TB_iframe=true&width=600&height=550' ) ), + esc_attr( sprintf( __( 'More information about %s' ), $plugin_name ) ), + esc_attr( $plugin_name ), + __( 'View details' ) + ); + } elseif ( ! empty( $plugin_data['PluginURI'] ) ) { + $plugin_meta[] = sprintf( '%s', + esc_url( $plugin_data['PluginURI'] ), + __( 'Visit plugin site' ) + ); + } + + /** + * Filter the array of row meta for each plugin in the Plugins list table. + * + * @since 2.8.0 + * + * @param array $plugin_meta An array of the plugin's metadata, + * including the version, author, + * author URI, and plugin URI. + * @param string $plugin_file Path to the plugin file, relative to the plugins directory. + * @param array $plugin_data An array of plugin data. + * @param string $status Status of the plugin. Defaults are 'All', 'Active', + * 'Inactive', 'Recently Activated', 'Upgrade', 'Must-Use', + * 'Drop-ins', 'Search'. + */ + $plugin_meta = apply_filters( 'plugin_row_meta', $plugin_meta, $plugin_file, $plugin_data, $status ); + echo implode( ' | ', $plugin_meta ); + + echo "
    "; + break; + default: + echo ""; + + /** + * Fires inside each custom column of the Plugins list table. + * + * @since 3.1.0 + * + * @param string $column_name Name of the column. + * @param string $plugin_file Path to the plugin file. + * @param array $plugin_data An array of plugin data. + */ + do_action( 'manage_plugins_custom_column', $column_name, $plugin_file, $plugin_data ); + echo ""; + } + } + + echo ""; + + /** + * Fires after each row in the Plugins list table. + * + * @since 2.3.0 + * + * @param string $plugin_file Path to the plugin file, relative to the plugins directory. + * @param array $plugin_data An array of plugin data. + * @param string $status Status of the plugin. Defaults are 'All', 'Active', + * 'Inactive', 'Recently Activated', 'Upgrade', 'Must-Use', + * 'Drop-ins', 'Search'. + */ + do_action( 'after_plugin_row', $plugin_file, $plugin_data, $status ); + + /** + * Fires after each specific row in the Plugins list table. + * + * The dynamic portion of the hook name, `$plugin_file`, refers to the path + * to the plugin file, relative to the plugins directory. + * + * @since 2.7.0 + * + * @param string $plugin_file Path to the plugin file, relative to the plugins directory. + * @param array $plugin_data An array of plugin data. + * @param string $status Status of the plugin. Defaults are 'All', 'Active', + * 'Inactive', 'Recently Activated', 'Upgrade', 'Must-Use', + * 'Drop-ins', 'Search'. + */ + do_action( "after_plugin_row_$plugin_file", $plugin_file, $plugin_data, $status ); + } +} diff --git a/wp-admin/includes/class-wp-posts-list-table.php b/wp-admin/includes/class-wp-posts-list-table.php new file mode 100644 index 0000000..76aa1e5 --- /dev/null +++ b/wp-admin/includes/class-wp-posts-list-table.php @@ -0,0 +1,1344 @@ + 'posts', + 'screen' => isset( $args['screen'] ) ? $args['screen'] : null, + ) ); + + $post_type = $this->screen->post_type; + $post_type_object = get_post_type_object( $post_type ); + + if ( !current_user_can( $post_type_object->cap->edit_others_posts ) ) { + $exclude_states = get_post_stati( array( 'show_in_admin_all_list' => false ) ); + $this->user_posts_count = $wpdb->get_var( $wpdb->prepare( " + SELECT COUNT( 1 ) FROM $wpdb->posts + WHERE post_type = %s AND post_status NOT IN ( '" . implode( "','", $exclude_states ) . "' ) + AND post_author = %d + ", $post_type, get_current_user_id() ) ); + + if ( $this->user_posts_count && empty( $_REQUEST['post_status'] ) && empty( $_REQUEST['all_posts'] ) && empty( $_REQUEST['author'] ) && empty( $_REQUEST['show_sticky'] ) ) + $_GET['author'] = get_current_user_id(); + } + + if ( 'post' == $post_type && $sticky_posts = get_option( 'sticky_posts' ) ) { + $sticky_posts = implode( ', ', array_map( 'absint', (array) $sticky_posts ) ); + $this->sticky_posts_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT( 1 ) FROM $wpdb->posts WHERE post_type = %s AND post_status NOT IN ('trash', 'auto-draft') AND ID IN ($sticky_posts)", $post_type ) ); + } + } + + public function ajax_user_can() { + return current_user_can( get_post_type_object( $this->screen->post_type )->cap->edit_posts ); + } + + public function prepare_items() { + global $avail_post_stati, $wp_query, $per_page, $mode; + + $avail_post_stati = wp_edit_posts_query(); + + $this->hierarchical_display = ( is_post_type_hierarchical( $this->screen->post_type ) && 'menu_order title' == $wp_query->query['orderby'] ); + + $total_items = $this->hierarchical_display ? $wp_query->post_count : $wp_query->found_posts; + + $post_type = $this->screen->post_type; + $per_page = $this->get_items_per_page( 'edit_' . $post_type . '_per_page' ); + + /** This filter is documented in wp-admin/includes/post.php */ + $per_page = apply_filters( 'edit_posts_per_page', $per_page, $post_type ); + + if ( $this->hierarchical_display ) + $total_pages = ceil( $total_items / $per_page ); + else + $total_pages = $wp_query->max_num_pages; + + if ( ! empty( $_REQUEST['mode'] ) ) { + $mode = $_REQUEST['mode'] == 'excerpt' ? 'excerpt' : 'list'; + set_user_setting ( 'posts_list_mode', $mode ); + } else { + $mode = get_user_setting ( 'posts_list_mode', 'list' ); + } + + $this->is_trash = isset( $_REQUEST['post_status'] ) && $_REQUEST['post_status'] == 'trash'; + + $this->set_pagination_args( array( + 'total_items' => $total_items, + 'total_pages' => $total_pages, + 'per_page' => $per_page + ) ); + } + + public function has_items() { + return have_posts(); + } + + public function no_items() { + if ( isset( $_REQUEST['post_status'] ) && 'trash' == $_REQUEST['post_status'] ) + echo get_post_type_object( $this->screen->post_type )->labels->not_found_in_trash; + else + echo get_post_type_object( $this->screen->post_type )->labels->not_found; + } + + protected function get_views() { + global $locked_post_status, $avail_post_stati; + + $post_type = $this->screen->post_type; + + if ( !empty($locked_post_status) ) + return array(); + + $status_links = array(); + $num_posts = wp_count_posts( $post_type, 'readable' ); + $class = ''; + $allposts = ''; + + $current_user_id = get_current_user_id(); + + if ( $this->user_posts_count ) { + if ( isset( $_GET['author'] ) && ( $_GET['author'] == $current_user_id ) ) + $class = ' class="current"'; + $status_links['mine'] = "" . sprintf( _nx( 'Mine (%s)', 'Mine (%s)', $this->user_posts_count, 'posts' ), number_format_i18n( $this->user_posts_count ) ) . ''; + $allposts = '&all_posts=1'; + } + + $total_posts = array_sum( (array) $num_posts ); + + // Subtract post types that are not included in the admin all list. + foreach ( get_post_stati( array('show_in_admin_all_list' => false) ) as $state ) + $total_posts -= $num_posts->$state; + + $class = empty( $class ) && empty( $_REQUEST['post_status'] ) && empty( $_REQUEST['show_sticky'] ) ? ' class="current"' : ''; + $status_links['all'] = "" . sprintf( _nx( 'All (%s)', 'All (%s)', $total_posts, 'posts' ), number_format_i18n( $total_posts ) ) . ''; + + foreach ( get_post_stati(array('show_in_admin_status_list' => true), 'objects') as $status ) { + $class = ''; + + $status_name = $status->name; + + if ( !in_array( $status_name, $avail_post_stati ) ) + continue; + + if ( empty( $num_posts->$status_name ) ) + continue; + + if ( isset($_REQUEST['post_status']) && $status_name == $_REQUEST['post_status'] ) + $class = ' class="current"'; + + $status_links[$status_name] = "" . sprintf( translate_nooped_plural( $status->label_count, $num_posts->$status_name ), number_format_i18n( $num_posts->$status_name ) ) . ''; + } + + if ( ! empty( $this->sticky_posts_count ) ) { + $class = ! empty( $_REQUEST['show_sticky'] ) ? ' class="current"' : ''; + + $sticky_link = array( 'sticky' => "" . sprintf( _nx( 'Sticky (%s)', 'Sticky (%s)', $this->sticky_posts_count, 'posts' ), number_format_i18n( $this->sticky_posts_count ) ) . '' ); + + // Sticky comes after Publish, or if not listed, after All. + $split = 1 + array_search( ( isset( $status_links['publish'] ) ? 'publish' : 'all' ), array_keys( $status_links ) ); + $status_links = array_merge( array_slice( $status_links, 0, $split ), $sticky_link, array_slice( $status_links, $split ) ); + } + + return $status_links; + } + + protected function get_bulk_actions() { + $actions = array(); + $post_type_obj = get_post_type_object( $this->screen->post_type ); + + if ( $this->is_trash ) { + $actions['untrash'] = __( 'Restore' ); + } else { + $actions['edit'] = __( 'Edit' ); + } + + if ( current_user_can( $post_type_obj->cap->delete_posts ) ) { + if ( $this->is_trash || ! EMPTY_TRASH_DAYS ) { + $actions['delete'] = __( 'Delete Permanently' ); + } else { + $actions['trash'] = __( 'Move to Trash' ); + } + } + + return $actions; + } + + /** + * @global int $cat + * @param string $which + */ + protected function extra_tablenav( $which ) { + global $cat; +?> +
    +months_dropdown( $this->screen->post_type ); + + if ( is_object_in_taxonomy( $this->screen->post_type, 'category' ) ) { + $dropdown_options = array( + 'show_option_all' => __( 'All categories' ), + 'hide_empty' => 0, + 'hierarchical' => 1, + 'show_count' => 0, + 'orderby' => 'name', + 'selected' => $cat + ); + + echo ''; + wp_dropdown_categories( $dropdown_options ); + } + + /** + * Fires before the Filter button on the Posts and Pages list tables. + * + * The Filter button allows sorting by date and/or category on the + * Posts list table, and sorting by date on the Pages list table. + * + * @since 2.1.0 + */ + do_action( 'restrict_manage_posts' ); + + submit_button( __( 'Filter' ), 'button', 'filter_action', false, array( 'id' => 'post-query-submit' ) ); + } + + if ( $this->is_trash && current_user_can( get_post_type_object( $this->screen->post_type )->cap->edit_others_posts ) ) { + submit_button( __( 'Empty Trash' ), 'apply', 'delete_all', false ); + } +?> +
    +screen->post_type ) ) + $this->view_switcher( $mode ); + } + + protected function get_table_classes() { + return array( 'widefat', 'fixed', is_post_type_hierarchical( $this->screen->post_type ) ? 'pages' : 'posts' ); + } + + public function get_columns() { + $post_type = $this->screen->post_type; + + $posts_columns = array(); + + $posts_columns['cb'] = ''; + + /* translators: manage posts column name */ + $posts_columns['title'] = _x( 'Title', 'column name' ); + + if ( post_type_supports( $post_type, 'author' ) ) { + $posts_columns['author'] = __( 'Author' ); + } + + $taxonomies = get_object_taxonomies( $post_type, 'objects' ); + $taxonomies = wp_filter_object_list( $taxonomies, array( 'show_admin_column' => true ), 'and', 'name' ); + + /** + * Filter the taxonomy columns in the Posts list table. + * + * The dynamic portion of the hook name, `$post_type`, refers to the post + * type slug. + * + * @since 3.5.0 + * + * @param array $taxonomies Array of taxonomies to show columns for. + * @param string $post_type The post type. + */ + $taxonomies = apply_filters( "manage_taxonomies_for_{$post_type}_columns", $taxonomies, $post_type ); + $taxonomies = array_filter( $taxonomies, 'taxonomy_exists' ); + + foreach ( $taxonomies as $taxonomy ) { + if ( 'category' == $taxonomy ) + $column_key = 'categories'; + elseif ( 'post_tag' == $taxonomy ) + $column_key = 'tags'; + else + $column_key = 'taxonomy-' . $taxonomy; + + $posts_columns[ $column_key ] = get_taxonomy( $taxonomy )->labels->name; + } + + $post_status = !empty( $_REQUEST['post_status'] ) ? $_REQUEST['post_status'] : 'all'; + if ( post_type_supports( $post_type, 'comments' ) && !in_array( $post_status, array( 'pending', 'draft', 'future' ) ) ) + $posts_columns['comments'] = ''; + + $posts_columns['date'] = __( 'Date' ); + + if ( 'page' == $post_type ) { + + /** + * Filter the columns displayed in the Pages list table. + * + * @since 2.5.0 + * + * @param array $post_columns An array of column names. + */ + $posts_columns = apply_filters( 'manage_pages_columns', $posts_columns ); + } else { + + /** + * Filter the columns displayed in the Posts list table. + * + * @since 1.5.0 + * + * @param array $posts_columns An array of column names. + * @param string $post_type The post type slug. + */ + $posts_columns = apply_filters( 'manage_posts_columns', $posts_columns, $post_type ); + } + + /** + * Filter the columns displayed in the Posts list table for a specific post type. + * + * The dynamic portion of the hook name, `$post_type`, refers to the post type slug. + * + * @since 3.0.0 + * + * @param array $post_columns An array of column names. + */ + $posts_columns = apply_filters( "manage_{$post_type}_posts_columns", $posts_columns ); + + return $posts_columns; + } + + protected function get_sortable_columns() { + return array( + 'title' => 'title', + 'parent' => 'parent', + 'comments' => 'comment_count', + 'date' => array( 'date', true ) + ); + } + + /** + * @global WP_Query $wp_query + * @global int $per_page + * @param array $posts + * @param int $level + */ + public function display_rows( $posts = array(), $level = 0 ) { + global $wp_query, $per_page; + + if ( empty( $posts ) ) + $posts = $wp_query->posts; + + add_filter( 'the_title', 'esc_html' ); + + if ( $this->hierarchical_display ) { + $this->_display_rows_hierarchical( $posts, $this->get_pagenum(), $per_page ); + } else { + $this->_display_rows( $posts, $level ); + } + } + + /** + * @global string $mode + * @param array $posts + * @param int $level + */ + private function _display_rows( $posts, $level = 0 ) { + global $mode; + + // Create array of post IDs. + $post_ids = array(); + + foreach ( $posts as $a_post ) + $post_ids[] = $a_post->ID; + + $this->comment_pending_count = get_pending_comments_num( $post_ids ); + + foreach ( $posts as $post ) + $this->single_row( $post, $level ); + } + + /** + * @global wpdb $wpdb + * @param array $pages + * @param int $pagenum + * @param int $per_page + * @return bool|null + */ + private function _display_rows_hierarchical( $pages, $pagenum = 1, $per_page = 20 ) { + global $wpdb; + + $level = 0; + + if ( ! $pages ) { + $pages = get_pages( array( 'sort_column' => 'menu_order' ) ); + + if ( ! $pages ) + return false; + } + + /* + * Arrange pages into two parts: top level pages and children_pages + * children_pages is two dimensional array, eg. + * children_pages[10][] contains all sub-pages whose parent is 10. + * It only takes O( N ) to arrange this and it takes O( 1 ) for subsequent lookup operations + * If searching, ignore hierarchy and treat everything as top level + */ + if ( empty( $_REQUEST['s'] ) ) { + + $top_level_pages = array(); + $children_pages = array(); + + foreach ( $pages as $page ) { + + // Catch and repair bad pages. + if ( $page->post_parent == $page->ID ) { + $page->post_parent = 0; + $wpdb->update( $wpdb->posts, array( 'post_parent' => 0 ), array( 'ID' => $page->ID ) ); + clean_post_cache( $page ); + } + + if ( 0 == $page->post_parent ) + $top_level_pages[] = $page; + else + $children_pages[ $page->post_parent ][] = $page; + } + + $pages = &$top_level_pages; + } + + $count = 0; + $start = ( $pagenum - 1 ) * $per_page; + $end = $start + $per_page; + + foreach ( $pages as $page ) { + if ( $count >= $end ) + break; + + if ( $count >= $start ) { + echo "\t"; + $this->single_row( $page, $level ); + } + + $count++; + + if ( isset( $children_pages ) ) + $this->_page_rows( $children_pages, $count, $page->ID, $level + 1, $pagenum, $per_page ); + } + + // If it is the last pagenum and there are orphaned pages, display them with paging as well. + if ( isset( $children_pages ) && $count < $end ){ + foreach ( $children_pages as $orphans ){ + foreach ( $orphans as $op ) { + if ( $count >= $end ) + break; + + if ( $count >= $start ) { + echo "\t"; + $this->single_row( $op, 0 ); + } + + $count++; + } + } + } + } + + /** + * Given a top level page ID, display the nested hierarchy of sub-pages + * together with paging support + * + * @since 3.1.0 (Standalone function exists since 2.6.0) + * + * @param array $children_pages + * @param int $count + * @param int $parent + * @param int $level + * @param int $pagenum + * @param int $per_page + */ + private function _page_rows( &$children_pages, &$count, $parent, $level, $pagenum, $per_page ) { + + if ( ! isset( $children_pages[$parent] ) ) + return; + + $start = ( $pagenum - 1 ) * $per_page; + $end = $start + $per_page; + + foreach ( $children_pages[$parent] as $page ) { + + if ( $count >= $end ) + break; + + // If the page starts in a subtree, print the parents. + if ( $count == $start && $page->post_parent > 0 ) { + $my_parents = array(); + $my_parent = $page->post_parent; + while ( $my_parent ) { + $my_parent = get_post( $my_parent ); + $my_parents[] = $my_parent; + if ( !$my_parent->post_parent ) + break; + $my_parent = $my_parent->post_parent; + } + $num_parents = count( $my_parents ); + while ( $my_parent = array_pop( $my_parents ) ) { + echo "\t"; + $this->single_row( $my_parent, $level - $num_parents ); + $num_parents--; + } + } + + if ( $count >= $start ) { + echo "\t"; + $this->single_row( $page, $level ); + } + + $count++; + + $this->_page_rows( $children_pages, $count, $page->ID, $level + 1, $pagenum, $per_page ); + } + + unset( $children_pages[$parent] ); //required in order to keep track of orphans + } + + /** + * @global string $mode + * @staticvar string $alternate + * @param WP_Post $post + * @param int $level + */ + public function single_row( $post, $level = 0 ) { + global $mode; + static $alternate; + + $global_post = get_post(); + $GLOBALS['post'] = $post; + setup_postdata( $post ); + + $edit_link = get_edit_post_link( $post->ID ); + $title = _draft_or_post_title(); + $post_type_object = get_post_type_object( $post->post_type ); + $can_edit_post = current_user_can( 'edit_post', $post->ID ); + + $alternate = 'alternate' == $alternate ? '' : 'alternate'; + $classes = $alternate . ' iedit author-' . ( get_current_user_id() == $post->post_author ? 'self' : 'other' ); + + $lock_holder = wp_check_post_lock( $post->ID ); + if ( $lock_holder ) { + $classes .= ' wp-locked'; + $lock_holder = get_userdata( $lock_holder ); + } + + if ( $post->post_parent ) { + $count = count( get_post_ancestors( $post->ID ) ); + $classes .= ' level-'. $count; + } else { + $classes .= ' level-0'; + } + ?> + + get_column_info(); + + foreach ( $columns as $column_name => $column_display_name ) { + $class = "class=\"$column_name column-$column_name\""; + + $style = ''; + if ( in_array( $column_name, $hidden ) ) + $style = ' style="display:none;"'; + + $attributes = "$class$style"; + + switch ( $column_name ) { + + case 'cb': + ?> + + + + +
    + + + hierarchical_display ) { + if ( 0 == $level && (int) $post->post_parent > 0 ) { + // Sent level 0 by accident, by default, or because we don't know the actual level. + $find_main_page = (int) $post->post_parent; + while ( $find_main_page > 0 ) { + $parent = get_post( $find_main_page ); + + if ( is_null( $parent ) ) + break; + + $level++; + $find_main_page = (int) $parent->post_parent; + + if ( !isset( $parent_name ) ) { + /** This filter is documented in wp-includes/post-template.php */ + $parent_name = apply_filters( 'the_title', $parent->post_title, $parent->ID ); + } + } + } + } + + $pad = str_repeat( '— ', $level ); + echo ""; + + if ( $format = get_post_format( $post->ID ) ) { + $label = get_post_format_string( $format ); + + echo '' . $label . ": "; + } + + if ( $can_edit_post && $post->post_status != 'trash' ) { + echo '' . $pad . $title . ''; + } else { + echo $pad . $title; + } + _post_states( $post ); + + if ( isset( $parent_name ) ) + echo ' | ' . $post_type_object->labels->parent_item_colon . ' ' . esc_html( $parent_name ); + + echo "\n"; + + if ( $can_edit_post && $post->post_status != 'trash' ) { + if ( $lock_holder ) { + $locked_avatar = get_avatar( $lock_holder->ID, 18 ); + $locked_text = esc_html( sprintf( __( '%s is currently editing' ), $lock_holder->display_name ) ); + } else { + $locked_avatar = $locked_text = ''; + } + + echo '
    ' . $locked_avatar . ' ' . $locked_text . "
    \n"; + } + + if ( ! $this->hierarchical_display && 'excerpt' == $mode && current_user_can( 'read_post', $post->ID ) ) + the_excerpt(); + + $actions = array(); + if ( $can_edit_post && 'trash' != $post->post_status ) { + $actions['edit'] = '' . __( 'Edit' ) . ''; + $actions['inline hide-if-no-js'] = '' . __( 'Quick Edit' ) . ''; + } + if ( current_user_can( 'delete_post', $post->ID ) ) { + if ( 'trash' == $post->post_status ) + $actions['untrash'] = "ID ) ), 'untrash-post_' . $post->ID ) . "'>" . __( 'Restore' ) . ""; + elseif ( EMPTY_TRASH_DAYS ) + $actions['trash'] = "" . __( 'Trash' ) . ""; + if ( 'trash' == $post->post_status || !EMPTY_TRASH_DAYS ) + $actions['delete'] = "" . __( 'Delete Permanently' ) . ""; + } + if ( $post_type_object->public ) { + if ( in_array( $post->post_status, array( 'pending', 'draft', 'future' ) ) ) { + if ( $can_edit_post ) { + $preview_link = set_url_scheme( get_permalink( $post->ID ) ); + /** This filter is documented in wp-admin/includes/meta-boxes.php */ + $preview_link = apply_filters( 'preview_post_link', add_query_arg( 'preview', 'true', $preview_link ), $post ); + $actions['view'] = '' . __( 'Preview' ) . ''; + } + } elseif ( 'trash' != $post->post_status ) { + $actions['view'] = '' . __( 'View' ) . ''; + } + } + + if ( is_post_type_hierarchical( $post->post_type ) ) { + + /** + * Filter the array of row action links on the Pages list table. + * + * The filter is evaluated only for hierarchical post types. + * + * @since 2.8.0 + * + * @param array $actions An array of row action links. Defaults are + * 'Edit', 'Quick Edit', 'Restore, 'Trash', + * 'Delete Permanently', 'Preview', and 'View'. + * @param WP_Post $post The post object. + */ + $actions = apply_filters( 'page_row_actions', $actions, $post ); + } else { + + /** + * Filter the array of row action links on the Posts list table. + * + * The filter is evaluated only for non-hierarchical post types. + * + * @since 2.8.0 + * + * @param array $actions An array of row action links. Defaults are + * 'Edit', 'Quick Edit', 'Restore, 'Trash', + * 'Delete Permanently', 'Preview', and 'View'. + * @param WP_Post $post The post object. + */ + $actions = apply_filters( 'post_row_actions', $actions, $post ); + } + + echo $this->row_actions( $actions ); + + get_inline_data( $post ); + echo ''; + break; + + case 'date': + if ( '0000-00-00 00:00:00' == $post->post_date ) { + $t_time = $h_time = __( 'Unpublished' ); + $time_diff = 0; + } else { + $t_time = get_the_time( __( 'Y/m/d g:i:s A' ) ); + $m_time = $post->post_date; + $time = get_post_time( 'G', true, $post ); + + $time_diff = time() - $time; + + if ( $time_diff > 0 && $time_diff < DAY_IN_SECONDS ) + $h_time = sprintf( __( '%s ago' ), human_time_diff( $time ) ); + else + $h_time = mysql2date( __( 'Y/m/d' ), $m_time ); + } + + echo ''; + if ( 'excerpt' == $mode ) { + + /** + * Filter the published time of the post. + * + * If $mode equals 'excerpt', the published time and date are both displayed. + * If $mode equals 'list' (default), the publish date is displayed, with the + * time and date together available as an abbreviation definition. + * + * @since 2.5.1 + * + * @param array $t_time The published time. + * @param WP_Post $post Post object. + * @param string $column_name The column name. + * @param string $mode The list display mode ('excerpt' or 'list'). + */ + echo apply_filters( 'post_date_column_time', $t_time, $post, $column_name, $mode ); + } else { + + /** This filter is documented in wp-admin/includes/class-wp-posts-list-table.php */ + echo '' . apply_filters( 'post_date_column_time', $h_time, $post, $column_name, $mode ) . ''; + } + echo '
    '; + if ( 'publish' == $post->post_status ) { + _e( 'Published' ); + } elseif ( 'future' == $post->post_status ) { + if ( $time_diff > 0 ) + echo '' . __( 'Missed schedule' ) . ''; + else + _e( 'Scheduled' ); + } else { + _e( 'Last Modified' ); + } + echo ''; + break; + + case 'comments': + ?> + >
    + comment_pending_count[$post->ID] ) ? $this->comment_pending_count[$post->ID] : 0; + + $this->comments_bubble( $post->ID, $pending_comments ); + ?> +
    + + >%s', + esc_url( add_query_arg( array( 'post_type' => $post->post_type, 'author' => get_the_author_meta( 'ID' ) ), 'edit.php' )), + get_the_author() + ); + ?> + '; + if ( $terms = get_the_terms( $post->ID, $taxonomy ) ) { + $out = array(); + foreach ( $terms as $t ) { + $posts_in_term_qv = array(); + if ( 'post' != $post->post_type ) + $posts_in_term_qv['post_type'] = $post->post_type; + if ( $taxonomy_object->query_var ) { + $posts_in_term_qv[ $taxonomy_object->query_var ] = $t->slug; + } else { + $posts_in_term_qv['taxonomy'] = $taxonomy; + $posts_in_term_qv['term'] = $t->slug; + } + + $out[] = sprintf( '%s', + esc_url( add_query_arg( $posts_in_term_qv, 'edit.php' ) ), + esc_html( sanitize_term_field( 'name', $t->name, $t->term_id, $taxonomy, 'display' ) ) + ); + } + /* translators: used between list items, there is a space after the comma */ + echo join( __( ', ' ), $out ); + } else { + echo '—'; + } + echo ''; + break; + } + ?> + >post_type ) ) { + + /** + * Fires in each custom column on the Posts list table. + * + * This hook only fires if the current post type is hierarchical, + * such as pages. + * + * @since 2.5.0 + * + * @param string $column_name The name of the column to display. + * @param int $post_id The current post ID. + */ + do_action( 'manage_pages_custom_column', $column_name, $post->ID ); + } else { + + /** + * Fires in each custom column in the Posts list table. + * + * This hook only fires if the current post type is non-hierarchical, + * such as posts. + * + * @since 1.5.0 + * + * @param string $column_name The name of the column to display. + * @param int $post_id The current post ID. + */ + do_action( 'manage_posts_custom_column', $column_name, $post->ID ); + } + + /** + * Fires for each custom column of a specific post type in the Posts list table. + * + * The dynamic portion of the hook name, `$post->post_type`, refers to the post type. + * + * @since 3.1.0 + * + * @param string $column_name The name of the column to display. + * @param int $post_id The current post ID. + */ + do_action( "manage_{$post->post_type}_posts_custom_column", $column_name, $post->ID ); + ?> + + + screen; + + $post = get_default_post_to_edit( $screen->post_type ); + $post_type_object = get_post_type_object( $screen->post_type ); + + $taxonomy_names = get_object_taxonomies( $screen->post_type ); + $hierarchical_taxonomies = array(); + $flat_taxonomies = array(); + foreach ( $taxonomy_names as $taxonomy_name ) { + $taxonomy = get_taxonomy( $taxonomy_name ); + + if ( !$taxonomy->show_ui ) + continue; + + if ( $taxonomy->hierarchical ) + $hierarchical_taxonomies[] = $taxonomy; + else + $flat_taxonomies[] = $taxonomy; + } + + $m = ( isset( $mode ) && 'excerpt' == $mode ) ? 'excerpt' : 'list'; + $can_publish = current_user_can( $post_type_object->cap->publish_posts ); + $core_columns = array( 'cb' => true, 'date' => true, 'title' => true, 'categories' => true, 'tags' => true, 'comments' => true, 'author' => true ); + + ?> + +
    + + + post_type; + echo $bulk ? " bulk-edit-row bulk-edit-row-$hclass bulk-edit-{$screen->post_type}" : " quick-edit-row quick-edit-row-$hclass inline-edit-{$screen->post_type}"; + ?>" style="display: none"> + +
    + +
    +

    + post_type, 'title' ) ) : + if ( $bulk ) : ?> +
    +
    +
    + + + + + + + + + + + +
    + +
    +
    + post_type, 'author' ) ) : + $authors_dropdown = ''; + + if ( is_super_admin() || current_user_can( $post_type_object->cap->edit_others_posts ) ) : + $users_opt = array( + 'hide_if_only_one_author' => false, + 'who' => 'authors', + 'name' => 'post_author', + 'class'=> 'authors', + 'multi' => 1, + 'echo' => 0 + ); + if ( $bulk ) + $users_opt['show_option_none'] = __( '— No Change —' ); + + if ( $authors = wp_dropdown_users( $users_opt ) ) : + $authors_dropdown = ''; + endif; + endif; // authors + ?> + + + +
    + + + + + + +
    + + + +
    + + + +
    + + + + labels->name ) ?> + +
      + $taxonomy->name ) ) ?> +
    + + + +
    + + + +
    + + post_type, 'author' ) && $bulk ) + echo $authors_dropdown; + + if ( post_type_supports( $screen->post_type, 'page-attributes' ) ) : + + if ( $post_type_object->hierarchical ) : + ?> + + + + + + + post_type ) : + ?> + + + + + + + + + cap->assign_terms ) ) : ?> + + + + + + + + post_type, 'comments' ) || post_type_supports( $screen->post_type, 'trackbacks' ) ) : + if ( $bulk ) : ?> + +
    + post_type, 'comments' ) ) : ?> + + post_type, 'trackbacks' ) ) : ?> + + +
    + + + +
    + post_type, 'comments' ) ) : ?> + + post_type, 'trackbacks' ) ) : ?> + + +
    + + + +
    + + + post_type && $can_publish && current_user_can( $post_type_object->cap->edit_others_posts ) ) : ?> + + + + + + + + + + + + + +
    + + post_type, 'post-formats' ) ) { + $post_formats = get_theme_support( 'post-formats' ); + + ?> + + + +
    + + get_column_info(); + + foreach ( $columns as $column_name => $column_display_name ) { + if ( isset( $core_columns[$column_name] ) ) + continue; + + if ( $bulk ) { + + /** + * Fires once for each column in Bulk Edit mode. + * + * @since 2.7.0 + * + * @param string $column_name Name of the column to edit. + * @param WP_Post $post_type The post type slug. + */ + do_action( 'bulk_edit_custom_box', $column_name, $screen->post_type ); + } else { + + /** + * Fires once for each column in Quick Edit mode. + * + * @since 2.7.0 + * + * @param string $column_name Name of the column to edit. + * @param WP_Post $post_type The post type slug. + */ + do_action( 'quick_edit_custom_box', $column_name, $screen->post_type ); + } + + } + ?> +

    + + + + + 's' ) ); + } ?> + + + post_type, 'author' ) ) { ?> + + + +
    +

    +
    + 'tags', + 'singular' => 'tag', + 'screen' => isset( $args['screen'] ) ? $args['screen'] : null, + ) ); + + $action = $this->screen->action; + $post_type = $this->screen->post_type; + $taxonomy = $this->screen->taxonomy; + + if ( empty( $taxonomy ) ) + $taxonomy = 'post_tag'; + + if ( ! taxonomy_exists( $taxonomy ) ) + wp_die( __( 'Invalid taxonomy' ) ); + + $tax = get_taxonomy( $taxonomy ); + + // @todo Still needed? Maybe just the show_ui part. + if ( empty( $post_type ) || !in_array( $post_type, get_post_types( array( 'show_ui' => true ) ) ) ) + $post_type = 'post'; + + } + + public function ajax_user_can() { + return current_user_can( get_taxonomy( $this->screen->taxonomy )->cap->manage_terms ); + } + + public function prepare_items() { + $tags_per_page = $this->get_items_per_page( 'edit_' . $this->screen->taxonomy . '_per_page' ); + + if ( 'post_tag' == $this->screen->taxonomy ) { + /** + * Filter the number of terms displayed per page for the Tags list table. + * + * @since 2.8.0 + * + * @param int $tags_per_page Number of tags to be displayed. Default 20. + */ + $tags_per_page = apply_filters( 'edit_tags_per_page', $tags_per_page ); + + /** + * Filter the number of terms displayed per page for the Tags list table. + * + * @since 2.7.0 + * @deprecated 2.8.0 Use edit_tags_per_page instead. + * + * @param int $tags_per_page Number of tags to be displayed. Default 20. + */ + $tags_per_page = apply_filters( 'tagsperpage', $tags_per_page ); + } elseif ( 'category' == $this->screen->taxonomy ) { + /** + * Filter the number of terms displayed per page for the Categories list table. + * + * @since 2.8.0 + * + * @param int $tags_per_page Number of categories to be displayed. Default 20. + */ + $tags_per_page = apply_filters( 'edit_categories_per_page', $tags_per_page ); + } + + $search = !empty( $_REQUEST['s'] ) ? trim( wp_unslash( $_REQUEST['s'] ) ) : ''; + + $args = array( + 'search' => $search, + 'page' => $this->get_pagenum(), + 'number' => $tags_per_page, + ); + + if ( !empty( $_REQUEST['orderby'] ) ) + $args['orderby'] = trim( wp_unslash( $_REQUEST['orderby'] ) ); + + if ( !empty( $_REQUEST['order'] ) ) + $args['order'] = trim( wp_unslash( $_REQUEST['order'] ) ); + + $this->callback_args = $args; + + $this->set_pagination_args( array( + 'total_items' => wp_count_terms( $this->screen->taxonomy, compact( 'search' ) ), + 'per_page' => $tags_per_page, + ) ); + } + + public function has_items() { + // todo: populate $this->items in prepare_items() + return true; + } + + protected function get_bulk_actions() { + $actions = array(); + $actions['delete'] = __( 'Delete' ); + + return $actions; + } + + public function current_action() { + if ( isset( $_REQUEST['action'] ) && isset( $_REQUEST['delete_tags'] ) && ( 'delete' == $_REQUEST['action'] || 'delete' == $_REQUEST['action2'] ) ) + return 'bulk-delete'; + + return parent::current_action(); + } + + public function get_columns() { + $columns = array( + 'cb' => '', + 'name' => _x( 'Name', 'term name' ), + 'description' => __( 'Description' ), + 'slug' => __( 'Slug' ), + ); + + if ( 'link_category' == $this->screen->taxonomy ) { + $columns['links'] = __( 'Links' ); + } else { + $columns['posts'] = _x( 'Count', 'Number/count of items' ); + } + + return $columns; + } + + protected function get_sortable_columns() { + return array( + 'name' => 'name', + 'description' => 'description', + 'slug' => 'slug', + 'posts' => 'count', + 'links' => 'count' + ); + } + + public function display_rows_or_placeholder() { + $taxonomy = $this->screen->taxonomy; + + $args = wp_parse_args( $this->callback_args, array( + 'page' => 1, + 'number' => 20, + 'search' => '', + 'hide_empty' => 0 + ) ); + + $page = $args['page']; + + // Set variable because $args['number'] can be subsequently overridden. + $number = $args['number']; + + $args['offset'] = $offset = ( $page - 1 ) * $number; + + // Convert it to table rows. + $count = 0; + + if ( is_taxonomy_hierarchical( $taxonomy ) && ! isset( $args['orderby'] ) ) { + // We'll need the full set of terms then. + $args['number'] = $args['offset'] = 0; + } + $terms = get_terms( $taxonomy, $args ); + + if ( empty( $terms ) ) { + echo ''; + $this->no_items(); + echo ''; + return; + } + + if ( is_taxonomy_hierarchical( $taxonomy ) && ! isset( $args['orderby'] ) ) { + if ( ! empty( $args['search'] ) ) {// Ignore children on searches. + $children = array(); + } else { + $children = _get_term_hierarchy( $taxonomy ); + } + // Some funky recursion to get the job done( Paging & parents mainly ) is contained within, Skip it for non-hierarchical taxonomies for performance sake + $this->_rows( $taxonomy, $terms, $children, $offset, $number, $count ); + } else { + $terms = get_terms( $taxonomy, $args ); + foreach ( $terms as $term ) { + $this->single_row( $term ); + } + } + } + + /** + * @param string $taxonomy + * @param array $terms + * @param array $children + * @param int $start + * @param int $per_page + * @param int $count + * @param int $parent + * @param int $level + */ + private function _rows( $taxonomy, $terms, &$children, $start, $per_page, &$count, $parent = 0, $level = 0 ) { + + $end = $start + $per_page; + + foreach ( $terms as $key => $term ) { + + if ( $count >= $end ) + break; + + if ( $term->parent != $parent && empty( $_REQUEST['s'] ) ) + continue; + + // If the page starts in a subtree, print the parents. + if ( $count == $start && $term->parent > 0 && empty( $_REQUEST['s'] ) ) { + $my_parents = $parent_ids = array(); + $p = $term->parent; + while ( $p ) { + $my_parent = get_term( $p, $taxonomy ); + $my_parents[] = $my_parent; + $p = $my_parent->parent; + if ( in_array( $p, $parent_ids ) ) // Prevent parent loops. + break; + $parent_ids[] = $p; + } + unset( $parent_ids ); + + $num_parents = count( $my_parents ); + while ( $my_parent = array_pop( $my_parents ) ) { + echo "\t"; + $this->single_row( $my_parent, $level - $num_parents ); + $num_parents--; + } + } + + if ( $count >= $start ) { + echo "\t"; + $this->single_row( $term, $level ); + } + + ++$count; + + unset( $terms[$key] ); + + if ( isset( $children[$term->term_id] ) && empty( $_REQUEST['s'] ) ) + $this->_rows( $taxonomy, $terms, $children, $start, $per_page, $count, $term->term_id, $level + 1 ); + } + } + + /** + * @global string $taxonomy + * @staticvar string $row_class + * @param object $tag + * @param int $level + */ + public function single_row( $tag, $level = 0 ) { + global $taxonomy; + $tag = sanitize_term( $tag, $taxonomy ); + + static $row_class = ''; + $row_class = ( $row_class == '' ? ' class="alternate"' : '' ); + + $this->level = $level; + + echo ''; + $this->single_row_columns( $tag ); + echo ''; + } + + /** + * @param object $tag + * @return string + */ + public function column_cb( $tag ) { + $default_term = get_option( 'default_' . $this->screen->taxonomy ); + + if ( current_user_can( get_taxonomy( $this->screen->taxonomy )->cap->delete_terms ) && $tag->term_id != $default_term ) + return '' + . ''; + + return ' '; + } + + /** + * @param object $tag + * @return string + */ + public function column_name( $tag ) { + $taxonomy = $this->screen->taxonomy; + $tax = get_taxonomy( $taxonomy ); + + $default_term = get_option( 'default_' . $taxonomy ); + + $pad = str_repeat( '— ', max( 0, $this->level ) ); + + /** + * Filter display of the term name in the terms list table. + * + * The default output may include padding due to the term's + * current level in the term hierarchy. + * + * @since 2.5.0 + * + * @see WP_Terms_List_Table::column_name() + * + * @param string $pad_tag_name The term name, padded if not top-level. + * @param object $tag Term object. + */ + $name = apply_filters( 'term_name', $pad . ' ' . $tag->name, $tag ); + + $qe_data = get_term( $tag->term_id, $taxonomy, OBJECT, 'edit' ); + $edit_link = esc_url( get_edit_term_link( $tag->term_id, $taxonomy, $this->screen->post_type ) ); + + $out = '' . $name . '
    '; + + $actions = array(); + if ( current_user_can( $tax->cap->edit_terms ) ) { + $actions['edit'] = '' . __( 'Edit' ) . ''; + $actions['inline hide-if-no-js'] = '' . __( 'Quick Edit' ) . ''; + } + if ( current_user_can( $tax->cap->delete_terms ) && $tag->term_id != $default_term ) + $actions['delete'] = "term_id ) . "'>" . __( 'Delete' ) . ""; + if ( $tax->public ) + $actions['view'] = '' . __( 'View' ) . ''; + + /** + * Filter the action links displayed for each term in the Tags list table. + * + * @since 2.8.0 + * @deprecated 3.0.0 Use {$taxonomy}_row_actions instead. + * + * @param array $actions An array of action links to be displayed. Default + * 'Edit', 'Quick Edit', 'Delete', and 'View'. + * @param object $tag Term object. + */ + $actions = apply_filters( 'tag_row_actions', $actions, $tag ); + + /** + * Filter the action links displayed for each term in the terms list table. + * + * The dynamic portion of the hook name, `$taxonomy`, refers to the taxonomy slug. + * + * @since 3.0.0 + * + * @param array $actions An array of action links to be displayed. Default + * 'Edit', 'Quick Edit', 'Delete', and 'View'. + * @param object $tag Term object. + */ + $actions = apply_filters( "{$taxonomy}_row_actions", $actions, $tag ); + + $out .= $this->row_actions( $actions ); + $out .= ''; + + return $out; + } + + /** + * @param object $tag + * @return string + */ + public function column_description( $tag ) { + return $tag->description; + } + + /** + * @param object $tag + * @return string + */ + public function column_slug( $tag ) { + /** This filter is documented in wp-admin/edit-tag-form.php */ + return apply_filters( 'editable_slug', $tag->slug ); + } + + /** + * @param object $tag + * @return string + */ + public function column_posts( $tag ) { + $count = number_format_i18n( $tag->count ); + + $tax = get_taxonomy( $this->screen->taxonomy ); + + $ptype_object = get_post_type_object( $this->screen->post_type ); + if ( ! $ptype_object->show_ui ) + return $count; + + if ( $tax->query_var ) { + $args = array( $tax->query_var => $tag->slug ); + } else { + $args = array( 'taxonomy' => $tax->name, 'term' => $tag->slug ); + } + + if ( 'post' != $this->screen->post_type ) + $args['post_type'] = $this->screen->post_type; + + if ( 'attachment' == $this->screen->post_type ) + return "$count"; + + return "$count"; + } + + /** + * @param object $tag + * @return string + */ + public function column_links( $tag ) { + $count = number_format_i18n( $tag->count ); + if ( $count ) + $count = "$count"; + return $count; + } + + /** + * @param object $tag + * @param string $column_name + * @return string + */ + public function column_default( $tag, $column_name ) { + /** + * Filter the displayed columns in the terms list table. + * + * The dynamic portion of the hook name, `$this->screen->taxonomy`, + * refers to the slug of the current taxonomy. + * + * @since 2.8.0 + * + * @param string $string Blank string. + * @param string $column_name Name of the column. + * @param int $term_id Term ID. + */ + return apply_filters( "manage_{$this->screen->taxonomy}_custom_column", '', $column_name, $tag->term_id ); + } + + /** + * Outputs the hidden row displayed when inline editing + * + * @since 3.1.0 + */ + public function inline_edit() { + $tax = get_taxonomy( $this->screen->taxonomy ); + + if ( ! current_user_can( $tax->cap->edit_terms ) ) + return; +?> + +
    + +
    + features = $_REQUEST['features']; + + $paged = $this->get_pagenum(); + + $per_page = 36; + + // These are the tabs which are shown on the page, + $tabs = array(); + $tabs['dashboard'] = __( 'Search' ); + if ( 'search' == $tab ) + $tabs['search'] = __( 'Search Results' ); + $tabs['upload'] = __( 'Upload' ); + $tabs['featured'] = _x( 'Featured', 'themes' ); + //$tabs['popular'] = _x( 'Popular', 'themes' ); + $tabs['new'] = _x( 'Latest', 'themes' ); + $tabs['updated'] = _x( 'Recently Updated', 'themes' ); + + $nonmenu_tabs = array( 'theme-information' ); // Valid actions to perform which do not have a Menu item. + + /** This filter is documented in wp-admin/theme-install.php */ + $tabs = apply_filters( 'install_themes_tabs', $tabs ); + + /** + * Filter tabs not associated with a menu item on the Install Themes screen. + * + * @since 2.8.0 + * + * @param array $nonmenu_tabs The tabs that don't have a menu item on + * the Install Themes screen. + */ + $nonmenu_tabs = apply_filters( 'install_themes_nonmenu_tabs', $nonmenu_tabs ); + + // If a non-valid menu tab has been selected, And it's not a non-menu action. + if ( empty( $tab ) || ( ! isset( $tabs[ $tab ] ) && ! in_array( $tab, (array) $nonmenu_tabs ) ) ) + $tab = key( $tabs ); + + $args = array( 'page' => $paged, 'per_page' => $per_page, 'fields' => $theme_field_defaults ); + + switch ( $tab ) { + case 'search': + $type = isset( $_REQUEST['type'] ) ? wp_unslash( $_REQUEST['type'] ) : 'term'; + switch ( $type ) { + case 'tag': + $args['tag'] = array_map( 'sanitize_key', $search_terms ); + break; + case 'term': + $args['search'] = $search_string; + break; + case 'author': + $args['author'] = $search_string; + break; + } + + if ( ! empty( $this->features ) ) { + $args['tag'] = $this->features; + $_REQUEST['s'] = implode( ',', $this->features ); + $_REQUEST['type'] = 'tag'; + } + + add_action( 'install_themes_table_header', 'install_theme_search_form', 10, 0 ); + break; + + case 'featured': + // case 'popular': + case 'new': + case 'updated': + $args['browse'] = $tab; + break; + + default: + $args = false; + break; + } + + /** + * Filter API request arguments for each Install Themes screen tab. + * + * The dynamic portion of the hook name, `$tab`, refers to the theme install + * tabs. Default tabs are 'dashboard', 'search', 'upload', 'featured', + * 'new', and 'updated'. + * + * @since 3.7.0 + * + * @param array $args An array of themes API arguments. + */ + $args = apply_filters( 'install_themes_table_api_args_' . $tab, $args ); + + if ( ! $args ) + return; + + $api = themes_api( 'query_themes', $args ); + + if ( is_wp_error( $api ) ) + wp_die( $api->get_error_message() . '

    ' . __( 'Try again' ) . '' ); + + $this->items = $api->themes; + + $this->set_pagination_args( array( + 'total_items' => $api->info['results'], + 'per_page' => $args['per_page'], + 'infinite_scroll' => true, + ) ); + } + + public function no_items() { + _e( 'No themes match your request.' ); + } + + protected function get_views() { + global $tabs, $tab; + + $display_tabs = array(); + foreach ( (array) $tabs as $action => $text ) { + $class = ( $action == $tab ) ? ' class="current"' : ''; + $href = self_admin_url('theme-install.php?tab=' . $action); + $display_tabs['theme-install-'.$action] = "$text"; + } + + return $display_tabs; + } + + public function display() { + wp_nonce_field( "fetch-list-" . get_class( $this ), '_ajax_fetch_list_nonce' ); +?> +

    +
    + +
    + pagination( 'top' ); ?> +
    +
    + +
    + display_rows_or_placeholder(); ?> +
    + + items; + foreach ( $themes as $theme ) { + ?> +
    single_row( $theme ); + ?>
    + theme_installer(); + } + + /** + * Prints a theme from the WordPress.org API. + * + * @param object $theme An object that contains theme data returned by the WordPress.org API. + * + * Example theme data: + * object(stdClass)[59] + * public 'name' => string 'Magazine Basic' + * public 'slug' => string 'magazine-basic' + * public 'version' => string '1.1' + * public 'author' => string 'tinkerpriest' + * public 'preview_url' => string 'http://wp-themes.com/?magazine-basic' + * public 'screenshot_url' => string 'http://wp-themes.com/wp-content/themes/magazine-basic/screenshot.png' + * public 'rating' => float 80 + * public 'num_ratings' => int 1 + * public 'homepage' => string 'http://wordpress.org/themes/magazine-basic' + * public 'description' => string 'A basic magazine style layout with a fully customizable layout through a backend interface. Designed by c.bavota of Tinker Priest Media.' + * public 'download_link' => string 'http://wordpress.org/themes/download/magazine-basic.1.1.zip' + */ + public function single_row( $theme ) { + global $themes_allowedtags; + + if ( empty( $theme ) ) + return; + + $name = wp_kses( $theme->name, $themes_allowedtags ); + $author = wp_kses( $theme->author, $themes_allowedtags ); + + $preview_title = sprintf( __('Preview “%s”'), $name ); + $preview_url = add_query_arg( array( + 'tab' => 'theme-information', + 'theme' => $theme->slug, + ), self_admin_url( 'theme-install.php' ) ); + + $actions = array(); + + $install_url = add_query_arg( array( + 'action' => 'install-theme', + 'theme' => $theme->slug, + ), self_admin_url( 'update.php' ) ); + + $update_url = add_query_arg( array( + 'action' => 'upgrade-theme', + 'theme' => $theme->slug, + ), self_admin_url( 'update.php' ) ); + + $status = $this->_get_theme_status( $theme ); + + switch ( $status ) { + case 'update_available': + $actions[] = '' . __( 'Update' ) . ''; + break; + case 'newer_installed': + case 'latest_installed': + $actions[] = '' . _x( 'Installed', 'theme' ) . ''; + break; + case 'install': + default: + $actions[] = '' . __( 'Install Now' ) . ''; + break; + } + + $actions[] = '' . __( 'Preview' ) . ''; + + /** + * Filter the install action links for a theme in the Install Themes list table. + * + * @since 3.4.0 + * + * @param array $actions An array of theme action hyperlinks. Defaults are + * links to Install Now, Preview, and Details. + * @param WP_Theme $theme Theme object. + */ + $actions = apply_filters( 'theme_install_actions', $actions, $theme ); + + ?> + + + + +

    +
    + + + + install_theme_info( $theme ); + } + + /** + * Prints the wrapper for the theme installer. + */ + public function theme_installer() { + ?> +
    +
    +
    + + +
    +
    +
    +
    + +
    +
    +
    + +
    +
    + install_theme_info( $theme ); ?> +
    +
    + +
    +
    + name, $themes_allowedtags ); + $author = wp_kses( $theme->author, $themes_allowedtags ); + + $install_url = add_query_arg( array( + 'action' => 'install-theme', + 'theme' => $theme->slug, + ), self_admin_url( 'update.php' ) ); + + $update_url = add_query_arg( array( + 'action' => 'upgrade-theme', + 'theme' => $theme->slug, + ), self_admin_url( 'update.php' ) ); + + $status = $this->_get_theme_status( $theme ); + + ?> +
    slug ) ) . '" title="' . esc_attr( sprintf( __( 'Update to version %s' ), $theme->version ) ) . '">' . __( 'Update' ) . ''; + break; + case 'newer_installed': + case 'latest_installed': + echo '' . _x( 'Installed', 'theme' ) . ''; + break; + case 'install': + default: + echo '' . __( 'Install' ) . ''; + break; + } ?> +

    + + screenshot_url ) ): ?> + + +
    + $theme->rating, 'type' => 'percent', 'number' => $theme->num_ratings ) ); ?> +
    + + version, $themes_allowedtags ); ?> +
    +
    + description, $themes_allowedtags ); ?> +
    +
    + +
    + Install screen + * @uses $type Global; type of search. + */ + public function _js_vars( $extra_args = array() ) { + global $tab, $type; + parent::_js_vars( compact( 'tab', 'type' ) ); + } + + /** + * Check to see if the theme is already installed. + * + * @since 3.4.0 + * @access private + * + * @param object $theme - A WordPress.org Theme API object. + * @return string Theme status. + */ + private function _get_theme_status( $theme ) { + $status = 'install'; + + $installed_theme = wp_get_theme( $theme->slug ); + if ( $installed_theme->exists() ) { + if ( version_compare( $installed_theme->get('Version'), $theme->version, '=' ) ) + $status = 'latest_installed'; + elseif ( version_compare( $installed_theme->get('Version'), $theme->version, '>' ) ) + $status = 'newer_installed'; + else + $status = 'update_available'; + } + + return $status; + } +} diff --git a/wp-admin/includes/class-wp-themes-list-table.php b/wp-admin/includes/class-wp-themes-list-table.php new file mode 100644 index 0000000..6311e75 --- /dev/null +++ b/wp-admin/includes/class-wp-themes-list-table.php @@ -0,0 +1,285 @@ + true, + 'screen' => isset( $args['screen'] ) ? $args['screen'] : null, + ) ); + } + + public function ajax_user_can() { + // Do not check edit_theme_options here. AJAX calls for available themes require switch_themes. + return current_user_can( 'switch_themes' ); + } + + public function prepare_items() { + $themes = wp_get_themes( array( 'allowed' => true ) ); + + if ( ! empty( $_REQUEST['s'] ) ) + $this->search_terms = array_unique( array_filter( array_map( 'trim', explode( ',', strtolower( wp_unslash( $_REQUEST['s'] ) ) ) ) ) ); + + if ( ! empty( $_REQUEST['features'] ) ) + $this->features = $_REQUEST['features']; + + if ( $this->search_terms || $this->features ) { + foreach ( $themes as $key => $theme ) { + if ( ! $this->search_theme( $theme ) ) + unset( $themes[ $key ] ); + } + } + + unset( $themes[ get_option( 'stylesheet' ) ] ); + WP_Theme::sort_by_name( $themes ); + + $per_page = 36; + $page = $this->get_pagenum(); + + $start = ( $page - 1 ) * $per_page; + + $this->items = array_slice( $themes, $start, $per_page, true ); + + $this->set_pagination_args( array( + 'total_items' => count( $themes ), + 'per_page' => $per_page, + 'infinite_scroll' => true, + ) ); + } + + public function no_items() { + if ( $this->search_terms || $this->features ) { + _e( 'No items found.' ); + return; + } + + if ( is_multisite() ) { + if ( current_user_can( 'install_themes' ) && current_user_can( 'manage_network_themes' ) ) { + printf( __( 'You only have one theme enabled for this site right now. Visit the Network Admin to enable or install more themes.' ), network_admin_url( 'site-themes.php?id=' . $GLOBALS['blog_id'] ), network_admin_url( 'theme-install.php' ) ); + + return; + } elseif ( current_user_can( 'manage_network_themes' ) ) { + printf( __( 'You only have one theme enabled for this site right now. Visit the Network Admin to enable more themes.' ), network_admin_url( 'site-themes.php?id=' . $GLOBALS['blog_id'] ) ); + + return; + } + // Else, fallthrough. install_themes doesn't help if you can't enable it. + } else { + if ( current_user_can( 'install_themes' ) ) { + printf( __( 'You only have one theme installed right now. Live a little! You can choose from over 1,000 free themes in the WordPress.org Theme Directory at any time: just click on the Install Themes tab above.' ), admin_url( 'theme-install.php' ) ); + + return; + } + } + // Fallthrough. + printf( __( 'Only the current theme is available to you. Contact the %s administrator for information about accessing additional themes.' ), get_site_option( 'site_name' ) ); + } + + /** + * @param string $which + * @return null + */ + public function tablenav( $which = 'top' ) { + if ( $this->get_pagination_arg( 'total_pages' ) <= 1 ) + return; + ?> +
    + pagination( $which ); ?> + +
    +
    + + tablenav( 'top' ); ?> + +
    + display_rows_or_placeholder(); ?> +
    + + tablenav( 'bottom' ); ?> +has_items() ) { + $this->display_rows(); + } else { + echo '
    '; + $this->no_items(); + echo '
    '; + } + } + + public function display_rows() { + $themes = $this->items; + + foreach ( $themes as $theme ): + ?>
    get_template(); + $stylesheet = $theme->get_stylesheet(); + $title = $theme->display('Name'); + $version = $theme->display('Version'); + $author = $theme->display('Author'); + + $activate_link = wp_nonce_url( "themes.php?action=activate&template=" . urlencode( $template ) . "&stylesheet=" . urlencode( $stylesheet ), 'switch-theme_' . $stylesheet ); + + $preview_link = esc_url( add_query_arg( + array( 'preview' => 1, 'template' => urlencode( $template ), 'stylesheet' => urlencode( $stylesheet ), 'preview_iframe' => true, 'TB_iframe' => 'true' ), + home_url( '/' ) ) ); + + $actions = array(); + $actions['activate'] = '' . __( 'Activate' ) . ''; + + $actions['preview'] = '' . __( 'Preview' ) . ''; + + if ( current_user_can( 'edit_theme_options' ) && current_user_can( 'customize' ) ) { + $actions['preview'] .= '' + . __( 'Live Preview' ) . ''; + } + + if ( ! is_multisite() && current_user_can( 'delete_themes' ) ) + $actions['delete'] = '' . __( 'Delete' ) . ''; + + /** This filter is documented in wp-admin/includes/class-wp-ms-themes-list-table.php */ + $actions = apply_filters( 'theme_action_links', $actions, $theme ); + + /** This filter is documented in wp-admin/includes/class-wp-ms-themes-list-table.php */ + $actions = apply_filters( "theme_action_links_$stylesheet", $actions, $theme ); + $delete_action = isset( $actions['delete'] ) ? '
    ' . $actions['delete'] . '
    ' : ''; + unset( $actions['delete'] ); + + ?> + + + get_screenshot() ) : ?> + + + + + get_screenshot() ) : ?> + + + + +

    +
    + + +
    +

    +

    display('Description'); ?>

    + parent() ) { + printf( '

    ' . __( 'This child theme requires its parent theme, %2$s.' ) . '

    ', + __( 'http://codex.wordpress.org/Child_Themes' ), + $theme->parent()->display( 'Name' ) ); + } ?> +
    + +
    + features as $word ) { + if ( ! in_array( $word, $theme->get('Tags') ) ) + return false; + } + + // Match all phrases + foreach ( $this->search_terms as $word ) { + if ( in_array( $word, $theme->get('Tags') ) ) + continue; + + foreach ( array( 'Name', 'Description', 'Author', 'AuthorURI' ) as $header ) { + // Don't mark up; Do translate. + if ( false !== stripos( strip_tags( $theme->display( $header, false, true ) ), $word ) ) { + continue 2; + } + } + + if ( false !== stripos( $theme->get_stylesheet(), $word ) ) + continue; + + if ( false !== stripos( $theme->get_template(), $word ) ) + continue; + + return false; + } + + return true; + } + + /** + * Send required variables to JavaScript land + * + * @since 3.4.0 + * @access public + * + * @param array $extra_args + */ + public function _js_vars( $extra_args = array() ) { + $search_string = isset( $_REQUEST['s'] ) ? esc_attr( wp_unslash( $_REQUEST['s'] ) ) : ''; + + $args = array( + 'search' => $search_string, + 'features' => $this->features, + 'paged' => $this->get_pagenum(), + 'total_pages' => ! empty( $this->_pagination_args['total_pages'] ) ? $this->_pagination_args['total_pages'] : 1, + ); + + if ( is_array( $extra_args ) ) + $args = array_merge( $args, $extra_args ); + + printf( "\n", wp_json_encode( $args ) ); + parent::_js_vars(); + } +} diff --git a/wp-admin/includes/class-wp-upgrader-skins.php b/wp-admin/includes/class-wp-upgrader-skins.php new file mode 100644 index 0000000..7d40373 --- /dev/null +++ b/wp-admin/includes/class-wp-upgrader-skins.php @@ -0,0 +1,784 @@ + '', 'nonce' => '', 'title' => '', 'context' => false ); + $this->options = wp_parse_args($args, $defaults); + } + + /** + * @param WP_Upgrader $upgrader + */ + public function set_upgrader(&$upgrader) { + if ( is_object($upgrader) ) + $this->upgrader =& $upgrader; + $this->add_strings(); + } + + public function add_strings() { + } + + public function set_result($result) { + $this->result = $result; + } + + public function request_filesystem_credentials( $error = false, $context = false, $allow_relaxed_file_ownership = false ) { + $url = $this->options['url']; + if ( ! $context ) { + $context = $this->options['context']; + } + if ( !empty($this->options['nonce']) ) { + $url = wp_nonce_url($url, $this->options['nonce']); + } + + $extra_fields = array(); + + return request_filesystem_credentials( $url, '', $error, $context, $extra_fields, $allow_relaxed_file_ownership ); + } + + public function header() { + if ( $this->done_header ) { + return; + } + $this->done_header = true; + echo '
    '; + echo '

    ' . $this->options['title'] . '

    '; + } + public function footer() { + if ( $this->done_footer ) { + return; + } + $this->done_footer = true; + echo '
    '; + } + + public function error($errors) { + if ( ! $this->done_header ) + $this->header(); + if ( is_string($errors) ) { + $this->feedback($errors); + } elseif ( is_wp_error($errors) && $errors->get_error_code() ) { + foreach ( $errors->get_error_messages() as $message ) { + if ( $errors->get_error_data() && is_string( $errors->get_error_data() ) ) + $this->feedback($message . ' ' . esc_html( strip_tags( $errors->get_error_data() ) ) ); + else + $this->feedback($message); + } + } + } + + public function feedback($string) { + if ( isset( $this->upgrader->strings[$string] ) ) + $string = $this->upgrader->strings[$string]; + + if ( strpos($string, '%') !== false ) { + $args = func_get_args(); + $args = array_splice($args, 1); + if ( $args ) { + $args = array_map( 'strip_tags', $args ); + $args = array_map( 'esc_html', $args ); + $string = vsprintf($string, $args); + } + } + if ( empty($string) ) + return; + show_message($string); + } + public function before() {} + public function after() {} + + /** + * Output JavaScript that calls function to decrement the update counts. + * + * @since 3.9.0 + * + * @param string $type Type of update count to decrement. Likely values include 'plugin', + * 'theme', 'translation', etc. + */ + protected function decrement_update_count( $type ) { + if ( ! $this->result || is_wp_error( $this->result ) || 'up_to_date' === $this->result ) { + return; + } + + if ( defined( 'IFRAME_REQUEST' ) ) { + echo ''; + } else { + echo ''; + } + } +} + +/** + * Plugin Upgrader Skin for WordPress Plugin Upgrades. + * + * @package WordPress + * @subpackage Upgrader + * @since 2.8.0 + */ +class Plugin_Upgrader_Skin extends WP_Upgrader_Skin { + public $plugin = ''; + public $plugin_active = false; + public $plugin_network_active = false; + + public function __construct($args = array()) { + $defaults = array( 'url' => '', 'plugin' => '', 'nonce' => '', 'title' => __('Update Plugin') ); + $args = wp_parse_args($args, $defaults); + + $this->plugin = $args['plugin']; + + $this->plugin_active = is_plugin_active( $this->plugin ); + $this->plugin_network_active = is_plugin_active_for_network( $this->plugin ); + + parent::__construct($args); + } + + public function after() { + $this->plugin = $this->upgrader->plugin_info(); + if ( !empty($this->plugin) && !is_wp_error($this->result) && $this->plugin_active ){ + echo ''; + } + + $this->decrement_update_count( 'plugin' ); + + $update_actions = array( + 'activate_plugin' => '' . __('Activate Plugin') . '', + 'plugins_page' => '' . __('Return to Plugins page') . '' + ); + if ( $this->plugin_active || ! $this->result || is_wp_error( $this->result ) || ! current_user_can( 'activate_plugins' ) ) + unset( $update_actions['activate_plugin'] ); + + /** + * Filter the list of action links available following a single plugin update. + * + * @since 2.7.0 + * + * @param array $update_actions Array of plugin action links. + * @param string $plugin Path to the plugin file. + */ + $update_actions = apply_filters( 'update_plugin_complete_actions', $update_actions, $this->plugin ); + + if ( ! empty($update_actions) ) + $this->feedback(implode(' | ', (array)$update_actions)); + } +} + +/** + * Plugin Upgrader Skin for WordPress Plugin Upgrades. + * + * @package WordPress + * @subpackage Upgrader + * @since 3.0.0 + */ +class Bulk_Upgrader_Skin extends WP_Upgrader_Skin { + public $in_loop = false; + public $error = false; + + public function __construct($args = array()) { + $defaults = array( 'url' => '', 'nonce' => '' ); + $args = wp_parse_args($args, $defaults); + + parent::__construct($args); + } + + public function add_strings() { + $this->upgrader->strings['skin_upgrade_start'] = __('The update process is starting. This process may take a while on some hosts, so please be patient.'); + $this->upgrader->strings['skin_update_failed_error'] = __('An error occurred while updating %1$s: %2$s'); + $this->upgrader->strings['skin_update_failed'] = __('The update of %1$s failed.'); + $this->upgrader->strings['skin_update_successful'] = __('%1$s updated successfully.').' '.__('Show Details').'.'; + $this->upgrader->strings['skin_upgrade_end'] = __('All updates have been completed.'); + } + + /** + * @param string $string + */ + public function feedback($string) { + if ( isset( $this->upgrader->strings[$string] ) ) + $string = $this->upgrader->strings[$string]; + + if ( strpos($string, '%') !== false ) { + $args = func_get_args(); + $args = array_splice($args, 1); + if ( $args ) { + $args = array_map( 'strip_tags', $args ); + $args = array_map( 'esc_html', $args ); + $string = vsprintf($string, $args); + } + } + if ( empty($string) ) + return; + if ( $this->in_loop ) + echo "$string
    \n"; + else + echo "

    $string

    \n"; + } + + public function header() { + // Nothing, This will be displayed within a iframe. + } + + public function footer() { + // Nothing, This will be displayed within a iframe. + } + public function error($error) { + if ( is_string($error) && isset( $this->upgrader->strings[$error] ) ) + $this->error = $this->upgrader->strings[$error]; + + if ( is_wp_error($error) ) { + $messages = array(); + foreach ( $error->get_error_messages() as $emessage ) { + if ( $error->get_error_data() && is_string( $error->get_error_data() ) ) + $messages[] = $emessage . ' ' . esc_html( strip_tags( $error->get_error_data() ) ); + else + $messages[] = $emessage; + } + $this->error = implode(', ', $messages); + } + echo ''; + } + + public function bulk_header() { + $this->feedback('skin_upgrade_start'); + } + + public function bulk_footer() { + $this->feedback('skin_upgrade_end'); + } + + public function before($title = '') { + $this->in_loop = true; + printf( '

    ' . $this->upgrader->strings['skin_before_update_header'] . '

    ', $title, $this->upgrader->update_current, $this->upgrader->update_count); + echo ''; + echo '

    '; + $this->flush_output(); + } + + public function after($title = '') { + echo '

    '; + if ( $this->error || ! $this->result ) { + if ( $this->error ) + echo '

    ' . sprintf($this->upgrader->strings['skin_update_failed_error'], $title, $this->error) . '

    '; + else + echo '

    ' . sprintf($this->upgrader->strings['skin_update_failed'], $title) . '

    '; + + echo ''; + } + if ( $this->result && ! is_wp_error( $this->result ) ) { + if ( ! $this->error ) + echo '

    ' . sprintf($this->upgrader->strings['skin_update_successful'], $title, 'jQuery(\'#progress-' . esc_js($this->upgrader->update_current) . '\').toggle();jQuery(\'span\', this).toggle(); return false;') . '

    '; + echo ''; + } + + $this->reset(); + $this->flush_output(); + } + + public function reset() { + $this->in_loop = false; + $this->error = false; + } + + public function flush_output() { + wp_ob_end_flush_all(); + flush(); + } +} + +class Bulk_Plugin_Upgrader_Skin extends Bulk_Upgrader_Skin { + public $plugin_info = array(); // Plugin_Upgrader::bulk() will fill this in. + + public function __construct($args = array()) { + parent::__construct($args); + } + + public function add_strings() { + parent::add_strings(); + $this->upgrader->strings['skin_before_update_header'] = __('Updating Plugin %1$s (%2$d/%3$d)'); + } + + public function before($title = '') { + parent::before($this->plugin_info['Title']); + } + + public function after($title = '') { + parent::after($this->plugin_info['Title']); + $this->decrement_update_count( 'plugin' ); + } + public function bulk_footer() { + parent::bulk_footer(); + $update_actions = array( + 'plugins_page' => '' . __('Return to Plugins page') . '', + 'updates_page' => '' . __('Return to WordPress Updates') . '' + ); + if ( ! current_user_can( 'activate_plugins' ) ) + unset( $update_actions['plugins_page'] ); + + /** + * Filter the list of action links available following bulk plugin updates. + * + * @since 3.0.0 + * + * @param array $update_actions Array of plugin action links. + * @param array $plugin_info Array of information for the last-updated plugin. + */ + $update_actions = apply_filters( 'update_bulk_plugins_complete_actions', $update_actions, $this->plugin_info ); + + if ( ! empty($update_actions) ) + $this->feedback(implode(' | ', (array)$update_actions)); + } +} + +class Bulk_Theme_Upgrader_Skin extends Bulk_Upgrader_Skin { + public $theme_info = array(); // Theme_Upgrader::bulk() will fill this in. + + public function __construct($args = array()) { + parent::__construct($args); + } + + public function add_strings() { + parent::add_strings(); + $this->upgrader->strings['skin_before_update_header'] = __('Updating Theme %1$s (%2$d/%3$d)'); + } + + public function before($title = '') { + parent::before( $this->theme_info->display('Name') ); + } + + public function after($title = '') { + parent::after( $this->theme_info->display('Name') ); + $this->decrement_update_count( 'theme' ); + } + + public function bulk_footer() { + parent::bulk_footer(); + $update_actions = array( + 'themes_page' => '' . __('Return to Themes page') . '', + 'updates_page' => '' . __('Return to WordPress Updates') . '' + ); + if ( ! current_user_can( 'switch_themes' ) && ! current_user_can( 'edit_theme_options' ) ) + unset( $update_actions['themes_page'] ); + + /** + * Filter the list of action links available following bulk theme updates. + * + * @since 3.0.0 + * + * @param array $update_actions Array of theme action links. + * @param array $theme_info Array of information for the last-updated theme. + */ + $update_actions = apply_filters( 'update_bulk_theme_complete_actions', $update_actions, $this->theme_info ); + + if ( ! empty($update_actions) ) + $this->feedback(implode(' | ', (array)$update_actions)); + } +} + +/** + * Plugin Installer Skin for WordPress Plugin Installer. + * + * @package WordPress + * @subpackage Upgrader + * @since 2.8.0 + */ +class Plugin_Installer_Skin extends WP_Upgrader_Skin { + public $api; + public $type; + + public function __construct($args = array()) { + $defaults = array( 'type' => 'web', 'url' => '', 'plugin' => '', 'nonce' => '', 'title' => '' ); + $args = wp_parse_args($args, $defaults); + + $this->type = $args['type']; + $this->api = isset($args['api']) ? $args['api'] : array(); + + parent::__construct($args); + } + + public function before() { + if ( !empty($this->api) ) + $this->upgrader->strings['process_success'] = sprintf( __('Successfully installed the plugin %s %s.'), $this->api->name, $this->api->version); + } + + public function after() { + + $plugin_file = $this->upgrader->plugin_info(); + + $install_actions = array(); + + $from = isset($_GET['from']) ? wp_unslash( $_GET['from'] ) : 'plugins'; + + if ( 'import' == $from ) + $install_actions['activate_plugin'] = '' . __('Activate Plugin & Run Importer') . ''; + else + $install_actions['activate_plugin'] = '' . __('Activate Plugin') . ''; + + if ( is_multisite() && current_user_can( 'manage_network_plugins' ) ) { + $install_actions['network_activate'] = '' . __('Network Activate') . ''; + unset( $install_actions['activate_plugin'] ); + } + + if ( 'import' == $from ) + $install_actions['importers_page'] = '' . __('Return to Importers') . ''; + else if ( $this->type == 'web' ) + $install_actions['plugins_page'] = '' . __('Return to Plugin Installer') . ''; + else + $install_actions['plugins_page'] = '' . __('Return to Plugins page') . ''; + + if ( ! $this->result || is_wp_error($this->result) ) { + unset( $install_actions['activate_plugin'], $install_actions['network_activate'] ); + } elseif ( ! current_user_can( 'activate_plugins' ) ) { + unset( $install_actions['activate_plugin'] ); + } + + /** + * Filter the list of action links available following a single plugin installation. + * + * @since 2.7.0 + * + * @param array $install_actions Array of plugin action links. + * @param object $api Object containing WordPress.org API plugin data. Empty + * for non-API installs, such as when a plugin is installed + * via upload. + * @param string $plugin_file Path to the plugin file. + */ + $install_actions = apply_filters( 'install_plugin_complete_actions', $install_actions, $this->api, $plugin_file ); + + if ( ! empty($install_actions) ) + $this->feedback(implode(' | ', (array)$install_actions)); + } +} + +/** + * Theme Installer Skin for the WordPress Theme Installer. + * + * @package WordPress + * @subpackage Upgrader + * @since 2.8.0 + */ +class Theme_Installer_Skin extends WP_Upgrader_Skin { + public $api; + public $type; + + public function __construct($args = array()) { + $defaults = array( 'type' => 'web', 'url' => '', 'theme' => '', 'nonce' => '', 'title' => '' ); + $args = wp_parse_args($args, $defaults); + + $this->type = $args['type']; + $this->api = isset($args['api']) ? $args['api'] : array(); + + parent::__construct($args); + } + + public function before() { + if ( !empty($this->api) ) + $this->upgrader->strings['process_success'] = sprintf( $this->upgrader->strings['process_success_specific'], $this->api->name, $this->api->version); + } + + public function after() { + if ( empty($this->upgrader->result['destination_name']) ) + return; + + $theme_info = $this->upgrader->theme_info(); + if ( empty( $theme_info ) ) + return; + + $name = $theme_info->display('Name'); + $stylesheet = $this->upgrader->result['destination_name']; + $template = $theme_info->get_template(); + + $preview_link = add_query_arg( array( + 'preview' => 1, + 'template' => urlencode( $template ), + 'stylesheet' => urlencode( $stylesheet ), + ), trailingslashit( home_url() ) ); + + $activate_link = add_query_arg( array( + 'action' => 'activate', + 'template' => urlencode( $template ), + 'stylesheet' => urlencode( $stylesheet ), + ), admin_url('themes.php') ); + $activate_link = wp_nonce_url( $activate_link, 'switch-theme_' . $stylesheet ); + + $install_actions = array(); + $install_actions['preview'] = '' . __('Preview') . ''; + if ( current_user_can( 'edit_theme_options' ) && current_user_can( 'customize' ) ) { + $install_actions['preview'] .= '' . __('Live Preview') . ''; + } + $install_actions['activate'] = '' . __('Activate') . ''; + + if ( is_network_admin() && current_user_can( 'manage_network_themes' ) ) + $install_actions['network_enable'] = '' . __( 'Network Enable' ) . ''; + + if ( $this->type == 'web' ) + $install_actions['themes_page'] = '' . __('Return to Theme Installer') . ''; + elseif ( current_user_can( 'switch_themes' ) || current_user_can( 'edit_theme_options' ) ) + $install_actions['themes_page'] = '' . __('Return to Themes page') . ''; + + if ( ! $this->result || is_wp_error($this->result) || is_network_admin() || ! current_user_can( 'switch_themes' ) ) + unset( $install_actions['activate'], $install_actions['preview'] ); + + /** + * Filter the list of action links available following a single theme installation. + * + * @since 2.8.0 + * + * @param array $install_actions Array of theme action links. + * @param object $api Object containing WordPress.org API theme data. + * @param string $stylesheet Theme directory name. + * @param WP_Theme $theme_info Theme object. + */ + $install_actions = apply_filters( 'install_theme_complete_actions', $install_actions, $this->api, $stylesheet, $theme_info ); + if ( ! empty($install_actions) ) + $this->feedback(implode(' | ', (array)$install_actions)); + } +} + +/** + * Theme Upgrader Skin for WordPress Theme Upgrades. + * + * @package WordPress + * @subpackage Upgrader + * @since 2.8.0 + */ +class Theme_Upgrader_Skin extends WP_Upgrader_Skin { + public $theme = ''; + + public function __construct($args = array()) { + $defaults = array( 'url' => '', 'theme' => '', 'nonce' => '', 'title' => __('Update Theme') ); + $args = wp_parse_args($args, $defaults); + + $this->theme = $args['theme']; + + parent::__construct($args); + } + + public function after() { + $this->decrement_update_count( 'theme' ); + + $update_actions = array(); + if ( ! empty( $this->upgrader->result['destination_name'] ) && $theme_info = $this->upgrader->theme_info() ) { + $name = $theme_info->display('Name'); + $stylesheet = $this->upgrader->result['destination_name']; + $template = $theme_info->get_template(); + + $preview_link = add_query_arg( array( + 'preview' => 1, + 'template' => urlencode( $template ), + 'stylesheet' => urlencode( $stylesheet ), + ), trailingslashit( home_url() ) ); + + $activate_link = add_query_arg( array( + 'action' => 'activate', + 'template' => urlencode( $template ), + 'stylesheet' => urlencode( $stylesheet ), + ), admin_url('themes.php') ); + $activate_link = wp_nonce_url( $activate_link, 'switch-theme_' . $stylesheet ); + + if ( get_stylesheet() == $stylesheet ) { + if ( current_user_can( 'edit_theme_options' ) && current_user_can( 'customize' ) ) { + $update_actions['preview'] = '' . __('Customize') . ''; + } + } elseif ( current_user_can( 'switch_themes' ) ) { + $update_actions['preview'] = '' . __('Preview') . ''; + if ( current_user_can( 'edit_theme_options' ) && current_user_can( 'customize' ) ) { + $update_actions['preview'] .= '' . __('Live Preview') . ''; + } + $update_actions['activate'] = '' . __('Activate') . ''; + } + + if ( ! $this->result || is_wp_error( $this->result ) || is_network_admin() ) + unset( $update_actions['preview'], $update_actions['activate'] ); + } + + $update_actions['themes_page'] = '' . __('Return to Themes page') . ''; + + /** + * Filter the list of action links available following a single theme update. + * + * @since 2.8.0 + * + * @param array $update_actions Array of theme action links. + * @param string $theme Theme directory name. + */ + $update_actions = apply_filters( 'update_theme_complete_actions', $update_actions, $this->theme ); + + if ( ! empty($update_actions) ) + $this->feedback(implode(' | ', (array)$update_actions)); + } +} + +/** + * Translation Upgrader Skin for WordPress Translation Upgrades. + * + * @package WordPress + * @subpackage Upgrader + * @since 3.7.0 + */ +class Language_Pack_Upgrader_Skin extends WP_Upgrader_Skin { + public $language_update = null; + public $done_header = false; + public $done_footer = false; + public $display_footer_actions = true; + + public function __construct( $args = array() ) { + $defaults = array( 'url' => '', 'nonce' => '', 'title' => __( 'Update Translations' ), 'skip_header_footer' => false ); + $args = wp_parse_args( $args, $defaults ); + if ( $args['skip_header_footer'] ) { + $this->done_header = true; + $this->done_footer = true; + $this->display_footer_actions = false; + } + parent::__construct( $args ); + } + + public function before() { + $name = $this->upgrader->get_name_for_update( $this->language_update ); + + echo '
    '; + + printf( '

    ' . __( 'Updating translations for %1$s (%2$s)…' ) . '

    ', $name, $this->language_update->language ); + } + + public function error( $error ) { + echo '
    '; + parent::error( $error ); + echo '
    '; + } + + public function after() { + echo '
    '; + } + + public function bulk_footer() { + $this->decrement_update_count( 'translation' ); + $update_actions = array(); + $update_actions['updates_page'] = '' . __( 'Return to WordPress Updates' ) . ''; + + /** + * Filter the list of action links available following a translations update. + * + * @since 3.7.0 + * + * @param array $update_actions Array of translations update links. + */ + $update_actions = apply_filters( 'update_translations_complete_actions', $update_actions ); + + if ( $update_actions && $this->display_footer_actions ) + $this->feedback( implode( ' | ', $update_actions ) ); + } +} + +/** + * Upgrader Skin for Automatic WordPress Upgrades + * + * This skin is designed to be used when no output is intended, all output + * is captured and stored for the caller to process and log/email/discard. + * + * @package WordPress + * @subpackage Upgrader + * @since 3.7.0 + */ +class Automatic_Upgrader_Skin extends WP_Upgrader_Skin { + protected $messages = array(); + + public function request_filesystem_credentials( $error = false, $context = '', $allow_relaxed_file_ownership = false ) { + if ( $context ) { + $this->options['context'] = $context; + } + // TODO: fix up request_filesystem_credentials(), or split it, to allow us to request a no-output version + // This will output a credentials form in event of failure, We don't want that, so just hide with a buffer + ob_start(); + $result = parent::request_filesystem_credentials( $error, $context, $allow_relaxed_file_ownership ); + ob_end_clean(); + return $result; + } + + public function get_upgrade_messages() { + return $this->messages; + } + + /** + * @param string|array|WP_Error $data + */ + public function feedback( $data ) { + if ( is_wp_error( $data ) ) + $string = $data->get_error_message(); + else if ( is_array( $data ) ) + return; + else + $string = $data; + + if ( ! empty( $this->upgrader->strings[ $string ] ) ) + $string = $this->upgrader->strings[ $string ]; + + if ( strpos( $string, '%' ) !== false ) { + $args = func_get_args(); + $args = array_splice( $args, 1 ); + if ( ! empty( $args ) ) + $string = vsprintf( $string, $args ); + } + + $string = trim( $string ); + + // Only allow basic HTML in the messages, as it'll be used in emails/logs rather than direct browser output. + $string = wp_kses( $string, array( + 'a' => array( + 'href' => true + ), + 'br' => true, + 'em' => true, + 'strong' => true, + ) ); + + if ( empty( $string ) ) + return; + + $this->messages[] = $string; + } + + public function header() { + ob_start(); + } + + public function footer() { + $output = ob_get_contents(); + if ( ! empty( $output ) ) + $this->feedback( $output ); + ob_end_clean(); + } + + public function bulk_header() {} + public function bulk_footer() {} + public function before() {} + public function after() {} +} diff --git a/wp-admin/includes/class-wp-upgrader.php b/wp-admin/includes/class-wp-upgrader.php new file mode 100644 index 0000000..0f1ca8b --- /dev/null +++ b/wp-admin/includes/class-wp-upgrader.php @@ -0,0 +1,3253 @@ +skin = new WP_Upgrader_Skin(); + else + $this->skin = $skin; + } + + /** + * Initialize the upgrader. + * + * This will set the relationship between the skin being used and this upgrader, + * and also add the generic strings to `WP_Upgrader::$strings`. + * + * @since 2.8.0 + */ + public function init() { + $this->skin->set_upgrader($this); + $this->generic_strings(); + } + + /** + * Add the generic strings to WP_Upgrader::$strings. + * + * @since 2.8.0 + */ + public function generic_strings() { + $this->strings['bad_request'] = __('Invalid Data provided.'); + $this->strings['fs_unavailable'] = __('Could not access filesystem.'); + $this->strings['fs_error'] = __('Filesystem error.'); + $this->strings['fs_no_root_dir'] = __('Unable to locate WordPress Root directory.'); + $this->strings['fs_no_content_dir'] = __('Unable to locate WordPress Content directory (wp-content).'); + $this->strings['fs_no_plugins_dir'] = __('Unable to locate WordPress Plugin directory.'); + $this->strings['fs_no_themes_dir'] = __('Unable to locate WordPress Theme directory.'); + /* translators: %s: directory name */ + $this->strings['fs_no_folder'] = __('Unable to locate needed folder (%s).'); + + $this->strings['download_failed'] = __('Download failed.'); + $this->strings['installing_package'] = __('Installing the latest version…'); + $this->strings['no_files'] = __('The package contains no files.'); + $this->strings['folder_exists'] = __('Destination folder already exists.'); + $this->strings['mkdir_failed'] = __('Could not create directory.'); + $this->strings['incompatible_archive'] = __('The package could not be installed.'); + + $this->strings['maintenance_start'] = __('Enabling Maintenance mode…'); + $this->strings['maintenance_end'] = __('Disabling Maintenance mode…'); + } + + /** + * Connect to the filesystem. + * + * @since 2.8.0 + * + * @param array $directories Optional. A list of directories. If any of these do + * not exist, a {@see WP_Error} object will be returned. + * Default empty array. + * @param bool $allow_relaxed_file_ownership Whether to allow relaxed file ownership. + * Default false. + * @return bool|WP_Error True if able to connect, false or a {@see WP_Error} otherwise. + */ + public function fs_connect( $directories = array(), $allow_relaxed_file_ownership = false ) { + global $wp_filesystem; + + if ( false === ( $credentials = $this->skin->request_filesystem_credentials( false, $directories[0], $allow_relaxed_file_ownership ) ) ) { + return false; + } + + if ( ! WP_Filesystem( $credentials, $directories[0], $allow_relaxed_file_ownership ) ) { + $error = true; + if ( is_object($wp_filesystem) && $wp_filesystem->errors->get_error_code() ) + $error = $wp_filesystem->errors; + // Failed to connect, Error and request again + $this->skin->request_filesystem_credentials( $error, $directories[0], $allow_relaxed_file_ownership ); + return false; + } + + if ( ! is_object($wp_filesystem) ) + return new WP_Error('fs_unavailable', $this->strings['fs_unavailable'] ); + + if ( is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code() ) + return new WP_Error('fs_error', $this->strings['fs_error'], $wp_filesystem->errors); + + foreach ( (array)$directories as $dir ) { + switch ( $dir ) { + case ABSPATH: + if ( ! $wp_filesystem->abspath() ) + return new WP_Error('fs_no_root_dir', $this->strings['fs_no_root_dir']); + break; + case WP_CONTENT_DIR: + if ( ! $wp_filesystem->wp_content_dir() ) + return new WP_Error('fs_no_content_dir', $this->strings['fs_no_content_dir']); + break; + case WP_PLUGIN_DIR: + if ( ! $wp_filesystem->wp_plugins_dir() ) + return new WP_Error('fs_no_plugins_dir', $this->strings['fs_no_plugins_dir']); + break; + case get_theme_root(): + if ( ! $wp_filesystem->wp_themes_dir() ) + return new WP_Error('fs_no_themes_dir', $this->strings['fs_no_themes_dir']); + break; + default: + if ( ! $wp_filesystem->find_folder($dir) ) + return new WP_Error( 'fs_no_folder', sprintf( $this->strings['fs_no_folder'], esc_html( basename( $dir ) ) ) ); + break; + } + } + return true; + } //end fs_connect(); + + /** + * Download a package. + * + * @since 2.8.0 + * + * @param string $package The URI of the package. If this is the full path to an + * existing local file, it will be returned untouched. + * @return string|WP_Error The full path to the downloaded package file, or a {@see WP_Error} object. + */ + public function download_package( $package ) { + + /** + * Filter whether to return the package. + * + * @since 3.7.0 + * + * @param bool $reply Whether to bail without returning the package. + * Default false. + * @param string $package The package file name. + * @param WP_Upgrader $this The WP_Upgrader instance. + */ + $reply = apply_filters( 'upgrader_pre_download', false, $package, $this ); + if ( false !== $reply ) + return $reply; + + if ( ! preg_match('!^(http|https|ftp)://!i', $package) && file_exists($package) ) //Local file or remote? + return $package; //must be a local file.. + + if ( empty($package) ) + return new WP_Error('no_package', $this->strings['no_package']); + + $this->skin->feedback('downloading_package', $package); + + $download_file = download_url($package); + + if ( is_wp_error($download_file) ) + return new WP_Error('download_failed', $this->strings['download_failed'], $download_file->get_error_message()); + + return $download_file; + } + + /** + * Unpack a compressed package file. + * + * @since 2.8.0 + * + * @param string $package Full path to the package file. + * @param bool $delete_package Optional. Whether to delete the package file after attempting + * to unpack it. Default true. + * @return string|WP_Error The path to the unpacked contents, or a {@see WP_Error} on failure. + */ + public function unpack_package( $package, $delete_package = true ) { + global $wp_filesystem; + + $this->skin->feedback('unpack_package'); + + $upgrade_folder = $wp_filesystem->wp_content_dir() . 'upgrade/'; + + //Clean up contents of upgrade directory beforehand. + $upgrade_files = $wp_filesystem->dirlist($upgrade_folder); + if ( !empty($upgrade_files) ) { + foreach ( $upgrade_files as $file ) + $wp_filesystem->delete($upgrade_folder . $file['name'], true); + } + + //We need a working directory + $working_dir = $upgrade_folder . basename($package, '.zip'); + + // Clean up working directory + if ( $wp_filesystem->is_dir($working_dir) ) + $wp_filesystem->delete($working_dir, true); + + // Unzip package to working directory + $result = unzip_file( $package, $working_dir ); + + // Once extracted, delete the package if required. + if ( $delete_package ) + unlink($package); + + if ( is_wp_error($result) ) { + $wp_filesystem->delete($working_dir, true); + if ( 'incompatible_archive' == $result->get_error_code() ) { + return new WP_Error( 'incompatible_archive', $this->strings['incompatible_archive'], $result->get_error_data() ); + } + return $result; + } + + return $working_dir; + } + + /** + * Install a package. + * + * Copies the contents of a package form a source directory, and installs them in + * a destination directory. Optionally removes the source. It can also optionally + * clear out the destination folder if it already exists. + * + * @since 2.8.0 + * + * @param array|string $args { + * Optional. Array or string of arguments for installing a package. Default empty array. + * + * @type string $source Required path to the package source. Default empty. + * @type string $destination Required path to a folder to install the package in. + * Default empty. + * @type bool $clear_destination Whether to delete any files already in the destination + * folder. Default false. + * @type bool $clear_working Whether to delete the files form the working directory + * after copying to the destination. Default false. + * @type bool $abort_if_destination_exists Whether to abort the installation if + * the destination folder already exists. Default true. + * @type array $hook_extra Extra arguments to pass to the filter hooks called by + * {@see WP_Upgrader::install_package()}. Default empty array. + * } + * + * @return array|WP_Error The result (also stored in `WP_Upgrader:$result`), or a {@see WP_Error} on failure. + */ + public function install_package( $args = array() ) { + global $wp_filesystem, $wp_theme_directories; + + $defaults = array( + 'source' => '', // Please always pass this + 'destination' => '', // and this + 'clear_destination' => false, + 'clear_working' => false, + 'abort_if_destination_exists' => true, + 'hook_extra' => array() + ); + + $args = wp_parse_args($args, $defaults); + + // These were previously extract()'d. + $source = $args['source']; + $destination = $args['destination']; + $clear_destination = $args['clear_destination']; + + @set_time_limit( 300 ); + + if ( empty( $source ) || empty( $destination ) ) { + return new WP_Error( 'bad_request', $this->strings['bad_request'] ); + } + $this->skin->feedback( 'installing_package' ); + + /** + * Filter the install response before the installation has started. + * + * Returning a truthy value, or one that could be evaluated as a WP_Error + * will effectively short-circuit the installation, returning that value + * instead. + * + * @since 2.8.0 + * + * @param bool|WP_Error $response Response. + * @param array $hook_extra Extra arguments passed to hooked filters. + */ + $res = apply_filters( 'upgrader_pre_install', true, $args['hook_extra'] ); + if ( is_wp_error( $res ) ) { + return $res; + } + + //Retain the Original source and destinations + $remote_source = $args['source']; + $local_destination = $destination; + + $source_files = array_keys( $wp_filesystem->dirlist( $remote_source ) ); + $remote_destination = $wp_filesystem->find_folder( $local_destination ); + + //Locate which directory to copy to the new folder, This is based on the actual folder holding the files. + if ( 1 == count( $source_files ) && $wp_filesystem->is_dir( trailingslashit( $args['source'] ) . $source_files[0] . '/' ) ) { //Only one folder? Then we want its contents. + $source = trailingslashit( $args['source'] ) . trailingslashit( $source_files[0] ); + } elseif ( count( $source_files ) == 0 ) { + return new WP_Error( 'incompatible_archive_empty', $this->strings['incompatible_archive'], $this->strings['no_files'] ); // There are no files? + } else { //It's only a single file, the upgrader will use the foldername of this file as the destination folder. foldername is based on zip filename. + $source = trailingslashit( $args['source'] ); + } + + /** + * Filter the source file location for the upgrade package. + * + * @since 2.8.0 + * + * @param string $source File source location. + * @param string $remote_source Remove file source location. + * @param WP_Upgrader $this WP_Upgrader instance. + */ + $source = apply_filters( 'upgrader_source_selection', $source, $remote_source, $this ); + if ( is_wp_error( $source ) ) { + return $source; + } + + // Has the source location changed? If so, we need a new source_files list. + if ( $source !== $remote_source ) { + $source_files = array_keys( $wp_filesystem->dirlist( $source ) ); + } + /* + * Protection against deleting files in any important base directories. + * Theme_Upgrader & Plugin_Upgrader also trigger this, as they pass the + * destination directory (WP_PLUGIN_DIR / wp-content/themes) intending + * to copy the directory into the directory, whilst they pass the source + * as the actual files to copy. + */ + $protected_directories = array( ABSPATH, WP_CONTENT_DIR, WP_PLUGIN_DIR, WP_CONTENT_DIR . '/themes' ); + + if ( is_array( $wp_theme_directories ) ) { + $protected_directories = array_merge( $protected_directories, $wp_theme_directories ); + } + if ( in_array( $destination, $protected_directories ) ) { + $remote_destination = trailingslashit( $remote_destination ) . trailingslashit( basename( $source ) ); + $destination = trailingslashit( $destination ) . trailingslashit( basename( $source ) ); + } + + if ( $clear_destination ) { + //We're going to clear the destination if there's something there + $this->skin->feedback('remove_old'); + $removed = true; + if ( $wp_filesystem->exists( $remote_destination ) ) { + $removed = $wp_filesystem->delete( $remote_destination, true ); + } + + /** + * Filter whether the upgrader cleared the destination. + * + * @since 2.8.0 + * + * @param bool $removed Whether the destination was cleared. + * @param string $local_destination The local package destination. + * @param string $remote_destination The remote package destination. + * @param array $hook_extra Extra arguments passed to hooked filters. + */ + $removed = apply_filters( 'upgrader_clear_destination', $removed, $local_destination, $remote_destination, $args['hook_extra'] ); + + if ( is_wp_error($removed) ) { + return $removed; + } else if ( ! $removed ) { + return new WP_Error('remove_old_failed', $this->strings['remove_old_failed']); + } + } elseif ( $args['abort_if_destination_exists'] && $wp_filesystem->exists($remote_destination) ) { + //If we're not clearing the destination folder and something exists there already, Bail. + //But first check to see if there are actually any files in the folder. + $_files = $wp_filesystem->dirlist($remote_destination); + if ( ! empty($_files) ) { + $wp_filesystem->delete($remote_source, true); //Clear out the source files. + return new WP_Error('folder_exists', $this->strings['folder_exists'], $remote_destination ); + } + } + + //Create destination if needed + if ( ! $wp_filesystem->exists( $remote_destination ) ) { + if ( ! $wp_filesystem->mkdir( $remote_destination, FS_CHMOD_DIR ) ) { + return new WP_Error( 'mkdir_failed_destination', $this->strings['mkdir_failed'], $remote_destination ); + } + } + // Copy new version of item into place. + $result = copy_dir($source, $remote_destination); + if ( is_wp_error($result) ) { + if ( $args['clear_working'] ) { + $wp_filesystem->delete( $remote_source, true ); + } + return $result; + } + + //Clear the Working folder? + if ( $args['clear_working'] ) { + $wp_filesystem->delete( $remote_source, true ); + } + + $destination_name = basename( str_replace($local_destination, '', $destination) ); + if ( '.' == $destination_name ) { + $destination_name = ''; + } + + $this->result = compact('local_source', 'source', 'source_name', 'source_files', 'destination', 'destination_name', 'local_destination', 'remote_destination', 'clear_destination', 'delete_source_dir'); + + /** + * Filter the install response after the installation has finished. + * + * @since 2.8.0 + * + * @param bool $response Install response. + * @param array $hook_extra Extra arguments passed to hooked filters. + * @param array $result Installation result data. + */ + $res = apply_filters( 'upgrader_post_install', true, $args['hook_extra'], $this->result ); + + if ( is_wp_error($res) ) { + $this->result = $res; + return $res; + } + + //Bombard the calling function will all the info which we've just used. + return $this->result; + } + + /** + * Run an upgrade/install. + * + * Attempts to download the package (if it is not a local file), unpack it, and + * install it in the destination folder. + * + * @since 2.8.0 + * + * @param array $options { + * Array or string of arguments for upgrading/installing a package. + * + * @type string $package The full path or URI of the package to install. + * Default empty. + * @type string $destination The full path to the destination folder. + * Default empty. + * @type bool $clear_destination Whether to delete any files already in the + * destination folder. Default false. + * @type bool $clear_working Whether to delete the files form the working + * directory after copying to the destination. + * Default false. + * @type bool $abort_if_destination_exists Whether to abort the installation if the destination + * folder already exists. When true, `$clear_destination` + * should be false. Default true. + * @type bool $is_multi Whether this run is one of multiple upgrade/install + * actions being performed in bulk. When true, the skin + * {@see WP_Upgrader::header()} and {@see WP_Upgrader::footer()} + * aren't called. Default false. + * @type array $hook_extra Extra arguments to pass to the filter hooks called by + * {@see WP_Upgrader::run()}. + * } + * + * @return array|false|WP_error The result from self::install_package() on success, otherwise a WP_Error, + * or false if unable to connect to the filesystem. + */ + public function run( $options ) { + + $defaults = array( + 'package' => '', // Please always pass this. + 'destination' => '', // And this + 'clear_destination' => false, + 'abort_if_destination_exists' => true, // Abort if the Destination directory exists, Pass clear_destination as false please + 'clear_working' => true, + 'is_multi' => false, + 'hook_extra' => array() // Pass any extra $hook_extra args here, this will be passed to any hooked filters. + ); + + $options = wp_parse_args( $options, $defaults ); + + if ( ! $options['is_multi'] ) { // call $this->header separately if running multiple times + $this->skin->header(); + } + + // Connect to the Filesystem first. + $res = $this->fs_connect( array( WP_CONTENT_DIR, $options['destination'] ) ); + // Mainly for non-connected filesystem. + if ( ! $res ) { + if ( ! $options['is_multi'] ) { + $this->skin->footer(); + } + return false; + } + + $this->skin->before(); + + if ( is_wp_error($res) ) { + $this->skin->error($res); + $this->skin->after(); + if ( ! $options['is_multi'] ) { + $this->skin->footer(); + } + return $res; + } + + //Download the package (Note, This just returns the filename of the file if the package is a local file) + $download = $this->download_package( $options['package'] ); + if ( is_wp_error($download) ) { + $this->skin->error($download); + $this->skin->after(); + if ( ! $options['is_multi'] ) { + $this->skin->footer(); + } + return $download; + } + + $delete_package = ( $download != $options['package'] ); // Do not delete a "local" file + + //Unzips the file into a temporary directory + $working_dir = $this->unpack_package( $download, $delete_package ); + if ( is_wp_error($working_dir) ) { + $this->skin->error($working_dir); + $this->skin->after(); + if ( ! $options['is_multi'] ) { + $this->skin->footer(); + } + return $working_dir; + } + + //With the given options, this installs it to the destination directory. + $result = $this->install_package( array( + 'source' => $working_dir, + 'destination' => $options['destination'], + 'clear_destination' => $options['clear_destination'], + 'abort_if_destination_exists' => $options['abort_if_destination_exists'], + 'clear_working' => $options['clear_working'], + 'hook_extra' => $options['hook_extra'] + ) ); + + $this->skin->set_result($result); + if ( is_wp_error($result) ) { + $this->skin->error($result); + $this->skin->feedback('process_failed'); + } else { + //Install Succeeded + $this->skin->feedback('process_success'); + } + + $this->skin->after(); + + if ( ! $options['is_multi'] ) { + + /** This action is documented in wp-admin/includes/class-wp-upgrader.php */ + do_action( 'upgrader_process_complete', $this, $options['hook_extra'] ); + $this->skin->footer(); + } + + return $result; + } + + /** + * Toggle maintenance mode for the site. + * + * Creates/deletes the maintenance file to enable/disable maintenance mode. + * + * @since 2.8.0 + * + * @param bool $enable True to enable maintenance mode, false to disable. + */ + public function maintenance_mode( $enable = false ) { + global $wp_filesystem; + $file = $wp_filesystem->abspath() . '.maintenance'; + if ( $enable ) { + $this->skin->feedback('maintenance_start'); + // Create maintenance file to signal that we are upgrading + $maintenance_string = ''; + $wp_filesystem->delete($file); + $wp_filesystem->put_contents($file, $maintenance_string, FS_CHMOD_FILE); + } else if ( !$enable && $wp_filesystem->exists($file) ) { + $this->skin->feedback('maintenance_end'); + $wp_filesystem->delete($file); + } + } + +} + +/** + * Plugin Upgrader class for WordPress Plugins, It is designed to upgrade/install plugins from a local zip, remote zip URL, or uploaded zip file. + * + * @package WordPress + * @subpackage Upgrader + * @since 2.8.0 + */ +class Plugin_Upgrader extends WP_Upgrader { + + /** + * Plugin upgrade result. + * + * @since 2.8.0 + * @var array|WP_Error $result + * @see WP_Upgrader::$result + */ + public $result; + + /** + * Whether a bulk upgrade/install is being performed. + * + * @since 2.9.0 + * @var bool $bulk + */ + public $bulk = false; + + /** + * Initialize the upgrade strings. + * + * @since 2.8.0 + */ + public function upgrade_strings() { + $this->strings['up_to_date'] = __('The plugin is at the latest version.'); + $this->strings['no_package'] = __('Update package not available.'); + $this->strings['downloading_package'] = __('Downloading update from %s…'); + $this->strings['unpack_package'] = __('Unpacking the update…'); + $this->strings['remove_old'] = __('Removing the old version of the plugin…'); + $this->strings['remove_old_failed'] = __('Could not remove the old plugin.'); + $this->strings['process_failed'] = __('Plugin update failed.'); + $this->strings['process_success'] = __('Plugin updated successfully.'); + } + + /** + * Initialize the install strings. + * + * @since 2.8.0 + */ + public function install_strings() { + $this->strings['no_package'] = __('Install package not available.'); + $this->strings['downloading_package'] = __('Downloading install package from %s…'); + $this->strings['unpack_package'] = __('Unpacking the package…'); + $this->strings['installing_package'] = __('Installing the plugin…'); + $this->strings['no_files'] = __('The plugin contains no files.'); + $this->strings['process_failed'] = __('Plugin install failed.'); + $this->strings['process_success'] = __('Plugin installed successfully.'); + } + + /** + * Install a plugin package. + * + * @since 2.8.0 + * @since 3.7.0 The `$args` parameter was added, making clearing the plugin update cache optional. + * + * @param string $package The full local path or URI of the package. + * @param array $args { + * Optional. Other arguments for installing a plugin package. Default empty array. + * + * @type bool $clear_update_cache Whether to clear the plugin updates cache if successful. + * Default true. + * } + * + * @return bool|WP_Error True if the install was successful, false or a WP_Error otherwise. + */ + public function install( $package, $args = array() ) { + + $defaults = array( + 'clear_update_cache' => true, + ); + $parsed_args = wp_parse_args( $args, $defaults ); + + $this->init(); + $this->install_strings(); + + add_filter('upgrader_source_selection', array($this, 'check_package') ); + + $this->run( array( + 'package' => $package, + 'destination' => WP_PLUGIN_DIR, + 'clear_destination' => false, // Do not overwrite files. + 'clear_working' => true, + 'hook_extra' => array( + 'type' => 'plugin', + 'action' => 'install', + ) + ) ); + + remove_filter('upgrader_source_selection', array($this, 'check_package') ); + + if ( ! $this->result || is_wp_error($this->result) ) + return $this->result; + + // Force refresh of plugin update information + wp_clean_plugins_cache( $parsed_args['clear_update_cache'] ); + + return true; + } + + /** + * Upgrade a plugin. + * + * @since 2.8.0 + * @since 3.7.0 The `$args` parameter was added, making clearing the plugin update cache optional. + * + * @param string $plugin The basename path to the main plugin file. + * @param array $args { + * Optional. Other arguments for upgrading a plugin package. Defualt empty array. + * + * @type bool $clear_update_cache Whether to clear the plugin updates cache if successful. + * Default true. + * } + * @return bool|WP_Error True if the upgrade was successful, false or a {@see WP_Error} object otherwise. + */ + public function upgrade( $plugin, $args = array() ) { + + $defaults = array( + 'clear_update_cache' => true, + ); + $parsed_args = wp_parse_args( $args, $defaults ); + + $this->init(); + $this->upgrade_strings(); + + $current = get_site_transient( 'update_plugins' ); + if ( !isset( $current->response[ $plugin ] ) ) { + $this->skin->before(); + $this->skin->set_result(false); + $this->skin->error('up_to_date'); + $this->skin->after(); + return false; + } + + // Get the URL to the zip file + $r = $current->response[ $plugin ]; + + add_filter('upgrader_pre_install', array($this, 'deactivate_plugin_before_upgrade'), 10, 2); + add_filter('upgrader_clear_destination', array($this, 'delete_old_plugin'), 10, 4); + //'source_selection' => array($this, 'source_selection'), //there's a trac ticket to move up the directory for zip's which are made a bit differently, useful for non-.org plugins. + + $this->run( array( + 'package' => $r->package, + 'destination' => WP_PLUGIN_DIR, + 'clear_destination' => true, + 'clear_working' => true, + 'hook_extra' => array( + 'plugin' => $plugin, + 'type' => 'plugin', + 'action' => 'update', + ), + ) ); + + // Cleanup our hooks, in case something else does a upgrade on this connection. + remove_filter('upgrader_pre_install', array($this, 'deactivate_plugin_before_upgrade')); + remove_filter('upgrader_clear_destination', array($this, 'delete_old_plugin')); + + if ( ! $this->result || is_wp_error($this->result) ) + return $this->result; + + // Force refresh of plugin update information + wp_clean_plugins_cache( $parsed_args['clear_update_cache'] ); + + return true; + } + + /** + * Bulk upgrade several plugins at once. + * + * @since 2.8.0 + * @since 3.7.0 The `$args` parameter was added, making clearing the plugin update cache optional. + * + * @param string $plugins Array of the basename paths of the plugins' main files. + * @param array $args { + * Optional. Other arguments for upgrading several plugins at once. Default empty array. + * + * @type bool $clear_update_cache Whether to clear the plugin updates cache if successful. + * Default true. + * } + * + * @return array|false An array of results indexed by plugin file, or false if unable to connect to the filesystem. + */ + public function bulk_upgrade( $plugins, $args = array() ) { + + $defaults = array( + 'clear_update_cache' => true, + ); + $parsed_args = wp_parse_args( $args, $defaults ); + + $this->init(); + $this->bulk = true; + $this->upgrade_strings(); + + $current = get_site_transient( 'update_plugins' ); + + add_filter('upgrader_clear_destination', array($this, 'delete_old_plugin'), 10, 4); + + $this->skin->header(); + + // Connect to the Filesystem first. + $res = $this->fs_connect( array(WP_CONTENT_DIR, WP_PLUGIN_DIR) ); + if ( ! $res ) { + $this->skin->footer(); + return false; + } + + $this->skin->bulk_header(); + + // Only start maintenance mode if: + // - running Multisite and there are one or more plugins specified, OR + // - a plugin with an update available is currently active. + // @TODO: For multisite, maintenance mode should only kick in for individual sites if at all possible. + $maintenance = ( is_multisite() && ! empty( $plugins ) ); + foreach ( $plugins as $plugin ) + $maintenance = $maintenance || ( is_plugin_active( $plugin ) && isset( $current->response[ $plugin] ) ); + if ( $maintenance ) + $this->maintenance_mode(true); + + $results = array(); + + $this->update_count = count($plugins); + $this->update_current = 0; + foreach ( $plugins as $plugin ) { + $this->update_current++; + $this->skin->plugin_info = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin, false, true); + + if ( !isset( $current->response[ $plugin ] ) ) { + $this->skin->set_result('up_to_date'); + $this->skin->before(); + $this->skin->feedback('up_to_date'); + $this->skin->after(); + $results[$plugin] = true; + continue; + } + + // Get the URL to the zip file + $r = $current->response[ $plugin ]; + + $this->skin->plugin_active = is_plugin_active($plugin); + + $result = $this->run( array( + 'package' => $r->package, + 'destination' => WP_PLUGIN_DIR, + 'clear_destination' => true, + 'clear_working' => true, + 'is_multi' => true, + 'hook_extra' => array( + 'plugin' => $plugin + ) + ) ); + + $results[$plugin] = $this->result; + + // Prevent credentials auth screen from displaying multiple times + if ( false === $result ) + break; + } //end foreach $plugins + + $this->maintenance_mode(false); + + /** + * Fires when the bulk upgrader process is complete. + * + * @since 3.6.0 + * + * @param Plugin_Upgrader $this Plugin_Upgrader instance. In other contexts, $this, might + * be a Theme_Upgrader or Core_Upgrade instance. + * @param array $data { + * Array of bulk item update data. + * + * @type string $action Type of action. Default 'update'. + * @type string $type Type of update process. Accepts 'plugin', 'theme', or 'core'. + * @type bool $bulk Whether the update process is a bulk update. Default true. + * @type array $packages Array of plugin, theme, or core packages to update. + * } + */ + do_action( 'upgrader_process_complete', $this, array( + 'action' => 'update', + 'type' => 'plugin', + 'bulk' => true, + 'plugins' => $plugins, + ) ); + + $this->skin->bulk_footer(); + + $this->skin->footer(); + + // Cleanup our hooks, in case something else does a upgrade on this connection. + remove_filter('upgrader_clear_destination', array($this, 'delete_old_plugin')); + + // Force refresh of plugin update information + wp_clean_plugins_cache( $parsed_args['clear_update_cache'] ); + + return $results; + } + + /** + * Check a source package to be sure it contains a plugin. + * + * This function is added to the {@see 'upgrader_source_selection'} filter by + * {@see Plugin_Upgrader::install()}. + * + * @since 3.3.0 + * + * @param string $source The path to the downloaded package source. + * @return string|WP_Error The source as passed, or a {@see WP_Error} object if no plugins were found. + */ + public function check_package($source) { + global $wp_filesystem; + + if ( is_wp_error($source) ) + return $source; + + $working_directory = str_replace( $wp_filesystem->wp_content_dir(), trailingslashit(WP_CONTENT_DIR), $source); + if ( ! is_dir($working_directory) ) // Sanity check, if the above fails, let's not prevent installation. + return $source; + + // Check the folder contains at least 1 valid plugin. + $plugins_found = false; + foreach ( glob( $working_directory . '*.php' ) as $file ) { + $info = get_plugin_data($file, false, false); + if ( !empty( $info['Name'] ) ) { + $plugins_found = true; + break; + } + } + + if ( ! $plugins_found ) + return new WP_Error( 'incompatible_archive_no_plugins', $this->strings['incompatible_archive'], __( 'No valid plugins were found.' ) ); + + return $source; + } + + /** + * Retrieve the path to the file that contains the plugin info. + * + * This isn't used internally in the class, but is called by the skins. + * + * @since 2.8.0 + * + * @return string|false The full path to the main plugin file, or false. + */ + public function plugin_info() { + if ( ! is_array($this->result) ) + return false; + if ( empty($this->result['destination_name']) ) + return false; + + $plugin = get_plugins('/' . $this->result['destination_name']); //Ensure to pass with leading slash + if ( empty($plugin) ) + return false; + + $pluginfiles = array_keys($plugin); //Assume the requested plugin is the first in the list + + return $this->result['destination_name'] . '/' . $pluginfiles[0]; + } + + /** + * Deactivates a plugin before it is upgraded. + * + * Hooked to the {@see 'upgrader_pre_install'} filter by {@see Plugin_Upgrader::upgrade()}. + * + * @since 2.8.0 + * @since 4.1.0 Added a return value. + * + * @param bool|WP_Error $return Upgrade offer return. + * @param array $plugin Plugin package arguments. + * @return bool|WP_Error The passed in $return param or {@see WP_Error}. + */ + public function deactivate_plugin_before_upgrade($return, $plugin) { + + if ( is_wp_error($return) ) //Bypass. + return $return; + + // When in cron (background updates) don't deactivate the plugin, as we require a browser to reactivate it + if ( defined( 'DOING_CRON' ) && DOING_CRON ) + return $return; + + $plugin = isset($plugin['plugin']) ? $plugin['plugin'] : ''; + if ( empty($plugin) ) + return new WP_Error('bad_request', $this->strings['bad_request']); + + if ( is_plugin_active($plugin) ) { + //Deactivate the plugin silently, Prevent deactivation hooks from running. + deactivate_plugins($plugin, true); + } + + return $return; + } + + /** + * Delete the old plugin during an upgrade. + * + * Hooked to the {@see 'upgrader_clear_destination'} filter by + * {@see Plugin_Upgrader::upgrade()} and {@see Plugin_Upgrader::bulk_upgrade()}. + * + * @since 2.8.0 + */ + public function delete_old_plugin($removed, $local_destination, $remote_destination, $plugin) { + global $wp_filesystem; + + if ( is_wp_error($removed) ) + return $removed; //Pass errors through. + + $plugin = isset($plugin['plugin']) ? $plugin['plugin'] : ''; + if ( empty($plugin) ) + return new WP_Error('bad_request', $this->strings['bad_request']); + + $plugins_dir = $wp_filesystem->wp_plugins_dir(); + $this_plugin_dir = trailingslashit( dirname($plugins_dir . $plugin) ); + + if ( ! $wp_filesystem->exists($this_plugin_dir) ) //If it's already vanished. + return $removed; + + // If plugin is in its own directory, recursively delete the directory. + if ( strpos($plugin, '/') && $this_plugin_dir != $plugins_dir ) //base check on if plugin includes directory separator AND that it's not the root plugin folder + $deleted = $wp_filesystem->delete($this_plugin_dir, true); + else + $deleted = $wp_filesystem->delete($plugins_dir . $plugin); + + if ( ! $deleted ) + return new WP_Error('remove_old_failed', $this->strings['remove_old_failed']); + + return true; + } +} + +/** + * Theme Upgrader class for WordPress Themes, It is designed to upgrade/install themes from a local zip, remote zip URL, or uploaded zip file. + * + * @package WordPress + * @subpackage Upgrader + * @since 2.8.0 + */ +class Theme_Upgrader extends WP_Upgrader { + + /** + * Result of the theme upgrade offer. + * + * @since 2.8.0 + * @var array|WP_Erorr $result + * @see WP_Upgrader::$result + */ + public $result; + + /** + * Whether multiple plugins are being upgraded/installed in bulk. + * + * @since 2.9.0 + * @var bool $bulk + */ + public $bulk = false; + + /** + * Initialize the upgrade strings. + * + * @since 2.8.0 + */ + public function upgrade_strings() { + $this->strings['up_to_date'] = __('The theme is at the latest version.'); + $this->strings['no_package'] = __('Update package not available.'); + $this->strings['downloading_package'] = __('Downloading update from %s…'); + $this->strings['unpack_package'] = __('Unpacking the update…'); + $this->strings['remove_old'] = __('Removing the old version of the theme…'); + $this->strings['remove_old_failed'] = __('Could not remove the old theme.'); + $this->strings['process_failed'] = __('Theme update failed.'); + $this->strings['process_success'] = __('Theme updated successfully.'); + } + + /** + * Initialize the install strings. + * + * @since 2.8.0 + */ + public function install_strings() { + $this->strings['no_package'] = __('Install package not available.'); + $this->strings['downloading_package'] = __('Downloading install package from %s…'); + $this->strings['unpack_package'] = __('Unpacking the package…'); + $this->strings['installing_package'] = __('Installing the theme…'); + $this->strings['no_files'] = __('The theme contains no files.'); + $this->strings['process_failed'] = __('Theme install failed.'); + $this->strings['process_success'] = __('Theme installed successfully.'); + /* translators: 1: theme name, 2: version */ + $this->strings['process_success_specific'] = __('Successfully installed the theme %1$s %2$s.'); + $this->strings['parent_theme_search'] = __('This theme requires a parent theme. Checking if it is installed…'); + /* translators: 1: theme name, 2: version */ + $this->strings['parent_theme_prepare_install'] = __('Preparing to install %1$s %2$s…'); + /* translators: 1: theme name, 2: version */ + $this->strings['parent_theme_currently_installed'] = __('The parent theme, %1$s %2$s, is currently installed.'); + /* translators: 1: theme name, 2: version */ + $this->strings['parent_theme_install_success'] = __('Successfully installed the parent theme, %1$s %2$s.'); + $this->strings['parent_theme_not_found'] = __('The parent theme could not be found. You will need to install the parent theme, %s, before you can use this child theme.'); + } + + /** + * Check if a child theme is being installed and we need to install its parent. + * + * Hooked to the {@see 'upgrader_post_install'} filter by {@see Theme_Upgrader::install()}. + * + * @since 3.4.0 + */ + public function check_parent_theme_filter( $install_result, $hook_extra, $child_result ) { + // Check to see if we need to install a parent theme + $theme_info = $this->theme_info(); + + if ( ! $theme_info->parent() ) + return $install_result; + + $this->skin->feedback( 'parent_theme_search' ); + + if ( ! $theme_info->parent()->errors() ) { + $this->skin->feedback( 'parent_theme_currently_installed', $theme_info->parent()->display('Name'), $theme_info->parent()->display('Version') ); + // We already have the theme, fall through. + return $install_result; + } + + // We don't have the parent theme, let's install it. + $api = themes_api('theme_information', array('slug' => $theme_info->get('Template'), 'fields' => array('sections' => false, 'tags' => false) ) ); //Save on a bit of bandwidth. + + if ( ! $api || is_wp_error($api) ) { + $this->skin->feedback( 'parent_theme_not_found', $theme_info->get('Template') ); + // Don't show activate or preview actions after install + add_filter('install_theme_complete_actions', array($this, 'hide_activate_preview_actions') ); + return $install_result; + } + + // Backup required data we're going to override: + $child_api = $this->skin->api; + $child_success_message = $this->strings['process_success']; + + // Override them + $this->skin->api = $api; + $this->strings['process_success_specific'] = $this->strings['parent_theme_install_success'];//, $api->name, $api->version); + + $this->skin->feedback('parent_theme_prepare_install', $api->name, $api->version); + + add_filter('install_theme_complete_actions', '__return_false', 999); // Don't show any actions after installing the theme. + + // Install the parent theme + $parent_result = $this->run( array( + 'package' => $api->download_link, + 'destination' => get_theme_root(), + 'clear_destination' => false, //Do not overwrite files. + 'clear_working' => true + ) ); + + if ( is_wp_error($parent_result) ) + add_filter('install_theme_complete_actions', array($this, 'hide_activate_preview_actions') ); + + // Start cleaning up after the parents installation + remove_filter('install_theme_complete_actions', '__return_false', 999); + + // Reset child's result and data + $this->result = $child_result; + $this->skin->api = $child_api; + $this->strings['process_success'] = $child_success_message; + + return $install_result; + } + + /** + * Don't display the activate and preview actions to the user. + * + * Hooked to the {@see 'install_theme_complete_actions'} filter by + * {@see Theme_Upgrader::check_parent_theme_filter()} when installing + * a child theme and installing the parent theme fails. + * + * @since 3.4.0 + * + * @param array $actions Preview actions. + */ + public function hide_activate_preview_actions( $actions ) { + unset($actions['activate'], $actions['preview']); + return $actions; + } + + /** + * Install a theme package. + * + * @since 2.8.0 + * @since 3.7.0 The `$args` parameter was added, making clearing the update cache optional. + * + * @param string $package The full local path or URI of the package. + * @param array $args { + * Optional. Other arguments for installing a theme package. Default empty array. + * + * @type bool $clear_update_cache Whether to clear the updates cache if successful. + * Default true. + * } + * + * @return bool|WP_Error True if the install was successful, false or a {@see WP_Error} object otherwise. + */ + public function install( $package, $args = array() ) { + + $defaults = array( + 'clear_update_cache' => true, + ); + $parsed_args = wp_parse_args( $args, $defaults ); + + $this->init(); + $this->install_strings(); + + add_filter('upgrader_source_selection', array($this, 'check_package') ); + add_filter('upgrader_post_install', array($this, 'check_parent_theme_filter'), 10, 3); + + $this->run( array( + 'package' => $package, + 'destination' => get_theme_root(), + 'clear_destination' => false, //Do not overwrite files. + 'clear_working' => true, + 'hook_extra' => array( + 'type' => 'theme', + 'action' => 'install', + ), + ) ); + + remove_filter('upgrader_source_selection', array($this, 'check_package') ); + remove_filter('upgrader_post_install', array($this, 'check_parent_theme_filter')); + + if ( ! $this->result || is_wp_error($this->result) ) + return $this->result; + + // Refresh the Theme Update information + wp_clean_themes_cache( $parsed_args['clear_update_cache'] ); + + return true; + } + + /** + * Upgrade a theme. + * + * @since 2.8.0 + * @since 3.7.0 The `$args` parameter was added, making clearing the update cache optional. + * + * @param string $theme The theme slug. + * @param array $args { + * Optional. Other arguments for upgrading a theme. Default empty array. + * + * @type bool $clear_update_cache Whether to clear the update cache if successful. + * Default true. + * } + * @return bool|WP_Error True if the upgrade was successful, false or a {@see WP_Error} object otherwise. + */ + public function upgrade( $theme, $args = array() ) { + + $defaults = array( + 'clear_update_cache' => true, + ); + $parsed_args = wp_parse_args( $args, $defaults ); + + $this->init(); + $this->upgrade_strings(); + + // Is an update available? + $current = get_site_transient( 'update_themes' ); + if ( !isset( $current->response[ $theme ] ) ) { + $this->skin->before(); + $this->skin->set_result(false); + $this->skin->error( 'up_to_date' ); + $this->skin->after(); + return false; + } + + $r = $current->response[ $theme ]; + + add_filter('upgrader_pre_install', array($this, 'current_before'), 10, 2); + add_filter('upgrader_post_install', array($this, 'current_after'), 10, 2); + add_filter('upgrader_clear_destination', array($this, 'delete_old_theme'), 10, 4); + + $this->run( array( + 'package' => $r['package'], + 'destination' => get_theme_root( $theme ), + 'clear_destination' => true, + 'clear_working' => true, + 'hook_extra' => array( + 'theme' => $theme, + 'type' => 'theme', + 'action' => 'update', + ), + ) ); + + remove_filter('upgrader_pre_install', array($this, 'current_before')); + remove_filter('upgrader_post_install', array($this, 'current_after')); + remove_filter('upgrader_clear_destination', array($this, 'delete_old_theme')); + + if ( ! $this->result || is_wp_error($this->result) ) + return $this->result; + + wp_clean_themes_cache( $parsed_args['clear_update_cache'] ); + + return true; + } + + /** + * Upgrade several themes at once. + * + * @since 3.0.0 + * @since 3.7.0 The `$args` parameter was added, making clearing the update cache optional. + * + * @param string $themes The theme slugs. + * @param array $args { + * Optional. Other arguments for upgrading several themes at once. Default empty array. + * + * @type bool $clear_update_cache Whether to clear the update cache if successful. + * Default true. + * } + * @return array[]|false An array of results, or false if unable to connect to the filesystem. + */ + public function bulk_upgrade( $themes, $args = array() ) { + + $defaults = array( + 'clear_update_cache' => true, + ); + $parsed_args = wp_parse_args( $args, $defaults ); + + $this->init(); + $this->bulk = true; + $this->upgrade_strings(); + + $current = get_site_transient( 'update_themes' ); + + add_filter('upgrader_pre_install', array($this, 'current_before'), 10, 2); + add_filter('upgrader_post_install', array($this, 'current_after'), 10, 2); + add_filter('upgrader_clear_destination', array($this, 'delete_old_theme'), 10, 4); + + $this->skin->header(); + + // Connect to the Filesystem first. + $res = $this->fs_connect( array(WP_CONTENT_DIR) ); + if ( ! $res ) { + $this->skin->footer(); + return false; + } + + $this->skin->bulk_header(); + + // Only start maintenance mode if: + // - running Multisite and there are one or more themes specified, OR + // - a theme with an update available is currently in use. + // @TODO: For multisite, maintenance mode should only kick in for individual sites if at all possible. + $maintenance = ( is_multisite() && ! empty( $themes ) ); + foreach ( $themes as $theme ) + $maintenance = $maintenance || $theme == get_stylesheet() || $theme == get_template(); + if ( $maintenance ) + $this->maintenance_mode(true); + + $results = array(); + + $this->update_count = count($themes); + $this->update_current = 0; + foreach ( $themes as $theme ) { + $this->update_current++; + + $this->skin->theme_info = $this->theme_info($theme); + + if ( !isset( $current->response[ $theme ] ) ) { + $this->skin->set_result(true); + $this->skin->before(); + $this->skin->feedback( 'up_to_date' ); + $this->skin->after(); + $results[$theme] = true; + continue; + } + + // Get the URL to the zip file + $r = $current->response[ $theme ]; + + $result = $this->run( array( + 'package' => $r['package'], + 'destination' => get_theme_root( $theme ), + 'clear_destination' => true, + 'clear_working' => true, + 'is_multi' => true, + 'hook_extra' => array( + 'theme' => $theme + ), + ) ); + + $results[$theme] = $this->result; + + // Prevent credentials auth screen from displaying multiple times + if ( false === $result ) + break; + } //end foreach $plugins + + $this->maintenance_mode(false); + + /** This action is documented in wp-admin/includes/class-wp-upgrader.php */ + do_action( 'upgrader_process_complete', $this, array( + 'action' => 'update', + 'type' => 'theme', + 'bulk' => true, + 'themes' => $themes, + ) ); + + $this->skin->bulk_footer(); + + $this->skin->footer(); + + // Cleanup our hooks, in case something else does a upgrade on this connection. + remove_filter('upgrader_pre_install', array($this, 'current_before')); + remove_filter('upgrader_post_install', array($this, 'current_after')); + remove_filter('upgrader_clear_destination', array($this, 'delete_old_theme')); + + // Refresh the Theme Update information + wp_clean_themes_cache( $parsed_args['clear_update_cache'] ); + + return $results; + } + + /** + * Check that the package source contains a valid theme. + * + * Hooked to the {@see 'upgrader_source_selection'} filter by {@see Theme_Upgrader::install()}. + * It will return an error if the theme doesn't have style.css or index.php + * files. + * + * @since 3.3.0 + * + * @param string $source The full path to the package source. + * @return string|WP_Error The source or a WP_Error. + */ + public function check_package( $source ) { + global $wp_filesystem; + + if ( is_wp_error($source) ) + return $source; + + // Check the folder contains a valid theme + $working_directory = str_replace( $wp_filesystem->wp_content_dir(), trailingslashit(WP_CONTENT_DIR), $source); + if ( ! is_dir($working_directory) ) // Sanity check, if the above fails, let's not prevent installation. + return $source; + + // A proper archive should have a style.css file in the single subdirectory + if ( ! file_exists( $working_directory . 'style.css' ) ) + return new WP_Error( 'incompatible_archive_theme_no_style', $this->strings['incompatible_archive'], __( 'The theme is missing the style.css stylesheet.' ) ); + + $info = get_file_data( $working_directory . 'style.css', array( 'Name' => 'Theme Name', 'Template' => 'Template' ) ); + + if ( empty( $info['Name'] ) ) + return new WP_Error( 'incompatible_archive_theme_no_name', $this->strings['incompatible_archive'], __( "The style.css stylesheet doesn't contain a valid theme header." ) ); + + // If it's not a child theme, it must have at least an index.php to be legit. + if ( empty( $info['Template'] ) && ! file_exists( $working_directory . 'index.php' ) ) + return new WP_Error( 'incompatible_archive_theme_no_index', $this->strings['incompatible_archive'], __( 'The theme is missing the index.php file.' ) ); + + return $source; + } + + /** + * Turn on maintenance mode before attempting to upgrade the current theme. + * + * Hooked to the {@see 'upgrader_pre_install'} filter by {@see Theme_Upgrader::upgrade()} and + * {@see Theme_Upgrader::bulk_upgrade()}. + * + * @since 2.8.0 + */ + public function current_before($return, $theme) { + + if ( is_wp_error($return) ) + return $return; + + $theme = isset($theme['theme']) ? $theme['theme'] : ''; + + if ( $theme != get_stylesheet() ) //If not current + return $return; + //Change to maintenance mode now. + if ( ! $this->bulk ) + $this->maintenance_mode(true); + + return $return; + } + + /** + * Turn off maintenance mode after upgrading the current theme. + * + * Hooked to the {@see 'upgrader_post_install'} filter by {@see Theme_Upgrader::upgrade()} + * and {@see Theme_Upgrader::bulk_upgrade()}. + * + * @since 2.8.0 + */ + public function current_after($return, $theme) { + if ( is_wp_error($return) ) + return $return; + + $theme = isset($theme['theme']) ? $theme['theme'] : ''; + + if ( $theme != get_stylesheet() ) // If not current + return $return; + + // Ensure stylesheet name hasn't changed after the upgrade: + if ( $theme == get_stylesheet() && $theme != $this->result['destination_name'] ) { + wp_clean_themes_cache(); + $stylesheet = $this->result['destination_name']; + switch_theme( $stylesheet ); + } + + //Time to remove maintenance mode + if ( ! $this->bulk ) + $this->maintenance_mode(false); + return $return; + } + + /** + * Delete the old theme during an upgrade. + * + * Hooked to the {@see 'upgrader_clear_destination'} filter by {@see Theme_Upgrader::upgrade()} + * and {@see Theme_Upgrader::bulk_upgrade()}. + * + * @since 2.8.0 + */ + public function delete_old_theme( $removed, $local_destination, $remote_destination, $theme ) { + global $wp_filesystem; + + if ( is_wp_error( $removed ) ) + return $removed; // Pass errors through. + + if ( ! isset( $theme['theme'] ) ) + return $removed; + + $theme = $theme['theme']; + $themes_dir = trailingslashit( $wp_filesystem->wp_themes_dir( $theme ) ); + if ( $wp_filesystem->exists( $themes_dir . $theme ) ) { + if ( ! $wp_filesystem->delete( $themes_dir . $theme, true ) ) + return false; + } + + return true; + } + + /** + * Get the WP_Theme object for a theme. + * + * @since 2.8.0 + * @since 3.0.0 The `$theme` argument was added. + * + * @param string $theme The directory name of the theme. This is optional, and if not supplied, + * the directory name from the last result will be used. + * @return WP_Theme|false The theme's info object, or false `$theme` is not supplied + * and the last result isn't set. + */ + public function theme_info($theme = null) { + + if ( empty($theme) ) { + if ( !empty($this->result['destination_name']) ) + $theme = $this->result['destination_name']; + else + return false; + } + return wp_get_theme( $theme ); + } + +} + +add_action( 'upgrader_process_complete', array( 'Language_Pack_Upgrader', 'async_upgrade' ), 20 ); + +/** + * Language pack upgrader, for updating translations of plugins, themes, and core. + * + * @package WordPress + * @subpackage Upgrader + * @since 3.7.0 + */ +class Language_Pack_Upgrader extends WP_Upgrader { + + /** + * Result of the language pack upgrade. + * + * @since 3.7.0 + * @var array|WP_Error $result + * @see WP_Upgrader::$result + */ + public $result; + + /** + * Whether a bulk upgrade/install is being performed. + * + * @since 3.7.0 + * @var bool $bulk + */ + public $bulk = true; + + /** + * Asynchronously upgrade language packs after other upgrades have been made. + * + * Hooked to the {@see 'upgrader_process_complete'} action by default. + * + * @since 3.7.0 + */ + public static function async_upgrade( $upgrader = false ) { + // Avoid recursion. + if ( $upgrader && $upgrader instanceof Language_Pack_Upgrader ) { + return; + } + + // Nothing to do? + $language_updates = wp_get_translation_updates(); + if ( ! $language_updates ) { + return; + } + + // Avoid messing with VCS installs, at least for now. + // Noted: this is not the ideal way to accomplish this. + $check_vcs = new WP_Automatic_Updater; + if ( $check_vcs->is_vcs_checkout( WP_CONTENT_DIR ) ) { + return; + } + + foreach ( $language_updates as $key => $language_update ) { + $update = ! empty( $language_update->autoupdate ); + + /** + * Filter whether to asynchronously update translation for core, a plugin, or a theme. + * + * @since 4.0.0 + * + * @param bool $update Whether to update. + * @param object $language_update The update offer. + */ + $update = apply_filters( 'async_update_translation', $update, $language_update ); + + if ( ! $update ) { + unset( $language_updates[ $key ] ); + } + } + + if ( empty( $language_updates ) ) { + return; + } + + $skin = new Language_Pack_Upgrader_Skin( array( + 'skip_header_footer' => true, + ) ); + + $lp_upgrader = new Language_Pack_Upgrader( $skin ); + $lp_upgrader->bulk_upgrade( $language_updates ); + } + + /** + * Initialize the upgrade strings. + * + * @since 3.7.0 + */ + public function upgrade_strings() { + $this->strings['starting_upgrade'] = __( 'Some of your translations need updating. Sit tight for a few more seconds while we update them as well.' ); + $this->strings['up_to_date'] = __( 'The translation is up to date.' ); // We need to silently skip this case + $this->strings['no_package'] = __( 'Update package not available.' ); + $this->strings['downloading_package'] = __( 'Downloading translation from %s…' ); + $this->strings['unpack_package'] = __( 'Unpacking the update…' ); + $this->strings['process_failed'] = __( 'Translation update failed.' ); + $this->strings['process_success'] = __( 'Translation updated successfully.' ); + } + + /** + * Upgrade a language pack. + * + * @since 3.7.0 + * + * @param string|false $update Optional. Whether an update offer is available. Default false. + * @param array $args Optional. Other optional arguments, see + * {@see Language_Pack_Upgrader::bulk_upgrade()}. Default empty array. + * @return array|WP_Error The result of the upgrade, or a {@see wP_Error} object instead. + */ + public function upgrade( $update = false, $args = array() ) { + if ( $update ) { + $update = array( $update ); + } + + $results = $this->bulk_upgrade( $update, $args ); + + if ( ! is_array( $results ) ) { + return $results; + } + + return $results[0]; + } + + /** + * Bulk upgrade language packs. + * + * @since 3.7.0 + * + * @param array $language_updates Optional. Language pack updates. Default empty array. + * @param array $args { + * Optional. Other arguments for upgrading multiple language packs. Default empty array + * + * @type bool $clear_update_cache Whether to clear the update cache when done. + * Default true. + * } + * @return array|true|false|WP_Error Will return an array of results, or true if there are no updates, + * false or WP_Error for initial errors. + */ + public function bulk_upgrade( $language_updates = array(), $args = array() ) { + global $wp_filesystem; + + $defaults = array( + 'clear_update_cache' => true, + ); + $parsed_args = wp_parse_args( $args, $defaults ); + + $this->init(); + $this->upgrade_strings(); + + if ( ! $language_updates ) + $language_updates = wp_get_translation_updates(); + + if ( empty( $language_updates ) ) { + $this->skin->header(); + $this->skin->before(); + $this->skin->set_result( true ); + $this->skin->feedback( 'up_to_date' ); + $this->skin->after(); + $this->skin->bulk_footer(); + $this->skin->footer(); + return true; + } + + if ( 'upgrader_process_complete' == current_filter() ) + $this->skin->feedback( 'starting_upgrade' ); + + // Remove any existing upgrade filters from the plugin/theme upgraders #WP29425 & #WP29230 + remove_all_filters( 'upgrader_pre_install' ); + remove_all_filters( 'upgrader_clear_destination' ); + remove_all_filterS( 'upgrader_post_install' ); + remove_all_filters( 'upgrader_source_selection' ); + + add_filter( 'upgrader_source_selection', array( $this, 'check_package' ), 10, 2 ); + + $this->skin->header(); + + // Connect to the Filesystem first. + $res = $this->fs_connect( array( WP_CONTENT_DIR, WP_LANG_DIR ) ); + if ( ! $res ) { + $this->skin->footer(); + return false; + } + + $results = array(); + + $this->update_count = count( $language_updates ); + $this->update_current = 0; + + /* + * The filesystem's mkdir() is not recursive. Make sure WP_LANG_DIR exists, + * as we then may need to create a /plugins or /themes directory inside of it. + */ + $remote_destination = $wp_filesystem->find_folder( WP_LANG_DIR ); + if ( ! $wp_filesystem->exists( $remote_destination ) ) + if ( ! $wp_filesystem->mkdir( $remote_destination, FS_CHMOD_DIR ) ) + return new WP_Error( 'mkdir_failed_lang_dir', $this->strings['mkdir_failed'], $remote_destination ); + + foreach ( $language_updates as $language_update ) { + + $this->skin->language_update = $language_update; + + $destination = WP_LANG_DIR; + if ( 'plugin' == $language_update->type ) + $destination .= '/plugins'; + elseif ( 'theme' == $language_update->type ) + $destination .= '/themes'; + + $this->update_current++; + + $options = array( + 'package' => $language_update->package, + 'destination' => $destination, + 'clear_destination' => false, + 'abort_if_destination_exists' => false, // We expect the destination to exist. + 'clear_working' => true, + 'is_multi' => true, + 'hook_extra' => array( + 'language_update_type' => $language_update->type, + 'language_update' => $language_update, + ) + ); + + $result = $this->run( $options ); + + $results[] = $this->result; + + // Prevent credentials auth screen from displaying multiple times. + if ( false === $result ) + break; + } + + $this->skin->bulk_footer(); + + $this->skin->footer(); + + // Clean up our hooks, in case something else does an upgrade on this connection. + remove_filter( 'upgrader_source_selection', array( $this, 'check_package' ) ); + + if ( $parsed_args['clear_update_cache'] ) { + wp_clean_update_cache(); + } + + return $results; + } + + /** + * Check the package source to make sure there are .mo and .po files. + * + * Hooked to the {@see 'upgrader_source_selection'} filter by + * {@see Language_Pack_Upgrader::bulk_upgrade()}. + * + * @since 3.7.0 + */ + public function check_package( $source, $remote_source ) { + global $wp_filesystem; + + if ( is_wp_error( $source ) ) + return $source; + + // Check that the folder contains a valid language. + $files = $wp_filesystem->dirlist( $remote_source ); + + // Check to see if a .po and .mo exist in the folder. + $po = $mo = false; + foreach ( (array) $files as $file => $filedata ) { + if ( '.po' == substr( $file, -3 ) ) + $po = true; + elseif ( '.mo' == substr( $file, -3 ) ) + $mo = true; + } + + if ( ! $mo || ! $po ) + return new WP_Error( 'incompatible_archive_pomo', $this->strings['incompatible_archive'], + __( 'The language pack is missing either the .po or .mo files.' ) ); + + return $source; + } + + /** + * Get the name of an item being updated. + * + * @since 3.7.0 + * + * @param object The data for an update. + * @return string The name of the item being updated. + */ + public function get_name_for_update( $update ) { + switch ( $update->type ) { + case 'core': + return 'WordPress'; // Not translated + break; + case 'theme': + $theme = wp_get_theme( $update->slug ); + if ( $theme->exists() ) + return $theme->Get( 'Name' ); + break; + case 'plugin': + $plugin_data = get_plugins( '/' . $update->slug ); + $plugin_data = array_shift( $plugin_data ); + if ( $plugin_data ) + return $plugin_data['Name']; + break; + } + return ''; + } + +} + +/** + * Core Upgrader class for WordPress. It allows for WordPress to upgrade itself in combination with the wp-admin/includes/update-core.php file + * + * @package WordPress + * @subpackage Upgrader + * @since 2.8.0 + */ +class Core_Upgrader extends WP_Upgrader { + + /** + * Initialize the upgrade strings. + * + * @since 2.8.0 + */ + public function upgrade_strings() { + $this->strings['up_to_date'] = __('WordPress is at the latest version.'); + $this->strings['no_package'] = __('Update package not available.'); + $this->strings['downloading_package'] = __('Downloading update from %s…'); + $this->strings['unpack_package'] = __('Unpacking the update…'); + $this->strings['copy_failed'] = __('Could not copy files.'); + $this->strings['copy_failed_space'] = __('Could not copy files. You may have run out of disk space.' ); + $this->strings['start_rollback'] = __( 'Attempting to roll back to previous version.' ); + $this->strings['rollback_was_required'] = __( 'Due to an error during updating, WordPress has rolled back to your previous version.' ); + } + + /** + * Upgrade WordPress core. + * + * @since 2.8.0 + * + * @param object $current Response object for whether WordPress is current. + * @param array $args { + * Optional. Arguments for upgrading WordPress core. Default empty array. + * + * @type bool $pre_check_md5 Whether to check the file checksums before + * attempting the upgrade. Default true. + * @type bool $attempt_rollback Whether to attempt to rollback the chances if + * there is a problem. Default false. + * @type bool $do_rollback Whether to perform this "upgrade" as a rollback. + * Default false. + * } + * @return null|false|WP_Error False or WP_Error on failure, null on success. + */ + public function upgrade( $current, $args = array() ) { + global $wp_filesystem; + + include( ABSPATH . WPINC . '/version.php' ); // $wp_version; + + $start_time = time(); + + $defaults = array( + 'pre_check_md5' => true, + 'attempt_rollback' => false, + 'do_rollback' => false, + 'allow_relaxed_file_ownership' => false, + ); + $parsed_args = wp_parse_args( $args, $defaults ); + + $this->init(); + $this->upgrade_strings(); + + // Is an update available? + if ( !isset( $current->response ) || $current->response == 'latest' ) + return new WP_Error('up_to_date', $this->strings['up_to_date']); + + $res = $this->fs_connect( array( ABSPATH, WP_CONTENT_DIR ), $parsed_args['allow_relaxed_file_ownership'] ); + if ( ! $res || is_wp_error( $res ) ) { + return $res; + } + + $wp_dir = trailingslashit($wp_filesystem->abspath()); + + $partial = true; + if ( $parsed_args['do_rollback'] ) + $partial = false; + elseif ( $parsed_args['pre_check_md5'] && ! $this->check_files() ) + $partial = false; + + /* + * If partial update is returned from the API, use that, unless we're doing + * a reinstall. If we cross the new_bundled version number, then use + * the new_bundled zip. Don't though if the constant is set to skip bundled items. + * If the API returns a no_content zip, go with it. Finally, default to the full zip. + */ + if ( $parsed_args['do_rollback'] && $current->packages->rollback ) + $to_download = 'rollback'; + elseif ( $current->packages->partial && 'reinstall' != $current->response && $wp_version == $current->partial_version && $partial ) + $to_download = 'partial'; + elseif ( $current->packages->new_bundled && version_compare( $wp_version, $current->new_bundled, '<' ) + && ( ! defined( 'CORE_UPGRADE_SKIP_NEW_BUNDLED' ) || ! CORE_UPGRADE_SKIP_NEW_BUNDLED ) ) + $to_download = 'new_bundled'; + elseif ( $current->packages->no_content ) + $to_download = 'no_content'; + else + $to_download = 'full'; + + $download = $this->download_package( $current->packages->$to_download ); + if ( is_wp_error($download) ) + return $download; + + $working_dir = $this->unpack_package( $download ); + if ( is_wp_error($working_dir) ) + return $working_dir; + + // Copy update-core.php from the new version into place. + if ( !$wp_filesystem->copy($working_dir . '/wordpress/wp-admin/includes/update-core.php', $wp_dir . 'wp-admin/includes/update-core.php', true) ) { + $wp_filesystem->delete($working_dir, true); + return new WP_Error( 'copy_failed_for_update_core_file', __( 'The update cannot be installed because we will be unable to copy some files. This is usually due to inconsistent file permissions.' ), 'wp-admin/includes/update-core.php' ); + } + $wp_filesystem->chmod($wp_dir . 'wp-admin/includes/update-core.php', FS_CHMOD_FILE); + + require_once( ABSPATH . 'wp-admin/includes/update-core.php' ); + + if ( ! function_exists( 'update_core' ) ) + return new WP_Error( 'copy_failed_space', $this->strings['copy_failed_space'] ); + + $result = update_core( $working_dir, $wp_dir ); + + // In the event of an issue, we may be able to roll back. + if ( $parsed_args['attempt_rollback'] && $current->packages->rollback && ! $parsed_args['do_rollback'] ) { + $try_rollback = false; + if ( is_wp_error( $result ) ) { + $error_code = $result->get_error_code(); + /* + * Not all errors are equal. These codes are critical: copy_failed__copy_dir, + * mkdir_failed__copy_dir, copy_failed__copy_dir_retry, and disk_full. + * do_rollback allows for update_core() to trigger a rollback if needed. + */ + if ( false !== strpos( $error_code, 'do_rollback' ) ) + $try_rollback = true; + elseif ( false !== strpos( $error_code, '__copy_dir' ) ) + $try_rollback = true; + elseif ( 'disk_full' === $error_code ) + $try_rollback = true; + } + + if ( $try_rollback ) { + /** This filter is documented in wp-admin/includes/update-core.php */ + apply_filters( 'update_feedback', $result ); + + /** This filter is documented in wp-admin/includes/update-core.php */ + apply_filters( 'update_feedback', $this->strings['start_rollback'] ); + + $rollback_result = $this->upgrade( $current, array_merge( $parsed_args, array( 'do_rollback' => true ) ) ); + + $original_result = $result; + $result = new WP_Error( 'rollback_was_required', $this->strings['rollback_was_required'], (object) array( 'update' => $original_result, 'rollback' => $rollback_result ) ); + } + } + + /** This action is documented in wp-admin/includes/class-wp-upgrader.php */ + do_action( 'upgrader_process_complete', $this, array( 'action' => 'update', 'type' => 'core' ) ); + + // Clear the current updates + delete_site_transient( 'update_core' ); + + if ( ! $parsed_args['do_rollback'] ) { + $stats = array( + 'update_type' => $current->response, + 'success' => true, + 'fs_method' => $wp_filesystem->method, + 'fs_method_forced' => defined( 'FS_METHOD' ) || has_filter( 'filesystem_method' ), + 'fs_method_direct' => !empty( $GLOBALS['_wp_filesystem_direct_method'] ) ? $GLOBALS['_wp_filesystem_direct_method'] : '', + 'time_taken' => time() - $start_time, + 'reported' => $wp_version, + 'attempted' => $current->version, + ); + + if ( is_wp_error( $result ) ) { + $stats['success'] = false; + // Did a rollback occur? + if ( ! empty( $try_rollback ) ) { + $stats['error_code'] = $original_result->get_error_code(); + $stats['error_data'] = $original_result->get_error_data(); + // Was the rollback successful? If not, collect its error too. + $stats['rollback'] = ! is_wp_error( $rollback_result ); + if ( is_wp_error( $rollback_result ) ) { + $stats['rollback_code'] = $rollback_result->get_error_code(); + $stats['rollback_data'] = $rollback_result->get_error_data(); + } + } else { + $stats['error_code'] = $result->get_error_code(); + $stats['error_data'] = $result->get_error_data(); + } + } + + wp_version_check( $stats ); + } + + return $result; + } + + /** + * Determines if this WordPress Core version should update to an offered version or not. + * + * @since 3.7.0 + * + * @param string $offered_ver The offered version, of the format x.y.z. + * @return bool True if we should update to the offered version, otherwise false. + */ + public static function should_update_to_version( $offered_ver ) { + include( ABSPATH . WPINC . '/version.php' ); // $wp_version; // x.y.z + + $current_branch = implode( '.', array_slice( preg_split( '/[.-]/', $wp_version ), 0, 2 ) ); // x.y + $new_branch = implode( '.', array_slice( preg_split( '/[.-]/', $offered_ver ), 0, 2 ) ); // x.y + $current_is_development_version = (bool) strpos( $wp_version, '-' ); + + // Defaults: + $upgrade_dev = true; + $upgrade_minor = true; + $upgrade_major = false; + + // WP_AUTO_UPDATE_CORE = true (all), 'minor', false. + if ( defined( 'WP_AUTO_UPDATE_CORE' ) ) { + if ( false === WP_AUTO_UPDATE_CORE ) { + // Defaults to turned off, unless a filter allows it + $upgrade_dev = $upgrade_minor = $upgrade_major = false; + } elseif ( true === WP_AUTO_UPDATE_CORE ) { + // ALL updates for core + $upgrade_dev = $upgrade_minor = $upgrade_major = true; + } elseif ( 'minor' === WP_AUTO_UPDATE_CORE ) { + // Only minor updates for core + $upgrade_dev = $upgrade_major = false; + $upgrade_minor = true; + } + } + + // 1: If we're already on that version, not much point in updating? + if ( $offered_ver == $wp_version ) + return false; + + // 2: If we're running a newer version, that's a nope + if ( version_compare( $wp_version, $offered_ver, '>' ) ) + return false; + + $failure_data = get_site_option( 'auto_core_update_failed' ); + if ( $failure_data ) { + // If this was a critical update failure, cannot update. + if ( ! empty( $failure_data['critical'] ) ) + return false; + + // Don't claim we can update on update-core.php if we have a non-critical failure logged. + if ( $wp_version == $failure_data['current'] && false !== strpos( $offered_ver, '.1.next.minor' ) ) + return false; + + // Cannot update if we're retrying the same A to B update that caused a non-critical failure. + // Some non-critical failures do allow retries, like download_failed. + // 3.7.1 => 3.7.2 resulted in files_not_writable, if we are still on 3.7.1 and still trying to update to 3.7.2. + if ( empty( $failure_data['retry'] ) && $wp_version == $failure_data['current'] && $offered_ver == $failure_data['attempted'] ) + return false; + } + + // 3: 3.7-alpha-25000 -> 3.7-alpha-25678 -> 3.7-beta1 -> 3.7-beta2 + if ( $current_is_development_version ) { + + /** + * Filter whether to enable automatic core updates for development versions. + * + * @since 3.7.0 + * + * @param bool $upgrade_dev Whether to enable automatic updates for + * development versions. + */ + if ( ! apply_filters( 'allow_dev_auto_core_updates', $upgrade_dev ) ) + return false; + // Else fall through to minor + major branches below. + } + + // 4: Minor In-branch updates (3.7.0 -> 3.7.1 -> 3.7.2 -> 3.7.4) + if ( $current_branch == $new_branch ) { + + /** + * Filter whether to enable minor automatic core updates. + * + * @since 3.7.0 + * + * @param bool $upgrade_minor Whether to enable minor automatic core updates. + */ + return apply_filters( 'allow_minor_auto_core_updates', $upgrade_minor ); + } + + // 5: Major version updates (3.7.0 -> 3.8.0 -> 3.9.1) + if ( version_compare( $new_branch, $current_branch, '>' ) ) { + + /** + * Filter whether to enable major automatic core updates. + * + * @since 3.7.0 + * + * @param bool $upgrade_major Whether to enable major automatic core updates. + */ + return apply_filters( 'allow_major_auto_core_updates', $upgrade_major ); + } + + // If we're not sure, we don't want it + return false; + } + + /** + * Compare the disk file checksums agains the expected checksums. + * + * @since 3.7.0 + * + * @return bool True if the checksums match, otherwise false. + */ + public function check_files() { + global $wp_version, $wp_local_package; + + $checksums = get_core_checksums( $wp_version, isset( $wp_local_package ) ? $wp_local_package : 'en_US' ); + + if ( ! is_array( $checksums ) ) + return false; + + foreach ( $checksums as $file => $checksum ) { + // Skip files which get updated + if ( 'wp-content' == substr( $file, 0, 10 ) ) + continue; + if ( ! file_exists( ABSPATH . $file ) || md5_file( ABSPATH . $file ) !== $checksum ) + return false; + } + + return true; + } +} + +/** + * Upgrade Skin helper for File uploads. This class handles the upload process and passes it as if it's a local file to the Upgrade/Installer functions. + * + * @package WordPress + * @subpackage Upgrader + * @since 2.8.0 + */ +class File_Upload_Upgrader { + + /** + * The full path to the file package. + * + * @since 2.8.0 + * @var string $package + */ + public $package; + + /** + * The name of the file. + * + * @since 2.8.0 + * @var string $filename + */ + public $filename; + + /** + * The ID of the attachment post for this file. + * + * @since 3.3.0 + * @var int $id + */ + public $id = 0; + + /** + * Construct the upgrader for a form. + * + * @since 2.8.0 + * + * @param string $form The name of the form the file was uploaded from. + * @param string $urlholder The name of the `GET` parameter that holds the filename. + */ + public function __construct( $form, $urlholder ) { + + if ( empty($_FILES[$form]['name']) && empty($_GET[$urlholder]) ) + wp_die(__('Please select a file')); + + //Handle a newly uploaded file, Else assume it's already been uploaded + if ( ! empty($_FILES) ) { + $overrides = array( 'test_form' => false, 'test_type' => false ); + $file = wp_handle_upload( $_FILES[$form], $overrides ); + + if ( isset( $file['error'] ) ) + wp_die( $file['error'] ); + + $this->filename = $_FILES[$form]['name']; + $this->package = $file['file']; + + // Construct the object array + $object = array( + 'post_title' => $this->filename, + 'post_content' => $file['url'], + 'post_mime_type' => $file['type'], + 'guid' => $file['url'], + 'context' => 'upgrader', + 'post_status' => 'private' + ); + + // Save the data. + $this->id = wp_insert_attachment( $object, $file['file'] ); + + // Schedule a cleanup for 2 hours from now in case of failed install. + wp_schedule_single_event( time() + 2 * HOUR_IN_SECONDS, 'upgrader_scheduled_cleanup', array( $this->id ) ); + + } elseif ( is_numeric( $_GET[$urlholder] ) ) { + // Numeric Package = previously uploaded file, see above. + $this->id = (int) $_GET[$urlholder]; + $attachment = get_post( $this->id ); + if ( empty($attachment) ) + wp_die(__('Please select a file')); + + $this->filename = $attachment->post_title; + $this->package = get_attached_file( $attachment->ID ); + } else { + // Else, It's set to something, Back compat for plugins using the old (pre-3.3) File_Uploader handler. + if ( ! ( ( $uploads = wp_upload_dir() ) && false === $uploads['error'] ) ) + wp_die( $uploads['error'] ); + + $this->filename = $_GET[$urlholder]; + $this->package = $uploads['basedir'] . '/' . $this->filename; + } + } + + /** + * Delete the attachment/uploaded file. + * + * @since 3.2.2 + * + * @return bool Whether the cleanup was successful. + */ + public function cleanup() { + if ( $this->id ) + wp_delete_attachment( $this->id ); + + elseif ( file_exists( $this->package ) ) + return @unlink( $this->package ); + + return true; + } +} + +/** + * The WordPress automatic background updater. + * + * @package WordPress + * @subpackage Upgrader + * @since 3.7.0 + */ +class WP_Automatic_Updater { + + /** + * Tracks update results during processing. + * + * @var array + */ + protected $update_results = array(); + + /** + * Whether the entire automatic updater is disabled. + * + * @since 3.7.0 + */ + public function is_disabled() { + // Background updates are disabled if you don't want file changes. + if ( defined( 'DISALLOW_FILE_MODS' ) && DISALLOW_FILE_MODS ) + return true; + + if ( defined( 'WP_INSTALLING' ) ) + return true; + + // More fine grained control can be done through the WP_AUTO_UPDATE_CORE constant and filters. + $disabled = defined( 'AUTOMATIC_UPDATER_DISABLED' ) && AUTOMATIC_UPDATER_DISABLED; + + /** + * Filter whether to entirely disable background updates. + * + * There are more fine-grained filters and controls for selective disabling. + * This filter parallels the AUTOMATIC_UPDATER_DISABLED constant in name. + * + * This also disables update notification emails. That may change in the future. + * + * @since 3.7.0 + * + * @param bool $disabled Whether the updater should be disabled. + */ + return apply_filters( 'automatic_updater_disabled', $disabled ); + } + + /** + * Check for version control checkouts. + * + * Checks for Subversion, Git, Mercurial, and Bazaar. It recursively looks up the + * filesystem to the top of the drive, erring on the side of detecting a VCS + * checkout somewhere. + * + * ABSPATH is always checked in addition to whatever $context is (which may be the + * wp-content directory, for example). The underlying assumption is that if you are + * using version control *anywhere*, then you should be making decisions for + * how things get updated. + * + * @since 3.7.0 + * + * @param string $context The filesystem path to check, in addition to ABSPATH. + */ + public function is_vcs_checkout( $context ) { + $context_dirs = array( untrailingslashit( $context ) ); + if ( $context !== ABSPATH ) + $context_dirs[] = untrailingslashit( ABSPATH ); + + $vcs_dirs = array( '.svn', '.git', '.hg', '.bzr' ); + $check_dirs = array(); + + foreach ( $context_dirs as $context_dir ) { + // Walk up from $context_dir to the root. + do { + $check_dirs[] = $context_dir; + + // Once we've hit '/' or 'C:\', we need to stop. dirname will keep returning the input here. + if ( $context_dir == dirname( $context_dir ) ) + break; + + // Continue one level at a time. + } while ( $context_dir = dirname( $context_dir ) ); + } + + $check_dirs = array_unique( $check_dirs ); + + // Search all directories we've found for evidence of version control. + foreach ( $vcs_dirs as $vcs_dir ) { + foreach ( $check_dirs as $check_dir ) { + if ( $checkout = @is_dir( rtrim( $check_dir, '\\/' ) . "/$vcs_dir" ) ) + break 2; + } + } + + /** + * Filter whether the automatic updater should consider a filesystem + * location to be potentially managed by a version control system. + * + * @since 3.7.0 + * + * @param bool $checkout Whether a VCS checkout was discovered at $context + * or ABSPATH, or anywhere higher. + * @param string $context The filesystem context (a path) against which + * filesystem status should be checked. + */ + return apply_filters( 'automatic_updates_is_vcs_checkout', $checkout, $context ); + } + + /** + * Tests to see if we can and should update a specific item. + * + * @since 3.7.0 + * + * @param string $type The type of update being checked: 'core', 'theme', + * 'plugin', 'translation'. + * @param object $item The update offer. + * @param string $context The filesystem context (a path) against which filesystem + * access and status should be checked. + */ + public function should_update( $type, $item, $context ) { + // Used to see if WP_Filesystem is set up to allow unattended updates. + $skin = new Automatic_Upgrader_Skin; + + if ( $this->is_disabled() ) + return false; + + // Only relax the filesystem checks when the update doesn't include new files + $allow_relaxed_file_ownership = false; + if ( 'core' == $type && isset( $item->new_files ) && ! $item->new_files ) { + $allow_relaxed_file_ownership = true; + } + + // If we can't do an auto core update, we may still be able to email the user. + if ( ! $skin->request_filesystem_credentials( false, $context, $allow_relaxed_file_ownership ) || $this->is_vcs_checkout( $context ) ) { + if ( 'core' == $type ) + $this->send_core_update_notification_email( $item ); + return false; + } + + // Next up, is this an item we can update? + if ( 'core' == $type ) + $update = Core_Upgrader::should_update_to_version( $item->current ); + else + $update = ! empty( $item->autoupdate ); + + /** + * Filter whether to automatically update core, a plugin, a theme, or a language. + * + * The dynamic portion of the hook name, `$type`, refers to the type of update + * being checked. Can be 'core', 'theme', 'plugin', or 'translation'. + * + * Generally speaking, plugins, themes, and major core versions are not updated + * by default, while translations and minor and development versions for core + * are updated by default. + * + * See the {@see 'allow_dev_auto_core_updates', {@see 'allow_minor_auto_core_updates'}, + * and {@see 'allow_major_auto_core_updates'} filters for a more straightforward way to + * adjust core updates. + * + * @since 3.7.0 + * + * @param bool $update Whether to update. + * @param object $item The update offer. + */ + $update = apply_filters( 'auto_update_' . $type, $update, $item ); + + if ( ! $update ) { + if ( 'core' == $type ) + $this->send_core_update_notification_email( $item ); + return false; + } + + // If it's a core update, are we actually compatible with its requirements? + if ( 'core' == $type ) { + global $wpdb; + + $php_compat = version_compare( phpversion(), $item->php_version, '>=' ); + if ( file_exists( WP_CONTENT_DIR . '/db.php' ) && empty( $wpdb->is_mysql ) ) + $mysql_compat = true; + else + $mysql_compat = version_compare( $wpdb->db_version(), $item->mysql_version, '>=' ); + + if ( ! $php_compat || ! $mysql_compat ) + return false; + } + + return true; + } + + /** + * Notifies an administrator of a core update. + * + * @since 3.7.0 + * + * @param object $item The update offer. + */ + protected function send_core_update_notification_email( $item ) { + $notified = get_site_option( 'auto_core_update_notified' ); + + // Don't notify if we've already notified the same email address of the same version. + if ( $notified && $notified['email'] == get_site_option( 'admin_email' ) && $notified['version'] == $item->current ) + return false; + + // See if we need to notify users of a core update. + $notify = ! empty( $item->notify_email ); + + /** + * Filter whether to notify the site administrator of a new core update. + * + * By default, administrators are notified when the update offer received + * from WordPress.org sets a particular flag. This allows some discretion + * in if and when to notify. + * + * This filter is only evaluated once per release. If the same email address + * was already notified of the same new version, WordPress won't repeatedly + * email the administrator. + * + * This filter is also used on about.php to check if a plugin has disabled + * these notifications. + * + * @since 3.7.0 + * + * @param bool $notify Whether the site administrator is notified. + * @param object $item The update offer. + */ + if ( ! apply_filters( 'send_core_update_notification_email', $notify, $item ) ) + return false; + + $this->send_email( 'manual', $item ); + return true; + } + + /** + * Update an item, if appropriate. + * + * @since 3.7.0 + * + * @param string $type The type of update being checked: 'core', 'theme', 'plugin', 'translation'. + * @param object $item The update offer. + */ + public function update( $type, $item ) { + $skin = new Automatic_Upgrader_Skin; + + switch ( $type ) { + case 'core': + // The Core upgrader doesn't use the Upgrader's skin during the actual main part of the upgrade, instead, firing a filter. + add_filter( 'update_feedback', array( $skin, 'feedback' ) ); + $upgrader = new Core_Upgrader( $skin ); + $context = ABSPATH; + break; + case 'plugin': + $upgrader = new Plugin_Upgrader( $skin ); + $context = WP_PLUGIN_DIR; // We don't support custom Plugin directories, or updates for WPMU_PLUGIN_DIR + break; + case 'theme': + $upgrader = new Theme_Upgrader( $skin ); + $context = get_theme_root( $item->theme ); + break; + case 'translation': + $upgrader = new Language_Pack_Upgrader( $skin ); + $context = WP_CONTENT_DIR; // WP_LANG_DIR; + break; + } + + // Determine whether we can and should perform this update. + if ( ! $this->should_update( $type, $item, $context ) ) + return false; + + $upgrader_item = $item; + switch ( $type ) { + case 'core': + $skin->feedback( __( 'Updating to WordPress %s' ), $item->version ); + $item_name = sprintf( __( 'WordPress %s' ), $item->version ); + break; + case 'theme': + $upgrader_item = $item->theme; + $theme = wp_get_theme( $upgrader_item ); + $item_name = $theme->Get( 'Name' ); + $skin->feedback( __( 'Updating theme: %s' ), $item_name ); + break; + case 'plugin': + $upgrader_item = $item->plugin; + $plugin_data = get_plugin_data( $context . '/' . $upgrader_item ); + $item_name = $plugin_data['Name']; + $skin->feedback( __( 'Updating plugin: %s' ), $item_name ); + break; + case 'translation': + $language_item_name = $upgrader->get_name_for_update( $item ); + $item_name = sprintf( __( 'Translations for %s' ), $language_item_name ); + $skin->feedback( sprintf( __( 'Updating translations for %1$s (%2$s)…' ), $language_item_name, $item->language ) ); + break; + } + + $allow_relaxed_file_ownership = false; + if ( 'core' == $type && isset( $item->new_files ) && ! $item->new_files ) { + $allow_relaxed_file_ownership = true; + } + + // Boom, This sites about to get a whole new splash of paint! + $upgrade_result = $upgrader->upgrade( $upgrader_item, array( + 'clear_update_cache' => false, + // Always use partial builds if possible for core updates. + 'pre_check_md5' => false, + // Only available for core updates. + 'attempt_rollback' => true, + // Allow relaxed file ownership in some scenarios + 'allow_relaxed_file_ownership' => $allow_relaxed_file_ownership, + ) ); + + // If the filesystem is unavailable, false is returned. + if ( false === $upgrade_result ) { + $upgrade_result = new WP_Error( 'fs_unavailable', __( 'Could not access filesystem.' ) ); + } + + // Core doesn't output this, so let's append it so we don't get confused. + if ( 'core' == $type ) { + if ( is_wp_error( $upgrade_result ) ) { + $skin->error( __( 'Installation Failed' ), $upgrade_result ); + } else { + $skin->feedback( __( 'WordPress updated successfully' ) ); + } + } + + $this->update_results[ $type ][] = (object) array( + 'item' => $item, + 'result' => $upgrade_result, + 'name' => $item_name, + 'messages' => $skin->get_upgrade_messages() + ); + + return $upgrade_result; + } + + /** + * Kicks off the background update process, looping through all pending updates. + * + * @since 3.7.0 + */ + public function run() { + global $wpdb, $wp_version; + + if ( $this->is_disabled() ) + return; + + if ( ! is_main_network() || ! is_main_site() ) + return; + + $lock_name = 'auto_updater.lock'; + + // Try to lock + $lock_result = $wpdb->query( $wpdb->prepare( "INSERT IGNORE INTO `$wpdb->options` ( `option_name`, `option_value`, `autoload` ) VALUES (%s, %s, 'no') /* LOCK */", $lock_name, time() ) ); + + if ( ! $lock_result ) { + $lock_result = get_option( $lock_name ); + + // If we couldn't create a lock, and there isn't a lock, bail + if ( ! $lock_result ) + return; + + // Check to see if the lock is still valid + if ( $lock_result > ( time() - HOUR_IN_SECONDS ) ) + return; + } + + // Update the lock, as by this point we've definitely got a lock, just need to fire the actions + update_option( $lock_name, time() ); + + // Don't automatically run these thins, as we'll handle it ourselves + remove_action( 'upgrader_process_complete', array( 'Language_Pack_Upgrader', 'async_upgrade' ), 20 ); + remove_action( 'upgrader_process_complete', 'wp_version_check' ); + remove_action( 'upgrader_process_complete', 'wp_update_plugins' ); + remove_action( 'upgrader_process_complete', 'wp_update_themes' ); + + // Next, Plugins + wp_update_plugins(); // Check for Plugin updates + $plugin_updates = get_site_transient( 'update_plugins' ); + if ( $plugin_updates && !empty( $plugin_updates->response ) ) { + foreach ( $plugin_updates->response as $plugin ) { + $this->update( 'plugin', $plugin ); + } + // Force refresh of plugin update information + wp_clean_plugins_cache(); + } + + // Next, those themes we all love + wp_update_themes(); // Check for Theme updates + $theme_updates = get_site_transient( 'update_themes' ); + if ( $theme_updates && !empty( $theme_updates->response ) ) { + foreach ( $theme_updates->response as $theme ) { + $this->update( 'theme', (object) $theme ); + } + // Force refresh of theme update information + wp_clean_themes_cache(); + } + + // Next, Process any core update + wp_version_check(); // Check for Core updates + $core_update = find_core_auto_update(); + + if ( $core_update ) + $this->update( 'core', $core_update ); + + // Clean up, and check for any pending translations + // (Core_Upgrader checks for core updates) + $theme_stats = array(); + if ( isset( $this->update_results['theme'] ) ) { + foreach ( $this->update_results['theme'] as $upgrade ) { + $theme_stats[ $upgrade->item->theme ] = ( true === $upgrade->result ); + } + } + wp_update_themes( $theme_stats ); // Check for Theme updates + + $plugin_stats = array(); + if ( isset( $this->update_results['plugin'] ) ) { + foreach ( $this->update_results['plugin'] as $upgrade ) { + $plugin_stats[ $upgrade->item->plugin ] = ( true === $upgrade->result ); + } + } + wp_update_plugins( $plugin_stats ); // Check for Plugin updates + + // Finally, Process any new translations + $language_updates = wp_get_translation_updates(); + if ( $language_updates ) { + foreach ( $language_updates as $update ) { + $this->update( 'translation', $update ); + } + + // Clear existing caches + wp_clean_update_cache(); + + wp_version_check(); // check for Core updates + wp_update_themes(); // Check for Theme updates + wp_update_plugins(); // Check for Plugin updates + } + + // Send debugging email to all development installs. + if ( ! empty( $this->update_results ) ) { + $development_version = false !== strpos( $wp_version, '-' ); + + /** + * Filter whether to send a debugging email for each automatic background update. + * + * @since 3.7.0 + * + * @param bool $development_version By default, emails are sent if the + * install is a development version. + * Return false to avoid the email. + */ + if ( apply_filters( 'automatic_updates_send_debug_email', $development_version ) ) + $this->send_debug_email(); + + if ( ! empty( $this->update_results['core'] ) ) + $this->after_core_update( $this->update_results['core'][0] ); + + /** + * Fires after all automatic updates have run. + * + * @since 3.8.0 + * + * @param array $update_results The results of all attempted updates. + */ + do_action( 'automatic_updates_complete', $this->update_results ); + } + + // Clear the lock + delete_option( $lock_name ); + } + + /** + * If we tried to perform a core update, check if we should send an email, + * and if we need to avoid processing future updates. + * + * @param object $update_result The result of the core update. Includes the update offer and result. + */ + protected function after_core_update( $update_result ) { + global $wp_version; + + $core_update = $update_result->item; + $result = $update_result->result; + + if ( ! is_wp_error( $result ) ) { + $this->send_email( 'success', $core_update ); + return; + } + + $error_code = $result->get_error_code(); + + // Any of these WP_Error codes are critical failures, as in they occurred after we started to copy core files. + // We should not try to perform a background update again until there is a successful one-click update performed by the user. + $critical = false; + if ( $error_code === 'disk_full' || false !== strpos( $error_code, '__copy_dir' ) ) { + $critical = true; + } elseif ( $error_code === 'rollback_was_required' && is_wp_error( $result->get_error_data()->rollback ) ) { + // A rollback is only critical if it failed too. + $critical = true; + $rollback_result = $result->get_error_data()->rollback; + } elseif ( false !== strpos( $error_code, 'do_rollback' ) ) { + $critical = true; + } + + if ( $critical ) { + $critical_data = array( + 'attempted' => $core_update->current, + 'current' => $wp_version, + 'error_code' => $error_code, + 'error_data' => $result->get_error_data(), + 'timestamp' => time(), + 'critical' => true, + ); + if ( isset( $rollback_result ) ) { + $critical_data['rollback_code'] = $rollback_result->get_error_code(); + $critical_data['rollback_data'] = $rollback_result->get_error_data(); + } + update_site_option( 'auto_core_update_failed', $critical_data ); + $this->send_email( 'critical', $core_update, $result ); + return; + } + + /* + * Any other WP_Error code (like download_failed or files_not_writable) occurs before + * we tried to copy over core files. Thus, the failures are early and graceful. + * + * We should avoid trying to perform a background update again for the same version. + * But we can try again if another version is released. + * + * For certain 'transient' failures, like download_failed, we should allow retries. + * In fact, let's schedule a special update for an hour from now. (It's possible + * the issue could actually be on WordPress.org's side.) If that one fails, then email. + */ + $send = true; + $transient_failures = array( 'incompatible_archive', 'download_failed', 'insane_distro' ); + if ( in_array( $error_code, $transient_failures ) && ! get_site_option( 'auto_core_update_failed' ) ) { + wp_schedule_single_event( time() + HOUR_IN_SECONDS, 'wp_maybe_auto_update' ); + $send = false; + } + + $n = get_site_option( 'auto_core_update_notified' ); + // Don't notify if we've already notified the same email address of the same version of the same notification type. + if ( $n && 'fail' == $n['type'] && $n['email'] == get_site_option( 'admin_email' ) && $n['version'] == $core_update->current ) + $send = false; + + update_site_option( 'auto_core_update_failed', array( + 'attempted' => $core_update->current, + 'current' => $wp_version, + 'error_code' => $error_code, + 'error_data' => $result->get_error_data(), + 'timestamp' => time(), + 'retry' => in_array( $error_code, $transient_failures ), + ) ); + + if ( $send ) + $this->send_email( 'fail', $core_update, $result ); + } + + /** + * Sends an email upon the completion or failure of a background core update. + * + * @since 3.7.0 + * + * @param string $type The type of email to send. Can be one of 'success', 'fail', 'manual', 'critical'. + * @param object $core_update The update offer that was attempted. + * @param mixed $result Optional. The result for the core update. Can be WP_Error. + */ + protected function send_email( $type, $core_update, $result = null ) { + update_site_option( 'auto_core_update_notified', array( + 'type' => $type, + 'email' => get_site_option( 'admin_email' ), + 'version' => $core_update->current, + 'timestamp' => time(), + ) ); + + $next_user_core_update = get_preferred_from_update_core(); + // If the update transient is empty, use the update we just performed + if ( ! $next_user_core_update ) + $next_user_core_update = $core_update; + $newer_version_available = ( 'upgrade' == $next_user_core_update->response && version_compare( $next_user_core_update->version, $core_update->version, '>' ) ); + + /** + * Filter whether to send an email following an automatic background core update. + * + * @since 3.7.0 + * + * @param bool $send Whether to send the email. Default true. + * @param string $type The type of email to send. Can be one of + * 'success', 'fail', 'critical'. + * @param object $core_update The update offer that was attempted. + * @param mixed $result The result for the core update. Can be WP_Error. + */ + if ( 'manual' !== $type && ! apply_filters( 'auto_core_update_send_email', true, $type, $core_update, $result ) ) + return; + + switch ( $type ) { + case 'success' : // We updated. + /* translators: 1: Site name, 2: WordPress version number. */ + $subject = __( '[%1$s] Your site has updated to WordPress %2$s' ); + break; + + case 'fail' : // We tried to update but couldn't. + case 'manual' : // We can't update (and made no attempt). + /* translators: 1: Site name, 2: WordPress version number. */ + $subject = __( '[%1$s] WordPress %2$s is available. Please update!' ); + break; + + case 'critical' : // We tried to update, started to copy files, then things went wrong. + /* translators: 1: Site name. */ + $subject = __( '[%1$s] URGENT: Your site may be down due to a failed update' ); + break; + + default : + return; + } + + // If the auto update is not to the latest version, say that the current version of WP is available instead. + $version = 'success' === $type ? $core_update->current : $next_user_core_update->current; + $subject = sprintf( $subject, wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ), $version ); + + $body = ''; + + switch ( $type ) { + case 'success' : + $body .= sprintf( __( 'Howdy! Your site at %1$s has been updated automatically to WordPress %2$s.' ), home_url(), $core_update->current ); + $body .= "\n\n"; + if ( ! $newer_version_available ) + $body .= __( 'No further action is needed on your part.' ) . ' '; + + // Can only reference the About screen if their update was successful. + list( $about_version ) = explode( '-', $core_update->current, 2 ); + $body .= sprintf( __( "For more on version %s, see the About WordPress screen:" ), $about_version ); + $body .= "\n" . admin_url( 'about.php' ); + + if ( $newer_version_available ) { + $body .= "\n\n" . sprintf( __( 'WordPress %s is also now available.' ), $next_user_core_update->current ) . ' '; + $body .= __( 'Updating is easy and only takes a few moments:' ); + $body .= "\n" . network_admin_url( 'update-core.php' ); + } + + break; + + case 'fail' : + case 'manual' : + $body .= sprintf( __( 'Please update your site at %1$s to WordPress %2$s.' ), home_url(), $next_user_core_update->current ); + + $body .= "\n\n"; + + // Don't show this message if there is a newer version available. + // Potential for confusion, and also not useful for them to know at this point. + if ( 'fail' == $type && ! $newer_version_available ) + $body .= __( 'We tried but were unable to update your site automatically.' ) . ' '; + + $body .= __( 'Updating is easy and only takes a few moments:' ); + $body .= "\n" . network_admin_url( 'update-core.php' ); + break; + + case 'critical' : + if ( $newer_version_available ) + $body .= sprintf( __( 'Your site at %1$s experienced a critical failure while trying to update WordPress to version %2$s.' ), home_url(), $core_update->current ); + else + $body .= sprintf( __( 'Your site at %1$s experienced a critical failure while trying to update to the latest version of WordPress, %2$s.' ), home_url(), $core_update->current ); + + $body .= "\n\n" . __( "This means your site may be offline or broken. Don't panic; this can be fixed." ); + + $body .= "\n\n" . __( "Please check out your site now. It's possible that everything is working. If it says you need to update, you should do so:" ); + $body .= "\n" . network_admin_url( 'update-core.php' ); + break; + } + + $critical_support = 'critical' === $type && ! empty( $core_update->support_email ); + if ( $critical_support ) { + // Support offer if available. + $body .= "\n\n" . sprintf( __( "The WordPress team is willing to help you. Forward this email to %s and the team will work with you to make sure your site is working." ), $core_update->support_email ); + } else { + // Add a note about the support forums. + $body .= "\n\n" . __( 'If you experience any issues or need support, the volunteers in the WordPress.org support forums may be able to help.' ); + $body .= "\n" . __( 'https://wordpress.org/support/' ); + } + + // Updates are important! + if ( $type != 'success' || $newer_version_available ) { + $body .= "\n\n" . __( 'Keeping your site updated is important for security. It also makes the internet a safer place for you and your readers.' ); + } + + if ( $critical_support ) { + $body .= " " . __( "If you reach out to us, we'll also ensure you'll never have this problem again." ); + } + + // If things are successful and we're now on the latest, mention plugins and themes if any are out of date. + if ( $type == 'success' && ! $newer_version_available && ( get_plugin_updates() || get_theme_updates() ) ) { + $body .= "\n\n" . __( 'You also have some plugins or themes with updates available. Update them now:' ); + $body .= "\n" . network_admin_url(); + } + + $body .= "\n\n" . __( 'The WordPress Team' ) . "\n"; + + if ( 'critical' == $type && is_wp_error( $result ) ) { + $body .= "\n***\n\n"; + $body .= sprintf( __( 'Your site was running version %s.' ), $GLOBALS['wp_version'] ); + $body .= ' ' . __( 'We have some data that describes the error your site encountered.' ); + $body .= ' ' . __( 'Your hosting company, support forum volunteers, or a friendly developer may be able to use this information to help you:' ); + + // If we had a rollback and we're still critical, then the rollback failed too. + // Loop through all errors (the main WP_Error, the update result, the rollback result) for code, data, etc. + if ( 'rollback_was_required' == $result->get_error_code() ) + $errors = array( $result, $result->get_error_data()->update, $result->get_error_data()->rollback ); + else + $errors = array( $result ); + + foreach ( $errors as $error ) { + if ( ! is_wp_error( $error ) ) + continue; + $error_code = $error->get_error_code(); + $body .= "\n\n" . sprintf( __( "Error code: %s" ), $error_code ); + if ( 'rollback_was_required' == $error_code ) + continue; + if ( $error->get_error_message() ) + $body .= "\n" . $error->get_error_message(); + $error_data = $error->get_error_data(); + if ( $error_data ) + $body .= "\n" . implode( ', ', (array) $error_data ); + } + $body .= "\n"; + } + + $to = get_site_option( 'admin_email' ); + $headers = ''; + + $email = compact( 'to', 'subject', 'body', 'headers' ); + + /** + * Filter the email sent following an automatic background core update. + * + * @since 3.7.0 + * + * @param array $email { + * Array of email arguments that will be passed to wp_mail(). + * + * @type string $to The email recipient. An array of emails + * can be returned, as handled by wp_mail(). + * @type string $subject The email's subject. + * @type string $body The email message body. + * @type string $headers Any email headers, defaults to no headers. + * } + * @param string $type The type of email being sent. Can be one of + * 'success', 'fail', 'manual', 'critical'. + * @param object $core_update The update offer that was attempted. + * @param mixed $result The result for the core update. Can be WP_Error. + */ + $email = apply_filters( 'auto_core_update_email', $email, $type, $core_update, $result ); + + wp_mail( $email['to'], wp_specialchars_decode( $email['subject'] ), $email['body'], $email['headers'] ); + } + + /** + * Prepares and sends an email of a full log of background update results, useful for debugging and geekery. + * + * @since 3.7.0 + */ + protected function send_debug_email() { + $update_count = 0; + foreach ( $this->update_results as $type => $updates ) + $update_count += count( $updates ); + + $body = array(); + $failures = 0; + + $body[] = sprintf( __( 'WordPress site: %s' ), network_home_url( '/' ) ); + + // Core + if ( isset( $this->update_results['core'] ) ) { + $result = $this->update_results['core'][0]; + if ( $result->result && ! is_wp_error( $result->result ) ) { + $body[] = sprintf( __( 'SUCCESS: WordPress was successfully updated to %s' ), $result->name ); + } else { + $body[] = sprintf( __( 'FAILED: WordPress failed to update to %s' ), $result->name ); + $failures++; + } + $body[] = ''; + } + + // Plugins, Themes, Translations + foreach ( array( 'plugin', 'theme', 'translation' ) as $type ) { + if ( ! isset( $this->update_results[ $type ] ) ) + continue; + $success_items = wp_list_filter( $this->update_results[ $type ], array( 'result' => true ) ); + if ( $success_items ) { + $messages = array( + 'plugin' => __( 'The following plugins were successfully updated:' ), + 'theme' => __( 'The following themes were successfully updated:' ), + 'translation' => __( 'The following translations were successfully updated:' ), + ); + + $body[] = $messages[ $type ]; + foreach ( wp_list_pluck( $success_items, 'name' ) as $name ) { + $body[] = ' * ' . sprintf( __( 'SUCCESS: %s' ), $name ); + } + } + if ( $success_items != $this->update_results[ $type ] ) { + // Failed updates + $messages = array( + 'plugin' => __( 'The following plugins failed to update:' ), + 'theme' => __( 'The following themes failed to update:' ), + 'translation' => __( 'The following translations failed to update:' ), + ); + + $body[] = $messages[ $type ]; + foreach ( $this->update_results[ $type ] as $item ) { + if ( ! $item->result || is_wp_error( $item->result ) ) { + $body[] = ' * ' . sprintf( __( 'FAILED: %s' ), $item->name ); + $failures++; + } + } + } + $body[] = ''; + } + + $site_title = wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ); + if ( $failures ) { + $body[] = trim( __( " +BETA TESTING? +============= + +This debugging email is sent when you are using a development version of WordPress. + +If you think these failures might be due to a bug in WordPress, could you report it? + * Open a thread in the support forums: https://wordpress.org/support/forum/alphabeta + * Or, if you're comfortable writing a bug report: https://core.trac.wordpress.org/ + +Thanks! -- The WordPress Team" ) ); + $body[] = ''; + + $subject = sprintf( __( '[%s] There were failures during background updates' ), $site_title ); + } else { + $subject = sprintf( __( '[%s] Background updates have finished' ), $site_title ); + } + + $body[] = trim( __( ' +UPDATE LOG +==========' ) ); + $body[] = ''; + + foreach ( array( 'core', 'plugin', 'theme', 'translation' ) as $type ) { + if ( ! isset( $this->update_results[ $type ] ) ) + continue; + foreach ( $this->update_results[ $type ] as $update ) { + $body[] = $update->name; + $body[] = str_repeat( '-', strlen( $update->name ) ); + foreach ( $update->messages as $message ) + $body[] = " " . html_entity_decode( str_replace( '…', '...', $message ) ); + if ( is_wp_error( $update->result ) ) { + $results = array( 'update' => $update->result ); + // If we rolled back, we want to know an error that occurred then too. + if ( 'rollback_was_required' === $update->result->get_error_code() ) + $results = (array) $update->result->get_error_data(); + foreach ( $results as $result_type => $result ) { + if ( ! is_wp_error( $result ) ) + continue; + + if ( 'rollback' === $result_type ) { + /* translators: 1: Error code, 2: Error message. */ + $body[] = ' ' . sprintf( __( 'Rollback Error: [%1$s] %2$s' ), $result->get_error_code(), $result->get_error_message() ); + } else { + /* translators: 1: Error code, 2: Error message. */ + $body[] = ' ' . sprintf( __( 'Error: [%1$s] %2$s' ), $result->get_error_code(), $result->get_error_message() ); + } + + if ( $result->get_error_data() ) + $body[] = ' ' . implode( ', ', (array) $result->get_error_data() ); + } + } + $body[] = ''; + } + } + + $email = array( + 'to' => get_site_option( 'admin_email' ), + 'subject' => $subject, + 'body' => implode( "\n", $body ), + 'headers' => '' + ); + + /** + * Filter the debug email that can be sent following an automatic + * background core update. + * + * @since 3.8.0 + * + * @param array $email { + * Array of email arguments that will be passed to wp_mail(). + * + * @type string $to The email recipient. An array of emails + * can be returned, as handled by wp_mail(). + * @type string $subject Email subject. + * @type string $body Email message body. + * @type string $headers Any email headers. Default empty. + * } + * @param int $failures The number of failures encountered while upgrading. + * @param mixed $results The results of all attempted updates. + */ + $email = apply_filters( 'automatic_updates_debug_email', $email, $failures, $this->update_results ); + + wp_mail( $email['to'], wp_specialchars_decode( $email['subject'] ), $email['body'], $email['headers'] ); + } +} diff --git a/wp-admin/includes/class-wp-users-list-table.php b/wp-admin/includes/class-wp-users-list-table.php new file mode 100644 index 0000000..81637e9 --- /dev/null +++ b/wp-admin/includes/class-wp-users-list-table.php @@ -0,0 +1,459 @@ + 'user', + 'plural' => 'users', + 'screen' => isset( $args['screen'] ) ? $args['screen'] : null, + ) ); + + $this->is_site_users = 'site-users-network' == $this->screen->id; + + if ( $this->is_site_users ) + $this->site_id = isset( $_REQUEST['id'] ) ? intval( $_REQUEST['id'] ) : 0; + } + + /** + * Check the current user's permissions. + * + * @since 3.1.0 + * @access public + */ + public function ajax_user_can() { + if ( $this->is_site_users ) + return current_user_can( 'manage_sites' ); + else + return current_user_can( 'list_users' ); + } + + /** + * Prepare the users list for display. + * + * @since 3.1.0 + * @access public + */ + public function prepare_items() { + global $role, $usersearch; + + $usersearch = isset( $_REQUEST['s'] ) ? wp_unslash( trim( $_REQUEST['s'] ) ) : ''; + + $role = isset( $_REQUEST['role'] ) ? $_REQUEST['role'] : ''; + + $per_page = ( $this->is_site_users ) ? 'site_users_network_per_page' : 'users_per_page'; + $users_per_page = $this->get_items_per_page( $per_page ); + + $paged = $this->get_pagenum(); + + $args = array( + 'number' => $users_per_page, + 'offset' => ( $paged-1 ) * $users_per_page, + 'role' => $role, + 'search' => $usersearch, + 'fields' => 'all_with_meta' + ); + + if ( '' !== $args['search'] ) + $args['search'] = '*' . $args['search'] . '*'; + + if ( $this->is_site_users ) + $args['blog_id'] = $this->site_id; + + if ( isset( $_REQUEST['orderby'] ) ) + $args['orderby'] = $_REQUEST['orderby']; + + if ( isset( $_REQUEST['order'] ) ) + $args['order'] = $_REQUEST['order']; + + // Query the user IDs for this page + $wp_user_search = new WP_User_Query( $args ); + + $this->items = $wp_user_search->get_results(); + + $this->set_pagination_args( array( + 'total_items' => $wp_user_search->get_total(), + 'per_page' => $users_per_page, + ) ); + } + + /** + * Output 'no users' message. + * + * @since 3.1.0 + * @access public + */ + public function no_items() { + _e( 'No matching users were found.' ); + } + + /** + * Return an associative array listing all the views that can be used + * with this table. + * + * Provides a list of roles and user count for that role for easy + * filtering of the user table. + * + * @since 3.1.0 + * @access protected + * + * @return array An array of HTML links, one for each view. + */ + protected function get_views() { + global $wp_roles, $role; + + if ( $this->is_site_users ) { + $url = 'site-users.php?id=' . $this->site_id; + switch_to_blog( $this->site_id ); + $users_of_blog = count_users(); + restore_current_blog(); + } else { + $url = 'users.php'; + $users_of_blog = count_users(); + } + $total_users = $users_of_blog['total_users']; + $avail_roles =& $users_of_blog['avail_roles']; + unset($users_of_blog); + + $class = empty($role) ? ' class="current"' : ''; + $role_links = array(); + $role_links['all'] = "" . sprintf( _nx( 'All (%s)', 'All (%s)', $total_users, 'users' ), number_format_i18n( $total_users ) ) . ''; + foreach ( $wp_roles->get_names() as $this_role => $name ) { + if ( !isset($avail_roles[$this_role]) ) + continue; + + $class = ''; + + if ( $this_role == $role ) { + $class = ' class="current"'; + } + + $name = translate_user_role( $name ); + /* translators: User role name with count */ + $name = sprintf( __('%1$s (%2$s)'), $name, number_format_i18n( $avail_roles[$this_role] ) ); + $role_links[$this_role] = "$name"; + } + + return $role_links; + } + + /** + * Retrieve an associative array of bulk actions available on this table. + * + * @since 3.1.0 + * @access protected + * + * @return array Array of bulk actions. + */ + protected function get_bulk_actions() { + $actions = array(); + + if ( is_multisite() ) { + if ( current_user_can( 'remove_users' ) ) + $actions['remove'] = __( 'Remove' ); + } else { + if ( current_user_can( 'delete_users' ) ) + $actions['delete'] = __( 'Delete' ); + } + + return $actions; + } + + /** + * Output the controls to allow user roles to be changed in bulk. + * + * @since 3.1.0 + * @access protected + * + * @param string $which Whether this is being invoked above ("top") + * or below the table ("bottom"). + */ + protected function extra_tablenav( $which ) { + if ( 'top' != $which ) + return; + ?> +
    + + + + '; + } + + /** + * Capture the bulk action required, and return it. + * + * Overridden from the base class implementation to capture + * the role change drop-down. + * + * @since 3.1.0 + * @access public + * + * @return string The bulk action required. + */ + public function current_action() { + if ( isset($_REQUEST['changeit']) && !empty($_REQUEST['new_role']) ) + return 'promote'; + + return parent::current_action(); + } + + /** + * Get a list of columns for the list table. + * + * @since 3.1.0 + * @access public + * + * @return array Array in which the key is the ID of the column, + * and the value is the description. + */ + public function get_columns() { + $c = array( + 'cb' => '', + 'username' => __( 'Username' ), + 'name' => __( 'Name' ), + 'email' => __( 'E-mail' ), + 'role' => __( 'Role' ), + 'posts' => __( 'Posts' ) + ); + + if ( $this->is_site_users ) + unset( $c['posts'] ); + + return $c; + } + + /** + * Get a list of sortable columns for the list table. + * + * @since 3.1.0 + * @access protected + * + * @return array Array of sortable columns. + */ + protected function get_sortable_columns() { + $c = array( + 'username' => 'login', + 'name' => 'name', + 'email' => 'email', + ); + + if ( $this->is_site_users ) + unset( $c['posts'] ); + + return $c; + } + + /** + * Generate the list table rows. + * + * @since 3.1.0 + * @access public + */ + public function display_rows() { + // Query the post counts for this page + if ( ! $this->is_site_users ) + $post_counts = count_many_users_posts( array_keys( $this->items ) ); + + $editable_roles = array_keys( get_editable_roles() ); + + $style = ''; + foreach ( $this->items as $userid => $user_object ) { + if ( count( $user_object->roles ) <= 1 ) { + $role = reset( $user_object->roles ); + } elseif ( $roles = array_intersect( array_values( $user_object->roles ), $editable_roles ) ) { + $role = reset( $roles ); + } else { + $role = reset( $user_object->roles ); + } + + if ( is_multisite() && empty( $user_object->allcaps ) ) + continue; + + $style = ( ' class="alternate"' == $style ) ? '' : ' class="alternate"'; + echo "\n\t" . $this->single_row( $user_object, $style, $role, isset( $post_counts ) ? $post_counts[ $userid ] : 0 ); + } + } + + /** + * Generate HTML for a single row on the users.php admin panel. + * + * @since 3.1.0 + * @access public + * + * @param object $user_object The current user object. + * @param string $style Optional. Style attributes added to the `` element. + * Must be sanitized. Default empty. + * @param string $role Optional. Key for the $wp_roles array. Default empty. + * @param int $numposts Optional. Post count to display for this user. Defaults + * to zero, as in, a new user has made zero posts. + * @return string Output for a single row. + */ + public function single_row( $user_object, $style = '', $role = '', $numposts = 0 ) { + global $wp_roles; + + if ( !( is_object( $user_object ) && is_a( $user_object, 'WP_User' ) ) ) + $user_object = get_userdata( (int) $user_object ); + $user_object->filter = 'display'; + $email = $user_object->user_email; + + if ( $this->is_site_users ) + $url = "site-users.php?id={$this->site_id}&"; + else + $url = 'users.php?'; + + $checkbox = ''; + // Check if the user for this row is editable + if ( current_user_can( 'list_users' ) ) { + // Set up the user editing link + $edit_link = esc_url( add_query_arg( 'wp_http_referer', urlencode( wp_unslash( $_SERVER['REQUEST_URI'] ) ), get_edit_user_link( $user_object->ID ) ) ); + + // Set up the hover actions for this user + $actions = array(); + + if ( current_user_can( 'edit_user', $user_object->ID ) ) { + $edit = "$user_object->user_login
    "; + $actions['edit'] = '' . __( 'Edit' ) . ''; + } else { + $edit = "$user_object->user_login
    "; + } + + if ( !is_multisite() && get_current_user_id() != $user_object->ID && current_user_can( 'delete_user', $user_object->ID ) ) + $actions['delete'] = "" . __( 'Delete' ) . ""; + if ( is_multisite() && get_current_user_id() != $user_object->ID && current_user_can( 'remove_user', $user_object->ID ) ) + $actions['remove'] = "" . __( 'Remove' ) . ""; + + /** + * Filter the action links displayed under each user in the Users list table. + * + * @since 2.8.0 + * + * @param array $actions An array of action links to be displayed. + * Default 'Edit', 'Delete' for single site, and + * 'Edit', 'Remove' for Multisite. + * @param WP_User $user_object WP_User object for the currently-listed user. + */ + $actions = apply_filters( 'user_row_actions', $actions, $user_object ); + $edit .= $this->row_actions( $actions ); + + // Set up the checkbox ( because the user is editable, otherwise it's empty ) + $checkbox = '' + . ""; + + } else { + $edit = '' . $user_object->user_login . ''; + } + $role_name = isset( $wp_roles->role_names[$role] ) ? translate_user_role( $wp_roles->role_names[$role] ) : __( 'None' ); + $avatar = get_avatar( $user_object->ID, 32 ); + + $r = ""; + + list( $columns, $hidden ) = $this->get_column_info(); + + foreach ( $columns as $column_name => $column_display_name ) { + $class = "class=\"$column_name column-$column_name\""; + + $style = ''; + if ( in_array( $column_name, $hidden ) ) + $style = ' style="display:none;"'; + + $attributes = "$class$style"; + + switch ( $column_name ) { + case 'cb': + $r .= "$checkbox"; + break; + case 'username': + $r .= "$avatar $edit"; + break; + case 'name': + $r .= "$user_object->first_name $user_object->last_name"; + break; + case 'email': + $r .= "$email"; + break; + case 'role': + $r .= "$role_name"; + break; + case 'posts': + $attributes = 'class="posts column-posts num"' . $style; + $r .= ""; + if ( $numposts > 0 ) { + $r .= ""; + $r .= $numposts; + $r .= ''; + } else { + $r .= 0; + } + $r .= ""; + break; + default: + $r .= ""; + + /** + * Filter the display output of custom columns in the Users list table. + * + * @since 2.8.0 + * + * @param string $output Custom column output. Default empty. + * @param string $column_name Column name. + * @param int $user_id ID of the currently-listed user. + */ + $r .= apply_filters( 'manage_users_custom_column', '', $column_name, $user_object->ID ); + $r .= ""; + } + } + $r .= ''; + + return $r; + } +} diff --git a/wp-admin/includes/comment.php b/wp-admin/includes/comment.php new file mode 100644 index 0000000..06447d4 --- /dev/null +++ b/wp-admin/includes/comment.php @@ -0,0 +1,173 @@ +get_var( $wpdb->prepare("SELECT comment_post_ID FROM $wpdb->comments + WHERE comment_author = %s AND comment_date = %s", $comment_author, $comment_date) ); +} + +/** + * Update a comment with values provided in $_POST. + * + * @since 2.0.0 + */ +function edit_comment() { + + if ( ! current_user_can( 'edit_comment', (int) $_POST['comment_ID'] ) ) + wp_die ( __( 'You are not allowed to edit comments on this post.' ) ); + + if ( isset( $_POST['newcomment_author'] ) ) + $_POST['comment_author'] = $_POST['newcomment_author']; + if ( isset( $_POST['newcomment_author_email'] ) ) + $_POST['comment_author_email'] = $_POST['newcomment_author_email']; + if ( isset( $_POST['newcomment_author_url'] ) ) + $_POST['comment_author_url'] = $_POST['newcomment_author_url']; + if ( isset( $_POST['comment_status'] ) ) + $_POST['comment_approved'] = $_POST['comment_status']; + if ( isset( $_POST['content'] ) ) + $_POST['comment_content'] = $_POST['content']; + if ( isset( $_POST['comment_ID'] ) ) + $_POST['comment_ID'] = (int) $_POST['comment_ID']; + + foreach ( array ('aa', 'mm', 'jj', 'hh', 'mn') as $timeunit ) { + if ( !empty( $_POST['hidden_' . $timeunit] ) && $_POST['hidden_' . $timeunit] != $_POST[$timeunit] ) { + $_POST['edit_date'] = '1'; + break; + } + } + + if ( !empty ( $_POST['edit_date'] ) ) { + $aa = $_POST['aa']; + $mm = $_POST['mm']; + $jj = $_POST['jj']; + $hh = $_POST['hh']; + $mn = $_POST['mn']; + $ss = $_POST['ss']; + $jj = ($jj > 31 ) ? 31 : $jj; + $hh = ($hh > 23 ) ? $hh -24 : $hh; + $mn = ($mn > 59 ) ? $mn -60 : $mn; + $ss = ($ss > 59 ) ? $ss -60 : $ss; + $_POST['comment_date'] = "$aa-$mm-$jj $hh:$mn:$ss"; + } + + wp_update_comment( $_POST ); +} + +/** + * Returns a comment object based on comment ID. + * + * @since 2.0.0 + * + * @param int $id ID of comment to retrieve. + * @return bool|object Comment if found. False on failure. + */ +function get_comment_to_edit( $id ) { + if ( !$comment = get_comment($id) ) + return false; + + $comment->comment_ID = (int) $comment->comment_ID; + $comment->comment_post_ID = (int) $comment->comment_post_ID; + + $comment->comment_content = format_to_edit( $comment->comment_content ); + /** + * Filter the comment content before editing. + * + * @since 2.0.0 + * + * @param string $comment->comment_content Comment content. + */ + $comment->comment_content = apply_filters( 'comment_edit_pre', $comment->comment_content ); + + $comment->comment_author = format_to_edit( $comment->comment_author ); + $comment->comment_author_email = format_to_edit( $comment->comment_author_email ); + $comment->comment_author_url = format_to_edit( $comment->comment_author_url ); + $comment->comment_author_url = esc_url($comment->comment_author_url); + + return $comment; +} + +/** + * Get the number of pending comments on a post or posts + * + * @since 2.3.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @param int|array $post_id Either a single Post ID or an array of Post IDs + * @return int|array Either a single Posts pending comments as an int or an array of ints keyed on the Post IDs + */ +function get_pending_comments_num( $post_id ) { + global $wpdb; + + $single = false; + if ( !is_array($post_id) ) { + $post_id_array = (array) $post_id; + $single = true; + } else { + $post_id_array = $post_id; + } + $post_id_array = array_map('intval', $post_id_array); + $post_id_in = "'" . implode("', '", $post_id_array) . "'"; + + $pending = $wpdb->get_results( "SELECT comment_post_ID, COUNT(comment_ID) as num_comments FROM $wpdb->comments WHERE comment_post_ID IN ( $post_id_in ) AND comment_approved = '0' GROUP BY comment_post_ID", ARRAY_A ); + + if ( $single ) { + if ( empty($pending) ) + return 0; + else + return absint($pending[0]['num_comments']); + } + + $pending_keyed = array(); + + // Default to zero pending for all posts in request + foreach ( $post_id_array as $id ) + $pending_keyed[$id] = 0; + + if ( !empty($pending) ) + foreach ( $pending as $pend ) + $pending_keyed[$pend['comment_post_ID']] = absint($pend['num_comments']); + + return $pending_keyed; +} + +/** + * Add avatars to relevant places in admin, or try to. + * + * @since 2.5.0 + * @uses $comment + * + * @param string $name User name. + * @return string Avatar with Admin name. + */ +function floated_admin_avatar( $name ) { + global $comment; + $avatar = get_avatar( $comment, 32, 'mystery' ); + return "$avatar $name"; +} + +function enqueue_comment_hotkeys_js() { + if ( 'true' == get_user_option( 'comment_shortcuts' ) ) + wp_enqueue_script( 'jquery-table-hotkeys' ); +} diff --git a/wp-admin/includes/continents-cities.php b/wp-admin/includes/continents-cities.php new file mode 100644 index 0000000..0ebc4f3 --- /dev/null +++ b/wp-admin/includes/continents-cities.php @@ -0,0 +1,493 @@ +%1$s %2$s', __( 'Quick Draft' ), __( 'Drafts' ) ); + wp_add_dashboard_widget( 'dashboard_quick_press', $quick_draft_title, 'wp_dashboard_quick_press' ); + } + + // WordPress News + wp_add_dashboard_widget( 'dashboard_primary', __( 'WordPress News' ), 'wp_dashboard_primary' ); + + if ( is_network_admin() ) { + + /** + * Fires after core widgets for the Network Admin dashboard have been registered. + * + * @since 3.1.0 + */ + do_action( 'wp_network_dashboard_setup' ); + + /** + * Filter the list of widgets to load for the Network Admin dashboard. + * + * @since 3.1.0 + * + * @param array $dashboard_widgets An array of dashboard widgets. + */ + $dashboard_widgets = apply_filters( 'wp_network_dashboard_widgets', array() ); + } elseif ( is_user_admin() ) { + + /** + * Fires after core widgets for the User Admin dashboard have been registered. + * + * @since 3.1.0 + */ + do_action( 'wp_user_dashboard_setup' ); + + /** + * Filter the list of widgets to load for the User Admin dashboard. + * + * @since 3.1.0 + * + * @param array $dashboard_widgets An array of dashboard widgets. + */ + $dashboard_widgets = apply_filters( 'wp_user_dashboard_widgets', array() ); + } else { + + /** + * Fires after core widgets for the admin dashboard have been registered. + * + * @since 2.5.0 + */ + do_action( 'wp_dashboard_setup' ); + + /** + * Filter the list of widgets to load for the admin dashboard. + * + * @since 2.5.0 + * + * @param array $dashboard_widgets An array of dashboard widgets. + */ + $dashboard_widgets = apply_filters( 'wp_dashboard_widgets', array() ); + } + + foreach ( $dashboard_widgets as $widget_id ) { + $name = empty( $wp_registered_widgets[$widget_id]['all_link'] ) ? $wp_registered_widgets[$widget_id]['name'] : $wp_registered_widgets[$widget_id]['name'] . " " . __('View all') . ''; + wp_add_dashboard_widget( $widget_id, $name, $wp_registered_widgets[$widget_id]['callback'], $wp_registered_widget_controls[$widget_id]['callback'] ); + } + + if ( 'POST' == $_SERVER['REQUEST_METHOD'] && isset($_POST['widget_id']) ) { + check_admin_referer( 'edit-dashboard-widget_' . $_POST['widget_id'], 'dashboard-widget-nonce' ); + ob_start(); // hack - but the same hack wp-admin/widgets.php uses + wp_dashboard_trigger_widget_control( $_POST['widget_id'] ); + ob_end_clean(); + wp_redirect( remove_query_arg( 'edit' ) ); + exit; + } + + /** This action is documented in wp-admin/edit-form-advanced.php */ + do_action( 'do_meta_boxes', $screen->id, 'normal', '' ); + + /** This action is documented in wp-admin/edit-form-advanced.php */ + do_action( 'do_meta_boxes', $screen->id, 'side', '' ); +} + +function wp_add_dashboard_widget( $widget_id, $widget_name, $callback, $control_callback = null, $callback_args = null ) { + $screen = get_current_screen(); + global $wp_dashboard_control_callbacks; + + if ( $control_callback && current_user_can( 'edit_dashboard' ) && is_callable( $control_callback ) ) { + $wp_dashboard_control_callbacks[$widget_id] = $control_callback; + if ( isset( $_GET['edit'] ) && $widget_id == $_GET['edit'] ) { + list($url) = explode( '#', add_query_arg( 'edit', false ), 2 ); + $widget_name .= ' ' . __( 'Cancel' ) . ''; + $callback = '_wp_dashboard_control_callback'; + } else { + list($url) = explode( '#', add_query_arg( 'edit', $widget_id ), 2 ); + $widget_name .= ' ' . __( 'Configure' ) . ''; + } + } + + $side_widgets = array( 'dashboard_quick_press', 'dashboard_primary' ); + + $location = 'normal'; + if ( in_array($widget_id, $side_widgets) ) + $location = 'side'; + + $priority = 'core'; + if ( 'dashboard_browser_nag' === $widget_id ) + $priority = 'high'; + + add_meta_box( $widget_id, $widget_name, $callback, $screen, $location, $priority, $callback_args ); +} + +function _wp_dashboard_control_callback( $dashboard, $meta_box ) { + echo '
    '; + wp_dashboard_trigger_widget_control( $meta_box['id'] ); + wp_nonce_field( 'edit-dashboard-widget_' . $meta_box['id'], 'dashboard-widget-nonce' ); + echo ''; + submit_button( __('Submit') ); + echo '
    '; +} + +/** + * Displays the dashboard. + * + * @since 2.5.0 + */ +function wp_dashboard() { + $screen = get_current_screen(); + $columns = absint( $screen->get_columns() ); + $columns_css = ''; + if ( $columns ) { + $columns_css = " columns-$columns"; + } + +?> +
    +
    + id, 'normal', '' ); ?> +
    +
    + id, 'side', '' ); ?> +
    +
    + id, 'column3', '' ); ?> +
    +
    + id, 'column4', '' ); ?> +
    +
    + + +
    +
      + publish ) { + if ( 'post' == $post_type ) { + $text = _n( '%s Post', '%s Posts', $num_posts->publish ); + } else { + $text = _n( '%s Page', '%s Pages', $num_posts->publish ); + } + $text = sprintf( $text, number_format_i18n( $num_posts->publish ) ); + $post_type_object = get_post_type_object( $post_type ); + if ( $post_type_object && current_user_can( $post_type_object->cap->edit_posts ) ) { + printf( '
    • %2$s
    • ', $post_type, $text ); + } else { + printf( '
    • %2$s
    • ', $post_type, $text ); + } + + } + } + // Comments + $num_comm = wp_count_comments(); + if ( $num_comm && $num_comm->approved ) { + $text = sprintf( _n( '%s Comment', '%s Comments', $num_comm->approved ), number_format_i18n( $num_comm->approved ) ); + ?> +
    • + moderated ) { + /* translators: Number of comments in moderation */ + $text = sprintf( _nx( '%s in moderation', '%s in moderation', $num_comm->moderated, 'comments' ), number_format_i18n( $num_comm->moderated ) ); + ?> +
    • + ' . implode( "\n
    • ", $elements ) . "
    • \n"; + } + + ?> +
    + $content

    "; + } + ?> +
    + +
    + +
    + ' . __( 'Create a New Site' ) . ''; + if ( current_user_can('create_users') ) + $actions['create-user'] = '' . __( 'Create a New User' ) . ''; + + $c_users = get_user_count(); + $c_blogs = get_blog_count(); + + $user_text = sprintf( _n( '%s user', '%s users', $c_users ), number_format_i18n( $c_users ) ); + $blog_text = sprintf( _n( '%s site', '%s sites', $c_blogs ), number_format_i18n( $c_blogs ) ); + + $sentence = sprintf( __( 'You have %1$s and %2$s.' ), $blog_text, $user_text ); + + if ( $actions ) { + echo '
      '; + foreach ( $actions as $class => $action ) { + $actions[ $class ] = "\t
    • $action"; + } + echo implode( " |
    • \n", $actions ) . "\n"; + echo '
    '; + } +?> +
    + +

    + + + + +
    +

    + + 'submit_users' ) ); ?> +

    +
    + +
    +

    + + 'submit_sites' ) ); ?> +

    +
    +post_status != 'auto-draft' ) { // auto-draft doesn't exists anymore + $post = get_default_post_to_edit( 'post', true ); + update_user_option( get_current_user_id(), 'dashboard_quick_press_last_post_id', (int) $post->ID ); // Save post_ID + } else { + $post->post_title = ''; // Remove the auto draft title + } + } else { + $post = get_default_post_to_edit( 'post' , true); + $user_id = get_current_user_id(); + // Don't create an option if this is a super admin who does not belong to this site. + if ( ! ( is_super_admin( $user_id ) && ! in_array( get_current_blog_id(), array_keys( get_blogs_of_user( $user_id ) ) ) ) ) + update_user_option( $user_id, 'dashboard_quick_press_last_post_id', (int) $post->ID ); // Save post_ID + } + + $post_ID = (int) $post->ID; +?> + +
    + + +
    + + +
    + + +
    + +
    + + +
    + +

    + + + + + 'save-post' ) ); ?> +
    +

    + +
    + 'post', + 'post_status' => 'draft', + 'author' => get_current_user_id(), + 'posts_per_page' => 4, + 'orderby' => 'modified', + 'order' => 'DESC' + ); + $drafts = get_posts( $query_args ); + if ( ! $drafts ) { + return; + } + } + + echo '
    '; + if ( count( $drafts ) > 3 ) { + echo '

    ' . _x( 'View all', 'drafts' ) . "

    \n"; + } + echo '

    ' . __( 'Drafts' ) . "

    \n
      "; + + $drafts = array_slice( $drafts, 0, 3 ); + foreach ( $drafts as $draft ) { + $url = get_edit_post_link( $draft->ID ); + $title = _draft_or_post_title( $draft->ID ); + echo "
    • \n"; + echo '
      ' . esc_html( $title ) . ''; + echo '
      '; + if ( $the_content = wp_trim_words( $draft->post_content, 10 ) ) { + echo '

      ' . $the_content . '

      '; + } + echo "
    • \n"; + } + echo "
    \n
    "; +} + +function _wp_dashboard_recent_comments_row( &$comment, $show_date = true ) { + $GLOBALS['comment'] =& $comment; + + $comment_post_title = strip_tags(get_the_title( $comment->comment_post_ID )); + + if ( current_user_can( 'edit_post', $comment->comment_post_ID ) ) { + $comment_post_url = get_edit_post_link( $comment->comment_post_ID ); + $comment_post_link = "$comment_post_title"; + } else { + $comment_post_link = $comment_post_title; + } + + $comment_link = '#'; + + $actions_string = ''; + if ( current_user_can( 'edit_comment', $comment->comment_ID ) ) { + // Pre-order it: Approve | Reply | Edit | Spam | Trash. + $actions = array( + 'approve' => '', 'unapprove' => '', + 'reply' => '', + 'edit' => '', + 'spam' => '', + 'trash' => '', 'delete' => '' + ); + + $del_nonce = esc_html( '_wpnonce=' . wp_create_nonce( "delete-comment_$comment->comment_ID" ) ); + $approve_nonce = esc_html( '_wpnonce=' . wp_create_nonce( "approve-comment_$comment->comment_ID" ) ); + + $approve_url = esc_url( "comment.php?action=approvecomment&p=$comment->comment_post_ID&c=$comment->comment_ID&$approve_nonce" ); + $unapprove_url = esc_url( "comment.php?action=unapprovecomment&p=$comment->comment_post_ID&c=$comment->comment_ID&$approve_nonce" ); + $spam_url = esc_url( "comment.php?action=spamcomment&p=$comment->comment_post_ID&c=$comment->comment_ID&$del_nonce" ); + $trash_url = esc_url( "comment.php?action=trashcomment&p=$comment->comment_post_ID&c=$comment->comment_ID&$del_nonce" ); + $delete_url = esc_url( "comment.php?action=deletecomment&p=$comment->comment_post_ID&c=$comment->comment_ID&$del_nonce" ); + + $actions['approve'] = "" . __( 'Approve' ) . ''; + $actions['unapprove'] = "" . __( 'Unapprove' ) . ''; + $actions['edit'] = "". __('Edit') . ''; + $actions['reply'] = '' . __('Reply') . ''; + $actions['spam'] = "" . /* translators: mark as spam link */ _x( 'Spam', 'verb' ) . ''; + if ( !EMPTY_TRASH_DAYS ) + $actions['delete'] = "" . __('Delete Permanently') . ''; + else + $actions['trash'] = "" . _x('Trash', 'verb') . ''; + + /** + * Filter the action links displayed for each comment in the 'Recent Comments' + * dashboard widget. + * + * @since 2.6.0 + * + * @param array $actions An array of comment actions. Default actions include: + * 'Approve', 'Unapprove', 'Edit', 'Reply', 'Spam', + * 'Delete', and 'Trash'. + * @param object $comment The comment object. + */ + $actions = apply_filters( 'comment_row_actions', array_filter($actions), $comment ); + + $i = 0; + foreach ( $actions as $action => $link ) { + ++$i; + ( ( ('approve' == $action || 'unapprove' == $action) && 2 === $i ) || 1 === $i ) ? $sep = '' : $sep = ' | '; + + // Reply and quickedit need a hide-if-no-js span + if ( 'reply' == $action || 'quickedit' == $action ) + $action .= ' hide-if-no-js'; + + $actions_string .= "$sep$link"; + } + } + +?> + +
    comment_ID) ) ); ?>> + + + + comment_type || 'comment' == $comment->comment_type ) : ?> + +
    +

    + ' . get_comment_author_link() . '', $comment_post_link.' '.$comment_link, ' ' . __( '[Pending]' ) . '' ); ?> +

    + + comment_type ) { + case 'pingback' : + $type = __( 'Pingback' ); + break; + case 'trackback' : + $type = __( 'Trackback' ); + break; + default : + $type = ucwords( $comment->comment_type ); + } + $type = esc_html( $type ); + ?> +
    + +

    $type", $comment_post_link." ".$comment_link ); ?>

    +

    + + +

    +

    +
    +
    +'; + + $future_posts = wp_dashboard_recent_posts( array( + 'max' => 5, + 'status' => 'future', + 'order' => 'ASC', + 'title' => __( 'Publishing Soon' ), + 'id' => 'future-posts', + ) ); + $recent_posts = wp_dashboard_recent_posts( array( + 'max' => 5, + 'status' => 'publish', + 'order' => 'DESC', + 'title' => __( 'Recently Published' ), + 'id' => 'published-posts', + ) ); + + $recent_comments = wp_dashboard_recent_comments(); + + if ( !$future_posts && !$recent_posts && !$recent_comments ) { + echo '
    '; + echo '

    '; + echo '

    ' . __( 'No activity yet!' ) . '

    '; + echo '
    '; + } + + echo '
    '; +} + +/** + * Generates Publishing Soon and Recently Published sections. + * + * @since 3.8.0 + * + * @param array $args { + * An array of query and display arguments. + * + * @type int $max Number of posts to display. + * @type string $status Post status. + * @type string $order Designates ascending ('ASC') or descending ('DESC') order. + * @type string $title Section title. + * @type string $id The container id. + * } + * @return bool False if no posts were found. True otherwise. + */ +function wp_dashboard_recent_posts( $args ) { + $query_args = array( + 'post_type' => 'post', + 'post_status' => $args['status'], + 'orderby' => 'date', + 'order' => $args['order'], + 'posts_per_page' => intval( $args['max'] ), + 'no_found_rows' => true, + 'cache_results' => false, + 'perm' => ( 'future' === $args['status'] ) ? 'editable' : 'readable', + ); + $posts = new WP_Query( $query_args ); + + if ( $posts->have_posts() ) { + + echo '
    '; + + echo '

    ' . $args['title'] . '

    '; + + echo '
      '; + + $today = date( 'Y-m-d', current_time( 'timestamp' ) ); + $tomorrow = date( 'Y-m-d', strtotime( '+1 day', current_time( 'timestamp' ) ) ); + + while ( $posts->have_posts() ) { + $posts->the_post(); + + $time = get_the_time( 'U' ); + if ( date( 'Y-m-d', $time ) == $today ) { + $relative = __( 'Today' ); + } elseif ( date( 'Y-m-d', $time ) == $tomorrow ) { + $relative = __( 'Tomorrow' ); + } else { + /* translators: date and time format for recent posts on the dashboard, see http://php.net/date */ + $relative = date_i18n( __( 'M jS' ), $time ); + } + + if ( current_user_can( 'edit_post', get_the_ID() ) ) { + /* translators: 1: relative date, 2: time, 3: post edit link, 4: post title */ + $format = __( '%1$s, %2$s %4$s' ); + printf( "
    • $format
    • ", $relative, get_the_time(), get_edit_post_link(), _draft_or_post_title() ); + } else { + /* translators: 1: relative date, 2: time, 3: post title */ + $format = __( '%1$s, %2$s %3$s' ); + printf( "
    • $format
    • ", $relative, get_the_time(), _draft_or_post_title() ); + } + } + + echo '
    '; + echo '
    '; + + } else { + return false; + } + + wp_reset_postdata(); + + return true; +} + +/** + * Show Comments section. + * + * @since 3.8.0 + * + * @param int $total_items Optional. Number of comments to query. Default 5. + * @return bool False if no comments were found. True otherwise. + */ +function wp_dashboard_recent_comments( $total_items = 5 ) { + // Select all comment types and filter out spam later for better query performance. + $comments = array(); + + $comments_query = array( + 'number' => $total_items * 5, + 'offset' => 0 + ); + if ( ! current_user_can( 'edit_posts' ) ) + $comments_query['status'] = 'approve'; + + while ( count( $comments ) < $total_items && $possible = get_comments( $comments_query ) ) { + foreach ( $possible as $comment ) { + if ( ! current_user_can( 'read_post', $comment->comment_post_ID ) ) + continue; + $comments[] = $comment; + if ( count( $comments ) == $total_items ) + break 2; + } + $comments_query['offset'] += $comments_query['number']; + $comments_query['number'] = $total_items * 10; + } + + if ( $comments ) { + echo '
    '; + echo '

    ' . __( 'Comments' ) . '

    '; + + echo '
    '; + foreach ( $comments as $comment ) + _wp_dashboard_recent_comments_row( $comment ); + echo '
    '; + + if ( current_user_can('edit_posts') ) + _get_list_table('WP_Comments_List_Table')->views(); + + wp_comment_reply( -1, false, 'dashboard', false ); + wp_comment_trashnotice(); + + echo '
    '; + } else { + return false; + } + return true; +} + +/** + * Display generic dashboard RSS widget feed. + * + * @since 2.5.0 + * + * @param string $widget_id + */ +function wp_dashboard_rss_output( $widget_id ) { + $widgets = get_option( 'dashboard_widget_options' ); + echo '
    '; + wp_widget_rss_output( $widgets[ $widget_id ] ); + echo "
    "; +} + +/** + * Checks to see if all of the feed url in $check_urls are cached. + * + * If $check_urls is empty, look for the rss feed url found in the dashboard + * widget options of $widget_id. If cached, call $callback, a function that + * echoes out output for this widget. If not cache, echo a "Loading..." stub + * which is later replaced by AJAX call (see top of /wp-admin/index.php) + * + * @since 2.5.0 + * + * @param string $widget_id + * @param callback $callback + * @param array $check_urls RSS feeds + * @return bool False on failure. True on success. + */ +function wp_dashboard_cached_rss_widget( $widget_id, $callback, $check_urls = array() ) { + $loading = '

    ' . __( 'Loading…' ) . '

    ' . __( 'This widget requires JavaScript.' ) . '

    '; + $doing_ajax = ( defined('DOING_AJAX') && DOING_AJAX ); + + if ( empty($check_urls) ) { + $widgets = get_option( 'dashboard_widget_options' ); + if ( empty($widgets[$widget_id]['url']) && ! $doing_ajax ) { + echo $loading; + return false; + } + $check_urls = array( $widgets[$widget_id]['url'] ); + } + + $cache_key = 'dash_' . md5( $widget_id ); + if ( false !== ( $output = get_transient( $cache_key ) ) ) { + echo $output; + return true; + } + + if ( ! $doing_ajax ) { + echo $loading; + return false; + } + + if ( $callback && is_callable( $callback ) ) { + $args = array_slice( func_get_args(), 3 ); + array_unshift( $args, $widget_id, $check_urls ); + ob_start(); + call_user_func_array( $callback, $args ); + set_transient( $cache_key, ob_get_flush(), 12 * HOUR_IN_SECONDS ); // Default lifetime in cache of 12 hours (same as the feeds) + } + + return true; +} + +/* Dashboard Widgets Controls */ + +// Calls widget_control callback +/** + * Calls widget control callback. + * + * @since 2.5.0 + * + * @param int $widget_control_id Registered Widget ID. + */ +function wp_dashboard_trigger_widget_control( $widget_control_id = false ) { + global $wp_dashboard_control_callbacks; + + if ( is_scalar($widget_control_id) && $widget_control_id && isset($wp_dashboard_control_callbacks[$widget_control_id]) && is_callable($wp_dashboard_control_callbacks[$widget_control_id]) ) { + call_user_func( $wp_dashboard_control_callbacks[$widget_control_id], '', array( 'id' => $widget_control_id, 'callback' => $wp_dashboard_control_callbacks[$widget_control_id] ) ); + } +} + +/** + * The RSS dashboard widget control. + * + * Sets up $args to be used as input to wp_widget_rss_form(). Handles POST data + * from RSS-type widgets. + * + * @since 2.5.0 + * + * @param string $widget_id + * @param array $form_inputs + */ +function wp_dashboard_rss_control( $widget_id, $form_inputs = array() ) { + if ( !$widget_options = get_option( 'dashboard_widget_options' ) ) + $widget_options = array(); + + if ( !isset($widget_options[$widget_id]) ) + $widget_options[$widget_id] = array(); + + $number = 1; // Hack to use wp_widget_rss_form() + $widget_options[$widget_id]['number'] = $number; + + if ( 'POST' == $_SERVER['REQUEST_METHOD'] && isset($_POST['widget-rss'][$number]) ) { + $_POST['widget-rss'][$number] = wp_unslash( $_POST['widget-rss'][$number] ); + $widget_options[$widget_id] = wp_widget_rss_process( $_POST['widget-rss'][$number] ); + $widget_options[$widget_id]['number'] = $number; + + // Title is optional. If black, fill it if possible. + if ( !$widget_options[$widget_id]['title'] && isset($_POST['widget-rss'][$number]['title']) ) { + $rss = fetch_feed($widget_options[$widget_id]['url']); + if ( is_wp_error($rss) ) { + $widget_options[$widget_id]['title'] = htmlentities(__('Unknown Feed')); + } else { + $widget_options[$widget_id]['title'] = htmlentities(strip_tags($rss->get_title())); + $rss->__destruct(); + unset($rss); + } + } + update_option( 'dashboard_widget_options', $widget_options ); + $cache_key = 'dash_' . md5( $widget_id ); + delete_transient( $cache_key ); + } + + wp_widget_rss_form( $widget_options[$widget_id], $form_inputs ); +} + +/** + * WordPress News dashboard widget. + * + * @since 2.7.0 + */ +function wp_dashboard_primary() { + $feeds = array( + 'news' => array( + + /** + * Filter the primary link URL for the 'WordPress News' dashboard widget. + * + * @since 2.5.0 + * + * @param string $link The widget's primary link URL. + */ + 'link' => apply_filters( 'dashboard_primary_link', __( 'http://wordpress.org/news/' ) ), + + /** + * Filter the primary feed URL for the 'WordPress News' dashboard widget. + * + * @since 2.3.0 + * + * @param string $url The widget's primary feed URL. + */ + 'url' => apply_filters( 'dashboard_primary_feed', __( 'http://wordpress.org/news/feed/' ) ), + + /** + * Filter the primary link title for the 'WordPress News' dashboard widget. + * + * @since 2.3.0 + * + * @param string $title Title attribute for the widget's primary link. + */ + 'title' => apply_filters( 'dashboard_primary_title', __( 'WordPress Blog' ) ), + 'items' => 1, + 'show_summary' => 1, + 'show_author' => 0, + 'show_date' => 1, + ), + 'planet' => array( + + /** + * Filter the secondary link URL for the 'WordPress News' dashboard widget. + * + * @since 2.3.0 + * + * @param string $link The widget's secondary link URL. + */ + 'link' => apply_filters( 'dashboard_secondary_link', __( 'https://planet.wordpress.org/' ) ), + + /** + * Filter the secondary feed URL for the 'WordPress News' dashboard widget. + * + * @since 2.3.0 + * + * @param string $url The widget's secondary feed URL. + */ + 'url' => apply_filters( 'dashboard_secondary_feed', __( 'https://planet.wordpress.org/feed/' ) ), + + /** + * Filter the secondary link title for the 'WordPress News' dashboard widget. + * + * @since 2.3.0 + * + * @param string $title Title attribute for the widget's secondary link. + */ + 'title' => apply_filters( 'dashboard_secondary_title', __( 'Other WordPress News' ) ), + 'items' => 3, + 'show_summary' => 0, + 'show_author' => 0, + 'show_date' => 0, + ) + ); + + if ( ( ! is_multisite() && is_blog_admin() && current_user_can( 'install_plugins' ) ) || ( is_network_admin() && current_user_can( 'manage_network_plugins' ) && current_user_can( 'install_plugins' ) ) ) { + $feeds['plugins'] = array( + 'link' => '', + 'url' => array( + 'popular' => 'http://wordpress.org/plugins/rss/browse/popular/', + ), + 'title' => '', + 'items' => 1, + 'show_summary' => 0, + 'show_author' => 0, + 'show_date' => 0, + ); + } + + wp_dashboard_cached_rss_widget( 'dashboard_primary', 'wp_dashboard_primary_output', $feeds ); +} + +/** + * Display the WordPress news feeds. + * + * @since 3.8.0 + * + * @param string $widget_id Widget ID. + * @param array $feeds Array of RSS feeds. + */ +function wp_dashboard_primary_output( $widget_id, $feeds ) { + foreach( $feeds as $type => $args ) { + $args['type'] = $type; + echo '
    '; + if ( $type === 'plugins' ) { + wp_dashboard_plugins_output( $args['url'], $args ); + } else { + wp_widget_rss_output( $args['url'], $args ); + } + echo "
    "; + } +} + +/** + * Display plugins text for the WordPress news widget. + * + * @since 2.5.0 + */ +function wp_dashboard_plugins_output( $rss, $args = array() ) { + // Plugin feeds plus link to install them + $popular = fetch_feed( $args['url']['popular'] ); + + if ( false === $plugin_slugs = get_transient( 'plugin_slugs' ) ) { + $plugin_slugs = array_keys( get_plugins() ); + set_transient( 'plugin_slugs', $plugin_slugs, DAY_IN_SECONDS ); + } + + echo '
      '; + + foreach ( array( $popular ) as $feed ) { + if ( is_wp_error( $feed ) || ! $feed->get_item_quantity() ) + continue; + + $items = $feed->get_items(0, 5); + + // Pick a random, non-installed plugin + while ( true ) { + // Abort this foreach loop iteration if there's no plugins left of this type + if ( 0 == count($items) ) + continue 2; + + $item_key = array_rand($items); + $item = $items[$item_key]; + + list($link, $frag) = explode( '#', $item->get_link() ); + + $link = esc_url($link); + if ( preg_match( '|/([^/]+?)/?$|', $link, $matches ) ) + $slug = $matches[1]; + else { + unset( $items[$item_key] ); + continue; + } + + // Is this random plugin's slug already installed? If so, try again. + reset( $plugin_slugs ); + foreach ( $plugin_slugs as $plugin_slug ) { + if ( $slug == substr( $plugin_slug, 0, strlen( $slug ) ) ) { + unset( $items[$item_key] ); + continue 2; + } + } + + // If we get to this point, then the random plugin isn't installed and we can stop the while(). + break; + } + + // Eliminate some common badly formed plugin descriptions + while ( ( null !== $item_key = array_rand($items) ) && false !== strpos( $items[$item_key]->get_description(), 'Plugin Name:' ) ) + unset($items[$item_key]); + + if ( !isset($items[$item_key]) ) + continue; + + $title = esc_html( $item->get_title() ); + + $ilink = wp_nonce_url('plugin-install.php?tab=plugin-information&plugin=' . $slug, 'install-plugin_' . $slug) . '&TB_iframe=true&width=600&height=800'; + echo "
    • " . __( 'Popular Plugin' ) . ": $title (" . __( 'Install' ) . ")
    • "; + + $feed->__destruct(); + unset( $feed ); + } + + echo '
    '; +} + +/** + * Display file upload quota on dashboard. + * + * Runs on the activity_box_end hook in wp_dashboard_right_now(). + * + * @since 3.0.0 + * + * @return bool|null True if not multisite, user can't upload files, or the space check option is disabled. +*/ +function wp_dashboard_quota() { + if ( !is_multisite() || !current_user_can( 'upload_files' ) || get_site_option( 'upload_space_check_disabled' ) ) + return true; + + $quota = get_space_allowed(); + $used = get_space_used(); + + if ( $used > $quota ) + $percentused = '100'; + else + $percentused = ( $used / $quota ) * 100; + $used_class = ( $percentused >= 70 ) ? ' warning' : ''; + $used = round( $used, 2 ); + $percentused = number_format( $percentused ); + + ?> +

    +
    +
      +
    • + %3$s', + esc_url( admin_url( 'upload.php' ) ), + __( 'Manage Uploads' ), + $text + ); ?> +
    • + %3$s', + esc_url( admin_url( 'upload.php' ) ), + __( 'Manage Uploads' ), + $text + ); ?> +
    • +
    +
    + %s. Using an outdated browser makes your computer unsafe. For the best WordPress experience, please update your browser." ), esc_attr( $response['update_url'] ), esc_html( $response['name'] ) ); + } else { + $msg = sprintf( __( "It looks like you're using an old version of %s. For the best WordPress experience, please update your browser." ), esc_attr( $response['update_url'] ), esc_html( $response['name'] ) ); + } + + $browser_nag_class = ''; + if ( !empty( $response['img_src'] ) ) { + $img_src = ( is_ssl() && ! empty( $response['img_src_ssl'] ) )? $response['img_src_ssl'] : $response['img_src']; + + $notice .= '
    '; + $browser_nag_class = ' has-browser-icon'; + } + $notice .= "

    {$msg}

    "; + + $browsehappy = 'http://browsehappy.com/'; + $locale = get_locale(); + if ( 'en_US' !== $locale ) + $browsehappy = add_query_arg( 'locale', $locale, $browsehappy ); + + $notice .= '

    ' . sprintf( __( 'Update %2$s or learn how to browse happy' ), esc_attr( $response['update_url'] ), esc_html( $response['name'] ), esc_url( $browsehappy ) ) . '

    '; + $notice .= '

    ' . __( 'Dismiss' ) . '

    '; + $notice .= '
    '; + } + + /** + * Filter the notice output for the 'Browse Happy' nag meta box. + * + * @since 3.2.0 + * + * @param string $notice The notice content. + * @param array $response An array containing web browser information. + */ + echo apply_filters( 'browse-happy-notice', $notice, $response ); +} + +function dashboard_browser_nag_class( $classes ) { + $response = wp_check_browser_version(); + + if ( $response && $response['insecure'] ) + $classes[] = 'browser-insecure'; + + return $classes; +} + +/** + * Check if the user needs a browser update + * + * @since 3.2.0 + * + * @return array|bool False on failure, array of browser data on success. + */ +function wp_check_browser_version() { + if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) + return false; + + $key = md5( $_SERVER['HTTP_USER_AGENT'] ); + + if ( false === ($response = get_site_transient('browser_' . $key) ) ) { + global $wp_version; + + $options = array( + 'body' => array( 'useragent' => $_SERVER['HTTP_USER_AGENT'] ), + 'user-agent' => 'WordPress/' . $wp_version . '; ' . home_url() + ); + + $response = wp_remote_post( 'http://api.wordpress.org/core/browse-happy/1.1/', $options ); + + if ( is_wp_error( $response ) || 200 != wp_remote_retrieve_response_code( $response ) ) + return false; + + /** + * Response should be an array with: + * 'name' - string - A user friendly browser name + * 'version' - string - The most recent version of the browser + * 'current_version' - string - The version of the browser the user is using + * 'upgrade' - boolean - Whether the browser needs an upgrade + * 'insecure' - boolean - Whether the browser is deemed insecure + * 'upgrade_url' - string - The url to visit to upgrade + * 'img_src' - string - An image representing the browser + * 'img_src_ssl' - string - An image (over SSL) representing the browser + */ + $response = json_decode( wp_remote_retrieve_body( $response ), true ); + + if ( ! is_array( $response ) ) + return false; + + set_site_transient( 'browser_' . $key, $response, WEEK_IN_SECONDS ); + } + + return $response; +} + +/** + * Empty function usable by plugins to output empty dashboard widget (to be populated later by JS). + */ +function wp_dashboard_empty() {} + +/** + * Displays a welcome panel to introduce users to WordPress. + * + * @since 3.3.0 + */ +function wp_welcome_panel() { + ?> +
    +

    +

    +
    +
    + +

    + + + + true ) ) ) > 1 ) ) : ?> +

    change your theme completely' ), admin_url( 'themes.php' ) ); ?>

    + +
    +
    +

    +
      + +
    • ' . __( 'Edit your front page' ) . '', get_edit_post_link( get_option( 'page_on_front' ) ) ); ?>
    • +
    • ' . __( 'Add additional pages' ) . '', admin_url( 'post-new.php?post_type=page' ) ); ?>
    • + +
    • ' . __( 'Edit your front page' ) . '', get_edit_post_link( get_option( 'page_on_front' ) ) ); ?>
    • +
    • ' . __( 'Add additional pages' ) . '', admin_url( 'post-new.php?post_type=page' ) ); ?>
    • +
    • ' . __( 'Add a blog post' ) . '', admin_url( 'post-new.php' ) ); ?>
    • + +
    • ' . __( 'Write your first blog post' ) . '', admin_url( 'post-new.php' ) ); ?>
    • +
    • ' . __( 'Add an About page' ) . '', admin_url( 'post-new.php?post_type=page' ) ); ?>
    • + +
    • ' . __( 'View your site' ) . '', home_url( '/' ) ); ?>
    • +
    +
    +
    +

    +
      + +
    • widgets or menus' ), + admin_url( 'widgets.php' ), admin_url( 'nav-menus.php' ) ); + } elseif ( current_theme_supports( 'widgets' ) ) { + echo '' . __( 'Manage widgets' ) . ''; + } else { + echo '' . __( 'Manage menus' ) . ''; + } + ?>
    • + + +
    • ' . __( 'Turn comments on or off' ) . '', admin_url( 'options-discussion.php' ) ); ?>
    • + +
    • ' . __( 'Learn more about getting started' ) . '', __( 'http://codex.wordpress.org/First_Steps_With_WordPress' ) ); ?>
    • +
    +
    +
    +
    + 0) ); + + if ( $categories ) { + foreach ( $categories as $category ) { + if ( $currentcat != $category->term_id && $parent == $category->parent) { + $pad = str_repeat( '– ', $level ); + $category->name = esc_html( $category->name ); + echo "\n\t"; + wp_dropdown_cats( $currentcat, $currentparent, $category->term_id, $level +1, $categories ); + } + } + } else { + return false; + } +} + +/** + * Register a setting and its sanitization callback + * + * @since 2.7.0 + * @deprecated 3.0.0 + * @deprecated Use register_setting() + * @see register_setting() + * + * @param string $option_group A settings group name. Should correspond to a whitelisted option key name. + * Default whitelisted option key names include "general," "discussion," and "reading," among others. + * @param string $option_name The name of an option to sanitize and save. + * @param callable $sanitize_callback A callback function that sanitizes the option's value. + */ +function add_option_update_handler( $option_group, $option_name, $sanitize_callback = '' ) { + _deprecated_function( __FUNCTION__, '3.0', 'register_setting()' ); + register_setting( $option_group, $option_name, $sanitize_callback ); +} + +/** + * Unregister a setting + * + * @since 2.7.0 + * @deprecated 3.0.0 + * @deprecated Use unregister_setting() + * @see unregister_setting() + * + * @param string $option_group + * @param string $option_name + * @param callable $sanitize_callback + */ +function remove_option_update_handler( $option_group, $option_name, $sanitize_callback = '' ) { + _deprecated_function( __FUNCTION__, '3.0', 'unregister_setting()' ); + unregister_setting( $option_group, $option_name, $sanitize_callback ); +} + +/** + * Determines the language to use for CodePress syntax highlighting. + * + * @since 2.8.0 + * @deprecated 3.0.0 + * + * @param string $filename +**/ +function codepress_get_lang( $filename ) { + _deprecated_function( __FUNCTION__, '3.0' ); + return; +} + +/** + * Adds JavaScript required to make CodePress work on the theme/plugin editors. + * + * @since 2.8.0 + * @deprecated 3.0.0 +**/ +function codepress_footer_js() { + _deprecated_function( __FUNCTION__, '3.0' ); + return; +} + +/** + * Determine whether to use CodePress. + * + * @since 2.8.0 + * @deprecated 3.0.0 +**/ +function use_codepress() { + _deprecated_function( __FUNCTION__, '3.0' ); + return; +} + +/** + * @deprecated 3.1.0 + * + * @return array List of user IDs. + */ +function get_author_user_ids() { + _deprecated_function( __FUNCTION__, '3.1', 'get_users()' ); + + global $wpdb; + if ( !is_multisite() ) + $level_key = $wpdb->get_blog_prefix() . 'user_level'; + else + $level_key = $wpdb->get_blog_prefix() . 'capabilities'; // wpmu site admins don't have user_levels + + return $wpdb->get_col( $wpdb->prepare("SELECT user_id FROM $wpdb->usermeta WHERE meta_key = %s AND meta_value != '0'", $level_key) ); +} + +/** + * @deprecated 3.1.0 + * + * @param int $user_id User ID. + * @return array|bool List of editable authors. False if no editable users. + */ +function get_editable_authors( $user_id ) { + _deprecated_function( __FUNCTION__, '3.1', 'get_users()' ); + + global $wpdb; + + $editable = get_editable_user_ids( $user_id ); + + if ( !$editable ) { + return false; + } else { + $editable = join(',', $editable); + $authors = $wpdb->get_results( "SELECT * FROM $wpdb->users WHERE ID IN ($editable) ORDER BY display_name" ); + } + + return apply_filters('get_editable_authors', $authors); +} + +/** + * @deprecated 3.1.0 + * + * @param int $user_id User ID. + * @param bool $exclude_zeros Optional, default is true. Whether to exclude zeros. + * @return mixed + */ +function get_editable_user_ids( $user_id, $exclude_zeros = true, $post_type = 'post' ) { + _deprecated_function( __FUNCTION__, '3.1', 'get_users()' ); + + global $wpdb; + + if ( ! $user = get_userdata( $user_id ) ) + return array(); + $post_type_obj = get_post_type_object($post_type); + + if ( ! $user->has_cap($post_type_obj->cap->edit_others_posts) ) { + if ( $user->has_cap($post_type_obj->cap->edit_posts) || ! $exclude_zeros ) + return array($user->ID); + else + return array(); + } + + if ( !is_multisite() ) + $level_key = $wpdb->get_blog_prefix() . 'user_level'; + else + $level_key = $wpdb->get_blog_prefix() . 'capabilities'; // wpmu site admins don't have user_levels + + $query = $wpdb->prepare("SELECT user_id FROM $wpdb->usermeta WHERE meta_key = %s", $level_key); + if ( $exclude_zeros ) + $query .= " AND meta_value != '0'"; + + return $wpdb->get_col( $query ); +} + +/** + * @deprecated 3.1.0 + */ +function get_nonauthor_user_ids() { + _deprecated_function( __FUNCTION__, '3.1', 'get_users()' ); + + global $wpdb; + + if ( !is_multisite() ) + $level_key = $wpdb->get_blog_prefix() . 'user_level'; + else + $level_key = $wpdb->get_blog_prefix() . 'capabilities'; // wpmu site admins don't have user_levels + + return $wpdb->get_col( $wpdb->prepare("SELECT user_id FROM $wpdb->usermeta WHERE meta_key = %s AND meta_value = '0'", $level_key) ); +} + +if ( !class_exists('WP_User_Search') ) : +/** + * WordPress User Search class. + * + * @since 2.1.0 + * @deprecated 3.1.0 + */ +class WP_User_Search { + + /** + * {@internal Missing Description}} + * + * @since 2.1.0 + * @access private + * @var mixed + */ + var $results; + + /** + * {@internal Missing Description}} + * + * @since 2.1.0 + * @access private + * @var string + */ + var $search_term; + + /** + * Page number. + * + * @since 2.1.0 + * @access private + * @var int + */ + var $page; + + /** + * Role name that users have. + * + * @since 2.5.0 + * @access private + * @var string + */ + var $role; + + /** + * Raw page number. + * + * @since 2.1.0 + * @access private + * @var int|bool + */ + var $raw_page; + + /** + * Amount of users to display per page. + * + * @since 2.1.0 + * @access public + * @var int + */ + var $users_per_page = 50; + + /** + * {@internal Missing Description}} + * + * @since 2.1.0 + * @access private + * @var int + */ + var $first_user; + + /** + * {@internal Missing Description}} + * + * @since 2.1.0 + * @access private + * @var int + */ + var $last_user; + + /** + * {@internal Missing Description}} + * + * @since 2.1.0 + * @access private + * @var string + */ + var $query_limit; + + /** + * {@internal Missing Description}} + * + * @since 3.0.0 + * @access private + * @var string + */ + var $query_orderby; + + /** + * {@internal Missing Description}} + * + * @since 3.0.0 + * @access private + * @var string + */ + var $query_from; + + /** + * {@internal Missing Description}} + * + * @since 3.0.0 + * @access private + * @var string + */ + var $query_where; + + /** + * {@internal Missing Description}} + * + * @since 2.1.0 + * @access private + * @var int + */ + var $total_users_for_query = 0; + + /** + * {@internal Missing Description}} + * + * @since 2.1.0 + * @access private + * @var bool + */ + var $too_many_total_users = false; + + /** + * {@internal Missing Description}} + * + * @since 2.1.0 + * @access private + * @var WP_Error + */ + var $search_errors; + + /** + * {@internal Missing Description}} + * + * @since 2.7.0 + * @access private + * @var string + */ + var $paging_text; + + /** + * PHP4 Constructor - Sets up the object properties. + * + * @since 2.1.0 + * + * @param string $search_term Search terms string. + * @param int $page Optional. Page ID. + * @param string $role Role name. + * @return WP_User_Search + */ + function WP_User_Search ($search_term = '', $page = '', $role = '') { + _deprecated_function( __FUNCTION__, '3.1', 'WP_User_Query' ); + + $this->search_term = wp_unslash( $search_term ); + $this->raw_page = ( '' == $page ) ? false : (int) $page; + $this->page = (int) ( '' == $page ) ? 1 : $page; + $this->role = $role; + + $this->prepare_query(); + $this->query(); + $this->do_paging(); + } + + /** + * {@internal Missing Short Description}} + * + * {@internal Missing Long Description}} + * + * @since 2.1.0 + * @access public + */ + function prepare_query() { + global $wpdb; + $this->first_user = ($this->page - 1) * $this->users_per_page; + + $this->query_limit = $wpdb->prepare(" LIMIT %d, %d", $this->first_user, $this->users_per_page); + $this->query_orderby = ' ORDER BY user_login'; + + $search_sql = ''; + if ( $this->search_term ) { + $searches = array(); + $search_sql = 'AND ('; + foreach ( array('user_login', 'user_nicename', 'user_email', 'user_url', 'display_name') as $col ) + $searches[] = $wpdb->prepare( $col . ' LIKE %s', '%' . like_escape($this->search_term) . '%' ); + $search_sql .= implode(' OR ', $searches); + $search_sql .= ')'; + } + + $this->query_from = " FROM $wpdb->users"; + $this->query_where = " WHERE 1=1 $search_sql"; + + if ( $this->role ) { + $this->query_from .= " INNER JOIN $wpdb->usermeta ON $wpdb->users.ID = $wpdb->usermeta.user_id"; + $this->query_where .= $wpdb->prepare(" AND $wpdb->usermeta.meta_key = '{$wpdb->prefix}capabilities' AND $wpdb->usermeta.meta_value LIKE %s", '%' . $this->role . '%'); + } elseif ( is_multisite() ) { + $level_key = $wpdb->prefix . 'capabilities'; // wpmu site admins don't have user_levels + $this->query_from .= ", $wpdb->usermeta"; + $this->query_where .= " AND $wpdb->users.ID = $wpdb->usermeta.user_id AND meta_key = '{$level_key}'"; + } + + do_action_ref_array( 'pre_user_search', array( &$this ) ); + } + + /** + * {@internal Missing Short Description}} + * + * {@internal Missing Long Description}} + * + * @since 2.1.0 + * @access public + */ + function query() { + global $wpdb; + + $this->results = $wpdb->get_col("SELECT DISTINCT($wpdb->users.ID)" . $this->query_from . $this->query_where . $this->query_orderby . $this->query_limit); + + if ( $this->results ) + $this->total_users_for_query = $wpdb->get_var("SELECT COUNT(DISTINCT($wpdb->users.ID))" . $this->query_from . $this->query_where); // no limit + else + $this->search_errors = new WP_Error('no_matching_users_found', __('No matching users were found!')); + } + + /** + * {@internal Missing Short Description}} + * + * {@internal Missing Long Description}} + * + * @since 2.1.0 + * @access public + */ + function prepare_vars_for_template_usage() {} + + /** + * {@internal Missing Short Description}} + * + * {@internal Missing Long Description}} + * + * @since 2.1.0 + * @access public + */ + function do_paging() { + if ( $this->total_users_for_query > $this->users_per_page ) { // have to page the results + $args = array(); + if( ! empty($this->search_term) ) + $args['usersearch'] = urlencode($this->search_term); + if( ! empty($this->role) ) + $args['role'] = urlencode($this->role); + + $this->paging_text = paginate_links( array( + 'total' => ceil($this->total_users_for_query / $this->users_per_page), + 'current' => $this->page, + 'base' => 'users.php?%_%', + 'format' => 'userspage=%#%', + 'add_args' => $args + ) ); + if ( $this->paging_text ) { + $this->paging_text = sprintf( '' . __( 'Displaying %s–%s of %s' ) . '%s', + number_format_i18n( ( $this->page - 1 ) * $this->users_per_page + 1 ), + number_format_i18n( min( $this->page * $this->users_per_page, $this->total_users_for_query ) ), + number_format_i18n( $this->total_users_for_query ), + $this->paging_text + ); + } + } + } + + /** + * {@internal Missing Short Description}} + * + * {@internal Missing Long Description}} + * + * @since 2.1.0 + * @access public + * + * @return array + */ + function get_results() { + return (array) $this->results; + } + + /** + * Displaying paging text. + * + * @see do_paging() Builds paging text. + * + * @since 2.1.0 + * @access public + */ + function page_links() { + echo $this->paging_text; + } + + /** + * Whether paging is enabled. + * + * @see do_paging() Builds paging text. + * + * @since 2.1.0 + * @access public + * + * @return bool + */ + function results_are_paged() { + if ( $this->paging_text ) + return true; + return false; + } + + /** + * Whether there are search terms. + * + * @since 2.1.0 + * @access public + * + * @return bool + */ + function is_search() { + if ( $this->search_term ) + return true; + return false; + } +} +endif; + +/** + * Retrieve editable posts from other users. + * + * @deprecated 3.1.0 + * + * @param int $user_id User ID to not retrieve posts from. + * @param string $type Optional, defaults to 'any'. Post type to retrieve, can be 'draft' or 'pending'. + * @return array List of posts from others. + */ +function get_others_unpublished_posts($user_id, $type='any') { + _deprecated_function( __FUNCTION__, '3.1' ); + + global $wpdb; + + $editable = get_editable_user_ids( $user_id ); + + if ( in_array($type, array('draft', 'pending')) ) + $type_sql = " post_status = '$type' "; + else + $type_sql = " ( post_status = 'draft' OR post_status = 'pending' ) "; + + $dir = ( 'pending' == $type ) ? 'ASC' : 'DESC'; + + if ( !$editable ) { + $other_unpubs = ''; + } else { + $editable = join(',', $editable); + $other_unpubs = $wpdb->get_results( $wpdb->prepare("SELECT ID, post_title, post_author FROM $wpdb->posts WHERE post_type = 'post' AND $type_sql AND post_author IN ($editable) AND post_author != %d ORDER BY post_modified $dir", $user_id) ); + } + + return apply_filters('get_others_drafts', $other_unpubs); +} + +/** + * Retrieve drafts from other users. + * + * @deprecated 3.1.0 + * + * @param int $user_id User ID. + * @return array List of drafts from other users. + */ +function get_others_drafts($user_id) { + _deprecated_function( __FUNCTION__, '3.1' ); + + return get_others_unpublished_posts($user_id, 'draft'); +} + +/** + * Retrieve pending review posts from other users. + * + * @deprecated 3.1.0 + * + * @param int $user_id User ID. + * @return array List of posts with pending review post type from other users. + */ +function get_others_pending($user_id) { + _deprecated_function( __FUNCTION__, '3.1' ); + + return get_others_unpublished_posts($user_id, 'pending'); +} + +/** + * Output the QuickPress dashboard widget. + * + * @since 3.0.0 + * @deprecated 3.2.0 + * @deprecated Use wp_dashboard_quick_press() + * @see wp_dashboard_quick_press() + */ +function wp_dashboard_quick_press_output() { + _deprecated_function( __FUNCTION__, '3.2', 'wp_dashboard_quick_press()' ); + wp_dashboard_quick_press(); +} + +/** + * @since 2.7.0 + * @deprecated 3.3.0 + * @deprecated Use wp_editor() + * @see wp_editor() + */ +function wp_tiny_mce( $teeny = false, $settings = false ) { + _deprecated_function( __FUNCTION__, '3.3', 'wp_editor()' ); + + static $num = 1; + + if ( ! class_exists('_WP_Editors' ) ) + require_once( ABSPATH . WPINC . '/class-wp-editor.php' ); + + $editor_id = 'content' . $num++; + + $set = array( + 'teeny' => $teeny, + 'tinymce' => $settings ? $settings : true, + 'quicktags' => false + ); + + $set = _WP_Editors::parse_settings($editor_id, $set); + _WP_Editors::editor_settings($editor_id, $set); +} + +/** + * @deprecated 3.3.0 + * @deprecated Use wp_editor() + * @see wp_editor() + */ +function wp_preload_dialogs() { + _deprecated_function( __FUNCTION__, '3.3', 'wp_editor()' ); +} + +/** + * @deprecated 3.3.0 + * @deprecated Use wp_editor() + * @see wp_editor() + */ +function wp_print_editor_js() { + _deprecated_function( __FUNCTION__, '3.3', 'wp_editor()' ); +} + +/** + * @deprecated 3.3.0 + * @deprecated Use wp_editor() + * @see wp_editor() + */ +function wp_quicktags() { + _deprecated_function( __FUNCTION__, '3.3', 'wp_editor()' ); +} + +/** + * Returns the screen layout options. + * + * @since 2.8.0 + * @deprecated 3.3.0 + * @deprecated Use $current_screen->render_screen_layout() + * @see WP_Screen::render_screen_layout() + */ +function screen_layout( $screen ) { + _deprecated_function( __FUNCTION__, '3.3', '$current_screen->render_screen_layout()' ); + + $current_screen = get_current_screen(); + + if ( ! $current_screen ) + return ''; + + ob_start(); + $current_screen->render_screen_layout(); + return ob_get_clean(); +} + +/** + * Returns the screen's per-page options. + * + * @since 2.8.0 + * @deprecated 3.3.0 + * @deprecated Use $current_screen->render_per_page_options() + * @see WP_Screen::render_per_page_options() + */ +function screen_options( $screen ) { + _deprecated_function( __FUNCTION__, '3.3', '$current_screen->render_per_page_options()' ); + + $current_screen = get_current_screen(); + + if ( ! $current_screen ) + return ''; + + ob_start(); + $current_screen->render_per_page_options(); + return ob_get_clean(); +} + +/** + * Renders the screen's help. + * + * @since 2.7.0 + * @deprecated 3.3.0 + * @deprecated Use $current_screen->render_screen_meta() + * @see WP_Screen::render_screen_meta() + */ +function screen_meta( $screen ) { + $current_screen = get_current_screen(); + $current_screen->render_screen_meta(); +} + +/** + * Favorite actions were deprecated in version 3.2. Use the admin bar instead. + * + * @since 2.7.0 + * @deprecated 3.2.0 + */ +function favorite_actions() { + _deprecated_function( __FUNCTION__, '3.2', 'WP_Admin_Bar' ); +} + +function media_upload_image() { + _deprecated_function( __FUNCTION__, '3.3', 'wp_media_upload_handler()' ); + return wp_media_upload_handler(); +} + +function media_upload_audio() { + _deprecated_function( __FUNCTION__, '3.3', 'wp_media_upload_handler()' ); + return wp_media_upload_handler(); +} + +function media_upload_video() { + _deprecated_function( __FUNCTION__, '3.3', 'wp_media_upload_handler()' ); + return wp_media_upload_handler(); +} + +function media_upload_file() { + _deprecated_function( __FUNCTION__, '3.3', 'wp_media_upload_handler()' ); + return wp_media_upload_handler(); +} + +function type_url_form_image() { + _deprecated_function( __FUNCTION__, '3.3', "wp_media_insert_url_form('image')" ); + return wp_media_insert_url_form( 'image' ); +} + +function type_url_form_audio() { + _deprecated_function( __FUNCTION__, '3.3', "wp_media_insert_url_form('audio')" ); + return wp_media_insert_url_form( 'audio' ); +} + +function type_url_form_video() { + _deprecated_function( __FUNCTION__, '3.3', "wp_media_insert_url_form('video')" ); + return wp_media_insert_url_form( 'video' ); +} + +function type_url_form_file() { + _deprecated_function( __FUNCTION__, '3.3', "wp_media_insert_url_form('file')" ); + return wp_media_insert_url_form( 'file' ); +} + +/** + * Add contextual help text for a page. + * + * Creates an 'Overview' help tab. + * + * @since 2.7.0 + * @deprecated 3.3.0 + * @deprecated Use get_current_screen()->add_help_tab() + * @see WP_Screen + * + * @param string $screen The handle for the screen to add help to. This is usually the hook name returned by the add_*_page() functions. + * @param string $help The content of an 'Overview' help tab. + */ +function add_contextual_help( $screen, $help ) { + _deprecated_function( __FUNCTION__, '3.3', 'get_current_screen()->add_help_tab()' ); + + if ( is_string( $screen ) ) + $screen = convert_to_screen( $screen ); + + WP_Screen::add_old_compat_help( $screen, $help ); +} + +/** + * Get the allowed themes for the current blog. + * + * @since 3.0.0 + * @deprecated 3.4.0 + * @deprecated Use wp_get_themes() + * @see wp_get_themes() + * + * @return array $themes Array of allowed themes. + */ +function get_allowed_themes() { + _deprecated_function( __FUNCTION__, '3.4', "wp_get_themes( array( 'allowed' => true ) )" ); + + $themes = wp_get_themes( array( 'allowed' => true ) ); + + $wp_themes = array(); + foreach ( $themes as $theme ) { + $wp_themes[ $theme->get('Name') ] = $theme; + } + + return $wp_themes; +} + +/** + * {@internal Missing Short Description}} + * + * @since 1.5.0 + * @deprecated 3.4.0 + * + * @return array + */ +function get_broken_themes() { + _deprecated_function( __FUNCTION__, '3.4', "wp_get_themes( array( 'errors' => true )" ); + + $themes = wp_get_themes( array( 'errors' => true ) ); + $broken = array(); + foreach ( $themes as $theme ) { + $name = $theme->get('Name'); + $broken[ $name ] = array( + 'Name' => $name, + 'Title' => $name, + 'Description' => $theme->errors()->get_error_message(), + ); + } + return $broken; +} + +/** + * {@internal Missing Short Description}} + * + * @since 2.0.0 + * @deprecated 3.4.0 + * + * @return WP_Theme + */ +function current_theme_info() { + _deprecated_function( __FUNCTION__, '3.4', 'wp_get_theme()' ); + + return wp_get_theme(); +} + +/** + * This was once used to display an 'Insert into Post' button. Now it is deprecated and stubbed. + * + * @deprecated 3.5.0 + */ +function _insert_into_post_button( $type ) { + _deprecated_function( __FUNCTION__, '3.5' ); +} + +/** + * This was once used to display a media button. Now it is deprecated and stubbed. + * + * @deprecated 3.5.0 + */ +function _media_button($title, $icon, $type, $id) { + _deprecated_function( __FUNCTION__, '3.5' ); +} + +/** + * Get an existing post and format it for editing. + * + * @since 2.0.0 + * @deprecated 3.5.0 + * + * @param int $id + * @return object + */ +function get_post_to_edit( $id ) { + _deprecated_function( __FUNCTION__, '3.5', 'get_post()' ); + + return get_post( $id, OBJECT, 'edit' ); +} + +/** + * Get the default page information to use. + * + * @since 2.5.0 + * @deprecated 3.5.0 + * @deprecated Use get_default_post_to_edit() + * + * @return WP_Post Post object containing all the default post data as attributes + */ +function get_default_page_to_edit() { + _deprecated_function( __FUNCTION__, '3.5', "get_default_post_to_edit( 'page' )" ); + + $page = get_default_post_to_edit(); + $page->post_type = 'page'; + return $page; +} + +/** + * This was once used to create a thumbnail from an Image given a maximum side size. + * + * @since 1.2.0 + * @deprecated 3.5.0 + * @deprecated Use image_resize() + * @see image_resize() + * + * @param mixed $file Filename of the original image, Or attachment id. + * @param int $max_side Maximum length of a single side for the thumbnail. + * @param mixed $deprecated Never used. + * @return string Thumbnail path on success, Error string on failure. + */ +function wp_create_thumbnail( $file, $max_side, $deprecated = '' ) { + _deprecated_function( __FUNCTION__, '3.5', 'image_resize()' ); + return apply_filters( 'wp_create_thumbnail', image_resize( $file, $max_side, $max_side ) ); +} + +/** + * This was once used to display a metabox for the nav menu theme locations. + * + * Deprecated in favor of a 'Manage Locations' tab added to nav menus management screen. + * + * @since 3.0.0 + * @deprecated 3.6.0 + */ +function wp_nav_menu_locations_meta_box() { + _deprecated_function( __FUNCTION__, '3.6' ); +} + +/** + * This was once used to kick-off the Core Updater. + * + * Deprecated in favor of instantating a Core_Upgrader instance directly, + * and calling the 'upgrade' method. + * + * @since 2.7.0 + * @deprecated 3.7.0 + * @see Core_Upgrader + */ +function wp_update_core($current, $feedback = '') { + _deprecated_function( __FUNCTION__, '3.7', 'new Core_Upgrader();' ); + + if ( !empty($feedback) ) + add_filter('update_feedback', $feedback); + + include( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' ); + $upgrader = new Core_Upgrader(); + return $upgrader->upgrade($current); + +} + +/** + * This was once used to kick-off the Plugin Updater. + * + * Deprecated in favor of instantating a Plugin_Upgrader instance directly, + * and calling the 'upgrade' method. + * Unused since 2.8.0. + * + * @since 2.5.0 + * @deprecated 3.7.0 + * @see Plugin_Upgrader + */ +function wp_update_plugin($plugin, $feedback = '') { + _deprecated_function( __FUNCTION__, '3.7', 'new Plugin_Upgrader();' ); + + if ( !empty($feedback) ) + add_filter('update_feedback', $feedback); + + include( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' ); + $upgrader = new Plugin_Upgrader(); + return $upgrader->upgrade($plugin); +} + +/** + * This was once used to kick-off the Theme Updater. + * + * Deprecated in favor of instantating a Theme_Upgrader instance directly, + * and calling the 'upgrade' method. + * Unused since 2.8.0. + * + * @since 2.7.0 + * @deprecated 3.7.0 + * @see Theme_Upgrader + */ +function wp_update_theme($theme, $feedback = '') { + _deprecated_function( __FUNCTION__, '3.7', 'new Theme_Upgrader();' ); + + if ( !empty($feedback) ) + add_filter('update_feedback', $feedback); + + include( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' ); + $upgrader = new Theme_Upgrader(); + return $upgrader->upgrade($theme); +} + +/** + * This was once used to display attachment links. Now it is deprecated and stubbed. + * + * {@internal Missing Short Description}} + * + * @since 2.0.0 + * @deprecated 3.7.0 + * + * @param int|bool $id + */ +function the_attachment_links( $id = false ) { + _deprecated_function( __FUNCTION__, '3.7' ); +} + +/**#@+ + * Displays a screen icon. + * + * @since 2.7.0 + * @since 3.8.0 Screen icons are no longer used in WordPress. This function no longer produces output. + * @deprecated 3.8.0 + */ +function screen_icon() { + echo get_screen_icon(); +} +function get_screen_icon() { + return ''; +} +/**#@-*/ + +/**#@+ + * Deprecated dashboard widget controls. + * + * @since 2.5.0 + * @deprecated 3.8.0 + */ +function wp_dashboard_incoming_links_output() {} +function wp_dashboard_secondary_output() {} +/**#@-*/ + +/**#@+ + * Deprecated dashboard widget controls. + * + * @since 2.7.0 + * @deprecated 3.8.0 + */ +function wp_dashboard_incoming_links() {} +function wp_dashboard_incoming_links_control() {} +function wp_dashboard_plugins() {} +function wp_dashboard_primary_control() {} +function wp_dashboard_recent_comments_control() {} +function wp_dashboard_secondary() {} +function wp_dashboard_secondary_control() {} +/**#@-*/ + +/** + * This was once used to move child posts to a new parent. + * + * @since 2.3.0 + * @deprecated 3.9.0 + * @access private + * + * @param int $old_ID + * @param int $new_ID + */ +function _relocate_children( $old_ID, $new_ID ) { + _deprecated_function( __FUNCTION__, '3.9' ); +} diff --git a/wp-admin/includes/export.php b/wp-admin/includes/export.php new file mode 100644 index 0000000..1ffc1d3 --- /dev/null +++ b/wp-admin/includes/export.php @@ -0,0 +1,510 @@ + 'all', 'author' => false, 'category' => false, + 'start_date' => false, 'end_date' => false, 'status' => false, + ); + $args = wp_parse_args( $args, $defaults ); + + /** + * Fires at the beginning of an export, before any headers are sent. + * + * @since 2.3.0 + * + * @param array $args An array of export arguments. + */ + do_action( 'export_wp', $args ); + + $sitename = sanitize_key( get_bloginfo( 'name' ) ); + if ( ! empty($sitename) ) $sitename .= '.'; + $filename = $sitename . 'wordpress.' . date( 'Y-m-d' ) . '.xml'; + + header( 'Content-Description: File Transfer' ); + header( 'Content-Disposition: attachment; filename=' . $filename ); + header( 'Content-Type: text/xml; charset=' . get_option( 'blog_charset' ), true ); + + if ( 'all' != $args['content'] && post_type_exists( $args['content'] ) ) { + $ptype = get_post_type_object( $args['content'] ); + if ( ! $ptype->can_export ) + $args['content'] = 'post'; + + $where = $wpdb->prepare( "{$wpdb->posts}.post_type = %s", $args['content'] ); + } else { + $post_types = get_post_types( array( 'can_export' => true ) ); + $esses = array_fill( 0, count($post_types), '%s' ); + $where = $wpdb->prepare( "{$wpdb->posts}.post_type IN (" . implode( ',', $esses ) . ')', $post_types ); + } + + if ( $args['status'] && ( 'post' == $args['content'] || 'page' == $args['content'] ) ) + $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_status = %s", $args['status'] ); + else + $where .= " AND {$wpdb->posts}.post_status != 'auto-draft'"; + + $join = ''; + if ( $args['category'] && 'post' == $args['content'] ) { + if ( $term = term_exists( $args['category'], 'category' ) ) { + $join = "INNER JOIN {$wpdb->term_relationships} ON ({$wpdb->posts}.ID = {$wpdb->term_relationships}.object_id)"; + $where .= $wpdb->prepare( " AND {$wpdb->term_relationships}.term_taxonomy_id = %d", $term['term_taxonomy_id'] ); + } + } + + if ( 'post' == $args['content'] || 'page' == $args['content'] ) { + if ( $args['author'] ) + $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_author = %d", $args['author'] ); + + if ( $args['start_date'] ) + $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_date >= %s", date( 'Y-m-d', strtotime($args['start_date']) ) ); + + if ( $args['end_date'] ) + $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_date < %s", date( 'Y-m-d', strtotime('+1 month', strtotime($args['end_date'])) ) ); + } + + // Grab a snapshot of post IDs, just in case it changes during the export. + $post_ids = $wpdb->get_col( "SELECT ID FROM {$wpdb->posts} $join WHERE $where" ); + + /* + * Get the requested terms ready, empty unless posts filtered by category + * or all content. + */ + $cats = $tags = $terms = array(); + if ( isset( $term ) && $term ) { + $cat = get_term( $term['term_id'], 'category' ); + $cats = array( $cat->term_id => $cat ); + unset( $term, $cat ); + } else if ( 'all' == $args['content'] ) { + $categories = (array) get_categories( array( 'get' => 'all' ) ); + $tags = (array) get_tags( array( 'get' => 'all' ) ); + + $custom_taxonomies = get_taxonomies( array( '_builtin' => false ) ); + $custom_terms = (array) get_terms( $custom_taxonomies, array( 'get' => 'all' ) ); + + // Put categories in order with no child going before its parent. + while ( $cat = array_shift( $categories ) ) { + if ( $cat->parent == 0 || isset( $cats[$cat->parent] ) ) + $cats[$cat->term_id] = $cat; + else + $categories[] = $cat; + } + + // Put terms in order with no child going before its parent. + while ( $t = array_shift( $custom_terms ) ) { + if ( $t->parent == 0 || isset( $terms[$t->parent] ) ) + $terms[$t->term_id] = $t; + else + $custom_terms[] = $t; + } + + unset( $categories, $custom_taxonomies, $custom_terms ); + } + + /** + * Wrap given string in XML CDATA tag. + * + * @since 2.1.0 + * + * @param string $str String to wrap in XML CDATA tag. + * @return string + */ + function wxr_cdata( $str ) { + if ( seems_utf8( $str ) == false ) + $str = utf8_encode( $str ); + + // $str = ent2ncr(esc_html($str)); + $str = '', ']]]]>', $str ) . ']]>'; + + return $str; + } + + /** + * Return the URL of the site + * + * @since 2.5.0 + * + * @return string Site URL. + */ + function wxr_site_url() { + // Multisite: the base URL. + if ( is_multisite() ) + return network_home_url(); + // WordPress (single site): the blog URL. + else + return get_bloginfo_rss( 'url' ); + } + + /** + * Output a cat_name XML tag from a given category object + * + * @since 2.1.0 + * + * @param object $category Category Object + */ + function wxr_cat_name( $category ) { + if ( empty( $category->name ) ) + return; + + echo '' . wxr_cdata( $category->name ) . ''; + } + + /** + * Output a category_description XML tag from a given category object + * + * @since 2.1.0 + * + * @param object $category Category Object + */ + function wxr_category_description( $category ) { + if ( empty( $category->description ) ) + return; + + echo '' . wxr_cdata( $category->description ) . ''; + } + + /** + * Output a tag_name XML tag from a given tag object + * + * @since 2.3.0 + * + * @param object $tag Tag Object + */ + function wxr_tag_name( $tag ) { + if ( empty( $tag->name ) ) + return; + + echo '' . wxr_cdata( $tag->name ) . ''; + } + + /** + * Output a tag_description XML tag from a given tag object + * + * @since 2.3.0 + * + * @param object $tag Tag Object + */ + function wxr_tag_description( $tag ) { + if ( empty( $tag->description ) ) + return; + + echo '' . wxr_cdata( $tag->description ) . ''; + } + + /** + * Output a term_name XML tag from a given term object + * + * @since 2.9.0 + * + * @param object $term Term Object + */ + function wxr_term_name( $term ) { + if ( empty( $term->name ) ) + return; + + echo '' . wxr_cdata( $term->name ) . ''; + } + + /** + * Output a term_description XML tag from a given term object + * + * @since 2.9.0 + * + * @param object $term Term Object + */ + function wxr_term_description( $term ) { + if ( empty( $term->description ) ) + return; + + echo '' . wxr_cdata( $term->description ) . ''; + } + + /** + * Output list of authors with posts + * + * @since 3.1.0 + * + * @param array $post_ids Array of post IDs to filter the query by. Optional. + */ + function wxr_authors_list( array $post_ids = null ) { + global $wpdb; + + if ( !empty( $post_ids ) ) { + $post_ids = array_map( 'absint', $post_ids ); + $and = 'AND ID IN ( ' . implode( ', ', $post_ids ) . ')'; + } else { + $and = ''; + } + + $authors = array(); + $results = $wpdb->get_results( "SELECT DISTINCT post_author FROM $wpdb->posts WHERE post_status != 'auto-draft' $and" ); + foreach ( (array) $results as $result ) + $authors[] = get_userdata( $result->post_author ); + + $authors = array_filter( $authors ); + + foreach ( $authors as $author ) { + echo "\t"; + echo '' . $author->ID . ''; + echo '' . $author->user_login . ''; + echo '' . $author->user_email . ''; + echo '' . wxr_cdata( $author->display_name ) . ''; + echo '' . wxr_cdata( $author->user_firstname ) . ''; + echo '' . wxr_cdata( $author->user_lastname ) . ''; + echo "\n"; + } + } + + /** + * Ouput all navigation menu terms + * + * @since 3.1.0 + */ + function wxr_nav_menu_terms() { + $nav_menus = wp_get_nav_menus(); + if ( empty( $nav_menus ) || ! is_array( $nav_menus ) ) + return; + + foreach ( $nav_menus as $menu ) { + echo "\t{$menu->term_id}nav_menu{$menu->slug}"; + wxr_term_name( $menu ); + echo "\n"; + } + } + + /** + * Output list of taxonomy terms, in XML tag format, associated with a post + * + * @since 2.3.0 + */ + function wxr_post_taxonomy() { + $post = get_post(); + + $taxonomies = get_object_taxonomies( $post->post_type ); + if ( empty( $taxonomies ) ) + return; + $terms = wp_get_object_terms( $post->ID, $taxonomies ); + + foreach ( (array) $terms as $term ) { + echo "\t\ttaxonomy}\" nicename=\"{$term->slug}\">" . wxr_cdata( $term->name ) . "\n"; + } + } + + function wxr_filter_postmeta( $return_me, $meta_key ) { + if ( '_edit_lock' == $meta_key ) + $return_me = true; + return $return_me; + } + add_filter( 'wxr_export_skip_postmeta', 'wxr_filter_postmeta', 10, 2 ); + + echo '\n"; + + ?> + + + + + + + + + + + + + + + + + + + + + + <?php bloginfo_rss( 'name' ); ?> + + + + + + + + + + + + term_id ?>slug; ?>parent ? $cats[$c->parent]->slug : ''; ?> + + + term_id ?>slug; ?> + + + term_id ?>taxonomy; ?>slug; ?>parent ? $terms[$t->parent]->slug : ''; ?> + + + + + +in_the_loop = true; + + // Fetch 20 posts at a time rather than loading the entire table into memory. + while ( $next_posts = array_splice( $post_ids, 0, 20 ) ) { + $where = 'WHERE ID IN (' . join( ',', $next_posts ) . ')'; + $posts = $wpdb->get_results( "SELECT * FROM {$wpdb->posts} $where" ); + + // Begin Loop. + foreach ( $posts as $post ) { + setup_postdata( $post ); + $is_sticky = is_sticky( $post->ID ) ? 1 : 0; +?> + + <?php + /** This filter is documented in wp-includes/feed.php */ + echo apply_filters( 'the_title_rss', $post->post_title ); + ?> + + + + + + post_content ) ); + ?> + post_excerpt ) ); + ?> + ID; ?> + post_date; ?> + post_date_gmt; ?> + comment_status; ?> + ping_status; ?> + post_name; ?> + post_status; ?> + post_parent; ?> + menu_order; ?> + post_type; ?> + post_password; ?> + +post_type == 'attachment' ) : ?> + ID ); ?> + + +get_results( $wpdb->prepare( "SELECT * FROM $wpdb->postmeta WHERE post_id = %d", $post->ID ) ); + foreach ( $postmeta as $meta ) : + /** + * Filter whether to selectively skip post meta used for WXR exports. + * + * Returning a truthy value to the filter will skip the current meta + * object from being exported. + * + * @since 3.3.0 + * + * @param bool $skip Whether to skip the current post meta. Default false. + * @param string $meta_key Current meta key. + * @param object $meta Current meta object. + */ + if ( apply_filters( 'wxr_export_skip_postmeta', false, $meta->meta_key, $meta ) ) + continue; + ?> + + meta_key; ?> + meta_value ); ?> + +get_results( $wpdb->prepare( "SELECT * FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_approved <> 'spam'", $post->ID ) ); + foreach ( $comments as $c ) : ?> + + comment_ID; ?> + comment_author ); ?> + comment_author_email; ?> + comment_author_url ); ?> + comment_author_IP; ?> + comment_date; ?> + comment_date_gmt; ?> + comment_content ) ?> + comment_approved; ?> + comment_type; ?> + comment_parent; ?> + user_id; ?> +get_results( $wpdb->prepare( "SELECT * FROM $wpdb->commentmeta WHERE comment_id = %d", $c->comment_ID ) ); + foreach ( $c_meta as $meta ) : + /** + * Filter whether to selectively skip comment meta used for WXR exports. + * + * Returning a truthy value to the filter will skip the current meta + * object from being exported. + * + * @since 4.0.0 + * + * @param bool $skip Whether to skip the current comment meta. Default false. + * @param string $meta_key Current meta key. + * @param object $meta Current meta object. + */ + if ( apply_filters( 'wxr_export_skip_commentmeta', false, $meta->meta_key, $meta ) ) { + continue; + } + ?> + + meta_key; ?> + meta_value ); ?> + + + + + + + + + __( 'Main Index Template' ), + 'style.css' => __( 'Stylesheet' ), + 'editor-style.css' => __( 'Visual Editor Stylesheet' ), + 'editor-style-rtl.css' => __( 'Visual Editor RTL Stylesheet' ), + 'rtl.css' => __( 'RTL Stylesheet' ), + 'comments.php' => __( 'Comments' ), + 'comments-popup.php' => __( 'Popup Comments' ), + 'footer.php' => __( 'Footer' ), + 'header.php' => __( 'Header' ), + 'sidebar.php' => __( 'Sidebar' ), + 'archive.php' => __( 'Archives' ), + 'author.php' => __( 'Author Template' ), + 'tag.php' => __( 'Tag Template' ), + 'category.php' => __( 'Category Template' ), + 'page.php' => __( 'Page Template' ), + 'search.php' => __( 'Search Results' ), + 'searchform.php' => __( 'Search Form' ), + 'single.php' => __( 'Single Post' ), + '404.php' => __( '404 Template' ), + 'link.php' => __( 'Links Template' ), + 'functions.php' => __( 'Theme Functions' ), + 'attachment.php' => __( 'Attachment Template' ), + 'image.php' => __('Image Attachment Template'), + 'video.php' => __('Video Attachment Template'), + 'audio.php' => __('Audio Attachment Template'), + 'application.php' => __('Application Attachment Template'), + 'my-hacks.php' => __( 'my-hacks.php (legacy hacks support)' ), + '.htaccess' => __( '.htaccess (for rewrite rules )' ), + // Deprecated files + 'wp-layout.css' => __( 'Stylesheet' ), + 'wp-comments.php' => __( 'Comments Template' ), + 'wp-comments-popup.php' => __( 'Popup Comments Template' ), +); + +/** + * Get the description for standard WordPress theme files and other various standard + * WordPress files + * + * @since 1.5.0 + * + * @uses $wp_file_descriptions + * @param string $file Filesystem path or filename + * @return string Description of file from $wp_file_descriptions or basename of $file if description doesn't exist + */ +function get_file_description( $file ) { + global $wp_file_descriptions; + + if ( isset( $wp_file_descriptions[basename( $file )] ) ) { + return $wp_file_descriptions[basename( $file )]; + } + elseif ( file_exists( $file ) && is_file( $file ) ) { + $template_data = implode( '', file( $file ) ); + if ( preg_match( '|Template Name:(.*)$|mi', $template_data, $name )) + return sprintf( __( '%s Page Template' ), _cleanup_header_comment($name[1]) ); + } + + return trim( basename( $file ) ); +} + +/** + * Get the absolute filesystem path to the root of the WordPress installation + * + * @since 1.5.0 + * + * @return string Full filesystem path to the root of the WordPress installation + */ +function get_home_path() { + $home = set_url_scheme( get_option( 'home' ), 'http' ); + $siteurl = set_url_scheme( get_option( 'siteurl' ), 'http' ); + if ( ! empty( $home ) && 0 !== strcasecmp( $home, $siteurl ) ) { + $wp_path_rel_to_home = str_ireplace( $home, '', $siteurl ); /* $siteurl - $home */ + $pos = strripos( str_replace( '\\', '/', $_SERVER['SCRIPT_FILENAME'] ), trailingslashit( $wp_path_rel_to_home ) ); + $home_path = substr( $_SERVER['SCRIPT_FILENAME'], 0, $pos ); + $home_path = trailingslashit( $home_path ); + } else { + $home_path = ABSPATH; + } + + return str_replace( '\\', '/', $home_path ); +} + +/** + * Returns a listing of all files in the specified folder and all subdirectories up to 100 levels deep. + * The depth of the recursiveness can be controlled by the $levels param. + * + * @since 2.6.0 + * + * @param string $folder Optional. Full path to folder. Default empty. + * @param int $levels Optional. Levels of folders to follow, Default 100 (PHP Loop limit). + * @return bool|array False on failure, Else array of files + */ +function list_files( $folder = '', $levels = 100 ) { + if ( empty($folder) ) + return false; + + if ( ! $levels ) + return false; + + $files = array(); + if ( $dir = @opendir( $folder ) ) { + while (($file = readdir( $dir ) ) !== false ) { + if ( in_array($file, array('.', '..') ) ) + continue; + if ( is_dir( $folder . '/' . $file ) ) { + $files2 = list_files( $folder . '/' . $file, $levels - 1); + if ( $files2 ) + $files = array_merge($files, $files2 ); + else + $files[] = $folder . '/' . $file . '/'; + } else { + $files[] = $folder . '/' . $file; + } + } + } + @closedir( $dir ); + return $files; +} + +/** + * Returns a filename of a Temporary unique file. + * Please note that the calling function must unlink() this itself. + * + * The filename is based off the passed parameter or defaults to the current unix timestamp, + * while the directory can either be passed as well, or by leaving it blank, default to a writable temporary directory. + * + * @since 2.6.0 + * + * @param string $filename Optional. Filename to base the Unique file off. Default empty. + * @param string $dir Optional. Directory to store the file in. Default empty. + * @return string a writable filename + */ +function wp_tempnam($filename = '', $dir = '') { + if ( empty($dir) ) + $dir = get_temp_dir(); + $filename = basename($filename); + if ( empty($filename) ) + $filename = time(); + + $filename = preg_replace('|\..*$|', '.tmp', $filename); + $filename = $dir . wp_unique_filename($dir, $filename); + touch($filename); + return $filename; +} + +/** + * Make sure that the file that was requested to edit, is allowed to be edited + * + * Function will die if if you are not allowed to edit the file + * + * @since 1.5.0 + * + * @param string $file file the users is attempting to edit + * @param array $allowed_files Array of allowed files to edit, $file must match an entry exactly + * @return string|null + */ +function validate_file_to_edit( $file, $allowed_files = '' ) { + $code = validate_file( $file, $allowed_files ); + + if (!$code ) + return $file; + + switch ( $code ) { + case 1 : + wp_die( __( 'Sorry, that file cannot be edited.' ) ); + + // case 2 : + // wp_die( __('Sorry, can’t call files with their real path.' )); + + case 3 : + wp_die( __( 'Sorry, that file cannot be edited.' ) ); + } +} + +/** + * Handle PHP uploads in WordPress, sanitizing file names, checking extensions for mime type, + * and moving the file to the appropriate directory within the uploads directory. + * + * @since 4.0.0 + * + * @see wp_handle_upload_error + * + * @param array $file Reference to a single element of $_FILES. Call the function once for + * each uploaded file. + * @param array $overrides An associative array of names => values to override default variables. + * @param string $time Time formatted in 'yyyy/mm'. + * @param string $action Expected value for $_POST['action']. + * @return array On success, returns an associative array of file attributes. On failure, returns + * $overrides['upload_error_handler'](&$file, $message ) or array( 'error'=>$message ). +*/ +function _wp_handle_upload( &$file, $overrides, $time, $action ) { + // The default error handler. + if ( ! function_exists( 'wp_handle_upload_error' ) ) { + function wp_handle_upload_error( &$file, $message ) { + return array( 'error' => $message ); + } + } + + /** + * Filter the data for a file before it is uploaded to WordPress. + * + * The dynamic portion of the hook name, `$action`, refers to the post action. + * + * @since 2.9.0 as 'wp_handle_upload_prefilter'. + * @since 4.0.0 Converted to a dynamic hook with `$action`. + * + * @param array $file An array of data for a single file. + */ + $file = apply_filters( "{$action}_prefilter", $file ); + + // You may define your own function and pass the name in $overrides['upload_error_handler'] + $upload_error_handler = 'wp_handle_upload_error'; + if ( isset( $overrides['upload_error_handler'] ) ) { + $upload_error_handler = $overrides['upload_error_handler']; + } + + // You may have had one or more 'wp_handle_upload_prefilter' functions error out the file. Handle that gracefully. + if ( isset( $file['error'] ) && ! is_numeric( $file['error'] ) && $file['error'] ) { + return $upload_error_handler( $file, $file['error'] ); + } + + // Install user overrides. Did we mention that this voids your warranty? + + // You may define your own function and pass the name in $overrides['unique_filename_callback'] + $unique_filename_callback = null; + if ( isset( $overrides['unique_filename_callback'] ) ) { + $unique_filename_callback = $overrides['unique_filename_callback']; + } + + /* + * This may not have orignially been intended to be overrideable, + * but historically has been. + */ + if ( isset( $overrides['upload_error_strings'] ) ) { + $upload_error_strings = $overrides['upload_error_strings']; + } else { + // Courtesy of php.net, the strings that describe the error indicated in $_FILES[{form field}]['error']. + $upload_error_strings = array( + false, + __( 'The uploaded file exceeds the upload_max_filesize directive in php.ini.' ), + __( 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.' ), + __( 'The uploaded file was only partially uploaded.' ), + __( 'No file was uploaded.' ), + '', + __( 'Missing a temporary folder.' ), + __( 'Failed to write file to disk.' ), + __( 'File upload stopped by extension.' ) + ); + } + + // All tests are on by default. Most can be turned off by $overrides[{test_name}] = false; + $test_form = isset( $overrides['test_form'] ) ? $overrides['test_form'] : true; + $test_size = isset( $overrides['test_size'] ) ? $overrides['test_size'] : true; + + // If you override this, you must provide $ext and $type!! + $test_type = isset( $overrides['test_type'] ) ? $overrides['test_type'] : true; + $mimes = isset( $overrides['mimes'] ) ? $overrides['mimes'] : false; + + // A correct form post will pass this test. + if ( $test_form && ( ! isset( $_POST['action'] ) || ( $_POST['action'] != $action ) ) ) { + return call_user_func( $upload_error_handler, $file, __( 'Invalid form submission.' ) ); + } + // A successful upload will pass this test. It makes no sense to override this one. + if ( isset( $file['error'] ) && $file['error'] > 0 ) { + return call_user_func( $upload_error_handler, $file, $upload_error_strings[ $file['error'] ] ); + } + + $test_file_size = 'wp_handle_upload' === $action ? $file['size'] : filesize( $file['tmp_name'] ); + // A non-empty file will pass this test. + if ( $test_size && ! ( $test_file_size > 0 ) ) { + if ( is_multisite() ) { + $error_msg = __( 'File is empty. Please upload something more substantial.' ); + } else { + $error_msg = __( 'File is empty. Please upload something more substantial. This error could also be caused by uploads being disabled in your php.ini or by post_max_size being defined as smaller than upload_max_filesize in php.ini.' ); + } + return call_user_func( $upload_error_handler, $file, $error_msg ); + } + + // A properly uploaded file will pass this test. There should be no reason to override this one. + $test_uploaded_file = 'wp_handle_upload' === $action ? @ is_uploaded_file( $file['tmp_name'] ) : @ is_file( $file['tmp_name'] ); + if ( ! $test_uploaded_file ) { + return call_user_func( $upload_error_handler, $file, __( 'Specified file failed upload test.' ) ); + } + + // A correct MIME type will pass this test. Override $mimes or use the upload_mimes filter. + if ( $test_type ) { + $wp_filetype = wp_check_filetype_and_ext( $file['tmp_name'], $file['name'], $mimes ); + $ext = empty( $wp_filetype['ext'] ) ? '' : $wp_filetype['ext']; + $type = empty( $wp_filetype['type'] ) ? '' : $wp_filetype['type']; + $proper_filename = empty( $wp_filetype['proper_filename'] ) ? '' : $wp_filetype['proper_filename']; + + // Check to see if wp_check_filetype_and_ext() determined the filename was incorrect + if ( $proper_filename ) { + $file['name'] = $proper_filename; + } + if ( ( ! $type || !$ext ) && ! current_user_can( 'unfiltered_upload' ) ) { + return call_user_func( $upload_error_handler, $file, __( 'Sorry, this file type is not permitted for security reasons.' ) ); + } + if ( ! $type ) { + $type = $file['type']; + } + } else { + $type = ''; + } + + /* + * A writable uploads dir will pass this test. Again, there's no point + * overriding this one. + */ + if ( ! ( ( $uploads = wp_upload_dir( $time ) ) && false === $uploads['error'] ) ) { + return call_user_func( $upload_error_handler, $file, $uploads['error'] ); + } + + $filename = wp_unique_filename( $uploads['path'], $file['name'], $unique_filename_callback ); + + // Move the file to the uploads dir. + $new_file = $uploads['path'] . "/$filename"; + if ( 'wp_handle_upload' === $action ) { + $move_new_file = @ move_uploaded_file( $file['tmp_name'], $new_file ); + } else { + $move_new_file = @ rename( $file['tmp_name'], $new_file ); + } + + if ( false === $move_new_file ) { + if ( 0 === strpos( $uploads['basedir'], ABSPATH ) ) { + $error_path = str_replace( ABSPATH, '', $uploads['basedir'] ) . $uploads['subdir']; + } else { + $error_path = basename( $uploads['basedir'] ) . $uploads['subdir']; + } + return $upload_error_handler( $file, sprintf( __('The uploaded file could not be moved to %s.' ), $error_path ) ); + } + + // Set correct file permissions. + $stat = stat( dirname( $new_file )); + $perms = $stat['mode'] & 0000666; + @ chmod( $new_file, $perms ); + + // Compute the URL. + $url = $uploads['url'] . "/$filename"; + + if ( is_multisite() ) { + delete_transient( 'dirsize_cache' ); + } + + /** + * Filter the data array for the uploaded file. + * + * @since 2.1.0 + * + * @param array $upload { + * Array of upload data. + * + * @type string $file Filename of the newly-uploaded file. + * @type string $url URL of the uploaded file. + * @type string $type File type. + * } + * @param string $context The type of upload action. Values include 'upload' or 'sideload'. + */ + return apply_filters( 'wp_handle_upload', array( + 'file' => $new_file, + 'url' => $url, + 'type' => $type + ), 'wp_handle_sideload' === $action ? 'sideload' : 'upload' ); } + +/** + * Wrapper for _wp_handle_upload(), passes 'wp_handle_upload' action. + * + * @since 2.0.0 + * + * @see _wp_handle_upload() + * + * @param array $file Reference to a single element of $_FILES. Call the function once for + * each uploaded file. + * @param array|bool $overrides Optional. An associative array of names=>values to override default + * variables. Default false. + * @param string $time Optional. Time formatted in 'yyyy/mm'. Default null. + * @return array On success, returns an associative array of file attributes. On failure, returns + * $overrides['upload_error_handler'](&$file, $message ) or array( 'error'=>$message ). + */ +function wp_handle_upload( &$file, $overrides = false, $time = null ) { + /* + * $_POST['action'] must be set and its value must equal $overrides['action'] + * or this: + */ + $action = 'wp_handle_upload'; + if ( isset( $overrides['action'] ) ) { + $action = $overrides['action']; + } + + return _wp_handle_upload( $file, $overrides, $time, $action ); +} + +/** + * Wrapper for _wp_handle_upload(), passes 'wp_handle_sideload' action + * + * @since 2.6.0 + * + * @see _wp_handle_upload() + * + * @param array $file An array similar to that of a PHP $_FILES POST array + * @param array|bool $overrides Optional. An associative array of names=>values to override default + * variables. Default false. + * @param string $time Optional. Time formatted in 'yyyy/mm'. Default null. + * @return array On success, returns an associative array of file attributes. On failure, returns + * $overrides['upload_error_handler'](&$file, $message ) or array( 'error'=>$message ). + */ +function wp_handle_sideload( &$file, $overrides = false, $time = null ) { + /* + * $_POST['action'] must be set and its value must equal $overrides['action'] + * or this: + */ + $action = 'wp_handle_sideload'; + if ( isset( $overrides['action'] ) ) { + $action = $overrides['action']; + } + return _wp_handle_upload( $file, $overrides, $time, $action ); +} + + +/** + * Downloads a url to a local temporary file using the WordPress HTTP Class. + * Please note, That the calling function must unlink() the file. + * + * @since 2.5.0 + * + * @param string $url the URL of the file to download + * @param int $timeout The timeout for the request to download the file default 300 seconds + * @return mixed WP_Error on failure, string Filename on success. + */ +function download_url( $url, $timeout = 300 ) { + //WARNING: The file is not automatically deleted, The script must unlink() the file. + if ( ! $url ) + return new WP_Error('http_no_url', __('Invalid URL Provided.')); + + $tmpfname = wp_tempnam($url); + if ( ! $tmpfname ) + return new WP_Error('http_no_file', __('Could not create Temporary file.')); + + $response = wp_safe_remote_get( $url, array( 'timeout' => $timeout, 'stream' => true, 'filename' => $tmpfname ) ); + + if ( is_wp_error( $response ) ) { + unlink( $tmpfname ); + return $response; + } + + if ( 200 != wp_remote_retrieve_response_code( $response ) ){ + unlink( $tmpfname ); + return new WP_Error( 'http_404', trim( wp_remote_retrieve_response_message( $response ) ) ); + } + + $content_md5 = wp_remote_retrieve_header( $response, 'content-md5' ); + if ( $content_md5 ) { + $md5_check = verify_file_md5( $tmpfname, $content_md5 ); + if ( is_wp_error( $md5_check ) ) { + unlink( $tmpfname ); + return $md5_check; + } + } + + return $tmpfname; +} + +/** + * Calculates and compares the MD5 of a file to its expected value. + * + * @since 3.7.0 + * + * @param string $filename The filename to check the MD5 of. + * @param string $expected_md5 The expected MD5 of the file, either a base64 encoded raw md5, or a hex-encoded md5 + * @return bool|object WP_Error on failure, true on success, false when the MD5 format is unknown/unexpected + */ +function verify_file_md5( $filename, $expected_md5 ) { + if ( 32 == strlen( $expected_md5 ) ) + $expected_raw_md5 = pack( 'H*', $expected_md5 ); + elseif ( 24 == strlen( $expected_md5 ) ) + $expected_raw_md5 = base64_decode( $expected_md5 ); + else + return false; // unknown format + + $file_md5 = md5_file( $filename, true ); + + if ( $file_md5 === $expected_raw_md5 ) + return true; + + return new WP_Error( 'md5_mismatch', sprintf( __( 'The checksum of the file (%1$s) does not match the expected checksum value (%2$s).' ), bin2hex( $file_md5 ), bin2hex( $expected_raw_md5 ) ) ); +} + +/** + * Unzips a specified ZIP file to a location on the Filesystem via the WordPress Filesystem Abstraction. + * Assumes that WP_Filesystem() has already been called and set up. Does not extract a root-level __MACOSX directory, if present. + * + * Attempts to increase the PHP Memory limit to 256M before uncompressing, + * However, The most memory required shouldn't be much larger than the Archive itself. + * + * @since 2.5.0 + * + * @param string $file Full path and filename of zip archive + * @param string $to Full path on the filesystem to extract archive to + * @return mixed WP_Error on failure, True on success + */ +function unzip_file($file, $to) { + global $wp_filesystem; + + if ( ! $wp_filesystem || !is_object($wp_filesystem) ) + return new WP_Error('fs_unavailable', __('Could not access filesystem.')); + + // Unzip can use a lot of memory, but not this much hopefully + /** This filter is documented in wp-admin/admin.php */ + @ini_set( 'memory_limit', apply_filters( 'admin_memory_limit', WP_MAX_MEMORY_LIMIT ) ); + + $needed_dirs = array(); + $to = trailingslashit($to); + + // Determine any parent dir's needed (of the upgrade directory) + if ( ! $wp_filesystem->is_dir($to) ) { //Only do parents if no children exist + $path = preg_split('![/\\\]!', untrailingslashit($to)); + for ( $i = count($path); $i >= 0; $i-- ) { + if ( empty($path[$i]) ) + continue; + + $dir = implode('/', array_slice($path, 0, $i+1) ); + if ( preg_match('!^[a-z]:$!i', $dir) ) // Skip it if it looks like a Windows Drive letter. + continue; + + if ( ! $wp_filesystem->is_dir($dir) ) + $needed_dirs[] = $dir; + else + break; // A folder exists, therefor, we dont need the check the levels below this + } + } + + /** + * Filter whether to use ZipArchive to unzip archives. + * + * @since 3.0.0 + * + * @param bool $ziparchive Whether to use ZipArchive. Default true. + */ + if ( class_exists( 'ZipArchive' ) && apply_filters( 'unzip_file_use_ziparchive', true ) ) { + $result = _unzip_file_ziparchive($file, $to, $needed_dirs); + if ( true === $result ) { + return $result; + } elseif ( is_wp_error($result) ) { + if ( 'incompatible_archive' != $result->get_error_code() ) + return $result; + } + } + // Fall through to PclZip if ZipArchive is not available, or encountered an error opening the file. + return _unzip_file_pclzip($file, $to, $needed_dirs); +} + +/** + * This function should not be called directly, use unzip_file instead. Attempts to unzip an archive using the ZipArchive class. + * Assumes that WP_Filesystem() has already been called and set up. + * + * @since 3.0.0 + * @see unzip_file + * @access private + * + * @param string $file Full path and filename of zip archive + * @param string $to Full path on the filesystem to extract archive to + * @param array $needed_dirs A partial list of required folders needed to be created. + * @return mixed WP_Error on failure, True on success + */ +function _unzip_file_ziparchive($file, $to, $needed_dirs = array() ) { + global $wp_filesystem; + + $z = new ZipArchive(); + + $zopen = $z->open( $file, ZIPARCHIVE::CHECKCONS ); + if ( true !== $zopen ) + return new WP_Error( 'incompatible_archive', __( 'Incompatible Archive.' ), array( 'ziparchive_error' => $zopen ) ); + + $uncompressed_size = 0; + + for ( $i = 0; $i < $z->numFiles; $i++ ) { + if ( ! $info = $z->statIndex($i) ) + return new WP_Error( 'stat_failed_ziparchive', __( 'Could not retrieve file from archive.' ) ); + + if ( '__MACOSX/' === substr($info['name'], 0, 9) ) // Skip the OS X-created __MACOSX directory + continue; + + $uncompressed_size += $info['size']; + + if ( '/' == substr($info['name'], -1) ) // directory + $needed_dirs[] = $to . untrailingslashit($info['name']); + else + $needed_dirs[] = $to . untrailingslashit(dirname($info['name'])); + } + + /* + * disk_free_space() could return false. Assume that any falsey value is an error. + * A disk that has zero free bytes has bigger problems. + * Require we have enough space to unzip the file and copy its contents, with a 10% buffer. + */ + if ( defined( 'DOING_CRON' ) && DOING_CRON ) { + $available_space = @disk_free_space( WP_CONTENT_DIR ); + if ( $available_space && ( $uncompressed_size * 2.1 ) > $available_space ) + return new WP_Error( 'disk_full_unzip_file', __( 'Could not copy files. You may have run out of disk space.' ), compact( 'uncompressed_size', 'available_space' ) ); + } + + $needed_dirs = array_unique($needed_dirs); + foreach ( $needed_dirs as $dir ) { + // Check the parent folders of the folders all exist within the creation array. + if ( untrailingslashit($to) == $dir ) // Skip over the working directory, We know this exists (or will exist) + continue; + if ( strpos($dir, $to) === false ) // If the directory is not within the working directory, Skip it + continue; + + $parent_folder = dirname($dir); + while ( !empty($parent_folder) && untrailingslashit($to) != $parent_folder && !in_array($parent_folder, $needed_dirs) ) { + $needed_dirs[] = $parent_folder; + $parent_folder = dirname($parent_folder); + } + } + asort($needed_dirs); + + // Create those directories if need be: + foreach ( $needed_dirs as $_dir ) { + if ( ! $wp_filesystem->mkdir($_dir, FS_CHMOD_DIR) && ! $wp_filesystem->is_dir($_dir) ) // Only check to see if the Dir exists upon creation failure. Less I/O this way. + return new WP_Error( 'mkdir_failed_ziparchive', __( 'Could not create directory.' ), substr( $_dir, strlen( $to ) ) ); + } + unset($needed_dirs); + + for ( $i = 0; $i < $z->numFiles; $i++ ) { + if ( ! $info = $z->statIndex($i) ) + return new WP_Error( 'stat_failed_ziparchive', __( 'Could not retrieve file from archive.' ) ); + + if ( '/' == substr($info['name'], -1) ) // directory + continue; + + if ( '__MACOSX/' === substr($info['name'], 0, 9) ) // Don't extract the OS X-created __MACOSX directory files + continue; + + $contents = $z->getFromIndex($i); + if ( false === $contents ) + return new WP_Error( 'extract_failed_ziparchive', __( 'Could not extract file from archive.' ), $info['name'] ); + + if ( ! $wp_filesystem->put_contents( $to . $info['name'], $contents, FS_CHMOD_FILE) ) + return new WP_Error( 'copy_failed_ziparchive', __( 'Could not copy file.' ), $info['name'] ); + } + + $z->close(); + + return true; +} + +/** + * This function should not be called directly, use unzip_file instead. Attempts to unzip an archive using the PclZip library. + * Assumes that WP_Filesystem() has already been called and set up. + * + * @since 3.0.0 + * @see unzip_file + * @access private + * + * @param string $file Full path and filename of zip archive + * @param string $to Full path on the filesystem to extract archive to + * @param array $needed_dirs A partial list of required folders needed to be created. + * @return mixed WP_Error on failure, True on success + */ +function _unzip_file_pclzip($file, $to, $needed_dirs = array()) { + global $wp_filesystem; + + mbstring_binary_safe_encoding(); + + require_once(ABSPATH . 'wp-admin/includes/class-pclzip.php'); + + $archive = new PclZip($file); + + $archive_files = $archive->extract(PCLZIP_OPT_EXTRACT_AS_STRING); + + reset_mbstring_encoding(); + + // Is the archive valid? + if ( !is_array($archive_files) ) + return new WP_Error('incompatible_archive', __('Incompatible Archive.'), $archive->errorInfo(true)); + + if ( 0 == count($archive_files) ) + return new WP_Error( 'empty_archive_pclzip', __( 'Empty archive.' ) ); + + $uncompressed_size = 0; + + // Determine any children directories needed (From within the archive) + foreach ( $archive_files as $file ) { + if ( '__MACOSX/' === substr($file['filename'], 0, 9) ) // Skip the OS X-created __MACOSX directory + continue; + + $uncompressed_size += $file['size']; + + $needed_dirs[] = $to . untrailingslashit( $file['folder'] ? $file['filename'] : dirname($file['filename']) ); + } + + /* + * disk_free_space() could return false. Assume that any falsey value is an error. + * A disk that has zero free bytes has bigger problems. + * Require we have enough space to unzip the file and copy its contents, with a 10% buffer. + */ + if ( defined( 'DOING_CRON' ) && DOING_CRON ) { + $available_space = @disk_free_space( WP_CONTENT_DIR ); + if ( $available_space && ( $uncompressed_size * 2.1 ) > $available_space ) + return new WP_Error( 'disk_full_unzip_file', __( 'Could not copy files. You may have run out of disk space.' ), compact( 'uncompressed_size', 'available_space' ) ); + } + + $needed_dirs = array_unique($needed_dirs); + foreach ( $needed_dirs as $dir ) { + // Check the parent folders of the folders all exist within the creation array. + if ( untrailingslashit($to) == $dir ) // Skip over the working directory, We know this exists (or will exist) + continue; + if ( strpos($dir, $to) === false ) // If the directory is not within the working directory, Skip it + continue; + + $parent_folder = dirname($dir); + while ( !empty($parent_folder) && untrailingslashit($to) != $parent_folder && !in_array($parent_folder, $needed_dirs) ) { + $needed_dirs[] = $parent_folder; + $parent_folder = dirname($parent_folder); + } + } + asort($needed_dirs); + + // Create those directories if need be: + foreach ( $needed_dirs as $_dir ) { + // Only check to see if the dir exists upon creation failure. Less I/O this way. + if ( ! $wp_filesystem->mkdir( $_dir, FS_CHMOD_DIR ) && ! $wp_filesystem->is_dir( $_dir ) ) + return new WP_Error( 'mkdir_failed_pclzip', __( 'Could not create directory.' ), substr( $_dir, strlen( $to ) ) ); + } + unset($needed_dirs); + + // Extract the files from the zip + foreach ( $archive_files as $file ) { + if ( $file['folder'] ) + continue; + + if ( '__MACOSX/' === substr($file['filename'], 0, 9) ) // Don't extract the OS X-created __MACOSX directory files + continue; + + if ( ! $wp_filesystem->put_contents( $to . $file['filename'], $file['content'], FS_CHMOD_FILE) ) + return new WP_Error( 'copy_failed_pclzip', __( 'Could not copy file.' ), $file['filename'] ); + } + return true; +} + +/** + * Copies a directory from one location to another via the WordPress Filesystem Abstraction. + * Assumes that WP_Filesystem() has already been called and setup. + * + * @since 2.5.0 + * + * @param string $from source directory + * @param string $to destination directory + * @param array $skip_list a list of files/folders to skip copying + * @return mixed WP_Error on failure, True on success. + */ +function copy_dir($from, $to, $skip_list = array() ) { + global $wp_filesystem; + + $dirlist = $wp_filesystem->dirlist($from); + + $from = trailingslashit($from); + $to = trailingslashit($to); + + foreach ( (array) $dirlist as $filename => $fileinfo ) { + if ( in_array( $filename, $skip_list ) ) + continue; + + if ( 'f' == $fileinfo['type'] ) { + if ( ! $wp_filesystem->copy($from . $filename, $to . $filename, true, FS_CHMOD_FILE) ) { + // If copy failed, chmod file to 0644 and try again. + $wp_filesystem->chmod( $to . $filename, FS_CHMOD_FILE ); + if ( ! $wp_filesystem->copy($from . $filename, $to . $filename, true, FS_CHMOD_FILE) ) + return new WP_Error( 'copy_failed_copy_dir', __( 'Could not copy file.' ), $to . $filename ); + } + } elseif ( 'd' == $fileinfo['type'] ) { + if ( !$wp_filesystem->is_dir($to . $filename) ) { + if ( !$wp_filesystem->mkdir($to . $filename, FS_CHMOD_DIR) ) + return new WP_Error( 'mkdir_failed_copy_dir', __( 'Could not create directory.' ), $to . $filename ); + } + + // generate the $sub_skip_list for the subdirectory as a sub-set of the existing $skip_list + $sub_skip_list = array(); + foreach ( $skip_list as $skip_item ) { + if ( 0 === strpos( $skip_item, $filename . '/' ) ) + $sub_skip_list[] = preg_replace( '!^' . preg_quote( $filename, '!' ) . '/!i', '', $skip_item ); + } + + $result = copy_dir($from . $filename, $to . $filename, $sub_skip_list); + if ( is_wp_error($result) ) + return $result; + } + } + return true; +} + +/** + * Initialises and connects the WordPress Filesystem Abstraction classes. + * This function will include the chosen transport and attempt connecting. + * + * Plugins may add extra transports, And force WordPress to use them by returning + * the filename via the {@see 'filesystem_method_file'} filter. + * + * @since 2.5.0 + * + * @param array $args Optional. Connection args, These are passed directly to + * the `WP_Filesystem_*()` classes. Default false. + * @param string $context Optional. Context for {@see get_filesystem_method()}. + * Default false. + * @param bool $allow_relaxed_file_ownership Optional. Whether to allow Group/World writable. + * Default false. + * @return null|boolean false on failure, true on success. + */ +function WP_Filesystem( $args = false, $context = false, $allow_relaxed_file_ownership = false ) { + global $wp_filesystem; + + require_once(ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php'); + + $method = get_filesystem_method( $args, $context, $allow_relaxed_file_ownership ); + + if ( ! $method ) + return false; + + if ( ! class_exists("WP_Filesystem_$method") ) { + + /** + * Filter the path for a specific filesystem method class file. + * + * @since 2.6.0 + * + * @see get_filesystem_method() + * + * @param string $path Path to the specific filesystem method class file. + * @param string $method The filesystem method to use. + */ + $abstraction_file = apply_filters( 'filesystem_method_file', ABSPATH . 'wp-admin/includes/class-wp-filesystem-' . $method . '.php', $method ); + + if ( ! file_exists($abstraction_file) ) + return; + + require_once($abstraction_file); + } + $method = "WP_Filesystem_$method"; + + $wp_filesystem = new $method($args); + + //Define the timeouts for the connections. Only available after the construct is called to allow for per-transport overriding of the default. + if ( ! defined('FS_CONNECT_TIMEOUT') ) + define('FS_CONNECT_TIMEOUT', 30); + if ( ! defined('FS_TIMEOUT') ) + define('FS_TIMEOUT', 30); + + if ( is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code() ) + return false; + + if ( !$wp_filesystem->connect() ) + return false; //There was an error connecting to the server. + + // Set the permission constants if not already set. + if ( ! defined('FS_CHMOD_DIR') ) + define('FS_CHMOD_DIR', ( fileperms( ABSPATH ) & 0777 | 0755 ) ); + if ( ! defined('FS_CHMOD_FILE') ) + define('FS_CHMOD_FILE', ( fileperms( ABSPATH . 'index.php' ) & 0777 | 0644 ) ); + + return true; +} + +/** + * Determines which Filesystem Method to use. + * + * The priority of the Transports are: Direct, SSH2, FTP PHP Extension, + * FTP Sockets (Via Sockets class, or `fsockopen()`). + * + * Note that the return value of this function can be overridden in 2 ways + * + * - By defining FS_METHOD in your `wp-config.php` file + * - By using the filesystem_method filter + * + * Valid values for these are: 'direct', 'ssh2', 'ftpext' or 'ftpsockets'. + * + * Plugins may also define a custom transport handler, See the WP_Filesystem + * function for more information. + * + * @since 2.5.0 + * + * @todo Properly mark arguments as optional. + * + * @param array $args Connection details. + * @param string $context Full path to the directory that is tested for being writable. + * @param bool $allow_relaxed_file_ownership Whether to allow Group/World writable. + * @return string The transport to use, see description for valid return values. + */ +function get_filesystem_method( $args = array(), $context = false, $allow_relaxed_file_ownership = false ) { + $method = defined('FS_METHOD') ? FS_METHOD : false; // Please ensure that this is either 'direct', 'ssh2', 'ftpext' or 'ftpsockets' + + if ( ! $context ) { + $context = WP_CONTENT_DIR; + } + + // If the directory doesn't exist (wp-content/languages) then use the parent directory as we'll create it. + if ( WP_LANG_DIR == $context && ! is_dir( $context ) ) { + $context = dirname( $context ); + } + + $context = trailingslashit( $context ); + + if ( ! $method ) { + + $temp_file_name = $context . 'temp-write-test-' . time(); + $temp_handle = @fopen($temp_file_name, 'w'); + if ( $temp_handle ) { + + // Attempt to determine the file owner of the WordPress files, and that of newly created files + $wp_file_owner = $temp_file_owner = false; + if ( function_exists('fileowner') ) { + $wp_file_owner = @fileowner( __FILE__ ); + $temp_file_owner = @fileowner( $temp_file_name ); + } + + if ( $wp_file_owner !== false && $wp_file_owner === $temp_file_owner ) { + // WordPress is creating files as the same owner as the WordPress files, + // this means it's safe to modify & create new files via PHP. + $method = 'direct'; + $GLOBALS['_wp_filesystem_direct_method'] = 'file_owner'; + } else if ( $allow_relaxed_file_ownership ) { + // The $context directory is writable, and $allow_relaxed_file_ownership is set, this means we can modify files + // safely in this directory. This mode doesn't create new files, only alter existing ones. + $method = 'direct'; + $GLOBALS['_wp_filesystem_direct_method'] = 'relaxed_ownership'; + } + + @fclose($temp_handle); + @unlink($temp_file_name); + } + } + + if ( ! $method && isset($args['connection_type']) && 'ssh' == $args['connection_type'] && extension_loaded('ssh2') && function_exists('stream_get_contents') ) $method = 'ssh2'; + if ( ! $method && extension_loaded('ftp') ) $method = 'ftpext'; + if ( ! $method && ( extension_loaded('sockets') || function_exists('fsockopen') ) ) $method = 'ftpsockets'; //Sockets: Socket extension; PHP Mode: FSockopen / fwrite / fread + + /** + * Filter the filesystem method to use. + * + * @since 2.6.0 + * + * @param string $method Filesystem method to return. + * @param array $args An array of connection details for the method. + * @param string $context Full path to the directory that is tested for being writable. + * @param bool $allow_relaxed_file_ownership Whether to allow Group/World writable. + */ + return apply_filters( 'filesystem_method', $method, $args, $context, $allow_relaxed_file_ownership ); +} + +/** + * Displays a form to the user to request for their FTP/SSH details in order + * to connect to the filesystem. + * + * All chosen/entered details are saved, Excluding the Password. + * + * Hostnames may be in the form of hostname:portnumber (eg: wordpress.org:2467) + * to specify an alternate FTP/SSH port. + * + * Plugins may override this form by returning true|false via the + * {@see 'request_filesystem_credentials'} filter. + * + * @since 2.5. + * + * @todo Properly mark optional arguments as such + * + * @param string $form_post the URL to post the form to + * @param string $type the chosen Filesystem method in use + * @param boolean $error if the current request has failed to connect + * @param string $context The directory which is needed access to, The write-test will be performed on this directory by get_filesystem_method() + * @param string $extra_fields Extra POST fields which should be checked for to be included in the post. + * @param bool $allow_relaxed_file_ownership Whether to allow Group/World writable. + * @return boolean False on failure. True on success. + */ +function request_filesystem_credentials($form_post, $type = '', $error = false, $context = false, $extra_fields = null, $allow_relaxed_file_ownership = false ) { + + /** + * Filter the filesystem credentials form output. + * + * Returning anything other than an empty string will effectively short-circuit + * output of the filesystem credentials form, returning that value instead. + * + * @since 2.5.0 + * + * @param mixed $output Form output to return instead. Default empty. + * @param string $form_post URL to POST the form to. + * @param string $type Chosen type of filesystem. + * @param bool $error Whether the current request has failed to connect. + * Default false. + * @param string $context Full path to the directory that is tested for + * being writable. + * @param bool $allow_relaxed_file_ownership Whether to allow Group/World writable. + * @param array $extra_fields Extra POST fields. + */ + $req_cred = apply_filters( 'request_filesystem_credentials', '', $form_post, $type, $error, $context, $extra_fields, $allow_relaxed_file_ownership ); + if ( '' !== $req_cred ) + return $req_cred; + + if ( empty($type) ) { + $type = get_filesystem_method( array(), $context, $allow_relaxed_file_ownership ); + } + + if ( 'direct' == $type ) + return true; + + if ( is_null( $extra_fields ) ) + $extra_fields = array( 'version', 'locale' ); + + $credentials = get_option('ftp_credentials', array( 'hostname' => '', 'username' => '')); + + // If defined, set it to that, Else, If POST'd, set it to that, If not, Set it to whatever it previously was(saved details in option) + $credentials['hostname'] = defined('FTP_HOST') ? FTP_HOST : (!empty($_POST['hostname']) ? wp_unslash( $_POST['hostname'] ) : $credentials['hostname']); + $credentials['username'] = defined('FTP_USER') ? FTP_USER : (!empty($_POST['username']) ? wp_unslash( $_POST['username'] ) : $credentials['username']); + $credentials['password'] = defined('FTP_PASS') ? FTP_PASS : (!empty($_POST['password']) ? wp_unslash( $_POST['password'] ) : ''); + + // Check to see if we are setting the public/private keys for ssh + $credentials['public_key'] = defined('FTP_PUBKEY') ? FTP_PUBKEY : (!empty($_POST['public_key']) ? wp_unslash( $_POST['public_key'] ) : ''); + $credentials['private_key'] = defined('FTP_PRIKEY') ? FTP_PRIKEY : (!empty($_POST['private_key']) ? wp_unslash( $_POST['private_key'] ) : ''); + + // Sanitize the hostname, Some people might pass in odd-data: + $credentials['hostname'] = preg_replace('|\w+://|', '', $credentials['hostname']); //Strip any schemes off + + if ( strpos($credentials['hostname'], ':') ) { + list( $credentials['hostname'], $credentials['port'] ) = explode(':', $credentials['hostname'], 2); + if ( ! is_numeric($credentials['port']) ) + unset($credentials['port']); + } else { + unset($credentials['port']); + } + + if ( ( defined('FTP_SSH') && FTP_SSH ) || ( defined('FS_METHOD') && 'ssh2' == FS_METHOD ) ) + $credentials['connection_type'] = 'ssh'; + else if ( (defined('FTP_SSL') && FTP_SSL) && 'ftpext' == $type ) //Only the FTP Extension understands SSL + $credentials['connection_type'] = 'ftps'; + else if ( !empty($_POST['connection_type']) ) + $credentials['connection_type'] = wp_unslash( $_POST['connection_type'] ); + else if ( !isset($credentials['connection_type']) ) //All else fails (And it's not defaulted to something else saved), Default to FTP + $credentials['connection_type'] = 'ftp'; + + if ( ! $error && + ( + ( !empty($credentials['password']) && !empty($credentials['username']) && !empty($credentials['hostname']) ) || + ( 'ssh' == $credentials['connection_type'] && !empty($credentials['public_key']) && !empty($credentials['private_key']) ) + ) ) { + $stored_credentials = $credentials; + if ( !empty($stored_credentials['port']) ) //save port as part of hostname to simplify above code. + $stored_credentials['hostname'] .= ':' . $stored_credentials['port']; + + unset($stored_credentials['password'], $stored_credentials['port'], $stored_credentials['private_key'], $stored_credentials['public_key']); + if ( ! defined( 'WP_INSTALLING' ) ) { + update_option( 'ftp_credentials', $stored_credentials ); + } + return $credentials; + } + $hostname = isset( $credentials['hostname'] ) ? $credentials['hostname'] : ''; + $username = isset( $credentials['username'] ) ? $credentials['username'] : ''; + $public_key = isset( $credentials['public_key'] ) ? $credentials['public_key'] : ''; + $private_key = isset( $credentials['private_key'] ) ? $credentials['private_key'] : ''; + $port = isset( $credentials['port'] ) ? $credentials['port'] : ''; + $connection_type = isset( $credentials['connection_type'] ) ? $credentials['connection_type'] : ''; + + if ( $error ) { + $error_string = __('ERROR: There was an error connecting to the server, Please verify the settings are correct.'); + if ( is_wp_error($error) ) + $error_string = esc_html( $error->get_error_message() ); + echo '

    ' . $error_string . '

    '; + } + + $types = array(); + if ( extension_loaded('ftp') || extension_loaded('sockets') || function_exists('fsockopen') ) + $types[ 'ftp' ] = __('FTP'); + if ( extension_loaded('ftp') ) //Only this supports FTPS + $types[ 'ftps' ] = __('FTPS (SSL)'); + if ( extension_loaded('ssh2') && function_exists('stream_get_contents') ) + $types[ 'ssh' ] = __('SSH2'); + + /** + * Filter the connection types to output to the filesystem credentials form. + * + * @since 2.9.0 + * + * @param array $types Types of connections. + * @param array $credentials Credentials to connect with. + * @param string $type Chosen filesystem method. + * @param object $error Error object. + * @param string $context Full path to the directory that is tested + * for being writable. + */ + $types = apply_filters( 'fs_ftp_connection_types', $types, $credentials, $type, $error, $context ); + +?> + +
    +
    +

    +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    " size="40" />
    size="40" />
    size="40" />
    +
    +
    +
    + +

    size="40" /> +
    size="40" /> +
    +
    + $text ) : ?> + + +
    +
    + +'; +} +submit_button( __( 'Proceed' ), 'button', 'upgrade' ); +?> +
    +
    + 400 ? 400 / $big : 1; + + $backup_sizes = get_post_meta( $post_id, '_wp_attachment_backup_sizes', true ); + $can_restore = false; + if ( ! empty( $backup_sizes ) && isset( $backup_sizes['full-orig'], $meta['file'] ) ) + $can_restore = $backup_sizes['full-orig']['file'] != basename( $meta['file'] ); + + if ( $msg ) { + if ( isset($msg->error) ) + $note = "

    $msg->error

    "; + elseif ( isset($msg->msg) ) + $note = "

    $msg->msg

    "; + } + + ?> +
    +
    + +
    +
    +
    +

    +
    +

    +
    + +

    + +
    + × + ! + , 'scale')" class="button button-primary" value="" /> +
    +
    +
    + + + +
    +
    +

    +
    +

    +
    + , 'restore')" class="button button-primary" value="" /> +
    +
    +
    +
    + + + +
    +
    +

    + +
    +

    + +


    +

    + +


    +

    +
    +
    + +

    + + + + : + + +

    + +

    + + + + × + + +

    +
    + + + +
    +
    +

    +

    +
    + +

    + +
    +

    + +

    +
    + + + + + + +

    +
    + + + +
    + +
    + +
    +
    , this)" class="imgedit-crop disabled" title="">
    get_post_mime_type( $post_id ), 'methods' => array( 'rotate' ) ) ) ) { ?> +
    , this)" title="">
    +
    , this)" title="">
    + +
    +
    + + +
    , this)" class="imgedit-flipv" title="">
    +
    , this)" class="imgedit-fliph" title="">
    + +
    , this)" class="imgedit-undo disabled" title="">
    +
    , this)" class="imgedit-redo disabled" title="">
    +
    +
    + + + + + + + + +
    + +
    + +
    + + )" disabled="disabled" class="button button-primary imgedit-submit-btn" value="" /> +
    +
    + +
    +
    + + +
    +stream( $mime_type ) ) ) + return false; + + return true; + } else { + _deprecated_argument( __FUNCTION__, '3.5', __( '$image needs to be an WP_Image_Editor object' ) ); + + /** + * Filter the GD image resource to be streamed to the browser. + * + * @since 2.9.0 + * @deprecated 3.5.0 Use image_editor_save_pre instead. + * + * @param resource $image Image resource to be streamed. + * @param int $post_id Post ID. + */ + $image = apply_filters( 'image_save_pre', $image, $post_id ); + + switch ( $mime_type ) { + case 'image/jpeg': + header( 'Content-Type: image/jpeg' ); + return imagejpeg( $image, null, 90 ); + case 'image/png': + header( 'Content-Type: image/png' ); + return imagepng( $image ); + case 'image/gif': + header( 'Content-Type: image/gif' ); + return imagegif( $image ); + default: + return false; + } + } +} + +/** + * Saves Image to File + * + * @param string $filename + * @param WP_Image_Editor $image + * @param string $mime_type + * @param int $post_id + * @return boolean + */ +function wp_save_image_file( $filename, $image, $mime_type, $post_id ) { + if ( $image instanceof WP_Image_Editor ) { + + /** This filter is documented in wp-admin/includes/image-edit.php */ + $image = apply_filters( 'image_editor_save_pre', $image, $post_id ); + + /** + * Filter whether to skip saving the image file. + * + * Returning a non-null value will short-circuit the save method, + * returning that value instead. + * + * @since 3.5.0 + * + * @param mixed $override Value to return instead of saving. Default null. + * @param string $filename Name of the file to be saved. + * @param WP_Image_Editor $image WP_Image_Editor instance. + * @param string $mime_type Image mime type. + * @param int $post_id Post ID. + */ + $saved = apply_filters( 'wp_save_image_editor_file', null, $filename, $image, $mime_type, $post_id ); + + if ( null !== $saved ) + return $saved; + + return $image->save( $filename, $mime_type ); + } else { + _deprecated_argument( __FUNCTION__, '3.5', __( '$image needs to be an WP_Image_Editor object' ) ); + + /** This filter is documented in wp-admin/includes/image-edit.php */ + $image = apply_filters( 'image_save_pre', $image, $post_id ); + + /** + * Filter whether to skip saving the image file. + * + * Returning a non-null value will short-circuit the save method, + * returning that value instead. + * + * @since 2.9.0 + * @deprecated 3.5.0 Use wp_save_image_editor_file instead. + * + * @param mixed $override Value to return instead of saving. Default null. + * @param string $filename Name of the file to be saved. + * @param WP_Image_Editor $image WP_Image_Editor instance. + * @param string $mime_type Image mime type. + * @param int $post_id Post ID. + */ + $saved = apply_filters( 'wp_save_image_file', null, $filename, $image, $mime_type, $post_id ); + + if ( null !== $saved ) + return $saved; + + switch ( $mime_type ) { + case 'image/jpeg': + + /** This filter is documented in wp-includes/class-wp-image-editor.php */ + return imagejpeg( $image, $filename, apply_filters( 'jpeg_quality', 90, 'edit_image' ) ); + case 'image/png': + return imagepng( $image, $filename ); + case 'image/gif': + return imagegif( $image, $filename ); + default: + return false; + } + } +} + +function _image_get_preview_ratio($w, $h) { + $max = max($w, $h); + return $max > 400 ? (400 / $max) : 1; +} + +// @TODO: Returns GD resource, but is NOT public +function _rotate_image_resource($img, $angle) { + _deprecated_function( __FUNCTION__, '3.5', __( 'Use WP_Image_Editor::rotate' ) ); + if ( function_exists('imagerotate') ) { + $rotated = imagerotate($img, $angle, 0); + if ( is_resource($rotated) ) { + imagedestroy($img); + $img = $rotated; + } + } + return $img; +} + +/** + * @TODO: Only used within image_edit_apply_changes + * and receives/returns GD Resource. + * Consider removal. + * + * @param GD_Resource $img + * @param boolean $horz + * @param boolean $vert + * @return GD_Resource + */ +function _flip_image_resource($img, $horz, $vert) { + _deprecated_function( __FUNCTION__, '3.5', __( 'Use WP_Image_Editor::flip' ) ); + $w = imagesx($img); + $h = imagesy($img); + $dst = wp_imagecreatetruecolor($w, $h); + if ( is_resource($dst) ) { + $sx = $vert ? ($w - 1) : 0; + $sy = $horz ? ($h - 1) : 0; + $sw = $vert ? -$w : $w; + $sh = $horz ? -$h : $h; + + if ( imagecopyresampled($dst, $img, 0, 0, $sx, $sy, $w, $h, $sw, $sh) ) { + imagedestroy($img); + $img = $dst; + } + } + return $img; +} + +/** + * @TODO: Only used within image_edit_apply_changes + * and receives/returns GD Resource. + * Consider removal. + * + * @param GD_Resource $img + * @param float $x + * @param float $y + * @param float $w + * @param float $h + * @return GD_Resource + */ +function _crop_image_resource($img, $x, $y, $w, $h) { + $dst = wp_imagecreatetruecolor($w, $h); + if ( is_resource($dst) ) { + if ( imagecopy($dst, $img, 0, 0, $x, $y, $w, $h) ) { + imagedestroy($img); + $img = $dst; + } + } + return $img; +} + +/** + * Performs group of changes on Editor specified. + * + * @since 2.9.0 + * + * @param WP_Image_Editor $image {@see WP_Image_Editor} instance. + * @param array $changes Array of change operations. + * @return WP_Image_Editor {@see WP_Image_Editor} instance with changes applied. + */ +function image_edit_apply_changes( $image, $changes ) { + if ( is_resource( $image ) ) + _deprecated_argument( __FUNCTION__, '3.5', __( '$image needs to be an WP_Image_Editor object' ) ); + + if ( !is_array($changes) ) + return $image; + + // Expand change operations. + foreach ( $changes as $key => $obj ) { + if ( isset($obj->r) ) { + $obj->type = 'rotate'; + $obj->angle = $obj->r; + unset($obj->r); + } elseif ( isset($obj->f) ) { + $obj->type = 'flip'; + $obj->axis = $obj->f; + unset($obj->f); + } elseif ( isset($obj->c) ) { + $obj->type = 'crop'; + $obj->sel = $obj->c; + unset($obj->c); + } + $changes[$key] = $obj; + } + + // Combine operations. + if ( count($changes) > 1 ) { + $filtered = array($changes[0]); + for ( $i = 0, $j = 1; $j < count($changes); $j++ ) { + $combined = false; + if ( $filtered[$i]->type == $changes[$j]->type ) { + switch ( $filtered[$i]->type ) { + case 'rotate': + $filtered[$i]->angle += $changes[$j]->angle; + $combined = true; + break; + case 'flip': + $filtered[$i]->axis ^= $changes[$j]->axis; + $combined = true; + break; + } + } + if ( !$combined ) + $filtered[++$i] = $changes[$j]; + } + $changes = $filtered; + unset($filtered); + } + + // Image resource before applying the changes. + if ( $image instanceof WP_Image_Editor ) { + + /** + * Filter the WP_Image_Editor instance before applying changes to the image. + * + * @since 3.5.0 + * + * @param WP_Image_Editor $image WP_Image_Editor instance. + * @param array $changes Array of change operations. + */ + $image = apply_filters( 'wp_image_editor_before_change', $image, $changes ); + } elseif ( is_resource( $image ) ) { + + /** + * Filter the GD image resource before applying changes to the image. + * + * @since 2.9.0 + * @deprecated 3.5.0 Use wp_image_editor_before_change instead. + * + * @param resource $image GD image resource. + * @param array $changes Array of change operations. + */ + $image = apply_filters( 'image_edit_before_change', $image, $changes ); + } + + foreach ( $changes as $operation ) { + switch ( $operation->type ) { + case 'rotate': + if ( $operation->angle != 0 ) { + if ( $image instanceof WP_Image_Editor ) + $image->rotate( $operation->angle ); + else + $image = _rotate_image_resource( $image, $operation->angle ); + } + break; + case 'flip': + if ( $operation->axis != 0 ) + if ( $image instanceof WP_Image_Editor ) + $image->flip( ($operation->axis & 1) != 0, ($operation->axis & 2) != 0 ); + else + $image = _flip_image_resource( $image, ( $operation->axis & 1 ) != 0, ( $operation->axis & 2 ) != 0 ); + break; + case 'crop': + $sel = $operation->sel; + + if ( $image instanceof WP_Image_Editor ) { + $size = $image->get_size(); + $w = $size['width']; + $h = $size['height']; + + $scale = 1 / _image_get_preview_ratio( $w, $h ); // discard preview scaling + $image->crop( $sel->x * $scale, $sel->y * $scale, $sel->w * $scale, $sel->h * $scale ); + } else { + $scale = 1 / _image_get_preview_ratio( imagesx( $image ), imagesy( $image ) ); // discard preview scaling + $image = _crop_image_resource( $image, $sel->x * $scale, $sel->y * $scale, $sel->w * $scale, $sel->h * $scale ); + } + break; + } + } + + return $image; +} + + +/** + * Streams image in post to browser, along with enqueued changes + * in $_REQUEST['history'] + * + * @param int $post_id + * @return boolean + */ +function stream_preview_image( $post_id ) { + $post = get_post( $post_id ); + + /** This filter is documented in wp-admin/admin.php */ + @ini_set( 'memory_limit', apply_filters( 'admin_memory_limit', WP_MAX_MEMORY_LIMIT ) ); + + $img = wp_get_image_editor( _load_image_to_edit_path( $post_id ) ); + + if ( is_wp_error( $img ) ) + return false; + + $changes = !empty($_REQUEST['history']) ? json_decode( wp_unslash($_REQUEST['history']) ) : null; + if ( $changes ) + $img = image_edit_apply_changes( $img, $changes ); + + // Scale the image. + $size = $img->get_size(); + $w = $size['width']; + $h = $size['height']; + + $ratio = _image_get_preview_ratio( $w, $h ); + $w2 = max ( 1, $w * $ratio ); + $h2 = max ( 1, $h * $ratio ); + + if ( is_wp_error( $img->resize( $w2, $h2 ) ) ) + return false; + + return wp_stream_image( $img, $post->post_mime_type, $post_id ); +} + +/** + * @param int $post_id + * @return stdClass + */ +function wp_restore_image($post_id) { + $meta = wp_get_attachment_metadata($post_id); + $file = get_attached_file($post_id); + $backup_sizes = get_post_meta( $post_id, '_wp_attachment_backup_sizes', true ); + $restored = false; + $msg = new stdClass; + + if ( !is_array($backup_sizes) ) { + $msg->error = __('Cannot load image metadata.'); + return $msg; + } + + $parts = pathinfo($file); + $suffix = time() . rand(100, 999); + $default_sizes = get_intermediate_image_sizes(); + + if ( isset($backup_sizes['full-orig']) && is_array($backup_sizes['full-orig']) ) { + $data = $backup_sizes['full-orig']; + + if ( $parts['basename'] != $data['file'] ) { + if ( defined('IMAGE_EDIT_OVERWRITE') && IMAGE_EDIT_OVERWRITE ) { + + // Delete only if it's edited image. + if ( preg_match('/-e[0-9]{13}\./', $parts['basename']) ) { + + /** This filter is documented in wp-admin/custom-header.php */ + $delpath = apply_filters( 'wp_delete_file', $file ); + @unlink($delpath); + } + } elseif ( isset( $meta['width'], $meta['height'] ) ) { + $backup_sizes["full-$suffix"] = array('width' => $meta['width'], 'height' => $meta['height'], 'file' => $parts['basename']); + } + } + + $restored_file = path_join($parts['dirname'], $data['file']); + $restored = update_attached_file($post_id, $restored_file); + + $meta['file'] = _wp_relative_upload_path( $restored_file ); + $meta['width'] = $data['width']; + $meta['height'] = $data['height']; + } + + foreach ( $default_sizes as $default_size ) { + if ( isset($backup_sizes["$default_size-orig"]) ) { + $data = $backup_sizes["$default_size-orig"]; + if ( isset($meta['sizes'][$default_size]) && $meta['sizes'][$default_size]['file'] != $data['file'] ) { + if ( defined('IMAGE_EDIT_OVERWRITE') && IMAGE_EDIT_OVERWRITE ) { + + // Delete only if it's edited image + if ( preg_match('/-e[0-9]{13}-/', $meta['sizes'][$default_size]['file']) ) { + /** This filter is documented in wp-admin/custom-header.php */ + $delpath = apply_filters( 'wp_delete_file', path_join($parts['dirname'], $meta['sizes'][$default_size]['file']) ); + @unlink($delpath); + } + } else { + $backup_sizes["$default_size-{$suffix}"] = $meta['sizes'][$default_size]; + } + } + + $meta['sizes'][$default_size] = $data; + } else { + unset($meta['sizes'][$default_size]); + } + } + + if ( !wp_update_attachment_metadata($post_id, $meta) || !update_post_meta( $post_id, '_wp_attachment_backup_sizes', $backup_sizes) ) { + $msg->error = __('Cannot save image metadata.'); + return $msg; + } + + if ( !$restored ) + $msg->error = __('Image metadata is inconsistent.'); + else + $msg->msg = __('Image restored successfully.'); + + return $msg; +} + +/** + * Saves image to post along with enqueued changes + * in $_REQUEST['history'] + * + * @param int $post_id + * @return \stdClass + */ +function wp_save_image( $post_id ) { + global $_wp_additional_image_sizes; + + $return = new stdClass; + $success = $delete = $scaled = $nocrop = false; + $post = get_post( $post_id ); + + $img = wp_get_image_editor( _load_image_to_edit_path( $post_id, 'full' ) ); + if ( is_wp_error( $img ) ) { + $return->error = esc_js( __('Unable to create new image.') ); + return $return; + } + + $fwidth = !empty($_REQUEST['fwidth']) ? intval($_REQUEST['fwidth']) : 0; + $fheight = !empty($_REQUEST['fheight']) ? intval($_REQUEST['fheight']) : 0; + $target = !empty($_REQUEST['target']) ? preg_replace('/[^a-z0-9_-]+/i', '', $_REQUEST['target']) : ''; + $scale = !empty($_REQUEST['do']) && 'scale' == $_REQUEST['do']; + + if ( $scale && $fwidth > 0 && $fheight > 0 ) { + $size = $img->get_size(); + $sX = $size['width']; + $sY = $size['height']; + + // Check if it has roughly the same w / h ratio. + $diff = round($sX / $sY, 2) - round($fwidth / $fheight, 2); + if ( -0.1 < $diff && $diff < 0.1 ) { + // Scale the full size image. + if ( $img->resize( $fwidth, $fheight ) ) + $scaled = true; + } + + if ( !$scaled ) { + $return->error = esc_js( __('Error while saving the scaled image. Please reload the page and try again.') ); + return $return; + } + } elseif ( !empty($_REQUEST['history']) ) { + $changes = json_decode( wp_unslash($_REQUEST['history']) ); + if ( $changes ) + $img = image_edit_apply_changes($img, $changes); + } else { + $return->error = esc_js( __('Nothing to save, the image has not changed.') ); + return $return; + } + + $meta = wp_get_attachment_metadata($post_id); + $backup_sizes = get_post_meta( $post->ID, '_wp_attachment_backup_sizes', true ); + + if ( !is_array($meta) ) { + $return->error = esc_js( __('Image data does not exist. Please re-upload the image.') ); + return $return; + } + + if ( !is_array($backup_sizes) ) + $backup_sizes = array(); + + // Generate new filename. + $path = get_attached_file($post_id); + $path_parts = pathinfo( $path ); + $filename = $path_parts['filename']; + $suffix = time() . rand(100, 999); + + if ( defined('IMAGE_EDIT_OVERWRITE') && IMAGE_EDIT_OVERWRITE && + isset($backup_sizes['full-orig']) && $backup_sizes['full-orig']['file'] != $path_parts['basename'] ) { + + if ( 'thumbnail' == $target ) + $new_path = "{$path_parts['dirname']}/{$filename}-temp.{$path_parts['extension']}"; + else + $new_path = $path; + } else { + while( true ) { + $filename = preg_replace( '/-e([0-9]+)$/', '', $filename ); + $filename .= "-e{$suffix}"; + $new_filename = "{$filename}.{$path_parts['extension']}"; + $new_path = "{$path_parts['dirname']}/$new_filename"; + if ( file_exists($new_path) ) + $suffix++; + else + break; + } + } + + // Save the full-size file, also needed to create sub-sizes. + if ( !wp_save_image_file($new_path, $img, $post->post_mime_type, $post_id) ) { + $return->error = esc_js( __('Unable to save the image.') ); + return $return; + } + + if ( 'nothumb' == $target || 'all' == $target || 'full' == $target || $scaled ) { + $tag = false; + if ( isset($backup_sizes['full-orig']) ) { + if ( ( !defined('IMAGE_EDIT_OVERWRITE') || !IMAGE_EDIT_OVERWRITE ) && $backup_sizes['full-orig']['file'] != $path_parts['basename'] ) + $tag = "full-$suffix"; + } else { + $tag = 'full-orig'; + } + + if ( $tag ) + $backup_sizes[$tag] = array('width' => $meta['width'], 'height' => $meta['height'], 'file' => $path_parts['basename']); + + $success = update_attached_file( $post_id, $new_path ); + + $meta['file'] = _wp_relative_upload_path( $new_path ); + + $size = $img->get_size(); + $meta['width'] = $size['width']; + $meta['height'] = $size['height']; + + if ( $success && ('nothumb' == $target || 'all' == $target) ) { + $sizes = get_intermediate_image_sizes(); + if ( 'nothumb' == $target ) + $sizes = array_diff( $sizes, array('thumbnail') ); + } + + $return->fw = $meta['width']; + $return->fh = $meta['height']; + } elseif ( 'thumbnail' == $target ) { + $sizes = array( 'thumbnail' ); + $success = $delete = $nocrop = true; + } + + if ( isset( $sizes ) ) { + $_sizes = array(); + + foreach ( $sizes as $size ) { + $tag = false; + if ( isset( $meta['sizes'][$size] ) ) { + if ( isset($backup_sizes["$size-orig"]) ) { + if ( ( !defined('IMAGE_EDIT_OVERWRITE') || !IMAGE_EDIT_OVERWRITE ) && $backup_sizes["$size-orig"]['file'] != $meta['sizes'][$size]['file'] ) + $tag = "$size-$suffix"; + } else { + $tag = "$size-orig"; + } + + if ( $tag ) + $backup_sizes[$tag] = $meta['sizes'][$size]; + } + + if ( isset( $_wp_additional_image_sizes[ $size ] ) ) { + $width = intval( $_wp_additional_image_sizes[ $size ]['width'] ); + $height = intval( $_wp_additional_image_sizes[ $size ]['height'] ); + $crop = ( $nocrop ) ? false : $_wp_additional_image_sizes[ $size ]['crop']; + } else { + $height = get_option( "{$size}_size_h" ); + $width = get_option( "{$size}_size_w" ); + $crop = ( $nocrop ) ? false : get_option( "{$size}_crop" ); + } + + $_sizes[ $size ] = array( 'width' => $width, 'height' => $height, 'crop' => $crop ); + } + + $meta['sizes'] = array_merge( $meta['sizes'], $img->multi_resize( $_sizes ) ); + } + + unset( $img ); + + if ( $success ) { + wp_update_attachment_metadata( $post_id, $meta ); + update_post_meta( $post_id, '_wp_attachment_backup_sizes', $backup_sizes); + + if ( $target == 'thumbnail' || $target == 'all' || $target == 'full' ) { + // Check if it's an image edit from attachment edit screen + if ( ! empty( $_REQUEST['context'] ) && 'edit-attachment' == $_REQUEST['context'] ) { + $thumb_url = wp_get_attachment_image_src( $post_id, array( 900, 600 ), true ); + $return->thumbnail = $thumb_url[0]; + } else { + $file_url = wp_get_attachment_url($post_id); + if ( ! empty( $meta['sizes']['thumbnail'] ) && $thumb = $meta['sizes']['thumbnail'] ) { + $return->thumbnail = path_join( dirname($file_url), $thumb['file'] ); + } else { + $return->thumbnail = "$file_url?w=128&h=128"; + } + } + } + } else { + $delete = true; + } + + if ( $delete ) { + + /** This filter is documented in wp-admin/custom-header.php */ + $delpath = apply_filters( 'wp_delete_file', $new_path ); + @unlink( $delpath ); + } + + $return->msg = esc_js( __('Image saved') ); + return $return; +} diff --git a/wp-admin/includes/image.php b/wp-admin/includes/image.php new file mode 100644 index 0000000..5fc7161 --- /dev/null +++ b/wp-admin/includes/image.php @@ -0,0 +1,604 @@ +crop( $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h, $src_abs ); + if ( is_wp_error( $src ) ) + return $src; + + if ( ! $dst_file ) + $dst_file = str_replace( basename( $src_file ), 'cropped-' . basename( $src_file ), $src_file ); + + /* + * The directory containing the original file may no longer exist when + * using a replication plugin. + */ + wp_mkdir_p( dirname( $dst_file ) ); + + $dst_file = dirname( $dst_file ) . '/' . wp_unique_filename( dirname( $dst_file ), basename( $dst_file ) ); + + $result = $editor->save( $dst_file ); + if ( is_wp_error( $result ) ) + return $result; + + return $dst_file; +} + +/** + * Generate post thumbnail attachment meta data. + * + * @since 2.1.0 + * + * @param int $attachment_id Attachment Id to process. + * @param string $file Filepath of the Attached image. + * @return mixed Metadata for attachment. + */ +function wp_generate_attachment_metadata( $attachment_id, $file ) { + $attachment = get_post( $attachment_id ); + + $metadata = array(); + $support = false; + if ( preg_match('!^image/!', get_post_mime_type( $attachment )) && file_is_displayable_image($file) ) { + $imagesize = getimagesize( $file ); + $metadata['width'] = $imagesize[0]; + $metadata['height'] = $imagesize[1]; + + // Make the file path relative to the upload dir. + $metadata['file'] = _wp_relative_upload_path($file); + + // Make thumbnails and other intermediate sizes. + global $_wp_additional_image_sizes; + + $sizes = array(); + foreach ( get_intermediate_image_sizes() as $s ) { + $sizes[$s] = array( 'width' => '', 'height' => '', 'crop' => false ); + if ( isset( $_wp_additional_image_sizes[$s]['width'] ) ) + $sizes[$s]['width'] = intval( $_wp_additional_image_sizes[$s]['width'] ); // For theme-added sizes + else + $sizes[$s]['width'] = get_option( "{$s}_size_w" ); // For default sizes set in options + if ( isset( $_wp_additional_image_sizes[$s]['height'] ) ) + $sizes[$s]['height'] = intval( $_wp_additional_image_sizes[$s]['height'] ); // For theme-added sizes + else + $sizes[$s]['height'] = get_option( "{$s}_size_h" ); // For default sizes set in options + if ( isset( $_wp_additional_image_sizes[$s]['crop'] ) ) + $sizes[$s]['crop'] = $_wp_additional_image_sizes[$s]['crop']; // For theme-added sizes + else + $sizes[$s]['crop'] = get_option( "{$s}_crop" ); // For default sizes set in options + } + + /** + * Filter the image sizes automatically generated when uploading an image. + * + * @since 2.9.0 + * + * @param array $sizes An associative array of image sizes. + */ + $sizes = apply_filters( 'intermediate_image_sizes_advanced', $sizes ); + + if ( $sizes ) { + $editor = wp_get_image_editor( $file ); + + if ( ! is_wp_error( $editor ) ) + $metadata['sizes'] = $editor->multi_resize( $sizes ); + } else { + $metadata['sizes'] = array(); + } + + // Fetch additional metadata from EXIF/IPTC. + $image_meta = wp_read_image_metadata( $file ); + if ( $image_meta ) + $metadata['image_meta'] = $image_meta; + + } elseif ( preg_match( '#^video/#', get_post_mime_type( $attachment ) ) ) { + $metadata = wp_read_video_metadata( $file ); + $support = current_theme_supports( 'post-thumbnails', 'attachment:video' ) || post_type_supports( 'attachment:video', 'thumbnail' ); + } elseif ( preg_match( '#^audio/#', get_post_mime_type( $attachment ) ) ) { + $metadata = wp_read_audio_metadata( $file ); + $support = current_theme_supports( 'post-thumbnails', 'attachment:audio' ) || post_type_supports( 'attachment:audio', 'thumbnail' ); + } + + if ( $support && ! empty( $metadata['image']['data'] ) ) { + // Check for existing cover. + $hash = md5( $metadata['image']['data'] ); + $posts = get_posts( array( + 'fields' => 'ids', + 'post_type' => 'attachment', + 'post_mime_type' => $metadata['image']['mime'], + 'post_status' => 'inherit', + 'posts_per_page' => 1, + 'meta_key' => '_cover_hash', + 'meta_value' => $hash + ) ); + $exists = reset( $posts ); + + if ( ! empty( $exists ) ) { + update_post_meta( $attachment_id, '_thumbnail_id', $exists ); + } else { + $ext = '.jpg'; + switch ( $metadata['image']['mime'] ) { + case 'image/gif': + $ext = '.gif'; + break; + case 'image/png': + $ext = '.png'; + break; + } + $basename = str_replace( '.', '-', basename( $file ) ) . '-image' . $ext; + $uploaded = wp_upload_bits( $basename, '', $metadata['image']['data'] ); + if ( false === $uploaded['error'] ) { + $image_attachment = array( + 'post_mime_type' => $metadata['image']['mime'], + 'post_type' => 'attachment', + 'post_content' => '', + ); + /** + * Filter the parameters for the attachment thumbnail creation. + * + * @since 3.9.0 + * + * @param array $image_attachment An array of parameters to create the thumbnail. + * @param array $metadata Current attachment metadata. + * @param array $uploaded An array containing the thumbnail path and url. + */ + $image_attachment = apply_filters( 'attachment_thumbnail_args', $image_attachment, $metadata, $uploaded ); + + $sub_attachment_id = wp_insert_attachment( $image_attachment, $uploaded['file'] ); + add_post_meta( $sub_attachment_id, '_cover_hash', $hash ); + $attach_data = wp_generate_attachment_metadata( $sub_attachment_id, $uploaded['file'] ); + wp_update_attachment_metadata( $sub_attachment_id, $attach_data ); + update_post_meta( $attachment_id, '_thumbnail_id', $sub_attachment_id ); + } + } + } + + // Remove the blob of binary data from the array. + if ( isset( $metadata['image']['data'] ) ) + unset( $metadata['image']['data'] ); + + /** + * Filter the generated attachment meta data. + * + * @since 2.1.0 + * + * @param array $metadata An array of attachment meta data. + * @param int $attachment_id Current attachment ID. + */ + return apply_filters( 'wp_generate_attachment_metadata', $metadata, $attachment_id ); +} + +/** + * Convert a fraction string to a decimal. + * + * @since 2.5.0 + * + * @param string $str + * @return int|float + */ +function wp_exif_frac2dec($str) { + @list( $n, $d ) = explode( '/', $str ); + if ( !empty($d) ) + return $n / $d; + return $str; +} + +/** + * Convert the exif date format to a unix timestamp. + * + * @since 2.5.0 + * + * @param string $str + * @return int + */ +function wp_exif_date2ts($str) { + @list( $date, $time ) = explode( ' ', trim($str) ); + @list( $y, $m, $d ) = explode( ':', $date ); + + return strtotime( "{$y}-{$m}-{$d} {$time}" ); +} + +/** + * Get extended image metadata, exif or iptc as available. + * + * Retrieves the EXIF metadata aperture, credit, camera, caption, copyright, iso + * created_timestamp, focal_length, shutter_speed, and title. + * + * The IPTC metadata that is retrieved is APP13, credit, byline, created date + * and time, caption, copyright, and title. Also includes FNumber, Model, + * DateTimeDigitized, FocalLength, ISOSpeedRatings, and ExposureTime. + * + * @todo Try other exif libraries if available. + * @since 2.5.0 + * + * @param string $file + * @return bool|array False on failure. Image metadata array on success. + */ +function wp_read_image_metadata( $file ) { + if ( ! file_exists( $file ) ) + return false; + + list( , , $sourceImageType ) = getimagesize( $file ); + + /* + * EXIF contains a bunch of data we'll probably never need formatted in ways + * that are difficult to use. We'll normalize it and just extract the fields + * that are likely to be useful. Fractions and numbers are converted to + * floats, dates to unix timestamps, and everything else to strings. + */ + $meta = array( + 'aperture' => 0, + 'credit' => '', + 'camera' => '', + 'caption' => '', + 'created_timestamp' => 0, + 'copyright' => '', + 'focal_length' => 0, + 'iso' => 0, + 'shutter_speed' => 0, + 'title' => '', + 'orientation' => 0, + ); + + /* + * Read IPTC first, since it might contain data not available in exif such + * as caption, description etc. + */ + if ( is_callable( 'iptcparse' ) ) { + getimagesize( $file, $info ); + + if ( ! empty( $info['APP13'] ) ) { + $iptc = iptcparse( $info['APP13'] ); + + // Headline, "A brief synopsis of the caption." + if ( ! empty( $iptc['2#105'][0] ) ) { + $meta['title'] = trim( $iptc['2#105'][0] ); + /* + * Title, "Many use the Title field to store the filename of the image, + * though the field may be used in many ways." + */ + } elseif ( ! empty( $iptc['2#005'][0] ) ) { + $meta['title'] = trim( $iptc['2#005'][0] ); + } + + if ( ! empty( $iptc['2#120'][0] ) ) { // description / legacy caption + $caption = trim( $iptc['2#120'][0] ); + if ( empty( $meta['title'] ) ) { + mbstring_binary_safe_encoding(); + $caption_length = strlen( $caption ); + reset_mbstring_encoding(); + + // Assume the title is stored in 2:120 if it's short. + if ( $caption_length < 80 ) { + $meta['title'] = $caption; + } else { + $meta['caption'] = $caption; + } + } elseif ( $caption != $meta['title'] ) { + $meta['caption'] = $caption; + } + } + + if ( ! empty( $iptc['2#110'][0] ) ) // credit + $meta['credit'] = trim( $iptc['2#110'][0] ); + elseif ( ! empty( $iptc['2#080'][0] ) ) // creator / legacy byline + $meta['credit'] = trim( $iptc['2#080'][0] ); + + if ( ! empty( $iptc['2#055'][0] ) and ! empty( $iptc['2#060'][0] ) ) // created date and time + $meta['created_timestamp'] = strtotime( $iptc['2#055'][0] . ' ' . $iptc['2#060'][0] ); + + if ( ! empty( $iptc['2#116'][0] ) ) // copyright + $meta['copyright'] = trim( $iptc['2#116'][0] ); + } + } + + /** + * Filter the image types to check for exif data. + * + * @since 2.5.0 + * + * @param array $image_types Image types to check for exif data. + */ + if ( is_callable( 'exif_read_data' ) && in_array( $sourceImageType, apply_filters( 'wp_read_image_metadata_types', array( IMAGETYPE_JPEG, IMAGETYPE_TIFF_II, IMAGETYPE_TIFF_MM ) ) ) ) { + $exif = @exif_read_data( $file ); + + if ( empty( $meta['title'] ) && ! empty( $exif['Title'] ) ) { + $meta['title'] = trim( $exif['Title'] ); + } + + if ( ! empty( $exif['ImageDescription'] ) ) { + mbstring_binary_safe_encoding(); + $description_length = strlen( $exif['ImageDescription'] ); + reset_mbstring_encoding(); + + if ( empty( $meta['title'] ) && $description_length < 80 ) { + // Assume the title is stored in ImageDescription + $meta['title'] = trim( $exif['ImageDescription'] ); + if ( empty( $meta['caption'] ) && ! empty( $exif['COMPUTED']['UserComment'] ) && trim( $exif['COMPUTED']['UserComment'] ) != $meta['title'] ) { + $meta['caption'] = trim( $exif['COMPUTED']['UserComment'] ); + } + } elseif ( empty( $meta['caption'] ) && trim( $exif['ImageDescription'] ) != $meta['title'] ) { + $meta['caption'] = trim( $exif['ImageDescription'] ); + } + } elseif ( empty( $meta['caption'] ) && ! empty( $exif['Comments'] ) && trim( $exif['Comments'] ) != $meta['title'] ) { + $meta['caption'] = trim( $exif['Comments'] ); + } + + if ( empty( $meta['credit'] ) ) { + if ( ! empty( $exif['Artist'] ) ) { + $meta['credit'] = trim( $exif['Artist'] ); + } elseif ( ! empty($exif['Author'] ) ) { + $meta['credit'] = trim( $exif['Author'] ); + } + } + + if ( empty( $meta['copyright'] ) && ! empty( $exif['Copyright'] ) ) { + $meta['copyright'] = trim( $exif['Copyright'] ); + } + if ( ! empty( $exif['FNumber'] ) ) { + $meta['aperture'] = round( wp_exif_frac2dec( $exif['FNumber'] ), 2 ); + } + if ( ! empty( $exif['Model'] ) ) { + $meta['camera'] = trim( $exif['Model'] ); + } + if ( empty( $meta['created_timestamp'] ) && ! empty( $exif['DateTimeDigitized'] ) ) { + $meta['created_timestamp'] = wp_exif_date2ts( $exif['DateTimeDigitized'] ); + } + if ( ! empty( $exif['FocalLength'] ) ) { + $meta['focal_length'] = (string) wp_exif_frac2dec( $exif['FocalLength'] ); + } + if ( ! empty( $exif['ISOSpeedRatings'] ) ) { + $meta['iso'] = is_array( $exif['ISOSpeedRatings'] ) ? reset( $exif['ISOSpeedRatings'] ) : $exif['ISOSpeedRatings']; + $meta['iso'] = trim( $meta['iso'] ); + } + if ( ! empty( $exif['ExposureTime'] ) ) { + $meta['shutter_speed'] = (string) wp_exif_frac2dec( $exif['ExposureTime'] ); + } + if ( ! empty( $exif['Orientation'] ) ) { + $meta['orientation'] = $exif['Orientation']; + } + } + + foreach ( array( 'title', 'caption', 'credit', 'copyright', 'camera', 'iso' ) as $key ) { + if ( $meta[ $key ] && ! seems_utf8( $meta[ $key ] ) ) { + $meta[ $key ] = utf8_encode( $meta[ $key ] ); + } + } + + foreach ( $meta as &$value ) { + if ( is_string( $value ) ) { + $value = wp_kses_post( $value ); + } + } + + /** + * Filter the array of meta data read from an image's exif data. + * + * @since 2.5.0 + * + * @param array $meta Image meta data. + * @param string $file Path to image file. + * @param int $sourceImageType Type of image. + */ + return apply_filters( 'wp_read_image_metadata', $meta, $file, $sourceImageType ); + +} + +/** + * Validate that file is an image. + * + * @since 2.5.0 + * + * @param string $path File path to test if valid image. + * @return bool True if valid image, false if not valid image. + */ +function file_is_valid_image($path) { + $size = @getimagesize($path); + return !empty($size); +} + +/** + * Validate that file is suitable for displaying within a web page. + * + * @since 2.5.0 + * + * @param string $path File path to test. + * @return bool True if suitable, false if not suitable. + */ +function file_is_displayable_image($path) { + $displayable_image_types = array( IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG, IMAGETYPE_BMP ); + + $info = @getimagesize( $path ); + if ( empty( $info ) ) { + $result = false; + } elseif ( ! in_array( $info[2], $displayable_image_types ) ) { + $result = false; + } else { + $result = true; + } + + /** + * Filter whether the current image is displayable in the browser. + * + * @since 2.5.0 + * + * @param bool $result Whether the image can be displayed. Default true. + * @param string $path Path to the image. + */ + return apply_filters( 'file_is_displayable_image', $result, $path ); +} + +/** + * Load an image resource for editing. + * + * @since 2.9.0 + * + * @param string $attachment_id Attachment ID. + * @param string $mime_type Image mime type. + * @param string $size Optional. Image size, defaults to 'full'. + * @return resource|false The resulting image resource on success, false on failure. + */ +function load_image_to_edit( $attachment_id, $mime_type, $size = 'full' ) { + $filepath = _load_image_to_edit_path( $attachment_id, $size ); + if ( empty( $filepath ) ) + return false; + + switch ( $mime_type ) { + case 'image/jpeg': + $image = imagecreatefromjpeg($filepath); + break; + case 'image/png': + $image = imagecreatefrompng($filepath); + break; + case 'image/gif': + $image = imagecreatefromgif($filepath); + break; + default: + $image = false; + break; + } + if ( is_resource($image) ) { + /** + * Filter the current image being loaded for editing. + * + * @since 2.9.0 + * + * @param resource $image Current image. + * @param string $attachment_id Attachment ID. + * @param string $size Image size. + */ + $image = apply_filters( 'load_image_to_edit', $image, $attachment_id, $size ); + if ( function_exists('imagealphablending') && function_exists('imagesavealpha') ) { + imagealphablending($image, false); + imagesavealpha($image, true); + } + } + return $image; +} + +/** + * Retrieve the path or url of an attachment's attached file. + * + * If the attached file is not present on the local filesystem (usually due to replication plugins), + * then the url of the file is returned if url fopen is supported. + * + * @since 3.4.0 + * @access private + * + * @param string $attachment_id Attachment ID. + * @param string $size Optional. Image size, defaults to 'full'. + * @return string|false File path or url on success, false on failure. + */ +function _load_image_to_edit_path( $attachment_id, $size = 'full' ) { + $filepath = get_attached_file( $attachment_id ); + + if ( $filepath && file_exists( $filepath ) ) { + if ( 'full' != $size && ( $data = image_get_intermediate_size( $attachment_id, $size ) ) ) { + /** + * Filter the path to the current image. + * + * The filter is evaluated for all image sizes except 'full'. + * + * @since 3.1.0 + * + * @param string $path Path to the current image. + * @param string $attachment_id Attachment ID. + * @param string $size Size of the image. + */ + $filepath = apply_filters( 'load_image_to_edit_filesystempath', path_join( dirname( $filepath ), $data['file'] ), $attachment_id, $size ); + } + } elseif ( function_exists( 'fopen' ) && function_exists( 'ini_get' ) && true == ini_get( 'allow_url_fopen' ) ) { + /** + * Filter the image URL if not in the local filesystem. + * + * The filter is only evaluated if fopen is enabled on the server. + * + * @since 3.1.0 + * + * @param string $image_url Current image URL. + * @param string $attachment_id Attachment ID. + * @param string $size Size of the image. + */ + $filepath = apply_filters( 'load_image_to_edit_attachmenturl', wp_get_attachment_url( $attachment_id ), $attachment_id, $size ); + } + + /** + * Filter the returned path or URL of the current image. + * + * @since 2.9.0 + * + * @param string|bool $filepath File path or URL to current image, or false. + * @param string $attachment_id Attachment ID. + * @param string $size Size of the image. + */ + return apply_filters( 'load_image_to_edit_path', $filepath, $attachment_id, $size ); +} + +/** + * Copy an existing image file. + * + * @since 3.4.0 + * @access private + * + * @param string $attachment_id Attachment ID. + * @return string|false New file path on success, false on failure. + */ +function _copy_image_file( $attachment_id ) { + $dst_file = $src_file = get_attached_file( $attachment_id ); + if ( ! file_exists( $src_file ) ) + $src_file = _load_image_to_edit_path( $attachment_id ); + + if ( $src_file ) { + $dst_file = str_replace( basename( $dst_file ), 'copy-' . basename( $dst_file ), $dst_file ); + $dst_file = dirname( $dst_file ) . '/' . wp_unique_filename( dirname( $dst_file ), basename( $dst_file ) ); + + /* + * The directory containing the original file may no longer + * exist when using a replication plugin. + */ + wp_mkdir_p( dirname( $dst_file ) ); + + if ( ! @copy( $src_file, $dst_file ) ) + $dst_file = false; + } else { + $dst_file = false; + } + + return $dst_file; +} diff --git a/wp-admin/includes/import.php b/wp-admin/includes/import.php new file mode 100644 index 0000000..ab8e8e3 --- /dev/null +++ b/wp-admin/includes/import.php @@ -0,0 +1,206 @@ + false, 'test_type' => false ); + $_FILES['import']['name'] .= '.txt'; + $file = wp_handle_upload( $_FILES['import'], $overrides ); + + if ( isset( $file['error'] ) ) + return $file; + + $url = $file['url']; + $type = $file['type']; + $file = $file['file']; + $filename = basename( $file ); + + // Construct the object array + $object = array( 'post_title' => $filename, + 'post_content' => $url, + 'post_mime_type' => $type, + 'guid' => $url, + 'context' => 'import', + 'post_status' => 'private' + ); + + // Save the data + $id = wp_insert_attachment( $object, $file ); + + /* + * Schedule a cleanup for one day from now in case of failed + * import or missing wp_import_cleanup() call. + */ + wp_schedule_single_event( time() + DAY_IN_SECONDS, 'importer_scheduled_cleanup', array( $id ) ); + + return array( 'file' => $file, 'id' => $id ); +} + +/** + * Returns a list from WordPress.org of popular importer plugins. + * + * @since 3.5.0 + * + * @return array Importers with metadata for each. + */ +function wp_get_popular_importers() { + include( ABSPATH . WPINC . '/version.php' ); // include an unmodified $wp_version + + $locale = get_locale(); + $popular_importers = get_site_transient( 'popular_importers_' . $locale ); + + if ( ! $popular_importers ) { + $url = add_query_arg( 'locale', get_locale(), 'http://api.wordpress.org/core/importers/1.1/' ); + $options = array( 'user-agent' => 'WordPress/' . $wp_version . '; ' . home_url() ); + $response = wp_remote_get( $url, $options ); + $popular_importers = json_decode( wp_remote_retrieve_body( $response ), true ); + + if ( is_array( $popular_importers ) ) + set_site_transient( 'popular_importers_' . $locale, $popular_importers, 2 * DAY_IN_SECONDS ); + else + $popular_importers = false; + } + + if ( is_array( $popular_importers ) ) { + // If the data was received as translated, return it as-is. + if ( $popular_importers['translated'] ) + return $popular_importers['importers']; + + foreach ( $popular_importers['importers'] as &$importer ) { + $importer['description'] = translate( $importer['description'] ); + if ( $importer['name'] != 'WordPress' ) + $importer['name'] = translate( $importer['name'] ); + } + return $popular_importers['importers']; + } + + return array( + // slug => name, description, plugin slug, and register_importer() slug + 'blogger' => array( + 'name' => __( 'Blogger' ), + 'description' => __( 'Install the Blogger importer to import posts, comments, and users from a Blogger blog.' ), + 'plugin-slug' => 'blogger-importer', + 'importer-id' => 'blogger', + ), + 'wpcat2tag' => array( + 'name' => __( 'Categories and Tags Converter' ), + 'description' => __( 'Install the category/tag converter to convert existing categories to tags or tags to categories, selectively.' ), + 'plugin-slug' => 'wpcat2tag-importer', + 'importer-id' => 'wp-cat2tag', + ), + 'livejournal' => array( + 'name' => __( 'LiveJournal' ), + 'description' => __( 'Install the LiveJournal importer to import posts from LiveJournal using their API.' ), + 'plugin-slug' => 'livejournal-importer', + 'importer-id' => 'livejournal', + ), + 'movabletype' => array( + 'name' => __( 'Movable Type and TypePad' ), + 'description' => __( 'Install the Movable Type importer to import posts and comments from a Movable Type or TypePad blog.' ), + 'plugin-slug' => 'movabletype-importer', + 'importer-id' => 'mt', + ), + 'opml' => array( + 'name' => __( 'Blogroll' ), + 'description' => __( 'Install the blogroll importer to import links in OPML format.' ), + 'plugin-slug' => 'opml-importer', + 'importer-id' => 'opml', + ), + 'rss' => array( + 'name' => __( 'RSS' ), + 'description' => __( 'Install the RSS importer to import posts from an RSS feed.' ), + 'plugin-slug' => 'rss-importer', + 'importer-id' => 'rss', + ), + 'tumblr' => array( + 'name' => __( 'Tumblr' ), + 'description' => __( 'Install the Tumblr importer to import posts & media from Tumblr using their API.' ), + 'plugin-slug' => 'tumblr-importer', + 'importer-id' => 'tumblr', + ), + 'wordpress' => array( + 'name' => 'WordPress', + 'description' => __( 'Install the WordPress importer to import posts, pages, comments, custom fields, categories, and tags from a WordPress export file.' ), + 'plugin-slug' => 'wordpress-importer', + 'importer-id' => 'wordpress', + ), + ); +} diff --git a/wp-admin/includes/list-table.php b/wp-admin/includes/list-table.php new file mode 100644 index 0000000..04b552f --- /dev/null +++ b/wp-admin/includes/list-table.php @@ -0,0 +1,113 @@ + 'posts', + 'WP_Media_List_Table' => 'media', + 'WP_Terms_List_Table' => 'terms', + 'WP_Users_List_Table' => 'users', + 'WP_Comments_List_Table' => 'comments', + 'WP_Post_Comments_List_Table' => 'comments', + 'WP_Links_List_Table' => 'links', + 'WP_Plugin_Install_List_Table' => 'plugin-install', + 'WP_Themes_List_Table' => 'themes', + 'WP_Theme_Install_List_Table' => array( 'themes', 'theme-install' ), + 'WP_Plugins_List_Table' => 'plugins', + // Network Admin + 'WP_MS_Sites_List_Table' => 'ms-sites', + 'WP_MS_Users_List_Table' => 'ms-users', + 'WP_MS_Themes_List_Table' => 'ms-themes', + ); + + if ( isset( $core_classes[ $class ] ) ) { + foreach ( (array) $core_classes[ $class ] as $required ) + require_once( ABSPATH . 'wp-admin/includes/class-wp-' . $required . '-list-table.php' ); + + if ( isset( $args['screen'] ) ) + $args['screen'] = convert_to_screen( $args['screen'] ); + elseif ( isset( $GLOBALS['hook_suffix'] ) ) + $args['screen'] = get_current_screen(); + else + $args['screen'] = null; + + return new $class( $args ); + } + + return false; +} + +/** + * Register column headers for a particular screen. + * + * @since 2.7.0 + * + * @param string $screen The handle for the screen to add help to. This is usually the hook name returned by the add_*_page() functions. + * @param array $columns An array of columns with column IDs as the keys and translated column names as the values + * @see get_column_headers(), print_column_headers(), get_hidden_columns() + */ +function register_column_headers($screen, $columns) { + $wp_list_table = new _WP_List_Table_Compat($screen, $columns); +} + +/** + * Prints column headers for a particular screen. + * + * @since 2.7.0 + */ +function print_column_headers($screen, $id = true) { + $wp_list_table = new _WP_List_Table_Compat($screen); + + $wp_list_table->print_column_headers($id); +} + +/** + * Helper class to be used only by back compat functions + * + * @since 3.1.0 + */ +class _WP_List_Table_Compat extends WP_List_Table { + public $_screen; + public $_columns; + + public function __construct( $screen, $columns = array() ) { + if ( is_string( $screen ) ) + $screen = convert_to_screen( $screen ); + + $this->_screen = $screen; + + if ( !empty( $columns ) ) { + $this->_columns = $columns; + add_filter( 'manage_' . $screen->id . '_columns', array( $this, 'get_columns' ), 0 ); + } + } + + protected function get_column_info() { + $columns = get_column_headers( $this->_screen ); + $hidden = get_hidden_columns( $this->_screen ); + $sortable = array(); + + return array( $columns, $hidden, $sortable ); + } + + public function get_columns() { + return $this->_columns; + } +} diff --git a/wp-admin/includes/media.php b/wp-admin/includes/media.php new file mode 100644 index 0000000..7fa4dd8 --- /dev/null +++ b/wp-admin/includes/media.php @@ -0,0 +1,3020 @@ + __('From Computer'), // handler action suffix => tab text + 'type_url' => __('From URL'), + 'gallery' => __('Gallery'), + 'library' => __('Media Library') + ); + + /** + * Filter the available tabs in the legacy (pre-3.5.0) media popup. + * + * @since 2.5.0 + * + * @param array $_default_tabs An array of media tabs. + */ + return apply_filters( 'media_upload_tabs', $_default_tabs ); +} + +/** + * Adds the gallery tab back to the tabs array if post has image attachments + * + * @since 2.5.0 + * + * @param array $tabs + * @return array $tabs with gallery if post has image attachment + */ +function update_gallery_tab($tabs) { + global $wpdb; + + if ( !isset($_REQUEST['post_id']) ) { + unset($tabs['gallery']); + return $tabs; + } + + $post_id = intval($_REQUEST['post_id']); + + if ( $post_id ) + $attachments = intval( $wpdb->get_var( $wpdb->prepare( "SELECT count(*) FROM $wpdb->posts WHERE post_type = 'attachment' AND post_status != 'trash' AND post_parent = %d", $post_id ) ) ); + + if ( empty($attachments) ) { + unset($tabs['gallery']); + return $tabs; + } + + $tabs['gallery'] = sprintf(__('Gallery (%s)'), "$attachments"); + + return $tabs; +} +add_filter('media_upload_tabs', 'update_gallery_tab'); + +/** + * {@internal Missing Short Description}} + * + * @since 2.5.0 + */ +function the_media_upload_tabs() { + global $redir_tab; + $tabs = media_upload_tabs(); + $default = 'type'; + + if ( !empty($tabs) ) { + echo "
      \n"; + if ( isset($redir_tab) && array_key_exists($redir_tab, $tabs) ) { + $current = $redir_tab; + } elseif ( isset($_GET['tab']) && array_key_exists($_GET['tab'], $tabs) ) { + $current = $_GET['tab']; + } else { + /** This filter is documented in wp-admin/media-upload.php */ + $current = apply_filters( 'media_upload_default_tab', $default ); + } + + foreach ( $tabs as $callback => $text ) { + $class = ''; + + if ( $current == $callback ) + $class = " class='current'"; + + $href = add_query_arg(array('tab' => $callback, 's' => false, 'paged' => false, 'post_mime_type' => false, 'm' => false)); + $link = "$text"; + echo "\t
    • $link
    • \n"; + } + echo "
    \n"; + } +} + +/** + * {@internal Missing Short Description}} + * + * @since 2.5.0 + * + * @param integer $id image attachment id + * @param string $caption image caption + * @param string $alt image alt attribute + * @param string $title image title attribute + * @param string $align image css alignment property + * @param string $url image src url + * @param string|bool $rel image rel attribute + * @param string $size image size (thumbnail, medium, large, full or added with add_image_size() ) + * @return string the html to insert into editor + */ +function get_image_send_to_editor($id, $caption, $title, $align, $url='', $rel = false, $size='medium', $alt = '') { + + $html = get_image_tag($id, $alt, '', $align, $size); + + $rel = $rel ? ' rel="attachment wp-att-' . esc_attr($id).'"' : ''; + + if ( $url ) + $html = '$html"; + + /** + * Filter the image HTML markup to send to the editor. + * + * @since 2.5.0 + * + * @param string $html The image HTML markup to send. + * @param int $id The attachment id. + * @param string $caption The image caption. + * @param string $title The image title. + * @param string $align The image alignment. + * @param string $url The image source URL. + * @param string $size The image size. + * @param string $alt The image alternative, or alt, text. + */ + $html = apply_filters( 'image_send_to_editor', $html, $id, $caption, $title, $align, $url, $size, $alt ); + + return $html; +} + +/** + * Adds image shortcode with caption to editor + * + * @since 2.6.0 + * + * @param string $html + * @param integer $id + * @param string $caption image caption + * @param string $alt image alt attribute + * @param string $title image title attribute + * @param string $align image css alignment property + * @param string $url image src url + * @param string $size image size (thumbnail, medium, large, full or added with add_image_size() ) + * @return string + */ +function image_add_caption( $html, $id, $caption, $title, $align, $url, $size, $alt = '' ) { + + /** + * Filter the caption text. + * + * Note: If the caption text is empty, the caption shortcode will not be appended + * to the image HTML when inserted into the editor. + * + * Passing an empty value also prevents the {@see 'image_add_caption_shortcode'} + * filter from being evaluated at the end of {@see image_add_caption()}. + * + * @since 4.1.0 + * + * @param string $caption The original caption text. + * @param int $id The attachment ID. + */ + $caption = apply_filters( 'image_add_caption_text', $caption, $id ); + + /** + * Filter whether to disable captions. + * + * Prevents image captions from being appended to image HTML when inserted into the editor. + * + * @since 2.6.0 + * + * @param bool $bool Whether to disable appending captions. Returning true to the filter + * will disable captions. Default empty string. + */ + if ( empty($caption) || apply_filters( 'disable_captions', '' ) ) + return $html; + + $id = ( 0 < (int) $id ) ? 'attachment_' . $id : ''; + + if ( ! preg_match( '/width=["\']([0-9]+)/', $html, $matches ) ) + return $html; + + $width = $matches[1]; + + $caption = str_replace( array("\r\n", "\r"), "\n", $caption); + $caption = preg_replace_callback( '/<[a-zA-Z0-9]+(?: [^<>]+>)*/', '_cleanup_image_add_caption', $caption ); + + // Convert any remaining line breaks to
    . + $caption = preg_replace( '/[ \n\t]*\n[ \t]*/', '
    ', $caption ); + + $html = preg_replace( '/(class=["\'][^\'"]*)align(none|left|right|center)\s?/', '$1', $html ); + if ( empty($align) ) + $align = 'none'; + + $shcode = '[caption id="' . $id . '" align="align' . $align . '" width="' . $width . '"]' . $html . ' ' . $caption . '[/caption]'; + + /** + * Filter the image HTML markup including the caption shortcode. + * + * @since 2.6.0 + * + * @param string $shcode The image HTML markup with caption shortcode. + * @param string $html The image HTML markup. + */ + return apply_filters( 'image_add_caption_shortcode', $shcode, $html ); +} +add_filter( 'image_send_to_editor', 'image_add_caption', 20, 8 ); + +/** + * Private preg_replace callback used in image_add_caption() + * + * @access private + * @since 3.4.0 + */ +function _cleanup_image_add_caption( $matches ) { + // Remove any line breaks from inside the tags. + return preg_replace( '/[\r\n\t]+/', ' ', $matches[0] ); +} + +/** + * Adds image html to editor + * + * @since 2.5.0 + * + * @param string $html + */ +function media_send_to_editor($html) { +?> + + false )) { + + $time = current_time('mysql'); + if ( $post = get_post($post_id) ) { + if ( substr( $post->post_date, 0, 4 ) > 0 ) + $time = $post->post_date; + } + + $name = $_FILES[$file_id]['name']; + $file = wp_handle_upload($_FILES[$file_id], $overrides, $time); + + if ( isset($file['error']) ) + return new WP_Error( 'upload_error', $file['error'] ); + + $name_parts = pathinfo($name); + $name = trim( substr( $name, 0, -(1 + strlen($name_parts['extension'])) ) ); + + $url = $file['url']; + $type = $file['type']; + $file = $file['file']; + $title = $name; + $content = ''; + + if ( preg_match( '#^audio#', $type ) ) { + $meta = wp_read_audio_metadata( $file ); + + if ( ! empty( $meta['title'] ) ) + $title = $meta['title']; + + $content = ''; + + if ( ! empty( $title ) ) { + + if ( ! empty( $meta['album'] ) && ! empty( $meta['artist'] ) ) { + /* translators: 1: audio track title, 2: album title, 3: artist name */ + $content .= sprintf( __( '"%1$s" from %2$s by %3$s.' ), $title, $meta['album'], $meta['artist'] ); + } else if ( ! empty( $meta['album'] ) ) { + /* translators: 1: audio track title, 2: album title */ + $content .= sprintf( __( '"%1$s" from %2$s.' ), $title, $meta['album'] ); + } else if ( ! empty( $meta['artist'] ) ) { + /* translators: 1: audio track title, 2: artist name */ + $content .= sprintf( __( '"%1$s" by %2$s.' ), $title, $meta['artist'] ); + } else { + $content .= sprintf( __( '"%s".' ), $title ); + } + + } else if ( ! empty( $meta['album'] ) ) { + + if ( ! empty( $meta['artist'] ) ) { + /* translators: 1: audio album title, 2: artist name */ + $content .= sprintf( __( '%1$s by %2$s.' ), $meta['album'], $meta['artist'] ); + } else { + $content .= $meta['album'] . '.'; + } + + } else if ( ! empty( $meta['artist'] ) ) { + + $content .= $meta['artist'] . '.'; + + } + + if ( ! empty( $meta['year'] ) ) + $content .= ' ' . sprintf( __( 'Released: %d.' ), $meta['year'] ); + + if ( ! empty( $meta['track_number'] ) ) { + $track_number = explode( '/', $meta['track_number'] ); + if ( isset( $track_number[1] ) ) + $content .= ' ' . sprintf( __( 'Track %1$s of %2$s.' ), number_format_i18n( $track_number[0] ), number_format_i18n( $track_number[1] ) ); + else + $content .= ' ' . sprintf( __( 'Track %1$s.' ), number_format_i18n( $track_number[0] ) ); + } + + if ( ! empty( $meta['genre'] ) ) + $content .= ' ' . sprintf( __( 'Genre: %s.' ), $meta['genre'] ); + + // Use image exif/iptc data for title and caption defaults if possible. + } elseif ( 0 === strpos( $type, 'image/' ) && $image_meta = @wp_read_image_metadata( $file ) ) { + if ( trim( $image_meta['title'] ) && ! is_numeric( sanitize_title( $image_meta['title'] ) ) ) + $title = $image_meta['title']; + if ( trim( $image_meta['caption'] ) ) + $content = $image_meta['caption']; + } + + // Construct the attachment array + $attachment = array_merge( array( + 'post_mime_type' => $type, + 'guid' => $url, + 'post_parent' => $post_id, + 'post_title' => $title, + 'post_content' => $content, + ), $post_data ); + + // This should never be set as it would then overwrite an existing attachment. + if ( isset( $attachment['ID'] ) ) + unset( $attachment['ID'] ); + + // Save the data + $id = wp_insert_attachment($attachment, $file, $post_id); + if ( !is_wp_error($id) ) { + wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $file ) ); + } + + return $id; + +} + +/** + * This handles a sideloaded file in the same way as an uploaded file is handled by {@link media_handle_upload()} + * + * @since 2.6.0 + * + * @param array $file_array Array similar to a {@link $_FILES} upload array + * @param int $post_id The post ID the media is associated with + * @param string $desc Description of the sideloaded file + * @param array $post_data allows you to overwrite some of the attachment + * @return int|object The ID of the attachment or a WP_Error on failure + */ +function media_handle_sideload($file_array, $post_id, $desc = null, $post_data = array()) { + $overrides = array('test_form'=>false); + + $time = current_time( 'mysql' ); + if ( $post = get_post( $post_id ) ) { + if ( substr( $post->post_date, 0, 4 ) > 0 ) + $time = $post->post_date; + } + + $file = wp_handle_sideload( $file_array, $overrides, $time ); + if ( isset($file['error']) ) + return new WP_Error( 'upload_error', $file['error'] ); + + $url = $file['url']; + $type = $file['type']; + $file = $file['file']; + $title = preg_replace('/\.[^.]+$/', '', basename($file)); + $content = ''; + + // Use image exif/iptc data for title and caption defaults if possible. + if ( $image_meta = @wp_read_image_metadata($file) ) { + if ( trim( $image_meta['title'] ) && ! is_numeric( sanitize_title( $image_meta['title'] ) ) ) + $title = $image_meta['title']; + if ( trim( $image_meta['caption'] ) ) + $content = $image_meta['caption']; + } + + if ( isset( $desc ) ) + $title = $desc; + + // Construct the attachment array. + $attachment = array_merge( array( + 'post_mime_type' => $type, + 'guid' => $url, + 'post_parent' => $post_id, + 'post_title' => $title, + 'post_content' => $content, + ), $post_data ); + + // This should never be set as it would then overwrite an existing attachment. + if ( isset( $attachment['ID'] ) ) + unset( $attachment['ID'] ); + + // Save the attachment metadata + $id = wp_insert_attachment($attachment, $file, $post_id); + if ( !is_wp_error($id) ) + wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $file ) ); + + return $id; +} + +/** + * Adds the iframe to display content for the media upload page + * + * @since 2.5.0 + * + * @param string|callable $content_func + */ +function wp_iframe($content_func /* ... */) { + _wp_admin_html_begin(); +?> +<?php bloginfo('name') ?> › <?php _e('Uploads'); ?> — <?php _e('WordPress'); ?> + + + + + class="wp-core-ui no-js"> + + + + + + $post + ) ); + + $img = ' '; + + $id_attribute = $instance === 1 ? ' id="insert-media-button"' : ''; + printf( '%s', + $id_attribute, + esc_attr( $editor_id ), + esc_attr__( 'Add Media' ), + $img . __( 'Add Media' ) + ); + /** + * Filter the legacy (pre-3.5.0) media buttons. + * + * @since 2.5.0 + * @deprecated 3.5.0 Use 'media_buttons' action instead. + * + * @param string $string Media buttons context. Default empty. + */ + $legacy_filter = apply_filters( 'media_buttons_context', '' ); + + if ( $legacy_filter ) { + // #WP22559. Close if a plugin started by closing to open their own tag. + if ( 0 === stripos( trim( $legacy_filter ), '' ) ) + $legacy_filter .= ''; + echo $legacy_filter; + } +} +add_action( 'media_buttons', 'media_buttons' ); + +/** + * + * @global int $post_ID + * @param string $type + * @param int $post_id + * @param string $tab + * @return string + */ +function get_upload_iframe_src( $type = null, $post_id = null, $tab = null ) { + global $post_ID; + + if ( empty( $post_id ) ) + $post_id = $post_ID; + + $upload_iframe_src = add_query_arg( 'post_id', (int) $post_id, admin_url('media-upload.php') ); + + if ( $type && 'media' != $type ) + $upload_iframe_src = add_query_arg('type', $type, $upload_iframe_src); + + if ( ! empty( $tab ) ) + $upload_iframe_src = add_query_arg('tab', $tab, $upload_iframe_src); + + /** + * Filter the upload iframe source URL for a specific media type. + * + * The dynamic portion of the hook name, `$type`, refers to the type + * of media uploaded. + * + * @since 3.0.0 + * + * @param string $upload_iframe_src The upload iframe source URL by type. + */ + $upload_iframe_src = apply_filters( $type . '_upload_iframe_src', $upload_iframe_src ); + + return add_query_arg('TB_iframe', true, $upload_iframe_src); +} + +/** + * {@internal Missing Short Description}} + * + * @since 2.5.0 + * + * @return mixed void|object WP_Error on failure + */ +function media_upload_form_handler() { + check_admin_referer('media-form'); + + $errors = null; + + if ( isset($_POST['send']) ) { + $keys = array_keys($_POST['send']); + $send_id = (int) array_shift($keys); + } + + if ( !empty($_POST['attachments']) ) foreach ( $_POST['attachments'] as $attachment_id => $attachment ) { + $post = $_post = get_post($attachment_id, ARRAY_A); + + if ( !current_user_can( 'edit_post', $attachment_id ) ) + continue; + + if ( isset($attachment['post_content']) ) + $post['post_content'] = $attachment['post_content']; + if ( isset($attachment['post_title']) ) + $post['post_title'] = $attachment['post_title']; + if ( isset($attachment['post_excerpt']) ) + $post['post_excerpt'] = $attachment['post_excerpt']; + if ( isset($attachment['menu_order']) ) + $post['menu_order'] = $attachment['menu_order']; + + if ( isset($send_id) && $attachment_id == $send_id ) { + if ( isset($attachment['post_parent']) ) + $post['post_parent'] = $attachment['post_parent']; + } + + /** + * Filter the attachment fields to be saved. + * + * @since 2.5.0 + * + * @see wp_get_attachment_metadata() + * + * @param WP_Post $post The WP_Post object. + * @param array $attachment An array of attachment metadata. + */ + $post = apply_filters( 'attachment_fields_to_save', $post, $attachment ); + + if ( isset($attachment['image_alt']) ) { + $image_alt = wp_unslash( $attachment['image_alt'] ); + if ( $image_alt != get_post_meta($attachment_id, '_wp_attachment_image_alt', true) ) { + $image_alt = wp_strip_all_tags( $image_alt, true ); + + // Update_meta expects slashed. + update_post_meta( $attachment_id, '_wp_attachment_image_alt', wp_slash( $image_alt ) ); + } + } + + if ( isset($post['errors']) ) { + $errors[$attachment_id] = $post['errors']; + unset($post['errors']); + } + + if ( $post != $_post ) + wp_update_post($post); + + foreach ( get_attachment_taxonomies($post) as $t ) { + if ( isset($attachment[$t]) ) + wp_set_object_terms($attachment_id, array_map('trim', preg_split('/,+/', $attachment[$t])), $t, false); + } + } + + if ( isset($_POST['insert-gallery']) || isset($_POST['update-gallery']) ) { ?> + + $html"; + } + + /** + * Filter the HTML markup for a media item sent to the editor. + * + * @since 2.5.0 + * + * @see wp_get_attachment_metadata() + * + * @param string $html HTML markup for a media item sent to the editor. + * @param int $send_id The first key from the $_POST['send'] data. + * @param array $attachment Array of attachment metadata. + */ + $html = apply_filters( 'media_send_to_editor', $html, $send_id, $attachment ); + return media_send_to_editor($html); + } + + return $errors; +} + +/** + * {@internal Missing Short Description}} + * + * @since 2.5.0 + * + * @return null|string + */ +function wp_media_upload_handler() { + $errors = array(); + $id = 0; + + if ( isset($_POST['html-upload']) && !empty($_FILES) ) { + check_admin_referer('media-form'); + // Upload File button was clicked + $id = media_handle_upload('async-upload', $_REQUEST['post_id']); + unset($_FILES); + if ( is_wp_error($id) ) { + $errors['upload_error'] = $id; + $id = false; + } + } + + if ( !empty($_POST['insertonlybutton']) ) { + $src = $_POST['src']; + if ( !empty($src) && !strpos($src, '://') ) + $src = "http://$src"; + + if ( isset( $_POST['media_type'] ) && 'image' != $_POST['media_type'] ) { + $title = esc_html( wp_unslash( $_POST['title'] ) ); + if ( empty( $title ) ) + $title = esc_html( basename( $src ) ); + + if ( $title && $src ) + $html = "$title"; + + $type = 'file'; + if ( ( $ext = preg_replace( '/^.+?\.([^.]+)$/', '$1', $src ) ) && ( $ext_type = wp_ext2type( $ext ) ) + && ( 'audio' == $ext_type || 'video' == $ext_type ) ) + $type = $ext_type; + + /** + * Filter the URL sent to the editor for a specific media type. + * + * The dynamic portion of the hook name, `$type`, refers to the type + * of media being sent. + * + * @since 3.3.0 + * + * @param string $html HTML markup sent to the editor. + * @param string $src Media source URL. + * @param string $title Media title. + */ + $html = apply_filters( $type . '_send_to_editor_url', $html, esc_url_raw( $src ), $title ); + } else { + $align = ''; + $alt = esc_attr( wp_unslash( $_POST['alt'] ) ); + if ( isset($_POST['align']) ) { + $align = esc_attr( wp_unslash( $_POST['align'] ) ); + $class = " class='align$align'"; + } + if ( !empty($src) ) + $html = "$alt"; + + /** + * Filter the image URL sent to the editor. + * + * @since 2.8.0 + * + * @param string $html HTML markup sent to the editor for an image. + * @param string $src Image source URL. + * @param string $alt Image alternate, or alt, text. + * @param string $align The image alignment. Default 'alignnone'. Possible values include + * 'alignleft', 'aligncenter', 'alignright', 'alignnone'. + */ + $html = apply_filters( 'image_send_to_editor_url', $html, esc_url_raw( $src ), $alt, $align ); + } + + return media_send_to_editor($html); + } + + if ( isset( $_POST['save'] ) ) { + $errors['upload_notice'] = __('Saved.'); + return media_upload_gallery(); + } elseif ( ! empty( $_POST ) ) { + $return = media_upload_form_handler(); + + if ( is_string($return) ) + return $return; + if ( is_array($return) ) + $errors = $return; + } + + if ( isset($_GET['tab']) && $_GET['tab'] == 'type_url' ) { + $type = 'image'; + if ( isset( $_GET['type'] ) && in_array( $_GET['type'], array( 'video', 'audio', 'file' ) ) ) + $type = $_GET['type']; + return wp_iframe( 'media_upload_type_url_form', $type, $errors, $id ); + } + + return wp_iframe( 'media_upload_type_form', 'image', $errors, $id ); +} + +/** + * Download an image from the specified URL and attach it to a post. + * + * @since 2.6.0 + * + * @param string $file The URL of the image to download + * @param int $post_id The post ID the media is to be associated with + * @param string $desc Optional. Description of the image + * @return string|WP_Error Populated HTML img tag on success + */ +function media_sideload_image( $file, $post_id, $desc = null ) { + if ( ! empty( $file ) ) { + // Set variables for storage, fix file filename for query strings. + preg_match( '/[^\?]+\.(jpe?g|jpe|gif|png)\b/i', $file, $matches ); + $file_array = array(); + $file_array['name'] = basename( $matches[0] ); + + // Download file to temp location. + $file_array['tmp_name'] = download_url( $file ); + + // If error storing temporarily, return the error. + if ( is_wp_error( $file_array['tmp_name'] ) ) { + return $file_array['tmp_name']; + } + + // Do the validation and storage stuff. + $id = media_handle_sideload( $file_array, $post_id, $desc ); + + // If error storing permanently, unlink. + if ( is_wp_error( $id ) ) { + @unlink( $file_array['tmp_name'] ); + return $id; + } + + $src = wp_get_attachment_url( $id ); + } + + // Finally check to make sure the file has been saved, then return the HTML. + if ( ! empty( $src ) ) { + $alt = isset( $desc ) ? esc_attr( $desc ) : ''; + $html = "$alt"; + return $html; + } +} + +/** + * {@internal Missing Short Description}} + * + * @since 2.5.0 + * + * @return string|null + */ +function media_upload_gallery() { + $errors = array(); + + if ( !empty($_POST) ) { + $return = media_upload_form_handler(); + + if ( is_string($return) ) + return $return; + if ( is_array($return) ) + $errors = $return; + } + + wp_enqueue_script('admin-gallery'); + return wp_iframe( 'media_upload_gallery_form', $errors ); +} + +/** + * {@internal Missing Short Description}} + * + * @since 2.5.0 + * + * @return string|null + */ +function media_upload_library() { + $errors = array(); + if ( !empty($_POST) ) { + $return = media_upload_form_handler(); + + if ( is_string($return) ) + return $return; + if ( is_array($return) ) + $errors = $return; + } + + return wp_iframe( 'media_upload_library_form', $errors ); +} + +/** + * Retrieve HTML for the image alignment radio buttons with the specified one checked. + * + * @since 2.7.0 + * + * @param WP_Post $post + * @param string $checked + * @return string + */ +function image_align_input_fields( $post, $checked = '' ) { + + if ( empty($checked) ) + $checked = get_user_setting('align', 'none'); + + $alignments = array('none' => __('None'), 'left' => __('Left'), 'center' => __('Center'), 'right' => __('Right')); + if ( !array_key_exists( (string) $checked, $alignments ) ) + $checked = 'none'; + + $out = array(); + foreach ( $alignments as $name => $label ) { + $name = esc_attr($name); + $out[] = ""; + } + return join("\n", $out); +} + +/** + * Retrieve HTML for the size radio buttons with the specified one checked. + * + * @since 2.7.0 + * + * @param WP_Post $post + * @param bool|string $check + * @return array + */ +function image_size_input_fields( $post, $check = '' ) { + + /** + * Filter the names and labels of the default image sizes. + * + * @since 3.3.0 + * + * @param array $size_names Array of image sizes and their names. Default values + * include 'Thumbnail', 'Medium', 'Large', 'Full Size'. + */ + $size_names = apply_filters( 'image_size_names_choose', array( + 'thumbnail' => __( 'Thumbnail' ), + 'medium' => __( 'Medium' ), + 'large' => __( 'Large' ), + 'full' => __( 'Full Size' ) + ) ); + + if ( empty($check) ) + $check = get_user_setting('imgsize', 'medium'); + + foreach ( $size_names as $size => $label ) { + $downsize = image_downsize($post->ID, $size); + $checked = ''; + + // Is this size selectable? + $enabled = ( $downsize[3] || 'full' == $size ); + $css_id = "image-size-{$size}-{$post->ID}"; + + // If this size is the default but that's not available, don't select it. + if ( $size == $check ) { + if ( $enabled ) + $checked = " checked='checked'"; + else + $check = ''; + } elseif ( !$check && $enabled && 'thumbnail' != $size ) { + /* + * If $check is not enabled, default to the first available size + * that's bigger than a thumbnail. + */ + $check = $size; + $checked = " checked='checked'"; + } + + $html = "
    "; + + $html .= ""; + + // Only show the dimensions if that choice is available. + if ( $enabled ) + $html .= " "; + + $html .= '
    '; + + $out[] = $html; + } + + return array( + 'label' => __('Size'), + 'input' => 'html', + 'html' => join("\n", $out), + ); +} + +/** + * Retrieve HTML for the Link URL buttons with the default link type as specified. + * + * @since 2.7.0 + * + * @param WP_Post $post + * @param string $url_type + * @return string + */ +function image_link_input_fields($post, $url_type = '') { + + $file = wp_get_attachment_url($post->ID); + $link = get_attachment_link($post->ID); + + if ( empty($url_type) ) + $url_type = get_user_setting('urlbutton', 'post'); + + $url = ''; + if ( $url_type == 'file' ) + $url = $file; + elseif ( $url_type == 'post' ) + $url = $link; + + return " +
    + + + +"; +} + +function wp_caption_input_textarea($edit_post) { + // Post data is already escaped. + $name = "attachments[{$edit_post->ID}][post_excerpt]"; + + return ''; +} + +/** + * {@internal Missing Short Description}} + * + * @since 2.5.0 + * + * @param array $form_fields + * @param object $post + * @return array + */ +function image_attachment_fields_to_edit($form_fields, $post) { + return $form_fields; +} + +/** + * {@internal Missing Short Description}} + * + * @since 2.5.0 + * + * @param array $form_fields An array of attachment form fields. + * @param WP_Post $post The WP_Post attachment object. + * @return array Filtered attachment form fields. + */ +function media_single_attachment_fields_to_edit( $form_fields, $post ) { + unset($form_fields['url'], $form_fields['align'], $form_fields['image-size']); + return $form_fields; +} + +/** + * {@internal Missing Short Description}} + * + * @since 2.8.0 + * + * @param array $form_fields An array of attachment form fields. + * @param WP_Post $post The WP_Post attachment object. + * @return array Filtered attachment form fields. + */ +function media_post_single_attachment_fields_to_edit( $form_fields, $post ) { + unset($form_fields['image_url']); + return $form_fields; +} + +/** + * Filters input from media_upload_form_handler() and assigns a default + * post_title from the file name if none supplied. + * + * Illustrates the use of the attachment_fields_to_save filter + * which can be used to add default values to any field before saving to DB. + * + * @since 2.5.0 + * + * @param array $post The WP_Post attachment object converted to an array. + * @param array $attachment An array of attachment metadata. + * @return array Filtered attachment post object. + */ +function image_attachment_fields_to_save( $post, $attachment ) { + if ( substr( $post['post_mime_type'], 0, 5 ) == 'image' ) { + if ( strlen( trim( $post['post_title'] ) ) == 0 ) { + $attachment_url = ( isset( $post['attachment_url'] ) ) ? $post['attachment_url'] : $post['guid']; + $post['post_title'] = preg_replace( '/\.\w+$/', '', wp_basename( $attachment_url ) ); + $post['errors']['post_title']['errors'][] = __( 'Empty Title filled from filename.' ); + } + } + + return $post; +} + +add_filter( 'attachment_fields_to_save', 'image_attachment_fields_to_save', 10, 2 ); + +/** + * {@internal Missing Short Description}} + * + * @since 2.5.0 + * + * @param string $html + * @param integer $attachment_id + * @param array $attachment + * @return string + */ +function image_media_send_to_editor($html, $attachment_id, $attachment) { + $post = get_post($attachment_id); + if ( substr($post->post_mime_type, 0, 5) == 'image' ) { + $url = $attachment['url']; + $align = !empty($attachment['align']) ? $attachment['align'] : 'none'; + $size = !empty($attachment['image-size']) ? $attachment['image-size'] : 'medium'; + $alt = !empty($attachment['image_alt']) ? $attachment['image_alt'] : ''; + $rel = ( $url == get_attachment_link($attachment_id) ); + + return get_image_send_to_editor($attachment_id, $attachment['post_excerpt'], $attachment['post_title'], $align, $url, $rel, $size, $alt); + } + + return $html; +} + +add_filter('media_send_to_editor', 'image_media_send_to_editor', 10, 3); + +/** + * {@internal Missing Short Description}} + * + * @since 2.5.0 + * + * @param WP_Post $post + * @param array $errors + * @return array + */ +function get_attachment_fields_to_edit($post, $errors = null) { + if ( is_int($post) ) + $post = get_post($post); + if ( is_array($post) ) + $post = new WP_Post( (object) $post ); + + $image_url = wp_get_attachment_url($post->ID); + + $edit_post = sanitize_post($post, 'edit'); + + $form_fields = array( + 'post_title' => array( + 'label' => __('Title'), + 'value' => $edit_post->post_title + ), + 'image_alt' => array(), + 'post_excerpt' => array( + 'label' => __('Caption'), + 'input' => 'html', + 'html' => wp_caption_input_textarea($edit_post) + ), + 'post_content' => array( + 'label' => __('Description'), + 'value' => $edit_post->post_content, + 'input' => 'textarea' + ), + 'url' => array( + 'label' => __('Link URL'), + 'input' => 'html', + 'html' => image_link_input_fields($post, get_option('image_default_link_type')), + 'helps' => __('Enter a link URL or click above for presets.') + ), + 'menu_order' => array( + 'label' => __('Order'), + 'value' => $edit_post->menu_order + ), + 'image_url' => array( + 'label' => __('File URL'), + 'input' => 'html', + 'html' => "
    ", + 'value' => wp_get_attachment_url($post->ID), + 'helps' => __('Location of the uploaded file.') + ) + ); + + foreach ( get_attachment_taxonomies($post) as $taxonomy ) { + $t = (array) get_taxonomy($taxonomy); + if ( ! $t['public'] || ! $t['show_ui'] ) + continue; + if ( empty($t['label']) ) + $t['label'] = $taxonomy; + if ( empty($t['args']) ) + $t['args'] = array(); + + $terms = get_object_term_cache($post->ID, $taxonomy); + if ( false === $terms ) + $terms = wp_get_object_terms($post->ID, $taxonomy, $t['args']); + + $values = array(); + + foreach ( $terms as $term ) + $values[] = $term->slug; + $t['value'] = join(', ', $values); + + $form_fields[$taxonomy] = $t; + } + + // Merge default fields with their errors, so any key passed with the error (e.g. 'error', 'helps', 'value') will replace the default + // The recursive merge is easily traversed with array casting: foreach( (array) $things as $thing ) + $form_fields = array_merge_recursive($form_fields, (array) $errors); + + // This was formerly in image_attachment_fields_to_edit(). + if ( substr($post->post_mime_type, 0, 5) == 'image' ) { + $alt = get_post_meta($post->ID, '_wp_attachment_image_alt', true); + if ( empty($alt) ) + $alt = ''; + + $form_fields['post_title']['required'] = true; + + $form_fields['image_alt'] = array( + 'value' => $alt, + 'label' => __('Alternative Text'), + 'helps' => __('Alt text for the image, e.g. “The Mona Lisa”') + ); + + $form_fields['align'] = array( + 'label' => __('Alignment'), + 'input' => 'html', + 'html' => image_align_input_fields($post, get_option('image_default_align')), + ); + + $form_fields['image-size'] = image_size_input_fields( $post, get_option('image_default_size', 'medium') ); + + } else { + unset( $form_fields['image_alt'] ); + } + + /** + * Filter the attachment fields to edit. + * + * @since 2.5.0 + * + * @param array $form_fields An array of attachment form fields. + * @param WP_Post $post The WP_Post attachment object. + */ + $form_fields = apply_filters( 'attachment_fields_to_edit', $form_fields, $post ); + + return $form_fields; +} + +/** + * Retrieve HTML for media items of post gallery. + * + * The HTML markup retrieved will be created for the progress of SWF Upload + * component. Will also create link for showing and hiding the form to modify + * the image attachment. + * + * @since 2.5.0 + * + * @param int $post_id Optional. Post ID. + * @param array $errors Errors for attachment, if any. + * @return string + */ +function get_media_items( $post_id, $errors ) { + $attachments = array(); + if ( $post_id ) { + $post = get_post($post_id); + if ( $post && $post->post_type == 'attachment' ) + $attachments = array($post->ID => $post); + else + $attachments = get_children( array( 'post_parent' => $post_id, 'post_type' => 'attachment', 'orderby' => 'menu_order ASC, ID', 'order' => 'DESC') ); + } else { + if ( is_array($GLOBALS['wp_the_query']->posts) ) + foreach ( $GLOBALS['wp_the_query']->posts as $attachment ) + $attachments[$attachment->ID] = $attachment; + } + + $output = ''; + foreach ( (array) $attachments as $id => $attachment ) { + if ( $attachment->post_status == 'trash' ) + continue; + if ( $item = get_media_item( $id, array( 'errors' => isset($errors[$id]) ? $errors[$id] : null) ) ) + $output .= "\n
    $item\n
    "; + } + + return $output; +} + +/** + * Retrieve HTML form for modifying the image attachment. + * + * @since 2.5.0 + * + * @param int $attachment_id Attachment ID for modification. + * @param string|array $args Optional. Override defaults. + * @return string HTML form for attachment. + */ +function get_media_item( $attachment_id, $args = null ) { + global $redir_tab; + + if ( ( $attachment_id = intval( $attachment_id ) ) && $thumb_url = wp_get_attachment_image_src( $attachment_id, 'thumbnail', true ) ) + $thumb_url = $thumb_url[0]; + else + $thumb_url = false; + + $post = get_post( $attachment_id ); + $current_post_id = !empty( $_GET['post_id'] ) ? (int) $_GET['post_id'] : 0; + + $default_args = array( + 'errors' => null, + 'send' => $current_post_id ? post_type_supports( get_post_type( $current_post_id ), 'editor' ) : true, + 'delete' => true, + 'toggle' => true, + 'show_title' => true + ); + $args = wp_parse_args( $args, $default_args ); + + /** + * Filter the arguments used to retrieve an image for the edit image form. + * + * @since 3.1.0 + * + * @see get_media_item + * + * @param array $args An array of arguments. + */ + $r = apply_filters( 'get_media_item_args', $args ); + + $toggle_on = __( 'Show' ); + $toggle_off = __( 'Hide' ); + + $filename = esc_html( wp_basename( $post->guid ) ); + $title = esc_attr( $post->post_title ); + + $post_mime_types = get_post_mime_types(); + $keys = array_keys( wp_match_mime_types( array_keys( $post_mime_types ), $post->post_mime_type ) ); + $type = array_shift( $keys ); + $type_html = ""; + + $form_fields = get_attachment_fields_to_edit( $post, $r['errors'] ); + + if ( $r['toggle'] ) { + $class = empty( $r['errors'] ) ? 'startclosed' : 'startopen'; + $toggle_links = " + $toggle_on + $toggle_off"; + } else { + $class = ''; + $toggle_links = ''; + } + + $display_title = ( !empty( $title ) ) ? $title : $filename; // $title shouldn't ever be empty, but just in case + $display_title = $r['show_title'] ? "
    " . wp_html_excerpt( $display_title, 60, '…' ) . "
    " : ''; + + $gallery = ( ( isset( $_REQUEST['tab'] ) && 'gallery' == $_REQUEST['tab'] ) || ( isset( $redir_tab ) && 'gallery' == $redir_tab ) ); + $order = ''; + + foreach ( $form_fields as $key => $val ) { + if ( 'menu_order' == $key ) { + if ( $gallery ) + $order = ""; + else + $order = ""; + + unset( $form_fields['menu_order'] ); + break; + } + } + + $media_dims = ''; + $meta = wp_get_attachment_metadata( $post->ID ); + if ( isset( $meta['width'], $meta['height'] ) ) + $media_dims .= "{$meta['width']} × {$meta['height']} "; + + /** + * Filter the media metadata. + * + * @since 2.5.0 + * + * @param string $media_dims The HTML markup containing the media dimensions. + * @param WP_Post $post The WP_Post attachment object. + */ + $media_dims = apply_filters( 'media_meta', $media_dims, $post ); + + $image_edit_button = ''; + if ( wp_attachment_is_image( $post->ID ) && wp_image_editor_supports( array( 'mime_type' => $post->post_mime_type ) ) ) { + $nonce = wp_create_nonce( "image_editor-$post->ID" ); + $image_edit_button = " "; + } + + $attachment_url = get_permalink( $attachment_id ); + + $item = " + $type_html + $toggle_links + $order + $display_title + + + + + \n"; + + $item .= " + + + + \n"; + + $defaults = array( + 'input' => 'text', + 'required' => false, + 'value' => '', + 'extra_rows' => array(), + ); + + if ( $r['send'] ) { + $r['send'] = get_submit_button( __( 'Insert into Post' ), 'button', "send[$attachment_id]", false ); + } + + $delete = empty( $r['delete'] ) ? '' : $r['delete']; + if ( $delete && current_user_can( 'delete_post', $attachment_id ) ) { + if ( !EMPTY_TRASH_DAYS ) { + $delete = "" . __( 'Delete Permanently' ) . ''; + } elseif ( !MEDIA_TRASH ) { + $delete = "" . __( 'Delete' ) . " + "; + } else { + $delete = "" . __( 'Move to Trash' ) . " + "; + } + } else { + $delete = ''; + } + + $thumbnail = ''; + $calling_post_id = 0; + if ( isset( $_GET['post_id'] ) ) { + $calling_post_id = absint( $_GET['post_id'] ); + } elseif ( isset( $_POST ) && count( $_POST ) ) {// Like for async-upload where $_GET['post_id'] isn't set + $calling_post_id = $post->post_parent; + } + if ( 'image' == $type && $calling_post_id && current_theme_supports( 'post-thumbnails', get_post_type( $calling_post_id ) ) + && post_type_supports( get_post_type( $calling_post_id ), 'thumbnail' ) && get_post_thumbnail_id( $calling_post_id ) != $attachment_id ) { + $ajax_nonce = wp_create_nonce( "set_post_thumbnail-$calling_post_id" ); + $thumbnail = "" . esc_html__( "Use as featured image" ) . ""; + } + + if ( ( $r['send'] || $thumbnail || $delete ) && !isset( $form_fields['buttons'] ) ) { + $form_fields['buttons'] = array( 'tr' => "\t\t\n" ); + } + $hidden_fields = array(); + + foreach ( $form_fields as $id => $field ) { + if ( $id[0] == '_' ) + continue; + + if ( !empty( $field['tr'] ) ) { + $item .= $field['tr']; + continue; + } + + $field = array_merge( $defaults, $field ); + $name = "attachments[$attachment_id][$id]"; + + if ( $field['input'] == 'hidden' ) { + $hidden_fields[$name] = $field['value']; + continue; + } + + $required = $field['required'] ? '*' : ''; + $aria_required = $field['required'] ? " aria-required='true' " : ''; + $class = $id; + $class .= $field['required'] ? ' form-required' : ''; + + $item .= "\t\t\n\t\t\t\n\t\t\t\n\t\t\n"; + + $extra_rows = array(); + + if ( !empty( $field['errors'] ) ) + foreach ( array_unique( (array) $field['errors'] ) as $error ) + $extra_rows['error'][] = $error; + + if ( !empty( $field['extra_rows'] ) ) + foreach ( $field['extra_rows'] as $class => $rows ) + foreach ( (array) $rows as $html ) + $extra_rows[$class][] = $html; + + foreach ( $extra_rows as $class => $rows ) + foreach ( $rows as $html ) + $item .= "\t\t\n"; + } + + if ( !empty( $form_fields['_final'] ) ) + $item .= "\t\t\n"; + $item .= "\t\n"; + $item .= "\t
    +

    +

    $image_edit_button

    +
    +

    " . __('File name:') . " $filename

    +

    " . __('File type:') . " $post->post_mime_type

    +

    " . __('Upload date:') . " " . mysql2date( get_option('date_format'), $post->post_date ). '

    '; + if ( !empty( $media_dims ) ) + $item .= "

    " . __('Dimensions:') . " $media_dims

    \n"; + + $item .= "
    " . $r['send'] . " $thumbnail $delete
    "; + if ( !empty( $field[ $field['input'] ] ) ) + $item .= $field[ $field['input'] ]; + elseif ( $field['input'] == 'textarea' ) { + if ( 'post_content' == $id && user_can_richedit() ) { + // Sanitize_post() skips the post_content when user_can_richedit. + $field['value'] = htmlspecialchars( $field['value'], ENT_QUOTES ); + } + // Post_excerpt is already escaped by sanitize_post() in get_attachment_fields_to_edit(). + $item .= "'; + } else { + $item .= ""; + } + if ( !empty( $field['helps'] ) ) + $item .= "

    " . join( "

    \n

    ", array_unique( (array) $field['helps'] ) ) . '

    '; + $item .= "
    $html
    {$form_fields['_final']}
    \n"; + + foreach ( $hidden_fields as $name => $value ) + $item .= "\t\n"; + + if ( $post->post_parent < 1 && isset( $_REQUEST['post_id'] ) ) { + $parent = (int) $_REQUEST['post_id']; + $parent_name = "attachments[$attachment_id][post_parent]"; + $item .= "\t\n"; + } + + return $item; +} + +function get_compat_media_markup( $attachment_id, $args = null ) { + $post = get_post( $attachment_id ); + + $default_args = array( + 'errors' => null, + 'in_modal' => false, + ); + + $user_can_edit = current_user_can( 'edit_post', $attachment_id ); + + $args = wp_parse_args( $args, $default_args ); + + /** This filter is documented in wp-admin/includes/media.php */ + $args = apply_filters( 'get_media_item_args', $args ); + + $form_fields = array(); + + if ( $args['in_modal'] ) { + foreach ( get_attachment_taxonomies($post) as $taxonomy ) { + $t = (array) get_taxonomy($taxonomy); + if ( ! $t['public'] || ! $t['show_ui'] ) + continue; + if ( empty($t['label']) ) + $t['label'] = $taxonomy; + if ( empty($t['args']) ) + $t['args'] = array(); + + $terms = get_object_term_cache($post->ID, $taxonomy); + if ( false === $terms ) + $terms = wp_get_object_terms($post->ID, $taxonomy, $t['args']); + + $values = array(); + + foreach ( $terms as $term ) + $values[] = $term->slug; + $t['value'] = join(', ', $values); + $t['taxonomy'] = true; + + $form_fields[$taxonomy] = $t; + } + } + + // Merge default fields with their errors, so any key passed with the error (e.g. 'error', 'helps', 'value') will replace the default + // The recursive merge is easily traversed with array casting: foreach( (array) $things as $thing ) + $form_fields = array_merge_recursive($form_fields, (array) $args['errors'] ); + + /** This filter is documented in wp-admin/includes/media.php */ + $form_fields = apply_filters( 'attachment_fields_to_edit', $form_fields, $post ); + + unset( $form_fields['image-size'], $form_fields['align'], $form_fields['image_alt'], + $form_fields['post_title'], $form_fields['post_excerpt'], $form_fields['post_content'], + $form_fields['url'], $form_fields['menu_order'], $form_fields['image_url'] ); + + /** This filter is documented in wp-admin/includes/media.php */ + $media_meta = apply_filters( 'media_meta', '', $post ); + + $defaults = array( + 'input' => 'text', + 'required' => false, + 'value' => '', + 'extra_rows' => array(), + 'show_in_edit' => true, + 'show_in_modal' => true, + ); + + $hidden_fields = array(); + + $item = ''; + foreach ( $form_fields as $id => $field ) { + if ( $id[0] == '_' ) + continue; + + $name = "attachments[$attachment_id][$id]"; + $id_attr = "attachments-$attachment_id-$id"; + + if ( !empty( $field['tr'] ) ) { + $item .= $field['tr']; + continue; + } + + $field = array_merge( $defaults, $field ); + + if ( ( ! $field['show_in_edit'] && ! $args['in_modal'] ) || ( ! $field['show_in_modal'] && $args['in_modal'] ) ) + continue; + + if ( $field['input'] == 'hidden' ) { + $hidden_fields[$name] = $field['value']; + continue; + } + + $readonly = ! $user_can_edit && ! empty( $field['taxonomy'] ) ? " readonly='readonly' " : ''; + $required = $field['required'] ? '*' : ''; + $aria_required = $field['required'] ? " aria-required='true' " : ''; + $class = 'compat-field-' . $id; + $class .= $field['required'] ? ' form-required' : ''; + + $item .= "\t\t"; + $item .= "\t\t\t"; + $item .= "\n\t\t\t"; + + if ( !empty( $field[ $field['input'] ] ) ) + $item .= $field[ $field['input'] ]; + elseif ( $field['input'] == 'textarea' ) { + if ( 'post_content' == $id && user_can_richedit() ) { + // sanitize_post() skips the post_content when user_can_richedit. + $field['value'] = htmlspecialchars( $field['value'], ENT_QUOTES ); + } + $item .= "'; + } else { + $item .= ""; + } + if ( !empty( $field['helps'] ) ) + $item .= "

    " . join( "

    \n

    ", array_unique( (array) $field['helps'] ) ) . '

    '; + $item .= "\n\t\t\n"; + + $extra_rows = array(); + + if ( !empty( $field['errors'] ) ) + foreach ( array_unique( (array) $field['errors'] ) as $error ) + $extra_rows['error'][] = $error; + + if ( !empty( $field['extra_rows'] ) ) + foreach ( $field['extra_rows'] as $class => $rows ) + foreach ( (array) $rows as $html ) + $extra_rows[$class][] = $html; + + foreach ( $extra_rows as $class => $rows ) + foreach ( $rows as $html ) + $item .= "\t\t$html\n"; + } + + if ( !empty( $form_fields['_final'] ) ) + $item .= "\t\t{$form_fields['_final']}\n"; + if ( $item ) + $item = '' . $item . '
    '; + + foreach ( $hidden_fields as $hidden_field => $value ) { + $item .= '' . "\n"; + } + + if ( $item ) + $item = '' . $item; + + return array( + 'item' => $item, + 'meta' => $media_meta, + ); +} + +/** + * {@internal Missing Short Description}} + * + * @since 2.5.0 + */ +function media_upload_header() { + $post_id = isset( $_REQUEST['post_id'] ) ? intval( $_REQUEST['post_id'] ) : 0; + echo '\n"; + if ( empty( $_GET['chromeless'] ) ) { + echo '
    '; + the_media_upload_tabs(); + echo '
    '; + } +} + +/** + * {@internal Missing Short Description}} + * + * @since 2.5.0 + * + * @param array $errors + */ +function media_upload_form( $errors = null ) { + global $type, $tab, $is_IE, $is_opera; + + if ( ! _device_can_upload() ) { + echo '

    ' . sprintf( __('The web browser on your device cannot be used to upload files. You may be able to use the native app for your device instead.'), 'https://apps.wordpress.org/' ) . '

    '; + return; + } + + $upload_action_url = admin_url('async-upload.php'); + $post_id = isset($_REQUEST['post_id']) ? intval($_REQUEST['post_id']) : 0; + $_type = isset($type) ? $type : ''; + $_tab = isset($tab) ? $tab : ''; + + $max_upload_size = wp_max_upload_size(); + if ( ! $max_upload_size ) { + $max_upload_size = 0; + } +?> + +
    +
    get_error_message(); + +?>
    + $post_id, + "_wpnonce" => wp_create_nonce('media-form'), + "type" => $_type, + "tab" => $_tab, + "short" => "1", +); + +/** + * Filter the media upload post parameters. + * + * @since 3.1.0 As 'swfupload_post_params' + * @since 3.3.0 + * + * @param array $post_params An array of media upload parameters used by Plupload. + */ +$post_params = apply_filters( 'upload_post_params', $post_params ); + +$plupload_init = array( + 'runtimes' => 'html5,flash,silverlight,html4', + 'browse_button' => 'plupload-browse-button', + 'container' => 'plupload-upload-ui', + 'drop_element' => 'drag-drop-area', + 'file_data_name' => 'async-upload', + 'url' => $upload_action_url, + 'flash_swf_url' => includes_url( 'js/plupload/plupload.flash.swf' ), + 'silverlight_xap_url' => includes_url( 'js/plupload/plupload.silverlight.xap' ), + 'filters' => array( + 'max_file_size' => $max_upload_size . 'b', + ), + 'multipart_params' => $post_params, +); + +// Currently only iOS Safari supports multiple files uploading but iOS 7.x has a bug that prevents uploading of videos +// when enabled. See #29602. +if ( wp_is_mobile() && strpos( $_SERVER['HTTP_USER_AGENT'], 'OS 7_' ) !== false && + strpos( $_SERVER['HTTP_USER_AGENT'], 'like Mac OS X' ) !== false ) { + + $plupload_init['multi_selection'] = false; +} + +/** + * Filter the default Plupload settings. + * + * @since 3.3.0 + * + * @param array $plupload_init An array of default settings used by Plupload. + */ +$plupload_init = apply_filters( 'plupload_init', $plupload_init ); + +?> + + + +
    + +
    +
    +

    +

    +

    +
    +
    + +
    + +
    + +

    + + + + +

    +
    + +
    + +

    + + +
    + + + + +

    + + + + +
    '.esc_html($id->get_error_message()).'
    '; + exit; + } +} +?>
    + +

    + +

    + + + +
    + + + +

    + + + +
    +
    + +
    +
    +
    + + + +
    + + + + + + + | + | + +
    + + + +
    + + + + + + + + +
      + $reals ) + foreach ( $reals as $real ) + if ( isset($num_posts[$_type]) ) + $num_posts[$_type] += $_num_posts[$real]; + else + $num_posts[$_type] = $_num_posts[$real]; +// If available type specified by media button clicked, filter by that type +if ( empty($_GET['post_mime_type']) && !empty($num_posts[$type]) ) { + $_GET['post_mime_type'] = $type; + list($post_mime_types, $avail_post_mime_types) = wp_edit_attachments_query(); +} +if ( empty($_GET['post_mime_type']) || $_GET['post_mime_type'] == 'all' ) + $class = ' class="current"'; +else + $class = ''; +$type_links[] = '
    • ' . __('All Types') . ''; +foreach ( $post_mime_types as $mime_type => $label ) { + $class = ''; + + if ( !wp_match_mime_types($mime_type, $avail_post_mime_types) ) + continue; + + if ( isset($_GET['post_mime_type']) && wp_match_mime_types($mime_type, $_GET['post_mime_type']) ) + $class = ' class="current"'; + + $type_links[] = '
    • ' . sprintf( translate_nooped_plural( $label[2], $num_posts[$mime_type] ), '' . number_format_i18n( $num_posts[$mime_type] ) . '') . ''; +} +/** + * Filter the media upload mime type list items. + * + * Returned values should begin with an `
    • ` tag. + * + * @since 3.1.0 + * + * @param array $type_links An array of list items containing mime type link HTML. + */ +echo implode(' |
    • ', apply_filters( 'media_upload_mime_type_links', $type_links ) ) . ''; +unset($type_links); +?> +
    + +
    + + add_query_arg( 'paged', '%#%' ), + 'format' => '', + 'prev_text' => __('«'), + 'next_text' => __('»'), + 'total' => ceil($wp_query->found_posts / 10), + 'current' => $q['paged'], +)); + +if ( $page_links ) + echo "
    $page_links
    "; +?> + +
    +posts WHERE post_type = 'attachment' ORDER BY post_date DESC"; + +$arc_result = $wpdb->get_results( $arc_query ); + +$month_count = count($arc_result); +$selected_month = isset( $_GET['m'] ) ? $_GET['m'] : 0; + +if ( $month_count && !( 1 == $month_count && 0 == $arc_result[0]->mmonth ) ) { ?> + + + + + +
    + +
    +
    +
    + +
    + + + + + + +
    + + +
    +

    + + +

    +
    + + + + + + +'; + } else { + $caption = ''; + } + + $default_align = get_option('image_default_align'); + if ( empty($default_align) ) + $default_align = 'none'; + + if ( 'image' == $default_view ) { + $view = 'image-only'; + $table_class = ''; + } else { + $view = $table_class = 'not-image'; + } + + return ' +

       

    + + + + + + + + + + + + + + + + + + ' . $caption . ' + + + + + + + + + + + + + + + + + +
    + + * +
    + + * +

    ' . __('Link text, e.g. “Ransom Demands (PDF)”') . '

    + + +

    ' . __('Alt text for the image, e.g. “The Mona Lisa”') . '

    + + + + + + + + +
    + +
    + + + +

    ' . __('Enter a link URL or click above for presets.') . '

    + +
    + ' . get_submit_button( __( 'Insert into Post' ), 'button', 'insertonlybutton', false ) . ' +
    +'; + +} + +/** + * Displays the multi-file uploader message. + * + * @since 2.6.0 + */ +function media_upload_flash_bypass() { + $browser_uploader = admin_url( 'media-new.php?browser-uploader' ); + + if ( $post = get_post() ) + $browser_uploader .= '&post_id=' . intval( $post->ID ); + elseif ( ! empty( $GLOBALS['post_ID'] ) ) + $browser_uploader .= '&post_id=' . intval( $GLOBALS['post_ID'] ); + + ?> +

    + browser uploader instead.' ), $browser_uploader, '_blank' ); ?> +

    + +

    + Switch to the multi-file uploader.'); ?> +

    + '; + $end = ''; + } +?> +

    +' . sprintf( __( 'Sorry, you have used all of your storage quota of %s MB.' ), get_space_allowed() ) . '

    '; +} + +/** + * Displays the image and editor in the post editor + * + * @since 3.5.0 + */ +function edit_form_image_editor( $post ) { + $open = isset( $_GET['image-editor'] ); + if ( $open ) + require_once ABSPATH . 'wp-admin/includes/image-edit.php'; + + $thumb_url = false; + if ( $attachment_id = intval( $post->ID ) ) + $thumb_url = wp_get_attachment_image_src( $attachment_id, array( 900, 450 ), true ); + + $alt_text = get_post_meta( $post->ID, '_wp_attachment_image_alt', true ); + + $att_url = wp_get_attachment_url( $post->ID ); ?> +
    + ID ) ) : + $image_edit_button = ''; + if ( wp_image_editor_supports( array( 'mime_type' => $post->post_mime_type ) ) ) { + $nonce = wp_create_nonce( "image_editor-$post->ID" ); + $image_edit_button = " "; + } + ?> + +
    + + class="wp_attachment_image" id="media-head-"> +

    +

    +
    + class="image-editor" id="image-editor-"> + + + post_mime_type, 'audio/' ) ): + + wp_maybe_generate_attachment_metadata( $post ); + + echo wp_audio_shortcode( array( 'src' => $att_url ) ); + + elseif ( $attachment_id && 0 === strpos( $post->post_mime_type, 'video/' ) ): + + wp_maybe_generate_attachment_metadata( $post ); + + $meta = wp_get_attachment_metadata( $attachment_id ); + $w = ! empty( $meta['width'] ) ? min( $meta['width'], 640 ) : 0; + $h = ! empty( $meta['height'] ) ? $meta['height'] : 0; + if ( $h && $w < $meta['width'] ) { + $h = round( ( $meta['height'] * $w ) / $meta['width'] ); + } + + $attr = array( 'src' => $att_url ); + if ( ! empty( $w ) && ! empty( $h ) ) { + $attr['width'] = $w; + $attr['height'] = $h; + } + + $thumb_id = get_post_thumbnail_id( $attachment_id ); + if ( ! empty( $thumb_id ) ) { + $attr['poster'] = wp_get_attachment_url( $thumb_id ); + } + + echo wp_video_shortcode( $attr ); + + endif; ?> + +
    +

    +
    + +

    + + + post_mime_type, 0, 5 ) ) : ?> +

    +
    + +

    + + + 'strong,em,link,block,del,ins,img,ul,ol,li,code,close' ); + $editor_args = array( + 'textarea_name' => 'content', + 'textarea_rows' => 5, + 'media_buttons' => false, + 'tinymce' => false, + 'quicktags' => $quicktags_settings, + ); + ?> + + + post_content, 'attachment_content', $editor_args ); ?> + +
    + ID ); + echo $extras['item']; + echo '' . "\n"; +} + +/** + * Displays non-editable attachment metadata in the publish metabox + * + * @since 3.5.0 + */ +function attachment_submitbox_metadata() { + $post = get_post(); + + $filename = esc_html( wp_basename( $post->guid ) ); + + $media_dims = ''; + $meta = wp_get_attachment_metadata( $post->ID ); + if ( isset( $meta['width'], $meta['height'] ) ) + $media_dims .= "{$meta['width']} × {$meta['height']} "; + /** This filter is documented in wp-admin/includes/media.php */ + $media_dims = apply_filters( 'media_meta', $media_dims, $post ); + + $att_url = wp_get_attachment_url( $post->ID ); +?> +
    + + +
    +
    + +
    +
    + ID ), $matches ) ) { + echo esc_html( strtoupper( $matches[1] ) ); + list( $mime_type ) = explode( '/', $post->post_mime_type ); + if ( $mime_type !== 'image' && ! empty( $meta['mime_type'] ) ) { + if ( $meta['mime_type'] !== "$mime_type/" . strtolower( $matches[1] ) ) { + echo ' (' . $meta['mime_type'] . ')'; + } + } + } else { + echo strtoupper( str_replace( 'image/', '', $post->post_mime_type ) ); + } + ?> +
    + + ID ); + $file_size = false; + + if ( isset( $meta['filesize'] ) ) + $file_size = $meta['filesize']; + elseif ( file_exists( $file ) ) + $file_size = filesize( $file ); + + if ( ! empty( $file_size ) ) : ?> +
    + +
    + post_mime_type ) ) { + + /** + * Filter the audio and video metadata fields to be shown in the publish meta box. + * + * The key for each item in the array should correspond to an attachment + * metadata key, and the value should be the desired label. + * + * @since 3.7.0 + * + * @param array $fields An array of the attachment metadata keys and labels. + */ + $fields = apply_filters( 'media_submitbox_misc_sections', array( + 'length_formatted' => __( 'Length:' ), + 'bitrate' => __( 'Bitrate:' ), + ) ); + + foreach ( $fields as $key => $label ) { + if ( empty( $meta[ $key ] ) ) { + continue; + } + ?> +
    + +
    + __( 'Audio Format:' ), + 'codec' => __( 'Audio Codec:' ) + ) ); + + foreach ( $audio_fields as $key => $label ) { + if ( empty( $meta['audio'][ $key ] ) ) { + continue; + } + ?> +
    + +
    + +
    + +
    + $list ) { + if ( 'length' !== $key && ! empty( $list ) ) { + $metadata[$key] = reset( $list ); + // Fix bug in byte stream analysis. + if ( 'terms_of_use' === $key && 0 === strpos( $metadata[$key], 'yright notice.' ) ) + $metadata[$key] = 'Cop' . $metadata[$key]; + } + } + break; + } + } + + if ( ! empty( $data['id3v2']['APIC'] ) ) { + $image = reset( $data['id3v2']['APIC']); + if ( ! empty( $image['data'] ) ) { + $metadata['image'] = array( + 'data' => $image['data'], + 'mime' => $image['image_mime'], + 'width' => $image['image_width'], + 'height' => $image['image_height'] + ); + } + } elseif ( ! empty( $data['comments']['picture'] ) ) { + $image = reset( $data['comments']['picture'] ); + if ( ! empty( $image['data'] ) ) { + $metadata['image'] = array( + 'data' => $image['data'], + 'mime' => $image['image_mime'] + ); + } + } +} + +/** + * Retrieve metadata from a video file's ID3 tags + * + * @since 3.6.0 + * + * @param string $file Path to file. + * @return array|bool Returns array of metadata, if found. + */ +function wp_read_video_metadata( $file ) { + if ( ! file_exists( $file ) ) + return false; + + $metadata = array(); + + if ( ! class_exists( 'getID3' ) ) + require( ABSPATH . WPINC . '/ID3/getid3.php' ); + $id3 = new getID3(); + $data = $id3->analyze( $file ); + + if ( isset( $data['video']['lossless'] ) ) + $metadata['lossless'] = $data['video']['lossless']; + if ( ! empty( $data['video']['bitrate'] ) ) + $metadata['bitrate'] = (int) $data['video']['bitrate']; + if ( ! empty( $data['video']['bitrate_mode'] ) ) + $metadata['bitrate_mode'] = $data['video']['bitrate_mode']; + if ( ! empty( $data['filesize'] ) ) + $metadata['filesize'] = (int) $data['filesize']; + if ( ! empty( $data['mime_type'] ) ) + $metadata['mime_type'] = $data['mime_type']; + if ( ! empty( $data['playtime_seconds'] ) ) + $metadata['length'] = (int) round( $data['playtime_seconds'] ); + if ( ! empty( $data['playtime_string'] ) ) + $metadata['length_formatted'] = $data['playtime_string']; + if ( ! empty( $data['video']['resolution_x'] ) ) + $metadata['width'] = (int) $data['video']['resolution_x']; + if ( ! empty( $data['video']['resolution_y'] ) ) + $metadata['height'] = (int) $data['video']['resolution_y']; + if ( ! empty( $data['fileformat'] ) ) + $metadata['fileformat'] = $data['fileformat']; + if ( ! empty( $data['video']['dataformat'] ) ) + $metadata['dataformat'] = $data['video']['dataformat']; + if ( ! empty( $data['video']['encoder'] ) ) + $metadata['encoder'] = $data['video']['encoder']; + if ( ! empty( $data['video']['codec'] ) ) + $metadata['codec'] = $data['video']['codec']; + + if ( ! empty( $data['audio'] ) ) { + unset( $data['audio']['streams'] ); + $metadata['audio'] = $data['audio']; + } + + wp_add_id3_tag_data( $metadata, $data ); + + return $metadata; +} + +/** + * Retrieve metadata from a audio file's ID3 tags + * + * @since 3.6.0 + * + * @param string $file Path to file. + * @return array|boolean Returns array of metadata, if found. + */ +function wp_read_audio_metadata( $file ) { + if ( ! file_exists( $file ) ) + return false; + $metadata = array(); + + if ( ! class_exists( 'getID3' ) ) + require( ABSPATH . WPINC . '/ID3/getid3.php' ); + $id3 = new getID3(); + $data = $id3->analyze( $file ); + + if ( ! empty( $data['audio'] ) ) { + unset( $data['audio']['streams'] ); + $metadata = $data['audio']; + } + + if ( ! empty( $data['fileformat'] ) ) + $metadata['fileformat'] = $data['fileformat']; + if ( ! empty( $data['filesize'] ) ) + $metadata['filesize'] = (int) $data['filesize']; + if ( ! empty( $data['mime_type'] ) ) + $metadata['mime_type'] = $data['mime_type']; + if ( ! empty( $data['playtime_seconds'] ) ) + $metadata['length'] = (int) round( $data['playtime_seconds'] ); + if ( ! empty( $data['playtime_string'] ) ) + $metadata['length_formatted'] = $data['playtime_string']; + + wp_add_id3_tag_data( $metadata, $data ); + + return $metadata; +} diff --git a/wp-admin/includes/menu.php b/wp-admin/includes/menu.php new file mode 100644 index 0000000..873f737 --- /dev/null +++ b/wp-admin/includes/menu.php @@ -0,0 +1,322 @@ + $sub) { + foreach ($sub as $index => $data) { + if ( ! current_user_can($data[1]) ) { + unset($submenu[$parent][$index]); + $_wp_submenu_nopriv[$parent][$data[2]] = true; + } + } + unset($index, $data); + + if ( empty($submenu[$parent]) ) + unset($submenu[$parent]); +} +unset($sub, $parent); + +/* + * Loop over the top-level menu. + * Menus for which the original parent is not accessible due to lack of privileges + * will have the next submenu in line be assigned as the new menu parent. + */ +foreach ( $menu as $id => $data ) { + if ( empty($submenu[$data[2]]) ) + continue; + $subs = $submenu[$data[2]]; + $first_sub = array_shift($subs); + $old_parent = $data[2]; + $new_parent = $first_sub[2]; + /* + * If the first submenu is not the same as the assigned parent, + * make the first submenu the new parent. + */ + if ( $new_parent != $old_parent ) { + $_wp_real_parent_file[$old_parent] = $new_parent; + $menu[$id][2] = $new_parent; + + foreach ($submenu[$old_parent] as $index => $data) { + $submenu[$new_parent][$index] = $submenu[$old_parent][$index]; + unset($submenu[$old_parent][$index]); + } + unset($submenu[$old_parent], $index); + + if ( isset($_wp_submenu_nopriv[$old_parent]) ) + $_wp_submenu_nopriv[$new_parent] = $_wp_submenu_nopriv[$old_parent]; + } +} +unset($id, $data, $subs, $first_sub, $old_parent, $new_parent); + +if ( is_network_admin() ) { + + /** + * Fires before the administration menu loads in the Network Admin. + * + * @since 3.1.0 + * + * @param string $context Empty context. + */ + do_action( 'network_admin_menu', '' ); +} elseif ( is_user_admin() ) { + + /** + * Fires before the administration menu loads in the User Admin. + * + * @since 3.1.0 + * + * @param string $context Empty context. + */ + do_action( 'user_admin_menu', '' ); +} else { + + /** + * Fires before the administration menu loads in the admin. + * + * @since 1.5.0 + * + * @param string $context Empty context. + */ + do_action( 'admin_menu', '' ); +} + +/* + * Remove menus that have no accessible submenus and require privileges + * that the user does not have. Run re-parent loop again. + */ +foreach ( $menu as $id => $data ) { + if ( ! current_user_can($data[1]) ) + $_wp_menu_nopriv[$data[2]] = true; + + /* + * If there is only one submenu and it is has same destination as the parent, + * remove the submenu. + */ + if ( ! empty( $submenu[$data[2]] ) && 1 == count ( $submenu[$data[2]] ) ) { + $subs = $submenu[$data[2]]; + $first_sub = array_shift($subs); + if ( $data[2] == $first_sub[2] ) + unset( $submenu[$data[2]] ); + } + + // If submenu is empty... + if ( empty($submenu[$data[2]]) ) { + // And user doesn't have privs, remove menu. + if ( isset( $_wp_menu_nopriv[$data[2]] ) ) { + unset($menu[$id]); + } + } +} +unset($id, $data, $subs, $first_sub); + +// Remove any duplicated separators +$separator_found = false; +foreach ( $menu as $id => $data ) { + if ( 0 == strcmp('wp-menu-separator', $data[4] ) ) { + if (false == $separator_found) { + $separator_found = true; + } else { + unset($menu[$id]); + $separator_found = false; + } + } else { + $separator_found = false; + } +} +unset($id, $data); + +function add_cssclass($add, $class) { + $class = empty($class) ? $add : $class .= ' ' . $add; + return $class; +} + +function add_menu_classes($menu) { + + $first = $lastorder = false; + $i = 0; + $mc = count($menu); + foreach ( $menu as $order => $top ) { + $i++; + + if ( 0 == $order ) { // dashboard is always shown/single + $menu[0][4] = add_cssclass('menu-top-first', $top[4]); + $lastorder = 0; + continue; + } + + if ( 0 === strpos($top[2], 'separator') && false !== $lastorder ) { // if separator + $first = true; + $c = $menu[$lastorder][4]; + $menu[$lastorder][4] = add_cssclass('menu-top-last', $c); + continue; + } + + if ( $first ) { + $c = $menu[$order][4]; + $menu[$order][4] = add_cssclass('menu-top-first', $c); + $first = false; + } + + if ( $mc == $i ) { // last item + $c = $menu[$order][4]; + $menu[$order][4] = add_cssclass('menu-top-last', $c); + } + + $lastorder = $order; + } + + /** + * Filter administration menus array with classes added for top-level items. + * + * @since 2.7.0 + * + * @param array $menu Associative array of administration menu items. + */ + return apply_filters( 'add_menu_classes', $menu ); +} + +uksort($menu, "strnatcasecmp"); // make it all pretty + +/** + * Filter whether to enable custom ordering of the administration menu. + * + * See the 'menu_order' filter for reordering menu items. + * + * @since 2.8.0 + * + * @param bool $custom Whether custom ordering is enabled. Default false. + */ +if ( apply_filters( 'custom_menu_order', false ) ) { + $menu_order = array(); + foreach ( $menu as $menu_item ) { + $menu_order[] = $menu_item[2]; + } + unset($menu_item); + $default_menu_order = $menu_order; + + /** + * Filter the order of administration menu items. + * + * A truthy value must first be passed to the 'custom_menu_order' filter + * for this filter to work. Use the following to enable custom menu ordering: + * + * add_filter( 'custom_menu_order', '__return_true' ); + * + * @since 2.8.0 + * + * @param array $menu_order An ordered array of menu items. + */ + $menu_order = apply_filters( 'menu_order', $menu_order ); + $menu_order = array_flip($menu_order); + $default_menu_order = array_flip($default_menu_order); + + function sort_menu($a, $b) { + global $menu_order, $default_menu_order; + $a = $a[2]; + $b = $b[2]; + if ( isset($menu_order[$a]) && !isset($menu_order[$b]) ) { + return -1; + } elseif ( !isset($menu_order[$a]) && isset($menu_order[$b]) ) { + return 1; + } elseif ( isset($menu_order[$a]) && isset($menu_order[$b]) ) { + if ( $menu_order[$a] == $menu_order[$b] ) + return 0; + return ($menu_order[$a] < $menu_order[$b]) ? -1 : 1; + } else { + return ($default_menu_order[$a] <= $default_menu_order[$b]) ? -1 : 1; + } + } + + usort($menu, 'sort_menu'); + unset($menu_order, $default_menu_order); +} + +// Remove the last menu item if it is a separator. +$last_menu_key = array_keys( $menu ); +$last_menu_key = array_pop( $last_menu_key ); +if ( !empty( $menu ) && 'wp-menu-separator' == $menu[ $last_menu_key ][ 4 ] ) + unset( $menu[ $last_menu_key ] ); +unset( $last_menu_key ); + +if ( !user_can_access_admin_page() ) { + + /** + * Fires when access to an admin page is denied. + * + * @since 2.5.0 + */ + do_action( 'admin_page_access_denied' ); + + wp_die( __('You do not have sufficient permissions to access this page.') ); +} + +$menu = add_menu_classes($menu); diff --git a/wp-admin/includes/meta-boxes.php b/wp-admin/includes/meta-boxes.php new file mode 100644 index 0000000..c7307d5 --- /dev/null +++ b/wp-admin/includes/meta-boxes.php @@ -0,0 +1,1131 @@ +post_type; + $post_type_object = get_post_type_object($post_type); + $can_publish = current_user_can($post_type_object->cap->publish_posts); +?> +
    + +
    + + +
    + +
    + +
    +
    +post_status && 'future' != $post->post_status && 'pending' != $post->post_status ) { ?> +post_status ) { ?>style="display:none" type="submit" name="save" id="save-post" value="" class="button" /> +post_status && $can_publish ) { ?> + + + +
    +public ) : ?> +
    +post_status ) { + $preview_link = esc_url( get_permalink( $post->ID ) ); + $preview_button = __( 'Preview Changes' ); +} else { + $preview_link = set_url_scheme( get_permalink( $post->ID ) ); + + /** + * Filter the URI of a post preview in the post submit box. + * + * @since 2.0.5 + * @since 4.0.0 $post parameter was added. + * + * @param string $preview_link URI the user will be directed to for a post preview. + * @param WP_Post $post Post object. + */ + $preview_link = esc_url( apply_filters( 'preview_post_link', add_query_arg( 'preview', 'true', $preview_link ), $post ) ); + $preview_button = __( 'Preview' ); +} +?> + + +
    + +
    +
    + +
    + +
    + +post_status ) { + case 'private': + _e('Privately Published'); + break; + case 'publish': + _e('Published'); + break; + case 'future': + _e('Scheduled'); + break; + case 'pending': + _e('Pending Review'); + break; + case 'draft': + case 'auto-draft': + _e('Draft'); + break; +} +?> + +post_status || 'private' == $post->post_status || $can_publish ) { ?> +post_status ) { ?>style="display:none;" class="edit-post-status hide-if-no-js"> + +
    + + + + +
    + + +
    + +
    + post_status ) { + $post->post_password = ''; + $visibility = 'private'; + $visibility_trans = __('Private'); +} elseif ( !empty( $post->post_password ) ) { + $visibility = 'password'; + $visibility_trans = __('Password protected'); +} elseif ( $post_type == 'post' && is_sticky( $post->ID ) ) { + $visibility = 'public'; + $visibility_trans = __('Public, Sticky'); +} else { + $visibility = 'public'; + $visibility_trans = __('Public'); +} + +echo esc_html( $visibility_trans ); ?> + + + +
    + + +ID)); ?> /> + + + />
    + +ID ) ); ?> />
    + + />
    +
    + />
    + +

    + + +

    +
    + + +
    + +ID ) { + if ( 'future' == $post->post_status ) { // scheduled for publishing at a future date + $stamp = __('Scheduled for: %1$s'); + } else if ( 'publish' == $post->post_status || 'private' == $post->post_status ) { // already published + $stamp = __('Published on: %1$s'); + } else if ( '0000-00-00 00:00:00' == $post->post_date_gmt ) { // draft, 1 or more saves, no date specified + $stamp = __('Publish immediately'); + } else if ( time() < strtotime( $post->post_date_gmt . ' +0000' ) ) { // draft, 1 or more saves, future date specified + $stamp = __('Schedule for: %1$s'); + } else { // draft, 1 or more saves, date specified + $stamp = __('Publish on: %1$s'); + } + $date = date_i18n( $datef, strtotime( $post->post_date ) ); +} else { // draft (no saves, and thus no date specified) + $stamp = __('Publish immediately'); + $date = date_i18n( $datef, strtotime( current_time('mysql') ) ); +} + +if ( ! empty( $args['args']['revisions_count'] ) ) : + $revisions_to_keep = wp_revisions_to_keep( $post ); +?> +
    + 0 && $revisions_to_keep <= $args['args']['revisions_count'] ) { + echo ''; + printf( __( 'Revisions: %s' ), '' . number_format_i18n( $args['args']['revisions_count'] ) . '+' ); + echo ''; + } else { + printf( __( 'Revisions: %s' ), '' . number_format_i18n( $args['args']['revisions_count'] ) . '' ); + } +?> + +
    + +
    + + + +
    +
    + + + +
    +
    +
    + +
    + +
    +ID ) ) { + if ( !EMPTY_TRASH_DAYS ) + $delete_text = __('Delete Permanently'); + else + $delete_text = __('Move to Trash'); + ?> + +
    + +
    + +post_status, array('publish', 'future', 'private') ) || 0 == $post->ID ) { + if ( $can_publish ) : + if ( !empty($post->post_date_gmt) && time() < strtotime( $post->post_date_gmt . ' +0000' ) ) : ?> + + 'p' ) ); ?> + + + 'p' ) ); ?> + + + 'p' ) ); ?> + + + + +
    +
    +
    +
    + + +
    + +
    + + +
    + +
    + + +
    + %1$s'); + $date = date_i18n( $datef, strtotime( $post->post_date ) ); + ?> +
    + +
    + + +
    +
    +
    + +
    +
    + ID ) ) + if ( EMPTY_TRASH_DAYS && MEDIA_TRASH ) { + echo "" . __( 'Trash' ) . ""; + } else { + $delete_ays = ! MEDIA_TRASH ? " onclick='return showNotice.warn();'" : ''; + echo "" . __( 'Delete Permanently' ) . ""; + } + ?> +
    + +
    + + + +
    +
    +
    + +
    + +post_type, 'post-formats' ) ) : + $post_formats = get_theme_support( 'post-formats' ); + + if ( is_array( $post_formats[0] ) ) : + $post_format = get_post_format( $post->ID ); + if ( !$post_format ) + $post_format = '0'; + // Add in the current one if it isn't there yet, in case the current theme doesn't support it + if ( $post_format && !in_array( $post_format, $post_formats[0] ) ) + $post_formats[0][] = $post_format; + ?> +
    + /> + +
    /> +
    +
    + 'post_tag' ); + if ( ! isset( $box['args'] ) || ! is_array( $box['args'] ) ) { + $args = array(); + } else { + $args = $box['args']; + } + $r = wp_parse_args( $args, $defaults ); + $tax_name = esc_attr( $r['taxonomy'] ); + $taxonomy = get_taxonomy( $r['taxonomy'] ); + $user_can_assign_terms = current_user_can( $taxonomy->cap->assign_terms ); + $comma = _x( ',', 'tag delimiter' ); +?> +
    +
    +
    +

    labels->add_or_remove_items; ?>

    +
    + +
    + +
    labels->add_new_item; ?>
    +

    +

    +
    +

    labels->separate_items_with_commas; ?>

    + +
    +
    +
    + +

    labels->choose_from_most_used; ?>

    + + 'category' ); + if ( ! isset( $box['args'] ) || ! is_array( $box['args'] ) ) { + $args = array(); + } else { + $args = $box['args']; + } + $r = wp_parse_args( $args, $defaults ); + $tax_name = esc_attr( $r['taxonomy'] ); + $taxonomy = get_taxonomy( $r['taxonomy'] ); + ?> +
    + + + + +
    + "; // Allows for an empty term set to be sent. 0 is an invalid Term ID and will be ignored by empty() checks. + ?> +
      + ID, array( 'taxonomy' => $tax_name, 'popular_cats' => $popular_ids ) ); ?> +
    +
    + cap->edit_terms ) ) : ?> +
    +

    + + labels->add_new_item ); + ?> + +

    +

    + + + + $tax_name, 'hide_empty' => 0, 'name' => 'new' . $tax_name . '_parent', 'orderby' => 'name', 'hierarchical' => 1, 'show_option_none' => '— ' . $taxonomy->labels->parent_item . ' —' ) ); ?> + + + +

    +
    + +
    + + +

    Learn more about manual excerpts.'); ?>

    +to_ping) ) .'" />'; + if ('' != $post->pinged) { + $pings = '

    '. __('Already pinged:') . '

      '; + $already_pinged = explode("\n", trim($post->pinged)); + foreach ($already_pinged as $pinged_url) { + $pings .= "\n\t
    • " . esc_html($pinged_url) . "
    • "; + } + $pings .= '
    '; + } + +?> +


    ()

    +

    pingbacks, no other action necessary.'); ?>

    + +
    +
    +ID); +foreach ( $metadata as $key => $value ) { + if ( is_protected_meta( $metadata[ $key ][ 'meta_key' ], 'post' ) || ! current_user_can( 'edit_post_meta', $post->ID, $metadata[ $key ][ 'meta_key' ] ) ) + unset( $metadata[ $key ] ); +} +list_meta( $metadata ); +meta_form( $post ); ?> +
    +

    use in your theme.'); ?>

    + + +

    +
    + + +

    + +

    + $post->ID, 'number' => 1, 'count' => true ) ); + $wp_list_table = _get_list_table('WP_Post_Comments_List_Table'); + $wp_list_table->display( true ); + + if ( 1 > $total ) { + echo '

    ' . __('No comments yet.') . '

    '; + } else { + $hidden = get_hidden_meta_boxes( get_current_screen() ); + if ( ! in_array('commentsdiv', $hidden) ) { + ?> + + +

    + + + + + 'authors', + 'name' => 'post_author_override', + 'selected' => empty($post->ID) ? $user_ID : $post->post_author, + 'include_selected' => true + ) ); +} + +/** + * Display list of revisions. + * + * @since 2.6.0 + * + * @param object $post + */ +function post_revisions_meta_box( $post ) { + wp_list_post_revisions( $post ); +} + +// -- Page related Meta Boxes + +/** + * Display page attributes form fields. + * + * @since 2.7.0 + * + * @param object $post + */ +function page_attributes_meta_box($post) { + $post_type_object = get_post_type_object($post->post_type); + if ( $post_type_object->hierarchical ) { + $dropdown_args = array( + 'post_type' => $post->post_type, + 'exclude_tree' => $post->ID, + 'selected' => $post->post_parent, + 'name' => 'parent_id', + 'show_option_none' => __('(no parent)'), + 'sort_column' => 'menu_order, post_title', + 'echo' => 0, + ); + + /** + * Filter the arguments used to generate a Pages drop-down element. + * + * @since 3.3.0 + * + * @see wp_dropdown_pages() + * + * @param array $dropdown_args Array of arguments used to generate the pages drop-down. + * @param WP_Post $post The current WP_Post object. + */ + $dropdown_args = apply_filters( 'page_attributes_dropdown_pages_args', $dropdown_args, $post ); + $pages = wp_dropdown_pages( $dropdown_args ); + if ( ! empty($pages) ) { +?> +

    + + +post_type && 0 != count( get_page_templates( $post ) ) ) { + $template = !empty($post->page_template) ? $post->page_template : false; + ?> +

    + + +

    +

    +

    post_type ) _e( 'Need help? Use the Help tab in the upper right of your screen.' ); ?>

    + + + +
    +
      +
    • +
    • +
    + +
    +
      + link_id) ) + wp_link_category_checklist($link->link_id); + else + wp_link_category_checklist(); + ?> +
    +
    + + + +
    +

    + +
    +
    + +
    +

    — new window or tab.'); ?>

    +

    — current window or tab, with no frames.'); ?>

    +

    +
    +

    +link_rel ) ? $link->link_rel : ''; // In PHP 5.3: $link_rel = $link->link_rel ?: ''; + $rels = preg_split('/\s+/', $link_rel); + + if ('' != $value && in_array($value, $rels) ) { + echo ' checked="checked"'; + } + + if ('' == $value) { + if ('family' == $class && strpos($link_rel, 'child') === false && strpos($link_rel, 'parent') === false && strpos($link_rel, 'sibling') === false && strpos($link_rel, 'spouse') === false && strpos($link_rel, 'kin') === false) echo ' checked="checked"'; + if ('friendship' == $class && strpos($link_rel, 'friend') === false && strpos($link_rel, 'acquaintance') === false && strpos($link_rel, 'contact') === false) echo ' checked="checked"'; + if ('geographical' == $class && strpos($link_rel, 'co-resident') === false && strpos($link_rel, 'neighbor') === false) echo ' checked="checked"'; + if ('identity' == $class && in_array('me', $rels) ) echo ' checked="checked"'; + } +} + +/** + * Display xfn form fields. + * + * @since 2.6.0 + * + * @param object $link + */ +function link_xfn_meta_box($link) { +?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    XFN.'); ?>

    + + + + + + + + + + + + + + + + + + + +ID, '_thumbnail_id', true ); + echo _wp_post_thumbnail_html( $thumbnail_id, $post->ID ); +} + +/** + * Display fields for ID3 data + * + * @since 3.9.0 + * + * @param WP_Post $post + */ +function attachment_id3_data_meta_box( $post ) { + $meta = array(); + if ( ! empty( $post->ID ) ) { + $meta = wp_get_attachment_metadata( $post->ID ); + } + + foreach ( wp_get_attachment_id3_keys( $post, 'edit' ) as $key => $label ) : ?> +

    +
    + +

    + $markerline ) { + if (strpos($markerline, '# BEGIN ' . $marker) !== false) + $state = false; + if ( $state ) { + if ( $n + 1 < count( $markerdata ) ) + fwrite( $f, "{$markerline}\n" ); + else + fwrite( $f, "{$markerline}" ); + } + if (strpos($markerline, '# END ' . $marker) !== false) { + fwrite( $f, "# BEGIN {$marker}\n" ); + if ( is_array( $insertion )) + foreach ( $insertion as $insertline ) + fwrite( $f, "{$insertline}\n" ); + fwrite( $f, "# END {$marker}\n" ); + $state = true; + $foundit = true; + } + } + } + if (!$foundit) { + fwrite( $f, "\n# BEGIN {$marker}\n" ); + foreach ( $insertion as $insertline ) + fwrite( $f, "{$insertline}\n" ); + fwrite( $f, "# END {$marker}\n" ); + } + fclose( $f ); + return true; + } else { + return false; + } +} + +/** + * Updates the htaccess file with the current rules if it is writable. + * + * Always writes to the file if it exists and is writable to ensure that we + * blank out old rules. + * + * @since 1.5.0 + */ +function save_mod_rewrite_rules() { + if ( is_multisite() ) + return; + + global $wp_rewrite; + + $home_path = get_home_path(); + $htaccess_file = $home_path.'.htaccess'; + + /* + * If the file doesn't already exist check for write access to the directory + * and whether we have some rules. Else check for write access to the file. + */ + if ((!file_exists($htaccess_file) && is_writable($home_path) && $wp_rewrite->using_mod_rewrite_permalinks()) || is_writable($htaccess_file)) { + if ( got_mod_rewrite() ) { + $rules = explode( "\n", $wp_rewrite->mod_rewrite_rules() ); + return insert_with_markers( $htaccess_file, 'WordPress', $rules ); + } + } + + return false; +} + +/** + * Updates the IIS web.config file with the current rules if it is writable. + * If the permalinks do not require rewrite rules then the rules are deleted from the web.config file. + * + * @since 2.8.0 + * + * @return bool True if web.config was updated successfully + */ +function iis7_save_url_rewrite_rules(){ + if ( is_multisite() ) + return; + + global $wp_rewrite; + + $home_path = get_home_path(); + $web_config_file = $home_path . 'web.config'; + + // Using win_is_writable() instead of is_writable() because of a bug in Windows PHP + if ( iis7_supports_permalinks() && ( ( ! file_exists($web_config_file) && win_is_writable($home_path) && $wp_rewrite->using_mod_rewrite_permalinks() ) || win_is_writable($web_config_file) ) ) { + $rule = $wp_rewrite->iis7_url_rewrite_rules(false, '', ''); + if ( ! empty($rule) ) { + return iis7_add_rewrite_rule($web_config_file, $rule); + } else { + return iis7_delete_rewrite_rule($web_config_file); + } + } + return false; +} + +/** + * {@internal Missing Short Description}} + * + * @since 1.5.0 + * + * @param string $file + */ +function update_recently_edited( $file ) { + $oldfiles = (array ) get_option( 'recently_edited' ); + if ( $oldfiles ) { + $oldfiles = array_reverse( $oldfiles ); + $oldfiles[] = $file; + $oldfiles = array_reverse( $oldfiles ); + $oldfiles = array_unique( $oldfiles ); + if ( 5 < count( $oldfiles )) + array_pop( $oldfiles ); + } else { + $oldfiles[] = $file; + } + update_option( 'recently_edited', $oldfiles ); +} + +/** + * If siteurl, home or page_on_front changed, flush rewrite rules. + * + * @since 2.1.0 + * + * @param string $old_value + * @param string $value + */ +function update_home_siteurl( $old_value, $value ) { + if ( defined( "WP_INSTALLING" ) ) + return; + + // If home changed, write rewrite rules to new location. + flush_rewrite_rules(); +} + +add_action( 'update_option_home', 'update_home_siteurl', 10, 2 ); +add_action( 'update_option_siteurl', 'update_home_siteurl', 10, 2 ); +add_action( 'update_option_page_on_front', 'update_home_siteurl', 10, 2 ); + +/** + * Shorten an URL, to be used as link text + * + * @since 1.2.0 + * + * @param string $url + * @return string + */ +function url_shorten( $url ) { + $short_url = str_replace( array( 'http://', 'www.' ), '', $url ); + $short_url = untrailingslashit( $short_url ); + if ( strlen( $short_url ) > 35 ) + $short_url = substr( $short_url, 0, 32 ) . '…'; + return $short_url; +} + +/** + * Resets global variables based on $_GET and $_POST + * + * This function resets global variables based on the names passed + * in the $vars array to the value of $_POST[$var] or $_GET[$var] or '' + * if neither is defined. + * + * @since 2.0.0 + * + * @param array $vars An array of globals to reset. + */ +function wp_reset_vars( $vars ) { + foreach ( $vars as $var ) { + if ( empty( $_POST[ $var ] ) ) { + if ( empty( $_GET[ $var ] ) ) { + $GLOBALS[ $var ] = ''; + } else { + $GLOBALS[ $var ] = $_GET[ $var ]; + } + } else { + $GLOBALS[ $var ] = $_POST[ $var ]; + } + } +} + +/** + * {@internal Missing Short Description}} + * + * @since 2.1.0 + * + * @param string|WP_Error $message + */ +function show_message($message) { + if ( is_wp_error($message) ){ + if ( $message->get_error_data() && is_string( $message->get_error_data() ) ) + $message = $message->get_error_message() . ': ' . $message->get_error_data(); + else + $message = $message->get_error_message(); + } + echo "

    $message

    \n"; + wp_ob_end_flush_all(); + flush(); +} + +function wp_doc_link_parse( $content ) { + if ( !is_string( $content ) || empty( $content ) ) + return array(); + + if ( !function_exists('token_get_all') ) + return array(); + + $tokens = token_get_all( $content ); + $count = count( $tokens ); + $functions = array(); + $ignore_functions = array(); + for ( $t = 0; $t < $count - 2; $t++ ) { + if ( ! is_array( $tokens[ $t ] ) ) { + continue; + } + + if ( T_STRING == $tokens[ $t ][0] && ( '(' == $tokens[ $t + 1 ] || '(' == $tokens[ $t + 2 ] ) ) { + // If it's a function or class defined locally, there's not going to be any docs available + if ( ( isset( $tokens[ $t - 2 ][1] ) && in_array( $tokens[ $t - 2 ][1], array( 'function', 'class' ) ) ) || ( isset( $tokens[ $t - 2 ][0] ) && T_OBJECT_OPERATOR == $tokens[ $t - 1 ][0] ) ) { + $ignore_functions[] = $tokens[$t][1]; + } + // Add this to our stack of unique references + $functions[] = $tokens[$t][1]; + } + } + + $functions = array_unique( $functions ); + sort( $functions ); + + /** + * Filter the list of functions and classes to be ignored from the documentation lookup. + * + * @since 2.8.0 + * + * @param array $ignore_functions Functions and classes to be ignored. + */ + $ignore_functions = apply_filters( 'documentation_ignore_functions', $ignore_functions ); + + $ignore_functions = array_unique( $ignore_functions ); + + $out = array(); + foreach ( $functions as $function ) { + if ( in_array( $function, $ignore_functions ) ) + continue; + $out[] = $function; + } + + return $out; +} + +/** + * Saves option for number of rows when listing posts, pages, comments, etc. + * + * @since 2.8.0 + */ +function set_screen_options() { + + if ( isset($_POST['wp_screen_options']) && is_array($_POST['wp_screen_options']) ) { + check_admin_referer( 'screen-options-nonce', 'screenoptionnonce' ); + + if ( !$user = wp_get_current_user() ) + return; + $option = $_POST['wp_screen_options']['option']; + $value = $_POST['wp_screen_options']['value']; + + if ( $option != sanitize_key( $option ) ) + return; + + $map_option = $option; + $type = str_replace('edit_', '', $map_option); + $type = str_replace('_per_page', '', $type); + if ( in_array( $type, get_taxonomies() ) ) + $map_option = 'edit_tags_per_page'; + elseif ( in_array( $type, get_post_types() ) ) + $map_option = 'edit_per_page'; + else + $option = str_replace('-', '_', $option); + + switch ( $map_option ) { + case 'edit_per_page': + case 'users_per_page': + case 'edit_comments_per_page': + case 'upload_per_page': + case 'edit_tags_per_page': + case 'plugins_per_page': + // Network admin + case 'sites_network_per_page': + case 'users_network_per_page': + case 'site_users_network_per_page': + case 'plugins_network_per_page': + case 'themes_network_per_page': + case 'site_themes_network_per_page': + $value = (int) $value; + if ( $value < 1 || $value > 999 ) + return; + break; + default: + + /** + * Filter a screen option value before it is set. + * + * The filter can also be used to modify non-standard [items]_per_page + * settings. See the parent function for a full list of standard options. + * + * Returning false to the filter will skip saving the current option. + * + * @since 2.8.0 + * + * @see set_screen_options() + * + * @param bool|int $value Screen option value. Default false to skip. + * @param string $option The option name. + * @param int $value The number of rows to use. + */ + $value = apply_filters( 'set-screen-option', false, $option, $value ); + + if ( false === $value ) + return; + break; + } + + update_user_meta($user->ID, $option, $value); + wp_safe_redirect( remove_query_arg( array('pagenum', 'apage', 'paged'), wp_get_referer() ) ); + exit; + } +} + +/** + * Check if rewrite rule for WordPress already exists in the IIS 7+ configuration file + * + * @since 2.8.0 + * + * @return bool + * @param string $filename The file path to the configuration file + */ +function iis7_rewrite_rule_exists($filename) { + if ( ! file_exists($filename) ) + return false; + if ( ! class_exists('DOMDocument') ) + return false; + + $doc = new DOMDocument(); + if ( $doc->load($filename) === false ) + return false; + $xpath = new DOMXPath($doc); + $rules = $xpath->query('/configuration/system.webServer/rewrite/rules/rule[starts-with(@name,\'wordpress\')]'); + if ( $rules->length == 0 ) + return false; + else + return true; +} + +/** + * Delete WordPress rewrite rule from web.config file if it exists there + * + * @since 2.8.0 + * + * @param string $filename Name of the configuration file + * @return bool + */ +function iis7_delete_rewrite_rule($filename) { + // If configuration file does not exist then rules also do not exist so there is nothing to delete + if ( ! file_exists($filename) ) + return true; + + if ( ! class_exists('DOMDocument') ) + return false; + + $doc = new DOMDocument(); + $doc->preserveWhiteSpace = false; + + if ( $doc -> load($filename) === false ) + return false; + $xpath = new DOMXPath($doc); + $rules = $xpath->query('/configuration/system.webServer/rewrite/rules/rule[starts-with(@name,\'wordpress\')]'); + if ( $rules->length > 0 ) { + $child = $rules->item(0); + $parent = $child->parentNode; + $parent->removeChild($child); + $doc->formatOutput = true; + saveDomDocument($doc, $filename); + } + return true; +} + +/** + * Add WordPress rewrite rule to the IIS 7+ configuration file. + * + * @since 2.8.0 + * + * @param string $filename The file path to the configuration file + * @param string $rewrite_rule The XML fragment with URL Rewrite rule + * @return bool + */ +function iis7_add_rewrite_rule($filename, $rewrite_rule) { + if ( ! class_exists('DOMDocument') ) + return false; + + // If configuration file does not exist then we create one. + if ( ! file_exists($filename) ) { + $fp = fopen( $filename, 'w'); + fwrite($fp, ''); + fclose($fp); + } + + $doc = new DOMDocument(); + $doc->preserveWhiteSpace = false; + + if ( $doc->load($filename) === false ) + return false; + + $xpath = new DOMXPath($doc); + + // First check if the rule already exists as in that case there is no need to re-add it + $wordpress_rules = $xpath->query('/configuration/system.webServer/rewrite/rules/rule[starts-with(@name,\'wordpress\')]'); + if ( $wordpress_rules->length > 0 ) + return true; + + // Check the XPath to the rewrite rule and create XML nodes if they do not exist + $xmlnodes = $xpath->query('/configuration/system.webServer/rewrite/rules'); + if ( $xmlnodes->length > 0 ) { + $rules_node = $xmlnodes->item(0); + } else { + $rules_node = $doc->createElement('rules'); + + $xmlnodes = $xpath->query('/configuration/system.webServer/rewrite'); + if ( $xmlnodes->length > 0 ) { + $rewrite_node = $xmlnodes->item(0); + $rewrite_node->appendChild($rules_node); + } else { + $rewrite_node = $doc->createElement('rewrite'); + $rewrite_node->appendChild($rules_node); + + $xmlnodes = $xpath->query('/configuration/system.webServer'); + if ( $xmlnodes->length > 0 ) { + $system_webServer_node = $xmlnodes->item(0); + $system_webServer_node->appendChild($rewrite_node); + } else { + $system_webServer_node = $doc->createElement('system.webServer'); + $system_webServer_node->appendChild($rewrite_node); + + $xmlnodes = $xpath->query('/configuration'); + if ( $xmlnodes->length > 0 ) { + $config_node = $xmlnodes->item(0); + $config_node->appendChild($system_webServer_node); + } else { + $config_node = $doc->createElement('configuration'); + $doc->appendChild($config_node); + $config_node->appendChild($system_webServer_node); + } + } + } + } + + $rule_fragment = $doc->createDocumentFragment(); + $rule_fragment->appendXML($rewrite_rule); + $rules_node->appendChild($rule_fragment); + + $doc->encoding = "UTF-8"; + $doc->formatOutput = true; + saveDomDocument($doc, $filename); + + return true; +} + +/** + * Saves the XML document into a file + * + * @since 2.8.0 + * + * @param DOMDocument $doc + * @param string $filename + */ +function saveDomDocument($doc, $filename) { + $config = $doc->saveXML(); + $config = preg_replace("/([^\r])\n/", "$1\r\n", $config); + $fp = fopen($filename, 'w'); + fwrite($fp, $config); + fclose($fp); +} + +/** + * Display the default admin color scheme picker (Used in user-edit.php) + * + * @since 3.0.0 + */ +function admin_color_scheme_picker( $user_id ) { + global $_wp_admin_css_colors; + + ksort( $_wp_admin_css_colors ); + + if ( isset( $_wp_admin_css_colors['fresh'] ) ) { + // Set Default ('fresh') and Light should go first. + $_wp_admin_css_colors = array_filter( array_merge( array( 'fresh' => '', 'light' => '' ), $_wp_admin_css_colors ) ); + } + + $current_color = get_user_option( 'admin_color', $user_id ); + + if ( empty( $current_color ) || ! isset( $_wp_admin_css_colors[ $current_color ] ) ) { + $current_color = 'fresh'; + } + + ?> +
    + + $color_info ) : + + ?> +
    + /> + + + + + + colors as $html_color ) { + ?> + + + +
     
    +
    + +
    + icon_colors ) ) { + $icon_colors = $_wp_admin_css_colors[ $color_scheme ]->icon_colors; + } elseif ( ! empty( $_wp_admin_css_colors['fresh']->icon_colors ) ) { + $icon_colors = $_wp_admin_css_colors['fresh']->icon_colors; + } else { + // Fall back to the default set of icon colors if the default scheme is missing. + $icon_colors = array( 'base' => '#999', 'focus' => '#2ea2cc', 'current' => '#fff' ); + } + + echo '\n"; +} +add_action( 'admin_head', 'wp_color_scheme_settings' ); + +function _ipad_meta() { + if ( wp_is_mobile() ) { + ?> + + sprintf( __( '%s is currently editing' ), $user->display_name ) ); + + if ( ( $avatar = get_avatar( $user->ID, 18 ) ) && preg_match( "|src='([^']+)'|", $avatar, $matches ) ) + $send['avatar_src'] = $matches[1]; + + $checked[$key] = $send; + } + } + } + + if ( ! empty( $checked ) ) + $response['wp-check-locked-posts'] = $checked; + + return $response; +} +add_filter( 'heartbeat_received', 'wp_check_locked_posts', 10, 3 ); + +/** + * Check lock status on the New/Edit Post screen and refresh the lock + * + * @since 3.6.0 + */ +function wp_refresh_post_lock( $response, $data, $screen_id ) { + if ( array_key_exists( 'wp-refresh-post-lock', $data ) ) { + $received = $data['wp-refresh-post-lock']; + $send = array(); + + if ( ! $post_id = absint( $received['post_id'] ) ) + return $response; + + if ( ! current_user_can('edit_post', $post_id) ) + return $response; + + if ( ( $user_id = wp_check_post_lock( $post_id ) ) && ( $user = get_userdata( $user_id ) ) ) { + $error = array( + 'text' => sprintf( __( '%s has taken over and is currently editing.' ), $user->display_name ) + ); + + if ( $avatar = get_avatar( $user->ID, 64 ) ) { + if ( preg_match( "|src='([^']+)'|", $avatar, $matches ) ) + $error['avatar_src'] = $matches[1]; + } + + $send['lock_error'] = $error; + } else { + if ( $new_lock = wp_set_post_lock( $post_id ) ) + $send['new_lock'] = implode( ':', $new_lock ); + } + + $response['wp-refresh-post-lock'] = $send; + } + + return $response; +} +add_filter( 'heartbeat_received', 'wp_refresh_post_lock', 10, 3 ); + +/** + * Check nonce expiration on the New/Edit Post screen and refresh if needed + * + * @since 3.6.0 + */ +function wp_refresh_post_nonces( $response, $data, $screen_id ) { + if ( array_key_exists( 'wp-refresh-post-nonces', $data ) ) { + $received = $data['wp-refresh-post-nonces']; + $response['wp-refresh-post-nonces'] = array( 'check' => 1 ); + + if ( ! $post_id = absint( $received['post_id'] ) ) + return $response; + + if ( ! current_user_can( 'edit_post', $post_id ) || empty( $received['post_nonce'] ) ) + return $response; + + if ( 2 === wp_verify_nonce( $received['post_nonce'], 'update-post_' . $post_id ) ) { + $response['wp-refresh-post-nonces'] = array( + 'replace' => array( + 'getpermalinknonce' => wp_create_nonce('getpermalink'), + 'samplepermalinknonce' => wp_create_nonce('samplepermalink'), + 'closedpostboxesnonce' => wp_create_nonce('closedpostboxes'), + '_ajax_linking_nonce' => wp_create_nonce( 'internal-linking' ), + '_wpnonce' => wp_create_nonce( 'update-post_' . $post_id ), + ), + 'heartbeatNonce' => wp_create_nonce( 'heartbeat-nonce' ), + ); + } + } + + return $response; +} +add_filter( 'heartbeat_received', 'wp_refresh_post_nonces', 10, 3 ); + +/** + * Disable suspension of Heartbeat on the Add/Edit Post screens. + * + * @since 3.8.0 + * + * @param array $settings An array of Heartbeat settings. + * @return array Filtered Heartbeat settings. + */ +function wp_heartbeat_set_suspension( $settings ) { + global $pagenow; + + if ( 'post.php' === $pagenow || 'post-new.php' === $pagenow ) { + $settings['suspension'] = 'disable'; + } + + return $settings; +} +add_filter( 'heartbeat_settings', 'wp_heartbeat_set_suspension' ); + +/** + * Autosave with heartbeat + * + * @since 3.9.0 + */ +function heartbeat_autosave( $response, $data ) { + if ( ! empty( $data['wp_autosave'] ) ) { + $saved = wp_autosave( $data['wp_autosave'] ); + + if ( is_wp_error( $saved ) ) { + $response['wp_autosave'] = array( 'success' => false, 'message' => $saved->get_error_message() ); + } elseif ( empty( $saved ) ) { + $response['wp_autosave'] = array( 'success' => false, 'message' => __( 'Error while saving.' ) ); + } else { + /* translators: draft saved date format, see http://php.net/date */ + $draft_saved_date_format = __( 'g:i:s a' ); + /* translators: %s: date and time */ + $response['wp_autosave'] = array( 'success' => true, 'message' => sprintf( __( 'Draft saved at %s.' ), date_i18n( $draft_saved_date_format ) ) ); + } + } + + return $response; +} +// Run later as we have to set DOING_AUTOSAVE for back-compat +add_filter( 'heartbeat_received', 'heartbeat_autosave', 500, 2 ); + +/** + * Disables autocomplete on the 'post' form (Add/Edit Post screens) for WebKit browsers, + * as they disregard the autocomplete setting on the editor textarea. That can break the editor + * when the user navigates to it with the browser's Back button. See #28037 + * + * @since 4.0 + */ +function post_form_autocomplete_off() { + global $is_safari, $is_chrome; + + if ( $is_safari || $is_chrome ) { + echo ' autocomplete="off"'; + } +} + +add_action( 'post_edit_form_tag', 'post_form_autocomplete_off' ); diff --git a/wp-admin/includes/ms-deprecated.php b/wp-admin/includes/ms-deprecated.php new file mode 100644 index 0000000..44e2a82 --- /dev/null +++ b/wp-admin/includes/ms-deprecated.php @@ -0,0 +1,78 @@ + ( 1024 * get_site_option( 'fileupload_maxk', 1500 ) ) ) + $file['error'] = sprintf(__('This file is too big. Files must be less than %1$s KB in size.'), get_site_option( 'fileupload_maxk', 1500 ) ); + if ( upload_is_user_over_quota( false ) ) { + $file['error'] = __( 'You have used your space quota. Please delete files before uploading.' ); + } + if ( $file['error'] != '0' && ! isset( $_POST['html-upload'] ) && ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) ) { + wp_die( $file['error'] . ' ' . __( 'Back' ) . '' ); + } + + return $file; +} +add_filter( 'wp_handle_upload_prefilter', 'check_upload_size' ); + +/** + * Delete a blog. + * + * @since 3.0.0 + * + * @param int $blog_id Blog ID. + * @param bool $drop True if blog's table should be dropped. Default is false. + */ +function wpmu_delete_blog( $blog_id, $drop = false ) { + global $wpdb; + + $switch = false; + if ( get_current_blog_id() != $blog_id ) { + $switch = true; + switch_to_blog( $blog_id ); + } + + $blog = get_blog_details( $blog_id ); + /** + * Fires before a blog is deleted. + * + * @since MU + * + * @param int $blog_id The blog ID. + * @param bool $drop True if blog's table should be dropped. Default is false. + */ + do_action( 'delete_blog', $blog_id, $drop ); + + $users = get_users( array( 'blog_id' => $blog_id, 'fields' => 'ids' ) ); + + // Remove users from this blog. + if ( ! empty( $users ) ) { + foreach ( $users as $user_id ) { + remove_user_from_blog( $user_id, $blog_id ); + } + } + + update_blog_status( $blog_id, 'deleted', 1 ); + + $current_site = get_current_site(); + + // If a full blog object is not available, do not destroy anything. + if ( $drop && ! $blog ) { + $drop = false; + } + + // Don't destroy the initial, main, or root blog. + if ( $drop && ( 1 == $blog_id || is_main_site( $blog_id ) || ( $blog->path == $current_site->path && $blog->domain == $current_site->domain ) ) ) { + $drop = false; + } + + $upload_path = trim( get_option( 'upload_path' ) ); + + // If ms_files_rewriting is enabled and upload_path is empty, wp_upload_dir is not reliable. + if ( $drop && get_site_option( 'ms_files_rewriting' ) && empty( $upload_path ) ) { + $drop = false; + } + + if ( $drop ) { + $uploads = wp_upload_dir(); + + $tables = $wpdb->tables( 'blog' ); + /** + * Filter the tables to drop when the blog is deleted. + * + * @since MU + * + * @param array $tables The blog tables to be dropped. + * @param int $blog_id The ID of the blog to drop tables for. + */ + $drop_tables = apply_filters( 'wpmu_drop_tables', $tables, $blog_id ); + + foreach ( (array) $drop_tables as $table ) { + $wpdb->query( "DROP TABLE IF EXISTS `$table`" ); + } + + $wpdb->delete( $wpdb->blogs, array( 'blog_id' => $blog_id ) ); + + /** + * Filter the upload base directory to delete when the blog is deleted. + * + * @since MU + * + * @param string $uploads['basedir'] Uploads path without subdirectory. @see wp_upload_dir() + * @param int $blog_id The blog ID. + */ + $dir = apply_filters( 'wpmu_delete_blog_upload_dir', $uploads['basedir'], $blog_id ); + $dir = rtrim( $dir, DIRECTORY_SEPARATOR ); + $top_dir = $dir; + $stack = array($dir); + $index = 0; + + while ( $index < count( $stack ) ) { + # Get indexed directory from stack + $dir = $stack[$index]; + + $dh = @opendir( $dir ); + if ( $dh ) { + while ( ( $file = @readdir( $dh ) ) !== false ) { + if ( $file == '.' || $file == '..' ) + continue; + + if ( @is_dir( $dir . DIRECTORY_SEPARATOR . $file ) ) + $stack[] = $dir . DIRECTORY_SEPARATOR . $file; + else if ( @is_file( $dir . DIRECTORY_SEPARATOR . $file ) ) + @unlink( $dir . DIRECTORY_SEPARATOR . $file ); + } + @closedir( $dh ); + } + $index++; + } + + $stack = array_reverse( $stack ); // Last added dirs are deepest + foreach( (array) $stack as $dir ) { + if ( $dir != $top_dir) + @rmdir( $dir ); + } + + clean_blog_cache( $blog ); + } + + if ( $switch ) + restore_current_blog(); +} + +/** + * Delete a user from the network and remove from all sites. + * + * @since 3.0.0 + * + * @todo Merge with wp_delete_user() ? + * + * @param int $id The user ID. + * @return bool True if the user was deleted, otherwise false. + */ +function wpmu_delete_user( $id ) { + global $wpdb; + + $id = (int) $id; + $user = new WP_User( $id ); + + if ( !$user->exists() ) + return false; + /** + * Fires before a user is deleted from the network. + * + * @since MU + * + * @param int $id ID of the user about to be deleted from the network. + */ + do_action( 'wpmu_delete_user', $id ); + + $blogs = get_blogs_of_user( $id ); + + if ( ! empty( $blogs ) ) { + foreach ( $blogs as $blog ) { + switch_to_blog( $blog->userblog_id ); + remove_user_from_blog( $id, $blog->userblog_id ); + + $post_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_author = %d", $id ) ); + foreach ( (array) $post_ids as $post_id ) { + wp_delete_post( $post_id ); + } + + // Clean links + $link_ids = $wpdb->get_col( $wpdb->prepare( "SELECT link_id FROM $wpdb->links WHERE link_owner = %d", $id ) ); + + if ( $link_ids ) { + foreach ( $link_ids as $link_id ) + wp_delete_link( $link_id ); + } + + restore_current_blog(); + } + } + + $meta = $wpdb->get_col( $wpdb->prepare( "SELECT umeta_id FROM $wpdb->usermeta WHERE user_id = %d", $id ) ); + foreach ( $meta as $mid ) + delete_metadata_by_mid( 'user', $mid ); + + $wpdb->delete( $wpdb->users, array( 'ID' => $id ) ); + + clean_user_cache( $user ); + + /** This action is documented in wp-admin/includes/user.php */ + do_action( 'deleted_user', $id ); + + return true; +} + +/** + * Sends an email when a site administrator email address is changed. + * + * @since 3.0.0 + * + * @param string $old_value The old email address. Not currently used. + * @param string $value The new email address. + */ +function update_option_new_admin_email( $old_value, $value ) { + if ( $value == get_option( 'admin_email' ) || !is_email( $value ) ) + return; + + $hash = md5( $value. time() .mt_rand() ); + $new_admin_email = array( + 'hash' => $hash, + 'newemail' => $value + ); + update_option( 'adminhash', $new_admin_email ); + + $email_text = __( 'Dear user, + +You recently requested to have the administration email address on +your site changed. +If this is correct, please click on the following link to change it: +###ADMIN_URL### + +You can safely ignore and delete this email if you do not want to +take this action. + +This email has been sent to ###EMAIL### + +Regards, +All at ###SITENAME### +###SITEURL###' ); + + /** + * Filter the email text sent when the site admin email is changed. + * + * The following strings have a special meaning and will get replaced dynamically: + * ###ADMIN_URL### The link to click on to confirm the email change. Required otherwise this functunalty is will break. + * ###EMAIL### The new email. + * ###SITENAME### The name of the site. + * ###SITEURL### The URL to the site. + * + * @since MU + * + * @param string $email_text Text in the email. + * @param string $new_admin_email New admin email that the current administration email was changed to. + */ + $content = apply_filters( 'new_admin_email_content', $email_text, $new_admin_email ); + + $content = str_replace( '###ADMIN_URL###', esc_url( admin_url( 'options.php?adminhash='.$hash ) ), $content ); + $content = str_replace( '###EMAIL###', $value, $content ); + $content = str_replace( '###SITENAME###', get_site_option( 'site_name' ), $content ); + $content = str_replace( '###SITEURL###', network_home_url(), $content ); + + wp_mail( $value, sprintf( __( '[%s] New Admin Email Address' ), wp_specialchars_decode( get_option( 'blogname' ) ) ), $content ); +} +add_action( 'update_option_new_admin_email', 'update_option_new_admin_email', 10, 2 ); +add_action( 'add_option_new_admin_email', 'update_option_new_admin_email', 10, 2 ); + +/** + * Sends an email when an email address change is requested. + * + * @since 3.0.0 + * + * @global object $errors WP_Error object. + * @global object $wpdb WordPress database object. + */ +function send_confirmation_on_profile_email() { + global $errors, $wpdb; + $current_user = wp_get_current_user(); + if ( ! is_object($errors) ) + $errors = new WP_Error(); + + if ( $current_user->ID != $_POST['user_id'] ) + return false; + + if ( $current_user->user_email != $_POST['email'] ) { + if ( !is_email( $_POST['email'] ) ) { + $errors->add( 'user_email', __( "ERROR: The email address isn’t correct." ), array( 'form-field' => 'email' ) ); + return; + } + + if ( $wpdb->get_var( $wpdb->prepare( "SELECT user_email FROM {$wpdb->users} WHERE user_email=%s", $_POST['email'] ) ) ) { + $errors->add( 'user_email', __( "ERROR: The email address is already used." ), array( 'form-field' => 'email' ) ); + delete_option( $current_user->ID . '_new_email' ); + return; + } + + $hash = md5( $_POST['email'] . time() . mt_rand() ); + $new_user_email = array( + 'hash' => $hash, + 'newemail' => $_POST['email'] + ); + update_option( $current_user->ID . '_new_email', $new_user_email ); + + $email_text = __( 'Dear user, + +You recently requested to have the email address on your account changed. +If this is correct, please click on the following link to change it: +###ADMIN_URL### + +You can safely ignore and delete this email if you do not want to +take this action. + +This email has been sent to ###EMAIL### + +Regards, +All at ###SITENAME### +###SITEURL###' ); + + /** + * Filter the email text sent when a user changes emails. + * + * The following strings have a special meaning and will get replaced dynamically: + * ###ADMIN_URL### The link to click on to confirm the email change. Required otherwise this functunalty is will break. + * ###EMAIL### The new email. + * ###SITENAME### The name of the site. + * ###SITEURL### The URL to the site. + * + * @since MU + * + * @param string $email_text Text in the email. + * @param string $new_user_email New user email that the current user has changed to. + */ + $content = apply_filters( 'new_user_email_content', $email_text, $new_user_email ); + + $content = str_replace( '###ADMIN_URL###', esc_url( admin_url( 'profile.php?newuseremail='.$hash ) ), $content ); + $content = str_replace( '###EMAIL###', $_POST['email'], $content); + $content = str_replace( '###SITENAME###', get_site_option( 'site_name' ), $content ); + $content = str_replace( '###SITEURL###', network_home_url(), $content ); + + wp_mail( $_POST['email'], sprintf( __( '[%s] New Email Address' ), wp_specialchars_decode( get_option( 'blogname' ) ) ), $content ); + $_POST['email'] = $current_user->user_email; + } +} +add_action( 'personal_options_update', 'send_confirmation_on_profile_email' ); + +/** + * Adds an admin notice alerting the user to check for confirmation email + * after email address change. + * + * @since 3.0.0 + */ +function new_user_email_admin_notice() { + if ( strpos( $_SERVER['PHP_SELF'], 'profile.php' ) && isset( $_GET['updated'] ) && $email = get_option( get_current_user_id() . '_new_email' ) ) + echo "
    " . sprintf( __( "Your email address has not been updated yet. Please check your inbox at %s for a confirmation email." ), $email['newemail'] ) . "
    "; +} +add_action( 'admin_notices', 'new_user_email_admin_notice' ); + +/** + * Check whether a blog has used its allotted upload space. + * + * @since MU + * + * @param bool $echo Optional. If $echo is set and the quota is exceeded, a warning message is echoed. Default is true. + * @return bool True if user is over upload space quota, otherwise false. + */ +function upload_is_user_over_quota( $echo = true ) { + if ( get_site_option( 'upload_space_check_disabled' ) ) + return false; + + $space_allowed = get_space_allowed(); + if ( empty( $space_allowed ) || !is_numeric( $space_allowed ) ) + $space_allowed = 10; // Default space allowed is 10 MB + + $space_used = get_space_used(); + + if ( ( $space_allowed - $space_used ) < 0 ) { + if ( $echo ) + _e( 'Sorry, you have used your space allocation. Please delete some files to upload more files.' ); + return true; + } else { + return false; + } +} + +/** + * Displays the amount of disk space used by the current blog. Not used in core. + * + * @since MU + */ +function display_space_usage() { + $space_allowed = get_space_allowed(); + $space_used = get_space_used(); + + $percent_used = ( $space_used / $space_allowed ) * 100; + + if ( $space_allowed > 1000 ) { + $space = number_format( $space_allowed / 1024 ); + /* translators: Gigabytes */ + $space .= __( 'GB' ); + } else { + $space = number_format( $space_allowed ); + /* translators: Megabytes */ + $space .= __( 'MB' ); + } + ?> + + + + + + + update( $wpdb->users, array( sanitize_key( $pref ) => $value ), array( 'ID' => $id ) ); + + $user = new WP_User( $id ); + clean_user_cache( $user ); + + if ( $pref == 'spam' ) { + if ( $value == 1 ) { + /** + * Fires after the user is marked as a SPAM user. + * + * @since 3.0.0 + * + * @param int $id ID of the user marked as SPAM. + */ + do_action( 'make_spam_user', $id ); + } else { + /** + * Fires after the user is marked as a HAM user. Opposite of SPAM. + * + * @since 3.0.0 + * + * @param int $id ID of the user marked as HAM. + */ + do_action( 'make_ham_user', $id ); + } + } + + return $value; +} + +/** + * Cleans the user cache for a specific user. + * + * @since 3.0.0 + * + * @param int $id The user ID. + * @return bool|int The ID of the refreshed user or false if the user does not exist. + */ +function refresh_user_details( $id ) { + $id = (int) $id; + + if ( !$user = get_userdata( $id ) ) + return false; + + clean_user_cache( $user ); + + return $id; +} + +/** + * Returns the language for a language code. + * + * @since 3.0.0 + * + * @param string $code Optional. The two-letter language code. Default empty. + * @return string The language corresponding to $code if it exists. If it does not exist, + * then the first two letters of $code is returned. + */ +function format_code_lang( $code = '' ) { + $code = strtolower( substr( $code, 0, 2 ) ); + $lang_codes = array( + 'aa' => 'Afar', 'ab' => 'Abkhazian', 'af' => 'Afrikaans', 'ak' => 'Akan', 'sq' => 'Albanian', 'am' => 'Amharic', 'ar' => 'Arabic', 'an' => 'Aragonese', 'hy' => 'Armenian', 'as' => 'Assamese', 'av' => 'Avaric', 'ae' => 'Avestan', 'ay' => 'Aymara', 'az' => 'Azerbaijani', 'ba' => 'Bashkir', 'bm' => 'Bambara', 'eu' => 'Basque', 'be' => 'Belarusian', 'bn' => 'Bengali', + 'bh' => 'Bihari', 'bi' => 'Bislama', 'bs' => 'Bosnian', 'br' => 'Breton', 'bg' => 'Bulgarian', 'my' => 'Burmese', 'ca' => 'Catalan; Valencian', 'ch' => 'Chamorro', 'ce' => 'Chechen', 'zh' => 'Chinese', 'cu' => 'Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic', 'cv' => 'Chuvash', 'kw' => 'Cornish', 'co' => 'Corsican', 'cr' => 'Cree', + 'cs' => 'Czech', 'da' => 'Danish', 'dv' => 'Divehi; Dhivehi; Maldivian', 'nl' => 'Dutch; Flemish', 'dz' => 'Dzongkha', 'en' => 'English', 'eo' => 'Esperanto', 'et' => 'Estonian', 'ee' => 'Ewe', 'fo' => 'Faroese', 'fj' => 'Fijjian', 'fi' => 'Finnish', 'fr' => 'French', 'fy' => 'Western Frisian', 'ff' => 'Fulah', 'ka' => 'Georgian', 'de' => 'German', 'gd' => 'Gaelic; Scottish Gaelic', + 'ga' => 'Irish', 'gl' => 'Galician', 'gv' => 'Manx', 'el' => 'Greek, Modern', 'gn' => 'Guarani', 'gu' => 'Gujarati', 'ht' => 'Haitian; Haitian Creole', 'ha' => 'Hausa', 'he' => 'Hebrew', 'hz' => 'Herero', 'hi' => 'Hindi', 'ho' => 'Hiri Motu', 'hu' => 'Hungarian', 'ig' => 'Igbo', 'is' => 'Icelandic', 'io' => 'Ido', 'ii' => 'Sichuan Yi', 'iu' => 'Inuktitut', 'ie' => 'Interlingue', + 'ia' => 'Interlingua (International Auxiliary Language Association)', 'id' => 'Indonesian', 'ik' => 'Inupiaq', 'it' => 'Italian', 'jv' => 'Javanese', 'ja' => 'Japanese', 'kl' => 'Kalaallisut; Greenlandic', 'kn' => 'Kannada', 'ks' => 'Kashmiri', 'kr' => 'Kanuri', 'kk' => 'Kazakh', 'km' => 'Central Khmer', 'ki' => 'Kikuyu; Gikuyu', 'rw' => 'Kinyarwanda', 'ky' => 'Kirghiz; Kyrgyz', + 'kv' => 'Komi', 'kg' => 'Kongo', 'ko' => 'Korean', 'kj' => 'Kuanyama; Kwanyama', 'ku' => 'Kurdish', 'lo' => 'Lao', 'la' => 'Latin', 'lv' => 'Latvian', 'li' => 'Limburgan; Limburger; Limburgish', 'ln' => 'Lingala', 'lt' => 'Lithuanian', 'lb' => 'Luxembourgish; Letzeburgesch', 'lu' => 'Luba-Katanga', 'lg' => 'Ganda', 'mk' => 'Macedonian', 'mh' => 'Marshallese', 'ml' => 'Malayalam', + 'mi' => 'Maori', 'mr' => 'Marathi', 'ms' => 'Malay', 'mg' => 'Malagasy', 'mt' => 'Maltese', 'mo' => 'Moldavian', 'mn' => 'Mongolian', 'na' => 'Nauru', 'nv' => 'Navajo; Navaho', 'nr' => 'Ndebele, South; South Ndebele', 'nd' => 'Ndebele, North; North Ndebele', 'ng' => 'Ndonga', 'ne' => 'Nepali', 'nn' => 'Norwegian Nynorsk; Nynorsk, Norwegian', 'nb' => 'Bokmål, Norwegian, Norwegian Bokmål', + 'no' => 'Norwegian', 'ny' => 'Chichewa; Chewa; Nyanja', 'oc' => 'Occitan, Provençal', 'oj' => 'Ojibwa', 'or' => 'Oriya', 'om' => 'Oromo', 'os' => 'Ossetian; Ossetic', 'pa' => 'Panjabi; Punjabi', 'fa' => 'Persian', 'pi' => 'Pali', 'pl' => 'Polish', 'pt' => 'Portuguese', 'ps' => 'Pushto', 'qu' => 'Quechua', 'rm' => 'Romansh', 'ro' => 'Romanian', 'rn' => 'Rundi', 'ru' => 'Russian', + 'sg' => 'Sango', 'sa' => 'Sanskrit', 'sr' => 'Serbian', 'hr' => 'Croatian', 'si' => 'Sinhala; Sinhalese', 'sk' => 'Slovak', 'sl' => 'Slovenian', 'se' => 'Northern Sami', 'sm' => 'Samoan', 'sn' => 'Shona', 'sd' => 'Sindhi', 'so' => 'Somali', 'st' => 'Sotho, Southern', 'es' => 'Spanish; Castilian', 'sc' => 'Sardinian', 'ss' => 'Swati', 'su' => 'Sundanese', 'sw' => 'Swahili', + 'sv' => 'Swedish', 'ty' => 'Tahitian', 'ta' => 'Tamil', 'tt' => 'Tatar', 'te' => 'Telugu', 'tg' => 'Tajik', 'tl' => 'Tagalog', 'th' => 'Thai', 'bo' => 'Tibetan', 'ti' => 'Tigrinya', 'to' => 'Tonga (Tonga Islands)', 'tn' => 'Tswana', 'ts' => 'Tsonga', 'tk' => 'Turkmen', 'tr' => 'Turkish', 'tw' => 'Twi', 'ug' => 'Uighur; Uyghur', 'uk' => 'Ukrainian', 'ur' => 'Urdu', 'uz' => 'Uzbek', + 've' => 'Venda', 'vi' => 'Vietnamese', 'vo' => 'Volapük', 'cy' => 'Welsh','wa' => 'Walloon','wo' => 'Wolof', 'xh' => 'Xhosa', 'yi' => 'Yiddish', 'yo' => 'Yoruba', 'za' => 'Zhuang; Chuang', 'zu' => 'Zulu' ); + + /** + * Filter the language codes. + * + * @since MU + * + * @param array $lang_codes Key/value pair of language codes where key is the short version. + * @param string $code A two-letter designation of the language. + */ + $lang_codes = apply_filters( 'lang_codes', $lang_codes, $code ); + return strtr( $code, $lang_codes ); +} + +/** + * Synchronize category and post tag slugs when global terms are enabled. + * + * @since 3.0.0 + * + * @param $term The term. + * @param $taxonomy The taxonomy for $term. Should be 'category' or 'post_tag', as these are + * the only taxonomies which are processed by this function; anything else + * will be returned untouched. + * @return object|array Returns `$term`, after filtering the 'slug' field with {@see sanitize_title()} + * if $taxonomy is 'category' or 'post_tag'. + */ +function sync_category_tag_slugs( $term, $taxonomy ) { + if ( global_terms_enabled() && ( $taxonomy == 'category' || $taxonomy == 'post_tag' ) ) { + if ( is_object( $term ) ) { + $term->slug = sanitize_title( $term->name ); + } else { + $term['slug'] = sanitize_title( $term['name'] ); + } + } + return $term; +} +add_filter( 'get_term', 'sync_category_tag_slugs', 10, 2 ); + +/** + * Displays an access denied message when a user tries to view a site's dashboard they + * do not have access to. + * + * @since 3.2.0 + * @access private + */ +function _access_denied_splash() { + if ( ! is_user_logged_in() || is_network_admin() ) + return; + + $blogs = get_blogs_of_user( get_current_user_id() ); + + if ( wp_list_filter( $blogs, array( 'userblog_id' => get_current_blog_id() ) ) ) + return; + + $blog_name = get_bloginfo( 'name' ); + + if ( empty( $blogs ) ) + wp_die( sprintf( __( 'You attempted to access the "%1$s" dashboard, but you do not currently have privileges on this site. If you believe you should be able to access the "%1$s" dashboard, please contact your network administrator.' ), $blog_name ) ); + + $output = '

    ' . sprintf( __( 'You attempted to access the "%1$s" dashboard, but you do not currently have privileges on this site. If you believe you should be able to access the "%1$s" dashboard, please contact your network administrator.' ), $blog_name ) . '

    '; + $output .= '

    ' . __( 'If you reached this screen by accident and meant to visit one of your own sites, here are some shortcuts to help you find your way.' ) . '

    '; + + $output .= '

    ' . __('Your Sites') . '

    '; + $output .= ''; + + foreach ( $blogs as $blog ) { + $output .= ''; + $output .= ""; + $output .= ''; + $output .= ''; + } + + $output .= '
    {$blog->blogname}' . __( 'Visit Dashboard' ) . ' | ' . + '' . __( 'View Site' ) . '
    '; + + wp_die( $output ); +} +add_action( 'admin_page_access_denied', '_access_denied_splash', 99 ); + +/** + * Checks if the current user has permissions to import new users. + * + * @since 3.0.0 + * + * @param string $permission A permission to be checked. Currently not used. + * @return bool True if the user has proper permissions, false if they do not. + */ +function check_import_new_users( $permission ) { + if ( !is_super_admin() ) + return false; + return true; +} +add_filter( 'import_allow_create_users', 'check_import_new_users' ); +// See "import_allow_fetch_attachments" and "import_attachment_size_limit" filters too. + +/** + * Generates and displays a drop-down of available languages. + * + * @since 3.0.0 + * + * @param array $lang_files Optional. An array of the language files. Default empty array. + * @param string $current Optional. The current language code. Default empty. + */ +function mu_dropdown_languages( $lang_files = array(), $current = '' ) { + $flag = false; + $output = array(); + + foreach ( (array) $lang_files as $val ) { + $code_lang = basename( $val, '.mo' ); + + if ( $code_lang == 'en_US' ) { // American English + $flag = true; + $ae = __( 'American English' ); + $output[$ae] = ''; + } elseif ( $code_lang == 'en_GB' ) { // British English + $flag = true; + $be = __( 'British English' ); + $output[$be] = ''; + } else { + $translated = format_code_lang( $code_lang ); + $output[$translated] = ''; + } + + } + + if ( $flag === false ) // WordPress english + $output[] = '"; + + // Order by name + uksort( $output, 'strnatcasecmp' ); + + /** + * Filter the languages available in the dropdown. + * + * @since MU + * + * @param array $output HTML output of the dropdown. + * @param array $lang_files Available language files. + * @param string $current The current language code. + */ + $output = apply_filters( 'mu_dropdown_languages', $output, $lang_files, $current ); + + echo implode( "\n\t", $output ); +} + +/** + * Displays an admin notice to upgrade all sites after a core upgrade. + * + * @since 3.0.0 + * + * @global int $wp_db_version The version number of the database. + */ +function site_admin_notice() { + global $wp_db_version; + if ( !is_super_admin() ) + return false; + if ( get_site_option( 'wpmu_upgrade_site' ) != $wp_db_version ) + echo "
    " . sprintf( __( 'Thank you for Updating! Please visit the Upgrade Network page to update all your sites.' ), esc_url( network_admin_url( 'upgrade.php' ) ) ) . "
    "; +} +add_action( 'admin_notices', 'site_admin_notice' ); +add_action( 'network_admin_notices', 'site_admin_notice' ); + +/** + * Avoids a collision between a site slug and a permalink slug. + * + * In a subdirectory install this will make sure that a site and a post do not use the + * same subdirectory by checking for a site with the same name as a new post. + * + * @since 3.0.0 + * + * @param array $data An array of post data. + * @param array $postarr An array of posts. Not currently used. + * @return array The new array of post data after checking for collisions. + */ +function avoid_blog_page_permalink_collision( $data, $postarr ) { + if ( is_subdomain_install() ) + return $data; + if ( $data['post_type'] != 'page' ) + return $data; + if ( !isset( $data['post_name'] ) || $data['post_name'] == '' ) + return $data; + if ( !is_main_site() ) + return $data; + + $post_name = $data['post_name']; + $c = 0; + while( $c < 10 && get_id_from_blogname( $post_name ) ) { + $post_name .= mt_rand( 1, 10 ); + $c ++; + } + if ( $post_name != $data['post_name'] ) { + $data['post_name'] = $post_name; + } + return $data; +} +add_filter( 'wp_insert_post_data', 'avoid_blog_page_permalink_collision', 10, 2 ); + +/** + * Handles the display of choosing a user's primary site. + * + * This displays the user's primary site and allows the user to choose + * which site is primary. + * + * @since 3.0.0 + */ +function choose_primary_blog() { + ?> + + + + + + + + + + + +
    + 1 ) { + $found = false; + ?> + + userblog_id ); + } + } elseif ( count( $all_blogs ) == 1 ) { + $blog = array_shift( $all_blogs ); + echo $blog->domain; + if ( $primary_blog != $blog->userblog_id ) // Set the primary blog again if it's out of sync with blog list. + update_user_meta( get_current_user_id(), 'primary_blog', $blog->userblog_id ); + } else { + echo "N/A"; + } + ?> +
    + + +
    + user_login, $super_admins ) ) { + $super_admins[] = $user->user_login; + update_site_option( 'site_admins' , $super_admins ); + + /** + * Fires after the user is granted Super Admin privileges. + * + * @since 3.0.0 + * + * @param int $user_id ID of the user that was granted Super Admin privileges. + */ + do_action( 'granted_super_admin', $user_id ); + return true; + } + return false; +} + +/** + * Revokes Super Admin privileges. + * + * @since 3.0.0 + * + * @param int $user_id ID of the user Super Admin privileges to be revoked from. + * @return bool True on success, false on failure. This can fail when the user's email + * is the network admin email or when the `$super_admins` global is defined. + */ +function revoke_super_admin( $user_id ) { + // If global super_admins override is defined, there is nothing to do here. + if ( isset( $GLOBALS['super_admins'] ) ) { + return false; + } + + /** + * Fires before the user's Super Admin privileges are revoked. + * + * @since 3.0.0 + * + * @param int $user_id ID of the user Super Admin privileges are being revoked from. + */ + do_action( 'revoke_super_admin', $user_id ); + + // Directly fetch site_admins instead of using get_super_admins() + $super_admins = get_site_option( 'site_admins', array( 'admin' ) ); + + $user = get_userdata( $user_id ); + if ( $user && 0 !== strcasecmp( $user->user_email, get_site_option( 'admin_email' ) ) ) { + if ( false !== ( $key = array_search( $user->user_login, $super_admins ) ) ) { + unset( $super_admins[$key] ); + update_site_option( 'site_admins', $super_admins ); + + /** + * Fires after the user's Super Admin privileges are revoked. + * + * @since 3.0.0 + * + * @param int $user_id ID of the user Super Admin privileges were revoked from. + */ + do_action( 'revoked_super_admin', $user_id ); + return true; + } + } + return false; +} + +/** + * Whether or not we can edit this network from this page. + * + * By default editing of network is restricted to the Network Admin for that `$site_id` + * this allows for this to be overridden. + * + * @since 3.1.0 + * + * @param int $site_id The network/site ID to check. + * @return bool True if network can be edited, otherwise false. + */ +function can_edit_network( $site_id ) { + global $wpdb; + + if ( $site_id == $wpdb->siteid ) + $result = true; + else + $result = false; + + /** + * Filter whether this network can be edited from this page. + * + * @since 3.1.0 + * + * @param bool $result Whether the network can be edited from this page. + * @param int $site_id The network/site ID to check. + */ + return apply_filters( 'can_edit_network', $result, $site_id ); +} + +/** + * Thickbox image paths for Network Admin. + * + * @since 3.1.0 + * + * @access private + */ +function _thickbox_path_admin_subfolder() { +?> + + $_wp_nav_menu_max_depth ? $depth : $_wp_nav_menu_max_depth; + + ob_start(); + $item_id = esc_attr( $item->ID ); + $removed_args = array( + 'action', + 'customlink-tab', + 'edit-menu-item', + 'menu-item', + 'page-tab', + '_wpnonce', + ); + + $original_title = ''; + if ( 'taxonomy' == $item->type ) { + $original_title = get_term_field( 'name', $item->object_id, $item->object, 'raw' ); + if ( is_wp_error( $original_title ) ) + $original_title = false; + } elseif ( 'post_type' == $item->type ) { + $original_object = get_post( $item->object_id ); + $original_title = get_the_title( $original_object->ID ); + } + + $classes = array( + 'menu-item menu-item-depth-' . $depth, + 'menu-item-' . esc_attr( $item->object ), + 'menu-item-edit-' . ( ( isset( $_GET['edit-menu-item'] ) && $item_id == $_GET['edit-menu-item'] ) ? 'active' : 'inactive'), + ); + + $title = $item->title; + + if ( ! empty( $item->_invalid ) ) { + $classes[] = 'menu-item-invalid'; + /* translators: %s: title of menu item which is invalid */ + $title = sprintf( __( '%s (Invalid)' ), $item->title ); + } elseif ( isset( $item->post_status ) && 'draft' == $item->post_status ) { + $classes[] = 'pending'; + /* translators: %s: title of menu item in draft status */ + $title = sprintf( __('%s (Pending)'), $item->title ); + } + + $title = ( ! isset( $item->label ) || '' == $item->label ) ? $title : $item->label; + + $submenu_text = ''; + if ( 0 == $depth ) + $submenu_text = 'style="display: none;"'; + + ?> +
  • '; + $output .= ''; + + // Menu item hidden fields + $output .= ''; + $output .= ''; + $output .= ''; + $output .= ''; + $output .= ''; + $output .= ''; + $output .= ''; + $output .= ''; + $output .= ''; + $output .= ''; + } + +} // Walker_Nav_Menu_Checklist + +/** + * Prints the appropriate response to a menu quick search. + * + * @since 3.0.0 + * + * @param array $request The unsanitized request values. + */ +function _wp_ajax_menu_quick_search( $request = array() ) { + $args = array(); + $type = isset( $request['type'] ) ? $request['type'] : ''; + $object_type = isset( $request['object_type'] ) ? $request['object_type'] : ''; + $query = isset( $request['q'] ) ? $request['q'] : ''; + $response_format = isset( $request['response-format'] ) && in_array( $request['response-format'], array( 'json', 'markup' ) ) ? $request['response-format'] : 'json'; + + if ( 'markup' == $response_format ) { + $args['walker'] = new Walker_Nav_Menu_Checklist; + } + + if ( 'get-post-item' == $type ) { + if ( post_type_exists( $object_type ) ) { + if ( isset( $request['ID'] ) ) { + $object_id = (int) $request['ID']; + if ( 'markup' == $response_format ) { + echo walk_nav_menu_tree( array_map('wp_setup_nav_menu_item', array( get_post( $object_id ) ) ), 0, (object) $args ); + } elseif ( 'json' == $response_format ) { + echo wp_json_encode( + array( + 'ID' => $object_id, + 'post_title' => get_the_title( $object_id ), + 'post_type' => get_post_type( $object_id ), + ) + ); + echo "\n"; + } + } + } elseif ( taxonomy_exists( $object_type ) ) { + if ( isset( $request['ID'] ) ) { + $object_id = (int) $request['ID']; + if ( 'markup' == $response_format ) { + echo walk_nav_menu_tree( array_map('wp_setup_nav_menu_item', array( get_term( $object_id, $object_type ) ) ), 0, (object) $args ); + } elseif ( 'json' == $response_format ) { + $post_obj = get_term( $object_id, $object_type ); + echo wp_json_encode( + array( + 'ID' => $object_id, + 'post_title' => $post_obj->name, + 'post_type' => $object_type, + ) + ); + echo "\n"; + } + } + + } + + } elseif ( preg_match('/quick-search-(posttype|taxonomy)-([a-zA-Z_-]*\b)/', $type, $matches) ) { + if ( 'posttype' == $matches[1] && get_post_type_object( $matches[2] ) ) { + query_posts(array( + 'posts_per_page' => 10, + 'post_type' => $matches[2], + 's' => $query, + )); + if ( ! have_posts() ) + return; + while ( have_posts() ) { + the_post(); + if ( 'markup' == $response_format ) { + $var_by_ref = get_the_ID(); + echo walk_nav_menu_tree( array_map('wp_setup_nav_menu_item', array( get_post( $var_by_ref ) ) ), 0, (object) $args ); + } elseif ( 'json' == $response_format ) { + echo wp_json_encode( + array( + 'ID' => get_the_ID(), + 'post_title' => get_the_title(), + 'post_type' => get_post_type(), + ) + ); + echo "\n"; + } + } + } elseif ( 'taxonomy' == $matches[1] ) { + $terms = get_terms( $matches[2], array( + 'name__like' => $query, + 'number' => 10, + )); + if ( empty( $terms ) || is_wp_error( $terms ) ) + return; + foreach( (array) $terms as $term ) { + if ( 'markup' == $response_format ) { + echo walk_nav_menu_tree( array_map('wp_setup_nav_menu_item', array( $term ) ), 0, (object) $args ); + } elseif ( 'json' == $response_format ) { + echo wp_json_encode( + array( + 'ID' => $term->term_id, + 'post_title' => $term->name, + 'post_type' => $matches[2], + ) + ); + echo "\n"; + } + } + } + } +} + +/** + * Register nav menu metaboxes and advanced menu items + * + * @since 3.0.0 + **/ +function wp_nav_menu_setup() { + // Register meta boxes + wp_nav_menu_post_type_meta_boxes(); + add_meta_box( 'add-custom-links', __( 'Links' ), 'wp_nav_menu_item_link_meta_box', 'nav-menus', 'side', 'default' ); + wp_nav_menu_taxonomy_meta_boxes(); + + // Register advanced menu items (columns) + add_filter( 'manage_nav-menus_columns', 'wp_nav_menu_manage_columns' ); + + // If first time editing, disable advanced items by default. + if( false === get_user_option( 'managenav-menuscolumnshidden' ) ) { + $user = wp_get_current_user(); + update_user_option($user->ID, 'managenav-menuscolumnshidden', + array( 0 => 'link-target', 1 => 'css-classes', 2 => 'xfn', 3 => 'description', ), + true); + } +} + +/** + * Limit the amount of meta boxes to just links, pages and cats for first time users. + * + * @since 3.0.0 + **/ +function wp_initial_nav_menu_meta_boxes() { + global $wp_meta_boxes; + + if ( get_user_option( 'metaboxhidden_nav-menus' ) !== false || ! is_array($wp_meta_boxes) ) + return; + + $initial_meta_boxes = array( 'nav-menu-theme-locations', 'add-page', 'add-custom-links', 'add-category' ); + $hidden_meta_boxes = array(); + + foreach ( array_keys($wp_meta_boxes['nav-menus']) as $context ) { + foreach ( array_keys($wp_meta_boxes['nav-menus'][$context]) as $priority ) { + foreach ( $wp_meta_boxes['nav-menus'][$context][$priority] as $box ) { + if ( in_array( $box['id'], $initial_meta_boxes ) ) { + unset( $box['id'] ); + } else { + $hidden_meta_boxes[] = $box['id']; + } + } + } + } + + $user = wp_get_current_user(); + update_user_option( $user->ID, 'metaboxhidden_nav-menus', $hidden_meta_boxes, true ); +} + +/** + * Creates metaboxes for any post type menu item. + * + * @since 3.0.0 + */ +function wp_nav_menu_post_type_meta_boxes() { + $post_types = get_post_types( array( 'show_in_nav_menus' => true ), 'object' ); + + if ( ! $post_types ) + return; + + foreach ( $post_types as $post_type ) { + /** + * Filter whether a menu items meta box will be added for the current + * object type. + * + * If a falsey value is returned instead of an object, the menu items + * meta box for the current meta box object will not be added. + * + * @since 3.0.0 + * + * @param object $meta_box_object The current object to add a menu items + * meta box for. + */ + $post_type = apply_filters( 'nav_menu_meta_box_object', $post_type ); + if ( $post_type ) { + $id = $post_type->name; + // Give pages a higher priority. + $priority = ( 'page' == $post_type->name ? 'core' : 'default' ); + add_meta_box( "add-{$id}", $post_type->labels->name, 'wp_nav_menu_item_post_type_meta_box', 'nav-menus', 'side', $priority, $post_type ); + } + } +} + +/** + * Creates metaboxes for any taxonomy menu item. + * + * @since 3.0.0 + */ +function wp_nav_menu_taxonomy_meta_boxes() { + $taxonomies = get_taxonomies( array( 'show_in_nav_menus' => true ), 'object' ); + + if ( !$taxonomies ) + return; + + foreach ( $taxonomies as $tax ) { + /** This filter is documented in wp-admin/includes/nav-menu.php */ + $tax = apply_filters( 'nav_menu_meta_box_object', $tax ); + if ( $tax ) { + $id = $tax->name; + add_meta_box( "add-{$id}", $tax->labels->name, 'wp_nav_menu_item_taxonomy_meta_box', 'nav-menus', 'side', 'default', $tax ); + } + } +} + +/** + * Check whether to disable the Menu Locations meta box submit button + * + * @since 3.6.0 + * + * @uses global $one_theme_location_no_menus to determine if no menus exist + * + * @param int|string $nav_menu_selected_id (id, name or slug) of the currently-selected menu + * @return string Disabled attribute if at least one menu exists, false if not +*/ +function wp_nav_menu_disabled_check( $nav_menu_selected_id ) { + global $one_theme_location_no_menus; + + if ( $one_theme_location_no_menus ) + return false; + + return disabled( $nav_menu_selected_id, 0 ); +} + +/** + * Displays a metabox for the custom links menu item. + * + * @since 3.0.0 + */ +function wp_nav_menu_item_link_meta_box() { + global $_nav_menu_placeholder, $nav_menu_selected_id; + + $_nav_menu_placeholder = 0 > $_nav_menu_placeholder ? $_nav_menu_placeholder - 1 : -1; + + ?> +
    + + + + + +

    + + class="button-secondary submit-add-to-menu right" value="" name="add-custom-menu-item" id="submit-customlinkdiv" /> + + +

    + +
    + name; + + // Paginate browsing for large numbers of post objects. + $per_page = 50; + $pagenum = isset( $_REQUEST[$post_type_name . '-tab'] ) && isset( $_REQUEST['paged'] ) ? absint( $_REQUEST['paged'] ) : 1; + $offset = 0 < $pagenum ? $per_page * ( $pagenum - 1 ) : 0; + + $args = array( + 'offset' => $offset, + 'order' => 'ASC', + 'orderby' => 'title', + 'posts_per_page' => $per_page, + 'post_type' => $post_type_name, + 'suppress_filters' => true, + 'update_post_term_cache' => false, + 'update_post_meta_cache' => false + ); + + if ( isset( $post_type['args']->_default_query ) ) + $args = array_merge($args, (array) $post_type['args']->_default_query ); + + // @todo transient caching of these results with proper invalidation on updating of a post of this type + $get_posts = new WP_Query; + $posts = $get_posts->query( $args ); + if ( ! $get_posts->post_count ) { + echo '

    ' . __( 'No items.' ) . '

    '; + return; + } + + $num_pages = $get_posts->max_num_pages; + + $page_links = paginate_links( array( + 'base' => add_query_arg( + array( + $post_type_name . '-tab' => 'all', + 'paged' => '%#%', + 'item-type' => 'post_type', + 'item-object' => $post_type_name, + ) + ), + 'format' => '', + 'prev_text' => __('«'), + 'next_text' => __('»'), + 'total' => $num_pages, + 'current' => $pagenum + )); + + $db_fields = false; + if ( is_post_type_hierarchical( $post_type_name ) ) { + $db_fields = array( 'parent' => 'post_parent', 'id' => 'ID' ); + } + + $walker = new Walker_Nav_Menu_Checklist( $db_fields ); + + $current_tab = 'most-recent'; + if ( isset( $_REQUEST[$post_type_name . '-tab'] ) && in_array( $_REQUEST[$post_type_name . '-tab'], array('all', 'search') ) ) { + $current_tab = $_REQUEST[$post_type_name . '-tab']; + } + + if ( ! empty( $_REQUEST['quick-search-posttype-' . $post_type_name] ) ) { + $current_tab = 'search'; + } + + $removed_args = array( + 'action', + 'customlink-tab', + 'edit-menu-item', + 'menu-item', + 'page-tab', + '_wpnonce', + ); + + ?> +
    + + +
    +
      + 'post_date', 'order' => 'DESC', 'posts_per_page' => 15 ) ); + $most_recent = $get_posts->query( $recent_args ); + $args['walker'] = $walker; + echo walk_nav_menu_tree( array_map('wp_setup_nav_menu_item', $most_recent), 0, (object) $args ); + ?> +
    +
    + + + +
    + + + +
      + front_or_home = true; + array_unshift( $posts, $front_page_obj ); + } else { + $_nav_menu_placeholder = ( 0 > $_nav_menu_placeholder ) ? intval($_nav_menu_placeholder) - 1 : -1; + array_unshift( $posts, (object) array( + 'front_or_home' => true, + 'ID' => 0, + 'object_id' => $_nav_menu_placeholder, + 'post_content' => '', + 'post_excerpt' => '', + 'post_parent' => '', + 'post_title' => _x('Home', 'nav menu home label'), + 'post_type' => 'nav_menu_item', + 'type' => 'custom', + 'url' => home_url('/'), + ) ); + } + } + + /** + * Filter the posts displayed in the 'View All' tab of the current + * post type's menu items meta box. + * + * The dynamic portion of the hook name, `$post_type_name`, refers + * to the slug of the current post type. + * + * @since 3.2.0 + * + * @see WP_Query::query() + * + * @param array $posts The posts for the current post type. + * @param array $args An array of WP_Query arguments. + * @param object $post_type The current post type object for this menu item meta box. + */ + $posts = apply_filters( "nav_menu_items_{$post_type_name}", $posts, $args, $post_type ); + $checkbox_items = walk_nav_menu_tree( array_map('wp_setup_nav_menu_item', $posts), 0, (object) $args ); + + if ( 'all' == $current_tab && ! empty( $_REQUEST['selectall'] ) ) { + $checkbox_items = preg_replace('/(type=(.)checkbox(\2))/', '$1 checked=$2checked$2', $checkbox_items); + + } + + echo $checkbox_items; + ?> +
    + + + +
    + +

    + + + + + + class="button-secondary submit-add-to-menu right" value="" name="add-post-type-menu-item" id="" /> + + +

    + +
    + name; + + // Paginate browsing for large numbers of objects. + $per_page = 50; + $pagenum = isset( $_REQUEST[$taxonomy_name . '-tab'] ) && isset( $_REQUEST['paged'] ) ? absint( $_REQUEST['paged'] ) : 1; + $offset = 0 < $pagenum ? $per_page * ( $pagenum - 1 ) : 0; + + $args = array( + 'child_of' => 0, + 'exclude' => '', + 'hide_empty' => false, + 'hierarchical' => 1, + 'include' => '', + 'number' => $per_page, + 'offset' => $offset, + 'order' => 'ASC', + 'orderby' => 'name', + 'pad_counts' => false, + ); + + $terms = get_terms( $taxonomy_name, $args ); + + if ( ! $terms || is_wp_error($terms) ) { + echo '

    ' . __( 'No items.' ) . '

    '; + return; + } + + $num_pages = ceil( wp_count_terms( $taxonomy_name , array_merge( $args, array('number' => '', 'offset' => '') ) ) / $per_page ); + + $page_links = paginate_links( array( + 'base' => add_query_arg( + array( + $taxonomy_name . '-tab' => 'all', + 'paged' => '%#%', + 'item-type' => 'taxonomy', + 'item-object' => $taxonomy_name, + ) + ), + 'format' => '', + 'prev_text' => __('«'), + 'next_text' => __('»'), + 'total' => $num_pages, + 'current' => $pagenum + )); + + $db_fields = false; + if ( is_taxonomy_hierarchical( $taxonomy_name ) ) { + $db_fields = array( 'parent' => 'parent', 'id' => 'term_id' ); + } + + $walker = new Walker_Nav_Menu_Checklist( $db_fields ); + + $current_tab = 'most-used'; + if ( isset( $_REQUEST[$taxonomy_name . '-tab'] ) && in_array( $_REQUEST[$taxonomy_name . '-tab'], array('all', 'most-used', 'search') ) ) { + $current_tab = $_REQUEST[$taxonomy_name . '-tab']; + } + + if ( ! empty( $_REQUEST['quick-search-taxonomy-' . $taxonomy_name] ) ) { + $current_tab = 'search'; + } + + $removed_args = array( + 'action', + 'customlink-tab', + 'edit-menu-item', + 'menu-item', + 'page-tab', + '_wpnonce', + ); + + ?> +
    + + +
    +
      + 'count', 'order' => 'DESC', 'number' => 10, 'hierarchical' => false ) ); + $args['walker'] = $walker; + echo walk_nav_menu_tree( array_map('wp_setup_nav_menu_item', $popular_terms), 0, (object) $args ); + ?> +
    +
    + +
    + + + +
      + +
    + + + +
    + +
    + $searched, 'fields' => 'all', 'orderby' => 'count', 'order' => 'DESC', 'hierarchical' => false ) ); + } else { + $searched = ''; + $search_results = array(); + } + ?> +

    + + + 'submit-quick-search-taxonomy-' . $taxonomy_name ) ); ?> +

    + +
      + + + +
    • get_error_message(); ?>
    • + +
    • + +
    +
    + +

    + + + + + + class="button-secondary submit-add-to-menu right" value="" name="add-taxonomy-menu-item" id="" /> + + +

    + +
    + $_item_object_data ) { + if ( + // Checkbox is not checked. + empty( $_item_object_data['menu-item-object-id'] ) && + ( + // And item type either isn't set. + ! isset( $_item_object_data['menu-item-type'] ) || + // Or URL is the default. + in_array( $_item_object_data['menu-item-url'], array( 'http://', '' ) ) || + ! ( 'custom' == $_item_object_data['menu-item-type'] && ! isset( $_item_object_data['menu-item-db-id'] ) ) || // or it's not a custom menu item (but not the custom home page) + // Or it *is* a custom menu item that already exists. + ! empty( $_item_object_data['menu-item-db-id'] ) + ) + ) { + // Then this potential menu item is not getting added to this menu. + continue; + } + + // If this possible menu item doesn't actually have a menu database ID yet. + if ( + empty( $_item_object_data['menu-item-db-id'] ) || + ( 0 > $_possible_db_id ) || + $_possible_db_id != $_item_object_data['menu-item-db-id'] + ) { + $_actual_db_id = 0; + } else { + $_actual_db_id = (int) $_item_object_data['menu-item-db-id']; + } + + $args = array( + 'menu-item-db-id' => ( isset( $_item_object_data['menu-item-db-id'] ) ? $_item_object_data['menu-item-db-id'] : '' ), + 'menu-item-object-id' => ( isset( $_item_object_data['menu-item-object-id'] ) ? $_item_object_data['menu-item-object-id'] : '' ), + 'menu-item-object' => ( isset( $_item_object_data['menu-item-object'] ) ? $_item_object_data['menu-item-object'] : '' ), + 'menu-item-parent-id' => ( isset( $_item_object_data['menu-item-parent-id'] ) ? $_item_object_data['menu-item-parent-id'] : '' ), + 'menu-item-position' => ( isset( $_item_object_data['menu-item-position'] ) ? $_item_object_data['menu-item-position'] : '' ), + 'menu-item-type' => ( isset( $_item_object_data['menu-item-type'] ) ? $_item_object_data['menu-item-type'] : '' ), + 'menu-item-title' => ( isset( $_item_object_data['menu-item-title'] ) ? $_item_object_data['menu-item-title'] : '' ), + 'menu-item-url' => ( isset( $_item_object_data['menu-item-url'] ) ? $_item_object_data['menu-item-url'] : '' ), + 'menu-item-description' => ( isset( $_item_object_data['menu-item-description'] ) ? $_item_object_data['menu-item-description'] : '' ), + 'menu-item-attr-title' => ( isset( $_item_object_data['menu-item-attr-title'] ) ? $_item_object_data['menu-item-attr-title'] : '' ), + 'menu-item-target' => ( isset( $_item_object_data['menu-item-target'] ) ? $_item_object_data['menu-item-target'] : '' ), + 'menu-item-classes' => ( isset( $_item_object_data['menu-item-classes'] ) ? $_item_object_data['menu-item-classes'] : '' ), + 'menu-item-xfn' => ( isset( $_item_object_data['menu-item-xfn'] ) ? $_item_object_data['menu-item-xfn'] : '' ), + ); + + $items_saved[] = wp_update_nav_menu_item( $menu_id, $_actual_db_id, $args ); + + } + } + return $items_saved; +} + +/** + * Adds custom arguments to some of the meta box object types. + * + * @since 3.0.0 + * + * @access private + * + * @param object $object The post type or taxonomy meta-object. + * @return object The post type of taxonomy object. + */ +function _wp_nav_menu_meta_box_object( $object = null ) { + if ( isset( $object->name ) ) { + + if ( 'page' == $object->name ) { + $object->_default_query = array( + 'orderby' => 'menu_order title', + 'post_status' => 'publish', + ); + + // Posts should show only published items. + } elseif ( 'post' == $object->name ) { + $object->_default_query = array( + 'post_status' => 'publish', + ); + + // Categories should be in reverse chronological order. + } elseif ( 'category' == $object->name ) { + $object->_default_query = array( + 'orderby' => 'id', + 'order' => 'DESC', + ); + + // Custom post types should show only published items. + } else { + $object->_default_query = array( + 'post_status' => 'publish', + ); + } + } + + return $object; +} + +/** + * Returns the menu formatted to edit. + * + * @since 3.0.0 + * + * @param int $menu_id Optional. The ID of the menu to format. Default 0. + * @return string|WP_Error $output The menu formatted to edit or error object on failure. + */ +function wp_get_nav_menu_to_edit( $menu_id = 0 ) { + $menu = wp_get_nav_menu_object( $menu_id ); + + // If the menu exists, get its items. + if ( is_nav_menu( $menu ) ) { + $menu_items = wp_get_nav_menu_items( $menu->term_id, array('post_status' => 'any') ); + $result = '
    ' : '">'; + $result .= '

    ' . __( 'Add menu items from the column on the left.' ) . '

    '; + $result .= '
    '; + + if( empty($menu_items) ) + return $result . ' '; + + /** + * Filter the Walker class used when adding nav menu items. + * + * @since 3.0.0 + * + * @param string $class The walker class to use. Default 'Walker_Nav_Menu_Edit'. + * @param int $menu_id ID of the menu being rendered. + */ + $walker_class_name = apply_filters( 'wp_edit_nav_menu_walker', 'Walker_Nav_Menu_Edit', $menu_id ); + + if ( class_exists( $walker_class_name ) ) + $walker = new $walker_class_name; + else + return new WP_Error( 'menu_walker_not_exist', sprintf( __('The Walker class named %s does not exist.'), $walker_class_name ) ); + + $some_pending_menu_items = $some_invalid_menu_items = false; + foreach( (array) $menu_items as $menu_item ) { + if ( isset( $menu_item->post_status ) && 'draft' == $menu_item->post_status ) + $some_pending_menu_items = true; + if ( ! empty( $menu_item->_invalid ) ) + $some_invalid_menu_items = true; + } + + if ( $some_pending_menu_items ) + $result .= '

    ' . __('Click Save Menu to make pending menu items public.') . '

    '; + + if ( $some_invalid_menu_items ) + $result .= '

    ' . __('There are some invalid menu items. Please check or delete them.') . '

    '; + + $result .= ' '; + return $result; + } elseif ( is_wp_error( $menu ) ) { + return $menu; + } + +} + +/** + * Returns the columns for the nav menus page. + * + * @since 3.0.0 + * + * @return string|WP_Error $output The menu formatted to edit or error object on failure. + */ +function wp_nav_menu_manage_columns() { + return array( + '_title' => __('Show advanced menu properties'), + 'cb' => '', + 'link-target' => __('Link Target'), + 'css-classes' => __('CSS Classes'), + 'xfn' => __('Link Relationship (XFN)'), + 'description' => __('Description'), + ); +} + +/** + * Deletes orphaned draft menu items + * + * @access private + * @since 3.0.0 + * + */ +function _wp_delete_orphaned_draft_menu_items() { + global $wpdb; + $delete_timestamp = time() - ( DAY_IN_SECONDS * EMPTY_TRASH_DAYS ); + + // Delete orphaned draft menu items. + $menu_items_to_delete = $wpdb->get_col($wpdb->prepare("SELECT ID FROM $wpdb->posts AS p LEFT JOIN $wpdb->postmeta AS m ON p.ID = m.post_id WHERE post_type = 'nav_menu_item' AND post_status = 'draft' AND meta_key = '_menu_item_orphaned' AND meta_value < '%d'", $delete_timestamp ) ); + + foreach( (array) $menu_items_to_delete as $menu_item_id ) + wp_delete_post( $menu_item_id, true ); +} +add_action('admin_head-nav-menus.php', '_wp_delete_orphaned_draft_menu_items'); + +/** + * Saves nav menu items + * + * @since 3.6.0 + * + * @param int|string $nav_menu_selected_id (id, slug, or name ) of the currently-selected menu + * @param string $nav_menu_selected_title Title of the currently-selected menu + * @return array $messages The menu updated message + */ +function wp_nav_menu_update_menu_items ( $nav_menu_selected_id, $nav_menu_selected_title ) { + $unsorted_menu_items = wp_get_nav_menu_items( $nav_menu_selected_id, array( 'orderby' => 'ID', 'output' => ARRAY_A, 'output_key' => 'ID', 'post_status' => 'draft,publish' ) ); + + $menu_items = array(); + // Index menu items by db ID + foreach ( $unsorted_menu_items as $_item ) + $menu_items[$_item->db_id] = $_item; + + $post_fields = array( + 'menu-item-db-id', 'menu-item-object-id', 'menu-item-object', + 'menu-item-parent-id', 'menu-item-position', 'menu-item-type', + 'menu-item-title', 'menu-item-url', 'menu-item-description', + 'menu-item-attr-title', 'menu-item-target', 'menu-item-classes', 'menu-item-xfn' + ); + + wp_defer_term_counting( true ); + // Loop through all the menu items' POST variables + if ( ! empty( $_POST['menu-item-db-id'] ) ) { + foreach( (array) $_POST['menu-item-db-id'] as $_key => $k ) { + + // Menu item title can't be blank + if ( ! isset( $_POST['menu-item-title'][ $_key ] ) || '' == $_POST['menu-item-title'][ $_key ] ) + continue; + + $args = array(); + foreach ( $post_fields as $field ) + $args[$field] = isset( $_POST[$field][$_key] ) ? $_POST[$field][$_key] : ''; + + $menu_item_db_id = wp_update_nav_menu_item( $nav_menu_selected_id, ( $_POST['menu-item-db-id'][$_key] != $_key ? 0 : $_key ), $args ); + + if ( is_wp_error( $menu_item_db_id ) ) + $messages[] = '

    ' . $menu_item_db_id->get_error_message() . '

    '; + elseif ( isset( $menu_items[$menu_item_db_id] ) ) + unset( $menu_items[$menu_item_db_id] ); + } + } + + // Remove menu items from the menu that weren't in $_POST + if ( ! empty( $menu_items ) ) { + foreach ( array_keys( $menu_items ) as $menu_item_id ) { + if ( is_nav_menu_item( $menu_item_id ) ) { + wp_delete_post( $menu_item_id ); + } + } + } + + // Store 'auto-add' pages. + $auto_add = ! empty( $_POST['auto-add-pages'] ); + $nav_menu_option = (array) get_option( 'nav_menu_options' ); + if ( ! isset( $nav_menu_option['auto_add'] ) ) + $nav_menu_option['auto_add'] = array(); + if ( $auto_add ) { + if ( ! in_array( $nav_menu_selected_id, $nav_menu_option['auto_add'] ) ) + $nav_menu_option['auto_add'][] = $nav_menu_selected_id; + } else { + if ( false !== ( $key = array_search( $nav_menu_selected_id, $nav_menu_option['auto_add'] ) ) ) + unset( $nav_menu_option['auto_add'][$key] ); + } + // Remove nonexistent/deleted menus + $nav_menu_option['auto_add'] = array_intersect( $nav_menu_option['auto_add'], wp_get_nav_menus( array( 'fields' => 'ids' ) ) ); + update_option( 'nav_menu_options', $nav_menu_option ); + + wp_defer_term_counting( false ); + + /** This action is documented in wp-includes/nav-menu.php */ + do_action( 'wp_update_nav_menu', $nav_menu_selected_id ); + + $messages[] = '

    ' . sprintf( __( '%1$s has been updated.' ), $nav_menu_selected_title ) . '

    '; + unset( $menu_items, $unsorted_menu_items ); + + return $messages; +} diff --git a/wp-admin/includes/plugin-install.php b/wp-admin/includes/plugin-install.php new file mode 100644 index 0000000..3031341 --- /dev/null +++ b/wp-admin/includes/plugin-install.php @@ -0,0 +1,566 @@ +per_page ) ) { + $args->per_page = 24; + } + + if ( ! isset( $args->locale ) ) { + $args->locale = get_locale(); + } + + /** + * Override the Plugin Install API arguments. + * + * Please ensure that an object is returned. + * + * @since 2.7.0 + * + * @param object $args Plugin API arguments. + * @param string $action The type of information being requested from the Plugin Install API. + */ + $args = apply_filters( 'plugins_api_args', $args, $action ); + + /** + * Allows a plugin to override the WordPress.org Plugin Install API entirely. + * + * Please ensure that an object is returned. + * + * @since 2.7.0 + * + * @param bool|object $result The result object. Default false. + * @param string $action The type of information being requested from the Plugin Install API. + * @param object $args Plugin API arguments. + */ + $res = apply_filters( 'plugins_api', false, $action, $args ); + + if ( false === $res ) { + $url = $http_url = 'http://api.wordpress.org/plugins/info/1.0/'; + if ( $ssl = wp_http_supports( array( 'ssl' ) ) ) + $url = set_url_scheme( $url, 'https' ); + + $args = array( + 'timeout' => 15, + 'body' => array( + 'action' => $action, + 'request' => serialize( $args ) + ) + ); + $request = wp_remote_post( $url, $args ); + + if ( $ssl && is_wp_error( $request ) ) { + trigger_error( __( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server’s configuration. If you continue to have problems, please try the support forums.' ) . ' ' . __( '(WordPress could not establish a secure connection to WordPress.org. Please contact your server administrator.)' ), headers_sent() || WP_DEBUG ? E_USER_WARNING : E_USER_NOTICE ); + $request = wp_remote_post( $http_url, $args ); + } + + if ( is_wp_error($request) ) { + $res = new WP_Error('plugins_api_failed', __( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server’s configuration. If you continue to have problems, please try the support forums.' ), $request->get_error_message() ); + } else { + $res = maybe_unserialize( wp_remote_retrieve_body( $request ) ); + if ( ! is_object( $res ) && ! is_array( $res ) ) + $res = new WP_Error('plugins_api_failed', __( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server’s configuration. If you continue to have problems, please try the support forums.' ), wp_remote_retrieve_body( $request ) ); + } + } elseif ( !is_wp_error($res) ) { + $res->external = true; + } + + /** + * Filter the Plugin Install API response results. + * + * @since 2.7.0 + * + * @param object|WP_Error $res Response object or WP_Error. + * @param string $action The type of information being requested from the Plugin Install API. + * @param object $args Plugin API arguments. + */ + return apply_filters( 'plugins_api_result', $res, $action, $args ); +} + +/** + * Retrieve popular WordPress plugin tags. + * + * @since 2.7.0 + * + * @param array $args + * @return array + */ +function install_popular_tags( $args = array() ) { + $key = md5(serialize($args)); + if ( false !== ($tags = get_site_transient('poptags_' . $key) ) ) + return $tags; + + $tags = plugins_api('hot_tags', $args); + + if ( is_wp_error($tags) ) + return $tags; + + set_site_transient( 'poptags_' . $key, $tags, 3 * HOUR_IN_SECONDS ); + + return $tags; +} + +function install_dashboard() { + ?> +

    WordPress Plugin Directory or upload a plugin in .zip format via this page.' ), 'https://wordpress.org/plugins/', self_admin_url( 'plugin-install.php?tab=upload' ) ); ?>

    + + + +

    +

    + '; + if ( is_wp_error($api_tags) ) { + echo $api_tags->get_error_message(); + } else { + //Set up the tags in a way which can be interpreted by wp_generate_tag_cloud() + $tags = array(); + foreach ( (array)$api_tags as $tag ) + $tags[ $tag['name'] ] = (object) array( + 'link' => esc_url( self_admin_url('plugin-install.php?tab=search&type=tag&s=' . urlencode($tag['name'])) ), + 'name' => $tag['name'], + 'id' => sanitize_title_with_dashes($tag['name']), + 'count' => $tag['count'] ); + echo wp_generate_tag_cloud($tags, array( 'single_text' => __('%s plugin'), 'multiple_text' => __('%s plugins') ) ); + } + echo '


    '; +} +add_action( 'install_plugins_featured', 'install_dashboard' ); + +/** + * Display search form for searching plugins. + * + * @since 2.7.0 + */ +function install_search_form( $type_selector = true ) { + $type = isset($_REQUEST['type']) ? wp_unslash( $_REQUEST['type'] ) : 'term'; + $term = isset($_REQUEST['s']) ? wp_unslash( $_REQUEST['s'] ) : ''; + $input_attrs = ''; + $button_type = 'button screen-reader-text'; + + // assume no $type_selector means it's a simplified search form + if ( ! $type_selector ) { + $input_attrs = 'class="wp-filter-search" placeholder="' . esc_attr__( 'Search Plugins' ) . '" '; + } + + ?>
    + + + + + + 'search-submit' ) ); ?> +
    +
    +

    +
    + + + + +
    +
    + +

    +
    + +

    + + + +

    +
    + ' . __( 'These suggestions are based on the plugins you and other users have installed.' ) . '

    '; + break; + } + + ?> +
    + display(); ?> +
    + response ) ) { + foreach ( (array)$update_plugins->response as $file => $plugin ) { + if ( $plugin->slug === $api->slug ) { + $status = 'update_available'; + $update_file = $file; + $version = $plugin->new_version; + if ( current_user_can('update_plugins') ) + $url = wp_nonce_url(self_admin_url('update.php?action=upgrade-plugin&plugin=' . $update_file), 'upgrade-plugin_' . $update_file); + break; + } + } + } + + if ( 'install' == $status ) { + if ( is_dir( WP_PLUGIN_DIR . '/' . $api->slug ) ) { + $installed_plugin = get_plugins('/' . $api->slug); + if ( empty($installed_plugin) ) { + if ( current_user_can('install_plugins') ) + $url = wp_nonce_url(self_admin_url('update.php?action=install-plugin&plugin=' . $api->slug), 'install-plugin_' . $api->slug); + } else { + $key = array_keys( $installed_plugin ); + $key = array_shift( $key ); //Use the first plugin regardless of the name, Could have issues for multiple-plugins in one directory if they share different version numbers + if ( version_compare($api->version, $installed_plugin[ $key ]['Version'], '=') ){ + $status = 'latest_installed'; + } elseif ( version_compare($api->version, $installed_plugin[ $key ]['Version'], '<') ) { + $status = 'newer_installed'; + $version = $installed_plugin[ $key ]['Version']; + } else { + //If the above update check failed, Then that probably means that the update checker has out-of-date information, force a refresh + if ( ! $loop ) { + delete_site_transient('update_plugins'); + wp_update_plugins(); + return install_plugin_install_status($api, true); + } + } + } + } else { + // "install" & no directory with that slug + if ( current_user_can('install_plugins') ) + $url = wp_nonce_url(self_admin_url('update.php?action=install-plugin&plugin=' . $api->slug), 'install-plugin_' . $api->slug); + } + } + if ( isset($_GET['from']) ) + $url .= '&from=' . urlencode( wp_unslash( $_GET['from'] ) ); + + return compact('status', 'url', 'version'); +} + +/** + * Display plugin information in dialog box form. + * + * @since 2.7.0 + */ +function install_plugin_information() { + global $tab; + + if ( empty( $_REQUEST['plugin'] ) ) { + return; + } + + $api = plugins_api( 'plugin_information', array( + 'slug' => wp_unslash( $_REQUEST['plugin'] ), + 'is_ssl' => is_ssl(), + 'fields' => array( 'banners' => true, 'reviews' => true ) + ) ); + + if ( is_wp_error( $api ) ) { + wp_die( $api ); + } + + $plugins_allowedtags = array( + 'a' => array( 'href' => array(), 'title' => array(), 'target' => array() ), + 'abbr' => array( 'title' => array() ), 'acronym' => array( 'title' => array() ), + 'code' => array(), 'pre' => array(), 'em' => array(), 'strong' => array(), + 'div' => array( 'class' => array() ), 'span' => array( 'class' => array() ), + 'p' => array(), 'ul' => array(), 'ol' => array(), 'li' => array(), + 'h1' => array(), 'h2' => array(), 'h3' => array(), 'h4' => array(), 'h5' => array(), 'h6' => array(), + 'img' => array( 'src' => array(), 'class' => array(), 'alt' => array() ) + ); + + $plugins_section_titles = array( + 'description' => _x( 'Description', 'Plugin installer section title' ), + 'installation' => _x( 'Installation', 'Plugin installer section title' ), + 'faq' => _x( 'FAQ', 'Plugin installer section title' ), + 'screenshots' => _x( 'Screenshots', 'Plugin installer section title' ), + 'changelog' => _x( 'Changelog', 'Plugin installer section title' ), + 'reviews' => _x( 'Reviews', 'Plugin installer section title' ), + 'other_notes' => _x( 'Other Notes', 'Plugin installer section title' ) + ); + + // Sanitize HTML + foreach ( (array) $api->sections as $section_name => $content ) { + $api->sections[$section_name] = wp_kses( $content, $plugins_allowedtags ); + } + + foreach ( array( 'version', 'author', 'requires', 'tested', 'homepage', 'downloaded', 'slug' ) as $key ) { + if ( isset( $api->$key ) ) { + $api->$key = wp_kses( $api->$key, $plugins_allowedtags ); + } + } + + $_tab = esc_attr( $tab ); + + $section = isset( $_REQUEST['section'] ) ? wp_unslash( $_REQUEST['section'] ) : 'description'; // Default to the Description tab, Do not translate, API returns English. + if ( empty( $section ) || ! isset( $api->sections[ $section ] ) ) { + $section_titles = array_keys( (array) $api->sections ); + $section = array_shift( $section_titles ); + } + + iframe_header( __( 'Plugin Install' ) ); + + $_with_banner = ''; + + if ( ! empty( $api->banners ) && ( ! empty( $api->banners['low'] ) || ! empty( $api->banners['high'] ) ) ) { + $_with_banner = 'with-banner'; + $low = empty( $api->banners['low'] ) ? $api->banners['high'] : $api->banners['low']; + $high = empty( $api->banners['high'] ) ? $api->banners['low'] : $api->banners['high']; + ?> + + '; + echo "

    {$api->name}

    "; + echo "
    \n"; + + foreach ( (array) $api->sections as $section_name => $content ) { + if ( 'reviews' === $section_name && ( empty( $api->ratings ) || 0 === array_sum( (array) $api->ratings ) ) ) { + continue; + } + + if ( isset( $plugins_section_titles[ $section_name ] ) ) { + $title = $plugins_section_titles[ $section_name ]; + } else { + $title = ucwords( str_replace( '_', ' ', $section_name ) ); + } + + $class = ( $section_name === $section ) ? ' class="current"' : ''; + $href = add_query_arg( array('tab' => $tab, 'section' => $section_name) ); + $href = esc_url( $href ); + $san_section = esc_attr( $section_name ); + echo "\t$title\n"; + } + + echo "
    \n"; + + ?> +
    +
    +
      + version ) ) { ?> +
    • version; ?>
    • + author ) ) { ?> +
    • author, '_blank' ); ?>
    • + last_updated ) ) { ?> +
    • + last_updated ) ) ); ?> +
    • + requires ) ) { ?> +
    • requires ); ?>
    • + tested ) ) { ?> +
    • tested; ?>
    • + downloaded ) ) { ?> +
    • downloaded ), number_format_i18n( $api->downloaded ) ); ?>
    • + slug ) && empty( $api->external ) ) { ?> +
    • + homepage ) ) { ?> +
    • + donate_link ) && empty( $api->contributors ) ) { ?> +
    • + +
    + rating ) ) { ?> +

    + $api->rating, 'type' => 'percent', 'number' => $api->num_ratings ) ); ?> + num_ratings ), number_format_i18n( $api->num_ratings ) ); ?> + ratings ) && array_sum( (array) $api->ratings ) > 0 ) { + foreach( $api->ratings as $key => $ratecount ) { + // Avoid div-by-zero. + $_rating = $api->num_ratings ? ( $ratecount / $api->num_ratings ) : 0; + ?> +
    + + + + + +
    + contributors ) ) { ?> +

    +
      + contributors as $contrib_username => $contrib_profile ) { + if ( empty( $contrib_username ) && empty( $contrib_profile ) ) { + continue; + } + if ( empty( $contrib_username ) ) { + $contrib_username = preg_replace( '/^.+\/(.+)\/?$/', '\1', $contrib_profile ); + } + $contrib_username = sanitize_user( $contrib_username ); + if ( empty( $contrib_profile ) ) { + echo "
    • {$contrib_username}
    • "; + } else { + echo "
    • {$contrib_username}
    • "; + } + } + ?> +
    + donate_link ) ) { ?> + + + +
    +
    + tested ) && version_compare( substr( $GLOBALS['wp_version'], 0, strlen( $api->tested ) ), $api->tested, '>' ) ) { + echo '

    ' . __('Warning: This plugin has not been tested with your current version of WordPress.') . '

    '; + } else if ( ! empty( $api->requires ) && version_compare( substr( $GLOBALS['wp_version'], 0, strlen( $api->requires ) ), $api->requires, '<' ) ) { + echo '

    ' . __('Warning: This plugin has not been marked as compatible with your version of WordPress.') . '

    '; + } + + foreach ( (array) $api->sections as $section_name => $content ) { + $content = links_add_base_url( $content, 'https://wordpress.org/plugins/' . $api->slug . '/' ); + $content = links_add_target( $content, '_blank' ); + + $san_section = esc_attr( $section_name ); + + $display = ( $section_name === $section ) ? 'block' : 'none'; + + echo "\t
    \n"; + echo $content; + echo "\t
    \n"; + } + echo "
    \n"; + echo "
    \n"; + echo "\n"; // #plugin-information-scrollable + echo "\n"; + + iframe_footer(); + exit; +} +add_action('install_plugins_pre_plugin-information', 'install_plugin_information'); diff --git a/wp-admin/includes/plugin.php b/wp-admin/includes/plugin.php new file mode 100644 index 0000000..e2e3776 --- /dev/null +++ b/wp-admin/includes/plugin.php @@ -0,0 +1,1897 @@ + 'Plugin Name', + 'PluginURI' => 'Plugin URI', + 'Version' => 'Version', + 'Description' => 'Description', + 'Author' => 'Author', + 'AuthorURI' => 'Author URI', + 'TextDomain' => 'Text Domain', + 'DomainPath' => 'Domain Path', + 'Network' => 'Network', + // Site Wide Only is deprecated in favor of Network. + '_sitewide' => 'Site Wide Only', + ); + + $plugin_data = get_file_data( $plugin_file, $default_headers, 'plugin' ); + + // Site Wide Only is the old header for Network + if ( ! $plugin_data['Network'] && $plugin_data['_sitewide'] ) { + _deprecated_argument( __FUNCTION__, '3.0', sprintf( __( 'The %1$s plugin header is deprecated. Use %2$s instead.' ), 'Site Wide Only: true', 'Network: true' ) ); + $plugin_data['Network'] = $plugin_data['_sitewide']; + } + $plugin_data['Network'] = ( 'true' == strtolower( $plugin_data['Network'] ) ); + unset( $plugin_data['_sitewide'] ); + + if ( $markup || $translate ) { + $plugin_data = _get_plugin_data_markup_translate( $plugin_file, $plugin_data, $markup, $translate ); + } else { + $plugin_data['Title'] = $plugin_data['Name']; + $plugin_data['AuthorName'] = $plugin_data['Author']; + } + + return $plugin_data; +} + +/** + * Sanitizes plugin data, optionally adds markup, optionally translates. + * + * @since 2.7.0 + * @access private + * @see get_plugin_data() + */ +function _get_plugin_data_markup_translate( $plugin_file, $plugin_data, $markup = true, $translate = true ) { + + // Sanitize the plugin filename to a WP_PLUGIN_DIR relative path + $plugin_file = plugin_basename( $plugin_file ); + + // Translate fields + if ( $translate ) { + if ( $textdomain = $plugin_data['TextDomain'] ) { + if ( $plugin_data['DomainPath'] ) + load_plugin_textdomain( $textdomain, false, dirname( $plugin_file ) . $plugin_data['DomainPath'] ); + else + load_plugin_textdomain( $textdomain, false, dirname( $plugin_file ) ); + } elseif ( in_array( basename( $plugin_file ), array( 'hello.php', 'akismet.php' ) ) ) { + $textdomain = 'default'; + } + if ( $textdomain ) { + foreach ( array( 'Name', 'PluginURI', 'Description', 'Author', 'AuthorURI', 'Version' ) as $field ) + $plugin_data[ $field ] = translate( $plugin_data[ $field ], $textdomain ); + } + } + + // Sanitize fields + $allowed_tags = $allowed_tags_in_links = array( + 'abbr' => array( 'title' => true ), + 'acronym' => array( 'title' => true ), + 'code' => true, + 'em' => true, + 'strong' => true, + ); + $allowed_tags['a'] = array( 'href' => true, 'title' => true ); + + // Name is marked up inside tags. Don't allow these. + // Author is too, but some plugins have used here (omitting Author URI). + $plugin_data['Name'] = wp_kses( $plugin_data['Name'], $allowed_tags_in_links ); + $plugin_data['Author'] = wp_kses( $plugin_data['Author'], $allowed_tags ); + + $plugin_data['Description'] = wp_kses( $plugin_data['Description'], $allowed_tags ); + $plugin_data['Version'] = wp_kses( $plugin_data['Version'], $allowed_tags ); + + $plugin_data['PluginURI'] = esc_url( $plugin_data['PluginURI'] ); + $plugin_data['AuthorURI'] = esc_url( $plugin_data['AuthorURI'] ); + + $plugin_data['Title'] = $plugin_data['Name']; + $plugin_data['AuthorName'] = $plugin_data['Author']; + + // Apply markup + if ( $markup ) { + if ( $plugin_data['PluginURI'] && $plugin_data['Name'] ) + $plugin_data['Title'] = '' . $plugin_data['Name'] . ''; + + if ( $plugin_data['AuthorURI'] && $plugin_data['Author'] ) + $plugin_data['Author'] = '' . $plugin_data['Author'] . ''; + + $plugin_data['Description'] = wptexturize( $plugin_data['Description'] ); + + if ( $plugin_data['Author'] ) + $plugin_data['Description'] .= ' ' . sprintf( __('By %s.'), $plugin_data['Author'] ) . ''; + } + + return $plugin_data; +} + +/** + * Get a list of a plugin's files. + * + * @since 2.8.0 + * + * @param string $plugin Plugin ID + * @return array List of files relative to the plugin root. + */ +function get_plugin_files($plugin) { + $plugin_file = WP_PLUGIN_DIR . '/' . $plugin; + $dir = dirname($plugin_file); + $plugin_files = array($plugin); + if ( is_dir($dir) && $dir != WP_PLUGIN_DIR ) { + $plugins_dir = @ opendir( $dir ); + if ( $plugins_dir ) { + while (($file = readdir( $plugins_dir ) ) !== false ) { + if ( substr($file, 0, 1) == '.' ) + continue; + if ( is_dir( $dir . '/' . $file ) ) { + $plugins_subdir = @ opendir( $dir . '/' . $file ); + if ( $plugins_subdir ) { + while (($subfile = readdir( $plugins_subdir ) ) !== false ) { + if ( substr($subfile, 0, 1) == '.' ) + continue; + $plugin_files[] = plugin_basename("$dir/$file/$subfile"); + } + @closedir( $plugins_subdir ); + } + } else { + if ( plugin_basename("$dir/$file") != $plugin ) + $plugin_files[] = plugin_basename("$dir/$file"); + } + } + @closedir( $plugins_dir ); + } + } + + return $plugin_files; +} + +/** + * Check the plugins directory and retrieve all plugin files with plugin data. + * + * WordPress only supports plugin files in the base plugins directory + * (wp-content/plugins) and in one directory above the plugins directory + * (wp-content/plugins/my-plugin). The file it looks for has the plugin data and + * must be found in those two locations. It is recommended that do keep your + * plugin files in directories. + * + * The file with the plugin data is the file that will be included and therefore + * needs to have the main execution for the plugin. This does not mean + * everything must be contained in the file and it is recommended that the file + * be split for maintainability. Keep everything in one file for extreme + * optimization purposes. + * + * @since 1.5.0 + * + * @param string $plugin_folder Optional. Relative path to single plugin folder. + * @return array Key is the plugin file path and the value is an array of the plugin data. + */ +function get_plugins($plugin_folder = '') { + + if ( ! $cache_plugins = wp_cache_get('plugins', 'plugins') ) + $cache_plugins = array(); + + if ( isset($cache_plugins[ $plugin_folder ]) ) + return $cache_plugins[ $plugin_folder ]; + + $wp_plugins = array (); + $plugin_root = WP_PLUGIN_DIR; + if ( !empty($plugin_folder) ) + $plugin_root .= $plugin_folder; + + // Files in wp-content/plugins directory + $plugins_dir = @ opendir( $plugin_root); + $plugin_files = array(); + if ( $plugins_dir ) { + while (($file = readdir( $plugins_dir ) ) !== false ) { + if ( substr($file, 0, 1) == '.' ) + continue; + if ( is_dir( $plugin_root.'/'.$file ) ) { + $plugins_subdir = @ opendir( $plugin_root.'/'.$file ); + if ( $plugins_subdir ) { + while (($subfile = readdir( $plugins_subdir ) ) !== false ) { + if ( substr($subfile, 0, 1) == '.' ) + continue; + if ( substr($subfile, -4) == '.php' ) + $plugin_files[] = "$file/$subfile"; + } + closedir( $plugins_subdir ); + } + } else { + if ( substr($file, -4) == '.php' ) + $plugin_files[] = $file; + } + } + closedir( $plugins_dir ); + } + + if ( empty($plugin_files) ) + return $wp_plugins; + + foreach ( $plugin_files as $plugin_file ) { + if ( !is_readable( "$plugin_root/$plugin_file" ) ) + continue; + + $plugin_data = get_plugin_data( "$plugin_root/$plugin_file", false, false ); //Do not apply markup/translate as it'll be cached. + + if ( empty ( $plugin_data['Name'] ) ) + continue; + + $wp_plugins[plugin_basename( $plugin_file )] = $plugin_data; + } + + uasort( $wp_plugins, '_sort_uname_callback' ); + + $cache_plugins[ $plugin_folder ] = $wp_plugins; + wp_cache_set('plugins', $cache_plugins, 'plugins'); + + return $wp_plugins; +} + +/** + * Check the mu-plugins directory and retrieve all mu-plugin files with any plugin data. + * + * WordPress only includes mu-plugin files in the base mu-plugins directory (wp-content/mu-plugins). + * + * @since 3.0.0 + * @return array Key is the mu-plugin file path and the value is an array of the mu-plugin data. + */ +function get_mu_plugins() { + $wp_plugins = array(); + // Files in wp-content/mu-plugins directory + $plugin_files = array(); + + if ( ! is_dir( WPMU_PLUGIN_DIR ) ) + return $wp_plugins; + if ( $plugins_dir = @ opendir( WPMU_PLUGIN_DIR ) ) { + while ( ( $file = readdir( $plugins_dir ) ) !== false ) { + if ( substr( $file, -4 ) == '.php' ) + $plugin_files[] = $file; + } + } else { + return $wp_plugins; + } + + @closedir( $plugins_dir ); + + if ( empty($plugin_files) ) + return $wp_plugins; + + foreach ( $plugin_files as $plugin_file ) { + if ( !is_readable( WPMU_PLUGIN_DIR . "/$plugin_file" ) ) + continue; + + $plugin_data = get_plugin_data( WPMU_PLUGIN_DIR . "/$plugin_file", false, false ); //Do not apply markup/translate as it'll be cached. + + if ( empty ( $plugin_data['Name'] ) ) + $plugin_data['Name'] = $plugin_file; + + $wp_plugins[ $plugin_file ] = $plugin_data; + } + + if ( isset( $wp_plugins['index.php'] ) && filesize( WPMU_PLUGIN_DIR . '/index.php') <= 30 ) // silence is golden + unset( $wp_plugins['index.php'] ); + + uasort( $wp_plugins, '_sort_uname_callback' ); + + return $wp_plugins; +} + +/** + * Callback to sort array by a 'Name' key. + * + * @since 3.1.0 + * @access private + */ +function _sort_uname_callback( $a, $b ) { + return strnatcasecmp( $a['Name'], $b['Name'] ); +} + +/** + * Check the wp-content directory and retrieve all drop-ins with any plugin data. + * + * @since 3.0.0 + * @return array Key is the file path and the value is an array of the plugin data. + */ +function get_dropins() { + $dropins = array(); + $plugin_files = array(); + + $_dropins = _get_dropins(); + + // These exist in the wp-content directory + if ( $plugins_dir = @ opendir( WP_CONTENT_DIR ) ) { + while ( ( $file = readdir( $plugins_dir ) ) !== false ) { + if ( isset( $_dropins[ $file ] ) ) + $plugin_files[] = $file; + } + } else { + return $dropins; + } + + @closedir( $plugins_dir ); + + if ( empty($plugin_files) ) + return $dropins; + + foreach ( $plugin_files as $plugin_file ) { + if ( !is_readable( WP_CONTENT_DIR . "/$plugin_file" ) ) + continue; + $plugin_data = get_plugin_data( WP_CONTENT_DIR . "/$plugin_file", false, false ); //Do not apply markup/translate as it'll be cached. + if ( empty( $plugin_data['Name'] ) ) + $plugin_data['Name'] = $plugin_file; + $dropins[ $plugin_file ] = $plugin_data; + } + + uksort( $dropins, 'strnatcasecmp' ); + + return $dropins; +} + +/** + * Returns drop-ins that WordPress uses. + * + * Includes Multisite drop-ins only when is_multisite() + * + * @since 3.0.0 + * @return array Key is file name. The value is an array, with the first value the + * purpose of the drop-in and the second value the name of the constant that must be + * true for the drop-in to be used, or true if no constant is required. + */ +function _get_dropins() { + $dropins = array( + 'advanced-cache.php' => array( __( 'Advanced caching plugin.' ), 'WP_CACHE' ), // WP_CACHE + 'db.php' => array( __( 'Custom database class.' ), true ), // auto on load + 'db-error.php' => array( __( 'Custom database error message.' ), true ), // auto on error + 'install.php' => array( __( 'Custom install script.' ), true ), // auto on install + 'maintenance.php' => array( __( 'Custom maintenance message.' ), true ), // auto on maintenance + 'object-cache.php' => array( __( 'External object cache.' ), true ), // auto on load + ); + + if ( is_multisite() ) { + $dropins['sunrise.php' ] = array( __( 'Executed before Multisite is loaded.' ), 'SUNRISE' ); // SUNRISE + $dropins['blog-deleted.php' ] = array( __( 'Custom site deleted message.' ), true ); // auto on deleted blog + $dropins['blog-inactive.php' ] = array( __( 'Custom site inactive message.' ), true ); // auto on inactive blog + $dropins['blog-suspended.php'] = array( __( 'Custom site suspended message.' ), true ); // auto on archived or spammed blog + } + + return $dropins; +} + +/** + * Check whether the plugin is active by checking the active_plugins list. + * + * @since 2.5.0 + * + * @param string $plugin Base plugin path from plugins directory. + * @return bool True, if in the active plugins list. False, not in the list. + */ +function is_plugin_active( $plugin ) { + return in_array( $plugin, (array) get_option( 'active_plugins', array() ) ) || is_plugin_active_for_network( $plugin ); +} + +/** + * Check whether the plugin is inactive. + * + * Reverse of is_plugin_active(). Used as a callback. + * + * @since 3.1.0 + * @see is_plugin_active() + * + * @param string $plugin Base plugin path from plugins directory. + * @return bool True if inactive. False if active. + */ +function is_plugin_inactive( $plugin ) { + return ! is_plugin_active( $plugin ); +} + +/** + * Check whether the plugin is active for the entire network. + * + * @since 3.0.0 + * + * @param string $plugin Base plugin path from plugins directory. + * @return bool True, if active for the network, otherwise false. + */ +function is_plugin_active_for_network( $plugin ) { + if ( !is_multisite() ) + return false; + + $plugins = get_site_option( 'active_sitewide_plugins'); + if ( isset($plugins[$plugin]) ) + return true; + + return false; +} + +/** + * Checks for "Network: true" in the plugin header to see if this should + * be activated only as a network wide plugin. The plugin would also work + * when Multisite is not enabled. + * + * Checks for "Site Wide Only: true" for backwards compatibility. + * + * @since 3.0.0 + * + * @param string $plugin Plugin to check + * @return bool True if plugin is network only, false otherwise. + */ +function is_network_only_plugin( $plugin ) { + $plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin ); + if ( $plugin_data ) + return $plugin_data['Network']; + return false; +} + +/** + * Attempts activation of plugin in a "sandbox" and redirects on success. + * + * A plugin that is already activated will not attempt to be activated again. + * + * The way it works is by setting the redirection to the error before trying to + * include the plugin file. If the plugin fails, then the redirection will not + * be overwritten with the success message. Also, the options will not be + * updated and the activation hook will not be called on plugin error. + * + * It should be noted that in no way the below code will actually prevent errors + * within the file. The code should not be used elsewhere to replicate the + * "sandbox", which uses redirection to work. + * {@source 13 1} + * + * If any errors are found or text is outputted, then it will be captured to + * ensure that the success redirection will update the error redirection. + * + * @since 2.5.0 + * + * @param string $plugin Plugin path to main plugin file with plugin data. + * @param string $redirect Optional. URL to redirect to. + * @param bool $network_wide Whether to enable the plugin for all sites in the + * network or just the current site. Multisite only. Default is false. + * @param bool $silent Prevent calling activation hooks. Optional, default is false. + * @return WP_Error|null WP_Error on invalid file or null on success. + */ +function activate_plugin( $plugin, $redirect = '', $network_wide = false, $silent = false ) { + $plugin = plugin_basename( trim( $plugin ) ); + + if ( is_multisite() && ( $network_wide || is_network_only_plugin($plugin) ) ) { + $network_wide = true; + $current = get_site_option( 'active_sitewide_plugins', array() ); + $_GET['networkwide'] = 1; // Back compat for plugins looking for this value. + } else { + $current = get_option( 'active_plugins', array() ); + } + + $valid = validate_plugin($plugin); + if ( is_wp_error($valid) ) + return $valid; + + if ( ( $network_wide && ! isset( $current[ $plugin ] ) ) || ( ! $network_wide && ! in_array( $plugin, $current ) ) ) { + if ( !empty($redirect) ) + wp_redirect(add_query_arg('_error_nonce', wp_create_nonce('plugin-activation-error_' . $plugin), $redirect)); // we'll override this later if the plugin can be included without fatal error + ob_start(); + wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $plugin ); + $_wp_plugin_file = $plugin; + include_once( WP_PLUGIN_DIR . '/' . $plugin ); + $plugin = $_wp_plugin_file; // Avoid stomping of the $plugin variable in a plugin. + + if ( ! $silent ) { + /** + * Fires before a plugin is activated. + * + * If a plugin is silently activated (such as during an update), + * this hook does not fire. + * + * @since 2.9.0 + * + * @param string $plugin Plugin path to main plugin file with plugin data. + * @param bool $network_wide Whether to enable the plugin for all sites in the network + * or just the current site. Multisite only. Default is false. + */ + do_action( 'activate_plugin', $plugin, $network_wide ); + + /** + * Fires as a specific plugin is being activated. + * + * This hook is the "activation" hook used internally by + * {@see register_activation_hook()}. The dynamic portion of the + * hook name, `$plugin`, refers to the plugin basename. + * + * If a plugin is silently activated (such as during an update), + * this hook does not fire. + * + * @since 2.0.0 + * + * @param bool $network_wide Whether to enable the plugin for all sites in the network + * or just the current site. Multisite only. Default is false. + */ + do_action( 'activate_' . $plugin, $network_wide ); + } + + if ( $network_wide ) { + $current[$plugin] = time(); + update_site_option( 'active_sitewide_plugins', $current ); + } else { + $current[] = $plugin; + sort($current); + update_option('active_plugins', $current); + } + + if ( ! $silent ) { + /** + * Fires after a plugin has been activated. + * + * If a plugin is silently activated (such as during an update), + * this hook does not fire. + * + * @since 2.9.0 + * + * @param string $plugin Plugin path to main plugin file with plugin data. + * @param bool $network_wide Whether to enable the plugin for all sites in the network + * or just the current site. Multisite only. Default is false. + */ + do_action( 'activated_plugin', $plugin, $network_wide ); + } + + if ( ob_get_length() > 0 ) { + $output = ob_get_clean(); + return new WP_Error('unexpected_output', __('The plugin generated unexpected output.'), $output); + } + ob_end_clean(); + } + + return null; +} + +/** + * Deactivate a single plugin or multiple plugins. + * + * The deactivation hook is disabled by the plugin upgrader by using the $silent + * parameter. + * + * @since 2.5.0 + * + * @param string|array $plugins Single plugin or list of plugins to deactivate. + * @param bool $silent Prevent calling deactivation hooks. Default is false. + * @param mixed $network_wide Whether to deactivate the plugin for all sites in the network. + * A value of null (the default) will deactivate plugins for both the site and the network. + */ +function deactivate_plugins( $plugins, $silent = false, $network_wide = null ) { + if ( is_multisite() ) + $network_current = get_site_option( 'active_sitewide_plugins', array() ); + $current = get_option( 'active_plugins', array() ); + $do_blog = $do_network = false; + + foreach ( (array) $plugins as $plugin ) { + $plugin = plugin_basename( trim( $plugin ) ); + if ( ! is_plugin_active($plugin) ) + continue; + + $network_deactivating = false !== $network_wide && is_plugin_active_for_network( $plugin ); + + if ( ! $silent ) { + /** + * Fires before a plugin is deactivated. + * + * If a plugin is silently deactivated (such as during an update), + * this hook does not fire. + * + * @since 2.9.0 + * + * @param string $plugin Plugin path to main plugin file with plugin data. + * @param bool $network_deactivating Whether the plugin is deactivated for all sites in the network + * or just the current site. Multisite only. Default is false. + */ + do_action( 'deactivate_plugin', $plugin, $network_deactivating ); + } + + if ( false !== $network_wide ) { + if ( is_plugin_active_for_network( $plugin ) ) { + $do_network = true; + unset( $network_current[ $plugin ] ); + } elseif ( $network_wide ) { + continue; + } + } + + if ( true !== $network_wide ) { + $key = array_search( $plugin, $current ); + if ( false !== $key ) { + $do_blog = true; + unset( $current[ $key ] ); + } + } + + if ( ! $silent ) { + /** + * Fires as a specific plugin is being deactivated. + * + * This hook is the "deactivation" hook used internally by + * {@see register_deactivation_hook()}. The dynamic portion of the + * hook name, `$plugin`, refers to the plugin basename. + * + * If a plugin is silently deactivated (such as during an update), + * this hook does not fire. + * + * @since 2.0.0 + * + * @param bool $network_deactivating Whether the plugin is deactivated for all sites in the network + * or just the current site. Multisite only. Default is false. + */ + do_action( 'deactivate_' . $plugin, $network_deactivating ); + + /** + * Fires after a plugin is deactivated. + * + * If a plugin is silently deactivated (such as during an update), + * this hook does not fire. + * + * @since 2.9.0 + * + * @param string $plugin Plugin basename. + * @param bool $network_deactivating Whether the plugin is deactivated for all sites in the network + * or just the current site. Multisite only. Default false. + */ + do_action( 'deactivated_plugin', $plugin, $network_deactivating ); + } + } + + if ( $do_blog ) + update_option('active_plugins', $current); + if ( $do_network ) + update_site_option( 'active_sitewide_plugins', $network_current ); +} + +/** + * Activate multiple plugins. + * + * When WP_Error is returned, it does not mean that one of the plugins had + * errors. It means that one or more of the plugins file path was invalid. + * + * The execution will be halted as soon as one of the plugins has an error. + * + * @since 2.6.0 + * + * @param string|array $plugins Single plugin or list of plugins to activate. + * @param string $redirect Redirect to page after successful activation. + * @param bool $network_wide Whether to enable the plugin for all sites in the network. + * @param bool $silent Prevent calling activation hooks. Default is false. + * @return bool|WP_Error True when finished or WP_Error if there were errors during a plugin activation. + */ +function activate_plugins( $plugins, $redirect = '', $network_wide = false, $silent = false ) { + if ( !is_array($plugins) ) + $plugins = array($plugins); + + $errors = array(); + foreach ( $plugins as $plugin ) { + if ( !empty($redirect) ) + $redirect = add_query_arg('plugin', $plugin, $redirect); + $result = activate_plugin($plugin, $redirect, $network_wide, $silent); + if ( is_wp_error($result) ) + $errors[$plugin] = $result; + } + + if ( !empty($errors) ) + return new WP_Error('plugins_invalid', __('One of the plugins is invalid.'), $errors); + + return true; +} + +/** + * Remove directory and files of a plugin for a list of plugins. + * + * @since 2.6.0 + * + * @param array $plugins List of plugins to delete. + * @param string $deprecated Deprecated. + * @return bool|null|WP_Error True on success, false is $plugins is empty, WP_Error on failure. + * Null if filesystem credentials are required to proceed. + */ +function delete_plugins( $plugins, $deprecated = '' ) { + global $wp_filesystem; + + if ( empty($plugins) ) + return false; + + $checked = array(); + foreach( $plugins as $plugin ) + $checked[] = 'checked[]=' . $plugin; + + ob_start(); + $url = wp_nonce_url('plugins.php?action=delete-selected&verify-delete=1&' . implode('&', $checked), 'bulk-plugins'); + if ( false === ($credentials = request_filesystem_credentials($url)) ) { + $data = ob_get_contents(); + ob_end_clean(); + if ( ! empty($data) ){ + include_once( ABSPATH . 'wp-admin/admin-header.php'); + echo $data; + include( ABSPATH . 'wp-admin/admin-footer.php'); + exit; + } + return; + } + + if ( ! WP_Filesystem($credentials) ) { + request_filesystem_credentials($url, '', true); //Failed to connect, Error and request again + $data = ob_get_contents(); + ob_end_clean(); + if ( ! empty($data) ){ + include_once( ABSPATH . 'wp-admin/admin-header.php'); + echo $data; + include( ABSPATH . 'wp-admin/admin-footer.php'); + exit; + } + return; + } + + if ( ! is_object($wp_filesystem) ) + return new WP_Error('fs_unavailable', __('Could not access filesystem.')); + + if ( is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code() ) + return new WP_Error('fs_error', __('Filesystem error.'), $wp_filesystem->errors); + + // Get the base plugin folder. + $plugins_dir = $wp_filesystem->wp_plugins_dir(); + if ( empty( $plugins_dir ) ) { + return new WP_Error( 'fs_no_plugins_dir', __( 'Unable to locate WordPress Plugin directory.' ) ); + } + + $plugins_dir = trailingslashit( $plugins_dir ); + + $translations_dir = $wp_filesystem->wp_lang_dir(); + $translations_dir = trailingslashit( $translations_dir ); + + $plugin_translations = wp_get_installed_translations( 'plugins' ); + + $errors = array(); + + foreach( $plugins as $plugin_file ) { + // Run Uninstall hook. + if ( is_uninstallable_plugin( $plugin_file ) ) { + uninstall_plugin($plugin_file); + } + + $this_plugin_dir = trailingslashit( dirname( $plugins_dir . $plugin_file ) ); + // If plugin is in its own directory, recursively delete the directory. + if ( strpos( $plugin_file, '/' ) && $this_plugin_dir != $plugins_dir ) { //base check on if plugin includes directory separator AND that it's not the root plugin folder + $deleted = $wp_filesystem->delete( $this_plugin_dir, true ); + } else { + $deleted = $wp_filesystem->delete( $plugins_dir . $plugin_file ); + } + + if ( ! $deleted ) { + $errors[] = $plugin_file; + continue; + } + + // Remove language files, silently. + $plugin_slug = dirname( $plugin_file ); + if ( '.' !== $plugin_slug && ! empty( $plugin_translations[ $plugin_slug ] ) ) { + $translations = $plugin_translations[ $plugin_slug ]; + + foreach ( $translations as $translation => $data ) { + $wp_filesystem->delete( WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '.po' ); + $wp_filesystem->delete( WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '.mo' ); + } + } + } + + // Remove deleted plugins from the plugin updates list. + if ( $current = get_site_transient('update_plugins') ) { + // Don't remove the plugins that weren't deleted. + $deleted = array_diff( $plugins, $errors ); + + foreach ( $deleted as $plugin_file ) { + unset( $current->response[ $plugin_file ] ); + } + + set_site_transient( 'update_plugins', $current ); + } + + if ( ! empty($errors) ) + return new WP_Error('could_not_remove_plugin', sprintf(__('Could not fully remove the plugin(s) %s.'), implode(', ', $errors)) ); + + return true; +} + +/** + * Validate active plugins + * + * Validate all active plugins, deactivates invalid and + * returns an array of deactivated ones. + * + * @since 2.5.0 + * @return array invalid plugins, plugin as key, error as value + */ +function validate_active_plugins() { + $plugins = get_option( 'active_plugins', array() ); + // Validate vartype: array. + if ( ! is_array( $plugins ) ) { + update_option( 'active_plugins', array() ); + $plugins = array(); + } + + if ( is_multisite() && current_user_can( 'manage_network_plugins' ) ) { + $network_plugins = (array) get_site_option( 'active_sitewide_plugins', array() ); + $plugins = array_merge( $plugins, array_keys( $network_plugins ) ); + } + + if ( empty( $plugins ) ) + return; + + $invalid = array(); + + // Invalid plugins get deactivated. + foreach ( $plugins as $plugin ) { + $result = validate_plugin( $plugin ); + if ( is_wp_error( $result ) ) { + $invalid[$plugin] = $result; + deactivate_plugins( $plugin, true ); + } + } + return $invalid; +} + +/** + * Validate the plugin path. + * + * Checks that the file exists and {@link validate_file() is valid file}. + * + * @since 2.5.0 + * + * @param string $plugin Plugin Path + * @return WP_Error|int 0 on success, WP_Error on failure. + */ +function validate_plugin($plugin) { + if ( validate_file($plugin) ) + return new WP_Error('plugin_invalid', __('Invalid plugin path.')); + if ( ! file_exists(WP_PLUGIN_DIR . '/' . $plugin) ) + return new WP_Error('plugin_not_found', __('Plugin file does not exist.')); + + $installed_plugins = get_plugins(); + if ( ! isset($installed_plugins[$plugin]) ) + return new WP_Error('no_plugin_header', __('The plugin does not have a valid header.')); + return 0; +} + +/** + * Whether the plugin can be uninstalled. + * + * @since 2.7.0 + * + * @param string $plugin Plugin path to check. + * @return bool Whether plugin can be uninstalled. + */ +function is_uninstallable_plugin($plugin) { + $file = plugin_basename($plugin); + + $uninstallable_plugins = (array) get_option('uninstall_plugins'); + if ( isset( $uninstallable_plugins[$file] ) || file_exists( WP_PLUGIN_DIR . '/' . dirname($file) . '/uninstall.php' ) ) + return true; + + return false; +} + +/** + * Uninstall a single plugin. + * + * Calls the uninstall hook, if it is available. + * + * @since 2.7.0 + * + * @param string $plugin Relative plugin path from Plugin Directory. + */ +function uninstall_plugin($plugin) { + $file = plugin_basename($plugin); + + $uninstallable_plugins = (array) get_option('uninstall_plugins'); + if ( file_exists( WP_PLUGIN_DIR . '/' . dirname($file) . '/uninstall.php' ) ) { + if ( isset( $uninstallable_plugins[$file] ) ) { + unset($uninstallable_plugins[$file]); + update_option('uninstall_plugins', $uninstallable_plugins); + } + unset($uninstallable_plugins); + + define('WP_UNINSTALL_PLUGIN', $file); + wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . dirname( $file ) ); + include( WP_PLUGIN_DIR . '/' . dirname($file) . '/uninstall.php' ); + + return true; + } + + if ( isset( $uninstallable_plugins[$file] ) ) { + $callable = $uninstallable_plugins[$file]; + unset($uninstallable_plugins[$file]); + update_option('uninstall_plugins', $uninstallable_plugins); + unset($uninstallable_plugins); + + wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $file ); + include( WP_PLUGIN_DIR . '/' . $file ); + + add_action( 'uninstall_' . $file, $callable ); + + /** + * Fires in uninstall_plugin() once the plugin has been uninstalled. + * + * The action concatenates the 'uninstall_' prefix with the basename of the + * plugin passed to {@see uninstall_plugin()} to create a dynamically-named action. + * + * @since 2.7.0 + */ + do_action( 'uninstall_' . $file ); + } +} + +// +// Menu +// + +/** + * Add a top level menu page + * + * This function takes a capability which will be used to determine whether + * or not a page is included in the menu. + * + * The function which is hooked in to handle the output of the page must check + * that the user has the required capability as well. + * + * @param string $page_title The text to be displayed in the title tags of the page when the menu is selected + * @param string $menu_title The text to be used for the menu + * @param string $capability The capability required for this menu to be displayed to the user. + * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu) + * @param callback $function The function to be called to output the content for this page. + * @param string $icon_url The url to the icon to be used for this menu. + * * Pass a base64-encoded SVG using a data URI, which will be colored to match the color scheme. + * This should begin with 'data:image/svg+xml;base64,'. + * * Pass the name of a Dashicons helper class to use a font icon, e.g. 'dashicons-chart-pie'. + * * Pass 'none' to leave div.wp-menu-image empty so an icon can be added via CSS. + * @param int $position The position in the menu order this one should appear + * + * @return string The resulting page's hook_suffix + */ +function add_menu_page( $page_title, $menu_title, $capability, $menu_slug, $function = '', $icon_url = '', $position = null ) { + global $menu, $admin_page_hooks, $_registered_pages, $_parent_pages; + + $menu_slug = plugin_basename( $menu_slug ); + + $admin_page_hooks[$menu_slug] = sanitize_title( $menu_title ); + + $hookname = get_plugin_page_hookname( $menu_slug, '' ); + + if ( !empty( $function ) && !empty( $hookname ) && current_user_can( $capability ) ) + add_action( $hookname, $function ); + + if ( empty($icon_url) ) { + $icon_url = 'dashicons-admin-generic'; + $icon_class = 'menu-icon-generic '; + } else { + $icon_url = set_url_scheme( $icon_url ); + $icon_class = ''; + } + + $new_menu = array( $menu_title, $capability, $menu_slug, $page_title, 'menu-top ' . $icon_class . $hookname, $hookname, $icon_url ); + + if ( null === $position ) + $menu[] = $new_menu; + else + $menu[$position] = $new_menu; + + $_registered_pages[$hookname] = true; + + // No parent as top level + $_parent_pages[$menu_slug] = false; + + return $hookname; +} + +/** + * Add a top level menu page in the 'objects' section + * + * This function takes a capability which will be used to determine whether + * or not a page is included in the menu. + * + * The function which is hooked in to handle the output of the page must check + * that the user has the required capability as well. + * + * @param string $page_title The text to be displayed in the title tags of the page when the menu is selected + * @param string $menu_title The text to be used for the menu + * @param string $capability The capability required for this menu to be displayed to the user. + * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu) + * @param callback $function The function to be called to output the content for this page. + * @param string $icon_url The url to the icon to be used for this menu + * + * @return string The resulting page's hook_suffix + */ +function add_object_page( $page_title, $menu_title, $capability, $menu_slug, $function = '', $icon_url = '') { + global $_wp_last_object_menu; + + $_wp_last_object_menu++; + + return add_menu_page($page_title, $menu_title, $capability, $menu_slug, $function, $icon_url, $_wp_last_object_menu); +} + +/** + * Add a top level menu page in the 'utility' section + * + * This function takes a capability which will be used to determine whether + * or not a page is included in the menu. + * + * The function which is hooked in to handle the output of the page must check + * that the user has the required capability as well. + * + * @param string $page_title The text to be displayed in the title tags of the page when the menu is selected + * @param string $menu_title The text to be used for the menu + * @param string $capability The capability required for this menu to be displayed to the user. + * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu) + * @param callback $function The function to be called to output the content for this page. + * @param string $icon_url The url to the icon to be used for this menu + * + * @return string The resulting page's hook_suffix + */ +function add_utility_page( $page_title, $menu_title, $capability, $menu_slug, $function = '', $icon_url = '') { + global $_wp_last_utility_menu; + + $_wp_last_utility_menu++; + + return add_menu_page($page_title, $menu_title, $capability, $menu_slug, $function, $icon_url, $_wp_last_utility_menu); +} + +/** + * Add a sub menu page + * + * This function takes a capability which will be used to determine whether + * or not a page is included in the menu. + * + * The function which is hooked in to handle the output of the page must check + * that the user has the required capability as well. + * + * @param string $parent_slug The slug name for the parent menu (or the file name of a standard WordPress admin page) + * @param string $page_title The text to be displayed in the title tags of the page when the menu is selected + * @param string $menu_title The text to be used for the menu + * @param string $capability The capability required for this menu to be displayed to the user. + * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu) + * @param callback $function The function to be called to output the content for this page. + * + * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required. + */ +function add_submenu_page( $parent_slug, $page_title, $menu_title, $capability, $menu_slug, $function = '' ) { + global $submenu; + global $menu; + global $_wp_real_parent_file; + global $_wp_submenu_nopriv; + global $_registered_pages; + global $_parent_pages; + + $menu_slug = plugin_basename( $menu_slug ); + $parent_slug = plugin_basename( $parent_slug); + + if ( isset( $_wp_real_parent_file[$parent_slug] ) ) + $parent_slug = $_wp_real_parent_file[$parent_slug]; + + if ( !current_user_can( $capability ) ) { + $_wp_submenu_nopriv[$parent_slug][$menu_slug] = true; + return false; + } + + /* + * If the parent doesn't already have a submenu, add a link to the parent + * as the first item in the submenu. If the submenu file is the same as the + * parent file someone is trying to link back to the parent manually. In + * this case, don't automatically add a link back to avoid duplication. + */ + if (!isset( $submenu[$parent_slug] ) && $menu_slug != $parent_slug ) { + foreach ( (array)$menu as $parent_menu ) { + if ( $parent_menu[2] == $parent_slug && current_user_can( $parent_menu[1] ) ) + $submenu[$parent_slug][] = array_slice( $parent_menu, 0, 4 ); + } + } + + $submenu[$parent_slug][] = array ( $menu_title, $capability, $menu_slug, $page_title ); + + $hookname = get_plugin_page_hookname( $menu_slug, $parent_slug); + if (!empty ( $function ) && !empty ( $hookname )) + add_action( $hookname, $function ); + + $_registered_pages[$hookname] = true; + + /* + * Backward-compatibility for plugins using add_management page. + * See wp-admin/admin.php for redirect from edit.php to tools.php + */ + if ( 'tools.php' == $parent_slug ) + $_registered_pages[get_plugin_page_hookname( $menu_slug, 'edit.php')] = true; + + // No parent as top level. + $_parent_pages[$menu_slug] = $parent_slug; + + return $hookname; +} + +/** + * Add sub menu page to the tools main menu. + * + * This function takes a capability which will be used to determine whether + * or not a page is included in the menu. + * + * The function which is hooked in to handle the output of the page must check + * that the user has the required capability as well. + * + * @param string $page_title The text to be displayed in the title tags of the page when the menu is selected + * @param string $menu_title The text to be used for the menu + * @param string $capability The capability required for this menu to be displayed to the user. + * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu) + * @param callback $function The function to be called to output the content for this page. + * + * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required. + */ +function add_management_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) { + return add_submenu_page( 'tools.php', $page_title, $menu_title, $capability, $menu_slug, $function ); +} + +/** + * Add sub menu page to the options main menu. + * + * This function takes a capability which will be used to determine whether + * or not a page is included in the menu. + * + * The function which is hooked in to handle the output of the page must check + * that the user has the required capability as well. + * + * @param string $page_title The text to be displayed in the title tags of the page when the menu is selected + * @param string $menu_title The text to be used for the menu + * @param string $capability The capability required for this menu to be displayed to the user. + * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu) + * @param callback $function The function to be called to output the content for this page. + * + * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required. + */ +function add_options_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) { + return add_submenu_page( 'options-general.php', $page_title, $menu_title, $capability, $menu_slug, $function ); +} + +/** + * Add sub menu page to the themes main menu. + * + * This function takes a capability which will be used to determine whether + * or not a page is included in the menu. + * + * The function which is hooked in to handle the output of the page must check + * that the user has the required capability as well. + * + * @param string $page_title The text to be displayed in the title tags of the page when the menu is selected + * @param string $menu_title The text to be used for the menu + * @param string $capability The capability required for this menu to be displayed to the user. + * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu) + * @param callback $function The function to be called to output the content for this page. + * + * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required. + */ +function add_theme_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) { + return add_submenu_page( 'themes.php', $page_title, $menu_title, $capability, $menu_slug, $function ); +} + +/** + * Add sub menu page to the plugins main menu. + * + * This function takes a capability which will be used to determine whether + * or not a page is included in the menu. + * + * The function which is hooked in to handle the output of the page must check + * that the user has the required capability as well. + * + * @param string $page_title The text to be displayed in the title tags of the page when the menu is selected + * @param string $menu_title The text to be used for the menu + * @param string $capability The capability required for this menu to be displayed to the user. + * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu) + * @param callback $function The function to be called to output the content for this page. + * + * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required. + */ +function add_plugins_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) { + return add_submenu_page( 'plugins.php', $page_title, $menu_title, $capability, $menu_slug, $function ); +} + +/** + * Add sub menu page to the Users/Profile main menu. + * + * This function takes a capability which will be used to determine whether + * or not a page is included in the menu. + * + * The function which is hooked in to handle the output of the page must check + * that the user has the required capability as well. + * + * @param string $page_title The text to be displayed in the title tags of the page when the menu is selected + * @param string $menu_title The text to be used for the menu + * @param string $capability The capability required for this menu to be displayed to the user. + * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu) + * @param callback $function The function to be called to output the content for this page. + * + * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required. + */ +function add_users_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) { + if ( current_user_can('edit_users') ) + $parent = 'users.php'; + else + $parent = 'profile.php'; + return add_submenu_page( $parent, $page_title, $menu_title, $capability, $menu_slug, $function ); +} +/** + * Add sub menu page to the Dashboard main menu. + * + * This function takes a capability which will be used to determine whether + * or not a page is included in the menu. + * + * The function which is hooked in to handle the output of the page must check + * that the user has the required capability as well. + * + * @param string $page_title The text to be displayed in the title tags of the page when the menu is selected + * @param string $menu_title The text to be used for the menu + * @param string $capability The capability required for this menu to be displayed to the user. + * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu) + * @param callback $function The function to be called to output the content for this page. + * + * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required. + */ +function add_dashboard_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) { + return add_submenu_page( 'index.php', $page_title, $menu_title, $capability, $menu_slug, $function ); +} + +/** + * Add sub menu page to the posts main menu. + * + * This function takes a capability which will be used to determine whether + * or not a page is included in the menu. + * + * The function which is hooked in to handle the output of the page must check + * that the user has the required capability as well. + * + * @param string $page_title The text to be displayed in the title tags of the page when the menu is selected + * @param string $menu_title The text to be used for the menu + * @param string $capability The capability required for this menu to be displayed to the user. + * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu) + * @param callback $function The function to be called to output the content for this page. + * + * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required. + */ +function add_posts_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) { + return add_submenu_page( 'edit.php', $page_title, $menu_title, $capability, $menu_slug, $function ); +} + +/** + * Add sub menu page to the media main menu. + * + * This function takes a capability which will be used to determine whether + * or not a page is included in the menu. + * + * The function which is hooked in to handle the output of the page must check + * that the user has the required capability as well. + * + * @param string $page_title The text to be displayed in the title tags of the page when the menu is selected + * @param string $menu_title The text to be used for the menu + * @param string $capability The capability required for this menu to be displayed to the user. + * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu) + * @param callback $function The function to be called to output the content for this page. + * + * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required. + */ +function add_media_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) { + return add_submenu_page( 'upload.php', $page_title, $menu_title, $capability, $menu_slug, $function ); +} + +/** + * Add sub menu page to the links main menu. + * + * This function takes a capability which will be used to determine whether + * or not a page is included in the menu. + * + * The function which is hooked in to handle the output of the page must check + * that the user has the required capability as well. + * + * @param string $page_title The text to be displayed in the title tags of the page when the menu is selected + * @param string $menu_title The text to be used for the menu + * @param string $capability The capability required for this menu to be displayed to the user. + * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu) + * @param callback $function The function to be called to output the content for this page. + * + * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required. + */ +function add_links_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) { + return add_submenu_page( 'link-manager.php', $page_title, $menu_title, $capability, $menu_slug, $function ); +} + +/** + * Add sub menu page to the pages main menu. + * + * This function takes a capability which will be used to determine whether + * or not a page is included in the menu. + * + * The function which is hooked in to handle the output of the page must check + * that the user has the required capability as well. + * + * @param string $page_title The text to be displayed in the title tags of the page when the menu is selected + * @param string $menu_title The text to be used for the menu + * @param string $capability The capability required for this menu to be displayed to the user. + * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu) + * @param callback $function The function to be called to output the content for this page. + * + * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required. +*/ +function add_pages_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) { + return add_submenu_page( 'edit.php?post_type=page', $page_title, $menu_title, $capability, $menu_slug, $function ); +} + +/** + * Add sub menu page to the comments main menu. + * + * This function takes a capability which will be used to determine whether + * or not a page is included in the menu. + * + * The function which is hooked in to handle the output of the page must check + * that the user has the required capability as well. + * + * @param string $page_title The text to be displayed in the title tags of the page when the menu is selected + * @param string $menu_title The text to be used for the menu + * @param string $capability The capability required for this menu to be displayed to the user. + * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu) + * @param callback $function The function to be called to output the content for this page. + * + * @return false|string The resulting page's hook_suffix, or false if the user does not have the capability required. +*/ +function add_comments_page( $page_title, $menu_title, $capability, $menu_slug, $function = '' ) { + return add_submenu_page( 'edit-comments.php', $page_title, $menu_title, $capability, $menu_slug, $function ); +} + +/** + * Remove a top level admin menu + * + * @since 3.1.0 + * + * @param string $menu_slug The slug of the menu + * @return array|bool The removed menu on success, False if not found + */ +function remove_menu_page( $menu_slug ) { + global $menu; + + foreach ( $menu as $i => $item ) { + if ( $menu_slug == $item[2] ) { + unset( $menu[$i] ); + return $item; + } + } + + return false; +} + +/** + * Remove an admin submenu + * + * @since 3.1.0 + * + * @param string $menu_slug The slug for the parent menu + * @param string $submenu_slug The slug of the submenu + * @return array|bool The removed submenu on success, False if not found + */ +function remove_submenu_page( $menu_slug, $submenu_slug ) { + global $submenu; + + if ( !isset( $submenu[$menu_slug] ) ) + return false; + + foreach ( $submenu[$menu_slug] as $i => $item ) { + if ( $submenu_slug == $item[2] ) { + unset( $submenu[$menu_slug][$i] ); + return $item; + } + } + + return false; +} + +/** + * Get the url to access a particular menu page based on the slug it was registered with. + * + * If the slug hasn't been registered properly no url will be returned + * + * @since 3.0.0 + * + * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu) + * @param bool $echo Whether or not to echo the url - default is true + * @return string the url + */ +function menu_page_url($menu_slug, $echo = true) { + global $_parent_pages; + + if ( isset( $_parent_pages[$menu_slug] ) ) { + $parent_slug = $_parent_pages[$menu_slug]; + if ( $parent_slug && ! isset( $_parent_pages[$parent_slug] ) ) { + $url = admin_url( add_query_arg( 'page', $menu_slug, $parent_slug ) ); + } else { + $url = admin_url( 'admin.php?page=' . $menu_slug ); + } + } else { + $url = ''; + } + + $url = esc_url($url); + + if ( $echo ) + echo $url; + + return $url; +} + +// +// Pluggable Menu Support -- Private +// + +function get_admin_page_parent( $parent = '' ) { + global $parent_file; + global $menu; + global $submenu; + global $pagenow; + global $typenow; + global $plugin_page; + global $_wp_real_parent_file; + global $_wp_menu_nopriv; + global $_wp_submenu_nopriv; + + if ( !empty ( $parent ) && 'admin.php' != $parent ) { + if ( isset( $_wp_real_parent_file[$parent] ) ) + $parent = $_wp_real_parent_file[$parent]; + return $parent; + } + + if ( $pagenow == 'admin.php' && isset( $plugin_page ) ) { + foreach ( (array)$menu as $parent_menu ) { + if ( $parent_menu[2] == $plugin_page ) { + $parent_file = $plugin_page; + if ( isset( $_wp_real_parent_file[$parent_file] ) ) + $parent_file = $_wp_real_parent_file[$parent_file]; + return $parent_file; + } + } + if ( isset( $_wp_menu_nopriv[$plugin_page] ) ) { + $parent_file = $plugin_page; + if ( isset( $_wp_real_parent_file[$parent_file] ) ) + $parent_file = $_wp_real_parent_file[$parent_file]; + return $parent_file; + } + } + + if ( isset( $plugin_page ) && isset( $_wp_submenu_nopriv[$pagenow][$plugin_page] ) ) { + $parent_file = $pagenow; + if ( isset( $_wp_real_parent_file[$parent_file] ) ) + $parent_file = $_wp_real_parent_file[$parent_file]; + return $parent_file; + } + + foreach (array_keys( (array)$submenu ) as $parent) { + foreach ( $submenu[$parent] as $submenu_array ) { + if ( isset( $_wp_real_parent_file[$parent] ) ) + $parent = $_wp_real_parent_file[$parent]; + if ( !empty($typenow) && ($submenu_array[2] == "$pagenow?post_type=$typenow") ) { + $parent_file = $parent; + return $parent; + } elseif ( $submenu_array[2] == $pagenow && empty($typenow) && ( empty($parent_file) || false === strpos($parent_file, '?') ) ) { + $parent_file = $parent; + return $parent; + } else + if ( isset( $plugin_page ) && ($plugin_page == $submenu_array[2] ) ) { + $parent_file = $parent; + return $parent; + } + } + } + + if ( empty($parent_file) ) + $parent_file = ''; + return ''; +} + +function get_admin_page_title() { + global $title; + global $menu; + global $submenu; + global $pagenow; + global $plugin_page; + global $typenow; + + if ( ! empty ( $title ) ) + return $title; + + $hook = get_plugin_page_hook( $plugin_page, $pagenow ); + + $parent = $parent1 = get_admin_page_parent(); + + if ( empty ( $parent) ) { + foreach ( (array)$menu as $menu_array ) { + if ( isset( $menu_array[3] ) ) { + if ( $menu_array[2] == $pagenow ) { + $title = $menu_array[3]; + return $menu_array[3]; + } else + if ( isset( $plugin_page ) && ($plugin_page == $menu_array[2] ) && ($hook == $menu_array[3] ) ) { + $title = $menu_array[3]; + return $menu_array[3]; + } + } else { + $title = $menu_array[0]; + return $title; + } + } + } else { + foreach ( array_keys( $submenu ) as $parent ) { + foreach ( $submenu[$parent] as $submenu_array ) { + if ( isset( $plugin_page ) && + ( $plugin_page == $submenu_array[2] ) && + ( + ( $parent == $pagenow ) || + ( $parent == $plugin_page ) || + ( $plugin_page == $hook ) || + ( $pagenow == 'admin.php' && $parent1 != $submenu_array[2] ) || + ( !empty($typenow) && $parent == $pagenow . '?post_type=' . $typenow) + ) + ) { + $title = $submenu_array[3]; + return $submenu_array[3]; + } + + if ( $submenu_array[2] != $pagenow || isset( $_GET['page'] ) ) // not the current page + continue; + + if ( isset( $submenu_array[3] ) ) { + $title = $submenu_array[3]; + return $submenu_array[3]; + } else { + $title = $submenu_array[0]; + return $title; + } + } + } + if ( empty ( $title ) ) { + foreach ( $menu as $menu_array ) { + if ( isset( $plugin_page ) && + ( $plugin_page == $menu_array[2] ) && + ( $pagenow == 'admin.php' ) && + ( $parent1 == $menu_array[2] ) ) + { + $title = $menu_array[3]; + return $menu_array[3]; + } + } + } + } + + return $title; +} + +function get_plugin_page_hook( $plugin_page, $parent_page ) { + $hook = get_plugin_page_hookname( $plugin_page, $parent_page ); + if ( has_action($hook) ) + return $hook; + else + return null; +} + +function get_plugin_page_hookname( $plugin_page, $parent_page ) { + global $admin_page_hooks; + + $parent = get_admin_page_parent( $parent_page ); + + $page_type = 'admin'; + if ( empty ( $parent_page ) || 'admin.php' == $parent_page || isset( $admin_page_hooks[$plugin_page] ) ) { + if ( isset( $admin_page_hooks[$plugin_page] ) ) + $page_type = 'toplevel'; + else + if ( isset( $admin_page_hooks[$parent] )) + $page_type = $admin_page_hooks[$parent]; + } else if ( isset( $admin_page_hooks[$parent] ) ) { + $page_type = $admin_page_hooks[$parent]; + } + + $plugin_name = preg_replace( '!\.php!', '', $plugin_page ); + + return $page_type . '_page_' . $plugin_name; +} + +function user_can_access_admin_page() { + global $pagenow; + global $menu; + global $submenu; + global $_wp_menu_nopriv; + global $_wp_submenu_nopriv; + global $plugin_page; + global $_registered_pages; + + $parent = get_admin_page_parent(); + + if ( !isset( $plugin_page ) && isset( $_wp_submenu_nopriv[$parent][$pagenow] ) ) + return false; + + if ( isset( $plugin_page ) ) { + if ( isset( $_wp_submenu_nopriv[$parent][$plugin_page] ) ) + return false; + + $hookname = get_plugin_page_hookname($plugin_page, $parent); + + if ( !isset($_registered_pages[$hookname]) ) + return false; + } + + if ( empty( $parent) ) { + if ( isset( $_wp_menu_nopriv[$pagenow] ) ) + return false; + if ( isset( $_wp_submenu_nopriv[$pagenow][$pagenow] ) ) + return false; + if ( isset( $plugin_page ) && isset( $_wp_submenu_nopriv[$pagenow][$plugin_page] ) ) + return false; + if ( isset( $plugin_page ) && isset( $_wp_menu_nopriv[$plugin_page] ) ) + return false; + foreach (array_keys( $_wp_submenu_nopriv ) as $key ) { + if ( isset( $_wp_submenu_nopriv[$key][$pagenow] ) ) + return false; + if ( isset( $plugin_page ) && isset( $_wp_submenu_nopriv[$key][$plugin_page] ) ) + return false; + } + return true; + } + + if ( isset( $plugin_page ) && ( $plugin_page == $parent ) && isset( $_wp_menu_nopriv[$plugin_page] ) ) + return false; + + if ( isset( $submenu[$parent] ) ) { + foreach ( $submenu[$parent] as $submenu_array ) { + if ( isset( $plugin_page ) && ( $submenu_array[2] == $plugin_page ) ) { + if ( current_user_can( $submenu_array[1] )) + return true; + else + return false; + } else if ( $submenu_array[2] == $pagenow ) { + if ( current_user_can( $submenu_array[1] )) + return true; + else + return false; + } + } + } + + foreach ( $menu as $menu_array ) { + if ( $menu_array[2] == $parent) { + if ( current_user_can( $menu_array[1] )) + return true; + else + return false; + } + } + + return true; +} + +/* Whitelist functions */ + +/** + * Register a setting and its sanitization callback + * + * @since 2.7.0 + * + * @param string $option_group A settings group name. Should correspond to a whitelisted option key name. + * Default whitelisted option key names include "general," "discussion," and "reading," among others. + * @param string $option_name The name of an option to sanitize and save. + * @param callable $sanitize_callback A callback function that sanitizes the option's value. + */ +function register_setting( $option_group, $option_name, $sanitize_callback = '' ) { + global $new_whitelist_options; + + if ( 'misc' == $option_group ) { + _deprecated_argument( __FUNCTION__, '3.0', sprintf( __( 'The "%s" options group has been removed. Use another settings group.' ), 'misc' ) ); + $option_group = 'general'; + } + + if ( 'privacy' == $option_group ) { + _deprecated_argument( __FUNCTION__, '3.5', sprintf( __( 'The "%s" options group has been removed. Use another settings group.' ), 'privacy' ) ); + $option_group = 'reading'; + } + + $new_whitelist_options[ $option_group ][] = $option_name; + if ( $sanitize_callback != '' ) + add_filter( "sanitize_option_{$option_name}", $sanitize_callback ); +} + +/** + * Unregister a setting + * + * @since 2.7.0 + * + * @param string $option_group + * @param string $option_name + * @param callable $sanitize_callback + */ +function unregister_setting( $option_group, $option_name, $sanitize_callback = '' ) { + global $new_whitelist_options; + + if ( 'misc' == $option_group ) { + _deprecated_argument( __FUNCTION__, '3.0', sprintf( __( 'The "%s" options group has been removed. Use another settings group.' ), 'misc' ) ); + $option_group = 'general'; + } + + if ( 'privacy' == $option_group ) { + _deprecated_argument( __FUNCTION__, '3.5', sprintf( __( 'The "%s" options group has been removed. Use another settings group.' ), 'privacy' ) ); + $option_group = 'reading'; + } + + $pos = array_search( $option_name, (array) $new_whitelist_options ); + if ( $pos !== false ) + unset( $new_whitelist_options[ $option_group ][ $pos ] ); + if ( $sanitize_callback != '' ) + remove_filter( "sanitize_option_{$option_name}", $sanitize_callback ); +} + +/** + * {@internal Missing Short Description}} + * + * @since 2.7.0 + * + * @param array $options + * @return array + */ +function option_update_filter( $options ) { + global $new_whitelist_options; + + if ( is_array( $new_whitelist_options ) ) + $options = add_option_whitelist( $new_whitelist_options, $options ); + + return $options; +} +add_filter( 'whitelist_options', 'option_update_filter' ); + +/** + * {@internal Missing Short Description}} + * + * @since 2.7.0 + * + * @param array $new_options + * @param string|array $options + * @return array + */ +function add_option_whitelist( $new_options, $options = '' ) { + if ( $options == '' ) + global $whitelist_options; + else + $whitelist_options = $options; + + foreach ( $new_options as $page => $keys ) { + foreach ( $keys as $key ) { + if ( !isset($whitelist_options[ $page ]) || !is_array($whitelist_options[ $page ]) ) { + $whitelist_options[ $page ] = array(); + $whitelist_options[ $page ][] = $key; + } else { + $pos = array_search( $key, $whitelist_options[ $page ] ); + if ( $pos === false ) + $whitelist_options[ $page ][] = $key; + } + } + } + + return $whitelist_options; +} + +/** + * {@internal Missing Short Description}} + * + * @since 2.7.0 + * + * @param array $del_options + * @param string|array $options + * @return array + */ +function remove_option_whitelist( $del_options, $options = '' ) { + if ( $options == '' ) + global $whitelist_options; + else + $whitelist_options = $options; + + foreach ( $del_options as $page => $keys ) { + foreach ( $keys as $key ) { + if ( isset($whitelist_options[ $page ]) && is_array($whitelist_options[ $page ]) ) { + $pos = array_search( $key, $whitelist_options[ $page ] ); + if ( $pos !== false ) + unset( $whitelist_options[ $page ][ $pos ] ); + } + } + } + + return $whitelist_options; +} + +/** + * Output nonce, action, and option_page fields for a settings page. + * + * @since 2.7.0 + * + * @param string $option_group A settings group name. This should match the group name used in register_setting(). + */ +function settings_fields($option_group) { + echo ""; + echo ''; + wp_nonce_field("$option_group-options"); +} + +/** + * Clears the Plugins cache used by get_plugins() and by default, the Plugin Update cache. + * + * @since 3.7.0 + * + * @param bool $clear_update_cache Whether to clear the Plugin updates cache + */ +function wp_clean_plugins_cache( $clear_update_cache = true ) { + if ( $clear_update_cache ) + delete_site_transient( 'update_plugins' ); + wp_cache_delete( 'plugins', 'plugins' ); +} diff --git a/wp-admin/includes/post.php b/wp-admin/includes/post.php new file mode 100644 index 0000000..4906030 --- /dev/null +++ b/wp-admin/includes/post.php @@ -0,0 +1,1673 @@ +cap->create_posts ) ) { + if ( 'page' == $post_data['post_type'] ) + return new WP_Error( 'edit_others_pages', __( 'You are not allowed to create pages as this user.' ) ); + else + return new WP_Error( 'edit_others_posts', __( 'You are not allowed to create posts as this user.' ) ); + } + + if ( isset( $post_data['content'] ) ) + $post_data['post_content'] = $post_data['content']; + + if ( isset( $post_data['excerpt'] ) ) + $post_data['post_excerpt'] = $post_data['excerpt']; + + if ( isset( $post_data['parent_id'] ) ) + $post_data['post_parent'] = (int) $post_data['parent_id']; + + if ( isset($post_data['trackback_url']) ) + $post_data['to_ping'] = $post_data['trackback_url']; + + $post_data['user_ID'] = get_current_user_id(); + + if (!empty ( $post_data['post_author_override'] ) ) { + $post_data['post_author'] = (int) $post_data['post_author_override']; + } else { + if (!empty ( $post_data['post_author'] ) ) { + $post_data['post_author'] = (int) $post_data['post_author']; + } else { + $post_data['post_author'] = (int) $post_data['user_ID']; + } + } + + if ( isset( $post_data['user_ID'] ) && ( $post_data['post_author'] != $post_data['user_ID'] ) + && ! current_user_can( $ptype->cap->edit_others_posts ) ) { + if ( $update ) { + if ( 'page' == $post_data['post_type'] ) + return new WP_Error( 'edit_others_pages', __( 'You are not allowed to edit pages as this user.' ) ); + else + return new WP_Error( 'edit_others_posts', __( 'You are not allowed to edit posts as this user.' ) ); + } else { + if ( 'page' == $post_data['post_type'] ) + return new WP_Error( 'edit_others_pages', __( 'You are not allowed to create pages as this user.' ) ); + else + return new WP_Error( 'edit_others_posts', __( 'You are not allowed to create posts as this user.' ) ); + } + } + + if ( ! empty( $post_data['post_status'] ) ) { + $post_data['post_status'] = sanitize_key( $post_data['post_status'] ); + + // No longer an auto-draft + if ( 'auto-draft' === $post_data['post_status'] ) { + $post_data['post_status'] = 'draft'; + } + + if ( ! get_post_status_object( $post_data['post_status'] ) ) { + unset( $post_data['post_status'] ); + } + } + + // What to do based on which button they pressed + if ( isset($post_data['saveasdraft']) && '' != $post_data['saveasdraft'] ) + $post_data['post_status'] = 'draft'; + if ( isset($post_data['saveasprivate']) && '' != $post_data['saveasprivate'] ) + $post_data['post_status'] = 'private'; + if ( isset($post_data['publish']) && ( '' != $post_data['publish'] ) && ( !isset($post_data['post_status']) || $post_data['post_status'] != 'private' ) ) + $post_data['post_status'] = 'publish'; + if ( isset($post_data['advanced']) && '' != $post_data['advanced'] ) + $post_data['post_status'] = 'draft'; + if ( isset($post_data['pending']) && '' != $post_data['pending'] ) + $post_data['post_status'] = 'pending'; + + if ( isset( $post_data['ID'] ) ) + $post_id = $post_data['ID']; + else + $post_id = false; + $previous_status = $post_id ? get_post_field( 'post_status', $post_id ) : false; + + if ( isset( $post_data['post_status'] ) && 'private' == $post_data['post_status'] && ! current_user_can( $ptype->cap->publish_posts ) ) { + $post_data['post_status'] = $previous_status ? $previous_status : 'pending'; + } + + $published_statuses = array( 'publish', 'future' ); + + // Posts 'submitted for approval' present are submitted to $_POST the same as if they were being published. + // Change status from 'publish' to 'pending' if user lacks permissions to publish or to resave published posts. + if ( isset($post_data['post_status']) && (in_array( $post_data['post_status'], $published_statuses ) && !current_user_can( $ptype->cap->publish_posts )) ) + if ( ! in_array( $previous_status, $published_statuses ) || !current_user_can( 'edit_post', $post_id ) ) + $post_data['post_status'] = 'pending'; + + if ( ! isset( $post_data['post_status'] ) ) { + $post_data['post_status'] = 'auto-draft' === $previous_status ? 'draft' : $previous_status; + } + + if ( isset( $post_data['post_password'] ) && ! current_user_can( $ptype->cap->publish_posts ) ) { + unset( $post_data['post_password'] ); + } + + if (!isset( $post_data['comment_status'] )) + $post_data['comment_status'] = 'closed'; + + if (!isset( $post_data['ping_status'] )) + $post_data['ping_status'] = 'closed'; + + foreach ( array('aa', 'mm', 'jj', 'hh', 'mn') as $timeunit ) { + if ( !empty( $post_data['hidden_' . $timeunit] ) && $post_data['hidden_' . $timeunit] != $post_data[$timeunit] ) { + $post_data['edit_date'] = '1'; + break; + } + } + + if ( !empty( $post_data['edit_date'] ) ) { + $aa = $post_data['aa']; + $mm = $post_data['mm']; + $jj = $post_data['jj']; + $hh = $post_data['hh']; + $mn = $post_data['mn']; + $ss = $post_data['ss']; + $aa = ($aa <= 0 ) ? date('Y') : $aa; + $mm = ($mm <= 0 ) ? date('n') : $mm; + $jj = ($jj > 31 ) ? 31 : $jj; + $jj = ($jj <= 0 ) ? date('j') : $jj; + $hh = ($hh > 23 ) ? $hh -24 : $hh; + $mn = ($mn > 59 ) ? $mn -60 : $mn; + $ss = ($ss > 59 ) ? $ss -60 : $ss; + $post_data['post_date'] = sprintf( "%04d-%02d-%02d %02d:%02d:%02d", $aa, $mm, $jj, $hh, $mn, $ss ); + $valid_date = wp_checkdate( $mm, $jj, $aa, $post_data['post_date'] ); + if ( !$valid_date ) { + return new WP_Error( 'invalid_date', __( 'Whoops, the provided date is invalid.' ) ); + } + $post_data['post_date_gmt'] = get_gmt_from_date( $post_data['post_date'] ); + } + + return $post_data; +} + +/** + * Update an existing post with values provided in $_POST. + * + * @since 1.5.0 + * + * @param array $post_data Optional. + * @return int Post ID. + */ +function edit_post( $post_data = null ) { + global $wpdb; + + if ( empty($post_data) ) + $post_data = &$_POST; + + // Clear out any data in internal vars. + unset( $post_data['filter'] ); + + $post_ID = (int) $post_data['post_ID']; + $post = get_post( $post_ID ); + $post_data['post_type'] = $post->post_type; + $post_data['post_mime_type'] = $post->post_mime_type; + + if ( ! empty( $post_data['post_status'] ) ) { + $post_data['post_status'] = sanitize_key( $post_data['post_status'] ); + + if ( 'inherit' == $post_data['post_status'] ) { + unset( $post_data['post_status'] ); + } + } + + $ptype = get_post_type_object($post_data['post_type']); + if ( !current_user_can( 'edit_post', $post_ID ) ) { + if ( 'page' == $post_data['post_type'] ) + wp_die( __('You are not allowed to edit this page.' )); + else + wp_die( __('You are not allowed to edit this post.' )); + } + + if ( post_type_supports( $ptype->name, 'revisions' ) ) { + $revisions = wp_get_post_revisions( $post_ID, array( 'order' => 'ASC', 'posts_per_page' => 1 ) ); + $revision = current( $revisions ); + + // Check if the revisions have been upgraded + if ( $revisions && _wp_get_post_revision_version( $revision ) < 1 ) + _wp_upgrade_revisions_of_post( $post, wp_get_post_revisions( $post_ID ) ); + } + + if ( isset($post_data['visibility']) ) { + switch ( $post_data['visibility'] ) { + case 'public' : + $post_data['post_password'] = ''; + break; + case 'password' : + unset( $post_data['sticky'] ); + break; + case 'private' : + $post_data['post_status'] = 'private'; + $post_data['post_password'] = ''; + unset( $post_data['sticky'] ); + break; + } + } + + $post_data = _wp_translate_postdata( true, $post_data ); + if ( is_wp_error($post_data) ) + wp_die( $post_data->get_error_message() ); + + // Post Formats + if ( isset( $post_data['post_format'] ) ) + set_post_format( $post_ID, $post_data['post_format'] ); + + $format_meta_urls = array( 'url', 'link_url', 'quote_source_url' ); + foreach ( $format_meta_urls as $format_meta_url ) { + $keyed = '_format_' . $format_meta_url; + if ( isset( $post_data[ $keyed ] ) ) + update_post_meta( $post_ID, $keyed, wp_slash( esc_url_raw( wp_unslash( $post_data[ $keyed ] ) ) ) ); + } + + $format_keys = array( 'quote', 'quote_source_name', 'image', 'gallery', 'audio_embed', 'video_embed' ); + + foreach ( $format_keys as $key ) { + $keyed = '_format_' . $key; + if ( isset( $post_data[ $keyed ] ) ) { + if ( current_user_can( 'unfiltered_html' ) ) + update_post_meta( $post_ID, $keyed, $post_data[ $keyed ] ); + else + update_post_meta( $post_ID, $keyed, wp_filter_post_kses( $post_data[ $keyed ] ) ); + } + } + + if ( 'attachment' === $post_data['post_type'] && preg_match( '#^(audio|video)/#', $post_data['post_mime_type'] ) ) { + $id3data = wp_get_attachment_metadata( $post_ID ); + if ( ! is_array( $id3data ) ) { + $id3data = array(); + } + + foreach ( wp_get_attachment_id3_keys( $post, 'edit' ) as $key => $label ) { + if ( isset( $post_data[ 'id3_' . $key ] ) ) { + $id3data[ $key ] = sanitize_text_field( wp_unslash( $post_data[ 'id3_' . $key ] ) ); + } + } + wp_update_attachment_metadata( $post_ID, $id3data ); + } + + // Meta Stuff + if ( isset($post_data['meta']) && $post_data['meta'] ) { + foreach ( $post_data['meta'] as $key => $value ) { + if ( !$meta = get_post_meta_by_id( $key ) ) + continue; + if ( $meta->post_id != $post_ID ) + continue; + if ( is_protected_meta( $value['key'], 'post' ) || ! current_user_can( 'edit_post_meta', $post_ID, $value['key'] ) ) + continue; + update_meta( $key, $value['key'], $value['value'] ); + } + } + + if ( isset($post_data['deletemeta']) && $post_data['deletemeta'] ) { + foreach ( $post_data['deletemeta'] as $key => $value ) { + if ( !$meta = get_post_meta_by_id( $key ) ) + continue; + if ( $meta->post_id != $post_ID ) + continue; + if ( is_protected_meta( $meta->meta_key, 'post' ) || ! current_user_can( 'delete_post_meta', $post_ID, $meta->meta_key ) ) + continue; + delete_meta( $key ); + } + } + + // Attachment stuff + if ( 'attachment' == $post_data['post_type'] ) { + if ( isset( $post_data[ '_wp_attachment_image_alt' ] ) ) { + $image_alt = wp_unslash( $post_data['_wp_attachment_image_alt'] ); + if ( $image_alt != get_post_meta( $post_ID, '_wp_attachment_image_alt', true ) ) { + $image_alt = wp_strip_all_tags( $image_alt, true ); + // update_meta expects slashed. + update_post_meta( $post_ID, '_wp_attachment_image_alt', wp_slash( $image_alt ) ); + } + } + + $attachment_data = isset( $post_data['attachments'][ $post_ID ] ) ? $post_data['attachments'][ $post_ID ] : array(); + + /** This filter is documented in wp-admin/includes/media.php */ + $post_data = apply_filters( 'attachment_fields_to_save', $post_data, $attachment_data ); + } + + add_meta( $post_ID ); + + update_post_meta( $post_ID, '_edit_last', get_current_user_id() ); + + $success = wp_update_post( $post_data ); + // If the save failed, see if we can sanity check the main fields and try again + if ( ! $success && is_callable( array( $wpdb, 'strip_invalid_text_for_column' ) ) ) { + $fields = array( 'post_title', 'post_content', 'post_excerpt' ); + + foreach( $fields as $field ) { + if ( isset( $post_data[ $field ] ) ) { + $post_data[ $field ] = $wpdb->strip_invalid_text_for_column( $wpdb->posts, $field, $post_data[ $field ] ); + } + } + + wp_update_post( $post_data ); + } + + // Now that we have an ID we can fix any attachment anchor hrefs + _fix_attachment_links( $post_ID ); + + wp_set_post_lock( $post_ID ); + + if ( current_user_can( $ptype->cap->edit_others_posts ) ) { + if ( ! empty( $post_data['sticky'] ) ) + stick_post( $post_ID ); + else + unstick_post( $post_ID ); + } + + return $post_ID; +} + +/** + * Process the post data for the bulk editing of posts. + * + * Updates all bulk edited posts/pages, adding (but not removing) tags and + * categories. Skips pages when they would be their own parent or child. + * + * @since 2.7.0 + * + * @param array $post_data Optional, the array of post data to process if not provided will use $_POST superglobal. + * @return array + */ +function bulk_edit_posts( $post_data = null ) { + global $wpdb; + + if ( empty($post_data) ) + $post_data = &$_POST; + + if ( isset($post_data['post_type']) ) + $ptype = get_post_type_object($post_data['post_type']); + else + $ptype = get_post_type_object('post'); + + if ( !current_user_can( $ptype->cap->edit_posts ) ) { + if ( 'page' == $ptype->name ) + wp_die( __('You are not allowed to edit pages.')); + else + wp_die( __('You are not allowed to edit posts.')); + } + + if ( -1 == $post_data['_status'] ) { + $post_data['post_status'] = null; + unset($post_data['post_status']); + } else { + $post_data['post_status'] = $post_data['_status']; + } + unset($post_data['_status']); + + if ( ! empty( $post_data['post_status'] ) ) { + $post_data['post_status'] = sanitize_key( $post_data['post_status'] ); + + if ( 'inherit' == $post_data['post_status'] ) { + unset( $post_data['post_status'] ); + } + } + + $post_IDs = array_map( 'intval', (array) $post_data['post'] ); + + $reset = array( + 'post_author', 'post_status', 'post_password', + 'post_parent', 'page_template', 'comment_status', + 'ping_status', 'keep_private', 'tax_input', + 'post_category', 'sticky', 'post_format', + ); + + foreach ( $reset as $field ) { + if ( isset($post_data[$field]) && ( '' == $post_data[$field] || -1 == $post_data[$field] ) ) + unset($post_data[$field]); + } + + if ( isset($post_data['post_category']) ) { + if ( is_array($post_data['post_category']) && ! empty($post_data['post_category']) ) + $new_cats = array_map( 'absint', $post_data['post_category'] ); + else + unset($post_data['post_category']); + } + + $tax_input = array(); + if ( isset($post_data['tax_input'])) { + foreach ( $post_data['tax_input'] as $tax_name => $terms ) { + if ( empty($terms) ) + continue; + if ( is_taxonomy_hierarchical( $tax_name ) ) { + $tax_input[ $tax_name ] = array_map( 'absint', $terms ); + } else { + $comma = _x( ',', 'tag delimiter' ); + if ( ',' !== $comma ) + $terms = str_replace( $comma, ',', $terms ); + $tax_input[ $tax_name ] = explode( ',', trim( $terms, " \n\t\r\0\x0B," ) ); + } + } + } + + if ( isset($post_data['post_parent']) && ($parent = (int) $post_data['post_parent']) ) { + $pages = $wpdb->get_results("SELECT ID, post_parent FROM $wpdb->posts WHERE post_type = 'page'"); + $children = array(); + + for ( $i = 0; $i < 50 && $parent > 0; $i++ ) { + $children[] = $parent; + + foreach ( $pages as $page ) { + if ( $page->ID == $parent ) { + $parent = $page->post_parent; + break; + } + } + } + } + + $updated = $skipped = $locked = array(); + $shared_post_data = $post_data; + + foreach ( $post_IDs as $post_ID ) { + // Start with fresh post data with each iteration. + $post_data = $shared_post_data; + + $post_type_object = get_post_type_object( get_post_type( $post_ID ) ); + + if ( !isset( $post_type_object ) || ( isset($children) && in_array($post_ID, $children) ) || !current_user_can( 'edit_post', $post_ID ) ) { + $skipped[] = $post_ID; + continue; + } + + if ( wp_check_post_lock( $post_ID ) ) { + $locked[] = $post_ID; + continue; + } + + $post = get_post( $post_ID ); + $tax_names = get_object_taxonomies( $post ); + foreach ( $tax_names as $tax_name ) { + $taxonomy_obj = get_taxonomy($tax_name); + if ( isset( $tax_input[$tax_name]) && current_user_can( $taxonomy_obj->cap->assign_terms ) ) + $new_terms = $tax_input[$tax_name]; + else + $new_terms = array(); + + if ( $taxonomy_obj->hierarchical ) + $current_terms = (array) wp_get_object_terms( $post_ID, $tax_name, array('fields' => 'ids') ); + else + $current_terms = (array) wp_get_object_terms( $post_ID, $tax_name, array('fields' => 'names') ); + + $post_data['tax_input'][$tax_name] = array_merge( $current_terms, $new_terms ); + } + + if ( isset($new_cats) && in_array( 'category', $tax_names ) ) { + $cats = (array) wp_get_post_categories($post_ID); + $post_data['post_category'] = array_unique( array_merge($cats, $new_cats) ); + unset( $post_data['tax_input']['category'] ); + } + + $post_data['post_type'] = $post->post_type; + $post_data['post_mime_type'] = $post->post_mime_type; + $post_data['guid'] = $post->guid; + + foreach ( array( 'comment_status', 'ping_status', 'post_author' ) as $field ) { + if ( ! isset( $post_data[ $field ] ) ) { + $post_data[ $field ] = $post->$field; + } + } + + $post_data['ID'] = $post_ID; + $post_data['post_ID'] = $post_ID; + + $post_data = _wp_translate_postdata( true, $post_data ); + if ( is_wp_error( $post_data ) ) { + $skipped[] = $post_ID; + continue; + } + + $updated[] = wp_update_post( $post_data ); + + if ( isset( $post_data['sticky'] ) && current_user_can( $ptype->cap->edit_others_posts ) ) { + if ( 'sticky' == $post_data['sticky'] ) + stick_post( $post_ID ); + else + unstick_post( $post_ID ); + } + + if ( isset( $post_data['post_format'] ) ) + set_post_format( $post_ID, $post_data['post_format'] ); + } + + return array( 'updated' => $updated, 'skipped' => $skipped, 'locked' => $locked ); +} + +/** + * Default post information to use when populating the "Write Post" form. + * + * @since 2.0.0 + * + * @param string $post_type A post type string, defaults to 'post'. + * @return WP_Post Post object containing all the default post data as attributes + */ +function get_default_post_to_edit( $post_type = 'post', $create_in_db = false ) { + $post_title = ''; + if ( !empty( $_REQUEST['post_title'] ) ) + $post_title = esc_html( wp_unslash( $_REQUEST['post_title'] )); + + $post_content = ''; + if ( !empty( $_REQUEST['content'] ) ) + $post_content = esc_html( wp_unslash( $_REQUEST['content'] )); + + $post_excerpt = ''; + if ( !empty( $_REQUEST['excerpt'] ) ) + $post_excerpt = esc_html( wp_unslash( $_REQUEST['excerpt'] )); + + if ( $create_in_db ) { + $post_id = wp_insert_post( array( 'post_title' => __( 'Auto Draft' ), 'post_type' => $post_type, 'post_status' => 'auto-draft' ) ); + $post = get_post( $post_id ); + if ( current_theme_supports( 'post-formats' ) && post_type_supports( $post->post_type, 'post-formats' ) && get_option( 'default_post_format' ) ) + set_post_format( $post, get_option( 'default_post_format' ) ); + } else { + $post = new stdClass; + $post->ID = 0; + $post->post_author = ''; + $post->post_date = ''; + $post->post_date_gmt = ''; + $post->post_password = ''; + $post->post_name = ''; + $post->post_type = $post_type; + $post->post_status = 'draft'; + $post->to_ping = ''; + $post->pinged = ''; + $post->comment_status = get_option( 'default_comment_status' ); + $post->ping_status = get_option( 'default_ping_status' ); + $post->post_pingback = get_option( 'default_pingback_flag' ); + $post->post_category = get_option( 'default_category' ); + $post->page_template = 'default'; + $post->post_parent = 0; + $post->menu_order = 0; + $post = new WP_Post( $post ); + } + + /** + * Filter the default post content initially used in the "Write Post" form. + * + * @since 1.5.0 + * + * @param string $post_content Default post content. + * @param WP_Post $post Post object. + */ + $post->post_content = apply_filters( 'default_content', $post_content, $post ); + + /** + * Filter the default post title initially used in the "Write Post" form. + * + * @since 1.5.0 + * + * @param string $post_title Default post title. + * @param WP_Post $post Post object. + */ + $post->post_title = apply_filters( 'default_title', $post_title, $post ); + + /** + * Filter the default post excerpt initially used in the "Write Post" form. + * + * @since 1.5.0 + * + * @param string $post_excerpt Default post excerpt. + * @param WP_Post $post Post object. + */ + $post->post_excerpt = apply_filters( 'default_excerpt', $post_excerpt, $post ); + + return $post; +} + +/** + * Determine if a post exists based on title, content, and date + * + * @since 2.0.0 + * + * @param string $title Post title + * @param string $content Optional post content + * @param string $date Optional post date + * @return int Post ID if post exists, 0 otherwise. + */ +function post_exists($title, $content = '', $date = '') { + global $wpdb; + + $post_title = wp_unslash( sanitize_post_field( 'post_title', $title, 0, 'db' ) ); + $post_content = wp_unslash( sanitize_post_field( 'post_content', $content, 0, 'db' ) ); + $post_date = wp_unslash( sanitize_post_field( 'post_date', $date, 0, 'db' ) ); + + $query = "SELECT ID FROM $wpdb->posts WHERE 1=1"; + $args = array(); + + if ( !empty ( $date ) ) { + $query .= ' AND post_date = %s'; + $args[] = $post_date; + } + + if ( !empty ( $title ) ) { + $query .= ' AND post_title = %s'; + $args[] = $post_title; + } + + if ( !empty ( $content ) ) { + $query .= 'AND post_content = %s'; + $args[] = $post_content; + } + + if ( !empty ( $args ) ) + return (int) $wpdb->get_var( $wpdb->prepare($query, $args) ); + + return 0; +} + +/** + * Creates a new post from the "Write Post" form using $_POST information. + * + * @since 2.1.0 + * + * @return int|WP_Error + */ +function wp_write_post() { + if ( isset($_POST['post_type']) ) + $ptype = get_post_type_object($_POST['post_type']); + else + $ptype = get_post_type_object('post'); + + if ( !current_user_can( $ptype->cap->edit_posts ) ) { + if ( 'page' == $ptype->name ) + return new WP_Error( 'edit_pages', __( 'You are not allowed to create pages on this site.' ) ); + else + return new WP_Error( 'edit_posts', __( 'You are not allowed to create posts or drafts on this site.' ) ); + } + + $_POST['post_mime_type'] = ''; + + // Clear out any data in internal vars. + unset( $_POST['filter'] ); + + // Edit don't write if we have a post id. + if ( isset( $_POST['post_ID'] ) ) + return edit_post(); + + if ( isset($_POST['visibility']) ) { + switch ( $_POST['visibility'] ) { + case 'public' : + $_POST['post_password'] = ''; + break; + case 'password' : + unset( $_POST['sticky'] ); + break; + case 'private' : + $_POST['post_status'] = 'private'; + $_POST['post_password'] = ''; + unset( $_POST['sticky'] ); + break; + } + } + + $translated = _wp_translate_postdata( false ); + if ( is_wp_error($translated) ) + return $translated; + + // Create the post. + $post_ID = wp_insert_post( $_POST ); + if ( is_wp_error( $post_ID ) ) + return $post_ID; + + if ( empty($post_ID) ) + return 0; + + add_meta( $post_ID ); + + add_post_meta( $post_ID, '_edit_last', $GLOBALS['current_user']->ID ); + + // Now that we have an ID we can fix any attachment anchor hrefs + _fix_attachment_links( $post_ID ); + + wp_set_post_lock( $post_ID ); + + return $post_ID; +} + +/** + * Calls wp_write_post() and handles the errors. + * + * @since 2.0.0 + * + * @return int|null + */ +function write_post() { + $result = wp_write_post(); + if ( is_wp_error( $result ) ) + wp_die( $result->get_error_message() ); + else + return $result; +} + +// +// Post Meta +// + +/** + * {@internal Missing Short Description}} + * + * @since 1.2.0 + * + * @param int $post_ID + * @return int|bool + */ +function add_meta( $post_ID ) { + $post_ID = (int) $post_ID; + + $metakeyselect = isset($_POST['metakeyselect']) ? wp_unslash( trim( $_POST['metakeyselect'] ) ) : ''; + $metakeyinput = isset($_POST['metakeyinput']) ? wp_unslash( trim( $_POST['metakeyinput'] ) ) : ''; + $metavalue = isset($_POST['metavalue']) ? $_POST['metavalue'] : ''; + if ( is_string( $metavalue ) ) + $metavalue = trim( $metavalue ); + + if ( ('0' === $metavalue || ! empty ( $metavalue ) ) && ( ( ( '#NONE#' != $metakeyselect ) && !empty ( $metakeyselect) ) || !empty ( $metakeyinput ) ) ) { + /* + * We have a key/value pair. If both the select and the input + * for the key have data, the input takes precedence. + */ + if ( '#NONE#' != $metakeyselect ) + $metakey = $metakeyselect; + + if ( $metakeyinput ) + $metakey = $metakeyinput; // default + + if ( is_protected_meta( $metakey, 'post' ) || ! current_user_can( 'add_post_meta', $post_ID, $metakey ) ) + return false; + + $metakey = wp_slash( $metakey ); + + return add_post_meta( $post_ID, $metakey, $metavalue ); + } + + return false; +} // add_meta + +/** + * {@internal Missing Short Description}} + * + * @since 1.2.0 + * + * @param int $mid + * @return bool + */ +function delete_meta( $mid ) { + return delete_metadata_by_mid( 'post' , $mid ); +} + +/** + * Get a list of previously defined keys. + * + * @since 1.2.0 + * + * @return mixed + */ +function get_meta_keys() { + global $wpdb; + + $keys = $wpdb->get_col( " + SELECT meta_key + FROM $wpdb->postmeta + GROUP BY meta_key + ORDER BY meta_key" ); + + return $keys; +} + +/** + * {@internal Missing Short Description}} + * + * @since 2.1.0 + * + * @param int $mid + * @return object|bool + */ +function get_post_meta_by_id( $mid ) { + return get_metadata_by_mid( 'post', $mid ); +} + +/** + * {@internal Missing Short Description}} + * + * Some postmeta stuff. + * + * @since 1.2.0 + * + * @param int $postid + * @return mixed + */ +function has_meta( $postid ) { + global $wpdb; + + return $wpdb->get_results( $wpdb->prepare("SELECT meta_key, meta_value, meta_id, post_id + FROM $wpdb->postmeta WHERE post_id = %d + ORDER BY meta_key,meta_id", $postid), ARRAY_A ); +} + +/** + * {@internal Missing Short Description}} + * + * @since 1.2.0 + * + * @param int $meta_id + * @param string $meta_key Expect Slashed + * @param string $meta_value Expect Slashed + * @return bool + */ +function update_meta( $meta_id, $meta_key, $meta_value ) { + $meta_key = wp_unslash( $meta_key ); + $meta_value = wp_unslash( $meta_value ); + + return update_metadata_by_mid( 'post', $meta_id, $meta_value, $meta_key ); +} + +// +// Private +// + +/** + * Replace hrefs of attachment anchors with up-to-date permalinks. + * + * @since 2.3.0 + * @access private + * + * @param int|object $post Post ID or post object. + * @return void|int|WP_Error Void if nothing fixed. 0 or WP_Error on update failure. The post ID on update success. + */ +function _fix_attachment_links( $post ) { + $post = get_post( $post, ARRAY_A ); + $content = $post['post_content']; + + // Don't run if no pretty permalinks or post is not published, scheduled, or privately published. + if ( ! get_option( 'permalink_structure' ) || ! in_array( $post['post_status'], array( 'publish', 'future', 'private' ) ) ) + return; + + // Short if there aren't any links or no '?attachment_id=' strings (strpos cannot be zero) + if ( !strpos($content, '?attachment_id=') || !preg_match_all( '/]+)>[\s\S]+?<\/a>/', $content, $link_matches ) ) + return; + + $site_url = get_bloginfo('url'); + $site_url = substr( $site_url, (int) strpos($site_url, '://') ); // remove the http(s) + $replace = ''; + + foreach ( $link_matches[1] as $key => $value ) { + if ( !strpos($value, '?attachment_id=') || !strpos($value, 'wp-att-') + || !preg_match( '/href=(["\'])[^"\']*\?attachment_id=(\d+)[^"\']*\\1/', $value, $url_match ) + || !preg_match( '/rel=["\'][^"\']*wp-att-(\d+)/', $value, $rel_match ) ) + continue; + + $quote = $url_match[1]; // the quote (single or double) + $url_id = (int) $url_match[2]; + $rel_id = (int) $rel_match[1]; + + if ( !$url_id || !$rel_id || $url_id != $rel_id || strpos($url_match[0], $site_url) === false ) + continue; + + $link = $link_matches[0][$key]; + $replace = str_replace( $url_match[0], 'href=' . $quote . get_attachment_link( $url_id ) . $quote, $link ); + + $content = str_replace( $link, $replace, $content ); + } + + if ( $replace ) { + $post['post_content'] = $content; + // Escape data pulled from DB. + $post = add_magic_quotes($post); + + return wp_update_post($post); + } +} + +/** + * Get all the possible statuses for a post_type + * + * @since 2.5.0 + * + * @param string $type The post_type you want the statuses for + * @return array As array of all the statuses for the supplied post type + */ +function get_available_post_statuses($type = 'post') { + $stati = wp_count_posts($type); + + return array_keys(get_object_vars($stati)); +} + +/** + * Run the wp query to fetch the posts for listing on the edit posts page + * + * @since 2.5.0 + * + * @param array|bool $q Array of query variables to use to build the query or false to use $_GET superglobal. + * @return array + */ +function wp_edit_posts_query( $q = false ) { + if ( false === $q ) + $q = $_GET; + $q['m'] = isset($q['m']) ? (int) $q['m'] : 0; + $q['cat'] = isset($q['cat']) ? (int) $q['cat'] : 0; + $post_stati = get_post_stati(); + + if ( isset($q['post_type']) && in_array( $q['post_type'], get_post_types() ) ) + $post_type = $q['post_type']; + else + $post_type = 'post'; + + $avail_post_stati = get_available_post_statuses($post_type); + + if ( isset($q['post_status']) && in_array( $q['post_status'], $post_stati ) ) { + $post_status = $q['post_status']; + $perm = 'readable'; + } + + if ( isset($q['orderby']) ) + $orderby = $q['orderby']; + elseif ( isset($q['post_status']) && in_array($q['post_status'], array('pending', 'draft')) ) + $orderby = 'modified'; + + if ( isset($q['order']) ) + $order = $q['order']; + elseif ( isset($q['post_status']) && 'pending' == $q['post_status'] ) + $order = 'ASC'; + + $per_page = "edit_{$post_type}_per_page"; + $posts_per_page = (int) get_user_option( $per_page ); + if ( empty( $posts_per_page ) || $posts_per_page < 1 ) + $posts_per_page = 20; + + /** + * Filter the number of items per page to show for a specific 'per_page' type. + * + * The dynamic portion of the hook name, `$post_type`, refers to the post type. + * + * Some examples of filter hooks generated here include: 'edit_attachment_per_page', + * 'edit_post_per_page', 'edit_page_per_page', etc. + * + * @since 3.0.0 + * + * @param int $posts_per_page Number of posts to display per page for the given post + * type. Default 20. + */ + $posts_per_page = apply_filters( "edit_{$post_type}_per_page", $posts_per_page ); + + /** + * Filter the number of posts displayed per page when specifically listing "posts". + * + * @since 2.8.0 + * + * @param int $posts_per_page Number of posts to be displayed. Default 20. + * @param string $post_type The post type. + */ + $posts_per_page = apply_filters( 'edit_posts_per_page', $posts_per_page, $post_type ); + + $query = compact('post_type', 'post_status', 'perm', 'order', 'orderby', 'posts_per_page'); + + // Hierarchical types require special args. + if ( is_post_type_hierarchical( $post_type ) && !isset($orderby) ) { + $query['orderby'] = 'menu_order title'; + $query['order'] = 'asc'; + $query['posts_per_page'] = -1; + $query['posts_per_archive_page'] = -1; + } + + if ( ! empty( $q['show_sticky'] ) ) + $query['post__in'] = (array) get_option( 'sticky_posts' ); + + wp( $query ); + + return $avail_post_stati; +} + +/** + * {@internal Missing Short Description}} + * + * @since 2.5.0 + * + * @param string $type + * @return mixed + */ +function get_available_post_mime_types($type = 'attachment') { + global $wpdb; + + $types = $wpdb->get_col($wpdb->prepare("SELECT DISTINCT post_mime_type FROM $wpdb->posts WHERE post_type = %s", $type)); + return $types; +} + +/** + * Executes a query for attachments. An array of WP_Query arguments + * can be passed in, which will override the arguments set by this function. + * + * @since 2.5.0 + * + * @param array|bool $q Array of query variables to use to build the query or false to use $_GET superglobal. + * @return array + */ +function wp_edit_attachments_query( $q = false ) { + if ( false === $q ) + $q = $_GET; + + $q['m'] = isset( $q['m'] ) ? (int) $q['m'] : 0; + $q['cat'] = isset( $q['cat'] ) ? (int) $q['cat'] : 0; + $q['post_type'] = 'attachment'; + $post_type = get_post_type_object( 'attachment' ); + $states = 'inherit'; + if ( current_user_can( $post_type->cap->read_private_posts ) ) + $states .= ',private'; + + $q['post_status'] = isset( $q['status'] ) && 'trash' == $q['status'] ? 'trash' : $states; + $q['post_status'] = isset( $q['attachment-filter'] ) && 'trash' == $q['attachment-filter'] ? 'trash' : $states; + + $media_per_page = (int) get_user_option( 'upload_per_page' ); + if ( empty( $media_per_page ) || $media_per_page < 1 ) + $media_per_page = 20; + + /** + * Filter the number of items to list per page when listing media items. + * + * @since 2.9.0 + * + * @param int $media_per_page Number of media to list. Default 20. + */ + $q['posts_per_page'] = apply_filters( 'upload_per_page', $media_per_page ); + + $post_mime_types = get_post_mime_types(); + $avail_post_mime_types = get_available_post_mime_types('attachment'); + + if ( isset($q['post_mime_type']) && !array_intersect( (array) $q['post_mime_type'], array_keys($post_mime_types) ) ) + unset($q['post_mime_type']); + + foreach( array_keys( $post_mime_types ) as $type ) { + if ( isset( $q['attachment-filter'] ) && "post_mime_type:$type" == $q['attachment-filter'] ) { + $q['post_mime_type'] = $type; + break; + } + } + + if ( isset( $q['detached'] ) || ( isset( $q['attachment-filter'] ) && 'detached' == $q['attachment-filter'] ) ) { + $q['post_parent'] = 0; + } + + wp( $q ); + + return array($post_mime_types, $avail_post_mime_types); +} + +/** + * Returns the list of classes to be used by a metabox + * + * @since 2.5.0 + * + * @param string $id + * @param string $page + * @return string + */ +function postbox_classes( $id, $page ) { + if ( isset( $_GET['edit'] ) && $_GET['edit'] == $id ) { + $classes = array( '' ); + } elseif ( $closed = get_user_option('closedpostboxes_'.$page ) ) { + if ( !is_array( $closed ) ) { + $classes = array( '' ); + } else { + $classes = in_array( $id, $closed ) ? array( 'closed' ) : array( '' ); + } + } else { + $classes = array( '' ); + } + + /** + * Filter the postbox classes for a specific screen and screen ID combo. + * + * The dynamic portions of the hook name, `$page` and `$id`, refer to + * the screen and screen ID, respectively. + * + * @since 3.2.0 + * + * @param array $classes An array of postbox classes. + */ + $classes = apply_filters( "postbox_classes_{$page}_{$id}", $classes ); + return implode( ' ', $classes ); +} + +/** + * Get a sample permalink based off of the post name. + * + * @since 2.5.0 + * + * @param int $id Post ID or post object. + * @param string $title Optional. Title. Default null. + * @param string $name Optional. Name. Default null. + * @return array Array with two entries of type string. + */ +function get_sample_permalink($id, $title = null, $name = null) { + $post = get_post( $id ); + if ( ! $post ) + return array( '', '' ); + + $ptype = get_post_type_object($post->post_type); + + $original_status = $post->post_status; + $original_date = $post->post_date; + $original_name = $post->post_name; + + // Hack: get_permalink() would return ugly permalink for drafts, so we will fake that our post is published. + if ( in_array( $post->post_status, array( 'draft', 'pending' ) ) ) { + $post->post_status = 'publish'; + $post->post_name = sanitize_title($post->post_name ? $post->post_name : $post->post_title, $post->ID); + } + + // If the user wants to set a new name -- override the current one + // Note: if empty name is supplied -- use the title instead, see #6072 + if ( !is_null($name) ) + $post->post_name = sanitize_title($name ? $name : $title, $post->ID); + + $post->post_name = wp_unique_post_slug($post->post_name, $post->ID, $post->post_status, $post->post_type, $post->post_parent); + + $post->filter = 'sample'; + + $permalink = get_permalink($post, true); + + // Replace custom post_type Token with generic pagename token for ease of use. + $permalink = str_replace("%$post->post_type%", '%pagename%', $permalink); + + // Handle page hierarchy + if ( $ptype->hierarchical ) { + $uri = get_page_uri($post); + $uri = untrailingslashit($uri); + $uri = strrev( stristr( strrev( $uri ), '/' ) ); + $uri = untrailingslashit($uri); + + /** This filter is documented in wp-admin/edit-tag-form.php */ + $uri = apply_filters( 'editable_slug', $uri ); + if ( !empty($uri) ) + $uri .= '/'; + $permalink = str_replace('%pagename%', "{$uri}%pagename%", $permalink); + } + + /** This filter is documented in wp-admin/edit-tag-form.php */ + $permalink = array( $permalink, apply_filters( 'editable_slug', $post->post_name ) ); + $post->post_status = $original_status; + $post->post_date = $original_date; + $post->post_name = $original_name; + unset($post->filter); + + return $permalink; +} + +/** + * Returns the HTML of the sample permalink slug editor. + * + * @since 2.5.0 + * + * @param int $id Post ID or post object. + * @param string $new_title Optional. New title. Default null. + * @param string $new_slug Optional. New slug. Default null. + * @return string The HTML of the sample permalink slug editor. + */ +function get_sample_permalink_html( $id, $new_title = null, $new_slug = null ) { + $post = get_post( $id ); + if ( ! $post ) + return ''; + + list($permalink, $post_name) = get_sample_permalink($post->ID, $new_title, $new_slug); + + if ( current_user_can( 'read_post', $post->ID ) ) { + $ptype = get_post_type_object( $post->post_type ); + $view_post = $ptype->labels->view_item; + } + + if ( 'publish' == get_post_status( $post ) ) { + $title = __('Click to edit this part of the permalink'); + } else { + $title = __('Temporary permalink. Click to edit this part.'); + } + + if ( false === strpos( $permalink, '%postname%' ) && false === strpos( $permalink, '%pagename%' ) ) { + $return = '' . __('Permalink:') . "\n" . '' . $permalink . "\n"; + if ( '' == get_option( 'permalink_structure' ) && current_user_can( 'manage_options' ) && !( 'page' == get_option('show_on_front') && $id == get_option('page_on_front') ) ) { + $return .= '' . __('Change Permalinks') . "\n"; + } + } else { + if ( function_exists( 'mb_strlen' ) ) { + if ( mb_strlen( $post_name ) > 30 ) { + $post_name_abridged = mb_substr( $post_name, 0, 14 ) . '…' . mb_substr( $post_name, -14 ); + } else { + $post_name_abridged = $post_name; + } + } else { + if ( strlen( $post_name ) > 30 ) { + $post_name_abridged = substr( $post_name, 0, 14 ) . '…' . substr( $post_name, -14 ); + } else { + $post_name_abridged = $post_name; + } + } + + $post_name_html = '' . $post_name_abridged . ''; + $display_link = str_replace( array( '%pagename%', '%postname%' ), $post_name_html, urldecode( $permalink ) ); + + $return = '' . __( 'Permalink:' ) . "\n"; + $return .= '' . $display_link . "\n"; + $return .= '‎'; // Fix bi-directional text display defect in RTL languages. + $return .= '' . __( 'Edit' ) . "\n"; + $return .= '' . $post_name . "\n"; + } + + if ( isset( $view_post ) ) { + if( 'draft' == $post->post_status ) { + $preview_link = set_url_scheme( get_permalink( $post->ID ) ); + /** This filter is documented in wp-admin/includes/meta-boxes.php */ + $preview_link = apply_filters( 'preview_post_link', add_query_arg( 'preview', 'true', $preview_link ), $post ); + $return .= "$view_post\n"; + } else { + $return .= "$view_post\n"; + } + } + + /** + * Filter the sample permalink HTML markup. + * + * @since 2.9.0 + * + * @param string $return Sample permalink HTML markup. + * @param int|WP_Post $id Post object or ID. + * @param string $new_title New sample permalink title. + * @param string $new_slug New sample permalink slug. + */ + $return = apply_filters( 'get_sample_permalink_html', $return, $id, $new_title, $new_slug ); + + return $return; +} + +/** + * Output HTML for the post thumbnail meta-box. + * + * @since 2.9.0 + * + * @param int $thumbnail_id ID of the attachment used for thumbnail + * @param mixed $post The post ID or object associated with the thumbnail, defaults to global $post. + * @return string html + */ +function _wp_post_thumbnail_html( $thumbnail_id = null, $post = null ) { + global $content_width, $_wp_additional_image_sizes; + + $post = get_post( $post ); + + $upload_iframe_src = esc_url( get_upload_iframe_src('image', $post->ID ) ); + $set_thumbnail_link = '

    %s

    '; + $content = sprintf( $set_thumbnail_link, $upload_iframe_src, esc_html__( 'Set featured image' ) ); + + if ( $thumbnail_id && get_post( $thumbnail_id ) ) { + $old_content_width = $content_width; + $content_width = 266; + if ( !isset( $_wp_additional_image_sizes['post-thumbnail'] ) ) + $thumbnail_html = wp_get_attachment_image( $thumbnail_id, array( $content_width, $content_width ) ); + else + $thumbnail_html = wp_get_attachment_image( $thumbnail_id, 'post-thumbnail' ); + if ( !empty( $thumbnail_html ) ) { + $ajax_nonce = wp_create_nonce( 'set_post_thumbnail-' . $post->ID ); + $content = sprintf( $set_thumbnail_link, $upload_iframe_src, $thumbnail_html ); + $content .= '

    ' . esc_html__( 'Remove featured image' ) . '

    '; + } + $content_width = $old_content_width; + } + + /** + * Filter the admin post thumbnail HTML markup to return. + * + * @since 2.9.0 + * + * @param string $content Admin post thumbnail HTML markup. + * @param int $post_id Post ID. + */ + return apply_filters( 'admin_post_thumbnail_html', $content, $post->ID ); +} + +/** + * Check to see if the post is currently being edited by another user. + * + * @since 2.5.0 + * + * @param int $post_id ID of the post to check for editing + * @return integer False: not locked or locked by current user. Int: user ID of user with lock. + */ +function wp_check_post_lock( $post_id ) { + if ( !$post = get_post( $post_id ) ) + return false; + + if ( !$lock = get_post_meta( $post->ID, '_edit_lock', true ) ) + return false; + + $lock = explode( ':', $lock ); + $time = $lock[0]; + $user = isset( $lock[1] ) ? $lock[1] : get_post_meta( $post->ID, '_edit_last', true ); + + /** This filter is documented in wp-admin/includes/ajax-actions.php */ + $time_window = apply_filters( 'wp_check_post_lock_window', 150 ); + + if ( $time && $time > time() - $time_window && $user != get_current_user_id() ) + return $user; + return false; +} + +/** + * Mark the post as currently being edited by the current user + * + * @since 2.5.0 + * + * @param int $post_id ID of the post to being edited + * @return bool|array Returns false if the post doesn't exist of there is no current user, or + * an array of the lock time and the user ID. + */ +function wp_set_post_lock( $post_id ) { + if ( !$post = get_post( $post_id ) ) + return false; + if ( 0 == ($user_id = get_current_user_id()) ) + return false; + + $now = time(); + $lock = "$now:$user_id"; + + update_post_meta( $post->ID, '_edit_lock', $lock ); + return array( $now, $user_id ); +} + +/** + * Outputs the HTML for the notice to say that someone else is editing or has taken over editing of this post. + * + * @since 2.8.5 + * @return none + */ +function _admin_notice_post_locked() { + if ( ! $post = get_post() ) + return; + + $user = null; + if ( $user_id = wp_check_post_lock( $post->ID ) ) + $user = get_userdata( $user_id ); + + if ( $user ) { + + /** + * Filter whether to show the post locked dialog. + * + * Returning a falsey value to the filter will short-circuit displaying the dialog. + * + * @since 3.6.0 + * + * @param bool $display Whether to display the dialog. Default true. + * @param WP_User|bool $user WP_User object on success, false otherwise. + */ + if ( ! apply_filters( 'show_post_locked_dialog', true, $post, $user ) ) + return; + + $locked = true; + } else { + $locked = false; + } + + if ( $locked && ( $sendback = wp_get_referer() ) && + false === strpos( $sendback, 'post.php' ) && false === strpos( $sendback, 'post-new.php' ) ) { + + $sendback_text = __('Go back'); + } else { + $sendback = admin_url( 'edit.php' ); + + if ( 'post' != $post->post_type ) + $sendback = add_query_arg( 'post_type', $post->post_type, $sendback ); + + $sendback_text = get_post_type_object( $post->post_type )->labels->all_items; + } + + $hidden = $locked ? '' : ' hidden'; + + ?> +
    +
    +
    + post_type )->public ) { + $preview_link = set_url_scheme( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ); + + if ( 'publish' == $post->post_status || $user->ID != $post->post_author ) { + // Latest content is in autosave + $nonce = wp_create_nonce( 'post_preview_' . $post->ID ); + $preview_link = add_query_arg( array( 'preview_id' => $post->ID, 'preview_nonce' => $nonce ), $preview_link ); + } + } else { + $preview_link = ''; + } + + /** This filter is documented in wp-admin/includes/meta-boxes.php */ + $preview_link = apply_filters( 'preview_post_link', $preview_link, $post ); + + /** + * Filter whether to allow the post lock to be overridden. + * + * Returning a falsey value to the filter will disable the ability + * to override the post lock. + * + * @since 3.6.0 + * + * @param bool $override Whether to allow overriding post locks. Default true. + * @param WP_Post $post Post object. + * @param WP_User $user User object. + */ + $override = apply_filters( 'override_post_lock', true, $post, $user ); + $tab_last = $override ? '' : ' wp-tab-last'; + + ?> +
    +
    ID, 64 ); ?>
    +

    + display_name ) ); + ?> +

    + +

    + + + + + + +

    +
    + +
    +
    +

    +
    + + +

    + +

    +
    + +
    +
    + ID; + $new_autosave['post_author'] = $post_author; + + // If the new autosave has the same content as the post, delete the autosave. + $post = get_post( $post_id ); + $autosave_is_different = false; + foreach ( array_intersect( array_keys( $new_autosave ), array_keys( _wp_post_revision_fields() ) ) as $field ) { + if ( normalize_whitespace( $new_autosave[ $field ] ) != normalize_whitespace( $post->$field ) ) { + $autosave_is_different = true; + break; + } + } + + if ( ! $autosave_is_different ) { + wp_delete_post_revision( $old_autosave->ID ); + return 0; + } + + /** + * Fires before an autosave is stored. + * + * @since 4.1.0 + * + * @param array $new_autosave Post array - the autosave that is about to be saved. + */ + do_action( 'wp_creating_autosave', $new_autosave ); + + return wp_update_post( $new_autosave ); + } + + // _wp_put_post_revision() expects unescaped. + $post_data = wp_unslash( $post_data ); + + // Otherwise create the new autosave as a special post revision + return _wp_put_post_revision( $post_data, true ); +} + +/** + * Save draft or manually autosave for showing preview. + * + * @package WordPress + * @since 2.7.0 + * + * @return str URL to redirect to show the preview + */ +function post_preview() { + + $post_ID = (int) $_POST['post_ID']; + $_POST['ID'] = $post_ID; + + if ( ! $post = get_post( $post_ID ) ) { + wp_die( __( 'You are not allowed to edit this post.' ) ); + } + + if ( ! current_user_can( 'edit_post', $post->ID ) ) { + wp_die( __( 'You are not allowed to edit this post.' ) ); + } + + $is_autosave = false; + + if ( ! wp_check_post_lock( $post->ID ) && get_current_user_id() == $post->post_author && ( 'draft' == $post->post_status || 'auto-draft' == $post->post_status ) ) { + $saved_post_id = edit_post(); + } else { + $is_autosave = true; + + if ( 'auto-draft' == $_POST['post_status'] ) + $_POST['post_status'] = 'draft'; + + $saved_post_id = wp_create_post_autosave( $post->ID ); + } + + if ( is_wp_error( $saved_post_id ) ) + wp_die( $saved_post_id->get_error_message() ); + + $query_args = array( 'preview' => 'true' ); + + if ( $is_autosave && $saved_post_id ) { + $query_args['preview_id'] = $post->ID; + $query_args['preview_nonce'] = wp_create_nonce( 'post_preview_' . $post->ID ); + + if ( isset( $_POST['post_format'] ) ) + $query_args['post_format'] = empty( $_POST['post_format'] ) ? 'standard' : sanitize_key( $_POST['post_format'] ); + } + + $url = add_query_arg( $query_args, get_permalink( $post->ID ) ); + + /** This filter is documented in wp-admin/includes/meta-boxes.php */ + return apply_filters( 'preview_post_link', $url, $post ); +} + +/** + * Save a post submitted with XHR + * + * Intended for use with heartbeat and autosave.js + * + * @since 3.9.0 + * + * @param array $post_data Associative array of the submitted post data. + * @return mixed The value 0 or WP_Error on failure. The saved post ID on success. + * Te ID can be the draft post_id or the autosave revision post_id. + */ +function wp_autosave( $post_data ) { + // Back-compat + if ( ! defined( 'DOING_AUTOSAVE' ) ) + define( 'DOING_AUTOSAVE', true ); + + $post_id = (int) $post_data['post_id']; + $post_data['ID'] = $post_data['post_ID'] = $post_id; + + if ( false === wp_verify_nonce( $post_data['_wpnonce'], 'update-post_' . $post_id ) ) { + return new WP_Error( 'invalid_nonce', __( 'Error while saving.' ) ); + } + + $post = get_post( $post_id ); + + if ( ! current_user_can( 'edit_post', $post->ID ) ) { + return new WP_Error( 'edit_posts', __( 'You are not allowed to edit this item.' ) ); + } + + if ( 'auto-draft' == $post->post_status ) + $post_data['post_status'] = 'draft'; + + if ( $post_data['post_type'] != 'page' && ! empty( $post_data['catslist'] ) ) + $post_data['post_category'] = explode( ',', $post_data['catslist'] ); + + if ( ! wp_check_post_lock( $post->ID ) && get_current_user_id() == $post->post_author && ( 'auto-draft' == $post->post_status || 'draft' == $post->post_status ) ) { + // Drafts and auto-drafts are just overwritten by autosave for the same user if the post is not locked + return edit_post( wp_slash( $post_data ) ); + } else { + // Non drafts or other users drafts are not overwritten. The autosave is stored in a special post revision for each user. + return wp_create_post_autosave( wp_slash( $post_data ) ); + } +} diff --git a/wp-admin/includes/revision.php b/wp-admin/includes/revision.php new file mode 100644 index 0000000..ae9f93a --- /dev/null +++ b/wp-admin/includes/revision.php @@ -0,0 +1,378 @@ +post_parent !== $post->ID && $compare_from->ID !== $post->ID ) + return false; + if ( $compare_to->post_parent !== $post->ID && $compare_to->ID !== $post->ID ) + return false; + + if ( $compare_from && strtotime( $compare_from->post_date_gmt ) > strtotime( $compare_to->post_date_gmt ) ) { + $temp = $compare_from; + $compare_from = $compare_to; + $compare_to = $temp; + } + + // Add default title if title field is empty + if ( $compare_from && empty( $compare_from->post_title ) ) + $compare_from->post_title = __( '(no title)' ); + if ( empty( $compare_to->post_title ) ) + $compare_to->post_title = __( '(no title)' ); + + $return = array(); + + foreach ( _wp_post_revision_fields() as $field => $name ) { + /** + * Contextually filter a post revision field. + * + * The dynamic portion of the hook name, `$field`, corresponds to each of the post + * fields of the revision object being iterated over in a foreach statement. + * + * @since 3.6.0 + * + * @param string $compare_from->$field The current revision field to compare to or from. + * @param string $field The current revision field. + * @param WP_Post $compare_from The revision post object to compare to or from. + * @param string null The context of whether the current revision is the old + * or the new one. Values are 'to' or 'from'. + */ + $content_from = $compare_from ? apply_filters( "_wp_post_revision_field_$field", $compare_from->$field, $field, $compare_from, 'from' ) : ''; + + /** This filter is documented in wp-admin/includes/revision.php */ + $content_to = apply_filters( "_wp_post_revision_field_$field", $compare_to->$field, $field, $compare_to, 'to' ); + + $args = array( + 'show_split_view' => true + ); + + /** + * Filter revisions text diff options. + * + * Filter the options passed to {@see wp_text_diff()} when viewing a post revision. + * + * @since 4.1.0 + * + * @param array $args { + * Associative array of options to pass to {@see wp_text_diff()}. + * + * @type bool $show_split_view True for split view (two columns), false for + * un-split view (single column). Default true. + * } + * @param string $field The current revision field. + * @param WP_Post $compare_from The revision post to compare from. + * @param WP_Post $compare_to The revision post to compare to. + */ + $args = apply_filters( 'revision_text_diff_options', $args, $field, $compare_from, $compare_to ); + + $diff = wp_text_diff( $content_from, $content_to, $args ); + + if ( ! $diff && 'post_title' === $field ) { + // It's a better user experience to still show the Title, even if it didn't change. + // No, you didn't see this. + $diff = ''; + $diff .= ''; + $diff .= ''; + $diff .= '
    ' . esc_html( $compare_from->post_title ) . '' . esc_html( $compare_to->post_title ) . '
    '; + } + + if ( $diff ) { + $return[] = array( + 'id' => $field, + 'name' => $name, + 'diff' => $diff, + ); + } + } + + /** + * Filter the fields displayed in the post revision diff UI. + * + * @since 4.1.0 + * + * @param array $return Revision UI fields. Each item is an array of id, name and diff. + * @param WP_Post $compare_from The revision post to compare from. + * @param WP_Post $compare_to The revision post to compare to. + */ + return apply_filters( 'wp_get_revision_ui_diff', $return, $compare_from, $compare_to ); + +} + +/** + * Prepare revisions for JavaScript. + * + * @since 3.6.0 + * + * @param object|int $post The post object. Also accepts a post ID. + * @param int $selected_revision_id The selected revision ID. + * @param int $from Optional. The revision ID to compare from. + * + * @return array An associative array of revision data and related settings. + */ +function wp_prepare_revisions_for_js( $post, $selected_revision_id, $from = null ) { + $post = get_post( $post ); + $authors = array(); + $now_gmt = time(); + + $revisions = wp_get_post_revisions( $post->ID, array( 'order' => 'ASC', 'check_enabled' => false ) ); + // If revisions are disabled, we only want autosaves and the current post. + if ( ! wp_revisions_enabled( $post ) ) { + foreach ( $revisions as $revision_id => $revision ) { + if ( ! wp_is_post_autosave( $revision ) ) + unset( $revisions[ $revision_id ] ); + } + $revisions = array( $post->ID => $post ) + $revisions; + } + + $show_avatars = get_option( 'show_avatars' ); + + cache_users( wp_list_pluck( $revisions, 'post_author' ) ); + + $can_restore = current_user_can( 'edit_post', $post->ID ); + $current_id = false; + + foreach ( $revisions as $revision ) { + $modified = strtotime( $revision->post_modified ); + $modified_gmt = strtotime( $revision->post_modified_gmt ); + if ( $can_restore ) { + $restore_link = str_replace( '&', '&', wp_nonce_url( + add_query_arg( + array( 'revision' => $revision->ID, + 'action' => 'restore' ), + admin_url( 'revision.php' ) + ), + "restore-post_{$revision->ID}" + ) ); + } + + if ( ! isset( $authors[ $revision->post_author ] ) ) { + $authors[ $revision->post_author ] = array( + 'id' => (int) $revision->post_author, + 'avatar' => $show_avatars ? get_avatar( $revision->post_author, 32 ) : '', + 'name' => get_the_author_meta( 'display_name', $revision->post_author ), + ); + } + + $autosave = (bool) wp_is_post_autosave( $revision ); + $current = ! $autosave && $revision->post_modified_gmt === $post->post_modified_gmt; + if ( $current && ! empty( $current_id ) ) { + // If multiple revisions have the same post_modified_gmt, highest ID is current. + if ( $current_id < $revision->ID ) { + $revisions[ $current_id ]['current'] = false; + $current_id = $revision->ID; + } else { + $current = false; + } + } elseif ( $current ) { + $current_id = $revision->ID; + } + + $revisions[ $revision->ID ] = array( + 'id' => $revision->ID, + 'title' => get_the_title( $post->ID ), + 'author' => $authors[ $revision->post_author ], + 'date' => date_i18n( __( 'M j, Y @ G:i' ), $modified ), + 'dateShort' => date_i18n( _x( 'j M @ G:i', 'revision date short format' ), $modified ), + 'timeAgo' => sprintf( __( '%s ago' ), human_time_diff( $modified_gmt, $now_gmt ) ), + 'autosave' => $autosave, + 'current' => $current, + 'restoreUrl' => $can_restore ? $restore_link : false, + ); + } + + /** + * If we only have one revision, the initial revision is missing; This happens + * when we have an autsosave and the user has clicked 'View the Autosave' + */ + if ( 1 === sizeof( $revisions ) ) { + $revisions[ $post->ID ] = array( + 'id' => $post->ID, + 'title' => get_the_title( $post->ID ), + 'author' => $authors[ $post->post_author ], + 'date' => date_i18n( __( 'M j, Y @ G:i' ), strtotime( $post->modified ) ), + 'dateShort' => date_i18n( _x( 'j M @ G:i', 'revision date short format' ), strtotime( $post->modified ) ), + 'timeAgo' => sprintf( __( '%s ago' ), human_time_diff( strtotime( $post->post_modified_gmt ), $now_gmt ) ), + 'autosave' => false, + 'current' => true, + 'restoreUrl' => false, + ); + $current_id = $post->ID; + } + + /* + * If a post has been saved since the last revision (no revisioned fields + * were changed), we may not have a "current" revision. Mark the latest + * revision as "current". + */ + if ( empty( $current_id ) ) { + if ( $revisions[ $revision->ID ]['autosave'] ) { + $revision = end( $revisions ); + while ( $revision['autosave'] ) { + $revision = prev( $revisions ); + } + $current_id = $revision['id']; + } else { + $current_id = $revision->ID; + } + $revisions[ $current_id ]['current'] = true; + } + + // Now, grab the initial diff. + $compare_two_mode = is_numeric( $from ); + if ( ! $compare_two_mode ) { + $found = array_search( $selected_revision_id, array_keys( $revisions ) ); + if ( $found ) { + $from = array_keys( array_slice( $revisions, $found - 1, 1, true ) ); + $from = reset( $from ); + } else { + $from = 0; + } + } + + $from = absint( $from ); + + $diffs = array( array( + 'id' => $from . ':' . $selected_revision_id, + 'fields' => wp_get_revision_ui_diff( $post->ID, $from, $selected_revision_id ), + )); + + return array( + 'postId' => $post->ID, + 'nonce' => wp_create_nonce( 'revisions-ajax-nonce' ), + 'revisionData' => array_values( $revisions ), + 'to' => $selected_revision_id, + 'from' => $from, + 'diffData' => $diffs, + 'baseUrl' => parse_url( admin_url( 'revision.php' ), PHP_URL_PATH ), + 'compareTwoMode' => absint( $compare_two_mode ), // Apparently booleans are not allowed + 'revisionIds' => array_keys( $revisions ), + ); +} + +/** + * Print JavaScript templates required for the revisions experience. + * + * @since 4.1.0 + * + * @global WP_Post $post The global `$post` object. + */ +function wp_print_revision_templates() { + global $post; + ?> + + + + + + + + get_charset_collate(); + +/** + * Retrieve the SQL for creating database tables. + * + * @since 3.3.0 + * + * @param string $scope Optional. The tables for which to retrieve SQL. Can be all, global, ms_global, or blog tables. Defaults to all. + * @param int $blog_id Optional. The blog ID for which to retrieve SQL. Default is the current blog ID. + * @return string The SQL needed to create the requested tables. + */ +function wp_get_db_schema( $scope = 'all', $blog_id = null ) { + global $wpdb; + + $charset_collate = ''; + + if ( ! empty($wpdb->charset) ) + $charset_collate = "DEFAULT CHARACTER SET $wpdb->charset"; + if ( ! empty($wpdb->collate) ) + $charset_collate .= " COLLATE $wpdb->collate"; + + if ( $blog_id && $blog_id != $wpdb->blogid ) + $old_blog_id = $wpdb->set_blog_id( $blog_id ); + + // Engage multisite if in the middle of turning it on from network.php. + $is_multisite = is_multisite() || ( defined( 'WP_INSTALLING_NETWORK' ) && WP_INSTALLING_NETWORK ); + + // Blog specific tables. + $blog_tables = "CREATE TABLE $wpdb->terms ( + term_id bigint(20) unsigned NOT NULL auto_increment, + name varchar(200) NOT NULL default '', + slug varchar(200) NOT NULL default '', + term_group bigint(10) NOT NULL default 0, + PRIMARY KEY (term_id), + KEY slug (slug), + KEY name (name) +) $charset_collate; +CREATE TABLE $wpdb->term_taxonomy ( + term_taxonomy_id bigint(20) unsigned NOT NULL auto_increment, + term_id bigint(20) unsigned NOT NULL default 0, + taxonomy varchar(32) NOT NULL default '', + description longtext NOT NULL, + parent bigint(20) unsigned NOT NULL default 0, + count bigint(20) NOT NULL default 0, + PRIMARY KEY (term_taxonomy_id), + UNIQUE KEY term_id_taxonomy (term_id,taxonomy), + KEY taxonomy (taxonomy) +) $charset_collate; +CREATE TABLE $wpdb->term_relationships ( + object_id bigint(20) unsigned NOT NULL default 0, + term_taxonomy_id bigint(20) unsigned NOT NULL default 0, + term_order int(11) NOT NULL default 0, + PRIMARY KEY (object_id,term_taxonomy_id), + KEY term_taxonomy_id (term_taxonomy_id) +) $charset_collate; +CREATE TABLE $wpdb->commentmeta ( + meta_id bigint(20) unsigned NOT NULL auto_increment, + comment_id bigint(20) unsigned NOT NULL default '0', + meta_key varchar(255) default NULL, + meta_value longtext, + PRIMARY KEY (meta_id), + KEY comment_id (comment_id), + KEY meta_key (meta_key) +) $charset_collate; +CREATE TABLE $wpdb->comments ( + comment_ID bigint(20) unsigned NOT NULL auto_increment, + comment_post_ID bigint(20) unsigned NOT NULL default '0', + comment_author tinytext NOT NULL, + comment_author_email varchar(100) NOT NULL default '', + comment_author_url varchar(200) NOT NULL default '', + comment_author_IP varchar(100) NOT NULL default '', + comment_date datetime NOT NULL default '0000-00-00 00:00:00', + comment_date_gmt datetime NOT NULL default '0000-00-00 00:00:00', + comment_content text NOT NULL, + comment_karma int(11) NOT NULL default '0', + comment_approved varchar(20) NOT NULL default '1', + comment_agent varchar(255) NOT NULL default '', + comment_type varchar(20) NOT NULL default '', + comment_parent bigint(20) unsigned NOT NULL default '0', + user_id bigint(20) unsigned NOT NULL default '0', + PRIMARY KEY (comment_ID), + KEY comment_post_ID (comment_post_ID), + KEY comment_approved_date_gmt (comment_approved,comment_date_gmt), + KEY comment_date_gmt (comment_date_gmt), + KEY comment_parent (comment_parent), + KEY comment_author_email (comment_author_email(10)) +) $charset_collate; +CREATE TABLE $wpdb->links ( + link_id bigint(20) unsigned NOT NULL auto_increment, + link_url varchar(255) NOT NULL default '', + link_name varchar(255) NOT NULL default '', + link_image varchar(255) NOT NULL default '', + link_target varchar(25) NOT NULL default '', + link_description varchar(255) NOT NULL default '', + link_visible varchar(20) NOT NULL default 'Y', + link_owner bigint(20) unsigned NOT NULL default '1', + link_rating int(11) NOT NULL default '0', + link_updated datetime NOT NULL default '0000-00-00 00:00:00', + link_rel varchar(255) NOT NULL default '', + link_notes mediumtext NOT NULL, + link_rss varchar(255) NOT NULL default '', + PRIMARY KEY (link_id), + KEY link_visible (link_visible) +) $charset_collate; +CREATE TABLE $wpdb->options ( + option_id bigint(20) unsigned NOT NULL auto_increment, + option_name varchar(64) NOT NULL default '', + option_value longtext NOT NULL, + autoload varchar(20) NOT NULL default 'yes', + PRIMARY KEY (option_id), + UNIQUE KEY option_name (option_name) +) $charset_collate; +CREATE TABLE $wpdb->postmeta ( + meta_id bigint(20) unsigned NOT NULL auto_increment, + post_id bigint(20) unsigned NOT NULL default '0', + meta_key varchar(255) default NULL, + meta_value longtext, + PRIMARY KEY (meta_id), + KEY post_id (post_id), + KEY meta_key (meta_key) +) $charset_collate; +CREATE TABLE $wpdb->posts ( + ID bigint(20) unsigned NOT NULL auto_increment, + post_author bigint(20) unsigned NOT NULL default '0', + post_date datetime NOT NULL default '0000-00-00 00:00:00', + post_date_gmt datetime NOT NULL default '0000-00-00 00:00:00', + post_content longtext NOT NULL, + post_title text NOT NULL, + post_excerpt text NOT NULL, + post_status varchar(20) NOT NULL default 'publish', + comment_status varchar(20) NOT NULL default 'open', + ping_status varchar(20) NOT NULL default 'open', + post_password varchar(20) NOT NULL default '', + post_name varchar(200) NOT NULL default '', + to_ping text NOT NULL, + pinged text NOT NULL, + post_modified datetime NOT NULL default '0000-00-00 00:00:00', + post_modified_gmt datetime NOT NULL default '0000-00-00 00:00:00', + post_content_filtered longtext NOT NULL, + post_parent bigint(20) unsigned NOT NULL default '0', + guid varchar(255) NOT NULL default '', + menu_order int(11) NOT NULL default '0', + post_type varchar(20) NOT NULL default 'post', + post_mime_type varchar(100) NOT NULL default '', + comment_count bigint(20) NOT NULL default '0', + PRIMARY KEY (ID), + KEY post_name (post_name), + KEY type_status_date (post_type,post_status,post_date,ID), + KEY post_parent (post_parent), + KEY post_author (post_author) +) $charset_collate;\n"; + + // Single site users table. The multisite flavor of the users table is handled below. + $users_single_table = "CREATE TABLE $wpdb->users ( + ID bigint(20) unsigned NOT NULL auto_increment, + user_login varchar(60) NOT NULL default '', + user_pass varchar(64) NOT NULL default '', + user_nicename varchar(50) NOT NULL default '', + user_email varchar(100) NOT NULL default '', + user_url varchar(100) NOT NULL default '', + user_registered datetime NOT NULL default '0000-00-00 00:00:00', + user_activation_key varchar(60) NOT NULL default '', + user_status int(11) NOT NULL default '0', + display_name varchar(250) NOT NULL default '', + PRIMARY KEY (ID), + KEY user_login_key (user_login), + KEY user_nicename (user_nicename) +) $charset_collate;\n"; + + // Multisite users table + $users_multi_table = "CREATE TABLE $wpdb->users ( + ID bigint(20) unsigned NOT NULL auto_increment, + user_login varchar(60) NOT NULL default '', + user_pass varchar(64) NOT NULL default '', + user_nicename varchar(50) NOT NULL default '', + user_email varchar(100) NOT NULL default '', + user_url varchar(100) NOT NULL default '', + user_registered datetime NOT NULL default '0000-00-00 00:00:00', + user_activation_key varchar(60) NOT NULL default '', + user_status int(11) NOT NULL default '0', + display_name varchar(250) NOT NULL default '', + spam tinyint(2) NOT NULL default '0', + deleted tinyint(2) NOT NULL default '0', + PRIMARY KEY (ID), + KEY user_login_key (user_login), + KEY user_nicename (user_nicename) +) $charset_collate;\n"; + + // Usermeta. + $usermeta_table = "CREATE TABLE $wpdb->usermeta ( + umeta_id bigint(20) unsigned NOT NULL auto_increment, + user_id bigint(20) unsigned NOT NULL default '0', + meta_key varchar(255) default NULL, + meta_value longtext, + PRIMARY KEY (umeta_id), + KEY user_id (user_id), + KEY meta_key (meta_key) +) $charset_collate;\n"; + + // Global tables + if ( $is_multisite ) + $global_tables = $users_multi_table . $usermeta_table; + else + $global_tables = $users_single_table . $usermeta_table; + + // Multisite global tables. + $ms_global_tables = "CREATE TABLE $wpdb->blogs ( + blog_id bigint(20) NOT NULL auto_increment, + site_id bigint(20) NOT NULL default '0', + domain varchar(200) NOT NULL default '', + path varchar(100) NOT NULL default '', + registered datetime NOT NULL default '0000-00-00 00:00:00', + last_updated datetime NOT NULL default '0000-00-00 00:00:00', + public tinyint(2) NOT NULL default '1', + archived tinyint(2) NOT NULL default '0', + mature tinyint(2) NOT NULL default '0', + spam tinyint(2) NOT NULL default '0', + deleted tinyint(2) NOT NULL default '0', + lang_id int(11) NOT NULL default '0', + PRIMARY KEY (blog_id), + KEY domain (domain(50),path(5)), + KEY lang_id (lang_id) +) $charset_collate; +CREATE TABLE $wpdb->blog_versions ( + blog_id bigint(20) NOT NULL default '0', + db_version varchar(20) NOT NULL default '', + last_updated datetime NOT NULL default '0000-00-00 00:00:00', + PRIMARY KEY (blog_id), + KEY db_version (db_version) +) $charset_collate; +CREATE TABLE $wpdb->registration_log ( + ID bigint(20) NOT NULL auto_increment, + email varchar(255) NOT NULL default '', + IP varchar(30) NOT NULL default '', + blog_id bigint(20) NOT NULL default '0', + date_registered datetime NOT NULL default '0000-00-00 00:00:00', + PRIMARY KEY (ID), + KEY IP (IP) +) $charset_collate; +CREATE TABLE $wpdb->site ( + id bigint(20) NOT NULL auto_increment, + domain varchar(200) NOT NULL default '', + path varchar(100) NOT NULL default '', + PRIMARY KEY (id), + KEY domain (domain,path) +) $charset_collate; +CREATE TABLE $wpdb->sitemeta ( + meta_id bigint(20) NOT NULL auto_increment, + site_id bigint(20) NOT NULL default '0', + meta_key varchar(255) default NULL, + meta_value longtext, + PRIMARY KEY (meta_id), + KEY meta_key (meta_key), + KEY site_id (site_id) +) $charset_collate; +CREATE TABLE $wpdb->signups ( + signup_id bigint(20) NOT NULL auto_increment, + domain varchar(200) NOT NULL default '', + path varchar(100) NOT NULL default '', + title longtext NOT NULL, + user_login varchar(60) NOT NULL default '', + user_email varchar(100) NOT NULL default '', + registered datetime NOT NULL default '0000-00-00 00:00:00', + activated datetime NOT NULL default '0000-00-00 00:00:00', + active tinyint(1) NOT NULL default '0', + activation_key varchar(50) NOT NULL default '', + meta longtext, + PRIMARY KEY (signup_id), + KEY activation_key (activation_key), + KEY user_email (user_email), + KEY user_login_email (user_login,user_email), + KEY domain_path (domain,path) +) $charset_collate;"; + + switch ( $scope ) { + case 'blog' : + $queries = $blog_tables; + break; + case 'global' : + $queries = $global_tables; + if ( $is_multisite ) + $queries .= $ms_global_tables; + break; + case 'ms_global' : + $queries = $ms_global_tables; + break; + case 'all' : + default: + $queries = $global_tables . $blog_tables; + if ( $is_multisite ) + $queries .= $ms_global_tables; + break; + } + + if ( isset( $old_blog_id ) ) + $wpdb->set_blog_id( $old_blog_id ); + + return $queries; +} + +// Populate for back compat. +$wp_queries = wp_get_db_schema( 'all' ); + +/** + * Create WordPress options and set the default values. + * + * @since 1.5.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + * @uses $wp_db_version + */ +function populate_options() { + global $wpdb, $wp_db_version, $wp_current_db_version; + + $guessurl = wp_guess_url(); + /** + * Fires before creating WordPress options and populating their default values. + * + * @since 2.6.0 + */ + do_action( 'populate_options' ); + + if ( ini_get('safe_mode') ) { + // Safe mode can break mkdir() so use a flat structure by default. + $uploads_use_yearmonth_folders = 0; + } else { + $uploads_use_yearmonth_folders = 1; + } + + $template = WP_DEFAULT_THEME; + // If default theme is a child theme, we need to get its template + $theme = wp_get_theme( $template ); + if ( ! $theme->errors() ) + $template = $theme->get_template(); + + $timezone_string = ''; + $gmt_offset = 0; + /* translators: default GMT offset or timezone string. Must be either a valid offset (-12 to 14) + or a valid timezone string (America/New_York). See http://us3.php.net/manual/en/timezones.php + for all timezone strings supported by PHP. + */ + $offset_or_tz = _x( '0', 'default GMT offset or timezone string' ); + if ( is_numeric( $offset_or_tz ) ) + $gmt_offset = $offset_or_tz; + elseif ( $offset_or_tz && in_array( $offset_or_tz, timezone_identifiers_list() ) ) + $timezone_string = $offset_or_tz; + + $options = array( + 'siteurl' => $guessurl, + 'home' => $guessurl, + 'blogname' => __('My Site'), + /* translators: blog tagline */ + 'blogdescription' => __('Just another WordPress site'), + 'users_can_register' => 0, + 'admin_email' => 'you@example.com', + /* translators: default start of the week. 0 = Sunday, 1 = Monday */ + 'start_of_week' => _x( '1', 'start of week' ), + 'use_balanceTags' => 0, + 'use_smilies' => 1, + 'require_name_email' => 1, + 'comments_notify' => 1, + 'posts_per_rss' => 10, + 'rss_use_excerpt' => 0, + 'mailserver_url' => 'mail.example.com', + 'mailserver_login' => 'login@example.com', + 'mailserver_pass' => 'password', + 'mailserver_port' => 110, + 'default_category' => 1, + 'default_comment_status' => 'open', + 'default_ping_status' => 'open', + 'default_pingback_flag' => 1, + 'posts_per_page' => 10, + /* translators: default date format, see http://php.net/date */ + 'date_format' => __('F j, Y'), + /* translators: default time format, see http://php.net/date */ + 'time_format' => __('g:i a'), + /* translators: links last updated date format, see http://php.net/date */ + 'links_updated_date_format' => __('F j, Y g:i a'), + 'comment_moderation' => 0, + 'moderation_notify' => 1, + 'permalink_structure' => '', + 'gzipcompression' => 0, + 'hack_file' => 0, + 'blog_charset' => 'UTF-8', + 'moderation_keys' => '', + 'active_plugins' => array(), + 'category_base' => '', + 'ping_sites' => 'http://rpc.pingomatic.com/', + 'advanced_edit' => 0, + 'comment_max_links' => 2, + 'gmt_offset' => $gmt_offset, + + // 1.5 + 'default_email_category' => 1, + 'recently_edited' => '', + 'template' => $template, + 'stylesheet' => WP_DEFAULT_THEME, + 'comment_whitelist' => 1, + 'blacklist_keys' => '', + 'comment_registration' => 0, + 'html_type' => 'text/html', + + // 1.5.1 + 'use_trackback' => 0, + + // 2.0 + 'default_role' => 'subscriber', + 'db_version' => $wp_db_version, + + // 2.0.1 + 'uploads_use_yearmonth_folders' => $uploads_use_yearmonth_folders, + 'upload_path' => '', + + // 2.1 + 'blog_public' => '1', + 'default_link_category' => 2, + 'show_on_front' => 'posts', + + // 2.2 + 'tag_base' => '', + + // 2.5 + 'show_avatars' => '1', + 'avatar_rating' => 'G', + 'upload_url_path' => '', + 'thumbnail_size_w' => 150, + 'thumbnail_size_h' => 150, + 'thumbnail_crop' => 1, + 'medium_size_w' => 300, + 'medium_size_h' => 300, + + // 2.6 + 'avatar_default' => 'mystery', + + // 2.7 + 'large_size_w' => 1024, + 'large_size_h' => 1024, + 'image_default_link_type' => 'file', + 'image_default_size' => '', + 'image_default_align' => '', + 'close_comments_for_old_posts' => 0, + 'close_comments_days_old' => 14, + 'thread_comments' => 1, + 'thread_comments_depth' => 5, + 'page_comments' => 0, + 'comments_per_page' => 50, + 'default_comments_page' => 'newest', + 'comment_order' => 'asc', + 'sticky_posts' => array(), + 'widget_categories' => array(), + 'widget_text' => array(), + 'widget_rss' => array(), + 'uninstall_plugins' => array(), + + // 2.8 + 'timezone_string' => $timezone_string, + + // 3.0 + 'page_for_posts' => 0, + 'page_on_front' => 0, + + // 3.1 + 'default_post_format' => 0, + + // 3.5 + 'link_manager_enabled' => 0, + ); + + // 3.3 + if ( ! is_multisite() ) { + $options['initial_db_version'] = ! empty( $wp_current_db_version ) && $wp_current_db_version < $wp_db_version + ? $wp_current_db_version : $wp_db_version; + } + + // 3.0 multisite + if ( is_multisite() ) { + /* translators: blog tagline */ + $options[ 'blogdescription' ] = sprintf(__('Just another %s site'), get_current_site()->site_name ); + $options[ 'permalink_structure' ] = '/%year%/%monthnum%/%day%/%postname%/'; + } + + // Set autoload to no for these options + $fat_options = array( 'moderation_keys', 'recently_edited', 'blacklist_keys', 'uninstall_plugins' ); + + $keys = "'" . implode( "', '", array_keys( $options ) ) . "'"; + $existing_options = $wpdb->get_col( "SELECT option_name FROM $wpdb->options WHERE option_name in ( $keys )" ); + + $insert = ''; + foreach ( $options as $option => $value ) { + if ( in_array($option, $existing_options) ) + continue; + if ( in_array($option, $fat_options) ) + $autoload = 'no'; + else + $autoload = 'yes'; + + if ( is_array($value) ) + $value = serialize($value); + if ( !empty($insert) ) + $insert .= ', '; + $insert .= $wpdb->prepare( "(%s, %s, %s)", $option, $value, $autoload ); + } + + if ( !empty($insert) ) + $wpdb->query("INSERT INTO $wpdb->options (option_name, option_value, autoload) VALUES " . $insert); + + // In case it is set, but blank, update "home". + if ( !__get_option('home') ) update_option('home', $guessurl); + + // Delete unused options. + $unusedoptions = array( + 'blodotgsping_url', 'bodyterminator', 'emailtestonly', 'phoneemail_separator', 'smilies_directory', + 'subjectprefix', 'use_bbcode', 'use_blodotgsping', 'use_phoneemail', 'use_quicktags', 'use_weblogsping', + 'weblogs_cache_file', 'use_preview', 'use_htmltrans', 'smilies_directory', 'fileupload_allowedusers', + 'use_phoneemail', 'default_post_status', 'default_post_category', 'archive_mode', 'time_difference', + 'links_minadminlevel', 'links_use_adminlevels', 'links_rating_type', 'links_rating_char', + 'links_rating_ignore_zero', 'links_rating_single_image', 'links_rating_image0', 'links_rating_image1', + 'links_rating_image2', 'links_rating_image3', 'links_rating_image4', 'links_rating_image5', + 'links_rating_image6', 'links_rating_image7', 'links_rating_image8', 'links_rating_image9', + 'links_recently_updated_time', 'links_recently_updated_prepend', 'links_recently_updated_append', + 'weblogs_cacheminutes', 'comment_allowed_tags', 'search_engine_friendly_urls', 'default_geourl_lat', + 'default_geourl_lon', 'use_default_geourl', 'weblogs_xml_url', 'new_users_can_blog', '_wpnonce', + '_wp_http_referer', 'Update', 'action', 'rich_editing', 'autosave_interval', 'deactivated_plugins', + 'can_compress_scripts', 'page_uris', 'update_core', 'update_plugins', 'update_themes', 'doing_cron', + 'random_seed', 'rss_excerpt_length', 'secret', 'use_linksupdate', 'default_comment_status_page', + 'wporg_popular_tags', 'what_to_show', 'rss_language', 'language', 'enable_xmlrpc', 'enable_app', + 'embed_autourls', 'default_post_edit_rows', + ); + foreach ( $unusedoptions as $option ) + delete_option($option); + + // Delete obsolete magpie stuff. + $wpdb->query("DELETE FROM $wpdb->options WHERE option_name REGEXP '^rss_[0-9a-f]{32}(_ts)?$'"); + + /* + * Deletes all expired transients. The multi-table delete syntax is used + * to delete the transient record from table a, and the corresponding + * transient_timeout record from table b. + */ + $time = time(); + $sql = "DELETE a, b FROM $wpdb->options a, $wpdb->options b + WHERE a.option_name LIKE %s + AND a.option_name NOT LIKE %s + AND b.option_name = CONCAT( '_transient_timeout_', SUBSTRING( a.option_name, 12 ) ) + AND b.option_value < %d"; + $wpdb->query( $wpdb->prepare( $sql, $wpdb->esc_like( '_transient_' ) . '%', $wpdb->esc_like( '_transient_timeout_' ) . '%', $time ) ); + + if ( is_main_site() && is_main_network() ) { + $sql = "DELETE a, b FROM $wpdb->options a, $wpdb->options b + WHERE a.option_name LIKE %s + AND a.option_name NOT LIKE %s + AND b.option_name = CONCAT( '_site_transient_timeout_', SUBSTRING( a.option_name, 17 ) ) + AND b.option_value < %d"; + $wpdb->query( $wpdb->prepare( $sql, $wpdb->esc_like( '_site_transient_' ) . '%', $wpdb->esc_like( '_site_transient_timeout_' ) . '%', $time ) ); + } +} + +/** + * Execute WordPress role creation for the various WordPress versions. + * + * @since 2.0.0 + */ +function populate_roles() { + populate_roles_160(); + populate_roles_210(); + populate_roles_230(); + populate_roles_250(); + populate_roles_260(); + populate_roles_270(); + populate_roles_280(); + populate_roles_300(); +} + +/** + * Create the roles for WordPress 2.0 + * + * @since 2.0.0 + */ +function populate_roles_160() { + // Add roles + + // Dummy gettext calls to get strings in the catalog. + /* translators: user role */ + _x('Administrator', 'User role'); + /* translators: user role */ + _x('Editor', 'User role'); + /* translators: user role */ + _x('Author', 'User role'); + /* translators: user role */ + _x('Contributor', 'User role'); + /* translators: user role */ + _x('Subscriber', 'User role'); + + add_role('administrator', 'Administrator'); + add_role('editor', 'Editor'); + add_role('author', 'Author'); + add_role('contributor', 'Contributor'); + add_role('subscriber', 'Subscriber'); + + // Add caps for Administrator role + $role = get_role('administrator'); + $role->add_cap('switch_themes'); + $role->add_cap('edit_themes'); + $role->add_cap('activate_plugins'); + $role->add_cap('edit_plugins'); + $role->add_cap('edit_users'); + $role->add_cap('edit_files'); + $role->add_cap('manage_options'); + $role->add_cap('moderate_comments'); + $role->add_cap('manage_categories'); + $role->add_cap('manage_links'); + $role->add_cap('upload_files'); + $role->add_cap('import'); + $role->add_cap('unfiltered_html'); + $role->add_cap('edit_posts'); + $role->add_cap('edit_others_posts'); + $role->add_cap('edit_published_posts'); + $role->add_cap('publish_posts'); + $role->add_cap('edit_pages'); + $role->add_cap('read'); + $role->add_cap('level_10'); + $role->add_cap('level_9'); + $role->add_cap('level_8'); + $role->add_cap('level_7'); + $role->add_cap('level_6'); + $role->add_cap('level_5'); + $role->add_cap('level_4'); + $role->add_cap('level_3'); + $role->add_cap('level_2'); + $role->add_cap('level_1'); + $role->add_cap('level_0'); + + // Add caps for Editor role + $role = get_role('editor'); + $role->add_cap('moderate_comments'); + $role->add_cap('manage_categories'); + $role->add_cap('manage_links'); + $role->add_cap('upload_files'); + $role->add_cap('unfiltered_html'); + $role->add_cap('edit_posts'); + $role->add_cap('edit_others_posts'); + $role->add_cap('edit_published_posts'); + $role->add_cap('publish_posts'); + $role->add_cap('edit_pages'); + $role->add_cap('read'); + $role->add_cap('level_7'); + $role->add_cap('level_6'); + $role->add_cap('level_5'); + $role->add_cap('level_4'); + $role->add_cap('level_3'); + $role->add_cap('level_2'); + $role->add_cap('level_1'); + $role->add_cap('level_0'); + + // Add caps for Author role + $role = get_role('author'); + $role->add_cap('upload_files'); + $role->add_cap('edit_posts'); + $role->add_cap('edit_published_posts'); + $role->add_cap('publish_posts'); + $role->add_cap('read'); + $role->add_cap('level_2'); + $role->add_cap('level_1'); + $role->add_cap('level_0'); + + // Add caps for Contributor role + $role = get_role('contributor'); + $role->add_cap('edit_posts'); + $role->add_cap('read'); + $role->add_cap('level_1'); + $role->add_cap('level_0'); + + // Add caps for Subscriber role + $role = get_role('subscriber'); + $role->add_cap('read'); + $role->add_cap('level_0'); +} + +/** + * Create and modify WordPress roles for WordPress 2.1. + * + * @since 2.1.0 + */ +function populate_roles_210() { + $roles = array('administrator', 'editor'); + foreach ($roles as $role) { + $role = get_role($role); + if ( empty($role) ) + continue; + + $role->add_cap('edit_others_pages'); + $role->add_cap('edit_published_pages'); + $role->add_cap('publish_pages'); + $role->add_cap('delete_pages'); + $role->add_cap('delete_others_pages'); + $role->add_cap('delete_published_pages'); + $role->add_cap('delete_posts'); + $role->add_cap('delete_others_posts'); + $role->add_cap('delete_published_posts'); + $role->add_cap('delete_private_posts'); + $role->add_cap('edit_private_posts'); + $role->add_cap('read_private_posts'); + $role->add_cap('delete_private_pages'); + $role->add_cap('edit_private_pages'); + $role->add_cap('read_private_pages'); + } + + $role = get_role('administrator'); + if ( ! empty($role) ) { + $role->add_cap('delete_users'); + $role->add_cap('create_users'); + } + + $role = get_role('author'); + if ( ! empty($role) ) { + $role->add_cap('delete_posts'); + $role->add_cap('delete_published_posts'); + } + + $role = get_role('contributor'); + if ( ! empty($role) ) { + $role->add_cap('delete_posts'); + } +} + +/** + * Create and modify WordPress roles for WordPress 2.3. + * + * @since 2.3.0 + */ +function populate_roles_230() { + $role = get_role( 'administrator' ); + + if ( !empty( $role ) ) { + $role->add_cap( 'unfiltered_upload' ); + } +} + +/** + * Create and modify WordPress roles for WordPress 2.5. + * + * @since 2.5.0 + */ +function populate_roles_250() { + $role = get_role( 'administrator' ); + + if ( !empty( $role ) ) { + $role->add_cap( 'edit_dashboard' ); + } +} + +/** + * Create and modify WordPress roles for WordPress 2.6. + * + * @since 2.6.0 + */ +function populate_roles_260() { + $role = get_role( 'administrator' ); + + if ( !empty( $role ) ) { + $role->add_cap( 'update_plugins' ); + $role->add_cap( 'delete_plugins' ); + } +} + +/** + * Create and modify WordPress roles for WordPress 2.7. + * + * @since 2.7.0 + */ +function populate_roles_270() { + $role = get_role( 'administrator' ); + + if ( !empty( $role ) ) { + $role->add_cap( 'install_plugins' ); + $role->add_cap( 'update_themes' ); + } +} + +/** + * Create and modify WordPress roles for WordPress 2.8. + * + * @since 2.8.0 + */ +function populate_roles_280() { + $role = get_role( 'administrator' ); + + if ( !empty( $role ) ) { + $role->add_cap( 'install_themes' ); + } +} + +/** + * Create and modify WordPress roles for WordPress 3.0. + * + * @since 3.0.0 + */ +function populate_roles_300() { + $role = get_role( 'administrator' ); + + if ( !empty( $role ) ) { + $role->add_cap( 'update_core' ); + $role->add_cap( 'list_users' ); + $role->add_cap( 'remove_users' ); + + /* + * Never used, will be removed. create_users or promote_users + * is the capability you're looking for. + */ + $role->add_cap( 'add_users' ); + + $role->add_cap( 'promote_users' ); + $role->add_cap( 'edit_theme_options' ); + $role->add_cap( 'delete_themes' ); + $role->add_cap( 'export' ); + } +} + +/** + * Install Network. + * + * @since 3.0.0 + * + */ +if ( !function_exists( 'install_network' ) ) : +function install_network() { + if ( ! defined( 'WP_INSTALLING_NETWORK' ) ) + define( 'WP_INSTALLING_NETWORK', true ); + + dbDelta( wp_get_db_schema( 'global' ) ); +} +endif; + +/** + * Populate network settings. + * + * @since 3.0.0 + * + * @param int $network_id ID of network to populate. + * @return bool|WP_Error True on success, or WP_Error on warning (with the install otherwise successful, + * so the error code must be checked) or failure. + */ +function populate_network( $network_id = 1, $domain = '', $email = '', $site_name = '', $path = '/', $subdomain_install = false ) { + global $wpdb, $current_site, $wp_db_version, $wp_rewrite; + + $errors = new WP_Error(); + if ( '' == $domain ) + $errors->add( 'empty_domain', __( 'You must provide a domain name.' ) ); + if ( '' == $site_name ) + $errors->add( 'empty_sitename', __( 'You must provide a name for your network of sites.' ) ); + + // Check for network collision. + if ( $network_id == $wpdb->get_var( $wpdb->prepare( "SELECT id FROM $wpdb->site WHERE id = %d", $network_id ) ) ) + $errors->add( 'siteid_exists', __( 'The network already exists.' ) ); + + $site_user = get_user_by( 'email', $email ); + if ( ! is_email( $email ) ) + $errors->add( 'invalid_email', __( 'You must provide a valid e-mail address.' ) ); + + if ( $errors->get_error_code() ) + return $errors; + + // Set up site tables. + $template = get_option( 'template' ); + $stylesheet = get_option( 'stylesheet' ); + $allowed_themes = array( $stylesheet => true ); + if ( $template != $stylesheet ) + $allowed_themes[ $template ] = true; + if ( WP_DEFAULT_THEME != $stylesheet && WP_DEFAULT_THEME != $template ) + $allowed_themes[ WP_DEFAULT_THEME ] = true; + + if ( 1 == $network_id ) { + $wpdb->insert( $wpdb->site, array( 'domain' => $domain, 'path' => $path ) ); + $network_id = $wpdb->insert_id; + } else { + $wpdb->insert( $wpdb->site, array( 'domain' => $domain, 'path' => $path, 'id' => $network_id ) ); + } + + wp_cache_delete( 'networks_have_paths', 'site-options' ); + + if ( !is_multisite() ) { + $site_admins = array( $site_user->user_login ); + $users = get_users( array( 'fields' => array( 'ID', 'user_login' ) ) ); + if ( $users ) { + foreach ( $users as $user ) { + if ( is_super_admin( $user->ID ) && !in_array( $user->user_login, $site_admins ) ) + $site_admins[] = $user->user_login; + } + } + } else { + $site_admins = get_site_option( 'site_admins' ); + } + + $welcome_email = __( 'Dear User, + +Your new SITE_NAME site has been successfully set up at: +BLOG_URL + +You can log in to the administrator account with the following information: + +Username: USERNAME +Password: PASSWORD +Log in here: BLOG_URLwp-login.php + +We hope you enjoy your new site. Thanks! + +--The Team @ SITE_NAME' ); + + $misc_exts = array( + // Images. + 'jpg', 'jpeg', 'png', 'gif', + // Video. + 'mov', 'avi', 'mpg', '3gp', '3g2', + // "audio". + 'midi', 'mid', + // Miscellaneous. + 'pdf', 'doc', 'ppt', 'odt', 'pptx', 'docx', 'pps', 'ppsx', 'xls', 'xlsx', 'key', + ); + $audio_exts = wp_get_audio_extensions(); + $video_exts = wp_get_video_extensions(); + $upload_filetypes = array_unique( array_merge( $misc_exts, $audio_exts, $video_exts ) ); + + $sitemeta = array( + 'site_name' => $site_name, + 'admin_email' => $site_user->user_email, + 'admin_user_id' => $site_user->ID, + 'registration' => 'none', + 'upload_filetypes' => implode( ' ', $upload_filetypes ), + 'blog_upload_space' => 100, + 'fileupload_maxk' => 1500, + 'site_admins' => $site_admins, + 'allowedthemes' => $allowed_themes, + 'illegal_names' => array( 'www', 'web', 'root', 'admin', 'main', 'invite', 'administrator', 'files' ), + 'wpmu_upgrade_site' => $wp_db_version, + 'welcome_email' => $welcome_email, + 'first_post' => __( 'Welcome to SITE_NAME. This is your first post. Edit or delete it, then start blogging!' ), + // @todo - network admins should have a method of editing the network siteurl (used for cookie hash) + 'siteurl' => get_option( 'siteurl' ) . '/', + 'add_new_users' => '0', + 'upload_space_check_disabled' => is_multisite() ? get_site_option( 'upload_space_check_disabled' ) : '1', + 'subdomain_install' => intval( $subdomain_install ), + 'global_terms_enabled' => global_terms_enabled() ? '1' : '0', + 'ms_files_rewriting' => is_multisite() ? get_site_option( 'ms_files_rewriting' ) : '0', + 'initial_db_version' => get_option( 'initial_db_version' ), + 'active_sitewide_plugins' => array(), + 'WPLANG' => get_locale(), + ); + if ( ! $subdomain_install ) + $sitemeta['illegal_names'][] = 'blog'; + + /** + * Filter meta for a network on creation. + * + * @since 3.7.0 + * + * @param array $sitemeta Associative array of network meta keys and values to be inserted. + * @param int $network_id ID of network to populate. + */ + $sitemeta = apply_filters( 'populate_network_meta', $sitemeta, $network_id ); + + $insert = ''; + foreach ( $sitemeta as $meta_key => $meta_value ) { + if ( is_array( $meta_value ) ) + $meta_value = serialize( $meta_value ); + if ( !empty( $insert ) ) + $insert .= ', '; + $insert .= $wpdb->prepare( "( %d, %s, %s)", $network_id, $meta_key, $meta_value ); + } + $wpdb->query( "INSERT INTO $wpdb->sitemeta ( site_id, meta_key, meta_value ) VALUES " . $insert ); + + /* + * When upgrading from single to multisite, assume the current site will + * become the main site of the network. When using populate_network() + * to create another network in an existing multisite environment, skip + * these steps since the main site of the new network has not yet been + * created. + */ + if ( ! is_multisite() ) { + $current_site = new stdClass; + $current_site->domain = $domain; + $current_site->path = $path; + $current_site->site_name = ucfirst( $domain ); + $wpdb->insert( $wpdb->blogs, array( 'site_id' => $network_id, 'blog_id' => 1, 'domain' => $domain, 'path' => $path, 'registered' => current_time( 'mysql' ) ) ); + $current_site->blog_id = $blog_id = $wpdb->insert_id; + update_user_meta( $site_user->ID, 'source_domain', $domain ); + update_user_meta( $site_user->ID, 'primary_blog', $blog_id ); + + if ( $subdomain_install ) + $wp_rewrite->set_permalink_structure( '/%year%/%monthnum%/%day%/%postname%/' ); + else + $wp_rewrite->set_permalink_structure( '/blog/%year%/%monthnum%/%day%/%postname%/' ); + + flush_rewrite_rules(); + + if ( ! $subdomain_install ) + return true; + + $vhost_ok = false; + $errstr = ''; + $hostname = substr( md5( time() ), 0, 6 ) . '.' . $domain; // Very random hostname! + $page = wp_remote_get( 'http://' . $hostname, array( 'timeout' => 5, 'httpversion' => '1.1' ) ); + if ( is_wp_error( $page ) ) + $errstr = $page->get_error_message(); + elseif ( 200 == wp_remote_retrieve_response_code( $page ) ) + $vhost_ok = true; + + if ( ! $vhost_ok ) { + $msg = '

    ' . __( 'Warning! Wildcard DNS may not be configured correctly!' ) . '

    '; + $msg .= '

    ' . sprintf( __( 'The installer attempted to contact a random hostname (%1$s) on your domain.' ), $hostname ); + if ( ! empty ( $errstr ) ) + $msg .= ' ' . sprintf( __( 'This resulted in an error message: %s' ), '' . $errstr . '' ); + $msg .= '

    '; + $msg .= '

    ' . __( 'To use a subdomain configuration, you must have a wildcard entry in your DNS. This usually means adding a * hostname record pointing at your web server in your DNS configuration tool.' ) . '

    '; + $msg .= '

    ' . __( 'You can still use your site but any subdomain you create may not be accessible. If you know your DNS is correct, ignore this message.' ) . '

    '; + return new WP_Error( 'no_wildcard_dns', $msg ); + } + } + + return true; +} diff --git a/wp-admin/includes/screen.php b/wp-admin/includes/screen.php new file mode 100644 index 0000000..0482d78 --- /dev/null +++ b/wp-admin/includes/screen.php @@ -0,0 +1,1180 @@ + UI String + */ +function get_column_headers( $screen ) { + if ( is_string( $screen ) ) + $screen = convert_to_screen( $screen ); + + static $column_headers = array(); + + if ( ! isset( $column_headers[ $screen->id ] ) ) { + + /** + * Filter the column headers for a list table on a specific screen. + * + * The dynamic portion of the hook name, `$screen->id`, refers to the + * ID of a specific screen. For example, the screen ID for the Posts + * list table is edit-post, so the filter for that screen would be + * manage_edit-post_columns. + * + * @since 3.0.0 + * + * @param array $columns An array of column headers. Default empty. + */ + $column_headers[ $screen->id ] = apply_filters( "manage_{$screen->id}_columns", array() ); + } + + return $column_headers[ $screen->id ]; +} + +/** + * Get a list of hidden columns. + * + * @since 2.7.0 + * + * @param string|WP_Screen $screen The screen you want the hidden columns for + * @return array + */ +function get_hidden_columns( $screen ) { + if ( is_string( $screen ) ) + $screen = convert_to_screen( $screen ); + + return (array) get_user_option( 'manage' . $screen->id . 'columnshidden' ); +} + +/** + * Prints the meta box preferences for screen meta. + * + * @since 2.7.0 + * + * @param WP_Screen $screen + */ +function meta_box_prefs( $screen ) { + global $wp_meta_boxes; + + if ( is_string( $screen ) ) + $screen = convert_to_screen( $screen ); + + if ( empty($wp_meta_boxes[$screen->id]) ) + return; + + $hidden = get_hidden_meta_boxes($screen); + + foreach ( array_keys($wp_meta_boxes[$screen->id]) as $context ) { + foreach ( array_keys($wp_meta_boxes[$screen->id][$context]) as $priority ) { + foreach ( $wp_meta_boxes[$screen->id][$context][$priority] as $box ) { + if ( false == $box || ! $box['title'] ) + continue; + // Submit box cannot be hidden + if ( 'submitdiv' == $box['id'] || 'linksubmitdiv' == $box['id'] ) + continue; + $box_id = $box['id']; + echo '\n"; + } + } + } +} + +/** + * Get Hidden Meta Boxes + * + * @since 2.7.0 + * + * @param string|WP_Screen $screen Screen identifier + * @return array Hidden Meta Boxes + */ +function get_hidden_meta_boxes( $screen ) { + if ( is_string( $screen ) ) + $screen = convert_to_screen( $screen ); + + $hidden = get_user_option( "metaboxhidden_{$screen->id}" ); + + $use_defaults = ! is_array( $hidden ); + + // Hide slug boxes by default + if ( $use_defaults ) { + $hidden = array(); + if ( 'post' == $screen->base ) { + if ( 'post' == $screen->post_type || 'page' == $screen->post_type || 'attachment' == $screen->post_type ) + $hidden = array('slugdiv', 'trackbacksdiv', 'postcustom', 'postexcerpt', 'commentstatusdiv', 'commentsdiv', 'authordiv', 'revisionsdiv'); + else + $hidden = array( 'slugdiv' ); + } + + /** + * Filter the default list of hidden meta boxes. + * + * @since 3.1.0 + * + * @param array $hidden An array of meta boxes hidden by default. + * @param WP_Screen $screen WP_Screen object of the current screen. + */ + $hidden = apply_filters( 'default_hidden_meta_boxes', $hidden, $screen ); + } + + /** + * Filter the list of hidden meta boxes. + * + * @since 3.3.0 + * + * @param array $hidden An array of hidden meta boxes. + * @param WP_Screen $screen WP_Screen object of the current screen. + * @param bool $use_defaults Whether to show the default meta boxes. + * Default true. + */ + return apply_filters( 'hidden_meta_boxes', $hidden, $screen, $use_defaults ); +} + +/** + * Register and configure an admin screen option + * + * @since 3.1.0 + * + * @param string $option An option name. + * @param mixed $args Option-dependent arguments. + */ +function add_screen_option( $option, $args = array() ) { + $current_screen = get_current_screen(); + + if ( ! $current_screen ) + return; + + $current_screen->add_option( $option, $args ); +} + +/** + * Get the current screen object + * + * @since 3.1.0 + * + * @return WP_Screen Current screen object + */ +function get_current_screen() { + global $current_screen; + + if ( ! isset( $current_screen ) ) + return null; + + return $current_screen; +} + +/** + * Set the current screen object + * + * @since 3.0.0 + * + * @param mixed $hook_name Optional. The hook name (also known as the hook suffix) used to determine the screen, + * or an existing screen object. + */ +function set_current_screen( $hook_name = '' ) { + WP_Screen::get( $hook_name )->set_current_screen(); +} + +/** + * A class representing the admin screen. + * + * @since 3.3.0 + * @access public + */ +final class WP_Screen { + /** + * Any action associated with the screen. 'add' for *-add.php and *-new.php screens. Empty otherwise. + * + * @since 3.3.0 + * @var string + * @access public + */ + public $action; + + /** + * The base type of the screen. This is typically the same as $id but with any post types and taxonomies stripped. + * For example, for an $id of 'edit-post' the base is 'edit'. + * + * @since 3.3.0 + * @var string + * @access public + */ + public $base; + + /** + * The number of columns to display. Access with get_columns(). + * + * @since 3.4.0 + * @var int + * @access private + */ + private $columns = 0; + + /** + * The unique ID of the screen. + * + * @since 3.3.0 + * @var string + * @access public + */ + public $id; + + /** + * Which admin the screen is in. network | user | site | false + * + * @since 3.5.0 + * @var string + * @access protected + */ + protected $in_admin; + + /** + * Whether the screen is in the network admin. + * + * Deprecated. Use in_admin() instead. + * + * @since 3.3.0 + * @deprecated 3.5.0 + * @var bool + * @access public + */ + public $is_network; + + /** + * Whether the screen is in the user admin. + * + * Deprecated. Use in_admin() instead. + * + * @since 3.3.0 + * @deprecated 3.5.0 + * @var bool + * @access public + */ + public $is_user; + + /** + * The base menu parent. + * This is derived from $parent_file by removing the query string and any .php extension. + * $parent_file values of 'edit.php?post_type=page' and 'edit.php?post_type=post' have a $parent_base of 'edit'. + * + * @since 3.3.0 + * @var string + * @access public + */ + public $parent_base; + + /** + * The parent_file for the screen per the admin menu system. + * Some $parent_file values are 'edit.php?post_type=page', 'edit.php', and 'options-general.php'. + * + * @since 3.3.0 + * @var string + * @access public + */ + public $parent_file; + + /** + * The post type associated with the screen, if any. + * The 'edit.php?post_type=page' screen has a post type of 'page'. + * The 'edit-tags.php?taxonomy=$taxonomy&post_type=page' screen has a post type of 'page'. + * + * @since 3.3.0 + * @var string + * @access public + */ + public $post_type; + + /** + * The taxonomy associated with the screen, if any. + * The 'edit-tags.php?taxonomy=category' screen has a taxonomy of 'category'. + * @since 3.3.0 + * @var string + * @access public + */ + public $taxonomy; + + /** + * The help tab data associated with the screen, if any. + * + * @since 3.3.0 + * @var array + * @access private + */ + private $_help_tabs = array(); + + /** + * The help sidebar data associated with screen, if any. + * + * @since 3.3.0 + * @var string + * @access private + */ + private $_help_sidebar = ''; + + /** + * Stores old string-based help. + */ + private static $_old_compat_help = array(); + + /** + * The screen options associated with screen, if any. + * + * @since 3.3.0 + * @var array + * @access private + */ + private $_options = array(); + + /** + * The screen object registry. + * + * @since 3.3.0 + * @var array + * @access private + */ + private static $_registry = array(); + + /** + * Stores the result of the public show_screen_options function. + * + * @since 3.3.0 + * @var bool + * @access private + */ + private $_show_screen_options; + + /** + * Stores the 'screen_settings' section of screen options. + * + * @since 3.3.0 + * @var string + * @access private + */ + private $_screen_settings; + + /** + * Fetches a screen object. + * + * @since 3.3.0 + * @access public + * + * @param string $hook_name Optional. The hook name (also known as the hook suffix) used to determine the screen. + * Defaults to the current $hook_suffix global. + * @return WP_Screen Screen object. + */ + public static function get( $hook_name = '' ) { + + if ( is_a( $hook_name, 'WP_Screen' ) ) + return $hook_name; + + $post_type = $taxonomy = null; + $in_admin = false; + $action = ''; + + if ( $hook_name ) + $id = $hook_name; + else + $id = $GLOBALS['hook_suffix']; + + // For those pesky meta boxes. + if ( $hook_name && post_type_exists( $hook_name ) ) { + $post_type = $id; + $id = 'post'; // changes later. ends up being $base. + } else { + if ( '.php' == substr( $id, -4 ) ) + $id = substr( $id, 0, -4 ); + + if ( 'post-new' == $id || 'link-add' == $id || 'media-new' == $id || 'user-new' == $id ) { + $id = substr( $id, 0, -4 ); + $action = 'add'; + } + } + + if ( ! $post_type && $hook_name ) { + if ( '-network' == substr( $id, -8 ) ) { + $id = substr( $id, 0, -8 ); + $in_admin = 'network'; + } elseif ( '-user' == substr( $id, -5 ) ) { + $id = substr( $id, 0, -5 ); + $in_admin = 'user'; + } + + $id = sanitize_key( $id ); + if ( 'edit-comments' != $id && 'edit-tags' != $id && 'edit-' == substr( $id, 0, 5 ) ) { + $maybe = substr( $id, 5 ); + if ( taxonomy_exists( $maybe ) ) { + $id = 'edit-tags'; + $taxonomy = $maybe; + } elseif ( post_type_exists( $maybe ) ) { + $id = 'edit'; + $post_type = $maybe; + } + } + + if ( ! $in_admin ) + $in_admin = 'site'; + } else { + if ( defined( 'WP_NETWORK_ADMIN' ) && WP_NETWORK_ADMIN ) + $in_admin = 'network'; + elseif ( defined( 'WP_USER_ADMIN' ) && WP_USER_ADMIN ) + $in_admin = 'user'; + else + $in_admin = 'site'; + } + + if ( 'index' == $id ) + $id = 'dashboard'; + elseif ( 'front' == $id ) + $in_admin = false; + + $base = $id; + + // If this is the current screen, see if we can be more accurate for post types and taxonomies. + if ( ! $hook_name ) { + if ( isset( $_REQUEST['post_type'] ) ) + $post_type = post_type_exists( $_REQUEST['post_type'] ) ? $_REQUEST['post_type'] : false; + if ( isset( $_REQUEST['taxonomy'] ) ) + $taxonomy = taxonomy_exists( $_REQUEST['taxonomy'] ) ? $_REQUEST['taxonomy'] : false; + + switch ( $base ) { + case 'post' : + if ( isset( $_GET['post'] ) ) + $post_id = (int) $_GET['post']; + elseif ( isset( $_POST['post_ID'] ) ) + $post_id = (int) $_POST['post_ID']; + else + $post_id = 0; + + if ( $post_id ) { + $post = get_post( $post_id ); + if ( $post ) + $post_type = $post->post_type; + } + break; + case 'edit-tags' : + if ( null === $post_type && is_object_in_taxonomy( 'post', $taxonomy ? $taxonomy : 'post_tag' ) ) + $post_type = 'post'; + break; + } + } + + switch ( $base ) { + case 'post' : + if ( null === $post_type ) + $post_type = 'post'; + $id = $post_type; + break; + case 'edit' : + if ( null === $post_type ) + $post_type = 'post'; + $id .= '-' . $post_type; + break; + case 'edit-tags' : + if ( null === $taxonomy ) + $taxonomy = 'post_tag'; + // The edit-tags ID does not contain the post type. Look for it in the request. + if ( null === $post_type ) { + $post_type = 'post'; + if ( isset( $_REQUEST['post_type'] ) && post_type_exists( $_REQUEST['post_type'] ) ) + $post_type = $_REQUEST['post_type']; + } + + $id = 'edit-' . $taxonomy; + break; + } + + if ( 'network' == $in_admin ) { + $id .= '-network'; + $base .= '-network'; + } elseif ( 'user' == $in_admin ) { + $id .= '-user'; + $base .= '-user'; + } + + if ( isset( self::$_registry[ $id ] ) ) { + $screen = self::$_registry[ $id ]; + if ( $screen === get_current_screen() ) + return $screen; + } else { + $screen = new WP_Screen(); + $screen->id = $id; + } + + $screen->base = $base; + $screen->action = $action; + $screen->post_type = (string) $post_type; + $screen->taxonomy = (string) $taxonomy; + $screen->is_user = ( 'user' == $in_admin ); + $screen->is_network = ( 'network' == $in_admin ); + $screen->in_admin = $in_admin; + + self::$_registry[ $id ] = $screen; + + return $screen; + } + + /** + * Makes the screen object the current screen. + * + * @see set_current_screen() + * @since 3.3.0 + */ + public function set_current_screen() { + global $current_screen, $taxnow, $typenow; + $current_screen = $this; + $taxnow = $this->taxonomy; + $typenow = $this->post_type; + + /** + * Fires after the current screen has been set. + * + * @since 3.0.0 + * + * @param WP_Screen $current_screen Current WP_Screen object. + */ + do_action( 'current_screen', $current_screen ); + } + + /** + * Constructor + * + * @since 3.3.0 + * @access private + */ + private function __construct() {} + + /** + * Indicates whether the screen is in a particular admin + * + * @since 3.5.0 + * + * @param string $admin The admin to check against (network | user | site). + * If empty any of the three admins will result in true. + * @return boolean True if the screen is in the indicated admin, false otherwise. + * + */ + public function in_admin( $admin = null ) { + if ( empty( $admin ) ) + return (bool) $this->in_admin; + + return ( $admin == $this->in_admin ); + } + + /** + * Sets the old string-based contextual help for the screen. + * + * For backwards compatibility. + * + * @since 3.3.0 + * + * @param WP_Screen $screen A screen object. + * @param string $help Help text. + */ + public static function add_old_compat_help( $screen, $help ) { + self::$_old_compat_help[ $screen->id ] = $help; + } + + /** + * Set the parent information for the screen. + * This is called in admin-header.php after the menu parent for the screen has been determined. + * + * @since 3.3.0 + * + * @param string $parent_file The parent file of the screen. Typically the $parent_file global. + */ + public function set_parentage( $parent_file ) { + $this->parent_file = $parent_file; + list( $this->parent_base ) = explode( '?', $parent_file ); + $this->parent_base = str_replace( '.php', '', $this->parent_base ); + } + + /** + * Adds an option for the screen. + * Call this in template files after admin.php is loaded and before admin-header.php is loaded to add screen options. + * + * @since 3.3.0 + * + * @param string $option Option ID + * @param mixed $args Option-dependent arguments. + */ + public function add_option( $option, $args = array() ) { + $this->_options[ $option ] = $args; + } + + /** + * Remove an option from the screen. + * + * @since 3.8.0 + * + * @param string $option Option ID. + */ + public function remove_option( $option ) { + unset( $this->_options[ $option ] ); + } + + /** + * Remove all options from the screen. + * + * @since 3.8.0 + */ + public function remove_options() { + $this->_options = array(); + } + + /** + * Get the options registered for the screen. + * + * @since 3.8.0 + * + * @return array Options with arguments. + */ + public function get_options() { + return $this->_options; + } + + /** + * Gets the arguments for an option for the screen. + * + * @since 3.3.0 + * + * @param string $option Option name. + * @param string $key Optional. Specific array key for when the option is an array. + * Default false. + * @return string The option value if set, null otherwise. + */ + public function get_option( $option, $key = false ) { + if ( ! isset( $this->_options[ $option ] ) ) + return null; + if ( $key ) { + if ( isset( $this->_options[ $option ][ $key ] ) ) + return $this->_options[ $option ][ $key ]; + return null; + } + return $this->_options[ $option ]; + } + + /** + * Gets the help tabs registered for the screen. + * + * @since 3.4.0 + * + * @return array Help tabs with arguments. + */ + public function get_help_tabs() { + return $this->_help_tabs; + } + + /** + * Gets the arguments for a help tab. + * + * @since 3.4.0 + * + * @param string $id Help Tab ID. + * @return array Help tab arguments. + */ + public function get_help_tab( $id ) { + if ( ! isset( $this->_help_tabs[ $id ] ) ) + return null; + return $this->_help_tabs[ $id ]; + } + + /** + * Add a help tab to the contextual help for the screen. + * Call this on the load-$pagenow hook for the relevant screen. + * + * @since 3.3.0 + * + * @param array $args + * - string - title - Title for the tab. + * - string - id - Tab ID. Must be HTML-safe. + * - string - content - Help tab content in plain text or HTML. Optional. + * - callback - callback - A callback to generate the tab content. Optional. + * + */ + public function add_help_tab( $args ) { + $defaults = array( + 'title' => false, + 'id' => false, + 'content' => '', + 'callback' => false, + ); + $args = wp_parse_args( $args, $defaults ); + + $args['id'] = sanitize_html_class( $args['id'] ); + + // Ensure we have an ID and title. + if ( ! $args['id'] || ! $args['title'] ) + return; + + // Allows for overriding an existing tab with that ID. + $this->_help_tabs[ $args['id'] ] = $args; + } + + /** + * Removes a help tab from the contextual help for the screen. + * + * @since 3.3.0 + * + * @param string $id The help tab ID. + */ + public function remove_help_tab( $id ) { + unset( $this->_help_tabs[ $id ] ); + } + + /** + * Removes all help tabs from the contextual help for the screen. + * + * @since 3.3.0 + */ + public function remove_help_tabs() { + $this->_help_tabs = array(); + } + + /** + * Gets the content from a contextual help sidebar. + * + * @since 3.4.0 + * + * @return string Contents of the help sidebar. + */ + public function get_help_sidebar() { + return $this->_help_sidebar; + } + + /** + * Add a sidebar to the contextual help for the screen. + * Call this in template files after admin.php is loaded and before admin-header.php is loaded to add a sidebar to the contextual help. + * + * @since 3.3.0 + * + * @param string $content Sidebar content in plain text or HTML. + */ + public function set_help_sidebar( $content ) { + $this->_help_sidebar = $content; + } + + /** + * Gets the number of layout columns the user has selected. + * + * The layout_columns option controls the max number and default number of + * columns. This method returns the number of columns within that range selected + * by the user via Screen Options. If no selection has been made, the default + * provisioned in layout_columns is returned. If the screen does not support + * selecting the number of layout columns, 0 is returned. + * + * @since 3.4.0 + * + * @return int Number of columns to display. + */ + public function get_columns() { + return $this->columns; + } + + /** + * Render the screen's help section. + * + * This will trigger the deprecated filters for backwards compatibility. + * + * @since 3.3.0 + */ + public function render_screen_meta() { + + /** + * Filter the legacy contextual help list. + * + * @since 2.7.0 + * @deprecated 3.3.0 Use get_current_screen()->add_help_tab() or + * get_current_screen()->remove_help_tab() instead. + * + * @param array $old_compat_help Old contextual help. + * @param WP_Screen $this Current WP_Screen instance. + */ + self::$_old_compat_help = apply_filters( 'contextual_help_list', self::$_old_compat_help, $this ); + + $old_help = isset( self::$_old_compat_help[ $this->id ] ) ? self::$_old_compat_help[ $this->id ] : ''; + + /** + * Filter the legacy contextual help text. + * + * @since 2.7.0 + * @deprecated 3.3.0 Use get_current_screen()->add_help_tab() or + * get_current_screen()->remove_help_tab() instead. + * + * @param string $old_help Help text that appears on the screen. + * @param string $screen_id Screen ID. + * @param WP_Screen $this Current WP_Screen instance. + * + */ + $old_help = apply_filters( 'contextual_help', $old_help, $this->id, $this ); + + // Default help only if there is no old-style block of text and no new-style help tabs. + if ( empty( $old_help ) && ! $this->get_help_tabs() ) { + + /** + * Filter the default legacy contextual help text. + * + * @since 2.8.0 + * @deprecated 3.3.0 Use get_current_screen()->add_help_tab() or + * get_current_screen()->remove_help_tab() instead. + * + * @param string $old_help_default Default contextual help text. + */ + $default_help = apply_filters( 'default_contextual_help', '' ); + if ( $default_help ) + $old_help = '

    ' . $default_help . '

    '; + } + + if ( $old_help ) { + $this->add_help_tab( array( + 'id' => 'old-contextual-help', + 'title' => __('Overview'), + 'content' => $old_help, + ) ); + } + + $help_sidebar = $this->get_help_sidebar(); + + $help_class = 'hidden'; + if ( ! $help_sidebar ) + $help_class .= ' no-sidebar'; + + // Time to render! + ?> +
    + +
    +
    +
    +
    +
      + get_help_tabs() as $tab ) : + $link_id = "tab-link-{$tab['id']}"; + $panel_id = "tab-panel-{$tab['id']}"; + ?> + + + +
    +
    + + +
    + +
    + + +
    + get_help_tabs() as $tab ): + $panel_id = "tab-panel-{$tab['id']}"; + ?> + +
    + +
    + +
    +
    +
    + id, $this ); + + if ( ! empty( $columns ) && isset( $columns[ $this->id ] ) ) + $this->add_option( 'layout_columns', array('max' => $columns[ $this->id ] ) ); + + if ( $this->get_option( 'layout_columns' ) ) { + $this->columns = (int) get_user_option("screen_layout_$this->id"); + + if ( ! $this->columns && $this->get_option( 'layout_columns', 'default' ) ) + $this->columns = $this->get_option( 'layout_columns', 'default' ); + } + $GLOBALS[ 'screen_layout_columns' ] = $this->columns; // Set the global for back-compat. + + // Add screen options + if ( $this->show_screen_options() ) + $this->render_screen_options(); + ?> +
    + get_help_tabs() && ! $this->show_screen_options() ) + return; + ?> + + _show_screen_options ) ) + return $this->_show_screen_options; + + $columns = get_column_headers( $this ); + + $show_screen = ! empty( $wp_meta_boxes[ $this->id ] ) || $columns || $this->get_option( 'per_page' ); + + switch ( $this->base ) { + case 'widgets': + $this->_screen_settings = '

    ' . __('Enable accessibility mode') . '' . __('Disable accessibility mode') . "

    \n"; + break; + case 'post' : + $expand = ''; + $this->_screen_settings = $expand; + break; + default: + $this->_screen_settings = ''; + break; + } + + /** + * Filter the screen settings text displayed in the Screen Options tab. + * + * This filter is currently only used on the Widgets screen to enable + * accessibility mode. + * + * @since 3.0.0 + * + * @param string $screen_settings Screen settings. + * @param WP_Screen $this WP_Screen object. + */ + $this->_screen_settings = apply_filters( 'screen_settings', $this->_screen_settings, $this ); + + if ( $this->_screen_settings || $this->_options ) + $show_screen = true; + + /** + * Filter whether to show the Screen Options tab. + * + * @since 3.2.0 + * + * @param bool $show_screen Whether to show Screen Options tab. + * Default true. + * @param WP_Screen $this Current WP_Screen instance. + */ + $this->_show_screen_options = apply_filters( 'screen_options_show_screen', $show_screen, $this ); + return $this->_show_screen_options; + } + + /** + * Render the screen options tab. + * + * @since 3.3.0 + */ + public function render_screen_options() { + global $wp_meta_boxes; + + $columns = get_column_headers( $this ); + $hidden = get_hidden_columns( $this ); + + ?> + + get_option('layout_columns') ) + return; + + $screen_layout_columns = $this->get_columns(); + $num = $this->get_option( 'layout_columns', 'max' ); + + ?> +
    +
    + + +
    + get_option( 'per_page' ) ) + return; + + $per_page_label = $this->get_option( 'per_page', 'label' ); + + $option = $this->get_option( 'per_page', 'option' ); + if ( ! $option ) + $option = str_replace( '-', '_', "{$this->id}_per_page" ); + + $per_page = (int) get_user_option( $option ); + if ( empty( $per_page ) || $per_page < 1 ) { + $per_page = $this->get_option( 'per_page', 'default' ); + if ( ! $per_page ) + $per_page = 20; + } + + if ( 'edit_comments_per_page' == $option ) { + $comment_status = isset( $_REQUEST['comment_status'] ) ? $_REQUEST['comment_status'] : 'all'; + + /** This filter is documented in wp-admin/includes/class-wp-comments-list-table.php */ + $per_page = apply_filters( 'comments_per_page', $per_page, $comment_status ); + } elseif ( 'categories_per_page' == $option ) { + /** This filter is documented in wp-admin/includes/class-wp-terms-list-table.php */ + $per_page = apply_filters( 'edit_categories_per_page', $per_page ); + } else { + /** This filter is documented in wp-admin/includes/class-wp-list-table.php */ + $per_page = apply_filters( $option, $per_page ); + } + + // Back compat + if ( isset( $this->post_type ) ) { + /** This filter is documented in wp-admin/includes/class-wp-posts-list-table.php */ + $per_page = apply_filters( 'edit_posts_per_page', $per_page, $this->post_type ); + } + + ?> +
    + + + + + +
    + $cat_name, 'category_parent' => $parent) ); +} + +/** + * Create categories for the given post. + * + * @since 2.0.0 + * + * @param array $categories List of categories to create. + * @param int $post_id Optional. The post ID. Default empty. + * @return List of categories to create for the given post. + */ +function wp_create_categories( $categories, $post_id = '' ) { + $cat_ids = array (); + foreach ($categories as $category) { + if ($id = category_exists($category)) + $cat_ids[] = $id; + else + if ($id = wp_create_category($category)) + $cat_ids[] = $id; + } + + if ( $post_id ) + wp_set_post_categories($post_id, $cat_ids); + + return $cat_ids; +} + +/** + * Updates an existing Category or creates a new Category. + * + * @since 2.0.0 + * @since 2.5.0 $wp_error parameter was added. + * @since 3.0.0 The 'taxonomy' argument was added. + * + * @param array $catarr { + * Array of arguments for inserting a new category. + * + * @type int $cat_ID Categoriy ID. A non-zero value updates an existing category. + * Default 0. + * @type string $taxonomy Taxonomy slug. Defualt 'category'. + * @type string $cat_nam Category name. Default empty. + * @type string $category_description Category description. Default empty. + * @type string $category_nicename Category nice (display) name. Default empty. + * @type int|string $category_parent Category parent ID. Default empty. + * } + * @param bool $wp_error Optional. Default false. + * @return int|object The ID number of the new or updated Category on success. Zero or a WP_Error on failure, + * depending on param $wp_error. + */ +function wp_insert_category( $catarr, $wp_error = false ) { + $cat_defaults = array( 'cat_ID' => 0, 'taxonomy' => 'category', 'cat_name' => '', 'category_description' => '', 'category_nicename' => '', 'category_parent' => '' ); + $catarr = wp_parse_args( $catarr, $cat_defaults ); + + if ( trim( $catarr['cat_name'] ) == '' ) { + if ( ! $wp_error ) { + return 0; + } else { + return new WP_Error( 'cat_name', __( 'You did not enter a category name.' ) ); + } + } + + $catarr['cat_ID'] = (int) $catarr['cat_ID']; + + // Are we updating or creating? + $update = ! empty ( $catarr['cat_ID'] ); + + $name = $catarr['cat_name']; + $description = $catarr['category_description']; + $slug = $catarr['category_nicename']; + $parent = (int) $catarr['category_parent']; + if ( $parent < 0 ) { + $parent = 0; + } + + if ( empty( $parent ) + || ! term_exists( $parent, $catarr['taxonomy'] ) + || ( $catarr['cat_ID'] && term_is_ancestor_of( $catarr['cat_ID'], $parent, $catarr['taxonomy'] ) ) ) { + $parent = 0; + } + + $args = compact('name', 'slug', 'parent', 'description'); + + if ( $update ) { + $catarr['cat_ID'] = wp_update_term( $catarr['cat_ID'], $catarr['taxonomy'], $args ); + } else { + $catarr['cat_ID'] = wp_insert_term( $catarr['cat_name'], $catarr['taxonomy'], $args ); + } + + if ( is_wp_error( $catarr['cat_ID'] ) ) { + if ( $wp_error ) { + return $catarr['cat_ID']; + } else { + return 0; + } + } + return $catarr['cat_ID']['term_id']; +} + +/** + * Aliases wp_insert_category() with minimal args. + * + * If you want to update only some fields of an existing category, call this + * function with only the new values set inside $catarr. + * + * @since 2.0.0 + * + * @param array $catarr The 'cat_ID' value is required. All other keys are optional. + * @return int|bool The ID number of the new or updated Category on success. Zero or FALSE on failure. + */ +function wp_update_category($catarr) { + $cat_ID = (int) $catarr['cat_ID']; + + if ( isset($catarr['category_parent']) && ($cat_ID == $catarr['category_parent']) ) + return false; + + // First, get all of the original fields + $category = get_term( $cat_ID, 'category', ARRAY_A ); + _make_cat_compat( $category ); + + // Escape data pulled from DB. + $category = wp_slash($category); + + // Merge old and new fields with new fields overwriting old ones. + $catarr = array_merge($category, $catarr); + + return wp_insert_category($catarr); +} + +// +// Tags +// + +/** + * {@internal Missing Short Description}} + * + * @since 2.3.0 + * + * @param int|string $tag_name + * @return mixed + */ +function tag_exists($tag_name) { + return term_exists($tag_name, 'post_tag'); +} + +/** + * {@internal Missing Short Description}} + * + * @since 2.3.0 + * + * @param int|string $tag_name + * @return array|WP_Error + */ +function wp_create_tag($tag_name) { + return wp_create_term( $tag_name, 'post_tag'); +} + +/** + * {@internal Missing Short Description}} + * + * @since 2.3.0 + * + * @param int $post_id + * @return string|bool|WP_Error + */ +function get_tags_to_edit( $post_id, $taxonomy = 'post_tag' ) { + return get_terms_to_edit( $post_id, $taxonomy); +} + +/** + * {@internal Missing Short Description}} + * + * @since 2.8.0 + * + * @param int $post_id + * @return string|bool|WP_Error + */ +function get_terms_to_edit( $post_id, $taxonomy = 'post_tag' ) { + $post_id = (int) $post_id; + if ( !$post_id ) + return false; + + $terms = get_object_term_cache( $post_id, $taxonomy ); + if ( false === $terms ) { + $terms = wp_get_object_terms( $post_id, $taxonomy ); + wp_cache_add( $post_id, $terms, $taxonomy . '_relationships' ); + } + + if ( ! $terms ) { + return false; + } + if ( is_wp_error( $terms ) ) { + return $terms; + } + $term_names = array(); + foreach ( $terms as $term ) { + $term_names[] = $term->name; + } + + $terms_to_edit = esc_attr( join( ',', $term_names ) ); + + /** + * Filter the comma-separated list of terms available to edit. + * + * @since 2.8.0 + * + * @see get_terms_to_edit() + * + * @param array $terms_to_edit An array of terms. + * @param string $taxonomy The taxonomy for which to retrieve terms. Default 'post_tag'. + */ + $terms_to_edit = apply_filters( 'terms_to_edit', $terms_to_edit, $taxonomy ); + + return $terms_to_edit; +} + +/** + * {@internal Missing Short Description}} + * + * @since 2.8.0 + * + * @param int|string $tag_name + * @return array|WP_Error + */ +function wp_create_term($tag_name, $taxonomy = 'post_tag') { + if ( $id = term_exists($tag_name, $taxonomy) ) + return $id; + + return wp_insert_term($tag_name, $taxonomy); +} diff --git a/wp-admin/includes/template.php b/wp-admin/includes/template.php new file mode 100644 index 0000000..425e40b --- /dev/null +++ b/wp-admin/includes/template.php @@ -0,0 +1,2178 @@ + 'parent', 'id' => 'term_id'); //TODO: decouple this + + /** + * Starts the list before the elements are added. + * + * @see Walker:start_lvl() + * + * @since 2.5.1 + * + * @param string $output Passed by reference. Used to append additional content. + * @param int $depth Depth of category. Used for tab indentation. + * @param array $args An array of arguments. @see wp_terms_checklist() + */ + public function start_lvl( &$output, $depth = 0, $args = array() ) { + $indent = str_repeat("\t", $depth); + $output .= "$indent
      \n"; + } + + /** + * Ends the list of after the elements are added. + * + * @see Walker::end_lvl() + * + * @since 2.5.1 + * + * @param string $output Passed by reference. Used to append additional content. + * @param int $depth Depth of category. Used for tab indentation. + * @param array $args An array of arguments. @see wp_terms_checklist() + */ + public function end_lvl( &$output, $depth = 0, $args = array() ) { + $indent = str_repeat("\t", $depth); + $output .= "$indent
    \n"; + } + + /** + * Start the element output. + * + * @see Walker::start_el() + * + * @since 2.5.1 + * + * @param string $output Passed by reference. Used to append additional content. + * @param object $category The current term object. + * @param int $depth Depth of the term in reference to parents. Default 0. + * @param array $args An array of arguments. @see wp_terms_checklist() + * @param int $id ID of the current term. + */ + public function start_el( &$output, $category, $depth = 0, $args = array(), $id = 0 ) { + if ( empty( $args['taxonomy'] ) ) { + $taxonomy = 'category'; + } else { + $taxonomy = $args['taxonomy']; + } + + if ( $taxonomy == 'category' ) { + $name = 'post_category'; + } else { + $name = 'tax_input[' . $taxonomy . ']'; + } + $args['popular_cats'] = empty( $args['popular_cats'] ) ? array() : $args['popular_cats']; + $class = in_array( $category->term_id, $args['popular_cats'] ) ? ' class="popular-category"' : ''; + + $args['selected_cats'] = empty( $args['selected_cats'] ) ? array() : $args['selected_cats']; + + /** This filter is documented in wp-includes/category-template.php */ + $output .= "\n
  • " . + ''; + } + + /** + * Ends the element output, if needed. + * + * @see Walker::end_el() + * + * @since 2.5.1 + * + * @param string $output Passed by reference. Used to append additional content. + * @param object $category The current term object. + * @param int $depth Depth of the term in reference to parents. Default 0. + * @param array $args An array of arguments. @see wp_terms_checklist() + */ + public function end_el( &$output, $category, $depth = 0, $args = array() ) { + $output .= "
  • \n"; + } +} + +/** + * Output an unordered list of checkbox input elements labelled + * with category names. + * + * @since 2.5.1 + * + * @todo Properly document optional arguments as such. + * + * @see wp_terms_checklist() + * + * @param int $post_id Mark categories associated with this post as checked. $selected_cats must not be an array. + * @param int $descendants_and_self ID of the category to output along with its descendents. + * @param bool|array $selected_cats List of categories to mark as checked. + * @param bool|array $popular_cats Override the list of categories that receive the "popular-category" class. + * @param object $walker Walker object to use to build the output. + * @param bool $checked_ontop Move checked items out of the hierarchy and to the top of the list. + */ +function wp_category_checklist( $post_id = 0, $descendants_and_self = 0, $selected_cats = false, $popular_cats = false, $walker = null, $checked_ontop = true ) { + wp_terms_checklist( $post_id, array( + 'taxonomy' => 'category', + 'descendants_and_self' => $descendants_and_self, + 'selected_cats' => $selected_cats, + 'popular_cats' => $popular_cats, + 'walker' => $walker, + 'checked_ontop' => $checked_ontop + ) ); +} + +/** + * Output an unordered list of checkbox input elements labelled with term names. + * + * Taxonomy independent version of {@see wp_category_checklist()}. + * + * @since 3.0.0 + * + * @todo Properly document optional default arguments. + * + * @param int $post_id Post ID. + * @param array $args Arguments to form the terms checklist. + */ +function wp_terms_checklist( $post_id = 0, $args = array() ) { + $defaults = array( + 'descendants_and_self' => 0, + 'selected_cats' => false, + 'popular_cats' => false, + 'walker' => null, + 'taxonomy' => 'category', + 'checked_ontop' => true + ); + + /** + * Filter the taxonomy terms checklist arguments. + * + * @since 3.4.0 + * + * @see wp_terms_checklist() + * + * @param array $args An array of arguments. + * @param int $post_id The post ID. + */ + $params = apply_filters( 'wp_terms_checklist_args', $args, $post_id ); + + $r = wp_parse_args( $params, $defaults ); + + if ( empty( $r['walker'] ) || ! is_a( $r['walker'], 'Walker' ) ) { + $walker = new Walker_Category_Checklist; + } else { + $walker = $r['walker']; + } + + $taxonomy = $r['taxonomy']; + $descendants_and_self = (int) $r['descendants_and_self']; + + $args = array( 'taxonomy' => $taxonomy ); + + $tax = get_taxonomy( $taxonomy ); + $args['disabled'] = ! current_user_can( $tax->cap->assign_terms ); + + if ( is_array( $r['selected_cats'] ) ) { + $args['selected_cats'] = $r['selected_cats']; + } elseif ( $post_id ) { + $args['selected_cats'] = wp_get_object_terms( $post_id, $taxonomy, array_merge( $args, array( 'fields' => 'ids' ) ) ); + } else { + $args['selected_cats'] = array(); + } + if ( is_array( $r['popular_cats'] ) ) { + $args['popular_cats'] = $r['popular_cats']; + } else { + $args['popular_cats'] = get_terms( $taxonomy, array( + 'fields' => 'ids', + 'orderby' => 'count', + 'order' => 'DESC', + 'number' => 10, + 'hierarchical' => false + ) ); + } + if ( $descendants_and_self ) { + $categories = (array) get_terms( $taxonomy, array( + 'child_of' => $descendants_and_self, + 'hierarchical' => 0, + 'hide_empty' => 0 + ) ); + $self = get_term( $descendants_and_self, $taxonomy ); + array_unshift( $categories, $self ); + } else { + $categories = (array) get_terms( $taxonomy, array( 'get' => 'all' ) ); + } + + if ( $r['checked_ontop'] ) { + // Post process $categories rather than adding an exclude to the get_terms() query to keep the query the same across all posts (for any query cache) + $checked_categories = array(); + $keys = array_keys( $categories ); + + foreach( $keys as $k ) { + if ( in_array( $categories[$k]->term_id, $args['selected_cats'] ) ) { + $checked_categories[] = $categories[$k]; + unset( $categories[$k] ); + } + } + + // Put checked cats on top + echo call_user_func_array( array( $walker, 'walk' ), array( $checked_categories, 0, $args ) ); + } + // Then the rest of them + echo call_user_func_array( array( $walker, 'walk' ), array( $categories, 0, $args ) ); +} + +/** + * Retrieve a list of the most popular terms from the specified taxonomy. + * + * If the $echo argument is true then the elements for a list of checkbox + * `` elements labelled with the names of the selected terms is output. + * If the $post_ID global isn't empty then the terms associated with that + * post will be marked as checked. + * + * @since 2.5.0 + * + * @param string $taxonomy Taxonomy to retrieve terms from. + * @param int $default Unused. + * @param int $number Number of terms to retrieve. Defaults to 10. + * @param bool $echo Optionally output the list as well. Defaults to true. + * @return array List of popular term IDs. + */ +function wp_popular_terms_checklist( $taxonomy, $default = 0, $number = 10, $echo = true ) { + $post = get_post(); + + if ( $post && $post->ID ) + $checked_terms = wp_get_object_terms($post->ID, $taxonomy, array('fields'=>'ids')); + else + $checked_terms = array(); + + $terms = get_terms( $taxonomy, array( 'orderby' => 'count', 'order' => 'DESC', 'number' => $number, 'hierarchical' => false ) ); + + $tax = get_taxonomy($taxonomy); + + $popular_ids = array(); + foreach ( (array) $terms as $term ) { + $popular_ids[] = $term->term_id; + if ( !$echo ) // hack for AJAX use + continue; + $id = "popular-$taxonomy-$term->term_id"; + $checked = in_array( $term->term_id, $checked_terms ) ? 'checked="checked"' : ''; + ?> + + + + 'name', 'hide_empty' => 0 ) ); + + if ( empty( $categories ) ) + return; + + foreach ( $categories as $category ) { + $cat_id = $category->term_id; + + /** This filter is documented in wp-includes/category-template.php */ + $name = esc_html( apply_filters( 'the_category', $category->name ) ); + $checked = in_array( $cat_id, $checked_categories ) ? ' checked="checked"' : ''; + echo '"; + } +} + +// adds hidden fields with the data for use in the inline editor for posts and pages +/** + * {@internal Missing Short Description}} + * + * @since 2.7.0 + * + * @param WP_Post $post + */ +function get_inline_data($post) { + $post_type_object = get_post_type_object($post->post_type); + if ( ! current_user_can( 'edit_post', $post->ID ) ) + return; + + $title = esc_textarea( trim( $post->post_title ) ); + + /** This filter is documented in wp-admin/edit-tag-form.php */ + echo ' +'; +} + +/** + * {@internal Missing Short Description}} + * + * @since 2.7.0 + * + * @param int $position + * @param bool $checkbox + * @param string $mode + * @param bool $table_row + */ +function wp_comment_reply($position = '1', $checkbox = false, $mode = 'single', $table_row = true) { + + /** + * Filter the in-line comment reply-to form output in the Comments + * list table. + * + * Returning a non-empty value here will short-circuit display + * of the in-line comment-reply form in the Comments list table, + * echoing the returned value instead. + * + * @since 2.7.0 + * + * @see wp_comment_reply() + * + * @param string $content The reply-to form content. + * @param array $args An array of default args. + */ + $content = apply_filters( 'wp_comment_reply', '', array( 'position' => $position, 'checkbox' => $checkbox, 'mode' => $mode ) ); + + if ( ! empty($content) ) { + echo $content; + return; + } + + if ( $mode == 'single' ) { + $wp_list_table = _get_list_table('WP_Post_Comments_List_Table'); + } else { + $wp_list_table = _get_list_table('WP_Comments_List_Table'); + } + +?> +
    + +
    + + + +
    + + + + + + + ' . _x( 'Name', 'meta name' ) . ' + ' . __( 'Value' ) . ' + + + + + +'; //TBODY needed for list-manipulation JS + return; + } + $count = 0; +?> + + + + + + + + + + +
    + + $entry['meta_id'] = (int) $entry['meta_id']; + + $delete_nonce = wp_create_nonce( 'delete-meta_' . $entry['meta_id'] ); + + $r .= "\n\t"; + $r .= "\n\t\t"; + + $r .= "\n\t\t
    "; + $r .= get_submit_button( __( 'Delete' ), 'deletemeta small', "deletemeta[{$entry['meta_id']}]", false, array( 'data-wp-lists' => "delete:the-list:meta-{$entry['meta_id']}::_ajax_nonce=$delete_nonce" ) ); + $r .= "\n\t\t"; + $r .= get_submit_button( __( 'Update' ), 'updatemeta small', "meta-{$entry['meta_id']}-submit", false, array( 'data-wp-lists' => "add:the-list:meta-{$entry['meta_id']}::_ajax_nonce-add-meta=$update_nonce" ) ); + $r .= "
    "; + $r .= wp_nonce_field( 'change-meta', '_ajax_nonce', false, false ); + $r .= ""; + + $r .= "\n\t\t\n\t"; + return $r; +} + +/** + * Prints the form in the Custom Fields meta box. + * + * @since 1.2.0 + * + * @param WP_Post $post Optional. The post being edited. + */ +function meta_form( $post = null ) { + global $wpdb; + $post = get_post( $post ); + + /** + * Filter the number of custom fields to retrieve for the drop-down + * in the Custom Fields meta box. + * + * @since 2.1.0 + * + * @param int $limit Number of custom fields to retrieve. Default 30. + */ + $limit = apply_filters( 'postmeta_form_limit', 30 ); + $sql = "SELECT meta_key + FROM $wpdb->postmeta + GROUP BY meta_key + HAVING meta_key NOT LIKE %s + ORDER BY meta_key + LIMIT %d"; + $keys = $wpdb->get_col( $wpdb->prepare( $sql, $wpdb->esc_like( '_' ) . '%', $limit ) ); + if ( $keys ) { + natcasesort( $keys ); + $meta_key_input_id = 'metakeyselect'; + } else { + $meta_key_input_id = 'metakeyinput'; + } +?> +

    + + + + + + + + + + + + + + + + +
    + + + + + + + + + +
    +
    + 'newmeta-submit', 'data-wp-lists' => 'add:the-list:newmeta' ) ); ?> +
    + +
    +post_status, array('draft', 'pending') ) && (!$post->post_date_gmt || '0000-00-00 00:00:00' == $post->post_date_gmt ) ); + + $tab_index_attribute = ''; + if ( (int) $tab_index > 0 ) + $tab_index_attribute = " tabindex=\"$tab_index\""; + + // todo: Remove this? + // echo '
    '; + + $time_adj = current_time('timestamp'); + $post_date = ($for_post) ? $post->post_date : $comment->comment_date; + $jj = ($edit) ? mysql2date( 'd', $post_date, false ) : gmdate( 'd', $time_adj ); + $mm = ($edit) ? mysql2date( 'm', $post_date, false ) : gmdate( 'm', $time_adj ); + $aa = ($edit) ? mysql2date( 'Y', $post_date, false ) : gmdate( 'Y', $time_adj ); + $hh = ($edit) ? mysql2date( 'H', $post_date, false ) : gmdate( 'H', $time_adj ); + $mn = ($edit) ? mysql2date( 'i', $post_date, false ) : gmdate( 'i', $time_adj ); + $ss = ($edit) ? mysql2date( 's', $post_date, false ) : gmdate( 's', $time_adj ); + + $cur_jj = gmdate( 'd', $time_adj ); + $cur_mm = gmdate( 'm', $time_adj ); + $cur_aa = gmdate( 'Y', $time_adj ); + $cur_hh = gmdate( 'H', $time_adj ); + $cur_mn = gmdate( 'i', $time_adj ); + + $month = ''; + + $day = ''; + $year = ''; + $hour = ''; + $minute = ''; + + echo '
    '; + /* translators: 1: month, 2: day, 3: year, 4: hour, 5: minute */ + printf( __( '%1$s %2$s, %3$s @ %4$s : %5$s' ), $month, $day, $year, $hour, $minute ); + + echo '
    '; + + if ( $multi ) return; + + echo "\n\n"; + $map = array( + 'mm' => array( $mm, $cur_mm ), + 'jj' => array( $jj, $cur_jj ), + 'aa' => array( $aa, $cur_aa ), + 'hh' => array( $hh, $cur_hh ), + 'mn' => array( $mn, $cur_mn ), + ); + foreach ( $map as $timeunit => $value ) { + list( $unit, $curr ) = $value; + + echo '' . "\n"; + $cur_timeunit = 'cur_' . $timeunit; + echo '' . "\n"; + } +?> + +

    + + +

    +$template"; + } +} + +/** + * Print out option HTML elements for the page parents drop-down. + * + * @since 1.5.0 + * + * @param int $default Optional. The default page ID to be pre-selected. Default 0. + * @param int $parent Optional. The parent page ID. Default 0. + * @param int $level Optional. Page depth level. Default 0. + * + * @return null|false Boolean False if page has no children, otherwise print out html elements + */ +function parent_dropdown( $default = 0, $parent = 0, $level = 0 ) { + global $wpdb; + $post = get_post(); + $items = $wpdb->get_results( $wpdb->prepare("SELECT ID, post_parent, post_title FROM $wpdb->posts WHERE post_parent = %d AND post_type = 'page' ORDER BY menu_order", $parent) ); + + if ( $items ) { + foreach ( $items as $item ) { + // A page cannot be its own parent. + if ( $post && $post->ID && $item->ID == $post->ID ) + continue; + + $pad = str_repeat( ' ', $level * 3 ); + $selected = selected( $default, $item->ID, false ); + + echo "\n\t"; + parent_dropdown( $default, $item->ID, $level +1 ); + } + } else { + return false; + } +} + +/** + * Print out option html elements for role selectors. + * + * @since 2.1.0 + * + * @param string $selected slug for the role that should be already selected + */ +function wp_dropdown_roles( $selected = false ) { + $p = ''; + $r = ''; + + $editable_roles = array_reverse( get_editable_roles() ); + + foreach ( $editable_roles as $role => $details ) { + $name = translate_user_role($details['name'] ); + if ( $selected == $role ) // preselect specified role + $p = "\n\t"; + else + $r .= "\n\t"; + } + echo $p . $r; +} + +/** + * Outputs the form used by the importers to accept the data to be imported + * + * @since 2.0.0 + * + * @param string $action The action attribute for the form. + */ +function wp_import_upload_form( $action ) { + + /** + * Filter the maximum allowed upload size for import files. + * + * @since 2.3.0 + * + * @see wp_max_upload_size() + * + * @param int $max_upload_size Allowed upload size. Default 1 MB. + */ + $bytes = apply_filters( 'import_upload_size_limit', wp_max_upload_size() ); + $size = size_format( $bytes ); + $upload_dir = wp_upload_dir(); + if ( ! empty( $upload_dir['error'] ) ) : + ?>

    +

    +
    +

    + () + + + +

    + +
    +id; + + if ( !isset($wp_meta_boxes) ) + $wp_meta_boxes = array(); + if ( !isset($wp_meta_boxes[$page]) ) + $wp_meta_boxes[$page] = array(); + if ( !isset($wp_meta_boxes[$page][$context]) ) + $wp_meta_boxes[$page][$context] = array(); + + foreach ( array_keys($wp_meta_boxes[$page]) as $a_context ) { + foreach ( array('high', 'core', 'default', 'low') as $a_priority ) { + if ( !isset($wp_meta_boxes[$page][$a_context][$a_priority][$id]) ) + continue; + + // If a core box was previously added or removed by a plugin, don't add. + if ( 'core' == $priority ) { + // If core box previously deleted, don't add + if ( false === $wp_meta_boxes[$page][$a_context][$a_priority][$id] ) + return; + + /* + * If box was added with default priority, give it core priority to + * maintain sort order. + */ + if ( 'default' == $a_priority ) { + $wp_meta_boxes[$page][$a_context]['core'][$id] = $wp_meta_boxes[$page][$a_context]['default'][$id]; + unset($wp_meta_boxes[$page][$a_context]['default'][$id]); + } + return; + } + // If no priority given and id already present, use existing priority. + if ( empty($priority) ) { + $priority = $a_priority; + /* + * Else, if we're adding to the sorted priority, we don't know the title + * or callback. Grab them from the previously added context/priority. + */ + } elseif ( 'sorted' == $priority ) { + $title = $wp_meta_boxes[$page][$a_context][$a_priority][$id]['title']; + $callback = $wp_meta_boxes[$page][$a_context][$a_priority][$id]['callback']; + $callback_args = $wp_meta_boxes[$page][$a_context][$a_priority][$id]['args']; + } + // An id can be in only one priority and one context. + if ( $priority != $a_priority || $context != $a_context ) + unset($wp_meta_boxes[$page][$a_context][$a_priority][$id]); + } + } + + if ( empty($priority) ) + $priority = 'low'; + + if ( !isset($wp_meta_boxes[$page][$context][$priority]) ) + $wp_meta_boxes[$page][$context][$priority] = array(); + + $wp_meta_boxes[$page][$context][$priority][$id] = array('id' => $id, 'title' => $title, 'callback' => $callback, 'args' => $callback_args); +} + +/** + * Meta-Box template function + * + * @since 2.5.0 + * + * @staticvar bool $already_sorted + * @param string|WP_Screen $screen Screen identifier + * @param string $context box context + * @param mixed $object gets passed to the box callback function as first parameter + * @return int number of meta_boxes + */ +function do_meta_boxes( $screen, $context, $object ) { + global $wp_meta_boxes; + static $already_sorted = false; + + if ( empty( $screen ) ) + $screen = get_current_screen(); + elseif ( is_string( $screen ) ) + $screen = convert_to_screen( $screen ); + + $page = $screen->id; + + $hidden = get_hidden_meta_boxes( $screen ); + + printf('
    ', htmlspecialchars($context)); + + // Grab the ones the user has manually sorted. Pull them out of their previous context/priority and into the one the user chose + if ( ! $already_sorted && $sorted = get_user_option( "meta-box-order_$page" ) ) { + foreach ( $sorted as $box_context => $ids ) { + foreach ( explode( ',', $ids ) as $id ) { + if ( $id && 'dashboard_browser_nag' !== $id ) { + add_meta_box( $id, null, null, $screen, $box_context, 'sorted' ); + } + } + } + } + + $already_sorted = true; + + $i = 0; + + if ( isset( $wp_meta_boxes[ $page ][ $context ] ) ) { + foreach ( array( 'high', 'sorted', 'core', 'default', 'low' ) as $priority ) { + if ( isset( $wp_meta_boxes[ $page ][ $context ][ $priority ]) ) { + foreach ( (array) $wp_meta_boxes[ $page ][ $context ][ $priority ] as $box ) { + if ( false == $box || ! $box['title'] ) + continue; + $i++; + $hidden_class = in_array($box['id'], $hidden) ? ' hide-if-js' : ''; + echo '
    ' . "\n"; + if ( 'dashboard_browser_nag' != $box['id'] ) + echo '

    '; + echo "

    {$box['title']}

    \n"; + echo '
    ' . "\n"; + call_user_func($box['callback'], $object, $box); + echo "
    \n"; + echo "
    \n"; + } + } + } + } + + echo "
    "; + + return $i; + +} + +/** + * Remove a meta box from an edit form. + * + * @since 2.6.0 + * + * @param string $id String for use in the 'id' attribute of tags. + * @param string|object $screen The screen on which to show the box (post, page, link). + * @param string $context The context within the page where the boxes should show ('normal', 'advanced'). + */ +function remove_meta_box($id, $screen, $context) { + global $wp_meta_boxes; + + if ( empty( $screen ) ) + $screen = get_current_screen(); + elseif ( is_string( $screen ) ) + $screen = convert_to_screen( $screen ); + + $page = $screen->id; + + if ( !isset($wp_meta_boxes) ) + $wp_meta_boxes = array(); + if ( !isset($wp_meta_boxes[$page]) ) + $wp_meta_boxes[$page] = array(); + if ( !isset($wp_meta_boxes[$page][$context]) ) + $wp_meta_boxes[$page][$context] = array(); + + foreach ( array('high', 'core', 'default', 'low') as $priority ) + $wp_meta_boxes[$page][$context][$priority][$id] = false; +} + +/** + * Meta Box Accordion Template Function + * + * Largely made up of abstracted code from {@link do_meta_boxes()}, this + * function serves to build meta boxes as list items for display as + * a collapsible accordion. + * + * @since 3.6.0 + * + * @uses global $wp_meta_boxes Used to retrieve registered meta boxes. + * + * @param string|object $screen The screen identifier. + * @param string $context The meta box context. + * @param mixed $object gets passed to the section callback function as first parameter. + * @return int number of meta boxes as accordion sections. + */ +function do_accordion_sections( $screen, $context, $object ) { + global $wp_meta_boxes; + + wp_enqueue_script( 'accordion' ); + + if ( empty( $screen ) ) + $screen = get_current_screen(); + elseif ( is_string( $screen ) ) + $screen = convert_to_screen( $screen ); + + $page = $screen->id; + + $hidden = get_hidden_meta_boxes( $screen ); + ?> +
    +
      + +
    • +

      + + +

      +
      +
      + +
      +
      +
    • + +
    +
    + $id, 'title' => $title, 'callback' => $callback); +} + +/** + * Add a new field to a section of a settings page + * + * Part of the Settings API. Use this to define a settings field that will show + * as part of a settings section inside a settings page. The fields are shown using + * do_settings_fields() in do_settings-sections() + * + * The $callback argument should be the name of a function that echoes out the + * html input tags for this setting field. Use get_option() to retrieve existing + * values to show. + * + * @since 2.7.0 + * + * @global $wp_settings_fields Storage array of settings fields and info about their pages/sections + * + * @param string $id Slug-name to identify the field. Used in the 'id' attribute of tags. + * @param string $title Formatted title of the field. Shown as the label for the field during output. + * @param string $callback Function that fills the field with the desired form inputs. The function should echo its output. + * @param string $page The slug-name of the settings page on which to show the section (general, reading, writing, ...). + * @param string $section The slug-name of the section of the settings page in which to show the box (default, ...). + * @param array $args Additional arguments + */ +function add_settings_field($id, $title, $callback, $page, $section = 'default', $args = array()) { + global $wp_settings_fields; + + if ( 'misc' == $page ) { + _deprecated_argument( __FUNCTION__, '3.0', __( 'The miscellaneous options group has been removed. Use another settings group.' ) ); + $page = 'general'; + } + + if ( 'privacy' == $page ) { + _deprecated_argument( __FUNCTION__, '3.5', __( 'The privacy options group has been removed. Use another settings group.' ) ); + $page = 'reading'; + } + + $wp_settings_fields[$page][$section][$id] = array('id' => $id, 'title' => $title, 'callback' => $callback, 'args' => $args); +} + +/** + * Prints out all settings sections added to a particular settings page + * + * Part of the Settings API. Use this in a settings page callback function + * to output all the sections and fields that were added to that $page with + * add_settings_section() and add_settings_field() + * + * @global $wp_settings_sections Storage array of all settings sections added to admin pages + * @global $wp_settings_fields Storage array of settings fields and info about their pages/sections + * @since 2.7.0 + * + * @param string $page The slug name of the page whos settings sections you want to output + */ +function do_settings_sections( $page ) { + global $wp_settings_sections, $wp_settings_fields; + + if ( ! isset( $wp_settings_sections[$page] ) ) + return; + + foreach ( (array) $wp_settings_sections[$page] as $section ) { + if ( $section['title'] ) + echo "

    {$section['title']}

    \n"; + + if ( $section['callback'] ) + call_user_func( $section['callback'], $section ); + + if ( ! isset( $wp_settings_fields ) || !isset( $wp_settings_fields[$page] ) || !isset( $wp_settings_fields[$page][$section['id']] ) ) + continue; + echo ''; + do_settings_fields( $page, $section['id'] ); + echo '
    '; + } +} + +/** + * Print out the settings fields for a particular settings section + * + * Part of the Settings API. Use this in a settings page to output + * a specific section. Should normally be called by do_settings_sections() + * rather than directly. + * + * @global $wp_settings_fields Storage array of settings fields and their pages/sections + * + * @since 2.7.0 + * + * @param string $page Slug title of the admin page who's settings fields you want to show. + * @param string $section Slug title of the settings section who's fields you want to show. + */ +function do_settings_fields($page, $section) { + global $wp_settings_fields; + + if ( ! isset( $wp_settings_fields[$page][$section] ) ) + return; + + foreach ( (array) $wp_settings_fields[$page][$section] as $field ) { + echo ''; + if ( !empty($field['args']['label_for']) ) + echo ''; + else + echo '' . $field['title'] . ''; + echo ''; + call_user_func($field['callback'], $field['args']); + echo ''; + echo ''; + } +} + +/** + * Register a settings error to be displayed to the user + * + * Part of the Settings API. Use this to show messages to users about settings validation + * problems, missing settings or anything else. + * + * Settings errors should be added inside the $sanitize_callback function defined in + * register_setting() for a given setting to give feedback about the submission. + * + * By default messages will show immediately after the submission that generated the error. + * Additional calls to settings_errors() can be used to show errors even when the settings + * page is first accessed. + * + * @since 3.0.0 + * + * @todo Properly document optional arguments as such. + * + * @global array $wp_settings_errors Storage array of errors registered during this pageload + * + * @param string $setting Slug title of the setting to which this error applies + * @param string $code Slug-name to identify the error. Used as part of 'id' attribute in HTML output. + * @param string $message The formatted message text to display to the user (will be shown inside styled + * `
    ` and `

    ` tags). + * @param string $type The type of message it is, controls HTML class. Use 'error' or 'updated'. + */ +function add_settings_error( $setting, $code, $message, $type = 'error' ) { + global $wp_settings_errors; + + $wp_settings_errors[] = array( + 'setting' => $setting, + 'code' => $code, + 'message' => $message, + 'type' => $type + ); +} + +/** + * Fetch settings errors registered by add_settings_error() + * + * Checks the $wp_settings_errors array for any errors declared during the current + * pageload and returns them. + * + * If changes were just submitted ($_GET['settings-updated']) and settings errors were saved + * to the 'settings_errors' transient then those errors will be returned instead. This + * is used to pass errors back across pageloads. + * + * Use the $sanitize argument to manually re-sanitize the option before returning errors. + * This is useful if you have errors or notices you want to show even when the user + * hasn't submitted data (i.e. when they first load an options page, or in admin_notices action hook) + * + * @since 3.0.0 + * + * @global array $wp_settings_errors Storage array of errors registered during this pageload + * + * @param string $setting Optional slug title of a specific setting who's errors you want. + * @param boolean $sanitize Whether to re-sanitize the setting value before returning errors. + * @return array Array of settings errors + */ +function get_settings_errors( $setting = '', $sanitize = false ) { + global $wp_settings_errors; + + /* + * If $sanitize is true, manually re-run the sanitization for this option + * This allows the $sanitize_callback from register_setting() to run, adding + * any settings errors you want to show by default. + */ + if ( $sanitize ) + sanitize_option( $setting, get_option( $setting ) ); + + // If settings were passed back from options.php then use them. + if ( isset( $_GET['settings-updated'] ) && $_GET['settings-updated'] && get_transient( 'settings_errors' ) ) { + $wp_settings_errors = array_merge( (array) $wp_settings_errors, get_transient( 'settings_errors' ) ); + delete_transient( 'settings_errors' ); + } + + // Check global in case errors have been added on this pageload. + if ( ! count( $wp_settings_errors ) ) + return array(); + + // Filter the results to those of a specific setting if one was set. + if ( $setting ) { + $setting_errors = array(); + foreach ( (array) $wp_settings_errors as $key => $details ) { + if ( $setting == $details['setting'] ) + $setting_errors[] = $wp_settings_errors[$key]; + } + return $setting_errors; + } + + return $wp_settings_errors; +} + +/** + * Display settings errors registered by {@see add_settings_error()}. + * + * Part of the Settings API. Outputs a div for each error retrieved by + * {@see get_settings_errors()}. + * + * This is called automatically after a settings page based on the + * Settings API is submitted. Errors should be added during the validation + * callback function for a setting defined in {@see register_setting()} + * + * The $sanitize option is passed into {@see get_settings_errors()} and will + * re-run the setting sanitization + * on its current value. + * + * The $hide_on_update option will cause errors to only show when the settings + * page is first loaded. if the user has already saved new values it will be + * hidden to avoid repeating messages already shown in the default error + * reporting after submission. This is useful to show general errors like + * missing settings when the user arrives at the settings page. + * + * @since 3.0.0 + * + * @param string $setting Optional slug title of a specific setting who's errors you want. + * @param boolean $sanitize Whether to re-sanitize the setting value before returning errors. + * @param boolean $hide_on_update If set to true errors will not be shown if the settings page has already been submitted. + */ +function settings_errors( $setting = '', $sanitize = false, $hide_on_update = false ) { + + if ( $hide_on_update && ! empty( $_GET['settings-updated'] ) ) + return; + + $settings_errors = get_settings_errors( $setting, $sanitize ); + + if ( empty( $settings_errors ) ) + return; + + $output = ''; + foreach ( $settings_errors as $key => $details ) { + $css_id = 'setting-error-' . $details['code']; + $css_class = $details['type'] . ' settings-error'; + $output .= "

    \n"; + $output .= "

    {$details['message']}

    "; + $output .= "
    \n"; + } + echo $output; +} + +/** + * {@internal Missing Short Description}} + * + * @since 2.7.0 + * + * @param string $found_action + */ +function find_posts_div($found_action = '') { +?> + +post_password ) ) + echo esc_attr( $post->post_password ); +} + +/** + * Get the post title. + * + * The post title is fetched and if it is blank then a default string is + * returned. + * + * @since 2.7.0 + * + * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post. + * @return string The post title if set. + */ +function _draft_or_post_title( $post = 0 ) { + $title = get_the_title( $post ); + if ( empty( $title ) ) + $title = __( '(no title)' ); + return $title; +} + +/** + * Display the search query. + * + * A simple wrapper to display the "s" parameter in a GET URI. This function + * should only be used when {@link the_search_query()} cannot. + * + * @since 2.7.0 + * + */ +function _admin_search_query() { + echo isset($_REQUEST['s']) ? esc_attr( wp_unslash( $_REQUEST['s'] ) ) : ''; +} + +/** + * Generic Iframe header for use with Thickbox + * + * @since 2.7.0 + * @param string $title Title of the Iframe page. + * @param bool $limit_styles Limit styles to colour-related styles only (unless others are enqueued). + * + */ +function iframe_header( $title = '', $limit_styles = false ) { + show_admin_bar( false ); + global $hook_suffix, $current_user, $admin_body_class, $wp_locale; + $admin_body_class = preg_replace('/[^a-z0-9_-]+/i', '-', $hook_suffix); + + $current_screen = get_current_screen(); + + @header( 'Content-Type: ' . get_option( 'html_type' ) . '; charset=' . get_option( 'blog_charset' ) ); + _wp_admin_html_begin(); +?> +<?php bloginfo('name') ?> › <?php echo $title ?> — <?php _e('WordPress'); ?> + + + + + + class="wp-admin wp-core-ui no-js iframe "> + + + + + + +post_password) ) + $post_states['protected'] = __('Password protected'); + if ( 'private' == $post->post_status && 'private' != $post_status ) + $post_states['private'] = __('Private'); + if ( 'draft' == $post->post_status && 'draft' != $post_status ) + $post_states['draft'] = __('Draft'); + if ( 'pending' == $post->post_status && 'pending' != $post_status ) + /* translators: post state */ + $post_states['pending'] = _x('Pending', 'post state'); + if ( is_sticky($post->ID) ) + $post_states['sticky'] = __('Sticky'); + + /** + * Filter the default post display states used in the Posts list table. + * + * @since 2.8.0 + * + * @param array $post_states An array of post display states. Values include 'Password protected', + * 'Private', 'Draft', 'Pending', and 'Sticky'. + * @param int $post The post ID. + */ + $post_states = apply_filters( 'display_post_states', $post_states, $post ); + + if ( ! empty($post_states) ) { + $state_count = count($post_states); + $i = 0; + echo ' - '; + foreach ( $post_states as $state ) { + ++$i; + ( $i == $state_count ) ? $sep = '' : $sep = ', '; + echo "$state$sep"; + } + } + +} + +function _media_states( $post ) { + $media_states = array(); + $stylesheet = get_option('stylesheet'); + + if ( current_theme_supports( 'custom-header') ) { + $meta_header = get_post_meta($post->ID, '_wp_attachment_is_custom_header', true ); + if ( ! empty( $meta_header ) && $meta_header == $stylesheet ) + $media_states[] = __( 'Header Image' ); + } + + if ( current_theme_supports( 'custom-background') ) { + $meta_background = get_post_meta($post->ID, '_wp_attachment_is_custom_background', true ); + if ( ! empty( $meta_background ) && $meta_background == $stylesheet ) + $media_states[] = __( 'Background Image' ); + } + + /** + * Filter the default media display states for items in the Media list table. + * + * @since 3.2.0 + * + * @param array $media_states An array of media states. Default 'Header Image', + * 'Background Image'. + */ + $media_states = apply_filters( 'display_media_states', $media_states ); + + if ( ! empty( $media_states ) ) { + $state_count = count( $media_states ); + $i = 0; + echo ' - '; + foreach ( $media_states as $state ) { + ++$i; + ( $i == $state_count ) ? $sep = '' : $sep = ', '; + echo "$state$sep"; + } + } +} + +/** + * Test support for compressing JavaScript from PHP + * + * Outputs JavaScript that tests if compression from PHP works as expected + * and sets an option with the result. Has no effect when the current user + * is not an administrator. To run the test again the option 'can_compress_scripts' + * has to be deleted. + * + * @since 2.8.0 + */ +function compression_test() { +?> + + '1' ). + * These attributes will be output as attribute="value", such as tabindex="1". + * Defaults to no other attributes. Other attributes can also be provided as a + * string such as 'tabindex="1"', though the array format is typically cleaner. + */ +function get_submit_button( $text = null, $type = 'primary large', $name = 'submit', $wrap = true, $other_attributes = null ) { + if ( ! is_array( $type ) ) + $type = explode( ' ', $type ); + + $button_shorthand = array( 'primary', 'small', 'large' ); + $classes = array( 'button' ); + foreach ( $type as $t ) { + if ( 'secondary' === $t || 'button-secondary' === $t ) + continue; + $classes[] = in_array( $t, $button_shorthand ) ? 'button-' . $t : $t; + } + $class = implode( ' ', array_unique( $classes ) ); + + if ( 'delete' === $type ) + $class = 'button-secondary delete'; + + $text = $text ? $text : __( 'Save Changes' ); + + // Default the id attribute to $name unless an id was specifically provided in $other_attributes + $id = $name; + if ( is_array( $other_attributes ) && isset( $other_attributes['id'] ) ) { + $id = $other_attributes['id']; + unset( $other_attributes['id'] ); + } + + $attributes = ''; + if ( is_array( $other_attributes ) ) { + foreach ( $other_attributes as $attribute => $value ) { + $attributes .= $attribute . '="' . esc_attr( $value ) . '" '; // Trailing space is important + } + } else if ( !empty( $other_attributes ) ) { // Attributes provided as a string + $attributes = $other_attributes; + } + + $button = ''; + + if ( $wrap ) { + $button = '

    ' . $button . '

    '; + } + + return $button; +} + +function _wp_admin_html_begin() { + global $is_IE; + + $admin_html_class = ( is_admin_bar_showing() ) ? 'wp-toolbar' : ''; + + if ( $is_IE ) + @header('X-UA-Compatible: IE=edge'); + +?> + + + + > + + + + pointer_id ) + */ + + $registered_pointers = array( + 'post-new.php' => 'wp410_dfw', + 'post.php' => 'wp410_dfw', + 'edit.php' => 'wp360_locks', + 'widgets.php' => 'wp390_widgets', + 'themes.php' => 'wp390_widgets', + ); + + // Check if screen related pointer is registered + if ( empty( $registered_pointers[ $hook_suffix ] ) ) + return; + + $pointers = (array) $registered_pointers[ $hook_suffix ]; + + $caps_required = array( + 'wp390_widgets' => array( 'edit_theme_options' ), + ); + + // Get dismissed pointers + $dismissed = explode( ',', (string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) ); + + $got_pointers = false; + foreach ( array_diff( $pointers, $dismissed ) as $pointer ) { + if ( isset( $caps_required[ $pointer ] ) ) { + foreach ( $caps_required[ $pointer ] as $cap ) { + if ( ! current_user_can( $cap ) ) + continue 2; + } + } + + // Bind pointer print function + add_action( 'admin_print_footer_scripts', array( 'WP_Internal_Pointers', 'pointer_' . $pointer ) ); + $got_pointers = true; + } + + if ( ! $got_pointers ) + return; + + // Add pointers script and style to queue + wp_enqueue_style( 'wp-pointer' ); + wp_enqueue_script( 'wp-pointer' ); + } + + /** + * Print the pointer JavaScript data. + * + * @since 3.3.0 + * + * @param string $pointer_id The pointer ID. + * @param string $selector The HTML elements, on which the pointer should be attached. + * @param array $args Arguments to be passed to the pointer JS (see wp-pointer.js). + */ + private static function print_js( $pointer_id, $selector, $args ) { + if ( empty( $pointer_id ) || empty( $selector ) || empty( $args ) || empty( $args['content'] ) ) + return; + + ?> + + ' . __( 'Edit Lock' ) . ''; + $content .= '

    ' . __( 'Someone else is editing this. No need to refresh; the lock will disappear when they’re done.' ) . '

    '; + + self::print_js( 'wp360_locks', 'tr.wp-locked .locked-indicator', array( + 'content' => $content, + 'position' => array( 'edge' => 'left', 'align' => 'left' ), + ) ); + } + + public static function pointer_wp390_widgets() { + if ( ! current_theme_supports( 'widgets' ) ) { + return; + } + + $content = '

    ' . __( 'New Feature: Live Widget Previews' ) . '

    '; + $content .= '

    ' . __( 'Add, edit, and play around with your widgets from the Customizer.' ) . ' ' . __( 'Preview your changes in real-time and only save them when you’re ready.' ) . '

    '; + + if ( 'themes' === get_current_screen()->id ) { + $selector = '.theme.active .customize'; + $position = array( 'edge' => is_rtl() ? 'right' : 'left', 'align' => 'center' ); + } else { + $selector = 'a[href^="customize.php"]'; + if ( is_rtl() ) { + $position = array( 'edge' => 'right', 'align' => 'center', 'my' => 'right-5px' ); + } else { + $position = array( 'edge' => 'left', 'align' => 'center', 'my' => 'left-5px' ); + } + } + + self::print_js( 'wp390_widgets', $selector, array( + 'content' => $content, + 'position' => $position, + ) ); + } + + public static function pointer_wp410_dfw() { + // Don't show when editor-scrolling is not used. + if ( empty( $GLOBALS['_wp_editor_expand'] ) ) { + return; + } + + $content = '

    ' . __( 'Distraction-Free Writing' ) . '

    '; + $content .= '

    ' . __( 'Enable distraction-free writing mode, and everything surrounding the editor will fade away when you start typing. Move your mouse out of the editor to reveal everything again.' ) . '

    '; + + if ( is_rtl() ) { + $position = array( 'edge' => 'left', 'align' => 'center', 'my' => 'left+40 top-11', 'at' => 'left top' ); + } else { + $position = array( 'edge' => 'right', 'align' => 'center', 'my' => 'right-40 top-11', 'at' => 'right top' ); + } + + self::print_js( 'wp410_dfw', '#wp-content-wrap', array( + 'content' => $content, + 'position' => $position, + ) ); + } + + /** + * Prevents new users from seeing existing 'new feature' pointers. + * + * @since 3.3.0 + */ + public static function dismiss_pointers_for_new_users( $user_id ) { + add_user_meta( $user_id, 'dismissed_wp_pointers', 'wp360_locks,wp390_widgets' ); + } +} + +add_action( 'admin_enqueue_scripts', array( 'WP_Internal_Pointers', 'enqueue_scripts' ) ); +add_action( 'user_register', array( 'WP_Internal_Pointers', 'dismiss_pointers_for_new_users' ) ); + +/** + * Convert a screen string to a screen object + * + * @since 3.0.0 + * + * @param string $hook_name The hook name (also known as the hook suffix) used to determine the screen. + * @return WP_Screen Screen object. + */ +function convert_to_screen( $hook_name ) { + if ( ! class_exists( 'WP_Screen' ) ) { + _doing_it_wrong( 'convert_to_screen(), add_meta_box()', __( "Likely direct inclusion of wp-admin/includes/template.php in order to use add_meta_box(). This is very wrong. Hook the add_meta_box() call into the add_meta_boxes action instead." ), '3.3' ); + return (object) array( 'id' => '_invalid', 'base' => '_are_belong_to_us' ); + } + + return WP_Screen::get( $hook_name ); +} + +/** + * Output the HTML for restoring the post data from DOM storage + * + * @since 3.6.0 + * @access private + */ +function _local_storage_notice() { + ?> + + 0, + 'type' => 'rating', + 'number' => 0, + ); + $r = wp_parse_args( $args, $defaults ); + + // Non-english decimal places when the $rating is coming from a string + $rating = str_replace( ',', '.', $r['rating'] ); + + // Convert Percentage to star rating, 0..5 in .5 increments + if ( 'percent' == $r['type'] ) { + $rating = round( $rating / 10, 0 ) / 2; + } + + // Calculate the number of each type of star needed + $full_stars = floor( $rating ); + $half_stars = ceil( $rating - $full_stars ); + $empty_stars = 5 - $full_stars - $half_stars; + + if ( $r['number'] ) { + /* translators: 1: The rating, 2: The number of ratings */ + $format = _n( '%1$s rating based on %2$s rating', '%1$s rating based on %2$s ratings', $r['number'] ); + $title = sprintf( $format, number_format_i18n( $rating, 1 ), number_format_i18n( $r['number'] ) ); + } else { + /* translators: 1: The rating */ + $title = sprintf( __( '%s rating' ), number_format_i18n( $rating, 1 ) ); + } + + echo '
    '; + echo '' . $title . ''; + echo str_repeat( '
    ', $full_stars ); + echo str_repeat( '
    ', $half_stars ); + echo str_repeat( '
    ', $empty_stars); + echo '
    '; +} diff --git a/wp-admin/includes/theme-install.php b/wp-admin/includes/theme-install.php new file mode 100644 index 0000000..0a28e0f --- /dev/null +++ b/wp-admin/includes/theme-install.php @@ -0,0 +1,205 @@ + array('href' => array(), 'title' => array(), 'target' => array()), + 'abbr' => array('title' => array()), 'acronym' => array('title' => array()), + 'code' => array(), 'pre' => array(), 'em' => array(), 'strong' => array(), + 'div' => array(), 'p' => array(), 'ul' => array(), 'ol' => array(), 'li' => array(), + 'h1' => array(), 'h2' => array(), 'h3' => array(), 'h4' => array(), 'h5' => array(), 'h6' => array(), + 'img' => array('src' => array(), 'class' => array(), 'alt' => array()) +); + +$theme_field_defaults = array( 'description' => true, 'sections' => false, 'tested' => true, 'requires' => true, + 'rating' => true, 'downloaded' => true, 'downloadlink' => true, 'last_updated' => true, 'homepage' => true, + 'tags' => true, 'num_ratings' => true +); + +/** + * Retrieve list of WordPress theme features (aka theme tags) + * + * @since 2.8.0 + * + * @deprecated since 3.1.0 Use get_theme_feature_list() instead. + * + * @return array + */ +function install_themes_feature_list() { + _deprecated_function( __FUNCTION__, '3.1', 'get_theme_feature_list()' ); + + if ( !$cache = get_transient( 'wporg_theme_feature_list' ) ) + set_transient( 'wporg_theme_feature_list', array(), 3 * HOUR_IN_SECONDS ); + + if ( $cache ) + return $cache; + + $feature_list = themes_api( 'feature_list', array() ); + if ( is_wp_error( $feature_list ) ) + return array(); + + set_transient( 'wporg_theme_feature_list', $feature_list, 3 * HOUR_IN_SECONDS ); + + return $feature_list; +} + +/** + * Display search form for searching themes. + * + * @since 2.8.0 + */ +function install_theme_search_form( $type_selector = true ) { + $type = isset( $_REQUEST['type'] ) ? wp_unslash( $_REQUEST['type'] ) : 'term'; + $term = isset( $_REQUEST['s'] ) ? wp_unslash( $_REQUEST['s'] ) : ''; + if ( ! $type_selector ) + echo '

    ' . __( 'Search for themes by keyword.' ) . '

    '; + ?> +
    + + + + + + + + + + +
    + +

    +

    + +
    + + '; + + foreach ( (array) $feature_list as $feature_name => $features ) { + $feature_name = esc_html( $feature_name ); + echo '
    ' . $feature_name . '
    '; + + echo '
      '; + foreach ( $features as $feature => $feature_name ) { + $feature_name = esc_html( $feature_name ); + $feature = esc_attr($feature); +?> + +
    1. + + +
    2. + + +
    +
    + + +
    +
    + + + +

    +
    + + + +
    + prepare_items(); + $wp_list_table->single_row( $theme ); +} + +/** + * Display theme content based on theme list. + * + * @since 2.8.0 + */ +function display_themes() { + global $wp_list_table; + + if ( ! isset( $wp_list_table ) ) { + $wp_list_table = _get_list_table('WP_Theme_Install_List_Table'); + } + $wp_list_table->prepare_items(); + $wp_list_table->display(); + +} +// add_action('install_themes_search', 'display_themes'); +// add_action('install_themes_featured', 'display_themes'); +// add_action('install_themes_new', 'display_themes'); +// add_action('install_themes_updated', 'display_themes'); + +/** + * Display theme information in dialog box form. + * + * @since 2.8.0 + */ +function install_theme_information() { + global $wp_list_table; + + $theme = themes_api( 'theme_information', array( 'slug' => wp_unslash( $_REQUEST['theme'] ) ) ); + + if ( is_wp_error( $theme ) ) + wp_die( $theme ); + + iframe_header( __('Theme Install') ); + if ( ! isset( $wp_list_table ) ) { + $wp_list_table = _get_list_table('WP_Theme_Install_List_Table'); + } + $wp_list_table->theme_installer_single( $theme ); + iframe_footer(); + exit; +} +add_action('install_themes_pre_theme-information', 'install_theme_information'); diff --git a/wp-admin/includes/theme.php b/wp-admin/includes/theme.php new file mode 100644 index 0000000..1bc7212 --- /dev/null +++ b/wp-admin/includes/theme.php @@ -0,0 +1,487 @@ +errors) && $wp_filesystem->errors->get_error_code() ) + return new WP_Error('fs_error', __('Filesystem error.'), $wp_filesystem->errors); + + // Get the base plugin folder. + $themes_dir = $wp_filesystem->wp_themes_dir(); + if ( empty( $themes_dir ) ) { + return new WP_Error( 'fs_no_themes_dir', __( 'Unable to locate WordPress theme directory.' ) ); + } + + $themes_dir = trailingslashit( $themes_dir ); + $theme_dir = trailingslashit( $themes_dir . $stylesheet ); + $deleted = $wp_filesystem->delete( $theme_dir, true ); + + if ( ! $deleted ) { + return new WP_Error( 'could_not_remove_theme', sprintf( __( 'Could not fully remove the theme %s.' ), $stylesheet ) ); + } + + $translations_dir = $wp_filesystem->wp_lang_dir(); + $translations_dir = trailingslashit( $translations_dir ); + + $theme_translations = wp_get_installed_translations( 'themes' ); + + // Remove language files, silently. + if ( ! empty( $theme_translations[ $stylesheet ] ) ) { + $translations = $theme_translations[ $stylesheet ]; + + foreach ( $translations as $translation => $data ) { + $wp_filesystem->delete( WP_LANG_DIR . '/themes/' . $stylesheet . '-' . $translation . '.po' ); + $wp_filesystem->delete( WP_LANG_DIR . '/themes/' . $stylesheet . '-' . $translation . '.mo' ); + } + } + + // Force refresh of theme update information. + delete_site_transient( 'update_themes' ); + + return true; +} + +/** + * Get the Page Templates available in this theme + * + * @since 1.5.0 + * + * @param WP_Post|null $post Optional. The post being edited, provided for context. + * @return array Key is the template name, value is the filename of the template + */ +function get_page_templates( $post = null ) { + return array_flip( wp_get_theme()->get_page_templates( $post ) ); +} + +/** + * Tidies a filename for url display by the theme editor. + * + * @since 2.9.0 + * @access private + * + * @param string $fullpath Full path to the theme file + * @param string $containingfolder Path of the theme parent folder + * @return string + */ +function _get_template_edit_filename($fullpath, $containingfolder) { + return str_replace(dirname(dirname( $containingfolder )) , '', $fullpath); +} + +/** + * Check if there is an update for a theme available. + * + * Will display link, if there is an update available. + * + * @since 2.7.0 + * @see get_theme_update_available() + * + * @param object $theme Theme data object. + */ +function theme_update_available( $theme ) { + echo get_theme_update_available( $theme ); +} + +/** + * Retrieve the update link if there is a theme update available. + * + * Will return a link if there is an update available. + * + * @since 3.8.0 + * + * @param WP_Theme $theme WP_Theme object. + * @return false|string HTML for the update link, or false if invalid info was passed. + */ +function get_theme_update_available( $theme ) { + static $themes_update; + + if ( !current_user_can('update_themes' ) ) + return false; + + if ( !isset($themes_update) ) + $themes_update = get_site_transient('update_themes'); + + if ( ! is_a( $theme, 'WP_Theme' ) ) + return false; + + $stylesheet = $theme->get_stylesheet(); + + $html = ''; + + if ( isset($themes_update->response[ $stylesheet ]) ) { + $update = $themes_update->response[ $stylesheet ]; + $theme_name = $theme->display('Name'); + $details_url = add_query_arg(array('TB_iframe' => 'true', 'width' => 1024, 'height' => 800), $update['url']); //Theme browser inside WP? replace this, Also, theme preview JS will override this on the available list. + $update_url = wp_nonce_url( admin_url( 'update.php?action=upgrade-theme&theme=' . urlencode( $stylesheet ) ), 'upgrade-theme_' . $stylesheet ); + $update_onclick = 'onclick="if ( confirm(\'' . esc_js( __("Updating this theme will lose any customizations you have made. 'Cancel' to stop, 'OK' to update.") ) . '\') ) {return true;}return false;"'; + + if ( !is_multisite() ) { + if ( ! current_user_can('update_themes') ) { + $html = sprintf( '

    ' . __( 'There is a new version of %1$s available. View version %4$s details.' ) . '

    ', + $theme_name, esc_url( $details_url ), esc_attr( $theme['Name'] ), $update['new_version'] ); + } else if ( empty( $update['package'] ) ) { + $html = sprintf( '

    ' . __( 'There is a new version of %1$s available. View version %4$s details. Automatic update is unavailable for this theme.' ) . '

    ', + $theme_name, esc_url( $details_url ), esc_attr( $theme['Name'] ), $update['new_version'] ); + } else { + $html = sprintf( '

    ' . __( 'There is a new version of %1$s available. View version %4$s details or update now.' ) . '

    ', + $theme_name, esc_url( $details_url ), esc_attr( $theme['Name'] ), $update['new_version'], $update_url, $update_onclick ); + } + } + } + + return $html; +} + +/** + * Retrieve list of WordPress theme features (aka theme tags) + * + * @since 3.1.0 + * + * @param bool $api Optional. Whether try to fetch tags from the WP.org API. Defaults to true. + * @return array Array of features keyed by category with translations keyed by slug. + */ +function get_theme_feature_list( $api = true ) { + // Hard-coded list is used if api not accessible. + $features = array( + __( 'Colors' ) => array( + 'black' => __( 'Black' ), + 'blue' => __( 'Blue' ), + 'brown' => __( 'Brown' ), + 'gray' => __( 'Gray' ), + 'green' => __( 'Green' ), + 'orange' => __( 'Orange' ), + 'pink' => __( 'Pink' ), + 'purple' => __( 'Purple' ), + 'red' => __( 'Red' ), + 'silver' => __( 'Silver' ), + 'tan' => __( 'Tan' ), + 'white' => __( 'White' ), + 'yellow' => __( 'Yellow' ), + 'dark' => __( 'Dark' ), + 'light' => __( 'Light' ), + ), + + __( 'Layout' ) => array( + 'fixed-layout' => __( 'Fixed Layout' ), + 'fluid-layout' => __( 'Fluid Layout' ), + 'responsive-layout' => __( 'Responsive Layout' ), + 'one-column' => __( 'One Column' ), + 'two-columns' => __( 'Two Columns' ), + 'three-columns' => __( 'Three Columns' ), + 'four-columns' => __( 'Four Columns' ), + 'left-sidebar' => __( 'Left Sidebar' ), + 'right-sidebar' => __( 'Right Sidebar' ), + ), + + __( 'Features' ) => array( + 'accessibility-ready' => __( 'Accessibility Ready' ), + 'blavatar' => __( 'Blavatar' ), + 'buddypress' => __( 'BuddyPress' ), + 'custom-background' => __( 'Custom Background' ), + 'custom-colors' => __( 'Custom Colors' ), + 'custom-header' => __( 'Custom Header' ), + 'custom-menu' => __( 'Custom Menu' ), + 'editor-style' => __( 'Editor Style' ), + 'featured-image-header' => __( 'Featured Image Header' ), + 'featured-images' => __( 'Featured Images' ), + 'flexible-header' => __( 'Flexible Header' ), + 'front-page-post-form' => __( 'Front Page Posting' ), + 'full-width-template' => __( 'Full Width Template' ), + 'microformats' => __( 'Microformats' ), + 'post-formats' => __( 'Post Formats' ), + 'rtl-language-support' => __( 'RTL Language Support' ), + 'sticky-post' => __( 'Sticky Post' ), + 'theme-options' => __( 'Theme Options' ), + 'threaded-comments' => __( 'Threaded Comments' ), + 'translation-ready' => __( 'Translation Ready' ), + ), + + __( 'Subject' ) => array( + 'holiday' => __( 'Holiday' ), + 'photoblogging' => __( 'Photoblogging' ), + 'seasonal' => __( 'Seasonal' ), + ) + ); + + if ( ! $api || ! current_user_can( 'install_themes' ) ) + return $features; + + if ( !$feature_list = get_site_transient( 'wporg_theme_feature_list' ) ) + set_site_transient( 'wporg_theme_feature_list', array(), 3 * HOUR_IN_SECONDS ); + + if ( !$feature_list ) { + $feature_list = themes_api( 'feature_list', array() ); + if ( is_wp_error( $feature_list ) ) + return $features; + } + + if ( !$feature_list ) + return $features; + + set_site_transient( 'wporg_theme_feature_list', $feature_list, 3 * HOUR_IN_SECONDS ); + + $category_translations = array( + 'Colors' => __( 'Colors' ), + 'Layout' => __( 'Layout' ), + 'Features' => __( 'Features' ), + 'Subject' => __( 'Subject' ) + ); + + // Loop over the wporg canonical list and apply translations + $wporg_features = array(); + foreach ( (array) $feature_list as $feature_category => $feature_items ) { + if ( isset($category_translations[$feature_category]) ) + $feature_category = $category_translations[$feature_category]; + $wporg_features[$feature_category] = array(); + + foreach ( $feature_items as $feature ) { + if ( isset($features[$feature_category][$feature]) ) + $wporg_features[$feature_category][$feature] = $features[$feature_category][$feature]; + else + $wporg_features[$feature_category][$feature] = $feature; + } + } + + return $wporg_features; +} + +/** + * Retrieve theme installer pages from WordPress Themes API. + * + * It is possible for a theme to override the Themes API result with three + * filters. Assume this is for themes, which can extend on the Theme Info to + * offer more choices. This is very powerful and must be used with care, when + * overriding the filters. + * + * The first filter, 'themes_api_args', is for the args and gives the action as + * the second parameter. The hook for 'themes_api_args' must ensure that an + * object is returned. + * + * The second filter, 'themes_api', is the result that would be returned. + * + * @since 2.8.0 + * + * @param string $action The requested action. Likely values are 'theme_information', + * 'feature_list', or 'query_themes'. + * @param array|object $args Optional. Arguments to serialize for the Theme Info API. + * @return mixed + */ +function themes_api( $action, $args = null ) { + + if ( is_array( $args ) ) { + $args = (object) $args; + } + + if ( ! isset( $args->per_page ) ) { + $args->per_page = 24; + } + + if ( ! isset( $args->locale ) ) { + $args->locale = get_locale(); + } + + /** + * Filter arguments used to query for installer pages from the WordPress.org Themes API. + * + * Important: An object MUST be returned to this filter. + * + * @since 2.8.0 + * + * @param object $args Arguments used to query for installer pages from the WordPress.org Themes API. + * @param string $action Requested action. Likely values are 'theme_information', + * 'feature_list', or 'query_themes'. + */ + $args = apply_filters( 'themes_api_args', $args, $action ); + + /** + * Filter whether to override the WordPress.org Themes API. + * + * Returning a value of true to this filter allows a theme to completely + * override the built-in WordPress.org API. + * + * @since 2.8.0 + * + * @param bool $bool Whether to override the WordPress.org Themes API. Default false. + * @param string $action Requested action. Likely values are 'theme_information', + * 'feature_list', or 'query_themes'. + * @param object $args Arguments used to query for installer pages from the Themes API. + */ + $res = apply_filters( 'themes_api', false, $action, $args ); + + if ( ! $res ) { + $url = $http_url = 'http://api.wordpress.org/themes/info/1.0/'; + if ( $ssl = wp_http_supports( array( 'ssl' ) ) ) + $url = set_url_scheme( $url, 'https' ); + + $args = array( + 'body' => array( + 'action' => $action, + 'request' => serialize( $args ) + ) + ); + $request = wp_remote_post( $url, $args ); + + if ( $ssl && is_wp_error( $request ) ) { + if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) { + trigger_error( __( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server’s configuration. If you continue to have problems, please try the support forums.' ) . ' ' . __( '(WordPress could not establish a secure connection to WordPress.org. Please contact your server administrator.)' ), headers_sent() || WP_DEBUG ? E_USER_WARNING : E_USER_NOTICE ); + } + $request = wp_remote_post( $http_url, $args ); + } + + if ( is_wp_error($request) ) { + $res = new WP_Error('themes_api_failed', __( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server’s configuration. If you continue to have problems, please try the support forums.' ), $request->get_error_message() ); + } else { + $res = maybe_unserialize( wp_remote_retrieve_body( $request ) ); + if ( ! is_object( $res ) && ! is_array( $res ) ) + $res = new WP_Error('themes_api_failed', __( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server’s configuration. If you continue to have problems, please try the support forums.' ), wp_remote_retrieve_body( $request ) ); + } + } + + /** + * Filter the returned WordPress.org Themes API response. + * + * @since 2.8.0 + * + * @param array|object $res WordPress.org Themes API response. + * @param string $action Requested action. Likely values are 'theme_information', + * 'feature_list', or 'query_themes'. + * @param object $args Arguments used to query for installer pages from the WordPress.org Themes API. + */ + return apply_filters( 'themes_api_result', $res, $action, $args ); +} + +/** + * Prepare themes for JavaScript. + * + * @since 3.8.0 + * + * @param array $themes Optional. Array of WP_Theme objects to prepare. + * Defaults to all allowed themes. + * + * @return array An associative array of theme data, sorted by name. + */ +function wp_prepare_themes_for_js( $themes = null ) { + $current_theme = get_stylesheet(); + + // Make sure the current theme is listed first. + $prepared_themes = array( $current_theme => array() ); + + if ( null === $themes ) { + $themes = wp_get_themes( array( 'allowed' => true ) ); + if ( ! isset( $themes[ $current_theme ] ) ) { + $themes[ $current_theme ] = wp_get_theme(); + } + } + + $updates = array(); + if ( current_user_can( 'update_themes' ) ) { + $updates_transient = get_site_transient( 'update_themes' ); + if ( isset( $updates_transient->response ) ) { + $updates = $updates_transient->response; + } + } + + WP_Theme::sort_by_name( $themes ); + foreach ( $themes as $theme ) { + $slug = $theme->get_stylesheet(); + $encoded_slug = urlencode( $slug ); + + $parent = false; + if ( $theme->parent() ) { + $parent = $theme->parent()->display( 'Name' ); + $parents[ $slug ] = $theme->parent()->get_stylesheet(); + } + + $prepared_themes[ $slug ] = array( + 'id' => $slug, + 'name' => $theme->display( 'Name' ), + 'screenshot' => array( $theme->get_screenshot() ), // @todo multiple + 'description' => $theme->display( 'Description' ), + 'author' => $theme->display( 'Author', false, true ), + 'authorAndUri' => $theme->display( 'Author' ), + 'version' => $theme->display( 'Version' ), + 'tags' => $theme->display( 'Tags' ), + 'parent' => $parent, + 'active' => $slug === $current_theme, + 'hasUpdate' => isset( $updates[ $slug ] ), + 'update' => get_theme_update_available( $theme ), + 'actions' => array( + 'activate' => current_user_can( 'switch_themes' ) ? wp_nonce_url( admin_url( 'themes.php?action=activate&stylesheet=' . $encoded_slug ), 'switch-theme_' . $slug ) : null, + 'customize' => ( current_user_can( 'edit_theme_options' ) && current_user_can( 'customize' ) ) ? wp_customize_url( $slug ) : null, + 'preview' => add_query_arg( array( + 'preview' => 1, + 'template' => urlencode( $theme->get_template() ), + 'stylesheet' => urlencode( $slug ), + 'preview_iframe' => true, + 'TB_iframe' => true, + ), home_url( '/' ) ), + 'delete' => current_user_can( 'delete_themes' ) ? wp_nonce_url( admin_url( 'themes.php?action=delete&stylesheet=' . $encoded_slug ), 'delete-theme_' . $slug ) : null, + ), + ); + } + + // Remove 'delete' action if theme has an active child + if ( isset( $parents ) && array_key_exists( $current_theme, $parents ) ) { + unset( $prepared_themes[ $parents[ $current_theme ] ]['actions']['delete'] ); + } + + /** + * Filter the themes prepared for JavaScript, for themes.php. + * + * Could be useful for changing the order, which is by name by default. + * + * @since 3.8.0 + * + * @param array $prepared_themes Array of themes. + */ + $prepared_themes = apply_filters( 'wp_prepare_themes_for_js', $prepared_themes ); + return array_values( $prepared_themes ); +} diff --git a/wp-admin/includes/translation-install.php b/wp-admin/includes/translation-install.php new file mode 100644 index 0000000..f3cc370 --- /dev/null +++ b/wp-admin/includes/translation-install.php @@ -0,0 +1,240 @@ + 3, + 'body' => array( + 'wp_version' => $wp_version, + 'locale' => get_locale(), + 'version' => $args['version'], // Version of plugin, theme or core + ), + ); + + if ( 'core' !== $type ) { + $options['body']['slug'] = $args['slug']; // Plugin or theme slug + } + + $request = wp_remote_post( $url, $options ); + + if ( $ssl && is_wp_error( $request ) ) { + trigger_error( __( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server’s configuration. If you continue to have problems, please try the support forums.' ) . ' ' . __( '(WordPress could not establish a secure connection to WordPress.org. Please contact your server administrator.)' ), headers_sent() || WP_DEBUG ? E_USER_WARNING : E_USER_NOTICE ); + + $request = wp_remote_post( $http_url, $options ); + } + + if ( is_wp_error( $request ) ) { + $res = new WP_Error( 'translations_api_failed', __( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server’s configuration. If you continue to have problems, please try the support forums.' ), $request->get_error_message() ); + } else { + $res = json_decode( wp_remote_retrieve_body( $request ), true ); + if ( ! is_object( $res ) && ! is_array( $res ) ) { + $res = new WP_Error( 'translations_api_failed', __( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server’s configuration. If you continue to have problems, please try the support forums.' ), wp_remote_retrieve_body( $request ) ); + } + } + } + + /** + * Filter the Translation Install API response results. + * + * @since 4.0.0 + * + * @param object|WP_Error $res Response object or WP_Error. + * @param string $type The type of translations being requested. + * @param object $args Translation API arguments. + */ + return apply_filters( 'translations_api_result', $res, $type, $args ); +} + +/** + * Get available translations from the WordPress.org API. + * + * @since 4.0.0 + * + * @see translations_api() + * + * @return array Array of translations, each an array of data. If the API response results + * in an error, an empty array will be returned. + */ +function wp_get_available_translations() { + if ( ! defined( 'WP_INSTALLING' ) && false !== ( $translations = get_site_transient( 'available_translations' ) ) ) { + return $translations; + } + + include( ABSPATH . WPINC . '/version.php' ); // include an unmodified $wp_version + + $api = translations_api( 'core', array( 'version' => $wp_version ) ); + + if ( is_wp_error( $api ) || empty( $api['translations'] ) ) { + return array(); + } + + $translations = array(); + // Key the array with the language code for now. + foreach ( $api['translations'] as $translation ) { + $translations[ $translation['language'] ] = $translation; + } + + if ( ! defined( 'WP_INSTALLING' ) ) { + set_site_transient( 'available_translations', $translations, 3 * HOUR_IN_SECONDS ); + } + + return $translations; +} + +/** + * Output the select form for the language selection on the installation screen. + * + * @since 4.0.0 + * + * @param array $languages Array of available languages (populated via the Translation API). + */ +function wp_install_language_form( $languages ) { + global $wp_local_package; + + $installed_languages = get_available_languages(); + + echo "\n"; + echo "\n"; + echo '

    '; +} + +/** + * Download a language pack. + * + * @since 4.0.0 + * + * @see wp_get_available_translations() + * + * @param string $download Language code to download. + * @return string|bool Returns the language code if successfully downloaded + * (or already installed), or false on failure. + */ +function wp_download_language_pack( $download ) { + // Check if the translation is already installed. + if ( in_array( $download, get_available_languages() ) ) { + return $download; + } + + if ( defined( 'DISALLOW_FILE_MODS' ) && DISALLOW_FILE_MODS ) { + return false; + } + + // Confirm the translation is one we can download. + $translations = wp_get_available_translations(); + if ( ! $translations ) { + return false; + } + foreach ( $translations as $translation ) { + if ( $translation['language'] === $download ) { + $translation_to_load = true; + break; + } + } + + if ( empty( $translation_to_load ) ) { + return false; + } + $translation = (object) $translation; + + require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; + $skin = new Automatic_Upgrader_Skin; + $upgrader = new Language_Pack_Upgrader( $skin ); + $translation->type = 'core'; + $result = $upgrader->upgrade( $translation, array( 'clear_update_cache' => false ) ); + + if ( ! $result || is_wp_error( $result ) ) { + return false; + } + + return $translation->language; +} + +/** + * Check if WordPress has access to the filesystem without asking for + * credentials. + * + * @since 4.0.0 + * + * @return bool Returns true on success, false on failure. + */ +function wp_can_install_language_pack() { + if ( defined( 'DISALLOW_FILE_MODS' ) && DISALLOW_FILE_MODS ) { + return false; + } + + require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; + $skin = new Automatic_Upgrader_Skin; + $upgrader = new Language_Pack_Upgrader( $skin ); + + $check = $upgrader->fs_connect( array( WP_CONTENT_DIR, WP_LANG_DIR ) ); + + if ( ! $check || is_wp_error( $check ) ) { + return false; + } + + return true; +} diff --git a/wp-admin/includes/update-core.php b/wp-admin/includes/update-core.php new file mode 100644 index 0000000..2189d55 --- /dev/null +++ b/wp-admin/includes/update-core.php @@ -0,0 +1,1188 @@ + Introduced version + * Directories should be noted by suffixing it with a trailing slash (/) + * + * @since 3.2.0 + * @global array $_new_bundled_files + * @var array + * @name $_new_bundled_files + */ +global $_new_bundled_files; + +$_new_bundled_files = array( + 'plugins/akismet/' => '2.0', + 'themes/twentyten/' => '3.0', + 'themes/twentyeleven/' => '3.2', + 'themes/twentytwelve/' => '3.5', + 'themes/twentythirteen/' => '3.6', + 'themes/twentyfourteen/' => '3.8', + 'themes/twentyfifteen/' => '4.1', +); + +/** + * Upgrade the core of WordPress. + * + * This will create a .maintenance file at the base of the WordPress directory + * to ensure that people can not access the web site, when the files are being + * copied to their locations. + * + * The files in the {@link $_old_files} list will be removed and the new files + * copied from the zip file after the database is upgraded. + * + * The files in the {@link $_new_bundled_files} list will be added to the installation + * if the version is greater than or equal to the old version being upgraded. + * + * The steps for the upgrader for after the new release is downloaded and + * unzipped is: + * 1. Test unzipped location for select files to ensure that unzipped worked. + * 2. Create the .maintenance file in current WordPress base. + * 3. Copy new WordPress directory over old WordPress files. + * 4. Upgrade WordPress to new version. + * 4.1. Copy all files/folders other than wp-content + * 4.2. Copy any language files to WP_LANG_DIR (which may differ from WP_CONTENT_DIR + * 4.3. Copy any new bundled themes/plugins to their respective locations + * 5. Delete new WordPress directory path. + * 6. Delete .maintenance file. + * 7. Remove old files. + * 8. Delete 'update_core' option. + * + * There are several areas of failure. For instance if PHP times out before step + * 6, then you will not be able to access any portion of your site. Also, since + * the upgrade will not continue where it left off, you will not be able to + * automatically remove old files and remove the 'update_core' option. This + * isn't that bad. + * + * If the copy of the new WordPress over the old fails, then the worse is that + * the new WordPress directory will remain. + * + * If it is assumed that every file will be copied over, including plugins and + * themes, then if you edit the default theme, you should rename it, so that + * your changes remain. + * + * @since 2.7.0 + * + * @param string $from New release unzipped path. + * @param string $to Path to old WordPress installation. + * @return WP_Error|null WP_Error on failure, null on success. + */ +function update_core($from, $to) { + global $wp_filesystem, $_old_files, $_new_bundled_files, $wpdb; + + @set_time_limit( 300 ); + + /** + * Filter feedback messages displayed during the core update process. + * + * The filter is first evaluated after the zip file for the latest version + * has been downloaded and unzipped. It is evaluated five more times during + * the process: + * + * 1. Before WordPress begins the core upgrade process. + * 2. Before Maintenance Mode is enabled. + * 3. Before WordPress begins copying over the necessary files. + * 4. Before Maintenance Mode is disabled. + * 5. Before the database is upgraded. + * + * @since 2.5.0 + * + * @param string $feedback The core update feedback messages. + */ + apply_filters( 'update_feedback', __( 'Verifying the unpacked files…' ) ); + + // Sanity check the unzipped distribution. + $distro = ''; + $roots = array( '/wordpress/', '/wordpress-mu/' ); + foreach ( $roots as $root ) { + if ( $wp_filesystem->exists( $from . $root . 'readme.html' ) && $wp_filesystem->exists( $from . $root . 'wp-includes/version.php' ) ) { + $distro = $root; + break; + } + } + if ( ! $distro ) { + $wp_filesystem->delete( $from, true ); + return new WP_Error( 'insane_distro', __('The update could not be unpacked') ); + } + + // Import $wp_version, $required_php_version, and $required_mysql_version from the new version + // $wp_filesystem->wp_content_dir() returned unslashed pre-2.8 + $versions_file = trailingslashit( $wp_filesystem->wp_content_dir() ) . 'upgrade/version-current.php'; + if ( ! $wp_filesystem->copy( $from . $distro . 'wp-includes/version.php', $versions_file ) ) { + $wp_filesystem->delete( $from, true ); + return new WP_Error( 'copy_failed_for_version_file', __( 'The update cannot be installed because we will be unable to copy some files. This is usually due to inconsistent file permissions.' ), 'wp-includes/version.php' ); + } + + $wp_filesystem->chmod( $versions_file, FS_CHMOD_FILE ); + require( WP_CONTENT_DIR . '/upgrade/version-current.php' ); + $wp_filesystem->delete( $versions_file ); + + $php_version = phpversion(); + $mysql_version = $wpdb->db_version(); + $old_wp_version = $GLOBALS['wp_version']; // The version of WordPress we're updating from + $development_build = ( false !== strpos( $old_wp_version . $wp_version, '-' ) ); // a dash in the version indicates a Development release + $php_compat = version_compare( $php_version, $required_php_version, '>=' ); + if ( file_exists( WP_CONTENT_DIR . '/db.php' ) && empty( $wpdb->is_mysql ) ) + $mysql_compat = true; + else + $mysql_compat = version_compare( $mysql_version, $required_mysql_version, '>=' ); + + if ( !$mysql_compat || !$php_compat ) + $wp_filesystem->delete($from, true); + + if ( !$mysql_compat && !$php_compat ) + return new WP_Error( 'php_mysql_not_compatible', sprintf( __('The update cannot be installed because WordPress %1$s requires PHP version %2$s or higher and MySQL version %3$s or higher. You are running PHP version %4$s and MySQL version %5$s.'), $wp_version, $required_php_version, $required_mysql_version, $php_version, $mysql_version ) ); + elseif ( !$php_compat ) + return new WP_Error( 'php_not_compatible', sprintf( __('The update cannot be installed because WordPress %1$s requires PHP version %2$s or higher. You are running version %3$s.'), $wp_version, $required_php_version, $php_version ) ); + elseif ( !$mysql_compat ) + return new WP_Error( 'mysql_not_compatible', sprintf( __('The update cannot be installed because WordPress %1$s requires MySQL version %2$s or higher. You are running version %3$s.'), $wp_version, $required_mysql_version, $mysql_version ) ); + + /** This filter is documented in wp-admin/includes/update-core.php */ + apply_filters( 'update_feedback', __( 'Preparing to install the latest version…' ) ); + + // Don't copy wp-content, we'll deal with that below + // We also copy version.php last so failed updates report their old version + $skip = array( 'wp-content', 'wp-includes/version.php' ); + $check_is_writable = array(); + + // Check to see which files don't really need updating - only available for 3.7 and higher + if ( function_exists( 'get_core_checksums' ) ) { + // Find the local version of the working directory + $working_dir_local = WP_CONTENT_DIR . '/upgrade/' . basename( $from ) . $distro; + + $checksums = get_core_checksums( $wp_version, isset( $wp_local_package ) ? $wp_local_package : 'en_US' ); + if ( is_array( $checksums ) && isset( $checksums[ $wp_version ] ) ) + $checksums = $checksums[ $wp_version ]; // Compat code for 3.7-beta2 + if ( is_array( $checksums ) ) { + foreach( $checksums as $file => $checksum ) { + if ( 'wp-content' == substr( $file, 0, 10 ) ) + continue; + if ( ! file_exists( ABSPATH . $file ) ) + continue; + if ( ! file_exists( $working_dir_local . $file ) ) + continue; + if ( md5_file( ABSPATH . $file ) === $checksum ) + $skip[] = $file; + else + $check_is_writable[ $file ] = ABSPATH . $file; + } + } + } + + // If we're using the direct method, we can predict write failures that are due to permissions. + if ( $check_is_writable && 'direct' === $wp_filesystem->method ) { + $files_writable = array_filter( $check_is_writable, array( $wp_filesystem, 'is_writable' ) ); + if ( $files_writable !== $check_is_writable ) { + $files_not_writable = array_diff_key( $check_is_writable, $files_writable ); + foreach ( $files_not_writable as $relative_file_not_writable => $file_not_writable ) { + // If the writable check failed, chmod file to 0644 and try again, same as copy_dir(). + $wp_filesystem->chmod( $file_not_writable, FS_CHMOD_FILE ); + if ( $wp_filesystem->is_writable( $file_not_writable ) ) + unset( $files_not_writable[ $relative_file_not_writable ] ); + } + + // Store package-relative paths (the key) of non-writable files in the WP_Error object. + $error_data = version_compare( $old_wp_version, '3.7-beta2', '>' ) ? array_keys( $files_not_writable ) : ''; + + if ( $files_not_writable ) + return new WP_Error( 'files_not_writable', __( 'The update cannot be installed because we will be unable to copy some files. This is usually due to inconsistent file permissions.' ), implode( ', ', $error_data ) ); + } + } + + /** This filter is documented in wp-admin/includes/update-core.php */ + apply_filters( 'update_feedback', __( 'Enabling Maintenance mode…' ) ); + // Create maintenance file to signal that we are upgrading + $maintenance_string = ''; + $maintenance_file = $to . '.maintenance'; + $wp_filesystem->delete($maintenance_file); + $wp_filesystem->put_contents($maintenance_file, $maintenance_string, FS_CHMOD_FILE); + + /** This filter is documented in wp-admin/includes/update-core.php */ + apply_filters( 'update_feedback', __( 'Copying the required files…' ) ); + // Copy new versions of WP files into place. + $result = _copy_dir( $from . $distro, $to, $skip ); + if ( is_wp_error( $result ) ) + $result = new WP_Error( $result->get_error_code(), $result->get_error_message(), substr( $result->get_error_data(), strlen( $to ) ) ); + + // Since we know the core files have copied over, we can now copy the version file + if ( ! is_wp_error( $result ) ) { + if ( ! $wp_filesystem->copy( $from . $distro . 'wp-includes/version.php', $to . 'wp-includes/version.php', true /* overwrite */ ) ) { + $wp_filesystem->delete( $from, true ); + $result = new WP_Error( 'copy_failed_for_version_file', __( 'The update cannot be installed because we will be unable to copy some files. This is usually due to inconsistent file permissions.' ), 'wp-includes/version.php' ); + } + $wp_filesystem->chmod( $to . 'wp-includes/version.php', FS_CHMOD_FILE ); + } + + // Check to make sure everything copied correctly, ignoring the contents of wp-content + $skip = array( 'wp-content' ); + $failed = array(); + if ( isset( $checksums ) && is_array( $checksums ) ) { + foreach ( $checksums as $file => $checksum ) { + if ( 'wp-content' == substr( $file, 0, 10 ) ) + continue; + if ( ! file_exists( $working_dir_local . $file ) ) + continue; + if ( file_exists( ABSPATH . $file ) && md5_file( ABSPATH . $file ) == $checksum ) + $skip[] = $file; + else + $failed[] = $file; + } + } + + // Some files didn't copy properly + if ( ! empty( $failed ) ) { + $total_size = 0; + foreach ( $failed as $file ) { + if ( file_exists( $working_dir_local . $file ) ) + $total_size += filesize( $working_dir_local . $file ); + } + + // If we don't have enough free space, it isn't worth trying again. + // Unlikely to be hit due to the check in unzip_file(). + $available_space = @disk_free_space( ABSPATH ); + if ( $available_space && $total_size >= $available_space ) { + $result = new WP_Error( 'disk_full', __( 'There is not enough free disk space to complete the update.' ) ); + } else { + $result = _copy_dir( $from . $distro, $to, $skip ); + if ( is_wp_error( $result ) ) + $result = new WP_Error( $result->get_error_code() . '_retry', $result->get_error_message(), substr( $result->get_error_data(), strlen( $to ) ) ); + } + } + + // Custom Content Directory needs updating now. + // Copy Languages + if ( !is_wp_error($result) && $wp_filesystem->is_dir($from . $distro . 'wp-content/languages') ) { + if ( WP_LANG_DIR != ABSPATH . WPINC . '/languages' || @is_dir(WP_LANG_DIR) ) + $lang_dir = WP_LANG_DIR; + else + $lang_dir = WP_CONTENT_DIR . '/languages'; + + if ( !@is_dir($lang_dir) && 0 === strpos($lang_dir, ABSPATH) ) { // Check the language directory exists first + $wp_filesystem->mkdir($to . str_replace(ABSPATH, '', $lang_dir), FS_CHMOD_DIR); // If it's within the ABSPATH we can handle it here, otherwise they're out of luck. + clearstatcache(); // for FTP, Need to clear the stat cache + } + + if ( @is_dir($lang_dir) ) { + $wp_lang_dir = $wp_filesystem->find_folder($lang_dir); + if ( $wp_lang_dir ) { + $result = copy_dir($from . $distro . 'wp-content/languages/', $wp_lang_dir); + if ( is_wp_error( $result ) ) + $result = new WP_Error( $result->get_error_code() . '_languages', $result->get_error_message(), substr( $result->get_error_data(), strlen( $wp_lang_dir ) ) ); + } + } + } + + /** This filter is documented in wp-admin/includes/update-core.php */ + apply_filters( 'update_feedback', __( 'Disabling Maintenance mode…' ) ); + // Remove maintenance file, we're done with potential site-breaking changes + $wp_filesystem->delete( $maintenance_file ); + + // 3.5 -> 3.5+ - an empty twentytwelve directory was created upon upgrade to 3.5 for some users, preventing installation of Twenty Twelve. + if ( '3.5' == $old_wp_version ) { + if ( is_dir( WP_CONTENT_DIR . '/themes/twentytwelve' ) && ! file_exists( WP_CONTENT_DIR . '/themes/twentytwelve/style.css' ) ) { + $wp_filesystem->delete( $wp_filesystem->wp_themes_dir() . 'twentytwelve/' ); + } + } + + // Copy New bundled plugins & themes + // This gives us the ability to install new plugins & themes bundled with future versions of WordPress whilst avoiding the re-install upon upgrade issue. + // $development_build controls us overwriting bundled themes and plugins when a non-stable release is being updated + if ( !is_wp_error($result) && ( ! defined('CORE_UPGRADE_SKIP_NEW_BUNDLED') || ! CORE_UPGRADE_SKIP_NEW_BUNDLED ) ) { + foreach ( (array) $_new_bundled_files as $file => $introduced_version ) { + // If a $development_build or if $introduced version is greater than what the site was previously running + if ( $development_build || version_compare( $introduced_version, $old_wp_version, '>' ) ) { + $directory = ('/' == $file[ strlen($file)-1 ]); + list($type, $filename) = explode('/', $file, 2); + + // Check to see if the bundled items exist before attempting to copy them + if ( ! $wp_filesystem->exists( $from . $distro . 'wp-content/' . $file ) ) + continue; + + if ( 'plugins' == $type ) + $dest = $wp_filesystem->wp_plugins_dir(); + elseif ( 'themes' == $type ) + $dest = trailingslashit($wp_filesystem->wp_themes_dir()); // Back-compat, ::wp_themes_dir() did not return trailingslash'd pre-3.2 + else + continue; + + if ( ! $directory ) { + if ( ! $development_build && $wp_filesystem->exists( $dest . $filename ) ) + continue; + + if ( ! $wp_filesystem->copy($from . $distro . 'wp-content/' . $file, $dest . $filename, FS_CHMOD_FILE) ) + $result = new WP_Error( "copy_failed_for_new_bundled_$type", __( 'Could not copy file.' ), $dest . $filename ); + } else { + if ( ! $development_build && $wp_filesystem->is_dir( $dest . $filename ) ) + continue; + + $wp_filesystem->mkdir($dest . $filename, FS_CHMOD_DIR); + $_result = copy_dir( $from . $distro . 'wp-content/' . $file, $dest . $filename); + + // If a error occurs partway through this final step, keep the error flowing through, but keep process going. + if ( is_wp_error( $_result ) ) { + if ( ! is_wp_error( $result ) ) + $result = new WP_Error; + $result->add( $_result->get_error_code() . "_$type", $_result->get_error_message(), substr( $_result->get_error_data(), strlen( $dest ) ) ); + } + } + } + } //end foreach + } + + // Handle $result error from the above blocks + if ( is_wp_error($result) ) { + $wp_filesystem->delete($from, true); + return $result; + } + + // Remove old files + foreach ( $_old_files as $old_file ) { + $old_file = $to . $old_file; + if ( !$wp_filesystem->exists($old_file) ) + continue; + $wp_filesystem->delete($old_file, true); + } + + // Upgrade DB with separate request + /** This filter is documented in wp-admin/includes/update-core.php */ + apply_filters( 'update_feedback', __( 'Upgrading database…' ) ); + $db_upgrade_url = admin_url('upgrade.php?step=upgrade_db'); + wp_remote_post($db_upgrade_url, array('timeout' => 60)); + + // Clear the cache to prevent an update_option() from saving a stale db_version to the cache + wp_cache_flush(); + // (Not all cache backends listen to 'flush') + wp_cache_delete( 'alloptions', 'options' ); + + // Remove working directory + $wp_filesystem->delete($from, true); + + // Force refresh of update information + if ( function_exists('delete_site_transient') ) + delete_site_transient('update_core'); + else + delete_option('update_core'); + + /** + * Fires after WordPress core has been successfully updated. + * + * @since 3.3.0 + * + * @param string $wp_version The current WordPress version. + */ + do_action( '_core_updated_successfully', $wp_version ); + + // Clear the option that blocks auto updates after failures, now that we've been successful. + if ( function_exists( 'delete_site_option' ) ) + delete_site_option( 'auto_core_update_failed' ); + + return $wp_version; +} + +/** + * Copies a directory from one location to another via the WordPress Filesystem Abstraction. + * Assumes that WP_Filesystem() has already been called and setup. + * + * This is a temporary function for the 3.1 -> 3.2 upgrade, as well as for those upgrading to + * 3.7+ + * + * @ignore + * @since 3.2.0 + * @since 3.7.0 Updated not to use a regular expression for the skip list + * @see copy_dir() + * + * @param string $from source directory + * @param string $to destination directory + * @param array $skip_list a list of files/folders to skip copying + * @return mixed WP_Error on failure, True on success. + */ +function _copy_dir($from, $to, $skip_list = array() ) { + global $wp_filesystem; + + $dirlist = $wp_filesystem->dirlist($from); + + $from = trailingslashit($from); + $to = trailingslashit($to); + + foreach ( (array) $dirlist as $filename => $fileinfo ) { + if ( in_array( $filename, $skip_list ) ) + continue; + + if ( 'f' == $fileinfo['type'] ) { + if ( ! $wp_filesystem->copy($from . $filename, $to . $filename, true, FS_CHMOD_FILE) ) { + // If copy failed, chmod file to 0644 and try again. + $wp_filesystem->chmod( $to . $filename, FS_CHMOD_FILE ); + if ( ! $wp_filesystem->copy($from . $filename, $to . $filename, true, FS_CHMOD_FILE) ) + return new WP_Error( 'copy_failed__copy_dir', __( 'Could not copy file.' ), $to . $filename ); + } + } elseif ( 'd' == $fileinfo['type'] ) { + if ( !$wp_filesystem->is_dir($to . $filename) ) { + if ( !$wp_filesystem->mkdir($to . $filename, FS_CHMOD_DIR) ) + return new WP_Error( 'mkdir_failed__copy_dir', __( 'Could not create directory.' ), $to . $filename ); + } + + /* + * Generate the $sub_skip_list for the subdirectory as a sub-set + * of the existing $skip_list. + */ + $sub_skip_list = array(); + foreach ( $skip_list as $skip_item ) { + if ( 0 === strpos( $skip_item, $filename . '/' ) ) + $sub_skip_list[] = preg_replace( '!^' . preg_quote( $filename, '!' ) . '/!i', '', $skip_item ); + } + + $result = _copy_dir($from . $filename, $to . $filename, $sub_skip_list); + if ( is_wp_error($result) ) + return $result; + } + } + return true; +} + +/** + * Redirect to the About WordPress page after a successful upgrade. + * + * This function is only needed when the existing install is older than 3.4.0. + * + * @since 3.3.0 + * + */ +function _redirect_to_about_wordpress( $new_version ) { + global $wp_version, $pagenow, $action; + + if ( version_compare( $wp_version, '3.4-RC1', '>=' ) ) + return; + + // Ensure we only run this on the update-core.php page. The Core_Upgrader may be used in other contexts. + if ( 'update-core.php' != $pagenow ) + return; + + if ( 'do-core-upgrade' != $action && 'do-core-reinstall' != $action ) + return; + + // Load the updated default text localization domain for new strings. + load_default_textdomain(); + + // See do_core_upgrade() + show_message( __('WordPress updated successfully') ); + + // self_admin_url() won't exist when upgrading from <= 3.0, so relative URLs are intentional. + show_message( '' . sprintf( __( 'Welcome to WordPress %1$s. You will be redirected to the About WordPress screen. If not, click here.' ), $new_version, 'about.php?updated' ) . '' ); + show_message( '' . sprintf( __( 'Welcome to WordPress %1$s. Learn more.' ), $new_version, 'about.php?updated' ) . '' ); + echo ''; + ?> + + 'latest' ); + return $updates[0]; +} + +/** + * Get available core updates. + * + * @param array $options Set $options['dismissed'] to true to show dismissed upgrades too, + * set $options['available'] to false to skip not-dismissed updates. + * @return bool|array Array of the update objects on success, false on failure. + */ +function get_core_updates( $options = array() ) { + $options = array_merge( array( 'available' => true, 'dismissed' => false ), $options ); + $dismissed = get_site_option( 'dismissed_update_core' ); + + if ( ! is_array( $dismissed ) ) + $dismissed = array(); + + $from_api = get_site_transient( 'update_core' ); + + if ( ! isset( $from_api->updates ) || ! is_array( $from_api->updates ) ) + return false; + + $updates = $from_api->updates; + $result = array(); + foreach ( $updates as $update ) { + if ( $update->response == 'autoupdate' ) + continue; + + if ( array_key_exists( $update->current . '|' . $update->locale, $dismissed ) ) { + if ( $options['dismissed'] ) { + $update->dismissed = true; + $result[] = $update; + } + } else { + if ( $options['available'] ) { + $update->dismissed = false; + $result[] = $update; + } + } + } + return $result; +} + +/** + * Gets the best available (and enabled) Auto-Update for WordPress Core. + * + * If there's 1.2.3 and 1.3 on offer, it'll choose 1.3 if the install allows it, else, 1.2.3 + * + * @since 3.7.0 + * + * @return bool|array False on failure, otherwise the core update offering. + */ +function find_core_auto_update() { + $updates = get_site_transient( 'update_core' ); + if ( ! $updates || empty( $updates->updates ) ) + return false; + + include_once( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' ); + + $auto_update = false; + $upgrader = new WP_Automatic_Updater; + foreach ( $updates->updates as $update ) { + if ( 'autoupdate' != $update->response ) + continue; + + if ( ! $upgrader->should_update( 'core', $update, ABSPATH ) ) + continue; + + if ( ! $auto_update || version_compare( $update->current, $auto_update->current, '>' ) ) + $auto_update = $update; + } + return $auto_update; +} + +/** + * Gets and caches the checksums for the given version of WordPress. + * + * @since 3.7.0 + * + * @param string $version Version string to query. + * @param string $locale Locale to query. + * @return bool|array False on failure. An array of checksums on success. + */ +function get_core_checksums( $version, $locale ) { + $url = $http_url = 'http://api.wordpress.org/core/checksums/1.0/?' . http_build_query( compact( 'version', 'locale' ), null, '&' ); + + if ( $ssl = wp_http_supports( array( 'ssl' ) ) ) + $url = set_url_scheme( $url, 'https' ); + + $options = array( + 'timeout' => ( ( defined('DOING_CRON') && DOING_CRON ) ? 30 : 3 ), + ); + + $response = wp_remote_get( $url, $options ); + if ( $ssl && is_wp_error( $response ) ) { + trigger_error( __( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server’s configuration. If you continue to have problems, please try the support forums.' ) . ' ' . __( '(WordPress could not establish a secure connection to WordPress.org. Please contact your server administrator.)' ), headers_sent() || WP_DEBUG ? E_USER_WARNING : E_USER_NOTICE ); + $response = wp_remote_get( $http_url, $options ); + } + + if ( is_wp_error( $response ) || 200 != wp_remote_retrieve_response_code( $response ) ) + return false; + + $body = trim( wp_remote_retrieve_body( $response ) ); + $body = json_decode( $body, true ); + + if ( ! is_array( $body ) || ! isset( $body['checksums'] ) || ! is_array( $body['checksums'] ) ) + return false; + + return $body['checksums']; +} + +function dismiss_core_update( $update ) { + $dismissed = get_site_option( 'dismissed_update_core' ); + $dismissed[ $update->current . '|' . $update->locale ] = true; + return update_site_option( 'dismissed_update_core', $dismissed ); +} + +function undismiss_core_update( $version, $locale ) { + $dismissed = get_site_option( 'dismissed_update_core' ); + $key = $version . '|' . $locale; + + if ( ! isset( $dismissed[$key] ) ) + return false; + + unset( $dismissed[$key] ); + return update_site_option( 'dismissed_update_core', $dismissed ); +} + +function find_core_update( $version, $locale ) { + $from_api = get_site_transient( 'update_core' ); + + if ( ! isset( $from_api->updates ) || ! is_array( $from_api->updates ) ) + return false; + + $updates = $from_api->updates; + foreach ( $updates as $update ) { + if ( $update->current == $version && $update->locale == $locale ) + return $update; + } + return false; +} + +function core_update_footer( $msg = '' ) { + if ( !current_user_can('update_core') ) + return sprintf( __( 'Version %s' ), get_bloginfo( 'version', 'display' ) ); + + $cur = get_preferred_from_update_core(); + if ( ! is_object( $cur ) ) + $cur = new stdClass; + + if ( ! isset( $cur->current ) ) + $cur->current = ''; + + if ( ! isset( $cur->url ) ) + $cur->url = ''; + + if ( ! isset( $cur->response ) ) + $cur->response = ''; + + switch ( $cur->response ) { + case 'development' : + return sprintf( __( 'You are using a development version (%1$s). Cool! Please stay updated.' ), get_bloginfo( 'version', 'display' ), network_admin_url( 'update-core.php' ) ); + + case 'upgrade' : + return sprintf( ''.__( 'Get Version %2$s' ).'', network_admin_url( 'update-core.php' ), $cur->current); + + case 'latest' : + default : + return sprintf( __( 'Version %s' ), get_bloginfo( 'version', 'display' ) ); + } +} +add_filter( 'update_footer', 'core_update_footer' ); + +function update_nag() { + if ( is_multisite() && !current_user_can('update_core') ) + return false; + + global $pagenow; + + if ( 'update-core.php' == $pagenow ) + return; + + $cur = get_preferred_from_update_core(); + + if ( ! isset( $cur->response ) || $cur->response != 'upgrade' ) + return false; + + if ( current_user_can('update_core') ) { + $msg = sprintf( __('WordPress %1$s is available! Please update now.'), $cur->current, network_admin_url( 'update-core.php' ) ); + } else { + $msg = sprintf( __('WordPress %1$s is available! Please notify the site administrator.'), $cur->current ); + } + echo "
    $msg
    "; +} +add_action( 'admin_notices', 'update_nag', 3 ); +add_action( 'network_admin_notices', 'update_nag', 3 ); + +// Called directly from dashboard +function update_right_now_message() { + $theme_name = wp_get_theme(); + if ( current_user_can( 'switch_themes' ) ) { + $theme_name = sprintf( '%1$s', $theme_name ); + } + + $msg = sprintf( __( 'WordPress %1$s running %2$s theme.' ), get_bloginfo( 'version', 'display' ), $theme_name ); + + if ( current_user_can('update_core') ) { + $cur = get_preferred_from_update_core(); + + if ( isset( $cur->response ) && $cur->response == 'upgrade' ) + $msg .= " " . sprintf( __('Update to %s'), $cur->current ? $cur->current : __( 'Latest' ) ) . ''; + } + + echo "

    $msg

    "; +} + +function get_plugin_updates() { + $all_plugins = get_plugins(); + $upgrade_plugins = array(); + $current = get_site_transient( 'update_plugins' ); + foreach ( (array)$all_plugins as $plugin_file => $plugin_data) { + if ( isset( $current->response[ $plugin_file ] ) ) { + $upgrade_plugins[ $plugin_file ] = (object) $plugin_data; + $upgrade_plugins[ $plugin_file ]->update = $current->response[ $plugin_file ]; + } + } + + return $upgrade_plugins; +} + +function wp_plugin_update_rows() { + if ( !current_user_can('update_plugins' ) ) + return; + + $plugins = get_site_transient( 'update_plugins' ); + if ( isset($plugins->response) && is_array($plugins->response) ) { + $plugins = array_keys( $plugins->response ); + foreach( $plugins as $plugin_file ) { + add_action( "after_plugin_row_$plugin_file", 'wp_plugin_update_row', 10, 2 ); + } + } +} +add_action( 'admin_init', 'wp_plugin_update_rows' ); + +function wp_plugin_update_row( $file, $plugin_data ) { + $current = get_site_transient( 'update_plugins' ); + if ( !isset( $current->response[ $file ] ) ) + return false; + + $r = $current->response[ $file ]; + + $plugins_allowedtags = array('a' => array('href' => array(),'title' => array()),'abbr' => array('title' => array()),'acronym' => array('title' => array()),'code' => array(),'em' => array(),'strong' => array()); + $plugin_name = wp_kses( $plugin_data['Name'], $plugins_allowedtags ); + + $details_url = self_admin_url('plugin-install.php?tab=plugin-information&plugin=' . $r->slug . '§ion=changelog&TB_iframe=true&width=600&height=800'); + + $wp_list_table = _get_list_table('WP_Plugins_List_Table'); + + if ( is_network_admin() || !is_multisite() ) { + echo '
    '; + + if ( ! current_user_can('update_plugins') ) + printf( __('There is a new version of %1$s available. View version %4$s details.'), $plugin_name, esc_url($details_url), esc_attr($plugin_name), $r->new_version ); + else if ( empty($r->package) ) + printf( __('There is a new version of %1$s available. View version %4$s details. Automatic update is unavailable for this plugin.'), $plugin_name, esc_url($details_url), esc_attr($plugin_name), $r->new_version ); + else + printf( __('There is a new version of %1$s available. View version %4$s details or update now.'), $plugin_name, esc_url($details_url), esc_attr($plugin_name), $r->new_version, wp_nonce_url( self_admin_url('update.php?action=upgrade-plugin&plugin=') . $file, 'upgrade-plugin_' . $file) ); + + /** + * Fires at the end of the update message container in each + * row of the plugins list table. + * + * The dynamic portion of the hook name, `$file`, refers to the path + * of the plugin's primary file relative to the plugins directory. + * + * @since 2.8.0 + * + * @param array $plugin_data { + * An array of plugin metadata. + * + * @type string $name The human-readable name of the plugin. + * @type string $plugin_uri Plugin URI. + * @type string $version Plugin version. + * @type string $description Plugin description. + * @type string $author Plugin author. + * @type string $author_uri Plugin author URI. + * @type string $text_domain Plugin text domain. + * @type string $domain_path Relative path to the plugin's .mo file(s). + * @type bool $network Whether the plugin can only be activated network wide. + * @type string $title The human-readable title of the plugin. + * @type string $author_name Plugin author's name. + * @type bool $update Whether there's an available update. Default null. + * } + * @param array $r { + * An array of metadata about the available plugin update. + * + * @type int $id Plugin ID. + * @type string $slug Plugin slug. + * @type string $new_version New plugin version. + * @type string $url Plugin URL. + * @type string $package Plugin update package URL. + * } + */ + do_action( "in_plugin_update_message-{$file}", $plugin_data, $r ); + + echo '
    '; + } +} + +function get_theme_updates() { + $current = get_site_transient('update_themes'); + + if ( ! isset( $current->response ) ) + return array(); + + $update_themes = array(); + foreach ( $current->response as $stylesheet => $data ) { + $update_themes[ $stylesheet ] = wp_get_theme( $stylesheet ); + $update_themes[ $stylesheet ]->update = $data; + } + + return $update_themes; +} + +function wp_theme_update_rows() { + if ( !current_user_can('update_themes' ) ) + return; + + $themes = get_site_transient( 'update_themes' ); + if ( isset($themes->response) && is_array($themes->response) ) { + $themes = array_keys( $themes->response ); + + foreach( $themes as $theme ) { + add_action( "after_theme_row_$theme", 'wp_theme_update_row', 10, 2 ); + } + } +} +add_action( 'admin_init', 'wp_theme_update_rows' ); + +function wp_theme_update_row( $theme_key, $theme ) { + $current = get_site_transient( 'update_themes' ); + if ( !isset( $current->response[ $theme_key ] ) ) + return false; + $r = $current->response[ $theme_key ]; + + $details_url = add_query_arg( array( 'TB_iframe' => 'true', 'width' => 1024, 'height' => 800 ), $current->response[ $theme_key ]['url'] ); + + $wp_list_table = _get_list_table('WP_MS_Themes_List_Table'); + + echo '
    '; + if ( ! current_user_can('update_themes') ) + printf( __('There is a new version of %1$s available. View version %4$s details.'), $theme['Name'], esc_url($details_url), esc_attr($theme['Name']), $r->new_version ); + else if ( empty( $r['package'] ) ) + printf( __('There is a new version of %1$s available. View version %4$s details. Automatic update is unavailable for this theme.'), $theme['Name'], esc_url($details_url), esc_attr($theme['Name']), $r['new_version'] ); + else + printf( __('There is a new version of %1$s available. View version %4$s details or update now.'), $theme['Name'], esc_url($details_url), esc_attr($theme['Name']), $r['new_version'], wp_nonce_url( self_admin_url('update.php?action=upgrade-theme&theme=') . $theme_key, 'upgrade-theme_' . $theme_key) ); + + /** + * Fires at the end of the update message container in each + * row of the themes list table. + * + * The dynamic portion of the hook name, `$theme_key`, refers to + * the theme slug as found in the WordPress.org themes repository. + * + * @since 3.1.0 + * + * @param WP_Theme $theme The WP_Theme object. + * @param array $r { + * An array of metadata about the available theme update. + * + * @type string $new_version New theme version. + * @type string $url Theme URL. + * @type string $package Theme update package URL. + * } + */ + do_action( "in_theme_update_message-{$theme_key}", $theme, $r ); + + echo '
    '; +} + +function maintenance_nag() { + include( ABSPATH . WPINC . '/version.php' ); // include an unmodified $wp_version + global $upgrading; + $nag = isset( $upgrading ); + if ( ! $nag ) { + $failed = get_site_option( 'auto_core_update_failed' ); + /* + * If an update failed critically, we may have copied over version.php but not other files. + * In that case, if the install claims we're running the version we attempted, nag. + * This is serious enough to err on the side of nagging. + * + * If we simply failed to update before we tried to copy any files, then assume things are + * OK if they are now running the latest. + * + * This flag is cleared whenever a successful update occurs using Core_Upgrader. + */ + $comparison = ! empty( $failed['critical'] ) ? '>=' : '>'; + if ( version_compare( $failed['attempted'], $wp_version, $comparison ) ) + $nag = true; + } + + if ( ! $nag ) + return false; + + if ( current_user_can('update_core') ) + $msg = sprintf( __('An automated WordPress update has failed to complete - please attempt the update again now.'), 'update-core.php' ); + else + $msg = __('An automated WordPress update has failed to complete! Please notify the site administrator.'); + + echo "
    $msg
    "; +} +add_action( 'admin_notices', 'maintenance_nag' ); +add_action( 'network_admin_notices', 'maintenance_nag' ); diff --git a/wp-admin/includes/upgrade.php b/wp-admin/includes/upgrade.php new file mode 100644 index 0000000..15a1097 --- /dev/null +++ b/wp-admin/includes/upgrade.php @@ -0,0 +1,2223 @@ +Note that password carefully! It is a random password that was generated just for you.'); + $user_id = wp_create_user($user_name, $user_password, $user_email); + update_user_option($user_id, 'default_password_nag', true, true); + $email_password = true; + } else if ( !$user_id ) { + // Password has been provided + $message = ''.__('Your chosen password.').''; + $user_id = wp_create_user($user_name, $user_password, $user_email); + } else { + $message = __('User already exists. Password inherited.'); + } + + $user = new WP_User($user_id); + $user->set_role('administrator'); + + wp_install_defaults($user_id); + + flush_rewrite_rules(); + + wp_new_blog_notification($blog_title, $guessurl, $user_id, ($email_password ? $user_password : __('The password you chose during the install.') ) ); + + wp_cache_flush(); + + /** + * Fires after a site is fully installed. + * + * @since 3.9.0 + * + * @param WP_User $user The site owner. + */ + do_action( 'wp_install', $user ); + + return array('url' => $guessurl, 'user_id' => $user_id, 'password' => $user_password, 'password_message' => $message); +} +endif; + +if ( !function_exists('wp_install_defaults') ) : +/** + * {@internal Missing Short Description}} + * + * {@internal Missing Long Description}} + * + * @since 2.1.0 + * + * @param int $user_id User ID. + */ +function wp_install_defaults( $user_id ) { + global $wpdb, $wp_rewrite, $table_prefix; + + // Default category + $cat_name = __('Uncategorized'); + /* translators: Default category slug */ + $cat_slug = sanitize_title(_x('Uncategorized', 'Default category slug')); + + if ( global_terms_enabled() ) { + $cat_id = $wpdb->get_var( $wpdb->prepare( "SELECT cat_ID FROM {$wpdb->sitecategories} WHERE category_nicename = %s", $cat_slug ) ); + if ( $cat_id == null ) { + $wpdb->insert( $wpdb->sitecategories, array('cat_ID' => 0, 'cat_name' => $cat_name, 'category_nicename' => $cat_slug, 'last_updated' => current_time('mysql', true)) ); + $cat_id = $wpdb->insert_id; + } + update_option('default_category', $cat_id); + } else { + $cat_id = 1; + } + + $wpdb->insert( $wpdb->terms, array('term_id' => $cat_id, 'name' => $cat_name, 'slug' => $cat_slug, 'term_group' => 0) ); + $wpdb->insert( $wpdb->term_taxonomy, array('term_id' => $cat_id, 'taxonomy' => 'category', 'description' => '', 'parent' => 0, 'count' => 1)); + $cat_tt_id = $wpdb->insert_id; + + // First post + $now = date('Y-m-d H:i:s'); + $now_gmt = gmdate('Y-m-d H:i:s'); + $first_post_guid = get_option('home') . '/?p=1'; + + if ( is_multisite() ) { + $first_post = get_site_option( 'first_post' ); + + if ( empty($first_post) ) + $first_post = __( 'Welcome to SITE_NAME. This is your first post. Edit or delete it, then start blogging!' ); + + $first_post = str_replace( "SITE_URL", esc_url( network_home_url() ), $first_post ); + $first_post = str_replace( "SITE_NAME", get_current_site()->site_name, $first_post ); + } else { + $first_post = __('Welcome to WordPress. This is your first post. Edit or delete it, then start blogging!'); + } + + $wpdb->insert( $wpdb->posts, array( + 'post_author' => $user_id, + 'post_date' => $now, + 'post_date_gmt' => $now_gmt, + 'post_content' => $first_post, + 'post_excerpt' => '', + 'post_title' => __('Hello world!'), + /* translators: Default post slug */ + 'post_name' => sanitize_title( _x('hello-world', 'Default post slug') ), + 'post_modified' => $now, + 'post_modified_gmt' => $now_gmt, + 'guid' => $first_post_guid, + 'comment_count' => 1, + 'to_ping' => '', + 'pinged' => '', + 'post_content_filtered' => '' + )); + $wpdb->insert( $wpdb->term_relationships, array('term_taxonomy_id' => $cat_tt_id, 'object_id' => 1) ); + + // Default comment + $first_comment_author = __('Mr WordPress'); + $first_comment_url = 'https://wordpress.org/'; + $first_comment = __('Hi, this is a comment. +To delete a comment, just log in and view the post's comments. There you will have the option to edit or delete them.'); + if ( is_multisite() ) { + $first_comment_author = get_site_option( 'first_comment_author', $first_comment_author ); + $first_comment_url = get_site_option( 'first_comment_url', network_home_url() ); + $first_comment = get_site_option( 'first_comment', $first_comment ); + } + $wpdb->insert( $wpdb->comments, array( + 'comment_post_ID' => 1, + 'comment_author' => $first_comment_author, + 'comment_author_email' => '', + 'comment_author_url' => $first_comment_url, + 'comment_date' => $now, + 'comment_date_gmt' => $now_gmt, + 'comment_content' => $first_comment + )); + + // First Page + $first_page = sprintf( __( "This is an example page. It's different from a blog post because it will stay in one place and will show up in your site navigation (in most themes). Most people start with an About page that introduces them to potential site visitors. It might say something like this: + +
    Hi there! I'm a bike messenger by day, aspiring actor by night, and this is my blog. I live in Los Angeles, have a great dog named Jack, and I like piña coladas. (And gettin' caught in the rain.)
    + +...or something like this: + +
    The XYZ Doohickey Company was founded in 1971, and has been providing quality doohickeys to the public ever since. Located in Gotham City, XYZ employs over 2,000 people and does all kinds of awesome things for the Gotham community.
    + +As a new WordPress user, you should go to your dashboard to delete this page and create new pages for your content. Have fun!" ), admin_url() ); + if ( is_multisite() ) + $first_page = get_site_option( 'first_page', $first_page ); + $first_post_guid = get_option('home') . '/?page_id=2'; + $wpdb->insert( $wpdb->posts, array( + 'post_author' => $user_id, + 'post_date' => $now, + 'post_date_gmt' => $now_gmt, + 'post_content' => $first_page, + 'post_excerpt' => '', + 'post_title' => __( 'Sample Page' ), + /* translators: Default page slug */ + 'post_name' => __( 'sample-page' ), + 'post_modified' => $now, + 'post_modified_gmt' => $now_gmt, + 'guid' => $first_post_guid, + 'post_type' => 'page', + 'to_ping' => '', + 'pinged' => '', + 'post_content_filtered' => '' + )); + $wpdb->insert( $wpdb->postmeta, array( 'post_id' => 2, 'meta_key' => '_wp_page_template', 'meta_value' => 'default' ) ); + + // Set up default widgets for default theme. + update_option( 'widget_search', array ( 2 => array ( 'title' => '' ), '_multiwidget' => 1 ) ); + update_option( 'widget_recent-posts', array ( 2 => array ( 'title' => '', 'number' => 5 ), '_multiwidget' => 1 ) ); + update_option( 'widget_recent-comments', array ( 2 => array ( 'title' => '', 'number' => 5 ), '_multiwidget' => 1 ) ); + update_option( 'widget_archives', array ( 2 => array ( 'title' => '', 'count' => 0, 'dropdown' => 0 ), '_multiwidget' => 1 ) ); + update_option( 'widget_categories', array ( 2 => array ( 'title' => '', 'count' => 0, 'hierarchical' => 0, 'dropdown' => 0 ), '_multiwidget' => 1 ) ); + update_option( 'widget_meta', array ( 2 => array ( 'title' => '' ), '_multiwidget' => 1 ) ); + update_option( 'sidebars_widgets', array ( 'wp_inactive_widgets' => array (), 'sidebar-1' => array ( 0 => 'search-2', 1 => 'recent-posts-2', 2 => 'recent-comments-2', 3 => 'archives-2', 4 => 'categories-2', 5 => 'meta-2', ), 'array_version' => 3 ) ); + + if ( ! is_multisite() ) + update_user_meta( $user_id, 'show_welcome_panel', 1 ); + elseif ( ! is_super_admin( $user_id ) && ! metadata_exists( 'user', $user_id, 'show_welcome_panel' ) ) + update_user_meta( $user_id, 'show_welcome_panel', 2 ); + + if ( is_multisite() ) { + // Flush rules to pick up the new page. + $wp_rewrite->init(); + $wp_rewrite->flush_rules(); + + $user = new WP_User($user_id); + $wpdb->update( $wpdb->options, array('option_value' => $user->user_email), array('option_name' => 'admin_email') ); + + // Remove all perms except for the login user. + $wpdb->query( $wpdb->prepare("DELETE FROM $wpdb->usermeta WHERE user_id != %d AND meta_key = %s", $user_id, $table_prefix.'user_level') ); + $wpdb->query( $wpdb->prepare("DELETE FROM $wpdb->usermeta WHERE user_id != %d AND meta_key = %s", $user_id, $table_prefix.'capabilities') ); + + // Delete any caps that snuck into the previously active blog. (Hardcoded to blog 1 for now.) TODO: Get previous_blog_id. + if ( !is_super_admin( $user_id ) && $user_id != 1 ) + $wpdb->delete( $wpdb->usermeta, array( 'user_id' => $user_id , 'meta_key' => $wpdb->base_prefix.'1_capabilities' ) ); + } +} +endif; + +if ( !function_exists('wp_new_blog_notification') ) : +/** + * {@internal Missing Short Description}} + * + * {@internal Missing Long Description}} + * + * @since 2.1.0 + * + * @param string $blog_title Blog title. + * @param string $blog_url Blog url. + * @param int $user_id User ID. + * @param string $password User's Password. + */ +function wp_new_blog_notification($blog_title, $blog_url, $user_id, $password) { + $user = new WP_User( $user_id ); + $email = $user->user_email; + $name = $user->user_login; + $login_url = wp_login_url(); + $message = sprintf( __( "Your new WordPress site has been successfully set up at: + +%1\$s + +You can log in to the administrator account with the following information: + +Username: %2\$s +Password: %3\$s +Log in here: %4\$s + +We hope you enjoy your new site. Thanks! + +--The WordPress Team +https://wordpress.org/ +"), $blog_url, $name, $password, $login_url ); + + @wp_mail($email, __('New WordPress Site'), $message); +} +endif; + +if ( !function_exists('wp_upgrade') ) : +/** + * Run WordPress Upgrade functions. + * + * {@internal Missing Long Description}} + * + * @since 2.1.0 + * + * @return null + */ +function wp_upgrade() { + global $wp_current_db_version, $wp_db_version, $wpdb; + + $wp_current_db_version = __get_option('db_version'); + + // We are up-to-date. Nothing to do. + if ( $wp_db_version == $wp_current_db_version ) + return; + + if ( ! is_blog_installed() ) + return; + + wp_check_mysql_version(); + wp_cache_flush(); + pre_schema_upgrade(); + make_db_current_silent(); + upgrade_all(); + if ( is_multisite() && is_main_site() ) + upgrade_network(); + wp_cache_flush(); + + if ( is_multisite() ) { + if ( $wpdb->get_row( "SELECT blog_id FROM {$wpdb->blog_versions} WHERE blog_id = '{$wpdb->blogid}'" ) ) + $wpdb->query( "UPDATE {$wpdb->blog_versions} SET db_version = '{$wp_db_version}' WHERE blog_id = '{$wpdb->blogid}'" ); + else + $wpdb->query( "INSERT INTO {$wpdb->blog_versions} ( `blog_id` , `db_version` , `last_updated` ) VALUES ( '{$wpdb->blogid}', '{$wp_db_version}', NOW());" ); + } + + /** + * Fires after a site is fully upgraded. + * + * @since 3.9.0 + * + * @param int $wp_db_version The new $wp_db_version. + * @param int $wp_current_db_version The old (current) $wp_db_version. + */ + do_action( 'wp_upgrade', $wp_db_version, $wp_current_db_version ); +} +endif; + +/** + * Functions to be called in install and upgrade scripts. + * + * {@internal Missing Long Description}} + * + * @since 1.0.1 + */ +function upgrade_all() { + global $wp_current_db_version, $wp_db_version; + $wp_current_db_version = __get_option('db_version'); + + // We are up-to-date. Nothing to do. + if ( $wp_db_version == $wp_current_db_version ) + return; + + // If the version is not set in the DB, try to guess the version. + if ( empty($wp_current_db_version) ) { + $wp_current_db_version = 0; + + // If the template option exists, we have 1.5. + $template = __get_option('template'); + if ( !empty($template) ) + $wp_current_db_version = 2541; + } + + if ( $wp_current_db_version < 6039 ) + upgrade_230_options_table(); + + populate_options(); + + if ( $wp_current_db_version < 2541 ) { + upgrade_100(); + upgrade_101(); + upgrade_110(); + upgrade_130(); + } + + if ( $wp_current_db_version < 3308 ) + upgrade_160(); + + if ( $wp_current_db_version < 4772 ) + upgrade_210(); + + if ( $wp_current_db_version < 4351 ) + upgrade_old_slugs(); + + if ( $wp_current_db_version < 5539 ) + upgrade_230(); + + if ( $wp_current_db_version < 6124 ) + upgrade_230_old_tables(); + + if ( $wp_current_db_version < 7499 ) + upgrade_250(); + + if ( $wp_current_db_version < 7935 ) + upgrade_252(); + + if ( $wp_current_db_version < 8201 ) + upgrade_260(); + + if ( $wp_current_db_version < 8989 ) + upgrade_270(); + + if ( $wp_current_db_version < 10360 ) + upgrade_280(); + + if ( $wp_current_db_version < 11958 ) + upgrade_290(); + + if ( $wp_current_db_version < 15260 ) + upgrade_300(); + + if ( $wp_current_db_version < 19389 ) + upgrade_330(); + + if ( $wp_current_db_version < 20080 ) + upgrade_340(); + + if ( $wp_current_db_version < 22422 ) + upgrade_350(); + + if ( $wp_current_db_version < 25824 ) + upgrade_370(); + + if ( $wp_current_db_version < 26148 ) + upgrade_372(); + + if ( $wp_current_db_version < 26691 ) + upgrade_380(); + + if ( $wp_current_db_version < 29630 ) + upgrade_400(); + + maybe_disable_link_manager(); + + maybe_disable_automattic_widgets(); + + update_option( 'db_version', $wp_db_version ); + update_option( 'db_upgraded', true ); +} + +/** + * Execute changes made in WordPress 1.0. + * + * @since 1.0.0 + */ +function upgrade_100() { + global $wpdb; + + // Get the title and ID of every post, post_name to check if it already has a value + $posts = $wpdb->get_results("SELECT ID, post_title, post_name FROM $wpdb->posts WHERE post_name = ''"); + if ($posts) { + foreach($posts as $post) { + if ('' == $post->post_name) { + $newtitle = sanitize_title($post->post_title); + $wpdb->query( $wpdb->prepare("UPDATE $wpdb->posts SET post_name = %s WHERE ID = %d", $newtitle, $post->ID) ); + } + } + } + + $categories = $wpdb->get_results("SELECT cat_ID, cat_name, category_nicename FROM $wpdb->categories"); + foreach ($categories as $category) { + if ('' == $category->category_nicename) { + $newtitle = sanitize_title($category->cat_name); + $wpdb->update( $wpdb->categories, array('category_nicename' => $newtitle), array('cat_ID' => $category->cat_ID) ); + } + } + + $sql = "UPDATE $wpdb->options + SET option_value = REPLACE(option_value, 'wp-links/links-images/', 'wp-images/links/') + WHERE option_name LIKE %s + AND option_value LIKE %s"; + $wpdb->query( $wpdb->prepare( $sql, $wpdb->esc_like( 'links_rating_image' ) . '%', $wpdb->esc_like( 'wp-links/links-images/' ) . '%' ) ); + + $done_ids = $wpdb->get_results("SELECT DISTINCT post_id FROM $wpdb->post2cat"); + if ($done_ids) : + foreach ($done_ids as $done_id) : + $done_posts[] = $done_id->post_id; + endforeach; + $catwhere = ' AND ID NOT IN (' . implode(',', $done_posts) . ')'; + else: + $catwhere = ''; + endif; + + $allposts = $wpdb->get_results("SELECT ID, post_category FROM $wpdb->posts WHERE post_category != '0' $catwhere"); + if ($allposts) : + foreach ($allposts as $post) { + // Check to see if it's already been imported + $cat = $wpdb->get_row( $wpdb->prepare("SELECT * FROM $wpdb->post2cat WHERE post_id = %d AND category_id = %d", $post->ID, $post->post_category) ); + if (!$cat && 0 != $post->post_category) { // If there's no result + $wpdb->insert( $wpdb->post2cat, array('post_id' => $post->ID, 'category_id' => $post->post_category) ); + } + } + endif; +} + +/** + * Execute changes made in WordPress 1.0.1. + * + * @since 1.0.1 + */ +function upgrade_101() { + global $wpdb; + + // Clean up indices, add a few + add_clean_index($wpdb->posts, 'post_name'); + add_clean_index($wpdb->posts, 'post_status'); + add_clean_index($wpdb->categories, 'category_nicename'); + add_clean_index($wpdb->comments, 'comment_approved'); + add_clean_index($wpdb->comments, 'comment_post_ID'); + add_clean_index($wpdb->links , 'link_category'); + add_clean_index($wpdb->links , 'link_visible'); +} + +/** + * Execute changes made in WordPress 1.2. + * + * @since 1.2.0 + */ +function upgrade_110() { + global $wpdb; + + // Set user_nicename. + $users = $wpdb->get_results("SELECT ID, user_nickname, user_nicename FROM $wpdb->users"); + foreach ($users as $user) { + if ('' == $user->user_nicename) { + $newname = sanitize_title($user->user_nickname); + $wpdb->update( $wpdb->users, array('user_nicename' => $newname), array('ID' => $user->ID) ); + } + } + + $users = $wpdb->get_results("SELECT ID, user_pass from $wpdb->users"); + foreach ($users as $row) { + if (!preg_match('/^[A-Fa-f0-9]{32}$/', $row->user_pass)) { + $wpdb->update( $wpdb->users, array('user_pass' => md5($row->user_pass)), array('ID' => $row->ID) ); + } + } + + // Get the GMT offset, we'll use that later on + $all_options = get_alloptions_110(); + + $time_difference = $all_options->time_difference; + + $server_time = time()+date('Z'); + $weblogger_time = $server_time + $time_difference * HOUR_IN_SECONDS; + $gmt_time = time(); + + $diff_gmt_server = ($gmt_time - $server_time) / HOUR_IN_SECONDS; + $diff_weblogger_server = ($weblogger_time - $server_time) / HOUR_IN_SECONDS; + $diff_gmt_weblogger = $diff_gmt_server - $diff_weblogger_server; + $gmt_offset = -$diff_gmt_weblogger; + + // Add a gmt_offset option, with value $gmt_offset + add_option('gmt_offset', $gmt_offset); + + // Check if we already set the GMT fields (if we did, then + // MAX(post_date_gmt) can't be '0000-00-00 00:00:00' + // I just slapped myself silly for not thinking about it earlier + $got_gmt_fields = ! ($wpdb->get_var("SELECT MAX(post_date_gmt) FROM $wpdb->posts") == '0000-00-00 00:00:00'); + + if (!$got_gmt_fields) { + + // Add or subtract time to all dates, to get GMT dates + $add_hours = intval($diff_gmt_weblogger); + $add_minutes = intval(60 * ($diff_gmt_weblogger - $add_hours)); + $wpdb->query("UPDATE $wpdb->posts SET post_date_gmt = DATE_ADD(post_date, INTERVAL '$add_hours:$add_minutes' HOUR_MINUTE)"); + $wpdb->query("UPDATE $wpdb->posts SET post_modified = post_date"); + $wpdb->query("UPDATE $wpdb->posts SET post_modified_gmt = DATE_ADD(post_modified, INTERVAL '$add_hours:$add_minutes' HOUR_MINUTE) WHERE post_modified != '0000-00-00 00:00:00'"); + $wpdb->query("UPDATE $wpdb->comments SET comment_date_gmt = DATE_ADD(comment_date, INTERVAL '$add_hours:$add_minutes' HOUR_MINUTE)"); + $wpdb->query("UPDATE $wpdb->users SET user_registered = DATE_ADD(user_registered, INTERVAL '$add_hours:$add_minutes' HOUR_MINUTE)"); + } + +} + +/** + * Execute changes made in WordPress 1.5. + * + * @since 1.5.0 + */ +function upgrade_130() { + global $wpdb; + + // Remove extraneous backslashes. + $posts = $wpdb->get_results("SELECT ID, post_title, post_content, post_excerpt, guid, post_date, post_name, post_status, post_author FROM $wpdb->posts"); + if ($posts) { + foreach($posts as $post) { + $post_content = addslashes(deslash($post->post_content)); + $post_title = addslashes(deslash($post->post_title)); + $post_excerpt = addslashes(deslash($post->post_excerpt)); + if ( empty($post->guid) ) + $guid = get_permalink($post->ID); + else + $guid = $post->guid; + + $wpdb->update( $wpdb->posts, compact('post_title', 'post_content', 'post_excerpt', 'guid'), array('ID' => $post->ID) ); + + } + } + + // Remove extraneous backslashes. + $comments = $wpdb->get_results("SELECT comment_ID, comment_author, comment_content FROM $wpdb->comments"); + if ($comments) { + foreach($comments as $comment) { + $comment_content = deslash($comment->comment_content); + $comment_author = deslash($comment->comment_author); + + $wpdb->update($wpdb->comments, compact('comment_content', 'comment_author'), array('comment_ID' => $comment->comment_ID) ); + } + } + + // Remove extraneous backslashes. + $links = $wpdb->get_results("SELECT link_id, link_name, link_description FROM $wpdb->links"); + if ($links) { + foreach($links as $link) { + $link_name = deslash($link->link_name); + $link_description = deslash($link->link_description); + + $wpdb->update( $wpdb->links, compact('link_name', 'link_description'), array('link_id' => $link->link_id) ); + } + } + + $active_plugins = __get_option('active_plugins'); + + /* + * If plugins are not stored in an array, they're stored in the old + * newline separated format. Convert to new format. + */ + if ( !is_array( $active_plugins ) ) { + $active_plugins = explode("\n", trim($active_plugins)); + update_option('active_plugins', $active_plugins); + } + + // Obsolete tables + $wpdb->query('DROP TABLE IF EXISTS ' . $wpdb->prefix . 'optionvalues'); + $wpdb->query('DROP TABLE IF EXISTS ' . $wpdb->prefix . 'optiontypes'); + $wpdb->query('DROP TABLE IF EXISTS ' . $wpdb->prefix . 'optiongroups'); + $wpdb->query('DROP TABLE IF EXISTS ' . $wpdb->prefix . 'optiongroup_options'); + + // Update comments table to use comment_type + $wpdb->query("UPDATE $wpdb->comments SET comment_type='trackback', comment_content = REPLACE(comment_content, '', '') WHERE comment_content LIKE '%'"); + $wpdb->query("UPDATE $wpdb->comments SET comment_type='pingback', comment_content = REPLACE(comment_content, '', '') WHERE comment_content LIKE '%'"); + + // Some versions have multiple duplicate option_name rows with the same values + $options = $wpdb->get_results("SELECT option_name, COUNT(option_name) AS dupes FROM `$wpdb->options` GROUP BY option_name"); + foreach ( $options as $option ) { + if ( 1 != $option->dupes ) { // Could this be done in the query? + $limit = $option->dupes - 1; + $dupe_ids = $wpdb->get_col( $wpdb->prepare("SELECT option_id FROM $wpdb->options WHERE option_name = %s LIMIT %d", $option->option_name, $limit) ); + if ( $dupe_ids ) { + $dupe_ids = join($dupe_ids, ','); + $wpdb->query("DELETE FROM $wpdb->options WHERE option_id IN ($dupe_ids)"); + } + } + } + + make_site_theme(); +} + +/** + * Execute changes made in WordPress 2.0. + * + * @since 2.0.0 + */ +function upgrade_160() { + global $wpdb, $wp_current_db_version; + + populate_roles_160(); + + $users = $wpdb->get_results("SELECT * FROM $wpdb->users"); + foreach ( $users as $user ) : + if ( !empty( $user->user_firstname ) ) + update_user_meta( $user->ID, 'first_name', wp_slash($user->user_firstname) ); + if ( !empty( $user->user_lastname ) ) + update_user_meta( $user->ID, 'last_name', wp_slash($user->user_lastname) ); + if ( !empty( $user->user_nickname ) ) + update_user_meta( $user->ID, 'nickname', wp_slash($user->user_nickname) ); + if ( !empty( $user->user_level ) ) + update_user_meta( $user->ID, $wpdb->prefix . 'user_level', $user->user_level ); + if ( !empty( $user->user_icq ) ) + update_user_meta( $user->ID, 'icq', wp_slash($user->user_icq) ); + if ( !empty( $user->user_aim ) ) + update_user_meta( $user->ID, 'aim', wp_slash($user->user_aim) ); + if ( !empty( $user->user_msn ) ) + update_user_meta( $user->ID, 'msn', wp_slash($user->user_msn) ); + if ( !empty( $user->user_yim ) ) + update_user_meta( $user->ID, 'yim', wp_slash($user->user_icq) ); + if ( !empty( $user->user_description ) ) + update_user_meta( $user->ID, 'description', wp_slash($user->user_description) ); + + if ( isset( $user->user_idmode ) ): + $idmode = $user->user_idmode; + if ($idmode == 'nickname') $id = $user->user_nickname; + if ($idmode == 'login') $id = $user->user_login; + if ($idmode == 'firstname') $id = $user->user_firstname; + if ($idmode == 'lastname') $id = $user->user_lastname; + if ($idmode == 'namefl') $id = $user->user_firstname.' '.$user->user_lastname; + if ($idmode == 'namelf') $id = $user->user_lastname.' '.$user->user_firstname; + if (!$idmode) $id = $user->user_nickname; + $wpdb->update( $wpdb->users, array('display_name' => $id), array('ID' => $user->ID) ); + endif; + + // FIXME: RESET_CAPS is temporary code to reset roles and caps if flag is set. + $caps = get_user_meta( $user->ID, $wpdb->prefix . 'capabilities'); + if ( empty($caps) || defined('RESET_CAPS') ) { + $level = get_user_meta($user->ID, $wpdb->prefix . 'user_level', true); + $role = translate_level_to_role($level); + update_user_meta( $user->ID, $wpdb->prefix . 'capabilities', array($role => true) ); + } + + endforeach; + $old_user_fields = array( 'user_firstname', 'user_lastname', 'user_icq', 'user_aim', 'user_msn', 'user_yim', 'user_idmode', 'user_ip', 'user_domain', 'user_browser', 'user_description', 'user_nickname', 'user_level' ); + $wpdb->hide_errors(); + foreach ( $old_user_fields as $old ) + $wpdb->query("ALTER TABLE $wpdb->users DROP $old"); + $wpdb->show_errors(); + + // Populate comment_count field of posts table. + $comments = $wpdb->get_results( "SELECT comment_post_ID, COUNT(*) as c FROM $wpdb->comments WHERE comment_approved = '1' GROUP BY comment_post_ID" ); + if ( is_array( $comments ) ) + foreach ($comments as $comment) + $wpdb->update( $wpdb->posts, array('comment_count' => $comment->c), array('ID' => $comment->comment_post_ID) ); + + /* + * Some alpha versions used a post status of object instead of attachment + * and put the mime type in post_type instead of post_mime_type. + */ + if ( $wp_current_db_version > 2541 && $wp_current_db_version <= 3091 ) { + $objects = $wpdb->get_results("SELECT ID, post_type FROM $wpdb->posts WHERE post_status = 'object'"); + foreach ($objects as $object) { + $wpdb->update( $wpdb->posts, array( 'post_status' => 'attachment', + 'post_mime_type' => $object->post_type, + 'post_type' => ''), + array( 'ID' => $object->ID ) ); + + $meta = get_post_meta($object->ID, 'imagedata', true); + if ( ! empty($meta['file']) ) + update_attached_file( $object->ID, $meta['file'] ); + } + } +} + +/** + * Execute changes made in WordPress 2.1. + * + * @since 2.1.0 + */ +function upgrade_210() { + global $wpdb, $wp_current_db_version; + + if ( $wp_current_db_version < 3506 ) { + // Update status and type. + $posts = $wpdb->get_results("SELECT ID, post_status FROM $wpdb->posts"); + + if ( ! empty($posts) ) foreach ($posts as $post) { + $status = $post->post_status; + $type = 'post'; + + if ( 'static' == $status ) { + $status = 'publish'; + $type = 'page'; + } else if ( 'attachment' == $status ) { + $status = 'inherit'; + $type = 'attachment'; + } + + $wpdb->query( $wpdb->prepare("UPDATE $wpdb->posts SET post_status = %s, post_type = %s WHERE ID = %d", $status, $type, $post->ID) ); + } + } + + if ( $wp_current_db_version < 3845 ) { + populate_roles_210(); + } + + if ( $wp_current_db_version < 3531 ) { + // Give future posts a post_status of future. + $now = gmdate('Y-m-d H:i:59'); + $wpdb->query ("UPDATE $wpdb->posts SET post_status = 'future' WHERE post_status = 'publish' AND post_date_gmt > '$now'"); + + $posts = $wpdb->get_results("SELECT ID, post_date FROM $wpdb->posts WHERE post_status ='future'"); + if ( !empty($posts) ) + foreach ( $posts as $post ) + wp_schedule_single_event(mysql2date('U', $post->post_date, false), 'publish_future_post', array($post->ID)); + } +} + +/** + * Execute changes made in WordPress 2.3. + * + * @since 2.3.0 + */ +function upgrade_230() { + global $wp_current_db_version, $wpdb; + + if ( $wp_current_db_version < 5200 ) { + populate_roles_230(); + } + + // Convert categories to terms. + $tt_ids = array(); + $have_tags = false; + $categories = $wpdb->get_results("SELECT * FROM $wpdb->categories ORDER BY cat_ID"); + foreach ($categories as $category) { + $term_id = (int) $category->cat_ID; + $name = $category->cat_name; + $description = $category->category_description; + $slug = $category->category_nicename; + $parent = $category->category_parent; + $term_group = 0; + + // Associate terms with the same slug in a term group and make slugs unique. + if ( $exists = $wpdb->get_results( $wpdb->prepare("SELECT term_id, term_group FROM $wpdb->terms WHERE slug = %s", $slug) ) ) { + $term_group = $exists[0]->term_group; + $id = $exists[0]->term_id; + $num = 2; + do { + $alt_slug = $slug . "-$num"; + $num++; + $slug_check = $wpdb->get_var( $wpdb->prepare("SELECT slug FROM $wpdb->terms WHERE slug = %s", $alt_slug) ); + } while ( $slug_check ); + + $slug = $alt_slug; + + if ( empty( $term_group ) ) { + $term_group = $wpdb->get_var("SELECT MAX(term_group) FROM $wpdb->terms GROUP BY term_group") + 1; + $wpdb->query( $wpdb->prepare("UPDATE $wpdb->terms SET term_group = %d WHERE term_id = %d", $term_group, $id) ); + } + } + + $wpdb->query( $wpdb->prepare("INSERT INTO $wpdb->terms (term_id, name, slug, term_group) VALUES + (%d, %s, %s, %d)", $term_id, $name, $slug, $term_group) ); + + $count = 0; + if ( !empty($category->category_count) ) { + $count = (int) $category->category_count; + $taxonomy = 'category'; + $wpdb->query( $wpdb->prepare("INSERT INTO $wpdb->term_taxonomy (term_id, taxonomy, description, parent, count) VALUES ( %d, %s, %s, %d, %d)", $term_id, $taxonomy, $description, $parent, $count) ); + $tt_ids[$term_id][$taxonomy] = (int) $wpdb->insert_id; + } + + if ( !empty($category->link_count) ) { + $count = (int) $category->link_count; + $taxonomy = 'link_category'; + $wpdb->query( $wpdb->prepare("INSERT INTO $wpdb->term_taxonomy (term_id, taxonomy, description, parent, count) VALUES ( %d, %s, %s, %d, %d)", $term_id, $taxonomy, $description, $parent, $count) ); + $tt_ids[$term_id][$taxonomy] = (int) $wpdb->insert_id; + } + + if ( !empty($category->tag_count) ) { + $have_tags = true; + $count = (int) $category->tag_count; + $taxonomy = 'post_tag'; + $wpdb->insert( $wpdb->term_taxonomy, compact('term_id', 'taxonomy', 'description', 'parent', 'count') ); + $tt_ids[$term_id][$taxonomy] = (int) $wpdb->insert_id; + } + + if ( empty($count) ) { + $count = 0; + $taxonomy = 'category'; + $wpdb->insert( $wpdb->term_taxonomy, compact('term_id', 'taxonomy', 'description', 'parent', 'count') ); + $tt_ids[$term_id][$taxonomy] = (int) $wpdb->insert_id; + } + } + + $select = 'post_id, category_id'; + if ( $have_tags ) + $select .= ', rel_type'; + + $posts = $wpdb->get_results("SELECT $select FROM $wpdb->post2cat GROUP BY post_id, category_id"); + foreach ( $posts as $post ) { + $post_id = (int) $post->post_id; + $term_id = (int) $post->category_id; + $taxonomy = 'category'; + if ( !empty($post->rel_type) && 'tag' == $post->rel_type) + $taxonomy = 'tag'; + $tt_id = $tt_ids[$term_id][$taxonomy]; + if ( empty($tt_id) ) + continue; + + $wpdb->insert( $wpdb->term_relationships, array('object_id' => $post_id, 'term_taxonomy_id' => $tt_id) ); + } + + // < 3570 we used linkcategories. >= 3570 we used categories and link2cat. + if ( $wp_current_db_version < 3570 ) { + /* + * Create link_category terms for link categories. Create a map of link + * cat IDs to link_category terms. + */ + $link_cat_id_map = array(); + $default_link_cat = 0; + $tt_ids = array(); + $link_cats = $wpdb->get_results("SELECT cat_id, cat_name FROM " . $wpdb->prefix . 'linkcategories'); + foreach ( $link_cats as $category) { + $cat_id = (int) $category->cat_id; + $term_id = 0; + $name = wp_slash($category->cat_name); + $slug = sanitize_title($name); + $term_group = 0; + + // Associate terms with the same slug in a term group and make slugs unique. + if ( $exists = $wpdb->get_results( $wpdb->prepare("SELECT term_id, term_group FROM $wpdb->terms WHERE slug = %s", $slug) ) ) { + $term_group = $exists[0]->term_group; + $term_id = $exists[0]->term_id; + } + + if ( empty($term_id) ) { + $wpdb->insert( $wpdb->terms, compact('name', 'slug', 'term_group') ); + $term_id = (int) $wpdb->insert_id; + } + + $link_cat_id_map[$cat_id] = $term_id; + $default_link_cat = $term_id; + + $wpdb->insert( $wpdb->term_taxonomy, array('term_id' => $term_id, 'taxonomy' => 'link_category', 'description' => '', 'parent' => 0, 'count' => 0) ); + $tt_ids[$term_id] = (int) $wpdb->insert_id; + } + + // Associate links to cats. + $links = $wpdb->get_results("SELECT link_id, link_category FROM $wpdb->links"); + if ( !empty($links) ) foreach ( $links as $link ) { + if ( 0 == $link->link_category ) + continue; + if ( ! isset($link_cat_id_map[$link->link_category]) ) + continue; + $term_id = $link_cat_id_map[$link->link_category]; + $tt_id = $tt_ids[$term_id]; + if ( empty($tt_id) ) + continue; + + $wpdb->insert( $wpdb->term_relationships, array('object_id' => $link->link_id, 'term_taxonomy_id' => $tt_id) ); + } + + // Set default to the last category we grabbed during the upgrade loop. + update_option('default_link_category', $default_link_cat); + } else { + $links = $wpdb->get_results("SELECT link_id, category_id FROM $wpdb->link2cat GROUP BY link_id, category_id"); + foreach ( $links as $link ) { + $link_id = (int) $link->link_id; + $term_id = (int) $link->category_id; + $taxonomy = 'link_category'; + $tt_id = $tt_ids[$term_id][$taxonomy]; + if ( empty($tt_id) ) + continue; + $wpdb->insert( $wpdb->term_relationships, array('object_id' => $link_id, 'term_taxonomy_id' => $tt_id) ); + } + } + + if ( $wp_current_db_version < 4772 ) { + // Obsolete linkcategories table + $wpdb->query('DROP TABLE IF EXISTS ' . $wpdb->prefix . 'linkcategories'); + } + + // Recalculate all counts + $terms = $wpdb->get_results("SELECT term_taxonomy_id, taxonomy FROM $wpdb->term_taxonomy"); + foreach ( (array) $terms as $term ) { + if ( ('post_tag' == $term->taxonomy) || ('category' == $term->taxonomy) ) + $count = $wpdb->get_var( $wpdb->prepare("SELECT COUNT(*) FROM $wpdb->term_relationships, $wpdb->posts WHERE $wpdb->posts.ID = $wpdb->term_relationships.object_id AND post_status = 'publish' AND post_type = 'post' AND term_taxonomy_id = %d", $term->term_taxonomy_id) ); + else + $count = $wpdb->get_var( $wpdb->prepare("SELECT COUNT(*) FROM $wpdb->term_relationships WHERE term_taxonomy_id = %d", $term->term_taxonomy_id) ); + $wpdb->update( $wpdb->term_taxonomy, array('count' => $count), array('term_taxonomy_id' => $term->term_taxonomy_id) ); + } +} + +/** + * Remove old options from the database. + * + * @since 2.3.0 + */ +function upgrade_230_options_table() { + global $wpdb; + $old_options_fields = array( 'option_can_override', 'option_type', 'option_width', 'option_height', 'option_description', 'option_admin_level' ); + $wpdb->hide_errors(); + foreach ( $old_options_fields as $old ) + $wpdb->query("ALTER TABLE $wpdb->options DROP $old"); + $wpdb->show_errors(); +} + +/** + * Remove old categories, link2cat, and post2cat database tables. + * + * @since 2.3.0 + */ +function upgrade_230_old_tables() { + global $wpdb; + $wpdb->query('DROP TABLE IF EXISTS ' . $wpdb->prefix . 'categories'); + $wpdb->query('DROP TABLE IF EXISTS ' . $wpdb->prefix . 'link2cat'); + $wpdb->query('DROP TABLE IF EXISTS ' . $wpdb->prefix . 'post2cat'); +} + +/** + * Upgrade old slugs made in version 2.2. + * + * @since 2.2.0 + */ +function upgrade_old_slugs() { + // Upgrade people who were using the Redirect Old Slugs plugin. + global $wpdb; + $wpdb->query("UPDATE $wpdb->postmeta SET meta_key = '_wp_old_slug' WHERE meta_key = 'old_slug'"); +} + +/** + * Execute changes made in WordPress 2.5.0. + * + * @since 2.5.0 + */ +function upgrade_250() { + global $wp_current_db_version; + + if ( $wp_current_db_version < 6689 ) { + populate_roles_250(); + } + +} + +/** + * Execute changes made in WordPress 2.5.2. + * + * @since 2.5.2 + */ +function upgrade_252() { + global $wpdb; + + $wpdb->query("UPDATE $wpdb->users SET user_activation_key = ''"); +} + +/** + * Execute changes made in WordPress 2.6. + * + * @since 2.6.0 + */ +function upgrade_260() { + global $wp_current_db_version; + + if ( $wp_current_db_version < 8000 ) + populate_roles_260(); +} + +/** + * Execute changes made in WordPress 2.7. + * + * @since 2.7.0 + */ +function upgrade_270() { + global $wpdb, $wp_current_db_version; + + if ( $wp_current_db_version < 8980 ) + populate_roles_270(); + + // Update post_date for unpublished posts with empty timestamp + if ( $wp_current_db_version < 8921 ) + $wpdb->query( "UPDATE $wpdb->posts SET post_date = post_modified WHERE post_date = '0000-00-00 00:00:00'" ); +} + +/** + * Execute changes made in WordPress 2.8. + * + * @since 2.8.0 + */ +function upgrade_280() { + global $wp_current_db_version, $wpdb; + + if ( $wp_current_db_version < 10360 ) + populate_roles_280(); + if ( is_multisite() ) { + $start = 0; + while( $rows = $wpdb->get_results( "SELECT option_name, option_value FROM $wpdb->options ORDER BY option_id LIMIT $start, 20" ) ) { + foreach( $rows as $row ) { + $value = $row->option_value; + if ( !@unserialize( $value ) ) + $value = stripslashes( $value ); + if ( $value !== $row->option_value ) { + update_option( $row->option_name, $value ); + } + } + $start += 20; + } + refresh_blog_details( $wpdb->blogid ); + } +} + +/** + * Execute changes made in WordPress 2.9. + * + * @since 2.9.0 + */ +function upgrade_290() { + global $wp_current_db_version; + + if ( $wp_current_db_version < 11958 ) { + // Previously, setting depth to 1 would redundantly disable threading, but now 2 is the minimum depth to avoid confusion + if ( get_option( 'thread_comments_depth' ) == '1' ) { + update_option( 'thread_comments_depth', 2 ); + update_option( 'thread_comments', 0 ); + } + } +} + +/** + * Execute changes made in WordPress 3.0. + * + * @since 3.0.0 + */ +function upgrade_300() { + global $wp_current_db_version, $wpdb; + + if ( $wp_current_db_version < 15093 ) + populate_roles_300(); + + if ( $wp_current_db_version < 14139 && is_multisite() && is_main_site() && ! defined( 'MULTISITE' ) && get_site_option( 'siteurl' ) === false ) + add_site_option( 'siteurl', '' ); + + // 3.0 screen options key name changes. + if ( is_main_site() && !defined('DO_NOT_UPGRADE_GLOBAL_TABLES') ) { + $sql = "DELETE FROM $wpdb->usermeta + WHERE meta_key LIKE %s + OR meta_key LIKE %s + OR meta_key LIKE %s + OR meta_key LIKE %s + OR meta_key LIKE %s + OR meta_key LIKE %s + OR meta_key = 'manageedittagscolumnshidden' + OR meta_key = 'managecategoriescolumnshidden' + OR meta_key = 'manageedit-tagscolumnshidden' + OR meta_key = 'manageeditcolumnshidden' + OR meta_key = 'categories_per_page' + OR meta_key = 'edit_tags_per_page'"; + $prefix = $wpdb->esc_like( $wpdb->base_prefix ); + $wpdb->query( $wpdb->prepare( $sql, + $prefix . '%' . $wpdb->esc_like( 'meta-box-hidden' ) . '%', + $prefix . '%' . $wpdb->esc_like( 'closedpostboxes' ) . '%', + $prefix . '%' . $wpdb->esc_like( 'manage-' ) . '%' . $wpdb->esc_like( '-columns-hidden' ) . '%', + $prefix . '%' . $wpdb->esc_like( 'meta-box-order' ) . '%', + $prefix . '%' . $wpdb->esc_like( 'metaboxorder' ) . '%', + $prefix . '%' . $wpdb->esc_like( 'screen_layout' ) . '%' + ) ); + } + +} + +/** + * Execute changes made in WordPress 3.3. + * + * @since 3.3.0 + */ +function upgrade_330() { + global $wp_current_db_version, $wpdb, $wp_registered_widgets, $sidebars_widgets; + + if ( $wp_current_db_version < 19061 && is_main_site() && ! defined( 'DO_NOT_UPGRADE_GLOBAL_TABLES' ) ) { + $wpdb->query( "DELETE FROM $wpdb->usermeta WHERE meta_key IN ('show_admin_bar_admin', 'plugins_last_view')" ); + } + + if ( $wp_current_db_version >= 11548 ) + return; + + $sidebars_widgets = get_option( 'sidebars_widgets', array() ); + $_sidebars_widgets = array(); + + if ( isset($sidebars_widgets['wp_inactive_widgets']) || empty($sidebars_widgets) ) + $sidebars_widgets['array_version'] = 3; + elseif ( !isset($sidebars_widgets['array_version']) ) + $sidebars_widgets['array_version'] = 1; + + switch ( $sidebars_widgets['array_version'] ) { + case 1 : + foreach ( (array) $sidebars_widgets as $index => $sidebar ) + if ( is_array($sidebar) ) + foreach ( (array) $sidebar as $i => $name ) { + $id = strtolower($name); + if ( isset($wp_registered_widgets[$id]) ) { + $_sidebars_widgets[$index][$i] = $id; + continue; + } + $id = sanitize_title($name); + if ( isset($wp_registered_widgets[$id]) ) { + $_sidebars_widgets[$index][$i] = $id; + continue; + } + + $found = false; + + foreach ( $wp_registered_widgets as $widget_id => $widget ) { + if ( strtolower($widget['name']) == strtolower($name) ) { + $_sidebars_widgets[$index][$i] = $widget['id']; + $found = true; + break; + } elseif ( sanitize_title($widget['name']) == sanitize_title($name) ) { + $_sidebars_widgets[$index][$i] = $widget['id']; + $found = true; + break; + } + } + + if ( $found ) + continue; + + unset($_sidebars_widgets[$index][$i]); + } + $_sidebars_widgets['array_version'] = 2; + $sidebars_widgets = $_sidebars_widgets; + unset($_sidebars_widgets); + + case 2 : + $sidebars_widgets = retrieve_widgets(); + $sidebars_widgets['array_version'] = 3; + update_option( 'sidebars_widgets', $sidebars_widgets ); + } +} + +/** + * Execute changes made in WordPress 3.4. + * + * @since 3.4.0 + */ +function upgrade_340() { + global $wp_current_db_version, $wpdb; + + if ( $wp_current_db_version < 19798 ) { + $wpdb->hide_errors(); + $wpdb->query( "ALTER TABLE $wpdb->options DROP COLUMN blog_id" ); + $wpdb->show_errors(); + } + + if ( $wp_current_db_version < 19799 ) { + $wpdb->hide_errors(); + $wpdb->query("ALTER TABLE $wpdb->comments DROP INDEX comment_approved"); + $wpdb->show_errors(); + } + + if ( $wp_current_db_version < 20022 && is_main_site() && ! defined( 'DO_NOT_UPGRADE_GLOBAL_TABLES' ) ) { + $wpdb->query( "DELETE FROM $wpdb->usermeta WHERE meta_key = 'themes_last_view'" ); + } + + if ( $wp_current_db_version < 20080 ) { + if ( 'yes' == $wpdb->get_var( "SELECT autoload FROM $wpdb->options WHERE option_name = 'uninstall_plugins'" ) ) { + $uninstall_plugins = get_option( 'uninstall_plugins' ); + delete_option( 'uninstall_plugins' ); + add_option( 'uninstall_plugins', $uninstall_plugins, null, 'no' ); + } + } +} + +/** + * Execute changes made in WordPress 3.5. + * + * @since 3.5.0 + */ +function upgrade_350() { + global $wp_current_db_version, $wpdb; + + if ( $wp_current_db_version < 22006 && $wpdb->get_var( "SELECT link_id FROM $wpdb->links LIMIT 1" ) ) + update_option( 'link_manager_enabled', 1 ); // Previously set to 0 by populate_options() + + if ( $wp_current_db_version < 21811 && is_main_site() && ! defined( 'DO_NOT_UPGRADE_GLOBAL_TABLES' ) ) { + $meta_keys = array(); + foreach ( array_merge( get_post_types(), get_taxonomies() ) as $name ) { + if ( false !== strpos( $name, '-' ) ) + $meta_keys[] = 'edit_' . str_replace( '-', '_', $name ) . '_per_page'; + } + if ( $meta_keys ) { + $meta_keys = implode( "', '", $meta_keys ); + $wpdb->query( "DELETE FROM $wpdb->usermeta WHERE meta_key IN ('$meta_keys')" ); + } + } + + if ( $wp_current_db_version < 22422 && $term = get_term_by( 'slug', 'post-format-standard', 'post_format' ) ) + wp_delete_term( $term->term_id, 'post_format' ); +} + +/** + * Execute changes made in WordPress 3.7. + * + * @since 3.7.0 + */ +function upgrade_370() { + global $wp_current_db_version; + if ( $wp_current_db_version < 25824 ) + wp_clear_scheduled_hook( 'wp_auto_updates_maybe_update' ); +} + +/** + * Execute changes made in WordPress 3.7.2. + * + * @since 3.7.2 + * @since 3.8.0 + */ +function upgrade_372() { + global $wp_current_db_version; + if ( $wp_current_db_version < 26148 ) + wp_clear_scheduled_hook( 'wp_maybe_auto_update' ); +} + +/** + * Execute changes made in WordPress 3.8.0. + * + * @since 3.8.0 + */ +function upgrade_380() { + global $wp_current_db_version; + if ( $wp_current_db_version < 26691 ) { + deactivate_plugins( array( 'mp6/mp6.php' ), true ); + } +} + +/** + * Execute changes made in WordPress 4.0.0. + * + * @since 4.0.0 + */ +function upgrade_400() { + global $wp_current_db_version; + if ( $wp_current_db_version < 29630 ) { + if ( ! is_multisite() && false === get_option( 'WPLANG' ) ) { + if ( defined( 'WPLANG' ) && ( '' !== WPLANG ) && in_array( WPLANG, get_available_languages() ) ) { + update_option( 'WPLANG', WPLANG ); + } else { + update_option( 'WPLANG', '' ); + } + } + } +} + +/** + * Execute network level changes + * + * @since 3.0.0 + */ +function upgrade_network() { + global $wp_current_db_version, $wpdb; + + // Always. + if ( is_main_network() ) { + /* + * Deletes all expired transients. The multi-table delete syntax is used + * to delete the transient record from table a, and the corresponding + * transient_timeout record from table b. + */ + $time = time(); + $sql = "DELETE a, b FROM $wpdb->sitemeta a, $wpdb->sitemeta b + WHERE a.meta_key LIKE %s + AND a.meta_key NOT LIKE %s + AND b.meta_key = CONCAT( '_site_transient_timeout_', SUBSTRING( a.meta_key, 17 ) ) + AND b.meta_value < %d"; + $wpdb->query( $wpdb->prepare( $sql, $wpdb->esc_like( '_site_transient_' ) . '%', $wpdb->esc_like ( '_site_transient_timeout_' ) . '%', $time ) ); + } + + // 2.8. + if ( $wp_current_db_version < 11549 ) { + $wpmu_sitewide_plugins = get_site_option( 'wpmu_sitewide_plugins' ); + $active_sitewide_plugins = get_site_option( 'active_sitewide_plugins' ); + if ( $wpmu_sitewide_plugins ) { + if ( !$active_sitewide_plugins ) + $sitewide_plugins = (array) $wpmu_sitewide_plugins; + else + $sitewide_plugins = array_merge( (array) $active_sitewide_plugins, (array) $wpmu_sitewide_plugins ); + + update_site_option( 'active_sitewide_plugins', $sitewide_plugins ); + } + delete_site_option( 'wpmu_sitewide_plugins' ); + delete_site_option( 'deactivated_sitewide_plugins' ); + + $start = 0; + while( $rows = $wpdb->get_results( "SELECT meta_key, meta_value FROM {$wpdb->sitemeta} ORDER BY meta_id LIMIT $start, 20" ) ) { + foreach( $rows as $row ) { + $value = $row->meta_value; + if ( !@unserialize( $value ) ) + $value = stripslashes( $value ); + if ( $value !== $row->meta_value ) { + update_site_option( $row->meta_key, $value ); + } + } + $start += 20; + } + } + + // 3.0 + if ( $wp_current_db_version < 13576 ) + update_site_option( 'global_terms_enabled', '1' ); + + // 3.3 + if ( $wp_current_db_version < 19390 ) + update_site_option( 'initial_db_version', $wp_current_db_version ); + + if ( $wp_current_db_version < 19470 ) { + if ( false === get_site_option( 'active_sitewide_plugins' ) ) + update_site_option( 'active_sitewide_plugins', array() ); + } + + // 3.4 + if ( $wp_current_db_version < 20148 ) { + // 'allowedthemes' keys things by stylesheet. 'allowed_themes' keyed things by name. + $allowedthemes = get_site_option( 'allowedthemes' ); + $allowed_themes = get_site_option( 'allowed_themes' ); + if ( false === $allowedthemes && is_array( $allowed_themes ) && $allowed_themes ) { + $converted = array(); + $themes = wp_get_themes(); + foreach ( $themes as $stylesheet => $theme_data ) { + if ( isset( $allowed_themes[ $theme_data->get('Name') ] ) ) + $converted[ $stylesheet ] = true; + } + update_site_option( 'allowedthemes', $converted ); + delete_site_option( 'allowed_themes' ); + } + } + + // 3.5 + if ( $wp_current_db_version < 21823 ) + update_site_option( 'ms_files_rewriting', '1' ); + + // 3.5.2 + if ( $wp_current_db_version < 24448 ) { + $illegal_names = get_site_option( 'illegal_names' ); + if ( is_array( $illegal_names ) && count( $illegal_names ) === 1 ) { + $illegal_name = reset( $illegal_names ); + $illegal_names = explode( ' ', $illegal_name ); + update_site_option( 'illegal_names', $illegal_names ); + } + } +} + +// The functions we use to actually do stuff + +// General + +/** + * {@internal Missing Short Description}} + * + * {@internal Missing Long Description}} + * + * @since 1.0.0 + * + * @param string $table_name Database table name to create. + * @param string $create_ddl SQL statement to create table. + * @return bool If table already exists or was created by function. + */ +function maybe_create_table($table_name, $create_ddl) { + global $wpdb; + + $query = $wpdb->prepare( "SHOW TABLES LIKE %s", $wpdb->esc_like( $table_name ) ); + + if ( $wpdb->get_var( $query ) == $table_name ) { + return true; + } + + // Didn't find it try to create it.. + $wpdb->query($create_ddl); + + // We cannot directly tell that whether this succeeded! + if ( $wpdb->get_var( $query ) == $table_name ) { + return true; + } + return false; +} + +/** + * {@internal Missing Short Description}} + * + * {@internal Missing Long Description}} + * + * @since 1.0.1 + * + * @param string $table Database table name. + * @param string $index Index name to drop. + * @return bool True, when finished. + */ +function drop_index($table, $index) { + global $wpdb; + $wpdb->hide_errors(); + $wpdb->query("ALTER TABLE `$table` DROP INDEX `$index`"); + // Now we need to take out all the extra ones we may have created + for ($i = 0; $i < 25; $i++) { + $wpdb->query("ALTER TABLE `$table` DROP INDEX `{$index}_$i`"); + } + $wpdb->show_errors(); + return true; +} + +/** + * {@internal Missing Short Description}} + * + * {@internal Missing Long Description}} + * + * @since 1.0.1 + * + * @param string $table Database table name. + * @param string $index Database table index column. + * @return bool True, when done with execution. + */ +function add_clean_index($table, $index) { + global $wpdb; + drop_index($table, $index); + $wpdb->query("ALTER TABLE `$table` ADD INDEX ( `$index` )"); + return true; +} + +/** + ** maybe_add_column() + ** Add column to db table if it doesn't exist. + ** Returns: true if already exists or on successful completion + ** false on error + */ +function maybe_add_column($table_name, $column_name, $create_ddl) { + global $wpdb; + foreach ($wpdb->get_col("DESC $table_name", 0) as $column ) { + if ($column == $column_name) { + return true; + } + } + + // Didn't find it try to create it. + $wpdb->query($create_ddl); + + // We cannot directly tell that whether this succeeded! + foreach ($wpdb->get_col("DESC $table_name", 0) as $column ) { + if ($column == $column_name) { + return true; + } + } + return false; +} + +/** + * Retrieve all options as it was for 1.2. + * + * @since 1.2.0 + * + * @return stdClass List of options. + */ +function get_alloptions_110() { + global $wpdb; + $all_options = new stdClass; + if ( $options = $wpdb->get_results( "SELECT option_name, option_value FROM $wpdb->options" ) ) { + foreach ( $options as $option ) { + if ( 'siteurl' == $option->option_name || 'home' == $option->option_name || 'category_base' == $option->option_name ) + $option->option_value = untrailingslashit( $option->option_value ); + $all_options->{$option->option_name} = stripslashes( $option->option_value ); + } + } + return $all_options; +} + +/** + * Version of get_option that is private to install/upgrade. + * + * @since 1.5.1 + * @access private + * + * @param string $setting Option name. + * @return mixed + */ +function __get_option($setting) { + global $wpdb; + + if ( $setting == 'home' && defined( 'WP_HOME' ) ) + return untrailingslashit( WP_HOME ); + + if ( $setting == 'siteurl' && defined( 'WP_SITEURL' ) ) + return untrailingslashit( WP_SITEURL ); + + $option = $wpdb->get_var( $wpdb->prepare("SELECT option_value FROM $wpdb->options WHERE option_name = %s", $setting ) ); + + if ( 'home' == $setting && '' == $option ) + return __get_option( 'siteurl' ); + + if ( 'siteurl' == $setting || 'home' == $setting || 'category_base' == $setting || 'tag_base' == $setting ) + $option = untrailingslashit( $option ); + + return maybe_unserialize( $option ); +} + +/** + * {@internal Missing Short Description}} + * + * {@internal Missing Long Description}} + * + * @since 1.5.0 + * + * @param string $content + * @return string + */ +function deslash($content) { + // Note: \\\ inside a regex denotes a single backslash. + + /* + * Replace one or more backslashes followed by a single quote with + * a single quote. + */ + $content = preg_replace("/\\\+'/", "'", $content); + + /* + * Replace one or more backslashes followed by a double quote with + * a double quote. + */ + $content = preg_replace('/\\\+"/', '"', $content); + + // Replace one or more backslashes with one backslash. + $content = preg_replace("/\\\+/", "\\", $content); + + return $content; +} + +/** + * {@internal Missing Short Description}} + * + * {@internal Missing Long Description}} + * + * @since 1.5.0 + * + * @param string $queries + * @param bool $execute + * @return array + */ +function dbDelta( $queries = '', $execute = true ) { + global $wpdb; + + if ( in_array( $queries, array( '', 'all', 'blog', 'global', 'ms_global' ), true ) ) + $queries = wp_get_db_schema( $queries ); + + // Separate individual queries into an array + if ( !is_array($queries) ) { + $queries = explode( ';', $queries ); + $queries = array_filter( $queries ); + } + + /** + * Filter the dbDelta SQL queries. + * + * @since 3.3.0 + * + * @param array $queries An array of dbDelta SQL queries. + */ + $queries = apply_filters( 'dbdelta_queries', $queries ); + + $cqueries = array(); // Creation Queries + $iqueries = array(); // Insertion Queries + $for_update = array(); + + // Create a tablename index for an array ($cqueries) of queries + foreach($queries as $qry) { + if (preg_match("|CREATE TABLE ([^ ]*)|", $qry, $matches)) { + $cqueries[ trim( $matches[1], '`' ) ] = $qry; + $for_update[$matches[1]] = 'Created table '.$matches[1]; + } else if (preg_match("|CREATE DATABASE ([^ ]*)|", $qry, $matches)) { + array_unshift($cqueries, $qry); + } else if (preg_match("|INSERT INTO ([^ ]*)|", $qry, $matches)) { + $iqueries[] = $qry; + } else if (preg_match("|UPDATE ([^ ]*)|", $qry, $matches)) { + $iqueries[] = $qry; + } else { + // Unrecognized query type + } + } + + /** + * Filter the dbDelta SQL queries for creating tables and/or databases. + * + * Queries filterable via this hook contain "CREATE TABLE" or "CREATE DATABASE". + * + * @since 3.3.0 + * + * @param array $cqueries An array of dbDelta create SQL queries. + */ + $cqueries = apply_filters( 'dbdelta_create_queries', $cqueries ); + + /** + * Filter the dbDelta SQL queries for inserting or updating. + * + * Queries filterable via this hook contain "INSERT INTO" or "UPDATE". + * + * @since 3.3.0 + * + * @param array $iqueries An array of dbDelta insert or update SQL queries. + */ + $iqueries = apply_filters( 'dbdelta_insert_queries', $iqueries ); + + $global_tables = $wpdb->tables( 'global' ); + foreach ( $cqueries as $table => $qry ) { + // Upgrade global tables only for the main site. Don't upgrade at all if DO_NOT_UPGRADE_GLOBAL_TABLES is defined. + if ( in_array( $table, $global_tables ) && ( !is_main_site() || defined( 'DO_NOT_UPGRADE_GLOBAL_TABLES' ) ) ) { + unset( $cqueries[ $table ], $for_update[ $table ] ); + continue; + } + + // Fetch the table column structure from the database + $suppress = $wpdb->suppress_errors(); + $tablefields = $wpdb->get_results("DESCRIBE {$table};"); + $wpdb->suppress_errors( $suppress ); + + if ( ! $tablefields ) + continue; + + // Clear the field and index arrays. + $cfields = $indices = array(); + + // Get all of the field names in the query from between the parentheses. + preg_match("|\((.*)\)|ms", $qry, $match2); + $qryline = trim($match2[1]); + + // Separate field lines into an array. + $flds = explode("\n", $qryline); + + // todo: Remove this? + //echo "
    \n".print_r(strtolower($table), true).":\n".print_r($cqueries, true)."

    "; + + // For every field line specified in the query. + foreach ($flds as $fld) { + + // Extract the field name. + preg_match("|^([^ ]*)|", trim($fld), $fvals); + $fieldname = trim( $fvals[1], '`' ); + + // Verify the found field name. + $validfield = true; + switch (strtolower($fieldname)) { + case '': + case 'primary': + case 'index': + case 'fulltext': + case 'unique': + case 'key': + $validfield = false; + $indices[] = trim(trim($fld), ", \n"); + break; + } + $fld = trim($fld); + + // If it's a valid field, add it to the field array. + if ($validfield) { + $cfields[strtolower($fieldname)] = trim($fld, ", \n"); + } + } + + // For every field in the table. + foreach ($tablefields as $tablefield) { + + // If the table field exists in the field array ... + if (array_key_exists(strtolower($tablefield->Field), $cfields)) { + + // Get the field type from the query. + preg_match("|".$tablefield->Field." ([^ ]*( unsigned)?)|i", $cfields[strtolower($tablefield->Field)], $matches); + $fieldtype = $matches[1]; + + // Is actual field type different from the field type in query? + if ($tablefield->Type != $fieldtype) { + // Add a query to change the column type + $cqueries[] = "ALTER TABLE {$table} CHANGE COLUMN {$tablefield->Field} " . $cfields[strtolower($tablefield->Field)]; + $for_update[$table.'.'.$tablefield->Field] = "Changed type of {$table}.{$tablefield->Field} from {$tablefield->Type} to {$fieldtype}"; + } + + // Get the default value from the array + // todo: Remove this? + //echo "{$cfields[strtolower($tablefield->Field)]}
    "; + if (preg_match("| DEFAULT '(.*?)'|i", $cfields[strtolower($tablefield->Field)], $matches)) { + $default_value = $matches[1]; + if ($tablefield->Default != $default_value) { + // Add a query to change the column's default value + $cqueries[] = "ALTER TABLE {$table} ALTER COLUMN {$tablefield->Field} SET DEFAULT '{$default_value}'"; + $for_update[$table.'.'.$tablefield->Field] = "Changed default value of {$table}.{$tablefield->Field} from {$tablefield->Default} to {$default_value}"; + } + } + + // Remove the field from the array (so it's not added). + unset($cfields[strtolower($tablefield->Field)]); + } else { + // This field exists in the table, but not in the creation queries? + } + } + + // For every remaining field specified for the table. + foreach ($cfields as $fieldname => $fielddef) { + // Push a query line into $cqueries that adds the field to that table. + $cqueries[] = "ALTER TABLE {$table} ADD COLUMN $fielddef"; + $for_update[$table.'.'.$fieldname] = 'Added column '.$table.'.'.$fieldname; + } + + // Index stuff goes here. Fetch the table index structure from the database. + $tableindices = $wpdb->get_results("SHOW INDEX FROM {$table};"); + + if ($tableindices) { + // Clear the index array. + unset($index_ary); + + // For every index in the table. + foreach ($tableindices as $tableindex) { + + // Add the index to the index data array. + $keyname = $tableindex->Key_name; + $index_ary[$keyname]['columns'][] = array('fieldname' => $tableindex->Column_name, 'subpart' => $tableindex->Sub_part); + $index_ary[$keyname]['unique'] = ($tableindex->Non_unique == 0)?true:false; + } + + // For each actual index in the index array. + foreach ($index_ary as $index_name => $index_data) { + + // Build a create string to compare to the query. + $index_string = ''; + if ($index_name == 'PRIMARY') { + $index_string .= 'PRIMARY '; + } else if($index_data['unique']) { + $index_string .= 'UNIQUE '; + } + $index_string .= 'KEY '; + if ($index_name != 'PRIMARY') { + $index_string .= $index_name; + } + $index_columns = ''; + + // For each column in the index. + foreach ($index_data['columns'] as $column_data) { + if ($index_columns != '') $index_columns .= ','; + + // Add the field to the column list string. + $index_columns .= $column_data['fieldname']; + if ($column_data['subpart'] != '') { + $index_columns .= '('.$column_data['subpart'].')'; + } + } + // Add the column list to the index create string. + $index_string .= ' ('.$index_columns.')'; + if (!(($aindex = array_search($index_string, $indices)) === false)) { + unset($indices[$aindex]); + // todo: Remove this? + //echo "
    {$table}:
    Found index:".$index_string."
    \n"; + } + // todo: Remove this? + //else echo "
    {$table}:
    Did not find index:".$index_string."
    ".print_r($indices, true)."
    \n"; + } + } + + // For every remaining index specified for the table. + foreach ( (array) $indices as $index ) { + // Push a query line into $cqueries that adds the index to that table. + $cqueries[] = "ALTER TABLE {$table} ADD $index"; + $for_update[] = 'Added index ' . $table . ' ' . $index; + } + + // Remove the original table creation query from processing. + unset( $cqueries[ $table ], $for_update[ $table ] ); + } + + $allqueries = array_merge($cqueries, $iqueries); + if ($execute) { + foreach ($allqueries as $query) { + // todo: Remove this? + //echo "
    ".print_r($query, true)."
    \n"; + $wpdb->query($query); + } + } + + return $for_update; +} + +/** + * {@internal Missing Short Description}} + * + * {@internal Missing Long Description}} + * + * @since 1.5.0 + */ +function make_db_current( $tables = 'all' ) { + $alterations = dbDelta( $tables ); + echo "
      \n"; + foreach($alterations as $alteration) echo "
    1. $alteration
    2. \n"; + echo "
    \n"; +} + +/** + * {@internal Missing Short Description}} + * + * {@internal Missing Long Description}} + * + * @since 1.5.0 + */ +function make_db_current_silent( $tables = 'all' ) { + dbDelta( $tables ); +} + +/** + * {@internal Missing Short Description}} + * + * {@internal Missing Long Description}} + * + * @since 1.5.0 + * + * @param string $theme_name + * @param string $template + * @return bool + */ +function make_site_theme_from_oldschool($theme_name, $template) { + $home_path = get_home_path(); + $site_dir = WP_CONTENT_DIR . "/themes/$template"; + + if (! file_exists("$home_path/index.php")) + return false; + + /* + * Copy files from the old locations to the site theme. + * TODO: This does not copy arbitrary include dependencies. Only the standard WP files are copied. + */ + $files = array('index.php' => 'index.php', 'wp-layout.css' => 'style.css', 'wp-comments.php' => 'comments.php', 'wp-comments-popup.php' => 'comments-popup.php'); + + foreach ($files as $oldfile => $newfile) { + if ($oldfile == 'index.php') + $oldpath = $home_path; + else + $oldpath = ABSPATH; + + // Check to make sure it's not a new index. + if ($oldfile == 'index.php') { + $index = implode('', file("$oldpath/$oldfile")); + if (strpos($index, 'WP_USE_THEMES') !== false) { + if (! @copy(WP_CONTENT_DIR . '/themes/' . WP_DEFAULT_THEME . '/index.php', "$site_dir/$newfile")) + return false; + + // Don't copy anything. + continue; + } + } + + if (! @copy("$oldpath/$oldfile", "$site_dir/$newfile")) + return false; + + chmod("$site_dir/$newfile", 0777); + + // Update the blog header include in each file. + $lines = explode("\n", implode('', file("$site_dir/$newfile"))); + if ($lines) { + $f = fopen("$site_dir/$newfile", 'w'); + + foreach ($lines as $line) { + if (preg_match('/require.*wp-blog-header/', $line)) + $line = '//' . $line; + + // Update stylesheet references. + $line = str_replace("/wp-layout.css", "", $line); + + // Update comments template inclusion. + $line = str_replace("", "", $line); + + fwrite($f, "{$line}\n"); + } + fclose($f); + } + } + + // Add a theme header. + $header = "/*\nTheme Name: $theme_name\nTheme URI: " . __get_option('siteurl') . "\nDescription: A theme automatically created by the update.\nVersion: 1.0\nAuthor: Moi\n*/\n"; + + $stylelines = file_get_contents("$site_dir/style.css"); + if ($stylelines) { + $f = fopen("$site_dir/style.css", 'w'); + + fwrite($f, $header); + fwrite($f, $stylelines); + fclose($f); + } + + return true; +} + +/** + * {@internal Missing Short Description}} + * + * {@internal Missing Long Description}} + * + * @since 1.5.0 + * + * @param string $theme_name + * @param string $template + * @return null|false + */ +function make_site_theme_from_default($theme_name, $template) { + $site_dir = WP_CONTENT_DIR . "/themes/$template"; + $default_dir = WP_CONTENT_DIR . '/themes/' . WP_DEFAULT_THEME; + + // Copy files from the default theme to the site theme. + //$files = array('index.php', 'comments.php', 'comments-popup.php', 'footer.php', 'header.php', 'sidebar.php', 'style.css'); + + $theme_dir = @ opendir($default_dir); + if ($theme_dir) { + while(($theme_file = readdir( $theme_dir )) !== false) { + if (is_dir("$default_dir/$theme_file")) + continue; + if (! @copy("$default_dir/$theme_file", "$site_dir/$theme_file")) + return; + chmod("$site_dir/$theme_file", 0777); + } + } + @closedir($theme_dir); + + // Rewrite the theme header. + $stylelines = explode("\n", implode('', file("$site_dir/style.css"))); + if ($stylelines) { + $f = fopen("$site_dir/style.css", 'w'); + + foreach ($stylelines as $line) { + if (strpos($line, 'Theme Name:') !== false) $line = 'Theme Name: ' . $theme_name; + elseif (strpos($line, 'Theme URI:') !== false) $line = 'Theme URI: ' . __get_option('url'); + elseif (strpos($line, 'Description:') !== false) $line = 'Description: Your theme.'; + elseif (strpos($line, 'Version:') !== false) $line = 'Version: 1'; + elseif (strpos($line, 'Author:') !== false) $line = 'Author: You'; + fwrite($f, $line . "\n"); + } + fclose($f); + } + + // Copy the images. + umask(0); + if (! mkdir("$site_dir/images", 0777)) { + return false; + } + + $images_dir = @ opendir("$default_dir/images"); + if ($images_dir) { + while(($image = readdir($images_dir)) !== false) { + if (is_dir("$default_dir/images/$image")) + continue; + if (! @copy("$default_dir/images/$image", "$site_dir/images/$image")) + return; + chmod("$site_dir/images/$image", 0777); + } + } + @closedir($images_dir); +} + +// Create a site theme from the default theme. +/** + * {@internal Missing Short Description}} + * + * {@internal Missing Long Description}} + * + * @since 1.5.0 + * + * @return false|string + */ +function make_site_theme() { + // Name the theme after the blog. + $theme_name = __get_option('blogname'); + $template = sanitize_title($theme_name); + $site_dir = WP_CONTENT_DIR . "/themes/$template"; + + // If the theme already exists, nothing to do. + if ( is_dir($site_dir)) { + return false; + } + + // We must be able to write to the themes dir. + if (! is_writable(WP_CONTENT_DIR . "/themes")) { + return false; + } + + umask(0); + if (! mkdir($site_dir, 0777)) { + return false; + } + + if (file_exists(ABSPATH . 'wp-layout.css')) { + if (! make_site_theme_from_oldschool($theme_name, $template)) { + // TODO: rm -rf the site theme directory. + return false; + } + } else { + if (! make_site_theme_from_default($theme_name, $template)) + // TODO: rm -rf the site theme directory. + return false; + } + + // Make the new site theme active. + $current_template = __get_option('template'); + if ($current_template == WP_DEFAULT_THEME) { + update_option('template', $template); + update_option('stylesheet', $template); + } + return $template; +} + +/** + * Translate user level to user role name. + * + * @since 2.0.0 + * + * @param int $level User level. + * @return string User role name. + */ +function translate_level_to_role($level) { + switch ($level) { + case 10: + case 9: + case 8: + return 'administrator'; + case 7: + case 6: + case 5: + return 'editor'; + case 4: + case 3: + case 2: + return 'author'; + case 1: + return 'contributor'; + case 0: + return 'subscriber'; + } +} + +/** + * {@internal Missing Short Description}} + * + * {@internal Missing Long Description}} + * + * @since 2.1.0 + */ +function wp_check_mysql_version() { + global $wpdb; + $result = $wpdb->check_database_version(); + if ( is_wp_error( $result ) ) + die( $result->get_error_message() ); +} + +/** + * Disables the Automattic widgets plugin, which was merged into core. + * + * @since 2.2.0 + */ +function maybe_disable_automattic_widgets() { + $plugins = __get_option( 'active_plugins' ); + + foreach ( (array) $plugins as $plugin ) { + if ( basename( $plugin ) == 'widgets.php' ) { + array_splice( $plugins, array_search( $plugin, $plugins ), 1 ); + update_option( 'active_plugins', $plugins ); + break; + } + } +} + +/** + * Disables the Link Manager on upgrade, if at the time of upgrade, no links exist in the DB. + * + * @since 3.5.0 + */ +function maybe_disable_link_manager() { + global $wp_current_db_version, $wpdb; + + if ( $wp_current_db_version >= 22006 && get_option( 'link_manager_enabled' ) && ! $wpdb->get_var( "SELECT link_id FROM $wpdb->links LIMIT 1" ) ) + update_option( 'link_manager_enabled', 0 ); +} + +/** + * Runs before the schema is upgraded. + * + * @since 2.9.0 + */ +function pre_schema_upgrade() { + global $wp_current_db_version, $wpdb; + + // Upgrade versions prior to 2.9 + if ( $wp_current_db_version < 11557 ) { + // Delete duplicate options. Keep the option with the highest option_id. + $wpdb->query("DELETE o1 FROM $wpdb->options AS o1 JOIN $wpdb->options AS o2 USING (`option_name`) WHERE o2.option_id > o1.option_id"); + + // Drop the old primary key and add the new. + $wpdb->query("ALTER TABLE $wpdb->options DROP PRIMARY KEY, ADD PRIMARY KEY(option_id)"); + + // Drop the old option_name index. dbDelta() doesn't do the drop. + $wpdb->query("ALTER TABLE $wpdb->options DROP INDEX option_name"); + } + + // Multisite schema upgrades. + if ( $wp_current_db_version < 25448 && is_multisite() && ! defined( 'DO_NOT_UPGRADE_GLOBAL_TABLES' ) && is_main_network() ) { + + // Upgrade verions prior to 3.7 + if ( $wp_current_db_version < 25179 ) { + // New primary key for signups. + $wpdb->query( "ALTER TABLE $wpdb->signups ADD signup_id BIGINT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST" ); + $wpdb->query( "ALTER TABLE $wpdb->signups DROP INDEX domain" ); + } + + if ( $wp_current_db_version < 25448 ) { + // Convert archived from enum to tinyint. + $wpdb->query( "ALTER TABLE $wpdb->blogs CHANGE COLUMN archived archived varchar(1) NOT NULL default '0'" ); + $wpdb->query( "ALTER TABLE $wpdb->blogs CHANGE COLUMN archived archived tinyint(2) NOT NULL default 0" ); + } + } + + if ( $wp_current_db_version < 30133 ) { + // dbDelta() can recreate but can't drop the index. + $wpdb->query( "ALTER TABLE $wpdb->terms DROP INDEX slug" ); + } +} + +/** + * Install global terms. + * + * @since 3.0.0 + * + */ +if ( !function_exists( 'install_global_terms' ) ) : +function install_global_terms() { + global $wpdb, $charset_collate; + $ms_queries = " +CREATE TABLE $wpdb->sitecategories ( + cat_ID bigint(20) NOT NULL auto_increment, + cat_name varchar(55) NOT NULL default '', + category_nicename varchar(200) NOT NULL default '', + last_updated timestamp NOT NULL, + PRIMARY KEY (cat_ID), + KEY category_nicename (category_nicename), + KEY last_updated (last_updated) +) $charset_collate; +"; +// now create tables + dbDelta( $ms_queries ); +} +endif; diff --git a/wp-admin/includes/user.php b/wp-admin/includes/user.php new file mode 100644 index 0000000..bcf1362 --- /dev/null +++ b/wp-admin/includes/user.php @@ -0,0 +1,442 @@ +ID = (int) $user_id; + $userdata = get_userdata( $user_id ); + $user->user_login = wp_slash( $userdata->user_login ); + } else { + $update = false; + } + + if ( !$update && isset( $_POST['user_login'] ) ) + $user->user_login = sanitize_user($_POST['user_login'], true); + + $pass1 = $pass2 = ''; + if ( isset( $_POST['pass1'] ) ) + $pass1 = $_POST['pass1']; + if ( isset( $_POST['pass2'] ) ) + $pass2 = $_POST['pass2']; + + if ( isset( $_POST['role'] ) && current_user_can( 'edit_users' ) ) { + $new_role = sanitize_text_field( $_POST['role'] ); + $potential_role = isset($wp_roles->role_objects[$new_role]) ? $wp_roles->role_objects[$new_role] : false; + // Don't let anyone with 'edit_users' (admins) edit their own role to something without it. + // Multisite super admins can freely edit their blog roles -- they possess all caps. + if ( ( is_multisite() && current_user_can( 'manage_sites' ) ) || $user_id != get_current_user_id() || ($potential_role && $potential_role->has_cap( 'edit_users' ) ) ) + $user->role = $new_role; + + // If the new role isn't editable by the logged-in user die with error + $editable_roles = get_editable_roles(); + if ( ! empty( $new_role ) && empty( $editable_roles[$new_role] ) ) + wp_die(__('You can’t give users that role.')); + } + + if ( isset( $_POST['email'] )) + $user->user_email = sanitize_text_field( wp_unslash( $_POST['email'] ) ); + if ( isset( $_POST['url'] ) ) { + if ( empty ( $_POST['url'] ) || $_POST['url'] == 'http://' ) { + $user->user_url = ''; + } else { + $user->user_url = esc_url_raw( $_POST['url'] ); + $protocols = implode( '|', array_map( 'preg_quote', wp_allowed_protocols() ) ); + $user->user_url = preg_match('/^(' . $protocols . '):/is', $user->user_url) ? $user->user_url : 'http://'.$user->user_url; + } + } + if ( isset( $_POST['first_name'] ) ) + $user->first_name = sanitize_text_field( $_POST['first_name'] ); + if ( isset( $_POST['last_name'] ) ) + $user->last_name = sanitize_text_field( $_POST['last_name'] ); + if ( isset( $_POST['nickname'] ) ) + $user->nickname = sanitize_text_field( $_POST['nickname'] ); + if ( isset( $_POST['display_name'] ) ) + $user->display_name = sanitize_text_field( $_POST['display_name'] ); + + if ( isset( $_POST['description'] ) ) + $user->description = trim( $_POST['description'] ); + + foreach ( wp_get_user_contact_methods( $user ) as $method => $name ) { + if ( isset( $_POST[$method] )) + $user->$method = sanitize_text_field( $_POST[$method] ); + } + + if ( $update ) { + $user->rich_editing = isset( $_POST['rich_editing'] ) && 'false' == $_POST['rich_editing'] ? 'false' : 'true'; + $user->admin_color = isset( $_POST['admin_color'] ) ? sanitize_text_field( $_POST['admin_color'] ) : 'fresh'; + $user->show_admin_bar_front = isset( $_POST['admin_bar_front'] ) ? 'true' : 'false'; + } + + $user->comment_shortcuts = isset( $_POST['comment_shortcuts'] ) && 'true' == $_POST['comment_shortcuts'] ? 'true' : ''; + + $user->use_ssl = 0; + if ( !empty($_POST['use_ssl']) ) + $user->use_ssl = 1; + + $errors = new WP_Error(); + + /* checking that username has been typed */ + if ( $user->user_login == '' ) + $errors->add( 'user_login', __( 'ERROR: Please enter a username.' ) ); + + /* checking the password has been typed twice */ + /** + * Fires before the password and confirm password fields are checked for congruity. + * + * @since 1.5.1 + * + * @param string $user_login The username. + * @param string &$pass1 The password, passed by reference. + * @param string &$pass2 The confirmed password, passed by reference. + */ + do_action_ref_array( 'check_passwords', array( $user->user_login, &$pass1, &$pass2 ) ); + + if ( $update ) { + if ( empty($pass1) && !empty($pass2) ) + $errors->add( 'pass', __( 'ERROR: You entered your new password only once.' ), array( 'form-field' => 'pass1' ) ); + elseif ( !empty($pass1) && empty($pass2) ) + $errors->add( 'pass', __( 'ERROR: You entered your new password only once.' ), array( 'form-field' => 'pass2' ) ); + } else { + if ( empty($pass1) ) + $errors->add( 'pass', __( 'ERROR: Please enter your password.' ), array( 'form-field' => 'pass1' ) ); + elseif ( empty($pass2) ) + $errors->add( 'pass', __( 'ERROR: Please enter your password twice.' ), array( 'form-field' => 'pass2' ) ); + } + + /* Check for "\" in password */ + if ( false !== strpos( wp_unslash( $pass1 ), "\\" ) ) + $errors->add( 'pass', __( 'ERROR: Passwords may not contain the character "\\".' ), array( 'form-field' => 'pass1' ) ); + + /* checking the password has been typed twice the same */ + if ( $pass1 != $pass2 ) + $errors->add( 'pass', __( 'ERROR: Please enter the same password in the two password fields.' ), array( 'form-field' => 'pass1' ) ); + + if ( !empty( $pass1 ) ) + $user->user_pass = $pass1; + + if ( !$update && isset( $_POST['user_login'] ) && !validate_username( $_POST['user_login'] ) ) + $errors->add( 'user_login', __( 'ERROR: This username is invalid because it uses illegal characters. Please enter a valid username.' )); + + if ( !$update && username_exists( $user->user_login ) ) + $errors->add( 'user_login', __( 'ERROR: This username is already registered. Please choose another one.' )); + + /* checking e-mail address */ + if ( empty( $user->user_email ) ) { + $errors->add( 'empty_email', __( 'ERROR: Please enter an e-mail address.' ), array( 'form-field' => 'email' ) ); + } elseif ( !is_email( $user->user_email ) ) { + $errors->add( 'invalid_email', __( 'ERROR: The email address isn’t correct.' ), array( 'form-field' => 'email' ) ); + } elseif ( ( $owner_id = email_exists($user->user_email) ) && ( !$update || ( $owner_id != $user->ID ) ) ) { + $errors->add( 'email_exists', __('ERROR: This email is already registered, please choose another one.'), array( 'form-field' => 'email' ) ); + } + + /** + * Fires before user profile update errors are returned. + * + * @since 2.8.0 + * + * @param array &$errors An array of user profile update errors, passed by reference. + * @param bool $update Whether this is a user update. + * @param WP_User &$user WP_User object, passed by reference. + */ + do_action_ref_array( 'user_profile_update_errors', array( &$errors, $update, &$user ) ); + + if ( $errors->get_error_codes() ) + return $errors; + + if ( $update ) { + $user_id = wp_update_user( $user ); + } else { + $user_id = wp_insert_user( $user ); + wp_new_user_notification( $user_id, isset( $_POST['send_password'] ) ? wp_unslash( $pass1 ) : '' ); + } + return $user_id; +} + +/** + * Fetch a filtered list of user roles that the current user is + * allowed to edit. + * + * Simple function who's main purpose is to allow filtering of the + * list of roles in the $wp_roles object so that plugins can remove + * inappropriate ones depending on the situation or user making edits. + * Specifically because without filtering anyone with the edit_users + * capability can edit others to be administrators, even if they are + * only editors or authors. This filter allows admins to delegate + * user management. + * + * @since 2.8.0 + * + * @return array + */ +function get_editable_roles() { + global $wp_roles; + + $all_roles = $wp_roles->roles; + + /** + * Filter the list of editable roles. + * + * @since 2.8.0 + * + * @param array $all_roles List of roles. + */ + $editable_roles = apply_filters( 'editable_roles', $all_roles ); + + return $editable_roles; +} + +/** + * Retrieve user data and filter it. + * + * @since 2.0.5 + * + * @param int $user_id User ID. + * @return WP_User|bool WP_User object on success, false on failure. + */ +function get_user_to_edit( $user_id ) { + $user = get_userdata( $user_id ); + + if ( $user ) + $user->filter = 'edit'; + + return $user; +} + +/** + * Retrieve the user's drafts. + * + * @since 2.0.0 + * + * @param int $user_id User ID. + * @return array + */ +function get_users_drafts( $user_id ) { + global $wpdb; + $query = $wpdb->prepare("SELECT ID, post_title FROM $wpdb->posts WHERE post_type = 'post' AND post_status = 'draft' AND post_author = %d ORDER BY post_modified DESC", $user_id); + + /** + * Filter the user's drafts query string. + * + * @since 2.0.0 + * + * @param string $query The user's drafts query string. + */ + $query = apply_filters( 'get_users_drafts', $query ); + return $wpdb->get_results( $query ); +} + +/** + * Remove user and optionally reassign posts and links to another user. + * + * If the $reassign parameter is not assigned to an User ID, then all posts will + * be deleted of that user. The action 'delete_user' that is passed the User ID + * being deleted will be run after the posts are either reassigned or deleted. + * The user meta will also be deleted that are for that User ID. + * + * @since 2.0.0 + * + * @param int $id User ID. + * @param int $reassign Optional. Reassign posts and links to new User ID. + * @return bool True when finished. + */ +function wp_delete_user( $id, $reassign = null ) { + global $wpdb; + + $id = (int) $id; + $user = new WP_User( $id ); + + if ( !$user->exists() ) + return false; + + // Normalize $reassign to null or a user ID. 'novalue' was an older default. + if ( 'novalue' === $reassign ) { + $reassign = null; + } elseif ( null !== $reassign ) { + $reassign = (int) $reassign; + } + + /** + * Fires immediately before a user is deleted from the database. + * + * @since 2.0.0 + * + * @param int $id ID of the user to delete. + * @param int|null $reassign ID of the user to reassign posts and links to. + * Default null, for no reassignment. + */ + do_action( 'delete_user', $id, $reassign ); + + if ( null === $reassign ) { + $post_types_to_delete = array(); + foreach ( get_post_types( array(), 'objects' ) as $post_type ) { + if ( $post_type->delete_with_user ) { + $post_types_to_delete[] = $post_type->name; + } elseif ( null === $post_type->delete_with_user && post_type_supports( $post_type->name, 'author' ) ) { + $post_types_to_delete[] = $post_type->name; + } + } + + /** + * Filter the list of post types to delete with a user. + * + * @since 3.4.0 + * + * @param array $post_types_to_delete Post types to delete. + * @param int $id User ID. + */ + $post_types_to_delete = apply_filters( 'post_types_to_delete_with_user', $post_types_to_delete, $id ); + $post_types_to_delete = implode( "', '", $post_types_to_delete ); + $post_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_author = %d AND post_type IN ('$post_types_to_delete')", $id ) ); + if ( $post_ids ) { + foreach ( $post_ids as $post_id ) + wp_delete_post( $post_id ); + } + + // Clean links + $link_ids = $wpdb->get_col( $wpdb->prepare("SELECT link_id FROM $wpdb->links WHERE link_owner = %d", $id) ); + + if ( $link_ids ) { + foreach ( $link_ids as $link_id ) + wp_delete_link($link_id); + } + } else { + $post_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_author = %d", $id ) ); + $wpdb->update( $wpdb->posts, array('post_author' => $reassign), array('post_author' => $id) ); + if ( ! empty( $post_ids ) ) { + foreach ( $post_ids as $post_id ) + clean_post_cache( $post_id ); + } + $link_ids = $wpdb->get_col( $wpdb->prepare("SELECT link_id FROM $wpdb->links WHERE link_owner = %d", $id) ); + $wpdb->update( $wpdb->links, array('link_owner' => $reassign), array('link_owner' => $id) ); + if ( ! empty( $link_ids ) ) { + foreach ( $link_ids as $link_id ) + clean_bookmark_cache( $link_id ); + } + } + + // FINALLY, delete user + if ( is_multisite() ) { + remove_user_from_blog( $id, get_current_blog_id() ); + } else { + $meta = $wpdb->get_col( $wpdb->prepare( "SELECT umeta_id FROM $wpdb->usermeta WHERE user_id = %d", $id ) ); + foreach ( $meta as $mid ) + delete_metadata_by_mid( 'user', $mid ); + + $wpdb->delete( $wpdb->users, array( 'ID' => $id ) ); + } + + clean_user_cache( $user ); + + /** + * Fires immediately after a user is deleted from the database. + * + * @since 2.9.0 + * + * @param int $id ID of the deleted user. + * @param int|null $reassign ID of the user to reassign posts and links to. + * Default null, for no reassignment. + */ + do_action( 'deleted_user', $id, $reassign ); + + return true; +} + +/** + * Remove all capabilities from user. + * + * @since 2.1.0 + * + * @param int $id User ID. + */ +function wp_revoke_user($id) { + $id = (int) $id; + + $user = new WP_User($id); + $user->remove_all_caps(); +} + +add_action('admin_init', 'default_password_nag_handler'); +/** + * @since 2.8.0 + */ +function default_password_nag_handler($errors = false) { + global $user_ID; + // Short-circuit it. + if ( ! get_user_option('default_password_nag') ) + return; + + // get_user_setting = JS saved UI setting. else no-js-fallback code. + if ( 'hide' == get_user_setting('default_password_nag') || isset($_GET['default_password_nag']) && '0' == $_GET['default_password_nag'] ) { + delete_user_setting('default_password_nag'); + update_user_option($user_ID, 'default_password_nag', false, true); + } +} + +add_action('profile_update', 'default_password_nag_edit_user', 10, 2); + +/** + * @since 2.8.0 + */ +function default_password_nag_edit_user($user_ID, $old_data) { + // Short-circuit it. + if ( ! get_user_option('default_password_nag', $user_ID) ) + return; + + $new_data = get_userdata($user_ID); + + // Remove the nag if the password has been changed. + if ( $new_data->user_pass != $old_data->user_pass ) { + delete_user_setting('default_password_nag'); + update_user_option($user_ID, 'default_password_nag', false, true); + } +} + +add_action('admin_notices', 'default_password_nag'); + +/** + * @since 2.8.0 + */ +function default_password_nag() { + global $pagenow; + // Short-circuit it. + if ( 'profile.php' == $pagenow || ! get_user_option('default_password_nag') ) + return; + + echo '
    '; + echo '

    '; + echo '' . __('Notice:') . ' '; + _e('You’re using the auto-generated password for your account. Would you like to change it to something easier to remember?'); + echo '

    '; + printf( '' . __('Yes, take me to my profile page') . ' | ', get_edit_profile_url() . '#password' ); + printf( '' . __('No thanks, do not remind me again') . '', '?default_password_nag=0' ); + echo '

    '; +} diff --git a/wp-admin/includes/widgets.php b/wp-admin/includes/widgets.php new file mode 100644 index 0000000..c869449 --- /dev/null +++ b/wp-admin/includes/widgets.php @@ -0,0 +1,245 @@ + $widget['id'], 'widget_name' => $widget['name'], '_display' => 'template' ); + + if ( isset($wp_registered_widget_controls[$widget['id']]['id_base']) && isset($widget['params'][0]['number']) ) { + $id_base = $wp_registered_widget_controls[$widget['id']]['id_base']; + $args['_temp_id'] = "$id_base-__i__"; + $args['_multi_num'] = next_widget_id_number($id_base); + $args['_add'] = 'multi'; + } else { + $args['_add'] = 'single'; + if ( $sidebar ) + $args['_hide'] = '1'; + } + + $args = wp_list_widget_controls_dynamic_sidebar( array( 0 => $args, 1 => $widget['params'][0] ) ); + call_user_func_array( 'wp_widget_control', $args ); + } +} + +/** + * Callback to sort array by a 'name' key. + * + * @since 3.1.0 + * @access private + */ +function _sort_name_callback( $a, $b ) { + return strnatcasecmp( $a['name'], $b['name'] ); +} + +/** + * Show the widgets and their settings for a sidebar. + * Used in the admin widget config screen. + * + * @since 2.5.0 + * + * @param string $sidebar id slug of the sidebar + * @param string optional $sidebar_name Include the HTML for the sidebar name + */ +function wp_list_widget_controls( $sidebar, $sidebar_name = '' ) { + add_filter( 'dynamic_sidebar_params', 'wp_list_widget_controls_dynamic_sidebar' ); + + $description = wp_sidebar_description( $sidebar ); + + echo '
    '; + + if ( $sidebar_name ) { + ?> + + '; + + if ( ! empty( $description ) ) { + echo '

    ' . $description . '

    '; + } + + echo '
    '; + + dynamic_sidebar( $sidebar ); + + echo ''; +} + +/** + * {@internal Missing Short Description}} + * + * @since 2.5.0 + * + * @param array $params + * @return array + */ +function wp_list_widget_controls_dynamic_sidebar( $params ) { + global $wp_registered_widgets; + static $i = 0; + $i++; + + $widget_id = $params[0]['widget_id']; + $id = isset($params[0]['_temp_id']) ? $params[0]['_temp_id'] : $widget_id; + $hidden = isset($params[0]['_hide']) ? ' style="display:none;"' : ''; + + $params[0]['before_widget'] = "
    "; + $params[0]['after_widget'] = "
    "; + $params[0]['before_title'] = "%BEG_OF_TITLE%"; // deprecated + $params[0]['after_title'] = "%END_OF_TITLE%"; // deprecated + if ( is_callable( $wp_registered_widgets[$widget_id]['callback'] ) ) { + $wp_registered_widgets[$widget_id]['_callback'] = $wp_registered_widgets[$widget_id]['callback']; + $wp_registered_widgets[$widget_id]['callback'] = 'wp_widget_control'; + } + + return $params; +} + +function next_widget_id_number($id_base) { + global $wp_registered_widgets; + $number = 1; + + foreach ( $wp_registered_widgets as $widget_id => $widget ) { + if ( preg_match( '/' . $id_base . '-([0-9]+)$/', $widget_id, $matches ) ) + $number = max($number, $matches[1]); + } + $number++; + + return $number; +} + +/** + * Meta widget used to display the control form for a widget. + * + * Called from dynamic_sidebar(). + * + * @since 2.5.0 + * + * @param array $sidebar_args + * @return array + */ +function wp_widget_control( $sidebar_args ) { + global $wp_registered_widgets, $wp_registered_widget_controls, $sidebars_widgets; + + $widget_id = $sidebar_args['widget_id']; + $sidebar_id = isset($sidebar_args['id']) ? $sidebar_args['id'] : false; + $key = $sidebar_id ? array_search( $widget_id, $sidebars_widgets[$sidebar_id] ) : '-1'; // position of widget in sidebar + $control = isset($wp_registered_widget_controls[$widget_id]) ? $wp_registered_widget_controls[$widget_id] : array(); + $widget = $wp_registered_widgets[$widget_id]; + + $id_format = $widget['id']; + $widget_number = isset($control['params'][0]['number']) ? $control['params'][0]['number'] : ''; + $id_base = isset($control['id_base']) ? $control['id_base'] : $widget_id; + $multi_number = isset($sidebar_args['_multi_num']) ? $sidebar_args['_multi_num'] : ''; + $add_new = isset($sidebar_args['_add']) ? $sidebar_args['_add'] : ''; + + $query_arg = array( 'editwidget' => $widget['id'] ); + if ( $add_new ) { + $query_arg['addnew'] = 1; + if ( $multi_number ) { + $query_arg['num'] = $multi_number; + $query_arg['base'] = $id_base; + } + } else { + $query_arg['sidebar'] = $sidebar_id; + $query_arg['key'] = $key; + } + + /* + * We aren't showing a widget control, we're outputting a template + * for a multi-widget control. + */ + if ( isset($sidebar_args['_display']) && 'template' == $sidebar_args['_display'] && $widget_number ) { + // number == -1 implies a template where id numbers are replaced by a generic '__i__' + $control['params'][0]['number'] = -1; + // With id_base widget id's are constructed like {$id_base}-{$id_number}. + if ( isset($control['id_base']) ) + $id_format = $control['id_base'] . '-__i__'; + } + + $wp_registered_widgets[$widget_id]['callback'] = $wp_registered_widgets[$widget_id]['_callback']; + unset($wp_registered_widgets[$widget_id]['_callback']); + + $widget_title = esc_html( strip_tags( $sidebar_args['widget_name'] ) ); + $has_form = 'noform'; + + echo $sidebar_args['before_widget']; ?> +
    +
    + + + + + + +
    +

    +
    + +
    +
    +
    +" . __('There are no options for this widget.') . "

    \n"; ?> +
    + + + + + + + + +
    +
    + | + +
    +
    + 'widget-' . esc_attr( $id_format ) . '-savewidget' ) ); ?> + +
    +
    +
    +
    +
    + +
    + +
    +' . __( 'Welcome to your WordPress Dashboard! This is the screen you will see when you log in to your site, and gives you access to all the site management features of WordPress. You can get help for any screen by clicking the Help tab in the upper corner.' ) . '

    '; + +// Not using chaining here, so as to be parseable by PHP4. +$screen = get_current_screen(); + +$screen->add_help_tab( array( + 'id' => 'overview', + 'title' => __( 'Overview' ), + 'content' => $help, +) ); + +// Help tabs + +$help = '

    ' . __( 'The left-hand navigation menu provides links to all of the WordPress administration screens, with submenu items displayed on hover. You can minimize this menu to a narrow icon strip by clicking on the Collapse Menu arrow at the bottom.' ) . '

    '; +$help .= '

    ' . __( 'Links in the Toolbar at the top of the screen connect your dashboard and the front end of your site, and provide access to your profile and helpful WordPress information.' ) . '

    '; + +$screen->add_help_tab( array( + 'id' => 'help-navigation', + 'title' => __( 'Navigation' ), + 'content' => $help, +) ); + +$help = '

    ' . __( 'You can use the following controls to arrange your Dashboard screen to suit your workflow. This is true on most other administration screens as well.' ) . '

    '; +$help .= '

    ' . __( 'Screen Options - Use the Screen Options tab to choose which Dashboard boxes to show.' ) . '

    '; +$help .= '

    ' . __( 'Drag and Drop - To rearrange the boxes, drag and drop by clicking on the title bar of the selected box and releasing when you see a gray dotted-line rectangle appear in the location you want to place the box.' ) . '

    '; +$help .= '

    ' . __( 'Box Controls - Click the title bar of the box to expand or collapse it. Some boxes added by plugins may have configurable content, and will show a “Configure” link in the title bar if you hover over it.' ) . '

    '; + +$screen->add_help_tab( array( + 'id' => 'help-layout', + 'title' => __( 'Layout' ), + 'content' => $help, +) ); + +$help = '

    ' . __( 'The boxes on your Dashboard screen are:' ) . '

    '; +if ( current_user_can( 'edit_posts' ) ) + $help .= '

    ' . __( 'At A Glance - Displays a summary of the content on your site and identifies which theme and version of WordPress you are using.' ) . '

    '; + $help .= '

    ' . __( 'Activity - Shows the upcoming scheduled posts, recently published posts, and the most recent comments on your posts and allows you to moderate them.' ) . '

    '; +if ( is_blog_admin() && current_user_can( 'edit_posts' ) ) + $help .= '

    ' . __( "Quick Draft - Allows you to create a new post and save it as a draft. Also displays links to the 5 most recent draft posts you've started." ) . '

    '; +if ( ! is_multisite() && current_user_can( 'install_plugins' ) ) + $help .= '

    ' . __( 'WordPress News - Latest news from the official WordPress project, the WordPress Planet, and popular and recent plugins.' ) . '

    '; +else + $help .= '

    ' . __( 'WordPress News - Latest news from the official WordPress project, the WordPress Planet.' ) . '

    '; +if ( current_user_can( 'edit_theme_options' ) ) + $help .= '

    ' . __( 'Welcome - Shows links for some of the most common tasks when setting up a new site.' ) . '

    '; + +$screen->add_help_tab( array( + 'id' => 'help-content', + 'title' => __( 'Content' ), + 'content' => $help, +) ); + +unset( $help ); + +$screen->set_help_sidebar( + '

    ' . __( 'For more information:' ) . '

    ' . + '

    ' . __( 'Documentation on Dashboard' ) . '

    ' . + '

    ' . __( 'Support Forums' ) . '

    ' +); + +include( ABSPATH . 'wp-admin/admin-header.php' ); +?> + +
    +

    + +user_email != get_option( 'admin_email' ) ); + if ( $hide ) + $classes .= ' hidden'; ?> + +
    + + + +
    + + +
    + +
    + +
    + +comments, 'comment_author', 'tinytext' ) ) { + * echo "ok\n"; + * } + * + * $error_count = 0; + * $tablename = $wpdb->links; + * // Check the column. + * if ( ! check_column($wpdb->links, 'link_description', 'varchar( 255 )' ) ) { + * $ddl = "ALTER TABLE $wpdb->links MODIFY COLUMN link_description varchar(255) NOT NULL DEFAULT '' "; + * $q = $wpdb->query( $ddl ); + * } + * + * if ( check_column( $wpdb->links, 'link_description', 'varchar( 255 )' ) ) { + * $res .= $tablename . ' - ok
    '; + * } else { + * $res .= 'There was a problem with ' . $tablename . '
    '; + * ++$error_count; + * } + * + * @package WordPress + * @subpackage Plugin + */ + +/** Load WordPress Bootstrap */ +require_once(dirname(dirname(__FILE__)).'/wp-load.php'); + +if ( ! function_exists('maybe_create_table') ) : +/** + * Create database table, if it doesn't already exist. + * + * @since 1.0.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @param string $table_name Database table name. + * @param string $create_ddl Create database table SQL. + * @return bool False on error, true if already exists or success. + */ +function maybe_create_table($table_name, $create_ddl) { + global $wpdb; + foreach ($wpdb->get_col("SHOW TABLES",0) as $table ) { + if ($table == $table_name) { + return true; + } + } + // Didn't find it, so try to create it. + $wpdb->query($create_ddl); + + // We cannot directly tell that whether this succeeded! + foreach ($wpdb->get_col("SHOW TABLES",0) as $table ) { + if ($table == $table_name) { + return true; + } + } + return false; +} +endif; + +if ( ! function_exists('maybe_add_column') ) : +/** + * Add column to database table, if column doesn't already exist in table. + * + * @since 1.0.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @param string $table_name Database table name + * @param string $column_name Table column name + * @param string $create_ddl SQL to add column to table. + * @return bool False on failure. True, if already exists or was successful. + */ +function maybe_add_column($table_name, $column_name, $create_ddl) { + global $wpdb; + foreach ($wpdb->get_col("DESC $table_name",0) as $column ) { + + if ($column == $column_name) { + return true; + } + } + + // Didn't find it, so try to create it. + $wpdb->query($create_ddl); + + // We cannot directly tell that whether this succeeded! + foreach ($wpdb->get_col("DESC $table_name",0) as $column ) { + if ($column == $column_name) { + return true; + } + } + return false; +} +endif; + +/** + * Drop column from database table, if it exists. + * + * @since 1.0.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @param string $table_name Table name + * @param string $column_name Column name + * @param string $drop_ddl SQL statement to drop column. + * @return bool False on failure, true on success or doesn't exist. + */ +function maybe_drop_column($table_name, $column_name, $drop_ddl) { + global $wpdb; + foreach ($wpdb->get_col("DESC $table_name",0) as $column ) { + if ($column == $column_name) { + + // Found it, so try to drop it. + $wpdb->query($drop_ddl); + + // We cannot directly tell that whether this succeeded! + foreach ($wpdb->get_col("DESC $table_name",0) as $column ) { + if ($column == $column_name) { + return false; + } + } + } + } + // Else didn't find it. + return true; +} + +/** + * Check column matches criteria. + * + * Uses the SQL DESC for retrieving the table info for the column. It will help + * understand the parameters, if you do more research on what column information + * is returned by the SQL statement. Pass in null to skip checking that + * criteria. + * + * Column names returned from DESC table are case sensitive and are listed: + * Field + * Type + * Null + * Key + * Default + * Extra + * + * @since 1.0.0 + * + * @param string $table_name Table name + * @param string $col_name Column name + * @param string $col_type Column type + * @param bool $is_null Optional. Check is null. + * @param mixed $key Optional. Key info. + * @param mixed $default Optional. Default value. + * @param mixed $extra Optional. Extra value. + * @return bool True, if matches. False, if not matching. + */ +function check_column($table_name, $col_name, $col_type, $is_null = null, $key = null, $default = null, $extra = null) { + global $wpdb; + $diffs = 0; + $results = $wpdb->get_results("DESC $table_name"); + + foreach ($results as $row ) { + + if ($row->Field == $col_name) { + + // Got our column, check the params. + if (($col_type != null) && ($row->Type != $col_type)) { + ++$diffs; + } + if (($is_null != null) && ($row->Null != $is_null)) { + ++$diffs; + } + if (($key != null) && ($row->Key != $key)) { + ++$diffs; + } + if (($default != null) && ($row->Default != $default)) { + ++$diffs; + } + if (($extra != null) && ($row->Extra != $extra)) { + ++$diffs; + } + if ($diffs > 0) { + return false; + } + return true; + } // end if found our column + } + return false; +} diff --git a/wp-admin/install.php b/wp-admin/install.php new file mode 100644 index 0000000..93c9d72 --- /dev/null +++ b/wp-admin/install.php @@ -0,0 +1,305 @@ + + + + + + Error: PHP is not running + + +

    WordPress

    +

    Error: PHP is not running

    +

    WordPress requires that your web server is running PHP. Your server does not have PHP installed, or PHP is turned off.

    + + + + +> + + + + <?php _e( 'WordPress › Installation' ); ?> + + + +

    + +prepare( "SHOW TABLES LIKE %s", $wpdb->esc_like( $wpdb->users ) ); + $user_table = ( $wpdb->get_var( $sql ) != null ); + + // Ensure that Blogs appear in search engines by default. + $blog_public = 1; + if ( isset( $_POST['weblog_title'] ) ) { + $blog_public = isset( $_POST['blog_public'] ); + } + + $weblog_title = isset( $_POST['weblog_title'] ) ? trim( wp_unslash( $_POST['weblog_title'] ) ) : ''; + $user_name = isset($_POST['user_name']) ? trim( wp_unslash( $_POST['user_name'] ) ) : ''; + $admin_email = isset( $_POST['admin_email'] ) ? trim( wp_unslash( $_POST['admin_email'] ) ) : ''; + + if ( ! is_null( $error ) ) { +?> +

    + +
    + + + + + + + + + + + + + + + + + + + + + + + +
    + '; + } else { + ?> +

    + +
    + +

    +
    + +

    +
    +

    +
    +

    +

    + +
    +' . __( 'Already Installed' ) . '

    ' . __( 'You appear to have already installed WordPress. To reinstall please clear your old database tables first.' ) . '

    ' . __( 'Log In' ) . '

    ' ); +} + +$php_version = phpversion(); +$mysql_version = $wpdb->db_version(); +$php_compat = version_compare( $php_version, $required_php_version, '>=' ); +$mysql_compat = version_compare( $mysql_version, $required_mysql_version, '>=' ) || file_exists( WP_CONTENT_DIR . '/db.php' ); + +if ( !$mysql_compat && !$php_compat ) + $compat = sprintf( __( 'You cannot install because WordPress %1$s requires PHP version %2$s or higher and MySQL version %3$s or higher. You are running PHP version %4$s and MySQL version %5$s.' ), $wp_version, $required_php_version, $required_mysql_version, $php_version, $mysql_version ); +elseif ( !$php_compat ) + $compat = sprintf( __( 'You cannot install because WordPress %1$s requires PHP version %2$s or higher. You are running version %3$s.' ), $wp_version, $required_php_version, $php_version ); +elseif ( !$mysql_compat ) + $compat = sprintf( __( 'You cannot install because WordPress %1$s requires MySQL version %2$s or higher. You are running version %3$s.' ), $wp_version, $required_mysql_version, $mysql_version ); + +if ( !$mysql_compat || !$php_compat ) { + display_header(); + die( '

    ' . __( 'Insufficient Requirements' ) . '

    ' . $compat . '

    ' ); +} + +if ( ! is_string( $wpdb->base_prefix ) || '' === $wpdb->base_prefix ) { + display_header(); + die( '

    ' . __( 'Configuration Error' ) . '

    ' . __( 'Your wp-config.php file has an empty database table prefix, which is not supported.' ) . '

    ' ); +} + +$language = ''; +if ( ! empty( $_REQUEST['language'] ) ) { + $language = preg_replace( '/[^a-zA-Z_]/', '', $_REQUEST['language'] ); +} elseif ( isset( $GLOBALS['wp_local_package'] ) ) { + $language = $GLOBALS['wp_local_package']; +} + +switch($step) { + case 0: // Step 0 + + if ( wp_can_install_language_pack() && empty( $language ) && ( $languages = wp_get_available_translations() ) ) { + display_header( 'language-chooser' ); + echo '
    '; + wp_install_language_form( $languages ); + echo '
    '; + break; + } + + // Deliberately fall through if we can't reach the translations API. + + case 1: // Step 1, direct link or from language chooser. + if ( ! empty( $language ) ) { + $loaded_language = wp_download_language_pack( $language ); + if ( $loaded_language ) { + load_default_textdomain( $loaded_language ); + $GLOBALS['wp_locale'] = new WP_Locale(); + } + } + + display_header(); +?> +

    +

    + +

    +

    + +error ) ) + wp_die( $wpdb->error->get_error_message() ); + + display_header(); + // Fill in the data we gathered + $weblog_title = isset( $_POST['weblog_title'] ) ? trim( wp_unslash( $_POST['weblog_title'] ) ) : ''; + $user_name = isset($_POST['user_name']) ? trim( wp_unslash( $_POST['user_name'] ) ) : ''; + $admin_password = isset($_POST['admin_password']) ? wp_unslash( $_POST['admin_password'] ) : ''; + $admin_password_check = isset($_POST['admin_password2']) ? wp_unslash( $_POST['admin_password2'] ) : ''; + $admin_email = isset( $_POST['admin_email'] ) ?trim( wp_unslash( $_POST['admin_email'] ) ) : ''; + $public = isset( $_POST['blog_public'] ) ? (int) $_POST['blog_public'] : 0; + + // Check e-mail address. + $error = false; + if ( empty( $user_name ) ) { + // TODO: poka-yoke + display_setup_form( __( 'Please provide a valid username.' ) ); + $error = true; + } elseif ( $user_name != sanitize_user( $user_name, true ) ) { + display_setup_form( __( 'The username you provided has invalid characters.' ) ); + $error = true; + } elseif ( $admin_password != $admin_password_check ) { + // TODO: poka-yoke + display_setup_form( __( 'Your passwords do not match. Please try again.' ) ); + $error = true; + } else if ( empty( $admin_email ) ) { + // TODO: poka-yoke + display_setup_form( __( 'You must provide an email address.' ) ); + $error = true; + } elseif ( ! is_email( $admin_email ) ) { + // TODO: poka-yoke + display_setup_form( __( 'Sorry, that isn’t a valid email address. Email addresses look like username@example.com.' ) ); + $error = true; + } + + if ( $error === false ) { + $wpdb->show_errors(); + $result = wp_install( $weblog_title, $user_name, $admin_email, $public, '', wp_slash( $admin_password ), $loaded_language ); +?> + +

    + +

    + + + + + + + + + + +
    +
    + +

    +
    + +

    + + + + + + + + diff --git a/wp-admin/js/accordion.js b/wp-admin/js/accordion.js new file mode 100644 index 0000000..1769d27 --- /dev/null +++ b/wp-admin/js/accordion.js @@ -0,0 +1,75 @@ +/** + * Accordion-folding functionality. + * + * Markup with the appropriate classes will be automatically hidden, + * with one section opening at a time when its title is clicked. + * Use the following markup structure for accordion behavior: + * + *
    + *
    + *

    + *
    + *
    + *
    + *
    + *

    + *
    + *
    + *
    + *
    + *

    + *
    + *
    + *
    + *
    + * + * Note that any appropriate tags may be used, as long as the above classes are present. + * + * @since 3.6.0. + */ + +( function( $ ){ + + $( document ).ready( function () { + + // Expand/Collapse accordion sections on click. + $( '.accordion-container' ).on( 'click keydown', '.accordion-section-title', function( e ) { + if ( e.type === 'keydown' && 13 !== e.which ) { // "return" key + return; + } + + e.preventDefault(); // Keep this AFTER the key filter above + + accordionSwitch( $( this ) ); + }); + + }); + + /** + * Close the current accordion section and open a new one. + * + * @param {Object} el Title element of the accordion section to toggle. + * @since 3.6.0 + */ + function accordionSwitch ( el ) { + var section = el.closest( '.accordion-section' ), + siblings = section.closest( '.accordion-container' ).find( '.open' ), + content = section.find( '.accordion-section-content' ); + + // This section has no content and cannot be expanded. + if ( section.hasClass( 'cannot-expand' ) ) { + return; + } + + if ( section.hasClass( 'open' ) ) { + section.toggleClass( 'open' ); + content.toggle( true ).slideToggle( 150 ); + } else { + siblings.removeClass( 'open' ); + siblings.find( '.accordion-section-content' ).show().slideUp( 150 ); + content.toggle( false ).slideToggle( 150 ); + section.toggleClass( 'open' ); + } + } + +})(jQuery); diff --git a/wp-admin/js/accordion.min.js b/wp-admin/js/accordion.min.js new file mode 100644 index 0000000..bcdf3aa --- /dev/null +++ b/wp-admin/js/accordion.min.js @@ -0,0 +1 @@ +!function(a){function b(a){var b=a.closest(".accordion-section"),c=b.closest(".accordion-container").find(".open"),d=b.find(".accordion-section-content");b.hasClass("cannot-expand")||(b.hasClass("open")?(b.toggleClass("open"),d.toggle(!0).slideToggle(150)):(c.removeClass("open"),c.find(".accordion-section-content").show().slideUp(150),d.toggle(!1).slideToggle(150),b.toggleClass("open")))}a(document).ready(function(){a(".accordion-container").on("click keydown",".accordion-section-title",function(c){("keydown"!==c.type||13===c.which)&&(c.preventDefault(),b(a(this)))})})}(jQuery); \ No newline at end of file diff --git a/wp-admin/js/color-picker.js b/wp-admin/js/color-picker.js new file mode 100644 index 0000000..03cc178 --- /dev/null +++ b/wp-admin/js/color-picker.js @@ -0,0 +1,157 @@ +/* global wpColorPickerL10n */ +( function( $, undef ){ + + var ColorPicker, + // html stuff + _before = '', + _after = '
    ', + _wrap = '
    ', + _button = ''; + + // jQuery UI Widget constructor + ColorPicker = { + options: { + defaultColor: false, + change: false, + clear: false, + hide: true, + palettes: true, + width: 255, + mode: 'hsv' + }, + _create: function() { + // bail early for unsupported Iris. + if ( ! $.support.iris ) { + return; + } + + var self = this, + el = self.element; + + $.extend( self.options, el.data() ); + + // keep close bound so it can be attached to a body listener + self.close = $.proxy( self.close, self ); + + self.initialValue = el.val(); + + // Set up HTML structure, hide things + el.addClass( 'wp-color-picker' ).hide().wrap( _wrap ); + self.wrap = el.parent(); + self.toggler = $( _before ).insertBefore( el ).css( { backgroundColor: self.initialValue } ).attr( 'title', wpColorPickerL10n.pick ).attr( 'data-current', wpColorPickerL10n.current ); + self.pickerContainer = $( _after ).insertAfter( el ); + self.button = $( _button ); + + if ( self.options.defaultColor ) { + self.button.addClass( 'wp-picker-default' ).val( wpColorPickerL10n.defaultString ); + } else { + self.button.addClass( 'wp-picker-clear' ).val( wpColorPickerL10n.clear ); + } + + el.wrap( '' ).after(self.button); + + el.iris( { + target: self.pickerContainer, + hide: self.options.hide, + width: self.options.width, + mode: self.options.mode, + palettes: self.options.palettes, + change: function( event, ui ) { + self.toggler.css( { backgroundColor: ui.color.toString() } ); + // check for a custom cb + if ( $.isFunction( self.options.change ) ) { + self.options.change.call( this, event, ui ); + } + } + } ); + + el.val( self.initialValue ); + self._addListeners(); + if ( ! self.options.hide ) { + self.toggler.click(); + } + }, + _addListeners: function() { + var self = this; + + // prevent any clicks inside this widget from leaking to the top and closing it + self.wrap.on( 'click.wpcolorpicker', function( event ) { + event.stopPropagation(); + }); + + self.toggler.click( function(){ + if ( self.toggler.hasClass( 'wp-picker-open' ) ) { + self.close(); + } else { + self.open(); + } + }); + + self.element.change( function( event ) { + var me = $( this ), + val = me.val(); + // Empty = clear + if ( val === '' || val === '#' ) { + self.toggler.css( 'backgroundColor', '' ); + // fire clear callback if we have one + if ( $.isFunction( self.options.clear ) ) { + self.options.clear.call( this, event ); + } + } + }); + + // open a keyboard-focused closed picker with space or enter + self.toggler.on( 'keyup', function( event ) { + if ( event.keyCode === 13 || event.keyCode === 32 ) { + event.preventDefault(); + self.toggler.trigger( 'click' ).next().focus(); + } + }); + + self.button.click( function( event ) { + var me = $( this ); + if ( me.hasClass( 'wp-picker-clear' ) ) { + self.element.val( '' ); + self.toggler.css( 'backgroundColor', '' ); + if ( $.isFunction( self.options.clear ) ) { + self.options.clear.call( this, event ); + } + } else if ( me.hasClass( 'wp-picker-default' ) ) { + self.element.val( self.options.defaultColor ).change(); + } + }); + }, + open: function() { + this.element.show().iris( 'toggle' ).focus(); + this.button.removeClass( 'hidden' ); + this.toggler.addClass( 'wp-picker-open' ); + $( 'body' ).trigger( 'click.wpcolorpicker' ).on( 'click.wpcolorpicker', this.close ); + }, + close: function() { + this.element.hide().iris( 'toggle' ); + this.button.addClass( 'hidden' ); + this.toggler.removeClass( 'wp-picker-open' ); + $( 'body' ).off( 'click.wpcolorpicker', this.close ); + }, + // $("#input").wpColorPicker('color') returns the current color + // $("#input").wpColorPicker('color', '#bada55') to set + color: function( newColor ) { + if ( newColor === undef ) { + return this.element.iris( 'option', 'color' ); + } + + this.element.iris( 'option', 'color', newColor ); + }, + //$("#input").wpColorPicker('defaultColor') returns the current default color + //$("#input").wpColorPicker('defaultColor', newDefaultColor) to set + defaultColor: function( newDefaultColor ) { + if ( newDefaultColor === undef ) { + return this.options.defaultColor; + } + + this.options.defaultColor = newDefaultColor; + } + }; + + $.widget( 'wp.wpColorPicker', ColorPicker ); +}( jQuery ) ); diff --git a/wp-admin/js/color-picker.min.js b/wp-admin/js/color-picker.min.js new file mode 100644 index 0000000..24d5d1a --- /dev/null +++ b/wp-admin/js/color-picker.min.js @@ -0,0 +1 @@ +!function(a,b){var c,d='',e='
    ',f='
    ',g='';c={options:{defaultColor:!1,change:!1,clear:!1,hide:!0,palettes:!0,width:255,mode:"hsv"},_create:function(){if(a.support.iris){var b=this,c=b.element;a.extend(b.options,c.data()),b.close=a.proxy(b.close,b),b.initialValue=c.val(),c.addClass("wp-color-picker").hide().wrap(f),b.wrap=c.parent(),b.toggler=a(d).insertBefore(c).css({backgroundColor:b.initialValue}).attr("title",wpColorPickerL10n.pick).attr("data-current",wpColorPickerL10n.current),b.pickerContainer=a(e).insertAfter(c),b.button=a(g),b.options.defaultColor?b.button.addClass("wp-picker-default").val(wpColorPickerL10n.defaultString):b.button.addClass("wp-picker-clear").val(wpColorPickerL10n.clear),c.wrap('').after(b.button),c.iris({target:b.pickerContainer,hide:b.options.hide,width:b.options.width,mode:b.options.mode,palettes:b.options.palettes,change:function(c,d){b.toggler.css({backgroundColor:d.color.toString()}),a.isFunction(b.options.change)&&b.options.change.call(this,c,d)}}),c.val(b.initialValue),b._addListeners(),b.options.hide||b.toggler.click()}},_addListeners:function(){var b=this;b.wrap.on("click.wpcolorpicker",function(a){a.stopPropagation()}),b.toggler.click(function(){b.toggler.hasClass("wp-picker-open")?b.close():b.open()}),b.element.change(function(c){var d=a(this),e=d.val();(""===e||"#"===e)&&(b.toggler.css("backgroundColor",""),a.isFunction(b.options.clear)&&b.options.clear.call(this,c))}),b.toggler.on("keyup",function(a){(13===a.keyCode||32===a.keyCode)&&(a.preventDefault(),b.toggler.trigger("click").next().focus())}),b.button.click(function(c){var d=a(this);d.hasClass("wp-picker-clear")?(b.element.val(""),b.toggler.css("backgroundColor",""),a.isFunction(b.options.clear)&&b.options.clear.call(this,c)):d.hasClass("wp-picker-default")&&b.element.val(b.options.defaultColor).change()})},open:function(){this.element.show().iris("toggle").focus(),this.button.removeClass("hidden"),this.toggler.addClass("wp-picker-open"),a("body").trigger("click.wpcolorpicker").on("click.wpcolorpicker",this.close)},close:function(){this.element.hide().iris("toggle"),this.button.addClass("hidden"),this.toggler.removeClass("wp-picker-open"),a("body").off("click.wpcolorpicker",this.close)},color:function(a){return a===b?this.element.iris("option","color"):void this.element.iris("option","color",a)},defaultColor:function(a){return a===b?this.options.defaultColor:void(this.options.defaultColor=a)}},a.widget("wp.wpColorPicker",c)}(jQuery); \ No newline at end of file diff --git a/wp-admin/js/comment.js b/wp-admin/js/comment.js new file mode 100644 index 0000000..b5506ea --- /dev/null +++ b/wp-admin/js/comment.js @@ -0,0 +1,50 @@ +/* global postboxes:true, commentL10n:true */ +jQuery(document).ready( function($) { + + postboxes.add_postbox_toggles('comment'); + + var stamp = $('#timestamp').html(); + $('.edit-timestamp').click(function () { + if ($('#timestampdiv').is(':hidden')) { + $('#timestampdiv').slideDown('normal'); + $('.edit-timestamp').hide(); + } + return false; + }); + + $('.cancel-timestamp').click(function() { + $('#timestampdiv').slideUp('normal'); + $('#mm').val($('#hidden_mm').val()); + $('#jj').val($('#hidden_jj').val()); + $('#aa').val($('#hidden_aa').val()); + $('#hh').val($('#hidden_hh').val()); + $('#mn').val($('#hidden_mn').val()); + $('#timestamp').html(stamp); + $('.edit-timestamp').show(); + return false; + }); + + $('.save-timestamp').click(function () { // crazyhorse - multiple ok cancels + var aa = $('#aa').val(), mm = $('#mm').val(), jj = $('#jj').val(), hh = $('#hh').val(), mn = $('#mn').val(), + newD = new Date( aa, mm - 1, jj, hh, mn ); + + if ( newD.getFullYear() != aa || (1 + newD.getMonth()) != mm || newD.getDate() != jj || newD.getMinutes() != mn ) { + $('.timestamp-wrap', '#timestampdiv').addClass('form-invalid'); + return false; + } else { + $('.timestamp-wrap', '#timestampdiv').removeClass('form-invalid'); + } + + $('#timestampdiv').slideUp('normal'); + $('.edit-timestamp').show(); + $('#timestamp').html( + commentL10n.submittedOn + ' ' + + $( '#mm option[value="' + mm + '"]' ).text() + ' ' + + jj + ', ' + + aa + ' @ ' + + hh + ':' + + mn + ' ' + ); + return false; + }); +}); diff --git a/wp-admin/js/comment.min.js b/wp-admin/js/comment.min.js new file mode 100644 index 0000000..fbfa877 --- /dev/null +++ b/wp-admin/js/comment.min.js @@ -0,0 +1 @@ +jQuery(document).ready(function(a){postboxes.add_postbox_toggles("comment");var b=a("#timestamp").html();a(".edit-timestamp").click(function(){return a("#timestampdiv").is(":hidden")&&(a("#timestampdiv").slideDown("normal"),a(".edit-timestamp").hide()),!1}),a(".cancel-timestamp").click(function(){return a("#timestampdiv").slideUp("normal"),a("#mm").val(a("#hidden_mm").val()),a("#jj").val(a("#hidden_jj").val()),a("#aa").val(a("#hidden_aa").val()),a("#hh").val(a("#hidden_hh").val()),a("#mn").val(a("#hidden_mn").val()),a("#timestamp").html(b),a(".edit-timestamp").show(),!1}),a(".save-timestamp").click(function(){var b=a("#aa").val(),c=a("#mm").val(),d=a("#jj").val(),e=a("#hh").val(),f=a("#mn").val(),g=new Date(b,c-1,d,e,f);return g.getFullYear()!=b||1+g.getMonth()!=c||g.getDate()!=d||g.getMinutes()!=f?(a(".timestamp-wrap","#timestampdiv").addClass("form-invalid"),!1):(a(".timestamp-wrap","#timestampdiv").removeClass("form-invalid"),a("#timestampdiv").slideUp("normal"),a(".edit-timestamp").show(),a("#timestamp").html(commentL10n.submittedOn+" "+a('#mm option[value="'+c+'"]').text()+" "+d+", "+b+" @ "+e+":"+f+" "),!1)})}); \ No newline at end of file diff --git a/wp-admin/js/common.js b/wp-admin/js/common.js new file mode 100644 index 0000000..f4253b3 --- /dev/null +++ b/wp-admin/js/common.js @@ -0,0 +1,874 @@ +/* global setUserSetting, ajaxurl, commonL10n, alert, confirm, pagenow */ +var showNotice, adminMenu, columns, validateForm, screenMeta; +( function( $, window, undefined ) { +// Removed in 3.3. +// (perhaps) needed for back-compat +adminMenu = { + init : function() {}, + fold : function() {}, + restoreMenuState : function() {}, + toggle : function() {}, + favorites : function() {} +}; + +// show/hide/save table columns +columns = { + init : function() { + var that = this; + $('.hide-column-tog', '#adv-settings').click( function() { + var $t = $(this), column = $t.val(); + if ( $t.prop('checked') ) + that.checked(column); + else + that.unchecked(column); + + columns.saveManageColumnsState(); + }); + }, + + saveManageColumnsState : function() { + var hidden = this.hidden(); + $.post(ajaxurl, { + action: 'hidden-columns', + hidden: hidden, + screenoptionnonce: $('#screenoptionnonce').val(), + page: pagenow + }); + }, + + checked : function(column) { + $('.column-' + column).show(); + this.colSpanChange(+1); + }, + + unchecked : function(column) { + $('.column-' + column).hide(); + this.colSpanChange(-1); + }, + + hidden : function() { + return $('.manage-column').filter(':hidden').map(function() { return this.id; }).get().join(','); + }, + + useCheckboxesForHidden : function() { + this.hidden = function(){ + return $('.hide-column-tog').not(':checked').map(function() { + var id = this.id; + return id.substring( id, id.length - 5 ); + }).get().join(','); + }; + }, + + colSpanChange : function(diff) { + var $t = $('table').find('.colspanchange'), n; + if ( !$t.length ) + return; + n = parseInt( $t.attr('colspan'), 10 ) + diff; + $t.attr('colspan', n.toString()); + } +}; + +$(document).ready(function(){columns.init();}); + +validateForm = function( form ) { + return !$( form ) + .find( '.form-required' ) + .filter( function() { return $( 'input:visible', this ).val() === ''; } ) + .addClass( 'form-invalid' ) + .find( 'input:visible' ) + .change( function() { $( this ).closest( '.form-invalid' ).removeClass( 'form-invalid' ); } ) + .size(); +}; + +// stub for doing better warnings +showNotice = { + warn : function() { + var msg = commonL10n.warnDelete || ''; + if ( confirm(msg) ) { + return true; + } + + return false; + }, + + note : function(text) { + alert(text); + } +}; + +screenMeta = { + element: null, // #screen-meta + toggles: null, // .screen-meta-toggle + page: null, // #wpcontent + + init: function() { + this.element = $('#screen-meta'); + this.toggles = $('.screen-meta-toggle a'); + this.page = $('#wpcontent'); + + this.toggles.click( this.toggleEvent ); + }, + + toggleEvent: function( e ) { + var panel = $( this.href.replace(/.+#/, '#') ); + e.preventDefault(); + + if ( !panel.length ) + return; + + if ( panel.is(':visible') ) + screenMeta.close( panel, $(this) ); + else + screenMeta.open( panel, $(this) ); + }, + + open: function( panel, link ) { + + $('.screen-meta-toggle').not( link.parent() ).css('visibility', 'hidden'); + + panel.parent().show(); + panel.slideDown( 'fast', function() { + panel.focus(); + link.addClass('screen-meta-active').attr('aria-expanded', true); + }); + + $( document ).trigger( 'screen:options:open' ); + }, + + close: function( panel, link ) { + panel.slideUp( 'fast', function() { + link.removeClass('screen-meta-active').attr('aria-expanded', false); + $('.screen-meta-toggle').css('visibility', ''); + panel.parent().hide(); + }); + + $( document ).trigger( 'screen:options:close' ); + } +}; + +/** + * Help tabs. + */ +$('.contextual-help-tabs').delegate('a', 'click', function(e) { + var link = $(this), + panel; + + e.preventDefault(); + + // Don't do anything if the click is for the tab already showing. + if ( link.is('.active a') ) + return false; + + // Links + $('.contextual-help-tabs .active').removeClass('active'); + link.parent('li').addClass('active'); + + panel = $( link.attr('href') ); + + // Panels + $('.help-tab-content').not( panel ).removeClass('active').hide(); + panel.addClass('active').show(); +}); + +$(document).ready( function() { + var checks, first, last, checked, sliced, mobileEvent, transitionTimeout, focusedRowActions, $firstHeading, + lastClicked = false, + pageInput = $('input.current-page'), + currentPage = pageInput.val(), + isIOS = /iPhone|iPad|iPod/.test( navigator.userAgent ), + isAndroid = navigator.userAgent.indexOf( 'Android' ) !== -1, + isIE8 = $( document.documentElement ).hasClass( 'ie8' ), + $document = $( document ), + $window = $( window ), + $body = $( document.body ), + $adminMenuWrap = $( '#adminmenuwrap' ), + $wpwrap = $( '#wpwrap' ), + $adminmenu = $( '#adminmenu' ), + $overlay = $( '#wp-responsive-overlay' ), + $toolbar = $( '#wp-toolbar' ), + $toolbarPopups = $toolbar.find( 'a[aria-haspopup="true"]' ), + $sortables = $('.meta-box-sortables'), + wpResponsiveActive = false, + $adminbar = $( '#wpadminbar' ), + lastScrollPosition = 0, + pinnedMenuTop = false, + pinnedMenuBottom = false, + menuTop = 0, + menuIsPinned = false, + height = { + window: $window.height(), + wpwrap: $wpwrap.height(), + adminbar: $adminbar.height(), + menu: $adminMenuWrap.height() + }; + + + // when the menu is folded, make the fly-out submenu header clickable + $adminmenu.on('click.wp-submenu-head', '.wp-submenu-head', function(e){ + $(e.target).parent().siblings('a').get(0).click(); + }); + + $('#collapse-menu').on('click.collapse-menu', function() { + var body = $( document.body ), respWidth, state; + + // reset any compensation for submenus near the bottom of the screen + $('#adminmenu div.wp-submenu').css('margin-top', ''); + + if ( window.innerWidth ) { + // window.innerWidth is affected by zooming on phones + respWidth = Math.max( window.innerWidth, document.documentElement.clientWidth ); + } else { + // IE < 9 doesn't support @media CSS rules + respWidth = 961; + } + + if ( respWidth && respWidth < 960 ) { + if ( body.hasClass('auto-fold') ) { + body.removeClass('auto-fold').removeClass('folded'); + setUserSetting('unfold', 1); + setUserSetting('mfold', 'o'); + state = 'open'; + } else { + body.addClass('auto-fold'); + setUserSetting('unfold', 0); + state = 'folded'; + } + } else { + if ( body.hasClass('folded') ) { + body.removeClass('folded'); + setUserSetting('mfold', 'o'); + state = 'open'; + } else { + body.addClass('folded'); + setUserSetting('mfold', 'f'); + state = 'folded'; + } + } + + $( document ).trigger( 'wp-collapse-menu', { state: state } ); + }); + + /** + * Ensure an admin submenu is within the visual viewport. + * + * @since 4.1.0 + * + * @param {jQuery} $menuItem The parent menu item containing the submenu. + */ + function adjustSubmenu( $menuItem ) { + var bottomOffset, pageHeight, adjustment, theFold, menutop, wintop, maxtop, + $submenu = $menuItem.find( '.wp-submenu' ); + + menutop = $menuItem.offset().top; + wintop = $window.scrollTop(); + maxtop = menutop - wintop - 30; // max = make the top of the sub almost touch admin bar + + bottomOffset = menutop + $submenu.height() + 1; // Bottom offset of the menu + pageHeight = $wpwrap.height(); // Height of the entire page + adjustment = 60 + bottomOffset - pageHeight; + theFold = $window.height() + wintop - 50; // The fold + + if ( theFold < ( bottomOffset - adjustment ) ) { + adjustment = bottomOffset - theFold; + } + + if ( adjustment > maxtop ) { + adjustment = maxtop; + } + + if ( adjustment > 1 ) { + $submenu.css( 'margin-top', '-' + adjustment + 'px' ); + } else { + $submenu.css( 'margin-top', '' ); + } + } + + if ( 'ontouchstart' in window || /IEMobile\/[1-9]/.test(navigator.userAgent) ) { // touch screen device + // iOS Safari works with touchstart, the rest work with click + mobileEvent = isIOS ? 'touchstart' : 'click'; + + // close any open submenus when touch/click is not on the menu + $(document.body).on( mobileEvent+'.wp-mobile-hover', function(e) { + if ( $adminmenu.data('wp-responsive') ) { + return; + } + + if ( ! $( e.target ).closest( '#adminmenu' ).length ) { + $adminmenu.find( 'li.opensub' ).removeClass( 'opensub' ); + } + }); + + $adminmenu.find( 'a.wp-has-submenu' ).on( mobileEvent + '.wp-mobile-hover', function( event ) { + var $menuItem = $(this).parent(); + + if ( $adminmenu.data( 'wp-responsive' ) ) { + return; + } + + // Show the sub instead of following the link if: + // - the submenu is not open + // - the submenu is not shown inline or the menu is not folded + if ( ! $menuItem.hasClass( 'opensub' ) && ( ! $menuItem.hasClass( 'wp-menu-open' ) || $menuItem.width() < 40 ) ) { + event.preventDefault(); + adjustSubmenu( $menuItem ); + $adminmenu.find( 'li.opensub' ).removeClass( 'opensub' ); + $menuItem.addClass('opensub'); + } + }); + } + + if ( ! isIOS && ! isAndroid ) { + $adminmenu.find( 'li.wp-has-submenu' ).hoverIntent({ + over: function() { + var $menuItem = $( this ), + $submenu = $menuItem.find( '.wp-submenu' ), + top = parseInt( $submenu.css( 'top' ), 10 ); + + if ( isNaN( top ) || top > -5 ) { // the submenu is visible + return; + } + + if ( $adminmenu.data( 'wp-responsive' ) ) { + // The menu is in responsive mode, bail + return; + } + + adjustSubmenu( $menuItem ); + $adminmenu.find( 'li.opensub' ).removeClass( 'opensub' ); + $menuItem.addClass( 'opensub' ); + }, + out: function(){ + if ( $adminmenu.data( 'wp-responsive' ) ) { + // The menu is in responsive mode, bail + return; + } + + $( this ).removeClass( 'opensub' ).find( '.wp-submenu' ).css( 'margin-top', '' ); + }, + timeout: 200, + sensitivity: 7, + interval: 90 + }); + + $adminmenu.on( 'focus.adminmenu', '.wp-submenu a', function( event ) { + if ( $adminmenu.data( 'wp-responsive' ) ) { + // The menu is in responsive mode, bail + return; + } + + $( event.target ).closest( 'li.menu-top' ).addClass( 'opensub' ); + }).on( 'blur.adminmenu', '.wp-submenu a', function( event ) { + if ( $adminmenu.data( 'wp-responsive' ) ) { + return; + } + + $( event.target ).closest( 'li.menu-top' ).removeClass( 'opensub' ); + }).find( 'li.wp-has-submenu.wp-not-current-submenu' ).on( 'focusin.adminmenu', function() { + adjustSubmenu( $( this ) ); + }); + } + + // Move .notice, .updated and .error alert boxes. Don't move boxes designed to be inline. + $firstHeading = $( 'div.wrap h2:first' ); + $firstHeading.nextAll( 'div.updated, div.error, div.notice' ).addClass( 'below-h2' ); + $( 'div.updated, div.error, div.notice' ).not( '.below-h2, .inline' ).insertAfter( $firstHeading ); + + // Init screen meta + screenMeta.init(); + + // check all checkboxes + $('tbody').children().children('.check-column').find(':checkbox').click( function(e) { + if ( 'undefined' == e.shiftKey ) { return true; } + if ( e.shiftKey ) { + if ( !lastClicked ) { return true; } + checks = $( lastClicked ).closest( 'form' ).find( ':checkbox' ); + first = checks.index( lastClicked ); + last = checks.index( this ); + checked = $(this).prop('checked'); + if ( 0 < first && 0 < last && first != last ) { + sliced = ( last > first ) ? checks.slice( first, last ) : checks.slice( last, first ); + sliced.prop( 'checked', function() { + if ( $(this).closest('tr').is(':visible') ) + return checked; + + return false; + }); + } + } + lastClicked = this; + + // toggle "check all" checkboxes + var unchecked = $(this).closest('tbody').find(':checkbox').filter(':visible').not(':checked'); + $(this).closest('table').children('thead, tfoot').find(':checkbox').prop('checked', function() { + return ( 0 === unchecked.length ); + }); + + return true; + }); + + $('thead, tfoot').find('.check-column :checkbox').on( 'click.wp-toggle-checkboxes', function( event ) { + var $this = $(this), + $table = $this.closest( 'table' ), + controlChecked = $this.prop('checked'), + toggle = event.shiftKey || $this.data('wp-toggle'); + + $table.children( 'tbody' ).filter(':visible') + .children().children('.check-column').find(':checkbox') + .prop('checked', function() { + if ( $(this).is(':hidden') ) { + return false; + } + + if ( toggle ) { + return ! $(this).prop( 'checked' ); + } else if ( controlChecked ) { + return true; + } + + return false; + }); + + $table.children('thead, tfoot').filter(':visible') + .children().children('.check-column').find(':checkbox') + .prop('checked', function() { + if ( toggle ) { + return false; + } else if ( controlChecked ) { + return true; + } + + return false; + }); + }); + + // Show row actions on keyboard focus of its parent container element or any other elements contained within + $( 'td.post-title, td.title, td.comment, .bookmarks td.column-name, td.blogname, td.username, .dashboard-comment-wrap' ).focusin(function(){ + clearTimeout( transitionTimeout ); + focusedRowActions = $(this).find( '.row-actions' ); + focusedRowActions.addClass( 'visible' ); + }).focusout(function(){ + // Tabbing between post title and .row-actions links needs a brief pause, otherwise + // the .row-actions div gets hidden in transit in some browsers (ahem, Firefox). + transitionTimeout = setTimeout(function(){ + focusedRowActions.removeClass( 'visible' ); + }, 30); + }); + + $('#default-password-nag-no').click( function() { + setUserSetting('default_password_nag', 'hide'); + $('div.default-password-nag').hide(); + return false; + }); + + // tab in textareas + $('#newcontent').bind('keydown.wpevent_InsertTab', function(e) { + var el = e.target, selStart, selEnd, val, scroll, sel; + + if ( e.keyCode == 27 ) { // escape key + $(el).data('tab-out', true); + return; + } + + if ( e.keyCode != 9 || e.ctrlKey || e.altKey || e.shiftKey ) // tab key + return; + + if ( $(el).data('tab-out') ) { + $(el).data('tab-out', false); + return; + } + + selStart = el.selectionStart; + selEnd = el.selectionEnd; + val = el.value; + + try { + this.lastKey = 9; // not a standard DOM property, lastKey is to help stop Opera tab event. See blur handler below. + } catch(err) {} + + if ( document.selection ) { + el.focus(); + sel = document.selection.createRange(); + sel.text = '\t'; + } else if ( selStart >= 0 ) { + scroll = this.scrollTop; + el.value = val.substring(0, selStart).concat('\t', val.substring(selEnd) ); + el.selectionStart = el.selectionEnd = selStart + 1; + this.scrollTop = scroll; + } + + if ( e.stopPropagation ) + e.stopPropagation(); + if ( e.preventDefault ) + e.preventDefault(); + }); + + $('#newcontent').bind('blur.wpevent_InsertTab', function() { + if ( this.lastKey && 9 == this.lastKey ) + this.focus(); + }); + + if ( pageInput.length ) { + pageInput.closest('form').submit( function() { + + // Reset paging var for new filters/searches but not for bulk actions. See #17685. + if ( $('select[name="action"]').val() == -1 && $('select[name="action2"]').val() == -1 && pageInput.val() == currentPage ) + pageInput.val('1'); + }); + } + + $('.search-box input[type="search"], .search-box input[type="submit"]').mousedown(function () { + $('select[name^="action"]').val('-1'); + }); + + // Scroll into view when focused + $('#contextual-help-link, #show-settings-link').on( 'focus.scroll-into-view', function(e){ + if ( e.target.scrollIntoView ) + e.target.scrollIntoView(false); + }); + + // Disable upload buttons until files are selected + (function(){ + var button, input, form = $('form.wp-upload-form'); + if ( ! form.length ) + return; + button = form.find('input[type="submit"]'); + input = form.find('input[type="file"]'); + + function toggleUploadButton() { + button.prop('disabled', '' === input.map( function() { + return $(this).val(); + }).get().join('')); + } + toggleUploadButton(); + input.on('change', toggleUploadButton); + })(); + + function pinMenu( event ) { + var windowPos = $window.scrollTop(), + resizing = ! event || event.type !== 'scroll'; + + if ( isIOS || isIE8 || $adminmenu.data( 'wp-responsive' ) ) { + return; + } + + if ( height.menu + height.adminbar < height.window || + height.menu + height.adminbar + 20 > height.wpwrap ) { + unpinMenu(); + return; + } + + menuIsPinned = true; + + if ( height.menu + height.adminbar > height.window ) { + // Check for overscrolling + if ( windowPos < 0 ) { + if ( ! pinnedMenuTop ) { + pinnedMenuTop = true; + pinnedMenuBottom = false; + + $adminMenuWrap.css({ + position: 'fixed', + top: '', + bottom: '' + }); + } + + return; + } else if ( windowPos + height.window > $document.height() - 1 ) { + if ( ! pinnedMenuBottom ) { + pinnedMenuBottom = true; + pinnedMenuTop = false; + + $adminMenuWrap.css({ + position: 'fixed', + top: '', + bottom: 0 + }); + } + + return; + } + + if ( windowPos > lastScrollPosition ) { + // Scrolling down + if ( pinnedMenuTop ) { + // let it scroll + pinnedMenuTop = false; + menuTop = $adminMenuWrap.offset().top - height.adminbar - ( windowPos - lastScrollPosition ); + + if ( menuTop + height.menu + height.adminbar < windowPos + height.window ) { + menuTop = windowPos + height.window - height.menu - height.adminbar; + } + + $adminMenuWrap.css({ + position: 'absolute', + top: menuTop, + bottom: '' + }); + } else if ( ! pinnedMenuBottom && $adminMenuWrap.offset().top + height.menu < windowPos + height.window ) { + // pin the bottom + pinnedMenuBottom = true; + + $adminMenuWrap.css({ + position: 'fixed', + top: '', + bottom: 0 + }); + } + } else if ( windowPos < lastScrollPosition ) { + // Scrolling up + if ( pinnedMenuBottom ) { + // let it scroll + pinnedMenuBottom = false; + menuTop = $adminMenuWrap.offset().top - height.adminbar + ( lastScrollPosition - windowPos ); + + if ( menuTop + height.menu > windowPos + height.window ) { + menuTop = windowPos; + } + + $adminMenuWrap.css({ + position: 'absolute', + top: menuTop, + bottom: '' + }); + } else if ( ! pinnedMenuTop && $adminMenuWrap.offset().top >= windowPos + height.adminbar ) { + // pin the top + pinnedMenuTop = true; + + $adminMenuWrap.css({ + position: 'fixed', + top: '', + bottom: '' + }); + } + } else if ( resizing ) { + // Resizing + pinnedMenuTop = pinnedMenuBottom = false; + menuTop = windowPos + height.window - height.menu - height.adminbar - 1; + + if ( menuTop > 0 ) { + $adminMenuWrap.css({ + position: 'absolute', + top: menuTop, + bottom: '' + }); + } else { + unpinMenu(); + } + } + } + + lastScrollPosition = windowPos; + } + + function resetHeights() { + height = { + window: $window.height(), + wpwrap: $wpwrap.height(), + adminbar: $adminbar.height(), + menu: $adminMenuWrap.height() + }; + } + + function unpinMenu() { + if ( isIOS || ! menuIsPinned ) { + return; + } + + pinnedMenuTop = pinnedMenuBottom = menuIsPinned = false; + $adminMenuWrap.css({ + position: '', + top: '', + bottom: '' + }); + } + + function setPinMenu() { + resetHeights(); + + if ( $adminmenu.data('wp-responsive') ) { + $body.removeClass( 'sticky-menu' ); + unpinMenu(); + } else if ( height.menu + height.adminbar > height.window ) { + pinMenu(); + $body.removeClass( 'sticky-menu' ); + } else { + $body.addClass( 'sticky-menu' ); + unpinMenu(); + } + } + + if ( ! isIOS ) { + $window.on( 'scroll.pin-menu', pinMenu ); + $document.on( 'tinymce-editor-init.pin-menu', function( event, editor ) { + editor.on( 'wp-autoresize', resetHeights ); + }); + } + + window.wpResponsive = { + init: function() { + var self = this; + + // Modify functionality based on custom activate/deactivate event + $document.on( 'wp-responsive-activate.wp-responsive', function() { + self.activate(); + }).on( 'wp-responsive-deactivate.wp-responsive', function() { + self.deactivate(); + }); + + $( '#wp-admin-bar-menu-toggle a' ).attr( 'aria-expanded', 'false' ); + + // Toggle sidebar when toggle is clicked + $( '#wp-admin-bar-menu-toggle' ).on( 'click.wp-responsive', function( event ) { + event.preventDefault(); + $wpwrap.toggleClass( 'wp-responsive-open' ); + if ( $wpwrap.hasClass( 'wp-responsive-open' ) ) { + $(this).find('a').attr( 'aria-expanded', 'true' ); + $( '#adminmenu a:first' ).focus(); + } else { + $(this).find('a').attr( 'aria-expanded', 'false' ); + } + } ); + + // Add menu events + $adminmenu.on( 'click.wp-responsive', 'li.wp-has-submenu > a', function( event ) { + if ( ! $adminmenu.data('wp-responsive') ) { + return; + } + + $( this ).parent( 'li' ).toggleClass( 'selected' ); + event.preventDefault(); + }); + + self.trigger(); + $document.on( 'wp-window-resized.wp-responsive', $.proxy( this.trigger, this ) ); + + // This needs to run later as UI Sortable may be initialized later on $(document).ready() + $window.on( 'load.wp-responsive', function() { + var width = navigator.userAgent.indexOf('AppleWebKit/') > -1 ? $window.width() : window.innerWidth; + + if ( width <= 782 ) { + self.disableSortables(); + } + }); + }, + + activate: function() { + setPinMenu(); + + if ( ! $body.hasClass( 'auto-fold' ) ) { + $body.addClass( 'auto-fold' ); + } + + $adminmenu.data( 'wp-responsive', 1 ); + this.disableSortables(); + }, + + deactivate: function() { + setPinMenu(); + $adminmenu.removeData('wp-responsive'); + this.enableSortables(); + }, + + trigger: function() { + var width; + + if ( window.innerWidth ) { + // window.innerWidth is affected by zooming on phones + width = Math.max( window.innerWidth, document.documentElement.clientWidth ); + } else { + // Exclude IE < 9, it doesn't support @media CSS rules + return; + } + + if ( width <= 782 ) { + if ( ! wpResponsiveActive ) { + $document.trigger( 'wp-responsive-activate' ); + wpResponsiveActive = true; + } + } else { + if ( wpResponsiveActive ) { + $document.trigger( 'wp-responsive-deactivate' ); + wpResponsiveActive = false; + } + } + + if ( width <= 480 ) { + this.enableOverlay(); + } else { + this.disableOverlay(); + } + }, + + enableOverlay: function() { + if ( $overlay.length === 0 ) { + $overlay = $( '
    ' ) + .insertAfter( '#wpcontent' ) + .hide() + .on( 'click.wp-responsive', function() { + $toolbar.find( '.menupop.hover' ).removeClass( 'hover' ); + $( this ).hide(); + }); + } + + $toolbarPopups.on( 'click.wp-responsive', function() { + $overlay.show(); + }); + }, + + disableOverlay: function() { + $toolbarPopups.off( 'click.wp-responsive' ); + $overlay.hide(); + }, + + disableSortables: function() { + if ( $sortables.length ) { + try { + $sortables.sortable('disable'); + } catch(e) {} + } + }, + + enableSortables: function() { + if ( $sortables.length ) { + try { + $sortables.sortable('enable'); + } catch(e) {} + } + } + }; + + window.wpResponsive.init(); + setPinMenu(); + + $document.on( 'wp-window-resized.pin-menu postboxes-columnchange.pin-menu postbox-toggled.pin-menu wp-collapse-menu.pin-menu wp-scroll-start.pin-menu', setPinMenu ); +}); + +// Fire a custom jQuery event at the end of window resize +( function() { + var timeout; + + function triggerEvent() { + $(document).trigger( 'wp-window-resized' ); + } + + function fireOnce() { + window.clearTimeout( timeout ); + timeout = window.setTimeout( triggerEvent, 200 ); + } + + $(window).on( 'resize.wp-fire-once', fireOnce ); +}()); + +// Make Windows 8 devices play along nicely. +(function(){ + if ( '-ms-user-select' in document.documentElement.style && navigator.userAgent.match(/IEMobile\/10\.0/) ) { + var msViewportStyle = document.createElement( 'style' ); + msViewportStyle.appendChild( + document.createTextNode( '@-ms-viewport{width:auto!important}' ) + ); + document.getElementsByTagName( 'head' )[0].appendChild( msViewportStyle ); + } +})(); + +}( jQuery, window )); diff --git a/wp-admin/js/common.min.js b/wp-admin/js/common.min.js new file mode 100644 index 0000000..798ebce --- /dev/null +++ b/wp-admin/js/common.min.js @@ -0,0 +1 @@ +var showNotice,adminMenu,columns,validateForm,screenMeta;!function(a,b){adminMenu={init:function(){},fold:function(){},restoreMenuState:function(){},toggle:function(){},favorites:function(){}},columns={init:function(){var b=this;a(".hide-column-tog","#adv-settings").click(function(){var c=a(this),d=c.val();c.prop("checked")?b.checked(d):b.unchecked(d),columns.saveManageColumnsState()})},saveManageColumnsState:function(){var b=this.hidden();a.post(ajaxurl,{action:"hidden-columns",hidden:b,screenoptionnonce:a("#screenoptionnonce").val(),page:pagenow})},checked:function(b){a(".column-"+b).show(),this.colSpanChange(1)},unchecked:function(b){a(".column-"+b).hide(),this.colSpanChange(-1)},hidden:function(){return a(".manage-column").filter(":hidden").map(function(){return this.id}).get().join(",")},useCheckboxesForHidden:function(){this.hidden=function(){return a(".hide-column-tog").not(":checked").map(function(){var a=this.id;return a.substring(a,a.length-5)}).get().join(",")}},colSpanChange:function(b){var c,d=a("table").find(".colspanchange");d.length&&(c=parseInt(d.attr("colspan"),10)+b,d.attr("colspan",c.toString()))}},a(document).ready(function(){columns.init()}),validateForm=function(b){return!a(b).find(".form-required").filter(function(){return""===a("input:visible",this).val()}).addClass("form-invalid").find("input:visible").change(function(){a(this).closest(".form-invalid").removeClass("form-invalid")}).size()},showNotice={warn:function(){var a=commonL10n.warnDelete||"";return confirm(a)?!0:!1},note:function(a){alert(a)}},screenMeta={element:null,toggles:null,page:null,init:function(){this.element=a("#screen-meta"),this.toggles=a(".screen-meta-toggle a"),this.page=a("#wpcontent"),this.toggles.click(this.toggleEvent)},toggleEvent:function(b){var c=a(this.href.replace(/.+#/,"#"));b.preventDefault(),c.length&&(c.is(":visible")?screenMeta.close(c,a(this)):screenMeta.open(c,a(this)))},open:function(b,c){a(".screen-meta-toggle").not(c.parent()).css("visibility","hidden"),b.parent().show(),b.slideDown("fast",function(){b.focus(),c.addClass("screen-meta-active").attr("aria-expanded",!0)}),a(document).trigger("screen:options:open")},close:function(b,c){b.slideUp("fast",function(){c.removeClass("screen-meta-active").attr("aria-expanded",!1),a(".screen-meta-toggle").css("visibility",""),b.parent().hide()}),a(document).trigger("screen:options:close")}},a(".contextual-help-tabs").delegate("a","click",function(b){var c,d=a(this);return b.preventDefault(),d.is(".active a")?!1:(a(".contextual-help-tabs .active").removeClass("active"),d.parent("li").addClass("active"),c=a(d.attr("href")),a(".help-tab-content").not(c).removeClass("active").hide(),void c.addClass("active").show())}),a(document).ready(function(){function c(a){var b,c,d,e,f,g,h,i=a.find(".wp-submenu");f=a.offset().top,g=x.scrollTop(),h=f-g-30,b=f+i.height()+1,c=A.height(),d=60+b-c,e=x.height()+g-50,b-d>e&&(d=b-e),d>h&&(d=h),d>1?i.css("margin-top","-"+d+"px"):i.css("margin-top","")}function d(a){var b=x.scrollTop(),c=!a||"scroll"!==a.type;if(!(t||v||B.data("wp-responsive"))){if(N.menu+N.adminbarN.wpwrap)return void f();if(M=!0,N.menu+N.adminbar>N.window){if(0>b)return void(J||(J=!0,K=!1,z.css({position:"fixed",top:"",bottom:""})));if(b+N.window>w.height()-1)return void(K||(K=!0,J=!1,z.css({position:"fixed",top:"",bottom:0})));b>I?J?(J=!1,L=z.offset().top-N.adminbar-(b-I),L+N.menu+N.adminbarb?K?(K=!1,L=z.offset().top-N.adminbar+(I-b),L+N.menu>b+N.window&&(L=b),z.css({position:"absolute",top:L,bottom:""})):!J&&z.offset().top>=b+N.adminbar&&(J=!0,z.css({position:"fixed",top:"",bottom:""})):c&&(J=K=!1,L=b+N.window-N.menu-N.adminbar-1,L>0?z.css({position:"absolute",top:L,bottom:""}):f())}I=b}}function e(){N={window:x.height(),wpwrap:A.height(),adminbar:H.height(),menu:z.height()}}function f(){!t&&M&&(J=K=M=!1,z.css({position:"",top:"",bottom:""}))}function g(){e(),B.data("wp-responsive")?(y.removeClass("sticky-menu"),f()):N.menu+N.adminbar>N.window?(d(),y.removeClass("sticky-menu")):(y.addClass("sticky-menu"),f())}var h,i,j,k,l,m,n,o,p,q=!1,r=a("input.current-page"),s=r.val(),t=/iPhone|iPad|iPod/.test(navigator.userAgent),u=-1!==navigator.userAgent.indexOf("Android"),v=a(document.documentElement).hasClass("ie8"),w=a(document),x=a(b),y=a(document.body),z=a("#adminmenuwrap"),A=a("#wpwrap"),B=a("#adminmenu"),C=a("#wp-responsive-overlay"),D=a("#wp-toolbar"),E=D.find('a[aria-haspopup="true"]'),F=a(".meta-box-sortables"),G=!1,H=a("#wpadminbar"),I=0,J=!1,K=!1,L=0,M=!1,N={window:x.height(),wpwrap:A.height(),adminbar:H.height(),menu:z.height()};B.on("click.wp-submenu-head",".wp-submenu-head",function(b){a(b.target).parent().siblings("a").get(0).click()}),a("#collapse-menu").on("click.collapse-menu",function(){var c,d,e=a(document.body);a("#adminmenu div.wp-submenu").css("margin-top",""),c=b.innerWidth?Math.max(b.innerWidth,document.documentElement.clientWidth):961,c&&960>c?e.hasClass("auto-fold")?(e.removeClass("auto-fold").removeClass("folded"),setUserSetting("unfold",1),setUserSetting("mfold","o"),d="open"):(e.addClass("auto-fold"),setUserSetting("unfold",0),d="folded"):e.hasClass("folded")?(e.removeClass("folded"),setUserSetting("mfold","o"),d="open"):(e.addClass("folded"),setUserSetting("mfold","f"),d="folded"),a(document).trigger("wp-collapse-menu",{state:d})}),("ontouchstart"in b||/IEMobile\/[1-9]/.test(navigator.userAgent))&&(m=t?"touchstart":"click",a(document.body).on(m+".wp-mobile-hover",function(b){B.data("wp-responsive")||a(b.target).closest("#adminmenu").length||B.find("li.opensub").removeClass("opensub")}),B.find("a.wp-has-submenu").on(m+".wp-mobile-hover",function(b){var d=a(this).parent();B.data("wp-responsive")||d.hasClass("opensub")||d.hasClass("wp-menu-open")&&!(d.width()<40)||(b.preventDefault(),c(d),B.find("li.opensub").removeClass("opensub"),d.addClass("opensub"))})),t||u||(B.find("li.wp-has-submenu").hoverIntent({over:function(){var b=a(this),d=b.find(".wp-submenu"),e=parseInt(d.css("top"),10);isNaN(e)||e>-5||B.data("wp-responsive")||(c(b),B.find("li.opensub").removeClass("opensub"),b.addClass("opensub"))},out:function(){B.data("wp-responsive")||a(this).removeClass("opensub").find(".wp-submenu").css("margin-top","")},timeout:200,sensitivity:7,interval:90}),B.on("focus.adminmenu",".wp-submenu a",function(b){B.data("wp-responsive")||a(b.target).closest("li.menu-top").addClass("opensub")}).on("blur.adminmenu",".wp-submenu a",function(b){B.data("wp-responsive")||a(b.target).closest("li.menu-top").removeClass("opensub")}).find("li.wp-has-submenu.wp-not-current-submenu").on("focusin.adminmenu",function(){c(a(this))})),p=a("div.wrap h2:first"),p.nextAll("div.updated, div.error, div.notice").addClass("below-h2"),a("div.updated, div.error, div.notice").not(".below-h2, .inline").insertAfter(p),screenMeta.init(),a("tbody").children().children(".check-column").find(":checkbox").click(function(b){if("undefined"==b.shiftKey)return!0;if(b.shiftKey){if(!q)return!0;h=a(q).closest("form").find(":checkbox"),i=h.index(q),j=h.index(this),k=a(this).prop("checked"),i>0&&j>0&&i!=j&&(l=j>i?h.slice(i,j):h.slice(j,i),l.prop("checked",function(){return a(this).closest("tr").is(":visible")?k:!1}))}q=this;var c=a(this).closest("tbody").find(":checkbox").filter(":visible").not(":checked");return a(this).closest("table").children("thead, tfoot").find(":checkbox").prop("checked",function(){return 0===c.length}),!0}),a("thead, tfoot").find(".check-column :checkbox").on("click.wp-toggle-checkboxes",function(b){var c=a(this),d=c.closest("table"),e=c.prop("checked"),f=b.shiftKey||c.data("wp-toggle");d.children("tbody").filter(":visible").children().children(".check-column").find(":checkbox").prop("checked",function(){return a(this).is(":hidden")?!1:f?!a(this).prop("checked"):e?!0:!1}),d.children("thead, tfoot").filter(":visible").children().children(".check-column").find(":checkbox").prop("checked",function(){return f?!1:e?!0:!1})}),a("td.post-title, td.title, td.comment, .bookmarks td.column-name, td.blogname, td.username, .dashboard-comment-wrap").focusin(function(){clearTimeout(n),o=a(this).find(".row-actions"),o.addClass("visible")}).focusout(function(){n=setTimeout(function(){o.removeClass("visible")},30)}),a("#default-password-nag-no").click(function(){return setUserSetting("default_password_nag","hide"),a("div.default-password-nag").hide(),!1}),a("#newcontent").bind("keydown.wpevent_InsertTab",function(b){var c,d,e,f,g,h=b.target;if(27==b.keyCode)return void a(h).data("tab-out",!0);if(!(9!=b.keyCode||b.ctrlKey||b.altKey||b.shiftKey)){if(a(h).data("tab-out"))return void a(h).data("tab-out",!1);c=h.selectionStart,d=h.selectionEnd,e=h.value;try{this.lastKey=9}catch(i){}document.selection?(h.focus(),g=document.selection.createRange(),g.text=" "):c>=0&&(f=this.scrollTop,h.value=e.substring(0,c).concat(" ",e.substring(d)),h.selectionStart=h.selectionEnd=c+1,this.scrollTop=f),b.stopPropagation&&b.stopPropagation(),b.preventDefault&&b.preventDefault()}}),a("#newcontent").bind("blur.wpevent_InsertTab",function(){this.lastKey&&9==this.lastKey&&this.focus()}),r.length&&r.closest("form").submit(function(){-1==a('select[name="action"]').val()&&-1==a('select[name="action2"]').val()&&r.val()==s&&r.val("1")}),a('.search-box input[type="search"], .search-box input[type="submit"]').mousedown(function(){a('select[name^="action"]').val("-1")}),a("#contextual-help-link, #show-settings-link").on("focus.scroll-into-view",function(a){a.target.scrollIntoView&&a.target.scrollIntoView(!1)}),function(){function b(){c.prop("disabled",""===d.map(function(){return a(this).val()}).get().join(""))}var c,d,e=a("form.wp-upload-form");e.length&&(c=e.find('input[type="submit"]'),d=e.find('input[type="file"]'),b(),d.on("change",b))}(),t||(x.on("scroll.pin-menu",d),w.on("tinymce-editor-init.pin-menu",function(a,b){b.on("wp-autoresize",e)})),b.wpResponsive={init:function(){var c=this;w.on("wp-responsive-activate.wp-responsive",function(){c.activate()}).on("wp-responsive-deactivate.wp-responsive",function(){c.deactivate()}),a("#wp-admin-bar-menu-toggle a").attr("aria-expanded","false"),a("#wp-admin-bar-menu-toggle").on("click.wp-responsive",function(b){b.preventDefault(),A.toggleClass("wp-responsive-open"),A.hasClass("wp-responsive-open")?(a(this).find("a").attr("aria-expanded","true"),a("#adminmenu a:first").focus()):a(this).find("a").attr("aria-expanded","false")}),B.on("click.wp-responsive","li.wp-has-submenu > a",function(b){B.data("wp-responsive")&&(a(this).parent("li").toggleClass("selected"),b.preventDefault())}),c.trigger(),w.on("wp-window-resized.wp-responsive",a.proxy(this.trigger,this)),x.on("load.wp-responsive",function(){var a=navigator.userAgent.indexOf("AppleWebKit/")>-1?x.width():b.innerWidth;782>=a&&c.disableSortables()})},activate:function(){g(),y.hasClass("auto-fold")||y.addClass("auto-fold"),B.data("wp-responsive",1),this.disableSortables()},deactivate:function(){g(),B.removeData("wp-responsive"),this.enableSortables()},trigger:function(){var a;b.innerWidth&&(a=Math.max(b.innerWidth,document.documentElement.clientWidth),782>=a?G||(w.trigger("wp-responsive-activate"),G=!0):G&&(w.trigger("wp-responsive-deactivate"),G=!1),480>=a?this.enableOverlay():this.disableOverlay())},enableOverlay:function(){0===C.length&&(C=a('
    ').insertAfter("#wpcontent").hide().on("click.wp-responsive",function(){D.find(".menupop.hover").removeClass("hover"),a(this).hide()})),E.on("click.wp-responsive",function(){C.show()})},disableOverlay:function(){E.off("click.wp-responsive"),C.hide()},disableSortables:function(){if(F.length)try{F.sortable("disable")}catch(a){}},enableSortables:function(){if(F.length)try{F.sortable("enable")}catch(a){}}},b.wpResponsive.init(),g(),w.on("wp-window-resized.pin-menu postboxes-columnchange.pin-menu postbox-toggled.pin-menu wp-collapse-menu.pin-menu wp-scroll-start.pin-menu",g)}),function(){function c(){a(document).trigger("wp-window-resized")}function d(){b.clearTimeout(e),e=b.setTimeout(c,200)}var e;a(b).on("resize.wp-fire-once",d)}(),function(){if("-ms-user-select"in document.documentElement.style&&navigator.userAgent.match(/IEMobile\/10\.0/)){var a=document.createElement("style");a.appendChild(document.createTextNode("@-ms-viewport{width:auto!important}")),document.getElementsByTagName("head")[0].appendChild(a)}}()}(jQuery,window); \ No newline at end of file diff --git a/wp-admin/js/custom-background.js b/wp-admin/js/custom-background.js new file mode 100644 index 0000000..81fd591 --- /dev/null +++ b/wp-admin/js/custom-background.js @@ -0,0 +1,75 @@ +/* global ajaxurl */ +(function($) { + $(document).ready(function() { + var frame, + bgImage = $( '#custom-background-image' ); + + $('#background-color').wpColorPicker({ + change: function( event, ui ) { + bgImage.css('background-color', ui.color.toString()); + }, + clear: function() { + bgImage.css('background-color', ''); + } + }); + + $('input[name="background-position-x"]').change(function() { + bgImage.css('background-position', $(this).val() + ' top'); + }); + + $('input[name="background-repeat"]').change(function() { + bgImage.css('background-repeat', $(this).val()); + }); + + $('#choose-from-library-link').click( function( event ) { + var $el = $(this); + + event.preventDefault(); + + // If the media frame already exists, reopen it. + if ( frame ) { + frame.open(); + return; + } + + // Create the media frame. + frame = wp.media.frames.customBackground = wp.media({ + // Set the title of the modal. + title: $el.data('choose'), + + // Tell the modal to show only images. + library: { + type: 'image' + }, + + // Customize the submit button. + button: { + // Set the text of the button. + text: $el.data('update'), + // Tell the button not to close the modal, since we're + // going to refresh the page when the image is selected. + close: false + } + }); + + // When an image is selected, run a callback. + frame.on( 'select', function() { + // Grab the selected attachment. + var attachment = frame.state().get('selection').first(); + + // Run an AJAX request to set the background image. + $.post( ajaxurl, { + action: 'set-background-image', + attachment_id: attachment.id, + size: 'full' + }).done( function() { + // When the request completes, reload the window. + window.location.reload(); + }); + }); + + // Finally, open the modal. + frame.open(); + }); + }); +})(jQuery); diff --git a/wp-admin/js/custom-background.min.js b/wp-admin/js/custom-background.min.js new file mode 100644 index 0000000..f53e02c --- /dev/null +++ b/wp-admin/js/custom-background.min.js @@ -0,0 +1 @@ +!function(a){a(document).ready(function(){var b,c=a("#custom-background-image");a("#background-color").wpColorPicker({change:function(a,b){c.css("background-color",b.color.toString())},clear:function(){c.css("background-color","")}}),a('input[name="background-position-x"]').change(function(){c.css("background-position",a(this).val()+" top")}),a('input[name="background-repeat"]').change(function(){c.css("background-repeat",a(this).val())}),a("#choose-from-library-link").click(function(c){var d=a(this);return c.preventDefault(),b?void b.open():(b=wp.media.frames.customBackground=wp.media({title:d.data("choose"),library:{type:"image"},button:{text:d.data("update"),close:!1}}),b.on("select",function(){var c=b.state().get("selection").first();a.post(ajaxurl,{action:"set-background-image",attachment_id:c.id,size:"full"}).done(function(){window.location.reload()})}),void b.open())})})}(jQuery); \ No newline at end of file diff --git a/wp-admin/js/custom-header.js b/wp-admin/js/custom-header.js new file mode 100644 index 0000000..ce224e4 --- /dev/null +++ b/wp-admin/js/custom-header.js @@ -0,0 +1,61 @@ +/* global isRtl */ +(function($) { + var frame; + + $( function() { + // Fetch available headers and apply jQuery.masonry + // once the images have loaded. + var $headers = $('.available-headers'); + + $headers.imagesLoaded( function() { + $headers.masonry({ + itemSelector: '.default-header', + isRTL: !! ( 'undefined' != typeof isRtl && isRtl ) + }); + }); + + // Build the choose from library frame. + $('#choose-from-library-link').click( function( event ) { + var $el = $(this); + event.preventDefault(); + + // If the media frame already exists, reopen it. + if ( frame ) { + frame.open(); + return; + } + + // Create the media frame. + frame = wp.media.frames.customHeader = wp.media({ + // Set the title of the modal. + title: $el.data('choose'), + + // Tell the modal to show only images. + library: { + type: 'image' + }, + + // Customize the submit button. + button: { + // Set the text of the button. + text: $el.data('update'), + // Tell the button not to close the modal, since we're + // going to refresh the page when the image is selected. + close: false + } + }); + + // When an image is selected, run a callback. + frame.on( 'select', function() { + // Grab the selected attachment. + var attachment = frame.state().get('selection').first(), + link = $el.data('updateLink'); + + // Tell the browser to navigate to the crop step. + window.location = link + '&file=' + attachment.id; + }); + + frame.open(); + }); + }); +}(jQuery)); diff --git a/wp-admin/js/customize-controls.js b/wp-admin/js/customize-controls.js new file mode 100644 index 0000000..f43630a --- /dev/null +++ b/wp-admin/js/customize-controls.js @@ -0,0 +1,2347 @@ +/* globals _wpCustomizeHeader, _wpCustomizeBackground, _wpMediaViewsL10n */ +(function( exports, $ ){ + var Container, focus, api = wp.customize; + + /** + * @class + * @augments wp.customize.Value + * @augments wp.customize.Class + * + * @param options + * - previewer - The Previewer instance to sync with. + * - transport - The transport to use for previewing. Supports 'refresh' and 'postMessage'. + */ + api.Setting = api.Value.extend({ + initialize: function( id, value, options ) { + api.Value.prototype.initialize.call( this, value, options ); + + this.id = id; + this.transport = this.transport || 'refresh'; + + this.bind( this.preview ); + }, + preview: function() { + switch ( this.transport ) { + case 'refresh': + return this.previewer.refresh(); + case 'postMessage': + return this.previewer.send( 'setting', [ this.id, this() ] ); + } + } + }); + + /** + * Utility function namespace + */ + api.utils = {}; + + /** + * Watch all changes to Value properties, and bubble changes to parent Values instance + * + * @since 4.1.0 + * + * @param {wp.customize.Class} instance + * @param {Array} properties The names of the Value instances to watch. + */ + api.utils.bubbleChildValueChanges = function ( instance, properties ) { + $.each( properties, function ( i, key ) { + instance[ key ].bind( function ( to, from ) { + if ( instance.parent && to !== from ) { + instance.parent.trigger( 'change', instance ); + } + } ); + } ); + }; + + /** + * Expand a panel, section, or control and focus on the first focusable element. + * + * @since 4.1.0 + * + * @param {Object} [params] + * @param {Callback} [params.completeCallback] + */ + focus = function ( params ) { + var construct, completeCallback, focus; + construct = this; + params = params || {}; + focus = function () { + construct.container.find( ':focusable:first' ).focus(); + construct.container[0].scrollIntoView( true ); + }; + if ( params.completeCallback ) { + completeCallback = params.completeCallback; + params.completeCallback = function () { + focus(); + completeCallback(); + }; + } else { + params.completeCallback = focus; + } + if ( construct.expand ) { + construct.expand( params ); + } else { + params.completeCallback(); + } + }; + + /** + * Stable sort for Panels, Sections, and Controls. + * + * If a.priority() === b.priority(), then sort by their respective params.instanceNumber. + * + * @since 4.1.0 + * + * @param {(wp.customize.Panel|wp.customize.Section|wp.customize.Control)} a + * @param {(wp.customize.Panel|wp.customize.Section|wp.customize.Control)} b + * @returns {Number} + */ + api.utils.prioritySort = function ( a, b ) { + if ( a.priority() === b.priority() && typeof a.params.instanceNumber === 'number' && typeof b.params.instanceNumber === 'number' ) { + return a.params.instanceNumber - b.params.instanceNumber; + } else { + return a.priority() - b.priority(); + } + }; + + /** + * Return whether the supplied Event object is for a keydown event but not the Enter key. + * + * @since 4.1.0 + * + * @param {jQuery.Event} event + * @returns {boolean} + */ + api.utils.isKeydownButNotEnterEvent = function ( event ) { + return ( 'keydown' === event.type && 13 !== event.which ); + }; + + /** + * Return whether the two lists of elements are the same and are in the same order. + * + * @since 4.1.0 + * + * @param {Array|jQuery} listA + * @param {Array|jQuery} listB + * @returns {boolean} + */ + api.utils.areElementListsEqual = function ( listA, listB ) { + var equal = ( + listA.length === listB.length && // if lists are different lengths, then naturally they are not equal + -1 === _.indexOf( _.map( // are there any false values in the list returned by map? + _.zip( listA, listB ), // pair up each element between the two lists + function ( pair ) { + return $( pair[0] ).is( pair[1] ); // compare to see if each pair are equal + } + ), false ) // check for presence of false in map's return value + ); + return equal; + }; + + /** + * Base class for Panel and Section. + * + * @since 4.1.0 + * + * @class + * @augments wp.customize.Class + */ + Container = api.Class.extend({ + defaultActiveArguments: { duration: 'fast', completeCallback: $.noop }, + defaultExpandedArguments: { duration: 'fast', completeCallback: $.noop }, + + /** + * @since 4.1.0 + * + * @param {String} id + * @param {Object} options + */ + initialize: function ( id, options ) { + var container = this; + container.id = id; + container.params = {}; + $.extend( container, options || {} ); + container.container = $( container.params.content ); + + container.deferred = { + embedded: new $.Deferred() + }; + container.priority = new api.Value(); + container.active = new api.Value(); + container.activeArgumentsQueue = []; + container.expanded = new api.Value(); + container.expandedArgumentsQueue = []; + + container.active.bind( function ( active ) { + var args = container.activeArgumentsQueue.shift(); + args = $.extend( {}, container.defaultActiveArguments, args ); + active = ( active && container.isContextuallyActive() ); + container.onChangeActive( active, args ); + }); + container.expanded.bind( function ( expanded ) { + var args = container.expandedArgumentsQueue.shift(); + args = $.extend( {}, container.defaultExpandedArguments, args ); + container.onChangeExpanded( expanded, args ); + }); + + container.attachEvents(); + + api.utils.bubbleChildValueChanges( container, [ 'priority', 'active' ] ); + + container.priority.set( isNaN( container.params.priority ) ? 100 : container.params.priority ); + container.active.set( container.params.active ); + container.expanded.set( false ); + }, + + /** + * @since 4.1.0 + * + * @abstract + */ + ready: function() {}, + + /** + * Get the child models associated with this parent, sorting them by their priority Value. + * + * @since 4.1.0 + * + * @param {String} parentType + * @param {String} childType + * @returns {Array} + */ + _children: function ( parentType, childType ) { + var parent = this, + children = []; + api[ childType ].each( function ( child ) { + if ( child[ parentType ].get() === parent.id ) { + children.push( child ); + } + } ); + children.sort( api.utils.prioritySort ); + return children; + }, + + /** + * To override by subclass, to return whether the container has active children. + * + * @since 4.1.0 + * + * @abstract + */ + isContextuallyActive: function () { + throw new Error( 'Container.isContextuallyActive() must be overridden in a subclass.' ); + }, + + /** + * Handle changes to the active state. + * + * This does not change the active state, it merely handles the behavior + * for when it does change. + * + * To override by subclass, update the container's UI to reflect the provided active state. + * + * @since 4.1.0 + * + * @param {Boolean} active + * @param {Object} args + * @param {Object} args.duration + * @param {Object} args.completeCallback + */ + onChangeActive: function ( active, args ) { + var duration = ( 'resolved' === api.previewer.deferred.active.state() ? args.duration : 0 ); + if ( ! $.contains( document, this.container ) ) { + // jQuery.fn.slideUp is not hiding an element if it is not in the DOM + this.container.toggle( active ); + if ( args.completeCallback ) { + args.completeCallback(); + } + } else if ( active ) { + this.container.stop( true, true ).slideDown( duration, args.completeCallback ); + } else { + this.container.stop( true, true ).slideUp( duration, args.completeCallback ); + } + }, + + /** + * @since 4.1.0 + * + * @params {Boolean} active + * @param {Object} [params] + * @returns {Boolean} false if state already applied + */ + _toggleActive: function ( active, params ) { + var self = this; + params = params || {}; + if ( ( active && this.active.get() ) || ( ! active && ! this.active.get() ) ) { + params.unchanged = true; + self.onChangeActive( self.active.get(), params ); + return false; + } else { + params.unchanged = false; + this.activeArgumentsQueue.push( params ); + this.active.set( active ); + return true; + } + }, + + /** + * @param {Object} [params] + * @returns {Boolean} false if already active + */ + activate: function ( params ) { + return this._toggleActive( true, params ); + }, + + /** + * @param {Object} [params] + * @returns {Boolean} false if already inactive + */ + deactivate: function ( params ) { + return this._toggleActive( false, params ); + }, + + /** + * To override by subclass, update the container's UI to reflect the provided active state. + * @abstract + */ + onChangeExpanded: function () { + throw new Error( 'Must override with subclass.' ); + }, + + /** + * @param {Boolean} expanded + * @param {Object} [params] + * @returns {Boolean} false if state already applied + */ + _toggleExpanded: function ( expanded, params ) { + var self = this; + params = params || {}; + if ( ( expanded && this.expanded.get() ) || ( ! expanded && ! this.expanded.get() ) ) { + params.unchanged = true; + self.onChangeExpanded( self.expanded.get(), params ); + return false; + } else { + params.unchanged = false; + this.expandedArgumentsQueue.push( params ); + this.expanded.set( expanded ); + return true; + } + }, + + /** + * @param {Object} [params] + * @returns {Boolean} false if already expanded + */ + expand: function ( params ) { + return this._toggleExpanded( true, params ); + }, + + /** + * @param {Object} [params] + * @returns {Boolean} false if already collapsed + */ + collapse: function ( params ) { + return this._toggleExpanded( false, params ); + }, + + /** + * Bring the container into view and then expand this and bring it into view + * @param {Object} [params] + */ + focus: focus + }); + + /** + * @since 4.1.0 + * + * @class + * @augments wp.customize.Class + */ + api.Section = Container.extend({ + + /** + * @since 4.1.0 + * + * @param {String} id + * @param {Array} options + */ + initialize: function ( id, options ) { + var section = this; + Container.prototype.initialize.call( section, id, options ); + + section.id = id; + section.panel = new api.Value(); + section.panel.bind( function ( id ) { + $( section.container ).toggleClass( 'control-subsection', !! id ); + }); + section.panel.set( section.params.panel || '' ); + api.utils.bubbleChildValueChanges( section, [ 'panel' ] ); + + section.embed(); + section.deferred.embedded.done( function () { + section.ready(); + }); + }, + + /** + * Embed the container in the DOM when any parent panel is ready. + * + * @since 4.1.0 + */ + embed: function () { + var section = this, inject; + + // Watch for changes to the panel state + inject = function ( panelId ) { + var parentContainer; + if ( panelId ) { + // The panel has been supplied, so wait until the panel object is registered + api.panel( panelId, function ( panel ) { + // The panel has been registered, wait for it to become ready/initialized + panel.deferred.embedded.done( function () { + parentContainer = panel.container.find( 'ul:first' ); + if ( ! section.container.parent().is( parentContainer ) ) { + parentContainer.append( section.container ); + } + section.deferred.embedded.resolve(); + }); + } ); + } else { + // There is no panel, so embed the section in the root of the customizer + parentContainer = $( '#customize-theme-controls' ).children( 'ul' ); // @todo This should be defined elsewhere, and to be configurable + if ( ! section.container.parent().is( parentContainer ) ) { + parentContainer.append( section.container ); + } + section.deferred.embedded.resolve(); + } + }; + section.panel.bind( inject ); + inject( section.panel.get() ); // Since a section may never get a panel, assume that it won't ever get one + }, + + /** + * Add behaviors for the accordion section. + * + * @since 4.1.0 + */ + attachEvents: function () { + var section = this; + + // Expand/Collapse accordion sections on click. + section.container.find( '.accordion-section-title' ).on( 'click keydown', function( event ) { + if ( api.utils.isKeydownButNotEnterEvent( event ) ) { + return; + } + event.preventDefault(); // Keep this AFTER the key filter above + + if ( section.expanded() ) { + section.collapse(); + } else { + section.expand(); + } + }); + }, + + /** + * Return whether this section has any active controls. + * + * @since 4.1.0 + * + * @returns {Boolean} + */ + isContextuallyActive: function () { + var section = this, + controls = section.controls(), + activeCount = 0; + _( controls ).each( function ( control ) { + if ( control.active() ) { + activeCount += 1; + } + } ); + return ( activeCount !== 0 ); + }, + + /** + * Get the controls that are associated with this section, sorted by their priority Value. + * + * @since 4.1.0 + * + * @returns {Array} + */ + controls: function () { + return this._children( 'section', 'control' ); + }, + + /** + * Update UI to reflect expanded state. + * + * @since 4.1.0 + * + * @param {Boolean} expanded + * @param {Object} args + */ + onChangeExpanded: function ( expanded, args ) { + var section = this, + content = section.container.find( '.accordion-section-content' ), + expand; + + if ( expanded ) { + + if ( args.unchanged ) { + expand = args.completeCallback; + } else { + expand = function () { + content.stop().slideDown( args.duration, args.completeCallback ); + section.container.addClass( 'open' ); + }; + } + + if ( ! args.allowMultiple ) { + api.section.each( function ( otherSection ) { + if ( otherSection !== section ) { + otherSection.collapse( { duration: args.duration } ); + } + }); + } + + if ( section.panel() ) { + api.panel( section.panel() ).expand({ + duration: args.duration, + completeCallback: expand + }); + } else { + expand(); + } + + } else { + section.container.removeClass( 'open' ); + content.slideUp( args.duration, args.completeCallback ); + } + } + }); + + /** + * @since 4.1.0 + * + * @class + * @augments wp.customize.Class + */ + api.Panel = Container.extend({ + /** + * @since 4.1.0 + * + * @param {String} id + * @param {Object} options + */ + initialize: function ( id, options ) { + var panel = this; + Container.prototype.initialize.call( panel, id, options ); + panel.embed(); + panel.deferred.embedded.done( function () { + panel.ready(); + }); + }, + + /** + * Embed the container in the DOM when any parent panel is ready. + * + * @since 4.1.0 + */ + embed: function () { + var panel = this, + parentContainer = $( '#customize-theme-controls > ul' ); // @todo This should be defined elsewhere, and to be configurable + + if ( ! panel.container.parent().is( parentContainer ) ) { + parentContainer.append( panel.container ); + } + panel.deferred.embedded.resolve(); + }, + + /** + * @since 4.1.0 + */ + attachEvents: function () { + var meta, panel = this; + + // Expand/Collapse accordion sections on click. + panel.container.find( '.accordion-section-title' ).on( 'click keydown', function( event ) { + if ( api.utils.isKeydownButNotEnterEvent( event ) ) { + return; + } + event.preventDefault(); // Keep this AFTER the key filter above + + if ( ! panel.expanded() ) { + panel.expand(); + } + }); + + meta = panel.container.find( '.panel-meta:first' ); + + meta.find( '> .accordion-section-title' ).on( 'click keydown', function( event ) { + if ( api.utils.isKeydownButNotEnterEvent( event ) ) { + return; + } + event.preventDefault(); // Keep this AFTER the key filter above + + if ( meta.hasClass( 'cannot-expand' ) ) { + return; + } + + var content = meta.find( '.accordion-section-content:first' ); + if ( meta.hasClass( 'open' ) ) { + meta.toggleClass( 'open' ); + content.slideUp( panel.defaultExpandedArguments.duration ); + } else { + content.slideDown( panel.defaultExpandedArguments.duration ); + meta.toggleClass( 'open' ); + } + }); + + }, + + /** + * Get the sections that are associated with this panel, sorted by their priority Value. + * + * @since 4.1.0 + * + * @returns {Array} + */ + sections: function () { + return this._children( 'panel', 'section' ); + }, + + /** + * Return whether this panel has any active sections. + * + * @since 4.1.0 + * + * @returns {boolean} + */ + isContextuallyActive: function () { + var panel = this, + sections = panel.sections(), + activeCount = 0; + _( sections ).each( function ( section ) { + if ( section.active() && section.isContextuallyActive() ) { + activeCount += 1; + } + } ); + return ( activeCount !== 0 ); + }, + + /** + * Update UI to reflect expanded state + * + * @since 4.1.0 + * + * @param {Boolean} expanded + * @param {Object} args + * @param {Boolean} args.unchanged + * @param {Callback} args.completeCallback + */ + onChangeExpanded: function ( expanded, args ) { + + // Immediately call the complete callback if there were no changes + if ( args.unchanged ) { + if ( args.completeCallback ) { + args.completeCallback(); + } + return; + } + + // Note: there is a second argument 'args' passed + var position, scroll, + panel = this, + section = panel.container.closest( '.accordion-section' ), + overlay = section.closest( '.wp-full-overlay' ), + container = section.closest( '.accordion-container' ), + siblings = container.find( '.open' ), + topPanel = overlay.find( '#customize-theme-controls > ul > .accordion-section > .accordion-section-title' ).add( '#customize-info > .accordion-section-title' ), + backBtn = overlay.find( '.control-panel-back' ), + panelTitle = section.find( '.accordion-section-title' ).first(), + content = section.find( '.control-panel-content' ); + + if ( expanded ) { + + // Collapse any sibling sections/panels + api.section.each( function ( section ) { + if ( ! section.panel() ) { + section.collapse( { duration: 0 } ); + } + }); + api.panel.each( function ( otherPanel ) { + if ( panel !== otherPanel ) { + otherPanel.collapse( { duration: 0 } ); + } + }); + + content.show( 0, function() { + position = content.offset().top; + scroll = container.scrollTop(); + content.css( 'margin-top', ( 45 - position - scroll ) ); + section.addClass( 'current-panel' ); + overlay.addClass( 'in-sub-panel' ); + container.scrollTop( 0 ); + if ( args.completeCallback ) { + args.completeCallback(); + } + } ); + topPanel.attr( 'tabindex', '-1' ); + backBtn.attr( 'tabindex', '0' ); + backBtn.focus(); + } else { + siblings.removeClass( 'open' ); + section.removeClass( 'current-panel' ); + overlay.removeClass( 'in-sub-panel' ); + content.delay( 180 ).hide( 0, function() { + content.css( 'margin-top', 'inherit' ); // Reset + if ( args.completeCallback ) { + args.completeCallback(); + } + } ); + topPanel.attr( 'tabindex', '0' ); + backBtn.attr( 'tabindex', '-1' ); + panelTitle.focus(); + container.scrollTop( 0 ); + } + } + }); + + /** + * A Customizer Control. + * + * A control provides a UI element that allows a user to modify a Customizer Setting. + * + * @see PHP class WP_Customize_Control. + * + * @class + * @augments wp.customize.Class + * + * @param {string} id Unique identifier for the control instance. + * @param {object} options Options hash for the control instance. + * @param {object} options.params + * @param {object} options.params.type Type of control (e.g. text, radio, dropdown-pages, etc.) + * @param {string} options.params.content The HTML content for the control. + * @param {string} options.params.priority Order of priority to show the control within the section. + * @param {string} options.params.active + * @param {string} options.params.section + * @param {string} options.params.label + * @param {string} options.params.description + * @param {string} options.params.instanceNumber Order in which this instance was created in relation to other instances. + */ + api.Control = api.Class.extend({ + defaultActiveArguments: { duration: 'fast', completeCallback: $.noop }, + + initialize: function( id, options ) { + var control = this, + nodes, radios, settings; + + control.params = {}; + $.extend( control, options || {} ); + control.id = id; + control.selector = '#customize-control-' + id.replace( /\]/g, '' ).replace( /\[/g, '-' ); + control.templateSelector = 'customize-control-' + control.params.type + '-content'; + control.container = control.params.content ? $( control.params.content ) : $( control.selector ); + + control.deferred = { + embedded: new $.Deferred() + }; + control.section = new api.Value(); + control.priority = new api.Value(); + control.active = new api.Value(); + control.activeArgumentsQueue = []; + + control.elements = []; + + nodes = control.container.find('[data-customize-setting-link]'); + radios = {}; + + nodes.each( function() { + var node = $( this ), + name; + + if ( node.is( ':radio' ) ) { + name = node.prop( 'name' ); + if ( radios[ name ] ) { + return; + } + + radios[ name ] = true; + node = nodes.filter( '[name="' + name + '"]' ); + } + + api( node.data( 'customizeSettingLink' ), function( setting ) { + var element = new api.Element( node ); + control.elements.push( element ); + element.sync( setting ); + element.set( setting() ); + }); + }); + + control.active.bind( function ( active ) { + var args = control.activeArgumentsQueue.shift(); + args = $.extend( {}, control.defaultActiveArguments, args ); + control.onChangeActive( active, args ); + } ); + + control.section.set( control.params.section ); + control.priority.set( isNaN( control.params.priority ) ? 10 : control.params.priority ); + control.active.set( control.params.active ); + + api.utils.bubbleChildValueChanges( control, [ 'section', 'priority', 'active' ] ); + + // Associate this control with its settings when they are created + settings = $.map( control.params.settings, function( value ) { + return value; + }); + api.apply( api, settings.concat( function () { + var key; + + control.settings = {}; + for ( key in control.params.settings ) { + control.settings[ key ] = api( control.params.settings[ key ] ); + } + + control.setting = control.settings['default'] || null; + + control.embed(); + }) ); + + control.deferred.embedded.done( function () { + control.ready(); + }); + }, + + /** + * Embed the control into the page. + */ + embed: function () { + var control = this, + inject; + + // Watch for changes to the section state + inject = function ( sectionId ) { + var parentContainer; + if ( ! sectionId ) { // @todo allow a control to be embedded without a section, for instance a control embedded in the frontend + return; + } + // Wait for the section to be registered + api.section( sectionId, function ( section ) { + // Wait for the section to be ready/initialized + section.deferred.embedded.done( function () { + parentContainer = section.container.find( 'ul:first' ); + if ( ! control.container.parent().is( parentContainer ) ) { + parentContainer.append( control.container ); + control.renderContent(); + } + control.deferred.embedded.resolve(); + }); + }); + }; + control.section.bind( inject ); + inject( control.section.get() ); + }, + + /** + * Triggered when the control's markup has been injected into the DOM. + * + * @abstract + */ + ready: function() {}, + + /** + * Normal controls do not expand, so just expand its parent + * + * @param {Object} [params] + */ + expand: function ( params ) { + api.section( this.section() ).expand( params ); + }, + + /** + * Bring the containing section and panel into view and then + * this control into view, focusing on the first input. + */ + focus: focus, + + /** + * Update UI in response to a change in the control's active state. + * This does not change the active state, it merely handles the behavior + * for when it does change. + * + * @since 4.1.0 + * + * @param {Boolean} active + * @param {Object} args + * @param {Number} args.duration + * @param {Callback} args.completeCallback + */ + onChangeActive: function ( active, args ) { + if ( ! $.contains( document, this.container ) ) { + // jQuery.fn.slideUp is not hiding an element if it is not in the DOM + this.container.toggle( active ); + if ( args.completeCallback ) { + args.completeCallback(); + } + } else if ( active ) { + this.container.slideDown( args.duration, args.completeCallback ); + } else { + this.container.slideUp( args.duration, args.completeCallback ); + } + }, + + /** + * @deprecated 4.1.0 Use this.onChangeActive() instead. + */ + toggle: function ( active ) { + return this.onChangeActive( active, this.defaultActiveArguments ); + }, + + /** + * Shorthand way to enable the active state. + * + * @since 4.1.0 + * + * @param {Object} [params] + * @returns {Boolean} false if already active + */ + activate: Container.prototype.activate, + + /** + * Shorthand way to disable the active state. + * + * @since 4.1.0 + * + * @param {Object} [params] + * @returns {Boolean} false if already inactive + */ + deactivate: Container.prototype.deactivate, + + /** + * Re-use _toggleActive from Container class. + * + * @access private + */ + _toggleActive: Container.prototype._toggleActive, + + dropdownInit: function() { + var control = this, + statuses = this.container.find('.dropdown-status'), + params = this.params, + toggleFreeze = false, + update = function( to ) { + if ( typeof to === 'string' && params.statuses && params.statuses[ to ] ) + statuses.html( params.statuses[ to ] ).show(); + else + statuses.hide(); + }; + + // Support the .dropdown class to open/close complex elements + this.container.on( 'click keydown', '.dropdown', function( event ) { + if ( api.utils.isKeydownButNotEnterEvent( event ) ) { + return; + } + + event.preventDefault(); + + if (!toggleFreeze) + control.container.toggleClass('open'); + + if ( control.container.hasClass('open') ) + control.container.parent().parent().find('li.library-selected').focus(); + + // Don't want to fire focus and click at same time + toggleFreeze = true; + setTimeout(function () { + toggleFreeze = false; + }, 400); + }); + + this.setting.bind( update ); + update( this.setting() ); + }, + + /** + * Render the control from its JS template, if it exists. + * + * The control's container must already exist in the DOM. + * + * @since 4.1.0 + */ + renderContent: function () { + var template, + control = this; + + // Replace the container element's content with the control. + if ( 0 !== $( '#tmpl-' + control.templateSelector ).length ) { + template = wp.template( control.templateSelector ); + if ( template && control.container ) { + control.container.html( template( control.params ) ); + } + } + } + }); + + /** + * A colorpicker control. + * + * @class + * @augments wp.customize.Control + * @augments wp.customize.Class + */ + api.ColorControl = api.Control.extend({ + ready: function() { + var control = this, + picker = this.container.find('.color-picker-hex'); + + picker.val( control.setting() ).wpColorPicker({ + change: function() { + control.setting.set( picker.wpColorPicker('color') ); + }, + clear: function() { + control.setting.set( false ); + } + }); + + this.setting.bind( function ( value ) { + picker.val( value ); + picker.wpColorPicker( 'color', value ); + }); + } + }); + + /** + * An upload control, which utilizes the media modal. + * + * @class + * @augments wp.customize.Control + * @augments wp.customize.Class + */ + api.UploadControl = api.Control.extend({ + + /** + * When the control's DOM structure is ready, + * set up internal event bindings. + */ + ready: function() { + var control = this; + // Shortcut so that we don't have to use _.bind every time we add a callback. + _.bindAll( control, 'restoreDefault', 'removeFile', 'openFrame', 'select' ); + + // Bind events, with delegation to facilitate re-rendering. + control.container.on( 'click keydown', '.upload-button', control.openFrame ); + control.container.on( 'click keydown', '.thumbnail-image img', control.openFrame ); + control.container.on( 'click keydown', '.default-button', control.restoreDefault ); + control.container.on( 'click keydown', '.remove-button', control.removeFile ); + + // Re-render whenever the control's setting changes. + control.setting.bind( function () { control.renderContent(); } ); + }, + + /** + * Open the media modal. + */ + openFrame: function( event ) { + if ( api.utils.isKeydownButNotEnterEvent( event ) ) { + return; + } + + event.preventDefault(); + + if ( ! this.frame ) { + this.initFrame(); + } + + this.frame.open(); + }, + + /** + * Create a media modal select frame, and store it so the instance can be reused when needed. + */ + initFrame: function() { + this.frame = wp.media({ + button: { + text: this.params.button_labels.frame_button + }, + states: [ + new wp.media.controller.Library({ + title: this.params.button_labels.frame_title, + library: wp.media.query({ type: this.params.mime_type }), + multiple: false, + date: false + }) + ] + }); + + // When a file is selected, run a callback. + this.frame.on( 'select', this.select ); + }, + + /** + * Callback handler for when an attachment is selected in the media modal. + * Gets the selected image information, and sets it within the control. + */ + select: function() { + // Get the attachment from the modal frame. + var attachment = this.frame.state().get( 'selection' ).first().toJSON(); + + this.params.attachment = attachment; + + // Set the Customizer setting; the callback takes care of rendering. + this.setting( attachment.url ); + }, + + /** + * Reset the setting to the default value. + */ + restoreDefault: function( event ) { + if ( api.utils.isKeydownButNotEnterEvent( event ) ) { + return; + } + event.preventDefault(); + + this.params.attachment = this.params.defaultAttachment; + this.setting( this.params.defaultAttachment.url ); + }, + + /** + * Called when the "Remove" link is clicked. Empties the setting. + * + * @param {object} event jQuery Event object + */ + removeFile: function( event ) { + if ( api.utils.isKeydownButNotEnterEvent( event ) ) { + return; + } + event.preventDefault(); + + this.params.attachment = {}; + this.setting( '' ); + this.renderContent(); // Not bound to setting change when emptying. + }, + + // @deprecated + success: function() {}, + + // @deprecated + removerVisibility: function() {} + }); + + /** + * A control for uploading images. + * + * This control no longer needs to do anything more + * than what the upload control does in JS. + * + * @class + * @augments wp.customize.UploadControl + * @augments wp.customize.Control + * @augments wp.customize.Class + */ + api.ImageControl = api.UploadControl.extend({ + // @deprecated + thumbnailSrc: function() {} + }); + + /** + * A control for uploading background images. + * + * @class + * @augments wp.customize.UploadControl + * @augments wp.customize.Control + * @augments wp.customize.Class + */ + api.BackgroundControl = api.UploadControl.extend({ + + /** + * When the control's DOM structure is ready, + * set up internal event bindings. + */ + ready: function() { + api.UploadControl.prototype.ready.apply( this, arguments ); + }, + + /** + * Callback handler for when an attachment is selected in the media modal. + * Does an additional AJAX request for setting the background context. + */ + select: function() { + api.UploadControl.prototype.select.apply( this, arguments ); + + wp.ajax.post( 'custom-background-add', { + nonce: _wpCustomizeBackground.nonces.add, + wp_customize: 'on', + theme: api.settings.theme.stylesheet, + attachment_id: this.params.attachment.id + } ); + } + }); + + /** + * @class + * @augments wp.customize.Control + * @augments wp.customize.Class + */ + api.HeaderControl = api.Control.extend({ + ready: function() { + this.btnRemove = $('#customize-control-header_image .actions .remove'); + this.btnNew = $('#customize-control-header_image .actions .new'); + + _.bindAll(this, 'openMedia', 'removeImage'); + + this.btnNew.on( 'click', this.openMedia ); + this.btnRemove.on( 'click', this.removeImage ); + + api.HeaderTool.currentHeader = new api.HeaderTool.ImageModel(); + + new api.HeaderTool.CurrentView({ + model: api.HeaderTool.currentHeader, + el: '#customize-control-header_image .current .container' + }); + + new api.HeaderTool.ChoiceListView({ + collection: api.HeaderTool.UploadsList = new api.HeaderTool.ChoiceList(), + el: '#customize-control-header_image .choices .uploaded .list' + }); + + new api.HeaderTool.ChoiceListView({ + collection: api.HeaderTool.DefaultsList = new api.HeaderTool.DefaultsList(), + el: '#customize-control-header_image .choices .default .list' + }); + + api.HeaderTool.combinedList = api.HeaderTool.CombinedList = new api.HeaderTool.CombinedList([ + api.HeaderTool.UploadsList, + api.HeaderTool.DefaultsList + ]); + }, + + /** + * Returns a set of options, computed from the attached image data and + * theme-specific data, to be fed to the imgAreaSelect plugin in + * wp.media.view.Cropper. + * + * @param {wp.media.model.Attachment} attachment + * @param {wp.media.controller.Cropper} controller + * @returns {Object} Options + */ + calculateImageSelectOptions: function(attachment, controller) { + var xInit = parseInt(_wpCustomizeHeader.data.width, 10), + yInit = parseInt(_wpCustomizeHeader.data.height, 10), + flexWidth = !! parseInt(_wpCustomizeHeader.data['flex-width'], 10), + flexHeight = !! parseInt(_wpCustomizeHeader.data['flex-height'], 10), + ratio, xImg, yImg, realHeight, realWidth, + imgSelectOptions; + + realWidth = attachment.get('width'); + realHeight = attachment.get('height'); + + this.headerImage = new api.HeaderTool.ImageModel(); + this.headerImage.set({ + themeWidth: xInit, + themeHeight: yInit, + themeFlexWidth: flexWidth, + themeFlexHeight: flexHeight, + imageWidth: realWidth, + imageHeight: realHeight + }); + + controller.set( 'canSkipCrop', ! this.headerImage.shouldBeCropped() ); + + ratio = xInit / yInit; + xImg = realWidth; + yImg = realHeight; + + if ( xImg / yImg > ratio ) { + yInit = yImg; + xInit = yInit * ratio; + } else { + xInit = xImg; + yInit = xInit / ratio; + } + + imgSelectOptions = { + handles: true, + keys: true, + instance: true, + persistent: true, + imageWidth: realWidth, + imageHeight: realHeight, + x1: 0, + y1: 0, + x2: xInit, + y2: yInit + }; + + if (flexHeight === false && flexWidth === false) { + imgSelectOptions.aspectRatio = xInit + ':' + yInit; + } + if (flexHeight === false ) { + imgSelectOptions.maxHeight = yInit; + } + if (flexWidth === false ) { + imgSelectOptions.maxWidth = xInit; + } + + return imgSelectOptions; + }, + + /** + * Sets up and opens the Media Manager in order to select an image. + * Depending on both the size of the image and the properties of the + * current theme, a cropping step after selection may be required or + * skippable. + * + * @param {event} event + */ + openMedia: function(event) { + var l10n = _wpMediaViewsL10n; + + event.preventDefault(); + + this.frame = wp.media({ + button: { + text: l10n.selectAndCrop, + close: false + }, + states: [ + new wp.media.controller.Library({ + title: l10n.chooseImage, + library: wp.media.query({ type: 'image' }), + multiple: false, + date: false, + priority: 20, + suggestedWidth: _wpCustomizeHeader.data.width, + suggestedHeight: _wpCustomizeHeader.data.height + }), + new wp.media.controller.Cropper({ + imgSelectOptions: this.calculateImageSelectOptions + }) + ] + }); + + this.frame.on('select', this.onSelect, this); + this.frame.on('cropped', this.onCropped, this); + this.frame.on('skippedcrop', this.onSkippedCrop, this); + + this.frame.open(); + }, + + /** + * After an image is selected in the media modal, + * switch to the cropper state. + */ + onSelect: function() { + this.frame.setState('cropper'); + }, + + /** + * After the image has been cropped, apply the cropped image data to the setting. + * + * @param {object} croppedImage Cropped attachment data. + */ + onCropped: function(croppedImage) { + var url = croppedImage.post_content, + attachmentId = croppedImage.attachment_id, + w = croppedImage.width, + h = croppedImage.height; + this.setImageFromURL(url, attachmentId, w, h); + }, + + /** + * If cropping was skipped, apply the image data directly to the setting. + * + * @param {object} selection + */ + onSkippedCrop: function(selection) { + var url = selection.get('url'), + w = selection.get('width'), + h = selection.get('height'); + this.setImageFromURL(url, selection.id, w, h); + }, + + /** + * Creates a new wp.customize.HeaderTool.ImageModel from provided + * header image data and inserts it into the user-uploaded headers + * collection. + * + * @param {String} url + * @param {Number} attachmentId + * @param {Number} width + * @param {Number} height + */ + setImageFromURL: function(url, attachmentId, width, height) { + var choice, data = {}; + + data.url = url; + data.thumbnail_url = url; + data.timestamp = _.now(); + + if (attachmentId) { + data.attachment_id = attachmentId; + } + + if (width) { + data.width = width; + } + + if (height) { + data.height = height; + } + + choice = new api.HeaderTool.ImageModel({ + header: data, + choice: url.split('/').pop() + }); + api.HeaderTool.UploadsList.add(choice); + api.HeaderTool.currentHeader.set(choice.toJSON()); + choice.save(); + choice.importImage(); + }, + + /** + * Triggers the necessary events to deselect an image which was set as + * the currently selected one. + */ + removeImage: function() { + api.HeaderTool.currentHeader.trigger('hide'); + api.HeaderTool.CombinedList.trigger('control:removeImage'); + } + + }); + + // Change objects contained within the main customize object to Settings. + api.defaultConstructor = api.Setting; + + // Create the collections for Controls, Sections and Panels. + api.control = new api.Values({ defaultConstructor: api.Control }); + api.section = new api.Values({ defaultConstructor: api.Section }); + api.panel = new api.Values({ defaultConstructor: api.Panel }); + + /** + * @class + * @augments wp.customize.Messenger + * @augments wp.customize.Class + * @mixes wp.customize.Events + */ + api.PreviewFrame = api.Messenger.extend({ + sensitivity: 2000, + + initialize: function( params, options ) { + var deferred = $.Deferred(); + + // This is the promise object. + deferred.promise( this ); + + this.container = params.container; + this.signature = params.signature; + + $.extend( params, { channel: api.PreviewFrame.uuid() }); + + api.Messenger.prototype.initialize.call( this, params, options ); + + this.add( 'previewUrl', params.previewUrl ); + + this.query = $.extend( params.query || {}, { customize_messenger_channel: this.channel() }); + + this.run( deferred ); + }, + + run: function( deferred ) { + var self = this, + loaded = false, + ready = false; + + if ( this._ready ) { + this.unbind( 'ready', this._ready ); + } + + this._ready = function() { + ready = true; + + if ( loaded ) { + deferred.resolveWith( self ); + } + }; + + this.bind( 'ready', this._ready ); + + this.bind( 'ready', function ( data ) { + if ( ! data ) { + return; + } + + /* + * Walk over all panels, sections, and controls and set their + * respective active states to true if the preview explicitly + * indicates as such. + */ + var constructs = { + panel: data.activePanels, + section: data.activeSections, + control: data.activeControls + }; + _( constructs ).each( function ( activeConstructs, type ) { + api[ type ].each( function ( construct, id ) { + var active = !! ( activeConstructs && activeConstructs[ id ] ); + construct.active( active ); + } ); + } ); + } ); + + this.request = $.ajax( this.previewUrl(), { + type: 'POST', + data: this.query, + xhrFields: { + withCredentials: true + } + } ); + + this.request.fail( function() { + deferred.rejectWith( self, [ 'request failure' ] ); + }); + + this.request.done( function( response ) { + var location = self.request.getResponseHeader('Location'), + signature = self.signature, + index; + + // Check if the location response header differs from the current URL. + // If so, the request was redirected; try loading the requested page. + if ( location && location !== self.previewUrl() ) { + deferred.rejectWith( self, [ 'redirect', location ] ); + return; + } + + // Check if the user is not logged in. + if ( '0' === response ) { + self.login( deferred ); + return; + } + + // Check for cheaters. + if ( '-1' === response ) { + deferred.rejectWith( self, [ 'cheatin' ] ); + return; + } + + // Check for a signature in the request. + index = response.lastIndexOf( signature ); + if ( -1 === index || index < response.lastIndexOf('') ) { + deferred.rejectWith( self, [ 'unsigned' ] ); + return; + } + + // Strip the signature from the request. + response = response.slice( 0, index ) + response.slice( index + signature.length ); + + // Create the iframe and inject the html content. + self.iframe = $('"; + echo '
    '; + require_once(ABSPATH . 'wp-admin/admin-footer.php'); + exit; + case 'delete-selected': + if ( ! current_user_can( 'delete_themes' ) ) { + wp_die( __('You do not have sufficient permissions to delete themes for this site.') ); + } + + check_admin_referer( 'bulk-themes' ); + + $themes = isset( $_REQUEST['checked'] ) ? (array) $_REQUEST['checked'] : array(); + + if ( empty( $themes ) ) { + wp_safe_redirect( add_query_arg( 'error', 'none', $referer ) ); + exit; + } + + $themes = array_diff( $themes, array( get_option( 'stylesheet' ), get_option( 'template' ) ) ); + + if ( empty( $themes ) ) { + wp_safe_redirect( add_query_arg( 'error', 'main', $referer ) ); + exit; + } + + $files_to_delete = $theme_info = array(); + $theme_translations = wp_get_installed_translations( 'themes' ); + foreach ( $themes as $key => $theme ) { + $theme_info[ $theme ] = wp_get_theme( $theme ); + + // Locate all the files in that folder. + $files = list_files( $theme_info[ $theme ]->get_stylesheet_directory() ); + if ( $files ) { + $files_to_delete = array_merge( $files_to_delete, $files ); + } + + // Add translation files. + $theme_slug = $theme_info[ $theme ]->get_stylesheet(); + if ( ! empty( $theme_translations[ $theme_slug ] ) ) { + $translations = $theme_translations[ $theme_slug ]; + + foreach ( $translations as $translation => $data ) { + $files_to_delete[] = $theme_slug . '-' . $translation . '.po'; + $files_to_delete[] = $theme_slug . '-' . $translation . '.mo'; + } + } + } + + include(ABSPATH . 'wp-admin/update.php'); + + $parent_file = 'themes.php'; + + if ( ! isset( $_REQUEST['verify-delete'] ) ) { + wp_enqueue_script( 'jquery' ); + require_once( ABSPATH . 'wp-admin/admin-header.php' ); + ?> +
    + 1, + 'action' => 'delete-selected', + 'checked' => $_REQUEST['checked'], + '_wpnonce' => $_REQUEST['_wpnonce'] + ), network_admin_url( 'themes.php' ) ) ) ); + } + + $paged = ( $_REQUEST['paged'] ) ? $_REQUEST['paged'] : 1; + wp_redirect( add_query_arg( array( + 'deleted' => count( $themes ), + 'paged' => $paged, + 's' => $s + ), network_admin_url( 'themes.php' ) ) ); + exit; + } +} + +$wp_list_table->prepare_items(); + +add_thickbox(); + +add_screen_option( 'per_page', array('label' => _x( 'Themes', 'themes per page (screen options)' )) ); + +get_current_screen()->add_help_tab( array( + 'id' => 'overview', + 'title' => __('Overview'), + 'content' => + '

    ' . __('This screen enables and disables the inclusion of themes available to choose in the Appearance menu for each site. It does not activate or deactivate which theme a site is currently using.') . '

    ' . + '

    ' . __('If the network admin disables a theme that is in use, it can still remain selected on that site. If another theme is chosen, the disabled theme will not appear in the site’s Appearance > Themes screen.') . '

    ' . + '

    ' . __('Themes can be enabled on a site by site basis by the network admin on the Edit Site screen (which has a Themes tab); get there via the Edit action link on the All Sites screen. Only network admins are able to install or edit themes.') . '

    ' +) ); + +get_current_screen()->set_help_sidebar( + '

    ' . __('For more information:') . '

    ' . + '

    ' . __('Documentation on Network Themes') . '

    ' . + '

    ' . __('Support Forums') . '

    ' +); + +$title = __('Themes'); +$parent_file = 'themes.php'; + +wp_enqueue_script( 'theme-preview' ); + +require_once(ABSPATH . 'wp-admin/admin-header.php'); + +?> + +
    +

    ' . __('Search results for “%s”') . '', esc_html( $s ) ); ?> +

    + +

    ' . sprintf( _n( 'Theme enabled.', '%s themes enabled.', $_GET['enabled'] ), number_format_i18n( $_GET['enabled'] ) ) . '

    '; +} elseif ( isset( $_GET['disabled'] ) ) { + $_GET['disabled'] = absint( $_GET['disabled'] ); + echo '

    ' . sprintf( _n( 'Theme disabled.', '%s themes disabled.', $_GET['disabled'] ), number_format_i18n( $_GET['disabled'] ) ) . '

    '; +} elseif ( isset( $_GET['deleted'] ) ) { + $_GET['deleted'] = absint( $_GET['deleted'] ); + echo '

    ' . sprintf( _nx( 'Theme deleted.', '%s themes deleted.', $_GET['deleted'], 'network' ), number_format_i18n( $_GET['deleted'] ) ) . '

    '; +} elseif ( isset( $_GET['error'] ) && 'none' == $_GET['error'] ) { + echo '

    ' . __( 'No theme selected.' ) . '

    '; +} elseif ( isset( $_GET['error'] ) && 'main' == $_GET['error'] ) { + echo '

    ' . __( 'You cannot delete a theme while it is active on the main site.' ) . '

    '; +} + +?> + +
    +search_box( __( 'Search Installed Themes' ), 'theme' ); ?> +
    + +views(); + +if ( 'broken' == $status ) + echo '

    ' . __('The following themes are installed but incomplete. Themes must have a stylesheet and a template.') . '

    '; +?> + +
    + + + +display(); ?> +
    + +
    + +add_help_tab( array( + 'id' => 'overview', + 'title' => __('Overview'), + 'content' => + '

    ' . __('Only use this screen once you have updated to a new version of WordPress through Updates/Available Updates (via the Network Administration navigation menu or the Toolbar). Clicking the Upgrade Network button will step through each site in the network, five at a time, and make sure any database updates are applied.') . '

    ' . + '

    ' . __('If a version update to core has not happened, clicking this button won’t affect anything.') . '

    ' . + '

    ' . __('If this process fails for any reason, users logging in to their sites will force the same update.') . '

    ' +) ); + +get_current_screen()->set_help_sidebar( + '

    ' . __('For more information:') . '

    ' . + '

    ' . __('Documentation on Upgrade Network') . '

    ' . + '

    ' . __('Support Forums') . '

    ' +); + +require_once( ABSPATH . 'wp-admin/admin-header.php' ); + +if ( ! current_user_can( 'manage_network' ) ) + wp_die( __( 'You do not have permission to access this page.' ) ); + +echo '
    '; +echo '

    ' . __( 'Upgrade Network' ) . '

    '; + +$action = isset($_GET['action']) ? $_GET['action'] : 'show'; + +switch ( $action ) { + case "upgrade": + $n = ( isset($_GET['n']) ) ? intval($_GET['n']) : 0; + + if ( $n < 5 ) { + global $wp_db_version; + update_site_option( 'wpmu_upgrade_site', $wp_db_version ); + } + + $blogs = $wpdb->get_results( "SELECT blog_id FROM {$wpdb->blogs} WHERE site_id = '{$wpdb->siteid}' AND spam = '0' AND deleted = '0' AND archived = '0' ORDER BY registered DESC LIMIT {$n}, 5", ARRAY_A ); + if ( empty( $blogs ) ) { + echo '

    ' . __( 'All done!' ) . '

    '; + break; + } + echo "
      "; + foreach ( (array) $blogs as $details ) { + switch_to_blog( $details['blog_id'] ); + $siteurl = site_url(); + $upgrade_url = admin_url( 'upgrade.php?step=upgrade_db' ); + restore_current_blog(); + echo "
    • $siteurl
    • "; + $response = wp_remote_get( $upgrade_url, array( 'timeout' => 120, 'httpversion' => '1.1' ) ); + if ( is_wp_error( $response ) ) + wp_die( sprintf( __( 'Warning! Problem updating %1$s. Your server may not be able to connect to sites running on it. Error message: %2$s' ), $siteurl, $response->get_error_message() ) ); + /** + * Fires after the Multisite DB upgrade for each site is complete. + * + * @since MU + * + * @param array|WP_Error $response The upgrade response array or WP_Error on failure. + */ + do_action( 'after_mu_upgrade', $response ); + /** + * Fires after each site has been upgraded. + * + * @since MU + * + * @param int $blog_id The id of the blog. + */ + do_action( 'wpmu_upgrade_site', $details[ 'blog_id' ] ); + } + echo "
    "; + ?>

    + +

    +

    + + +

    +

    + +
    + + diff --git a/wp-admin/network/user-edit.php b/wp-admin/network/user-edit.php new file mode 100644 index 0000000..effaaf8 --- /dev/null +++ b/wp-admin/network/user-edit.php @@ -0,0 +1,16 @@ +add_help_tab( array( + 'id' => 'overview', + 'title' => __('Overview'), + 'content' => + '

    ' . __('Add User will set up a new user account on the network and send that person an email with username and password.') . '

    ' . + '

    ' . __('Users who are signed up to the network without a site are added as subscribers to the main or primary dashboard site, giving them profile pages to manage their accounts. These users will only see Dashboard and My Sites in the main navigation until a site is created for them.') . '

    ' +) ); + +get_current_screen()->set_help_sidebar( + '

    ' . __('For more information:') . '

    ' . + '

    ' . __('Documentation on Network Users') . '

    ' . + '

    ' . __('Support Forums') . '

    ' +); + +if ( isset($_REQUEST['action']) && 'add-user' == $_REQUEST['action'] ) { + check_admin_referer( 'add-user', '_wpnonce_add-user' ); + if ( ! current_user_can( 'manage_network_users' ) ) + wp_die( __( 'You do not have permission to access this page.' ) ); + + if ( ! is_array( $_POST['user'] ) ) + wp_die( __( 'Cannot create an empty user.' ) ); + + $user = wp_unslash( $_POST['user'] ); + + $user_details = wpmu_validate_user_signup( $user['username'], $user['email'] ); + if ( is_wp_error( $user_details[ 'errors' ] ) && ! empty( $user_details[ 'errors' ]->errors ) ) { + $add_user_errors = $user_details[ 'errors' ]; + } else { + $password = wp_generate_password( 12, false); + $user_id = wpmu_create_user( esc_html( strtolower( $user['username'] ) ), $password, sanitize_email( $user['email'] ) ); + + if ( ! $user_id ) { + $add_user_errors = new WP_Error( 'add_user_fail', __( 'Cannot add user.' ) ); + } else { + wp_new_user_notification( $user_id, $password ); + wp_redirect( add_query_arg( array('update' => 'added'), 'user-new.php' ) ); + exit; + } + } +} + +if ( isset($_GET['update']) ) { + $messages = array(); + if ( 'added' == $_GET['update'] ) + $messages[] = __('User added.'); +} + +$title = __('Add New User'); +$parent_file = 'users.php'; + +require( ABSPATH . 'wp-admin/admin-header.php' ); ?> + +
    +

    +

    ' . $msg . '

    '; +} + +if ( isset( $add_user_errors ) && is_wp_error( $add_user_errors ) ) { ?> +
    + get_error_messages() as $message ) + echo "

    $message

    "; + ?> +
    + +
    + + + + + + + + + + + + +
    + + +
    +
    + +

    +

    +
    + + ID . '">' . $current_user->user_login . ''; + + foreach ( ( $allusers = (array) $_POST['allusers'] ) as $user_id ) { + if ( $user_id != '' && $user_id != '0' ) { + $delete_user = get_userdata( $user_id ); + + if ( ! current_user_can( 'delete_user', $delete_user->ID ) ) + wp_die( sprintf( __( 'Warning! User %s cannot be deleted.' ), $delete_user->user_login ) ); + + if ( in_array( $delete_user->user_login, $site_admins ) ) + wp_die( sprintf( __( 'Warning! User cannot be deleted. The user %s is a network administrator.' ), $delete_user->user_login ) ); + + echo "\n"; + $blogs = get_blogs_of_user( $user_id, true ); + + if ( !empty( $blogs ) ) { + ?> +

    ' . $delete_user->user_login . '' ); ?>

    + $details ) { + $blog_users = get_users( array( 'blog_id' => $details->userblog_id, 'fields' => array( 'ID', 'user_login' ) ) ); + if ( is_array( $blog_users ) && !empty( $blog_users ) ) { + $user_site = "{$details->blogname}"; + $user_dropdown = "\n"; + ?> +
      +
    • +
    • +
    • +
    + "; + } + } + } + + /** This action is documented in wp-admin/users.php */ + do_action( 'delete_user_form', $current_user ); + + submit_button( __('Confirm Deletion'), 'delete' ); + ?> + + '; + confirm_delete_users( $_POST['allusers'] ); + echo '
    '; + require_once( ABSPATH . 'wp-admin/admin-footer.php' ); + } else { + wp_redirect( network_admin_url( 'users.php' ) ); + } + exit(); + + case 'allusers': + if ( !current_user_can( 'manage_network_users' ) ) + wp_die( __( 'You do not have permission to access this page.' ) ); + + if ( ( isset( $_POST['action']) || isset($_POST['action2'] ) ) && isset( $_POST['allusers'] ) ) { + check_admin_referer( 'bulk-users-network' ); + + $doaction = $_POST['action'] != -1 ? $_POST['action'] : $_POST['action2']; + $userfunction = ''; + + foreach ( (array) $_POST['allusers'] as $user_id ) { + if ( !empty( $user_id ) ) { + switch ( $doaction ) { + case 'delete': + if ( ! current_user_can( 'delete_users' ) ) + wp_die( __( 'You do not have permission to access this page.' ) ); + $title = __( 'Users' ); + $parent_file = 'users.php'; + require_once( ABSPATH . 'wp-admin/admin-header.php' ); + echo '
    '; + confirm_delete_users( $_POST['allusers'] ); + echo '
    '; + require_once( ABSPATH . 'wp-admin/admin-footer.php' ); + exit(); + + case 'spam': + $user = get_userdata( $user_id ); + if ( is_super_admin( $user->ID ) ) + wp_die( sprintf( __( 'Warning! User cannot be modified. The user %s is a network administrator.' ), esc_html( $user->user_login ) ) ); + + $userfunction = 'all_spam'; + $blogs = get_blogs_of_user( $user_id, true ); + foreach ( (array) $blogs as $details ) { + if ( $details->userblog_id != $current_site->blog_id ) // main blog not a spam ! + update_blog_status( $details->userblog_id, 'spam', '1' ); + } + update_user_status( $user_id, 'spam', '1' ); + break; + + case 'notspam': + $userfunction = 'all_notspam'; + $blogs = get_blogs_of_user( $user_id, true ); + foreach ( (array) $blogs as $details ) + update_blog_status( $details->userblog_id, 'spam', '0' ); + + update_user_status( $user_id, 'spam', '0' ); + break; + } + } + } + + wp_safe_redirect( add_query_arg( array( 'updated' => 'true', 'action' => $userfunction ), wp_get_referer() ) ); + } else { + $location = network_admin_url( 'users.php' ); + + if ( ! empty( $_REQUEST['paged'] ) ) + $location = add_query_arg( 'paged', (int) $_REQUEST['paged'], $location ); + wp_redirect( $location ); + } + exit(); + + case 'dodelete': + check_admin_referer( 'ms-users-delete' ); + if ( ! ( current_user_can( 'manage_network_users' ) && current_user_can( 'delete_users' ) ) ) + wp_die( __( 'You do not have permission to access this page.' ) ); + + if ( ! empty( $_POST['blog'] ) && is_array( $_POST['blog'] ) ) { + foreach ( $_POST['blog'] as $id => $users ) { + foreach ( $users as $blogid => $user_id ) { + if ( ! current_user_can( 'delete_user', $id ) ) + continue; + + if ( ! empty( $_POST['delete'] ) && 'reassign' == $_POST['delete'][$blogid][$id] ) + remove_user_from_blog( $id, $blogid, $user_id ); + else + remove_user_from_blog( $id, $blogid ); + } + } + } + $i = 0; + if ( is_array( $_POST['user'] ) && ! empty( $_POST['user'] ) ) + foreach( $_POST['user'] as $id ) { + if ( ! current_user_can( 'delete_user', $id ) ) + continue; + wpmu_delete_user( $id ); + $i++; + } + + if ( $i == 1 ) + $deletefunction = 'delete'; + else + $deletefunction = 'all_delete'; + + wp_redirect( add_query_arg( array( 'updated' => 'true', 'action' => $deletefunction ), network_admin_url( 'users.php' ) ) ); + exit(); + } +} + +$wp_list_table = _get_list_table('WP_MS_Users_List_Table'); +$pagenum = $wp_list_table->get_pagenum(); +$wp_list_table->prepare_items(); +$total_pages = $wp_list_table->get_pagination_arg( 'total_pages' ); + +if ( $pagenum > $total_pages && $total_pages > 0 ) { + wp_redirect( add_query_arg( 'paged', $total_pages ) ); + exit; +} +$title = __( 'Users' ); +$parent_file = 'users.php'; + +add_screen_option( 'per_page', array('label' => _x( 'Users', 'users per page (screen options)' )) ); + +get_current_screen()->add_help_tab( array( + 'id' => 'overview', + 'title' => __('Overview'), + 'content' => + '

    ' . __('This table shows all users across the network and the sites to which they are assigned.') . '

    ' . + '

    ' . __('Hover over any user on the list to make the edit links appear. The Edit link on the left will take you to their Edit User profile page; the Edit link on the right by any site name goes to an Edit Site screen for that site.') . '

    ' . + '

    ' . __('You can also go to the user’s profile page by clicking on the individual username.') . '

    ' . + '

    ' . __('You can sort the table by clicking on any of the bold headings and switch between list and excerpt views by using the icons in the upper right.') . '

    ' . + '

    ' . __('The bulk action will permanently delete selected users, or mark/unmark those selected as spam. Spam users will have posts removed and will be unable to sign up again with the same email addresses.') . '

    ' . + '

    ' . __('You can make an existing user an additional super admin by going to the Edit User profile page and checking the box to grant that privilege.') . '

    ' +) ); + +get_current_screen()->set_help_sidebar( + '

    ' . __('For more information:') . '

    ' . + '

    ' . __('Documentation on Network Users') . '

    ' . + '

    ' . __('Support Forums') . '

    ' +); + +require_once( ABSPATH . 'wp-admin/admin-header.php' ); + +if ( isset( $_REQUEST['updated'] ) && $_REQUEST['updated'] == 'true' && ! empty( $_REQUEST['action'] ) ) { + ?> +

    + +

    + +
    +

    + ' . __( 'Search results for “%s”' ) . '', esc_html( $usersearch ) ); + ?> +

    + + views(); ?> + +
    + search_box( __( 'Search Users' ), 'all-user' ); ?> +
    + +
    + display(); ?> +
    +
    + + diff --git a/wp-admin/options-discussion.php b/wp-admin/options-discussion.php new file mode 100644 index 0000000..c9c8627 --- /dev/null +++ b/wp-admin/options-discussion.php @@ -0,0 +1,273 @@ +add_help_tab( array( + 'id' => 'overview', + 'title' => __('Overview'), + 'content' => '

    ' . __('This screen provides many options for controlling the management and display of comments and links to your posts/pages. So many, in fact, they won’t all fit here! :) Use the documentation links to get information on what each discussion setting does.') . '

    ' . + '

    ' . __('You must click the Save Changes button at the bottom of the screen for new settings to take effect.') . '

    ', +) ); + +get_current_screen()->set_help_sidebar( + '

    ' . __('For more information:') . '

    ' . + '

    ' . __('Documentation on Discussion Settings') . '

    ' . + '

    ' . __('Support Forums') . '

    ' +); + +include( ABSPATH . 'wp-admin/admin-header.php' ); +?> + +
    +

    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + +
    + +
    +

    +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    +

    + +

    +

    + +

    +
    +

    +

    + +

    +
    + +

    + +

    + + + + + + + + + + + + + + + + + +
    + +
    + + __('G — Suitable for all audiences'), + /* translators: Content suitability rating: http://bit.ly/89QxZA */ + 'PG' => __('PG — Possibly offensive, usually for audiences 13 and above'), + /* translators: Content suitability rating: http://bit.ly/89QxZA */ + 'R' => __('R — Intended for adult audiences above 17'), + /* translators: Content suitability rating: http://bit.ly/89QxZA */ + 'X' => __('X — Even more mature than above') +); +foreach ($ratings as $key => $rating) : + $selected = (get_option('avatar_rating') == $key) ? 'checked="checked"' : ''; + echo "\n\t
    "; +endforeach; +?> + +
    + +
    + + __('Mystery Person'), + 'blank' => __('Blank'), + 'gravatar_default' => __('Gravatar Logo'), + 'identicon' => __('Identicon (Generated)'), + 'wavatar' => __('Wavatar (Generated)'), + 'monsterid' => __('MonsterID (Generated)'), + 'retro' => __('Retro (Generated)') +); +/** + * Filter the default avatars. + * + * Avatars are stored in key/value pairs, where the key is option value, + * and the name is the displayed avatar name. + * + * @since 2.6.0 + * + * @param array $avatar_defaults Array of default avatars. + */ +$avatar_defaults = apply_filters( 'avatar_defaults', $avatar_defaults ); +$default = get_option('avatar_default'); +if ( empty($default) ) + $default = 'mystery'; +$size = 32; +$avatar_list = ''; +foreach ( $avatar_defaults as $default_key => $default_name ) { + $selected = ($default == $default_key) ? 'checked="checked" ' : ''; + $avatar_list .= "\n\t'; + $avatar_list .= '
    '; +} +/** + * Filter the HTML output of the default avatar list. + * + * @since 2.6.0 + * + * @param string $avatar_list HTML markup of the avatar list. + */ +echo apply_filters( 'default_avatar_select', $avatar_list ); +?> + +
    + + + + +
    +
    + + diff --git a/wp-admin/options-general.php b/wp-admin/options-general.php new file mode 100644 index 0000000..629c4d2 --- /dev/null +++ b/wp-admin/options-general.php @@ -0,0 +1,385 @@ + + +' . __('The fields on this screen determine some of the basics of your site setup.') . '

    ' . + '

    ' . __('Most themes display the site title at the top of every page, in the title bar of the browser, and as the identifying name for syndicated feeds. The tagline is also displayed by many themes.') . '

    '; + +if ( ! is_multisite() ) { + $options_help .= '

    ' . __('The WordPress URL and the Site URL can be the same (example.com) or different; for example, having the WordPress core files (example.com/wordpress) in a subdirectory instead of the root directory.') . '

    ' . + '

    ' . __('If you want site visitors to be able to register themselves, as opposed to by the site administrator, check the membership box. A default user role can be set for all new users, whether self-registered or registered by the site admin.') . '

    '; +} + +$options_help .= '

    ' . __( 'You can set the language, and the translation files will be automatically downloaded and installed (available if your filesystem is writable).' ) . '

    ' . + '

    ' . __( 'UTC means Coordinated Universal Time.' ) . '

    ' . + '

    ' . __( 'You must click the Save Changes button at the bottom of the screen for new settings to take effect.' ) . '

    '; + +get_current_screen()->add_help_tab( array( + 'id' => 'overview', + 'title' => __('Overview'), + 'content' => $options_help, +) ); + +get_current_screen()->set_help_sidebar( + '

    ' . __('For more information:') . '

    ' . + '

    ' . __('Documentation on General Settings') . '

    ' . + '

    ' . __('Support Forums') . '

    ' +); + +include( ABSPATH . 'wp-admin/admin-header.php' ); +?> + +
    +

    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    class="regular-text code" />
    class="regular-text code" /> +

    to be different from the directory you installed WordPress.'); ?>

    +

    +
    + +
    +

    The new address will not become active until confirmed.') ?>

    + +
    +

    %1$s. Cancel'), esc_html( $new_admin_email ), esc_url( admin_url( 'options.php?dismiss=new_admin_email' ) ) ); ?>

    +
    + +
    + + + + UTC time is %s'), date_i18n($timezone_format, false, 'gmt')); ?> + + %1$s'), date_i18n($timezone_format)); ?> + +

    + +
    + + +
    + $right_now ) { + $found = true; + break; + } + } + + if ( $found ) { + echo ' '; + $message = $tr['isdst'] ? + __('Daylight saving time begins on: %s.') : + __('Standard time begins on: %s.'); + // Add the difference between the current offset and the new offset to ts to get the correct transition time from date_i18n(). + printf( $message, date_i18n(get_option('date_format') . ' ' . get_option('time_format'), $tr['ts'] + ($tz_offset - $tr['offset']) ) ); + } else { + _e('This timezone does not observe daylight saving time.'); + } + } + // Set back to UTC. + date_default_timezone_set('UTC'); + ?> +
    + +
    +
    + ' . date_i18n( $format ) . "
    \n"; + } + + echo ' ' . date_i18n( get_option('date_format') ) . " \n"; +?> +
    +
    +
    + ' . date_i18n( $format ) . "
    \n"; + } + + echo ' ' . date_i18n( get_option('time_format') ) . " \n"; + + echo "\t

    " . __('Documentation on date and time formatting.') . "

    \n"; +?> +
    +
    + 'WPLANG', + 'id' => 'WPLANG', + 'selected' => $locale, + 'languages' => $languages, + 'translations' => $translations, + 'show_available_translations' => ( ! is_multisite() || is_super_admin() ) && wp_can_install_language_pack(), + ) ); + + // Add note about deprecated WPLANG constant. + if ( defined( 'WPLANG' ) && ( '' !== WPLANG ) && $locale !== WPLANG ) { + if ( is_super_admin() ) { + ?> +

    + WPLANG', 'wp-config.php' ); ?> +

    + +
    + + + + +
    + +
    + + diff --git a/wp-admin/options-head.php b/wp-admin/options-head.php new file mode 100644 index 0000000..1c706c8 --- /dev/null +++ b/wp-admin/options-head.php @@ -0,0 +1,18 @@ +' . __('You can set maximum sizes for images inserted into your written content; you can also insert an image as Full Size.') . '

    '; + +if ( ! is_multisite() && ( get_option('upload_url_path') || ( get_option('upload_path') != 'wp-content/uploads' && get_option('upload_path') ) ) ) { + $media_options_help .= '

    ' . __('Uploading Files allows you to choose the folder and path for storing your uploaded files.') . '

    '; +} + +$media_options_help .= '

    ' . __('You must click the Save Changes button at the bottom of the screen for new settings to take effect.') . '

    '; + +get_current_screen()->add_help_tab( array( + 'id' => 'overview', + 'title' => __('Overview'), + 'content' => $media_options_help, +) ); + +get_current_screen()->set_help_sidebar( + '

    ' . __('For more information:') . '

    ' . + '

    ' . __('Documentation on Media Settings') . '

    ' . + '

    ' . __('Support Forums') . '

    ' +); + +include( ABSPATH . 'wp-admin/admin-header.php' ); + +?> + +
    +

    + +
    + + +

    +

    + + + + + + + + + + + + + + + + + + +
    + + + +
    +/> + +
    + + + + +
    + + + + +
    + + +

    + + +
    + + + +

    + + + + + + + + + + + + + + + + + +
    +

    wp-content/uploads'); ?>

    +
    +

    +
    + +
    + + + + + + +
    + +
    + + diff --git a/wp-admin/options-permalink.php b/wp-admin/options-permalink.php new file mode 100644 index 0000000..110ceda --- /dev/null +++ b/wp-admin/options-permalink.php @@ -0,0 +1,289 @@ +add_help_tab( array( + 'id' => 'overview', + 'title' => __('Overview'), + 'content' => '

    ' . __('Permalinks are the permanent URLs to your individual pages and blog posts, as well as your category and tag archives. A permalink is the web address used to link to your content. The URL to each post should be permanent, and never change — hence the name permalink.') . '

    ' . + '

    ' . __('This screen allows you to choose your default permalink structure. You can choose from common settings or create custom URL structures.') . '

    ' . + '

    ' . __('You must click the Save Changes button at the bottom of the screen for new settings to take effect.') . '

    ', +) ); + +get_current_screen()->add_help_tab( array( + 'id' => 'common-settings', + 'title' => __('Common Settings'), + 'content' => '

    ' . __('Many people choose to use “pretty permalinks,” URLs that contain useful information such as the post title rather than generic post ID numbers. You can choose from any of the permalink formats under Common Settings, or can craft your own if you select Custom Structure.') . '

    ' . + '

    ' . __('If you pick an option other than Default, your general URL path with structure tags, terms surrounded by %, will also appear in the custom structure field and your path can be further modified there.') . '

    ' . + '

    ' . __('When you assign multiple categories or tags to a post, only one can show up in the permalink: the lowest numbered category. This applies if your custom structure includes %category% or %tag%.') . '

    ' . + '

    ' . __('You must click the Save Changes button at the bottom of the screen for new settings to take effect.') . '

    ', +) ); + +get_current_screen()->add_help_tab( array( + 'id' => 'custom-structures', + 'title' => __('Custom Structures'), + 'content' => '

    ' . __('The Optional fields let you customize the “category” and “tag” base names that will appear in archive URLs. For example, the page listing all posts in the “Uncategorized” category could be /topics/uncategorized instead of /category/uncategorized.') . '

    ' . + '

    ' . __('You must click the Save Changes button at the bottom of the screen for new settings to take effect.') . '

    ', +) ); + +get_current_screen()->set_help_sidebar( + '

    ' . __('For more information:') . '

    ' . + '

    ' . __('Documentation on Permalinks Settings') . '

    ' . + '

    ' . __('Documentation on Using Permalinks') . '

    ' . + '

    ' . __('Support Forums') . '

    ' +); + +/** + * Display JavaScript on the page. + * + * @since 3.5.0 + */ +function options_permalink_add_js() { + ?> + +set_permalink_structure( $permalink_structure ); + } + + if ( isset( $_POST['category_base'] ) ) { + $category_base = $_POST['category_base']; + if ( ! empty( $category_base ) ) + $category_base = $blog_prefix . preg_replace('#/+#', '/', '/' . str_replace( '#', '', $category_base ) ); + $wp_rewrite->set_category_base( $category_base ); + } + + if ( isset( $_POST['tag_base'] ) ) { + $tag_base = $_POST['tag_base']; + if ( ! empty( $tag_base ) ) + $tag_base = $blog_prefix . preg_replace('#/+#', '/', '/' . str_replace( '#', '', $tag_base ) ); + $wp_rewrite->set_tag_base( $tag_base ); + } + + wp_redirect( admin_url( 'options-permalink.php?settings-updated=true' ) ); + exit; +} + +$permalink_structure = get_option( 'permalink_structure' ); +$category_base = get_option( 'category_base' ); +$tag_base = get_option( 'tag_base' ); +$update_required = false; + +if ( $iis7_permalinks ) { + if ( ( ! file_exists($home_path . 'web.config') && win_is_writable($home_path) ) || win_is_writable($home_path . 'web.config') ) + $writable = true; + else + $writable = false; +} elseif ( $is_nginx ) { + $writable = false; +} else { + if ( ( ! file_exists( $home_path . '.htaccess' ) && is_writable( $home_path ) ) || is_writable( $home_path . '.htaccess' ) ) { + $writable = true; + } else { + $writable = false; + $existing_rules = array_filter( extract_from_markers( $home_path . '.htaccess', 'WordPress' ) ); + $new_rules = array_filter( explode( "\n", $wp_rewrite->mod_rewrite_rules() ) ); + $update_required = ( $new_rules !== $existing_rules ); + } +} + +if ( $wp_rewrite->using_index_permalinks() ) + $usingpi = true; +else + $usingpi = false; + +flush_rewrite_rules(); + +require( ABSPATH . 'wp-admin/admin-header.php' ); + +if ( ! empty( $_GET['settings-updated'] ) ) : ?> +

    +

    + + +
    +

    + +
    + + +

    URLs which have question marks and lots of numbers in them; however, WordPress offers you the ability to create a custom URL structure for your permalinks and archives. This can improve the aesthetics, usability, and forward-compatibility of your links. A number of tags are available, and here are some examples to get you started.'); ?>

    + + '', + 1 => $prefix . '/%year%/%monthnum%/%day%/%postname%/', + 2 => $prefix . '/%year%/%monthnum%/%postname%/', + 3 => $prefix . '/' . _x( 'archives', 'sample permalink base' ) . '/%post_id%', + 4 => $prefix . '/%postname%/', +); +?> +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    +

    URLs here. For example, using topics as your category base would make your category links like %s/topics/uncategorized/. If you leave these blank the defaults will be used.'), get_option('home') . $blog_prefix . $prefix ); ?>

    + + + + + + + + + + + +
    + + + + +
    + + +

    web.config file were writable, we could do this automatically, but it isn’t so this is the url rewrite rule you should have in your web.config file. Click in the field and press CTRL + a to select all. Then insert this rule inside of the /<configuration>/<system.webServer>/<rewrite>/<rules> element in web.config file.') ?>

    +
    + +

    +
    +

    web.config file writable for us to generate rewrite rules automatically, do not forget to revert the permissions after rule has been saved.') ?>

    + +

    writable, we could do this automatically, but it isn’t so this is the url rewrite rule you should have in your web.config file. Create a new file, called web.config in the root directory of your site. Click in the field and press CTRL + a to select all. Then insert this code into the web.config file.') ?>

    +
    + +

    +
    +

    web.config file automatically, do not forget to revert the permissions after the file has been created.') ?>

    + + + +

    .htaccess file were writable, we could do this automatically, but it isn’t so these are the mod_rewrite rules you should have in your .htaccess file. Click in the field and press CTRL + a to select all.') ?>

    +
    + +

    +
    + + + + +
    + + diff --git a/wp-admin/options-reading.php b/wp-admin/options-reading.php new file mode 100644 index 0000000..7bcb20b --- /dev/null +++ b/wp-admin/options-reading.php @@ -0,0 +1,184 @@ + + +'; + echo '

    ' . __( 'The character encoding of your site (UTF-8 is recommended)' ) . '

    '; +} + +get_current_screen()->add_help_tab( array( + 'id' => 'overview', + 'title' => __('Overview'), + 'content' => '

    ' . __('This screen contains the settings that affect the display of your content.') . '

    ' . + '

    ' . sprintf(__('You can choose what’s displayed on the front page of your site. It can be posts in reverse chronological order (classic blog), or a fixed/static page. To set a static home page, you first need to create two Pages. One will become the front page, and the other will be where your posts are displayed.'), 'post-new.php?post_type=page') . '

    ' . + '

    ' . __('You can also control the display of your content in RSS feeds, including the maximum numbers of posts to display and whether to show full text or a summary.') . '

    ' . + '

    ' . __('You must click the Save Changes button at the bottom of the screen for new settings to take effect.') . '

    ', +) ); + +get_current_screen()->add_help_tab( array( + 'id' => 'site-visibility', + 'title' => has_action( 'blog_privacy_selector' ) ? __( 'Site Visibility' ) : __( 'Search Engine Visibility' ), + 'content' => '

    ' . __( 'You can choose whether or not your site will be crawled by robots, ping services, and spiders. If you want those services to ignore your site, click the checkbox next to “Discourage search engines from indexing this site” and click the Save Changes button at the bottom of the screen. Note that your privacy is not complete; your site is still visible on the web.' ) . '

    ' . + '

    ' . __( 'When this setting is in effect, a reminder is shown in the At a Glance box of the Dashboard that says, “Search Engines Discouraged,” to remind you that your site is not being crawled.' ) . '

    ', +) ); + +get_current_screen()->set_help_sidebar( + '

    ' . __('For more information:') . '

    ' . + '

    ' . __('Documentation on Reading Settings') . '

    ' . + '

    ' . __('Support Forums') . '

    ' +); + +include( ABSPATH . 'wp-admin/admin-header.php' ); +?> + +
    +

    + +
    + 'blog_charset' ) ); +?> + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    +

    +

    +

    +
      +
    • +
    • +
    + +

    Warning: these pages should not be the same!' ); ?>

    + +
    + +
    +


    +

    +
    + + /> +
    + /> + +

    + + + +

    + +
    + + + + +
    +
    + diff --git a/wp-admin/options-writing.php b/wp-admin/options-writing.php new file mode 100644 index 0000000..4c761d2 --- /dev/null +++ b/wp-admin/options-writing.php @@ -0,0 +1,194 @@ +add_help_tab( array( + 'id' => 'overview', + 'title' => __('Overview'), + 'content' => '

    ' . __('You can submit content in several different ways; this screen holds the settings for all of them. The top section controls the editor within the dashboard, while the rest control external publishing methods. For more information on any of these methods, use the documentation links.') . '

    ' . + '

    ' . __('You must click the Save Changes button at the bottom of the screen for new settings to take effect.') . '

    ', +) ); + +get_current_screen()->add_help_tab( array( + 'id' => 'options-press', + 'title' => __('Press This'), + 'content' => '

    ' . __('Press This is a bookmarklet that makes it easy to blog about something you come across on the web. You can use it to just grab a link, or to post an excerpt. Press This will even allow you to choose from images included on the page and use them in your post. Just drag the Press This link on this screen to your bookmarks bar in your browser, and you’ll be on your way to easier content creation. Clicking on it while on another website opens a popup window with all these options.') . '

    ', +) ); + +/** This filter is documented in wp-admin/options.php */ +if ( apply_filters( 'enable_post_by_email_configuration', true ) ) { + get_current_screen()->add_help_tab( array( + 'id' => 'options-postemail', + 'title' => __( 'Post Via Email' ), + 'content' => '

    ' . __( 'Post via email settings allow you to send your WordPress install an email with the content of your post. You must set up a secret e-mail account with POP3 access to use this, and any mail received at this address will be posted, so it’s a good idea to keep this address very secret.' ) . '

    ', + ) ); +} + +/** This filter is documented in wp-admin/options-writing.php */ +if ( apply_filters( 'enable_update_services_configuration', true ) ) { + get_current_screen()->add_help_tab( array( + 'id' => 'options-services', + 'title' => __( 'Update Services' ), + 'content' => '

    ' . __( 'If desired, WordPress will automatically alert various services of your new posts.' ) . '

    ', + ) ); +} + +get_current_screen()->set_help_sidebar( + '

    ' . __('For more information:') . '

    ' . + '

    ' . __('Documentation on Writing Settings') . '

    ' . + '

    ' . __('Support Forums') . '

    ' +); + +include( ABSPATH . 'wp-admin/admin-header.php' ); +?> + +
    +

    + +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + +
    + 0, 'name' => 'default_category', 'orderby' => 'name', 'selected' => get_option('default_category'), 'hierarchical' => true)); +?> +
    + +
    + 0, 'name' => 'default_link_category', 'orderby' => 'name', 'selected' => get_option('default_link_category'), 'hierarchical' => true, 'taxonomy' => 'link_category')); +?> +
    + +

    +

    +

    +

    +

    + + + +

    +

    %s, %s, %s.'), wp_generate_password(8, false), wp_generate_password(8, false), wp_generate_password(8, false)) ?>

    + + + + + + + + + + + + + + + + + + + +
    + + +
    + +
    + 0, 'name' => 'default_email_category', 'orderby' => 'name', 'selected' => get_option('default_email_category'), 'hierarchical' => true)); +?> +
    + + + +

    + + + +

    + + + + + +

    Update Services because of your site’s visibility settings.'), 'options-reading.php'); ?>

    + + + + + + + +
    +
    + + diff --git a/wp-admin/options.php b/wp-admin/options.php new file mode 100644 index 0000000..6cf5823 --- /dev/null +++ b/wp-admin/options.php @@ -0,0 +1,277 @@ + array( 'blogname', 'blogdescription', 'gmt_offset', 'date_format', 'time_format', 'start_of_week', 'timezone_string', 'WPLANG' ), + 'discussion' => array( 'default_pingback_flag', 'default_ping_status', 'default_comment_status', 'comments_notify', 'moderation_notify', 'comment_moderation', 'require_name_email', 'comment_whitelist', 'comment_max_links', 'moderation_keys', 'blacklist_keys', 'show_avatars', 'avatar_rating', 'avatar_default', 'close_comments_for_old_posts', 'close_comments_days_old', 'thread_comments', 'thread_comments_depth', 'page_comments', 'comments_per_page', 'default_comments_page', 'comment_order', 'comment_registration' ), + 'media' => array( 'thumbnail_size_w', 'thumbnail_size_h', 'thumbnail_crop', 'medium_size_w', 'medium_size_h', 'large_size_w', 'large_size_h', 'image_default_size', 'image_default_align', 'image_default_link_type' ), + 'reading' => array( 'posts_per_page', 'posts_per_rss', 'rss_use_excerpt', 'show_on_front', 'page_on_front', 'page_for_posts', 'blog_public' ), + 'writing' => array( 'use_smilies', 'default_category', 'default_email_category', 'use_balanceTags', 'default_link_category', 'default_post_format' ) +); +$whitelist_options['misc'] = $whitelist_options['options'] = $whitelist_options['privacy'] = array(); + +$mail_options = array('mailserver_url', 'mailserver_port', 'mailserver_login', 'mailserver_pass'); + +if ( ! in_array( get_option( 'blog_charset' ), array( 'utf8', 'utf-8', 'UTF8', 'UTF-8' ) ) ) + $whitelist_options['reading'][] = 'blog_charset'; + +if ( !is_multisite() ) { + if ( !defined( 'WP_SITEURL' ) ) + $whitelist_options['general'][] = 'siteurl'; + if ( !defined( 'WP_HOME' ) ) + $whitelist_options['general'][] = 'home'; + + $whitelist_options['general'][] = 'admin_email'; + $whitelist_options['general'][] = 'users_can_register'; + $whitelist_options['general'][] = 'default_role'; + + $whitelist_options['writing'] = array_merge($whitelist_options['writing'], $mail_options); + $whitelist_options['writing'][] = 'ping_sites'; + + $whitelist_options['media'][] = 'uploads_use_yearmonth_folders'; + + // If upload_url_path and upload_path are both default values, they're locked. + if ( get_option( 'upload_url_path' ) || ( get_option('upload_path') != 'wp-content/uploads' && get_option('upload_path') ) ) { + $whitelist_options['media'][] = 'upload_path'; + $whitelist_options['media'][] = 'upload_url_path'; + } +} else { + $whitelist_options['general'][] = 'new_admin_email'; + + /** + * Filter whether the post-by-email functionality is enabled. + * + * @since 3.0.0 + * + * @param bool $enabled Whether post-by-email configuration is enabled. Default true. + */ + if ( apply_filters( 'enable_post_by_email_configuration', true ) ) + $whitelist_options['writing'] = array_merge($whitelist_options['writing'], $mail_options); +} + +/** + * Filter the options white list. + * + * @since 2.7.0 + * + * @param array White list options. + */ +$whitelist_options = apply_filters( 'whitelist_options', $whitelist_options ); + +/* + * If $_GET['action'] == 'update' we are saving settings sent from a settings page + */ +if ( 'update' == $action ) { + if ( 'options' == $option_page && !isset( $_POST['option_page'] ) ) { // This is for back compat and will eventually be removed. + $unregistered = true; + check_admin_referer( 'update-options' ); + } else { + $unregistered = false; + check_admin_referer( $option_page . '-options' ); + } + + if ( !isset( $whitelist_options[ $option_page ] ) ) + wp_die( __( 'ERROR: options page not found.' ) ); + + if ( 'options' == $option_page ) { + if ( is_multisite() && ! is_super_admin() ) + wp_die( __( 'You do not have sufficient permissions to modify unregistered settings for this site.' ) ); + $options = explode( ',', wp_unslash( $_POST[ 'page_options' ] ) ); + } else { + $options = $whitelist_options[ $option_page ]; + } + + if ( 'general' == $option_page ) { + // Handle custom date/time formats. + if ( !empty($_POST['date_format']) && isset($_POST['date_format_custom']) && '\c\u\s\t\o\m' == wp_unslash( $_POST['date_format'] ) ) + $_POST['date_format'] = $_POST['date_format_custom']; + if ( !empty($_POST['time_format']) && isset($_POST['time_format_custom']) && '\c\u\s\t\o\m' == wp_unslash( $_POST['time_format'] ) ) + $_POST['time_format'] = $_POST['time_format_custom']; + // Map UTC+- timezones to gmt_offsets and set timezone_string to empty. + if ( !empty($_POST['timezone_string']) && preg_match('/^UTC[+-]/', $_POST['timezone_string']) ) { + $_POST['gmt_offset'] = $_POST['timezone_string']; + $_POST['gmt_offset'] = preg_replace('/UTC\+?/', '', $_POST['gmt_offset']); + $_POST['timezone_string'] = ''; + } + + // Handle translation install. + if ( ! empty( $_POST['WPLANG'] ) && ( ! is_multisite() || is_super_admin() ) ) { // @todo: Skip if already installed + require_once( ABSPATH . 'wp-admin/includes/translation-install.php' ); + + if ( wp_can_install_language_pack() ) { + $language = wp_download_language_pack( $_POST['WPLANG'] ); + if ( $language ) { + $_POST['WPLANG'] = $language; + } + } + } + } + + if ( $options ) { + foreach ( $options as $option ) { + if ( $unregistered ) + _deprecated_argument( 'options.php', '2.7', sprintf( __( 'The %1$s setting is unregistered. Unregistered settings are deprecated. See http://codex.wordpress.org/Settings_API' ), $option, $option_page ) ); + + $option = trim( $option ); + $value = null; + if ( isset( $_POST[ $option ] ) ) { + $value = $_POST[ $option ]; + if ( ! is_array( $value ) ) + $value = trim( $value ); + $value = wp_unslash( $value ); + } + update_option( $option, $value ); + } + + // Switch translation in case WPLANG was changed. + $language = get_option( 'WPLANG' ); + if ( $language ) { + load_default_textdomain( $language ); + } else { + unload_textdomain( 'default' ); + } + } + + /** + * Handle settings errors and return to options page + */ + // If no settings errors were registered add a general 'updated' message. + if ( !count( get_settings_errors() ) ) + add_settings_error('general', 'settings_updated', __('Settings saved.'), 'updated'); + set_transient('settings_errors', get_settings_errors(), 30); + + /** + * Redirect back to the settings page that was submitted + */ + $goback = add_query_arg( 'settings-updated', 'true', wp_get_referer() ); + wp_redirect( $goback ); + exit; +} + +include( ABSPATH . 'wp-admin/admin-header.php' ); ?> + +
    +

    +
    + + + + +get_results( "SELECT * FROM $wpdb->options ORDER BY option_name" ); + +foreach ( (array) $options as $option ) : + $disabled = false; + if ( $option->option_name == '' ) + continue; + if ( is_serialized( $option->option_value ) ) { + if ( is_serialized_string( $option->option_value ) ) { + // This is a serialized string, so we should display it. + $value = maybe_unserialize( $option->option_value ); + $options_to_update[] = $option->option_name; + $class = 'all-options'; + } else { + $value = 'SERIALIZED DATA'; + $disabled = true; + $class = 'all-options disabled'; + } + } else { + $value = $option->option_value; + $options_to_update[] = $option->option_name; + $class = 'all-options'; + } + $name = esc_attr( $option->option_name ); + ?> + + + + + +
    + + + + /> +
    + + + + + +
    +
    + + +
    +

    +

    +
    + time() ) + (array) get_option( 'recently_activated' ) ); + + wp_redirect(add_query_arg('_wpnonce', wp_create_nonce('edit-plugin-test_' . $file), "plugin-editor.php?file=$file&liveupdate=1&scrollto=$scrollto&networkwide=" . $network_wide)); + exit; + } + wp_redirect( self_admin_url("plugin-editor.php?file=$file&a=te&scrollto=$scrollto") ); + } else { + wp_redirect( self_admin_url("plugin-editor.php?file=$file&scrollto=$scrollto") ); + } + exit; + +default: + + if ( isset($_GET['liveupdate']) ) { + check_admin_referer('edit-plugin-test_' . $file); + + $error = validate_plugin($file); + if ( is_wp_error($error) ) + wp_die( $error ); + + if ( ( ! empty( $_GET['networkwide'] ) && ! is_plugin_active_for_network($file) ) || ! is_plugin_active($file) ) + activate_plugin($file, "plugin-editor.php?file=$file&phperror=1", ! empty( $_GET['networkwide'] ) ); // we'll override this later if the plugin can be included without fatal error + + wp_redirect( self_admin_url("plugin-editor.php?file=$file&a=te&scrollto=$scrollto") ); + exit; + } + + // List of allowable extensions + $editable_extensions = array('php', 'txt', 'text', 'js', 'css', 'html', 'htm', 'xml', 'inc', 'include'); + + /** + * Filter file type extensions editable in the plugin editor. + * + * @since 2.8.0 + * + * @param array $editable_extensions An array of editable plugin file extensions. + */ + $editable_extensions = (array) apply_filters( 'editable_extensions', $editable_extensions ); + + if ( ! is_file($real_file) ) { + wp_die(sprintf('

    %s

    ', __('No such file exists! Double check the name and try again.'))); + } else { + // Get the extension of the file + if ( preg_match('/\.([^.]+)$/', $real_file, $matches) ) { + $ext = strtolower($matches[1]); + // If extension is not in the acceptable list, skip it + if ( !in_array( $ext, $editable_extensions) ) + wp_die(sprintf('

    %s

    ', __('Files of this type are not editable.'))); + } + } + + get_current_screen()->add_help_tab( array( + 'id' => 'overview', + 'title' => __('Overview'), + 'content' => + '

    ' . __('You can use the editor to make changes to any of your plugins’ individual PHP files. Be aware that if you make changes, plugins updates will overwrite your customizations.') . '

    ' . + '

    ' . __('Choose a plugin to edit from the menu in the upper right and click the Select button. Click once on any file name to load it in the editor, and make your changes. Don’t forget to save your changes (Update File) when you’re finished.') . '

    ' . + '

    ' . __('The Documentation menu below the editor lists the PHP functions recognized in the plugin file. Clicking Look Up takes you to a web page about that particular function.') . '

    ' . + '

    ' . __('In the editing area the Tab key enters a tab character. To move below this area by pressing Tab, press the Esc key followed by the Tab key.') . '

    ' . + '

    ' . __('If you want to make changes but don’t want them to be overwritten when the plugin is updated, you may be ready to think about writing your own plugin. For information on how to edit plugins, write your own from scratch, or just better understand their anatomy, check out the links below.') . '

    ' . + ( is_network_admin() ? '

    ' . __('Any edits to files from this screen will be reflected on all sites in the network.') . '

    ' : '' ) + ) ); + + get_current_screen()->set_help_sidebar( + '

    ' . __('For more information:') . '

    ' . + '

    ' . __('Documentation on Editing Plugins') . '

    ' . + '

    ' . __('Documentation on Writing Plugins') . '

    ' . + '

    ' . __('Support Forums') . '

    ' + ); + + require_once(ABSPATH . 'wp-admin/admin-header.php'); + + update_recently_edited(WP_PLUGIN_DIR . '/' . $file); + + $content = file_get_contents( $real_file ); + + if ( '.php' == substr( $real_file, strrpos( $real_file, '.' ) ) ) { + $functions = wp_doc_link_parse( $content ); + + if ( !empty($functions) ) { + $docs_select = ''; + } + } + + $content = esc_textarea( $content ); + ?> + +

    + +

    fatal error.') ?>

    + + + +
    + +
    +

    + +
    +
    +%s (active)'), $file); + else + echo sprintf(__('Browsing %s (active)'), $file); + } else { + if ( is_writeable($real_file) ) + echo sprintf(__('Editing %s (inactive)'), $file); + else + echo sprintf(__('Browsing %s (inactive)'), $file); + } + ?> +
    +
    +
    + + + +
    +
    +
    +
    + +
    +

    + +
      + + > + +
    +
    +
    + +
    + + + + +
    + +
    + + + +

    Warning: Making changes to active plugins is not recommended. If your changes cause a fatal error, the plugin will be automatically deactivated.'); ?>

    + +

    + "; + submit_button( __( 'Update File and Attempt to Reactivate' ), 'primary', 'submit', false ); + } else { + submit_button( __( 'Update File' ), 'primary', 'submit', false ); + } + ?> +

    + +

    the Codex for more information.'); ?>

    + +
    +
    +
    + +get_pagenum(); + +if ( ! empty( $_REQUEST['_wp_http_referer'] ) ) { + $location = remove_query_arg( '_wp_http_referer', wp_unslash( $_SERVER['REQUEST_URI'] ) ); + + if ( ! empty( $_REQUEST['paged'] ) ) { + $location = add_query_arg( 'paged', (int) $_REQUEST['paged'], $location ); + } + + wp_redirect( $location ); + exit; +} + +$wp_list_table->prepare_items(); + +$total_pages = $wp_list_table->get_pagination_arg( 'total_pages' ); + +if ( $pagenum > $total_pages && $total_pages > 0 ) { + wp_redirect( add_query_arg( 'paged', $total_pages ) ); + exit; +} + +$title = __( 'Add Plugins' ); +$parent_file = 'plugins.php'; + +wp_enqueue_script( 'plugin-install' ); +if ( 'plugin-information' != $tab ) + add_thickbox(); + +$body_id = $tab; + +/** + * Fires before each tab on the Install Plugins screen is loaded. + * + * The dynamic portion of the action hook, `$tab`, allows for targeting + * individual tabs, for instance 'install_plugins_pre_plugin-information'. + * + * @since 2.7.0 + */ +do_action( "install_plugins_pre_$tab" ); + +get_current_screen()->add_help_tab( array( +'id' => 'overview', +'title' => __('Overview'), +'content' => + '

    ' . sprintf(__('Plugins hook into WordPress to extend its functionality with custom features. Plugins are developed independently from the core WordPress application by thousands of developers all over the world. All plugins in the official WordPress.org Plugin Directory are compatible with the license WordPress uses. You can find new plugins to install by searching or browsing the Directory right here in your own Plugins section.'), 'https://wordpress.org/plugins/') . '

    ' +) ); +get_current_screen()->add_help_tab( array( +'id' => 'adding-plugins', +'title' => __('Adding Plugins'), +'content' => + '

    ' . __('If you know what you’re looking for, Search is your best bet. The Search screen has options to search the WordPress.org Plugin Directory for a particular Term, Author, or Tag. You can also search the directory by selecting popular tags. Tags in larger type mean more plugins have been labeled with that tag.') . '

    ' . + '

    ' . __('If you just want to get an idea of what’s available, you can browse Featured and Popular plugins by using the links in the upper left of the screen. These sections rotate regularly.') . '

    ' . + '

    ' . __('You can also browse a user’s favorite plugins, by using the Favorites link in the upper left of the screen and entering their WordPress.org username.') . '

    ' . + '

    ' . __('If you want to install a plugin that you’ve downloaded elsewhere, click the Upload link in the upper left. You will be prompted to upload the .zip package, and once uploaded, you can activate the new plugin.') . '

    ' +) ); + +get_current_screen()->set_help_sidebar( + '

    ' . __('For more information:') . '

    ' . + '

    ' . __('Documentation on Installing Plugins') . '

    ' . + '

    ' . __('Support Forums') . '

    ' +); + +/** + * WordPress Administration Template Header. + */ +include(ABSPATH . 'wp-admin/admin-header.php'); +?> +
    +

    + ' . $text . ''; + } + ?> +

    + +views(); + echo '
    '; +} + +/** + * Fires after the plugins list table in each tab of the Install Plugins screen. + * + * The dynamic portion of the action hook, `$tab`, allows for targeting + * individual tabs, for instance 'install_plugins_plugin-information'. + * + * @since 2.7.0 + * + * @param int $paged The current page number of the plugins list table. + */ +do_action( "install_plugins_$tab", $paged ); ?> +
    +get_pagenum(); + +$action = $wp_list_table->current_action(); + +$plugin = isset($_REQUEST['plugin']) ? $_REQUEST['plugin'] : ''; +$s = isset($_REQUEST['s']) ? urlencode($_REQUEST['s']) : ''; + +// Clean up request URI from temporary args for screen options/paging uri's to work as expected. +$_SERVER['REQUEST_URI'] = remove_query_arg(array('error', 'deleted', 'activate', 'activate-multi', 'deactivate', 'deactivate-multi', '_error_nonce'), $_SERVER['REQUEST_URI']); + +if ( $action ) { + + switch ( $action ) { + case 'activate': + if ( ! current_user_can('activate_plugins') ) + wp_die(__('You do not have sufficient permissions to activate plugins for this site.')); + + if ( is_multisite() && ! is_network_admin() && is_network_only_plugin( $plugin ) ) { + wp_redirect( self_admin_url("plugins.php?plugin_status=$status&paged=$page&s=$s") ); + exit; + } + + check_admin_referer('activate-plugin_' . $plugin); + + $result = activate_plugin($plugin, self_admin_url('plugins.php?error=true&plugin=' . $plugin), is_network_admin() ); + if ( is_wp_error( $result ) ) { + if ( 'unexpected_output' == $result->get_error_code() ) { + $redirect = self_admin_url('plugins.php?error=true&charsout=' . strlen($result->get_error_data()) . '&plugin=' . $plugin . "&plugin_status=$status&paged=$page&s=$s"); + wp_redirect(add_query_arg('_error_nonce', wp_create_nonce('plugin-activation-error_' . $plugin), $redirect)); + exit; + } else { + wp_die($result); + } + } + + if ( ! is_network_admin() ) { + $recent = (array) get_option( 'recently_activated' ); + unset( $recent[ $plugin ] ); + update_option( 'recently_activated', $recent ); + } + + if ( isset($_GET['from']) && 'import' == $_GET['from'] ) { + wp_redirect( self_admin_url("import.php?import=" . str_replace('-importer', '', dirname($plugin))) ); // overrides the ?error=true one above and redirects to the Imports page, stripping the -importer suffix + } else { + wp_redirect( self_admin_url("plugins.php?activate=true&plugin_status=$status&paged=$page&s=$s") ); // overrides the ?error=true one above + } + exit; + + case 'activate-selected': + if ( ! current_user_can('activate_plugins') ) + wp_die(__('You do not have sufficient permissions to activate plugins for this site.')); + + check_admin_referer('bulk-plugins'); + + $plugins = isset( $_POST['checked'] ) ? (array) $_POST['checked'] : array(); + + if ( is_network_admin() ) { + foreach ( $plugins as $i => $plugin ) { + // Only activate plugins which are not already network activated. + if ( is_plugin_active_for_network( $plugin ) ) { + unset( $plugins[ $i ] ); + } + } + } else { + foreach ( $plugins as $i => $plugin ) { + // Only activate plugins which are not already active and are not network-only when on Multisite. + if ( is_plugin_active( $plugin ) || ( is_multisite() && is_network_only_plugin( $plugin ) ) ) { + unset( $plugins[ $i ] ); + } + } + } + + if ( empty($plugins) ) { + wp_redirect( self_admin_url("plugins.php?plugin_status=$status&paged=$page&s=$s") ); + exit; + } + + activate_plugins($plugins, self_admin_url('plugins.php?error=true'), is_network_admin() ); + + if ( ! is_network_admin() ) { + $recent = (array) get_option('recently_activated' ); + foreach ( $plugins as $plugin ) + unset( $recent[ $plugin ] ); + update_option( 'recently_activated', $recent ); + } + + wp_redirect( self_admin_url("plugins.php?activate-multi=true&plugin_status=$status&paged=$page&s=$s") ); + exit; + + case 'update-selected' : + + check_admin_referer( 'bulk-plugins' ); + + if ( isset( $_GET['plugins'] ) ) + $plugins = explode( ',', $_GET['plugins'] ); + elseif ( isset( $_POST['checked'] ) ) + $plugins = (array) $_POST['checked']; + else + $plugins = array(); + + $title = __( 'Update Plugins' ); + $parent_file = 'plugins.php'; + + wp_enqueue_script( 'updates' ); + require_once(ABSPATH . 'wp-admin/admin-header.php'); + + echo '
    '; + echo '

    ' . esc_html( $title ) . '

    '; + + $url = self_admin_url('update.php?action=update-selected&plugins=' . urlencode( join(',', $plugins) )); + $url = wp_nonce_url($url, 'bulk-update-plugins'); + + echo ""; + echo '
    '; + require_once(ABSPATH . 'wp-admin/admin-footer.php'); + exit; + + case 'error_scrape': + if ( ! current_user_can('activate_plugins') ) + wp_die(__('You do not have sufficient permissions to activate plugins for this site.')); + + check_admin_referer('plugin-activation-error_' . $plugin); + + $valid = validate_plugin($plugin); + if ( is_wp_error($valid) ) + wp_die($valid); + + if ( ! WP_DEBUG ) { + error_reporting( E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_ERROR | E_WARNING | E_PARSE | E_USER_ERROR | E_USER_WARNING | E_RECOVERABLE_ERROR ); + } + + @ini_set('display_errors', true); //Ensure that Fatal errors are displayed. + // Go back to "sandbox" scope so we get the same errors as before + function plugin_sandbox_scrape( $plugin ) { + wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $plugin ); + include( WP_PLUGIN_DIR . '/' . $plugin ); + } + plugin_sandbox_scrape( $plugin ); + /** This action is documented in wp-admin/includes/plugin.php */ + do_action( "activate_{$plugin}" ); + exit; + + case 'deactivate': + if ( ! current_user_can('activate_plugins') ) + wp_die(__('You do not have sufficient permissions to deactivate plugins for this site.')); + + check_admin_referer('deactivate-plugin_' . $plugin); + + if ( ! is_network_admin() && is_plugin_active_for_network( $plugin ) ) { + wp_redirect( self_admin_url("plugins.php?plugin_status=$status&paged=$page&s=$s") ); + exit; + } + + deactivate_plugins( $plugin, false, is_network_admin() ); + if ( ! is_network_admin() ) + update_option( 'recently_activated', array( $plugin => time() ) + (array) get_option( 'recently_activated' ) ); + if ( headers_sent() ) + echo ""; + else + wp_redirect( self_admin_url("plugins.php?deactivate=true&plugin_status=$status&paged=$page&s=$s") ); + exit; + + case 'deactivate-selected': + if ( ! current_user_can('activate_plugins') ) + wp_die(__('You do not have sufficient permissions to deactivate plugins for this site.')); + + check_admin_referer('bulk-plugins'); + + $plugins = isset( $_POST['checked'] ) ? (array) $_POST['checked'] : array(); + // Do not deactivate plugins which are already deactivated. + if ( is_network_admin() ) { + $plugins = array_filter( $plugins, 'is_plugin_active_for_network' ); + } else { + $plugins = array_filter( $plugins, 'is_plugin_active' ); + $plugins = array_diff( $plugins, array_filter( $plugins, 'is_plugin_active_for_network' ) ); + } + if ( empty($plugins) ) { + wp_redirect( self_admin_url("plugins.php?plugin_status=$status&paged=$page&s=$s") ); + exit; + } + + deactivate_plugins( $plugins, false, is_network_admin() ); + + if ( ! is_network_admin() ) { + $deactivated = array(); + foreach ( $plugins as $plugin ) + $deactivated[ $plugin ] = time(); + update_option( 'recently_activated', $deactivated + (array) get_option( 'recently_activated' ) ); + } + + wp_redirect( self_admin_url("plugins.php?deactivate-multi=true&plugin_status=$status&paged=$page&s=$s") ); + exit; + + case 'delete-selected': + if ( ! current_user_can('delete_plugins') ) { + wp_die(__('You do not have sufficient permissions to delete plugins for this site.')); + } + + check_admin_referer('bulk-plugins'); + + //$_POST = from the plugin form; $_GET = from the FTP details screen. + $plugins = isset( $_REQUEST['checked'] ) ? (array) $_REQUEST['checked'] : array(); + if ( empty( $plugins ) ) { + wp_redirect( self_admin_url("plugins.php?plugin_status=$status&paged=$page&s=$s") ); + exit; + } + + $plugins = array_filter($plugins, 'is_plugin_inactive'); // Do not allow to delete Activated plugins. + if ( empty( $plugins ) ) { + wp_redirect( self_admin_url( "plugins.php?error=true&main=true&plugin_status=$status&paged=$page&s=$s" ) ); + exit; + } + + include(ABSPATH . 'wp-admin/update.php'); + + $parent_file = 'plugins.php'; + + if ( ! isset($_REQUEST['verify-delete']) ) { + wp_enqueue_script('jquery'); + require_once(ABSPATH . 'wp-admin/admin-header.php'); + ?> +
    + $data ) { + $plugin_info[ $plugin_file ] = _get_plugin_data_markup_translate( $plugin_file, $data ); + $plugin_info[ $plugin_file ]['is_uninstallable'] = is_uninstallable_plugin( $plugin ); + if ( ! $plugin_info[ $plugin_file ]['Network'] ) { + $have_non_network_plugins = true; + } + } + } + + // Add translation files. + if ( ! empty( $plugin_translations[ $plugin_slug ] ) ) { + $translations = $plugin_translations[ $plugin_slug ]; + + foreach ( $translations as $translation => $data ) { + $files_to_delete[] = $plugin_slug . '-' . $translation . '.po'; + $files_to_delete[] = $plugin_slug . '-' . $translation . '.mo'; + } + } + } + } + $plugins_to_delete = count( $plugin_info ); + echo '

    ' . _n( 'Delete Plugin', 'Delete Plugins', $plugins_to_delete ) . '

    '; + ?> + +

    + +

    +
      + ', sprintf( __( '%1$s by %2$s (will also delete its data)' ), esc_html($plugin['Name']), esc_html($plugin['AuthorName']) ), ''; + $data_to_delete = true; + } else { + /* translators: 1: plugin name, 2: plugin author */ + echo '
    • ', sprintf( __('%1$s by %2$s' ), esc_html($plugin['Name']), esc_html($plugin['AuthorName']) ), '
    • '; + } + } + ?> +
    +

    +
    + + + '; + } + ?> + + +
    +
    + +
    + +

    + +
    + prepare_items(); + +wp_enqueue_script('plugin-install'); +add_thickbox(); + +add_screen_option( 'per_page', array('label' => _x( 'Plugins', 'plugins per page (screen options)' ), 'default' => 999 ) ); + +get_current_screen()->add_help_tab( array( +'id' => 'overview', +'title' => __('Overview'), +'content' => + '

    ' . __('Plugins extend and expand the functionality of WordPress. Once a plugin is installed, you may activate it or deactivate it here.') . '

    ' . + '

    ' . sprintf(__('You can find additional plugins for your site by using the Plugin Browser/Installer functionality or by browsing the WordPress Plugin Directory directly and installing new plugins manually. To manually install a plugin you generally just need to upload the plugin file into your /wp-content/plugins directory. Once a plugin has been installed, you can activate it here.'), 'plugin-install.php', 'https://wordpress.org/plugins/') . '

    ' +) ); +get_current_screen()->add_help_tab( array( +'id' => 'compatibility-problems', +'title' => __('Troubleshooting'), +'content' => + '

    ' . __('Most of the time, plugins play nicely with the core of WordPress and with other plugins. Sometimes, though, a plugin’s code will get in the way of another plugin, causing compatibility issues. If your site starts doing strange things, this may be the problem. Try deactivating all your plugins and re-activating them in various combinations until you isolate which one(s) caused the issue.') . '

    ' . + '

    ' . sprintf( __('If something goes wrong with a plugin and you can’t use WordPress, delete or rename that file in the %s directory and it will be automatically deactivated.'), WP_PLUGIN_DIR) . '

    ' +) ); + +get_current_screen()->set_help_sidebar( + '

    ' . __('For more information:') . '

    ' . + '

    ' . __('Documentation on Managing Plugins') . '

    ' . + '

    ' . __('Support Forums') . '

    ' +); + +$title = __('Plugins'); +$parent_file = 'plugins.php'; + +require_once(ABSPATH . 'wp-admin/admin-header.php'); + +$invalid = validate_active_plugins(); +if ( !empty($invalid) ) + foreach ( $invalid as $plugin_file => $error ) + echo '

    ' . sprintf(__('The plugin %s has been deactivated due to an error: %s'), esc_html($plugin_file), $error->get_error_message()) . '

    '; +?> + +unexpected output during activation. If you notice “headers already sent” messages, problems with syndication feeds or other issues, try deactivating or removing this plugin.'), $_GET['charsout']); + else + $errmsg = __('Plugin could not be activated because it triggered a fatal error.'); + ?> +

    + + + +
    + +

    get_error_message() ); ?>

    + +

    deleted.'); ?>

    + + +

    activated.') ?>

    + +

    activated.'); ?>

    + +

    deactivated.') ?>

    + +

    deactivated.'); ?>

    + +

    + + +
    +

    + +' . __('Search results for “%s”') . '', esc_html( $s ) ); ?> +

    + + + +views(); ?> + +
    +search_box( __( 'Search Installed Plugins' ), 'plugin' ); ?> +
    + +
    + + + + +display(); ?> +
    + +
    + + true ) ) ) ) + $post_type = $_GET['post_type']; +else + wp_die( __('Invalid post type') ); + +$post_type_object = get_post_type_object( $post_type ); + +if ( 'post' == $post_type ) { + $parent_file = 'edit.php'; + $submenu_file = 'post-new.php'; +} elseif ( 'attachment' == $post_type ) { + if ( wp_redirect( admin_url( 'media-new.php' ) ) ) + exit; +} else { + $submenu_file = "post-new.php?post_type=$post_type"; + if ( isset( $post_type_object ) && $post_type_object->show_in_menu && $post_type_object->show_in_menu !== true ) { + $parent_file = $post_type_object->show_in_menu; + // What if there isn't a post-new.php item for this post type? + if ( ! isset( $_registered_pages[ get_plugin_page_hookname( "post-new.php?post_type=$post_type", $post_type_object->show_in_menu ) ] ) ) { + if ( isset( $_registered_pages[ get_plugin_page_hookname( "edit.php?post_type=$post_type", $post_type_object->show_in_menu ) ] ) ) { + // Fall back to edit.php for that post type, if it exists + $submenu_file = "edit.php?post_type=$post_type"; + } else { + // Otherwise, give up and highlight the parent + $submenu_file = $parent_file; + } + } + } else { + $parent_file = "edit.php?post_type=$post_type"; + } +} + +$title = $post_type_object->labels->add_new_item; + +$editing = true; + +if ( ! current_user_can( $post_type_object->cap->edit_posts ) || ! current_user_can( $post_type_object->cap->create_posts ) ) + wp_die( __( 'Cheatin’ uh?' ), 403 ); + +// Schedule auto-draft cleanup +if ( ! wp_next_scheduled( 'wp_scheduled_auto_draft_delete' ) ) + wp_schedule_event( time(), 'daily', 'wp_scheduled_auto_draft_delete' ); + +wp_enqueue_script( 'autosave' ); + +if ( is_multisite() ) { + add_action( 'admin_footer', '_admin_notice_post_locked' ); +} else { + $check_users = get_users( array( 'fields' => 'ID', 'number' => 2 ) ); + + if ( count( $check_users ) > 1 ) + add_action( 'admin_footer', '_admin_notice_post_locked' ); + + unset( $check_users ); +} + +// Show post form. +$post = get_default_post_to_edit( $post_type, true ); +$post_ID = $post->ID; +include( ABSPATH . 'wp-admin/edit-form-advanced.php' ); +include( ABSPATH . 'wp-admin/admin-footer.php' ); diff --git a/wp-admin/post.php b/wp-admin/post.php new file mode 100644 index 0000000..805c46e --- /dev/null +++ b/wp-admin/post.php @@ -0,0 +1,318 @@ +post_type; + $post_type_object = get_post_type_object( $post_type ); +} + +/** + * Redirect to previous page. + * + * @param int $post_id Optional. Post ID. + */ +function redirect_post($post_id = '') { + if ( isset($_POST['save']) || isset($_POST['publish']) ) { + $status = get_post_status( $post_id ); + + if ( isset( $_POST['publish'] ) ) { + switch ( $status ) { + case 'pending': + $message = 8; + break; + case 'future': + $message = 9; + break; + default: + $message = 6; + } + } else { + $message = 'draft' == $status ? 10 : 1; + } + + $location = add_query_arg( 'message', $message, get_edit_post_link( $post_id, 'url' ) ); + } elseif ( isset($_POST['addmeta']) && $_POST['addmeta'] ) { + $location = add_query_arg( 'message', 2, wp_get_referer() ); + $location = explode('#', $location); + $location = $location[0] . '#postcustom'; + } elseif ( isset($_POST['deletemeta']) && $_POST['deletemeta'] ) { + $location = add_query_arg( 'message', 3, wp_get_referer() ); + $location = explode('#', $location); + $location = $location[0] . '#postcustom'; + } else { + $location = add_query_arg( 'message', 4, get_edit_post_link( $post_id, 'url' ) ); + } + + /** + * Filter the post redirect destination URL. + * + * @since 2.9.0 + * + * @param string $location The destination URL. + * @param int $post_id The post ID. + */ + wp_redirect( apply_filters( 'redirect_post_location', $location, $post_id ) ); + exit; +} + +if ( isset( $_POST['deletepost'] ) ) + $action = 'delete'; +elseif ( isset($_POST['wp-preview']) && 'dopreview' == $_POST['wp-preview'] ) + $action = 'preview'; + +$sendback = wp_get_referer(); +if ( ! $sendback || + strpos( $sendback, 'post.php' ) !== false || + strpos( $sendback, 'post-new.php' ) !== false ) { + if ( 'attachment' == $post_type ) { + $sendback = admin_url( 'upload.php' ); + } else { + $sendback = admin_url( 'edit.php' ); + $sendback .= ( ! empty( $post_type ) ) ? '?post_type=' . $post_type : ''; + } +} else { + $sendback = remove_query_arg( array('trashed', 'untrashed', 'deleted', 'ids'), $sendback ); +} + +switch($action) { +case 'post-quickdraft-save': + // Check nonce and capabilities + $nonce = $_REQUEST['_wpnonce']; + $error_msg = false; + + // For output of the quickdraft dashboard widget + require_once ABSPATH . 'wp-admin/includes/dashboard.php'; + + if ( ! wp_verify_nonce( $nonce, 'add-post' ) ) + $error_msg = __( 'Unable to submit this form, please refresh and try again.' ); + + if ( ! current_user_can( 'edit_posts' ) ) + $error_msg = __( 'Oops, you don’t have access to add new drafts.' ); + + if ( $error_msg ) + return wp_dashboard_quick_press( $error_msg ); + + $post = get_post( $_REQUEST['post_ID'] ); + check_admin_referer( 'add-' . $post->post_type ); + + $_POST['comment_status'] = get_option( 'default_comment_status' ); + $_POST['ping_status'] = get_option( 'default_ping_status' ); + + edit_post(); + wp_dashboard_quick_press(); + exit; + +case 'postajaxpost': +case 'post': + check_admin_referer( 'add-' . $post_type ); + $post_id = 'postajaxpost' == $action ? edit_post() : write_post(); + redirect_post( $post_id ); + exit(); + +case 'edit': + $editing = true; + + if ( empty( $post_id ) ) { + wp_redirect( admin_url('post.php') ); + exit(); + } + + if ( ! $post ) + wp_die( __( 'You attempted to edit an item that doesn’t exist. Perhaps it was deleted?' ) ); + + if ( ! $post_type_object ) + wp_die( __( 'Unknown post type.' ) ); + + if ( ! current_user_can( 'edit_post', $post_id ) ) + wp_die( __( 'You are not allowed to edit this item.' ) ); + + if ( 'trash' == $post->post_status ) + wp_die( __( 'You can’t edit this item because it is in the Trash. Please restore it and try again.' ) ); + + if ( ! empty( $_GET['get-post-lock'] ) ) { + wp_set_post_lock( $post_id ); + wp_redirect( get_edit_post_link( $post_id, 'url' ) ); + exit(); + } + + $post_type = $post->post_type; + if ( 'post' == $post_type ) { + $parent_file = "edit.php"; + $submenu_file = "edit.php"; + $post_new_file = "post-new.php"; + } elseif ( 'attachment' == $post_type ) { + $parent_file = 'upload.php'; + $submenu_file = 'upload.php'; + $post_new_file = 'media-new.php'; + } else { + if ( isset( $post_type_object ) && $post_type_object->show_in_menu && $post_type_object->show_in_menu !== true ) + $parent_file = $post_type_object->show_in_menu; + else + $parent_file = "edit.php?post_type=$post_type"; + $submenu_file = "edit.php?post_type=$post_type"; + $post_new_file = "post-new.php?post_type=$post_type"; + } + + if ( ! wp_check_post_lock( $post->ID ) ) { + $active_post_lock = wp_set_post_lock( $post->ID ); + + if ( 'attachment' !== $post_type ) + wp_enqueue_script('autosave'); + } + + if ( is_multisite() ) { + add_action( 'admin_footer', '_admin_notice_post_locked' ); + } else { + $check_users = get_users( array( 'fields' => 'ID', 'number' => 2 ) ); + + if ( count( $check_users ) > 1 ) + add_action( 'admin_footer', '_admin_notice_post_locked' ); + + unset( $check_users ); + } + + $title = $post_type_object->labels->edit_item; + $post = get_post($post_id, OBJECT, 'edit'); + + if ( post_type_supports($post_type, 'comments') ) { + wp_enqueue_script('admin-comments'); + enqueue_comment_hotkeys_js(); + } + + include( ABSPATH . 'wp-admin/edit-form-advanced.php' ); + + break; + +case 'editattachment': + check_admin_referer('update-post_' . $post_id); + + // Don't let these be changed + unset($_POST['guid']); + $_POST['post_type'] = 'attachment'; + + // Update the thumbnail filename + $newmeta = wp_get_attachment_metadata( $post_id, true ); + $newmeta['thumb'] = $_POST['thumb']; + + wp_update_attachment_metadata( $post_id, $newmeta ); + +case 'editpost': + check_admin_referer('update-post_' . $post_id); + + $post_id = edit_post(); + + // Session cookie flag that the post was saved + if ( isset( $_COOKIE['wp-saving-post'] ) && $_COOKIE['wp-saving-post'] === $post_id . '-check' ) { + setcookie( 'wp-saving-post', $post_id . '-saved', time() + DAY_IN_SECONDS ); + } + + redirect_post($post_id); // Send user on their way while we keep working + + exit(); + +case 'trash': + check_admin_referer('trash-post_' . $post_id); + + if ( ! $post ) + wp_die( __( 'The item you are trying to move to the Trash no longer exists.' ) ); + + if ( ! $post_type_object ) + wp_die( __( 'Unknown post type.' ) ); + + if ( ! current_user_can( 'delete_post', $post_id ) ) + wp_die( __( 'You are not allowed to move this item to the Trash.' ) ); + + if ( $user_id = wp_check_post_lock( $post_id ) ) { + $user = get_userdata( $user_id ); + wp_die( sprintf( __( 'You cannot move this item to the Trash. %s is currently editing.' ), $user->display_name ) ); + } + + if ( ! wp_trash_post( $post_id ) ) + wp_die( __( 'Error in moving to Trash.' ) ); + + wp_redirect( add_query_arg( array('trashed' => 1, 'ids' => $post_id), $sendback ) ); + exit(); + +case 'untrash': + check_admin_referer('untrash-post_' . $post_id); + + if ( ! $post ) + wp_die( __( 'The item you are trying to restore from the Trash no longer exists.' ) ); + + if ( ! $post_type_object ) + wp_die( __( 'Unknown post type.' ) ); + + if ( ! current_user_can( 'delete_post', $post_id ) ) + wp_die( __( 'You are not allowed to move this item out of the Trash.' ) ); + + if ( ! wp_untrash_post( $post_id ) ) + wp_die( __( 'Error in restoring from Trash.' ) ); + + wp_redirect( add_query_arg('untrashed', 1, $sendback) ); + exit(); + +case 'delete': + check_admin_referer('delete-post_' . $post_id); + + if ( ! $post ) + wp_die( __( 'This item has already been deleted.' ) ); + + if ( ! $post_type_object ) + wp_die( __( 'Unknown post type.' ) ); + + if ( ! current_user_can( 'delete_post', $post_id ) ) + wp_die( __( 'You are not allowed to delete this item.' ) ); + + $force = ! EMPTY_TRASH_DAYS; + if ( $post->post_type == 'attachment' ) { + $force = ( $force || ! MEDIA_TRASH ); + if ( ! wp_delete_attachment( $post_id, $force ) ) + wp_die( __( 'Error in deleting.' ) ); + } else { + if ( ! wp_delete_post( $post_id, $force ) ) + wp_die( __( 'Error in deleting.' ) ); + } + + wp_redirect( add_query_arg('deleted', 1, $sendback) ); + exit(); + +case 'preview': + check_admin_referer( 'update-post_' . $post_id ); + + $url = post_preview(); + + wp_redirect($url); + exit(); + +default: + wp_redirect( admin_url('edit.php') ); + exit(); +} // end switch +include( ABSPATH . 'wp-admin/admin-footer.php' ); diff --git a/wp-admin/press-this.php b/wp-admin/press-this.php new file mode 100644 index 0000000..6ae7341 --- /dev/null +++ b/wp-admin/press-this.php @@ -0,0 +1,691 @@ +cap->create_posts ) ) + wp_die( __( 'Cheatin’ uh?' ), 403 ); + +/** + * Press It form handler. + * + * @since 2.6.0 + * + * @return int Post ID + */ +function press_it() { + + $post = get_default_post_to_edit(); + $post = get_object_vars($post); + $post_ID = $post['ID'] = (int) $_POST['post_id']; + + if ( !current_user_can('edit_post', $post_ID) ) + wp_die(__('You are not allowed to edit this post.')); + + $post['post_category'] = isset($_POST['post_category']) ? $_POST['post_category'] : ''; + $post['tax_input'] = isset($_POST['tax_input']) ? $_POST['tax_input'] : ''; + $post['post_title'] = isset($_POST['title']) ? $_POST['title'] : ''; + $content = isset($_POST['content']) ? $_POST['content'] : ''; + + $upload = false; + if ( !empty($_POST['photo_src']) && current_user_can('upload_files') ) { + foreach( (array) $_POST['photo_src'] as $key => $image) { + // See if files exist in content - we don't want to upload non-used selected files. + if ( strpos($_POST['content'], htmlspecialchars($image)) !== false ) { + $desc = isset($_POST['photo_description'][$key]) ? $_POST['photo_description'][$key] : ''; + $upload = media_sideload_image($image, $post_ID, $desc); + + // Replace the POSTED content with correct uploaded ones. Regex contains fix for Magic Quotes + if ( !is_wp_error($upload) ) + $content = preg_replace('/]*)src=\\\?(\"|\')'.preg_quote(htmlspecialchars($image), '/').'\\\?(\2)([^>\/]*)\/*>/is', $upload, $content); + } + } + } + // Set the post_content and status. + $post['post_content'] = $content; + if ( isset( $_POST['publish'] ) && current_user_can( 'publish_posts' ) ) + $post['post_status'] = 'publish'; + elseif ( isset( $_POST['review'] ) ) + $post['post_status'] = 'pending'; + else + $post['post_status'] = 'draft'; + + // Error handling for media_sideload. + if ( is_wp_error($upload) ) { + wp_delete_post($post_ID); + wp_die( esc_html( $upload->get_error_message() ) ); + } else { + // Post formats. + if ( isset( $_POST['post_format'] ) ) { + if ( current_theme_supports( 'post-formats', $_POST['post_format'] ) ) + set_post_format( $post_ID, $_POST['post_format'] ); + elseif ( '0' == $_POST['post_format'] ) + set_post_format( $post_ID, false ); + } + + $post_ID = wp_update_post($post); + } + + return $post_ID; +} + +// For submitted posts. +if ( isset($_REQUEST['action']) && 'post' == $_REQUEST['action'] ) { + check_admin_referer('press-this'); + $posted = $post_ID = press_it(); +} else { + $post = get_default_post_to_edit('post', true); + $post_ID = $post->ID; +} + +// Set Variables +$title = isset( $_GET['t'] ) ? trim( strip_tags( html_entity_decode( wp_unslash( $_GET['t'] ) , ENT_QUOTES) ) ) : ''; + +$selection = ''; +if ( !empty($_GET['s']) ) { + $selection = str_replace(''', "'", wp_unslash($_GET['s'])); + $selection = trim( htmlspecialchars( html_entity_decode($selection, ENT_QUOTES) ) ); +} + +if ( ! empty($selection) ) { + $selection = preg_replace('/(\r?\n|\r)/', '

    ', $selection); + $selection = '

    ' . str_replace('

    ', '', $selection) . '

    '; +} + +$url = isset($_GET['u']) ? esc_url($_GET['u']) : ''; +$image = isset($_GET['i']) ? $_GET['i'] : ''; + +if ( !empty($_REQUEST['ajax']) ) { + switch ($_REQUEST['ajax']) { + case 'video': ?> + +
    +

    +
    + +

    +
    +
    + + +

    +
    +
    + +
    +
    + +

    + + + <?php esc_attr_e( 'Click to insert.' ); ?> + +

    + +

    + ]*)src=(\"|\')([^<>\'\"]+)(\2)([^>]*)\/*>/i'; + $content = str_replace(array("\n","\t","\r"), '', $content); + preg_match_all($pattern, $content, $matches); + if ( empty($matches[0]) ) + return ''; + $sources = array(); + foreach ($matches[3] as $src) { + + // If no http in URL. + if (strpos($src, 'http') === false) + // If it doesn't have a relative URI. + if ( strpos($src, '../') === false && strpos($src, './') === false && strpos($src, '/') === 0) + $src = 'http://'.str_replace('//','/', $host['host'].'/'.$src); + else + $src = 'http://'.str_replace('//','/', $host['host'].'/'.dirname($host['path']).'/'.$src); + $sources[] = esc_url($src); + } + return "'" . implode("','", $sources) . "'"; + } + $url = wp_kses(urldecode($url), null); + echo 'new Array('.get_images_from_uri($url).')'; + break; + + case 'photo_js': ?> + // Gather images and load some default JS. + var last = null + var img, img_tag, aspect, w, h, skip, i, strtoappend = ""; + if(photostorage == false) { + var my_src = eval( + jQuery.ajax({ + type: "GET", + url: "", + cache : false, + async : false, + data: "ajax=photo_images&u=", + dataType : "script" + }).responseText + ); + if(my_src.length == 0) { + var my_src = eval( + jQuery.ajax({ + type: "GET", + url: "", + cache : false, + async : false, + data: "ajax=photo_images&u=", + dataType : "script" + }).responseText + ); + if(my_src.length == 0) { + strtoappend = ''; + } + } + } + for (i = 0; i < my_src.length; i++) { + img = new Image(); + img.src = my_src[i]; + img_attr = 'id="img' + i + '"'; + skip = false; + + maybeappend = ''; + + if (img.width && img.height) { + if (img.width >= 30 && img.height >= 30) { + aspect = img.width / img.height; + scale = (aspect > 1) ? (71 / img.width) : (71 / img.height); + + w = img.width; + h = img.height; + + if (scale < 1) { + w = parseInt(img.width * scale); + h = parseInt(img.height * scale); + } + img_attr += ' style="width: ' + w + 'px; height: ' + h + 'px;"'; + strtoappend += maybeappend; + } + } else { + strtoappend += maybeappend; + } + } + + function pick(img, desc) { + if (img) { + if('object' == typeof jQuery('.photolist input') && jQuery('.photolist input').length != 0) length = jQuery('.photolist input').length; + if(length == 0) length = 1; + jQuery('.photolist').append(''); + jQuery('.photolist').append(''); + insert_editor( "\n\n" + encodeURI('

    ' + desc + '

    ')); + } + return false; + } + + function image_selector(el) { + var desc, src, parent = jQuery(el).closest('#photo-add-url-div'); + + if ( parent.length ) { + desc = parent.find('input.tb_this_photo_description').val() || ''; + src = parent.find('input.tb_this_photo').val() || '' + } else { + desc = jQuery('#tb_this_photo_description').val() || ''; + src = jQuery('#tb_this_photo').val() || '' + } + + tb_remove(); + pick(src, desc); + jQuery('#extra-fields').hide(); + jQuery('#extra-fields').html(''); + return false; + } + + jQuery('#extra-fields').html('

    ()

    '); + jQuery('#img_container').html(strtoappend); + +<?php _e('Press This') ?> + + + + + + + +
    +
    +
    +
    + + + + + + + + +
    + +
    +

    +

    +
    +

    + 'save' ) ); + if ( current_user_can('publish_posts') ) { + submit_button( __( 'Publish' ), 'primary', 'publish', false ); + } else { + echo '

    '; + submit_button( __( 'Submit for Review' ), 'primary', 'review', false ); + } ?> + +

    + +

    + +

    + +
    +
    + + +
    +

    +

    +
    +
    + + + + + +
    +
      + 'category', 'popular_cats' => $popular_ids ) ) ?> +
    +
    + + cap->assign_terms) ) : ?> +

    + + cap->edit_terms) ) : ?> +
    +

    + + labels->add_new_item ); ?> + +

    +

    + + + + 'category', 'hide_empty' => 0, 'name' => 'newcategory_parent', 'orderby' => 'name', 'hierarchical' => 1, 'show_option_none' => '— ' . $tax->labels->parent_item . ' —' ) ); ?> + + + +

    +
    + +
    +
    +
    + +
    +

    +

    +
    +
    +
    + + +
    + + +
    +
    +
    +
    + +
    +
    +
    +
    +
    + + + + +
    +

    + + | + |

    +
    + + +
    +
    + +
    +
    + + + + + +
    + true, + 'textarea_rows' => '15' + ); + + $content = ''; + if ( $selection ) + $content .= $selection; + + if ( $url ) { + $content .= '

    '; + + if ( $selection ) + $content .= __('via '); + + $content .= sprintf( "%s.

    ", esc_url( $url ), esc_html( $title ) ); + } + + remove_action( 'media_buttons', 'media_buttons' ); + add_action( 'media_buttons', 'press_this_media_buttons' ); + function press_this_media_buttons() { + _e( 'Add:' ); + + if ( current_user_can('upload_files') ) { + ?> + + <?php esc_attr_e('Insert an Image'); ?> + + <?php esc_attr_e('Embed a Video'); ?> + +
    +
    +
    +
    + + + + + diff --git a/wp-admin/profile.php b/wp-admin/profile.php new file mode 100644 index 0000000..0f33eb2 --- /dev/null +++ b/wp-admin/profile.php @@ -0,0 +1,18 @@ +post_parent ) ) + break; + + if ( ! $post = get_post( $revision->post_parent ) ) + break; + + // Revisions disabled (previously checked autosaves && ! wp_is_post_autosave( $revision )) + if ( ! wp_revisions_enabled( $post ) ) { + $redirect = 'edit.php?post_type=' . $post->post_type; + break; + } + + // Don't allow revision restore when post is locked + if ( wp_check_post_lock( $post->ID ) ) + break; + + check_admin_referer( "restore-post_{$revision->ID}" ); + + wp_restore_post_revision( $revision->ID ); + $redirect = add_query_arg( array( 'message' => 5, 'revision' => $revision->ID ), get_edit_post_link( $post->ID, 'url' ) ); + break; +case 'view' : +case 'edit' : +default : + if ( ! $revision = wp_get_post_revision( $revision_id ) ) + break; + if ( ! $post = get_post( $revision->post_parent ) ) + break; + + if ( ! current_user_can( 'read_post', $revision->ID ) || ! current_user_can( 'read_post', $post->ID ) ) + break; + + // Revisions disabled and we're not looking at an autosave + if ( ! wp_revisions_enabled( $post ) && ! wp_is_post_autosave( $revision ) ) { + $redirect = 'edit.php?post_type=' . $post->post_type; + break; + } + + $post_edit_link = get_edit_post_link(); + $post_title = '' . _draft_or_post_title() . ''; + $h2 = sprintf( __( 'Compare Revisions of “%1$s”' ), $post_title ); + $return_to_post = '' . __( '← Return to post editor' ) . ''; + $title = __( 'Revisions' ); + + $redirect = false; + break; +} + +// Empty post_type means either malformed object found, or no valid parent was found. +if ( ! $redirect && empty( $post->post_type ) ) + $redirect = 'edit.php'; + +if ( ! empty( $redirect ) ) { + wp_redirect( $redirect ); + exit; +} + +// This is so that the correct "Edit" menu item is selected. +if ( ! empty( $post->post_type ) && 'post' != $post->post_type ) + $parent_file = $submenu_file = 'edit.php?post_type=' . $post->post_type; +else + $parent_file = $submenu_file = 'edit.php'; + +wp_enqueue_script( 'revisions' ); +wp_localize_script( 'revisions', '_wpRevisionsSettings', wp_prepare_revisions_for_js( $post, $revision_id, $from ) ); + +/* Revisions Help Tab */ + +$revisions_overview = '

    ' . __( 'This screen is used for managing your content revisions.' ) . '

    '; +$revisions_overview .= '

    ' . __( 'Revisions are saved copies of your post or page, which are periodically created as you update your content. The red text on the left shows the content that was removed. The green text on the right shows the content that was added.' ) . '

    '; +$revisions_overview .= '

    ' . __( 'From this screen you can review, compare, and restore revisions:' ) . '

    '; +$revisions_overview .= '
    • ' . __( 'To navigate between revisions, drag the slider handle left or right or use the Previous or Next buttons.' ) . '
    • '; +$revisions_overview .= '
    • ' . __( 'Compare two different revisions by selecting the “Compare any two revisions” box to the side.' ) . '
    • '; +$revisions_overview .= '
    • ' . __( 'To restore a revision, click Restore This Revision.' ) . '
    '; + +get_current_screen()->add_help_tab( array( + 'id' => 'revisions-overview', + 'title' => __( 'Overview' ), + 'content' => $revisions_overview +) ); + +$revisions_sidebar = '

    ' . __( 'For more information:' ) . '

    '; +$revisions_sidebar .= '

    ' . __( 'Revisions Management' ) . '

    '; +$revisions_sidebar .= '

    ' . __( 'Support Forums' ) . '

    '; + +get_current_screen()->set_help_sidebar( $revisions_sidebar ); + +require_once( ABSPATH . 'wp-admin/admin-header.php' ); + +?> + +
    +

    + +
    +' . sprintf( __( "The file 'wp-config.php' already exists. If you need to reset any of the configuration items in this file, please delete it first. You may try installing now." ), 'install.php' ) . '

    ' ); + +// Check if wp-config.php exists above the root directory but is not part of another install +if ( file_exists(ABSPATH . '../wp-config.php' ) && ! file_exists( ABSPATH . '../wp-settings.php' ) ) + wp_die( '

    ' . sprintf( __( "The file 'wp-config.php' already exists one level above your WordPress installation. If you need to reset any of the configuration items in this file, please delete it first. You may try installing now."), 'install.php' ) . '

    ' ); + +$step = isset( $_GET['step'] ) ? (int) $_GET['step'] : -1; + +/** + * Display setup wp-config.php file header. + * + * @ignore + * @since 2.3.0 + */ +function setup_config_display_header( $body_classes = array() ) { + global $wp_version; + $body_classes = (array) $body_classes; + $body_classes[] = 'wp-core-ui'; + if ( is_rtl() ) { + $body_classes[] = 'rtl'; + } + + header( 'Content-Type: text/html; charset=utf-8' ); +?> + +> + + + + <?php _e( 'WordPress › Setup Configuration File' ); ?> + + + +

    +'; + wp_install_language_form( $languages ); + echo ''; + break; + } + + // Deliberately fall through if we can't reach the translations API. + + case 0: + if ( ! empty( $language ) ) { + $loaded_language = wp_download_language_pack( $language ); + if ( $loaded_language ) { + load_default_textdomain( $loaded_language ); + $GLOBALS['wp_locale'] = new WP_Locale(); + } + } + + setup_config_display_header(); + $step_1 = 'setup-config.php?step=1'; + if ( isset( $_REQUEST['noapi'] ) ) { + $step_1 .= '&noapi'; + } + if ( ! empty( $loaded_language ) ) { + $step_1 .= '&language=' . $loaded_language; + } +?> + +

    +
      +
    1. +
    2. +
    3. +
    4. +
    5. +
    +

    + wp-config.php file.' ); ?> + wp-config-sample.php in a text editor, fill in your information, and save it as wp-config.php." ); ?> + We got it." ); ?> +

    +

    + +

    + +
    +

    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    localhost does not work.' ); ?>
    + + +

    +
    +

    ' . __( 'Try again' ) . ''; + + if ( empty( $prefix ) ) + wp_die( __( 'ERROR: "Table Prefix" must not be empty.' . $tryagain_link ) ); + + // Validate $prefix: it can only contain letters, numbers and underscores. + if ( preg_match( '|[^a-z0-9_]|i', $prefix ) ) + wp_die( __( 'ERROR: "Table Prefix" can only contain numbers, letters, and underscores.' . $tryagain_link ) ); + + // Test the db connection. + /**#@+ + * @ignore + */ + define('DB_NAME', $dbname); + define('DB_USER', $uname); + define('DB_PASSWORD', $pwd); + define('DB_HOST', $dbhost); + /**#@-*/ + + // Re-construct $wpdb with these new values. + unset( $wpdb ); + require_wp_db(); + + /* + * The wpdb constructor bails when WP_SETUP_CONFIG is set, so we must + * fire this manually. We'll fail here if the values are no good. + */ + $wpdb->db_connect(); + + if ( ! empty( $wpdb->error ) ) + wp_die( $wpdb->error->get_error_message() . $tryagain_link ); + + // Fetch or generate keys and salts. + $no_api = isset( $_POST['noapi'] ); + if ( ! $no_api ) { + $secret_keys = wp_remote_get( 'https://api.wordpress.org/secret-key/1.1/salt/' ); + } + + if ( $no_api || is_wp_error( $secret_keys ) ) { + $secret_keys = array(); + for ( $i = 0; $i < 8; $i++ ) { + $secret_keys[] = wp_generate_password( 64, true, true ); + } + } else { + $secret_keys = explode( "\n", wp_remote_retrieve_body( $secret_keys ) ); + foreach ( $secret_keys as $k => $v ) { + $secret_keys[$k] = substr( $v, 28, 64 ); + } + } + + $key = 0; + // Not a PHP5-style by-reference foreach, as this file must be parseable by PHP4. + foreach ( $config_file as $line_num => $line ) { + if ( '$table_prefix =' == substr( $line, 0, 16 ) ) { + $config_file[ $line_num ] = '$table_prefix = \'' . addcslashes( $prefix, "\\'" ) . "';\r\n"; + continue; + } + + if ( ! preg_match( '/^define\(\'([A-Z_]+)\',([ ]+)/', $line, $match ) ) + continue; + + $constant = $match[1]; + $padding = $match[2]; + + switch ( $constant ) { + case 'DB_NAME' : + case 'DB_USER' : + case 'DB_PASSWORD' : + case 'DB_HOST' : + $config_file[ $line_num ] = "define('" . $constant . "'," . $padding . "'" . addcslashes( constant( $constant ), "\\'" ) . "');\r\n"; + break; + case 'AUTH_KEY' : + case 'SECURE_AUTH_KEY' : + case 'LOGGED_IN_KEY' : + case 'NONCE_KEY' : + case 'AUTH_SALT' : + case 'SECURE_AUTH_SALT' : + case 'LOGGED_IN_SALT' : + case 'NONCE_SALT' : + $config_file[ $line_num ] = "define('" . $constant . "'," . $padding . "'" . $secret_keys[$key++] . "');\r\n"; + break; + } + } + unset( $line ); + + if ( ! is_writable(ABSPATH) ) : + setup_config_display_header(); +?> +

    wp-config.php file." ); ?>

    +

    wp-config.php manually and paste the following text into it.' ); ?>

    + +

    +

    + + +

    + +

    + + + + diff --git a/wp-admin/theme-editor.php b/wp-admin/theme-editor.php new file mode 100644 index 0000000..d0ecee4 --- /dev/null +++ b/wp-admin/theme-editor.php @@ -0,0 +1,243 @@ +'.__('You do not have sufficient permissions to edit templates for this site.').'

    '); + +$title = __("Edit Themes"); +$parent_file = 'themes.php'; + +get_current_screen()->add_help_tab( array( +'id' => 'overview', +'title' => __('Overview'), +'content' => + '

    ' . __('You can use the Theme Editor to edit the individual CSS and PHP files which make up your theme.') . '

    +

    ' . __('Begin by choosing a theme to edit from the dropdown menu and clicking Select. A list then appears of all the template files. Clicking once on any file name causes the file to appear in the large Editor box.') . '

    +

    ' . __('For PHP files, you can use the Documentation dropdown to select from functions recognized in that file. Look Up takes you to a web page with reference material about that particular function.') . '

    +

    ' . __('In the editing area the Tab key enters a tab character. To move below this area by pressing Tab, press the Esc key followed by the Tab key.') . '

    +

    ' . __('After typing in your edits, click Update File.') . '

    +

    ' . __('Advice: think very carefully about your site crashing if you are live-editing the theme currently in use.') . '

    +

    ' . sprintf( __('Upgrading to a newer version of the same theme will override changes made here. To avoid this, consider creating a child theme instead.'), __('http://codex.wordpress.org/Child_Themes') ) . '

    ' . + ( is_network_admin() ? '

    ' . __('Any edits to files from this screen will be reflected on all sites in the network.') . '

    ' : '' ) +) ); + +get_current_screen()->set_help_sidebar( + '

    ' . __('For more information:') . '

    ' . + '

    ' . __('Documentation on Theme Development') . '

    ' . + '

    ' . __('Documentation on Using Themes') . '

    ' . + '

    ' . __('Documentation on Editing Files') . '

    ' . + '

    ' . __('Documentation on Template Tags') . '

    ' . + '

    ' . __('Support Forums') . '

    ' +); + +wp_reset_vars( array( 'action', 'error', 'file', 'theme' ) ); + +if ( $theme ) + $stylesheet = $theme; +else + $stylesheet = get_stylesheet(); + +$theme = wp_get_theme( $stylesheet ); + +if ( ! $theme->exists() ) + wp_die( __( 'The requested theme does not exist.' ) ); + +if ( $theme->errors() && 'theme_no_stylesheet' == $theme->errors()->get_error_code() ) + wp_die( __( 'The requested theme does not exist.' ) . ' ' . $theme->errors()->get_error_message() ); + +$allowed_files = $theme->get_files( 'php', 1 ); +$has_templates = ! empty( $allowed_files ); +$style_files = $theme->get_files( 'css' ); +$allowed_files['style.css'] = $style_files['style.css']; +$allowed_files += $style_files; + +if ( empty( $file ) ) { + $relative_file = 'style.css'; + $file = $allowed_files['style.css']; +} else { + $relative_file = $file; + $file = $theme->get_stylesheet_directory() . '/' . $relative_file; +} + +validate_file_to_edit( $file, $allowed_files ); +$scrollto = isset( $_REQUEST['scrollto'] ) ? (int) $_REQUEST['scrollto'] : 0; + +switch( $action ) { +case 'update': + check_admin_referer( 'edit-theme_' . $file . $stylesheet ); + $newcontent = wp_unslash( $_POST['newcontent'] ); + $location = 'theme-editor.php?file=' . urlencode( $relative_file ) . '&theme=' . urlencode( $stylesheet ) . '&scrollto=' . $scrollto; + if ( is_writeable( $file ) ) { + // is_writable() not always reliable, check return value. see comments @ http://uk.php.net/is_writable + $f = fopen( $file, 'w+' ); + if ( $f !== false ) { + fwrite( $f, $newcontent ); + fclose( $f ); + $location .= '&updated=true'; + $theme->cache_delete(); + } + } + wp_redirect( $location ); + exit; + +default: + + require_once( ABSPATH . 'wp-admin/admin-header.php' ); + + update_recently_edited( $file ); + + if ( ! is_file( $file ) ) + $error = true; + + $content = ''; + if ( ! $error && filesize( $file ) > 0 ) { + $f = fopen($file, 'r'); + $content = fread($f, filesize($file)); + + if ( '.php' == substr( $file, strrpos( $file, '.' ) ) ) { + $functions = wp_doc_link_parse( $content ); + + $docs_select = ''; + } + + $content = esc_textarea( $content ); + } + + if ( isset( $_GET['updated'] ) ) : ?> +

    +(' . $file_show . ')'; +?> +
    +

    + +
    +
    +

    display('Name'); if ( $description ) echo ': ' . $description; ?>

    +
    +
    +
    + + + +
    +
    +
    +
    +errors() ) + echo '

    ' . __( 'This theme is broken.' ) . ' ' . $theme->errors()->get_error_message() . '

    '; +?> +
    +parent() ) : +?> +

    + parent() ) : ?> +

    get_template() ) ) . '">' . $theme->parent()->display('Name') . '' ); ?>

    + +
      + $absolute_filename ) : + if ( 'style.css' == $filename ) + echo "\t
    \n\t

    " . _x( 'Styles', 'Theme stylesheets in theme editor' ) . "

    \n\t
      \n"; + + $file_description = get_file_description( $absolute_filename ); + if ( $file_description != basename( $filename ) ) + $file_description .= '
      (' . $filename . ')'; + + if ( $absolute_filename == $file ) + $file_description = '' . $file_description . ''; +?> +
    • + +
    + +
    +

    ' . __('Oops, no such file exists! Double check the name and try again, merci.') . '

    '; +else : ?> +
    + +
    + + + + +
    + +
    + + + +
    + + +
    + get_stylesheet() == get_template() ) : ?> +

    +

    + + +

    the Codex for more information.'); ?>

    + +
    +
    + +
    +
    + + $v ) { + if ( false !== strpos( $k, '/' ) ) { + unset( $installed_themes[ $k ] ); + } +} + +wp_localize_script( 'theme', '_wpThemeSettings', array( + 'themes' => false, + 'settings' => array( + 'isInstall' => true, + 'canInstall' => current_user_can( 'install_themes' ), + 'installURI' => current_user_can( 'install_themes' ) ? self_admin_url( 'theme-install.php' ) : null, + 'adminUrl' => parse_url( self_admin_url(), PHP_URL_PATH ) + ), + 'l10n' => array( + 'addNew' => __( 'Add New Theme' ), + 'search' => __( 'Search Themes' ), + 'searchPlaceholder' => __( 'Search themes...' ), // placeholder (no ellipsis) + 'upload' => __( 'Upload Theme' ), + 'back' => __( 'Back' ), + 'error' => __( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server’s configuration. If you continue to have problems, please try the support forums.' ) + ), + 'installedThemes' => array_keys( $installed_themes ), +) ); + +wp_enqueue_script( 'theme' ); + +if ( $tab ) { + /** + * Fires before each of the tabs are rendered on the Install Themes page. + * + * The dynamic portion of the hook name, `$tab`, refers to the current + * theme install tab. Possible values are 'dashboard', 'search', 'upload', + * 'featured', 'new', or 'updated'. + * + * @since 2.8.0 + */ + do_action( "install_themes_pre_{$tab}" ); +} + +$help_overview = + '

    ' . sprintf(__('You can find additional themes for your site by using the Theme Browser/Installer on this screen, which will display themes from the WordPress.org Theme Directory. These themes are designed and developed by third parties, are available free of charge, and are compatible with the license WordPress uses.'), 'https://wordpress.org/themes/') . '

    ' . + '

    ' . __('You can Search for themes by keyword, author, or tag, or can get more specific and search by criteria listed in the feature filter. Alternately, you can browse the themes that are Featured, Popular, or Latest. When you find a theme you like, you can preview it or install it.') . '

    ' . + '

    ' . __('You can Upload a theme manually if you have already downloaded its ZIP archive onto your computer (make sure it is from a trusted and original source). You can also do it the old-fashioned way and copy a downloaded theme’s folder via FTP into your /wp-content/themes directory.') . '

    '; + +get_current_screen()->add_help_tab( array( + 'id' => 'overview', + 'title' => __('Overview'), + 'content' => $help_overview +) ); + +$help_installing = + '

    ' . __('Once you have generated a list of themes, you can preview and install any of them. Click on the thumbnail of the theme you’re interested in previewing. It will open up in a full-screen Preview page to give you a better idea of how that theme will look.') . '

    ' . + '

    ' . __('To install the theme so you can preview it with your site’s content and customize its theme options, click the "Install" button at the top of the left-hand pane. The theme files will be downloaded to your website automatically. When this is complete, the theme is now available for activation, which you can do by clicking the "Activate" link, or by navigating to your Manage Themes screen and clicking the "Live Preview" link under any installed theme’s thumbnail image.') . '

    '; + +get_current_screen()->add_help_tab( array( + 'id' => 'installing', + 'title' => __('Previewing and Installing'), + 'content' => $help_installing +) ); + +get_current_screen()->set_help_sidebar( + '

    ' . __('For more information:') . '

    ' . + '

    ' . __('Documentation on Adding New Themes') . '

    ' . + '

    ' . __('Support Forums') . '

    ' +); + +include(ABSPATH . 'wp-admin/admin-header.php'); + +?> +
    +

    __( 'Upload Theme' ) ) ); + if ( ! empty( $tabs['upload'] ) && current_user_can( 'upload_themes' ) ) { + echo ' ' . __( 'Upload Theme' ) . ''; + echo ' ' . _x( 'Browse', 'themes' ) . ''; + } + ?>

    + +
    + +
    + +
    +
    + +
    + + + + + +
    + +
    +
    + + +
    + $features ) { + echo '
    '; + $feature_name = esc_html( $feature_name ); + echo '

    ' . $feature_name . '

    '; + echo '
      '; + foreach ( $features as $feature => $feature_name ) { + $feature = esc_attr( $feature ); + echo '
    1. '; + echo '
    2. '; + } + echo '
    '; + echo '
    '; + } + ?> +
    + +
    + +
    +
    +
    +
    +
    + +

    + + +
    + +
    + + + + + +exists() || ! $theme->is_allowed() ) + wp_die( __( 'Cheatin’ uh?' ), 403 ); + switch_theme( $theme->get_stylesheet() ); + wp_redirect( admin_url('themes.php?activated=true') ); + exit; + } elseif ( 'delete' == $_GET['action'] ) { + check_admin_referer('delete-theme_' . $_GET['stylesheet']); + $theme = wp_get_theme( $_GET['stylesheet'] ); + if ( !current_user_can('delete_themes') || ! $theme->exists() ) + wp_die( __( 'Cheatin’ uh?' ), 403 ); + $active = wp_get_theme(); + if ( $active->get( 'Template' ) == $_GET['stylesheet'] ) { + wp_redirect( admin_url( 'themes.php?delete-active-child=true' ) ); + } else { + delete_theme( $_GET['stylesheet'] ); + wp_redirect( admin_url( 'themes.php?deleted=true' ) ); + } + exit; + } +} + +$title = __('Manage Themes'); +$parent_file = 'themes.php'; + +// Help tab: Overview +if ( current_user_can( 'switch_themes' ) ) { + $help_overview = '

    ' . __( 'This screen is used for managing your installed themes. Aside from the default theme(s) included with your WordPress installation, themes are designed and developed by third parties.' ) . '

    ' . + '

    ' . __( 'From this screen you can:' ) . '

    ' . + '
    • ' . __( 'Hover or tap to see Activate and Live Preview buttons' ) . '
    • ' . + '
    • ' . __( 'Click on the theme to see the theme name, version, author, description, tags, and the Delete link' ) . '
    • ' . + '
    • ' . __( 'Click Customize for the current theme or Live Preview for any other theme to see a live preview' ) . '
    ' . + '

    ' . __( 'The current theme is displayed highlighted as the first theme.' ) . '

    '; + + get_current_screen()->add_help_tab( array( + 'id' => 'overview', + 'title' => __( 'Overview' ), + 'content' => $help_overview + ) ); +} // switch_themes + +// Help tab: Adding Themes +if ( current_user_can( 'install_themes' ) ) { + if ( is_multisite() ) { + $help_install = '

    ' . __('Installing themes on Multisite can only be done from the Network Admin section.') . '

    '; + } else { + $help_install = '

    ' . sprintf( __('If you would like to see more themes to choose from, click on the “Add New” button and you will be able to browse or search for additional themes from the WordPress.org Theme Directory. Themes in the WordPress.org Theme Directory are designed and developed by third parties, and are compatible with the license WordPress uses. Oh, and they’re free!'), 'https://wordpress.org/themes/' ) . '

    '; + } + + get_current_screen()->add_help_tab( array( + 'id' => 'adding-themes', + 'title' => __('Adding Themes'), + 'content' => $help_install + ) ); +} // install_themes + +// Help tab: Previewing and Customizing +if ( current_user_can( 'edit_theme_options' ) && current_user_can( 'customize' ) ) { + $help_customize = + '

    ' . __( 'Tap or hover on any theme then click the Live Preview button to see a live preview of that theme and change theme options in a separate, full-screen view. You can also find a Live Preview button at the bottom of the theme details screen. Any installed theme can be previewed and customized in this way.' ) . '

    '. + '

    ' . __( 'The theme being previewed is fully interactive — navigate to different pages to see how the theme handles posts, archives, and other page templates. The settings may differ depending on what theme features the theme being previewed supports. To accept the new settings and activate the theme all in one step, click the Save & Activate button above the menu.' ) . '

    ' . + '

    ' . __( 'When previewing on smaller monitors, you can use the collapse icon at the bottom of the left-hand pane. This will hide the pane, giving you more room to preview your site in the new theme. To bring the pane back, click on the collapse icon again.' ) . '

    '; + + get_current_screen()->add_help_tab( array( + 'id' => 'customize-preview-themes', + 'title' => __( 'Previewing and Customizing' ), + 'content' => $help_customize + ) ); +} // edit_theme_options && customize + +get_current_screen()->set_help_sidebar( + '

    ' . __( 'For more information:' ) . '

    ' . + '

    ' . __( 'Documentation on Using Themes' ) . '

    ' . + '

    ' . __( 'Support Forums' ) . '

    ' +); + +if ( current_user_can( 'switch_themes' ) ) { + $themes = wp_prepare_themes_for_js(); +} else { + $themes = wp_prepare_themes_for_js( array( wp_get_theme() ) ); +} +wp_reset_vars( array( 'theme', 'search' ) ); + +wp_localize_script( 'theme', '_wpThemeSettings', array( + 'themes' => $themes, + 'settings' => array( + 'canInstall' => ( ! is_multisite() && current_user_can( 'install_themes' ) ), + 'installURI' => ( ! is_multisite() && current_user_can( 'install_themes' ) ) ? admin_url( 'theme-install.php' ) : null, + 'confirmDelete' => __( "Are you sure you want to delete this theme?\n\nClick 'Cancel' to go back, 'OK' to confirm the delete." ), + 'adminUrl' => parse_url( admin_url(), PHP_URL_PATH ), + ), + 'l10n' => array( + 'addNew' => __( 'Add New Theme' ), + 'search' => __( 'Search Installed Themes' ), + 'searchPlaceholder' => __( 'Search installed themes...' ), // placeholder (no ellipsis) + ), +) ); + +add_thickbox(); +wp_enqueue_script( 'theme' ); +wp_enqueue_script( 'customize-loader' ); + +require_once( ABSPATH . 'wp-admin/admin-header.php' ); +?> + +
    +

    + + + + +

    + +

    + +

    Visit site' ), home_url( '/' ) ); ?>

    + +

    Visit site' ), home_url( '/' ) ); ?>

    +

    + +

    +errors() && ( ! is_multisite() || current_user_can( 'manage_network_themes' ) ) ) { + echo '

    ' . sprintf( __( 'ERROR: %s' ), $ct->errors()->get_error_message() ) . '

    '; +} + +/* +// Certain error codes are less fatal than others. We can still display theme information in most cases. +if ( ! $ct->errors() || ( 1 == count( $ct->errors()->get_error_codes() ) + && in_array( $ct->errors()->get_error_code(), array( 'theme_no_parent', 'theme_parent_invalid', 'theme_no_index' ) ) ) ) : ?> +*/ + + // Pretend you didn't see this. + $current_theme_actions = array(); + if ( is_array( $submenu ) && isset( $submenu['themes.php'] ) ) { + foreach ( (array) $submenu['themes.php'] as $item) { + $class = ''; + if ( 'themes.php' == $item[2] || 'theme-editor.php' == $item[2] || 0 === strpos( $item[2], 'customize.php' ) ) + continue; + // 0 = name, 1 = capability, 2 = file + if ( ( strcmp($self, $item[2]) == 0 && empty($parent_file)) || ($parent_file && ($item[2] == $parent_file)) ) + $class = ' current'; + if ( !empty($submenu[$item[2]]) ) { + $submenu[$item[2]] = array_values($submenu[$item[2]]); // Re-index. + $menu_hook = get_plugin_page_hook($submenu[$item[2]][0][2], $item[2]); + if ( file_exists(WP_PLUGIN_DIR . "/{$submenu[$item[2]][0][2]}") || !empty($menu_hook)) + $current_theme_actions[] = "{$item[0]}"; + else + $current_theme_actions[] = "{$item[0]}"; + } elseif ( ! empty( $item[2] ) && current_user_can( $item[1] ) ) { + $menu_file = $item[2]; + + if ( current_user_can( 'customize' ) ) { + if ( 'custom-header' === $menu_file ) { + $current_theme_actions[] = "{$item[0]}"; + } elseif ( 'custom-background' === $menu_file ) { + $current_theme_actions[] = "{$item[0]}"; + } + } + + if ( false !== ( $pos = strpos( $menu_file, '?' ) ) ) { + $menu_file = substr( $menu_file, 0, $pos ); + } + + if ( file_exists( ABSPATH . "wp-admin/$menu_file" ) ) { + $current_theme_actions[] = "{$item[0]}"; + } else { + $current_theme_actions[] = "{$item[0]}"; + } + } + } + } + +?> + +
    +
    + + +
    + +
    + +
    + +
    + + +
    + + +

    + +

    + + +
    + + + + + + + + + + + + + +
    + + +
    + +
    + +
    +
    +
    +
    + +

    + + true ) ) ) { +?> + +
    +

    +

    + + + + + + + + + + + + + + + + get_stylesheet(); + $delete_url = add_query_arg( array( + 'action' => 'delete', + 'stylesheet' => urlencode( $stylesheet ), + ), admin_url( 'themes.php' ) ); + $delete_url = wp_nonce_url( $delete_url, 'delete-theme_' . $stylesheet ); + ?> + + + + +
    get( 'Name' ) ? $broken_theme->display( 'Name' ) : $broken_theme->get_stylesheet(); ?>errors()->get_error_message(); ?>
    +
    + + +
    + + + + + + +add_help_tab( array( + 'id' => 'press-this', + 'title' => __('Press This'), + 'content' => '

    ' . __('Press This is a bookmarklet that makes it easy to blog about something you come across on the web. You can use it to just grab a link, or to post an excerpt. Press This will even allow you to choose from images included on the page and use them in your post. Just drag the Press This link on this screen to your bookmarks bar in your browser, and you’ll be on your way to easier content creation. Clicking on it while on another website opens a popup window with all these options.') . '

    ', +) ); +get_current_screen()->add_help_tab( array( + 'id' => 'converter', + 'title' => __('Categories and Tags Converter'), + 'content' => '

    ' . __('Categories have hierarchy, meaning that you can nest sub-categories. Tags do not have hierarchy and cannot be nested. Sometimes people start out using one on their posts, then later realize that the other would work better for their content.' ) . '

    ' . + '

    ' . __( 'The Categories and Tags Converter link on this screen will take you to the Import screen, where that Converter is one of the plugins you can install. Once that plugin is installed, the Activate Plugin & Run Importer link will take you to a screen where you can choose to convert tags into categories or vice versa.' ) . '

    ', +) ); + +get_current_screen()->set_help_sidebar( + '

    ' . __('For more information:') . '

    ' . + '

    ' . __('Documentation on Tools') . '

    ' . + '

    ' . __('Support Forums') . '

    ' +); + +require_once( ABSPATH . 'wp-admin/admin-header.php' ); + +?> +
    +

    + + +
    +

    +

    + +

    +

    +

    + +
    +cap->manage_terms) || current_user_can($tags->cap->manage_terms) ) : ?> +
    +

    +

    Categories and Tags Converter available from the Import screen.'), 'import.php' ); ?>

    +
    + +
    +locale && 'en_US' == get_locale() ) + $version_string = $update->current; + // If the only available update is a partial builds, it doesn't need a language-specific version string. + elseif ( 'en_US' == $update->locale && $update->packages->partial && $wp_version == $update->partial_version && ( $updates = get_core_updates() ) && 1 == count( $updates ) ) + $version_string = $update->current; + else + $version_string = sprintf( "%s–%s", $update->current, $update->locale ); + + $current = false; + if ( !isset($update->response) || 'latest' == $update->response ) + $current = true; + $submit = __('Update Now'); + $form_action = 'update-core.php?action=do-core-upgrade'; + $php_version = phpversion(); + $mysql_version = $wpdb->db_version(); + $show_buttons = true; + if ( 'development' == $update->response ) { + $message = __('You are using a development version of WordPress. You can update to the latest nightly build automatically or download the nightly build and install it manually:'); + $download = __('Download nightly build'); + } else { + if ( $current ) { + $message = sprintf( __( 'If you need to re-install version %s, you can do so here or download the package and re-install manually:' ), $version_string ); + $submit = __('Re-install Now'); + $form_action = 'update-core.php?action=do-core-reinstall'; + } else { + $php_compat = version_compare( $php_version, $update->php_version, '>=' ); + if ( file_exists( WP_CONTENT_DIR . '/db.php' ) && empty( $wpdb->is_mysql ) ) + $mysql_compat = true; + else + $mysql_compat = version_compare( $mysql_version, $update->mysql_version, '>=' ); + + if ( !$mysql_compat && !$php_compat ) + $message = sprintf( __('You cannot update because WordPress %1$s requires PHP version %2$s or higher and MySQL version %3$s or higher. You are running PHP version %4$s and MySQL version %5$s.'), $update->current, $update->php_version, $update->mysql_version, $php_version, $mysql_version ); + elseif ( !$php_compat ) + $message = sprintf( __('You cannot update because WordPress %1$s requires PHP version %2$s or higher. You are running version %3$s.'), $update->current, $update->php_version, $php_version ); + elseif ( !$mysql_compat ) + $message = sprintf( __('You cannot update because WordPress %1$s requires MySQL version %2$s or higher. You are running version %3$s.'), $update->current, $update->mysql_version, $mysql_version ); + else + $message = sprintf(__('You can update to WordPress %2$s automatically or download the package and install it manually:'), $update->current, $version_string); + if ( !$mysql_compat || !$php_compat ) + $show_buttons = false; + } + $download = sprintf(__('Download %s'), $version_string); + } + + echo '

    '; + echo $message; + echo '

    '; + echo '
    '; + wp_nonce_field('upgrade-core'); + echo '

    '; + echo ''; + echo ''; + if ( $show_buttons ) { + if ( $first_pass ) { + submit_button( $submit, $current ? 'button' : 'primary regular', 'upgrade', false ); + $first_pass = false; + } else { + submit_button( $submit, 'button', 'upgrade', false ); + } + echo ' ' . $download . ' '; + } + if ( 'en_US' != $update->locale ) + if ( !isset( $update->dismissed ) || !$update->dismissed ) + submit_button( __('Hide this update'), 'button', 'dismiss', false ); + else + submit_button( __('Bring back this update'), 'button', 'undismiss', false ); + echo '

    '; + if ( 'en_US' != $update->locale && ( !isset($wp_local_package) || $wp_local_package != $update->locale ) ) + echo '

    '.__('This localized version contains both the translation and various other localization fixes. You can skip upgrading if you want to keep your current translation.').'

    '; + // Partial builds don't need language-specific warnings. + elseif ( 'en_US' == $update->locale && get_locale() != 'en_US' && ( ! $update->packages->partial && $wp_version == $update->partial_version ) ) { + echo '

    '.sprintf( __('You are about to install WordPress %s in English (US). There is a chance this update will break your translation. You may prefer to wait for the localized version to be released.'), $update->response != 'development' ? $update->current : '' ).'

    '; + } + echo '
    '; + +} + +function dismissed_updates() { + $dismissed = get_core_updates( array( 'dismissed' => true, 'available' => false ) ); + if ( $dismissed ) { + + $show_text = esc_js(__('Show hidden updates')); + $hide_text = esc_js(__('Hide hidden updates')); + ?> + + '.__('Show hidden updates').'

    '; + echo '
      '; + foreach( (array) $dismissed as $update) { + echo '
    • '; + list_core_update( $update ); + echo '
    • '; + } + echo '
    '; + } +} + +/** + * Display upgrade WordPress for downloading latest or upgrading automatically form. + * + * @since 2.7.0 + * + * @return null + */ +function core_upgrade_preamble() { + global $wp_version, $required_php_version, $required_mysql_version; + + $updates = get_core_updates(); + + if ( !isset($updates[0]->response) || 'latest' == $updates[0]->response ) { + echo '

    '; + _e('You have the latest version of WordPress.'); + + if ( wp_http_supports( array( 'ssl' ) ) ) { + require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; + $upgrader = new WP_Automatic_Updater; + $future_minor_update = (object) array( + 'current' => $wp_version . '.1.next.minor', + 'version' => $wp_version . '.1.next.minor', + 'php_version' => $required_php_version, + 'mysql_version' => $required_mysql_version, + ); + $should_auto_update = $upgrader->should_update( 'core', $future_minor_update, ABSPATH ); + if ( $should_auto_update ) + echo ' ' . __( 'Future security updates will be applied automatically.' ); + } + echo '

    '; + } else { + echo '

    '; + _e('Important: before updating, please back up your database and files. For help with updates, visit the Updating WordPress Codex page.'); + echo '

    '; + + echo '

    '; + _e( 'An updated version of WordPress is available.' ); + echo '

    '; + } + + if ( isset( $updates[0] ) && $updates[0]->response == 'development' ) { + require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; + $upgrader = new WP_Automatic_Updater; + if ( wp_http_supports( 'ssl' ) && $upgrader->should_update( 'core', $updates[0], ABSPATH ) ) { + echo '

    '; + echo '' . __( 'BETA TESTERS:' ) . ' ' . __( 'This site is set up to install updates of future beta versions automatically.' ); + echo '

    '; + } + } + + echo '
      '; + foreach( (array) $updates as $update ) { + echo '
    • '; + list_core_update( $update ); + echo '
    • '; + } + echo '
    '; + // Don't show the maintenance mode notice when we are only showing a single re-install option. + if ( $updates && ( count( $updates ) > 1 || $updates[0]->response != 'latest' ) ) { + echo '

    ' . __( 'While your site is being updated, it will be in maintenance mode. As soon as your updates are complete, your site will return to normal.' ) . '

    '; + } elseif ( ! $updates ) { + list( $normalized_version ) = explode( '-', $wp_version ); + echo '

    ' . sprintf( __( 'Learn more about WordPress %s.' ), esc_url( self_admin_url( 'about.php' ) ), $normalized_version ) . '

    '; + } + dismissed_updates(); +} + +function list_plugin_updates() { + global $wp_version; + + $cur_wp_version = preg_replace('/-.*$/', '', $wp_version); + + require_once(ABSPATH . 'wp-admin/includes/plugin-install.php'); + $plugins = get_plugin_updates(); + if ( empty( $plugins ) ) { + echo '

    ' . __( 'Plugins' ) . '

    '; + echo '

    ' . __( 'Your plugins are all up to date.' ) . '

    '; + return; + } + $form_action = 'update-core.php?action=do-plugin-upgrade'; + + $core_updates = get_core_updates(); + if ( !isset($core_updates[0]->response) || 'latest' == $core_updates[0]->response || 'development' == $core_updates[0]->response || version_compare( $core_updates[0]->current, $cur_wp_version, '=') ) + $core_update_version = false; + else + $core_update_version = $core_updates[0]->current; + ?> +

    +

    +
    + +

    + + + + + + + + + + + + + + + + $plugin_data) { + $info = plugins_api('plugin_information', array('slug' => $plugin_data->update->slug )); + if ( is_wp_error( $info ) ) { + $info = false; + } + + // Get plugin compat for running version of WordPress. + if ( isset($info->tested) && version_compare($info->tested, $cur_wp_version, '>=') ) { + $compat = '
    ' . sprintf(__('Compatibility with WordPress %1$s: 100%% (according to its author)'), $cur_wp_version); + } elseif ( isset($info->compatibility[$cur_wp_version][$plugin_data->update->new_version]) ) { + $compat = $info->compatibility[$cur_wp_version][$plugin_data->update->new_version]; + $compat = '
    ' . sprintf(__('Compatibility with WordPress %1$s: %2$d%% (%3$d "works" votes out of %4$d total)'), $cur_wp_version, $compat[0], $compat[2], $compat[1]); + } else { + $compat = '
    ' . sprintf(__('Compatibility with WordPress %1$s: Unknown'), $cur_wp_version); + } + // Get plugin compat for updated version of WordPress. + if ( $core_update_version ) { + if ( isset($info->compatibility[$core_update_version][$plugin_data->update->new_version]) ) { + $update_compat = $info->compatibility[$core_update_version][$plugin_data->update->new_version]; + $compat .= '
    ' . sprintf(__('Compatibility with WordPress %1$s: %2$d%% (%3$d "works" votes out of %4$d total)'), $core_update_version, $update_compat[0], $update_compat[2], $update_compat[1]); + } else { + $compat .= '
    ' . sprintf(__('Compatibility with WordPress %1$s: Unknown'), $core_update_version); + } + } + // Get the upgrade notice for the new plugin version. + if ( isset($plugin_data->update->upgrade_notice) ) { + $upgrade_notice = '
    ' . strip_tags($plugin_data->update->upgrade_notice); + } else { + $upgrade_notice = ''; + } + + $details_url = self_admin_url('plugin-install.php?tab=plugin-information&plugin=' . $plugin_data->update->slug . '§ion=changelog&TB_iframe=true&width=640&height=662'); + $details_text = sprintf(__('View version %1$s details'), $plugin_data->update->new_version); + $details = sprintf('%3$s.', esc_url($details_url), esc_attr($plugin_data->Name), $details_text); + + echo " + + + + "; + } +?> + +

    {$plugin_data->Name}
    " . sprintf(__('You have version %1$s installed. Update to %2$s.'), $plugin_data->Version, $plugin_data->update->new_version) . ' ' . $details . $compat . $upgrade_notice . "

    +

    +
    +' . __( 'Themes' ) . ''; + echo '

    ' . __( 'Your themes are all up to date.' ) . '

    '; + return; + } + + $form_action = 'update-core.php?action=do-theme-upgrade'; + +?> +

    +

    +

    Please Note: Any customizations you have made to theme files will be lost. Please consider using child themes for modifications.' ), __( 'http://codex.wordpress.org/Child_Themes' ) ); ?>

    +
    + +

    + + + + + + + + + + + + + + + + $theme ) { + echo " + + + + "; + } +?> + +
    " . $theme->display('Name') . ' ' . sprintf( __( 'You have version %1$s installed. Update to %2$s.' ), $theme->display('Version'), $theme->update['new_version'] ) . "
    +

    +
    +' . __( 'Translations' ) . ''; + echo '

    ' . __( 'Your translations are all up to date.' ) . '

    '; + } + return; + } + + $form_action = 'update-core.php?action=do-translation-upgrade'; + ?> +

    +
    +

    + +

    +
    + new_files ) && ! $update->new_files; + +?> +
    +

    +'; + return; + } + + if ( ! WP_Filesystem( $credentials, ABSPATH, $allow_relaxed_file_ownership ) ) { + // Failed to connect, Error and request again + request_filesystem_credentials( $url, '', true, ABSPATH, array(), $allow_relaxed_file_ownership ); + echo '
    '; + return; + } + + if ( $wp_filesystem->errors->get_error_code() ) { + foreach ( $wp_filesystem->errors->get_error_messages() as $message ) + show_message($message); + echo ''; + return; + } + + if ( $reinstall ) + $update->response = 'reinstall'; + + add_filter( 'update_feedback', 'show_message' ); + + $upgrader = new Core_Upgrader(); + $result = $upgrader->upgrade( $update, array( + 'allow_relaxed_file_ownership' => $allow_relaxed_file_ownership + ) ); + + if ( is_wp_error($result) ) { + show_message($result); + if ('up_to_date' != $result->get_error_code() ) + show_message( __('Installation Failed') ); + echo ''; + return; + } + + show_message( __('WordPress updated successfully') ); + show_message( '' . sprintf( __( 'Welcome to WordPress %1$s. You will be redirected to the About WordPress screen. If not, click here.' ), $result, esc_url( self_admin_url( 'about.php?updated' ) ) ) . '' ); + show_message( '' . sprintf( __( 'Welcome to WordPress %1$s. Learn more.' ), $result, esc_url( self_admin_url( 'about.php?updated' ) ) ) . '' ); + ?> + + + ' . __( 'On this screen, you can update to the latest version of WordPress, as well as update your themes and plugins from the WordPress.org repositories.' ) . '

    '; +$updates_overview .= '

    ' . __( 'If an update is available, you᾿ll see a notification appear in the Toolbar and navigation menu.' ) . ' ' . __( 'Keeping your site updated is important for security. It also makes the internet a safer place for you and your readers.' ) . '

    '; + +get_current_screen()->add_help_tab( array( + 'id' => 'overview', + 'title' => __( 'Overview' ), + 'content' => $updates_overview +) ); + +$updates_howto = '

    ' . __( 'WordPress — Updating your WordPress installation is a simple one-click procedure: just click on the “Update Now” button when you are notified that a new version is available.' ) . ' ' . __( 'In most cases, WordPress will automatically apply maintenance and security updates in the background for you.' ) . '

    '; +$updates_howto .= '

    ' . __( 'Themes and Plugins — To update individual themes or plugins from this screen, use the checkboxes to make your selection, then click on the appropriate “Update” button. To update all of your themes or plugins at once, you can check the box at the top of the section to select all before clicking the update button.' ) . '

    '; + +if ( 'en_US' != get_locale() ) { + $updates_howto .= '

    ' . __( 'Translations — The files translating WordPress into your language are updated for you whenever any other updates occur. But if these files are out of date, you can click the “Update Translations” button.' ) . '

    '; +} + +get_current_screen()->add_help_tab( array( + 'id' => 'how-to-update', + 'title' => __( 'How to Update' ), + 'content' => $updates_howto +) ); + +get_current_screen()->set_help_sidebar( + '

    ' . __('For more information:') . '

    ' . + '

    ' . __( 'Documentation on Updating WordPress' ) . '

    ' . + '

    ' . __( 'Support Forums' ) . '

    ' +); + +if ( 'upgrade-core' == $action ) { + // Force a update check when requested + $force_check = ! empty( $_GET['force-check'] ); + wp_version_check( array(), $force_check ); + + require_once(ABSPATH . 'wp-admin/admin-header.php'); + ?> +
    +

    +

    '; + if ( $upgrade_error == 'themes' ) + _e('Please select one or more themes to update.'); + else + _e('Please select one or more plugins to update.'); + echo '

    '; + } + + echo '

    '; + /* translators: %1 date, %2 time. */ + printf( __('Last checked on %1$s at %2$s.'), date_i18n( get_option( 'date_format' ) ), date_i18n( get_option( 'time_format' ) ) ); + echo '   ' . __( 'Check Again' ) . ''; + echo '

    '; + + if ( $core = current_user_can( 'update_core' ) ) + core_upgrade_preamble(); + if ( $plugins = current_user_can( 'update_plugins' ) ) + list_plugin_updates(); + if ( $themes = current_user_can( 'update_themes' ) ) + list_theme_updates(); + if ( $core || $plugins || $themes ) + list_translation_updates(); + unset( $core, $plugins, $themes ); + /** + * Fires after the core, plugin, and theme update tables. + * + * @since 2.9.0 + */ + do_action( 'core_upgrade_preamble' ); + echo ''; + include(ABSPATH . 'wp-admin/admin-footer.php'); + +} elseif ( 'do-core-upgrade' == $action || 'do-core-reinstall' == $action ) { + + if ( ! current_user_can( 'update_core' ) ) + wp_die( __( 'You do not have sufficient permissions to update this site.' ) ); + + check_admin_referer('upgrade-core'); + + // Do the (un)dismiss actions before headers, so that they can redirect. + if ( isset( $_POST['dismiss'] ) ) + do_dismiss_core_update(); + elseif ( isset( $_POST['undismiss'] ) ) + do_undismiss_core_update(); + + require_once(ABSPATH . 'wp-admin/admin-header.php'); + if ( 'do-core-reinstall' == $action ) + $reinstall = true; + else + $reinstall = false; + + if ( isset( $_POST['upgrade'] ) ) + do_core_upgrade($reinstall); + + include(ABSPATH . 'wp-admin/admin-footer.php'); + +} elseif ( 'do-plugin-upgrade' == $action ) { + + if ( ! current_user_can( 'update_plugins' ) ) + wp_die( __( 'You do not have sufficient permissions to update this site.' ) ); + + check_admin_referer('upgrade-core'); + + if ( isset( $_GET['plugins'] ) ) { + $plugins = explode( ',', $_GET['plugins'] ); + } elseif ( isset( $_POST['checked'] ) ) { + $plugins = (array) $_POST['checked']; + } else { + wp_redirect( admin_url('update-core.php') ); + exit; + } + + $url = 'update.php?action=update-selected&plugins=' . urlencode(implode(',', $plugins)); + $url = wp_nonce_url($url, 'bulk-update-plugins'); + + $title = __('Update Plugins'); + + require_once(ABSPATH . 'wp-admin/admin-header.php'); + echo '
    '; + echo '

    ' . esc_html__('Update Plugins') . '

    '; + echo ''; + echo '
    '; + include(ABSPATH . 'wp-admin/admin-footer.php'); + +} elseif ( 'do-theme-upgrade' == $action ) { + + if ( ! current_user_can( 'update_themes' ) ) + wp_die( __( 'You do not have sufficient permissions to update this site.' ) ); + + check_admin_referer('upgrade-core'); + + if ( isset( $_GET['themes'] ) ) { + $themes = explode( ',', $_GET['themes'] ); + } elseif ( isset( $_POST['checked'] ) ) { + $themes = (array) $_POST['checked']; + } else { + wp_redirect( admin_url('update-core.php') ); + exit; + } + + $url = 'update.php?action=update-selected-themes&themes=' . urlencode(implode(',', $themes)); + $url = wp_nonce_url($url, 'bulk-update-themes'); + + $title = __('Update Themes'); + + require_once(ABSPATH . 'wp-admin/admin-header.php'); + ?> +
    +

    + +
    + bulk_upgrade(); + + require_once( ABSPATH . 'wp-admin/admin-footer.php' ); + +} else { + /** + * Fires for each custom update action on the WordPress Updates screen. + * + * The dynamic portion of the hook name, `$action`, refers to the + * passed update action. The hook fires in lieu of all available + * default update actions. + * + * @since 3.2.0 + */ + do_action( "update-core-custom_{$action}" ); +} diff --git a/wp-admin/update.php b/wp-admin/update.php new file mode 100644 index 0000000..6ded7a7 --- /dev/null +++ b/wp-admin/update.php @@ -0,0 +1,272 @@ +bulk_upgrade( $plugins ); + + iframe_footer(); + + } elseif ( 'upgrade-plugin' == $action ) { + if ( ! current_user_can('update_plugins') ) + wp_die(__('You do not have sufficient permissions to update plugins for this site.')); + + check_admin_referer('upgrade-plugin_' . $plugin); + + $title = __('Update Plugin'); + $parent_file = 'plugins.php'; + $submenu_file = 'plugins.php'; + + wp_enqueue_script( 'updates' ); + require_once(ABSPATH . 'wp-admin/admin-header.php'); + + $nonce = 'upgrade-plugin_' . $plugin; + $url = 'update.php?action=upgrade-plugin&plugin=' . urlencode( $plugin ); + + $upgrader = new Plugin_Upgrader( new Plugin_Upgrader_Skin( compact('title', 'nonce', 'url', 'plugin') ) ); + $upgrader->upgrade($plugin); + + include(ABSPATH . 'wp-admin/admin-footer.php'); + + } elseif ('activate-plugin' == $action ) { + if ( ! current_user_can('update_plugins') ) + wp_die(__('You do not have sufficient permissions to update plugins for this site.')); + + check_admin_referer('activate-plugin_' . $plugin); + if ( ! isset($_GET['failure']) && ! isset($_GET['success']) ) { + wp_redirect( admin_url('update.php?action=activate-plugin&failure=true&plugin=' . urlencode( $plugin ) . '&_wpnonce=' . $_GET['_wpnonce']) ); + activate_plugin( $plugin, '', ! empty( $_GET['networkwide'] ), true ); + wp_redirect( admin_url('update.php?action=activate-plugin&success=true&plugin=' . urlencode( $plugin ) . '&_wpnonce=' . $_GET['_wpnonce']) ); + die(); + } + iframe_header( __('Plugin Reactivation'), true ); + if ( isset($_GET['success']) ) + echo '

    ' . __('Plugin reactivated successfully.') . '

    '; + + if ( isset($_GET['failure']) ){ + echo '

    ' . __('Plugin failed to reactivate due to a fatal error.') . '

    '; + + error_reporting( E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_ERROR | E_WARNING | E_PARSE | E_USER_ERROR | E_USER_WARNING | E_RECOVERABLE_ERROR ); + @ini_set('display_errors', true); //Ensure that Fatal errors are displayed. + wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $plugin ); + include( WP_PLUGIN_DIR . '/' . $plugin ); + } + iframe_footer(); + } elseif ( 'install-plugin' == $action ) { + + if ( ! current_user_can('install_plugins') ) + wp_die( __( 'You do not have sufficient permissions to install plugins on this site.' ) ); + + include_once( ABSPATH . 'wp-admin/includes/plugin-install.php' ); //for plugins_api.. + + check_admin_referer('install-plugin_' . $plugin); + $api = plugins_api('plugin_information', array('slug' => $plugin, 'fields' => array('sections' => false) ) ); //Save on a bit of bandwidth. + + if ( is_wp_error($api) ) + wp_die($api); + + $title = __('Plugin Install'); + $parent_file = 'plugins.php'; + $submenu_file = 'plugin-install.php'; + require_once(ABSPATH . 'wp-admin/admin-header.php'); + + $title = sprintf( __('Installing Plugin: %s'), $api->name . ' ' . $api->version ); + $nonce = 'install-plugin_' . $plugin; + $url = 'update.php?action=install-plugin&plugin=' . urlencode( $plugin ); + if ( isset($_GET['from']) ) + $url .= '&from=' . urlencode(stripslashes($_GET['from'])); + + $type = 'web'; //Install plugin type, From Web or an Upload. + + $upgrader = new Plugin_Upgrader( new Plugin_Installer_Skin( compact('title', 'url', 'nonce', 'plugin', 'api') ) ); + $upgrader->install($api->download_link); + + include(ABSPATH . 'wp-admin/admin-footer.php'); + + } elseif ( 'upload-plugin' == $action ) { + + if ( ! current_user_can( 'upload_plugins' ) ) { + wp_die( __( 'You do not have sufficient permissions to install plugins on this site.' ) ); + } + + check_admin_referer('plugin-upload'); + + $file_upload = new File_Upload_Upgrader('pluginzip', 'package'); + + $title = __('Upload Plugin'); + $parent_file = 'plugins.php'; + $submenu_file = 'plugin-install.php'; + require_once(ABSPATH . 'wp-admin/admin-header.php'); + + $title = sprintf( __('Installing Plugin from uploaded file: %s'), esc_html( basename( $file_upload->filename ) ) ); + $nonce = 'plugin-upload'; + $url = add_query_arg(array('package' => $file_upload->id), 'update.php?action=upload-plugin'); + $type = 'upload'; //Install plugin type, From Web or an Upload. + + $upgrader = new Plugin_Upgrader( new Plugin_Installer_Skin( compact('type', 'title', 'nonce', 'url') ) ); + $result = $upgrader->install( $file_upload->package ); + + if ( $result || is_wp_error($result) ) + $file_upload->cleanup(); + + include(ABSPATH . 'wp-admin/admin-footer.php'); + + } elseif ( 'upgrade-theme' == $action ) { + + if ( ! current_user_can('update_themes') ) + wp_die(__('You do not have sufficient permissions to update themes for this site.')); + + check_admin_referer('upgrade-theme_' . $theme); + + wp_enqueue_script( 'customize-loader' ); + wp_enqueue_script( 'updates' ); + + $title = __('Update Theme'); + $parent_file = 'themes.php'; + $submenu_file = 'themes.php'; + require_once(ABSPATH . 'wp-admin/admin-header.php'); + + $nonce = 'upgrade-theme_' . $theme; + $url = 'update.php?action=upgrade-theme&theme=' . urlencode( $theme ); + + $upgrader = new Theme_Upgrader( new Theme_Upgrader_Skin( compact('title', 'nonce', 'url', 'theme') ) ); + $upgrader->upgrade($theme); + + include(ABSPATH . 'wp-admin/admin-footer.php'); + } elseif ( 'update-selected-themes' == $action ) { + if ( ! current_user_can( 'update_themes' ) ) + wp_die( __( 'You do not have sufficient permissions to update themes for this site.' ) ); + + check_admin_referer( 'bulk-update-themes' ); + + if ( isset( $_GET['themes'] ) ) + $themes = explode( ',', stripslashes($_GET['themes']) ); + elseif ( isset( $_POST['checked'] ) ) + $themes = (array) $_POST['checked']; + else + $themes = array(); + + $themes = array_map('urldecode', $themes); + + $url = 'update.php?action=update-selected-themes&themes=' . urlencode(implode(',', $themes)); + $nonce = 'bulk-update-themes'; + + wp_enqueue_script( 'updates' ); + iframe_header(); + + $upgrader = new Theme_Upgrader( new Bulk_Theme_Upgrader_Skin( compact( 'nonce', 'url' ) ) ); + $upgrader->bulk_upgrade( $themes ); + + iframe_footer(); + } elseif ( 'install-theme' == $action ) { + + if ( ! current_user_can('install_themes') ) + wp_die( __( 'You do not have sufficient permissions to install themes on this site.' ) ); + + include_once( ABSPATH . 'wp-admin/includes/theme-install.php' ); //for themes_api.. + + check_admin_referer( 'install-theme_' . $theme ); + $api = themes_api('theme_information', array('slug' => $theme, 'fields' => array('sections' => false, 'tags' => false) ) ); //Save on a bit of bandwidth. + + if ( is_wp_error($api) ) + wp_die($api); + + wp_enqueue_script( 'customize-loader' ); + + $title = __('Install Themes'); + $parent_file = 'themes.php'; + $submenu_file = 'themes.php'; + require_once(ABSPATH . 'wp-admin/admin-header.php'); + + $title = sprintf( __('Installing Theme: %s'), $api->name . ' ' . $api->version ); + $nonce = 'install-theme_' . $theme; + $url = 'update.php?action=install-theme&theme=' . urlencode( $theme ); + $type = 'web'; //Install theme type, From Web or an Upload. + + $upgrader = new Theme_Upgrader( new Theme_Installer_Skin( compact('title', 'url', 'nonce', 'plugin', 'api') ) ); + $upgrader->install($api->download_link); + + include(ABSPATH . 'wp-admin/admin-footer.php'); + + } elseif ( 'upload-theme' == $action ) { + + if ( ! current_user_can( 'upload_themes' ) ) { + wp_die( __( 'You do not have sufficient permissions to install themes on this site.' ) ); + } + + check_admin_referer('theme-upload'); + + $file_upload = new File_Upload_Upgrader('themezip', 'package'); + + wp_enqueue_script( 'customize-loader' ); + + $title = __('Upload Theme'); + $parent_file = 'themes.php'; + $submenu_file = 'theme-install.php'; + + require_once(ABSPATH . 'wp-admin/admin-header.php'); + + $title = sprintf( __('Installing Theme from uploaded file: %s'), esc_html( basename( $file_upload->filename ) ) ); + $nonce = 'theme-upload'; + $url = add_query_arg(array('package' => $file_upload->id), 'update.php?action=upload-theme'); + $type = 'upload'; //Install plugin type, From Web or an Upload. + + $upgrader = new Theme_Upgrader( new Theme_Installer_Skin( compact('type', 'title', 'nonce', 'url') ) ); + $result = $upgrader->install( $file_upload->package ); + + if ( $result || is_wp_error($result) ) + $file_upload->cleanup(); + + include(ABSPATH . 'wp-admin/admin-footer.php'); + + } else { + /** + * Fires when a custom plugin or theme update request is received. + * + * The dynamic portion of the hook name, `$action`, refers to the action + * provided in the request for wp-admin/update.php. Can be used to + * provide custom update functionality for themes and plugins. + * + * @since 2.8.0 + */ + do_action( "update-custom_{$action}" ); + } +} diff --git a/wp-admin/upgrade-functions.php b/wp-admin/upgrade-functions.php new file mode 100644 index 0000000..7be637c --- /dev/null +++ b/wp-admin/upgrade-functions.php @@ -0,0 +1,12 @@ +db_version(); +$php_compat = version_compare( $php_version, $required_php_version, '>=' ); +if ( file_exists( WP_CONTENT_DIR . '/db.php' ) && empty( $wpdb->is_mysql ) ) + $mysql_compat = true; +else + $mysql_compat = version_compare( $mysql_version, $required_mysql_version, '>=' ); + +@header( 'Content-Type: ' . get_option( 'html_type' ) . '; charset=' . get_option( 'blog_charset' ) ); +?> + +> + + + + <?php _e( 'WordPress › Update' ); ?> + + + +

    + + + +

    +

    +

    + +WordPress %1$s requires PHP version %2$s or higher and MySQL version %3$s or higher. You are running PHP version %4$s and MySQL version %5$s.'), $wp_version, $required_php_version, $required_mysql_version, $php_version, $mysql_version ); + elseif ( !$php_compat ) + printf( __('You cannot update because WordPress %1$s requires PHP version %2$s or higher. You are running version %3$s.'), $wp_version, $required_php_version, $php_version ); + elseif ( !$mysql_compat ) + printf( __('You cannot update because WordPress %1$s requires MySQL version %2$s or higher. You are running version %3$s.'), $wp_version, $required_mysql_version, $mysql_version ); +?> + +

    +

    +

    +

    + +

    +

    +

    + + + + + + diff --git a/wp-admin/upload.php b/wp-admin/upload.php new file mode 100644 index 0000000..dbdf310 --- /dev/null +++ b/wp-admin/upload.php @@ -0,0 +1,292 @@ + parse_url( self_admin_url(), PHP_URL_PATH ), + ) ); + + get_current_screen()->add_help_tab( array( + 'id' => 'overview', + 'title' => __( 'Overview' ), + 'content' => + '

    ' . __( 'All the files you’ve uploaded are listed in the Media Library, with the most recent uploads listed first.' ) . '

    ' . + '

    ' . __( 'You can view your media in a simple visual grid or a list with columns. Switch between these views using the icons to the left above the media.' ) . '

    ' . + '

    ' . __( 'To delete media items, click the Bulk Select button at the top of the screen. Select any items you wish to delete, then click the Delete Selected button. Clicking the Cancel Selection button takes you back to viewing your media.' ) . '

    ' + ) ); + + get_current_screen()->add_help_tab( array( + 'id' => 'attachment-details', + 'title' => __( 'Attachment Details' ), + 'content' => + '

    ' . __( 'Clicking an item will display an Attachment Details dialog, which allows you to preview media and make quick edits. Any changes you make to the attachment details will be automatically saved.' ) . '

    ' . + '

    ' . __( 'Use the arrow buttons at the top of the dialog, or the left and right arrow keys on your keyboard, to navigate between media items quickly.' ) . '

    ' . + '

    ' . __( 'You can also delete individual items and access the extended edit screen from the details dialog.' ) . '

    ' + ) ); + + get_current_screen()->set_help_sidebar( + '

    ' . __( 'For more information:' ) . '

    ' . + '

    ' . __( 'Documentation on Media Library' ) . '

    ' . + '

    ' . __( 'Support Forums' ) . '

    ' + ); + + $title = __('Media Library'); + $parent_file = 'upload.php'; + + require_once( ABSPATH . 'wp-admin/admin-header.php' ); + ?> +
    +

    + + +

    +
    +

    Switch to the list view.' ); ?>

    +
    +
    + get_pagenum(); + +// Handle bulk actions +$doaction = $wp_list_table->current_action(); + +if ( $doaction ) { + check_admin_referer('bulk-media'); + + if ( 'delete_all' == $doaction ) { + $post_ids = $wpdb->get_col( "SELECT ID FROM $wpdb->posts WHERE post_type='attachment' AND post_status = 'trash'" ); + $doaction = 'delete'; + } elseif ( isset( $_REQUEST['media'] ) ) { + $post_ids = $_REQUEST['media']; + } elseif ( isset( $_REQUEST['ids'] ) ) { + $post_ids = explode( ',', $_REQUEST['ids'] ); + } + + $location = 'upload.php'; + if ( $referer = wp_get_referer() ) { + if ( false !== strpos( $referer, 'upload.php' ) ) + $location = remove_query_arg( array( 'trashed', 'untrashed', 'deleted', 'message', 'ids', 'posted' ), $referer ); + } + + switch ( $doaction ) { + case 'attach': + $parent_id = (int) $_REQUEST['found_post_id']; + if ( !$parent_id ) + return; + + $parent = get_post( $parent_id ); + if ( !current_user_can( 'edit_post', $parent_id ) ) + wp_die( __( 'You are not allowed to edit this post.' ) ); + + $attach = array(); + foreach ( (array) $_REQUEST['media'] as $att_id ) { + $att_id = (int) $att_id; + + if ( !current_user_can( 'edit_post', $att_id ) ) + continue; + + $attach[] = $att_id; + } + + if ( ! empty( $attach ) ) { + $attach_string = implode( ',', $attach ); + $attached = $wpdb->query( $wpdb->prepare( "UPDATE $wpdb->posts SET post_parent = %d WHERE post_type = 'attachment' AND ID IN ( $attach_string )", $parent_id ) ); + foreach ( $attach as $att_id ) { + clean_attachment_cache( $att_id ); + } + } + + if ( isset( $attached ) ) { + $location = 'upload.php'; + if ( $referer = wp_get_referer() ) { + if ( false !== strpos( $referer, 'upload.php' ) ) + $location = $referer; + } + + $location = add_query_arg( array( 'attached' => $attached ) , $location ); + wp_redirect( $location ); + exit; + } + break; + case 'trash': + if ( !isset( $post_ids ) ) + break; + foreach ( (array) $post_ids as $post_id ) { + if ( !current_user_can( 'delete_post', $post_id ) ) + wp_die( __( 'You are not allowed to move this post to the trash.' ) ); + + if ( !wp_trash_post( $post_id ) ) + wp_die( __( 'Error in moving to trash.' ) ); + } + $location = add_query_arg( array( 'trashed' => count( $post_ids ), 'ids' => join( ',', $post_ids ) ), $location ); + break; + case 'untrash': + if ( !isset( $post_ids ) ) + break; + foreach ( (array) $post_ids as $post_id ) { + if ( !current_user_can( 'delete_post', $post_id ) ) + wp_die( __( 'You are not allowed to move this post out of the trash.' ) ); + + if ( !wp_untrash_post( $post_id ) ) + wp_die( __( 'Error in restoring from trash.' ) ); + } + $location = add_query_arg( 'untrashed', count( $post_ids ), $location ); + break; + case 'delete': + if ( !isset( $post_ids ) ) + break; + foreach ( (array) $post_ids as $post_id_del ) { + if ( !current_user_can( 'delete_post', $post_id_del ) ) + wp_die( __( 'You are not allowed to delete this post.' ) ); + + if ( !wp_delete_attachment( $post_id_del ) ) + wp_die( __( 'Error in deleting.' ) ); + } + $location = add_query_arg( 'deleted', count( $post_ids ), $location ); + break; + } + + wp_redirect( $location ); + exit; +} elseif ( ! empty( $_GET['_wp_http_referer'] ) ) { + wp_redirect( remove_query_arg( array( '_wp_http_referer', '_wpnonce' ), wp_unslash( $_SERVER['REQUEST_URI'] ) ) ); + exit; +} + +$wp_list_table->prepare_items(); + +$title = __('Media Library'); +$parent_file = 'upload.php'; + +wp_enqueue_script( 'media' ); + +add_screen_option( 'per_page', array('label' => _x( 'Media items', 'items per page (screen options)' )) ); + +get_current_screen()->add_help_tab( array( +'id' => 'overview', +'title' => __('Overview'), +'content' => + '

    ' . __( 'All the files you’ve uploaded are listed in the Media Library, with the most recent uploads listed first. You can use the Screen Options tab to customize the display of this screen.' ) . '

    ' . + '

    ' . __( 'You can narrow the list by file type/status using the text link filters at the top of the screen. You also can refine the list by date using the dropdown menu above the media table.' ) . '

    ' . + '

    ' . __( 'You can view your media in a simple visual grid or a list with columns. Switch between these views using the icons to the left above the media.' ) . '

    ' +) ); +get_current_screen()->add_help_tab( array( +'id' => 'actions-links', +'title' => __('Available Actions'), +'content' => + '

    ' . __( 'Hovering over a row reveals action links: Edit, Delete Permanently, and View. Clicking Edit or on the media file’s name displays a simple screen to edit that individual file’s metadata. Clicking Delete Permanently will delete the file from the media library (as well as from any posts to which it is currently attached). View will take you to the display page for that file.' ) . '

    ' +) ); +get_current_screen()->add_help_tab( array( +'id' => 'attaching-files', +'title' => __('Attaching Files'), +'content' => + '

    ' . __( 'If a media file has not been attached to any post, you will see that in the Attached To column, and can click on Attach File to launch a small popup that will allow you to search for a post and attach the file.' ) . '

    ' +) ); + +get_current_screen()->set_help_sidebar( + '

    ' . __( 'For more information:' ) . '

    ' . + '

    ' . __( 'Documentation on Media Library' ) . '

    ' . + '

    ' . __( 'Support Forums' ) . '

    ' +); + +require_once( ABSPATH . 'wp-admin/admin-header.php' ); +?> + +
    +

    + + ' . __('Search results for “%s”') . '', get_search_query() ); ?> +

    + +' . __('Undo') . ''; + $_SERVER['REQUEST_URI'] = remove_query_arg(array('trashed'), $_SERVER['REQUEST_URI']); +} + +if ( ! empty( $_GET['untrashed'] ) && $untrashed = absint( $_GET['untrashed'] ) ) { + $message = sprintf( _n( 'Media attachment restored from the trash.', '%d media attachments restored from the trash.', $untrashed ), number_format_i18n( $_GET['untrashed'] ) ); + $_SERVER['REQUEST_URI'] = remove_query_arg(array('untrashed'), $_SERVER['REQUEST_URI']); +} + +$messages[1] = __('Media attachment updated.'); +$messages[2] = __('Media permanently deleted.'); +$messages[3] = __('Error saving media attachment.'); +$messages[4] = __('Media moved to the trash.') . ' ' . __('Undo') . ''; +$messages[5] = __('Media restored from the trash.'); + +if ( ! empty( $_GET['message'] ) && isset( $messages[ $_GET['message'] ] ) ) { + $message = $messages[ $_GET['message'] ]; + $_SERVER['REQUEST_URI'] = remove_query_arg(array('message'), $_SERVER['REQUEST_URI']); +} + +if ( !empty($message) ) { ?> +

    + + +
    + +views(); ?> + +display(); ?> + +
    + +
    +
    + +ID ) ); + +if ( ! $user_id && IS_PROFILE_PAGE ) + $user_id = $current_user->ID; +elseif ( ! $user_id && ! IS_PROFILE_PAGE ) + wp_die(__( 'Invalid user ID.' ) ); +elseif ( ! get_userdata( $user_id ) ) + wp_die( __('Invalid user ID.') ); + +wp_enqueue_script('user-profile'); + +$title = IS_PROFILE_PAGE ? __('Profile') : __('Edit User'); +if ( current_user_can('edit_users') && !IS_PROFILE_PAGE ) + $submenu_file = 'users.php'; +else + $submenu_file = 'profile.php'; + +if ( current_user_can('edit_users') && !is_user_admin() ) + $parent_file = 'users.php'; +else + $parent_file = 'profile.php'; + +$profile_help = '

    ' . __('Your profile contains information about you (your “account”) as well as some personal options related to using WordPress.') . '

    ' . + '

    ' . __('You can change your password, turn on keyboard shortcuts, change the color scheme of your WordPress administration screens, and turn off the WYSIWYG (Visual) editor, among other things. You can hide the Toolbar (formerly called the Admin Bar) from the front end of your site, however it cannot be disabled on the admin screens.') . '

    ' . + '

    ' . __('Your username cannot be changed, but you can use other fields to enter your real name or a nickname, and change which name to display on your posts.') . '

    ' . + '

    ' . __( 'You can log out of other devices, such as your phone or a public computer, by clicking the Log Out of All Other Sessions button. The button will only display if you are logged in to more than one device.' ) . '

    ' . + '

    ' . __('Required fields are indicated; the rest are optional. Profile information will only be displayed if your theme is set up to do so.') . '

    ' . + '

    ' . __('Remember to click the Update Profile button when you are finished.') . '

    '; + +get_current_screen()->add_help_tab( array( + 'id' => 'overview', + 'title' => __('Overview'), + 'content' => $profile_help, +) ); + +get_current_screen()->set_help_sidebar( + '

    ' . __('For more information:') . '

    ' . + '

    ' . __('Documentation on User Profiles') . '

    ' . + '

    ' . __('Support Forums') . '

    ' +); + +$wp_http_referer = remove_query_arg(array('update', 'delete_count'), $wp_http_referer ); + +$user_can_edit = current_user_can( 'edit_posts' ) || current_user_can( 'edit_pages' ); + +/** + * Optional SSL preference that can be turned on by hooking to the 'personal_options' action. + * + * @since 2.7.0 + * + * @param object $user User data object + */ +function use_ssl_preference($user) { +?> + + + + +ID + && ! apply_filters( 'enable_edit_any_user_configuration', true ) +) { + wp_die( __( 'You do not have permission to edit this user.' ) ); +} + +// Execute confirmed email change. See send_confirmation_on_profile_email(). +if ( is_multisite() && IS_PROFILE_PAGE && isset( $_GET[ 'newuseremail' ] ) && $current_user->ID ) { + $new_email = get_option( $current_user->ID . '_new_email' ); + if ( $new_email[ 'hash' ] == $_GET[ 'newuseremail' ] ) { + $user = new stdClass; + $user->ID = $current_user->ID; + $user->user_email = esc_html( trim( $new_email[ 'newemail' ] ) ); + if ( $wpdb->get_var( $wpdb->prepare( "SELECT user_login FROM {$wpdb->signups} WHERE user_login = %s", $current_user->user_login ) ) ) + $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->signups} SET user_email = %s WHERE user_login = %s", $user->user_email, $current_user->user_login ) ); + wp_update_user( $user ); + delete_option( $current_user->ID . '_new_email' ); + wp_redirect( add_query_arg( array('updated' => 'true'), self_admin_url( 'profile.php' ) ) ); + die(); + } +} elseif ( is_multisite() && IS_PROFILE_PAGE && !empty( $_GET['dismiss'] ) && $current_user->ID . '_new_email' == $_GET['dismiss'] ) { + delete_option( $current_user->ID . '_new_email' ); + wp_redirect( add_query_arg( array('updated' => 'true'), self_admin_url( 'profile.php' ) ) ); + die(); +} + +switch ($action) { +case 'update': + +check_admin_referer('update-user_' . $user_id); + +if ( !current_user_can('edit_user', $user_id) ) + wp_die(__('You do not have permission to edit this user.')); + +if ( IS_PROFILE_PAGE ) { + /** + * Fires before the page loads on the 'Your Profile' editing screen. + * + * The action only fires if the current user is editing their own profile. + * + * @since 2.0.0 + * + * @param int $user_id The user ID. + */ + do_action( 'personal_options_update', $user_id ); +} else { + /** + * Fires before the page loads on the 'Edit User' screen. + * + * @since 2.7.0 + * + * @param int $user_id The user ID. + */ + do_action( 'edit_user_profile_update', $user_id ); +} + +if ( !is_multisite() ) { + $errors = edit_user($user_id); +} else { + $user = get_userdata( $user_id ); + + // Update the email address in signups, if present. + if ( $user->user_login && isset( $_POST[ 'email' ] ) && is_email( $_POST[ 'email' ] ) && $wpdb->get_var( $wpdb->prepare( "SELECT user_login FROM {$wpdb->signups} WHERE user_login = %s", $user->user_login ) ) ) + $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->signups} SET user_email = %s WHERE user_login = %s", $_POST[ 'email' ], $user_login ) ); + + // We must delete the user from the current blog if WP added them after editing. + $delete_role = false; + $blog_prefix = $wpdb->get_blog_prefix(); + if ( $user_id != $current_user->ID ) { + $cap = $wpdb->get_var( "SELECT meta_value FROM {$wpdb->usermeta} WHERE user_id = '{$user_id}' AND meta_key = '{$blog_prefix}capabilities' AND meta_value = 'a:0:{}'" ); + if ( !is_network_admin() && null == $cap && $_POST[ 'role' ] == '' ) { + $_POST[ 'role' ] = 'contributor'; + $delete_role = true; + } + } + if ( !isset( $errors ) || ( isset( $errors ) && is_object( $errors ) && false == $errors->get_error_codes() ) ) + $errors = edit_user($user_id); + if ( $delete_role ) // stops users being added to current blog when they are edited + delete_user_meta( $user_id, $blog_prefix . 'capabilities' ); + + if ( is_multisite() && is_network_admin() && !IS_PROFILE_PAGE && current_user_can( 'manage_network_options' ) && !isset($super_admins) && empty( $_POST['super_admin'] ) == is_super_admin( $user_id ) ) + empty( $_POST['super_admin'] ) ? revoke_super_admin( $user_id ) : grant_super_admin( $user_id ); +} + +if ( !is_wp_error( $errors ) ) { + $redirect = add_query_arg( 'updated', true, get_edit_user_link( $user_id ) ); + if ( $wp_http_referer ) + $redirect = add_query_arg('wp_http_referer', urlencode($wp_http_referer), $redirect); + wp_redirect($redirect); + exit; +} + +default: +$profileuser = get_user_to_edit($user_id); + +if ( !current_user_can('edit_user', $user_id) ) + wp_die(__('You do not have permission to edit this user.')); + +$sessions = WP_Session_Tokens::get_instance( $profileuser->ID ); + +include(ABSPATH . 'wp-admin/admin-header.php'); +?> + +ID ) && current_user_can( 'manage_network_options' ) ) { ?> +

    + + +
    + +

    + +

    + + +

    + +
    + + +

    \n

    ", $errors->get_error_messages() ); ?>

    + + +
    +

    + + + + + +

    +
    > + + + + +

    + + +

    + +

    + + + + + + + + + 1 && has_action('admin_color_scheme_picker') ) : ?> + + + + + + + + + + + + + + + + +
    More information'); ?>
    +
    +
    +
    + + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +user_email != get_site_option( 'admin_email' ) || ! is_super_admin( $profileuser->ID ) ) : ?> +

    + +

    + +
    + +
    + +

    + + + + + + + + + + + + + $desc ) { +?> + + + + + +
    + ID . '_new_email' ); + if ( $new_email && $new_email['newemail'] != $current_user->user_email && $profileuser->ID == $current_user->ID ) : ?> +
    +

    %1$s. Cancel'), $new_email['newemail'], esc_url( self_admin_url( 'profile.php?dismiss=' . $current_user->ID . '_new_email' ) ) ); ?>

    +
    + +
    + +

    + + + + + + + + + + + + + + + + + + +get_all() ) === 1 ) : ?> + + + + +get_all() ) > 1 ) : ?> + + + + +get_all() ) : ?> + + + + + + +
    +

    + + +

    +
    + +

    +
    +
    +

    +
      +
    +

    + +

    +
      +
    +

    + +

    +
      +

    +

    + display_name ); + ?> +

    +
    + + + +caps ) > count( $profileuser->roles ) + && apply_filters( 'additional_capabilities_display', true, $profileuser ) +) : ?> +

    + + + + + +
    +caps as $cap => $value ) { + if ( ! $wp_roles->is_role( $cap ) ) { + if ( '' != $output ) + $output .= ', '; + $output .= $value ? $cap : sprintf( __( 'Denied: %s' ), $cap ); + } + } + echo $output; +?> +
    + + + + + + + +
    +
    + + + 'enter_email'), 'user-new.php' ) ); + die(); + } + } + + if ( !$user_details ) { + wp_redirect( add_query_arg( array('update' => 'does_not_exist'), 'user-new.php' ) ); + die(); + } + + if ( ! current_user_can('promote_user', $user_details->ID) ) + wp_die( __( 'Cheatin’ uh?' ), 403 ); + + // Adding an existing user to this blog + $new_user_email = $user_details->user_email; + $redirect = 'user-new.php'; + $username = $user_details->user_login; + $user_id = $user_details->ID; + if ( ( $username != null && !is_super_admin( $user_id ) ) && ( array_key_exists($blog_id, get_blogs_of_user($user_id)) ) ) { + $redirect = add_query_arg( array('update' => 'addexisting'), 'user-new.php' ); + } else { + if ( isset( $_POST[ 'noconfirmation' ] ) && is_super_admin() ) { + add_existing_user_to_blog( array( 'user_id' => $user_id, 'role' => $_REQUEST[ 'role' ] ) ); + $redirect = add_query_arg( array('update' => 'addnoconfirmation'), 'user-new.php' ); + } else { + $newuser_key = substr( md5( $user_id ), 0, 5 ); + add_option( 'new_user_' . $newuser_key, array( 'user_id' => $user_id, 'email' => $user_details->user_email, 'role' => $_REQUEST[ 'role' ] ) ); + + $roles = get_editable_roles(); + $role = $roles[ $_REQUEST['role'] ]; + /* translators: 1: Site name, 2: site URL, 3: role, 4: activation URL */ + $message = __( 'Hi, + +You\'ve been invited to join \'%1$s\' at +%2$s with the role of %3$s. + +Please click the following link to confirm the invite: +%4$s' ); + wp_mail( $new_user_email, sprintf( __( '[%s] Joining confirmation' ), wp_specialchars_decode( get_option( 'blogname' ) ) ), sprintf( $message, get_option( 'blogname' ), home_url(), wp_specialchars_decode( translate_user_role( $role['name'] ) ), home_url( "/newbloguser/$newuser_key/" ) ) ); + $redirect = add_query_arg( array('update' => 'add'), 'user-new.php' ); + } + } + wp_redirect( $redirect ); + die(); +} elseif ( isset($_REQUEST['action']) && 'createuser' == $_REQUEST['action'] ) { + check_admin_referer( 'create-user', '_wpnonce_create-user' ); + + if ( ! current_user_can('create_users') ) + wp_die( __( 'Cheatin’ uh?' ), 403 ); + + if ( ! is_multisite() ) { + $user_id = edit_user(); + + if ( is_wp_error( $user_id ) ) { + $add_user_errors = $user_id; + } else { + if ( current_user_can( 'list_users' ) ) + $redirect = 'users.php?update=add&id=' . $user_id; + else + $redirect = add_query_arg( 'update', 'add', 'user-new.php' ); + wp_redirect( $redirect ); + die(); + } + } else { + // Adding a new user to this site + $new_user_email = wp_unslash( $_REQUEST['email'] ); + $user_details = wpmu_validate_user_signup( $_REQUEST['user_login'], $new_user_email ); + if ( is_wp_error( $user_details[ 'errors' ] ) && !empty( $user_details[ 'errors' ]->errors ) ) { + $add_user_errors = $user_details[ 'errors' ]; + } else { + /** + * Filter the user_login, also known as the username, before it is added to the site. + * + * @since 2.0.3 + * + * @param string $user_login The sanitized username. + */ + $new_user_login = apply_filters( 'pre_user_login', sanitize_user( wp_unslash( $_REQUEST['user_login'] ), true ) ); + if ( isset( $_POST[ 'noconfirmation' ] ) && is_super_admin() ) { + add_filter( 'wpmu_signup_user_notification', '__return_false' ); // Disable confirmation email + add_filter( 'wpmu_welcome_user_notification', '__return_false' ); // Disable welcome email + } + wpmu_signup_user( $new_user_login, $new_user_email, array( 'add_to_blog' => $wpdb->blogid, 'new_role' => $_REQUEST['role'] ) ); + if ( isset( $_POST[ 'noconfirmation' ] ) && is_super_admin() ) { + $key = $wpdb->get_var( $wpdb->prepare( "SELECT activation_key FROM {$wpdb->signups} WHERE user_login = %s AND user_email = %s", $new_user_login, $new_user_email ) ); + wpmu_activate_signup( $key ); + $redirect = add_query_arg( array('update' => 'addnoconfirmation'), 'user-new.php' ); + } else { + $redirect = add_query_arg( array('update' => 'newuserconfirmation'), 'user-new.php' ); + } + wp_redirect( $redirect ); + die(); + } + } +} + +$title = __('Add New User'); +$parent_file = 'users.php'; + +$do_both = false; +if ( is_multisite() && current_user_can('promote_users') && current_user_can('create_users') ) + $do_both = true; + +$help = '

    ' . __('To add a new user to your site, fill in the form on this screen and click the Add New User button at the bottom.') . '

    '; + +if ( is_multisite() ) { + $help .= '

    ' . __('Because this is a multisite installation, you may add accounts that already exist on the Network by specifying a username or email, and defining a role. For more options, such as specifying a password, you have to be a Network Administrator and use the hover link under an existing user’s name to Edit the user profile under Network Admin > All Users.') . '

    ' . + '

    ' . __('New users will receive an email letting them know they’ve been added as a user for your site. This email will also contain their password. Check the box if you don’t want the user to receive a welcome email.') . '

    '; +} else { + $help .= '

    ' . __('You must assign a password to the new user, which they can change after logging in. The username, however, cannot be changed.') . '

    ' . + '

    ' . __('New users will receive an email letting them know they’ve been added as a user for your site. By default, this email will also contain their password. Uncheck the box if you don’t want the password to be included in the welcome email.') . '

    '; +} + +$help .= '

    ' . __('Remember to click the Add New User button at the bottom of this screen when you are finished.') . '

    '; + +get_current_screen()->add_help_tab( array( + 'id' => 'overview', + 'title' => __('Overview'), + 'content' => $help, +) ); + +get_current_screen()->add_help_tab( array( +'id' => 'user-roles', +'title' => __('User Roles'), +'content' => '

    ' . __('Here is a basic overview of the different user roles and the permissions associated with each one:') . '

    ' . + '
      ' . + '
    • ' . __('Subscribers can read comments/comment/receive newsletters, etc. but cannot create regular site content.') . '
    • ' . + '
    • ' . __('Contributors can write and manage their posts but not publish posts or upload media files.') . '
    • ' . + '
    • ' . __('Authors can publish and manage their own posts, and are able to upload files.') . '
    • ' . + '
    • ' . __('Editors can publish posts, manage posts as well as manage other people’s posts, etc.') . '
    • ' . + '
    • ' . __('Administrators have access to all the administration features.') . '
    • ' . + '
    ' +) ); + +get_current_screen()->set_help_sidebar( + '

    ' . __('For more information:') . '

    ' . + '

    ' . __('Documentation on Adding New Users') . '

    ' . + '

    ' . __('Support Forums') . '

    ' +); + +wp_enqueue_script('wp-ajax-response'); +wp_enqueue_script('user-profile'); + +/** + * Filter whether to enable user auto-complete for non-super admins in Multisite. + * + * @since 3.4.0 + * + * @param bool $enable Whether to enable auto-complete for non-super admins. Default false. + */ +if ( is_multisite() && current_user_can( 'promote_users' ) && ! wp_is_large_network( 'users' ) + && ( is_super_admin() || apply_filters( 'autocomplete_users_for_site_admins', false ) ) +) { + wp_enqueue_script( 'user-suggest' ); +} + +require_once( ABSPATH . 'wp-admin/admin-header.php' ); + +if ( isset($_GET['update']) ) { + $messages = array(); + if ( is_multisite() ) { + switch ( $_GET['update'] ) { + case "newuserconfirmation": + $messages[] = __('Invitation email sent to new user. A confirmation link must be clicked before their account is created.'); + break; + case "add": + $messages[] = __('Invitation email sent to user. A confirmation link must be clicked for them to be added to your site.'); + break; + case "addnoconfirmation": + $messages[] = __('User has been added to your site.'); + break; + case "addexisting": + $messages[] = __('That user is already a member of this site.'); + break; + case "does_not_exist": + $messages[] = __('The requested user does not exist.'); + break; + case "enter_email": + $messages[] = __('Please enter a valid email address.'); + break; + } + } else { + if ( 'add' == $_GET['update'] ) + $messages[] = __('User added.'); + } +} +?> +
    +

    +

    + + +
    +
      + get_error_messages() as $err ) + echo "
    • $err
    • \n"; + ?> +
    +
    +

    ' . $msg . '

    '; +} ?> + + +
    + get_error_messages() as $message ) + echo "

    $message

    "; + ?> +
    + +
    + +' . __('Add Existing User') . ''; + if ( !is_super_admin() ) { + echo '

    ' . __( 'Enter the email address of an existing user on this network to invite them to this site. That person will be sent an email asking them to confirm the invite.' ) . '

    '; + $label = __('E-mail'); + $type = 'email'; + } else { + echo '

    ' . __( 'Enter the email address or username of an existing user on this network to invite them to this site. That person will be sent an email asking them to confirm the invite.' ) . '

    '; + $label = __('E-mail or Username'); + $type = 'text'; + } +?> +
    > + + + + + + + + + + + + + + + + + + +
    +
    + + 'addusersub' ) ); ?> +
    +' . __( 'Add New User' ) . ''; +?> +

    +
    > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + +
    + +
    +
    +

    +
    +
    + + + + 'createusersub' ) ); ?> + +
    + + +domain != $current_site->domain ) || ( $current_blog->path != $current_site->path ) ); +/** + * Filter whether to redirect the request to the User Admin in Multisite. + * + * @since 3.2.0 + * + * @param bool $redirect_user_admin_request Whether the request should be redirected. + */ +$redirect_user_admin_request = apply_filters( 'redirect_user_admin_request', $redirect_user_admin_request ); +if ( $redirect_user_admin_request ) { + wp_redirect( user_admin_url() ); + exit; +} +unset( $redirect_user_admin_request ); diff --git a/wp-admin/user/credits.php b/wp-admin/user/credits.php new file mode 100644 index 0000000..2b4021a --- /dev/null +++ b/wp-admin/user/credits.php @@ -0,0 +1,13 @@ +get_pagenum(); +$title = __('Users'); +$parent_file = 'users.php'; + +add_screen_option( 'per_page', array('label' => _x( 'Users', 'users per page (screen options)' )) ); + +// contextual help - choose Help on the top right of admin panel to preview this. +get_current_screen()->add_help_tab( array( + 'id' => 'overview', + 'title' => __('Overview'), + 'content' => '

    ' . __('This screen lists all the existing users for your site. Each user has one of five defined roles as set by the site admin: Site Administrator, Editor, Author, Contributor, or Subscriber. Users with roles other than Administrator will see fewer options in the dashboard navigation when they are logged in, based on their role.') . '

    ' . + '

    ' . __('To add a new user for your site, click the Add New button at the top of the screen or Add New in the Users menu section.') . '

    ' +) ) ; + +get_current_screen()->add_help_tab( array( + 'id' => 'screen-display', + 'title' => __('Screen Display'), + 'content' => '

    ' . __('You can customize the display of this screen in a number of ways:') . '

    ' . + '
      ' . + '
    • ' . __('You can hide/display columns based on your needs and decide how many users to list per screen using the Screen Options tab.') . '
    • ' . + '
    • ' . __('You can filter the list of users by User Role using the text links in the upper left to show All, Administrator, Editor, Author, Contributor, or Subscriber. The default view is to show all users. Unused User Roles are not listed.') . '
    • ' . + '
    • ' . __('You can view all posts made by a user by clicking on the number under the Posts column.') . '
    • ' . + '
    ' +) ); + +$help = '

    ' . __('Hovering over a row in the users list will display action links that allow you to manage users. You can perform the following actions:') . '

    ' . + '
      ' . + '
    • ' . __('Edit takes you to the editable profile screen for that user. You can also reach that screen by clicking on the username.') . '
    • '; + +if ( is_multisite() ) + $help .= '
    • ' . __( 'Remove allows you to remove a user from your site. It does not delete their content. You can also remove multiple users at once by using Bulk Actions.' ) . '
    • '; +else + $help .= '
    • ' . __( 'Delete brings you to the Delete Users screen for confirmation, where you can permanently remove a user from your site and delete their content. You can also delete multiple users at once by using Bulk Actions.' ) . '
    • '; + +$help .= '
    '; + +get_current_screen()->add_help_tab( array( + 'id' => 'actions', + 'title' => __('Actions'), + 'content' => $help, +) ); +unset( $help ); + +get_current_screen()->set_help_sidebar( + '

    ' . __('For more information:') . '

    ' . + '

    ' . __('Documentation on Managing Users') . '

    ' . + '

    ' . __('Descriptions of Roles and Capabilities') . '

    ' . + '

    ' . __('Support Forums') . '

    ' +); + +if ( empty($_REQUEST) ) { + $referer = ''; +} elseif ( isset($_REQUEST['wp_http_referer']) ) { + $redirect = remove_query_arg(array('wp_http_referer', 'updated', 'delete_count'), wp_unslash( $_REQUEST['wp_http_referer'] ) ); + $referer = ''; +} else { + $redirect = 'users.php'; + $referer = ''; +} + +$update = ''; + +/** + * @since 3.5.0 + * @access private + */ +function delete_users_add_js() { ?> + +current_action() ) { + +/* Bulk Dropdown menu Role changes */ +case 'promote': + check_admin_referer('bulk-users'); + + if ( ! current_user_can( 'promote_users' ) ) + wp_die( __( 'You can’t edit that user.' ) ); + + if ( empty($_REQUEST['users']) ) { + wp_redirect($redirect); + exit(); + } + + $editable_roles = get_editable_roles(); + if ( empty( $editable_roles[$_REQUEST['new_role']] ) ) + wp_die(__('You can’t give users that role.')); + + $userids = $_REQUEST['users']; + $update = 'promote'; + foreach ( $userids as $id ) { + $id = (int) $id; + + if ( ! current_user_can('promote_user', $id) ) + wp_die(__('You can’t edit that user.')); + // The new role of the current user must also have the promote_users cap or be a multisite super admin + if ( $id == $current_user->ID && ! $wp_roles->role_objects[ $_REQUEST['new_role'] ]->has_cap('promote_users') + && ! ( is_multisite() && is_super_admin() ) ) { + $update = 'err_admin_role'; + continue; + } + + // If the user doesn't already belong to the blog, bail. + if ( is_multisite() && !is_user_member_of_blog( $id ) ) + wp_die( __( 'Cheatin’ uh?' ), 403 ); + + $user = get_userdata( $id ); + $user->set_role($_REQUEST['new_role']); + } + + wp_redirect(add_query_arg('update', $update, $redirect)); + exit(); + +case 'dodelete': + if ( is_multisite() ) + wp_die( __('User deletion is not allowed from this screen.') ); + + check_admin_referer('delete-users'); + + if ( empty($_REQUEST['users']) ) { + wp_redirect($redirect); + exit(); + } + + $userids = array_map( 'intval', (array) $_REQUEST['users'] ); + + if ( empty( $_REQUEST['delete_option'] ) ) { + $url = self_admin_url( 'users.php?action=delete&users[]=' . implode( '&users[]=', $userids ) . '&error=true' ); + $url = str_replace( '&', '&', wp_nonce_url( $url, 'bulk-users' ) ); + wp_redirect( $url ); + exit; + } + + if ( ! current_user_can( 'delete_users' ) ) + wp_die(__('You can’t delete users.')); + + $update = 'del'; + $delete_count = 0; + + foreach ( $userids as $id ) { + if ( ! current_user_can( 'delete_user', $id ) ) + wp_die(__( 'You can’t delete that user.' ) ); + + if ( $id == $current_user->ID ) { + $update = 'err_admin_del'; + continue; + } + switch ( $_REQUEST['delete_option'] ) { + case 'delete': + wp_delete_user( $id ); + break; + case 'reassign': + wp_delete_user( $id, $_REQUEST['reassign_user'] ); + break; + } + ++$delete_count; + } + + $redirect = add_query_arg( array('delete_count' => $delete_count, 'update' => $update), $redirect); + wp_redirect($redirect); + exit(); + +case 'delete': + if ( is_multisite() ) + wp_die( __('User deletion is not allowed from this screen.') ); + + check_admin_referer('bulk-users'); + + if ( empty($_REQUEST['users']) && empty($_REQUEST['user']) ) { + wp_redirect($redirect); + exit(); + } + + if ( ! current_user_can( 'delete_users' ) ) + $errors = new WP_Error( 'edit_users', __( 'You can’t delete users.' ) ); + + if ( empty($_REQUEST['users']) ) + $userids = array( intval( $_REQUEST['user'] ) ); + else + $userids = array_map( 'intval', (array) $_REQUEST['users'] ); + + add_action( 'admin_head', 'delete_users_add_js' ); + + include( ABSPATH . 'wp-admin/admin-header.php' ); +?> +
    + + + +
    +

    + +
    +

    +
    + +

    +
      +ID ) { + echo "
    • " . sprintf(__('ID #%1$s: %2$s The current user will not be deleted.'), $id, $user->user_login) . "
    • \n"; + } else { + echo "
    • " . sprintf(__('ID #%1$s: %2$s'), $id, $user->user_login) . "
    • \n"; + $go_delete++; + } + } + ?> +
    + +

    +
      +
    • +
    • + ' . __( 'Attribute all content to:' ) . ' '; + wp_dropdown_users( array( 'name' => 'reassign_user', 'exclude' => array_diff( $userids, array($current_user->ID) ) ) ); ?>
    • +
    + + + + +

    + +
    +
    +ID && !is_super_admin() ) { + $update = 'err_admin_remove'; + continue; + } + if ( !current_user_can('remove_user', $id) ) { + $update = 'err_admin_remove'; + continue; + } + remove_user_from_blog($id, $blog_id); + } + + $redirect = add_query_arg( array('update' => $update), $redirect); + wp_redirect($redirect); + exit; + +case 'remove': + + check_admin_referer('bulk-users'); + + if ( ! is_multisite() ) + wp_die( __( 'You can’t remove users.' ) ); + + if ( empty($_REQUEST['users']) && empty($_REQUEST['user']) ) { + wp_redirect($redirect); + exit(); + } + + if ( !current_user_can('remove_users') ) + $error = new WP_Error('edit_users', __('You can’t remove users.')); + + if ( empty($_REQUEST['users']) ) + $userids = array(intval($_REQUEST['user'])); + else + $userids = $_REQUEST['users']; + + include( ABSPATH . 'wp-admin/admin-header.php' ); +?> +
    + + + +
    +

    +

    +
      +ID && !is_super_admin() ) { + echo "
    • " . sprintf(__('ID #%1$s: %2$s The current user will not be removed.'), $id, $user->user_login) . "
    • \n"; + } elseif ( !current_user_can('remove_user', $id) ) { + echo "
    • " . sprintf(__('ID #%1$s: %2$s You don\'t have permission to remove this user.'), $id, $user->user_login) . "
    • \n"; + } else { + echo "
    • " . sprintf(__('ID #%1$s: %2$s'), $id, $user->user_login) . "
    • \n"; + $go_remove = true; + } + } + ?> +
    + + + + +

    + +
    +
    +prepare_items(); + $total_pages = $wp_list_table->get_pagination_arg( 'total_pages' ); + if ( $pagenum > $total_pages && $total_pages > 0 ) { + wp_redirect( add_query_arg( 'paged', $total_pages ) ); + exit; + } + + include( ABSPATH . 'wp-admin/admin-header.php' ); + + $messages = array(); + if ( isset($_GET['update']) ) : + switch($_GET['update']) { + case 'del': + case 'del_many': + $delete_count = isset($_GET['delete_count']) ? (int) $_GET['delete_count'] : 0; + $messages[] = '

    ' . sprintf( _n( 'User deleted.', '%s users deleted.', $delete_count ), number_format_i18n( $delete_count ) ) . '

    '; + break; + case 'add': + if ( isset( $_GET['id'] ) && ( $user_id = $_GET['id'] ) && current_user_can( 'edit_user', $user_id ) ) { + $messages[] = '

    ' . sprintf( __( 'New user created. Edit user' ), + esc_url( add_query_arg( 'wp_http_referer', urlencode( wp_unslash( $_SERVER['REQUEST_URI'] ) ), + self_admin_url( 'user-edit.php?user_id=' . $user_id ) ) ) ) . '

    '; + } else { + $messages[] = '

    ' . __( 'New user created.' ) . '

    '; + } + break; + case 'promote': + $messages[] = '

    ' . __('Changed roles.') . '

    '; + break; + case 'err_admin_role': + $messages[] = '

    ' . __('The current user’s role must have user editing capabilities.') . '

    '; + $messages[] = '

    ' . __('Other user roles have been changed.') . '

    '; + break; + case 'err_admin_del': + $messages[] = '

    ' . __('You can’t delete the current user.') . '

    '; + $messages[] = '

    ' . __('Other users have been deleted.') . '

    '; + break; + case 'remove': + $messages[] = '

    ' . __('User removed from this site.') . '

    '; + break; + case 'err_admin_remove': + $messages[] = '

    ' . __("You can't remove the current user.") . '

    '; + $messages[] = '

    ' . __('Other users have been removed.') . '

    '; + break; + } + endif; ?> + + +
    +
      + get_error_messages() as $err ) + echo "
    • $err
    • \n"; + ?> +
    +
    + + +
    +

    + + + + +' . __('Search results for “%s”') . '', esc_html( $usersearch ) ); ?> +

    + +views(); ?> + +
    + +search_box( __( 'Search Users' ), 'user' ); ?> + +display(); ?> +
    + +
    +
    +add_help_tab( array( +'id' => 'overview', +'title' => __('Overview'), +'content' => + '

    ' . __('Widgets are independent sections of content that can be placed into any widgetized area provided by your theme (commonly called sidebars). To populate your sidebars/widget areas with individual widgets, drag and drop the title bars into the desired area. By default, only the first widget area is expanded. To populate additional widget areas, click on their title bars to expand them.') . '

    +

    ' . __('The Available Widgets section contains all the widgets you can choose from. Once you drag a widget into a sidebar, it will open to allow you to configure its settings. When you are happy with the widget settings, click the Save button and the widget will go live on your site. If you click Delete, it will remove the widget.') . '

    ' +) ); +get_current_screen()->add_help_tab( array( +'id' => 'removing-reusing', +'title' => __('Removing and Reusing'), +'content' => + '

    ' . __('If you want to remove the widget but save its setting for possible future use, just drag it into the Inactive Widgets area. You can add them back anytime from there. This is especially helpful when you switch to a theme with fewer or different widget areas.') . '

    +

    ' . __('Widgets may be used multiple times. You can give each widget a title, to display on your site, but it’s not required.') . '

    +

    ' . __('Enabling Accessibility Mode, via Screen Options, allows you to use Add and Edit buttons instead of using drag and drop.') . '

    ' +) ); +get_current_screen()->add_help_tab( array( +'id' => 'missing-widgets', +'title' => __('Missing Widgets'), +'content' => + '

    ' . __('Many themes show some sidebar widgets by default until you edit your sidebars, but they are not automatically displayed in your sidebar management tool. After you make your first widget change, you can re-add the default widgets by adding them from the Available Widgets area.') . '

    ' . + '

    ' . __('When changing themes, there is often some variation in the number and setup of widget areas/sidebars and sometimes these conflicts make the transition a bit less smooth. If you changed themes and seem to be missing widgets, scroll down on this screen to the Inactive Widgets area, where all of your widgets and their settings will have been saved.') . '

    ' +) ); + +get_current_screen()->set_help_sidebar( + '

    ' . __('For more information:') . '

    ' . + '

    ' . __('Documentation on Widgets') . '

    ' . + '

    ' . __('Support Forums') . '

    ' +); + +if ( ! current_theme_supports( 'widgets' ) ) { + wp_die( __( 'The theme you are currently using isn’t widget-aware, meaning that it has no sidebars that you are able to change. For information on making your theme widget-aware, please follow these instructions.' ) ); +} + +// These are the widgets grouped by sidebar +$sidebars_widgets = wp_get_sidebars_widgets(); + +if ( empty( $sidebars_widgets ) ) + $sidebars_widgets = wp_get_widget_defaults(); + +foreach ( $sidebars_widgets as $sidebar_id => $widgets ) { + if ( 'wp_inactive_widgets' == $sidebar_id ) + continue; + + if ( !isset( $wp_registered_sidebars[ $sidebar_id ] ) ) { + if ( ! empty( $widgets ) ) { // register the inactive_widgets area as sidebar + register_sidebar(array( + 'name' => __( 'Inactive Sidebar (not used)' ), + 'id' => $sidebar_id, + 'class' => 'inactive-sidebar orphan-sidebar', + 'description' => __( 'This sidebar is no longer available and does not show anywhere on your site. Remove each of the widgets below to fully remove this inactive sidebar.' ), + 'before_widget' => '', + 'after_widget' => '', + 'before_title' => '', + 'after_title' => '', + )); + } else { + unset( $sidebars_widgets[ $sidebar_id ] ); + } + } +} + +// register the inactive_widgets area as sidebar +register_sidebar(array( + 'name' => __('Inactive Widgets'), + 'id' => 'wp_inactive_widgets', + 'class' => 'inactive-sidebar', + 'description' => __( 'Drag widgets here to remove them from the sidebar but keep their settings.' ), + 'before_widget' => '', + 'after_widget' => '', + 'before_title' => '', + 'after_title' => '', +)); + +retrieve_widgets(); + +// We're saving a widget without js +if ( isset($_POST['savewidget']) || isset($_POST['removewidget']) ) { + $widget_id = $_POST['widget-id']; + check_admin_referer("save-delete-widget-$widget_id"); + + $number = isset($_POST['multi_number']) ? (int) $_POST['multi_number'] : ''; + if ( $number ) { + foreach ( $_POST as $key => $val ) { + if ( is_array($val) && preg_match('/__i__|%i%/', key($val)) ) { + $_POST[$key] = array( $number => array_shift($val) ); + break; + } + } + } + + $sidebar_id = $_POST['sidebar']; + $position = isset($_POST[$sidebar_id . '_position']) ? (int) $_POST[$sidebar_id . '_position'] - 1 : 0; + + $id_base = $_POST['id_base']; + $sidebar = isset($sidebars_widgets[$sidebar_id]) ? $sidebars_widgets[$sidebar_id] : array(); + + // Delete. + if ( isset($_POST['removewidget']) && $_POST['removewidget'] ) { + + if ( !in_array($widget_id, $sidebar, true) ) { + wp_redirect( admin_url('widgets.php?error=0') ); + exit; + } + + $sidebar = array_diff( $sidebar, array($widget_id) ); + $_POST = array('sidebar' => $sidebar_id, 'widget-' . $id_base => array(), 'the-widget-id' => $widget_id, 'delete_widget' => '1'); + } + + $_POST['widget-id'] = $sidebar; + + foreach ( (array) $wp_registered_widget_updates as $name => $control ) { + if ( $name != $id_base || !is_callable($control['callback']) ) + continue; + + ob_start(); + call_user_func_array( $control['callback'], $control['params'] ); + ob_end_clean(); + + break; + } + + $sidebars_widgets[$sidebar_id] = $sidebar; + + // Remove old position. + if ( !isset($_POST['delete_widget']) ) { + foreach ( $sidebars_widgets as $key => $sb ) { + if ( is_array($sb) ) + $sidebars_widgets[$key] = array_diff( $sb, array($widget_id) ); + } + array_splice( $sidebars_widgets[$sidebar_id], $position, 0, $widget_id ); + } + + wp_set_sidebars_widgets($sidebars_widgets); + wp_redirect( admin_url('widgets.php?message=0') ); + exit; +} + +// Output the widget form without js +if ( isset($_GET['editwidget']) && $_GET['editwidget'] ) { + $widget_id = $_GET['editwidget']; + + if ( isset($_GET['addnew']) ) { + // Default to the first sidebar + $keys = array_keys( $wp_registered_sidebars ); + $sidebar = array_shift( $keys ); + + if ( isset($_GET['base']) && isset($_GET['num']) ) { // multi-widget + // Copy minimal info from an existing instance of this widget to a new instance + foreach ( $wp_registered_widget_controls as $control ) { + if ( $_GET['base'] === $control['id_base'] ) { + $control_callback = $control['callback']; + $multi_number = (int) $_GET['num']; + $control['params'][0]['number'] = -1; + $widget_id = $control['id'] = $control['id_base'] . '-' . $multi_number; + $wp_registered_widget_controls[$control['id']] = $control; + break; + } + } + } + } + + if ( isset($wp_registered_widget_controls[$widget_id]) && !isset($control) ) { + $control = $wp_registered_widget_controls[$widget_id]; + $control_callback = $control['callback']; + } elseif ( !isset($wp_registered_widget_controls[$widget_id]) && isset($wp_registered_widgets[$widget_id]) ) { + $name = esc_html( strip_tags($wp_registered_widgets[$widget_id]['name']) ); + } + + if ( !isset($name) ) + $name = esc_html( strip_tags($control['name']) ); + + if ( !isset($sidebar) ) + $sidebar = isset($_GET['sidebar']) ? $_GET['sidebar'] : 'wp_inactive_widgets'; + + if ( !isset($multi_number) ) + $multi_number = isset($control['params'][0]['number']) ? $control['params'][0]['number'] : ''; + + $id_base = isset($control['id_base']) ? $control['id_base'] : $control['id']; + + // Show the widget form. + $width = ' style="width:' . max($control['width'], 350) . 'px"'; + $key = isset($_GET['key']) ? (int) $_GET['key'] : 0; + + require_once( ABSPATH . 'wp-admin/admin-header.php' ); ?> +
    +

    +
    > +

    + +
    +
    +' . __('There are no options for this widget.') . "

    \n"; ?> +
    + +

    +
    + + $sbvalue ) { + echo "\t\t\n"; + } ?> +
    "; + if ( 'wp_inactive_widgets' == $sbname || 'orphaned_widgets' == substr( $sbname, 0, 16 ) ) { + echo ' '; + } else { + if ( !isset($sidebars_widgets[$sbname]) || !is_array($sidebars_widgets[$sbname]) ) { + $j = 1; + $sidebars_widgets[$sbname] = array(); + } else { + $j = count($sidebars_widgets[$sbname]); + if ( isset($_GET['addnew']) || !in_array($widget_id, $sidebars_widgets[$sbname], true) ) + $j++; + } + $selected = ''; + echo "\t\t\n"; + } + echo "
    +
    + +
    + + + + + + + +
    +
    +
    +
    +
    + + +
    +

    +%2$s', + admin_url( 'customize.php?autofocus[panel]=widgets' ), + __( 'Manage in Customizer' ) + ); + } +?> +

    + + +

    + + +

    + + + + +
    +
    +
    + +
    + +
    + +
    +
    +
    +
    +
    + + $registered_sidebar ) { + if ( false !== strpos( $registered_sidebar['class'], 'inactive-sidebar' ) || 'orphaned_widgets' == substr( $sidebar, 0, 16 ) ) { + $wrap_class = 'widgets-holder-wrap'; + if ( !empty( $registered_sidebar['class'] ) ) + $wrap_class .= ' ' . $registered_sidebar['class']; + + ?> +
    +
    + +
    +
    +
    + +
    +
    + 1 ) { + $split = ceil( $sidebars_count / 2 ); +} else { + $single_sidebar_class = ' class="single-sidebar"'; +} + +?> +
    +
    > + +
    +
    +
    + +
    +
    +
    + +
    +
      +
      + + +
      +
      + +comment_status ) ) { + /** + * Fires when a comment is attempted on a post that does not exist. + * + * @since 1.5.0 + * + * @param int $comment_post_ID Post ID. + */ + do_action( 'comment_id_not_found', $comment_post_ID ); + exit; +} + +// get_post_status() will get the parent status for attachments. +$status = get_post_status($post); + +$status_obj = get_post_status_object($status); + +if ( ! comments_open( $comment_post_ID ) ) { + /** + * Fires when a comment is attempted on a post that has comments closed. + * + * @since 1.5.0 + * + * @param int $comment_post_ID Post ID. + */ + do_action( 'comment_closed', $comment_post_ID ); + wp_die( __( 'Sorry, comments are closed for this item.' ), 403 ); +} elseif ( 'trash' == $status ) { + /** + * Fires when a comment is attempted on a trashed post. + * + * @since 2.9.0 + * + * @param int $comment_post_ID Post ID. + */ + do_action( 'comment_on_trash', $comment_post_ID ); + exit; +} elseif ( ! $status_obj->public && ! $status_obj->private ) { + /** + * Fires when a comment is attempted on a post in draft mode. + * + * @since 1.5.1 + * + * @param int $comment_post_ID Post ID. + */ + do_action( 'comment_on_draft', $comment_post_ID ); + exit; +} elseif ( post_password_required( $comment_post_ID ) ) { + /** + * Fires when a comment is attempted on a password-protected post. + * + * @since 2.9.0 + * + * @param int $comment_post_ID Post ID. + */ + do_action( 'comment_on_password_protected', $comment_post_ID ); + exit; +} else { + /** + * Fires before a comment is posted. + * + * @since 2.8.0 + * + * @param int $comment_post_ID Post ID. + */ + do_action( 'pre_comment_on_post', $comment_post_ID ); +} + +$comment_author = ( isset($_POST['author']) ) ? trim(strip_tags($_POST['author'])) : null; +$comment_author_email = ( isset($_POST['email']) ) ? trim($_POST['email']) : null; +$comment_author_url = ( isset($_POST['url']) ) ? trim($_POST['url']) : null; +$comment_content = ( isset($_POST['comment']) ) ? trim($_POST['comment']) : null; + +// If the user is logged in +$user = wp_get_current_user(); +if ( $user->exists() ) { + if ( empty( $user->display_name ) ) + $user->display_name=$user->user_login; + $comment_author = wp_slash( $user->display_name ); + $comment_author_email = wp_slash( $user->user_email ); + $comment_author_url = wp_slash( $user->user_url ); + if ( current_user_can( 'unfiltered_html' ) ) { + if ( ! isset( $_POST['_wp_unfiltered_html_comment'] ) + || ! wp_verify_nonce( $_POST['_wp_unfiltered_html_comment'], 'unfiltered-html-comment_' . $comment_post_ID ) + ) { + kses_remove_filters(); // start with a clean slate + kses_init_filters(); // set up the filters + } + } +} else { + if ( get_option( 'comment_registration' ) || 'private' == $status ) { + wp_die( __( 'Sorry, you must be logged in to post a comment.' ), 403 ); + } +} + +$comment_type = ''; + +if ( get_option('require_name_email') && !$user->exists() ) { + if ( 6 > strlen( $comment_author_email ) || '' == $comment_author ) { + wp_die( __( 'ERROR: please fill the required fields (name, email).' ), 200 ); + } else if ( ! is_email( $comment_author_email ) ) { + wp_die( __( 'ERROR: please enter a valid email address.' ), 200 ); + } +} + +if ( '' == $comment_content ) { + wp_die( __( 'ERROR: please type a comment.' ), 200 ); +} + +$comment_parent = isset($_POST['comment_parent']) ? absint($_POST['comment_parent']) : 0; + +$commentdata = compact('comment_post_ID', 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_content', 'comment_type', 'comment_parent', 'user_ID'); + +$comment_id = wp_new_comment( $commentdata ); +if ( ! $comment_id ) { + wp_die( __( "ERROR: The comment could not be saved. Please try again later." ) ); +} + +$comment = get_comment( $comment_id ); + +/** + * Perform other actions when comment cookies are set. + * + * @since 3.4.0 + * + * @param object $comment Comment object. + * @param WP_User $user User object. The user may not exist. + */ +do_action( 'set_comment_cookies', $comment, $user ); + +$location = empty($_POST['redirect_to']) ? get_comment_link($comment_id) : $_POST['redirect_to'] . '#comment-' . $comment_id; + +/** + * Filter the location URI to send the commenter after posting. + * + * @since 2.0.5 + * + * @param string $location The 'redirect_to' URI sent via $_POST. + * @param object $comment Comment object. + */ +$location = apply_filters( 'comment_post_redirect', $location, $comment ); + +wp_safe_redirect( $location ); +exit; diff --git a/wp-config-sample.php b/wp-config-sample.php new file mode 100644 index 0000000..41e4a63 --- /dev/null +++ b/wp-config-sample.php @@ -0,0 +1,80 @@ + + Order Deny,Allow + Deny from all + + +# Apache 2.4 + + Require all denied + + +# Akismet CSS and JS + + + Allow from all + + + + Require all granted + + + +# Akismet images + + + Allow from all + + + + Require all granted + + \ No newline at end of file diff --git a/wp-content/plugins/akismet/_inc/akismet.css b/wp-content/plugins/akismet/_inc/akismet.css new file mode 100644 index 0000000..ff076aa --- /dev/null +++ b/wp-content/plugins/akismet/_inc/akismet.css @@ -0,0 +1,361 @@ + +#submitted-on { + position: relative; +} +#the-comment-list .author .akismet-user-comment-count { + display: inline; +} +#the-comment-list .author a span { + text-decoration: none; + color: #999; +} +#the-comment-list .author a span.akismet-span-link { + text-decoration: inherit; + color: inherit; +} +#the-comment-list .remove_url { + margin-left: 3px; + color: #999; + padding: 2px 3px 2px 0; +} +#the-comment-list .remove_url:hover { + color: #A7301F; + font-weight: bold; + padding: 2px 2px 2px 0; +} +#dashboard_recent_comments .akismet-status { + display: none; +} +.akismet-status { + float: right; +} +.akismet-status a { + color: #AAA; + font-style: italic; +} +span.comment-link a { + text-decoration: underline; +} +span.comment-link:after { + content: " "attr(title) " "; + color: #aaa; + text-decoration: none; +} +.mshot-arrow { + width: 0; + height: 0; + border-top: 10px solid transparent; + border-bottom: 10px solid transparent; + border-right: 10px solid #5C5C5C; + position: absolute; + left: -6px; + top: 91px; +} +.mshot-container { + background: #5C5C5C; + position: absolute; + top: -94px; + padding: 7px; + width: 450px; + height: 338px; + z-index: 20000; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-border-radius: 6px; +} +h2.ak-header { + padding: 30px; + background: #649316 url('img/logo-full-2x.png') no-repeat 20px center; + background-size: 185px 33px; + height: 33px; + text-indent: -9999em; + margin-right: 10px; +} +.checkforspam { + display: inline-block !important; +} +.checkforspam-spinner { + display: inline-block; + margin-top: 7px; +} + +.config-wrap { + margin-top: 2em; + max-width: 700px; +} + +.activate-option { + background: #e3e3e3; + border-radius: 3px; + margin-bottom: 30px; + overflow: hidden; + padding: 20px; +} +.activate-option.clicked { + background: #649316; + color: #fff; +} +.activate-option.clicked:hover { + background: #68802E; + color: #fff; +} + +.activate-option .button.button-secondary { + margin: 15px 0; +} + +.activate-option p { + margin: 10px 0 10px; +} + +.activate-highlight { + background: #fff; + padding: 30px; + margin-right: 10px; +} + +.activate-highlight.secondary { + background: #ddd; + padding: 20px 30px; +} + +.activate-highlight h3 { + margin: 0 0 0.3em; +} +.activate-highlight p { + color: #777; +} +.activate-highlight .button-primary { + margin-top: 15px; +} + +#akismet-enter-api-key .regular-text { + width: 18em; + margin-top: 15px; +} + +.right { + float: right; +} + +.alert-text { + color: #dd3d36; +} +.success { + color: #649316; +} +.option-description { + float: left; + font-size: 16px; +} +.option-description span { + color: #666; + display: block; + font-size: 14px; + margin-top: 5px; +} +.option-action { + float: right; +} +.key-config-link { + font-size: 14px; + margin-left: 20px; +} +.jetpack-account { + float: left; + font-size: 18px; + margin-right: 40px; +} +.small-heading { + color: #777; + display: block; + font-size: 12px; + font-weight: bold; + margin-bottom: 5px; + text-transform: uppercase; +} +.inline-label { + background: #ddd; + border-radius: 3px; + font-size: 11px; + padding: 3px 8px; + text-transform: uppercase; +} +.inline-label.alert { + background: #e54747; + color: #fff; +} +.jetpack-account .inline-label { + margin-left: 5px; +} +.option-action .manual-key { + margin-top: 7px; +} + +.alert { + border: 1px solid #e5e5e5; + padding: 0.4em 1em 1.4em 1em; + border-radius: 3px; + -webkit-border-radius: 3px; + border-width: 1px; + border-style: solid; +} + +.alert h3.key-status { + color: #fff; + margin: 1em 0 0.5em 0; +} + +.alert.critical { + background-color: #993300; +} + +.alert.active { + background-color: #649316; +} + +.alert p.key-status { + font-size: 24px; +} + +.alert p.description { + color:#fff; + font-size: 14px; + margin: 0 0; + font-style: normal; +} + +.alert p.description a, +.alert p.description a, +.alert p.description a, +.alert p.description a { + color: #fff; +} + +.new-snapshot { + margin-top: 1em; + padding: 1em; + text-align: center; +} + +.new-snapshot.stats { + background: #fff; + border: 1px solid #e5e5e5; +} + +.new-snapshot h3 { + background: #f5f5f5; + color: #888; + font-size: 11px; + margin: 0; + padding: 3px; +} + +.new-snapspot ul { + font-size: 12px; + width: 100%; +} + +.new-snapshot ul li { + color: #999; + float: left; + font-size: 11px; + padding: 0 20px; + text-transform: uppercase; + width: 33%; + box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + -ms-box-sizing: border-box; +} + +.new-snapshot.stats ul li:first-child, +.new-snapshot.stats ul li:nth-child(2) { + border-right:1px dotted #ccc; +} + +.new-snapshot.account ul li:nth-child(2) { + border-right: none; +} + +.new-snapshot ul li span { + color: #52accc; + display: block; + font-size: 32px; + font-weight: lighter; + line-height: 1.5em; +} + +.new-snapshot.stats { +} + +.new-snapshot.account, +.new-snapshot.settings { + float: left; + padding: 0; + text-align: left; + width: 50%; + box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + -ms-box-sizing: border-box; +} + +.account-container { + background: #fff; + border: 1px solid #e5e5e5; + margin-right: 0.5em; +} + +.settings-container { + background: #fff; + border: 1px solid #e5e5e5; + margin-left: 0.5em; +} + +.new-snapshot.account ul li { + width:100% +} + +.new-snapshot.account ul li span { + font-size: 14px; + font-weight: normal; +} + + +.new-snapshot.settings ul li { + border: none; + display: block; + width:100% +} + +.new-snapshot.settings ul li span { + display: block; + font-size: 14px; + font-weight: normal; +} + +.new-snapshot.settings p.submit { + margin: 0; + text-align: center; +} + +.akismet-settings th.strictness { + vertical-align: top; + padding-top: 15px; +} + +.akismet-settings input[type=text] { + width: 75%; +} + +.akismet-settings span.note{ + float: left; + padding-left: 23px; + font-size: 75%; + margin-top: -10px; +} + +.clearfix { + clear:both; +} \ No newline at end of file diff --git a/wp-content/plugins/akismet/_inc/akismet.js b/wp-content/plugins/akismet/_inc/akismet.js new file mode 100644 index 0000000..7da76cd --- /dev/null +++ b/wp-content/plugins/akismet/_inc/akismet.js @@ -0,0 +1,158 @@ +jQuery( function ( $ ) { + $( 'a.activate-option' ).click( function(){ + var link = $( this ); + if ( link.hasClass( 'clicked' ) ) { + link.removeClass( 'clicked' ); + } + else { + link.addClass( 'clicked' ); + } + $( '.toggle-have-key' ).slideToggle( 'slow', function() {}); + return false; + }); + $('.akismet-status').each(function () { + var thisId = $(this).attr('commentid'); + $(this).prependTo('#comment-' + thisId + ' .column-comment'); + }); + $('.akismet-user-comment-count').each(function () { + var thisId = $(this).attr('commentid'); + $(this).insertAfter('#comment-' + thisId + ' .author strong:first').show(); + }); + $('#the-comment-list').find('tr.comment, tr[id ^= "comment-"]').find('.column-author a[title ^= "http://"], .column-author a[title ^= "https://"]').each(function () { + var thisTitle = $(this).attr('title'); + thisCommentId = $(this).parents('tr:first').attr('id').split("-"); + + $(this).attr("id", "author_comment_url_"+ thisCommentId[1]); + + if (thisTitle) { + $(this).after( + $( 'x' ) + .attr( 'commentid', thisCommentId[1] ) + .attr( 'title', WPAkismet.strings['Remove this URL'] ) + ); + } + }); + $('.remove_url').live('click', function () { + var thisId = $(this).attr('commentid'); + var data = { + action: 'comment_author_deurl', + _wpnonce: WPAkismet.comment_author_url_nonce, + id: thisId + }; + $.ajax({ + url: ajaxurl, + type: 'POST', + data: data, + beforeSend: function () { + // Removes "x" link + $("a[commentid='"+ thisId +"']").hide(); + // Show temp status + $("#author_comment_url_"+ thisId).html( $( '' ).text( WPAkismet.strings['Removing...'] ) ); + }, + success: function (response) { + if (response) { + // Show status/undo link + $("#author_comment_url_"+ thisId) + .attr('cid', thisId) + .addClass('akismet_undo_link_removal') + .html( + $( '' ).text( WPAkismet.strings['URL removed'] ) + ) + .append( ' ' ) + .append( + $( '' ) + .text( WPAkismet.strings['(undo)'] ) + .addClass( 'akismet-span-link' ) + ); + } + } + }); + + return false; + }); + $('.akismet_undo_link_removal').live('click', function () { + var thisId = $(this).attr('cid'); + var thisUrl = $(this).attr('href'); + var data = { + action: 'comment_author_reurl', + _wpnonce: WPAkismet.comment_author_url_nonce, + id: thisId, + url: thisUrl + }; + $.ajax({ + url: ajaxurl, + type: 'POST', + data: data, + beforeSend: function () { + // Show temp status + $("#author_comment_url_"+ thisId).html( $( '' ).text( WPAkismet.strings['Re-adding...'] ) ); + }, + success: function (response) { + if (response) { + // Add "x" link + $("a[commentid='"+ thisId +"']").show(); + // Show link. Core strips leading http://, so let's do that too. + $("#author_comment_url_"+ thisId).removeClass('akismet_undo_link_removal').text( thisUrl.replace( /^http:\/\/(www\.)?/ig, '' ) ); + } + } + }); + + return false; + }); + $('a[id^="author_comment_url"], tr.pingback td.column-author a:first-of-type').mouseover(function () { + var wpcomProtocol = ( 'https:' === location.protocol ) ? 'https://' : 'http://'; + // Need to determine size of author column + var thisParentWidth = $(this).parent().width(); + // It changes based on if there is a gravatar present + thisParentWidth = ($(this).parent().find('.grav-hijack').length) ? thisParentWidth - 42 + 'px' : thisParentWidth + 'px'; + if ($(this).find('.mShot').length == 0 && !$(this).hasClass('akismet_undo_link_removal')) { + var self = $( this ); + $('.widefat td').css('overflow', 'visible'); + $(this).css('position', 'relative'); + var thisHref = $.URLEncode( $(this).attr('href') ); + $(this).append('
      '); + setTimeout(function () { + self.find( '.mshot-image' ).attr('src', '//s0.wordpress.com/mshots/v1/'+thisHref+'?w=450&r=2'); + }, 6000); + setTimeout(function () { + self.find( '.mshot-image' ).attr('src', '//s0.wordpress.com/mshots/v1/'+thisHref+'?w=450&r=3'); + }, 12000); + } else { + $(this).find('.mShot').css('left', thisParentWidth).show(); + } + }).mouseout(function () { + $(this).find('.mShot').hide(); + }); + $('.checkforspam:not(.button-disabled)').click( function(e) { + $('.checkforspam:not(.button-disabled)').addClass('button-disabled'); + $('.checkforspam-spinner').addClass( 'spinner' ); + akismet_check_for_spam(0, 100); + e.preventDefault(); + }); + + function akismet_check_for_spam(offset, limit) { + $.post( + ajaxurl, + { + 'action': 'akismet_recheck_queue', + 'offset': offset, + 'limit': limit + }, + function(result) { + if (result.processed < limit) { + window.location.reload(); + } + else { + akismet_check_for_spam(offset + limit, limit); + } + } + ); + } +}); +// URL encode plugin +jQuery.extend({URLEncode:function(c){var o='';var x=0;c=c.toString();var r=/(^[a-zA-Z0-9_.]*)/; + while(x1 && m[1]!=''){o+=m[1];x+=m[1].length; + }else{if(c[x]==' ')o+='+';else{var d=c.charCodeAt(x);var h=d.toString(16); + o+='%'+(h.length<2?'0':'')+h.toUpperCase();}x++;}}return o;} +}); diff --git a/wp-content/plugins/akismet/_inc/form.js b/wp-content/plugins/akismet/_inc/form.js new file mode 100644 index 0000000..3a5be8a --- /dev/null +++ b/wp-content/plugins/akismet/_inc/form.js @@ -0,0 +1,30 @@ +var ak_js = document.getElementById( "ak_js" ); + +if ( ! ak_js ) { + ak_js = document.createElement( 'input' ); + ak_js.setAttribute( 'id', 'ak_js' ); + ak_js.setAttribute( 'name', 'ak_js' ); + ak_js.setAttribute( 'type', 'hidden' ); +} +else { + ak_js.parentNode.removeChild( ak_js ); +} + +ak_js.setAttribute( 'value', ( new Date() ).getTime() ); + +var commentForm = document.getElementById( 'commentform' ); + +if ( commentForm ) { + commentForm.appendChild( ak_js ); +} +else { + var replyRowContainer = document.getElementById( 'replyrow' ); + + if ( replyRowContainer ) { + var children = replyRowContainer.getElementsByTagName( 'td' ); + + if ( children.length > 0 ) { + children[0].appendChild( ak_js ); + } + } +} \ No newline at end of file diff --git a/wp-content/plugins/akismet/_inc/img/logo-full-2x.png b/wp-content/plugins/akismet/_inc/img/logo-full-2x.png new file mode 100644 index 0000000..a9bed8b Binary files /dev/null and b/wp-content/plugins/akismet/_inc/img/logo-full-2x.png differ diff --git a/wp-content/plugins/akismet/akismet.php b/wp-content/plugins/akismet/akismet.php new file mode 100644 index 0000000..dc42c2a --- /dev/null +++ b/wp-content/plugins/akismet/akismet.php @@ -0,0 +1,59 @@ +protect your blog from comment and trackback spam. It keeps your site protected from spam even while you sleep. To get started: 1) Click the "Activate" link to the left of this description, 2) Sign up for an Akismet API key, and 3) Go to your Akismet configuration page, and save your API key. +Version: 3.0.4 +Author: Automattic +Author URI: http://automattic.com/wordpress-plugins/ +License: GPLv2 or later +Text Domain: akismet +*/ + +/* +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +// Make sure we don't expose any info if called directly +if ( !function_exists( 'add_action' ) ) { + echo 'Hi there! I\'m just a plugin, not much I can do when called directly.'; + exit; +} + +define( 'AKISMET_VERSION', '3.0.4' ); +define( 'AKISMET__MINIMUM_WP_VERSION', '3.1' ); +define( 'AKISMET__PLUGIN_URL', plugin_dir_url( __FILE__ ) ); +define( 'AKISMET__PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); +define( 'AKISMET_DELETE_LIMIT', 100000 ); + +register_activation_hook( __FILE__, array( 'Akismet', 'plugin_activation' ) ); +register_deactivation_hook( __FILE__, array( 'Akismet', 'plugin_deactivation' ) ); + +require_once( AKISMET__PLUGIN_DIR . 'class.akismet.php' ); +require_once( AKISMET__PLUGIN_DIR . 'class.akismet-widget.php' ); + +add_action( 'init', array( 'Akismet', 'init' ) ); + +if ( is_admin() ) { + require_once( AKISMET__PLUGIN_DIR . 'class.akismet-admin.php' ); + add_action( 'init', array( 'Akismet_Admin', 'init' ) ); +} + +//add wrapper class around deprecated akismet functions that are referenced elsewhere +require_once( AKISMET__PLUGIN_DIR . 'wrapper.php' ); + diff --git a/wp-content/plugins/akismet/class.akismet-admin.php b/wp-content/plugins/akismet/class.akismet-admin.php new file mode 100644 index 0000000..840b367 --- /dev/null +++ b/wp-content/plugins/akismet/class.akismet-admin.php @@ -0,0 +1,885 @@ +'.__('Settings', 'akismet').''; + array_unshift( $links, $settings_link ); + return $links; + } + + public static function load_menu() { + if ( class_exists( 'Jetpack' ) ) + $hook = add_submenu_page( 'jetpack', __( 'Akismet' , 'akismet'), __( 'Akismet' , 'akismet'), 'manage_options', 'akismet-key-config', array( 'Akismet_Admin', 'display_page' ) ); + else + $hook = add_options_page( __('Akismet', 'akismet'), __('Akismet', 'akismet'), 'manage_options', 'akismet-key-config', array( 'Akismet_Admin', 'display_page' ) ); + + if ( version_compare( $GLOBALS['wp_version'], '3.3', '>=' ) ) { + add_action( "load-$hook", array( 'Akismet_Admin', 'admin_help' ) ); + } + } + + public static function load_resources() { + global $hook_suffix; + + if ( in_array( $hook_suffix, array( + 'index.php', # dashboard + 'edit-comments.php', + 'comment.php', + 'post.php', + 'settings_page_akismet-key-config', + 'jetpack_page_akismet-key-config', + ) ) ) { + wp_register_style( 'akismet.css', AKISMET__PLUGIN_URL . '_inc/akismet.css', array(), AKISMET_VERSION ); + wp_enqueue_style( 'akismet.css'); + + wp_register_script( 'akismet.js', AKISMET__PLUGIN_URL . '_inc/akismet.js', array('jquery','postbox'), AKISMET_VERSION ); + wp_enqueue_script( 'akismet.js' ); + wp_localize_script( 'akismet.js', 'WPAkismet', array( + 'comment_author_url_nonce' => wp_create_nonce( 'comment_author_url_nonce' ), + 'strings' => array( + 'Remove this URL' => __( 'Remove this URL' , 'akismet'), + 'Removing...' => __( 'Removing...' , 'akismet'), + 'URL removed' => __( 'URL removed' , 'akismet'), + '(undo)' => __( '(undo)' , 'akismet'), + 'Re-adding...' => __( 'Re-adding...' , 'akismet'), + ) + ) ); + } + } + + /** + * Add help to the Akismet page + * + * @return false if not the Akismet page + */ + public static function admin_help() { + $current_screen = get_current_screen(); + + // Screen Content + if ( current_user_can( 'manage_options' ) ) { + if ( !Akismet::get_api_key() || ( isset( $_GET['view'] ) && $_GET['view'] == 'start' ) ) { + //setup page + $current_screen->add_help_tab( + array( + 'id' => 'overview', + 'title' => __( 'Overview' , 'akismet'), + 'content' => + '

      ' . esc_html__( 'Akismet Setup' , 'akismet') . '

      ' . + '

      ' . esc_html__( 'Akismet filters out your comment and trackback spam for you, so you can focus on more important things.' , 'akismet') . '

      ' . + '

      ' . esc_html__( 'On this page, you are able to setup the Akismet plugin.' , 'akismet') . '

      ', + ) + ); + + $current_screen->add_help_tab( + array( + 'id' => 'setup-signup', + 'title' => __( 'New to Akismet' , 'akismet'), + 'content' => + '

      ' . esc_html__( 'Akismet Setup' , 'akismet') . '

      ' . + '

      ' . esc_html__( 'You need to enter an API key to activate the Akismet service on your site.' , 'akismet') . '

      ' . + '

      ' . sprintf( __( 'Signup for an account on %s to get an API Key.' , 'akismet'), 'Akismet.com' ) . '

      ', + ) + ); + + $current_screen->add_help_tab( + array( + 'id' => 'setup-manual', + 'title' => __( 'Enter an API Key' , 'akismet'), + 'content' => + '

      ' . esc_html__( 'Akismet Setup' , 'akismet') . '

      ' . + '

      ' . esc_html__( 'If you already have an API key' , 'akismet') . '

      ' . + '
        ' . + '
      1. ' . esc_html__( 'Copy and paste the API key into the text field.' , 'akismet') . '
      2. ' . + '
      3. ' . esc_html__( 'Click the Use this Key button.' , 'akismet') . '
      4. ' . + '
      ', + ) + ); + } + elseif ( isset( $_GET['view'] ) && $_GET['view'] == 'stats' ) { + //stats page + $current_screen->add_help_tab( + array( + 'id' => 'overview', + 'title' => __( 'Overview' , 'akismet'), + 'content' => + '

      ' . esc_html__( 'Akismet Stats' , 'akismet') . '

      ' . + '

      ' . esc_html__( 'Akismet filters out your comment and trackback spam for you, so you can focus on more important things.' , 'akismet') . '

      ' . + '

      ' . esc_html__( 'On this page, you are able to view stats on spam filtered on your site.' , 'akismet') . '

      ', + ) + ); + } + else { + //configuration page + $current_screen->add_help_tab( + array( + 'id' => 'overview', + 'title' => __( 'Overview' , 'akismet'), + 'content' => + '

      ' . esc_html__( 'Akismet Configuration' , 'akismet') . '

      ' . + '

      ' . esc_html__( 'Akismet filters out your comment and trackback spam for you, so you can focus on more important things.' , 'akismet') . '

      ' . + '

      ' . esc_html__( 'On this page, you are able to enter/remove an API key, view account information and view spam stats.' , 'akismet') . '

      ', + ) + ); + + $current_screen->add_help_tab( + array( + 'id' => 'settings', + 'title' => __( 'Settings' , 'akismet'), + 'content' => + '

      ' . esc_html__( 'Akismet Configuration' , 'akismet') . '

      ' . + '

      ' . esc_html__( 'API Key' , 'akismet') . ' - ' . esc_html__( 'Enter/remove an API key.' , 'akismet') . '

      ' . + '

      ' . esc_html__( 'Comments' , 'akismet') . ' - ' . esc_html__( 'Show the number of approved comments beside each comment author in the comments list page.' , 'akismet') . '

      ' . + '

      ' . esc_html__( 'Strictness' , 'akismet') . ' - ' . esc_html__( 'Choose to either discard the worst spam automatically or to always put all spam in spam folder.' , 'akismet') . '

      ', + ) + ); + + $current_screen->add_help_tab( + array( + 'id' => 'account', + 'title' => __( 'Account' , 'akismet'), + 'content' => + '

      ' . esc_html__( 'Akismet Configuration' , 'akismet') . '

      ' . + '

      ' . esc_html__( 'Subscription Type' , 'akismet') . ' - ' . esc_html__( 'The Akismet subscription plan' , 'akismet') . '

      ' . + '

      ' . esc_html__( 'Status' , 'akismet') . ' - ' . esc_html__( 'The subscription status - active, cancelled or suspended' , 'akismet') . '

      ', + ) + ); + } + } + + // Help Sidebar + $current_screen->set_help_sidebar( + '

      ' . esc_html__( 'For more information:' , 'akismet') . '

      ' . + '

      ' . esc_html__( 'Akismet FAQ' , 'akismet') . '

      ' . + '

      ' . esc_html__( 'Akismet Support' , 'akismet') . '

      ' + ); + } + + public static function enter_api_key() { + if ( function_exists('current_user_can') && !current_user_can('manage_options') ) + die(__('Cheatin’ uh?', 'akismet')); + + if ( !wp_verify_nonce( $_POST['_wpnonce'], self::NONCE ) ) + return false; + + foreach( array( 'akismet_strictness', 'akismet_show_user_comments_approved' ) as $option ) { + update_option( $option, isset( $_POST[$option] ) && (int) $_POST[$option] == 1 ? '1' : '0' ); + } + + if ( defined( 'WPCOM_API_KEY' ) ) + return false; //shouldn't have option to save key if already defined + + $new_key = preg_replace( '/[^a-h0-9]/i', '', $_POST['key'] ); + $old_key = Akismet::get_api_key(); + + if ( empty( $new_key ) ) { + if ( !empty( $old_key ) ) { + delete_option( 'wordpress_api_key' ); + self::$notices[] = 'new-key-empty'; + } + } + elseif ( $new_key != $old_key ) { + self::save_key( $new_key ); + } + + return true; + } + + public static function save_key( $api_key ) { + $key_status = Akismet::verify_key( $api_key ); + + if ( $key_status == 'valid' ) { + $akismet_user = self::get_akismet_user( $api_key ); + + if ( $akismet_user ) { + if ( in_array( $akismet_user->status, array( 'active', 'active-dunning', 'no-sub' ) ) ) + update_option( 'wordpress_api_key', $api_key ); + + if ( $akismet_user->status == 'active' ) + self::$notices['status'] = 'new-key-valid'; + else + self::$notices['status'] = $akismet_user->status; + } + else + self::$notices['status'] = 'new-key-invalid'; + } + elseif ( in_array( $key_status, array( 'invalid', 'failed' ) ) ) + self::$notices['status'] = 'new-key-'.$key_status; + } + + public static function dashboard_stats() { + if ( !function_exists('did_action') || did_action( 'rightnow_end' ) ) + return; // We already displayed this info in the "Right Now" section + + if ( !$count = get_option('akismet_spam_count') ) + return; + + global $submenu; + + echo '

      ' . esc_html( _x( 'Spam', 'comments' , 'akismet') ) . '

      '; + + echo '

      '.sprintf( _n( + 'Akismet has protected your site from %3$s spam comment.', + 'Akismet has protected your site from %3$s spam comments.', + $count + , 'akismet'), 'https://akismet.com/wordpress/', esc_url( add_query_arg( array( 'page' => 'akismet-admin' ), admin_url( isset( $submenu['edit-comments.php'] ) ? 'edit-comments.php' : 'edit.php' ) ) ), number_format_i18n($count) ).'

      '; + } + + // WP 2.5+ + public static function rightnow_stats() { + global $submenu, $wp_db_version; + + if ( 8645 < $wp_db_version ) // 2.7 + $link = add_query_arg( array( 'comment_status' => 'spam' ), admin_url( 'edit-comments.php' ) ); + elseif ( isset( $submenu['edit-comments.php'] ) ) + $link = add_query_arg( array( 'page' => 'akismet-admin' ), admin_url( 'edit-comments.php' ) ); + else + $link = add_query_arg( array( 'page' => 'akismet-admin' ), admin_url( 'edit.php' ) ); + + if ( $count = get_option('akismet_spam_count') ) { + $intro = sprintf( _n( + 'Akismet has protected your site from %2$s spam comment already. ', + 'Akismet has protected your site from %2$s spam comments already. ', + $count + , 'akismet'), 'https://akismet.com/wordpress/', number_format_i18n( $count ) ); + } else { + $intro = sprintf( __('Akismet blocks spam from getting to your blog. ', 'akismet'), 'https://akismet.com/wordpress/' ); + } + + $link = function_exists( 'esc_url' ) ? esc_url( $link ) : clean_url( $link ); + if ( $queue_count = self::get_spam_count() ) { + $queue_text = sprintf( _n( + 'There’s %1$s comment in your spam queue right now.', + 'There are %1$s comments in your spam queue right now.', + $queue_count + , 'akismet'), number_format_i18n( $queue_count ), $link ); + } else { + $queue_text = sprintf( __( "There’s nothing in your spam queue at the moment." , 'akismet'), $link ); + } + + $text = $intro . '
      ' . $queue_text; + echo "

      $text

      \n"; + } + + public static function check_for_spam_button( $comment_status ) { + // The "Check for Spam" button should only appear when the page might be showing + // a comment with comment_approved=0, which means an un-trashed, un-spammed, + // not-yet-moderated comment. + if ( 'all' != $comment_status && 'moderated' != $comment_status ) { + return; + } + + if ( function_exists('plugins_url') ) + $link = add_query_arg( array( 'action' => 'akismet_recheck_queue' ), admin_url( 'admin.php' ) ); + else + $link = add_query_arg( array( 'page' => 'akismet-admin', 'recheckqueue' => 'true', 'noheader' => 'true' ), admin_url( 'edit-comments.php' ) ); + + echo '
      ' . esc_html__('Check for Spam', 'akismet') . ''; + } + + public static function recheck_queue() { + global $wpdb; + + Akismet::fix_scheduled_recheck(); + + if ( ! ( isset( $_GET['recheckqueue'] ) || ( isset( $_REQUEST['action'] ) && 'akismet_recheck_queue' == $_REQUEST['action'] ) ) ) + return; + + $paginate = ''; + if ( isset( $_POST['limit'] ) && isset( $_POST['offset'] ) ) { + $paginate = $wpdb->prepare( " LIMIT %d OFFSET %d", array( $_POST['limit'], $_POST['offset'] ) ); + } + $moderation = $wpdb->get_results( "SELECT * FROM {$wpdb->comments} WHERE comment_approved = '0'{$paginate}", ARRAY_A ); + + foreach ( (array) $moderation as $c ) { + $c['user_ip'] = $c['comment_author_IP']; + $c['user_agent'] = $c['comment_agent']; + $c['referrer'] = ''; + $c['blog'] = get_bloginfo('url'); + $c['blog_lang'] = get_locale(); + $c['blog_charset'] = get_option('blog_charset'); + $c['permalink'] = get_permalink($c['comment_post_ID']); + + $c['user_role'] = ''; + if ( isset( $c['user_ID'] ) ) + $c['user_role'] = Akismet::get_user_roles($c['user_ID']); + + if ( Akismet::is_test_mode() ) + $c['is_test'] = 'true'; + + add_comment_meta( $c['comment_ID'], 'akismet_rechecking', true ); + + $response = Akismet::http_post( Akismet::build_query( $c ), 'comment-check' ); + + if ( 'true' == $response[1] ) { + wp_set_comment_status( $c['comment_ID'], 'spam' ); + update_comment_meta( $c['comment_ID'], 'akismet_result', 'true' ); + delete_comment_meta( $c['comment_ID'], 'akismet_error' ); + delete_comment_meta( $c['comment_ID'], 'akismet_delayed_moderation_email' ); + Akismet::update_comment_history( $c['comment_ID'], __('Akismet re-checked and caught this comment as spam', 'akismet'), 'check-spam' ); + + } elseif ( 'false' == $response[1] ) { + update_comment_meta( $c['comment_ID'], 'akismet_result', 'false' ); + delete_comment_meta( $c['comment_ID'], 'akismet_error' ); + delete_comment_meta( $c['comment_ID'], 'akismet_delayed_moderation_email' ); + Akismet::update_comment_history( $c['comment_ID'], __('Akismet re-checked and cleared this comment', 'akismet'), 'check-ham' ); + // abnormal result: error + } else { + update_comment_meta( $c['comment_ID'], 'akismet_result', 'error' ); + Akismet::update_comment_history( $c['comment_ID'], sprintf( __('Akismet was unable to re-check this comment (response: %s)', 'akismet'), substr($response[1], 0, 50)), 'check-error' ); + } + + delete_comment_meta( $c['comment_ID'], 'akismet_rechecking' ); + } + if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { + wp_send_json( array( + 'processed' => count((array) $moderation), + )); + } + else { + $redirect_to = isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : admin_url( 'edit-comments.php' ); + wp_safe_redirect( $redirect_to ); + exit; + } + } + + // Adds an 'x' link next to author URLs, clicking will remove the author URL and show an undo link + public static function remove_comment_author_url() { + if ( !empty( $_POST['id'] ) && check_admin_referer( 'comment_author_url_nonce' ) ) { + $comment = get_comment( intval( $_POST['id'] ), ARRAY_A ); + if ( $comment && current_user_can( 'edit_comment', $comment['comment_ID'] ) ) { + $comment['comment_author_url'] = ''; + do_action( 'comment_remove_author_url' ); + print( wp_update_comment( $comment ) ); + die(); + } + } + } + + public static function add_comment_author_url() { + if ( !empty( $_POST['id'] ) && !empty( $_POST['url'] ) && check_admin_referer( 'comment_author_url_nonce' ) ) { + $comment = get_comment( intval( $_POST['id'] ), ARRAY_A ); + if ( $comment && current_user_can( 'edit_comment', $comment['comment_ID'] ) ) { + $comment['comment_author_url'] = esc_url( $_POST['url'] ); + do_action( 'comment_add_author_url' ); + print( wp_update_comment( $comment ) ); + die(); + } + } + } + + public static function comment_row_action( $a, $comment ) { + + // failsafe for old WP versions + if ( !function_exists('add_comment_meta') ) + return $a; + + $akismet_result = get_comment_meta( $comment->comment_ID, 'akismet_result', true ); + $akismet_error = get_comment_meta( $comment->comment_ID, 'akismet_error', true ); + $user_result = get_comment_meta( $comment->comment_ID, 'akismet_user_result', true); + $comment_status = wp_get_comment_status( $comment->comment_ID ); + $desc = null; + if ( $akismet_error ) { + $desc = __( 'Awaiting spam check' , 'akismet'); + } elseif ( !$user_result || $user_result == $akismet_result ) { + // Show the original Akismet result if the user hasn't overridden it, or if their decision was the same + if ( $akismet_result == 'true' && $comment_status != 'spam' && $comment_status != 'trash' ) + $desc = __( 'Flagged as spam by Akismet' , 'akismet'); + elseif ( $akismet_result == 'false' && $comment_status == 'spam' ) + $desc = __( 'Cleared by Akismet' , 'akismet'); + } else { + $who = get_comment_meta( $comment->comment_ID, 'akismet_user', true ); + if ( $user_result == 'true' ) + $desc = sprintf( __('Flagged as spam by %s', 'akismet'), $who ); + else + $desc = sprintf( __('Un-spammed by %s', 'akismet'), $who ); + } + + // add a History item to the hover links, just after Edit + if ( $akismet_result ) { + $b = array(); + foreach ( $a as $k => $item ) { + $b[ $k ] = $item; + if ( + $k == 'edit' + || ( $k == 'unspam' && $GLOBALS['wp_version'] >= 3.4 ) + ) { + $b['history'] = ' '. esc_html__('History', 'akismet') . ''; + } + } + + $a = $b; + } + + if ( $desc ) + echo ''.esc_html( $desc ).''; + + $show_user_comments = apply_filters( 'akismet_show_user_comments_approved', get_option('akismet_show_user_comments_approved') ); + $show_user_comments = $show_user_comments === 'false' ? false : $show_user_comments; //option used to be saved as 'false' / 'true' + + if ( $show_user_comments ) { + $comment_count = Akismet::get_user_comments_approved( $comment->user_id, $comment->comment_author_email, $comment->comment_author, $comment->comment_author_url ); + $comment_count = intval( $comment_count ); + echo ''; + } + + return $a; + } + + public static function comment_status_meta_box( $comment ) { + $history = Akismet::get_comment_history( $comment->comment_ID ); + + if ( $history ) { + echo '
      '; + foreach ( $history as $row ) { + $time = date( 'D d M Y @ h:i:m a', $row['time'] ) . ' GMT'; + echo '
      ' . sprintf( esc_html__('%s ago', 'akismet'), human_time_diff( $row['time'] ) ) . ' - '; + echo esc_html( $row['message'] ) . '
      '; + } + echo '
      '; + } + } + + public static function plugin_action_links( $links, $file ) { + if ( $file == plugin_basename( AKISMET__PLUGIN_URL . '/akismet.php' ) ) { + $links[] = ''.esc_html__( 'Settings' , 'akismet').''; + } + + return $links; + } + + public static function text_add_link_callback( $m ) { + // bare link? + if ( $m[4] == $m[2] ) + return ''.$m[4].''; + else + return ''.$m[4].''; + } + + public static function text_add_link_class( $comment_text ) { + return preg_replace_callback( '#]*)href="([^"]+)"([^>]*)>(.*?)#i', array( 'Akismet_Admin', 'text_add_link_callback' ), $comment_text ); + } + + // Total spam in queue + // get_option( 'akismet_spam_count' ) is the total caught ever + public static function get_spam_count( $type = false ) { + global $wpdb; + + if ( !$type ) { // total + $count = wp_cache_get( 'akismet_spam_count', 'widget' ); + if ( false === $count ) { + if ( function_exists('wp_count_comments') ) { + $count = wp_count_comments(); + $count = $count->spam; + } else { + $count = (int) $wpdb->get_var("SELECT COUNT(comment_ID) FROM {$wpdb->comments} WHERE comment_approved = 'spam'"); + } + wp_cache_set( 'akismet_spam_count', $count, 'widget', 3600 ); + } + return $count; + } elseif ( 'comments' == $type || 'comment' == $type ) { // comments + $type = ''; + } + + return (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(comment_ID) FROM {$wpdb->comments} WHERE comment_approved = 'spam' AND comment_type = %s", $type ) ); + } + + // Check connectivity between the WordPress blog and Akismet's servers. + // Returns an associative array of server IP addresses, where the key is the IP address, and value is true (available) or false (unable to connect). + public static function check_server_ip_connectivity() { + + $servers = $ips = array(); + + // Some web hosts may disable this function + if ( function_exists('gethostbynamel') ) { + + $ips = gethostbynamel( 'rest.akismet.com' ); + if ( $ips && is_array($ips) && count($ips) ) { + $api_key = Akismet::get_api_key(); + + foreach ( $ips as $ip ) { + $response = Akismet::verify_key( $api_key, $ip ); + // even if the key is invalid, at least we know we have connectivity + if ( $response == 'valid' || $response == 'invalid' ) + $servers[$ip] = 'connected'; + else + $servers[$ip] = $response ? $response : 'unable to connect'; + } + } + } + + return $servers; + } + + // Simpler connectivity check + public static function check_server_connectivity($cache_timeout = 86400) { + + $debug = array(); + $debug[ 'PHP_VERSION' ] = PHP_VERSION; + $debug[ 'WORDPRESS_VERSION' ] = $GLOBALS['wp_version']; + $debug[ 'AKISMET_VERSION' ] = AKISMET_VERSION; + $debug[ 'AKISMET__PLUGIN_DIR' ] = AKISMET__PLUGIN_DIR; + $debug[ 'SITE_URL' ] = site_url(); + $debug[ 'HOME_URL' ] = home_url(); + + $servers = get_option('akismet_available_servers'); + if ( (time() - get_option('akismet_connectivity_time') < $cache_timeout) && $servers !== false ) { + $servers = self::check_server_ip_connectivity(); + update_option('akismet_available_servers', $servers); + update_option('akismet_connectivity_time', time()); + } + + $response = wp_remote_get( 'http://rest.akismet.com/1.1/test' ); + + $debug[ 'gethostbynamel' ] = function_exists('gethostbynamel') ? 'exists' : 'not here'; + $debug[ 'Servers' ] = $servers; + $debug[ 'Test Connection' ] = $response; + + Akismet::log( $debug ); + + if ( $response && 'connected' == wp_remote_retrieve_body( $response ) ) + return true; + + return false; + } + + // Check the server connectivity and store the available servers in an option. + public static function get_server_connectivity($cache_timeout = 86400) { + return self::check_server_connectivity( $cache_timeout ); + } + + public static function get_number_spam_waiting() { + global $wpdb; + return (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->commentmeta} WHERE meta_key = 'akismet_error'" ); + } + + public static function get_page_url( $page = 'config' ) { + + $args = array( 'page' => 'akismet-key-config' ); + + if ( $page == 'stats' ) + $args = array( 'page' => 'akismet-key-config', 'view' => 'stats' ); + elseif ( $page == 'delete_key' ) + $args = array( 'page' => 'akismet-key-config', 'view' => 'start', 'action' => 'delete-key', '_wpnonce' => wp_create_nonce( self::NONCE ) ); + + $url = add_query_arg( $args, class_exists( 'Jetpack' ) ? admin_url( 'admin.php' ) : admin_url( 'options-general.php' ) ); + + return $url; + } + + public static function get_akismet_user( $api_key ) { + $akismet_user = Akismet::http_post( Akismet::build_query( array( 'key' => $api_key ) ), 'get-subscription' ); + + if ( ! empty( $akismet_user[1] ) ) + $akismet_user = json_decode( $akismet_user[1] ); + else + $akismet_user = false; + + return $akismet_user; + } + + public static function get_stats( $api_key ) { + $stat_totals = array(); + + foreach( array( '6-months', 'all' ) as $interval ) { + $response = Akismet::http_post( Akismet::build_query( array( 'blog' => urlencode( get_bloginfo('url') ), 'key' => $api_key, 'from' => $interval ) ), 'get-stats' ); + + if ( ! empty( $response[1] ) ) { + $stat_totals[$interval] = json_decode( $response[1] ); + } + } + return $stat_totals; + } + + public static function verify_wpcom_key( $api_key, $user_id, $extra = array() ) { + $akismet_account = Akismet::http_post( Akismet::build_query( array_merge( array( + 'user_id' => $user_id, + 'api_key' => $api_key, + 'get_account_type' => 'true' + ), $extra ) ), 'verify-wpcom-key' ); + + if ( ! empty( $akismet_account[1] ) ) + $akismet_account = json_decode( $akismet_account[1] ); + + Akismet::log( compact( 'akismet_account' ) ); + + return $akismet_account; + } + + public static function connect_jetpack_user() { + + if ( $jetpack_user = self::get_jetpack_user() ) { + if ( isset( $jetpack_user['user_id'] ) && isset( $jetpack_user['api_key'] ) ) { + $akismet_user = self::verify_wpcom_key( $jetpack_user['api_key'], $jetpack_user['user_id'], array( 'action' => 'connect_jetpack_user' ) ); + + if ( is_object( $akismet_user ) ) { + self::save_key( $akismet_user->api_key ); + return in_array( $akismet_user->status, array( 'active', 'active-dunning', 'no-sub' ) ); + } + } + } + + return false; + } + + public static function display_alert() { + Akismet::view( 'notice', array( + 'type' => 'alert', + 'code' => (int) get_option( 'akismet_alert_code' ), + 'msg' => get_option( 'akismet_alert_msg' ) + ) ); + } + + public static function display_spam_check_warning() { + Akismet::fix_scheduled_recheck(); + + if ( wp_next_scheduled('akismet_schedule_cron_recheck') > time() && self::get_number_spam_waiting() > 0 ) { + $link_text = apply_filters( 'akismet_spam_check_warning_link_text', sprintf( __( 'Please check your Akismet configuration and contact your web host if problems persist.', 'akismet'), esc_url( self::get_page_url() ) ) ); + Akismet::view( 'notice', array( 'type' => 'spam-check', 'link_text' => $link_text ) ); + } + } + + public static function display_invalid_version() { + Akismet::view( 'notice', array( 'type' => 'version' ) ); + } + + public static function display_api_key_warning() { + Akismet::view( 'notice', array( 'type' => 'plugin' ) ); + } + + public static function display_page() { + if ( !Akismet::get_api_key() || ( isset( $_GET['view'] ) && $_GET['view'] == 'start' ) ) + self::display_start_page(); + elseif ( isset( $_GET['view'] ) && $_GET['view'] == 'stats' ) + self::display_stats_page(); + else + self::display_configuration_page(); + } + + public static function display_start_page() { + if ( isset( $_GET['action'] ) ) { + if ( $_GET['action'] == 'delete-key' ) { + if ( isset( $_GET['_wpnonce'] ) && wp_verify_nonce( $_GET['_wpnonce'], self::NONCE ) ) + delete_option( 'wordpress_api_key' ); + } + } + + if ( $api_key = Akismet::get_api_key() ) { + self::display_configuration_page(); + return; + } + + //the user can choose to auto connect their API key by clicking a button on the akismet done page + //if jetpack, get verified api key by using connected wpcom user id + //if no jetpack, get verified api key by using an akismet token + + $akismet_user = false; + + if ( isset( $_GET['token'] ) && preg_match('/^(\d+)-[0-9a-f]{20}$/', $_GET['token'] ) ) + $akismet_user = self::verify_wpcom_key( '', '', array( 'token' => $_GET['token'] ) ); + elseif ( $jetpack_user = self::get_jetpack_user() ) + $akismet_user = self::verify_wpcom_key( $jetpack_user['api_key'], $jetpack_user['user_id'] ); + + if ( isset( $_GET['action'] ) ) { + if ( $_GET['action'] == 'save-key' ) { + if ( is_object( $akismet_user ) ) { + self::save_key( $akismet_user->api_key ); + self::display_notice(); + self::display_configuration_page(); + return; + } + } + } + + echo '

      '.esc_html__('Akismet', 'akismet').'

      '; + + self::display_status(); + + Akismet::view( 'start', compact( 'akismet_user' ) ); + } + + public static function display_stats_page() { + Akismet::view( 'stats' ); + } + + public static function display_configuration_page() { + $api_key = Akismet::get_api_key(); + $akismet_user = self::get_akismet_user( $api_key ); + $stat_totals = self::get_stats( $api_key ); + + // If unset, create the new strictness option using the old discard option to determine its default + if ( get_option( 'akismet_strictness' ) === false ) + add_option( 'akismet_strictness', (get_option('akismet_discard_month') === 'true' ? '1' : '0') ); + + if ( empty( self::$notices ) ) { + //show status + if ( ! empty( $stat_totals['all'] ) && isset( $stat_totals['all']->time_saved ) && $akismet_user->status == 'active' && $akismet_user->account_type == 'free-api-key' ) { + + $time_saved = false; + + if ( $stat_totals['all']->time_saved > 1800 ) { + $total_in_minutes = round( $stat_totals['all']->time_saved / 60 ); + $total_in_hours = round( $total_in_minutes / 60 ); + $total_in_days = round( $total_in_hours / 8 ); + $cleaning_up = __( 'Cleaning up spam takes time.' , 'akismet'); + + if ( $total_in_days > 1 ) + $time_saved = $cleaning_up . ' ' . sprintf( __( 'Since you joined us, Akismet has saved you %s days!' , 'akismet'), number_format_i18n( $total_in_days ) ); + elseif ( $total_in_hours > 1 ) + $time_saved = $cleaning_up . ' ' . sprintf( __( 'Since you joined us, Akismet has saved you %d hours!' , 'akismet'), $total_in_hours ); + elseif ( $total_in_minutes >= 30 ) + $time_saved = $cleaning_up . ' ' . sprintf( __( 'Since you joined us, Akismet has saved you %d minutes!' , 'akismet'), $total_in_minutes ); + } + + Akismet::view( 'notice', array( 'type' => 'active-notice', 'time_saved' => $time_saved ) ); + } + + if ( !empty( $akismet_user->limit_reached ) && in_array( $akismet_user->limit_reached, array( 'yellow', 'red' ) ) ) { + Akismet::view( 'notice', array( 'type' => 'limit-reached', 'level' => $akismet_user->limit_reached ) ); + } + } + + if ( !isset( self::$notices['status'] ) && in_array( $akismet_user->status, array( 'cancelled', 'suspended', 'missing', 'no-sub' ) ) ) + Akismet::view( 'notice', array( 'type' => $akismet_user->status ) ); + + Akismet::log( compact( 'stat_totals', 'akismet_user' ) ); + Akismet::view( 'config', compact( 'api_key', 'akismet_user', 'stat_totals' ) ); + } + + public static function display_notice() { + global $hook_suffix; + + if ( in_array( $hook_suffix, array( 'jetpack_page_akismet-key-config', 'settings_page_akismet-key-config', 'edit-comments.php' ) ) && (int) get_option( 'akismet_alert_code' ) > 0 ) { + Akismet::verify_key( Akismet::get_api_key() ); //verify that the key is still in alert state + + if ( get_option( 'akismet_alert_code' ) > 0 ) + self::display_alert(); + } + elseif ( $hook_suffix == 'plugins.php' && !Akismet::get_api_key() ) { + self::display_api_key_warning(); + } + elseif ( $hook_suffix == 'edit-comments.php' && wp_next_scheduled( 'akismet_schedule_cron_recheck' ) ) { + self::display_spam_check_warning(); + } + elseif ( in_array( $hook_suffix, array( 'jetpack_page_akismet-key-config', 'settings_page_akismet-key-config' ) ) && Akismet::get_api_key() ) { + self::display_status(); + } + } + + public static function display_status() { + $type = ''; + + if ( !self::get_server_connectivity() ) + $type = 'servers-be-down'; + + if ( !empty( $type ) ) + Akismet::view( 'notice', compact( 'type' ) ); + elseif ( !empty( self::$notices ) ) { + foreach ( self::$notices as $type ) + Akismet::view( 'notice', compact( 'type' ) ); + } + } + + private static function get_jetpack_user() { + if ( !class_exists('Jetpack') ) + return false; + + Jetpack::load_xml_rpc_client(); + $xml = new Jetpack_IXR_ClientMulticall( array( 'user_id' => get_current_user_id() ) ); + + $xml->addCall( 'wpcom.getUserID' ); + $xml->addCall( 'akismet.getAPIKey' ); + $xml->query(); + + Akismet::log( compact( 'xml' ) ); + + if ( !$xml->isError() ) { + $responses = $xml->getResponse(); + if ( count( $responses ) > 1 ) { + $api_key = array_shift( $responses[0] ); + $user_id = (int) array_shift( $responses[1] ); + return compact( 'api_key', 'user_id' ); + } + } + return false; + } + + /** + * Some commentmeta isn't useful in an export file. Suppress it (when supported). + * + * @param bool $exclude + * @param string $key The meta key + * @param object $meta The meta object + * @return bool Whether to exclude this meta entry from the export. + */ + public static function exclude_commentmeta_from_export( $exclude, $key, $meta ) { + if ( in_array( $key, array( 'akismet_as_submitted', 'akismet_rechecking', 'akismet_delayed_moderation_email' ) ) ) { + return true; + } + + return $exclude; + } +} \ No newline at end of file diff --git a/wp-content/plugins/akismet/class.akismet-widget.php b/wp-content/plugins/akismet/class.akismet-widget.php new file mode 100644 index 0000000..a60ae60 --- /dev/null +++ b/wp-content/plugins/akismet/class.akismet-widget.php @@ -0,0 +1,110 @@ + __( 'Display the number of spam comments Akismet has caught' , 'akismet') ) + ); + + if ( is_active_widget( false, false, $this->id_base ) ) { + add_action( 'wp_head', array( $this, 'css' ) ); + } + } + + function css() { +?> + + + + + +

      + + +

      + + + + + + $key, 'blog' => get_option('home') ) ), 'verify-key', $ip ); + } + + public static function verify_key( $key, $ip = null ) { + $response = self::check_key_status( $key, $ip ); + + if ( $response[1] != 'valid' && $response[1] != 'invalid' ) + return 'failed'; + + self::update_alert( $response ); + + return $response[1]; + } + + public static function auto_check_comment( $commentdata ) { + self::$last_comment_result = null; + + $comment = $commentdata; + + $comment['user_ip'] = self::get_ip_address(); + $comment['user_agent'] = self::get_user_agent(); + $comment['referrer'] = self::get_referer(); + $comment['blog'] = get_option('home'); + $comment['blog_lang'] = get_locale(); + $comment['blog_charset'] = get_option('blog_charset'); + $comment['permalink'] = get_permalink( $comment['comment_post_ID'] ); + + if ( !empty( $comment['user_ID'] ) ) + $comment['user_role'] = Akismet::get_user_roles( $comment['user_ID'] ); + + $akismet_nonce_option = apply_filters( 'akismet_comment_nonce', get_option( 'akismet_comment_nonce' ) ); + $comment['akismet_comment_nonce'] = 'inactive'; + if ( $akismet_nonce_option == 'true' || $akismet_nonce_option == '' ) { + $comment['akismet_comment_nonce'] = 'failed'; + if ( isset( $_POST['akismet_comment_nonce'] ) && wp_verify_nonce( $_POST['akismet_comment_nonce'], 'akismet_comment_nonce_' . $comment['comment_post_ID'] ) ) + $comment['akismet_comment_nonce'] = 'passed'; + + // comment reply in wp-admin + if ( isset( $_POST['_ajax_nonce-replyto-comment'] ) && check_ajax_referer( 'replyto-comment', '_ajax_nonce-replyto-comment' ) ) + $comment['akismet_comment_nonce'] = 'passed'; + + } + + if ( self::is_test_mode() ) + $comment['is_test'] = 'true'; + + foreach( $_POST as $key => $value ) { + if ( is_string( $value ) ) + $comment["POST_{$key}"] = $value; + } + + $ignore = array( 'HTTP_COOKIE', 'HTTP_COOKIE2', 'PHP_AUTH_PW' ); + + foreach ( $_SERVER as $key => $value ) { + if ( !in_array( $key, $ignore ) && is_string($value) ) + $comment["$key"] = $value; + else + $comment["$key"] = ''; + } + + $post = get_post( $comment['comment_post_ID'] ); + $comment[ 'comment_post_modified_gmt' ] = $post->post_modified_gmt; + + $response = self::http_post( Akismet::build_query( $comment ), 'comment-check' ); + + do_action( 'akismet_comment_check_response', $response ); + + self::update_alert( $response ); + + $commentdata['comment_as_submitted'] = array_intersect_key( $comment, array( 'blog' => '', 'blog_charset' => '', 'blog_lang' => '', 'blog_ua' => '', 'comment_agent' => '', 'comment_author' => '', 'comment_author_IP' => '', 'comment_author_email' => '', 'comment_author_url' => '', 'comment_content' => '', 'comment_date_gmt' => '', 'comment_tags' => '', 'comment_type' => '', 'guid' => '', 'is_test' => '', 'permalink' => '', 'reporter' => '', 'site_domain' => '', 'submit_referer' => '', 'submit_uri' => '', 'user_ID' => '', 'user_agent' => '', 'user_id' => '', 'user_ip' => '' ) ); + $commentdata['akismet_result'] = $response[1]; + + if ( isset( $response[0]['x-akismet-pro-tip'] ) ) + $commentdata['akismet_pro_tip'] = $response[0]['x-akismet-pro-tip']; + + if ( isset( $response[0]['x-akismet-error'] ) ) { + // An error occurred that we anticipated (like a suspended key) and want the user to act on. + // Send to moderation. + self::$last_comment_result = '0'; + } + else if ( 'true' == $response[1] ) { + // akismet_spam_count will be incremented later by comment_is_spam() + self::$last_comment_result = 'spam'; + + $discard = ( isset( $commentdata['akismet_pro_tip'] ) && $commentdata['akismet_pro_tip'] === 'discard' && self::allow_discard() ); + + do_action( 'akismet_spam_caught', $discard ); + + if ( $discard ) { + // akismet_result_spam() won't be called so bump the counter here + if ( $incr = apply_filters('akismet_spam_count_incr', 1) ) + update_option( 'akismet_spam_count', get_option('akismet_spam_count') + $incr ); + $redirect_to = isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : get_permalink( $post ); + wp_safe_redirect( esc_url_raw( $redirect_to ) ); + die(); + } + } + + // if the response is neither true nor false, hold the comment for moderation and schedule a recheck + if ( 'true' != $response[1] && 'false' != $response[1] ) { + if ( !current_user_can('moderate_comments') ) { + // Comment status should be moderated + self::$last_comment_result = '0'; + } + if ( function_exists('wp_next_scheduled') && function_exists('wp_schedule_single_event') ) { + if ( !wp_next_scheduled( 'akismet_schedule_cron_recheck' ) ) { + wp_schedule_single_event( time() + 1200, 'akismet_schedule_cron_recheck' ); + } + } + + self::$prevent_moderation_email_for_these_comments[] = $commentdata; + } + + if ( function_exists('wp_next_scheduled') && function_exists('wp_schedule_event') ) { + // WP 2.1+: delete old comments daily + if ( !wp_next_scheduled( 'akismet_scheduled_delete' ) ) + wp_schedule_event( time(), 'daily', 'akismet_scheduled_delete' ); + } + elseif ( (mt_rand(1, 10) == 3) ) { + // WP 2.0: run this one time in ten + self::delete_old_comments(); + } + + self::set_last_comment( $commentdata ); + self::fix_scheduled_recheck(); + + return self::$last_comment; + } + + public static function get_last_comment() { + return self::$last_comment; + } + + public static function set_last_comment( $comment ) { + if ( is_null( $comment ) ) { + self::$last_comment = null; + } + else { + // We filter it here so that it matches the filtered comment data that we'll have to compare against later. + // wp_filter_comment expects comment_author_IP + self::$last_comment = wp_filter_comment( + array_merge( + array( 'comment_author_IP' => self::get_ip_address() ), + $comment + ) + ); + } + } + + // this fires on wp_insert_comment. we can't update comment_meta when auto_check_comment() runs + // because we don't know the comment ID at that point. + public static function auto_check_update_meta( $id, $comment ) { + + // failsafe for old WP versions + if ( !function_exists('add_comment_meta') ) + return false; + + if ( !isset( self::$last_comment['comment_author_email'] ) ) + self::$last_comment['comment_author_email'] = ''; + + // wp_insert_comment() might be called in other contexts, so make sure this is the same comment + // as was checked by auto_check_comment + if ( is_object( $comment ) && !empty( self::$last_comment ) && is_array( self::$last_comment ) ) { + if ( self::matches_last_comment( $comment ) ) { + + load_plugin_textdomain( 'akismet' ); + + // normal result: true or false + if ( self::$last_comment['akismet_result'] == 'true' ) { + update_comment_meta( $comment->comment_ID, 'akismet_result', 'true' ); + self::update_comment_history( $comment->comment_ID, __('Akismet caught this comment as spam', 'akismet'), 'check-spam' ); + if ( $comment->comment_approved != 'spam' ) + self::update_comment_history( $comment->comment_ID, sprintf( __('Comment status was changed to %s', 'akismet'), $comment->comment_approved), 'status-changed'.$comment->comment_approved ); + } + elseif ( self::$last_comment['akismet_result'] == 'false' ) { + update_comment_meta( $comment->comment_ID, 'akismet_result', 'false' ); + self::update_comment_history( $comment->comment_ID, __('Akismet cleared this comment', 'akismet'), 'check-ham' ); + if ( $comment->comment_approved == 'spam' ) { + if ( wp_blacklist_check($comment->comment_author, $comment->comment_author_email, $comment->comment_author_url, $comment->comment_content, $comment->comment_author_IP, $comment->comment_agent) ) + self::update_comment_history( $comment->comment_ID, __('Comment was caught by wp_blacklist_check', 'akismet'), 'wp-blacklisted' ); + else + self::update_comment_history( $comment->comment_ID, sprintf( __('Comment status was changed to %s', 'akismet'), $comment->comment_approved), 'status-changed-'.$comment->comment_approved ); + } + } // abnormal result: error + else { + update_comment_meta( $comment->comment_ID, 'akismet_error', time() ); + self::update_comment_history( $comment->comment_ID, sprintf( __('Akismet was unable to check this comment (response: %s), will automatically retry again later.', 'akismet'), substr(self::$last_comment['akismet_result'], 0, 50)), 'check-error' ); + } + + // record the complete original data as submitted for checking + if ( isset( self::$last_comment['comment_as_submitted'] ) ) + update_comment_meta( $comment->comment_ID, 'akismet_as_submitted', self::$last_comment['comment_as_submitted'] ); + + if ( isset( self::$last_comment['akismet_pro_tip'] ) ) + update_comment_meta( $comment->comment_ID, 'akismet_pro_tip', self::$last_comment['akismet_pro_tip'] ); + } + } + } + + public static function delete_old_comments() { + global $wpdb; + + /** + * Determines how many comments will be deleted in each batch. + * + * @param int The default, as defined by AKISMET_DELETE_LIMIT. + */ + $delete_limit = apply_filters( 'akismet_delete_comment_limit', defined( 'AKISMET_DELETE_LIMIT' ) ? AKISMET_DELETE_LIMIT : 10000 ); + $delete_limit = max( 1, intval( $delete_limit ) ); + + /** + * Determines how many days a comment will be left in the Spam queue before being deleted. + * + * @param int The default number of days. + */ + $delete_interval = apply_filters( 'akismet_delete_comment_interval', 15 ); + $delete_interval = max( 1, intval( $delete_interval ) ); + + while ( $comment_ids = $wpdb->get_col( $wpdb->prepare( "SELECT comment_id FROM {$wpdb->comments} WHERE DATE_SUB(NOW(), INTERVAL %d DAY) > comment_date_gmt AND comment_approved = 'spam' LIMIT %d", $delete_interval, $delete_limit ) ) ) { + if ( empty( $comment_ids ) ) + return; + + $wpdb->queries = array(); + + foreach ( $comment_ids as $comment_id ) { + do_action( 'delete_comment', $comment_id ); + } + + $comma_comment_ids = implode( ', ', array_map('intval', $comment_ids) ); + + $wpdb->query("DELETE FROM {$wpdb->comments} WHERE comment_id IN ( $comma_comment_ids )"); + $wpdb->query("DELETE FROM {$wpdb->commentmeta} WHERE comment_id IN ( $comma_comment_ids )"); + + clean_comment_cache( $comment_ids ); + } + + if ( apply_filters( 'akismet_optimize_table', ( mt_rand(1, 5000) == 11), $wpdb->comments ) ) // lucky number + $wpdb->query("OPTIMIZE TABLE {$wpdb->comments}"); + } + + public static function delete_old_comments_meta() { + global $wpdb; + + $interval = apply_filters( 'akismet_delete_commentmeta_interval', 15 ); + + # enfore a minimum of 1 day + $interval = absint( $interval ); + if ( $interval < 1 ) + $interval = 1; + + // akismet_as_submitted meta values are large, so expire them + // after $interval days regardless of the comment status + while ( $comment_ids = $wpdb->get_col( $wpdb->prepare( "SELECT m.comment_id FROM {$wpdb->commentmeta} as m INNER JOIN {$wpdb->comments} as c USING(comment_id) WHERE m.meta_key = 'akismet_as_submitted' AND DATE_SUB(NOW(), INTERVAL %d DAY) > c.comment_date_gmt LIMIT 10000", $interval ) ) ) { + if ( empty( $comment_ids ) ) + return; + + $wpdb->queries = array(); + + foreach ( $comment_ids as $comment_id ) { + delete_comment_meta( $comment_id, 'akismet_as_submitted' ); + } + } + + if ( apply_filters( 'akismet_optimize_table', ( mt_rand(1, 5000) == 11), $wpdb->commentmeta ) ) // lucky number + $wpdb->query("OPTIMIZE TABLE {$wpdb->commentmeta}"); + } + + // how many approved comments does this author have? + public static function get_user_comments_approved( $user_id, $comment_author_email, $comment_author, $comment_author_url ) { + global $wpdb; + + if ( !empty( $user_id ) ) + return (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->comments} WHERE user_id = %d AND comment_approved = 1", $user_id ) ); + + if ( !empty( $comment_author_email ) ) + return (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->comments} WHERE comment_author_email = %s AND comment_author = %s AND comment_author_url = %s AND comment_approved = 1", $comment_author_email, $comment_author, $comment_author_url ) ); + + return 0; + } + + // get the full comment history for a given comment, as an array in reverse chronological order + public static function get_comment_history( $comment_id ) { + + // failsafe for old WP versions + if ( !function_exists('add_comment_meta') ) + return false; + + $history = get_comment_meta( $comment_id, 'akismet_history', false ); + usort( $history, array( 'Akismet', '_cmp_time' ) ); + return $history; + } + + // log an event for a given comment, storing it in comment_meta + public static function update_comment_history( $comment_id, $message, $event=null ) { + global $current_user; + + // failsafe for old WP versions + if ( !function_exists('add_comment_meta') ) + return false; + + $user = ''; + if ( is_object( $current_user ) && isset( $current_user->user_login ) ) + $user = $current_user->user_login; + + $event = array( + 'time' => self::_get_microtime(), + 'message' => $message, + 'event' => $event, + 'user' => $user, + ); + + // $unique = false so as to allow multiple values per comment + $r = add_comment_meta( $comment_id, 'akismet_history', $event, false ); + } + + public static function check_db_comment( $id, $recheck_reason = 'recheck_queue' ) { + global $wpdb; + + $c = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->comments} WHERE comment_ID = %d", $id ), ARRAY_A ); + if ( !$c ) + return; + + $c['user_ip'] = $c['comment_author_IP']; + $c['user_agent'] = $c['comment_agent']; + $c['referrer'] = ''; + $c['blog'] = get_option('home'); + $c['blog_lang'] = get_locale(); + $c['blog_charset'] = get_option('blog_charset'); + $c['permalink'] = get_permalink($c['comment_post_ID']); + $c['recheck_reason'] = $recheck_reason; + + if ( self::is_test_mode() ) + $c['is_test'] = 'true'; + + $response = self::http_post( Akismet::build_query( $c ), 'comment-check' ); + + return ( is_array( $response ) && ! empty( $response[1] ) ) ? $response[1] : false; + } + + + + public static function transition_comment_status( $new_status, $old_status, $comment ) { + + if ( $new_status == $old_status ) + return; + + # we don't need to record a history item for deleted comments + if ( $new_status == 'delete' ) + return; + + if ( !current_user_can( 'edit_post', $comment->comment_post_ID ) && !current_user_can( 'moderate_comments' ) ) + return; + + if ( defined('WP_IMPORTING') && WP_IMPORTING == true ) + return; + + // if this is present, it means the status has been changed by a re-check, not an explicit user action + if ( get_comment_meta( $comment->comment_ID, 'akismet_rechecking' ) ) + return; + + global $current_user; + $reporter = ''; + if ( is_object( $current_user ) ) + $reporter = $current_user->user_login; + + // Assumption alert: + // We want to submit comments to Akismet only when a moderator explicitly spams or approves it - not if the status + // is changed automatically by another plugin. Unfortunately WordPress doesn't provide an unambiguous way to + // determine why the transition_comment_status action was triggered. And there are several different ways by which + // to spam and unspam comments: bulk actions, ajax, links in moderation emails, the dashboard, and perhaps others. + // We'll assume that this is an explicit user action if certain POST/GET variables exist. + if ( ( isset( $_POST['status'] ) && in_array( $_POST['status'], array( 'spam', 'unspam' ) ) ) || + ( isset( $_POST['spam'] ) && (int) $_POST['spam'] == 1 ) || + ( isset( $_POST['unspam'] ) && (int) $_POST['unspam'] == 1 ) || + ( isset( $_POST['comment_status'] ) && in_array( $_POST['comment_status'], array( 'spam', 'unspam' ) ) ) || + ( isset( $_GET['action'] ) && in_array( $_GET['action'], array( 'spam', 'unspam' ) ) ) || + ( isset( $_POST['action'] ) && in_array( $_POST['action'], array( 'editedcomment' ) ) ) + ) { + if ( $new_status == 'spam' && ( $old_status == 'approved' || $old_status == 'unapproved' || !$old_status ) ) { + return self::submit_spam_comment( $comment->comment_ID ); + } elseif ( $old_status == 'spam' && ( $new_status == 'approved' || $new_status == 'unapproved' ) ) { + return self::submit_nonspam_comment( $comment->comment_ID ); + } + } + + self::update_comment_history( $comment->comment_ID, sprintf( __('%1$s changed the comment status to %2$s', 'akismet'), $reporter, $new_status ), 'status-' . $new_status ); + } + + public static function submit_spam_comment( $comment_id ) { + global $wpdb, $current_user, $current_site; + + $comment_id = (int) $comment_id; + + $comment = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->comments} WHERE comment_ID = %d", $comment_id ) ); + + if ( !$comment ) // it was deleted + return; + + if ( 'spam' != $comment->comment_approved ) + return; + + // use the original version stored in comment_meta if available + $as_submitted = get_comment_meta( $comment_id, 'akismet_as_submitted', true); + + if ( $as_submitted && is_array( $as_submitted ) && isset( $as_submitted['comment_content'] ) ) + $comment = (object) array_merge( (array)$comment, $as_submitted ); + + $comment->blog = get_bloginfo('url'); + $comment->blog_lang = get_locale(); + $comment->blog_charset = get_option('blog_charset'); + $comment->permalink = get_permalink($comment->comment_post_ID); + + if ( is_object($current_user) ) + $comment->reporter = $current_user->user_login; + + if ( is_object($current_site) ) + $comment->site_domain = $current_site->domain; + + $comment->user_role = ''; + if ( isset( $comment->user_ID ) ) + $comment->user_role = Akismet::get_user_roles( $comment->user_ID ); + + if ( self::is_test_mode() ) + $comment->is_test = 'true'; + + $post = get_post( $comment->comment_post_ID ); + $comment->comment_post_modified_gmt = $post->post_modified_gmt; + + $response = Akismet::http_post( Akismet::build_query( $comment ), 'submit-spam' ); + if ( $comment->reporter ) { + self::update_comment_history( $comment_id, sprintf( __('%s reported this comment as spam', 'akismet'), $comment->reporter ), 'report-spam' ); + update_comment_meta( $comment_id, 'akismet_user_result', 'true' ); + update_comment_meta( $comment_id, 'akismet_user', $comment->reporter ); + } + + do_action('akismet_submit_spam_comment', $comment_id, $response[1]); + } + + public static function submit_nonspam_comment( $comment_id ) { + global $wpdb, $current_user, $current_site; + + $comment_id = (int) $comment_id; + + $comment = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->comments} WHERE comment_ID = %d", $comment_id ) ); + if ( !$comment ) // it was deleted + return; + + // use the original version stored in comment_meta if available + $as_submitted = get_comment_meta( $comment_id, 'akismet_as_submitted', true); + + if ( $as_submitted && is_array($as_submitted) && isset($as_submitted['comment_content']) ) + $comment = (object) array_merge( (array)$comment, $as_submitted ); + + $comment->blog = get_bloginfo('url'); + $comment->blog_lang = get_locale(); + $comment->blog_charset = get_option('blog_charset'); + $comment->permalink = get_permalink( $comment->comment_post_ID ); + $comment->user_role = ''; + + if ( is_object($current_user) ) + $comment->reporter = $current_user->user_login; + + if ( is_object($current_site) ) + $comment->site_domain = $current_site->domain; + + if ( isset( $comment->user_ID ) ) + $comment->user_role = Akismet::get_user_roles($comment->user_ID); + + if ( Akismet::is_test_mode() ) + $comment->is_test = 'true'; + + $post = get_post( $comment->comment_post_ID ); + $comment->comment_post_modified_gmt = $post->post_modified_gmt; + + $response = self::http_post( Akismet::build_query( $comment ), 'submit-ham' ); + if ( $comment->reporter ) { + self::update_comment_history( $comment_id, sprintf( __('%s reported this comment as not spam', 'akismet'), $comment->reporter ), 'report-ham' ); + update_comment_meta( $comment_id, 'akismet_user_result', 'false' ); + update_comment_meta( $comment_id, 'akismet_user', $comment->reporter ); + } + + do_action('akismet_submit_nonspam_comment', $comment_id, $response[1]); + } + + public static function cron_recheck() { + global $wpdb; + + $api_key = self::get_api_key(); + + $status = self::verify_key( $api_key ); + if ( get_option( 'akismet_alert_code' ) || $status == 'invalid' ) { + // since there is currently a problem with the key, reschedule a check for 6 hours hence + wp_schedule_single_event( time() + 21600, 'akismet_schedule_cron_recheck' ); + return false; + } + + delete_option('akismet_available_servers'); + + $comment_errors = $wpdb->get_col( "SELECT comment_id FROM {$wpdb->commentmeta} WHERE meta_key = 'akismet_error' LIMIT 100" ); + + load_plugin_textdomain( 'akismet' ); + + foreach ( (array) $comment_errors as $comment_id ) { + // if the comment no longer exists, or is too old, remove the meta entry from the queue to avoid getting stuck + $comment = get_comment( $comment_id ); + if ( !$comment || strtotime( $comment->comment_date_gmt ) < strtotime( "-15 days" ) ) { + delete_comment_meta( $comment_id, 'akismet_error' ); + delete_comment_meta( $comment_id, 'akismet_delayed_moderation_email' ); + continue; + } + + add_comment_meta( $comment_id, 'akismet_rechecking', true ); + $status = self::check_db_comment( $comment_id, 'retry' ); + + $msg = ''; + if ( $status == 'true' ) { + $msg = __( 'Akismet caught this comment as spam during an automatic retry.' , 'akismet'); + } elseif ( $status == 'false' ) { + $msg = __( 'Akismet cleared this comment during an automatic retry.' , 'akismet'); + } + + // If we got back a legit response then update the comment history + // other wise just bail now and try again later. No point in + // re-trying all the comments once we hit one failure. + if ( !empty( $msg ) ) { + delete_comment_meta( $comment_id, 'akismet_error' ); + self::update_comment_history( $comment_id, $msg, 'cron-retry' ); + update_comment_meta( $comment_id, 'akismet_result', $status ); + // make sure the comment status is still pending. if it isn't, that means the user has already moved it elsewhere. + $comment = get_comment( $comment_id ); + if ( $comment && 'unapproved' == wp_get_comment_status( $comment_id ) ) { + if ( $status == 'true' ) { + wp_spam_comment( $comment_id ); + } elseif ( $status == 'false' ) { + // comment is good, but it's still in the pending queue. depending on the moderation settings + // we may need to change it to approved. + if ( check_comment($comment->comment_author, $comment->comment_author_email, $comment->comment_author_url, $comment->comment_content, $comment->comment_author_IP, $comment->comment_agent, $comment->comment_type) ) + wp_set_comment_status( $comment_id, 1 ); + else if ( get_comment_meta( $comment_id, 'akismet_delayed_moderation_email', true ) ) + wp_notify_moderator( $comment_id ); + } + } + + delete_comment_meta( $comment_id, 'akismet_delayed_moderation_email' ); + } else { + // If this comment has been pending moderation for longer than MAX_DELAY_BEFORE_MODERATION_EMAIL, + // send a moderation email now. + if ( ( intval( gmdate( 'U' ) ) - strtotime( $comment->comment_date_gmt ) ) < self::MAX_DELAY_BEFORE_MODERATION_EMAIL ) { + delete_comment_meta( $comment_id, 'akismet_delayed_moderation_email' ); + wp_notify_moderator( $comment_id ); + } + + delete_comment_meta( $comment_id, 'akismet_rechecking' ); + wp_schedule_single_event( time() + 1200, 'akismet_schedule_cron_recheck' ); + return; + } + delete_comment_meta( $comment_id, 'akismet_rechecking' ); + } + + $remaining = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->commentmeta} WHERE meta_key = 'akismet_error'" ); + if ( $remaining && !wp_next_scheduled('akismet_schedule_cron_recheck') ) { + wp_schedule_single_event( time() + 1200, 'akismet_schedule_cron_recheck' ); + } + } + + public static function fix_scheduled_recheck() { + $future_check = wp_next_scheduled( 'akismet_schedule_cron_recheck' ); + if ( !$future_check ) { + return; + } + + if ( get_option( 'akismet_alert_code' ) > 0 ) { + return; + } + + $check_range = time() + 1200; + if ( $future_check > $check_range ) { + wp_clear_scheduled_hook( 'akismet_schedule_cron_recheck' ); + wp_schedule_single_event( time() + 300, 'akismet_schedule_cron_recheck' ); + } + } + + public static function add_comment_nonce( $post_id ) { + echo '

      '; + wp_nonce_field( 'akismet_comment_nonce_' . $post_id, 'akismet_comment_nonce', FALSE ); + echo '

      '; + } + + public static function is_test_mode() { + return defined('AKISMET_TEST_MODE') && AKISMET_TEST_MODE; + } + + public static function allow_discard() { + if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) + return false; + if ( is_user_logged_in() ) + return false; + + return ( get_option( 'akismet_strictness' ) === '1' ); + } + + public static function get_ip_address() { + return isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : null; + } + + /** + * Do these two comments, without checking the comment_ID, "match"? + * + * @param mixed $comment1 A comment object or array. + * @param mixed $comment2 A comment object or array. + * @return bool Whether the two comments should be treated as the same comment. + */ + private static function comments_match( $comment1, $comment2 ) { + $comment1 = (array) $comment1; + $comment2 = (array) $comment2; + + return ( + isset( $comment1['comment_post_ID'], $comment2['comment_post_ID'] ) + && intval( $comment1['comment_post_ID'] ) == intval( $comment2['comment_post_ID'] ) + && $comment1['comment_author'] == $comment2['comment_author'] + && $comment1['comment_author_email'] == $comment2['comment_author_email'] + ); + } + + // Does the supplied comment match the details of the one most recently stored in self::$last_comment? + public static function matches_last_comment( $comment ) { + if ( is_object( $comment ) ) + $comment = (array) $comment; + + return self::comments_match( self::$last_comment, $comment ); + } + + private static function get_user_agent() { + return isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : null; + } + + private static function get_referer() { + return isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : null; + } + + // return a comma-separated list of role names for the given user + public static function get_user_roles( $user_id ) { + $roles = false; + + if ( !class_exists('WP_User') ) + return false; + + if ( $user_id > 0 ) { + $comment_user = new WP_User( $user_id ); + if ( isset( $comment_user->roles ) ) + $roles = join( ',', $comment_user->roles ); + } + + if ( is_multisite() && is_super_admin( $user_id ) ) { + if ( empty( $roles ) ) { + $roles = 'super_admin'; + } else { + $comment_user->roles[] = 'super_admin'; + $roles = join( ',', $comment_user->roles ); + } + } + + return $roles; + } + + // filter handler used to return a spam result to pre_comment_approved + public static function last_comment_status( $approved, $comment ) { + // Only do this if it's the correct comment + if ( is_null(self::$last_comment_result) || ! self::matches_last_comment( $comment ) ) { + self::log( "comment_is_spam mismatched comment, returning unaltered $approved" ); + return $approved; + } + + // bump the counter here instead of when the filter is added to reduce the possibility of overcounting + if ( $incr = apply_filters('akismet_spam_count_incr', 1) ) + update_option( 'akismet_spam_count', get_option('akismet_spam_count') + $incr ); + + return self::$last_comment_result; + } + + /** + * If Akismet is temporarily unreachable, we don't want to "spam" the blogger with + * moderation emails for comments that will be automatically cleared or spammed on + * the next retry. + * + * For comments that will be rechecked later, empty the list of email addresses that + * the moderation email would be sent to. + * + * @param array $emails An array of email addresses that the moderation email will be sent to. + * @param int $comment_id The ID of the relevant comment. + * @return array An array of email addresses that the moderation email will be sent to. + */ + public static function disable_moderation_emails_if_unreachable( $emails, $comment_id ) { + if ( ! empty( self::$prevent_moderation_email_for_these_comments ) && ! empty( $emails ) ) { + $comment = get_comment( $comment_id ); + + foreach ( self::$prevent_moderation_email_for_these_comments as $possible_match ) { + if ( self::comments_match( $possible_match, $comment ) ) { + update_comment_meta( $comment_id, 'akismet_delayed_moderation_email', true ); + return array(); + } + } + } + + return $emails; + } + + public static function _cmp_time( $a, $b ) { + return $a['time'] > $b['time'] ? -1 : 1; + } + + public static function _get_microtime() { + $mtime = explode( ' ', microtime() ); + return $mtime[1] + $mtime[0]; + } + + /** + * Make a POST request to the Akismet API. + * + * @param string $request The body of the request. + * @param string $path The path for the request. + * @param string $ip The specific IP address to hit. + * @return array A two-member array consisting of the headers and the response body, both empty in the case of a failure. + */ + public static function http_post( $request, $path, $ip=null ) { + + $akismet_ua = sprintf( 'WordPress/%s | Akismet/%s', $GLOBALS['wp_version'], constant( 'AKISMET_VERSION' ) ); + $akismet_ua = apply_filters( 'akismet_ua', $akismet_ua ); + + $content_length = strlen( $request ); + + $api_key = self::get_api_key(); + $host = self::API_HOST; + + if ( !empty( $api_key ) ) + $host = $api_key.'.'.$host; + + $http_host = $host; + // use a specific IP if provided + // needed by Akismet_Admin::check_server_connectivity() + if ( $ip && long2ip( ip2long( $ip ) ) ) { + $http_host = $ip; + } + + $http_args = array( + 'body' => $request, + 'headers' => array( + 'Content-Type' => 'application/x-www-form-urlencoded; charset=' . get_option( 'blog_charset' ), + 'Host' => $host, + 'User-Agent' => $akismet_ua, + ), + 'httpversion' => '1.0', + 'timeout' => 15 + ); + + $akismet_url = "http://{$http_host}/1.1/{$path}"; + $response = wp_remote_post( $akismet_url, $http_args ); + Akismet::log( compact( 'akismet_url', 'http_args', 'response' ) ); + if ( is_wp_error( $response ) ) + return array( '', '' ); + + return array( $response['headers'], $response['body'] ); + } + + // given a response from an API call like check_key_status(), update the alert code options if an alert is present. + private static function update_alert( $response ) { + $code = $msg = null; + if ( isset( $response[0]['x-akismet-alert-code'] ) ) { + $code = $response[0]['x-akismet-alert-code']; + $msg = $response[0]['x-akismet-alert-msg']; + } + + // only call update_option() if the value has changed + if ( $code != get_option( 'akismet_alert_code' ) ) { + if ( ! $code ) { + delete_option( 'akismet_alert_code' ); + delete_option( 'akismet_alert_msg' ); + } + else { + update_option( 'akismet_alert_code', $code ); + update_option( 'akismet_alert_msg', $msg ); + } + } + } + + public static function load_form_js() { + // WP < 3.3 can't enqueue a script this late in the game and still have it appear in the footer. + // Once we drop support for everything pre-3.3, this can change back to a single enqueue call. + wp_register_script( 'akismet-form', AKISMET__PLUGIN_URL . '_inc/form.js', array(), AKISMET_VERSION, true ); + add_action( 'wp_footer', array( 'Akismet', 'print_form_js' ) ); + add_action( 'admin_footer', array( 'Akismet', 'print_form_js' ) ); + } + + public static function print_form_js() { + wp_print_scripts( 'akismet-form' ); + } + + public static function inject_ak_js( $fields ) { + echo '

      '; + echo ''; + echo '

      '; + } + + private static function bail_on_activation( $message, $deactivate = true ) { +?> + + + + + + +

      + + + $plugin ) { + if ( $plugin === $akismet ) { + $plugins[$i] = false; + $update = true; + } + } + + if ( $update ) { + update_option( 'active_plugins', array_filter( $plugins ) ); + } + } + exit; + } + + public static function view( $name, array $args = array() ) { + $args = apply_filters( 'akismet_view_arguments', $args, $name ); + + foreach ( $args AS $key => $val ) { + $$key = $val; + } + + load_plugin_textdomain( 'akismet' ); + + $file = AKISMET__PLUGIN_DIR . 'views/'. $name . '.php'; + + include( $file ); + } + + /** + * Attached to activate_{ plugin_basename( __FILES__ ) } by register_activation_hook() + * @static + */ + public static function plugin_activation() { + if ( version_compare( $GLOBALS['wp_version'], AKISMET__MINIMUM_WP_VERSION, '<' ) ) { + load_plugin_textdomain( 'akismet' ); + + $message = ''.sprintf(esc_html__( 'Akismet %s requires WordPress %s or higher.' , 'akismet'), AKISMET_VERSION, AKISMET__MINIMUM_WP_VERSION ).' '.sprintf(__('Please upgrade WordPress to a current version, or downgrade to version 2.4 of the Akismet plugin.', 'akismet'), 'https://codex.wordpress.org/Upgrading_WordPress', 'http://wordpress.org/extend/plugins/akismet/download/'); + + Akismet::bail_on_activation( $message ); + } + } + + /** + * Removes all connection options + * @static + */ + public static function plugin_deactivation( ) { + //tidy up + } + + /** + * Essentially a copy of WP's build_query but one that doesn't expect pre-urlencoded values. + * + * @param array $args An array of key => value pairs + * @return string A string ready for use as a URL query string. + */ + public static function build_query( $args ) { + return _http_build_query( $args, '', '&' ); + } + + /** + * Log debugging info to the error log. + * + * Enabled when WP_DEBUG_LOG is enabled, but can be disabled via the akismet_debug_log filter. + * + * @param mixed $akismet_debug The data to log. + */ + public static function log( $akismet_debug ) { + if ( apply_filters( 'akismet_debug_log', defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) ) { + error_log( print_r( compact( 'akismet_debug' ), true ) ); + } + } +} \ No newline at end of file diff --git a/wp-content/plugins/akismet/index.php b/wp-content/plugins/akismet/index.php new file mode 100644 index 0000000..cf879a5 --- /dev/null +++ b/wp-content/plugins/akismet/index.php @@ -0,0 +1,2 @@ + + +

      + +
      + + spam > 0 ) : ?> + +
      + + + + + + +
        +
      • +

        + spam );?> + +
      • +
      • +

        + spam );?> + +
      • +
      • +

        + accuracy; ?>% + false_positives , 'akismet') + ), + number_format( $stat_totals['all']->missed_spam ), + number_format( $stat_totals['all']->false_positives ) + ); ?> +
      • +
      +
      +
      + + + + +
      + +
      +
      +
      +

      +

      +
      +
      + + + + + + + + + + + + + + + + + + + +
      + + +
      +

      + +

      +
      +
      +

      +

      +
      + + spam folder older than 1 day is deleted automatically.', + 'Spam in the spam folder older than %2$d days is deleted automatically.', + $delete_interval, + 'akismet' + ), + admin_url( 'edit-comments.php?comment_status=spam' ), + $delete_interval + ); + + ?> +
      +
      +
      + +
      + +
      + + +
      + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +

      +

      +
      + + + + + + + + + + + next_billing_date ) : ?> + + + + + + +
      + + account_name; ?> +
      + + status ) : + esc_html_e( 'Cancelled', 'akismet' ); + elseif ( 'suspended' == $akismet_user->status ) : + esc_html_e( 'Suspended', 'akismet' ); + elseif ( 'missing' == $akismet_user->status ) : + esc_html_e( 'Missing', 'akismet' ); + elseif ( 'no-sub' == $akismet_user->status ) : + esc_html_e( 'No Subscription Found', 'akismet' ); + else : + esc_html_e( 'Active', 'akismet' ); + endif; ?> +
      + + next_billing_date ); ?> +
      +
      +
      +
      + ( $akismet_user->account_type == 'free-api-key' && $akismet_user->status == 'active' ? __( 'Upgrade' , 'akismet') : __( 'Change' , 'akismet') ), 'redirect' => 'upgrade' ) ); ?> +
      +
      +
      +
      +
      +
      +
      + + + +
      +
      \ No newline at end of file diff --git a/wp-content/plugins/akismet/views/get.php b/wp-content/plugins/akismet/views/get.php new file mode 100644 index 0000000..70727f6 --- /dev/null +++ b/wp-content/plugins/akismet/views/get.php @@ -0,0 +1,5 @@ +
      + + + +
      \ No newline at end of file diff --git a/wp-content/plugins/akismet/views/notice.php b/wp-content/plugins/akismet/views/notice.php new file mode 100644 index 0000000..60f2e8c --- /dev/null +++ b/wp-content/plugins/akismet/views/notice.php @@ -0,0 +1,102 @@ + +
      + +
      +
      +
      A
      +
      +
      +
      +
      +
      +
      Almost done - activate your account and say goodbye to comment spam', 'akismet');?>
      +
      +
      +
      + +
      +

      +

      + +

      + +
      + +

      upgrade WordPress to a current version, or downgrade to version 2.4 of the Akismet plugin.', 'akismet'), 'https://codex.wordpress.org/Upgrading_WordPress', 'https://wordpress.org/extend/plugins/akismet/download/');?>

      + +
      +

      +

      +

      https://akismet.com/errors/' . $code . '' ); + + ?> +

      +
      + +
      +

      +

      gethostbynamel functions. Akismet cannot work correctly until this is fixed. Please contact your web host or firewall administrator and give them this information about Akismet’s system requirements.', 'akismet'), 'http://blog.akismet.com/akismet-hosting-faq/'); ?>

      +
      + +
      +

      +

      our guide about firewalls.', 'akismet'), 'http://blog.akismet.com/akismet-hosting-faq/'); ?>

      +
      + +
      +

      +

      update your payment details.', 'akismet'), 'https://akismet.com/account/'); ?>

      +
      + +
      +

      +

      Akismet account page to reactivate your subscription.', 'akismet'), 'https://akismet.com/account/'); ?>

      +
      + +
      +

      +

      Akismet support for assistance.', 'akismet'), 'https://akismet.com/contact/'); ?>

      +
      + +
      +

      +

      contributing a token amount.', 'akismet'), 'https://akismet.com/account/upgrade/'); ?>

      +
      + +
      +

      +

      Akismet support for assistance.', 'akismet'), 'https://akismet.com/contact/'); ?>

      +
      + +
      +

      +

      sign into your account and choose one. Please contact our support team with any questions.', 'akismet'), 'https://akismet.com/account/upgrade/', 'https://akismet.com/contact/' ); ?>

      +
      + +
      +

      +
      + +
      +

      +
      + +
      +

      +
      + +
      + +

      +

      upgrade to an Enterprise subscription. If you have any questions, please get in touch with our support team', 'akismet'), 'https://akismet.com/account/upgrade/', 'https://akismet.com/contact/'); ?>

      + +

      +

      upgrade to an Enterprise subscription, which covers an unlimited number of sites. Please contact our support team with any questions.', 'akismet'), 'https://akismet.com/account/upgrade/', 'https://akismet.com/contact/'); ?>

      + +
      + diff --git a/wp-content/plugins/akismet/views/start.php b/wp-content/plugins/akismet/views/start.php new file mode 100644 index 0000000..692b8af --- /dev/null +++ b/wp-content/plugins/akismet/views/start.php @@ -0,0 +1,95 @@ +
      status, array( 'active', 'active-dunning', 'no-sub', 'missing', 'cancelled', 'suspended' ) ) ) : + if ( $akismet_user->status == 'missing' ) :?> +

      +
      +
      + + user_email ); ?> +
      +
      + + + + +
      +
      +status == 'cancelled' ) :?> +

      +
      +
      + + user_email ); ?> +
      +
      + + + + +
      +
      +status == 'suspended' ) : ?> +

      +
      + +

      user_email ); ?>

      +

      + +
      + +

      +
      +
      + + user_email ); ?> +
      +
      + + + + +
      +
      + +
      +
      + +

      +
      + __( 'Register a different email address' , 'akismet'), 'classes' => array( 'right', 'button', 'button-secondary' ) ) ); ?> +
      +
      +
      + +

      +
      +
      + + + + +
      +
      + +

      +
      +
      + +

      +
      + __( 'Get your API key' , 'akismet'), 'classes' => array( 'right', 'button', 'button-primary' ) ) ); ?> +
      +
      +
      + +

      +
      +
      + + + + +
      +
      +
      \ No newline at end of file diff --git a/wp-content/plugins/akismet/views/stats.php b/wp-content/plugins/akismet/views/stats.php new file mode 100644 index 0000000..1ca7661 --- /dev/null +++ b/wp-content/plugins/akismet/views/stats.php @@ -0,0 +1,4 @@ +
      +

      + +
      \ No newline at end of file diff --git a/wp-content/plugins/akismet/views/strict.php b/wp-content/plugins/akismet/views/strict.php new file mode 100644 index 0000000..3cf197c --- /dev/null +++ b/wp-content/plugins/akismet/views/strict.php @@ -0,0 +1,7 @@ + + +
      +

      +

      +
      + \ No newline at end of file diff --git a/wp-content/plugins/akismet/wrapper.php b/wp-content/plugins/akismet/wrapper.php new file mode 100644 index 0000000..12641c7 --- /dev/null +++ b/wp-content/plugins/akismet/wrapper.php @@ -0,0 +1,293 @@ +Hello, Dolly in the upper right of your admin screen on every page. +Author: Matt Mullenweg +Version: 1.6 +Author URI: http://ma.tt/ +*/ + +function hello_dolly_get_lyric() { + /** These are the lyrics to Hello Dolly */ + $lyrics = "Hello, Dolly +Well, hello, Dolly +It's so nice to have you back where you belong +You're lookin' swell, Dolly +I can tell, Dolly +You're still glowin', you're still crowin' +You're still goin' strong +We feel the room swayin' +While the band's playin' +One of your old favourite songs from way back when +So, take her wrap, fellas +Find her an empty lap, fellas +Dolly'll never go away again +Hello, Dolly +Well, hello, Dolly +It's so nice to have you back where you belong +You're lookin' swell, Dolly +I can tell, Dolly +You're still glowin', you're still crowin' +You're still goin' strong +We feel the room swayin' +While the band's playin' +One of your old favourite songs from way back when +Golly, gee, fellas +Find her a vacant knee, fellas +Dolly'll never go away +Dolly'll never go away +Dolly'll never go away again"; + + // Here we split it into lines + $lyrics = explode( "\n", $lyrics ); + + // And then randomly choose a line + return wptexturize( $lyrics[ mt_rand( 0, count( $lyrics ) - 1 ) ] ); +} + +// This just echoes the chosen line, we'll position it later +function hello_dolly() { + $chosen = hello_dolly_get_lyric(); + echo "

      $chosen

      "; +} + +// Now we set that function up to execute when the admin_notices action is called +add_action( 'admin_notices', 'hello_dolly' ); + +// We need some CSS to position the paragraph +function dolly_css() { + // This makes sure that the positioning is also good for right-to-left languages + $x = is_rtl() ? 'left' : 'right'; + + echo " + + "; +} + +add_action( 'admin_head', 'dolly_css' ); + +?> diff --git a/wp-content/plugins/index.php b/wp-content/plugins/index.php new file mode 100644 index 0000000..6220032 --- /dev/null +++ b/wp-content/plugins/index.php @@ -0,0 +1,2 @@ + + +
      +
      + +
      + + +
      +

      + + +
      +
      + +
      +
      + + diff --git a/wp-content/themes/twentyfifteen/archive.php b/wp-content/themes/twentyfifteen/archive.php new file mode 100644 index 0000000..bb6021e --- /dev/null +++ b/wp-content/themes/twentyfifteen/archive.php @@ -0,0 +1,64 @@ + + +
      +
      + + + + + + __( 'Previous page', 'twentyfifteen' ), + 'next_text' => __( 'Next page', 'twentyfifteen' ), + 'before_page_number' => '' . __( 'Page', 'twentyfifteen' ) . ' ', + ) ); + + // If no content, include the "No posts found" template. + else : + get_template_part( 'content', 'none' ); + + endif; + ?> + +
      +
      + + diff --git a/wp-content/themes/twentyfifteen/author-bio.php b/wp-content/themes/twentyfifteen/author-bio.php new file mode 100644 index 0000000..839df91 --- /dev/null +++ b/wp-content/themes/twentyfifteen/author-bio.php @@ -0,0 +1,39 @@ + + +
      +

      +
      + +
      + +
      +

      + +

      + + +

      + +
      +
      diff --git a/wp-content/themes/twentyfifteen/comments.php b/wp-content/themes/twentyfifteen/comments.php new file mode 100644 index 0000000..12b892f --- /dev/null +++ b/wp-content/themes/twentyfifteen/comments.php @@ -0,0 +1,58 @@ + + +
      + + +

      + +

      + + + +
        + 'ol', + 'short_ping' => true, + 'avatar_size' => 56, + ) ); + ?> +
      + + + + + + +

      + + + + +
      diff --git a/wp-content/themes/twentyfifteen/content-link.php b/wp-content/themes/twentyfifteen/content-link.php new file mode 100644 index 0000000..2beb1f7 --- /dev/null +++ b/wp-content/themes/twentyfifteen/content-link.php @@ -0,0 +1,60 @@ + + + diff --git a/wp-content/themes/twentyfifteen/content-none.php b/wp-content/themes/twentyfifteen/content-none.php new file mode 100644 index 0000000..f1d59be --- /dev/null +++ b/wp-content/themes/twentyfifteen/content-none.php @@ -0,0 +1,37 @@ + + +
      + + +
      + + + +

      Get started here.', 'twentyfifteen' ), esc_url( admin_url( 'post-new.php' ) ) ); ?>

      + + + +

      + + + + +

      + + + + +
      +
      diff --git a/wp-content/themes/twentyfifteen/content-page.php b/wp-content/themes/twentyfifteen/content-page.php new file mode 100644 index 0000000..7d37daa --- /dev/null +++ b/wp-content/themes/twentyfifteen/content-page.php @@ -0,0 +1,37 @@ + + +
      > + + +
      + ', '' ); ?> +
      + +
      + + '', + 'link_before' => '', + 'link_after' => '', + 'pagelink' => '' . __( 'Page', 'twentyfifteen' ) . ' %', + 'separator' => ', ', + ) ); + ?> +
      + + ', '' ); ?> + +
      diff --git a/wp-content/themes/twentyfifteen/content-search.php b/wp-content/themes/twentyfifteen/content-search.php new file mode 100644 index 0000000..33e2d99 --- /dev/null +++ b/wp-content/themes/twentyfifteen/content-search.php @@ -0,0 +1,37 @@ + + + diff --git a/wp-content/themes/twentyfifteen/content.php b/wp-content/themes/twentyfifteen/content.php new file mode 100644 index 0000000..2475600 --- /dev/null +++ b/wp-content/themes/twentyfifteen/content.php @@ -0,0 +1,60 @@ + + +
      > + + +
      + ', '' ); + else : + the_title( sprintf( '

      ', esc_url( get_permalink() ) ), '

      ' ); + endif; + ?> +
      + +
      + ', '', false ) + ) ); + + wp_link_pages( array( + 'before' => '', + 'link_before' => '', + 'link_after' => '', + 'pagelink' => '' . __( 'Page', 'twentyfifteen' ) . ' %', + 'separator' => ', ', + ) ); + ?> +
      + + + +
      + + ', '' ); ?> +
      + +
      diff --git a/wp-content/themes/twentyfifteen/css/editor-style.css b/wp-content/themes/twentyfifteen/css/editor-style.css new file mode 100644 index 0000000..c9ea518 --- /dev/null +++ b/wp-content/themes/twentyfifteen/css/editor-style.css @@ -0,0 +1,481 @@ +/* +Theme Name: Twenty Fifteen +Description: Used to style the TinyMCE editor. +*/ + + +/** + * Table of Contents: + * + * 1.0 - Body + * 2.0 - Typography + * 3.0 - Elements + * 4.0 - Alignment + * 5.0 - Caption + * 6.0 - Galleries + * 7.0 - Audio / Video + * 8.0 - RTL + */ + + +/** + * 1.0 Body + */ + +body { + color: #333; + font-family: "Noto Serif", serif; + font-weight: 400; + font-size: 17px; + line-height: 1.6471; + margin: 20px 40px; + max-width: 660px; + vertical-align: baseline; +} + + +/** + * 2.0 Typography + */ + +h1, +h2, +h3, +h4, +h5, +h6 { + clear: both; + font-weight: 700; + margin: 56px 0 28px; +} + +h1 { + font-size: 35px; + line-height: 1.2308; +} + +h2 { + font-size: 29px; + line-height: 1.2069; +} + +h3 { + font-size: 24px; + line-height: 1.1667; +} + +h4 { + font-size: 20px; + line-height: 1.4; +} + +h5, +h6 { + font-size: 17px; + letter-spacing: 0.1em; + line-height: 1.2353; + text-transform: uppercase; +} + +h1:first-child, +h2:first-child, +h3:first-child, +h4:first-child, +h5:first-child, +h6:first-child { + margin-top: 0; +} + +p { + margin: 0 0 28px; +} + +b, +strong { + font-weight: 700; +} + +dfn, +cite, +em, +i { + font-style: italic; +} + +blockquote { + border-left: 4px solid #707070; + color: #707070; + font-size: 20px; + font-style: italic; + line-height: 1.8182; + margin: 0 0 35px -21px; + padding-left: 17px; +} + +blockquote > blockquote { + margin-left: 0; +} + +blockquote p { + margin-bottom: 35px; +} + +blockquote > p:last-child { + margin-bottom: 0; +} + +blockquote cite, +blockquote small { + color: #333; + font-family: "Noto Sans", sans-serif; + font-size: 17px; + line-height: 1.6471; +} + +blockquote em, +blockquote i, +blockquote cite { + font-style: normal; +} + +blockquote strong, +blockquote b { + font-weight: 400; +} + +address { + font-style: italic; + margin: 0 0 28px; +} + +code, +kbd, +tt, +var, +samp, +pre { + font-family: Inconsolata, monospace; +} + +pre { + background-color: #fcfcfc; + border: 1px solid #eaeaea; + font-size: 17px; + line-height: 1.2353; + margin-bottom: 28px; + max-width: 100%; + overflow: auto; + padding: 14px; + white-space: pre; + white-space: pre-wrap; + word-wrap: break-word; +} + +abbr[title] { + border-bottom: 1px dotted #eaeaea; + cursor: help; +} + +mark, +ins { + background-color: #fff9c0; + text-decoration: none; +} + +sup, +sub { + font-size: 75%; + height: 0; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sup { + bottom: 1ex; +} + +sub { + top: .5ex; +} + +small { + font-size: 75%; +} + +big { + font-size: 125%; +} + + +/** + * 3.0 Elements + */ + +hr { + background-color: #eaeaea; + border: 0; + height: 1px; + margin-bottom: 28px; +} + +ul, +ol { + margin: 0 0 28px 0; + padding: 0; +} + +ul { + list-style: disc; +} + +ol { + list-style: decimal; +} + +li > ul, +li > ol { + margin: 0 0 0 23px; +} + +dl { + margin: 0 0 28px; +} + +dt { + font-weight: bold; +} + +dd { + margin: 0 0 28px; +} + +table, +th, +td, +.mce-item-table, +.mce-item-table th, +.mce-item-table td { + border: 1px solid #eaeaea; +} + +table a { + color: #333; +} + +table, +.mce-item-table { + border-collapse: separate; + border-spacing: 0; + border-width: 1px 0 0 1px; + margin: 0 0 28px; + width: 100%; +} + +table th, +.mce-item-table th, +table caption { + border-width: 0 1px 1px 0; + font-family: "Noto Serif", serif; + font-size: 17px; + font-weight: 700; + padding: 7px; + text-align: left; + vertical-align: baseline; +} + +table td, +.mce-item-table td { + border-width: 0 1px 1px 0; + font-family: "Noto Serif", serif; + font-size: 17px; + padding: 7px; + vertical-align: baseline; +} + +img { + border: 0; + height: auto; + max-width: 660px; + vertical-align: middle; +} + +a img { + display: block; +} + +figure { + margin: 0; +} + +del { + opacity: 0.8; +} + +a { + border-bottom: 1px solid #333; + color: #333; + text-decoration: none; +} + + +/** + * 4.0 Alignment + */ + +.alignleft { + float: left; + margin: 7px 28px 28px 0; +} + +.alignright { + float: right; + margin: 7px 0 28px 28px; +} + +.aligncenter { + clear: both; + display: block; + margin: 7px auto; +} + + +/** + * 5.0 Caption + */ + +.wp-caption { + background: transparent; + border: none; + color: #707070; + font-family: "Noto Sans", sans-serif; + margin: 0 0 28px 0; + max-width: 660px; + padding: 0; + text-align: inherit; +} + +.wp-caption.alignleft { + margin: 7px 28px 21px 0; +} + +.wp-caption.alignright { + margin: 7px 0 21px 28px; +} + +.wp-caption.aligncenter { + margin: 7px auto; +} + +.wp-caption .wp-caption-text, +.wp-caption-dd { + font-size: 14px; + line-height: 1.5; + padding: 7px 0; +} + + +/** + * 6.0 Galleries + */ + +.gallery-item { + display: inline-block; + padding: 1.79104477%; + text-align: center; + vertical-align: top; + width: 100%; +} + +.gallery-columns-2 .gallery-item { + max-width: 50%; +} + +.gallery-columns-3 .gallery-item { + max-width: 33.33%; +} + +.gallery-columns-4 .gallery-item { + max-width: 25%; +} + +.gallery-columns-5 .gallery-item { + max-width: 20%; +} + +.gallery-columns-6 .gallery-item { + max-width: 16.66%; +} + +.gallery-columns-7 .gallery-item { + max-width: 14.28%; +} + +.gallery-columns-8 .gallery-item { + max-width: 12.5%; +} + +.gallery-columns-9 .gallery-item { + max-width: 11.11%; +} + +.gallery .gallery-caption { + color: #707070; + display: block; + font-family: "Noto Sans", sans-serif; + font-size: 14px; + line-height: 1.5; + padding: 7px 0; +} + +.gallery-columns-6 .gallery-caption, +.gallery-columns-7 .gallery-caption, +.gallery-columns-8 .gallery-caption, +.gallery-columns-9 .gallery-caption { + display: none; +} + + +/** + * 7.0 Audio / Video + */ + +.mce-content-body .wpview-wrap { + margin-bottom: 32px; +} + +.mce-content-body .wp-audio-playlist { + margin: 0; +} + + +/** + * 8.0 RTL + */ + +body.rtl { + font-family: Arial, Tahoma, sans-serif; +} + +.rtl blockquote { + border-left: none; + border-right: 4px solid #707070; + margin: 0 -21px 35px 0; + padding-left: 0; + padding-right: 17px; +} + +.rtl blockquote > blockquote { + margin-left: auto; + margin-right: 0; +} + +.rtl li > ul, +.rtl li > ol { + margin: 0 23px 0 0; +} + +.rtl table th, +.rtl table caption { + text-align: right; +} diff --git a/wp-content/themes/twentyfifteen/css/ie.css b/wp-content/themes/twentyfifteen/css/ie.css new file mode 100644 index 0000000..b7203f4 --- /dev/null +++ b/wp-content/themes/twentyfifteen/css/ie.css @@ -0,0 +1,915 @@ +/* +Theme Name: Twenty Fifteen +Description: Global Styles for older IE versions (previous to IE9). +*/ + +body, +button, +input, +select, +textarea { + font-size: 19px; + line-height: 1.6842; +} + +button, +input { + line-height: normal; +} + +p, +address, +pre, +hr, +ul, +ol, +dl, +dd, +table { + margin-bottom: 1.6842em; +} + +ul, +ol { + margin-left: 0; +} + +li > ul, +li > ol, +blockquote > ul, +blockquote > ol { + margin-left: 1.3333em; +} + +blockquote { + border-color: inherit; + border-style: solid; + border-width: 0 0 0 4px; + font-size: 22px; + line-height: 1.8182; + margin-bottom: 1.8182em; + margin-left: -1.0909em; + padding-left: 0.9091em; +} + +blockquote > blockquote { + margin-left: 0; +} + +blockquote p { + margin-bottom: 1.8182em; +} + +blockquote cite, +blockquote small { + font-size: 19px; + line-height: 1.6842; +} + +pre { + line-height: 1.2632; +} + +.entry-content img, +.entry-summary img, +.page-content img, +.comment-content img, +.widget img { + max-width: 660px; +} + +img.size-full, +img.size-large, +img.header-image, +img.wp-post-image, +img[class*="align"], +img[class*="wp-image-"], +img[class*="attachment-"] { + height: auto; + width: auto; /* Prevent stretching of full-size and large-size images with height and width attributes in IE8 */ +} + +button, +input[type="button"], +input[type="reset"], +input[type="submit"], +.post-password-form input[type="submit"] { + font-size: 16px; + padding: 0.8125em 1.625em; +} + +input[type="text"], +input[type="email"], +input[type="url"], +input[type="password"], +input[type="search"], +textarea { + padding: 0.5278em; +} + +.main-navigation { + font-size: 16px; + line-height: 1.5; + margin: 9.0909%; +} + +.main-navigation ul ul { + border-bottom: 0; + border-top: 0; + margin-left: 1em; +} + +.main-navigation a { + padding: 0.75em 0; +} + +.main-navigation .page_item_has_children > a, +.main-navigation .main-navigation .menu-item-has-children > a { + padding-right: 53px; +} + +.main-navigation .menu-item-description { + font-size: 13px; + line-height: 1.8462; + margin-top: 0; +} + +.social-navigation { + margin: 9.0909%; + max-width: 660px; + padding-top: 0; +} + +.social-navigation ul { + margin-bottom: -1.2632em; +} + +.social-navigation a { + width: 2.5263em; + height: 2.5263em; +} + +.secondary-toggle { + margin-top: -32px; + right: 7.6897%; + width: 64px; + height: 64px; +} + +.secondary-toggle:before { + line-height: 64px; +} + +.post-password-form label, +.post-navigation .meta-nav, +.comment-navigation, +.image-navigation, +.author-heading, +.author-bio, +.entry-footer, +.page-links a, +.page-links span, +.comment-metadata, +.pingback .edit-link, +.comment-list .reply, +.comment-notes, +.comment-awaiting-moderation, +.logged-in-as, +.comment-form label, +.form-allowed-tags, +.site-info, +.wp-caption-text, +.gallery-caption, +.entry-caption { + font-size: 16px; +} + +.post-navigation .post-title { + font-size: 24px; + line-height: 1.1667; +} + +.pagination .nav-links { + min-height: 3.3684em; +} + +.pagination .page-numbers { + line-height: 3.3684em; + padding: 0 0.8421em; +} + +.pagination .prev, +.pagination .next { + padding: 0; + width: 64px; + height: 64px; +} + +.pagination .prev:before, +.pagination .next:before { + line-height: 64px; + width: 64px; + height: 64px; +} + +.image-navigation a { + display: block; + margin-bottom: 2em; +} + +.image-navigation .nav-previous, +.comment-navigation .nav-previous { + float: left; + width: 50%; +} +.image-navigation .nav-next, +.comment-navigation .nav-next { + float: right; + text-align: right; + width: 50%; +} + +.image-navigation .nav-previous a:before, +.image-navigation .nav-next a:after, +.comment-navigation .nav-previous a:before, +.comment-navigation .nav-next a:after { + font-size: 24px; + top: -1px; +} + +blockquote.alignleft, +.wp-caption.alignleft, +img.alignleft { + margin: 0.4211em 1.6842em 1.6842em 0; +} + +blockquote.alignright, +.wp-caption.alignright, +img.alignright { + margin: 0.4211em 0 1.6842em 1.6842em; +} + +blockquote.aligncenter, +.wp-caption.aligncenter, +img.aligncenter { + margin-top: 0.4211em; + margin-bottom: 1.6842em; +} + +.site-header { + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + padding: 0; +} + +.secondary { + background-color: #fff; + margin: 0 auto; + max-width: 807px; + padding: 0; +} + +.site-main { + padding: 7.6923% 0; +} + +.site-content { + margin: 0 auto; + max-width: 954px; +} + +.site-branding { + background-color: inherit; + margin: 0 auto; + max-width: 954px; + padding: 0; +} + +.site-title { + font-size: 32px; + line-height: 1.25; + margin: 7.6897% 7.6897% 0; +} + +.site-description { + background-color: inherit; + display: block; + filter: alpha(opacity=70); + font-size: 16px; + margin: 0.5em 7.6897% 7.6897%; +} + +.sidebar { + position: static !important; +} + +.widget-area { + clear: both; + margin: 9.0909% 9.0909% 0; + max-width: 660px; +} + +.widget { + font-size: 16px; + margin: 0 0 11.1111%; +} + +.widget p, +.widget address, +.widget hr, +.widget ul, +.widget ol, +.widget dl, +.widget dd, +.widget table, +.widget pre { + margin-bottom: 1.5em; +} + +.widget li > ul, +.widget li > ol { + margin-bottom: 0; +} + +.widget blockquote { + font-size: 19px; + line-height: 1.6842; + margin-bottom: 1.6842em; + margin-left: -1.2632em; + padding-left: 1.0526em; +} + +.widget blockquote > blockquote { + margin-left: 0; +} + +.widget blockquote p { + margin-bottom: 1.6842em; +} + +.widget blockquote cite, +.widget blockquote small { + font-size: 16px; + line-height: 1.5; +} + +.widget pre { + line-height: 1.5; + padding: 0.75em; +} + +.widget button, +.widget input, +.widget select, +.widget textarea { + line-height: 1.5; +} + +.widget button, +.widget input { + line-height: normal; +} + +.widget button, +.widget input[type="button"], +.widget input[type="reset"], +.widget input[type="submit"] { + font-size: 16px; + padding: 0.8125em 1.625em; +} + +.widget input[type="text"], +.widget input[type="email"], +.widget input[type="url"], +.widget input[type="password"], +.widget input[type="search"], +.widget textarea { + padding: 0.75em; +} + +.widget-title { + margin: 0 0 1.5em; +} + +.widget_calendar td, +.widget_calendar th { + line-height: 2.9375; +} + +.widget_calendar caption { + margin: 0 0 1.5em; +} + +.widget_archive li, +.widget_categories li, +.widget_links li, +.widget_meta li, +.widget_nav_menu li, +.widget_pages li, +.widget_recent_comments li, +.widget_recent_entries li { + padding: 0.7188em 0; +} + +.widget_categories .children, +.widget_nav_menu .sub-menu, +.widget_pages .children { + margin: 0.7188em 0 0 1em; + padding-top: 0.7188em; +} + +.widget_rss li { + margin-bottom: 1.5em; +} + +.widget_rss .rss-date, +.widget_rss cite { + font-size: 13px; + line-height: 1.8462; +} + +.widget .wp-caption-text, +.widget .gallery-caption { + line-height: 1.5; + padding: 0.5em 0; +} + +.hentry, +.page-header, +.page-content { + margin: 0 7.6923%; +} + +.hentry + .hentry, +.page-header + .hentry, +.page-header + .page-content { + margin-top: 7.6923%; +} + +.post-thumbnail { + margin-bottom: 2.9474em; +} + +.entry-header { + padding: 0 9.0909%; +} + +.entry-title { + font-size: 39px; + line-height: 1.2308; + margin-bottom: 1.2308em; +} + +.entry-content, +.entry-summary { + padding: 0 9.0909% 9.0909%; +} + +.entry-content h1, +.entry-summary h1, +.page-content h1, +.comment-content h1 { + font-size: 39px; + line-height: 1.2308; + margin-top: 1.641em; + margin-bottom: 0.8205em; +} + +.entry-content h2, +.entry-summary h2, +.page-content h2, +.comment-content h2 { + font-size: 32px; + line-height: 1.25; + margin-top: 2em; + margin-bottom: 1em; +} + +.entry-content h3, +.entry-summary h3, +.page-content h3, +.comment-content h3 { + font-size: 27px; + line-height: 1.1852; + margin-top: 2.3704em; + margin-bottom: 1.1852em; +} + +.entry-content h4, +.entry-summary h4, +.page-content h4, +.comment-content h4 { + font-size: 22px; + line-height: 1.4545; + margin-top: 2.9091em; + margin-bottom: 1.4545em; +} + +.entry-content h5, +.entry-content h6, +.entry-summary h5, +.entry-summary h6, +.page-content h5, +.page-content h6, +.comment-content h5, +.comment-content h6 { + font-size: 19px; + line-height: 1.2632; + margin-top: 3.3684em; + margin-bottom: 1.6842em; +} + +.entry-content .more-link:after { + font-size: 24px; + top: 3px; +} + +.author-info { + margin: 0 9.0909%; + padding: 9.0909% 0; +} + +.author-info .avatar { + margin: 0 1.6842em 1.6842em 0; + width: 56px; + height: 56px; +} + +.author-link:after { + font-size: 24px; + top: 0; +} + +.entry-footer { + padding: 4.5454% 9.0909%; +} + +.posted-on:before, +.byline:before, +.cat-links:before, +.tags-links:before, +.comments-link:before, +.entry-format:before, +.edit-link:before, +.full-size-link:before { + top: 4px; +} + +.updated { + display: none; +} + +.updated.published { + display: inline; +} + +.page-header { + border-color: inherit; + border-style: solid; + border-width: 0 0 0 7px; + padding: 3.8461% 7.6923%; +} + +.page-title, +.taxonomy-description { + margin-left: -7px; +} + +.taxonomy-description { + padding-top: 0.4211em; +} + +.page-title, +.comments-title, +.comment-reply-title, +.post-navigation .post-title { + font-size: 27px; + line-height: 1.1852; +} + +.page-content { + padding: 7.6923%; +} + +.page-links { + margin-bottom: 1.4736em; +} + +.page-links a, +.page-links > span { + margin: 0 0.25em 0.25em 0; +} + +.format-aside .entry-title, +.format-image .entry-title, +.format-video .entry-title, +.format-quote .entry-title, +.format-gallery .entry-title, +.format-status .entry-title, +.format-link .entry-title, +.format-audio .entry-title, +.format-chat .entry-title { + font-size: 22px; + line-height: 1.4545; + margin-bottom: 32px; +} + +.format-link .entry-title a:after { + top: 0.125em; +} + +.comments-title { + margin-bottom: 1.4545em; +} + +.comment-list article, +.comment-list .pingback, +.comment-list .trackback { + padding: 1.6842em 0; +} + +.comment-list + .comment-respond, +.comment-navigation + .comment-respond { + padding-top: 1.6842em; +} + +.comment-list .children > li { + padding-left: 1.4737em; +} + +.comment-meta { + position: relative; +} + +.comment-author { + margin-bottom: 0; + padding-left: 4.6315em; +} + +.comment-author .avatar { + margin: 0; + position: absolute; + top: 3px; + left: 0; + width: 56px; + height: 56px; +} + +.comment-metadata { + line-height: 2; + padding-left: 5.5em; +} + +.comment-metadata .edit-link:before, +.pingback .edit-link:before { + top: 8px; +} + +.bypostauthor > article .fn:after { + top: 8px; + left: 6px; +} + +.comment-content ul, +.comment-content ol { + margin: 0 0 1.6842em 0; +} + +.comment-content li > ul, +.comment-content li > ol, +.comment-content blockquote > ul, +.comment-content blockquote > ol { + margin-left: 1.3333em; +} + +.comment-list .reply a { + padding: 0.4375em 0.875em; +} + +.comment-form, +.no-comments { + padding-top: 1.6842em; +} + +.comment-reply-title small a:before { + top: -1px; +} + +.comment-list .reply { + margin-top: 0; +} + +.site-footer { + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + margin: 0 auto; + max-width: 806px; + padding: 0; +} + +.site-info { + margin: 4.5454% 9.0909%; +} + +.post-navigation { + border-top: 0; + margin: 7.6923% 7.6923% 0; +} + +.post-navigation a { + padding: 4.5454% 9.0909%; +} + +.pagination { + border-top: 0; + margin: 7.6923% 7.6923% 0; + padding: 0; +} + +.pagination .page-numbers { + display: inline-block; +} + +.pagination .meta-nav { + display: none; +} + +.image-navigation { + padding: 0 9.0909%; +} + +.comments-area { + border-top: 0; + margin: 7.6923% 7.6923% 0; +} + +embed, +iframe, +object, +video { + margin-bottom: 1.6842em; +} + +.wp-audio-shortcode, +.wp-video, +.wp-playlist.wp-audio-playlist { + font-size: 19px; + margin-bottom: 1.6842em; +} + +.wp-caption, +.gallery { + margin-bottom: 1.6842em; +} + +.wp-caption-text, +.gallery-caption { + padding: 0.5em 0; +} + + +/** + * RTL + */ + +.rtl ul, +.rtl ol { + margin-right: 0; + margin-left: auto; +} + +.rtl li > ul, +.rtl li > ol, +.rtl blockquote > ul, +.rtl blockquote > ol { + margin-right: 1.3333em; + margin-left: auto; +} + +.rtl blockquote { + border-width: 0 4px 0 0; + margin-right: -1.0909em; + margin-left: auto; + padding-right: 0.9091em; + padding-left: 0; +} + +.rtl blockquote > blockquote { + margin-right: 0; + margin-left: auto; +} + +.rtl .main-navigation ul ul { + margin-right: 1em; + margin-left: auto; +} + +.rtl .main-navigation .page_item_has_children > a, +.rtl .main-navigation .main-navigation .menu-item-has-children > a { + padding-right: 0; + padding-left: 53px; +} + +.rtl .secondary-toggle { + right: auto; + left: 7.6897%; +} + +.rtl .image-navigation .nav-previous, +.rtl .comment-navigation .nav-previous { + float: right; +} + +.rtl .image-navigation .nav-next, +.rtl .comment-navigation .nav-next { + float: left; + text-align: left; +} + +.rtl blockquote.alignright, +.rtl .wp-caption.alignright +.rtl img.alignright { + margin: 0.4211em 0 1.6842em 1.6842em; +} + +.rtl blockquote.alignleft, +.rtl .wp-caption.alignleft, +.rtl img.alignleft { + margin: 0.4211em 1.6842em 1.6842em 0; +} + +.rtl .widget blockquote { + margin-right: -1.2632em; + margin-left: auto; + padding-right: 1.0526em; + padding-left: 0; +} + +.rtl .widget blockquote > blockquote { + margin-right: 0; + margin-left: auto; +} + +.rtl .widget_categories .children, +.rtl .widget_nav_menu .sub-menu, +.rtl .widget_pages .children { + margin: 0.7188em 1em 0 0; +} + +.rtl .page-links a, +.rtl .page-links > span { + margin: 0 0 0.25em 0.25em; +} + +.rtl .author-info .avatar { + margin: 0 0 1.6842em 1.6842em; +} + +.rtl .page-header { + border-width: 0 7px 0 0; +} + +.rtl .page-title, +.rtl .taxonomy-description { + margin-right: -7px; + margin-left: auto; +} + +.rtl .comment-list .children > li { + padding-right: 1.4737em; + padding-left: 0; +} + +.rtl .comment-author { + padding-right: 4.6315em; + padding-left: 0; +} + +.rtl .comment-author .avatar { + right: 0; + left: auto; +} + +.rtl .comment-content ul, +.rtl .comment-content ol { + margin-right: 0; + margin-left: auto; +} + +.rtl .comment-content li > ul, +.rtl .comment-content li > ol, +.rtl .comment-content blockquote > ul, +.rtl .comment-content blockquote > ol { + margin-right: 1.3333em; + margin-left: auto; +} + +.rtl .comment-metadata { + padding-right: 5.5em; + padding-left: 0; +} + +.rtl .bypostauthor > article .fn:after { + right: 6px; + left: auto; +} diff --git a/wp-content/themes/twentyfifteen/css/ie7.css b/wp-content/themes/twentyfifteen/css/ie7.css new file mode 100644 index 0000000..6f8cd43 --- /dev/null +++ b/wp-content/themes/twentyfifteen/css/ie7.css @@ -0,0 +1,89 @@ +/* +Theme Name: Twenty Fifteen +Description: IE7 specific style. +*/ + +.screen-reader-text { + clip: rect(1px 1px 1px 1px); +} + +.secondary-toggle { + color: #333; + font-size: 16px; + line-height: 60px; + width: auto; +} + +.pagination .prev, +.pagination .next { + font-size: 16px; + font-weight: 700; + line-height: 64px; + padding: 0 19px; + width: auto; +} + +.image-navigation, +.comment-navigation { + width: 662px; +} + +.post-navigation { + text-align: left; +} + +.site-main { + text-align: center; +} + +.hentry { + margin-bottom: 7.6923%; + text-align: left; + width: 808px; +} + +.page-header { + margin-bottom: 7.6923%; + text-align: left; +} + +.comments-area { + text-align: left; +} + +.comment-list, +.comment-navigation { + margin-bottom: 1.6471em; +} + +.gallery-columns-2 .gallery-item { + max-width: 48%; +} + +.gallery-columns-3 .gallery-item { + max-width: 31%; +} + +.gallery-columns-4 .gallery-item { + max-width: 22%; +} + +.gallery-columns-5 .gallery-item { + max-width: 17%; +} + +.gallery-columns-6 .gallery-item { + max-width: 13.5%; +} + +.gallery-columns-7 .gallery-item { + max-width: 11%; +} + +.gallery-columns-8 .gallery-item { + max-width: 9.5%; +} + +.gallery-columns-9 .gallery-item { + max-width: 8%; +} diff --git a/wp-content/themes/twentyfifteen/footer.php b/wp-content/themes/twentyfifteen/footer.php new file mode 100644 index 0000000..e57b5f5 --- /dev/null +++ b/wp-content/themes/twentyfifteen/footer.php @@ -0,0 +1,34 @@ + + + + +
      +
      + + +
      +
      + + + + + + + diff --git a/wp-content/themes/twentyfifteen/functions.php b/wp-content/themes/twentyfifteen/functions.php new file mode 100644 index 0000000..2c4b59f --- /dev/null +++ b/wp-content/themes/twentyfifteen/functions.php @@ -0,0 +1,331 @@ + tag in the document head, and expect WordPress to + * provide it for us. + */ + add_theme_support( 'title-tag' ); + + /* + * Enable support for Post Thumbnails on posts and pages. + * + * See: https://codex.wordpress.org/Function_Reference/add_theme_support#Post_Thumbnails + */ + add_theme_support( 'post-thumbnails' ); + set_post_thumbnail_size( 825, 510, true ); + + // This theme uses wp_nav_menu() in two locations. + register_nav_menus( array( + 'primary' => __( 'Primary Menu', 'twentyfifteen' ), + 'social' => __( 'Social Links Menu', 'twentyfifteen' ), + ) ); + + /* + * Switch default core markup for search form, comment form, and comments + * to output valid HTML5. + */ + add_theme_support( 'html5', array( + 'search-form', 'comment-form', 'comment-list', 'gallery', 'caption' + ) ); + + /* + * Enable support for Post Formats. + * + * See: https://codex.wordpress.org/Post_Formats + */ + add_theme_support( 'post-formats', array( + 'aside', 'image', 'video', 'quote', 'link', 'gallery', 'status', 'audio', 'chat' + ) ); + + $color_scheme = twentyfifteen_get_color_scheme(); + $default_color = trim( $color_scheme[0], '#' ); + + // Setup the WordPress core custom background feature. + add_theme_support( 'custom-background', apply_filters( 'twentyfifteen_custom_background_args', array( + 'default-color' => $default_color, + 'default-attachment' => 'fixed', + ) ) ); + + /* + * This theme styles the visual editor to resemble the theme style, + * specifically font, colors, icons, and column width. + */ + add_editor_style( array( 'css/editor-style.css', 'genericons/genericons.css', twentyfifteen_fonts_url() ) ); +} +endif; // twentyfifteen_setup +add_action( 'after_setup_theme', 'twentyfifteen_setup' ); + +/** + * Register widget area. + * + * @since Twenty Fifteen 1.0 + * + * @link https://codex.wordpress.org/Function_Reference/register_sidebar + */ +function twentyfifteen_widgets_init() { + register_sidebar( array( + 'name' => __( 'Widget Area', 'twentyfifteen' ), + 'id' => 'sidebar-1', + 'description' => __( 'Add widgets here to appear in your sidebar.', 'twentyfifteen' ), + 'before_widget' => '', + 'before_title' => '

      ', + 'after_title' => '

      ', + ) ); +} +add_action( 'widgets_init', 'twentyfifteen_widgets_init' ); + +if ( ! function_exists( 'twentyfifteen_fonts_url' ) ) : +/** + * Register Google fonts for Twenty Fifteen. + * + * @since Twenty Fifteen 1.0 + * + * @return string Google fonts URL for the theme. + */ +function twentyfifteen_fonts_url() { + $fonts_url = ''; + $fonts = array(); + $subsets = 'latin,latin-ext'; + + /* translators: If there are characters in your language that are not supported by Noto Sans, translate this to 'off'. Do not translate into your own language. */ + if ( 'off' !== _x( 'on', 'Noto Sans font: on or off', 'twentyfifteen' ) ) { + $fonts[] = 'Noto Sans:400italic,700italic,400,700'; + } + + /* translators: If there are characters in your language that are not supported by Noto Serif, translate this to 'off'. Do not translate into your own language. */ + if ( 'off' !== _x( 'on', 'Noto Serif font: on or off', 'twentyfifteen' ) ) { + $fonts[] = 'Noto Serif:400italic,700italic,400,700'; + } + + /* translators: If there are characters in your language that are not supported by Inconsolata, translate this to 'off'. Do not translate into your own language. */ + if ( 'off' !== _x( 'on', 'Inconsolata font: on or off', 'twentyfifteen' ) ) { + $fonts[] = 'Inconsolata:400,700'; + } + + /* translators: To add an additional character subset specific to your language, translate this to 'greek', 'cyrillic', 'devanagari' or 'vietnamese'. Do not translate into your own language. */ + $subset = _x( 'no-subset', 'Add new subset (greek, cyrillic, devanagari, vietnamese)', 'twentyfifteen' ); + + if ( 'cyrillic' == $subset ) { + $subsets .= ',cyrillic,cyrillic-ext'; + } elseif ( 'greek' == $subset ) { + $subsets .= ',greek,greek-ext'; + } elseif ( 'devanagari' == $subset ) { + $subsets .= ',devanagari'; + } elseif ( 'vietnamese' == $subset ) { + $subsets .= ',vietnamese'; + } + + if ( $fonts ) { + $fonts_url = add_query_arg( array( + 'family' => urlencode( implode( '|', $fonts ) ), + 'subset' => urlencode( $subsets ), + ), '//fonts.googleapis.com/css' ); + } + + return $fonts_url; +} +endif; + +/** + * Enqueue scripts and styles. + * + * @since Twenty Fifteen 1.0 + */ +function twentyfifteen_scripts() { + // Add custom fonts, used in the main stylesheet. + wp_enqueue_style( 'twentyfifteen-fonts', twentyfifteen_fonts_url(), array(), null ); + + // Add Genericons, used in the main stylesheet. + wp_enqueue_style( 'genericons', get_template_directory_uri() . '/genericons/genericons.css', array(), '3.2' ); + + // Load our main stylesheet. + wp_enqueue_style( 'twentyfifteen-style', get_stylesheet_uri() ); + + // Load the Internet Explorer specific stylesheet. + wp_enqueue_style( 'twentyfifteen-ie', get_template_directory_uri() . '/css/ie.css', array( 'twentyfifteen-style' ), '20141010' ); + wp_style_add_data( 'twentyfifteen-ie', 'conditional', 'lt IE 9' ); + + // Load the Internet Explorer 7 specific stylesheet. + wp_enqueue_style( 'twentyfifteen-ie7', get_template_directory_uri() . '/css/ie7.css', array( 'twentyfifteen-style' ), '20141010' ); + wp_style_add_data( 'twentyfifteen-ie7', 'conditional', 'lt IE 8' ); + + wp_enqueue_script( 'twentyfifteen-skip-link-focus-fix', get_template_directory_uri() . '/js/skip-link-focus-fix.js', array(), '20141010', true ); + + if ( is_singular() && comments_open() && get_option( 'thread_comments' ) ) { + wp_enqueue_script( 'comment-reply' ); + } + + if ( is_singular() && wp_attachment_is_image() ) { + wp_enqueue_script( 'twentyfifteen-keyboard-image-navigation', get_template_directory_uri() . '/js/keyboard-image-navigation.js', array( 'jquery' ), '20141010' ); + } + + wp_enqueue_script( 'twentyfifteen-script', get_template_directory_uri() . '/js/functions.js', array( 'jquery' ), '20141212', true ); + wp_localize_script( 'twentyfifteen-script', 'screenReaderText', array( + 'expand' => '' . __( 'expand child menu', 'twentyfifteen' ) . '', + 'collapse' => '' . __( 'collapse child menu', 'twentyfifteen' ) . '', + ) ); +} +add_action( 'wp_enqueue_scripts', 'twentyfifteen_scripts' ); + +/** + * Add featured image as background image to post navigation elements. + * + * @since Twenty Fifteen 1.0 + * + * @see wp_add_inline_style() + */ +function twentyfifteen_post_nav_background() { + if ( ! is_single() ) { + return; + } + + $previous = ( is_attachment() ) ? get_post( get_post()->post_parent ) : get_adjacent_post( false, '', true ); + $next = get_adjacent_post( false, '', false ); + $css = ''; + + if ( is_attachment() && 'attachment' == $previous->post_type ) { + return; + } + + if ( $previous && has_post_thumbnail( $previous->ID ) ) { + $prevthumb = wp_get_attachment_image_src( get_post_thumbnail_id( $previous->ID ), 'post-thumbnail' ); + $css .= ' + .post-navigation .nav-previous { background-image: url(' . esc_url( $prevthumb[0] ) . '); } + .post-navigation .nav-previous .post-title, .post-navigation .nav-previous a:hover .post-title, .post-navigation .nav-previous .meta-nav { color: #fff; } + .post-navigation .nav-previous a:before { background-color: rgba(0, 0, 0, 0.4); } + '; + } + + if ( $next && has_post_thumbnail( $next->ID ) ) { + $nextthumb = wp_get_attachment_image_src( get_post_thumbnail_id( $next->ID ), 'post-thumbnail' ); + $css .= ' + .post-navigation .nav-next { background-image: url(' . esc_url( $nextthumb[0] ) . '); } + .post-navigation .nav-next .post-title, .post-navigation .nav-next a:hover .post-title, .post-navigation .nav-next .meta-nav { color: #fff; } + .post-navigation .nav-next a:before { background-color: rgba(0, 0, 0, 0.4); } + '; + } + + wp_add_inline_style( 'twentyfifteen-style', $css ); +} +add_action( 'wp_enqueue_scripts', 'twentyfifteen_post_nav_background' ); + +/** + * Display descriptions in main navigation. + * + * @since Twenty Fifteen 1.0 + * + * @param string $item_output The menu item output. + * @param WP_Post $item Menu item object. + * @param int $depth Depth of the menu. + * @param array $args wp_nav_menu() arguments. + * @return string Menu item with possible description. + */ +function twentyfifteen_nav_description( $item_output, $item, $depth, $args ) { + if ( 'primary' == $args->theme_location && $item->description ) { + $item_output = str_replace( $args->link_after . '', '' . $args->link_after . '', $item_output ); + } + + return $item_output; +} +add_filter( 'walker_nav_menu_start_el', 'twentyfifteen_nav_description', 10, 4 ); + +/** + * Add a `screen-reader-text` class to the search form's submit button. + * + * @since Twenty Fifteen 1.0 + * + * @param string $html Search form HTML. + * @return string Modified search form HTML. + */ +function twentyfifteen_search_form_modify( $html ) { + return str_replace( 'class="search-submit"', 'class="search-submit screen-reader-text"', $html ); +} +add_filter( 'get_search_form', 'twentyfifteen_search_form_modify' ); + +/** + * Implement the Custom Header feature. + * + * @since Twenty Fifteen 1.0 + */ +require get_template_directory() . '/inc/custom-header.php'; + +/** + * Custom template tags for this theme. + * + * @since Twenty Fifteen 1.0 + */ +require get_template_directory() . '/inc/template-tags.php'; + +/** + * Customizer additions. + * + * @since Twenty Fifteen 1.0 + */ +require get_template_directory() . '/inc/customizer.php'; diff --git a/wp-content/themes/twentyfifteen/genericons/COPYING.txt b/wp-content/themes/twentyfifteen/genericons/COPYING.txt new file mode 100644 index 0000000..aece214 --- /dev/null +++ b/wp-content/themes/twentyfifteen/genericons/COPYING.txt @@ -0,0 +1,9 @@ +Genericons is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. + +The fonts are distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +As a special exception, if you create a document which uses this font, and embed this font or unaltered portions of this font into the document, this font does not by itself cause the resulting document to be covered by the GNU General Public License. This exception does not however invalidate any other reasons why the document might be covered by the GNU General Public License. If you modify this font, you may extend this exception to your version of the font, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. + +This license does not convey any intellectual property rights to third party trademarks that may be included in the icon font; such marks remain subject to all rights and guidelines of use of their owner. \ No newline at end of file diff --git a/wp-content/themes/twentyfifteen/genericons/Genericons.eot b/wp-content/themes/twentyfifteen/genericons/Genericons.eot new file mode 100644 index 0000000..b5f8647 Binary files /dev/null and b/wp-content/themes/twentyfifteen/genericons/Genericons.eot differ diff --git a/wp-content/themes/twentyfifteen/genericons/Genericons.svg b/wp-content/themes/twentyfifteen/genericons/Genericons.svg new file mode 100644 index 0000000..f813110 --- /dev/null +++ b/wp-content/themes/twentyfifteen/genericons/Genericons.svg @@ -0,0 +1,543 @@ + + + + + +Created by FontForge 20120731 at Fri Oct 3 09:39:07 2014 + By Joen +Created by Joen with FontForge 2.0 (http://fontforge.sf.net) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wp-content/themes/twentyfifteen/genericons/Genericons.ttf b/wp-content/themes/twentyfifteen/genericons/Genericons.ttf new file mode 100644 index 0000000..1f160dd Binary files /dev/null and b/wp-content/themes/twentyfifteen/genericons/Genericons.ttf differ diff --git a/wp-content/themes/twentyfifteen/genericons/Genericons.woff b/wp-content/themes/twentyfifteen/genericons/Genericons.woff new file mode 100644 index 0000000..973e033 Binary files /dev/null and b/wp-content/themes/twentyfifteen/genericons/Genericons.woff differ diff --git a/wp-content/themes/twentyfifteen/genericons/LICENSE.txt b/wp-content/themes/twentyfifteen/genericons/LICENSE.txt new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/wp-content/themes/twentyfifteen/genericons/LICENSE.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/wp-content/themes/twentyfifteen/genericons/README.md b/wp-content/themes/twentyfifteen/genericons/README.md new file mode 100644 index 0000000..faf8f60 --- /dev/null +++ b/wp-content/themes/twentyfifteen/genericons/README.md @@ -0,0 +1,152 @@ +## Genericons + +Genericons are vector icons embedded in a webfont designed to be clean and simple keeping with a generic aesthetic. + +Use genericons for instant HiDPI, to change icon colors on the fly, or even with CSS effects such as drop-shadows or gradients! + + +### Usage + +To use it, place the `font` folder in your stylesheet directory and enqueue the genericons.css file. Now you can create an icon like this: + +``` +.my-icon:before { + content: '\f101'; + font: normal 16px/1 'Genericons'; + display: inline-block; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +``` + +This will output a comment icon before every element with the class "my-icon". The `content: '\f101';` part of this CSS is easily copied from the helper tool at http://genericons.com/, or `example.html` in the `font` directory. + +You can also use the bundled example.css if you'd rather insert the icons using HTML tags. + + +### Notes + +**Photoshop mockups** + +The `Genericons.ttf` file found in the `font` directory can be placed in your system fonts folder and used Photoshop or other graphics apps if you like. + +If you're using Genericons in your Photoshop mockups, please remember to delete the old version of the font from Font Book, and grab the new one from the zip file. This also affects using it in your webdesigns: if you have an old version of the font installed locally, that's the font that'll be used in your website as well, so if you're missing icons, check for old versions of the font on your system. + +**Pixel grid** + +Genericons has been designed for a 16x16px grid. That means it'll look sharp at font-size: 16px exactly. It'll also be crisp at multiples thereof, such as 32px or 64px. It'll look reasonably crisp at in-between font sizes such as 24px or 48px, but not quite as crisp as 16 or 32. Please don't set the font-size to 17px, though, that'll just look terrible blurry. + +**Antialiasing** + +If you keep intact the `-webkit-font-smoothing: antialiased;` and `-moz-osx-font-smoothing: grayscale;` CSS properties. That'll make the icons look their best possible, in Firefox and WebKit based browsers. + +**optimizeLegibility** + +Note: On Android browsers with version 4.2, 4.3, and probably later, Genericons will simply not show up if you're using the CSS property "text-rendering" set to "optimizeLegibility. + +**Updates** + +We don't often update icons, but do very carefully when we get good feedback suggesting improvements. Please be mindful if you upgrade, and check that the updated icons behave as you intended. + + +### Changelog + +**3.2** + +A number of new icons and a couple of quick updates. + +* New: Activity +* New: HTML anchor +* New: Bug +* New: Download +* New: Handset +* New: Microphone +* New: Minus +* New: Plus +* New: Move +* New: Rating stars, empty, half, full +* New: Shuffle +* New: video camera +* New: Spotify +* New: Twitch +* Update: Fixed geometry in Edit icon +* Update: Updated Foursquare icon + +Twitch and Spotify mark the last social icons that will be added to Genericons. +Future social icons will have to happen in a separate font. + +**3.1** + +Genericons is now generated using a commandline tool called FontCustom. This makes it far easier to add new icons to the font, but the switch means the download zip now has a different layout, fonts have different filenames, there's now no .otf font included (but the .ttf should suffice), and the font now has slightly different metrics. I've taken great care to ensure this new version should work as a drop-in replacement, but please be mindful and test carefully if you choose to upgrade. + +* Per feedback, the baked-in 16px width and height has been removed from the helper CSS. It wasn't really necessary (the glyph itself has these dimensions naturally), and it caused some headaches. +* Base64 encoding is now included by default in the helper CSS. This makes it drop-in easy to get Genericons working in Firefox even when using a CDN. +* Title attribute on website tool. +* New: Website. +* New: Ellipsis. +* New: Foursquare. +* New: X-post. +* New: Sitemap. +* New: Hierarchy. +* New: Paintbrush. +* Updated: Show and Hide icons were updated for clarity. + +**3.0.3** + +Bunch of updates mostly. + +* Two new icons, Dropbox and Fullscreen. +* Updates to all icons containing an exclamation mark. +* Updates to Image and Quote. +* Nicer "Share" icon. +* Bigger default Linkedin icon. + +**3.0.2** + +A slew of new stuff and updates. + +* Social icons: Skype, Digg, Reddit, Stumbleupon, Pocket. +* New generic icons: heart, lock and print. +* New editing icons: code, bold, italic, image +* New interaction icons: subscribe, unsubscribe, subscribed, reply all, reply, flag. +* The hyperlink icon has been updated to be clearer, chunkier. +* The "home" icon has been updated for style, size and clarity. +* The email icon has been updated for style and clarity, and to fit with the new subscribe icons. +* The document icon has been updated for style. +* The "pin" icon has been updated for style and clarity. +* The Twitter icon has been scaled down to fit with the other social icons. + +**3.0.1** + +Mostly maintenance. + +* Fixed an issue with the example page that showed an old "top" icon instead of the actual NEW "refresh" icon. +* Added inverse Google+ and Path. +* Replaced tabs with spaces in the helper CSS. +* Changed the Genericons.com copy/paste tool to serve span's instead of div's for casual icon insertion. It's being converted to "inline-block" anyway. + +**3.0** + +Mainly maintenance and a few new icons. + +* Fast forward, rewind, PollDaddy, Notice, Info, Help, Portfolio +* Updated the feed icon. It's a bit smaller now for consistency, the previous one was rather big. +* So, the previous version numbering, 2.09, wasn't very PHP version compare friendly. So from now on it'll be 3.0, 3.1 etc. Props Ipstenu. +* Genericons.com now has a mini release blog. +* The CSS has prettier formatting, props Konstantin Obenland. + +**2.09** + +Updated Facebook icon to new version. Updated Instagram logo to use new one-color version. Updated Google+ icon to use same radius as Instagram and Facebook. Added a bunch of new icons, cog, unapprove, cart, media player buttons, tablet, send to tablet. + +**2.06** + +Included Base64 encoded version. This is necessary for Genericons to work with CDNs in Firefox. Firefox blocks fonts linked from a different domain. A CDN (typically s.example.com) usually puts the font on a subdomain, and is hence blocked in Firefox. + +**2.05** + +Added a bunch of new icons, including upload to cloud, download to cloud, many more. + +**2.0** + +Initial public release diff --git a/wp-content/themes/twentyfifteen/genericons/example.html b/wp-content/themes/twentyfifteen/genericons/example.html new file mode 100644 index 0000000..7e4db85 --- /dev/null +++ b/wp-content/themes/twentyfifteen/genericons/example.html @@ -0,0 +1,719 @@ + + + +Genericons + + + + + + + + +
      + +
      +
      + +

      Genericons — A free, GPL, flexible icon font for blogs!

      + + + +
      +
      + +
      +

      Genericons are vector icons embedded in a webfont designed to be clean and simple keeping with a generic aesthetic. Use for instant HiDPI or to easily change colors on the fly.

      +
      + +
      +
      + +
      +
      + + + +
      404
      + +
      activity
      + +
      anchor
      + +
      aside
      + +
      attachment
      + +
      audio
      + +
      bold
      + +
      book
      + +
      bug
      + +
      cart
      + +
      category
      + +
      chat
      + +
      checkmark
      + +
      close
      + +
      close-alt
      + +
      cloud
      + +
      cloud-download
      + +
      cloud-upload
      + +
      code
      + +
      codepen
      + +
      cog
      + +
      collapse
      + +
      comment
      + +
      day
      + +
      digg
      + +
      document
      + +
      dot
      + +
      downarrow
      + +
      download
      + +
      draggable
      + +
      dribbble
      + +
      dropbox
      + +
      dropdown
      + +
      dropdown-left
      + +
      edit
      + +
      ellipsis
      + +
      expand
      + +
      external
      + +
      facebook
      + +
      facebook-alt
      + +
      fastforward
      + +
      feed
      + +
      flag
      + +
      flickr
      + +
      foursquare
      + +
      fullscreen
      + + + +
      github
      + +
      googleplus
      + +
      googleplus-alt
      + +
      handset
      + +
      heart
      + +
      help
      + +
      hide
      + +
      hierarchy
      + +
      home
      + +
      image
      + +
      info
      + +
      instagram
      + +
      italic
      + +
      key
      + +
      leftarrow
      + + + +
      linkedin
      + +
      linkedin-alt
      + +
      location
      + +
      lock
      + +
      mail
      + +
      maximize
      + +
      menu
      + +
      microphone
      + +
      minimize
      + +
      minus
      + +
      month
      + +
      move
      + +
      next
      + +
      notice
      + +
      paintbrush
      + +
      path
      + +
      pause
      + +
      phone
      + +
      picture
      + +
      pinned
      + +
      pinterest
      + +
      pinterest-alt
      + +
      play
      + +
      plugin
      + +
      plus
      + +
      pocket
      + +
      polldaddy
      + +
      portfolio
      + +
      previous
      + +
      print
      + +
      quote
      + +
      rating-empty
      + +
      rating-full
      + +
      rating-half
      + +
      reddit
      + +
      refresh
      + +
      reply
      + +
      reply-alt
      + +
      reply-single
      + +
      rewind
      + +
      rightarrow
      + + + +
      send-to-phone
      + +
      send-to-tablet
      + +
      share
      + +
      show
      + +
      shuffle
      + +
      sitemap
      + +
      skip-ahead
      + +
      skip-back
      + +
      skype
      + +
      spam
      + +
      spotify
      + +
      standard
      + +
      star
      + +
      status
      + +
      stop
      + +
      stumbleupon
      + +
      subscribe
      + +
      subscribed
      + +
      summary
      + +
      tablet
      + +
      tag
      + +
      time
      + +
      top
      + +
      trash
      + +
      tumblr
      + +
      twitch
      + +
      twitter
      + +
      unapprove
      + +
      unsubscribe
      + +
      unzoom
      + +
      uparrow
      + +
      user
      + +
      video
      + +
      videocamera
      + +
      vimeo
      + +
      warning
      + +
      website
      + +
      week
      + +
      wordpress
      + +
      xpost
      + +
      youtube
      + +
      zoom
      + + +
      + + + +
      + + + +
      + + + diff --git a/wp-content/themes/twentyfifteen/genericons/genericons.css b/wp-content/themes/twentyfifteen/genericons/genericons.css new file mode 100644 index 0000000..36f02a3 --- /dev/null +++ b/wp-content/themes/twentyfifteen/genericons/genericons.css @@ -0,0 +1,209 @@ +/** + + Genericons + +*/ + + +/* IE8 and below use EOT and allow cross-site embedding. + IE9 uses WOFF which is base64 encoded to allow cross-site embedding. + So unfortunately, IE9 will throw a console error, but it'll still work. + When the font is base64 encoded, cross-site embedding works in Firefox */ + +@font-face { + font-family: 'Genericons'; + src: url('Genericons.eot'); +} + +@font-face { + font-family: 'Genericons'; + src: url(data:application/font-woff;charset=utf-8;base64,d09GRgABAAAAADgYAA0AAAAAWDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAA3/AAAABoAAAAcbOWpBk9TLzIAAAGUAAAARQAAAGBVb3cYY21hcAAAAngAAACUAAABqq7WqvhjdnQgAAADDAAAAAQAAAAEAEQFEWdhc3AAADf0AAAACAAAAAj//wADZ2x5ZgAABEAAADAqAABJ0A3bTddoZWFkAAABMAAAACkAAAA2B8ZTM2hoZWEAAAFcAAAAGAAAACQQuQgFaG10eAAAAdwAAACZAAABNGKqU2Vsb2NhAAADEAAAAS4AAAEuB9f1Nm1heHAAAAF0AAAAIAAAACAA6AEZbmFtZQAANGwAAAFRAAAChXCWuFJwb3N0AAA1wAAAAjEAAAXmlxz2knjaY2BkYGAA4rplZ/Tj+W2+MnBzMIDAhRBmaWSag4EDQjGBKADj7gZyAAAAeNpjYGRg4GAAgh1gEsRmZEAFLAAWNADXAAEAAACWAOgAEAAAAAAAAgAAAAEAAQAAAEAALgAAAAB42mNg4WBg/MLAysDAasw6k4GBUQ5CM19nSGMSYmBgYmDjZIADAQSTISDNNYXhwEeGr+IcIO4ODogwI5ISBQZGAOtvCU0AAAB42kVPuxXCQAyTL+GRmmVoKdgA6FNRMoObdAyRnj3o6NkGLOl4+N75I381AUeUTPoNASSyoWVUBMYUYkmt/KOQVdG79IceFtwj8QpN4JxI+vL4LrYUTlL294GNerLNcGfiRMu6gfhOGMbSzTOz30lv9SbvMoe+TRfHFld08b4wQ/Mhk6ocD8rtKzrHrV/49A34cy/9BURAKJ4AAAB42t2NPw8BQRTEZ+/E2Xi7NlHIJsI1hGgodVqdVqfVqZRqH8QXvL25eq0/USh8AL/kzWReJhkAOV43hMKDW0rqmVu4Jh/BpY+tdNDBh2ndoabnnGtuueeR52YQI1AhILhQ1iDoWHLJDXc88NQgxl5ujS2sMjNZyUImMhYvfTFSdC/v3R+oNj4llSXJvgv4e+6zoCcQAEQFEQAAACwALAAsAFoAhADMAPIBAAEcAUYBlAHOAggCsgNMA6QD4AQSBMIFXAWoBgQGdgcIByoHageOB8gIJgkeCn4LOgvIDH4Myg2YDeoOLA5oDtIO9A8QDy4PeA+aD+AQNhCgEN4RFBFSEZwR9hJgEoISpBLuEwwTKBNEE3ITihPOFAYUWBSYFMgU3BT4FT4VTBViFaAVzhY6FmYWlhaoFsIW2hbuFwQXEhcgFzYXlBfEGAIYNhh4GLIY2hj8GSoZhBnAGfAaBhoUGioaQBpOGn4awBr4GyobgBuWG6wb3hwCHCwccByqHOgdFh02HWodmh3MHgQeHh5GHowfpB/OH9wf6B/2IAQgWCCOIOYhdiGuIfAiciKOIrQi6CL2IyojRCN2I5QjviQIJJAkxCToAAB42oV8CWBU1dX/PW+dyT57Mkkms2RmAkkmyazZCEPYE3ZCWALKJkhYI7IorT4XFERwQdEiAtaK1l0roMUln3WtSktBPltrP7CLyx9b21o/hczlf+59MyGA+jF579333n3vbuf+zu+cex5EICMIERbK04hIVBJ6BkhN87OqRL4IP6PIf2x+VhQwSZ4R2WWZXX5WVaCv+Vlg1yMmj8nvMXlGCG5aDvfSy+Vppx8bIb1HCFEEIhCFyBp/bzbJJxbiIAQ8No9s88TkmMcGuPkxbcKjQCTSRwQtpYkESErDFDmLj8pa+t9Zwg8UNyIA5lHxh++1YFluyVwgSO5yocBMwvFowKtYxRr4Kcw7fJjuoZfQPYcPw1vHduw4tkMl567MYzn6Du9gNwgWr4GmaoqGr3WQYjIY6yqz5lk8JNwiREOCN0+wukC0yTESdoHNmif4vCGIxmVNIN9iY/FAHzqwb/3o0ev36YezZ4nw8ye3d0amrRs2fXtnJzamTxM1DcgZrT8TO4jfzk3upb2d26cPWzct0rn9ye2sPgIxDOw/7DuTB7BKbGM/Cd/Vp/UREXsFMAWajHuBAJ5Tvmcb9g+wawprm0CIUcC+1s7gWQp/eI8/h32ZixmtimqSTSGIReNuu6zd1nOW9Nx2ElpOytqG1ytSn2rCvRWvb9hz8iQfA3xKYWPAxhXrY80Dnykcj8G5pAdwTDef2tK9Q8gkKNaajfOWU5uB7OgekCQCqyevSxGJsnG120xYo1g8ZmKDiicOG9bNFHVg/+MddwDTLZCwsVv2MMsWFA9B1qHuzmTP7p5kZ3dvZ/ch+vWhus4GfkElhzZSbd7uwD2NHaBN7OmZSLWOxnsCu+eBtvEEHqi28dChjaAl10wvwjyU5wHMw3qO9KqsbgXEh+0N87pVggk8CQ9rtH7BhyPk87J6xSOK1r1jR7dGk3S/Blv2nKT8HE+TPKFgk9klmoRe7eQeQTt3uqMbMEVEyIybjKW6mASw8sDFxikYj0WDmCzAZIsQiwaCLDcfe03Kjzc1xWe1t0PBjAULZnTVtPonjpbx9hnchIL4rbtujc1q7+7G+zM/p32fz+yq6blx1OWHRmMR2M6oASWPrOMzyyWYbVZBkVQlgELBimlRsOAWIRAMQZ6gBoKKGhLzIQ9wcjgUm9UlOxQ1TwhBMCQFB+N1u8MlOVxKwmq32qxKMFAewNqaWwRxDdgh68RLN7YteYHSe30+CLpiMxeMH1tbskQxGvMtUl64eUHiqptvvioxf2goK6sg32CUlpTUjpkwf2YsmmsPjR46yikYS73xUimnyGhyisZSpzcXFIc7MWp+M/h899DUC0vabnzphIGwPf16y8P0rTOvhFV3ofSrKcPnOhVLeXjC/E1T916RXzHm0joQZXOd3wvg9deZFEGomNSQKMlevWfK5vkTwn6zEurKypMLYtVSrq+4UFCznWZQCl31Hil3kGtwXpapfGJdVqFbibx8Bhoe3sIbh53IgIoQ3qcGYiKliC1hkiSTCPGHE4KoENXuj5sT5bILzIgrZkecJALBHGDd6xIccckhAMtUnhAsXsVnt7RIiUAVuCWCsEcQ9wgDPonsP+R56k90U/cH4phd7xbSU/RYXmPX6fuvXPZjePyTgiT9G+2Rl4w+8L/N9tKg8iiMu9p5pvFV+s+aV+GrW7Y+4dbci36t7B2/Zcmga+hBehXsgg1g+dnP6Bd0I12I2xc/+xlYtElQBTe20SNv9u5dBh29oVDxvfTXwubkw/Q369+D+PharTMMHzRc2u0qjXTkeJRiKIV/T6OHjtvHhMAJ8YJ9dJ/Q6G5pLb/mTu2Cl2OBvFDWXYB4XIV4/BFpwBNFtSPgSpLP7bdHwjjlUbwwgYchKF8MrxJ2yYES2iJEwnZHPJEHalzV2pcL1bO0p39L6TZ6mJ6tqpr24B1D173k87vraq99ZMKM9hnhW+CWj7MaF2xqn7Al8uNl1o6GFUrtqgnFtiXH3jt0/+phD8mBUXXitpVqbtE7N8qVYvinlyzofPSd7EGVbZsWNA5JFCWTS7y5en0J6g9VI8F+dPAhSls8Q1BHRByJgA8VSCnCIirN8wCC/g3ycujfKlv3yeOXXHLnjCpKU1XshoqIcIYgdL4JUm9OcwL+lRW/dM2IU7Qv1bCjW8Y7HNuxXPkTLNfN8EFkioGVEW2RsCfKQPTyckVpN4zNp2/Q3j/9yVE95pJr2hLdTqc6Z2FF1GmUvqFH+g6KY6EGhOjc6WPipYoo0r+Z/NVeUTASRJ9M2yyIzB6ykKzg2GA3s0HxeXFGF5jjgJILCoRRdrPBbgFLPNEixqIMCAwIHZGwI1Du80qKGo6E40MhbldURQWLiDgSd9jPXfPjUKti3ByLim2wDMZ9uW3Y6n2vfXr1Afrcl9u2fUn/ePo9eu0oMXDL9ZLwzb9W/Rl8kwSpIM+iOgqt4JDNcp6kChMawbiCfnbfLfTs4THFRf5lPq/NkmetqgX/09d0WPOt1o0TA0t9PrxoqxR88pCvD/5B1fDtzx24+tPX9q0etu1LGMdLT+WdohsWSqX399WEZEV4ODXMI+3t2w05Sk5d3ahIYWhmzCv4De7skvxCW3ZDJyxc1fXgClkQocwrykLfPYIJZqiC1w1ZmYtqReXNO1MN3bD6w8NM1lHXk2t5/+YjykfIUhxJnOhe1cRknGEqWLAbAy3gcIkOuwKsh1CIgngB0VUBNuRIrJhocbFDnA4JQW9IxX5PcNCOJDxehZ1GPCibQrN5rOXgPde86/S4nWWeH79ty6u/enJzz/Qh2TYNclRIPTftpqLGD7Qp4yyjfPFSj1XsRQJ2ls9KprZk2RLtaoNgTqDAnW821LT/YubUvTenHrj2r5N0yRQaYSr89VqxpcHTXA5TpN/uXvLUPFFIdt8+aW9vKubxCPZFk6ZdLkBhbm1hRWkwKBcASRfRh8+X2Mcuumx2fWlWaUGJtdBmjI5uuvX5Vc/Xbps/dRibG1w3IrAqLyE/MpM6nR0FmeplooaqCCkIXoqyaQcqEgSPOeixtSh4T7AJc+gBaHtImHzZ4qmJjiqo6pQL6MHJnZWjB+dm04OSBGOzbW5PTaS1fMrmxQ1AxP+5ef7YtnnV4+tqx4fO7BTMS9b5I+7ieOq/xevnbDWV+IqLLdmJpU+s5GOppcfSgnOyeQAapKc940oWpAwh8CGpsdrxAq+moMY89gKbirVOcByzmXSEYCCAlMBBv71hxGSY1Dp8yuRhUtPDm8KT670F9BsAMBiyvA3ekcMykKEPwmkiFvV9Im6c2Ng8fkJT48S+DfDmUweKKoOFqzx09f4DcKjS5hxUemkHnYGd+RgqqsmooyaxGrskfWoHggLO0mAgYQkJvGcZDmN/svlqZlKG9casSMjUPPYXZNlaZKlu7e+f3DY3Wj31qh0HFi54yju2wDvnbrX0p1KefeuiqTMCzXmOqxeueWH+yBve+vGcx25eMTY41ayqolVQffZpaxPl45bd84s/G0hi/qa9++ds+PiVXcub5yTpR/UbtscfuVp42uhZEr310NIpke3/1bDg9ueh7sDlz1zXFpq86qZ7J9093+YszJmYVWgy+u56cdX43fdtXT89rOuUjB5ekOE2BUKegM0MxhMWFzDNwhol6o2yO+wIYZCIB4JpzYKiw5gt0v4Ep1xMtjBfGWAnOQLkQl6T5hx3bWsvGVOydfJVv7l9ctMVu95bvfbI7msmDupebC6RBZMgy3kjRmu9PZc92F0/acclsQ5/Tnada/Tw+KxYgcHYY3HI++mpXQNZDP2cfs3eP3j9AnDG2pceAvHurifuWplMXPKj2+9uu+XoYEOexZDMstpME6+a9+zNk5uX3DZt+zd3x7piNbvWDW6dPuLq9srJFgv1T52/eSI4YO3hfrIikL3CXHWuvBcnVz7n4AXIswvK00fZCjO++oo+8lXqynRC3sv2X6XP8KjrbsK5shdPJBFtBR9qkiAKC9LWBP4sZocZoQ1TeMmsbABrQQ4aZnem7l+2wjt5tvWqjo3XPT3zSF3U2jy2vmeVoWBTcuSNKjHQh2iKDqGDoAxuuwbKOpZdufpeg5X+lj4/kf7z6adn31sKT7A2ZGy5fMSGi+afUVAImjB7+vgeuNWpIAOn/FzAfR9n0gTgA6IpFTiXvbqFg+iKgMtA2YSKCsWGkeCYyRfjjUpIw+HndLqpoLp53KabV8+Zs2zDpZcMb42+0d3eHqo2qRptop/Q6K6qKmf5DPq3uN1eVtbQeN0GYU3Kl0zOmrklowsy+OEg1WTIxfUnbqXA7o4XYI34bHRz/oN1syO4x00ol5WoPkrBam+CcHwghIhl9NWTzJxDM+Hv5s2n6OenNpvp39tjMom1t8e09O58FKHkpP5U30mRjGpEYw3tuKaRKfaItD/zTDufWmcBVFDOkm3kTrKD/ITcTx4gD5FHmGWJTbDVKuzPqtSh/aLUKaqV7RQbAxTsTiUfQPEGobYGAsHaQCygd28gGA3yGRiI4cUodkGsNh6L10VZn8fCCX7Uf0OhNgHxsANq7XW19ojd0f+zsa2W/Vkd1jo7mOSEERx+2ZYAk1/1J4KqEYKyP6aqOOr8n4B/QnqPh1SrqcKUagURUJxFdlWA8/4J0J8Z1bzwMmYXXgYB+t+RfhHgq8D1SWpd6swn4Eq98RDcTT/+RBj92WefQaUgf0I/Fhofkv4lS7RaUAWQ2DOsUIEVmX4Dvh9odXYOHGWvT9dU5PfxAPgQPijBUUkWQAYBT9nGHuMvYPuj2dm0Ot1CUX8jK4NlwydgIn3vlZ0wgz6y85W9f1yRehmir9w3YdeuXZiasfOVB/644nxZtaCee5l8wmQVWWEB2otubua1IClH01FA/eCwSwmcMlw/IKYisA4FhqmYA21CC2eDCiP1iKy10TrGd8rZJf5onIFwCBT9gnAOmJHmBLji4dmYWYBvYzfZOVNKIhquQY7XyJ3wlD2RPhUgXJ7QqRJ7JWK4hGUGA+ZEHK8nFElBuDfbJYkcYCyUkUN6FyOhnI8e3U2PL1++0Gra96P14N4wtn3lu3dNL0+GsEeNIgz72WuLHwTXPLf/cvrh7eLgwZ1brlzbMWvuU9e0Z3d3LKJfLb9ySEuWYefyFf/T1OJoD23cFOu02CIFVbHSqlmBQNRgMBcVVIaLndFqc7FDVirLKmpCY3LRJjTa7CMDgVFWm2w2Fnsr7JVdHq9fFDo3tkam1eTYzJMWra0vHxYxFRvNjg2PdEy/fRrdcAo2LWqavuPt1eNvmOeMj1m9ih58+GH62ei23OkzoPpZk/k++tnba6/7EEI6B9abyShwmg3fY1izcin9/d13nR07Jq/BNmP7u6tGbVoTxrZmCdC+rOnWDZHqa+5OZQ2/qX71YF+Jt/2ap+YKS19pGW9talmy9Efrf+XyTJnT9XF7pNoaHDJ33rTiyjI1O8/hGD1ocIfH4bEIQo7TXNzm97eYkN7WVwpQNrbU5RGg0ufrCFo9TotkLCpzz6wdtjRkyhl5ycpYtKPaYM+rGVKe2NA88apYfs7yB/tu/ubdm25cc+S+pVb38q2T76FPrt+wqtT5P3t2wfKf3Pc7lyTk3PIB/dPuffR3H17fL78G1FQkm3SRK8mtun+SkekYkmlQfZwGodgwz18ZuGR2hjIsMslG6ybBU0osLdcopR6IhlCKOOnkHAJ5khhPcwrGQ60utMviiDIZtqtR+z13FroSbmehu7nK77AUOiyWaZ7yeKk7N7z4jnfWLHx47ZSgoaA0mPBGNtzaNsSSV5yFU1xQwNBomnXP3Nj4sfeDAew5ZeXDWiIWn2XY2urC8mGV3j8f+tmBl5oc4REL6l0tcUu0oCw8tLO2aoakZZi8QKZZSpJDLomEZ7a0Bkrt9praSkt+a4k7UT1kZHD4dT2dYf/QznkxeygSCddY3ZV2VSqyhKqcan52npovIXlJLrlhVMfDyetOz3NFwoMToXJRNucb8wfXTq65du9WcVFTT/TK1bMbLD5HcsWgWZdOG1Hhx7I3Im7E1evIIuxxF07qPDmExqcpz4AzmadcQjyB6tYlYj/HQ4ov6A3kYTZwiWWghiSc/C0i2kLybrVo7MgZI5qceWWVy1auW3X59KTZjGrEYLK6/dHS6IqOkWaLZ8Tw+gKoV6zJoTPGTxlalyWUt0zpmj11mMUiFUSi7aOmjh5TUlwkmpxFRuNJ1dE4qDR7zPCRjzz89E/v3TDbqQ4ScwaHp825YdvB+TM3T01Y5NxcVaH/T1DtDrfL5yrNNgtFrpxcKPRW5pVXi8+m/ibI2ZJsqR6+dOS467vaqrz5BoRYJb+wItJeXT138rjGqpzst43uJSseeuCN2ROuaHILeSVFWYTzr1uxb65EmRxErsPesavc0RxkIiahmmdMVERbmhk5KI7AvICBgT/Mw2xte5qo9N9HosV0rXWATrSmOUz/fVuG3sTVYREYf8P+hVctnzjuig+fR/ptGl7Xtf7uSVvXtY2a//JD21dPraKLmry+IU0dU5Z0utzlbktBNNE1v3Kwp8RRVBP1eYuc9fVTp63atmRZfUMi1jVj4+yWeq+npfXyCdWhQqfDVlJWFff64tHp6w78ZMUqsXXxFQv33zC+MW/Isl0v/GF1x7QrNk66e31XXXtO1dTV2x96ef4c+uuOy2cMaa4IFjsdFqPRnI/vCHnL3e6WkM1eXl4dCtcitXIGB41tm7toRGswUGI1mzyu8NDBVXabxxOrLSxCm659/LiaoaEQtweQ5RGF8dQoYyg4P3XrBvdKJbIuzrlCQiWYuFbiHc88/0hU0IpWNHuwyM629liSsSCaHHbl6FmDtd66FfOSoCKieWaOKjAYYG+sXSLFdeUGT1DfY+7u9oraCkG75IFvNsumak9Jx84p0/b6A+26ifIebFUj6mruLQySWjKUjEG7bDPWMo7V0octikQHxwqwlmmr117OzDOFnfnj3DxR7ajjWJJ7Xqx2CayOOHNFKcSrMJd51GLVfWuAGpvzyIydh/ksCGgOuQXtItYVaPUE/aLdwc5dIL2VP9iV3/nCoc581+D8+tvuoP9oDYWGDQuFWmHE7NbW2a2Cp7JhUHXZ1NSWx8D36KP0o8cepx89+ij4Uh9X1EwrrRrUKFfjQAyt3lcfyrvydfolPU6/fH1NQWll0dqpdVNLDv51tmw226ChcEpd25IlbTUT60R6evyfniqZFo7PjouGfFdlfmdnfqUrvx6UUCsW39qq70OhIWW1gxqCQ1KLu/cvXXagu/vA8QPdwn01JeOGlDcIHaGWUHUy9XSiqzhcd9kLGydO3Pj8ZWjPRob5pq6tDswzwtv27Bx5zKC6JXctqR4faqbX5MytCMVns/nJUFNFqSE+ksDxYA4uZsaLfDlIGIIKRF+K4N3msKmyJ2MzBmOOhH5Tmmz32701ALPvnzNSmx0HtWZEjfzmli1vSfcjLVJn754zZ/dsWHI/XpaOzLb7bSEvLZv1k5mxrh+POHLYU1PjgU82vfTKpqXV1x7p2jVr5s6u39WGjrHrRK8jW5tBuc4n5Rn7gS+Q6f4HtkSGfJetkzkg4UIjIeFQkOln1sbQUPhDoL3bT/9A/+Dvbg/AEtnUMKLBJKt8yeKIvnx2hK1RpPaxDPRD8PMHdkilPl+pRHSf4cvIDVv7168chBhFkzEnYTNCzCHcBj2pL+h2WC5YKKYFCyxP/VPIp9tTX0APvR2u2J36MvXlbrWVvksPQnnqBfDR5+m7EIUx9CP6sLiX/hHGQvTMt/S9xavpq9CyejFvu0DIWWUktt1FRvK2q6KAqpiZRCrkgW6xMWue8Uec32ztKGFGxsiMJZ1VMkuLe2094RaQ35jRaI3OlGXFWlTjOm2QVboub7A721qWX9ZcIZz0yk5LaoWtVP6301pa9pG1WBRcouSy0H8W+3zFMDTbXqCS+fMppS1Wq63CZhYMtKEgV5TVygrZ5qiqKqErf2Evc5v7DIqMclKY58wz7Mq1+rzFwWJPjoXjFFt7YmttA63ZAQtN5HsXltIrSRzrBJRavl7H1pHQmHUg1xEjQi/z7TGLF7OnNE2T0BxGZoQcISNLWLLC2FIO97IZIbPIKuFUSBFKxHe6GaApmEwRtobXzs5JZv2Ky2EZ8ad9xhnrgLmM9ZVVxCY8kywmNB5NYh24QH5x1aoX6Rn6MT3z0sqVL8Fda96/r6vrvvfX7KJf79wJWX+EwV30GZWsfEnPxLKj3YIPvnRmZdfO458f39m1k35N38LsEqGz6H93wST4gy4fWCfC13lNeO5lOGq3iqxXPawzpW6+UqwxL8DJPZLG14fp5yf3MM605yTrk3PtyibFpEr3PSJnjNhwszBnni5W3B5PjxcbKh8rLCKj0jmNmyZgZ7fH+rgFLeI+1etE5h9I4t6paGfYFNK0M5iNZUixvbA/4KSE3YdezHl+XVxkMGnEutSi5a+KjEclLHqJniaoDUfQICqBuh+qqoRlKaFIibrsSV4GYdahw81drd9ZY+lXIBhUrFFxTqgInsEqCW4H2qeHvqvyhOT013VgTEAxykYlaUIdN5zhacQmprdM2pNOR3Az/VBPZ549FyrAasyP39MASvQ87B7faPqY2Qvku5oCMT0ggc+PaTBNvVq9GtvjRoQDB6DB0CJAAtSAN5+vf6qQsIeHIuzCn4SyWamT5U2NQW+OtV745jmhbL+/O7C/0GwufC51Yn8A036hnufy15TmGUORKdKL+1MnnvP79xe1thbuF8owecDf3T83Oc4XkBLsOxVQS7MoiHK3ZEZ2R9BqQQRDDYXYh4aG6d4X0vMH6iFr58q+lesPf3V4PdsBNvgfKzN3cOrseuFeeCd9c/16kvG3p8viLb2gOJIuKg+sdkvMY5NN8I+LykyN6n+nQdDEldR0Ubn023O1MvA+FgfEe5SQCu6L6zfTfrAeotZvZwn/R3UUcm6FI/V/1IvrNwKVBqK8T3KxTqWIbtUstoJBW9AIcayKaATe8UZgnuU4mhpx7kQVOO9C/JThDJUX0q+Q93x1GVXg9GWQA4Mhxw9r6Nbxr3/w2jh6K1wx/vVly16fmCLMbXeSvjqPY6uMT1J50erVi+E0nF68enVfJVwJqydMnTKB3kq34hFe3aM/cFKIcXQ+r84sxsXHZx0Bb5CtJyms7kgrE8xiTUDQ4oBggjUEbYkM3vs5c8QGJXS+KZEiDzynnBQA5vKW3P3zXdsv6Vj2ejus+X3oujPkOo028mbd/b9vp7bwasB73bc9sow3raVn6Mk9yxBy4DlP0Z6Twgm6l7Vp4nbvlAlw5QfwMX8DvMEauDf1Lm/4191LeBNf7Zm7nIMxCAy09DgU7H/mxsP6GQGVUS8kNdpLezVI8h0k5QvONZYnvXbL1wXOf4eB9PWKSa2vt69XE5N8JybVC841lofJqJbWKxbEsxiLHrJVGmJ+fcVNZT3IsAqRSo70O3Mj534y0QFH07GnPQYINEwhOM+mAV/TwUfPofDMCEX7EXTxrzfFTRABj5mN8wYoRd6wgxjZfLXgH8jFoBJafpD6qf8gLRfGPfecdC09kPoMxtHnBAe0geBIfcawRecLGnZtFp/tCLxB5gRHra9pfUQTccIoDDApc7ineqGXJs/xY8YXjNyfYgT8M3kYi0jhT8TfaUzz8KRetmNVJRLvv16lF58zkDzGdIwCm90OHIoaQfWjPGIf9fZpNClqqSfmClNTe7W5ybkajMf0XAVL79OgF1vO7vXN5fdy2a00f8K3syE2ZkKoVOQ5jPYgDCVT/ElWFegdiDc5OLc5g+ZxMJ6oUO4zhVGNOQFPsiBQBT4zM45QzQLR11DazpLDdPdvj8A2mAwlb6w4S2Y/9AX9hO5/ctXeVfgnZ0JRfgvzD4tkxRv0L/QpesWRJ6Edir54aHafxvNx3U5krMdZ9RXsDSeP/3GhPuE2KU7RFmQW/VOzGDwW9d3KvOiVU7891bq42eHwCd9UrrpiVSX9Xz7vfh+lf4sIs0ZpcxK+5LTueun9UWPHjjp9hM8qiLE1ECwvs25iQ2yI6LyGoQLaLglub3IkQ1BD9PUwaLA7WOODakgQOI1SvCwajv66nf7q1ekPbW0EtAoCsS3jWfATbmi+tsOQV6//dCa7Dr6pC77ijZVQlB4/FupoArQm/PEhJ4UytjDz+LGFM9kFKA+X0lree3osG48Rq8xEiOWBl3F6nFZ2Nw8V83n7A8L4XOM0mQeGcQTXWKpn4qRVOG80dmRhYSntaobtVzNsYDFggjaxZ9WkNNl6jTazM4FsZPMC7lCYbOSRQj32EMFTZVgfi5rRhChgxRfYxXKuOWZOokvokkkzd8K+G1988UZ8s0qYNllzFG/APZOOrtkFWSnni2B4kQWqMTyby/BMPsGmEJIJHyQcMucl9IR2Qj4xN0Vgr9aLY4UyaiD9XIoU4WCx8WJHA/mG6BtwRyPTbSmuCgdwBgsZhO8I4qzOY35uhwkHkTWBeUAcHlMZChiP3jCh6MOf/yxon9aM8P/+4ZtPPTZ/vbyp/rJRf05plvfHTFr45Ap2TSnF809DqzaOfIb+o4qetm9+A8Rbd4GdTrj8jUdG4/OW90f98vI1h7eVgoI3aYrZJCK2VdJ4a9i01FhMY7qeDH9YJ7D2cUn0p3OcQfOkD5/rIzyQkCHNVCFpYH2mcjuzjM1yzg/SB3BI6fVLc3q+CPX0P7BdoxZYIz2UTqzqG46CwYbhn7t7enb3yA/QMsq8pHtSJ/Vjyzx2F8WHHuphWc7jJirnswxfeJjewJkp87g8NJXwCO3n5iMicfqqyIPzBk5Gwl7FdUr63RmmnNCZMknjjvmCoz8dWaszZV39yFzxeLgSQrMRybPPxPII+7jyGPgH6cBRFqOaUUM0qZsDfJ/EyrH7OAj8CdAfpPphn06MJU6bmUbS33qGW5QswJcROkbEicps0RJuz+rqMBpvgrQfi/uYuH9ywOKlqh7a2Lq2KvTiFXtOFkqE22U7yjwbD0WqL9twck9LK5+bmgqqnI41tlsZ/w6yiREMRIeylUERablyoL39s7Yj7bSBnoA3oa3ts/ZjbTP2niV75V3tR/EWjKEN4Ga3juFZW2rHXiAMkIHpLpnRKPVc/4t6RWS9Qtyn+Dv57/KTXNcIWHjMAxKBL6hlOkxn4b/05/IT1EItnTBdg+ncD4kT7HeKpj+Dcx7JLZJaiUynP2cRvjB9OrXIT3TSn+OznfAFt+WTCqsHY3RMQQJCRKo3haymV2a6WEBqk+T5GJYkWT6sixGzcS+BkMSfxhQ2JlO9/bERIlaPRbqiBIs8VLmPyyHgDMWq6fdQttkkzdxL8wRZ4+HexCiyymuMlDEJOEMEPaib8/gCdiJrysX2n48EUbJrUOckuCVIMvYe2xIRm2/geWSAPfh950I/mUplUn3ahYn+4PJMdPn3pHjXCNwPwn0ZrM4XrcpnkIXhmKw7ZPhe940wRwnznvXxaxILztHSs13EW2kc4e9n+BW44P0RpnBtvtiAcsQYM4ThXFEae5GWKZCzMuYFzJSJFh4zjM8VvJ+ZuGd1H0LGD85wpljHYqbP5fQRPFZBYQQwBIKIz/AG8UMfDvJNn91xltzx2U0KBw7uCdePqXfupf/5RSn9N+SW/gKyGU0k+rxX0lYcw+c0ADC0GggCLuhHAQmrx8KaAeWGtxYbpwdTK8qhjVUdo0t1UBCwajp2AXPbMD2CB7d74yFHpSuNEeewp7wfe/R6fF/p6ShNkqmDPqznl8zhSIfO7yhT4N9CMF5l5B48E1va8qhcXyMQI0bgpGWR+8z+ZO6I1B9mCQE6S2AjRHHecY8cKvB9/MZ5Pqx8piZKeXAK7nwx/l0AMKjFPGcZy2bDcpWaYrORvZvF1+nzNj3mJj7iTEM0IatNSzOrWyCa4BaLwk2LZEZ0+4gYDof7DjN/FBMlTZfnM1ha4s4EszQFRMs96lx1LqniKyuqX1EtapARxaAlEJSDzH5MBBNyPCEmHIjKCYdod/gdqh3Hmgu3PazObaS/qWm2b3l7qLPl7S22plr6m8ZPDYZPG6Gutsm25e1h1mFv32pvqoU6dplu4vArnLrV3lxzLqf+gtzsJL6huUbP+qn+4lvfwheXcewmF/gYrGjPn/dVCXAnvwpxv5Ux4AQoF35fIoU3n9qyaYNwaEwf4anUyDEXfWySOrzl1OYxqZEbNrGjcGjDRfyh+JxeKc/YFQiobPaz6S7r3CGlHxgLQhgmTGgklB79qj6532E6mM3uc7Ki8yiTzhLZ1Yyql4kO1Yxb93MunpN9laN/mdP/vUcG5/VwKBFvnmbFkwzeD1h/yORFMmRh4ql/Y6OXmOIKov/bFDLg2xQsLf1tigg8eN7wvZhLBmCu7gRPY10adLFzDAiAp/UZi/tvMqDLqypyPGLvV9C6YpjLMdV4XjGe9G9AcUIaXIX+IoFXG6d+pmj+lQ/2v6hliseHsN2s9f3VuFDuLBfKnZRZpIux+N4IMrcL5U5YrKP9Xtqr7b1I4MK8mL52Bi00rcfOK8/x3V9PMc560RdUqYG89YKCzhw+z448r4zId5ehr1zjrHLw5WoGtOxXCpEYj+j6nvLhFX9Hx13P/Wz2TQsripyFRdERxc53TeaRU76vTkJD4+RVyWGXPDe6oKDEV1LsHVxdNazBW2q1VUfT3xnoNq8u1eynotwwRwXH3BPUjcPmhhMX5GUZjSxvCkdeIsxhz/Iy5kPdzJ+R8YMwpmMmdnwigoZBxIJb0Oe3oGUXKWZJhVGNFHt5J3TQ/3e8Ukt93sl9kVrnUDyTeV24H5NnTKf5mo6Kc+db5Sq2ksEs0BbBXgaJFnChtsbKrx/bFLzxhZfHPvDA2Jef31jRPBZF9rKRv3rzvpbBI++9d+TglvveenUk9zMsghPqTsWNM1j/0oz5v0RQLaKDObSDwtLj9AjUHD8iHTl+5MhxqDnT/Q2Qb+SGbcihG7ZBA7y5jb5J39wGb9KyFom0MJuM26dpP1ARW/0xCjFUtGjFXRQQHTsXwK47iRREFZGHgqvnvO4xpt91F63MYYR583CHVPZcDu7T73f6XlyP0h+uh+2Hy0/9XyVr5DvKLPuBMi2o/oPqD5XaB6/Nojv2d/1QySg+r3WxTAxF0zIqox7Dck1GgQUtmIKowpg/zSRwrycDYJGgHtrR9uLCsxyP5STzjtJeLsLsYz16bEfbOKrp5+l4CR3X83iM+MC3yhe8i3zH8+d8DyLrk4wu8vLgKNFnCvMAC44eEhfyUSvb21eOGr2sJdLg8zVEWpaN5leA95SMM49ZpGwT+1MDMI7zo2zmpYE0iPMSWby2J8iX6oF7RhhwSxqbWA31q1JklT9SxMy8FFePUvqThPatiZ6e8lmXhrWB3In7Gi4cUhbg6MbOkT0x/tmiwg3hPr7ffArspzazVVLkHdJ5Y6jpkbWapn/fwHSxPB3bUECcPP7Yw1FSUW08BMXnYa44BqGVUKQnfaiTFn+1cuW8Scvn/eVXdDKQ6xfOrKu7fM32y+a+q2ijRv5k8Y15atFNK+9/Rnh+yOjW0lLaQo+Nn3QbSfvRiZxZH/aJEdWTiFh8CY88Q/tSq6DJCnZA85IbVFxzpn3eGucW2QyDWD9nAkvAFGSBpZxdwP60PkbB7T3LsVLS6UrfO0KyNzUX3ExAjP1x44w3GEkOj9+24Qii7reYPBb24QSTtkEAumdY9RsBTXpNN25A+5aPme5uAd3FrH2rcSKM53KaGFMsPeN4YSMMGmdRGjczmLNNO19Pmsl/na/DHEFFHcrDR4OJGiEfaoShqmMolEGgBvKl4FBwJIJDhUBQdeBfvsgy4SnqugTCM8+YyBfK8BomyiAfEmoZqIl8Q7ASTxwJfKHkUGtkhYWfOmrkoQIS56ECPi2pmFXENzryUeouVJF5opglm1wCeQ2SbUq+r6iwPloRBJBlR64l1x8oHu4szHXIeaUOZ6RQzK0xFNoq8setlqweyWZoHt+sFOSE7O6RrqXz338qUOv21biUkuza9vJEbrDYa/F4jKXZ1vb4YDkvO1TgLMvzObPcTkNhKFinlDbmDwpWocFoAIOcJYPT9aMPNklZ2cPdWWqewZBvzW0OCvmWEXVeo8FjqKktExwl4Ypyk+CRBl+kuP8jKRZk2H0Tfv90VqTIYLGJpXF3QjX78qxOH2Sp/qzmuKwKdl+2scIp2p1Ge/b6dsEkZwnGLF9ps8dmNRlM4L8ZcgwGRTWLDrnINjjfXOINOEzmrITVYs8xFagWi5xvslgLnc3O2opKt6vSaTRPrC1oNWWZchzloQVT76Bnny3PuWVoa31JQaxFzjaquebiItXutch1xoJsydI4bERZl+wwORWuQ/eKbnWulPFBXsTj+/m875c33PDLG0Rx4EE6cQM/DvhLf1PI/C69DNVR5g3kG03sFfv9NXhiYHOFxEwg9iLq9yXZM1KSr2XhdeQa/KqB9CW5HyeZXucSOH9hl/V3DvQBVJBaUq9/C65HLiEn8+jfhKe//jEhY4sPgfSl8vSEl9LEDpGmkX/pfZY0jmK2cGPg6pu6d/B0n74WKbSnA0ZGrfE+yPRGtyb5vGtHMuQLdbY6qH30ju4HvWtG4QU7z7s/Q5iVftvi/P9XIK1LMos7mW/kgejapI8wA15EBU75FZGBBLOccKMkkwLOw/Q0x7cExwCN5OrrIUYRbWIItkh8xdTnDUIsGFDyQWGxXA7d3VgG51w0BD7DAv/t94MfeJSf+Os4tiNODySdXf5x/m5/vqDl+zGV70xqT8cCgZhf1agDaWeuvzsA5aJsGz1l42kaG9feHYc2LenMx8z6U92Y6nImU//Bh/wxQgZ+pzmCjCMdZDZZyNeM0jGBLZBgQYEeU/8VFmPLhnfABf6J4LnRZl4fPGZAvT/y54Kj2j/U7bH0sI9qPIsaL51kqznpJAuiSeli0Jc2084/zNHHnQvCg0iqPkqfj1zrBV977MG0nODpg3tOQkZsUJLoRyf3pNXK6fYBxnB7RnYE7JOTalLp5etpRF+XjxgFEdmugy2PZuas/Kivp1XMFuiqszqTpMf+OppHBuBPX4iSV8dahL4TApceNAenr97GXGLsXPhpegVPgBU4p+7EOeXhay0OHh2QcIHD5ItFYgM62Rax+UwtkOlmmd61mD5IF9IHF9816vXVmpbuO01b/Tr9sd5Nh2c+9ut3Hp3ZtsgC/9EePNcLD2o023KZmEo3WkjLBCETUB50j1cl+57aXAqsrUMgGmRLfOVBpf+COREI+nRvWDQRMPFa4k2X4G4RWFwcOytQ7TY//wSVO8vyBJUvEryX6501PxANXD+Lfr3zJ/Q/M2/AkwUzPXnvsbu9pffj6WWPfwHSF49fhsldJSltZ2rIrH9t6nrijqaKLb/kiwrD2hbTs1v5+5LHH1t3y+Z1jx/Tz7YCLB7bilkmzT0Mgn7tenwVvvJ6/YyePdzVqf1887zlka7krFsmZHxd2oC1bMGTRgtZ0116bN4zniJxxsDGkDIEgH4OwLiNPWLyVgHJQivB6lDtxCG/df99R+gV9Cn6lzdWCKT7pUUQPiRGIpSseANKYDJsO/LF8Zeeof+YwuvwBspCI/9/Nkp53BnnipxEWxMRRWDu1YAQjLjAHZcm7enpmRidGXmh1/rVM2fJM19Zex3vQ/ExUeuZKJCJPZGZUUomFRykXw6iX0LBICg4uPngwXRMs4gtHbimJpP0mtq5b9QdGQ8Od3yaBqbVdJ8M2HMCldkz6vRd1yH9XMZO4P2dnfluTv+xcAGGt8yXzoi1nmL9zb/ZI7xuRraKBqJHFv345xFRifHIBY9E1tKtULUW7ejoOqiiW9ceFZ5Ivf9+6njq+Pup94Un5E/oT35H93z4Icz7nYhmCP1R6ka4ha4VfgQ3Zv5PgUwZmXgITzGgCT/gJUePork/4MH0YtzA+uUPfFrklbzwHUczVbz4ZbSC1Q8Wp2P3uK1mR4ZfyfxPRpQutprNcdrDo82Z3KmBIMIyuwvhhN3BfNYKH9Oz3OzqZoPBE7PGDJp+wx591beP6GeUcWMOZFwtA0n/hyxN18zv0q9TnoYLvz8MoCE/47uiNvkn5QEP/2KAfy4QcTvsCd0cKfcNuByWHHZLmC0k6zf457L9dzLf9w/85EhcYfeYzB/T3//0ydqyImHwjo1gfNN2RemgQRvp/qeferZ+UKnRt/Wen0Kgp0RzBApr7qRXH/77oeLyunJDYM+bv4S564ou/IiJl3JmsbuwsCj75gpj1OExlK3L+2JQaa1j0rS6/CbXoGz/+OEFaBkGChPO6Z0JQ6W3PJxVOXFM3oD+EHnEaBGTaB//Txb4grvoy7ANWwIldJdQsqvvUmUIraYPfP4XSpSFp8/ApZ/B4/LjtBqOsg2OnXmJDmckQ3orNVyceWbH0aMca9L+ovQa8kCLkqlg3ag5L/qSmzNs9vErfP//ATHKtuMAAHjajZA9TgMxEIWfyY9EhBBFDuAKhSKON0m10EUKUgRt+vx4ky3wRruOktByFlpKuAT0nICOO/DWsUBICFhrPd+8Gc+MDeAYDxDYfxe4DSzQwEvgA9TxFriCU3EeuIqG2Aau4UTcB65Tf2amqB7S2/pTJQs08RT4AEd4DVzBFd4DV9EU08A1SHEXuE79EQPkMJjAcZ9DYood9xEy+pa0QcrYkjSkZsmlzbFgXKILBU3bYobjWiFGhysJuclnrkJBT1E11M+AQW4mzszldCdHmbFyk7qlHGbWDbN8YWRXadlaOreKO52EalKqqkiUNY6nL/14hsVTzHyzgqKxJk9nmSVf+/ukWOOGjpmna9rfrhDz/6nqPtJDGxHz2szXpD6LfZs1ll/d6fTakW53ddT/x6hjHywYzvyTa99BeVtOhrHJizSzUutIaa3l3zU/ABw5cLgAAAB42l3SZ5MVVRSF4fuOBEmCiZyDiInb5+zTPYOkgWEIEpUgQUkShpyVoCA5Jy3/LlBz3/ED/WVVdVU/1XvVanW1Bp83rdbRd0Hr/ee/wbdddPEBwxjOCEbyIaMYzRjGMo6PGM8EPuYTPuUzPmcik5jMFKYyjenMYCazmM0c5jKP+SzgCxbyJYv4iq/5hm/5jsW0qUhkgkJNQzc9LOF7lrKM5axgJb2sYjV9rKGftaxjPRv4gY1sYjNb2Mo2fuQntrODneziZ3azh73s4xd+ZT8HOMghDvMbRzjKMY4zwAlOcorTnOEs5zjPBS5yictc4Xf+4CrXuM4N/uQvbnKLv7nNHe5yj/s84CGPeMwTnvKM57zgJa94zT/8O/LymYH+qt02KzOZ2QyzmLXZmN1mz2AmvaSX9JJe0kt6SS/pJb005FV6lV6lV+lVepVepVfpVXqVXtJLekkv6SW9pJc6Xvau7F3Zu7J3Ze/K3pXbQ981Zuc/Qid0Qid0Qid0Qid04n+nc0/YT9hP2E/YT9hP2E/YT9hP2E/YT9hP2E/YT9hP2E/YT9hPJL2kl/SyXtbLelkv62W9rJf1sl7WC73QC73QC73QC73QC73QK3pFr+gVvaJX9Ipe0St6Ra/Wq/VqvVqv1qv1ar1ar9ar9Rq9Rq/Ra/QavUav6XjFnRV3VtxZcWfFnRV3VtpD3zVmt9lj9pqrzNVmn7nG7O+kuyzusrjL4i6LuyzusrjLUjVvAQpVcTgAAAAAAAAB//8AAnjaY2BgYGQAgjO2i86D6AshzNIwGgBAmQUAAAA=) format('woff'), + url('Genericons.ttf') format('truetype'), + url('Genericons.svg#genericonsregular') format('svg'); + font-weight: normal; + font-style: normal; +} + +@media screen and (-webkit-min-device-pixel-ratio:0) { + @font-face { + font-family: "Genericons"; + src: url("./Genericons.svg#Genericons") format("svg"); + } +} + + +/** + * All Genericons + */ + +.genericon { + font-size: 16px; + vertical-align: top; + text-align: center; + -moz-transition: color .1s ease-in 0; + -webkit-transition: color .1s ease-in 0; + display: inline-block; + font-family: "Genericons"; + font-style: normal; + font-weight: normal; + font-variant: normal; + line-height: 1; + text-decoration: inherit; + text-transform: none; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + speak: none; +} + + +/** + * Individual icons + */ + +.genericon-404:before { content: "\f423"; } +.genericon-activity:before { content: "\f508"; } +.genericon-anchor:before { content: "\f509"; } +.genericon-aside:before { content: "\f101"; } +.genericon-attachment:before { content: "\f416"; } +.genericon-audio:before { content: "\f109"; } +.genericon-bold:before { content: "\f471"; } +.genericon-book:before { content: "\f444"; } +.genericon-bug:before { content: "\f50a"; } +.genericon-cart:before { content: "\f447"; } +.genericon-category:before { content: "\f301"; } +.genericon-chat:before { content: "\f108"; } +.genericon-checkmark:before { content: "\f418"; } +.genericon-close:before { content: "\f405"; } +.genericon-close-alt:before { content: "\f406"; } +.genericon-cloud:before { content: "\f426"; } +.genericon-cloud-download:before { content: "\f440"; } +.genericon-cloud-upload:before { content: "\f441"; } +.genericon-code:before { content: "\f462"; } +.genericon-codepen:before { content: "\f216"; } +.genericon-cog:before { content: "\f445"; } +.genericon-collapse:before { content: "\f432"; } +.genericon-comment:before { content: "\f300"; } +.genericon-day:before { content: "\f305"; } +.genericon-digg:before { content: "\f221"; } +.genericon-document:before { content: "\f443"; } +.genericon-dot:before { content: "\f428"; } +.genericon-downarrow:before { content: "\f502"; } +.genericon-download:before { content: "\f50b"; } +.genericon-draggable:before { content: "\f436"; } +.genericon-dribbble:before { content: "\f201"; } +.genericon-dropbox:before { content: "\f225"; } +.genericon-dropdown:before { content: "\f433"; } +.genericon-dropdown-left:before { content: "\f434"; } +.genericon-edit:before { content: "\f411"; } +.genericon-ellipsis:before { content: "\f476"; } +.genericon-expand:before { content: "\f431"; } +.genericon-external:before { content: "\f442"; } +.genericon-facebook:before { content: "\f203"; } +.genericon-facebook-alt:before { content: "\f204"; } +.genericon-fastforward:before { content: "\f458"; } +.genericon-feed:before { content: "\f413"; } +.genericon-flag:before { content: "\f468"; } +.genericon-flickr:before { content: "\f211"; } +.genericon-foursquare:before { content: "\f226"; } +.genericon-fullscreen:before { content: "\f474"; } +.genericon-gallery:before { content: "\f103"; } +.genericon-github:before { content: "\f200"; } +.genericon-googleplus:before { content: "\f206"; } +.genericon-googleplus-alt:before { content: "\f218"; } +.genericon-handset:before { content: "\f50c"; } +.genericon-heart:before { content: "\f461"; } +.genericon-help:before { content: "\f457"; } +.genericon-hide:before { content: "\f404"; } +.genericon-hierarchy:before { content: "\f505"; } +.genericon-home:before { content: "\f409"; } +.genericon-image:before { content: "\f102"; } +.genericon-info:before { content: "\f455"; } +.genericon-instagram:before { content: "\f215"; } +.genericon-italic:before { content: "\f472"; } +.genericon-key:before { content: "\f427"; } +.genericon-leftarrow:before { content: "\f503"; } +.genericon-link:before { content: "\f107"; } +.genericon-linkedin:before { content: "\f207"; } +.genericon-linkedin-alt:before { content: "\f208"; } +.genericon-location:before { content: "\f417"; } +.genericon-lock:before { content: "\f470"; } +.genericon-mail:before { content: "\f410"; } +.genericon-maximize:before { content: "\f422"; } +.genericon-menu:before { content: "\f419"; } +.genericon-microphone:before { content: "\f50d"; } +.genericon-minimize:before { content: "\f421"; } +.genericon-minus:before { content: "\f50e"; } +.genericon-month:before { content: "\f307"; } +.genericon-move:before { content: "\f50f"; } +.genericon-next:before { content: "\f429"; } +.genericon-notice:before { content: "\f456"; } +.genericon-paintbrush:before { content: "\f506"; } +.genericon-path:before { content: "\f219"; } +.genericon-pause:before { content: "\f448"; } +.genericon-phone:before { content: "\f437"; } +.genericon-picture:before { content: "\f473"; } +.genericon-pinned:before { content: "\f308"; } +.genericon-pinterest:before { content: "\f209"; } +.genericon-pinterest-alt:before { content: "\f210"; } +.genericon-play:before { content: "\f452"; } +.genericon-plugin:before { content: "\f439"; } +.genericon-plus:before { content: "\f510"; } +.genericon-pocket:before { content: "\f224"; } +.genericon-polldaddy:before { content: "\f217"; } +.genericon-portfolio:before { content: "\f460"; } +.genericon-previous:before { content: "\f430"; } +.genericon-print:before { content: "\f469"; } +.genericon-quote:before { content: "\f106"; } +.genericon-rating-empty:before { content: "\f511"; } +.genericon-rating-full:before { content: "\f512"; } +.genericon-rating-half:before { content: "\f513"; } +.genericon-reddit:before { content: "\f222"; } +.genericon-refresh:before { content: "\f420"; } +.genericon-reply:before { content: "\f412"; } +.genericon-reply-alt:before { content: "\f466"; } +.genericon-reply-single:before { content: "\f467"; } +.genericon-rewind:before { content: "\f459"; } +.genericon-rightarrow:before { content: "\f501"; } +.genericon-search:before { content: "\f400"; } +.genericon-send-to-phone:before { content: "\f438"; } +.genericon-send-to-tablet:before { content: "\f454"; } +.genericon-share:before { content: "\f415"; } +.genericon-show:before { content: "\f403"; } +.genericon-shuffle:before { content: "\f514"; } +.genericon-sitemap:before { content: "\f507"; } +.genericon-skip-ahead:before { content: "\f451"; } +.genericon-skip-back:before { content: "\f450"; } +.genericon-skype:before { content: "\f220"; } +.genericon-spam:before { content: "\f424"; } +.genericon-spotify:before { content: "\f515"; } +.genericon-standard:before { content: "\f100"; } +.genericon-star:before { content: "\f408"; } +.genericon-status:before { content: "\f105"; } +.genericon-stop:before { content: "\f449"; } +.genericon-stumbleupon:before { content: "\f223"; } +.genericon-subscribe:before { content: "\f463"; } +.genericon-subscribed:before { content: "\f465"; } +.genericon-summary:before { content: "\f425"; } +.genericon-tablet:before { content: "\f453"; } +.genericon-tag:before { content: "\f302"; } +.genericon-time:before { content: "\f303"; } +.genericon-top:before { content: "\f435"; } +.genericon-trash:before { content: "\f407"; } +.genericon-tumblr:before { content: "\f214"; } +.genericon-twitch:before { content: "\f516"; } +.genericon-twitter:before { content: "\f202"; } +.genericon-unapprove:before { content: "\f446"; } +.genericon-unsubscribe:before { content: "\f464"; } +.genericon-unzoom:before { content: "\f401"; } +.genericon-uparrow:before { content: "\f500"; } +.genericon-user:before { content: "\f304"; } +.genericon-video:before { content: "\f104"; } +.genericon-videocamera:before { content: "\f517"; } +.genericon-vimeo:before { content: "\f212"; } +.genericon-warning:before { content: "\f414"; } +.genericon-website:before { content: "\f475"; } +.genericon-week:before { content: "\f306"; } +.genericon-wordpress:before { content: "\f205"; } +.genericon-xpost:before { content: "\f504"; } +.genericon-youtube:before { content: "\f213"; } +.genericon-zoom:before { content: "\f402"; } diff --git a/wp-content/themes/twentyfifteen/header.php b/wp-content/themes/twentyfifteen/header.php new file mode 100644 index 0000000..d8e387f --- /dev/null +++ b/wp-content/themes/twentyfifteen/header.php @@ -0,0 +1,51 @@ + + class="no-js"> + + + + + + + + + + +> +
      + + + + +
      diff --git a/wp-content/themes/twentyfifteen/image.php b/wp-content/themes/twentyfifteen/image.php new file mode 100644 index 0000000..5a471d4 --- /dev/null +++ b/wp-content/themes/twentyfifteen/image.php @@ -0,0 +1,94 @@ + + +
      +
      + + + +
      > + + + +
      + ', '' ); ?> +
      + +
      + +
      + + + +
      + +
      + + +
      + + '', + 'link_before' => '', + 'link_after' => '', + 'pagelink' => '' . __( 'Page', 'twentyfifteen' ) . ' %', + 'separator' => ', ', + ) ); + ?> +
      + +
      + + ', '' ); ?> +
      + +
      + + _x( 'Published in%title', 'Parent post link', 'twentyfifteen' ), + ) ); + + // End the loop. + endwhile; + ?> + +
      +
      + + diff --git a/wp-content/themes/twentyfifteen/inc/back-compat.php b/wp-content/themes/twentyfifteen/inc/back-compat.php new file mode 100644 index 0000000..73cd44d --- /dev/null +++ b/wp-content/themes/twentyfifteen/inc/back-compat.php @@ -0,0 +1,63 @@ +

      %s

      ', $message ); +} + +/** + * Prevent the Customizer from being loaded on WordPress versions prior to 4.1. + * + * @since Twenty Fifteen 1.0 + */ +function twentyfifteen_customize() { + wp_die( sprintf( __( 'Twenty Fifteen requires at least WordPress version 4.1. You are running version %s. Please upgrade and try again.', 'twentyfifteen' ), $GLOBALS['wp_version'] ), '', array( + 'back_link' => true, + ) ); +} +add_action( 'load-customize.php', 'twentyfifteen_customize' ); + +/** + * Prevent the Theme Preview from being loaded on WordPress versions prior to 4.1. + * + * @since Twenty Fifteen 1.0 + */ +function twentyfifteen_preview() { + if ( isset( $_GET['preview'] ) ) { + wp_die( sprintf( __( 'Twenty Fifteen requires at least WordPress version 4.1. You are running version %s. Please upgrade and try again.', 'twentyfifteen' ), $GLOBALS['wp_version'] ) ); + } +} +add_action( 'template_redirect', 'twentyfifteen_preview' ); diff --git a/wp-content/themes/twentyfifteen/inc/custom-header.php b/wp-content/themes/twentyfifteen/inc/custom-header.php new file mode 100644 index 0000000..34994a9 --- /dev/null +++ b/wp-content/themes/twentyfifteen/inc/custom-header.php @@ -0,0 +1,356 @@ + $default_text_color, + 'width' => 954, + 'height' => 1300, + 'wp-head-callback' => 'twentyfifteen_header_style', + ) ) ); +} +add_action( 'after_setup_theme', 'twentyfifteen_custom_header_setup' ); + +/** + * Convert HEX to RGB. + * + * @since Twenty Fifteen 1.0 + * + * @param string $color The original color, in 3- or 6-digit hexadecimal form. + * @return array Array containing RGB (red, green, and blue) values for the given + * HEX code, empty array otherwise. + */ +function twentyfifteen_hex2rgb( $color ) { + $color = trim( $color, '#' ); + + if ( strlen( $color ) == 3 ) { + $r = hexdec( substr( $color, 0, 1 ).substr( $color, 0, 1 ) ); + $g = hexdec( substr( $color, 1, 1 ).substr( $color, 1, 1 ) ); + $b = hexdec( substr( $color, 2, 1 ).substr( $color, 2, 1 ) ); + } else if ( strlen( $color ) == 6 ) { + $r = hexdec( substr( $color, 0, 2 ) ); + $g = hexdec( substr( $color, 2, 2 ) ); + $b = hexdec( substr( $color, 4, 2 ) ); + } else { + return array(); + } + + return array( 'red' => $r, 'green' => $g, 'blue' => $b ); +} + +if ( ! function_exists( 'twentyfifteen_header_style' ) ) : +/** + * Styles the header image and text displayed on the blog. + * + * @since Twenty Fifteen 1.0 + * + * @see twentyfifteen_custom_header_setup() + */ +function twentyfifteen_header_style() { + $header_image = get_header_image(); + + // If no custom options for text are set, let's bail. + if ( empty( $header_image ) && display_header_text() ) { + return; + } + + // If we get this far, we have custom styles. Let's do this. + ?> + + get_setting( 'blogname' )->transport = 'postMessage'; + $wp_customize->get_setting( 'blogdescription' )->transport = 'postMessage'; + + // Add color scheme setting and control. + $wp_customize->add_setting( 'color_scheme', array( + 'default' => 'default', + 'sanitize_callback' => 'twentyfifteen_sanitize_color_scheme', + 'transport' => 'postMessage', + ) ); + + $wp_customize->add_control( 'color_scheme', array( + 'label' => __( 'Base Color Scheme', 'twentyfifteen' ), + 'section' => 'colors', + 'type' => 'select', + 'choices' => twentyfifteen_get_color_scheme_choices(), + 'priority' => 1, + ) ); + + // Add custom header and sidebar text color setting and control. + $wp_customize->add_setting( 'sidebar_textcolor', array( + 'default' => $color_scheme[4], + 'sanitize_callback' => 'sanitize_hex_color', + 'transport' => 'postMessage', + ) ); + + $wp_customize->add_control( new WP_Customize_Color_Control( $wp_customize, 'sidebar_textcolor', array( + 'label' => __( 'Header and Sidebar Text Color', 'twentyfifteen' ), + 'description' => __( 'Applied to the header on small screens and the sidebar on wide screens.', 'twentyfifteen' ), + 'section' => 'colors', + ) ) ); + + // Remove the core header textcolor control, as it shares the sidebar text color. + $wp_customize->remove_control( 'header_textcolor' ); + + // Add custom header and sidebar background color setting and control. + $wp_customize->add_setting( 'header_background_color', array( + 'default' => $color_scheme[1], + 'sanitize_callback' => 'sanitize_hex_color', + 'transport' => 'postMessage', + ) ); + + $wp_customize->add_control( new WP_Customize_Color_Control( $wp_customize, 'header_background_color', array( + 'label' => __( 'Header and Sidebar Background Color', 'twentyfifteen' ), + 'description' => __( 'Applied to the header on small screens and the sidebar on wide screens.', 'twentyfifteen' ), + 'section' => 'colors', + ) ) ); + + // Add an additional description to the header image section. + $wp_customize->get_section( 'header_image' )->description = __( 'Applied to the header on small screens and the sidebar on wide screens.', 'twentyfifteen' ); +} +add_action( 'customize_register', 'twentyfifteen_customize_register', 11 ); + +/** + * Register color schemes for Twenty Fifteen. + * + * Can be filtered with {@see 'twentyfifteen_color_schemes'}. + * + * The order of colors in a colors array: + * 1. Main Background Color. + * 2. Sidebar Background Color. + * 3. Box Background Color. + * 4. Main Text and Link Color. + * 5. Sidebar Text and Link Color. + * 6. Meta Box Background Color. + * + * @since Twenty Fifteen 1.0 + * + * @return array An associative array of color scheme options. + */ +function twentyfifteen_get_color_schemes() { + return apply_filters( 'twentyfifteen_color_schemes', array( + 'default' => array( + 'label' => __( 'Default', 'twentyfifteen' ), + 'colors' => array( + '#f1f1f1', + '#ffffff', + '#ffffff', + '#333333', + '#333333', + '#f7f7f7', + ), + ), + 'dark' => array( + 'label' => __( 'Dark', 'twentyfifteen' ), + 'colors' => array( + '#111111', + '#202020', + '#202020', + '#bebebe', + '#bebebe', + '#1b1b1b', + ), + ), + 'yellow' => array( + 'label' => __( 'Yellow', 'twentyfifteen' ), + 'colors' => array( + '#f4ca16', + '#ffdf00', + '#ffffff', + '#111111', + '#111111', + '#f1f1f1', + ), + ), + 'pink' => array( + 'label' => __( 'Pink', 'twentyfifteen' ), + 'colors' => array( + '#ffe5d1', + '#e53b51', + '#ffffff', + '#352712', + '#ffffff', + '#f1f1f1', + ), + ), + 'purple' => array( + 'label' => __( 'Purple', 'twentyfifteen' ), + 'colors' => array( + '#674970', + '#2e2256', + '#ffffff', + '#2e2256', + '#ffffff', + '#f1f1f1', + ), + ), + 'blue' => array( + 'label' => __( 'Blue', 'twentyfifteen' ), + 'colors' => array( + '#e9f2f9', + '#55c3dc', + '#ffffff', + '#22313f', + '#ffffff', + '#f1f1f1', + ), + ), + ) ); +} + +if ( ! function_exists( 'twentyfifteen_get_color_scheme' ) ) : +/** + * Get the current Twenty Fifteen color scheme. + * + * @since Twenty Fifteen 1.0 + * + * @return array An associative array of either the current or default color scheme hex values. + */ +function twentyfifteen_get_color_scheme() { + $color_scheme_option = get_theme_mod( 'color_scheme', 'default' ); + $color_schemes = twentyfifteen_get_color_schemes(); + + if ( array_key_exists( $color_scheme_option, $color_schemes ) ) { + return $color_schemes[ $color_scheme_option ]['colors']; + } + + return $color_schemes['default']['colors']; +} +endif; // twentyfifteen_get_color_scheme + +if ( ! function_exists( 'twentyfifteen_get_color_scheme_choices' ) ) : +/** + * Returns an array of color scheme choices registered for Twenty Fifteen. + * + * @since Twenty Fifteen 1.0 + * + * @return array Array of color schemes. + */ +function twentyfifteen_get_color_scheme_choices() { + $color_schemes = twentyfifteen_get_color_schemes(); + $color_scheme_control_options = array(); + + foreach ( $color_schemes as $color_scheme => $value ) { + $color_scheme_control_options[ $color_scheme ] = $value['label']; + } + + return $color_scheme_control_options; +} +endif; // twentyfifteen_get_color_scheme_choices + +if ( ! function_exists( 'twentyfifteen_sanitize_color_scheme' ) ) : +/** + * Sanitization callback for color schemes. + * + * @since Twenty Fifteen 1.0 + * + * @param string $value Color scheme name value. + * @return string Color scheme name. + */ +function twentyfifteen_sanitize_color_scheme( $value ) { + $color_schemes = twentyfifteen_get_color_scheme_choices(); + + if ( ! array_key_exists( $value, $color_schemes ) ) { + $value = 'default'; + } + + return $value; +} +endif; // twentyfifteen_sanitize_color_scheme + +/** + * Enqueues front-end CSS for color scheme. + * + * @since Twenty Fifteen 1.0 + * + * @see wp_add_inline_style() + */ +function twentyfifteen_color_scheme_css() { + $color_scheme_option = get_theme_mod( 'color_scheme', 'default' ); + + // Don't do anything if the default color scheme is selected. + if ( 'default' === $color_scheme_option ) { + return; + } + + $color_scheme = twentyfifteen_get_color_scheme(); + + // Convert main and sidebar text hex color to rgba. + $color_textcolor_rgb = twentyfifteen_hex2rgb( $color_scheme[3] ); + $color_sidebar_textcolor_rgb = twentyfifteen_hex2rgb( $color_scheme[4] ); + $colors = array( + 'background_color' => $color_scheme[0], + 'header_background_color' => $color_scheme[1], + 'box_background_color' => $color_scheme[2], + 'textcolor' => $color_scheme[3], + 'secondary_textcolor' => vsprintf( 'rgba( %1$s, %2$s, %3$s, 0.7)', $color_textcolor_rgb ), + 'border_color' => vsprintf( 'rgba( %1$s, %2$s, %3$s, 0.1)', $color_textcolor_rgb ), + 'border_focus_color' => vsprintf( 'rgba( %1$s, %2$s, %3$s, 0.3)', $color_textcolor_rgb ), + 'sidebar_textcolor' => $color_scheme[4], + 'sidebar_border_color' => vsprintf( 'rgba( %1$s, %2$s, %3$s, 0.1)', $color_sidebar_textcolor_rgb ), + 'sidebar_border_focus_color' => vsprintf( 'rgba( %1$s, %2$s, %3$s, 0.3)', $color_sidebar_textcolor_rgb ), + 'secondary_sidebar_textcolor' => vsprintf( 'rgba( %1$s, %2$s, %3$s, 0.7)', $color_sidebar_textcolor_rgb ), + 'meta_box_background_color' => $color_scheme[5], + ); + + $color_scheme_css = twentyfifteen_get_color_scheme_css( $colors ); + + wp_add_inline_style( 'twentyfifteen-style', $color_scheme_css ); +} +add_action( 'wp_enqueue_scripts', 'twentyfifteen_color_scheme_css' ); + +/** + * Binds JS listener to make Customizer color_scheme control. + * + * Passes color scheme data as colorScheme global. + * + * @since Twenty Fifteen 1.0 + */ +function twentyfifteen_customize_control_js() { + wp_enqueue_script( 'color-scheme-control', get_template_directory_uri() . '/js/color-scheme-control.js', array( 'customize-controls', 'iris', 'underscore', 'wp-util' ), '20141216', true ); + wp_localize_script( 'color-scheme-control', 'colorScheme', twentyfifteen_get_color_schemes() ); +} +add_action( 'customize_controls_enqueue_scripts', 'twentyfifteen_customize_control_js' ); + +/** + * Binds JS handlers to make the Customizer preview reload changes asynchronously. + * + * @since Twenty Fifteen 1.0 + */ +function twentyfifteen_customize_preview_js() { + wp_enqueue_script( 'twentyfifteen-customize-preview', get_template_directory_uri() . '/js/customize-preview.js', array( 'customize-preview' ), '20141216', true ); +} +add_action( 'customize_preview_init', 'twentyfifteen_customize_preview_js' ); + +/** + * Returns CSS for the color schemes. + * + * @since Twenty Fifteen 1.0 + * + * @param array $colors Color scheme colors. + * @return string Color scheme CSS. + */ +function twentyfifteen_get_color_scheme_css( $colors ) { + $colors = wp_parse_args( $colors, array( + 'background_color' => '', + 'header_background_color' => '', + 'box_background_color' => '', + 'textcolor' => '', + 'secondary_textcolor' => '', + 'border_color' => '', + 'border_focus_color' => '', + 'sidebar_textcolor' => '', + 'sidebar_border_color' => '', + 'sidebar_border_focus_color' => '', + 'secondary_sidebar_textcolor' => '', + 'meta_box_background_color' => '', + ) ); + + $css = << a, + .author-description a, + .taxonomy-description a, + .textwidget a, + .entry-footer a:hover, + .comment-metadata a:hover, + .pingback .edit-link a:hover, + .comment-list .reply a:hover, + .site-info a:hover { + border-color: {$colors['textcolor']}; + } + + /* Secondary Text Color */ + button:hover, + button:focus, + input[type="button"]:hover, + input[type="button"]:focus, + input[type="reset"]:hover, + input[type="reset"]:focus, + input[type="submit"]:hover, + input[type="submit"]:focus, + .pagination .prev:hover, + .pagination .prev:focus, + .pagination .next:hover, + .pagination .next:focus, + .widget_calendar tbody a:hover, + .widget_calendar tbody a:focus, + .page-links a:hover, + .page-links a:focus { + background-color: {$colors['textcolor']}; /* Fallback for IE7 and IE8 */ + background-color: {$colors['secondary_textcolor']}; + } + + /* Secondary Text Color */ + blockquote, + a:hover, + a:focus, + .main-navigation .menu-item-description, + .post-navigation .meta-nav, + .post-navigation a:hover .post-title, + .post-navigation a:focus .post-title, + .image-navigation, + .image-navigation a, + .comment-navigation, + .comment-navigation a, + .widget, + .author-heading, + .entry-footer, + .entry-footer a, + .taxonomy-description, + .page-links > .page-links-title, + .entry-caption, + .comment-author, + .comment-metadata, + .comment-metadata a, + .pingback .edit-link, + .pingback .edit-link a, + .post-password-form label, + .comment-form label, + .comment-notes, + .comment-awaiting-moderation, + .logged-in-as, + .form-allowed-tags, + .no-comments, + .site-info, + .site-info a, + .wp-caption-text, + .gallery-caption, + .comment-list .reply a { + color: {$colors['textcolor']}; /* Fallback for IE7 and IE8 */ + color: {$colors['secondary_textcolor']}; + } + + /* Secondary Text Color */ + blockquote, + .logged-in-as a:hover, + .comment-author a:hover { + border-color: {$colors['textcolor']}; /* Fallback for IE7 and IE8 */ + border-color: {$colors['secondary_textcolor']}; + } + + /* Border Color */ + hr, + .dropdown-toggle:hover, + .dropdown-toggle:focus { + background-color: {$colors['textcolor']}; /* Fallback for IE7 and IE8 */ + background-color: {$colors['border_color']}; + } + + /* Border Color */ + pre, + abbr[title], + table, + th, + td, + input, + textarea, + .main-navigation ul, + .main-navigation li, + .post-navigation, + .post-navigation div + div, + .pagination, + .comment-navigation, + .widget li, + .widget_categories .children, + .widget_nav_menu .sub-menu, + .widget_pages .children, + .site-header, + .site-footer, + .hentry + .hentry, + .author-info, + .entry-content .page-links a, + .page-links > span, + .page-header, + .comments-area, + .comment-list + .comment-respond, + .comment-list article, + .comment-list .pingback, + .comment-list .trackback, + .comment-list .reply a, + .no-comments { + border-color: {$colors['textcolor']}; /* Fallback for IE7 and IE8 */ + border-color: {$colors['border_color']}; + } + + /* Border Focus Color */ + a:focus, + button:focus, + input:focus { + outline-color: {$colors['textcolor']}; /* Fallback for IE7 and IE8 */ + outline-color: {$colors['border_focus_color']}; + } + + input:focus, + textarea:focus { + border-color: {$colors['textcolor']}; /* Fallback for IE7 and IE8 */ + border-color: {$colors['border_focus_color']}; + } + + /* Sidebar Link Color */ + .secondary-toggle:before { + color: {$colors['sidebar_textcolor']}; + } + + .site-title a, + .site-description { + color: {$colors['sidebar_textcolor']}; + } + + /* Sidebar Text Color */ + .site-title a:hover, + .site-title a:focus { + color: {$colors['secondary_sidebar_textcolor']}; + } + + /* Sidebar Border Color */ + .secondary-toggle { + border-color: {$colors['sidebar_textcolor']}; /* Fallback for IE7 and IE8 */ + border-color: {$colors['sidebar_border_color']}; + } + + /* Sidebar Border Focus Color */ + .secondary-toggle:hover, + .secondary-toggle:focus { + border-color: {$colors['sidebar_textcolor']}; /* Fallback for IE7 and IE8 */ + border-color: {$colors['sidebar_border_focus_color']}; + } + + .site-title a { + outline-color: {$colors['sidebar_textcolor']}; /* Fallback for IE7 and IE8 */ + outline-color: {$colors['sidebar_border_focus_color']}; + } + + /* Meta Background Color */ + .entry-footer { + background-color: {$colors['meta_box_background_color']}; + } + + @media screen and (min-width: 38.75em) { + /* Main Text Color */ + .page-header { + border-color: {$colors['textcolor']}; + } + } + + @media screen and (min-width: 59.6875em) { + /* Make sure its transparent on desktop */ + .site-header, + .secondary { + background-color: transparent; + } + + /* Sidebar Background Color */ + .widget button, + .widget input[type="button"], + .widget input[type="reset"], + .widget input[type="submit"], + .widget_calendar tbody a, + .widget_calendar tbody a:hover, + .widget_calendar tbody a:focus { + color: {$colors['header_background_color']}; + } + + /* Sidebar Link Color */ + .secondary a, + .dropdown-toggle:after, + .widget-title, + .widget blockquote cite, + .widget blockquote small { + color: {$colors['sidebar_textcolor']}; + } + + .widget button, + .widget input[type="button"], + .widget input[type="reset"], + .widget input[type="submit"], + .widget_calendar tbody a { + background-color: {$colors['sidebar_textcolor']}; + } + + .textwidget a { + border-color: {$colors['sidebar_textcolor']}; + } + + /* Sidebar Text Color */ + .secondary a:hover, + .secondary a:focus, + .main-navigation .menu-item-description, + .widget, + .widget blockquote, + .widget .wp-caption-text, + .widget .gallery-caption { + color: {$colors['secondary_sidebar_textcolor']}; + } + + .widget button:hover, + .widget button:focus, + .widget input[type="button"]:hover, + .widget input[type="button"]:focus, + .widget input[type="reset"]:hover, + .widget input[type="reset"]:focus, + .widget input[type="submit"]:hover, + .widget input[type="submit"]:focus, + .widget_calendar tbody a:hover, + .widget_calendar tbody a:focus { + background-color: {$colors['secondary_sidebar_textcolor']}; + } + + .widget blockquote { + border-color: {$colors['secondary_sidebar_textcolor']}; + } + + /* Sidebar Border Color */ + .main-navigation ul, + .main-navigation li, + .widget input, + .widget textarea, + .widget table, + .widget th, + .widget td, + .widget pre, + .widget li, + .widget_categories .children, + .widget_nav_menu .sub-menu, + .widget_pages .children, + .widget abbr[title] { + border-color: {$colors['sidebar_border_color']}; + } + + .dropdown-toggle:hover, + .dropdown-toggle:focus, + .widget hr { + background-color: {$colors['sidebar_border_color']}; + } + + .widget input:focus, + .widget textarea:focus { + border-color: {$colors['sidebar_border_focus_color']}; + } + + .sidebar a:focus, + .dropdown-toggle:focus { + outline-color: {$colors['sidebar_border_focus_color']}; + } + } +CSS; + + return $css; +} + +/** + * Output an Underscore template for generating CSS for the color scheme. + * + * The template generates the css dynamically for instant display in the Customizer + * preview. + * + * @since Twenty Fifteen 1.0 + */ +function twentyfifteen_color_scheme_css_template() { + $colors = array( + 'background_color' => '{{ data.background_color }}', + 'header_background_color' => '{{ data.header_background_color }}', + 'box_background_color' => '{{ data.box_background_color }}', + 'textcolor' => '{{ data.textcolor }}', + 'secondary_textcolor' => '{{ data.secondary_textcolor }}', + 'border_color' => '{{ data.border_color }}', + 'border_focus_color' => '{{ data.border_focus_color }}', + 'sidebar_textcolor' => '{{ data.sidebar_textcolor }}', + 'sidebar_border_color' => '{{ data.sidebar_border_color }}', + 'sidebar_border_focus_color' => '{{ data.sidebar_border_focus_color }}', + 'secondary_sidebar_textcolor' => '{{ data.secondary_sidebar_textcolor }}', + 'meta_box_background_color' => '{{ data.meta_box_background_color }}', + ); + ?> + + 1 && get_option( 'page_comments' ) ) : + ?> +
      + + %s
      ', __( 'Featured', 'twentyfifteen' ) ); + } + + $format = get_post_format(); + if ( current_theme_supports( 'post-formats', $format ) ) { + printf( '%1$s%3$s', + sprintf( '%s ', _x( 'Format', 'Used before post format.', 'twentyfifteen' ) ), + esc_url( get_post_format_link( $format ) ), + get_post_format_string( $format ) + ); + } + + if ( in_array( get_post_type(), array( 'post', 'attachment' ) ) ) { + $time_string = ''; + + if ( get_the_time( 'U' ) !== get_the_modified_time( 'U' ) ) { + $time_string = ''; + } + + $time_string = sprintf( $time_string, + esc_attr( get_the_date( 'c' ) ), + get_the_date(), + esc_attr( get_the_modified_date( 'c' ) ), + get_the_modified_date() + ); + + printf( '%1$s %3$s', + _x( 'Posted on', 'Used before publish date.', 'twentyfifteen' ), + esc_url( get_permalink() ), + $time_string + ); + } + + if ( 'post' == get_post_type() ) { + if ( is_singular() || is_multi_author() ) { + printf( '', + _x( 'Author', 'Used before post author name.', 'twentyfifteen' ), + esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ), + get_the_author() + ); + } + + $categories_list = get_the_category_list( _x( ', ', 'Used between list items, there is a space after the comma.', 'twentyfifteen' ) ); + if ( $categories_list && twentyfifteen_categorized_blog() ) { + printf( '%1$s %2$s', + _x( 'Categories', 'Used before category names.', 'twentyfifteen' ), + $categories_list + ); + } + + $tags_list = get_the_tag_list( '', _x( ', ', 'Used between list items, there is a space after the comma.', 'twentyfifteen' ) ); + if ( $tags_list ) { + printf( '%1$s %2$s', + _x( 'Tags', 'Used before tag names.', 'twentyfifteen' ), + $tags_list + ); + } + } + + if ( is_attachment() && wp_attachment_is_image() ) { + // Retrieve attachment metadata. + $metadata = wp_get_attachment_metadata(); + + printf( '%1$s %3$s × %4$s', + _x( 'Full size', 'Used before full size attachment link.', 'twentyfifteen' ), + esc_url( wp_get_attachment_url() ), + $metadata['width'], + $metadata['height'] + ); + } + + if ( ! is_single() && ! post_password_required() && ( comments_open() || get_comments_number() ) ) { + echo ''; + comments_popup_link( __( 'Leave a comment', 'twentyfifteen' ), __( '1 Comment', 'twentyfifteen' ), __( '% Comments', 'twentyfifteen' ) ); + echo ''; + } +} +endif; + +/** + * Determine whether blog/site has more than one category. + * + * @since Twenty Fifteen 1.0 + * + * @return bool True of there is more than one category, false otherwise. + */ +function twentyfifteen_categorized_blog() { + if ( false === ( $all_the_cool_cats = get_transient( 'twentyfifteen_categories' ) ) ) { + // Create an array of all the categories that are attached to posts. + $all_the_cool_cats = get_categories( array( + 'fields' => 'ids', + 'hide_empty' => 1, + + // We only need to know if there is more than one category. + 'number' => 2, + ) ); + + // Count the number of categories that are attached to the posts. + $all_the_cool_cats = count( $all_the_cool_cats ); + + set_transient( 'twentyfifteen_categories', $all_the_cool_cats ); + } + + if ( $all_the_cool_cats > 1 ) { + // This blog has more than 1 category so twentyfifteen_categorized_blog should return true. + return true; + } else { + // This blog has only 1 category so twentyfifteen_categorized_blog should return false. + return false; + } +} + +/** + * Flush out the transients used in {@see twentyfifteen_categorized_blog()}. + * + * @since Twenty Fifteen 1.0 + */ +function twentyfifteen_category_transient_flusher() { + // Like, beat it. Dig? + delete_transient( 'twentyfifteen_categories' ); +} +add_action( 'edit_category', 'twentyfifteen_category_transient_flusher' ); +add_action( 'save_post', 'twentyfifteen_category_transient_flusher' ); + +if ( ! function_exists( 'twentyfifteen_post_thumbnail' ) ) : +/** + * Display an optional post thumbnail. + * + * Wraps the post thumbnail in an anchor element on index views, or a div + * element when on single views. + * + * @since Twenty Fifteen 1.0 + */ +function twentyfifteen_post_thumbnail() { + if ( post_password_required() || is_attachment() || ! has_post_thumbnail() ) { + return; + } + + if ( is_singular() ) : + ?> + +
      + +
      + + + + + + %2$s', + esc_url( get_permalink( get_the_ID() ) ), + /* translators: %s: Name of current post */ + sprintf( __( 'Continue reading %s', 'twentyfifteen' ), '' . get_the_title( get_the_ID() ) . '' ) + ); + return ' … ' . $link; +} +add_filter( 'excerpt_more', 'twentyfifteen_excerpt_more' ); +endif; diff --git a/wp-content/themes/twentyfifteen/index.php b/wp-content/themes/twentyfifteen/index.php new file mode 100644 index 0000000..db77651 --- /dev/null +++ b/wp-content/themes/twentyfifteen/index.php @@ -0,0 +1,61 @@ + + +
      +
      + + + + +
      +

      +
      + + + __( 'Previous page', 'twentyfifteen' ), + 'next_text' => __( 'Next page', 'twentyfifteen' ), + 'before_page_number' => '' . __( 'Page', 'twentyfifteen' ) . ' ', + ) ); + + // If no content, include the "No posts found" template. + else : + get_template_part( 'content', 'none' ); + + endif; + ?> + +
      +
      + + diff --git a/wp-content/themes/twentyfifteen/js/color-scheme-control.js b/wp-content/themes/twentyfifteen/js/color-scheme-control.js new file mode 100644 index 0000000..3563239 --- /dev/null +++ b/wp-content/themes/twentyfifteen/js/color-scheme-control.js @@ -0,0 +1,78 @@ +/* global colorScheme, Color */ +/** + * Add a listener to the Color Scheme control to update other color controls to new values/defaults. + * Also trigger an update of the Color Scheme CSS when a color is changed. + */ + +( function( api ) { + var cssTemplate = wp.template( 'twentyfifteen-color-scheme' ), + colorSchemeKeys = [ + 'background_color', + 'header_background_color', + 'box_background_color', + 'textcolor', + 'sidebar_textcolor', + 'meta_box_background_color' + ], + colorSettings = [ + 'background_color', + 'header_background_color', + 'sidebar_textcolor' + ]; + + api.controlConstructor.select = api.Control.extend( { + ready: function() { + if ( 'color_scheme' === this.id ) { + this.setting.bind( 'change', function( value ) { + // Update Background Color. + api( 'background_color' ).set( colorScheme[value].colors[0] ); + api.control( 'background_color' ).container.find( '.color-picker-hex' ) + .data( 'data-default-color', colorScheme[value].colors[0] ) + .wpColorPicker( 'defaultColor', colorScheme[value].colors[0] ); + + // Update Header/Sidebar Background Color. + api( 'header_background_color' ).set( colorScheme[value].colors[1] ); + api.control( 'header_background_color' ).container.find( '.color-picker-hex' ) + .data( 'data-default-color', colorScheme[value].colors[1] ) + .wpColorPicker( 'defaultColor', colorScheme[value].colors[1] ); + + // Update Header/Sidebar Text Color. + api( 'sidebar_textcolor' ).set( colorScheme[value].colors[4] ); + api.control( 'sidebar_textcolor' ).container.find( '.color-picker-hex' ) + .data( 'data-default-color', colorScheme[value].colors[4] ) + .wpColorPicker( 'defaultColor', colorScheme[value].colors[4] ); + } ); + } + } + } ); + + // Generate the CSS for the current Color Scheme. + function updateCSS() { + var scheme = api( 'color_scheme' )(), css, + colors = _.object( colorSchemeKeys, colorScheme[ scheme ].colors ); + + // Merge in color scheme overrides. + _.each( colorSettings, function( setting ) { + colors[ setting ] = api( setting )(); + }); + + // Add additional colors. + colors.secondary_textcolor = Color( colors.textcolor ).toCSS( 'rgba', 0.7 ); + colors.border_color = Color( colors.textcolor ).toCSS( 'rgba', 0.1 ); + colors.border_focus_color = Color( colors.textcolor ).toCSS( 'rgba', 0.3 ); + colors.secondary_sidebar_textcolor = Color( colors.sidebar_textcolor ).toCSS( 'rgba', 0.7 ); + colors.sidebar_border_color = Color( colors.sidebar_textcolor ).toCSS( 'rgba', 0.1 ); + colors.sidebar_border_focus_color = Color( colors.sidebar_textcolor ).toCSS( 'rgba', 0.3 ); + + css = cssTemplate( colors ); + + api.previewer.send( 'update-color-scheme-css', css ); + } + + // Update the CSS whenever a color setting is changed. + _.each( colorSettings, function( setting ) { + api( setting, function( setting ) { + setting.bind( updateCSS ); + } ); + } ); +} )( wp.customize ); diff --git a/wp-content/themes/twentyfifteen/js/customize-preview.js b/wp-content/themes/twentyfifteen/js/customize-preview.js new file mode 100644 index 0000000..58ca269 --- /dev/null +++ b/wp-content/themes/twentyfifteen/js/customize-preview.js @@ -0,0 +1,35 @@ +/** + * Live-update changed settings in real time in the Customizer preview. + */ + +( function( $ ) { + var $style = $( '#twentyfifteen-color-scheme-css' ), + api = wp.customize; + + if ( ! $style.length ) { + $style = $( 'head' ).append( '"; +c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="";j="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode|| +"undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version:"3.7.0",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f); +if(g)return a.createDocumentFragment();for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;d -1 || ua.indexOf( 'opera' ) > -1 || ua.indexOf( 'msie' ) > -1 ) && + document.getElementById && window.addEventListener ) { + + window.addEventListener( 'hashchange', function() { + var element = document.getElementById( location.hash.substring( 1 ) ); + + if ( element ) { + if ( ! /^(?:a|select|input|button|textarea)$/i.test( element.nodeName ) ) { + element.tabIndex = -1; + } + + element.focus(); + } + }, false ); + } +} )(); diff --git a/wp-content/themes/twentyfifteen/languages/twentyfifteen.pot b/wp-content/themes/twentyfifteen/languages/twentyfifteen.pot new file mode 100644 index 0000000..2029927 --- /dev/null +++ b/wp-content/themes/twentyfifteen/languages/twentyfifteen.pot @@ -0,0 +1,328 @@ +# Copyright (C) 2014 the WordPress team +# This file is distributed under the GNU General Public License v2 or later. +msgid "" +msgstr "" +"Project-Id-Version: Twenty Fifteen 1.0\n" +"Report-Msgid-Bugs-To: http://wordpress.org/tags/twentyfifteen\n" +"POT-Creation-Date: 2014-12-14 12:26:59+00:00\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"PO-Revision-Date: 2014-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" + +#: 404.php:17 +msgid "Oops! That page can’t be found." +msgstr "" + +#: 404.php:21 +msgid "It looks like nothing was found at this location. Maybe try a search?" +msgstr "" + +#: archive.php:49 index.php:46 search.php:38 +msgid "Previous page" +msgstr "" + +#: archive.php:50 index.php:47 search.php:39 +msgid "Next page" +msgstr "" + +#: archive.php:51 content-link.php:40 content-page.php:29 content.php:42 +#: image.php:63 index.php:48 search.php:40 +msgid "Page" +msgstr "" + +#: author-bio.php:12 +msgid "Published by" +msgstr "" + +#: author-bio.php:34 +msgid "View all posts by %s" +msgstr "" + +#: comments.php:28 +msgctxt "comments title" +msgid "One thought on “%2$s”" +msgid_plural "%1$s thoughts on “%2$s”" +msgstr[0] "" +msgstr[1] "" + +#: comments.php:53 +msgid "Comments are closed." +msgstr "" + +#. translators: %s: Name of current post +#: content-link.php:31 content.php:33 inc/template-tags.php:237 +msgid "Continue reading %s" +msgstr "" + +#: content-link.php:36 content-page.php:25 content.php:38 image.php:59 +msgid "Pages:" +msgstr "" + +#: content-link.php:56 content-page.php:35 content-search.php:28 +#: content-search.php:33 content.php:57 image.php:71 +msgid "Edit" +msgstr "" + +#: content-none.php:15 +msgid "Nothing Found" +msgstr "" + +#: content-none.php:22 +msgid "" +"Ready to publish your first post? Get started here." +msgstr "" + +#: content-none.php:26 +msgid "" +"Sorry, but nothing matched your search terms. Please try again with some " +"different keywords." +msgstr "" + +#: content-none.php:31 +msgid "" +"It seems we can’t find what you’re looking for. Perhaps " +"searching can help." +msgstr "" + +#. #-#-#-#-# twentyfifteen.pot (Twenty Fifteen 1.0) #-#-#-#-# +#. Author URI of the plugin/theme +#: footer.php:25 +msgid "https://wordpress.org/" +msgstr "" + +#: footer.php:25 +msgid "Proudly powered by %s" +msgstr "" + +#: functions.php:85 +msgid "Primary Menu" +msgstr "" + +#: functions.php:86 +msgid "Social Links Menu" +msgstr "" + +#: functions.php:133 +msgid "Widget Area" +msgstr "" + +#: functions.php:135 +msgid "Add widgets here to appear in your sidebar." +msgstr "" + +#. translators: If there are characters in your language that are not supported +#. by Noto Sans, translate this to 'off'. Do not translate into your own +#. language. +#: functions.php:158 +msgctxt "Noto Sans font: on or off" +msgid "on" +msgstr "" + +#. translators: If there are characters in your language that are not supported +#. by Noto Serif, translate this to 'off'. Do not translate into your own +#. language. +#: functions.php:163 +msgctxt "Noto Serif font: on or off" +msgid "on" +msgstr "" + +#. translators: If there are characters in your language that are not supported +#. by Inconsolata, translate this to 'off'. Do not translate into your own +#. language. +#: functions.php:168 +msgctxt "Inconsolata font: on or off" +msgid "on" +msgstr "" + +#. translators: To add an additional character subset specific to your +#. language, translate this to 'greek', 'cyrillic', 'devanagari' or +#. 'vietnamese'. Do not translate into your own language. +#: functions.php:173 +msgctxt "Add new subset (greek, cyrillic, devanagari, vietnamese)" +msgid "no-subset" +msgstr "" + +#: functions.php:231 +msgid "expand child menu" +msgstr "" + +#: functions.php:232 +msgid "collapse child menu" +msgstr "" + +#: header.php:27 +msgid "Skip to content" +msgstr "" + +#: header.php:44 +msgid "Menu and widgets" +msgstr "" + +#: image.php:24 +msgid "Previous Image" +msgstr "" + +#: image.php:24 +msgid "Next Image" +msgstr "" + +#: image.php:84 +msgctxt "Parent post link" +msgid "" +"Published in" +"%title" +msgstr "" + +#: inc/back-compat.php:37 inc/back-compat.php:47 inc/back-compat.php:60 +msgid "" +"Twenty Fifteen requires at least WordPress version 4.1. You are running " +"version %s. Please upgrade and try again." +msgstr "" + +#: inc/customizer.php:36 +msgid "Base Color Scheme" +msgstr "" + +#: inc/customizer.php:51 +msgid "Header and Sidebar Text Color" +msgstr "" + +#: inc/customizer.php:52 inc/customizer.php:68 inc/customizer.php:73 +msgid "Applied to the header on small screens and the sidebar on wide screens." +msgstr "" + +#: inc/customizer.php:67 +msgid "Header and Sidebar Background Color" +msgstr "" + +#: inc/customizer.php:97 +msgid "Default" +msgstr "" + +#: inc/customizer.php:108 +msgid "Dark" +msgstr "" + +#: inc/customizer.php:119 +msgid "Yellow" +msgstr "" + +#: inc/customizer.php:130 +msgid "Pink" +msgstr "" + +#: inc/customizer.php:141 +msgid "Purple" +msgstr "" + +#: inc/customizer.php:152 +msgid "Blue" +msgstr "" + +#: inc/template-tags.php:23 +msgid "Comment navigation" +msgstr "" + +#: inc/template-tags.php:26 +msgid "Older Comments" +msgstr "" + +#: inc/template-tags.php:30 +msgid "Newer Comments" +msgstr "" + +#: inc/template-tags.php:49 +msgid "Featured" +msgstr "" + +#: inc/template-tags.php:55 +msgctxt "Used before post format." +msgid "Format" +msgstr "" + +#: inc/template-tags.php:76 +msgctxt "Used before publish date." +msgid "Posted on" +msgstr "" + +#: inc/template-tags.php:85 +msgctxt "Used before post author name." +msgid "Author" +msgstr "" + +#: inc/template-tags.php:91 inc/template-tags.php:99 +msgctxt "Used between list items, there is a space after the comma." +msgid ", " +msgstr "" + +#: inc/template-tags.php:94 +msgctxt "Used before category names." +msgid "Categories" +msgstr "" + +#: inc/template-tags.php:102 +msgctxt "Used before tag names." +msgid "Tags" +msgstr "" + +#: inc/template-tags.php:113 +msgctxt "Used before full size attachment link." +msgid "Full size" +msgstr "" + +#: inc/template-tags.php:122 +msgid "Leave a comment" +msgstr "" + +#: inc/template-tags.php:122 +msgid "1 Comment" +msgstr "" + +#: inc/template-tags.php:122 +msgid "% Comments" +msgstr "" + +#: search.php:18 +msgid "Search Results for: %s" +msgstr "" + +#: single.php:33 +msgid "Next" +msgstr "" + +#: single.php:34 +msgid "Next post:" +msgstr "" + +#: single.php:36 +msgid "Previous" +msgstr "" + +#: single.php:37 +msgid "Previous post:" +msgstr "" + +#. Theme Name of the plugin/theme +msgid "Twenty Fifteen" +msgstr "" + +#. Theme URI of the plugin/theme +msgid "https://wordpress.org/themes/twentyfifteen" +msgstr "" + +#. Description of the plugin/theme +msgid "" +"Our 2015 default theme is clean, blog-focused, and designed for clarity. " +"Twenty Fifteen's simple, straightforward typography is readable on a wide " +"variety of screen sizes, and suitable for multiple languages. We designed it " +"using a mobile-first approach, meaning your content takes center-stage, " +"regardless of whether your visitors arrive by smartphone, tablet, laptop, or " +"desktop computer." +msgstr "" + +#. Author of the plugin/theme +msgid "the WordPress team" +msgstr "" diff --git a/wp-content/themes/twentyfifteen/page.php b/wp-content/themes/twentyfifteen/page.php new file mode 100644 index 0000000..5c7a0b0 --- /dev/null +++ b/wp-content/themes/twentyfifteen/page.php @@ -0,0 +1,38 @@ + + +
      +
      + + + +
      +
      + + diff --git a/wp-content/themes/twentyfifteen/readme.txt b/wp-content/themes/twentyfifteen/readme.txt new file mode 100644 index 0000000..aee7f17 --- /dev/null +++ b/wp-content/themes/twentyfifteen/readme.txt @@ -0,0 +1,92 @@ +=== Twenty Fifteen === +Contributors: the WordPress team +Tags: black, blue, gray, pink, purple, white, yellow, dark, light, two-columns, left-sidebar, fixed-layout, responsive-layout, accessibility-ready, custom-background, custom-colors, custom-header, custom-menu, editor-style, featured-images, microformats, post-formats, rtl-language-support, sticky-post, threaded-comments, translation-ready +Requires at least: 4.1 +Tested up to: 4.1 +Stable tag: 4.1 +License: GPLv2 or later +License URI: http://www.gnu.org/licenses/gpl-2.0.html + +== Description == +Our 2015 default theme is clean, blog-focused, and designed for clarity. Twenty Fifteen's simple, straightforward typography is readable on a wide variety of screen sizes, and suitable for multiple languages. We designed it using a mobile-first approach, meaning your content takes center-stage, regardless of whether your visitors arrive by smartphone, tablet, laptop, or desktop computer. + +* Responsive Layout +* Custom Colors +* Custom Header +* Social Links +* Menu Description +* Post Formats +* The GPL v2.0 or later license. :) Use it to make something cool. + +== Installation == + +1. In your admin panel, go to Appearance -> Themes and click the Add New button. +2. Click Upload and Choose File, then select the theme's ZIP file. Click Install Now. +3. Click Activate to use your new theme right away. + +== Frequently Asked Questions == + += How do I change the color scheme? = + +You can change the colors of your site easily using Twenty Fifteen. + +1. In your admin panel, go to Appearance -> Customize. +4. Now you will see the Customizer and a tab called 'Colors'. Click this tab. +5. You can now change your color scheme by selecting one of the predefined ones. Choose a color scheme you want from Base Color Scheme dropdown. You can preview the change in the Customizer. +6. Should you wish to create your own color scheme or modify an existing one, you can by selecting the colors for each area listed. +7. Once you are happy with your color changes you can click save and your changes will be reflected on your live site. + += How do I add the Social Links to the sidebar? = + +Twenty Fifteen allows you display links to your social media profiles, like Twitter and Facebook, with icons. + +1. Create a new Custom Menu, and assign it to the Social Links Menu location. +2. Add links to each of your social services using the Links panel. +3. Icons for your social links will automatically appear if it's available. + +Available icons: (Linking to any of the following sites will automatically display its icon in your social menu). + +* Codepen +* Digg +* Dribbble +* Dropbox +* Facebook +* Flickr +* Foursquare +* GitHub +* Google+ +* Instagram +* LinkedIn +* Email (mailto: links) +* Pinterest +* Pocket +* PollDaddy +* Reddit +* RSS Feed (URLs with /feed/) +* Spotify +* StumbleUpon +* Tumblr +* Twitch +* Twitter +* Vimeo +* WordPress +* YouTube + +Social networks that aren't currently supported will be indicated by a generic share icon. + += How do I add a description for my menu link in navigation? = + +Twenty Fifteen sports a menu design that's easy to navigate -- especially when you add menu descriptions. + +1. Visit the Menus page in your admin. +2. Use the Screen Options tab to "Show advanced menu properties". +3. Select "Description" there to start editing menu descriptions. +4. Select the menu you want to add links and descriptions to. +5. When in the Menu Structure section, you can click open the link and add a description. +6. Once you save the menu with your link, the new description should show up. + += Quick Specs = + +1. The main content width is 660px. +2. The sidebar width is 248px. +3. Featured Images are 825px wide by 510px high. diff --git a/wp-content/themes/twentyfifteen/rtl.css b/wp-content/themes/twentyfifteen/rtl.css new file mode 100644 index 0000000..35259a6 --- /dev/null +++ b/wp-content/themes/twentyfifteen/rtl.css @@ -0,0 +1,856 @@ +/* +Theme Name: Twenty Fifteen +Description: Adds support for languages written in a Right To Left (RTL) direction. +It's easy, just a matter of overwriting all the horizontal positioning attributes +of your CSS stylesheet in a separate stylesheet file named rtl.css. + +See: https://codex.wordpress.org/Right_to_Left_Language_Support +*/ + +/** + * Table of Contents: + * + * 1.0 - Reset + * 2.0 - Typography + * 3.0 - Elements + * 4.0 - Forms + * 5.0 - Navigations + * 6.0 - Accessibility + * 7.0 - Alignments + * 8.0 - Header + * 9.0 - Widgets + * 10.0 - Content + * 10.1 - Posts and pages + * 10.2 - Comments + * 11.0 - Media Queries + * 11.1 - Mobile Large + * 11.2 - Tablet Small + * 11.3 - Tablet Large + * 11.4 - Desktop Small + * 11.5 - Desktop Medium + * 11.6 - Desktop Large + * 11.7 - Desktop X-Large + */ + + +/** + * 1.0 Reset + */ + +body { + direction: rtl; + unicode-bidi: embed; +} + +caption, +th, +td { + text-align: right; +} + + +/** + * 2.0 Typography + */ + +body, +button, +input[type="button"], +input[type="reset"], +input[type="submit"], +input, +select, +textarea, +blockquote cite, +blockquote small, +.post-password-form label, +.main-navigation .menu-item-description, +.post-navigation .meta-nav, +.post-navigation .post-title, +.pagination, +.image-navigation, +.comment-navigation, +.site-title, +.site-description, +.widget-title, +.widget_calendar caption, +.widget_rss .rss-date, +.widget_rss cite, +.author-heading, +.entry-footer, +.page-title, +.page-links, +.entry-caption, +.comments-title, +.comment-reply-title, +.comment-metadata, +.pingback .edit-link, +.comment-list .reply a, +.comment-form label, +.comment-notes, +.comment-awaiting-moderation, +.logged-in-as, +.form-allowed-tags, +.no-comments, +.wp-caption-text, +.gallery-caption { + font-family: Arial, Tahoma, sans-serif; +} + +::-webkit-input-placeholder { + font-family: Arial, Tahoma, sans-serif; +} + +:-moz-placeholder { + font-family: Arial, Tahoma, sans-serif; +} + +::-moz-placeholder { + font-family: Arial, Tahoma, sans-serif; +} + +:-ms-input-placeholder { + font-family: Arial, Tahoma, sans-serif; +} + +blockquote { + border-right: 4px solid rgba(51, 51, 51, 0.7); + border-left: 0; + padding-right: 0.7778em; + padding-left: 0; +} + + +/** + * 3.0 Elements + */ + +ul, +ol { + margin: 0 1.3333em 1.6em 0; +} + +caption, +th, +td { + text-align: right; +} + + +/** + * 4.0 Forms + */ + +.post-password-form input[type="submit"] { + right: auto; + left: 0; +} + + +/** + * 5.0 Navigations + */ + +.main-navigation ul ul { + margin-right: 0.8em; + margin-left: auto; +} + +.main-navigation .page_item_has_children > a, +.main-navigation .menu-item-has-children > a { + padding-right: 0; + padding-left: 48px; +} + +.dropdown-toggle { + right: auto; + left: 0; +} + +.dropdown-toggle:after { + right: -1px; + left: auto; +} + +.social-navigation li { + float: right; +} + +.social-navigation a:before { + right: 0; + left: auto; +} + +.secondary-toggle { + right: auto; + left: 0; +} + +.post-navigation .has-post-thumbnail a:before { + right: 0; + left: auto; +} + +.pagination .prev { + right: 0; + left: auto; +} + +.pagination .prev:before { + content: "\f429"; + right: -1px; + left: auto; +} + +.pagination .next { + right: auto; + left: 0; +} + +.pagination .next:before { + content: "\f430"; + right: auto; + left: -1px; +} + +.image-navigation .nav-previous a:before, +.comment-navigation .nav-previous a:before { + content: "\f429"; + margin-right: auto; + margin-left: 0.2em; +} + +.image-navigation .nav-next a:after, +.comment-navigation .nav-next a:after { + content: "\f430"; + margin-right: 0.2em; + margin-left: auto; +} + + +/** + * 6.0 Accessibility + */ + +.screen-reader-text:hover, +.screen-reader-text:focus { + right: 5px; + left: auto; +} + + +/** + * 7.0 Alignments + */ + +.alignright { + float: right; +} + +.alignleft { + float: left; +} + +.aligncenter { + margin-right: auto; + margin-left: auto; +} + +blockquote.alignright, +.wp-caption.alignright, +img.alignright { + margin: 0.4em 0 1.6em 1.6em; +} + +blockquote.alignleft, +.wp-caption.alignleft, +img.alignleft { + margin: 0.4em 1.6em 1.6em 0; +} + + +/** + * 8.0 Header + */ + +.site-branding { + padding-right: 0; + padding-left: 60px; +} + + +/** + * 9.0 Widgets + */ + +.widget_categories .children, +.widget_nav_menu .sub-menu, +.widget_pages .children { + margin: 0.7667em 0.8em 0 0; +} + + +/** + * 10.0 Content + */ + +/** + * 10.1 Posts and pages + */ + +.entry-content .more-link:after { + content: "\f430"; +} + +.author-link:after { + content: "\f430"; +} + +.author-info .avatar { + float: right; + margin: 0 0 1.6em 1.6em; +} + +.posted-on:before, +.byline:before, +.cat-links:before, +.tags-links:before, +.comments-link:before, +.entry-format:before, +.edit-link:before, +.full-size-link:before { + margin-right: auto; + margin-left: 2px; +} + +.posted-on, +.byline, +.cat-links, +.tags-links, +.comments-link, +.entry-format, +.full-size-link { + margin-right: auto; + margin-left: 1em; +} + +.page-links a, +.page-links > span { + margin: 0 0 0.3333em 0.3333em; +} + +.page-links > .page-links-title { + padding-right: 0; + padding-left: 0.5em; +} + +.type-attachment .entry-header { + clear: left; +} + +.format-link .entry-title a:after { + -webkit-transform: scaleX(-1); + -moz-transform: scaleX(-1); + -ms-transform: scaleX(-1); + -o-transform: scaleX(-1); + transform: scaleX(-1); +} + + +/** + * 10.2 Comments + */ + +.comment-list .children > li { + padding-right: 0.8em; + padding-left: 0; +} + +.comment-author .avatar { + float: right; + margin-right: 0; + margin-left: 0.4em; +} + +.bypostauthor > article .fn:after { + right: 3px; + left: auto; +} + +.comment-metadata .edit-link { + margin-right: 1em; + margin-left: auto; +} + +.pingback .edit-link { + margin-right: 1em; + margin-left: auto; +} + +.comment-content ul, +.comment-content ol { + margin: 0 1.3333em 1.6em 0; +} + +.comment-reply-title small a { + float: left; +} + + +/** + * 11.0 Media Queries + */ + + +/** + * 11.1 Mobile Large 620px + */ + +@media screen and (min-width: 38.75em) { + ul, + ol { + margin-right: 0; + margin-left: auto; + } + + li > ul, + li > ol, + blockquote > ul, + blockquote > ol { + margin-right: 1.3333em; + margin-left: auto; + } + + blockquote { + margin-right: -1em; + margin-left: auto; + } + + blockquote > blockquote { + margin-right: 0; + margin-left: auto; + } + + .page-header { + border-color: inherit; + border-left: none; + border-style: solid; + border-width: 0 7px 0 0; + } + + .page-title, + .taxonomy-description { + margin-right: -7px; + margin-left: auto; + } + + .comment-content ul, + .comment-content ol { + margin-right: 0; + margin-left: auto; + } + + .comment-content li > ul, + .comment-content li > ol, + .comment-content blockquote > ul, + .comment-content blockquote > ol { + margin-right: 1.3333em; + margin-left: auto; + } +} + + +/** + * 11.2 Tablet Small 740px + */ + +@media screen and (min-width: 46.25em) { + blockquote { + margin-right: -1.05em; + margin-left: auto; + padding-right: 0.85em; + padding-left: 0; + } + + .main-navigation ul ul { + margin-right: 1em; + margin-left: auto; + } + + .main-navigation .page_item_has_children > a, + .main-navigation .main-navigation .menu-item-has-children > a { + padding-right: 0; + padding-left: 54px; + } + + blockquote.alignright, + .wp-caption.alignright + img.alignright { + margin: 0.4118em 0 1.6471em 1.6471em; + } + + blockquote.alignleft, + .wp-caption.alignleft, + img.alignleft { + margin: 0.4118em 1.6471em 1.6471em 0; + } + + .site-branding { + padding-right: 0; + padding-left: 66px; + } + + .widget blockquote { + margin-right: -1.2353em; + margin-left: auto; + padding-right: 1em; + padding-left: 0; + } + + .widget blockquote > blockquote { + margin-right: 0; + margin-left: auto; + } + + .widget blockquote.alignright, + .widget .wp-caption.alignright, + .widget img.alignright { + margin: 0.5em 0 1.5em 1.5em; + } + + .widget blockquote.alignleft, + .widget .wp-caption.alignleft, + .widget img.alignleft { + margin: 0.5em 1.5em 1.5em 0; + } + + .widget_categories .children, + .widget_nav_menu .sub-menu, + .widget_pages .children { + margin: 0.9643em 1em 0 0; + } + + .page-links a, + .page-links > span { + margin: 0 0 0.2857em 0.2857em; + } + + .author-info .avatar { + margin: 0 0 1.6471em 1.6471em; + } + + .comment-list .children > li { + padding-right: 1.2353em; + padding-left: 0; + } + + .comment-author .avatar { + margin-left: 1.64705em; + } + + .bypostauthor > article .fn:after { + right: 6px; + left: auto; + } +} + + +/** + * 11.3 Tablet Large 880px + */ + +@media screen and (min-width: 55em) { + blockquote { + margin-right: -1.0909em; + margin-left: auto; + padding-right: 0.9091em; + padding-left: 0; + } + + .main-navigation .page_item_has_children > a, + .main-navigation .main-navigation .menu-item-has-children > a { + padding-right: 0; + padding-left: 53px; + } + + blockquote.alignright, + .wp-caption.alignright + img.alignright { + margin: 0.4211em 0 1.6842em 1.6842em; + } + + blockquote.alignleft, + .wp-caption.alignleft, + img.alignleft { + margin: 0.4211em 1.6842em 1.6842em 0; + } + + .site-branding { + padding-right: 0; + padding-left: 74px; + } + + .widget blockquote { + margin-right: -1.2632em; + margin-left: auto; + padding-right: 1.0526em; + padding-left: 0; + } + + .widget_categories .children, + .widget_nav_menu .sub-menu, + .widget_pages .children { + margin: 0.7188em 1em 0 0; + } + + .page-links a, + .page-links > span { + margin: 0 0 0.25em 0.25em; + } + + .author-info .avatar { + margin: 0 0 1.6842em 1.6842em; + } + + .comment-list .children > li { + padding-right: 1.4737em; + padding-left: 0; + } + + .comment-author .avatar { + margin-left: 1.6842em; + } +} + + +/** + * 11.4 Desktop Small 955px + */ + +@media screen and (min-width: 59.6875em) { + body:before { + right: 0; + left: auto; + } + + .sidebar { + float: right; + margin-right: auto; + margin-left: -100%; + } + + .site-content { + float: right; + margin-right: 29.4118%; + margin-left: auto; + } + + blockquote { + margin-right: -1.3333em; + margin-left: auto; + padding-right: 1.1111em; + padding-left: 0; + } + + .main-navigation .page_item_has_children > a, + .main-navigation .menu-item-has-children > a { + padding-right: 0; + padding-left: 35px; + } + + blockquote.alignright, + .wp-caption.alignright, + img.alignright { + margin: 0.4em 0 1.6em 1.6em; + } + + blockquote.alignleft, + .wp-caption.alignleft, + img.alignleft { + margin: 0.4em 1.6em 1.6em 0; + } + + .widget blockquote { + margin-right: -1.5em; + margin-left: auto; + padding-right: 1.1667em; + padding-left: 0; + } + + .widget_categories .children, + .widget_nav_menu .sub-menu, + .widget_pages .children { + margin: 0.4583em 1em 0 0; + } + + .page-links a, + .page-links > span { + margin: 0 0 0.3333em 0.3333em; + } + + .author-info .avatar { + margin: 0 0 1.5em 1.5em; + } + + .comment-list .children > li { + padding-right: 0.8em; + padding-left: 0; + } + + .comment-author .avatar { + margin-left: 0.8em; + } + + .bypostauthor > article .fn:after { + right: 3px; + left: auto; + } + + .site-branding { + padding: 0; + } + + .site-footer { + float: right; + margin: 0 35.2941% 0 0; + } +} + + +/** + * 11.5 Desktop Medium 1100px + */ + +@media screen and (min-width: 68.75em) { + blockquote { + margin-right: -1.05em; + margin-left: auto; + padding-right: 0.85em; + padding-left: 0; + } + + .main-navigation .page_item_has_children > a, + .main-navigation .menu-item-has-children > a { + padding-right: 0; + padding-left: 33px; + } + + blockquote.alignright, + .wp-caption.alignright + img.alignright { + margin: 0.4118em 0 1.6471em 1.6471em; + } + + blockquote.alignleft, + .wp-caption.alignleft, + img.alignleft { + margin: 0.4118em 1.6471em 1.6471em 0; + } + + .widget blockquote { + padding-right: 1.2143em; + padding-left: 0; + } + + .widget_categories .children, + .widget_nav_menu .sub-menu, + .widget_pages .children { + margin: 0.4643em 1em 0 0; + } + + .page-links a, + .page-links > span { + margin: 0 0 0.2857em 0.2857em; + } + + .author-info .avatar { + margin: 0 0 1.6471em 1.6471em; + } + + .comment-list .children > li { + padding-right: 1.1667em; + padding-left: 0; + } + + .comment-author .avatar { + margin-left: 1.64705em; + } + + .bypostauthor > article .fn:after { + right: 6px; + left: auto; + } +} + + +/** + * 11.6 Desktop Large 1240px + */ + +@media screen and (min-width: 77.5em) { + blockquote { + margin-right: -1.0909em; + margin-left: auto; + padding-right: 0.9091em; + padding-left: 0; + } + + .main-navigation .page_item_has_children > a, + .main-navigation .menu-item-has-children > a { + padding-right: 0; + padding-left: 32px; + } + + blockquote.alignright, + .wp-caption.alignright + img.alignright { + margin: 0.4211em 0 1.6842em 1.6842em; + } + + blockquote.alignleft, + .wp-caption.alignleft, + img.alignleft { + margin: 0.4211em 1.6842em 1.6842em 0; + } + + .widget blockquote { + padding-right: 1.25em; + padding-left: 0; + } + + .widget_categories .children, + .widget_nav_menu .sub-menu, + .widget_pages .children { + margin: 0.4688em 1em 0 0; + } + + .page-links a, + .page-links > span { + margin: 0 0 0.25em 0.25em; + } + + .author-info .avatar { + margin: 0 0 1.6842em 1.6842em; + } + + .comment-list .children > li { + padding-right: 1.4737em; + padding-left: 0; + } + + .comment-author .avatar { + margin-left: 1.64705em; + } +} + + +/** + * 11.7 Desktop X-Large 1403px + */ + +@media screen and (min-width: 87.6875em) { + body:before { + width: -webkit-calc(50% - 289px); + width: calc(50% - 289px); + } +} diff --git a/wp-content/themes/twentyfifteen/screenshot.png b/wp-content/themes/twentyfifteen/screenshot.png new file mode 100644 index 0000000..d7fcd5f Binary files /dev/null and b/wp-content/themes/twentyfifteen/screenshot.png differ diff --git a/wp-content/themes/twentyfifteen/search.php b/wp-content/themes/twentyfifteen/search.php new file mode 100644 index 0000000..3352841 --- /dev/null +++ b/wp-content/themes/twentyfifteen/search.php @@ -0,0 +1,53 @@ + + +
      +
      + + + + + + + + __( 'Previous page', 'twentyfifteen' ), + 'next_text' => __( 'Next page', 'twentyfifteen' ), + 'before_page_number' => '' . __( 'Page', 'twentyfifteen' ) . ' ', + ) ); + + // If no content, include the "No posts found" template. + else : + get_template_part( 'content', 'none' ); + + endif; + ?> + +
      +
      + + diff --git a/wp-content/themes/twentyfifteen/sidebar.php b/wp-content/themes/twentyfifteen/sidebar.php new file mode 100644 index 0000000..02308ef --- /dev/null +++ b/wp-content/themes/twentyfifteen/sidebar.php @@ -0,0 +1,47 @@ + +
      + + + + + + + + + + + + + +
      + + diff --git a/wp-content/themes/twentyfifteen/single.php b/wp-content/themes/twentyfifteen/single.php new file mode 100644 index 0000000..afbb7b5 --- /dev/null +++ b/wp-content/themes/twentyfifteen/single.php @@ -0,0 +1,48 @@ + + +
      +
      + + ' ' . + '' . __( 'Next post:', 'twentyfifteen' ) . ' ' . + '%title', + 'prev_text' => ' ' . + '' . __( 'Previous post:', 'twentyfifteen' ) . ' ' . + '%title', + ) ); + + // End the loop. + endwhile; + ?> + +
      +
      + + diff --git a/wp-content/themes/twentyfifteen/style.css b/wp-content/themes/twentyfifteen/style.css new file mode 100644 index 0000000..f90dfba --- /dev/null +++ b/wp-content/themes/twentyfifteen/style.css @@ -0,0 +1,5731 @@ +/* +Theme Name: Twenty Fifteen +Theme URI: https://wordpress.org/themes/twentyfifteen +Author: the WordPress team +Author URI: https://wordpress.org/ +Description: Our 2015 default theme is clean, blog-focused, and designed for clarity. Twenty Fifteen's simple, straightforward typography is readable on a wide variety of screen sizes, and suitable for multiple languages. We designed it using a mobile-first approach, meaning your content takes center-stage, regardless of whether your visitors arrive by smartphone, tablet, laptop, or desktop computer. +Version: 1.0 +License: GNU General Public License v2 or later +License URI: http://www.gnu.org/licenses/gpl-2.0.html +Tags: black, blue, gray, pink, purple, white, yellow, dark, light, two-columns, left-sidebar, fixed-layout, responsive-layout, accessibility-ready, custom-background, custom-colors, custom-header, custom-menu, editor-style, featured-images, microformats, post-formats, rtl-language-support, sticky-post, threaded-comments, translation-ready +Text Domain: twentyfifteen + +This theme, like WordPress, is licensed under the GPL. +Use it to make something cool, have fun, and share what you've learned with others. +*/ + + +/** + * Table of Contents + * + * 1.0 - Reset + * 2.0 - Genericons + * 3.0 - Typography + * 4.0 - Elements + * 5.0 - Forms + * 6.0 - Navigations + * 6.1 - Links + * 6.2 - Menus + * 7.0 - Accessibility + * 8.0 - Alignments + * 9.0 - Clearings + * 10.0 - Header + * 11.0 - Widgets + * 12.0 - Content + * 12.1 - Posts and pages + * 12.2 - Post Formats + * 12.3 - Comments + * 13.0 - Footer + * 14.0 - Media + * 14.1 - Captions + * 14.2 - Galleries + * 15.0 - Media Queries + * 15.1 - Mobile Large + * 15.2 - Tablet Small + * 15.3 - Tablet Large + * 15.4 - Desktop Small + * 15.5 - Desktop Medium + * 15.6 - Desktop Large + * 15.7 - Desktop X-Large + */ + + +/** + * 1.0 - Reset + * + * Resetting and rebuilding styles have been helped along thanks to the fine work of + * Eric Meyer http://meyerweb.com/eric/tools/css/reset/index.html + * along with Nicolas Gallagher and Jonathan Neal http://necolas.github.com/normalize.css/ + * and Blueprint http://www.blueprintcss.org/ + */ + +html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td { + border: 0; + font-family: inherit; + font-size: 100%; + font-style: inherit; + font-weight: inherit; + margin: 0; + outline: 0; + padding: 0; + vertical-align: baseline; +} + +html { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + font-size: 62.5%; + overflow-y: scroll; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} + +*, +*:before, +*:after { + -webkit-box-sizing: inherit; + -moz-box-sizing: inherit; + box-sizing: inherit; +} + +body { + background: #f1f1f1; +} + +article, +aside, +details, +figcaption, +figure, +footer, +header, +main, +nav, +section { + display: block; +} + +ol, +ul { + list-style: none; +} + +table { + border-collapse: separate; + border-spacing: 0; +} + +caption, +th, +td { + font-weight: normal; + text-align: left; +} + +blockquote:before, +blockquote:after, +q:before, +q:after { + content: ""; +} + +blockquote, +q { + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + quotes: none; +} + +a:focus { + outline: 2px solid #c1c1c1; + outline: 2px solid rgba(51, 51, 51, 0.3); +} + +a:hover, +a:active { + outline: 0; +} + +a img { + border: 0; +} + + +/** + * 2.0 - Genericons + */ + +.social-navigation a:before, +.secondary-toggle:before, +.dropdown-toggle:after, +.bypostauthor > article .fn:after, +.comment-reply-title small a:before, +.comment-navigation .nav-next a:after, +.comment-navigation .nav-previous a:before, +.posted-on:before, +.byline:before, +.cat-links:before, +.tags-links:before, +.comments-link:before, +.entry-format:before, +.edit-link:before, +.full-size-link:before, +.pagination .prev:before, +.pagination .next:before, +.image-navigation a:before, +.image-navigation a:after, +.format-link .entry-title a:after, +.entry-content .more-link:after, +.entry-summary .more-link:after, +.author-link:after { + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + display: inline-block; + font-family: "Genericons"; + font-size: 16px; + font-style: normal; + font-weight: normal; + font-variant: normal; + line-height: 1; + speak: none; + text-align: center; + text-decoration: inherit; + text-transform: none; + vertical-align: top; +} + + +/** + * 3.0 Typography + */ + +body, +button, +input, +select, +textarea { + color: #333; + font-family: "Noto Serif", serif; + font-size: 15px; + font-size: 1.5rem; + line-height: 1.6; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + clear: both; + font-weight: 700; +} + +p { + margin-bottom: 1.6em; +} + +b, +strong { + font-weight: 700; +} + +dfn, +cite, +em, +i { + font-style: italic; +} + +blockquote { + border-left: 4px solid #707070; + border-left: 4px solid rgba(51, 51, 51, 0.7); + color: #707070; + color: rgba(51, 51, 51, 0.7); + font-size: 18px; + font-size: 1.8rem; + font-style: italic; + line-height: 1.6667; + margin-bottom: 1.6667em; + padding-left: 0.7778em; +} + +blockquote p { + margin-bottom: 1.6667em; +} + +blockquote > p:last-child { + margin-bottom: 0; +} + +blockquote cite, +blockquote small { + color: #333; + font-size: 15px; + font-size: 1.5rem; + font-family: "Noto Sans", sans-serif; + line-height: 1.6; +} + +blockquote em, +blockquote i, +blockquote cite { + font-style: normal; +} + +blockquote strong, +blockquote b { + font-weight: 400; +} + +address { + font-style: italic; + margin: 0 0 1.6em; +} + +code, +kbd, +tt, +var, +samp, +pre { + font-family: Inconsolata, monospace; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; +} + +pre { + background-color: transparent; + background-color: rgba(0, 0, 0, 0.01); + border: 1px solid #eaeaea; + border: 1px solid rgba(51, 51, 51, 0.1); + line-height: 1.2; + margin-bottom: 1.6em; + max-width: 100%; + overflow: auto; + padding: 0.8em; + white-space: pre; + white-space: pre-wrap; + word-wrap: break-word; +} + +abbr[title] { + border-bottom: 1px dotted #eaeaea; + border-bottom: 1px dotted rgba(51, 51, 51, 0.1); + cursor: help; +} + +mark, +ins { + background-color: #fff9c0; + text-decoration: none; +} + +sup, +sub { + font-size: 75%; + height: 0; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sup { + bottom: 1ex; +} + +sub { + top: .5ex; +} + +small { + font-size: 75%; +} + +big { + font-size: 125%; +} + + +/** + * 4.0 Elements + */ + +hr { + background-color: #eaeaea; + background-color: rgba(51, 51, 51, 0.1); + border: 0; + height: 1px; + margin-bottom: 1.6em; +} + +ul, +ol { + margin: 0 0 1.6em 1.3333em; +} + +ul { + list-style: disc; +} + +ol { + list-style: decimal; +} + +li > ul, +li > ol { + margin-bottom: 0; +} + +dl { + margin-bottom: 1.6em; +} + +dt { + font-weight: bold; +} + +dd { + margin-bottom: 1.6em; +} + +table, +th, +td { + border: 1px solid #eaeaea; + border: 1px solid rgba(51, 51, 51, 0.1); +} + +table { + border-collapse: separate; + border-spacing: 0; + border-width: 1px 0 0 1px; + margin: 0 0 1.6em; + table-layout: fixed; /* Prevents HTML tables from becoming too wide */ + width: 100%; +} + +caption, +th, +td { + font-weight: normal; + text-align: left; +} + +th { + border-width: 0 1px 1px 0; + font-weight: 700; +} + +td { + border-width: 0 1px 1px 0; +} + +th, td { + padding: 0.4em; +} + +img { + -ms-interpolation-mode: bicubic; + border: 0; + height: auto; + max-width: 100%; + vertical-align: middle; +} + +figure { + margin: 0; +} + +del { + opacity: 0.8; +} + +/* Placeholder text color -- selectors need to be separate to work. */ + +::-webkit-input-placeholder { + color: rgba(51, 51, 51, 0.7); + font-family: "Noto Sans", sans-serif; +} + +:-moz-placeholder { + color: rgba(51, 51, 51, 0.7); + font-family: "Noto Sans", sans-serif; +} + +::-moz-placeholder { + color: rgba(51, 51, 51, 0.7); + font-family: "Noto Sans", sans-serif; + opacity: 1; /* Since FF19 lowers the opacity of the placeholder by default */ +} + +:-ms-input-placeholder { + color: rgba(51, 51, 51, 0.7); + font-family: "Noto Sans", sans-serif; +} + + +/** + * 5.0 Forms + */ + +button, +input, +select, +textarea { + background-color: #f7f7f7; + border-radius: 0; + font-size: 16px; + font-size: 1.6rem; + line-height: 1.5; + margin: 0; + max-width: 100%; + vertical-align: baseline; +} + +button, +input { + line-height: normal; +} + +input, +textarea { + background-image: -webkit-linear-gradient(rgba(255, 255, 255, 0), rgba(255, 255, 255, 0)); /* Removing the inner shadow on iOS inputs */ + border: 1px solid #eaeaea; + border: 1px solid rgba(51, 51, 51, 0.1); + color: #707070; + color: rgba(51, 51, 51, 0.7); +} + +input:focus, +textarea:focus { + background-color: #fff; + border: 1px solid #c1c1c1; + border: 1px solid rgba(51, 51, 51, 0.3); + color: #333; +} + +input:focus, +select:focus { + outline: 2px solid #c1c1c1; + outline: 2px solid rgba(51, 51, 51, 0.3); +} + +button[disabled], +input[disabled], +select[disabled], +textarea[disabled] { + cursor: default; + opacity: .5; +} + +button, +input[type="button"], +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; + background-color: #333; + border: 0; + color: #fff; + cursor: pointer; + font-family: "Noto Sans", sans-serif; + font-size: 12px; + font-size: 1.2rem; + font-weight: 700; + padding: 0.7917em 1.5em; + text-transform: uppercase; +} + +button:hover, +input[type="button"]:hover, +input[type="reset"]:hover, +input[type="submit"]:hover, +button:focus, +input[type="button"]:focus, +input[type="reset"]:focus, +input[type="submit"]:focus { + background-color: #707070; + background-color: rgba(51, 51, 51, 0.7); + outline: 0; +} + +input[type="search"] { + -webkit-appearance: textfield; +} + +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} + +input[type="text"], +input[type="email"], +input[type="url"], +input[type="password"], +input[type="search"], +textarea { + padding: 0.375em; + width: 100%; +} + +textarea { + overflow: auto; + vertical-align: top; +} + +input[type="text"]:focus, +input[type="email"]:focus, +input[type="url"]:focus, +input[type="password"]:focus, +input[type="search"]:focus, +textarea:focus { + outline: 0; +} + +.post-password-form { + position: relative; +} + +.post-password-form label { + color: #707070; + color: rgba(51, 51, 51, 0.7); + display: block; + font-family: "Noto Sans", sans-serif; + font-size: 12px; + font-size: 1.2rem; + font-weight: 700; + letter-spacing: 0.04em; + line-height: 1.5; + text-transform: uppercase; +} + +.post-password-form input[type="submit"] { + padding: 0.7917em; + position: absolute; + right: 0; + bottom: 0; +} + +input[type="checkbox"], +input[type="radio"] { + padding: 0; +} + +.search-form input[type="submit"], +.widget .search-form input[type="submit"] { + padding: 0; +} + + +/** + * 6.0 Navigations + */ + + +/** + * 6.1 Links + */ + +a { + color: #333; + text-decoration: none; +} + +a:hover, +a:focus { + color: #707070; + color: rgba(51, 51, 51, 0.7); +} + + +/** + * 6.2 Menus + */ + +.main-navigation a { + display: block; + padding: 0.8em 0; + position: relative; + text-decoration: none; +} + +.main-navigation ul { + list-style: none; + margin: 0; +} + +.main-navigation ul ul { + display: none; + margin-left: 0.8em; +} + +.main-navigation ul .toggled-on { + display: block; +} + +.main-navigation li { + border-top: 1px solid #eaeaea; + border-top: 1px solid rgba(51, 51, 51, 0.1); + position: relative; +} + +.main-navigation .current_page_item > a, +.main-navigation .current-menu-item > a, +.main-navigation .current_page_ancestor > a { + font-weight: 700; +} + +.main-navigation .nav-menu > ul > li:first-child, +.main-navigation .nav-menu > li:first-child { + border-top: 0; +} + +.main-navigation .page_item_has_children > a, +.main-navigation .menu-item-has-children > a { + padding-right: 48px; +} + +.main-navigation .menu-item-description { + color: #707070; + color: rgba(51, 51, 51, 0.7); + font-family: "Noto Sans", sans-serif; + font-size: 12px; + font-size: 1.2rem; + font-weight: 400; + line-height: 1.5; + margin-top: 0.5em; +} + +.no-js .main-navigation ul ul { + display: block; +} + +.dropdown-toggle { + background-color: transparent; + border: 0; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + content: ""; + height: 42px; + padding: 0; + position: absolute; + text-transform: lowercase; /* Stop screen readers to read the text as capital letters */ + top: 3px; + right: 0; + width: 42px; +} + +.dropdown-toggle:after { + color: #333; + content: "\f431"; + font-size: 24px; + line-height: 42px; + position: relative; + top: 0; + left: 1px; + width: 42px; +} + +.dropdown-toggle:hover, +.dropdown-toggle:focus { + background-color: #eaeaea; + background-color: rgba(51, 51, 51, 0.1); +} + +.dropdown-toggle:focus { + outline: 1px solid #c1c1c1; + outline: 1px solid rgba(51, 51, 51, 0.3); +} + +.dropdown-toggle.toggle-on:after { + content: "\f432"; +} + +.social-navigation { + margin: 9.0909% 0; +} + +.social-navigation ul { + list-style: none; + margin: 0 0 -1.6em 0; +} + +.social-navigation li { + float: left; +} + +.social-navigation a { + display: block; + height: 3.2em; + position: relative; + width: 3.2em; +} + +.social-navigation a:before { + content: "\f415"; + font-size: 24px; + position: absolute; + top: 0; + left: 0; +} + +.social-navigation a[href$="/feed/"]:before { + content: "\f413"; +} + +.social-navigation a[href*="codepen.io"]:before { + content: "\f216"; +} + +.social-navigation a[href*="digg.com"]:before { + content: "\f221"; +} + +.social-navigation a[href*="dribbble.com"]:before { + content: "\f201"; +} + +.social-navigation a[href*="dropbox.com"]:before { + content: "\f225"; +} + +.social-navigation a[href*="facebook.com"]:before { + content: "\f203"; +} + +.social-navigation a[href*="flickr.com"]:before { + content: "\f211"; +} + +.social-navigation a[href*="foursquare.com"]:before { + content: "\f226"; +} + +.social-navigation a[href*="plus.google.com"]:before { + content: "\f206"; +} + +.social-navigation a[href*="github.com"]:before { + content: "\f200"; +} + +.social-navigation a[href*="instagram.com"]:before { + content: "\f215"; +} + +.social-navigation a[href*="linkedin.com"]:before { + content: "\f208"; +} + +.social-navigation a[href*="pinterest.com"]:before { + content: "\f210"; +} + +.social-navigation a[href*="getpocket.com"]:before { + content: "\f224"; +} + +.social-navigation a[href*="polldaddy.com"]:before { + content: "\f217"; +} + +.social-navigation a[href*="reddit.com"]:before { + content: "\f222"; +} + +.social-navigation a[href*="stumbleupon.com"]:before { + content: "\f223"; +} + +.social-navigation a[href*="tumblr.com"]:before { + content: "\f214"; +} + +.social-navigation a[href*="twitter.com"]:before { + content: "\f202"; +} + +.social-navigation a[href*="vimeo.com"]:before { + content: "\f212"; +} + +.social-navigation a[href*="wordpress.com"]:before, +.social-navigation a[href*="wordpress.org"]:before { + content: "\f205"; +} + +.social-navigation a[href*="youtube.com"]:before { + content: "\f213"; +} + +.social-navigation a[href*="mailto:"]:before { + content: "\f410"; +} + +.social-navigation a[href*="spotify.com"]:before { + content: "\f515"; +} + +.social-navigation a[href*="twitch.tv"]:before { + content: "\f516"; +} + +.secondary-toggle { + background-color: transparent; + border: 1px solid #eaeaea; + border: 1px solid rgba(51, 51, 51, 0.1); + height: 42px; + overflow: hidden; + padding: 0; + position: absolute; + top: 50%; + right: 0; + text-align: center; + -webkit-transform: translateY(-50%); + -ms-transform: translateY(-50%); + transform: translateY(-50%); + width: 42px; +} + +.secondary-toggle:before { + color: #333; + content: "\f419"; + line-height: 40px; + width: 40px; +} + +.secondary-toggle:hover, +.secondary-toggle:focus { + background-color: transparent; + border: 1px solid #c1c1c1; + border: 1px solid rgba(51, 51, 51, 0.3); + outline: 0; +} + +.secondary-toggle.toggled-on:before { + content: "\f405"; + font-size: 32px; + position: relative; + top: 1px; + left: -1px; +} + +.post-navigation { + background-color: #fff; + border-top: 1px solid #eaeaea; + border-top: 1px solid rgba(51, 51, 51, 0.1); + font-weight: 700; +} + +.post-navigation a { + display: block; + padding: 3.8461% 7.6923%; +} + +.post-navigation span { + display: block; +} + +.post-navigation .meta-nav { + color: #707070; + color: rgba(51, 51, 51, 0.7); + font-family: "Noto Sans", sans-serif; + font-size: 12px; + font-size: 1.2rem; + letter-spacing: 0.04em; + line-height: 1.5; + position: relative; + text-transform: uppercase; + z-index: 2; +} + +.post-navigation .post-title { + font-family: "Noto Serif", serif; + font-size: 18px; + font-size: 1.8rem; + line-height: 1.3333; + position: relative; + z-index: 2; +} + +.post-navigation .nav-next, +.post-navigation .nav-previous { + background-position: center; + background-size: cover; + position: relative; +} + +.post-navigation a:before { + content: ""; + display: block; + height: 100%; + position: absolute; + top: 0; + left: 0; + width: 100%; + z-index: 1; +} + +.post-navigation a:hover:before, +.post-navigation a:focus:before { + opacity: 0.5; +} + +.post-navigation .meta-nav { + opacity: 0.8; +} + +.post-navigation div + div { + border-top: 1px solid #eaeaea; + border-top: 1px solid rgba(51, 51, 51, 0.1); +} + +.pagination { + background-color: #fff; + border-top: 1px solid rgba(51, 51, 51, 0.1); + font-family: "Noto Sans", sans-serif; +} + +.pagination .nav-links { + min-height: 3.2em; + position: relative; + text-align: center; +} + +/* reset screen-reader-text */ +.pagination .current .screen-reader-text { + position: static !important; +} + +.pagination .page-numbers { + display: none; + line-height: 3.2em; + padding: 0 0.6667em; +} + +.pagination .page-numbers.current { + text-transform: uppercase; +} + +.pagination .current { + display: inline-block; + font-weight: 700; +} + +.pagination .prev, +.pagination .next { + -webkit-tap-highlight-color: rgba(255, 255, 255, 0.3); + background-color: #333; + color: #fff; + display: inline-block; + height: 48px; + overflow: hidden; + padding: 0; + position: absolute; + width: 48px; +} + +.pagination .prev:before, +.pagination .next:before { + font-size: 32px; + height: 48px; + line-height: 48px; + position: relative; + width: 48px; +} + +.pagination .prev:hover, +.pagination .prev:focus, +.pagination .next:hover, +.pagination .next:focus { + background-color: #707070; + background-color: rgba(51, 51, 51, 0.7); +} + +.pagination .prev { + left: 0; +} + +.pagination .prev:before { + content: "\f430"; + left: -1px; +} + +.pagination .next { + right: 0; +} + +.pagination .next:before { + content: "\f429"; + right: -1px; +} + +.image-navigation, +.comment-navigation { + color: #707070; + color: rgba(51, 51, 51, 0.7); + font-size: 12px; + font-size: 1.2rem; + font-family: "Noto Sans", sans-serif; + font-weight: 700; + line-height: 1.5; + text-transform: uppercase; +} + +.image-navigation a, +.comment-navigation a { + color: #707070; + color: rgba(51, 51, 51, 0.7); +} + +.image-navigation a:hover, +.image-navigation a:focus, +.comment-navigation a:hover, +.comment-navigation a:focus { + color: #333; +} + +.image-navigation .nav-previous:not(:empty), +.image-navigation .nav-next:not(:empty), +.comment-navigation .nav-previous:not(:empty), +.comment-navigation .nav-next:not(:empty) { + display: inline-block; +} + +.image-navigation .nav-previous:not(:empty) + .nav-next:not(:empty):before, +.comment-navigation .nav-previous:not(:empty) + .nav-next:not(:empty):before { + content: "\2215"; + font-weight: 400; + margin: 0 0.7em; +} + +.image-navigation .nav-previous a:before, +.comment-navigation .nav-previous a:before { + content: "\f430"; + margin-right: 0.2em; + position: relative; +} + +.image-navigation .nav-next a:after, +.comment-navigation .nav-next a:after { + content: "\f429"; + margin-left: 0.2em; + position: relative; +} + +.comment-navigation { + border-top: 1px solid #eaeaea; + border-top: 1px solid rgba(51, 51, 51, 0.1); + border-bottom: 1px solid #eaeaea; + border-bottom: 1px solid rgba(51, 51, 51, 0.1); + padding: 2em 0; +} + +.comments-title + .comment-navigation { + border-bottom: 0; +} + +.image-navigation { + padding: 0 7.6923%; +} + +.image-navigation .nav-previous:not(:empty), +.image-navigation .nav-next:not(:empty) { + margin-bottom: 2em; +} + + +/** + * 7.0 Accessibility + */ + +/* Text meant only for screen readers */ +.says, +.screen-reader-text { + clip: rect(1px, 1px, 1px, 1px); + height: 1px; + overflow: hidden; + position: absolute !important; + width: 1px; +} + +/* must have higher specificity than alternative color schemes inline styles */ +.site .skip-link { + background-color: #f1f1f1; + box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.2); + color: #21759b; + display: block; + font: bold 14px/normal "Noto Sans", sans-serif; + left: -9999em; + outline: none; + padding: 15px 23px 14px; + text-decoration: none; + text-transform: none; + top: -9999em; +} + +.logged-in .site .skip-link { + box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.6); + font: bold 14px/normal "Open Sans", sans-serif; +} + +.site .skip-link:focus { + clip: auto; + height: auto; + left: 6px; + top: 7px; + width: auto; + z-index: 100000; +} + + +/** + * 8.0 Alignments + */ + +.alignleft { + display: inline; + float: left; +} + +.alignright { + display: inline; + float: right; +} + +.aligncenter { + display: block; + margin-right: auto; + margin-left: auto; +} + +blockquote.alignleft, +.wp-caption.alignleft, +img.alignleft { + margin: 0.4em 1.6em 1.6em 0; +} + +blockquote.alignright, +.wp-caption.alignright, +img.alignright { + margin: 0.4em 0 1.6em 1.6em; +} + +blockquote.aligncenter, +.wp-caption.aligncenter, +img.aligncenter { + clear: both; + margin-top: 0.4em; + margin-bottom: 1.6em; +} + +.wp-caption.alignleft, +.wp-caption.alignright, +.wp-caption.aligncenter { + margin-bottom: 1.2em; +} + + +/** + * 9.0 Clearings + */ + +.clear:before, +.clear:after, +.site:before, +.site:after, +.entry-content:before, +.entry-content:after, +.comment-content:before, +.comment-content:after, +.site-content:before, +.site-content:after, +.nav-links:before, +.nav-links:after, +.comment-navigation:before, +.comment-navigation:after, +.social-navigation ul:before, +.social-navigation ul:after, +.textwidget:before, +.textwidget:after { + content: ""; + display: table; +} + +.clear:after, +.site:after, +.entry-content:after, +.comment-content:after, +.site-content:after, +.nav-links:after, +.comment-navigation:after, +.social-navigation ul:after, +.textwidget:after { + clear: both; +} + + +/** + * 10.0 Header + */ + +.site-header { + background-color: #fff; + border-bottom: 1px solid rgba(51, 51, 51, 0.1); + padding: 7.6923%; +} + +.site-branding { + min-height: 2em; + padding-right: 60px; + position: relative; +} + +.site-title { + font-family: "Noto Sans", sans-serif; + font-size: 22px; + font-size: 2.2rem; + font-weight: 700; + line-height: 1.3636; + margin-bottom: 0; +} + +.site-description { + display: none; + font-family: "Noto Sans", sans-serif; + font-size: 12px; + font-size: 1.2rem; + font-weight: 400; + line-height: 1.5; + margin: 0.5em 0 0; + opacity: 0.7; +} + + +/** + * 11.0 Widgets + */ + +.widget { + color: #707070; + color: rgba(51, 51, 51, 0.7); + -webkit-hyphens: auto; + -moz-hyphens: auto; + -ms-hyphens: auto; + hyphens: auto; + margin: 0 auto 9.09090%; + width: 100%; + word-wrap: break-word; +} + +.widget pre { + line-height: 1.2; +} + +.widget button, +.widget input, +.widget select, +.widget textarea { + font-size: 16px; + font-size: 1.6rem; + line-height: 1.5; +} + +.widget button, +.widget input { + line-height: normal; +} + +.widget button, +.widget input[type="button"], +.widget input[type="reset"], +.widget input[type="submit"] { + font-size: 12px; + font-size: 1.2rem; + padding: 0.7917em 1.5833em; +} + +.widget input[type="text"], +.widget input[type="email"], +.widget input[type="url"], +.widget input[type="password"], +.widget input[type="search"], +.widget textarea { + padding: 0.375em; +} + +.widget-title { + color: #333; + font-family: "Noto Sans", sans-serif; + margin: 0 0 1.6em; + letter-spacing: 0.04em; + text-transform: uppercase; +} + +.widget > :last-child { + margin-bottom: 0; +} + +.widget_calendar table { + margin: 0; +} + +.widget_calendar td, +.widget_calendar th { + line-height: 2.3333; + text-align: center; + padding: 0; +} + +.widget_calendar caption { + font-family: "Noto Serif", serif; + font-weight: 700; + margin: 0 0 1.6em; + letter-spacing: 0.04em; + text-transform: uppercase; +} + +.widget_calendar tbody a { + -webkit-tap-highlight-color: rgba(255, 255, 255, 0.3); + background-color: #333; + color: #fff; + display: block; + font-weight: 700; +} + +.widget_calendar tbody a:hover, +.widget_calendar tbody a:focus { + background-color: #707070; + background-color: rgba(51, 51, 51, 0.7); + color: #fff; +} + +.widget_archive a, +.widget_categories a, +.widget_links a, +.widget_meta a, +.widget_nav_menu a, +.widget_pages a, +.widget_recent_comments a, +.widget_recent_entries a { + border: 0; +} + +.widget_archive ul, +.widget_categories ul, +.widget_links ul, +.widget_meta ul, +.widget_nav_menu ul, +.widget_pages ul, +.widget_recent_comments ul, +.widget_recent_entries ul { + list-style: none; + margin: 0; +} + +.widget_archive li, +.widget_categories li, +.widget_links li, +.widget_meta li, +.widget_nav_menu li, +.widget_pages li, +.widget_recent_comments li, +.widget_recent_entries li { + border-top: 1px solid #eaeaea; + border-top: 1px solid rgba(51, 51, 51, 0.1); + padding: 0.7667em 0; +} + +.widget_archive li:first-child, +.widget_categories li:first-child, +.widget_links li:first-child, +.widget_meta li:first-child, +.widget_nav_menu li:first-child, +.widget_pages li:first-child, +.widget_recent_comments li:first-child, +.widget_recent_entries li:first-child { + border-top: 0; + padding-top: 0; +} + +.widget_archive li:last-child, +.widget_categories li:last-child, +.widget_links li:last-child, +.widget_meta li:last-child, +.widget_nav_menu li:last-child, +.widget_pages li:last-child, +.widget_recent_comments li:last-child, +.widget_recent_entries li:last-child { + padding-bottom: 0; +} + +.widget_categories .children, +.widget_nav_menu .sub-menu, +.widget_pages .children { + border-top: 1px solid #eaeaea; + border-top: 1px solid rgba(51, 51, 51, 0.1); + margin: 0.7667em 0 0 0.8em; + padding-top: 0.7667em; +} + +.widget_recent_entries .post-date { + display: block; +} + +.widget_rss ul { + list-style: none; + margin: 0; +} + +.widget_rss li { + margin-bottom: 1.6em; +} + +.widget_rss ul:last-child, +.widget_rss li:last-child { + margin-bottom: 0; +} + +.widget_rss .rsswidget { + border: 0; + font-weight: 700; +} + +.widget_rss .rsswidget img { + margin-top: -4px; +} + +.widget_rss .rss-date, +.widget_rss cite { + font-family: "Noto Sans", sans-serif; + font-size: 12px; + font-size: 1.2rem; + font-style: normal; + display: block; + line-height: 2; + opacity: 0.8; +} + +.textwidget > :last-child { + margin-bottom: 0; +} + +.textwidget a { + border-bottom: 1px solid #333; +} + +.textwidget a:hover, +.textwidget a:focus { + border-bottom: 0; +} + + +/** + * 12.0 Content + */ + +.secondary { + background-color: #fff; + display: none; + padding: 0 7.6923%; +} + +.secondary.toggled-on { + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + display: block; +} + +.widget-area { + margin: 9.09090% auto 0; +} + +.site-footer { + background-color: #fff; + border-top: 1px solid rgba(51, 51, 51, 0.1); + padding: 3.84615% 7.6923%; +} + + +/** + * 12.1 Posts and pages + */ + +.hentry { + background-color: #fff; + padding-top: 7.6923%; + position: relative; +} + +.hentry.has-post-thumbnail { + padding-top: 0; +} + +.hentry.sticky:not(.has-post-thumbnail) { + padding-top: -webkit-calc(7.6923% + 24px); + padding-top: calc(7.6923% + 24px); +} + +.hentry + .hentry { + border-top: 1px solid rgba(51, 51, 51, 0.1); +} + +.post-thumbnail { + border: 0; + display: block; + margin-bottom: 2.4em; +} +.post-thumbnail img { + display: block; + margin: 0 auto; +} + +a.post-thumbnail:hover, +a.post-thumbnail:focus { + opacity: 0.85; +} + +.entry-header { + padding: 0 7.6923%; +} + +.entry-title { + font-size: 26px; + font-size: 2.6rem; + line-height: 1.1538; + margin-bottom: 0.9231em; +} + +.entry-content, +.entry-summary { + padding: 0 7.6923% 7.6923%; +} + +.entry-content > :last-child, +.entry-summary > :last-child { + margin-bottom: 0; +} + +.entry-content, +.entry-summary, +.page-content, +.comment-content { + -webkit-hyphens: auto; + -moz-hyphens: auto; + -ms-hyphens: auto; + hyphens: auto; + word-wrap: break-word; +} + +.entry-content h1, +.entry-summary h1, +.page-content h1, +.comment-content h1 { + font-size: 26px; + font-size: 2.6rem; + line-height: 1.1538; + margin-top: 1.8462em; + margin-bottom: 0.9231em; +} + +.entry-content h2, +.entry-summary h2, +.page-content h2, +.comment-content h2 { + font-size: 22px; + font-size: 2.2rem; + line-height: 1.3636; + margin-top: 2.1818em; + margin-bottom: 1.0909em; +} + +.entry-content h3, +.entry-summary h3, +.page-content h3, +.comment-content h3 { + font-size: 18px; + font-size: 1.8rem; + line-height: 1.3333; + margin-top: 2.6667em; + margin-bottom: 1.3333em; +} + +.entry-content h4, +.entry-content h5, +.entry-content h6, +.entry-summary h4, +.entry-summary h5, +.entry-summary h6, +.page-content h4, +.page-content h5, +.page-content h6, +.comment-content h4, +.comment-content h5, +.comment-content h6 { + font-size: 15px; + font-size: 1.5rem; + line-height: 1.2; + margin-top: 3.2em; + margin-bottom: 1.6em; +} + +.entry-content h5, +.entry-content h6, +.entry-summary h5, +.entry-summary h6, +.page-content h5, +.page-content h6, +.comment-content h5, +.comment-content h6 { + letter-spacing: 0.1em; + text-transform: uppercase; +} + +.entry-content > h1:first-child, +.entry-content > h2:first-child, +.entry-content > h3:first-child, +.entry-content > h4:first-child, +.entry-content > h5:first-child, +.entry-content > h6:first-child, +.entry-summary > h1:first-child, +.entry-summary > h2:first-child, +.entry-summary > h3:first-child, +.entry-summary > h4:first-child, +.entry-summary > h5:first-child, +.entry-summary > h6:first-child, +.page-content > h1:first-child, +.page-content > h2:first-child, +.page-content > h3:first-child, +.page-content > h4:first-child, +.page-content > h5:first-child, +.page-content > h6:first-child, +.comment-content > h1:first-child, +.comment-content > h2:first-child, +.comment-content > h3:first-child, +.comment-content > h4:first-child, +.comment-content > h5:first-child, +.comment-content > h6:first-child { + margin-top: 0; +} + +.entry-content a, +.entry-summary a, +.page-content a, +.comment-content a, +.pingback .comment-body > a { + border-bottom: 1px solid #333; +} + +.entry-content a:hover, +.entry-content a:focus, +.entry-summary a:hover, +.entry-summary a:focus, +.page-content a:hover, +.page-content a:focus, +.comment-content a:hover, +.comment-content a:focus, +.pingback .comment-body > a:hover, +.pingback .comment-body > a:focus { + border-bottom: 0; +} + +.entry-content a img, +.entry-summary a img, +.page-content a img, +.comment-content a img { + display: block; +} + +.entry-content .more-link, +.entry-summary .more-link:after { + white-space: nowrap; +} + +.entry-content .more-link:after, +.entry-summary .more-link:after { + content: "\f429"; + font-size: 16px; + position: relative; + top: 5px; +} + +.author-info { + border-top: 1px solid #eaeaea; + border-top: 1px solid rgba(51, 51, 51, 0.1); + margin: 0 7.6923%; + padding: 7.6923% 0; +} + +.author-info .avatar { + float: left; + height: 36px; + margin: 0 1.6em 1.6em 0; + width: 36px; +} + +.author-heading { + color: #707070; + color: rgba(51, 51, 51, 0.7); + font-family: "Noto Sans", sans-serif; + font-size: 12px; + font-size: 1.2rem; + letter-spacing: 0.04em; + margin-bottom: 1.5em; + text-transform: uppercase; +} + +.author-title { + clear: none; +} + +.author-bio { + font-size: 12px; + font-size: 1.2rem; + line-height: 1.5; + overflow: hidden; + padding-bottom: 1px; +} + +.author-description { + -webkit-hyphens: auto; + -moz-hyphens: auto; + -ms-hyphens: auto; + hyphens: auto; + word-wrap: break-word; +} + +.author-description a { + border-bottom: 1px solid #333; +} + +.author-description a:hover, +.author-description a:focus { + border-bottom: 0; +} + +.author-description > :last-child { + margin-bottom: 0; +} + +.author-link { + white-space: nowrap; +} + +.author-link:after { + content: "\f429"; + position: relative; + top: 1px; +} + +.entry-footer { + background-color: #f7f7f7; + color: #707070; + color: rgba(51, 51, 51, 0.7); + font-family: "Noto Sans", sans-serif; + font-size: 12px; + font-size: 1.2rem; + line-height: 1.5; + padding: 3.8461% 7.6923%; +} + +.entry-footer a { + border-bottom: 1px solid transparent; + color: #707070; + color: rgba(51, 51, 51, 0.7); +} + +.entry-footer a:hover { + border-bottom: 1px solid #333; +} + +.entry-footer a:hover, +.entry-footer a:focus { + color: #333; +} + +.sticky-post { + background-color: #333; + color: #fff; + font-weight: 700; + letter-spacing: 0.04em; + padding: 0.25em 0.5em; + position: absolute; + top: 0; + text-transform: uppercase; +} + +.updated:not(.published) { + display: none; +} + +.sticky .posted-on { + display: none; +} + +.posted-on:before, +.byline:before, +.cat-links:before, +.tags-links:before, +.comments-link:before, +.entry-format:before, +.edit-link:before, +.full-size-link:before { + margin-right: 2px; + position: relative; +} + +.posted-on, +.byline, +.cat-links, +.tags-links, +.comments-link, +.entry-format, +.full-size-link { + margin-right: 1em; +} + +.format-aside .entry-format:before { + content: "\f101"; +} + +.format-image .entry-format:before { + content: "\f473"; +} + +.format-gallery .entry-format:before { + content: "\f103"; +} + +.format-video .entry-format:before { + content: "\f104"; +} + +.format-status .entry-format:before { + content: "\f105"; +} + +.format-quote .entry-format:before { + content: "\f106"; +} + +.format-link .entry-format:before { + content: "\f107"; +} + +.format-chat .entry-format:before { + content: "\f108"; +} + +.format-audio .entry-format:before { + content: "\f109"; +} + +.posted-on:before { + content: "\f307"; +} + +.byline:before { + content: "\f304"; +} + +.cat-links:before { + content: "\f301"; +} + +.tags-links:before { + content: "\f302"; +} + +.comments-link:before { + content: "\f300"; +} + +.full-size-link:before { + content: "\f402"; +} + +.edit-link:before { + content: "\f411"; +} + +.comments-link, +.edit-link { + white-space: nowrap; +} + +.page-header { + background-color: #fff; + border-bottom: 1px solid rgba(51, 51, 51, 0.1); + padding: 7.6923%; +} + +.page-title { + font-family: "Noto Serif", serif; + font-size: 18px; + font-size: 1.8rem; + line-height: 1.3333; +} + +.taxonomy-description { + color: #707070; + color: rgba(51, 51, 51, 0.7); + padding-top: 0.4em; +} + +.taxonomy-description a { + border-bottom: 1px solid #333; +} + +.taxonomy-description a:hover, +.taxonomy-description a:focus { + border-bottom: 0; +} + +.taxonomy-description > :last-child { + margin-bottom: 0; +} + +.page-content { + background-color: #fff; + padding: 7.6923%; +} + +.page-content > :last-child { + margin-bottom: 0; +} + +.page-links { + clear: both; + font-family: "Noto Sans", sans-serif; + margin-bottom: 1.3333em; +} + +.page-links a, +.page-links > span { + border: 1px solid #eaeaea; + border: 1px solid rgba(51, 51, 51, 0.1); + display: inline-block; + font-size: 12px; + font-size: 1.2rem; + height: 2em; + line-height: 2; + margin: 0 0.3333em 0.3333em 0; + text-align: center; + width: 2em; +} + +.page-links a { + -webkit-tap-highlight-color: rgba(255, 255, 255, 0.3); + background-color: #333; + border-color: #333; + color: #fff; +} + +.page-links a:hover, +.page-links a:focus { + background-color: #707070; + background-color: rgba(51, 51, 51, 0.7); + border-color: transparent; + color: #fff; +} + +.page-links > .page-links-title { + border: 0; + color: #707070; + color: rgba(51, 51, 51, 0.7); + height: auto; + margin: 0; + padding-right: 0.5em; + width: auto; +} + +.entry-attachment { + margin-bottom: 1.6em; +} + +.type-attachment .entry-title { + -webkit-hyphens: auto; + -moz-hyphens: auto; + -ms-hyphens: auto; + hyphens: auto; + word-wrap: break-word; +} + +.entry-caption { + color: #707070; + color: rgba(51, 51, 51, 0.7); + font-family: "Noto Sans", sans-serif; + font-size: 12px; + font-size: 1.2rem; + -webkit-hyphens: auto; + -moz-hyphens: auto; + -ms-hyphens: auto; + hyphens: auto; + line-height: 1.5; + padding-top: 0.5em; + word-wrap: break-word; +} + +.entry-caption > :last-child { + margin-bottom: 0; +} + + +/** + * 12.2 Post Formats + */ + +.format-aside .entry-title, +.format-image .entry-title, +.format-video .entry-title, +.format-quote .entry-title, +.format-gallery .entry-title, +.format-status .entry-title, +.format-link .entry-title, +.format-audio .entry-title, +.format-chat .entry-title { + font-size: 18px; + font-size: 1.8rem; + line-height: 1.3333; + margin-bottom: 1.3333em; +} + +.format-link .entry-title a:after { + content: "\f442"; + font-size: 24px; + height: 24px; + position: relative; + top: 0; + width: 24px; +} + +.blog .format-status .entry-title, +.archive .format-status .entry-title { + display: none; +} + + +/** + * 12.3 Comments + */ + +.comments-area { + background-color: #fff; + border-top: 1px solid #eaeaea; + border-top: 1px solid rgba(51, 51, 51, 0.1); + padding: 7.6923%; +} + +.comments-area > :last-child { + margin-bottom: 0; +} + +.comment-list + .comment-respond { + border-top: 1px solid #eaeaea; + border-top: 1px solid rgba(51, 51, 51, 0.1); +} + +.comment-list + .comment-respond, +.comment-navigation + .comment-respond { + padding-top: 1.6em; +} + +.comments-title, +.comment-reply-title { + font-family: "Noto Serif", serif; + font-size: 18px; + font-size: 1.8rem; + line-height: 1.3333; +} + +.comments-title { + margin-bottom: 1.3333em; +} + +.comment-list { + list-style: none; + margin: 0; +} + +.comment-list article, +.comment-list .pingback, +.comment-list .trackback { + border-top: 1px solid #eaeaea; + border-top: 1px solid rgba(51, 51, 51, 0.1); + padding: 1.6em 0; +} + +.comment-list .children { + list-style: none; + margin: 0; +} + +.comment-list .children > li { + padding-left: 0.8em; +} + +.comment-author { + color: #707070; + color: rgba(51, 51, 51, 0.7); + margin-bottom: 0.4em; +} + +.comment-author a:hover { + border-bottom: 1px solid #707070; + border-bottom: 1px solid rgba(51, 51, 51, 0.7); +} + +.comment-author .avatar { + float: left; + height: 24px; + margin-right: 0.8em; + width: 24px; +} + +.bypostauthor > article .fn:after { + content: "\f304"; + position: relative; + top: 5px; + left: 3px; +} + +.comment-metadata, +.pingback .edit-link { + color: #707070; + color: rgba(51, 51, 51, 0.7); + font-family: "Noto Sans", sans-serif; + font-size: 12px; + font-size: 1.2rem; + line-height: 1.5; +} + +.comment-metadata a, +.pingback .edit-link a { + color: #707070; + color: rgba(51, 51, 51, 0.7); +} + +.comment-metadata a:hover, +.pingback .edit-link a:hover { + border-bottom: 1px solid #333; +} + +.comment-metadata a:hover, +.comment-metadata a:focus, +.pingback .edit-link a:hover, +.pingback .edit-link a:focus { + color: #333; +} + +.comment-metadata { + margin-bottom: 1.6em; +} + +.comment-metadata .edit-link { + margin-left: 1em; +} + +.pingback .edit-link { + margin-left: 1em; +} + +.pingback .edit-link:before { + top: 5px; +} + +.comment-content ul, +.comment-content ol { + margin: 0 0 1.6em 1.3333em; +} + +.comment-content li > ul, +.comment-content li > ol { + margin-bottom: 0; +} + +.comment-content > :last-child { + margin-bottom: 0; +} + +.comment-list .reply { + font-size: 12px; + font-size: 1.2rem; +} + +.comment-list .reply a { + border: 1px solid #eaeaea; + border: 1px solid rgba(51, 51, 51, 0.1); + color: #707070; + color: rgba(51, 51, 51, 0.7); + display: inline-block; + font-family: "Noto Sans", sans-serif; + font-weight: 700; + line-height: 1; + margin-top: 2em; + padding: 0.4167em 0.8333em; + text-transform: uppercase; +} + +.comment-list .reply a:hover, +.comment-list .reply a:focus { + border-color: #333; + color: #333; + outline: 0; +} + +.comment-form { + padding-top: 1.6em; +} + +.comment-form label { + color: #707070; + color: rgba(51, 51, 51, 0.7); + font-family: "Noto Sans", sans-serif; + font-size: 12px; + font-size: 1.2rem; + font-weight: 700; + display: block; + letter-spacing: 0.04em; + line-height: 1.5; + text-transform: uppercase; +} + +.comment-form input[type="text"], +.comment-form input[type="email"], +.comment-form input[type="url"], +.comment-form input[type="submit"] { + width: 100%; +} + +.comment-notes, +.comment-awaiting-moderation, +.logged-in-as, +.form-allowed-tags { + color: #707070; + color: rgba(51, 51, 51, 0.7); + font-family: "Noto Sans", sans-serif; + font-size: 12px; + font-size: 1.2rem; + line-height: 1.5; + margin-bottom: 2em; +} + +.logged-in-as a:hover { + border-bottom: 1px solid #333; +} + +.no-comments { + border-top: 1px solid #eaeaea; + border-top: 1px solid rgba(51, 51, 51, 0.1); + color: #707070; + color: rgba(51, 51, 51, 0.7); + font-family: "Noto Sans", sans-serif; + font-weight: 700; + padding-top: 1.6em; +} + +.comment-navigation + .no-comments { + border-top: 0; +} + +.form-allowed-tags code { + font-family: Inconsolata, monospace; +} + +.form-submit { + margin-bottom: 0; +} + +.required { + color: #c0392b; +} + +.comment-reply-title small { + font-size: 100%; +} + +.comment-reply-title small a { + border: 0; + float: right; + height: 32px; + overflow: hidden; + width: 26px; +} + +.comment-reply-title small a:before { + content: "\f405"; + font-size: 32px; + position: relative; + top: -3px; +} + + +/** + * 13.0 Footer + */ + +.site-info { + color: #707070; + color: rgba(51, 51, 51, 0.7); + font-size: 12px; + font-size: 1.2rem; + line-height: 1.5; +} + +.site-info a { + border-bottom: 1px solid transparent; + color: #707070; + color: rgba(51, 51, 51, 0.7); +} + +.site-info a:hover { + border-bottom: 1px solid #333; +} + +.site-info a:hover, +.site-info a:focus { + color: #333; +} + + +/** + * 14.0 Media + */ + +.site .avatar { + border-radius: 50%; +} + +.page-content img.wp-smiley, +.entry-content img.wp-smiley, +.comment-content img.wp-smiley { + border: none; + margin-top: 0; + margin-bottom: 0; + padding: 0; +} + +audio, +canvas { + display: inline-block; +} + +embed, +iframe, +object, +video { + margin-bottom: 1.6em; + max-width: 100%; + vertical-align: middle; +} + +p > embed, +p > iframe, +p > object, +p > video { + margin-bottom: 0; +} + +.wp-audio-shortcode, +.wp-video, +.wp-playlist.wp-audio-playlist { + font-size: 15px; + font-size: 1.5rem; + margin-top: 0; + margin-bottom: 1.6em; +} + +.wp-playlist.wp-playlist { + padding-bottom: 0; +} + +.wp-playlist .wp-playlist-tracks { + margin-top: 0; +} + +.wp-playlist-item .wp-playlist-caption { + border-bottom: 0; + padding: 10px 0; +} + +.wp-playlist-item .wp-playlist-item-length { + top: 10px; +} + + +/** + * 14.1 Captions + */ + +.wp-caption { + margin-bottom: 1.6em; + max-width: 100%; +} + +.wp-caption img[class*="wp-image-"] { + display: block; + margin: 0; +} + +.wp-caption-text { + color: #707070; + color: rgba(51, 51, 51, 0.7); + font-family: "Noto Sans", sans-serif; + font-size: 12px; + font-size: 1.2rem; + line-height: 1.5; + padding: 0.5em 0; +} + + +/** + * 14.2 Galleries + */ + +.gallery { + margin-bottom: 1.6em; +} + +.gallery-item { + display: inline-block; + padding: 1.79104477%; + text-align: center; + vertical-align: top; + width: 100%; +} + +.gallery-columns-2 .gallery-item { + max-width: 50%; +} + +.gallery-columns-3 .gallery-item { + max-width: 33.33%; +} + +.gallery-columns-4 .gallery-item { + max-width: 25%; +} + +.gallery-columns-5 .gallery-item { + max-width: 20%; +} + +.gallery-columns-6 .gallery-item { + max-width: 16.66%; +} + +.gallery-columns-7 .gallery-item { + max-width: 14.28%; +} + +.gallery-columns-8 .gallery-item { + max-width: 12.5%; +} + +.gallery-columns-9 .gallery-item { + max-width: 11.11%; +} + +.gallery-icon img { + margin: 0 auto; +} + +.gallery-caption { + color: #707070; + color: rgba(51, 51, 51, 0.7); + display: block; + font-family: "Noto Sans", sans-serif; + font-size: 12px; + font-size: 1.2rem; + line-height: 1.5; + padding: 0.5em 0; +} + +.gallery-columns-6 .gallery-caption, +.gallery-columns-7 .gallery-caption, +.gallery-columns-8 .gallery-caption, +.gallery-columns-9 .gallery-caption { + display: none; +} + + +/** + * 15.0 Media Queries + */ + +/* + * Does the same thing as , + * but in the future W3C standard way. -ms- prefix is required for IE10+ to + * render responsive styling in Windows 8 "snapped" views; IE10+ does not honor + * the meta tag. See https://core.trac.wordpress.org/ticket/25888. + */ +@-ms-viewport { + width: device-width; +} + +@viewport { + width: device-width; +} + +/** + * 15.1 Mobile Large 620px + */ + +@media screen and (min-width: 38.75em) { + ul, + ol { + margin-left: 0; + } + + li > ul, + li > ol, + blockquote > ul, + blockquote > ol { + margin-left: 1.3333em; + } + + blockquote { + margin-left: -1em; + } + + blockquote > blockquote { + margin-left: 0; + } + + .site-branding { + min-height: 3.2em; + } + + .site-title { + font-size: 22px; + font-size: 2.2rem; + line-height: 1.0909; + } + + .site-description { + display: block; + } + + .secondary { + margin: 7.6923% 7.6923% 0; + padding: 7.6923% 7.6923% 0; + } + + .main-navigation { + margin-bottom: 11.1111%; + } + + .main-navigation ul { + border-top: 1px solid rgba(51, 51, 51, 0.1); + border-bottom: 1px solid rgba(51, 51, 51, 0.1); + } + + .main-navigation ul ul { + border-top: 0; + border-bottom: 0; + } + + .social-navigation { + margin-bottom: 11.1111%; + } + + .social-navigation { + margin-top: 0; + } + + .widget-area { + margin-top: 0; + } + + .widget { + margin-bottom: 11.1111%; + } + + .site-main { + padding: 7.6923% 0; + } + + .hentry.sticky:not(.has-post-thumbnail) { + padding-top: inherit; + } + + .hentry, + .page-header, + .page-content { + box-shadow: 0 0 1px rgba(0, 0, 0, 0.15); + margin: 0 7.6923%; + } + + .hentry + .hentry, + .page-header + .hentry, + .page-header + .page-content { + margin-top: 7.6923%; + } + + .hentry + .hentry { + border-top: 0; + } + + .post-thumbnail { + margin-bottom: 2.4em; + } + + .entry-header { + padding: 0 9.0909%; + } + + .entry-content, + .entry-summary { + padding: 0 9.0909% 9.0909%; + } + + .entry-footer { + padding: 4.5454% 9.0909%; + } + + .page-header { + border-bottom: 0; + border-left: 7px solid #333; + padding: 3.8461% 7.6923%; + } + + .page-title, + .taxonomy-description { + margin-left: -7px; + } + + .page-content { + padding: 9.0909%; + } + + .site-footer { + border-top: 0; + box-shadow: 0 0 1px rgba(0, 0, 0, 0.15); + margin: 0 7.6923%; + padding: 3.84615% 7.6923%; + } + + .post-navigation { + border-top: 0; + box-shadow: 0 0 1px rgba(0, 0, 0, 0.15); + margin: 7.6923% 7.6923% 0; + } + + .post-navigation a { + padding: 4.5454% 9.0909%; + } + + .pagination { + border-top: 0; + box-shadow: 0 0 1px rgba(0, 0, 0, 0.15); + margin: 7.6923% 7.6923% 0; + padding: 0; + } + + /* restore screen-reader-text */ + .pagination .current .screen-reader-text { + position: absolute !important; + } + + .pagination .page-numbers { + display: inline-block; + } + + .image-navigation { + padding: 0 9.0909%; + } + + .comments-area { + border-top: 0; + box-shadow: 0 0 1px rgba(0, 0, 0, 0.15); + margin: 7.6923% 7.6923% 0; + } + + .comment-content ul, + .comment-content ol { + margin-left: 0; + } + + .comment-content li > ul, + .comment-content li > ol, + .comment-content blockquote > ul, + .comment-content blockquote > ol { + margin-left: 1.3333em; + } +} + + +/** + * 15.2 Tablet Small 740px + */ + +@media screen and (min-width: 46.25em) { + body, + button, + input, + select, + textarea { + font-size: 17px; + font-size: 1.7rem; + line-height: 1.6471; + } + + button, + input { + line-height: normal; + } + + p, + address, + pre, + hr, + ul, + ol, + dl, + dd, + table { + margin-bottom: 1.6471em; + } + + blockquote { + font-size: 20px; + font-size: 2rem; + line-height: 1.75; + margin-bottom: 1.75em; + margin-left: -1.05em; + padding-left: 0.85em; + } + + blockquote p { + margin-bottom: 1.75em; + } + + blockquote cite, + blockquote small { + font-size: 17px; + font-size: 1.7rem; + line-height: 1.6471; + } + + pre { + line-height: 1.2353; + } + + button, + input[type="button"], + input[type="reset"], + input[type="submit"], + .post-password-form input[type="submit"] { + font-size: 14px; + font-size: 1.4rem; + padding: 0.8214em 1.6429em; + } + + input[type="text"], + input[type="email"], + input[type="url"], + input[type="password"], + input[type="search"], + textarea { + padding: 0.5em; + } + + .main-navigation { + font-size: 14px; + font-size: 1.4rem; + line-height: 1.5; + } + + .main-navigation a { + padding: 1em 0; + } + + .main-navigation ul ul { + margin-left: 1em; + } + + .main-navigation .page_item_has_children > a, + .main-navigation .main-navigation .menu-item-has-children > a { + padding-right: 54px; + } + + .main-navigation .menu-item-description { + font-size: 14px; + font-size: 1.4rem; + line-height: 1.5; + } + + .social-navigation ul { + margin-bottom: -1.4706em; + } + + .social-navigation a { + height: 2.8824em; + width: 2.8824em; + } + + .secondary-toggle { + height: 56px; + width: 56px; + } + + .secondary-toggle:before { + line-height: 54px; + width: 54px; + } + + .post-password-form label, + .post-navigation .meta-nav, + .image-navigation, + .comment-navigation, + .author-heading, + .author-bio, + .entry-footer, + .page-links a, + .page-links span, + .comment-metadata, + .pingback .edit-link, + .comment-list .reply, + .comment-notes, + .comment-awaiting-moderation, + .logged-in-as, + .comment-form label, + .form-allowed-tags, + .site-info, + .wp-caption-text, + .gallery-caption, + .entry-caption { + font-size: 14px; + font-size: 1.4rem; + } + + .pagination .nav-links { + min-height: 3.2941em; + } + + .pagination .page-numbers { + line-height: 3.2941em; + padding: 0 0.8235em; + } + + .pagination .prev, + .pagination .next { + height: 56px; + padding: 0; + width: 56px; + } + + .pagination .prev:before, + .pagination .next:before { + height: 56px; + line-height: 56px; + width: 56px; + } + + .image-navigation .nav-previous a:before, + .image-navigation .nav-next a:after, + .comment-navigation .nav-previous a:before, + .comment-navigation .nav-next a:after { + top: 2px; + } + + blockquote.alignleft, + .wp-caption.alignleft, + img.alignleft { + margin: 0.4118em 1.6471em 1.6471em 0; + } + + blockquote.alignright, + .wp-caption.alignright, + img.alignright { + margin: 0.4118em 0 1.6471em 1.6471em; + } + + blockquote.aligncenter, + .wp-caption.aligncenter, + img.aligncenter { + margin-top: 0.4118em; + margin-bottom: 1.6471em; + } + + .wp-caption.alignleft, + .wp-caption.alignright, + .wp-caption.aligncenter { + margin-bottom: 1.2353em; + } + + .site-branding { + min-height: 3.7059em; + padding-right: 66px; + } + + .site-title { + font-size: 29px; + font-size: 2.9rem; + line-height: 1.2069; + } + + .site-description { + font-size: 14px; + font-size: 1.4rem; + } + + .widget { + font-size: 14px; + font-size: 1.4rem; + line-height: 1.5; + } + + .widget p, + .widget address, + .widget hr, + .widget ul, + .widget ol, + .widget dl, + .widget dd, + .widget table, + .widget pre { + margin-bottom: 1.5em; + } + + .widget li > ul, + .widget li > ol { + margin-bottom: 0; + } + + .widget blockquote { + font-size: 17px; + font-size: 1.7rem; + line-height: 1.6471; + margin-bottom: 1.6471em; + margin-left: -1.2353em; + padding-left: 1em; + } + + .widget blockquote p { + margin-bottom: 1.6471em; + } + + .widget blockquote cite, + .widget blockquote small { + font-size: 14px; + font-size: 1.4rem; + line-height: 1.5; + } + + .widget blockquote > blockquote { + margin-left: 0; + } + + .widget pre { + line-height: 1.5; + padding: 0.75em; + } + + .widget button, + .widget input, + .widget select, + .widget textarea { + line-height: 1.75; + } + + .widget button, + .widget input { + line-height: normal; + } + + .widget button, + .widget input[type="button"], + .widget input[type="reset"], + .widget input[type="submit"] { + font-size: 14px; + font-size: 1.4rem; + padding: 0.8214em 1.6429em; + } + + .widget input[type="text"], + .widget input[type="email"], + .widget input[type="url"], + .widget input[type="password"], + .widget input[type="search"], + .widget textarea { + padding: 0.5625em; + } + + .widget blockquote.alignleft, + .widget .wp-caption.alignleft, + .widget img.alignleft { + margin: 0.5em 1.5em 1.5em 0; + } + + .widget blockquote.alignright, + .widget .wp-caption.alignright, + .widget img.alignright { + margin: 0.5em 0 1.5em 1.5em; + } + + .widget blockquote.aligncenter, + .widget .wp-caption.aligncenter, + .widget img.aligncenter { + margin-top: 0.5em; + margin-bottom: 1.5em; + } + + .widget .wp-caption.alignleft, + .widget .wp-caption.alignright, + .widget .wp-caption.aligncenter { + margin-bottom: 1em; + } + + .widget-title { + margin: 0 0 1.5em; + } + + .widget_calendar td, + .widget_calendar th { + line-height: 2.9286; + } + + .widget_calendar caption { + margin: 0 0 1.5em; + } + + .widget_archive li, + .widget_categories li, + .widget_links li, + .widget_meta li, + .widget_nav_menu li, + .widget_pages li, + .widget_recent_comments li, + .widget_recent_entries li { + padding: 0.9643em 0; + } + + .widget_categories .children, + .widget_nav_menu .sub-menu, + .widget_pages .children { + margin: 0.9643em 0 0 1em; + padding-top: 0.9643em; + } + + .widget_rss li { + margin-bottom: 1.5em; + } + + .widget_rss .rss-date, + .widget_rss cite { + line-height: 1.75; + } + + .post-thumbnail { + margin-bottom: 3em; + } + + .entry-title { + font-size: 35px; + font-size: 3.5rem; + line-height: 1.2; + margin-bottom: 1.2em; + } + + .entry-content h1, + .entry-summary h1, + .page-content h1, + .comment-content h1 { + font-size: 35px; + font-size: 3.5rem; + line-height: 1.2; + margin-top: 1.6em; + margin-bottom: 0.8em; + } + + .entry-content h2, + .entry-summary h2, + .page-content h2, + .comment-content h2 { + font-size: 29px; + font-size: 2.9rem; + line-height: 1.2069; + margin-top: 1.931em; + margin-bottom: 0.9655em; + } + + .entry-content h3, + .entry-summary h3, + .page-content h3, + .comment-content h3 { + font-size: 24px; + font-size: 2.4rem; + line-height: 1.1667; + margin-top: 2.3333em; + margin-bottom: 1.1667em; + } + + .entry-content h4, + .entry-summary h4, + .page-content h4, + .comment-content h4 { + font-size: 20px; + font-size: 2rem; + line-height: 1.4; + margin-top: 2.8em; + margin-bottom: 1.4em; + } + + .entry-content h5, + .entry-content h6, + .entry-summary h5, + .entry-summary h6, + .page-content h5, + .page-content h6, + .comment-content h5, + .comment-content h6 { + font-size: 17px; + font-size: 1.7rem; + line-height: 1.2353; + margin-top: 3.2941em; + margin-bottom: 1.6471em; + } + + .entry-content .more-link:after, + .entry-summary .more-link:after { + font-size: 24px; + top: 2px; + } + + .author-info { + margin: 0 9.0909%; + padding: 9.0909% 0; + } + + .author-info .avatar { + height: 42px; + margin: 0 1.6471em 1.6471em 0; + width: 42px; + } + + .author-link:after { + top: 3px; + } + + .posted-on:before, + .byline:before, + .cat-links:before, + .tags-links:before, + .comments-link:before, + .entry-format:before, + .edit-link:before, + .full-size-link:before { + top: 3px; + } + + .taxonomy-description { + padding-top: 0.4118em; + } + + .page-title, + .comments-title, + .comment-reply-title, + .post-navigation .post-title { + font-size: 24px; + font-size: 2.4rem; + line-height: 1.1667; + } + + .page-links { + margin-bottom: 1.4117em; + } + + .page-links a, + .page-links > span { + margin: 0 0.2857em 0.2857em 0; + } + + .entry-attachment { + margin-bottom: 1.6471em; + } + + .format-aside .entry-title, + .format-image .entry-title, + .format-video .entry-title, + .format-quote .entry-title, + .format-gallery .entry-title, + .format-status .entry-title, + .format-link .entry-title, + .format-audio .entry-title, + .format-chat .entry-title { + font-size: 20px; + font-size: 2rem; + line-height: 1.4; + margin-bottom: 1.4em; + } + + .format-link .entry-title a:after { + top: 0.0833em; + } + + .comments-title { + margin-bottom: 1.4em; + } + + .comment-list article, + .comment-list .pingback, + .comment-list .trackback { + padding: 1.6471em 0; + } + + .comment-list + .comment-respond, + .comment-navigation + .comment-respond { + padding-top: 1.6471em; + } + + .comment-list .children > li { + padding-left: 1.2353em; + } + + .comment-meta { + position: relative; + } + + .comment-author { + margin-bottom: 0; + } + + .comment-author .avatar { + height: 42px; + margin-right: 1.64705em; + position: relative; + top: 5px; + width: 42px; + } + + .comment-metadata .edit-link:before { + top: 2px; + } + + .pingback .edit-link:before { + top: 6px; + } + + .bypostauthor > article .fn:after { + top: 7px; + left: 6px; + } + + .comment-content ul, + .comment-content ol { + margin-bottom: 1.6471em; + } + + .comment-list .reply a { + padding: 0.4286em 0.8571em; + } + + .comment-form, + .no-comments { + padding-top: 1.6471em; + } + + .comment-reply-title small a:before { + top: -1px; + } + + embed, + iframe, + object, + video { + margin-bottom: 1.6471em; + } + + .wp-audio-shortcode, + .wp-video, + .wp-playlist.wp-audio-playlist { + font-size: 17px; + font-size: 1.7rem; + margin-bottom: 1.6471em; + } + + .wp-caption, + .gallery { + margin-bottom: 1.6471em; + } +} + + +/** + * 15.3 Tablet Large 880px + */ + +@media screen and (min-width: 55em) { + body, + button, + input, + select, + textarea { + font-size: 19px; + font-size: 1.9rem; + line-height: 1.6842; + } + + button, + input { + line-height: normal; + } + + p, + address, + pre, + hr, + ul, + ol, + dl, + dd, + table { + margin-bottom: 1.6842em; + } + + blockquote { + font-size: 22px; + font-size: 2.2rem; + line-height: 1.8182; + margin-bottom: 1.8182em; + margin-left: -1.0909em; + padding-left: 0.9091em; + } + + blockquote p { + margin-bottom: 1.8182em; + } + + blockquote cite, + blockquote small { + font-size: 19px; + font-size: 1.9rem; + line-height: 1.6842; + } + + pre { + line-height: 1.2632; + } + + button, + input[type="button"], + input[type="reset"], + input[type="submit"], + .post-password-form input[type="submit"] { + font-size: 16px; + font-size: 1.6rem; + padding: 0.8125em 1.625em; + } + + input[type="text"], + input[type="email"], + input[type="url"], + input[type="password"], + input[type="search"], + textarea { + padding: 0.5278em; + } + + .main-navigation { + font-size: 16px; + font-size: 1.6rem; + line-height: 1.5; + } + + .main-navigation a { + padding: 0.75em 0; + } + + .main-navigation .page_item_has_children > a, + .main-navigation .main-navigation .menu-item-has-children > a { + padding-right: 53px; + } + + .main-navigation .menu-item-description { + font-size: 16px; + font-size: 1.6rem; + line-height: 1.5; + } + + .social-navigation ul { + margin-bottom: -1.2632em; + } + + .social-navigation a { + height: 2.5263em; + width: 2.5263em; + } + + .secondary-toggle { + height: 64px; + width: 64px; + } + + .secondary-toggle:before { + line-height: 62px; + width: 62px; + } + + .post-password-form label, + .post-navigation .meta-nav, + .comment-navigation, + .image-navigation, + .author-heading, + .author-bio, + .entry-footer, + .page-links a, + .page-links span, + .comment-metadata, + .pingback .edit-link, + .comment-list .reply, + .comment-notes, + .comment-awaiting-moderation, + .logged-in-as, + .comment-form label, + .form-allowed-tags, + .site-info, + .wp-caption-text, + .gallery-caption, + .entry-caption { + font-size: 16px; + font-size: 1.6rem; + } + + .pagination .nav-links { + min-height: 3.3684em; + } + + .pagination .page-numbers { + line-height: 3.3684em; + padding: 0 0.8421em; + } + + .pagination .prev, + .pagination .next { + height: 64px; + padding: 0; + width: 64px; + } + + .pagination .prev:before, + .pagination .next:before { + height: 64px; + line-height: 64px; + width: 64px; + } + + .image-navigation .nav-previous a:before, + .image-navigation .nav-next a:after, + .comment-navigation .nav-previous a:before, + .comment-navigation .nav-next a:after { + font-size: 24px; + top: -1px; + } + + blockquote.alignleft, + .wp-caption.alignleft, + img.alignleft { + margin: 0.4211em 1.6842em 1.6842em 0; + } + + blockquote.alignright, + .wp-caption.alignright, + img.alignright { + margin: 0.4211em 0 1.6842em 1.6842em; + } + + blockquote.aligncenter, + .wp-caption.aligncenter, + img.aligncenter { + margin-top: 0.4211em; + margin-bottom: 1.6842em; + } + + .wp-caption.alignleft, + .wp-caption.alignright, + .wp-caption.aligncenter { + margin-bottom: 1.2632em; + } + + .site-branding { + min-height: 3.7895em; + padding-right: 74px; + } + + .site-title { + font-size: 32px; + font-size: 3.2rem; + line-height: 1.25; + } + + .site-description { + font-size: 16px; + font-size: 1.6rem; + } + + .widget { + font-size: 16px; + font-size: 1.6rem; + } + + .widget blockquote { + font-size: 19px; + font-size: 1.9rem; + line-height: 1.6842; + margin-bottom: 1.6842em; + margin-left: -1.2632em; + padding-left: 1.0526em; + } + + .widget blockquote p { + margin-bottom: 1.6842em; + } + + .widget blockquote cite, + .widget blockquote small { + font-size: 16px; + font-size: 1.6rem; + } + + .widget button, + .widget input, + .widget select, + .widget textarea { + line-height: 1.5; + } + + .widget button, + .widget input { + line-height: normal; + } + + .widget button, + .widget input[type="button"], + .widget input[type="reset"], + .widget input[type="submit"] { + font-size: 16px; + font-size: 1.6rem; + padding: 0.8125em 1.625em; + } + + .widget input[type="text"], + .widget input[type="email"], + .widget input[type="url"], + .widget input[type="password"], + .widget input[type="search"], + .widget textarea { + padding: 0.75em; + } + + .widget .wp-caption-text, + .widget .gallery-caption { + line-height: 1.5; + } + + .widget_calendar td, + .widget_calendar th { + line-height: 2.9375; + } + + .widget_archive li, + .widget_categories li, + .widget_links li, + .widget_meta li, + .widget_nav_menu li, + .widget_pages li, + .widget_recent_comments li, + .widget_recent_entries li { + padding: 0.7188em 0; + } + + .widget_categories .children, + .widget_nav_menu .sub-menu, + .widget_pages .children { + margin: 0.7188em 0 0 1em; + padding-top: 0.7188em; + } + + .widget_rss .rss-date, + .widget_rss cite { + font-size: 13px; + font-size: 1.3rem; + line-height: 1.8462; + } + + .post-thumbnail { + margin-bottom: 2.9474em; + } + + .entry-title { + font-size: 39px; + font-size: 3.9rem; + line-height: 1.2308; + margin-bottom: 1.2308em; + } + + .entry-content h1, + .entry-summary h1, + .page-content h1, + .comment-content h1 { + font-size: 39px; + font-size: 3.9rem; + line-height: 1.2308; + margin-top: 1.641em; + margin-bottom: 0.8205em; + } + + .entry-content h2, + .entry-summary h2, + .page-content h2, + .comment-content h2 { + font-size: 32px; + font-size: 3.2rem; + line-height: 1.25; + margin-top: 2em; + margin-bottom: 1em; + } + + .entry-content h3, + .entry-summary h3, + .page-content h3, + .comment-content h3 { + font-size: 27px; + font-size: 2.7rem; + line-height: 1.1852; + margin-top: 2.3704em; + margin-bottom: 1.1852em; + } + + .entry-content h4, + .entry-summary h4, + .page-content h4, + .comment-content h4 { + font-size: 22px; + font-size: 2.2rem; + line-height: 1.4545; + margin-top: 2.9091em; + margin-bottom: 1.4545em; + } + + .entry-content h5, + .entry-content h6, + .entry-summary h5, + .entry-summary h6, + .page-content h5, + .page-content h6, + .comment-content h5, + .comment-content h6 { + font-size: 19px; + font-size: 1.9rem; + line-height: 1.2632; + margin-top: 3.3684em; + margin-bottom: 1.6842em; + } + + .entry-content .more-link:after, + .entry-summary .more-link:after { + top: 3px; + } + + .author-info .avatar { + height: 56px; + margin: 0 1.6842em 1.6842em 0; + width: 56px; + } + + .author-link:after { + font-size: 24px; + top: 0; + } + + .posted-on:before, + .byline:before, + .cat-links:before, + .tags-links:before, + .comments-link:before, + .entry-format:before, + .edit-link:before, + .full-size-link:before { + top: 4px; + } + + .taxonomy-description { + padding-top: 0.4211em; + } + + .page-title, + .comments-title, + .comment-reply-title, + .post-navigation .post-title { + font-size: 27px; + font-size: 2.7rem; + line-height: 1.1852; + } + + .page-links { + margin-bottom: 1.4736em; + } + + .page-links a, + .page-links > span { + margin: 0 0.25em 0.25em 0; + } + + .entry-attachment { + margin-bottom: 1.6842em + } + + .format-aside .entry-title, + .format-image .entry-title, + .format-video .entry-title, + .format-quote .entry-title, + .format-gallery .entry-title, + .format-status .entry-title, + .format-link .entry-title, + .format-audio .entry-title, + .format-chat .entry-title { + font-size: 22px; + font-size: 2.2rem; + line-height: 1.4545; + margin-bottom: 1.4545em; + } + + .format-link .entry-title a:after { + top: 0.125em; + } + + .comments-title { + margin-bottom: 1.4545em; + } + + .comment-list article, + .comment-list .pingback, + .comment-list .trackback { + padding: 1.6842em 0; + } + + .comment-list + .comment-respond, + .comment-navigation + .comment-respond { + padding-top: 1.6842em; + } + + .comment-list .children > li { + padding-left: 1.4737em; + } + + .comment-author .avatar { + height: 56px; + margin-right: 1.6842em; + top: 3px; + width: 56px; + } + + .comment-metadata { + line-height: 2; + } + + .comment-metadata .edit-link:before { + top: 8px; + } + + .pingback .edit-link:before { + top: 8px; + } + + .bypostauthor > article .fn:after { + top: 8px; + } + + .comment-content ul, + .comment-content ol { + margin-bottom: 1.6842em; + } + + .comment-list .reply a { + padding: 0.4375em 0.875em; + } + + .comment-form, + .no-comments { + padding-top: 1.6842em; + } + + embed, + iframe, + object, + video { + margin-bottom: 1.6842em; + } + + .wp-audio-shortcode, + .wp-video, + .wp-playlist.wp-audio-playlist { + font-size: 19px; + font-size: 1.9rem; + margin-bottom: 1.6842em; + } + + .wp-caption, + .gallery { + margin-bottom: 1.6842em; + } +} + + +/** + * 15.4 Desktop Small 955px + */ + +@media screen and (min-width: 59.6875em) { + body:before { + background-color: #fff; + box-shadow: 0 0 1px rgba(0, 0, 0, 0.15); + content: ""; + display: block; + height: 100%; + min-height: 100%; + position: fixed; + top: 0; + left: 0; + width: 29.4118%; + z-index: 0; /* Fixes flashing bug with scrolling on Safari */ + } + + .site { + margin: 0 auto; + max-width: 1403px; + } + + .sidebar { + float: left; + margin-right: -100%; + max-width: 413px; + position: relative; + width: 29.4118%; + } + + .secondary { + background-color: transparent; + display: block; + margin: 0; + padding: 0; + } + + .site-main { + padding: 8.3333% 0; + } + + .site-content { + display: block; + float: left; + margin-left: 29.4118%; + width: 70.5882%; + } + + body { + font-size: 15px; + font-size: 1.5rem; + line-height: 1.6; + } + + p, + address, + pre, + hr, + ul, + ol, + dl, + dd, + table { + margin-bottom: 1.6em; + } + + blockquote { + font-size: 18px; + font-size: 1.8rem; + line-height: 1.6667; + margin-bottom: 1.6667em; + margin-left: -1.3333em; + padding-left: 1.1111em; + } + + blockquote cite, + blockquote small { + font-size: 15px; + font-size: 1.5rem; + line-height: 1.6; + } + + pre { + line-height: 1.2; + } + + button, + input, + select, + textarea { + font-size: 16px; + font-size: 1.6rem; + line-height: 1.5; + } + + button, + input { + line-height: normal; + } + + button, + input[type="button"], + input[type="reset"], + input[type="submit"], + .post-password-form input[type="submit"] { + font-size: 12px; + font-size: 1.2rem; + padding: 0.7917em 1.5833em; + } + + input[type="text"], + input[type="email"], + input[type="url"], + input[type="password"], + input[type="search"], + textarea { + padding: 0.375em; + } + + .main-navigation { + font-size: 12px; + font-size: 1.2rem; + margin: 0 20% 20%; + } + + .main-navigation a { + padding: 0.5em 0; + } + + .main-navigation .page_item_has_children > a, + .main-navigation .menu-item-has-children > a { + padding-right: 35px; + } + + .main-navigation .menu-item-description { + font-size: 12px; + font-size: 1.2rem; + line-height: 1.5; + } + + .dropdown-toggle { + height: 24px; + width: 24px; + } + + .dropdown-toggle:after { + font-size: 16px; + line-height: 24px; + width: 24px; + } + + .social-navigation { + margin: 0 20% 20%; + } + + .social-navigation ul { + margin-bottom: -1.6em; + } + + .social-navigation li { + width: 25%; + } + + .social-navigation a { + height: 3.2em; + } + + .secondary-toggle { + display: none; + } + + .post-password-form label, + .post-navigation .meta-nav, + .comment-navigation, + .image-navigation, + .author-heading, + .author-bio, + .entry-footer, + .page-links a, + .page-links span, + .comment-metadata, + .pingback .edit-link, + .comment-list .reply, + .comment-notes, + .comment-awaiting-moderation, + .logged-in-as, + .comment-form label, + .form-allowed-tags, + .site-info, + .wp-caption-text, + .gallery-caption, + .entry-caption { + font-size: 12px; + font-size: 1.2rem; + } + + .post-navigation { + margin: 8.3333% 8.3333% 0; + } + + .post-navigation a { + padding: 5% 10%; + } + + .pagination { + margin: 8.333% 8.333% 0; + } + + .pagination .nav-links { + min-height: 3.2em; + } + + .pagination .page-numbers { + line-height: 3.2em; + padding: 0 0.8em; + } + + .pagination .prev, + .pagination .next { + height: 48px; + padding: 0; + width: 48px; + } + + .pagination .prev:before, + .pagination .next:before { + height: 48px; + line-height: 48px; + width: 48px; + } + + .image-navigation .nav-previous a:before, + .image-navigation .nav-next a:after, + .comment-navigation .nav-previous a:before, + .comment-navigation .nav-next a:after { + font-size: 16px; + top: 0; + } + + .image-navigation { + padding: 0 10%; + } + + blockquote.alignleft, + .wp-caption.alignleft, + img.alignleft { + margin: 0.4em 1.6em 1.6em 0; + } + + blockquote.alignright, + .wp-caption.alignright, + img.alignright { + margin: 0.4em 0 1.6em 1.6em; + } + + blockquote.aligncenter, + .wp-caption.aligncenter, + img.aligncenter { + clear: both; + margin-top: 0.4em; + margin-bottom: 1.6em; + } + + .wp-caption.alignleft, + .wp-caption.alignright, + .wp-caption.aligncenter { + margin-bottom: 1.2em; + } + + .site-header { + background-color: transparent; + border-bottom: 0; + margin: 20% 0; + padding: 0 20%; + } + + .site-branding { + min-height: 0; + padding: 0; + } + + .site-title { + font-size: 22px; + font-size: 2.2rem; + line-height: 1.3636; + } + + .site-description { + font-size: 12px; + font-size: 1.2rem; + } + + .widget { + font-size: 12px; + font-size: 1.2rem; + margin: 0 0 20%; + padding: 0 20%; + } + + .widget blockquote { + font-size: 12px; + font-size: 1.2rem; + line-height: 1.5; + margin-bottom: 1.5em; + margin-left: -1.5em; + padding-left: 1.1667em; + } + + .widget blockquote p { + margin-bottom: 1.5em; + } + + .widget blockquote cite, + .widget blockquote small { + font-size: 12px; + font-size: 1.2rem; + } + + .widget pre { + padding: 0.5em; + } + + .widget button, + .widget input, + .widget select, + .widget textarea { + font-size: 12px; + font-size: 1.2rem; + } + + .widget button, + .widget input[type="button"], + .widget input[type="reset"], + .widget input[type="submit"] { + font-size: 12px; + font-size: 1.2rem; + padding: 0.5417em 1.0833em; + } + + .widget input[type="text"], + .widget input[type="email"], + .widget input[type="url"], + .widget input[type="password"], + .widget input[type="search"], + .widget textarea { + padding: 0.4583em; + } + + .widget .wp-caption-text, + .widget .gallery-caption { + font-size: 12px; + font-size: 1.2rem; + } + + .widget_calendar td, + .widget_calendar th { + line-height: 1.9167; + } + + .widget_archive li, + .widget_categories li, + .widget_links li, + .widget_meta li, + .widget_nav_menu li, + .widget_pages li, + .widget_recent_comments li, + .widget_recent_entries li { + padding: 0.4583em 0; + } + + .widget_categories .children, + .widget_nav_menu .sub-menu, + .widget_pages .children { + margin: 0.4583em 0 0 1em; + padding-top: 0.4583em; + } + + .widget_rss .rss-date, + .widget_rss cite { + font-size: 12px; + font-size: 1.2rem; + line-height: 1.5; + } + + .hentry, + .page-header, + .page-content { + margin: 0 8.3333%; + } + + .hentry { + padding-top: 8.3333%; + } + + .hentry + .hentry, + .page-header + .hentry, + .page-header + .page-content { + margin-top: 8.3333%; + } + + .post-thumbnail { + margin-bottom: 2.4em; + } + + .entry-header { + padding: 0 10%; + } + + .entry-title { + font-size: 31px; + font-size: 3.1rem; + line-height: 1.1613; + margin-bottom: 1.1613em; + } + + .entry-content, + .entry-summary { + padding: 0 10% 10%; + } + + .entry-content h1, + .entry-summary h1, + .page-content h1, + .comment-content h1 { + font-size: 31px; + font-size: 3.1rem; + line-height: 1.1613; + margin-top: 1.5484em; + margin-bottom: 0.7742em; + } + + .entry-content h2, + .entry-summary h2, + .page-content h2, + .comment-content h2 { + font-size: 26px; + font-size: 2.6rem; + line-height: 1.3846; + margin-top: 1.8462em; + margin-bottom: 0.9231em; + } + + .entry-content h3, + .entry-summary h3, + .page-content h3, + .comment-content h3 { + font-size: 22px; + font-size: 2.2rem; + line-height: 1.3636; + margin-top: 2.1818em; + margin-bottom: 1.0909em; + } + + .entry-content h4, + .entry-summary h4, + .page-content h4, + .comment-content h4 { + font-size: 18px; + font-size: 1.8rem; + line-height: 1.3333; + margin-top: 2.6667em; + margin-bottom: 1.3333em; + } + + .entry-content h5, + .entry-content h6, + .entry-summary h5, + .entry-summary h6, + .page-content h5, + .page-content h6, + .comment-content h5, + .comment-content h6 { + font-size: 15px; + font-size: 1.5rem; + line-height: 1.2; + margin-top: 3.2em; + margin-bottom: 1.6em; + } + + .entry-content .more-link:after, + .entry-summary .more-link:after { + font-size: 16px; + top: 5px; + } + + .author-info { + margin: 0 10%; + padding: 10% 0; + } + + .author-info .avatar { + height: 36px; + margin: 0 1.5em 1.5em 0; + width: 36px; + } + + .author-link:after { + font-size: 16px; + top: 1px; + } + + .entry-footer { + padding: 5% 10%; + } + + .posted-on:before, + .byline:before, + .cat-links:before, + .tags-links:before, + .comments-link:before, + .entry-format:before, + .edit-link:before, + .full-size-link:before { + top: 0; + } + + .page-header { + padding: 4.1666% 8.3333%; + } + + .page-content { + padding: 8.3333%; + } + + .taxonomy-description { + padding-top: 0.4em; + } + + .page-title, + .comments-title, + .comment-reply-title, + .post-navigation .post-title { + font-size: 18px; + font-size: 1.8rem; + line-height: 1.3333; + } + + .page-links { + margin-bottom: 1.3333em; + } + + .page-links a, + .page-links > span { + margin: 0 0.3333em 0.3333em 0; + } + + .entry-attachment { + margin-bottom: 1.6em; + } + + .format-aside .entry-title, + .format-image .entry-title, + .format-video .entry-title, + .format-quote .entry-title, + .format-gallery .entry-title, + .format-status .entry-title, + .format-link .entry-title, + .format-audio .entry-title, + .format-chat .entry-title { + font-size: 18px; + font-size: 1.8rem; + line-height: 1.3333; + margin-bottom: 1.3333em; + } + + .format-link .entry-title a:after { + top: 0; + } + + .comments-area { + margin: 8.3333% 8.3333% 0; + padding: 8.3333%; + } + + .comments-title { + margin-bottom: 1.3333em; + } + + .comment-list article, + .comment-list .pingback, + .comment-list .trackback { + padding: 1.6em 0; + } + + .comment-list + .comment-respond, + .comment-navigation + .comment-respond { + padding-top: 1.6em; + } + + .comment-list .children > li { + padding-left: 0.8em; + } + + .comment-author { + margin-bottom: 0.4em; + } + + .comment-author .avatar { + height: 24px; + margin-right: 0.8em; + top: 0; + width: 24px; + } + + .comment-metadata .edit-link:before { + top: 3px; + } + + .pingback .edit-link:before { + top: 5px; + } + + .bypostauthor > article .fn:after { + top: 5px; + left: 3px; + } + + .comment-content ul, + .comment-content ol { + margin-bottom: 2em; + } + + .comment-list .reply a { + padding: 0.4167em 0.8333em; + } + + .comment-form, + .no-comments { + padding-top: 1.6em; + } + + .comment-reply-title small a:before { + top: -3px; + } + + .site-footer { + float: left; + margin: 0 0 0 35.2941%; + padding: 0; + width: 58.8235%; + } + + .site-info { + padding: 5% 10%; + } + + embed, + iframe, + object, + video { + margin-bottom: 1.6em; + } + + .wp-audio-shortcode, + .wp-video, + .wp-playlist.wp-audio-playlist { + font-size: 15px; + font-size: 1.5rem; + margin-bottom: 1.6em; + } + + .wp-caption, + .gallery { + margin-bottom: 1.6em; + } +} + + +/** + * 15.5 Desktop Medium 1100px + */ + +@media screen and (min-width: 68.75em) { + body, + button, + input, + select, + textarea { + font-size: 17px; + font-size: 1.7rem; + line-height: 1.6471; + } + + button, + input { + line-height: normal; + } + + p, + address, + pre, + hr, + ul, + ol, + dl, + dd, + table { + margin-bottom: 1.6471em; + } + + blockquote { + font-size: 20px; + font-size: 2rem; + line-height: 1.75; + margin-bottom: 1.75em; + margin-left: -1.05em; + padding-left: 0.85em; + } + + blockquote p { + margin-bottom: 1.75em; + } + + blockquote cite, + blockquote small { + font-size: 17px; + font-size: 1.7rem; + line-height: 1.6471; + } + + pre { + line-height: 1.2353; + } + + button, + input[type="button"], + input[type="reset"], + input[type="submit"], + .post-password-form input[type="submit"] { + font-size: 14px; + font-size: 1.4rem; + padding: 0.8214em 1.5714em; + } + + input[type="text"], + input[type="email"], + input[type="url"], + input[type="password"], + input[type="search"], + textarea { + padding: 0.5em; + } + + .main-navigation { + font-size: 14px; + font-size: 1.4rem; + } + + .main-navigation a { + padding: 0.4643em 0; + } + + .main-navigation .page_item_has_children > a, + .main-navigation .menu-item-has-children > a { + padding-right: 33px; + } + + .main-navigation .menu-item-description { + line-height: 1.4583; + margin-top: 0.25em; + } + + .dropdown-toggle { + height: 28px; + width: 28px; + } + + .dropdown-toggle:after { + line-height: 28px; + width: 28px; + } + + .social-navigation ul { + margin-bottom: -1.4706em; + } + + .social-navigation li { + width: 20%; + } + + .social-navigation a { + height: 2.8824em; + } + + .post-password-form label, + .post-navigation .meta-nav, + .comment-navigation, + .image-navigation, + .author-heading, + .author-bio, + .entry-footer, + .page-links a, + .page-links span, + .comment-metadata, + .pingback .edit-link, + .comment-list .reply, + .comment-notes, + .comment-awaiting-moderation, + .logged-in-as, + .comment-form label, + .form-allowed-tags, + .site-info, + .wp-caption-text, + .gallery-caption, + .entry-caption { + font-size: 14px; + font-size: 1.4rem; + } + + .pagination .nav-links { + min-height: 3.2941em; + } + + .pagination .page-numbers { + line-height: 3.2941em; + padding: 0 0.8235em; + } + + .pagination .prev, + .pagination .next { + height: 56px; + padding: 0; + width: 56px; + } + + .pagination .prev:before, + .pagination .next:before { + height: 56px; + line-height: 56px; + width: 56px; + } + + .image-navigation .nav-previous a:before, + .image-navigation .nav-next a:after, + .comment-navigation .nav-previous a:before, + .comment-navigation .nav-next a:after { + top: 2px; + } + + blockquote.alignleft, + .wp-caption.alignleft, + img.alignleft { + margin: 0.4118em 1.6471em 1.6471em 0; + } + + blockquote.alignright, + .wp-caption.alignright, + img.alignright { + margin: 0.4118em 0 1.6471em 1.6471em; + } + + blockquote.aligncenter, + .wp-caption.aligncenter, + img.aligncenter { + margin-top: 0.4118em; + margin-bottom: 1.6471em; + } + + .wp-caption.alignleft, + .wp-caption.alignright, + .wp-caption.aligncenter { + margin-bottom: 1.2353em; + } + + .site-title { + font-size: 24px; + font-size: 2.4rem; + line-height: 1.1667; + } + + .site-description { + font-size: 14px; + font-size: 1.4rem; + } + + .widget { + font-size: 14px; + font-size: 1.4rem; + } + + .widget blockquote { + font-size: 14px; + font-size: 1.4rem; + padding-left: 1.2143em; + } + + .widget button, + .widget input, + .widget select, + .widget textarea { + font-size: 14px; + font-size: 1.4rem; + } + + .widget button, + .widget input[type="button"], + .widget input[type="reset"], + .widget input[type="submit"] { + font-size: 12px; + font-size: 1.2rem; + padding: 0.75em 1.5em; + } + + .widget input[type="text"], + .widget input[type="email"], + .widget input[type="url"], + .widget input[type="password"], + .widget input[type="search"], + .widget textarea { + padding: 0.5em; + } + + .widget .wp-caption-text, + .widget .gallery-caption { + line-height: 1.4583; + padding: 0.5833em 0; + } + + .widget_calendar caption { + margin: 0 0 1.9286em; + } + + .widget_calendar td, + .widget_calendar th { + line-height: 1.9286; + } + + .widget_archive li, + .widget_categories li, + .widget_links li, + .widget_meta li, + .widget_nav_menu li, + .widget_pages li, + .widget_recent_comments li, + .widget_recent_entries li { + padding: 0.4643em 0; + } + + .widget_categories .children, + .widget_nav_menu .sub-menu, + .widget_pages .children { + margin: 0.4643em 0 0 1em; + padding-top: 0.4643em; + } + + .widget_rss .rss-date, + .widget_rss cite { + line-height: 1.75; + } + + .post-thumbnail { + margin-bottom: 2.4706em; + } + + .entry-title { + font-size: 35px; + font-size: 3.5rem; + line-height: 1.2; + margin-bottom: 1.2em; + } + + .entry-content h1, + .entry-summary h1, + .page-content h1, + .comment-content h1 { + font-size: 35px; + font-size: 3.5rem; + line-height: 1.2; + margin-top: 1.6em; + margin-bottom: 0.8em; + } + + .entry-content h2, + .entry-summary h2, + .page-content h2, + .comment-content h2 { + font-size: 29px; + font-size: 2.9rem; + line-height: 1.2069; + margin-top: 1.931em; + margin-bottom: 0.9655em; + } + + .entry-content h3, + .entry-summary h3, + .page-content h3, + .comment-content h3 { + font-size: 24px; + font-size: 2.4rem; + line-height: 1.1667; + margin-top: 2.3333em; + margin-bottom: 1.1667em; + } + + .entry-content h4, + .entry-summary h4, + .page-content h4, + .comment-content h4 { + font-size: 20px; + font-size: 2rem; + line-height: 1.4; + margin-top: 2.8em; + margin-bottom: 1.4em; + } + + .entry-content h5, + .entry-content h6, + .entry-summary h5, + .entry-summary h6, + .page-content h5, + .page-content h6, + .comment-content h5, + .comment-content h6 { + font-size: 17px; + font-size: 1.7rem; + line-height: 1.2353; + margin-top: 3.2941em; + margin-bottom: 1.6471em; + } + + .entry-content .more-link:after, + .entry-summary .more-link:after { + font-size: 24px; + top: 2px; + } + + .author-info .avatar { + height: 42px; + margin: 0 1.6471em 1.6471em 0; + width: 42px; + } + + .author-link:after { + top: 3px; + } + + .posted-on:before, + .byline:before, + .cat-links:before, + .tags-links:before, + .comments-link:before, + .entry-format:before, + .edit-link:before, + .full-size-link:before { + top: 3px; + } + + .taxonomy-description { + padding-top: 0.4118em; + } + + .page-title, + .comments-title, + .comment-reply-title, + .post-navigation .post-title { + font-size: 24px; + font-size: 2.4rem; + line-height: 1.1667; + } + + .page-links { + margin-bottom: 1.4117em; + } + + .page-links a, + .page-links > span { + margin: 0 0.2857em 0.2857em 0; + } + + .entry-attachment { + margin-bottom: 1.6471em; + } + + .format-aside .entry-title, + .format-image .entry-title, + .format-video .entry-title, + .format-quote .entry-title, + .format-gallery .entry-title, + .format-status .entry-title, + .format-link .entry-title, + .format-audio .entry-title, + .format-chat .entry-title { + font-size: 20px; + font-size: 2rem; + line-height: 1.4; + margin-bottom: 1.4em; + } + + .format-link .entry-title a:after { + top: 0.0833em; + } + + .comments-title { + margin-bottom: 1.4em; + } + + .comment-list article, + .comment-list .pingback, + .comment-list .trackback { + padding: 1.6471em 0; + } + + .comment-list + .comment-respond, + .comment-navigation + .comment-respond { + padding-top: 1.6471em; + } + + .comment-list .children > li { + padding-left: 1.1667em; + } + + .comment-author { + margin-bottom: 0; + } + + .comment-author .avatar { + height: 42px; + margin-right: 1.64705em; + top: 5px; + width: 42px; + } + + .bypostauthor > article .fn:after { + top: 7px; + left: 6px; + } + + .comment-metadata .edit-link:before { + top: 6px; + } + + .pingback .edit-link:before { + top: 6px; + } + + .comment-content ul, + .comment-content ol { + margin-bottom: 1.6471em; + } + + .comment-list .reply a { + padding: 0.4286em 0.8571em; + } + + .comment-form, + .no-comments { + padding-top: 1.6471em; + } + + .comment-reply-title small a:before { + top: -1px; + } + + embed, + iframe, + object, + video { + margin-bottom: 1.6471em; + } + + .wp-audio-shortcode, + .wp-video, + .wp-playlist.wp-audio-playlist { + font-size: 17px; + font-size: 1.7rem; + margin-bottom: 1.6471em; + } + + .wp-caption, + .gallery { + margin-bottom: 1.6471em; + } +} + + +/** + * 15.6 Desktop Large 1240px + */ + +@media screen and (min-width: 77.5em) { + body, + button, + input, + select, + textarea { + font-size: 19px; + font-size: 1.9rem; + line-height: 1.6842; + } + + button, + input { + line-height: normal; + } + + p, + address, + pre, + hr, + ul, + ol, + dl, + dd, + table { + margin-bottom: 1.6842em; + } + + blockquote { + font-size: 22px; + font-size: 2.2rem; + line-height: 1.8182; + margin-bottom: 1.8182em; + margin-left: -1.0909em; + padding-left: 0.9091em; + } + + blockquote p { + margin-bottom: 1.8182em; + } + + blockquote cite, + blockquote small { + font-size: 19px; + font-size: 1.9rem; + line-height: 1.6842; + } + + pre { + line-height: 1.2632; + } + + button, + input[type="button"], + input[type="reset"], + input[type="submit"], + .post-password-form input[type="submit"] { + font-size: 16px; + font-size: 1.6rem; + padding: 0.8125em 1.625em; + } + + input[type="text"], + input[type="email"], + input[type="url"], + input[type="password"], + input[type="search"], + textarea { + padding: 0.5278em; + } + + .main-navigation { + font-size: 16px; + font-size: 1.6rem; + } + + .main-navigation a { + padding: 0.5em 0; + } + + .main-navigation .page_item_has_children > a, + .main-navigation .menu-item-has-children > a { + padding-right: 32px; + } + + .main-navigation .menu-item-description { + font-size: 13px; + font-size: 1.3rem; + line-height: 1.5385; + margin-top: 0.3077em; + } + + .dropdown-toggle { + height: 32px; + top: 4px; + width: 32px; + } + + .dropdown-toggle:after { + line-height: 32px; + width: 32px; + } + + .social-navigation ul { + margin-bottom: -1.2632em; + } + + .social-navigation a { + height: 2.5263em; + } + + .post-password-form label, + .post-navigation .meta-nav, + .comment-navigation, + .image-navigation, + .author-heading, + .author-bio, + .entry-footer, + .page-links a, + .page-links span, + .comment-metadata, + .pingback .edit-link, + .comment-list .reply, + .comment-notes, + .comment-awaiting-moderation, + .logged-in-as, + .comment-form label, + .form-allowed-tags, + .site-info, + .wp-caption-text, + .gallery-caption, + .entry-caption { + font-size: 16px; + font-size: 1.6rem; + } + + .pagination .nav-links { + min-height: 3.3684em; + } + + .pagination .page-numbers { + line-height: 3.3684em; + padding: 0 0.8421em; + } + + .pagination .prev, + .pagination .next { + height: 64px; + padding: 0; + width: 64px; + } + + .pagination .prev:before, + .pagination .next:before { + height: 64px; + line-height: 64px; + width: 64px; + } + + .image-navigation .nav-previous a:before, + .image-navigation .nav-next a:after, + .comment-navigation .nav-previous a:before, + .comment-navigation .nav-next a:after { + font-size: 24px; + top: -1px; + } + + blockquote.alignleft, + .wp-caption.alignleft, + img.alignleft { + margin: 0.4211em 1.6842em 1.6842em 0; + } + + blockquote.alignright, + .wp-caption.alignright, + img.alignright { + margin: 0.4211em 0 1.6842em 1.6842em; + } + + blockquote.aligncenter, + .wp-caption.aligncenter, + img.aligncenter { + margin-top: 0.4211em; + margin-bottom: 1.6842em; + } + + .wp-caption.alignleft, + .wp-caption.alignright, + .wp-caption.aligncenter { + margin-bottom: 1.2632em; + } + + .site-title { + font-size: 27px; + font-size: 2.7rem; + line-height: 1.1852; + } + + .site-description { + font-size: 16px; + font-size: 1.6rem; + } + + .widget { + font-size: 16px; + font-size: 1.6rem; + } + + .widget blockquote { + font-size: 16px; + font-size: 1.6rem; + padding-left: 1.25em; + } + + .widget blockquote cite, + .widget blockquote small { + font-size: 13px; + font-size: 1.3rem; + line-height: 1.8462; + } + + .widget button, + .widget input, + .widget select, + .widget textarea { + font-size: 16px; + font-size: 1.6rem; + } + + .widget button, + .widget input[type="button"], + .widget input[type="reset"], + .widget input[type="submit"] { + font-size: 13px; + font-size: 1.3rem; + padding: 0.8462em 1.6923em; + } + + .widget input[type="text"], + .widget input[type="email"], + .widget input[type="url"], + .widget input[type="password"], + .widget input[type="search"], + .widget textarea { + padding: 0.5em; + } + + .widget .wp-caption-text, + .widget .gallery-caption { + font-size: 13px; + font-size: 1.3rem; + line-height: 1.5385; + padding: 0.6154em 0; + } + + .widget_calendar td, + .widget_calendar th { + line-height: 1.9375; + } + + .widget_calendar caption { + margin: 0 0 1.5em; + } + + .widget_archive li, + .widget_categories li, + .widget_links li, + .widget_meta li, + .widget_nav_menu li, + .widget_pages li, + .widget_recent_comments li, + .widget_recent_entries li { + padding: 0.4688em 0; + } + + .widget_categories .children, + .widget_nav_menu .sub-menu, + .widget_pages .children { + margin: 0.4688em 0 0 1em; + padding-top: 0.4688em; + } + + .widget_rss .rss-date, + .widget_rss cite { + font-size: 13px; + font-size: 1.3rem; + line-height: 1.8462; + } + + .post-thumbnail { + margin-bottom: 2.9474em; + } + + .entry-title { + font-size: 39px; + font-size: 3.9rem; + line-height: 1.2308; + margin-bottom: 1.2308em; + } + + .entry-content h1, + .entry-summary h1, + .page-content h1, + .comment-content h1 { + font-size: 39px; + font-size: 3.9rem; + line-height: 1.2308; + margin-top: 1.641em; + margin-bottom: 0.8205em; + } + + .entry-content h2, + .entry-summary h2, + .page-content h2, + .comment-content h2 { + font-size: 32px; + font-size: 3.2rem; + line-height: 1.25; + margin-top: 2em; + margin-bottom: 1em; + } + + .entry-content h3, + .entry-summary h3, + .page-content h3, + .comment-content h3 { + font-size: 27px; + font-size: 2.7rem; + line-height: 1.1852; + margin-top: 2.3704em; + margin-bottom: 1.1852em; + } + + .entry-content h4, + .entry-summary h4, + .page-content h4, + .comment-content h4 { + font-size: 22px; + font-size: 2.2rem; + line-height: 1.4545; + margin-top: 2.9091em; + margin-bottom: 1.4545em; + } + + .entry-content h5, + .entry-content h6, + .entry-summary h5, + .entry-summary h6, + .page-content h5, + .page-content h6, + .comment-content h5, + .comment-content h6 { + font-size: 19px; + font-size: 1.9rem; + line-height: 1.2632; + margin-top: 3.3684em; + margin-bottom: 1.6842em; + } + + .entry-content .more-link:after, + .entry-summary .more-link:after { + top: 3px; + } + + .author-info .avatar { + height: 56px; + margin: 0 1.6842em 1.6842em 0; + width: 56px; + } + + .author-link:after { + font-size: 24px; + top: 0; + } + + .posted-on:before, + .byline:before, + .cat-links:before, + .tags-links:before, + .comments-link:before, + .entry-format:before, + .edit-link:before, + .full-size-link:before { + top: 4px; + } + + .taxonomy-description { + padding-top: 0.4211em; + } + + .page-title, + .comments-title, + .comment-reply-title, + .post-navigation .post-title { + font-size: 27px; + font-size: 2.7rem; + line-height: 1.1852; + } + + .page-links { + margin-bottom: 1.4736em; + } + + .page-links a, + .page-links > span { + margin: 0 0.25em 0.25em 0; + } + + .entry-attachment { + margin-bottom: 1.6842em; + } + + .format-aside .entry-title, + .format-image .entry-title, + .format-video .entry-title, + .format-quote .entry-title, + .format-gallery .entry-title, + .format-status .entry-title, + .format-link .entry-title, + .format-audio .entry-title, + .format-chat .entry-title { + font-size: 22px; + font-size: 2.2rem; + line-height: 1.4545; + margin-bottom: 1.4545em; + } + + .format-link .entry-title a:after { + top: 3px; + } + + .comments-title { + margin-bottom: 1.4545em; + } + + .comment-list article, + .comment-list .pingback, + .comment-list .trackback { + padding: 1.6842em 0; + } + + .comment-list + .comment-respond, + .comment-navigation + .comment-respond { + padding-top: 1.6842em; + } + + .comment-list .children > li { + padding-left: 1.4737em; + } + + .comment-author .avatar { + height: 56px; + margin-right: 1.6842em; + top: 3px; + width: 56px; + } + + .bypostauthor > article .fn:after { + top: 8px; + } + + .comment-metadata .edit-link:before { + top: 8px; + } + + .pingback .edit-link:before { + top: 8px; + } + + .comment-content ul, + .comment-content ol { + margin-bottom: 1.6842em; + } + + .comment-list .reply a { + padding: 0.4375em 0.875em; + } + + .comment-form, + .no-comments { + padding-top: 1.6842em; + } + + embed, + iframe, + object, + video { + margin-bottom: 1.6842em; + } + + .wp-audio-shortcode, + .wp-video, + .wp-playlist.wp-audio-playlist { + font-size: 19px; + font-size: 1.9rem; + margin-bottom: 1.6842em; + } + + .wp-caption, + .gallery { + margin-bottom: 1.6842em; + } +} + + +/** + * 15.7 Desktop X-Large 1403px + */ + +@media screen and (min-width: 87.6875em) { + body:before { + width: -webkit-calc(50% - 289px); + width: calc(50% - 289px); + } +} + + +/** + * 16.0 Print + */ + +@media print { + body { + background: none !important; /* Brute force since user agents all print differently. */ + font-size: 11.25pt; + } + + .secondary-toggle, + .navigation, + .page-links, + .edit-link, + #reply-title, + .comment-form, + .comment-edit-link, + .comment-list .reply a, + button, + input, + textarea, + select { + display: none; + } + + .site-header, + .site-footer, + .hentry, + .entry-footer, + .page-header, + .page-content, + .comments-area { + background: none !important; /* Make sure color schemes dont't affect to print */ + } + + body, + blockquote, + blockquote cite, + blockquote small, + label, + a, + .site-title a, + .site-description, + .post-title, + .author-heading, + .entry-footer, + .entry-footer a, + .taxonomy-description, + .entry-caption, + .comment-author, + .comment-metadata, + .comment-metadata a, + .comment-notes, + .comment-awaiting-moderation, + .no-comments, + .site-info, + .site-info a, + .wp-caption-text, + .gallery-caption { + color: #000 !important; /* Make sure color schemes don't affect to print */ + } + + pre, + abbr[title], + table, + th, + td, + .site-header, + .site-footer, + .hentry + .hentry, + .author-info, + .page-header, + .comments-area, + .comment-list + .comment-respond, + .comment-list article, + .comment-list .pingback, + .comment-list .trackback, + .no-comments { + border-color: #eaeaea !important; /* Make sure color schemes don't affect to print */ + } + + .site { + margin: 0 7.6923%; + } + + .site-branding { + padding: 0; + } + + .site-header { + padding: 7.6923% 0; + } + + .site-description { + display: block; + } + + .hentry + .hentry { + margin-top: 7.6923%; + } + + .hentry.has-post-thumbnail { + padding-top: 7.6923%; + } + + .sticky-post { + background: #000 !important; + color: #fff !important; + } + + .entry-header, + .entry-footer { + padding: 0; + } + + .entry-content, + .entry-summary { + padding: 0 0 7.6923%; + } + + .post-thumbnail img { + margin: 0; + } + + .author-info { + margin: 0; + } + + .page-content { + padding: 7.6923% 0 0; + } + + .page-header { + padding: 3.84615% 0; + } + + .comments-area { + border: 0; + padding: 7.6923% 0 0; + } + + .site-footer { + margin-top: 7.6923%; + padding: 3.84615% 0; + } +} diff --git a/wp-content/themes/twentyfourteen/404.php b/wp-content/themes/twentyfourteen/404.php new file mode 100644 index 0000000..7f5bef8 --- /dev/null +++ b/wp-content/themes/twentyfourteen/404.php @@ -0,0 +1,32 @@ + + + +
      +
      + + + +
      +

      + + +
      + +
      +
      + + + +
      +
      + + + + + + +
      +
      + + + +
      +
      + + + +
      +

      + +

      + +
      + +
      + + + +
      +
      + + + +
      +
      + + + +
      +

      + + %s
      ', $term_description ); + endif; + ?> + + + + +
      + + + +
      + + + +

      + +

      + + 1 && get_option( 'page_comments' ) ) : ?> + + + +
        + 'ol', + 'short_ping' => true, + 'avatar_size'=> 34, + ) ); + ?> +
      + + 1 && get_option( 'page_comments' ) ) : ?> + + + + +

      + + + + + + +
      diff --git a/wp-content/themes/twentyfourteen/content-aside.php b/wp-content/themes/twentyfourteen/content-aside.php new file mode 100644 index 0000000..0401579 --- /dev/null +++ b/wp-content/themes/twentyfourteen/content-aside.php @@ -0,0 +1,62 @@ + + +
      > + + +
      + + + ', '' ); + else : + the_title( '

      ', '

      ' ); + endif; + ?> + + +
      + +
      + →', 'twentyfourteen' ), + the_title( '', '', false ) + ) ); + + wp_link_pages( array( + 'before' => '', + 'link_before' => '', + 'link_after' => '', + ) ); + ?> +
      + + ', '', '' ); ?> +
      diff --git a/wp-content/themes/twentyfourteen/content-audio.php b/wp-content/themes/twentyfourteen/content-audio.php new file mode 100644 index 0000000..9fa9df4 --- /dev/null +++ b/wp-content/themes/twentyfourteen/content-audio.php @@ -0,0 +1,62 @@ + + +
      > + + +
      + + + ', '' ); + else : + the_title( '

      ', '

      ' ); + endif; + ?> + + +
      + +
      + →', 'twentyfourteen' ), + the_title( '', '', false ) + ) ); + + wp_link_pages( array( + 'before' => '', + 'link_before' => '', + 'link_after' => '', + ) ); + ?> +
      + + ', '', '' ); ?> +
      diff --git a/wp-content/themes/twentyfourteen/content-featured-post.php b/wp-content/themes/twentyfourteen/content-featured-post.php new file mode 100644 index 0000000..6cc8822 --- /dev/null +++ b/wp-content/themes/twentyfourteen/content-featured-post.php @@ -0,0 +1,34 @@ + + +
      > + + + + +
      + + + + + ','' ); ?> +
      +
      diff --git a/wp-content/themes/twentyfourteen/content-gallery.php b/wp-content/themes/twentyfourteen/content-gallery.php new file mode 100644 index 0000000..6e3fe83 --- /dev/null +++ b/wp-content/themes/twentyfourteen/content-gallery.php @@ -0,0 +1,62 @@ + + +
      > + + +
      + + + ', '' ); + else : + the_title( '

      ', '

      ' ); + endif; + ?> + + +
      + +
      + →', 'twentyfourteen' ), + the_title( '', '', false ) + ) ); + + wp_link_pages( array( + 'before' => '', + 'link_before' => '', + 'link_after' => '', + ) ); + ?> +
      + + ', '', '' ); ?> +
      diff --git a/wp-content/themes/twentyfourteen/content-image.php b/wp-content/themes/twentyfourteen/content-image.php new file mode 100644 index 0000000..a81e493 --- /dev/null +++ b/wp-content/themes/twentyfourteen/content-image.php @@ -0,0 +1,62 @@ + + +
      > + + +
      + + + ', '' ); + else : + the_title( '

      ', '

      ' ); + endif; + ?> + + +
      + +
      + →', 'twentyfourteen' ), + the_title( '', '', false ) + ) ); + + wp_link_pages( array( + 'before' => '', + 'link_before' => '', + 'link_after' => '', + ) ); + ?> +
      + + ', '', '' ); ?> +
      diff --git a/wp-content/themes/twentyfourteen/content-link.php b/wp-content/themes/twentyfourteen/content-link.php new file mode 100644 index 0000000..858301e --- /dev/null +++ b/wp-content/themes/twentyfourteen/content-link.php @@ -0,0 +1,62 @@ + + +
      > + + +
      + + + ', '' ); + else : + the_title( '

      ', '

      ' ); + endif; + ?> + + +
      + +
      + →', 'twentyfourteen' ), + the_title( '', '', false ) + ) ); + + wp_link_pages( array( + 'before' => '', + 'link_before' => '', + 'link_after' => '', + ) ); + ?> +
      + + ', '', '' ); ?> +
      diff --git a/wp-content/themes/twentyfourteen/content-none.php b/wp-content/themes/twentyfourteen/content-none.php new file mode 100644 index 0000000..a83e06e --- /dev/null +++ b/wp-content/themes/twentyfourteen/content-none.php @@ -0,0 +1,31 @@ + + + + +
      + + +

      Get started here.', 'twentyfourteen' ), admin_url( 'post-new.php' ) ); ?>

      + + + +

      + + + + +

      + + + +
      diff --git a/wp-content/themes/twentyfourteen/content-page.php b/wp-content/themes/twentyfourteen/content-page.php new file mode 100644 index 0000000..9839419 --- /dev/null +++ b/wp-content/themes/twentyfourteen/content-page.php @@ -0,0 +1,31 @@ + + +
      > +

      ', '

      ' ); + ?> + +
      + '', + 'link_before' => '', + 'link_after' => '', + ) ); + + edit_post_link( __( 'Edit', 'twentyfourteen' ), '', '' ); + ?> +
      +
      diff --git a/wp-content/themes/twentyfourteen/content-quote.php b/wp-content/themes/twentyfourteen/content-quote.php new file mode 100644 index 0000000..10a5d11 --- /dev/null +++ b/wp-content/themes/twentyfourteen/content-quote.php @@ -0,0 +1,62 @@ + + +
      > + + +
      + + + ', '' ); + else : + the_title( '

      ', '

      ' ); + endif; + ?> + + +
      + +
      + →', 'twentyfourteen' ), + the_title( '', '', false ) + ) ); + + wp_link_pages( array( + 'before' => '', + 'link_before' => '', + 'link_after' => '', + ) ); + ?> +
      + + ', '', '' ); ?> +
      diff --git a/wp-content/themes/twentyfourteen/content-video.php b/wp-content/themes/twentyfourteen/content-video.php new file mode 100644 index 0000000..4c49aaa --- /dev/null +++ b/wp-content/themes/twentyfourteen/content-video.php @@ -0,0 +1,62 @@ + + +
      > + + +
      + + + ', '' ); + else : + the_title( '

      ', '

      ' ); + endif; + ?> + + +
      + +
      + →', 'twentyfourteen' ), + the_title( '', '', false ) + ) ); + + wp_link_pages( array( + 'before' => '', + 'link_before' => '', + 'link_after' => '', + ) ); + ?> +
      + + ', '', '' ); ?> +
      diff --git a/wp-content/themes/twentyfourteen/content.php b/wp-content/themes/twentyfourteen/content.php new file mode 100644 index 0000000..e571d8b --- /dev/null +++ b/wp-content/themes/twentyfourteen/content.php @@ -0,0 +1,71 @@ + + +
      > + + +
      + + + ', '' ); + else : + the_title( '

      ', '

      ' ); + endif; + ?> + + +
      + + +
      + +
      + +
      + →', 'twentyfourteen' ), + the_title( '', '', false ) + ) ); + + wp_link_pages( array( + 'before' => '', + 'link_before' => '', + 'link_after' => '', + ) ); + ?> +
      + + + ', '', '' ); ?> +
      diff --git a/wp-content/themes/twentyfourteen/css/editor-style.css b/wp-content/themes/twentyfourteen/css/editor-style.css new file mode 100644 index 0000000..010f358 --- /dev/null +++ b/wp-content/themes/twentyfourteen/css/editor-style.css @@ -0,0 +1,721 @@ +/* +Theme Name: Twenty Fourteen +Description: Used to style the TinyMCE editor. +*/ + + +/** + * Table of Contents: + * + * 1.0 - Body + * 2.0 - Headings + * 3.0 - Text Elements + * 4.0 - Links + * 5.0 - Alignment + * 6.0 - Tables + * 7.0 - Images + * 8.0 - Galleries + * 9.0 - Audio/Video + * 10.0 - RTL + * ---------------------------------------------------------------------------- + */ + + +/** + * 1.0 Body + * ---------------------------------------------------------------------------- + */ + +html .mceContentBody { + font-size: 100%; + max-width: 474px; +} + +body { + color: #2b2b2b; + font-family: Lato, sans-serif; + font-weight: 400; + line-height: 1.5; + vertical-align: baseline; +} + + +/** + * 2.0 Headings + * ---------------------------------------------------------------------------- + */ + +h1, +h2, +h3, +h4, +h5, +h6 { + clear: both; + font-weight: 700; + margin: 36px 0 12px; +} + +h1 { + font-size: 26px; + line-height: 1.3846153846; +} + +h2 { + font-size: 24px; + line-height: 1; +} + +h3 { + font-size: 22px; + line-height: 1.0909090909; +} + +h4 { + font-size: 20px; + line-height: 1.2; +} + +h5 { + font-size: 18px; + line-height: 1.3333333333; +} + +h6 { + font-size: 16px; + line-height: 1.5; +} + +h1:first-child, +h2:first-child, +h3:first-child, +h4:first-child, +h5:first-child, +h6:first-child { + margin-top: 0; +} + + +/** + * 3.0 Text Elements + * ---------------------------------------------------------------------------- + */ + +address { + font-style: italic; + margin-bottom: 24px; +} + +abbr[title] { + border-bottom: 1px dotted #2b2b2b; + cursor: help; +} + +b, +strong { + font-weight: 700; +} + +cite { + border: 0; +} + +cite, +dfn, +em, +i { + font-style: italic; +} + +mark, +ins { + background: #fff9c0; + border: 0; + color: inherit; + text-decoration: none; +} + +p { + margin: 0 0 24px; +} + +code, +kbd, +tt, +var, +samp, +pre { + font-family: monospace, serif; + font-size: 15px; + line-height: 1.6; +} + +pre { + border: 1px solid rgba(0, 0, 0, 0.1); + margin-bottom: 24px; + max-width: 100%; + overflow: auto; + padding: 12px; + white-space: pre; + white-space: pre-wrap; + word-wrap: break-word; +} + +blockquote, +q { + quotes: none; +} + +blockquote:before, +blockquote:after, +q:before, +q:after { + content: ""; + content: none; +} + +blockquote { + color: #767676; + font-size: 19px; + font-style: italic; + font-weight: 300; + line-height: 1.2631578947; + margin: 0 0 24px; +} + +blockquote cite, +blockquote small { + color: #2b2b2b; + font-size: 16px; + font-weight: 400; + line-height: 1.5; +} + +blockquote em, +blockquote i, +blockquote cite { + font-style: normal; +} + +blockquote strong, +blockquote b { + font-weight: 400; +} + +small { + font-size: smaller; +} + +big { + font-size: 125%; +} + +sup, +sub { + font-size: 75%; + height: 0; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sup { + bottom: 1ex; +} + +sub { + top: .5ex; +} + +dl { + margin: 0 0 24px; +} + +dt { + font-weight: bold; +} + +dd { + margin: 0 0 24px; +} + +ul, +ol { + list-style: none; + margin: 0 0 24px 20px; + padding-left: 0; +} + +ul { + list-style: disc; +} + +ol { + list-style: decimal; +} + +li > ul, +li > ol { + margin: 0 0 0 20px; +} + +del { + color: #767676; +} + +hr { + background-color: rgba(0, 0, 0, 0.1); + border: 0; + height: 1px; + margin-bottom: 23px; +} + + +/** + * 4.0 Links + * ---------------------------------------------------------------------------- + */ + +a { + color: #24890d; + text-decoration: none; +} + +a:visited { + color: #24890d; +} + +a:focus { + outline: thin dotted; +} + +a:active, +a:hover { + color: #41a62a; + outline: 0; +} + + +/** + * 5.0 Alignment + * ---------------------------------------------------------------------------- + */ + +.alignleft { + float: left; + margin: 7px 24px 7px 0; +} + +.alignright { + float: right; + margin: 7px 0 7px 24px; +} + +.aligncenter { + clear: both; + display: block; + margin: 7px auto; +} + +blockquote.alignleft, +blockquote.alignright { + border-top: 1px solid rgba(0, 0, 0, 0.1); + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + padding-top: 17px; + width: 50%; +} + +blockquote.alignleft p, +blockquote.alignright p { + margin-bottom: 17px; +} + + +/** + * 6.0 Tables + * ---------------------------------------------------------------------------- + */ + +.mceItemTable, +.mce-item-table { + border: 1px solid rgba(0, 0, 0, 0.1); + border-width: 1px 0 0 1px; + border-collapse: separate; + border-spacing: 0; + font-size: 14px; + line-height: 1.2857142857; + margin-bottom: 24px; + width: 100%; +} + +.mceItemTable th, +.mceItemTable caption, +.mce-item-table th, +.mce-item-table caption { + border: 1px solid rgba(0, 0, 0, 0.1); + border-width: 0 1px 1px 0; + font-weight: 700; + padding: 8px; + text-align: left; + text-transform: uppercase; + vertical-align: baseline; +} + +.mceItemTable td, +.mce-item-table td { + border: 1px solid rgba(0, 0, 0, 0.1); + border-width: 0 1px 1px 0; + font-family: Lato, sans-serif; + font-size: 14px; + padding: 8px; + vertical-align: baseline; +} + + +/** + * 7.0 Images + * ---------------------------------------------------------------------------- + */ + +img { + height: auto; + max-width: 474px; + vertical-align: middle; +} + +.wp-caption { + background: transparent; + border: none; + color: #767676; + margin: 0 0 24px 0; + max-width: 474px; + padding: 0; + text-align: left; +} + +.html5-captions .wp-caption { + padding: 0; +} + +.wp-caption.alignleft { + margin: 7px 14px 7px 0; +} + +.html5-captions .wp-caption.alignleft { + margin-right: 24px; +} + +.wp-caption.alignright { + margin: 7px 0 7px 14px; +} + +.wp-caption.alignright img, +.wp-caption.alignright .wp-caption-dd { + padding-left: 10px; +} + +.html5-captions .wp-caption.alignright { + margin-left: 24px; +} + +.html5-captions .wp-caption.alignright img, +.html5-captions .wp-caption.alignright .wp-caption-dd { + padding: 0; +} + +.wp-caption.aligncenter { + margin: 7px auto; +} + +.wp-caption-dt { + margin: 0; +} + +.wp-caption .wp-caption-text, +.wp-caption-dd { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + font-size: 12px; + font-style: italic; + line-height: 1.5; + margin: 9px 0; + padding: 0 10px 0 0; /* Avoid the caption to overflow the width of the image because wp-caption has 10px wider width */ + text-align: left; +} + +.mceTemp + ul, +.mceTemp + ol { + list-style-position: inside; +} + +/** + * 8.0 Gallery + * ----------------------------------------------------------------------------- + */ + +.gallery .gallery-item { + float: left; + margin: 0 4px 4px 0; + overflow: hidden; + padding: 0; + position: relative; +} + +.gallery-columns-1 .gallery-item { + max-width: 100%; + width: auto; +} + +.gallery-columns-2 .gallery-item { + max-width: 48%; + max-width: -webkit-calc(50% - 14px); + max-width: calc(50% - 14px); + width: auto; +} + +.gallery-columns-3 .gallery-item { + max-width: 32%; + max-width: -webkit-calc(33.3% - 11px); + max-width: calc(33.3% - 11px); + width: auto; +} + +.gallery-columns-4 .gallery-item { + max-width: 23%; + max-width: -webkit-calc(25% - 9px); + max-width: calc(25% - 9px); + width: auto; +} + +.gallery-columns-5 .gallery-item { + max-width: 19%; + max-width: -webkit-calc(20% - 8px); + max-width: calc(20% - 8px); + width: auto; +} + +.gallery-columns-6 .gallery-item { + max-width: 15%; + max-width: -webkit-calc(16.7% - 7px); + max-width: calc(16.7% - 7px); + width: auto; +} + +.gallery-columns-7 .gallery-item { + max-width: 13%; + max-width: -webkit-calc(14.28% - 7px); + max-width: calc(14.28% - 7px); + width: auto; +} + +.gallery-columns-8 .gallery-item { + max-width: 11%; + max-width: -webkit-calc(12.5% - 6px); + max-width: calc(12.5% - 6px); + width: auto; +} + +.gallery-columns-9 .gallery-item { + max-width: 9%; + max-width: -webkit-calc(11.1% - 6px); + max-width: calc(11.1% - 6px); + width: auto; +} + +.gallery-columns-1 .gallery-item:nth-of-type(1n), +.gallery-columns-2 .gallery-item:nth-of-type(2n), +.gallery-columns-3 .gallery-item:nth-of-type(3n), +.gallery-columns-4 .gallery-item:nth-of-type(4n), +.gallery-columns-5 .gallery-item:nth-of-type(5n), +.gallery-columns-6 .gallery-item:nth-of-type(6n), +.gallery-columns-7 .gallery-item:nth-of-type(7n), +.gallery-columns-8 .gallery-item:nth-of-type(8n), +.gallery-columns-9 .gallery-item:nth-of-type(9n) { + margin-right: 0; +} + +.gallery-columns-1 .gallery-item:nth-of-type(1n), +.gallery-columns-2 .gallery-item:nth-of-type(2n - 1), +.gallery-columns-3 .gallery-item:nth-of-type(3n - 2), +.gallery-columns-4 .gallery-item:nth-of-type(4n - 3), +.gallery-columns-5 .gallery-item:nth-of-type(5n - 4), +.gallery-columns-6 .gallery-item:nth-of-type(6n - 5), +.gallery-columns-7 .gallery-item:nth-of-type(7n - 6), +.gallery-columns-8 .gallery-item:nth-of-type(8n - 7), +.gallery-columns-9 .gallery-item:nth-of-type(9n - 8) { + margin-left: 12px; /* Compensate for the default negative margin on .gallery, which can't be changed. */ +} + +.gallery .gallery-caption { + background-color: rgba(0, 0, 0, 0.7); + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + color: #fff; + font-size: 12px; + line-height: 1.5; + margin: 0; + max-height: 50%; + opacity: 0; + padding: 6px 8px; + position: absolute; + bottom: 0; + left: 0; + text-align: left; + width: 100%; +} + +.gallery .gallery-caption:before { + content: ""; + height: 100%; + min-height: 49px; + position: absolute; + top: 0; + left: 0; + width: 100%; +} + +.gallery-item:hover .gallery-caption { + opacity: 1; +} + +.gallery-columns-7 .gallery-caption, +.gallery-columns-8 .gallery-caption, +.gallery-columns-9 .gallery-caption { + display: none; +} + + +/** + * 9.0 Audio/Video + * ---------------------------------------------------------------------------- + */ + +.mejs-mediaelement, +.mejs-container .mejs-controls { + background: #000; +} + +.mejs-controls .mejs-time-rail .mejs-time-loaded, +.mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-current { + background: #fff; +} + +.mejs-controls .mejs-time-rail .mejs-time-current { + background: #24890d; +} + +.mejs-controls .mejs-time-rail .mejs-time-total, +.mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-total { + background: rgba(255, 255, 255, .33); +} + +.mejs-controls .mejs-time-rail span, +.mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-total, +.mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-current { + border-radius: 0; +} + +.mejs-overlay-loading { + background: transparent; +} + +.mejs-overlay-button { + background-color: #fff; + background-image: none; + border-radius: 2px; + box-shadow: 1px 1px 1px rgba(0,0,0,.8); + color: #000; + height: 36px; + margin-left: -24px; + width: 48px; +} + +.mejs-overlay-button:before { + -webkit-font-smoothing: antialiased; + content: '\f452'; + display: inline-block; + font: normal 32px/1.125 Genericons; + position: absolute; + top: 1px; + left: 10px; +} + +.mejs-controls .mejs-button button:focus { + outline: none; +} + +.mejs-controls .mejs-button button { + -webkit-font-smoothing: antialiased; + background: none; + color: #fff; + display: inline-block; + font: normal 16px/1 Genericons; +} + +.mejs-playpause-button.mejs-play button:before { + content: '\f452'; +} + +.mejs-playpause-button.mejs-pause button:before { + content: '\f448'; +} + +.mejs-volume-button.mejs-mute button:before { + content: '\f109'; + font-size: 20px; + position: absolute; + top: -2px; + left: 0; +} + +.mejs-volume-button.mejs-unmute button:before { + content: '\f109'; + left: 0; + position: absolute; + top: 0; +} + +.mejs-fullscreen-button button:before { + content: '\f474'; +} + +.mejs-fullscreen-button.mejs-unfullscreen button:before { + content: '\f406'; +} + +.mejs-overlay:hover .mejs-overlay-button { + background-color: #24890d; + color: #fff; +} + +.mejs-controls .mejs-button button:hover { + color: #41a62a; +} + + +/** + * 10.0 RTL + * ---------------------------------------------------------------------------- + */ + +html .mceContentBody.rtl { + direction: rtl; + unicode-bidi: embed; +} + +.rtl ol, +.rtl ul { + margin-left: 0; + margin-right: 24px; +} + +.rtl .wp-caption, +.rtl tr th { + text-align: right; +} + +.rtl td { + text-align: right; +} diff --git a/wp-content/themes/twentyfourteen/css/ie.css b/wp-content/themes/twentyfourteen/css/ie.css new file mode 100644 index 0000000..82142d2 --- /dev/null +++ b/wp-content/themes/twentyfourteen/css/ie.css @@ -0,0 +1,1335 @@ +/** + * Global Styles for older IE versions (previous to IE9). + */ + +pre, +fieldset, +table, +th, +td, +input, +textarea { + border: 1px solid #e5e5e5; +} + +hr { + background-color: #e5e5e5; +} + +button, +input, +select, +textarea { + vertical-align: middle; +} + + +input:focus, +textarea:focus { + border: 1px solid #b2b2b2; +} + +.site-title { + max-width: 71%; +} + +.site-content blockquote.alignleft, +.site-content blockquote.alignright { + border-top: 1px solid #e5e5e5; + border-bottom: 1px solid #e5e5e5; +} + +.post-thumbnail, +a.post-thumbnail:hover { + background: transparent; +} + +.list-view .site-content .hentry { + border-top: 1px solid #e5e5e5; + padding-top: 48px; +} + +.gallery-caption { + background: #000; + filter: alpha(opacity=0); +} + +.gallery-item:hover .gallery-caption { + filter: alpha(opacity=70); +} + +.nav-links { + border-top: 1px solid #e5e5e5; +} + +.post-navigation a, +.image-navigation .previous-image, +.image-navigation .next-image, +.contributor { + border-bottom: 1px solid #e5e5e5; +} + +.contributor-avatar, +.comment-author .avatar { + border: 1px solid #e5e5e5; +} + +.comment-list article, +.comment-list .pingback, +.comment-list .trackback { + border-top: 1px solid #e5e5e5; +} + +.comment-list .reply { + margin-top: 0; +} + +#secondary { + color: #b3b3b3; +} + +.widget abbr[title] { + border-color: #b3b3b3; +} + +.widget pre, +.widget fieldset, +.widget table, +.widget th, +.widget td, +.widget input, +.widget textarea { + border-color: #4d4d4d; +} + +.widget blockquote, +.widget .wp-caption, +.widget_twentyfourteen_ephemera .entry-meta a { + color: #b3b3b3; +} + +.widget del { + color: #666; +} + +.widget hr { + background-color: #4d4d4d; +} + +.widget input, +.widget textarea { + background-color: #1a1a1a; +} + +.widget input:focus, +.widget textarea:focus { + border-color: #262626; +} + +.widget_calendar thead th { + background-color: #1a1a1a; +} + +.widget_twentyfourteen_ephemera > ol > li { + border-bottom: 1px solid #4d4d4d; +} + +.widget_archive li, +.widget_categories li, +.widget_links li, +.widget_meta li, +.widget_nav_menu li, +.widget_pages li, +.widget_recent_comments li, +.widget_recent_entries li, +.widget_categories li ul, +.widget_nav_menu li ul, +.widget_pages li ul { + border-top: 1px solid #4d4d4d; +} + +.content-sidebar .widget pre, +.content-sidebar .widget fieldset, +.content-sidebar .widget table, +.content-sidebar .widget th, +.content-sidebar .widget td, +.content-sidebar .widget input, +.content-sidebar .widget textarea, +.content-sidebar .widget_archive li, +.content-sidebar .widget_categories li, +.content-sidebar .widget_links li, +.content-sidebar .widget_meta li, +.content-sidebar .widget_nav_menu li, +.content-sidebar .widget_pages li, +.content-sidebar .widget_recent_comments li, +.content-sidebar .widget_recent_entries li, +.content-sidebar .widget_categories li ul, +.content-sidebar .widget_nav_menu li ul, +.content-sidebar .widget_pages li ul { + border-color: #e5e5e5; +} + +.content-sidebar .widget hr { + background-color: #e5e5e5; +} + +.content-sidebar .widget input:focus, +.content-sidebar .widget textarea:focus { + border: 1px solid #b2b2b2; +} + +.content-sidebar .widget_calendar thead th { + background-color: #fafafa; +} + +.content-sidebar .widget_twentyfourteen_ephemera > ol > li { + border-bottom: 1px solid #e5e5e5; +} + +.site-footer, +.site-info, +.site-info a { + color: #b3b3b3; +} + +#supplementary + .site-info { + border-top: 1px solid #4d4d4d; +} + +.featured-content { + background: #000; +} + + +/** + * Internet Explorer 8 + */ + +.ie8 img.size-full, +.ie8 img.size-large, +.ie8 img.header-image, +.ie8 img.wp-post-image, +.ie8 img[class*="align"], +.ie8 img[class*="wp-image-"], +.ie8 img[class*="attachment-"] { + height: auto; + width: auto; /* Prevent stretching of full-size and large-size images with height and width attributes in IE8 */ +} + +.ie8 .full-size-link:before, +.ie8 .parent-post-link:before, +.ie8 .site-content span + .byline:before, +.ie8 .site-content span + .comments-link:before, +.ie8 .site-content span + .edit-link:before, +.ie8 .site-content span + .entry-date:before { + content: ""; +} + +.ie8 .attachment span.entry-date:before, +.ie8 .entry-content .edit-link a:before, +.ie8 .entry-meta .edit-link a:before, +.ie8 .site-content .byline a:before, +.ie8 .site-content .comments-link a:before, +.ie8 .site-content .entry-date a:before, +.ie8 .site-content .featured-post:before, +.ie8 .site-content .full-size-link a:before, +.ie8 .site-content .parent-post-link a:before, +.ie8 .site-content .post-format a:before { + display: inline-block; + font: normal 16px/1 Genericons; + text-decoration: inherit; + vertical-align: text-bottom; +} + +.ie8 .site-content .entry-meta > span { + margin-right: 10px; +} + +.ie8 .site-content .format-video .post-format a:before { + content: "\f104"; +} + +.ie8 .site-content .format-audio .post-format a:before { + content: "\f109"; +} + +.ie8 .site-content .format-image .post-format a:before { + content: "\f473"; + position: relative; + top: 1px; +} + +.ie8 .site-content .format-quote .post-format a:before { + content: "\f106"; + margin-right: 2px; +} + +.ie8 .site-content .format-gallery .post-format a:before { + content: "\f103"; + margin-right: 4px; +} + +.ie8 .site-content .format-aside .post-format a:before { + content: "\f101"; + margin-right: 2px; +} + +.ie8 .site-content .format-link .post-format a:before { + content: "\f107"; + position: relative; + top: 1px; +} + +.ie8 .site-content .featured-post:before { + content: "\f308"; + margin-right: 3px; + position: relative; + top: 1px; +} + +.ie8 .site-content .entry-date a:before, +.ie8 .attachment .site-content span.entry-date:before { + content: "\f303"; + margin-right: 1px; + position: relative; + top: 1px; +} + +.ie8 .site-content .byline a:before { + content: "\f304"; +} + +.ie8 .site-content .comments-link a:before { + content: "\f300"; + margin-right: 2px; +} + +.ie8 .entry-content .edit-link a:before, +.ie8 .entry-meta .edit-link a:before { + content: "\f411"; +} + +.ie8 .site-content .full-size-link a:before { + content: "\f402"; + margin-right: 1px; +} + +.ie8 .site-content .parent-post-link a:before { + content: "\f301"; +} + +.ie8 .main-content { + float: left; +} + +.ie8 .content-area { + float: left; + padding-top: 72px; + width: 100%; +} + +.ie8 .site-content { + margin-right: 29.04761904%; + margin-left: 17.61904761%; +} + +.ie8 .search-box-wrapper, +.ie8 .featured-content { + padding-left: 17.61904761%; +} + +.ie8 .header-main { + padding: 0 0 0 30px; +} + +.ie8 .search-toggle { + margin-right: 0; +} + +.ie8 .search-box .search-field { + width: 324px; +} + +.ie8 .site-navigation li .current_page_item > a, +.ie8 .site-navigation li .current_page_ancestor > a, +.ie8 .site-navigation li .current-menu-item > a, +.ie8 .site-navigation li .current-menu-ancestor > a { + background-color: #000; +} + +.ie8 .primary-navigation { + float: right; + font-size: 11px; + margin: 0 1px 0 -10px; + padding: 0; + text-transform: uppercase; +} + +.ie8 .primary-navigation .menu-toggle { + display: none; + padding: 0; +} + +.ie8 .primary-navigation .nav-menu { + border-bottom: 0; + display: block; +} + +.ie8 .primary-navigation.toggled-on { + border-bottom: 0; + margin: 0; + padding: 0; +} + +.ie8 .primary-navigation li { + border: 0; + display: inline-block; + height: 48px; + line-height: 48px; + position: relative; +} + +.ie8 .primary-navigation a { + display: inline-block; + padding: 0 10px; + white-space: nowrap; +} + +.ie8 .primary-navigation ul ul { + background-color: #24890d; + float: left; + margin: 0; + position: absolute; + top: 48px; + left: -999em; + z-index: 99999; +} + +.ie8 .primary-navigation li li { + border: 0; + display: block; + height: auto; + line-height: 1.0909090909; +} + +.ie8 .primary-navigation ul ul ul { + left: -999em; + top: 0; +} + +.ie8 .primary-navigation ul ul a { + padding: 18px 12px; + white-space: normal; + width: 176px; +} + +.ie8 .primary-navigation li:hover > a, +.ie8 .primary-navigation li.focus > a { + background-color: #24890d; + color: #fff; +} + +.ie8 .primary-navigation ul ul a:hover, +.ie8 .primary-navigation ul ul li.focus > a { + background-color: #41a62a; +} + +.ie8 .primary-navigation ul li:hover > ul, +.ie8 .primary-navigation ul li.focus > ul { + left: auto; +} + +.ie8 .primary-navigation ul ul li:hover > ul, +.ie8 .primary-navigation ul ul li.focus > ul { + left: 100%; +} + +.ie8 .archive-header, +.ie8 .page-header { + margin: 0 auto 60px; + padding: 0 10px; +} + +.ie8 .site-content .has-post-thumbnail .entry-header { + margin-top: -48px; +} + +.ie8 .archive-header, +.ie8 .comments-area, +.ie8 .image-navigation, +.ie8 .page-header, +.ie8 .page-content, +.ie8 .post-navigation, +.ie8 .site-content .entry-header, +.ie8 .site-content .entry-content, +.ie8 .site-content .entry-summary, +.ie8 .site-content footer.entry-meta { + margin-right: 54px; + padding-right: 30px; + padding-left: 30px; +} + +.ie8 .list-view .site-content .hentry:first-child, +.ie8 .list-view .site-content .hentry.has-post-thumbnail { + border-top: 0; + padding-top: 0; +} + +.ie8 .comment-list .trackback, +.ie8 .comment-list .pingback, +.ie8 .comment-list article { + margin-bottom: 36px; + padding-top: 36px; +} + +.ie8 .comment-author .avatar { + height: 34px; + top: 2px; + width: 34px; +} + +.ie8 .comment-author, +.ie8 .comment-awaiting-moderation, +.ie8 .comment-content, +.ie8 .comment-list .reply, +.ie8 .comment-metadata { + padding-left: 50px; +} + +.ie8 .comment-list .children { + margin-left: 20px; +} + +.ie8 .full-width .site-content { + margin-right: 0; +} + +.ie8 .full-width .archive-header, +.ie8 .full-width .comments-area, +.ie8 .full-width .image-navigation, +.ie8 .full-width .page-header, +.ie8 .full-width .page-content, +.ie8 .full-width .post-navigation, +.ie8 .full-width .site-content .entry-header, +.ie8 .full-width .site-content .entry-content, +.ie8 .full-width .site-content .entry-summary, +.ie8 .full-width .site-content footer.entry-meta { + padding-right: 30px; + padding-left: 30px; + margin-right: auto; +} + +.ie8 .full-width .hentry.has-post-thumbnail:first-child { + margin-top: -72px; +} + + +.ie8 .singular .site-content .hentry.has-post-thumbnail { + margin-top: 0; +} + +.ie8 .error404 .page-header { + margin-bottom: 24px; +} + +.ie8 .contributor-avatar { + margin-left: -168px; +} + +.ie8 .contributor-summary { + float: left; +} + +.ie8 .site:before { + background-color: #000; + content: ""; + display: block; + height: 100%; + min-height: 100%; + position: absolute; + top: 0; + left: 0; + width: 17.61904761%; + z-index: 2; +} + +.ie8 #secondary { + border: 0; + clear: none; + color: #b3b3b3; + float: left; + margin: 0 0 0 -100%; + min-height: 100vh; + padding: 0 30px; + width: 12.85714285%; +} + +.ie8 .site-description { + display: block; + margin: -3px 0 21px; +} + +.ie8 .secondary-navigation { + font-size: 11px; + margin: 0 -30px 48px; + width: calc(100% + 60px); +} + +.ie8 .secondary-navigation li { + border-top: 1px solid #4d4d4d; + position: relative; +} + +.ie8 .secondary-navigation a { + padding: 10px 30px; +} + +.ie8 .secondary-navigation ul ul { + background-color: #24890d; + position: absolute; + top: 0; + left: -999em; + width: 222px; + z-index: 99999; +} + +.ie8 .secondary-navigation li li { + border-top: 0; +} + +.ie8 .secondary-navigation li:hover > a, +.ie8 .secondary-navigation li.focus > a { + background-color: #24890d; + color: #fff; +} + +.ie8 .secondary-navigation ul ul a:hover, +.ie8 .secondary-navigation ul ul li.focus > a { + background-color: #41a62a; +} + +.ie8 .secondary-navigation ul li:hover > ul, +.ie8 .secondary-navigation ul li.focus > ul { + left: 202px; +} + +.ie8 .content-sidebar { + border: 0; + float: right; + margin-left: -29.04761904%; + padding: 72px 30px 24px; + width: 29.04761904%; +} + +.ie8 #supplementary { + padding: 0; +} + +.ie8 .footer-sidebar { + font-size: 12px; + line-height: 1.5; +} + +.ie8 .footer-sidebar .widget, +.ie8 .primary-sidebar .widget { + font-size: 12px; + line-height: 1.5; +} + +.ie8 .footer-sidebar .widget { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + float: left; + padding: 0 30px; + width: 25%; +} + +.ie8 .footer-sidebar .widget h1, +.ie8 .primary-sidebar .widget h1 { + font-size: 20px; + line-height: 1.2; +} + +.ie8 .footer-sidebar .widget h2, +.ie8 .primary-sidebar .widget h2 { + font-size: 18px; + line-height: 1.3333333333; +} + +.ie8 .footer-sidebar .widget h3, +.ie8 .primary-sidebar .widget h3 { + font-size: 16px; + line-height: 1.5; +} + +.ie8 .footer-sidebar .widget h4, +.ie8 .primary-sidebar .widget h4 { + font-size: 14px; + line-height: 1.7142857142; +} + +.ie8 .footer-sidebar .widget h5, +.ie8 .primary-sidebar .widget h5 { + font-size: 12px; + line-height: 2; +} + +.ie8 .footer-sidebar .widget h6, +.ie8 .primary-sidebar .widget h6 { + font-size: 11px; + line-height: 2.1818181818; +} + +.ie8 .footer-sidebar .widget code, +.ie8 .footer-sidebar .widget kbd, +.ie8 .footer-sidebar .widget tt, +.ie8 .footer-sidebar .widget var, +.ie8 .footer-sidebar .widget samp, +.ie8 .footer-sidebar .widget pre, +.ie8 .primary-sidebar .widget code, +.ie8 .primary-sidebar .widget kbd, +.ie8 .primary-sidebar .widget tt, +.ie8 .primary-sidebar .widget var, +.ie8 .primary-sidebar .widget samp, +.ie8 .primary-sidebar .widget pre { + font-size: 11px; + line-height: 1.6363636363; +} + +.ie8 .footer-sidebar .widget blockquote, +.ie8 .primary-sidebar .widget blockquote { + font-size: 14px; + line-height: 1.2857142857; +} + +.ie8 .footer-sidebar .widget blockquote cite, +.ie8 .primary-sidebar .widget blockquote cite { + font-size: 12px; + line-height: 1.5; +} + +.ie8 .footer-sidebar .widget input, +.ie8 .footer-sidebar .widget textarea, +.ie8 .primary-sidebar .widget input, +.ie8 .primary-sidebar .widget textarea { + font-size: 12px; + padding: 3px 2px 4px 4px; +} + +.ie8 .footer-sidebar .widget input[type="button"], +.ie8 .footer-sidebar .widget input[type="reset"], +.ie8 .footer-sidebar .widget input[type="submit"], +.ie8 .primary-sidebar .widget input[type="button"], +.ie8 .primary-sidebar .widget input[type="reset"], +.ie8 .primary-sidebar .widget input[type="submit"] { + padding: 5px 15px 4px; +} + +.ie8 .footer-sidebar .widget .widget-title, +.ie8 .primary-sidebar .widget .widget-title { + font-size: 11px; + font-weight: 700; + line-height: 1.6363636363; + margin-bottom: 18px; +} + +.ie8 .footer-sidebar .widget_twentyfourteen_ephemera .entry-title, +.ie8 .footer-sidebar .widget_twentyfourteen_ephemera .entry-meta, +.ie8 .footer-sidebar .widget_twentyfourteen_ephemera .wp-caption-text, +.ie8 .footer-sidebar .widget_twentyfourteen_ephemera .post-format-archive-link, +.ie8 .footer-sidebar .widget_twentyfourteen_ephemera .entry-content table, +.ie8 .primary-sidebar .widget_twentyfourteen_ephemera .entry-title, +.ie8 .primary-sidebar .widget_twentyfourteen_ephemera .entry-meta, +.ie8 .primary-sidebar .widget_twentyfourteen_ephemera .wp-caption-text, +.ie8 .primary-sidebar .widget_twentyfourteen_ephemera .post-format-archive-link, +.ie8 .primary-sidebar .widget_twentyfourteen_ephemera .entry-content table { + font-size: 11px; + line-height: 1.6363636363; +} + +.ie8 .footer-sidebar .widget_archive li, +.ie8 .footer-sidebar .widget_categories li, +.ie8 .footer-sidebar .widget_links li, +.ie8 .footer-sidebar .widget_meta li, +.ie8 .footer-sidebar .widget_nav_menu li, +.ie8 .footer-sidebar .widget_pages li, +.ie8 .footer-sidebar .widget_recent_comments li, +.ie8 .footer-sidebar .widget_recent_entries li, +.ie8 .primary-sidebar .widget_archive li, +.ie8 .primary-sidebar .widget_categories li, +.ie8 .primary-sidebar .widget_links li, +.ie8 .primary-sidebar .widget_meta li, +.ie8 .primary-sidebar .widget_nav_menu li, +.ie8 .primary-sidebar .widget_pages li, +.ie8 .primary-sidebar .widget_recent_comments li, +.ie8 .primary-sidebar .widget_recent_entries li { + border-top: 0; + padding: 0 0 6px; +} + +.ie8 .footer-sidebar .widget_categories li ul, +.ie8 .footer-sidebar .widget_nav_menu li ul, +.ie8 .footer-sidebar .widget_pages li ul, +.ie8 .primary-sidebar .widget_categories li ul, +.ie8 .primary-sidebar .widget_nav_menu li ul, +.ie8 .primary-sidebar .widget_pages li ul { + border-top: 0; + margin-top: 0; +} + +.ie8 .grid .featured-content .entry-header { + border-color: #000; + border-style: solid; + border-width: 12px 10px; + height: 96px; + padding: 0; +} + +.ie8 .featured-content { + padding-left: 17.61904761%; +} + +.ie8 .grid .featured-content .hentry { + float: left; + width: 33.3333333%; +} + +.ie8 .grid .featured-content .hentry:nth-child( 3n+1 ) { + clear: both; +} + +.ie8 .grid .featured-content .entry-header { + height: 120px; +} + +.ie8 .slider .featured-content .entry-title { + font-size: 33px; + line-height: 1.0909090909; +} + +.ie8 .slider .featured-content .entry-header { + min-height: inherit; + padding: 24px 30px 48px; + position: absolute; + left: 0; + bottom: 0; + width: 50%; + z-index: 3; +} + +.ie8 .slider-control-paging { + background: transparent; + margin-top: -48px; + padding-left: 24px; + width: 50%; +} + +.ie8 .slider-control-paging li { + margin: 12px 12px 12px 0; +} + +.ie8 .slider-control-paging a { + height: 24px; + width: 24px; +} + +.ie8 .slider-control-paging a:before { + top: 6px; + left: 6px; +} + +.ie8 .slider-direction-nav { + clear: none; + float: right; + margin-top: -48px; + width: 98px; +} + +.ie8 .slider-direction-nav li:first-child { + padding: 0 1px 0 0; +} + +.ie8 .slider-direction-nav li { + border: 0; + padding: 0 0 0 1px; +} + +.ie8 .slider-direction-nav a { + height: 48px; +} + +.ie8 .slider-direction-nav a:before { + line-height: 48px; +} + + +/** + * Internet Explorer 7 + */ + +.ie7 audio, +.ie7 canvas, +.ie7 video { + display: inline; + zoom: 1; +} + +.ie7 button, +.ie7 input, +.ie7 select, +.ie7 textarea { + vertical-align: middle; +} + +.ie7 button, +.ie7 input[type="button"], +.ie7 input[type="reset"], +.ie7 input[type="submit"] { + overflow: visible; +} + +.ie7 .screen-reader-text { + clip: rect(1px 1px 1px 1px); +} + +.ie7 .site, +.ie7 .site-header { + max-width: 100%; +} + +.ie7 .search-toggle { + line-height: 45px; + margin-right: 190px; + padding: 0 20px; + text-transform: uppercase; + width: auto; +} + +.ie7 .search-toggle .screen-reader-text { + color: #fff; + position: relative; /* Override inherited `absolute` value set in style.css. */ +} + +.ie7 .search-box { + height: 24px; + padding: 12px 0; +} + +.ie7 .search-box .search-field { + margin: 0 10px; + width: 33%; +} + +.ie7 .site-navigation li { + border-top: 1px solid #4d4d4d; +} + +.ie7 .primary-navigation .nav-menu, +.ie7 .secondary-navigation { + border-bottom: 1px solid #4d4d4d; +} + +.ie7 .secondary-navigation { + margin: 48px auto; + max-width: 474px +} + +.ie7 .content-area { + padding-top: 48px; +} + +.ie7 .hentry { + max-width: 100%; +} + +.ie7 .menu-toggle { + color: #fff; + font-weight: 400; + font-size: 16px; + line-height: 45px; + text-transform: uppercase; + width: 200px; +} + +.ie7 .post-thumbnail img { + display: block; + margin: 0 auto; +} + +.ie7 .entry-meta .tag-links a { + margin-left: 0; +} + +.ie7 .content-sidebar { + padding: 48px 10px; +} + +.ie7 .singular .hentry.has-post-thumbnail { + margin-top: -48px; +} + +.ie7 .entry-meta > span, +.ie7 .widget_twentyfourteen_ephemera .entry-title { + margin-right: 20px; +} + +.ie7 #secondary { + border-bottom: 1px solid #4d4d4d; +} + +.ie7 .content-sidebar { + border-top: 1px solid #e5e5e5; + border-bottom: 1px solid #e5e5e5; +} + +.ie7 .widget { + margin: 0 auto 48px; + max-width: 474px; +} + +.ie7 .content-sidebar .widget_twentyfourteen_ephemera .widget-title { + padding-top: 7px; +} + +.ie7 .slider .featured-content .hentry { + display: block; +} + +.ie7 .featured-content .entry-header { + min-height: 0; +} + +.ie7 .slider-control-paging a { + line-height: 40px; + text-indent: 0; +} + +.ie7 .slider-control-paging .slider-active { + color: #41a62a; +} + +.ie7 .slider-direction-nav { + border-top: 2px solid #fff; +} + +.ie7 .slider-direction-nav li { + border: 0; + width: 49%; +} + +.ie7 .slider-direction-nav a { + font-size: 16px; + line-height: 45px; + text-transform: uppercase; +} + +.ie7 .slider-direction-nav a:hover { + background-color: #000; + color: #41a62a; +} + +.ie7 .search-toggle { + line-height: 45px; + margin-right: 190px; +} + +.ie7 .featured-content .post-thumbnail, +.ie7 .slider .featured-content .post-thumbnail { + padding-top: 0; +} + +.ie7 .featured-content .post-thumbnail img { + position: relative; +} + +.ie7 .featured-content .entry-header { + width: auto; +} + +.ie7 .grid .featured-content .hentry { + float: left; + margin: 0 auto; + max-width: 672px; + width: 33.333333%; +} + +.ie7 .slider .featured-content .entry-header { + margin: 0 auto; + max-width: 1038px; +} + +.ie7 .slider-control-paging { + float: none; + margin: -24px auto 0; + max-width: 1038px; + width: auto; +} + + +/** + * RTL for Internet Explorer 8 & 7 + */ + +.rtl .attachment a, +.rtl .gallery a, +.rtl .wp-caption a, +.rtl .widget_twentyfourteen_ephemera .entry-content a { + display: inline; +} + + +/** + * RTL overrides for Internet Explorer 8 + */ + +.ie8 .rtl .site-content .entry-meta > span { + margin-right: auto; + margin-left: 10px; +} + +.ie8 .rtl .site-content .format-quote .post-format a:before { + margin-right: auto; + margin-left: 2px; +} + +.ie8 .rtl .site-content .format-gallery .post-format a:before { + margin-right: auto; + margin-left: 4px; +} + +.ie8 .rtl .site-content .format-aside .post-format a:before { + margin-right: auto; + margin-left: 2px; +} + +.ie8 .rtl .site-content .featured-post:before { + margin-right: auto; + margin-left: 3px; +} + +.ie8 .rtl .site-content .entry-date a:before, +.ie8 .rtl .attachment .site-content span.entry-date:before { + margin-right: auto; + margin-left: 1px; +} + +.ie8 .rtl .site-content .comments-link a:before { + margin-right: auto; + margin-left: 2px; +} + +.ie8 .rtl .site-content .full-size-link a:before { + margin-right: auto; + margin-left: 1px; +} + +.ie8 .rtl .main-content { + float: right; +} + +.ie8 .rtl .content-area { + float: right; +} + +.ie8 .rtl .site-content { + margin-right: 17.61904761%; + margin-left: 29.04761904%; +} + +.ie8 .rtl .search-box-wrapper, +.ie8 .rtl .featured-content { + padding-right: 17.61904761%; + padding-left: 0; +} + +.ie8 .rtl .header-main { + padding: 0 30px 0 0; +} + +.ie8 .rtl .search-toggle { + margin-right: auto; + margin-left: 0; +} + +.ie8 .rtl .primary-navigation { + float: left; + margin: 0 -10px 0 1px; +} + +.ie8 .rtl .primary-navigation ul ul { + float: right; + right: -999em; + left: auto; +} + +.ie8 .rtl .primary-navigation ul ul ul { + right: -999em; + left: auto; +} + +.ie8 .rtl .primary-navigation ul li:hover > ul, +.ie8 .rtl .primary-navigation ul li.focus > ul { + right: auto; + left: auto; +} + +.ie8 .rtl .primary-navigation ul ul li:hover > ul, +.ie8 .rtl .primary-navigation ul ul li.focus > ul { + right: 100%; + left: auto; +} + +.ie8 .rtl .entry-meta .tag-links a:before { + right: -8px; +} + +.ie8 .rtl .archive-header, +.ie8 .rtl .comments-area, +.ie8 .rtl .image-navigation, +.ie8 .rtl .page-header, +.ie8 .rtl .page-content, +.ie8 .rtl .post-navigation, +.ie8 .rtl .site-content .entry-header, +.ie8 .rtl .site-content .entry-content, +.ie8 .rtl .site-content .entry-summary, +.ie8 .rtl .site-content footer.entry-meta { + margin-right: auto; + margin-left: 54px; +} + +.ie8 .rtl .comment-author, +.ie8 .rtl .comment-awaiting-moderation, +.ie8 .rtl .comment-content, +.ie8 .rtl .comment-list .reply, +.ie8 .rtl .comment-metadata { + padding-right: 50px; + padding-left: 0; +} + +.ie8 .rtl .comment-list .children { + margin-right: 20px; + margin-left: auto; +} + + +.ie8 .rtl.full-width .site-content { + margin-left: 0; +} + +.ie8 .rtl.full-width .archive-header, +.ie8 .rtl.full-width .comments-area, +.ie8 .rtl.full-width .image-navigation, +.ie8 .rtl.full-width .page-header, +.ie8 .rtl.full-width .page-content, +.ie8 .rtl.full-width .post-navigation, +.ie8 .rtl.full-width .site-content .entry-header, +.ie8 .rtl.full-width .site-content .entry-content, +.ie8 .rtl.full-width .site-content .entry-summary, +.ie8 .rtl.full-width .site-content footer.entry-meta { + margin-left: auto; +} + +.ie8 .rtl .contributor-avatar { + margin-right: -168px; + margin-left: auto; +} + +.ie8 .rtl .contributor-summary { + float: right; +} + +.ie8 .rtl .site:before { + right: 0; + left: auto; +} + +.ie8 .rtl #secondary { + float: right; + margin: 0 -100% 0 0; +} + +.ie8 .rtl .secondary-navigation ul ul { + right: -999em; + left: auto; +} + +.ie8 .rtl .secondary-navigation ul li:hover > ul, +.ie8 .rtl .secondary-navigation ul li.focus > ul { + right: 202px; + left: auto; +} + +.ie8 .rtl .content-sidebar { + float: left; + margin-right: -29.04761904%; + margin-left: auto; +} + +.ie8 .rtl .footer-sidebar .widget { + float: right; +} + +.ie8 .rtl .featured-content { + padding-right: 17.61904761%; + padding-left: 0; +} + +.ie8 .rtl.grid .featured-content .hentry { + float: right; +} + +.ie8 .rtl.slider .featured-content .entry-header { + right: 0; + left: auto; +} + +.ie8 .rtl .slider-control-paging { + padding-right: 24px; + padding-left: 0; +} + +.ie8 .rtl .slider-control-paging li { + margin: 12px 0 12px 12px; +} + +.ie8 .rtl .slider-control-paging a:before { + right: 6px; + left: auto; +} + +.ie8 .rtl .slider-direction-nav { + float: left; +} + +.ie8 .rtl .slider-direction-nav li { + padding: 0 1px 0 0; +} + +.ie8 .rtl .slider-direction-nav li:first-child { + padding: 0 0 0 1px; +} + + +/** + * RTL overrides for Internet Explorer 7 + */ + +.ie7 .rtl.grid .featured-content .hentry { + float: right; +} + +.ie7 .rtl .slider-control-paging { + float: none; + margin: -24px auto 0; +} + +.ie7 .rtl .entry-meta .tag-links a { + margin-right: 0; + margin-left: auto; +} + +.ie7 .rtl .search-toggle { + margin-right: auto; + margin-left: 190px; +} \ No newline at end of file diff --git a/wp-content/themes/twentyfourteen/featured-content.php b/wp-content/themes/twentyfourteen/featured-content.php new file mode 100644 index 0000000..1a623ac --- /dev/null +++ b/wp-content/themes/twentyfourteen/featured-content.php @@ -0,0 +1,39 @@ + + + diff --git a/wp-content/themes/twentyfourteen/footer.php b/wp-content/themes/twentyfourteen/footer.php new file mode 100644 index 0000000..b297a2e --- /dev/null +++ b/wp-content/themes/twentyfourteen/footer.php @@ -0,0 +1,28 @@ + + + + +
      + + + +
      + + +
      +
      + + + + + \ No newline at end of file diff --git a/wp-content/themes/twentyfourteen/functions.php b/wp-content/themes/twentyfourteen/functions.php new file mode 100644 index 0000000..2e66b2e --- /dev/null +++ b/wp-content/themes/twentyfourteen/functions.php @@ -0,0 +1,519 @@ + for posts and comments. + add_theme_support( 'automatic-feed-links' ); + + // Enable support for Post Thumbnails, and declare two sizes. + add_theme_support( 'post-thumbnails' ); + set_post_thumbnail_size( 672, 372, true ); + add_image_size( 'twentyfourteen-full-width', 1038, 576, true ); + + // This theme uses wp_nav_menu() in two locations. + register_nav_menus( array( + 'primary' => __( 'Top primary menu', 'twentyfourteen' ), + 'secondary' => __( 'Secondary menu in left sidebar', 'twentyfourteen' ), + ) ); + + /* + * Switch default core markup for search form, comment form, and comments + * to output valid HTML5. + */ + add_theme_support( 'html5', array( + 'search-form', 'comment-form', 'comment-list', 'gallery', 'caption' + ) ); + + /* + * Enable support for Post Formats. + * See http://codex.wordpress.org/Post_Formats + */ + add_theme_support( 'post-formats', array( + 'aside', 'image', 'video', 'audio', 'quote', 'link', 'gallery', + ) ); + + // This theme allows users to set a custom background. + add_theme_support( 'custom-background', apply_filters( 'twentyfourteen_custom_background_args', array( + 'default-color' => 'f5f5f5', + ) ) ); + + // Add support for featured content. + add_theme_support( 'featured-content', array( + 'featured_content_filter' => 'twentyfourteen_get_featured_posts', + 'max_posts' => 6, + ) ); + + // This theme uses its own gallery styles. + add_filter( 'use_default_gallery_style', '__return_false' ); +} +endif; // twentyfourteen_setup +add_action( 'after_setup_theme', 'twentyfourteen_setup' ); + +/** + * Adjust content_width value for image attachment template. + * + * @since Twenty Fourteen 1.0 + */ +function twentyfourteen_content_width() { + if ( is_attachment() && wp_attachment_is_image() ) { + $GLOBALS['content_width'] = 810; + } +} +add_action( 'template_redirect', 'twentyfourteen_content_width' ); + +/** + * Getter function for Featured Content Plugin. + * + * @since Twenty Fourteen 1.0 + * + * @return array An array of WP_Post objects. + */ +function twentyfourteen_get_featured_posts() { + /** + * Filter the featured posts to return in Twenty Fourteen. + * + * @since Twenty Fourteen 1.0 + * + * @param array|bool $posts Array of featured posts, otherwise false. + */ + return apply_filters( 'twentyfourteen_get_featured_posts', array() ); +} + +/** + * A helper conditional function that returns a boolean value. + * + * @since Twenty Fourteen 1.0 + * + * @return bool Whether there are featured posts. + */ +function twentyfourteen_has_featured_posts() { + return ! is_paged() && (bool) twentyfourteen_get_featured_posts(); +} + +/** + * Register three Twenty Fourteen widget areas. + * + * @since Twenty Fourteen 1.0 + */ +function twentyfourteen_widgets_init() { + require get_template_directory() . '/inc/widgets.php'; + register_widget( 'Twenty_Fourteen_Ephemera_Widget' ); + + register_sidebar( array( + 'name' => __( 'Primary Sidebar', 'twentyfourteen' ), + 'id' => 'sidebar-1', + 'description' => __( 'Main sidebar that appears on the left.', 'twentyfourteen' ), + 'before_widget' => '', + 'before_title' => '

      ', + 'after_title' => '

      ', + ) ); + register_sidebar( array( + 'name' => __( 'Content Sidebar', 'twentyfourteen' ), + 'id' => 'sidebar-2', + 'description' => __( 'Additional sidebar that appears on the right.', 'twentyfourteen' ), + 'before_widget' => '', + 'before_title' => '

      ', + 'after_title' => '

      ', + ) ); + register_sidebar( array( + 'name' => __( 'Footer Widget Area', 'twentyfourteen' ), + 'id' => 'sidebar-3', + 'description' => __( 'Appears in the footer section of the site.', 'twentyfourteen' ), + 'before_widget' => '', + 'before_title' => '

      ', + 'after_title' => '

      ', + ) ); +} +add_action( 'widgets_init', 'twentyfourteen_widgets_init' ); + +/** + * Register Lato Google font for Twenty Fourteen. + * + * @since Twenty Fourteen 1.0 + * + * @return string + */ +function twentyfourteen_font_url() { + $font_url = ''; + /* + * Translators: If there are characters in your language that are not supported + * by Lato, translate this to 'off'. Do not translate into your own language. + */ + if ( 'off' !== _x( 'on', 'Lato font: on or off', 'twentyfourteen' ) ) { + $query_args = array( + 'family' => urlencode( 'Lato:300,400,700,900,300italic,400italic,700italic' ), + 'subset' => urlencode( 'latin,latin-ext' ), + ); + $font_url = add_query_arg( $query_args, '//fonts.googleapis.com/css' ); + } + + return $font_url; +} + +/** + * Enqueue scripts and styles for the front end. + * + * @since Twenty Fourteen 1.0 + */ +function twentyfourteen_scripts() { + // Add Lato font, used in the main stylesheet. + wp_enqueue_style( 'twentyfourteen-lato', twentyfourteen_font_url(), array(), null ); + + // Add Genericons font, used in the main stylesheet. + wp_enqueue_style( 'genericons', get_template_directory_uri() . '/genericons/genericons.css', array(), '3.0.3' ); + + // Load our main stylesheet. + wp_enqueue_style( 'twentyfourteen-style', get_stylesheet_uri() ); + + // Load the Internet Explorer specific stylesheet. + wp_enqueue_style( 'twentyfourteen-ie', get_template_directory_uri() . '/css/ie.css', array( 'twentyfourteen-style' ), '20131205' ); + wp_style_add_data( 'twentyfourteen-ie', 'conditional', 'lt IE 9' ); + + if ( is_singular() && comments_open() && get_option( 'thread_comments' ) ) { + wp_enqueue_script( 'comment-reply' ); + } + + if ( is_singular() && wp_attachment_is_image() ) { + wp_enqueue_script( 'twentyfourteen-keyboard-image-navigation', get_template_directory_uri() . '/js/keyboard-image-navigation.js', array( 'jquery' ), '20130402' ); + } + + if ( is_active_sidebar( 'sidebar-3' ) ) { + wp_enqueue_script( 'jquery-masonry' ); + } + + if ( is_front_page() && 'slider' == get_theme_mod( 'featured_content_layout' ) ) { + wp_enqueue_script( 'twentyfourteen-slider', get_template_directory_uri() . '/js/slider.js', array( 'jquery' ), '20131205', true ); + wp_localize_script( 'twentyfourteen-slider', 'featuredSliderDefaults', array( + 'prevText' => __( 'Previous', 'twentyfourteen' ), + 'nextText' => __( 'Next', 'twentyfourteen' ) + ) ); + } + + wp_enqueue_script( 'twentyfourteen-script', get_template_directory_uri() . '/js/functions.js', array( 'jquery' ), '20140616', true ); +} +add_action( 'wp_enqueue_scripts', 'twentyfourteen_scripts' ); + +/** + * Enqueue Google fonts style to admin screen for custom header display. + * + * @since Twenty Fourteen 1.0 + */ +function twentyfourteen_admin_fonts() { + wp_enqueue_style( 'twentyfourteen-lato', twentyfourteen_font_url(), array(), null ); +} +add_action( 'admin_print_scripts-appearance_page_custom-header', 'twentyfourteen_admin_fonts' ); + +if ( ! function_exists( 'twentyfourteen_the_attached_image' ) ) : +/** + * Print the attached image with a link to the next attached image. + * + * @since Twenty Fourteen 1.0 + */ +function twentyfourteen_the_attached_image() { + $post = get_post(); + /** + * Filter the default Twenty Fourteen attachment size. + * + * @since Twenty Fourteen 1.0 + * + * @param array $dimensions { + * An array of height and width dimensions. + * + * @type int $height Height of the image in pixels. Default 810. + * @type int $width Width of the image in pixels. Default 810. + * } + */ + $attachment_size = apply_filters( 'twentyfourteen_attachment_size', array( 810, 810 ) ); + $next_attachment_url = wp_get_attachment_url(); + + /* + * Grab the IDs of all the image attachments in a gallery so we can get the URL + * of the next adjacent image in a gallery, or the first image (if we're + * looking at the last image in a gallery), or, in a gallery of one, just the + * link to that image file. + */ + $attachment_ids = get_posts( array( + 'post_parent' => $post->post_parent, + 'fields' => 'ids', + 'numberposts' => -1, + 'post_status' => 'inherit', + 'post_type' => 'attachment', + 'post_mime_type' => 'image', + 'order' => 'ASC', + 'orderby' => 'menu_order ID', + ) ); + + // If there is more than 1 attachment in a gallery... + if ( count( $attachment_ids ) > 1 ) { + foreach ( $attachment_ids as $attachment_id ) { + if ( $attachment_id == $post->ID ) { + $next_id = current( $attachment_ids ); + break; + } + } + + // get the URL of the next image attachment... + if ( $next_id ) { + $next_attachment_url = get_attachment_link( $next_id ); + } + + // or get the URL of the first image attachment. + else { + $next_attachment_url = get_attachment_link( array_shift( $attachment_ids ) ); + } + } + + printf( '%2$s', + esc_url( $next_attachment_url ), + wp_get_attachment_image( $post->ID, $attachment_size ) + ); +} +endif; + +if ( ! function_exists( 'twentyfourteen_list_authors' ) ) : +/** + * Print a list of all site contributors who published at least one post. + * + * @since Twenty Fourteen 1.0 + */ +function twentyfourteen_list_authors() { + $contributor_ids = get_users( array( + 'fields' => 'ID', + 'orderby' => 'post_count', + 'order' => 'DESC', + 'who' => 'authors', + ) ); + + foreach ( $contributor_ids as $contributor_id ) : + $post_count = count_user_posts( $contributor_id ); + + // Move on if user has not published a post (yet). + if ( ! $post_count ) { + continue; + } + ?> + +
      +
      +
      +
      +

      +

      + +

      + + + +
      +
      +
      + + = 2 || $page >= 2 ) && ! is_404() ) { + $title = "$title $sep " . sprintf( __( 'Page %s', 'twentyfourteen' ), max( $paged, $page ) ); + } + + return $title; +} +add_filter( 'wp_title', 'twentyfourteen_wp_title', 10, 2 ); + +// Implement Custom Header features. +require get_template_directory() . '/inc/custom-header.php'; + +// Custom template tags for this theme. +require get_template_directory() . '/inc/template-tags.php'; + +// Add Customizer functionality. +require get_template_directory() . '/inc/customizer.php'; + +/* + * Add Featured Content functionality. + * + * To overwrite in a plugin, define your own Featured_Content class on or + * before the 'setup_theme' hook. + */ +if ( ! class_exists( 'Featured_Content' ) && 'plugins.php' !== $GLOBALS['pagenow'] ) { + require get_template_directory() . '/inc/featured-content.php'; +} diff --git a/wp-content/themes/twentyfourteen/genericons/COPYING.txt b/wp-content/themes/twentyfourteen/genericons/COPYING.txt new file mode 100644 index 0000000..aece214 --- /dev/null +++ b/wp-content/themes/twentyfourteen/genericons/COPYING.txt @@ -0,0 +1,9 @@ +Genericons is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. + +The fonts are distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +As a special exception, if you create a document which uses this font, and embed this font or unaltered portions of this font into the document, this font does not by itself cause the resulting document to be covered by the GNU General Public License. This exception does not however invalidate any other reasons why the document might be covered by the GNU General Public License. If you modify this font, you may extend this exception to your version of the font, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. + +This license does not convey any intellectual property rights to third party trademarks that may be included in the icon font; such marks remain subject to all rights and guidelines of use of their owner. \ No newline at end of file diff --git a/wp-content/themes/twentyfourteen/genericons/Genericons-Regular.otf b/wp-content/themes/twentyfourteen/genericons/Genericons-Regular.otf new file mode 100644 index 0000000..5cd41e8 Binary files /dev/null and b/wp-content/themes/twentyfourteen/genericons/Genericons-Regular.otf differ diff --git a/wp-content/themes/twentyfourteen/genericons/LICENSE.txt b/wp-content/themes/twentyfourteen/genericons/LICENSE.txt new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/wp-content/themes/twentyfourteen/genericons/LICENSE.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/wp-content/themes/twentyfourteen/genericons/README.txt b/wp-content/themes/twentyfourteen/genericons/README.txt new file mode 100644 index 0000000..7a0a92e --- /dev/null +++ b/wp-content/themes/twentyfourteen/genericons/README.txt @@ -0,0 +1,123 @@ + ___ ____ __ _ ____ ____ __ ___ __ __ _ ____ + / __)( __)( ( \( __)( _ \( )/ __)/ \ ( ( \/ ___) +( (_ \ ) _) / / ) _) ) / )(( (__( O )/ /\___ \ + \___/(____)\_)__)(____)(__\_)(__)\___)\__/ \_)__)(____/ + + +Genericons are vector icons embedded in a webfont designed to be clean and simple keeping with a generic aesthetic. + +Use genericons for instant HiDPI, to change icon colors on the fly, or even with CSS effects such as drop-shadows or gradients! + + +_ _ ____ ____ ____ ____ +| | [__ |__| | __ |___ +|__| ___] | | |__] |___ + + +To use it, place the font folder in your stylesheet directory and paste this in your CSS file: + +/* =Genericons, thanks to FontSquirrel.com for conversion! +-------------------------------------------------------------- */ +@font-face { + font-family: 'Genericons'; + src: url('font/genericons-regular-webfont.eot'); + src: url('font/genericons-regular-webfont.eot?#iefix') format('embedded-opentype'), + url('font/genericons-regular-webfont.woff') format('woff'), + url('font/genericons-regular-webfont.ttf') format('truetype'), + url('font/genericons-regular-webfont.svg#genericonsregular') format('svg'); + font-weight: normal; + font-style: normal; + +} + +Note: the above only works if you don't use a CDN. If you do, or don't know what that is, you should use the syntax that's embedded in genericons.css. + +From then on, you can create an icon like this: + +.my-icon:before { + content: '\f101'; + display: inline-block; + -webkit-font-smoothing: antialiased; + font: normal 16px/1 'Genericons'; + vertical-align: top; +} + +This will output a comment icon before every element with the class "my-icon". The "content: '\f101';" part of this CSS is easily copied from the helper tool at http://genericons.com/ + +You can also use the bundled example.css if you'd rather insert the icons using HTML tags. + + +_ _ ____ ___ ____ ____ +|\ | | | | |___ [__ +| \| |__| | |___ ___] + + +Photoshop mockups: + +Genericons-Regular.otf found in the root directory of this zip has not been web-font-ified. So you can drop it in your system fonts folder and use the font in Photoshop if you like. + +For those of you using Genericons in your Photoshop mockup, remember to delete the old version of the font from Font Book, and grab the new one from the zip file. This also affects using it in your webdesigns: if you have an old version of the font installed locally, that's the font that'll be used in your website as well, so if you're missing icons, check for old versions of the font on your system. + +Pixel grid: + +Note that Genericons has been designed for a 16x16 pixel grid. That means it'll look sharp at font-size: 16px exactly. It'll also be crisp at multiples thereof, such as 32px or 64px. It'll also look reasonably crisp at in-between font sizes such as 24px or 48px, but not quite as crisp as 16 or 32. Please don't set the font-size to 17px, though, that'll just look terrible. + +Also note the CSS property "-webkit-font-smoothing: antialiased". That makes the icons look great in WebKit browsers. Please see http://noscope.com/2012/font-smoothing for more info. + +Updates: + +We don't often update icons, but do very carefully when we get good feedback suggesting improvements. Please be mindful if you upgrade, and check that the updated icons behave as you intended. + + + +____ _ _ ____ _ _ ____ ____ _ ____ ____ +| |__| |__| |\ | | __ |___ | | | | __ +|___ | | | | | \| |__] |___ |___ |__| |__] + +V3.0.3: +Bunch of updates mostly. +- Two new icons, Dropbox and Fullscreen. +- Updates to all icons containing an exclamation mark. +- Updates to Image and Quote. +- Nicer "Share" icon. +- Bigger default Linkedin icon. + +V3.0.2: +A slew of new stuff and updates. +- Social icons: Skype, Digg, Reddit, Stumbleupon, Pocket. +- New generic icons: heart, lock and print. +- New editing icons: code, bold, italic, image +- New interaction icons: subscribe, unsubscribe, subscribed, reply all, reply, flag. +- The hyperlink icon has been updated to be clearer, chunkier. +- The "home" icon has been updated for style, size and clarity. +- The email icon has been updated for style and clarity, and to fit with the new subscribe icons. +- The document icon has been updated for style. +- The "pin" icon has been updated for style and clarity. +- The Twitter icon has been scaled down to fit with the other social icons. + +V3.0.1: +Mostly maintenance. +- Fixed an issue with the example page that showed an old "top" icon instead of the actual NEW "refresh" icon. +- Added inverse Google+ and Path. +- Replaced tabs with spaces in the helper CSS. +- Changed the Genericons.com copy/paste tool to serve span's instead of div's for casual icon insertion. It's being converted to "inline-block" anyway. + +V3.0: +Mainly maintenance and a few new icons. +- Fast forward, rewind, PollDaddy, Notice, Info, Help, Portfolio +- Updated the feed icon. It's a bit smaller now for consistency, the previous one was rather big. +- So, the previous version numbering, 2.09, wasn't very PHP version compare friendly. So from now on it'll be 3.0, 3.1 etc. Props Ipstenu. +- Genericons.com now has a mini release blog. +- The CSS has prettier formatting, props Konstantin Obenland. + +V2.09: +Updated Facebook icon to new version. Updated Instagram logo to use new one-color version. Updated Google+ icon to use same radius as Instagram and Facebook. Added a bunch of new icons, cog, unapprove, cart, media player buttons, tablet, send to tablet. + +V2.06: +Included Base64 encoded version. This is necessary for Genericons to work with CDNs in Firefox. Firefox blocks fonts linked from a different domain. A CDN (typically s.example.com) usually puts the font on a subdomain, and is hence blocked in Firefox. + +V2.05: +Added a bunch of new icons, including upload to cloud, download to cloud, many more. + +V2: +Initial public release \ No newline at end of file diff --git a/wp-content/themes/twentyfourteen/genericons/example.html b/wp-content/themes/twentyfourteen/genericons/example.html new file mode 100644 index 0000000..cdc7d04 --- /dev/null +++ b/wp-content/themes/twentyfourteen/genericons/example.html @@ -0,0 +1,464 @@ + + + +Genericons + + + + + +
      + +

      Genericons Usage

      + +

      Copy the font folder and the genericons.css file together into your project. Link the CSS in your HTML:

      + +

      <link href="path/to/genericons.css" rel="stylesheet">

      + +

      Drop in the following HTML with the name of the icon you want to display:

      + +

      <div class="genericon genericon-standard"></div>

      + +
      + + +
      +
      +
      + +
      +
      +
      + +
      +
      + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + + +
      +
      +
      +
      +
      +
      +
      +
      +
      + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + + +
      +
      +
      +
      + +
      + +

      If you want to insert an icon manually using the :before selector, you can setup CSS rules like the following example. Make sure to set the size to a multiple of 16px or the icons could end up looking fuzzy:

      + +

      + +

      Add a matching class to your HTML:

      + +

      <div class="my-icon">You're a Star!</div>

      + +

      Here's the result: You're a Star!

      + +

      Examples

      + +

      Turn every icon a Salmon color:

      + +

      + +

      Or turn the stars Gold:

      + +

      + +

      Use icons for bulleted lists:

      + +
        +
      • One
      • +
      • Two
      • +
      • Three
      • +
      • Four
      • +
      + +

      + +

      + +

      Use icons to style blockquotes:

      + +
      Sometimes I've believed as many as six impossible things before breakfast. —Lewis Carroll
      +
      `Twas brillig, and the slithy toves Did gyre and gimble in the wabe: All mimsy were the borogoves, And the mome raths outgrabe. "Beware the Jabberwock, my son! The jaws that bite, the claws that catch! Beware the Jubjub bird, and shun The frumious Bandersnatch!"
      + +

      + +

      + +

      Use icons to style buttons:

      + + View + Listen + +

      + +

      /

      + +

      CSS Preprocessors

      + +

      Preprocessing extensions such as Sass (SCSS Syntax) or LESS can make it easier to manage CSS for a lot of things at once using things like variables and mixins.

      + +

      This example will seup the basic genericon rules and sets a color you can use for all icons using Sass:

      + +

      + +

      Here is a similar example for LESS:

      + +

      + +

      Fallback images for IE7 and below

      + +

      Genericons does not come with fallback icons by default -- therefore you have to create them yourself. If you are using HTML similar to this example: + +

      <span class="genericon genericon-warning"></span>

      + +

      You can use the asterisk hack to serve a different icon to IE7 once you have saved the fallback icons to your project:

      + + + +
      + + + diff --git a/wp-content/themes/twentyfourteen/genericons/font/genericons-regular-webfont.eot b/wp-content/themes/twentyfourteen/genericons/font/genericons-regular-webfont.eot new file mode 100644 index 0000000..4657469 Binary files /dev/null and b/wp-content/themes/twentyfourteen/genericons/font/genericons-regular-webfont.eot differ diff --git a/wp-content/themes/twentyfourteen/genericons/font/genericons-regular-webfont.svg b/wp-content/themes/twentyfourteen/genericons/font/genericons-regular-webfont.svg new file mode 100644 index 0000000..ef236c1 --- /dev/null +++ b/wp-content/themes/twentyfourteen/genericons/font/genericons-regular-webfont.svg @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/wp-content/themes/twentyfourteen/genericons/font/genericons-regular-webfont.ttf b/wp-content/themes/twentyfourteen/genericons/font/genericons-regular-webfont.ttf new file mode 100644 index 0000000..b6f125e Binary files /dev/null and b/wp-content/themes/twentyfourteen/genericons/font/genericons-regular-webfont.ttf differ diff --git a/wp-content/themes/twentyfourteen/genericons/font/genericons-regular-webfont.woff b/wp-content/themes/twentyfourteen/genericons/font/genericons-regular-webfont.woff new file mode 100644 index 0000000..da8be38 Binary files /dev/null and b/wp-content/themes/twentyfourteen/genericons/font/genericons-regular-webfont.woff differ diff --git a/wp-content/themes/twentyfourteen/genericons/genericons.css b/wp-content/themes/twentyfourteen/genericons/genericons.css new file mode 100644 index 0000000..b10b86f --- /dev/null +++ b/wp-content/themes/twentyfourteen/genericons/genericons.css @@ -0,0 +1,197 @@ +/** + + Genericons Helper CSS + +*/ + + +/** + * The font was graciously generated by Font Squirrel (http://www.fontsquirrel.com). We love those guys. + */ + +@font-face { + font-family: 'Genericons'; + src: url('font/genericons-regular-webfont.eot'); +} + +@font-face { + font-family: 'Genericons'; + src: url(data:application/font-woff;charset=utf-8;base64,d09GRgABAAAAAENIABEAAAAAatQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAABgAAAABwAAAAcaii0EkdERUYAAAGcAAAAHQAAACAArQAET1MvMgAAAbwAAABCAAAAYJdbaIVjbWFwAAACAAAAAJgAAAGyqWnWY2N2dCAAAAKYAAAADgAAAA4BYgHJZnBnbQAAAqgAAAGxAAACZVO0L6dnYXNwAAAEXAAAAAgAAAAIAAAAEGdseWYAAARkAAA5fgAAWkD4H3YjaGVhZAAAPeQAAAArAAAANgUfUT9oaGVhAAA+EAAAABwAAAAkEAMH3WhtdHgAAD4sAAAAiAAAAQpVkUB7bG9jYQAAPrQAAAECAAABAoDMauhtYXhwAAA/uAAAACAAAAAgAagCQm5hbWUAAD/YAAABYgAAAthC114IcG9zdAAAQTwAAAHUAAAFCuMEJONwcmVwAABDEAAAAC4AAAAusPIrFHdlYmYAAENAAAAABgAAAAbRQFLPAAAAAQAAAADMPaLPAAAAAM71j4QAAAAAzvWBvnjaY2BkYGDgA2IJBhBgYmAEwnogZgHzGAAJvwCyAAAAeNpjYGb/zDiBgZWBhdWY5QwDA8NMCM10hsEIzAdKYQeh3uF+DA6qf74ys6X9S2Ng4GBg0AAKMyIpUWBgBACOigvWAAB42mNgYGBmgGAZBkYGEFgD5DGC+SwME4C0AhCyMDCo/vnI+Ynzk+Qn1c8cXzi/SH7R/GL5xfNL5JfMLyVfmf//B6tg+MTwSeCTwmeGLwxfBL4ofDH44vAl4EvCl4KvDP//32LnZ+Hj4+PgY+LV4DHk0eZR5ZHnkeQR5uHlYeeugdqOFzCyMcCVMTIBCSZ0BQzDHgAA5FwqMwAAAQkARQBBAGYAfwC3AAB42l1Ru05bQRDdDQ8DgcTYIDnaFLOZkMZ7oQUJxNWNYmQ7heUIaTdykYtxAR9AgUQN2q8ZoKGkSJsGIRdIfEI+IRIza4iiNDs7s3POmTNLypGqd+lrz1PnJJDC3QbNNv1OSLWzAPek6+uNjLSDB1psZvTKdfv+Cwab0ZQ7agDlPW8pDxlNO4FatKf+0fwKhvv8H/M7GLQ00/TUOgnpIQTmm3FLg+8ZzbrLD/qC1eFiMDCkmKbiLj+mUv63NOdqy7C1kdG8gzMR+ck0QFNrbQSa/tQh1fNxFEuQy6axNpiYsv4kE8GFyXRVU7XM+NrBXbKz6GCDKs2BB9jDVnkMHg4PJhTStyTKLA0R9mKrxAgRkxwKOeXcyf6kQPlIEsa8SUo744a1BsaR18CgNk+z/zybTW1vHcL4WRzBd78ZSzr4yIbaGBFiO2IpgAlEQkZV+YYaz70sBuRS+89AlIDl8Y9/nQi07thEPJe1dQ4xVgh6ftvc8suKu1a5zotCd2+qaqjSKc37Xs6+xwOeHgvDQWPBm8/7/kqB+jwsrjRoDgRDejd6/6K16oirvBc+sifTv7FaAAAAAAEAAf//AA942q18C3xU1bnvWnvveSaZmT3PZJKZzHtCJpkJ88hkIIQhCAECCAQCCCooggTkjS9q3Vqpioo9tqJVK2hbsdpj90xA2mJrjtVaW0fLFbmt1h6xp1ptPcfe9rSKmc39vrVnQhBsz/39bmBm7732npm1vvU9/t9jLaIh8Ef/yj1DeKIlBlJLzIRMFP1i2Mbb/DXUZeNdIv2r0vPEE166+An4u/MJ7pnyBZeS0+R0+XVymi6HE+X4aaoQSsb9TSREyxEOvlQjwXfrSA18s424yJVEJgmZlmQhIVtSsqYki0lZn5DtKdlQkh1JuTYh15WoXJ+QhRNFoq9NJpOyrlTUCcbYcF7HG/C9xhCTdZaCncZkV6lgsiaTRbsL79sthlihgcZIx0Sa8TvO9+KgO2Xo7GnCSWVJIGWJk07DNUckiY57KZUj4Sjc1cE/GION9BLZmJDNJdkGHYR+2mEwJ6DHcp2lIEJ/dKWCg8YKYp1oHRYMRj7kypGCzQxXVKsjcNUxkVisIZ9gtXCCL0TszmRnOhKg5BW6mj5KV7/yirJfuUTZT5P7ju/bd5xPjG985RXuIWzdhyQWiEQlnaSVGHVdxE+uZ7SFvvkSciMQMyHzpWEj79DH5JqSrIfeBlhva0tyraVQD731lGSPpWCFM22pEIR+11LRWtAbczm5XpS5nOyBUfAOM/RbtoqyBsbS6IOxaKm1FtscYoHT5GBMNuAYv00jIoVtdpJKkkyaBAPEle70OR12rS8iAYHZ/0+ArHmq+8EPqVY59cMfKJ9IR6nx6FHlb0epxCPNTxNpVBJ8B1aV34a7Y0/uPnp09y3PPIPj5oh+PF9Nx3EX9LWpFDKWIYm8BYxVl6SyJSGTE7KQBErIvKWgp4wU2qRcY4GxxoBYOGsEB+AXaeWVghfQVoHuKHCEA0fwUn1XiHprVALRwSYtzgEHFyJcCvABDTAV3sNTCfimjqQJlU2sK9AvTWnYoCEwKcYS8pKhVDAD5Y1EtALFCxoDHPkccnCFdjpRI8bh207SnpN3bz1Ntt6tkfafPLn/C8+3lP8gcfe3PM94FH5JS4iROMhKImsTspgCZpStSeSJGkaZWiCIk/WCUUP9/aKRR8kxakGmgEI1QBRTSTZZZAdyUNFhwrsOEeTKpcoVEMdOgmKyM+M/cwryIynHjw/t46onQDSQr+PKcUr2DY07JRzSjNGlgaTIPoKiDnMSS8he4NA065++VNQT/GG9AN3SWwpu6Fa8VIy7sTE+ERrjlkIdNDpKxToHNtZBF2WHpRCFRn+pGPVjYzQE/c4Add164GtjfS5XqIsD/9a4PDHg30LUAc3e1hzwdawGJVYMTWQySsV0Z9ahdYgonxkxHc14KVwAH+MdmBY412XwTiSAT7kcMENkaDC/5cCW/OAQ42aCfD3WxI1QafX+8H25JYq0YMuWBVRakrsvvH+1IgFjcxqKh91K5RHKHlHUR0DWgbvIiA5pZiVB0kZkf0K2pXCKgMFrU0wThRJy/QmQ6EIY5qkgWICNGmAkDcBGKX+S9Tjop2IwEKFZPw5KbYsB2x5YJZBVBw6sUvJKXlp1gEfN8vivsEVS8sjR7Ca8K3k6ckBZJf3qcSqdaSGEp1U50EAPfWRmRctT7Kj+BOoks6XghKlpKhUCMB9mmI9ho9VWj1rEKRYafDgHFGTgsNZgdjibKrMAHabhznQ06+VRElw9NB2BC+qwm6gOf5TJZaa/f4V7gscyOXNR34UX9q1Ydnl8YBJPkNE+hVd///H+FY1TZsyNzr+z86K+o7882rdi+Qc3L33srslo/uCV1oNGIevIBiJfkZAvKcmtqEGofCXjxs6S3GkpNFKU2MJ66H0n9LPYP29BDvRko/i0xuLovmDJZUzVX3IFcJTlMrjRKuZrjDYPaWlL52cPXooD1VgPBULhjiQbnJi2klAqKRCrw0I02kgm3ZlJR3sEfOMi0Tg1cbpIVKuL82aqdWkddi/v0upMNE6jcSHaSk3U6fIKLq+uM2tHNRENkUepje765TG6i1ofVa5TfhEK0BnzrpMGs+u1Rr3ZJtSlui/PXr1nz9XZy3oSRuOkjvXZQem6uZnapqnLlvo4gyfQ6RFqGwyimzd43IE6ytdZm0OdUxbFaSCk/EK5TiC/pF+AL39U+U9l9zGlUP7jOl1zg/D8wpsnG5pnDT217ZGt5pZZl06knGCdGPZznD88UdRy3D03bN+/7amhWT594qI6E+3KCnXBxnpOV+O2wtiau/y83t3Q3OAEXZS8Vqj3addxTrRxOnxjc2MmjYzzJ5E+soDsIMU6QmJypITao7kkd6nztZDZNwuIhaVwIcxXbxLV6yKYsgtBHvJ1mto6wdnUHGppz0yexearPgLtRgOxtfZMzfcumIvT1Cwe0tMmz2Q877IW/YkLcmjj6ilMmA/mywJqHkw3b7e6Okk2Eq2l0awzlOWiWkKd/mSW47XE5rT1CNlIKBjQUi/n6hRcXNTE2bwUPmPNhr6FM0UfgpftW99SPlR2K2vg9WFox8Yb6Hffs+SVd5Wtf/c9R/+6567h55Q/U/FXdNbho/7v/Va57W9rf649MO+O9RO+qBz5gU+iC5yeqPYJOvd695f7nv77YtOkFZ6HXq5X/sQnz/3+b8HvcrMPKq9eW6Kd8zqkwWT9V5yz4tT9tyXK0U8fGFlA2+gtc5RjmvWPKY9xk3w9vaEv3mMpb/GkFtf6tY3UM5y7dEh5tPF+5ef3baSLR+JMfiTaBjjkN6DNYdgpXxY41JlKwmEKsGicZtJZp+BC/k4lXZ1ZrQ5fyLImXgj6pI4WSn52zTOhqDeRvPHxBUvnLkvuoXveMf7q/gMbpfWt11y1dvYm2rPz6XeUX39LeZUe03yDu3uzrs7981s0MT756CVXLH7iFzXR9vv/9w731Fv66to3L9D59Nd//MEv7l+KfSOAkXQSiZILCKpIUJYBMG9JWUzIvpTsLMlulXVaAHeeQDAKMNRgAVwpuwBLpQoTgHlcgOZkd47BhPHaVPTb/FNQv7qykWDAxHloEMFDICLtG9KQoX37hpR3qalWeTfW+5h2/vpL7lnWpijltqF9iBHw9qfwzr1IhZHa7iz9P8bsJTsv+JMyWs4hwAOLTyTNe9D3BjKf6VMHs+K2ZJFQNG7EBRYPUIVetexupv+5JHZdTBZd9fiMy2GIFesZNq4nYAsbKzY8JaZ7uFTS2Ux54FAP5+fRmHPSb9Nrn7wqO+R26/5tborONikvKCP8SzRBufl7NuW1PK+8m59helU5NnqEn01A21fpawbcsRiQx1qyl8h1CXlpSW5OFMJpwGSNpcKEOKD4RSqh142T0W6Q0QuT8ppSsXsN9rG7H4a0xlJYBe0guFcC7btRcA0ouDbnkuUXM6FtXorCTPUGYrcFsn0rL161BmW1UTzkjM3qR0UsL7IWWjpQaq0WaydIrROkVgtSG0GppVpbKtk5lXY6tTqtjtp40LadLqfa5qVqYw+XSaOuNSDjulCSBYpsHYnytNMKWho4WCft/YjOpRvp3I/27v1IOaR8TTn0UfpUSblx5u50eGMw4LCZ7G0TaUS+YYndbLfvvjCyIRi02KjZEptIgwvrATnU2zmbxqKt1eh5fv4k4ybl/QdfVR6iF27ZsedmgfuY3nrkjcs1U/g5n/kVOOO4Pym71gieh6hJw/G0OcBruNH7OJEu03EBHzVio63ByUHrw7T2wtxKf3x5JiB4jY019SanaDfmBukVm58/9XV/XKvhDpb3DtHtb7463NJ66wOqfzE2tzPIcnIFeYjISxNyS0qeXUK+AxA5HRyNlGwvFafbcfqme2H6GoAX16pzjJ4bOmpg8WV3Ug6Btk4WAyF8NNAF3LgO5lcHHscwb5q5AmctIOaNmhrvhFhv/+LB1WyuZ8NcF0lsJqjgAm+Cc128C+3udPEItfiDockrL2Pm1Cbi5KCZpK6ANhjgM6qkeqhfDIp+hwrrUWrBzIJ51cP9LDtNZf0BLd9DXWBPNS6cVZBgW6TTBd/k1AJrSDeUvB6fu9lrnW07cp8q2uCknGaqDyCtotFcfDcfNIdsHlHUx+ceumjgwK3lR278/YzcG9LiObbBULfHo9PR8qElt01z3L3ruh85HdKuG16i79Lf38hPyfm7wx4qaKehRlD9H/zqUfiVJufdT23g3LVNYqO93mFMz5x815GtRzr2Xnbqm0vWU9pQN7lhYmBigyds0V8hdD7ya0H4/TcPjAjCL4mKycCAap8Br94CunkWQ9owB3wCcEwVaasT5IEJ8pYYUtYBtinUmYDCHrEghhCWhepF6yGLua09rqIyu3MyBQAZp6A6bKA3gMLpbA9NJREjw3mcA2Wo0WX8XmrhAKVdsZBbvJauGRhYZ6NzlKcBls2usQ9OnTTXT2fn1t2+KNSbSvh9jhrlCIU/rTj7sstm969aferb/L+P+rkJnY3JmZNWzyj/J9e15bsbsjW2xsZgk3iX+23lPeU/Lz6LT5sAe2bJDUwDARL2x0DtdDBn0Oc7IcqdKdkG/pdFdsP4u9j4wQO2MCfYy/wG2a9yawwcEkuhTVVDOSCL18NMgOwXC/UuIE7AKmdyckwsdHQiiXxwu9CSUV3h8SYC0PbnkosRixkOoNWYyUCbQMnCaXT6ALegd/oiC9WBF/x1qtdbZqR2U/3B25MLuwIW5ePxRmSfcO2kCy+c1D1v/qdH+IbR9+jRdltL17CyjL74vafr2yINW4AZngRAtQCw1DTyXVJ0In4yJ+QJJaSQFgjSywiSKckZS6EJRg52MmAptDOXuTAdDp3uH/bUfDSHOGJGk9wAVBwp2OkncmRk2GqP2GJFePft8e0JakFMc+SQ1d7gjsTxj447l/NuWmjKgCC7clNQkANiUevswLN2a8E8AanZMQF9NNLco0o2mCoEyk6rw84J4L9EOVDQ0UjWpmIKJ3MGtKi+rSzqYOIcdhBeHaLlaIR7su/eYzT2lEwTL+94QvnZi5d/LzDbErj4Xp3n0Za71g4sC08xua67YucPLlc++PiOD7+xbMCq01kMuqDzxi8Jf7rqN688fOl1Lymf3vk35eqTF+eV3+Z2fbXz4C5OXnjNHUc3LErd81zu8q98n058+gQ1XX7wzWu/usbhrp/SUm8xpKgaXhvDsINkNymakO4AO2Yn5C60kcwmLmWkD5fksKWQAkrPLclzLYWZcDa5JE9W3V/wPZYBI85NAW1iiYHFqC9nikdMGltz1zTLArycbC04pyIBnSb0QhYDTWeDF2IwEps7PCE1eeqCz3geiGSDgWhnFoCpSj4mu+BrOV3OTmDSbGckClRmWAHJDNTPomEErgVVC/ABpsJ1tuOh+gZfvXuOZ1bT3gWPlvdc8tjf9971f75zfW5ondUjcBZeozFd0CeNbH3p5IJ9lyy63FYz0ds3fdF2i96w1VavBbT61Fl+hnIJvP7z0dYd66g703+ETv3ZtuPfvGzeTY8NL9/zWqveZDPkDTanOP/61cVbF7751Nf+fu/OBfGHr27tXXr/1thCm00JD6zecy0dZX70AW6VbpXmAChGM2khTBeyOIlJDRZRNUJjKRiA4nXV4JDV4vR1WiI+oXI88Fe67K9/VR7n7qycCN9VHv9r5ZwdK7iY6G4EF8ZPMgRjnPUl2ZqQTSwOh9E28D7ADZa1GFsrEo0FZcBkHa5r8vhUxncBdzdSaypJic0aDvFwCUyNxi3CowxopcXX2Vcu/MrGb5TpJrq61qL8Sbnjlhn52yz6LVu7Znfb0xOPLZdv1Fy+cbFysvwX5ST93/QnlKcr9LXKgOf+lbJMzRfSWTRh09+/lTD6VGOKZvDjrYRimJMgWsNgKzlXuYUNVDq5XyAYjxqFd45FfdD1xhYF35vRSUd60F8RSdsCejoAnpxsSMC3UjmYkJtOYLTSmSyEkCQWjH/VoZlJiXZmgsGsd2ZFGHUUeVFEoBpEiAYg7Vc/dbtvufTiGzatWHbtl2+f290mivQJZfC02N4xe84G4dHyHdf1Ttvma3bau6h7WaihPf4AfZk20BfuWH7xlHzwLNsRJDEymdzMbAdYCW9CjpbkhoQcSMkJFp4SSrJgAaGlcneFKAhhfcAoquCCp4ADabRgpExOMddzCkhs2AcjCuTkCeKw19PGvMpGjM2QQkMUZLnRF27BtoRYCE04nwEB9z7FAjZ+EEEwcOBP+UMVTgyrxgWckEiMgkieZUWk/oyGZPqVjyzKcWWZctyifERZFPGk8hzX3J+RMv3s7SxDMoSNPOntwXhd2/Ge3mbluZP4oerT/RlQZ4AtKGALhdiJCzzzZqBeFOgXB9+cyglGHowfWjAYL3sZ9GuB9zFz0gF0aXDA6J31Tcjsckg8pNUgnnOhHRgOhFvbEP6xSFyWdiZdFOmho8gGNDKVRm1UDPOusMi7snAe1YiarIG6MpR4uB+LLSL3Y4+n3CvarbZyr+eWb387w2mUd957j3oPvv/BB72c5j3lHep9r/wpvffbvJO+1lxPX6upUdrrm5V2n1Npq6mhx50PbdqkPK48TtPP0q4HnqWp8rMPPfRQOUCXPfgs1/TsA3RZ+dlNvzmLhzJkKXmGPMZ4yF6SexLy90rynETV9fnRONcHsYUaWLoHzq4pydeoxAKm+TGGmNqAaZbm5HvEQ88sX9d7AOlyjbVYJ1yNWqJBzNtqNXZvoCPZ3TNn3qVbbv/6Y/9aHGaAuccOtjUyaTIC5jnguD5N9RZv97zvY7xTswjudSRTuc/xjIRUMoSgOM5FUfJAxwjRSCgasWY7Q1lA1wLHJFLIwjSgYz+V70RD4oqwpwSdltPg/U40G3E0wFoA1U5mR1B44RJvZ+PgUEbQvOCVDo033AS74vJyzGTjBWWP4ldgMIFMwbhXJMSU3nl8rp436bVv/Ynetnby0n0vbd8hRztnb9usPH3wceWDvjl1S5fR9iLn/6Vy8Gf3iY994Vrq2zV31r3lr93Dm+hl1PrQN6n3slDgSuU3+7+hvH7VVWuoqH/gqk3/PnmKs3/mmxcusTtSyZUrF0TSejGVXjwwOVerjTW3JOKz6jiTweGcMbfPFo9Y+2KxFf45Wm5wd+8FV3jqw+9s3taVjQQ/uOlL3+e1Swfv2HbtwIqfUIdxw+K1yl+v2jHlc1y6t5Tb3vz7y7fdvPPYQ0P2jueuu0956tpdWzyNv93/EL3q6w/+L6/W8rZy74dfOz27z5xzfE2598R+GMU26c5duegX79Xqdm7eoPz6+mue9/oHLl7xzpx59u6eSy9bvLjeNdHVN2FZ3yyNtjs7EJ5qcWhoV4z3zvF4/UIsMHdRNKs3NDRfcMW0DQmr5ao752xYF4tt33nddXe6bG/cvnf79tZgU4A6fsJteLZnnn1yz/oNpOoj6gnw/nxyJbmR3EFvIrImIa8tyVJCvjUl31SSdyeL0k3o8kl7DLHiTRKe3vQlcBRvssjXIyoHxBlNyJtSciuIyJ0JOXFCXl8avnB9Qh+TSQkDHxeW5PWWQp6l+2SXRc6W5GwCTgo7oMlXGr7ct0PNcfkshT3QdHNSvr0k35Is3r4Hf+32W+GH99yOp3skcDvvUrGsYevIsIplZ1nkmSOFQe4TednID4UdIy1qc59FnjFSWMp/Ii8fKcyaqYeG4Zl9M2yxQt8MPXxouG/WTFtMHrQMLx1cBq2Dy/TyUsvwsqXLbTFyZMbMvlmDS5ctj1f+6DktDArn14NIZjSbUKxdYnHl2utRcH07QDeK7ihahsKeGtAFE0C0pbXQSgDRoTa4SSw6XUzKo9dDszuxfoeKGuxeQGs94P/GhQSNc2mQPowqxwX0dH0gYBhKBqNqN6G3zLlMvM7EZ9M9fLYHmsEHdoDdAQ+44tMBGNSZABXGeZphTrQDHWopf90LX9j5i39Zl6zzeTpD/iU2m6ve5gq3dfvqLc3eeL39nvuURuXjb8ye55u+8ouzbV16quUESo2NJtuUOXfuSiVnt1hfDcSmheqDA7Paa4O2VM+0UHPt0986+rurU00r4l2XX5B0TbampzRNWjO9w8EfZYAKnGP6y95rLu1KDm6VprfMmNKebfb0mm2xjoTT6Yn09ixPxuZPhQvLkpvyBxd3bbikr1XDiYJZZ6ox69xtcVuDoHPGfJ7++X2WxMKOVrOhRtTxfCiebU2mvFvvOiAc2pQPtuZWbt+R3jrZ5rHmLtq6qXzqjF+uYvovg87vAr6/CP3qvgTLrq5A9V5IA3cBgzYni+ksslw6AbyetSAUAJtQWAnKPU1hzi9cMohznhULgb4cWjorThTv5ZupVwMk16CWFE1qyB/OvBygIL/YAfoT9GtcGw12MBBkovgRXZy/qaZv+syDBwuP3L9rpbuhtuWqi6/ItsQ2br5285VLp4lWytWIvpap4fSmxTNsVv8F07sstGvaK7vWu7jg1EUrVg7k7bbeX+/NtTQ28GJjvcFwUueaNEH45iM/XTl/22QfZ2pqMBo0tllLvvLo725YfvtA1qapq9NplT/ytYFAe7SlzsY1eGvraH0gZgq188Xyu3W+lfO/PffmFXPa/WY95Sw3JKe1r1owb1JbTe1LBt/6TYg37wI6bgc6+sm14JUi3mopFRtakHANDiDchoR8eUlekmApwSXVlCCVr0vI3hPyCnBRS8WAl0WU1oGUewN46iXwyRWWQpyB+GK8jmUNe0D0rwfqB7wgTr5cIb4CjKPgaGjRz9uJAlUnymYQspYGuA1Sd/kGkCpzPMDuLRGH67ykE0/1iNiZV0oxnl1xTHVOHXOPoiA6oQh4SFlw/NH4MfSKmZ3I+H9wH6PhzuoTldvBAE6pw67ewH/wzRXkW71/15dO7r7rmhn9T9Kud3bbUvRLJ2/ZtfHCuU8qP3tntzid3tmZXnrNkX1bN3dPDgSnTFoyb9PyxqDfLwKoXLm6LebzOhoSmUCgoX5SbtHg5js2bsjlsumVl37x4ik5v79n2vr57QlXo9PR5IulgyHNfbtPfqm/dvc7ys+eXLVkaDNcTTJ9+R3a9eTgwI7yX/rnz01MjccXL1m3bEpPJNrYUG/XG6xml90TD4R8vp4OmzMUXJlMtLc3uFuic2avXnvBtJYWN4CyZm8yP6HN6fQF0hNdbr+f+QcgY1rMcSbJCiK3If4uRttYGrcOpzyVkHUnZLFUFHXYKLZiLYjYwN697D0IHKATWaEIBrvTWIihg9l0wLRGEVARllQE7QgThMoOE4laM0Wwbdfqxt5iNOlk2Bu8YSqNTNy0Ok91tW6rf/lMi15PD2T6OyJO+N+fySMeVvLTdvRd1ErB97nkkY9v14jt/qbFDyxaciAc6c9M6K3zR9kbPDrU39LRwsIBJbpXl9JtJxPJJDKbLCJryEayg9xAryaYe5xaki9LyMtLxeWXwWjI8kHg55Usgr4hJc8rFdPrrsG6mK6E/IUUxmEBYTsS8paSvEP1qr6YkNtPyN2l4WR3+5gVTZbkbkuhH2RiQUleYCmshbOhkjxkYcGdSEnehtbYVhq+LjJdj8Gwwo2VoM9P/rJLtYg6i6wfKbiFT+SGkR/++eC/PYLNBXeDHsNB9SOFWrhTN0Ke1ulr6+ob3FXL95lrZve620VrIZEGDdgvDvOaLiZbC6zF1oGlqBbXisNT+5azUP6QdXjCiktYAnW6mDdYHE3eq7Zs3/kFbIhYC6FrMOKxaDlMb3dOnicejrQnQpOnq8m7w+A4kZ3X4QUvFjVNffjdDmtB2wh2c8cW6ILNynyuSnLKBrLq0qBkO5kRjIZ5p0uNMamsgUAZhDdOs3Z4HMMgTrsTYTOGkjFH4GQhKbs2YE+D18KEGy6ZEIfSnexOtegHv5qFUkpXD6zpPvL7lRqr1UFz9QMdc9avn9O3VqOcmvfb73WvG9jZFTe9oDylbFP+9QVLW2ZtS2KJp23CpIVP0OB3n6TBJ55Q3nryu8pb26bFE9N6V3pbzV13/0uXudVrHvzB0UH6L9MugVba0Z5vb8/TgY5YbkK78JWBqwdWG+hLzppazawJE9d/bf3qvm7li7WrBq8eyK5oTE689d3du39/a7KzcXkm0dTfE8q9cuLpoaHDGzbC+ycre3tX9t4f85q7uszemHlw8H3Wwl+PP9Fe/vGUec0dLZMI1qVwGIOWiAd8wzuI3JiQ21KytlTUNqKYaikwdgtj3tpS1XE8U6pTX5Lr1cismKyk7QJqhUer6kLqeZj1RlasVJNjir1Q247soG0EC9sQCrPpFp82mC31zT4/skGtVTbm1PIwtbajh/qcLocummGlHDyLcYUzriy7PYX6WfUS+Lu6xAUJzYvU+aLmG+vhlNKX7tr7Er9w/TfwQveS8h8/4xcee8WfSPjpe7f96NnbNrR3rAzE4wGlec9zP73tf3XEj+O9Xx2746c/qdbr6DCvHSJTmL/oLMkeFm1ATzHMKCGWZFEtPACvMALjbRQxOF+LI/Q4mRVTS1Uq4QKsKOOI3UWzzmTWRTuRt3QGGgnoME0hgHtfLSSjJEKHhPDesIYOhed0ZsLKG8qb4Y0hLPZgeUvpGJab0dX01qGIsk/I5wU6FBmaA8/RSDiMGAhzf8+C39vL6rDU6j5iM2htGZeBZh2UN2glehVnU+4u/5kz063lD4WH6Ta67eHyR5Sz043lPyt3062cWfka/ygNKt9XXuYP0OXKy8qRcnb7OppSSuu2Kz/hfkxnKW8pB/kXaFo5qPwG7QTWwmCtk5U4yLgCGFuiEqSi4rklL5Xxw8iwxgXLk6oDHdqHNSz70P5wwKlarPMLsnyroYR1VMCOHHx7bQLrjUjBgHVaOrU4xQVYmAdQjaZLgi8pS5KU50dOA9ODZwRoUSpX6ge12F+B1JJ6ghWOWBkDU25EZi+YWKcN1C/SM+WAGEIrY+3KEFgNHi4VuBQyeNU/Vm/D+KeZhMnFjIMcjIOQfSIs0KCyDwaogiU5OBZeAPkp+ICRhusEuwf9i4agaD1c69A0hcIsguBxwDVmV/3hasHiWYECK3gNYELTcS5gophLxlKczT+iGvDnNT/avPlHyqfKO8qnPxJXPPj6B68/uEI90G9LtPUivvCZh+CMM5x5Cg7KQ/QNZYVyYgVRa8W0qD+A7MTLZkUoYe4ea0StCbkGa4sKts9MO6koWo6c3E/J/pNlwoopEWBgWPI04fepZZRn6FhDGkkbuapaQRnDqpJirBmVVCwKSqo+AVxQ0BiSrJRQl6RyOxNRtZaSA8qqcWMMAoZKxQmY5CQTPPDJkKWgZYSXDRbsKYa/4tVSukzKwV4irQb5QGb9oIeEdOdkqrJwIJIBFkYuAoABQ/iU9Gd4FogbZcG7iFtyRyLpCFhVCYQS/j6FZ/E+x566KB2JuBUCz7jH1WpVxtxJ7quOOZOQ0ykspjl3rNnzjvWcYXVV8ELDR19CYCCn4yY5NVJo03wit4+QYlt7CtHAMBzTYzhAqwFma4pEWya0MubLmEG+Erl/Sp2UfzLnpS4Pb9eBzo6CQbb9YyKBKaK8089zkrbd7W7SbXzq8+nF+VwcRzmNEcjWpIPnaYHUnkW3asQzQVIkSyaTqWQ6OVKlZFOiEOpIpVgx3kSgoi9RbEl3p6DFD6yRmYLYrS1R6MpDS3upkJsG1+cS/YJxRG8CmeV8cK+5VBACeLRguLzQ0gbn0VKhFQssohZmFNNdcJ4qFTon4ZEVWRS0eTifWiroe/E4NmEz/ikf2qCVh1f1+Hnnn0d56Tx/5yc7Kk+qas1zirDHtzP/mw7SQd31uusBVxDaw2WxAis5lWKlINVFAmaqtbt0UQrqabDW3tVB7/jd4fCGyOFI5DDXfDg8FDkcDh/+nbLzghV0sD29UL0fPhwZCh8un8Sn8JF34H6SjKv/tsGvzWcWx4VzzDStl2laNdbtVvVrM9abYmxbI5gsCDkMiE5IwYUlmaac3CQOU1JjUFVruodLejnw8iiLbcep1YLV0xaCzFxRiZvpf0mK+PXv73z9wfCZwmmhv6I1d37/64oo0f/avOJBjlf2Ysk02FlWOsef1Xc/WVvtvZdVzVXGEDh3DMHKGA7jGLy+84zCKw4TR00Dq5ezygYcVpG67Syy/I9GxWMd/j8e2a2c9M8Gp6iMcPb4JpDhceMLM10WTBa9TIt7W8A5bGaRgmY/qOXK2FvHjb0Fo4koTnIgWYyyGEQ0DJ9qieJpCxa3RMcoFEPpAwrJQk6OikVXE0vfua0FDdZO/P8j1ljE7Z8RrRoe+x+Q7qxYlgAS1KYn2uOkjtVYxpBLahKytYTJuWjFFrIFGUAvcMs9J8YlvMBLbMcclwd4pUbk670sgzNBPGQUrM0BptGjCC90JkeTH9c/YM2Ex4cDFymiCgCLCCqiPOCCZGcW0Cr4VDrO0ulzWrQ+axUQnbqC1tA2WrOGfqpor1D+Wzmu/PeaP9Jt81741fNz6U7lroff3vhCv1DJbu1nsEkg9NS67dvXKhpFs24bYMpTyl3zBwbm0R10+yOL5pc/VB8+yVVhFWDaKi0QzzYCLVIkzzxyoIBBpUA6gXUypNDcCi6GpUnMsenOG4nO7HJ7wpF2LO+VBWtRa7XlquME51LHBkZdZuqiUcr8TRqxZbFsAdUXkiEYsFEni76y8e77t2/fvW4LDEu586PbwhdpyEWj7Sf3t3UqbSY33sCB//k2ei0jyL5/u5QeN8FtddSX3h1fNB8/9yZ+rjyw/6RJaessH7k7juP/863KbUgTehk93tm2/yRR6w05ieHUGkIMHGbTDBS8B06ieWUE3mheUkbYmzLCk7Ov2TNErbdktQ416AvQsS+R+PzoCLzxeWl0hL2NjgDIPeuaPYMyLVUwXw1orHZyC8EqCUR5rmSyaGbCbDaBWIqOBjSkrNC8YAwzm8pkOg4uQbXm3AI8aivJtupqIcwa1LNEbSEBfGsBtFHkAkEMo7vsWMMzQV37YgDGbcPFMJwhx9zFcAVcahyIeMf/U7O0RDWczGwi0OzPUAQeZJRUrB5aOGxAJIJY7DRxoxlkWVWpTLiRcn78C9oFcxpxHbN3hHrB57kXcDAxgGtFeaqpwdbfHKFv0jeP0N+UDx8+JNyoPF1+n85VDnEuOodyrvL7aL9Uv0aCqTWSVpaf0QGVQMQ11fovdLaFEq6IKegxYEYxHm3gdLggBiuWJOQNaRTr7UF1CPCFoEUUcFHU8v8xPx+1iQFXwhgoWwpE0ZHhySm4AyOEMeJ6mnKeB3IoqL8FNtcj2hH4nJ7VqeFnhSzNoozgSwJHbWQUGQ01VvsqbmCVMg/f4ZMjvKTkR+EbMCmg3ivX4XvFR4Rvhm/1MVTGw4gNTNeDx2VE+eWJqEZyKVv0gz0m6kBxSRgu1ygzl64ssSGOszU6tsahF6tHCqbGFKsf0TN30YZpX7bogZ4o6G3AkipSNldX1bDCqka2BgIoPBYIEtkyAH+aC8EpAE03dfgtHAlRsuXAFvivVtacJuC+HztG99KFtOmnm06TXyjfUSKchT2CU6OW3hyjq18Bv4ls+qnyH8r3lG3HqEDfoEt/gWMgYHt1f9Q9xWhdX/FG7Uy7m6HjDQk0b5iLiGRpD3W6qBM9aFvKVu3q/G3LuI9zDz44ifv7sm0HP/kjd0NqOK38helbSl7eK7x+8fTpF38a2/uyhi2tGz1c1a38WG2JlURInFxKEHg0lIoNGLMnDU4wryDoTSU5jnHI1lJloaAZhbpo1uBD5loMBCcScssJuU0NAbW1YJi+IaBhtqapGUQ22qaWUfhR7zpd6AlGWcESVwnJsaVLWlZKlq36ihLt7KdTnrv5/WXhOUORHQ/sP3nl3KHw1of2nwQu/3m/8pPnbv7Dcko5NiGgY8l3j69ZHh6aG9l2cr+yZmhOeDs6lthI6TY2I6SyPoytdYpiVWIEC+2wUNtaKlrZUiYr5jhgCnxqBfpY9KuJrU1DBXZGbemZI88K0s1NoLY07gjaHrtYqG3G5CFYnAYW8NKLhRq2nqbWigqM5tSot2h3+s6sWGKxr1TFvawsaQKu5ghbjgfdB80jwQGvlE8QPvB5VPK4TIlTlyepLuXzSjdecQTvlCW2ZI/VEgFH3qNFeTERJ8w3Lj1D7ewaVwRhV7EUKOSC3YJDEmpzLBdWAUV2LYavquVXVKogoOULlPXK+gUHKwsHxxDPB68tUIbovgW0pPztKN5U7doqtGuat1E9oWJx0SC3SnqbjqB7IfikEY6sKiN/wqTqsb/qukLvuJWqmoqAj4WBcF3VmQWDevIxUV+0srL0zPs4/0EkIfAfqsE9ISkbS0UjW+ZmBHsma6BBNU6+khxKFut9rGy/CW5Zkyz8x9YI8rmCrx6OQXWNoDUb9YtRTOpaXWIkxFGxGSQ3k+aiolPAikmdi5JrN/yOk/4wa8GvDx5SfvM4L9le71sI5zT0ONwRyIPUteF3ZekPfT+4UlY+jCmnvCfojPJRDp/74TqZ2mJU1/y68sOjDyrvb/idmqvhgaYCrsF0VOmEQS0hUdCMLROkkoDqG4lAqnYIJwHp21KN5ejUaJhepQmWWOE3oJY2jH1RmNkgAQwQLrvE4NooOptobQa4vJ5o/h2+0cbQ680Ew0IupjyaWG6kOYlrHUHu/EkMP9eqS+W04wv9zpQqqTIXUIFtuFqkVCtaMeVeCCBaaPI2I48WeBfc0Zsd9erSg2GDyd6gJuCBwxCwd6Z7aNJL7SYaiFRxrKFyb4Du3KL8N/2qNDL41ae+OohvrVsf3rr1Yfpo9Q6f5/b3KM1gMcn6yiODgzSHD21VpLF7Z9klXKGcJEhTdYWykfEtx9Yp47pkdSlyQUMA7uiNcCZUlAPFhXb+RnpG0aMx5NlS1zL5yxkdz401KtLZGt6g4rbKOmnVk6hGRu5ns13L1mm5U3IOy/2wii6Qkqew7FU+Ibem5GklOcJW5iRY700p6Dqu5+1UNcf4gAgMZpgTm0IhVxJtiA8DIXBmwRhUoRujHZNLwwZzTy8+MFldK6oGPAqTu2DgWAOlclcF1zEuCzr8maC1Gj38zNE6DuHZxq8qPwvtAbbBSEaEQbdx/y8ah/suomxhMb4wFoIQ8FNQRYAGRx9jj9PIWYc32GF0XDBErS8FzIXx6kaSIGhVeLY4iGeGhTdgRpQ3ob1sYhoXjUgJK/3RvGN0sbIiiyW7wPtMVXKe0r4hne7o7i9fkji6bf9Jl6tSGcjtO77PE9x9dNUVu07u7+lVF6Gjjsc8hqBG/4GHopH0VLVcz26mJhoFRwKj4y/SOXPe7z8+h3rhOOdYv5KjByg5cBoRpQ/vHu9/f84c5Z3+1/rfn83NUnL8L0+TA8xBpYggNeia6VAn69g4eVVlVMp1q7qiast5Nd5bjfKqueXXNB9q/hVUtHtsbaohoSJBXHYqa9SkELosUabelO8spR8qtqV0Ka5KXzqo2BTbIF0K9sRGX9NK7LuA6bPUD5+KQuOHS5XvoH6iS5fyI+xZ/BjLK+S12H/0LtEjghbAlGB/yiMCAOE8O2PPoZ3K43OAvQ3sgxz4V3klzxMuXwYva0TJj9WU89BJsNciKcIFTDOgUYGRATxOXl2gTkFDQzc/5zmQeVTQ6lL2qp+gkdi2DVZWG43+ri6ByAP9ARa6YQj5U+gjR9RSX2RGC15oJC05a6+H80VJv4/UL1p8HSm2Wr8o+iei4AqJoj2UxjbAeo5wBtv0iWJ9Sxe2GQAkTshhW22i2NTGIql1paKnHSOpFJUHOVEU1L0i+FJRazSxM+b9Fe31TXhlKxWdbi87YzmRYkhVGcFSMYLR0yRmSABQFtNd3UkWQC12TuqBs8K0yn4SZ4Kenw2C/k+uOSL94z9OOnsDijL5f7tmLMGxXBPLC6EOnsryQiD5jVgXUN2zomlc+bJYjeGDFkX470Gbh1Ere+6cTFoggstXqgaCw3X9akoNa43VXTVUG0HUVBuuYNpHh3gyOj5vpfZPC7IcIV8i2JlACZExgqEIA0N6QDwqpGCl2MU6G1vgb0ZdFlXXkyN2kuuSiJSM6qYFLFeMdcahBAbyCj4jrivTaDm1ulgWwGQTIxN0meKlXIdYSo1+G2gGADLYAL8jmDl7yKExe6hu/wC+Jg5VGj/4SpoOvQK4f5qwPSKYaIyRYX/VDWLxDbXOBXNaDVg/ZgSPIIUOokNdx2ms5u60NZhrKWq0SAANNcSKWg3Lm2OBE4AXK9xvKFVXdfrtBD32CMFpwxH4K0c0Mspbb50mbylvsTlib4L0nvJIu/IXWtdOL6XrKAtFoE1Sj5X1AES1Mc0wW4tJMYKrV7zgtqWq1sb7WWsDM+Q/ARPBZiHkB1tbE0G85I0AePL5Q+ih8GKxkQVd/qEpwlL/gIYdKNBbq/2MVcK9OBRpiA5RhrsBFIG29/nG2yi1YBDe1PGcsZkBXA/sYwPxgngki16Gtr1sIF6E4z6LOkRco6AuTfD6YDAuwvpfoM5/2ntM6TJ7em7PWXcRvimqZf1sr1VOw/xnJXZjAI18NbNcGuaR4HYemAUt1rLitloLkt42tsXI+OScheHaosWMD1rAg0a3i+XdipzRipEvtuYC49UCNurVRtwKRhZVAdHhJGRFA9o6DEVjtyT0cDAIFFEugpPyG5yKfShj/ze5MJ4/Vn6D8dFYHlcgRtJVHYdRtcM1n+l2JRKFPZQ56JVRI46JKmXOgOhHg0PBcPEqHZHB4Uri1LUm3JiMiaAFZxIMIjhwmTRKlzY1TguCX6BlmsRWGjZqcVeWehYNMTKoVDCCp1VwNuTOKEMxGNDiogsH6IZORISRdBYjflhfwKrB8qPq0gsebMfoKxlVGX6KGkJCLZC9J8vWIZEDulW6VeAXd+K8Rlh5VqcahckCxDkhd5TkDgurJnWzzGqhg8e0vEZfO6EddVpaHLaE6tjSEbf1sOiob2oOshhIEgTtsIf4Qy24x4ncKQ5TTT1uqCGbrYe1xjqLzaX6KVmsZIpmNZ1ZPusCzJZ18U5X2IV1TjqXLqzV2XRYzhjVRW2RqKaq9w/8Qa//wyQ6MdHtuOOl6ZbpL93p7ErRiZNYs/Jq21QnNBv001+6w9GVUl7lIgyFcX+sNnYnzvcwfony6qTKd0M7Z6yAN/6s/ZfYbKo7MLnHrNnYnkYudQcmnDkXOG2HcQcm0c6o4jYj9bQ6YnWcswsT27EoS7U22skWEJ6zG1OSth2/9QvlGbPK3NFZyvHjt52zL1PyuHJ8Fnf0izRVnrHrtuO07Zx+byIon+D9mJn3Y8QobKW+pIJHm5jmr2Wrprlk0cjKHI2o6o0WNAg65vagodYn2Rh16MKZbKCQRLFgBqll7ipu08SwLC41dWDyyFLBxdUCNNQvAsjvKGK/is0+zA5azLOi/yKQU79gJqu/arjOyDBivCZS9dnVgJWgUv6Mz872E2ABY9XJQcj4qRqPIWNxAO/ZsYPq15XGBRFQflSPX40zs32OJLZfGa5P01U+VMGo+AmbGsmFIai/qwLWcZ/lznyWRw0w9lnKdoFSd9ZSt3Eqs2+o7PNExu/zRKr7PPGUbVSir2KuaZW9Sf7/oS46DnWdKWQaZeuEkTAV+IHICm+cUmGXhpzKjIu9Vvqo4q4bSLEJ+/j/iLx045DX58CuELDkIU6jFZqZ1J0XcdmqiMtf+Xd+xFXdb0tSR3n6rJFzn4VcZdx4ipkBtbDr1HjUdbYsukgH0yF2dY+PsRmqT7C949REkA7tvFkEg5T7nD3b+JQYPHfftiLrV2xk5LMqgn+PdWYU+nlWf8xj/bGx/piYZR/fH5Or2p/a8/VnfHj+3P58+0zs5rM9EpLj4zfj+4R5zytZnxIlrLv2sB2R1OwnatumUrGJ7UHSZMNUugWjkyj+uIFAE+CGw7yxtq6NmamI+LRBMLfGO1JqbVmqOpbW847Fxcriseod/3loCivlqxX0wYD1c8fJrfzymkiju74+c0Gj+2XROmMgHF685KuHas87dP74oT6L2Bhsagy0trdNywU8dkd7ZtKkhZunTTuTsmBreNWcBagrm8jyWgA5VKEDXvPRt1mC6O1znsWkEjwrsN0GcZdB+rbiUz/B8l7VfBnm5KzECTKJ1HawiJcJdD83tilbTRJj5hgXRtWQsvltNGhLRYM2dp6iPJzDbw/SxYMgBo4TMWmkcgmC8Ue41LCYdBmVAOKlsYtKXlHViaCy3Jir7bRGQmYajQgfW7Zwt3G3bbH8XHltyyOPbFFe09yhPD9UfpC7Yoh2/0kc+vrXh0Tmz5C19KTuAW0zKHgD7h9po1nepmbvMPlrW0s7Xj927HW66WNKPqb3vE4TyrHXfykQJbeX5mhu7+iyHoH0jD6+l75IX9yrvFipSQBtVcknNpAw2U5QNtwsWNhUwhC6L1XZqC6IMUJW82hEzizogkkgYBMjoAYVGior/GiTutGFP6lmhDGn35zAckhAB00YDMWwaDAMZzyXY1un0TBLYxrO4wenRBUL+3m2V4dWrdYcq2XK9Et0rNBCzXxKSiXxiVJXfaqfZir7iJw+z7g96B2q4/aoK8e9bJRw7VYLg21qvVGjG/dt1KGf5XZVh2LyIMQ38Ll/NpjM5w+CZUP/Yfel8/S7mZB/8HsYYYt+3i9upBb6/EXKvsP/8FdnKB/RF/AptjdeRe40oNu9LP6vHxfx1luBCryWVGoxUIR5MD1J4hCjcRrEXY9YGQOmawC37ZvzzJrTiDlYXQP+Q/yg4KaGyvF9c+YqUiWeWJUbN8uhYozdlMBELylwtZWlJoazfoee5yfO/tpxuSIeaJgmiIlslV1SixqhGgVgOsFXRT5+/E2NwPYnqSAgMclWt/ApdEDjPG7pwAr0grlUz8a+mZv7+zfPRI5Tz/o29qRyiB5OEQzvY5AaX+Wxs7G9ZHCvUZE4SD/zqNT6aFoqaulYUIIf22NSrSwQ1FwOAPOCoAPDoDfWmK02dT2GbMqNA4tZrMhvpv5ohDn80J3TmObhydYtZbJlK88qCFiUCcgGfw9vhT/+YUrO8vccIDPziGrRCzXOJMv56FXBEU7IzlJRcGJnBeysU/UYakrFGrZFVg0AHSZOLmdVnCpoDNwCH/HQ8ZhMXS+AzEvb6OO0jVU8Eqw15TD8TBm/SjDdMMWV4o9+PsOwCcbpOMLipXWsx0sYDjaxPVvVdQCc2mWgo4m5CyZ1m1bAXm7MjmtUsddYWTE6KThMIq5rkJ0iXMqeSjhbZAubdGyVUzCcQssZo5nKEbswtC83sPbobmVkBFO4I2oxCoLbfUNLZ0UfumT3UUwBS8waYn2Q2ucapHANm38OQ7cFXp9Sly2o2VGsAZ7i/NP7rAaYi8v6uMxZCgbtJ7iTJ6/9hB/meL2hUgGMFM6mbMGsXxfkUzpOapE+klroCDtwhEXRykSq1gVJDPtj5Kx3XDU4VavBAZnUlHBr10oG0QYkqqFqjZ5GLBhMqB9FARFIbS43lluMUp6r4grE+5iJUfIjFVTNPACJy4+UV1EfPYMNNGx/D+Q5DUNxDsD+VlLdv7gpUUm12ERXVufCGIorymQzG3VlRcqyttID7z7wAALWBx54l66FkcIRrh94AFrQMuMlrsiAw+i7WHOjEPUTgsSeY/VWGN8fOW98P4V1VyP4bzSv5gzgUs80JuBvbGW3Vewi5FndFsfqtli7nozm4S4V8pWaLg7zHaIAjfj6WCL/F8P1u2sAAHjaY2BkYGBgZjjy6Mpmh3h+m68M8hwMIHDua+N+ZJqDgQNCMYEoAHf+C1gAeNpjYGRg4GD4fwNEMjD8/w8kgSIogBUAY/wD9XjaNU+7FcJADJNNCvq87MMOvEdNxRyq0mWH1GEWegZhACz54nvnj+yTzvGDLQ8gKr8iEQDBRDKqgmqZMMq7/y5kd/UdCLFiC+ITZiivaz6fR0er6d054SksUgzmU3qFEXdFzV2Ez8Ywlc/m5Pilsr2VWitP/bGJ4wvDWi96P3Not+n2B3lgIYIAAAAmACYAJgAuAJIA3gFaAaABrgHkAjoC1AMkA4IEUAUiBXAFzgYgBw4H7ghiCPYJsgp4Cq4LCAs2C4AMHAyiDiAPnBBAEUYRvBMwE7wUHhRaFIYUshTcFVAVgBX6FpYXXBeSF/AYYBkCGYgaBhooGkoa1BryGyQbQBtsG5Yb+Bw2HLAdLh1yHYYdsh4cHjYeYB7iHyYf3iAgIFIgdCCaILIgxiDcIPAhBiEkIegiOCK6IxAjeCPQJDQkbCS8JVIlriYWJjomWCZ2JpQmoib0J3QnvCgGKJAopii8KQApIilMKcgqJCpiKpwqyCsUK2QrvCwWLFYsnizgLPYtBC0SLSAAAAABAAAAgAC9ABAAAAAAAAIAAQACABYAAAEAAYEAAAAAeNqNkr1OAkEUhc8CmmBhRSysNtFCTfiXqFBZiIkaQzRqZ7KaBYz8CStg4/PpC1j6EJZWfjMMwSCFmczOuWfOPffOzEpa0avi8hJJSZ/MCfaUIprgmFb15XBcZW+qSWjTKzu8pLF36/Ay/IfDSa173w6/aS2WcvhdudiOjlXTmXwNFaqvgR7UVYe4wOzC+AqIX1hboMiq/qpHoEhNUN0yESjUWPd8e0RT3RaaiNFTWVnGyI6MGuw+s5qKDfgWGSa3Q42QmYXtwabxD/SE0vi0YTZUdRWP/tTb5nTGw/Rq/LrW74K4QTVznr6KeOUYRVV0pVPd6By0KC89l7lI489prufu6Xe1mi5hJtGMbaKMnN+Q/bzdy2iPb4UTB3rE02jqsOae7nirjEp27uNR0MG/+j+BD21Xh+y24Qf2tjvcQYjr7CUnPVStm09eYLPycKb/Em9Zoq755u2fk2Pd/QGe+3ARAAB42m3S1XIUURRG4VmDBHd3d5k+Z5/uBIdAcHd3CRI0OBRPyCshmRWu6Kqp/6brm9qrutVujTy/frZS63/Pjz8/Wm3ajGEs4xhPDxOYyCQmM4WpTGM6M5jJLGYzh7nMYz4LWMgiFrOEpSxjOStYySpWs4a1rGM9G9jIJjazha1sYzsdKhKZoFDT0EsfO9jJLnazh73sYz8H6OcghxjgMEc4yjGOc4KTnOI0ZzjLOc5zgYtc4jJXuMo1rnODm9ziNne4yz3u84CHPOIxTxjkKc94zguGeMkrXvOGt7xjmPd84COf+MwXvvKN7z3DQ4OpDPT/3YGq03ErN7nZDbe4tdu4vW7fyCa9pJf0kl7SS3pJL+klvTTqVXqVXqVX6VV6lV6lV+lVepVe0kt6SS/pJb3U9bL3ZO/J3pO9J3tP7oy+X7uN2/3/0Amd0Amd0Amd0Amd+Od07wi7hF3CLmGXsEvYJewSdgm7hF3CLmGXsEvYJewSdomkl/SSXtLLelkv62W9rJf1sl7Wy3pZL/RCL/RCL/RCL/RCL/RCr+gVvaJX9Ipe0St6Ra/oFb1ar9ar9Wq9Wq/Wq/VqvVqv1mv0Gr1Gr9Frul7xuyp+V8XvqnTyb1UoNRm4Af+FsAGNAEuwCFBYsQEBjlmxRgYrWCGwEFlLsBRSWCGwgFkdsAYrXFhZsBQrAAAAAVLP0T8AAA==) format('woff'), + url('font/genericons-regular-webfont.ttf') format('truetype'), + url('font/genericons-regular-webfont.svg#genericonsregular') format('svg'); + font-weight: normal; + font-style: normal; +} + + +/** + * All Genericons + */ + +.genericon { + display: inline-block; + width: 16px; + height: 16px; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + font-size: 16px; + line-height: 1; + font-family: 'Genericons'; + text-decoration: inherit; + font-weight: normal; + font-style: normal; + vertical-align: top; +} + +/** + * IE7 and IE6 hacks + */ + +.genericon { + *overflow: auto; + *zoom: 1; + *display: inline; +} + +/** + * Individual icons + */ + +/* Post formats */ +.genericon-standard:before { content: '\f100'; } +.genericon-aside:before { content: '\f101'; } +.genericon-image:before { content: '\f102'; } +.genericon-gallery:before { content: '\f103'; } +.genericon-video:before { content: '\f104'; } +.genericon-status:before { content: '\f105'; } +.genericon-quote:before { content: '\f106'; } +.genericon-link:before { content: '\f107'; } +.genericon-chat:before { content: '\f108'; } +.genericon-audio:before { content: '\f109'; } + +/* Social icons */ +.genericon-github:before { content: '\f200'; } +.genericon-dribbble:before { content: '\f201'; } +.genericon-twitter:before { content: '\f202'; } +.genericon-facebook:before { content: '\f203'; } +.genericon-facebook-alt:before { content: '\f204'; } +.genericon-wordpress:before { content: '\f205'; } +.genericon-googleplus:before { content: '\f206'; } +.genericon-linkedin:before { content: '\f207'; } +.genericon-linkedin-alt:before { content: '\f208'; } +.genericon-pinterest:before { content: '\f209'; } +.genericon-pinterest-alt:before { content: '\f210'; } +.genericon-flickr:before { content: '\f211'; } +.genericon-vimeo:before { content: '\f212'; } +.genericon-youtube:before { content: '\f213'; } +.genericon-tumblr:before { content: '\f214'; } +.genericon-instagram:before { content: '\f215'; } +.genericon-codepen:before { content: '\f216'; } +.genericon-polldaddy:before { content: '\f217'; } +.genericon-googleplus-alt:before { content: '\f218'; } +.genericon-path:before { content: '\f219'; } +.genericon-skype:before { content: '\f220'; } +.genericon-digg:before { content: '\f221'; } +.genericon-reddit:before { content: '\f222'; } +.genericon-stumbleupon:before { content: '\f223'; } +.genericon-pocket:before { content: '\f224'; } +.genericon-dropbox:before { content: '\f225'; } + +/* Meta icons */ +.genericon-comment:before { content: '\f300'; } +.genericon-category:before { content: '\f301'; } +.genericon-tag:before { content: '\f302'; } +.genericon-time:before { content: '\f303'; } +.genericon-user:before { content: '\f304'; } +.genericon-day:before { content: '\f305'; } +.genericon-week:before { content: '\f306'; } +.genericon-month:before { content: '\f307'; } +.genericon-pinned:before { content: '\f308'; } + +/* Other icons */ +.genericon-search:before { content: '\f400'; } +.genericon-unzoom:before { content: '\f401'; } +.genericon-zoom:before { content: '\f402'; } +.genericon-show:before { content: '\f403'; } +.genericon-hide:before { content: '\f404'; } +.genericon-close:before { content: '\f405'; } +.genericon-close-alt:before { content: '\f406'; } +.genericon-trash:before { content: '\f407'; } +.genericon-star:before { content: '\f408'; } +.genericon-home:before { content: '\f409'; } +.genericon-mail:before { content: '\f410'; } +.genericon-edit:before { content: '\f411'; } +.genericon-reply:before { content: '\f412'; } +.genericon-feed:before { content: '\f413'; } +.genericon-warning:before { content: '\f414'; } +.genericon-share:before { content: '\f415'; } +.genericon-attachment:before { content: '\f416'; } +.genericon-location:before { content: '\f417'; } +.genericon-checkmark:before { content: '\f418'; } +.genericon-menu:before { content: '\f419'; } +.genericon-refresh:before { content: '\f420'; } +.genericon-minimize:before { content: '\f421'; } +.genericon-maximize:before { content: '\f422'; } +.genericon-404:before { content: '\f423'; } +.genericon-spam:before { content: '\f424'; } +.genericon-summary:before { content: '\f425'; } +.genericon-cloud:before { content: '\f426'; } +.genericon-key:before { content: '\f427'; } +.genericon-dot:before { content: '\f428'; } +.genericon-next:before { content: '\f429'; } +.genericon-previous:before { content: '\f430'; } +.genericon-expand:before { content: '\f431'; } +.genericon-collapse:before { content: '\f432'; } +.genericon-dropdown:before { content: '\f433'; } +.genericon-dropdown-left:before { content: '\f434'; } +.genericon-top:before { content: '\f435'; } +.genericon-draggable:before { content: '\f436'; } +.genericon-phone:before { content: '\f437'; } +.genericon-send-to-phone:before { content: '\f438'; } +.genericon-plugin:before { content: '\f439'; } +.genericon-cloud-download:before { content: '\f440'; } +.genericon-cloud-upload:before { content: '\f441'; } +.genericon-external:before { content: '\f442'; } +.genericon-document:before { content: '\f443'; } +.genericon-book:before { content: '\f444'; } +.genericon-cog:before { content: '\f445'; } +.genericon-unapprove:before { content: '\f446'; } +.genericon-cart:before { content: '\f447'; } +.genericon-pause:before { content: '\f448'; } +.genericon-stop:before { content: '\f449'; } +.genericon-skip-back:before { content: '\f450'; } +.genericon-skip-ahead:before { content: '\f451'; } +.genericon-play:before { content: '\f452'; } +.genericon-tablet:before { content: '\f453'; } +.genericon-send-to-tablet:before { content: '\f454'; } +.genericon-info:before { content: '\f455'; } +.genericon-notice:before { content: '\f456'; } +.genericon-help:before { content: '\f457'; } +.genericon-fastforward:before { content: '\f458'; } +.genericon-rewind:before { content: '\f459'; } +.genericon-portfolio:before { content: '\f460'; } +.genericon-heart:before { content: '\f461'; } +.genericon-code:before { content: '\f462'; } +.genericon-subscribe:before { content: '\f463'; } +.genericon-unsubscribe:before { content: '\f464'; } +.genericon-subscribed:before { content: '\f465'; } +.genericon-reply-alt:before { content: '\f466'; } +.genericon-reply-single:before { content: '\f467'; } +.genericon-flag:before { content: '\f468'; } +.genericon-print:before { content: '\f469'; } +.genericon-lock:before { content: '\f470'; } +.genericon-bold:before { content: '\f471'; } +.genericon-italic:before { content: '\f472'; } +.genericon-picture:before { content: '\f473'; } +.genericon-fullscreen:before { content: '\f474'; } + +/* Generic shapes */ +.genericon-uparrow:before { content: '\f500'; } +.genericon-rightarrow:before { content: '\f501'; } +.genericon-downarrow:before { content: '\f502'; } +.genericon-leftarrow:before { content: '\f503'; } + + + + + diff --git a/wp-content/themes/twentyfourteen/header.php b/wp-content/themes/twentyfourteen/header.php new file mode 100644 index 0000000..139e207 --- /dev/null +++ b/wp-content/themes/twentyfourteen/header.php @@ -0,0 +1,65 @@ + section and everything up till
      + * + * @package WordPress + * @subpackage Twenty_Fourteen + * @since Twenty Fourteen 1.0 + */ +?> + + + +> + + + + + <?php wp_title( '|', true, 'right' ); ?> + + + + + + +> +
      + + + + + + +
      diff --git a/wp-content/themes/twentyfourteen/image.php b/wp-content/themes/twentyfourteen/image.php new file mode 100644 index 0000000..4e7fb82 --- /dev/null +++ b/wp-content/themes/twentyfourteen/image.php @@ -0,0 +1,79 @@ + + +
      +
      + + +
      > +
      + ', '' ); ?> + + +
      + +
      +
      +
      + +
      + + +
      + +
      + +
      + + '', + 'link_before' => '', + 'link_after' => '', + ) ); + ?> +
      +
      + +
      ' ); ?> +
      + + + + + + +
      + + + + + + + + + + + + + + + + + diff --git a/wp-content/themes/twentyfourteen/images/pattern-light.svg b/wp-content/themes/twentyfourteen/images/pattern-light.svg new file mode 100644 index 0000000..55a48f1 --- /dev/null +++ b/wp-content/themes/twentyfourteen/images/pattern-light.svg @@ -0,0 +1,6 @@ + + + diff --git a/wp-content/themes/twentyfourteen/inc/back-compat.php b/wp-content/themes/twentyfourteen/inc/back-compat.php new file mode 100644 index 0000000..c184d91 --- /dev/null +++ b/wp-content/themes/twentyfourteen/inc/back-compat.php @@ -0,0 +1,63 @@ +

      %s

      ', $message ); +} + +/** + * Prevent the Customizer from being loaded on WordPress versions prior to 3.6. + * + * @since Twenty Fourteen 1.0 + */ +function twentyfourteen_customize() { + wp_die( sprintf( __( 'Twenty Fourteen requires at least WordPress version 3.6. You are running version %s. Please upgrade and try again.', 'twentyfourteen' ), $GLOBALS['wp_version'] ), '', array( + 'back_link' => true, + ) ); +} +add_action( 'load-customize.php', 'twentyfourteen_customize' ); + +/** + * Prevent the Theme Preview from being loaded on WordPress versions prior to 3.4. + * + * @since Twenty Fourteen 1.0 + */ +function twentyfourteen_preview() { + if ( isset( $_GET['preview'] ) ) { + wp_die( sprintf( __( 'Twenty Fourteen requires at least WordPress version 3.6. You are running version %s. Please upgrade and try again.', 'twentyfourteen' ), $GLOBALS['wp_version'] ) ); + } +} +add_action( 'template_redirect', 'twentyfourteen_preview' ); diff --git a/wp-content/themes/twentyfourteen/inc/custom-header.php b/wp-content/themes/twentyfourteen/inc/custom-header.php new file mode 100644 index 0000000..c922e64 --- /dev/null +++ b/wp-content/themes/twentyfourteen/inc/custom-header.php @@ -0,0 +1,147 @@ + Header screen. + * @type string $admin_preview_callback Callback function used to create the custom header markup in + * the Appearance > Header screen. + * } + */ + add_theme_support( 'custom-header', apply_filters( 'twentyfourteen_custom_header_args', array( + 'default-text-color' => 'fff', + 'width' => 1260, + 'height' => 240, + 'flex-height' => true, + 'wp-head-callback' => 'twentyfourteen_header_style', + 'admin-head-callback' => 'twentyfourteen_admin_header_style', + 'admin-preview-callback' => 'twentyfourteen_admin_header_image', + ) ) ); +} +add_action( 'after_setup_theme', 'twentyfourteen_custom_header_setup' ); + +if ( ! function_exists( 'twentyfourteen_header_style' ) ) : +/** + * Styles the header image and text displayed on the blog + * + * @see twentyfourteen_custom_header_setup(). + * + */ +function twentyfourteen_header_style() { + $text_color = get_header_textcolor(); + + // If no custom color for text is set, let's bail. + if ( display_header_text() && $text_color === get_theme_support( 'custom-header', 'default-text-color' ) ) + return; + + // If we get this far, we have custom styles. + ?> + + Header screen. + * + * @see twentyfourteen_custom_header_setup() + * + * @since Twenty Fourteen 1.0 + */ +function twentyfourteen_admin_header_style() { +?> + + Header screen. + * + * @see twentyfourteen_custom_header_setup() + * + * @since Twenty Fourteen 1.0 + */ +function twentyfourteen_admin_header_image() { +?> + +get_setting( 'blogname' )->transport = 'postMessage'; + $wp_customize->get_setting( 'blogdescription' )->transport = 'postMessage'; + $wp_customize->get_setting( 'header_textcolor' )->transport = 'postMessage'; + + // Rename the label to "Site Title Color" because this only affects the site title in this theme. + $wp_customize->get_control( 'header_textcolor' )->label = __( 'Site Title Color', 'twentyfourteen' ); + + // Rename the label to "Display Site Title & Tagline" in order to make this option extra clear. + $wp_customize->get_control( 'display_header_text' )->label = __( 'Display Site Title & Tagline', 'twentyfourteen' ); + + // Add custom description to Colors and Background controls or sections. + if ( property_exists( $wp_customize->get_control( 'background_color' ), 'description' ) ) { + $wp_customize->get_control( 'background_color' )->description = __( 'May only be visible on wide screens.', 'twentyfourteen' ); + $wp_customize->get_control( 'background_image' )->description = __( 'May only be visible on wide screens.', 'twentyfourteen' ); + } else { + $wp_customize->get_section( 'colors' )->description = __( 'Background may only be visible on wide screens.', 'twentyfourteen' ); + $wp_customize->get_section( 'background_image' )->description = __( 'Background may only be visible on wide screens.', 'twentyfourteen' ); + } + + // Add the featured content section in case it's not already there. + $wp_customize->add_section( 'featured_content', array( + 'title' => __( 'Featured Content', 'twentyfourteen' ), + 'description' => sprintf( __( 'Use a tag to feature your posts. If no posts match the tag, sticky posts will be displayed instead.', 'twentyfourteen' ), + esc_url( add_query_arg( 'tag', _x( 'featured', 'featured content default tag slug', 'twentyfourteen' ), admin_url( 'edit.php' ) ) ), + admin_url( 'edit.php?show_sticky=1' ) + ), + 'priority' => 130, + 'active_callback' => 'is_front_page', + ) ); + + // Add the featured content layout setting and control. + $wp_customize->add_setting( 'featured_content_layout', array( + 'default' => 'grid', + 'sanitize_callback' => 'twentyfourteen_sanitize_layout', + ) ); + + $wp_customize->add_control( 'featured_content_layout', array( + 'label' => __( 'Layout', 'twentyfourteen' ), + 'section' => 'featured_content', + 'type' => 'select', + 'choices' => array( + 'grid' => __( 'Grid', 'twentyfourteen' ), + 'slider' => __( 'Slider', 'twentyfourteen' ), + ), + ) ); +} +add_action( 'customize_register', 'twentyfourteen_customize_register' ); + +/** + * Sanitize the Featured Content layout value. + * + * @since Twenty Fourteen 1.0 + * + * @param string $layout Layout type. + * @return string Filtered layout type (grid|slider). + */ +function twentyfourteen_sanitize_layout( $layout ) { + if ( ! in_array( $layout, array( 'grid', 'slider' ) ) ) { + $layout = 'grid'; + } + + return $layout; +} + +/** + * Bind JS handlers to make Customizer preview reload changes asynchronously. + * + * @since Twenty Fourteen 1.0 + */ +function twentyfourteen_customize_preview_js() { + wp_enqueue_script( 'twentyfourteen_customizer', get_template_directory_uri() . '/js/customizer.js', array( 'customize-preview' ), '20131205', true ); +} +add_action( 'customize_preview_init', 'twentyfourteen_customize_preview_js' ); + +/** + * Add contextual help to the Themes and Post edit screens. + * + * @since Twenty Fourteen 1.0 + */ +function twentyfourteen_contextual_help() { + if ( 'admin_head-edit.php' === current_filter() && 'post' !== $GLOBALS['typenow'] ) { + return; + } + + get_current_screen()->add_help_tab( array( + 'id' => 'twentyfourteen', + 'title' => __( 'Twenty Fourteen', 'twentyfourteen' ), + 'content' => + '
        ' . + '
      • ' . sprintf( __( 'The home page features your choice of up to 6 posts prominently displayed in a grid or slider, controlled by a tag; you can change the tag and layout in Appearance → Customize. If no posts match the tag, sticky posts will be displayed instead.', 'twentyfourteen' ), esc_url( add_query_arg( 'tag', _x( 'featured', 'featured content default tag slug', 'twentyfourteen' ), admin_url( 'edit.php' ) ) ), admin_url( 'customize.php' ), admin_url( 'edit.php?show_sticky=1' ) ) . '
      • ' . + '
      • ' . sprintf( __( 'Enhance your site design by using Featured Images for posts you’d like to stand out (also known as post thumbnails). This allows you to associate an image with your post without inserting it. Twenty Fourteen uses featured images for posts and pages—above the title—and in the Featured Content area on the home page.', 'twentyfourteen' ), 'http://codex.wordpress.org/Post_Thumbnails#Setting_a_Post_Thumbnail' ) . '
      • ' . + '
      • ' . sprintf( __( 'For an in-depth tutorial, and more tips and tricks, visit the Twenty Fourteen documentation.', 'twentyfourteen' ), 'http://codex.wordpress.org/Twenty_Fourteen' ) . '
      • ' . + '
      ', + ) ); +} +add_action( 'admin_head-themes.php', 'twentyfourteen_contextual_help' ); +add_action( 'admin_head-edit.php', 'twentyfourteen_contextual_help' ); diff --git a/wp-content/themes/twentyfourteen/inc/featured-content.php b/wp-content/themes/twentyfourteen/inc/featured-content.php new file mode 100644 index 0000000..8bc7dc5 --- /dev/null +++ b/wp-content/themes/twentyfourteen/inc/featured-content.php @@ -0,0 +1,531 @@ + $post_ids, + 'posts_per_page' => count( $post_ids ), + ) ); + + return $featured_posts; + } + + /** + * Get featured post IDs + * + * This function will return the an array containing the + * post IDs of all featured posts. + * + * Sets the "featured_content_ids" transient. + * + * @static + * @access public + * @since Twenty Fourteen 1.0 + * + * @return array Array of post IDs. + */ + public static function get_featured_post_ids() { + // Get array of cached results if they exist. + $featured_ids = get_transient( 'featured_content_ids' ); + + if ( false === $featured_ids ) { + $settings = self::get_setting(); + $term = get_term_by( 'name', $settings['tag-name'], 'post_tag' ); + + if ( $term ) { + // Query for featured posts. + $featured_ids = get_posts( array( + 'fields' => 'ids', + 'numberposts' => self::$max_posts, + 'suppress_filters' => false, + 'tax_query' => array( + array( + 'field' => 'term_id', + 'taxonomy' => 'post_tag', + 'terms' => $term->term_id, + ), + ), + ) ); + } + + // Get sticky posts if no Featured Content exists. + if ( ! $featured_ids ) { + $featured_ids = self::get_sticky_posts(); + } + + set_transient( 'featured_content_ids', $featured_ids ); + } + + // Ensure correct format before return. + return array_map( 'absint', $featured_ids ); + } + + /** + * Return an array with IDs of posts maked as sticky. + * + * @static + * @access public + * @since Twenty Fourteen 1.0 + * + * @return array Array of sticky posts. + */ + public static function get_sticky_posts() { + return array_slice( get_option( 'sticky_posts', array() ), 0, self::$max_posts ); + } + + /** + * Delete featured content ids transient. + * + * Hooks in the "save_post" action. + * + * @see Featured_Content::validate_settings(). + * + * @static + * @access public + * @since Twenty Fourteen 1.0 + */ + public static function delete_transient() { + delete_transient( 'featured_content_ids' ); + } + + /** + * Exclude featured posts from the home page blog query. + * + * Filter the home page posts, and remove any featured post ID's from it. + * Hooked onto the 'pre_get_posts' action, this changes the parameters of + * the query before it gets any posts. + * + * @static + * @access public + * @since Twenty Fourteen 1.0 + * + * @param WP_Query $query WP_Query object. + * @return WP_Query Possibly-modified WP_Query. + */ + public static function pre_get_posts( $query ) { + + // Bail if not home or not main query. + if ( ! $query->is_home() || ! $query->is_main_query() ) { + return; + } + + // Bail if the blog page is not the front page. + if ( 'posts' !== get_option( 'show_on_front' ) ) { + return; + } + + $featured = self::get_featured_post_ids(); + + // Bail if no featured posts. + if ( ! $featured ) { + return; + } + + // We need to respect post ids already in the blacklist. + $post__not_in = $query->get( 'post__not_in' ); + + if ( ! empty( $post__not_in ) ) { + $featured = array_merge( (array) $post__not_in, $featured ); + $featured = array_unique( $featured ); + } + + $query->set( 'post__not_in', $featured ); + } + + /** + * Reset tag option when the saved tag is deleted. + * + * It's important to mention that the transient needs to be deleted, + * too. While it may not be obvious by looking at the function alone, + * the transient is deleted by Featured_Content::validate_settings(). + * + * Hooks in the "delete_post_tag" action. + * + * @see Featured_Content::validate_settings(). + * + * @static + * @access public + * @since Twenty Fourteen 1.0 + * + * @param int $tag_id The term_id of the tag that has been deleted. + */ + public static function delete_post_tag( $tag_id ) { + $settings = self::get_setting(); + + if ( empty( $settings['tag-id'] ) || $tag_id != $settings['tag-id'] ) { + return; + } + + $settings['tag-id'] = 0; + $settings = self::validate_settings( $settings ); + update_option( 'featured-content', $settings ); + } + + /** + * Hide featured tag from displaying when global terms are queried from the front-end. + * + * Hooks into the "get_terms" filter. + * + * @static + * @access public + * @since Twenty Fourteen 1.0 + * + * @param array $terms List of term objects. This is the return value of get_terms(). + * @param array $taxonomies An array of taxonomy slugs. + * @return array A filtered array of terms. + * + * @uses Featured_Content::get_setting() + */ + public static function hide_featured_term( $terms, $taxonomies, $args ) { + + // This filter is only appropriate on the front-end. + if ( is_admin() ) { + return $terms; + } + + // We only want to hide the featured tag. + if ( ! in_array( 'post_tag', $taxonomies ) ) { + return $terms; + } + + // Bail if no terms were returned. + if ( empty( $terms ) ) { + return $terms; + } + + // Bail if term objects are unavailable. + if ( 'all' != $args['fields'] ) { + return $terms; + } + + $settings = self::get_setting(); + foreach( $terms as $order => $term ) { + if ( ( $settings['tag-id'] === $term->term_id || $settings['tag-name'] === $term->name ) && 'post_tag' === $term->taxonomy ) { + unset( $terms[ $order ] ); + } + } + + return $terms; + } + + /** + * Hide featured tag from display when terms associated with a post object + * are queried from the front-end. + * + * Hooks into the "get_the_terms" filter. + * + * @static + * @access public + * @since Twenty Fourteen 1.0 + * + * @param array $terms A list of term objects. This is the return value of get_the_terms(). + * @param int $id The ID field for the post object that terms are associated with. + * @param array $taxonomy An array of taxonomy slugs. + * @return array Filtered array of terms. + * + * @uses Featured_Content::get_setting() + */ + public static function hide_the_featured_term( $terms, $id, $taxonomy ) { + + // This filter is only appropriate on the front-end. + if ( is_admin() ) { + return $terms; + } + + // Make sure we are in the correct taxonomy. + if ( 'post_tag' != $taxonomy ) { + return $terms; + } + + // No terms? Return early! + if ( empty( $terms ) ) { + return $terms; + } + + $settings = self::get_setting(); + foreach( $terms as $order => $term ) { + if ( ( $settings['tag-id'] === $term->term_id || $settings['tag-name'] === $term->name ) && 'post_tag' === $term->taxonomy ) { + unset( $terms[ $term->term_id ] ); + } + } + + return $terms; + } + + /** + * Register custom setting on the Settings -> Reading screen. + * + * @static + * @access public + * @since Twenty Fourteen 1.0 + */ + public static function register_setting() { + register_setting( 'featured-content', 'featured-content', array( __CLASS__, 'validate_settings' ) ); + } + + /** + * Add settings to the Customizer. + * + * @static + * @access public + * @since Twenty Fourteen 1.0 + * + * @param WP_Customize_Manager $wp_customize Customizer object. + */ + public static function customize_register( $wp_customize ) { + $wp_customize->add_section( 'featured_content', array( + 'title' => __( 'Featured Content', 'twentyfourteen' ), + 'description' => sprintf( __( 'Use a tag to feature your posts. If no posts match the tag, sticky posts will be displayed instead.', 'twentyfourteen' ), + esc_url( add_query_arg( 'tag', _x( 'featured', 'featured content default tag slug', 'twentyfourteen' ), admin_url( 'edit.php' ) ) ), + admin_url( 'edit.php?show_sticky=1' ) + ), + 'priority' => 130, + 'theme_supports' => 'featured-content', + ) ); + + // Add Featured Content settings. + $wp_customize->add_setting( 'featured-content[tag-name]', array( + 'default' => _x( 'featured', 'featured content default tag slug', 'twentyfourteen' ), + 'type' => 'option', + 'sanitize_js_callback' => array( __CLASS__, 'delete_transient' ), + ) ); + $wp_customize->add_setting( 'featured-content[hide-tag]', array( + 'default' => true, + 'type' => 'option', + 'sanitize_js_callback' => array( __CLASS__, 'delete_transient' ), + ) ); + + // Add Featured Content controls. + $wp_customize->add_control( 'featured-content[tag-name]', array( + 'label' => __( 'Tag Name', 'twentyfourteen' ), + 'section' => 'featured_content', + 'priority' => 20, + ) ); + $wp_customize->add_control( 'featured-content[hide-tag]', array( + 'label' => __( 'Don’t display tag on front end.', 'twentyfourteen' ), + 'section' => 'featured_content', + 'type' => 'checkbox', + 'priority' => 30, + ) ); + } + + /** + * Enqueue the tag suggestion script. + * + * @static + * @access public + * @since Twenty Fourteen 1.0 + */ + public static function enqueue_scripts() { + wp_enqueue_script( 'featured-content-suggest', get_template_directory_uri() . '/js/featured-content-admin.js', array( 'jquery', 'suggest' ), '20131022', true ); + } + + /** + * Get featured content settings. + * + * Get all settings recognized by this module. This function + * will return all settings whether or not they have been stored + * in the database yet. This ensures that all keys are available + * at all times. + * + * In the event that you only require one setting, you may pass + * its name as the first parameter to the function and only that + * value will be returned. + * + * @static + * @access public + * @since Twenty Fourteen 1.0 + * + * @param string $key The key of a recognized setting. + * @return mixed Array of all settings by default. A single value if passed as first parameter. + */ + public static function get_setting( $key = 'all' ) { + $saved = (array) get_option( 'featured-content' ); + + $defaults = array( + 'hide-tag' => 1, + 'tag-id' => 0, + 'tag-name' => _x( 'featured', 'featured content default tag slug', 'twentyfourteen' ), + ); + + $options = wp_parse_args( $saved, $defaults ); + $options = array_intersect_key( $options, $defaults ); + + if ( 'all' != $key ) { + return isset( $options[ $key ] ) ? $options[ $key ] : false; + } + + return $options; + } + + /** + * Validate featured content settings. + * + * Make sure that all user supplied content is in an expected + * format before saving to the database. This function will also + * delete the transient set in Featured_Content::get_featured_content(). + * + * @static + * @access public + * @since Twenty Fourteen 1.0 + * + * @param array $input Array of settings input. + * @return array Validated settings output. + */ + public static function validate_settings( $input ) { + $output = array(); + + if ( empty( $input['tag-name'] ) ) { + $output['tag-id'] = 0; + } else { + $term = get_term_by( 'name', $input['tag-name'], 'post_tag' ); + + if ( $term ) { + $output['tag-id'] = $term->term_id; + } else { + $new_tag = wp_create_tag( $input['tag-name'] ); + + if ( ! is_wp_error( $new_tag ) && isset( $new_tag['term_id'] ) ) { + $output['tag-id'] = $new_tag['term_id']; + } + } + + $output['tag-name'] = $input['tag-name']; + } + + $output['hide-tag'] = isset( $input['hide-tag'] ) && $input['hide-tag'] ? 1 : 0; + + // Delete the featured post ids transient. + self::delete_transient(); + + return $output; + } +} // Featured_Content + +Featured_Content::setup(); diff --git a/wp-content/themes/twentyfourteen/inc/template-tags.php b/wp-content/themes/twentyfourteen/inc/template-tags.php new file mode 100644 index 0000000..084f97a --- /dev/null +++ b/wp-content/themes/twentyfourteen/inc/template-tags.php @@ -0,0 +1,224 @@ +max_num_pages < 2 ) { + return; + } + + $paged = get_query_var( 'paged' ) ? intval( get_query_var( 'paged' ) ) : 1; + $pagenum_link = html_entity_decode( get_pagenum_link() ); + $query_args = array(); + $url_parts = explode( '?', $pagenum_link ); + + if ( isset( $url_parts[1] ) ) { + wp_parse_str( $url_parts[1], $query_args ); + } + + $pagenum_link = remove_query_arg( array_keys( $query_args ), $pagenum_link ); + $pagenum_link = trailingslashit( $pagenum_link ) . '%_%'; + + $format = $wp_rewrite->using_index_permalinks() && ! strpos( $pagenum_link, 'index.php' ) ? 'index.php/' : ''; + $format .= $wp_rewrite->using_permalinks() ? user_trailingslashit( $wp_rewrite->pagination_base . '/%#%', 'paged' ) : '?paged=%#%'; + + // Set up paginated links. + $links = paginate_links( array( + 'base' => $pagenum_link, + 'format' => $format, + 'total' => $wp_query->max_num_pages, + 'current' => $paged, + 'mid_size' => 1, + 'add_args' => array_map( 'urlencode', $query_args ), + 'prev_text' => __( '← Previous', 'twentyfourteen' ), + 'next_text' => __( 'Next →', 'twentyfourteen' ), + ) ); + + if ( $links ) : + + ?> + + post_parent ) : get_adjacent_post( false, '', true ); + $next = get_adjacent_post( false, '', false ); + + if ( ! $next && ! $previous ) { + return; + } + + ?> + + ' . __( 'Sticky', 'twentyfourteen' ) . '
      '; + } + + // Set up and print post meta information. + printf( ' ', + esc_url( get_permalink() ), + esc_attr( get_the_date( 'c' ) ), + esc_html( get_the_date() ), + esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ), + get_the_author() + ); +} +endif; + +/** + * Find out if blog has more than one category. + * + * @since Twenty Fourteen 1.0 + * + * @return boolean true if blog has more than 1 category + */ +function twentyfourteen_categorized_blog() { + if ( false === ( $all_the_cool_cats = get_transient( 'twentyfourteen_category_count' ) ) ) { + // Create an array of all the categories that are attached to posts + $all_the_cool_cats = get_categories( array( + 'hide_empty' => 1, + ) ); + + // Count the number of categories that are attached to the posts + $all_the_cool_cats = count( $all_the_cool_cats ); + + set_transient( 'twentyfourteen_category_count', $all_the_cool_cats ); + } + + if ( 1 !== (int) $all_the_cool_cats ) { + // This blog has more than 1 category so twentyfourteen_categorized_blog should return true + return true; + } else { + // This blog has only 1 category so twentyfourteen_categorized_blog should return false + return false; + } +} + +/** + * Flush out the transients used in twentyfourteen_categorized_blog. + * + * @since Twenty Fourteen 1.0 + */ +function twentyfourteen_category_transient_flusher() { + // Like, beat it. Dig? + delete_transient( 'twentyfourteen_category_count' ); +} +add_action( 'edit_category', 'twentyfourteen_category_transient_flusher' ); +add_action( 'save_post', 'twentyfourteen_category_transient_flusher' ); + +/** + * Display an optional post thumbnail. + * + * Wraps the post thumbnail in an anchor element on index + * views, or a div element when on single views. + * + * @since Twenty Fourteen 1.0 + */ +function twentyfourteen_post_thumbnail() { + if ( post_password_required() || is_attachment() || ! has_post_thumbnail() ) { + return; + } + + if ( is_singular() ) : + ?> + +
      + +
      + + + + + + %2$s', + esc_url( get_permalink( get_the_ID() ) ), + /* translators: %s: Name of current post */ + sprintf( __( 'Continue reading %s ', 'twentyfourteen' ), '' . get_the_title( get_the_ID() ) . '' ) + ); + return ' … ' . $link; +} +add_filter( 'excerpt_more', 'twentyfourteen_excerpt_more' ); +endif; diff --git a/wp-content/themes/twentyfourteen/inc/widgets.php b/wp-content/themes/twentyfourteen/inc/widgets.php new file mode 100644 index 0000000..9bd1e79 --- /dev/null +++ b/wp-content/themes/twentyfourteen/inc/widgets.php @@ -0,0 +1,269 @@ + 'widget_twentyfourteen_ephemera', + 'description' => __( 'Use this widget to list your recent Aside, Quote, Video, Audio, Image, Gallery, and Link posts.', 'twentyfourteen' ), + ) ); + } + + /** + * Output the HTML for this widget. + * + * @access public + * @since Twenty Fourteen 1.0 + * + * @param array $args An array of standard parameters for widgets in this theme. + * @param array $instance An array of settings for this widget instance. + */ + public function widget( $args, $instance ) { + $format = isset( $instance['format'] ) && in_array( $instance['format'], $this->formats ) ? $instance['format'] : 'aside'; + + switch ( $format ) { + case 'image': + $format_string = __( 'Images', 'twentyfourteen' ); + $format_string_more = __( 'More images', 'twentyfourteen' ); + break; + case 'video': + $format_string = __( 'Videos', 'twentyfourteen' ); + $format_string_more = __( 'More videos', 'twentyfourteen' ); + break; + case 'audio': + $format_string = __( 'Audio', 'twentyfourteen' ); + $format_string_more = __( 'More audio', 'twentyfourteen' ); + break; + case 'quote': + $format_string = __( 'Quotes', 'twentyfourteen' ); + $format_string_more = __( 'More quotes', 'twentyfourteen' ); + break; + case 'link': + $format_string = __( 'Links', 'twentyfourteen' ); + $format_string_more = __( 'More links', 'twentyfourteen' ); + break; + case 'gallery': + $format_string = __( 'Galleries', 'twentyfourteen' ); + $format_string_more = __( 'More galleries', 'twentyfourteen' ); + break; + case 'aside': + default: + $format_string = __( 'Asides', 'twentyfourteen' ); + $format_string_more = __( 'More asides', 'twentyfourteen' ); + break; + } + + $number = empty( $instance['number'] ) ? 2 : absint( $instance['number'] ); + $title = apply_filters( 'widget_title', empty( $instance['title'] ) ? $format_string : $instance['title'], $instance, $this->id_base ); + + $ephemera = new WP_Query( array( + 'order' => 'DESC', + 'posts_per_page' => $number, + 'no_found_rows' => true, + 'post_status' => 'publish', + 'post__not_in' => get_option( 'sticky_posts' ), + 'tax_query' => array( + array( + 'taxonomy' => 'post_format', + 'terms' => array( "post-format-$format" ), + 'field' => 'slug', + 'operator' => 'IN', + ), + ), + ) ); + + if ( $ephemera->have_posts() ) : + $tmp_content_width = $GLOBALS['content_width']; + $GLOBALS['content_width'] = 306; + + echo $args['before_widget']; + ?> +

      + +

      +
        + + have_posts() ) : + $ephemera->the_post(); + $tmp_more = $GLOBALS['more']; + $GLOBALS['more'] = 0; + ?> +
      1. +
        > +
        + →', 'twentyfourteen' ) ); + else : + $images = array(); + + $galleries = get_post_galleries( get_the_ID(), false ); + if ( isset( $galleries[0]['ids'] ) ) + $images = explode( ',', $galleries[0]['ids'] ); + + if ( ! $images ) : + $images = get_posts( array( + 'fields' => 'ids', + 'numberposts' => -1, + 'order' => 'ASC', + 'orderby' => 'menu_order', + 'post_mime_type' => 'image', + 'post_parent' => get_the_ID(), + 'post_type' => 'attachment', + ) ); + endif; + + $total_images = count( $images ); + + if ( has_post_thumbnail() ) : + $post_thumbnail = get_the_post_thumbnail(); + elseif ( $total_images > 0 ) : + $image = array_shift( $images ); + $post_thumbnail = wp_get_attachment_image( $image, 'post-thumbnail' ); + endif; + + if ( ! empty ( $post_thumbnail ) ) : + ?> + + +

        + %2$s photo.', 'This gallery contains %2$s photos.', $total_images, 'twentyfourteen' ), + esc_url( get_permalink() ), + number_format_i18n( $total_images ) + ); + ?> +

        + →', 'twentyfourteen' ) ); + endif; + ?> +
        + +
        + +
        +
        +
      2. + + +
      + +
      ', 'twentyfourteen' ), $format_string_more ); + ?> + + formats ) ) { + $instance['format'] = $new_instance['format']; + } + + return $instance; + } + + /** + * Display the form for this widget on the Widgets page of the Admin area. + * + * @since Twenty Fourteen 1.0 + * + * @param array $instance + */ + function form( $instance ) { + $title = empty( $instance['title'] ) ? '' : esc_attr( $instance['title'] ); + $number = empty( $instance['number'] ) ? 2 : absint( $instance['number'] ); + $format = isset( $instance['format'] ) && in_array( $instance['format'], $this->formats ) ? $instance['format'] : 'aside'; + ?> +

      +

      + +

      +

      + +

      + + + +

      + + + +
      +
      + + + +
      +
      + +
      + + 781 ) { + var mastheadHeight = $( '#masthead' ).height(), + toolbarOffset, mastheadOffset; + + if ( mastheadHeight > 48 ) { + body.removeClass( 'masthead-fixed' ); + } + + if ( body.is( '.header-image' ) ) { + toolbarOffset = body.is( '.admin-bar' ) ? $( '#wpadminbar' ).height() : 0; + mastheadOffset = $( '#masthead' ).offset().top - toolbarOffset; + + _window.on( 'scroll.twentyfourteen', function() { + if ( _window.scrollTop() > mastheadOffset && mastheadHeight < 49 ) { + body.addClass( 'masthead-fixed' ); + } else { + body.removeClass( 'masthead-fixed' ); + } + } ); + } + } + + // Focus styles for menus. + $( '.primary-navigation, .secondary-navigation' ).find( 'a' ).on( 'focus.twentyfourteen blur.twentyfourteen', function() { + $( this ).parents().toggleClass( 'focus' ); + } ); + } ); + + _window.load( function() { + // Arrange footer widgets vertically. + if ( $.isFunction( $.fn.masonry ) ) { + $( '#footer-sidebar' ).masonry( { + itemSelector: '.widget', + columnWidth: function( containerWidth ) { + return containerWidth / 4; + }, + gutterWidth: 0, + isResizable: true, + isRTL: $( 'body' ).is( '.rtl' ) + } ); + } + + // Initialize Featured Content slider. + if ( body.is( '.slider' ) ) { + $( '.featured-content' ).featuredslider( { + selector: '.featured-content-inner > article', + controlsContainer: '.featured-content' + } ); + } + } ); +} )( jQuery ); diff --git a/wp-content/themes/twentyfourteen/js/html5.js b/wp-content/themes/twentyfourteen/js/html5.js new file mode 100644 index 0000000..6168aac --- /dev/null +++ b/wp-content/themes/twentyfourteen/js/html5.js @@ -0,0 +1,8 @@ +/* + HTML5 Shiv v3.7.0 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed +*/ +(function(l,f){function m(){var a=e.elements;return"string"==typeof a?a.split(" "):a}function i(a){var b=n[a[o]];b||(b={},h++,a[o]=h,n[h]=b);return b}function p(a,b,c){b||(b=f);if(g)return b.createElement(a);c||(c=i(b));b=c.cache[a]?c.cache[a].cloneNode():r.test(a)?(c.cache[a]=c.createElem(a)).cloneNode():c.createElem(a);return b.canHaveChildren&&!s.test(a)?c.frag.appendChild(b):b}function t(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag(); +a.createElement=function(c){return!e.shivMethods?b.createElem(c):p(c,a,b)};a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/[\w\-]+/g,function(a){b.createElem(a);b.frag.createElement(a);return'c("'+a+'")'})+");return n}")(e,b.frag)}function q(a){a||(a=f);var b=i(a);if(e.shivCSS&&!j&&!b.hasCSS){var c,d=a;c=d.createElement("p");d=d.getElementsByTagName("head")[0]||d.documentElement;c.innerHTML="x"; +c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="";j="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode|| +"undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version:"3.7.0",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f); +if(g)return a.createDocumentFragment();for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;d 0 && $( slider.vars.controlsContainer ); + } + + slider.doMath(); + + // INIT + slider.setup( 'init' ); + + // CONTROLNAV + methods.controlNav.setup(); + + // DIRECTIONNAV + methods.directionNav.setup(); + + // KEYBOARD + if ( $( slider.containerSelector ).length === 1 ) { + $( document ).bind( 'keyup', function( event ) { + var keycode = event.keyCode, + target = false; + if ( ! slider.animating && ( keycode === 39 || keycode === 37 ) ) { + if ( keycode === 39 ) { + target = slider.getTarget( 'next' ); + } else if ( keycode === 37 ) { + target = slider.getTarget( 'prev' ); + } + + slider.featureAnimate( target ); + } + } ); + } + + // TOUCH + if ( touch ) { + methods.touch(); + } + + $( window ).bind( 'resize orientationchange focus', methods.resize ); + + slider.find( 'img' ).attr( 'draggable', 'false' ); + }, + + controlNav: { + setup: function() { + methods.controlNav.setupPaging(); + }, + setupPaging: function() { + var type = 'control-paging', + j = 1, + item, + slide, + i; + + slider.controlNavScaffold = $( '
        ' ); + + if ( slider.pagingCount > 1 ) { + for ( i = 0; i < slider.pagingCount; i++ ) { + slide = slider.slides.eq( i ); + item = '' + j + ''; + slider.controlNavScaffold.append( '
      1. ' + item + '
      2. ' ); + j++; + } + } + + // CONTROLSCONTAINER + ( slider.controlsContainer ) ? $( slider.controlsContainer ).append( slider.controlNavScaffold ) : slider.append( slider.controlNavScaffold ); + methods.controlNav.set(); + + methods.controlNav.active(); + + slider.controlNavScaffold.delegate( 'a, img', eventType, function( event ) { + event.preventDefault(); + + if ( watchedEvent === '' || watchedEvent === event.type ) { + var $this = $( this ), + target = slider.controlNav.index( $this ); + + if ( ! $this.hasClass( namespace + 'active' ) ) { + slider.direction = ( target > slider.currentSlide ) ? 'next' : 'prev'; + slider.featureAnimate( target ); + } + } + + // Set up flags to prevent event duplication. + if ( watchedEvent === '' ) { + watchedEvent = event.type; + } + + methods.setToClearWatchedEvent(); + } ); + }, + set: function() { + var selector = 'a'; + slider.controlNav = $( '.' + namespace + 'control-nav li ' + selector, ( slider.controlsContainer ) ? slider.controlsContainer : slider ); + }, + active: function() { + slider.controlNav.removeClass( namespace + 'active' ).eq( slider.animatingTo ).addClass( namespace + 'active' ); + }, + update: function( action, pos ) { + if ( slider.pagingCount > 1 && action === 'add' ) { + slider.controlNavScaffold.append( $( '
      3. ' + slider.count + '
      4. ' ) ); + } else if ( slider.pagingCount === 1 ) { + slider.controlNavScaffold.find( 'li' ).remove(); + } else { + slider.controlNav.eq( pos ).closest( 'li' ).remove(); + } + methods.controlNav.set(); + ( slider.pagingCount > 1 && slider.pagingCount !== slider.controlNav.length ) ? slider.update( pos, action ) : methods.controlNav.active(); + } + }, + + directionNav: { + setup: function() { + var directionNavScaffold = $( '' ); + + // CONTROLSCONTAINER + if ( slider.controlsContainer ) { + $( slider.controlsContainer ).append( directionNavScaffold ); + slider.directionNav = $( '.' + namespace + 'direction-nav li a', slider.controlsContainer ); + } else { + slider.append( directionNavScaffold ); + slider.directionNav = $( '.' + namespace + 'direction-nav li a', slider ); + } + + methods.directionNav.update(); + + slider.directionNav.bind( eventType, function( event ) { + event.preventDefault(); + var target; + + if ( watchedEvent === '' || watchedEvent === event.type ) { + target = ( $( this ).hasClass( namespace + 'next' ) ) ? slider.getTarget( 'next' ) : slider.getTarget( 'prev' ); + slider.featureAnimate( target ); + } + + // Set up flags to prevent event duplication. + if ( watchedEvent === '' ) { + watchedEvent = event.type; + } + + methods.setToClearWatchedEvent(); + } ); + }, + update: function() { + var disabledClass = namespace + 'disabled'; + if ( slider.pagingCount === 1 ) { + slider.directionNav.addClass( disabledClass ).attr( 'tabindex', '-1' ); + } else { + slider.directionNav.removeClass( disabledClass ).removeAttr( 'tabindex' ); + } + } + }, + + touch: function() { + var startX, + startY, + offset, + cwidth, + dx, + startT, + scrolling = false, + localX = 0, + localY = 0, + accDx = 0; + + if ( ! msGesture ) { + el.addEventListener( 'touchstart', onTouchStart, false ); + } else { + el.style.msTouchAction = 'none'; + el._gesture = new MSGesture(); // MSFT specific. + el._gesture.target = el; + el.addEventListener( 'MSPointerDown', onMSPointerDown, false ); + el._slider = slider; + el.addEventListener( 'MSGestureChange', onMSGestureChange, false ); + el.addEventListener( 'MSGestureEnd', onMSGestureEnd, false ); + } + + function onTouchStart( e ) { + if ( slider.animating ) { + e.preventDefault(); + } else if ( ( window.navigator.msPointerEnabled ) || e.touches.length === 1 ) { + cwidth = slider.w; + startT = Number( new Date() ); + + // Local vars for X and Y points. + localX = e.touches[0].pageX; + localY = e.touches[0].pageY; + + offset = ( slider.currentSlide + slider.cloneOffset ) * cwidth; + if ( slider.animatingTo === slider.last && slider.direction !== 'next' ) { + offset = 0; + } + + startX = localX; + startY = localY; + + el.addEventListener( 'touchmove', onTouchMove, false ); + el.addEventListener( 'touchend', onTouchEnd, false ); + } + } + + function onTouchMove( e ) { + // Local vars for X and Y points. + localX = e.touches[0].pageX; + localY = e.touches[0].pageY; + + dx = startX - localX; + scrolling = Math.abs( dx ) < Math.abs( localY - startY ); + + if ( ! scrolling ) { + e.preventDefault(); + if ( slider.transitions ) { + slider.setProps( offset + dx, 'setTouch' ); + } + } + } + + function onTouchEnd() { + // Finish the touch by undoing the touch session. + el.removeEventListener( 'touchmove', onTouchMove, false ); + + if ( slider.animatingTo === slider.currentSlide && ! scrolling && dx !== null ) { + var updateDx = dx, + target = ( updateDx > 0 ) ? slider.getTarget( 'next' ) : slider.getTarget( 'prev' ); + + slider.featureAnimate( target ); + } + el.removeEventListener( 'touchend', onTouchEnd, false ); + + startX = null; + startY = null; + dx = null; + offset = null; + } + + function onMSPointerDown( e ) { + e.stopPropagation(); + if ( slider.animating ) { + e.preventDefault(); + } else { + el._gesture.addPointer( e.pointerId ); + accDx = 0; + cwidth = slider.w; + startT = Number( new Date() ); + offset = ( slider.currentSlide + slider.cloneOffset ) * cwidth; + if ( slider.animatingTo === slider.last && slider.direction !== 'next' ) { + offset = 0; + } + } + } + + function onMSGestureChange( e ) { + e.stopPropagation(); + var slider = e.target._slider, + transX, + transY; + if ( ! slider ) { + return; + } + + transX = -e.translationX, + transY = -e.translationY; + + // Accumulate translations. + accDx = accDx + transX; + dx = accDx; + scrolling = Math.abs( accDx ) < Math.abs( -transY ); + + if ( e.detail === e.MSGESTURE_FLAG_INERTIA ) { + setImmediate( function () { // MSFT specific. + el._gesture.stop(); + } ); + + return; + } + + if ( ! scrolling || Number( new Date() ) - startT > 500 ) { + e.preventDefault(); + if ( slider.transitions ) { + slider.setProps( offset + dx, 'setTouch' ); + } + } + } + + function onMSGestureEnd( e ) { + e.stopPropagation(); + var slider = e.target._slider, + updateDx, + target; + if ( ! slider ) { + return; + } + + if ( slider.animatingTo === slider.currentSlide && ! scrolling && dx !== null ) { + updateDx = dx, + target = ( updateDx > 0 ) ? slider.getTarget( 'next' ) : slider.getTarget( 'prev' ); + + slider.featureAnimate( target ); + } + + startX = null; + startY = null; + dx = null; + offset = null; + accDx = 0; + } + }, + + resize: function() { + if ( ! slider.animating && slider.is( ':visible' ) ) { + slider.doMath(); + + // SMOOTH HEIGHT + methods.smoothHeight(); + slider.newSlides.width( slider.computedW ); + slider.setProps( slider.computedW, 'setTotal' ); + } + }, + + smoothHeight: function( dur ) { + var $obj = slider.viewport; + ( dur ) ? $obj.animate( { 'height': slider.slides.eq( slider.animatingTo ).height() }, dur ) : $obj.height( slider.slides.eq( slider.animatingTo ).height() ); + }, + + setToClearWatchedEvent: function() { + clearTimeout( watchedEventClearTimer ); + watchedEventClearTimer = setTimeout( function() { + watchedEvent = ''; + }, 3000 ); + } + }; + + // Public methods. + slider.featureAnimate = function( target ) { + if ( target !== slider.currentSlide ) { + slider.direction = ( target > slider.currentSlide ) ? 'next' : 'prev'; + } + + if ( ! slider.animating && slider.is( ':visible' ) ) { + slider.animating = true; + slider.animatingTo = target; + + // CONTROLNAV + methods.controlNav.active(); + + slider.slides.removeClass( namespace + 'active-slide' ).eq( target ).addClass( namespace + 'active-slide' ); + + slider.atEnd = target === 0 || target === slider.last; + + // DIRECTIONNAV + methods.directionNav.update(); + + var dimension = slider.computedW, + slideString; + + if ( slider.currentSlide === 0 && target === slider.count - 1 && slider.direction !== 'next' ) { + slideString = 0; + } else if ( slider.currentSlide === slider.last && target === 0 && slider.direction !== 'prev' ) { + slideString = ( slider.count + 1 ) * dimension; + } else { + slideString = ( target + slider.cloneOffset ) * dimension; + } + slider.setProps( slideString, '', slider.vars.animationSpeed ); + if ( slider.transitions ) { + if ( ! slider.atEnd ) { + slider.animating = false; + slider.currentSlide = slider.animatingTo; + } + slider.container.unbind( 'webkitTransitionEnd transitionend' ); + slider.container.bind( 'webkitTransitionEnd transitionend', function() { + slider.wrapup( dimension ); + } ); + } else { + slider.container.animate( slider.args, slider.vars.animationSpeed, 'swing', function() { + slider.wrapup( dimension ); + } ); + } + + // SMOOTH HEIGHT + methods.smoothHeight( slider.vars.animationSpeed ); + } + }; + + slider.wrapup = function( dimension ) { + if ( slider.currentSlide === 0 && slider.animatingTo === slider.last ) { + slider.setProps( dimension, 'jumpEnd' ); + } else if ( slider.currentSlide === slider.last && slider.animatingTo === 0 ) { + slider.setProps( dimension, 'jumpStart' ); + } + slider.animating = false; + slider.currentSlide = slider.animatingTo; + }; + + slider.getTarget = function( dir ) { + slider.direction = dir; + + // Swap for RTL. + if ( slider.isRtl ) { + dir = 'next' === dir ? 'prev' : 'next'; + } + + if ( dir === 'next' ) { + return ( slider.currentSlide === slider.last ) ? 0 : slider.currentSlide + 1; + } else { + return ( slider.currentSlide === 0 ) ? slider.last : slider.currentSlide - 1; + } + }; + + slider.setProps = function( pos, special, dur ) { + var target = ( function() { + var posCalc = ( function() { + switch ( special ) { + case 'setTotal': return ( slider.currentSlide + slider.cloneOffset ) * pos; + case 'setTouch': return pos; + case 'jumpEnd': return slider.count * pos; + case 'jumpStart': return pos; + default: return pos; + } + }() ); + + return ( posCalc * -1 ) + 'px'; + }() ); + + if ( slider.transitions ) { + target = 'translate3d(' + target + ',0,0 )'; + dur = ( dur !== undefined ) ? ( dur / 1000 ) + 's' : '0s'; + slider.container.css( '-' + slider.pfx + '-transition-duration', dur ); + } + + slider.args[slider.prop] = target; + if ( slider.transitions || dur === undefined ) { + slider.container.css( slider.args ); + } + }; + + slider.setup = function( type ) { + var sliderOffset; + + if ( type === 'init' ) { + slider.viewport = $( '
        ' ).css( { 'overflow': 'hidden', 'position': 'relative' } ).appendTo( slider ).append( slider.container ); + slider.cloneCount = 0; + slider.cloneOffset = 0; + } + slider.cloneCount = 2; + slider.cloneOffset = 1; + // Clear out old clones. + if ( type !== 'init' ) { + slider.container.find( '.clone' ).remove(); + } + + slider.container.append( slider.slides.first().clone().addClass( 'clone' ).attr( 'aria-hidden', 'true' ) ).prepend( slider.slides.last().clone().addClass( 'clone' ).attr( 'aria-hidden', 'true' ) ); + slider.newSlides = $( slider.vars.selector, slider ); + + sliderOffset = slider.currentSlide + slider.cloneOffset; + slider.container.width( ( slider.count + slider.cloneCount ) * 200 + '%' ); + slider.setProps( sliderOffset * slider.computedW, 'init' ); + setTimeout( function() { + slider.doMath(); + slider.newSlides.css( { 'width': slider.computedW, 'float': 'left', 'display': 'block' } ); + // SMOOTH HEIGHT + methods.smoothHeight(); + }, ( type === 'init' ) ? 100 : 0 ); + + slider.slides.removeClass( namespace + 'active-slide' ).eq( slider.currentSlide ).addClass( namespace + 'active-slide' ); + }; + + slider.doMath = function() { + var slide = slider.slides.first(); + + slider.w = ( slider.viewport===undefined ) ? slider.width() : slider.viewport.width(); + slider.h = slide.height(); + slider.boxPadding = slide.outerWidth() - slide.width(); + + slider.itemW = slider.w; + slider.pagingCount = slider.count; + slider.last = slider.count - 1; + slider.computedW = slider.itemW - slider.boxPadding; + }; + + slider.update = function( pos, action ) { + slider.doMath(); + + // Update currentSlide and slider.animatingTo if necessary. + if ( pos < slider.currentSlide ) { + slider.currentSlide += 1; + } else if ( pos <= slider.currentSlide && pos !== 0 ) { + slider.currentSlide -= 1; + } + slider.animatingTo = slider.currentSlide; + + // Update controlNav. + if ( action === 'add' || slider.pagingCount > slider.controlNav.length ) { + methods.controlNav.update( 'add' ); + } else if ( action === 'remove' || slider.pagingCount < slider.controlNav.length ) { + if ( slider.currentSlide > slider.last ) { + slider.currentSlide -= 1; + slider.animatingTo -= 1; + } + methods.controlNav.update( 'remove', slider.last ); + } + // Update directionNav. + methods.directionNav.update(); + }; + + // FeaturedSlider: initialize. + methods.init(); + }; + + // Default settings. + $.featuredslider.defaults = { + namespace: 'slider-', // String: prefix string attached to the class of every element generated by the plugin. + selector: '.slides > li', // String: selector, must match a simple pattern. + animationSpeed: 600, // Integer: Set the speed of animations, in milliseconds. + controlsContainer: '', // jQuery Object/Selector: container navigation to append elements. + + // Text labels. + prevText: featuredSliderDefaults.prevText, // String: Set the text for the "previous" directionNav item. + nextText: featuredSliderDefaults.nextText // String: Set the text for the "next" directionNav item. + }; + + // FeaturedSlider: plugin function. + $.fn.featuredslider = function( options ) { + if ( options === undefined ) { + options = {}; + } + + if ( typeof options === 'object' ) { + return this.each( function() { + var $this = $( this ), + selector = ( options.selector ) ? options.selector : '.slides > li', + $slides = $this.find( selector ); + + if ( $slides.length === 1 || $slides.length === 0 ) { + $slides.fadeIn( 400 ); + } else if ( $this.data( 'featuredslider' ) === undefined ) { + new $.featuredslider( this, options ); + } + } ); + } + }; +} )( jQuery ); diff --git a/wp-content/themes/twentyfourteen/languages/twentyfourteen.pot b/wp-content/themes/twentyfourteen/languages/twentyfourteen.pot new file mode 100644 index 0000000..634f3e2 --- /dev/null +++ b/wp-content/themes/twentyfourteen/languages/twentyfourteen.pot @@ -0,0 +1,476 @@ +# Copyright (C) 2014 the WordPress team +# This file is distributed under the GNU General Public License v2 or later. +msgid "" +msgstr "" +"Project-Id-Version: Twenty Fourteen 1.3\n" +"Report-Msgid-Bugs-To: http://wordpress.org/tags/twentyfourteen\n" +"POT-Creation-Date: 2014-12-18 16:59:51+00:00\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"PO-Revision-Date: 2014-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" + +#: 404.php:17 +msgid "Not Found" +msgstr "" + +#: 404.php:21 +msgid "It looks like nothing was found at this location. Maybe try a search?" +msgstr "" + +#: archive.php:31 +msgid "Daily Archives: %s" +msgstr "" + +#: archive.php:34 +msgid "Monthly Archives: %s" +msgstr "" + +#: archive.php:34 +msgctxt "monthly archives date format" +msgid "F Y" +msgstr "" + +#: archive.php:37 +msgid "Yearly Archives: %s" +msgstr "" + +#: archive.php:37 +msgctxt "yearly archives date format" +msgid "Y" +msgstr "" + +#: archive.php:40 taxonomy-post_format.php:51 +msgid "Archives" +msgstr "" + +#: author.php:31 +msgid "All posts by %s" +msgstr "" + +#: category.php:20 +msgid "Category Archives: %s" +msgstr "" + +#: comments.php:27 +msgid "One thought on “%2$s”" +msgid_plural "%1$s thoughts on “%2$s”" +msgstr[0] "" +msgstr[1] "" + +#: comments.php:34 comments.php:52 +msgid "Comment navigation" +msgstr "" + +#: comments.php:35 comments.php:53 +msgid "← Older Comments" +msgstr "" + +#: comments.php:36 comments.php:54 +msgid "Newer Comments →" +msgstr "" + +#: comments.php:59 +msgid "Comments are closed." +msgstr "" + +#: content-aside.php:17 content-audio.php:17 content-featured-post.php:28 +#: content-gallery.php:17 content-image.php:17 content-link.php:17 +#: content-quote.php:17 content-video.php:17 content.php:19 +msgctxt "Used between list items, there is a space after the comma." +msgid ", " +msgstr "" + +#: content-aside.php:37 content-audio.php:37 content-gallery.php:37 +#: content-image.php:37 content-link.php:37 content-quote.php:37 +#: content-video.php:37 content.php:38 inc/widgets.php:194 +msgid "Leave a comment" +msgstr "" + +#: content-aside.php:37 content-audio.php:37 content-gallery.php:37 +#: content-image.php:37 content-link.php:37 content-quote.php:37 +#: content-video.php:37 content.php:38 inc/widgets.php:194 +msgid "1 Comment" +msgstr "" + +#: content-aside.php:37 content-audio.php:37 content-gallery.php:37 +#: content-image.php:37 content-link.php:37 content-quote.php:37 +#: content-video.php:37 content.php:38 inc/widgets.php:194 +msgid "% Comments" +msgstr "" + +#: content-aside.php:40 content-audio.php:40 content-gallery.php:40 +#: content-image.php:40 content-link.php:40 content-page.php:28 +#: content-quote.php:40 content-video.php:40 content.php:42 image.php:34 +#: page-templates/contributors.php:35 +msgid "Edit" +msgstr "" + +#. translators: %s: Name of current post +#: content-aside.php:48 content-audio.php:48 content-gallery.php:48 +#: content-image.php:48 content-link.php:48 content-quote.php:48 +#: content-video.php:48 content.php:56 inc/template-tags.php:219 +msgid "Continue reading %s " +msgstr "" + +#: content-aside.php:53 content-audio.php:53 content-gallery.php:53 +#: content-image.php:53 content-link.php:53 content-page.php:22 +#: content-quote.php:53 content-video.php:53 content.php:61 image.php:54 +msgid "Pages:" +msgstr "" + +#: content-none.php:12 +msgid "Nothing Found" +msgstr "" + +#: content-none.php:18 +msgid "" +"Ready to publish your first post? Get started here." +msgstr "" + +#: content-none.php:22 +msgid "" +"Sorry, but nothing matched your search terms. Please try again with some " +"different keywords." +msgstr "" + +#: content-none.php:27 +msgid "" +"It seems we can’t find what you’re looking for. Perhaps " +"searching can help." +msgstr "" + +#. #-#-#-#-# twentyfourteen.pot (Twenty Fourteen 1.3) #-#-#-#-# +#. Author URI of the plugin/theme +#: footer.php:21 +msgid "http://wordpress.org/" +msgstr "" + +#: footer.php:21 +msgid "Proudly powered by %s" +msgstr "" + +#: functions.php:83 +msgid "Top primary menu" +msgstr "" + +#: functions.php:84 +msgid "Secondary menu in left sidebar" +msgstr "" + +#: functions.php:171 +msgid "Primary Sidebar" +msgstr "" + +#: functions.php:173 +msgid "Main sidebar that appears on the left." +msgstr "" + +#: functions.php:180 +msgid "Content Sidebar" +msgstr "" + +#: functions.php:182 +msgid "Additional sidebar that appears on the right." +msgstr "" + +#: functions.php:189 +msgid "Footer Widget Area" +msgstr "" + +#: functions.php:191 +msgid "Appears in the footer section of the site." +msgstr "" + +#: functions.php:213 +msgctxt "Lato font: on or off" +msgid "on" +msgstr "" + +#: functions.php:258 +msgid "Previous" +msgstr "" + +#: functions.php:259 +msgid "Next" +msgstr "" + +#: functions.php:376 +msgid "%d Article" +msgid_plural "%d Articles" +msgstr[0] "" +msgstr[1] "" + +#: functions.php:495 +msgid "Page %s" +msgstr "" + +#: header.php:48 +msgid "Search" +msgstr "" + +#: header.php:52 +msgid "Primary Menu" +msgstr "" + +#: header.php:53 +msgid "Skip to content" +msgstr "" + +#: image.php:65 +msgid "Previous Image" +msgstr "" + +#: image.php:66 +msgid "Next Image" +msgstr "" + +#: inc/back-compat.php:37 inc/back-compat.php:47 inc/back-compat.php:60 +msgid "" +"Twenty Fourteen requires at least WordPress version 3.6. You are running " +"version %s. Please upgrade and try again." +msgstr "" + +#: inc/customizer.php:24 +msgid "Site Title Color" +msgstr "" + +#: inc/customizer.php:27 +msgid "Display Site Title & Tagline" +msgstr "" + +#: inc/customizer.php:31 inc/customizer.php:32 +msgid "May only be visible on wide screens." +msgstr "" + +#: inc/customizer.php:34 inc/customizer.php:35 +msgid "Background may only be visible on wide screens." +msgstr "" + +#: inc/customizer.php:40 inc/featured-content.php:403 +msgid "Featured Content" +msgstr "" + +#: inc/customizer.php:41 inc/featured-content.php:404 +msgid "" +"Use a tag to feature your posts. If no posts match the " +"tag, sticky posts will be displayed instead." +msgstr "" + +#: inc/customizer.php:42 inc/customizer.php:108 inc/featured-content.php:405 +#: inc/featured-content.php:414 inc/featured-content.php:474 +msgctxt "featured content default tag slug" +msgid "featured" +msgstr "" + +#: inc/customizer.php:56 +msgid "Layout" +msgstr "" + +#: inc/customizer.php:60 +msgid "Grid" +msgstr "" + +#: inc/customizer.php:61 +msgid "Slider" +msgstr "" + +#. #-#-#-#-# twentyfourteen.pot (Twenty Fourteen 1.3) #-#-#-#-# +#. Theme Name of the plugin/theme +#: inc/customizer.php:105 +msgid "Twenty Fourteen" +msgstr "" + +#: inc/customizer.php:108 +msgid "" +"The home page features your choice of up to 6 posts prominently displayed in " +"a grid or slider, controlled by a tag; you can change " +"the tag and layout in Appearance → Customize. If " +"no posts match the tag, sticky posts will be displayed " +"instead." +msgstr "" + +#: inc/customizer.php:109 +msgid "" +"Enhance your site design by using Featured Images for " +"posts you’d like to stand out (also known as post thumbnails). This " +"allows you to associate an image with your post without inserting it. Twenty " +"Fourteen uses featured images for posts and pages—above the " +"title—and in the Featured Content area on the home page." +msgstr "" + +#: inc/customizer.php:110 +msgid "" +"For an in-depth tutorial, and more tips and tricks, visit the Twenty Fourteen documentation." +msgstr "" + +#: inc/featured-content.php:426 +msgid "Tag Name" +msgstr "" + +#: inc/featured-content.php:431 +msgid "Don’t display tag on front end." +msgstr "" + +#: inc/template-tags.php:50 +msgid "← Previous" +msgstr "" + +#: inc/template-tags.php:51 +msgid "Next →" +msgstr "" + +#: inc/template-tags.php:58 +msgid "Posts navigation" +msgstr "" + +#: inc/template-tags.php:85 +msgid "Post navigation" +msgstr "" + +#: inc/template-tags.php:89 +msgid "Published In%title" +msgstr "" + +#: inc/template-tags.php:91 +msgid "Previous Post%title" +msgstr "" + +#: inc/template-tags.php:92 +msgid "Next Post%title" +msgstr "" + +#: inc/template-tags.php:109 +msgid "Sticky" +msgstr "" + +#: inc/widgets.php:34 +msgid "Twenty Fourteen Ephemera" +msgstr "" + +#: inc/widgets.php:36 +msgid "" +"Use this widget to list your recent Aside, Quote, Video, Audio, Image, " +"Gallery, and Link posts." +msgstr "" + +#: inc/widgets.php:54 taxonomy-post_format.php:33 +msgid "Images" +msgstr "" + +#: inc/widgets.php:55 +msgid "More images" +msgstr "" + +#: inc/widgets.php:58 taxonomy-post_format.php:36 +msgid "Videos" +msgstr "" + +#: inc/widgets.php:59 +msgid "More videos" +msgstr "" + +#: inc/widgets.php:62 taxonomy-post_format.php:39 +msgid "Audio" +msgstr "" + +#: inc/widgets.php:63 +msgid "More audio" +msgstr "" + +#: inc/widgets.php:66 taxonomy-post_format.php:42 +msgid "Quotes" +msgstr "" + +#: inc/widgets.php:67 +msgid "More quotes" +msgstr "" + +#: inc/widgets.php:70 taxonomy-post_format.php:45 +msgid "Links" +msgstr "" + +#: inc/widgets.php:71 +msgid "More links" +msgstr "" + +#: inc/widgets.php:74 taxonomy-post_format.php:48 +msgid "Galleries" +msgstr "" + +#: inc/widgets.php:75 +msgid "More galleries" +msgstr "" + +#: inc/widgets.php:79 taxonomy-post_format.php:30 +msgid "Asides" +msgstr "" + +#: inc/widgets.php:80 +msgid "More asides" +msgstr "" + +#: inc/widgets.php:127 inc/widgets.php:172 +msgid "Continue reading " +msgstr "" + +#: inc/widgets.php:162 +msgid "This gallery contains %2$s photo." +msgid_plural "" +"This gallery contains %2$s photos." +msgstr[0] "" +msgstr[1] "" + +#. translators: used with More archives link +#: inc/widgets.php:206 +msgid "%s " +msgstr "" + +#: inc/widgets.php:255 +msgid "Title:" +msgstr "" + +#: inc/widgets.php:258 +msgid "Number of posts to show:" +msgstr "" + +#: inc/widgets.php:261 +msgid "Post format to show:" +msgstr "" + +#: search.php:18 +msgid "Search Results for: %s" +msgstr "" + +#: tag.php:22 +msgid "Tag Archives: %s" +msgstr "" + +#. Theme URI of the plugin/theme +msgid "http://wordpress.org/themes/twentyfourteen" +msgstr "" + +#. Description of the plugin/theme +msgid "" +"In 2014, our default theme lets you create a responsive magazine website " +"with a sleek, modern design. Feature your favorite homepage content in " +"either a grid or a slider. Use the three widget areas to customize your " +"website, and change your content's layout with a full-width page template " +"and a contributor page to show off your authors. Creating a magazine website " +"with WordPress has never been easier." +msgstr "" + +#. Author of the plugin/theme +msgid "the WordPress team" +msgstr "" + +#. Template Name of the plugin/theme +msgid "Contributor Page" +msgstr "" + +#. Template Name of the plugin/theme +msgid "Full Width Page" +msgstr "" diff --git a/wp-content/themes/twentyfourteen/page-templates/contributors.php b/wp-content/themes/twentyfourteen/page-templates/contributors.php new file mode 100644 index 0000000..92602ab --- /dev/null +++ b/wp-content/themes/twentyfourteen/page-templates/contributors.php @@ -0,0 +1,52 @@ + + +
        + + + +
        +
        + + +
        > +

        ', '

        ' ); + + // Output the authors list. + twentyfourteen_list_authors(); + + edit_post_link( __( 'Edit', 'twentyfourteen' ), '
        ', '
        ' ); + ?> +
        + + +
        +
        +
        + + + +
        + + + +
        +
        + +
        +
        +
        + + + +
        + + +
        +
        + + + +
        +
        + +
        + + ul, +li > ol { + margin: 0 20px 0 0; +} + +caption, +th, +td { + text-align: right; +} + + +/** + * 2.0 Repeatable Patterns + * ----------------------------------------------------------------------------- + */ + +.wp-caption-text { + padding-left: 10px; + padding-right: 0; +} + +.screen-reader-text:focus { + right: 5px; + left: auto; +} + + +/** + * 4.0 Header + * ----------------------------------------------------------------------------- + */ + +.site-title { + float: right; +} + +.search-toggle { + float: left; + margin-left: 38px; + margin-right: auto; +} + +.search-box .search-field { + float: left; + padding: 1px 6px 2px 2px; +} + +.search-toggle .screen-reader-text { + right: 5px; /* Avoid a horizontal scrollbar when the site has a long menu */ + left: auto; +} + + +/** + * 5.0 Navigation + * ----------------------------------------------------------------------------- + */ + +.site-navigation ul ul { + margin-right: 20px; + margin-left: auto; +} + +.menu-toggle { + right: auto; + left: 0; +} + + +/** + * 6.0 Content + * ----------------------------------------------------------------------------- + */ + +/** + * 6.3 Entry Meta + * ----------------------------------------------------------------------------- + */ + +.entry-meta .tag-links a { + margin: 0 10px 4px 4px; +} + +.entry-meta .tag-links a:before { + border-right: 0; + border-left: 8px solid #767676; + right: -7px; + left: auto; +} + +.entry-meta .tag-links a:hover:before, +.entry-meta .tag-links a:focus:before { + border-left-color: #41a62a; +} + +.entry-meta .tag-links a:after { + right: -2px; + left: auto; +} + + +/** + * 6.4 Entry Content + * ----------------------------------------------------------------------------- + */ + +.page-links a, +.page-links > span { + margin: 0 0 2px 1px; +} + +.page-links > .page-links-title { + padding-right: 0; + padding-left: 7px; +} + + +/** + * 6.5 Galleries + * ----------------------------------------------------------------------------- + */ + +.gallery-item { + float: right; + margin: 0 0 4px 4px; +} + +.gallery-columns-1 .gallery-item:nth-of-type(1n), +.gallery-columns-2 .gallery-item:nth-of-type(2n), +.gallery-columns-3 .gallery-item:nth-of-type(3n), +.gallery-columns-4 .gallery-item:nth-of-type(4n), +.gallery-columns-5 .gallery-item:nth-of-type(5n), +.gallery-columns-6 .gallery-item:nth-of-type(6n), +.gallery-columns-7 .gallery-item:nth-of-type(7n), +.gallery-columns-8 .gallery-item:nth-of-type(8n), +.gallery-columns-9 .gallery-item:nth-of-type(9n) { + margin-right: auto; + margin-left: 0; +} + +.gallery-caption { + padding: 6px 8px; + right: 0; + left: auto; + text-align: right; +} + +.gallery-caption:before { + right: 0; + left: auto; +} + + +/** + * 6.7 Post/Image/Paging Navigation + * ----------------------------------------------------------------------------- + */ + +.paging-navigation .page-numbers { + margin-right: auto; + margin-left: 1px; +} + + +/** + * 6.10 Contributor Page + * ----------------------------------------------------------------------------- + */ + +.contributor-avatar { + float: right; + margin: 0 0 20px 30px; +} + + +/** + * 6.14 Comments + * ----------------------------------------------------------------------------- + */ + +.comment-author .avatar { + right: 0; + left: auto; +} + +.bypostauthor > article .fn:before { + margin: 0 -2px 0 2px; +} + +.comment-author, +.comment-awaiting-moderation, +.comment-content, +.comment-list .reply, +.comment-metadata { + padding-right: 30px; + padding-left: 0; +} + +.comment-edit-link { + margin-right: 10px; + margin-left: auto; +} + +.comment-reply-link:before, +.comment-reply-login:before { + margin-left: auto; + margin-right: 2px; +} + +.comment-reply-link:before, +.comment-reply-login:before, +.comment-edit-link:before { + -webkit-transform: scaleX(-1); + -moz-transform: scaleX(-1); + -ms-transform: scaleX(-1); + -o-transform: scaleX(-1); + transform: scaleX(-1); +} + +.comment-content ul, +.comment-content ol { + margin: 0 22px 24px 0; +} + +.comment-list .children { + margin-right: 15px; + margin-left: auto; +} + +.comment-reply-title small a { + float: left; +} + +.comment-navigation .nav-previous a { + margin-right: auto; + margin-left: 10px; +} + + +/** + * 7.0 Sidebars + * ----------------------------------------------------------------------------- + */ + +/** + * 7.1 Widgets + * ----------------------------------------------------------------------------- + */ + +.widget li > ol, +.widget li > ul { + margin-right: 10px; + margin-left: auto; +} + +.widget input, +.widget textarea { + padding: 1px 4px 2px 2px; +} + +.widget_calendar caption { + text-align: right; +} + +.widget_calendar #prev { + padding-right: 5px; + padding-left: 0; +} + +.widget_calendar #next { + padding-right: 0; + padding-left: 5px; + text-align: left; +} + +.widget_twentyfourteen_ephemera .entry-content ul, +.widget_twentyfourteen_ephemera .entry-content ol { + margin: 0 20px 18px 0; +} + +.widget_twentyfourteen_ephemera .entry-content li > ul, +.widget_twentyfourteen_ephemera .entry-content li > ol { + margin: 0 20px 0 0; +} + + +/** + * 7.2 Content Sidebar Widgets + * ----------------------------------------------------------------------------- + */ + +.content-sidebar .widget li > ol, +.content-sidebar .widget li > ul { + margin-right: 18px; + margin-left: auto; +} + +.content-sidebar .widget_twentyfourteen_ephemera .widget-title:before { + margin: -1px 0 0 18px; +} + + +/** + * 9.0 Featured Content + * ----------------------------------------------------------------------------- + */ + +.featured-content .post-thumbnail img { + right: 0; + left: auto; +} + +.slider-viewport { + direction: ltr; +} + +.slider .featured-content .entry-header { + right: 0; + left: auto; + text-align: right; +} + +.slider-control-paging { + float: right; +} + +.slider-control-paging li { + float: right; + margin: 2px 0 2px 4px; +} + +.slider-control-paging li:last-child { + margin-right: auto; + margin-left: 0; +} + +.slider-control-paging a:before { + right: 10px; + left: auto; +} + +.slider-direction-nav li { + border-width: 2px 0 0 1px; + float: right; +} + +.slider-direction-nav li:last-child { + border-width: 2px 1px 0 0; +} + +.slider-direction-nav a:before { + content: "\f429"; +} + +.slider-direction-nav .slider-next:before { + content: "\f430"; +} + + +/** + * 10.0 Media Queries + * ----------------------------------------------------------------------------- + */ + +@media screen and (max-width: 400px) { + .list-view .site-content .post-thumbnail img { + float: right; + margin: 0 0 3px 10px; + } +} + +@media screen and (min-width: 401px) { + .site-content .entry-meta > span { + margin-right: auto; + margin-left: 10px; + } + + .site-content .format-quote .post-format a:before { + margin-right: auto; + margin-left: 2px; + } + + .site-content .format-gallery .post-format a:before { + margin-right: auto; + margin-left: 4px; + } + + .site-content .format-aside .post-format a:before { + margin-right: auto; + margin-left: 2px; + } + + .site-content .featured-post:before { + margin-right: auto; + margin-left: 3px; + } + + .site-content .entry-date a:before, + .attachment .site-content span.entry-date:before { + margin-right: auto; + margin-left: 1px; + } + + .site-content .comments-link a:before { + margin-right: auto; + margin-left: 2px; + } + + .site-content .full-size-link a:before { + margin-right: auto; + margin-left: 1px; + } + + .entry-content .edit-link a:before, + .entry-meta .edit-link a:before { + -webkit-transform: scaleX(-1); + -moz-transform: scaleX(-1); + -ms-transform: scaleX(-1); + -o-transform: scaleX(-1); + transform: scaleX(-1); + } +} + +@media screen and (min-width: 594px) { + .site-content .entry-header { + padding-right: 30px; + padding-left: 30px; + } +} + +@media screen and (min-width: 673px) { + .search-toggle { + margin-right: auto; + margin-left: 18px; + } + + .content-area { + float: right; + } + + .site-content { + margin-right: auto; + margin-left: 33.33333333%; + } + + .archive-header, + .comments-area, + .image-navigation, + .page-header, + .page-content, + .post-navigation, + .site-content .entry-content, + .site-content .entry-summary, + .site-content footer.entry-meta { + padding-right: 30px; + padding-left: 30px; + } + + .full-width .site-content { + margin-left: 0; + } + + .content-sidebar { + float: left; + margin-right: -33.33333333%; + margin-left: auto; + } + + .grid .featured-content .hentry { + float: right; + } + + .slider-control-paging { + padding-right: 20px; + padding-left: 0; + } + + .slider-direction-nav { + float: left; + } + + .slider-direction-nav li { + padding: 0 0 0 1px; + } + + .slider-direction-nav li:last-child { + padding: 0 1px 0 0; + } +} + +@media screen and (min-width: 783px) { + .header-main { + padding-right: 30px; + padding-left: 0; + } + + .search-toggle { + margin-right: auto; + margin-left: 0; + } + + .primary-navigation { + float: left; + margin: 0 -12px 0 1px; + } + + .primary-navigation ul ul { + float: right; + margin: 0; + right: -999em; + left: auto; + } + + .primary-navigation ul ul ul { + right: -999em; + left: auto; + } + + .primary-navigation ul li:hover > ul, + .primary-navigation ul li.focus > ul { + right: auto; + } + + .primary-navigation ul ul li:hover > ul, + .primary-navigation ul ul li.focus > ul { + right: 100%; + left: auto; + } + + .primary-navigation .menu-item-has-children > a, + .primary-navigation .page_item_has_children > a { + padding-right: 12px; + padding-left: 26px; + } + + .primary-navigation .menu-item-has-children > a:after, + .primary-navigation .page_item_has_children > a:after { + right: auto; + left: 12px; + } + + .primary-navigation li .menu-item-has-children > a, + .primary-navigation li .page_item_has_children > a { + padding-right: 12px; + padding-left: 20px; + } + + .primary-navigation .menu-item-has-children li.menu-item-has-children > a:after, + .primary-navigation .menu-item-has-children li.page_item_has_children > a:after, + .primary-navigation .page_item_has_children li.menu-item-has-children > a:after, + .primary-navigation .page_item_has_children li.page_item_has_children > a:after { + content: "\f503"; + right: auto; + left: 8px; + } +} + +@media screen and (min-width: 810px) { + .attachment .entry-attachment .attachment { + margin-right: -168px; + margin-left: -168px; + } + + .attachment .entry-attachment .attachment a { + display: block; + } + + .contributor-avatar { + margin-right: -168px; + margin-left: auto; + } + + .contributor-summary { + float: right; + } + + .full-width .site-content blockquote.alignright, + .full-width .site-content img.size-full.alignright, + .full-width .site-content img.size-large.alignright, + .full-width .site-content img.size-medium.alignright, + .full-width .site-content .wp-caption.alignright { + margin-right: -168px; + margin-left: auto; + } + + .full-width .site-content blockquote.alignleft, + .full-width .site-content img.size-full.alignleft, + .full-width .site-content img.size-large.alignleft, + .full-width .site-content img.size-medium.alignleft, + .full-width .site-content .wp-caption.alignleft { + margin-right: auto; + margin-left: -168px; + } +} + +@media screen and (min-width: 846px) { + .comment-author, + .comment-awaiting-moderation, + .comment-content, + .comment-list .reply, + .comment-metadata { + padding-right: 50px; + padding-left: 0; + } + + .comment-list .children { + margin-right: 20px; + margin-left: auto; + } +} + +@media screen and (min-width: 1008px) { + .search-box-wrapper { + padding-right: 182px; + padding-left: 0; + } + + .main-content { + float: right; + } + + .site-content { + margin-right: 182px; + margin-left: 29.04761904%; + } + + .full-width .site-content { + margin-right: 182px; + } + + .content-sidebar { + margin-right: -29.04761904%; + margin-left: auto; + } + + .site:before { + right: 0; + left: auto; + } + + #secondary { + float: right; + margin: 0 -100% 0 0; + } + + .secondary-navigation ul ul { + right: -999em; + left: auto; + } + + .secondary-navigation ul li:hover > ul, + .secondary-navigation ul li.focus > ul { + right: 162px; + left: auto; + } + + .secondary-navigation .menu-item-has-children > a { + padding-right: 30px; + padding-left: 38px; + } + + .secondary-navigation .menu-item-has-children > a:after { + border-right-color: #fff; + border-left-color: transparent; + right: auto; + left: 26px; + content: "\f503"; + } + + .footer-sidebar .widget { + float: right; + } + + .featured-content { + padding-right: 182px; + padding-left: 0; + } +} + +@media screen and (min-width: 1040px) { + .archive-header, + .comments-area, + .image-navigation, + .page-header, + .page-content, + .post-navigation, + .site-content .entry-header, + .site-content .entry-content, + .site-content .entry-summary, + .site-content footer.entry-meta { + padding-right: 15px; + padding-left: 15px; + } + + .full-width .archive-header, + .full-width .comments-area, + .full-width .image-navigation, + .full-width .page-header, + .full-width .page-content, + .full-width .post-navigation, + .full-width .site-content .entry-header, + .full-width .site-content .entry-content, + .full-width .site-content .entry-summary, + .full-width .site-content footer.entry-meta { + padding-right: 30px; + padding-left: 30px; + } +} + +@media screen and (min-width: 1080px) { + .site-content { + margin-right: 222px; + margin-left: 29.04761904%; + } + + .full-width .site-content { + margin-right: 222px; + } + + .search-box-wrapper, + .featured-content { + padding-right: 222px; + padding-left: 0; + } + + .secondary-navigation ul li:hover > ul, + .secondary-navigation ul li.focus > ul { + right: 202px; + left: auto; + } + + .slider-control-paging { + padding-right: 24px; + padding-left: 0; + } + + .slider-control-paging li { + margin: 12px 0 12px 12px; + } + + .slider-control-paging a:before { + right: 6px; + left: auto; + } +} + +@media screen and (min-width: 1110px) { + .archive-header, + .comments-area, + .image-navigation, + .page-header, + .page-content, + .post-navigation, + .site-content .entry-header, + .site-content .entry-content, + .site-content .entry-summary, + .site-content footer.entry-meta { + padding-right: 30px; + padding-left: 30px; + } +} + +@media screen and (min-width: 1218px) { + .archive-header, + .comments-area, + .image-navigation, + .page-header, + .page-content, + .post-navigation, + .site-content .entry-header, + .site-content .entry-content, + .site-content .entry-summary, + .site-content footer.entry-meta { + margin-left: 54px; + } + + .full-width .archive-header, + .full-width .comments-area, + .full-width .image-navigation, + .full-width .page-header, + .full-width .page-content, + .full-width .post-navigation, + .full-width .site-content .entry-header, + .full-width .site-content .entry-content, + .full-width .site-content .entry-summary, + .full-width .site-content footer.entry-meta { + margin-right: auto; + margin-left: auto; + } +} + +@media screen and (min-width: 1260px) { + .site-content blockquote.alignright { + margin-right: -18%; + margin-left: auto; + } + + .site-content blockquote.alignleft { + margin-left: -18%; + margin-right: auto; + } +} \ No newline at end of file diff --git a/wp-content/themes/twentyfourteen/screenshot.png b/wp-content/themes/twentyfourteen/screenshot.png new file mode 100644 index 0000000..0273112 Binary files /dev/null and b/wp-content/themes/twentyfourteen/screenshot.png differ diff --git a/wp-content/themes/twentyfourteen/search.php b/wp-content/themes/twentyfourteen/search.php new file mode 100644 index 0000000..3fe9bdb --- /dev/null +++ b/wp-content/themes/twentyfourteen/search.php @@ -0,0 +1,49 @@ + + +
        +
        + + + + + + + +
        +
        + + + diff --git a/wp-content/themes/twentyfourteen/sidebar-footer.php b/wp-content/themes/twentyfourteen/sidebar-footer.php new file mode 100644 index 0000000..20f3798 --- /dev/null +++ b/wp-content/themes/twentyfourteen/sidebar-footer.php @@ -0,0 +1,19 @@ + + +
        + +
        diff --git a/wp-content/themes/twentyfourteen/sidebar.php b/wp-content/themes/twentyfourteen/sidebar.php new file mode 100644 index 0000000..be3c8e0 --- /dev/null +++ b/wp-content/themes/twentyfourteen/sidebar.php @@ -0,0 +1,29 @@ + +
        + +

        + + + + + + + + + +
        diff --git a/wp-content/themes/twentyfourteen/single.php b/wp-content/themes/twentyfourteen/single.php new file mode 100644 index 0000000..e2db3b0 --- /dev/null +++ b/wp-content/themes/twentyfourteen/single.php @@ -0,0 +1,40 @@ + + +
        +
        + +
        +
        + + ul, +li > ol { + margin: 0 0 0 20px; +} + +img { + -ms-interpolation-mode: bicubic; + border: 0; + vertical-align: middle; +} + +figure { + margin: 0; +} + +fieldset { + border: 1px solid rgba(0, 0, 0, 0.1); + margin: 0 0 24px; + padding: 11px 12px 0; +} + +legend { + white-space: normal; +} + +button, +input, +select, +textarea { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + font-size: 100%; + margin: 0; + max-width: 100%; + vertical-align: baseline; +} + +button, +input { + line-height: normal; +} + +input, +textarea { + background-image: -webkit-linear-gradient(hsla(0,0%,100%,0), hsla(0,0%,100%,0)); /* Removing the inner shadow, rounded corners on iOS inputs */ +} + +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; + cursor: pointer; +} + +button[disabled], +input[disabled] { + cursor: default; +} + +input[type="checkbox"], +input[type="radio"] { + padding: 0; +} + +input[type="search"] { + -webkit-appearance: textfield; +} + +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} + +textarea { + overflow: auto; + vertical-align: top; +} + +table, +th, +td { + border: 1px solid rgba(0, 0, 0, 0.1); +} + +table { + border-collapse: separate; + border-spacing: 0; + border-width: 1px 0 0 1px; + margin-bottom: 24px; + width: 100%; +} + +caption, +th, +td { + font-weight: normal; + text-align: left; +} + +th { + border-width: 0 1px 1px 0; + font-weight: bold; +} + +td { + border-width: 0 1px 1px 0; +} + +del { + color: #767676; +} + +hr { + background-color: rgba(0, 0, 0, 0.1); + border: 0; + height: 1px; + margin-bottom: 23px; +} + +/* Support a widely-adopted but non-standard selector for text selection styles + * to achieve a better experience. See https://core.trac.wordpress.org/ticket/25898. + */ +::selection { + background: #24890d; + color: #fff; + text-shadow: none; +} + +::-moz-selection { + background: #24890d; + color: #fff; + text-shadow: none; +} + + +/** + * 2.0 Repeatable Patterns + * ----------------------------------------------------------------------------- + */ + +/* Input fields */ + +input, +textarea { + border: 1px solid rgba(0, 0, 0, 0.1); + border-radius: 2px; + color: #2b2b2b; + padding: 8px 10px 7px; +} + +textarea { + width: 100%; +} + +input:focus, +textarea:focus { + border: 1px solid rgba(0, 0, 0, 0.3); + outline: 0; +} + +/* Buttons */ + +button, +.button, +input[type="button"], +input[type="reset"], +input[type="submit"] { + background-color: #24890d; + border: 0; + border-radius: 2px; + color: #fff; + font-size: 12px; + font-weight: 700; + padding: 10px 30px 11px; + text-transform: uppercase; + vertical-align: bottom; +} + +button:hover, +button:focus, +.button:hover, +.button:focus, +input[type="button"]:hover, +input[type="button"]:focus, +input[type="reset"]:hover, +input[type="reset"]:focus, +input[type="submit"]:hover, +input[type="submit"]:focus { + background-color: #41a62a; + color: #fff; +} + +button:active, +.button:active, +input[type="button"]:active, +input[type="reset"]:active, +input[type="submit"]:active { + background-color: #55d737; +} + +.search-field { + width: 100%; +} + +.search-submit { + display: none; +} + +/* Placeholder text color -- selectors need to be separate to work. */ + +::-webkit-input-placeholder { + color: #939393; +} + +:-moz-placeholder { + color: #939393; +} + +::-moz-placeholder { + color: #939393; + opacity: 1; /* Since FF19 lowers the opacity of the placeholder by default */ +} + +:-ms-input-placeholder { + color: #939393; +} + +/* Responsive images. Fluid images for posts, comments, and widgets */ + +.comment-content img, +.entry-content img, +.entry-summary img, +#site-header img, +.widget img, +.wp-caption { + max-width: 100%; +} + +/** + * Make sure images with WordPress-added height and width attributes are + * scaled correctly. + */ + +.comment-content img[height], +.entry-content img, +.entry-summary img, +img[class*="align"], +img[class*="wp-image-"], +img[class*="attachment-"], +#site-header img { + height: auto; +} + +img.size-full, +img.size-large, +.wp-post-image, +.post-thumbnail img { + height: auto; + max-width: 100%; +} + +/* Make sure embeds and iframes fit their containers */ + +embed, +iframe, +object, +video { + margin-bottom: 24px; + max-width: 100%; +} + +p > embed, +p > iframe, +p > object, +span > embed, +span > iframe, +span > object { + margin-bottom: 0; +} + +/* Alignment */ + +.alignleft { + float: left; +} + +.alignright { + float: right; +} + +.aligncenter { + display: block; + margin-left: auto; + margin-right: auto; +} + +blockquote.alignleft, +figure.wp-caption.alignleft, +img.alignleft { + margin: 7px 24px 7px 0; +} + +.wp-caption.alignleft { + margin: 7px 14px 7px 0; +} + +blockquote.alignright, +figure.wp-caption.alignright, +img.alignright { + margin: 7px 0 7px 24px; +} + +.wp-caption.alignright { + margin: 7px 0 7px 14px; +} + +blockquote.aligncenter, +img.aligncenter, +.wp-caption.aligncenter { + margin-top: 7px; + margin-bottom: 7px; +} + +.site-content blockquote.alignleft, +.site-content blockquote.alignright { + border-top: 1px solid rgba(0, 0, 0, 0.1); + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + padding-top: 17px; + width: 50%; +} + +.site-content blockquote.alignleft p, +.site-content blockquote.alignright p { + margin-bottom: 17px; +} + +.wp-caption { + margin-bottom: 24px; +} + +.wp-caption img[class*="wp-image-"] { + display: block; + margin: 0; +} + +.wp-caption { + color: #767676; +} + +.wp-caption-text { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + font-size: 12px; + font-style: italic; + line-height: 1.5; + margin: 9px 0; +} + +div.wp-caption .wp-caption-text { + padding-right: 10px; +} + +div.wp-caption.alignright img[class*="wp-image-"], +div.wp-caption.alignright .wp-caption-text { + padding-left: 10px; + padding-right: 0; +} + +.wp-smiley { + border: 0; + margin-bottom: 0; + margin-top: 0; + padding: 0; +} + +/* Assistive text */ + +.screen-reader-text { + clip: rect(1px, 1px, 1px, 1px); + position: absolute; +} + +.screen-reader-text:focus { + background-color: #f1f1f1; + border-radius: 3px; + box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.6); + clip: auto; + color: #21759b; + display: block; + font-size: 14px; + font-weight: bold; + height: auto; + line-height: normal; + padding: 15px 23px 14px; + position: absolute; + left: 5px; + top: 5px; + text-decoration: none; + text-transform: none; + width: auto; + z-index: 100000; /* Above WP toolbar */ +} + +.hide { + display: none; +} + +/* Clearing floats */ + +.footer-sidebar:before, +.footer-sidebar:after, +.hentry:before, +.hentry:after, +.gallery:before, +.gallery:after, +.slider-direction-nav:before, +.slider-direction-nav:after, +.contributor-info:before, +.contributor-info:after, +.search-box:before, +.search-box:after, +[class*="content"]:before, +[class*="content"]:after, +[class*="site"]:before, +[class*="site"]:after { + content: ""; + display: table; +} + +.footer-sidebar:after, +.hentry:after, +.gallery:after, +.slider-direction-nav:after, +.contributor-info:after, +.search-box:after, +[class*="content"]:after, +[class*="site"]:after { + clear: both; +} + +/* Genericons */ + +.bypostauthor > article .fn:before, +.comment-edit-link:before, +.comment-reply-link:before, +.comment-reply-login:before, +.comment-reply-title small a:before, +.contributor-posts-link:before, +.menu-toggle:before, +.search-toggle:before, +.slider-direction-nav a:before, +.widget_twentyfourteen_ephemera .widget-title:before { + -webkit-font-smoothing: antialiased; + display: inline-block; + font: normal 16px/1 Genericons; + text-decoration: inherit; + vertical-align: text-bottom; +} + +/* Separators */ + +.site-content span + .entry-date:before, +.full-size-link:before, +.parent-post-link:before, +span + .byline:before, +span + .comments-link:before, +span + .edit-link:before, +.widget_twentyfourteen_ephemera .entry-title:after { + content: "\0020\007c\0020"; +} + + +/** + * 3.0 Basic Structure + * ----------------------------------------------------------------------------- + */ + +.site { + background-color: #fff; + max-width: 1260px; + position: relative; +} + +.main-content { + width: 100%; +} + + +/** + * 4.0 Header + * ----------------------------------------------------------------------------- + */ + +/* Ensure that there is no gap between the header and + the admin bar for WordPress versions before 3.8. */ +#wpadminbar { + min-height: 32px; +} + +#site-header { + position: relative; + z-index: 3; +} + +.site-header { + background-color: #000; + max-width: 1260px; + position: relative; + width: 100%; + z-index: 4; +} + +.header-main { + min-height: 48px; + padding: 0 10px; +} + +.site-title { + float: left; + font-size: 18px; + font-weight: 700; + line-height: 48px; + margin: 0; + + /* Nav-toggle width + search-toggle width - gutter = 86px */ + max-width: -webkit-calc(100% - 86px); + max-width: calc(100% - 86px); +} + +.site-title a, +.site-title a:hover { + color: #fff; + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +/* Search in the header */ + +.search-toggle { + background-color: #24890d; + cursor: pointer; + float: right; + height: 48px; + margin-right: 38px; + text-align: center; + width: 48px; +} + +.search-toggle:hover, +.search-toggle.active { + background-color: #41a62a; +} + +.search-toggle:before { + color: #fff; + content: "\f400"; + font-size: 20px; + margin-top: 14px; +} + +.search-toggle .screen-reader-text { + left: 5px; /* Avoid a horizontal scrollbar when the site has a long menu */ +} + +.search-box-wrapper { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + position: absolute; + top: 48px; + right: 0; + width: 100%; + z-index: 2; +} + +.search-box { + background-color: #41a62a; + padding: 12px; +} + +.search-box .search-field { + background-color: #fff; + border: 0; + float: right; + font-size: 16px; + padding: 2px 2px 3px 6px; + width: 100%; +} + + +/** + * 5.0 Navigation + * ----------------------------------------------------------------------------- + */ + +.site-navigation ul { + list-style: none; + margin: 0; +} + +.site-navigation li { + border-top: 1px solid rgba(255, 255, 255, 0.2); +} + +.site-navigation ul ul { + margin-left: 20px; +} + +.site-navigation a { + color: #fff; + display: block; + text-transform: uppercase; +} + +.site-navigation a:hover { + color: #41a62a; +} + +.site-navigation .current_page_item > a, +.site-navigation .current_page_ancestor > a, +.site-navigation .current-menu-item > a, +.site-navigation .current-menu-ancestor > a { + color: #55d737; + font-weight: 900; +} + +/* Primary Navigation */ + +.primary-navigation { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + font-size: 14px; + padding-top: 24px; +} + +.primary-navigation.toggled-on { + padding: 72px 0 36px; +} + +.primary-navigation .nav-menu { + border-bottom: 1px solid rgba(255, 255, 255, 0.2); + display: none; +} + +.primary-navigation.toggled-on .nav-menu { + display: block; +} + +.primary-navigation a { + padding: 7px 0; +} + +/* Secondary Navigation */ + +.secondary-navigation { + border-bottom: 1px solid rgba(255, 255, 255, 0.2); + font-size: 12px; + margin: 48px 0; +} + +.secondary-navigation a { + padding: 9px 0; +} + +.menu-toggle { + background-color: #000; + border-radius: 0; + cursor: pointer; + height: 48px; + margin: 0; + overflow: hidden; + padding: 0; + position: absolute; + top: 0; + right: 0; + text-align: center; + width: 48px; +} + +.menu-toggle:before { + color: #fff; + content: "\f419"; + padding: 16px; +} + +.menu-toggle:active, +.menu-toggle:focus, +.menu-toggle:hover { + background-color: #444; +} + +.menu-toggle:focus { + outline: 1px dotted; +} + + +/** + * 6.0 Content + * ----------------------------------------------------------------------------- + */ + +.content-area { + padding-top: 48px; +} + +.hentry { + margin: 0 auto 48px; + max-width: 672px; +} + +.site-content .entry-header, +.site-content .entry-content, +.site-content .entry-summary, +.site-content .entry-meta, +.page-content { + margin: 0 auto; + max-width: 474px; +} + +.page-content { + margin-bottom: 48px; +} + + +/** + * 6.1 Post Thumbnail + * ----------------------------------------------------------------------------- + */ + +.post-thumbnail { + background: #b2b2b2 url(images/pattern-light.svg) repeat fixed; + display: block; + position: relative; + width: 100%; + z-index: 0; +} + +a.post-thumbnail:hover { + background-color: #999; +} + +.full-width .post-thumbnail img { + display: block; + margin: 0 auto; +} + + +/** + * 6.2 Entry Header + * ----------------------------------------------------------------------------- + */ + +.entry-header { + position: relative; + z-index: 1; +} + +.entry-title { + font-size: 33px; + font-weight: 300; + line-height: 1.0909090909; + margin-bottom: 12px; + margin: 0 0 12px 0; + text-transform: uppercase; +} + +.entry-title a { + color: #2b2b2b; +} + +.entry-title a:hover { + color: #41a62a; +} + +.site-content .entry-header { + background-color: #fff; + padding: 0 10px 12px; +} + +.site-content .has-post-thumbnail .entry-header { + padding-top: 24px; +} + + +/** + * 6.3 Entry Meta + * ----------------------------------------------------------------------------- + */ + +.entry-meta { + clear: both; + color: #767676; + font-size: 12px; + font-weight: 400; + line-height: 1.3333333333; + text-transform: uppercase; +} + +.entry-meta a { + color: #767676; +} + +.entry-meta a:hover { + color: #41a62a; +} + +.sticky .entry-date { + display: none; +} + +.cat-links { + font-weight: 900; + text-transform: uppercase; +} + +.cat-links a { + color: #2b2b2b; +} + +.cat-links a:hover { + color: #41a62a; +} + +.byline { + display: none; +} + +.single .byline, +.group-blog .byline { + display: inline; +} + +.site-content .entry-meta { + background-color: #fff; + margin-bottom: 8px; +} + +.site-content footer.entry-meta { + margin: 24px auto 0; + padding: 0 10px; +} + +/* Tag links style */ + +.entry-meta .tag-links a { + background-color: #767676; + border-radius: 0 2px 2px 0; + color: #fff; + display: inline-block; + font-size: 11px; + font-weight: 700; + line-height: 1.2727272727; + margin: 2px 4px 2px 10px; + padding: 3px 7px; + position: relative; + text-transform: uppercase; +} + +.entry-meta .tag-links a:hover { + background-color: #41a62a; + color: #fff; +} + +.entry-meta .tag-links a:before { + border-top: 10px solid transparent; + border-right: 8px solid #767676; + border-bottom: 10px solid transparent; + content: ""; + height: 0; + position: absolute; + top: 0; + left: -8px; + width: 0; +} + +.entry-meta .tag-links a:hover:before { + border-right-color: #41a62a; +} + +.entry-meta .tag-links a:after { + background-color: #fff; + border-radius: 50%; + content: ""; + height: 4px; + position: absolute; + top: 8px; + left: -2px; + width: 4px; +} + + +/** + * 6.4 Entry Content + * ----------------------------------------------------------------------------- + */ + +.entry-content, +.entry-summary, +.page-content { + -webkit-hyphens: auto; + -moz-hyphens: auto; + -ms-hyphens: auto; + hyphens: auto; + word-wrap: break-word; +} + +.site-content .entry-content, +.site-content .entry-summary, +.page-content { + background-color: #fff; + padding: 12px 10px 0; +} + +.page .entry-content { + padding-top: 0; +} + +.entry-content h1:first-child, +.entry-content h2:first-child, +.entry-content h3:first-child, +.entry-content h4:first-child, +.entry-content h5:first-child, +.entry-content h6:first-child, +.entry-summary h1:first-child, +.entry-summary h2:first-child, +.entry-summary h3:first-child, +.entry-summary h4:first-child, +.entry-summary h5:first-child, +.entry-summary h6:first-child, +.page-content h1:first-child, +.page-content h2:first-child, +.page-content h3:first-child, +.page-content h4:first-child, +.page-content h5:first-child, +.page-content h6:first-child { + margin-top: 0; +} + +.entry-content a, +.entry-summary a, +.page-content a, +.comment-content a { + text-decoration: underline; +} + +.entry-content a:hover, +.entry-summary a:hover, +.page-content a:hover, +.comment-content a:hover, +.entry-content a.button, +.entry-summary a.button, +.page-content a.button, +.comment-content a.button { + text-decoration: none; +} + +.entry-content table, +.comment-content table { + font-size: 14px; + line-height: 1.2857142857; + margin-bottom: 24px; +} + +.entry-content th, +.comment-content th { + font-weight: 700; + padding: 8px; + text-transform: uppercase; +} + +.entry-content td, +.comment-content td { + padding: 8px; +} + +.entry-content .edit-link { + clear: both; + display: block; + font-size: 12px; + font-weight: 400; + line-height: 1.3333333333; + text-transform: uppercase; +} + +.entry-content .edit-link a { + color: #767676; + text-decoration: none; +} + +.entry-content .edit-link a:hover { + color: #41a62a; +} + +.entry-content .more-link { + white-space: nowrap; +} + +/* Mediaelements */ + +.hentry .mejs-container { + margin: 12px 0 18px; +} + +.hentry .mejs-mediaelement, +.hentry .mejs-container .mejs-controls { + background: #000; +} + +.hentry .mejs-controls .mejs-time-rail .mejs-time-loaded, +.hentry .mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-current { + background: #fff; +} + +.hentry .mejs-controls .mejs-time-rail .mejs-time-current { + background: #24890d; +} + +.hentry .mejs-controls .mejs-time-rail .mejs-time-total, +.hentry .mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-total { + background: rgba(255, 255, 255, .33); +} + +.hentry .mejs-container .mejs-controls .mejs-time { + padding-top: 9px; +} + +.hentry .mejs-controls .mejs-time-rail span, +.hentry .mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-total, +.hentry .mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-current { + border-radius: 0; +} + +.hentry .mejs-overlay-loading { + background: transparent; +} + +.hentry .mejs-overlay-button { + background-color: #fff; + background-image: none; + border-radius: 2px; + box-shadow: 1px 1px 1px rgba(0,0,0,.8); + color: #000; + height: 36px; + margin-left: -24px; + width: 48px; +} + +.hentry .mejs-overlay-button:before { + -webkit-font-smoothing: antialiased; + content: '\f452'; + display: inline-block; + font: normal 32px/1.125 Genericons; + position: absolute; + top: 1px; + left: 10px; +} + +.hentry .mejs-controls .mejs-button button:focus { + outline: none; +} + +.hentry .mejs-controls .mejs-button button { + -webkit-font-smoothing: antialiased; + background: none; + color: #fff; + display: inline-block; + font: normal 16px/1 Genericons; +} + +.hentry .mejs-playpause-button.mejs-play button:before { + content: '\f452'; +} + +.hentry .mejs-playpause-button.mejs-pause button:before { + content: '\f448'; +} + +.hentry .mejs-volume-button.mejs-mute button:before { + content: '\f109'; + font-size: 20px; + position: absolute; + top: -2px; + left: 0; +} + +.hentry .mejs-volume-button.mejs-unmute button:before { + content: '\f109'; + left: 0; + position: absolute; + top: 0; +} + +.hentry .mejs-fullscreen-button button:before { + content: '\f474'; +} + +.hentry .mejs-fullscreen-button.mejs-unfullscreen button:before { + content: '\f406'; +} + +.hentry .mejs-overlay:hover .mejs-overlay-button { + background-color: #24890d; + color: #fff; +} + +.hentry .mejs-controls .mejs-button button:hover { + color: #41a62a; +} + +.content-sidebar .wp-playlist-item .wp-playlist-caption { + color: #000; +} + +/* Page links */ + +.page-links { + clear: both; + font-size: 12px; + font-weight: 900; + line-height: 2; + margin: 24px 0; + text-transform: uppercase; +} + +.page-links a, +.page-links > span { + background: #fff; + border: 1px solid #fff; + display: inline-block; + height: 22px; + margin: 0 1px 2px 0; + text-align: center; + width: 22px; +} + +.page-links a { + background: #000; + border: 1px solid #000; + color: #fff; + text-decoration: none; +} + +.page-links a:hover { + background: #41a62a; + border: 1px solid #41a62a; + color: #fff; +} + +.page-links > .page-links-title { + height: auto; + margin: 0; + padding-right: 7px; + width: auto; +} + + +/** + * 6.5 Gallery + * ----------------------------------------------------------------------------- + */ + +.gallery { + margin-bottom: 20px; +} + +.gallery-item { + float: left; + margin: 0 4px 4px 0; + overflow: hidden; + position: relative; +} + +.gallery-columns-1 .gallery-item { + max-width: 100%; +} + +.gallery-columns-2 .gallery-item { + max-width: 48%; + max-width: -webkit-calc(50% - 4px); + max-width: calc(50% - 4px); +} + +.gallery-columns-3 .gallery-item { + max-width: 32%; + max-width: -webkit-calc(33.3% - 4px); + max-width: calc(33.3% - 4px); +} + +.gallery-columns-4 .gallery-item { + max-width: 23%; + max-width: -webkit-calc(25% - 4px); + max-width: calc(25% - 4px); +} + +.gallery-columns-5 .gallery-item { + max-width: 19%; + max-width: -webkit-calc(20% - 4px); + max-width: calc(20% - 4px); +} + +.gallery-columns-6 .gallery-item { + max-width: 15%; + max-width: -webkit-calc(16.7% - 4px); + max-width: calc(16.7% - 4px); +} + +.gallery-columns-7 .gallery-item { + max-width: 13%; + max-width: -webkit-calc(14.28% - 4px); + max-width: calc(14.28% - 4px); +} + +.gallery-columns-8 .gallery-item { + max-width: 11%; + max-width: -webkit-calc(12.5% - 4px); + max-width: calc(12.5% - 4px); +} + +.gallery-columns-9 .gallery-item { + max-width: 9%; + max-width: -webkit-calc(11.1% - 4px); + max-width: calc(11.1% - 4px); +} + +.gallery-columns-1 .gallery-item:nth-of-type(1n), +.gallery-columns-2 .gallery-item:nth-of-type(2n), +.gallery-columns-3 .gallery-item:nth-of-type(3n), +.gallery-columns-4 .gallery-item:nth-of-type(4n), +.gallery-columns-5 .gallery-item:nth-of-type(5n), +.gallery-columns-6 .gallery-item:nth-of-type(6n), +.gallery-columns-7 .gallery-item:nth-of-type(7n), +.gallery-columns-8 .gallery-item:nth-of-type(8n), +.gallery-columns-9 .gallery-item:nth-of-type(9n) { + margin-right: 0; +} + +.gallery-columns-1.gallery-size-medium figure.gallery-item:nth-of-type(1n+1), +.gallery-columns-1.gallery-size-thumbnail figure.gallery-item:nth-of-type(1n+1), +.gallery-columns-2.gallery-size-thumbnail figure.gallery-item:nth-of-type(2n+1), +.gallery-columns-3.gallery-size-thumbnail figure.gallery-item:nth-of-type(3n+1) { + clear: left; +} + +.gallery-caption { + background-color: rgba(0, 0, 0, 0.7); + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + color: #fff; + font-size: 12px; + line-height: 1.5; + margin: 0; + max-height: 50%; + opacity: 0; + padding: 6px 8px; + position: absolute; + bottom: 0; + left: 0; + text-align: left; + width: 100%; +} + +.gallery-caption:before { + content: ""; + height: 100%; + min-height: 49px; + position: absolute; + top: 0; + left: 0; + width: 100%; +} + +.gallery-item:hover .gallery-caption { + opacity: 1; +} + +.gallery-columns-7 .gallery-caption, +.gallery-columns-8 .gallery-caption, +.gallery-columns-9 .gallery-caption { + display: none; +} + + +/** + * 6.6 Post Formats + * ----------------------------------------------------------------------------- + */ + +.format-aside .entry-content, +.format-aside .entry-summary, +.format-quote .entry-content, +.format-quote .entry-summary, +.format-link .entry-content, +.format-link .entry-summary { + padding-top: 0; +} + +.site-content .format-link .entry-title, +.site-content .format-aside .entry-title, +.site-content .format-quote .entry-title { + display: none; +} + + +/** + * 6.7 Post/Image/Paging Navigation + * ----------------------------------------------------------------------------- + */ + +.nav-links { + -webkit-hyphens: auto; + -moz-hyphens: auto; + -ms-hyphens: auto; + border-top: 1px solid rgba(0, 0, 0, 0.1); + hyphens: auto; + word-wrap: break-word; +} + +.post-navigation, +.image-navigation { + margin: 24px auto 48px; + max-width: 474px; + padding: 0 10px; +} + +.post-navigation a, +.image-navigation .previous-image, +.image-navigation .next-image { + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + padding: 11px 0 12px; + width: 100%; +} + +.post-navigation .meta-nav { + color: #767676; + display: block; + font-size: 12px; + font-weight: 900; + line-height: 2; + text-transform: uppercase; +} + +.post-navigation a, +.image-navigation a { + color: #2b2b2b; + display: block; + font-size: 14px; + font-weight: 700; + line-height: 1.7142857142; + text-transform: none; +} + +.post-navigation a:hover, +.image-navigation a:hover { + color: #41a62a; +} + +/* Paging Navigation */ + +.paging-navigation { + border-top: 5px solid #000; + margin: 48px 0; +} + +.paging-navigation .loop-pagination { + margin-top: -5px; + text-align: center; +} + +.paging-navigation .page-numbers { + border-top: 5px solid transparent; + display: inline-block; + font-size: 14px; + font-weight: 900; + margin-right: 1px; + padding: 7px 16px; + text-transform: uppercase; +} + +.paging-navigation a { + color: #2b2b2b; +} + +.paging-navigation .page-numbers.current { + border-top: 5px solid #24890d; +} + +.paging-navigation a:hover { + border-top: 5px solid #41a62a; + color: #2b2b2b; +} + + +/** + * 6.8 Attachments + * ----------------------------------------------------------------------------- + */ + +.attachment .content-sidebar, +.attachment .post-thumbnail { + display: none; +} + +.attachment .entry-content { + padding-top: 0; +} + +.attachment footer.entry-meta { + text-transform: none; +} + +.entry-attachment .attachment { + margin-bottom: 24px; +} + + +/** + * 6.9 Archives + * ----------------------------------------------------------------------------- + */ + +.archive-header, +.page-header { + margin: 24px auto; + max-width: 474px; +} + +.archive-title, +.page-title { + font-size: 16px; + font-weight: 900; + line-height: 1.5; + margin: 0; +} + +.taxonomy-description, +.author-description { + color: #767676; + font-size: 14px; + line-height: 1.2857142857; + padding-top: 18px; +} + +.taxonomy-description p, +.author-description p { + margin-bottom: 18px; +} + +.taxonomy-description p:last-child, +.author-description p:last-child { + margin-bottom: 0; +} + +.taxonomy-description a, +.author-description a { + text-decoration: underline; +} + +.taxonomy-description a:hover, +.author-description a:hover { + text-decoration: none; +} + + +/** + * 6.10 Contributor Page + * ----------------------------------------------------------------------------- + */ + +.contributor { + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 48px 10px; +} + +.contributor:first-of-type { + padding-top: 24px; +} + +.contributor-info { + margin: 0 auto; + max-width: 474px; +} + +.contributor-avatar { + border: 1px solid rgba(0, 0, 0, 0.1); + float: left; + margin: 0 30px 20px 0; + padding: 2px; +} + +.contributor-name { + font-size: 16px; + font-weight: 900; + line-height: 1.5; + margin: 0; +} + +.contributor-bio a { + text-decoration: underline; +} + +.contributor-bio a:hover { + text-decoration: none; +} + +.contributor-posts-link { + display: inline-block; + line-height: normal; + padding: 10px 30px; +} + +.contributor-posts-link:before { + content: "\f443"; +} + + +/** + * 6.11 404 Page + * ----------------------------------------------------------------------------- + */ + +.error404 .page-content { + padding-top: 0; +} + +.error404 .page-content .search-form { + margin-bottom: 24px; +} + + +/** + * 6.12 Full-width + * ----------------------------------------------------------------------------- + */ + +.full-width .hentry { + max-width: 100%; +} + + +/** + * 6.13 Singular + * ----------------------------------------------------------------------------- + */ + +.singular .site-content .hentry.has-post-thumbnail { + margin-top: -48px; +} + + +/** + * 6.14 Comments + * ----------------------------------------------------------------------------- + */ + +.comments-area { + margin: 48px auto; + max-width: 474px; + padding: 0 10px; +} + +.comment-reply-title, +.comments-title { + font: 900 16px/1.5 Lato, sans-serif; + margin: 0; + text-transform: uppercase; +} + +.comment-list { + list-style: none; + margin: 0 0 48px 0; +} + +.comment-author { + font-size: 14px; + line-height: 1.7142857142; +} + +.comment-list .reply, +.comment-metadata { + font-size: 12px; + line-height: 2; + text-transform: uppercase; +} + +.comment-list .reply { + margin-top: 24px; +} + +.comment-author .fn { + font-weight: 900; +} + +.comment-author a { + color: #2b2b2b; +} + +.comment-list .trackback a, +.comment-list .pingback a, +.comment-metadata a { + color: #767676; +} + +.comment-author a:hover, +.comment-list .pingback a:hover, +.comment-list .trackback a:hover, +.comment-metadata a:hover { + color: #41a62a; +} + +.comment-list article, +.comment-list .pingback, +.comment-list .trackback { + border-top: 1px solid rgba(0, 0, 0, 0.1); + margin-bottom: 24px; + padding-top: 24px; +} + +.comment-list > li:first-child > article, +.comment-list > .pingback:first-child, +.comment-list > .trackback:first-child { + border-top: 0; +} + +.comment-author { + position: relative; +} + +.comment-author .avatar { + border: 1px solid rgba(0, 0, 0, 0.1); + height: 18px; + padding: 2px; + position: absolute; + top: 0; + left: 0; + width: 18px; +} + +.bypostauthor > article .fn:before { + content: "\f408"; + margin: 0 2px 0 -2px; + position: relative; + top: -1px; +} + +.says { + display: none; +} + +.comment-author, +.comment-awaiting-moderation, +.comment-content, +.comment-list .reply, +.comment-metadata { + padding-left: 30px; +} + +.comment-edit-link { + margin-left: 10px; +} + +.comment-edit-link:before { + content: "\f411"; +} + +.comment-reply-link:before, +.comment-reply-login:before { + content: "\f412"; + margin-right: 2px; +} + +.comment-content { + -webkit-hyphens: auto; + -moz-hyphens: auto; + -ms-hyphens: auto; + hyphens: auto; + word-wrap: break-word; +} + +.comment-content ul, +.comment-content ol { + margin: 0 0 24px 22px; +} + +.comment-content li > ul, +.comment-content li > ol { + margin-bottom: 0; +} + +.comment-content > :last-child { + margin-bottom: 0; +} + +.comment-list .children { + list-style: none; + margin-left: 15px; +} + +.comment-respond { + margin-bottom: 24px; + padding: 0; +} + +.comment .comment-respond { + margin-top: 24px; +} + +.comment-respond h3 { + margin-top: 0; + margin-bottom: 24px; +} + +.comment-notes, +.comment-awaiting-moderation, +.logged-in-as, +.no-comments, +.form-allowed-tags, +.form-allowed-tags code { + color: #767676; +} + +.comment-notes, +.comment-awaiting-moderation, +.logged-in-as { + font-size: 14px; + line-height: 1.7142857142; +} + +.no-comments { + font-size: 16px; + font-weight: 900; + line-height: 1.5; + margin-top: 24px; + text-transform: uppercase; +} + +.comment-form label { + display: block; +} + +.comment-form input[type="text"], +.comment-form input[type="email"], +.comment-form input[type="url"] { + width: 100%; +} + +.form-allowed-tags, +.form-allowed-tags code { + font-size: 12px; + line-height: 1.5; +} + +.required { + color: #c0392b; +} + +.comment-reply-title small a { + color: #2b2b2b; + float: right; + height: 24px; + overflow: hidden; + width: 24px; +} + +.comment-reply-title small a:hover { + color: #41a62a; +} + +.comment-reply-title small a:before { + content: "\f405"; + font-size: 32px; +} + +.comment-navigation { + font-size: 12px; + line-height: 2; + margin-bottom: 48px; + text-transform: uppercase; +} + +.comment-navigation .nav-next, +.comment-navigation .nav-previous { + display: inline-block; +} + +.comment-navigation .nav-previous a { + margin-right: 10px; +} + +#comment-nav-above { + margin-top: 36px; + margin-bottom: 0; +} + + +/** + * 7.0 Sidebars + * ----------------------------------------------------------------------------- + */ + +/* Secondary */ + +#secondary { + background-color: #000; + border-top: 1px solid #000; + border-bottom: 1px solid rgba(255, 255, 255, 0.2); + clear: both; + color: rgba(255, 255, 255, 0.7); + margin-top: -1px; + padding: 0 10px; + position: relative; + z-index: 2; +} + +.site-description { + display: none; + font-size: 12px; + font-weight: 400; + line-height: 1.5; +} + +/* Primary Sidebar */ + +.primary-sidebar { + padding-top: 48px; +} + +.secondary-navigation + .primary-sidebar { + padding-top: 0; +} + +/* Content Sidebar */ + +.content-sidebar { + border-top: 1px solid rgba(0, 0, 0, 0.1); + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + color: #767676; + padding: 48px 10px 0; +} + + +/** + * 7.1 Widgets + * ----------------------------------------------------------------------------- + */ + +/* Primary Sidebar, Footer Sidebar */ + +.widget { + font-size: 14px; + -webkit-hyphens: auto; + -moz-hyphens: auto; + -ms-hyphens: auto; + hyphens: auto; + line-height: 1.2857142857; + margin-bottom: 48px; + width: 100%; + word-wrap: break-word; +} + +.widget a { + color: #fff; +} + +.widget a:hover { + color: #41a62a; +} + +.widget h1, +.widget h2, +.widget h3, +.widget h4, +.widget h5, +.widget h6 { + margin: 24px 0 12px; +} + +.widget h1 { + font-size: 22px; + line-height: 1.0909090909; +} + +.widget h2 { + font-size: 20px; + line-height: 1.2; +} + +.widget h3 { + font-size: 18px; + line-height: 1.3333333333; +} + +.widget h4 { + font-size: 16px; + line-height: 1.5; +} + +.widget h5 { + font-size: 14px; + line-height: 1.7142857142; +} + +.widget h6 { + font-size: 12px; + line-height: 2; +} + +.widget address { + margin-bottom: 18px; +} + +.widget abbr[title] { + border-color: rgba(255, 255, 255, 0.7); +} + +.widget mark, +.widget ins { + color: #000; +} + +.widget pre, +.widget fieldset { + border-color: rgba(255, 255, 255, 0.2); +} + +.widget code, +.widget kbd, +.widget tt, +.widget var, +.widget samp, +.widget pre { + font-size: 12px; + line-height: 1.5; +} + +.widget blockquote { + color: rgba(255, 255, 255, 0.7); + font-size: 18px; + line-height: 1.5; + margin-bottom: 18px; +} + +.widget blockquote cite { + color: #fff; + font-size: 14px; + line-height: 1.2857142857; +} + +.widget dl, +.widget dd { + margin-bottom: 18px; +} + +.widget ul, +.widget ol { + list-style: none; + margin: 0; +} + +.widget li > ol, +.widget li > ul { + margin-left: 10px; +} + +.widget table, +.widget th, +.widget td { + border-color: rgba(255, 255, 255, 0.2); +} + +.widget table { + margin-bottom: 18px; +} + +.widget del { + color: rgba(255, 255, 255, 0.4); +} + +.widget hr { + background-color: rgba(255, 255, 255, 0.2); +} + +.widget p { + margin-bottom: 18px; +} + +.widget-area .widget input, +.widget-area .widget textarea { + background-color: rgba(255, 255, 255, 0.1); + border-color: rgba(255, 255, 255, 0.2); + color: #fff; + font-size: 16px; + padding: 1px 2px 2px 4px; +} + +.widget-area .widget input:focus, +.widget-area .widget textarea:focus { + border-color: rgba(255, 255, 255, 0.3); +} + +.widget button, +.widget .button, +.widget input[type="button"], +.widget input[type="reset"], +.widget input[type="submit"] { + background-color: #24890d; + border: 0; + font-size: 12px; + padding: 5px 15px 4px; +} + +.widget input[type="button"]:hover, +.widget input[type="button"]:focus, +.widget input[type="reset"]:hover, +.widget input[type="reset"]:focus, +.widget input[type="submit"]:hover, +.widget input[type="submit"]:focus { + background-color: #41a62a; +} + +.widget input[type="button"]:active, +.widget input[type="reset"]:active, +.widget input[type="submit"]:active { + background-color: #55d737; +} + +.widget .wp-caption { + color: rgba(255, 255, 255, 0.7); + margin-bottom: 18px; +} + +.widget .widget-title { + font-size: 14px; + font-weight: 700; + line-height: 1.7142857142; + margin: 0 0 24px 0; + text-transform: uppercase; +} + +.widget-title, +.widget-title a { + color: #fff; +} + +.widget-title a:hover { + color: #41a62a; +} + +/* Calendar Widget*/ + +.widget_calendar table { + line-height: 2; + margin: 0; +} + +.widget_calendar caption { + color: #fff; + font-weight: 700; + line-height: 1.7142857142; + margin-bottom: 18px; + text-align: left; + text-transform: uppercase; +} + +.widget_calendar thead th { + background-color: rgba(255, 255, 255, 0.1); +} + +.widget_calendar tbody td, +.widget_calendar thead th { + text-align: center; +} + +.widget_calendar tbody a { + background-color: #24890d; + color: #fff; + display: block; +} + +.widget_calendar tbody a:hover { + background-color: #41a62a; +} + +.widget_calendar tbody a:hover { + color: #fff; +} + +.widget_calendar #prev { + padding-left: 5px; +} + +.widget_calendar #next { + padding-right: 5px; + text-align: right; +} + +/* Ephemera Widget*/ + +.widget_twentyfourteen_ephemera > ol > li { + border-bottom: 1px solid rgba(255, 255, 255, 0.2); + margin-bottom: 18px; + padding: 0; +} + +.widget_twentyfourteen_ephemera .hentry { + margin: 0; + max-width: 100%; +} + +.widget_twentyfourteen_ephemera .entry-title, +.widget_twentyfourteen_ephemera .entry-meta, +.widget_twentyfourteen_ephemera .wp-caption-text, +.widget_twentyfourteen_ephemera .post-format-archive-link, +.widget_twentyfourteen_ephemera .entry-content table { + font-size: 12px; + line-height: 1.5; +} + +.widget_twentyfourteen_ephemera .entry-title { + display: inline; + font-weight: 400; +} + +.widget_twentyfourteen_ephemera .entry-meta { + margin-bottom: 18px; +} + +.widget_twentyfourteen_ephemera .entry-meta a { + color: rgba(255, 255, 255, 0.7); +} + +.widget_twentyfourteen_ephemera .entry-meta a:hover { + color: #41a62a; +} + +.widget_twentyfourteen_ephemera .entry-content ul, +.widget_twentyfourteen_ephemera .entry-content ol { + margin: 0 0 18px 20px; +} + +.widget_twentyfourteen_ephemera .entry-content ul { + list-style: disc; +} + +.widget_twentyfourteen_ephemera .entry-content ol { + list-style: decimal; +} + +.widget_twentyfourteen_ephemera .entry-content li > ul, +.widget_twentyfourteen_ephemera .entry-content li > ol { + margin: 0 0 0 20px; +} + +.widget_twentyfourteen_ephemera .entry-content th, +.widget_twentyfourteen_ephemera .entry-content td { + padding: 6px; +} + +.widget_twentyfourteen_ephemera .post-format-archive-link { + font-weight: 700; + text-transform: uppercase; +} + +/* List Style Widgets*/ + +.widget_archive li, +.widget_categories li, +.widget_links li, +.widget_meta li, +.widget_nav_menu li, +.widget_pages li, +.widget_recent_comments li, +.widget_recent_entries li { + border-top: 1px solid rgba(255, 255, 255, 0.2); + padding: 8px 0 9px; +} + +.widget_archive li:first-child, +.widget_categories li:first-child, +.widget_links li:first-child, +.widget_meta li:first-child, +.widget_nav_menu li:first-child, +.widget_pages li:first-child, +.widget_recent_comments li:first-child, +.widget_recent_entries li:first-child { + border-top: 0; +} + +.widget_categories li ul, +.widget_nav_menu li ul, +.widget_pages li ul { + border-top: 1px solid rgba(255, 255, 255, 0.2); + margin-top: 9px; +} + +.widget_categories li li:last-child, +.widget_nav_menu li li:last-child, +.widget_pages li li:last-child { + padding-bottom: 0; +} + +/* Recent Posts Widget */ + +.widget_recent_entries .post-date { + display: block; +} + +/* RSS Widget */ + +.rsswidget img { + margin-top: -4px; +} + +.rssSummary { + margin: 9px 0; +} + +.rss-date { + display: block; +} + +.widget_rss li { + margin-bottom: 18px; +} + +.widget_rss li:last-child { + margin-bottom: 0; +} + +/* Text Widget */ + +.widget_text > div > :last-child { + margin-bottom: 0; +} + + +/** + * 7.2 Content Sidebar Widgets + * ----------------------------------------------------------------------------- + */ + +.content-sidebar .widget a { + color: #24890d; +} + +.content-sidebar .widget a:hover { + color: #41a62a; +} + +.content-sidebar .widget pre { + border-color: rgba(0, 0, 0, 0.1); +} + +.content-sidebar .widget mark, +.content-sidebar .widget ins { + color: #2b2b2b; +} + +.content-sidebar .widget abbr[title] { + border-color: #2b2b2b; +} + +.content-sidebar .widget fieldset { + border-color: rgba(0, 0, 0, 0.1); +} + +.content-sidebar .widget blockquote { + color: #767676; +} + +.content-sidebar .widget blockquote cite { + color: #2b2b2b; +} + +.content-sidebar .widget li > ol, +.content-sidebar .widget li > ul { + margin-left: 18px; +} + +.content-sidebar .widget table, +.content-sidebar .widget th, +.content-sidebar .widget td { + border-color: rgba(0, 0, 0, 0.1); +} + +.content-sidebar .widget del { + color: #767676; +} + +.content-sidebar .widget hr { + background-color: rgba(0, 0, 0, 0.1); +} + +.content-sidebar .widget input, +.content-sidebar .widget textarea { + background-color: #fff; + border-color: rgba(0, 0, 0, 0.1); + color: #2b2b2b; +} + +.content-sidebar .widget input:focus, +.content-sidebar .widget textarea:focus { + border-color: rgba(0, 0, 0, 0.3); +} + +.content-sidebar .widget input[type="button"], +.content-sidebar .widget input[type="reset"], +.content-sidebar .widget input[type="submit"] { + background-color: #24890d; + border: 0; + color: #fff; +} + +.content-sidebar .widget input[type="button"]:hover, +.content-sidebar .widget input[type="button"]:focus, +.content-sidebar .widget input[type="reset"]:hover, +.content-sidebar .widget input[type="reset"]:focus, +.content-sidebar .widget input[type="submit"]:hover, +.content-sidebar .widget input[type="submit"]:focus { + background-color: #41a62a; +} + +.content-sidebar .widget input[type="button"]:active, +.content-sidebar .widget input[type="reset"]:active, +.content-sidebar .widget input[type="submit"]:active { + background-color: #55d737; +} + +.content-sidebar .widget .wp-caption { + color: #767676; +} + +.content-sidebar .widget .widget-title { + border-top: 5px solid #000; + color: #2b2b2b; + font-size: 14px; + font-weight: 900; + margin: 0 0 18px; + padding-top: 7px; + text-transform: uppercase; +} + +.content-sidebar .widget .widget-title a { + color: #2b2b2b; +} + +.content-sidebar .widget .widget-title a:hover { + color: #41a62a; +} + +/* List Style Widgets*/ + +.content-sidebar .widget_archive li, +.content-sidebar .widget_categories li, +.content-sidebar .widget_links li, +.content-sidebar .widget_meta li, +.content-sidebar .widget_nav_menu li, +.content-sidebar .widget_pages li, +.content-sidebar .widget_recent_comments li, +.content-sidebar .widget_recent_entries li, +.content-sidebar .widget_categories li ul, +.content-sidebar .widget_nav_menu li ul, +.content-sidebar .widget_pages li ul { + border-color: rgba(0, 0, 0, 0.1); +} + +/* Calendar Widget */ + +.content-sidebar .widget_calendar caption { + color: #2b2b2b; + font-weight: 900; +} + +.content-sidebar .widget_calendar thead th { + background-color: rgba(0, 0, 0, 0.02); +} + +.content-sidebar .widget_calendar tbody a, +.content-sidebar .widget_calendar tbody a:hover { + color: #fff; +} + +/* Ephemera widget*/ + +.content-sidebar .widget_twentyfourteen_ephemera .widget-title { + line-height: 1.2857142857; + padding-top: 1px; +} + +.content-sidebar .widget_twentyfourteen_ephemera .widget-title:before { + background-color: #000; + color: #fff; + margin: -1px 9px 0 0; + padding: 6px 0 9px; + text-align: center; + vertical-align: middle; + width: 36px; +} + +.content-sidebar .widget_twentyfourteen_ephemera .video.widget-title:before { + content: "\f104"; +} + +.content-sidebar .widget_twentyfourteen_ephemera .audio.widget-title:before { + content: "\f109"; +} + +.content-sidebar .widget_twentyfourteen_ephemera .image.widget-title:before { + content: "\f473"; +} + +.content-sidebar .widget_twentyfourteen_ephemera .gallery.widget-title:before { + content: "\f103"; +} + +.content-sidebar .widget_twentyfourteen_ephemera .aside.widget-title:before { + content: "\f101"; +} + +.content-sidebar .widget_twentyfourteen_ephemera .quote.widget-title:before { + content: "\f106"; +} + +.content-sidebar .widget_twentyfourteen_ephemera .link.widget-title:before { + content: "\f107"; +} + +.content-sidebar .widget_twentyfourteen_ephemera > ol > li { + border-bottom: 1px solid rgba(0, 0, 0, 0.1); +} + +.content-sidebar .widget_twentyfourteen_ephemera .entry-meta { + color: #ccc; +} + +.content-sidebar .widget_twentyfourteen_ephemera .entry-meta a { + color: #767676; +} + +.content-sidebar .widget_twentyfourteen_ephemera .entry-meta a:hover { + color: #41a62a; +} + +.content-sidebar.widget_twentyfourteen_ephemera blockquote cite { + font-size: 13px; + line-height: 1.3846153846; +} + +.content-sidebar .widget_twentyfourteen_ephemera .post-format-archive-link { + font-weight: 900; +} + + +/** + * 8.0 Footer + * ----------------------------------------------------------------------------- + */ + +#supplementary { + padding: 0 10px; +} + +.site-footer, +.site-info, +.site-info a { + color: rgba(255, 255, 255, 0.7); +} + +.site-footer { + background-color: #000; + font-size: 12px; + position: relative; + z-index: 3; +} + +.footer-sidebar { + padding-top: 48px; +} + +.site-info { + padding: 15px 10px; +} + +#supplementary + .site-info { + border-top: 1px solid rgba(255, 255, 255, 0.2); +} + +.site-info a:hover { + color: #41a62a; +} + + +/** + * 9.0 Featured Content + * ----------------------------------------------------------------------------- + */ + +.featured-content { + background: #000 url(images/pattern-dark.svg) repeat fixed; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + position: relative; + width: 100%; +} + +.featured-content-inner { + overflow: hidden; +} + +.featured-content .hentry { + color: #fff; + margin: 0; + max-width: 100%; + width: 100%; +} + +.featured-content .post-thumbnail, +.featured-content .post-thumbnail:hover { + background: transparent; +} + +.featured-content .post-thumbnail { + display: block; + position: relative; + padding-top: 55.357142857%; + overflow: hidden; +} + +.featured-content .post-thumbnail img { + left: 0; + position: absolute; + top: 0; +} + +.featured-content .entry-header { + background-color: #000; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + min-height: 96px; + overflow: hidden; + padding: 24px 10px; +} + +.featured-content a { + color: #fff; +} + +.featured-content a:hover { + color: #41a62a; +} + +.featured-content .entry-meta { + color: #fff; + font-size: 11px; + font-weight: 700; + line-height: 1.0909090909; + margin-bottom: 12px; +} + +.featured-content .cat-links { + font-weight: 700; +} + +.featured-content .entry-title { + font-size: 18px; + font-weight: 300; + line-height: 1.3333333333; + margin: 0; + text-transform: uppercase; +} + + +/* Slider */ + +.slider .featured-content .hentry { + -webkit-backface-visibility: hidden; + display: none; + position: relative; +} + +.slider .featured-content .post-thumbnail { + padding-top: 55.49132947%; +} + +.slider-control-paging { + background-color: #000; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + float: left; + list-style: none; + margin: -24px 0 0 0; + position: relative; + width: 100%; + z-index: 3; +} + +.slider-control-paging li { + float: left; + margin: 2px 4px 2px 0; +} + +.slider-control-paging li:last-child { + margin-right: 0; +} + +.slider-control-paging a { + cursor: pointer; + display: block; + height: 44px; + position: relative; + text-indent: -999em; + width: 44px; +} + +.slider-control-paging a:before { + background-color: #4d4d4d; + content: ""; + height: 12px; + left: 10px; + position: absolute; + top: 16px; + width: 12px; +} + +.slider-control-paging a:hover:before { + background-color: #41a62a; +} + +.slider-control-paging .slider-active:before, +.slider-control-paging .slider-active:hover:before { + background-color: #24890d; +} + +.slider-direction-nav { + clear: both; + list-style: none; + margin: 0; + position: relative; + width: 100%; + z-index: 3; +} + +.slider-direction-nav li { + border-color: #fff; + border-style: solid; + border-width: 2px 1px 0 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + float: left; + text-align: center; + width: 50%; +} + +.slider-direction-nav li:last-child { + border-width: 2px 0 0 1px; +} + +.slider-direction-nav a { + background-color: #000; + display: block; + font-size: 0; + height: 46px; +} + +.slider-direction-nav a:hover { + background-color: #24890d; +} + +.slider-direction-nav a:before { + color: #fff; + content: "\f430"; + font-size: 32px; + line-height: 46px; +} + +.slider-direction-nav .slider-next:before { + content: "\f429"; +} + +.slider-direction-nav .slider-disabled { + display: none; +} + + +/** + * 10.0 Multisite + * ----------------------------------------------------------------------------- + */ + +.site-main .widecolumn { + padding-top: 72px; + width: auto; +} +.site-main .mu_register, +.widecolumn > h2, +.widecolumn > form { + margin: 0 auto 48px; + max-width: 474px; + padding: 0 30px; +} + +.site-main .mu_register #blog_title, +.site-main .mu_register #user_email, +.site-main .mu_register #blogname, +.site-main .mu_register #user_name { + font-size: inherit; + width: 90%; +} + +.site-main .mu_register input[type="submit"], +.widecolumn #submit { + font-size: inherit; + width: auto; +} + + +/** + * 11.0 Media Queries + * ----------------------------------------------------------------------------- + */ + +/* Does the same thing as , + * but in the future W3C standard way. -ms- prefix is required for IE10+ to + * render responsive styling in Windows 8 "snapped" views; IE10+ does not honor + * the meta tag. See https://core.trac.wordpress.org/ticket/25888. + */ +@-ms-viewport { + width: device-width; +} + +@viewport { + width: device-width; +} + +@media screen and (max-width: 400px) { + .list-view .site-content .post-thumbnail { + background: none; + width: auto; + z-index: 2; + } + + .list-view .site-content .post-thumbnail img { + float: left; + margin: 0 10px 3px 0; + width: 84px; + } + + .list-view .site-content .entry-header { + background-color: transparent; + padding: 0; + } + + .list-view .content-area { + padding: 0 10px; + } + + .list-view .site-content .hentry { + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + margin: 0; + min-height: 60px; + padding: 12px 0 9px; + } + + .list-view .site-content .cat-links, + .list-view .site-content .type-post .entry-content, + .list-view .site-content .type-page .entry-content, + .list-view .site-content .type-post .entry-summary, + .list-view .site-content .type-page .entry-summary, + .list-view .site-content footer.entry-meta { + display: none; + } + + .list-view .site-content .entry-title { + clear: none; + font-size: 15px; + font-weight: 900; + line-height: 1.2; + margin-bottom: 6px; + text-transform: none; + } + + .list-view .site-content .format-aside .entry-title, + .list-view .site-content .format-link .entry-title, + .list-view .site-content .format-quote .entry-title { + display: block; + } + + .list-view .site-content .entry-meta { + background-color: transparent; + clear: none; + margin: 0; + text-transform: none; + } + + .archive-header, + .page-header { + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + margin: 24px auto 0; + padding-bottom: 24px; + } + + .error404 .page-header { + border-bottom: 0; + margin: 0 auto 24px; + padding: 0 10px; + } +} + +@media screen and (min-width: 401px) { + a.post-thumbnail:hover img { + opacity: 0.85; + } + + .full-size-link:before, + .parent-post-link:before, + .site-content span + .byline:before, + .site-content span + .comments-link:before, + .site-content span + .edit-link:before, + .site-content span + .entry-date:before { + content: ""; + } + + .attachment span.entry-date:before, + .entry-content .edit-link a:before, + .entry-meta .edit-link a:before, + .site-content .byline a:before, + .site-content .comments-link a:before, + .site-content .entry-date a:before, + .site-content .featured-post:before, + .site-content .full-size-link a:before, + .site-content .parent-post-link a:before, + .site-content .post-format a:before { + -webkit-font-smoothing: antialiased; + display: inline-block; + font: normal 16px/1 Genericons; + text-decoration: inherit; + vertical-align: text-bottom; + } + + .site-content .entry-meta > span { + margin-right: 10px; + } + + .site-content .format-video .post-format a:before { + content: "\f104"; + } + + .site-content .format-audio .post-format a:before { + content: "\f109"; + } + + .site-content .format-image .post-format a:before { + content: "\f473"; + } + + .site-content .format-quote .post-format a:before { + content: "\f106"; + margin-right: 2px; + } + + .site-content .format-gallery .post-format a:before { + content: "\f103"; + margin-right: 4px; + } + + .site-content .format-aside .post-format a:before { + content: "\f101"; + margin-right: 2px; + } + + .site-content .format-link .post-format a:before { + content: "\f107"; + position: relative; + top: 1px; + } + + .site-content .featured-post:before { + content: "\f308"; + margin-right: 3px; + position: relative; + top: 1px; + } + + .site-content .entry-date a:before, + .attachment .site-content span.entry-date:before { + content: "\f303"; + margin-right: 1px; + position: relative; + top: 1px; + } + + .site-content .byline a:before { + content: "\f304"; + } + + .site-content .comments-link a:before { + content: "\f300"; + margin-right: 2px; + } + + .entry-content .edit-link a:before, + .entry-meta .edit-link a:before { + content: "\f411"; + } + + .site-content .full-size-link a:before { + content: "\f402"; + margin-right: 1px; + } + + .site-content .parent-post-link a:before { + content: "\f301"; + } + + .list-view .site-content .hentry { + border-top: 1px solid rgba(0, 0, 0, 0.1); + padding-top: 48px; + } + + .list-view .site-content .hentry:first-of-type, + .list-view .site-content .hentry.has-post-thumbnail { + border-top: 0; + padding-top: 0; + } + + .archive-header, + .page-header { + margin: 0 auto 60px; + padding: 0 10px; + } + + .error404 .page-header { + margin-bottom: 24px; + } +} + +@media screen and (min-width: 594px) { + .site-content .entry-header { + padding-right: 30px; + padding-left: 30px; + } + + .site-content .has-post-thumbnail .entry-header { + margin-top: -48px; + } +} + +@media screen and (min-width: 673px) { + .header-main { + padding: 0 30px; + } + + .search-toggle { + margin-right: 18px; + } + + .search-box .search-field { + width: 50%; + } + + .content-area { + float: left; + width: 100%; + } + + .site-content { + margin-right: 33.33333333%; + } + + .site-content .has-post-thumbnail .entry-header { + margin-top: 0; + } + + .archive-header, + .comments-area, + .image-navigation, + .page-header, + .page-content, + .post-navigation, + .site-content .entry-content, + .site-content .entry-summary, + .site-content footer.entry-meta { + padding-right: 30px; + padding-left: 30px; + } + + .singular .site-content .hentry.has-post-thumbnail { + margin-top: 0; + } + + .full-width .site-content { + margin-right: 0; + } + + .full-width .site-content .has-post-thumbnail .entry-header, + .full-width .site-content .hentry.has-post-thumbnail:first-child { + margin-top: -48px; + } + + #secondary, + #supplementary { + padding: 0 30px; + } + + .content-sidebar { + border: 0; + float: right; + margin-left: -33.33333333%; + padding: 48px 30px 24px; + position: relative; + width: 33.33333333%; + } + + .grid .featured-content .hentry { + float: left; + width: 50%; + } + + .grid .featured-content .hentry:nth-child( 2n+1 ) { + clear: both; + } + + .grid .featured-content .entry-header { + border-color: #000; + border-style: solid; + border-width: 12px 10px; + height: 96px; + padding: 0; + } + + .slider .featured-content .entry-title { + font-size: 22px; + line-height: 1.0909090909; + } + + .slider .featured-content .entry-header { + min-height: inherit; + padding: 24px 30px 48px; + position: absolute; + left: 0; + bottom: 0; + width: 50%; + z-index: 3; + } + + .slider-control-paging { + background: transparent; + margin-top: -48px; + padding-left: 20px; + width: 50%; + } + + .slider-direction-nav { + clear: none; + float: right; + margin-top: -48px; + width: 98px; + } + + .slider-direction-nav li { + border: 0; + padding: 0 1px 0 0; + } + + .slider-direction-nav li:last-child { + padding: 0 0 0 1px; + } + + .slider-direction-nav a { + height: 48px; + } + + .slider-direction-nav a:before { + line-height: 48px; + } + + .site-info { + padding: 15px 30px; + } +} + +@media screen and (min-width: 783px) { + .site-title { + /* Search-toggle width = 48px */ + max-width: -webkit-calc(100% - 48px); + max-width: calc(100% - 48px); + } + + .header-main { + padding-right: 0; + } + + .search-toggle { + margin-right: 0; + } + + /* Fixed Header */ + + .masthead-fixed .site-header { + position: fixed; + top: 0; + } + + .admin-bar.masthead-fixed .site-header { + top: 32px; + } + + .masthead-fixed .site-main { + margin-top: 48px; + } + + /* Navigation */ + + .site-navigation li .current_page_item > a, + .site-navigation li .current_page_ancestor > a, + .site-navigation li .current-menu-item > a, + .site-navigation li .current-menu-ancestor > a { + color: #fff; + } + + /* Primary Navigation */ + + .primary-navigation { + float: right; + font-size: 11px; + margin: 0 1px 0 -12px; + padding: 0; + text-transform: uppercase; + } + + .primary-navigation .menu-toggle { + display: none; + padding: 0; + } + + .primary-navigation .nav-menu { + border-bottom: 0; + display: block; + } + + .primary-navigation.toggled-on { + border-bottom: 0; + margin: 0; + padding: 0; + } + + .primary-navigation li { + border: 0; + display: inline-block; + height: 48px; + line-height: 48px; + position: relative; + } + + .primary-navigation a { + display: inline-block; + padding: 0 12px; + white-space: nowrap; + } + + .primary-navigation ul ul { + background-color: #24890d; + float: left; + margin: 0; + position: absolute; + top: 48px; + left: -999em; + z-index: 99999; + } + + .primary-navigation li li { + border: 0; + display: block; + height: auto; + line-height: 1.0909090909; + } + + .primary-navigation ul ul ul { + left: -999em; + top: 0; + } + + .primary-navigation ul ul a { + padding: 18px 12px; + white-space: normal; + width: 176px; + } + + .primary-navigation li:hover > a, + .primary-navigation li.focus > a { + background-color: #24890d; + color: #fff; + } + + .primary-navigation ul ul a:hover, + .primary-navigation ul ul li.focus > a { + background-color: #41a62a; + } + + .primary-navigation ul li:hover > ul, + .primary-navigation ul li.focus > ul { + left: auto; + } + + .primary-navigation ul ul li:hover > ul, + .primary-navigation ul ul li.focus > ul { + left: 100%; + } + + .primary-navigation .menu-item-has-children > a, + .primary-navigation .page_item_has_children > a { + padding-right: 26px; + } + + .primary-navigation .menu-item-has-children > a:after, + .primary-navigation .page_item_has_children > a:after { + -webkit-font-smoothing: antialiased; + content: "\f502"; + display: inline-block; + font: normal 8px/1 Genericons; + position: absolute; + right: 12px; + top: 22px; + vertical-align: text-bottom; + } + + .primary-navigation li .menu-item-has-children > a, + .primary-navigation li .page_item_has_children > a { + padding-right: 20px; + width: 168px; + } + + .primary-navigation .menu-item-has-children li.menu-item-has-children > a:after, + .primary-navigation .menu-item-has-children li.page_item_has_children > a:after, + .primary-navigation .page_item_has_children li.menu-item-has-children > a:after, + .primary-navigation .page_item_has_children li.page_item_has_children > a:after { + content: "\f501"; + right: 8px; + top: 20px; + } +} + +@media screen and (min-width: 810px) { + .attachment .entry-attachment .attachment { + margin-right: -168px; + margin-left: -168px; + max-width: 810px; + } + + .attachment .site-content .attachment img { + display: block; + margin: 0 auto; + } + + .contributor-avatar { + margin-left: -168px; + } + + .contributor-summary { + float: left; + } + + .full-width .site-content blockquote.alignleft, + .full-width .site-content blockquote.alignright { + width: -webkit-calc(50% + 130px); + width: calc(50% + 130px); + } + + .full-width .site-content blockquote.alignleft, + .full-width .site-content img.size-full.alignleft, + .full-width .site-content img.size-large.alignleft, + .full-width .site-content img.size-medium.alignleft, + .full-width .site-content .wp-caption.alignleft { + margin-left: -168px; + } + + .full-width .site-content .alignleft { + clear: left; + } + + .full-width .site-content blockquote.alignright, + .full-width .site-content img.size-full.alignright, + .full-width .site-content img.size-large.alignright, + .full-width .site-content img.size-medium.alignright, + .full-width .site-content .wp-caption.alignright { + margin-right: -168px; + } + + .full-width .site-content .alignright { + clear: right; + } +} + +@media screen and (min-width: 846px) { + .content-area, + .content-sidebar { + padding-top: 72px; + } + + .site-content .has-post-thumbnail .entry-header { + margin-top: -48px; + } + + .comment-list .trackback, + .comment-list .pingback, + .comment-list article { + margin-bottom: 36px; + padding-top: 36px; + } + + .comment-author .avatar { + height: 34px; + top: 2px; + width: 34px; + } + + .comment-author, + .comment-awaiting-moderation, + .comment-content, + .comment-list .reply, + .comment-metadata { + padding-left: 50px; + } + + .comment-list .children { + margin-left: 20px; + } + + .full-width .site-content .hentry.has-post-thumbnail:first-child { + margin-top: -72px; + } + + .featured-content { + margin-bottom: 0; + } +} + +@media screen and (min-width: 1008px) { + .search-box-wrapper { + padding-left: 182px; + } + + .main-content { + float: left; + } + + .site-content { + margin-right: 29.04761904%; + margin-left: 182px; + } + + .site-content .entry-header { + margin-top: 0; + } + + .site-content .has-post-thumbnail .entry-header { + margin-top: 0; + } + + .content-sidebar { + margin-left: -29.04761904%; + width: 29.04761904%; + } + + .site:before { + background-color: #000; + content: ""; + display: block; + height: 100%; + min-height: 100%; + position: absolute; + top: 0; + left: 0; + width: 182px; + z-index: 2; + } + + #secondary { + background-color: transparent; + border: 0; + clear: none; + float: left; + margin: 0 0 0 -100%; + min-height: 100vh; + width: 122px; + } + + .primary-sidebar { + padding-top: 0; + } + + .site-description { + display: block; + margin: 0 0 18px; + } + + .site-description:empty { + margin: 0; + } + + .secondary-navigation { + font-size: 11px; + margin: 0 -30px 48px; + width: 182px; + } + + .secondary-navigation li { + border-top: 1px solid rgba(255, 255, 255, 0.2); + position: relative; + } + + .secondary-navigation a { + padding: 10px 30px; + } + + .secondary-navigation ul ul { + background-color: #24890d; + position: absolute; + top: 0; + left: -999em; + width: 182px; + z-index: 99999; + } + + .secondary-navigation li li { + border-top: 0; + } + + .secondary-navigation li:hover > a, + .secondary-navigation li.focus > a { + background-color: #24890d; + color: #fff; + } + + .secondary-navigation ul ul a:hover, + .secondary-navigation ul ul li.focus > a { + background-color: #41a62a; + } + + .secondary-navigation ul li:hover > ul, + .secondary-navigation ul li.focus > ul { + left: 162px; + } + + .secondary-navigation .menu-item-has-children > a { + padding-right: 38px; + } + + .secondary-navigation .menu-item-has-children > a:after { + -webkit-font-smoothing: antialiased; + content: "\f501"; + display: inline-block; + font: normal 8px/1 Genericons; + position: absolute; + right: 26px; + top: 14px; + vertical-align: text-bottom; + } + + .footer-sidebar .widget, + .primary-sidebar .widget { + font-size: 12px; + line-height: 1.5; + } + + .footer-sidebar .widget { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + float: left; + padding: 0 30px; + width: 25%; + } + + .footer-sidebar .widget h1, + .primary-sidebar .widget h1 { + font-size: 20px; + line-height: 1.2; + } + + .footer-sidebar .widget h2, + .primary-sidebar .widget h2 { + font-size: 18px; + line-height: 1.3333333333; + } + + .footer-sidebar .widget h3, + .primary-sidebar .widget h3 { + font-size: 16px; + line-height: 1.5; + } + + .footer-sidebar .widget h4, + .primary-sidebar .widget h4 { + font-size: 14px; + line-height: 1.7142857142; + } + + .footer-sidebar .widget h5, + .primary-sidebar .widget h5 { + font-size: 12px; + line-height: 2; + } + + .footer-sidebar .widget h6, + .primary-sidebar .widget h6 { + font-size: 11px; + line-height: 2.1818181818; + } + + .footer-sidebar .widget code, + .footer-sidebar .widget kbd, + .footer-sidebar .widget tt, + .footer-sidebar .widget var, + .footer-sidebar .widget samp, + .footer-sidebar .widget pre, + .primary-sidebar .widget code, + .primary-sidebar .widget kbd, + .primary-sidebar .widget tt, + .primary-sidebar .widget var, + .primary-sidebar .widget samp, + .primary-sidebar .widget pre { + font-size: 11px; + line-height: 1.6363636363; + } + + .footer-sidebar .widget blockquote, + .primary-sidebar .widget blockquote { + font-size: 14px; + line-height: 1.2857142857; + } + + .footer-sidebar .widget blockquote cite, + .primary-sidebar .widget blockquote cite { + font-size: 12px; + line-height: 1.5; + } + + .footer-sidebar .widget input, + .footer-sidebar .widget textarea, + .primary-sidebar .widget input, + .primary-sidebar .widget textarea { + font-size: 12px; + padding: 3px 2px 4px 4px; + } + + .footer-sidebar .widget input[type="button"], + .footer-sidebar .widget input[type="reset"], + .footer-sidebar .widget input[type="submit"], + .primary-sidebar .widget input[type="button"], + .primary-sidebar .widget input[type="reset"], + .primary-sidebar .widget input[type="submit"] { + padding: 5px 15px 4px; + } + + .footer-sidebar .widget .widget-title, + .primary-sidebar .widget .widget-title { + font-size: 11px; + font-weight: 900; + line-height: 1.6363636363; + margin-bottom: 18px; + } + + .footer-sidebar .widget_twentyfourteen_ephemera .entry-title, + .footer-sidebar .widget_twentyfourteen_ephemera .entry-meta, + .footer-sidebar .widget_twentyfourteen_ephemera .wp-caption-text, + .footer-sidebar .widget_twentyfourteen_ephemera .post-format-archive-link, + .footer-sidebar .widget_twentyfourteen_ephemera .entry-content table, + .primary-sidebar .widget_twentyfourteen_ephemera .entry-title, + .primary-sidebar .widget_twentyfourteen_ephemera .entry-meta, + .primary-sidebar .widget_twentyfourteen_ephemera .wp-caption-text, + .primary-sidebar .widget_twentyfourteen_ephemera .post-format-archive-link, + .primary-sidebar .widget_twentyfourteen_ephemera .entry-content table { + font-size: 11px; + line-height: 1.6363636363; + } + + .footer-sidebar .widget_archive li, + .footer-sidebar .widget_categories li, + .footer-sidebar .widget_links li, + .footer-sidebar .widget_meta li, + .footer-sidebar .widget_nav_menu li, + .footer-sidebar .widget_pages li, + .footer-sidebar .widget_recent_comments li, + .footer-sidebar .widget_recent_entries li, + .primary-sidebar .widget_archive li, + .primary-sidebar .widget_categories li, + .primary-sidebar .widget_links li, + .primary-sidebar .widget_meta li, + .primary-sidebar .widget_nav_menu li, + .primary-sidebar .widget_pages li, + .primary-sidebar .widget_recent_comments li, + .primary-sidebar .widget_recent_entries li { + border-top: 0; + padding: 0 0 6px; + } + + .footer-sidebar .widget_archive li:last-child, + .footer-sidebar .widget_categories li:last-child, + .footer-sidebar .widget_links li:last-child, + .footer-sidebar .widget_meta li:last-child, + .footer-sidebar .widget_nav_menu li:last-child, + .footer-sidebar .widget_pages li:last-child, + .footer-sidebar .widget_recent_comments li:last-child, + .footer-sidebar .widget_recent_entries li:last-child, + .primary-sidebar .widget_archive li:last-child, + .primary-sidebar .widget_categories li:last-child, + .primary-sidebar .widget_links li:last-child, + .primary-sidebar .widget_meta li:last-child, + .primary-sidebar .widget_nav_menu li:last-child, + .primary-sidebar .widget_pages li:last-child, + .primary-sidebar .widget_recent_comments li:last-child, + .primary-sidebar .widget_recent_entries li:last-child { + padding: 0; + } + + .footer-sidebar .widget_categories li ul, + .footer-sidebar .widget_nav_menu li ul, + .footer-sidebar .widget_pages li ul, + .primary-sidebar .widget_categories li ul, + .primary-sidebar .widget_nav_menu li ul, + .primary-sidebar .widget_pages li ul { + border-top: 0; + margin-top: 6px; + } + + #supplementary { + padding: 0; + } + + .footer-sidebar { + font-size: 12px; + line-height: 1.5; + } + + .featured-content { + padding-left: 182px; + } + + .grid .featured-content .hentry { + width: 33.3333333%; + } + + .grid .featured-content .hentry:nth-child( 2n+1 ) { + clear: none; + } + + .grid .featured-content .hentry:nth-child( 3n+1 ) { + clear: both; + } + + .grid .featured-content .entry-header { + height: 120px; + } +} + +@media screen and (min-width: 1040px) { + .site-content .has-post-thumbnail .entry-header { + margin-top: -48px; + } + + .archive-header, + .comments-area, + .image-navigation, + .page-header, + .page-content, + .post-navigation, + .site-content .entry-header, + .site-content .entry-content, + .site-content .entry-summary, + .site-content footer.entry-meta { + padding-right: 15px; + padding-left: 15px; + } + + .full-width .archive-header, + .full-width .comments-area, + .full-width .image-navigation, + .full-width .page-header, + .full-width .page-content, + .full-width .post-navigation, + .full-width .site-content .entry-header, + .full-width .site-content .entry-content, + .full-width .site-content .entry-summary, + .full-width .site-content footer.entry-meta { + padding-right: 30px; + padding-left: 30px; + } +} + +@media screen and (min-width: 1080px) { + .search-box .search-field { + width: 324px; + } + + .site-content, + .site-main .widecolumn { + margin-left: 222px; + } + + .site:before { + width: 222px; + } + + .search-box-wrapper, + .featured-content { + padding-left: 222px; + } + + #secondary { + width: 162px; + } + + .secondary-navigation, + .secondary-navigation ul ul { + width: 222px; + } + + .secondary-navigation ul li:hover > ul, + .secondary-navigation ul li.focus > ul { + left: 202px; + } + + .slider .featured-content .entry-title { + font-size: 33px; + } + + .slider .featured-content .entry-header, + .slider-control-paging { + width: 534px; + } + + .slider-control-paging { + padding-left: 24px; + } + + .slider-control-paging li { + margin: 12px 12px 12px 0; + } + + .slider-control-paging a { + height: 24px; + width: 24px; + } + + .slider-control-paging a:before { + top: 6px; + left: 6px; + } +} + +@media screen and (min-width: 1110px) { + .archive-header, + .comments-area, + .image-navigation, + .page-header, + .page-content, + .post-navigation, + .site-content .entry-header, + .site-content .entry-content, + .site-content .entry-summary, + .site-content footer.entry-meta { + padding-right: 30px; + padding-left: 30px; + } +} + +@media screen and (min-width: 1218px) { + .archive-header, + .comments-area, + .image-navigation, + .page-header, + .page-content, + .post-navigation, + .site-content .entry-header, + .site-content .entry-content, + .site-content .entry-summary, + .site-content footer.entry-meta { + margin-right: 54px; + } + + .full-width .archive-header, + .full-width .comments-area, + .full-width .image-navigation, + .full-width .page-header, + .full-width .page-content, + .full-width .post-navigation, + .full-width .site-content .entry-header, + .full-width .site-content .entry-content, + .full-width .site-content .entry-summary, + .full-width .site-content footer.entry-meta { + margin-right: auto; + } +} + +@media screen and (min-width: 1260px) { + .site-content blockquote.alignleft, + .site-content blockquote.alignright { + width: -webkit-calc(50% + 18px); + width: calc(50% + 18px); + } + + .site-content blockquote.alignleft { + margin-left: -18%; + } + + .site-content blockquote.alignright { + margin-right: -18%; + } +} + + +/** + * 12.0 Print + * ----------------------------------------------------------------------------- + */ + +@media print { + body { + background: none !important; /* Brute force since user agents all print differently. */ + color: #2b2b2b; + font-size: 12pt; + } + + .site, + .site-header, + .hentry, + .site-content .entry-header, + .site-content .entry-content, + .site-content .entry-summary, + .site-content .entry-meta, + .page-content, + .archive-header, + .page-header, + .contributor-info, + .comments-area, + .attachment .entry-attachment .attachment { + max-width: 100%; + } + + #site-header img, + .search-toggle, + .site-navigation, + .site-content nav, + .edit-link, + .page-links, + .widget-area, + .more-link, + .post-format-archive-link, + .comment-respond, + .comment-list .reply, + .comment-reply-login, + #secondary, + .site-footer, + .slider-control-paging, + .slider-direction-nav { + display: none; + } + + .site-title a, + .entry-meta, + .entry-meta a, + .featured-content .hentry, + .featured-content a { + color: #2b2b2b; + } + + .entry-content a, + .entry-summary a, + .page-content a, + .comment-content a { + text-decoration: none; + } + + .site-header, + .post-thumbnail, + a.post-thumbnail:hover, + .site-content .entry-header, + .site-footer, + .featured-content, + .featured-content .entry-header { + background: transparent; + } + + .header-main { + padding: 48px 10px; + } + + .site-title { + float: none; + font-size: 19pt; + } + + .content-area { + padding-top: 0; + } + + .list-view .site-content .hentry { + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + margin-bottom: 48px; + padding-bottom: 24px; + } + + .post-thumbnail img { + margin: 0 10px 24px; + } + + .site-content .has-post-thumbnail .entry-header { + padding-top: 0; + } + + .site-content footer.entry-meta { + margin: 24px auto; + } + + .entry-meta .tag-links a { + color: #fff; + } + + .singular .site-content .hentry.has-post-thumbnail { + margin-top: 0; + } + + .gallery-columns-1.gallery-size-medium, + .gallery-columns-1.gallery-size-thumbnail, + .gallery-columns-2.gallery-size-thumbnail, + .gallery-columns-3.gallery-size-thumbnail { + display: block; + } + + .archive-title, + .page-title { + margin: 0 10px 48px; + } + + .featured-content .hentry { + margin-bottom: 48px; + } + + .featured-content .post-thumbnail, + .slider .featured-content .post-thumbnail { + padding-top: 0; + } + + .featured-content .post-thumbnail img { + position: relative; + } + + .featured-content .entry-header { + padding: 0 10px 24px; + } + + .featured-content .entry-meta { + font-size: 9pt; + margin-bottom: 11px; + } + + .featured-content .cat-links { + font-weight: 900; + } + + .featured-content .entry-title { + font-size: 25pt; + line-height: 36px; + } +} diff --git a/wp-content/themes/twentyfourteen/tag.php b/wp-content/themes/twentyfourteen/tag.php new file mode 100644 index 0000000..8f6e69b --- /dev/null +++ b/wp-content/themes/twentyfourteen/tag.php @@ -0,0 +1,60 @@ + + +
        +
        + + + +
        +

        + + %s
        ', $term_description ); + endif; + ?> + + + + +
        + + + +
        +
        + + + +
        +

        + +

        +
        + + +
        +
        + + + +
        +
        + + + +
        +
        +

        +

        + + +
        +
        + +
        +
        + + \ No newline at end of file diff --git a/wp-content/themes/twentythirteen/archive.php b/wp-content/themes/twentythirteen/archive.php new file mode 100644 index 0000000..9e0dd4d --- /dev/null +++ b/wp-content/themes/twentythirteen/archive.php @@ -0,0 +1,55 @@ + + +
        +
        + + +
        +

        +
        + + + + + + + + + + + + +
        +
        + + + \ No newline at end of file diff --git a/wp-content/themes/twentythirteen/author-bio.php b/wp-content/themes/twentythirteen/author-bio.php new file mode 100644 index 0000000..ae2f522 --- /dev/null +++ b/wp-content/themes/twentythirteen/author-bio.php @@ -0,0 +1,34 @@ + + + \ No newline at end of file diff --git a/wp-content/themes/twentythirteen/author.php b/wp-content/themes/twentythirteen/author.php new file mode 100644 index 0000000..3d76ba3 --- /dev/null +++ b/wp-content/themes/twentythirteen/author.php @@ -0,0 +1,62 @@ + + +
        +
        + + + + + +
        +

        ' . get_the_author() . '' ); ?>

        +
        + + + + + + + + + + + + + + + + + + +
        +
        + + + \ No newline at end of file diff --git a/wp-content/themes/twentythirteen/category.php b/wp-content/themes/twentythirteen/category.php new file mode 100644 index 0000000..a221239 --- /dev/null +++ b/wp-content/themes/twentythirteen/category.php @@ -0,0 +1,41 @@ + + +
        +
        + + +
        +

        + + +
        + +
        + + + + + + + + + + + + +
        +
        + + + \ No newline at end of file diff --git a/wp-content/themes/twentythirteen/comments.php b/wp-content/themes/twentythirteen/comments.php new file mode 100644 index 0000000..3d1aff4 --- /dev/null +++ b/wp-content/themes/twentythirteen/comments.php @@ -0,0 +1,59 @@ + + +
        + + +

        + ' . get_the_title() . '' ); + ?> +

        + +
          + 'ol', + 'short_ping' => true, + 'avatar_size' => 74, + ) ); + ?> +
        + + 1 && get_option( 'page_comments' ) ) : + ?> + + + + +

        + + + + + + +
        \ No newline at end of file diff --git a/wp-content/themes/twentythirteen/content-aside.php b/wp-content/themes/twentythirteen/content-aside.php new file mode 100644 index 0000000..fbc01e6 --- /dev/null +++ b/wp-content/themes/twentythirteen/content-aside.php @@ -0,0 +1,38 @@ + + +
        > +
        + →', 'twentythirteen' ), + the_title( '', '', false ) + ) ); + + wp_link_pages( array( 'before' => '', 'link_before' => '', 'link_after' => '' ) ); + ?> +
        + +
        + + + ', '' ); ?> + + + + + + + + ', '' ); ?> + +
        +
        diff --git a/wp-content/themes/twentythirteen/content-audio.php b/wp-content/themes/twentythirteen/content-audio.php new file mode 100644 index 0000000..73a0d69 --- /dev/null +++ b/wp-content/themes/twentythirteen/content-audio.php @@ -0,0 +1,44 @@ + + +
        > +
        + +

        + +

        + +

        + +
        + +
        +
        + →', 'twentythirteen' ), + the_title( '', '', false ) + ) ); + + wp_link_pages( array( 'before' => '', 'link_before' => '', 'link_after' => '' ) ); + ?> +
        +
        + +
        + + ', '' ); ?> + + + + +
        +
        diff --git a/wp-content/themes/twentythirteen/content-chat.php b/wp-content/themes/twentythirteen/content-chat.php new file mode 100644 index 0000000..6a40b89 --- /dev/null +++ b/wp-content/themes/twentythirteen/content-chat.php @@ -0,0 +1,38 @@ + + +
        > +
        + +

        + +

        + +

        + +
        + +
        + →', 'twentythirteen' ), + the_title( '', '', false ) + ) ); + + wp_link_pages( array( 'before' => '', 'link_before' => '', 'link_after' => '' ) ); + ?> +
        + +
        + + ', '' ); ?> +
        +
        diff --git a/wp-content/themes/twentythirteen/content-gallery.php b/wp-content/themes/twentythirteen/content-gallery.php new file mode 100644 index 0000000..a43647f --- /dev/null +++ b/wp-content/themes/twentythirteen/content-gallery.php @@ -0,0 +1,52 @@ + + +
        > +
        + +

        + +

        + +

        + +
        + +
        + + →', 'twentythirteen' ), + the_title( '', '', false ) + ) ); + + wp_link_pages( array( 'before' => '', 'link_before' => '', 'link_after' => '' ) ); + ?> + + + +
        + +
        + + + + + ' . __( 'Leave a comment', 'twentythirteen' ) . '', __( 'One comment so far', 'twentythirteen' ), __( 'View all % comments', 'twentythirteen' ) ); ?> + + + ', '' ); ?> + + + + +
        +
        diff --git a/wp-content/themes/twentythirteen/content-image.php b/wp-content/themes/twentythirteen/content-image.php new file mode 100644 index 0000000..01e2f34 --- /dev/null +++ b/wp-content/themes/twentythirteen/content-image.php @@ -0,0 +1,48 @@ + + +
        > +
        + +

        + +

        + +

        + +
        + +
        + →', 'twentythirteen' ), + the_title( '', '', false ) + ) ); + + wp_link_pages( array( 'before' => '', 'link_before' => '', 'link_after' => '' ) ); + ?> +
        + +
        + + + + + ' . __( 'Leave a comment', 'twentythirteen' ) . '', __( 'One comment so far', 'twentythirteen' ), __( 'View all % comments', 'twentythirteen' ) ); ?> + + + ', '' ); ?> + + + + +
        +
        diff --git a/wp-content/themes/twentythirteen/content-link.php b/wp-content/themes/twentythirteen/content-link.php new file mode 100644 index 0000000..cc02d82 --- /dev/null +++ b/wp-content/themes/twentythirteen/content-link.php @@ -0,0 +1,43 @@ + + +
        > +
        +

        + +

        + + +
        + +
        + →', 'twentythirteen' ), + the_title( '', '', false ) + ) ); + + wp_link_pages( array( 'before' => '', 'link_before' => '', 'link_after' => '' ) ); + ?> +
        + + +
        + + + + +
        + +
        diff --git a/wp-content/themes/twentythirteen/content-none.php b/wp-content/themes/twentythirteen/content-none.php new file mode 100644 index 0000000..d9549e1 --- /dev/null +++ b/wp-content/themes/twentythirteen/content-none.php @@ -0,0 +1,31 @@ + + + + +
        + + +

        Get started here.', 'twentythirteen' ), admin_url( 'post-new.php' ) ); ?>

        + + + +

        + + + + +

        + + + +
        diff --git a/wp-content/themes/twentythirteen/content-quote.php b/wp-content/themes/twentythirteen/content-quote.php new file mode 100644 index 0000000..ac4de67 --- /dev/null +++ b/wp-content/themes/twentythirteen/content-quote.php @@ -0,0 +1,34 @@ + + +
        > +
        + →', 'twentythirteen' ), + the_title( '', '', false ) + ) ); + + wp_link_pages( array( 'before' => '', 'link_before' => '', 'link_after' => '' ) ); + ?> +
        + +
        + + + + + ' . __( 'Leave a comment', 'twentythirteen' ) . '', __( 'One comment so far', 'twentythirteen' ), __( 'View all % comments', 'twentythirteen' ) ); ?> + + + ', '' ); ?> +
        +
        diff --git a/wp-content/themes/twentythirteen/content-status.php b/wp-content/themes/twentythirteen/content-status.php new file mode 100644 index 0000000..e0e51f3 --- /dev/null +++ b/wp-content/themes/twentythirteen/content-status.php @@ -0,0 +1,32 @@ + + +
        > +
        + →', 'twentythirteen' ), + the_title( '', '', false ) + ) ); + + wp_link_pages( array( 'before' => '', 'link_before' => '', 'link_after' => '' ) ); + ?> +
        + +
        + + ', '' ); ?> + + + + +
        +
        diff --git a/wp-content/themes/twentythirteen/content-video.php b/wp-content/themes/twentythirteen/content-video.php new file mode 100644 index 0000000..8118a08 --- /dev/null +++ b/wp-content/themes/twentythirteen/content-video.php @@ -0,0 +1,48 @@ + + +
        > +
        + +

        + +

        + +

        + +
        + +
        + →', 'twentythirteen' ), + the_title( '', '', false ) + ) ); + + wp_link_pages( array( 'before' => '', 'link_before' => '', 'link_after' => '' ) ); + ?> +
        + +
        + + + + + ' . __( 'Leave a comment', 'twentythirteen' ) . '', __( 'One comment so far', 'twentythirteen' ), __( 'View all % comments', 'twentythirteen' ) ); ?> + + + ', '' ); ?> + + + + +
        +
        diff --git a/wp-content/themes/twentythirteen/content.php b/wp-content/themes/twentythirteen/content.php new file mode 100644 index 0000000..98b6c31 --- /dev/null +++ b/wp-content/themes/twentythirteen/content.php @@ -0,0 +1,64 @@ + + +
        > +
        + +
        + +
        + + + +

        + +

        + +

        + + + +
        + + +
        + +
        + +
        + →', 'twentythirteen' ), + the_title( '', '', false ) + ) ); + + wp_link_pages( array( 'before' => '', 'link_before' => '', 'link_after' => '' ) ); + ?> +
        + + +
        + + + + + + + +
        +
        diff --git a/wp-content/themes/twentythirteen/css/editor-style.css b/wp-content/themes/twentythirteen/css/editor-style.css new file mode 100644 index 0000000..5684189 --- /dev/null +++ b/wp-content/themes/twentythirteen/css/editor-style.css @@ -0,0 +1,771 @@ +/* +Theme Name: Twenty Thirteen +Description: Used to style the TinyMCE editor. +*/ + + +/** + * Table of Contents: + * + * 1.0 - Body + * 2.0 - Headings + * 3.0 - Text Elements + * 4.0 - Links + * 5.0 - Alignment + * 6.0 - Tables + * 7.0 - Images + * 8.0 - Galleries + * 9.0 - Audio/Video + * 10.0 - Post Formats + * 11.0 - RTL + * ---------------------------------------------------------------------------- + */ + + +/** + * 1.0 Body + * ---------------------------------------------------------------------------- + */ + +html .mceContentBody { + font-size: 100%; + max-width: 604px; +} + +body { + color: #141412; + font-family: "Source Sans Pro", Helvetica, sans-serif; + line-height: 1.5; + text-rendering: optimizeLegibility; + vertical-align: baseline; +} + + +/** + * 2.0 Headings + * ---------------------------------------------------------------------------- + */ + +h1, +h2, +h3, +h4, +h5, +h6 { + clear: both; + font-family: Bitter, Georgia, serif; + line-height: 1.3; +} + +h1 { + font-size: 48px; + margin: 33px 0; +} + +h2 { + font-size: 30px; + margin: 25px 0; +} + +h3 { + font-size: 22px; + margin: 22px 0; +} + +h4 { + font-size: 20px; + margin: 25px 0; +} + +h5 { + font-size: 18px; + margin: 30px 0; +} + +h6 { + font-size: 16px; + margin: 36px 0; +} + +hr { + background: url(../images/dotted-line.png) repeat center top; + background-size: 4px 4px; + border: 0; + height: 1px; + margin: 0 0 24px; +} + + +/** + * 3.0 Text Elements + * ---------------------------------------------------------------------------- + */ + +p { + margin: 0 0 24px; +} + +ol, +ul { + margin: 16px 0; + padding: 0 0 0 40px; +} + +ul { + list-style-type: square; +} + +ol { + list-style: decimal outside; +} + +li > ul, +li > ol { + margin: 0; +} + +dl { + margin: 0 20px; +} + +dt { + font-weight: bold; +} + +dd { + margin: 0 0 20px; +} + +strong { + font-weight: bold; +} + +code, +kbd, +pre, +samp { + font-family: monospace, serif; + font-size: 14px; +} + +pre { + background: #f5f5f5; + color: #666; + font-family: monospace; + font-size: 14px; + margin: 20px 0; + overflow: auto; + padding: 20px; + white-space: pre; + white-space: pre-wrap; + word-wrap: break-word; +} + +blockquote, +q { + quotes: none; +} + +blockquote:before, +blockquote:after, +q:before, +q:after { + content: ""; + content: none; +} + +blockquote { + font-size: 24px; + font-style: italic; + font-weight: 300; + margin: 24px 40px; +} + +blockquote blockquote { + margin-right: 0; +} + +blockquote cite, +blockquote small { + font-size: 14px; + font-weight: normal; + text-transform: uppercase; +} + +cite { + border-bottom: 0; +} + +abbr[title] { + border-bottom: 1px dotted; +} + +address { + font-style: italic; + margin: 0 0 24px; +} + +del { + color: #333; +} + +ins { + background: #fff9c0; + border: none; + color: #333; + text-decoration: none; +} + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + + +/** + * 4.0 Links + * ---------------------------------------------------------------------------- + */ + +a { + color: #ca3c08; + text-decoration: none; +} + +a:visited { + color: #ac0404; +} + +a:focus { + outline: thin dotted; +} + +a:active, +a:hover { + color: #ea9629; + outline: 0; +} + +a:hover { + text-decoration: underline; +} + + +/** + * 5.0 Alignment + * ---------------------------------------------------------------------------- + */ + +.alignleft { + float: left; + margin: 5px 20px 5px 0; +} + +.alignright { + float: right; + margin: 5px 0 5px 20px; +} + +.aligncenter { + display: block; + margin: 5px auto; +} + +img.alignnone { + margin: 5px 0; +} + + +/** + * 6.0 Tables + * ---------------------------------------------------------------------------- + */ + +table { + border-bottom: 1px solid #ededed; + border-collapse: collapse; + border-spacing: 0; + font-size: 14px; + line-height: 2; + margin: 0 0 20px; + width: 100%; +} + +caption, +th, +td { + font-weight: normal; + text-align: left; +} + +caption { + font-size: 16px; + margin: 20px 0; +} + +th { + font-weight: bold; + text-transform: uppercase; +} + +td { + border-top: 1px solid #ededed; + padding: 6px 10px 6px 0; +} + + +/** + * 7.0 Images + * ---------------------------------------------------------------------------- + */ + +img { + height: auto; + max-width: 100%; + vertical-align: middle; +} + +.wp-caption { + background: transparent; + border: none; + margin: 0; + padding: 0; + text-align: left; +} + +.html5-captions .wp-caption { + padding: 0; +} + +.wp-caption.alignleft { + margin: 5px 10px 5px 0; +} + +.html5-captions .wp-caption.alignleft { + margin-right: 20px; +} + +.wp-caption.alignright { + margin: 5px 0 5px 10px; +} + +.wp-caption.alignright img, +.wp-caption.alignright .wp-caption-dd { + padding-left: 10px; +} + +.html5-captions .wp-caption.alignright { + margin-left: 20px; +} + +.html5-captions .wp-caption.alignright img, +.html5-captions .wp-caption.alignright .wp-caption-dd { + padding: 0; +} + +.wp-caption-dt { + margin: 0; +} + +.wp-caption .wp-caption-text, +.wp-caption-dd { + color: #220e10; + font-size: 18px; + font-style: italic; + font-weight: 300; + line-height: 1.5; + margin-bottom: 24px; + padding: 0; +} + +.mceTemp + ul, +.mceTemp + ol { + list-style-position: inside; +} + + +/** + * 8.0 Galleries + * ---------------------------------------------------------------------------- + */ + +.gallery .gallery-item { + float: left; + margin: 0 4px 4px 0; + overflow: hidden; + padding: 0; + position: relative; +} + +.gallery-columns-1 .gallery-item { + max-width: 100%; + width: auto; +} + +.gallery-columns-2 .gallery-item { + max-width: 48%; + max-width: -webkit-calc(50% - 14px); + max-width: calc(50% - 14px); + width: auto; +} + +.gallery-columns-3 .gallery-item { + max-width: 32%; + max-width: -webkit-calc(33.3% - 11px); + max-width: calc(33.3% - 11px); + width: auto; +} + +.gallery-columns-4 .gallery-item { + max-width: 23%; + max-width: -webkit-calc(25% - 9px); + max-width: calc(25% - 9px); + width: auto; +} + +.gallery-columns-5 .gallery-item { + max-width: 19%; + max-width: -webkit-calc(20% - 8px); + max-width: calc(20% - 8px); + width: auto; +} + +.gallery-columns-6 .gallery-item { + max-width: 15%; + max-width: -webkit-calc(16.7% - 7px); + max-width: calc(16.7% - 7px); + width: auto; +} + +.gallery-columns-7 .gallery-item { + max-width: 13%; + max-width: -webkit-calc(14.28% - 7px); + max-width: calc(14.28% - 7px); + width: auto; +} + +.gallery-columns-8 .gallery-item { + max-width: 11%; + max-width: -webkit-calc(12.5% - 6px); + max-width: calc(12.5% - 6px); + width: auto; +} + +.gallery-columns-9 .gallery-item { + max-width: 9%; + max-width: -webkit-calc(11.1% - 6px); + max-width: calc(11.1% - 6px); + width: auto; +} + +.gallery-columns-1 .gallery-item:nth-of-type(1n), +.gallery-columns-2 .gallery-item:nth-of-type(2n), +.gallery-columns-3 .gallery-item:nth-of-type(3n), +.gallery-columns-4 .gallery-item:nth-of-type(4n), +.gallery-columns-5 .gallery-item:nth-of-type(5n), +.gallery-columns-6 .gallery-item:nth-of-type(6n), +.gallery-columns-7 .gallery-item:nth-of-type(7n), +.gallery-columns-8 .gallery-item:nth-of-type(8n), +.gallery-columns-9 .gallery-item:nth-of-type(9n) { + margin-right: 0; +} + +.gallery-columns-1 .gallery-item:nth-of-type(1n), +.gallery-columns-2 .gallery-item:nth-of-type(2n - 1), +.gallery-columns-3 .gallery-item:nth-of-type(3n - 2), +.gallery-columns-4 .gallery-item:nth-of-type(4n - 3), +.gallery-columns-5 .gallery-item:nth-of-type(5n - 4), +.gallery-columns-6 .gallery-item:nth-of-type(6n - 5), +.gallery-columns-7 .gallery-item:nth-of-type(7n - 6), +.gallery-columns-8 .gallery-item:nth-of-type(8n - 7), +.gallery-columns-9 .gallery-item:nth-of-type(9n - 8) { + margin-left: 12px; /* Compensate for the default negative margin on .gallery, which can't be changed. */ +} + +.gallery .gallery-caption { + background-color: rgba(0, 0, 0, 0.7); + box-sizing: border-box; + color: #fff; + font-size: 14px; + line-height: 1.3; + margin: 0; + max-height: 50%; + opacity: 0; + padding: 2px 8px; + position: absolute; + bottom: 0; + left: 0; + text-align: left; + -webkit-transition: opacity 400ms ease; + transition: opacity 400ms ease; + width: 100%; +} + +.gallery .gallery-caption:before { + box-shadow: 0 -10px 15px #000 inset; + content: ""; + height: 100%; + min-height: 49px; + position: absolute; + left: 0; + top: 0; + width: 100%; +} + +.gallery-item:hover .gallery-caption { + opacity: 1; +} + +.gallery-columns-7 .gallery-caption, +.gallery-columns-8 .gallery-caption, +.gallery-columns-9 .gallery-caption { + display: none; +} + + +/** + * 9.0 Audio/Video + * ---------------------------------------------------------------------------- + */ +.mejs-mediaelement, +.mejs-container .mejs-controls { + background: #220e10; +} + +.mejs-controls .mejs-time-rail .mejs-time-loaded, +.mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-current { + background: #fff; +} + +.mejs-controls .mejs-time-rail .mejs-time-current { + background: #ea9629; +} + +.mejs-controls .mejs-time-rail .mejs-time-total, +.mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-total { + background: #595959; +} + +.mejs-controls .mejs-time-rail span, +.mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-total, +.mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-current { + border-radius: 0; +} + + +/** + * 10.0 Post Formats + * ---------------------------------------------------------------------------- + */ + +/* Aside */ +.post-format-aside { + background-color: #f7f5e7; +} + +.post-format-aside blockquote { + font-size: 100%; + font-weight: normal; +} + +.post-format-aside cite { + font-size: 100%; + text-transform: none; +} + +.post-format-aside cite:before { + content: "\2014"; + margin-right: 5px; +} + +/* Audio */ +.post-format-audio { + background-color: #db572f; +} + +.post-format-audio a { + color: #fbfaf3; +} + +.post-format-audio:before { + background: url(../images/dotted-line.png) repeat-y 85px 0; + background-size: 4px 4px; + content: "\f109"; + display: block; + float: left; + font-family: Genericons; + font-size: 64px; + -webkit-font-smoothing: antialiased; + height: 100%; + line-height: 1; + width: 120px; +} + +/* Chat */ +.post-format-chat { + background-color: #eadaa6; +} + +.post-format-chat a { + color: #722d19; +} + +/* Gallery */ +.post-format-gallery { + background-color: #fbca3c; +} + +.post-format-gallery a { + color: #722d19; +} + +/* Image: same as Standard/Defaults */ + +/* Link */ +.post-format-link { + background-color: #f7f5e7; +} + +/* Quote */ +.post-format-quote { + background-color: #210d10; + color: #f7f5e7; +} + +.post-format-quote a { + color: #e63f2a; +} + +.post-format-quote blockquote { + font-size: 28px; + font-style: italic; + font-weight: 300; + margin: 0; + padding-left: 75px; + position: relative; +} + +.post-format-quote blockquote:before { + content: '\201C'; + font-size: 140px; + font-weight: 400; + line-height: .8; + padding-right: 25px; + position: absolute; + left: -15px; + top: -3px; +} + +.post-format-quote blockquote small, +.post-format-quote blockquote cite { + display: block; + font-size: 16px; +} + +.format-quote .entry-content cite a { + border-bottom: 1px dotted #fff; + color: #fff; +} + +.format-quote .entry-content cite a:hover { + text-decoration: none; +} + + +/* Status */ +.post-format-status { + background-color: #722d19; + color: #f7f5e7; + font-style: italic; + font-weight: 300; + padding: 0; + padding-left: 35px; +} + +.post-format-status.mceContentBody { + font-size: 24px; +} + +.post-format-status:before { + background: url(../images/dotted-line.png) repeat-y left bottom; + background-size: 4px 4px; + content: ""; + display: block; + float: left; + height: 100%; + position: relative; + left: -30px; + width: 1px; +} + +.post-format-status > p:first-child:before { + background-color: rgba(0, 0, 0, 0.65); + content: ""; + height: 3px; + width: 13px; + margin-top: 13px; + position: absolute; + left: 9px; +} + +.post-format-status a { + color: #eadaa6; +} + +/* Video */ +.post-format-video { + background-color: #db572f; +} + +.post-format-video a { + color: #fbfaf3; +} + + +/** + * 11.0 RTL + * ---------------------------------------------------------------------------- + */ + +html .mceContentBody.rtl { + direction: rtl; + unicode-bidi: embed; +} + +.rtl ol, +.rtl ul { + padding: 0 40px 0 0; +} + +.rtl .wp-caption, +.rtl tr th { + text-align: right; +} + +.rtl td { + padding: 6px 0 6px 10px; + text-align: right; +} + +.rtl blockquote blockquote { + margin-left: 0; + margin-right: 24px; +} + +.rtl.post-format-audio:before, +.rtl.post-format-status:before, +.rtl.post-format-status > p:first-child:before { + background: none; + content: none; +} diff --git a/wp-content/themes/twentythirteen/css/ie.css b/wp-content/themes/twentythirteen/css/ie.css new file mode 100644 index 0000000..5724b94 --- /dev/null +++ b/wp-content/themes/twentythirteen/css/ie.css @@ -0,0 +1,288 @@ +/* +Styles for older IE versions (previous to IE9). +*/ + + + +.genericon:before:hover, +.menu-toggle:after:hover, +.date a:before:hover, +.entry-meta .author a:before:hover, +.format-audio .entry-content:before:hover, +.comments-link a:before:hover, +.tags-links a:first-child:before:hover, +.categories-links a:first-child:before:hover, +.edit-link > a:before:hover, +.attachment-meta:before:hover, +.attachment-meta a:before:hover, +.comment-awaiting-moderation:before:hover, +.comment-reply-link:before:hover, +.comment-reply-title small a:before:hover, +.bypostauthor > .comment-body .fn:before:hover { + text-decoration: none; +} + +.nav-menu .sub-menu ul, +.nav-menu .children ul { + left: 100%; +} + +.site-header .home-link { + max-width: 1040px; +} + +.site-header .search-form [type="search"], +.site-header .search-form [type="text"] { + padding-top: 6px; +} + +img.alignright { + margin-right: 0; +} + +img.alignleft { + margin-left: 0; +} + +.site-main .sidebar-inner { + width: 1040px; +} + +.site-main .widget-area { + margin-right: 60px; +} + +.format-image .entry-content .size-full { + margin: 0; + max-width: 604px; +} + +.gallery-columns-1 .gallery-item, +.gallery-columns-2 .gallery-item, +.gallery-columns-3 .gallery-item { + max-width: none; +} + +.gallery img { + width: auto; +} + +.gallery-caption { + background: #000; + filter: alpha(opacity=0); +} + +.gallery-item:hover .gallery-caption { + filter: alpha(opacity=70); +} + +.comment { + clear: both; +} + +.comment-meta, +.comment-content, +.comment-list .reply { + width: 480px; +} + +.depth-2 .comment-meta, +.depth-2 .comment-content, +.comment-list .depth-2 .reply { + width: 460px; +} + +.depth-3 .comment-meta, +.depth-3 .comment-content, +.comment-list .depth-3 .reply { + width: 440px; +} + +.depth-4 .comment-meta, +.depth-4 .comment-content, +.comment-list .depth-4 .reply { + width: 420px; +} + +.depth-5 .comment-meta, +.depth-5 .comment-content, +.comment-list .depth-5 .reply { + width: 400px; +} + +.comment-meta { + margin-bottom: 0; +} + +.widget { + background: #f7f5e7; +} + +.site-footer .widget { + background: none; +} + +/* Internet Explorer 8 */ +.ie8 .site { + border: 0; +} + +.ie8 img.size-full, +.ie8 img.size-large { + height: auto; + width: auto; +} + +.ie8 .sidebar .entry-header, +.ie8 .sidebar .entry-content, +.ie8 .sidebar .entry-summary, +.ie8 .sidebar .entry-meta { + max-width: 724px; +} + +.ie8 .author-info { + margin-left: 0; +} + +.ie8 .paging-navigation .nav-previous .meta-nav { + padding: 5px 0 8px; + width: 40px; +} + +.ie8 .paging-navigation .nav-next { + line-height: 1; +} + +.ie8 .format-status .entry-content:before, +.ie8 .format-status .entry-meta:before { + content: none; +} + +.ie8 .site-main .widget-area { + margin-right: 0; +} + +/* Internet Explorer 7 */ +.ie7 audio, +.ie7 canvas, +.ie7 video { + display: inline; + zoom: 1; +} + +.ie7 legend { + margin-left: -7px; +} + +.ie7 button, +.ie7 input, +.ie7 select, +.ie7 textarea { + vertical-align: middle; +} + +.ie7 button, +.ie7 input[type="button"], +.ie7 input[type="reset"], +.ie7 input[type="submit"] { + overflow: visible; +} + +.ie7 input[type="checkbox"], +.ie7 input[type="radio"] { + height: 13px; + width: 13px; +} + +.ie7 .screen-reader-text { + clip: rect(1px 1px 1px 1px); /* IE7 */ +} + +.ie7 .site-header { + position: relative; + z-index: 1; +} + +.ie7 .main-navigation { + max-width: 930px; + padding-right: 150px; +} + +.ie7 .nav-menu li a, +.ie7 .nav-menu li { + display: block; + float: left; +} + +.ie7 .nav-menu ul { + top: 40px; +} + +.ie7 .nav-menu .sub-menu, +.ie7 .nav-menu .children { + display: none; + overflow: visible; +} + +.ie7 ul.nav-menu li:hover > ul, +.ie7 .nav-menu ul li:hover > ul { + display: block; +} + +.ie7 .site-header .search-form [type="search"], +.ie7 .site-header .search-form [type="text"] { + background-color: #fff; + border: 2px solid #c3c0ab; + cursor: text; + height: 28px; + outline: 0; + width: 150px; +} + +.ie7 .entry-header, +.ie7 .entry-content, +.ie7 .entry-summary, +.ie7 .entry-meta { + width: 604px; +} + +.ie7 .format-status .entry-content, +.ie7 .format-status .entry-meta { + padding-left: 60px; +} + +.ie7 .sidebar .format-status .entry-content, +.ie7 .sidebar .format-status .entry-meta { + padding-left: 60px; +} + +.ie7 .sidebar .post-navigation .nav-links, +.ie7 .sidebar .paging-navigation .nav-links { + width: 604px; +} + +.ie7 .paging-navigation .meta-nav { + padding: 0 0 10px; + vertical-align: middle; + width: 40px; +} + +.ie7 .comments-title, +.ie7 .comment-list, +.ie7 .comment-reply-title, +.ie7 .comment-respond .comment-form { + width: 604px; +} + +.ie7 .site-footer .widget-area { + max-width: none; + left: auto; +} + +/* RTL for Internet Explorer 7 & 8 */ +.rtl .format-audio .entry-content:before, +.rtl .comment-reply-link:before, +.rtl .comment-reply-login:before { + -ms-filter: "FlipH"; + filter: FlipH; +} diff --git a/wp-content/themes/twentythirteen/footer.php b/wp-content/themes/twentythirteen/footer.php new file mode 100644 index 0000000..725251c --- /dev/null +++ b/wp-content/themes/twentythirteen/footer.php @@ -0,0 +1,26 @@ + + + +
        + + +
        + + +
        +
        + + + + + \ No newline at end of file diff --git a/wp-content/themes/twentythirteen/functions.php b/wp-content/themes/twentythirteen/functions.php new file mode 100644 index 0000000..5d0b0a3 --- /dev/null +++ b/wp-content/themes/twentythirteen/functions.php @@ -0,0 +1,552 @@ + for posts and comments. + add_theme_support( 'automatic-feed-links' ); + + /* + * Switches default core markup for search form, comment form, + * and comments to output valid HTML5. + */ + add_theme_support( 'html5', array( + 'search-form', 'comment-form', 'comment-list', 'gallery', 'caption' + ) ); + + /* + * This theme supports all available post formats by default. + * See http://codex.wordpress.org/Post_Formats + */ + add_theme_support( 'post-formats', array( + 'aside', 'audio', 'chat', 'gallery', 'image', 'link', 'quote', 'status', 'video' + ) ); + + // This theme uses wp_nav_menu() in one location. + register_nav_menu( 'primary', __( 'Navigation Menu', 'twentythirteen' ) ); + + /* + * This theme uses a custom image size for featured images, displayed on + * "standard" posts and pages. + */ + add_theme_support( 'post-thumbnails' ); + set_post_thumbnail_size( 604, 270, true ); + + // This theme uses its own gallery styles. + add_filter( 'use_default_gallery_style', '__return_false' ); +} +add_action( 'after_setup_theme', 'twentythirteen_setup' ); + +/** + * Return the Google font stylesheet URL, if available. + * + * The use of Source Sans Pro and Bitter by default is localized. For languages + * that use characters not supported by the font, the font can be disabled. + * + * @since Twenty Thirteen 1.0 + * + * @return string Font stylesheet or empty string if disabled. + */ +function twentythirteen_fonts_url() { + $fonts_url = ''; + + /* Translators: If there are characters in your language that are not + * supported by Source Sans Pro, translate this to 'off'. Do not translate + * into your own language. + */ + $source_sans_pro = _x( 'on', 'Source Sans Pro font: on or off', 'twentythirteen' ); + + /* Translators: If there are characters in your language that are not + * supported by Bitter, translate this to 'off'. Do not translate into your + * own language. + */ + $bitter = _x( 'on', 'Bitter font: on or off', 'twentythirteen' ); + + if ( 'off' !== $source_sans_pro || 'off' !== $bitter ) { + $font_families = array(); + + if ( 'off' !== $source_sans_pro ) + $font_families[] = 'Source Sans Pro:300,400,700,300italic,400italic,700italic'; + + if ( 'off' !== $bitter ) + $font_families[] = 'Bitter:400,700'; + + $query_args = array( + 'family' => urlencode( implode( '|', $font_families ) ), + 'subset' => urlencode( 'latin,latin-ext' ), + ); + $fonts_url = add_query_arg( $query_args, "//fonts.googleapis.com/css" ); + } + + return $fonts_url; +} + +/** + * Enqueue scripts and styles for the front end. + * + * @since Twenty Thirteen 1.0 + */ +function twentythirteen_scripts_styles() { + /* + * Adds JavaScript to pages with the comment form to support + * sites with threaded comments (when in use). + */ + if ( is_singular() && comments_open() && get_option( 'thread_comments' ) ) + wp_enqueue_script( 'comment-reply' ); + + // Adds Masonry to handle vertical alignment of footer widgets. + if ( is_active_sidebar( 'sidebar-1' ) ) + wp_enqueue_script( 'jquery-masonry' ); + + // Loads JavaScript file with functionality specific to Twenty Thirteen. + wp_enqueue_script( 'twentythirteen-script', get_template_directory_uri() . '/js/functions.js', array( 'jquery' ), '2014-06-08', true ); + + // Add Source Sans Pro and Bitter fonts, used in the main stylesheet. + wp_enqueue_style( 'twentythirteen-fonts', twentythirteen_fonts_url(), array(), null ); + + // Add Genericons font, used in the main stylesheet. + wp_enqueue_style( 'genericons', get_template_directory_uri() . '/genericons/genericons.css', array(), '3.03' ); + + // Loads our main stylesheet. + wp_enqueue_style( 'twentythirteen-style', get_stylesheet_uri(), array(), '2013-07-18' ); + + // Loads the Internet Explorer specific stylesheet. + wp_enqueue_style( 'twentythirteen-ie', get_template_directory_uri() . '/css/ie.css', array( 'twentythirteen-style' ), '2013-07-18' ); + wp_style_add_data( 'twentythirteen-ie', 'conditional', 'lt IE 9' ); +} +add_action( 'wp_enqueue_scripts', 'twentythirteen_scripts_styles' ); + +/** + * Filter the page title. + * + * Creates a nicely formatted and more specific title element text for output + * in head of document, based on current view. + * + * @since Twenty Thirteen 1.0 + * + * @param string $title Default title text for current view. + * @param string $sep Optional separator. + * @return string The filtered title. + */ +function twentythirteen_wp_title( $title, $sep ) { + global $paged, $page; + + if ( is_feed() ) + return $title; + + // Add the site name. + $title .= get_bloginfo( 'name', 'display' ); + + // Add the site description for the home/front page. + $site_description = get_bloginfo( 'description', 'display' ); + if ( $site_description && ( is_home() || is_front_page() ) ) + $title = "$title $sep $site_description"; + + // Add a page number if necessary. + if ( ( $paged >= 2 || $page >= 2 ) && ! is_404() ) + $title = "$title $sep " . sprintf( __( 'Page %s', 'twentythirteen' ), max( $paged, $page ) ); + + return $title; +} +add_filter( 'wp_title', 'twentythirteen_wp_title', 10, 2 ); + +/** + * Register two widget areas. + * + * @since Twenty Thirteen 1.0 + */ +function twentythirteen_widgets_init() { + register_sidebar( array( + 'name' => __( 'Main Widget Area', 'twentythirteen' ), + 'id' => 'sidebar-1', + 'description' => __( 'Appears in the footer section of the site.', 'twentythirteen' ), + 'before_widget' => '', + 'before_title' => '

        ', + 'after_title' => '

        ', + ) ); + + register_sidebar( array( + 'name' => __( 'Secondary Widget Area', 'twentythirteen' ), + 'id' => 'sidebar-2', + 'description' => __( 'Appears on posts and pages in the sidebar.', 'twentythirteen' ), + 'before_widget' => '', + 'before_title' => '

        ', + 'after_title' => '

        ', + ) ); +} +add_action( 'widgets_init', 'twentythirteen_widgets_init' ); + +if ( ! function_exists( 'twentythirteen_paging_nav' ) ) : +/** + * Display navigation to next/previous set of posts when applicable. + * + * @since Twenty Thirteen 1.0 + */ +function twentythirteen_paging_nav() { + global $wp_query; + + // Don't print empty markup if there's only one page. + if ( $wp_query->max_num_pages < 2 ) + return; + ?> + + post_parent ) : get_adjacent_post( false, '', true ); + $next = get_adjacent_post( false, '', false ); + + if ( ! $next && ! $previous ) + return; + ?> + + ' . __( 'Sticky', 'twentythirteen' ) . '
        '; + + if ( ! has_post_format( 'link' ) && 'post' == get_post_type() ) + twentythirteen_entry_date(); + + // Translators: used between list items, there is a space after the comma. + $categories_list = get_the_category_list( __( ', ', 'twentythirteen' ) ); + if ( $categories_list ) { + echo '' . $categories_list . ''; + } + + // Translators: used between list items, there is a space after the comma. + $tag_list = get_the_tag_list( '', __( ', ', 'twentythirteen' ) ); + if ( $tag_list ) { + echo '' . $tag_list . ''; + } + + // Post author + if ( 'post' == get_post_type() ) { + printf( '', + esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ), + esc_attr( sprintf( __( 'View all posts by %s', 'twentythirteen' ), get_the_author() ) ), + get_the_author() + ); + } +} +endif; + +if ( ! function_exists( 'twentythirteen_entry_date' ) ) : +/** + * Print HTML with date information for current post. + * + * Create your own twentythirteen_entry_date() to override in a child theme. + * + * @since Twenty Thirteen 1.0 + * + * @param boolean $echo (optional) Whether to echo the date. Default true. + * @return string The HTML-formatted post date. + */ +function twentythirteen_entry_date( $echo = true ) { + if ( has_post_format( array( 'chat', 'status' ) ) ) + $format_prefix = _x( '%1$s on %2$s', '1: post format name. 2: date', 'twentythirteen' ); + else + $format_prefix = '%2$s'; + + $date = sprintf( '', + esc_url( get_permalink() ), + esc_attr( sprintf( __( 'Permalink to %s', 'twentythirteen' ), the_title_attribute( 'echo=0' ) ) ), + esc_attr( get_the_date( 'c' ) ), + esc_html( sprintf( $format_prefix, get_post_format_string( get_post_format() ), get_the_date() ) ) + ); + + if ( $echo ) + echo $date; + + return $date; +} +endif; + +if ( ! function_exists( 'twentythirteen_the_attached_image' ) ) : +/** + * Print the attached image with a link to the next attached image. + * + * @since Twenty Thirteen 1.0 + */ +function twentythirteen_the_attached_image() { + /** + * Filter the image attachment size to use. + * + * @since Twenty thirteen 1.0 + * + * @param array $size { + * @type int The attachment height in pixels. + * @type int The attachment width in pixels. + * } + */ + $attachment_size = apply_filters( 'twentythirteen_attachment_size', array( 724, 724 ) ); + $next_attachment_url = wp_get_attachment_url(); + $post = get_post(); + + /* + * Grab the IDs of all the image attachments in a gallery so we can get the URL + * of the next adjacent image in a gallery, or the first image (if we're + * looking at the last image in a gallery), or, in a gallery of one, just the + * link to that image file. + */ + $attachment_ids = get_posts( array( + 'post_parent' => $post->post_parent, + 'fields' => 'ids', + 'numberposts' => -1, + 'post_status' => 'inherit', + 'post_type' => 'attachment', + 'post_mime_type' => 'image', + 'order' => 'ASC', + 'orderby' => 'menu_order ID' + ) ); + + // If there is more than 1 attachment in a gallery... + if ( count( $attachment_ids ) > 1 ) { + foreach ( $attachment_ids as $attachment_id ) { + if ( $attachment_id == $post->ID ) { + $next_id = current( $attachment_ids ); + break; + } + } + + // get the URL of the next image attachment... + if ( $next_id ) + $next_attachment_url = get_attachment_link( $next_id ); + + // or get the URL of the first image attachment. + else + $next_attachment_url = get_attachment_link( array_shift( $attachment_ids ) ); + } + + printf( '%3$s', + esc_url( $next_attachment_url ), + the_title_attribute( array( 'echo' => false ) ), + wp_get_attachment_image( $post->ID, $attachment_size ) + ); +} +endif; + +/** + * Return the post URL. + * + * @uses get_url_in_content() to get the URL in the post meta (if it exists) or + * the first link found in the post content. + * + * Falls back to the post permalink if no URL is found in the post. + * + * @since Twenty Thirteen 1.0 + * + * @return string The Link format URL. + */ +function twentythirteen_get_link_url() { + $content = get_the_content(); + $has_url = get_url_in_content( $content ); + + return ( $has_url ) ? $has_url : apply_filters( 'the_permalink', get_permalink() ); +} + +if ( ! function_exists( 'twentythirteen_excerpt_more' ) && ! is_admin() ) : +/** + * Replaces "[...]" (appended to automatically generated excerpts) with ... + * and a Continue reading link. + * + * @since Twenty Thirteen 1.4 + * + * @param string $more Default Read More excerpt link. + * @return string Filtered Read More excerpt link. + */ +function twentythirteen_excerpt_more( $more ) { + $link = sprintf( '%2$s', + esc_url( get_permalink( get_the_ID() ) ), + /* translators: %s: Name of current post */ + sprintf( __( 'Continue reading %s ', 'twentythirteen' ), '' . get_the_title( get_the_ID() ) . '' ) + ); + return ' … ' . $link; +} +add_filter( 'excerpt_more', 'twentythirteen_excerpt_more' ); +endif; + +/** + * Extend the default WordPress body classes. + * + * Adds body classes to denote: + * 1. Single or multiple authors. + * 2. Active widgets in the sidebar to change the layout and spacing. + * 3. When avatars are disabled in discussion settings. + * + * @since Twenty Thirteen 1.0 + * + * @param array $classes A list of existing body class values. + * @return array The filtered body class list. + */ +function twentythirteen_body_class( $classes ) { + if ( ! is_multi_author() ) + $classes[] = 'single-author'; + + if ( is_active_sidebar( 'sidebar-2' ) && ! is_attachment() && ! is_404() ) + $classes[] = 'sidebar'; + + if ( ! get_option( 'show_avatars' ) ) + $classes[] = 'no-avatars'; + + return $classes; +} +add_filter( 'body_class', 'twentythirteen_body_class' ); + +/** + * Adjust content_width value for video post formats and attachment templates. + * + * @since Twenty Thirteen 1.0 + */ +function twentythirteen_content_width() { + global $content_width; + + if ( is_attachment() ) + $content_width = 724; + elseif ( has_post_format( 'audio' ) ) + $content_width = 484; +} +add_action( 'template_redirect', 'twentythirteen_content_width' ); + +/** + * Add postMessage support for site title and description for the Customizer. + * + * @since Twenty Thirteen 1.0 + * + * @param WP_Customize_Manager $wp_customize Customizer object. + */ +function twentythirteen_customize_register( $wp_customize ) { + $wp_customize->get_setting( 'blogname' )->transport = 'postMessage'; + $wp_customize->get_setting( 'blogdescription' )->transport = 'postMessage'; + $wp_customize->get_setting( 'header_textcolor' )->transport = 'postMessage'; +} +add_action( 'customize_register', 'twentythirteen_customize_register' ); + +/** + * Enqueue Javascript postMessage handlers for the Customizer. + * + * Binds JavaScript handlers to make the Customizer preview + * reload changes asynchronously. + * + * @since Twenty Thirteen 1.0 + */ +function twentythirteen_customize_preview_js() { + wp_enqueue_script( 'twentythirteen-customizer', get_template_directory_uri() . '/js/theme-customizer.js', array( 'customize-preview' ), '20141120', true ); +} +add_action( 'customize_preview_init', 'twentythirteen_customize_preview_js' ); diff --git a/wp-content/themes/twentythirteen/genericons/COPYING.txt b/wp-content/themes/twentythirteen/genericons/COPYING.txt new file mode 100644 index 0000000..aece214 --- /dev/null +++ b/wp-content/themes/twentythirteen/genericons/COPYING.txt @@ -0,0 +1,9 @@ +Genericons is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. + +The fonts are distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +As a special exception, if you create a document which uses this font, and embed this font or unaltered portions of this font into the document, this font does not by itself cause the resulting document to be covered by the GNU General Public License. This exception does not however invalidate any other reasons why the document might be covered by the GNU General Public License. If you modify this font, you may extend this exception to your version of the font, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. + +This license does not convey any intellectual property rights to third party trademarks that may be included in the icon font; such marks remain subject to all rights and guidelines of use of their owner. \ No newline at end of file diff --git a/wp-content/themes/twentythirteen/genericons/Genericons-Regular.otf b/wp-content/themes/twentythirteen/genericons/Genericons-Regular.otf new file mode 100644 index 0000000..5cd41e8 Binary files /dev/null and b/wp-content/themes/twentythirteen/genericons/Genericons-Regular.otf differ diff --git a/wp-content/themes/twentythirteen/genericons/LICENSE.txt b/wp-content/themes/twentythirteen/genericons/LICENSE.txt new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/wp-content/themes/twentythirteen/genericons/LICENSE.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/wp-content/themes/twentythirteen/genericons/README.txt b/wp-content/themes/twentythirteen/genericons/README.txt new file mode 100644 index 0000000..7a0a92e --- /dev/null +++ b/wp-content/themes/twentythirteen/genericons/README.txt @@ -0,0 +1,123 @@ + ___ ____ __ _ ____ ____ __ ___ __ __ _ ____ + / __)( __)( ( \( __)( _ \( )/ __)/ \ ( ( \/ ___) +( (_ \ ) _) / / ) _) ) / )(( (__( O )/ /\___ \ + \___/(____)\_)__)(____)(__\_)(__)\___)\__/ \_)__)(____/ + + +Genericons are vector icons embedded in a webfont designed to be clean and simple keeping with a generic aesthetic. + +Use genericons for instant HiDPI, to change icon colors on the fly, or even with CSS effects such as drop-shadows or gradients! + + +_ _ ____ ____ ____ ____ +| | [__ |__| | __ |___ +|__| ___] | | |__] |___ + + +To use it, place the font folder in your stylesheet directory and paste this in your CSS file: + +/* =Genericons, thanks to FontSquirrel.com for conversion! +-------------------------------------------------------------- */ +@font-face { + font-family: 'Genericons'; + src: url('font/genericons-regular-webfont.eot'); + src: url('font/genericons-regular-webfont.eot?#iefix') format('embedded-opentype'), + url('font/genericons-regular-webfont.woff') format('woff'), + url('font/genericons-regular-webfont.ttf') format('truetype'), + url('font/genericons-regular-webfont.svg#genericonsregular') format('svg'); + font-weight: normal; + font-style: normal; + +} + +Note: the above only works if you don't use a CDN. If you do, or don't know what that is, you should use the syntax that's embedded in genericons.css. + +From then on, you can create an icon like this: + +.my-icon:before { + content: '\f101'; + display: inline-block; + -webkit-font-smoothing: antialiased; + font: normal 16px/1 'Genericons'; + vertical-align: top; +} + +This will output a comment icon before every element with the class "my-icon". The "content: '\f101';" part of this CSS is easily copied from the helper tool at http://genericons.com/ + +You can also use the bundled example.css if you'd rather insert the icons using HTML tags. + + +_ _ ____ ___ ____ ____ +|\ | | | | |___ [__ +| \| |__| | |___ ___] + + +Photoshop mockups: + +Genericons-Regular.otf found in the root directory of this zip has not been web-font-ified. So you can drop it in your system fonts folder and use the font in Photoshop if you like. + +For those of you using Genericons in your Photoshop mockup, remember to delete the old version of the font from Font Book, and grab the new one from the zip file. This also affects using it in your webdesigns: if you have an old version of the font installed locally, that's the font that'll be used in your website as well, so if you're missing icons, check for old versions of the font on your system. + +Pixel grid: + +Note that Genericons has been designed for a 16x16 pixel grid. That means it'll look sharp at font-size: 16px exactly. It'll also be crisp at multiples thereof, such as 32px or 64px. It'll also look reasonably crisp at in-between font sizes such as 24px or 48px, but not quite as crisp as 16 or 32. Please don't set the font-size to 17px, though, that'll just look terrible. + +Also note the CSS property "-webkit-font-smoothing: antialiased". That makes the icons look great in WebKit browsers. Please see http://noscope.com/2012/font-smoothing for more info. + +Updates: + +We don't often update icons, but do very carefully when we get good feedback suggesting improvements. Please be mindful if you upgrade, and check that the updated icons behave as you intended. + + + +____ _ _ ____ _ _ ____ ____ _ ____ ____ +| |__| |__| |\ | | __ |___ | | | | __ +|___ | | | | | \| |__] |___ |___ |__| |__] + +V3.0.3: +Bunch of updates mostly. +- Two new icons, Dropbox and Fullscreen. +- Updates to all icons containing an exclamation mark. +- Updates to Image and Quote. +- Nicer "Share" icon. +- Bigger default Linkedin icon. + +V3.0.2: +A slew of new stuff and updates. +- Social icons: Skype, Digg, Reddit, Stumbleupon, Pocket. +- New generic icons: heart, lock and print. +- New editing icons: code, bold, italic, image +- New interaction icons: subscribe, unsubscribe, subscribed, reply all, reply, flag. +- The hyperlink icon has been updated to be clearer, chunkier. +- The "home" icon has been updated for style, size and clarity. +- The email icon has been updated for style and clarity, and to fit with the new subscribe icons. +- The document icon has been updated for style. +- The "pin" icon has been updated for style and clarity. +- The Twitter icon has been scaled down to fit with the other social icons. + +V3.0.1: +Mostly maintenance. +- Fixed an issue with the example page that showed an old "top" icon instead of the actual NEW "refresh" icon. +- Added inverse Google+ and Path. +- Replaced tabs with spaces in the helper CSS. +- Changed the Genericons.com copy/paste tool to serve span's instead of div's for casual icon insertion. It's being converted to "inline-block" anyway. + +V3.0: +Mainly maintenance and a few new icons. +- Fast forward, rewind, PollDaddy, Notice, Info, Help, Portfolio +- Updated the feed icon. It's a bit smaller now for consistency, the previous one was rather big. +- So, the previous version numbering, 2.09, wasn't very PHP version compare friendly. So from now on it'll be 3.0, 3.1 etc. Props Ipstenu. +- Genericons.com now has a mini release blog. +- The CSS has prettier formatting, props Konstantin Obenland. + +V2.09: +Updated Facebook icon to new version. Updated Instagram logo to use new one-color version. Updated Google+ icon to use same radius as Instagram and Facebook. Added a bunch of new icons, cog, unapprove, cart, media player buttons, tablet, send to tablet. + +V2.06: +Included Base64 encoded version. This is necessary for Genericons to work with CDNs in Firefox. Firefox blocks fonts linked from a different domain. A CDN (typically s.example.com) usually puts the font on a subdomain, and is hence blocked in Firefox. + +V2.05: +Added a bunch of new icons, including upload to cloud, download to cloud, many more. + +V2: +Initial public release \ No newline at end of file diff --git a/wp-content/themes/twentythirteen/genericons/example.html b/wp-content/themes/twentythirteen/genericons/example.html new file mode 100644 index 0000000..cdc7d04 --- /dev/null +++ b/wp-content/themes/twentythirteen/genericons/example.html @@ -0,0 +1,464 @@ + + + +Genericons + + + + + +
        + +

        Genericons Usage

        + +

        Copy the font folder and the genericons.css file together into your project. Link the CSS in your HTML:

        + +

        <link href="path/to/genericons.css" rel="stylesheet">

        + +

        Drop in the following HTML with the name of the icon you want to display:

        + +

        <div class="genericon genericon-standard"></div>

        + +
        + + +
        +
        +
        + +
        +
        +
        + +
        +
        + + +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        + + +
        +
        +
        +
        +
        +
        +
        +
        +
        + + + +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        + + +
        +
        +
        +
        + +
        + +

        If you want to insert an icon manually using the :before selector, you can setup CSS rules like the following example. Make sure to set the size to a multiple of 16px or the icons could end up looking fuzzy:

        + +

        + +

        Add a matching class to your HTML:

        + +

        <div class="my-icon">You're a Star!</div>

        + +

        Here's the result: You're a Star!

        + +

        Examples

        + +

        Turn every icon a Salmon color:

        + +

        + +

        Or turn the stars Gold:

        + +

        + +

        Use icons for bulleted lists:

        + +
          +
        • One
        • +
        • Two
        • +
        • Three
        • +
        • Four
        • +
        + +

        + +

        + +

        Use icons to style blockquotes:

        + +
        Sometimes I've believed as many as six impossible things before breakfast. —Lewis Carroll
        +
        `Twas brillig, and the slithy toves Did gyre and gimble in the wabe: All mimsy were the borogoves, And the mome raths outgrabe. "Beware the Jabberwock, my son! The jaws that bite, the claws that catch! Beware the Jubjub bird, and shun The frumious Bandersnatch!"
        + +

        + +

        + +

        Use icons to style buttons:

        + + View + Listen + +

        + +

        /

        + +

        CSS Preprocessors

        + +

        Preprocessing extensions such as Sass (SCSS Syntax) or LESS can make it easier to manage CSS for a lot of things at once using things like variables and mixins.

        + +

        This example will seup the basic genericon rules and sets a color you can use for all icons using Sass:

        + +

        + +

        Here is a similar example for LESS:

        + +

        + +

        Fallback images for IE7 and below

        + +

        Genericons does not come with fallback icons by default -- therefore you have to create them yourself. If you are using HTML similar to this example: + +

        <span class="genericon genericon-warning"></span>

        + +

        You can use the asterisk hack to serve a different icon to IE7 once you have saved the fallback icons to your project:

        + + + +
        + + + diff --git a/wp-content/themes/twentythirteen/genericons/font/genericons-regular-webfont.eot b/wp-content/themes/twentythirteen/genericons/font/genericons-regular-webfont.eot new file mode 100644 index 0000000..4657469 Binary files /dev/null and b/wp-content/themes/twentythirteen/genericons/font/genericons-regular-webfont.eot differ diff --git a/wp-content/themes/twentythirteen/genericons/font/genericons-regular-webfont.svg b/wp-content/themes/twentythirteen/genericons/font/genericons-regular-webfont.svg new file mode 100644 index 0000000..ef236c1 --- /dev/null +++ b/wp-content/themes/twentythirteen/genericons/font/genericons-regular-webfont.svg @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/wp-content/themes/twentythirteen/genericons/font/genericons-regular-webfont.ttf b/wp-content/themes/twentythirteen/genericons/font/genericons-regular-webfont.ttf new file mode 100644 index 0000000..b6f125e Binary files /dev/null and b/wp-content/themes/twentythirteen/genericons/font/genericons-regular-webfont.ttf differ diff --git a/wp-content/themes/twentythirteen/genericons/font/genericons-regular-webfont.woff b/wp-content/themes/twentythirteen/genericons/font/genericons-regular-webfont.woff new file mode 100644 index 0000000..da8be38 Binary files /dev/null and b/wp-content/themes/twentythirteen/genericons/font/genericons-regular-webfont.woff differ diff --git a/wp-content/themes/twentythirteen/genericons/genericons.css b/wp-content/themes/twentythirteen/genericons/genericons.css new file mode 100644 index 0000000..b10b86f --- /dev/null +++ b/wp-content/themes/twentythirteen/genericons/genericons.css @@ -0,0 +1,197 @@ +/** + + Genericons Helper CSS + +*/ + + +/** + * The font was graciously generated by Font Squirrel (http://www.fontsquirrel.com). We love those guys. + */ + +@font-face { + font-family: 'Genericons'; + src: url('font/genericons-regular-webfont.eot'); +} + +@font-face { + font-family: 'Genericons'; + src: url(data:application/font-woff;charset=utf-8;base64,d09GRgABAAAAAENIABEAAAAAatQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAABgAAAABwAAAAcaii0EkdERUYAAAGcAAAAHQAAACAArQAET1MvMgAAAbwAAABCAAAAYJdbaIVjbWFwAAACAAAAAJgAAAGyqWnWY2N2dCAAAAKYAAAADgAAAA4BYgHJZnBnbQAAAqgAAAGxAAACZVO0L6dnYXNwAAAEXAAAAAgAAAAIAAAAEGdseWYAAARkAAA5fgAAWkD4H3YjaGVhZAAAPeQAAAArAAAANgUfUT9oaGVhAAA+EAAAABwAAAAkEAMH3WhtdHgAAD4sAAAAiAAAAQpVkUB7bG9jYQAAPrQAAAECAAABAoDMauhtYXhwAAA/uAAAACAAAAAgAagCQm5hbWUAAD/YAAABYgAAAthC114IcG9zdAAAQTwAAAHUAAAFCuMEJONwcmVwAABDEAAAAC4AAAAusPIrFHdlYmYAAENAAAAABgAAAAbRQFLPAAAAAQAAAADMPaLPAAAAAM71j4QAAAAAzvWBvnjaY2BkYGDgA2IJBhBgYmAEwnogZgHzGAAJvwCyAAAAeNpjYGb/zDiBgZWBhdWY5QwDA8NMCM10hsEIzAdKYQeh3uF+DA6qf74ys6X9S2Ng4GBg0AAKMyIpUWBgBACOigvWAAB42mNgYGBmgGAZBkYGEFgD5DGC+SwME4C0AhCyMDCo/vnI+Ynzk+Qn1c8cXzi/SH7R/GL5xfNL5JfMLyVfmf//B6tg+MTwSeCTwmeGLwxfBL4ofDH44vAl4EvCl4KvDP//32LnZ+Hj4+PgY+LV4DHk0eZR5ZHnkeQR5uHlYeeugdqOFzCyMcCVMTIBCSZ0BQzDHgAA5FwqMwAAAQkARQBBAGYAfwC3AAB42l1Ru05bQRDdDQ8DgcTYIDnaFLOZkMZ7oQUJxNWNYmQ7heUIaTdykYtxAR9AgUQN2q8ZoKGkSJsGIRdIfEI+IRIza4iiNDs7s3POmTNLypGqd+lrz1PnJJDC3QbNNv1OSLWzAPek6+uNjLSDB1psZvTKdfv+Cwab0ZQ7agDlPW8pDxlNO4FatKf+0fwKhvv8H/M7GLQ00/TUOgnpIQTmm3FLg+8ZzbrLD/qC1eFiMDCkmKbiLj+mUv63NOdqy7C1kdG8gzMR+ck0QFNrbQSa/tQh1fNxFEuQy6axNpiYsv4kE8GFyXRVU7XM+NrBXbKz6GCDKs2BB9jDVnkMHg4PJhTStyTKLA0R9mKrxAgRkxwKOeXcyf6kQPlIEsa8SUo744a1BsaR18CgNk+z/zybTW1vHcL4WRzBd78ZSzr4yIbaGBFiO2IpgAlEQkZV+YYaz70sBuRS+89AlIDl8Y9/nQi07thEPJe1dQ4xVgh6ftvc8suKu1a5zotCd2+qaqjSKc37Xs6+xwOeHgvDQWPBm8/7/kqB+jwsrjRoDgRDejd6/6K16oirvBc+sifTv7FaAAAAAAEAAf//AA942q18C3xU1bnvWnvveSaZmT3PZJKZzHtCJpkJ88hkIIQhCAECCAQCCCooggTkjS9q3Vqpioo9tqJVK2hbsdpj90xA2mJrjtVaW0fLFbmt1h6xp1ptPcfe9rSKmc39vrVnQhBsz/39bmBm7732npm1vvU9/t9jLaIh8Ef/yj1DeKIlBlJLzIRMFP1i2Mbb/DXUZeNdIv2r0vPEE166+An4u/MJ7pnyBZeS0+R0+XVymi6HE+X4aaoQSsb9TSREyxEOvlQjwXfrSA18s424yJVEJgmZlmQhIVtSsqYki0lZn5DtKdlQkh1JuTYh15WoXJ+QhRNFoq9NJpOyrlTUCcbYcF7HG/C9xhCTdZaCncZkV6lgsiaTRbsL79sthlihgcZIx0Sa8TvO9+KgO2Xo7GnCSWVJIGWJk07DNUckiY57KZUj4Sjc1cE/GION9BLZmJDNJdkGHYR+2mEwJ6DHcp2lIEJ/dKWCg8YKYp1oHRYMRj7kypGCzQxXVKsjcNUxkVisIZ9gtXCCL0TszmRnOhKg5BW6mj5KV7/yirJfuUTZT5P7ju/bd5xPjG985RXuIWzdhyQWiEQlnaSVGHVdxE+uZ7SFvvkSciMQMyHzpWEj79DH5JqSrIfeBlhva0tyraVQD731lGSPpWCFM22pEIR+11LRWtAbczm5XpS5nOyBUfAOM/RbtoqyBsbS6IOxaKm1FtscYoHT5GBMNuAYv00jIoVtdpJKkkyaBAPEle70OR12rS8iAYHZ/0+ArHmq+8EPqVY59cMfKJ9IR6nx6FHlb0epxCPNTxNpVBJ8B1aV34a7Y0/uPnp09y3PPIPj5oh+PF9Nx3EX9LWpFDKWIYm8BYxVl6SyJSGTE7KQBErIvKWgp4wU2qRcY4GxxoBYOGsEB+AXaeWVghfQVoHuKHCEA0fwUn1XiHprVALRwSYtzgEHFyJcCvABDTAV3sNTCfimjqQJlU2sK9AvTWnYoCEwKcYS8pKhVDAD5Y1EtALFCxoDHPkccnCFdjpRI8bh207SnpN3bz1Ntt6tkfafPLn/C8+3lP8gcfe3PM94FH5JS4iROMhKImsTspgCZpStSeSJGkaZWiCIk/WCUUP9/aKRR8kxakGmgEI1QBRTSTZZZAdyUNFhwrsOEeTKpcoVEMdOgmKyM+M/cwryIynHjw/t46onQDSQr+PKcUr2DY07JRzSjNGlgaTIPoKiDnMSS8he4NA065++VNQT/GG9AN3SWwpu6Fa8VIy7sTE+ERrjlkIdNDpKxToHNtZBF2WHpRCFRn+pGPVjYzQE/c4Add164GtjfS5XqIsD/9a4PDHg30LUAc3e1hzwdawGJVYMTWQySsV0Z9ahdYgonxkxHc14KVwAH+MdmBY412XwTiSAT7kcMENkaDC/5cCW/OAQ42aCfD3WxI1QafX+8H25JYq0YMuWBVRakrsvvH+1IgFjcxqKh91K5RHKHlHUR0DWgbvIiA5pZiVB0kZkf0K2pXCKgMFrU0wThRJy/QmQ6EIY5qkgWICNGmAkDcBGKX+S9Tjop2IwEKFZPw5KbYsB2x5YJZBVBw6sUvJKXlp1gEfN8vivsEVS8sjR7Ca8K3k6ckBZJf3qcSqdaSGEp1U50EAPfWRmRctT7Kj+BOoks6XghKlpKhUCMB9mmI9ho9VWj1rEKRYafDgHFGTgsNZgdjibKrMAHabhznQ06+VRElw9NB2BC+qwm6gOf5TJZaa/f4V7gscyOXNR34UX9q1Ydnl8YBJPkNE+hVd///H+FY1TZsyNzr+z86K+o7882rdi+Qc3L33srslo/uCV1oNGIevIBiJfkZAvKcmtqEGofCXjxs6S3GkpNFKU2MJ66H0n9LPYP29BDvRko/i0xuLovmDJZUzVX3IFcJTlMrjRKuZrjDYPaWlL52cPXooD1VgPBULhjiQbnJi2klAqKRCrw0I02kgm3ZlJR3sEfOMi0Tg1cbpIVKuL82aqdWkddi/v0upMNE6jcSHaSk3U6fIKLq+uM2tHNRENkUepje765TG6i1ofVa5TfhEK0BnzrpMGs+u1Rr3ZJtSlui/PXr1nz9XZy3oSRuOkjvXZQem6uZnapqnLlvo4gyfQ6RFqGwyimzd43IE6ytdZm0OdUxbFaSCk/EK5TiC/pF+AL39U+U9l9zGlUP7jOl1zg/D8wpsnG5pnDT217ZGt5pZZl06knGCdGPZznD88UdRy3D03bN+/7amhWT594qI6E+3KCnXBxnpOV+O2wtiau/y83t3Q3OAEXZS8Vqj3addxTrRxOnxjc2MmjYzzJ5E+soDsIMU6QmJypITao7kkd6nztZDZNwuIhaVwIcxXbxLV6yKYsgtBHvJ1mto6wdnUHGppz0yexearPgLtRgOxtfZMzfcumIvT1Cwe0tMmz2Q877IW/YkLcmjj6ilMmA/mywJqHkw3b7e6Okk2Eq2l0awzlOWiWkKd/mSW47XE5rT1CNlIKBjQUi/n6hRcXNTE2bwUPmPNhr6FM0UfgpftW99SPlR2K2vg9WFox8Yb6Hffs+SVd5Wtf/c9R/+6567h55Q/U/FXdNbho/7v/Va57W9rf649MO+O9RO+qBz5gU+iC5yeqPYJOvd695f7nv77YtOkFZ6HXq5X/sQnz/3+b8HvcrMPKq9eW6Kd8zqkwWT9V5yz4tT9tyXK0U8fGFlA2+gtc5RjmvWPKY9xk3w9vaEv3mMpb/GkFtf6tY3UM5y7dEh5tPF+5ef3baSLR+JMfiTaBjjkN6DNYdgpXxY41JlKwmEKsGicZtJZp+BC/k4lXZ1ZrQ5fyLImXgj6pI4WSn52zTOhqDeRvPHxBUvnLkvuoXveMf7q/gMbpfWt11y1dvYm2rPz6XeUX39LeZUe03yDu3uzrs7981s0MT756CVXLH7iFzXR9vv/9w731Fv66to3L9D59Nd//MEv7l+KfSOAkXQSiZILCKpIUJYBMG9JWUzIvpTsLMlulXVaAHeeQDAKMNRgAVwpuwBLpQoTgHlcgOZkd47BhPHaVPTb/FNQv7qykWDAxHloEMFDICLtG9KQoX37hpR3qalWeTfW+5h2/vpL7lnWpijltqF9iBHw9qfwzr1IhZHa7iz9P8bsJTsv+JMyWs4hwAOLTyTNe9D3BjKf6VMHs+K2ZJFQNG7EBRYPUIVetexupv+5JHZdTBZd9fiMy2GIFesZNq4nYAsbKzY8JaZ7uFTS2Ux54FAP5+fRmHPSb9Nrn7wqO+R26/5tborONikvKCP8SzRBufl7NuW1PK+8m59helU5NnqEn01A21fpawbcsRiQx1qyl8h1CXlpSW5OFMJpwGSNpcKEOKD4RSqh142T0W6Q0QuT8ppSsXsN9rG7H4a0xlJYBe0guFcC7btRcA0ouDbnkuUXM6FtXorCTPUGYrcFsn0rL161BmW1UTzkjM3qR0UsL7IWWjpQaq0WaydIrROkVgtSG0GppVpbKtk5lXY6tTqtjtp40LadLqfa5qVqYw+XSaOuNSDjulCSBYpsHYnytNMKWho4WCft/YjOpRvp3I/27v1IOaR8TTn0UfpUSblx5u50eGMw4LCZ7G0TaUS+YYndbLfvvjCyIRi02KjZEptIgwvrATnU2zmbxqKt1eh5fv4k4ybl/QdfVR6iF27ZsedmgfuY3nrkjcs1U/g5n/kVOOO4Pym71gieh6hJw/G0OcBruNH7OJEu03EBHzVio63ByUHrw7T2wtxKf3x5JiB4jY019SanaDfmBukVm58/9XV/XKvhDpb3DtHtb7463NJ66wOqfzE2tzPIcnIFeYjISxNyS0qeXUK+AxA5HRyNlGwvFafbcfqme2H6GoAX16pzjJ4bOmpg8WV3Ug6Btk4WAyF8NNAF3LgO5lcHHscwb5q5AmctIOaNmhrvhFhv/+LB1WyuZ8NcF0lsJqjgAm+Cc128C+3udPEItfiDockrL2Pm1Cbi5KCZpK6ANhjgM6qkeqhfDIp+hwrrUWrBzIJ51cP9LDtNZf0BLd9DXWBPNS6cVZBgW6TTBd/k1AJrSDeUvB6fu9lrnW07cp8q2uCknGaqDyCtotFcfDcfNIdsHlHUx+ceumjgwK3lR278/YzcG9LiObbBULfHo9PR8qElt01z3L3ruh85HdKuG16i79Lf38hPyfm7wx4qaKehRlD9H/zqUfiVJufdT23g3LVNYqO93mFMz5x815GtRzr2Xnbqm0vWU9pQN7lhYmBigyds0V8hdD7ya0H4/TcPjAjCL4mKycCAap8Br94CunkWQ9owB3wCcEwVaasT5IEJ8pYYUtYBtinUmYDCHrEghhCWhepF6yGLua09rqIyu3MyBQAZp6A6bKA3gMLpbA9NJREjw3mcA2Wo0WX8XmrhAKVdsZBbvJauGRhYZ6NzlKcBls2usQ9OnTTXT2fn1t2+KNSbSvh9jhrlCIU/rTj7sstm969aferb/L+P+rkJnY3JmZNWzyj/J9e15bsbsjW2xsZgk3iX+23lPeU/Lz6LT5sAe2bJDUwDARL2x0DtdDBn0Oc7IcqdKdkG/pdFdsP4u9j4wQO2MCfYy/wG2a9yawwcEkuhTVVDOSCL18NMgOwXC/UuIE7AKmdyckwsdHQiiXxwu9CSUV3h8SYC0PbnkosRixkOoNWYyUCbQMnCaXT6ALegd/oiC9WBF/x1qtdbZqR2U/3B25MLuwIW5ePxRmSfcO2kCy+c1D1v/qdH+IbR9+jRdltL17CyjL74vafr2yINW4AZngRAtQCw1DTyXVJ0In4yJ+QJJaSQFgjSywiSKckZS6EJRg52MmAptDOXuTAdDp3uH/bUfDSHOGJGk9wAVBwp2OkncmRk2GqP2GJFePft8e0JakFMc+SQ1d7gjsTxj447l/NuWmjKgCC7clNQkANiUevswLN2a8E8AanZMQF9NNLco0o2mCoEyk6rw84J4L9EOVDQ0UjWpmIKJ3MGtKi+rSzqYOIcdhBeHaLlaIR7su/eYzT2lEwTL+94QvnZi5d/LzDbErj4Xp3n0Za71g4sC08xua67YucPLlc++PiOD7+xbMCq01kMuqDzxi8Jf7rqN688fOl1Lymf3vk35eqTF+eV3+Z2fbXz4C5OXnjNHUc3LErd81zu8q98n058+gQ1XX7wzWu/usbhrp/SUm8xpKgaXhvDsINkNymakO4AO2Yn5C60kcwmLmWkD5fksKWQAkrPLclzLYWZcDa5JE9W3V/wPZYBI85NAW1iiYHFqC9nikdMGltz1zTLArycbC04pyIBnSb0QhYDTWeDF2IwEps7PCE1eeqCz3geiGSDgWhnFoCpSj4mu+BrOV3OTmDSbGckClRmWAHJDNTPomEErgVVC/ABpsJ1tuOh+gZfvXuOZ1bT3gWPlvdc8tjf9971f75zfW5ondUjcBZeozFd0CeNbH3p5IJ9lyy63FYz0ds3fdF2i96w1VavBbT61Fl+hnIJvP7z0dYd66g703+ETv3ZtuPfvGzeTY8NL9/zWqveZDPkDTanOP/61cVbF7751Nf+fu/OBfGHr27tXXr/1thCm00JD6zecy0dZX70AW6VbpXmAChGM2khTBeyOIlJDRZRNUJjKRiA4nXV4JDV4vR1WiI+oXI88Fe67K9/VR7n7qycCN9VHv9r5ZwdK7iY6G4EF8ZPMgRjnPUl2ZqQTSwOh9E28D7ADZa1GFsrEo0FZcBkHa5r8vhUxncBdzdSaypJic0aDvFwCUyNxi3CowxopcXX2Vcu/MrGb5TpJrq61qL8Sbnjlhn52yz6LVu7Znfb0xOPLZdv1Fy+cbFysvwX5ST93/QnlKcr9LXKgOf+lbJMzRfSWTRh09+/lTD6VGOKZvDjrYRimJMgWsNgKzlXuYUNVDq5XyAYjxqFd45FfdD1xhYF35vRSUd60F8RSdsCejoAnpxsSMC3UjmYkJtOYLTSmSyEkCQWjH/VoZlJiXZmgsGsd2ZFGHUUeVFEoBpEiAYg7Vc/dbtvufTiGzatWHbtl2+f290mivQJZfC02N4xe84G4dHyHdf1Ttvma3bau6h7WaihPf4AfZk20BfuWH7xlHzwLNsRJDEymdzMbAdYCW9CjpbkhoQcSMkJFp4SSrJgAaGlcneFKAhhfcAoquCCp4ADabRgpExOMddzCkhs2AcjCuTkCeKw19PGvMpGjM2QQkMUZLnRF27BtoRYCE04nwEB9z7FAjZ+EEEwcOBP+UMVTgyrxgWckEiMgkieZUWk/oyGZPqVjyzKcWWZctyifERZFPGk8hzX3J+RMv3s7SxDMoSNPOntwXhd2/Ge3mbluZP4oerT/RlQZ4AtKGALhdiJCzzzZqBeFOgXB9+cyglGHowfWjAYL3sZ9GuB9zFz0gF0aXDA6J31Tcjsckg8pNUgnnOhHRgOhFvbEP6xSFyWdiZdFOmho8gGNDKVRm1UDPOusMi7snAe1YiarIG6MpR4uB+LLSL3Y4+n3CvarbZyr+eWb387w2mUd957j3oPvv/BB72c5j3lHep9r/wpvffbvJO+1lxPX6upUdrrm5V2n1Npq6mhx50PbdqkPK48TtPP0q4HnqWp8rMPPfRQOUCXPfgs1/TsA3RZ+dlNvzmLhzJkKXmGPMZ4yF6SexLy90rynETV9fnRONcHsYUaWLoHzq4pydeoxAKm+TGGmNqAaZbm5HvEQ88sX9d7AOlyjbVYJ1yNWqJBzNtqNXZvoCPZ3TNn3qVbbv/6Y/9aHGaAuccOtjUyaTIC5jnguD5N9RZv97zvY7xTswjudSRTuc/xjIRUMoSgOM5FUfJAxwjRSCgasWY7Q1lA1wLHJFLIwjSgYz+V70RD4oqwpwSdltPg/U40G3E0wFoA1U5mR1B44RJvZ+PgUEbQvOCVDo033AS74vJyzGTjBWWP4ldgMIFMwbhXJMSU3nl8rp436bVv/Ynetnby0n0vbd8hRztnb9usPH3wceWDvjl1S5fR9iLn/6Vy8Gf3iY994Vrq2zV31r3lr93Dm+hl1PrQN6n3slDgSuU3+7+hvH7VVWuoqH/gqk3/PnmKs3/mmxcusTtSyZUrF0TSejGVXjwwOVerjTW3JOKz6jiTweGcMbfPFo9Y+2KxFf45Wm5wd+8FV3jqw+9s3taVjQQ/uOlL3+e1Swfv2HbtwIqfUIdxw+K1yl+v2jHlc1y6t5Tb3vz7y7fdvPPYQ0P2jueuu0956tpdWzyNv93/EL3q6w/+L6/W8rZy74dfOz27z5xzfE2598R+GMU26c5duegX79Xqdm7eoPz6+mue9/oHLl7xzpx59u6eSy9bvLjeNdHVN2FZ3yyNtjs7EJ5qcWhoV4z3zvF4/UIsMHdRNKs3NDRfcMW0DQmr5ao752xYF4tt33nddXe6bG/cvnf79tZgU4A6fsJteLZnnn1yz/oNpOoj6gnw/nxyJbmR3EFvIrImIa8tyVJCvjUl31SSdyeL0k3o8kl7DLHiTRKe3vQlcBRvssjXIyoHxBlNyJtSciuIyJ0JOXFCXl8avnB9Qh+TSQkDHxeW5PWWQp6l+2SXRc6W5GwCTgo7oMlXGr7ct0PNcfkshT3QdHNSvr0k35Is3r4Hf+32W+GH99yOp3skcDvvUrGsYevIsIplZ1nkmSOFQe4TednID4UdIy1qc59FnjFSWMp/Ii8fKcyaqYeG4Zl9M2yxQt8MPXxouG/WTFtMHrQMLx1cBq2Dy/TyUsvwsqXLbTFyZMbMvlmDS5ctj1f+6DktDArn14NIZjSbUKxdYnHl2utRcH07QDeK7ihahsKeGtAFE0C0pbXQSgDRoTa4SSw6XUzKo9dDszuxfoeKGuxeQGs94P/GhQSNc2mQPowqxwX0dH0gYBhKBqNqN6G3zLlMvM7EZ9M9fLYHmsEHdoDdAQ+44tMBGNSZABXGeZphTrQDHWopf90LX9j5i39Zl6zzeTpD/iU2m6ve5gq3dfvqLc3eeL39nvuURuXjb8ye55u+8ouzbV16quUESo2NJtuUOXfuSiVnt1hfDcSmheqDA7Paa4O2VM+0UHPt0986+rurU00r4l2XX5B0TbampzRNWjO9w8EfZYAKnGP6y95rLu1KDm6VprfMmNKebfb0mm2xjoTT6Yn09ixPxuZPhQvLkpvyBxd3bbikr1XDiYJZZ6ox69xtcVuDoHPGfJ7++X2WxMKOVrOhRtTxfCiebU2mvFvvOiAc2pQPtuZWbt+R3jrZ5rHmLtq6qXzqjF+uYvovg87vAr6/CP3qvgTLrq5A9V5IA3cBgzYni+ksslw6AbyetSAUAJtQWAnKPU1hzi9cMohznhULgb4cWjorThTv5ZupVwMk16CWFE1qyB/OvBygIL/YAfoT9GtcGw12MBBkovgRXZy/qaZv+syDBwuP3L9rpbuhtuWqi6/ItsQ2br5285VLp4lWytWIvpap4fSmxTNsVv8F07sstGvaK7vWu7jg1EUrVg7k7bbeX+/NtTQ28GJjvcFwUueaNEH45iM/XTl/22QfZ2pqMBo0tllLvvLo725YfvtA1qapq9NplT/ytYFAe7SlzsY1eGvraH0gZgq188Xyu3W+lfO/PffmFXPa/WY95Sw3JKe1r1owb1JbTe1LBt/6TYg37wI6bgc6+sm14JUi3mopFRtakHANDiDchoR8eUlekmApwSXVlCCVr0vI3hPyCnBRS8WAl0WU1oGUewN46iXwyRWWQpyB+GK8jmUNe0D0rwfqB7wgTr5cIb4CjKPgaGjRz9uJAlUnymYQspYGuA1Sd/kGkCpzPMDuLRGH67ykE0/1iNiZV0oxnl1xTHVOHXOPoiA6oQh4SFlw/NH4MfSKmZ3I+H9wH6PhzuoTldvBAE6pw67ewH/wzRXkW71/15dO7r7rmhn9T9Kud3bbUvRLJ2/ZtfHCuU8qP3tntzid3tmZXnrNkX1bN3dPDgSnTFoyb9PyxqDfLwKoXLm6LebzOhoSmUCgoX5SbtHg5js2bsjlsumVl37x4ik5v79n2vr57QlXo9PR5IulgyHNfbtPfqm/dvc7ys+eXLVkaDNcTTJ9+R3a9eTgwI7yX/rnz01MjccXL1m3bEpPJNrYUG/XG6xml90TD4R8vp4OmzMUXJlMtLc3uFuic2avXnvBtJYWN4CyZm8yP6HN6fQF0hNdbr+f+QcgY1rMcSbJCiK3If4uRttYGrcOpzyVkHUnZLFUFHXYKLZiLYjYwN697D0IHKATWaEIBrvTWIihg9l0wLRGEVARllQE7QgThMoOE4laM0Wwbdfqxt5iNOlk2Bu8YSqNTNy0Ok91tW6rf/lMi15PD2T6OyJO+N+fySMeVvLTdvRd1ErB97nkkY9v14jt/qbFDyxaciAc6c9M6K3zR9kbPDrU39LRwsIBJbpXl9JtJxPJJDKbLCJryEayg9xAryaYe5xaki9LyMtLxeWXwWjI8kHg55Usgr4hJc8rFdPrrsG6mK6E/IUUxmEBYTsS8paSvEP1qr6YkNtPyN2l4WR3+5gVTZbkbkuhH2RiQUleYCmshbOhkjxkYcGdSEnehtbYVhq+LjJdj8Gwwo2VoM9P/rJLtYg6i6wfKbiFT+SGkR/++eC/PYLNBXeDHsNB9SOFWrhTN0Ke1ulr6+ob3FXL95lrZve620VrIZEGDdgvDvOaLiZbC6zF1oGlqBbXisNT+5azUP6QdXjCiktYAnW6mDdYHE3eq7Zs3/kFbIhYC6FrMOKxaDlMb3dOnicejrQnQpOnq8m7w+A4kZ3X4QUvFjVNffjdDmtB2wh2c8cW6ILNynyuSnLKBrLq0qBkO5kRjIZ5p0uNMamsgUAZhDdOs3Z4HMMgTrsTYTOGkjFH4GQhKbs2YE+D18KEGy6ZEIfSnexOtegHv5qFUkpXD6zpPvL7lRqr1UFz9QMdc9avn9O3VqOcmvfb73WvG9jZFTe9oDylbFP+9QVLW2ZtS2KJp23CpIVP0OB3n6TBJ55Q3nryu8pb26bFE9N6V3pbzV13/0uXudVrHvzB0UH6L9MugVba0Z5vb8/TgY5YbkK78JWBqwdWG+hLzppazawJE9d/bf3qvm7li7WrBq8eyK5oTE689d3du39/a7KzcXkm0dTfE8q9cuLpoaHDGzbC+ycre3tX9t4f85q7uszemHlw8H3Wwl+PP9Fe/vGUec0dLZMI1qVwGIOWiAd8wzuI3JiQ21KytlTUNqKYaikwdgtj3tpS1XE8U6pTX5Lr1cismKyk7QJqhUer6kLqeZj1RlasVJNjir1Q247soG0EC9sQCrPpFp82mC31zT4/skGtVTbm1PIwtbajh/qcLocummGlHDyLcYUzriy7PYX6WfUS+Lu6xAUJzYvU+aLmG+vhlNKX7tr7Er9w/TfwQveS8h8/4xcee8WfSPjpe7f96NnbNrR3rAzE4wGlec9zP73tf3XEj+O9Xx2746c/qdbr6DCvHSJTmL/oLMkeFm1ATzHMKCGWZFEtPACvMALjbRQxOF+LI/Q4mRVTS1Uq4QKsKOOI3UWzzmTWRTuRt3QGGgnoME0hgHtfLSSjJEKHhPDesIYOhed0ZsLKG8qb4Y0hLPZgeUvpGJab0dX01qGIsk/I5wU6FBmaA8/RSDiMGAhzf8+C39vL6rDU6j5iM2htGZeBZh2UN2glehVnU+4u/5kz063lD4WH6Ta67eHyR5Sz043lPyt3062cWfka/ygNKt9XXuYP0OXKy8qRcnb7OppSSuu2Kz/hfkxnKW8pB/kXaFo5qPwG7QTWwmCtk5U4yLgCGFuiEqSi4rklL5Xxw8iwxgXLk6oDHdqHNSz70P5wwKlarPMLsnyroYR1VMCOHHx7bQLrjUjBgHVaOrU4xQVYmAdQjaZLgi8pS5KU50dOA9ODZwRoUSpX6ge12F+B1JJ6ghWOWBkDU25EZi+YWKcN1C/SM+WAGEIrY+3KEFgNHi4VuBQyeNU/Vm/D+KeZhMnFjIMcjIOQfSIs0KCyDwaogiU5OBZeAPkp+ICRhusEuwf9i4agaD1c69A0hcIsguBxwDVmV/3hasHiWYECK3gNYELTcS5gophLxlKczT+iGvDnNT/avPlHyqfKO8qnPxJXPPj6B68/uEI90G9LtPUivvCZh+CMM5x5Cg7KQ/QNZYVyYgVRa8W0qD+A7MTLZkUoYe4ea0StCbkGa4sKts9MO6koWo6c3E/J/pNlwoopEWBgWPI04fepZZRn6FhDGkkbuapaQRnDqpJirBmVVCwKSqo+AVxQ0BiSrJRQl6RyOxNRtZaSA8qqcWMMAoZKxQmY5CQTPPDJkKWgZYSXDRbsKYa/4tVSukzKwV4irQb5QGb9oIeEdOdkqrJwIJIBFkYuAoABQ/iU9Gd4FogbZcG7iFtyRyLpCFhVCYQS/j6FZ/E+x566KB2JuBUCz7jH1WpVxtxJ7quOOZOQ0ykspjl3rNnzjvWcYXVV8ELDR19CYCCn4yY5NVJo03wit4+QYlt7CtHAMBzTYzhAqwFma4pEWya0MubLmEG+Erl/Sp2UfzLnpS4Pb9eBzo6CQbb9YyKBKaK8089zkrbd7W7SbXzq8+nF+VwcRzmNEcjWpIPnaYHUnkW3asQzQVIkSyaTqWQ6OVKlZFOiEOpIpVgx3kSgoi9RbEl3p6DFD6yRmYLYrS1R6MpDS3upkJsG1+cS/YJxRG8CmeV8cK+5VBACeLRguLzQ0gbn0VKhFQssohZmFNNdcJ4qFTon4ZEVWRS0eTifWiroe/E4NmEz/ikf2qCVh1f1+Hnnn0d56Tx/5yc7Kk+qas1zirDHtzP/mw7SQd31uusBVxDaw2WxAis5lWKlINVFAmaqtbt0UQrqabDW3tVB7/jd4fCGyOFI5DDXfDg8FDkcDh/+nbLzghV0sD29UL0fPhwZCh8un8Sn8JF34H6SjKv/tsGvzWcWx4VzzDStl2laNdbtVvVrM9abYmxbI5gsCDkMiE5IwYUlmaac3CQOU1JjUFVruodLejnw8iiLbcep1YLV0xaCzFxRiZvpf0mK+PXv73z9wfCZwmmhv6I1d37/64oo0f/avOJBjlf2Ysk02FlWOsef1Xc/WVvtvZdVzVXGEDh3DMHKGA7jGLy+84zCKw4TR00Dq5ezygYcVpG67Syy/I9GxWMd/j8e2a2c9M8Gp6iMcPb4JpDhceMLM10WTBa9TIt7W8A5bGaRgmY/qOXK2FvHjb0Fo4koTnIgWYyyGEQ0DJ9qieJpCxa3RMcoFEPpAwrJQk6OikVXE0vfua0FDdZO/P8j1ljE7Z8RrRoe+x+Q7qxYlgAS1KYn2uOkjtVYxpBLahKytYTJuWjFFrIFGUAvcMs9J8YlvMBLbMcclwd4pUbk670sgzNBPGQUrM0BptGjCC90JkeTH9c/YM2Ex4cDFymiCgCLCCqiPOCCZGcW0Cr4VDrO0ulzWrQ+axUQnbqC1tA2WrOGfqpor1D+Wzmu/PeaP9Jt81741fNz6U7lroff3vhCv1DJbu1nsEkg9NS67dvXKhpFs24bYMpTyl3zBwbm0R10+yOL5pc/VB8+yVVhFWDaKi0QzzYCLVIkzzxyoIBBpUA6gXUypNDcCi6GpUnMsenOG4nO7HJ7wpF2LO+VBWtRa7XlquME51LHBkZdZuqiUcr8TRqxZbFsAdUXkiEYsFEni76y8e77t2/fvW4LDEu586PbwhdpyEWj7Sf3t3UqbSY33sCB//k2ei0jyL5/u5QeN8FtddSX3h1fNB8/9yZ+rjyw/6RJaessH7k7juP/863KbUgTehk93tm2/yRR6w05ieHUGkIMHGbTDBS8B06ieWUE3mheUkbYmzLCk7Ov2TNErbdktQ416AvQsS+R+PzoCLzxeWl0hL2NjgDIPeuaPYMyLVUwXw1orHZyC8EqCUR5rmSyaGbCbDaBWIqOBjSkrNC8YAwzm8pkOg4uQbXm3AI8aivJtupqIcwa1LNEbSEBfGsBtFHkAkEMo7vsWMMzQV37YgDGbcPFMJwhx9zFcAVcahyIeMf/U7O0RDWczGwi0OzPUAQeZJRUrB5aOGxAJIJY7DRxoxlkWVWpTLiRcn78C9oFcxpxHbN3hHrB57kXcDAxgGtFeaqpwdbfHKFv0jeP0N+UDx8+JNyoPF1+n85VDnEuOodyrvL7aL9Uv0aCqTWSVpaf0QGVQMQ11fovdLaFEq6IKegxYEYxHm3gdLggBiuWJOQNaRTr7UF1CPCFoEUUcFHU8v8xPx+1iQFXwhgoWwpE0ZHhySm4AyOEMeJ6mnKeB3IoqL8FNtcj2hH4nJ7VqeFnhSzNoozgSwJHbWQUGQ01VvsqbmCVMg/f4ZMjvKTkR+EbMCmg3ivX4XvFR4Rvhm/1MVTGw4gNTNeDx2VE+eWJqEZyKVv0gz0m6kBxSRgu1ygzl64ssSGOszU6tsahF6tHCqbGFKsf0TN30YZpX7bogZ4o6G3AkipSNldX1bDCqka2BgIoPBYIEtkyAH+aC8EpAE03dfgtHAlRsuXAFvivVtacJuC+HztG99KFtOmnm06TXyjfUSKchT2CU6OW3hyjq18Bv4ls+qnyH8r3lG3HqEDfoEt/gWMgYHt1f9Q9xWhdX/FG7Uy7m6HjDQk0b5iLiGRpD3W6qBM9aFvKVu3q/G3LuI9zDz44ifv7sm0HP/kjd0NqOK38helbSl7eK7x+8fTpF38a2/uyhi2tGz1c1a38WG2JlURInFxKEHg0lIoNGLMnDU4wryDoTSU5jnHI1lJloaAZhbpo1uBD5loMBCcScssJuU0NAbW1YJi+IaBhtqapGUQ22qaWUfhR7zpd6AlGWcESVwnJsaVLWlZKlq36ihLt7KdTnrv5/WXhOUORHQ/sP3nl3KHw1of2nwQu/3m/8pPnbv7Dcko5NiGgY8l3j69ZHh6aG9l2cr+yZmhOeDs6lthI6TY2I6SyPoytdYpiVWIEC+2wUNtaKlrZUiYr5jhgCnxqBfpY9KuJrU1DBXZGbemZI88K0s1NoLY07gjaHrtYqG3G5CFYnAYW8NKLhRq2nqbWigqM5tSot2h3+s6sWGKxr1TFvawsaQKu5ghbjgfdB80jwQGvlE8QPvB5VPK4TIlTlyepLuXzSjdecQTvlCW2ZI/VEgFH3qNFeTERJ8w3Lj1D7ewaVwRhV7EUKOSC3YJDEmpzLBdWAUV2LYavquVXVKogoOULlPXK+gUHKwsHxxDPB68tUIbovgW0pPztKN5U7doqtGuat1E9oWJx0SC3SnqbjqB7IfikEY6sKiN/wqTqsb/qukLvuJWqmoqAj4WBcF3VmQWDevIxUV+0srL0zPs4/0EkIfAfqsE9ISkbS0UjW+ZmBHsma6BBNU6+khxKFut9rGy/CW5Zkyz8x9YI8rmCrx6OQXWNoDUb9YtRTOpaXWIkxFGxGSQ3k+aiolPAikmdi5JrN/yOk/4wa8GvDx5SfvM4L9le71sI5zT0ONwRyIPUteF3ZekPfT+4UlY+jCmnvCfojPJRDp/74TqZ2mJU1/y68sOjDyrvb/idmqvhgaYCrsF0VOmEQS0hUdCMLROkkoDqG4lAqnYIJwHp21KN5ejUaJhepQmWWOE3oJY2jH1RmNkgAQwQLrvE4NooOptobQa4vJ5o/h2+0cbQ680Ew0IupjyaWG6kOYlrHUHu/EkMP9eqS+W04wv9zpQqqTIXUIFtuFqkVCtaMeVeCCBaaPI2I48WeBfc0Zsd9erSg2GDyd6gJuCBwxCwd6Z7aNJL7SYaiFRxrKFyb4Du3KL8N/2qNDL41ae+OohvrVsf3rr1Yfpo9Q6f5/b3KM1gMcn6yiODgzSHD21VpLF7Z9klXKGcJEhTdYWykfEtx9Yp47pkdSlyQUMA7uiNcCZUlAPFhXb+RnpG0aMx5NlS1zL5yxkdz401KtLZGt6g4rbKOmnVk6hGRu5ns13L1mm5U3IOy/2wii6Qkqew7FU+Ibem5GklOcJW5iRY700p6Dqu5+1UNcf4gAgMZpgTm0IhVxJtiA8DIXBmwRhUoRujHZNLwwZzTy8+MFldK6oGPAqTu2DgWAOlclcF1zEuCzr8maC1Gj38zNE6DuHZxq8qPwvtAbbBSEaEQbdx/y8ah/suomxhMb4wFoIQ8FNQRYAGRx9jj9PIWYc32GF0XDBErS8FzIXx6kaSIGhVeLY4iGeGhTdgRpQ3ob1sYhoXjUgJK/3RvGN0sbIiiyW7wPtMVXKe0r4hne7o7i9fkji6bf9Jl6tSGcjtO77PE9x9dNUVu07u7+lVF6Gjjsc8hqBG/4GHopH0VLVcz26mJhoFRwKj4y/SOXPe7z8+h3rhOOdYv5KjByg5cBoRpQ/vHu9/f84c5Z3+1/rfn83NUnL8L0+TA8xBpYggNeia6VAn69g4eVVlVMp1q7qiast5Nd5bjfKqueXXNB9q/hVUtHtsbaohoSJBXHYqa9SkELosUabelO8spR8qtqV0Ka5KXzqo2BTbIF0K9sRGX9NK7LuA6bPUD5+KQuOHS5XvoH6iS5fyI+xZ/BjLK+S12H/0LtEjghbAlGB/yiMCAOE8O2PPoZ3K43OAvQ3sgxz4V3klzxMuXwYva0TJj9WU89BJsNciKcIFTDOgUYGRATxOXl2gTkFDQzc/5zmQeVTQ6lL2qp+gkdi2DVZWG43+ri6ByAP9ARa6YQj5U+gjR9RSX2RGC15oJC05a6+H80VJv4/UL1p8HSm2Wr8o+iei4AqJoj2UxjbAeo5wBtv0iWJ9Sxe2GQAkTshhW22i2NTGIql1paKnHSOpFJUHOVEU1L0i+FJRazSxM+b9Fe31TXhlKxWdbi87YzmRYkhVGcFSMYLR0yRmSABQFtNd3UkWQC12TuqBs8K0yn4SZ4Kenw2C/k+uOSL94z9OOnsDijL5f7tmLMGxXBPLC6EOnsryQiD5jVgXUN2zomlc+bJYjeGDFkX470Gbh1Ere+6cTFoggstXqgaCw3X9akoNa43VXTVUG0HUVBuuYNpHh3gyOj5vpfZPC7IcIV8i2JlACZExgqEIA0N6QDwqpGCl2MU6G1vgb0ZdFlXXkyN2kuuSiJSM6qYFLFeMdcahBAbyCj4jrivTaDm1ulgWwGQTIxN0meKlXIdYSo1+G2gGADLYAL8jmDl7yKExe6hu/wC+Jg5VGj/4SpoOvQK4f5qwPSKYaIyRYX/VDWLxDbXOBXNaDVg/ZgSPIIUOokNdx2ms5u60NZhrKWq0SAANNcSKWg3Lm2OBE4AXK9xvKFVXdfrtBD32CMFpwxH4K0c0Mspbb50mbylvsTlib4L0nvJIu/IXWtdOL6XrKAtFoE1Sj5X1AES1Mc0wW4tJMYKrV7zgtqWq1sb7WWsDM+Q/ARPBZiHkB1tbE0G85I0AePL5Q+ih8GKxkQVd/qEpwlL/gIYdKNBbq/2MVcK9OBRpiA5RhrsBFIG29/nG2yi1YBDe1PGcsZkBXA/sYwPxgngki16Gtr1sIF6E4z6LOkRco6AuTfD6YDAuwvpfoM5/2ntM6TJ7em7PWXcRvimqZf1sr1VOw/xnJXZjAI18NbNcGuaR4HYemAUt1rLitloLkt42tsXI+OScheHaosWMD1rAg0a3i+XdipzRipEvtuYC49UCNurVRtwKRhZVAdHhJGRFA9o6DEVjtyT0cDAIFFEugpPyG5yKfShj/ze5MJ4/Vn6D8dFYHlcgRtJVHYdRtcM1n+l2JRKFPZQ56JVRI46JKmXOgOhHg0PBcPEqHZHB4Uri1LUm3JiMiaAFZxIMIjhwmTRKlzY1TguCX6BlmsRWGjZqcVeWehYNMTKoVDCCp1VwNuTOKEMxGNDiogsH6IZORISRdBYjflhfwKrB8qPq0gsebMfoKxlVGX6KGkJCLZC9J8vWIZEDulW6VeAXd+K8Rlh5VqcahckCxDkhd5TkDgurJnWzzGqhg8e0vEZfO6EddVpaHLaE6tjSEbf1sOiob2oOshhIEgTtsIf4Qy24x4ncKQ5TTT1uqCGbrYe1xjqLzaX6KVmsZIpmNZ1ZPusCzJZ18U5X2IV1TjqXLqzV2XRYzhjVRW2RqKaq9w/8Qa//wyQ6MdHtuOOl6ZbpL93p7ErRiZNYs/Jq21QnNBv001+6w9GVUl7lIgyFcX+sNnYnzvcwfony6qTKd0M7Z6yAN/6s/ZfYbKo7MLnHrNnYnkYudQcmnDkXOG2HcQcm0c6o4jYj9bQ6YnWcswsT27EoS7U22skWEJ6zG1OSth2/9QvlGbPK3NFZyvHjt52zL1PyuHJ8Fnf0izRVnrHrtuO07Zx+byIon+D9mJn3Y8QobKW+pIJHm5jmr2Wrprlk0cjKHI2o6o0WNAg65vagodYn2Rh16MKZbKCQRLFgBqll7ipu08SwLC41dWDyyFLBxdUCNNQvAsjvKGK/is0+zA5azLOi/yKQU79gJqu/arjOyDBivCZS9dnVgJWgUv6Mz872E2ABY9XJQcj4qRqPIWNxAO/ZsYPq15XGBRFQflSPX40zs32OJLZfGa5P01U+VMGo+AmbGsmFIai/qwLWcZ/lznyWRw0w9lnKdoFSd9ZSt3Eqs2+o7PNExu/zRKr7PPGUbVSir2KuaZW9Sf7/oS46DnWdKWQaZeuEkTAV+IHICm+cUmGXhpzKjIu9Vvqo4q4bSLEJ+/j/iLx045DX58CuELDkIU6jFZqZ1J0XcdmqiMtf+Xd+xFXdb0tSR3n6rJFzn4VcZdx4ipkBtbDr1HjUdbYsukgH0yF2dY+PsRmqT7C949REkA7tvFkEg5T7nD3b+JQYPHfftiLrV2xk5LMqgn+PdWYU+nlWf8xj/bGx/piYZR/fH5Or2p/a8/VnfHj+3P58+0zs5rM9EpLj4zfj+4R5zytZnxIlrLv2sB2R1OwnatumUrGJ7UHSZMNUugWjkyj+uIFAE+CGw7yxtq6NmamI+LRBMLfGO1JqbVmqOpbW847Fxcriseod/3loCivlqxX0wYD1c8fJrfzymkiju74+c0Gj+2XROmMgHF685KuHas87dP74oT6L2Bhsagy0trdNywU8dkd7ZtKkhZunTTuTsmBreNWcBagrm8jyWgA5VKEDXvPRt1mC6O1znsWkEjwrsN0GcZdB+rbiUz/B8l7VfBnm5KzECTKJ1HawiJcJdD83tilbTRJj5hgXRtWQsvltNGhLRYM2dp6iPJzDbw/SxYMgBo4TMWmkcgmC8Ue41LCYdBmVAOKlsYtKXlHViaCy3Jir7bRGQmYajQgfW7Zwt3G3bbH8XHltyyOPbFFe09yhPD9UfpC7Yoh2/0kc+vrXh0Tmz5C19KTuAW0zKHgD7h9po1nepmbvMPlrW0s7Xj927HW66WNKPqb3vE4TyrHXfykQJbeX5mhu7+iyHoH0jD6+l75IX9yrvFipSQBtVcknNpAw2U5QNtwsWNhUwhC6L1XZqC6IMUJW82hEzizogkkgYBMjoAYVGior/GiTutGFP6lmhDGn35zAckhAB00YDMWwaDAMZzyXY1un0TBLYxrO4wenRBUL+3m2V4dWrdYcq2XK9Et0rNBCzXxKSiXxiVJXfaqfZir7iJw+z7g96B2q4/aoK8e9bJRw7VYLg21qvVGjG/dt1KGf5XZVh2LyIMQ38Ll/NpjM5w+CZUP/Yfel8/S7mZB/8HsYYYt+3i9upBb6/EXKvsP/8FdnKB/RF/AptjdeRe40oNu9LP6vHxfx1luBCryWVGoxUIR5MD1J4hCjcRrEXY9YGQOmawC37ZvzzJrTiDlYXQP+Q/yg4KaGyvF9c+YqUiWeWJUbN8uhYozdlMBELylwtZWlJoazfoee5yfO/tpxuSIeaJgmiIlslV1SixqhGgVgOsFXRT5+/E2NwPYnqSAgMclWt/ApdEDjPG7pwAr0grlUz8a+mZv7+zfPRI5Tz/o29qRyiB5OEQzvY5AaX+Wxs7G9ZHCvUZE4SD/zqNT6aFoqaulYUIIf22NSrSwQ1FwOAPOCoAPDoDfWmK02dT2GbMqNA4tZrMhvpv5ohDn80J3TmObhydYtZbJlK88qCFiUCcgGfw9vhT/+YUrO8vccIDPziGrRCzXOJMv56FXBEU7IzlJRcGJnBeysU/UYakrFGrZFVg0AHSZOLmdVnCpoDNwCH/HQ8ZhMXS+AzEvb6OO0jVU8Eqw15TD8TBm/SjDdMMWV4o9+PsOwCcbpOMLipXWsx0sYDjaxPVvVdQCc2mWgo4m5CyZ1m1bAXm7MjmtUsddYWTE6KThMIq5rkJ0iXMqeSjhbZAubdGyVUzCcQssZo5nKEbswtC83sPbobmVkBFO4I2oxCoLbfUNLZ0UfumT3UUwBS8waYn2Q2ucapHANm38OQ7cFXp9Sly2o2VGsAZ7i/NP7rAaYi8v6uMxZCgbtJ7iTJ6/9hB/meL2hUgGMFM6mbMGsXxfkUzpOapE+klroCDtwhEXRykSq1gVJDPtj5Kx3XDU4VavBAZnUlHBr10oG0QYkqqFqjZ5GLBhMqB9FARFIbS43lluMUp6r4grE+5iJUfIjFVTNPACJy4+UV1EfPYMNNGx/D+Q5DUNxDsD+VlLdv7gpUUm12ERXVufCGIorymQzG3VlRcqyttID7z7wAALWBx54l66FkcIRrh94AFrQMuMlrsiAw+i7WHOjEPUTgsSeY/VWGN8fOW98P4V1VyP4bzSv5gzgUs80JuBvbGW3Vewi5FndFsfqtli7nozm4S4V8pWaLg7zHaIAjfj6WCL/F8P1u2sAAHjaY2BkYGBgZjjy6Mpmh3h+m68M8hwMIHDua+N+ZJqDgQNCMYEoAHf+C1gAeNpjYGRg4GD4fwNEMjD8/w8kgSIogBUAY/wD9XjaNU+7FcJADJNNCvq87MMOvEdNxRyq0mWH1GEWegZhACz54nvnj+yTzvGDLQ8gKr8iEQDBRDKqgmqZMMq7/y5kd/UdCLFiC+ITZiivaz6fR0er6d054SksUgzmU3qFEXdFzV2Ez8Ywlc/m5Pilsr2VWitP/bGJ4wvDWi96P3Not+n2B3lgIYIAAAAmACYAJgAuAJIA3gFaAaABrgHkAjoC1AMkA4IEUAUiBXAFzgYgBw4H7ghiCPYJsgp4Cq4LCAs2C4AMHAyiDiAPnBBAEUYRvBMwE7wUHhRaFIYUshTcFVAVgBX6FpYXXBeSF/AYYBkCGYgaBhooGkoa1BryGyQbQBtsG5Yb+Bw2HLAdLh1yHYYdsh4cHjYeYB7iHyYf3iAgIFIgdCCaILIgxiDcIPAhBiEkIegiOCK6IxAjeCPQJDQkbCS8JVIlriYWJjomWCZ2JpQmoib0J3QnvCgGKJAopii8KQApIilMKcgqJCpiKpwqyCsUK2QrvCwWLFYsnizgLPYtBC0SLSAAAAABAAAAgAC9ABAAAAAAAAIAAQACABYAAAEAAYEAAAAAeNqNkr1OAkEUhc8CmmBhRSysNtFCTfiXqFBZiIkaQzRqZ7KaBYz8CStg4/PpC1j6EJZWfjMMwSCFmczOuWfOPffOzEpa0avi8hJJSZ/MCfaUIprgmFb15XBcZW+qSWjTKzu8pLF36/Ay/IfDSa173w6/aS2WcvhdudiOjlXTmXwNFaqvgR7UVYe4wOzC+AqIX1hboMiq/qpHoEhNUN0yESjUWPd8e0RT3RaaiNFTWVnGyI6MGuw+s5qKDfgWGSa3Q42QmYXtwabxD/SE0vi0YTZUdRWP/tTb5nTGw/Rq/LrW74K4QTVznr6KeOUYRVV0pVPd6By0KC89l7lI489prufu6Xe1mi5hJtGMbaKMnN+Q/bzdy2iPb4UTB3rE02jqsOae7nirjEp27uNR0MG/+j+BD21Xh+y24Qf2tjvcQYjr7CUnPVStm09eYLPycKb/Em9Zoq755u2fk2Pd/QGe+3ARAAB42m3S1XIUURRG4VmDBHd3d5k+Z5/uBIdAcHd3CRI0OBRPyCshmRWu6Kqp/6brm9qrutVujTy/frZS63/Pjz8/Wm3ajGEs4xhPDxOYyCQmM4WpTGM6M5jJLGYzh7nMYz4LWMgiFrOEpSxjOStYySpWs4a1rGM9G9jIJjazha1sYzsdKhKZoFDT0EsfO9jJLnazh73sYz8H6OcghxjgMEc4yjGOc4KTnOI0ZzjLOc5zgYtc4jJXuMo1rnODm9ziNne4yz3u84CHPOIxTxjkKc94zguGeMkrXvOGt7xjmPd84COf+MwXvvKN7z3DQ4OpDPT/3YGq03ErN7nZDbe4tdu4vW7fyCa9pJf0kl7SS3pJL+klvTTqVXqVXqVX6VV6lV6lV+lVepVe0kt6SS/pJb3U9bL3ZO/J3pO9J3tP7oy+X7uN2/3/0Amd0Amd0Amd0Amd+Od07wi7hF3CLmGXsEvYJewSdgm7hF3CLmGXsEvYJewSdomkl/SSXtLLelkv62W9rJf1sl7Wy3pZL/RCL/RCL/RCL/RCL/RCr+gVvaJX9Ipe0St6Ra/oFb1ar9ar9Wq9Wq/Wq/VqvVqv1mv0Gr1Gr9Frul7xuyp+V8XvqnTyb1UoNRm4Af+FsAGNAEuwCFBYsQEBjlmxRgYrWCGwEFlLsBRSWCGwgFkdsAYrXFhZsBQrAAAAAVLP0T8AAA==) format('woff'), + url('font/genericons-regular-webfont.ttf') format('truetype'), + url('font/genericons-regular-webfont.svg#genericonsregular') format('svg'); + font-weight: normal; + font-style: normal; +} + + +/** + * All Genericons + */ + +.genericon { + display: inline-block; + width: 16px; + height: 16px; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + font-size: 16px; + line-height: 1; + font-family: 'Genericons'; + text-decoration: inherit; + font-weight: normal; + font-style: normal; + vertical-align: top; +} + +/** + * IE7 and IE6 hacks + */ + +.genericon { + *overflow: auto; + *zoom: 1; + *display: inline; +} + +/** + * Individual icons + */ + +/* Post formats */ +.genericon-standard:before { content: '\f100'; } +.genericon-aside:before { content: '\f101'; } +.genericon-image:before { content: '\f102'; } +.genericon-gallery:before { content: '\f103'; } +.genericon-video:before { content: '\f104'; } +.genericon-status:before { content: '\f105'; } +.genericon-quote:before { content: '\f106'; } +.genericon-link:before { content: '\f107'; } +.genericon-chat:before { content: '\f108'; } +.genericon-audio:before { content: '\f109'; } + +/* Social icons */ +.genericon-github:before { content: '\f200'; } +.genericon-dribbble:before { content: '\f201'; } +.genericon-twitter:before { content: '\f202'; } +.genericon-facebook:before { content: '\f203'; } +.genericon-facebook-alt:before { content: '\f204'; } +.genericon-wordpress:before { content: '\f205'; } +.genericon-googleplus:before { content: '\f206'; } +.genericon-linkedin:before { content: '\f207'; } +.genericon-linkedin-alt:before { content: '\f208'; } +.genericon-pinterest:before { content: '\f209'; } +.genericon-pinterest-alt:before { content: '\f210'; } +.genericon-flickr:before { content: '\f211'; } +.genericon-vimeo:before { content: '\f212'; } +.genericon-youtube:before { content: '\f213'; } +.genericon-tumblr:before { content: '\f214'; } +.genericon-instagram:before { content: '\f215'; } +.genericon-codepen:before { content: '\f216'; } +.genericon-polldaddy:before { content: '\f217'; } +.genericon-googleplus-alt:before { content: '\f218'; } +.genericon-path:before { content: '\f219'; } +.genericon-skype:before { content: '\f220'; } +.genericon-digg:before { content: '\f221'; } +.genericon-reddit:before { content: '\f222'; } +.genericon-stumbleupon:before { content: '\f223'; } +.genericon-pocket:before { content: '\f224'; } +.genericon-dropbox:before { content: '\f225'; } + +/* Meta icons */ +.genericon-comment:before { content: '\f300'; } +.genericon-category:before { content: '\f301'; } +.genericon-tag:before { content: '\f302'; } +.genericon-time:before { content: '\f303'; } +.genericon-user:before { content: '\f304'; } +.genericon-day:before { content: '\f305'; } +.genericon-week:before { content: '\f306'; } +.genericon-month:before { content: '\f307'; } +.genericon-pinned:before { content: '\f308'; } + +/* Other icons */ +.genericon-search:before { content: '\f400'; } +.genericon-unzoom:before { content: '\f401'; } +.genericon-zoom:before { content: '\f402'; } +.genericon-show:before { content: '\f403'; } +.genericon-hide:before { content: '\f404'; } +.genericon-close:before { content: '\f405'; } +.genericon-close-alt:before { content: '\f406'; } +.genericon-trash:before { content: '\f407'; } +.genericon-star:before { content: '\f408'; } +.genericon-home:before { content: '\f409'; } +.genericon-mail:before { content: '\f410'; } +.genericon-edit:before { content: '\f411'; } +.genericon-reply:before { content: '\f412'; } +.genericon-feed:before { content: '\f413'; } +.genericon-warning:before { content: '\f414'; } +.genericon-share:before { content: '\f415'; } +.genericon-attachment:before { content: '\f416'; } +.genericon-location:before { content: '\f417'; } +.genericon-checkmark:before { content: '\f418'; } +.genericon-menu:before { content: '\f419'; } +.genericon-refresh:before { content: '\f420'; } +.genericon-minimize:before { content: '\f421'; } +.genericon-maximize:before { content: '\f422'; } +.genericon-404:before { content: '\f423'; } +.genericon-spam:before { content: '\f424'; } +.genericon-summary:before { content: '\f425'; } +.genericon-cloud:before { content: '\f426'; } +.genericon-key:before { content: '\f427'; } +.genericon-dot:before { content: '\f428'; } +.genericon-next:before { content: '\f429'; } +.genericon-previous:before { content: '\f430'; } +.genericon-expand:before { content: '\f431'; } +.genericon-collapse:before { content: '\f432'; } +.genericon-dropdown:before { content: '\f433'; } +.genericon-dropdown-left:before { content: '\f434'; } +.genericon-top:before { content: '\f435'; } +.genericon-draggable:before { content: '\f436'; } +.genericon-phone:before { content: '\f437'; } +.genericon-send-to-phone:before { content: '\f438'; } +.genericon-plugin:before { content: '\f439'; } +.genericon-cloud-download:before { content: '\f440'; } +.genericon-cloud-upload:before { content: '\f441'; } +.genericon-external:before { content: '\f442'; } +.genericon-document:before { content: '\f443'; } +.genericon-book:before { content: '\f444'; } +.genericon-cog:before { content: '\f445'; } +.genericon-unapprove:before { content: '\f446'; } +.genericon-cart:before { content: '\f447'; } +.genericon-pause:before { content: '\f448'; } +.genericon-stop:before { content: '\f449'; } +.genericon-skip-back:before { content: '\f450'; } +.genericon-skip-ahead:before { content: '\f451'; } +.genericon-play:before { content: '\f452'; } +.genericon-tablet:before { content: '\f453'; } +.genericon-send-to-tablet:before { content: '\f454'; } +.genericon-info:before { content: '\f455'; } +.genericon-notice:before { content: '\f456'; } +.genericon-help:before { content: '\f457'; } +.genericon-fastforward:before { content: '\f458'; } +.genericon-rewind:before { content: '\f459'; } +.genericon-portfolio:before { content: '\f460'; } +.genericon-heart:before { content: '\f461'; } +.genericon-code:before { content: '\f462'; } +.genericon-subscribe:before { content: '\f463'; } +.genericon-unsubscribe:before { content: '\f464'; } +.genericon-subscribed:before { content: '\f465'; } +.genericon-reply-alt:before { content: '\f466'; } +.genericon-reply-single:before { content: '\f467'; } +.genericon-flag:before { content: '\f468'; } +.genericon-print:before { content: '\f469'; } +.genericon-lock:before { content: '\f470'; } +.genericon-bold:before { content: '\f471'; } +.genericon-italic:before { content: '\f472'; } +.genericon-picture:before { content: '\f473'; } +.genericon-fullscreen:before { content: '\f474'; } + +/* Generic shapes */ +.genericon-uparrow:before { content: '\f500'; } +.genericon-rightarrow:before { content: '\f501'; } +.genericon-downarrow:before { content: '\f502'; } +.genericon-leftarrow:before { content: '\f503'; } + + + + + diff --git a/wp-content/themes/twentythirteen/header.php b/wp-content/themes/twentythirteen/header.php new file mode 100644 index 0000000..f61c28f --- /dev/null +++ b/wp-content/themes/twentythirteen/header.php @@ -0,0 +1,51 @@ + section and everything up till
        + * + * @package WordPress + * @subpackage Twenty_Thirteen + * @since Twenty Thirteen 1.0 + */ +?> + + + +> + + + + + <?php wp_title( '|', true, 'right' ); ?> + + + + + + +> +
        + + +
        diff --git a/wp-content/themes/twentythirteen/image.php b/wp-content/themes/twentythirteen/image.php new file mode 100644 index 0000000..b2d86bf --- /dev/null +++ b/wp-content/themes/twentythirteen/image.php @@ -0,0 +1,82 @@ + + +
        +
        +
        > +
        +

        + + +
        + +
        + + +
        +
        + + + +
        + +
        + +
        +
        + + post_content ) ) : ?> +
        + + '' ) ); ?> +
        + + +
        +
        + + + +
        +
        + + \ No newline at end of file diff --git a/wp-content/themes/twentythirteen/images/dotted-line-2x.png b/wp-content/themes/twentythirteen/images/dotted-line-2x.png new file mode 100644 index 0000000..07f6c93 Binary files /dev/null and b/wp-content/themes/twentythirteen/images/dotted-line-2x.png differ diff --git a/wp-content/themes/twentythirteen/images/dotted-line-light-2x.png b/wp-content/themes/twentythirteen/images/dotted-line-light-2x.png new file mode 100644 index 0000000..059d4ec Binary files /dev/null and b/wp-content/themes/twentythirteen/images/dotted-line-light-2x.png differ diff --git a/wp-content/themes/twentythirteen/images/dotted-line-light.png b/wp-content/themes/twentythirteen/images/dotted-line-light.png new file mode 100644 index 0000000..b7f82cd Binary files /dev/null and b/wp-content/themes/twentythirteen/images/dotted-line-light.png differ diff --git a/wp-content/themes/twentythirteen/images/dotted-line.png b/wp-content/themes/twentythirteen/images/dotted-line.png new file mode 100644 index 0000000..115b583 Binary files /dev/null and b/wp-content/themes/twentythirteen/images/dotted-line.png differ diff --git a/wp-content/themes/twentythirteen/images/headers/circle-thumbnail.png b/wp-content/themes/twentythirteen/images/headers/circle-thumbnail.png new file mode 100644 index 0000000..2f9344c Binary files /dev/null and b/wp-content/themes/twentythirteen/images/headers/circle-thumbnail.png differ diff --git a/wp-content/themes/twentythirteen/images/headers/circle.png b/wp-content/themes/twentythirteen/images/headers/circle.png new file mode 100644 index 0000000..0bd9401 Binary files /dev/null and b/wp-content/themes/twentythirteen/images/headers/circle.png differ diff --git a/wp-content/themes/twentythirteen/images/headers/diamond-thumbnail.png b/wp-content/themes/twentythirteen/images/headers/diamond-thumbnail.png new file mode 100644 index 0000000..82777a0 Binary files /dev/null and b/wp-content/themes/twentythirteen/images/headers/diamond-thumbnail.png differ diff --git a/wp-content/themes/twentythirteen/images/headers/diamond.png b/wp-content/themes/twentythirteen/images/headers/diamond.png new file mode 100644 index 0000000..a14de61 Binary files /dev/null and b/wp-content/themes/twentythirteen/images/headers/diamond.png differ diff --git a/wp-content/themes/twentythirteen/images/headers/star-thumbnail.png b/wp-content/themes/twentythirteen/images/headers/star-thumbnail.png new file mode 100644 index 0000000..693bb76 Binary files /dev/null and b/wp-content/themes/twentythirteen/images/headers/star-thumbnail.png differ diff --git a/wp-content/themes/twentythirteen/images/headers/star.png b/wp-content/themes/twentythirteen/images/headers/star.png new file mode 100644 index 0000000..24ca626 Binary files /dev/null and b/wp-content/themes/twentythirteen/images/headers/star.png differ diff --git a/wp-content/themes/twentythirteen/images/search-icon-2x.png b/wp-content/themes/twentythirteen/images/search-icon-2x.png new file mode 100644 index 0000000..02b63b8 Binary files /dev/null and b/wp-content/themes/twentythirteen/images/search-icon-2x.png differ diff --git a/wp-content/themes/twentythirteen/images/search-icon.png b/wp-content/themes/twentythirteen/images/search-icon.png new file mode 100644 index 0000000..11d8dc8 Binary files /dev/null and b/wp-content/themes/twentythirteen/images/search-icon.png differ diff --git a/wp-content/themes/twentythirteen/inc/back-compat.php b/wp-content/themes/twentythirteen/inc/back-compat.php new file mode 100644 index 0000000..993ef1c --- /dev/null +++ b/wp-content/themes/twentythirteen/inc/back-compat.php @@ -0,0 +1,63 @@ +

        %s

        ', $message ); +} + +/** + * Prevent the Customizer from being loaded on WordPress versions prior to 3.6. + * + * @since Twenty Thirteen 1.0 + */ +function twentythirteen_customize() { + wp_die( sprintf( __( 'Twenty Thirteen requires at least WordPress version 3.6. You are running version %s. Please upgrade and try again.', 'twentythirteen' ), $GLOBALS['wp_version'] ), '', array( + 'back_link' => true, + ) ); +} +add_action( 'load-customize.php', 'twentythirteen_customize' ); + +/** + * Prevent the Theme Preview from being loaded on WordPress versions prior to 3.4. + * + * @since Twenty Thirteen 1.0 + */ +function twentythirteen_preview() { + if ( isset( $_GET['preview'] ) ) { + wp_die( sprintf( __( 'Twenty Thirteen requires at least WordPress version 3.6. You are running version %s. Please upgrade and try again.', 'twentythirteen' ), $GLOBALS['wp_version'] ) ); + } +} +add_action( 'template_redirect', 'twentythirteen_preview' ); diff --git a/wp-content/themes/twentythirteen/inc/custom-header.php b/wp-content/themes/twentythirteen/inc/custom-header.php new file mode 100644 index 0000000..61bc6af --- /dev/null +++ b/wp-content/themes/twentythirteen/inc/custom-header.php @@ -0,0 +1,227 @@ + '220e10', + 'default-image' => '%s/images/headers/circle.png', + + // Set height and width, with a maximum value for the width. + 'height' => 230, + 'width' => 1600, + + // Callbacks for styling the header and the admin preview. + 'wp-head-callback' => 'twentythirteen_header_style', + 'admin-head-callback' => 'twentythirteen_admin_header_style', + 'admin-preview-callback' => 'twentythirteen_admin_header_image', + ); + + add_theme_support( 'custom-header', $args ); + + /* + * Default custom headers packaged with the theme. + * %s is a placeholder for the theme template directory URI. + */ + register_default_headers( array( + 'circle' => array( + 'url' => '%s/images/headers/circle.png', + 'thumbnail_url' => '%s/images/headers/circle-thumbnail.png', + 'description' => _x( 'Circle', 'header image description', 'twentythirteen' ) + ), + 'diamond' => array( + 'url' => '%s/images/headers/diamond.png', + 'thumbnail_url' => '%s/images/headers/diamond-thumbnail.png', + 'description' => _x( 'Diamond', 'header image description', 'twentythirteen' ) + ), + 'star' => array( + 'url' => '%s/images/headers/star.png', + 'thumbnail_url' => '%s/images/headers/star-thumbnail.png', + 'description' => _x( 'Star', 'header image description', 'twentythirteen' ) + ), + ) ); +} +add_action( 'after_setup_theme', 'twentythirteen_custom_header_setup', 11 ); + +/** + * Load our special font CSS files. + * + * @since Twenty Thirteen 1.0 + */ +function twentythirteen_custom_header_fonts() { + // Add Source Sans Pro and Bitter fonts. + wp_enqueue_style( 'twentythirteen-fonts', twentythirteen_fonts_url(), array(), null ); + + // Add Genericons font. + wp_enqueue_style( 'genericons', get_template_directory_uri() . '/genericons/genericons.css', array(), '3.03' ); +} +add_action( 'admin_print_styles-appearance_page_custom-header', 'twentythirteen_custom_header_fonts' ); + +/** + * Style the header text displayed on the blog. + * + * get_header_textcolor() options: Hide text (returns 'blank'), or any hex value. + * + * @since Twenty Thirteen 1.0 + */ +function twentythirteen_header_style() { + $header_image = get_header_image(); + $text_color = get_header_textcolor(); + + // If no custom options for text are set, let's bail. + if ( empty( $header_image ) && $text_color == get_theme_support( 'custom-header', 'default-text-color' ) ) + return; + + // If we get this far, we have custom styles. + ?> + + Header admin panel. + * + * @since Twenty Thirteen 1.0 + */ +function twentythirteen_admin_header_style() { + $header_image = get_header_image(); +?> + + Header admin panel. + * + * This callback overrides the default markup displayed there. + * + * @since Twenty Thirteen 1.0 + */ +function twentythirteen_admin_header_image() { + ?> + + + +
        +
        + + + + + + + + + + + + + +
        +
        + + + \ No newline at end of file diff --git a/wp-content/themes/twentythirteen/js/functions.js b/wp-content/themes/twentythirteen/js/functions.js new file mode 100644 index 0000000..526c15d --- /dev/null +++ b/wp-content/themes/twentythirteen/js/functions.js @@ -0,0 +1,103 @@ +/** + * Functionality specific to Twenty Thirteen. + * + * Provides helper functions to enhance the theme experience. + */ + +( function( $ ) { + var body = $( 'body' ), + _window = $( window ); + + /** + * Adds a top margin to the footer if the sidebar widget area is higher + * than the rest of the page, to help the footer always visually clear + * the sidebar. + */ + $( function() { + if ( body.is( '.sidebar' ) ) { + var sidebar = $( '#secondary .widget-area' ), + secondary = ( 0 === sidebar.length ) ? -40 : sidebar.height(), + margin = $( '#tertiary .widget-area' ).height() - $( '#content' ).height() - secondary; + + if ( margin > 0 && _window.innerWidth() > 999 ) { + $( '#colophon' ).css( 'margin-top', margin + 'px' ); + } + } + } ); + + /** + * Enables menu toggle for small screens. + */ + ( function() { + var nav = $( '#site-navigation' ), button, menu; + if ( ! nav ) { + return; + } + + button = nav.find( '.menu-toggle' ); + if ( ! button ) { + return; + } + + // Hide button if menu is missing or empty. + menu = nav.find( '.nav-menu' ); + if ( ! menu || ! menu.children().length ) { + button.hide(); + return; + } + + button.on( 'click.twentythirteen', function() { + nav.toggleClass( 'toggled-on' ); + } ); + + // Fix sub-menus for touch devices. + if ( 'ontouchstart' in window ) { + menu.find( '.menu-item-has-children > a' ).on( 'touchstart.twentythirteen', function( e ) { + var el = $( this ).parent( 'li' ); + + if ( ! el.hasClass( 'focus' ) ) { + e.preventDefault(); + el.toggleClass( 'focus' ); + el.siblings( '.focus' ).removeClass( 'focus' ); + } + } ); + } + + // Better focus for hidden submenu items for accessibility. + menu.find( 'a' ).on( 'focus.twentythirteen blur.twentythirteen', function() { + $( this ).parents( '.menu-item, .page_item' ).toggleClass( 'focus' ); + } ); + } )(); + + /** + * Makes "skip to content" link work correctly in IE9 and Chrome for better + * accessibility. + * + * @link http://www.nczonline.net/blog/2013/01/15/fixing-skip-to-content-links/ + */ + _window.on( 'hashchange.twentythirteen', function() { + var element = document.getElementById( location.hash.substring( 1 ) ); + + if ( element ) { + if ( ! /^(?:a|select|input|button|textarea)$/i.test( element.tagName ) ) { + element.tabIndex = -1; + } + + element.focus(); + } + } ); + + /** + * Arranges footer widgets vertically. + */ + if ( $.isFunction( $.fn.masonry ) ) { + var columnWidth = body.is( '.sidebar' ) ? 228 : 245; + + $( '#secondary .widget-area' ).masonry( { + itemSelector: '.widget', + columnWidth: columnWidth, + gutterWidth: 20, + isRTL: body.is( '.rtl' ) + } ); + } +} )( jQuery ); \ No newline at end of file diff --git a/wp-content/themes/twentythirteen/js/html5.js b/wp-content/themes/twentythirteen/js/html5.js new file mode 100644 index 0000000..6168aac --- /dev/null +++ b/wp-content/themes/twentythirteen/js/html5.js @@ -0,0 +1,8 @@ +/* + HTML5 Shiv v3.7.0 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed +*/ +(function(l,f){function m(){var a=e.elements;return"string"==typeof a?a.split(" "):a}function i(a){var b=n[a[o]];b||(b={},h++,a[o]=h,n[h]=b);return b}function p(a,b,c){b||(b=f);if(g)return b.createElement(a);c||(c=i(b));b=c.cache[a]?c.cache[a].cloneNode():r.test(a)?(c.cache[a]=c.createElem(a)).cloneNode():c.createElem(a);return b.canHaveChildren&&!s.test(a)?c.frag.appendChild(b):b}function t(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag(); +a.createElement=function(c){return!e.shivMethods?b.createElem(c):p(c,a,b)};a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/[\w\-]+/g,function(a){b.createElem(a);b.frag.createElement(a);return'c("'+a+'")'})+");return n}")(e,b.frag)}function q(a){a||(a=f);var b=i(a);if(e.shivCSS&&!j&&!b.hasCSS){var c,d=a;c=d.createElement("p");d=d.getElementsByTagName("head")[0]||d.documentElement;c.innerHTML="x"; +c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="";j="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode|| +"undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version:"3.7.0",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f); +if(g)return a.createDocumentFragment();for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;d\n" +"Language-Team: LANGUAGE \n" + +#: 404.php:16 +msgid "Not Found" +msgstr "" + +#: 404.php:21 +msgid "This is somewhat embarrassing, isn’t it?" +msgstr "" + +#: 404.php:22 +msgid "It looks like nothing was found at this location. Maybe try a search?" +msgstr "" + +#: archive.php:29 +msgid "Daily Archives: %s" +msgstr "" + +#: archive.php:31 +msgid "Monthly Archives: %s" +msgstr "" + +#: archive.php:31 +msgctxt "monthly archives date format" +msgid "F Y" +msgstr "" + +#: archive.php:33 +msgid "Yearly Archives: %s" +msgstr "" + +#: archive.php:33 +msgctxt "yearly archives date format" +msgid "Y" +msgstr "" + +#: archive.php:35 +msgid "Archives" +msgstr "" + +#: author-bio.php:26 +msgid "About %s" +msgstr "" + +#: author-bio.php:30 +msgid "View all posts by %s " +msgstr "" + +#: author.php:31 +msgid "All posts by %s" +msgstr "" + +#: category.php:19 +msgid "Category Archives: %s" +msgstr "" + +#: comments.php:25 +msgctxt "comments title" +msgid "One thought on “%2$s”" +msgid_plural "%1$s thoughts on “%2$s”" +msgstr[0] "" +msgstr[1] "" + +#: comments.php:45 +msgid "Comment navigation" +msgstr "" + +#: comments.php:46 +msgid "← Older Comments" +msgstr "" + +#: comments.php:47 +msgid "Newer Comments →" +msgstr "" + +#: comments.php:52 +msgid "Comments are closed." +msgstr "" + +#. translators: %s: Name of current post +#: content-aside.php:16 content-audio.php:27 content-chat.php:26 +#: content-gallery.php:27 content-image.php:26 content-link.php:27 +#: content-quote.php:16 content-status.php:16 content-video.php:26 +#: content.php:44 functions.php:478 +msgid "Continue reading %s " +msgstr "" + +#: content-aside.php:20 content-audio.php:31 content-chat.php:30 +#: content-gallery.php:31 content-image.php:30 content-link.php:31 +#: content-quote.php:20 content-status.php:20 content-video.php:30 +#: content.php:48 image.php:70 page.php:35 +msgid "Pages:" +msgstr "" + +#: content-aside.php:27 content-aside.php:35 content-audio.php:38 +#: content-chat.php:36 content-gallery.php:46 content-image.php:42 +#: content-link.php:19 content-quote.php:32 content-status.php:26 +#: content-video.php:42 content.php:31 image.php:44 page.php:39 +msgid "Edit" +msgstr "" + +#: content-gallery.php:43 content-image.php:39 content-quote.php:29 +#: content-video.php:39 content.php:56 +msgid "Leave a comment" +msgstr "" + +#: content-gallery.php:43 content-image.php:39 content-quote.php:29 +#: content-video.php:39 content.php:56 +msgid "One comment so far" +msgstr "" + +#: content-gallery.php:43 content-image.php:39 content-quote.php:29 +#: content-video.php:39 content.php:56 +msgid "View all % comments" +msgstr "" + +#: content-none.php:12 +msgid "Nothing Found" +msgstr "" + +#: content-none.php:18 +msgid "" +"Ready to publish your first post? Get started here." +msgstr "" + +#: content-none.php:22 +msgid "" +"Sorry, but nothing matched your search terms. Please try again with " +"different keywords." +msgstr "" + +#: content-none.php:27 +msgid "" +"It seems we can’t find what you’re looking for. Perhaps " +"searching can help." +msgstr "" + +#. #-#-#-#-# twentythirteen.pot (Twenty Thirteen 1.4) #-#-#-#-# +#. Author URI of the plugin/theme +#: footer.php:19 +msgid "http://wordpress.org/" +msgstr "" + +#: footer.php:19 +msgid "Semantic Personal Publishing Platform" +msgstr "" + +#: footer.php:19 +msgid "Proudly powered by %s" +msgstr "" + +#: functions.php:97 +msgid "Navigation Menu" +msgstr "" + +#. Translators: If there are characters in your language that are not +#. * supported by Source Sans Pro, translate this to 'off'. Do not translate +#. * into your own language. +#: functions.php:128 +msgctxt "Source Sans Pro font: on or off" +msgid "on" +msgstr "" + +#. Translators: If there are characters in your language that are not +#. * supported by Bitter, translate this to 'off'. Do not translate into your +#. * own language. +#: functions.php:134 +msgctxt "Bitter font: on or off" +msgid "on" +msgstr "" + +#: functions.php:218 +msgid "Page %s" +msgstr "" + +#: functions.php:231 +msgid "Main Widget Area" +msgstr "" + +#: functions.php:233 +msgid "Appears in the footer section of the site." +msgstr "" + +#: functions.php:241 +msgid "Secondary Widget Area" +msgstr "" + +#: functions.php:243 +msgid "Appears on posts and pages in the sidebar." +msgstr "" + +#: functions.php:266 +msgid "Posts navigation" +msgstr "" + +#: functions.php:270 +msgid " Older posts" +msgstr "" + +#: functions.php:274 +msgid "Newer posts " +msgstr "" + +#: functions.php:300 +msgid "Post navigation" +msgstr "" + +#: functions.php:303 +msgctxt "Previous post link" +msgid " %title" +msgstr "" + +#: functions.php:304 +msgctxt "Next post link" +msgid "%title " +msgstr "" + +#: functions.php:322 +msgid "Sticky" +msgstr "" + +#. Translators: used between list items, there is a space after the comma. +#: functions.php:328 functions.php:334 +msgid ", " +msgstr "" + +#: functions.php:343 +msgid "View all posts by %s" +msgstr "" + +#: functions.php:363 +msgctxt "1: post format name. 2: date" +msgid "%1$s on %2$s" +msgstr "" + +#: functions.php:369 +msgid "Permalink to %s" +msgstr "" + +#: header.php:43 +msgid "Menu" +msgstr "" + +#: header.php:44 +msgid "Skip to content" +msgstr "" + +#: image.php:22 +msgid "" +"Published on in %5$s" +msgstr "" + +#: image.php:38 +msgid "Link to full-size image" +msgstr "" + +#: image.php:39 +msgid "Full resolution" +msgstr "" + +#: image.php:51 +msgid " Previous" +msgstr "" + +#: image.php:52 +msgid "Next " +msgstr "" + +#: inc/back-compat.php:37 inc/back-compat.php:47 inc/back-compat.php:60 +msgid "" +"Twenty Thirteen requires at least WordPress version 3.6. You are running " +"version %s. Please upgrade and try again." +msgstr "" + +#: inc/custom-header.php:49 +msgctxt "header image description" +msgid "Circle" +msgstr "" + +#: inc/custom-header.php:54 +msgctxt "header image description" +msgid "Diamond" +msgstr "" + +#: inc/custom-header.php:59 +msgctxt "header image description" +msgid "Star" +msgstr "" + +#: search.php:18 +msgid "Search Results for: %s" +msgstr "" + +#: tag.php:21 +msgid "Tag Archives: %s" +msgstr "" + +#: taxonomy-post_format.php:23 +msgid "%s Archives" +msgstr "" + +#. Theme Name of the plugin/theme +msgid "Twenty Thirteen" +msgstr "" + +#. Theme URI of the plugin/theme +msgid "http://wordpress.org/themes/twentythirteen" +msgstr "" + +#. Description of the plugin/theme +msgid "" +"The 2013 theme for WordPress takes us back to the blog, featuring a full " +"range of post formats, each displayed beautifully in their own unique way. " +"Design details abound, starting with a vibrant color scheme and matching " +"header images, beautiful typography and icons, and a flexible layout that " +"looks great on any device, big or small." +msgstr "" + +#. Author of the plugin/theme +msgid "the WordPress team" +msgstr "" diff --git a/wp-content/themes/twentythirteen/page.php b/wp-content/themes/twentythirteen/page.php new file mode 100644 index 0000000..5545367 --- /dev/null +++ b/wp-content/themes/twentythirteen/page.php @@ -0,0 +1,50 @@ + + +
        +
        + + + + +
        > +
        + +
        + +
        + + +

        +
        + +
        + + '', 'link_before' => '', 'link_after' => '' ) ); ?> +
        + +
        + ', '' ); ?> +
        +
        + + + + +
        +
        + + + \ No newline at end of file diff --git a/wp-content/themes/twentythirteen/rtl.css b/wp-content/themes/twentythirteen/rtl.css new file mode 100644 index 0000000..5428630 --- /dev/null +++ b/wp-content/themes/twentythirteen/rtl.css @@ -0,0 +1,766 @@ +/* +Theme Name: Twenty Thirteen +Description: Adds support for languages written in a Right To Left (RTL) direction. +It's easy, just a matter of overwriting all the horizontal positioning attributes +of your CSS stylesheet in a separate stylesheet file named rtl.css. + +See http://codex.wordpress.org/Right_to_Left_Language_Support +*/ + +/** + * Table of Contents: + * + * 1.0 - Reset + * 4.0 - Header + * 4.1 - Site Header + * 4.2 - Navigation + * 5.0 - Content + * 5.2 - Entry Meta + * 5.4 - Galleries + * 5.5 - Post Formats + * 5.6 - Attachments + * 5.7 - Post/Paging Navigation + * 5.8 - Author Bio + * 5.9 - Archives + * 5.10 - Search Results/No posts + * 5.12 - Comments + * 6.0 - Sidebar + * 6.1 - Widgets + * 7.0 - Footer + * 8.0 - Media Queries + * 9.0 - Print + * ---------------------------------------------------------------------------- + */ + + +/** + * 1.0 Reset + * ---------------------------------------------------------------------------- + */ + +body { + direction: rtl; + unicode-bidi: embed; +} + +a { + display: inline-block; +} + +blockquote blockquote { + margin-left: 0; + margin-right: 24px; +} + +menu, +ol, +ul { + padding: 0 40px 0 0; +} + +caption, +th, +td { + text-align: right; +} + +td { + padding-left: 10px; + padding-right: 0; +} + +.assistive-text:focus { + left: auto; + right: 5px; +} + + +/** + * 4.0 Header + * ---------------------------------------------------------------------------- + */ + +/** + * 4.1 Site Header + * ---------------------------------------------------------------------------- + */ + +.site-header > a:first-child { + display: inherit; +} + +.site-description { + font-style: normal; +} + + +/** + * 4.2 Navigation + * ---------------------------------------------------------------------------- + */ + +/* Navbar */ +ul.nav-menu, +div.nav-menu > ul { + margin: 0 -20px 0 0; + padding: 0 0 0 40px; +} + +.nav-menu .sub-menu, +.nav-menu .children { + float: right; + left: auto; + right: -2px; +} + +.nav-menu .sub-menu ul, +.nav-menu .children ul { + border-left: 2px solid #f7f5e7; + border-right: 0; + left: auto; + right: 100%; +} + +.main-navigation .search-form { + left: 0; + right: auto; +} + +.site-header .search-field { + background-position: 98% center; + padding: 0 34px 0 0; +} + +.nav-menu .current_page_item > a, +.nav-menu .current_page_ancestor > a, +.nav-menu .current-menu-item > a, +.nav-menu .current-menu-ancestor > a { + font-style: normal; +} + +.menu-toggle { + padding-left: 0; + padding-right: 20px; +} + + +/** + * 5.0 Content + * ---------------------------------------------------------------------------- + */ + +.sidebar .entry-header, +.sidebar .entry-content, +.sidebar .entry-summary, +.sidebar .entry-meta { + padding-left: 376px; + padding-right: 60px; +} + + +/** + * 5.2 Entry Meta + * ---------------------------------------------------------------------------- + */ + +.entry-meta > span { + margin-left: 20px; + margin-right: auto; +} + +.entry-meta > span:last-child { + margin-left: 0; + margin-right: auto; +} + +.featured-post:before { + margin-left: 2px; + margin-right: auto; +} + +.entry-meta .date a:before { + margin-left: 2px; +} + +.comments-link a:before { + margin-left: 2px; + margin-right: auto; +} + +.tags-links a:first-child:before { + margin-left: 2px; +} + +.edit-link a:before { + margin-left: 2px; +} + +.page-links .page-links-title { + margin-left: 20px; + margin-right: auto; +} + +/** + * 5.4 Galleries + * ---------------------------------------------------------------------------- + */ + +.gallery { + margin-left: auto; + margin-right: -4px; +} + +.gallery-item { + float: right; + margin: 0 0 4px 4px; +} + +.gallery-item a { + display: inline; +} + + +/** + * 5.5 Post Formats + * ---------------------------------------------------------------------------- + */ + +.entry-content a { + display: inline; +} + +.format-aside cite:before { + content: normal; + margin-right: auto; +} + +.format-aside cite:after { + content: "\2014"; + margin-left: 5px; +} + +.format-audio .entry-content:before { + float: right; + -webkit-transform: scaleX(-1); + -moz-transform: scaleX(-1); + -ms-transform: scaleX(-1); + -o-transform: scaleX(-1); + transform: scaleX(-1); +} + +.format-audio .audio-content { + background-position: right top; + float: left; + padding-left: 0; + padding-right: 35px; +} + +.format-chat .entry-meta .date a:before { + margin-left: 4px; + margin-right: auto; +} + +.format-image .wp-caption-text { + text-align: right; +} + +.format-link .entry-title { + margin-left: 20px; + margin-right: auto; +} + +.format-status .entry-content, +.format-status .entry-meta { + padding-left: 0; + padding-right: 35px; +} + +.sidebar .format-status .entry-content, +.sidebar .format-status .entry-meta { + padding-left: 376px; + padding-right: 95px; +} + +.format-status .entry-content:before, +.format-status .entry-meta:before { + left: auto; + right: 10px; +} + +.sidebar .format-status .entry-content:before, +.sidebar .format-status .entry-meta:before { + left: auto; + right: 70px; +} + +.format-status .entry-content p:first-child:before { + left: auto; + right: 4px; +} + +.sidebar .format-status .entry-content p:first-child:before { + left: auto; + right: 64px; +} + +.format-quote blockquote { + padding-left: 0; + padding-right: 75px; +} + +.format-quote blockquote:before { + content: '\201D'; + padding-left: 25px; + padding-right: 0; + left: auto; + right: -15px; +} + + +/** + * 5.6 Attachments + * ---------------------------------------------------------------------------- + */ + +.attachment .entry-title { + float: right; +} + +.attachment .entry-title:before { + margin-left: 10px; + margin-right: auto; +} + +.attachment .entry-meta { + float: left; +} + +.image-navigation .nav-previous { + left: auto; + right: 0; +} + +.image-navigation .nav-next { + left: 0; + right: auto; +} + +.attachment .entry-caption { + text-align: right; +} + + +/** + * 5.7 Post/Paging Navigation + * ---------------------------------------------------------------------------- + */ + +.navigation .nav-previous { + float: right; +} + +.navigation .nav-next { + float: left; +} + +.sidebar .paging-navigation .nav-links, +.sidebar .post-navigation .nav-links { + padding-left: 376px; + padding-right: 60px; +} + +.paging-navigation .nav-previous .meta-nav { + margin-left: 10px; + margin-right: auto; +} + +.paging-navigation .nav-next .meta-nav { + margin-left: auto; + margin-right: 10px; +} + +.post-navigation a[rel="next"] { + float: left; + text-align: left; +} + + +/** + * 5.8 Author Bio + * ---------------------------------------------------------------------------- + */ + +.author-info { + text-align: right; /* gallery & video post formats */ +} + +.author.sidebar .author-info { + padding-left: 376px; + padding-right: 60px; +} + +.author-avatar .avatar { + float: right; + margin: 0 0 30px 30px; +} + +.author-link { + margin-left: auto; + margin-right: 2px; +} + + +/** + * 5.9 Archives + * ---------------------------------------------------------------------------- + */ + +.sidebar .archive-meta { + padding-left: 316px; + padding-right: 0; +} + + +/** + * 5.10 Search Results/No posts + * ---------------------------------------------------------------------------- + */ + +.sidebar .page-content { + padding-left: 376px; + padding-right: 60px; +} + +/** + * 5.12 Comments + * ---------------------------------------------------------------------------- + */ + +.sidebar .comments-title, +.sidebar .comment-list, +.sidebar .comment-reply-title, +.sidebar .comment-navigation, +.sidebar .comment-respond .comment-form { + padding-left: 376px; + padding-right: 60px; +} + +.comment-list .children { + margin-left: auto; + margin-right: 20px; +} + +.comment-author { + float: right; + margin-left: 50px; + margin-right: auto; +} + +.comment-list .edit-link { + margin-left: auto; + margin-right: 20px; +} + +.comment-metadata, +.comment-content, +.comment-list .reply, +.comment-awaiting-moderation { + float: left; +} + +.comment-awaiting-moderation:before { + margin-left: 5px; + margin-right: auto; +} + +.comment-reply-link:before, +.comment-reply-login:before { + margin-left: 3px; + margin-right: auto; + -webkit-transform: scaleX(-1); + -moz-transform: scaleX(-1); + -ms-transform: scaleX(-1); + -o-transform: scaleX(-1); + transform: scaleX(-1); +} + +.comment-reply-title small a { + float: left; +} + +.comment-form [for="author"], +.comment-form [for="email"], +.comment-form [for="url"], +.comment-form [for="comment"] { + float: right; +} + +.form-allowed-tags code { + margin-left: auto; + margin-right: 3px; +} + +.sidebar .no-comments { + padding-left: 376px; + padding-right: 60px; +} + + +/** + * 6.0 Sidebar + * ---------------------------------------------------------------------------- + */ + +.site-main .widget-area { + float: left; +} + +.widget-area a { + max-width: 100%; +} + + +/** + * 6.1 Widgets + * ---------------------------------------------------------------------------- + */ + +.widget .widget-title { + font-style: normal; +} + +.widget li > ul, +.widget li > ol { + margin-left: auto; + margin-right: 20px; +} + +/** + * 7.0 Footer + * ---------------------------------------------------------------------------- + */ + +.site-footer .widget-area, +.sidebar .site-footer { + text-align: right; +} +.sidebar .site-footer .widget-area { + left: auto; + right: -158px; +} + +.site-footer .widget { + float: right; + margin-left: 20px; + margin-right: auto; +} + +.sidebar .site-footer .widget:nth-of-type(4), +.sidebar .site-footer .widget:nth-of-type(3) { + margin-left: 0; + margin-right: auto; +} + + +/** + * 8.0 Media Queries + * ---------------------------------------------------------------------------- + */ + +@media (max-width: 1069px) { + ul.nav-menu, + div.nav-menu > ul { + margin-left: auto; + margin-right: 0; + } + + .error404 .page-header, + .sidebar .format-image .entry-content img.size-full, + .sidebar .format-image .wp-caption:first-child .wp-caption-text { + margin-right: auto; + } + + .main-navigation .search-form { + left: 20px; + right: auto; + } + + .site-main .widget-area { + margin-left: 60px; + margin-right: auto; + } +} + +@media (max-width: 999px) { + .sidebar .entry-header, + .sidebar .entry-content, + .sidebar .entry-summary, + .sidebar .entry-meta, + .sidebar .comment-list, + .sidebar .comment-reply-title, + .sidebar .comment-navigation, + .sidebar .comment-respond .comment-form, + .sidebar .featured-gallery, + .sidebar .post-navigation .nav-links, + .author.sidebar .author-info, + .sidebar .format-image .entry-content { + max-width: 604px; + padding-left: 0; + padding-right: 0; + } + + .site-main .widget-area { + float: none; + margin-left: auto; + } + + .attachment .entry-meta { + float: right; + text-align: right; + } + + .sidebar .format-status .entry-content, + .sidebar .format-status .entry-meta { + padding-left: 0; + padding-right: 35px; + } + + .sidebar .format-status .entry-content:before, + .sidebar .format-status .entry-meta:before { + left: auto; + right: 10px; + } + + .sidebar .format-status .entry-content p:first-child:before { + left: auto; + right: 4px; + } + + .sidebar .site-footer .widget-area { + left: auto; + right: 0; + } + + .sidebar .paging-navigation .nav-links { + padding: 0 60px; + } +} + +@media (max-width: 767px) { + .format-image .entry-content img:first-of-type, + .format-image .wp-caption:first-child .wp-caption-text { + margin-right: auto; + } +} + +@media (max-width: 643px) { + .sidebar .entry-header, + .sidebar .entry-content, + .sidebar .entry-summary, + .sidebar .entry-meta, + .sidebar .comment-list, + .sidebar .comment-navigation, + .sidebar .featured-gallery, + .sidebar .post-navigation .nav-links, + .sidebar .format-image .entry-content { + padding-left: 20px; + padding-right: 20px; + } + + #content .format-status .entry-content, + #content .format-status .entry-met { + padding-left: 0; + padding-right: 35px; + } + + .menu-toggle:after { + padding-left: 0; + padding-right: 8px; + } + + .toggled-on .nav-menu, + .toggled-on .nav-menu > ul { + margin-left: auto; + margin-right: 0; + } + + .toggled-on .nav-menu li > ul { + margin-left: auto; + margin-right: 20px; + right: auto; + } + + #content .featured-gallery { + padding-left: 0; + padding-right: 24px; + } + + .gallery-columns-1 .gallery-item { + margin-left: 0; + margin-right: auto; + } + + .comment-author { + margin-left: 30px; + margin-right: auto; + } + + .format-audio .audio-content { + background: none; + float: none; + padding-left: 0; + padding-right: 0; + } + + .gallery-columns-3 .gallery-item:nth-of-type(3n) { + margin-left: 4px; + margin-right: auto; + } +} + +@media (max-width: 359px) { + .gallery { + margin-left: auto; + margin-right: 0; + } + + .gallery .gallery-item:nth-of-type(even) { + margin-left: 0; + margin-right: auto; + } + + .gallery .gallery-item, + .gallery.gallery-columns-3 .gallery-item:nth-of-type(even), + .gallery-columns-3 .gallery-item:nth-of-type(3n), + .gallery-columns-5 .gallery-item:nth-of-type(5n), + .gallery-columns-7 .gallery-item:nth-of-type(7n), + .gallery-columns-9 .gallery-item:nth-of-type(9n) { + margin-left: 4px; + margin-right: auto; + } + + .comment-author .avatar { + margin-left: 5px; + margin-right: auto; + } +} + + +/** + * 9.0 Print + * ---------------------------------------------------------------------------- + */ + +@media print { + .entry-content img.alignleft, + .entry-content .wp-caption.alignleft { + margin-left: auto; + margin-right: 0; + } + + .entry-content img.alignright, + .entry-content .wp-caption.alignright { + margin-left: 0; + margin-right: auto; + } +} \ No newline at end of file diff --git a/wp-content/themes/twentythirteen/screenshot.png b/wp-content/themes/twentythirteen/screenshot.png new file mode 100644 index 0000000..e53088b Binary files /dev/null and b/wp-content/themes/twentythirteen/screenshot.png differ diff --git a/wp-content/themes/twentythirteen/search.php b/wp-content/themes/twentythirteen/search.php new file mode 100644 index 0000000..1519c13 --- /dev/null +++ b/wp-content/themes/twentythirteen/search.php @@ -0,0 +1,36 @@ + + +
        +
        + + + + + + + + + + + + + + + + +
        +
        + + + \ No newline at end of file diff --git a/wp-content/themes/twentythirteen/sidebar-main.php b/wp-content/themes/twentythirteen/sidebar-main.php new file mode 100644 index 0000000..3c700ad --- /dev/null +++ b/wp-content/themes/twentythirteen/sidebar-main.php @@ -0,0 +1,18 @@ + + + \ No newline at end of file diff --git a/wp-content/themes/twentythirteen/sidebar.php b/wp-content/themes/twentythirteen/sidebar.php new file mode 100644 index 0000000..cb5cf98 --- /dev/null +++ b/wp-content/themes/twentythirteen/sidebar.php @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/wp-content/themes/twentythirteen/single.php b/wp-content/themes/twentythirteen/single.php new file mode 100644 index 0000000..1694a0d --- /dev/null +++ b/wp-content/themes/twentythirteen/single.php @@ -0,0 +1,28 @@ + + +
        +
        + + + + + + + + + + +
        +
        + + + \ No newline at end of file diff --git a/wp-content/themes/twentythirteen/style.css b/wp-content/themes/twentythirteen/style.css new file mode 100644 index 0000000..c229164 --- /dev/null +++ b/wp-content/themes/twentythirteen/style.css @@ -0,0 +1,3224 @@ +/* +Theme Name: Twenty Thirteen +Theme URI: http://wordpress.org/themes/twentythirteen +Author: the WordPress team +Author URI: http://wordpress.org/ +Description: The 2013 theme for WordPress takes us back to the blog, featuring a full range of post formats, each displayed beautifully in their own unique way. Design details abound, starting with a vibrant color scheme and matching header images, beautiful typography and icons, and a flexible layout that looks great on any device, big or small. +Version: 1.4 +License: GNU General Public License v2 or later +License URI: http://www.gnu.org/licenses/gpl-2.0.html +Tags: black, brown, orange, tan, white, yellow, light, one-column, two-columns, right-sidebar, fluid-layout, responsive-layout, custom-header, custom-menu, editor-style, featured-images, microformats, post-formats, rtl-language-support, sticky-post, translation-ready, accessibility-ready +Text Domain: twentythirteen + +This theme, like WordPress, is licensed under the GPL. +Use it to make something cool, have fun, and share what you've learned with others. +*/ + + +/** + * Table of Contents: + * + * 1.0 - Reset + * 2.0 - Repeatable Patterns + * 3.0 - Basic Structure + * 4.0 - Header + * 4.1 - Site Header + * 4.2 - Navigation + * 5.0 - Content + * 5.1 - Entry Header + * 5.2 - Entry Meta + * 5.3 - Entry Content + * 5.4 - Galleries + * 5.5 - Post Formats + * 5.6 - Attachments + * 5.7 - Post/Paging Navigation + * 5.8 - Author Bio + * 5.9 - Archives + * 5.10 - Search Results/No posts + * 5.11 - 404 + * 5.12 - Comments + * 5.13 - Multisite + * 6.0 - Sidebar + * 6.1 - Widgets + * 7.0 - Footer + * 8.0 - Media Queries + * 9.0 - Print + * ---------------------------------------------------------------------------- + */ + + +/** + * 1.0 Reset + * + * Modified from Normalize.css to provide cross-browser consistency and a smart + * default styling of HTML elements. + * + * @see http://git.io/normalize + * ---------------------------------------------------------------------------- + */ + +* { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +article, +aside, +details, +figcaption, +figure, +footer, +header, +nav, +section, +summary { + display: block; +} + +audio, +canvas, +video { + display: inline-block; +} + +audio:not([controls]) { + display: none; + height: 0; +} + +[hidden] { + display: none; +} + +html { + font-size: 100%; + overflow-y: scroll; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} + +html, +button, +input, +select, +textarea { + font-family: "Source Sans Pro", Helvetica, sans-serif; +} + +body { + color: #141412; + line-height: 1.5; + margin: 0; +} + +a { + color: #ca3c08; + text-decoration: none; +} + +a:visited { + color: #ac0404; +} + +a:focus { + outline: thin dotted; +} + +a:active, +a:hover { + color: #ea9629; + outline: 0; +} + +a:hover { + text-decoration: underline; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + clear: both; + font-family: Bitter, Georgia, serif; + line-height: 1.3; +} + +h1 { + font-size: 48px; + margin: 33px 0; +} + +h2 { + font-size: 30px; + margin: 25px 0; +} + +h3 { + font-size: 22px; + margin: 22px 0; +} + +h4 { + font-size: 20px; + margin: 25px 0; +} + +h5 { + font-size: 18px; + margin: 30px 0; +} + +h6 { + font-size: 16px; + margin: 36px 0; +} + +address { + font-style: italic; + margin: 0 0 24px; +} + +abbr[title] { + border-bottom: 1px dotted; +} + +b, +strong { + font-weight: bold; +} + +dfn { + font-style: italic; +} + +mark { + background: #ff0; + color: #000; +} + +p { + margin: 0 0 24px; +} + +code, +kbd, +pre, +samp { + font-family: monospace, serif; + font-size: 14px; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; +} + +pre { + background: #f5f5f5; + color: #666; + font-family: monospace; + font-size: 14px; + margin: 20px 0; + overflow: auto; + padding: 20px; + white-space: pre; + white-space: pre-wrap; + word-wrap: break-word; +} + +blockquote, +q { + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + quotes: none; +} + +blockquote:before, +blockquote:after, +q:before, +q:after { + content: ""; + content: none; +} + +blockquote { + font-size: 18px; + font-style: italic; + font-weight: 300; + margin: 24px 40px; +} + +blockquote blockquote { + margin-right: 0; +} + +blockquote cite, +blockquote small { + font-size: 14px; + font-weight: normal; + text-transform: uppercase; +} + +blockquote em, +blockquote i { + font-style: normal; + font-weight: 300; +} + +blockquote strong, +blockquote b { + font-weight: 400; +} + +small { + font-size: smaller; +} + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +dl { + margin: 0 20px; +} + +dt { + font-weight: bold; +} + +dd { + margin: 0 0 20px; +} + +menu, +ol, +ul { + margin: 16px 0; + padding: 0 0 0 40px; +} + +ul { + list-style-type: square; +} + +nav ul, +nav ol { + list-style: none; + list-style-image: none; +} + +li > ul, +li > ol { + margin: 0; +} + +img { + -ms-interpolation-mode: bicubic; + border: 0; + vertical-align: middle; +} + +svg:not(:root) { + overflow: hidden; +} + +figure { + margin: 0; +} + +form { + margin: 0; +} + +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} + +legend { + border: 0; + padding: 0; + white-space: normal; +} + +button, +input, +select, +textarea { + font-size: 100%; + margin: 0; + max-width: 100%; + vertical-align: baseline; +} + +button, +input { + line-height: normal; +} + +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; + cursor: pointer; +} + +button[disabled], +input[disabled] { + cursor: default; +} + +input[type="checkbox"], +input[type="radio"] { + padding: 0; +} + +input[type="search"] { + -webkit-appearance: textfield; + padding-right: 2px; /* Don't cut off the webkit search cancel button */ + width: 270px; +} + +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} + +textarea { + overflow: auto; + vertical-align: top; +} + +table { + border-bottom: 1px solid #ededed; + border-collapse: collapse; + border-spacing: 0; + font-size: 14px; + line-height: 2; + margin: 0 0 20px; + width: 100%; +} + +caption, +th, +td { + font-weight: normal; + text-align: left; +} + +caption { + font-size: 16px; + margin: 20px 0; +} + +th { + font-weight: bold; + text-transform: uppercase; +} + +td { + border-top: 1px solid #ededed; + padding: 6px 10px 6px 0; +} + +del { + color: #333; +} + +ins { + background: #fff9c0; + text-decoration: none; +} + +hr { + background: url(images/dotted-line.png) repeat center top; + background-size: 4px 4px; + border: 0; + height: 1px; + margin: 0 0 24px; +} + + +/** + * 2.0 Repeatable Patterns + * ---------------------------------------------------------------------------- + */ + +.genericon:before, +.menu-toggle:after, +.featured-post:before, +.date a:before, +.entry-meta .author a:before, +.format-audio .entry-content:before, +.comments-link a:before, +.tags-links a:first-child:before, +.categories-links a:first-child:before, +.edit-link a:before, +.attachment .entry-title:before, +.attachment-meta:before, +.attachment-meta a:before, +.comment-awaiting-moderation:before, +.comment-reply-link:before, +.comment-reply-login:before, +.comment-reply-title small a:before, +.bypostauthor > .comment-body .fn:before, +.error404 .page-title:before { + -webkit-font-smoothing: antialiased; + display: inline-block; + font: normal 16px/1 Genericons; + vertical-align: text-bottom; +} + +/* Clearing floats */ +.clear:after, +.attachment .entry-header:after, +.site-footer .widget-area:after, +.entry-content:after, +.page-content:after, +.navigation:after, +.nav-links:after, +.gallery:after, +.comment-form-author:after, +.comment-form-email:after, +.comment-form-url:after, +.comment-body:after { + clear: both; +} + +.clear:before, +.clear:after, +.attachment .entry-header:before, +.attachment .entry-header:after, +.site-footer .widget-area:before, +.site-footer .widget-area:after, +.entry-content:before, +.entry-content:after, +.page-content:before, +.page-content:after, +.navigation:before, +.navigation:after, +.nav-links:before, +.nav-links:after, +.gallery:before, +.gallery:after, +.comment-form-author:before, +.comment-form-author:after, +.comment-form-email:before, +.comment-form-email:after, +.comment-form-url:before, +.comment-form-url:after, +.comment-body:before, +.comment-body:after { + content: ""; + display: table; +} + +/* Assistive text */ +.screen-reader-text { + clip: rect(1px, 1px, 1px, 1px); + position: absolute !important; +} + +.screen-reader-text:focus { + background-color: #f1f1f1; + border-radius: 3px; + box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.6); + clip: auto !important; + color: #21759b; + display: block; + font-size: 14px; + font-weight: bold; + height: auto; + line-height: normal; + padding: 15px 23px 14px; + position: absolute; + left: 5px; + top: 5px; + text-decoration: none; + width: auto; + z-index: 100000; /* Above WP toolbar */ +} + +/* Form fields, general styles first. */ +button, +input, +textarea { + border: 2px solid #d4d0ba; + font-family: inherit; + padding: 5px; +} + +input, +textarea { + color: #141412; +} + +input:focus, +textarea:focus { + border: 2px solid #c3c0ab; + outline: 0; +} + +/* Buttons */ +button, +input[type="submit"], +input[type="button"], +input[type="reset"] { + background: #e05d22; /* Old browsers */ + background: -webkit-linear-gradient(top, #e05d22 0%, #d94412 100%); /* Chrome 10+, Safari 5.1+ */ + background: linear-gradient(to bottom, #e05d22 0%, #d94412 100%); /* W3C */ + border: none; + border-bottom: 3px solid #b93207; + border-radius: 2px; + color: #fff; + display: inline-block; + padding: 11px 24px 10px; + text-decoration: none; +} + +button:hover, +button:focus, +input[type="submit"]:hover, +input[type="button"]:hover, +input[type="reset"]:hover, +input[type="submit"]:focus, +input[type="button"]:focus, +input[type="reset"]:focus { + background: #ed6a31; /* Old browsers */ + background: -webkit-linear-gradient(top, #ed6a31 0%, #e55627 100%); /* Chrome 10+, Safari 5.1+ */ + background: linear-gradient(to bottom, #ed6a31 0%, #e55627 100%); /* W3C */ + outline: none; +} + +button:active, +input[type="submit"]:active, +input[type="button"]:active, +input[type="reset"]:active { + background: #d94412; /* Old browsers */ + background: -webkit-linear-gradient(top, #d94412 0%, #e05d22 100%); /* Chrome 10+, Safari 5.1+ */ + background: linear-gradient(to bottom, #d94412 0%, #e05d22 100%); /* W3C */ + border: none; + border-top: 3px solid #b93207; + padding: 10px 24px 11px; +} + +.post-password-required input[type="submit"] { + padding: 7px 24px 4px; + vertical-align: bottom; +} + +.post-password-required input[type="submit"]:active { + padding: 5px 24px 6px; +} + +/* Placeholder text color -- selectors need to be separate to work. */ +::-webkit-input-placeholder { + color: #7d7b6d; +} + +:-moz-placeholder { + color: #7d7b6d; +} + +::-moz-placeholder { + color: #7d7b6d; +} + +:-ms-input-placeholder { + color: #7d7b6d; +} + +/* + * Responsive images + * + * Fluid images for posts, comments, and widgets + */ +.entry-content img, +.entry-summary img, +.comment-content img, +.widget img, +.wp-caption { + max-width: 100%; +} + +/* Make sure images with WordPress-added height and width attributes are scaled correctly. */ +.entry-content img, +.entry-summary img, +.comment-content img[height], +img[class*="align"], +img[class*="wp-image-"], +img[class*="attachment-"] { + height: auto; +} + +img.size-full, +img.size-large, +img.wp-post-image { + height: auto; + max-width: 100%; +} + +/* Make sure videos and embeds fit their containers. */ +embed, +iframe, +object, +video { + max-width: 100%; +} + +/* Override the Twitter embed fixed width. */ +.entry-content .twitter-tweet-rendered { + max-width: 100% !important; +} + +/* Images */ +.alignleft { + float: left; +} + +.alignright { + float: right; +} + +.aligncenter { + display: block; + margin-left: auto; + margin-right: auto; +} + +figure.wp-caption.alignleft, +img.alignleft { + margin: 5px 20px 5px 0; +} + +.wp-caption.alignleft { + margin: 5px 10px 5px 0; +} + +figure.wp-caption.alignright, +img.alignright { + margin: 5px 0 5px 20px; +} + +.wp-caption.alignright { + margin: 5px 0 5px 10px; +} + +img.aligncenter { + margin: 5px auto; +} + +img.alignnone { + margin: 5px 0; +} + +.wp-caption .wp-caption-text, +.entry-caption, +.gallery-caption { + color: #220e10; + font-size: 18px; + font-style: italic; + font-weight: 300; + margin: 0 0 24px; +} + +div.wp-caption.alignright img[class*="wp-image-"] { + float: right; +} + +div.wp-caption.alignright .wp-caption-text { + padding-left: 10px; +} + +img.wp-smiley, +.rsswidget img { + border: 0; + border-radius: 0; + box-shadow: none; + margin-bottom: 0; + margin-top: 0; + padding: 0; +} + +.wp-caption.alignleft + ul, +.wp-caption.alignleft + ol { + list-style-position: inside; +} + + +/** + * 3.0 Basic Structure + * ---------------------------------------------------------------------------- + */ + +.site { + background-color: #fff; + border-left: 1px solid #f2f2f2; + border-right: 1px solid #f2f2f2; + margin: 0 auto; + max-width: 1600px; + width: 100%; +} + +.site-main { + position: relative; +} + +.site-main .sidebar-container { + height: 0; + position: absolute; + top: 40px; + width: 100%; + z-index: 1; +} + +.site-main .sidebar-inner { + margin: 0 auto; + max-width: 1040px; +} + + +/** + * 4.0 Header + * ---------------------------------------------------------------------------- + */ + +/** + * 4.1 Site Header + * ---------------------------------------------------------------------------- + */ + +.site-header { + position: relative; +} + +.site-header .home-link { + color: #141412; + display: block; + margin: 0 auto; + max-width: 1080px; + min-height: 230px; + padding: 0 20px; + text-decoration: none; + width: 100%; +} + +.site-header .site-title:hover { + text-decoration: underline; +} + +.site-title { + font-size: 60px; + font-weight: bold; + line-height: 1; + margin: 0; + padding: 58px 0 10px; +} + +.site-description { + font: 300 italic 24px "Source Sans Pro", Helvetica, sans-serif; + margin: 0; +} + + +/** + * 4.2 Navigation + * ---------------------------------------------------------------------------- + */ + +.main-navigation { + clear: both; + margin: 0 auto; + max-width: 1080px; + min-height: 45px; + position: relative; +} + +ul.nav-menu, +div.nav-menu > ul { + margin: 0; + padding: 0 40px 0 0; +} + +.nav-menu li { + display: inline-block; + position: relative; +} + +.nav-menu li a { + color: #141412; + display: block; + font-size: 15px; + line-height: 1; + padding: 15px 20px; + text-decoration: none; +} + +.nav-menu li:hover > a, +.nav-menu li a:hover, +.nav-menu li:focus > a, +.nav-menu li a:focus { + background-color: #220e10; + color: #fff; +} + +.nav-menu .sub-menu, +.nav-menu .children { + background-color: #220e10; + border: 2px solid #f7f5e7; + border-top: 0; + padding: 0; + position: absolute; + left: -2px; + z-index: 99999; + height: 1px; + width: 1px; + overflow: hidden; + clip: rect(1px, 1px, 1px, 1px); +} + +.nav-menu .sub-menu ul, +.nav-menu .children ul { + border-left: 0; + left: 100%; + top: 0; +} + +ul.nav-menu ul a, +.nav-menu ul ul a { + color: #fff; + margin: 0; + width: 200px; +} + +ul.nav-menu ul a:hover, +.nav-menu ul ul a:hover, +ul.nav-menu ul a:focus, +.nav-menu ul ul a:focus { + background-color: #db572f; +} + +ul.nav-menu li:hover > ul, +.nav-menu ul li:hover > ul, +ul.nav-menu .focus > ul, +.nav-menu .focus > ul { + clip: inherit; + overflow: inherit; + height: inherit; + width: inherit; +} + +.nav-menu .current_page_item > a, +.nav-menu .current_page_ancestor > a, +.nav-menu .current-menu-item > a, +.nav-menu .current-menu-ancestor > a { + color: #bc360a; + font-style: italic; +} + +.menu-toggle { + display: none; +} + +/* Navbar */ +.navbar { + background-color: #f7f5e7; + margin: 0 auto; + max-width: 1600px; + width: 100%; +} + +.site-header .search-form { + position: absolute; + right: 20px; + top: 1px; +} + +.site-header .search-field { + background-color: transparent; + background-image: url(images/search-icon.png); + background-position: 5px center; + background-repeat: no-repeat; + background-size: 24px 24px; + border: none; + cursor: pointer; + height: 37px; + margin: 3px 0; + padding: 0 0 0 34px; + position: relative; + -webkit-transition: width 400ms ease, background 400ms ease; + transition: width 400ms ease, background 400ms ease; + width: 1px; +} + +.site-header .search-field:focus { + background-color: #fff; + border: 2px solid #c3c0ab; + cursor: text; + outline: 0; + width: 230px; +} + + +/** + * 5.0 Content + * ---------------------------------------------------------------------------- + */ + +.hentry { + padding: 40px 0; +} + +.entry-header, +.entry-content, +.entry-summary, +.entry-meta { + margin: 0 auto; + max-width: 604px; + width: 100%; +} + +.sidebar .entry-header, +.sidebar .entry-content, +.sidebar .entry-summary, +.sidebar .entry-meta { + max-width: 1040px; + padding: 0 376px 0 60px; +} + + +/** + * 5.1 Entry Header + * ---------------------------------------------------------------------------- + */ + +.sidebar .entry-header .entry-meta { + padding: 0; +} + +.entry-thumbnail img { + display: block; + margin: 0 auto 10px; +} + +.entry-header { + margin-bottom: 30px; +} + +.entry-title { + font-weight: normal; + margin: 0 0 5px; +} + +.entry-title a { + color: #141412; +} + +.entry-title a:hover { + color: #ea9629; +} + + +/** + * 5.2 Entry Meta + * ---------------------------------------------------------------------------- + */ + +.entry-meta { + clear: both; + font-size: 14px; +} + +.entry-meta a { + color: #bc360a; +} + +.entry-meta a:hover { + color: #bc360a; +} + +.entry-meta > span { + margin-right: 20px; +} + +.entry-meta > span:last-child { + margin-right: 0; +} + +.featured-post:before { + content: "\f308"; + margin-right: 2px; +} + +.entry-meta .date a:before { + content: "\f303"; +} + +.comments-link a:before { + content: "\f300"; + margin-right: 2px; + position: relative; + top: -1px; +} + +.entry-meta .author a:before { + content: "\f304"; + position: relative; + top: -1px; +} + +.categories-links a:first-child:before { + content: "\f301"; +} + +.tags-links a:first-child:before { + content: "\f302"; + position: relative; + top: -1px; +} + +.edit-link a:before { + content: "\f411"; + position: relative; + top: -1px; +} + +.single-author .entry-meta .author, +.sticky.format-standard .entry-meta .date, +.sticky.format-audio .entry-meta .date, +.sticky.format-chat .entry-meta .date, +.sticky.format-image .entry-meta .date, +.sticky.format-gallery .entry-meta .date { + display: none; +} + + +/** + * 5.3 Entry Content + * ---------------------------------------------------------------------------- + */ + +.entry-content { + -webkit-hyphens: auto; + -moz-hyphens: auto; + -ms-hyphens: auto; + hyphens: auto; + word-wrap: break-word; +} + +.entry-content a, +.comment-content a { + color: #bc360a; +} + +.entry-content a:hover, +.comment-content a:hover { + color: #ea9629; +} + +.entry-content .more-link { + white-space: nowrap; +} + +.entry-content blockquote { + font-size: 24px; +} + +.entry-content blockquote cite, +.entry-content blockquote small { + font-size: 16px; +} + +.entry-content img.alignleft, +.entry-content .wp-caption.alignleft { + margin-left: -60px; +} + +.entry-content img.alignright, +.entry-content .wp-caption.alignright { + margin-right: -60px; +} + +footer.entry-meta { + margin-top: 24px; +} + +.format-standard footer.entry-meta { + margin-top: 0; +} + +/* Page links */ +.page-links { + clear: both; + font-size: 16px; + font-style: italic; + font-weight: normal; + line-height: 2.2; + margin: 20px 0; + text-transform: uppercase; +} + +.page-links a, +.page-links > span { + background: #fff; + border: 1px solid #fff; + padding: 5px 10px; + text-decoration: none; +} + +.format-status .entry-content .page-links a, +.format-gallery .entry-content .page-links a, +.format-chat .entry-content .page-links a, +.format-quote .entry-content .page-links a, +.page-links a { + background: #e63f2a; + border: 1px solid #e63f2a; + color: #fff; +} + +.format-gallery .entry-content .page-links a:hover, +.format-audio .entry-content .page-links a:hover, +.format-status .entry-content .page-links a:hover, +.format-video .entry-content .page-links a:hover, +.format-chat .entry-content .page-links a:hover, +.format-quote .entry-content .page-links a:hover, +.page-links a:hover { + background: #fff; + color: #e63f2a; +} + +.format-status .entry-content .page-links > span, +.format-quote .entry-content .page-links > span { + background: none; +} + +.page-links .page-links-title { + background: transparent; + border: none; + margin-right: 20px; + padding: 0; +} + +/* Mediaelements */ +.hentry .mejs-mediaelement, +.hentry .mejs-container .mejs-controls { + background: #220e10; +} + +.hentry .mejs-controls .mejs-time-rail .mejs-time-loaded, +.hentry .mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-current { + background: #fff; +} + +.hentry .mejs-controls .mejs-time-rail .mejs-time-current { + background: #ea9629; +} + +.hentry .mejs-controls .mejs-time-rail .mejs-time-total, +.hentry .mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-total { + background: #595959; +} + +.hentry .mejs-controls .mejs-time-rail span, +.hentry .mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-total, +.hentry .mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-current { + border-radius: 0; +} + + +/** + * 5.4 Galleries + * ---------------------------------------------------------------------------- + */ + +.gallery { + margin-bottom: 20px; + margin-left: -4px; +} + +.gallery-item { + float: left; + margin: 0 4px 4px 0; + overflow: hidden; + position: relative; +} + +.gallery-columns-1.gallery-size-medium, +.gallery-columns-1.gallery-size-thumbnail, +.gallery-columns-2.gallery-size-thumbnail, +.gallery-columns-3.gallery-size-thumbnail { + display: table; + margin: 0 auto 20px; +} + +.gallery-columns-1 .gallery-item, +.gallery-columns-2 .gallery-item, +.gallery-columns-3 .gallery-item { + text-align: center; +} + +.gallery-columns-4 .gallery-item { + max-width: 23%; + max-width: -webkit-calc(25% - 4px); + max-width: calc(25% - 4px); +} + +.gallery-columns-5 .gallery-item { + max-width: 19%; + max-width: -webkit-calc(20% - 4px); + max-width: calc(20% - 4px); +} + +.gallery-columns-6 .gallery-item { + max-width: 15%; + max-width: -webkit-calc(16.7% - 4px); + max-width: calc(16.7% - 4px); +} + +.gallery-columns-7 .gallery-item { + max-width: 13%; + max-width: -webkit-calc(14.28% - 4px); + max-width: calc(14.28% - 4px); +} + +.gallery-columns-8 .gallery-item { + max-width: 11%; + max-width: -webkit-calc(12.5% - 4px); + max-width: calc(12.5% - 4px); +} + +.gallery-columns-9 .gallery-item { + max-width: 9%; + max-width: -webkit-calc(11.1% - 4px); + max-width: calc(11.1% - 4px); +} + +.gallery-columns-1 .gallery-item:nth-of-type(1n), +.gallery-columns-2 .gallery-item:nth-of-type(2n), +.gallery-columns-3 .gallery-item:nth-of-type(3n), +.gallery-columns-4 .gallery-item:nth-of-type(4n), +.gallery-columns-5 .gallery-item:nth-of-type(5n), +.gallery-columns-6 .gallery-item:nth-of-type(6n), +.gallery-columns-7 .gallery-item:nth-of-type(7n), +.gallery-columns-8 .gallery-item:nth-of-type(8n), +.gallery-columns-9 .gallery-item:nth-of-type(9n) { + margin-right: 0; +} + +.gallery-columns-1.gallery-size-medium figure.gallery-item:nth-of-type(1n+1), +.gallery-columns-1.gallery-size-thumbnail figure.gallery-item:nth-of-type(1n+1), +.gallery-columns-2.gallery-size-thumbnail figure.gallery-item:nth-of-type(2n+1), +.gallery-columns-3.gallery-size-thumbnail figure.gallery-item:nth-of-type(3n+1) { + clear: left; +} + +.gallery-caption { + background-color: rgba(0, 0, 0, 0.7); + box-sizing: border-box; + color: #fff; + font-size: 14px; + line-height: 1.3; + margin: 0; + max-height: 50%; + opacity: 0; + padding: 2px 8px; + position: absolute; + bottom: 0; + left: 0; + text-align: left; + -webkit-transition: opacity 400ms ease; + transition: opacity 400ms ease; + width: 100%; +} + +.gallery-caption:before { + box-shadow: 0 -10px 15px #000 inset; + content: ""; + height: 100%; + min-height: 49px; + position: absolute; + left: 0; + top: 0; + width: 100%; +} + +.gallery-item:hover .gallery-caption { + opacity: 1; +} + +.gallery-columns-7 .gallery-caption, +.gallery-columns-8 .gallery-caption, +.gallery-columns-9 .gallery-caption { + display: none; +} + + +/** + * 5.5 Post Formats + * ---------------------------------------------------------------------------- + */ + +/* Aside */ +.format-aside { + background-color: #f7f5e7; +} + +.blog .format-aside:first-of-type, +.single .format-aside:first-of-type, +.format-aside + .format-aside, +.format-aside + .format-link, +.format-link + .format-aside { + box-shadow: inset 0 2px 2px rgba(173, 165, 105, 0.2); +} + +.format-aside .entry-meta { + margin-top: 0; +} + +.format-aside blockquote { + font-size: 100%; + font-weight: normal; +} + +.format-aside cite { + font-size: 100%; + text-transform: none; +} + +.format-aside cite:before { + content: "\2014"; + margin-right: 5px; +} + +/* Audio */ +.format-audio { + background-color: #db572f; +} + +.format-audio .entry-title { + font-size: 28px; + font-weight: bold; +} + +.format-audio .entry-content:before { + content: "\f109"; + float: left; + font-size: 64px; + position: relative; + top: 4px; +} + +.format-audio .entry-content a, +.format-audio .entry-meta a, +.format-audio .entry-content a:hover, +.format-audio .entry-meta a:hover { + color: #fbfaf3; +} + +.format-audio .audio-content { + background: url(images/dotted-line.png) repeat-y left top; + background-size: 4px 4px; + float: right; + padding-left: 35px; + width: 80%; + width: -webkit-calc(100% - 85px); + width: calc(100% - 85px); +} + +.format-audio .wp-audio-shortcode { + height: 30px !important; /* Override mediaelement.js style */ + margin: 20px 0; + max-width: 400px !important; /* Override mediaelement.js style */ +} + +.format-audio audio { + max-width: 100% !important; /* Avoid player width overflow. */ +} + +/* Chat */ +.format-chat { + background-color: #eadaa6; +} + +.format-chat .entry-title { + font-size: 28px; + font-weight: bold; +} + +.format-chat .entry-meta a, +.format-chat .entry-content a { + color: #722d19; +} + +.format-chat .entry-meta .date a:before { + content: "\f108"; + margin-right: 2px; +} + +.format-chat .entry-meta .author { + display: none; +} + +.format-chat .chat { + margin: 0; +} + +.format-chat .chat .chat-timestamp { + color: #722d19; + float: right; + font-size: 12px; + font-weight: normal; + margin: 5px 10px 0; +} + +.format-chat .chat .fn { + font-style: normal; +} + +/* Gallery */ +.format-gallery { + background-color: #fbca3c; +} + +.format-gallery .entry-header { + margin-bottom: 15px; +} + +.format-gallery .entry-title { + font-size: 50px; + font-weight: 400; + margin: 0; +} + +.format-gallery .entry-meta a, +.format-gallery .entry-content a { + color: #722d19; +} + +/* Image */ +.format-image .entry-title { + font-size: 28px; + font-weight: bold; +} + +.format-image .categories-links, +.format-image .tags-links { + display: none; +} + +/* Link */ +.format-link { + background-color: #f7f5e7; +} + +.blog .format-link:first-of-type, +.single .format-link:first-of-type { + box-shadow: inset 0 2px 2px rgba(173, 165, 105, 0.2); +} + +.format-link .entry-header, +.format-link .entry-content p:last-child { + margin-bottom: 0; +} + +.format-link .entry-title { + color: #ca3c08; + display: inline; + font: 300 italic 20px "Source Sans Pro", Helvetica, sans-serif; + margin-right: 20px; +} + +.format-link .entry-title a { + color: #bc360a; +} + +.format-link div.entry-meta { + display: inline; +} + +/* Standard */ +.format-standard .wp-video, +.format-standard .wp-audio-shortcode, +.format-audio .wp-audio-shortcode, +.format-standard .video-player { + margin-bottom: 24px; +} + +/* Quote */ +.format-quote { + background-color: #210d10; +} + +.format-quote .entry-content, +.format-quote .entry-meta { + color: #f7f5e7; +} + +.format-quote .entry-content blockquote { + font-size: 28px; + margin: 0; +} + +.format-quote .entry-content a, +.format-quote .entry-meta a, +.format-quote .linked { + color: #e63f2a; +} + +.format-quote .entry-content cite a { + border-bottom: 1px dotted #fff; + color: #fff; +} + +.format-quote .entry-content cite a:hover { + text-decoration: none; +} + +.format-quote blockquote small, +.format-quote blockquote cite { + display: block; + font-size: 16px; +} + +.format-quote blockquote { + font-style: italic; + font-weight: 300; + padding-left: 75px; + position: relative; +} + +.format-quote blockquote:before { + content: '\201C'; + font-size: 140px; + font-weight: 400; + line-height: .8; + padding-right: 25px; + position: absolute; + left: -15px; + top: -3px; +} + +.format-quote .entry-meta .author { + display: none; +} + +/* Status */ +.format-status { + background-color: #722d19; + padding: 0; +} + +.format-status .entry-content, +.format-status .entry-meta { + padding-left: 35px; + position: relative; +} + +.format-status .entry-content a { + color: #eadaa6; +} + +.format-status .entry-meta a { + color: #f7f5e7; +} + +.sidebar .format-status .entry-content, +.sidebar .format-status .entry-meta { + padding-left: 95px; +} + +.format-status .entry-content:before, +.format-status .entry-meta:before { + background: url(images/dotted-line.png) repeat-y left bottom; + background-size: 4px 4px; + content: ""; + display: block; + height: 100%; + position: absolute; + left: 10px; + top: 0; + width: 1px; +} + +.sidebar .format-status .entry-content:before, +.sidebar .format-status .entry-meta:before { + left: 70px; +} + +.format-status .categories-links, +.format-status .tags-links { + display: none; +} + +/* Ensures the dots in the dot background are in lockstep. */ +.format-status .entry-meta:before { + background-position: left top; +} + +.format-status .entry-content { + color: #f7f5e7; + font-size: 24px; + font-style: italic; + font-weight: 300; + padding-bottom: 30px; + padding-top: 40px; + position: relative; +} + +.format-status .entry-content p:first-child:before { + background-color: rgba(0, 0, 0, 0.65); + content: ""; + height: 3px; + margin-top: 13px; + position: absolute; + left: 4px; + width: 13px; +} + +.sidebar .format-status .entry-content > p:first-child:before { + left: 64px; +} + +.format-status .entry-content p:last-child { + margin-bottom: 0; +} + +.format-status .entry-meta { + margin-top: 0; + padding-bottom: 40px; +} + +.format-status .entry-meta .date a:before { + content: "\f105"; +} + +/* Video */ +.format-video { + background-color: #db572f; +} + +.format-video .entry-content a, +.format-video .entry-meta a, +.format-video .entry-content a:hover, +.format-video .entry-meta a:hover { + color: #fbfaf3; +} + +.format-video .entry-title { + font-size: 50px; + font-weight: 400; +} + +.format-video .entry-meta { + color: #220e10; +} + + +/** + * 5.6 Attachments + * ---------------------------------------------------------------------------- + */ + +.attachment .hentry { + background-color: #e8e5ce; + margin: 0; + padding: 0; +} + +.attachment .entry-header { + margin-bottom: 0; + max-width: 1040px; + padding: 30px 0; +} + +.attachment .entry-title { + display: inline-block; + float: left; + font: 300 italic 30px "Source Sans Pro", Helvetica, sans-serif; + margin: 0; +} + +.attachment .entry-title:before { + content: "\f416"; + font-size: 32px; + margin-right: 10px; +} + +.attachment .entry-meta { + clear: none; + color: inherit; + float: right; + max-width: 604px; + padding: 9px 0 0; + text-align: right; +} + +.hentry.attachment:not(.image-attachment) .entry-meta { + max-width: 104px; +} + +.attachment footer.entry-meta { + display: none; +} + +.attachment-meta:before { + content: "\f307"; +} + +.full-size-link a:before { + content: "\f402"; +} + +.full-size-link:before { + content: none; +} + +.attachment .entry-meta a, +.attachment .entry-meta .edit-link:before, +.attachment .full-size-link:before { + color: #ca3c08; +} + +.attachment .entry-content { + background-color: #fff; + max-width: 100%; + padding: 40px 0; +} + +.image-navigation { + margin: 0 auto; + max-width: 1040px; + position: relative; +} + +.image-navigation a:hover { + text-decoration: none; +} + +.image-navigation .nav-previous, +.image-navigation .nav-next { + position: absolute; + top: 50px; +} + +.image-navigation .nav-previous { + left: 0; +} + +.image-navigation .nav-next { + right: 0; +} + +.image-navigation .meta-nav { + font-size: 32px; + font-weight: 300; + vertical-align: -4px; +} + +.attachment .entry-attachment, +.attachment .type-attachment p { + margin: 0 auto; + max-width: 724px; + text-align: center; +} + +.attachment .entry-attachment .attachment { + display: inline-block; +} + +.attachment .entry-caption { + text-align: left; +} + +.attachment .entry-description { + margin: 20px auto 0; + max-width: 604px; +} + +.attachment .entry-caption p:last-child, +.attachment .entry-description p:last-child { + margin: 0; +} + +.attachment .site-main .sidebar-container { + display: none; +} + +.attachment .entry-content .mejs-audio { + max-width: 400px; + margin: 0 auto; +} + +.attachment .entry-content .wp-video { + margin: 0 auto; +} + +.attachment .entry-content .mejs-container { + margin-bottom: 24px; +} + +/** + * 5.7 Post/Paging Navigation + * ---------------------------------------------------------------------------- + */ + +.navigation .nav-previous { + float: left; +} + +.navigation .nav-next { + float: right; +} + +.navigation a { + color: #bc360a; +} + +.navigation a:hover { + color: #ea9629; + text-decoration: none; +} + +.paging-navigation { + background-color: #e8e5ce; + padding: 40px 0; +} + +.paging-navigation .nav-links { + margin: 0 auto; + max-width: 604px; + width: 100%; +} + +.sidebar .paging-navigation .nav-links { + max-width: 1040px; + padding: 0 376px 0 60px; +} + +.paging-navigation .nav-next { + padding: 13px 0; +} + +.paging-navigation a { + font-size: 22px; + font-style: italic; + font-weight: 300; +} + +.paging-navigation .meta-nav { + background-color: #e63f2a; + border-radius: 50%; + color: #fff; + display: inline-block; + font-size: 26px; + padding: 3px 0 8px; + text-align: center; + width: 50px; +} + +.paging-navigation .nav-previous .meta-nav { + margin-right: 10px; + padding: 17px 0 23px; + width: 80px; +} + +.paging-navigation .nav-next .meta-nav { + margin-left: 10px; +} + +.paging-navigation a:hover .meta-nav { + background-color: #ea9629; + text-decoration: none; +} + +.post-navigation { + background-color: #fff; + color: #ca3c08; + font-size: 20px; + font-style: italic; + font-weight: 300; + padding: 20px 0; +} + +.post-navigation .nav-links { + margin: 0 auto; + max-width: 1040px; +} + +.sidebar .post-navigation .nav-links { + padding: 0 376px 0 60px; +} + +.post-navigation a[rel="next"] { + float: right; + text-align: right; +} + + +/** + * 5.8 Author Bio + * ---------------------------------------------------------------------------- + */ + +.author-info { + margin: 0 auto; + max-width: 604px; + padding: 30px 0 10px; + text-align: left; /* gallery & video post formats */ + width: 100%; +} + +.author.sidebar .author-info { + max-width: 1040px; + padding: 30px 376px 10px 60px; +} + +.single .author-info { + padding: 50px 0 0; +} + +.author-avatar .avatar { + float: left; + margin: 0 30px 30px 0; +} + +.single-format-status .author-description { + color: #f7f5e7; +} + +.author-description .author-title { + clear: none; + font: 300 italic 20px "Source Sans Pro", Helvetica, sans-serif; + margin: 0 0 8px; +} + +.author-link { + color: #ca3c08; + margin-left: 2px; +} + +.author.archive .author-link { + display: none; +} + + +/** + * 5.9 Archives + * ---------------------------------------------------------------------------- + */ + +.archive-header { + background-color: #e8e5ce; +} + +.archive-title, +.archive-meta { + font: 300 italic 30px "Source Sans Pro", Helvetica, sans-serif; + margin: 0 auto; + max-width: 1040px; + padding: 30px 0; + width: 100%; +} + +.archive-meta { + font-size: 16px; + font-style: normal; + font-weight: normal; + margin-top: -15px; + padding: 0 0 11px; +} + +.sidebar .archive-meta { + padding-right: 316px; +} + + +/** + * 5.10 Search Results/No posts + * ---------------------------------------------------------------------------- + */ + +.page-header { + background-color: #e8e5ce; +} + +.page-title { + font: 300 italic 30px "Source Sans Pro", Helvetica, sans-serif; + margin: 0 auto; + max-width: 1040px; + padding: 30px 0; + width: 100%; +} + +.page-content { + margin: 0 auto; + max-width: 604px; + padding: 40px 0; + width: 100%; +} + +.sidebar .page-content { + margin: 0 auto; + max-width: 1040px; + padding: 40px 376px 40px 60px; +} + + +/** + * 5.11 404 + * ---------------------------------------------------------------------------- + */ + +.error404 .page-header { + background-color: #fff; +} + +.error404 .page-title { + line-height: 0.6; + margin: 0; + padding: 300px; + position: relative; + text-align: center; + width: auto; +} + +.error404 .page-title:before { + color: #e8e5ce; + content: "\f423"; + font-size: 964px; + line-height: 0.6; + overflow: hidden; + position: absolute; + left: 7px; + top: 28px; +} + +.error404 .page-wrapper { + background-color: #e8e5ce; +} + +.error404 .page-header, +.error404 .page-content { + margin: 0 auto; + max-width: 1040px; + padding-bottom: 40px; + width: 100%; +} + + +/** + * 5.12 Comments + * ---------------------------------------------------------------------------- + */ + +.comments-title, +.comment-list, +.comment-reply-title, +.must-log-in, +.comment-respond .comment-form, +.comment-respond iframe { + display: block; + margin-left: auto; + margin-right: auto; + max-width: 604px; + width: 100%; +} + +.sidebar .comments-title, +.sidebar .comment-list, +.sidebar .must-log-in, +.sidebar .comment-reply-title, +.sidebar .comment-navigation, +.sidebar .comment-respond .comment-form { + max-width: 1040px; + padding-left: 60px; + padding-right: 376px; +} + +.comments-title { + font: 300 italic 28px "Source Sans Pro", Helvetica, sans-serif; +} + +.comment-list, +.comment-list .children { + list-style-type: none; + padding: 0; +} + +.comment-list .children { + margin-left: 20px; +} + +.comment-list > li:after, +.comment-list .children > li:before { + background: url(images/dotted-line.png) repeat left top; + background-size: 4px 4px; + content: ""; + display: block; + height: 1px; + width: 100%; +} + +.comment-list > li:last-child:after { + display: none; +} + +.comment-body { + padding: 24px 0; + position: relative; +} + +.comment-author { + float: left; + max-width: 74px; +} + +.comment-author .avatar { + display: block; + margin-bottom: 10px; +} + +.comment-author .fn { + word-wrap: break-word; +} + +.comment-author .fn, +.comment-author .url, +.comment-reply-link, +.comment-reply-login { + color: #bc360a; + font-size: 14px; + font-style: normal; + font-weight: normal; +} + +.says { + display: none; +} + +.no-avatars .comment-author { + margin: 0 0 5px; + max-width: 100%; + position: relative; +} + +.no-avatars .comment-metadata, +.no-avatars .comment-content, +.no-avatars .comment-list .reply { + width: 100%; +} + +.bypostauthor > .comment-body .fn:before { + content: "\f408"; + vertical-align: text-top; +} + +.comment-list .edit-link { + margin-left: 20px; +} + +.comment-metadata, +.comment-awaiting-moderation, +.comment-content, +.comment-list .reply { + float: right; + width: 79%; + width: -webkit-calc(100% - 124px); + width: calc(100% - 124px); + word-wrap: break-word; +} + +.comment-meta, +.comment-meta a { + color: #a2a2a2; + font-size: 13px; +} + +.comment-meta a:hover { + color: #ea9629; +} + +.comment-metadata { + margin-bottom: 20px; +} + +.ping-meta { + color: #a2a2a2; + font-size: 13px; + line-height: 2; +} + +.comment-awaiting-moderation { + color: #a2a2a2; +} + +.comment-awaiting-moderation:before { + content: "\f414"; + margin-right: 5px; + position: relative; + top: -2px; +} + +.comment-reply-link:before, +.comment-reply-login:before { + content: "\f412"; + margin-right: 3px; +} + +/* Comment form */ +.comment-respond { + background-color: #f7f5e7; + padding: 30px 0; +} + +.comment .comment-respond { + margin-bottom: 20px; + padding: 20px; +} + +.comment-reply-title { + font: 300 italic 28px "Source Sans Pro", Helvetica, sans-serif; +} + +.comment-reply-title small a { + color: #131310; + display: inline-block; + float: right; + height: 16px; + overflow: hidden; + width: 16px; +} + +.comment-reply-title small a:hover { + color: #ed331c; + text-decoration: none; +} + +.comment-reply-title small a:before { + content: "\f406"; + vertical-align: top; +} + +.sidebar .comment-list .comment-reply-title, +.sidebar .comment-list .comment-respond .comment-form { + padding: 0; +} + +.comment-form .comment-notes { + margin-bottom: 15px; +} + +.comment-form .comment-form-author, +.comment-form .comment-form-email, +.comment-form .comment-form-url { + margin-bottom: 8px; +} + +.comment-form [for="author"], +.comment-form [for="email"], +.comment-form [for="url"], +.comment-form [for="comment"] { + float: left; + padding: 5px 0; + width: 120px; +} + +.comment-form .required { + color: #ed331c; +} + +.comment-form input[type="text"], +.comment-form input[type="email"], +.comment-form input[type="url"] { + max-width: 270px; + width: 60%; +} + +.comment-form textarea { + width: 100%; +} + +.form-allowed-tags, +.form-allowed-tags code { + color: #686758; + font-size: 12px; +} + +.form-allowed-tags code { + font-size: 10px; + margin-left: 3px; +} + +.comment-list .pingback, +.comment-list .trackback { + padding-top: 24px; +} + +.comment-navigation { + font-size: 20px; + font-style: italic; + font-weight: 300; + margin: 0 auto; + max-width: 604px; + padding: 20px 0 30px; + width: 100%; +} + +.no-comments { + background-color: #f7f5e7; + font-size: 20px; + font-style: italic; + font-weight: 300; + margin: 0; + padding: 40px 0; + text-align: center; +} + +.sidebar .no-comments { + padding-left: 60px; + padding-right: 376px; +} + + +/** + * 5.13 Multisite + * ---------------------------------------------------------------------------- + */ + +.site-main .mu_register { + margin: 0 auto; + max-width: 604px; + width: 100%; +} + +.mu_alert { + margin-top: 25px; +} + +.site-main .mu_register input[type="submit"], +.site-main .mu_register #blog_title, +.site-main .mu_register #user_email, +.site-main .mu_register #blogname, +.site-main .mu_register #user_name { + font-size: inherit; + width: 270px; +} + +.site-main .mu_register input[type="submit"] { + width: auto; +} + + +/** + * 6.0 Sidebar + * ---------------------------------------------------------------------------- + */ + +.site-main .widget-area { + float: right; + width: 300px; +} + + +/** + * 6.1 Widgets + * ---------------------------------------------------------------------------- + */ + +.widget { + background-color: rgba(247, 245, 231, 0.7); + font-size: 14px; + -webkit-hyphens: auto; + -moz-hyphens: auto; + -ms-hyphens: auto; + hyphens: auto; + margin: 0 0 24px; + padding: 20px; + word-wrap: break-word; +} + +.widget .widget-title { + font: 300 italic 20px "Source Sans Pro", Helvetica, sans-serif; + margin: 0 0 10px; +} + +.widget ul, +.widget ol { + list-style-type: none; + margin: 0; + padding: 0; +} + +.widget li { + padding: 5px 0; +} + +.widget .children li:last-child { + padding-bottom: 0; +} + +.widget li > ul, +.widget li > ol { + margin-left: 20px; +} + +.widget a { + color: #bc360a; +} + +.widget a:hover { + color: #ea9629; +} + +/* Search widget */ +.search-form .search-submit { + display: none; +} + +/* RSS Widget */ +.widget_rss .rss-date { + display: block; +} + +.widget_rss .rss-date, +.widget_rss li > cite { + color: #a2a2a2; +} + +/* Calendar Widget */ +.widget_calendar table, +.widget_calendar td { + border: 0; + border-collapse: separate; + border-spacing: 1px; +} + +.widget_calendar caption { + font-size: 14px; + margin: 0; +} + +.widget_calendar th, +.widget_calendar td { + padding: 0; + text-align: center; +} + +.widget_calendar a { + display: block; +} + +.widget_calendar a:hover { + background-color: rgba(0, 0, 0, 0.15); +} + +.widget_calendar tbody td { + background-color: rgba(255, 255, 255, 0.5); +} + +.site-footer .widget_calendar tbody td { + background-color: rgba(255, 255, 255, 0.05); +} + +.widget_calendar tbody .pad, .site-footer .widget_calendar tbody .pad { + background-color: transparent; +} + + +/** + * 7.0 Footer + * ---------------------------------------------------------------------------- + */ + +.site-footer { + background-color: #e8e5ce; + color: #686758; + font-size: 14px; + text-align: center; +} + +.site-footer .widget-area, +.sidebar .site-footer { + text-align: left; +} + +.site-footer a { + color: #686758; +} + +.site-footer .sidebar-container { + background-color: #220e10; + padding: 20px 0; +} + +.site-footer .widget-area { + margin: 0 auto; + max-width: 1040px; + width: 100%; +} + +.sidebar .site-footer .widget-area { + max-width: 724px; + position: relative; + left: -158px; +} + +.site-footer .widget { + background: transparent; + color: #fff; + float: left; + margin-right: 20px; + width: 245px; +} + +.sidebar .site-footer .widget { + width: 228px; +} + +.sidebar .site-footer .widget:nth-of-type(4), +.sidebar .site-footer .widget:nth-of-type(3) { + margin-right: 0; +} + +.site-footer .widget a { + color: #e6402a; +} + +.site-footer .widget-title, +.site-footer .widget-title a, +.site-footer .wp-caption-text { + color: #fff; +} + +.site-info { + margin: 0 auto; + max-width: 1040px; + padding: 30px 0; + width: 100%; +} + +#wpstats { + display: block; + margin: -10px auto 0; +} + + +/** + * 8.0 Media Queries + * ---------------------------------------------------------------------------- + */ + +/* Does the same thing as , + * but in the future W3C standard way. -ms- prefix is required for IE10+ to + * render responsive styling in Windows 8 "snapped" views; IE10+ does not honor + * the meta tag. See https://core.trac.wordpress.org/ticket/25888. + */ +@-ms-viewport { + width: device-width; +} +@viewport { + width: device-width; +} + +@media (max-width: 1599px) { + .site { + border: 0; + } +} + +@media (max-width: 1069px) { + .sidebar img.alignleft, + .sidebar .wp-caption.alignleft { + margin-left: 0; + } + + .sidebar img.alignright, + .sidebar .wp-caption.alignright { + margin-right: 0; + } + + .error404 .page-header { + margin-left: auto; + max-width: 604px; + width: 100%; + } + + .archive-header, + .search .page-header, + .archive .page-header, + .blog .page-header, + .error404 .page-content, + .search .page-content, + .archive .page-content, + .attachment .entry-header, + .attachment .entry-content, + .post-navigation .nav-links, + .sidebar .site-info, + .site-footer .widget-area { + padding-left: 20px; + padding-right: 20px; + } + + .error404 .page-title { + font-size: 24px; + padding: 180px; + } + + .error404 .page-title:before { + font-size: 554px; + } + + .attachment .image-navigation { + max-width: 724px; + } + + .image-navigation .nav-previous, + .image-navigation .nav-next { + position: static; + } + + .site-main .widget-area { + margin-right: 60px; + } +} + +@media (max-width: 999px) { + .sidebar .entry-header, + .sidebar .entry-content, + .sidebar .entry-summary, + .sidebar .entry-meta, + .sidebar .comment-list, + .sidebar .comment-reply-title, + .sidebar .comment-navigation, + .sidebar .comment-respond .comment-form, + .sidebar .featured-gallery, + .sidebar .post-navigation .nav-links, + .author.sidebar .author-info { + max-width: 604px; + padding-left: 0; + padding-right: 0; + } + + .sidebar .site-info, + .search.sidebar .page-content, + .blog.sidebar .page-content, + .attachment .entry-header, + .sidebar .comments-title { + max-width: 604px; + } + + .sidebar .archive-meta, + .attachment .entry-header, + .search.sidebar .page-content, + .blog.sidebar .page-content, + .sidebar .site-info, + .sidebar .comments-title, + .sidebar .no-comments { + padding-left: 0; + padding-right: 0; + } + + .attachment .entry-meta { + float: left; + text-align: left; + width: 100%; + } + + .attachment .entry-content { + max-width: 100%; + padding: 40px 0; + } + + .format-status .entry-content { + padding-top: 40px; + } + + .format-status .entry-meta { + padding-bottom: 40px; + } + + .sidebar .format-status .entry-content, + .sidebar .format-status .entry-meta { + padding-left: 35px; + } + + .sidebar .format-status .entry-content:before, + .sidebar .format-status .entry-meta:before { + left: 10px; + } + + .sidebar .format-status .entry-content p:first-child:before { + left: 4px; + } + + .sidebar .paging-navigation .nav-links { + padding: 0 60px; + } + + .site-main .sidebar-container { + height: auto; + margin: 0 auto; + max-width: 604px; + position: relative; + top: 20px; + } + + .site-main .widget-area { + float: none; + margin: 0; + width: 100%; + } + + .sidebar .site-footer .widget-area { + max-width: 100%; + left: 0; + } +} + +/* Collapse oversized image and pulled images after iPad breakpoint. */ +@media (max-width: 767px) { + .site-header .home-link { + min-height: 0; + } + .site-title { + font-size: 36px; + padding: 8px 0 10px; + } + .entry-content img.alignleft, + .entry-content .wp-caption.alignleft { + margin-left: 0; + } + + .entry-content img.alignright, + .entry-content .wp-caption.alignright { + margin-right: 0; + } + + .attachment .image-navigation, + .attachment .entry-attachment .attachment { + max-width: 604px; + padding: 0; + width: 100%; + } + + .gallery-caption { + display: none; + } +} + +@media (max-width: 643px) { + .site-title { + font-size: 30px; + } + + #content .entry-header, + #content .entry-content, + #content .entry-summary, + #content footer.entry-meta, + #content .featured-gallery, + .search.sidebar .page-content, + .blog.sidebar .page-content, + .sidebar .post-navigation .nav-links, + .paging-navigation .nav-links, + #content .author-info, + .comments-area .comments-title, + .comments-area .comment-list, + .comments-area .comment-navigation, + .comment-respond, + .sidebar .site-info, + .sidebar .paging-navigation .nav-links { + padding-left: 20px; + padding-right: 20px; + } + + #content .format-status .entry-content, + #content .format-status .entry-met { + padding-left: 35px; + } + + /* Small menu */ + .menu-toggle { + cursor: pointer; + display: inline-block; + font: bold 16px/1.3 "Source Sans Pro", Helvetica, sans-serif; + margin: 0; + } + + .menu-toggle, + .menu-toggle:hover, + .menu-toggle:focus, + .menu-toggle:active { + background: none; + border: none; + color: #141412; + padding: 12px 0 12px 20px; + } + + .menu-toggle:focus { + outline: thin dotted; + } + + .menu-toggle:after { + content: "\f502"; + font-size: 12px; + padding-left: 8px; + vertical-align: -4px; + } + + .toggled-on .menu-toggle:after { + content: "\f500"; + vertical-align: 2px; + } + + .toggled-on .nav-menu, + .toggled-on .nav-menu > ul { + display: block; + margin-left: 0; + padding: 0; + width: 100%; + } + + .toggled-on li, + .toggled-on .children { + display: block; + } + + .toggled-on .nav-menu li > ul { + background-color: transparent; + display: block; + float: none; + margin-left: 20px; + position: relative; + left: auto; + top: auto; + } + + .toggled-on .nav-menu li > ul a { + color: #141412; + width: auto; + } + + .toggled-on .nav-menu li:hover > a, + .toggled-on .nav-menu .children a { + background-color: transparent; + color: #141412; + } + + .toggled-on .nav-menu > li a:hover, + .toggled-on .nav-menu > ul a:hover { + background-color: #db572f; + color: #fff; + } + + .toggled-on .nav-menu > li a:focus, + .toggled-on .nav-menu > ul a:focus { + background-color: #220e10; + color: #fff; + } + + ul.nav-menu, + div.nav-menu > ul { + display: none; + } + + #content .featured-gallery { + padding-left: 24px; + } + + .gallery-columns-1 .gallery-item { + margin-right: 0; + width: 100%; + } + + .entry-title, + .format-chat .entry-title, + .format-image .entry-title, + .format-gallery .entry-title, + .format-video .entry-title { + font-size: 22px; + font-weight: bold; + } + + .format-quote blockquote, + .format-status .entry-content { + font-size: 18px; + } + + .format-quote blockquote small, + .format-quote blockquote cite { + font-size: 13px; + } + + .error404 .page-title { + padding: 40px 0 0; + } + + .error404 .page-title:before { + content: normal; + } + + .comment-author { + margin-right: 30px; + } + + .comment-author .avatar { + height: auto; + max-width: 100%; + } + + .comment-metadata, + .comment-content, + .comment-list .reply { + width: 70%; + width: -webkit-calc(100% - 104px); + width: calc(100% - 104px); + } + + .comment-form input[type="text"], + .comment-form input[type="email"], + .comment-form input[type="url"] { + width: -webkit-calc(100% - 120px); + width: calc(100% - 120px); + } + + .comment-form textarea { + height: 80px; /* Smaller field for mobile. */ + } + + /* Audio */ + .format-audio .entry-content:before { + display: none; + } + + .format-audio .audio-content { + background-image: none; + float: none; + padding-left: 0; + width: auto; + } +} + +/* Mobile devices */ +@media (max-width: 359px) { + .site-title { + font-weight: normal; + } + .site-description { + clip: rect(1px, 1px, 1px, 1px); + position: absolute; + } + .gallery { + margin-left: 0; + } + + .gallery .gallery-item, + .gallery-columns-2.gallery-size-thumbnail .gallery-item { + max-width: none; + width: 49%; + width: -webkit-calc(50% - 4px); + width: calc(50% - 4px); + } + + .gallery-columns-1.gallery-size-medium, + .gallery-columns-1.gallery-size-thumbnail, + .gallery-columns-2.gallery-size-thumbnail, + .gallery-columns-3.gallery-size-thumbnail { + display: block; + } + + .gallery-columns-1 .gallery-item, + .gallery-columns-1.gallery-size-medium .gallery-item, + .gallery-columns-1.gallery-size-thumbnail .gallery-item { + text-align: center; + width: 98%; + width: -webkit-calc(100% - 4px); + width: calc(100% - 4px); + } + + .gallery-columns-3 .gallery-item:nth-of-type(3n), + .gallery-columns-5 .gallery-item:nth-of-type(5n), + .gallery-columns-7 .gallery-item:nth-of-type(7n), + .gallery-columns-9 .gallery-item:nth-of-type(9n) { + margin-right: 4px; + } + + .gallery br { + display: none; + } + + .gallery .gallery-item:nth-of-type(even) { + margin-right: 0; + } + + /* Comments */ + .comment-author { + margin: 0 0 5px; + max-width: 100%; + } + + .comment-author .avatar { + display: inline; + margin: 0 5px 0 0; + max-width: 20px; + } + + .comment-metadata, + .comment-content, + .comment-list .reply { + width: 100%; + } +} + + +/** + * 9.0 Print + * ---------------------------------------------------------------------------- + */ + +/* Retina-specific styles. */ +@media print, + (-o-min-device-pixel-ratio: 5/4), + (-webkit-min-device-pixel-ratio: 1.25), + (min-resolution: 120dpi) { + + .site-header .search-field { + background-image: url(images/search-icon-2x.png); + } + + .format-audio .audio-content, + .format-status .entry-content:before, + .format-status .entry-meta:before, + .comment-list > li:after, + .comment-list .children > li:before { + background-image: url(images/dotted-line-2x.png); + } +} + +@media print { + body { + background: none !important; + color: #000; + font-size: 10pt; + } + + footer a[rel="bookmark"]:link:after, + footer a[rel="bookmark"]:visited:after { + content: " [" attr(href) "] "; /* Show URLs */ + } + + .site { + max-width: 98%; + } + + .site-header { + background-image: none !important; + } + + .site-header .home-link { + max-width: none; + min-height: 0; + } + + .site-title { + color: #000; + font-size: 21pt; + } + + .site-description { + font-size: 10pt; + } + + .author-avatar, + .site-footer, + .comment-respond, + .comments-area .comment-edit-link, + .comments-area .reply, + .comments-link, + .entry-meta .edit-link, + .page-links, + .site-content nav, + .widget-area, + .main-navigation, + .navbar, + .more-link { + display: none; + } + + .entry-header, + .entry-content, + .entry-summary, + .entry-meta { + margin: 0; + width: 100%; + } + + .page-title, + .entry-title { + font-size: 21pt; + } + + .entry-meta, + .entry-meta a { + color: #444; + font-size: 10pt; + } + + .entry-content img.alignleft, + .entry-content .wp-caption.alignleft { + margin-left: 0; + } + + .entry-content img.alignright, + .entry-content .wp-caption.alignright { + margin-right: 0; + } + + .format-image .entry-content .size-full { + margin: 0; + } + + /* Remove colors from post formats */ + .hentry { + background-color: #fff; + } + + /* Comments */ + .comments-area > li.comment { + background: none; + position: relative; + width: auto; + } + + .comment-metadata { + float: none; + } + + .comment-author .fn, + .comment-reply-link, + .comment-reply-login { + color: #333; + } +} diff --git a/wp-content/themes/twentythirteen/tag.php b/wp-content/themes/twentythirteen/tag.php new file mode 100644 index 0000000..2929321 --- /dev/null +++ b/wp-content/themes/twentythirteen/tag.php @@ -0,0 +1,43 @@ + + +
        +
        + + +
        +

        + + +
        + +
        + + + + + + + + + + + + +
        +
        + + + \ No newline at end of file diff --git a/wp-content/themes/twentythirteen/taxonomy-post_format.php b/wp-content/themes/twentythirteen/taxonomy-post_format.php new file mode 100644 index 0000000..bbec046 --- /dev/null +++ b/wp-content/themes/twentythirteen/taxonomy-post_format.php @@ -0,0 +1,41 @@ + + +
        +
        + + +
        +

        ' . get_post_format_string( get_post_format() ) . '' ); ?>

        +
        + + + + + + + + + + + + +
        +
        + + + \ No newline at end of file diff --git a/wp-cron.php b/wp-cron.php new file mode 100644 index 0000000..2b965dd --- /dev/null +++ b/wp-cron.php @@ -0,0 +1,115 @@ +get_row( $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1", '_transient_doing_cron' ) ); + if ( is_object( $row ) ) + $value = $row->option_value; + } + + return $value; +} + +if ( false === $crons = _get_cron_array() ) + die(); + +$keys = array_keys( $crons ); +$gmt_time = microtime( true ); + +if ( isset($keys[0]) && $keys[0] > $gmt_time ) + die(); + +$doing_cron_transient = get_transient( 'doing_cron'); + +// Use global $doing_wp_cron lock otherwise use the GET lock. If no lock, trying grabbing a new lock. +if ( empty( $doing_wp_cron ) ) { + if ( empty( $_GET[ 'doing_wp_cron' ] ) ) { + // Called from external script/job. Try setting a lock. + if ( $doing_cron_transient && ( $doing_cron_transient + WP_CRON_LOCK_TIMEOUT > $gmt_time ) ) + return; + $doing_cron_transient = $doing_wp_cron = sprintf( '%.22F', microtime( true ) ); + set_transient( 'doing_cron', $doing_wp_cron ); + } else { + $doing_wp_cron = $_GET[ 'doing_wp_cron' ]; + } +} + +// Check lock +if ( $doing_cron_transient != $doing_wp_cron ) + return; + +foreach ( $crons as $timestamp => $cronhooks ) { + if ( $timestamp > $gmt_time ) + break; + + foreach ( $cronhooks as $hook => $keys ) { + + foreach ( $keys as $k => $v ) { + + $schedule = $v['schedule']; + + if ( $schedule != false ) { + $new_args = array($timestamp, $schedule, $hook, $v['args']); + call_user_func_array('wp_reschedule_event', $new_args); + } + + wp_unschedule_event( $timestamp, $hook, $v['args'] ); + + /** + * Fires scheduled events. + * + * @internal + * @since 2.1.0 + * + * @param string $hook Name of the hook that was scheduled to be fired. + * @param array $args The arguments to be passed to the hook. + */ + do_action_ref_array( $hook, $v['args'] ); + + // If the hook ran too long and another cron process stole the lock, quit. + if ( _get_cron_lock() != $doing_wp_cron ) + return; + } + } +} + +if ( _get_cron_lock() == $doing_wp_cron ) + delete_transient( 'doing_cron' ); + +die(); diff --git a/wp-includes/ID3/getid3.lib.php b/wp-includes/ID3/getid3.lib.php new file mode 100644 index 0000000..0c92e06 --- /dev/null +++ b/wp-includes/ID3/getid3.lib.php @@ -0,0 +1,1376 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +// also https://github.com/JamesHeinrich/getID3 // +///////////////////////////////////////////////////////////////// +// // +// getid3.lib.php - part of getID3() // +// See readme.txt for more details // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_lib +{ + + public static function PrintHexBytes($string, $hex=true, $spaces=true, $htmlencoding='UTF-8') { + $returnstring = ''; + for ($i = 0; $i < strlen($string); $i++) { + if ($hex) { + $returnstring .= str_pad(dechex(ord($string{$i})), 2, '0', STR_PAD_LEFT); + } else { + $returnstring .= ' '.(preg_match("#[\x20-\x7E]#", $string{$i}) ? $string{$i} : '¤'); + } + if ($spaces) { + $returnstring .= ' '; + } + } + if (!empty($htmlencoding)) { + if ($htmlencoding === true) { + $htmlencoding = 'UTF-8'; // prior to getID3 v1.9.0 the function's 4th parameter was boolean + } + $returnstring = htmlentities($returnstring, ENT_QUOTES, $htmlencoding); + } + return $returnstring; + } + + public static function trunc($floatnumber) { + // truncates a floating-point number at the decimal point + // returns int (if possible, otherwise float) + if ($floatnumber >= 1) { + $truncatednumber = floor($floatnumber); + } elseif ($floatnumber <= -1) { + $truncatednumber = ceil($floatnumber); + } else { + $truncatednumber = 0; + } + if (self::intValueSupported($truncatednumber)) { + $truncatednumber = (int) $truncatednumber; + } + return $truncatednumber; + } + + + public static function safe_inc(&$variable, $increment=1) { + if (isset($variable)) { + $variable += $increment; + } else { + $variable = $increment; + } + return true; + } + + public static function CastAsInt($floatnum) { + // convert to float if not already + $floatnum = (float) $floatnum; + + // convert a float to type int, only if possible + if (self::trunc($floatnum) == $floatnum) { + // it's not floating point + if (self::intValueSupported($floatnum)) { + // it's within int range + $floatnum = (int) $floatnum; + } + } + return $floatnum; + } + + public static function intValueSupported($num) { + // check if integers are 64-bit + static $hasINT64 = null; + if ($hasINT64 === null) { // 10x faster than is_null() + $hasINT64 = is_int(pow(2, 31)); // 32-bit int are limited to (2^31)-1 + if (!$hasINT64 && !defined('PHP_INT_MIN')) { + define('PHP_INT_MIN', ~PHP_INT_MAX); + } + } + // if integers are 64-bit - no other check required + if ($hasINT64 || (($num <= PHP_INT_MAX) && ($num >= PHP_INT_MIN))) { + return true; + } + return false; + } + + public static function DecimalizeFraction($fraction) { + list($numerator, $denominator) = explode('/', $fraction); + return $numerator / ($denominator ? $denominator : 1); + } + + + public static function DecimalBinary2Float($binarynumerator) { + $numerator = self::Bin2Dec($binarynumerator); + $denominator = self::Bin2Dec('1'.str_repeat('0', strlen($binarynumerator))); + return ($numerator / $denominator); + } + + + public static function NormalizeBinaryPoint($binarypointnumber, $maxbits=52) { + // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html + if (strpos($binarypointnumber, '.') === false) { + $binarypointnumber = '0.'.$binarypointnumber; + } elseif ($binarypointnumber{0} == '.') { + $binarypointnumber = '0'.$binarypointnumber; + } + $exponent = 0; + while (($binarypointnumber{0} != '1') || (substr($binarypointnumber, 1, 1) != '.')) { + if (substr($binarypointnumber, 1, 1) == '.') { + $exponent--; + $binarypointnumber = substr($binarypointnumber, 2, 1).'.'.substr($binarypointnumber, 3); + } else { + $pointpos = strpos($binarypointnumber, '.'); + $exponent += ($pointpos - 1); + $binarypointnumber = str_replace('.', '', $binarypointnumber); + $binarypointnumber = $binarypointnumber{0}.'.'.substr($binarypointnumber, 1); + } + } + $binarypointnumber = str_pad(substr($binarypointnumber, 0, $maxbits + 2), $maxbits + 2, '0', STR_PAD_RIGHT); + return array('normalized'=>$binarypointnumber, 'exponent'=>(int) $exponent); + } + + + public static function Float2BinaryDecimal($floatvalue) { + // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html + $maxbits = 128; // to how many bits of precision should the calculations be taken? + $intpart = self::trunc($floatvalue); + $floatpart = abs($floatvalue - $intpart); + $pointbitstring = ''; + while (($floatpart != 0) && (strlen($pointbitstring) < $maxbits)) { + $floatpart *= 2; + $pointbitstring .= (string) self::trunc($floatpart); + $floatpart -= self::trunc($floatpart); + } + $binarypointnumber = decbin($intpart).'.'.$pointbitstring; + return $binarypointnumber; + } + + + public static function Float2String($floatvalue, $bits) { + // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee-expl.html + switch ($bits) { + case 32: + $exponentbits = 8; + $fractionbits = 23; + break; + + case 64: + $exponentbits = 11; + $fractionbits = 52; + break; + + default: + return false; + break; + } + if ($floatvalue >= 0) { + $signbit = '0'; + } else { + $signbit = '1'; + } + $normalizedbinary = self::NormalizeBinaryPoint(self::Float2BinaryDecimal($floatvalue), $fractionbits); + $biasedexponent = pow(2, $exponentbits - 1) - 1 + $normalizedbinary['exponent']; // (127 or 1023) +/- exponent + $exponentbitstring = str_pad(decbin($biasedexponent), $exponentbits, '0', STR_PAD_LEFT); + $fractionbitstring = str_pad(substr($normalizedbinary['normalized'], 2), $fractionbits, '0', STR_PAD_RIGHT); + + return self::BigEndian2String(self::Bin2Dec($signbit.$exponentbitstring.$fractionbitstring), $bits % 8, false); + } + + + public static function LittleEndian2Float($byteword) { + return self::BigEndian2Float(strrev($byteword)); + } + + + public static function BigEndian2Float($byteword) { + // ANSI/IEEE Standard 754-1985, Standard for Binary Floating Point Arithmetic + // http://www.psc.edu/general/software/packages/ieee/ieee.html + // http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee.html + + $bitword = self::BigEndian2Bin($byteword); + if (!$bitword) { + return 0; + } + $signbit = $bitword{0}; + + switch (strlen($byteword) * 8) { + case 32: + $exponentbits = 8; + $fractionbits = 23; + break; + + case 64: + $exponentbits = 11; + $fractionbits = 52; + break; + + case 80: + // 80-bit Apple SANE format + // http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/ + $exponentstring = substr($bitword, 1, 15); + $isnormalized = intval($bitword{16}); + $fractionstring = substr($bitword, 17, 63); + $exponent = pow(2, self::Bin2Dec($exponentstring) - 16383); + $fraction = $isnormalized + self::DecimalBinary2Float($fractionstring); + $floatvalue = $exponent * $fraction; + if ($signbit == '1') { + $floatvalue *= -1; + } + return $floatvalue; + break; + + default: + return false; + break; + } + $exponentstring = substr($bitword, 1, $exponentbits); + $fractionstring = substr($bitword, $exponentbits + 1, $fractionbits); + $exponent = self::Bin2Dec($exponentstring); + $fraction = self::Bin2Dec($fractionstring); + + if (($exponent == (pow(2, $exponentbits) - 1)) && ($fraction != 0)) { + // Not a Number + $floatvalue = false; + } elseif (($exponent == (pow(2, $exponentbits) - 1)) && ($fraction == 0)) { + if ($signbit == '1') { + $floatvalue = '-infinity'; + } else { + $floatvalue = '+infinity'; + } + } elseif (($exponent == 0) && ($fraction == 0)) { + if ($signbit == '1') { + $floatvalue = -0; + } else { + $floatvalue = 0; + } + $floatvalue = ($signbit ? 0 : -0); + } elseif (($exponent == 0) && ($fraction != 0)) { + // These are 'unnormalized' values + $floatvalue = pow(2, (-1 * (pow(2, $exponentbits - 1) - 2))) * self::DecimalBinary2Float($fractionstring); + if ($signbit == '1') { + $floatvalue *= -1; + } + } elseif ($exponent != 0) { + $floatvalue = pow(2, ($exponent - (pow(2, $exponentbits - 1) - 1))) * (1 + self::DecimalBinary2Float($fractionstring)); + if ($signbit == '1') { + $floatvalue *= -1; + } + } + return (float) $floatvalue; + } + + + public static function BigEndian2Int($byteword, $synchsafe=false, $signed=false) { + $intvalue = 0; + $bytewordlen = strlen($byteword); + if ($bytewordlen == 0) { + return false; + } + for ($i = 0; $i < $bytewordlen; $i++) { + if ($synchsafe) { // disregard MSB, effectively 7-bit bytes + //$intvalue = $intvalue | (ord($byteword{$i}) & 0x7F) << (($bytewordlen - 1 - $i) * 7); // faster, but runs into problems past 2^31 on 32-bit systems + $intvalue += (ord($byteword{$i}) & 0x7F) * pow(2, ($bytewordlen - 1 - $i) * 7); + } else { + $intvalue += ord($byteword{$i}) * pow(256, ($bytewordlen - 1 - $i)); + } + } + if ($signed && !$synchsafe) { + // synchsafe ints are not allowed to be signed + if ($bytewordlen <= PHP_INT_SIZE) { + $signMaskBit = 0x80 << (8 * ($bytewordlen - 1)); + if ($intvalue & $signMaskBit) { + $intvalue = 0 - ($intvalue & ($signMaskBit - 1)); + } + } else { + throw new Exception('ERROR: Cannot have signed integers larger than '.(8 * PHP_INT_SIZE).'-bits ('.strlen($byteword).') in self::BigEndian2Int()'); + } + } + return self::CastAsInt($intvalue); + } + + + public static function LittleEndian2Int($byteword, $signed=false) { + return self::BigEndian2Int(strrev($byteword), false, $signed); + } + + + public static function BigEndian2Bin($byteword) { + $binvalue = ''; + $bytewordlen = strlen($byteword); + for ($i = 0; $i < $bytewordlen; $i++) { + $binvalue .= str_pad(decbin(ord($byteword{$i})), 8, '0', STR_PAD_LEFT); + } + return $binvalue; + } + + + public static function BigEndian2String($number, $minbytes=1, $synchsafe=false, $signed=false) { + if ($number < 0) { + throw new Exception('ERROR: self::BigEndian2String() does not support negative numbers'); + } + $maskbyte = (($synchsafe || $signed) ? 0x7F : 0xFF); + $intstring = ''; + if ($signed) { + if ($minbytes > PHP_INT_SIZE) { + throw new Exception('ERROR: Cannot have signed integers larger than '.(8 * PHP_INT_SIZE).'-bits in self::BigEndian2String()'); + } + $number = $number & (0x80 << (8 * ($minbytes - 1))); + } + while ($number != 0) { + $quotient = ($number / ($maskbyte + 1)); + $intstring = chr(ceil(($quotient - floor($quotient)) * $maskbyte)).$intstring; + $number = floor($quotient); + } + return str_pad($intstring, $minbytes, "\x00", STR_PAD_LEFT); + } + + + public static function Dec2Bin($number) { + while ($number >= 256) { + $bytes[] = (($number / 256) - (floor($number / 256))) * 256; + $number = floor($number / 256); + } + $bytes[] = $number; + $binstring = ''; + for ($i = 0; $i < count($bytes); $i++) { + $binstring = (($i == count($bytes) - 1) ? decbin($bytes[$i]) : str_pad(decbin($bytes[$i]), 8, '0', STR_PAD_LEFT)).$binstring; + } + return $binstring; + } + + + public static function Bin2Dec($binstring, $signed=false) { + $signmult = 1; + if ($signed) { + if ($binstring{0} == '1') { + $signmult = -1; + } + $binstring = substr($binstring, 1); + } + $decvalue = 0; + for ($i = 0; $i < strlen($binstring); $i++) { + $decvalue += ((int) substr($binstring, strlen($binstring) - $i - 1, 1)) * pow(2, $i); + } + return self::CastAsInt($decvalue * $signmult); + } + + + public static function Bin2String($binstring) { + // return 'hi' for input of '0110100001101001' + $string = ''; + $binstringreversed = strrev($binstring); + for ($i = 0; $i < strlen($binstringreversed); $i += 8) { + $string = chr(self::Bin2Dec(strrev(substr($binstringreversed, $i, 8)))).$string; + } + return $string; + } + + + public static function LittleEndian2String($number, $minbytes=1, $synchsafe=false) { + $intstring = ''; + while ($number > 0) { + if ($synchsafe) { + $intstring = $intstring.chr($number & 127); + $number >>= 7; + } else { + $intstring = $intstring.chr($number & 255); + $number >>= 8; + } + } + return str_pad($intstring, $minbytes, "\x00", STR_PAD_RIGHT); + } + + + public static function array_merge_clobber($array1, $array2) { + // written by kcØhireability*com + // taken from http://www.php.net/manual/en/function.array-merge-recursive.php + if (!is_array($array1) || !is_array($array2)) { + return false; + } + $newarray = $array1; + foreach ($array2 as $key => $val) { + if (is_array($val) && isset($newarray[$key]) && is_array($newarray[$key])) { + $newarray[$key] = self::array_merge_clobber($newarray[$key], $val); + } else { + $newarray[$key] = $val; + } + } + return $newarray; + } + + + public static function array_merge_noclobber($array1, $array2) { + if (!is_array($array1) || !is_array($array2)) { + return false; + } + $newarray = $array1; + foreach ($array2 as $key => $val) { + if (is_array($val) && isset($newarray[$key]) && is_array($newarray[$key])) { + $newarray[$key] = self::array_merge_noclobber($newarray[$key], $val); + } elseif (!isset($newarray[$key])) { + $newarray[$key] = $val; + } + } + return $newarray; + } + + + public static function ksort_recursive(&$theArray) { + ksort($theArray); + foreach ($theArray as $key => $value) { + if (is_array($value)) { + self::ksort_recursive($theArray[$key]); + } + } + return true; + } + + public static function fileextension($filename, $numextensions=1) { + if (strstr($filename, '.')) { + $reversedfilename = strrev($filename); + $offset = 0; + for ($i = 0; $i < $numextensions; $i++) { + $offset = strpos($reversedfilename, '.', $offset + 1); + if ($offset === false) { + return ''; + } + } + return strrev(substr($reversedfilename, 0, $offset)); + } + return ''; + } + + + public static function PlaytimeString($seconds) { + $sign = (($seconds < 0) ? '-' : ''); + $seconds = round(abs($seconds)); + $H = (int) floor( $seconds / 3600); + $M = (int) floor(($seconds - (3600 * $H) ) / 60); + $S = (int) round( $seconds - (3600 * $H) - (60 * $M) ); + return $sign.($H ? $H.':' : '').($H ? str_pad($M, 2, '0', STR_PAD_LEFT) : intval($M)).':'.str_pad($S, 2, 0, STR_PAD_LEFT); + } + + + public static function DateMac2Unix($macdate) { + // Macintosh timestamp: seconds since 00:00h January 1, 1904 + // UNIX timestamp: seconds since 00:00h January 1, 1970 + return self::CastAsInt($macdate - 2082844800); + } + + + public static function FixedPoint8_8($rawdata) { + return self::BigEndian2Int(substr($rawdata, 0, 1)) + (float) (self::BigEndian2Int(substr($rawdata, 1, 1)) / pow(2, 8)); + } + + + public static function FixedPoint16_16($rawdata) { + return self::BigEndian2Int(substr($rawdata, 0, 2)) + (float) (self::BigEndian2Int(substr($rawdata, 2, 2)) / pow(2, 16)); + } + + + public static function FixedPoint2_30($rawdata) { + $binarystring = self::BigEndian2Bin($rawdata); + return self::Bin2Dec(substr($binarystring, 0, 2)) + (float) (self::Bin2Dec(substr($binarystring, 2, 30)) / pow(2, 30)); + } + + + public static function CreateDeepArray($ArrayPath, $Separator, $Value) { + // assigns $Value to a nested array path: + // $foo = self::CreateDeepArray('/path/to/my', '/', 'file.txt') + // is the same as: + // $foo = array('path'=>array('to'=>'array('my'=>array('file.txt')))); + // or + // $foo['path']['to']['my'] = 'file.txt'; + $ArrayPath = ltrim($ArrayPath, $Separator); + if (($pos = strpos($ArrayPath, $Separator)) !== false) { + $ReturnedArray[substr($ArrayPath, 0, $pos)] = self::CreateDeepArray(substr($ArrayPath, $pos + 1), $Separator, $Value); + } else { + $ReturnedArray[$ArrayPath] = $Value; + } + return $ReturnedArray; + } + + public static function array_max($arraydata, $returnkey=false) { + $maxvalue = false; + $maxkey = false; + foreach ($arraydata as $key => $value) { + if (!is_array($value)) { + if ($value > $maxvalue) { + $maxvalue = $value; + $maxkey = $key; + } + } + } + return ($returnkey ? $maxkey : $maxvalue); + } + + public static function array_min($arraydata, $returnkey=false) { + $minvalue = false; + $minkey = false; + foreach ($arraydata as $key => $value) { + if (!is_array($value)) { + if ($value > $minvalue) { + $minvalue = $value; + $minkey = $key; + } + } + } + return ($returnkey ? $minkey : $minvalue); + } + + public static function XML2array($XMLstring) { + if ( function_exists( 'simplexml_load_string' ) && function_exists( 'libxml_disable_entity_loader' ) ) { + $loader = libxml_disable_entity_loader( true ); + $XMLobject = simplexml_load_string( $XMLstring, 'SimpleXMLElement', LIBXML_NOENT ); + $return = self::SimpleXMLelement2array( $XMLobject ); + libxml_disable_entity_loader( $loader ); + return $return; + } + return false; + } + + public static function SimpleXMLelement2array($XMLobject) { + if (!is_object($XMLobject) && !is_array($XMLobject)) { + return $XMLobject; + } + $XMLarray = (is_object($XMLobject) ? get_object_vars($XMLobject) : $XMLobject); + foreach ($XMLarray as $key => $value) { + $XMLarray[$key] = self::SimpleXMLelement2array($value); + } + return $XMLarray; + } + + + // Allan Hansen + // self::md5_data() - returns md5sum for a file from startuing position to absolute end position + public static function hash_data($file, $offset, $end, $algorithm) { + static $tempdir = ''; + if (!self::intValueSupported($end)) { + return false; + } + switch ($algorithm) { + case 'md5': + $hash_function = 'md5_file'; + $unix_call = 'md5sum'; + $windows_call = 'md5sum.exe'; + $hash_length = 32; + break; + + case 'sha1': + $hash_function = 'sha1_file'; + $unix_call = 'sha1sum'; + $windows_call = 'sha1sum.exe'; + $hash_length = 40; + break; + + default: + throw new Exception('Invalid algorithm ('.$algorithm.') in self::hash_data()'); + break; + } + $size = $end - $offset; + while (true) { + if (GETID3_OS_ISWINDOWS) { + + // It seems that sha1sum.exe for Windows only works on physical files, does not accept piped data + // Fall back to create-temp-file method: + if ($algorithm == 'sha1') { + break; + } + + $RequiredFiles = array('cygwin1.dll', 'head.exe', 'tail.exe', $windows_call); + foreach ($RequiredFiles as $required_file) { + if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) { + // helper apps not available - fall back to old method + break 2; + } + } + $commandline = GETID3_HELPERAPPSDIR.'head.exe -c '.$end.' '.escapeshellarg(str_replace('/', DIRECTORY_SEPARATOR, $file)).' | '; + $commandline .= GETID3_HELPERAPPSDIR.'tail.exe -c '.$size.' | '; + $commandline .= GETID3_HELPERAPPSDIR.$windows_call; + + } else { + + $commandline = 'head -c'.$end.' '.escapeshellarg($file).' | '; + $commandline .= 'tail -c'.$size.' | '; + $commandline .= $unix_call; + + } + if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) { + //throw new Exception('PHP running in Safe Mode - backtick operator not available, using slower non-system-call '.$algorithm.' algorithm'); + break; + } + return substr(`$commandline`, 0, $hash_length); + } + + if (empty($tempdir)) { + // yes this is ugly, feel free to suggest a better way + require_once(dirname(__FILE__).'/getid3.php'); + $getid3_temp = new getID3(); + $tempdir = $getid3_temp->tempdir; + unset($getid3_temp); + } + // try to create a temporary file in the system temp directory - invalid dirname should force to system temp dir + if (($data_filename = tempnam($tempdir, 'gI3')) === false) { + // can't find anywhere to create a temp file, just fail + return false; + } + + // Init + $result = false; + + // copy parts of file + try { + self::CopyFileParts($file, $data_filename, $offset, $end - $offset); + $result = $hash_function($data_filename); + } catch (Exception $e) { + throw new Exception('self::CopyFileParts() failed in getid_lib::hash_data(): '.$e->getMessage()); + } + unlink($data_filename); + return $result; + } + + public static function CopyFileParts($filename_source, $filename_dest, $offset, $length) { + if (!self::intValueSupported($offset + $length)) { + throw new Exception('cannot copy file portion, it extends beyond the '.round(PHP_INT_MAX / 1073741824).'GB limit'); + } + if (is_readable($filename_source) && is_file($filename_source) && ($fp_src = fopen($filename_source, 'rb'))) { + if (($fp_dest = fopen($filename_dest, 'wb'))) { + if (fseek($fp_src, $offset) == 0) { + $byteslefttowrite = $length; + while (($byteslefttowrite > 0) && ($buffer = fread($fp_src, min($byteslefttowrite, getID3::FREAD_BUFFER_SIZE)))) { + $byteswritten = fwrite($fp_dest, $buffer, $byteslefttowrite); + $byteslefttowrite -= $byteswritten; + } + return true; + } else { + throw new Exception('failed to seek to offset '.$offset.' in '.$filename_source); + } + fclose($fp_dest); + } else { + throw new Exception('failed to create file for writing '.$filename_dest); + } + fclose($fp_src); + } else { + throw new Exception('failed to open file for reading '.$filename_source); + } + return false; + } + + public static function iconv_fallback_int_utf8($charval) { + if ($charval < 128) { + // 0bbbbbbb + $newcharstring = chr($charval); + } elseif ($charval < 2048) { + // 110bbbbb 10bbbbbb + $newcharstring = chr(($charval >> 6) | 0xC0); + $newcharstring .= chr(($charval & 0x3F) | 0x80); + } elseif ($charval < 65536) { + // 1110bbbb 10bbbbbb 10bbbbbb + $newcharstring = chr(($charval >> 12) | 0xE0); + $newcharstring .= chr(($charval >> 6) | 0xC0); + $newcharstring .= chr(($charval & 0x3F) | 0x80); + } else { + // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb + $newcharstring = chr(($charval >> 18) | 0xF0); + $newcharstring .= chr(($charval >> 12) | 0xC0); + $newcharstring .= chr(($charval >> 6) | 0xC0); + $newcharstring .= chr(($charval & 0x3F) | 0x80); + } + return $newcharstring; + } + + // ISO-8859-1 => UTF-8 + public static function iconv_fallback_iso88591_utf8($string, $bom=false) { + if (function_exists('utf8_encode')) { + return utf8_encode($string); + } + // utf8_encode() unavailable, use getID3()'s iconv_fallback() conversions (possibly PHP is compiled without XML support) + $newcharstring = ''; + if ($bom) { + $newcharstring .= "\xEF\xBB\xBF"; + } + for ($i = 0; $i < strlen($string); $i++) { + $charval = ord($string{$i}); + $newcharstring .= self::iconv_fallback_int_utf8($charval); + } + return $newcharstring; + } + + // ISO-8859-1 => UTF-16BE + public static function iconv_fallback_iso88591_utf16be($string, $bom=false) { + $newcharstring = ''; + if ($bom) { + $newcharstring .= "\xFE\xFF"; + } + for ($i = 0; $i < strlen($string); $i++) { + $newcharstring .= "\x00".$string{$i}; + } + return $newcharstring; + } + + // ISO-8859-1 => UTF-16LE + public static function iconv_fallback_iso88591_utf16le($string, $bom=false) { + $newcharstring = ''; + if ($bom) { + $newcharstring .= "\xFF\xFE"; + } + for ($i = 0; $i < strlen($string); $i++) { + $newcharstring .= $string{$i}."\x00"; + } + return $newcharstring; + } + + // ISO-8859-1 => UTF-16LE (BOM) + public static function iconv_fallback_iso88591_utf16($string) { + return self::iconv_fallback_iso88591_utf16le($string, true); + } + + // UTF-8 => ISO-8859-1 + public static function iconv_fallback_utf8_iso88591($string) { + if (function_exists('utf8_decode')) { + return utf8_decode($string); + } + // utf8_decode() unavailable, use getID3()'s iconv_fallback() conversions (possibly PHP is compiled without XML support) + $newcharstring = ''; + $offset = 0; + $stringlength = strlen($string); + while ($offset < $stringlength) { + if ((ord($string{$offset}) | 0x07) == 0xF7) { + // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb + $charval = ((ord($string{($offset + 0)}) & 0x07) << 18) & + ((ord($string{($offset + 1)}) & 0x3F) << 12) & + ((ord($string{($offset + 2)}) & 0x3F) << 6) & + (ord($string{($offset + 3)}) & 0x3F); + $offset += 4; + } elseif ((ord($string{$offset}) | 0x0F) == 0xEF) { + // 1110bbbb 10bbbbbb 10bbbbbb + $charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) & + ((ord($string{($offset + 1)}) & 0x3F) << 6) & + (ord($string{($offset + 2)}) & 0x3F); + $offset += 3; + } elseif ((ord($string{$offset}) | 0x1F) == 0xDF) { + // 110bbbbb 10bbbbbb + $charval = ((ord($string{($offset + 0)}) & 0x1F) << 6) & + (ord($string{($offset + 1)}) & 0x3F); + $offset += 2; + } elseif ((ord($string{$offset}) | 0x7F) == 0x7F) { + // 0bbbbbbb + $charval = ord($string{$offset}); + $offset += 1; + } else { + // error? throw some kind of warning here? + $charval = false; + $offset += 1; + } + if ($charval !== false) { + $newcharstring .= (($charval < 256) ? chr($charval) : '?'); + } + } + return $newcharstring; + } + + // UTF-8 => UTF-16BE + public static function iconv_fallback_utf8_utf16be($string, $bom=false) { + $newcharstring = ''; + if ($bom) { + $newcharstring .= "\xFE\xFF"; + } + $offset = 0; + $stringlength = strlen($string); + while ($offset < $stringlength) { + if ((ord($string{$offset}) | 0x07) == 0xF7) { + // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb + $charval = ((ord($string{($offset + 0)}) & 0x07) << 18) & + ((ord($string{($offset + 1)}) & 0x3F) << 12) & + ((ord($string{($offset + 2)}) & 0x3F) << 6) & + (ord($string{($offset + 3)}) & 0x3F); + $offset += 4; + } elseif ((ord($string{$offset}) | 0x0F) == 0xEF) { + // 1110bbbb 10bbbbbb 10bbbbbb + $charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) & + ((ord($string{($offset + 1)}) & 0x3F) << 6) & + (ord($string{($offset + 2)}) & 0x3F); + $offset += 3; + } elseif ((ord($string{$offset}) | 0x1F) == 0xDF) { + // 110bbbbb 10bbbbbb + $charval = ((ord($string{($offset + 0)}) & 0x1F) << 6) & + (ord($string{($offset + 1)}) & 0x3F); + $offset += 2; + } elseif ((ord($string{$offset}) | 0x7F) == 0x7F) { + // 0bbbbbbb + $charval = ord($string{$offset}); + $offset += 1; + } else { + // error? throw some kind of warning here? + $charval = false; + $offset += 1; + } + if ($charval !== false) { + $newcharstring .= (($charval < 65536) ? self::BigEndian2String($charval, 2) : "\x00".'?'); + } + } + return $newcharstring; + } + + // UTF-8 => UTF-16LE + public static function iconv_fallback_utf8_utf16le($string, $bom=false) { + $newcharstring = ''; + if ($bom) { + $newcharstring .= "\xFF\xFE"; + } + $offset = 0; + $stringlength = strlen($string); + while ($offset < $stringlength) { + if ((ord($string{$offset}) | 0x07) == 0xF7) { + // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb + $charval = ((ord($string{($offset + 0)}) & 0x07) << 18) & + ((ord($string{($offset + 1)}) & 0x3F) << 12) & + ((ord($string{($offset + 2)}) & 0x3F) << 6) & + (ord($string{($offset + 3)}) & 0x3F); + $offset += 4; + } elseif ((ord($string{$offset}) | 0x0F) == 0xEF) { + // 1110bbbb 10bbbbbb 10bbbbbb + $charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) & + ((ord($string{($offset + 1)}) & 0x3F) << 6) & + (ord($string{($offset + 2)}) & 0x3F); + $offset += 3; + } elseif ((ord($string{$offset}) | 0x1F) == 0xDF) { + // 110bbbbb 10bbbbbb + $charval = ((ord($string{($offset + 0)}) & 0x1F) << 6) & + (ord($string{($offset + 1)}) & 0x3F); + $offset += 2; + } elseif ((ord($string{$offset}) | 0x7F) == 0x7F) { + // 0bbbbbbb + $charval = ord($string{$offset}); + $offset += 1; + } else { + // error? maybe throw some warning here? + $charval = false; + $offset += 1; + } + if ($charval !== false) { + $newcharstring .= (($charval < 65536) ? self::LittleEndian2String($charval, 2) : '?'."\x00"); + } + } + return $newcharstring; + } + + // UTF-8 => UTF-16LE (BOM) + public static function iconv_fallback_utf8_utf16($string) { + return self::iconv_fallback_utf8_utf16le($string, true); + } + + // UTF-16BE => UTF-8 + public static function iconv_fallback_utf16be_utf8($string) { + if (substr($string, 0, 2) == "\xFE\xFF") { + // strip BOM + $string = substr($string, 2); + } + $newcharstring = ''; + for ($i = 0; $i < strlen($string); $i += 2) { + $charval = self::BigEndian2Int(substr($string, $i, 2)); + $newcharstring .= self::iconv_fallback_int_utf8($charval); + } + return $newcharstring; + } + + // UTF-16LE => UTF-8 + public static function iconv_fallback_utf16le_utf8($string) { + if (substr($string, 0, 2) == "\xFF\xFE") { + // strip BOM + $string = substr($string, 2); + } + $newcharstring = ''; + for ($i = 0; $i < strlen($string); $i += 2) { + $charval = self::LittleEndian2Int(substr($string, $i, 2)); + $newcharstring .= self::iconv_fallback_int_utf8($charval); + } + return $newcharstring; + } + + // UTF-16BE => ISO-8859-1 + public static function iconv_fallback_utf16be_iso88591($string) { + if (substr($string, 0, 2) == "\xFE\xFF") { + // strip BOM + $string = substr($string, 2); + } + $newcharstring = ''; + for ($i = 0; $i < strlen($string); $i += 2) { + $charval = self::BigEndian2Int(substr($string, $i, 2)); + $newcharstring .= (($charval < 256) ? chr($charval) : '?'); + } + return $newcharstring; + } + + // UTF-16LE => ISO-8859-1 + public static function iconv_fallback_utf16le_iso88591($string) { + if (substr($string, 0, 2) == "\xFF\xFE") { + // strip BOM + $string = substr($string, 2); + } + $newcharstring = ''; + for ($i = 0; $i < strlen($string); $i += 2) { + $charval = self::LittleEndian2Int(substr($string, $i, 2)); + $newcharstring .= (($charval < 256) ? chr($charval) : '?'); + } + return $newcharstring; + } + + // UTF-16 (BOM) => ISO-8859-1 + public static function iconv_fallback_utf16_iso88591($string) { + $bom = substr($string, 0, 2); + if ($bom == "\xFE\xFF") { + return self::iconv_fallback_utf16be_iso88591(substr($string, 2)); + } elseif ($bom == "\xFF\xFE") { + return self::iconv_fallback_utf16le_iso88591(substr($string, 2)); + } + return $string; + } + + // UTF-16 (BOM) => UTF-8 + public static function iconv_fallback_utf16_utf8($string) { + $bom = substr($string, 0, 2); + if ($bom == "\xFE\xFF") { + return self::iconv_fallback_utf16be_utf8(substr($string, 2)); + } elseif ($bom == "\xFF\xFE") { + return self::iconv_fallback_utf16le_utf8(substr($string, 2)); + } + return $string; + } + + public static function iconv_fallback($in_charset, $out_charset, $string) { + + if ($in_charset == $out_charset) { + return $string; + } + + // iconv() availble + if (function_exists('iconv')) { + if ($converted_string = @iconv($in_charset, $out_charset.'//TRANSLIT', $string)) { + switch ($out_charset) { + case 'ISO-8859-1': + $converted_string = rtrim($converted_string, "\x00"); + break; + } + return $converted_string; + } + + // iconv() may sometimes fail with "illegal character in input string" error message + // and return an empty string, but returning the unconverted string is more useful + return $string; + } + + + // iconv() not available + static $ConversionFunctionList = array(); + if (empty($ConversionFunctionList)) { + $ConversionFunctionList['ISO-8859-1']['UTF-8'] = 'iconv_fallback_iso88591_utf8'; + $ConversionFunctionList['ISO-8859-1']['UTF-16'] = 'iconv_fallback_iso88591_utf16'; + $ConversionFunctionList['ISO-8859-1']['UTF-16BE'] = 'iconv_fallback_iso88591_utf16be'; + $ConversionFunctionList['ISO-8859-1']['UTF-16LE'] = 'iconv_fallback_iso88591_utf16le'; + $ConversionFunctionList['UTF-8']['ISO-8859-1'] = 'iconv_fallback_utf8_iso88591'; + $ConversionFunctionList['UTF-8']['UTF-16'] = 'iconv_fallback_utf8_utf16'; + $ConversionFunctionList['UTF-8']['UTF-16BE'] = 'iconv_fallback_utf8_utf16be'; + $ConversionFunctionList['UTF-8']['UTF-16LE'] = 'iconv_fallback_utf8_utf16le'; + $ConversionFunctionList['UTF-16']['ISO-8859-1'] = 'iconv_fallback_utf16_iso88591'; + $ConversionFunctionList['UTF-16']['UTF-8'] = 'iconv_fallback_utf16_utf8'; + $ConversionFunctionList['UTF-16LE']['ISO-8859-1'] = 'iconv_fallback_utf16le_iso88591'; + $ConversionFunctionList['UTF-16LE']['UTF-8'] = 'iconv_fallback_utf16le_utf8'; + $ConversionFunctionList['UTF-16BE']['ISO-8859-1'] = 'iconv_fallback_utf16be_iso88591'; + $ConversionFunctionList['UTF-16BE']['UTF-8'] = 'iconv_fallback_utf16be_utf8'; + } + if (isset($ConversionFunctionList[strtoupper($in_charset)][strtoupper($out_charset)])) { + $ConversionFunction = $ConversionFunctionList[strtoupper($in_charset)][strtoupper($out_charset)]; + return self::$ConversionFunction($string); + } + throw new Exception('PHP does not have iconv() support - cannot convert from '.$in_charset.' to '.$out_charset); + } + + public static function recursiveMultiByteCharString2HTML($data, $charset='ISO-8859-1') { + if (is_string($data)) { + return self::MultiByteCharString2HTML($data, $charset); + } elseif (is_array($data)) { + $return_data = array(); + foreach ($data as $key => $value) { + $return_data[$key] = self::recursiveMultiByteCharString2HTML($value, $charset); + } + return $return_data; + } + // integer, float, objects, resources, etc + return $data; + } + + public static function MultiByteCharString2HTML($string, $charset='ISO-8859-1') { + $string = (string) $string; // in case trying to pass a numeric (float, int) string, would otherwise return an empty string + $HTMLstring = ''; + + switch ($charset) { + case '1251': + case '1252': + case '866': + case '932': + case '936': + case '950': + case 'BIG5': + case 'BIG5-HKSCS': + case 'cp1251': + case 'cp1252': + case 'cp866': + case 'EUC-JP': + case 'EUCJP': + case 'GB2312': + case 'ibm866': + case 'ISO-8859-1': + case 'ISO-8859-15': + case 'ISO8859-1': + case 'ISO8859-15': + case 'KOI8-R': + case 'koi8-ru': + case 'koi8r': + case 'Shift_JIS': + case 'SJIS': + case 'win-1251': + case 'Windows-1251': + case 'Windows-1252': + $HTMLstring = htmlentities($string, ENT_COMPAT, $charset); + break; + + case 'UTF-8': + $strlen = strlen($string); + for ($i = 0; $i < $strlen; $i++) { + $char_ord_val = ord($string{$i}); + $charval = 0; + if ($char_ord_val < 0x80) { + $charval = $char_ord_val; + } elseif ((($char_ord_val & 0xF0) >> 4) == 0x0F && $i+3 < $strlen) { + $charval = (($char_ord_val & 0x07) << 18); + $charval += ((ord($string{++$i}) & 0x3F) << 12); + $charval += ((ord($string{++$i}) & 0x3F) << 6); + $charval += (ord($string{++$i}) & 0x3F); + } elseif ((($char_ord_val & 0xE0) >> 5) == 0x07 && $i+2 < $strlen) { + $charval = (($char_ord_val & 0x0F) << 12); + $charval += ((ord($string{++$i}) & 0x3F) << 6); + $charval += (ord($string{++$i}) & 0x3F); + } elseif ((($char_ord_val & 0xC0) >> 6) == 0x03 && $i+1 < $strlen) { + $charval = (($char_ord_val & 0x1F) << 6); + $charval += (ord($string{++$i}) & 0x3F); + } + if (($charval >= 32) && ($charval <= 127)) { + $HTMLstring .= htmlentities(chr($charval)); + } else { + $HTMLstring .= '&#'.$charval.';'; + } + } + break; + + case 'UTF-16LE': + for ($i = 0; $i < strlen($string); $i += 2) { + $charval = self::LittleEndian2Int(substr($string, $i, 2)); + if (($charval >= 32) && ($charval <= 127)) { + $HTMLstring .= chr($charval); + } else { + $HTMLstring .= '&#'.$charval.';'; + } + } + break; + + case 'UTF-16BE': + for ($i = 0; $i < strlen($string); $i += 2) { + $charval = self::BigEndian2Int(substr($string, $i, 2)); + if (($charval >= 32) && ($charval <= 127)) { + $HTMLstring .= chr($charval); + } else { + $HTMLstring .= '&#'.$charval.';'; + } + } + break; + + default: + $HTMLstring = 'ERROR: Character set "'.$charset.'" not supported in MultiByteCharString2HTML()'; + break; + } + return $HTMLstring; + } + + + + public static function RGADnameLookup($namecode) { + static $RGADname = array(); + if (empty($RGADname)) { + $RGADname[0] = 'not set'; + $RGADname[1] = 'Track Gain Adjustment'; + $RGADname[2] = 'Album Gain Adjustment'; + } + + return (isset($RGADname[$namecode]) ? $RGADname[$namecode] : ''); + } + + + public static function RGADoriginatorLookup($originatorcode) { + static $RGADoriginator = array(); + if (empty($RGADoriginator)) { + $RGADoriginator[0] = 'unspecified'; + $RGADoriginator[1] = 'pre-set by artist/producer/mastering engineer'; + $RGADoriginator[2] = 'set by user'; + $RGADoriginator[3] = 'determined automatically'; + } + + return (isset($RGADoriginator[$originatorcode]) ? $RGADoriginator[$originatorcode] : ''); + } + + + public static function RGADadjustmentLookup($rawadjustment, $signbit) { + $adjustment = $rawadjustment / 10; + if ($signbit == 1) { + $adjustment *= -1; + } + return (float) $adjustment; + } + + + public static function RGADgainString($namecode, $originatorcode, $replaygain) { + if ($replaygain < 0) { + $signbit = '1'; + } else { + $signbit = '0'; + } + $storedreplaygain = intval(round($replaygain * 10)); + $gainstring = str_pad(decbin($namecode), 3, '0', STR_PAD_LEFT); + $gainstring .= str_pad(decbin($originatorcode), 3, '0', STR_PAD_LEFT); + $gainstring .= $signbit; + $gainstring .= str_pad(decbin($storedreplaygain), 9, '0', STR_PAD_LEFT); + + return $gainstring; + } + + public static function RGADamplitude2dB($amplitude) { + return 20 * log10($amplitude); + } + + + public static function GetDataImageSize($imgData, &$imageinfo=array()) { + static $tempdir = ''; + if (empty($tempdir)) { + // yes this is ugly, feel free to suggest a better way + require_once(dirname(__FILE__).'/getid3.php'); + $getid3_temp = new getID3(); + $tempdir = $getid3_temp->tempdir; + unset($getid3_temp); + } + $GetDataImageSize = false; + if ($tempfilename = tempnam($tempdir, 'gI3')) { + if (is_writable($tempfilename) && is_file($tempfilename) && ($tmp = fopen($tempfilename, 'wb'))) { + fwrite($tmp, $imgData); + fclose($tmp); + $GetDataImageSize = @getimagesize($tempfilename, $imageinfo); + } + unlink($tempfilename); + } + return $GetDataImageSize; + } + + public static function ImageExtFromMime($mime_type) { + // temporary way, works OK for now, but should be reworked in the future + return str_replace(array('image/', 'x-', 'jpeg'), array('', '', 'jpg'), $mime_type); + } + + public static function ImageTypesLookup($imagetypeid) { + static $ImageTypesLookup = array(); + if (empty($ImageTypesLookup)) { + $ImageTypesLookup[1] = 'gif'; + $ImageTypesLookup[2] = 'jpeg'; + $ImageTypesLookup[3] = 'png'; + $ImageTypesLookup[4] = 'swf'; + $ImageTypesLookup[5] = 'psd'; + $ImageTypesLookup[6] = 'bmp'; + $ImageTypesLookup[7] = 'tiff (little-endian)'; + $ImageTypesLookup[8] = 'tiff (big-endian)'; + $ImageTypesLookup[9] = 'jpc'; + $ImageTypesLookup[10] = 'jp2'; + $ImageTypesLookup[11] = 'jpx'; + $ImageTypesLookup[12] = 'jb2'; + $ImageTypesLookup[13] = 'swc'; + $ImageTypesLookup[14] = 'iff'; + } + return (isset($ImageTypesLookup[$imagetypeid]) ? $ImageTypesLookup[$imagetypeid] : ''); + } + + public static function CopyTagsToComments(&$ThisFileInfo) { + + // Copy all entries from ['tags'] into common ['comments'] + if (!empty($ThisFileInfo['tags'])) { + foreach ($ThisFileInfo['tags'] as $tagtype => $tagarray) { + foreach ($tagarray as $tagname => $tagdata) { + foreach ($tagdata as $key => $value) { + if (!empty($value)) { + if (empty($ThisFileInfo['comments'][$tagname])) { + + // fall through and append value + + } elseif ($tagtype == 'id3v1') { + + $newvaluelength = strlen(trim($value)); + foreach ($ThisFileInfo['comments'][$tagname] as $existingkey => $existingvalue) { + $oldvaluelength = strlen(trim($existingvalue)); + if (($newvaluelength <= $oldvaluelength) && (substr($existingvalue, 0, $newvaluelength) == trim($value))) { + // new value is identical but shorter-than (or equal-length to) one already in comments - skip + break 2; + } + } + + } elseif (!is_array($value)) { + + $newvaluelength = strlen(trim($value)); + foreach ($ThisFileInfo['comments'][$tagname] as $existingkey => $existingvalue) { + $oldvaluelength = strlen(trim($existingvalue)); + if ((strlen($existingvalue) > 10) && ($newvaluelength > $oldvaluelength) && (substr(trim($value), 0, strlen($existingvalue)) == $existingvalue)) { + $ThisFileInfo['comments'][$tagname][$existingkey] = trim($value); + //break 2; + break; + } + } + + } + if (is_array($value) || empty($ThisFileInfo['comments'][$tagname]) || !in_array(trim($value), $ThisFileInfo['comments'][$tagname])) { + $value = (is_string($value) ? trim($value) : $value); + if (!is_numeric($key)) { + $ThisFileInfo['comments'][$tagname][$key] = $value; + } else { + $ThisFileInfo['comments'][$tagname][] = $value; + } + } + } + } + } + } + + // Copy to ['comments_html'] + if (!empty($ThisFileInfo['comments'])) { + foreach ($ThisFileInfo['comments'] as $field => $values) { + if ($field == 'picture') { + // pictures can take up a lot of space, and we don't need multiple copies of them + // let there be a single copy in [comments][picture], and not elsewhere + continue; + } + foreach ($values as $index => $value) { + if (is_array($value)) { + $ThisFileInfo['comments_html'][$field][$index] = $value; + } else { + $ThisFileInfo['comments_html'][$field][$index] = str_replace('�', '', self::MultiByteCharString2HTML($value, $ThisFileInfo['encoding'])); + } + } + } + } + + } + return true; + } + + + public static function EmbeddedLookup($key, $begin, $end, $file, $name) { + + // Cached + static $cache; + if (isset($cache[$file][$name])) { + return (isset($cache[$file][$name][$key]) ? $cache[$file][$name][$key] : ''); + } + + // Init + $keylength = strlen($key); + $line_count = $end - $begin - 7; + + // Open php file + $fp = fopen($file, 'r'); + + // Discard $begin lines + for ($i = 0; $i < ($begin + 3); $i++) { + fgets($fp, 1024); + } + + // Loop thru line + while (0 < $line_count--) { + + // Read line + $line = ltrim(fgets($fp, 1024), "\t "); + + // METHOD A: only cache the matching key - less memory but slower on next lookup of not-previously-looked-up key + //$keycheck = substr($line, 0, $keylength); + //if ($key == $keycheck) { + // $cache[$file][$name][$keycheck] = substr($line, $keylength + 1); + // break; + //} + + // METHOD B: cache all keys in this lookup - more memory but faster on next lookup of not-previously-looked-up key + //$cache[$file][$name][substr($line, 0, $keylength)] = trim(substr($line, $keylength + 1)); + $explodedLine = explode("\t", $line, 2); + $ThisKey = (isset($explodedLine[0]) ? $explodedLine[0] : ''); + $ThisValue = (isset($explodedLine[1]) ? $explodedLine[1] : ''); + $cache[$file][$name][$ThisKey] = trim($ThisValue); + } + + // Close and return + fclose($fp); + return (isset($cache[$file][$name][$key]) ? $cache[$file][$name][$key] : ''); + } + + public static function IncludeDependency($filename, $sourcefile, $DieOnFailure=false) { + global $GETID3_ERRORARRAY; + + if (file_exists($filename)) { + if (include_once($filename)) { + return true; + } else { + $diemessage = basename($sourcefile).' depends on '.$filename.', which has errors'; + } + } else { + $diemessage = basename($sourcefile).' depends on '.$filename.', which is missing'; + } + if ($DieOnFailure) { + throw new Exception($diemessage); + } else { + $GETID3_ERRORARRAY[] = $diemessage; + } + return false; + } + + public static function trimNullByte($string) { + return trim($string, "\x00"); + } + + public static function getFileSizeSyscall($path) { + $filesize = false; + + if (GETID3_OS_ISWINDOWS) { + if (class_exists('COM')) { // From PHP 5.3.15 and 5.4.5, COM and DOTNET is no longer built into the php core.you have to add COM support in php.ini: + $filesystem = new COM('Scripting.FileSystemObject'); + $file = $filesystem->GetFile($path); + $filesize = $file->Size(); + unset($filesystem, $file); + } else { + $commandline = 'for %I in ('.escapeshellarg($path).') do @echo %~zI'; + } + } else { + $commandline = 'ls -l '.escapeshellarg($path).' | awk \'{print $5}\''; + } + if (isset($commandline)) { + $output = trim(`$commandline`); + if (ctype_digit($output)) { + $filesize = (float) $output; + } + } + return $filesize; + } + + + /** + * Workaround for Bug #37268 (https://bugs.php.net/bug.php?id=37268) + * @param string $path A path. + * @param string $suffix If the name component ends in suffix this will also be cut off. + * @return string + */ + public static function mb_basename($path, $suffix = null) { + $splited = preg_split('#/#', rtrim($path, '/ ')); + return substr(basename('X'.$splited[count($splited) - 1], $suffix), 1); + } + +} \ No newline at end of file diff --git a/wp-includes/ID3/getid3.php b/wp-includes/ID3/getid3.php new file mode 100644 index 0000000..394e25e --- /dev/null +++ b/wp-includes/ID3/getid3.php @@ -0,0 +1,1796 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +// also https://github.com/JamesHeinrich/getID3 // +///////////////////////////////////////////////////////////////// +// // +// Please see readme.txt for more information // +// /// +///////////////////////////////////////////////////////////////// + +// define a constant rather than looking up every time it is needed +if (!defined('GETID3_OS_ISWINDOWS')) { + define('GETID3_OS_ISWINDOWS', (stripos(PHP_OS, 'WIN') === 0)); +} +// Get base path of getID3() - ONCE +if (!defined('GETID3_INCLUDEPATH')) { + define('GETID3_INCLUDEPATH', dirname(__FILE__).DIRECTORY_SEPARATOR); +} +// Workaround Bug #39923 (https://bugs.php.net/bug.php?id=39923) +if (!defined('IMG_JPG') && defined('IMAGETYPE_JPEG')) { + define('IMG_JPG', IMAGETYPE_JPEG); +} + +// attempt to define temp dir as something flexible but reliable +$temp_dir = ini_get('upload_tmp_dir'); +if ($temp_dir && (!is_dir($temp_dir) || !is_readable($temp_dir))) { + $temp_dir = ''; +} +if (!$temp_dir) { + // sys_get_temp_dir() may give inaccessible temp dir, e.g. with open_basedir on virtual hosts + $temp_dir = sys_get_temp_dir(); +} +$temp_dir = @realpath($temp_dir); // see https://github.com/JamesHeinrich/getID3/pull/10 +$open_basedir = ini_get('open_basedir'); +if ($open_basedir) { + // e.g. "/var/www/vhosts/getid3.org/httpdocs/:/tmp/" + $temp_dir = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $temp_dir); + $open_basedir = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $open_basedir); + if (substr($temp_dir, -1, 1) != DIRECTORY_SEPARATOR) { + $temp_dir .= DIRECTORY_SEPARATOR; + } + $found_valid_tempdir = false; + $open_basedirs = explode(PATH_SEPARATOR, $open_basedir); + foreach ($open_basedirs as $basedir) { + if (substr($basedir, -1, 1) != DIRECTORY_SEPARATOR) { + $basedir .= DIRECTORY_SEPARATOR; + } + if (preg_match('#^'.preg_quote($basedir).'#', $temp_dir)) { + $found_valid_tempdir = true; + break; + } + } + if (!$found_valid_tempdir) { + $temp_dir = ''; + } + unset($open_basedirs, $found_valid_tempdir, $basedir); +} +if (!$temp_dir) { + $temp_dir = '*'; // invalid directory name should force tempnam() to use system default temp dir +} +// $temp_dir = '/something/else/'; // feel free to override temp dir here if it works better for your system +if (!defined('GETID3_TEMP_DIR')) { + define('GETID3_TEMP_DIR', $temp_dir); +} +unset($open_basedir, $temp_dir); + +// End: Defines + + +class getID3 +{ + // public: Settings + public $encoding = 'UTF-8'; // CASE SENSITIVE! - i.e. (must be supported by iconv()). Examples: ISO-8859-1 UTF-8 UTF-16 UTF-16BE + public $encoding_id3v1 = 'ISO-8859-1'; // Should always be 'ISO-8859-1', but some tags may be written in other encodings such as 'EUC-CN' or 'CP1252' + + // public: Optional tag checks - disable for speed. + public $option_tag_id3v1 = true; // Read and process ID3v1 tags + public $option_tag_id3v2 = true; // Read and process ID3v2 tags + public $option_tag_lyrics3 = true; // Read and process Lyrics3 tags + public $option_tag_apetag = true; // Read and process APE tags + public $option_tags_process = true; // Copy tags to root key 'tags' and encode to $this->encoding + public $option_tags_html = true; // Copy tags to root key 'tags_html' properly translated from various encodings to HTML entities + + // public: Optional tag/comment calucations + public $option_extra_info = true; // Calculate additional info such as bitrate, channelmode etc + + // public: Optional handling of embedded attachments (e.g. images) + public $option_save_attachments = true; // defaults to true (ATTACHMENTS_INLINE) for backward compatibility + + // public: Optional calculations + public $option_md5_data = false; // Get MD5 sum of data part - slow + public $option_md5_data_source = false; // Use MD5 of source file if availble - only FLAC and OptimFROG + public $option_sha1_data = false; // Get SHA1 sum of data part - slow + public $option_max_2gb_check = null; // Check whether file is larger than 2GB and thus not supported by 32-bit PHP (null: auto-detect based on PHP_INT_MAX) + + // public: Read buffer size in bytes + public $option_fread_buffer_size = 32768; + + // Public variables + public $filename; // Filename of file being analysed. + public $fp; // Filepointer to file being analysed. + public $info; // Result array. + public $tempdir = GETID3_TEMP_DIR; + public $memory_limit = 0; + + // Protected variables + protected $startup_error = ''; + protected $startup_warning = ''; + + const VERSION = '1.9.8-20140511'; + const FREAD_BUFFER_SIZE = 32768; + + const ATTACHMENTS_NONE = false; + const ATTACHMENTS_INLINE = true; + + // public: constructor + public function __construct() { + + // Check memory + $this->memory_limit = ini_get('memory_limit'); + if (preg_match('#([0-9]+)M#i', $this->memory_limit, $matches)) { + // could be stored as "16M" rather than 16777216 for example + $this->memory_limit = $matches[1] * 1048576; + } elseif (preg_match('#([0-9]+)G#i', $this->memory_limit, $matches)) { // The 'G' modifier is available since PHP 5.1.0 + // could be stored as "2G" rather than 2147483648 for example + $this->memory_limit = $matches[1] * 1073741824; + } + if ($this->memory_limit <= 0) { + // memory limits probably disabled + } elseif ($this->memory_limit <= 4194304) { + $this->startup_error .= 'PHP has less than 4MB available memory and will very likely run out. Increase memory_limit in php.ini'; + } elseif ($this->memory_limit <= 12582912) { + $this->startup_warning .= 'PHP has less than 12MB available memory and might run out if all modules are loaded. Increase memory_limit in php.ini'; + } + + // Check safe_mode off + if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) { + $this->warning('WARNING: Safe mode is on, shorten support disabled, md5data/sha1data for ogg vorbis disabled, ogg vorbos/flac tag writing disabled.'); + } + + if (intval(ini_get('mbstring.func_overload')) > 0) { + $this->warning('WARNING: php.ini contains "mbstring.func_overload = '.ini_get('mbstring.func_overload').'", this may break things.'); + } + + // Check for magic_quotes_runtime + if (function_exists('get_magic_quotes_runtime')) { + if (get_magic_quotes_runtime()) { + return $this->startup_error('magic_quotes_runtime must be disabled before running getID3(). Surround getid3 block by set_magic_quotes_runtime(0) and set_magic_quotes_runtime(1).'); + } + } + + // Check for magic_quotes_gpc + if (function_exists('magic_quotes_gpc')) { + if (get_magic_quotes_gpc()) { + return $this->startup_error('magic_quotes_gpc must be disabled before running getID3(). Surround getid3 block by set_magic_quotes_gpc(0) and set_magic_quotes_gpc(1).'); + } + } + + // Load support library + if (!include_once(GETID3_INCLUDEPATH.'getid3.lib.php')) { + $this->startup_error .= 'getid3.lib.php is missing or corrupt'; + } + + if ($this->option_max_2gb_check === null) { + $this->option_max_2gb_check = (PHP_INT_MAX <= 2147483647); + } + + + // Needed for Windows only: + // Define locations of helper applications for Shorten, VorbisComment, MetaFLAC + // as well as other helper functions such as head, tail, md5sum, etc + // This path cannot contain spaces, but the below code will attempt to get the + // 8.3-equivalent path automatically + // IMPORTANT: This path must include the trailing slash + if (GETID3_OS_ISWINDOWS && !defined('GETID3_HELPERAPPSDIR')) { + + $helperappsdir = GETID3_INCLUDEPATH.'..'.DIRECTORY_SEPARATOR.'helperapps'; // must not have any space in this path + + if (!is_dir($helperappsdir)) { + $this->startup_warning .= '"'.$helperappsdir.'" cannot be defined as GETID3_HELPERAPPSDIR because it does not exist'; + } elseif (strpos(realpath($helperappsdir), ' ') !== false) { + $DirPieces = explode(DIRECTORY_SEPARATOR, realpath($helperappsdir)); + $path_so_far = array(); + foreach ($DirPieces as $key => $value) { + if (strpos($value, ' ') !== false) { + if (!empty($path_so_far)) { + $commandline = 'dir /x '.escapeshellarg(implode(DIRECTORY_SEPARATOR, $path_so_far)); + $dir_listing = `$commandline`; + $lines = explode("\n", $dir_listing); + foreach ($lines as $line) { + $line = trim($line); + if (preg_match('#^([0-9/]{10}) +([0-9:]{4,5}( [AP]M)?) +(|[0-9,]+) +([^ ]{0,11}) +(.+)$#', $line, $matches)) { + list($dummy, $date, $time, $ampm, $filesize, $shortname, $filename) = $matches; + if ((strtoupper($filesize) == '') && (strtolower($filename) == strtolower($value))) { + $value = $shortname; + } + } + } + } else { + $this->startup_warning .= 'GETID3_HELPERAPPSDIR must not have any spaces in it - use 8dot3 naming convention if neccesary. You can run "dir /x" from the commandline to see the correct 8.3-style names.'; + } + } + $path_so_far[] = $value; + } + $helperappsdir = implode(DIRECTORY_SEPARATOR, $path_so_far); + } + define('GETID3_HELPERAPPSDIR', $helperappsdir.DIRECTORY_SEPARATOR); + } + + return true; + } + + public function version() { + return self::VERSION; + } + + public function fread_buffer_size() { + return $this->option_fread_buffer_size; + } + + + // public: setOption + public function setOption($optArray) { + if (!is_array($optArray) || empty($optArray)) { + return false; + } + foreach ($optArray as $opt => $val) { + if (isset($this->$opt) === false) { + continue; + } + $this->$opt = $val; + } + return true; + } + + + public function openfile($filename) { + try { + if (!empty($this->startup_error)) { + throw new getid3_exception($this->startup_error); + } + if (!empty($this->startup_warning)) { + $this->warning($this->startup_warning); + } + + // init result array and set parameters + $this->filename = $filename; + $this->info = array(); + $this->info['GETID3_VERSION'] = $this->version(); + $this->info['php_memory_limit'] = $this->memory_limit; + + // remote files not supported + if (preg_match('/^(ht|f)tp:\/\//', $filename)) { + throw new getid3_exception('Remote files are not supported - please copy the file locally first'); + } + + $filename = str_replace('/', DIRECTORY_SEPARATOR, $filename); + $filename = preg_replace('#(.+)'.preg_quote(DIRECTORY_SEPARATOR).'{2,}#U', '\1'.DIRECTORY_SEPARATOR, $filename); + + // open local file + //if (is_readable($filename) && is_file($filename) && ($this->fp = fopen($filename, 'rb'))) { // see http://www.getid3.org/phpBB3/viewtopic.php?t=1720 + if ((is_readable($filename) || file_exists($filename)) && is_file($filename) && ($this->fp = fopen($filename, 'rb'))) { + // great + } else { + $errormessagelist = array(); + if (!is_readable($filename)) { + $errormessagelist[] = '!is_readable'; + } + if (!is_file($filename)) { + $errormessagelist[] = '!is_file'; + } + if (!file_exists($filename)) { + $errormessagelist[] = '!file_exists'; + } + if (empty($errormessagelist)) { + $errormessagelist[] = 'fopen failed'; + } + throw new getid3_exception('Could not open "'.$filename.'" ('.implode('; ', $errormessagelist).')'); + } + + $this->info['filesize'] = filesize($filename); + // set redundant parameters - might be needed in some include file + // filenames / filepaths in getID3 are always expressed with forward slashes (unix-style) for both Windows and other to try and minimize confusion + $filename = str_replace('\\', '/', $filename); + $this->info['filepath'] = str_replace('\\', '/', realpath(dirname($filename))); + $this->info['filename'] = getid3_lib::mb_basename($filename); + $this->info['filenamepath'] = $this->info['filepath'].'/'.$this->info['filename']; + + + // option_max_2gb_check + if ($this->option_max_2gb_check) { + // PHP (32-bit all, and 64-bit Windows) doesn't support integers larger than 2^31 (~2GB) + // filesize() simply returns (filesize % (pow(2, 32)), no matter the actual filesize + // ftell() returns 0 if seeking to the end is beyond the range of unsigned integer + $fseek = fseek($this->fp, 0, SEEK_END); + if (($fseek < 0) || (($this->info['filesize'] != 0) && (ftell($this->fp) == 0)) || + ($this->info['filesize'] < 0) || + (ftell($this->fp) < 0)) { + $real_filesize = getid3_lib::getFileSizeSyscall($this->info['filenamepath']); + + if ($real_filesize === false) { + unset($this->info['filesize']); + fclose($this->fp); + throw new getid3_exception('Unable to determine actual filesize. File is most likely larger than '.round(PHP_INT_MAX / 1073741824).'GB and is not supported by PHP.'); + } elseif (getid3_lib::intValueSupported($real_filesize)) { + unset($this->info['filesize']); + fclose($this->fp); + throw new getid3_exception('PHP seems to think the file is larger than '.round(PHP_INT_MAX / 1073741824).'GB, but filesystem reports it as '.number_format($real_filesize, 3).'GB, please report to info@getid3.org'); + } + $this->info['filesize'] = $real_filesize; + $this->warning('File is larger than '.round(PHP_INT_MAX / 1073741824).'GB (filesystem reports it as '.number_format($real_filesize, 3).'GB) and is not properly supported by PHP.'); + } + } + + // set more parameters + $this->info['avdataoffset'] = 0; + $this->info['avdataend'] = $this->info['filesize']; + $this->info['fileformat'] = ''; // filled in later + $this->info['audio']['dataformat'] = ''; // filled in later, unset if not used + $this->info['video']['dataformat'] = ''; // filled in later, unset if not used + $this->info['tags'] = array(); // filled in later, unset if not used + $this->info['error'] = array(); // filled in later, unset if not used + $this->info['warning'] = array(); // filled in later, unset if not used + $this->info['comments'] = array(); // filled in later, unset if not used + $this->info['encoding'] = $this->encoding; // required by id3v2 and iso modules - can be unset at the end if desired + + return true; + + } catch (Exception $e) { + $this->error($e->getMessage()); + } + return false; + } + + // public: analyze file + public function analyze($filename) { + try { + if (!$this->openfile($filename)) { + return $this->info; + } + + // Handle tags + foreach (array('id3v2'=>'id3v2', 'id3v1'=>'id3v1', 'apetag'=>'ape', 'lyrics3'=>'lyrics3') as $tag_name => $tag_key) { + $option_tag = 'option_tag_'.$tag_name; + if ($this->$option_tag) { + $this->include_module('tag.'.$tag_name); + try { + $tag_class = 'getid3_'.$tag_name; + $tag = new $tag_class($this); + $tag->Analyze(); + } + catch (getid3_exception $e) { + throw $e; + } + } + } + if (isset($this->info['id3v2']['tag_offset_start'])) { + $this->info['avdataoffset'] = max($this->info['avdataoffset'], $this->info['id3v2']['tag_offset_end']); + } + foreach (array('id3v1'=>'id3v1', 'apetag'=>'ape', 'lyrics3'=>'lyrics3') as $tag_name => $tag_key) { + if (isset($this->info[$tag_key]['tag_offset_start'])) { + $this->info['avdataend'] = min($this->info['avdataend'], $this->info[$tag_key]['tag_offset_start']); + } + } + + // ID3v2 detection (NOT parsing), even if ($this->option_tag_id3v2 == false) done to make fileformat easier + if (!$this->option_tag_id3v2) { + fseek($this->fp, 0); + $header = fread($this->fp, 10); + if ((substr($header, 0, 3) == 'ID3') && (strlen($header) == 10)) { + $this->info['id3v2']['header'] = true; + $this->info['id3v2']['majorversion'] = ord($header{3}); + $this->info['id3v2']['minorversion'] = ord($header{4}); + $this->info['avdataoffset'] += getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10; // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length + } + } + + // read 32 kb file data + fseek($this->fp, $this->info['avdataoffset']); + $formattest = fread($this->fp, 32774); + + // determine format + $determined_format = $this->GetFileFormat($formattest, $filename); + + // unable to determine file format + if (!$determined_format) { + fclose($this->fp); + return $this->error('unable to determine file format'); + } + + // check for illegal ID3 tags + if (isset($determined_format['fail_id3']) && (in_array('id3v1', $this->info['tags']) || in_array('id3v2', $this->info['tags']))) { + if ($determined_format['fail_id3'] === 'ERROR') { + fclose($this->fp); + return $this->error('ID3 tags not allowed on this file type.'); + } elseif ($determined_format['fail_id3'] === 'WARNING') { + $this->warning('ID3 tags not allowed on this file type.'); + } + } + + // check for illegal APE tags + if (isset($determined_format['fail_ape']) && in_array('ape', $this->info['tags'])) { + if ($determined_format['fail_ape'] === 'ERROR') { + fclose($this->fp); + return $this->error('APE tags not allowed on this file type.'); + } elseif ($determined_format['fail_ape'] === 'WARNING') { + $this->warning('APE tags not allowed on this file type.'); + } + } + + // set mime type + $this->info['mime_type'] = $determined_format['mime_type']; + + // supported format signature pattern detected, but module deleted + if (!file_exists(GETID3_INCLUDEPATH.$determined_format['include'])) { + fclose($this->fp); + return $this->error('Format not supported, module "'.$determined_format['include'].'" was removed.'); + } + + // module requires iconv support + // Check encoding/iconv support + if (!empty($determined_format['iconv_req']) && !function_exists('iconv') && !in_array($this->encoding, array('ISO-8859-1', 'UTF-8', 'UTF-16LE', 'UTF-16BE', 'UTF-16'))) { + $errormessage = 'iconv() support is required for this module ('.$determined_format['include'].') for encodings other than ISO-8859-1, UTF-8, UTF-16LE, UTF16-BE, UTF-16. '; + if (GETID3_OS_ISWINDOWS) { + $errormessage .= 'PHP does not have iconv() support. Please enable php_iconv.dll in php.ini, and copy iconv.dll from c:/php/dlls to c:/windows/system32'; + } else { + $errormessage .= 'PHP is not compiled with iconv() support. Please recompile with the --with-iconv switch'; + } + return $this->error($errormessage); + } + + // include module + include_once(GETID3_INCLUDEPATH.$determined_format['include']); + + // instantiate module class + $class_name = 'getid3_'.$determined_format['module']; + if (!class_exists($class_name)) { + return $this->error('Format not supported, module "'.$determined_format['include'].'" is corrupt.'); + } + $class = new $class_name($this); + $class->Analyze(); + unset($class); + + // close file + fclose($this->fp); + + // process all tags - copy to 'tags' and convert charsets + if ($this->option_tags_process) { + $this->HandleAllTags(); + } + + // perform more calculations + if ($this->option_extra_info) { + $this->ChannelsBitratePlaytimeCalculations(); + $this->CalculateCompressionRatioVideo(); + $this->CalculateCompressionRatioAudio(); + $this->CalculateReplayGain(); + $this->ProcessAudioStreams(); + } + + // get the MD5 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags + if ($this->option_md5_data) { + // do not calc md5_data if md5_data_source is present - set by flac only - future MPC/SV8 too + if (!$this->option_md5_data_source || empty($this->info['md5_data_source'])) { + $this->getHashdata('md5'); + } + } + + // get the SHA1 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags + if ($this->option_sha1_data) { + $this->getHashdata('sha1'); + } + + // remove undesired keys + $this->CleanUp(); + + } catch (Exception $e) { + $this->error('Caught exception: '.$e->getMessage()); + } + + // return info array + return $this->info; + } + + + // private: error handling + public function error($message) { + $this->CleanUp(); + if (!isset($this->info['error'])) { + $this->info['error'] = array(); + } + $this->info['error'][] = $message; + return $this->info; + } + + + // private: warning handling + public function warning($message) { + $this->info['warning'][] = $message; + return true; + } + + + // private: CleanUp + private function CleanUp() { + + // remove possible empty keys + $AVpossibleEmptyKeys = array('dataformat', 'bits_per_sample', 'encoder_options', 'streams', 'bitrate'); + foreach ($AVpossibleEmptyKeys as $dummy => $key) { + if (empty($this->info['audio'][$key]) && isset($this->info['audio'][$key])) { + unset($this->info['audio'][$key]); + } + if (empty($this->info['video'][$key]) && isset($this->info['video'][$key])) { + unset($this->info['video'][$key]); + } + } + + // remove empty root keys + if (!empty($this->info)) { + foreach ($this->info as $key => $value) { + if (empty($this->info[$key]) && ($this->info[$key] !== 0) && ($this->info[$key] !== '0')) { + unset($this->info[$key]); + } + } + } + + // remove meaningless entries from unknown-format files + if (empty($this->info['fileformat'])) { + if (isset($this->info['avdataoffset'])) { + unset($this->info['avdataoffset']); + } + if (isset($this->info['avdataend'])) { + unset($this->info['avdataend']); + } + } + + // remove possible duplicated identical entries + if (!empty($this->info['error'])) { + $this->info['error'] = array_values(array_unique($this->info['error'])); + } + if (!empty($this->info['warning'])) { + $this->info['warning'] = array_values(array_unique($this->info['warning'])); + } + + // remove "global variable" type keys + unset($this->info['php_memory_limit']); + + return true; + } + + + // return array containing information about all supported formats + public function GetFileFormatArray() { + static $format_info = array(); + if (empty($format_info)) { + $format_info = array( + + // Audio formats + + // AC-3 - audio - Dolby AC-3 / Dolby Digital + 'ac3' => array( + 'pattern' => '^\x0B\x77', + 'group' => 'audio', + 'module' => 'ac3', + 'mime_type' => 'audio/ac3', + ), + + // AAC - audio - Advanced Audio Coding (AAC) - ADIF format + 'adif' => array( + 'pattern' => '^ADIF', + 'group' => 'audio', + 'module' => 'aac', + 'mime_type' => 'application/octet-stream', + 'fail_ape' => 'WARNING', + ), + +/* + // AA - audio - Audible Audiobook + 'aa' => array( + 'pattern' => '^.{4}\x57\x90\x75\x36', + 'group' => 'audio', + 'module' => 'aa', + 'mime_type' => 'audio/audible', + ), +*/ + // AAC - audio - Advanced Audio Coding (AAC) - ADTS format (very similar to MP3) + 'adts' => array( + 'pattern' => '^\xFF[\xF0-\xF1\xF8-\xF9]', + 'group' => 'audio', + 'module' => 'aac', + 'mime_type' => 'application/octet-stream', + 'fail_ape' => 'WARNING', + ), + + + // AU - audio - NeXT/Sun AUdio (AU) + 'au' => array( + 'pattern' => '^\.snd', + 'group' => 'audio', + 'module' => 'au', + 'mime_type' => 'audio/basic', + ), + + // AMR - audio - Adaptive Multi Rate + 'amr' => array( + 'pattern' => '^\x23\x21AMR\x0A', // #!AMR[0A] + 'group' => 'audio', + 'module' => 'amr', + 'mime_type' => 'audio/amr', + ), + + // AVR - audio - Audio Visual Research + 'avr' => array( + 'pattern' => '^2BIT', + 'group' => 'audio', + 'module' => 'avr', + 'mime_type' => 'application/octet-stream', + ), + + // BONK - audio - Bonk v0.9+ + 'bonk' => array( + 'pattern' => '^\x00(BONK|INFO|META| ID3)', + 'group' => 'audio', + 'module' => 'bonk', + 'mime_type' => 'audio/xmms-bonk', + ), + + // DSS - audio - Digital Speech Standard + 'dss' => array( + 'pattern' => '^[\x02-\x03]ds[s2]', + 'group' => 'audio', + 'module' => 'dss', + 'mime_type' => 'application/octet-stream', + ), + + // DTS - audio - Dolby Theatre System + 'dts' => array( + 'pattern' => '^\x7F\xFE\x80\x01', + 'group' => 'audio', + 'module' => 'dts', + 'mime_type' => 'audio/dts', + ), + + // FLAC - audio - Free Lossless Audio Codec + 'flac' => array( + 'pattern' => '^fLaC', + 'group' => 'audio', + 'module' => 'flac', + 'mime_type' => 'audio/x-flac', + ), + + // LA - audio - Lossless Audio (LA) + 'la' => array( + 'pattern' => '^LA0[2-4]', + 'group' => 'audio', + 'module' => 'la', + 'mime_type' => 'application/octet-stream', + ), + + // LPAC - audio - Lossless Predictive Audio Compression (LPAC) + 'lpac' => array( + 'pattern' => '^LPAC', + 'group' => 'audio', + 'module' => 'lpac', + 'mime_type' => 'application/octet-stream', + ), + + // MIDI - audio - MIDI (Musical Instrument Digital Interface) + 'midi' => array( + 'pattern' => '^MThd', + 'group' => 'audio', + 'module' => 'midi', + 'mime_type' => 'audio/midi', + ), + + // MAC - audio - Monkey's Audio Compressor + 'mac' => array( + 'pattern' => '^MAC ', + 'group' => 'audio', + 'module' => 'monkey', + 'mime_type' => 'application/octet-stream', + ), + +// has been known to produce false matches in random files (e.g. JPEGs), leave out until more precise matching available +// // MOD - audio - MODule (assorted sub-formats) +// 'mod' => array( +// 'pattern' => '^.{1080}(M\\.K\\.|M!K!|FLT4|FLT8|[5-9]CHN|[1-3][0-9]CH)', +// 'group' => 'audio', +// 'module' => 'mod', +// 'option' => 'mod', +// 'mime_type' => 'audio/mod', +// ), + + // MOD - audio - MODule (Impulse Tracker) + 'it' => array( + 'pattern' => '^IMPM', + 'group' => 'audio', + 'module' => 'mod', + //'option' => 'it', + 'mime_type' => 'audio/it', + ), + + // MOD - audio - MODule (eXtended Module, various sub-formats) + 'xm' => array( + 'pattern' => '^Extended Module', + 'group' => 'audio', + 'module' => 'mod', + //'option' => 'xm', + 'mime_type' => 'audio/xm', + ), + + // MOD - audio - MODule (ScreamTracker) + 's3m' => array( + 'pattern' => '^.{44}SCRM', + 'group' => 'audio', + 'module' => 'mod', + //'option' => 's3m', + 'mime_type' => 'audio/s3m', + ), + + // MPC - audio - Musepack / MPEGplus + 'mpc' => array( + 'pattern' => '^(MPCK|MP\+|[\x00\x01\x10\x11\x40\x41\x50\x51\x80\x81\x90\x91\xC0\xC1\xD0\xD1][\x20-37][\x00\x20\x40\x60\x80\xA0\xC0\xE0])', + 'group' => 'audio', + 'module' => 'mpc', + 'mime_type' => 'audio/x-musepack', + ), + + // MP3 - audio - MPEG-audio Layer 3 (very similar to AAC-ADTS) + 'mp3' => array( + 'pattern' => '^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\x0B\x10-\x1B\x20-\x2B\x30-\x3B\x40-\x4B\x50-\x5B\x60-\x6B\x70-\x7B\x80-\x8B\x90-\x9B\xA0-\xAB\xB0-\xBB\xC0-\xCB\xD0-\xDB\xE0-\xEB\xF0-\xFB]', + 'group' => 'audio', + 'module' => 'mp3', + 'mime_type' => 'audio/mpeg', + ), + + // OFR - audio - OptimFROG + 'ofr' => array( + 'pattern' => '^(\*RIFF|OFR)', + 'group' => 'audio', + 'module' => 'optimfrog', + 'mime_type' => 'application/octet-stream', + ), + + // RKAU - audio - RKive AUdio compressor + 'rkau' => array( + 'pattern' => '^RKA', + 'group' => 'audio', + 'module' => 'rkau', + 'mime_type' => 'application/octet-stream', + ), + + // SHN - audio - Shorten + 'shn' => array( + 'pattern' => '^ajkg', + 'group' => 'audio', + 'module' => 'shorten', + 'mime_type' => 'audio/xmms-shn', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + ), + + // TTA - audio - TTA Lossless Audio Compressor (http://tta.corecodec.org) + 'tta' => array( + 'pattern' => '^TTA', // could also be '^TTA(\x01|\x02|\x03|2|1)' + 'group' => 'audio', + 'module' => 'tta', + 'mime_type' => 'application/octet-stream', + ), + + // VOC - audio - Creative Voice (VOC) + 'voc' => array( + 'pattern' => '^Creative Voice File', + 'group' => 'audio', + 'module' => 'voc', + 'mime_type' => 'audio/voc', + ), + + // VQF - audio - transform-domain weighted interleave Vector Quantization Format (VQF) + 'vqf' => array( + 'pattern' => '^TWIN', + 'group' => 'audio', + 'module' => 'vqf', + 'mime_type' => 'application/octet-stream', + ), + + // WV - audio - WavPack (v4.0+) + 'wv' => array( + 'pattern' => '^wvpk', + 'group' => 'audio', + 'module' => 'wavpack', + 'mime_type' => 'application/octet-stream', + ), + + + // Audio-Video formats + + // ASF - audio/video - Advanced Streaming Format, Windows Media Video, Windows Media Audio + 'asf' => array( + 'pattern' => '^\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C', + 'group' => 'audio-video', + 'module' => 'asf', + 'mime_type' => 'video/x-ms-asf', + 'iconv_req' => false, + ), + + // BINK - audio/video - Bink / Smacker + 'bink' => array( + 'pattern' => '^(BIK|SMK)', + 'group' => 'audio-video', + 'module' => 'bink', + 'mime_type' => 'application/octet-stream', + ), + + // FLV - audio/video - FLash Video + 'flv' => array( + 'pattern' => '^FLV\x01', + 'group' => 'audio-video', + 'module' => 'flv', + 'mime_type' => 'video/x-flv', + ), + + // MKAV - audio/video - Mastroka + 'matroska' => array( + 'pattern' => '^\x1A\x45\xDF\xA3', + 'group' => 'audio-video', + 'module' => 'matroska', + 'mime_type' => 'video/x-matroska', // may also be audio/x-matroska + ), + + // MPEG - audio/video - MPEG (Moving Pictures Experts Group) + 'mpeg' => array( + 'pattern' => '^\x00\x00\x01(\xBA|\xB3)', + 'group' => 'audio-video', + 'module' => 'mpeg', + 'mime_type' => 'video/mpeg', + ), + + // NSV - audio/video - Nullsoft Streaming Video (NSV) + 'nsv' => array( + 'pattern' => '^NSV[sf]', + 'group' => 'audio-video', + 'module' => 'nsv', + 'mime_type' => 'application/octet-stream', + ), + + // Ogg - audio/video - Ogg (Ogg-Vorbis, Ogg-FLAC, Speex, Ogg-Theora(*), Ogg-Tarkin(*)) + 'ogg' => array( + 'pattern' => '^OggS', + 'group' => 'audio', + 'module' => 'ogg', + 'mime_type' => 'application/ogg', + 'fail_id3' => 'WARNING', + 'fail_ape' => 'WARNING', + ), + + // QT - audio/video - Quicktime + 'quicktime' => array( + 'pattern' => '^.{4}(cmov|free|ftyp|mdat|moov|pnot|skip|wide)', + 'group' => 'audio-video', + 'module' => 'quicktime', + 'mime_type' => 'video/quicktime', + ), + + // RIFF - audio/video - Resource Interchange File Format (RIFF) / WAV / AVI / CD-audio / SDSS = renamed variant used by SmartSound QuickTracks (www.smartsound.com) / FORM = Audio Interchange File Format (AIFF) + 'riff' => array( + 'pattern' => '^(RIFF|SDSS|FORM)', + 'group' => 'audio-video', + 'module' => 'riff', + 'mime_type' => 'audio/x-wave', + 'fail_ape' => 'WARNING', + ), + + // Real - audio/video - RealAudio, RealVideo + 'real' => array( + 'pattern' => '^(\\.RMF|\\.ra)', + 'group' => 'audio-video', + 'module' => 'real', + 'mime_type' => 'audio/x-realaudio', + ), + + // SWF - audio/video - ShockWave Flash + 'swf' => array( + 'pattern' => '^(F|C)WS', + 'group' => 'audio-video', + 'module' => 'swf', + 'mime_type' => 'application/x-shockwave-flash', + ), + + // TS - audio/video - MPEG-2 Transport Stream + 'ts' => array( + 'pattern' => '^(\x47.{187}){10,}', // packets are 188 bytes long and start with 0x47 "G". Check for at least 10 packets matching this pattern + 'group' => 'audio-video', + 'module' => 'ts', + 'mime_type' => 'video/MP2T', + ), + + + // Still-Image formats + + // BMP - still image - Bitmap (Windows, OS/2; uncompressed, RLE8, RLE4) + 'bmp' => array( + 'pattern' => '^BM', + 'group' => 'graphic', + 'module' => 'bmp', + 'mime_type' => 'image/bmp', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + ), + + // GIF - still image - Graphics Interchange Format + 'gif' => array( + 'pattern' => '^GIF', + 'group' => 'graphic', + 'module' => 'gif', + 'mime_type' => 'image/gif', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + ), + + // JPEG - still image - Joint Photographic Experts Group (JPEG) + 'jpg' => array( + 'pattern' => '^\xFF\xD8\xFF', + 'group' => 'graphic', + 'module' => 'jpg', + 'mime_type' => 'image/jpeg', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + ), + + // PCD - still image - Kodak Photo CD + 'pcd' => array( + 'pattern' => '^.{2048}PCD_IPI\x00', + 'group' => 'graphic', + 'module' => 'pcd', + 'mime_type' => 'image/x-photo-cd', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + ), + + + // PNG - still image - Portable Network Graphics (PNG) + 'png' => array( + 'pattern' => '^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A', + 'group' => 'graphic', + 'module' => 'png', + 'mime_type' => 'image/png', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + ), + + + // SVG - still image - Scalable Vector Graphics (SVG) + 'svg' => array( + 'pattern' => '( 'graphic', + 'module' => 'svg', + 'mime_type' => 'image/svg+xml', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + ), + + + // TIFF - still image - Tagged Information File Format (TIFF) + 'tiff' => array( + 'pattern' => '^(II\x2A\x00|MM\x00\x2A)', + 'group' => 'graphic', + 'module' => 'tiff', + 'mime_type' => 'image/tiff', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + ), + + + // EFAX - still image - eFax (TIFF derivative) + 'efax' => array( + 'pattern' => '^\xDC\xFE', + 'group' => 'graphic', + 'module' => 'efax', + 'mime_type' => 'image/efax', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + ), + + + // Data formats + + // ISO - data - International Standards Organization (ISO) CD-ROM Image + 'iso' => array( + 'pattern' => '^.{32769}CD001', + 'group' => 'misc', + 'module' => 'iso', + 'mime_type' => 'application/octet-stream', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + 'iconv_req' => false, + ), + + // RAR - data - RAR compressed data + 'rar' => array( + 'pattern' => '^Rar\!', + 'group' => 'archive', + 'module' => 'rar', + 'mime_type' => 'application/octet-stream', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + ), + + // SZIP - audio/data - SZIP compressed data + 'szip' => array( + 'pattern' => '^SZ\x0A\x04', + 'group' => 'archive', + 'module' => 'szip', + 'mime_type' => 'application/octet-stream', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + ), + + // TAR - data - TAR compressed data + 'tar' => array( + 'pattern' => '^.{100}[0-9\x20]{7}\x00[0-9\x20]{7}\x00[0-9\x20]{7}\x00[0-9\x20\x00]{12}[0-9\x20\x00]{12}', + 'group' => 'archive', + 'module' => 'tar', + 'mime_type' => 'application/x-tar', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + ), + + // GZIP - data - GZIP compressed data + 'gz' => array( + 'pattern' => '^\x1F\x8B\x08', + 'group' => 'archive', + 'module' => 'gzip', + 'mime_type' => 'application/x-gzip', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + ), + + // ZIP - data - ZIP compressed data + 'zip' => array( + 'pattern' => '^PK\x03\x04', + 'group' => 'archive', + 'module' => 'zip', + 'mime_type' => 'application/zip', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + ), + + + // Misc other formats + + // PAR2 - data - Parity Volume Set Specification 2.0 + 'par2' => array ( + 'pattern' => '^PAR2\x00PKT', + 'group' => 'misc', + 'module' => 'par2', + 'mime_type' => 'application/octet-stream', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + ), + + // PDF - data - Portable Document Format + 'pdf' => array( + 'pattern' => '^\x25PDF', + 'group' => 'misc', + 'module' => 'pdf', + 'mime_type' => 'application/pdf', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + ), + + // MSOFFICE - data - ZIP compressed data + 'msoffice' => array( + 'pattern' => '^\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1', // D0CF11E == DOCFILE == Microsoft Office Document + 'group' => 'misc', + 'module' => 'msoffice', + 'mime_type' => 'application/octet-stream', + 'fail_id3' => 'ERROR', + 'fail_ape' => 'ERROR', + ), + + // CUE - data - CUEsheet (index to single-file disc images) + 'cue' => array( + 'pattern' => '', // empty pattern means cannot be automatically detected, will fall through all other formats and match based on filename and very basic file contents + 'group' => 'misc', + 'module' => 'cue', + 'mime_type' => 'application/octet-stream', + ), + + ); + } + + return $format_info; + } + + + + public function GetFileFormat(&$filedata, $filename='') { + // this function will determine the format of a file based on usually + // the first 2-4 bytes of the file (8 bytes for PNG, 16 bytes for JPG, + // and in the case of ISO CD image, 6 bytes offset 32kb from the start + // of the file). + + // Identify file format - loop through $format_info and detect with reg expr + foreach ($this->GetFileFormatArray() as $format_name => $info) { + // The /s switch on preg_match() forces preg_match() NOT to treat + // newline (0x0A) characters as special chars but do a binary match + if (!empty($info['pattern']) && preg_match('#'.$info['pattern'].'#s', $filedata)) { + $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php'; + return $info; + } + } + + + if (preg_match('#\.mp[123a]$#i', $filename)) { + // Too many mp3 encoders on the market put gabage in front of mpeg files + // use assume format on these if format detection failed + $GetFileFormatArray = $this->GetFileFormatArray(); + $info = $GetFileFormatArray['mp3']; + $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php'; + return $info; + } elseif (preg_match('/\.cue$/i', $filename) && preg_match('#FILE "[^"]+" (BINARY|MOTOROLA|AIFF|WAVE|MP3)#', $filedata)) { + // there's not really a useful consistent "magic" at the beginning of .cue files to identify them + // so until I think of something better, just go by filename if all other format checks fail + // and verify there's at least one instance of "TRACK xx AUDIO" in the file + $GetFileFormatArray = $this->GetFileFormatArray(); + $info = $GetFileFormatArray['cue']; + $info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php'; + return $info; + } + + return false; + } + + + // converts array to $encoding charset from $this->encoding + public function CharConvert(&$array, $encoding) { + + // identical encoding - end here + if ($encoding == $this->encoding) { + return; + } + + // loop thru array + foreach ($array as $key => $value) { + + // go recursive + if (is_array($value)) { + $this->CharConvert($array[$key], $encoding); + } + + // convert string + elseif (is_string($value)) { + $array[$key] = trim(getid3_lib::iconv_fallback($encoding, $this->encoding, $value)); + } + } + } + + + public function HandleAllTags() { + + // key name => array (tag name, character encoding) + static $tags; + if (empty($tags)) { + $tags = array( + 'asf' => array('asf' , 'UTF-16LE'), + 'midi' => array('midi' , 'ISO-8859-1'), + 'nsv' => array('nsv' , 'ISO-8859-1'), + 'ogg' => array('vorbiscomment' , 'UTF-8'), + 'png' => array('png' , 'UTF-8'), + 'tiff' => array('tiff' , 'ISO-8859-1'), + 'quicktime' => array('quicktime' , 'UTF-8'), + 'real' => array('real' , 'ISO-8859-1'), + 'vqf' => array('vqf' , 'ISO-8859-1'), + 'zip' => array('zip' , 'ISO-8859-1'), + 'riff' => array('riff' , 'ISO-8859-1'), + 'lyrics3' => array('lyrics3' , 'ISO-8859-1'), + 'id3v1' => array('id3v1' , $this->encoding_id3v1), + 'id3v2' => array('id3v2' , 'UTF-8'), // not according to the specs (every frame can have a different encoding), but getID3() force-converts all encodings to UTF-8 + 'ape' => array('ape' , 'UTF-8'), + 'cue' => array('cue' , 'ISO-8859-1'), + 'matroska' => array('matroska' , 'UTF-8'), + 'flac' => array('vorbiscomment' , 'UTF-8'), + 'divxtag' => array('divx' , 'ISO-8859-1'), + 'iptc' => array('iptc' , 'ISO-8859-1'), + ); + } + + // loop through comments array + foreach ($tags as $comment_name => $tagname_encoding_array) { + list($tag_name, $encoding) = $tagname_encoding_array; + + // fill in default encoding type if not already present + if (isset($this->info[$comment_name]) && !isset($this->info[$comment_name]['encoding'])) { + $this->info[$comment_name]['encoding'] = $encoding; + } + + // copy comments if key name set + if (!empty($this->info[$comment_name]['comments'])) { + foreach ($this->info[$comment_name]['comments'] as $tag_key => $valuearray) { + foreach ($valuearray as $key => $value) { + if (is_string($value)) { + $value = trim($value, " \r\n\t"); // do not trim nulls from $value!! Unicode characters will get mangled if trailing nulls are removed! + } + if ($value) { + if (!is_numeric($key)) { + $this->info['tags'][trim($tag_name)][trim($tag_key)][$key] = $value; + } else { + $this->info['tags'][trim($tag_name)][trim($tag_key)][] = $value; + } + } + } + if ($tag_key == 'picture') { + unset($this->info[$comment_name]['comments'][$tag_key]); + } + } + + if (!isset($this->info['tags'][$tag_name])) { + // comments are set but contain nothing but empty strings, so skip + continue; + } + + if ($this->option_tags_html) { + foreach ($this->info['tags'][$tag_name] as $tag_key => $valuearray) { + $this->info['tags_html'][$tag_name][$tag_key] = getid3_lib::recursiveMultiByteCharString2HTML($valuearray, $encoding); + } + } + + $this->CharConvert($this->info['tags'][$tag_name], $encoding); // only copy gets converted! + } + + } + + // pictures can take up a lot of space, and we don't need multiple copies of them + // let there be a single copy in [comments][picture], and not elsewhere + if (!empty($this->info['tags'])) { + $unset_keys = array('tags', 'tags_html'); + foreach ($this->info['tags'] as $tagtype => $tagarray) { + foreach ($tagarray as $tagname => $tagdata) { + if ($tagname == 'picture') { + foreach ($tagdata as $key => $tagarray) { + $this->info['comments']['picture'][] = $tagarray; + if (isset($tagarray['data']) && isset($tagarray['image_mime'])) { + if (isset($this->info['tags'][$tagtype][$tagname][$key])) { + unset($this->info['tags'][$tagtype][$tagname][$key]); + } + if (isset($this->info['tags_html'][$tagtype][$tagname][$key])) { + unset($this->info['tags_html'][$tagtype][$tagname][$key]); + } + } + } + } + } + foreach ($unset_keys as $unset_key) { + // remove possible empty keys from (e.g. [tags][id3v2][picture]) + if (empty($this->info[$unset_key][$tagtype]['picture'])) { + unset($this->info[$unset_key][$tagtype]['picture']); + } + if (empty($this->info[$unset_key][$tagtype])) { + unset($this->info[$unset_key][$tagtype]); + } + if (empty($this->info[$unset_key])) { + unset($this->info[$unset_key]); + } + } + // remove duplicate copy of picture data from (e.g. [id3v2][comments][picture]) + if (isset($this->info[$tagtype]['comments']['picture'])) { + unset($this->info[$tagtype]['comments']['picture']); + } + if (empty($this->info[$tagtype]['comments'])) { + unset($this->info[$tagtype]['comments']); + } + if (empty($this->info[$tagtype])) { + unset($this->info[$tagtype]); + } + } + } + return true; + } + + public function getHashdata($algorithm) { + switch ($algorithm) { + case 'md5': + case 'sha1': + break; + + default: + return $this->error('bad algorithm "'.$algorithm.'" in getHashdata()'); + break; + } + + if (!empty($this->info['fileformat']) && !empty($this->info['dataformat']) && ($this->info['fileformat'] == 'ogg') && ($this->info['audio']['dataformat'] == 'vorbis')) { + + // We cannot get an identical md5_data value for Ogg files where the comments + // span more than 1 Ogg page (compared to the same audio data with smaller + // comments) using the normal getID3() method of MD5'ing the data between the + // end of the comments and the end of the file (minus any trailing tags), + // because the page sequence numbers of the pages that the audio data is on + // do not match. Under normal circumstances, where comments are smaller than + // the nominal 4-8kB page size, then this is not a problem, but if there are + // very large comments, the only way around it is to strip off the comment + // tags with vorbiscomment and MD5 that file. + // This procedure must be applied to ALL Ogg files, not just the ones with + // comments larger than 1 page, because the below method simply MD5's the + // whole file with the comments stripped, not just the portion after the + // comments block (which is the standard getID3() method. + + // The above-mentioned problem of comments spanning multiple pages and changing + // page sequence numbers likely happens for OggSpeex and OggFLAC as well, but + // currently vorbiscomment only works on OggVorbis files. + + if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) { + + $this->warning('Failed making system call to vorbiscomment.exe - '.$algorithm.'_data is incorrect - error returned: PHP running in Safe Mode (backtick operator not available)'); + $this->info[$algorithm.'_data'] = false; + + } else { + + // Prevent user from aborting script + $old_abort = ignore_user_abort(true); + + // Create empty file + $empty = tempnam(GETID3_TEMP_DIR, 'getID3'); + touch($empty); + + // Use vorbiscomment to make temp file without comments + $temp = tempnam(GETID3_TEMP_DIR, 'getID3'); + $file = $this->info['filenamepath']; + + if (GETID3_OS_ISWINDOWS) { + + if (file_exists(GETID3_HELPERAPPSDIR.'vorbiscomment.exe')) { + + $commandline = '"'.GETID3_HELPERAPPSDIR.'vorbiscomment.exe" -w -c "'.$empty.'" "'.$file.'" "'.$temp.'"'; + $VorbisCommentError = `$commandline`; + + } else { + + $VorbisCommentError = 'vorbiscomment.exe not found in '.GETID3_HELPERAPPSDIR; + + } + + } else { + + $commandline = 'vorbiscomment -w -c "'.$empty.'" "'.$file.'" "'.$temp.'" 2>&1'; + $commandline = 'vorbiscomment -w -c '.escapeshellarg($empty).' '.escapeshellarg($file).' '.escapeshellarg($temp).' 2>&1'; + $VorbisCommentError = `$commandline`; + + } + + if (!empty($VorbisCommentError)) { + + $this->info['warning'][] = 'Failed making system call to vorbiscomment(.exe) - '.$algorithm.'_data will be incorrect. If vorbiscomment is unavailable, please download from http://www.vorbis.com/download.psp and put in the getID3() directory. Error returned: '.$VorbisCommentError; + $this->info[$algorithm.'_data'] = false; + + } else { + + // Get hash of newly created file + switch ($algorithm) { + case 'md5': + $this->info[$algorithm.'_data'] = md5_file($temp); + break; + + case 'sha1': + $this->info[$algorithm.'_data'] = sha1_file($temp); + break; + } + } + + // Clean up + unlink($empty); + unlink($temp); + + // Reset abort setting + ignore_user_abort($old_abort); + + } + + } else { + + if (!empty($this->info['avdataoffset']) || (isset($this->info['avdataend']) && ($this->info['avdataend'] < $this->info['filesize']))) { + + // get hash from part of file + $this->info[$algorithm.'_data'] = getid3_lib::hash_data($this->info['filenamepath'], $this->info['avdataoffset'], $this->info['avdataend'], $algorithm); + + } else { + + // get hash from whole file + switch ($algorithm) { + case 'md5': + $this->info[$algorithm.'_data'] = md5_file($this->info['filenamepath']); + break; + + case 'sha1': + $this->info[$algorithm.'_data'] = sha1_file($this->info['filenamepath']); + break; + } + } + + } + return true; + } + + + public function ChannelsBitratePlaytimeCalculations() { + + // set channelmode on audio + if (!empty($this->info['audio']['channelmode']) || !isset($this->info['audio']['channels'])) { + // ignore + } elseif ($this->info['audio']['channels'] == 1) { + $this->info['audio']['channelmode'] = 'mono'; + } elseif ($this->info['audio']['channels'] == 2) { + $this->info['audio']['channelmode'] = 'stereo'; + } + + // Calculate combined bitrate - audio + video + $CombinedBitrate = 0; + $CombinedBitrate += (isset($this->info['audio']['bitrate']) ? $this->info['audio']['bitrate'] : 0); + $CombinedBitrate += (isset($this->info['video']['bitrate']) ? $this->info['video']['bitrate'] : 0); + if (($CombinedBitrate > 0) && empty($this->info['bitrate'])) { + $this->info['bitrate'] = $CombinedBitrate; + } + //if ((isset($this->info['video']) && !isset($this->info['video']['bitrate'])) || (isset($this->info['audio']) && !isset($this->info['audio']['bitrate']))) { + // // for example, VBR MPEG video files cannot determine video bitrate: + // // should not set overall bitrate and playtime from audio bitrate only + // unset($this->info['bitrate']); + //} + + // video bitrate undetermined, but calculable + if (isset($this->info['video']['dataformat']) && $this->info['video']['dataformat'] && (!isset($this->info['video']['bitrate']) || ($this->info['video']['bitrate'] == 0))) { + // if video bitrate not set + if (isset($this->info['audio']['bitrate']) && ($this->info['audio']['bitrate'] > 0) && ($this->info['audio']['bitrate'] == $this->info['bitrate'])) { + // AND if audio bitrate is set to same as overall bitrate + if (isset($this->info['playtime_seconds']) && ($this->info['playtime_seconds'] > 0)) { + // AND if playtime is set + if (isset($this->info['avdataend']) && isset($this->info['avdataoffset'])) { + // AND if AV data offset start/end is known + // THEN we can calculate the video bitrate + $this->info['bitrate'] = round((($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['playtime_seconds']); + $this->info['video']['bitrate'] = $this->info['bitrate'] - $this->info['audio']['bitrate']; + } + } + } + } + + if ((!isset($this->info['playtime_seconds']) || ($this->info['playtime_seconds'] <= 0)) && !empty($this->info['bitrate'])) { + $this->info['playtime_seconds'] = (($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['bitrate']; + } + + if (!isset($this->info['bitrate']) && !empty($this->info['playtime_seconds'])) { + $this->info['bitrate'] = (($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['playtime_seconds']; + } + if (isset($this->info['bitrate']) && empty($this->info['audio']['bitrate']) && empty($this->info['video']['bitrate'])) { + if (isset($this->info['audio']['dataformat']) && empty($this->info['video']['resolution_x'])) { + // audio only + $this->info['audio']['bitrate'] = $this->info['bitrate']; + } elseif (isset($this->info['video']['resolution_x']) && empty($this->info['audio']['dataformat'])) { + // video only + $this->info['video']['bitrate'] = $this->info['bitrate']; + } + } + + // Set playtime string + if (!empty($this->info['playtime_seconds']) && empty($this->info['playtime_string'])) { + $this->info['playtime_string'] = getid3_lib::PlaytimeString($this->info['playtime_seconds']); + } + } + + + public function CalculateCompressionRatioVideo() { + if (empty($this->info['video'])) { + return false; + } + if (empty($this->info['video']['resolution_x']) || empty($this->info['video']['resolution_y'])) { + return false; + } + if (empty($this->info['video']['bits_per_sample'])) { + return false; + } + + switch ($this->info['video']['dataformat']) { + case 'bmp': + case 'gif': + case 'jpeg': + case 'jpg': + case 'png': + case 'tiff': + $FrameRate = 1; + $PlaytimeSeconds = 1; + $BitrateCompressed = $this->info['filesize'] * 8; + break; + + default: + if (!empty($this->info['video']['frame_rate'])) { + $FrameRate = $this->info['video']['frame_rate']; + } else { + return false; + } + if (!empty($this->info['playtime_seconds'])) { + $PlaytimeSeconds = $this->info['playtime_seconds']; + } else { + return false; + } + if (!empty($this->info['video']['bitrate'])) { + $BitrateCompressed = $this->info['video']['bitrate']; + } else { + return false; + } + break; + } + $BitrateUncompressed = $this->info['video']['resolution_x'] * $this->info['video']['resolution_y'] * $this->info['video']['bits_per_sample'] * $FrameRate; + + $this->info['video']['compression_ratio'] = $BitrateCompressed / $BitrateUncompressed; + return true; + } + + + public function CalculateCompressionRatioAudio() { + if (empty($this->info['audio']['bitrate']) || empty($this->info['audio']['channels']) || empty($this->info['audio']['sample_rate']) || !is_numeric($this->info['audio']['sample_rate'])) { + return false; + } + $this->info['audio']['compression_ratio'] = $this->info['audio']['bitrate'] / ($this->info['audio']['channels'] * $this->info['audio']['sample_rate'] * (!empty($this->info['audio']['bits_per_sample']) ? $this->info['audio']['bits_per_sample'] : 16)); + + if (!empty($this->info['audio']['streams'])) { + foreach ($this->info['audio']['streams'] as $streamnumber => $streamdata) { + if (!empty($streamdata['bitrate']) && !empty($streamdata['channels']) && !empty($streamdata['sample_rate'])) { + $this->info['audio']['streams'][$streamnumber]['compression_ratio'] = $streamdata['bitrate'] / ($streamdata['channels'] * $streamdata['sample_rate'] * (!empty($streamdata['bits_per_sample']) ? $streamdata['bits_per_sample'] : 16)); + } + } + } + return true; + } + + + public function CalculateReplayGain() { + if (isset($this->info['replay_gain'])) { + if (!isset($this->info['replay_gain']['reference_volume'])) { + $this->info['replay_gain']['reference_volume'] = (double) 89.0; + } + if (isset($this->info['replay_gain']['track']['adjustment'])) { + $this->info['replay_gain']['track']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['track']['adjustment']; + } + if (isset($this->info['replay_gain']['album']['adjustment'])) { + $this->info['replay_gain']['album']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['album']['adjustment']; + } + + if (isset($this->info['replay_gain']['track']['peak'])) { + $this->info['replay_gain']['track']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['track']['peak']); + } + if (isset($this->info['replay_gain']['album']['peak'])) { + $this->info['replay_gain']['album']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['album']['peak']); + } + } + return true; + } + + public function ProcessAudioStreams() { + if (!empty($this->info['audio']['bitrate']) || !empty($this->info['audio']['channels']) || !empty($this->info['audio']['sample_rate'])) { + if (!isset($this->info['audio']['streams'])) { + foreach ($this->info['audio'] as $key => $value) { + if ($key != 'streams') { + $this->info['audio']['streams'][0][$key] = $value; + } + } + } + } + return true; + } + + public function getid3_tempnam() { + return tempnam($this->tempdir, 'gI3'); + } + + public function include_module($name) { + //if (!file_exists($this->include_path.'module.'.$name.'.php')) { + if (!file_exists(GETID3_INCLUDEPATH.'module.'.$name.'.php')) { + throw new getid3_exception('Required module.'.$name.'.php is missing.'); + } + include_once(GETID3_INCLUDEPATH.'module.'.$name.'.php'); + return true; + } + +} + + +abstract class getid3_handler { + + /** + * @var getID3 + */ + protected $getid3; // pointer + + protected $data_string_flag = false; // analyzing filepointer or string + protected $data_string = ''; // string to analyze + protected $data_string_position = 0; // seek position in string + protected $data_string_length = 0; // string length + + private $dependency_to = null; + + + public function __construct(getID3 $getid3, $call_module=null) { + $this->getid3 = $getid3; + + if ($call_module) { + $this->dependency_to = str_replace('getid3_', '', $call_module); + } + } + + + // Analyze from file pointer + abstract public function Analyze(); + + + // Analyze from string instead + public function AnalyzeString($string) { + // Enter string mode + $this->setStringMode($string); + + // Save info + $saved_avdataoffset = $this->getid3->info['avdataoffset']; + $saved_avdataend = $this->getid3->info['avdataend']; + $saved_filesize = (isset($this->getid3->info['filesize']) ? $this->getid3->info['filesize'] : null); // may be not set if called as dependency without openfile() call + + // Reset some info + $this->getid3->info['avdataoffset'] = 0; + $this->getid3->info['avdataend'] = $this->getid3->info['filesize'] = $this->data_string_length; + + // Analyze + $this->Analyze(); + + // Restore some info + $this->getid3->info['avdataoffset'] = $saved_avdataoffset; + $this->getid3->info['avdataend'] = $saved_avdataend; + $this->getid3->info['filesize'] = $saved_filesize; + + // Exit string mode + $this->data_string_flag = false; + } + + public function setStringMode($string) { + $this->data_string_flag = true; + $this->data_string = $string; + $this->data_string_length = strlen($string); + } + + protected function ftell() { + if ($this->data_string_flag) { + return $this->data_string_position; + } + return ftell($this->getid3->fp); + } + + protected function fread($bytes) { + if ($this->data_string_flag) { + $this->data_string_position += $bytes; + return substr($this->data_string, $this->data_string_position - $bytes, $bytes); + } + $pos = $this->ftell() + $bytes; + if (!getid3_lib::intValueSupported($pos)) { + throw new getid3_exception('cannot fread('.$bytes.' from '.$this->ftell().') because beyond PHP filesystem limit', 10); + } + return fread($this->getid3->fp, $bytes); + } + + protected function fseek($bytes, $whence=SEEK_SET) { + if ($this->data_string_flag) { + switch ($whence) { + case SEEK_SET: + $this->data_string_position = $bytes; + break; + + case SEEK_CUR: + $this->data_string_position += $bytes; + break; + + case SEEK_END: + $this->data_string_position = $this->data_string_length + $bytes; + break; + } + return 0; + } else { + $pos = $bytes; + if ($whence == SEEK_CUR) { + $pos = $this->ftell() + $bytes; + } elseif ($whence == SEEK_END) { + $pos = $this->getid3->info['filesize'] + $bytes; + } + if (!getid3_lib::intValueSupported($pos)) { + throw new getid3_exception('cannot fseek('.$pos.') because beyond PHP filesystem limit', 10); + } + } + return fseek($this->getid3->fp, $bytes, $whence); + } + + protected function feof() { + if ($this->data_string_flag) { + return $this->data_string_position >= $this->data_string_length; + } + return feof($this->getid3->fp); + } + + final protected function isDependencyFor($module) { + return $this->dependency_to == $module; + } + + protected function error($text) { + $this->getid3->info['error'][] = $text; + + return false; + } + + protected function warning($text) { + return $this->getid3->warning($text); + } + + protected function notice($text) { + // does nothing for now + } + + public function saveAttachment($name, $offset, $length, $image_mime=null) { + try { + + // do not extract at all + if ($this->getid3->option_save_attachments === getID3::ATTACHMENTS_NONE) { + + $attachment = null; // do not set any + + // extract to return array + } elseif ($this->getid3->option_save_attachments === getID3::ATTACHMENTS_INLINE) { + + $this->fseek($offset); + $attachment = $this->fread($length); // get whole data in one pass, till it is anyway stored in memory + if ($attachment === false || strlen($attachment) != $length) { + throw new Exception('failed to read attachment data'); + } + + // assume directory path is given + } else { + + // set up destination path + $dir = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->getid3->option_save_attachments), DIRECTORY_SEPARATOR); + if (!is_dir($dir) || !is_writable($dir)) { // check supplied directory + throw new Exception('supplied path ('.$dir.') does not exist, or is not writable'); + } + $dest = $dir.DIRECTORY_SEPARATOR.$name.($image_mime ? '.'.getid3_lib::ImageExtFromMime($image_mime) : ''); + + // create dest file + if (($fp_dest = fopen($dest, 'wb')) == false) { + throw new Exception('failed to create file '.$dest); + } + + // copy data + $this->fseek($offset); + $buffersize = ($this->data_string_flag ? $length : $this->getid3->fread_buffer_size()); + $bytesleft = $length; + while ($bytesleft > 0) { + if (($buffer = $this->fread(min($buffersize, $bytesleft))) === false || ($byteswritten = fwrite($fp_dest, $buffer)) === false || ($byteswritten === 0)) { + throw new Exception($buffer === false ? 'not enough data to read' : 'failed to write to destination file, may be not enough disk space'); + } + $bytesleft -= $byteswritten; + } + + fclose($fp_dest); + $attachment = $dest; + + } + + } catch (Exception $e) { + + // close and remove dest file if created + if (isset($fp_dest) && is_resource($fp_dest)) { + fclose($fp_dest); + unlink($dest); + } + + // do not set any is case of error + $attachment = null; + $this->warning('Failed to extract attachment '.$name.': '.$e->getMessage()); + + } + + // seek to the end of attachment + $this->fseek($offset + $length); + + return $attachment; + } + +} + + +class getid3_exception extends Exception +{ + public $message; +} \ No newline at end of file diff --git a/wp-includes/ID3/license.commercial.txt b/wp-includes/ID3/license.commercial.txt new file mode 100644 index 0000000..bc7861b --- /dev/null +++ b/wp-includes/ID3/license.commercial.txt @@ -0,0 +1,27 @@ + getID3() Commercial License + =========================== + +getID3() is licensed under the "GNU Public License" (GPL) and/or the +"getID3() Commercial License" (gCL). This document describes the gCL. + +--------------------------------------------------------------------- + +The license is non-exclusively granted to a single person or company, +per payment of the license fee, for the lifetime of that person or +company. The license is non-transferrable. + +The gCL grants the licensee the right to use getID3() in commercial +closed-source projects. Modifications may be made to getID3() with no +obligation to release the modified source code. getID3() (or pieces +thereof) may be included in any number of projects authored (in whole +or in part) by the licensee. + +The licensee may use any version of getID3(), past, present or future, +as is most convenient. This license does not entitle the licensee to +receive any technical support, updates or bugfixes, except as such are +made publicly available to all getID3() users. + +The licensee may not sub-license getID3() itself, meaning that any +commercially released product containing all or parts of getID3() must +have added functionality beyond what is available in getID3(); +getID3() itself may not be re-licensed by the licensee. \ No newline at end of file diff --git a/wp-includes/ID3/license.txt b/wp-includes/ID3/license.txt new file mode 100644 index 0000000..32d8ec7 --- /dev/null +++ b/wp-includes/ID3/license.txt @@ -0,0 +1,29 @@ +///////////////////////////////////////////////////////////////// +/// getID3() by James Heinrich // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +// also https://github.com/JamesHeinrich/getID3 // +///////////////////////////////////////////////////////////////// + +***************************************************************** +***************************************************************** + + getID3() is released under multiple licenses. You may choose + from the following licenses, and use getID3 according to the + terms of the license most suitable to your project. + +GNU GPL: https://gnu.org/licenses/gpl.html (v3) + https://gnu.org/licenses/old-licenses/gpl-2.0.html (v2) + https://gnu.org/licenses/old-licenses/gpl-1.0.html (v1) + +GNU LGPL: https://gnu.org/licenses/lgpl.html (v3) + +Mozilla MPL: http://www.mozilla.org/MPL/2.0/ (v2) + +getID3 Commercial License: http://getid3.org/#gCL (payment required) + +***************************************************************** +***************************************************************** + +Copies of each of the above licenses are included in the 'licenses' +directory of the getID3 distribution. \ No newline at end of file diff --git a/wp-includes/ID3/module.audio-video.asf.php b/wp-includes/ID3/module.audio-video.asf.php new file mode 100644 index 0000000..884e2ff --- /dev/null +++ b/wp-includes/ID3/module.audio-video.asf.php @@ -0,0 +1,2013 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +// also https://github.com/JamesHeinrich/getID3 // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio-video.asf.php // +// module for analyzing ASF, WMA and WMV files // +// dependencies: module.audio-video.riff.php // +// /// +///////////////////////////////////////////////////////////////// + +getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); + +class getid3_asf extends getid3_handler { + + public function __construct(getID3 $getid3) { + parent::__construct($getid3); // extends getid3_handler::__construct() + + // initialize all GUID constants + $GUIDarray = $this->KnownGUIDs(); + foreach ($GUIDarray as $GUIDname => $hexstringvalue) { + if (!defined($GUIDname)) { + define($GUIDname, $this->GUIDtoBytestring($hexstringvalue)); + } + } + } + + public function Analyze() { + $info = &$this->getid3->info; + + // Shortcuts + $thisfile_audio = &$info['audio']; + $thisfile_video = &$info['video']; + $info['asf'] = array(); + $thisfile_asf = &$info['asf']; + $thisfile_asf['comments'] = array(); + $thisfile_asf_comments = &$thisfile_asf['comments']; + $thisfile_asf['header_object'] = array(); + $thisfile_asf_headerobject = &$thisfile_asf['header_object']; + + + // ASF structure: + // * Header Object [required] + // * File Properties Object [required] (global file attributes) + // * Stream Properties Object [required] (defines media stream & characteristics) + // * Header Extension Object [required] (additional functionality) + // * Content Description Object (bibliographic information) + // * Script Command Object (commands for during playback) + // * Marker Object (named jumped points within the file) + // * Data Object [required] + // * Data Packets + // * Index Object + + // Header Object: (mandatory, one only) + // Field Name Field Type Size (bits) + // Object ID GUID 128 // GUID for header object - GETID3_ASF_Header_Object + // Object Size QWORD 64 // size of header object, including 30 bytes of Header Object header + // Number of Header Objects DWORD 32 // number of objects in header object + // Reserved1 BYTE 8 // hardcoded: 0x01 + // Reserved2 BYTE 8 // hardcoded: 0x02 + + $info['fileformat'] = 'asf'; + + $this->fseek($info['avdataoffset']); + $HeaderObjectData = $this->fread(30); + + $thisfile_asf_headerobject['objectid'] = substr($HeaderObjectData, 0, 16); + $thisfile_asf_headerobject['objectid_guid'] = $this->BytestringToGUID($thisfile_asf_headerobject['objectid']); + if ($thisfile_asf_headerobject['objectid'] != GETID3_ASF_Header_Object) { + unset($info['fileformat'], $info['asf']); + return $this->error('ASF header GUID {'.$this->BytestringToGUID($thisfile_asf_headerobject['objectid']).'} does not match expected "GETID3_ASF_Header_Object" GUID {'.$this->BytestringToGUID(GETID3_ASF_Header_Object).'}'); + } + $thisfile_asf_headerobject['objectsize'] = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 16, 8)); + $thisfile_asf_headerobject['headerobjects'] = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 24, 4)); + $thisfile_asf_headerobject['reserved1'] = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 28, 1)); + $thisfile_asf_headerobject['reserved2'] = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 29, 1)); + + $NextObjectOffset = $this->ftell(); + $ASFHeaderData = $this->fread($thisfile_asf_headerobject['objectsize'] - 30); + $offset = 0; + + for ($HeaderObjectsCounter = 0; $HeaderObjectsCounter < $thisfile_asf_headerobject['headerobjects']; $HeaderObjectsCounter++) { + $NextObjectGUID = substr($ASFHeaderData, $offset, 16); + $offset += 16; + $NextObjectGUIDtext = $this->BytestringToGUID($NextObjectGUID); + $NextObjectSize = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8)); + $offset += 8; + switch ($NextObjectGUID) { + + case GETID3_ASF_File_Properties_Object: + // File Properties Object: (mandatory, one only) + // Field Name Field Type Size (bits) + // Object ID GUID 128 // GUID for file properties object - GETID3_ASF_File_Properties_Object + // Object Size QWORD 64 // size of file properties object, including 104 bytes of File Properties Object header + // File ID GUID 128 // unique ID - identical to File ID in Data Object + // File Size QWORD 64 // entire file in bytes. Invalid if Broadcast Flag == 1 + // Creation Date QWORD 64 // date & time of file creation. Maybe invalid if Broadcast Flag == 1 + // Data Packets Count QWORD 64 // number of data packets in Data Object. Invalid if Broadcast Flag == 1 + // Play Duration QWORD 64 // playtime, in 100-nanosecond units. Invalid if Broadcast Flag == 1 + // Send Duration QWORD 64 // time needed to send file, in 100-nanosecond units. Players can ignore this value. Invalid if Broadcast Flag == 1 + // Preroll QWORD 64 // time to buffer data before starting to play file, in 1-millisecond units. If <> 0, PlayDuration and PresentationTime have been offset by this amount + // Flags DWORD 32 // + // * Broadcast Flag bits 1 (0x01) // file is currently being written, some header values are invalid + // * Seekable Flag bits 1 (0x02) // is file seekable + // * Reserved bits 30 (0xFFFFFFFC) // reserved - set to zero + // Minimum Data Packet Size DWORD 32 // in bytes. should be same as Maximum Data Packet Size. Invalid if Broadcast Flag == 1 + // Maximum Data Packet Size DWORD 32 // in bytes. should be same as Minimum Data Packet Size. Invalid if Broadcast Flag == 1 + // Maximum Bitrate DWORD 32 // maximum instantaneous bitrate in bits per second for entire file, including all data streams and ASF overhead + + // shortcut + $thisfile_asf['file_properties_object'] = array(); + $thisfile_asf_filepropertiesobject = &$thisfile_asf['file_properties_object']; + + $thisfile_asf_filepropertiesobject['offset'] = $NextObjectOffset + $offset; + $thisfile_asf_filepropertiesobject['objectid'] = $NextObjectGUID; + $thisfile_asf_filepropertiesobject['objectid_guid'] = $NextObjectGUIDtext; + $thisfile_asf_filepropertiesobject['objectsize'] = $NextObjectSize; + $thisfile_asf_filepropertiesobject['fileid'] = substr($ASFHeaderData, $offset, 16); + $offset += 16; + $thisfile_asf_filepropertiesobject['fileid_guid'] = $this->BytestringToGUID($thisfile_asf_filepropertiesobject['fileid']); + $thisfile_asf_filepropertiesobject['filesize'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8)); + $offset += 8; + $thisfile_asf_filepropertiesobject['creation_date'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8)); + $thisfile_asf_filepropertiesobject['creation_date_unix'] = $this->FILETIMEtoUNIXtime($thisfile_asf_filepropertiesobject['creation_date']); + $offset += 8; + $thisfile_asf_filepropertiesobject['data_packets'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8)); + $offset += 8; + $thisfile_asf_filepropertiesobject['play_duration'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8)); + $offset += 8; + $thisfile_asf_filepropertiesobject['send_duration'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8)); + $offset += 8; + $thisfile_asf_filepropertiesobject['preroll'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8)); + $offset += 8; + $thisfile_asf_filepropertiesobject['flags_raw'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); + $offset += 4; + $thisfile_asf_filepropertiesobject['flags']['broadcast'] = (bool) ($thisfile_asf_filepropertiesobject['flags_raw'] & 0x0001); + $thisfile_asf_filepropertiesobject['flags']['seekable'] = (bool) ($thisfile_asf_filepropertiesobject['flags_raw'] & 0x0002); + + $thisfile_asf_filepropertiesobject['min_packet_size'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); + $offset += 4; + $thisfile_asf_filepropertiesobject['max_packet_size'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); + $offset += 4; + $thisfile_asf_filepropertiesobject['max_bitrate'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); + $offset += 4; + + if ($thisfile_asf_filepropertiesobject['flags']['broadcast']) { + + // broadcast flag is set, some values invalid + unset($thisfile_asf_filepropertiesobject['filesize']); + unset($thisfile_asf_filepropertiesobject['data_packets']); + unset($thisfile_asf_filepropertiesobject['play_duration']); + unset($thisfile_asf_filepropertiesobject['send_duration']); + unset($thisfile_asf_filepropertiesobject['min_packet_size']); + unset($thisfile_asf_filepropertiesobject['max_packet_size']); + + } else { + + // broadcast flag NOT set, perform calculations + $info['playtime_seconds'] = ($thisfile_asf_filepropertiesobject['play_duration'] / 10000000) - ($thisfile_asf_filepropertiesobject['preroll'] / 1000); + + //$info['bitrate'] = $thisfile_asf_filepropertiesobject['max_bitrate']; + $info['bitrate'] = ((isset($thisfile_asf_filepropertiesobject['filesize']) ? $thisfile_asf_filepropertiesobject['filesize'] : $info['filesize']) * 8) / $info['playtime_seconds']; + } + break; + + case GETID3_ASF_Stream_Properties_Object: + // Stream Properties Object: (mandatory, one per media stream) + // Field Name Field Type Size (bits) + // Object ID GUID 128 // GUID for stream properties object - GETID3_ASF_Stream_Properties_Object + // Object Size QWORD 64 // size of stream properties object, including 78 bytes of Stream Properties Object header + // Stream Type GUID 128 // GETID3_ASF_Audio_Media, GETID3_ASF_Video_Media or GETID3_ASF_Command_Media + // Error Correction Type GUID 128 // GETID3_ASF_Audio_Spread for audio-only streams, GETID3_ASF_No_Error_Correction for other stream types + // Time Offset QWORD 64 // 100-nanosecond units. typically zero. added to all timestamps of samples in the stream + // Type-Specific Data Length DWORD 32 // number of bytes for Type-Specific Data field + // Error Correction Data Length DWORD 32 // number of bytes for Error Correction Data field + // Flags WORD 16 // + // * Stream Number bits 7 (0x007F) // number of this stream. 1 <= valid <= 127 + // * Reserved bits 8 (0x7F80) // reserved - set to zero + // * Encrypted Content Flag bits 1 (0x8000) // stream contents encrypted if set + // Reserved DWORD 32 // reserved - set to zero + // Type-Specific Data BYTESTREAM variable // type-specific format data, depending on value of Stream Type + // Error Correction Data BYTESTREAM variable // error-correction-specific format data, depending on value of Error Correct Type + + // There is one GETID3_ASF_Stream_Properties_Object for each stream (audio, video) but the + // stream number isn't known until halfway through decoding the structure, hence it + // it is decoded to a temporary variable and then stuck in the appropriate index later + + $StreamPropertiesObjectData['offset'] = $NextObjectOffset + $offset; + $StreamPropertiesObjectData['objectid'] = $NextObjectGUID; + $StreamPropertiesObjectData['objectid_guid'] = $NextObjectGUIDtext; + $StreamPropertiesObjectData['objectsize'] = $NextObjectSize; + $StreamPropertiesObjectData['stream_type'] = substr($ASFHeaderData, $offset, 16); + $offset += 16; + $StreamPropertiesObjectData['stream_type_guid'] = $this->BytestringToGUID($StreamPropertiesObjectData['stream_type']); + $StreamPropertiesObjectData['error_correct_type'] = substr($ASFHeaderData, $offset, 16); + $offset += 16; + $StreamPropertiesObjectData['error_correct_guid'] = $this->BytestringToGUID($StreamPropertiesObjectData['error_correct_type']); + $StreamPropertiesObjectData['time_offset'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8)); + $offset += 8; + $StreamPropertiesObjectData['type_data_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); + $offset += 4; + $StreamPropertiesObjectData['error_data_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); + $offset += 4; + $StreamPropertiesObjectData['flags_raw'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $StreamPropertiesObjectStreamNumber = $StreamPropertiesObjectData['flags_raw'] & 0x007F; + $StreamPropertiesObjectData['flags']['encrypted'] = (bool) ($StreamPropertiesObjectData['flags_raw'] & 0x8000); + + $offset += 4; // reserved - DWORD + $StreamPropertiesObjectData['type_specific_data'] = substr($ASFHeaderData, $offset, $StreamPropertiesObjectData['type_data_length']); + $offset += $StreamPropertiesObjectData['type_data_length']; + $StreamPropertiesObjectData['error_correct_data'] = substr($ASFHeaderData, $offset, $StreamPropertiesObjectData['error_data_length']); + $offset += $StreamPropertiesObjectData['error_data_length']; + + switch ($StreamPropertiesObjectData['stream_type']) { + + case GETID3_ASF_Audio_Media: + $thisfile_audio['dataformat'] = (!empty($thisfile_audio['dataformat']) ? $thisfile_audio['dataformat'] : 'asf'); + $thisfile_audio['bitrate_mode'] = (!empty($thisfile_audio['bitrate_mode']) ? $thisfile_audio['bitrate_mode'] : 'cbr'); + + $audiodata = getid3_riff::parseWAVEFORMATex(substr($StreamPropertiesObjectData['type_specific_data'], 0, 16)); + unset($audiodata['raw']); + $thisfile_audio = getid3_lib::array_merge_noclobber($audiodata, $thisfile_audio); + break; + + case GETID3_ASF_Video_Media: + $thisfile_video['dataformat'] = (!empty($thisfile_video['dataformat']) ? $thisfile_video['dataformat'] : 'asf'); + $thisfile_video['bitrate_mode'] = (!empty($thisfile_video['bitrate_mode']) ? $thisfile_video['bitrate_mode'] : 'cbr'); + break; + + case GETID3_ASF_Command_Media: + default: + // do nothing + break; + + } + + $thisfile_asf['stream_properties_object'][$StreamPropertiesObjectStreamNumber] = $StreamPropertiesObjectData; + unset($StreamPropertiesObjectData); // clear for next stream, if any + break; + + case GETID3_ASF_Header_Extension_Object: + // Header Extension Object: (mandatory, one only) + // Field Name Field Type Size (bits) + // Object ID GUID 128 // GUID for Header Extension object - GETID3_ASF_Header_Extension_Object + // Object Size QWORD 64 // size of Header Extension object, including 46 bytes of Header Extension Object header + // Reserved Field 1 GUID 128 // hardcoded: GETID3_ASF_Reserved_1 + // Reserved Field 2 WORD 16 // hardcoded: 0x00000006 + // Header Extension Data Size DWORD 32 // in bytes. valid: 0, or > 24. equals object size minus 46 + // Header Extension Data BYTESTREAM variable // array of zero or more extended header objects + + // shortcut + $thisfile_asf['header_extension_object'] = array(); + $thisfile_asf_headerextensionobject = &$thisfile_asf['header_extension_object']; + + $thisfile_asf_headerextensionobject['offset'] = $NextObjectOffset + $offset; + $thisfile_asf_headerextensionobject['objectid'] = $NextObjectGUID; + $thisfile_asf_headerextensionobject['objectid_guid'] = $NextObjectGUIDtext; + $thisfile_asf_headerextensionobject['objectsize'] = $NextObjectSize; + $thisfile_asf_headerextensionobject['reserved_1'] = substr($ASFHeaderData, $offset, 16); + $offset += 16; + $thisfile_asf_headerextensionobject['reserved_1_guid'] = $this->BytestringToGUID($thisfile_asf_headerextensionobject['reserved_1']); + if ($thisfile_asf_headerextensionobject['reserved_1'] != GETID3_ASF_Reserved_1) { + $info['warning'][] = 'header_extension_object.reserved_1 GUID ('.$this->BytestringToGUID($thisfile_asf_headerextensionobject['reserved_1']).') does not match expected "GETID3_ASF_Reserved_1" GUID ('.$this->BytestringToGUID(GETID3_ASF_Reserved_1).')'; + //return false; + break; + } + $thisfile_asf_headerextensionobject['reserved_2'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + if ($thisfile_asf_headerextensionobject['reserved_2'] != 6) { + $info['warning'][] = 'header_extension_object.reserved_2 ('.getid3_lib::PrintHexBytes($thisfile_asf_headerextensionobject['reserved_2']).') does not match expected value of "6"'; + //return false; + break; + } + $thisfile_asf_headerextensionobject['extension_data_size'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); + $offset += 4; + $thisfile_asf_headerextensionobject['extension_data'] = substr($ASFHeaderData, $offset, $thisfile_asf_headerextensionobject['extension_data_size']); + $unhandled_sections = 0; + $thisfile_asf_headerextensionobject['extension_data_parsed'] = $this->HeaderExtensionObjectDataParse($thisfile_asf_headerextensionobject['extension_data'], $unhandled_sections); + if ($unhandled_sections === 0) { + unset($thisfile_asf_headerextensionobject['extension_data']); + } + $offset += $thisfile_asf_headerextensionobject['extension_data_size']; + break; + + case GETID3_ASF_Codec_List_Object: + // Codec List Object: (optional, one only) + // Field Name Field Type Size (bits) + // Object ID GUID 128 // GUID for Codec List object - GETID3_ASF_Codec_List_Object + // Object Size QWORD 64 // size of Codec List object, including 44 bytes of Codec List Object header + // Reserved GUID 128 // hardcoded: 86D15241-311D-11D0-A3A4-00A0C90348F6 + // Codec Entries Count DWORD 32 // number of entries in Codec Entries array + // Codec Entries array of: variable // + // * Type WORD 16 // 0x0001 = Video Codec, 0x0002 = Audio Codec, 0xFFFF = Unknown Codec + // * Codec Name Length WORD 16 // number of Unicode characters stored in the Codec Name field + // * Codec Name WCHAR variable // array of Unicode characters - name of codec used to create the content + // * Codec Description Length WORD 16 // number of Unicode characters stored in the Codec Description field + // * Codec Description WCHAR variable // array of Unicode characters - description of format used to create the content + // * Codec Information Length WORD 16 // number of Unicode characters stored in the Codec Information field + // * Codec Information BYTESTREAM variable // opaque array of information bytes about the codec used to create the content + + // shortcut + $thisfile_asf['codec_list_object'] = array(); + $thisfile_asf_codeclistobject = &$thisfile_asf['codec_list_object']; + + $thisfile_asf_codeclistobject['offset'] = $NextObjectOffset + $offset; + $thisfile_asf_codeclistobject['objectid'] = $NextObjectGUID; + $thisfile_asf_codeclistobject['objectid_guid'] = $NextObjectGUIDtext; + $thisfile_asf_codeclistobject['objectsize'] = $NextObjectSize; + $thisfile_asf_codeclistobject['reserved'] = substr($ASFHeaderData, $offset, 16); + $offset += 16; + $thisfile_asf_codeclistobject['reserved_guid'] = $this->BytestringToGUID($thisfile_asf_codeclistobject['reserved']); + if ($thisfile_asf_codeclistobject['reserved'] != $this->GUIDtoBytestring('86D15241-311D-11D0-A3A4-00A0C90348F6')) { + $info['warning'][] = 'codec_list_object.reserved GUID {'.$this->BytestringToGUID($thisfile_asf_codeclistobject['reserved']).'} does not match expected "GETID3_ASF_Reserved_1" GUID {86D15241-311D-11D0-A3A4-00A0C90348F6}'; + //return false; + break; + } + $thisfile_asf_codeclistobject['codec_entries_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); + $offset += 4; + for ($CodecEntryCounter = 0; $CodecEntryCounter < $thisfile_asf_codeclistobject['codec_entries_count']; $CodecEntryCounter++) { + // shortcut + $thisfile_asf_codeclistobject['codec_entries'][$CodecEntryCounter] = array(); + $thisfile_asf_codeclistobject_codecentries_current = &$thisfile_asf_codeclistobject['codec_entries'][$CodecEntryCounter]; + + $thisfile_asf_codeclistobject_codecentries_current['type_raw'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $thisfile_asf_codeclistobject_codecentries_current['type'] = self::codecListObjectTypeLookup($thisfile_asf_codeclistobject_codecentries_current['type_raw']); + + $CodecNameLength = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)) * 2; // 2 bytes per character + $offset += 2; + $thisfile_asf_codeclistobject_codecentries_current['name'] = substr($ASFHeaderData, $offset, $CodecNameLength); + $offset += $CodecNameLength; + + $CodecDescriptionLength = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)) * 2; // 2 bytes per character + $offset += 2; + $thisfile_asf_codeclistobject_codecentries_current['description'] = substr($ASFHeaderData, $offset, $CodecDescriptionLength); + $offset += $CodecDescriptionLength; + + $CodecInformationLength = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $thisfile_asf_codeclistobject_codecentries_current['information'] = substr($ASFHeaderData, $offset, $CodecInformationLength); + $offset += $CodecInformationLength; + + if ($thisfile_asf_codeclistobject_codecentries_current['type_raw'] == 2) { // audio codec + + if (strpos($thisfile_asf_codeclistobject_codecentries_current['description'], ',') === false) { + $info['warning'][] = '[asf][codec_list_object][codec_entries]['.$CodecEntryCounter.'][description] expected to contain comma-seperated list of parameters: "'.$thisfile_asf_codeclistobject_codecentries_current['description'].'"'; + } else { + + list($AudioCodecBitrate, $AudioCodecFrequency, $AudioCodecChannels) = explode(',', $this->TrimConvert($thisfile_asf_codeclistobject_codecentries_current['description'])); + $thisfile_audio['codec'] = $this->TrimConvert($thisfile_asf_codeclistobject_codecentries_current['name']); + + if (!isset($thisfile_audio['bitrate']) && strstr($AudioCodecBitrate, 'kbps')) { + $thisfile_audio['bitrate'] = (int) (trim(str_replace('kbps', '', $AudioCodecBitrate)) * 1000); + } + //if (!isset($thisfile_video['bitrate']) && isset($thisfile_audio['bitrate']) && isset($thisfile_asf['file_properties_object']['max_bitrate']) && ($thisfile_asf_codeclistobject['codec_entries_count'] > 1)) { + if (empty($thisfile_video['bitrate']) && !empty($thisfile_audio['bitrate']) && !empty($info['bitrate'])) { + //$thisfile_video['bitrate'] = $thisfile_asf['file_properties_object']['max_bitrate'] - $thisfile_audio['bitrate']; + $thisfile_video['bitrate'] = $info['bitrate'] - $thisfile_audio['bitrate']; + } + + $AudioCodecFrequency = (int) trim(str_replace('kHz', '', $AudioCodecFrequency)); + switch ($AudioCodecFrequency) { + case 8: + case 8000: + $thisfile_audio['sample_rate'] = 8000; + break; + + case 11: + case 11025: + $thisfile_audio['sample_rate'] = 11025; + break; + + case 12: + case 12000: + $thisfile_audio['sample_rate'] = 12000; + break; + + case 16: + case 16000: + $thisfile_audio['sample_rate'] = 16000; + break; + + case 22: + case 22050: + $thisfile_audio['sample_rate'] = 22050; + break; + + case 24: + case 24000: + $thisfile_audio['sample_rate'] = 24000; + break; + + case 32: + case 32000: + $thisfile_audio['sample_rate'] = 32000; + break; + + case 44: + case 441000: + $thisfile_audio['sample_rate'] = 44100; + break; + + case 48: + case 48000: + $thisfile_audio['sample_rate'] = 48000; + break; + + default: + $info['warning'][] = 'unknown frequency: "'.$AudioCodecFrequency.'" ('.$this->TrimConvert($thisfile_asf_codeclistobject_codecentries_current['description']).')'; + break; + } + + if (!isset($thisfile_audio['channels'])) { + if (strstr($AudioCodecChannels, 'stereo')) { + $thisfile_audio['channels'] = 2; + } elseif (strstr($AudioCodecChannels, 'mono')) { + $thisfile_audio['channels'] = 1; + } + } + + } + } + } + break; + + case GETID3_ASF_Script_Command_Object: + // Script Command Object: (optional, one only) + // Field Name Field Type Size (bits) + // Object ID GUID 128 // GUID for Script Command object - GETID3_ASF_Script_Command_Object + // Object Size QWORD 64 // size of Script Command object, including 44 bytes of Script Command Object header + // Reserved GUID 128 // hardcoded: 4B1ACBE3-100B-11D0-A39B-00A0C90348F6 + // Commands Count WORD 16 // number of Commands structures in the Script Commands Objects + // Command Types Count WORD 16 // number of Command Types structures in the Script Commands Objects + // Command Types array of: variable // + // * Command Type Name Length WORD 16 // number of Unicode characters for Command Type Name + // * Command Type Name WCHAR variable // array of Unicode characters - name of a type of command + // Commands array of: variable // + // * Presentation Time DWORD 32 // presentation time of that command, in milliseconds + // * Type Index WORD 16 // type of this command, as a zero-based index into the array of Command Types of this object + // * Command Name Length WORD 16 // number of Unicode characters for Command Name + // * Command Name WCHAR variable // array of Unicode characters - name of this command + + // shortcut + $thisfile_asf['script_command_object'] = array(); + $thisfile_asf_scriptcommandobject = &$thisfile_asf['script_command_object']; + + $thisfile_asf_scriptcommandobject['offset'] = $NextObjectOffset + $offset; + $thisfile_asf_scriptcommandobject['objectid'] = $NextObjectGUID; + $thisfile_asf_scriptcommandobject['objectid_guid'] = $NextObjectGUIDtext; + $thisfile_asf_scriptcommandobject['objectsize'] = $NextObjectSize; + $thisfile_asf_scriptcommandobject['reserved'] = substr($ASFHeaderData, $offset, 16); + $offset += 16; + $thisfile_asf_scriptcommandobject['reserved_guid'] = $this->BytestringToGUID($thisfile_asf_scriptcommandobject['reserved']); + if ($thisfile_asf_scriptcommandobject['reserved'] != $this->GUIDtoBytestring('4B1ACBE3-100B-11D0-A39B-00A0C90348F6')) { + $info['warning'][] = 'script_command_object.reserved GUID {'.$this->BytestringToGUID($thisfile_asf_scriptcommandobject['reserved']).'} does not match expected "GETID3_ASF_Reserved_1" GUID {4B1ACBE3-100B-11D0-A39B-00A0C90348F6}'; + //return false; + break; + } + $thisfile_asf_scriptcommandobject['commands_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $thisfile_asf_scriptcommandobject['command_types_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + for ($CommandTypesCounter = 0; $CommandTypesCounter < $thisfile_asf_scriptcommandobject['command_types_count']; $CommandTypesCounter++) { + $CommandTypeNameLength = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)) * 2; // 2 bytes per character + $offset += 2; + $thisfile_asf_scriptcommandobject['command_types'][$CommandTypesCounter]['name'] = substr($ASFHeaderData, $offset, $CommandTypeNameLength); + $offset += $CommandTypeNameLength; + } + for ($CommandsCounter = 0; $CommandsCounter < $thisfile_asf_scriptcommandobject['commands_count']; $CommandsCounter++) { + $thisfile_asf_scriptcommandobject['commands'][$CommandsCounter]['presentation_time'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); + $offset += 4; + $thisfile_asf_scriptcommandobject['commands'][$CommandsCounter]['type_index'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + + $CommandTypeNameLength = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)) * 2; // 2 bytes per character + $offset += 2; + $thisfile_asf_scriptcommandobject['commands'][$CommandsCounter]['name'] = substr($ASFHeaderData, $offset, $CommandTypeNameLength); + $offset += $CommandTypeNameLength; + } + break; + + case GETID3_ASF_Marker_Object: + // Marker Object: (optional, one only) + // Field Name Field Type Size (bits) + // Object ID GUID 128 // GUID for Marker object - GETID3_ASF_Marker_Object + // Object Size QWORD 64 // size of Marker object, including 48 bytes of Marker Object header + // Reserved GUID 128 // hardcoded: 4CFEDB20-75F6-11CF-9C0F-00A0C90349CB + // Markers Count DWORD 32 // number of Marker structures in Marker Object + // Reserved WORD 16 // hardcoded: 0x0000 + // Name Length WORD 16 // number of bytes in the Name field + // Name WCHAR variable // name of the Marker Object + // Markers array of: variable // + // * Offset QWORD 64 // byte offset into Data Object + // * Presentation Time QWORD 64 // in 100-nanosecond units + // * Entry Length WORD 16 // length in bytes of (Send Time + Flags + Marker Description Length + Marker Description + Padding) + // * Send Time DWORD 32 // in milliseconds + // * Flags DWORD 32 // hardcoded: 0x00000000 + // * Marker Description Length DWORD 32 // number of bytes in Marker Description field + // * Marker Description WCHAR variable // array of Unicode characters - description of marker entry + // * Padding BYTESTREAM variable // optional padding bytes + + // shortcut + $thisfile_asf['marker_object'] = array(); + $thisfile_asf_markerobject = &$thisfile_asf['marker_object']; + + $thisfile_asf_markerobject['offset'] = $NextObjectOffset + $offset; + $thisfile_asf_markerobject['objectid'] = $NextObjectGUID; + $thisfile_asf_markerobject['objectid_guid'] = $NextObjectGUIDtext; + $thisfile_asf_markerobject['objectsize'] = $NextObjectSize; + $thisfile_asf_markerobject['reserved'] = substr($ASFHeaderData, $offset, 16); + $offset += 16; + $thisfile_asf_markerobject['reserved_guid'] = $this->BytestringToGUID($thisfile_asf_markerobject['reserved']); + if ($thisfile_asf_markerobject['reserved'] != $this->GUIDtoBytestring('4CFEDB20-75F6-11CF-9C0F-00A0C90349CB')) { + $info['warning'][] = 'marker_object.reserved GUID {'.$this->BytestringToGUID($thisfile_asf_markerobject['reserved_1']).'} does not match expected "GETID3_ASF_Reserved_1" GUID {4CFEDB20-75F6-11CF-9C0F-00A0C90349CB}'; + break; + } + $thisfile_asf_markerobject['markers_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); + $offset += 4; + $thisfile_asf_markerobject['reserved_2'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + if ($thisfile_asf_markerobject['reserved_2'] != 0) { + $info['warning'][] = 'marker_object.reserved_2 ('.getid3_lib::PrintHexBytes($thisfile_asf_markerobject['reserved_2']).') does not match expected value of "0"'; + break; + } + $thisfile_asf_markerobject['name_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $thisfile_asf_markerobject['name'] = substr($ASFHeaderData, $offset, $thisfile_asf_markerobject['name_length']); + $offset += $thisfile_asf_markerobject['name_length']; + for ($MarkersCounter = 0; $MarkersCounter < $thisfile_asf_markerobject['markers_count']; $MarkersCounter++) { + $thisfile_asf_markerobject['markers'][$MarkersCounter]['offset'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8)); + $offset += 8; + $thisfile_asf_markerobject['markers'][$MarkersCounter]['presentation_time'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8)); + $offset += 8; + $thisfile_asf_markerobject['markers'][$MarkersCounter]['entry_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $thisfile_asf_markerobject['markers'][$MarkersCounter]['send_time'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); + $offset += 4; + $thisfile_asf_markerobject['markers'][$MarkersCounter]['flags'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); + $offset += 4; + $thisfile_asf_markerobject['markers'][$MarkersCounter]['marker_description_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); + $offset += 4; + $thisfile_asf_markerobject['markers'][$MarkersCounter]['marker_description'] = substr($ASFHeaderData, $offset, $thisfile_asf_markerobject['markers'][$MarkersCounter]['marker_description_length']); + $offset += $thisfile_asf_markerobject['markers'][$MarkersCounter]['marker_description_length']; + $PaddingLength = $thisfile_asf_markerobject['markers'][$MarkersCounter]['entry_length'] - 4 - 4 - 4 - $thisfile_asf_markerobject['markers'][$MarkersCounter]['marker_description_length']; + if ($PaddingLength > 0) { + $thisfile_asf_markerobject['markers'][$MarkersCounter]['padding'] = substr($ASFHeaderData, $offset, $PaddingLength); + $offset += $PaddingLength; + } + } + break; + + case GETID3_ASF_Bitrate_Mutual_Exclusion_Object: + // Bitrate Mutual Exclusion Object: (optional) + // Field Name Field Type Size (bits) + // Object ID GUID 128 // GUID for Bitrate Mutual Exclusion object - GETID3_ASF_Bitrate_Mutual_Exclusion_Object + // Object Size QWORD 64 // size of Bitrate Mutual Exclusion object, including 42 bytes of Bitrate Mutual Exclusion Object header + // Exlusion Type GUID 128 // nature of mutual exclusion relationship. one of: (GETID3_ASF_Mutex_Bitrate, GETID3_ASF_Mutex_Unknown) + // Stream Numbers Count WORD 16 // number of video streams + // Stream Numbers WORD variable // array of mutually exclusive video stream numbers. 1 <= valid <= 127 + + // shortcut + $thisfile_asf['bitrate_mutual_exclusion_object'] = array(); + $thisfile_asf_bitratemutualexclusionobject = &$thisfile_asf['bitrate_mutual_exclusion_object']; + + $thisfile_asf_bitratemutualexclusionobject['offset'] = $NextObjectOffset + $offset; + $thisfile_asf_bitratemutualexclusionobject['objectid'] = $NextObjectGUID; + $thisfile_asf_bitratemutualexclusionobject['objectid_guid'] = $NextObjectGUIDtext; + $thisfile_asf_bitratemutualexclusionobject['objectsize'] = $NextObjectSize; + $thisfile_asf_bitratemutualexclusionobject['reserved'] = substr($ASFHeaderData, $offset, 16); + $thisfile_asf_bitratemutualexclusionobject['reserved_guid'] = $this->BytestringToGUID($thisfile_asf_bitratemutualexclusionobject['reserved']); + $offset += 16; + if (($thisfile_asf_bitratemutualexclusionobject['reserved'] != GETID3_ASF_Mutex_Bitrate) && ($thisfile_asf_bitratemutualexclusionobject['reserved'] != GETID3_ASF_Mutex_Unknown)) { + $info['warning'][] = 'bitrate_mutual_exclusion_object.reserved GUID {'.$this->BytestringToGUID($thisfile_asf_bitratemutualexclusionobject['reserved']).'} does not match expected "GETID3_ASF_Mutex_Bitrate" GUID {'.$this->BytestringToGUID(GETID3_ASF_Mutex_Bitrate).'} or "GETID3_ASF_Mutex_Unknown" GUID {'.$this->BytestringToGUID(GETID3_ASF_Mutex_Unknown).'}'; + //return false; + break; + } + $thisfile_asf_bitratemutualexclusionobject['stream_numbers_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + for ($StreamNumberCounter = 0; $StreamNumberCounter < $thisfile_asf_bitratemutualexclusionobject['stream_numbers_count']; $StreamNumberCounter++) { + $thisfile_asf_bitratemutualexclusionobject['stream_numbers'][$StreamNumberCounter] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + } + break; + + case GETID3_ASF_Error_Correction_Object: + // Error Correction Object: (optional, one only) + // Field Name Field Type Size (bits) + // Object ID GUID 128 // GUID for Error Correction object - GETID3_ASF_Error_Correction_Object + // Object Size QWORD 64 // size of Error Correction object, including 44 bytes of Error Correction Object header + // Error Correction Type GUID 128 // type of error correction. one of: (GETID3_ASF_No_Error_Correction, GETID3_ASF_Audio_Spread) + // Error Correction Data Length DWORD 32 // number of bytes in Error Correction Data field + // Error Correction Data BYTESTREAM variable // structure depends on value of Error Correction Type field + + // shortcut + $thisfile_asf['error_correction_object'] = array(); + $thisfile_asf_errorcorrectionobject = &$thisfile_asf['error_correction_object']; + + $thisfile_asf_errorcorrectionobject['offset'] = $NextObjectOffset + $offset; + $thisfile_asf_errorcorrectionobject['objectid'] = $NextObjectGUID; + $thisfile_asf_errorcorrectionobject['objectid_guid'] = $NextObjectGUIDtext; + $thisfile_asf_errorcorrectionobject['objectsize'] = $NextObjectSize; + $thisfile_asf_errorcorrectionobject['error_correction_type'] = substr($ASFHeaderData, $offset, 16); + $offset += 16; + $thisfile_asf_errorcorrectionobject['error_correction_guid'] = $this->BytestringToGUID($thisfile_asf_errorcorrectionobject['error_correction_type']); + $thisfile_asf_errorcorrectionobject['error_correction_data_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); + $offset += 4; + switch ($thisfile_asf_errorcorrectionobject['error_correction_type']) { + case GETID3_ASF_No_Error_Correction: + // should be no data, but just in case there is, skip to the end of the field + $offset += $thisfile_asf_errorcorrectionobject['error_correction_data_length']; + break; + + case GETID3_ASF_Audio_Spread: + // Field Name Field Type Size (bits) + // Span BYTE 8 // number of packets over which audio will be spread. + // Virtual Packet Length WORD 16 // size of largest audio payload found in audio stream + // Virtual Chunk Length WORD 16 // size of largest audio payload found in audio stream + // Silence Data Length WORD 16 // number of bytes in Silence Data field + // Silence Data BYTESTREAM variable // hardcoded: 0x00 * (Silence Data Length) bytes + + $thisfile_asf_errorcorrectionobject['span'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 1)); + $offset += 1; + $thisfile_asf_errorcorrectionobject['virtual_packet_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $thisfile_asf_errorcorrectionobject['virtual_chunk_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $thisfile_asf_errorcorrectionobject['silence_data_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $thisfile_asf_errorcorrectionobject['silence_data'] = substr($ASFHeaderData, $offset, $thisfile_asf_errorcorrectionobject['silence_data_length']); + $offset += $thisfile_asf_errorcorrectionobject['silence_data_length']; + break; + + default: + $info['warning'][] = 'error_correction_object.error_correction_type GUID {'.$this->BytestringToGUID($thisfile_asf_errorcorrectionobject['reserved']).'} does not match expected "GETID3_ASF_No_Error_Correction" GUID {'.$this->BytestringToGUID(GETID3_ASF_No_Error_Correction).'} or "GETID3_ASF_Audio_Spread" GUID {'.$this->BytestringToGUID(GETID3_ASF_Audio_Spread).'}'; + //return false; + break; + } + + break; + + case GETID3_ASF_Content_Description_Object: + // Content Description Object: (optional, one only) + // Field Name Field Type Size (bits) + // Object ID GUID 128 // GUID for Content Description object - GETID3_ASF_Content_Description_Object + // Object Size QWORD 64 // size of Content Description object, including 34 bytes of Content Description Object header + // Title Length WORD 16 // number of bytes in Title field + // Author Length WORD 16 // number of bytes in Author field + // Copyright Length WORD 16 // number of bytes in Copyright field + // Description Length WORD 16 // number of bytes in Description field + // Rating Length WORD 16 // number of bytes in Rating field + // Title WCHAR 16 // array of Unicode characters - Title + // Author WCHAR 16 // array of Unicode characters - Author + // Copyright WCHAR 16 // array of Unicode characters - Copyright + // Description WCHAR 16 // array of Unicode characters - Description + // Rating WCHAR 16 // array of Unicode characters - Rating + + // shortcut + $thisfile_asf['content_description_object'] = array(); + $thisfile_asf_contentdescriptionobject = &$thisfile_asf['content_description_object']; + + $thisfile_asf_contentdescriptionobject['offset'] = $NextObjectOffset + $offset; + $thisfile_asf_contentdescriptionobject['objectid'] = $NextObjectGUID; + $thisfile_asf_contentdescriptionobject['objectid_guid'] = $NextObjectGUIDtext; + $thisfile_asf_contentdescriptionobject['objectsize'] = $NextObjectSize; + $thisfile_asf_contentdescriptionobject['title_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $thisfile_asf_contentdescriptionobject['author_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $thisfile_asf_contentdescriptionobject['copyright_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $thisfile_asf_contentdescriptionobject['description_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $thisfile_asf_contentdescriptionobject['rating_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $thisfile_asf_contentdescriptionobject['title'] = substr($ASFHeaderData, $offset, $thisfile_asf_contentdescriptionobject['title_length']); + $offset += $thisfile_asf_contentdescriptionobject['title_length']; + $thisfile_asf_contentdescriptionobject['author'] = substr($ASFHeaderData, $offset, $thisfile_asf_contentdescriptionobject['author_length']); + $offset += $thisfile_asf_contentdescriptionobject['author_length']; + $thisfile_asf_contentdescriptionobject['copyright'] = substr($ASFHeaderData, $offset, $thisfile_asf_contentdescriptionobject['copyright_length']); + $offset += $thisfile_asf_contentdescriptionobject['copyright_length']; + $thisfile_asf_contentdescriptionobject['description'] = substr($ASFHeaderData, $offset, $thisfile_asf_contentdescriptionobject['description_length']); + $offset += $thisfile_asf_contentdescriptionobject['description_length']; + $thisfile_asf_contentdescriptionobject['rating'] = substr($ASFHeaderData, $offset, $thisfile_asf_contentdescriptionobject['rating_length']); + $offset += $thisfile_asf_contentdescriptionobject['rating_length']; + + $ASFcommentKeysToCopy = array('title'=>'title', 'author'=>'artist', 'copyright'=>'copyright', 'description'=>'comment', 'rating'=>'rating'); + foreach ($ASFcommentKeysToCopy as $keytocopyfrom => $keytocopyto) { + if (!empty($thisfile_asf_contentdescriptionobject[$keytocopyfrom])) { + $thisfile_asf_comments[$keytocopyto][] = $this->TrimTerm($thisfile_asf_contentdescriptionobject[$keytocopyfrom]); + } + } + break; + + case GETID3_ASF_Extended_Content_Description_Object: + // Extended Content Description Object: (optional, one only) + // Field Name Field Type Size (bits) + // Object ID GUID 128 // GUID for Extended Content Description object - GETID3_ASF_Extended_Content_Description_Object + // Object Size QWORD 64 // size of ExtendedContent Description object, including 26 bytes of Extended Content Description Object header + // Content Descriptors Count WORD 16 // number of entries in Content Descriptors list + // Content Descriptors array of: variable // + // * Descriptor Name Length WORD 16 // size in bytes of Descriptor Name field + // * Descriptor Name WCHAR variable // array of Unicode characters - Descriptor Name + // * Descriptor Value Data Type WORD 16 // Lookup array: + // 0x0000 = Unicode String (variable length) + // 0x0001 = BYTE array (variable length) + // 0x0002 = BOOL (DWORD, 32 bits) + // 0x0003 = DWORD (DWORD, 32 bits) + // 0x0004 = QWORD (QWORD, 64 bits) + // 0x0005 = WORD (WORD, 16 bits) + // * Descriptor Value Length WORD 16 // number of bytes stored in Descriptor Value field + // * Descriptor Value variable variable // value for Content Descriptor + + // shortcut + $thisfile_asf['extended_content_description_object'] = array(); + $thisfile_asf_extendedcontentdescriptionobject = &$thisfile_asf['extended_content_description_object']; + + $thisfile_asf_extendedcontentdescriptionobject['offset'] = $NextObjectOffset + $offset; + $thisfile_asf_extendedcontentdescriptionobject['objectid'] = $NextObjectGUID; + $thisfile_asf_extendedcontentdescriptionobject['objectid_guid'] = $NextObjectGUIDtext; + $thisfile_asf_extendedcontentdescriptionobject['objectsize'] = $NextObjectSize; + $thisfile_asf_extendedcontentdescriptionobject['content_descriptors_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + for ($ExtendedContentDescriptorsCounter = 0; $ExtendedContentDescriptorsCounter < $thisfile_asf_extendedcontentdescriptionobject['content_descriptors_count']; $ExtendedContentDescriptorsCounter++) { + // shortcut + $thisfile_asf_extendedcontentdescriptionobject['content_descriptors'][$ExtendedContentDescriptorsCounter] = array(); + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current = &$thisfile_asf_extendedcontentdescriptionobject['content_descriptors'][$ExtendedContentDescriptorsCounter]; + + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['base_offset'] = $offset + 30; + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['name_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['name'] = substr($ASFHeaderData, $offset, $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['name_length']); + $offset += $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['name_length']; + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_type'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'] = substr($ASFHeaderData, $offset, $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_length']); + $offset += $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_length']; + switch ($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_type']) { + case 0x0000: // Unicode string + break; + + case 0x0001: // BYTE array + // do nothing + break; + + case 0x0002: // BOOL + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'] = (bool) getid3_lib::LittleEndian2Int($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']); + break; + + case 0x0003: // DWORD + case 0x0004: // QWORD + case 0x0005: // WORD + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'] = getid3_lib::LittleEndian2Int($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']); + break; + + default: + $info['warning'][] = 'extended_content_description.content_descriptors.'.$ExtendedContentDescriptorsCounter.'.value_type is invalid ('.$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_type'].')'; + //return false; + break; + } + switch ($this->TrimConvert(strtolower($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['name']))) { + + case 'wm/albumartist': + case 'artist': + // Note: not 'artist', that comes from 'author' tag + $thisfile_asf_comments['albumartist'] = array($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'])); + break; + + case 'wm/albumtitle': + case 'album': + $thisfile_asf_comments['album'] = array($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'])); + break; + + case 'wm/genre': + case 'genre': + $thisfile_asf_comments['genre'] = array($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'])); + break; + + case 'wm/partofset': + $thisfile_asf_comments['partofset'] = array($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'])); + break; + + case 'wm/tracknumber': + case 'tracknumber': + // be careful casting to int: casting unicode strings to int gives unexpected results (stops parsing at first non-numeric character) + $thisfile_asf_comments['track'] = array($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'])); + foreach ($thisfile_asf_comments['track'] as $key => $value) { + if (preg_match('/^[0-9\x00]+$/', $value)) { + $thisfile_asf_comments['track'][$key] = intval(str_replace("\x00", '', $value)); + } + } + break; + + case 'wm/track': + if (empty($thisfile_asf_comments['track'])) { + $thisfile_asf_comments['track'] = array(1 + $this->TrimConvert($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'])); + } + break; + + case 'wm/year': + case 'year': + case 'date': + $thisfile_asf_comments['year'] = array( $this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'])); + break; + + case 'wm/lyrics': + case 'lyrics': + $thisfile_asf_comments['lyrics'] = array($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'])); + break; + + case 'isvbr': + if ($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']) { + $thisfile_audio['bitrate_mode'] = 'vbr'; + $thisfile_video['bitrate_mode'] = 'vbr'; + } + break; + + case 'id3': + $this->getid3->include_module('tag.id3v2'); + + $getid3_id3v2 = new getid3_id3v2($this->getid3); + $getid3_id3v2->AnalyzeString($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']); + unset($getid3_id3v2); + + if ($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_length'] > 1024) { + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'] = ''; + } + break; + + case 'wm/encodingtime': + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['encoding_time_unix'] = $this->FILETIMEtoUNIXtime($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']); + $thisfile_asf_comments['encoding_time_unix'] = array($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['encoding_time_unix']); + break; + + case 'wm/picture': + $WMpicture = $this->ASF_WMpicture($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']); + foreach ($WMpicture as $key => $value) { + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current[$key] = $value; + } + unset($WMpicture); +/* + $wm_picture_offset = 0; + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_type_id'] = getid3_lib::LittleEndian2Int(substr($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'], $wm_picture_offset, 1)); + $wm_picture_offset += 1; + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_type'] = self::WMpictureTypeLookup($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_type_id']); + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_size'] = getid3_lib::LittleEndian2Int(substr($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'], $wm_picture_offset, 4)); + $wm_picture_offset += 4; + + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_mime'] = ''; + do { + $next_byte_pair = substr($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'], $wm_picture_offset, 2); + $wm_picture_offset += 2; + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_mime'] .= $next_byte_pair; + } while ($next_byte_pair !== "\x00\x00"); + + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_description'] = ''; + do { + $next_byte_pair = substr($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'], $wm_picture_offset, 2); + $wm_picture_offset += 2; + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_description'] .= $next_byte_pair; + } while ($next_byte_pair !== "\x00\x00"); + + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['dataoffset'] = $wm_picture_offset; + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['data'] = substr($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'], $wm_picture_offset); + unset($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']); + + $imageinfo = array(); + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_mime'] = ''; + $imagechunkcheck = getid3_lib::GetDataImageSize($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['data'], $imageinfo); + unset($imageinfo); + if (!empty($imagechunkcheck)) { + $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]); + } + if (!isset($thisfile_asf_comments['picture'])) { + $thisfile_asf_comments['picture'] = array(); + } + $thisfile_asf_comments['picture'][] = array('data'=>$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['data'], 'image_mime'=>$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_mime']); +*/ + break; + + default: + switch ($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_type']) { + case 0: // Unicode string + if (substr($this->TrimConvert($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['name']), 0, 3) == 'WM/') { + $thisfile_asf_comments[str_replace('wm/', '', strtolower($this->TrimConvert($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['name'])))] = array($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'])); + } + break; + + case 1: + break; + } + break; + } + + } + break; + + case GETID3_ASF_Stream_Bitrate_Properties_Object: + // Stream Bitrate Properties Object: (optional, one only) + // Field Name Field Type Size (bits) + // Object ID GUID 128 // GUID for Stream Bitrate Properties object - GETID3_ASF_Stream_Bitrate_Properties_Object + // Object Size QWORD 64 // size of Extended Content Description object, including 26 bytes of Stream Bitrate Properties Object header + // Bitrate Records Count WORD 16 // number of records in Bitrate Records + // Bitrate Records array of: variable // + // * Flags WORD 16 // + // * * Stream Number bits 7 (0x007F) // number of this stream + // * * Reserved bits 9 (0xFF80) // hardcoded: 0 + // * Average Bitrate DWORD 32 // in bits per second + + // shortcut + $thisfile_asf['stream_bitrate_properties_object'] = array(); + $thisfile_asf_streambitratepropertiesobject = &$thisfile_asf['stream_bitrate_properties_object']; + + $thisfile_asf_streambitratepropertiesobject['offset'] = $NextObjectOffset + $offset; + $thisfile_asf_streambitratepropertiesobject['objectid'] = $NextObjectGUID; + $thisfile_asf_streambitratepropertiesobject['objectid_guid'] = $NextObjectGUIDtext; + $thisfile_asf_streambitratepropertiesobject['objectsize'] = $NextObjectSize; + $thisfile_asf_streambitratepropertiesobject['bitrate_records_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + for ($BitrateRecordsCounter = 0; $BitrateRecordsCounter < $thisfile_asf_streambitratepropertiesobject['bitrate_records_count']; $BitrateRecordsCounter++) { + $thisfile_asf_streambitratepropertiesobject['bitrate_records'][$BitrateRecordsCounter]['flags_raw'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)); + $offset += 2; + $thisfile_asf_streambitratepropertiesobject['bitrate_records'][$BitrateRecordsCounter]['flags']['stream_number'] = $thisfile_asf_streambitratepropertiesobject['bitrate_records'][$BitrateRecordsCounter]['flags_raw'] & 0x007F; + $thisfile_asf_streambitratepropertiesobject['bitrate_records'][$BitrateRecordsCounter]['bitrate'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4)); + $offset += 4; + } + break; + + case GETID3_ASF_Padding_Object: + // Padding Object: (optional) + // Field Name Field Type Size (bits) + // Object ID GUID 128 // GUID for Padding object - GETID3_ASF_Padding_Object + // Object Size QWORD 64 // size of Padding object, including 24 bytes of ASF Padding Object header + // Padding Data BYTESTREAM variable // ignore + + // shortcut + $thisfile_asf['padding_object'] = array(); + $thisfile_asf_paddingobject = &$thisfile_asf['padding_object']; + + $thisfile_asf_paddingobject['offset'] = $NextObjectOffset + $offset; + $thisfile_asf_paddingobject['objectid'] = $NextObjectGUID; + $thisfile_asf_paddingobject['objectid_guid'] = $NextObjectGUIDtext; + $thisfile_asf_paddingobject['objectsize'] = $NextObjectSize; + $thisfile_asf_paddingobject['padding_length'] = $thisfile_asf_paddingobject['objectsize'] - 16 - 8; + $thisfile_asf_paddingobject['padding'] = substr($ASFHeaderData, $offset, $thisfile_asf_paddingobject['padding_length']); + $offset += ($NextObjectSize - 16 - 8); + break; + + case GETID3_ASF_Extended_Content_Encryption_Object: + case GETID3_ASF_Content_Encryption_Object: + // WMA DRM - just ignore + $offset += ($NextObjectSize - 16 - 8); + break; + + default: + // Implementations shall ignore any standard or non-standard object that they do not know how to handle. + if ($this->GUIDname($NextObjectGUIDtext)) { + $info['warning'][] = 'unhandled GUID "'.$this->GUIDname($NextObjectGUIDtext).'" {'.$NextObjectGUIDtext.'} in ASF header at offset '.($offset - 16 - 8); + } else { + $info['warning'][] = 'unknown GUID {'.$NextObjectGUIDtext.'} in ASF header at offset '.($offset - 16 - 8); + } + $offset += ($NextObjectSize - 16 - 8); + break; + } + } + if (isset($thisfile_asf_streambitrateproperties['bitrate_records_count'])) { + $ASFbitrateAudio = 0; + $ASFbitrateVideo = 0; + for ($BitrateRecordsCounter = 0; $BitrateRecordsCounter < $thisfile_asf_streambitrateproperties['bitrate_records_count']; $BitrateRecordsCounter++) { + if (isset($thisfile_asf_codeclistobject['codec_entries'][$BitrateRecordsCounter])) { + switch ($thisfile_asf_codeclistobject['codec_entries'][$BitrateRecordsCounter]['type_raw']) { + case 1: + $ASFbitrateVideo += $thisfile_asf_streambitrateproperties['bitrate_records'][$BitrateRecordsCounter]['bitrate']; + break; + + case 2: + $ASFbitrateAudio += $thisfile_asf_streambitrateproperties['bitrate_records'][$BitrateRecordsCounter]['bitrate']; + break; + + default: + // do nothing + break; + } + } + } + if ($ASFbitrateAudio > 0) { + $thisfile_audio['bitrate'] = $ASFbitrateAudio; + } + if ($ASFbitrateVideo > 0) { + $thisfile_video['bitrate'] = $ASFbitrateVideo; + } + } + if (isset($thisfile_asf['stream_properties_object']) && is_array($thisfile_asf['stream_properties_object'])) { + + $thisfile_audio['bitrate'] = 0; + $thisfile_video['bitrate'] = 0; + + foreach ($thisfile_asf['stream_properties_object'] as $streamnumber => $streamdata) { + + switch ($streamdata['stream_type']) { + case GETID3_ASF_Audio_Media: + // Field Name Field Type Size (bits) + // Codec ID / Format Tag WORD 16 // unique ID of audio codec - defined as wFormatTag field of WAVEFORMATEX structure + // Number of Channels WORD 16 // number of channels of audio - defined as nChannels field of WAVEFORMATEX structure + // Samples Per Second DWORD 32 // in Hertz - defined as nSamplesPerSec field of WAVEFORMATEX structure + // Average number of Bytes/sec DWORD 32 // bytes/sec of audio stream - defined as nAvgBytesPerSec field of WAVEFORMATEX structure + // Block Alignment WORD 16 // block size in bytes of audio codec - defined as nBlockAlign field of WAVEFORMATEX structure + // Bits per sample WORD 16 // bits per sample of mono data. set to zero for variable bitrate codecs. defined as wBitsPerSample field of WAVEFORMATEX structure + // Codec Specific Data Size WORD 16 // size in bytes of Codec Specific Data buffer - defined as cbSize field of WAVEFORMATEX structure + // Codec Specific Data BYTESTREAM variable // array of codec-specific data bytes + + // shortcut + $thisfile_asf['audio_media'][$streamnumber] = array(); + $thisfile_asf_audiomedia_currentstream = &$thisfile_asf['audio_media'][$streamnumber]; + + $audiomediaoffset = 0; + + $thisfile_asf_audiomedia_currentstream = getid3_riff::parseWAVEFORMATex(substr($streamdata['type_specific_data'], $audiomediaoffset, 16)); + $audiomediaoffset += 16; + + $thisfile_audio['lossless'] = false; + switch ($thisfile_asf_audiomedia_currentstream['raw']['wFormatTag']) { + case 0x0001: // PCM + case 0x0163: // WMA9 Lossless + $thisfile_audio['lossless'] = true; + break; + } + + if (!empty($thisfile_asf['stream_bitrate_properties_object']['bitrate_records'])) { + foreach ($thisfile_asf['stream_bitrate_properties_object']['bitrate_records'] as $dummy => $dataarray) { + if (isset($dataarray['flags']['stream_number']) && ($dataarray['flags']['stream_number'] == $streamnumber)) { + $thisfile_asf_audiomedia_currentstream['bitrate'] = $dataarray['bitrate']; + $thisfile_audio['bitrate'] += $dataarray['bitrate']; + break; + } + } + } else { + if (!empty($thisfile_asf_audiomedia_currentstream['bytes_sec'])) { + $thisfile_audio['bitrate'] += $thisfile_asf_audiomedia_currentstream['bytes_sec'] * 8; + } elseif (!empty($thisfile_asf_audiomedia_currentstream['bitrate'])) { + $thisfile_audio['bitrate'] += $thisfile_asf_audiomedia_currentstream['bitrate']; + } + } + $thisfile_audio['streams'][$streamnumber] = $thisfile_asf_audiomedia_currentstream; + $thisfile_audio['streams'][$streamnumber]['wformattag'] = $thisfile_asf_audiomedia_currentstream['raw']['wFormatTag']; + $thisfile_audio['streams'][$streamnumber]['lossless'] = $thisfile_audio['lossless']; + $thisfile_audio['streams'][$streamnumber]['bitrate'] = $thisfile_audio['bitrate']; + $thisfile_audio['streams'][$streamnumber]['dataformat'] = 'wma'; + unset($thisfile_audio['streams'][$streamnumber]['raw']); + + $thisfile_asf_audiomedia_currentstream['codec_data_size'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $audiomediaoffset, 2)); + $audiomediaoffset += 2; + $thisfile_asf_audiomedia_currentstream['codec_data'] = substr($streamdata['type_specific_data'], $audiomediaoffset, $thisfile_asf_audiomedia_currentstream['codec_data_size']); + $audiomediaoffset += $thisfile_asf_audiomedia_currentstream['codec_data_size']; + + break; + + case GETID3_ASF_Video_Media: + // Field Name Field Type Size (bits) + // Encoded Image Width DWORD 32 // width of image in pixels + // Encoded Image Height DWORD 32 // height of image in pixels + // Reserved Flags BYTE 8 // hardcoded: 0x02 + // Format Data Size WORD 16 // size of Format Data field in bytes + // Format Data array of: variable // + // * Format Data Size DWORD 32 // number of bytes in Format Data field, in bytes - defined as biSize field of BITMAPINFOHEADER structure + // * Image Width LONG 32 // width of encoded image in pixels - defined as biWidth field of BITMAPINFOHEADER structure + // * Image Height LONG 32 // height of encoded image in pixels - defined as biHeight field of BITMAPINFOHEADER structure + // * Reserved WORD 16 // hardcoded: 0x0001 - defined as biPlanes field of BITMAPINFOHEADER structure + // * Bits Per Pixel Count WORD 16 // bits per pixel - defined as biBitCount field of BITMAPINFOHEADER structure + // * Compression ID FOURCC 32 // fourcc of video codec - defined as biCompression field of BITMAPINFOHEADER structure + // * Image Size DWORD 32 // image size in bytes - defined as biSizeImage field of BITMAPINFOHEADER structure + // * Horizontal Pixels / Meter DWORD 32 // horizontal resolution of target device in pixels per meter - defined as biXPelsPerMeter field of BITMAPINFOHEADER structure + // * Vertical Pixels / Meter DWORD 32 // vertical resolution of target device in pixels per meter - defined as biYPelsPerMeter field of BITMAPINFOHEADER structure + // * Colors Used Count DWORD 32 // number of color indexes in the color table that are actually used - defined as biClrUsed field of BITMAPINFOHEADER structure + // * Important Colors Count DWORD 32 // number of color index required for displaying bitmap. if zero, all colors are required. defined as biClrImportant field of BITMAPINFOHEADER structure + // * Codec Specific Data BYTESTREAM variable // array of codec-specific data bytes + + // shortcut + $thisfile_asf['video_media'][$streamnumber] = array(); + $thisfile_asf_videomedia_currentstream = &$thisfile_asf['video_media'][$streamnumber]; + + $videomediaoffset = 0; + $thisfile_asf_videomedia_currentstream['image_width'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4)); + $videomediaoffset += 4; + $thisfile_asf_videomedia_currentstream['image_height'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4)); + $videomediaoffset += 4; + $thisfile_asf_videomedia_currentstream['flags'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 1)); + $videomediaoffset += 1; + $thisfile_asf_videomedia_currentstream['format_data_size'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 2)); + $videomediaoffset += 2; + $thisfile_asf_videomedia_currentstream['format_data']['format_data_size'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4)); + $videomediaoffset += 4; + $thisfile_asf_videomedia_currentstream['format_data']['image_width'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4)); + $videomediaoffset += 4; + $thisfile_asf_videomedia_currentstream['format_data']['image_height'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4)); + $videomediaoffset += 4; + $thisfile_asf_videomedia_currentstream['format_data']['reserved'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 2)); + $videomediaoffset += 2; + $thisfile_asf_videomedia_currentstream['format_data']['bits_per_pixel'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 2)); + $videomediaoffset += 2; + $thisfile_asf_videomedia_currentstream['format_data']['codec_fourcc'] = substr($streamdata['type_specific_data'], $videomediaoffset, 4); + $videomediaoffset += 4; + $thisfile_asf_videomedia_currentstream['format_data']['image_size'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4)); + $videomediaoffset += 4; + $thisfile_asf_videomedia_currentstream['format_data']['horizontal_pels'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4)); + $videomediaoffset += 4; + $thisfile_asf_videomedia_currentstream['format_data']['vertical_pels'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4)); + $videomediaoffset += 4; + $thisfile_asf_videomedia_currentstream['format_data']['colors_used'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4)); + $videomediaoffset += 4; + $thisfile_asf_videomedia_currentstream['format_data']['colors_important'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4)); + $videomediaoffset += 4; + $thisfile_asf_videomedia_currentstream['format_data']['codec_data'] = substr($streamdata['type_specific_data'], $videomediaoffset); + + if (!empty($thisfile_asf['stream_bitrate_properties_object']['bitrate_records'])) { + foreach ($thisfile_asf['stream_bitrate_properties_object']['bitrate_records'] as $dummy => $dataarray) { + if (isset($dataarray['flags']['stream_number']) && ($dataarray['flags']['stream_number'] == $streamnumber)) { + $thisfile_asf_videomedia_currentstream['bitrate'] = $dataarray['bitrate']; + $thisfile_video['streams'][$streamnumber]['bitrate'] = $dataarray['bitrate']; + $thisfile_video['bitrate'] += $dataarray['bitrate']; + break; + } + } + } + + $thisfile_asf_videomedia_currentstream['format_data']['codec'] = getid3_riff::fourccLookup($thisfile_asf_videomedia_currentstream['format_data']['codec_fourcc']); + + $thisfile_video['streams'][$streamnumber]['fourcc'] = $thisfile_asf_videomedia_currentstream['format_data']['codec_fourcc']; + $thisfile_video['streams'][$streamnumber]['codec'] = $thisfile_asf_videomedia_currentstream['format_data']['codec']; + $thisfile_video['streams'][$streamnumber]['resolution_x'] = $thisfile_asf_videomedia_currentstream['image_width']; + $thisfile_video['streams'][$streamnumber]['resolution_y'] = $thisfile_asf_videomedia_currentstream['image_height']; + $thisfile_video['streams'][$streamnumber]['bits_per_sample'] = $thisfile_asf_videomedia_currentstream['format_data']['bits_per_pixel']; + break; + + default: + break; + } + } + } + + while ($this->ftell() < $info['avdataend']) { + $NextObjectDataHeader = $this->fread(24); + $offset = 0; + $NextObjectGUID = substr($NextObjectDataHeader, 0, 16); + $offset += 16; + $NextObjectGUIDtext = $this->BytestringToGUID($NextObjectGUID); + $NextObjectSize = getid3_lib::LittleEndian2Int(substr($NextObjectDataHeader, $offset, 8)); + $offset += 8; + + switch ($NextObjectGUID) { + case GETID3_ASF_Data_Object: + // Data Object: (mandatory, one only) + // Field Name Field Type Size (bits) + // Object ID GUID 128 // GUID for Data object - GETID3_ASF_Data_Object + // Object Size QWORD 64 // size of Data object, including 50 bytes of Data Object header. may be 0 if FilePropertiesObject.BroadcastFlag == 1 + // File ID GUID 128 // unique identifier. identical to File ID field in Header Object + // Total Data Packets QWORD 64 // number of Data Packet entries in Data Object. invalid if FilePropertiesObject.BroadcastFlag == 1 + // Reserved WORD 16 // hardcoded: 0x0101 + + // shortcut + $thisfile_asf['data_object'] = array(); + $thisfile_asf_dataobject = &$thisfile_asf['data_object']; + + $DataObjectData = $NextObjectDataHeader.$this->fread(50 - 24); + $offset = 24; + + $thisfile_asf_dataobject['objectid'] = $NextObjectGUID; + $thisfile_asf_dataobject['objectid_guid'] = $NextObjectGUIDtext; + $thisfile_asf_dataobject['objectsize'] = $NextObjectSize; + + $thisfile_asf_dataobject['fileid'] = substr($DataObjectData, $offset, 16); + $offset += 16; + $thisfile_asf_dataobject['fileid_guid'] = $this->BytestringToGUID($thisfile_asf_dataobject['fileid']); + $thisfile_asf_dataobject['total_data_packets'] = getid3_lib::LittleEndian2Int(substr($DataObjectData, $offset, 8)); + $offset += 8; + $thisfile_asf_dataobject['reserved'] = getid3_lib::LittleEndian2Int(substr($DataObjectData, $offset, 2)); + $offset += 2; + if ($thisfile_asf_dataobject['reserved'] != 0x0101) { + $info['warning'][] = 'data_object.reserved ('.getid3_lib::PrintHexBytes($thisfile_asf_dataobject['reserved']).') does not match expected value of "0x0101"'; + //return false; + break; + } + + // Data Packets array of: variable // + // * Error Correction Flags BYTE 8 // + // * * Error Correction Data Length bits 4 // if Error Correction Length Type == 00, size of Error Correction Data in bytes, else hardcoded: 0000 + // * * Opaque Data Present bits 1 // + // * * Error Correction Length Type bits 2 // number of bits for size of the error correction data. hardcoded: 00 + // * * Error Correction Present bits 1 // If set, use Opaque Data Packet structure, else use Payload structure + // * Error Correction Data + + $info['avdataoffset'] = $this->ftell(); + $this->fseek(($thisfile_asf_dataobject['objectsize'] - 50), SEEK_CUR); // skip actual audio/video data + $info['avdataend'] = $this->ftell(); + break; + + case GETID3_ASF_Simple_Index_Object: + // Simple Index Object: (optional, recommended, one per video stream) + // Field Name Field Type Size (bits) + // Object ID GUID 128 // GUID for Simple Index object - GETID3_ASF_Data_Object + // Object Size QWORD 64 // size of Simple Index object, including 56 bytes of Simple Index Object header + // File ID GUID 128 // unique identifier. may be zero or identical to File ID field in Data Object and Header Object + // Index Entry Time Interval QWORD 64 // interval between index entries in 100-nanosecond units + // Maximum Packet Count DWORD 32 // maximum packet count for all index entries + // Index Entries Count DWORD 32 // number of Index Entries structures + // Index Entries array of: variable // + // * Packet Number DWORD 32 // number of the Data Packet associated with this index entry + // * Packet Count WORD 16 // number of Data Packets to sent at this index entry + + // shortcut + $thisfile_asf['simple_index_object'] = array(); + $thisfile_asf_simpleindexobject = &$thisfile_asf['simple_index_object']; + + $SimpleIndexObjectData = $NextObjectDataHeader.$this->fread(56 - 24); + $offset = 24; + + $thisfile_asf_simpleindexobject['objectid'] = $NextObjectGUID; + $thisfile_asf_simpleindexobject['objectid_guid'] = $NextObjectGUIDtext; + $thisfile_asf_simpleindexobject['objectsize'] = $NextObjectSize; + + $thisfile_asf_simpleindexobject['fileid'] = substr($SimpleIndexObjectData, $offset, 16); + $offset += 16; + $thisfile_asf_simpleindexobject['fileid_guid'] = $this->BytestringToGUID($thisfile_asf_simpleindexobject['fileid']); + $thisfile_asf_simpleindexobject['index_entry_time_interval'] = getid3_lib::LittleEndian2Int(substr($SimpleIndexObjectData, $offset, 8)); + $offset += 8; + $thisfile_asf_simpleindexobject['maximum_packet_count'] = getid3_lib::LittleEndian2Int(substr($SimpleIndexObjectData, $offset, 4)); + $offset += 4; + $thisfile_asf_simpleindexobject['index_entries_count'] = getid3_lib::LittleEndian2Int(substr($SimpleIndexObjectData, $offset, 4)); + $offset += 4; + + $IndexEntriesData = $SimpleIndexObjectData.$this->fread(6 * $thisfile_asf_simpleindexobject['index_entries_count']); + for ($IndexEntriesCounter = 0; $IndexEntriesCounter < $thisfile_asf_simpleindexobject['index_entries_count']; $IndexEntriesCounter++) { + $thisfile_asf_simpleindexobject['index_entries'][$IndexEntriesCounter]['packet_number'] = getid3_lib::LittleEndian2Int(substr($IndexEntriesData, $offset, 4)); + $offset += 4; + $thisfile_asf_simpleindexobject['index_entries'][$IndexEntriesCounter]['packet_count'] = getid3_lib::LittleEndian2Int(substr($IndexEntriesData, $offset, 4)); + $offset += 2; + } + + break; + + case GETID3_ASF_Index_Object: + // 6.2 ASF top-level Index Object (optional but recommended when appropriate, 0 or 1) + // Field Name Field Type Size (bits) + // Object ID GUID 128 // GUID for the Index Object - GETID3_ASF_Index_Object + // Object Size QWORD 64 // Specifies the size, in bytes, of the Index Object, including at least 34 bytes of Index Object header + // Index Entry Time Interval DWORD 32 // Specifies the time interval between each index entry in ms. + // Index Specifiers Count WORD 16 // Specifies the number of Index Specifiers structures in this Index Object. + // Index Blocks Count DWORD 32 // Specifies the number of Index Blocks structures in this Index Object. + + // Index Entry Time Interval DWORD 32 // Specifies the time interval between index entries in milliseconds. This value cannot be 0. + // Index Specifiers Count WORD 16 // Specifies the number of entries in the Index Specifiers list. Valid values are 1 and greater. + // Index Specifiers array of: varies // + // * Stream Number WORD 16 // Specifies the stream number that the Index Specifiers refer to. Valid values are between 1 and 127. + // * Index Type WORD 16 // Specifies Index Type values as follows: + // 1 = Nearest Past Data Packet - indexes point to the data packet whose presentation time is closest to the index entry time. + // 2 = Nearest Past Media Object - indexes point to the closest data packet containing an entire object or first fragment of an object. + // 3 = Nearest Past Cleanpoint. - indexes point to the closest data packet containing an entire object (or first fragment of an object) that has the Cleanpoint Flag set. + // Nearest Past Cleanpoint is the most common type of index. + // Index Entry Count DWORD 32 // Specifies the number of Index Entries in the block. + // * Block Positions QWORD varies // Specifies a list of byte offsets of the beginnings of the blocks relative to the beginning of the first Data Packet (i.e., the beginning of the Data Object + 50 bytes). The number of entries in this list is specified by the value of the Index Specifiers Count field. The order of those byte offsets is tied to the order in which Index Specifiers are listed. + // * Index Entries array of: varies // + // * * Offsets DWORD varies // An offset value of 0xffffffff indicates an invalid offset value + + // shortcut + $thisfile_asf['asf_index_object'] = array(); + $thisfile_asf_asfindexobject = &$thisfile_asf['asf_index_object']; + + $ASFIndexObjectData = $NextObjectDataHeader.$this->fread(34 - 24); + $offset = 24; + + $thisfile_asf_asfindexobject['objectid'] = $NextObjectGUID; + $thisfile_asf_asfindexobject['objectid_guid'] = $NextObjectGUIDtext; + $thisfile_asf_asfindexobject['objectsize'] = $NextObjectSize; + + $thisfile_asf_asfindexobject['entry_time_interval'] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 4)); + $offset += 4; + $thisfile_asf_asfindexobject['index_specifiers_count'] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 2)); + $offset += 2; + $thisfile_asf_asfindexobject['index_blocks_count'] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 4)); + $offset += 4; + + $ASFIndexObjectData .= $this->fread(4 * $thisfile_asf_asfindexobject['index_specifiers_count']); + for ($IndexSpecifiersCounter = 0; $IndexSpecifiersCounter < $thisfile_asf_asfindexobject['index_specifiers_count']; $IndexSpecifiersCounter++) { + $IndexSpecifierStreamNumber = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 2)); + $offset += 2; + $thisfile_asf_asfindexobject['index_specifiers'][$IndexSpecifiersCounter]['stream_number'] = $IndexSpecifierStreamNumber; + $thisfile_asf_asfindexobject['index_specifiers'][$IndexSpecifiersCounter]['index_type'] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 2)); + $offset += 2; + $thisfile_asf_asfindexobject['index_specifiers'][$IndexSpecifiersCounter]['index_type_text'] = $this->ASFIndexObjectIndexTypeLookup($thisfile_asf_asfindexobject['index_specifiers'][$IndexSpecifiersCounter]['index_type']); + } + + $ASFIndexObjectData .= $this->fread(4); + $thisfile_asf_asfindexobject['index_entry_count'] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 4)); + $offset += 4; + + $ASFIndexObjectData .= $this->fread(8 * $thisfile_asf_asfindexobject['index_specifiers_count']); + for ($IndexSpecifiersCounter = 0; $IndexSpecifiersCounter < $thisfile_asf_asfindexobject['index_specifiers_count']; $IndexSpecifiersCounter++) { + $thisfile_asf_asfindexobject['block_positions'][$IndexSpecifiersCounter] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 8)); + $offset += 8; + } + + $ASFIndexObjectData .= $this->fread(4 * $thisfile_asf_asfindexobject['index_specifiers_count'] * $thisfile_asf_asfindexobject['index_entry_count']); + for ($IndexEntryCounter = 0; $IndexEntryCounter < $thisfile_asf_asfindexobject['index_entry_count']; $IndexEntryCounter++) { + for ($IndexSpecifiersCounter = 0; $IndexSpecifiersCounter < $thisfile_asf_asfindexobject['index_specifiers_count']; $IndexSpecifiersCounter++) { + $thisfile_asf_asfindexobject['offsets'][$IndexSpecifiersCounter][$IndexEntryCounter] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 4)); + $offset += 4; + } + } + break; + + + default: + // Implementations shall ignore any standard or non-standard object that they do not know how to handle. + if ($this->GUIDname($NextObjectGUIDtext)) { + $info['warning'][] = 'unhandled GUID "'.$this->GUIDname($NextObjectGUIDtext).'" {'.$NextObjectGUIDtext.'} in ASF body at offset '.($offset - 16 - 8); + } else { + $info['warning'][] = 'unknown GUID {'.$NextObjectGUIDtext.'} in ASF body at offset '.($this->ftell() - 16 - 8); + } + $this->fseek(($NextObjectSize - 16 - 8), SEEK_CUR); + break; + } + } + + if (isset($thisfile_asf_codeclistobject['codec_entries']) && is_array($thisfile_asf_codeclistobject['codec_entries'])) { + foreach ($thisfile_asf_codeclistobject['codec_entries'] as $streamnumber => $streamdata) { + switch ($streamdata['information']) { + case 'WMV1': + case 'WMV2': + case 'WMV3': + case 'MSS1': + case 'MSS2': + case 'WMVA': + case 'WVC1': + case 'WMVP': + case 'WVP2': + $thisfile_video['dataformat'] = 'wmv'; + $info['mime_type'] = 'video/x-ms-wmv'; + break; + + case 'MP42': + case 'MP43': + case 'MP4S': + case 'mp4s': + $thisfile_video['dataformat'] = 'asf'; + $info['mime_type'] = 'video/x-ms-asf'; + break; + + default: + switch ($streamdata['type_raw']) { + case 1: + if (strstr($this->TrimConvert($streamdata['name']), 'Windows Media')) { + $thisfile_video['dataformat'] = 'wmv'; + if ($info['mime_type'] == 'video/x-ms-asf') { + $info['mime_type'] = 'video/x-ms-wmv'; + } + } + break; + + case 2: + if (strstr($this->TrimConvert($streamdata['name']), 'Windows Media')) { + $thisfile_audio['dataformat'] = 'wma'; + if ($info['mime_type'] == 'video/x-ms-asf') { + $info['mime_type'] = 'audio/x-ms-wma'; + } + } + break; + + } + break; + } + } + } + + switch (isset($thisfile_audio['codec']) ? $thisfile_audio['codec'] : '') { + case 'MPEG Layer-3': + $thisfile_audio['dataformat'] = 'mp3'; + break; + + default: + break; + } + + if (isset($thisfile_asf_codeclistobject['codec_entries'])) { + foreach ($thisfile_asf_codeclistobject['codec_entries'] as $streamnumber => $streamdata) { + switch ($streamdata['type_raw']) { + + case 1: // video + $thisfile_video['encoder'] = $this->TrimConvert($thisfile_asf_codeclistobject['codec_entries'][$streamnumber]['name']); + break; + + case 2: // audio + $thisfile_audio['encoder'] = $this->TrimConvert($thisfile_asf_codeclistobject['codec_entries'][$streamnumber]['name']); + + // AH 2003-10-01 + $thisfile_audio['encoder_options'] = $this->TrimConvert($thisfile_asf_codeclistobject['codec_entries'][0]['description']); + + $thisfile_audio['codec'] = $thisfile_audio['encoder']; + break; + + default: + $info['warning'][] = 'Unknown streamtype: [codec_list_object][codec_entries]['.$streamnumber.'][type_raw] == '.$streamdata['type_raw']; + break; + + } + } + } + + if (isset($info['audio'])) { + $thisfile_audio['lossless'] = (isset($thisfile_audio['lossless']) ? $thisfile_audio['lossless'] : false); + $thisfile_audio['dataformat'] = (!empty($thisfile_audio['dataformat']) ? $thisfile_audio['dataformat'] : 'asf'); + } + if (!empty($thisfile_video['dataformat'])) { + $thisfile_video['lossless'] = (isset($thisfile_audio['lossless']) ? $thisfile_audio['lossless'] : false); + $thisfile_video['pixel_aspect_ratio'] = (isset($thisfile_audio['pixel_aspect_ratio']) ? $thisfile_audio['pixel_aspect_ratio'] : (float) 1); + $thisfile_video['dataformat'] = (!empty($thisfile_video['dataformat']) ? $thisfile_video['dataformat'] : 'asf'); + } + if (!empty($thisfile_video['streams'])) { + $thisfile_video['resolution_x'] = 0; + $thisfile_video['resolution_y'] = 0; + foreach ($thisfile_video['streams'] as $key => $valuearray) { + if (($valuearray['resolution_x'] > $thisfile_video['resolution_x']) || ($valuearray['resolution_y'] > $thisfile_video['resolution_y'])) { + $thisfile_video['resolution_x'] = $valuearray['resolution_x']; + $thisfile_video['resolution_y'] = $valuearray['resolution_y']; + } + } + } + $info['bitrate'] = (isset($thisfile_audio['bitrate']) ? $thisfile_audio['bitrate'] : 0) + (isset($thisfile_video['bitrate']) ? $thisfile_video['bitrate'] : 0); + + if ((!isset($info['playtime_seconds']) || ($info['playtime_seconds'] <= 0)) && ($info['bitrate'] > 0)) { + $info['playtime_seconds'] = ($info['filesize'] - $info['avdataoffset']) / ($info['bitrate'] / 8); + } + + return true; + } + + public static function codecListObjectTypeLookup($CodecListType) { + static $lookup = array( + 0x0001 => 'Video Codec', + 0x0002 => 'Audio Codec', + 0xFFFF => 'Unknown Codec' + ); + + return (isset($lookup[$CodecListType]) ? $lookup[$CodecListType] : 'Invalid Codec Type'); + } + + public static function KnownGUIDs() { + static $GUIDarray = array( + 'GETID3_ASF_Extended_Stream_Properties_Object' => '14E6A5CB-C672-4332-8399-A96952065B5A', + 'GETID3_ASF_Padding_Object' => '1806D474-CADF-4509-A4BA-9AABCB96AAE8', + 'GETID3_ASF_Payload_Ext_Syst_Pixel_Aspect_Ratio' => '1B1EE554-F9EA-4BC8-821A-376B74E4C4B8', + 'GETID3_ASF_Script_Command_Object' => '1EFB1A30-0B62-11D0-A39B-00A0C90348F6', + 'GETID3_ASF_No_Error_Correction' => '20FB5700-5B55-11CF-A8FD-00805F5C442B', + 'GETID3_ASF_Content_Branding_Object' => '2211B3FA-BD23-11D2-B4B7-00A0C955FC6E', + 'GETID3_ASF_Content_Encryption_Object' => '2211B3FB-BD23-11D2-B4B7-00A0C955FC6E', + 'GETID3_ASF_Digital_Signature_Object' => '2211B3FC-BD23-11D2-B4B7-00A0C955FC6E', + 'GETID3_ASF_Extended_Content_Encryption_Object' => '298AE614-2622-4C17-B935-DAE07EE9289C', + 'GETID3_ASF_Simple_Index_Object' => '33000890-E5B1-11CF-89F4-00A0C90349CB', + 'GETID3_ASF_Degradable_JPEG_Media' => '35907DE0-E415-11CF-A917-00805F5C442B', + 'GETID3_ASF_Payload_Extension_System_Timecode' => '399595EC-8667-4E2D-8FDB-98814CE76C1E', + 'GETID3_ASF_Binary_Media' => '3AFB65E2-47EF-40F2-AC2C-70A90D71D343', + 'GETID3_ASF_Timecode_Index_Object' => '3CB73FD0-0C4A-4803-953D-EDF7B6228F0C', + 'GETID3_ASF_Metadata_Library_Object' => '44231C94-9498-49D1-A141-1D134E457054', + 'GETID3_ASF_Reserved_3' => '4B1ACBE3-100B-11D0-A39B-00A0C90348F6', + 'GETID3_ASF_Reserved_4' => '4CFEDB20-75F6-11CF-9C0F-00A0C90349CB', + 'GETID3_ASF_Command_Media' => '59DACFC0-59E6-11D0-A3AC-00A0C90348F6', + 'GETID3_ASF_Header_Extension_Object' => '5FBF03B5-A92E-11CF-8EE3-00C00C205365', + 'GETID3_ASF_Media_Object_Index_Parameters_Obj' => '6B203BAD-3F11-4E84-ACA8-D7613DE2CFA7', + 'GETID3_ASF_Header_Object' => '75B22630-668E-11CF-A6D9-00AA0062CE6C', + 'GETID3_ASF_Content_Description_Object' => '75B22633-668E-11CF-A6D9-00AA0062CE6C', + 'GETID3_ASF_Error_Correction_Object' => '75B22635-668E-11CF-A6D9-00AA0062CE6C', + 'GETID3_ASF_Data_Object' => '75B22636-668E-11CF-A6D9-00AA0062CE6C', + 'GETID3_ASF_Web_Stream_Media_Subtype' => '776257D4-C627-41CB-8F81-7AC7FF1C40CC', + 'GETID3_ASF_Stream_Bitrate_Properties_Object' => '7BF875CE-468D-11D1-8D82-006097C9A2B2', + 'GETID3_ASF_Language_List_Object' => '7C4346A9-EFE0-4BFC-B229-393EDE415C85', + 'GETID3_ASF_Codec_List_Object' => '86D15240-311D-11D0-A3A4-00A0C90348F6', + 'GETID3_ASF_Reserved_2' => '86D15241-311D-11D0-A3A4-00A0C90348F6', + 'GETID3_ASF_File_Properties_Object' => '8CABDCA1-A947-11CF-8EE4-00C00C205365', + 'GETID3_ASF_File_Transfer_Media' => '91BD222C-F21C-497A-8B6D-5AA86BFC0185', + 'GETID3_ASF_Old_RTP_Extension_Data' => '96800C63-4C94-11D1-837B-0080C7A37F95', + 'GETID3_ASF_Advanced_Mutual_Exclusion_Object' => 'A08649CF-4775-4670-8A16-6E35357566CD', + 'GETID3_ASF_Bandwidth_Sharing_Object' => 'A69609E6-517B-11D2-B6AF-00C04FD908E9', + 'GETID3_ASF_Reserved_1' => 'ABD3D211-A9BA-11cf-8EE6-00C00C205365', + 'GETID3_ASF_Bandwidth_Sharing_Exclusive' => 'AF6060AA-5197-11D2-B6AF-00C04FD908E9', + 'GETID3_ASF_Bandwidth_Sharing_Partial' => 'AF6060AB-5197-11D2-B6AF-00C04FD908E9', + 'GETID3_ASF_JFIF_Media' => 'B61BE100-5B4E-11CF-A8FD-00805F5C442B', + 'GETID3_ASF_Stream_Properties_Object' => 'B7DC0791-A9B7-11CF-8EE6-00C00C205365', + 'GETID3_ASF_Video_Media' => 'BC19EFC0-5B4D-11CF-A8FD-00805F5C442B', + 'GETID3_ASF_Audio_Spread' => 'BFC3CD50-618F-11CF-8BB2-00AA00B4E220', + 'GETID3_ASF_Metadata_Object' => 'C5F8CBEA-5BAF-4877-8467-AA8C44FA4CCA', + 'GETID3_ASF_Payload_Ext_Syst_Sample_Duration' => 'C6BD9450-867F-4907-83A3-C77921B733AD', + 'GETID3_ASF_Group_Mutual_Exclusion_Object' => 'D1465A40-5A79-4338-B71B-E36B8FD6C249', + 'GETID3_ASF_Extended_Content_Description_Object' => 'D2D0A440-E307-11D2-97F0-00A0C95EA850', + 'GETID3_ASF_Stream_Prioritization_Object' => 'D4FED15B-88D3-454F-81F0-ED5C45999E24', + 'GETID3_ASF_Payload_Ext_System_Content_Type' => 'D590DC20-07BC-436C-9CF7-F3BBFBF1A4DC', + 'GETID3_ASF_Old_File_Properties_Object' => 'D6E229D0-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_ASF_Header_Object' => 'D6E229D1-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_ASF_Data_Object' => 'D6E229D2-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Index_Object' => 'D6E229D3-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Stream_Properties_Object' => 'D6E229D4-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Content_Description_Object' => 'D6E229D5-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Script_Command_Object' => 'D6E229D6-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Marker_Object' => 'D6E229D7-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Component_Download_Object' => 'D6E229D8-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Stream_Group_Object' => 'D6E229D9-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Scalable_Object' => 'D6E229DA-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Prioritization_Object' => 'D6E229DB-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Bitrate_Mutual_Exclusion_Object' => 'D6E229DC-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Inter_Media_Dependency_Object' => 'D6E229DD-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Rating_Object' => 'D6E229DE-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Index_Parameters_Object' => 'D6E229DF-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Color_Table_Object' => 'D6E229E0-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Language_List_Object' => 'D6E229E1-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Audio_Media' => 'D6E229E2-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Video_Media' => 'D6E229E3-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Image_Media' => 'D6E229E4-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Timecode_Media' => 'D6E229E5-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Text_Media' => 'D6E229E6-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_MIDI_Media' => 'D6E229E7-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Command_Media' => 'D6E229E8-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_No_Error_Concealment' => 'D6E229EA-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Scrambled_Audio' => 'D6E229EB-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_No_Color_Table' => 'D6E229EC-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_SMPTE_Time' => 'D6E229ED-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_ASCII_Text' => 'D6E229EE-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Unicode_Text' => 'D6E229EF-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_HTML_Text' => 'D6E229F0-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_URL_Command' => 'D6E229F1-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Filename_Command' => 'D6E229F2-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_ACM_Codec' => 'D6E229F3-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_VCM_Codec' => 'D6E229F4-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_QuickTime_Codec' => 'D6E229F5-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_DirectShow_Transform_Filter' => 'D6E229F6-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_DirectShow_Rendering_Filter' => 'D6E229F7-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_No_Enhancement' => 'D6E229F8-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Unknown_Enhancement_Type' => 'D6E229F9-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Temporal_Enhancement' => 'D6E229FA-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Spatial_Enhancement' => 'D6E229FB-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Quality_Enhancement' => 'D6E229FC-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Number_of_Channels_Enhancement' => 'D6E229FD-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Frequency_Response_Enhancement' => 'D6E229FE-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Media_Object' => 'D6E229FF-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Mutex_Language' => 'D6E22A00-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Mutex_Bitrate' => 'D6E22A01-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Mutex_Unknown' => 'D6E22A02-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_ASF_Placeholder_Object' => 'D6E22A0E-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Old_Data_Unit_Extension_Object' => 'D6E22A0F-35DA-11D1-9034-00A0C90349BE', + 'GETID3_ASF_Web_Stream_Format' => 'DA1E6B13-8359-4050-B398-388E965BF00C', + 'GETID3_ASF_Payload_Ext_System_File_Name' => 'E165EC0E-19ED-45D7-B4A7-25CBD1E28E9B', + 'GETID3_ASF_Marker_Object' => 'F487CD01-A951-11CF-8EE6-00C00C205365', + 'GETID3_ASF_Timecode_Index_Parameters_Object' => 'F55E496D-9797-4B5D-8C8B-604DFE9BFB24', + 'GETID3_ASF_Audio_Media' => 'F8699E40-5B4D-11CF-A8FD-00805F5C442B', + 'GETID3_ASF_Media_Object_Index_Object' => 'FEB103F8-12AD-4C64-840F-2A1D2F7AD48C', + 'GETID3_ASF_Alt_Extended_Content_Encryption_Obj' => 'FF889EF1-ADEE-40DA-9E71-98704BB928CE', + 'GETID3_ASF_Index_Placeholder_Object' => 'D9AADE20-7C17-4F9C-BC28-8555DD98E2A2', // http://cpan.uwinnipeg.ca/htdocs/Audio-WMA/Audio/WMA.pm.html + 'GETID3_ASF_Compatibility_Object' => '26F18B5D-4584-47EC-9F5F-0E651F0452C9', // http://cpan.uwinnipeg.ca/htdocs/Audio-WMA/Audio/WMA.pm.html + ); + return $GUIDarray; + } + + public static function GUIDname($GUIDstring) { + static $GUIDarray = array(); + if (empty($GUIDarray)) { + $GUIDarray = self::KnownGUIDs(); + } + return array_search($GUIDstring, $GUIDarray); + } + + public static function ASFIndexObjectIndexTypeLookup($id) { + static $ASFIndexObjectIndexTypeLookup = array(); + if (empty($ASFIndexObjectIndexTypeLookup)) { + $ASFIndexObjectIndexTypeLookup[1] = 'Nearest Past Data Packet'; + $ASFIndexObjectIndexTypeLookup[2] = 'Nearest Past Media Object'; + $ASFIndexObjectIndexTypeLookup[3] = 'Nearest Past Cleanpoint'; + } + return (isset($ASFIndexObjectIndexTypeLookup[$id]) ? $ASFIndexObjectIndexTypeLookup[$id] : 'invalid'); + } + + public static function GUIDtoBytestring($GUIDstring) { + // Microsoft defines these 16-byte (128-bit) GUIDs in the strangest way: + // first 4 bytes are in little-endian order + // next 2 bytes are appended in little-endian order + // next 2 bytes are appended in little-endian order + // next 2 bytes are appended in big-endian order + // next 6 bytes are appended in big-endian order + + // AaBbCcDd-EeFf-GgHh-IiJj-KkLlMmNnOoPp is stored as this 16-byte string: + // $Dd $Cc $Bb $Aa $Ff $Ee $Hh $Gg $Ii $Jj $Kk $Ll $Mm $Nn $Oo $Pp + + $hexbytecharstring = chr(hexdec(substr($GUIDstring, 6, 2))); + $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 4, 2))); + $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 2, 2))); + $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 0, 2))); + + $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 11, 2))); + $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 9, 2))); + + $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 16, 2))); + $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 14, 2))); + + $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 19, 2))); + $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 21, 2))); + + $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 24, 2))); + $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 26, 2))); + $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 28, 2))); + $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 30, 2))); + $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 32, 2))); + $hexbytecharstring .= chr(hexdec(substr($GUIDstring, 34, 2))); + + return $hexbytecharstring; + } + + public static function BytestringToGUID($Bytestring) { + $GUIDstring = str_pad(dechex(ord($Bytestring{3})), 2, '0', STR_PAD_LEFT); + $GUIDstring .= str_pad(dechex(ord($Bytestring{2})), 2, '0', STR_PAD_LEFT); + $GUIDstring .= str_pad(dechex(ord($Bytestring{1})), 2, '0', STR_PAD_LEFT); + $GUIDstring .= str_pad(dechex(ord($Bytestring{0})), 2, '0', STR_PAD_LEFT); + $GUIDstring .= '-'; + $GUIDstring .= str_pad(dechex(ord($Bytestring{5})), 2, '0', STR_PAD_LEFT); + $GUIDstring .= str_pad(dechex(ord($Bytestring{4})), 2, '0', STR_PAD_LEFT); + $GUIDstring .= '-'; + $GUIDstring .= str_pad(dechex(ord($Bytestring{7})), 2, '0', STR_PAD_LEFT); + $GUIDstring .= str_pad(dechex(ord($Bytestring{6})), 2, '0', STR_PAD_LEFT); + $GUIDstring .= '-'; + $GUIDstring .= str_pad(dechex(ord($Bytestring{8})), 2, '0', STR_PAD_LEFT); + $GUIDstring .= str_pad(dechex(ord($Bytestring{9})), 2, '0', STR_PAD_LEFT); + $GUIDstring .= '-'; + $GUIDstring .= str_pad(dechex(ord($Bytestring{10})), 2, '0', STR_PAD_LEFT); + $GUIDstring .= str_pad(dechex(ord($Bytestring{11})), 2, '0', STR_PAD_LEFT); + $GUIDstring .= str_pad(dechex(ord($Bytestring{12})), 2, '0', STR_PAD_LEFT); + $GUIDstring .= str_pad(dechex(ord($Bytestring{13})), 2, '0', STR_PAD_LEFT); + $GUIDstring .= str_pad(dechex(ord($Bytestring{14})), 2, '0', STR_PAD_LEFT); + $GUIDstring .= str_pad(dechex(ord($Bytestring{15})), 2, '0', STR_PAD_LEFT); + + return strtoupper($GUIDstring); + } + + public static function FILETIMEtoUNIXtime($FILETIME, $round=true) { + // FILETIME is a 64-bit unsigned integer representing + // the number of 100-nanosecond intervals since January 1, 1601 + // UNIX timestamp is number of seconds since January 1, 1970 + // 116444736000000000 = 10000000 * 60 * 60 * 24 * 365 * 369 + 89 leap days + if ($round) { + return intval(round(($FILETIME - 116444736000000000) / 10000000)); + } + return ($FILETIME - 116444736000000000) / 10000000; + } + + public static function WMpictureTypeLookup($WMpictureType) { + static $lookup = null; + if ($lookup === null) { + $lookup = array( + 0x03 => 'Front Cover', + 0x04 => 'Back Cover', + 0x00 => 'User Defined', + 0x05 => 'Leaflet Page', + 0x06 => 'Media Label', + 0x07 => 'Lead Artist', + 0x08 => 'Artist', + 0x09 => 'Conductor', + 0x0A => 'Band', + 0x0B => 'Composer', + 0x0C => 'Lyricist', + 0x0D => 'Recording Location', + 0x0E => 'During Recording', + 0x0F => 'During Performance', + 0x10 => 'Video Screen Capture', + 0x12 => 'Illustration', + 0x13 => 'Band Logotype', + 0x14 => 'Publisher Logotype' + ); + $lookup = array_map(function($str) { + return getid3_lib::iconv_fallback('UTF-8', 'UTF-16LE', $str); + }, $lookup); + } + + return (isset($lookup[$WMpictureType]) ? $lookup[$WMpictureType] : ''); + } + + public function HeaderExtensionObjectDataParse(&$asf_header_extension_object_data, &$unhandled_sections) { + // http://msdn.microsoft.com/en-us/library/bb643323.aspx + + $offset = 0; + $objectOffset = 0; + $HeaderExtensionObjectParsed = array(); + while ($objectOffset < strlen($asf_header_extension_object_data)) { + $offset = $objectOffset; + $thisObject = array(); + + $thisObject['guid'] = substr($asf_header_extension_object_data, $offset, 16); + $offset += 16; + $thisObject['guid_text'] = $this->BytestringToGUID($thisObject['guid']); + $thisObject['guid_name'] = $this->GUIDname($thisObject['guid_text']); + + $thisObject['size'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 8)); + $offset += 8; + if ($thisObject['size'] <= 0) { + break; + } + + switch ($thisObject['guid']) { + case GETID3_ASF_Extended_Stream_Properties_Object: + $thisObject['start_time'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 8)); + $offset += 8; + $thisObject['start_time_unix'] = $this->FILETIMEtoUNIXtime($thisObject['start_time']); + + $thisObject['end_time'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 8)); + $offset += 8; + $thisObject['end_time_unix'] = $this->FILETIMEtoUNIXtime($thisObject['end_time']); + + $thisObject['data_bitrate'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4)); + $offset += 4; + + $thisObject['buffer_size'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4)); + $offset += 4; + + $thisObject['initial_buffer_fullness'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4)); + $offset += 4; + + $thisObject['alternate_data_bitrate'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4)); + $offset += 4; + + $thisObject['alternate_buffer_size'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4)); + $offset += 4; + + $thisObject['alternate_initial_buffer_fullness'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4)); + $offset += 4; + + $thisObject['maximum_object_size'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4)); + $offset += 4; + + $thisObject['flags_raw'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4)); + $offset += 4; + $thisObject['flags']['reliable'] = (bool) $thisObject['flags_raw'] & 0x00000001; + $thisObject['flags']['seekable'] = (bool) $thisObject['flags_raw'] & 0x00000002; + $thisObject['flags']['no_cleanpoints'] = (bool) $thisObject['flags_raw'] & 0x00000004; + $thisObject['flags']['resend_live_cleanpoints'] = (bool) $thisObject['flags_raw'] & 0x00000008; + + $thisObject['stream_number'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + $thisObject['stream_language_id_index'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + $thisObject['average_time_per_frame'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4)); + $offset += 4; + + $thisObject['stream_name_count'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + $thisObject['payload_extension_system_count'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + for ($i = 0; $i < $thisObject['stream_name_count']; $i++) { + $streamName = array(); + + $streamName['language_id_index'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + $streamName['stream_name_length'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + $streamName['stream_name'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, $streamName['stream_name_length'])); + $offset += $streamName['stream_name_length']; + + $thisObject['stream_names'][$i] = $streamName; + } + + for ($i = 0; $i < $thisObject['payload_extension_system_count']; $i++) { + $payloadExtensionSystem = array(); + + $payloadExtensionSystem['extension_system_id'] = substr($asf_header_extension_object_data, $offset, 16); + $offset += 16; + $payloadExtensionSystem['extension_system_id_text'] = $this->BytestringToGUID($payloadExtensionSystem['extension_system_id']); + + $payloadExtensionSystem['extension_system_size'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + if ($payloadExtensionSystem['extension_system_size'] <= 0) { + break 2; + } + + $payloadExtensionSystem['extension_system_info_length'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4)); + $offset += 4; + + $payloadExtensionSystem['extension_system_info_length'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, $payloadExtensionSystem['extension_system_info_length'])); + $offset += $payloadExtensionSystem['extension_system_info_length']; + + $thisObject['payload_extension_systems'][$i] = $payloadExtensionSystem; + } + + break; + + case GETID3_ASF_Padding_Object: + // padding, skip it + break; + + case GETID3_ASF_Metadata_Object: + $thisObject['description_record_counts'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + for ($i = 0; $i < $thisObject['description_record_counts']; $i++) { + $descriptionRecord = array(); + + $descriptionRecord['reserved_1'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); // must be zero + $offset += 2; + + $descriptionRecord['stream_number'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + $descriptionRecord['name_length'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + $descriptionRecord['data_type'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + $descriptionRecord['data_type_text'] = self::metadataLibraryObjectDataTypeLookup($descriptionRecord['data_type']); + + $descriptionRecord['data_length'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4)); + $offset += 4; + + $descriptionRecord['name'] = substr($asf_header_extension_object_data, $offset, $descriptionRecord['name_length']); + $offset += $descriptionRecord['name_length']; + + $descriptionRecord['data'] = substr($asf_header_extension_object_data, $offset, $descriptionRecord['data_length']); + $offset += $descriptionRecord['data_length']; + switch ($descriptionRecord['data_type']) { + case 0x0000: // Unicode string + break; + + case 0x0001: // BYTE array + // do nothing + break; + + case 0x0002: // BOOL + $descriptionRecord['data'] = (bool) getid3_lib::LittleEndian2Int($descriptionRecord['data']); + break; + + case 0x0003: // DWORD + case 0x0004: // QWORD + case 0x0005: // WORD + $descriptionRecord['data'] = getid3_lib::LittleEndian2Int($descriptionRecord['data']); + break; + + case 0x0006: // GUID + $descriptionRecord['data_text'] = $this->BytestringToGUID($descriptionRecord['data']); + break; + } + + $thisObject['description_record'][$i] = $descriptionRecord; + } + break; + + case GETID3_ASF_Language_List_Object: + $thisObject['language_id_record_counts'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + for ($i = 0; $i < $thisObject['language_id_record_counts']; $i++) { + $languageIDrecord = array(); + + $languageIDrecord['language_id_length'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 1)); + $offset += 1; + + $languageIDrecord['language_id'] = substr($asf_header_extension_object_data, $offset, $languageIDrecord['language_id_length']); + $offset += $languageIDrecord['language_id_length']; + + $thisObject['language_id_record'][$i] = $languageIDrecord; + } + break; + + case GETID3_ASF_Metadata_Library_Object: + $thisObject['description_records_count'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + for ($i = 0; $i < $thisObject['description_records_count']; $i++) { + $descriptionRecord = array(); + + $descriptionRecord['language_list_index'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + $descriptionRecord['stream_number'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + $descriptionRecord['name_length'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + + $descriptionRecord['data_type'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 2)); + $offset += 2; + $descriptionRecord['data_type_text'] = self::metadataLibraryObjectDataTypeLookup($descriptionRecord['data_type']); + + $descriptionRecord['data_length'] = getid3_lib::LittleEndian2Int(substr($asf_header_extension_object_data, $offset, 4)); + $offset += 4; + + $descriptionRecord['name'] = substr($asf_header_extension_object_data, $offset, $descriptionRecord['name_length']); + $offset += $descriptionRecord['name_length']; + + $descriptionRecord['data'] = substr($asf_header_extension_object_data, $offset, $descriptionRecord['data_length']); + $offset += $descriptionRecord['data_length']; + + if (preg_match('#^WM/Picture$#', str_replace("\x00", '', trim($descriptionRecord['name'])))) { + $WMpicture = $this->ASF_WMpicture($descriptionRecord['data']); + foreach ($WMpicture as $key => $value) { + $descriptionRecord['data'] = $WMpicture; + } + unset($WMpicture); + } + + $thisObject['description_record'][$i] = $descriptionRecord; + } + break; + + default: + $unhandled_sections++; + if ($this->GUIDname($thisObject['guid_text'])) { + $this->getid3->info['warning'][] = 'unhandled Header Extension Object GUID "'.$this->GUIDname($thisObject['guid_text']).'" {'.$thisObject['guid_text'].'} at offset '.($offset - 16 - 8); + } else { + $this->getid3->info['warning'][] = 'unknown Header Extension Object GUID {'.$thisObject['guid_text'].'} in at offset '.($offset - 16 - 8); + } + break; + } + $HeaderExtensionObjectParsed[] = $thisObject; + + $objectOffset += $thisObject['size']; + } + return $HeaderExtensionObjectParsed; + } + + + public static function metadataLibraryObjectDataTypeLookup($id) { + static $lookup = array( + 0x0000 => 'Unicode string', // The data consists of a sequence of Unicode characters + 0x0001 => 'BYTE array', // The type of the data is implementation-specific + 0x0002 => 'BOOL', // The data is 2 bytes long and should be interpreted as a 16-bit unsigned integer. Only 0x0000 or 0x0001 are permitted values + 0x0003 => 'DWORD', // The data is 4 bytes long and should be interpreted as a 32-bit unsigned integer + 0x0004 => 'QWORD', // The data is 8 bytes long and should be interpreted as a 64-bit unsigned integer + 0x0005 => 'WORD', // The data is 2 bytes long and should be interpreted as a 16-bit unsigned integer + 0x0006 => 'GUID', // The data is 16 bytes long and should be interpreted as a 128-bit GUID + ); + return (isset($lookup[$id]) ? $lookup[$id] : 'invalid'); + } + + public function ASF_WMpicture(&$data) { + //typedef struct _WMPicture{ + // LPWSTR pwszMIMEType; + // BYTE bPictureType; + // LPWSTR pwszDescription; + // DWORD dwDataLen; + // BYTE* pbData; + //} WM_PICTURE; + + $WMpicture = array(); + + $offset = 0; + $WMpicture['image_type_id'] = getid3_lib::LittleEndian2Int(substr($data, $offset, 1)); + $offset += 1; + $WMpicture['image_type'] = self::WMpictureTypeLookup($WMpicture['image_type_id']); + $WMpicture['image_size'] = getid3_lib::LittleEndian2Int(substr($data, $offset, 4)); + $offset += 4; + + $WMpicture['image_mime'] = ''; + do { + $next_byte_pair = substr($data, $offset, 2); + $offset += 2; + $WMpicture['image_mime'] .= $next_byte_pair; + } while ($next_byte_pair !== "\x00\x00"); + + $WMpicture['image_description'] = ''; + do { + $next_byte_pair = substr($data, $offset, 2); + $offset += 2; + $WMpicture['image_description'] .= $next_byte_pair; + } while ($next_byte_pair !== "\x00\x00"); + + $WMpicture['dataoffset'] = $offset; + $WMpicture['data'] = substr($data, $offset); + + $imageinfo = array(); + $WMpicture['image_mime'] = ''; + $imagechunkcheck = getid3_lib::GetDataImageSize($WMpicture['data'], $imageinfo); + unset($imageinfo); + if (!empty($imagechunkcheck)) { + $WMpicture['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]); + } + if (!isset($this->getid3->info['asf']['comments']['picture'])) { + $this->getid3->info['asf']['comments']['picture'] = array(); + } + $this->getid3->info['asf']['comments']['picture'][] = array('data'=>$WMpicture['data'], 'image_mime'=>$WMpicture['image_mime']); + + return $WMpicture; + } + + + // Remove terminator 00 00 and convert UTF-16LE to Latin-1 + public static function TrimConvert($string) { + return trim(getid3_lib::iconv_fallback('UTF-16LE', 'ISO-8859-1', self::TrimTerm($string)), ' '); + } + + + // Remove terminator 00 00 + public static function TrimTerm($string) { + // remove terminator, only if present (it should be, but...) + if (substr($string, -2) === "\x00\x00") { + $string = substr($string, 0, -2); + } + return $string; + } + +} \ No newline at end of file diff --git a/wp-includes/ID3/module.audio-video.flv.php b/wp-includes/ID3/module.audio-video.flv.php new file mode 100644 index 0000000..2ee077e --- /dev/null +++ b/wp-includes/ID3/module.audio-video.flv.php @@ -0,0 +1,745 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +// also https://github.com/JamesHeinrich/getID3 // +// // +// FLV module by Seth Kaufman // +// // +// * version 0.1 (26 June 2005) // +// // +// // +// * version 0.1.1 (15 July 2005) // +// minor modifications by James Heinrich // +// // +// * version 0.2 (22 February 2006) // +// Support for On2 VP6 codec and meta information // +// by Steve Webster // +// // +// * version 0.3 (15 June 2006) // +// Modified to not read entire file into memory // +// by James Heinrich // +// // +// * version 0.4 (07 December 2007) // +// Bugfixes for incorrectly parsed FLV dimensions // +// and incorrect parsing of onMetaTag // +// by Evgeny Moysevich // +// // +// * version 0.5 (21 May 2009) // +// Fixed parsing of audio tags and added additional codec // +// details. The duration is now read from onMetaTag (if // +// exists), rather than parsing whole file // +// by Nigel Barnes // +// // +// * version 0.6 (24 May 2009) // +// Better parsing of files with h264 video // +// by Evgeny Moysevich // +// // +// * version 0.6.1 (30 May 2011) // +// prevent infinite loops in expGolombUe() // +// // +// * version 0.7.0 (16 Jul 2013) // +// handle GETID3_FLV_VIDEO_VP6FLV_ALPHA // +// improved AVCSequenceParameterSetReader::readData() // +// by Xander Schouwerwou // +// // +///////////////////////////////////////////////////////////////// +// // +// module.audio-video.flv.php // +// module for analyzing Shockwave Flash Video files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + +define('GETID3_FLV_TAG_AUDIO', 8); +define('GETID3_FLV_TAG_VIDEO', 9); +define('GETID3_FLV_TAG_META', 18); + +define('GETID3_FLV_VIDEO_H263', 2); +define('GETID3_FLV_VIDEO_SCREEN', 3); +define('GETID3_FLV_VIDEO_VP6FLV', 4); +define('GETID3_FLV_VIDEO_VP6FLV_ALPHA', 5); +define('GETID3_FLV_VIDEO_SCREENV2', 6); +define('GETID3_FLV_VIDEO_H264', 7); + +define('H264_AVC_SEQUENCE_HEADER', 0); +define('H264_PROFILE_BASELINE', 66); +define('H264_PROFILE_MAIN', 77); +define('H264_PROFILE_EXTENDED', 88); +define('H264_PROFILE_HIGH', 100); +define('H264_PROFILE_HIGH10', 110); +define('H264_PROFILE_HIGH422', 122); +define('H264_PROFILE_HIGH444', 144); +define('H264_PROFILE_HIGH444_PREDICTIVE', 244); + +class getid3_flv extends getid3_handler { + + const magic = 'FLV'; + + public $max_frames = 100000; // break out of the loop if too many frames have been scanned; only scan this many if meta frame does not contain useful duration + + public function Analyze() { + $info = &$this->getid3->info; + + $this->fseek($info['avdataoffset']); + + $FLVdataLength = $info['avdataend'] - $info['avdataoffset']; + $FLVheader = $this->fread(5); + + $info['fileformat'] = 'flv'; + $info['flv']['header']['signature'] = substr($FLVheader, 0, 3); + $info['flv']['header']['version'] = getid3_lib::BigEndian2Int(substr($FLVheader, 3, 1)); + $TypeFlags = getid3_lib::BigEndian2Int(substr($FLVheader, 4, 1)); + + if ($info['flv']['header']['signature'] != self::magic) { + $info['error'][] = 'Expecting "'.getid3_lib::PrintHexBytes(self::magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['flv']['header']['signature']).'"'; + unset($info['flv'], $info['fileformat']); + return false; + } + + $info['flv']['header']['hasAudio'] = (bool) ($TypeFlags & 0x04); + $info['flv']['header']['hasVideo'] = (bool) ($TypeFlags & 0x01); + + $FrameSizeDataLength = getid3_lib::BigEndian2Int($this->fread(4)); + $FLVheaderFrameLength = 9; + if ($FrameSizeDataLength > $FLVheaderFrameLength) { + $this->fseek($FrameSizeDataLength - $FLVheaderFrameLength, SEEK_CUR); + } + $Duration = 0; + $found_video = false; + $found_audio = false; + $found_meta = false; + $found_valid_meta_playtime = false; + $tagParseCount = 0; + $info['flv']['framecount'] = array('total'=>0, 'audio'=>0, 'video'=>0); + $flv_framecount = &$info['flv']['framecount']; + while ((($this->ftell() + 16) < $info['avdataend']) && (($tagParseCount++ <= $this->max_frames) || !$found_valid_meta_playtime)) { + $ThisTagHeader = $this->fread(16); + + $PreviousTagLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 0, 4)); + $TagType = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 4, 1)); + $DataLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 5, 3)); + $Timestamp = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 8, 3)); + $LastHeaderByte = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 15, 1)); + $NextOffset = $this->ftell() - 1 + $DataLength; + if ($Timestamp > $Duration) { + $Duration = $Timestamp; + } + + $flv_framecount['total']++; + switch ($TagType) { + case GETID3_FLV_TAG_AUDIO: + $flv_framecount['audio']++; + if (!$found_audio) { + $found_audio = true; + $info['flv']['audio']['audioFormat'] = ($LastHeaderByte >> 4) & 0x0F; + $info['flv']['audio']['audioRate'] = ($LastHeaderByte >> 2) & 0x03; + $info['flv']['audio']['audioSampleSize'] = ($LastHeaderByte >> 1) & 0x01; + $info['flv']['audio']['audioType'] = $LastHeaderByte & 0x01; + } + break; + + case GETID3_FLV_TAG_VIDEO: + $flv_framecount['video']++; + if (!$found_video) { + $found_video = true; + $info['flv']['video']['videoCodec'] = $LastHeaderByte & 0x07; + + $FLVvideoHeader = $this->fread(11); + + if ($info['flv']['video']['videoCodec'] == GETID3_FLV_VIDEO_H264) { + // this code block contributed by: moysevichØgmail*com + + $AVCPacketType = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 0, 1)); + if ($AVCPacketType == H264_AVC_SEQUENCE_HEADER) { + // read AVCDecoderConfigurationRecord + $configurationVersion = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 1)); + $AVCProfileIndication = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 1)); + $profile_compatibility = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 1)); + $lengthSizeMinusOne = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 7, 1)); + $numOfSequenceParameterSets = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 8, 1)); + + if (($numOfSequenceParameterSets & 0x1F) != 0) { + // there is at least one SequenceParameterSet + // read size of the first SequenceParameterSet + //$spsSize = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 9, 2)); + $spsSize = getid3_lib::LittleEndian2Int(substr($FLVvideoHeader, 9, 2)); + // read the first SequenceParameterSet + $sps = $this->fread($spsSize); + if (strlen($sps) == $spsSize) { // make sure that whole SequenceParameterSet was red + $spsReader = new AVCSequenceParameterSetReader($sps); + $spsReader->readData(); + $info['video']['resolution_x'] = $spsReader->getWidth(); + $info['video']['resolution_y'] = $spsReader->getHeight(); + } + } + } + // end: moysevichØgmail*com + + } elseif ($info['flv']['video']['videoCodec'] == GETID3_FLV_VIDEO_H263) { + + $PictureSizeType = (getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 3, 2))) >> 7; + $PictureSizeType = $PictureSizeType & 0x0007; + $info['flv']['header']['videoSizeType'] = $PictureSizeType; + switch ($PictureSizeType) { + case 0: + //$PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2)); + //$PictureSizeEnc <<= 1; + //$info['video']['resolution_x'] = ($PictureSizeEnc & 0xFF00) >> 8; + //$PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 2)); + //$PictureSizeEnc <<= 1; + //$info['video']['resolution_y'] = ($PictureSizeEnc & 0xFF00) >> 8; + + $PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 2)) >> 7; + $PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2)) >> 7; + $info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFF; + $info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFF; + break; + + case 1: + $PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 3)) >> 7; + $PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 3)) >> 7; + $info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFFFF; + $info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFFFF; + break; + + case 2: + $info['video']['resolution_x'] = 352; + $info['video']['resolution_y'] = 288; + break; + + case 3: + $info['video']['resolution_x'] = 176; + $info['video']['resolution_y'] = 144; + break; + + case 4: + $info['video']['resolution_x'] = 128; + $info['video']['resolution_y'] = 96; + break; + + case 5: + $info['video']['resolution_x'] = 320; + $info['video']['resolution_y'] = 240; + break; + + case 6: + $info['video']['resolution_x'] = 160; + $info['video']['resolution_y'] = 120; + break; + + default: + $info['video']['resolution_x'] = 0; + $info['video']['resolution_y'] = 0; + break; + + } + + } elseif ($info['flv']['video']['videoCodec'] == GETID3_FLV_VIDEO_VP6FLV_ALPHA) { + + /* contributed by schouwerwouØgmail*com */ + if (!isset($info['video']['resolution_x'])) { // only when meta data isn't set + $PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 2)); + $PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 7, 2)); + $info['video']['resolution_x'] = ($PictureSizeEnc['x'] & 0xFF) << 3; + $info['video']['resolution_y'] = ($PictureSizeEnc['y'] & 0xFF) << 3; + } + /* end schouwerwouØgmail*com */ + + } + if (!empty($info['video']['resolution_x']) && !empty($info['video']['resolution_y'])) { + $info['video']['pixel_aspect_ratio'] = $info['video']['resolution_x'] / $info['video']['resolution_y']; + } + } + break; + + // Meta tag + case GETID3_FLV_TAG_META: + if (!$found_meta) { + $found_meta = true; + $this->fseek(-1, SEEK_CUR); + $datachunk = $this->fread($DataLength); + $AMFstream = new AMFStream($datachunk); + $reader = new AMFReader($AMFstream); + $eventName = $reader->readData(); + $info['flv']['meta'][$eventName] = $reader->readData(); + unset($reader); + + $copykeys = array('framerate'=>'frame_rate', 'width'=>'resolution_x', 'height'=>'resolution_y', 'audiodatarate'=>'bitrate', 'videodatarate'=>'bitrate'); + foreach ($copykeys as $sourcekey => $destkey) { + if (isset($info['flv']['meta']['onMetaData'][$sourcekey])) { + switch ($sourcekey) { + case 'width': + case 'height': + $info['video'][$destkey] = intval(round($info['flv']['meta']['onMetaData'][$sourcekey])); + break; + case 'audiodatarate': + $info['audio'][$destkey] = getid3_lib::CastAsInt(round($info['flv']['meta']['onMetaData'][$sourcekey] * 1000)); + break; + case 'videodatarate': + case 'frame_rate': + default: + $info['video'][$destkey] = $info['flv']['meta']['onMetaData'][$sourcekey]; + break; + } + } + } + if (!empty($info['flv']['meta']['onMetaData']['duration'])) { + $found_valid_meta_playtime = true; + } + } + break; + + default: + // noop + break; + } + $this->fseek($NextOffset); + } + + $info['playtime_seconds'] = $Duration / 1000; + if ($info['playtime_seconds'] > 0) { + $info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; + } + + if ($info['flv']['header']['hasAudio']) { + $info['audio']['codec'] = self::audioFormatLookup($info['flv']['audio']['audioFormat']); + $info['audio']['sample_rate'] = self::audioRateLookup($info['flv']['audio']['audioRate']); + $info['audio']['bits_per_sample'] = self::audioBitDepthLookup($info['flv']['audio']['audioSampleSize']); + + $info['audio']['channels'] = $info['flv']['audio']['audioType'] + 1; // 0=mono,1=stereo + $info['audio']['lossless'] = ($info['flv']['audio']['audioFormat'] ? false : true); // 0=uncompressed + $info['audio']['dataformat'] = 'flv'; + } + if (!empty($info['flv']['header']['hasVideo'])) { + $info['video']['codec'] = self::videoCodecLookup($info['flv']['video']['videoCodec']); + $info['video']['dataformat'] = 'flv'; + $info['video']['lossless'] = false; + } + + // Set information from meta + if (!empty($info['flv']['meta']['onMetaData']['duration'])) { + $info['playtime_seconds'] = $info['flv']['meta']['onMetaData']['duration']; + $info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; + } + if (isset($info['flv']['meta']['onMetaData']['audiocodecid'])) { + $info['audio']['codec'] = self::audioFormatLookup($info['flv']['meta']['onMetaData']['audiocodecid']); + } + if (isset($info['flv']['meta']['onMetaData']['videocodecid'])) { + $info['video']['codec'] = self::videoCodecLookup($info['flv']['meta']['onMetaData']['videocodecid']); + } + return true; + } + + + public static function audioFormatLookup($id) { + static $lookup = array( + 0 => 'Linear PCM, platform endian', + 1 => 'ADPCM', + 2 => 'mp3', + 3 => 'Linear PCM, little endian', + 4 => 'Nellymoser 16kHz mono', + 5 => 'Nellymoser 8kHz mono', + 6 => 'Nellymoser', + 7 => 'G.711A-law logarithmic PCM', + 8 => 'G.711 mu-law logarithmic PCM', + 9 => 'reserved', + 10 => 'AAC', + 11 => 'Speex', + 12 => false, // unknown? + 13 => false, // unknown? + 14 => 'mp3 8kHz', + 15 => 'Device-specific sound', + ); + return (isset($lookup[$id]) ? $lookup[$id] : false); + } + + public static function audioRateLookup($id) { + static $lookup = array( + 0 => 5500, + 1 => 11025, + 2 => 22050, + 3 => 44100, + ); + return (isset($lookup[$id]) ? $lookup[$id] : false); + } + + public static function audioBitDepthLookup($id) { + static $lookup = array( + 0 => 8, + 1 => 16, + ); + return (isset($lookup[$id]) ? $lookup[$id] : false); + } + + public static function videoCodecLookup($id) { + static $lookup = array( + GETID3_FLV_VIDEO_H263 => 'Sorenson H.263', + GETID3_FLV_VIDEO_SCREEN => 'Screen video', + GETID3_FLV_VIDEO_VP6FLV => 'On2 VP6', + GETID3_FLV_VIDEO_VP6FLV_ALPHA => 'On2 VP6 with alpha channel', + GETID3_FLV_VIDEO_SCREENV2 => 'Screen video v2', + GETID3_FLV_VIDEO_H264 => 'Sorenson H.264', + ); + return (isset($lookup[$id]) ? $lookup[$id] : false); + } +} + +class AMFStream { + public $bytes; + public $pos; + + public function __construct(&$bytes) { + $this->bytes =& $bytes; + $this->pos = 0; + } + + public function readByte() { + return getid3_lib::BigEndian2Int(substr($this->bytes, $this->pos++, 1)); + } + + public function readInt() { + return ($this->readByte() << 8) + $this->readByte(); + } + + public function readLong() { + return ($this->readByte() << 24) + ($this->readByte() << 16) + ($this->readByte() << 8) + $this->readByte(); + } + + public function readDouble() { + return getid3_lib::BigEndian2Float($this->read(8)); + } + + public function readUTF() { + $length = $this->readInt(); + return $this->read($length); + } + + public function readLongUTF() { + $length = $this->readLong(); + return $this->read($length); + } + + public function read($length) { + $val = substr($this->bytes, $this->pos, $length); + $this->pos += $length; + return $val; + } + + public function peekByte() { + $pos = $this->pos; + $val = $this->readByte(); + $this->pos = $pos; + return $val; + } + + public function peekInt() { + $pos = $this->pos; + $val = $this->readInt(); + $this->pos = $pos; + return $val; + } + + public function peekLong() { + $pos = $this->pos; + $val = $this->readLong(); + $this->pos = $pos; + return $val; + } + + public function peekDouble() { + $pos = $this->pos; + $val = $this->readDouble(); + $this->pos = $pos; + return $val; + } + + public function peekUTF() { + $pos = $this->pos; + $val = $this->readUTF(); + $this->pos = $pos; + return $val; + } + + public function peekLongUTF() { + $pos = $this->pos; + $val = $this->readLongUTF(); + $this->pos = $pos; + return $val; + } +} + +class AMFReader { + public $stream; + + public function __construct(&$stream) { + $this->stream =& $stream; + } + + public function readData() { + $value = null; + + $type = $this->stream->readByte(); + switch ($type) { + + // Double + case 0: + $value = $this->readDouble(); + break; + + // Boolean + case 1: + $value = $this->readBoolean(); + break; + + // String + case 2: + $value = $this->readString(); + break; + + // Object + case 3: + $value = $this->readObject(); + break; + + // null + case 6: + return null; + break; + + // Mixed array + case 8: + $value = $this->readMixedArray(); + break; + + // Array + case 10: + $value = $this->readArray(); + break; + + // Date + case 11: + $value = $this->readDate(); + break; + + // Long string + case 13: + $value = $this->readLongString(); + break; + + // XML (handled as string) + case 15: + $value = $this->readXML(); + break; + + // Typed object (handled as object) + case 16: + $value = $this->readTypedObject(); + break; + + // Long string + default: + $value = '(unknown or unsupported data type)'; + break; + } + + return $value; + } + + public function readDouble() { + return $this->stream->readDouble(); + } + + public function readBoolean() { + return $this->stream->readByte() == 1; + } + + public function readString() { + return $this->stream->readUTF(); + } + + public function readObject() { + // Get highest numerical index - ignored +// $highestIndex = $this->stream->readLong(); + + $data = array(); + + while ($key = $this->stream->readUTF()) { + $data[$key] = $this->readData(); + } + // Mixed array record ends with empty string (0x00 0x00) and 0x09 + if (($key == '') && ($this->stream->peekByte() == 0x09)) { + // Consume byte + $this->stream->readByte(); + } + return $data; + } + + public function readMixedArray() { + // Get highest numerical index - ignored + $highestIndex = $this->stream->readLong(); + + $data = array(); + + while ($key = $this->stream->readUTF()) { + if (is_numeric($key)) { + $key = (float) $key; + } + $data[$key] = $this->readData(); + } + // Mixed array record ends with empty string (0x00 0x00) and 0x09 + if (($key == '') && ($this->stream->peekByte() == 0x09)) { + // Consume byte + $this->stream->readByte(); + } + + return $data; + } + + public function readArray() { + $length = $this->stream->readLong(); + $data = array(); + + for ($i = 0; $i < $length; $i++) { + $data[] = $this->readData(); + } + return $data; + } + + public function readDate() { + $timestamp = $this->stream->readDouble(); + $timezone = $this->stream->readInt(); + return $timestamp; + } + + public function readLongString() { + return $this->stream->readLongUTF(); + } + + public function readXML() { + return $this->stream->readLongUTF(); + } + + public function readTypedObject() { + $className = $this->stream->readUTF(); + return $this->readObject(); + } +} + +class AVCSequenceParameterSetReader { + public $sps; + public $start = 0; + public $currentBytes = 0; + public $currentBits = 0; + public $width; + public $height; + + public function __construct($sps) { + $this->sps = $sps; + } + + public function readData() { + $this->skipBits(8); + $this->skipBits(8); + $profile = $this->getBits(8); // read profile + if ($profile > 0) { + $this->skipBits(8); + $level_idc = $this->getBits(8); // level_idc + $this->expGolombUe(); // seq_parameter_set_id // sps + $this->expGolombUe(); // log2_max_frame_num_minus4 + $picOrderType = $this->expGolombUe(); // pic_order_cnt_type + if ($picOrderType == 0) { + $this->expGolombUe(); // log2_max_pic_order_cnt_lsb_minus4 + } elseif ($picOrderType == 1) { + $this->skipBits(1); // delta_pic_order_always_zero_flag + $this->expGolombSe(); // offset_for_non_ref_pic + $this->expGolombSe(); // offset_for_top_to_bottom_field + $num_ref_frames_in_pic_order_cnt_cycle = $this->expGolombUe(); // num_ref_frames_in_pic_order_cnt_cycle + for ($i = 0; $i < $num_ref_frames_in_pic_order_cnt_cycle; $i++) { + $this->expGolombSe(); // offset_for_ref_frame[ i ] + } + } + $this->expGolombUe(); // num_ref_frames + $this->skipBits(1); // gaps_in_frame_num_value_allowed_flag + $pic_width_in_mbs_minus1 = $this->expGolombUe(); // pic_width_in_mbs_minus1 + $pic_height_in_map_units_minus1 = $this->expGolombUe(); // pic_height_in_map_units_minus1 + + $frame_mbs_only_flag = $this->getBits(1); // frame_mbs_only_flag + if ($frame_mbs_only_flag == 0) { + $this->skipBits(1); // mb_adaptive_frame_field_flag + } + $this->skipBits(1); // direct_8x8_inference_flag + $frame_cropping_flag = $this->getBits(1); // frame_cropping_flag + + $frame_crop_left_offset = 0; + $frame_crop_right_offset = 0; + $frame_crop_top_offset = 0; + $frame_crop_bottom_offset = 0; + + if ($frame_cropping_flag) { + $frame_crop_left_offset = $this->expGolombUe(); // frame_crop_left_offset + $frame_crop_right_offset = $this->expGolombUe(); // frame_crop_right_offset + $frame_crop_top_offset = $this->expGolombUe(); // frame_crop_top_offset + $frame_crop_bottom_offset = $this->expGolombUe(); // frame_crop_bottom_offset + } + $this->skipBits(1); // vui_parameters_present_flag + // etc + + $this->width = (($pic_width_in_mbs_minus1 + 1) * 16) - ($frame_crop_left_offset * 2) - ($frame_crop_right_offset * 2); + $this->height = ((2 - $frame_mbs_only_flag) * ($pic_height_in_map_units_minus1 + 1) * 16) - ($frame_crop_top_offset * 2) - ($frame_crop_bottom_offset * 2); + } + } + + public function skipBits($bits) { + $newBits = $this->currentBits + $bits; + $this->currentBytes += (int)floor($newBits / 8); + $this->currentBits = $newBits % 8; + } + + public function getBit() { + $result = (getid3_lib::BigEndian2Int(substr($this->sps, $this->currentBytes, 1)) >> (7 - $this->currentBits)) & 0x01; + $this->skipBits(1); + return $result; + } + + public function getBits($bits) { + $result = 0; + for ($i = 0; $i < $bits; $i++) { + $result = ($result << 1) + $this->getBit(); + } + return $result; + } + + public function expGolombUe() { + $significantBits = 0; + $bit = $this->getBit(); + while ($bit == 0) { + $significantBits++; + $bit = $this->getBit(); + + if ($significantBits > 31) { + // something is broken, this is an emergency escape to prevent infinite loops + return 0; + } + } + return (1 << $significantBits) + $this->getBits($significantBits) - 1; + } + + public function expGolombSe() { + $result = $this->expGolombUe(); + if (($result & 0x01) == 0) { + return -($result >> 1); + } else { + return ($result + 1) >> 1; + } + } + + public function getWidth() { + return $this->width; + } + + public function getHeight() { + return $this->height; + } +} \ No newline at end of file diff --git a/wp-includes/ID3/module.audio-video.matroska.php b/wp-includes/ID3/module.audio-video.matroska.php new file mode 100644 index 0000000..79d0b7d --- /dev/null +++ b/wp-includes/ID3/module.audio-video.matroska.php @@ -0,0 +1,1751 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +// also https://github.com/JamesHeinrich/getID3 // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio-video.matriska.php // +// module for analyzing Matroska containers // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +define('EBML_ID_CHAPTERS', 0x0043A770); // [10][43][A7][70] -- A system to define basic menus and partition data. For more detailed information, look at the Chapters Explanation. +define('EBML_ID_SEEKHEAD', 0x014D9B74); // [11][4D][9B][74] -- Contains the position of other level 1 elements. +define('EBML_ID_TAGS', 0x0254C367); // [12][54][C3][67] -- Element containing elements specific to Tracks/Chapters. A list of valid tags can be found . +define('EBML_ID_INFO', 0x0549A966); // [15][49][A9][66] -- Contains miscellaneous general information and statistics on the file. +define('EBML_ID_TRACKS', 0x0654AE6B); // [16][54][AE][6B] -- A top-level block of information with many tracks described. +define('EBML_ID_SEGMENT', 0x08538067); // [18][53][80][67] -- This element contains all other top-level (level 1) elements. Typically a Matroska file is composed of 1 segment. +define('EBML_ID_ATTACHMENTS', 0x0941A469); // [19][41][A4][69] -- Contain attached files. +define('EBML_ID_EBML', 0x0A45DFA3); // [1A][45][DF][A3] -- Set the EBML characteristics of the data to follow. Each EBML document has to start with this. +define('EBML_ID_CUES', 0x0C53BB6B); // [1C][53][BB][6B] -- A top-level element to speed seeking access. All entries are local to the segment. +define('EBML_ID_CLUSTER', 0x0F43B675); // [1F][43][B6][75] -- The lower level element containing the (monolithic) Block structure. +define('EBML_ID_LANGUAGE', 0x02B59C); // [22][B5][9C] -- Specifies the language of the track in the Matroska languages form. +define('EBML_ID_TRACKTIMECODESCALE', 0x03314F); // [23][31][4F] -- The scale to apply on this track to work at normal speed in relation with other tracks (mostly used to adjust video speed when the audio length differs). +define('EBML_ID_DEFAULTDURATION', 0x03E383); // [23][E3][83] -- Number of nanoseconds (i.e. not scaled) per frame. +define('EBML_ID_CODECNAME', 0x058688); // [25][86][88] -- A human-readable string specifying the codec. +define('EBML_ID_CODECDOWNLOADURL', 0x06B240); // [26][B2][40] -- A URL to download about the codec used. +define('EBML_ID_TIMECODESCALE', 0x0AD7B1); // [2A][D7][B1] -- Timecode scale in nanoseconds (1.000.000 means all timecodes in the segment are expressed in milliseconds). +define('EBML_ID_COLOURSPACE', 0x0EB524); // [2E][B5][24] -- Same value as in AVI (32 bits). +define('EBML_ID_GAMMAVALUE', 0x0FB523); // [2F][B5][23] -- Gamma Value. +define('EBML_ID_CODECSETTINGS', 0x1A9697); // [3A][96][97] -- A string describing the encoding setting used. +define('EBML_ID_CODECINFOURL', 0x1B4040); // [3B][40][40] -- A URL to find information about the codec used. +define('EBML_ID_PREVFILENAME', 0x1C83AB); // [3C][83][AB] -- An escaped filename corresponding to the previous segment. +define('EBML_ID_PREVUID', 0x1CB923); // [3C][B9][23] -- A unique ID to identify the previous chained segment (128 bits). +define('EBML_ID_NEXTFILENAME', 0x1E83BB); // [3E][83][BB] -- An escaped filename corresponding to the next segment. +define('EBML_ID_NEXTUID', 0x1EB923); // [3E][B9][23] -- A unique ID to identify the next chained segment (128 bits). +define('EBML_ID_CONTENTCOMPALGO', 0x0254); // [42][54] -- The compression algorithm used. Algorithms that have been specified so far are: +define('EBML_ID_CONTENTCOMPSETTINGS', 0x0255); // [42][55] -- Settings that might be needed by the decompressor. For Header Stripping (ContentCompAlgo=3), the bytes that were removed from the beggining of each frames of the track. +define('EBML_ID_DOCTYPE', 0x0282); // [42][82] -- A string that describes the type of document that follows this EBML header ('matroska' in our case). +define('EBML_ID_DOCTYPEREADVERSION', 0x0285); // [42][85] -- The minimum DocType version an interpreter has to support to read this file. +define('EBML_ID_EBMLVERSION', 0x0286); // [42][86] -- The version of EBML parser used to create the file. +define('EBML_ID_DOCTYPEVERSION', 0x0287); // [42][87] -- The version of DocType interpreter used to create the file. +define('EBML_ID_EBMLMAXIDLENGTH', 0x02F2); // [42][F2] -- The maximum length of the IDs you'll find in this file (4 or less in Matroska). +define('EBML_ID_EBMLMAXSIZELENGTH', 0x02F3); // [42][F3] -- The maximum length of the sizes you'll find in this file (8 or less in Matroska). This does not override the element size indicated at the beginning of an element. Elements that have an indicated size which is larger than what is allowed by EBMLMaxSizeLength shall be considered invalid. +define('EBML_ID_EBMLREADVERSION', 0x02F7); // [42][F7] -- The minimum EBML version a parser has to support to read this file. +define('EBML_ID_CHAPLANGUAGE', 0x037C); // [43][7C] -- The languages corresponding to the string, in the bibliographic ISO-639-2 form. +define('EBML_ID_CHAPCOUNTRY', 0x037E); // [43][7E] -- The countries corresponding to the string, same 2 octets as in Internet domains. +define('EBML_ID_SEGMENTFAMILY', 0x0444); // [44][44] -- A randomly generated unique ID that all segments related to each other must use (128 bits). +define('EBML_ID_DATEUTC', 0x0461); // [44][61] -- Date of the origin of timecode (value 0), i.e. production date. +define('EBML_ID_TAGLANGUAGE', 0x047A); // [44][7A] -- Specifies the language of the tag specified, in the Matroska languages form. +define('EBML_ID_TAGDEFAULT', 0x0484); // [44][84] -- Indication to know if this is the default/original language to use for the given tag. +define('EBML_ID_TAGBINARY', 0x0485); // [44][85] -- The values of the Tag if it is binary. Note that this cannot be used in the same SimpleTag as TagString. +define('EBML_ID_TAGSTRING', 0x0487); // [44][87] -- The value of the Tag. +define('EBML_ID_DURATION', 0x0489); // [44][89] -- Duration of the segment (based on TimecodeScale). +define('EBML_ID_CHAPPROCESSPRIVATE', 0x050D); // [45][0D] -- Some optional data attached to the ChapProcessCodecID information. For ChapProcessCodecID = 1, it is the "DVD level" equivalent. +define('EBML_ID_CHAPTERFLAGENABLED', 0x0598); // [45][98] -- Specify wether the chapter is enabled. It can be enabled/disabled by a Control Track. When disabled, the movie should skip all the content between the TimeStart and TimeEnd of this chapter. +define('EBML_ID_TAGNAME', 0x05A3); // [45][A3] -- The name of the Tag that is going to be stored. +define('EBML_ID_EDITIONENTRY', 0x05B9); // [45][B9] -- Contains all information about a segment edition. +define('EBML_ID_EDITIONUID', 0x05BC); // [45][BC] -- A unique ID to identify the edition. It's useful for tagging an edition. +define('EBML_ID_EDITIONFLAGHIDDEN', 0x05BD); // [45][BD] -- If an edition is hidden (1), it should not be available to the user interface (but still to Control Tracks). +define('EBML_ID_EDITIONFLAGDEFAULT', 0x05DB); // [45][DB] -- If a flag is set (1) the edition should be used as the default one. +define('EBML_ID_EDITIONFLAGORDERED', 0x05DD); // [45][DD] -- Specify if the chapters can be defined multiple times and the order to play them is enforced. +define('EBML_ID_FILEDATA', 0x065C); // [46][5C] -- The data of the file. +define('EBML_ID_FILEMIMETYPE', 0x0660); // [46][60] -- MIME type of the file. +define('EBML_ID_FILENAME', 0x066E); // [46][6E] -- Filename of the attached file. +define('EBML_ID_FILEREFERRAL', 0x0675); // [46][75] -- A binary value that a track/codec can refer to when the attachment is needed. +define('EBML_ID_FILEDESCRIPTION', 0x067E); // [46][7E] -- A human-friendly name for the attached file. +define('EBML_ID_FILEUID', 0x06AE); // [46][AE] -- Unique ID representing the file, as random as possible. +define('EBML_ID_CONTENTENCALGO', 0x07E1); // [47][E1] -- The encryption algorithm used. The value '0' means that the contents have not been encrypted but only signed. Predefined values: +define('EBML_ID_CONTENTENCKEYID', 0x07E2); // [47][E2] -- For public key algorithms this is the ID of the public key the the data was encrypted with. +define('EBML_ID_CONTENTSIGNATURE', 0x07E3); // [47][E3] -- A cryptographic signature of the contents. +define('EBML_ID_CONTENTSIGKEYID', 0x07E4); // [47][E4] -- This is the ID of the private key the data was signed with. +define('EBML_ID_CONTENTSIGALGO', 0x07E5); // [47][E5] -- The algorithm used for the signature. A value of '0' means that the contents have not been signed but only encrypted. Predefined values: +define('EBML_ID_CONTENTSIGHASHALGO', 0x07E6); // [47][E6] -- The hash algorithm used for the signature. A value of '0' means that the contents have not been signed but only encrypted. Predefined values: +define('EBML_ID_MUXINGAPP', 0x0D80); // [4D][80] -- Muxing application or library ("libmatroska-0.4.3"). +define('EBML_ID_SEEK', 0x0DBB); // [4D][BB] -- Contains a single seek entry to an EBML element. +define('EBML_ID_CONTENTENCODINGORDER', 0x1031); // [50][31] -- Tells when this modification was used during encoding/muxing starting with 0 and counting upwards. The decoder/demuxer has to start with the highest order number it finds and work its way down. This value has to be unique over all ContentEncodingOrder elements in the segment. +define('EBML_ID_CONTENTENCODINGSCOPE', 0x1032); // [50][32] -- A bit field that describes which elements have been modified in this way. Values (big endian) can be OR'ed. Possible values: +define('EBML_ID_CONTENTENCODINGTYPE', 0x1033); // [50][33] -- A value describing what kind of transformation has been done. Possible values: +define('EBML_ID_CONTENTCOMPRESSION', 0x1034); // [50][34] -- Settings describing the compression used. Must be present if the value of ContentEncodingType is 0 and absent otherwise. Each block must be decompressable even if no previous block is available in order not to prevent seeking. +define('EBML_ID_CONTENTENCRYPTION', 0x1035); // [50][35] -- Settings describing the encryption used. Must be present if the value of ContentEncodingType is 1 and absent otherwise. +define('EBML_ID_CUEREFNUMBER', 0x135F); // [53][5F] -- Number of the referenced Block of Track X in the specified Cluster. +define('EBML_ID_NAME', 0x136E); // [53][6E] -- A human-readable track name. +define('EBML_ID_CUEBLOCKNUMBER', 0x1378); // [53][78] -- Number of the Block in the specified Cluster. +define('EBML_ID_TRACKOFFSET', 0x137F); // [53][7F] -- A value to add to the Block's Timecode. This can be used to adjust the playback offset of a track. +define('EBML_ID_SEEKID', 0x13AB); // [53][AB] -- The binary ID corresponding to the element name. +define('EBML_ID_SEEKPOSITION', 0x13AC); // [53][AC] -- The position of the element in the segment in octets (0 = first level 1 element). +define('EBML_ID_STEREOMODE', 0x13B8); // [53][B8] -- Stereo-3D video mode. +define('EBML_ID_OLDSTEREOMODE', 0x13B9); // [53][B9] -- Bogus StereoMode value used in old versions of libmatroska. DO NOT USE. (0: mono, 1: right eye, 2: left eye, 3: both eyes). +define('EBML_ID_PIXELCROPBOTTOM', 0x14AA); // [54][AA] -- The number of video pixels to remove at the bottom of the image (for HDTV content). +define('EBML_ID_DISPLAYWIDTH', 0x14B0); // [54][B0] -- Width of the video frames to display. +define('EBML_ID_DISPLAYUNIT', 0x14B2); // [54][B2] -- Type of the unit for DisplayWidth/Height (0: pixels, 1: centimeters, 2: inches). +define('EBML_ID_ASPECTRATIOTYPE', 0x14B3); // [54][B3] -- Specify the possible modifications to the aspect ratio (0: free resizing, 1: keep aspect ratio, 2: fixed). +define('EBML_ID_DISPLAYHEIGHT', 0x14BA); // [54][BA] -- Height of the video frames to display. +define('EBML_ID_PIXELCROPTOP', 0x14BB); // [54][BB] -- The number of video pixels to remove at the top of the image. +define('EBML_ID_PIXELCROPLEFT', 0x14CC); // [54][CC] -- The number of video pixels to remove on the left of the image. +define('EBML_ID_PIXELCROPRIGHT', 0x14DD); // [54][DD] -- The number of video pixels to remove on the right of the image. +define('EBML_ID_FLAGFORCED', 0x15AA); // [55][AA] -- Set if that track MUST be used during playback. There can be many forced track for a kind (audio, video or subs), the player should select the one which language matches the user preference or the default + forced track. Overlay MAY happen between a forced and non-forced track of the same kind. +define('EBML_ID_MAXBLOCKADDITIONID', 0x15EE); // [55][EE] -- The maximum value of BlockAddID. A value 0 means there is no BlockAdditions for this track. +define('EBML_ID_WRITINGAPP', 0x1741); // [57][41] -- Writing application ("mkvmerge-0.3.3"). +define('EBML_ID_CLUSTERSILENTTRACKS', 0x1854); // [58][54] -- The list of tracks that are not used in that part of the stream. It is useful when using overlay tracks on seeking. Then you should decide what track to use. +define('EBML_ID_CLUSTERSILENTTRACKNUMBER', 0x18D7); // [58][D7] -- One of the track number that are not used from now on in the stream. It could change later if not specified as silent in a further Cluster. +define('EBML_ID_ATTACHEDFILE', 0x21A7); // [61][A7] -- An attached file. +define('EBML_ID_CONTENTENCODING', 0x2240); // [62][40] -- Settings for one content encoding like compression or encryption. +define('EBML_ID_BITDEPTH', 0x2264); // [62][64] -- Bits per sample, mostly used for PCM. +define('EBML_ID_CODECPRIVATE', 0x23A2); // [63][A2] -- Private data only known to the codec. +define('EBML_ID_TARGETS', 0x23C0); // [63][C0] -- Contain all UIDs where the specified meta data apply. It is void to describe everything in the segment. +define('EBML_ID_CHAPTERPHYSICALEQUIV', 0x23C3); // [63][C3] -- Specify the physical equivalent of this ChapterAtom like "DVD" (60) or "SIDE" (50), see complete list of values. +define('EBML_ID_TAGCHAPTERUID', 0x23C4); // [63][C4] -- A unique ID to identify the Chapter(s) the tags belong to. If the value is 0 at this level, the tags apply to all chapters in the Segment. +define('EBML_ID_TAGTRACKUID', 0x23C5); // [63][C5] -- A unique ID to identify the Track(s) the tags belong to. If the value is 0 at this level, the tags apply to all tracks in the Segment. +define('EBML_ID_TAGATTACHMENTUID', 0x23C6); // [63][C6] -- A unique ID to identify the Attachment(s) the tags belong to. If the value is 0 at this level, the tags apply to all the attachments in the Segment. +define('EBML_ID_TAGEDITIONUID', 0x23C9); // [63][C9] -- A unique ID to identify the EditionEntry(s) the tags belong to. If the value is 0 at this level, the tags apply to all editions in the Segment. +define('EBML_ID_TARGETTYPE', 0x23CA); // [63][CA] -- An informational string that can be used to display the logical level of the target like "ALBUM", "TRACK", "MOVIE", "CHAPTER", etc (see TargetType). +define('EBML_ID_TRACKTRANSLATE', 0x2624); // [66][24] -- The track identification for the given Chapter Codec. +define('EBML_ID_TRACKTRANSLATETRACKID', 0x26A5); // [66][A5] -- The binary value used to represent this track in the chapter codec data. The format depends on the ChapProcessCodecID used. +define('EBML_ID_TRACKTRANSLATECODEC', 0x26BF); // [66][BF] -- The chapter codec using this ID (0: Matroska Script, 1: DVD-menu). +define('EBML_ID_TRACKTRANSLATEEDITIONUID', 0x26FC); // [66][FC] -- Specify an edition UID on which this translation applies. When not specified, it means for all editions found in the segment. +define('EBML_ID_SIMPLETAG', 0x27C8); // [67][C8] -- Contains general information about the target. +define('EBML_ID_TARGETTYPEVALUE', 0x28CA); // [68][CA] -- A number to indicate the logical level of the target (see TargetType). +define('EBML_ID_CHAPPROCESSCOMMAND', 0x2911); // [69][11] -- Contains all the commands associated to the Atom. +define('EBML_ID_CHAPPROCESSTIME', 0x2922); // [69][22] -- Defines when the process command should be handled (0: during the whole chapter, 1: before starting playback, 2: after playback of the chapter). +define('EBML_ID_CHAPTERTRANSLATE', 0x2924); // [69][24] -- A tuple of corresponding ID used by chapter codecs to represent this segment. +define('EBML_ID_CHAPPROCESSDATA', 0x2933); // [69][33] -- Contains the command information. The data should be interpreted depending on the ChapProcessCodecID value. For ChapProcessCodecID = 1, the data correspond to the binary DVD cell pre/post commands. +define('EBML_ID_CHAPPROCESS', 0x2944); // [69][44] -- Contains all the commands associated to the Atom. +define('EBML_ID_CHAPPROCESSCODECID', 0x2955); // [69][55] -- Contains the type of the codec used for the processing. A value of 0 means native Matroska processing (to be defined), a value of 1 means the DVD command set is used. More codec IDs can be added later. +define('EBML_ID_CHAPTERTRANSLATEID', 0x29A5); // [69][A5] -- The binary value used to represent this segment in the chapter codec data. The format depends on the ChapProcessCodecID used. +define('EBML_ID_CHAPTERTRANSLATECODEC', 0x29BF); // [69][BF] -- The chapter codec using this ID (0: Matroska Script, 1: DVD-menu). +define('EBML_ID_CHAPTERTRANSLATEEDITIONUID', 0x29FC); // [69][FC] -- Specify an edition UID on which this correspondance applies. When not specified, it means for all editions found in the segment. +define('EBML_ID_CONTENTENCODINGS', 0x2D80); // [6D][80] -- Settings for several content encoding mechanisms like compression or encryption. +define('EBML_ID_MINCACHE', 0x2DE7); // [6D][E7] -- The minimum number of frames a player should be able to cache during playback. If set to 0, the reference pseudo-cache system is not used. +define('EBML_ID_MAXCACHE', 0x2DF8); // [6D][F8] -- The maximum cache size required to store referenced frames in and the current frame. 0 means no cache is needed. +define('EBML_ID_CHAPTERSEGMENTUID', 0x2E67); // [6E][67] -- A segment to play in place of this chapter. Edition ChapterSegmentEditionUID should be used for this segment, otherwise no edition is used. +define('EBML_ID_CHAPTERSEGMENTEDITIONUID', 0x2EBC); // [6E][BC] -- The edition to play from the segment linked in ChapterSegmentUID. +define('EBML_ID_TRACKOVERLAY', 0x2FAB); // [6F][AB] -- Specify that this track is an overlay track for the Track specified (in the u-integer). That means when this track has a gap (see SilentTracks) the overlay track should be used instead. The order of multiple TrackOverlay matters, the first one is the one that should be used. If not found it should be the second, etc. +define('EBML_ID_TAG', 0x3373); // [73][73] -- Element containing elements specific to Tracks/Chapters. +define('EBML_ID_SEGMENTFILENAME', 0x3384); // [73][84] -- A filename corresponding to this segment. +define('EBML_ID_SEGMENTUID', 0x33A4); // [73][A4] -- A randomly generated unique ID to identify the current segment between many others (128 bits). +define('EBML_ID_CHAPTERUID', 0x33C4); // [73][C4] -- A unique ID to identify the Chapter. +define('EBML_ID_TRACKUID', 0x33C5); // [73][C5] -- A unique ID to identify the Track. This should be kept the same when making a direct stream copy of the Track to another file. +define('EBML_ID_ATTACHMENTLINK', 0x3446); // [74][46] -- The UID of an attachment that is used by this codec. +define('EBML_ID_CLUSTERBLOCKADDITIONS', 0x35A1); // [75][A1] -- Contain additional blocks to complete the main one. An EBML parser that has no knowledge of the Block structure could still see and use/skip these data. +define('EBML_ID_CHANNELPOSITIONS', 0x347B); // [7D][7B] -- Table of horizontal angles for each successive channel, see appendix. +define('EBML_ID_OUTPUTSAMPLINGFREQUENCY', 0x38B5); // [78][B5] -- Real output sampling frequency in Hz (used for SBR techniques). +define('EBML_ID_TITLE', 0x3BA9); // [7B][A9] -- General name of the segment. +define('EBML_ID_CHAPTERDISPLAY', 0x00); // [80] -- Contains all possible strings to use for the chapter display. +define('EBML_ID_TRACKTYPE', 0x03); // [83] -- A set of track types coded on 8 bits (1: video, 2: audio, 3: complex, 0x10: logo, 0x11: subtitle, 0x12: buttons, 0x20: control). +define('EBML_ID_CHAPSTRING', 0x05); // [85] -- Contains the string to use as the chapter atom. +define('EBML_ID_CODECID', 0x06); // [86] -- An ID corresponding to the codec, see the codec page for more info. +define('EBML_ID_FLAGDEFAULT', 0x08); // [88] -- Set if that track (audio, video or subs) SHOULD be used if no language found matches the user preference. +define('EBML_ID_CHAPTERTRACKNUMBER', 0x09); // [89] -- UID of the Track to apply this chapter too. In the absense of a control track, choosing this chapter will select the listed Tracks and deselect unlisted tracks. Absense of this element indicates that the Chapter should be applied to any currently used Tracks. +define('EBML_ID_CLUSTERSLICES', 0x0E); // [8E] -- Contains slices description. +define('EBML_ID_CHAPTERTRACK', 0x0F); // [8F] -- List of tracks on which the chapter applies. If this element is not present, all tracks apply +define('EBML_ID_CHAPTERTIMESTART', 0x11); // [91] -- Timecode of the start of Chapter (not scaled). +define('EBML_ID_CHAPTERTIMEEND', 0x12); // [92] -- Timecode of the end of Chapter (timecode excluded, not scaled). +define('EBML_ID_CUEREFTIME', 0x16); // [96] -- Timecode of the referenced Block. +define('EBML_ID_CUEREFCLUSTER', 0x17); // [97] -- Position of the Cluster containing the referenced Block. +define('EBML_ID_CHAPTERFLAGHIDDEN', 0x18); // [98] -- If a chapter is hidden (1), it should not be available to the user interface (but still to Control Tracks). +define('EBML_ID_FLAGINTERLACED', 0x1A); // [9A] -- Set if the video is interlaced. +define('EBML_ID_CLUSTERBLOCKDURATION', 0x1B); // [9B] -- The duration of the Block (based on TimecodeScale). This element is mandatory when DefaultDuration is set for the track. When not written and with no DefaultDuration, the value is assumed to be the difference between the timecode of this Block and the timecode of the next Block in "display" order (not coding order). This element can be useful at the end of a Track (as there is not other Block available), or when there is a break in a track like for subtitle tracks. +define('EBML_ID_FLAGLACING', 0x1C); // [9C] -- Set if the track may contain blocks using lacing. +define('EBML_ID_CHANNELS', 0x1F); // [9F] -- Numbers of channels in the track. +define('EBML_ID_CLUSTERBLOCKGROUP', 0x20); // [A0] -- Basic container of information containing a single Block or BlockVirtual, and information specific to that Block/VirtualBlock. +define('EBML_ID_CLUSTERBLOCK', 0x21); // [A1] -- Block containing the actual data to be rendered and a timecode relative to the Cluster Timecode. +define('EBML_ID_CLUSTERBLOCKVIRTUAL', 0x22); // [A2] -- A Block with no data. It must be stored in the stream at the place the real Block should be in display order. +define('EBML_ID_CLUSTERSIMPLEBLOCK', 0x23); // [A3] -- Similar to Block but without all the extra information, mostly used to reduced overhead when no extra feature is needed. +define('EBML_ID_CLUSTERCODECSTATE', 0x24); // [A4] -- The new codec state to use. Data interpretation is private to the codec. This information should always be referenced by a seek entry. +define('EBML_ID_CLUSTERBLOCKADDITIONAL', 0x25); // [A5] -- Interpreted by the codec as it wishes (using the BlockAddID). +define('EBML_ID_CLUSTERBLOCKMORE', 0x26); // [A6] -- Contain the BlockAdditional and some parameters. +define('EBML_ID_CLUSTERPOSITION', 0x27); // [A7] -- Position of the Cluster in the segment (0 in live broadcast streams). It might help to resynchronise offset on damaged streams. +define('EBML_ID_CODECDECODEALL', 0x2A); // [AA] -- The codec can decode potentially damaged data. +define('EBML_ID_CLUSTERPREVSIZE', 0x2B); // [AB] -- Size of the previous Cluster, in octets. Can be useful for backward playing. +define('EBML_ID_TRACKENTRY', 0x2E); // [AE] -- Describes a track with all elements. +define('EBML_ID_CLUSTERENCRYPTEDBLOCK', 0x2F); // [AF] -- Similar to SimpleBlock but the data inside the Block are Transformed (encrypt and/or signed). +define('EBML_ID_PIXELWIDTH', 0x30); // [B0] -- Width of the encoded video frames in pixels. +define('EBML_ID_CUETIME', 0x33); // [B3] -- Absolute timecode according to the segment time base. +define('EBML_ID_SAMPLINGFREQUENCY', 0x35); // [B5] -- Sampling frequency in Hz. +define('EBML_ID_CHAPTERATOM', 0x36); // [B6] -- Contains the atom information to use as the chapter atom (apply to all tracks). +define('EBML_ID_CUETRACKPOSITIONS', 0x37); // [B7] -- Contain positions for different tracks corresponding to the timecode. +define('EBML_ID_FLAGENABLED', 0x39); // [B9] -- Set if the track is used. +define('EBML_ID_PIXELHEIGHT', 0x3A); // [BA] -- Height of the encoded video frames in pixels. +define('EBML_ID_CUEPOINT', 0x3B); // [BB] -- Contains all information relative to a seek point in the segment. +define('EBML_ID_CRC32', 0x3F); // [BF] -- The CRC is computed on all the data of the Master element it's in, regardless of its position. It's recommended to put the CRC value at the beggining of the Master element for easier reading. All level 1 elements should include a CRC-32. +define('EBML_ID_CLUSTERBLOCKADDITIONID', 0x4B); // [CB] -- The ID of the BlockAdditional element (0 is the main Block). +define('EBML_ID_CLUSTERLACENUMBER', 0x4C); // [CC] -- The reverse number of the frame in the lace (0 is the last frame, 1 is the next to last, etc). While there are a few files in the wild with this element, it is no longer in use and has been deprecated. Being able to interpret this element is not required for playback. +define('EBML_ID_CLUSTERFRAMENUMBER', 0x4D); // [CD] -- The number of the frame to generate from this lace with this delay (allow you to generate many frames from the same Block/Frame). +define('EBML_ID_CLUSTERDELAY', 0x4E); // [CE] -- The (scaled) delay to apply to the element. +define('EBML_ID_CLUSTERDURATION', 0x4F); // [CF] -- The (scaled) duration to apply to the element. +define('EBML_ID_TRACKNUMBER', 0x57); // [D7] -- The track number as used in the Block Header (using more than 127 tracks is not encouraged, though the design allows an unlimited number). +define('EBML_ID_CUEREFERENCE', 0x5B); // [DB] -- The Clusters containing the required referenced Blocks. +define('EBML_ID_VIDEO', 0x60); // [E0] -- Video settings. +define('EBML_ID_AUDIO', 0x61); // [E1] -- Audio settings. +define('EBML_ID_CLUSTERTIMESLICE', 0x68); // [E8] -- Contains extra time information about the data contained in the Block. While there are a few files in the wild with this element, it is no longer in use and has been deprecated. Being able to interpret this element is not required for playback. +define('EBML_ID_CUECODECSTATE', 0x6A); // [EA] -- The position of the Codec State corresponding to this Cue element. 0 means that the data is taken from the initial Track Entry. +define('EBML_ID_CUEREFCODECSTATE', 0x6B); // [EB] -- The position of the Codec State corresponding to this referenced element. 0 means that the data is taken from the initial Track Entry. +define('EBML_ID_VOID', 0x6C); // [EC] -- Used to void damaged data, to avoid unexpected behaviors when using damaged data. The content is discarded. Also used to reserve space in a sub-element for later use. +define('EBML_ID_CLUSTERTIMECODE', 0x67); // [E7] -- Absolute timecode of the cluster (based on TimecodeScale). +define('EBML_ID_CLUSTERBLOCKADDID', 0x6E); // [EE] -- An ID to identify the BlockAdditional level. +define('EBML_ID_CUECLUSTERPOSITION', 0x71); // [F1] -- The position of the Cluster containing the required Block. +define('EBML_ID_CUETRACK', 0x77); // [F7] -- The track for which a position is given. +define('EBML_ID_CLUSTERREFERENCEPRIORITY', 0x7A); // [FA] -- This frame is referenced and has the specified cache priority. In cache only a frame of the same or higher priority can replace this frame. A value of 0 means the frame is not referenced. +define('EBML_ID_CLUSTERREFERENCEBLOCK', 0x7B); // [FB] -- Timecode of another frame used as a reference (ie: B or P frame). The timecode is relative to the block it's attached to. +define('EBML_ID_CLUSTERREFERENCEVIRTUAL', 0x7D); // [FD] -- Relative position of the data that should be in position of the virtual block. + + +/** +* @tutorial http://www.matroska.org/technical/specs/index.html +* +* @todo Rewrite EBML parser to reduce it's size and honor default element values +* @todo After rewrite implement stream size calculation, that will provide additional useful info and enable AAC/FLAC audio bitrate detection +*/ +class getid3_matroska extends getid3_handler +{ + // public options + public static $hide_clusters = true; // if true, do not return information about CLUSTER chunks, since there's a lot of them and they're not usually useful [default: TRUE] + public static $parse_whole_file = false; // true to parse the whole file, not only header [default: FALSE] + + // private parser settings/placeholders + private $EBMLbuffer = ''; + private $EBMLbuffer_offset = 0; + private $EBMLbuffer_length = 0; + private $current_offset = 0; + private $unuseful_elements = array(EBML_ID_CRC32, EBML_ID_VOID); + + public function Analyze() + { + $info = &$this->getid3->info; + + // parse container + try { + $this->parseEBML($info); + } catch (Exception $e) { + $info['error'][] = 'EBML parser: '.$e->getMessage(); + } + + // calculate playtime + if (isset($info['matroska']['info']) && is_array($info['matroska']['info'])) { + foreach ($info['matroska']['info'] as $key => $infoarray) { + if (isset($infoarray['Duration'])) { + // TimecodeScale is how many nanoseconds each Duration unit is + $info['playtime_seconds'] = $infoarray['Duration'] * ((isset($infoarray['TimecodeScale']) ? $infoarray['TimecodeScale'] : 1000000) / 1000000000); + break; + } + } + } + + // extract tags + if (isset($info['matroska']['tags']) && is_array($info['matroska']['tags'])) { + foreach ($info['matroska']['tags'] as $key => $infoarray) { + $this->ExtractCommentsSimpleTag($infoarray); + } + } + + // process tracks + if (isset($info['matroska']['tracks']['tracks']) && is_array($info['matroska']['tracks']['tracks'])) { + foreach ($info['matroska']['tracks']['tracks'] as $key => $trackarray) { + + $track_info = array(); + $track_info['dataformat'] = self::CodecIDtoCommonName($trackarray['CodecID']); + $track_info['default'] = (isset($trackarray['FlagDefault']) ? $trackarray['FlagDefault'] : true); + if (isset($trackarray['Name'])) { $track_info['name'] = $trackarray['Name']; } + + switch ($trackarray['TrackType']) { + + case 1: // Video + $track_info['resolution_x'] = $trackarray['PixelWidth']; + $track_info['resolution_y'] = $trackarray['PixelHeight']; + $track_info['display_unit'] = self::displayUnit(isset($trackarray['DisplayUnit']) ? $trackarray['DisplayUnit'] : 0); + $track_info['display_x'] = (isset($trackarray['DisplayWidth']) ? $trackarray['DisplayWidth'] : $trackarray['PixelWidth']); + $track_info['display_y'] = (isset($trackarray['DisplayHeight']) ? $trackarray['DisplayHeight'] : $trackarray['PixelHeight']); + + if (isset($trackarray['PixelCropBottom'])) { $track_info['crop_bottom'] = $trackarray['PixelCropBottom']; } + if (isset($trackarray['PixelCropTop'])) { $track_info['crop_top'] = $trackarray['PixelCropTop']; } + if (isset($trackarray['PixelCropLeft'])) { $track_info['crop_left'] = $trackarray['PixelCropLeft']; } + if (isset($trackarray['PixelCropRight'])) { $track_info['crop_right'] = $trackarray['PixelCropRight']; } + if (isset($trackarray['DefaultDuration'])) { $track_info['frame_rate'] = round(1000000000 / $trackarray['DefaultDuration'], 3); } + if (isset($trackarray['CodecName'])) { $track_info['codec'] = $trackarray['CodecName']; } + + switch ($trackarray['CodecID']) { + case 'V_MS/VFW/FOURCC': + getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); + + $parsed = getid3_riff::ParseBITMAPINFOHEADER($trackarray['CodecPrivate']); + $track_info['codec'] = getid3_riff::fourccLookup($parsed['fourcc']); + $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $parsed; + break; + + /*case 'V_MPEG4/ISO/AVC': + $h264['profile'] = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], 1, 1)); + $h264['level'] = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], 3, 1)); + $rn = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], 4, 1)); + $h264['NALUlength'] = ($rn & 3) + 1; + $rn = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], 5, 1)); + $nsps = ($rn & 31); + $offset = 6; + for ($i = 0; $i < $nsps; $i ++) { + $length = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], $offset, 2)); + $h264['SPS'][] = substr($trackarray['CodecPrivate'], $offset + 2, $length); + $offset += 2 + $length; + } + $npps = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], $offset, 1)); + $offset += 1; + for ($i = 0; $i < $npps; $i ++) { + $length = getid3_lib::BigEndian2Int(substr($trackarray['CodecPrivate'], $offset, 2)); + $h264['PPS'][] = substr($trackarray['CodecPrivate'], $offset + 2, $length); + $offset += 2 + $length; + } + $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $h264; + break;*/ + } + + $info['video']['streams'][] = $track_info; + break; + + case 2: // Audio + $track_info['sample_rate'] = (isset($trackarray['SamplingFrequency']) ? $trackarray['SamplingFrequency'] : 8000.0); + $track_info['channels'] = (isset($trackarray['Channels']) ? $trackarray['Channels'] : 1); + $track_info['language'] = (isset($trackarray['Language']) ? $trackarray['Language'] : 'eng'); + if (isset($trackarray['BitDepth'])) { $track_info['bits_per_sample'] = $trackarray['BitDepth']; } + if (isset($trackarray['CodecName'])) { $track_info['codec'] = $trackarray['CodecName']; } + + switch ($trackarray['CodecID']) { + case 'A_PCM/INT/LIT': + case 'A_PCM/INT/BIG': + $track_info['bitrate'] = $trackarray['SamplingFrequency'] * $trackarray['Channels'] * $trackarray['BitDepth']; + break; + + case 'A_AC3': + case 'A_DTS': + case 'A_MPEG/L3': + case 'A_MPEG/L2': + case 'A_FLAC': + getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.'.($track_info['dataformat'] == 'mp2' ? 'mp3' : $track_info['dataformat']).'.php', __FILE__, true); + + if (!isset($info['matroska']['track_data_offsets'][$trackarray['TrackNumber']])) { + $this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because $info[matroska][track_data_offsets]['.$trackarray['TrackNumber'].'] not set'); + break; + } + + // create temp instance + $getid3_temp = new getID3(); + if ($track_info['dataformat'] != 'flac') { + $getid3_temp->openfile($this->getid3->filename); + } + $getid3_temp->info['avdataoffset'] = $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset']; + if ($track_info['dataformat'][0] == 'm' || $track_info['dataformat'] == 'flac') { + $getid3_temp->info['avdataend'] = $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset'] + $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['length']; + } + + // analyze + $class = 'getid3_'.($track_info['dataformat'] == 'mp2' ? 'mp3' : $track_info['dataformat']); + $header_data_key = $track_info['dataformat'][0] == 'm' ? 'mpeg' : $track_info['dataformat']; + $getid3_audio = new $class($getid3_temp, __CLASS__); + if ($track_info['dataformat'] == 'flac') { + $getid3_audio->AnalyzeString($trackarray['CodecPrivate']); + } + else { + $getid3_audio->Analyze(); + } + if (!empty($getid3_temp->info[$header_data_key])) { + $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $getid3_temp->info[$header_data_key]; + if (isset($getid3_temp->info['audio']) && is_array($getid3_temp->info['audio'])) { + foreach ($getid3_temp->info['audio'] as $key => $value) { + $track_info[$key] = $value; + } + } + } + else { + $this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because '.$class.'::Analyze() failed at offset '.$getid3_temp->info['avdataoffset']); + } + + // copy errors and warnings + if (!empty($getid3_temp->info['error'])) { + foreach ($getid3_temp->info['error'] as $newerror) { + $this->warning($class.'() says: ['.$newerror.']'); + } + } + if (!empty($getid3_temp->info['warning'])) { + foreach ($getid3_temp->info['warning'] as $newerror) { + $this->warning($class.'() says: ['.$newerror.']'); + } + } + unset($getid3_temp, $getid3_audio); + break; + + case 'A_AAC': + case 'A_AAC/MPEG2/LC': + case 'A_AAC/MPEG2/LC/SBR': + case 'A_AAC/MPEG4/LC': + case 'A_AAC/MPEG4/LC/SBR': + $this->warning($trackarray['CodecID'].' audio data contains no header, audio/video bitrates can\'t be calculated'); + break; + + case 'A_VORBIS': + if (!isset($trackarray['CodecPrivate'])) { + $this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because CodecPrivate data not set'); + break; + } + $vorbis_offset = strpos($trackarray['CodecPrivate'], 'vorbis', 1); + if ($vorbis_offset === false) { + $this->warning('Unable to parse audio data ['.basename(__FILE__).':'.__LINE__.'] because CodecPrivate data does not contain "vorbis" keyword'); + break; + } + $vorbis_offset -= 1; + + getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ogg.php', __FILE__, true); + + // create temp instance + $getid3_temp = new getID3(); + + // analyze + $getid3_ogg = new getid3_ogg($getid3_temp); + $oggpageinfo['page_seqno'] = 0; + $getid3_ogg->ParseVorbisPageHeader($trackarray['CodecPrivate'], $vorbis_offset, $oggpageinfo); + if (!empty($getid3_temp->info['ogg'])) { + $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $getid3_temp->info['ogg']; + if (isset($getid3_temp->info['audio']) && is_array($getid3_temp->info['audio'])) { + foreach ($getid3_temp->info['audio'] as $key => $value) { + $track_info[$key] = $value; + } + } + } + + // copy errors and warnings + if (!empty($getid3_temp->info['error'])) { + foreach ($getid3_temp->info['error'] as $newerror) { + $this->warning('getid3_ogg() says: ['.$newerror.']'); + } + } + if (!empty($getid3_temp->info['warning'])) { + foreach ($getid3_temp->info['warning'] as $newerror) { + $this->warning('getid3_ogg() says: ['.$newerror.']'); + } + } + + if (!empty($getid3_temp->info['ogg']['bitrate_nominal'])) { + $track_info['bitrate'] = $getid3_temp->info['ogg']['bitrate_nominal']; + } + unset($getid3_temp, $getid3_ogg, $oggpageinfo, $vorbis_offset); + break; + + case 'A_MS/ACM': + getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true); + + $parsed = getid3_riff::parseWAVEFORMATex($trackarray['CodecPrivate']); + foreach ($parsed as $key => $value) { + if ($key != 'raw') { + $track_info[$key] = $value; + } + } + $info['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $parsed; + break; + + default: + $this->warning('Unhandled audio type "'.(isset($trackarray['CodecID']) ? $trackarray['CodecID'] : '').'"'); + } + + $info['audio']['streams'][] = $track_info; + break; + } + } + + if (!empty($info['video']['streams'])) { + $info['video'] = self::getDefaultStreamInfo($info['video']['streams']); + } + if (!empty($info['audio']['streams'])) { + $info['audio'] = self::getDefaultStreamInfo($info['audio']['streams']); + } + } + + // process attachments + if (isset($info['matroska']['attachments']) && $this->getid3->option_save_attachments !== getID3::ATTACHMENTS_NONE) { + foreach ($info['matroska']['attachments'] as $i => $entry) { + if (strpos($entry['FileMimeType'], 'image/') === 0 && !empty($entry['FileData'])) { + $info['matroska']['comments']['picture'][] = array('data' => $entry['FileData'], 'image_mime' => $entry['FileMimeType'], 'filename' => $entry['FileName']); + } + } + } + + // determine mime type + if (!empty($info['video']['streams'])) { + $info['mime_type'] = ($info['matroska']['doctype'] == 'webm' ? 'video/webm' : 'video/x-matroska'); + } elseif (!empty($info['audio']['streams'])) { + $info['mime_type'] = ($info['matroska']['doctype'] == 'webm' ? 'audio/webm' : 'audio/x-matroska'); + } elseif (isset($info['mime_type'])) { + unset($info['mime_type']); + } + + return true; + } + + private function parseEBML(&$info) { + // http://www.matroska.org/technical/specs/index.html#EBMLBasics + $this->current_offset = $info['avdataoffset']; + + while ($this->getEBMLelement($top_element, $info['avdataend'])) { + switch ($top_element['id']) { + + case EBML_ID_EBML: + $info['matroska']['header']['offset'] = $top_element['offset']; + $info['matroska']['header']['length'] = $top_element['length']; + + while ($this->getEBMLelement($element_data, $top_element['end'], true)) { + switch ($element_data['id']) { + + case EBML_ID_EBMLVERSION: + case EBML_ID_EBMLREADVERSION: + case EBML_ID_EBMLMAXIDLENGTH: + case EBML_ID_EBMLMAXSIZELENGTH: + case EBML_ID_DOCTYPEVERSION: + case EBML_ID_DOCTYPEREADVERSION: + $element_data['data'] = getid3_lib::BigEndian2Int($element_data['data']); + break; + + case EBML_ID_DOCTYPE: + $element_data['data'] = getid3_lib::trimNullByte($element_data['data']); + $info['matroska']['doctype'] = $element_data['data']; + $info['fileformat'] = $element_data['data']; + break; + + default: + $this->unhandledElement('header', __LINE__, $element_data); + } + + unset($element_data['offset'], $element_data['end']); + $info['matroska']['header']['elements'][] = $element_data; + } + break; + + case EBML_ID_SEGMENT: + $info['matroska']['segment'][0]['offset'] = $top_element['offset']; + $info['matroska']['segment'][0]['length'] = $top_element['length']; + + while ($this->getEBMLelement($element_data, $top_element['end'])) { + if ($element_data['id'] != EBML_ID_CLUSTER || !self::$hide_clusters) { // collect clusters only if required + $info['matroska']['segments'][] = $element_data; + } + switch ($element_data['id']) { + + case EBML_ID_SEEKHEAD: // Contains the position of other level 1 elements. + + while ($this->getEBMLelement($seek_entry, $element_data['end'])) { + switch ($seek_entry['id']) { + + case EBML_ID_SEEK: // Contains a single seek entry to an EBML element + while ($this->getEBMLelement($sub_seek_entry, $seek_entry['end'], true)) { + + switch ($sub_seek_entry['id']) { + + case EBML_ID_SEEKID: + $seek_entry['target_id'] = self::EBML2Int($sub_seek_entry['data']); + $seek_entry['target_name'] = self::EBMLidName($seek_entry['target_id']); + break; + + case EBML_ID_SEEKPOSITION: + $seek_entry['target_offset'] = $element_data['offset'] + getid3_lib::BigEndian2Int($sub_seek_entry['data']); + break; + + default: + $this->unhandledElement('seekhead.seek', __LINE__, $sub_seek_entry); } + } + + if ($seek_entry['target_id'] != EBML_ID_CLUSTER || !self::$hide_clusters) { // collect clusters only if required + $info['matroska']['seek'][] = $seek_entry; + } + break; + + default: + $this->unhandledElement('seekhead', __LINE__, $seek_entry); + } + } + break; + + case EBML_ID_TRACKS: // A top-level block of information with many tracks described. + $info['matroska']['tracks'] = $element_data; + + while ($this->getEBMLelement($track_entry, $element_data['end'])) { + switch ($track_entry['id']) { + + case EBML_ID_TRACKENTRY: //subelements: Describes a track with all elements. + + while ($this->getEBMLelement($subelement, $track_entry['end'], array(EBML_ID_VIDEO, EBML_ID_AUDIO, EBML_ID_CONTENTENCODINGS, EBML_ID_CODECPRIVATE))) { + switch ($subelement['id']) { + + case EBML_ID_TRACKNUMBER: + case EBML_ID_TRACKUID: + case EBML_ID_TRACKTYPE: + case EBML_ID_MINCACHE: + case EBML_ID_MAXCACHE: + case EBML_ID_MAXBLOCKADDITIONID: + case EBML_ID_DEFAULTDURATION: // nanoseconds per frame + $track_entry[$subelement['id_name']] = getid3_lib::BigEndian2Int($subelement['data']); + break; + + case EBML_ID_TRACKTIMECODESCALE: + $track_entry[$subelement['id_name']] = getid3_lib::BigEndian2Float($subelement['data']); + break; + + case EBML_ID_CODECID: + case EBML_ID_LANGUAGE: + case EBML_ID_NAME: + case EBML_ID_CODECNAME: + $track_entry[$subelement['id_name']] = getid3_lib::trimNullByte($subelement['data']); + break; + + case EBML_ID_CODECPRIVATE: + $track_entry[$subelement['id_name']] = $this->readEBMLelementData($subelement['length'], true); + break; + + case EBML_ID_FLAGENABLED: + case EBML_ID_FLAGDEFAULT: + case EBML_ID_FLAGFORCED: + case EBML_ID_FLAGLACING: + case EBML_ID_CODECDECODEALL: + $track_entry[$subelement['id_name']] = (bool) getid3_lib::BigEndian2Int($subelement['data']); + break; + + case EBML_ID_VIDEO: + + while ($this->getEBMLelement($sub_subelement, $subelement['end'], true)) { + switch ($sub_subelement['id']) { + + case EBML_ID_PIXELWIDTH: + case EBML_ID_PIXELHEIGHT: + case EBML_ID_PIXELCROPBOTTOM: + case EBML_ID_PIXELCROPTOP: + case EBML_ID_PIXELCROPLEFT: + case EBML_ID_PIXELCROPRIGHT: + case EBML_ID_DISPLAYWIDTH: + case EBML_ID_DISPLAYHEIGHT: + case EBML_ID_DISPLAYUNIT: + case EBML_ID_ASPECTRATIOTYPE: + case EBML_ID_STEREOMODE: + case EBML_ID_OLDSTEREOMODE: + $track_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']); + break; + + case EBML_ID_FLAGINTERLACED: + $track_entry[$sub_subelement['id_name']] = (bool)getid3_lib::BigEndian2Int($sub_subelement['data']); + break; + + case EBML_ID_GAMMAVALUE: + $track_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Float($sub_subelement['data']); + break; + + case EBML_ID_COLOURSPACE: + $track_entry[$sub_subelement['id_name']] = getid3_lib::trimNullByte($sub_subelement['data']); + break; + + default: + $this->unhandledElement('track.video', __LINE__, $sub_subelement); + } + } + break; + + case EBML_ID_AUDIO: + + while ($this->getEBMLelement($sub_subelement, $subelement['end'], true)) { + switch ($sub_subelement['id']) { + + case EBML_ID_CHANNELS: + case EBML_ID_BITDEPTH: + $track_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']); + break; + + case EBML_ID_SAMPLINGFREQUENCY: + case EBML_ID_OUTPUTSAMPLINGFREQUENCY: + $track_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Float($sub_subelement['data']); + break; + + case EBML_ID_CHANNELPOSITIONS: + $track_entry[$sub_subelement['id_name']] = getid3_lib::trimNullByte($sub_subelement['data']); + break; + + default: + $this->unhandledElement('track.audio', __LINE__, $sub_subelement); + } + } + break; + + case EBML_ID_CONTENTENCODINGS: + + while ($this->getEBMLelement($sub_subelement, $subelement['end'])) { + switch ($sub_subelement['id']) { + + case EBML_ID_CONTENTENCODING: + + while ($this->getEBMLelement($sub_sub_subelement, $sub_subelement['end'], array(EBML_ID_CONTENTCOMPRESSION, EBML_ID_CONTENTENCRYPTION))) { + switch ($sub_sub_subelement['id']) { + + case EBML_ID_CONTENTENCODINGORDER: + case EBML_ID_CONTENTENCODINGSCOPE: + case EBML_ID_CONTENTENCODINGTYPE: + $track_entry[$sub_subelement['id_name']][$sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']); + break; + + case EBML_ID_CONTENTCOMPRESSION: + + while ($this->getEBMLelement($sub_sub_sub_subelement, $sub_sub_subelement['end'], true)) { + switch ($sub_sub_sub_subelement['id']) { + + case EBML_ID_CONTENTCOMPALGO: + $track_entry[$sub_subelement['id_name']][$sub_sub_subelement['id_name']][$sub_sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_sub_subelement['data']); + break; + + case EBML_ID_CONTENTCOMPSETTINGS: + $track_entry[$sub_subelement['id_name']][$sub_sub_subelement['id_name']][$sub_sub_sub_subelement['id_name']] = $sub_sub_sub_subelement['data']; + break; + + default: + $this->unhandledElement('track.contentencodings.contentencoding.contentcompression', __LINE__, $sub_sub_sub_subelement); + } + } + break; + + case EBML_ID_CONTENTENCRYPTION: + + while ($this->getEBMLelement($sub_sub_sub_subelement, $sub_sub_subelement['end'], true)) { + switch ($sub_sub_sub_subelement['id']) { + + case EBML_ID_CONTENTENCALGO: + case EBML_ID_CONTENTSIGALGO: + case EBML_ID_CONTENTSIGHASHALGO: + $track_entry[$sub_subelement['id_name']][$sub_sub_subelement['id_name']][$sub_sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_sub_subelement['data']); + break; + + case EBML_ID_CONTENTENCKEYID: + case EBML_ID_CONTENTSIGNATURE: + case EBML_ID_CONTENTSIGKEYID: + $track_entry[$sub_subelement['id_name']][$sub_sub_subelement['id_name']][$sub_sub_sub_subelement['id_name']] = $sub_sub_sub_subelement['data']; + break; + + default: + $this->unhandledElement('track.contentencodings.contentencoding.contentcompression', __LINE__, $sub_sub_sub_subelement); + } + } + break; + + default: + $this->unhandledElement('track.contentencodings.contentencoding', __LINE__, $sub_sub_subelement); + } + } + break; + + default: + $this->unhandledElement('track.contentencodings', __LINE__, $sub_subelement); + } + } + break; + + default: + $this->unhandledElement('track', __LINE__, $subelement); + } + } + + $info['matroska']['tracks']['tracks'][] = $track_entry; + break; + + default: + $this->unhandledElement('tracks', __LINE__, $track_entry); + } + } + break; + + case EBML_ID_INFO: // Contains miscellaneous general information and statistics on the file. + $info_entry = array(); + + while ($this->getEBMLelement($subelement, $element_data['end'], true)) { + switch ($subelement['id']) { + + case EBML_ID_TIMECODESCALE: + $info_entry[$subelement['id_name']] = getid3_lib::BigEndian2Int($subelement['data']); + break; + + case EBML_ID_DURATION: + $info_entry[$subelement['id_name']] = getid3_lib::BigEndian2Float($subelement['data']); + break; + + case EBML_ID_DATEUTC: + $info_entry[$subelement['id_name']] = getid3_lib::BigEndian2Int($subelement['data']); + $info_entry[$subelement['id_name'].'_unix'] = self::EBMLdate2unix($info_entry[$subelement['id_name']]); + break; + + case EBML_ID_SEGMENTUID: + case EBML_ID_PREVUID: + case EBML_ID_NEXTUID: + $info_entry[$subelement['id_name']] = getid3_lib::trimNullByte($subelement['data']); + break; + + case EBML_ID_SEGMENTFAMILY: + $info_entry[$subelement['id_name']][] = getid3_lib::trimNullByte($subelement['data']); + break; + + case EBML_ID_SEGMENTFILENAME: + case EBML_ID_PREVFILENAME: + case EBML_ID_NEXTFILENAME: + case EBML_ID_TITLE: + case EBML_ID_MUXINGAPP: + case EBML_ID_WRITINGAPP: + $info_entry[$subelement['id_name']] = getid3_lib::trimNullByte($subelement['data']); + $info['matroska']['comments'][strtolower($subelement['id_name'])][] = $info_entry[$subelement['id_name']]; + break; + + case EBML_ID_CHAPTERTRANSLATE: + $chaptertranslate_entry = array(); + + while ($this->getEBMLelement($sub_subelement, $subelement['end'], true)) { + switch ($sub_subelement['id']) { + + case EBML_ID_CHAPTERTRANSLATEEDITIONUID: + $chaptertranslate_entry[$sub_subelement['id_name']][] = getid3_lib::BigEndian2Int($sub_subelement['data']); + break; + + case EBML_ID_CHAPTERTRANSLATECODEC: + $chaptertranslate_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']); + break; + + case EBML_ID_CHAPTERTRANSLATEID: + $chaptertranslate_entry[$sub_subelement['id_name']] = getid3_lib::trimNullByte($sub_subelement['data']); + break; + + default: + $this->unhandledElement('info.chaptertranslate', __LINE__, $sub_subelement); + } + } + $info_entry[$subelement['id_name']] = $chaptertranslate_entry; + break; + + default: + $this->unhandledElement('info', __LINE__, $subelement); + } + } + $info['matroska']['info'][] = $info_entry; + break; + + case EBML_ID_CUES: // A top-level element to speed seeking access. All entries are local to the segment. Should be mandatory for non "live" streams. + if (self::$hide_clusters) { // do not parse cues if hide clusters is "ON" till they point to clusters anyway + $this->current_offset = $element_data['end']; + break; + } + $cues_entry = array(); + + while ($this->getEBMLelement($subelement, $element_data['end'])) { + switch ($subelement['id']) { + + case EBML_ID_CUEPOINT: + $cuepoint_entry = array(); + + while ($this->getEBMLelement($sub_subelement, $subelement['end'], array(EBML_ID_CUETRACKPOSITIONS))) { + switch ($sub_subelement['id']) { + + case EBML_ID_CUETRACKPOSITIONS: + $cuetrackpositions_entry = array(); + + while ($this->getEBMLelement($sub_sub_subelement, $sub_subelement['end'], true)) { + switch ($sub_sub_subelement['id']) { + + case EBML_ID_CUETRACK: + case EBML_ID_CUECLUSTERPOSITION: + case EBML_ID_CUEBLOCKNUMBER: + case EBML_ID_CUECODECSTATE: + $cuetrackpositions_entry[$sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']); + break; + + default: + $this->unhandledElement('cues.cuepoint.cuetrackpositions', __LINE__, $sub_sub_subelement); + } + } + $cuepoint_entry[$sub_subelement['id_name']][] = $cuetrackpositions_entry; + break; + + case EBML_ID_CUETIME: + $cuepoint_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']); + break; + + default: + $this->unhandledElement('cues.cuepoint', __LINE__, $sub_subelement); + } + } + $cues_entry[] = $cuepoint_entry; + break; + + default: + $this->unhandledElement('cues', __LINE__, $subelement); + } + } + $info['matroska']['cues'] = $cues_entry; + break; + + case EBML_ID_TAGS: // Element containing elements specific to Tracks/Chapters. + $tags_entry = array(); + + while ($this->getEBMLelement($subelement, $element_data['end'], false)) { + switch ($subelement['id']) { + + case EBML_ID_TAG: + $tag_entry = array(); + + while ($this->getEBMLelement($sub_subelement, $subelement['end'], false)) { + switch ($sub_subelement['id']) { + + case EBML_ID_TARGETS: + $targets_entry = array(); + + while ($this->getEBMLelement($sub_sub_subelement, $sub_subelement['end'], true)) { + switch ($sub_sub_subelement['id']) { + + case EBML_ID_TARGETTYPEVALUE: + $targets_entry[$sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']); + $targets_entry[strtolower($sub_sub_subelement['id_name']).'_long'] = self::TargetTypeValue($targets_entry[$sub_sub_subelement['id_name']]); + break; + + case EBML_ID_TARGETTYPE: + $targets_entry[$sub_sub_subelement['id_name']] = $sub_sub_subelement['data']; + break; + + case EBML_ID_TAGTRACKUID: + case EBML_ID_TAGEDITIONUID: + case EBML_ID_TAGCHAPTERUID: + case EBML_ID_TAGATTACHMENTUID: + $targets_entry[$sub_sub_subelement['id_name']][] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']); + break; + + default: + $this->unhandledElement('tags.tag.targets', __LINE__, $sub_sub_subelement); + } + } + $tag_entry[$sub_subelement['id_name']] = $targets_entry; + break; + + case EBML_ID_SIMPLETAG: + $tag_entry[$sub_subelement['id_name']][] = $this->HandleEMBLSimpleTag($sub_subelement['end']); + break; + + default: + $this->unhandledElement('tags.tag', __LINE__, $sub_subelement); + } + } + $tags_entry[] = $tag_entry; + break; + + default: + $this->unhandledElement('tags', __LINE__, $subelement); + } + } + $info['matroska']['tags'] = $tags_entry; + break; + + case EBML_ID_ATTACHMENTS: // Contain attached files. + + while ($this->getEBMLelement($subelement, $element_data['end'])) { + switch ($subelement['id']) { + + case EBML_ID_ATTACHEDFILE: + $attachedfile_entry = array(); + + while ($this->getEBMLelement($sub_subelement, $subelement['end'], array(EBML_ID_FILEDATA))) { + switch ($sub_subelement['id']) { + + case EBML_ID_FILEDESCRIPTION: + case EBML_ID_FILENAME: + case EBML_ID_FILEMIMETYPE: + $attachedfile_entry[$sub_subelement['id_name']] = $sub_subelement['data']; + break; + + case EBML_ID_FILEDATA: + $attachedfile_entry['data_offset'] = $this->current_offset; + $attachedfile_entry['data_length'] = $sub_subelement['length']; + + $attachedfile_entry[$sub_subelement['id_name']] = $this->saveAttachment( + $attachedfile_entry['FileName'], + $attachedfile_entry['data_offset'], + $attachedfile_entry['data_length']); + + $this->current_offset = $sub_subelement['end']; + break; + + case EBML_ID_FILEUID: + $attachedfile_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']); + break; + + default: + $this->unhandledElement('attachments.attachedfile', __LINE__, $sub_subelement); + } + } + $info['matroska']['attachments'][] = $attachedfile_entry; + break; + + default: + $this->unhandledElement('attachments', __LINE__, $subelement); + } + } + break; + + case EBML_ID_CHAPTERS: + + while ($this->getEBMLelement($subelement, $element_data['end'])) { + switch ($subelement['id']) { + + case EBML_ID_EDITIONENTRY: + $editionentry_entry = array(); + + while ($this->getEBMLelement($sub_subelement, $subelement['end'], array(EBML_ID_CHAPTERATOM))) { + switch ($sub_subelement['id']) { + + case EBML_ID_EDITIONUID: + $editionentry_entry[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']); + break; + + case EBML_ID_EDITIONFLAGHIDDEN: + case EBML_ID_EDITIONFLAGDEFAULT: + case EBML_ID_EDITIONFLAGORDERED: + $editionentry_entry[$sub_subelement['id_name']] = (bool)getid3_lib::BigEndian2Int($sub_subelement['data']); + break; + + case EBML_ID_CHAPTERATOM: + $chapteratom_entry = array(); + + while ($this->getEBMLelement($sub_sub_subelement, $sub_subelement['end'], array(EBML_ID_CHAPTERTRACK, EBML_ID_CHAPTERDISPLAY))) { + switch ($sub_sub_subelement['id']) { + + case EBML_ID_CHAPTERSEGMENTUID: + case EBML_ID_CHAPTERSEGMENTEDITIONUID: + $chapteratom_entry[$sub_sub_subelement['id_name']] = $sub_sub_subelement['data']; + break; + + case EBML_ID_CHAPTERFLAGENABLED: + case EBML_ID_CHAPTERFLAGHIDDEN: + $chapteratom_entry[$sub_sub_subelement['id_name']] = (bool)getid3_lib::BigEndian2Int($sub_sub_subelement['data']); + break; + + case EBML_ID_CHAPTERUID: + case EBML_ID_CHAPTERTIMESTART: + case EBML_ID_CHAPTERTIMEEND: + $chapteratom_entry[$sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']); + break; + + case EBML_ID_CHAPTERTRACK: + $chaptertrack_entry = array(); + + while ($this->getEBMLelement($sub_sub_sub_subelement, $sub_sub_subelement['end'], true)) { + switch ($sub_sub_sub_subelement['id']) { + + case EBML_ID_CHAPTERTRACKNUMBER: + $chaptertrack_entry[$sub_sub_sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_sub_sub_subelement['data']); + break; + + default: + $this->unhandledElement('chapters.editionentry.chapteratom.chaptertrack', __LINE__, $sub_sub_sub_subelement); + } + } + $chapteratom_entry[$sub_sub_subelement['id_name']][] = $chaptertrack_entry; + break; + + case EBML_ID_CHAPTERDISPLAY: + $chapterdisplay_entry = array(); + + while ($this->getEBMLelement($sub_sub_sub_subelement, $sub_sub_subelement['end'], true)) { + switch ($sub_sub_sub_subelement['id']) { + + case EBML_ID_CHAPSTRING: + case EBML_ID_CHAPLANGUAGE: + case EBML_ID_CHAPCOUNTRY: + $chapterdisplay_entry[$sub_sub_sub_subelement['id_name']] = $sub_sub_sub_subelement['data']; + break; + + default: + $this->unhandledElement('chapters.editionentry.chapteratom.chapterdisplay', __LINE__, $sub_sub_sub_subelement); + } + } + $chapteratom_entry[$sub_sub_subelement['id_name']][] = $chapterdisplay_entry; + break; + + default: + $this->unhandledElement('chapters.editionentry.chapteratom', __LINE__, $sub_sub_subelement); + } + } + $editionentry_entry[$sub_subelement['id_name']][] = $chapteratom_entry; + break; + + default: + $this->unhandledElement('chapters.editionentry', __LINE__, $sub_subelement); + } + } + $info['matroska']['chapters'][] = $editionentry_entry; + break; + + default: + $this->unhandledElement('chapters', __LINE__, $subelement); + } + } + break; + + case EBML_ID_CLUSTER: // The lower level element containing the (monolithic) Block structure. + $cluster_entry = array(); + + while ($this->getEBMLelement($subelement, $element_data['end'], array(EBML_ID_CLUSTERSILENTTRACKS, EBML_ID_CLUSTERBLOCKGROUP, EBML_ID_CLUSTERSIMPLEBLOCK))) { + switch ($subelement['id']) { + + case EBML_ID_CLUSTERTIMECODE: + case EBML_ID_CLUSTERPOSITION: + case EBML_ID_CLUSTERPREVSIZE: + $cluster_entry[$subelement['id_name']] = getid3_lib::BigEndian2Int($subelement['data']); + break; + + case EBML_ID_CLUSTERSILENTTRACKS: + $cluster_silent_tracks = array(); + + while ($this->getEBMLelement($sub_subelement, $subelement['end'], true)) { + switch ($sub_subelement['id']) { + + case EBML_ID_CLUSTERSILENTTRACKNUMBER: + $cluster_silent_tracks[] = getid3_lib::BigEndian2Int($sub_subelement['data']); + break; + + default: + $this->unhandledElement('cluster.silenttracks', __LINE__, $sub_subelement); + } + } + $cluster_entry[$subelement['id_name']][] = $cluster_silent_tracks; + break; + + case EBML_ID_CLUSTERBLOCKGROUP: + $cluster_block_group = array('offset' => $this->current_offset); + + while ($this->getEBMLelement($sub_subelement, $subelement['end'], array(EBML_ID_CLUSTERBLOCK))) { + switch ($sub_subelement['id']) { + + case EBML_ID_CLUSTERBLOCK: + $cluster_block_group[$sub_subelement['id_name']] = $this->HandleEMBLClusterBlock($sub_subelement, EBML_ID_CLUSTERBLOCK, $info); + break; + + case EBML_ID_CLUSTERREFERENCEPRIORITY: // unsigned-int + case EBML_ID_CLUSTERBLOCKDURATION: // unsigned-int + $cluster_block_group[$sub_subelement['id_name']] = getid3_lib::BigEndian2Int($sub_subelement['data']); + break; + + case EBML_ID_CLUSTERREFERENCEBLOCK: // signed-int + $cluster_block_group[$sub_subelement['id_name']][] = getid3_lib::BigEndian2Int($sub_subelement['data'], false, true); + break; + + case EBML_ID_CLUSTERCODECSTATE: + $cluster_block_group[$sub_subelement['id_name']] = getid3_lib::trimNullByte($sub_subelement['data']); + break; + + default: + $this->unhandledElement('clusters.blockgroup', __LINE__, $sub_subelement); + } + } + $cluster_entry[$subelement['id_name']][] = $cluster_block_group; + break; + + case EBML_ID_CLUSTERSIMPLEBLOCK: + $cluster_entry[$subelement['id_name']][] = $this->HandleEMBLClusterBlock($subelement, EBML_ID_CLUSTERSIMPLEBLOCK, $info); + break; + + default: + $this->unhandledElement('cluster', __LINE__, $subelement); + } + $this->current_offset = $subelement['end']; + } + if (!self::$hide_clusters) { + $info['matroska']['cluster'][] = $cluster_entry; + } + + // check to see if all the data we need exists already, if so, break out of the loop + if (!self::$parse_whole_file) { + if (isset($info['matroska']['info']) && is_array($info['matroska']['info'])) { + if (isset($info['matroska']['tracks']['tracks']) && is_array($info['matroska']['tracks']['tracks'])) { + if (count($info['matroska']['track_data_offsets']) == count($info['matroska']['tracks']['tracks'])) { + return; + } + } + } + } + break; + + default: + $this->unhandledElement('segment', __LINE__, $element_data); + } + } + break; + + default: + $this->unhandledElement('root', __LINE__, $top_element); + } + } + } + + private function EnsureBufferHasEnoughData($min_data=1024) { + if (($this->current_offset - $this->EBMLbuffer_offset) >= ($this->EBMLbuffer_length - $min_data)) { + $read_bytes = max($min_data, $this->getid3->fread_buffer_size()); + + try { + $this->fseek($this->current_offset); + $this->EBMLbuffer_offset = $this->current_offset; + $this->EBMLbuffer = $this->fread($read_bytes); + $this->EBMLbuffer_length = strlen($this->EBMLbuffer); + } catch (getid3_exception $e) { + $this->warning('EBML parser: '.$e->getMessage()); + return false; + } + + if ($this->EBMLbuffer_length == 0 && $this->feof()) { + return $this->error('EBML parser: ran out of file at offset '.$this->current_offset); + } + } + return true; + } + + private function readEBMLint() { + $actual_offset = $this->current_offset - $this->EBMLbuffer_offset; + + // get length of integer + $first_byte_int = ord($this->EBMLbuffer[$actual_offset]); + if (0x80 & $first_byte_int) { + $length = 1; + } elseif (0x40 & $first_byte_int) { + $length = 2; + } elseif (0x20 & $first_byte_int) { + $length = 3; + } elseif (0x10 & $first_byte_int) { + $length = 4; + } elseif (0x08 & $first_byte_int) { + $length = 5; + } elseif (0x04 & $first_byte_int) { + $length = 6; + } elseif (0x02 & $first_byte_int) { + $length = 7; + } elseif (0x01 & $first_byte_int) { + $length = 8; + } else { + throw new Exception('invalid EBML integer (leading 0x00) at '.$this->current_offset); + } + + // read + $int_value = self::EBML2Int(substr($this->EBMLbuffer, $actual_offset, $length)); + $this->current_offset += $length; + + return $int_value; + } + + private function readEBMLelementData($length, $check_buffer=false) { + if ($check_buffer && !$this->EnsureBufferHasEnoughData($length)) { + return false; + } + $data = substr($this->EBMLbuffer, $this->current_offset - $this->EBMLbuffer_offset, $length); + $this->current_offset += $length; + return $data; + } + + private function getEBMLelement(&$element, $parent_end, $get_data=false) { + if ($this->current_offset >= $parent_end) { + return false; + } + + if (!$this->EnsureBufferHasEnoughData()) { + $this->current_offset = PHP_INT_MAX; // do not exit parser right now, allow to finish current loop to gather maximum information + return false; + } + + $element = array(); + + // set offset + $element['offset'] = $this->current_offset; + + // get ID + $element['id'] = $this->readEBMLint(); + + // get name + $element['id_name'] = self::EBMLidName($element['id']); + + // get length + $element['length'] = $this->readEBMLint(); + + // get end offset + $element['end'] = $this->current_offset + $element['length']; + + // get raw data + $dont_parse = (in_array($element['id'], $this->unuseful_elements) || $element['id_name'] == dechex($element['id'])); + if (($get_data === true || (is_array($get_data) && !in_array($element['id'], $get_data))) && !$dont_parse) { + $element['data'] = $this->readEBMLelementData($element['length'], $element); + } + + return true; + } + + private function unhandledElement($type, $line, $element) { + // warn only about unknown and missed elements, not about unuseful + if (!in_array($element['id'], $this->unuseful_elements)) { + $this->warning('Unhandled '.$type.' element ['.basename(__FILE__).':'.$line.'] ('.$element['id'].'::'.$element['id_name'].' ['.$element['length'].' bytes]) at '.$element['offset']); + } + + // increase offset for unparsed elements + if (!isset($element['data'])) { + $this->current_offset = $element['end']; + } + } + + private function ExtractCommentsSimpleTag($SimpleTagArray) { + if (!empty($SimpleTagArray['SimpleTag'])) { + foreach ($SimpleTagArray['SimpleTag'] as $SimpleTagKey => $SimpleTagData) { + if (!empty($SimpleTagData['TagName']) && !empty($SimpleTagData['TagString'])) { + $this->getid3->info['matroska']['comments'][strtolower($SimpleTagData['TagName'])][] = $SimpleTagData['TagString']; + } + if (!empty($SimpleTagData['SimpleTag'])) { + $this->ExtractCommentsSimpleTag($SimpleTagData); + } + } + } + + return true; + } + + private function HandleEMBLSimpleTag($parent_end) { + $simpletag_entry = array(); + + while ($this->getEBMLelement($element, $parent_end, array(EBML_ID_SIMPLETAG))) { + switch ($element['id']) { + + case EBML_ID_TAGNAME: + case EBML_ID_TAGLANGUAGE: + case EBML_ID_TAGSTRING: + case EBML_ID_TAGBINARY: + $simpletag_entry[$element['id_name']] = $element['data']; + break; + + case EBML_ID_SIMPLETAG: + $simpletag_entry[$element['id_name']][] = $this->HandleEMBLSimpleTag($element['end']); + break; + + case EBML_ID_TAGDEFAULT: + $simpletag_entry[$element['id_name']] = (bool)getid3_lib::BigEndian2Int($element['data']); + break; + + default: + $this->unhandledElement('tag.simpletag', __LINE__, $element); + } + } + + return $simpletag_entry; + } + + private function HandleEMBLClusterBlock($element, $block_type, &$info) { + // http://www.matroska.org/technical/specs/index.html#block_structure + // http://www.matroska.org/technical/specs/index.html#simpleblock_structure + + $block_data = array(); + $block_data['tracknumber'] = $this->readEBMLint(); + $block_data['timecode'] = getid3_lib::BigEndian2Int($this->readEBMLelementData(2), false, true); + $block_data['flags_raw'] = getid3_lib::BigEndian2Int($this->readEBMLelementData(1)); + + if ($block_type == EBML_ID_CLUSTERSIMPLEBLOCK) { + $block_data['flags']['keyframe'] = (($block_data['flags_raw'] & 0x80) >> 7); + //$block_data['flags']['reserved1'] = (($block_data['flags_raw'] & 0x70) >> 4); + } + else { + //$block_data['flags']['reserved1'] = (($block_data['flags_raw'] & 0xF0) >> 4); + } + $block_data['flags']['invisible'] = (bool)(($block_data['flags_raw'] & 0x08) >> 3); + $block_data['flags']['lacing'] = (($block_data['flags_raw'] & 0x06) >> 1); // 00=no lacing; 01=Xiph lacing; 11=EBML lacing; 10=fixed-size lacing + if ($block_type == EBML_ID_CLUSTERSIMPLEBLOCK) { + $block_data['flags']['discardable'] = (($block_data['flags_raw'] & 0x01)); + } + else { + //$block_data['flags']['reserved2'] = (($block_data['flags_raw'] & 0x01) >> 0); + } + $block_data['flags']['lacing_type'] = self::BlockLacingType($block_data['flags']['lacing']); + + // Lace (when lacing bit is set) + if ($block_data['flags']['lacing'] > 0) { + $block_data['lace_frames'] = getid3_lib::BigEndian2Int($this->readEBMLelementData(1)) + 1; // Number of frames in the lace-1 (uint8) + if ($block_data['flags']['lacing'] != 0x02) { + for ($i = 1; $i < $block_data['lace_frames']; $i ++) { // Lace-coded size of each frame of the lace, except for the last one (multiple uint8). *This is not used with Fixed-size lacing as it is calculated automatically from (total size of lace) / (number of frames in lace). + if ($block_data['flags']['lacing'] == 0x03) { // EBML lacing + $block_data['lace_frames_size'][$i] = $this->readEBMLint(); // TODO: read size correctly, calc size for the last frame. For now offsets are deteminded OK with readEBMLint() and that's the most important thing. + } + else { // Xiph lacing + $block_data['lace_frames_size'][$i] = 0; + do { + $size = getid3_lib::BigEndian2Int($this->readEBMLelementData(1)); + $block_data['lace_frames_size'][$i] += $size; + } + while ($size == 255); + } + } + if ($block_data['flags']['lacing'] == 0x01) { // calc size of the last frame only for Xiph lacing, till EBML sizes are now anyway determined incorrectly + $block_data['lace_frames_size'][] = $element['end'] - $this->current_offset - array_sum($block_data['lace_frames_size']); + } + } + } + + if (!isset($info['matroska']['track_data_offsets'][$block_data['tracknumber']])) { + $info['matroska']['track_data_offsets'][$block_data['tracknumber']]['offset'] = $this->current_offset; + $info['matroska']['track_data_offsets'][$block_data['tracknumber']]['length'] = $element['end'] - $this->current_offset; + //$info['matroska']['track_data_offsets'][$block_data['tracknumber']]['total_length'] = 0; + } + //$info['matroska']['track_data_offsets'][$block_data['tracknumber']]['total_length'] += $info['matroska']['track_data_offsets'][$block_data['tracknumber']]['length']; + //$info['matroska']['track_data_offsets'][$block_data['tracknumber']]['duration'] = $block_data['timecode'] * ((isset($info['matroska']['info'][0]['TimecodeScale']) ? $info['matroska']['info'][0]['TimecodeScale'] : 1000000) / 1000000000); + + // set offset manually + $this->current_offset = $element['end']; + + return $block_data; + } + + private static function EBML2Int($EBMLstring) { + // http://matroska.org/specs/ + + // Element ID coded with an UTF-8 like system: + // 1xxx xxxx - Class A IDs (2^7 -2 possible values) (base 0x8X) + // 01xx xxxx xxxx xxxx - Class B IDs (2^14-2 possible values) (base 0x4X 0xXX) + // 001x xxxx xxxx xxxx xxxx xxxx - Class C IDs (2^21-2 possible values) (base 0x2X 0xXX 0xXX) + // 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx - Class D IDs (2^28-2 possible values) (base 0x1X 0xXX 0xXX 0xXX) + // Values with all x at 0 and 1 are reserved (hence the -2). + + // Data size, in octets, is also coded with an UTF-8 like system : + // 1xxx xxxx - value 0 to 2^7-2 + // 01xx xxxx xxxx xxxx - value 0 to 2^14-2 + // 001x xxxx xxxx xxxx xxxx xxxx - value 0 to 2^21-2 + // 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^28-2 + // 0000 1xxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^35-2 + // 0000 01xx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^42-2 + // 0000 001x xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^49-2 + // 0000 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - value 0 to 2^56-2 + + $first_byte_int = ord($EBMLstring[0]); + if (0x80 & $first_byte_int) { + $EBMLstring[0] = chr($first_byte_int & 0x7F); + } elseif (0x40 & $first_byte_int) { + $EBMLstring[0] = chr($first_byte_int & 0x3F); + } elseif (0x20 & $first_byte_int) { + $EBMLstring[0] = chr($first_byte_int & 0x1F); + } elseif (0x10 & $first_byte_int) { + $EBMLstring[0] = chr($first_byte_int & 0x0F); + } elseif (0x08 & $first_byte_int) { + $EBMLstring[0] = chr($first_byte_int & 0x07); + } elseif (0x04 & $first_byte_int) { + $EBMLstring[0] = chr($first_byte_int & 0x03); + } elseif (0x02 & $first_byte_int) { + $EBMLstring[0] = chr($first_byte_int & 0x01); + } elseif (0x01 & $first_byte_int) { + $EBMLstring[0] = chr($first_byte_int & 0x00); + } + + return getid3_lib::BigEndian2Int($EBMLstring); + } + + private static function EBMLdate2unix($EBMLdatestamp) { + // Date - signed 8 octets integer in nanoseconds with 0 indicating the precise beginning of the millennium (at 2001-01-01T00:00:00,000000000 UTC) + // 978307200 == mktime(0, 0, 0, 1, 1, 2001) == January 1, 2001 12:00:00am UTC + return round(($EBMLdatestamp / 1000000000) + 978307200); + } + + public static function TargetTypeValue($target_type) { + // http://www.matroska.org/technical/specs/tagging/index.html + static $TargetTypeValue = array(); + if (empty($TargetTypeValue)) { + $TargetTypeValue[10] = 'A: ~ V:shot'; // the lowest hierarchy found in music or movies + $TargetTypeValue[20] = 'A:subtrack/part/movement ~ V:scene'; // corresponds to parts of a track for audio (like a movement) + $TargetTypeValue[30] = 'A:track/song ~ V:chapter'; // the common parts of an album or a movie + $TargetTypeValue[40] = 'A:part/session ~ V:part/session'; // when an album or episode has different logical parts + $TargetTypeValue[50] = 'A:album/opera/concert ~ V:movie/episode/concert'; // the most common grouping level of music and video (equals to an episode for TV series) + $TargetTypeValue[60] = 'A:edition/issue/volume/opus ~ V:season/sequel/volume'; // a list of lower levels grouped together + $TargetTypeValue[70] = 'A:collection ~ V:collection'; // the high hierarchy consisting of many different lower items + } + return (isset($TargetTypeValue[$target_type]) ? $TargetTypeValue[$target_type] : $target_type); + } + + public static function BlockLacingType($lacingtype) { + // http://matroska.org/technical/specs/index.html#block_structure + static $BlockLacingType = array(); + if (empty($BlockLacingType)) { + $BlockLacingType[0x00] = 'no lacing'; + $BlockLacingType[0x01] = 'Xiph lacing'; + $BlockLacingType[0x02] = 'fixed-size lacing'; + $BlockLacingType[0x03] = 'EBML lacing'; + } + return (isset($BlockLacingType[$lacingtype]) ? $BlockLacingType[$lacingtype] : $lacingtype); + } + + public static function CodecIDtoCommonName($codecid) { + // http://www.matroska.org/technical/specs/codecid/index.html + static $CodecIDlist = array(); + if (empty($CodecIDlist)) { + $CodecIDlist['A_AAC'] = 'aac'; + $CodecIDlist['A_AAC/MPEG2/LC'] = 'aac'; + $CodecIDlist['A_AC3'] = 'ac3'; + $CodecIDlist['A_DTS'] = 'dts'; + $CodecIDlist['A_FLAC'] = 'flac'; + $CodecIDlist['A_MPEG/L1'] = 'mp1'; + $CodecIDlist['A_MPEG/L2'] = 'mp2'; + $CodecIDlist['A_MPEG/L3'] = 'mp3'; + $CodecIDlist['A_PCM/INT/LIT'] = 'pcm'; // PCM Integer Little Endian + $CodecIDlist['A_PCM/INT/BIG'] = 'pcm'; // PCM Integer Big Endian + $CodecIDlist['A_QUICKTIME/QDMC'] = 'quicktime'; // Quicktime: QDesign Music + $CodecIDlist['A_QUICKTIME/QDM2'] = 'quicktime'; // Quicktime: QDesign Music v2 + $CodecIDlist['A_VORBIS'] = 'vorbis'; + $CodecIDlist['V_MPEG1'] = 'mpeg'; + $CodecIDlist['V_THEORA'] = 'theora'; + $CodecIDlist['V_REAL/RV40'] = 'real'; + $CodecIDlist['V_REAL/RV10'] = 'real'; + $CodecIDlist['V_REAL/RV20'] = 'real'; + $CodecIDlist['V_REAL/RV30'] = 'real'; + $CodecIDlist['V_QUICKTIME'] = 'quicktime'; // Quicktime + $CodecIDlist['V_MPEG4/ISO/AP'] = 'mpeg4'; + $CodecIDlist['V_MPEG4/ISO/ASP'] = 'mpeg4'; + $CodecIDlist['V_MPEG4/ISO/AVC'] = 'h264'; + $CodecIDlist['V_MPEG4/ISO/SP'] = 'mpeg4'; + $CodecIDlist['V_VP8'] = 'vp8'; + $CodecIDlist['V_MS/VFW/FOURCC'] = 'vcm'; // Microsoft (TM) Video Codec Manager (VCM) + $CodecIDlist['A_MS/ACM'] = 'acm'; // Microsoft (TM) Audio Codec Manager (ACM) + } + return (isset($CodecIDlist[$codecid]) ? $CodecIDlist[$codecid] : $codecid); + } + + private static function EBMLidName($value) { + static $EBMLidList = array(); + if (empty($EBMLidList)) { + $EBMLidList[EBML_ID_ASPECTRATIOTYPE] = 'AspectRatioType'; + $EBMLidList[EBML_ID_ATTACHEDFILE] = 'AttachedFile'; + $EBMLidList[EBML_ID_ATTACHMENTLINK] = 'AttachmentLink'; + $EBMLidList[EBML_ID_ATTACHMENTS] = 'Attachments'; + $EBMLidList[EBML_ID_AUDIO] = 'Audio'; + $EBMLidList[EBML_ID_BITDEPTH] = 'BitDepth'; + $EBMLidList[EBML_ID_CHANNELPOSITIONS] = 'ChannelPositions'; + $EBMLidList[EBML_ID_CHANNELS] = 'Channels'; + $EBMLidList[EBML_ID_CHAPCOUNTRY] = 'ChapCountry'; + $EBMLidList[EBML_ID_CHAPLANGUAGE] = 'ChapLanguage'; + $EBMLidList[EBML_ID_CHAPPROCESS] = 'ChapProcess'; + $EBMLidList[EBML_ID_CHAPPROCESSCODECID] = 'ChapProcessCodecID'; + $EBMLidList[EBML_ID_CHAPPROCESSCOMMAND] = 'ChapProcessCommand'; + $EBMLidList[EBML_ID_CHAPPROCESSDATA] = 'ChapProcessData'; + $EBMLidList[EBML_ID_CHAPPROCESSPRIVATE] = 'ChapProcessPrivate'; + $EBMLidList[EBML_ID_CHAPPROCESSTIME] = 'ChapProcessTime'; + $EBMLidList[EBML_ID_CHAPSTRING] = 'ChapString'; + $EBMLidList[EBML_ID_CHAPTERATOM] = 'ChapterAtom'; + $EBMLidList[EBML_ID_CHAPTERDISPLAY] = 'ChapterDisplay'; + $EBMLidList[EBML_ID_CHAPTERFLAGENABLED] = 'ChapterFlagEnabled'; + $EBMLidList[EBML_ID_CHAPTERFLAGHIDDEN] = 'ChapterFlagHidden'; + $EBMLidList[EBML_ID_CHAPTERPHYSICALEQUIV] = 'ChapterPhysicalEquiv'; + $EBMLidList[EBML_ID_CHAPTERS] = 'Chapters'; + $EBMLidList[EBML_ID_CHAPTERSEGMENTEDITIONUID] = 'ChapterSegmentEditionUID'; + $EBMLidList[EBML_ID_CHAPTERSEGMENTUID] = 'ChapterSegmentUID'; + $EBMLidList[EBML_ID_CHAPTERTIMEEND] = 'ChapterTimeEnd'; + $EBMLidList[EBML_ID_CHAPTERTIMESTART] = 'ChapterTimeStart'; + $EBMLidList[EBML_ID_CHAPTERTRACK] = 'ChapterTrack'; + $EBMLidList[EBML_ID_CHAPTERTRACKNUMBER] = 'ChapterTrackNumber'; + $EBMLidList[EBML_ID_CHAPTERTRANSLATE] = 'ChapterTranslate'; + $EBMLidList[EBML_ID_CHAPTERTRANSLATECODEC] = 'ChapterTranslateCodec'; + $EBMLidList[EBML_ID_CHAPTERTRANSLATEEDITIONUID] = 'ChapterTranslateEditionUID'; + $EBMLidList[EBML_ID_CHAPTERTRANSLATEID] = 'ChapterTranslateID'; + $EBMLidList[EBML_ID_CHAPTERUID] = 'ChapterUID'; + $EBMLidList[EBML_ID_CLUSTER] = 'Cluster'; + $EBMLidList[EBML_ID_CLUSTERBLOCK] = 'ClusterBlock'; + $EBMLidList[EBML_ID_CLUSTERBLOCKADDID] = 'ClusterBlockAddID'; + $EBMLidList[EBML_ID_CLUSTERBLOCKADDITIONAL] = 'ClusterBlockAdditional'; + $EBMLidList[EBML_ID_CLUSTERBLOCKADDITIONID] = 'ClusterBlockAdditionID'; + $EBMLidList[EBML_ID_CLUSTERBLOCKADDITIONS] = 'ClusterBlockAdditions'; + $EBMLidList[EBML_ID_CLUSTERBLOCKDURATION] = 'ClusterBlockDuration'; + $EBMLidList[EBML_ID_CLUSTERBLOCKGROUP] = 'ClusterBlockGroup'; + $EBMLidList[EBML_ID_CLUSTERBLOCKMORE] = 'ClusterBlockMore'; + $EBMLidList[EBML_ID_CLUSTERBLOCKVIRTUAL] = 'ClusterBlockVirtual'; + $EBMLidList[EBML_ID_CLUSTERCODECSTATE] = 'ClusterCodecState'; + $EBMLidList[EBML_ID_CLUSTERDELAY] = 'ClusterDelay'; + $EBMLidList[EBML_ID_CLUSTERDURATION] = 'ClusterDuration'; + $EBMLidList[EBML_ID_CLUSTERENCRYPTEDBLOCK] = 'ClusterEncryptedBlock'; + $EBMLidList[EBML_ID_CLUSTERFRAMENUMBER] = 'ClusterFrameNumber'; + $EBMLidList[EBML_ID_CLUSTERLACENUMBER] = 'ClusterLaceNumber'; + $EBMLidList[EBML_ID_CLUSTERPOSITION] = 'ClusterPosition'; + $EBMLidList[EBML_ID_CLUSTERPREVSIZE] = 'ClusterPrevSize'; + $EBMLidList[EBML_ID_CLUSTERREFERENCEBLOCK] = 'ClusterReferenceBlock'; + $EBMLidList[EBML_ID_CLUSTERREFERENCEPRIORITY] = 'ClusterReferencePriority'; + $EBMLidList[EBML_ID_CLUSTERREFERENCEVIRTUAL] = 'ClusterReferenceVirtual'; + $EBMLidList[EBML_ID_CLUSTERSILENTTRACKNUMBER] = 'ClusterSilentTrackNumber'; + $EBMLidList[EBML_ID_CLUSTERSILENTTRACKS] = 'ClusterSilentTracks'; + $EBMLidList[EBML_ID_CLUSTERSIMPLEBLOCK] = 'ClusterSimpleBlock'; + $EBMLidList[EBML_ID_CLUSTERTIMECODE] = 'ClusterTimecode'; + $EBMLidList[EBML_ID_CLUSTERTIMESLICE] = 'ClusterTimeSlice'; + $EBMLidList[EBML_ID_CODECDECODEALL] = 'CodecDecodeAll'; + $EBMLidList[EBML_ID_CODECDOWNLOADURL] = 'CodecDownloadURL'; + $EBMLidList[EBML_ID_CODECID] = 'CodecID'; + $EBMLidList[EBML_ID_CODECINFOURL] = 'CodecInfoURL'; + $EBMLidList[EBML_ID_CODECNAME] = 'CodecName'; + $EBMLidList[EBML_ID_CODECPRIVATE] = 'CodecPrivate'; + $EBMLidList[EBML_ID_CODECSETTINGS] = 'CodecSettings'; + $EBMLidList[EBML_ID_COLOURSPACE] = 'ColourSpace'; + $EBMLidList[EBML_ID_CONTENTCOMPALGO] = 'ContentCompAlgo'; + $EBMLidList[EBML_ID_CONTENTCOMPRESSION] = 'ContentCompression'; + $EBMLidList[EBML_ID_CONTENTCOMPSETTINGS] = 'ContentCompSettings'; + $EBMLidList[EBML_ID_CONTENTENCALGO] = 'ContentEncAlgo'; + $EBMLidList[EBML_ID_CONTENTENCKEYID] = 'ContentEncKeyID'; + $EBMLidList[EBML_ID_CONTENTENCODING] = 'ContentEncoding'; + $EBMLidList[EBML_ID_CONTENTENCODINGORDER] = 'ContentEncodingOrder'; + $EBMLidList[EBML_ID_CONTENTENCODINGS] = 'ContentEncodings'; + $EBMLidList[EBML_ID_CONTENTENCODINGSCOPE] = 'ContentEncodingScope'; + $EBMLidList[EBML_ID_CONTENTENCODINGTYPE] = 'ContentEncodingType'; + $EBMLidList[EBML_ID_CONTENTENCRYPTION] = 'ContentEncryption'; + $EBMLidList[EBML_ID_CONTENTSIGALGO] = 'ContentSigAlgo'; + $EBMLidList[EBML_ID_CONTENTSIGHASHALGO] = 'ContentSigHashAlgo'; + $EBMLidList[EBML_ID_CONTENTSIGKEYID] = 'ContentSigKeyID'; + $EBMLidList[EBML_ID_CONTENTSIGNATURE] = 'ContentSignature'; + $EBMLidList[EBML_ID_CRC32] = 'CRC32'; + $EBMLidList[EBML_ID_CUEBLOCKNUMBER] = 'CueBlockNumber'; + $EBMLidList[EBML_ID_CUECLUSTERPOSITION] = 'CueClusterPosition'; + $EBMLidList[EBML_ID_CUECODECSTATE] = 'CueCodecState'; + $EBMLidList[EBML_ID_CUEPOINT] = 'CuePoint'; + $EBMLidList[EBML_ID_CUEREFCLUSTER] = 'CueRefCluster'; + $EBMLidList[EBML_ID_CUEREFCODECSTATE] = 'CueRefCodecState'; + $EBMLidList[EBML_ID_CUEREFERENCE] = 'CueReference'; + $EBMLidList[EBML_ID_CUEREFNUMBER] = 'CueRefNumber'; + $EBMLidList[EBML_ID_CUEREFTIME] = 'CueRefTime'; + $EBMLidList[EBML_ID_CUES] = 'Cues'; + $EBMLidList[EBML_ID_CUETIME] = 'CueTime'; + $EBMLidList[EBML_ID_CUETRACK] = 'CueTrack'; + $EBMLidList[EBML_ID_CUETRACKPOSITIONS] = 'CueTrackPositions'; + $EBMLidList[EBML_ID_DATEUTC] = 'DateUTC'; + $EBMLidList[EBML_ID_DEFAULTDURATION] = 'DefaultDuration'; + $EBMLidList[EBML_ID_DISPLAYHEIGHT] = 'DisplayHeight'; + $EBMLidList[EBML_ID_DISPLAYUNIT] = 'DisplayUnit'; + $EBMLidList[EBML_ID_DISPLAYWIDTH] = 'DisplayWidth'; + $EBMLidList[EBML_ID_DOCTYPE] = 'DocType'; + $EBMLidList[EBML_ID_DOCTYPEREADVERSION] = 'DocTypeReadVersion'; + $EBMLidList[EBML_ID_DOCTYPEVERSION] = 'DocTypeVersion'; + $EBMLidList[EBML_ID_DURATION] = 'Duration'; + $EBMLidList[EBML_ID_EBML] = 'EBML'; + $EBMLidList[EBML_ID_EBMLMAXIDLENGTH] = 'EBMLMaxIDLength'; + $EBMLidList[EBML_ID_EBMLMAXSIZELENGTH] = 'EBMLMaxSizeLength'; + $EBMLidList[EBML_ID_EBMLREADVERSION] = 'EBMLReadVersion'; + $EBMLidList[EBML_ID_EBMLVERSION] = 'EBMLVersion'; + $EBMLidList[EBML_ID_EDITIONENTRY] = 'EditionEntry'; + $EBMLidList[EBML_ID_EDITIONFLAGDEFAULT] = 'EditionFlagDefault'; + $EBMLidList[EBML_ID_EDITIONFLAGHIDDEN] = 'EditionFlagHidden'; + $EBMLidList[EBML_ID_EDITIONFLAGORDERED] = 'EditionFlagOrdered'; + $EBMLidList[EBML_ID_EDITIONUID] = 'EditionUID'; + $EBMLidList[EBML_ID_FILEDATA] = 'FileData'; + $EBMLidList[EBML_ID_FILEDESCRIPTION] = 'FileDescription'; + $EBMLidList[EBML_ID_FILEMIMETYPE] = 'FileMimeType'; + $EBMLidList[EBML_ID_FILENAME] = 'FileName'; + $EBMLidList[EBML_ID_FILEREFERRAL] = 'FileReferral'; + $EBMLidList[EBML_ID_FILEUID] = 'FileUID'; + $EBMLidList[EBML_ID_FLAGDEFAULT] = 'FlagDefault'; + $EBMLidList[EBML_ID_FLAGENABLED] = 'FlagEnabled'; + $EBMLidList[EBML_ID_FLAGFORCED] = 'FlagForced'; + $EBMLidList[EBML_ID_FLAGINTERLACED] = 'FlagInterlaced'; + $EBMLidList[EBML_ID_FLAGLACING] = 'FlagLacing'; + $EBMLidList[EBML_ID_GAMMAVALUE] = 'GammaValue'; + $EBMLidList[EBML_ID_INFO] = 'Info'; + $EBMLidList[EBML_ID_LANGUAGE] = 'Language'; + $EBMLidList[EBML_ID_MAXBLOCKADDITIONID] = 'MaxBlockAdditionID'; + $EBMLidList[EBML_ID_MAXCACHE] = 'MaxCache'; + $EBMLidList[EBML_ID_MINCACHE] = 'MinCache'; + $EBMLidList[EBML_ID_MUXINGAPP] = 'MuxingApp'; + $EBMLidList[EBML_ID_NAME] = 'Name'; + $EBMLidList[EBML_ID_NEXTFILENAME] = 'NextFilename'; + $EBMLidList[EBML_ID_NEXTUID] = 'NextUID'; + $EBMLidList[EBML_ID_OUTPUTSAMPLINGFREQUENCY] = 'OutputSamplingFrequency'; + $EBMLidList[EBML_ID_PIXELCROPBOTTOM] = 'PixelCropBottom'; + $EBMLidList[EBML_ID_PIXELCROPLEFT] = 'PixelCropLeft'; + $EBMLidList[EBML_ID_PIXELCROPRIGHT] = 'PixelCropRight'; + $EBMLidList[EBML_ID_PIXELCROPTOP] = 'PixelCropTop'; + $EBMLidList[EBML_ID_PIXELHEIGHT] = 'PixelHeight'; + $EBMLidList[EBML_ID_PIXELWIDTH] = 'PixelWidth'; + $EBMLidList[EBML_ID_PREVFILENAME] = 'PrevFilename'; + $EBMLidList[EBML_ID_PREVUID] = 'PrevUID'; + $EBMLidList[EBML_ID_SAMPLINGFREQUENCY] = 'SamplingFrequency'; + $EBMLidList[EBML_ID_SEEK] = 'Seek'; + $EBMLidList[EBML_ID_SEEKHEAD] = 'SeekHead'; + $EBMLidList[EBML_ID_SEEKID] = 'SeekID'; + $EBMLidList[EBML_ID_SEEKPOSITION] = 'SeekPosition'; + $EBMLidList[EBML_ID_SEGMENT] = 'Segment'; + $EBMLidList[EBML_ID_SEGMENTFAMILY] = 'SegmentFamily'; + $EBMLidList[EBML_ID_SEGMENTFILENAME] = 'SegmentFilename'; + $EBMLidList[EBML_ID_SEGMENTUID] = 'SegmentUID'; + $EBMLidList[EBML_ID_SIMPLETAG] = 'SimpleTag'; + $EBMLidList[EBML_ID_CLUSTERSLICES] = 'ClusterSlices'; + $EBMLidList[EBML_ID_STEREOMODE] = 'StereoMode'; + $EBMLidList[EBML_ID_OLDSTEREOMODE] = 'OldStereoMode'; + $EBMLidList[EBML_ID_TAG] = 'Tag'; + $EBMLidList[EBML_ID_TAGATTACHMENTUID] = 'TagAttachmentUID'; + $EBMLidList[EBML_ID_TAGBINARY] = 'TagBinary'; + $EBMLidList[EBML_ID_TAGCHAPTERUID] = 'TagChapterUID'; + $EBMLidList[EBML_ID_TAGDEFAULT] = 'TagDefault'; + $EBMLidList[EBML_ID_TAGEDITIONUID] = 'TagEditionUID'; + $EBMLidList[EBML_ID_TAGLANGUAGE] = 'TagLanguage'; + $EBMLidList[EBML_ID_TAGNAME] = 'TagName'; + $EBMLidList[EBML_ID_TAGTRACKUID] = 'TagTrackUID'; + $EBMLidList[EBML_ID_TAGS] = 'Tags'; + $EBMLidList[EBML_ID_TAGSTRING] = 'TagString'; + $EBMLidList[EBML_ID_TARGETS] = 'Targets'; + $EBMLidList[EBML_ID_TARGETTYPE] = 'TargetType'; + $EBMLidList[EBML_ID_TARGETTYPEVALUE] = 'TargetTypeValue'; + $EBMLidList[EBML_ID_TIMECODESCALE] = 'TimecodeScale'; + $EBMLidList[EBML_ID_TITLE] = 'Title'; + $EBMLidList[EBML_ID_TRACKENTRY] = 'TrackEntry'; + $EBMLidList[EBML_ID_TRACKNUMBER] = 'TrackNumber'; + $EBMLidList[EBML_ID_TRACKOFFSET] = 'TrackOffset'; + $EBMLidList[EBML_ID_TRACKOVERLAY] = 'TrackOverlay'; + $EBMLidList[EBML_ID_TRACKS] = 'Tracks'; + $EBMLidList[EBML_ID_TRACKTIMECODESCALE] = 'TrackTimecodeScale'; + $EBMLidList[EBML_ID_TRACKTRANSLATE] = 'TrackTranslate'; + $EBMLidList[EBML_ID_TRACKTRANSLATECODEC] = 'TrackTranslateCodec'; + $EBMLidList[EBML_ID_TRACKTRANSLATEEDITIONUID] = 'TrackTranslateEditionUID'; + $EBMLidList[EBML_ID_TRACKTRANSLATETRACKID] = 'TrackTranslateTrackID'; + $EBMLidList[EBML_ID_TRACKTYPE] = 'TrackType'; + $EBMLidList[EBML_ID_TRACKUID] = 'TrackUID'; + $EBMLidList[EBML_ID_VIDEO] = 'Video'; + $EBMLidList[EBML_ID_VOID] = 'Void'; + $EBMLidList[EBML_ID_WRITINGAPP] = 'WritingApp'; + } + + return (isset($EBMLidList[$value]) ? $EBMLidList[$value] : dechex($value)); + } + + public static function displayUnit($value) { + // http://www.matroska.org/technical/specs/index.html#DisplayUnit + static $units = array( + 0 => 'pixels', + 1 => 'centimeters', + 2 => 'inches', + 3 => 'Display Aspect Ratio'); + + return (isset($units[$value]) ? $units[$value] : 'unknown'); + } + + private static function getDefaultStreamInfo($streams) + { + foreach (array_reverse($streams) as $stream) { + if ($stream['default']) { + break; + } + } + + $unset = array('default', 'name'); + foreach ($unset as $u) { + if (isset($stream[$u])) { + unset($stream[$u]); + } + } + + $info = $stream; + $info['streams'] = $streams; + + return $info; + } + +} \ No newline at end of file diff --git a/wp-includes/ID3/module.audio-video.quicktime.php b/wp-includes/ID3/module.audio-video.quicktime.php new file mode 100644 index 0000000..03f7a92 --- /dev/null +++ b/wp-includes/ID3/module.audio-video.quicktime.php @@ -0,0 +1,2246 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +// also https://github.com/JamesHeinrich/getID3 // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio-video.quicktime.php // +// module for analyzing Quicktime and MP3-in-MP4 files // +// dependencies: module.audio.mp3.php // +// dependencies: module.tag.id3v2.php // +// /// +///////////////////////////////////////////////////////////////// + +getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true); +getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true); // needed for ISO 639-2 language code lookup + +class getid3_quicktime extends getid3_handler +{ + + public $ReturnAtomData = true; + public $ParseAllPossibleAtoms = false; + + public function Analyze() { + $info = &$this->getid3->info; + + $info['fileformat'] = 'quicktime'; + $info['quicktime']['hinting'] = false; + $info['quicktime']['controller'] = 'standard'; // may be overridden if 'ctyp' atom is present + + $this->fseek($info['avdataoffset']); + + $offset = 0; + $atomcounter = 0; + + while ($offset < $info['avdataend']) { + if (!getid3_lib::intValueSupported($offset)) { + $info['error'][] = 'Unable to parse atom at offset '.$offset.' because beyond '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions'; + break; + } + $this->fseek($offset); + $AtomHeader = $this->fread(8); + + $atomsize = getid3_lib::BigEndian2Int(substr($AtomHeader, 0, 4)); + $atomname = substr($AtomHeader, 4, 4); + + // 64-bit MOV patch by jlegateØktnc*com + if ($atomsize == 1) { + $atomsize = getid3_lib::BigEndian2Int($this->fread(8)); + } + + $info['quicktime'][$atomname]['name'] = $atomname; + $info['quicktime'][$atomname]['size'] = $atomsize; + $info['quicktime'][$atomname]['offset'] = $offset; + + if (($offset + $atomsize) > $info['avdataend']) { + $info['error'][] = 'Atom at offset '.$offset.' claims to go beyond end-of-file (length: '.$atomsize.' bytes)'; + return false; + } + + if ($atomsize == 0) { + // Furthermore, for historical reasons the list of atoms is optionally + // terminated by a 32-bit integer set to 0. If you are writing a program + // to read user data atoms, you should allow for the terminating 0. + break; + } + $atomHierarchy = array(); + $info['quicktime'][$atomname] = $this->QuicktimeParseAtom($atomname, $atomsize, $this->fread(min($atomsize, round($this->getid3->memory_limit / 2))), $offset, $atomHierarchy, $this->ParseAllPossibleAtoms); + + $offset += $atomsize; + $atomcounter++; + } + + if (!empty($info['avdataend_tmp'])) { + // this value is assigned to a temp value and then erased because + // otherwise any atoms beyond the 'mdat' atom would not get parsed + $info['avdataend'] = $info['avdataend_tmp']; + unset($info['avdataend_tmp']); + } + + if (!isset($info['bitrate']) && isset($info['playtime_seconds'])) { + $info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; + } + if (isset($info['bitrate']) && !isset($info['audio']['bitrate']) && !isset($info['quicktime']['video'])) { + $info['audio']['bitrate'] = $info['bitrate']; + } + if (!empty($info['playtime_seconds']) && !isset($info['video']['frame_rate']) && !empty($info['quicktime']['stts_framecount'])) { + foreach ($info['quicktime']['stts_framecount'] as $key => $samples_count) { + $samples_per_second = $samples_count / $info['playtime_seconds']; + if ($samples_per_second > 240) { + // has to be audio samples + } else { + $info['video']['frame_rate'] = $samples_per_second; + break; + } + } + } + if (($info['audio']['dataformat'] == 'mp4') && empty($info['video']['resolution_x'])) { + $info['fileformat'] = 'mp4'; + $info['mime_type'] = 'audio/mp4'; + unset($info['video']['dataformat']); + } + + if (!$this->ReturnAtomData) { + unset($info['quicktime']['moov']); + } + + if (empty($info['audio']['dataformat']) && !empty($info['quicktime']['audio'])) { + $info['audio']['dataformat'] = 'quicktime'; + } + if (empty($info['video']['dataformat']) && !empty($info['quicktime']['video'])) { + $info['video']['dataformat'] = 'quicktime'; + } + + return true; + } + + public function QuicktimeParseAtom($atomname, $atomsize, $atom_data, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) { + // http://developer.apple.com/techpubs/quicktime/qtdevdocs/APIREF/INDEX/atomalphaindex.htm + + $info = &$this->getid3->info; + + $atom_parent = end($atomHierarchy); // not array_pop($atomHierarchy); see http://www.getid3.org/phpBB3/viewtopic.php?t=1717 + array_push($atomHierarchy, $atomname); + $atom_structure['hierarchy'] = implode(' ', $atomHierarchy); + $atom_structure['name'] = $atomname; + $atom_structure['size'] = $atomsize; + $atom_structure['offset'] = $baseoffset; + switch ($atomname) { + case 'moov': // MOVie container atom + case 'trak': // TRAcK container atom + case 'clip': // CLIPping container atom + case 'matt': // track MATTe container atom + case 'edts': // EDiTS container atom + case 'tref': // Track REFerence container atom + case 'mdia': // MeDIA container atom + case 'minf': // Media INFormation container atom + case 'dinf': // Data INFormation container atom + case 'udta': // User DaTA container atom + case 'cmov': // Compressed MOVie container atom + case 'rmra': // Reference Movie Record Atom + case 'rmda': // Reference Movie Descriptor Atom + case 'gmhd': // Generic Media info HeaDer atom (seen on QTVR) + $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); + break; + + case 'ilst': // Item LiST container atom + if ($atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms)) { + // some "ilst" atoms contain data atoms that have a numeric name, and the data is far more accessible if the returned array is compacted + $allnumericnames = true; + foreach ($atom_structure['subatoms'] as $subatomarray) { + if (!is_integer($subatomarray['name']) || (count($subatomarray['subatoms']) != 1)) { + $allnumericnames = false; + break; + } + } + if ($allnumericnames) { + $newData = array(); + foreach ($atom_structure['subatoms'] as $subatomarray) { + foreach ($subatomarray['subatoms'] as $newData_subatomarray) { + unset($newData_subatomarray['hierarchy'], $newData_subatomarray['name']); + $newData[$subatomarray['name']] = $newData_subatomarray; + break; + } + } + $atom_structure['data'] = $newData; + unset($atom_structure['subatoms']); + } + } + break; + + case "\x00\x00\x00\x01": + case "\x00\x00\x00\x02": + case "\x00\x00\x00\x03": + case "\x00\x00\x00\x04": + case "\x00\x00\x00\x05": + $atomname = getid3_lib::BigEndian2Int($atomname); + $atom_structure['name'] = $atomname; + $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); + break; + + case 'stbl': // Sample TaBLe container atom + $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); + $isVideo = false; + $framerate = 0; + $framecount = 0; + foreach ($atom_structure['subatoms'] as $key => $value_array) { + if (isset($value_array['sample_description_table'])) { + foreach ($value_array['sample_description_table'] as $key2 => $value_array2) { + if (isset($value_array2['data_format'])) { + switch ($value_array2['data_format']) { + case 'avc1': + case 'mp4v': + // video data + $isVideo = true; + break; + case 'mp4a': + // audio data + break; + } + } + } + } elseif (isset($value_array['time_to_sample_table'])) { + foreach ($value_array['time_to_sample_table'] as $key2 => $value_array2) { + if (isset($value_array2['sample_count']) && isset($value_array2['sample_duration']) && ($value_array2['sample_duration'] > 0)) { + $framerate = round($info['quicktime']['time_scale'] / $value_array2['sample_duration'], 3); + $framecount = $value_array2['sample_count']; + } + } + } + } + if ($isVideo && $framerate) { + $info['quicktime']['video']['frame_rate'] = $framerate; + $info['video']['frame_rate'] = $info['quicktime']['video']['frame_rate']; + } + if ($isVideo && $framecount) { + $info['quicktime']['video']['frame_count'] = $framecount; + } + break; + + + case 'aART': // Album ARTist + case 'catg': // CaTeGory + case 'covr': // COVeR artwork + case 'cpil': // ComPILation + case 'cprt': // CoPyRighT + case 'desc': // DESCription + case 'disk': // DISK number + case 'egid': // Episode Global ID + case 'gnre': // GeNRE + case 'keyw': // KEYWord + case 'ldes': + case 'pcst': // PodCaST + case 'pgap': // GAPless Playback + case 'purd': // PURchase Date + case 'purl': // Podcast URL + case 'rati': + case 'rndu': + case 'rpdu': + case 'rtng': // RaTiNG + case 'stik': + case 'tmpo': // TeMPO (BPM) + case 'trkn': // TRacK Number + case 'tves': // TV EpiSode + case 'tvnn': // TV Network Name + case 'tvsh': // TV SHow Name + case 'tvsn': // TV SeasoN + case 'akID': // iTunes store account type + case 'apID': + case 'atID': + case 'cmID': + case 'cnID': + case 'geID': + case 'plID': + case 'sfID': // iTunes store country + case "\xA9".'alb': // ALBum + case "\xA9".'art': // ARTist + case "\xA9".'ART': + case "\xA9".'aut': + case "\xA9".'cmt': // CoMmenT + case "\xA9".'com': // COMposer + case "\xA9".'cpy': + case "\xA9".'day': // content created year + case "\xA9".'dir': + case "\xA9".'ed1': + case "\xA9".'ed2': + case "\xA9".'ed3': + case "\xA9".'ed4': + case "\xA9".'ed5': + case "\xA9".'ed6': + case "\xA9".'ed7': + case "\xA9".'ed8': + case "\xA9".'ed9': + case "\xA9".'enc': + case "\xA9".'fmt': + case "\xA9".'gen': // GENre + case "\xA9".'grp': // GRouPing + case "\xA9".'hst': + case "\xA9".'inf': + case "\xA9".'lyr': // LYRics + case "\xA9".'mak': + case "\xA9".'mod': + case "\xA9".'nam': // full NAMe + case "\xA9".'ope': + case "\xA9".'PRD': + case "\xA9".'prd': + case "\xA9".'prf': + case "\xA9".'req': + case "\xA9".'src': + case "\xA9".'swr': + case "\xA9".'too': // encoder + case "\xA9".'trk': // TRacK + case "\xA9".'url': + case "\xA9".'wrn': + case "\xA9".'wrt': // WRiTer + case '----': // itunes specific + if ($atom_parent == 'udta') { + // User data atom handler + $atom_structure['data_length'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2)); + $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2)); + $atom_structure['data'] = substr($atom_data, 4); + + $atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']); + if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) { + $info['comments']['language'][] = $atom_structure['language']; + } + } else { + // Apple item list box atom handler + $atomoffset = 0; + if (substr($atom_data, 2, 2) == "\x10\xB5") { + // not sure what it means, but observed on iPhone4 data. + // Each $atom_data has 2 bytes of datasize, plus 0x10B5, then data + while ($atomoffset < strlen($atom_data)) { + $boxsmallsize = getid3_lib::BigEndian2Int(substr($atom_data, $atomoffset, 2)); + $boxsmalltype = substr($atom_data, $atomoffset + 2, 2); + $boxsmalldata = substr($atom_data, $atomoffset + 4, $boxsmallsize); + if ($boxsmallsize <= 1) { + $info['warning'][] = 'Invalid QuickTime atom smallbox size "'.$boxsmallsize.'" in atom "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" at offset: '.($atom_structure['offset'] + $atomoffset); + $atom_structure['data'] = null; + $atomoffset = strlen($atom_data); + break; + } + switch ($boxsmalltype) { + case "\x10\xB5": + $atom_structure['data'] = $boxsmalldata; + break; + default: + $info['warning'][] = 'Unknown QuickTime smallbox type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $boxsmalltype).'" ('.trim(getid3_lib::PrintHexBytes($boxsmalltype)).') at offset '.$baseoffset; + $atom_structure['data'] = $atom_data; + break; + } + $atomoffset += (4 + $boxsmallsize); + } + } else { + while ($atomoffset < strlen($atom_data)) { + $boxsize = getid3_lib::BigEndian2Int(substr($atom_data, $atomoffset, 4)); + $boxtype = substr($atom_data, $atomoffset + 4, 4); + $boxdata = substr($atom_data, $atomoffset + 8, $boxsize - 8); + if ($boxsize <= 1) { + $info['warning'][] = 'Invalid QuickTime atom box size "'.$boxsize.'" in atom "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" at offset: '.($atom_structure['offset'] + $atomoffset); + $atom_structure['data'] = null; + $atomoffset = strlen($atom_data); + break; + } + $atomoffset += $boxsize; + + switch ($boxtype) { + case 'mean': + case 'name': + $atom_structure[$boxtype] = substr($boxdata, 4); + break; + + case 'data': + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($boxdata, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($boxdata, 1, 3)); + switch ($atom_structure['flags_raw']) { + case 0: // data flag + case 21: // tmpo/cpil flag + switch ($atomname) { + case 'cpil': + case 'pcst': + case 'pgap': + $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1)); + break; + + case 'tmpo': + $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 2)); + break; + + case 'disk': + case 'trkn': + $num = getid3_lib::BigEndian2Int(substr($boxdata, 10, 2)); + $num_total = getid3_lib::BigEndian2Int(substr($boxdata, 12, 2)); + $atom_structure['data'] = empty($num) ? '' : $num; + $atom_structure['data'] .= empty($num_total) ? '' : '/'.$num_total; + break; + + case 'gnre': + $GenreID = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4)); + $atom_structure['data'] = getid3_id3v1::LookupGenreName($GenreID - 1); + break; + + case 'rtng': + $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1)); + $atom_structure['data'] = $this->QuicktimeContentRatingLookup($atom_structure[$atomname]); + break; + + case 'stik': + $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1)); + $atom_structure['data'] = $this->QuicktimeSTIKLookup($atom_structure[$atomname]); + break; + + case 'sfID': + $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4)); + $atom_structure['data'] = $this->QuicktimeStoreFrontCodeLookup($atom_structure[$atomname]); + break; + + case 'egid': + case 'purl': + $atom_structure['data'] = substr($boxdata, 8); + break; + + default: + $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4)); + } + break; + + case 1: // text flag + case 13: // image flag + default: + $atom_structure['data'] = substr($boxdata, 8); + if ($atomname == 'covr') { + // not a foolproof check, but better than nothing + if (preg_match('#^\xFF\xD8\xFF#', $atom_structure['data'])) { + $atom_structure['image_mime'] = 'image/jpeg'; + } elseif (preg_match('#^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A#', $atom_structure['data'])) { + $atom_structure['image_mime'] = 'image/png'; + } elseif (preg_match('#^GIF#', $atom_structure['data'])) { + $atom_structure['image_mime'] = 'image/gif'; + } + } + break; + + } + break; + + default: + $info['warning'][] = 'Unknown QuickTime box type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $boxtype).'" ('.trim(getid3_lib::PrintHexBytes($boxtype)).') at offset '.$baseoffset; + $atom_structure['data'] = $atom_data; + + } + } + } + } + $this->CopyToAppropriateCommentsSection($atomname, $atom_structure['data'], $atom_structure['name']); + break; + + + case 'play': // auto-PLAY atom + $atom_structure['autoplay'] = (bool) getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + + $info['quicktime']['autoplay'] = $atom_structure['autoplay']; + break; + + + case 'WLOC': // Window LOCation atom + $atom_structure['location_x'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2)); + $atom_structure['location_y'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2)); + break; + + + case 'LOOP': // LOOPing atom + case 'SelO': // play SELection Only atom + case 'AllF': // play ALL Frames atom + $atom_structure['data'] = getid3_lib::BigEndian2Int($atom_data); + break; + + + case 'name': // + case 'MCPS': // Media Cleaner PRo + case '@PRM': // adobe PReMiere version + case '@PRQ': // adobe PRemiere Quicktime version + $atom_structure['data'] = $atom_data; + break; + + + case 'cmvd': // Compressed MooV Data atom + // Code by ubergeekØubergeek*tv based on information from + // http://developer.apple.com/quicktime/icefloe/dispatch012.html + $atom_structure['unCompressedSize'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); + + $CompressedFileData = substr($atom_data, 4); + if ($UncompressedHeader = @gzuncompress($CompressedFileData)) { + $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($UncompressedHeader, 0, $atomHierarchy, $ParseAllPossibleAtoms); + } else { + $info['warning'][] = 'Error decompressing compressed MOV atom at offset '.$atom_structure['offset']; + } + break; + + + case 'dcom': // Data COMpression atom + $atom_structure['compression_id'] = $atom_data; + $atom_structure['compression_text'] = $this->QuicktimeDCOMLookup($atom_data); + break; + + + case 'rdrf': // Reference movie Data ReFerence atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); + $atom_structure['flags']['internal_data'] = (bool) ($atom_structure['flags_raw'] & 0x000001); + + $atom_structure['reference_type_name'] = substr($atom_data, 4, 4); + $atom_structure['reference_length'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4)); + switch ($atom_structure['reference_type_name']) { + case 'url ': + $atom_structure['url'] = $this->NoNullString(substr($atom_data, 12)); + break; + + case 'alis': + $atom_structure['file_alias'] = substr($atom_data, 12); + break; + + case 'rsrc': + $atom_structure['resource_alias'] = substr($atom_data, 12); + break; + + default: + $atom_structure['data'] = substr($atom_data, 12); + break; + } + break; + + + case 'rmqu': // Reference Movie QUality atom + $atom_structure['movie_quality'] = getid3_lib::BigEndian2Int($atom_data); + break; + + + case 'rmcs': // Reference Movie Cpu Speed atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['cpu_speed_rating'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); + break; + + + case 'rmvc': // Reference Movie Version Check atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['gestalt_selector'] = substr($atom_data, 4, 4); + $atom_structure['gestalt_value_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4)); + $atom_structure['gestalt_value'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4)); + $atom_structure['gestalt_check_type'] = getid3_lib::BigEndian2Int(substr($atom_data, 14, 2)); + break; + + + case 'rmcd': // Reference Movie Component check atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['component_type'] = substr($atom_data, 4, 4); + $atom_structure['component_subtype'] = substr($atom_data, 8, 4); + $atom_structure['component_manufacturer'] = substr($atom_data, 12, 4); + $atom_structure['component_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4)); + $atom_structure['component_flags_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4)); + $atom_structure['component_min_version'] = getid3_lib::BigEndian2Int(substr($atom_data, 24, 4)); + break; + + + case 'rmdr': // Reference Movie Data Rate atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['data_rate'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); + + $atom_structure['data_rate_bps'] = $atom_structure['data_rate'] * 10; + break; + + + case 'rmla': // Reference Movie Language Atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); + + $atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']); + if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) { + $info['comments']['language'][] = $atom_structure['language']; + } + break; + + + case 'rmla': // Reference Movie Language Atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['track_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); + break; + + + case 'ptv ': // Print To Video - defines a movie's full screen mode + // http://developer.apple.com/documentation/QuickTime/APIREF/SOURCESIV/at_ptv-_pg.htm + $atom_structure['display_size_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2)); + $atom_structure['reserved_1'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2)); // hardcoded: 0x0000 + $atom_structure['reserved_2'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x0000 + $atom_structure['slide_show_flag'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 1)); + $atom_structure['play_on_open_flag'] = getid3_lib::BigEndian2Int(substr($atom_data, 7, 1)); + + $atom_structure['flags']['play_on_open'] = (bool) $atom_structure['play_on_open_flag']; + $atom_structure['flags']['slide_show'] = (bool) $atom_structure['slide_show_flag']; + + $ptv_lookup[0] = 'normal'; + $ptv_lookup[1] = 'double'; + $ptv_lookup[2] = 'half'; + $ptv_lookup[3] = 'full'; + $ptv_lookup[4] = 'current'; + if (isset($ptv_lookup[$atom_structure['display_size_raw']])) { + $atom_structure['display_size'] = $ptv_lookup[$atom_structure['display_size_raw']]; + } else { + $info['warning'][] = 'unknown "ptv " display constant ('.$atom_structure['display_size_raw'].')'; + } + break; + + + case 'stsd': // Sample Table Sample Description atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); + $stsdEntriesDataOffset = 8; + for ($i = 0; $i < $atom_structure['number_entries']; $i++) { + $atom_structure['sample_description_table'][$i]['size'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 4)); + $stsdEntriesDataOffset += 4; + $atom_structure['sample_description_table'][$i]['data_format'] = substr($atom_data, $stsdEntriesDataOffset, 4); + $stsdEntriesDataOffset += 4; + $atom_structure['sample_description_table'][$i]['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 6)); + $stsdEntriesDataOffset += 6; + $atom_structure['sample_description_table'][$i]['reference_index'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 2)); + $stsdEntriesDataOffset += 2; + $atom_structure['sample_description_table'][$i]['data'] = substr($atom_data, $stsdEntriesDataOffset, ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2)); + $stsdEntriesDataOffset += ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2); + + $atom_structure['sample_description_table'][$i]['encoder_version'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 0, 2)); + $atom_structure['sample_description_table'][$i]['encoder_revision'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 2, 2)); + $atom_structure['sample_description_table'][$i]['encoder_vendor'] = substr($atom_structure['sample_description_table'][$i]['data'], 4, 4); + + switch ($atom_structure['sample_description_table'][$i]['encoder_vendor']) { + + case "\x00\x00\x00\x00": + // audio tracks + $atom_structure['sample_description_table'][$i]['audio_channels'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 2)); + $atom_structure['sample_description_table'][$i]['audio_bit_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 10, 2)); + $atom_structure['sample_description_table'][$i]['audio_compression_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 2)); + $atom_structure['sample_description_table'][$i]['audio_packet_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 14, 2)); + $atom_structure['sample_description_table'][$i]['audio_sample_rate'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 16, 4)); + + // video tracks + // http://developer.apple.com/library/mac/#documentation/QuickTime/QTFF/QTFFChap3/qtff3.html + $atom_structure['sample_description_table'][$i]['temporal_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 4)); + $atom_structure['sample_description_table'][$i]['spatial_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 4)); + $atom_structure['sample_description_table'][$i]['width'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 16, 2)); + $atom_structure['sample_description_table'][$i]['height'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 18, 2)); + $atom_structure['sample_description_table'][$i]['resolution_x'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 24, 4)); + $atom_structure['sample_description_table'][$i]['resolution_y'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 28, 4)); + $atom_structure['sample_description_table'][$i]['data_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 32, 4)); + $atom_structure['sample_description_table'][$i]['frame_count'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 36, 2)); + $atom_structure['sample_description_table'][$i]['compressor_name'] = substr($atom_structure['sample_description_table'][$i]['data'], 38, 4); + $atom_structure['sample_description_table'][$i]['pixel_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 42, 2)); + $atom_structure['sample_description_table'][$i]['color_table_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 44, 2)); + + switch ($atom_structure['sample_description_table'][$i]['data_format']) { + case '2vuY': + case 'avc1': + case 'cvid': + case 'dvc ': + case 'dvcp': + case 'gif ': + case 'h263': + case 'jpeg': + case 'kpcd': + case 'mjpa': + case 'mjpb': + case 'mp4v': + case 'png ': + case 'raw ': + case 'rle ': + case 'rpza': + case 'smc ': + case 'SVQ1': + case 'SVQ3': + case 'tiff': + case 'v210': + case 'v216': + case 'v308': + case 'v408': + case 'v410': + case 'yuv2': + $info['fileformat'] = 'mp4'; + $info['video']['fourcc'] = $atom_structure['sample_description_table'][$i]['data_format']; +// http://www.getid3.org/phpBB3/viewtopic.php?t=1550 +//if ((!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($atom_structure['sample_description_table'][$i]['width'])) && (empty($info['video']['resolution_x']) || empty($info['video']['resolution_y']) || (number_format($info['video']['resolution_x'], 6) != number_format(round($info['video']['resolution_x']), 6)) || (number_format($info['video']['resolution_y'], 6) != number_format(round($info['video']['resolution_y']), 6)))) { // ugly check for floating point numbers +if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($atom_structure['sample_description_table'][$i]['height'])) { + // assume that values stored here are more important than values stored in [tkhd] atom + $info['video']['resolution_x'] = $atom_structure['sample_description_table'][$i]['width']; + $info['video']['resolution_y'] = $atom_structure['sample_description_table'][$i]['height']; + $info['quicktime']['video']['resolution_x'] = $info['video']['resolution_x']; + $info['quicktime']['video']['resolution_y'] = $info['video']['resolution_y']; +} + break; + + case 'qtvr': + $info['video']['dataformat'] = 'quicktimevr'; + break; + + case 'mp4a': + default: + $info['quicktime']['audio']['codec'] = $this->QuicktimeAudioCodecLookup($atom_structure['sample_description_table'][$i]['data_format']); + $info['quicktime']['audio']['sample_rate'] = $atom_structure['sample_description_table'][$i]['audio_sample_rate']; + $info['quicktime']['audio']['channels'] = $atom_structure['sample_description_table'][$i]['audio_channels']; + $info['quicktime']['audio']['bit_depth'] = $atom_structure['sample_description_table'][$i]['audio_bit_depth']; + $info['audio']['codec'] = $info['quicktime']['audio']['codec']; + $info['audio']['sample_rate'] = $info['quicktime']['audio']['sample_rate']; + $info['audio']['channels'] = $info['quicktime']['audio']['channels']; + $info['audio']['bits_per_sample'] = $info['quicktime']['audio']['bit_depth']; + switch ($atom_structure['sample_description_table'][$i]['data_format']) { + case 'raw ': // PCM + case 'alac': // Apple Lossless Audio Codec + $info['audio']['lossless'] = true; + break; + default: + $info['audio']['lossless'] = false; + break; + } + break; + } + break; + + default: + switch ($atom_structure['sample_description_table'][$i]['data_format']) { + case 'mp4s': + $info['fileformat'] = 'mp4'; + break; + + default: + // video atom + $atom_structure['sample_description_table'][$i]['video_temporal_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 4)); + $atom_structure['sample_description_table'][$i]['video_spatial_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 4)); + $atom_structure['sample_description_table'][$i]['video_frame_width'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 16, 2)); + $atom_structure['sample_description_table'][$i]['video_frame_height'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 18, 2)); + $atom_structure['sample_description_table'][$i]['video_resolution_x'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 20, 4)); + $atom_structure['sample_description_table'][$i]['video_resolution_y'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 24, 4)); + $atom_structure['sample_description_table'][$i]['video_data_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 28, 4)); + $atom_structure['sample_description_table'][$i]['video_frame_count'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 32, 2)); + $atom_structure['sample_description_table'][$i]['video_encoder_name_len'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 34, 1)); + $atom_structure['sample_description_table'][$i]['video_encoder_name'] = substr($atom_structure['sample_description_table'][$i]['data'], 35, $atom_structure['sample_description_table'][$i]['video_encoder_name_len']); + $atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 66, 2)); + $atom_structure['sample_description_table'][$i]['video_color_table_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 68, 2)); + + $atom_structure['sample_description_table'][$i]['video_pixel_color_type'] = (($atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] > 32) ? 'grayscale' : 'color'); + $atom_structure['sample_description_table'][$i]['video_pixel_color_name'] = $this->QuicktimeColorNameLookup($atom_structure['sample_description_table'][$i]['video_pixel_color_depth']); + + if ($atom_structure['sample_description_table'][$i]['video_pixel_color_name'] != 'invalid') { + $info['quicktime']['video']['codec_fourcc'] = $atom_structure['sample_description_table'][$i]['data_format']; + $info['quicktime']['video']['codec_fourcc_lookup'] = $this->QuicktimeVideoCodecLookup($atom_structure['sample_description_table'][$i]['data_format']); + $info['quicktime']['video']['codec'] = (($atom_structure['sample_description_table'][$i]['video_encoder_name_len'] > 0) ? $atom_structure['sample_description_table'][$i]['video_encoder_name'] : $atom_structure['sample_description_table'][$i]['data_format']); + $info['quicktime']['video']['color_depth'] = $atom_structure['sample_description_table'][$i]['video_pixel_color_depth']; + $info['quicktime']['video']['color_depth_name'] = $atom_structure['sample_description_table'][$i]['video_pixel_color_name']; + + $info['video']['codec'] = $info['quicktime']['video']['codec']; + $info['video']['bits_per_sample'] = $info['quicktime']['video']['color_depth']; + } + $info['video']['lossless'] = false; + $info['video']['pixel_aspect_ratio'] = (float) 1; + break; + } + break; + } + switch (strtolower($atom_structure['sample_description_table'][$i]['data_format'])) { + case 'mp4a': + $info['audio']['dataformat'] = 'mp4'; + $info['quicktime']['audio']['codec'] = 'mp4'; + break; + + case '3ivx': + case '3iv1': + case '3iv2': + $info['video']['dataformat'] = '3ivx'; + break; + + case 'xvid': + $info['video']['dataformat'] = 'xvid'; + break; + + case 'mp4v': + $info['video']['dataformat'] = 'mpeg4'; + break; + + case 'divx': + case 'div1': + case 'div2': + case 'div3': + case 'div4': + case 'div5': + case 'div6': + $info['video']['dataformat'] = 'divx'; + break; + + default: + // do nothing + break; + } + unset($atom_structure['sample_description_table'][$i]['data']); + } + break; + + + case 'stts': // Sample Table Time-to-Sample atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); + $sttsEntriesDataOffset = 8; + //$FrameRateCalculatorArray = array(); + $frames_count = 0; + + $max_stts_entries_to_scan = min(floor($this->getid3->memory_limit / 10000), $atom_structure['number_entries']); + if ($max_stts_entries_to_scan < $atom_structure['number_entries']) { + $info['warning'][] = 'QuickTime atom "stts" has '.$atom_structure['number_entries'].' but only scanning the first '.$max_stts_entries_to_scan.' entries due to limited PHP memory available ('.floor($this->getid3->memory_limit / 1048576).'MB).'; + } + for ($i = 0; $i < $max_stts_entries_to_scan; $i++) { + $atom_structure['time_to_sample_table'][$i]['sample_count'] = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4)); + $sttsEntriesDataOffset += 4; + $atom_structure['time_to_sample_table'][$i]['sample_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4)); + $sttsEntriesDataOffset += 4; + + $frames_count += $atom_structure['time_to_sample_table'][$i]['sample_count']; + + // THIS SECTION REPLACED WITH CODE IN "stbl" ATOM + //if (!empty($info['quicktime']['time_scale']) && ($atom_structure['time_to_sample_table'][$i]['sample_duration'] > 0)) { + // $stts_new_framerate = $info['quicktime']['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration']; + // if ($stts_new_framerate <= 60) { + // // some atoms have durations of "1" giving a very large framerate, which probably is not right + // $info['video']['frame_rate'] = max($info['video']['frame_rate'], $stts_new_framerate); + // } + //} + // + //$FrameRateCalculatorArray[($info['quicktime']['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration'])] += $atom_structure['time_to_sample_table'][$i]['sample_count']; + } + $info['quicktime']['stts_framecount'][] = $frames_count; + //$sttsFramesTotal = 0; + //$sttsSecondsTotal = 0; + //foreach ($FrameRateCalculatorArray as $frames_per_second => $frame_count) { + // if (($frames_per_second > 60) || ($frames_per_second < 1)) { + // // not video FPS information, probably audio information + // $sttsFramesTotal = 0; + // $sttsSecondsTotal = 0; + // break; + // } + // $sttsFramesTotal += $frame_count; + // $sttsSecondsTotal += $frame_count / $frames_per_second; + //} + //if (($sttsFramesTotal > 0) && ($sttsSecondsTotal > 0)) { + // if (($sttsFramesTotal / $sttsSecondsTotal) > $info['video']['frame_rate']) { + // $info['video']['frame_rate'] = $sttsFramesTotal / $sttsSecondsTotal; + // } + //} + break; + + + case 'stss': // Sample Table Sync Sample (key frames) atom + if ($ParseAllPossibleAtoms) { + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); + $stssEntriesDataOffset = 8; + for ($i = 0; $i < $atom_structure['number_entries']; $i++) { + $atom_structure['time_to_sample_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stssEntriesDataOffset, 4)); + $stssEntriesDataOffset += 4; + } + } + break; + + + case 'stsc': // Sample Table Sample-to-Chunk atom + if ($ParseAllPossibleAtoms) { + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); + $stscEntriesDataOffset = 8; + for ($i = 0; $i < $atom_structure['number_entries']; $i++) { + $atom_structure['sample_to_chunk_table'][$i]['first_chunk'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4)); + $stscEntriesDataOffset += 4; + $atom_structure['sample_to_chunk_table'][$i]['samples_per_chunk'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4)); + $stscEntriesDataOffset += 4; + $atom_structure['sample_to_chunk_table'][$i]['sample_description'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4)); + $stscEntriesDataOffset += 4; + } + } + break; + + + case 'stsz': // Sample Table SiZe atom + if ($ParseAllPossibleAtoms) { + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['sample_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); + $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4)); + $stszEntriesDataOffset = 12; + if ($atom_structure['sample_size'] == 0) { + for ($i = 0; $i < $atom_structure['number_entries']; $i++) { + $atom_structure['sample_size_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stszEntriesDataOffset, 4)); + $stszEntriesDataOffset += 4; + } + } + } + break; + + + case 'stco': // Sample Table Chunk Offset atom + if ($ParseAllPossibleAtoms) { + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); + $stcoEntriesDataOffset = 8; + for ($i = 0; $i < $atom_structure['number_entries']; $i++) { + $atom_structure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stcoEntriesDataOffset, 4)); + $stcoEntriesDataOffset += 4; + } + } + break; + + + case 'co64': // Chunk Offset 64-bit (version of "stco" that supports > 2GB files) + if ($ParseAllPossibleAtoms) { + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); + $stcoEntriesDataOffset = 8; + for ($i = 0; $i < $atom_structure['number_entries']; $i++) { + $atom_structure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stcoEntriesDataOffset, 8)); + $stcoEntriesDataOffset += 8; + } + } + break; + + + case 'dref': // Data REFerence atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); + $drefDataOffset = 8; + for ($i = 0; $i < $atom_structure['number_entries']; $i++) { + $atom_structure['data_references'][$i]['size'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 4)); + $drefDataOffset += 4; + $atom_structure['data_references'][$i]['type'] = substr($atom_data, $drefDataOffset, 4); + $drefDataOffset += 4; + $atom_structure['data_references'][$i]['version'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 1)); + $drefDataOffset += 1; + $atom_structure['data_references'][$i]['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 3)); // hardcoded: 0x0000 + $drefDataOffset += 3; + $atom_structure['data_references'][$i]['data'] = substr($atom_data, $drefDataOffset, ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3)); + $drefDataOffset += ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3); + + $atom_structure['data_references'][$i]['flags']['self_reference'] = (bool) ($atom_structure['data_references'][$i]['flags_raw'] & 0x001); + } + break; + + + case 'gmin': // base Media INformation atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['graphics_mode'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); + $atom_structure['opcolor_red'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2)); + $atom_structure['opcolor_green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 2)); + $atom_structure['opcolor_blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2)); + $atom_structure['balance'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 2)); + $atom_structure['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, 14, 2)); + break; + + + case 'smhd': // Sound Media information HeaDer atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['balance'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); + $atom_structure['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2)); + break; + + + case 'vmhd': // Video Media information HeaDer atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); + $atom_structure['graphics_mode'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); + $atom_structure['opcolor_red'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2)); + $atom_structure['opcolor_green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 2)); + $atom_structure['opcolor_blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2)); + + $atom_structure['flags']['no_lean_ahead'] = (bool) ($atom_structure['flags_raw'] & 0x001); + break; + + + case 'hdlr': // HanDLeR reference atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['component_type'] = substr($atom_data, 4, 4); + $atom_structure['component_subtype'] = substr($atom_data, 8, 4); + $atom_structure['component_manufacturer'] = substr($atom_data, 12, 4); + $atom_structure['component_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4)); + $atom_structure['component_flags_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4)); + $atom_structure['component_name'] = $this->Pascal2String(substr($atom_data, 24)); + + if (($atom_structure['component_subtype'] == 'STpn') && ($atom_structure['component_manufacturer'] == 'zzzz')) { + $info['video']['dataformat'] = 'quicktimevr'; + } + break; + + + case 'mdhd': // MeDia HeaDer atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['creation_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); + $atom_structure['modify_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4)); + $atom_structure['time_scale'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4)); + $atom_structure['duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4)); + $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 2)); + $atom_structure['quality'] = getid3_lib::BigEndian2Int(substr($atom_data, 22, 2)); + + if ($atom_structure['time_scale'] == 0) { + $info['error'][] = 'Corrupt Quicktime file: mdhd.time_scale == zero'; + return false; + } + $info['quicktime']['time_scale'] = (isset($info['quicktime']['time_scale']) ? max($info['quicktime']['time_scale'], $atom_structure['time_scale']) : $atom_structure['time_scale']); + + $atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']); + $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']); + $atom_structure['playtime_seconds'] = $atom_structure['duration'] / $atom_structure['time_scale']; + $atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']); + if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) { + $info['comments']['language'][] = $atom_structure['language']; + } + break; + + + case 'pnot': // Preview atom + $atom_structure['modification_date'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); // "standard Macintosh format" + $atom_structure['version_number'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x00 + $atom_structure['atom_type'] = substr($atom_data, 6, 4); // usually: 'PICT' + $atom_structure['atom_index'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2)); // usually: 0x01 + + $atom_structure['modification_date_unix'] = getid3_lib::DateMac2Unix($atom_structure['modification_date']); + break; + + + case 'crgn': // Clipping ReGioN atom + $atom_structure['region_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2)); // The Region size, Region boundary box, + $atom_structure['boundary_box'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 8)); // and Clipping region data fields + $atom_structure['clipping_data'] = substr($atom_data, 10); // constitute a QuickDraw region. + break; + + + case 'load': // track LOAD settings atom + $atom_structure['preload_start_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); + $atom_structure['preload_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); + $atom_structure['preload_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4)); + $atom_structure['default_hints_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4)); + + $atom_structure['default_hints']['double_buffer'] = (bool) ($atom_structure['default_hints_raw'] & 0x0020); + $atom_structure['default_hints']['high_quality'] = (bool) ($atom_structure['default_hints_raw'] & 0x0100); + break; + + + case 'tmcd': // TiMe CoDe atom + case 'chap': // CHAPter list atom + case 'sync': // SYNChronization atom + case 'scpt': // tranSCriPT atom + case 'ssrc': // non-primary SouRCe atom + for ($i = 0; $i < strlen($atom_data); $i += 4) { + @$atom_structure['track_id'][] = getid3_lib::BigEndian2Int(substr($atom_data, $i, 4)); + } + break; + + + case 'elst': // Edit LiST atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); + for ($i = 0; $i < $atom_structure['number_entries']; $i++ ) { + $atom_structure['edit_list'][$i]['track_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($i * 12) + 0, 4)); + $atom_structure['edit_list'][$i]['media_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($i * 12) + 4, 4)); + $atom_structure['edit_list'][$i]['media_rate'] = getid3_lib::FixedPoint16_16(substr($atom_data, 8 + ($i * 12) + 8, 4)); + } + break; + + + case 'kmat': // compressed MATte atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 + $atom_structure['matte_data_raw'] = substr($atom_data, 4); + break; + + + case 'ctab': // Color TABle atom + $atom_structure['color_table_seed'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); // hardcoded: 0x00000000 + $atom_structure['color_table_flags'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x8000 + $atom_structure['color_table_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2)) + 1; + for ($colortableentry = 0; $colortableentry < $atom_structure['color_table_size']; $colortableentry++) { + $atom_structure['color_table'][$colortableentry]['alpha'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 0, 2)); + $atom_structure['color_table'][$colortableentry]['red'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 2, 2)); + $atom_structure['color_table'][$colortableentry]['green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 4, 2)); + $atom_structure['color_table'][$colortableentry]['blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 6, 2)); + } + break; + + + case 'mvhd': // MoVie HeaDer atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); + $atom_structure['creation_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); + $atom_structure['modify_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4)); + $atom_structure['time_scale'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4)); + $atom_structure['duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4)); + $atom_structure['preferred_rate'] = getid3_lib::FixedPoint16_16(substr($atom_data, 20, 4)); + $atom_structure['preferred_volume'] = getid3_lib::FixedPoint8_8(substr($atom_data, 24, 2)); + $atom_structure['reserved'] = substr($atom_data, 26, 10); + $atom_structure['matrix_a'] = getid3_lib::FixedPoint16_16(substr($atom_data, 36, 4)); + $atom_structure['matrix_b'] = getid3_lib::FixedPoint16_16(substr($atom_data, 40, 4)); + $atom_structure['matrix_u'] = getid3_lib::FixedPoint2_30(substr($atom_data, 44, 4)); + $atom_structure['matrix_c'] = getid3_lib::FixedPoint16_16(substr($atom_data, 48, 4)); + $atom_structure['matrix_d'] = getid3_lib::FixedPoint16_16(substr($atom_data, 52, 4)); + $atom_structure['matrix_v'] = getid3_lib::FixedPoint2_30(substr($atom_data, 56, 4)); + $atom_structure['matrix_x'] = getid3_lib::FixedPoint16_16(substr($atom_data, 60, 4)); + $atom_structure['matrix_y'] = getid3_lib::FixedPoint16_16(substr($atom_data, 64, 4)); + $atom_structure['matrix_w'] = getid3_lib::FixedPoint2_30(substr($atom_data, 68, 4)); + $atom_structure['preview_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 72, 4)); + $atom_structure['preview_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 76, 4)); + $atom_structure['poster_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 80, 4)); + $atom_structure['selection_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 84, 4)); + $atom_structure['selection_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 88, 4)); + $atom_structure['current_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 92, 4)); + $atom_structure['next_track_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 96, 4)); + + if ($atom_structure['time_scale'] == 0) { + $info['error'][] = 'Corrupt Quicktime file: mvhd.time_scale == zero'; + return false; + } + $atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']); + $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']); + $info['quicktime']['time_scale'] = (isset($info['quicktime']['time_scale']) ? max($info['quicktime']['time_scale'], $atom_structure['time_scale']) : $atom_structure['time_scale']); + $info['quicktime']['display_scale'] = $atom_structure['matrix_a']; + $info['playtime_seconds'] = $atom_structure['duration'] / $atom_structure['time_scale']; + break; + + + case 'tkhd': // TracK HeaDer atom + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); + $atom_structure['creation_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); + $atom_structure['modify_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4)); + $atom_structure['trackid'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4)); + $atom_structure['reserved1'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4)); + $atom_structure['duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4)); + $atom_structure['reserved2'] = getid3_lib::BigEndian2Int(substr($atom_data, 24, 8)); + $atom_structure['layer'] = getid3_lib::BigEndian2Int(substr($atom_data, 32, 2)); + $atom_structure['alternate_group'] = getid3_lib::BigEndian2Int(substr($atom_data, 34, 2)); + $atom_structure['volume'] = getid3_lib::FixedPoint8_8(substr($atom_data, 36, 2)); + $atom_structure['reserved3'] = getid3_lib::BigEndian2Int(substr($atom_data, 38, 2)); +// http://developer.apple.com/library/mac/#documentation/QuickTime/RM/MovieBasics/MTEditing/K-Chapter/11MatrixFunctions.html +// http://developer.apple.com/library/mac/#documentation/QuickTime/qtff/QTFFChap4/qtff4.html#//apple_ref/doc/uid/TP40000939-CH206-18737 + $atom_structure['matrix_a'] = getid3_lib::FixedPoint16_16(substr($atom_data, 40, 4)); + $atom_structure['matrix_b'] = getid3_lib::FixedPoint16_16(substr($atom_data, 44, 4)); + $atom_structure['matrix_u'] = getid3_lib::FixedPoint2_30(substr($atom_data, 48, 4)); + $atom_structure['matrix_c'] = getid3_lib::FixedPoint16_16(substr($atom_data, 52, 4)); + $atom_structure['matrix_d'] = getid3_lib::FixedPoint16_16(substr($atom_data, 56, 4)); + $atom_structure['matrix_v'] = getid3_lib::FixedPoint2_30(substr($atom_data, 60, 4)); + $atom_structure['matrix_x'] = getid3_lib::FixedPoint16_16(substr($atom_data, 64, 4)); + $atom_structure['matrix_y'] = getid3_lib::FixedPoint16_16(substr($atom_data, 68, 4)); + $atom_structure['matrix_w'] = getid3_lib::FixedPoint2_30(substr($atom_data, 72, 4)); + $atom_structure['width'] = getid3_lib::FixedPoint16_16(substr($atom_data, 76, 4)); + $atom_structure['height'] = getid3_lib::FixedPoint16_16(substr($atom_data, 80, 4)); + $atom_structure['flags']['enabled'] = (bool) ($atom_structure['flags_raw'] & 0x0001); + $atom_structure['flags']['in_movie'] = (bool) ($atom_structure['flags_raw'] & 0x0002); + $atom_structure['flags']['in_preview'] = (bool) ($atom_structure['flags_raw'] & 0x0004); + $atom_structure['flags']['in_poster'] = (bool) ($atom_structure['flags_raw'] & 0x0008); + $atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']); + $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']); + + if ($atom_structure['flags']['enabled'] == 1) { + if (!isset($info['video']['resolution_x']) || !isset($info['video']['resolution_y'])) { + $info['video']['resolution_x'] = $atom_structure['width']; + $info['video']['resolution_y'] = $atom_structure['height']; + } + $info['video']['resolution_x'] = max($info['video']['resolution_x'], $atom_structure['width']); + $info['video']['resolution_y'] = max($info['video']['resolution_y'], $atom_structure['height']); + $info['quicktime']['video']['resolution_x'] = $info['video']['resolution_x']; + $info['quicktime']['video']['resolution_y'] = $info['video']['resolution_y']; + } else { + // see: http://www.getid3.org/phpBB3/viewtopic.php?t=1295 + //if (isset($info['video']['resolution_x'])) { unset($info['video']['resolution_x']); } + //if (isset($info['video']['resolution_y'])) { unset($info['video']['resolution_y']); } + //if (isset($info['quicktime']['video'])) { unset($info['quicktime']['video']); } + } + break; + + + case 'iods': // Initial Object DeScriptor atom + // http://www.koders.com/c/fid1FAB3E762903DC482D8A246D4A4BF9F28E049594.aspx?s=windows.h + // http://libquicktime.sourcearchive.com/documentation/1.0.2plus-pdebian/iods_8c-source.html + $offset = 0; + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); + $offset += 1; + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 3)); + $offset += 3; + $atom_structure['mp4_iod_tag'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); + $offset += 1; + $atom_structure['length'] = $this->quicktime_read_mp4_descr_length($atom_data, $offset); + //$offset already adjusted by quicktime_read_mp4_descr_length() + $atom_structure['object_descriptor_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2)); + $offset += 2; + $atom_structure['od_profile_level'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); + $offset += 1; + $atom_structure['scene_profile_level'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); + $offset += 1; + $atom_structure['audio_profile_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); + $offset += 1; + $atom_structure['video_profile_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); + $offset += 1; + $atom_structure['graphics_profile_level'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); + $offset += 1; + + $atom_structure['num_iods_tracks'] = ($atom_structure['length'] - 7) / 6; // 6 bytes would only be right if all tracks use 1-byte length fields + for ($i = 0; $i < $atom_structure['num_iods_tracks']; $i++) { + $atom_structure['track'][$i]['ES_ID_IncTag'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); + $offset += 1; + $atom_structure['track'][$i]['length'] = $this->quicktime_read_mp4_descr_length($atom_data, $offset); + //$offset already adjusted by quicktime_read_mp4_descr_length() + $atom_structure['track'][$i]['track_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 4)); + $offset += 4; + } + + $atom_structure['audio_profile_name'] = $this->QuicktimeIODSaudioProfileName($atom_structure['audio_profile_id']); + $atom_structure['video_profile_name'] = $this->QuicktimeIODSvideoProfileName($atom_structure['video_profile_id']); + break; + + case 'ftyp': // FileTYPe (?) atom (for MP4 it seems) + $atom_structure['signature'] = substr($atom_data, 0, 4); + $atom_structure['unknown_1'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); + $atom_structure['fourcc'] = substr($atom_data, 8, 4); + break; + + case 'mdat': // Media DATa atom + // 'mdat' contains the actual data for the audio/video, possibly also subtitles + +/* due to lack of known documentation, this is a kludge implementation. If you know of documentation on how mdat is properly structed, please send it to info@getid3.org */ + + // first, skip any 'wide' padding, and second 'mdat' header (with specified size of zero?) + $mdat_offset = 0; + while (true) { + if (substr($atom_data, $mdat_offset, 8) == "\x00\x00\x00\x08".'wide') { + $mdat_offset += 8; + } elseif (substr($atom_data, $mdat_offset, 8) == "\x00\x00\x00\x00".'mdat') { + $mdat_offset += 8; + } else { + break; + } + } + + // check to see if it looks like chapter titles, in the form of unterminated strings with a leading 16-bit size field + while (($chapter_string_length = getid3_lib::BigEndian2Int(substr($atom_data, $mdat_offset, 2))) + && ($chapter_string_length < 1000) + && ($chapter_string_length <= (strlen($atom_data) - $mdat_offset - 2)) + && preg_match('#^[\x20-\xFF]+$#', substr($atom_data, $mdat_offset + 2, $chapter_string_length), $chapter_matches)) { + $mdat_offset += (2 + $chapter_string_length); + @$info['quicktime']['comments']['chapters'][] = $chapter_matches[0]; + } + + + + if (($atomsize > 8) && (!isset($info['avdataend_tmp']) || ($info['quicktime'][$atomname]['size'] > ($info['avdataend_tmp'] - $info['avdataoffset'])))) { + + $info['avdataoffset'] = $atom_structure['offset'] + 8; // $info['quicktime'][$atomname]['offset'] + 8; + $OldAVDataEnd = $info['avdataend']; + $info['avdataend'] = $atom_structure['offset'] + $atom_structure['size']; // $info['quicktime'][$atomname]['offset'] + $info['quicktime'][$atomname]['size']; + + $getid3_temp = new getID3(); + $getid3_temp->openfile($this->getid3->filename); + $getid3_temp->info['avdataoffset'] = $info['avdataoffset']; + $getid3_temp->info['avdataend'] = $info['avdataend']; + $getid3_mp3 = new getid3_mp3($getid3_temp); + if ($getid3_mp3->MPEGaudioHeaderValid($getid3_mp3->MPEGaudioHeaderDecode($this->fread(4)))) { + $getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false); + if (!empty($getid3_temp->info['warning'])) { + foreach ($getid3_temp->info['warning'] as $value) { + $info['warning'][] = $value; + } + } + if (!empty($getid3_temp->info['mpeg'])) { + $info['mpeg'] = $getid3_temp->info['mpeg']; + if (isset($info['mpeg']['audio'])) { + $info['audio']['dataformat'] = 'mp3'; + $info['audio']['codec'] = (!empty($info['mpeg']['audio']['encoder']) ? $info['mpeg']['audio']['encoder'] : (!empty($info['mpeg']['audio']['codec']) ? $info['mpeg']['audio']['codec'] : (!empty($info['mpeg']['audio']['LAME']) ? 'LAME' :'mp3'))); + $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate']; + $info['audio']['channels'] = $info['mpeg']['audio']['channels']; + $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate']; + $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']); + $info['bitrate'] = $info['audio']['bitrate']; + } + } + } + unset($getid3_mp3, $getid3_temp); + $info['avdataend'] = $OldAVDataEnd; + unset($OldAVDataEnd); + + } + + unset($mdat_offset, $chapter_string_length, $chapter_matches); + break; + + case 'free': // FREE space atom + case 'skip': // SKIP atom + case 'wide': // 64-bit expansion placeholder atom + // 'free', 'skip' and 'wide' are just padding, contains no useful data at all + + // When writing QuickTime files, it is sometimes necessary to update an atom's size. + // It is impossible to update a 32-bit atom to a 64-bit atom since the 32-bit atom + // is only 8 bytes in size, and the 64-bit atom requires 16 bytes. Therefore, QuickTime + // puts an 8-byte placeholder atom before any atoms it may have to update the size of. + // In this way, if the atom needs to be converted from a 32-bit to a 64-bit atom, the + // placeholder atom can be overwritten to obtain the necessary 8 extra bytes. + // The placeholder atom has a type of kWideAtomPlaceholderType ( 'wide' ). + break; + + + case 'nsav': // NoSAVe atom + // http://developer.apple.com/technotes/tn/tn2038.html + $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); + break; + + case 'ctyp': // Controller TYPe atom (seen on QTVR) + // http://homepages.slingshot.co.nz/~helmboy/quicktime/formats/qtm-layout.txt + // some controller names are: + // 0x00 + 'std' for linear movie + // 'none' for no controls + $atom_structure['ctyp'] = substr($atom_data, 0, 4); + $info['quicktime']['controller'] = $atom_structure['ctyp']; + switch ($atom_structure['ctyp']) { + case 'qtvr': + $info['video']['dataformat'] = 'quicktimevr'; + break; + } + break; + + case 'pano': // PANOrama track (seen on QTVR) + $atom_structure['pano'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); + break; + + case 'hint': // HINT track + case 'hinf': // + case 'hinv': // + case 'hnti': // + $info['quicktime']['hinting'] = true; + break; + + case 'imgt': // IMaGe Track reference (kQTVRImageTrackRefType) (seen on QTVR) + for ($i = 0; $i < ($atom_structure['size'] - 8); $i += 4) { + $atom_structure['imgt'][] = getid3_lib::BigEndian2Int(substr($atom_data, $i, 4)); + } + break; + + + // Observed-but-not-handled atom types are just listed here to prevent warnings being generated + case 'FXTC': // Something to do with Adobe After Effects (?) + case 'PrmA': + case 'code': + case 'FIEL': // this is NOT "fiel" (Field Ordering) as describe here: http://developer.apple.com/documentation/QuickTime/QTFF/QTFFChap3/chapter_4_section_2.html + case 'tapt': // TrackApertureModeDimensionsAID - http://developer.apple.com/documentation/QuickTime/Reference/QT7-1_Update_Reference/Constants/Constants.html + // tapt seems to be used to compute the video size [http://www.getid3.org/phpBB3/viewtopic.php?t=838] + // * http://lists.apple.com/archives/quicktime-api/2006/Aug/msg00014.html + // * http://handbrake.fr/irclogs/handbrake-dev/handbrake-dev20080128_pg2.html + case 'ctts':// STCompositionOffsetAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html + case 'cslg':// STCompositionShiftLeastGreatestAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html + case 'sdtp':// STSampleDependencyAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html + case 'stps':// STPartialSyncSampleAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html + //$atom_structure['data'] = $atom_data; + break; + + case "\xA9".'xyz': // GPS latitude+longitude+altitude + $atom_structure['data'] = $atom_data; + if (preg_match('#([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)?/$#i', $atom_data, $matches)) { + @list($all, $latitude, $longitude, $altitude) = $matches; + $info['quicktime']['comments']['gps_latitude'][] = floatval($latitude); + $info['quicktime']['comments']['gps_longitude'][] = floatval($longitude); + if (!empty($altitude)) { + $info['quicktime']['comments']['gps_altitude'][] = floatval($altitude); + } + } else { + $info['warning'][] = 'QuickTime atom "©xyz" data does not match expected data pattern at offset '.$baseoffset.'. Please report as getID3() bug.'; + } + break; + + case 'NCDT': + // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html + // Nikon-specific QuickTime tags found in the NCDT atom of MOV videos from some Nikon cameras such as the Coolpix S8000 and D5100 + $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 4, $atomHierarchy, $ParseAllPossibleAtoms); + break; + case 'NCTH': // Nikon Camera THumbnail image + case 'NCVW': // Nikon Camera preVieW image + // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html + if (preg_match('/^\xFF\xD8\xFF/', $atom_data)) { + $atom_structure['data'] = $atom_data; + $atom_structure['image_mime'] = 'image/jpeg'; + $atom_structure['description'] = (($atomname == 'NCTH') ? 'Nikon Camera Thumbnail Image' : (($atomname == 'NCVW') ? 'Nikon Camera Preview Image' : 'Nikon preview image')); + $info['quicktime']['comments']['picture'][] = array('image_mime'=>$atom_structure['image_mime'], 'data'=>$atom_data, 'description'=>$atom_structure['description']); + } + break; + case 'NCTG': // Nikon - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#NCTG + $atom_structure['data'] = $this->QuicktimeParseNikonNCTG($atom_data); + break; + case 'NCHD': // Nikon:MakerNoteVersion - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html + case 'NCDB': // Nikon - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html + case 'CNCV': // Canon:CompressorVersion - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Canon.html + $atom_structure['data'] = $atom_data; + break; + + case "\x00\x00\x00\x00": + case 'meta': // METAdata atom + // some kind of metacontainer, may contain a big data dump such as: + // mdta keys  mdtacom.apple.quicktime.make (mdtacom.apple.quicktime.creationdate ,mdtacom.apple.quicktime.location.ISO6709 $mdtacom.apple.quicktime.software !mdtacom.apple.quicktime.model ilst   data DEApple 0  (data DE2011-05-11T17:54:04+0200 2  *data DE+52.4936+013.3897+040.247/   data DE4.3.1  data DEiPhone 4 + // http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt + + $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); + $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); + $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom(substr($atom_data, 4), $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); + //$atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); + break; + + case 'data': // metaDATA atom + // seems to be 2 bytes language code (ASCII), 2 bytes unknown (set to 0x10B5 in sample I have), remainder is useful data + $atom_structure['language'] = substr($atom_data, 4 + 0, 2); + $atom_structure['unknown'] = getid3_lib::BigEndian2Int(substr($atom_data, 4 + 2, 2)); + $atom_structure['data'] = substr($atom_data, 4 + 4); + break; + + default: + $info['warning'][] = 'Unknown QuickTime atom type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" ('.trim(getid3_lib::PrintHexBytes($atomname)).') at offset '.$baseoffset; + $atom_structure['data'] = $atom_data; + break; + } + array_pop($atomHierarchy); + return $atom_structure; + } + + public function QuicktimeParseContainerAtom($atom_data, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) { +//echo 'QuicktimeParseContainerAtom('.substr($atom_data, 4, 4).') @ '.$baseoffset.'

        '; + $atom_structure = false; + $subatomoffset = 0; + $subatomcounter = 0; + if ((strlen($atom_data) == 4) && (getid3_lib::BigEndian2Int($atom_data) == 0x00000000)) { + return false; + } + while ($subatomoffset < strlen($atom_data)) { + $subatomsize = getid3_lib::BigEndian2Int(substr($atom_data, $subatomoffset + 0, 4)); + $subatomname = substr($atom_data, $subatomoffset + 4, 4); + $subatomdata = substr($atom_data, $subatomoffset + 8, $subatomsize - 8); + if ($subatomsize == 0) { + // Furthermore, for historical reasons the list of atoms is optionally + // terminated by a 32-bit integer set to 0. If you are writing a program + // to read user data atoms, you should allow for the terminating 0. + return $atom_structure; + } + + $atom_structure[$subatomcounter] = $this->QuicktimeParseAtom($subatomname, $subatomsize, $subatomdata, $baseoffset + $subatomoffset, $atomHierarchy, $ParseAllPossibleAtoms); + + $subatomoffset += $subatomsize; + $subatomcounter++; + } + return $atom_structure; + } + + + public function quicktime_read_mp4_descr_length($data, &$offset) { + // http://libquicktime.sourcearchive.com/documentation/2:1.0.2plus-pdebian-2build1/esds_8c-source.html + $num_bytes = 0; + $length = 0; + do { + $b = ord(substr($data, $offset++, 1)); + $length = ($length << 7) | ($b & 0x7F); + } while (($b & 0x80) && ($num_bytes++ < 4)); + return $length; + } + + + public function QuicktimeLanguageLookup($languageid) { + // http://developer.apple.com/library/mac/#documentation/QuickTime/QTFF/QTFFChap4/qtff4.html#//apple_ref/doc/uid/TP40000939-CH206-34353 + static $QuicktimeLanguageLookup = array(); + if (empty($QuicktimeLanguageLookup)) { + $QuicktimeLanguageLookup[0] = 'English'; + $QuicktimeLanguageLookup[1] = 'French'; + $QuicktimeLanguageLookup[2] = 'German'; + $QuicktimeLanguageLookup[3] = 'Italian'; + $QuicktimeLanguageLookup[4] = 'Dutch'; + $QuicktimeLanguageLookup[5] = 'Swedish'; + $QuicktimeLanguageLookup[6] = 'Spanish'; + $QuicktimeLanguageLookup[7] = 'Danish'; + $QuicktimeLanguageLookup[8] = 'Portuguese'; + $QuicktimeLanguageLookup[9] = 'Norwegian'; + $QuicktimeLanguageLookup[10] = 'Hebrew'; + $QuicktimeLanguageLookup[11] = 'Japanese'; + $QuicktimeLanguageLookup[12] = 'Arabic'; + $QuicktimeLanguageLookup[13] = 'Finnish'; + $QuicktimeLanguageLookup[14] = 'Greek'; + $QuicktimeLanguageLookup[15] = 'Icelandic'; + $QuicktimeLanguageLookup[16] = 'Maltese'; + $QuicktimeLanguageLookup[17] = 'Turkish'; + $QuicktimeLanguageLookup[18] = 'Croatian'; + $QuicktimeLanguageLookup[19] = 'Chinese (Traditional)'; + $QuicktimeLanguageLookup[20] = 'Urdu'; + $QuicktimeLanguageLookup[21] = 'Hindi'; + $QuicktimeLanguageLookup[22] = 'Thai'; + $QuicktimeLanguageLookup[23] = 'Korean'; + $QuicktimeLanguageLookup[24] = 'Lithuanian'; + $QuicktimeLanguageLookup[25] = 'Polish'; + $QuicktimeLanguageLookup[26] = 'Hungarian'; + $QuicktimeLanguageLookup[27] = 'Estonian'; + $QuicktimeLanguageLookup[28] = 'Lettish'; + $QuicktimeLanguageLookup[28] = 'Latvian'; + $QuicktimeLanguageLookup[29] = 'Saamisk'; + $QuicktimeLanguageLookup[29] = 'Lappish'; + $QuicktimeLanguageLookup[30] = 'Faeroese'; + $QuicktimeLanguageLookup[31] = 'Farsi'; + $QuicktimeLanguageLookup[31] = 'Persian'; + $QuicktimeLanguageLookup[32] = 'Russian'; + $QuicktimeLanguageLookup[33] = 'Chinese (Simplified)'; + $QuicktimeLanguageLookup[34] = 'Flemish'; + $QuicktimeLanguageLookup[35] = 'Irish'; + $QuicktimeLanguageLookup[36] = 'Albanian'; + $QuicktimeLanguageLookup[37] = 'Romanian'; + $QuicktimeLanguageLookup[38] = 'Czech'; + $QuicktimeLanguageLookup[39] = 'Slovak'; + $QuicktimeLanguageLookup[40] = 'Slovenian'; + $QuicktimeLanguageLookup[41] = 'Yiddish'; + $QuicktimeLanguageLookup[42] = 'Serbian'; + $QuicktimeLanguageLookup[43] = 'Macedonian'; + $QuicktimeLanguageLookup[44] = 'Bulgarian'; + $QuicktimeLanguageLookup[45] = 'Ukrainian'; + $QuicktimeLanguageLookup[46] = 'Byelorussian'; + $QuicktimeLanguageLookup[47] = 'Uzbek'; + $QuicktimeLanguageLookup[48] = 'Kazakh'; + $QuicktimeLanguageLookup[49] = 'Azerbaijani'; + $QuicktimeLanguageLookup[50] = 'AzerbaijanAr'; + $QuicktimeLanguageLookup[51] = 'Armenian'; + $QuicktimeLanguageLookup[52] = 'Georgian'; + $QuicktimeLanguageLookup[53] = 'Moldavian'; + $QuicktimeLanguageLookup[54] = 'Kirghiz'; + $QuicktimeLanguageLookup[55] = 'Tajiki'; + $QuicktimeLanguageLookup[56] = 'Turkmen'; + $QuicktimeLanguageLookup[57] = 'Mongolian'; + $QuicktimeLanguageLookup[58] = 'MongolianCyr'; + $QuicktimeLanguageLookup[59] = 'Pashto'; + $QuicktimeLanguageLookup[60] = 'Kurdish'; + $QuicktimeLanguageLookup[61] = 'Kashmiri'; + $QuicktimeLanguageLookup[62] = 'Sindhi'; + $QuicktimeLanguageLookup[63] = 'Tibetan'; + $QuicktimeLanguageLookup[64] = 'Nepali'; + $QuicktimeLanguageLookup[65] = 'Sanskrit'; + $QuicktimeLanguageLookup[66] = 'Marathi'; + $QuicktimeLanguageLookup[67] = 'Bengali'; + $QuicktimeLanguageLookup[68] = 'Assamese'; + $QuicktimeLanguageLookup[69] = 'Gujarati'; + $QuicktimeLanguageLookup[70] = 'Punjabi'; + $QuicktimeLanguageLookup[71] = 'Oriya'; + $QuicktimeLanguageLookup[72] = 'Malayalam'; + $QuicktimeLanguageLookup[73] = 'Kannada'; + $QuicktimeLanguageLookup[74] = 'Tamil'; + $QuicktimeLanguageLookup[75] = 'Telugu'; + $QuicktimeLanguageLookup[76] = 'Sinhalese'; + $QuicktimeLanguageLookup[77] = 'Burmese'; + $QuicktimeLanguageLookup[78] = 'Khmer'; + $QuicktimeLanguageLookup[79] = 'Lao'; + $QuicktimeLanguageLookup[80] = 'Vietnamese'; + $QuicktimeLanguageLookup[81] = 'Indonesian'; + $QuicktimeLanguageLookup[82] = 'Tagalog'; + $QuicktimeLanguageLookup[83] = 'MalayRoman'; + $QuicktimeLanguageLookup[84] = 'MalayArabic'; + $QuicktimeLanguageLookup[85] = 'Amharic'; + $QuicktimeLanguageLookup[86] = 'Tigrinya'; + $QuicktimeLanguageLookup[87] = 'Galla'; + $QuicktimeLanguageLookup[87] = 'Oromo'; + $QuicktimeLanguageLookup[88] = 'Somali'; + $QuicktimeLanguageLookup[89] = 'Swahili'; + $QuicktimeLanguageLookup[90] = 'Ruanda'; + $QuicktimeLanguageLookup[91] = 'Rundi'; + $QuicktimeLanguageLookup[92] = 'Chewa'; + $QuicktimeLanguageLookup[93] = 'Malagasy'; + $QuicktimeLanguageLookup[94] = 'Esperanto'; + $QuicktimeLanguageLookup[128] = 'Welsh'; + $QuicktimeLanguageLookup[129] = 'Basque'; + $QuicktimeLanguageLookup[130] = 'Catalan'; + $QuicktimeLanguageLookup[131] = 'Latin'; + $QuicktimeLanguageLookup[132] = 'Quechua'; + $QuicktimeLanguageLookup[133] = 'Guarani'; + $QuicktimeLanguageLookup[134] = 'Aymara'; + $QuicktimeLanguageLookup[135] = 'Tatar'; + $QuicktimeLanguageLookup[136] = 'Uighur'; + $QuicktimeLanguageLookup[137] = 'Dzongkha'; + $QuicktimeLanguageLookup[138] = 'JavaneseRom'; + $QuicktimeLanguageLookup[32767] = 'Unspecified'; + } + if (($languageid > 138) && ($languageid < 32767)) { + /* + ISO Language Codes - http://www.loc.gov/standards/iso639-2/php/code_list.php + Because the language codes specified by ISO 639-2/T are three characters long, they must be packed to fit into a 16-bit field. + The packing algorithm must map each of the three characters, which are always lowercase, into a 5-bit integer and then concatenate + these integers into the least significant 15 bits of a 16-bit integer, leaving the 16-bit integer's most significant bit set to zero. + + One algorithm for performing this packing is to treat each ISO character as a 16-bit integer. Subtract 0x60 from the first character + and multiply by 2^10 (0x400), subtract 0x60 from the second character and multiply by 2^5 (0x20), subtract 0x60 from the third character, + and add the three 16-bit values. This will result in a single 16-bit value with the three codes correctly packed into the 15 least + significant bits and the most significant bit set to zero. + */ + $iso_language_id = ''; + $iso_language_id .= chr((($languageid & 0x7C00) >> 10) + 0x60); + $iso_language_id .= chr((($languageid & 0x03E0) >> 5) + 0x60); + $iso_language_id .= chr((($languageid & 0x001F) >> 0) + 0x60); + $QuicktimeLanguageLookup[$languageid] = getid3_id3v2::LanguageLookup($iso_language_id); + } + return (isset($QuicktimeLanguageLookup[$languageid]) ? $QuicktimeLanguageLookup[$languageid] : 'invalid'); + } + + public function QuicktimeVideoCodecLookup($codecid) { + static $QuicktimeVideoCodecLookup = array(); + if (empty($QuicktimeVideoCodecLookup)) { + $QuicktimeVideoCodecLookup['.SGI'] = 'SGI'; + $QuicktimeVideoCodecLookup['3IV1'] = '3ivx MPEG-4 v1'; + $QuicktimeVideoCodecLookup['3IV2'] = '3ivx MPEG-4 v2'; + $QuicktimeVideoCodecLookup['3IVX'] = '3ivx MPEG-4'; + $QuicktimeVideoCodecLookup['8BPS'] = 'Planar RGB'; + $QuicktimeVideoCodecLookup['avc1'] = 'H.264/MPEG-4 AVC'; + $QuicktimeVideoCodecLookup['avr '] = 'AVR-JPEG'; + $QuicktimeVideoCodecLookup['b16g'] = '16Gray'; + $QuicktimeVideoCodecLookup['b32a'] = '32AlphaGray'; + $QuicktimeVideoCodecLookup['b48r'] = '48RGB'; + $QuicktimeVideoCodecLookup['b64a'] = '64ARGB'; + $QuicktimeVideoCodecLookup['base'] = 'Base'; + $QuicktimeVideoCodecLookup['clou'] = 'Cloud'; + $QuicktimeVideoCodecLookup['cmyk'] = 'CMYK'; + $QuicktimeVideoCodecLookup['cvid'] = 'Cinepak'; + $QuicktimeVideoCodecLookup['dmb1'] = 'OpenDML JPEG'; + $QuicktimeVideoCodecLookup['dvc '] = 'DVC-NTSC'; + $QuicktimeVideoCodecLookup['dvcp'] = 'DVC-PAL'; + $QuicktimeVideoCodecLookup['dvpn'] = 'DVCPro-NTSC'; + $QuicktimeVideoCodecLookup['dvpp'] = 'DVCPro-PAL'; + $QuicktimeVideoCodecLookup['fire'] = 'Fire'; + $QuicktimeVideoCodecLookup['flic'] = 'FLC'; + $QuicktimeVideoCodecLookup['gif '] = 'GIF'; + $QuicktimeVideoCodecLookup['h261'] = 'H261'; + $QuicktimeVideoCodecLookup['h263'] = 'H263'; + $QuicktimeVideoCodecLookup['IV41'] = 'Indeo4'; + $QuicktimeVideoCodecLookup['jpeg'] = 'JPEG'; + $QuicktimeVideoCodecLookup['kpcd'] = 'PhotoCD'; + $QuicktimeVideoCodecLookup['mjpa'] = 'Motion JPEG-A'; + $QuicktimeVideoCodecLookup['mjpb'] = 'Motion JPEG-B'; + $QuicktimeVideoCodecLookup['msvc'] = 'Microsoft Video1'; + $QuicktimeVideoCodecLookup['myuv'] = 'MPEG YUV420'; + $QuicktimeVideoCodecLookup['path'] = 'Vector'; + $QuicktimeVideoCodecLookup['png '] = 'PNG'; + $QuicktimeVideoCodecLookup['PNTG'] = 'MacPaint'; + $QuicktimeVideoCodecLookup['qdgx'] = 'QuickDrawGX'; + $QuicktimeVideoCodecLookup['qdrw'] = 'QuickDraw'; + $QuicktimeVideoCodecLookup['raw '] = 'RAW'; + $QuicktimeVideoCodecLookup['ripl'] = 'WaterRipple'; + $QuicktimeVideoCodecLookup['rpza'] = 'Video'; + $QuicktimeVideoCodecLookup['smc '] = 'Graphics'; + $QuicktimeVideoCodecLookup['SVQ1'] = 'Sorenson Video 1'; + $QuicktimeVideoCodecLookup['SVQ1'] = 'Sorenson Video 3'; + $QuicktimeVideoCodecLookup['syv9'] = 'Sorenson YUV9'; + $QuicktimeVideoCodecLookup['tga '] = 'Targa'; + $QuicktimeVideoCodecLookup['tiff'] = 'TIFF'; + $QuicktimeVideoCodecLookup['WRAW'] = 'Windows RAW'; + $QuicktimeVideoCodecLookup['WRLE'] = 'BMP'; + $QuicktimeVideoCodecLookup['y420'] = 'YUV420'; + $QuicktimeVideoCodecLookup['yuv2'] = 'ComponentVideo'; + $QuicktimeVideoCodecLookup['yuvs'] = 'ComponentVideoUnsigned'; + $QuicktimeVideoCodecLookup['yuvu'] = 'ComponentVideoSigned'; + } + return (isset($QuicktimeVideoCodecLookup[$codecid]) ? $QuicktimeVideoCodecLookup[$codecid] : ''); + } + + public function QuicktimeAudioCodecLookup($codecid) { + static $QuicktimeAudioCodecLookup = array(); + if (empty($QuicktimeAudioCodecLookup)) { + $QuicktimeAudioCodecLookup['.mp3'] = 'Fraunhofer MPEG Layer-III alias'; + $QuicktimeAudioCodecLookup['aac '] = 'ISO/IEC 14496-3 AAC'; + $QuicktimeAudioCodecLookup['agsm'] = 'Apple GSM 10:1'; + $QuicktimeAudioCodecLookup['alac'] = 'Apple Lossless Audio Codec'; + $QuicktimeAudioCodecLookup['alaw'] = 'A-law 2:1'; + $QuicktimeAudioCodecLookup['conv'] = 'Sample Format'; + $QuicktimeAudioCodecLookup['dvca'] = 'DV'; + $QuicktimeAudioCodecLookup['dvi '] = 'DV 4:1'; + $QuicktimeAudioCodecLookup['eqal'] = 'Frequency Equalizer'; + $QuicktimeAudioCodecLookup['fl32'] = '32-bit Floating Point'; + $QuicktimeAudioCodecLookup['fl64'] = '64-bit Floating Point'; + $QuicktimeAudioCodecLookup['ima4'] = 'Interactive Multimedia Association 4:1'; + $QuicktimeAudioCodecLookup['in24'] = '24-bit Integer'; + $QuicktimeAudioCodecLookup['in32'] = '32-bit Integer'; + $QuicktimeAudioCodecLookup['lpc '] = 'LPC 23:1'; + $QuicktimeAudioCodecLookup['MAC3'] = 'Macintosh Audio Compression/Expansion (MACE) 3:1'; + $QuicktimeAudioCodecLookup['MAC6'] = 'Macintosh Audio Compression/Expansion (MACE) 6:1'; + $QuicktimeAudioCodecLookup['mixb'] = '8-bit Mixer'; + $QuicktimeAudioCodecLookup['mixw'] = '16-bit Mixer'; + $QuicktimeAudioCodecLookup['mp4a'] = 'ISO/IEC 14496-3 AAC'; + $QuicktimeAudioCodecLookup['MS'."\x00\x02"] = 'Microsoft ADPCM'; + $QuicktimeAudioCodecLookup['MS'."\x00\x11"] = 'DV IMA'; + $QuicktimeAudioCodecLookup['MS'."\x00\x55"] = 'Fraunhofer MPEG Layer III'; + $QuicktimeAudioCodecLookup['NONE'] = 'No Encoding'; + $QuicktimeAudioCodecLookup['Qclp'] = 'Qualcomm PureVoice'; + $QuicktimeAudioCodecLookup['QDM2'] = 'QDesign Music 2'; + $QuicktimeAudioCodecLookup['QDMC'] = 'QDesign Music 1'; + $QuicktimeAudioCodecLookup['ratb'] = '8-bit Rate'; + $QuicktimeAudioCodecLookup['ratw'] = '16-bit Rate'; + $QuicktimeAudioCodecLookup['raw '] = 'raw PCM'; + $QuicktimeAudioCodecLookup['sour'] = 'Sound Source'; + $QuicktimeAudioCodecLookup['sowt'] = 'signed/two\'s complement (Little Endian)'; + $QuicktimeAudioCodecLookup['str1'] = 'Iomega MPEG layer II'; + $QuicktimeAudioCodecLookup['str2'] = 'Iomega MPEG *layer II'; + $QuicktimeAudioCodecLookup['str3'] = 'Iomega MPEG **layer II'; + $QuicktimeAudioCodecLookup['str4'] = 'Iomega MPEG ***layer II'; + $QuicktimeAudioCodecLookup['twos'] = 'signed/two\'s complement (Big Endian)'; + $QuicktimeAudioCodecLookup['ulaw'] = 'mu-law 2:1'; + } + return (isset($QuicktimeAudioCodecLookup[$codecid]) ? $QuicktimeAudioCodecLookup[$codecid] : ''); + } + + public function QuicktimeDCOMLookup($compressionid) { + static $QuicktimeDCOMLookup = array(); + if (empty($QuicktimeDCOMLookup)) { + $QuicktimeDCOMLookup['zlib'] = 'ZLib Deflate'; + $QuicktimeDCOMLookup['adec'] = 'Apple Compression'; + } + return (isset($QuicktimeDCOMLookup[$compressionid]) ? $QuicktimeDCOMLookup[$compressionid] : ''); + } + + public function QuicktimeColorNameLookup($colordepthid) { + static $QuicktimeColorNameLookup = array(); + if (empty($QuicktimeColorNameLookup)) { + $QuicktimeColorNameLookup[1] = '2-color (monochrome)'; + $QuicktimeColorNameLookup[2] = '4-color'; + $QuicktimeColorNameLookup[4] = '16-color'; + $QuicktimeColorNameLookup[8] = '256-color'; + $QuicktimeColorNameLookup[16] = 'thousands (16-bit color)'; + $QuicktimeColorNameLookup[24] = 'millions (24-bit color)'; + $QuicktimeColorNameLookup[32] = 'millions+ (32-bit color)'; + $QuicktimeColorNameLookup[33] = 'black & white'; + $QuicktimeColorNameLookup[34] = '4-gray'; + $QuicktimeColorNameLookup[36] = '16-gray'; + $QuicktimeColorNameLookup[40] = '256-gray'; + } + return (isset($QuicktimeColorNameLookup[$colordepthid]) ? $QuicktimeColorNameLookup[$colordepthid] : 'invalid'); + } + + public function QuicktimeSTIKLookup($stik) { + static $QuicktimeSTIKLookup = array(); + if (empty($QuicktimeSTIKLookup)) { + $QuicktimeSTIKLookup[0] = 'Movie'; + $QuicktimeSTIKLookup[1] = 'Normal'; + $QuicktimeSTIKLookup[2] = 'Audiobook'; + $QuicktimeSTIKLookup[5] = 'Whacked Bookmark'; + $QuicktimeSTIKLookup[6] = 'Music Video'; + $QuicktimeSTIKLookup[9] = 'Short Film'; + $QuicktimeSTIKLookup[10] = 'TV Show'; + $QuicktimeSTIKLookup[11] = 'Booklet'; + $QuicktimeSTIKLookup[14] = 'Ringtone'; + $QuicktimeSTIKLookup[21] = 'Podcast'; + } + return (isset($QuicktimeSTIKLookup[$stik]) ? $QuicktimeSTIKLookup[$stik] : 'invalid'); + } + + public function QuicktimeIODSaudioProfileName($audio_profile_id) { + static $QuicktimeIODSaudioProfileNameLookup = array(); + if (empty($QuicktimeIODSaudioProfileNameLookup)) { + $QuicktimeIODSaudioProfileNameLookup = array( + 0x00 => 'ISO Reserved (0x00)', + 0x01 => 'Main Audio Profile @ Level 1', + 0x02 => 'Main Audio Profile @ Level 2', + 0x03 => 'Main Audio Profile @ Level 3', + 0x04 => 'Main Audio Profile @ Level 4', + 0x05 => 'Scalable Audio Profile @ Level 1', + 0x06 => 'Scalable Audio Profile @ Level 2', + 0x07 => 'Scalable Audio Profile @ Level 3', + 0x08 => 'Scalable Audio Profile @ Level 4', + 0x09 => 'Speech Audio Profile @ Level 1', + 0x0A => 'Speech Audio Profile @ Level 2', + 0x0B => 'Synthetic Audio Profile @ Level 1', + 0x0C => 'Synthetic Audio Profile @ Level 2', + 0x0D => 'Synthetic Audio Profile @ Level 3', + 0x0E => 'High Quality Audio Profile @ Level 1', + 0x0F => 'High Quality Audio Profile @ Level 2', + 0x10 => 'High Quality Audio Profile @ Level 3', + 0x11 => 'High Quality Audio Profile @ Level 4', + 0x12 => 'High Quality Audio Profile @ Level 5', + 0x13 => 'High Quality Audio Profile @ Level 6', + 0x14 => 'High Quality Audio Profile @ Level 7', + 0x15 => 'High Quality Audio Profile @ Level 8', + 0x16 => 'Low Delay Audio Profile @ Level 1', + 0x17 => 'Low Delay Audio Profile @ Level 2', + 0x18 => 'Low Delay Audio Profile @ Level 3', + 0x19 => 'Low Delay Audio Profile @ Level 4', + 0x1A => 'Low Delay Audio Profile @ Level 5', + 0x1B => 'Low Delay Audio Profile @ Level 6', + 0x1C => 'Low Delay Audio Profile @ Level 7', + 0x1D => 'Low Delay Audio Profile @ Level 8', + 0x1E => 'Natural Audio Profile @ Level 1', + 0x1F => 'Natural Audio Profile @ Level 2', + 0x20 => 'Natural Audio Profile @ Level 3', + 0x21 => 'Natural Audio Profile @ Level 4', + 0x22 => 'Mobile Audio Internetworking Profile @ Level 1', + 0x23 => 'Mobile Audio Internetworking Profile @ Level 2', + 0x24 => 'Mobile Audio Internetworking Profile @ Level 3', + 0x25 => 'Mobile Audio Internetworking Profile @ Level 4', + 0x26 => 'Mobile Audio Internetworking Profile @ Level 5', + 0x27 => 'Mobile Audio Internetworking Profile @ Level 6', + 0x28 => 'AAC Profile @ Level 1', + 0x29 => 'AAC Profile @ Level 2', + 0x2A => 'AAC Profile @ Level 4', + 0x2B => 'AAC Profile @ Level 5', + 0x2C => 'High Efficiency AAC Profile @ Level 2', + 0x2D => 'High Efficiency AAC Profile @ Level 3', + 0x2E => 'High Efficiency AAC Profile @ Level 4', + 0x2F => 'High Efficiency AAC Profile @ Level 5', + 0xFE => 'Not part of MPEG-4 audio profiles', + 0xFF => 'No audio capability required', + ); + } + return (isset($QuicktimeIODSaudioProfileNameLookup[$audio_profile_id]) ? $QuicktimeIODSaudioProfileNameLookup[$audio_profile_id] : 'ISO Reserved / User Private'); + } + + + public function QuicktimeIODSvideoProfileName($video_profile_id) { + static $QuicktimeIODSvideoProfileNameLookup = array(); + if (empty($QuicktimeIODSvideoProfileNameLookup)) { + $QuicktimeIODSvideoProfileNameLookup = array( + 0x00 => 'Reserved (0x00) Profile', + 0x01 => 'Simple Profile @ Level 1', + 0x02 => 'Simple Profile @ Level 2', + 0x03 => 'Simple Profile @ Level 3', + 0x08 => 'Simple Profile @ Level 0', + 0x10 => 'Simple Scalable Profile @ Level 0', + 0x11 => 'Simple Scalable Profile @ Level 1', + 0x12 => 'Simple Scalable Profile @ Level 2', + 0x15 => 'AVC/H264 Profile', + 0x21 => 'Core Profile @ Level 1', + 0x22 => 'Core Profile @ Level 2', + 0x32 => 'Main Profile @ Level 2', + 0x33 => 'Main Profile @ Level 3', + 0x34 => 'Main Profile @ Level 4', + 0x42 => 'N-bit Profile @ Level 2', + 0x51 => 'Scalable Texture Profile @ Level 1', + 0x61 => 'Simple Face Animation Profile @ Level 1', + 0x62 => 'Simple Face Animation Profile @ Level 2', + 0x63 => 'Simple FBA Profile @ Level 1', + 0x64 => 'Simple FBA Profile @ Level 2', + 0x71 => 'Basic Animated Texture Profile @ Level 1', + 0x72 => 'Basic Animated Texture Profile @ Level 2', + 0x81 => 'Hybrid Profile @ Level 1', + 0x82 => 'Hybrid Profile @ Level 2', + 0x91 => 'Advanced Real Time Simple Profile @ Level 1', + 0x92 => 'Advanced Real Time Simple Profile @ Level 2', + 0x93 => 'Advanced Real Time Simple Profile @ Level 3', + 0x94 => 'Advanced Real Time Simple Profile @ Level 4', + 0xA1 => 'Core Scalable Profile @ Level1', + 0xA2 => 'Core Scalable Profile @ Level2', + 0xA3 => 'Core Scalable Profile @ Level3', + 0xB1 => 'Advanced Coding Efficiency Profile @ Level 1', + 0xB2 => 'Advanced Coding Efficiency Profile @ Level 2', + 0xB3 => 'Advanced Coding Efficiency Profile @ Level 3', + 0xB4 => 'Advanced Coding Efficiency Profile @ Level 4', + 0xC1 => 'Advanced Core Profile @ Level 1', + 0xC2 => 'Advanced Core Profile @ Level 2', + 0xD1 => 'Advanced Scalable Texture @ Level1', + 0xD2 => 'Advanced Scalable Texture @ Level2', + 0xE1 => 'Simple Studio Profile @ Level 1', + 0xE2 => 'Simple Studio Profile @ Level 2', + 0xE3 => 'Simple Studio Profile @ Level 3', + 0xE4 => 'Simple Studio Profile @ Level 4', + 0xE5 => 'Core Studio Profile @ Level 1', + 0xE6 => 'Core Studio Profile @ Level 2', + 0xE7 => 'Core Studio Profile @ Level 3', + 0xE8 => 'Core Studio Profile @ Level 4', + 0xF0 => 'Advanced Simple Profile @ Level 0', + 0xF1 => 'Advanced Simple Profile @ Level 1', + 0xF2 => 'Advanced Simple Profile @ Level 2', + 0xF3 => 'Advanced Simple Profile @ Level 3', + 0xF4 => 'Advanced Simple Profile @ Level 4', + 0xF5 => 'Advanced Simple Profile @ Level 5', + 0xF7 => 'Advanced Simple Profile @ Level 3b', + 0xF8 => 'Fine Granularity Scalable Profile @ Level 0', + 0xF9 => 'Fine Granularity Scalable Profile @ Level 1', + 0xFA => 'Fine Granularity Scalable Profile @ Level 2', + 0xFB => 'Fine Granularity Scalable Profile @ Level 3', + 0xFC => 'Fine Granularity Scalable Profile @ Level 4', + 0xFD => 'Fine Granularity Scalable Profile @ Level 5', + 0xFE => 'Not part of MPEG-4 Visual profiles', + 0xFF => 'No visual capability required', + ); + } + return (isset($QuicktimeIODSvideoProfileNameLookup[$video_profile_id]) ? $QuicktimeIODSvideoProfileNameLookup[$video_profile_id] : 'ISO Reserved Profile'); + } + + + public function QuicktimeContentRatingLookup($rtng) { + static $QuicktimeContentRatingLookup = array(); + if (empty($QuicktimeContentRatingLookup)) { + $QuicktimeContentRatingLookup[0] = 'None'; + $QuicktimeContentRatingLookup[2] = 'Clean'; + $QuicktimeContentRatingLookup[4] = 'Explicit'; + } + return (isset($QuicktimeContentRatingLookup[$rtng]) ? $QuicktimeContentRatingLookup[$rtng] : 'invalid'); + } + + public function QuicktimeStoreAccountTypeLookup($akid) { + static $QuicktimeStoreAccountTypeLookup = array(); + if (empty($QuicktimeStoreAccountTypeLookup)) { + $QuicktimeStoreAccountTypeLookup[0] = 'iTunes'; + $QuicktimeStoreAccountTypeLookup[1] = 'AOL'; + } + return (isset($QuicktimeStoreAccountTypeLookup[$akid]) ? $QuicktimeStoreAccountTypeLookup[$akid] : 'invalid'); + } + + public function QuicktimeStoreFrontCodeLookup($sfid) { + static $QuicktimeStoreFrontCodeLookup = array(); + if (empty($QuicktimeStoreFrontCodeLookup)) { + $QuicktimeStoreFrontCodeLookup[143460] = 'Australia'; + $QuicktimeStoreFrontCodeLookup[143445] = 'Austria'; + $QuicktimeStoreFrontCodeLookup[143446] = 'Belgium'; + $QuicktimeStoreFrontCodeLookup[143455] = 'Canada'; + $QuicktimeStoreFrontCodeLookup[143458] = 'Denmark'; + $QuicktimeStoreFrontCodeLookup[143447] = 'Finland'; + $QuicktimeStoreFrontCodeLookup[143442] = 'France'; + $QuicktimeStoreFrontCodeLookup[143443] = 'Germany'; + $QuicktimeStoreFrontCodeLookup[143448] = 'Greece'; + $QuicktimeStoreFrontCodeLookup[143449] = 'Ireland'; + $QuicktimeStoreFrontCodeLookup[143450] = 'Italy'; + $QuicktimeStoreFrontCodeLookup[143462] = 'Japan'; + $QuicktimeStoreFrontCodeLookup[143451] = 'Luxembourg'; + $QuicktimeStoreFrontCodeLookup[143452] = 'Netherlands'; + $QuicktimeStoreFrontCodeLookup[143461] = 'New Zealand'; + $QuicktimeStoreFrontCodeLookup[143457] = 'Norway'; + $QuicktimeStoreFrontCodeLookup[143453] = 'Portugal'; + $QuicktimeStoreFrontCodeLookup[143454] = 'Spain'; + $QuicktimeStoreFrontCodeLookup[143456] = 'Sweden'; + $QuicktimeStoreFrontCodeLookup[143459] = 'Switzerland'; + $QuicktimeStoreFrontCodeLookup[143444] = 'United Kingdom'; + $QuicktimeStoreFrontCodeLookup[143441] = 'United States'; + } + return (isset($QuicktimeStoreFrontCodeLookup[$sfid]) ? $QuicktimeStoreFrontCodeLookup[$sfid] : 'invalid'); + } + + public function QuicktimeParseNikonNCTG($atom_data) { + // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#NCTG + // Nikon-specific QuickTime tags found in the NCDT atom of MOV videos from some Nikon cameras such as the Coolpix S8000 and D5100 + // Data is stored as records of: + // * 4 bytes record type + // * 2 bytes size of data field type: + // 0x0001 = flag (size field *= 1-byte) + // 0x0002 = char (size field *= 1-byte) + // 0x0003 = DWORD+ (size field *= 2-byte), values are stored CDAB + // 0x0004 = QWORD+ (size field *= 4-byte), values are stored EFGHABCD + // 0x0005 = float (size field *= 8-byte), values are stored aaaabbbb where value is aaaa/bbbb; possibly multiple sets of values appended together + // 0x0007 = bytes (size field *= 1-byte), values are stored as ?????? + // 0x0008 = ????? (size field *= 2-byte), values are stored as ?????? + // * 2 bytes data size field + // * ? bytes data (string data may be null-padded; datestamp fields are in the format "2011:05:25 20:24:15") + // all integers are stored BigEndian + + $NCTGtagName = array( + 0x00000001 => 'Make', + 0x00000002 => 'Model', + 0x00000003 => 'Software', + 0x00000011 => 'CreateDate', + 0x00000012 => 'DateTimeOriginal', + 0x00000013 => 'FrameCount', + 0x00000016 => 'FrameRate', + 0x00000022 => 'FrameWidth', + 0x00000023 => 'FrameHeight', + 0x00000032 => 'AudioChannels', + 0x00000033 => 'AudioBitsPerSample', + 0x00000034 => 'AudioSampleRate', + 0x02000001 => 'MakerNoteVersion', + 0x02000005 => 'WhiteBalance', + 0x0200000b => 'WhiteBalanceFineTune', + 0x0200001e => 'ColorSpace', + 0x02000023 => 'PictureControlData', + 0x02000024 => 'WorldTime', + 0x02000032 => 'UnknownInfo', + 0x02000083 => 'LensType', + 0x02000084 => 'Lens', + ); + + $offset = 0; + $datalength = strlen($atom_data); + $parsed = array(); + while ($offset < $datalength) { +//echo getid3_lib::PrintHexBytes(substr($atom_data, $offset, 4)).'
        '; + $record_type = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 4)); $offset += 4; + $data_size_type = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2)); $offset += 2; + $data_size = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2)); $offset += 2; + switch ($data_size_type) { + case 0x0001: // 0x0001 = flag (size field *= 1-byte) + $data = getid3_lib::BigEndian2Int(substr($atom_data, $offset, $data_size * 1)); + $offset += ($data_size * 1); + break; + case 0x0002: // 0x0002 = char (size field *= 1-byte) + $data = substr($atom_data, $offset, $data_size * 1); + $offset += ($data_size * 1); + $data = rtrim($data, "\x00"); + break; + case 0x0003: // 0x0003 = DWORD+ (size field *= 2-byte), values are stored CDAB + $data = ''; + for ($i = $data_size - 1; $i >= 0; $i--) { + $data .= substr($atom_data, $offset + ($i * 2), 2); + } + $data = getid3_lib::BigEndian2Int($data); + $offset += ($data_size * 2); + break; + case 0x0004: // 0x0004 = QWORD+ (size field *= 4-byte), values are stored EFGHABCD + $data = ''; + for ($i = $data_size - 1; $i >= 0; $i--) { + $data .= substr($atom_data, $offset + ($i * 4), 4); + } + $data = getid3_lib::BigEndian2Int($data); + $offset += ($data_size * 4); + break; + case 0x0005: // 0x0005 = float (size field *= 8-byte), values are stored aaaabbbb where value is aaaa/bbbb; possibly multiple sets of values appended together + $data = array(); + for ($i = 0; $i < $data_size; $i++) { + $numerator = getid3_lib::BigEndian2Int(substr($atom_data, $offset + ($i * 8) + 0, 4)); + $denomninator = getid3_lib::BigEndian2Int(substr($atom_data, $offset + ($i * 8) + 4, 4)); + if ($denomninator == 0) { + $data[$i] = false; + } else { + $data[$i] = (double) $numerator / $denomninator; + } + } + $offset += (8 * $data_size); + if (count($data) == 1) { + $data = $data[0]; + } + break; + case 0x0007: // 0x0007 = bytes (size field *= 1-byte), values are stored as ?????? + $data = substr($atom_data, $offset, $data_size * 1); + $offset += ($data_size * 1); + break; + case 0x0008: // 0x0008 = ????? (size field *= 2-byte), values are stored as ?????? + $data = substr($atom_data, $offset, $data_size * 2); + $offset += ($data_size * 2); + break; + default: +echo 'QuicktimeParseNikonNCTG()::unknown $data_size_type: '.$data_size_type.'
        '; + break 2; + } + + switch ($record_type) { + case 0x00000011: // CreateDate + case 0x00000012: // DateTimeOriginal + $data = strtotime($data); + break; + case 0x0200001e: // ColorSpace + switch ($data) { + case 1: + $data = 'sRGB'; + break; + case 2: + $data = 'Adobe RGB'; + break; + } + break; + case 0x02000023: // PictureControlData + $PictureControlAdjust = array(0=>'default', 1=>'quick', 2=>'full'); + $FilterEffect = array(0x80=>'off', 0x81=>'yellow', 0x82=>'orange', 0x83=>'red', 0x84=>'green', 0xff=>'n/a'); + $ToningEffect = array(0x80=>'b&w', 0x81=>'sepia', 0x82=>'cyanotype', 0x83=>'red', 0x84=>'yellow', 0x85=>'green', 0x86=>'blue-green', 0x87=>'blue', 0x88=>'purple-blue', 0x89=>'red-purple', 0xff=>'n/a'); + $data = array( + 'PictureControlVersion' => substr($data, 0, 4), + 'PictureControlName' => rtrim(substr($data, 4, 20), "\x00"), + 'PictureControlBase' => rtrim(substr($data, 24, 20), "\x00"), + //'?' => substr($data, 44, 4), + 'PictureControlAdjust' => $PictureControlAdjust[ord(substr($data, 48, 1))], + 'PictureControlQuickAdjust' => ord(substr($data, 49, 1)), + 'Sharpness' => ord(substr($data, 50, 1)), + 'Contrast' => ord(substr($data, 51, 1)), + 'Brightness' => ord(substr($data, 52, 1)), + 'Saturation' => ord(substr($data, 53, 1)), + 'HueAdjustment' => ord(substr($data, 54, 1)), + 'FilterEffect' => $FilterEffect[ord(substr($data, 55, 1))], + 'ToningEffect' => $ToningEffect[ord(substr($data, 56, 1))], + 'ToningSaturation' => ord(substr($data, 57, 1)), + ); + break; + case 0x02000024: // WorldTime + // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#WorldTime + // timezone is stored as offset from GMT in minutes + $timezone = getid3_lib::BigEndian2Int(substr($data, 0, 2)); + if ($timezone & 0x8000) { + $timezone = 0 - (0x10000 - $timezone); + } + $timezone /= 60; + + $dst = (bool) getid3_lib::BigEndian2Int(substr($data, 2, 1)); + switch (getid3_lib::BigEndian2Int(substr($data, 3, 1))) { + case 2: + $datedisplayformat = 'D/M/Y'; break; + case 1: + $datedisplayformat = 'M/D/Y'; break; + case 0: + default: + $datedisplayformat = 'Y/M/D'; break; + } + + $data = array('timezone'=>floatval($timezone), 'dst'=>$dst, 'display'=>$datedisplayformat); + break; + case 0x02000083: // LensType + $data = array( + //'_' => $data, + 'mf' => (bool) ($data & 0x01), + 'd' => (bool) ($data & 0x02), + 'g' => (bool) ($data & 0x04), + 'vr' => (bool) ($data & 0x08), + ); + break; + } + $tag_name = (isset($NCTGtagName[$record_type]) ? $NCTGtagName[$record_type] : '0x'.str_pad(dechex($record_type), 8, '0', STR_PAD_LEFT)); + $parsed[$tag_name] = $data; + } + return $parsed; + } + + + public function CopyToAppropriateCommentsSection($keyname, $data, $boxname='') { + static $handyatomtranslatorarray = array(); + if (empty($handyatomtranslatorarray)) { + $handyatomtranslatorarray["\xA9".'cpy'] = 'copyright'; + $handyatomtranslatorarray["\xA9".'day'] = 'creation_date'; // iTunes 4.0 + $handyatomtranslatorarray["\xA9".'dir'] = 'director'; + $handyatomtranslatorarray["\xA9".'ed1'] = 'edit1'; + $handyatomtranslatorarray["\xA9".'ed2'] = 'edit2'; + $handyatomtranslatorarray["\xA9".'ed3'] = 'edit3'; + $handyatomtranslatorarray["\xA9".'ed4'] = 'edit4'; + $handyatomtranslatorarray["\xA9".'ed5'] = 'edit5'; + $handyatomtranslatorarray["\xA9".'ed6'] = 'edit6'; + $handyatomtranslatorarray["\xA9".'ed7'] = 'edit7'; + $handyatomtranslatorarray["\xA9".'ed8'] = 'edit8'; + $handyatomtranslatorarray["\xA9".'ed9'] = 'edit9'; + $handyatomtranslatorarray["\xA9".'fmt'] = 'format'; + $handyatomtranslatorarray["\xA9".'inf'] = 'information'; + $handyatomtranslatorarray["\xA9".'prd'] = 'producer'; + $handyatomtranslatorarray["\xA9".'prf'] = 'performers'; + $handyatomtranslatorarray["\xA9".'req'] = 'system_requirements'; + $handyatomtranslatorarray["\xA9".'src'] = 'source_credit'; + $handyatomtranslatorarray["\xA9".'wrt'] = 'writer'; + + // http://www.geocities.com/xhelmboyx/quicktime/formats/qtm-layout.txt + $handyatomtranslatorarray["\xA9".'nam'] = 'title'; // iTunes 4.0 + $handyatomtranslatorarray["\xA9".'cmt'] = 'comment'; // iTunes 4.0 + $handyatomtranslatorarray["\xA9".'wrn'] = 'warning'; + $handyatomtranslatorarray["\xA9".'hst'] = 'host_computer'; + $handyatomtranslatorarray["\xA9".'mak'] = 'make'; + $handyatomtranslatorarray["\xA9".'mod'] = 'model'; + $handyatomtranslatorarray["\xA9".'PRD'] = 'product'; + $handyatomtranslatorarray["\xA9".'swr'] = 'software'; + $handyatomtranslatorarray["\xA9".'aut'] = 'author'; + $handyatomtranslatorarray["\xA9".'ART'] = 'artist'; + $handyatomtranslatorarray["\xA9".'trk'] = 'track'; + $handyatomtranslatorarray["\xA9".'alb'] = 'album'; // iTunes 4.0 + $handyatomtranslatorarray["\xA9".'com'] = 'comment'; + $handyatomtranslatorarray["\xA9".'gen'] = 'genre'; // iTunes 4.0 + $handyatomtranslatorarray["\xA9".'ope'] = 'composer'; + $handyatomtranslatorarray["\xA9".'url'] = 'url'; + $handyatomtranslatorarray["\xA9".'enc'] = 'encoder'; + + // http://atomicparsley.sourceforge.net/mpeg-4files.html + $handyatomtranslatorarray["\xA9".'art'] = 'artist'; // iTunes 4.0 + $handyatomtranslatorarray['aART'] = 'album_artist'; + $handyatomtranslatorarray['trkn'] = 'track_number'; // iTunes 4.0 + $handyatomtranslatorarray['disk'] = 'disc_number'; // iTunes 4.0 + $handyatomtranslatorarray['gnre'] = 'genre'; // iTunes 4.0 + $handyatomtranslatorarray["\xA9".'too'] = 'encoder'; // iTunes 4.0 + $handyatomtranslatorarray['tmpo'] = 'bpm'; // iTunes 4.0 + $handyatomtranslatorarray['cprt'] = 'copyright'; // iTunes 4.0? + $handyatomtranslatorarray['cpil'] = 'compilation'; // iTunes 4.0 + $handyatomtranslatorarray['covr'] = 'picture'; // iTunes 4.0 + $handyatomtranslatorarray['rtng'] = 'rating'; // iTunes 4.0 + $handyatomtranslatorarray["\xA9".'grp'] = 'grouping'; // iTunes 4.2 + $handyatomtranslatorarray['stik'] = 'stik'; // iTunes 4.9 + $handyatomtranslatorarray['pcst'] = 'podcast'; // iTunes 4.9 + $handyatomtranslatorarray['catg'] = 'category'; // iTunes 4.9 + $handyatomtranslatorarray['keyw'] = 'keyword'; // iTunes 4.9 + $handyatomtranslatorarray['purl'] = 'podcast_url'; // iTunes 4.9 + $handyatomtranslatorarray['egid'] = 'episode_guid'; // iTunes 4.9 + $handyatomtranslatorarray['desc'] = 'description'; // iTunes 5.0 + $handyatomtranslatorarray["\xA9".'lyr'] = 'lyrics'; // iTunes 5.0 + $handyatomtranslatorarray['tvnn'] = 'tv_network_name'; // iTunes 6.0 + $handyatomtranslatorarray['tvsh'] = 'tv_show_name'; // iTunes 6.0 + $handyatomtranslatorarray['tvsn'] = 'tv_season'; // iTunes 6.0 + $handyatomtranslatorarray['tves'] = 'tv_episode'; // iTunes 6.0 + $handyatomtranslatorarray['purd'] = 'purchase_date'; // iTunes 6.0.2 + $handyatomtranslatorarray['pgap'] = 'gapless_playback'; // iTunes 7.0 + + // http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt + + + + // boxnames: + /* + $handyatomtranslatorarray['iTunSMPB'] = 'iTunSMPB'; + $handyatomtranslatorarray['iTunNORM'] = 'iTunNORM'; + $handyatomtranslatorarray['Encoding Params'] = 'Encoding Params'; + $handyatomtranslatorarray['replaygain_track_gain'] = 'replaygain_track_gain'; + $handyatomtranslatorarray['replaygain_track_peak'] = 'replaygain_track_peak'; + $handyatomtranslatorarray['replaygain_track_minmax'] = 'replaygain_track_minmax'; + $handyatomtranslatorarray['MusicIP PUID'] = 'MusicIP PUID'; + $handyatomtranslatorarray['MusicBrainz Artist Id'] = 'MusicBrainz Artist Id'; + $handyatomtranslatorarray['MusicBrainz Album Id'] = 'MusicBrainz Album Id'; + $handyatomtranslatorarray['MusicBrainz Album Artist Id'] = 'MusicBrainz Album Artist Id'; + $handyatomtranslatorarray['MusicBrainz Track Id'] = 'MusicBrainz Track Id'; + $handyatomtranslatorarray['MusicBrainz Disc Id'] = 'MusicBrainz Disc Id'; + + // http://age.hobba.nl/audio/tag_frame_reference.html + $handyatomtranslatorarray['PLAY_COUNTER'] = 'play_counter'; // Foobar2000 - http://www.getid3.org/phpBB3/viewtopic.php?t=1355 + $handyatomtranslatorarray['MEDIATYPE'] = 'mediatype'; // Foobar2000 - http://www.getid3.org/phpBB3/viewtopic.php?t=1355 + */ + } + $info = &$this->getid3->info; + $comment_key = ''; + if ($boxname && ($boxname != $keyname)) { + $comment_key = (isset($handyatomtranslatorarray[$boxname]) ? $handyatomtranslatorarray[$boxname] : $boxname); + } elseif (isset($handyatomtranslatorarray[$keyname])) { + $comment_key = $handyatomtranslatorarray[$keyname]; + } + if ($comment_key) { + if ($comment_key == 'picture') { + if (!is_array($data)) { + $image_mime = ''; + if (preg_match('#^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A#', $data)) { + $image_mime = 'image/png'; + } elseif (preg_match('#^\xFF\xD8\xFF#', $data)) { + $image_mime = 'image/jpeg'; + } elseif (preg_match('#^GIF#', $data)) { + $image_mime = 'image/gif'; + } elseif (preg_match('#^BM#', $data)) { + $image_mime = 'image/bmp'; + } + $data = array('data'=>$data, 'image_mime'=>$image_mime); + } + } + $info['quicktime']['comments'][$comment_key][] = $data; + } + return true; + } + + public function NoNullString($nullterminatedstring) { + // remove the single null terminator on null terminated strings + if (substr($nullterminatedstring, strlen($nullterminatedstring) - 1, 1) === "\x00") { + return substr($nullterminatedstring, 0, strlen($nullterminatedstring) - 1); + } + return $nullterminatedstring; + } + + public function Pascal2String($pascalstring) { + // Pascal strings have 1 unsigned byte at the beginning saying how many chars (1-255) are in the string + return substr($pascalstring, 1); + } + +} \ No newline at end of file diff --git a/wp-includes/ID3/module.audio-video.riff.php b/wp-includes/ID3/module.audio-video.riff.php new file mode 100644 index 0000000..e8ba944 --- /dev/null +++ b/wp-includes/ID3/module.audio-video.riff.php @@ -0,0 +1,2586 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +// also https://github.com/JamesHeinrich/getID3 // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio-video.riff.php // +// module for analyzing RIFF files // +// multiple formats supported by this module: // +// Wave, AVI, AIFF/AIFC, (MP3,AC3)/RIFF, Wavpack v3, 8SVX // +// dependencies: module.audio.mp3.php // +// module.audio.ac3.php // +// module.audio.dts.php // +// /// +///////////////////////////////////////////////////////////////// + +/** +* @todo Parse AC-3/DTS audio inside WAVE correctly +* @todo Rewrite RIFF parser totally +*/ + +getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true); +getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, true); +getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.dts.php', __FILE__, true); + +class getid3_riff extends getid3_handler { + + protected $container = 'riff'; // default + + public function Analyze() { + $info = &$this->getid3->info; + + // initialize these values to an empty array, otherwise they default to NULL + // and you can't append array values to a NULL value + $info['riff'] = array('raw'=>array()); + + // Shortcuts + $thisfile_riff = &$info['riff']; + $thisfile_riff_raw = &$thisfile_riff['raw']; + $thisfile_audio = &$info['audio']; + $thisfile_video = &$info['video']; + $thisfile_audio_dataformat = &$thisfile_audio['dataformat']; + $thisfile_riff_audio = &$thisfile_riff['audio']; + $thisfile_riff_video = &$thisfile_riff['video']; + + $Original['avdataoffset'] = $info['avdataoffset']; + $Original['avdataend'] = $info['avdataend']; + + $this->fseek($info['avdataoffset']); + $RIFFheader = $this->fread(12); + $offset = $this->ftell(); + $RIFFtype = substr($RIFFheader, 0, 4); + $RIFFsize = substr($RIFFheader, 4, 4); + $RIFFsubtype = substr($RIFFheader, 8, 4); + + switch ($RIFFtype) { + + case 'FORM': // AIFF, AIFC + //$info['fileformat'] = 'aiff'; + $this->container = 'aiff'; + $thisfile_riff['header_size'] = $this->EitherEndian2Int($RIFFsize); + $thisfile_riff[$RIFFsubtype] = $this->ParseRIFF($offset, ($offset + $thisfile_riff['header_size'] - 4)); + break; + + case 'RIFF': // AVI, WAV, etc + case 'SDSS': // SDSS is identical to RIFF, just renamed. Used by SmartSound QuickTracks (www.smartsound.com) + case 'RMP3': // RMP3 is identical to RIFF, just renamed. Used by [unknown program] when creating RIFF-MP3s + //$info['fileformat'] = 'riff'; + $this->container = 'riff'; + $thisfile_riff['header_size'] = $this->EitherEndian2Int($RIFFsize); + if ($RIFFsubtype == 'RMP3') { + // RMP3 is identical to WAVE, just renamed. Used by [unknown program] when creating RIFF-MP3s + $RIFFsubtype = 'WAVE'; + } + if ($RIFFsubtype != 'AMV ') { + // AMV files are RIFF-AVI files with parts of the spec deliberately broken, such as chunk size fields hardcoded to zero (because players known in hardware that these fields are always a certain size + // Handled separately in ParseRIFFAMV() + $thisfile_riff[$RIFFsubtype] = $this->ParseRIFF($offset, ($offset + $thisfile_riff['header_size'] - 4)); + } + if (($info['avdataend'] - $info['filesize']) == 1) { + // LiteWave appears to incorrectly *not* pad actual output file + // to nearest WORD boundary so may appear to be short by one + // byte, in which case - skip warning + $info['avdataend'] = $info['filesize']; + } + + $nextRIFFoffset = $Original['avdataoffset'] + 8 + $thisfile_riff['header_size']; // 8 = "RIFF" + 32-bit offset + while ($nextRIFFoffset < min($info['filesize'], $info['avdataend'])) { + try { + $this->fseek($nextRIFFoffset); + } catch (getid3_exception $e) { + if ($e->getCode() == 10) { + //$this->warning('RIFF parser: '.$e->getMessage()); + $this->error('AVI extends beyond '.round(PHP_INT_MAX / 1073741824).'GB and PHP filesystem functions cannot read that far, playtime may be wrong'); + $this->warning('[avdataend] value may be incorrect, multiple AVIX chunks may be present'); + break; + } else { + throw $e; + } + } + $nextRIFFheader = $this->fread(12); + if ($nextRIFFoffset == ($info['avdataend'] - 1)) { + if (substr($nextRIFFheader, 0, 1) == "\x00") { + // RIFF padded to WORD boundary, we're actually already at the end + break; + } + } + $nextRIFFheaderID = substr($nextRIFFheader, 0, 4); + $nextRIFFsize = $this->EitherEndian2Int(substr($nextRIFFheader, 4, 4)); + $nextRIFFtype = substr($nextRIFFheader, 8, 4); + $chunkdata = array(); + $chunkdata['offset'] = $nextRIFFoffset + 8; + $chunkdata['size'] = $nextRIFFsize; + $nextRIFFoffset = $chunkdata['offset'] + $chunkdata['size']; + + switch ($nextRIFFheaderID) { + case 'RIFF': + $chunkdata['chunks'] = $this->ParseRIFF($chunkdata['offset'] + 4, $nextRIFFoffset); + if (!isset($thisfile_riff[$nextRIFFtype])) { + $thisfile_riff[$nextRIFFtype] = array(); + } + $thisfile_riff[$nextRIFFtype][] = $chunkdata; + break; + + case 'AMV ': + unset($info['riff']); + $info['amv'] = $this->ParseRIFFAMV($chunkdata['offset'] + 4, $nextRIFFoffset); + break; + + case 'JUNK': + // ignore + $thisfile_riff[$nextRIFFheaderID][] = $chunkdata; + break; + + case 'IDVX': + $info['divxtag']['comments'] = self::ParseDIVXTAG($this->fread($chunkdata['size'])); + break; + + default: + if ($info['filesize'] == ($chunkdata['offset'] - 8 + 128)) { + $DIVXTAG = $nextRIFFheader.$this->fread(128 - 12); + if (substr($DIVXTAG, -7) == 'DIVXTAG') { + // DIVXTAG is supposed to be inside an IDVX chunk in a LIST chunk, but some bad encoders just slap it on the end of a file + $this->warning('Found wrongly-structured DIVXTAG at offset '.($this->ftell() - 128).', parsing anyway'); + $info['divxtag']['comments'] = self::ParseDIVXTAG($DIVXTAG); + break 2; + } + } + $this->warning('Expecting "RIFF|JUNK|IDVX" at '.$nextRIFFoffset.', found "'.$nextRIFFheaderID.'" ('.getid3_lib::PrintHexBytes($nextRIFFheaderID).') - skipping rest of file'); + break 2; + + } + + } + if ($RIFFsubtype == 'WAVE') { + $thisfile_riff_WAVE = &$thisfile_riff['WAVE']; + } + break; + + default: + $this->error('Cannot parse RIFF (this is maybe not a RIFF / WAV / AVI file?) - expecting "FORM|RIFF|SDSS|RMP3" found "'.$RIFFsubtype.'" instead'); + //unset($info['fileformat']); + return false; + } + + $streamindex = 0; + switch ($RIFFsubtype) { + + // http://en.wikipedia.org/wiki/Wav + case 'WAVE': + $info['fileformat'] = 'wav'; + + if (empty($thisfile_audio['bitrate_mode'])) { + $thisfile_audio['bitrate_mode'] = 'cbr'; + } + if (empty($thisfile_audio_dataformat)) { + $thisfile_audio_dataformat = 'wav'; + } + + if (isset($thisfile_riff_WAVE['data'][0]['offset'])) { + $info['avdataoffset'] = $thisfile_riff_WAVE['data'][0]['offset'] + 8; + $info['avdataend'] = $info['avdataoffset'] + $thisfile_riff_WAVE['data'][0]['size']; + } + if (isset($thisfile_riff_WAVE['fmt '][0]['data'])) { + + $thisfile_riff_audio[$streamindex] = self::parseWAVEFORMATex($thisfile_riff_WAVE['fmt '][0]['data']); + $thisfile_audio['wformattag'] = $thisfile_riff_audio[$streamindex]['raw']['wFormatTag']; + if (!isset($thisfile_riff_audio[$streamindex]['bitrate']) || ($thisfile_riff_audio[$streamindex]['bitrate'] == 0)) { + $info['error'][] = 'Corrupt RIFF file: bitrate_audio == zero'; + return false; + } + $thisfile_riff_raw['fmt '] = $thisfile_riff_audio[$streamindex]['raw']; + unset($thisfile_riff_audio[$streamindex]['raw']); + $thisfile_audio['streams'][$streamindex] = $thisfile_riff_audio[$streamindex]; + + $thisfile_audio = getid3_lib::array_merge_noclobber($thisfile_audio, $thisfile_riff_audio[$streamindex]); + if (substr($thisfile_audio['codec'], 0, strlen('unknown: 0x')) == 'unknown: 0x') { + $info['warning'][] = 'Audio codec = '.$thisfile_audio['codec']; + } + $thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate']; + + if (empty($info['playtime_seconds'])) { // may already be set (e.g. DTS-WAV) + $info['playtime_seconds'] = (float) ((($info['avdataend'] - $info['avdataoffset']) * 8) / $thisfile_audio['bitrate']); + } + + $thisfile_audio['lossless'] = false; + if (isset($thisfile_riff_WAVE['data'][0]['offset']) && isset($thisfile_riff_raw['fmt ']['wFormatTag'])) { + switch ($thisfile_riff_raw['fmt ']['wFormatTag']) { + + case 0x0001: // PCM + $thisfile_audio['lossless'] = true; + break; + + case 0x2000: // AC-3 + $thisfile_audio_dataformat = 'ac3'; + break; + + default: + // do nothing + break; + + } + } + $thisfile_audio['streams'][$streamindex]['wformattag'] = $thisfile_audio['wformattag']; + $thisfile_audio['streams'][$streamindex]['bitrate_mode'] = $thisfile_audio['bitrate_mode']; + $thisfile_audio['streams'][$streamindex]['lossless'] = $thisfile_audio['lossless']; + $thisfile_audio['streams'][$streamindex]['dataformat'] = $thisfile_audio_dataformat; + } + + if (isset($thisfile_riff_WAVE['rgad'][0]['data'])) { + + // shortcuts + $rgadData = &$thisfile_riff_WAVE['rgad'][0]['data']; + $thisfile_riff_raw['rgad'] = array('track'=>array(), 'album'=>array()); + $thisfile_riff_raw_rgad = &$thisfile_riff_raw['rgad']; + $thisfile_riff_raw_rgad_track = &$thisfile_riff_raw_rgad['track']; + $thisfile_riff_raw_rgad_album = &$thisfile_riff_raw_rgad['album']; + + $thisfile_riff_raw_rgad['fPeakAmplitude'] = getid3_lib::LittleEndian2Float(substr($rgadData, 0, 4)); + $thisfile_riff_raw_rgad['nRadioRgAdjust'] = $this->EitherEndian2Int(substr($rgadData, 4, 2)); + $thisfile_riff_raw_rgad['nAudiophileRgAdjust'] = $this->EitherEndian2Int(substr($rgadData, 6, 2)); + + $nRadioRgAdjustBitstring = str_pad(getid3_lib::Dec2Bin($thisfile_riff_raw_rgad['nRadioRgAdjust']), 16, '0', STR_PAD_LEFT); + $nAudiophileRgAdjustBitstring = str_pad(getid3_lib::Dec2Bin($thisfile_riff_raw_rgad['nAudiophileRgAdjust']), 16, '0', STR_PAD_LEFT); + $thisfile_riff_raw_rgad_track['name'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 0, 3)); + $thisfile_riff_raw_rgad_track['originator'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 3, 3)); + $thisfile_riff_raw_rgad_track['signbit'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 6, 1)); + $thisfile_riff_raw_rgad_track['adjustment'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 7, 9)); + $thisfile_riff_raw_rgad_album['name'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 0, 3)); + $thisfile_riff_raw_rgad_album['originator'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 3, 3)); + $thisfile_riff_raw_rgad_album['signbit'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 6, 1)); + $thisfile_riff_raw_rgad_album['adjustment'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 7, 9)); + + $thisfile_riff['rgad']['peakamplitude'] = $thisfile_riff_raw_rgad['fPeakAmplitude']; + if (($thisfile_riff_raw_rgad_track['name'] != 0) && ($thisfile_riff_raw_rgad_track['originator'] != 0)) { + $thisfile_riff['rgad']['track']['name'] = getid3_lib::RGADnameLookup($thisfile_riff_raw_rgad_track['name']); + $thisfile_riff['rgad']['track']['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_riff_raw_rgad_track['originator']); + $thisfile_riff['rgad']['track']['adjustment'] = getid3_lib::RGADadjustmentLookup($thisfile_riff_raw_rgad_track['adjustment'], $thisfile_riff_raw_rgad_track['signbit']); + } + if (($thisfile_riff_raw_rgad_album['name'] != 0) && ($thisfile_riff_raw_rgad_album['originator'] != 0)) { + $thisfile_riff['rgad']['album']['name'] = getid3_lib::RGADnameLookup($thisfile_riff_raw_rgad_album['name']); + $thisfile_riff['rgad']['album']['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_riff_raw_rgad_album['originator']); + $thisfile_riff['rgad']['album']['adjustment'] = getid3_lib::RGADadjustmentLookup($thisfile_riff_raw_rgad_album['adjustment'], $thisfile_riff_raw_rgad_album['signbit']); + } + } + + if (isset($thisfile_riff_WAVE['fact'][0]['data'])) { + $thisfile_riff_raw['fact']['NumberOfSamples'] = $this->EitherEndian2Int(substr($thisfile_riff_WAVE['fact'][0]['data'], 0, 4)); + + // This should be a good way of calculating exact playtime, + // but some sample files have had incorrect number of samples, + // so cannot use this method + + // if (!empty($thisfile_riff_raw['fmt ']['nSamplesPerSec'])) { + // $info['playtime_seconds'] = (float) $thisfile_riff_raw['fact']['NumberOfSamples'] / $thisfile_riff_raw['fmt ']['nSamplesPerSec']; + // } + } + if (!empty($thisfile_riff_raw['fmt ']['nAvgBytesPerSec'])) { + $thisfile_audio['bitrate'] = getid3_lib::CastAsInt($thisfile_riff_raw['fmt ']['nAvgBytesPerSec'] * 8); + } + + if (isset($thisfile_riff_WAVE['bext'][0]['data'])) { + // shortcut + $thisfile_riff_WAVE_bext_0 = &$thisfile_riff_WAVE['bext'][0]; + + $thisfile_riff_WAVE_bext_0['title'] = trim(substr($thisfile_riff_WAVE_bext_0['data'], 0, 256)); + $thisfile_riff_WAVE_bext_0['author'] = trim(substr($thisfile_riff_WAVE_bext_0['data'], 256, 32)); + $thisfile_riff_WAVE_bext_0['reference'] = trim(substr($thisfile_riff_WAVE_bext_0['data'], 288, 32)); + $thisfile_riff_WAVE_bext_0['origin_date'] = substr($thisfile_riff_WAVE_bext_0['data'], 320, 10); + $thisfile_riff_WAVE_bext_0['origin_time'] = substr($thisfile_riff_WAVE_bext_0['data'], 330, 8); + $thisfile_riff_WAVE_bext_0['time_reference'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 338, 8)); + $thisfile_riff_WAVE_bext_0['bwf_version'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 346, 1)); + $thisfile_riff_WAVE_bext_0['reserved'] = substr($thisfile_riff_WAVE_bext_0['data'], 347, 254); + $thisfile_riff_WAVE_bext_0['coding_history'] = explode("\r\n", trim(substr($thisfile_riff_WAVE_bext_0['data'], 601))); + if (preg_match('#^([0-9]{4}).([0-9]{2}).([0-9]{2})$#', $thisfile_riff_WAVE_bext_0['origin_date'], $matches_bext_date)) { + if (preg_match('#^([0-9]{2}).([0-9]{2}).([0-9]{2})$#', $thisfile_riff_WAVE_bext_0['origin_time'], $matches_bext_time)) { + list($dummy, $bext_timestamp['year'], $bext_timestamp['month'], $bext_timestamp['day']) = $matches_bext_date; + list($dummy, $bext_timestamp['hour'], $bext_timestamp['minute'], $bext_timestamp['second']) = $matches_bext_time; + $thisfile_riff_WAVE_bext_0['origin_date_unix'] = gmmktime($bext_timestamp['hour'], $bext_timestamp['minute'], $bext_timestamp['second'], $bext_timestamp['month'], $bext_timestamp['day'], $bext_timestamp['year']); + } else { + $info['warning'][] = 'RIFF.WAVE.BEXT.origin_time is invalid'; + } + } else { + $info['warning'][] = 'RIFF.WAVE.BEXT.origin_date is invalid'; + } + $thisfile_riff['comments']['author'][] = $thisfile_riff_WAVE_bext_0['author']; + $thisfile_riff['comments']['title'][] = $thisfile_riff_WAVE_bext_0['title']; + } + + if (isset($thisfile_riff_WAVE['MEXT'][0]['data'])) { + // shortcut + $thisfile_riff_WAVE_MEXT_0 = &$thisfile_riff_WAVE['MEXT'][0]; + + $thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 0, 2)); + $thisfile_riff_WAVE_MEXT_0['flags']['homogenous'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0001); + if ($thisfile_riff_WAVE_MEXT_0['flags']['homogenous']) { + $thisfile_riff_WAVE_MEXT_0['flags']['padding'] = ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0002) ? false : true; + $thisfile_riff_WAVE_MEXT_0['flags']['22_or_44'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0004); + $thisfile_riff_WAVE_MEXT_0['flags']['free_format'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0008); + + $thisfile_riff_WAVE_MEXT_0['nominal_frame_size'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 2, 2)); + } + $thisfile_riff_WAVE_MEXT_0['anciliary_data_length'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 6, 2)); + $thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 8, 2)); + $thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_left'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x0001); + $thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_free'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x0002); + $thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_right'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x0004); + } + + if (isset($thisfile_riff_WAVE['cart'][0]['data'])) { + // shortcut + $thisfile_riff_WAVE_cart_0 = &$thisfile_riff_WAVE['cart'][0]; + + $thisfile_riff_WAVE_cart_0['version'] = substr($thisfile_riff_WAVE_cart_0['data'], 0, 4); + $thisfile_riff_WAVE_cart_0['title'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 4, 64)); + $thisfile_riff_WAVE_cart_0['artist'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 68, 64)); + $thisfile_riff_WAVE_cart_0['cut_id'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 132, 64)); + $thisfile_riff_WAVE_cart_0['client_id'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 196, 64)); + $thisfile_riff_WAVE_cart_0['category'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 260, 64)); + $thisfile_riff_WAVE_cart_0['classification'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 324, 64)); + $thisfile_riff_WAVE_cart_0['out_cue'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 388, 64)); + $thisfile_riff_WAVE_cart_0['start_date'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 452, 10)); + $thisfile_riff_WAVE_cart_0['start_time'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 462, 8)); + $thisfile_riff_WAVE_cart_0['end_date'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 470, 10)); + $thisfile_riff_WAVE_cart_0['end_time'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 480, 8)); + $thisfile_riff_WAVE_cart_0['producer_app_id'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 488, 64)); + $thisfile_riff_WAVE_cart_0['producer_app_version'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 552, 64)); + $thisfile_riff_WAVE_cart_0['user_defined_text'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 616, 64)); + $thisfile_riff_WAVE_cart_0['zero_db_reference'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_cart_0['data'], 680, 4), true); + for ($i = 0; $i < 8; $i++) { + $thisfile_riff_WAVE_cart_0['post_time'][$i]['usage_fourcc'] = substr($thisfile_riff_WAVE_cart_0['data'], 684 + ($i * 8), 4); + $thisfile_riff_WAVE_cart_0['post_time'][$i]['timer_value'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_cart_0['data'], 684 + ($i * 8) + 4, 4)); + } + $thisfile_riff_WAVE_cart_0['url'] = trim(substr($thisfile_riff_WAVE_cart_0['data'], 748, 1024)); + $thisfile_riff_WAVE_cart_0['tag_text'] = explode("\r\n", trim(substr($thisfile_riff_WAVE_cart_0['data'], 1772))); + + $thisfile_riff['comments']['artist'][] = $thisfile_riff_WAVE_cart_0['artist']; + $thisfile_riff['comments']['title'][] = $thisfile_riff_WAVE_cart_0['title']; + } + + if (isset($thisfile_riff_WAVE['SNDM'][0]['data'])) { + // SoundMiner metadata + + // shortcuts + $thisfile_riff_WAVE_SNDM_0 = &$thisfile_riff_WAVE['SNDM'][0]; + $thisfile_riff_WAVE_SNDM_0_data = &$thisfile_riff_WAVE_SNDM_0['data']; + $SNDM_startoffset = 0; + $SNDM_endoffset = $thisfile_riff_WAVE_SNDM_0['size']; + + while ($SNDM_startoffset < $SNDM_endoffset) { + $SNDM_thisTagOffset = 0; + $SNDM_thisTagSize = getid3_lib::BigEndian2Int(substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 4)); + $SNDM_thisTagOffset += 4; + $SNDM_thisTagKey = substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 4); + $SNDM_thisTagOffset += 4; + $SNDM_thisTagDataSize = getid3_lib::BigEndian2Int(substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 2)); + $SNDM_thisTagOffset += 2; + $SNDM_thisTagDataFlags = getid3_lib::BigEndian2Int(substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, 2)); + $SNDM_thisTagOffset += 2; + $SNDM_thisTagDataText = substr($thisfile_riff_WAVE_SNDM_0_data, $SNDM_startoffset + $SNDM_thisTagOffset, $SNDM_thisTagDataSize); + $SNDM_thisTagOffset += $SNDM_thisTagDataSize; + + if ($SNDM_thisTagSize != (4 + 4 + 2 + 2 + $SNDM_thisTagDataSize)) { + $info['warning'][] = 'RIFF.WAVE.SNDM.data contains tag not expected length (expected: '.$SNDM_thisTagSize.', found: '.(4 + 4 + 2 + 2 + $SNDM_thisTagDataSize).') at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')'; + break; + } elseif ($SNDM_thisTagSize <= 0) { + $info['warning'][] = 'RIFF.WAVE.SNDM.data contains zero-size tag at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')'; + break; + } + $SNDM_startoffset += $SNDM_thisTagSize; + + $thisfile_riff_WAVE_SNDM_0['parsed_raw'][$SNDM_thisTagKey] = $SNDM_thisTagDataText; + if ($parsedkey = self::waveSNDMtagLookup($SNDM_thisTagKey)) { + $thisfile_riff_WAVE_SNDM_0['parsed'][$parsedkey] = $SNDM_thisTagDataText; + } else { + $info['warning'][] = 'RIFF.WAVE.SNDM contains unknown tag "'.$SNDM_thisTagKey.'" at offset '.$SNDM_startoffset.' (file offset '.($thisfile_riff_WAVE_SNDM_0['offset'] + $SNDM_startoffset).')'; + } + } + + $tagmapping = array( + 'tracktitle'=>'title', + 'category' =>'genre', + 'cdtitle' =>'album', + 'tracktitle'=>'title', + ); + foreach ($tagmapping as $fromkey => $tokey) { + if (isset($thisfile_riff_WAVE_SNDM_0['parsed'][$fromkey])) { + $thisfile_riff['comments'][$tokey][] = $thisfile_riff_WAVE_SNDM_0['parsed'][$fromkey]; + } + } + } + + if (isset($thisfile_riff_WAVE['iXML'][0]['data'])) { + // requires functions simplexml_load_string and get_object_vars + if ($parsedXML = getid3_lib::XML2array($thisfile_riff_WAVE['iXML'][0]['data'])) { + $thisfile_riff_WAVE['iXML'][0]['parsed'] = $parsedXML; + if (isset($parsedXML['SPEED']['MASTER_SPEED'])) { + @list($numerator, $denominator) = explode('/', $parsedXML['SPEED']['MASTER_SPEED']); + $thisfile_riff_WAVE['iXML'][0]['master_speed'] = $numerator / ($denominator ? $denominator : 1000); + } + if (isset($parsedXML['SPEED']['TIMECODE_RATE'])) { + @list($numerator, $denominator) = explode('/', $parsedXML['SPEED']['TIMECODE_RATE']); + $thisfile_riff_WAVE['iXML'][0]['timecode_rate'] = $numerator / ($denominator ? $denominator : 1000); + } + if (isset($parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_LO']) && !empty($parsedXML['SPEED']['TIMESTAMP_SAMPLE_RATE']) && !empty($thisfile_riff_WAVE['iXML'][0]['timecode_rate'])) { + $samples_since_midnight = floatval(ltrim($parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_HI'].$parsedXML['SPEED']['TIMESTAMP_SAMPLES_SINCE_MIDNIGHT_LO'], '0')); + $thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] = $samples_since_midnight / $parsedXML['SPEED']['TIMESTAMP_SAMPLE_RATE']; + $h = floor( $thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] / 3600); + $m = floor(($thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - ($h * 3600)) / 60); + $s = floor( $thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - ($h * 3600) - ($m * 60)); + $f = ($thisfile_riff_WAVE['iXML'][0]['timecode_seconds'] - ($h * 3600) - ($m * 60) - $s) * $thisfile_riff_WAVE['iXML'][0]['timecode_rate']; + $thisfile_riff_WAVE['iXML'][0]['timecode_string'] = sprintf('%02d:%02d:%02d:%05.2f', $h, $m, $s, $f); + $thisfile_riff_WAVE['iXML'][0]['timecode_string_round'] = sprintf('%02d:%02d:%02d:%02d', $h, $m, $s, round($f)); + } + unset($parsedXML); + } + } + + + + if (!isset($thisfile_audio['bitrate']) && isset($thisfile_riff_audio[$streamindex]['bitrate'])) { + $thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate']; + $info['playtime_seconds'] = (float) ((($info['avdataend'] - $info['avdataoffset']) * 8) / $thisfile_audio['bitrate']); + } + + if (!empty($info['wavpack'])) { + $thisfile_audio_dataformat = 'wavpack'; + $thisfile_audio['bitrate_mode'] = 'vbr'; + $thisfile_audio['encoder'] = 'WavPack v'.$info['wavpack']['version']; + + // Reset to the way it was - RIFF parsing will have messed this up + $info['avdataend'] = $Original['avdataend']; + $thisfile_audio['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; + + $this->fseek($info['avdataoffset'] - 44); + $RIFFdata = $this->fread(44); + $OrignalRIFFheaderSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 4, 4)) + 8; + $OrignalRIFFdataSize = getid3_lib::LittleEndian2Int(substr($RIFFdata, 40, 4)) + 44; + + if ($OrignalRIFFheaderSize > $OrignalRIFFdataSize) { + $info['avdataend'] -= ($OrignalRIFFheaderSize - $OrignalRIFFdataSize); + $this->fseek($info['avdataend']); + $RIFFdata .= $this->fread($OrignalRIFFheaderSize - $OrignalRIFFdataSize); + } + + // move the data chunk after all other chunks (if any) + // so that the RIFF parser doesn't see EOF when trying + // to skip over the data chunk + $RIFFdata = substr($RIFFdata, 0, 36).substr($RIFFdata, 44).substr($RIFFdata, 36, 8); + $getid3_riff = new getid3_riff($this->getid3); + $getid3_riff->ParseRIFFdata($RIFFdata); + unset($getid3_riff); + } + + if (isset($thisfile_riff_raw['fmt ']['wFormatTag'])) { + switch ($thisfile_riff_raw['fmt ']['wFormatTag']) { + case 0x0001: // PCM + if (!empty($info['ac3'])) { + // Dolby Digital WAV files masquerade as PCM-WAV, but they're not + $thisfile_audio['wformattag'] = 0x2000; + $thisfile_audio['codec'] = self::wFormatTagLookup($thisfile_audio['wformattag']); + $thisfile_audio['lossless'] = false; + $thisfile_audio['bitrate'] = $info['ac3']['bitrate']; + $thisfile_audio['sample_rate'] = $info['ac3']['sample_rate']; + } + if (!empty($info['dts'])) { + // Dolby DTS files masquerade as PCM-WAV, but they're not + $thisfile_audio['wformattag'] = 0x2001; + $thisfile_audio['codec'] = self::wFormatTagLookup($thisfile_audio['wformattag']); + $thisfile_audio['lossless'] = false; + $thisfile_audio['bitrate'] = $info['dts']['bitrate']; + $thisfile_audio['sample_rate'] = $info['dts']['sample_rate']; + } + break; + case 0x08AE: // ClearJump LiteWave + $thisfile_audio['bitrate_mode'] = 'vbr'; + $thisfile_audio_dataformat = 'litewave'; + + //typedef struct tagSLwFormat { + // WORD m_wCompFormat; // low byte defines compression method, high byte is compression flags + // DWORD m_dwScale; // scale factor for lossy compression + // DWORD m_dwBlockSize; // number of samples in encoded blocks + // WORD m_wQuality; // alias for the scale factor + // WORD m_wMarkDistance; // distance between marks in bytes + // WORD m_wReserved; + // + // //following paramters are ignored if CF_FILESRC is not set + // DWORD m_dwOrgSize; // original file size in bytes + // WORD m_bFactExists; // indicates if 'fact' chunk exists in the original file + // DWORD m_dwRiffChunkSize; // riff chunk size in the original file + // + // PCMWAVEFORMAT m_OrgWf; // original wave format + // }SLwFormat, *PSLwFormat; + + // shortcut + $thisfile_riff['litewave']['raw'] = array(); + $riff_litewave = &$thisfile_riff['litewave']; + $riff_litewave_raw = &$riff_litewave['raw']; + + $flags = array( + 'compression_method' => 1, + 'compression_flags' => 1, + 'm_dwScale' => 4, + 'm_dwBlockSize' => 4, + 'm_wQuality' => 2, + 'm_wMarkDistance' => 2, + 'm_wReserved' => 2, + 'm_dwOrgSize' => 4, + 'm_bFactExists' => 2, + 'm_dwRiffChunkSize' => 4, + ); + $litewave_offset = 18; + foreach ($flags as $flag => $length) { + $riff_litewave_raw[$flag] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], $litewave_offset, $length)); + $litewave_offset += $length; + } + + //$riff_litewave['quality_factor'] = intval(round((2000 - $riff_litewave_raw['m_dwScale']) / 20)); + $riff_litewave['quality_factor'] = $riff_litewave_raw['m_wQuality']; + + $riff_litewave['flags']['raw_source'] = ($riff_litewave_raw['compression_flags'] & 0x01) ? false : true; + $riff_litewave['flags']['vbr_blocksize'] = ($riff_litewave_raw['compression_flags'] & 0x02) ? false : true; + $riff_litewave['flags']['seekpoints'] = (bool) ($riff_litewave_raw['compression_flags'] & 0x04); + + $thisfile_audio['lossless'] = (($riff_litewave_raw['m_wQuality'] == 100) ? true : false); + $thisfile_audio['encoder_options'] = '-q'.$riff_litewave['quality_factor']; + break; + + default: + break; + } + } + if ($info['avdataend'] > $info['filesize']) { + switch (!empty($thisfile_audio_dataformat) ? $thisfile_audio_dataformat : '') { + case 'wavpack': // WavPack + case 'lpac': // LPAC + case 'ofr': // OptimFROG + case 'ofs': // OptimFROG DualStream + // lossless compressed audio formats that keep original RIFF headers - skip warning + break; + + case 'litewave': + if (($info['avdataend'] - $info['filesize']) == 1) { + // LiteWave appears to incorrectly *not* pad actual output file + // to nearest WORD boundary so may appear to be short by one + // byte, in which case - skip warning + } else { + // Short by more than one byte, throw warning + $info['warning'][] = 'Probably truncated file - expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' (short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($info['filesize'] - $info['avdataoffset'])).' bytes)'; + $info['avdataend'] = $info['filesize']; + } + break; + + default: + if ((($info['avdataend'] - $info['filesize']) == 1) && (($thisfile_riff[$RIFFsubtype]['data'][0]['size'] % 2) == 0) && ((($info['filesize'] - $info['avdataoffset']) % 2) == 1)) { + // output file appears to be incorrectly *not* padded to nearest WORD boundary + // Output less severe warning + $info['warning'][] = 'File should probably be padded to nearest WORD boundary, but it is not (expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' therefore short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($info['filesize'] - $info['avdataoffset'])).' bytes)'; + $info['avdataend'] = $info['filesize']; + } else { + // Short by more than one byte, throw warning + $info['warning'][] = 'Probably truncated file - expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' (short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($info['filesize'] - $info['avdataoffset'])).' bytes)'; + $info['avdataend'] = $info['filesize']; + } + break; + } + } + if (!empty($info['mpeg']['audio']['LAME']['audio_bytes'])) { + if ((($info['avdataend'] - $info['avdataoffset']) - $info['mpeg']['audio']['LAME']['audio_bytes']) == 1) { + $info['avdataend']--; + $info['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored'; + } + } + if (isset($thisfile_audio_dataformat) && ($thisfile_audio_dataformat == 'ac3')) { + unset($thisfile_audio['bits_per_sample']); + if (!empty($info['ac3']['bitrate']) && ($info['ac3']['bitrate'] != $thisfile_audio['bitrate'])) { + $thisfile_audio['bitrate'] = $info['ac3']['bitrate']; + } + } + break; + + // http://en.wikipedia.org/wiki/Audio_Video_Interleave + case 'AVI ': + $info['fileformat'] = 'avi'; + $info['mime_type'] = 'video/avi'; + + $thisfile_video['bitrate_mode'] = 'vbr'; // maybe not, but probably + $thisfile_video['dataformat'] = 'avi'; + + if (isset($thisfile_riff[$RIFFsubtype]['movi']['offset'])) { + $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['movi']['offset'] + 8; + if (isset($thisfile_riff['AVIX'])) { + $info['avdataend'] = $thisfile_riff['AVIX'][(count($thisfile_riff['AVIX']) - 1)]['chunks']['movi']['offset'] + $thisfile_riff['AVIX'][(count($thisfile_riff['AVIX']) - 1)]['chunks']['movi']['size']; + } else { + $info['avdataend'] = $thisfile_riff['AVI ']['movi']['offset'] + $thisfile_riff['AVI ']['movi']['size']; + } + if ($info['avdataend'] > $info['filesize']) { + $info['warning'][] = 'Probably truncated file - expecting '.($info['avdataend'] - $info['avdataoffset']).' bytes of data, only found '.($info['filesize'] - $info['avdataoffset']).' (short by '.($info['avdataend'] - $info['filesize']).' bytes)'; + $info['avdataend'] = $info['filesize']; + } + } + + if (isset($thisfile_riff['AVI ']['hdrl']['strl']['indx'])) { + //$bIndexType = array( + // 0x00 => 'AVI_INDEX_OF_INDEXES', + // 0x01 => 'AVI_INDEX_OF_CHUNKS', + // 0x80 => 'AVI_INDEX_IS_DATA', + //); + //$bIndexSubtype = array( + // 0x01 => array( + // 0x01 => 'AVI_INDEX_2FIELD', + // ), + //); + foreach ($thisfile_riff['AVI ']['hdrl']['strl']['indx'] as $streamnumber => $steamdataarray) { + $ahsisd = &$thisfile_riff['AVI ']['hdrl']['strl']['indx'][$streamnumber]['data']; + + $thisfile_riff_raw['indx'][$streamnumber]['wLongsPerEntry'] = $this->EitherEndian2Int(substr($ahsisd, 0, 2)); + $thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType'] = $this->EitherEndian2Int(substr($ahsisd, 2, 1)); + $thisfile_riff_raw['indx'][$streamnumber]['bIndexType'] = $this->EitherEndian2Int(substr($ahsisd, 3, 1)); + $thisfile_riff_raw['indx'][$streamnumber]['nEntriesInUse'] = $this->EitherEndian2Int(substr($ahsisd, 4, 4)); + $thisfile_riff_raw['indx'][$streamnumber]['dwChunkId'] = substr($ahsisd, 8, 4); + $thisfile_riff_raw['indx'][$streamnumber]['dwReserved'] = $this->EitherEndian2Int(substr($ahsisd, 12, 4)); + + //$thisfile_riff_raw['indx'][$streamnumber]['bIndexType_name'] = $bIndexType[$thisfile_riff_raw['indx'][$streamnumber]['bIndexType']]; + //$thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType_name'] = $bIndexSubtype[$thisfile_riff_raw['indx'][$streamnumber]['bIndexType']][$thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType']]; + + unset($ahsisd); + } + } + if (isset($thisfile_riff['AVI ']['hdrl']['avih'][$streamindex]['data'])) { + $avihData = $thisfile_riff['AVI ']['hdrl']['avih'][$streamindex]['data']; + + // shortcut + $thisfile_riff_raw['avih'] = array(); + $thisfile_riff_raw_avih = &$thisfile_riff_raw['avih']; + + $thisfile_riff_raw_avih['dwMicroSecPerFrame'] = $this->EitherEndian2Int(substr($avihData, 0, 4)); // frame display rate (or 0L) + if ($thisfile_riff_raw_avih['dwMicroSecPerFrame'] == 0) { + $info['error'][] = 'Corrupt RIFF file: avih.dwMicroSecPerFrame == zero'; + return false; + } + + $flags = array( + 'dwMaxBytesPerSec', // max. transfer rate + 'dwPaddingGranularity', // pad to multiples of this size; normally 2K. + 'dwFlags', // the ever-present flags + 'dwTotalFrames', // # frames in file + 'dwInitialFrames', // + 'dwStreams', // + 'dwSuggestedBufferSize', // + 'dwWidth', // + 'dwHeight', // + 'dwScale', // + 'dwRate', // + 'dwStart', // + 'dwLength', // + ); + $avih_offset = 4; + foreach ($flags as $flag) { + $thisfile_riff_raw_avih[$flag] = $this->EitherEndian2Int(substr($avihData, $avih_offset, 4)); + $avih_offset += 4; + } + + $flags = array( + 'hasindex' => 0x00000010, + 'mustuseindex' => 0x00000020, + 'interleaved' => 0x00000100, + 'trustcktype' => 0x00000800, + 'capturedfile' => 0x00010000, + 'copyrighted' => 0x00020010, + ); + foreach ($flags as $flag => $value) { + $thisfile_riff_raw_avih['flags'][$flag] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & $value); + } + + // shortcut + $thisfile_riff_video[$streamindex] = array(); + $thisfile_riff_video_current = &$thisfile_riff_video[$streamindex]; + + if ($thisfile_riff_raw_avih['dwWidth'] > 0) { + $thisfile_riff_video_current['frame_width'] = $thisfile_riff_raw_avih['dwWidth']; + $thisfile_video['resolution_x'] = $thisfile_riff_video_current['frame_width']; + } + if ($thisfile_riff_raw_avih['dwHeight'] > 0) { + $thisfile_riff_video_current['frame_height'] = $thisfile_riff_raw_avih['dwHeight']; + $thisfile_video['resolution_y'] = $thisfile_riff_video_current['frame_height']; + } + if ($thisfile_riff_raw_avih['dwTotalFrames'] > 0) { + $thisfile_riff_video_current['total_frames'] = $thisfile_riff_raw_avih['dwTotalFrames']; + $thisfile_video['total_frames'] = $thisfile_riff_video_current['total_frames']; + } + + $thisfile_riff_video_current['frame_rate'] = round(1000000 / $thisfile_riff_raw_avih['dwMicroSecPerFrame'], 3); + $thisfile_video['frame_rate'] = $thisfile_riff_video_current['frame_rate']; + } + if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strh'][0]['data'])) { + if (is_array($thisfile_riff['AVI ']['hdrl']['strl']['strh'])) { + for ($i = 0; $i < count($thisfile_riff['AVI ']['hdrl']['strl']['strh']); $i++) { + if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strh'][$i]['data'])) { + $strhData = $thisfile_riff['AVI ']['hdrl']['strl']['strh'][$i]['data']; + $strhfccType = substr($strhData, 0, 4); + + if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strf'][$i]['data'])) { + $strfData = $thisfile_riff['AVI ']['hdrl']['strl']['strf'][$i]['data']; + + // shortcut + $thisfile_riff_raw_strf_strhfccType_streamindex = &$thisfile_riff_raw['strf'][$strhfccType][$streamindex]; + + switch ($strhfccType) { + case 'auds': + $thisfile_audio['bitrate_mode'] = 'cbr'; + $thisfile_audio_dataformat = 'wav'; + if (isset($thisfile_riff_audio) && is_array($thisfile_riff_audio)) { + $streamindex = count($thisfile_riff_audio); + } + + $thisfile_riff_audio[$streamindex] = self::parseWAVEFORMATex($strfData); + $thisfile_audio['wformattag'] = $thisfile_riff_audio[$streamindex]['raw']['wFormatTag']; + + // shortcut + $thisfile_audio['streams'][$streamindex] = $thisfile_riff_audio[$streamindex]; + $thisfile_audio_streams_currentstream = &$thisfile_audio['streams'][$streamindex]; + + if ($thisfile_audio_streams_currentstream['bits_per_sample'] == 0) { + unset($thisfile_audio_streams_currentstream['bits_per_sample']); + } + $thisfile_audio_streams_currentstream['wformattag'] = $thisfile_audio_streams_currentstream['raw']['wFormatTag']; + unset($thisfile_audio_streams_currentstream['raw']); + + // shortcut + $thisfile_riff_raw['strf'][$strhfccType][$streamindex] = $thisfile_riff_audio[$streamindex]['raw']; + + unset($thisfile_riff_audio[$streamindex]['raw']); + $thisfile_audio = getid3_lib::array_merge_noclobber($thisfile_audio, $thisfile_riff_audio[$streamindex]); + + $thisfile_audio['lossless'] = false; + switch ($thisfile_riff_raw_strf_strhfccType_streamindex['wFormatTag']) { + case 0x0001: // PCM + $thisfile_audio_dataformat = 'wav'; + $thisfile_audio['lossless'] = true; + break; + + case 0x0050: // MPEG Layer 2 or Layer 1 + $thisfile_audio_dataformat = 'mp2'; // Assume Layer-2 + break; + + case 0x0055: // MPEG Layer 3 + $thisfile_audio_dataformat = 'mp3'; + break; + + case 0x00FF: // AAC + $thisfile_audio_dataformat = 'aac'; + break; + + case 0x0161: // Windows Media v7 / v8 / v9 + case 0x0162: // Windows Media Professional v9 + case 0x0163: // Windows Media Lossess v9 + $thisfile_audio_dataformat = 'wma'; + break; + + case 0x2000: // AC-3 + $thisfile_audio_dataformat = 'ac3'; + break; + + case 0x2001: // DTS + $thisfile_audio_dataformat = 'dts'; + break; + + default: + $thisfile_audio_dataformat = 'wav'; + break; + } + $thisfile_audio_streams_currentstream['dataformat'] = $thisfile_audio_dataformat; + $thisfile_audio_streams_currentstream['lossless'] = $thisfile_audio['lossless']; + $thisfile_audio_streams_currentstream['bitrate_mode'] = $thisfile_audio['bitrate_mode']; + break; + + + case 'iavs': + case 'vids': + // shortcut + $thisfile_riff_raw['strh'][$i] = array(); + $thisfile_riff_raw_strh_current = &$thisfile_riff_raw['strh'][$i]; + + $thisfile_riff_raw_strh_current['fccType'] = substr($strhData, 0, 4); // same as $strhfccType; + $thisfile_riff_raw_strh_current['fccHandler'] = substr($strhData, 4, 4); + $thisfile_riff_raw_strh_current['dwFlags'] = $this->EitherEndian2Int(substr($strhData, 8, 4)); // Contains AVITF_* flags + $thisfile_riff_raw_strh_current['wPriority'] = $this->EitherEndian2Int(substr($strhData, 12, 2)); + $thisfile_riff_raw_strh_current['wLanguage'] = $this->EitherEndian2Int(substr($strhData, 14, 2)); + $thisfile_riff_raw_strh_current['dwInitialFrames'] = $this->EitherEndian2Int(substr($strhData, 16, 4)); + $thisfile_riff_raw_strh_current['dwScale'] = $this->EitherEndian2Int(substr($strhData, 20, 4)); + $thisfile_riff_raw_strh_current['dwRate'] = $this->EitherEndian2Int(substr($strhData, 24, 4)); + $thisfile_riff_raw_strh_current['dwStart'] = $this->EitherEndian2Int(substr($strhData, 28, 4)); + $thisfile_riff_raw_strh_current['dwLength'] = $this->EitherEndian2Int(substr($strhData, 32, 4)); + $thisfile_riff_raw_strh_current['dwSuggestedBufferSize'] = $this->EitherEndian2Int(substr($strhData, 36, 4)); + $thisfile_riff_raw_strh_current['dwQuality'] = $this->EitherEndian2Int(substr($strhData, 40, 4)); + $thisfile_riff_raw_strh_current['dwSampleSize'] = $this->EitherEndian2Int(substr($strhData, 44, 4)); + $thisfile_riff_raw_strh_current['rcFrame'] = $this->EitherEndian2Int(substr($strhData, 48, 4)); + + $thisfile_riff_video_current['codec'] = self::fourccLookup($thisfile_riff_raw_strh_current['fccHandler']); + $thisfile_video['fourcc'] = $thisfile_riff_raw_strh_current['fccHandler']; + if (!$thisfile_riff_video_current['codec'] && isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) && self::fourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) { + $thisfile_riff_video_current['codec'] = self::fourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']); + $thisfile_video['fourcc'] = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']; + } + $thisfile_video['codec'] = $thisfile_riff_video_current['codec']; + $thisfile_video['pixel_aspect_ratio'] = (float) 1; + switch ($thisfile_riff_raw_strh_current['fccHandler']) { + case 'HFYU': // Huffman Lossless Codec + case 'IRAW': // Intel YUV Uncompressed + case 'YUY2': // Uncompressed YUV 4:2:2 + $thisfile_video['lossless'] = true; + break; + + default: + $thisfile_video['lossless'] = false; + break; + } + + switch ($strhfccType) { + case 'vids': + $thisfile_riff_raw_strf_strhfccType_streamindex = self::ParseBITMAPINFOHEADER(substr($strfData, 0, 40), ($this->container == 'riff')); + $thisfile_video['bits_per_sample'] = $thisfile_riff_raw_strf_strhfccType_streamindex['biBitCount']; + + if ($thisfile_riff_video_current['codec'] == 'DV') { + $thisfile_riff_video_current['dv_type'] = 2; + } + break; + + case 'iavs': + $thisfile_riff_video_current['dv_type'] = 1; + break; + } + break; + + default: + $info['warning'][] = 'Unhandled fccType for stream ('.$i.'): "'.$strhfccType.'"'; + break; + + } + } + } + + if (isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) { + + $thisfile_video['fourcc'] = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']; + if (self::fourccLookup($thisfile_video['fourcc'])) { + $thisfile_riff_video_current['codec'] = self::fourccLookup($thisfile_video['fourcc']); + $thisfile_video['codec'] = $thisfile_riff_video_current['codec']; + } + + switch ($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) { + case 'HFYU': // Huffman Lossless Codec + case 'IRAW': // Intel YUV Uncompressed + case 'YUY2': // Uncompressed YUV 4:2:2 + $thisfile_video['lossless'] = true; + //$thisfile_video['bits_per_sample'] = 24; + break; + + default: + $thisfile_video['lossless'] = false; + //$thisfile_video['bits_per_sample'] = 24; + break; + } + + } + } + } + } + break; + + + case 'AMV ': + $info['fileformat'] = 'amv'; + $info['mime_type'] = 'video/amv'; + + $thisfile_video['bitrate_mode'] = 'vbr'; // it's MJPEG, presumably contant-quality encoding, thereby VBR + $thisfile_video['dataformat'] = 'mjpeg'; + $thisfile_video['codec'] = 'mjpeg'; + $thisfile_video['lossless'] = false; + $thisfile_video['bits_per_sample'] = 24; + + $thisfile_audio['dataformat'] = 'adpcm'; + $thisfile_audio['lossless'] = false; + break; + + + // http://en.wikipedia.org/wiki/CD-DA + case 'CDDA': + $info['fileformat'] = 'cda'; + unset($info['mime_type']); + + $thisfile_audio_dataformat = 'cda'; + + $info['avdataoffset'] = 44; + + if (isset($thisfile_riff['CDDA']['fmt '][0]['data'])) { + // shortcut + $thisfile_riff_CDDA_fmt_0 = &$thisfile_riff['CDDA']['fmt '][0]; + + $thisfile_riff_CDDA_fmt_0['unknown1'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 0, 2)); + $thisfile_riff_CDDA_fmt_0['track_num'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 2, 2)); + $thisfile_riff_CDDA_fmt_0['disc_id'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 4, 4)); + $thisfile_riff_CDDA_fmt_0['start_offset_frame'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 8, 4)); + $thisfile_riff_CDDA_fmt_0['playtime_frames'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 12, 4)); + $thisfile_riff_CDDA_fmt_0['unknown6'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 16, 4)); + $thisfile_riff_CDDA_fmt_0['unknown7'] = $this->EitherEndian2Int(substr($thisfile_riff_CDDA_fmt_0['data'], 20, 4)); + + $thisfile_riff_CDDA_fmt_0['start_offset_seconds'] = (float) $thisfile_riff_CDDA_fmt_0['start_offset_frame'] / 75; + $thisfile_riff_CDDA_fmt_0['playtime_seconds'] = (float) $thisfile_riff_CDDA_fmt_0['playtime_frames'] / 75; + $info['comments']['track'] = $thisfile_riff_CDDA_fmt_0['track_num']; + $info['playtime_seconds'] = $thisfile_riff_CDDA_fmt_0['playtime_seconds']; + + // hardcoded data for CD-audio + $thisfile_audio['lossless'] = true; + $thisfile_audio['sample_rate'] = 44100; + $thisfile_audio['channels'] = 2; + $thisfile_audio['bits_per_sample'] = 16; + $thisfile_audio['bitrate'] = $thisfile_audio['sample_rate'] * $thisfile_audio['channels'] * $thisfile_audio['bits_per_sample']; + $thisfile_audio['bitrate_mode'] = 'cbr'; + } + break; + + // http://en.wikipedia.org/wiki/AIFF + case 'AIFF': + case 'AIFC': + $info['fileformat'] = 'aiff'; + $info['mime_type'] = 'audio/x-aiff'; + + $thisfile_audio['bitrate_mode'] = 'cbr'; + $thisfile_audio_dataformat = 'aiff'; + $thisfile_audio['lossless'] = true; + + if (isset($thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'])) { + $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'] + 8; + $info['avdataend'] = $info['avdataoffset'] + $thisfile_riff[$RIFFsubtype]['SSND'][0]['size']; + if ($info['avdataend'] > $info['filesize']) { + if (($info['avdataend'] == ($info['filesize'] + 1)) && (($info['filesize'] % 2) == 1)) { + // structures rounded to 2-byte boundary, but dumb encoders + // forget to pad end of file to make this actually work + } else { + $info['warning'][] = 'Probable truncated AIFF file: expecting '.$thisfile_riff[$RIFFsubtype]['SSND'][0]['size'].' bytes of audio data, only '.($info['filesize'] - $info['avdataoffset']).' bytes found'; + } + $info['avdataend'] = $info['filesize']; + } + } + + if (isset($thisfile_riff[$RIFFsubtype]['COMM'][0]['data'])) { + + // shortcut + $thisfile_riff_RIFFsubtype_COMM_0_data = &$thisfile_riff[$RIFFsubtype]['COMM'][0]['data']; + + $thisfile_riff_audio['channels'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 0, 2), true); + $thisfile_riff_audio['total_samples'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 2, 4), false); + $thisfile_riff_audio['bits_per_sample'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 6, 2), true); + $thisfile_riff_audio['sample_rate'] = (int) getid3_lib::BigEndian2Float(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 8, 10)); + + if ($thisfile_riff[$RIFFsubtype]['COMM'][0]['size'] > 18) { + $thisfile_riff_audio['codec_fourcc'] = substr($thisfile_riff_RIFFsubtype_COMM_0_data, 18, 4); + $CodecNameSize = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 22, 1), false); + $thisfile_riff_audio['codec_name'] = substr($thisfile_riff_RIFFsubtype_COMM_0_data, 23, $CodecNameSize); + switch ($thisfile_riff_audio['codec_name']) { + case 'NONE': + $thisfile_audio['codec'] = 'Pulse Code Modulation (PCM)'; + $thisfile_audio['lossless'] = true; + break; + + case '': + switch ($thisfile_riff_audio['codec_fourcc']) { + // http://developer.apple.com/qa/snd/snd07.html + case 'sowt': + $thisfile_riff_audio['codec_name'] = 'Two\'s Compliment Little-Endian PCM'; + $thisfile_audio['lossless'] = true; + break; + + case 'twos': + $thisfile_riff_audio['codec_name'] = 'Two\'s Compliment Big-Endian PCM'; + $thisfile_audio['lossless'] = true; + break; + + default: + break; + } + break; + + default: + $thisfile_audio['codec'] = $thisfile_riff_audio['codec_name']; + $thisfile_audio['lossless'] = false; + break; + } + } + + $thisfile_audio['channels'] = $thisfile_riff_audio['channels']; + if ($thisfile_riff_audio['bits_per_sample'] > 0) { + $thisfile_audio['bits_per_sample'] = $thisfile_riff_audio['bits_per_sample']; + } + $thisfile_audio['sample_rate'] = $thisfile_riff_audio['sample_rate']; + if ($thisfile_audio['sample_rate'] == 0) { + $info['error'][] = 'Corrupted AIFF file: sample_rate == zero'; + return false; + } + $info['playtime_seconds'] = $thisfile_riff_audio['total_samples'] / $thisfile_audio['sample_rate']; + } + + if (isset($thisfile_riff[$RIFFsubtype]['COMT'])) { + $offset = 0; + $CommentCount = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), false); + $offset += 2; + for ($i = 0; $i < $CommentCount; $i++) { + $info['comments_raw'][$i]['timestamp'] = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 4), false); + $offset += 4; + $info['comments_raw'][$i]['marker_id'] = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), true); + $offset += 2; + $CommentLength = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), false); + $offset += 2; + $info['comments_raw'][$i]['comment'] = substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, $CommentLength); + $offset += $CommentLength; + + $info['comments_raw'][$i]['timestamp_unix'] = getid3_lib::DateMac2Unix($info['comments_raw'][$i]['timestamp']); + $thisfile_riff['comments']['comment'][] = $info['comments_raw'][$i]['comment']; + } + } + + $CommentsChunkNames = array('NAME'=>'title', 'author'=>'artist', '(c) '=>'copyright', 'ANNO'=>'comment'); + foreach ($CommentsChunkNames as $key => $value) { + if (isset($thisfile_riff[$RIFFsubtype][$key][0]['data'])) { + $thisfile_riff['comments'][$value][] = $thisfile_riff[$RIFFsubtype][$key][0]['data']; + } + } +/* + if (isset($thisfile_riff[$RIFFsubtype]['ID3 '])) { + getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true); + $getid3_temp = new getID3(); + $getid3_temp->openfile($this->getid3->filename); + $getid3_id3v2 = new getid3_id3v2($getid3_temp); + $getid3_id3v2->StartingOffset = $thisfile_riff[$RIFFsubtype]['ID3 '][0]['offset'] + 8; + if ($thisfile_riff[$RIFFsubtype]['ID3 '][0]['valid'] = $getid3_id3v2->Analyze()) { + $info['id3v2'] = $getid3_temp->info['id3v2']; + } + unset($getid3_temp, $getid3_id3v2); + } +*/ + break; + + // http://en.wikipedia.org/wiki/8SVX + case '8SVX': + $info['fileformat'] = '8svx'; + $info['mime_type'] = 'audio/8svx'; + + $thisfile_audio['bitrate_mode'] = 'cbr'; + $thisfile_audio_dataformat = '8svx'; + $thisfile_audio['bits_per_sample'] = 8; + $thisfile_audio['channels'] = 1; // overridden below, if need be + + if (isset($thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'])) { + $info['avdataoffset'] = $thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'] + 8; + $info['avdataend'] = $info['avdataoffset'] + $thisfile_riff[$RIFFsubtype]['BODY'][0]['size']; + if ($info['avdataend'] > $info['filesize']) { + $info['warning'][] = 'Probable truncated AIFF file: expecting '.$thisfile_riff[$RIFFsubtype]['BODY'][0]['size'].' bytes of audio data, only '.($info['filesize'] - $info['avdataoffset']).' bytes found'; + } + } + + if (isset($thisfile_riff[$RIFFsubtype]['VHDR'][0]['offset'])) { + // shortcut + $thisfile_riff_RIFFsubtype_VHDR_0 = &$thisfile_riff[$RIFFsubtype]['VHDR'][0]; + + $thisfile_riff_RIFFsubtype_VHDR_0['oneShotHiSamples'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 0, 4)); + $thisfile_riff_RIFFsubtype_VHDR_0['repeatHiSamples'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 4, 4)); + $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerHiCycle'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 8, 4)); + $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerSec'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 12, 2)); + $thisfile_riff_RIFFsubtype_VHDR_0['ctOctave'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 14, 1)); + $thisfile_riff_RIFFsubtype_VHDR_0['sCompression'] = getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 15, 1)); + $thisfile_riff_RIFFsubtype_VHDR_0['Volume'] = getid3_lib::FixedPoint16_16(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 16, 4)); + + $thisfile_audio['sample_rate'] = $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerSec']; + + switch ($thisfile_riff_RIFFsubtype_VHDR_0['sCompression']) { + case 0: + $thisfile_audio['codec'] = 'Pulse Code Modulation (PCM)'; + $thisfile_audio['lossless'] = true; + $ActualBitsPerSample = 8; + break; + + case 1: + $thisfile_audio['codec'] = 'Fibonacci-delta encoding'; + $thisfile_audio['lossless'] = false; + $ActualBitsPerSample = 4; + break; + + default: + $info['warning'][] = 'Unexpected sCompression value in 8SVX.VHDR chunk - expecting 0 or 1, found "'.sCompression.'"'; + break; + } + } + + if (isset($thisfile_riff[$RIFFsubtype]['CHAN'][0]['data'])) { + $ChannelsIndex = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['CHAN'][0]['data'], 0, 4)); + switch ($ChannelsIndex) { + case 6: // Stereo + $thisfile_audio['channels'] = 2; + break; + + case 2: // Left channel only + case 4: // Right channel only + $thisfile_audio['channels'] = 1; + break; + + default: + $info['warning'][] = 'Unexpected value in 8SVX.CHAN chunk - expecting 2 or 4 or 6, found "'.$ChannelsIndex.'"'; + break; + } + + } + + $CommentsChunkNames = array('NAME'=>'title', 'author'=>'artist', '(c) '=>'copyright', 'ANNO'=>'comment'); + foreach ($CommentsChunkNames as $key => $value) { + if (isset($thisfile_riff[$RIFFsubtype][$key][0]['data'])) { + $thisfile_riff['comments'][$value][] = $thisfile_riff[$RIFFsubtype][$key][0]['data']; + } + } + + $thisfile_audio['bitrate'] = $thisfile_audio['sample_rate'] * $ActualBitsPerSample * $thisfile_audio['channels']; + if (!empty($thisfile_audio['bitrate'])) { + $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) / ($thisfile_audio['bitrate'] / 8); + } + break; + + case 'CDXA': + $info['fileformat'] = 'vcd'; // Asume Video CD + $info['mime_type'] = 'video/mpeg'; + + if (!empty($thisfile_riff['CDXA']['data'][0]['size'])) { + getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.mpeg.php', __FILE__, true); + + $getid3_temp = new getID3(); + $getid3_temp->openfile($this->getid3->filename); + $getid3_mpeg = new getid3_mpeg($getid3_temp); + $getid3_mpeg->Analyze(); + if (empty($getid3_temp->info['error'])) { + $info['audio'] = $getid3_temp->info['audio']; + $info['video'] = $getid3_temp->info['video']; + $info['mpeg'] = $getid3_temp->info['mpeg']; + $info['warning'] = $getid3_temp->info['warning']; + } + unset($getid3_temp, $getid3_mpeg); + } + break; + + + default: + $info['error'][] = 'Unknown RIFF type: expecting one of (WAVE|RMP3|AVI |CDDA|AIFF|AIFC|8SVX|CDXA), found "'.$RIFFsubtype.'" instead'; + //unset($info['fileformat']); + } + + switch ($RIFFsubtype) { + case 'WAVE': + case 'AIFF': + case 'AIFC': + $ID3v2_key_good = 'id3 '; + $ID3v2_keys_bad = array('ID3 ', 'tag '); + foreach ($ID3v2_keys_bad as $ID3v2_key_bad) { + if (isset($thisfile_riff[$RIFFsubtype][$ID3v2_key_bad]) && !array_key_exists($ID3v2_key_good, $thisfile_riff[$RIFFsubtype])) { + $thisfile_riff[$RIFFsubtype][$ID3v2_key_good] = $thisfile_riff[$RIFFsubtype][$ID3v2_key_bad]; + $info['warning'][] = 'mapping "'.$ID3v2_key_bad.'" chunk to "'.$ID3v2_key_good.'"'; + } + } + + if (isset($thisfile_riff[$RIFFsubtype]['id3 '])) { + getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true); + + $getid3_temp = new getID3(); + $getid3_temp->openfile($this->getid3->filename); + $getid3_id3v2 = new getid3_id3v2($getid3_temp); + $getid3_id3v2->StartingOffset = $thisfile_riff[$RIFFsubtype]['id3 '][0]['offset'] + 8; + if ($thisfile_riff[$RIFFsubtype]['id3 '][0]['valid'] = $getid3_id3v2->Analyze()) { + $info['id3v2'] = $getid3_temp->info['id3v2']; + } + unset($getid3_temp, $getid3_id3v2); + } + break; + } + + if (isset($thisfile_riff_WAVE['DISP']) && is_array($thisfile_riff_WAVE['DISP'])) { + $thisfile_riff['comments']['title'][] = trim(substr($thisfile_riff_WAVE['DISP'][count($thisfile_riff_WAVE['DISP']) - 1]['data'], 4)); + } + if (isset($thisfile_riff_WAVE['INFO']) && is_array($thisfile_riff_WAVE['INFO'])) { + self::parseComments($thisfile_riff_WAVE['INFO'], $thisfile_riff['comments']); + } + if (isset($thisfile_riff['AVI ']['INFO']) && is_array($thisfile_riff['AVI ']['INFO'])) { + self::parseComments($thisfile_riff['AVI ']['INFO'], $thisfile_riff['comments']); + } + + if (empty($thisfile_audio['encoder']) && !empty($info['mpeg']['audio']['LAME']['short_version'])) { + $thisfile_audio['encoder'] = $info['mpeg']['audio']['LAME']['short_version']; + } + + if (!isset($info['playtime_seconds'])) { + $info['playtime_seconds'] = 0; + } + if (isset($thisfile_riff_raw['strh'][0]['dwLength']) && isset($thisfile_riff_raw['avih']['dwMicroSecPerFrame'])) { + // needed for >2GB AVIs where 'avih' chunk only lists number of frames in that chunk, not entire movie + $info['playtime_seconds'] = $thisfile_riff_raw['strh'][0]['dwLength'] * ($thisfile_riff_raw['avih']['dwMicroSecPerFrame'] / 1000000); + } elseif (isset($thisfile_riff_raw['avih']['dwTotalFrames']) && isset($thisfile_riff_raw['avih']['dwMicroSecPerFrame'])) { + $info['playtime_seconds'] = $thisfile_riff_raw['avih']['dwTotalFrames'] * ($thisfile_riff_raw['avih']['dwMicroSecPerFrame'] / 1000000); + } + + if ($info['playtime_seconds'] > 0) { + if (isset($thisfile_riff_audio) && isset($thisfile_riff_video)) { + + if (!isset($info['bitrate'])) { + $info['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8); + } + + } elseif (isset($thisfile_riff_audio) && !isset($thisfile_riff_video)) { + + if (!isset($thisfile_audio['bitrate'])) { + $thisfile_audio['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8); + } + + } elseif (!isset($thisfile_riff_audio) && isset($thisfile_riff_video)) { + + if (!isset($thisfile_video['bitrate'])) { + $thisfile_video['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8); + } + + } + } + + + if (isset($thisfile_riff_video) && isset($thisfile_audio['bitrate']) && ($thisfile_audio['bitrate'] > 0) && ($info['playtime_seconds'] > 0)) { + + $info['bitrate'] = ((($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds']) * 8); + $thisfile_audio['bitrate'] = 0; + $thisfile_video['bitrate'] = $info['bitrate']; + foreach ($thisfile_riff_audio as $channelnumber => $audioinfoarray) { + $thisfile_video['bitrate'] -= $audioinfoarray['bitrate']; + $thisfile_audio['bitrate'] += $audioinfoarray['bitrate']; + } + if ($thisfile_video['bitrate'] <= 0) { + unset($thisfile_video['bitrate']); + } + if ($thisfile_audio['bitrate'] <= 0) { + unset($thisfile_audio['bitrate']); + } + } + + if (isset($info['mpeg']['audio'])) { + $thisfile_audio_dataformat = 'mp'.$info['mpeg']['audio']['layer']; + $thisfile_audio['sample_rate'] = $info['mpeg']['audio']['sample_rate']; + $thisfile_audio['channels'] = $info['mpeg']['audio']['channels']; + $thisfile_audio['bitrate'] = $info['mpeg']['audio']['bitrate']; + $thisfile_audio['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']); + if (!empty($info['mpeg']['audio']['codec'])) { + $thisfile_audio['codec'] = $info['mpeg']['audio']['codec'].' '.$thisfile_audio['codec']; + } + if (!empty($thisfile_audio['streams'])) { + foreach ($thisfile_audio['streams'] as $streamnumber => $streamdata) { + if ($streamdata['dataformat'] == $thisfile_audio_dataformat) { + $thisfile_audio['streams'][$streamnumber]['sample_rate'] = $thisfile_audio['sample_rate']; + $thisfile_audio['streams'][$streamnumber]['channels'] = $thisfile_audio['channels']; + $thisfile_audio['streams'][$streamnumber]['bitrate'] = $thisfile_audio['bitrate']; + $thisfile_audio['streams'][$streamnumber]['bitrate_mode'] = $thisfile_audio['bitrate_mode']; + $thisfile_audio['streams'][$streamnumber]['codec'] = $thisfile_audio['codec']; + } + } + } + $getid3_mp3 = new getid3_mp3($this->getid3); + $thisfile_audio['encoder_options'] = $getid3_mp3->GuessEncoderOptions(); + unset($getid3_mp3); + } + + + if (!empty($thisfile_riff_raw['fmt ']['wBitsPerSample']) && ($thisfile_riff_raw['fmt ']['wBitsPerSample'] > 0)) { + switch ($thisfile_audio_dataformat) { + case 'ac3': + // ignore bits_per_sample + break; + + default: + $thisfile_audio['bits_per_sample'] = $thisfile_riff_raw['fmt ']['wBitsPerSample']; + break; + } + } + + + if (empty($thisfile_riff_raw)) { + unset($thisfile_riff['raw']); + } + if (empty($thisfile_riff_audio)) { + unset($thisfile_riff['audio']); + } + if (empty($thisfile_riff_video)) { + unset($thisfile_riff['video']); + } + + return true; + } + + public function ParseRIFFAMV($startoffset, $maxoffset) { + // AMV files are RIFF-AVI files with parts of the spec deliberately broken, such as chunk size fields hardcoded to zero (because players known in hardware that these fields are always a certain size + + // https://code.google.com/p/amv-codec-tools/wiki/AmvDocumentation + //typedef struct _amvmainheader { + //FOURCC fcc; // 'amvh' + //DWORD cb; + //DWORD dwMicroSecPerFrame; + //BYTE reserve[28]; + //DWORD dwWidth; + //DWORD dwHeight; + //DWORD dwSpeed; + //DWORD reserve0; + //DWORD reserve1; + //BYTE bTimeSec; + //BYTE bTimeMin; + //WORD wTimeHour; + //} AMVMAINHEADER; + + $info = &$this->getid3->info; + $RIFFchunk = false; + + try { + + $this->fseek($startoffset); + $maxoffset = min($maxoffset, $info['avdataend']); + $AMVheader = $this->fread(284); + if (substr($AMVheader, 0, 8) != 'hdrlamvh') { + throw new Exception('expecting "hdrlamv" at offset '.($startoffset + 0).', found "'.substr($AMVheader, 0, 8).'"'); + } + if (substr($AMVheader, 8, 4) != "\x38\x00\x00\x00") { // "amvh" chunk size, hardcoded to 0x38 = 56 bytes + throw new Exception('expecting "0x38000000" at offset '.($startoffset + 8).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 8, 4)).'"'); + } + $RIFFchunk = array(); + $RIFFchunk['amvh']['us_per_frame'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 12, 4)); + $RIFFchunk['amvh']['reserved28'] = substr($AMVheader, 16, 28); // null? reserved? + $RIFFchunk['amvh']['resolution_x'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 44, 4)); + $RIFFchunk['amvh']['resolution_y'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 48, 4)); + $RIFFchunk['amvh']['frame_rate_int'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 52, 4)); + $RIFFchunk['amvh']['reserved0'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 56, 4)); // 1? reserved? + $RIFFchunk['amvh']['reserved1'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 60, 4)); // 0? reserved? + $RIFFchunk['amvh']['runtime_sec'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 64, 1)); + $RIFFchunk['amvh']['runtime_min'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 65, 1)); + $RIFFchunk['amvh']['runtime_hrs'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 66, 2)); + + $info['video']['frame_rate'] = 1000000 / $RIFFchunk['amvh']['us_per_frame']; + $info['video']['resolution_x'] = $RIFFchunk['amvh']['resolution_x']; + $info['video']['resolution_y'] = $RIFFchunk['amvh']['resolution_y']; + $info['playtime_seconds'] = ($RIFFchunk['amvh']['runtime_hrs'] * 3600) + ($RIFFchunk['amvh']['runtime_min'] * 60) + $RIFFchunk['amvh']['runtime_sec']; + + // the rest is all hardcoded(?) and does not appear to be useful until you get to audio info at offset 256, even then everything is probably hardcoded + + if (substr($AMVheader, 68, 20) != 'LIST'."\x00\x00\x00\x00".'strlstrh'."\x38\x00\x00\x00") { + throw new Exception('expecting "LIST<0x00000000>strlstrh<0x38000000>" at offset '.($startoffset + 68).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 68, 20)).'"'); + } + // followed by 56 bytes of null: substr($AMVheader, 88, 56) -> 144 + if (substr($AMVheader, 144, 8) != 'strf'."\x24\x00\x00\x00") { + throw new Exception('expecting "strf<0x24000000>" at offset '.($startoffset + 144).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 144, 8)).'"'); + } + // followed by 36 bytes of null: substr($AMVheader, 144, 36) -> 180 + + if (substr($AMVheader, 188, 20) != 'LIST'."\x00\x00\x00\x00".'strlstrh'."\x30\x00\x00\x00") { + throw new Exception('expecting "LIST<0x00000000>strlstrh<0x30000000>" at offset '.($startoffset + 188).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 188, 20)).'"'); + } + // followed by 48 bytes of null: substr($AMVheader, 208, 48) -> 256 + if (substr($AMVheader, 256, 8) != 'strf'."\x14\x00\x00\x00") { + throw new Exception('expecting "strf<0x14000000>" at offset '.($startoffset + 256).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 256, 8)).'"'); + } + // followed by 20 bytes of a modified WAVEFORMATEX: + // typedef struct { + // WORD wFormatTag; //(Fixme: this is equal to PCM's 0x01 format code) + // WORD nChannels; //(Fixme: this is always 1) + // DWORD nSamplesPerSec; //(Fixme: for all known sample files this is equal to 22050) + // DWORD nAvgBytesPerSec; //(Fixme: for all known sample files this is equal to 44100) + // WORD nBlockAlign; //(Fixme: this seems to be 2 in AMV files, is this correct ?) + // WORD wBitsPerSample; //(Fixme: this seems to be 16 in AMV files instead of the expected 4) + // WORD cbSize; //(Fixme: this seems to be 0 in AMV files) + // WORD reserved; + // } WAVEFORMATEX; + $RIFFchunk['strf']['wformattag'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 264, 2)); + $RIFFchunk['strf']['nchannels'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 266, 2)); + $RIFFchunk['strf']['nsamplespersec'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 268, 4)); + $RIFFchunk['strf']['navgbytespersec'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 272, 4)); + $RIFFchunk['strf']['nblockalign'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 276, 2)); + $RIFFchunk['strf']['wbitspersample'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 278, 2)); + $RIFFchunk['strf']['cbsize'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 280, 2)); + $RIFFchunk['strf']['reserved'] = getid3_lib::LittleEndian2Int(substr($AMVheader, 282, 2)); + + + $info['audio']['lossless'] = false; + $info['audio']['sample_rate'] = $RIFFchunk['strf']['nsamplespersec']; + $info['audio']['channels'] = $RIFFchunk['strf']['nchannels']; + $info['audio']['bits_per_sample'] = $RIFFchunk['strf']['wbitspersample']; + $info['audio']['bitrate'] = $info['audio']['sample_rate'] * $info['audio']['channels'] * $info['audio']['bits_per_sample']; + $info['audio']['bitrate_mode'] = 'cbr'; + + + } catch (getid3_exception $e) { + if ($e->getCode() == 10) { + $this->warning('RIFFAMV parser: '.$e->getMessage()); + } else { + throw $e; + } + } + + return $RIFFchunk; + } + + + public function ParseRIFF($startoffset, $maxoffset) { + $info = &$this->getid3->info; + + $RIFFchunk = false; + $FoundAllChunksWeNeed = false; + + try { + $this->fseek($startoffset); + $maxoffset = min($maxoffset, $info['avdataend']); + while ($this->ftell() < $maxoffset) { + $chunknamesize = $this->fread(8); + //$chunkname = substr($chunknamesize, 0, 4); + $chunkname = str_replace("\x00", '_', substr($chunknamesize, 0, 4)); // note: chunk names of 4 null bytes do appear to be legal (has been observed inside INFO and PRMI chunks, for example), but makes traversing array keys more difficult + $chunksize = $this->EitherEndian2Int(substr($chunknamesize, 4, 4)); + //if (strlen(trim($chunkname, "\x00")) < 4) { + if (strlen($chunkname) < 4) { + $this->error('Expecting chunk name at offset '.($this->ftell() - 8).' but found nothing. Aborting RIFF parsing.'); + break; + } + if (($chunksize == 0) && ($chunkname != 'JUNK')) { + $this->warning('Chunk ('.$chunkname.') size at offset '.($this->ftell() - 4).' is zero. Aborting RIFF parsing.'); + break; + } + if (($chunksize % 2) != 0) { + // all structures are packed on word boundaries + $chunksize++; + } + + switch ($chunkname) { + case 'LIST': + $listname = $this->fread(4); + if (preg_match('#^(movi|rec )$#i', $listname)) { + $RIFFchunk[$listname]['offset'] = $this->ftell() - 4; + $RIFFchunk[$listname]['size'] = $chunksize; + + if (!$FoundAllChunksWeNeed) { + $WhereWeWere = $this->ftell(); + $AudioChunkHeader = $this->fread(12); + $AudioChunkStreamNum = substr($AudioChunkHeader, 0, 2); + $AudioChunkStreamType = substr($AudioChunkHeader, 2, 2); + $AudioChunkSize = getid3_lib::LittleEndian2Int(substr($AudioChunkHeader, 4, 4)); + + if ($AudioChunkStreamType == 'wb') { + $FirstFourBytes = substr($AudioChunkHeader, 8, 4); + if (preg_match('/^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]/s', $FirstFourBytes)) { + // MP3 + if (getid3_mp3::MPEGaudioHeaderBytesValid($FirstFourBytes)) { + $getid3_temp = new getID3(); + $getid3_temp->openfile($this->getid3->filename); + $getid3_temp->info['avdataoffset'] = $this->ftell() - 4; + $getid3_temp->info['avdataend'] = $this->ftell() + $AudioChunkSize; + $getid3_mp3 = new getid3_mp3($getid3_temp, __CLASS__); + $getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false); + if (isset($getid3_temp->info['mpeg']['audio'])) { + $info['mpeg']['audio'] = $getid3_temp->info['mpeg']['audio']; + $info['audio'] = $getid3_temp->info['audio']; + $info['audio']['dataformat'] = 'mp'.$info['mpeg']['audio']['layer']; + $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate']; + $info['audio']['channels'] = $info['mpeg']['audio']['channels']; + $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate']; + $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']); + //$info['bitrate'] = $info['audio']['bitrate']; + } + unset($getid3_temp, $getid3_mp3); + } + + } elseif (strpos($FirstFourBytes, getid3_ac3::syncword) === 0) { + + // AC3 + $getid3_temp = new getID3(); + $getid3_temp->openfile($this->getid3->filename); + $getid3_temp->info['avdataoffset'] = $this->ftell() - 4; + $getid3_temp->info['avdataend'] = $this->ftell() + $AudioChunkSize; + $getid3_ac3 = new getid3_ac3($getid3_temp); + $getid3_ac3->Analyze(); + if (empty($getid3_temp->info['error'])) { + $info['audio'] = $getid3_temp->info['audio']; + $info['ac3'] = $getid3_temp->info['ac3']; + if (!empty($getid3_temp->info['warning'])) { + foreach ($getid3_temp->info['warning'] as $key => $value) { + $info['warning'][] = $value; + } + } + } + unset($getid3_temp, $getid3_ac3); + } + } + $FoundAllChunksWeNeed = true; + $this->fseek($WhereWeWere); + } + $this->fseek($chunksize - 4, SEEK_CUR); + + } else { + + if (!isset($RIFFchunk[$listname])) { + $RIFFchunk[$listname] = array(); + } + $LISTchunkParent = $listname; + $LISTchunkMaxOffset = $this->ftell() - 4 + $chunksize; + if ($parsedChunk = $this->ParseRIFF($this->ftell(), $LISTchunkMaxOffset)) { + $RIFFchunk[$listname] = array_merge_recursive($RIFFchunk[$listname], $parsedChunk); + } + + } + break; + + default: + if (preg_match('#^[0-9]{2}(wb|pc|dc|db)$#', $chunkname)) { + $this->fseek($chunksize, SEEK_CUR); + break; + } + $thisindex = 0; + if (isset($RIFFchunk[$chunkname]) && is_array($RIFFchunk[$chunkname])) { + $thisindex = count($RIFFchunk[$chunkname]); + } + $RIFFchunk[$chunkname][$thisindex]['offset'] = $this->ftell() - 8; + $RIFFchunk[$chunkname][$thisindex]['size'] = $chunksize; + switch ($chunkname) { + case 'data': + $info['avdataoffset'] = $this->ftell(); + $info['avdataend'] = $info['avdataoffset'] + $chunksize; + + $testData = $this->fread(36); + if ($testData === '') { + break; + } + if (preg_match('/^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]/s', substr($testData, 0, 4))) { + + // Probably is MP3 data + if (getid3_mp3::MPEGaudioHeaderBytesValid(substr($testData, 0, 4))) { + $getid3_temp = new getID3(); + $getid3_temp->openfile($this->getid3->filename); + $getid3_temp->info['avdataoffset'] = $info['avdataoffset']; + $getid3_temp->info['avdataend'] = $info['avdataend']; + $getid3_mp3 = new getid3_mp3($getid3_temp, __CLASS__); + $getid3_mp3->getOnlyMPEGaudioInfo($info['avdataoffset'], false); + if (empty($getid3_temp->info['error'])) { + $info['audio'] = $getid3_temp->info['audio']; + $info['mpeg'] = $getid3_temp->info['mpeg']; + } + unset($getid3_temp, $getid3_mp3); + } + + } elseif (($isRegularAC3 = (substr($testData, 0, 2) == getid3_ac3::syncword)) || substr($testData, 8, 2) == strrev(getid3_ac3::syncword)) { + + // This is probably AC-3 data + $getid3_temp = new getID3(); + if ($isRegularAC3) { + $getid3_temp->openfile($this->getid3->filename); + $getid3_temp->info['avdataoffset'] = $info['avdataoffset']; + $getid3_temp->info['avdataend'] = $info['avdataend']; + } + $getid3_ac3 = new getid3_ac3($getid3_temp); + if ($isRegularAC3) { + $getid3_ac3->Analyze(); + } else { + // Dolby Digital WAV + // AC-3 content, but not encoded in same format as normal AC-3 file + // For one thing, byte order is swapped + $ac3_data = ''; + for ($i = 0; $i < 28; $i += 2) { + $ac3_data .= substr($testData, 8 + $i + 1, 1); + $ac3_data .= substr($testData, 8 + $i + 0, 1); + } + $getid3_ac3->AnalyzeString($ac3_data); + } + + if (empty($getid3_temp->info['error'])) { + $info['audio'] = $getid3_temp->info['audio']; + $info['ac3'] = $getid3_temp->info['ac3']; + if (!empty($getid3_temp->info['warning'])) { + foreach ($getid3_temp->info['warning'] as $newerror) { + $this->warning('getid3_ac3() says: ['.$newerror.']'); + } + } + } + unset($getid3_temp, $getid3_ac3); + + } elseif (preg_match('/^('.implode('|', array_map('preg_quote', getid3_dts::$syncwords)).')/', $testData)) { + + // This is probably DTS data + $getid3_temp = new getID3(); + $getid3_temp->openfile($this->getid3->filename); + $getid3_temp->info['avdataoffset'] = $info['avdataoffset']; + $getid3_dts = new getid3_dts($getid3_temp); + $getid3_dts->Analyze(); + if (empty($getid3_temp->info['error'])) { + $info['audio'] = $getid3_temp->info['audio']; + $info['dts'] = $getid3_temp->info['dts']; + $info['playtime_seconds'] = $getid3_temp->info['playtime_seconds']; // may not match RIFF calculations since DTS-WAV often used 14/16 bit-word packing + if (!empty($getid3_temp->info['warning'])) { + foreach ($getid3_temp->info['warning'] as $newerror) { + $this->warning('getid3_dts() says: ['.$newerror.']'); + } + } + } + + unset($getid3_temp, $getid3_dts); + + } elseif (substr($testData, 0, 4) == 'wvpk') { + + // This is WavPack data + $info['wavpack']['offset'] = $info['avdataoffset']; + $info['wavpack']['size'] = getid3_lib::LittleEndian2Int(substr($testData, 4, 4)); + $this->parseWavPackHeader(substr($testData, 8, 28)); + + } else { + // This is some other kind of data (quite possibly just PCM) + // do nothing special, just skip it + } + $nextoffset = $info['avdataend']; + $this->fseek($nextoffset); + break; + + case 'iXML': + case 'bext': + case 'cart': + case 'fmt ': + case 'strh': + case 'strf': + case 'indx': + case 'MEXT': + case 'DISP': + // always read data in + case 'JUNK': + // should be: never read data in + // but some programs write their version strings in a JUNK chunk (e.g. VirtualDub, AVIdemux, etc) + if ($chunksize < 1048576) { + if ($chunksize > 0) { + $RIFFchunk[$chunkname][$thisindex]['data'] = $this->fread($chunksize); + if ($chunkname == 'JUNK') { + if (preg_match('#^([\\x20-\\x7F]+)#', $RIFFchunk[$chunkname][$thisindex]['data'], $matches)) { + // only keep text characters [chr(32)-chr(127)] + $info['riff']['comments']['junk'][] = trim($matches[1]); + } + // but if nothing there, ignore + // remove the key in either case + unset($RIFFchunk[$chunkname][$thisindex]['data']); + } + } + } else { + $this->warning('Chunk "'.$chunkname.'" at offset '.$this->ftell().' is unexpectedly larger than 1MB (claims to be '.number_format($chunksize).' bytes), skipping data'); + $this->fseek($chunksize, SEEK_CUR); + } + break; + + //case 'IDVX': + // $info['divxtag']['comments'] = self::ParseDIVXTAG($this->fread($chunksize)); + // break; + + default: + if (!empty($LISTchunkParent) && (($RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size']) <= $LISTchunkMaxOffset)) { + $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset']; + $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['size'] = $RIFFchunk[$chunkname][$thisindex]['size']; + unset($RIFFchunk[$chunkname][$thisindex]['offset']); + unset($RIFFchunk[$chunkname][$thisindex]['size']); + if (isset($RIFFchunk[$chunkname][$thisindex]) && empty($RIFFchunk[$chunkname][$thisindex])) { + unset($RIFFchunk[$chunkname][$thisindex]); + } + if (isset($RIFFchunk[$chunkname]) && empty($RIFFchunk[$chunkname])) { + unset($RIFFchunk[$chunkname]); + } + $RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['data'] = $this->fread($chunksize); + } elseif ($chunksize < 2048) { + // only read data in if smaller than 2kB + $RIFFchunk[$chunkname][$thisindex]['data'] = $this->fread($chunksize); + } else { + $this->fseek($chunksize, SEEK_CUR); + } + break; + } + break; + } + } + + } catch (getid3_exception $e) { + if ($e->getCode() == 10) { + $this->warning('RIFF parser: '.$e->getMessage()); + } else { + throw $e; + } + } + + return $RIFFchunk; + } + + public function ParseRIFFdata(&$RIFFdata) { + $info = &$this->getid3->info; + if ($RIFFdata) { + $tempfile = tempnam(GETID3_TEMP_DIR, 'getID3'); + $fp_temp = fopen($tempfile, 'wb'); + $RIFFdataLength = strlen($RIFFdata); + $NewLengthString = getid3_lib::LittleEndian2String($RIFFdataLength, 4); + for ($i = 0; $i < 4; $i++) { + $RIFFdata[($i + 4)] = $NewLengthString[$i]; + } + fwrite($fp_temp, $RIFFdata); + fclose($fp_temp); + + $getid3_temp = new getID3(); + $getid3_temp->openfile($tempfile); + $getid3_temp->info['filesize'] = $RIFFdataLength; + $getid3_temp->info['filenamepath'] = $info['filenamepath']; + $getid3_temp->info['tags'] = $info['tags']; + $getid3_temp->info['warning'] = $info['warning']; + $getid3_temp->info['error'] = $info['error']; + $getid3_temp->info['comments'] = $info['comments']; + $getid3_temp->info['audio'] = (isset($info['audio']) ? $info['audio'] : array()); + $getid3_temp->info['video'] = (isset($info['video']) ? $info['video'] : array()); + $getid3_riff = new getid3_riff($getid3_temp); + $getid3_riff->Analyze(); + + $info['riff'] = $getid3_temp->info['riff']; + $info['warning'] = $getid3_temp->info['warning']; + $info['error'] = $getid3_temp->info['error']; + $info['tags'] = $getid3_temp->info['tags']; + $info['comments'] = $getid3_temp->info['comments']; + unset($getid3_riff, $getid3_temp); + unlink($tempfile); + } + return false; + } + + public static function parseComments(&$RIFFinfoArray, &$CommentsTargetArray) { + $RIFFinfoKeyLookup = array( + 'IARL'=>'archivallocation', + 'IART'=>'artist', + 'ICDS'=>'costumedesigner', + 'ICMS'=>'commissionedby', + 'ICMT'=>'comment', + 'ICNT'=>'country', + 'ICOP'=>'copyright', + 'ICRD'=>'creationdate', + 'IDIM'=>'dimensions', + 'IDIT'=>'digitizationdate', + 'IDPI'=>'resolution', + 'IDST'=>'distributor', + 'IEDT'=>'editor', + 'IENG'=>'engineers', + 'IFRM'=>'accountofparts', + 'IGNR'=>'genre', + 'IKEY'=>'keywords', + 'ILGT'=>'lightness', + 'ILNG'=>'language', + 'IMED'=>'orignalmedium', + 'IMUS'=>'composer', + 'INAM'=>'title', + 'IPDS'=>'productiondesigner', + 'IPLT'=>'palette', + 'IPRD'=>'product', + 'IPRO'=>'producer', + 'IPRT'=>'part', + 'IRTD'=>'rating', + 'ISBJ'=>'subject', + 'ISFT'=>'software', + 'ISGN'=>'secondarygenre', + 'ISHP'=>'sharpness', + 'ISRC'=>'sourcesupplier', + 'ISRF'=>'digitizationsource', + 'ISTD'=>'productionstudio', + 'ISTR'=>'starring', + 'ITCH'=>'encoded_by', + 'IWEB'=>'url', + 'IWRI'=>'writer', + '____'=>'comment', + ); + foreach ($RIFFinfoKeyLookup as $key => $value) { + if (isset($RIFFinfoArray[$key])) { + foreach ($RIFFinfoArray[$key] as $commentid => $commentdata) { + if (trim($commentdata['data']) != '') { + if (isset($CommentsTargetArray[$value])) { + $CommentsTargetArray[$value][] = trim($commentdata['data']); + } else { + $CommentsTargetArray[$value] = array(trim($commentdata['data'])); + } + } + } + } + } + return true; + } + + public static function parseWAVEFORMATex($WaveFormatExData) { + // shortcut + $WaveFormatEx['raw'] = array(); + $WaveFormatEx_raw = &$WaveFormatEx['raw']; + + $WaveFormatEx_raw['wFormatTag'] = substr($WaveFormatExData, 0, 2); + $WaveFormatEx_raw['nChannels'] = substr($WaveFormatExData, 2, 2); + $WaveFormatEx_raw['nSamplesPerSec'] = substr($WaveFormatExData, 4, 4); + $WaveFormatEx_raw['nAvgBytesPerSec'] = substr($WaveFormatExData, 8, 4); + $WaveFormatEx_raw['nBlockAlign'] = substr($WaveFormatExData, 12, 2); + $WaveFormatEx_raw['wBitsPerSample'] = substr($WaveFormatExData, 14, 2); + if (strlen($WaveFormatExData) > 16) { + $WaveFormatEx_raw['cbSize'] = substr($WaveFormatExData, 16, 2); + } + $WaveFormatEx_raw = array_map('getid3_lib::LittleEndian2Int', $WaveFormatEx_raw); + + $WaveFormatEx['codec'] = self::wFormatTagLookup($WaveFormatEx_raw['wFormatTag']); + $WaveFormatEx['channels'] = $WaveFormatEx_raw['nChannels']; + $WaveFormatEx['sample_rate'] = $WaveFormatEx_raw['nSamplesPerSec']; + $WaveFormatEx['bitrate'] = $WaveFormatEx_raw['nAvgBytesPerSec'] * 8; + $WaveFormatEx['bits_per_sample'] = $WaveFormatEx_raw['wBitsPerSample']; + + return $WaveFormatEx; + } + + public function parseWavPackHeader($WavPackChunkData) { + // typedef struct { + // char ckID [4]; + // long ckSize; + // short version; + // short bits; // added for version 2.00 + // short flags, shift; // added for version 3.00 + // long total_samples, crc, crc2; + // char extension [4], extra_bc, extras [3]; + // } WavpackHeader; + + // shortcut + $info = &$this->getid3->info; + $info['wavpack'] = array(); + $thisfile_wavpack = &$info['wavpack']; + + $thisfile_wavpack['version'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 0, 2)); + if ($thisfile_wavpack['version'] >= 2) { + $thisfile_wavpack['bits'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 2, 2)); + } + if ($thisfile_wavpack['version'] >= 3) { + $thisfile_wavpack['flags_raw'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 4, 2)); + $thisfile_wavpack['shift'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 6, 2)); + $thisfile_wavpack['total_samples'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 8, 4)); + $thisfile_wavpack['crc1'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 12, 4)); + $thisfile_wavpack['crc2'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 16, 4)); + $thisfile_wavpack['extension'] = substr($WavPackChunkData, 20, 4); + $thisfile_wavpack['extra_bc'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 24, 1)); + for ($i = 0; $i <= 2; $i++) { + $thisfile_wavpack['extras'][] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 25 + $i, 1)); + } + + // shortcut + $thisfile_wavpack['flags'] = array(); + $thisfile_wavpack_flags = &$thisfile_wavpack['flags']; + + $thisfile_wavpack_flags['mono'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000001); + $thisfile_wavpack_flags['fast_mode'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000002); + $thisfile_wavpack_flags['raw_mode'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000004); + $thisfile_wavpack_flags['calc_noise'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000008); + $thisfile_wavpack_flags['high_quality'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000010); + $thisfile_wavpack_flags['3_byte_samples'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000020); + $thisfile_wavpack_flags['over_20_bits'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000040); + $thisfile_wavpack_flags['use_wvc'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000080); + $thisfile_wavpack_flags['noiseshaping'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000100); + $thisfile_wavpack_flags['very_fast_mode'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000200); + $thisfile_wavpack_flags['new_high_quality'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000400); + $thisfile_wavpack_flags['cancel_extreme'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x000800); + $thisfile_wavpack_flags['cross_decorrelation'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x001000); + $thisfile_wavpack_flags['new_decorrelation'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x002000); + $thisfile_wavpack_flags['joint_stereo'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x004000); + $thisfile_wavpack_flags['extra_decorrelation'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x008000); + $thisfile_wavpack_flags['override_noiseshape'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x010000); + $thisfile_wavpack_flags['override_jointstereo'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x020000); + $thisfile_wavpack_flags['copy_source_filetime'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x040000); + $thisfile_wavpack_flags['create_exe'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x080000); + } + + return true; + } + + public static function ParseBITMAPINFOHEADER($BITMAPINFOHEADER, $littleEndian=true) { + + $parsed['biSize'] = substr($BITMAPINFOHEADER, 0, 4); // number of bytes required by the BITMAPINFOHEADER structure + $parsed['biWidth'] = substr($BITMAPINFOHEADER, 4, 4); // width of the bitmap in pixels + $parsed['biHeight'] = substr($BITMAPINFOHEADER, 8, 4); // height of the bitmap in pixels. If biHeight is positive, the bitmap is a 'bottom-up' DIB and its origin is the lower left corner. If biHeight is negative, the bitmap is a 'top-down' DIB and its origin is the upper left corner + $parsed['biPlanes'] = substr($BITMAPINFOHEADER, 12, 2); // number of color planes on the target device. In most cases this value must be set to 1 + $parsed['biBitCount'] = substr($BITMAPINFOHEADER, 14, 2); // Specifies the number of bits per pixels + $parsed['biSizeImage'] = substr($BITMAPINFOHEADER, 20, 4); // size of the bitmap data section of the image (the actual pixel data, excluding BITMAPINFOHEADER and RGBQUAD structures) + $parsed['biXPelsPerMeter'] = substr($BITMAPINFOHEADER, 24, 4); // horizontal resolution, in pixels per metre, of the target device + $parsed['biYPelsPerMeter'] = substr($BITMAPINFOHEADER, 28, 4); // vertical resolution, in pixels per metre, of the target device + $parsed['biClrUsed'] = substr($BITMAPINFOHEADER, 32, 4); // actual number of color indices in the color table used by the bitmap. If this value is zero, the bitmap uses the maximum number of colors corresponding to the value of the biBitCount member for the compression mode specified by biCompression + $parsed['biClrImportant'] = substr($BITMAPINFOHEADER, 36, 4); // number of color indices that are considered important for displaying the bitmap. If this value is zero, all colors are important + $parsed = array_map('getid3_lib::'.($littleEndian ? 'Little' : 'Big').'Endian2Int', $parsed); + + $parsed['fourcc'] = substr($BITMAPINFOHEADER, 16, 4); // compression identifier + + return $parsed; + } + + public static function ParseDIVXTAG($DIVXTAG, $raw=false) { + // structure from "IDivX" source, Form1.frm, by "Greg Frazier of Daemonic Software Group", email: gfrazier@icestorm.net, web: http://dsg.cjb.net/ + // source available at http://files.divx-digest.com/download/c663efe7ef8ad2e90bf4af4d3ea6188a/on0SWN2r/edit/IDivX.zip + // 'Byte Layout: '1111111111111111 + // '32 for Movie - 1 '1111111111111111 + // '28 for Author - 6 '6666666666666666 + // '4 for year - 2 '6666666666662222 + // '3 for genre - 3 '7777777777777777 + // '48 for Comments - 7 '7777777777777777 + // '1 for Rating - 4 '7777777777777777 + // '5 for Future Additions - 0 '333400000DIVXTAG + // '128 bytes total + + static $DIVXTAGgenre = array( + 0 => 'Action', + 1 => 'Action/Adventure', + 2 => 'Adventure', + 3 => 'Adult', + 4 => 'Anime', + 5 => 'Cartoon', + 6 => 'Claymation', + 7 => 'Comedy', + 8 => 'Commercial', + 9 => 'Documentary', + 10 => 'Drama', + 11 => 'Home Video', + 12 => 'Horror', + 13 => 'Infomercial', + 14 => 'Interactive', + 15 => 'Mystery', + 16 => 'Music Video', + 17 => 'Other', + 18 => 'Religion', + 19 => 'Sci Fi', + 20 => 'Thriller', + 21 => 'Western', + ), + $DIVXTAGrating = array( + 0 => 'Unrated', + 1 => 'G', + 2 => 'PG', + 3 => 'PG-13', + 4 => 'R', + 5 => 'NC-17', + ); + + $parsed['title'] = trim(substr($DIVXTAG, 0, 32)); + $parsed['artist'] = trim(substr($DIVXTAG, 32, 28)); + $parsed['year'] = intval(trim(substr($DIVXTAG, 60, 4))); + $parsed['comment'] = trim(substr($DIVXTAG, 64, 48)); + $parsed['genre_id'] = intval(trim(substr($DIVXTAG, 112, 3))); + $parsed['rating_id'] = ord(substr($DIVXTAG, 115, 1)); + //$parsed['padding'] = substr($DIVXTAG, 116, 5); // 5-byte null + //$parsed['magic'] = substr($DIVXTAG, 121, 7); // "DIVXTAG" + + $parsed['genre'] = (isset($DIVXTAGgenre[$parsed['genre_id']]) ? $DIVXTAGgenre[$parsed['genre_id']] : $parsed['genre_id']); + $parsed['rating'] = (isset($DIVXTAGrating[$parsed['rating_id']]) ? $DIVXTAGrating[$parsed['rating_id']] : $parsed['rating_id']); + + if (!$raw) { + unset($parsed['genre_id'], $parsed['rating_id']); + foreach ($parsed as $key => $value) { + if (!$value === '') { + unset($parsed['key']); + } + } + } + + foreach ($parsed as $tag => $value) { + $parsed[$tag] = array($value); + } + + return $parsed; + } + + public static function waveSNDMtagLookup($tagshortname) { + $begin = __LINE__; + + /** This is not a comment! + + ©kwd keywords + ©BPM bpm + ©trt tracktitle + ©des description + ©gen category + ©fin featuredinstrument + ©LID longid + ©bex bwdescription + ©pub publisher + ©cdt cdtitle + ©alb library + ©com composer + + */ + + return getid3_lib::EmbeddedLookup($tagshortname, $begin, __LINE__, __FILE__, 'riff-sndm'); + } + + public static function wFormatTagLookup($wFormatTag) { + + $begin = __LINE__; + + /** This is not a comment! + + 0x0000 Microsoft Unknown Wave Format + 0x0001 Pulse Code Modulation (PCM) + 0x0002 Microsoft ADPCM + 0x0003 IEEE Float + 0x0004 Compaq Computer VSELP + 0x0005 IBM CVSD + 0x0006 Microsoft A-Law + 0x0007 Microsoft mu-Law + 0x0008 Microsoft DTS + 0x0010 OKI ADPCM + 0x0011 Intel DVI/IMA ADPCM + 0x0012 Videologic MediaSpace ADPCM + 0x0013 Sierra Semiconductor ADPCM + 0x0014 Antex Electronics G.723 ADPCM + 0x0015 DSP Solutions DigiSTD + 0x0016 DSP Solutions DigiFIX + 0x0017 Dialogic OKI ADPCM + 0x0018 MediaVision ADPCM + 0x0019 Hewlett-Packard CU + 0x0020 Yamaha ADPCM + 0x0021 Speech Compression Sonarc + 0x0022 DSP Group TrueSpeech + 0x0023 Echo Speech EchoSC1 + 0x0024 Audiofile AF36 + 0x0025 Audio Processing Technology APTX + 0x0026 AudioFile AF10 + 0x0027 Prosody 1612 + 0x0028 LRC + 0x0030 Dolby AC2 + 0x0031 Microsoft GSM 6.10 + 0x0032 MSNAudio + 0x0033 Antex Electronics ADPCME + 0x0034 Control Resources VQLPC + 0x0035 DSP Solutions DigiREAL + 0x0036 DSP Solutions DigiADPCM + 0x0037 Control Resources CR10 + 0x0038 Natural MicroSystems VBXADPCM + 0x0039 Crystal Semiconductor IMA ADPCM + 0x003A EchoSC3 + 0x003B Rockwell ADPCM + 0x003C Rockwell Digit LK + 0x003D Xebec + 0x0040 Antex Electronics G.721 ADPCM + 0x0041 G.728 CELP + 0x0042 MSG723 + 0x0050 MPEG Layer-2 or Layer-1 + 0x0052 RT24 + 0x0053 PAC + 0x0055 MPEG Layer-3 + 0x0059 Lucent G.723 + 0x0060 Cirrus + 0x0061 ESPCM + 0x0062 Voxware + 0x0063 Canopus Atrac + 0x0064 G.726 ADPCM + 0x0065 G.722 ADPCM + 0x0066 DSAT + 0x0067 DSAT Display + 0x0069 Voxware Byte Aligned + 0x0070 Voxware AC8 + 0x0071 Voxware AC10 + 0x0072 Voxware AC16 + 0x0073 Voxware AC20 + 0x0074 Voxware MetaVoice + 0x0075 Voxware MetaSound + 0x0076 Voxware RT29HW + 0x0077 Voxware VR12 + 0x0078 Voxware VR18 + 0x0079 Voxware TQ40 + 0x0080 Softsound + 0x0081 Voxware TQ60 + 0x0082 MSRT24 + 0x0083 G.729A + 0x0084 MVI MV12 + 0x0085 DF G.726 + 0x0086 DF GSM610 + 0x0088 ISIAudio + 0x0089 Onlive + 0x0091 SBC24 + 0x0092 Dolby AC3 SPDIF + 0x0093 MediaSonic G.723 + 0x0094 Aculab PLC Prosody 8kbps + 0x0097 ZyXEL ADPCM + 0x0098 Philips LPCBB + 0x0099 Packed + 0x00FF AAC + 0x0100 Rhetorex ADPCM + 0x0101 IBM mu-law + 0x0102 IBM A-law + 0x0103 IBM AVC Adaptive Differential Pulse Code Modulation (ADPCM) + 0x0111 Vivo G.723 + 0x0112 Vivo Siren + 0x0123 Digital G.723 + 0x0125 Sanyo LD ADPCM + 0x0130 Sipro Lab Telecom ACELP NET + 0x0131 Sipro Lab Telecom ACELP 4800 + 0x0132 Sipro Lab Telecom ACELP 8V3 + 0x0133 Sipro Lab Telecom G.729 + 0x0134 Sipro Lab Telecom G.729A + 0x0135 Sipro Lab Telecom Kelvin + 0x0140 Windows Media Video V8 + 0x0150 Qualcomm PureVoice + 0x0151 Qualcomm HalfRate + 0x0155 Ring Zero Systems TUB GSM + 0x0160 Microsoft Audio 1 + 0x0161 Windows Media Audio V7 / V8 / V9 + 0x0162 Windows Media Audio Professional V9 + 0x0163 Windows Media Audio Lossless V9 + 0x0200 Creative Labs ADPCM + 0x0202 Creative Labs Fastspeech8 + 0x0203 Creative Labs Fastspeech10 + 0x0210 UHER Informatic GmbH ADPCM + 0x0220 Quarterdeck + 0x0230 I-link Worldwide VC + 0x0240 Aureal RAW Sport + 0x0250 Interactive Products HSX + 0x0251 Interactive Products RPELP + 0x0260 Consistent Software CS2 + 0x0270 Sony SCX + 0x0300 Fujitsu FM Towns Snd + 0x0400 BTV Digital + 0x0401 Intel Music Coder + 0x0450 QDesign Music + 0x0680 VME VMPCM + 0x0681 AT&T Labs TPC + 0x08AE ClearJump LiteWave + 0x1000 Olivetti GSM + 0x1001 Olivetti ADPCM + 0x1002 Olivetti CELP + 0x1003 Olivetti SBC + 0x1004 Olivetti OPR + 0x1100 Lernout & Hauspie Codec (0x1100) + 0x1101 Lernout & Hauspie CELP Codec (0x1101) + 0x1102 Lernout & Hauspie SBC Codec (0x1102) + 0x1103 Lernout & Hauspie SBC Codec (0x1103) + 0x1104 Lernout & Hauspie SBC Codec (0x1104) + 0x1400 Norris + 0x1401 AT&T ISIAudio + 0x1500 Soundspace Music Compression + 0x181C VoxWare RT24 Speech + 0x1FC4 NCT Soft ALF2CD (www.nctsoft.com) + 0x2000 Dolby AC3 + 0x2001 Dolby DTS + 0x2002 WAVE_FORMAT_14_4 + 0x2003 WAVE_FORMAT_28_8 + 0x2004 WAVE_FORMAT_COOK + 0x2005 WAVE_FORMAT_DNET + 0x674F Ogg Vorbis 1 + 0x6750 Ogg Vorbis 2 + 0x6751 Ogg Vorbis 3 + 0x676F Ogg Vorbis 1+ + 0x6770 Ogg Vorbis 2+ + 0x6771 Ogg Vorbis 3+ + 0x7A21 GSM-AMR (CBR, no SID) + 0x7A22 GSM-AMR (VBR, including SID) + 0xFFFE WAVE_FORMAT_EXTENSIBLE + 0xFFFF WAVE_FORMAT_DEVELOPMENT + + */ + + return getid3_lib::EmbeddedLookup('0x'.str_pad(strtoupper(dechex($wFormatTag)), 4, '0', STR_PAD_LEFT), $begin, __LINE__, __FILE__, 'riff-wFormatTag'); + } + + public static function fourccLookup($fourcc) { + + $begin = __LINE__; + + /** This is not a comment! + + swot http://developer.apple.com/qa/snd/snd07.html + ____ No Codec (____) + _BIT BI_BITFIELDS (Raw RGB) + _JPG JPEG compressed + _PNG PNG compressed W3C/ISO/IEC (RFC-2083) + _RAW Full Frames (Uncompressed) + _RGB Raw RGB Bitmap + _RL4 RLE 4bpp RGB + _RL8 RLE 8bpp RGB + 3IV1 3ivx MPEG-4 v1 + 3IV2 3ivx MPEG-4 v2 + 3IVX 3ivx MPEG-4 + AASC Autodesk Animator + ABYR Kensington ?ABYR? + AEMI Array Microsystems VideoONE MPEG1-I Capture + AFLC Autodesk Animator FLC + AFLI Autodesk Animator FLI + AMPG Array Microsystems VideoONE MPEG + ANIM Intel RDX (ANIM) + AP41 AngelPotion Definitive + ASV1 Asus Video v1 + ASV2 Asus Video v2 + ASVX Asus Video 2.0 (audio) + AUR2 AuraVision Aura 2 Codec - YUV 4:2:2 + AURA AuraVision Aura 1 Codec - YUV 4:1:1 + AVDJ Independent JPEG Group\'s codec (AVDJ) + AVRN Independent JPEG Group\'s codec (AVRN) + AYUV 4:4:4 YUV (AYUV) + AZPR Quicktime Apple Video (AZPR) + BGR Raw RGB32 + BLZ0 Blizzard DivX MPEG-4 + BTVC Conexant Composite Video + BINK RAD Game Tools Bink Video + BT20 Conexant Prosumer Video + BTCV Conexant Composite Video Codec + BW10 Data Translation Broadway MPEG Capture + CC12 Intel YUV12 + CDVC Canopus DV + CFCC Digital Processing Systems DPS Perception + CGDI Microsoft Office 97 Camcorder Video + CHAM Winnov Caviara Champagne + CJPG Creative WebCam JPEG + CLJR Cirrus Logic YUV 4:1:1 + CMYK Common Data Format in Printing (Colorgraph) + CPLA Weitek 4:2:0 YUV Planar + CRAM Microsoft Video 1 (CRAM) + cvid Radius Cinepak + CVID Radius Cinepak + CWLT Microsoft Color WLT DIB + CYUV Creative Labs YUV + CYUY ATI YUV + D261 H.261 + D263 H.263 + DIB Device Independent Bitmap + DIV1 FFmpeg OpenDivX + DIV2 Microsoft MPEG-4 v1/v2 + DIV3 DivX ;-) MPEG-4 v3.x Low-Motion + DIV4 DivX ;-) MPEG-4 v3.x Fast-Motion + DIV5 DivX MPEG-4 v5.x + DIV6 DivX ;-) (MS MPEG-4 v3.x) + DIVX DivX MPEG-4 v4 (OpenDivX / Project Mayo) + divx DivX MPEG-4 + DMB1 Matrox Rainbow Runner hardware MJPEG + DMB2 Paradigm MJPEG + DSVD ?DSVD? + DUCK Duck TrueMotion 1.0 + DPS0 DPS/Leitch Reality Motion JPEG + DPSC DPS/Leitch PAR Motion JPEG + DV25 Matrox DVCPRO codec + DV50 Matrox DVCPRO50 codec + DVC IEC 61834 and SMPTE 314M (DVC/DV Video) + DVCP IEC 61834 and SMPTE 314M (DVC/DV Video) + DVHD IEC Standard DV 1125 lines @ 30fps / 1250 lines @ 25fps + DVMA Darim Vision DVMPEG (dummy for MPEG compressor) (www.darvision.com) + DVSL IEC Standard DV compressed in SD (SDL) + DVAN ?DVAN? + DVE2 InSoft DVE-2 Videoconferencing + dvsd IEC 61834 and SMPTE 314M DVC/DV Video + DVSD IEC 61834 and SMPTE 314M DVC/DV Video + DVX1 Lucent DVX1000SP Video Decoder + DVX2 Lucent DVX2000S Video Decoder + DVX3 Lucent DVX3000S Video Decoder + DX50 DivX v5 + DXT1 Microsoft DirectX Compressed Texture (DXT1) + DXT2 Microsoft DirectX Compressed Texture (DXT2) + DXT3 Microsoft DirectX Compressed Texture (DXT3) + DXT4 Microsoft DirectX Compressed Texture (DXT4) + DXT5 Microsoft DirectX Compressed Texture (DXT5) + DXTC Microsoft DirectX Compressed Texture (DXTC) + DXTn Microsoft DirectX Compressed Texture (DXTn) + EM2V Etymonix MPEG-2 I-frame (www.etymonix.com) + EKQ0 Elsa ?EKQ0? + ELK0 Elsa ?ELK0? + ESCP Eidos Escape + ETV1 eTreppid Video ETV1 + ETV2 eTreppid Video ETV2 + ETVC eTreppid Video ETVC + FLIC Autodesk FLI/FLC Animation + FLV1 Sorenson Spark + FLV4 On2 TrueMotion VP6 + FRWT Darim Vision Forward Motion JPEG (www.darvision.com) + FRWU Darim Vision Forward Uncompressed (www.darvision.com) + FLJP D-Vision Field Encoded Motion JPEG + FPS1 FRAPS v1 + FRWA SoftLab-Nsk Forward Motion JPEG w/ alpha channel + FRWD SoftLab-Nsk Forward Motion JPEG + FVF1 Iterated Systems Fractal Video Frame + GLZW Motion LZW (gabest@freemail.hu) + GPEG Motion JPEG (gabest@freemail.hu) + GWLT Microsoft Greyscale WLT DIB + H260 Intel ITU H.260 Videoconferencing + H261 Intel ITU H.261 Videoconferencing + H262 Intel ITU H.262 Videoconferencing + H263 Intel ITU H.263 Videoconferencing + H264 Intel ITU H.264 Videoconferencing + H265 Intel ITU H.265 Videoconferencing + H266 Intel ITU H.266 Videoconferencing + H267 Intel ITU H.267 Videoconferencing + H268 Intel ITU H.268 Videoconferencing + H269 Intel ITU H.269 Videoconferencing + HFYU Huffman Lossless Codec + HMCR Rendition Motion Compensation Format (HMCR) + HMRR Rendition Motion Compensation Format (HMRR) + I263 FFmpeg I263 decoder + IF09 Indeo YVU9 ("YVU9 with additional delta-frame info after the U plane") + IUYV Interlaced version of UYVY (www.leadtools.com) + IY41 Interlaced version of Y41P (www.leadtools.com) + IYU1 12 bit format used in mode 2 of the IEEE 1394 Digital Camera 1.04 spec IEEE standard + IYU2 24 bit format used in mode 2 of the IEEE 1394 Digital Camera 1.04 spec IEEE standard + IYUV Planar YUV format (8-bpp Y plane, followed by 8-bpp 2×2 U and V planes) + i263 Intel ITU H.263 Videoconferencing (i263) + I420 Intel Indeo 4 + IAN Intel Indeo 4 (RDX) + ICLB InSoft CellB Videoconferencing + IGOR Power DVD + IJPG Intergraph JPEG + ILVC Intel Layered Video + ILVR ITU-T H.263+ + IPDV I-O Data Device Giga AVI DV Codec + IR21 Intel Indeo 2.1 + IRAW Intel YUV Uncompressed + IV30 Intel Indeo 3.0 + IV31 Intel Indeo 3.1 + IV32 Ligos Indeo 3.2 + IV33 Ligos Indeo 3.3 + IV34 Ligos Indeo 3.4 + IV35 Ligos Indeo 3.5 + IV36 Ligos Indeo 3.6 + IV37 Ligos Indeo 3.7 + IV38 Ligos Indeo 3.8 + IV39 Ligos Indeo 3.9 + IV40 Ligos Indeo Interactive 4.0 + IV41 Ligos Indeo Interactive 4.1 + IV42 Ligos Indeo Interactive 4.2 + IV43 Ligos Indeo Interactive 4.3 + IV44 Ligos Indeo Interactive 4.4 + IV45 Ligos Indeo Interactive 4.5 + IV46 Ligos Indeo Interactive 4.6 + IV47 Ligos Indeo Interactive 4.7 + IV48 Ligos Indeo Interactive 4.8 + IV49 Ligos Indeo Interactive 4.9 + IV50 Ligos Indeo Interactive 5.0 + JBYR Kensington ?JBYR? + JPEG Still Image JPEG DIB + JPGL Pegasus Lossless Motion JPEG + KMVC Team17 Software Karl Morton\'s Video Codec + LSVM Vianet Lighting Strike Vmail (Streaming) (www.vianet.com) + LEAD LEAD Video Codec + Ljpg LEAD MJPEG Codec + MDVD Alex MicroDVD Video (hacked MS MPEG-4) (www.tiasoft.de) + MJPA Morgan Motion JPEG (MJPA) (www.morgan-multimedia.com) + MJPB Morgan Motion JPEG (MJPB) (www.morgan-multimedia.com) + MMES Matrox MPEG-2 I-frame + MP2v Microsoft S-Mpeg 4 version 1 (MP2v) + MP42 Microsoft S-Mpeg 4 version 2 (MP42) + MP43 Microsoft S-Mpeg 4 version 3 (MP43) + MP4S Microsoft S-Mpeg 4 version 3 (MP4S) + MP4V FFmpeg MPEG-4 + MPG1 FFmpeg MPEG 1/2 + MPG2 FFmpeg MPEG 1/2 + MPG3 FFmpeg DivX ;-) (MS MPEG-4 v3) + MPG4 Microsoft MPEG-4 + MPGI Sigma Designs MPEG + MPNG PNG images decoder + MSS1 Microsoft Windows Screen Video + MSZH LCL (Lossless Codec Library) (www.geocities.co.jp/Playtown-Denei/2837/LRC.htm) + M261 Microsoft H.261 + M263 Microsoft H.263 + M4S2 Microsoft Fully Compliant MPEG-4 v2 simple profile (M4S2) + m4s2 Microsoft Fully Compliant MPEG-4 v2 simple profile (m4s2) + MC12 ATI Motion Compensation Format (MC12) + MCAM ATI Motion Compensation Format (MCAM) + MJ2C Morgan Multimedia Motion JPEG2000 + mJPG IBM Motion JPEG w/ Huffman Tables + MJPG Microsoft Motion JPEG DIB + MP42 Microsoft MPEG-4 (low-motion) + MP43 Microsoft MPEG-4 (fast-motion) + MP4S Microsoft MPEG-4 (MP4S) + mp4s Microsoft MPEG-4 (mp4s) + MPEG Chromatic Research MPEG-1 Video I-Frame + MPG4 Microsoft MPEG-4 Video High Speed Compressor + MPGI Sigma Designs MPEG + MRCA FAST Multimedia Martin Regen Codec + MRLE Microsoft Run Length Encoding + MSVC Microsoft Video 1 + MTX1 Matrox ?MTX1? + MTX2 Matrox ?MTX2? + MTX3 Matrox ?MTX3? + MTX4 Matrox ?MTX4? + MTX5 Matrox ?MTX5? + MTX6 Matrox ?MTX6? + MTX7 Matrox ?MTX7? + MTX8 Matrox ?MTX8? + MTX9 Matrox ?MTX9? + MV12 Motion Pixels Codec (old) + MWV1 Aware Motion Wavelets + nAVI SMR Codec (hack of Microsoft MPEG-4) (IRC #shadowrealm) + NT00 NewTek LightWave HDTV YUV w/ Alpha (www.newtek.com) + NUV1 NuppelVideo + NTN1 Nogatech Video Compression 1 + NVS0 nVidia GeForce Texture (NVS0) + NVS1 nVidia GeForce Texture (NVS1) + NVS2 nVidia GeForce Texture (NVS2) + NVS3 nVidia GeForce Texture (NVS3) + NVS4 nVidia GeForce Texture (NVS4) + NVS5 nVidia GeForce Texture (NVS5) + NVT0 nVidia GeForce Texture (NVT0) + NVT1 nVidia GeForce Texture (NVT1) + NVT2 nVidia GeForce Texture (NVT2) + NVT3 nVidia GeForce Texture (NVT3) + NVT4 nVidia GeForce Texture (NVT4) + NVT5 nVidia GeForce Texture (NVT5) + PIXL MiroXL, Pinnacle PCTV + PDVC I-O Data Device Digital Video Capture DV codec + PGVV Radius Video Vision + PHMO IBM Photomotion + PIM1 MPEG Realtime (Pinnacle Cards) + PIM2 Pegasus Imaging ?PIM2? + PIMJ Pegasus Imaging Lossless JPEG + PVEZ Horizons Technology PowerEZ + PVMM PacketVideo Corporation MPEG-4 + PVW2 Pegasus Imaging Wavelet Compression + Q1.0 Q-Team\'s QPEG 1.0 (www.q-team.de) + Q1.1 Q-Team\'s QPEG 1.1 (www.q-team.de) + QPEG Q-Team QPEG 1.0 + qpeq Q-Team QPEG 1.1 + RGB Raw BGR32 + RGBA Raw RGB w/ Alpha + RMP4 REALmagic MPEG-4 (unauthorized XVID copy) (www.sigmadesigns.com) + ROQV Id RoQ File Video Decoder + RPZA Quicktime Apple Video (RPZA) + RUD0 Rududu video codec (http://rududu.ifrance.com/rududu/) + RV10 RealVideo 1.0 (aka RealVideo 5.0) + RV13 RealVideo 1.0 (RV13) + RV20 RealVideo G2 + RV30 RealVideo 8 + RV40 RealVideo 9 + RGBT Raw RGB w/ Transparency + RLE Microsoft Run Length Encoder + RLE4 Run Length Encoded (4bpp, 16-color) + RLE8 Run Length Encoded (8bpp, 256-color) + RT21 Intel Indeo RealTime Video 2.1 + rv20 RealVideo G2 + rv30 RealVideo 8 + RVX Intel RDX (RVX ) + SMC Apple Graphics (SMC ) + SP54 Logitech Sunplus Sp54 Codec for Mustek GSmart Mini 2 + SPIG Radius Spigot + SVQ3 Sorenson Video 3 (Apple Quicktime 5) + s422 Tekram VideoCap C210 YUV 4:2:2 + SDCC Sun Communication Digital Camera Codec + SFMC CrystalNet Surface Fitting Method + SMSC Radius SMSC + SMSD Radius SMSD + smsv WorldConnect Wavelet Video + SPIG Radius Spigot + SPLC Splash Studios ACM Audio Codec (www.splashstudios.net) + SQZ2 Microsoft VXTreme Video Codec V2 + STVA ST Microelectronics CMOS Imager Data (Bayer) + STVB ST Microelectronics CMOS Imager Data (Nudged Bayer) + STVC ST Microelectronics CMOS Imager Data (Bunched) + STVX ST Microelectronics CMOS Imager Data (Extended CODEC Data Format) + STVY ST Microelectronics CMOS Imager Data (Extended CODEC Data Format with Correction Data) + SV10 Sorenson Video R1 + SVQ1 Sorenson Video + T420 Toshiba YUV 4:2:0 + TM2A Duck TrueMotion Archiver 2.0 (www.duck.com) + TVJP Pinnacle/Truevision Targa 2000 board (TVJP) + TVMJ Pinnacle/Truevision Targa 2000 board (TVMJ) + TY0N Tecomac Low-Bit Rate Codec (www.tecomac.com) + TY2C Trident Decompression Driver + TLMS TeraLogic Motion Intraframe Codec (TLMS) + TLST TeraLogic Motion Intraframe Codec (TLST) + TM20 Duck TrueMotion 2.0 + TM2X Duck TrueMotion 2X + TMIC TeraLogic Motion Intraframe Codec (TMIC) + TMOT Horizons Technology TrueMotion S + tmot Horizons TrueMotion Video Compression + TR20 Duck TrueMotion RealTime 2.0 + TSCC TechSmith Screen Capture Codec + TV10 Tecomac Low-Bit Rate Codec + TY2N Trident ?TY2N? + U263 UB Video H.263/H.263+/H.263++ Decoder + UMP4 UB Video MPEG 4 (www.ubvideo.com) + UYNV Nvidia UYVY packed 4:2:2 + UYVP Evans & Sutherland YCbCr 4:2:2 extended precision + UCOD eMajix.com ClearVideo + ULTI IBM Ultimotion + UYVY UYVY packed 4:2:2 + V261 Lucent VX2000S + VIFP VFAPI Reader Codec (www.yks.ne.jp/~hori/) + VIV1 FFmpeg H263+ decoder + VIV2 Vivo H.263 + VQC2 Vector-quantised codec 2 (research) http://eprints.ecs.soton.ac.uk/archive/00001310/01/VTC97-js.pdf) + VTLP Alaris VideoGramPiX + VYU9 ATI YUV (VYU9) + VYUY ATI YUV (VYUY) + V261 Lucent VX2000S + V422 Vitec Multimedia 24-bit YUV 4:2:2 Format + V655 Vitec Multimedia 16-bit YUV 4:2:2 Format + VCR1 ATI Video Codec 1 + VCR2 ATI Video Codec 2 + VCR3 ATI VCR 3.0 + VCR4 ATI VCR 4.0 + VCR5 ATI VCR 5.0 + VCR6 ATI VCR 6.0 + VCR7 ATI VCR 7.0 + VCR8 ATI VCR 8.0 + VCR9 ATI VCR 9.0 + VDCT Vitec Multimedia Video Maker Pro DIB + VDOM VDOnet VDOWave + VDOW VDOnet VDOLive (H.263) + VDTZ Darim Vison VideoTizer YUV + VGPX Alaris VideoGramPiX + VIDS Vitec Multimedia YUV 4:2:2 CCIR 601 for V422 + VIVO Vivo H.263 v2.00 + vivo Vivo H.263 + VIXL Miro/Pinnacle Video XL + VLV1 VideoLogic/PURE Digital Videologic Capture + VP30 On2 VP3.0 + VP31 On2 VP3.1 + VP6F On2 TrueMotion VP6 + VX1K Lucent VX1000S Video Codec + VX2K Lucent VX2000S Video Codec + VXSP Lucent VX1000SP Video Codec + WBVC Winbond W9960 + WHAM Microsoft Video 1 (WHAM) + WINX Winnov Software Compression + WJPG AverMedia Winbond JPEG + WMV1 Windows Media Video V7 + WMV2 Windows Media Video V8 + WMV3 Windows Media Video V9 + WNV1 Winnov Hardware Compression + XYZP Extended PAL format XYZ palette (www.riff.org) + x263 Xirlink H.263 + XLV0 NetXL Video Decoder + XMPG Xing MPEG (I-Frame only) + XVID XviD MPEG-4 (www.xvid.org) + XXAN ?XXAN? + YU92 Intel YUV (YU92) + YUNV Nvidia Uncompressed YUV 4:2:2 + YUVP Extended PAL format YUV palette (www.riff.org) + Y211 YUV 2:1:1 Packed + Y411 YUV 4:1:1 Packed + Y41B Weitek YUV 4:1:1 Planar + Y41P Brooktree PC1 YUV 4:1:1 Packed + Y41T Brooktree PC1 YUV 4:1:1 with transparency + Y42B Weitek YUV 4:2:2 Planar + Y42T Brooktree UYUV 4:2:2 with transparency + Y422 ADS Technologies Copy of UYVY used in Pyro WebCam firewire camera + Y800 Simple, single Y plane for monochrome images + Y8 Grayscale video + YC12 Intel YUV 12 codec + YUV8 Winnov Caviar YUV8 + YUV9 Intel YUV9 + YUY2 Uncompressed YUV 4:2:2 + YUYV Canopus YUV + YV12 YVU12 Planar + YVU9 Intel YVU9 Planar (8-bpp Y plane, followed by 8-bpp 4x4 U and V planes) + YVYU YVYU 4:2:2 Packed + ZLIB Lossless Codec Library zlib compression (www.geocities.co.jp/Playtown-Denei/2837/LRC.htm) + ZPEG Metheus Video Zipper + + */ + + return getid3_lib::EmbeddedLookup($fourcc, $begin, __LINE__, __FILE__, 'riff-fourcc'); + } + + private function EitherEndian2Int($byteword, $signed=false) { + if ($this->container == 'riff') { + return getid3_lib::LittleEndian2Int($byteword, $signed); + } + return getid3_lib::BigEndian2Int($byteword, false, $signed); + } + +} \ No newline at end of file diff --git a/wp-includes/ID3/module.audio.ac3.php b/wp-includes/ID3/module.audio.ac3.php new file mode 100644 index 0000000..38ddf35 --- /dev/null +++ b/wp-includes/ID3/module.audio.ac3.php @@ -0,0 +1,474 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +// also https://github.com/JamesHeinrich/getID3 // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio.ac3.php // +// module for analyzing AC-3 (aka Dolby Digital) audio files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_ac3 extends getid3_handler +{ + private $AC3header = array(); + private $BSIoffset = 0; + + const syncword = "\x0B\x77"; + + public function Analyze() { + $info = &$this->getid3->info; + + ///AH + $info['ac3']['raw']['bsi'] = array(); + $thisfile_ac3 = &$info['ac3']; + $thisfile_ac3_raw = &$thisfile_ac3['raw']; + $thisfile_ac3_raw_bsi = &$thisfile_ac3_raw['bsi']; + + + // http://www.atsc.org/standards/a_52a.pdf + + $info['fileformat'] = 'ac3'; + + // An AC-3 serial coded audio bit stream is made up of a sequence of synchronization frames + // Each synchronization frame contains 6 coded audio blocks (AB), each of which represent 256 + // new audio samples per channel. A synchronization information (SI) header at the beginning + // of each frame contains information needed to acquire and maintain synchronization. A + // bit stream information (BSI) header follows SI, and contains parameters describing the coded + // audio service. The coded audio blocks may be followed by an auxiliary data (Aux) field. At the + // end of each frame is an error check field that includes a CRC word for error detection. An + // additional CRC word is located in the SI header, the use of which, by a decoder, is optional. + // + // syncinfo() | bsi() | AB0 | AB1 | AB2 | AB3 | AB4 | AB5 | Aux | CRC + + // syncinfo() { + // syncword 16 + // crc1 16 + // fscod 2 + // frmsizecod 6 + // } /* end of syncinfo */ + + $this->fseek($info['avdataoffset']); + $this->AC3header['syncinfo'] = $this->fread(5); + + if (strpos($this->AC3header['syncinfo'], self::syncword) === 0) { + $thisfile_ac3_raw['synchinfo']['synchword'] = self::syncword; + $offset = 2; + } else { + if (!$this->isDependencyFor('matroska')) { + unset($info['fileformat'], $info['ac3']); + return $this->error('Expecting "'.getid3_lib::PrintHexBytes(self::syncword).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($this->AC3header['syncinfo'], 0, 2)).'"'); + } + $offset = 0; + $this->fseek(-2, SEEK_CUR); + } + + $info['audio']['dataformat'] = 'ac3'; + $info['audio']['bitrate_mode'] = 'cbr'; + $info['audio']['lossless'] = false; + + $thisfile_ac3_raw['synchinfo']['crc1'] = getid3_lib::LittleEndian2Int(substr($this->AC3header['syncinfo'], $offset, 2)); + $ac3_synchinfo_fscod_frmsizecod = getid3_lib::LittleEndian2Int(substr($this->AC3header['syncinfo'], ($offset + 2), 1)); + $thisfile_ac3_raw['synchinfo']['fscod'] = ($ac3_synchinfo_fscod_frmsizecod & 0xC0) >> 6; + $thisfile_ac3_raw['synchinfo']['frmsizecod'] = ($ac3_synchinfo_fscod_frmsizecod & 0x3F); + + $thisfile_ac3['sample_rate'] = self::sampleRateCodeLookup($thisfile_ac3_raw['synchinfo']['fscod']); + if ($thisfile_ac3_raw['synchinfo']['fscod'] <= 3) { + $info['audio']['sample_rate'] = $thisfile_ac3['sample_rate']; + } + + $thisfile_ac3['frame_length'] = self::frameSizeLookup($thisfile_ac3_raw['synchinfo']['frmsizecod'], $thisfile_ac3_raw['synchinfo']['fscod']); + $thisfile_ac3['bitrate'] = self::bitrateLookup($thisfile_ac3_raw['synchinfo']['frmsizecod']); + $info['audio']['bitrate'] = $thisfile_ac3['bitrate']; + + $this->AC3header['bsi'] = getid3_lib::BigEndian2Bin($this->fread(15)); + $ac3_bsi_offset = 0; + + $thisfile_ac3_raw_bsi['bsid'] = $this->readHeaderBSI(5); + if ($thisfile_ac3_raw_bsi['bsid'] > 8) { + // Decoders which can decode version 8 will thus be able to decode version numbers less than 8. + // If this standard is extended by the addition of additional elements or features, a value of bsid greater than 8 will be used. + // Decoders built to this version of the standard will not be able to decode versions with bsid greater than 8. + $this->error('Bit stream identification is version '.$thisfile_ac3_raw_bsi['bsid'].', but getID3() only understands up to version 8'); + unset($info['ac3']); + return false; + } + + $thisfile_ac3_raw_bsi['bsmod'] = $this->readHeaderBSI(3); + $thisfile_ac3_raw_bsi['acmod'] = $this->readHeaderBSI(3); + + $thisfile_ac3['service_type'] = self::serviceTypeLookup($thisfile_ac3_raw_bsi['bsmod'], $thisfile_ac3_raw_bsi['acmod']); + $ac3_coding_mode = self::audioCodingModeLookup($thisfile_ac3_raw_bsi['acmod']); + foreach($ac3_coding_mode as $key => $value) { + $thisfile_ac3[$key] = $value; + } + switch ($thisfile_ac3_raw_bsi['acmod']) { + case 0: + case 1: + $info['audio']['channelmode'] = 'mono'; + break; + case 3: + case 4: + $info['audio']['channelmode'] = 'stereo'; + break; + default: + $info['audio']['channelmode'] = 'surround'; + break; + } + $info['audio']['channels'] = $thisfile_ac3['num_channels']; + + if ($thisfile_ac3_raw_bsi['acmod'] & 0x01) { + // If the lsb of acmod is a 1, center channel is in use and cmixlev follows in the bit stream. + $thisfile_ac3_raw_bsi['cmixlev'] = $this->readHeaderBSI(2); + $thisfile_ac3['center_mix_level'] = self::centerMixLevelLookup($thisfile_ac3_raw_bsi['cmixlev']); + } + + if ($thisfile_ac3_raw_bsi['acmod'] & 0x04) { + // If the msb of acmod is a 1, surround channels are in use and surmixlev follows in the bit stream. + $thisfile_ac3_raw_bsi['surmixlev'] = $this->readHeaderBSI(2); + $thisfile_ac3['surround_mix_level'] = self::surroundMixLevelLookup($thisfile_ac3_raw_bsi['surmixlev']); + } + + if ($thisfile_ac3_raw_bsi['acmod'] == 0x02) { + // When operating in the two channel mode, this 2-bit code indicates whether or not the program has been encoded in Dolby Surround. + $thisfile_ac3_raw_bsi['dsurmod'] = $this->readHeaderBSI(2); + $thisfile_ac3['dolby_surround_mode'] = self::dolbySurroundModeLookup($thisfile_ac3_raw_bsi['dsurmod']); + } + + $thisfile_ac3_raw_bsi['lfeon'] = (bool) $this->readHeaderBSI(1); + $thisfile_ac3['lfe_enabled'] = $thisfile_ac3_raw_bsi['lfeon']; + if ($thisfile_ac3_raw_bsi['lfeon']) { + //$info['audio']['channels']++; + $info['audio']['channels'] .= '.1'; + } + + $thisfile_ac3['channels_enabled'] = self::channelsEnabledLookup($thisfile_ac3_raw_bsi['acmod'], $thisfile_ac3_raw_bsi['lfeon']); + + // This indicates how far the average dialogue level is below digital 100 percent. Valid values are 1-31. + // The value of 0 is reserved. The values of 1 to 31 are interpreted as -1 dB to -31 dB with respect to digital 100 percent. + $thisfile_ac3_raw_bsi['dialnorm'] = $this->readHeaderBSI(5); + $thisfile_ac3['dialogue_normalization'] = '-'.$thisfile_ac3_raw_bsi['dialnorm'].'dB'; + + $thisfile_ac3_raw_bsi['compre_flag'] = (bool) $this->readHeaderBSI(1); + if ($thisfile_ac3_raw_bsi['compre_flag']) { + $thisfile_ac3_raw_bsi['compr'] = $this->readHeaderBSI(8); + $thisfile_ac3['heavy_compression'] = self::heavyCompression($thisfile_ac3_raw_bsi['compr']); + } + + $thisfile_ac3_raw_bsi['langcode_flag'] = (bool) $this->readHeaderBSI(1); + if ($thisfile_ac3_raw_bsi['langcode_flag']) { + $thisfile_ac3_raw_bsi['langcod'] = $this->readHeaderBSI(8); + } + + $thisfile_ac3_raw_bsi['audprodie'] = (bool) $this->readHeaderBSI(1); + if ($thisfile_ac3_raw_bsi['audprodie']) { + $thisfile_ac3_raw_bsi['mixlevel'] = $this->readHeaderBSI(5); + $thisfile_ac3_raw_bsi['roomtyp'] = $this->readHeaderBSI(2); + + $thisfile_ac3['mixing_level'] = (80 + $thisfile_ac3_raw_bsi['mixlevel']).'dB'; + $thisfile_ac3['room_type'] = self::roomTypeLookup($thisfile_ac3_raw_bsi['roomtyp']); + } + + if ($thisfile_ac3_raw_bsi['acmod'] == 0x00) { + // If acmod is 0, then two completely independent program channels (dual mono) + // are encoded into the bit stream, and are referenced as Ch1, Ch2. In this case, + // a number of additional items are present in BSI or audblk to fully describe Ch2. + + // This indicates how far the average dialogue level is below digital 100 percent. Valid values are 1-31. + // The value of 0 is reserved. The values of 1 to 31 are interpreted as -1 dB to -31 dB with respect to digital 100 percent. + $thisfile_ac3_raw_bsi['dialnorm2'] = $this->readHeaderBSI(5); + $thisfile_ac3['dialogue_normalization2'] = '-'.$thisfile_ac3_raw_bsi['dialnorm2'].'dB'; + + $thisfile_ac3_raw_bsi['compre_flag2'] = (bool) $this->readHeaderBSI(1); + if ($thisfile_ac3_raw_bsi['compre_flag2']) { + $thisfile_ac3_raw_bsi['compr2'] = $this->readHeaderBSI(8); + $thisfile_ac3['heavy_compression2'] = self::heavyCompression($thisfile_ac3_raw_bsi['compr2']); + } + + $thisfile_ac3_raw_bsi['langcode_flag2'] = (bool) $this->readHeaderBSI(1); + if ($thisfile_ac3_raw_bsi['langcode_flag2']) { + $thisfile_ac3_raw_bsi['langcod2'] = $this->readHeaderBSI(8); + } + + $thisfile_ac3_raw_bsi['audprodie2'] = (bool) $this->readHeaderBSI(1); + if ($thisfile_ac3_raw_bsi['audprodie2']) { + $thisfile_ac3_raw_bsi['mixlevel2'] = $this->readHeaderBSI(5); + $thisfile_ac3_raw_bsi['roomtyp2'] = $this->readHeaderBSI(2); + + $thisfile_ac3['mixing_level2'] = (80 + $thisfile_ac3_raw_bsi['mixlevel2']).'dB'; + $thisfile_ac3['room_type2'] = self::roomTypeLookup($thisfile_ac3_raw_bsi['roomtyp2']); + } + + } + + $thisfile_ac3_raw_bsi['copyright'] = (bool) $this->readHeaderBSI(1); + + $thisfile_ac3_raw_bsi['original'] = (bool) $this->readHeaderBSI(1); + + $thisfile_ac3_raw_bsi['timecode1_flag'] = (bool) $this->readHeaderBSI(1); + if ($thisfile_ac3_raw_bsi['timecode1_flag']) { + $thisfile_ac3_raw_bsi['timecode1'] = $this->readHeaderBSI(14); + } + + $thisfile_ac3_raw_bsi['timecode2_flag'] = (bool) $this->readHeaderBSI(1); + if ($thisfile_ac3_raw_bsi['timecode2_flag']) { + $thisfile_ac3_raw_bsi['timecode2'] = $this->readHeaderBSI(14); + } + + $thisfile_ac3_raw_bsi['addbsi_flag'] = (bool) $this->readHeaderBSI(1); + if ($thisfile_ac3_raw_bsi['addbsi_flag']) { + $thisfile_ac3_raw_bsi['addbsi_length'] = $this->readHeaderBSI(6); + + $this->AC3header['bsi'] .= getid3_lib::BigEndian2Bin($this->fread($thisfile_ac3_raw_bsi['addbsi_length'])); + + $thisfile_ac3_raw_bsi['addbsi_data'] = substr($this->AC3header['bsi'], $this->BSIoffset, $thisfile_ac3_raw_bsi['addbsi_length'] * 8); + $this->BSIoffset += $thisfile_ac3_raw_bsi['addbsi_length'] * 8; + } + + return true; + } + + private function readHeaderBSI($length) { + $data = substr($this->AC3header['bsi'], $this->BSIoffset, $length); + $this->BSIoffset += $length; + + return bindec($data); + } + + public static function sampleRateCodeLookup($fscod) { + static $sampleRateCodeLookup = array( + 0 => 48000, + 1 => 44100, + 2 => 32000, + 3 => 'reserved' // If the reserved code is indicated, the decoder should not attempt to decode audio and should mute. + ); + return (isset($sampleRateCodeLookup[$fscod]) ? $sampleRateCodeLookup[$fscod] : false); + } + + public static function serviceTypeLookup($bsmod, $acmod) { + static $serviceTypeLookup = array(); + if (empty($serviceTypeLookup)) { + for ($i = 0; $i <= 7; $i++) { + $serviceTypeLookup[0][$i] = 'main audio service: complete main (CM)'; + $serviceTypeLookup[1][$i] = 'main audio service: music and effects (ME)'; + $serviceTypeLookup[2][$i] = 'associated service: visually impaired (VI)'; + $serviceTypeLookup[3][$i] = 'associated service: hearing impaired (HI)'; + $serviceTypeLookup[4][$i] = 'associated service: dialogue (D)'; + $serviceTypeLookup[5][$i] = 'associated service: commentary (C)'; + $serviceTypeLookup[6][$i] = 'associated service: emergency (E)'; + } + + $serviceTypeLookup[7][1] = 'associated service: voice over (VO)'; + for ($i = 2; $i <= 7; $i++) { + $serviceTypeLookup[7][$i] = 'main audio service: karaoke'; + } + } + return (isset($serviceTypeLookup[$bsmod][$acmod]) ? $serviceTypeLookup[$bsmod][$acmod] : false); + } + + public static function audioCodingModeLookup($acmod) { + // array(channel configuration, # channels (not incl LFE), channel order) + static $audioCodingModeLookup = array ( + 0 => array('channel_config'=>'1+1', 'num_channels'=>2, 'channel_order'=>'Ch1,Ch2'), + 1 => array('channel_config'=>'1/0', 'num_channels'=>1, 'channel_order'=>'C'), + 2 => array('channel_config'=>'2/0', 'num_channels'=>2, 'channel_order'=>'L,R'), + 3 => array('channel_config'=>'3/0', 'num_channels'=>3, 'channel_order'=>'L,C,R'), + 4 => array('channel_config'=>'2/1', 'num_channels'=>3, 'channel_order'=>'L,R,S'), + 5 => array('channel_config'=>'3/1', 'num_channels'=>4, 'channel_order'=>'L,C,R,S'), + 6 => array('channel_config'=>'2/2', 'num_channels'=>4, 'channel_order'=>'L,R,SL,SR'), + 7 => array('channel_config'=>'3/2', 'num_channels'=>5, 'channel_order'=>'L,C,R,SL,SR'), + ); + return (isset($audioCodingModeLookup[$acmod]) ? $audioCodingModeLookup[$acmod] : false); + } + + public static function centerMixLevelLookup($cmixlev) { + static $centerMixLevelLookup; + if (empty($centerMixLevelLookup)) { + $centerMixLevelLookup = array( + 0 => pow(2, -3.0 / 6), // 0.707 (-3.0 dB) + 1 => pow(2, -4.5 / 6), // 0.595 (-4.5 dB) + 2 => pow(2, -6.0 / 6), // 0.500 (-6.0 dB) + 3 => 'reserved' + ); + } + return (isset($centerMixLevelLookup[$cmixlev]) ? $centerMixLevelLookup[$cmixlev] : false); + } + + public static function surroundMixLevelLookup($surmixlev) { + static $surroundMixLevelLookup; + if (empty($surroundMixLevelLookup)) { + $surroundMixLevelLookup = array( + 0 => pow(2, -3.0 / 6), + 1 => pow(2, -6.0 / 6), + 2 => 0, + 3 => 'reserved' + ); + } + return (isset($surroundMixLevelLookup[$surmixlev]) ? $surroundMixLevelLookup[$surmixlev] : false); + } + + public static function dolbySurroundModeLookup($dsurmod) { + static $dolbySurroundModeLookup = array( + 0 => 'not indicated', + 1 => 'Not Dolby Surround encoded', + 2 => 'Dolby Surround encoded', + 3 => 'reserved' + ); + return (isset($dolbySurroundModeLookup[$dsurmod]) ? $dolbySurroundModeLookup[$dsurmod] : false); + } + + public static function channelsEnabledLookup($acmod, $lfeon) { + $lookup = array( + 'ch1'=>(bool) ($acmod == 0), + 'ch2'=>(bool) ($acmod == 0), + 'left'=>(bool) ($acmod > 1), + 'right'=>(bool) ($acmod > 1), + 'center'=>(bool) ($acmod & 0x01), + 'surround_mono'=>false, + 'surround_left'=>false, + 'surround_right'=>false, + 'lfe'=>$lfeon); + switch ($acmod) { + case 4: + case 5: + $lookup['surround_mono'] = true; + break; + case 6: + case 7: + $lookup['surround_left'] = true; + $lookup['surround_right'] = true; + break; + } + return $lookup; + } + + public static function heavyCompression($compre) { + // The first four bits indicate gain changes in 6.02dB increments which can be + // implemented with an arithmetic shift operation. The following four bits + // indicate linear gain changes, and require a 5-bit multiply. + // We will represent the two 4-bit fields of compr as follows: + // X0 X1 X2 X3 . Y4 Y5 Y6 Y7 + // The meaning of the X values is most simply described by considering X to represent a 4-bit + // signed integer with values from -8 to +7. The gain indicated by X is then (X + 1) * 6.02 dB. The + // following table shows this in detail. + + // Meaning of 4 msb of compr + // 7 +48.16 dB + // 6 +42.14 dB + // 5 +36.12 dB + // 4 +30.10 dB + // 3 +24.08 dB + // 2 +18.06 dB + // 1 +12.04 dB + // 0 +6.02 dB + // -1 0 dB + // -2 -6.02 dB + // -3 -12.04 dB + // -4 -18.06 dB + // -5 -24.08 dB + // -6 -30.10 dB + // -7 -36.12 dB + // -8 -42.14 dB + + $fourbit = str_pad(decbin(($compre & 0xF0) >> 4), 4, '0', STR_PAD_LEFT); + if ($fourbit{0} == '1') { + $log_gain = -8 + bindec(substr($fourbit, 1)); + } else { + $log_gain = bindec(substr($fourbit, 1)); + } + $log_gain = ($log_gain + 1) * getid3_lib::RGADamplitude2dB(2); + + // The value of Y is a linear representation of a gain change of up to -6 dB. Y is considered to + // be an unsigned fractional integer, with a leading value of 1, or: 0.1 Y4 Y5 Y6 Y7 (base 2). Y can + // represent values between 0.111112 (or 31/32) and 0.100002 (or 1/2). Thus, Y can represent gain + // changes from -0.28 dB to -6.02 dB. + + $lin_gain = (16 + ($compre & 0x0F)) / 32; + + // The combination of X and Y values allows compr to indicate gain changes from + // 48.16 - 0.28 = +47.89 dB, to + // -42.14 - 6.02 = -48.16 dB. + + return $log_gain - $lin_gain; + } + + public static function roomTypeLookup($roomtyp) { + static $roomTypeLookup = array( + 0 => 'not indicated', + 1 => 'large room, X curve monitor', + 2 => 'small room, flat monitor', + 3 => 'reserved' + ); + return (isset($roomTypeLookup[$roomtyp]) ? $roomTypeLookup[$roomtyp] : false); + } + + public static function frameSizeLookup($frmsizecod, $fscod) { + $padding = (bool) ($frmsizecod % 2); + $framesizeid = floor($frmsizecod / 2); + + static $frameSizeLookup = array(); + if (empty($frameSizeLookup)) { + $frameSizeLookup = array ( + 0 => array(128, 138, 192), + 1 => array(40, 160, 174, 240), + 2 => array(48, 192, 208, 288), + 3 => array(56, 224, 242, 336), + 4 => array(64, 256, 278, 384), + 5 => array(80, 320, 348, 480), + 6 => array(96, 384, 416, 576), + 7 => array(112, 448, 486, 672), + 8 => array(128, 512, 556, 768), + 9 => array(160, 640, 696, 960), + 10 => array(192, 768, 834, 1152), + 11 => array(224, 896, 974, 1344), + 12 => array(256, 1024, 1114, 1536), + 13 => array(320, 1280, 1392, 1920), + 14 => array(384, 1536, 1670, 2304), + 15 => array(448, 1792, 1950, 2688), + 16 => array(512, 2048, 2228, 3072), + 17 => array(576, 2304, 2506, 3456), + 18 => array(640, 2560, 2786, 3840) + ); + } + if (($fscod == 1) && $padding) { + // frame lengths are padded by 1 word (16 bits) at 44100 + $frameSizeLookup[$frmsizecod] += 2; + } + return (isset($frameSizeLookup[$framesizeid][$fscod]) ? $frameSizeLookup[$framesizeid][$fscod] : false); + } + + public static function bitrateLookup($frmsizecod) { + $framesizeid = floor($frmsizecod / 2); + + static $bitrateLookup = array( + 0 => 32000, + 1 => 40000, + 2 => 48000, + 3 => 56000, + 4 => 64000, + 5 => 80000, + 6 => 96000, + 7 => 112000, + 8 => 128000, + 9 => 160000, + 10 => 192000, + 11 => 224000, + 12 => 256000, + 13 => 320000, + 14 => 384000, + 15 => 448000, + 16 => 512000, + 17 => 576000, + 18 => 640000 + ); + return (isset($bitrateLookup[$framesizeid]) ? $bitrateLookup[$framesizeid] : false); + } + + +} \ No newline at end of file diff --git a/wp-includes/ID3/module.audio.dts.php b/wp-includes/ID3/module.audio.dts.php new file mode 100644 index 0000000..59831c8 --- /dev/null +++ b/wp-includes/ID3/module.audio.dts.php @@ -0,0 +1,291 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +// also https://github.com/JamesHeinrich/getID3 // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio.dts.php // +// module for analyzing DTS Audio files // +// dependencies: NONE // +// // +///////////////////////////////////////////////////////////////// + + +/** +* @tutorial http://wiki.multimedia.cx/index.php?title=DTS +*/ +class getid3_dts extends getid3_handler +{ + /** + * Default DTS syncword used in native .cpt or .dts formats + */ + const syncword = "\x7F\xFE\x80\x01"; + + private $readBinDataOffset = 0; + + /** + * Possible syncwords indicating bitstream encoding + */ + public static $syncwords = array( + 0 => "\x7F\xFE\x80\x01", // raw big-endian + 1 => "\xFE\x7F\x01\x80", // raw little-endian + 2 => "\x1F\xFF\xE8\x00", // 14-bit big-endian + 3 => "\xFF\x1F\x00\xE8"); // 14-bit little-endian + + public function Analyze() { + $info = &$this->getid3->info; + $info['fileformat'] = 'dts'; + + $this->fseek($info['avdataoffset']); + $DTSheader = $this->fread(20); // we only need 2 words magic + 6 words frame header, but these words may be normal 16-bit words OR 14-bit words with 2 highest bits set to zero, so 8 words can be either 8*16/8 = 16 bytes OR 8*16*(16/14)/8 = 18.3 bytes + + // check syncword + $sync = substr($DTSheader, 0, 4); + if (($encoding = array_search($sync, self::$syncwords)) !== false) { + + $info['dts']['raw']['magic'] = $sync; + $this->readBinDataOffset = 32; + + } elseif ($this->isDependencyFor('matroska')) { + + // Matroska contains DTS without syncword encoded as raw big-endian format + $encoding = 0; + $this->readBinDataOffset = 0; + + } else { + + unset($info['fileformat']); + return $this->error('Expecting "'.implode('| ', array_map('getid3_lib::PrintHexBytes', self::$syncwords)).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($sync).'"'); + + } + + // decode header + $fhBS = ''; + for ($word_offset = 0; $word_offset <= strlen($DTSheader); $word_offset += 2) { + switch ($encoding) { + case 0: // raw big-endian + $fhBS .= getid3_lib::BigEndian2Bin( substr($DTSheader, $word_offset, 2) ); + break; + case 1: // raw little-endian + $fhBS .= getid3_lib::BigEndian2Bin(strrev(substr($DTSheader, $word_offset, 2))); + break; + case 2: // 14-bit big-endian + $fhBS .= substr(getid3_lib::BigEndian2Bin( substr($DTSheader, $word_offset, 2) ), 2, 14); + break; + case 3: // 14-bit little-endian + $fhBS .= substr(getid3_lib::BigEndian2Bin(strrev(substr($DTSheader, $word_offset, 2))), 2, 14); + break; + } + } + + $info['dts']['raw']['frame_type'] = $this->readBinData($fhBS, 1); + $info['dts']['raw']['deficit_samples'] = $this->readBinData($fhBS, 5); + $info['dts']['flags']['crc_present'] = (bool) $this->readBinData($fhBS, 1); + $info['dts']['raw']['pcm_sample_blocks'] = $this->readBinData($fhBS, 7); + $info['dts']['raw']['frame_byte_size'] = $this->readBinData($fhBS, 14); + $info['dts']['raw']['channel_arrangement'] = $this->readBinData($fhBS, 6); + $info['dts']['raw']['sample_frequency'] = $this->readBinData($fhBS, 4); + $info['dts']['raw']['bitrate'] = $this->readBinData($fhBS, 5); + $info['dts']['flags']['embedded_downmix'] = (bool) $this->readBinData($fhBS, 1); + $info['dts']['flags']['dynamicrange'] = (bool) $this->readBinData($fhBS, 1); + $info['dts']['flags']['timestamp'] = (bool) $this->readBinData($fhBS, 1); + $info['dts']['flags']['auxdata'] = (bool) $this->readBinData($fhBS, 1); + $info['dts']['flags']['hdcd'] = (bool) $this->readBinData($fhBS, 1); + $info['dts']['raw']['extension_audio'] = $this->readBinData($fhBS, 3); + $info['dts']['flags']['extended_coding'] = (bool) $this->readBinData($fhBS, 1); + $info['dts']['flags']['audio_sync_insertion'] = (bool) $this->readBinData($fhBS, 1); + $info['dts']['raw']['lfe_effects'] = $this->readBinData($fhBS, 2); + $info['dts']['flags']['predictor_history'] = (bool) $this->readBinData($fhBS, 1); + if ($info['dts']['flags']['crc_present']) { + $info['dts']['raw']['crc16'] = $this->readBinData($fhBS, 16); + } + $info['dts']['flags']['mri_perfect_reconst'] = (bool) $this->readBinData($fhBS, 1); + $info['dts']['raw']['encoder_soft_version'] = $this->readBinData($fhBS, 4); + $info['dts']['raw']['copy_history'] = $this->readBinData($fhBS, 2); + $info['dts']['raw']['bits_per_sample'] = $this->readBinData($fhBS, 2); + $info['dts']['flags']['surround_es'] = (bool) $this->readBinData($fhBS, 1); + $info['dts']['flags']['front_sum_diff'] = (bool) $this->readBinData($fhBS, 1); + $info['dts']['flags']['surround_sum_diff'] = (bool) $this->readBinData($fhBS, 1); + $info['dts']['raw']['dialog_normalization'] = $this->readBinData($fhBS, 4); + + + $info['dts']['bitrate'] = self::bitrateLookup($info['dts']['raw']['bitrate']); + $info['dts']['bits_per_sample'] = self::bitPerSampleLookup($info['dts']['raw']['bits_per_sample']); + $info['dts']['sample_rate'] = self::sampleRateLookup($info['dts']['raw']['sample_frequency']); + $info['dts']['dialog_normalization'] = self::dialogNormalization($info['dts']['raw']['dialog_normalization'], $info['dts']['raw']['encoder_soft_version']); + $info['dts']['flags']['lossless'] = (($info['dts']['raw']['bitrate'] == 31) ? true : false); + $info['dts']['bitrate_mode'] = (($info['dts']['raw']['bitrate'] == 30) ? 'vbr' : 'cbr'); + $info['dts']['channels'] = self::numChannelsLookup($info['dts']['raw']['channel_arrangement']); + $info['dts']['channel_arrangement'] = self::channelArrangementLookup($info['dts']['raw']['channel_arrangement']); + + $info['audio']['dataformat'] = 'dts'; + $info['audio']['lossless'] = $info['dts']['flags']['lossless']; + $info['audio']['bitrate_mode'] = $info['dts']['bitrate_mode']; + $info['audio']['bits_per_sample'] = $info['dts']['bits_per_sample']; + $info['audio']['sample_rate'] = $info['dts']['sample_rate']; + $info['audio']['channels'] = $info['dts']['channels']; + $info['audio']['bitrate'] = $info['dts']['bitrate']; + if (isset($info['avdataend']) && !empty($info['dts']['bitrate']) && is_numeric($info['dts']['bitrate'])) { + $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) / ($info['dts']['bitrate'] / 8); + if (($encoding == 2) || ($encoding == 3)) { + // 14-bit data packed into 16-bit words, so the playtime is wrong because only (14/16) of the bytes in the data portion of the file are used at the specified bitrate + $info['playtime_seconds'] *= (14 / 16); + } + } + return true; + } + + private function readBinData($bin, $length) { + $data = substr($bin, $this->readBinDataOffset, $length); + $this->readBinDataOffset += $length; + + return bindec($data); + } + + public static function bitrateLookup($index) { + static $lookup = array( + 0 => 32000, + 1 => 56000, + 2 => 64000, + 3 => 96000, + 4 => 112000, + 5 => 128000, + 6 => 192000, + 7 => 224000, + 8 => 256000, + 9 => 320000, + 10 => 384000, + 11 => 448000, + 12 => 512000, + 13 => 576000, + 14 => 640000, + 15 => 768000, + 16 => 960000, + 17 => 1024000, + 18 => 1152000, + 19 => 1280000, + 20 => 1344000, + 21 => 1408000, + 22 => 1411200, + 23 => 1472000, + 24 => 1536000, + 25 => 1920000, + 26 => 2048000, + 27 => 3072000, + 28 => 3840000, + 29 => 'open', + 30 => 'variable', + 31 => 'lossless', + ); + return (isset($lookup[$index]) ? $lookup[$index] : false); + } + + public static function sampleRateLookup($index) { + static $lookup = array( + 0 => 'invalid', + 1 => 8000, + 2 => 16000, + 3 => 32000, + 4 => 'invalid', + 5 => 'invalid', + 6 => 11025, + 7 => 22050, + 8 => 44100, + 9 => 'invalid', + 10 => 'invalid', + 11 => 12000, + 12 => 24000, + 13 => 48000, + 14 => 'invalid', + 15 => 'invalid', + ); + return (isset($lookup[$index]) ? $lookup[$index] : false); + } + + public static function bitPerSampleLookup($index) { + static $lookup = array( + 0 => 16, + 1 => 20, + 2 => 24, + 3 => 24, + ); + return (isset($lookup[$index]) ? $lookup[$index] : false); + } + + public static function numChannelsLookup($index) { + switch ($index) { + case 0: + return 1; + break; + case 1: + case 2: + case 3: + case 4: + return 2; + break; + case 5: + case 6: + return 3; + break; + case 7: + case 8: + return 4; + break; + case 9: + return 5; + break; + case 10: + case 11: + case 12: + return 6; + break; + case 13: + return 7; + break; + case 14: + case 15: + return 8; + break; + } + return false; + } + + public static function channelArrangementLookup($index) { + static $lookup = array( + 0 => 'A', + 1 => 'A + B (dual mono)', + 2 => 'L + R (stereo)', + 3 => '(L+R) + (L-R) (sum-difference)', + 4 => 'LT + RT (left and right total)', + 5 => 'C + L + R', + 6 => 'L + R + S', + 7 => 'C + L + R + S', + 8 => 'L + R + SL + SR', + 9 => 'C + L + R + SL + SR', + 10 => 'CL + CR + L + R + SL + SR', + 11 => 'C + L + R+ LR + RR + OV', + 12 => 'CF + CR + LF + RF + LR + RR', + 13 => 'CL + C + CR + L + R + SL + SR', + 14 => 'CL + CR + L + R + SL1 + SL2 + SR1 + SR2', + 15 => 'CL + C+ CR + L + R + SL + S + SR', + ); + return (isset($lookup[$index]) ? $lookup[$index] : 'user-defined'); + } + + public static function dialogNormalization($index, $version) { + switch ($version) { + case 7: + return 0 - $index; + break; + case 6: + return 0 - 16 - $index; + break; + } + return false; + } + +} \ No newline at end of file diff --git a/wp-includes/ID3/module.audio.flac.php b/wp-includes/ID3/module.audio.flac.php new file mode 100644 index 0000000..8fa0f4c --- /dev/null +++ b/wp-includes/ID3/module.audio.flac.php @@ -0,0 +1,443 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +// also https://github.com/JamesHeinrich/getID3 // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio.flac.php // +// module for analyzing FLAC and OggFLAC audio files // +// dependencies: module.audio.ogg.php // +// /// +///////////////////////////////////////////////////////////////// + + +getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ogg.php', __FILE__, true); + +/** +* @tutorial http://flac.sourceforge.net/format.html +*/ +class getid3_flac extends getid3_handler +{ + const syncword = 'fLaC'; + + public function Analyze() { + $info = &$this->getid3->info; + + $this->fseek($info['avdataoffset']); + $StreamMarker = $this->fread(4); + if ($StreamMarker != self::syncword) { + return $this->error('Expecting "'.getid3_lib::PrintHexBytes(self::syncword).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($StreamMarker).'"'); + } + $info['fileformat'] = 'flac'; + $info['audio']['dataformat'] = 'flac'; + $info['audio']['bitrate_mode'] = 'vbr'; + $info['audio']['lossless'] = true; + + // parse flac container + return $this->parseMETAdata(); + } + + public function parseMETAdata() { + $info = &$this->getid3->info; + do { + $BlockOffset = $this->ftell(); + $BlockHeader = $this->fread(4); + $LBFBT = getid3_lib::BigEndian2Int(substr($BlockHeader, 0, 1)); + $LastBlockFlag = (bool) ($LBFBT & 0x80); + $BlockType = ($LBFBT & 0x7F); + $BlockLength = getid3_lib::BigEndian2Int(substr($BlockHeader, 1, 3)); + $BlockTypeText = self::metaBlockTypeLookup($BlockType); + + if (($BlockOffset + 4 + $BlockLength) > $info['avdataend']) { + $this->error('METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$BlockTypeText.') at offset '.$BlockOffset.' extends beyond end of file'); + break; + } + if ($BlockLength < 1) { + $this->error('METADATA_BLOCK_HEADER.BLOCK_LENGTH ('.$BlockLength.') at offset '.$BlockOffset.' is invalid'); + break; + } + + $info['flac'][$BlockTypeText]['raw'] = array(); + $BlockTypeText_raw = &$info['flac'][$BlockTypeText]['raw']; + + $BlockTypeText_raw['offset'] = $BlockOffset; + $BlockTypeText_raw['last_meta_block'] = $LastBlockFlag; + $BlockTypeText_raw['block_type'] = $BlockType; + $BlockTypeText_raw['block_type_text'] = $BlockTypeText; + $BlockTypeText_raw['block_length'] = $BlockLength; + if ($BlockTypeText_raw['block_type'] != 0x06) { // do not read attachment data automatically + $BlockTypeText_raw['block_data'] = $this->fread($BlockLength); + } + + switch ($BlockTypeText) { + case 'STREAMINFO': // 0x00 + if (!$this->parseSTREAMINFO($BlockTypeText_raw['block_data'])) { + return false; + } + break; + + case 'PADDING': // 0x01 + unset($info['flac']['PADDING']); // ignore + break; + + case 'APPLICATION': // 0x02 + if (!$this->parseAPPLICATION($BlockTypeText_raw['block_data'])) { + return false; + } + break; + + case 'SEEKTABLE': // 0x03 + if (!$this->parseSEEKTABLE($BlockTypeText_raw['block_data'])) { + return false; + } + break; + + case 'VORBIS_COMMENT': // 0x04 + if (!$this->parseVORBIS_COMMENT($BlockTypeText_raw['block_data'])) { + return false; + } + break; + + case 'CUESHEET': // 0x05 + if (!$this->parseCUESHEET($BlockTypeText_raw['block_data'])) { + return false; + } + break; + + case 'PICTURE': // 0x06 + if (!$this->parsePICTURE()) { + return false; + } + break; + + default: + $this->warning('Unhandled METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$BlockType.') at offset '.$BlockOffset); + } + + unset($info['flac'][$BlockTypeText]['raw']); + $info['avdataoffset'] = $this->ftell(); + } + while ($LastBlockFlag === false); + + // handle tags + if (!empty($info['flac']['VORBIS_COMMENT']['comments'])) { + $info['flac']['comments'] = $info['flac']['VORBIS_COMMENT']['comments']; + } + if (!empty($info['flac']['VORBIS_COMMENT']['vendor'])) { + $info['audio']['encoder'] = str_replace('reference ', '', $info['flac']['VORBIS_COMMENT']['vendor']); + } + + // copy attachments to 'comments' array if nesesary + if (isset($info['flac']['PICTURE']) && ($this->getid3->option_save_attachments !== getID3::ATTACHMENTS_NONE)) { + foreach ($info['flac']['PICTURE'] as $entry) { + if (!empty($entry['data'])) { + $info['flac']['comments']['picture'][] = array('image_mime'=>$entry['image_mime'], 'data'=>$entry['data']); + } + } + } + + if (isset($info['flac']['STREAMINFO'])) { + if (!$this->isDependencyFor('matroska')) { + $info['flac']['compressed_audio_bytes'] = $info['avdataend'] - $info['avdataoffset']; + } + $info['flac']['uncompressed_audio_bytes'] = $info['flac']['STREAMINFO']['samples_stream'] * $info['flac']['STREAMINFO']['channels'] * ($info['flac']['STREAMINFO']['bits_per_sample'] / 8); + if ($info['flac']['uncompressed_audio_bytes'] == 0) { + return $this->error('Corrupt FLAC file: uncompressed_audio_bytes == zero'); + } + if (!empty($info['flac']['compressed_audio_bytes'])) { + $info['flac']['compression_ratio'] = $info['flac']['compressed_audio_bytes'] / $info['flac']['uncompressed_audio_bytes']; + } + } + + // set md5_data_source - built into flac 0.5+ + if (isset($info['flac']['STREAMINFO']['audio_signature'])) { + + if ($info['flac']['STREAMINFO']['audio_signature'] === str_repeat("\x00", 16)) { + $this->warning('FLAC STREAMINFO.audio_signature is null (known issue with libOggFLAC)'); + } + else { + $info['md5_data_source'] = ''; + $md5 = $info['flac']['STREAMINFO']['audio_signature']; + for ($i = 0; $i < strlen($md5); $i++) { + $info['md5_data_source'] .= str_pad(dechex(ord($md5[$i])), 2, '00', STR_PAD_LEFT); + } + if (!preg_match('/^[0-9a-f]{32}$/', $info['md5_data_source'])) { + unset($info['md5_data_source']); + } + } + } + + if (isset($info['flac']['STREAMINFO']['bits_per_sample'])) { + $info['audio']['bits_per_sample'] = $info['flac']['STREAMINFO']['bits_per_sample']; + if ($info['audio']['bits_per_sample'] == 8) { + // special case + // must invert sign bit on all data bytes before MD5'ing to match FLAC's calculated value + // MD5sum calculates on unsigned bytes, but FLAC calculated MD5 on 8-bit audio data as signed + $this->warning('FLAC calculates MD5 data strangely on 8-bit audio, so the stored md5_data_source value will not match the decoded WAV file'); + } + } + + return true; + } + + private function parseSTREAMINFO($BlockData) { + $info = &$this->getid3->info; + + $info['flac']['STREAMINFO'] = array(); + $streaminfo = &$info['flac']['STREAMINFO']; + + $streaminfo['min_block_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 0, 2)); + $streaminfo['max_block_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 2, 2)); + $streaminfo['min_frame_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 4, 3)); + $streaminfo['max_frame_size'] = getid3_lib::BigEndian2Int(substr($BlockData, 7, 3)); + + $SRCSBSS = getid3_lib::BigEndian2Bin(substr($BlockData, 10, 8)); + $streaminfo['sample_rate'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 0, 20)); + $streaminfo['channels'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 20, 3)) + 1; + $streaminfo['bits_per_sample'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 23, 5)) + 1; + $streaminfo['samples_stream'] = getid3_lib::Bin2Dec(substr($SRCSBSS, 28, 36)); + + $streaminfo['audio_signature'] = substr($BlockData, 18, 16); + + if (!empty($streaminfo['sample_rate'])) { + + $info['audio']['bitrate_mode'] = 'vbr'; + $info['audio']['sample_rate'] = $streaminfo['sample_rate']; + $info['audio']['channels'] = $streaminfo['channels']; + $info['audio']['bits_per_sample'] = $streaminfo['bits_per_sample']; + $info['playtime_seconds'] = $streaminfo['samples_stream'] / $streaminfo['sample_rate']; + if ($info['playtime_seconds'] > 0) { + if (!$this->isDependencyFor('matroska')) { + $info['audio']['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; + } + else { + $this->warning('Cannot determine audio bitrate because total stream size is unknown'); + } + } + + } else { + return $this->error('Corrupt METAdata block: STREAMINFO'); + } + + return true; + } + + private function parseAPPLICATION($BlockData) { + $info = &$this->getid3->info; + + $ApplicationID = getid3_lib::BigEndian2Int(substr($BlockData, 0, 4)); + $info['flac']['APPLICATION'][$ApplicationID]['name'] = self::applicationIDLookup($ApplicationID); + $info['flac']['APPLICATION'][$ApplicationID]['data'] = substr($BlockData, 4); + + return true; + } + + private function parseSEEKTABLE($BlockData) { + $info = &$this->getid3->info; + + $offset = 0; + $BlockLength = strlen($BlockData); + $placeholderpattern = str_repeat("\xFF", 8); + while ($offset < $BlockLength) { + $SampleNumberString = substr($BlockData, $offset, 8); + $offset += 8; + if ($SampleNumberString == $placeholderpattern) { + + // placeholder point + getid3_lib::safe_inc($info['flac']['SEEKTABLE']['placeholders'], 1); + $offset += 10; + + } else { + + $SampleNumber = getid3_lib::BigEndian2Int($SampleNumberString); + $info['flac']['SEEKTABLE'][$SampleNumber]['offset'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 8)); + $offset += 8; + $info['flac']['SEEKTABLE'][$SampleNumber]['samples'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 2)); + $offset += 2; + + } + } + + return true; + } + + private function parseVORBIS_COMMENT($BlockData) { + $info = &$this->getid3->info; + + $getid3_ogg = new getid3_ogg($this->getid3); + if ($this->isDependencyFor('matroska')) { + $getid3_ogg->setStringMode($this->data_string); + } + $getid3_ogg->ParseVorbisComments(); + if (isset($info['ogg'])) { + unset($info['ogg']['comments_raw']); + $info['flac']['VORBIS_COMMENT'] = $info['ogg']; + unset($info['ogg']); + } + + unset($getid3_ogg); + + return true; + } + + private function parseCUESHEET($BlockData) { + $info = &$this->getid3->info; + $offset = 0; + $info['flac']['CUESHEET']['media_catalog_number'] = trim(substr($BlockData, $offset, 128), "\0"); + $offset += 128; + $info['flac']['CUESHEET']['lead_in_samples'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 8)); + $offset += 8; + $info['flac']['CUESHEET']['flags']['is_cd'] = (bool) (getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1)) & 0x80); + $offset += 1; + + $offset += 258; // reserved + + $info['flac']['CUESHEET']['number_tracks'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1)); + $offset += 1; + + for ($track = 0; $track < $info['flac']['CUESHEET']['number_tracks']; $track++) { + $TrackSampleOffset = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 8)); + $offset += 8; + $TrackNumber = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1)); + $offset += 1; + + $info['flac']['CUESHEET']['tracks'][$TrackNumber]['sample_offset'] = $TrackSampleOffset; + + $info['flac']['CUESHEET']['tracks'][$TrackNumber]['isrc'] = substr($BlockData, $offset, 12); + $offset += 12; + + $TrackFlagsRaw = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1)); + $offset += 1; + $info['flac']['CUESHEET']['tracks'][$TrackNumber]['flags']['is_audio'] = (bool) ($TrackFlagsRaw & 0x80); + $info['flac']['CUESHEET']['tracks'][$TrackNumber]['flags']['pre_emphasis'] = (bool) ($TrackFlagsRaw & 0x40); + + $offset += 13; // reserved + + $info['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points'] = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1)); + $offset += 1; + + for ($index = 0; $index < $info['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points']; $index++) { + $IndexSampleOffset = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 8)); + $offset += 8; + $IndexNumber = getid3_lib::BigEndian2Int(substr($BlockData, $offset, 1)); + $offset += 1; + + $offset += 3; // reserved + + $info['flac']['CUESHEET']['tracks'][$TrackNumber]['indexes'][$IndexNumber] = $IndexSampleOffset; + } + } + + return true; + } + + /** + * Parse METADATA_BLOCK_PICTURE flac structure and extract attachment + * External usage: audio.ogg + */ + public function parsePICTURE() { + $info = &$this->getid3->info; + + $picture['typeid'] = getid3_lib::BigEndian2Int($this->fread(4)); + $picture['type'] = self::pictureTypeLookup($picture['typeid']); + $picture['image_mime'] = $this->fread(getid3_lib::BigEndian2Int($this->fread(4))); + $descr_length = getid3_lib::BigEndian2Int($this->fread(4)); + if ($descr_length) { + $picture['description'] = $this->fread($descr_length); + } + $picture['width'] = getid3_lib::BigEndian2Int($this->fread(4)); + $picture['height'] = getid3_lib::BigEndian2Int($this->fread(4)); + $picture['color_depth'] = getid3_lib::BigEndian2Int($this->fread(4)); + $picture['colors_indexed'] = getid3_lib::BigEndian2Int($this->fread(4)); + $data_length = getid3_lib::BigEndian2Int($this->fread(4)); + + if ($picture['image_mime'] == '-->') { + $picture['data'] = $this->fread($data_length); + } else { + $picture['data'] = $this->saveAttachment( + str_replace('/', '_', $picture['type']).'_'.$this->ftell(), + $this->ftell(), + $data_length, + $picture['image_mime']); + } + + $info['flac']['PICTURE'][] = $picture; + + return true; + } + + public static function metaBlockTypeLookup($blocktype) { + static $lookup = array( + 0 => 'STREAMINFO', + 1 => 'PADDING', + 2 => 'APPLICATION', + 3 => 'SEEKTABLE', + 4 => 'VORBIS_COMMENT', + 5 => 'CUESHEET', + 6 => 'PICTURE', + ); + return (isset($lookup[$blocktype]) ? $lookup[$blocktype] : 'reserved'); + } + + public static function applicationIDLookup($applicationid) { + // http://flac.sourceforge.net/id.html + static $lookup = array( + 0x41544348 => 'FlacFile', // "ATCH" + 0x42534F4C => 'beSolo', // "BSOL" + 0x42554753 => 'Bugs Player', // "BUGS" + 0x43756573 => 'GoldWave cue points (specification)', // "Cues" + 0x46696361 => 'CUE Splitter', // "Fica" + 0x46746F6C => 'flac-tools', // "Ftol" + 0x4D4F5442 => 'MOTB MetaCzar', // "MOTB" + 0x4D505345 => 'MP3 Stream Editor', // "MPSE" + 0x4D754D4C => 'MusicML: Music Metadata Language', // "MuML" + 0x52494646 => 'Sound Devices RIFF chunk storage', // "RIFF" + 0x5346464C => 'Sound Font FLAC', // "SFFL" + 0x534F4E59 => 'Sony Creative Software', // "SONY" + 0x5351455A => 'flacsqueeze', // "SQEZ" + 0x54745776 => 'TwistedWave', // "TtWv" + 0x55495453 => 'UITS Embedding tools', // "UITS" + 0x61696666 => 'FLAC AIFF chunk storage', // "aiff" + 0x696D6167 => 'flac-image application for storing arbitrary files in APPLICATION metadata blocks', // "imag" + 0x7065656D => 'Parseable Embedded Extensible Metadata (specification)', // "peem" + 0x71667374 => 'QFLAC Studio', // "qfst" + 0x72696666 => 'FLAC RIFF chunk storage', // "riff" + 0x74756E65 => 'TagTuner', // "tune" + 0x78626174 => 'XBAT', // "xbat" + 0x786D6364 => 'xmcd', // "xmcd" + ); + return (isset($lookup[$applicationid]) ? $lookup[$applicationid] : 'reserved'); + } + + public static function pictureTypeLookup($type_id) { + static $lookup = array ( + 0 => 'Other', + 1 => '32x32 pixels \'file icon\' (PNG only)', + 2 => 'Other file icon', + 3 => 'Cover (front)', + 4 => 'Cover (back)', + 5 => 'Leaflet page', + 6 => 'Media (e.g. label side of CD)', + 7 => 'Lead artist/lead performer/soloist', + 8 => 'Artist/performer', + 9 => 'Conductor', + 10 => 'Band/Orchestra', + 11 => 'Composer', + 12 => 'Lyricist/text writer', + 13 => 'Recording Location', + 14 => 'During recording', + 15 => 'During performance', + 16 => 'Movie/video screen capture', + 17 => 'A bright coloured fish', + 18 => 'Illustration', + 19 => 'Band/artist logotype', + 20 => 'Publisher/Studio logotype', + ); + return (isset($lookup[$type_id]) ? $lookup[$type_id] : 'reserved'); + } + +} \ No newline at end of file diff --git a/wp-includes/ID3/module.audio.mp3.php b/wp-includes/ID3/module.audio.mp3.php new file mode 100644 index 0000000..98877a9 --- /dev/null +++ b/wp-includes/ID3/module.audio.mp3.php @@ -0,0 +1,2012 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +// also https://github.com/JamesHeinrich/getID3 // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio.mp3.php // +// module for analyzing MP3 files // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +// number of frames to scan to determine if MPEG-audio sequence is valid +// Lower this number to 5-20 for faster scanning +// Increase this number to 50+ for most accurate detection of valid VBR/CBR +// mpeg-audio streams +define('GETID3_MP3_VALID_CHECK_FRAMES', 35); + + +class getid3_mp3 extends getid3_handler +{ + + public $allow_bruteforce = false; // forces getID3() to scan the file byte-by-byte and log all the valid audio frame headers - extremely slow, unrecommended, but may provide data from otherwise-unusuable files + + public function Analyze() { + $info = &$this->getid3->info; + + $initialOffset = $info['avdataoffset']; + + if (!$this->getOnlyMPEGaudioInfo($info['avdataoffset'])) { + if ($this->allow_bruteforce) { + $info['error'][] = 'Rescanning file in BruteForce mode'; + $this->getOnlyMPEGaudioInfoBruteForce($this->getid3->fp, $info); + } + } + + + if (isset($info['mpeg']['audio']['bitrate_mode'])) { + $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']); + } + + if (((isset($info['id3v2']['headerlength']) && ($info['avdataoffset'] > $info['id3v2']['headerlength'])) || (!isset($info['id3v2']) && ($info['avdataoffset'] > 0) && ($info['avdataoffset'] != $initialOffset)))) { + + $synchoffsetwarning = 'Unknown data before synch '; + if (isset($info['id3v2']['headerlength'])) { + $synchoffsetwarning .= '(ID3v2 header ends at '.$info['id3v2']['headerlength'].', then '.($info['avdataoffset'] - $info['id3v2']['headerlength']).' bytes garbage, '; + } elseif ($initialOffset > 0) { + $synchoffsetwarning .= '(should be at '.$initialOffset.', '; + } else { + $synchoffsetwarning .= '(should be at beginning of file, '; + } + $synchoffsetwarning .= 'synch detected at '.$info['avdataoffset'].')'; + if (isset($info['audio']['bitrate_mode']) && ($info['audio']['bitrate_mode'] == 'cbr')) { + + if (!empty($info['id3v2']['headerlength']) && (($info['avdataoffset'] - $info['id3v2']['headerlength']) == $info['mpeg']['audio']['framelength'])) { + + $synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90-3.92) DLL in CBR mode.'; + $info['audio']['codec'] = 'LAME'; + $CurrentDataLAMEversionString = 'LAME3.'; + + } elseif (empty($info['id3v2']['headerlength']) && ($info['avdataoffset'] == $info['mpeg']['audio']['framelength'])) { + + $synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90 - 3.92) DLL in CBR mode.'; + $info['audio']['codec'] = 'LAME'; + $CurrentDataLAMEversionString = 'LAME3.'; + + } + + } + $info['warning'][] = $synchoffsetwarning; + + } + + if (isset($info['mpeg']['audio']['LAME'])) { + $info['audio']['codec'] = 'LAME'; + if (!empty($info['mpeg']['audio']['LAME']['long_version'])) { + $info['audio']['encoder'] = rtrim($info['mpeg']['audio']['LAME']['long_version'], "\x00"); + } elseif (!empty($info['mpeg']['audio']['LAME']['short_version'])) { + $info['audio']['encoder'] = rtrim($info['mpeg']['audio']['LAME']['short_version'], "\x00"); + } + } + + $CurrentDataLAMEversionString = (!empty($CurrentDataLAMEversionString) ? $CurrentDataLAMEversionString : (isset($info['audio']['encoder']) ? $info['audio']['encoder'] : '')); + if (!empty($CurrentDataLAMEversionString) && (substr($CurrentDataLAMEversionString, 0, 6) == 'LAME3.') && !preg_match('[0-9\)]', substr($CurrentDataLAMEversionString, -1))) { + // a version number of LAME that does not end with a number like "LAME3.92" + // or with a closing parenthesis like "LAME3.88 (alpha)" + // or a version of LAME with the LAMEtag-not-filled-in-DLL-mode bug (3.90-3.92) + + // not sure what the actual last frame length will be, but will be less than or equal to 1441 + $PossiblyLongerLAMEversion_FrameLength = 1441; + + // Not sure what version of LAME this is - look in padding of last frame for longer version string + $PossibleLAMEversionStringOffset = $info['avdataend'] - $PossiblyLongerLAMEversion_FrameLength; + $this->fseek($PossibleLAMEversionStringOffset); + $PossiblyLongerLAMEversion_Data = $this->fread($PossiblyLongerLAMEversion_FrameLength); + switch (substr($CurrentDataLAMEversionString, -1)) { + case 'a': + case 'b': + // "LAME3.94a" will have a longer version string of "LAME3.94 (alpha)" for example + // need to trim off "a" to match longer string + $CurrentDataLAMEversionString = substr($CurrentDataLAMEversionString, 0, -1); + break; + } + if (($PossiblyLongerLAMEversion_String = strstr($PossiblyLongerLAMEversion_Data, $CurrentDataLAMEversionString)) !== false) { + if (substr($PossiblyLongerLAMEversion_String, 0, strlen($CurrentDataLAMEversionString)) == $CurrentDataLAMEversionString) { + $PossiblyLongerLAMEversion_NewString = substr($PossiblyLongerLAMEversion_String, 0, strspn($PossiblyLongerLAMEversion_String, 'LAME0123456789., (abcdefghijklmnopqrstuvwxyzJFSOND)')); //"LAME3.90.3" "LAME3.87 (beta 1, Sep 27 2000)" "LAME3.88 (beta)" + if (empty($info['audio']['encoder']) || (strlen($PossiblyLongerLAMEversion_NewString) > strlen($info['audio']['encoder']))) { + $info['audio']['encoder'] = $PossiblyLongerLAMEversion_NewString; + } + } + } + } + if (!empty($info['audio']['encoder'])) { + $info['audio']['encoder'] = rtrim($info['audio']['encoder'], "\x00 "); + } + + switch (isset($info['mpeg']['audio']['layer']) ? $info['mpeg']['audio']['layer'] : '') { + case 1: + case 2: + $info['audio']['dataformat'] = 'mp'.$info['mpeg']['audio']['layer']; + break; + } + if (isset($info['fileformat']) && ($info['fileformat'] == 'mp3')) { + switch ($info['audio']['dataformat']) { + case 'mp1': + case 'mp2': + case 'mp3': + $info['fileformat'] = $info['audio']['dataformat']; + break; + + default: + $info['warning'][] = 'Expecting [audio][dataformat] to be mp1/mp2/mp3 when fileformat == mp3, [audio][dataformat] actually "'.$info['audio']['dataformat'].'"'; + break; + } + } + + if (empty($info['fileformat'])) { + unset($info['fileformat']); + unset($info['audio']['bitrate_mode']); + unset($info['avdataoffset']); + unset($info['avdataend']); + return false; + } + + $info['mime_type'] = 'audio/mpeg'; + $info['audio']['lossless'] = false; + + // Calculate playtime + if (!isset($info['playtime_seconds']) && isset($info['audio']['bitrate']) && ($info['audio']['bitrate'] > 0)) { + $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['audio']['bitrate']; + } + + $info['audio']['encoder_options'] = $this->GuessEncoderOptions(); + + return true; + } + + + public function GuessEncoderOptions() { + // shortcuts + $info = &$this->getid3->info; + if (!empty($info['mpeg']['audio'])) { + $thisfile_mpeg_audio = &$info['mpeg']['audio']; + if (!empty($thisfile_mpeg_audio['LAME'])) { + $thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME']; + } + } + + $encoder_options = ''; + static $NamedPresetBitrates = array(16, 24, 40, 56, 112, 128, 160, 192, 256); + + if (isset($thisfile_mpeg_audio['VBR_method']) && ($thisfile_mpeg_audio['VBR_method'] == 'Fraunhofer') && !empty($thisfile_mpeg_audio['VBR_quality'])) { + + $encoder_options = 'VBR q'.$thisfile_mpeg_audio['VBR_quality']; + + } elseif (!empty($thisfile_mpeg_audio_lame['preset_used']) && (!in_array($thisfile_mpeg_audio_lame['preset_used_id'], $NamedPresetBitrates))) { + + $encoder_options = $thisfile_mpeg_audio_lame['preset_used']; + + } elseif (!empty($thisfile_mpeg_audio_lame['vbr_quality'])) { + + static $KnownEncoderValues = array(); + if (empty($KnownEncoderValues)) { + + //$KnownEncoderValues[abrbitrate_minbitrate][vbr_quality][raw_vbr_method][raw_noise_shaping][raw_stereo_mode][ath_type][lowpass_frequency] = 'preset name'; + $KnownEncoderValues[0xFF][58][1][1][3][2][20500] = '--alt-preset insane'; // 3.90, 3.90.1, 3.92 + $KnownEncoderValues[0xFF][58][1][1][3][2][20600] = '--alt-preset insane'; // 3.90.2, 3.90.3, 3.91 + $KnownEncoderValues[0xFF][57][1][1][3][4][20500] = '--alt-preset insane'; // 3.94, 3.95 + $KnownEncoderValues['**'][78][3][2][3][2][19500] = '--alt-preset extreme'; // 3.90, 3.90.1, 3.92 + $KnownEncoderValues['**'][78][3][2][3][2][19600] = '--alt-preset extreme'; // 3.90.2, 3.91 + $KnownEncoderValues['**'][78][3][1][3][2][19600] = '--alt-preset extreme'; // 3.90.3 + $KnownEncoderValues['**'][78][4][2][3][2][19500] = '--alt-preset fast extreme'; // 3.90, 3.90.1, 3.92 + $KnownEncoderValues['**'][78][4][2][3][2][19600] = '--alt-preset fast extreme'; // 3.90.2, 3.90.3, 3.91 + $KnownEncoderValues['**'][78][3][2][3][4][19000] = '--alt-preset standard'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 + $KnownEncoderValues['**'][78][3][1][3][4][19000] = '--alt-preset standard'; // 3.90.3 + $KnownEncoderValues['**'][78][4][2][3][4][19000] = '--alt-preset fast standard'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 + $KnownEncoderValues['**'][78][4][1][3][4][19000] = '--alt-preset fast standard'; // 3.90.3 + $KnownEncoderValues['**'][88][4][1][3][3][19500] = '--r3mix'; // 3.90, 3.90.1, 3.92 + $KnownEncoderValues['**'][88][4][1][3][3][19600] = '--r3mix'; // 3.90.2, 3.90.3, 3.91 + $KnownEncoderValues['**'][67][4][1][3][4][18000] = '--r3mix'; // 3.94, 3.95 + $KnownEncoderValues['**'][68][3][2][3][4][18000] = '--alt-preset medium'; // 3.90.3 + $KnownEncoderValues['**'][68][4][2][3][4][18000] = '--alt-preset fast medium'; // 3.90.3 + + $KnownEncoderValues[0xFF][99][1][1][1][2][0] = '--preset studio'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 + $KnownEncoderValues[0xFF][58][2][1][3][2][20600] = '--preset studio'; // 3.90.3, 3.93.1 + $KnownEncoderValues[0xFF][58][2][1][3][2][20500] = '--preset studio'; // 3.93 + $KnownEncoderValues[0xFF][57][2][1][3][4][20500] = '--preset studio'; // 3.94, 3.95 + $KnownEncoderValues[0xC0][88][1][1][1][2][0] = '--preset cd'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 + $KnownEncoderValues[0xC0][58][2][2][3][2][19600] = '--preset cd'; // 3.90.3, 3.93.1 + $KnownEncoderValues[0xC0][58][2][2][3][2][19500] = '--preset cd'; // 3.93 + $KnownEncoderValues[0xC0][57][2][1][3][4][19500] = '--preset cd'; // 3.94, 3.95 + $KnownEncoderValues[0xA0][78][1][1][3][2][18000] = '--preset hifi'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 + $KnownEncoderValues[0xA0][58][2][2][3][2][18000] = '--preset hifi'; // 3.90.3, 3.93, 3.93.1 + $KnownEncoderValues[0xA0][57][2][1][3][4][18000] = '--preset hifi'; // 3.94, 3.95 + $KnownEncoderValues[0x80][67][1][1][3][2][18000] = '--preset tape'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 + $KnownEncoderValues[0x80][67][1][1][3][2][15000] = '--preset radio'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 + $KnownEncoderValues[0x70][67][1][1][3][2][15000] = '--preset fm'; // 3.90, 3.90.1, 3.90.2, 3.91, 3.92 + $KnownEncoderValues[0x70][58][2][2][3][2][16000] = '--preset tape/radio/fm'; // 3.90.3, 3.93, 3.93.1 + $KnownEncoderValues[0x70][57][2][1][3][4][16000] = '--preset tape/radio/fm'; // 3.94, 3.95 + $KnownEncoderValues[0x38][58][2][2][0][2][10000] = '--preset voice'; // 3.90.3, 3.93, 3.93.1 + $KnownEncoderValues[0x38][57][2][1][0][4][15000] = '--preset voice'; // 3.94, 3.95 + $KnownEncoderValues[0x38][57][2][1][0][4][16000] = '--preset voice'; // 3.94a14 + $KnownEncoderValues[0x28][65][1][1][0][2][7500] = '--preset mw-us'; // 3.90, 3.90.1, 3.92 + $KnownEncoderValues[0x28][65][1][1][0][2][7600] = '--preset mw-us'; // 3.90.2, 3.91 + $KnownEncoderValues[0x28][58][2][2][0][2][7000] = '--preset mw-us'; // 3.90.3, 3.93, 3.93.1 + $KnownEncoderValues[0x28][57][2][1][0][4][10500] = '--preset mw-us'; // 3.94, 3.95 + $KnownEncoderValues[0x28][57][2][1][0][4][11200] = '--preset mw-us'; // 3.94a14 + $KnownEncoderValues[0x28][57][2][1][0][4][8800] = '--preset mw-us'; // 3.94a15 + $KnownEncoderValues[0x18][58][2][2][0][2][4000] = '--preset phon+/lw/mw-eu/sw'; // 3.90.3, 3.93.1 + $KnownEncoderValues[0x18][58][2][2][0][2][3900] = '--preset phon+/lw/mw-eu/sw'; // 3.93 + $KnownEncoderValues[0x18][57][2][1][0][4][5900] = '--preset phon+/lw/mw-eu/sw'; // 3.94, 3.95 + $KnownEncoderValues[0x18][57][2][1][0][4][6200] = '--preset phon+/lw/mw-eu/sw'; // 3.94a14 + $KnownEncoderValues[0x18][57][2][1][0][4][3200] = '--preset phon+/lw/mw-eu/sw'; // 3.94a15 + $KnownEncoderValues[0x10][58][2][2][0][2][3800] = '--preset phone'; // 3.90.3, 3.93.1 + $KnownEncoderValues[0x10][58][2][2][0][2][3700] = '--preset phone'; // 3.93 + $KnownEncoderValues[0x10][57][2][1][0][4][5600] = '--preset phone'; // 3.94, 3.95 + } + + if (isset($KnownEncoderValues[$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']])) { + + $encoder_options = $KnownEncoderValues[$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']]; + + } elseif (isset($KnownEncoderValues['**'][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']])) { + + $encoder_options = $KnownEncoderValues['**'][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']]; + + } elseif ($info['audio']['bitrate_mode'] == 'vbr') { + + // http://gabriel.mp3-tech.org/mp3infotag.html + // int Quality = (100 - 10 * gfp->VBR_q - gfp->quality)h + + + $LAME_V_value = 10 - ceil($thisfile_mpeg_audio_lame['vbr_quality'] / 10); + $LAME_q_value = 100 - $thisfile_mpeg_audio_lame['vbr_quality'] - ($LAME_V_value * 10); + $encoder_options = '-V'.$LAME_V_value.' -q'.$LAME_q_value; + + } elseif ($info['audio']['bitrate_mode'] == 'cbr') { + + $encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000); + + } else { + + $encoder_options = strtoupper($info['audio']['bitrate_mode']); + + } + + } elseif (!empty($thisfile_mpeg_audio_lame['bitrate_abr'])) { + + $encoder_options = 'ABR'.$thisfile_mpeg_audio_lame['bitrate_abr']; + + } elseif (!empty($info['audio']['bitrate'])) { + + if ($info['audio']['bitrate_mode'] == 'cbr') { + $encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000); + } else { + $encoder_options = strtoupper($info['audio']['bitrate_mode']); + } + + } + if (!empty($thisfile_mpeg_audio_lame['bitrate_min'])) { + $encoder_options .= ' -b'.$thisfile_mpeg_audio_lame['bitrate_min']; + } + + if (!empty($thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev']) || !empty($thisfile_mpeg_audio_lame['encoding_flags']['nogap_next'])) { + $encoder_options .= ' --nogap'; + } + + if (!empty($thisfile_mpeg_audio_lame['lowpass_frequency'])) { + $ExplodedOptions = explode(' ', $encoder_options, 4); + if ($ExplodedOptions[0] == '--r3mix') { + $ExplodedOptions[1] = 'r3mix'; + } + switch ($ExplodedOptions[0]) { + case '--preset': + case '--alt-preset': + case '--r3mix': + if ($ExplodedOptions[1] == 'fast') { + $ExplodedOptions[1] .= ' '.$ExplodedOptions[2]; + } + switch ($ExplodedOptions[1]) { + case 'portable': + case 'medium': + case 'standard': + case 'extreme': + case 'insane': + case 'fast portable': + case 'fast medium': + case 'fast standard': + case 'fast extreme': + case 'fast insane': + case 'r3mix': + static $ExpectedLowpass = array( + 'insane|20500' => 20500, + 'insane|20600' => 20600, // 3.90.2, 3.90.3, 3.91 + 'medium|18000' => 18000, + 'fast medium|18000' => 18000, + 'extreme|19500' => 19500, // 3.90, 3.90.1, 3.92, 3.95 + 'extreme|19600' => 19600, // 3.90.2, 3.90.3, 3.91, 3.93.1 + 'fast extreme|19500' => 19500, // 3.90, 3.90.1, 3.92, 3.95 + 'fast extreme|19600' => 19600, // 3.90.2, 3.90.3, 3.91, 3.93.1 + 'standard|19000' => 19000, + 'fast standard|19000' => 19000, + 'r3mix|19500' => 19500, // 3.90, 3.90.1, 3.92 + 'r3mix|19600' => 19600, // 3.90.2, 3.90.3, 3.91 + 'r3mix|18000' => 18000, // 3.94, 3.95 + ); + if (!isset($ExpectedLowpass[$ExplodedOptions[1].'|'.$thisfile_mpeg_audio_lame['lowpass_frequency']]) && ($thisfile_mpeg_audio_lame['lowpass_frequency'] < 22050) && (round($thisfile_mpeg_audio_lame['lowpass_frequency'] / 1000) < round($thisfile_mpeg_audio['sample_rate'] / 2000))) { + $encoder_options .= ' --lowpass '.$thisfile_mpeg_audio_lame['lowpass_frequency']; + } + break; + + default: + break; + } + break; + } + } + + if (isset($thisfile_mpeg_audio_lame['raw']['source_sample_freq'])) { + if (($thisfile_mpeg_audio['sample_rate'] == 44100) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 1)) { + $encoder_options .= ' --resample 44100'; + } elseif (($thisfile_mpeg_audio['sample_rate'] == 48000) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 2)) { + $encoder_options .= ' --resample 48000'; + } elseif ($thisfile_mpeg_audio['sample_rate'] < 44100) { + switch ($thisfile_mpeg_audio_lame['raw']['source_sample_freq']) { + case 0: // <= 32000 + // may or may not be same as source frequency - ignore + break; + case 1: // 44100 + case 2: // 48000 + case 3: // 48000+ + $ExplodedOptions = explode(' ', $encoder_options, 4); + switch ($ExplodedOptions[0]) { + case '--preset': + case '--alt-preset': + switch ($ExplodedOptions[1]) { + case 'fast': + case 'portable': + case 'medium': + case 'standard': + case 'extreme': + case 'insane': + $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate']; + break; + + default: + static $ExpectedResampledRate = array( + 'phon+/lw/mw-eu/sw|16000' => 16000, + 'mw-us|24000' => 24000, // 3.95 + 'mw-us|32000' => 32000, // 3.93 + 'mw-us|16000' => 16000, // 3.92 + 'phone|16000' => 16000, + 'phone|11025' => 11025, // 3.94a15 + 'radio|32000' => 32000, // 3.94a15 + 'fm/radio|32000' => 32000, // 3.92 + 'fm|32000' => 32000, // 3.90 + 'voice|32000' => 32000); + if (!isset($ExpectedResampledRate[$ExplodedOptions[1].'|'.$thisfile_mpeg_audio['sample_rate']])) { + $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate']; + } + break; + } + break; + + case '--r3mix': + default: + $encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate']; + break; + } + break; + } + } + } + if (empty($encoder_options) && !empty($info['audio']['bitrate']) && !empty($info['audio']['bitrate_mode'])) { + //$encoder_options = strtoupper($info['audio']['bitrate_mode']).ceil($info['audio']['bitrate'] / 1000); + $encoder_options = strtoupper($info['audio']['bitrate_mode']); + } + + return $encoder_options; + } + + + public function decodeMPEGaudioHeader($offset, &$info, $recursivesearch=true, $ScanAsCBR=false, $FastMPEGheaderScan=false) { + static $MPEGaudioVersionLookup; + static $MPEGaudioLayerLookup; + static $MPEGaudioBitrateLookup; + static $MPEGaudioFrequencyLookup; + static $MPEGaudioChannelModeLookup; + static $MPEGaudioModeExtensionLookup; + static $MPEGaudioEmphasisLookup; + if (empty($MPEGaudioVersionLookup)) { + $MPEGaudioVersionLookup = self::MPEGaudioVersionArray(); + $MPEGaudioLayerLookup = self::MPEGaudioLayerArray(); + $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray(); + $MPEGaudioFrequencyLookup = self::MPEGaudioFrequencyArray(); + $MPEGaudioChannelModeLookup = self::MPEGaudioChannelModeArray(); + $MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray(); + $MPEGaudioEmphasisLookup = self::MPEGaudioEmphasisArray(); + } + + if ($this->fseek($offset) != 0) { + $info['error'][] = 'decodeMPEGaudioHeader() failed to seek to next offset at '.$offset; + return false; + } + //$headerstring = $this->fread(1441); // worst-case max length = 32kHz @ 320kbps layer 3 = 1441 bytes/frame + $headerstring = $this->fread(226); // LAME header at offset 36 + 190 bytes of Xing/LAME data + + // MP3 audio frame structure: + // $aa $aa $aa $aa [$bb $bb] $cc... + // where $aa..$aa is the four-byte mpeg-audio header (below) + // $bb $bb is the optional 2-byte CRC + // and $cc... is the audio data + + $head4 = substr($headerstring, 0, 4); + + static $MPEGaudioHeaderDecodeCache = array(); + if (isset($MPEGaudioHeaderDecodeCache[$head4])) { + $MPEGheaderRawArray = $MPEGaudioHeaderDecodeCache[$head4]; + } else { + $MPEGheaderRawArray = self::MPEGaudioHeaderDecode($head4); + $MPEGaudioHeaderDecodeCache[$head4] = $MPEGheaderRawArray; + } + + static $MPEGaudioHeaderValidCache = array(); + if (!isset($MPEGaudioHeaderValidCache[$head4])) { // Not in cache + //$MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, true); // allow badly-formatted freeformat (from LAME 3.90 - 3.93.1) + $MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, false); + } + + // shortcut + if (!isset($info['mpeg']['audio'])) { + $info['mpeg']['audio'] = array(); + } + $thisfile_mpeg_audio = &$info['mpeg']['audio']; + + + if ($MPEGaudioHeaderValidCache[$head4]) { + $thisfile_mpeg_audio['raw'] = $MPEGheaderRawArray; + } else { + $info['error'][] = 'Invalid MPEG audio header ('.getid3_lib::PrintHexBytes($head4).') at offset '.$offset; + return false; + } + + if (!$FastMPEGheaderScan) { + $thisfile_mpeg_audio['version'] = $MPEGaudioVersionLookup[$thisfile_mpeg_audio['raw']['version']]; + $thisfile_mpeg_audio['layer'] = $MPEGaudioLayerLookup[$thisfile_mpeg_audio['raw']['layer']]; + + $thisfile_mpeg_audio['channelmode'] = $MPEGaudioChannelModeLookup[$thisfile_mpeg_audio['raw']['channelmode']]; + $thisfile_mpeg_audio['channels'] = (($thisfile_mpeg_audio['channelmode'] == 'mono') ? 1 : 2); + $thisfile_mpeg_audio['sample_rate'] = $MPEGaudioFrequencyLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['raw']['sample_rate']]; + $thisfile_mpeg_audio['protection'] = !$thisfile_mpeg_audio['raw']['protection']; + $thisfile_mpeg_audio['private'] = (bool) $thisfile_mpeg_audio['raw']['private']; + $thisfile_mpeg_audio['modeextension'] = $MPEGaudioModeExtensionLookup[$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['modeextension']]; + $thisfile_mpeg_audio['copyright'] = (bool) $thisfile_mpeg_audio['raw']['copyright']; + $thisfile_mpeg_audio['original'] = (bool) $thisfile_mpeg_audio['raw']['original']; + $thisfile_mpeg_audio['emphasis'] = $MPEGaudioEmphasisLookup[$thisfile_mpeg_audio['raw']['emphasis']]; + + $info['audio']['channels'] = $thisfile_mpeg_audio['channels']; + $info['audio']['sample_rate'] = $thisfile_mpeg_audio['sample_rate']; + + if ($thisfile_mpeg_audio['protection']) { + $thisfile_mpeg_audio['crc'] = getid3_lib::BigEndian2Int(substr($headerstring, 4, 2)); + } + } + + if ($thisfile_mpeg_audio['raw']['bitrate'] == 15) { + // http://www.hydrogenaudio.org/?act=ST&f=16&t=9682&st=0 + $info['warning'][] = 'Invalid bitrate index (15), this is a known bug in free-format MP3s encoded by LAME v3.90 - 3.93.1'; + $thisfile_mpeg_audio['raw']['bitrate'] = 0; + } + $thisfile_mpeg_audio['padding'] = (bool) $thisfile_mpeg_audio['raw']['padding']; + $thisfile_mpeg_audio['bitrate'] = $MPEGaudioBitrateLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['bitrate']]; + + if (($thisfile_mpeg_audio['bitrate'] == 'free') && ($offset == $info['avdataoffset'])) { + // only skip multiple frame check if free-format bitstream found at beginning of file + // otherwise is quite possibly simply corrupted data + $recursivesearch = false; + } + + // For Layer 2 there are some combinations of bitrate and mode which are not allowed. + if (!$FastMPEGheaderScan && ($thisfile_mpeg_audio['layer'] == '2')) { + + $info['audio']['dataformat'] = 'mp2'; + switch ($thisfile_mpeg_audio['channelmode']) { + + case 'mono': + if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] <= 192000)) { + // these are ok + } else { + $info['error'][] = $thisfile_mpeg_audio['bitrate'].'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.'; + return false; + } + break; + + case 'stereo': + case 'joint stereo': + case 'dual channel': + if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] == 64000) || ($thisfile_mpeg_audio['bitrate'] >= 96000)) { + // these are ok + } else { + $info['error'][] = intval(round($thisfile_mpeg_audio['bitrate'] / 1000)).'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.'; + return false; + } + break; + + } + + } + + + if ($info['audio']['sample_rate'] > 0) { + $thisfile_mpeg_audio['framelength'] = self::MPEGaudioFrameLength($thisfile_mpeg_audio['bitrate'], $thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['layer'], (int) $thisfile_mpeg_audio['padding'], $info['audio']['sample_rate']); + } + + $nextframetestoffset = $offset + 1; + if ($thisfile_mpeg_audio['bitrate'] != 'free') { + + $info['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate']; + + if (isset($thisfile_mpeg_audio['framelength'])) { + $nextframetestoffset = $offset + $thisfile_mpeg_audio['framelength']; + } else { + $info['error'][] = 'Frame at offset('.$offset.') is has an invalid frame length.'; + return false; + } + + } + + $ExpectedNumberOfAudioBytes = 0; + + //////////////////////////////////////////////////////////////////////////////////// + // Variable-bitrate headers + + if (substr($headerstring, 4 + 32, 4) == 'VBRI') { + // Fraunhofer VBR header is hardcoded 'VBRI' at offset 0x24 (36) + // specs taken from http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html + + $thisfile_mpeg_audio['bitrate_mode'] = 'vbr'; + $thisfile_mpeg_audio['VBR_method'] = 'Fraunhofer'; + $info['audio']['codec'] = 'Fraunhofer'; + + $SideInfoData = substr($headerstring, 4 + 2, 32); + + $FraunhoferVBROffset = 36; + + $thisfile_mpeg_audio['VBR_encoder_version'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 4, 2)); // VbriVersion + $thisfile_mpeg_audio['VBR_encoder_delay'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 6, 2)); // VbriDelay + $thisfile_mpeg_audio['VBR_quality'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 8, 2)); // VbriQuality + $thisfile_mpeg_audio['VBR_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 10, 4)); // VbriStreamBytes + $thisfile_mpeg_audio['VBR_frames'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 14, 4)); // VbriStreamFrames + $thisfile_mpeg_audio['VBR_seek_offsets'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 18, 2)); // VbriTableSize + $thisfile_mpeg_audio['VBR_seek_scale'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 20, 2)); // VbriTableScale + $thisfile_mpeg_audio['VBR_entry_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 22, 2)); // VbriEntryBytes + $thisfile_mpeg_audio['VBR_entry_frames'] = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 24, 2)); // VbriEntryFrames + + $ExpectedNumberOfAudioBytes = $thisfile_mpeg_audio['VBR_bytes']; + + $previousbyteoffset = $offset; + for ($i = 0; $i < $thisfile_mpeg_audio['VBR_seek_offsets']; $i++) { + $Fraunhofer_OffsetN = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset, $thisfile_mpeg_audio['VBR_entry_bytes'])); + $FraunhoferVBROffset += $thisfile_mpeg_audio['VBR_entry_bytes']; + $thisfile_mpeg_audio['VBR_offsets_relative'][$i] = ($Fraunhofer_OffsetN * $thisfile_mpeg_audio['VBR_seek_scale']); + $thisfile_mpeg_audio['VBR_offsets_absolute'][$i] = ($Fraunhofer_OffsetN * $thisfile_mpeg_audio['VBR_seek_scale']) + $previousbyteoffset; + $previousbyteoffset += $Fraunhofer_OffsetN; + } + + + } else { + + // Xing VBR header is hardcoded 'Xing' at a offset 0x0D (13), 0x15 (21) or 0x24 (36) + // depending on MPEG layer and number of channels + + $VBRidOffset = self::XingVBRidOffset($thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['channelmode']); + $SideInfoData = substr($headerstring, 4 + 2, $VBRidOffset - 4); + + if ((substr($headerstring, $VBRidOffset, strlen('Xing')) == 'Xing') || (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Info')) { + // 'Xing' is traditional Xing VBR frame + // 'Info' is LAME-encoded CBR (This was done to avoid CBR files to be recognized as traditional Xing VBR files by some decoders.) + // 'Info' *can* legally be used to specify a VBR file as well, however. + + // http://www.multiweb.cz/twoinches/MP3inside.htm + //00..03 = "Xing" or "Info" + //04..07 = Flags: + // 0x01 Frames Flag set if value for number of frames in file is stored + // 0x02 Bytes Flag set if value for filesize in bytes is stored + // 0x04 TOC Flag set if values for TOC are stored + // 0x08 VBR Scale Flag set if values for VBR scale is stored + //08..11 Frames: Number of frames in file (including the first Xing/Info one) + //12..15 Bytes: File length in Bytes + //16..115 TOC (Table of Contents): + // Contains of 100 indexes (one Byte length) for easier lookup in file. Approximately solves problem with moving inside file. + // Each Byte has a value according this formula: + // (TOC[i] / 256) * fileLenInBytes + // So if song lasts eg. 240 sec. and you want to jump to 60. sec. (and file is 5 000 000 Bytes length) you can use: + // TOC[(60/240)*100] = TOC[25] + // and corresponding Byte in file is then approximately at: + // (TOC[25]/256) * 5000000 + //116..119 VBR Scale + + + // should be safe to leave this at 'vbr' and let it be overriden to 'cbr' if a CBR preset/mode is used by LAME +// if (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Xing') { + $thisfile_mpeg_audio['bitrate_mode'] = 'vbr'; + $thisfile_mpeg_audio['VBR_method'] = 'Xing'; +// } else { +// $ScanAsCBR = true; +// $thisfile_mpeg_audio['bitrate_mode'] = 'cbr'; +// } + + $thisfile_mpeg_audio['xing_flags_raw'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 4, 4)); + + $thisfile_mpeg_audio['xing_flags']['frames'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000001); + $thisfile_mpeg_audio['xing_flags']['bytes'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000002); + $thisfile_mpeg_audio['xing_flags']['toc'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000004); + $thisfile_mpeg_audio['xing_flags']['vbr_scale'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000008); + + if ($thisfile_mpeg_audio['xing_flags']['frames']) { + $thisfile_mpeg_audio['VBR_frames'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 8, 4)); + //$thisfile_mpeg_audio['VBR_frames']--; // don't count header Xing/Info frame + } + if ($thisfile_mpeg_audio['xing_flags']['bytes']) { + $thisfile_mpeg_audio['VBR_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 12, 4)); + } + + //if (($thisfile_mpeg_audio['bitrate'] == 'free') && !empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) { + if (!empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) { + + $framelengthfloat = $thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']; + + if ($thisfile_mpeg_audio['layer'] == '1') { + // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12 + //$info['audio']['bitrate'] = ((($framelengthfloat / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12; + $info['audio']['bitrate'] = ($framelengthfloat / 4) * $thisfile_mpeg_audio['sample_rate'] * (2 / $info['audio']['channels']) / 12; + } else { + // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144 + //$info['audio']['bitrate'] = (($framelengthfloat - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144; + $info['audio']['bitrate'] = $framelengthfloat * $thisfile_mpeg_audio['sample_rate'] * (2 / $info['audio']['channels']) / 144; + } + $thisfile_mpeg_audio['framelength'] = floor($framelengthfloat); + } + + if ($thisfile_mpeg_audio['xing_flags']['toc']) { + $LAMEtocData = substr($headerstring, $VBRidOffset + 16, 100); + for ($i = 0; $i < 100; $i++) { + $thisfile_mpeg_audio['toc'][$i] = ord($LAMEtocData{$i}); + } + } + if ($thisfile_mpeg_audio['xing_flags']['vbr_scale']) { + $thisfile_mpeg_audio['VBR_scale'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 116, 4)); + } + + + // http://gabriel.mp3-tech.org/mp3infotag.html + if (substr($headerstring, $VBRidOffset + 120, 4) == 'LAME') { + + // shortcut + $thisfile_mpeg_audio['LAME'] = array(); + $thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME']; + + + $thisfile_mpeg_audio_lame['long_version'] = substr($headerstring, $VBRidOffset + 120, 20); + $thisfile_mpeg_audio_lame['short_version'] = substr($thisfile_mpeg_audio_lame['long_version'], 0, 9); + + if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.90') { + + // extra 11 chars are not part of version string when LAMEtag present + unset($thisfile_mpeg_audio_lame['long_version']); + + // It the LAME tag was only introduced in LAME v3.90 + // http://www.hydrogenaudio.org/?act=ST&f=15&t=9933 + + // Offsets of various bytes in http://gabriel.mp3-tech.org/mp3infotag.html + // are assuming a 'Xing' identifier offset of 0x24, which is the case for + // MPEG-1 non-mono, but not for other combinations + $LAMEtagOffsetContant = $VBRidOffset - 0x24; + + // shortcuts + $thisfile_mpeg_audio_lame['RGAD'] = array('track'=>array(), 'album'=>array()); + $thisfile_mpeg_audio_lame_RGAD = &$thisfile_mpeg_audio_lame['RGAD']; + $thisfile_mpeg_audio_lame_RGAD_track = &$thisfile_mpeg_audio_lame_RGAD['track']; + $thisfile_mpeg_audio_lame_RGAD_album = &$thisfile_mpeg_audio_lame_RGAD['album']; + $thisfile_mpeg_audio_lame['raw'] = array(); + $thisfile_mpeg_audio_lame_raw = &$thisfile_mpeg_audio_lame['raw']; + + // byte $9B VBR Quality + // This field is there to indicate a quality level, although the scale was not precised in the original Xing specifications. + // Actually overwrites original Xing bytes + unset($thisfile_mpeg_audio['VBR_scale']); + $thisfile_mpeg_audio_lame['vbr_quality'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0x9B, 1)); + + // bytes $9C-$A4 Encoder short VersionString + $thisfile_mpeg_audio_lame['short_version'] = substr($headerstring, $LAMEtagOffsetContant + 0x9C, 9); + + // byte $A5 Info Tag revision + VBR method + $LAMEtagRevisionVBRmethod = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA5, 1)); + + $thisfile_mpeg_audio_lame['tag_revision'] = ($LAMEtagRevisionVBRmethod & 0xF0) >> 4; + $thisfile_mpeg_audio_lame_raw['vbr_method'] = $LAMEtagRevisionVBRmethod & 0x0F; + $thisfile_mpeg_audio_lame['vbr_method'] = self::LAMEvbrMethodLookup($thisfile_mpeg_audio_lame_raw['vbr_method']); + $thisfile_mpeg_audio['bitrate_mode'] = substr($thisfile_mpeg_audio_lame['vbr_method'], 0, 3); // usually either 'cbr' or 'vbr', but truncates 'vbr-old / vbr-rh' to 'vbr' + + // byte $A6 Lowpass filter value + $thisfile_mpeg_audio_lame['lowpass_frequency'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA6, 1)) * 100; + + // bytes $A7-$AE Replay Gain + // http://privatewww.essex.ac.uk/~djmrob/replaygain/rg_data_format.html + // bytes $A7-$AA : 32 bit floating point "Peak signal amplitude" + if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.94b') { + // LAME 3.94a16 and later - 9.23 fixed point + // ie 0x0059E2EE / (2^23) = 5890798 / 8388608 = 0.7022378444671630859375 + $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = (float) ((getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4))) / 8388608); + } else { + // LAME 3.94a15 and earlier - 32-bit floating point + // Actually 3.94a16 will fall in here too and be WRONG, but is hard to detect 3.94a16 vs 3.94a15 + $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = getid3_lib::LittleEndian2Float(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4)); + } + if ($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] == 0) { + unset($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']); + } else { + $thisfile_mpeg_audio_lame_RGAD['peak_db'] = getid3_lib::RGADamplitude2dB($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']); + } + + $thisfile_mpeg_audio_lame_raw['RGAD_track'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAB, 2)); + $thisfile_mpeg_audio_lame_raw['RGAD_album'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAD, 2)); + + + if ($thisfile_mpeg_audio_lame_raw['RGAD_track'] != 0) { + + $thisfile_mpeg_audio_lame_RGAD_track['raw']['name'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0xE000) >> 13; + $thisfile_mpeg_audio_lame_RGAD_track['raw']['originator'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x1C00) >> 10; + $thisfile_mpeg_audio_lame_RGAD_track['raw']['sign_bit'] = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x0200) >> 9; + $thisfile_mpeg_audio_lame_RGAD_track['raw']['gain_adjust'] = $thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x01FF; + $thisfile_mpeg_audio_lame_RGAD_track['name'] = getid3_lib::RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['name']); + $thisfile_mpeg_audio_lame_RGAD_track['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['originator']); + $thisfile_mpeg_audio_lame_RGAD_track['gain_db'] = getid3_lib::RGADadjustmentLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['gain_adjust'], $thisfile_mpeg_audio_lame_RGAD_track['raw']['sign_bit']); + + if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) { + $info['replay_gain']['track']['peak'] = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude']; + } + $info['replay_gain']['track']['originator'] = $thisfile_mpeg_audio_lame_RGAD_track['originator']; + $info['replay_gain']['track']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_track['gain_db']; + } else { + unset($thisfile_mpeg_audio_lame_RGAD['track']); + } + if ($thisfile_mpeg_audio_lame_raw['RGAD_album'] != 0) { + + $thisfile_mpeg_audio_lame_RGAD_album['raw']['name'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0xE000) >> 13; + $thisfile_mpeg_audio_lame_RGAD_album['raw']['originator'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x1C00) >> 10; + $thisfile_mpeg_audio_lame_RGAD_album['raw']['sign_bit'] = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x0200) >> 9; + $thisfile_mpeg_audio_lame_RGAD_album['raw']['gain_adjust'] = $thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x01FF; + $thisfile_mpeg_audio_lame_RGAD_album['name'] = getid3_lib::RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['name']); + $thisfile_mpeg_audio_lame_RGAD_album['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['originator']); + $thisfile_mpeg_audio_lame_RGAD_album['gain_db'] = getid3_lib::RGADadjustmentLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['gain_adjust'], $thisfile_mpeg_audio_lame_RGAD_album['raw']['sign_bit']); + + if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) { + $info['replay_gain']['album']['peak'] = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude']; + } + $info['replay_gain']['album']['originator'] = $thisfile_mpeg_audio_lame_RGAD_album['originator']; + $info['replay_gain']['album']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_album['gain_db']; + } else { + unset($thisfile_mpeg_audio_lame_RGAD['album']); + } + if (empty($thisfile_mpeg_audio_lame_RGAD)) { + unset($thisfile_mpeg_audio_lame['RGAD']); + } + + + // byte $AF Encoding flags + ATH Type + $EncodingFlagsATHtype = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAF, 1)); + $thisfile_mpeg_audio_lame['encoding_flags']['nspsytune'] = (bool) ($EncodingFlagsATHtype & 0x10); + $thisfile_mpeg_audio_lame['encoding_flags']['nssafejoint'] = (bool) ($EncodingFlagsATHtype & 0x20); + $thisfile_mpeg_audio_lame['encoding_flags']['nogap_next'] = (bool) ($EncodingFlagsATHtype & 0x40); + $thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev'] = (bool) ($EncodingFlagsATHtype & 0x80); + $thisfile_mpeg_audio_lame['ath_type'] = $EncodingFlagsATHtype & 0x0F; + + // byte $B0 if ABR {specified bitrate} else {minimal bitrate} + $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB0, 1)); + if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 2) { // Average BitRate (ABR) + $thisfile_mpeg_audio_lame['bitrate_abr'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']; + } elseif ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) { // Constant BitRate (CBR) + // ignore + } elseif ($thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] > 0) { // Variable BitRate (VBR) - minimum bitrate + $thisfile_mpeg_audio_lame['bitrate_min'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']; + } + + // bytes $B1-$B3 Encoder delays + $EncoderDelays = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB1, 3)); + $thisfile_mpeg_audio_lame['encoder_delay'] = ($EncoderDelays & 0xFFF000) >> 12; + $thisfile_mpeg_audio_lame['end_padding'] = $EncoderDelays & 0x000FFF; + + // byte $B4 Misc + $MiscByte = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB4, 1)); + $thisfile_mpeg_audio_lame_raw['noise_shaping'] = ($MiscByte & 0x03); + $thisfile_mpeg_audio_lame_raw['stereo_mode'] = ($MiscByte & 0x1C) >> 2; + $thisfile_mpeg_audio_lame_raw['not_optimal_quality'] = ($MiscByte & 0x20) >> 5; + $thisfile_mpeg_audio_lame_raw['source_sample_freq'] = ($MiscByte & 0xC0) >> 6; + $thisfile_mpeg_audio_lame['noise_shaping'] = $thisfile_mpeg_audio_lame_raw['noise_shaping']; + $thisfile_mpeg_audio_lame['stereo_mode'] = self::LAMEmiscStereoModeLookup($thisfile_mpeg_audio_lame_raw['stereo_mode']); + $thisfile_mpeg_audio_lame['not_optimal_quality'] = (bool) $thisfile_mpeg_audio_lame_raw['not_optimal_quality']; + $thisfile_mpeg_audio_lame['source_sample_freq'] = self::LAMEmiscSourceSampleFrequencyLookup($thisfile_mpeg_audio_lame_raw['source_sample_freq']); + + // byte $B5 MP3 Gain + $thisfile_mpeg_audio_lame_raw['mp3_gain'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB5, 1), false, true); + $thisfile_mpeg_audio_lame['mp3_gain_db'] = (getid3_lib::RGADamplitude2dB(2) / 4) * $thisfile_mpeg_audio_lame_raw['mp3_gain']; + $thisfile_mpeg_audio_lame['mp3_gain_factor'] = pow(2, ($thisfile_mpeg_audio_lame['mp3_gain_db'] / 6)); + + // bytes $B6-$B7 Preset and surround info + $PresetSurroundBytes = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB6, 2)); + // Reserved = ($PresetSurroundBytes & 0xC000); + $thisfile_mpeg_audio_lame_raw['surround_info'] = ($PresetSurroundBytes & 0x3800); + $thisfile_mpeg_audio_lame['surround_info'] = self::LAMEsurroundInfoLookup($thisfile_mpeg_audio_lame_raw['surround_info']); + $thisfile_mpeg_audio_lame['preset_used_id'] = ($PresetSurroundBytes & 0x07FF); + $thisfile_mpeg_audio_lame['preset_used'] = self::LAMEpresetUsedLookup($thisfile_mpeg_audio_lame); + if (!empty($thisfile_mpeg_audio_lame['preset_used_id']) && empty($thisfile_mpeg_audio_lame['preset_used'])) { + $info['warning'][] = 'Unknown LAME preset used ('.$thisfile_mpeg_audio_lame['preset_used_id'].') - please report to info@getid3.org'; + } + if (($thisfile_mpeg_audio_lame['short_version'] == 'LAME3.90.') && !empty($thisfile_mpeg_audio_lame['preset_used_id'])) { + // this may change if 3.90.4 ever comes out + $thisfile_mpeg_audio_lame['short_version'] = 'LAME3.90.3'; + } + + // bytes $B8-$BB MusicLength + $thisfile_mpeg_audio_lame['audio_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB8, 4)); + $ExpectedNumberOfAudioBytes = (($thisfile_mpeg_audio_lame['audio_bytes'] > 0) ? $thisfile_mpeg_audio_lame['audio_bytes'] : $thisfile_mpeg_audio['VBR_bytes']); + + // bytes $BC-$BD MusicCRC + $thisfile_mpeg_audio_lame['music_crc'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBC, 2)); + + // bytes $BE-$BF CRC-16 of Info Tag + $thisfile_mpeg_audio_lame['lame_tag_crc'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBE, 2)); + + + // LAME CBR + if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) { + + $thisfile_mpeg_audio['bitrate_mode'] = 'cbr'; + $thisfile_mpeg_audio['bitrate'] = self::ClosestStandardMP3Bitrate($thisfile_mpeg_audio['bitrate']); + $info['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate']; + //if (empty($thisfile_mpeg_audio['bitrate']) || (!empty($thisfile_mpeg_audio_lame['bitrate_min']) && ($thisfile_mpeg_audio_lame['bitrate_min'] != 255))) { + // $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio_lame['bitrate_min']; + //} + + } + + } + } + + } else { + + // not Fraunhofer or Xing VBR methods, most likely CBR (but could be VBR with no header) + $thisfile_mpeg_audio['bitrate_mode'] = 'cbr'; + if ($recursivesearch) { + $thisfile_mpeg_audio['bitrate_mode'] = 'vbr'; + if ($this->RecursiveFrameScanning($offset, $nextframetestoffset, true)) { + $recursivesearch = false; + $thisfile_mpeg_audio['bitrate_mode'] = 'cbr'; + } + if ($thisfile_mpeg_audio['bitrate_mode'] == 'vbr') { + $info['warning'][] = 'VBR file with no VBR header. Bitrate values calculated from actual frame bitrates.'; + } + } + + } + + } + + if (($ExpectedNumberOfAudioBytes > 0) && ($ExpectedNumberOfAudioBytes != ($info['avdataend'] - $info['avdataoffset']))) { + if ($ExpectedNumberOfAudioBytes > ($info['avdataend'] - $info['avdataoffset'])) { + if ($this->isDependencyFor('matroska') || $this->isDependencyFor('riff')) { + // ignore, audio data is broken into chunks so will always be data "missing" + } + elseif (($ExpectedNumberOfAudioBytes - ($info['avdataend'] - $info['avdataoffset'])) == 1) { + $this->warning('Last byte of data truncated (this is a known bug in Meracl ID3 Tag Writer before v1.3.5)'); + } + else { + $this->warning('Probable truncated file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, only found '.($info['avdataend'] - $info['avdataoffset']).' (short by '.($ExpectedNumberOfAudioBytes - ($info['avdataend'] - $info['avdataoffset'])).' bytes)'); + } + } else { + if ((($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes) == 1) { + // $prenullbytefileoffset = $this->ftell(); + // $this->fseek($info['avdataend']); + // $PossibleNullByte = $this->fread(1); + // $this->fseek($prenullbytefileoffset); + // if ($PossibleNullByte === "\x00") { + $info['avdataend']--; + // $info['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored'; + // } else { + // $info['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($info['avdataend'] - $info['avdataoffset']).' ('.(($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)'; + // } + } else { + $info['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($info['avdataend'] - $info['avdataoffset']).' ('.(($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)'; + } + } + } + + if (($thisfile_mpeg_audio['bitrate'] == 'free') && empty($info['audio']['bitrate'])) { + if (($offset == $info['avdataoffset']) && empty($thisfile_mpeg_audio['VBR_frames'])) { + $framebytelength = $this->FreeFormatFrameLength($offset, true); + if ($framebytelength > 0) { + $thisfile_mpeg_audio['framelength'] = $framebytelength; + if ($thisfile_mpeg_audio['layer'] == '1') { + // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12 + $info['audio']['bitrate'] = ((($framebytelength / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12; + } else { + // Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144 + $info['audio']['bitrate'] = (($framebytelength - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144; + } + } else { + $info['error'][] = 'Error calculating frame length of free-format MP3 without Xing/LAME header'; + } + } + } + + if (isset($thisfile_mpeg_audio['VBR_frames']) ? $thisfile_mpeg_audio['VBR_frames'] : '') { + switch ($thisfile_mpeg_audio['bitrate_mode']) { + case 'vbr': + case 'abr': + $bytes_per_frame = 1152; + if (($thisfile_mpeg_audio['version'] == '1') && ($thisfile_mpeg_audio['layer'] == 1)) { + $bytes_per_frame = 384; + } elseif ((($thisfile_mpeg_audio['version'] == '2') || ($thisfile_mpeg_audio['version'] == '2.5')) && ($thisfile_mpeg_audio['layer'] == 3)) { + $bytes_per_frame = 576; + } + $thisfile_mpeg_audio['VBR_bitrate'] = (isset($thisfile_mpeg_audio['VBR_bytes']) ? (($thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($info['audio']['sample_rate'] / $bytes_per_frame) : 0); + if ($thisfile_mpeg_audio['VBR_bitrate'] > 0) { + $info['audio']['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; + $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; // to avoid confusion + } + break; + } + } + + // End variable-bitrate headers + //////////////////////////////////////////////////////////////////////////////////// + + if ($recursivesearch) { + + if (!$this->RecursiveFrameScanning($offset, $nextframetestoffset, $ScanAsCBR)) { + return false; + } + + } + + + //if (false) { + // // experimental side info parsing section - not returning anything useful yet + // + // $SideInfoBitstream = getid3_lib::BigEndian2Bin($SideInfoData); + // $SideInfoOffset = 0; + // + // if ($thisfile_mpeg_audio['version'] == '1') { + // if ($thisfile_mpeg_audio['channelmode'] == 'mono') { + // // MPEG-1 (mono) + // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9); + // $SideInfoOffset += 9; + // $SideInfoOffset += 5; + // } else { + // // MPEG-1 (stereo, joint-stereo, dual-channel) + // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9); + // $SideInfoOffset += 9; + // $SideInfoOffset += 3; + // } + // } else { // 2 or 2.5 + // if ($thisfile_mpeg_audio['channelmode'] == 'mono') { + // // MPEG-2, MPEG-2.5 (mono) + // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8); + // $SideInfoOffset += 8; + // $SideInfoOffset += 1; + // } else { + // // MPEG-2, MPEG-2.5 (stereo, joint-stereo, dual-channel) + // $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8); + // $SideInfoOffset += 8; + // $SideInfoOffset += 2; + // } + // } + // + // if ($thisfile_mpeg_audio['version'] == '1') { + // for ($channel = 0; $channel < $info['audio']['channels']; $channel++) { + // for ($scfsi_band = 0; $scfsi_band < 4; $scfsi_band++) { + // $thisfile_mpeg_audio['scfsi'][$channel][$scfsi_band] = substr($SideInfoBitstream, $SideInfoOffset, 1); + // $SideInfoOffset += 2; + // } + // } + // } + // for ($granule = 0; $granule < (($thisfile_mpeg_audio['version'] == '1') ? 2 : 1); $granule++) { + // for ($channel = 0; $channel < $info['audio']['channels']; $channel++) { + // $thisfile_mpeg_audio['part2_3_length'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 12); + // $SideInfoOffset += 12; + // $thisfile_mpeg_audio['big_values'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9); + // $SideInfoOffset += 9; + // $thisfile_mpeg_audio['global_gain'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 8); + // $SideInfoOffset += 8; + // if ($thisfile_mpeg_audio['version'] == '1') { + // $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4); + // $SideInfoOffset += 4; + // } else { + // $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9); + // $SideInfoOffset += 9; + // } + // $thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); + // $SideInfoOffset += 1; + // + // if ($thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] == '1') { + // + // $thisfile_mpeg_audio['block_type'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 2); + // $SideInfoOffset += 2; + // $thisfile_mpeg_audio['mixed_block_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); + // $SideInfoOffset += 1; + // + // for ($region = 0; $region < 2; $region++) { + // $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5); + // $SideInfoOffset += 5; + // } + // $thisfile_mpeg_audio['table_select'][$granule][$channel][2] = 0; + // + // for ($window = 0; $window < 3; $window++) { + // $thisfile_mpeg_audio['subblock_gain'][$granule][$channel][$window] = substr($SideInfoBitstream, $SideInfoOffset, 3); + // $SideInfoOffset += 3; + // } + // + // } else { + // + // for ($region = 0; $region < 3; $region++) { + // $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5); + // $SideInfoOffset += 5; + // } + // + // $thisfile_mpeg_audio['region0_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4); + // $SideInfoOffset += 4; + // $thisfile_mpeg_audio['region1_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 3); + // $SideInfoOffset += 3; + // $thisfile_mpeg_audio['block_type'][$granule][$channel] = 0; + // } + // + // if ($thisfile_mpeg_audio['version'] == '1') { + // $thisfile_mpeg_audio['preflag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); + // $SideInfoOffset += 1; + // } + // $thisfile_mpeg_audio['scalefac_scale'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); + // $SideInfoOffset += 1; + // $thisfile_mpeg_audio['count1table_select'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1); + // $SideInfoOffset += 1; + // } + // } + //} + + return true; + } + + public function RecursiveFrameScanning(&$offset, &$nextframetestoffset, $ScanAsCBR) { + $info = &$this->getid3->info; + $firstframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']); + $this->decodeMPEGaudioHeader($offset, $firstframetestarray, false); + + for ($i = 0; $i < GETID3_MP3_VALID_CHECK_FRAMES; $i++) { + // check next GETID3_MP3_VALID_CHECK_FRAMES frames for validity, to make sure we haven't run across a false synch + if (($nextframetestoffset + 4) >= $info['avdataend']) { + // end of file + return true; + } + + $nextframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']); + if ($this->decodeMPEGaudioHeader($nextframetestoffset, $nextframetestarray, false)) { + if ($ScanAsCBR) { + // force CBR mode, used for trying to pick out invalid audio streams with valid(?) VBR headers, or VBR streams with no VBR header + if (!isset($nextframetestarray['mpeg']['audio']['bitrate']) || !isset($firstframetestarray['mpeg']['audio']['bitrate']) || ($nextframetestarray['mpeg']['audio']['bitrate'] != $firstframetestarray['mpeg']['audio']['bitrate'])) { + return false; + } + } + + + // next frame is OK, get ready to check the one after that + if (isset($nextframetestarray['mpeg']['audio']['framelength']) && ($nextframetestarray['mpeg']['audio']['framelength'] > 0)) { + $nextframetestoffset += $nextframetestarray['mpeg']['audio']['framelength']; + } else { + $info['error'][] = 'Frame at offset ('.$offset.') is has an invalid frame length.'; + return false; + } + + } elseif (!empty($firstframetestarray['mpeg']['audio']['framelength']) && (($nextframetestoffset + $firstframetestarray['mpeg']['audio']['framelength']) > $info['avdataend'])) { + + // it's not the end of the file, but there's not enough data left for another frame, so assume it's garbage/padding and return OK + return true; + + } else { + + // next frame is not valid, note the error and fail, so scanning can contiue for a valid frame sequence + $info['warning'][] = 'Frame at offset ('.$offset.') is valid, but the next one at ('.$nextframetestoffset.') is not.'; + + return false; + } + } + return true; + } + + public function FreeFormatFrameLength($offset, $deepscan=false) { + $info = &$this->getid3->info; + + $this->fseek($offset); + $MPEGaudioData = $this->fread(32768); + + $SyncPattern1 = substr($MPEGaudioData, 0, 4); + // may be different pattern due to padding + $SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) | 0x02).$SyncPattern1{3}; + if ($SyncPattern2 === $SyncPattern1) { + $SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) & 0xFD).$SyncPattern1{3}; + } + + $framelength = false; + $framelength1 = strpos($MPEGaudioData, $SyncPattern1, 4); + $framelength2 = strpos($MPEGaudioData, $SyncPattern2, 4); + if ($framelength1 > 4) { + $framelength = $framelength1; + } + if (($framelength2 > 4) && ($framelength2 < $framelength1)) { + $framelength = $framelength2; + } + if (!$framelength) { + + // LAME 3.88 has a different value for modeextension on the first frame vs the rest + $framelength1 = strpos($MPEGaudioData, substr($SyncPattern1, 0, 3), 4); + $framelength2 = strpos($MPEGaudioData, substr($SyncPattern2, 0, 3), 4); + + if ($framelength1 > 4) { + $framelength = $framelength1; + } + if (($framelength2 > 4) && ($framelength2 < $framelength1)) { + $framelength = $framelength2; + } + if (!$framelength) { + $info['error'][] = 'Cannot find next free-format synch pattern ('.getid3_lib::PrintHexBytes($SyncPattern1).' or '.getid3_lib::PrintHexBytes($SyncPattern2).') after offset '.$offset; + return false; + } else { + $info['warning'][] = 'ModeExtension varies between first frame and other frames (known free-format issue in LAME 3.88)'; + $info['audio']['codec'] = 'LAME'; + $info['audio']['encoder'] = 'LAME3.88'; + $SyncPattern1 = substr($SyncPattern1, 0, 3); + $SyncPattern2 = substr($SyncPattern2, 0, 3); + } + } + + if ($deepscan) { + + $ActualFrameLengthValues = array(); + $nextoffset = $offset + $framelength; + while ($nextoffset < ($info['avdataend'] - 6)) { + $this->fseek($nextoffset - 1); + $NextSyncPattern = $this->fread(6); + if ((substr($NextSyncPattern, 1, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 1, strlen($SyncPattern2)) == $SyncPattern2)) { + // good - found where expected + $ActualFrameLengthValues[] = $framelength; + } elseif ((substr($NextSyncPattern, 0, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 0, strlen($SyncPattern2)) == $SyncPattern2)) { + // ok - found one byte earlier than expected (last frame wasn't padded, first frame was) + $ActualFrameLengthValues[] = ($framelength - 1); + $nextoffset--; + } elseif ((substr($NextSyncPattern, 2, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 2, strlen($SyncPattern2)) == $SyncPattern2)) { + // ok - found one byte later than expected (last frame was padded, first frame wasn't) + $ActualFrameLengthValues[] = ($framelength + 1); + $nextoffset++; + } else { + $info['error'][] = 'Did not find expected free-format sync pattern at offset '.$nextoffset; + return false; + } + $nextoffset += $framelength; + } + if (count($ActualFrameLengthValues) > 0) { + $framelength = intval(round(array_sum($ActualFrameLengthValues) / count($ActualFrameLengthValues))); + } + } + return $framelength; + } + + public function getOnlyMPEGaudioInfoBruteForce() { + $MPEGaudioHeaderDecodeCache = array(); + $MPEGaudioHeaderValidCache = array(); + $MPEGaudioHeaderLengthCache = array(); + $MPEGaudioVersionLookup = self::MPEGaudioVersionArray(); + $MPEGaudioLayerLookup = self::MPEGaudioLayerArray(); + $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray(); + $MPEGaudioFrequencyLookup = self::MPEGaudioFrequencyArray(); + $MPEGaudioChannelModeLookup = self::MPEGaudioChannelModeArray(); + $MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray(); + $MPEGaudioEmphasisLookup = self::MPEGaudioEmphasisArray(); + $LongMPEGversionLookup = array(); + $LongMPEGlayerLookup = array(); + $LongMPEGbitrateLookup = array(); + $LongMPEGpaddingLookup = array(); + $LongMPEGfrequencyLookup = array(); + $Distribution['bitrate'] = array(); + $Distribution['frequency'] = array(); + $Distribution['layer'] = array(); + $Distribution['version'] = array(); + $Distribution['padding'] = array(); + + $info = &$this->getid3->info; + $this->fseek($info['avdataoffset']); + + $max_frames_scan = 5000; + $frames_scanned = 0; + + $previousvalidframe = $info['avdataoffset']; + while ($this->ftell() < $info['avdataend']) { + set_time_limit(30); + $head4 = $this->fread(4); + if (strlen($head4) < 4) { + break; + } + if ($head4{0} != "\xFF") { + for ($i = 1; $i < 4; $i++) { + if ($head4{$i} == "\xFF") { + $this->fseek($i - 4, SEEK_CUR); + continue 2; + } + } + continue; + } + if (!isset($MPEGaudioHeaderDecodeCache[$head4])) { + $MPEGaudioHeaderDecodeCache[$head4] = self::MPEGaudioHeaderDecode($head4); + } + if (!isset($MPEGaudioHeaderValidCache[$head4])) { + $MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$head4], false, false); + } + if ($MPEGaudioHeaderValidCache[$head4]) { + + if (!isset($MPEGaudioHeaderLengthCache[$head4])) { + $LongMPEGversionLookup[$head4] = $MPEGaudioVersionLookup[$MPEGaudioHeaderDecodeCache[$head4]['version']]; + $LongMPEGlayerLookup[$head4] = $MPEGaudioLayerLookup[$MPEGaudioHeaderDecodeCache[$head4]['layer']]; + $LongMPEGbitrateLookup[$head4] = $MPEGaudioBitrateLookup[$LongMPEGversionLookup[$head4]][$LongMPEGlayerLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['bitrate']]; + $LongMPEGpaddingLookup[$head4] = (bool) $MPEGaudioHeaderDecodeCache[$head4]['padding']; + $LongMPEGfrequencyLookup[$head4] = $MPEGaudioFrequencyLookup[$LongMPEGversionLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['sample_rate']]; + $MPEGaudioHeaderLengthCache[$head4] = self::MPEGaudioFrameLength( + $LongMPEGbitrateLookup[$head4], + $LongMPEGversionLookup[$head4], + $LongMPEGlayerLookup[$head4], + $LongMPEGpaddingLookup[$head4], + $LongMPEGfrequencyLookup[$head4]); + } + if ($MPEGaudioHeaderLengthCache[$head4] > 4) { + $WhereWeWere = $this->ftell(); + $this->fseek($MPEGaudioHeaderLengthCache[$head4] - 4, SEEK_CUR); + $next4 = $this->fread(4); + if ($next4{0} == "\xFF") { + if (!isset($MPEGaudioHeaderDecodeCache[$next4])) { + $MPEGaudioHeaderDecodeCache[$next4] = self::MPEGaudioHeaderDecode($next4); + } + if (!isset($MPEGaudioHeaderValidCache[$next4])) { + $MPEGaudioHeaderValidCache[$next4] = self::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$next4], false, false); + } + if ($MPEGaudioHeaderValidCache[$next4]) { + $this->fseek(-4, SEEK_CUR); + + getid3_lib::safe_inc($Distribution['bitrate'][$LongMPEGbitrateLookup[$head4]]); + getid3_lib::safe_inc($Distribution['layer'][$LongMPEGlayerLookup[$head4]]); + getid3_lib::safe_inc($Distribution['version'][$LongMPEGversionLookup[$head4]]); + getid3_lib::safe_inc($Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])]); + getid3_lib::safe_inc($Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]]); + if ($max_frames_scan && (++$frames_scanned >= $max_frames_scan)) { + $pct_data_scanned = ($this->ftell() - $info['avdataoffset']) / ($info['avdataend'] - $info['avdataoffset']); + $info['warning'][] = 'too many MPEG audio frames to scan, only scanned first '.$max_frames_scan.' frames ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.'; + foreach ($Distribution as $key1 => $value1) { + foreach ($value1 as $key2 => $value2) { + $Distribution[$key1][$key2] = round($value2 / $pct_data_scanned); + } + } + break; + } + continue; + } + } + unset($next4); + $this->fseek($WhereWeWere - 3); + } + + } + } + foreach ($Distribution as $key => $value) { + ksort($Distribution[$key], SORT_NUMERIC); + } + ksort($Distribution['version'], SORT_STRING); + $info['mpeg']['audio']['bitrate_distribution'] = $Distribution['bitrate']; + $info['mpeg']['audio']['frequency_distribution'] = $Distribution['frequency']; + $info['mpeg']['audio']['layer_distribution'] = $Distribution['layer']; + $info['mpeg']['audio']['version_distribution'] = $Distribution['version']; + $info['mpeg']['audio']['padding_distribution'] = $Distribution['padding']; + if (count($Distribution['version']) > 1) { + $info['error'][] = 'Corrupt file - more than one MPEG version detected'; + } + if (count($Distribution['layer']) > 1) { + $info['error'][] = 'Corrupt file - more than one MPEG layer detected'; + } + if (count($Distribution['frequency']) > 1) { + $info['error'][] = 'Corrupt file - more than one MPEG sample rate detected'; + } + + + $bittotal = 0; + foreach ($Distribution['bitrate'] as $bitratevalue => $bitratecount) { + if ($bitratevalue != 'free') { + $bittotal += ($bitratevalue * $bitratecount); + } + } + $info['mpeg']['audio']['frame_count'] = array_sum($Distribution['bitrate']); + if ($info['mpeg']['audio']['frame_count'] == 0) { + $info['error'][] = 'no MPEG audio frames found'; + return false; + } + $info['mpeg']['audio']['bitrate'] = ($bittotal / $info['mpeg']['audio']['frame_count']); + $info['mpeg']['audio']['bitrate_mode'] = ((count($Distribution['bitrate']) > 0) ? 'vbr' : 'cbr'); + $info['mpeg']['audio']['sample_rate'] = getid3_lib::array_max($Distribution['frequency'], true); + + $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate']; + $info['audio']['bitrate_mode'] = $info['mpeg']['audio']['bitrate_mode']; + $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate']; + $info['audio']['dataformat'] = 'mp'.getid3_lib::array_max($Distribution['layer'], true); + $info['fileformat'] = $info['audio']['dataformat']; + + return true; + } + + + public function getOnlyMPEGaudioInfo($avdataoffset, $BitrateHistogram=false) { + // looks for synch, decodes MPEG audio header + + $info = &$this->getid3->info; + + static $MPEGaudioVersionLookup; + static $MPEGaudioLayerLookup; + static $MPEGaudioBitrateLookup; + if (empty($MPEGaudioVersionLookup)) { + $MPEGaudioVersionLookup = self::MPEGaudioVersionArray(); + $MPEGaudioLayerLookup = self::MPEGaudioLayerArray(); + $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray(); + + } + + $this->fseek($avdataoffset); + $sync_seek_buffer_size = min(128 * 1024, $info['avdataend'] - $avdataoffset); + if ($sync_seek_buffer_size <= 0) { + $info['error'][] = 'Invalid $sync_seek_buffer_size at offset '.$avdataoffset; + return false; + } + $header = $this->fread($sync_seek_buffer_size); + $sync_seek_buffer_size = strlen($header); + $SynchSeekOffset = 0; + while ($SynchSeekOffset < $sync_seek_buffer_size) { + if ((($avdataoffset + $SynchSeekOffset) < $info['avdataend']) && !feof($this->getid3->fp)) { + + if ($SynchSeekOffset > $sync_seek_buffer_size) { + // if a synch's not found within the first 128k bytes, then give up + $info['error'][] = 'Could not find valid MPEG audio synch within the first '.round($sync_seek_buffer_size / 1024).'kB'; + if (isset($info['audio']['bitrate'])) { + unset($info['audio']['bitrate']); + } + if (isset($info['mpeg']['audio'])) { + unset($info['mpeg']['audio']); + } + if (empty($info['mpeg'])) { + unset($info['mpeg']); + } + return false; + + } elseif (feof($this->getid3->fp)) { + + $info['error'][] = 'Could not find valid MPEG audio synch before end of file'; + if (isset($info['audio']['bitrate'])) { + unset($info['audio']['bitrate']); + } + if (isset($info['mpeg']['audio'])) { + unset($info['mpeg']['audio']); + } + if (isset($info['mpeg']) && (!is_array($info['mpeg']) || (count($info['mpeg']) == 0))) { + unset($info['mpeg']); + } + return false; + } + } + + if (($SynchSeekOffset + 1) >= strlen($header)) { + $info['error'][] = 'Could not find valid MPEG synch before end of file'; + return false; + } + + if (($header{$SynchSeekOffset} == "\xFF") && ($header{($SynchSeekOffset + 1)} > "\xE0")) { // synch detected + if (!isset($FirstFrameThisfileInfo) && !isset($info['mpeg']['audio'])) { + $FirstFrameThisfileInfo = $info; + $FirstFrameAVDataOffset = $avdataoffset + $SynchSeekOffset; + if (!$this->decodeMPEGaudioHeader($FirstFrameAVDataOffset, $FirstFrameThisfileInfo, false)) { + // if this is the first valid MPEG-audio frame, save it in case it's a VBR header frame and there's + // garbage between this frame and a valid sequence of MPEG-audio frames, to be restored below + unset($FirstFrameThisfileInfo); + } + } + + $dummy = $info; // only overwrite real data if valid header found + if ($this->decodeMPEGaudioHeader($avdataoffset + $SynchSeekOffset, $dummy, true)) { + $info = $dummy; + $info['avdataoffset'] = $avdataoffset + $SynchSeekOffset; + switch (isset($info['fileformat']) ? $info['fileformat'] : '') { + case '': + case 'id3': + case 'ape': + case 'mp3': + $info['fileformat'] = 'mp3'; + $info['audio']['dataformat'] = 'mp3'; + break; + } + if (isset($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode']) && ($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr')) { + if (!(abs($info['audio']['bitrate'] - $FirstFrameThisfileInfo['audio']['bitrate']) <= 1)) { + // If there is garbage data between a valid VBR header frame and a sequence + // of valid MPEG-audio frames the VBR data is no longer discarded. + $info = $FirstFrameThisfileInfo; + $info['avdataoffset'] = $FirstFrameAVDataOffset; + $info['fileformat'] = 'mp3'; + $info['audio']['dataformat'] = 'mp3'; + $dummy = $info; + unset($dummy['mpeg']['audio']); + $GarbageOffsetStart = $FirstFrameAVDataOffset + $FirstFrameThisfileInfo['mpeg']['audio']['framelength']; + $GarbageOffsetEnd = $avdataoffset + $SynchSeekOffset; + if ($this->decodeMPEGaudioHeader($GarbageOffsetEnd, $dummy, true, true)) { + $info = $dummy; + $info['avdataoffset'] = $GarbageOffsetEnd; + $info['warning'][] = 'apparently-valid VBR header not used because could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.'), but did find valid CBR stream starting at '.$GarbageOffsetEnd; + } else { + $info['warning'][] = 'using data from VBR header even though could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.')'; + } + } + } + if (isset($info['mpeg']['audio']['bitrate_mode']) && ($info['mpeg']['audio']['bitrate_mode'] == 'vbr') && !isset($info['mpeg']['audio']['VBR_method'])) { + // VBR file with no VBR header + $BitrateHistogram = true; + } + + if ($BitrateHistogram) { + + $info['mpeg']['audio']['stereo_distribution'] = array('stereo'=>0, 'joint stereo'=>0, 'dual channel'=>0, 'mono'=>0); + $info['mpeg']['audio']['version_distribution'] = array('1'=>0, '2'=>0, '2.5'=>0); + + if ($info['mpeg']['audio']['version'] == '1') { + if ($info['mpeg']['audio']['layer'] == 3) { + $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 40000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 320000=>0); + } elseif ($info['mpeg']['audio']['layer'] == 2) { + $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 320000=>0, 384000=>0); + } elseif ($info['mpeg']['audio']['layer'] == 1) { + $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 64000=>0, 96000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 288000=>0, 320000=>0, 352000=>0, 384000=>0, 416000=>0, 448000=>0); + } + } elseif ($info['mpeg']['audio']['layer'] == 1) { + $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 144000=>0, 160000=>0, 176000=>0, 192000=>0, 224000=>0, 256000=>0); + } else { + $info['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 8000=>0, 16000=>0, 24000=>0, 32000=>0, 40000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 144000=>0, 160000=>0); + } + + $dummy = array('error'=>$info['error'], 'warning'=>$info['warning'], 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']); + $synchstartoffset = $info['avdataoffset']; + $this->fseek($info['avdataoffset']); + + // you can play with these numbers: + $max_frames_scan = 50000; + $max_scan_segments = 10; + + // don't play with these numbers: + $FastMode = false; + $SynchErrorsFound = 0; + $frames_scanned = 0; + $this_scan_segment = 0; + $frames_scan_per_segment = ceil($max_frames_scan / $max_scan_segments); + $pct_data_scanned = 0; + for ($current_segment = 0; $current_segment < $max_scan_segments; $current_segment++) { + $frames_scanned_this_segment = 0; + if ($this->ftell() >= $info['avdataend']) { + break; + } + $scan_start_offset[$current_segment] = max($this->ftell(), $info['avdataoffset'] + round($current_segment * (($info['avdataend'] - $info['avdataoffset']) / $max_scan_segments))); + if ($current_segment > 0) { + $this->fseek($scan_start_offset[$current_segment]); + $buffer_4k = $this->fread(4096); + for ($j = 0; $j < (strlen($buffer_4k) - 4); $j++) { + if (($buffer_4k{$j} == "\xFF") && ($buffer_4k{($j + 1)} > "\xE0")) { // synch detected + if ($this->decodeMPEGaudioHeader($scan_start_offset[$current_segment] + $j, $dummy, false, false, $FastMode)) { + $calculated_next_offset = $scan_start_offset[$current_segment] + $j + $dummy['mpeg']['audio']['framelength']; + if ($this->decodeMPEGaudioHeader($calculated_next_offset, $dummy, false, false, $FastMode)) { + $scan_start_offset[$current_segment] += $j; + break; + } + } + } + } + } + $synchstartoffset = $scan_start_offset[$current_segment]; + while ($this->decodeMPEGaudioHeader($synchstartoffset, $dummy, false, false, $FastMode)) { + $FastMode = true; + $thisframebitrate = $MPEGaudioBitrateLookup[$MPEGaudioVersionLookup[$dummy['mpeg']['audio']['raw']['version']]][$MPEGaudioLayerLookup[$dummy['mpeg']['audio']['raw']['layer']]][$dummy['mpeg']['audio']['raw']['bitrate']]; + + if (empty($dummy['mpeg']['audio']['framelength'])) { + $SynchErrorsFound++; + $synchstartoffset++; + } else { + getid3_lib::safe_inc($info['mpeg']['audio']['bitrate_distribution'][$thisframebitrate]); + getid3_lib::safe_inc($info['mpeg']['audio']['stereo_distribution'][$dummy['mpeg']['audio']['channelmode']]); + getid3_lib::safe_inc($info['mpeg']['audio']['version_distribution'][$dummy['mpeg']['audio']['version']]); + $synchstartoffset += $dummy['mpeg']['audio']['framelength']; + } + $frames_scanned++; + if ($frames_scan_per_segment && (++$frames_scanned_this_segment >= $frames_scan_per_segment)) { + $this_pct_scanned = ($this->ftell() - $scan_start_offset[$current_segment]) / ($info['avdataend'] - $info['avdataoffset']); + if (($current_segment == 0) && (($this_pct_scanned * $max_scan_segments) >= 1)) { + // file likely contains < $max_frames_scan, just scan as one segment + $max_scan_segments = 1; + $frames_scan_per_segment = $max_frames_scan; + } else { + $pct_data_scanned += $this_pct_scanned; + break; + } + } + } + } + if ($pct_data_scanned > 0) { + $info['warning'][] = 'too many MPEG audio frames to scan, only scanned '.$frames_scanned.' frames in '.$max_scan_segments.' segments ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.'; + foreach ($info['mpeg']['audio'] as $key1 => $value1) { + if (!preg_match('#_distribution$#i', $key1)) { + continue; + } + foreach ($value1 as $key2 => $value2) { + $info['mpeg']['audio'][$key1][$key2] = round($value2 / $pct_data_scanned); + } + } + } + + if ($SynchErrorsFound > 0) { + $info['warning'][] = 'Found '.$SynchErrorsFound.' synch errors in histogram analysis'; + //return false; + } + + $bittotal = 0; + $framecounter = 0; + foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bitratevalue => $bitratecount) { + $framecounter += $bitratecount; + if ($bitratevalue != 'free') { + $bittotal += ($bitratevalue * $bitratecount); + } + } + if ($framecounter == 0) { + $info['error'][] = 'Corrupt MP3 file: framecounter == zero'; + return false; + } + $info['mpeg']['audio']['frame_count'] = getid3_lib::CastAsInt($framecounter); + $info['mpeg']['audio']['bitrate'] = ($bittotal / $framecounter); + + $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate']; + + + // Definitively set VBR vs CBR, even if the Xing/LAME/VBRI header says differently + $distinct_bitrates = 0; + foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bitrate_value => $bitrate_count) { + if ($bitrate_count > 0) { + $distinct_bitrates++; + } + } + if ($distinct_bitrates > 1) { + $info['mpeg']['audio']['bitrate_mode'] = 'vbr'; + } else { + $info['mpeg']['audio']['bitrate_mode'] = 'cbr'; + } + $info['audio']['bitrate_mode'] = $info['mpeg']['audio']['bitrate_mode']; + + } + + break; // exit while() + } + } + + $SynchSeekOffset++; + if (($avdataoffset + $SynchSeekOffset) >= $info['avdataend']) { + // end of file/data + + if (empty($info['mpeg']['audio'])) { + + $info['error'][] = 'could not find valid MPEG synch before end of file'; + if (isset($info['audio']['bitrate'])) { + unset($info['audio']['bitrate']); + } + if (isset($info['mpeg']['audio'])) { + unset($info['mpeg']['audio']); + } + if (isset($info['mpeg']) && (!is_array($info['mpeg']) || empty($info['mpeg']))) { + unset($info['mpeg']); + } + return false; + + } + break; + } + + } + $info['audio']['channels'] = $info['mpeg']['audio']['channels']; + $info['audio']['channelmode'] = $info['mpeg']['audio']['channelmode']; + $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate']; + return true; + } + + + public static function MPEGaudioVersionArray() { + static $MPEGaudioVersion = array('2.5', false, '2', '1'); + return $MPEGaudioVersion; + } + + public static function MPEGaudioLayerArray() { + static $MPEGaudioLayer = array(false, 3, 2, 1); + return $MPEGaudioLayer; + } + + public static function MPEGaudioBitrateArray() { + static $MPEGaudioBitrate; + if (empty($MPEGaudioBitrate)) { + $MPEGaudioBitrate = array ( + '1' => array (1 => array('free', 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000, 416000, 448000), + 2 => array('free', 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 384000), + 3 => array('free', 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000) + ), + + '2' => array (1 => array('free', 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, 176000, 192000, 224000, 256000), + 2 => array('free', 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000), + ) + ); + $MPEGaudioBitrate['2'][3] = $MPEGaudioBitrate['2'][2]; + $MPEGaudioBitrate['2.5'] = $MPEGaudioBitrate['2']; + } + return $MPEGaudioBitrate; + } + + public static function MPEGaudioFrequencyArray() { + static $MPEGaudioFrequency; + if (empty($MPEGaudioFrequency)) { + $MPEGaudioFrequency = array ( + '1' => array(44100, 48000, 32000), + '2' => array(22050, 24000, 16000), + '2.5' => array(11025, 12000, 8000) + ); + } + return $MPEGaudioFrequency; + } + + public static function MPEGaudioChannelModeArray() { + static $MPEGaudioChannelMode = array('stereo', 'joint stereo', 'dual channel', 'mono'); + return $MPEGaudioChannelMode; + } + + public static function MPEGaudioModeExtensionArray() { + static $MPEGaudioModeExtension; + if (empty($MPEGaudioModeExtension)) { + $MPEGaudioModeExtension = array ( + 1 => array('4-31', '8-31', '12-31', '16-31'), + 2 => array('4-31', '8-31', '12-31', '16-31'), + 3 => array('', 'IS', 'MS', 'IS+MS') + ); + } + return $MPEGaudioModeExtension; + } + + public static function MPEGaudioEmphasisArray() { + static $MPEGaudioEmphasis = array('none', '50/15ms', false, 'CCIT J.17'); + return $MPEGaudioEmphasis; + } + + public static function MPEGaudioHeaderBytesValid($head4, $allowBitrate15=false) { + return self::MPEGaudioHeaderValid(self::MPEGaudioHeaderDecode($head4), false, $allowBitrate15); + } + + public static function MPEGaudioHeaderValid($rawarray, $echoerrors=false, $allowBitrate15=false) { + if (($rawarray['synch'] & 0x0FFE) != 0x0FFE) { + return false; + } + + static $MPEGaudioVersionLookup; + static $MPEGaudioLayerLookup; + static $MPEGaudioBitrateLookup; + static $MPEGaudioFrequencyLookup; + static $MPEGaudioChannelModeLookup; + static $MPEGaudioModeExtensionLookup; + static $MPEGaudioEmphasisLookup; + if (empty($MPEGaudioVersionLookup)) { + $MPEGaudioVersionLookup = self::MPEGaudioVersionArray(); + $MPEGaudioLayerLookup = self::MPEGaudioLayerArray(); + $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray(); + $MPEGaudioFrequencyLookup = self::MPEGaudioFrequencyArray(); + $MPEGaudioChannelModeLookup = self::MPEGaudioChannelModeArray(); + $MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray(); + $MPEGaudioEmphasisLookup = self::MPEGaudioEmphasisArray(); + } + + if (isset($MPEGaudioVersionLookup[$rawarray['version']])) { + $decodedVersion = $MPEGaudioVersionLookup[$rawarray['version']]; + } else { + echo ($echoerrors ? "\n".'invalid Version ('.$rawarray['version'].')' : ''); + return false; + } + if (isset($MPEGaudioLayerLookup[$rawarray['layer']])) { + $decodedLayer = $MPEGaudioLayerLookup[$rawarray['layer']]; + } else { + echo ($echoerrors ? "\n".'invalid Layer ('.$rawarray['layer'].')' : ''); + return false; + } + if (!isset($MPEGaudioBitrateLookup[$decodedVersion][$decodedLayer][$rawarray['bitrate']])) { + echo ($echoerrors ? "\n".'invalid Bitrate ('.$rawarray['bitrate'].')' : ''); + if ($rawarray['bitrate'] == 15) { + // known issue in LAME 3.90 - 3.93.1 where free-format has bitrate ID of 15 instead of 0 + // let it go through here otherwise file will not be identified + if (!$allowBitrate15) { + return false; + } + } else { + return false; + } + } + if (!isset($MPEGaudioFrequencyLookup[$decodedVersion][$rawarray['sample_rate']])) { + echo ($echoerrors ? "\n".'invalid Frequency ('.$rawarray['sample_rate'].')' : ''); + return false; + } + if (!isset($MPEGaudioChannelModeLookup[$rawarray['channelmode']])) { + echo ($echoerrors ? "\n".'invalid ChannelMode ('.$rawarray['channelmode'].')' : ''); + return false; + } + if (!isset($MPEGaudioModeExtensionLookup[$decodedLayer][$rawarray['modeextension']])) { + echo ($echoerrors ? "\n".'invalid Mode Extension ('.$rawarray['modeextension'].')' : ''); + return false; + } + if (!isset($MPEGaudioEmphasisLookup[$rawarray['emphasis']])) { + echo ($echoerrors ? "\n".'invalid Emphasis ('.$rawarray['emphasis'].')' : ''); + return false; + } + // These are just either set or not set, you can't mess that up :) + // $rawarray['protection']; + // $rawarray['padding']; + // $rawarray['private']; + // $rawarray['copyright']; + // $rawarray['original']; + + return true; + } + + public static function MPEGaudioHeaderDecode($Header4Bytes) { + // AAAA AAAA AAAB BCCD EEEE FFGH IIJJ KLMM + // A - Frame sync (all bits set) + // B - MPEG Audio version ID + // C - Layer description + // D - Protection bit + // E - Bitrate index + // F - Sampling rate frequency index + // G - Padding bit + // H - Private bit + // I - Channel Mode + // J - Mode extension (Only if Joint stereo) + // K - Copyright + // L - Original + // M - Emphasis + + if (strlen($Header4Bytes) != 4) { + return false; + } + + $MPEGrawHeader['synch'] = (getid3_lib::BigEndian2Int(substr($Header4Bytes, 0, 2)) & 0xFFE0) >> 4; + $MPEGrawHeader['version'] = (ord($Header4Bytes{1}) & 0x18) >> 3; // BB + $MPEGrawHeader['layer'] = (ord($Header4Bytes{1}) & 0x06) >> 1; // CC + $MPEGrawHeader['protection'] = (ord($Header4Bytes{1}) & 0x01); // D + $MPEGrawHeader['bitrate'] = (ord($Header4Bytes{2}) & 0xF0) >> 4; // EEEE + $MPEGrawHeader['sample_rate'] = (ord($Header4Bytes{2}) & 0x0C) >> 2; // FF + $MPEGrawHeader['padding'] = (ord($Header4Bytes{2}) & 0x02) >> 1; // G + $MPEGrawHeader['private'] = (ord($Header4Bytes{2}) & 0x01); // H + $MPEGrawHeader['channelmode'] = (ord($Header4Bytes{3}) & 0xC0) >> 6; // II + $MPEGrawHeader['modeextension'] = (ord($Header4Bytes{3}) & 0x30) >> 4; // JJ + $MPEGrawHeader['copyright'] = (ord($Header4Bytes{3}) & 0x08) >> 3; // K + $MPEGrawHeader['original'] = (ord($Header4Bytes{3}) & 0x04) >> 2; // L + $MPEGrawHeader['emphasis'] = (ord($Header4Bytes{3}) & 0x03); // MM + + return $MPEGrawHeader; + } + + public static function MPEGaudioFrameLength(&$bitrate, &$version, &$layer, $padding, &$samplerate) { + static $AudioFrameLengthCache = array(); + + if (!isset($AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate])) { + $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = false; + if ($bitrate != 'free') { + + if ($version == '1') { + + if ($layer == '1') { + + // For Layer I slot is 32 bits long + $FrameLengthCoefficient = 48; + $SlotLength = 4; + + } else { // Layer 2 / 3 + + // for Layer 2 and Layer 3 slot is 8 bits long. + $FrameLengthCoefficient = 144; + $SlotLength = 1; + + } + + } else { // MPEG-2 / MPEG-2.5 + + if ($layer == '1') { + + // For Layer I slot is 32 bits long + $FrameLengthCoefficient = 24; + $SlotLength = 4; + + } elseif ($layer == '2') { + + // for Layer 2 and Layer 3 slot is 8 bits long. + $FrameLengthCoefficient = 144; + $SlotLength = 1; + + } else { // layer 3 + + // for Layer 2 and Layer 3 slot is 8 bits long. + $FrameLengthCoefficient = 72; + $SlotLength = 1; + + } + + } + + // FrameLengthInBytes = ((Coefficient * BitRate) / SampleRate) + Padding + if ($samplerate > 0) { + $NewFramelength = ($FrameLengthCoefficient * $bitrate) / $samplerate; + $NewFramelength = floor($NewFramelength / $SlotLength) * $SlotLength; // round to next-lower multiple of SlotLength (1 byte for Layer 2/3, 4 bytes for Layer I) + if ($padding) { + $NewFramelength += $SlotLength; + } + $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = (int) $NewFramelength; + } + } + } + return $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate]; + } + + public static function ClosestStandardMP3Bitrate($bit_rate) { + static $standard_bit_rates = array (320000, 256000, 224000, 192000, 160000, 128000, 112000, 96000, 80000, 64000, 56000, 48000, 40000, 32000, 24000, 16000, 8000); + static $bit_rate_table = array (0=>'-'); + $round_bit_rate = intval(round($bit_rate, -3)); + if (!isset($bit_rate_table[$round_bit_rate])) { + if ($round_bit_rate > max($standard_bit_rates)) { + $bit_rate_table[$round_bit_rate] = round($bit_rate, 2 - strlen($bit_rate)); + } else { + $bit_rate_table[$round_bit_rate] = max($standard_bit_rates); + foreach ($standard_bit_rates as $standard_bit_rate) { + if ($round_bit_rate >= $standard_bit_rate + (($bit_rate_table[$round_bit_rate] - $standard_bit_rate) / 2)) { + break; + } + $bit_rate_table[$round_bit_rate] = $standard_bit_rate; + } + } + } + return $bit_rate_table[$round_bit_rate]; + } + + public static function XingVBRidOffset($version, $channelmode) { + static $XingVBRidOffsetCache = array(); + if (empty($XingVBRidOffset)) { + $XingVBRidOffset = array ( + '1' => array ('mono' => 0x15, // 4 + 17 = 21 + 'stereo' => 0x24, // 4 + 32 = 36 + 'joint stereo' => 0x24, + 'dual channel' => 0x24 + ), + + '2' => array ('mono' => 0x0D, // 4 + 9 = 13 + 'stereo' => 0x15, // 4 + 17 = 21 + 'joint stereo' => 0x15, + 'dual channel' => 0x15 + ), + + '2.5' => array ('mono' => 0x15, + 'stereo' => 0x15, + 'joint stereo' => 0x15, + 'dual channel' => 0x15 + ) + ); + } + return $XingVBRidOffset[$version][$channelmode]; + } + + public static function LAMEvbrMethodLookup($VBRmethodID) { + static $LAMEvbrMethodLookup = array( + 0x00 => 'unknown', + 0x01 => 'cbr', + 0x02 => 'abr', + 0x03 => 'vbr-old / vbr-rh', + 0x04 => 'vbr-new / vbr-mtrh', + 0x05 => 'vbr-mt', + 0x06 => 'vbr (full vbr method 4)', + 0x08 => 'cbr (constant bitrate 2 pass)', + 0x09 => 'abr (2 pass)', + 0x0F => 'reserved' + ); + return (isset($LAMEvbrMethodLookup[$VBRmethodID]) ? $LAMEvbrMethodLookup[$VBRmethodID] : ''); + } + + public static function LAMEmiscStereoModeLookup($StereoModeID) { + static $LAMEmiscStereoModeLookup = array( + 0 => 'mono', + 1 => 'stereo', + 2 => 'dual mono', + 3 => 'joint stereo', + 4 => 'forced stereo', + 5 => 'auto', + 6 => 'intensity stereo', + 7 => 'other' + ); + return (isset($LAMEmiscStereoModeLookup[$StereoModeID]) ? $LAMEmiscStereoModeLookup[$StereoModeID] : ''); + } + + public static function LAMEmiscSourceSampleFrequencyLookup($SourceSampleFrequencyID) { + static $LAMEmiscSourceSampleFrequencyLookup = array( + 0 => '<= 32 kHz', + 1 => '44.1 kHz', + 2 => '48 kHz', + 3 => '> 48kHz' + ); + return (isset($LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID]) ? $LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID] : ''); + } + + public static function LAMEsurroundInfoLookup($SurroundInfoID) { + static $LAMEsurroundInfoLookup = array( + 0 => 'no surround info', + 1 => 'DPL encoding', + 2 => 'DPL2 encoding', + 3 => 'Ambisonic encoding' + ); + return (isset($LAMEsurroundInfoLookup[$SurroundInfoID]) ? $LAMEsurroundInfoLookup[$SurroundInfoID] : 'reserved'); + } + + public static function LAMEpresetUsedLookup($LAMEtag) { + + if ($LAMEtag['preset_used_id'] == 0) { + // no preset used (LAME >=3.93) + // no preset recorded (LAME <3.93) + return ''; + } + $LAMEpresetUsedLookup = array(); + + ///// THIS PART CANNOT BE STATIC . + for ($i = 8; $i <= 320; $i++) { + switch ($LAMEtag['vbr_method']) { + case 'cbr': + $LAMEpresetUsedLookup[$i] = '--alt-preset '.$LAMEtag['vbr_method'].' '.$i; + break; + case 'abr': + default: // other VBR modes shouldn't be here(?) + $LAMEpresetUsedLookup[$i] = '--alt-preset '.$i; + break; + } + } + + // named old-style presets (studio, phone, voice, etc) are handled in GuessEncoderOptions() + + // named alt-presets + $LAMEpresetUsedLookup[1000] = '--r3mix'; + $LAMEpresetUsedLookup[1001] = '--alt-preset standard'; + $LAMEpresetUsedLookup[1002] = '--alt-preset extreme'; + $LAMEpresetUsedLookup[1003] = '--alt-preset insane'; + $LAMEpresetUsedLookup[1004] = '--alt-preset fast standard'; + $LAMEpresetUsedLookup[1005] = '--alt-preset fast extreme'; + $LAMEpresetUsedLookup[1006] = '--alt-preset medium'; + $LAMEpresetUsedLookup[1007] = '--alt-preset fast medium'; + + // LAME 3.94 additions/changes + $LAMEpresetUsedLookup[1010] = '--preset portable'; // 3.94a15 Oct 21 2003 + $LAMEpresetUsedLookup[1015] = '--preset radio'; // 3.94a15 Oct 21 2003 + + $LAMEpresetUsedLookup[320] = '--preset insane'; // 3.94a15 Nov 12 2003 + $LAMEpresetUsedLookup[410] = '-V9'; + $LAMEpresetUsedLookup[420] = '-V8'; + $LAMEpresetUsedLookup[440] = '-V6'; + $LAMEpresetUsedLookup[430] = '--preset radio'; // 3.94a15 Nov 12 2003 + $LAMEpresetUsedLookup[450] = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'portable'; // 3.94a15 Nov 12 2003 + $LAMEpresetUsedLookup[460] = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'medium'; // 3.94a15 Nov 12 2003 + $LAMEpresetUsedLookup[470] = '--r3mix'; // 3.94b1 Dec 18 2003 + $LAMEpresetUsedLookup[480] = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'standard'; // 3.94a15 Nov 12 2003 + $LAMEpresetUsedLookup[490] = '-V1'; + $LAMEpresetUsedLookup[500] = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'extreme'; // 3.94a15 Nov 12 2003 + + return (isset($LAMEpresetUsedLookup[$LAMEtag['preset_used_id']]) ? $LAMEpresetUsedLookup[$LAMEtag['preset_used_id']] : 'new/unknown preset: '.$LAMEtag['preset_used_id'].' - report to info@getid3.org'); + } + +} \ No newline at end of file diff --git a/wp-includes/ID3/module.audio.ogg.php b/wp-includes/ID3/module.audio.ogg.php new file mode 100644 index 0000000..f3bb004 --- /dev/null +++ b/wp-includes/ID3/module.audio.ogg.php @@ -0,0 +1,756 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +// also https://github.com/JamesHeinrich/getID3 // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.audio.ogg.php // +// module for analyzing Ogg Vorbis, OggFLAC and Speex files // +// dependencies: module.audio.flac.php // +// /// +///////////////////////////////////////////////////////////////// + +getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.flac.php', __FILE__, true); + +class getid3_ogg extends getid3_handler +{ + // http://xiph.org/vorbis/doc/Vorbis_I_spec.html + public function Analyze() { + $info = &$this->getid3->info; + + $info['fileformat'] = 'ogg'; + + // Warn about illegal tags - only vorbiscomments are allowed + if (isset($info['id3v2'])) { + $info['warning'][] = 'Illegal ID3v2 tag present.'; + } + if (isset($info['id3v1'])) { + $info['warning'][] = 'Illegal ID3v1 tag present.'; + } + if (isset($info['ape'])) { + $info['warning'][] = 'Illegal APE tag present.'; + } + + + // Page 1 - Stream Header + + $this->fseek($info['avdataoffset']); + + $oggpageinfo = $this->ParseOggPageHeader(); + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo; + + if ($this->ftell() >= $this->getid3->fread_buffer_size()) { + $info['error'][] = 'Could not find start of Ogg page in the first '.$this->getid3->fread_buffer_size().' bytes (this might not be an Ogg-Vorbis file?)'; + unset($info['fileformat']); + unset($info['ogg']); + return false; + } + + $filedata = $this->fread($oggpageinfo['page_length']); + $filedataoffset = 0; + + if (substr($filedata, 0, 4) == 'fLaC') { + + $info['audio']['dataformat'] = 'flac'; + $info['audio']['bitrate_mode'] = 'vbr'; + $info['audio']['lossless'] = true; + + } elseif (substr($filedata, 1, 6) == 'vorbis') { + + $this->ParseVorbisPageHeader($filedata, $filedataoffset, $oggpageinfo); + + } elseif (substr($filedata, 0, 8) == 'Speex ') { + + // http://www.speex.org/manual/node10.html + + $info['audio']['dataformat'] = 'speex'; + $info['mime_type'] = 'audio/speex'; + $info['audio']['bitrate_mode'] = 'abr'; + $info['audio']['lossless'] = false; + + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_string'] = substr($filedata, $filedataoffset, 8); // hard-coded to 'Speex ' + $filedataoffset += 8; + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version'] = substr($filedata, $filedataoffset, 20); + $filedataoffset += 20; + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version_id'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['header_size'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode_bitstream_version'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['bitrate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['framesize'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['frames_per_packet'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['extra_headers'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved1'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved2'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + + $info['speex']['speex_version'] = trim($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version']); + $info['speex']['sample_rate'] = $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate']; + $info['speex']['channels'] = $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels']; + $info['speex']['vbr'] = (bool) $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr']; + $info['speex']['band_type'] = $this->SpeexBandModeLookup($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode']); + + $info['audio']['sample_rate'] = $info['speex']['sample_rate']; + $info['audio']['channels'] = $info['speex']['channels']; + if ($info['speex']['vbr']) { + $info['audio']['bitrate_mode'] = 'vbr'; + } + + } elseif (substr($filedata, 0, 7) == "\x80".'theora') { + + // http://www.theora.org/doc/Theora.pdf (section 6.2) + + $info['ogg']['pageheader']['theora']['theora_magic'] = substr($filedata, $filedataoffset, 7); // hard-coded to "\x80.'theora' + $filedataoffset += 7; + $info['ogg']['pageheader']['theora']['version_major'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1)); + $filedataoffset += 1; + $info['ogg']['pageheader']['theora']['version_minor'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1)); + $filedataoffset += 1; + $info['ogg']['pageheader']['theora']['version_revision'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1)); + $filedataoffset += 1; + $info['ogg']['pageheader']['theora']['frame_width_macroblocks'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 2)); + $filedataoffset += 2; + $info['ogg']['pageheader']['theora']['frame_height_macroblocks'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 2)); + $filedataoffset += 2; + $info['ogg']['pageheader']['theora']['resolution_x'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3)); + $filedataoffset += 3; + $info['ogg']['pageheader']['theora']['resolution_y'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3)); + $filedataoffset += 3; + $info['ogg']['pageheader']['theora']['picture_offset_x'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1)); + $filedataoffset += 1; + $info['ogg']['pageheader']['theora']['picture_offset_y'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1)); + $filedataoffset += 1; + $info['ogg']['pageheader']['theora']['frame_rate_numerator'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $info['ogg']['pageheader']['theora']['frame_rate_denominator'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $info['ogg']['pageheader']['theora']['pixel_aspect_numerator'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3)); + $filedataoffset += 3; + $info['ogg']['pageheader']['theora']['pixel_aspect_denominator'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3)); + $filedataoffset += 3; + $info['ogg']['pageheader']['theora']['color_space_id'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 1)); + $filedataoffset += 1; + $info['ogg']['pageheader']['theora']['nominal_bitrate'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 3)); + $filedataoffset += 3; + $info['ogg']['pageheader']['theora']['flags'] = getid3_lib::BigEndian2Int(substr($filedata, $filedataoffset, 2)); + $filedataoffset += 2; + + $info['ogg']['pageheader']['theora']['quality'] = ($info['ogg']['pageheader']['theora']['flags'] & 0xFC00) >> 10; + $info['ogg']['pageheader']['theora']['kfg_shift'] = ($info['ogg']['pageheader']['theora']['flags'] & 0x03E0) >> 5; + $info['ogg']['pageheader']['theora']['pixel_format_id'] = ($info['ogg']['pageheader']['theora']['flags'] & 0x0018) >> 3; + $info['ogg']['pageheader']['theora']['reserved'] = ($info['ogg']['pageheader']['theora']['flags'] & 0x0007) >> 0; // should be 0 + $info['ogg']['pageheader']['theora']['color_space'] = self::TheoraColorSpace($info['ogg']['pageheader']['theora']['color_space_id']); + $info['ogg']['pageheader']['theora']['pixel_format'] = self::TheoraPixelFormat($info['ogg']['pageheader']['theora']['pixel_format_id']); + + $info['video']['dataformat'] = 'theora'; + $info['mime_type'] = 'video/ogg'; + //$info['audio']['bitrate_mode'] = 'abr'; + //$info['audio']['lossless'] = false; + $info['video']['resolution_x'] = $info['ogg']['pageheader']['theora']['resolution_x']; + $info['video']['resolution_y'] = $info['ogg']['pageheader']['theora']['resolution_y']; + if ($info['ogg']['pageheader']['theora']['frame_rate_denominator'] > 0) { + $info['video']['frame_rate'] = (float) $info['ogg']['pageheader']['theora']['frame_rate_numerator'] / $info['ogg']['pageheader']['theora']['frame_rate_denominator']; + } + if ($info['ogg']['pageheader']['theora']['pixel_aspect_denominator'] > 0) { + $info['video']['pixel_aspect_ratio'] = (float) $info['ogg']['pageheader']['theora']['pixel_aspect_numerator'] / $info['ogg']['pageheader']['theora']['pixel_aspect_denominator']; + } +$info['warning'][] = 'Ogg Theora (v3) not fully supported in this version of getID3 ['.$this->getid3->version().'] -- bitrate, playtime and all audio data are currently unavailable'; + + + } elseif (substr($filedata, 0, 8) == "fishead\x00") { + + // Ogg Skeleton version 3.0 Format Specification + // http://xiph.org/ogg/doc/skeleton.html + $filedataoffset += 8; + $info['ogg']['skeleton']['fishead']['raw']['version_major'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 2)); + $filedataoffset += 2; + $info['ogg']['skeleton']['fishead']['raw']['version_minor'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 2)); + $filedataoffset += 2; + $info['ogg']['skeleton']['fishead']['raw']['presentationtime_numerator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8)); + $filedataoffset += 8; + $info['ogg']['skeleton']['fishead']['raw']['presentationtime_denominator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8)); + $filedataoffset += 8; + $info['ogg']['skeleton']['fishead']['raw']['basetime_numerator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8)); + $filedataoffset += 8; + $info['ogg']['skeleton']['fishead']['raw']['basetime_denominator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8)); + $filedataoffset += 8; + $info['ogg']['skeleton']['fishead']['raw']['utc'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 20)); + $filedataoffset += 20; + + $info['ogg']['skeleton']['fishead']['version'] = $info['ogg']['skeleton']['fishead']['raw']['version_major'].'.'.$info['ogg']['skeleton']['fishead']['raw']['version_minor']; + $info['ogg']['skeleton']['fishead']['presentationtime'] = $info['ogg']['skeleton']['fishead']['raw']['presentationtime_numerator'] / $info['ogg']['skeleton']['fishead']['raw']['presentationtime_denominator']; + $info['ogg']['skeleton']['fishead']['basetime'] = $info['ogg']['skeleton']['fishead']['raw']['basetime_numerator'] / $info['ogg']['skeleton']['fishead']['raw']['basetime_denominator']; + $info['ogg']['skeleton']['fishead']['utc'] = $info['ogg']['skeleton']['fishead']['raw']['utc']; + + + $counter = 0; + do { + $oggpageinfo = $this->ParseOggPageHeader(); + $info['ogg']['pageheader'][$oggpageinfo['page_seqno'].'.'.$counter++] = $oggpageinfo; + $filedata = $this->fread($oggpageinfo['page_length']); + $this->fseek($oggpageinfo['page_end_offset']); + + if (substr($filedata, 0, 8) == "fisbone\x00") { + + $filedataoffset = 8; + $info['ogg']['skeleton']['fisbone']['raw']['message_header_offset'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $info['ogg']['skeleton']['fisbone']['raw']['serial_number'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $info['ogg']['skeleton']['fisbone']['raw']['number_header_packets'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $info['ogg']['skeleton']['fisbone']['raw']['granulerate_numerator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8)); + $filedataoffset += 8; + $info['ogg']['skeleton']['fisbone']['raw']['granulerate_denominator'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8)); + $filedataoffset += 8; + $info['ogg']['skeleton']['fisbone']['raw']['basegranule'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8)); + $filedataoffset += 8; + $info['ogg']['skeleton']['fisbone']['raw']['preroll'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $info['ogg']['skeleton']['fisbone']['raw']['granuleshift'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); + $filedataoffset += 1; + $info['ogg']['skeleton']['fisbone']['raw']['padding'] = substr($filedata, $filedataoffset, 3); + $filedataoffset += 3; + + } elseif (substr($filedata, 1, 6) == 'theora') { + + $info['video']['dataformat'] = 'theora1'; + $info['error'][] = 'Ogg Theora (v1) not correctly handled in this version of getID3 ['.$this->getid3->version().']'; + //break; + + } elseif (substr($filedata, 1, 6) == 'vorbis') { + + $this->ParseVorbisPageHeader($filedata, $filedataoffset, $oggpageinfo); + + } else { + $info['error'][] = 'unexpected'; + //break; + } + //} while ($oggpageinfo['page_seqno'] == 0); + } while (($oggpageinfo['page_seqno'] == 0) && (substr($filedata, 0, 8) != "fisbone\x00")); + + $this->fseek($oggpageinfo['page_start_offset']); + + $info['error'][] = 'Ogg Skeleton not correctly handled in this version of getID3 ['.$this->getid3->version().']'; + //return false; + + } else { + + $info['error'][] = 'Expecting either "Speex " or "vorbis" identifier strings, found "'.substr($filedata, 0, 8).'"'; + unset($info['ogg']); + unset($info['mime_type']); + return false; + + } + + // Page 2 - Comment Header + $oggpageinfo = $this->ParseOggPageHeader(); + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo; + + switch ($info['audio']['dataformat']) { + case 'vorbis': + $filedata = $this->fread($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']); + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, 0, 1)); + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, 1, 6); // hard-coded to 'vorbis' + + $this->ParseVorbisComments(); + break; + + case 'flac': + $flac = new getid3_flac($this->getid3); + if (!$flac->parseMETAdata()) { + $info['error'][] = 'Failed to parse FLAC headers'; + return false; + } + unset($flac); + break; + + case 'speex': + $this->fseek($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length'], SEEK_CUR); + $this->ParseVorbisComments(); + break; + } + + + // Last Page - Number of Samples + if (!getid3_lib::intValueSupported($info['avdataend'])) { + + $info['warning'][] = 'Unable to parse Ogg end chunk file (PHP does not support file operations beyond '.round(PHP_INT_MAX / 1073741824).'GB)'; + + } else { + + $this->fseek(max($info['avdataend'] - $this->getid3->fread_buffer_size(), 0)); + $LastChunkOfOgg = strrev($this->fread($this->getid3->fread_buffer_size())); + if ($LastOggSpostion = strpos($LastChunkOfOgg, 'SggO')) { + $this->fseek($info['avdataend'] - ($LastOggSpostion + strlen('SggO'))); + $info['avdataend'] = $this->ftell(); + $info['ogg']['pageheader']['eos'] = $this->ParseOggPageHeader(); + $info['ogg']['samples'] = $info['ogg']['pageheader']['eos']['pcm_abs_position']; + if ($info['ogg']['samples'] == 0) { + $info['error'][] = 'Corrupt Ogg file: eos.number of samples == zero'; + return false; + } + if (!empty($info['audio']['sample_rate'])) { + $info['ogg']['bitrate_average'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / ($info['ogg']['samples'] / $info['audio']['sample_rate']); + } + } + + } + + if (!empty($info['ogg']['bitrate_average'])) { + $info['audio']['bitrate'] = $info['ogg']['bitrate_average']; + } elseif (!empty($info['ogg']['bitrate_nominal'])) { + $info['audio']['bitrate'] = $info['ogg']['bitrate_nominal']; + } elseif (!empty($info['ogg']['bitrate_min']) && !empty($info['ogg']['bitrate_max'])) { + $info['audio']['bitrate'] = ($info['ogg']['bitrate_min'] + $info['ogg']['bitrate_max']) / 2; + } + if (isset($info['audio']['bitrate']) && !isset($info['playtime_seconds'])) { + if ($info['audio']['bitrate'] == 0) { + $info['error'][] = 'Corrupt Ogg file: bitrate_audio == zero'; + return false; + } + $info['playtime_seconds'] = (float) ((($info['avdataend'] - $info['avdataoffset']) * 8) / $info['audio']['bitrate']); + } + + if (isset($info['ogg']['vendor'])) { + $info['audio']['encoder'] = preg_replace('/^Encoded with /', '', $info['ogg']['vendor']); + + // Vorbis only + if ($info['audio']['dataformat'] == 'vorbis') { + + // Vorbis 1.0 starts with Xiph.Org + if (preg_match('/^Xiph.Org/', $info['audio']['encoder'])) { + + if ($info['audio']['bitrate_mode'] == 'abr') { + + // Set -b 128 on abr files + $info['audio']['encoder_options'] = '-b '.round($info['ogg']['bitrate_nominal'] / 1000); + + } elseif (($info['audio']['bitrate_mode'] == 'vbr') && ($info['audio']['channels'] == 2) && ($info['audio']['sample_rate'] >= 44100) && ($info['audio']['sample_rate'] <= 48000)) { + // Set -q N on vbr files + $info['audio']['encoder_options'] = '-q '.$this->get_quality_from_nominal_bitrate($info['ogg']['bitrate_nominal']); + + } + } + + if (empty($info['audio']['encoder_options']) && !empty($info['ogg']['bitrate_nominal'])) { + $info['audio']['encoder_options'] = 'Nominal bitrate: '.intval(round($info['ogg']['bitrate_nominal'] / 1000)).'kbps'; + } + } + } + + return true; + } + + public function ParseVorbisPageHeader(&$filedata, &$filedataoffset, &$oggpageinfo) { + $info = &$this->getid3->info; + $info['audio']['dataformat'] = 'vorbis'; + $info['audio']['lossless'] = false; + + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); + $filedataoffset += 1; + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, $filedataoffset, 6); // hard-coded to 'vorbis' + $filedataoffset += 6; + $info['ogg']['bitstreamversion'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $info['ogg']['numberofchannels'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); + $filedataoffset += 1; + $info['audio']['channels'] = $info['ogg']['numberofchannels']; + $info['ogg']['samplerate'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + if ($info['ogg']['samplerate'] == 0) { + $info['error'][] = 'Corrupt Ogg file: sample rate == zero'; + return false; + } + $info['audio']['sample_rate'] = $info['ogg']['samplerate']; + $info['ogg']['samples'] = 0; // filled in later + $info['ogg']['bitrate_average'] = 0; // filled in later + $info['ogg']['bitrate_max'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $info['ogg']['bitrate_nominal'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $info['ogg']['bitrate_min'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $info['ogg']['blocksize_small'] = pow(2, getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)) & 0x0F); + $info['ogg']['blocksize_large'] = pow(2, (getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)) & 0xF0) >> 4); + $info['ogg']['stop_bit'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); // must be 1, marks end of packet + + $info['audio']['bitrate_mode'] = 'vbr'; // overridden if actually abr + if ($info['ogg']['bitrate_max'] == 0xFFFFFFFF) { + unset($info['ogg']['bitrate_max']); + $info['audio']['bitrate_mode'] = 'abr'; + } + if ($info['ogg']['bitrate_nominal'] == 0xFFFFFFFF) { + unset($info['ogg']['bitrate_nominal']); + } + if ($info['ogg']['bitrate_min'] == 0xFFFFFFFF) { + unset($info['ogg']['bitrate_min']); + $info['audio']['bitrate_mode'] = 'abr'; + } + return true; + } + + public function ParseOggPageHeader() { + // http://xiph.org/ogg/vorbis/doc/framing.html + $oggheader['page_start_offset'] = $this->ftell(); // where we started from in the file + + $filedata = $this->fread($this->getid3->fread_buffer_size()); + $filedataoffset = 0; + while ((substr($filedata, $filedataoffset++, 4) != 'OggS')) { + if (($this->ftell() - $oggheader['page_start_offset']) >= $this->getid3->fread_buffer_size()) { + // should be found before here + return false; + } + if ((($filedataoffset + 28) > strlen($filedata)) || (strlen($filedata) < 28)) { + if ($this->feof() || (($filedata .= $this->fread($this->getid3->fread_buffer_size())) === false)) { + // get some more data, unless eof, in which case fail + return false; + } + } + } + $filedataoffset += strlen('OggS') - 1; // page, delimited by 'OggS' + + $oggheader['stream_structver'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); + $filedataoffset += 1; + $oggheader['flags_raw'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); + $filedataoffset += 1; + $oggheader['flags']['fresh'] = (bool) ($oggheader['flags_raw'] & 0x01); // fresh packet + $oggheader['flags']['bos'] = (bool) ($oggheader['flags_raw'] & 0x02); // first page of logical bitstream (bos) + $oggheader['flags']['eos'] = (bool) ($oggheader['flags_raw'] & 0x04); // last page of logical bitstream (eos) + + $oggheader['pcm_abs_position'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8)); + $filedataoffset += 8; + $oggheader['stream_serialno'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $oggheader['page_seqno'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $oggheader['page_checksum'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4)); + $filedataoffset += 4; + $oggheader['page_segments'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); + $filedataoffset += 1; + $oggheader['page_length'] = 0; + for ($i = 0; $i < $oggheader['page_segments']; $i++) { + $oggheader['segment_table'][$i] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); + $filedataoffset += 1; + $oggheader['page_length'] += $oggheader['segment_table'][$i]; + } + $oggheader['header_end_offset'] = $oggheader['page_start_offset'] + $filedataoffset; + $oggheader['page_end_offset'] = $oggheader['header_end_offset'] + $oggheader['page_length']; + $this->fseek($oggheader['header_end_offset']); + + return $oggheader; + } + + // http://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-810005 + public function ParseVorbisComments() { + $info = &$this->getid3->info; + + $OriginalOffset = $this->ftell(); + $commentdataoffset = 0; + $VorbisCommentPage = 1; + + switch ($info['audio']['dataformat']) { + case 'vorbis': + case 'speex': + $CommentStartOffset = $info['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset']; // Second Ogg page, after header block + $this->fseek($CommentStartOffset); + $commentdataoffset = 27 + $info['ogg']['pageheader'][$VorbisCommentPage]['page_segments']; + $commentdata = $this->fread(self::OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1) + $commentdataoffset); + + if ($info['audio']['dataformat'] == 'vorbis') { + $commentdataoffset += (strlen('vorbis') + 1); + } + break; + + case 'flac': + $CommentStartOffset = $info['flac']['VORBIS_COMMENT']['raw']['offset'] + 4; + $this->fseek($CommentStartOffset); + $commentdata = $this->fread($info['flac']['VORBIS_COMMENT']['raw']['block_length']); + break; + + default: + return false; + } + + $VendorSize = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4)); + $commentdataoffset += 4; + + $info['ogg']['vendor'] = substr($commentdata, $commentdataoffset, $VendorSize); + $commentdataoffset += $VendorSize; + + $CommentsCount = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4)); + $commentdataoffset += 4; + $info['avdataoffset'] = $CommentStartOffset + $commentdataoffset; + + $basicfields = array('TITLE', 'ARTIST', 'ALBUM', 'TRACKNUMBER', 'GENRE', 'DATE', 'DESCRIPTION', 'COMMENT'); + $ThisFileInfo_ogg_comments_raw = &$info['ogg']['comments_raw']; + for ($i = 0; $i < $CommentsCount; $i++) { + + $ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] = $CommentStartOffset + $commentdataoffset; + + if ($this->ftell() < ($ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] + 4)) { + if ($oggpageinfo = $this->ParseOggPageHeader()) { + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo; + + $VorbisCommentPage++; + + // First, save what we haven't read yet + $AsYetUnusedData = substr($commentdata, $commentdataoffset); + + // Then take that data off the end + $commentdata = substr($commentdata, 0, $commentdataoffset); + + // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct + $commentdata .= str_repeat("\x00", 27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']); + $commentdataoffset += (27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']); + + // Finally, stick the unused data back on the end + $commentdata .= $AsYetUnusedData; + + //$commentdata .= $this->fread($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']); + $commentdata .= $this->fread($this->OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1)); + } + + } + $ThisFileInfo_ogg_comments_raw[$i]['size'] = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4)); + + // replace avdataoffset with position just after the last vorbiscomment + $info['avdataoffset'] = $ThisFileInfo_ogg_comments_raw[$i]['dataoffset'] + $ThisFileInfo_ogg_comments_raw[$i]['size'] + 4; + + $commentdataoffset += 4; + while ((strlen($commentdata) - $commentdataoffset) < $ThisFileInfo_ogg_comments_raw[$i]['size']) { + if (($ThisFileInfo_ogg_comments_raw[$i]['size'] > $info['avdataend']) || ($ThisFileInfo_ogg_comments_raw[$i]['size'] < 0)) { + $info['warning'][] = 'Invalid Ogg comment size (comment #'.$i.', claims to be '.number_format($ThisFileInfo_ogg_comments_raw[$i]['size']).' bytes) - aborting reading comments'; + break 2; + } + + $VorbisCommentPage++; + + $oggpageinfo = $this->ParseOggPageHeader(); + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo; + + // First, save what we haven't read yet + $AsYetUnusedData = substr($commentdata, $commentdataoffset); + + // Then take that data off the end + $commentdata = substr($commentdata, 0, $commentdataoffset); + + // Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct + $commentdata .= str_repeat("\x00", 27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']); + $commentdataoffset += (27 + $info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']); + + // Finally, stick the unused data back on the end + $commentdata .= $AsYetUnusedData; + + //$commentdata .= $this->fread($info['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']); + if (!isset($info['ogg']['pageheader'][$VorbisCommentPage])) { + $info['warning'][] = 'undefined Vorbis Comment page "'.$VorbisCommentPage.'" at offset '.$this->ftell(); + break; + } + $readlength = self::OggPageSegmentLength($info['ogg']['pageheader'][$VorbisCommentPage], 1); + if ($readlength <= 0) { + $info['warning'][] = 'invalid length Vorbis Comment page "'.$VorbisCommentPage.'" at offset '.$this->ftell(); + break; + } + $commentdata .= $this->fread($readlength); + + //$filebaseoffset += $oggpageinfo['header_end_offset'] - $oggpageinfo['page_start_offset']; + } + $ThisFileInfo_ogg_comments_raw[$i]['offset'] = $commentdataoffset; + $commentstring = substr($commentdata, $commentdataoffset, $ThisFileInfo_ogg_comments_raw[$i]['size']); + $commentdataoffset += $ThisFileInfo_ogg_comments_raw[$i]['size']; + + if (!$commentstring) { + + // no comment? + $info['warning'][] = 'Blank Ogg comment ['.$i.']'; + + } elseif (strstr($commentstring, '=')) { + + $commentexploded = explode('=', $commentstring, 2); + $ThisFileInfo_ogg_comments_raw[$i]['key'] = strtoupper($commentexploded[0]); + $ThisFileInfo_ogg_comments_raw[$i]['value'] = (isset($commentexploded[1]) ? $commentexploded[1] : ''); + + if ($ThisFileInfo_ogg_comments_raw[$i]['key'] == 'METADATA_BLOCK_PICTURE') { + + // http://wiki.xiph.org/VorbisComment#METADATA_BLOCK_PICTURE + // The unencoded format is that of the FLAC picture block. The fields are stored in big endian order as in FLAC, picture data is stored according to the relevant standard. + // http://flac.sourceforge.net/format.html#metadata_block_picture + $flac = new getid3_flac($this->getid3); + $flac->setStringMode(base64_decode($ThisFileInfo_ogg_comments_raw[$i]['value'])); + $flac->parsePICTURE(); + $info['ogg']['comments']['picture'][] = $flac->getid3->info['flac']['PICTURE'][0]; + unset($flac); + + } elseif ($ThisFileInfo_ogg_comments_raw[$i]['key'] == 'COVERART') { + + $data = base64_decode($ThisFileInfo_ogg_comments_raw[$i]['value']); + $this->notice('Found deprecated COVERART tag, it should be replaced in honor of METADATA_BLOCK_PICTURE structure'); + /** @todo use 'coverartmime' where available */ + $imageinfo = getid3_lib::GetDataImageSize($data); + if ($imageinfo === false || !isset($imageinfo['mime'])) { + $this->warning('COVERART vorbiscomment tag contains invalid image'); + continue; + } + + $ogg = new self($this->getid3); + $ogg->setStringMode($data); + $info['ogg']['comments']['picture'][] = array( + 'image_mime' => $imageinfo['mime'], + 'data' => $ogg->saveAttachment('coverart', 0, strlen($data), $imageinfo['mime']), + ); + unset($ogg); + + } else { + + $info['ogg']['comments'][strtolower($ThisFileInfo_ogg_comments_raw[$i]['key'])][] = $ThisFileInfo_ogg_comments_raw[$i]['value']; + + } + + } else { + + $info['warning'][] = '[known problem with CDex >= v1.40, < v1.50b7] Invalid Ogg comment name/value pair ['.$i.']: '.$commentstring; + + } + unset($ThisFileInfo_ogg_comments_raw[$i]); + } + unset($ThisFileInfo_ogg_comments_raw); + + + // Replay Gain Adjustment + // http://privatewww.essex.ac.uk/~djmrob/replaygain/ + if (isset($info['ogg']['comments']) && is_array($info['ogg']['comments'])) { + foreach ($info['ogg']['comments'] as $index => $commentvalue) { + switch ($index) { + case 'rg_audiophile': + case 'replaygain_album_gain': + $info['replay_gain']['album']['adjustment'] = (double) $commentvalue[0]; + unset($info['ogg']['comments'][$index]); + break; + + case 'rg_radio': + case 'replaygain_track_gain': + $info['replay_gain']['track']['adjustment'] = (double) $commentvalue[0]; + unset($info['ogg']['comments'][$index]); + break; + + case 'replaygain_album_peak': + $info['replay_gain']['album']['peak'] = (double) $commentvalue[0]; + unset($info['ogg']['comments'][$index]); + break; + + case 'rg_peak': + case 'replaygain_track_peak': + $info['replay_gain']['track']['peak'] = (double) $commentvalue[0]; + unset($info['ogg']['comments'][$index]); + break; + + case 'replaygain_reference_loudness': + $info['replay_gain']['reference_volume'] = (double) $commentvalue[0]; + unset($info['ogg']['comments'][$index]); + break; + + default: + // do nothing + break; + } + } + } + + $this->fseek($OriginalOffset); + + return true; + } + + public static function SpeexBandModeLookup($mode) { + static $SpeexBandModeLookup = array(); + if (empty($SpeexBandModeLookup)) { + $SpeexBandModeLookup[0] = 'narrow'; + $SpeexBandModeLookup[1] = 'wide'; + $SpeexBandModeLookup[2] = 'ultra-wide'; + } + return (isset($SpeexBandModeLookup[$mode]) ? $SpeexBandModeLookup[$mode] : null); + } + + + public static function OggPageSegmentLength($OggInfoArray, $SegmentNumber=1) { + for ($i = 0; $i < $SegmentNumber; $i++) { + $segmentlength = 0; + foreach ($OggInfoArray['segment_table'] as $key => $value) { + $segmentlength += $value; + if ($value < 255) { + break; + } + } + } + return $segmentlength; + } + + + public static function get_quality_from_nominal_bitrate($nominal_bitrate) { + + // decrease precision + $nominal_bitrate = $nominal_bitrate / 1000; + + if ($nominal_bitrate < 128) { + // q-1 to q4 + $qval = ($nominal_bitrate - 64) / 16; + } elseif ($nominal_bitrate < 256) { + // q4 to q8 + $qval = $nominal_bitrate / 32; + } elseif ($nominal_bitrate < 320) { + // q8 to q9 + $qval = ($nominal_bitrate + 256) / 64; + } else { + // q9 to q10 + $qval = ($nominal_bitrate + 1300) / 180; + } + //return $qval; // 5.031324 + //return intval($qval); // 5 + return round($qval, 1); // 5 or 4.9 + } + + public static function TheoraColorSpace($colorspace_id) { + // http://www.theora.org/doc/Theora.pdf (table 6.3) + static $TheoraColorSpaceLookup = array(); + if (empty($TheoraColorSpaceLookup)) { + $TheoraColorSpaceLookup[0] = 'Undefined'; + $TheoraColorSpaceLookup[1] = 'Rec. 470M'; + $TheoraColorSpaceLookup[2] = 'Rec. 470BG'; + $TheoraColorSpaceLookup[3] = 'Reserved'; + } + return (isset($TheoraColorSpaceLookup[$colorspace_id]) ? $TheoraColorSpaceLookup[$colorspace_id] : null); + } + + public static function TheoraPixelFormat($pixelformat_id) { + // http://www.theora.org/doc/Theora.pdf (table 6.4) + static $TheoraPixelFormatLookup = array(); + if (empty($TheoraPixelFormatLookup)) { + $TheoraPixelFormatLookup[0] = '4:2:0'; + $TheoraPixelFormatLookup[1] = 'Reserved'; + $TheoraPixelFormatLookup[2] = '4:2:2'; + $TheoraPixelFormatLookup[3] = '4:4:4'; + } + return (isset($TheoraPixelFormatLookup[$pixelformat_id]) ? $TheoraPixelFormatLookup[$pixelformat_id] : null); + } + +} \ No newline at end of file diff --git a/wp-includes/ID3/module.tag.apetag.php b/wp-includes/ID3/module.tag.apetag.php new file mode 100644 index 0000000..5bd4b83 --- /dev/null +++ b/wp-includes/ID3/module.tag.apetag.php @@ -0,0 +1,371 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +// also https://github.com/JamesHeinrich/getID3 // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.tag.apetag.php // +// module for analyzing APE tags // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + +class getid3_apetag extends getid3_handler +{ + public $inline_attachments = true; // true: return full data for all attachments; false: return no data for all attachments; integer: return data for attachments <= than this; string: save as file to this directory + public $overrideendoffset = 0; + + public function Analyze() { + $info = &$this->getid3->info; + + if (!getid3_lib::intValueSupported($info['filesize'])) { + $info['warning'][] = 'Unable to check for APEtags because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB'; + return false; + } + + $id3v1tagsize = 128; + $apetagheadersize = 32; + $lyrics3tagsize = 10; + + if ($this->overrideendoffset == 0) { + + $this->fseek(0 - $id3v1tagsize - $apetagheadersize - $lyrics3tagsize, SEEK_END); + $APEfooterID3v1 = $this->fread($id3v1tagsize + $apetagheadersize + $lyrics3tagsize); + + //if (preg_match('/APETAGEX.{24}TAG.{125}$/i', $APEfooterID3v1)) { + if (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $id3v1tagsize - $apetagheadersize, 8) == 'APETAGEX') { + + // APE tag found before ID3v1 + $info['ape']['tag_offset_end'] = $info['filesize'] - $id3v1tagsize; + + //} elseif (preg_match('/APETAGEX.{24}$/i', $APEfooterID3v1)) { + } elseif (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $apetagheadersize, 8) == 'APETAGEX') { + + // APE tag found, no ID3v1 + $info['ape']['tag_offset_end'] = $info['filesize']; + + } + + } else { + + $this->fseek($this->overrideendoffset - $apetagheadersize); + if ($this->fread(8) == 'APETAGEX') { + $info['ape']['tag_offset_end'] = $this->overrideendoffset; + } + + } + if (!isset($info['ape']['tag_offset_end'])) { + + // APE tag not found + unset($info['ape']); + return false; + + } + + // shortcut + $thisfile_ape = &$info['ape']; + + $this->fseek($thisfile_ape['tag_offset_end'] - $apetagheadersize); + $APEfooterData = $this->fread(32); + if (!($thisfile_ape['footer'] = $this->parseAPEheaderFooter($APEfooterData))) { + $info['error'][] = 'Error parsing APE footer at offset '.$thisfile_ape['tag_offset_end']; + return false; + } + + if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) { + $this->fseek($thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'] - $apetagheadersize); + $thisfile_ape['tag_offset_start'] = $this->ftell(); + $APEtagData = $this->fread($thisfile_ape['footer']['raw']['tagsize'] + $apetagheadersize); + } else { + $thisfile_ape['tag_offset_start'] = $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize']; + $this->fseek($thisfile_ape['tag_offset_start']); + $APEtagData = $this->fread($thisfile_ape['footer']['raw']['tagsize']); + } + $info['avdataend'] = $thisfile_ape['tag_offset_start']; + + if (isset($info['id3v1']['tag_offset_start']) && ($info['id3v1']['tag_offset_start'] < $thisfile_ape['tag_offset_end'])) { + $info['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in APEtag data'; + unset($info['id3v1']); + foreach ($info['warning'] as $key => $value) { + if ($value == 'Some ID3v1 fields do not use NULL characters for padding') { + unset($info['warning'][$key]); + sort($info['warning']); + break; + } + } + } + + $offset = 0; + if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) { + if ($thisfile_ape['header'] = $this->parseAPEheaderFooter(substr($APEtagData, 0, $apetagheadersize))) { + $offset += $apetagheadersize; + } else { + $info['error'][] = 'Error parsing APE header at offset '.$thisfile_ape['tag_offset_start']; + return false; + } + } + + // shortcut + $info['replay_gain'] = array(); + $thisfile_replaygain = &$info['replay_gain']; + + for ($i = 0; $i < $thisfile_ape['footer']['raw']['tag_items']; $i++) { + $value_size = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4)); + $offset += 4; + $item_flags = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4)); + $offset += 4; + if (strstr(substr($APEtagData, $offset), "\x00") === false) { + $info['error'][] = 'Cannot find null-byte (0x00) seperator between ItemKey #'.$i.' and value. ItemKey starts '.$offset.' bytes into the APE tag, at file offset '.($thisfile_ape['tag_offset_start'] + $offset); + return false; + } + $ItemKeyLength = strpos($APEtagData, "\x00", $offset) - $offset; + $item_key = strtolower(substr($APEtagData, $offset, $ItemKeyLength)); + + // shortcut + $thisfile_ape['items'][$item_key] = array(); + $thisfile_ape_items_current = &$thisfile_ape['items'][$item_key]; + + $thisfile_ape_items_current['offset'] = $thisfile_ape['tag_offset_start'] + $offset; + + $offset += ($ItemKeyLength + 1); // skip 0x00 terminator + $thisfile_ape_items_current['data'] = substr($APEtagData, $offset, $value_size); + $offset += $value_size; + + $thisfile_ape_items_current['flags'] = $this->parseAPEtagFlags($item_flags); + switch ($thisfile_ape_items_current['flags']['item_contents_raw']) { + case 0: // UTF-8 + case 3: // Locator (URL, filename, etc), UTF-8 encoded + $thisfile_ape_items_current['data'] = explode("\x00", trim($thisfile_ape_items_current['data'])); + break; + + default: // binary data + break; + } + + switch (strtolower($item_key)) { + case 'replaygain_track_gain': + $thisfile_replaygain['track']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! + $thisfile_replaygain['track']['originator'] = 'unspecified'; + break; + + case 'replaygain_track_peak': + $thisfile_replaygain['track']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! + $thisfile_replaygain['track']['originator'] = 'unspecified'; + if ($thisfile_replaygain['track']['peak'] <= 0) { + $info['warning'][] = 'ReplayGain Track peak from APEtag appears invalid: '.$thisfile_replaygain['track']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")'; + } + break; + + case 'replaygain_album_gain': + $thisfile_replaygain['album']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! + $thisfile_replaygain['album']['originator'] = 'unspecified'; + break; + + case 'replaygain_album_peak': + $thisfile_replaygain['album']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero! + $thisfile_replaygain['album']['originator'] = 'unspecified'; + if ($thisfile_replaygain['album']['peak'] <= 0) { + $info['warning'][] = 'ReplayGain Album peak from APEtag appears invalid: '.$thisfile_replaygain['album']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")'; + } + break; + + case 'mp3gain_undo': + list($mp3gain_undo_left, $mp3gain_undo_right, $mp3gain_undo_wrap) = explode(',', $thisfile_ape_items_current['data'][0]); + $thisfile_replaygain['mp3gain']['undo_left'] = intval($mp3gain_undo_left); + $thisfile_replaygain['mp3gain']['undo_right'] = intval($mp3gain_undo_right); + $thisfile_replaygain['mp3gain']['undo_wrap'] = (($mp3gain_undo_wrap == 'Y') ? true : false); + break; + + case 'mp3gain_minmax': + list($mp3gain_globalgain_min, $mp3gain_globalgain_max) = explode(',', $thisfile_ape_items_current['data'][0]); + $thisfile_replaygain['mp3gain']['globalgain_track_min'] = intval($mp3gain_globalgain_min); + $thisfile_replaygain['mp3gain']['globalgain_track_max'] = intval($mp3gain_globalgain_max); + break; + + case 'mp3gain_album_minmax': + list($mp3gain_globalgain_album_min, $mp3gain_globalgain_album_max) = explode(',', $thisfile_ape_items_current['data'][0]); + $thisfile_replaygain['mp3gain']['globalgain_album_min'] = intval($mp3gain_globalgain_album_min); + $thisfile_replaygain['mp3gain']['globalgain_album_max'] = intval($mp3gain_globalgain_album_max); + break; + + case 'tracknumber': + if (is_array($thisfile_ape_items_current['data'])) { + foreach ($thisfile_ape_items_current['data'] as $comment) { + $thisfile_ape['comments']['track'][] = $comment; + } + } + break; + + case 'cover art (artist)': + case 'cover art (back)': + case 'cover art (band logo)': + case 'cover art (band)': + case 'cover art (colored fish)': + case 'cover art (composer)': + case 'cover art (conductor)': + case 'cover art (front)': + case 'cover art (icon)': + case 'cover art (illustration)': + case 'cover art (lead)': + case 'cover art (leaflet)': + case 'cover art (lyricist)': + case 'cover art (media)': + case 'cover art (movie scene)': + case 'cover art (other icon)': + case 'cover art (other)': + case 'cover art (performance)': + case 'cover art (publisher logo)': + case 'cover art (recording)': + case 'cover art (studio)': + // list of possible cover arts from http://taglib-sharp.sourcearchive.com/documentation/2.0.3.0-2/Ape_2Tag_8cs-source.html + list($thisfile_ape_items_current['filename'], $thisfile_ape_items_current['data']) = explode("\x00", $thisfile_ape_items_current['data'], 2); + $thisfile_ape_items_current['data_offset'] = $thisfile_ape_items_current['offset'] + strlen($thisfile_ape_items_current['filename']."\x00"); + $thisfile_ape_items_current['data_length'] = strlen($thisfile_ape_items_current['data']); + + $thisfile_ape_items_current['image_mime'] = ''; + $imageinfo = array(); + $imagechunkcheck = getid3_lib::GetDataImageSize($thisfile_ape_items_current['data'], $imageinfo); + $thisfile_ape_items_current['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]); + + do { + if ($this->inline_attachments === false) { + // skip entirely + unset($thisfile_ape_items_current['data']); + break; + } + if ($this->inline_attachments === true) { + // great + } elseif (is_int($this->inline_attachments)) { + if ($this->inline_attachments < $thisfile_ape_items_current['data_length']) { + // too big, skip + $info['warning'][] = 'attachment at '.$thisfile_ape_items_current['offset'].' is too large to process inline ('.number_format($thisfile_ape_items_current['data_length']).' bytes)'; + unset($thisfile_ape_items_current['data']); + break; + } + } elseif (is_string($this->inline_attachments)) { + $this->inline_attachments = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->inline_attachments), DIRECTORY_SEPARATOR); + if (!is_dir($this->inline_attachments) || !is_writable($this->inline_attachments)) { + // cannot write, skip + $info['warning'][] = 'attachment at '.$thisfile_ape_items_current['offset'].' cannot be saved to "'.$this->inline_attachments.'" (not writable)'; + unset($thisfile_ape_items_current['data']); + break; + } + } + // if we get this far, must be OK + if (is_string($this->inline_attachments)) { + $destination_filename = $this->inline_attachments.DIRECTORY_SEPARATOR.md5($info['filenamepath']).'_'.$thisfile_ape_items_current['data_offset']; + if (!file_exists($destination_filename) || is_writable($destination_filename)) { + file_put_contents($destination_filename, $thisfile_ape_items_current['data']); + } else { + $info['warning'][] = 'attachment at '.$thisfile_ape_items_current['offset'].' cannot be saved to "'.$destination_filename.'" (not writable)'; + } + $thisfile_ape_items_current['data_filename'] = $destination_filename; + unset($thisfile_ape_items_current['data']); + } else { + if (!isset($info['ape']['comments']['picture'])) { + $info['ape']['comments']['picture'] = array(); + } + $info['ape']['comments']['picture'][] = array('data'=>$thisfile_ape_items_current['data'], 'image_mime'=>$thisfile_ape_items_current['image_mime']); + } + } while (false); + break; + + default: + if (is_array($thisfile_ape_items_current['data'])) { + foreach ($thisfile_ape_items_current['data'] as $comment) { + $thisfile_ape['comments'][strtolower($item_key)][] = $comment; + } + } + break; + } + + } + if (empty($thisfile_replaygain)) { + unset($info['replay_gain']); + } + return true; + } + + public function parseAPEheaderFooter($APEheaderFooterData) { + // http://www.uni-jena.de/~pfk/mpp/sv8/apeheader.html + + // shortcut + $headerfooterinfo['raw'] = array(); + $headerfooterinfo_raw = &$headerfooterinfo['raw']; + + $headerfooterinfo_raw['footer_tag'] = substr($APEheaderFooterData, 0, 8); + if ($headerfooterinfo_raw['footer_tag'] != 'APETAGEX') { + return false; + } + $headerfooterinfo_raw['version'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 8, 4)); + $headerfooterinfo_raw['tagsize'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 12, 4)); + $headerfooterinfo_raw['tag_items'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 16, 4)); + $headerfooterinfo_raw['global_flags'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 20, 4)); + $headerfooterinfo_raw['reserved'] = substr($APEheaderFooterData, 24, 8); + + $headerfooterinfo['tag_version'] = $headerfooterinfo_raw['version'] / 1000; + if ($headerfooterinfo['tag_version'] >= 2) { + $headerfooterinfo['flags'] = $this->parseAPEtagFlags($headerfooterinfo_raw['global_flags']); + } + return $headerfooterinfo; + } + + public function parseAPEtagFlags($rawflagint) { + // "Note: APE Tags 1.0 do not use any of the APE Tag flags. + // All are set to zero on creation and ignored on reading." + // http://www.uni-jena.de/~pfk/mpp/sv8/apetagflags.html + $flags['header'] = (bool) ($rawflagint & 0x80000000); + $flags['footer'] = (bool) ($rawflagint & 0x40000000); + $flags['this_is_header'] = (bool) ($rawflagint & 0x20000000); + $flags['item_contents_raw'] = ($rawflagint & 0x00000006) >> 1; + $flags['read_only'] = (bool) ($rawflagint & 0x00000001); + + $flags['item_contents'] = $this->APEcontentTypeFlagLookup($flags['item_contents_raw']); + + return $flags; + } + + public function APEcontentTypeFlagLookup($contenttypeid) { + static $APEcontentTypeFlagLookup = array( + 0 => 'utf-8', + 1 => 'binary', + 2 => 'external', + 3 => 'reserved' + ); + return (isset($APEcontentTypeFlagLookup[$contenttypeid]) ? $APEcontentTypeFlagLookup[$contenttypeid] : 'invalid'); + } + + public function APEtagItemIsUTF8Lookup($itemkey) { + static $APEtagItemIsUTF8Lookup = array( + 'title', + 'subtitle', + 'artist', + 'album', + 'debut album', + 'publisher', + 'conductor', + 'track', + 'composer', + 'comment', + 'copyright', + 'publicationright', + 'file', + 'year', + 'record date', + 'record location', + 'genre', + 'media', + 'related', + 'isrc', + 'abstract', + 'language', + 'bibliography' + ); + return in_array(strtolower($itemkey), $APEtagItemIsUTF8Lookup); + } + +} \ No newline at end of file diff --git a/wp-includes/ID3/module.tag.id3v1.php b/wp-includes/ID3/module.tag.id3v1.php new file mode 100644 index 0000000..def281a --- /dev/null +++ b/wp-includes/ID3/module.tag.id3v1.php @@ -0,0 +1,360 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +// also https://github.com/JamesHeinrich/getID3 // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +// // +// module.tag.id3v1.php // +// module for analyzing ID3v1 tags // +// dependencies: NONE // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_id3v1 extends getid3_handler +{ + + public function Analyze() { + $info = &$this->getid3->info; + + if (!getid3_lib::intValueSupported($info['filesize'])) { + $info['warning'][] = 'Unable to check for ID3v1 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB'; + return false; + } + + $this->fseek(-256, SEEK_END); + $preid3v1 = $this->fread(128); + $id3v1tag = $this->fread(128); + + if (substr($id3v1tag, 0, 3) == 'TAG') { + + $info['avdataend'] = $info['filesize'] - 128; + + $ParsedID3v1['title'] = $this->cutfield(substr($id3v1tag, 3, 30)); + $ParsedID3v1['artist'] = $this->cutfield(substr($id3v1tag, 33, 30)); + $ParsedID3v1['album'] = $this->cutfield(substr($id3v1tag, 63, 30)); + $ParsedID3v1['year'] = $this->cutfield(substr($id3v1tag, 93, 4)); + $ParsedID3v1['comment'] = substr($id3v1tag, 97, 30); // can't remove nulls yet, track detection depends on them + $ParsedID3v1['genreid'] = ord(substr($id3v1tag, 127, 1)); + + // If second-last byte of comment field is null and last byte of comment field is non-null + // then this is ID3v1.1 and the comment field is 28 bytes long and the 30th byte is the track number + if (($id3v1tag{125} === "\x00") && ($id3v1tag{126} !== "\x00")) { + $ParsedID3v1['track'] = ord(substr($ParsedID3v1['comment'], 29, 1)); + $ParsedID3v1['comment'] = substr($ParsedID3v1['comment'], 0, 28); + } + $ParsedID3v1['comment'] = $this->cutfield($ParsedID3v1['comment']); + + $ParsedID3v1['genre'] = $this->LookupGenreName($ParsedID3v1['genreid']); + if (!empty($ParsedID3v1['genre'])) { + unset($ParsedID3v1['genreid']); + } + if (isset($ParsedID3v1['genre']) && (empty($ParsedID3v1['genre']) || ($ParsedID3v1['genre'] == 'Unknown'))) { + unset($ParsedID3v1['genre']); + } + + foreach ($ParsedID3v1 as $key => $value) { + $ParsedID3v1['comments'][$key][0] = $value; + } + + // ID3v1 data is supposed to be padded with NULL characters, but some taggers pad with spaces + $GoodFormatID3v1tag = $this->GenerateID3v1Tag( + $ParsedID3v1['title'], + $ParsedID3v1['artist'], + $ParsedID3v1['album'], + $ParsedID3v1['year'], + (isset($ParsedID3v1['genre']) ? $this->LookupGenreID($ParsedID3v1['genre']) : false), + $ParsedID3v1['comment'], + (!empty($ParsedID3v1['track']) ? $ParsedID3v1['track'] : '')); + $ParsedID3v1['padding_valid'] = true; + if ($id3v1tag !== $GoodFormatID3v1tag) { + $ParsedID3v1['padding_valid'] = false; + $info['warning'][] = 'Some ID3v1 fields do not use NULL characters for padding'; + } + + $ParsedID3v1['tag_offset_end'] = $info['filesize']; + $ParsedID3v1['tag_offset_start'] = $ParsedID3v1['tag_offset_end'] - 128; + + $info['id3v1'] = $ParsedID3v1; + } + + if (substr($preid3v1, 0, 3) == 'TAG') { + // The way iTunes handles tags is, well, brain-damaged. + // It completely ignores v1 if ID3v2 is present. + // This goes as far as adding a new v1 tag *even if there already is one* + + // A suspected double-ID3v1 tag has been detected, but it could be that + // the "TAG" identifier is a legitimate part of an APE or Lyrics3 tag + if (substr($preid3v1, 96, 8) == 'APETAGEX') { + // an APE tag footer was found before the last ID3v1, assume false "TAG" synch + } elseif (substr($preid3v1, 119, 6) == 'LYRICS') { + // a Lyrics3 tag footer was found before the last ID3v1, assume false "TAG" synch + } else { + // APE and Lyrics3 footers not found - assume double ID3v1 + $info['warning'][] = 'Duplicate ID3v1 tag detected - this has been known to happen with iTunes'; + $info['avdataend'] -= 128; + } + } + + return true; + } + + public static function cutfield($str) { + return trim(substr($str, 0, strcspn($str, "\x00"))); + } + + public static function ArrayOfGenres($allowSCMPXextended=false) { + static $GenreLookup = array( + 0 => 'Blues', + 1 => 'Classic Rock', + 2 => 'Country', + 3 => 'Dance', + 4 => 'Disco', + 5 => 'Funk', + 6 => 'Grunge', + 7 => 'Hip-Hop', + 8 => 'Jazz', + 9 => 'Metal', + 10 => 'New Age', + 11 => 'Oldies', + 12 => 'Other', + 13 => 'Pop', + 14 => 'R&B', + 15 => 'Rap', + 16 => 'Reggae', + 17 => 'Rock', + 18 => 'Techno', + 19 => 'Industrial', + 20 => 'Alternative', + 21 => 'Ska', + 22 => 'Death Metal', + 23 => 'Pranks', + 24 => 'Soundtrack', + 25 => 'Euro-Techno', + 26 => 'Ambient', + 27 => 'Trip-Hop', + 28 => 'Vocal', + 29 => 'Jazz+Funk', + 30 => 'Fusion', + 31 => 'Trance', + 32 => 'Classical', + 33 => 'Instrumental', + 34 => 'Acid', + 35 => 'House', + 36 => 'Game', + 37 => 'Sound Clip', + 38 => 'Gospel', + 39 => 'Noise', + 40 => 'Alt. Rock', + 41 => 'Bass', + 42 => 'Soul', + 43 => 'Punk', + 44 => 'Space', + 45 => 'Meditative', + 46 => 'Instrumental Pop', + 47 => 'Instrumental Rock', + 48 => 'Ethnic', + 49 => 'Gothic', + 50 => 'Darkwave', + 51 => 'Techno-Industrial', + 52 => 'Electronic', + 53 => 'Pop-Folk', + 54 => 'Eurodance', + 55 => 'Dream', + 56 => 'Southern Rock', + 57 => 'Comedy', + 58 => 'Cult', + 59 => 'Gangsta Rap', + 60 => 'Top 40', + 61 => 'Christian Rap', + 62 => 'Pop/Funk', + 63 => 'Jungle', + 64 => 'Native American', + 65 => 'Cabaret', + 66 => 'New Wave', + 67 => 'Psychedelic', + 68 => 'Rave', + 69 => 'Showtunes', + 70 => 'Trailer', + 71 => 'Lo-Fi', + 72 => 'Tribal', + 73 => 'Acid Punk', + 74 => 'Acid Jazz', + 75 => 'Polka', + 76 => 'Retro', + 77 => 'Musical', + 78 => 'Rock & Roll', + 79 => 'Hard Rock', + 80 => 'Folk', + 81 => 'Folk/Rock', + 82 => 'National Folk', + 83 => 'Swing', + 84 => 'Fast-Fusion', + 85 => 'Bebob', + 86 => 'Latin', + 87 => 'Revival', + 88 => 'Celtic', + 89 => 'Bluegrass', + 90 => 'Avantgarde', + 91 => 'Gothic Rock', + 92 => 'Progressive Rock', + 93 => 'Psychedelic Rock', + 94 => 'Symphonic Rock', + 95 => 'Slow Rock', + 96 => 'Big Band', + 97 => 'Chorus', + 98 => 'Easy Listening', + 99 => 'Acoustic', + 100 => 'Humour', + 101 => 'Speech', + 102 => 'Chanson', + 103 => 'Opera', + 104 => 'Chamber Music', + 105 => 'Sonata', + 106 => 'Symphony', + 107 => 'Booty Bass', + 108 => 'Primus', + 109 => 'Porn Groove', + 110 => 'Satire', + 111 => 'Slow Jam', + 112 => 'Club', + 113 => 'Tango', + 114 => 'Samba', + 115 => 'Folklore', + 116 => 'Ballad', + 117 => 'Power Ballad', + 118 => 'Rhythmic Soul', + 119 => 'Freestyle', + 120 => 'Duet', + 121 => 'Punk Rock', + 122 => 'Drum Solo', + 123 => 'A Cappella', + 124 => 'Euro-House', + 125 => 'Dance Hall', + 126 => 'Goa', + 127 => 'Drum & Bass', + 128 => 'Club-House', + 129 => 'Hardcore', + 130 => 'Terror', + 131 => 'Indie', + 132 => 'BritPop', + 133 => 'Negerpunk', + 134 => 'Polsk Punk', + 135 => 'Beat', + 136 => 'Christian Gangsta Rap', + 137 => 'Heavy Metal', + 138 => 'Black Metal', + 139 => 'Crossover', + 140 => 'Contemporary Christian', + 141 => 'Christian Rock', + 142 => 'Merengue', + 143 => 'Salsa', + 144 => 'Thrash Metal', + 145 => 'Anime', + 146 => 'JPop', + 147 => 'Synthpop', + + 255 => 'Unknown', + + 'CR' => 'Cover', + 'RX' => 'Remix' + ); + + static $GenreLookupSCMPX = array(); + if ($allowSCMPXextended && empty($GenreLookupSCMPX)) { + $GenreLookupSCMPX = $GenreLookup; + // http://www.geocities.co.jp/SiliconValley-Oakland/3664/alittle.html#GenreExtended + // Extended ID3v1 genres invented by SCMPX + // Note that 255 "Japanese Anime" conflicts with standard "Unknown" + $GenreLookupSCMPX[240] = 'Sacred'; + $GenreLookupSCMPX[241] = 'Northern Europe'; + $GenreLookupSCMPX[242] = 'Irish & Scottish'; + $GenreLookupSCMPX[243] = 'Scotland'; + $GenreLookupSCMPX[244] = 'Ethnic Europe'; + $GenreLookupSCMPX[245] = 'Enka'; + $GenreLookupSCMPX[246] = 'Children\'s Song'; + $GenreLookupSCMPX[247] = 'Japanese Sky'; + $GenreLookupSCMPX[248] = 'Japanese Heavy Rock'; + $GenreLookupSCMPX[249] = 'Japanese Doom Rock'; + $GenreLookupSCMPX[250] = 'Japanese J-POP'; + $GenreLookupSCMPX[251] = 'Japanese Seiyu'; + $GenreLookupSCMPX[252] = 'Japanese Ambient Techno'; + $GenreLookupSCMPX[253] = 'Japanese Moemoe'; + $GenreLookupSCMPX[254] = 'Japanese Tokusatsu'; + //$GenreLookupSCMPX[255] = 'Japanese Anime'; + } + + return ($allowSCMPXextended ? $GenreLookupSCMPX : $GenreLookup); + } + + public static function LookupGenreName($genreid, $allowSCMPXextended=true) { + switch ($genreid) { + case 'RX': + case 'CR': + break; + default: + if (!is_numeric($genreid)) { + return false; + } + $genreid = intval($genreid); // to handle 3 or '3' or '03' + break; + } + $GenreLookup = self::ArrayOfGenres($allowSCMPXextended); + return (isset($GenreLookup[$genreid]) ? $GenreLookup[$genreid] : false); + } + + public static function LookupGenreID($genre, $allowSCMPXextended=false) { + $GenreLookup = self::ArrayOfGenres($allowSCMPXextended); + $LowerCaseNoSpaceSearchTerm = strtolower(str_replace(' ', '', $genre)); + foreach ($GenreLookup as $key => $value) { + if (strtolower(str_replace(' ', '', $value)) == $LowerCaseNoSpaceSearchTerm) { + return $key; + } + } + return false; + } + + public static function StandardiseID3v1GenreName($OriginalGenre) { + if (($GenreID = self::LookupGenreID($OriginalGenre)) !== false) { + return self::LookupGenreName($GenreID); + } + return $OriginalGenre; + } + + public static function GenerateID3v1Tag($title, $artist, $album, $year, $genreid, $comment, $track='') { + $ID3v1Tag = 'TAG'; + $ID3v1Tag .= str_pad(trim(substr($title, 0, 30)), 30, "\x00", STR_PAD_RIGHT); + $ID3v1Tag .= str_pad(trim(substr($artist, 0, 30)), 30, "\x00", STR_PAD_RIGHT); + $ID3v1Tag .= str_pad(trim(substr($album, 0, 30)), 30, "\x00", STR_PAD_RIGHT); + $ID3v1Tag .= str_pad(trim(substr($year, 0, 4)), 4, "\x00", STR_PAD_LEFT); + if (!empty($track) && ($track > 0) && ($track <= 255)) { + $ID3v1Tag .= str_pad(trim(substr($comment, 0, 28)), 28, "\x00", STR_PAD_RIGHT); + $ID3v1Tag .= "\x00"; + if (gettype($track) == 'string') { + $track = (int) $track; + } + $ID3v1Tag .= chr($track); + } else { + $ID3v1Tag .= str_pad(trim(substr($comment, 0, 30)), 30, "\x00", STR_PAD_RIGHT); + } + if (($genreid < 0) || ($genreid > 147)) { + $genreid = 255; // 'unknown' genre + } + switch (gettype($genreid)) { + case 'string': + case 'integer': + $ID3v1Tag .= chr(intval($genreid)); + break; + default: + $ID3v1Tag .= chr(255); // 'unknown' genre + break; + } + + return $ID3v1Tag; + } + +} \ No newline at end of file diff --git a/wp-includes/ID3/module.tag.id3v2.php b/wp-includes/ID3/module.tag.id3v2.php new file mode 100644 index 0000000..d1c4fce --- /dev/null +++ b/wp-includes/ID3/module.tag.id3v2.php @@ -0,0 +1,3424 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +// also https://github.com/JamesHeinrich/getID3 // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +/// // +// module.tag.id3v2.php // +// module for analyzing ID3v2 tags // +// dependencies: module.tag.id3v1.php // +// /// +///////////////////////////////////////////////////////////////// + +getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE__, true); + +class getid3_id3v2 extends getid3_handler +{ + public $StartingOffset = 0; + + public function Analyze() { + $info = &$this->getid3->info; + + // Overall tag structure: + // +-----------------------------+ + // | Header (10 bytes) | + // +-----------------------------+ + // | Extended Header | + // | (variable length, OPTIONAL) | + // +-----------------------------+ + // | Frames (variable length) | + // +-----------------------------+ + // | Padding | + // | (variable length, OPTIONAL) | + // +-----------------------------+ + // | Footer (10 bytes, OPTIONAL) | + // +-----------------------------+ + + // Header + // ID3v2/file identifier "ID3" + // ID3v2 version $04 00 + // ID3v2 flags (%ab000000 in v2.2, %abc00000 in v2.3, %abcd0000 in v2.4.x) + // ID3v2 size 4 * %0xxxxxxx + + + // shortcuts + $info['id3v2']['header'] = true; + $thisfile_id3v2 = &$info['id3v2']; + $thisfile_id3v2['flags'] = array(); + $thisfile_id3v2_flags = &$thisfile_id3v2['flags']; + + + $this->fseek($this->StartingOffset); + $header = $this->fread(10); + if (substr($header, 0, 3) == 'ID3' && strlen($header) == 10) { + + $thisfile_id3v2['majorversion'] = ord($header{3}); + $thisfile_id3v2['minorversion'] = ord($header{4}); + + // shortcut + $id3v2_majorversion = &$thisfile_id3v2['majorversion']; + + } else { + + unset($info['id3v2']); + return false; + + } + + if ($id3v2_majorversion > 4) { // this script probably won't correctly parse ID3v2.5.x and above (if it ever exists) + + $info['error'][] = 'this script only parses up to ID3v2.4.x - this tag is ID3v2.'.$id3v2_majorversion.'.'.$thisfile_id3v2['minorversion']; + return false; + + } + + $id3_flags = ord($header{5}); + switch ($id3v2_majorversion) { + case 2: + // %ab000000 in v2.2 + $thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation + $thisfile_id3v2_flags['compression'] = (bool) ($id3_flags & 0x40); // b - Compression + break; + + case 3: + // %abc00000 in v2.3 + $thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation + $thisfile_id3v2_flags['exthead'] = (bool) ($id3_flags & 0x40); // b - Extended header + $thisfile_id3v2_flags['experim'] = (bool) ($id3_flags & 0x20); // c - Experimental indicator + break; + + case 4: + // %abcd0000 in v2.4 + $thisfile_id3v2_flags['unsynch'] = (bool) ($id3_flags & 0x80); // a - Unsynchronisation + $thisfile_id3v2_flags['exthead'] = (bool) ($id3_flags & 0x40); // b - Extended header + $thisfile_id3v2_flags['experim'] = (bool) ($id3_flags & 0x20); // c - Experimental indicator + $thisfile_id3v2_flags['isfooter'] = (bool) ($id3_flags & 0x10); // d - Footer present + break; + } + + $thisfile_id3v2['headerlength'] = getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10; // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length + + $thisfile_id3v2['tag_offset_start'] = $this->StartingOffset; + $thisfile_id3v2['tag_offset_end'] = $thisfile_id3v2['tag_offset_start'] + $thisfile_id3v2['headerlength']; + + + + // create 'encoding' key - used by getid3::HandleAllTags() + // in ID3v2 every field can have it's own encoding type + // so force everything to UTF-8 so it can be handled consistantly + $thisfile_id3v2['encoding'] = 'UTF-8'; + + + // Frames + + // All ID3v2 frames consists of one frame header followed by one or more + // fields containing the actual information. The header is always 10 + // bytes and laid out as follows: + // + // Frame ID $xx xx xx xx (four characters) + // Size 4 * %0xxxxxxx + // Flags $xx xx + + $sizeofframes = $thisfile_id3v2['headerlength'] - 10; // not including 10-byte initial header + if (!empty($thisfile_id3v2['exthead']['length'])) { + $sizeofframes -= ($thisfile_id3v2['exthead']['length'] + 4); + } + if (!empty($thisfile_id3v2_flags['isfooter'])) { + $sizeofframes -= 10; // footer takes last 10 bytes of ID3v2 header, after frame data, before audio + } + if ($sizeofframes > 0) { + + $framedata = $this->fread($sizeofframes); // read all frames from file into $framedata variable + + // if entire frame data is unsynched, de-unsynch it now (ID3v2.3.x) + if (!empty($thisfile_id3v2_flags['unsynch']) && ($id3v2_majorversion <= 3)) { + $framedata = $this->DeUnsynchronise($framedata); + } + // [in ID3v2.4.0] Unsynchronisation [S:6.1] is done on frame level, instead + // of on tag level, making it easier to skip frames, increasing the streamability + // of the tag. The unsynchronisation flag in the header [S:3.1] indicates that + // there exists an unsynchronised frame, while the new unsynchronisation flag in + // the frame header [S:4.1.2] indicates unsynchronisation. + + + //$framedataoffset = 10 + ($thisfile_id3v2['exthead']['length'] ? $thisfile_id3v2['exthead']['length'] + 4 : 0); // how many bytes into the stream - start from after the 10-byte header (and extended header length+4, if present) + $framedataoffset = 10; // how many bytes into the stream - start from after the 10-byte header + + + // Extended Header + if (!empty($thisfile_id3v2_flags['exthead'])) { + $extended_header_offset = 0; + + if ($id3v2_majorversion == 3) { + + // v2.3 definition: + //Extended header size $xx xx xx xx // 32-bit integer + //Extended Flags $xx xx + // %x0000000 %00000000 // v2.3 + // x - CRC data present + //Size of padding $xx xx xx xx + + $thisfile_id3v2['exthead']['length'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4), 0); + $extended_header_offset += 4; + + $thisfile_id3v2['exthead']['flag_bytes'] = 2; + $thisfile_id3v2['exthead']['flag_raw'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, $thisfile_id3v2['exthead']['flag_bytes'])); + $extended_header_offset += $thisfile_id3v2['exthead']['flag_bytes']; + + $thisfile_id3v2['exthead']['flags']['crc'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x8000); + + $thisfile_id3v2['exthead']['padding_size'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4)); + $extended_header_offset += 4; + + if ($thisfile_id3v2['exthead']['flags']['crc']) { + $thisfile_id3v2['exthead']['flag_data']['crc'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4)); + $extended_header_offset += 4; + } + $extended_header_offset += $thisfile_id3v2['exthead']['padding_size']; + + } elseif ($id3v2_majorversion == 4) { + + // v2.4 definition: + //Extended header size 4 * %0xxxxxxx // 28-bit synchsafe integer + //Number of flag bytes $01 + //Extended Flags $xx + // %0bcd0000 // v2.4 + // b - Tag is an update + // Flag data length $00 + // c - CRC data present + // Flag data length $05 + // Total frame CRC 5 * %0xxxxxxx + // d - Tag restrictions + // Flag data length $01 + + $thisfile_id3v2['exthead']['length'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4), true); + $extended_header_offset += 4; + + $thisfile_id3v2['exthead']['flag_bytes'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should always be 1 + $extended_header_offset += 1; + + $thisfile_id3v2['exthead']['flag_raw'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, $thisfile_id3v2['exthead']['flag_bytes'])); + $extended_header_offset += $thisfile_id3v2['exthead']['flag_bytes']; + + $thisfile_id3v2['exthead']['flags']['update'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x40); + $thisfile_id3v2['exthead']['flags']['crc'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x20); + $thisfile_id3v2['exthead']['flags']['restrictions'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x10); + + if ($thisfile_id3v2['exthead']['flags']['update']) { + $ext_header_chunk_length = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should be 0 + $extended_header_offset += 1; + } + + if ($thisfile_id3v2['exthead']['flags']['crc']) { + $ext_header_chunk_length = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should be 5 + $extended_header_offset += 1; + $thisfile_id3v2['exthead']['flag_data']['crc'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, $ext_header_chunk_length), true, false); + $extended_header_offset += $ext_header_chunk_length; + } + + if ($thisfile_id3v2['exthead']['flags']['restrictions']) { + $ext_header_chunk_length = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); // should be 1 + $extended_header_offset += 1; + + // %ppqrrstt + $restrictions_raw = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1)); + $extended_header_offset += 1; + $thisfile_id3v2['exthead']['flags']['restrictions']['tagsize'] = ($restrictions_raw & 0xC0) >> 6; // p - Tag size restrictions + $thisfile_id3v2['exthead']['flags']['restrictions']['textenc'] = ($restrictions_raw & 0x20) >> 5; // q - Text encoding restrictions + $thisfile_id3v2['exthead']['flags']['restrictions']['textsize'] = ($restrictions_raw & 0x18) >> 3; // r - Text fields size restrictions + $thisfile_id3v2['exthead']['flags']['restrictions']['imgenc'] = ($restrictions_raw & 0x04) >> 2; // s - Image encoding restrictions + $thisfile_id3v2['exthead']['flags']['restrictions']['imgsize'] = ($restrictions_raw & 0x03) >> 0; // t - Image size restrictions + + $thisfile_id3v2['exthead']['flags']['restrictions_text']['tagsize'] = $this->LookupExtendedHeaderRestrictionsTagSizeLimits($thisfile_id3v2['exthead']['flags']['restrictions']['tagsize']); + $thisfile_id3v2['exthead']['flags']['restrictions_text']['textenc'] = $this->LookupExtendedHeaderRestrictionsTextEncodings($thisfile_id3v2['exthead']['flags']['restrictions']['textenc']); + $thisfile_id3v2['exthead']['flags']['restrictions_text']['textsize'] = $this->LookupExtendedHeaderRestrictionsTextFieldSize($thisfile_id3v2['exthead']['flags']['restrictions']['textsize']); + $thisfile_id3v2['exthead']['flags']['restrictions_text']['imgenc'] = $this->LookupExtendedHeaderRestrictionsImageEncoding($thisfile_id3v2['exthead']['flags']['restrictions']['imgenc']); + $thisfile_id3v2['exthead']['flags']['restrictions_text']['imgsize'] = $this->LookupExtendedHeaderRestrictionsImageSizeSize($thisfile_id3v2['exthead']['flags']['restrictions']['imgsize']); + } + + if ($thisfile_id3v2['exthead']['length'] != $extended_header_offset) { + $info['warning'][] = 'ID3v2.4 extended header length mismatch (expecting '.intval($thisfile_id3v2['exthead']['length']).', found '.intval($extended_header_offset).')'; + } + } + + $framedataoffset += $extended_header_offset; + $framedata = substr($framedata, $extended_header_offset); + } // end extended header + + + while (isset($framedata) && (strlen($framedata) > 0)) { // cycle through until no more frame data is left to parse + if (strlen($framedata) <= $this->ID3v2HeaderLength($id3v2_majorversion)) { + // insufficient room left in ID3v2 header for actual data - must be padding + $thisfile_id3v2['padding']['start'] = $framedataoffset; + $thisfile_id3v2['padding']['length'] = strlen($framedata); + $thisfile_id3v2['padding']['valid'] = true; + for ($i = 0; $i < $thisfile_id3v2['padding']['length']; $i++) { + if ($framedata{$i} != "\x00") { + $thisfile_id3v2['padding']['valid'] = false; + $thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i; + $info['warning'][] = 'Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)'; + break; + } + } + break; // skip rest of ID3v2 header + } + if ($id3v2_majorversion == 2) { + // Frame ID $xx xx xx (three characters) + // Size $xx xx xx (24-bit integer) + // Flags $xx xx + + $frame_header = substr($framedata, 0, 6); // take next 6 bytes for header + $framedata = substr($framedata, 6); // and leave the rest in $framedata + $frame_name = substr($frame_header, 0, 3); + $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 3, 3), 0); + $frame_flags = 0; // not used for anything in ID3v2.2, just set to avoid E_NOTICEs + + } elseif ($id3v2_majorversion > 2) { + + // Frame ID $xx xx xx xx (four characters) + // Size $xx xx xx xx (32-bit integer in v2.3, 28-bit synchsafe in v2.4+) + // Flags $xx xx + + $frame_header = substr($framedata, 0, 10); // take next 10 bytes for header + $framedata = substr($framedata, 10); // and leave the rest in $framedata + + $frame_name = substr($frame_header, 0, 4); + if ($id3v2_majorversion == 3) { + $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer + } else { // ID3v2.4+ + $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 1); // 32-bit synchsafe integer (28-bit value) + } + + if ($frame_size < (strlen($framedata) + 4)) { + $nextFrameID = substr($framedata, $frame_size, 4); + if ($this->IsValidID3v2FrameName($nextFrameID, $id3v2_majorversion)) { + // next frame is OK + } elseif (($frame_name == "\x00".'MP3') || ($frame_name == "\x00\x00".'MP') || ($frame_name == ' MP3') || ($frame_name == 'MP3e')) { + // MP3ext known broken frames - "ok" for the purposes of this test + } elseif (($id3v2_majorversion == 4) && ($this->IsValidID3v2FrameName(substr($framedata, getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0), 4), 3))) { + $info['warning'][] = 'ID3v2 tag written as ID3v2.4, but with non-synchsafe integers (ID3v2.3 style). Older versions of (Helium2; iTunes) are known culprits of this. Tag has been parsed as ID3v2.3'; + $id3v2_majorversion = 3; + $frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer + } + } + + + $frame_flags = getid3_lib::BigEndian2Int(substr($frame_header, 8, 2)); + } + + if ((($id3v2_majorversion == 2) && ($frame_name == "\x00\x00\x00")) || ($frame_name == "\x00\x00\x00\x00")) { + // padding encountered + + $thisfile_id3v2['padding']['start'] = $framedataoffset; + $thisfile_id3v2['padding']['length'] = strlen($frame_header) + strlen($framedata); + $thisfile_id3v2['padding']['valid'] = true; + + $len = strlen($framedata); + for ($i = 0; $i < $len; $i++) { + if ($framedata{$i} != "\x00") { + $thisfile_id3v2['padding']['valid'] = false; + $thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i; + $info['warning'][] = 'Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)'; + break; + } + } + break; // skip rest of ID3v2 header + } + + if ($frame_name == 'COM ') { + $info['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))). [Note: this particular error has been known to happen with tags edited by iTunes (versions "X v2.0.3", "v3.0.1" are known-guilty, probably others too)]'; + $frame_name = 'COMM'; + } + if (($frame_size <= strlen($framedata)) && ($this->IsValidID3v2FrameName($frame_name, $id3v2_majorversion))) { + + unset($parsedFrame); + $parsedFrame['frame_name'] = $frame_name; + $parsedFrame['frame_flags_raw'] = $frame_flags; + $parsedFrame['data'] = substr($framedata, 0, $frame_size); + $parsedFrame['datalength'] = getid3_lib::CastAsInt($frame_size); + $parsedFrame['dataoffset'] = $framedataoffset; + + $this->ParseID3v2Frame($parsedFrame); + $thisfile_id3v2[$frame_name][] = $parsedFrame; + + $framedata = substr($framedata, $frame_size); + + } else { // invalid frame length or FrameID + + if ($frame_size <= strlen($framedata)) { + + if ($this->IsValidID3v2FrameName(substr($framedata, $frame_size, 4), $id3v2_majorversion)) { + + // next frame is valid, just skip the current frame + $framedata = substr($framedata, $frame_size); + $info['warning'][] = 'Next ID3v2 frame is valid, skipping current frame.'; + + } else { + + // next frame is invalid too, abort processing + //unset($framedata); + $framedata = null; + $info['error'][] = 'Next ID3v2 frame is also invalid, aborting processing.'; + + } + + } elseif ($frame_size == strlen($framedata)) { + + // this is the last frame, just skip + $info['warning'][] = 'This was the last ID3v2 frame.'; + + } else { + + // next frame is invalid too, abort processing + //unset($framedata); + $framedata = null; + $info['warning'][] = 'Invalid ID3v2 frame size, aborting.'; + + } + if (!$this->IsValidID3v2FrameName($frame_name, $id3v2_majorversion)) { + + switch ($frame_name) { + case "\x00\x00".'MP': + case "\x00".'MP3': + case ' MP3': + case 'MP3e': + case "\x00".'MP': + case ' MP': + case 'MP3': + $info['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: !IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))). [Note: this particular error has been known to happen with tags edited by "MP3ext (www.mutschler.de/mp3ext/)"]'; + break; + + default: + $info['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: !IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))).'; + break; + } + + } elseif (!isset($framedata) || ($frame_size > strlen($framedata))) { + + $info['error'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: $frame_size ('.$frame_size.') > strlen($framedata) ('.(isset($framedata) ? strlen($framedata) : 'null').')).'; + + } else { + + $info['error'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag).'; + + } + + } + $framedataoffset += ($frame_size + $this->ID3v2HeaderLength($id3v2_majorversion)); + + } + + } + + + // Footer + + // The footer is a copy of the header, but with a different identifier. + // ID3v2 identifier "3DI" + // ID3v2 version $04 00 + // ID3v2 flags %abcd0000 + // ID3v2 size 4 * %0xxxxxxx + + if (isset($thisfile_id3v2_flags['isfooter']) && $thisfile_id3v2_flags['isfooter']) { + $footer = $this->fread(10); + if (substr($footer, 0, 3) == '3DI') { + $thisfile_id3v2['footer'] = true; + $thisfile_id3v2['majorversion_footer'] = ord($footer{3}); + $thisfile_id3v2['minorversion_footer'] = ord($footer{4}); + } + if ($thisfile_id3v2['majorversion_footer'] <= 4) { + $id3_flags = ord(substr($footer{5})); + $thisfile_id3v2_flags['unsynch_footer'] = (bool) ($id3_flags & 0x80); + $thisfile_id3v2_flags['extfoot_footer'] = (bool) ($id3_flags & 0x40); + $thisfile_id3v2_flags['experim_footer'] = (bool) ($id3_flags & 0x20); + $thisfile_id3v2_flags['isfooter_footer'] = (bool) ($id3_flags & 0x10); + + $thisfile_id3v2['footerlength'] = getid3_lib::BigEndian2Int(substr($footer, 6, 4), 1); + } + } // end footer + + if (isset($thisfile_id3v2['comments']['genre'])) { + foreach ($thisfile_id3v2['comments']['genre'] as $key => $value) { + unset($thisfile_id3v2['comments']['genre'][$key]); + $thisfile_id3v2['comments'] = getid3_lib::array_merge_noclobber($thisfile_id3v2['comments'], array('genre'=>$this->ParseID3v2GenreString($value))); + } + } + + if (isset($thisfile_id3v2['comments']['track'])) { + foreach ($thisfile_id3v2['comments']['track'] as $key => $value) { + if (strstr($value, '/')) { + list($thisfile_id3v2['comments']['tracknum'][$key], $thisfile_id3v2['comments']['totaltracks'][$key]) = explode('/', $thisfile_id3v2['comments']['track'][$key]); + } + } + } + + if (!isset($thisfile_id3v2['comments']['year']) && !empty($thisfile_id3v2['comments']['recording_time'][0]) && preg_match('#^([0-9]{4})#', trim($thisfile_id3v2['comments']['recording_time'][0]), $matches)) { + $thisfile_id3v2['comments']['year'] = array($matches[1]); + } + + + if (!empty($thisfile_id3v2['TXXX'])) { + // MediaMonkey does this, maybe others: write a blank RGAD frame, but put replay-gain adjustment values in TXXX frames + foreach ($thisfile_id3v2['TXXX'] as $txxx_array) { + switch ($txxx_array['description']) { + case 'replaygain_track_gain': + if (empty($info['replay_gain']['track']['adjustment']) && !empty($txxx_array['data'])) { + $info['replay_gain']['track']['adjustment'] = floatval(trim(str_replace('dB', '', $txxx_array['data']))); + } + break; + case 'replaygain_track_peak': + if (empty($info['replay_gain']['track']['peak']) && !empty($txxx_array['data'])) { + $info['replay_gain']['track']['peak'] = floatval($txxx_array['data']); + } + break; + case 'replaygain_album_gain': + if (empty($info['replay_gain']['album']['adjustment']) && !empty($txxx_array['data'])) { + $info['replay_gain']['album']['adjustment'] = floatval(trim(str_replace('dB', '', $txxx_array['data']))); + } + break; + } + } + } + + + // Set avdataoffset + $info['avdataoffset'] = $thisfile_id3v2['headerlength']; + if (isset($thisfile_id3v2['footer'])) { + $info['avdataoffset'] += 10; + } + + return true; + } + + + public function ParseID3v2GenreString($genrestring) { + // Parse genres into arrays of genreName and genreID + // ID3v2.2.x, ID3v2.3.x: '(21)' or '(4)Eurodisco' or '(51)(39)' or '(55)((I think...)' + // ID3v2.4.x: '21' $00 'Eurodisco' $00 + $clean_genres = array(); + if (strpos($genrestring, "\x00") === false) { + $genrestring = preg_replace('#\(([0-9]{1,3})\)#', '$1'."\x00", $genrestring); + } + $genre_elements = explode("\x00", $genrestring); + foreach ($genre_elements as $element) { + $element = trim($element); + if ($element) { + if (preg_match('#^[0-9]{1,3}#', $element)) { + $clean_genres[] = getid3_id3v1::LookupGenreName($element); + } else { + $clean_genres[] = str_replace('((', '(', $element); + } + } + } + return $clean_genres; + } + + + public function ParseID3v2Frame(&$parsedFrame) { + + // shortcuts + $info = &$this->getid3->info; + $id3v2_majorversion = $info['id3v2']['majorversion']; + + $parsedFrame['framenamelong'] = $this->FrameNameLongLookup($parsedFrame['frame_name']); + if (empty($parsedFrame['framenamelong'])) { + unset($parsedFrame['framenamelong']); + } + $parsedFrame['framenameshort'] = $this->FrameNameShortLookup($parsedFrame['frame_name']); + if (empty($parsedFrame['framenameshort'])) { + unset($parsedFrame['framenameshort']); + } + + if ($id3v2_majorversion >= 3) { // frame flags are not part of the ID3v2.2 standard + if ($id3v2_majorversion == 3) { + // Frame Header Flags + // %abc00000 %ijk00000 + $parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x8000); // a - Tag alter preservation + $parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000); // b - File alter preservation + $parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000); // c - Read only + $parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0080); // i - Compression + $parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0040); // j - Encryption + $parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0020); // k - Grouping identity + + } elseif ($id3v2_majorversion == 4) { + // Frame Header Flags + // %0abc0000 %0h00kmnp + $parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000); // a - Tag alter preservation + $parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000); // b - File alter preservation + $parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x1000); // c - Read only + $parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0040); // h - Grouping identity + $parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0008); // k - Compression + $parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0004); // m - Encryption + $parsedFrame['flags']['Unsynchronisation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0002); // n - Unsynchronisation + $parsedFrame['flags']['DataLengthIndicator'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x0001); // p - Data length indicator + + // Frame-level de-unsynchronisation - ID3v2.4 + if ($parsedFrame['flags']['Unsynchronisation']) { + $parsedFrame['data'] = $this->DeUnsynchronise($parsedFrame['data']); + } + + if ($parsedFrame['flags']['DataLengthIndicator']) { + $parsedFrame['data_length_indicator'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 4), 1); + $parsedFrame['data'] = substr($parsedFrame['data'], 4); + } + } + + // Frame-level de-compression + if ($parsedFrame['flags']['compression']) { + $parsedFrame['decompressed_size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 4)); + if (!function_exists('gzuncompress')) { + $info['warning'][] = 'gzuncompress() support required to decompress ID3v2 frame "'.$parsedFrame['frame_name'].'"'; + } else { + if ($decompresseddata = @gzuncompress(substr($parsedFrame['data'], 4))) { + //if ($decompresseddata = @gzuncompress($parsedFrame['data'])) { + $parsedFrame['data'] = $decompresseddata; + unset($decompresseddata); + } else { + $info['warning'][] = 'gzuncompress() failed on compressed contents of ID3v2 frame "'.$parsedFrame['frame_name'].'"'; + } + } + } + } + + if (!empty($parsedFrame['flags']['DataLengthIndicator'])) { + if ($parsedFrame['data_length_indicator'] != strlen($parsedFrame['data'])) { + $info['warning'][] = 'ID3v2 frame "'.$parsedFrame['frame_name'].'" should be '.$parsedFrame['data_length_indicator'].' bytes long according to DataLengthIndicator, but found '.strlen($parsedFrame['data']).' bytes of data'; + } + } + + if (isset($parsedFrame['datalength']) && ($parsedFrame['datalength'] == 0)) { + + $warning = 'Frame "'.$parsedFrame['frame_name'].'" at offset '.$parsedFrame['dataoffset'].' has no data portion'; + switch ($parsedFrame['frame_name']) { + case 'WCOM': + $warning .= ' (this is known to happen with files tagged by RioPort)'; + break; + + default: + break; + } + $info['warning'][] = $warning; + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'UFID')) || // 4.1 UFID Unique file identifier + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'UFI'))) { // 4.1 UFI Unique file identifier + // There may be more than one 'UFID' frame in a tag, + // but only one with the same 'Owner identifier'. + //
        + // Owner identifier $00 + // Identifier + $exploded = explode("\x00", $parsedFrame['data'], 2); + $parsedFrame['ownerid'] = (isset($exploded[0]) ? $exploded[0] : ''); + $parsedFrame['data'] = (isset($exploded[1]) ? $exploded[1] : ''); + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'TXXX')) || // 4.2.2 TXXX User defined text information frame + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'TXX'))) { // 4.2.2 TXX User defined text information frame + // There may be more than one 'TXXX' frame in each tag, + // but only one with the same description. + //
        + // Text encoding $xx + // Description $00 (00) + // Value + + $frame_offset = 0; + $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + + if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { + $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + } + $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); + if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { + $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 + } + $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_description) === 0) { + $frame_description = ''; + } + $parsedFrame['encodingid'] = $frame_textencoding; + $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); + + $parsedFrame['description'] = $frame_description; + $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); + if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { + $commentkey = ($parsedFrame['description'] ? $parsedFrame['description'] : (isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) ? count($info['id3v2']['comments'][$parsedFrame['framenameshort']]) : 0)); + if (!isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) || !array_key_exists($commentkey, $info['id3v2']['comments'][$parsedFrame['framenameshort']])) { + $info['id3v2']['comments'][$parsedFrame['framenameshort']][$commentkey] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data'])); + } else { + $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data'])); + } + } + //unset($parsedFrame['data']); do not unset, may be needed elsewhere, e.g. for replaygain + + + } elseif ($parsedFrame['frame_name']{0} == 'T') { // 4.2. T??[?] Text information frame + // There may only be one text information frame of its kind in an tag. + //
        + // Text encoding $xx + // Information + + $frame_offset = 0; + $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { + $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + } + + $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); + + $parsedFrame['encodingid'] = $frame_textencoding; + $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); + + if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { + // ID3v2.3 specs say that TPE1 (and others) can contain multiple artist values separated with / + // This of course breaks when an artist name contains slash character, e.g. "AC/DC" + // MP3tag (maybe others) implement alternative system where multiple artists are null-separated, which makes more sense + // getID3 will split null-separated artists into multiple artists and leave slash-separated ones to the user + switch ($parsedFrame['encoding']) { + case 'UTF-16': + case 'UTF-16BE': + case 'UTF-16LE': + $wordsize = 2; + break; + case 'ISO-8859-1': + case 'UTF-8': + default: + $wordsize = 1; + break; + } + $Txxx_elements = array(); + $Txxx_elements_start_offset = 0; + for ($i = 0; $i < strlen($parsedFrame['data']); $i += $wordsize) { + if (substr($parsedFrame['data'], $i, $wordsize) == str_repeat("\x00", $wordsize)) { + $Txxx_elements[] = substr($parsedFrame['data'], $Txxx_elements_start_offset, $i - $Txxx_elements_start_offset); + $Txxx_elements_start_offset = $i + $wordsize; + } + } + $Txxx_elements[] = substr($parsedFrame['data'], $Txxx_elements_start_offset, $i - $Txxx_elements_start_offset); + foreach ($Txxx_elements as $Txxx_element) { + $string = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $Txxx_element); + if (!empty($string)) { + $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $string; + } + } + unset($string, $wordsize, $i, $Txxx_elements, $Txxx_element, $Txxx_elements_start_offset); + } + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'WXXX')) || // 4.3.2 WXXX User defined URL link frame + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'WXX'))) { // 4.3.2 WXX User defined URL link frame + // There may be more than one 'WXXX' frame in each tag, + // but only one with the same description + //
        + // Text encoding $xx + // Description $00 (00) + // URL + + $frame_offset = 0; + $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { + $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + } + $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); + if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { + $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 + } + $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + + if (ord($frame_description) === 0) { + $frame_description = ''; + } + $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); + + $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding)); + if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { + $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 + } + if ($frame_terminatorpos) { + // there are null bytes after the data - this is not according to spec + // only use data up to first null byte + $frame_urldata = (string) substr($parsedFrame['data'], 0, $frame_terminatorpos); + } else { + // no null bytes following data, just use all data + $frame_urldata = (string) $parsedFrame['data']; + } + + $parsedFrame['encodingid'] = $frame_textencoding; + $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); + + $parsedFrame['url'] = $frame_urldata; + $parsedFrame['description'] = $frame_description; + if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { + $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['url']); + } + unset($parsedFrame['data']); + + + } elseif ($parsedFrame['frame_name']{0} == 'W') { // 4.3. W??? URL link frames + // There may only be one URL link frame of its kind in a tag, + // except when stated otherwise in the frame description + //
        + // URL + + $parsedFrame['url'] = trim($parsedFrame['data']); + if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { + $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['url']; + } + unset($parsedFrame['data']); + + + } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'IPLS')) || // 4.4 IPLS Involved people list (ID3v2.3 only) + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'IPL'))) { // 4.4 IPL Involved people list (ID3v2.2 only) + // http://id3.org/id3v2.3.0#sec4.4 + // There may only be one 'IPL' frame in each tag + //
        + // Text encoding $xx + // People list strings + + $frame_offset = 0; + $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { + $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + } + $parsedFrame['encodingid'] = $frame_textencoding; + $parsedFrame['encoding'] = $this->TextEncodingNameLookup($parsedFrame['encodingid']); + $parsedFrame['data_raw'] = (string) substr($parsedFrame['data'], $frame_offset); + + // http://www.getid3.org/phpBB3/viewtopic.php?t=1369 + // "this tag typically contains null terminated strings, which are associated in pairs" + // "there are users that use the tag incorrectly" + $IPLS_parts = array(); + if (strpos($parsedFrame['data_raw'], "\x00") !== false) { + $IPLS_parts_unsorted = array(); + if (((strlen($parsedFrame['data_raw']) % 2) == 0) && ((substr($parsedFrame['data_raw'], 0, 2) == "\xFF\xFE") || (substr($parsedFrame['data_raw'], 0, 2) == "\xFE\xFF"))) { + // UTF-16, be careful looking for null bytes since most 2-byte characters may contain one; you need to find twin null bytes, and on even padding + $thisILPS = ''; + for ($i = 0; $i < strlen($parsedFrame['data_raw']); $i += 2) { + $twobytes = substr($parsedFrame['data_raw'], $i, 2); + if ($twobytes === "\x00\x00") { + $IPLS_parts_unsorted[] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $thisILPS); + $thisILPS = ''; + } else { + $thisILPS .= $twobytes; + } + } + if (strlen($thisILPS) > 2) { // 2-byte BOM + $IPLS_parts_unsorted[] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $thisILPS); + } + } else { + // ISO-8859-1 or UTF-8 or other single-byte-null character set + $IPLS_parts_unsorted = explode("\x00", $parsedFrame['data_raw']); + } + if (count($IPLS_parts_unsorted) == 1) { + // just a list of names, e.g. "Dino Baptiste, Jimmy Copley, John Gordon, Bernie Marsden, Sharon Watson" + foreach ($IPLS_parts_unsorted as $key => $value) { + $IPLS_parts_sorted = preg_split('#[;,\\r\\n\\t]#', $value); + $position = ''; + foreach ($IPLS_parts_sorted as $person) { + $IPLS_parts[] = array('position'=>$position, 'person'=>$person); + } + } + } elseif ((count($IPLS_parts_unsorted) % 2) == 0) { + $position = ''; + $person = ''; + foreach ($IPLS_parts_unsorted as $key => $value) { + if (($key % 2) == 0) { + $position = $value; + } else { + $person = $value; + $IPLS_parts[] = array('position'=>$position, 'person'=>$person); + $position = ''; + $person = ''; + } + } + } else { + foreach ($IPLS_parts_unsorted as $key => $value) { + $IPLS_parts[] = array($value); + } + } + + } else { + $IPLS_parts = preg_split('#[;,\\r\\n\\t]#', $parsedFrame['data_raw']); + } + $parsedFrame['data'] = $IPLS_parts; + + if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { + $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['data']; + } + + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'MCDI')) || // 4.4 MCDI Music CD identifier + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'MCI'))) { // 4.5 MCI Music CD identifier + // There may only be one 'MCDI' frame in each tag + //
        + // CD TOC + + if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { + $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['data']; + } + + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'ETCO')) || // 4.5 ETCO Event timing codes + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'ETC'))) { // 4.6 ETC Event timing codes + // There may only be one 'ETCO' frame in each tag + //
        + // Time stamp format $xx + // Where time stamp format is: + // $01 (32-bit value) MPEG frames from beginning of file + // $02 (32-bit value) milliseconds from beginning of file + // Followed by a list of key events in the following format: + // Type of event $xx + // Time stamp $xx (xx ...) + // The 'Time stamp' is set to zero if directly at the beginning of the sound + // or after the previous event. All events MUST be sorted in chronological order. + + $frame_offset = 0; + $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + + while ($frame_offset < strlen($parsedFrame['data'])) { + $parsedFrame['typeid'] = substr($parsedFrame['data'], $frame_offset++, 1); + $parsedFrame['type'] = $this->ETCOEventLookup($parsedFrame['typeid']); + $parsedFrame['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); + $frame_offset += 4; + } + unset($parsedFrame['data']); + + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'MLLT')) || // 4.6 MLLT MPEG location lookup table + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'MLL'))) { // 4.7 MLL MPEG location lookup table + // There may only be one 'MLLT' frame in each tag + //
        + // MPEG frames between reference $xx xx + // Bytes between reference $xx xx xx + // Milliseconds between reference $xx xx xx + // Bits for bytes deviation $xx + // Bits for milliseconds dev. $xx + // Then for every reference the following data is included; + // Deviation in bytes %xxx.... + // Deviation in milliseconds %xxx.... + + $frame_offset = 0; + $parsedFrame['framesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 2)); + $parsedFrame['bytesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 2, 3)); + $parsedFrame['msbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 5, 3)); + $parsedFrame['bitsforbytesdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 8, 1)); + $parsedFrame['bitsformsdeviation'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 9, 1)); + $parsedFrame['data'] = substr($parsedFrame['data'], 10); + while ($frame_offset < strlen($parsedFrame['data'])) { + $deviationbitstream .= getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1)); + } + $reference_counter = 0; + while (strlen($deviationbitstream) > 0) { + $parsedFrame[$reference_counter]['bytedeviation'] = bindec(substr($deviationbitstream, 0, $parsedFrame['bitsforbytesdeviation'])); + $parsedFrame[$reference_counter]['msdeviation'] = bindec(substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'], $parsedFrame['bitsformsdeviation'])); + $deviationbitstream = substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'] + $parsedFrame['bitsformsdeviation']); + $reference_counter++; + } + unset($parsedFrame['data']); + + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'SYTC')) || // 4.7 SYTC Synchronised tempo codes + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'STC'))) { // 4.8 STC Synchronised tempo codes + // There may only be one 'SYTC' frame in each tag + //
        + // Time stamp format $xx + // Tempo data + // Where time stamp format is: + // $01 (32-bit value) MPEG frames from beginning of file + // $02 (32-bit value) milliseconds from beginning of file + + $frame_offset = 0; + $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $timestamp_counter = 0; + while ($frame_offset < strlen($parsedFrame['data'])) { + $parsedFrame[$timestamp_counter]['tempo'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + if ($parsedFrame[$timestamp_counter]['tempo'] == 255) { + $parsedFrame[$timestamp_counter]['tempo'] += ord(substr($parsedFrame['data'], $frame_offset++, 1)); + } + $parsedFrame[$timestamp_counter]['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); + $frame_offset += 4; + $timestamp_counter++; + } + unset($parsedFrame['data']); + + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'USLT')) || // 4.8 USLT Unsynchronised lyric/text transcription + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'ULT'))) { // 4.9 ULT Unsynchronised lyric/text transcription + // There may be more than one 'Unsynchronised lyrics/text transcription' frame + // in each tag, but only one with the same language and content descriptor. + //
        + // Text encoding $xx + // Language $xx xx xx + // Content descriptor $00 (00) + // Lyrics/text + + $frame_offset = 0; + $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { + $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + } + $frame_language = substr($parsedFrame['data'], $frame_offset, 3); + $frame_offset += 3; + $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); + if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { + $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 + } + $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_description) === 0) { + $frame_description = ''; + } + $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); + + $parsedFrame['encodingid'] = $frame_textencoding; + $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); + + $parsedFrame['data'] = $parsedFrame['data']; + $parsedFrame['language'] = $frame_language; + $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); + $parsedFrame['description'] = $frame_description; + if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { + $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']); + } + unset($parsedFrame['data']); + + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'SYLT')) || // 4.9 SYLT Synchronised lyric/text + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'SLT'))) { // 4.10 SLT Synchronised lyric/text + // There may be more than one 'SYLT' frame in each tag, + // but only one with the same language and content descriptor. + //
        + // Text encoding $xx + // Language $xx xx xx + // Time stamp format $xx + // $01 (32-bit value) MPEG frames from beginning of file + // $02 (32-bit value) milliseconds from beginning of file + // Content type $xx + // Content descriptor $00 (00) + // Terminated text to be synced (typically a syllable) + // Sync identifier (terminator to above string) $00 (00) + // Time stamp $xx (xx ...) + + $frame_offset = 0; + $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { + $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + } + $frame_language = substr($parsedFrame['data'], $frame_offset, 3); + $frame_offset += 3; + $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $parsedFrame['contenttypeid'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $parsedFrame['contenttype'] = $this->SYTLContentTypeLookup($parsedFrame['contenttypeid']); + $parsedFrame['encodingid'] = $frame_textencoding; + $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); + + $parsedFrame['language'] = $frame_language; + $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); + + $timestampindex = 0; + $frame_remainingdata = substr($parsedFrame['data'], $frame_offset); + while (strlen($frame_remainingdata)) { + $frame_offset = 0; + $frame_terminatorpos = strpos($frame_remainingdata, $this->TextEncodingTerminatorLookup($frame_textencoding)); + if ($frame_terminatorpos === false) { + $frame_remainingdata = ''; + } else { + if (ord(substr($frame_remainingdata, $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { + $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 + } + $parsedFrame['lyrics'][$timestampindex]['data'] = substr($frame_remainingdata, $frame_offset, $frame_terminatorpos - $frame_offset); + + $frame_remainingdata = substr($frame_remainingdata, $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); + if (($timestampindex == 0) && (ord($frame_remainingdata{0}) != 0)) { + // timestamp probably omitted for first data item + } else { + $parsedFrame['lyrics'][$timestampindex]['timestamp'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 4)); + $frame_remainingdata = substr($frame_remainingdata, 4); + } + $timestampindex++; + } + } + unset($parsedFrame['data']); + + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'COMM')) || // 4.10 COMM Comments + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'COM'))) { // 4.11 COM Comments + // There may be more than one comment frame in each tag, + // but only one with the same language and content descriptor. + //
        + // Text encoding $xx + // Language $xx xx xx + // Short content descrip. $00 (00) + // The actual text + + if (strlen($parsedFrame['data']) < 5) { + + $info['warning'][] = 'Invalid data (too short) for "'.$parsedFrame['frame_name'].'" frame at offset '.$parsedFrame['dataoffset']; + + } else { + + $frame_offset = 0; + $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { + $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + } + $frame_language = substr($parsedFrame['data'], $frame_offset, 3); + $frame_offset += 3; + $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); + if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { + $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 + } + $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_description) === 0) { + $frame_description = ''; + } + $frame_text = (string) substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); + + $parsedFrame['encodingid'] = $frame_textencoding; + $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); + + $parsedFrame['language'] = $frame_language; + $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); + $parsedFrame['description'] = $frame_description; + $parsedFrame['data'] = $frame_text; + if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { + $commentkey = ($parsedFrame['description'] ? $parsedFrame['description'] : (!empty($info['id3v2']['comments'][$parsedFrame['framenameshort']]) ? count($info['id3v2']['comments'][$parsedFrame['framenameshort']]) : 0)); + if (!isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) || !array_key_exists($commentkey, $info['id3v2']['comments'][$parsedFrame['framenameshort']])) { + $info['id3v2']['comments'][$parsedFrame['framenameshort']][$commentkey] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']); + } else { + $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']); + } + } + + } + + } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'RVA2')) { // 4.11 RVA2 Relative volume adjustment (2) (ID3v2.4+ only) + // There may be more than one 'RVA2' frame in each tag, + // but only one with the same identification string + //
        + // Identification $00 + // The 'identification' string is used to identify the situation and/or + // device where this adjustment should apply. The following is then + // repeated for every channel: + // Type of channel $xx + // Volume adjustment $xx xx + // Bits representing peak $xx + // Peak volume $xx (xx ...) + + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00"); + $frame_idstring = substr($parsedFrame['data'], 0, $frame_terminatorpos); + if (ord($frame_idstring) === 0) { + $frame_idstring = ''; + } + $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen("\x00")); + $parsedFrame['description'] = $frame_idstring; + $RVA2channelcounter = 0; + while (strlen($frame_remainingdata) >= 5) { + $frame_offset = 0; + $frame_channeltypeid = ord(substr($frame_remainingdata, $frame_offset++, 1)); + $parsedFrame[$RVA2channelcounter]['channeltypeid'] = $frame_channeltypeid; + $parsedFrame[$RVA2channelcounter]['channeltype'] = $this->RVA2ChannelTypeLookup($frame_channeltypeid); + $parsedFrame[$RVA2channelcounter]['volumeadjust'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, 2), false, true); // 16-bit signed + $frame_offset += 2; + $parsedFrame[$RVA2channelcounter]['bitspeakvolume'] = ord(substr($frame_remainingdata, $frame_offset++, 1)); + if (($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] < 1) || ($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] > 4)) { + $info['warning'][] = 'ID3v2::RVA2 frame['.$RVA2channelcounter.'] contains invalid '.$parsedFrame[$RVA2channelcounter]['bitspeakvolume'].'-byte bits-representing-peak value'; + break; + } + $frame_bytespeakvolume = ceil($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] / 8); + $parsedFrame[$RVA2channelcounter]['peakvolume'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, $frame_bytespeakvolume)); + $frame_remainingdata = substr($frame_remainingdata, $frame_offset + $frame_bytespeakvolume); + $RVA2channelcounter++; + } + unset($parsedFrame['data']); + + + } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'RVAD')) || // 4.12 RVAD Relative volume adjustment (ID3v2.3 only) + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'RVA'))) { // 4.12 RVA Relative volume adjustment (ID3v2.2 only) + // There may only be one 'RVA' frame in each tag + //
        + // ID3v2.2 => Increment/decrement %000000ba + // ID3v2.3 => Increment/decrement %00fedcba + // Bits used for volume descr. $xx + // Relative volume change, right $xx xx (xx ...) // a + // Relative volume change, left $xx xx (xx ...) // b + // Peak volume right $xx xx (xx ...) + // Peak volume left $xx xx (xx ...) + // ID3v2.3 only, optional (not present in ID3v2.2): + // Relative volume change, right back $xx xx (xx ...) // c + // Relative volume change, left back $xx xx (xx ...) // d + // Peak volume right back $xx xx (xx ...) + // Peak volume left back $xx xx (xx ...) + // ID3v2.3 only, optional (not present in ID3v2.2): + // Relative volume change, center $xx xx (xx ...) // e + // Peak volume center $xx xx (xx ...) + // ID3v2.3 only, optional (not present in ID3v2.2): + // Relative volume change, bass $xx xx (xx ...) // f + // Peak volume bass $xx xx (xx ...) + + $frame_offset = 0; + $frame_incrdecrflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1)); + $parsedFrame['incdec']['right'] = (bool) substr($frame_incrdecrflags, 6, 1); + $parsedFrame['incdec']['left'] = (bool) substr($frame_incrdecrflags, 7, 1); + $parsedFrame['bitsvolume'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $frame_bytesvolume = ceil($parsedFrame['bitsvolume'] / 8); + $parsedFrame['volumechange']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); + if ($parsedFrame['incdec']['right'] === false) { + $parsedFrame['volumechange']['right'] *= -1; + } + $frame_offset += $frame_bytesvolume; + $parsedFrame['volumechange']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); + if ($parsedFrame['incdec']['left'] === false) { + $parsedFrame['volumechange']['left'] *= -1; + } + $frame_offset += $frame_bytesvolume; + $parsedFrame['peakvolume']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); + $frame_offset += $frame_bytesvolume; + $parsedFrame['peakvolume']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); + $frame_offset += $frame_bytesvolume; + if ($id3v2_majorversion == 3) { + $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset); + if (strlen($parsedFrame['data']) > 0) { + $parsedFrame['incdec']['rightrear'] = (bool) substr($frame_incrdecrflags, 4, 1); + $parsedFrame['incdec']['leftrear'] = (bool) substr($frame_incrdecrflags, 5, 1); + $parsedFrame['volumechange']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); + if ($parsedFrame['incdec']['rightrear'] === false) { + $parsedFrame['volumechange']['rightrear'] *= -1; + } + $frame_offset += $frame_bytesvolume; + $parsedFrame['volumechange']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); + if ($parsedFrame['incdec']['leftrear'] === false) { + $parsedFrame['volumechange']['leftrear'] *= -1; + } + $frame_offset += $frame_bytesvolume; + $parsedFrame['peakvolume']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); + $frame_offset += $frame_bytesvolume; + $parsedFrame['peakvolume']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); + $frame_offset += $frame_bytesvolume; + } + $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset); + if (strlen($parsedFrame['data']) > 0) { + $parsedFrame['incdec']['center'] = (bool) substr($frame_incrdecrflags, 3, 1); + $parsedFrame['volumechange']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); + if ($parsedFrame['incdec']['center'] === false) { + $parsedFrame['volumechange']['center'] *= -1; + } + $frame_offset += $frame_bytesvolume; + $parsedFrame['peakvolume']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); + $frame_offset += $frame_bytesvolume; + } + $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset); + if (strlen($parsedFrame['data']) > 0) { + $parsedFrame['incdec']['bass'] = (bool) substr($frame_incrdecrflags, 2, 1); + $parsedFrame['volumechange']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); + if ($parsedFrame['incdec']['bass'] === false) { + $parsedFrame['volumechange']['bass'] *= -1; + } + $frame_offset += $frame_bytesvolume; + $parsedFrame['peakvolume']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume)); + $frame_offset += $frame_bytesvolume; + } + } + unset($parsedFrame['data']); + + + } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'EQU2')) { // 4.12 EQU2 Equalisation (2) (ID3v2.4+ only) + // There may be more than one 'EQU2' frame in each tag, + // but only one with the same identification string + //
        + // Interpolation method $xx + // $00 Band + // $01 Linear + // Identification $00 + // The following is then repeated for every adjustment point + // Frequency $xx xx + // Volume adjustment $xx xx + + $frame_offset = 0; + $frame_interpolationmethod = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); + $frame_idstring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_idstring) === 0) { + $frame_idstring = ''; + } + $parsedFrame['description'] = $frame_idstring; + $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen("\x00")); + while (strlen($frame_remainingdata)) { + $frame_frequency = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 2)) / 2; + $parsedFrame['data'][$frame_frequency] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, 2), false, true); + $frame_remainingdata = substr($frame_remainingdata, 4); + } + $parsedFrame['interpolationmethod'] = $frame_interpolationmethod; + unset($parsedFrame['data']); + + + } elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'EQUA')) || // 4.12 EQUA Equalisation (ID3v2.3 only) + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'EQU'))) { // 4.13 EQU Equalisation (ID3v2.2 only) + // There may only be one 'EQUA' frame in each tag + //
        + // Adjustment bits $xx + // This is followed by 2 bytes + ('adjustment bits' rounded up to the + // nearest byte) for every equalisation band in the following format, + // giving a frequency range of 0 - 32767Hz: + // Increment/decrement %x (MSB of the Frequency) + // Frequency (lower 15 bits) + // Adjustment $xx (xx ...) + + $frame_offset = 0; + $parsedFrame['adjustmentbits'] = substr($parsedFrame['data'], $frame_offset++, 1); + $frame_adjustmentbytes = ceil($parsedFrame['adjustmentbits'] / 8); + + $frame_remainingdata = (string) substr($parsedFrame['data'], $frame_offset); + while (strlen($frame_remainingdata) > 0) { + $frame_frequencystr = getid3_lib::BigEndian2Bin(substr($frame_remainingdata, 0, 2)); + $frame_incdec = (bool) substr($frame_frequencystr, 0, 1); + $frame_frequency = bindec(substr($frame_frequencystr, 1, 15)); + $parsedFrame[$frame_frequency]['incdec'] = $frame_incdec; + $parsedFrame[$frame_frequency]['adjustment'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, $frame_adjustmentbytes)); + if ($parsedFrame[$frame_frequency]['incdec'] === false) { + $parsedFrame[$frame_frequency]['adjustment'] *= -1; + } + $frame_remainingdata = substr($frame_remainingdata, 2 + $frame_adjustmentbytes); + } + unset($parsedFrame['data']); + + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RVRB')) || // 4.13 RVRB Reverb + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'REV'))) { // 4.14 REV Reverb + // There may only be one 'RVRB' frame in each tag. + //
        + // Reverb left (ms) $xx xx + // Reverb right (ms) $xx xx + // Reverb bounces, left $xx + // Reverb bounces, right $xx + // Reverb feedback, left to left $xx + // Reverb feedback, left to right $xx + // Reverb feedback, right to right $xx + // Reverb feedback, right to left $xx + // Premix left to right $xx + // Premix right to left $xx + + $frame_offset = 0; + $parsedFrame['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); + $frame_offset += 2; + $parsedFrame['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); + $frame_offset += 2; + $parsedFrame['bouncesL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $parsedFrame['bouncesR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $parsedFrame['feedbackLL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $parsedFrame['feedbackLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $parsedFrame['feedbackRR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $parsedFrame['feedbackRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $parsedFrame['premixLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $parsedFrame['premixRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + unset($parsedFrame['data']); + + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'APIC')) || // 4.14 APIC Attached picture + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'PIC'))) { // 4.15 PIC Attached picture + // There may be several pictures attached to one file, + // each in their individual 'APIC' frame, but only one + // with the same content descriptor + //
        + // Text encoding $xx + // ID3v2.3+ => MIME type $00 + // ID3v2.2 => Image format $xx xx xx + // Picture type $xx + // Description $00 (00) + // Picture data + + $frame_offset = 0; + $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { + $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + } + + if ($id3v2_majorversion == 2 && strlen($parsedFrame['data']) > $frame_offset) { + $frame_imagetype = substr($parsedFrame['data'], $frame_offset, 3); + if (strtolower($frame_imagetype) == 'ima') { + // complete hack for mp3Rage (www.chaoticsoftware.com) that puts ID3v2.3-formatted + // MIME type instead of 3-char ID3v2.2-format image type (thanks xbhoffØpacbell*net) + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); + $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_mimetype) === 0) { + $frame_mimetype = ''; + } + $frame_imagetype = strtoupper(str_replace('image/', '', strtolower($frame_mimetype))); + if ($frame_imagetype == 'JPEG') { + $frame_imagetype = 'JPG'; + } + $frame_offset = $frame_terminatorpos + strlen("\x00"); + } else { + $frame_offset += 3; + } + } + if ($id3v2_majorversion > 2 && strlen($parsedFrame['data']) > $frame_offset) { + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); + $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_mimetype) === 0) { + $frame_mimetype = ''; + } + $frame_offset = $frame_terminatorpos + strlen("\x00"); + } + + $frame_picturetype = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + + if ($frame_offset >= $parsedFrame['datalength']) { + $info['warning'][] = 'data portion of APIC frame is missing at offset '.($parsedFrame['dataoffset'] + 8 + $frame_offset); + } else { + $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); + if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { + $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 + } + $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_description) === 0) { + $frame_description = ''; + } + $parsedFrame['encodingid'] = $frame_textencoding; + $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); + + if ($id3v2_majorversion == 2) { + $parsedFrame['imagetype'] = $frame_imagetype; + } else { + $parsedFrame['mime'] = $frame_mimetype; + } + $parsedFrame['picturetypeid'] = $frame_picturetype; + $parsedFrame['picturetype'] = $this->APICPictureTypeLookup($frame_picturetype); + $parsedFrame['description'] = $frame_description; + $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding))); + $parsedFrame['datalength'] = strlen($parsedFrame['data']); + + $parsedFrame['image_mime'] = ''; + $imageinfo = array(); + $imagechunkcheck = getid3_lib::GetDataImageSize($parsedFrame['data'], $imageinfo); + if (($imagechunkcheck[2] >= 1) && ($imagechunkcheck[2] <= 3)) { + $parsedFrame['image_mime'] = 'image/'.getid3_lib::ImageTypesLookup($imagechunkcheck[2]); + if ($imagechunkcheck[0]) { + $parsedFrame['image_width'] = $imagechunkcheck[0]; + } + if ($imagechunkcheck[1]) { + $parsedFrame['image_height'] = $imagechunkcheck[1]; + } + } + + do { + if ($this->getid3->option_save_attachments === false) { + // skip entirely + unset($parsedFrame['data']); + break; + } + if ($this->getid3->option_save_attachments === true) { + // great +/* + } elseif (is_int($this->getid3->option_save_attachments)) { + if ($this->getid3->option_save_attachments < $parsedFrame['data_length']) { + // too big, skip + $info['warning'][] = 'attachment at '.$frame_offset.' is too large to process inline ('.number_format($parsedFrame['data_length']).' bytes)'; + unset($parsedFrame['data']); + break; + } +*/ + } elseif (is_string($this->getid3->option_save_attachments)) { + $dir = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->getid3->option_save_attachments), DIRECTORY_SEPARATOR); + if (!is_dir($dir) || !is_writable($dir)) { + // cannot write, skip + $info['warning'][] = 'attachment at '.$frame_offset.' cannot be saved to "'.$dir.'" (not writable)'; + unset($parsedFrame['data']); + break; + } + } + // if we get this far, must be OK + if (is_string($this->getid3->option_save_attachments)) { + $destination_filename = $dir.DIRECTORY_SEPARATOR.md5($info['filenamepath']).'_'.$frame_offset; + if (!file_exists($destination_filename) || is_writable($destination_filename)) { + file_put_contents($destination_filename, $parsedFrame['data']); + } else { + $info['warning'][] = 'attachment at '.$frame_offset.' cannot be saved to "'.$destination_filename.'" (not writable)'; + } + $parsedFrame['data_filename'] = $destination_filename; + unset($parsedFrame['data']); + } else { + if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { + if (!isset($info['id3v2']['comments']['picture'])) { + $info['id3v2']['comments']['picture'] = array(); + } + $info['id3v2']['comments']['picture'][] = array('data'=>$parsedFrame['data'], 'image_mime'=>$parsedFrame['image_mime']); + } + } + } while (false); + } + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'GEOB')) || // 4.15 GEOB General encapsulated object + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'GEO'))) { // 4.16 GEO General encapsulated object + // There may be more than one 'GEOB' frame in each tag, + // but only one with the same content descriptor + //
        + // Text encoding $xx + // MIME type $00 + // Filename $00 (00) + // Content description $00 (00) + // Encapsulated object + + $frame_offset = 0; + $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { + $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + } + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); + $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_mimetype) === 0) { + $frame_mimetype = ''; + } + $frame_offset = $frame_terminatorpos + strlen("\x00"); + + $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); + if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { + $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 + } + $frame_filename = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_filename) === 0) { + $frame_filename = ''; + } + $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)); + + $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); + if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { + $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 + } + $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_description) === 0) { + $frame_description = ''; + } + $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)); + + $parsedFrame['objectdata'] = (string) substr($parsedFrame['data'], $frame_offset); + $parsedFrame['encodingid'] = $frame_textencoding; + $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); + + $parsedFrame['mime'] = $frame_mimetype; + $parsedFrame['filename'] = $frame_filename; + $parsedFrame['description'] = $frame_description; + unset($parsedFrame['data']); + + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'PCNT')) || // 4.16 PCNT Play counter + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CNT'))) { // 4.17 CNT Play counter + // There may only be one 'PCNT' frame in each tag. + // When the counter reaches all one's, one byte is inserted in + // front of the counter thus making the counter eight bits bigger + //
        + // Counter $xx xx xx xx (xx ...) + + $parsedFrame['data'] = getid3_lib::BigEndian2Int($parsedFrame['data']); + + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'POPM')) || // 4.17 POPM Popularimeter + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'POP'))) { // 4.18 POP Popularimeter + // There may be more than one 'POPM' frame in each tag, + // but only one with the same email address + //
        + // Email to user $00 + // Rating $xx + // Counter $xx xx xx xx (xx ...) + + $frame_offset = 0; + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); + $frame_emailaddress = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_emailaddress) === 0) { + $frame_emailaddress = ''; + } + $frame_offset = $frame_terminatorpos + strlen("\x00"); + $frame_rating = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $parsedFrame['counter'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset)); + $parsedFrame['email'] = $frame_emailaddress; + $parsedFrame['rating'] = $frame_rating; + unset($parsedFrame['data']); + + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RBUF')) || // 4.18 RBUF Recommended buffer size + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'BUF'))) { // 4.19 BUF Recommended buffer size + // There may only be one 'RBUF' frame in each tag + //
        + // Buffer size $xx xx xx + // Embedded info flag %0000000x + // Offset to next tag $xx xx xx xx + + $frame_offset = 0; + $parsedFrame['buffersize'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 3)); + $frame_offset += 3; + + $frame_embeddedinfoflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1)); + $parsedFrame['flags']['embededinfo'] = (bool) substr($frame_embeddedinfoflags, 7, 1); + $parsedFrame['nexttagoffset'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); + unset($parsedFrame['data']); + + + } elseif (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CRM')) { // 4.20 Encrypted meta frame (ID3v2.2 only) + // There may be more than one 'CRM' frame in a tag, + // but only one with the same 'owner identifier' + //
        + // Owner identifier $00 (00) + // Content/explanation $00 (00) + // Encrypted datablock + + $frame_offset = 0; + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); + $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + $frame_offset = $frame_terminatorpos + strlen("\x00"); + + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); + $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_description) === 0) { + $frame_description = ''; + } + $frame_offset = $frame_terminatorpos + strlen("\x00"); + + $parsedFrame['ownerid'] = $frame_ownerid; + $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); + $parsedFrame['description'] = $frame_description; + unset($parsedFrame['data']); + + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'AENC')) || // 4.19 AENC Audio encryption + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CRA'))) { // 4.21 CRA Audio encryption + // There may be more than one 'AENC' frames in a tag, + // but only one with the same 'Owner identifier' + //
        + // Owner identifier $00 + // Preview start $xx xx + // Preview length $xx xx + // Encryption info + + $frame_offset = 0; + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); + $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_ownerid) === 0) { + $frame_ownerid == ''; + } + $frame_offset = $frame_terminatorpos + strlen("\x00"); + $parsedFrame['ownerid'] = $frame_ownerid; + $parsedFrame['previewstart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); + $frame_offset += 2; + $parsedFrame['previewlength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); + $frame_offset += 2; + $parsedFrame['encryptioninfo'] = (string) substr($parsedFrame['data'], $frame_offset); + unset($parsedFrame['data']); + + + } elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'LINK')) || // 4.20 LINK Linked information + (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'LNK'))) { // 4.22 LNK Linked information + // There may be more than one 'LINK' frame in a tag, + // but only one with the same contents + //
        + // ID3v2.3+ => Frame identifier $xx xx xx xx + // ID3v2.2 => Frame identifier $xx xx xx + // URL $00 + // ID and additional data + + $frame_offset = 0; + if ($id3v2_majorversion == 2) { + $parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 3); + $frame_offset += 3; + } else { + $parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 4); + $frame_offset += 4; + } + + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); + $frame_url = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_url) === 0) { + $frame_url = ''; + } + $frame_offset = $frame_terminatorpos + strlen("\x00"); + $parsedFrame['url'] = $frame_url; + + $parsedFrame['additionaldata'] = (string) substr($parsedFrame['data'], $frame_offset); + if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) { + $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = utf8_encode($parsedFrame['url']); + } + unset($parsedFrame['data']); + + + } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'POSS')) { // 4.21 POSS Position synchronisation frame (ID3v2.3+ only) + // There may only be one 'POSS' frame in each tag + // + // Time stamp format $xx + // Position $xx (xx ...) + + $frame_offset = 0; + $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $parsedFrame['position'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset)); + unset($parsedFrame['data']); + + + } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'USER')) { // 4.22 USER Terms of use (ID3v2.3+ only) + // There may be more than one 'Terms of use' frame in a tag, + // but only one with the same 'Language' + //
        + // Text encoding $xx + // Language $xx xx xx + // The actual text + + $frame_offset = 0; + $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { + $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + } + $frame_language = substr($parsedFrame['data'], $frame_offset, 3); + $frame_offset += 3; + $parsedFrame['language'] = $frame_language; + $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false); + $parsedFrame['encodingid'] = $frame_textencoding; + $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); + + $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); + if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) { + $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']); + } + unset($parsedFrame['data']); + + + } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'OWNE')) { // 4.23 OWNE Ownership frame (ID3v2.3+ only) + // There may only be one 'OWNE' frame in a tag + //
        + // Text encoding $xx + // Price paid $00 + // Date of purch. + // Seller + + $frame_offset = 0; + $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { + $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + } + $parsedFrame['encodingid'] = $frame_textencoding; + $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); + + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); + $frame_pricepaid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + $frame_offset = $frame_terminatorpos + strlen("\x00"); + + $parsedFrame['pricepaid']['currencyid'] = substr($frame_pricepaid, 0, 3); + $parsedFrame['pricepaid']['currency'] = $this->LookupCurrencyUnits($parsedFrame['pricepaid']['currencyid']); + $parsedFrame['pricepaid']['value'] = substr($frame_pricepaid, 3); + + $parsedFrame['purchasedate'] = substr($parsedFrame['data'], $frame_offset, 8); + if (!$this->IsValidDateStampString($parsedFrame['purchasedate'])) { + $parsedFrame['purchasedateunix'] = mktime (0, 0, 0, substr($parsedFrame['purchasedate'], 4, 2), substr($parsedFrame['purchasedate'], 6, 2), substr($parsedFrame['purchasedate'], 0, 4)); + } + $frame_offset += 8; + + $parsedFrame['seller'] = (string) substr($parsedFrame['data'], $frame_offset); + unset($parsedFrame['data']); + + + } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'COMR')) { // 4.24 COMR Commercial frame (ID3v2.3+ only) + // There may be more than one 'commercial frame' in a tag, + // but no two may be identical + //
        + // Text encoding $xx + // Price string $00 + // Valid until + // Contact URL $00 + // Received as $xx + // Name of seller $00 (00) + // Description $00 (00) + // Picture MIME type $00 + // Seller logo + + $frame_offset = 0; + $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) { + $info['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding'; + } + + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); + $frame_pricestring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + $frame_offset = $frame_terminatorpos + strlen("\x00"); + $frame_rawpricearray = explode('/', $frame_pricestring); + foreach ($frame_rawpricearray as $key => $val) { + $frame_currencyid = substr($val, 0, 3); + $parsedFrame['price'][$frame_currencyid]['currency'] = $this->LookupCurrencyUnits($frame_currencyid); + $parsedFrame['price'][$frame_currencyid]['value'] = substr($val, 3); + } + + $frame_datestring = substr($parsedFrame['data'], $frame_offset, 8); + $frame_offset += 8; + + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); + $frame_contacturl = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + $frame_offset = $frame_terminatorpos + strlen("\x00"); + + $frame_receivedasid = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + + $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); + if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { + $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 + } + $frame_sellername = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_sellername) === 0) { + $frame_sellername = ''; + } + $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)); + + $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset); + if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) { + $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00 + } + $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_description) === 0) { + $frame_description = ''; + } + $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)); + + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); + $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + $frame_offset = $frame_terminatorpos + strlen("\x00"); + + $frame_sellerlogo = substr($parsedFrame['data'], $frame_offset); + + $parsedFrame['encodingid'] = $frame_textencoding; + $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding); + + $parsedFrame['pricevaliduntil'] = $frame_datestring; + $parsedFrame['contacturl'] = $frame_contacturl; + $parsedFrame['receivedasid'] = $frame_receivedasid; + $parsedFrame['receivedas'] = $this->COMRReceivedAsLookup($frame_receivedasid); + $parsedFrame['sellername'] = $frame_sellername; + $parsedFrame['description'] = $frame_description; + $parsedFrame['mime'] = $frame_mimetype; + $parsedFrame['logo'] = $frame_sellerlogo; + unset($parsedFrame['data']); + + + } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'ENCR')) { // 4.25 ENCR Encryption method registration (ID3v2.3+ only) + // There may be several 'ENCR' frames in a tag, + // but only one containing the same symbol + // and only one containing the same owner identifier + //
        + // Owner identifier $00 + // Method symbol $xx + // Encryption data + + $frame_offset = 0; + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); + $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_ownerid) === 0) { + $frame_ownerid = ''; + } + $frame_offset = $frame_terminatorpos + strlen("\x00"); + + $parsedFrame['ownerid'] = $frame_ownerid; + $parsedFrame['methodsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); + + + } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'GRID')) { // 4.26 GRID Group identification registration (ID3v2.3+ only) + + // There may be several 'GRID' frames in a tag, + // but only one containing the same symbol + // and only one containing the same owner identifier + //
        + // Owner identifier $00 + // Group symbol $xx + // Group dependent data + + $frame_offset = 0; + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); + $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_ownerid) === 0) { + $frame_ownerid = ''; + } + $frame_offset = $frame_terminatorpos + strlen("\x00"); + + $parsedFrame['ownerid'] = $frame_ownerid; + $parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); + + + } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'PRIV')) { // 4.27 PRIV Private frame (ID3v2.3+ only) + // The tag may contain more than one 'PRIV' frame + // but only with different contents + //
        + // Owner identifier $00 + // The private data + + $frame_offset = 0; + $frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset); + $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); + if (ord($frame_ownerid) === 0) { + $frame_ownerid = ''; + } + $frame_offset = $frame_terminatorpos + strlen("\x00"); + + $parsedFrame['ownerid'] = $frame_ownerid; + $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); + + + } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'SIGN')) { // 4.28 SIGN Signature frame (ID3v2.4+ only) + // There may be more than one 'signature frame' in a tag, + // but no two may be identical + //
        + // Group symbol $xx + // Signature + + $frame_offset = 0; + $parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset); + + + } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'SEEK')) { // 4.29 SEEK Seek frame (ID3v2.4+ only) + // There may only be one 'seek frame' in a tag + //
        + // Minimum offset to next tag $xx xx xx xx + + $frame_offset = 0; + $parsedFrame['data'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); + + + } elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'ASPI')) { // 4.30 ASPI Audio seek point index (ID3v2.4+ only) + // There may only be one 'audio seek point index' frame in a tag + //
        + // Indexed data start (S) $xx xx xx xx + // Indexed data length (L) $xx xx xx xx + // Number of index points (N) $xx xx + // Bits per index point (b) $xx + // Then for every index point the following data is included: + // Fraction at index (Fi) $xx (xx) + + $frame_offset = 0; + $parsedFrame['datastart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); + $frame_offset += 4; + $parsedFrame['indexeddatalength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4)); + $frame_offset += 4; + $parsedFrame['indexpoints'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2)); + $frame_offset += 2; + $parsedFrame['bitsperpoint'] = ord(substr($parsedFrame['data'], $frame_offset++, 1)); + $frame_bytesperpoint = ceil($parsedFrame['bitsperpoint'] / 8); + for ($i = 0; $i < $parsedFrame['indexpoints']; $i++) { + $parsedFrame['indexes'][$i] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesperpoint)); + $frame_offset += $frame_bytesperpoint; + } + unset($parsedFrame['data']); + + } elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RGAD')) { // Replay Gain Adjustment + // http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html + // There may only be one 'RGAD' frame in a tag + //
        + // Peak Amplitude $xx $xx $xx $xx + // Radio Replay Gain Adjustment %aaabbbcd %dddddddd + // Audiophile Replay Gain Adjustment %aaabbbcd %dddddddd + // a - name code + // b - originator code + // c - sign bit + // d - replay gain adjustment + + $frame_offset = 0; + $parsedFrame['peakamplitude'] = getid3_lib::BigEndian2Float(substr($parsedFrame['data'], $frame_offset, 4)); + $frame_offset += 4; + $rg_track_adjustment = getid3_lib::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2)); + $frame_offset += 2; + $rg_album_adjustment = getid3_lib::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2)); + $frame_offset += 2; + $parsedFrame['raw']['track']['name'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 0, 3)); + $parsedFrame['raw']['track']['originator'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 3, 3)); + $parsedFrame['raw']['track']['signbit'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 6, 1)); + $parsedFrame['raw']['track']['adjustment'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 7, 9)); + $parsedFrame['raw']['album']['name'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 0, 3)); + $parsedFrame['raw']['album']['originator'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 3, 3)); + $parsedFrame['raw']['album']['signbit'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 6, 1)); + $parsedFrame['raw']['album']['adjustment'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 7, 9)); + $parsedFrame['track']['name'] = getid3_lib::RGADnameLookup($parsedFrame['raw']['track']['name']); + $parsedFrame['track']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['track']['originator']); + $parsedFrame['track']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['track']['adjustment'], $parsedFrame['raw']['track']['signbit']); + $parsedFrame['album']['name'] = getid3_lib::RGADnameLookup($parsedFrame['raw']['album']['name']); + $parsedFrame['album']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['album']['originator']); + $parsedFrame['album']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['album']['adjustment'], $parsedFrame['raw']['album']['signbit']); + + $info['replay_gain']['track']['peak'] = $parsedFrame['peakamplitude']; + $info['replay_gain']['track']['originator'] = $parsedFrame['track']['originator']; + $info['replay_gain']['track']['adjustment'] = $parsedFrame['track']['adjustment']; + $info['replay_gain']['album']['originator'] = $parsedFrame['album']['originator']; + $info['replay_gain']['album']['adjustment'] = $parsedFrame['album']['adjustment']; + + unset($parsedFrame['data']); + + } + + return true; + } + + + public function DeUnsynchronise($data) { + return str_replace("\xFF\x00", "\xFF", $data); + } + + public function LookupExtendedHeaderRestrictionsTagSizeLimits($index) { + static $LookupExtendedHeaderRestrictionsTagSizeLimits = array( + 0x00 => 'No more than 128 frames and 1 MB total tag size', + 0x01 => 'No more than 64 frames and 128 KB total tag size', + 0x02 => 'No more than 32 frames and 40 KB total tag size', + 0x03 => 'No more than 32 frames and 4 KB total tag size', + ); + return (isset($LookupExtendedHeaderRestrictionsTagSizeLimits[$index]) ? $LookupExtendedHeaderRestrictionsTagSizeLimits[$index] : ''); + } + + public function LookupExtendedHeaderRestrictionsTextEncodings($index) { + static $LookupExtendedHeaderRestrictionsTextEncodings = array( + 0x00 => 'No restrictions', + 0x01 => 'Strings are only encoded with ISO-8859-1 or UTF-8', + ); + return (isset($LookupExtendedHeaderRestrictionsTextEncodings[$index]) ? $LookupExtendedHeaderRestrictionsTextEncodings[$index] : ''); + } + + public function LookupExtendedHeaderRestrictionsTextFieldSize($index) { + static $LookupExtendedHeaderRestrictionsTextFieldSize = array( + 0x00 => 'No restrictions', + 0x01 => 'No string is longer than 1024 characters', + 0x02 => 'No string is longer than 128 characters', + 0x03 => 'No string is longer than 30 characters', + ); + return (isset($LookupExtendedHeaderRestrictionsTextFieldSize[$index]) ? $LookupExtendedHeaderRestrictionsTextFieldSize[$index] : ''); + } + + public function LookupExtendedHeaderRestrictionsImageEncoding($index) { + static $LookupExtendedHeaderRestrictionsImageEncoding = array( + 0x00 => 'No restrictions', + 0x01 => 'Images are encoded only with PNG or JPEG', + ); + return (isset($LookupExtendedHeaderRestrictionsImageEncoding[$index]) ? $LookupExtendedHeaderRestrictionsImageEncoding[$index] : ''); + } + + public function LookupExtendedHeaderRestrictionsImageSizeSize($index) { + static $LookupExtendedHeaderRestrictionsImageSizeSize = array( + 0x00 => 'No restrictions', + 0x01 => 'All images are 256x256 pixels or smaller', + 0x02 => 'All images are 64x64 pixels or smaller', + 0x03 => 'All images are exactly 64x64 pixels, unless required otherwise', + ); + return (isset($LookupExtendedHeaderRestrictionsImageSizeSize[$index]) ? $LookupExtendedHeaderRestrictionsImageSizeSize[$index] : ''); + } + + public function LookupCurrencyUnits($currencyid) { + + $begin = __LINE__; + + /** This is not a comment! + + + AED Dirhams + AFA Afghanis + ALL Leke + AMD Drams + ANG Guilders + AOA Kwanza + ARS Pesos + ATS Schillings + AUD Dollars + AWG Guilders + AZM Manats + BAM Convertible Marka + BBD Dollars + BDT Taka + BEF Francs + BGL Leva + BHD Dinars + BIF Francs + BMD Dollars + BND Dollars + BOB Bolivianos + BRL Brazil Real + BSD Dollars + BTN Ngultrum + BWP Pulas + BYR Rubles + BZD Dollars + CAD Dollars + CDF Congolese Francs + CHF Francs + CLP Pesos + CNY Yuan Renminbi + COP Pesos + CRC Colones + CUP Pesos + CVE Escudos + CYP Pounds + CZK Koruny + DEM Deutsche Marks + DJF Francs + DKK Kroner + DOP Pesos + DZD Algeria Dinars + EEK Krooni + EGP Pounds + ERN Nakfa + ESP Pesetas + ETB Birr + EUR Euro + FIM Markkaa + FJD Dollars + FKP Pounds + FRF Francs + GBP Pounds + GEL Lari + GGP Pounds + GHC Cedis + GIP Pounds + GMD Dalasi + GNF Francs + GRD Drachmae + GTQ Quetzales + GYD Dollars + HKD Dollars + HNL Lempiras + HRK Kuna + HTG Gourdes + HUF Forints + IDR Rupiahs + IEP Pounds + ILS New Shekels + IMP Pounds + INR Rupees + IQD Dinars + IRR Rials + ISK Kronur + ITL Lire + JEP Pounds + JMD Dollars + JOD Dinars + JPY Yen + KES Shillings + KGS Soms + KHR Riels + KMF Francs + KPW Won + KWD Dinars + KYD Dollars + KZT Tenge + LAK Kips + LBP Pounds + LKR Rupees + LRD Dollars + LSL Maloti + LTL Litai + LUF Francs + LVL Lati + LYD Dinars + MAD Dirhams + MDL Lei + MGF Malagasy Francs + MKD Denars + MMK Kyats + MNT Tugriks + MOP Patacas + MRO Ouguiyas + MTL Liri + MUR Rupees + MVR Rufiyaa + MWK Kwachas + MXN Pesos + MYR Ringgits + MZM Meticais + NAD Dollars + NGN Nairas + NIO Gold Cordobas + NLG Guilders + NOK Krone + NPR Nepal Rupees + NZD Dollars + OMR Rials + PAB Balboa + PEN Nuevos Soles + PGK Kina + PHP Pesos + PKR Rupees + PLN Zlotych + PTE Escudos + PYG Guarani + QAR Rials + ROL Lei + RUR Rubles + RWF Rwanda Francs + SAR Riyals + SBD Dollars + SCR Rupees + SDD Dinars + SEK Kronor + SGD Dollars + SHP Pounds + SIT Tolars + SKK Koruny + SLL Leones + SOS Shillings + SPL Luigini + SRG Guilders + STD Dobras + SVC Colones + SYP Pounds + SZL Emalangeni + THB Baht + TJR Rubles + TMM Manats + TND Dinars + TOP Pa'anga + TRL Liras + TTD Dollars + TVD Tuvalu Dollars + TWD New Dollars + TZS Shillings + UAH Hryvnia + UGX Shillings + USD Dollars + UYU Pesos + UZS Sums + VAL Lire + VEB Bolivares + VND Dong + VUV Vatu + WST Tala + XAF Francs + XAG Ounces + XAU Ounces + XCD Dollars + XDR Special Drawing Rights + XPD Ounces + XPF Francs + XPT Ounces + YER Rials + YUM New Dinars + ZAR Rand + ZMK Kwacha + ZWD Zimbabwe Dollars + + */ + + return getid3_lib::EmbeddedLookup($currencyid, $begin, __LINE__, __FILE__, 'id3v2-currency-units'); + } + + + public function LookupCurrencyCountry($currencyid) { + + $begin = __LINE__; + + /** This is not a comment! + + AED United Arab Emirates + AFA Afghanistan + ALL Albania + AMD Armenia + ANG Netherlands Antilles + AOA Angola + ARS Argentina + ATS Austria + AUD Australia + AWG Aruba + AZM Azerbaijan + BAM Bosnia and Herzegovina + BBD Barbados + BDT Bangladesh + BEF Belgium + BGL Bulgaria + BHD Bahrain + BIF Burundi + BMD Bermuda + BND Brunei Darussalam + BOB Bolivia + BRL Brazil + BSD Bahamas + BTN Bhutan + BWP Botswana + BYR Belarus + BZD Belize + CAD Canada + CDF Congo/Kinshasa + CHF Switzerland + CLP Chile + CNY China + COP Colombia + CRC Costa Rica + CUP Cuba + CVE Cape Verde + CYP Cyprus + CZK Czech Republic + DEM Germany + DJF Djibouti + DKK Denmark + DOP Dominican Republic + DZD Algeria + EEK Estonia + EGP Egypt + ERN Eritrea + ESP Spain + ETB Ethiopia + EUR Euro Member Countries + FIM Finland + FJD Fiji + FKP Falkland Islands (Malvinas) + FRF France + GBP United Kingdom + GEL Georgia + GGP Guernsey + GHC Ghana + GIP Gibraltar + GMD Gambia + GNF Guinea + GRD Greece + GTQ Guatemala + GYD Guyana + HKD Hong Kong + HNL Honduras + HRK Croatia + HTG Haiti + HUF Hungary + IDR Indonesia + IEP Ireland (Eire) + ILS Israel + IMP Isle of Man + INR India + IQD Iraq + IRR Iran + ISK Iceland + ITL Italy + JEP Jersey + JMD Jamaica + JOD Jordan + JPY Japan + KES Kenya + KGS Kyrgyzstan + KHR Cambodia + KMF Comoros + KPW Korea + KWD Kuwait + KYD Cayman Islands + KZT Kazakstan + LAK Laos + LBP Lebanon + LKR Sri Lanka + LRD Liberia + LSL Lesotho + LTL Lithuania + LUF Luxembourg + LVL Latvia + LYD Libya + MAD Morocco + MDL Moldova + MGF Madagascar + MKD Macedonia + MMK Myanmar (Burma) + MNT Mongolia + MOP Macau + MRO Mauritania + MTL Malta + MUR Mauritius + MVR Maldives (Maldive Islands) + MWK Malawi + MXN Mexico + MYR Malaysia + MZM Mozambique + NAD Namibia + NGN Nigeria + NIO Nicaragua + NLG Netherlands (Holland) + NOK Norway + NPR Nepal + NZD New Zealand + OMR Oman + PAB Panama + PEN Peru + PGK Papua New Guinea + PHP Philippines + PKR Pakistan + PLN Poland + PTE Portugal + PYG Paraguay + QAR Qatar + ROL Romania + RUR Russia + RWF Rwanda + SAR Saudi Arabia + SBD Solomon Islands + SCR Seychelles + SDD Sudan + SEK Sweden + SGD Singapore + SHP Saint Helena + SIT Slovenia + SKK Slovakia + SLL Sierra Leone + SOS Somalia + SPL Seborga + SRG Suriname + STD São Tome and Principe + SVC El Salvador + SYP Syria + SZL Swaziland + THB Thailand + TJR Tajikistan + TMM Turkmenistan + TND Tunisia + TOP Tonga + TRL Turkey + TTD Trinidad and Tobago + TVD Tuvalu + TWD Taiwan + TZS Tanzania + UAH Ukraine + UGX Uganda + USD United States of America + UYU Uruguay + UZS Uzbekistan + VAL Vatican City + VEB Venezuela + VND Viet Nam + VUV Vanuatu + WST Samoa + XAF Communauté Financière Africaine + XAG Silver + XAU Gold + XCD East Caribbean + XDR International Monetary Fund + XPD Palladium + XPF Comptoirs Français du Pacifique + XPT Platinum + YER Yemen + YUM Yugoslavia + ZAR South Africa + ZMK Zambia + ZWD Zimbabwe + + */ + + return getid3_lib::EmbeddedLookup($currencyid, $begin, __LINE__, __FILE__, 'id3v2-currency-country'); + } + + + + public static function LanguageLookup($languagecode, $casesensitive=false) { + + if (!$casesensitive) { + $languagecode = strtolower($languagecode); + } + + // http://www.id3.org/id3v2.4.0-structure.txt + // [4. ID3v2 frame overview] + // The three byte language field, present in several frames, is used to + // describe the language of the frame's content, according to ISO-639-2 + // [ISO-639-2]. The language should be represented in lower case. If the + // language is not known the string "XXX" should be used. + + + // ISO 639-2 - http://www.id3.org/iso639-2.html + + $begin = __LINE__; + + /** This is not a comment! + + XXX unknown + xxx unknown + aar Afar + abk Abkhazian + ace Achinese + ach Acoli + ada Adangme + afa Afro-Asiatic (Other) + afh Afrihili + afr Afrikaans + aka Akan + akk Akkadian + alb Albanian + ale Aleut + alg Algonquian Languages + amh Amharic + ang English, Old (ca. 450-1100) + apa Apache Languages + ara Arabic + arc Aramaic + arm Armenian + arn Araucanian + arp Arapaho + art Artificial (Other) + arw Arawak + asm Assamese + ath Athapascan Languages + ava Avaric + ave Avestan + awa Awadhi + aym Aymara + aze Azerbaijani + bad Banda + bai Bamileke Languages + bak Bashkir + bal Baluchi + bam Bambara + ban Balinese + baq Basque + bas Basa + bat Baltic (Other) + bej Beja + bel Byelorussian + bem Bemba + ben Bengali + ber Berber (Other) + bho Bhojpuri + bih Bihari + bik Bikol + bin Bini + bis Bislama + bla Siksika + bnt Bantu (Other) + bod Tibetan + bra Braj + bre Breton + bua Buriat + bug Buginese + bul Bulgarian + bur Burmese + cad Caddo + cai Central American Indian (Other) + car Carib + cat Catalan + cau Caucasian (Other) + ceb Cebuano + cel Celtic (Other) + ces Czech + cha Chamorro + chb Chibcha + che Chechen + chg Chagatai + chi Chinese + chm Mari + chn Chinook jargon + cho Choctaw + chr Cherokee + chu Church Slavic + chv Chuvash + chy Cheyenne + cop Coptic + cor Cornish + cos Corsican + cpe Creoles and Pidgins, English-based (Other) + cpf Creoles and Pidgins, French-based (Other) + cpp Creoles and Pidgins, Portuguese-based (Other) + cre Cree + crp Creoles and Pidgins (Other) + cus Cushitic (Other) + cym Welsh + cze Czech + dak Dakota + dan Danish + del Delaware + deu German + din Dinka + div Divehi + doi Dogri + dra Dravidian (Other) + dua Duala + dum Dutch, Middle (ca. 1050-1350) + dut Dutch + dyu Dyula + dzo Dzongkha + efi Efik + egy Egyptian (Ancient) + eka Ekajuk + ell Greek, Modern (1453-) + elx Elamite + eng English + enm English, Middle (ca. 1100-1500) + epo Esperanto + esk Eskimo (Other) + esl Spanish + est Estonian + eus Basque + ewe Ewe + ewo Ewondo + fan Fang + fao Faroese + fas Persian + fat Fanti + fij Fijian + fin Finnish + fiu Finno-Ugrian (Other) + fon Fon + fra French + fre French + frm French, Middle (ca. 1400-1600) + fro French, Old (842- ca. 1400) + fry Frisian + ful Fulah + gaa Ga + gae Gaelic (Scots) + gai Irish + gay Gayo + gdh Gaelic (Scots) + gem Germanic (Other) + geo Georgian + ger German + gez Geez + gil Gilbertese + glg Gallegan + gmh German, Middle High (ca. 1050-1500) + goh German, Old High (ca. 750-1050) + gon Gondi + got Gothic + grb Grebo + grc Greek, Ancient (to 1453) + gre Greek, Modern (1453-) + grn Guarani + guj Gujarati + hai Haida + hau Hausa + haw Hawaiian + heb Hebrew + her Herero + hil Hiligaynon + him Himachali + hin Hindi + hmo Hiri Motu + hun Hungarian + hup Hupa + hye Armenian + iba Iban + ibo Igbo + ice Icelandic + ijo Ijo + iku Inuktitut + ilo Iloko + ina Interlingua (International Auxiliary language Association) + inc Indic (Other) + ind Indonesian + ine Indo-European (Other) + ine Interlingue + ipk Inupiak + ira Iranian (Other) + iri Irish + iro Iroquoian uages + isl Icelandic + ita Italian + jav Javanese + jaw Javanese + jpn Japanese + jpr Judeo-Persian + jrb Judeo-Arabic + kaa Kara-Kalpak + kab Kabyle + kac Kachin + kal Greenlandic + kam Kamba + kan Kannada + kar Karen + kas Kashmiri + kat Georgian + kau Kanuri + kaw Kawi + kaz Kazakh + kha Khasi + khi Khoisan (Other) + khm Khmer + kho Khotanese + kik Kikuyu + kin Kinyarwanda + kir Kirghiz + kok Konkani + kom Komi + kon Kongo + kor Korean + kpe Kpelle + kro Kru + kru Kurukh + kua Kuanyama + kum Kumyk + kur Kurdish + kus Kusaie + kut Kutenai + lad Ladino + lah Lahnda + lam Lamba + lao Lao + lat Latin + lav Latvian + lez Lezghian + lin Lingala + lit Lithuanian + lol Mongo + loz Lozi + ltz Letzeburgesch + lub Luba-Katanga + lug Ganda + lui Luiseno + lun Lunda + luo Luo (Kenya and Tanzania) + mac Macedonian + mad Madurese + mag Magahi + mah Marshall + mai Maithili + mak Macedonian + mak Makasar + mal Malayalam + man Mandingo + mao Maori + map Austronesian (Other) + mar Marathi + mas Masai + max Manx + may Malay + men Mende + mga Irish, Middle (900 - 1200) + mic Micmac + min Minangkabau + mis Miscellaneous (Other) + mkh Mon-Kmer (Other) + mlg Malagasy + mlt Maltese + mni Manipuri + mno Manobo Languages + moh Mohawk + mol Moldavian + mon Mongolian + mos Mossi + mri Maori + msa Malay + mul Multiple Languages + mun Munda Languages + mus Creek + mwr Marwari + mya Burmese + myn Mayan Languages + nah Aztec + nai North American Indian (Other) + nau Nauru + nav Navajo + nbl Ndebele, South + nde Ndebele, North + ndo Ndongo + nep Nepali + new Newari + nic Niger-Kordofanian (Other) + niu Niuean + nla Dutch + nno Norwegian (Nynorsk) + non Norse, Old + nor Norwegian + nso Sotho, Northern + nub Nubian Languages + nya Nyanja + nym Nyamwezi + nyn Nyankole + nyo Nyoro + nzi Nzima + oci Langue d'Oc (post 1500) + oji Ojibwa + ori Oriya + orm Oromo + osa Osage + oss Ossetic + ota Turkish, Ottoman (1500 - 1928) + oto Otomian Languages + paa Papuan-Australian (Other) + pag Pangasinan + pal Pahlavi + pam Pampanga + pan Panjabi + pap Papiamento + pau Palauan + peo Persian, Old (ca 600 - 400 B.C.) + per Persian + phn Phoenician + pli Pali + pol Polish + pon Ponape + por Portuguese + pra Prakrit uages + pro Provencal, Old (to 1500) + pus Pushto + que Quechua + raj Rajasthani + rar Rarotongan + roa Romance (Other) + roh Rhaeto-Romance + rom Romany + ron Romanian + rum Romanian + run Rundi + rus Russian + sad Sandawe + sag Sango + sah Yakut + sai South American Indian (Other) + sal Salishan Languages + sam Samaritan Aramaic + san Sanskrit + sco Scots + scr Serbo-Croatian + sel Selkup + sem Semitic (Other) + sga Irish, Old (to 900) + shn Shan + sid Sidamo + sin Singhalese + sio Siouan Languages + sit Sino-Tibetan (Other) + sla Slavic (Other) + slk Slovak + slo Slovak + slv Slovenian + smi Sami Languages + smo Samoan + sna Shona + snd Sindhi + sog Sogdian + som Somali + son Songhai + sot Sotho, Southern + spa Spanish + sqi Albanian + srd Sardinian + srr Serer + ssa Nilo-Saharan (Other) + ssw Siswant + ssw Swazi + suk Sukuma + sun Sudanese + sus Susu + sux Sumerian + sve Swedish + swa Swahili + swe Swedish + syr Syriac + tah Tahitian + tam Tamil + tat Tatar + tel Telugu + tem Timne + ter Tereno + tgk Tajik + tgl Tagalog + tha Thai + tib Tibetan + tig Tigre + tir Tigrinya + tiv Tivi + tli Tlingit + tmh Tamashek + tog Tonga (Nyasa) + ton Tonga (Tonga Islands) + tru Truk + tsi Tsimshian + tsn Tswana + tso Tsonga + tuk Turkmen + tum Tumbuka + tur Turkish + tut Altaic (Other) + twi Twi + tyv Tuvinian + uga Ugaritic + uig Uighur + ukr Ukrainian + umb Umbundu + und Undetermined + urd Urdu + uzb Uzbek + vai Vai + ven Venda + vie Vietnamese + vol Volapük + vot Votic + wak Wakashan Languages + wal Walamo + war Waray + was Washo + wel Welsh + wen Sorbian Languages + wol Wolof + xho Xhosa + yao Yao + yap Yap + yid Yiddish + yor Yoruba + zap Zapotec + zen Zenaga + zha Zhuang + zho Chinese + zul Zulu + zun Zuni + + */ + + return getid3_lib::EmbeddedLookup($languagecode, $begin, __LINE__, __FILE__, 'id3v2-languagecode'); + } + + + public static function ETCOEventLookup($index) { + if (($index >= 0x17) && ($index <= 0xDF)) { + return 'reserved for future use'; + } + if (($index >= 0xE0) && ($index <= 0xEF)) { + return 'not predefined synch 0-F'; + } + if (($index >= 0xF0) && ($index <= 0xFC)) { + return 'reserved for future use'; + } + + static $EventLookup = array( + 0x00 => 'padding (has no meaning)', + 0x01 => 'end of initial silence', + 0x02 => 'intro start', + 0x03 => 'main part start', + 0x04 => 'outro start', + 0x05 => 'outro end', + 0x06 => 'verse start', + 0x07 => 'refrain start', + 0x08 => 'interlude start', + 0x09 => 'theme start', + 0x0A => 'variation start', + 0x0B => 'key change', + 0x0C => 'time change', + 0x0D => 'momentary unwanted noise (Snap, Crackle & Pop)', + 0x0E => 'sustained noise', + 0x0F => 'sustained noise end', + 0x10 => 'intro end', + 0x11 => 'main part end', + 0x12 => 'verse end', + 0x13 => 'refrain end', + 0x14 => 'theme end', + 0x15 => 'profanity', + 0x16 => 'profanity end', + 0xFD => 'audio end (start of silence)', + 0xFE => 'audio file ends', + 0xFF => 'one more byte of events follows' + ); + + return (isset($EventLookup[$index]) ? $EventLookup[$index] : ''); + } + + public static function SYTLContentTypeLookup($index) { + static $SYTLContentTypeLookup = array( + 0x00 => 'other', + 0x01 => 'lyrics', + 0x02 => 'text transcription', + 0x03 => 'movement/part name', // (e.g. 'Adagio') + 0x04 => 'events', // (e.g. 'Don Quijote enters the stage') + 0x05 => 'chord', // (e.g. 'Bb F Fsus') + 0x06 => 'trivia/\'pop up\' information', + 0x07 => 'URLs to webpages', + 0x08 => 'URLs to images' + ); + + return (isset($SYTLContentTypeLookup[$index]) ? $SYTLContentTypeLookup[$index] : ''); + } + + public static function APICPictureTypeLookup($index, $returnarray=false) { + static $APICPictureTypeLookup = array( + 0x00 => 'Other', + 0x01 => '32x32 pixels \'file icon\' (PNG only)', + 0x02 => 'Other file icon', + 0x03 => 'Cover (front)', + 0x04 => 'Cover (back)', + 0x05 => 'Leaflet page', + 0x06 => 'Media (e.g. label side of CD)', + 0x07 => 'Lead artist/lead performer/soloist', + 0x08 => 'Artist/performer', + 0x09 => 'Conductor', + 0x0A => 'Band/Orchestra', + 0x0B => 'Composer', + 0x0C => 'Lyricist/text writer', + 0x0D => 'Recording Location', + 0x0E => 'During recording', + 0x0F => 'During performance', + 0x10 => 'Movie/video screen capture', + 0x11 => 'A bright coloured fish', + 0x12 => 'Illustration', + 0x13 => 'Band/artist logotype', + 0x14 => 'Publisher/Studio logotype' + ); + if ($returnarray) { + return $APICPictureTypeLookup; + } + return (isset($APICPictureTypeLookup[$index]) ? $APICPictureTypeLookup[$index] : ''); + } + + public static function COMRReceivedAsLookup($index) { + static $COMRReceivedAsLookup = array( + 0x00 => 'Other', + 0x01 => 'Standard CD album with other songs', + 0x02 => 'Compressed audio on CD', + 0x03 => 'File over the Internet', + 0x04 => 'Stream over the Internet', + 0x05 => 'As note sheets', + 0x06 => 'As note sheets in a book with other sheets', + 0x07 => 'Music on other media', + 0x08 => 'Non-musical merchandise' + ); + + return (isset($COMRReceivedAsLookup[$index]) ? $COMRReceivedAsLookup[$index] : ''); + } + + public static function RVA2ChannelTypeLookup($index) { + static $RVA2ChannelTypeLookup = array( + 0x00 => 'Other', + 0x01 => 'Master volume', + 0x02 => 'Front right', + 0x03 => 'Front left', + 0x04 => 'Back right', + 0x05 => 'Back left', + 0x06 => 'Front centre', + 0x07 => 'Back centre', + 0x08 => 'Subwoofer' + ); + + return (isset($RVA2ChannelTypeLookup[$index]) ? $RVA2ChannelTypeLookup[$index] : ''); + } + + public static function FrameNameLongLookup($framename) { + + $begin = __LINE__; + + /** This is not a comment! + + AENC Audio encryption + APIC Attached picture + ASPI Audio seek point index + BUF Recommended buffer size + CNT Play counter + COM Comments + COMM Comments + COMR Commercial frame + CRA Audio encryption + CRM Encrypted meta frame + ENCR Encryption method registration + EQU Equalisation + EQU2 Equalisation (2) + EQUA Equalisation + ETC Event timing codes + ETCO Event timing codes + GEO General encapsulated object + GEOB General encapsulated object + GRID Group identification registration + IPL Involved people list + IPLS Involved people list + LINK Linked information + LNK Linked information + MCDI Music CD identifier + MCI Music CD Identifier + MLL MPEG location lookup table + MLLT MPEG location lookup table + OWNE Ownership frame + PCNT Play counter + PIC Attached picture + POP Popularimeter + POPM Popularimeter + POSS Position synchronisation frame + PRIV Private frame + RBUF Recommended buffer size + REV Reverb + RVA Relative volume adjustment + RVA2 Relative volume adjustment (2) + RVAD Relative volume adjustment + RVRB Reverb + SEEK Seek frame + SIGN Signature frame + SLT Synchronised lyric/text + STC Synced tempo codes + SYLT Synchronised lyric/text + SYTC Synchronised tempo codes + TAL Album/Movie/Show title + TALB Album/Movie/Show title + TBP BPM (Beats Per Minute) + TBPM BPM (beats per minute) + TCM Composer + TCMP Part of a compilation + TCO Content type + TCOM Composer + TCON Content type + TCOP Copyright message + TCP Part of a compilation + TCR Copyright message + TDA Date + TDAT Date + TDEN Encoding time + TDLY Playlist delay + TDOR Original release time + TDRC Recording time + TDRL Release time + TDTG Tagging time + TDY Playlist delay + TEN Encoded by + TENC Encoded by + TEXT Lyricist/Text writer + TFLT File type + TFT File type + TIM Time + TIME Time + TIPL Involved people list + TIT1 Content group description + TIT2 Title/songname/content description + TIT3 Subtitle/Description refinement + TKE Initial key + TKEY Initial key + TLA Language(s) + TLAN Language(s) + TLE Length + TLEN Length + TMCL Musician credits list + TMED Media type + TMOO Mood + TMT Media type + TOA Original artist(s)/performer(s) + TOAL Original album/movie/show title + TOF Original filename + TOFN Original filename + TOL Original Lyricist(s)/text writer(s) + TOLY Original lyricist(s)/text writer(s) + TOPE Original artist(s)/performer(s) + TOR Original release year + TORY Original release year + TOT Original album/Movie/Show title + TOWN File owner/licensee + TP1 Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group + TP2 Band/Orchestra/Accompaniment + TP3 Conductor/Performer refinement + TP4 Interpreted, remixed, or otherwise modified by + TPA Part of a set + TPB Publisher + TPE1 Lead performer(s)/Soloist(s) + TPE2 Band/orchestra/accompaniment + TPE3 Conductor/performer refinement + TPE4 Interpreted, remixed, or otherwise modified by + TPOS Part of a set + TPRO Produced notice + TPUB Publisher + TRC ISRC (International Standard Recording Code) + TRCK Track number/Position in set + TRD Recording dates + TRDA Recording dates + TRK Track number/Position in set + TRSN Internet radio station name + TRSO Internet radio station owner + TS2 Album-Artist sort order + TSA Album sort order + TSC Composer sort order + TSI Size + TSIZ Size + TSO2 Album-Artist sort order + TSOA Album sort order + TSOC Composer sort order + TSOP Performer sort order + TSOT Title sort order + TSP Performer sort order + TSRC ISRC (international standard recording code) + TSS Software/hardware and settings used for encoding + TSSE Software/Hardware and settings used for encoding + TSST Set subtitle + TST Title sort order + TT1 Content group description + TT2 Title/Songname/Content description + TT3 Subtitle/Description refinement + TXT Lyricist/text writer + TXX User defined text information frame + TXXX User defined text information frame + TYE Year + TYER Year + UFI Unique file identifier + UFID Unique file identifier + ULT Unsychronised lyric/text transcription + USER Terms of use + USLT Unsynchronised lyric/text transcription + WAF Official audio file webpage + WAR Official artist/performer webpage + WAS Official audio source webpage + WCM Commercial information + WCOM Commercial information + WCOP Copyright/Legal information + WCP Copyright/Legal information + WOAF Official audio file webpage + WOAR Official artist/performer webpage + WOAS Official audio source webpage + WORS Official Internet radio station homepage + WPAY Payment + WPB Publishers official webpage + WPUB Publishers official webpage + WXX User defined URL link frame + WXXX User defined URL link frame + TFEA Featured Artist + TSTU Recording Studio + rgad Replay Gain Adjustment + + */ + + return getid3_lib::EmbeddedLookup($framename, $begin, __LINE__, __FILE__, 'id3v2-framename_long'); + + // Last three: + // from Helium2 [www.helium2.com] + // from http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html + } + + + public static function FrameNameShortLookup($framename) { + + $begin = __LINE__; + + /** This is not a comment! + + AENC audio_encryption + APIC attached_picture + ASPI audio_seek_point_index + BUF recommended_buffer_size + CNT play_counter + COM comment + COMM comment + COMR commercial_frame + CRA audio_encryption + CRM encrypted_meta_frame + ENCR encryption_method_registration + EQU equalisation + EQU2 equalisation + EQUA equalisation + ETC event_timing_codes + ETCO event_timing_codes + GEO general_encapsulated_object + GEOB general_encapsulated_object + GRID group_identification_registration + IPL involved_people_list + IPLS involved_people_list + LINK linked_information + LNK linked_information + MCDI music_cd_identifier + MCI music_cd_identifier + MLL mpeg_location_lookup_table + MLLT mpeg_location_lookup_table + OWNE ownership_frame + PCNT play_counter + PIC attached_picture + POP popularimeter + POPM popularimeter + POSS position_synchronisation_frame + PRIV private_frame + RBUF recommended_buffer_size + REV reverb + RVA relative_volume_adjustment + RVA2 relative_volume_adjustment + RVAD relative_volume_adjustment + RVRB reverb + SEEK seek_frame + SIGN signature_frame + SLT synchronised_lyric + STC synced_tempo_codes + SYLT synchronised_lyric + SYTC synchronised_tempo_codes + TAL album + TALB album + TBP bpm + TBPM bpm + TCM composer + TCMP part_of_a_compilation + TCO genre + TCOM composer + TCON genre + TCOP copyright_message + TCP part_of_a_compilation + TCR copyright_message + TDA date + TDAT date + TDEN encoding_time + TDLY playlist_delay + TDOR original_release_time + TDRC recording_time + TDRL release_time + TDTG tagging_time + TDY playlist_delay + TEN encoded_by + TENC encoded_by + TEXT lyricist + TFLT file_type + TFT file_type + TIM time + TIME time + TIPL involved_people_list + TIT1 content_group_description + TIT2 title + TIT3 subtitle + TKE initial_key + TKEY initial_key + TLA language + TLAN language + TLE length + TLEN length + TMCL musician_credits_list + TMED media_type + TMOO mood + TMT media_type + TOA original_artist + TOAL original_album + TOF original_filename + TOFN original_filename + TOL original_lyricist + TOLY original_lyricist + TOPE original_artist + TOR original_year + TORY original_year + TOT original_album + TOWN file_owner + TP1 artist + TP2 band + TP3 conductor + TP4 remixer + TPA part_of_a_set + TPB publisher + TPE1 artist + TPE2 band + TPE3 conductor + TPE4 remixer + TPOS part_of_a_set + TPRO produced_notice + TPUB publisher + TRC isrc + TRCK track_number + TRD recording_dates + TRDA recording_dates + TRK track_number + TRSN internet_radio_station_name + TRSO internet_radio_station_owner + TS2 album_artist_sort_order + TSA album_sort_order + TSC composer_sort_order + TSI size + TSIZ size + TSO2 album_artist_sort_order + TSOA album_sort_order + TSOC composer_sort_order + TSOP performer_sort_order + TSOT title_sort_order + TSP performer_sort_order + TSRC isrc + TSS encoder_settings + TSSE encoder_settings + TSST set_subtitle + TST title_sort_order + TT1 content_group_description + TT2 title + TT3 subtitle + TXT lyricist + TXX text + TXXX text + TYE year + TYER year + UFI unique_file_identifier + UFID unique_file_identifier + ULT unsychronised_lyric + USER terms_of_use + USLT unsynchronised_lyric + WAF url_file + WAR url_artist + WAS url_source + WCM commercial_information + WCOM commercial_information + WCOP copyright + WCP copyright + WOAF url_file + WOAR url_artist + WOAS url_source + WORS url_station + WPAY url_payment + WPB url_publisher + WPUB url_publisher + WXX url_user + WXXX url_user + TFEA featured_artist + TSTU recording_studio + rgad replay_gain_adjustment + + */ + + return getid3_lib::EmbeddedLookup($framename, $begin, __LINE__, __FILE__, 'id3v2-framename_short'); + } + + public static function TextEncodingTerminatorLookup($encoding) { + // http://www.id3.org/id3v2.4.0-structure.txt + // Frames that allow different types of text encoding contains a text encoding description byte. Possible encodings: + static $TextEncodingTerminatorLookup = array( + 0 => "\x00", // $00 ISO-8859-1. Terminated with $00. + 1 => "\x00\x00", // $01 UTF-16 encoded Unicode with BOM. All strings in the same frame SHALL have the same byteorder. Terminated with $00 00. + 2 => "\x00\x00", // $02 UTF-16BE encoded Unicode without BOM. Terminated with $00 00. + 3 => "\x00", // $03 UTF-8 encoded Unicode. Terminated with $00. + 255 => "\x00\x00" + ); + return (isset($TextEncodingTerminatorLookup[$encoding]) ? $TextEncodingTerminatorLookup[$encoding] : ''); + } + + public static function TextEncodingNameLookup($encoding) { + // http://www.id3.org/id3v2.4.0-structure.txt + // Frames that allow different types of text encoding contains a text encoding description byte. Possible encodings: + static $TextEncodingNameLookup = array( + 0 => 'ISO-8859-1', // $00 ISO-8859-1. Terminated with $00. + 1 => 'UTF-16', // $01 UTF-16 encoded Unicode with BOM. All strings in the same frame SHALL have the same byteorder. Terminated with $00 00. + 2 => 'UTF-16BE', // $02 UTF-16BE encoded Unicode without BOM. Terminated with $00 00. + 3 => 'UTF-8', // $03 UTF-8 encoded Unicode. Terminated with $00. + 255 => 'UTF-16BE' + ); + return (isset($TextEncodingNameLookup[$encoding]) ? $TextEncodingNameLookup[$encoding] : 'ISO-8859-1'); + } + + public static function IsValidID3v2FrameName($framename, $id3v2majorversion) { + switch ($id3v2majorversion) { + case 2: + return preg_match('#[A-Z][A-Z0-9]{2}#', $framename); + break; + + case 3: + case 4: + return preg_match('#[A-Z][A-Z0-9]{3}#', $framename); + break; + } + return false; + } + + public static function IsANumber($numberstring, $allowdecimal=false, $allownegative=false) { + for ($i = 0; $i < strlen($numberstring); $i++) { + if ((chr($numberstring{$i}) < chr('0')) || (chr($numberstring{$i}) > chr('9'))) { + if (($numberstring{$i} == '.') && $allowdecimal) { + // allowed + } elseif (($numberstring{$i} == '-') && $allownegative && ($i == 0)) { + // allowed + } else { + return false; + } + } + } + return true; + } + + public static function IsValidDateStampString($datestamp) { + if (strlen($datestamp) != 8) { + return false; + } + if (!self::IsANumber($datestamp, false)) { + return false; + } + $year = substr($datestamp, 0, 4); + $month = substr($datestamp, 4, 2); + $day = substr($datestamp, 6, 2); + if (($year == 0) || ($month == 0) || ($day == 0)) { + return false; + } + if ($month > 12) { + return false; + } + if ($day > 31) { + return false; + } + if (($day > 30) && (($month == 4) || ($month == 6) || ($month == 9) || ($month == 11))) { + return false; + } + if (($day > 29) && ($month == 2)) { + return false; + } + return true; + } + + public static function ID3v2HeaderLength($majorversion) { + return (($majorversion == 2) ? 6 : 10); + } + +} diff --git a/wp-includes/ID3/module.tag.lyrics3.php b/wp-includes/ID3/module.tag.lyrics3.php new file mode 100644 index 0000000..7891603 --- /dev/null +++ b/wp-includes/ID3/module.tag.lyrics3.php @@ -0,0 +1,294 @@ + // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +// also https://github.com/JamesHeinrich/getID3 // +///////////////////////////////////////////////////////////////// +// See readme.txt for more details // +///////////////////////////////////////////////////////////////// +/// // +// module.tag.lyrics3.php // +// module for analyzing Lyrics3 tags // +// dependencies: module.tag.apetag.php (optional) // +// /// +///////////////////////////////////////////////////////////////// + + +class getid3_lyrics3 extends getid3_handler +{ + + public function Analyze() { + $info = &$this->getid3->info; + + // http://www.volweb.cz/str/tags.htm + + if (!getid3_lib::intValueSupported($info['filesize'])) { + $info['warning'][] = 'Unable to check for Lyrics3 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB'; + return false; + } + + $this->fseek((0 - 128 - 9 - 6), SEEK_END); // end - ID3v1 - "LYRICSEND" - [Lyrics3size] + $lyrics3_id3v1 = $this->fread(128 + 9 + 6); + $lyrics3lsz = substr($lyrics3_id3v1, 0, 6); // Lyrics3size + $lyrics3end = substr($lyrics3_id3v1, 6, 9); // LYRICSEND or LYRICS200 + $id3v1tag = substr($lyrics3_id3v1, 15, 128); // ID3v1 + + if ($lyrics3end == 'LYRICSEND') { + // Lyrics3v1, ID3v1, no APE + + $lyrics3size = 5100; + $lyrics3offset = $info['filesize'] - 128 - $lyrics3size; + $lyrics3version = 1; + + } elseif ($lyrics3end == 'LYRICS200') { + // Lyrics3v2, ID3v1, no APE + + // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200' + $lyrics3size = $lyrics3lsz + 6 + strlen('LYRICS200'); + $lyrics3offset = $info['filesize'] - 128 - $lyrics3size; + $lyrics3version = 2; + + } elseif (substr(strrev($lyrics3_id3v1), 0, 9) == strrev('LYRICSEND')) { + // Lyrics3v1, no ID3v1, no APE + + $lyrics3size = 5100; + $lyrics3offset = $info['filesize'] - $lyrics3size; + $lyrics3version = 1; + $lyrics3offset = $info['filesize'] - $lyrics3size; + + } elseif (substr(strrev($lyrics3_id3v1), 0, 9) == strrev('LYRICS200')) { + + // Lyrics3v2, no ID3v1, no APE + + $lyrics3size = strrev(substr(strrev($lyrics3_id3v1), 9, 6)) + 6 + strlen('LYRICS200'); // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200' + $lyrics3offset = $info['filesize'] - $lyrics3size; + $lyrics3version = 2; + + } else { + + if (isset($info['ape']['tag_offset_start']) && ($info['ape']['tag_offset_start'] > 15)) { + + $this->fseek($info['ape']['tag_offset_start'] - 15); + $lyrics3lsz = $this->fread(6); + $lyrics3end = $this->fread(9); + + if ($lyrics3end == 'LYRICSEND') { + // Lyrics3v1, APE, maybe ID3v1 + + $lyrics3size = 5100; + $lyrics3offset = $info['ape']['tag_offset_start'] - $lyrics3size; + $info['avdataend'] = $lyrics3offset; + $lyrics3version = 1; + $info['warning'][] = 'APE tag located after Lyrics3, will probably break Lyrics3 compatability'; + + } elseif ($lyrics3end == 'LYRICS200') { + // Lyrics3v2, APE, maybe ID3v1 + + $lyrics3size = $lyrics3lsz + 6 + strlen('LYRICS200'); // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200' + $lyrics3offset = $info['ape']['tag_offset_start'] - $lyrics3size; + $lyrics3version = 2; + $info['warning'][] = 'APE tag located after Lyrics3, will probably break Lyrics3 compatability'; + + } + + } + + } + + if (isset($lyrics3offset)) { + $info['avdataend'] = $lyrics3offset; + $this->getLyrics3Data($lyrics3offset, $lyrics3version, $lyrics3size); + + if (!isset($info['ape'])) { + $GETID3_ERRORARRAY = &$info['warning']; + getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, true); + $getid3_temp = new getID3(); + $getid3_temp->openfile($this->getid3->filename); + $getid3_apetag = new getid3_apetag($getid3_temp); + $getid3_apetag->overrideendoffset = $info['lyrics3']['tag_offset_start']; + $getid3_apetag->Analyze(); + if (!empty($getid3_temp->info['ape'])) { + $info['ape'] = $getid3_temp->info['ape']; + } + if (!empty($getid3_temp->info['replay_gain'])) { + $info['replay_gain'] = $getid3_temp->info['replay_gain']; + } + unset($getid3_temp, $getid3_apetag); + } + + } + + return true; + } + + public function getLyrics3Data($endoffset, $version, $length) { + // http://www.volweb.cz/str/tags.htm + + $info = &$this->getid3->info; + + if (!getid3_lib::intValueSupported($endoffset)) { + $info['warning'][] = 'Unable to check for Lyrics3 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB'; + return false; + } + + $this->fseek($endoffset); + if ($length <= 0) { + return false; + } + $rawdata = $this->fread($length); + + $ParsedLyrics3['raw']['lyrics3version'] = $version; + $ParsedLyrics3['raw']['lyrics3tagsize'] = $length; + $ParsedLyrics3['tag_offset_start'] = $endoffset; + $ParsedLyrics3['tag_offset_end'] = $endoffset + $length - 1; + + if (substr($rawdata, 0, 11) != 'LYRICSBEGIN') { + if (strpos($rawdata, 'LYRICSBEGIN') !== false) { + + $info['warning'][] = '"LYRICSBEGIN" expected at '.$endoffset.' but actually found at '.($endoffset + strpos($rawdata, 'LYRICSBEGIN')).' - this is invalid for Lyrics3 v'.$version; + $info['avdataend'] = $endoffset + strpos($rawdata, 'LYRICSBEGIN'); + $rawdata = substr($rawdata, strpos($rawdata, 'LYRICSBEGIN')); + $length = strlen($rawdata); + $ParsedLyrics3['tag_offset_start'] = $info['avdataend']; + $ParsedLyrics3['raw']['lyrics3tagsize'] = $length; + + } else { + + $info['error'][] = '"LYRICSBEGIN" expected at '.$endoffset.' but found "'.substr($rawdata, 0, 11).'" instead'; + return false; + + } + + } + + switch ($version) { + + case 1: + if (substr($rawdata, strlen($rawdata) - 9, 9) == 'LYRICSEND') { + $ParsedLyrics3['raw']['LYR'] = trim(substr($rawdata, 11, strlen($rawdata) - 11 - 9)); + $this->Lyrics3LyricsTimestampParse($ParsedLyrics3); + } else { + $info['error'][] = '"LYRICSEND" expected at '.($this->ftell() - 11 + $length - 9).' but found "'.substr($rawdata, strlen($rawdata) - 9, 9).'" instead'; + return false; + } + break; + + case 2: + if (substr($rawdata, strlen($rawdata) - 9, 9) == 'LYRICS200') { + $ParsedLyrics3['raw']['unparsed'] = substr($rawdata, 11, strlen($rawdata) - 11 - 9 - 6); // LYRICSBEGIN + LYRICS200 + LSZ + $rawdata = $ParsedLyrics3['raw']['unparsed']; + while (strlen($rawdata) > 0) { + $fieldname = substr($rawdata, 0, 3); + $fieldsize = (int) substr($rawdata, 3, 5); + $ParsedLyrics3['raw'][$fieldname] = substr($rawdata, 8, $fieldsize); + $rawdata = substr($rawdata, 3 + 5 + $fieldsize); + } + + if (isset($ParsedLyrics3['raw']['IND'])) { + $i = 0; + $flagnames = array('lyrics', 'timestamps', 'inhibitrandom'); + foreach ($flagnames as $flagname) { + if (strlen($ParsedLyrics3['raw']['IND']) > $i++) { + $ParsedLyrics3['flags'][$flagname] = $this->IntString2Bool(substr($ParsedLyrics3['raw']['IND'], $i, 1 - 1)); + } + } + } + + $fieldnametranslation = array('ETT'=>'title', 'EAR'=>'artist', 'EAL'=>'album', 'INF'=>'comment', 'AUT'=>'author'); + foreach ($fieldnametranslation as $key => $value) { + if (isset($ParsedLyrics3['raw'][$key])) { + $ParsedLyrics3['comments'][$value][] = trim($ParsedLyrics3['raw'][$key]); + } + } + + if (isset($ParsedLyrics3['raw']['IMG'])) { + $imagestrings = explode("\r\n", $ParsedLyrics3['raw']['IMG']); + foreach ($imagestrings as $key => $imagestring) { + if (strpos($imagestring, '||') !== false) { + $imagearray = explode('||', $imagestring); + $ParsedLyrics3['images'][$key]['filename'] = (isset($imagearray[0]) ? $imagearray[0] : ''); + $ParsedLyrics3['images'][$key]['description'] = (isset($imagearray[1]) ? $imagearray[1] : ''); + $ParsedLyrics3['images'][$key]['timestamp'] = $this->Lyrics3Timestamp2Seconds(isset($imagearray[2]) ? $imagearray[2] : ''); + } + } + } + if (isset($ParsedLyrics3['raw']['LYR'])) { + $this->Lyrics3LyricsTimestampParse($ParsedLyrics3); + } + } else { + $info['error'][] = '"LYRICS200" expected at '.($this->ftell() - 11 + $length - 9).' but found "'.substr($rawdata, strlen($rawdata) - 9, 9).'" instead'; + return false; + } + break; + + default: + $info['error'][] = 'Cannot process Lyrics3 version '.$version.' (only v1 and v2)'; + return false; + break; + } + + + if (isset($info['id3v1']['tag_offset_start']) && ($info['id3v1']['tag_offset_start'] <= $ParsedLyrics3['tag_offset_end'])) { + $info['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in Lyrics3 tag data'; + unset($info['id3v1']); + foreach ($info['warning'] as $key => $value) { + if ($value == 'Some ID3v1 fields do not use NULL characters for padding') { + unset($info['warning'][$key]); + sort($info['warning']); + break; + } + } + } + + $info['lyrics3'] = $ParsedLyrics3; + + return true; + } + + public function Lyrics3Timestamp2Seconds($rawtimestamp) { + if (preg_match('#^\\[([0-9]{2}):([0-9]{2})\\]$#', $rawtimestamp, $regs)) { + return (int) (($regs[1] * 60) + $regs[2]); + } + return false; + } + + public function Lyrics3LyricsTimestampParse(&$Lyrics3data) { + $lyricsarray = explode("\r\n", $Lyrics3data['raw']['LYR']); + foreach ($lyricsarray as $key => $lyricline) { + $regs = array(); + unset($thislinetimestamps); + while (preg_match('#^(\\[[0-9]{2}:[0-9]{2}\\])#', $lyricline, $regs)) { + $thislinetimestamps[] = $this->Lyrics3Timestamp2Seconds($regs[0]); + $lyricline = str_replace($regs[0], '', $lyricline); + } + $notimestamplyricsarray[$key] = $lyricline; + if (isset($thislinetimestamps) && is_array($thislinetimestamps)) { + sort($thislinetimestamps); + foreach ($thislinetimestamps as $timestampkey => $timestamp) { + if (isset($Lyrics3data['synchedlyrics'][$timestamp])) { + // timestamps only have a 1-second resolution, it's possible that multiple lines + // could have the same timestamp, if so, append + $Lyrics3data['synchedlyrics'][$timestamp] .= "\r\n".$lyricline; + } else { + $Lyrics3data['synchedlyrics'][$timestamp] = $lyricline; + } + } + } + } + $Lyrics3data['unsynchedlyrics'] = implode("\r\n", $notimestamplyricsarray); + if (isset($Lyrics3data['synchedlyrics']) && is_array($Lyrics3data['synchedlyrics'])) { + ksort($Lyrics3data['synchedlyrics']); + } + return true; + } + + public function IntString2Bool($char) { + if ($char == '1') { + return true; + } elseif ($char == '0') { + return false; + } + return null; + } +} \ No newline at end of file diff --git a/wp-includes/ID3/readme.txt b/wp-includes/ID3/readme.txt new file mode 100644 index 0000000..b627cc2 --- /dev/null +++ b/wp-includes/ID3/readme.txt @@ -0,0 +1,604 @@ +///////////////////////////////////////////////////////////////// +/// getID3() by James Heinrich // +// available at http://getid3.sourceforge.net // +// or http://www.getid3.org // +// also https://github.com/JamesHeinrich/getID3 // +///////////////////////////////////////////////////////////////// + +***************************************************************** +***************************************************************** + + getID3() is released under multiple licenses. You may choose + from the following licenses, and use getID3 according to the + terms of the license most suitable to your project. + +GNU GPL: https://gnu.org/licenses/gpl.html (v3) + https://gnu.org/licenses/old-licenses/gpl-2.0.html (v2) + https://gnu.org/licenses/old-licenses/gpl-1.0.html (v1) + +GNU LGPL: https://gnu.org/licenses/lgpl.html (v3) + +Mozilla MPL: http://www.mozilla.org/MPL/2.0/ (v2) + +getID3 Commercial License: http://getid3.org/#gCL (payment required) + +***************************************************************** +***************************************************************** +Copies of each of the above licenses are included in the 'licenses' +directory of the getID3 distribution. + + + +---------------------------------------------+ + | If you want to donate, there is a link on | + | http://www.getid3.org for PayPal donations. | + +---------------------------------------------+ + + +Quick Start +=========================================================================== + +Q: How can I check that getID3() works on my server/files? +A: Unzip getID3() to a directory, then access /demos/demo.browse.php + + + +Support +=========================================================================== + +Q: I have a question, or I found a bug. What do I do? +A: The preferred method of support requests and/or bug reports is the + forum at http://support.getid3.org/ + + + +Sourceforge Notification +=========================================================================== + +It's highly recommended that you sign up for notification from +Sourceforge for when new versions are released. Please visit: +http://sourceforge.net/project/showfiles.php?group_id=55859 +and click the little "monitor package" icon/link. If you're +previously signed up for the mailing list, be aware that it has +been discontinued, only the automated Sourceforge notification +will be used from now on. + + + +What does getID3() do? +=========================================================================== + +Reads & parses (to varying degrees): + ¤ tags: + * APE (v1 and v2) + * ID3v1 (& ID3v1.1) + * ID3v2 (v2.4, v2.3, v2.2) + * Lyrics3 (v1 & v2) + + ¤ audio-lossy: + * MP3/MP2/MP1 + * MPC / Musepack + * Ogg (Vorbis, OggFLAC, Speex) + * AAC / MP4 + * AC3 + * DTS + * RealAudio + * Speex + * DSS + * VQF + + ¤ audio-lossless: + * AIFF + * AU + * Bonk + * CD-audio (*.cda) + * FLAC + * LA (Lossless Audio) + * LiteWave + * LPAC + * MIDI + * Monkey's Audio + * OptimFROG + * RKAU + * Shorten + * TTA + * VOC + * WAV (RIFF) + * WavPack + + ¤ audio-video: + * ASF: ASF, Windows Media Audio (WMA), Windows Media Video (WMV) + * AVI (RIFF) + * Flash + * Matroska (MKV) + * MPEG-1 / MPEG-2 + * NSV (Nullsoft Streaming Video) + * Quicktime (including MP4) + * RealVideo + + ¤ still image: + * BMP + * GIF + * JPEG + * PNG + * TIFF + * SWF (Flash) + * PhotoCD + + ¤ data: + * ISO-9660 CD-ROM image (directory structure) + * SZIP (limited support) + * ZIP (directory structure) + * TAR + * CUE + + +Writes: + * ID3v1 (& ID3v1.1) + * ID3v2 (v2.3 & v2.4) + * VorbisComment on OggVorbis + * VorbisComment on FLAC (not OggFLAC) + * APE v2 + * Lyrics3 (delete only) + + + +Requirements +=========================================================================== + +* PHP 4.2.0 up to 5.2.x for getID3() 1.7.x (and earlier) +* PHP 5.0.5 (or higher) for getID3() 1.8.x (and up) +* PHP 5.0.5 (or higher) for getID3() 2.0.x (and up) +* at least 4MB memory for PHP. 8MB or more is highly recommended. + 12MB is required with all modules loaded. + + + +Usage +=========================================================================== + +See /demos/demo.basic.php for a very basic use of getID3() with no +fancy output, just scanning one file. + +See structure.txt for the returned data structure. + +*> For an example of a complete directory-browsing, <* +*> file-scanning implementation of getID3(), please run <* +*> /demos/demo.browse.php <* + +See /demos/demo.mysql.php for a sample recursive scanning code that +scans every file in a given directory, and all sub-directories, stores +the results in a database and allows various analysis / maintenance +operations + +To analyze remote files over HTTP or FTP you need to copy the file +locally first before running getID3(). Your code would look something +like this: + +// Copy remote file locally to scan with getID3() +$remotefilename = 'http://www.example.com/filename.mp3'; +if ($fp_remote = fopen($remotefilename, 'rb')) { + $localtempfilename = tempnam('/tmp', 'getID3'); + if ($fp_local = fopen($localtempfilename, 'wb')) { + while ($buffer = fread($fp_remote, 8192)) { + fwrite($fp_local, $buffer); + } + fclose($fp_local); + + // Initialize getID3 engine + $getID3 = new getID3; + + $ThisFileInfo = $getID3->analyze($filename); + + // Delete temporary file + unlink($localtempfilename); + } + fclose($fp_remote); +} + + +See /demos/demo.write.php for how to write tags. + + + +What does the returned data structure look like? +=========================================================================== + +See structure.txt + +It is recommended that you look at the output of +/demos/demo.browse.php scanning the file(s) you're interested in to +confirm what data is actually returned for any particular filetype in +general, and your files in particular, as the actual data returned +may vary considerably depending on what information is available in +the file itself. + + + +Notes +=========================================================================== + +getID3() 1.x: +If the format parser encounters a critical problem, it will return +something in $fileinfo['error'], describing the encountered error. If +a less critical error or notice is generated it will appear in +$fileinfo['warning']. Both keys may contain more than one warning or +error. If something is returned in ['error'] then the file was not +correctly parsed and returned data may or may not be correct and/or +complete. If something is returned in ['warning'] (and not ['error']) +then the data that is returned is OK - usually getID3() is reporting +errors in the file that have been worked around due to known bugs in +other programs. Some warnings may indicate that the data that is +returned is OK but that some data could not be extracted due to +errors in the file. + +getID3() 2.x: +See above except errors are thrown (so you will only get one error). + + + +Disclaimer +=========================================================================== + +getID3() has been tested on many systems, on many types of files, +under many operating systems, and is generally believe to be stable +and safe. That being said, there is still the chance there is an +undiscovered and/or unfixed bug that may potentially corrupt your +file, especially within the writing functions. By using getID3() you +agree that it's not my fault if any of your files are corrupted. +In fact, I'm not liable for anything :) + + + +License +=========================================================================== + +GNU General Public License - see license.txt + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to: +Free Software Foundation, Inc. +59 Temple Place - Suite 330 +Boston, MA 02111-1307, USA. + +FAQ: +Q: Can I use getID3() in my program? Do I need a commercial license? +A: You're generally free to use getID3 however you see fit. The only + case in which you would require a commercial license is if you're + selling your closed-source program that integrates getID3. If you + sell your program including a copy of getID3, that's fine as long + as you include a copy of the sourcecode when you sell it. Or you + can distribute your code without getID3 and say "download it from + getid3.sourceforge.net" + + + +Why is it called "getID3()" if it does so much more than just that? +=========================================================================== + +v0.1 did in fact just do that. I don't have a copy of code that old, but I +could essentially write it today with a one-line function: + function getID3($filename) { return unpack('a3TAG/a30title/a30artist/a30album/a4year/a28comment/c1track/c1genreid', substr(file_get_contents($filename), -128)); } + + +Future Plans +=========================================================================== +http://www.getid3.org/phpBB3/viewforum.php?f=7 + +* Better support for MP4 container format +* Scan for appended ID3v2 tag at end of file per ID3v2.4 specs (Section 5.0) +* Support for JPEG-2000 (http://www.morgan-multimedia.com/jpeg2000_overview.htm) +* Support for MOD (mod/stm/s3m/it/xm/mtm/ult/669) +* Support for ACE (thanks Vince) +* Support for Ogg other than Vorbis, Speex and OggFlac (ie. Ogg+Xvid) +* Ability to create Xing/LAME VBR header for VBR MP3s that are missing VBR header +* Ability to "clean" ID3v2 padding (replace invalid padding with valid padding) +* Warn if MP3s change version mid-stream (in full-scan mode) +* check for corrupt/broken mid-file MP3 streams in histogram scan +* Support for lossless-compression formats + (http://www.firstpr.com.au/audiocomp/lossless/#Links) + (http://compression.ca/act-sound.html) + (http://web.inter.nl.net/users/hvdh/lossless/lossless.htm) +* Support for RIFF-INFO chunks + * http://lotto.st-andrews.ac.uk/~njh/tag_interchange.html + (thanks Nick Humfrey ) + * http://abcavi.narod.ru/sof/abcavi/infotags.htm + (thanks Kibi) +* Better support for Bink video +* http://www.hr/josip/DSP/AudioFile2.html +* http://www.pcisys.net/~melanson/codecs/ +* Detect mp3PRO +* Support for PSD +* Support for JPC +* Support for JP2 +* Support for JPX +* Support for JB2 +* Support for IFF +* Support for ICO +* Support for ANI +* Support for EXE (comments, author, etc) (thanks p*quaedackersØplanet*nl) +* Support for DVD-IFO (region, subtitles, aspect ratio, etc) + (thanks p*quaedackersØplanet*nl) +* More complete support for SWF - parsing encapsulated MP3 and/or JPEG content + (thanks n8n8Øyahoo*com) +* Support for a2b +* Optional scan-through-frames for AVI verification + (thanks rockcohenØmassive-interactive*nl) +* Support for TTF (thanks infoØbutterflyx*com) +* Support for DSS (http://www.getid3.org/phpBB3/viewtopic.php?t=171) +* Support for SMAF (http://smaf-yamaha.com/what/demo.html) + http://www.getid3.org/phpBB3/viewtopic.php?t=182 +* Support for AMR (http://www.getid3.org/phpBB3/viewtopic.php?t=195) +* Support for 3gpp (http://www.getid3.org/phpBB3/viewtopic.php?t=195) +* Support for ID4 (http://www.wackysoft.cjb.net grizlyY2KØhotmail*com) +* Parse XML data returned in Ogg comments +* Parse XML data from Quicktime SMIL metafiles (klausrathØmac*com) +* ID3v2 genre string creator function +* More complete parsing of JPG +* Support for all old-style ASF packets +* ASF/WMA/WMV tag writing +* Parse declared T??? ID3v2 text information frames, where appropriate + (thanks Christian Fritz for the idea) +* Recognize encoder: + http://www.guerillasoft.com/EncSpot2/index.html + http://ff123.net/identify.html + http://www.hydrogenaudio.org/?act=ST&f=16&t=9414 + http://www.hydrogenaudio.org/?showtopic=11785 +* Support for other OS/2 bitmap structures: Bitmap Array('BA'), + Color Icon('CI'), Color Pointer('CP'), Icon('IC'), Pointer ('PT') + http://netghost.narod.ru/gff/graphics/summary/os2bmp.htm +* Support for WavPack RAW mode +* ASF/WMA/WMV data packet parsing +* ID3v2FrameFlagsLookupTagAlter() +* ID3v2FrameFlagsLookupFileAlter() +* obey ID3v2 tag alter/preserve/discard rules +* http://www.geocities.com/SiliconValley/Sector/9654/Softdoc/Illyrium/Aolyr.htm +* proper checking for LINK/LNK frame validity in ID3v2 writing +* proper checking for ASPI-TLEN frame validity in ID3v2 writing +* proper checking for COMR frame validity in ID3v2 writing +* http://www.geocities.co.jp/SiliconValley-Oakland/3664/index.html +* decode GEOB ID3v2 structure as encoded by RealJukebox, + decode NCON ID3v2 structure as encoded by MusicMatch + (probably won't happen - the formats are proprietary) + + + +Known Bugs/Issues in getID3() that may be fixed eventually +=========================================================================== +http://www.getid3.org/phpBB3/viewtopic.php?t=25 + +* Cannot determine bitrate for MPEG video with VBR video data + (need documentation) +* Interlace/progressive cannot be determined for MPEG video + (need documentation) +* MIDI playtime is sometimes inaccurate +* AAC-RAW mode files cannot be identified +* WavPack-RAW mode files cannot be identified +* mp4 files report lots of "Unknown QuickTime atom type" + (need documentation) +* Encrypted ASF/WMA/WMV files warn about "unhandled GUID + ASF_Content_Encryption_Object" +* Bitrate split between audio and video cannot be calculated for + NSV, only the total bitrate. (need documentation) +* All Ogg formats (Vorbis, OggFLAC, Speex) are affected by the + problem of large VorbisComments spanning multiple Ogg pages, but + but only OggVorbis files can be processed with vorbiscomment. +* The version of "head" supplied with Mac OS 10.2.8 (maybe other + versions too) does only understands a single option (-n) and + therefore fails. getID3 ignores this and returns wrong md5_data. + + + +Known Bugs/Issues in getID3() that cannot be fixed +-------------------------------------------------- +http://www.getid3.org/phpBB3/viewtopic.php?t=25 + +* 32-bit PHP installations only: + Files larger than 2GB cannot always be parsed fully by getID3() + due to limitations in the 32-bit PHP filesystem functions. + NOTE: Since v1.7.8b3 there is partial support for larger-than- + 2GB files, most of which will parse OK, as long as no critical + data is located beyond the 2GB offset. + Known will-work: + * all file formats on 64-bit PHP + * ZIP (format doesn't support files >2GB) + * FLAC (current encoders don't support files >2GB) + Known will-not-work: + * ID3v1 tags (always located at end-of-file) + * Lyrics3 tags (always located at end-of-file) + * APE tags (always located at end-of-file) + Maybe-will-work: + * Quicktime (will work if needed metadata is before 2GB offset, + that is if the file has been hinted/optimized for streaming) + * RIFF.WAV (should work fine, but gives warnings about not being + able to parse all chunks) + * RIFF.AVI (playtime will probably be wrong, is only based on + "movi" chunk that fits in the first 2GB, should issue error + to show that playtime is incorrect. Other data should be mostly + correct, assuming that data is constant throughout the file) +* PHP <= v5 on Windows cannot read UTF-8 filenames + + +Known Bugs/Issues in other programs +----------------------------------- +http://www.getid3.org/phpBB3/viewtopic.php?t=25 + +* Windows Media Player (up to v11) and iTunes (up to v10+) do + not correctly handle ID3v2.3 tags with UTF-16BE+BOM + encoding (they assume the data is UTF-16LE+BOM and either + crash (WMP) or output Asian character set (iTunes) +* Winamp (up to v2.80 at least) does not support ID3v2.4 tags, + only ID3v2.3 + see: http://forums.winamp.com/showthread.php?postid=387524 +* Some versions of Helium2 (www.helium2.com) do not write + ID3v2.4-compliant Frame Sizes, even though the tag is marked + as ID3v2.4) (detected by getID3()) +* MP3ext V3.3.17 places a non-compliant padding string at the end + of the ID3v2 header. This is supposedly fixed in v3.4b21 but + only if you manually add a registry key. This fix is not yet + confirmed. (detected by getID3()) +* CDex v1.40 (fixed by v1.50b7) writes non-compliant Ogg comment + strings, supposed to be in the format "NAME=value" but actually + written just "value" (detected by getID3()) +* Oggenc 0.9-rc3 flags the encoded file as ABR whether it's + actually ABR or VBR. +* iTunes (versions "X v2.0.3", "v3.0.1" are known-guilty, probably + other versions are too) writes ID3v2.3 comment tags using a + frame name 'COM ' which is not valid for ID3v2.3+ (it's an + ID3v2.2-style frame name) (detected by getID3()) +* MP2enc does not encode mono CBR MP2 files properly (half speed + sound and double playtime) +* MP2enc does not encode mono VBR MP2 files properly (actually + encoded as stereo) +* tooLAME does not encode mono VBR MP2 files properly (actually + encoded as stereo) +* AACenc encodes files in VBR mode (actually ABR) even if CBR is + specified +* AAC/ADIF - bitrate_mode = cbr for vbr files +* LAME 3.90-3.92 prepends one frame of null data (space for the + LAME/VBR header, but it never gets written) when encoding in CBR + mode with the DLL +* Ahead Nero encodes TwinVQF with a DSIZ value (which is supposed + to be the filesize in bytes) of "0" for TwinVQF v1.0 and "1" for + TwinVQF v2.0 (detected by getID3()) +* Ahead Nero encodes TwinVQF files 1 second shorter than they + should be +* AAC-ADTS files are always actually encoded VBR, even if CBR mode + is specified (the CBR-mode switches on the encoder enable ABR + mode, not CBR as such, but it's not possible to tell the + difference between such ABR files and true VBR) +* STREAMINFO.audio_signature in OggFLAC is always null. "The reason + it's like that is because there is no seeking support in + libOggFLAC yet, so it has no way to go back and write the + computed sum after encoding. Seeking support in Ogg FLAC is the + #1 item for the next release." - Josh Coalson (FLAC developer) + NOTE: getID3() will calculate md5_data in a method similar to + other file formats, but that value cannot be compared to the + md5_data value from FLAC data in a FLAC file format. +* STREAMINFO.audio_signature is not calculated in FLAC v0.3.0 & + v0.4.0 - getID3() will calculate md5_data in a method similar to + other file formats, but that value cannot be compared to the + md5_data value from FLAC v0.5.0+ +* RioPort (various versions including 2.0 and 3.11) tags ID3v2 with + a WCOM frame that has no data portion +* Earlier versions of Coolplayer adds illegal ID3 tags to Ogg Vorbis + files, thus making them corrupt. +* Meracl ID3 Tag Writer v1.3.4 (and older) incorrectly truncates the + last byte of data from an MP3 file when appending a new ID3v1 tag. + (detected by getID3()) +* Lossless-Audio files encoded with and without the -noseek switch + do actually differ internally and therefore cannot match md5_data +* iTunes has been known to append a new ID3v1 tag on the end of an + existing ID3v1 tag when ID3v2 tag is also present + (detected by getID3()) +* MediaMonkey may write a blank RGAD ID3v2 frame but put actual + replay gain adjustments in a series of user-defined TXXX frames + (detected and handled by getID3() since v1.9.2) + + + + +Reference material: +=========================================================================== + +[www.id3.org material now mirrored at http://id3lib.sourceforge.net/id3/] +* http://www.id3.org/id3v2.4.0-structure.txt +* http://www.id3.org/id3v2.4.0-frames.txt +* http://www.id3.org/id3v2.4.0-changes.txt +* http://www.id3.org/id3v2.3.0.txt +* http://www.id3.org/id3v2-00.txt +* http://www.id3.org/mp3frame.html +* http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html +* http://www.dv.co.yu/mpgscript/mpeghdr.htm +* http://www.mp3-tech.org/programmer/frame_header.html +* http://users.belgacom.net/gc247244/extra/tag.html +* http://gabriel.mp3-tech.org/mp3infotag.html +* http://www.id3.org/iso4217.html +* http://www.unicode.org/Public/MAPPINGS/ISO8859/8859-1.TXT +* http://www.xiph.org/ogg/vorbis/doc/framing.html +* http://www.xiph.org/ogg/vorbis/doc/v-comment.html +* http://leknor.com/code/php/class.ogg.php.txt +* http://www.id3.org/iso639-2.html +* http://www.id3.org/lyrics3.html +* http://www.id3.org/lyrics3200.html +* http://www.psc.edu/general/software/packages/ieee/ieee.html +* http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee-expl.html +* http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html +* http://www.jmcgowan.com/avi.html +* http://www.wotsit.org/ +* http://www.herdsoft.com/ti/davincie/davp3xo2.htm +* http://www.mathdogs.com/vorbis-illuminated/bitstream-appendix.html +* "Standard MIDI File Format" by Dustin Caldwell (from www.wotsit.org) +* http://midistudio.com/Help/GMSpecs_Patches.htm +* http://www.xiph.org/archives/vorbis/200109/0459.html +* http://www.replaygain.org/ +* http://www.lossless-audio.com/ +* http://download.microsoft.com/download/winmediatech40/Doc/1.0/WIN98MeXP/EN-US/ASF_Specification_v.1.0.exe +* http://mediaxw.sourceforge.net/files/doc/Active%20Streaming%20Format%20(ASF)%201.0%20Specification.pdf +* http://www.uni-jena.de/~pfk/mpp/sv8/ (archived at http://www.hydrogenaudio.org/musepack/klemm/www.personal.uni-jena.de/~pfk/mpp/sv8/) +* http://jfaul.de/atl/ +* http://www.uni-jena.de/~pfk/mpp/ (archived at http://www.hydrogenaudio.org/musepack/klemm/www.personal.uni-jena.de/~pfk/mpp/) +* http://www.libpng.org/pub/png/spec/png-1.2-pdg.html +* http://www.real.com/devzone/library/creating/rmsdk/doc/rmff.htm +* http://www.fastgraph.com/help/bmp_os2_header_format.html +* http://netghost.narod.ru/gff/graphics/summary/os2bmp.htm +* http://flac.sourceforge.net/format.html +* http://www.research.att.com/projects/mpegaudio/mpeg2.html +* http://www.audiocoding.com/wiki/index.php?page=AAC +* http://libmpeg.org/mpeg4/doc/w2203tfs.pdf +* http://www.geocities.com/xhelmboyx/quicktime/formats/qtm-layout.txt +* http://developer.apple.com/techpubs/quicktime/qtdevdocs/RM/frameset.htm +* http://www.nullsoft.com/nsv/ +* http://www.wotsit.org/download.asp?f=iso9660 +* http://sandbox.mc.edu/~bennet/cs110/tc/tctod.html +* http://www.cdroller.com/htm/readdata.html +* http://www.speex.org/manual/node10.html +* http://www.harmony-central.com/Computer/Programming/aiff-file-format.doc +* http://www.faqs.org/rfcs/rfc2361.html +* http://ghido.shelter.ro/ +* http://www.ebu.ch/tech_t3285.pdf +* http://www.sr.se/utveckling/tu/bwf +* http://ftp.aessc.org/pub/aes46-2002.pdf +* http://cartchunk.org:8080/ +* http://www.broadcastpapers.com/radio/cartchunk01.htm +* http://www.hr/josip/DSP/AudioFile2.html +* http://home.attbi.com/~chris.bagwell/AudioFormats-11.html +* http://www.pure-mac.com/extkey.html +* http://cesnet.dl.sourceforge.net/sourceforge/bonkenc/bonk-binary-format-0.9.txt +* http://www.headbands.com/gspot/ +* http://www.openswf.org/spec/SWFfileformat.html +* http://j-faul.virtualave.net/ +* http://www.btinternet.com/~AnthonyJ/Atari/programming/avr_format.html +* http://cui.unige.ch/OSG/info/AudioFormats/ap11.html +* http://sswf.sourceforge.net/SWFalexref.html +* http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt +* http://www-lehre.informatik.uni-osnabrueck.de/~fbstark/diplom/docs/swf/Flash_Uncovered.htm +* http://developer.apple.com/quicktime/icefloe/dispatch012.html +* http://www.csdn.net/Dev/Format/graphics/PCD.htm +* http://tta.iszf.irk.ru/ +* http://www.atsc.org/standards/a_52a.pdf +* http://www.alanwood.net/unicode/ +* http://www.freelists.org/archives/matroska-devel/07-2003/msg00010.html +* http://www.its.msstate.edu/net/real/reports/config/tags.stats +* http://homepages.slingshot.co.nz/~helmboy/quicktime/formats/qtm-layout.txt +* http://brennan.young.net/Comp/LiveStage/things.html +* http://www.multiweb.cz/twoinches/MP3inside.htm +* http://www.geocities.co.jp/SiliconValley-Oakland/3664/alittle.html#GenreExtended +* http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/ +* http://www.unicode.org/unicode/faq/utf_bom.html +* http://tta.corecodec.org/?menu=format +* http://www.scvi.net/nsvformat.htm +* http://pda.etsi.org/pda/queryform.asp +* http://cpansearch.perl.org/src/RGIBSON/Audio-DSS-0.02/lib/Audio/DSS.pm +* http://trac.musepack.net/trac/wiki/SV8Specification +* http://wyday.com/cuesharp/specification.php +* http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html \ No newline at end of file diff --git a/wp-includes/SimplePie/Author.php b/wp-includes/SimplePie/Author.php new file mode 100644 index 0000000..bbf3812 --- /dev/null +++ b/wp-includes/SimplePie/Author.php @@ -0,0 +1,157 @@ +name = $name; + $this->link = $link; + $this->email = $email; + } + + /** + * String-ified version + * + * @return string + */ + public function __toString() + { + // There is no $this->data here + return md5(serialize($this)); + } + + /** + * Author's name + * + * @return string|null + */ + public function get_name() + { + if ($this->name !== null) + { + return $this->name; + } + else + { + return null; + } + } + + /** + * Author's link + * + * @return string|null + */ + public function get_link() + { + if ($this->link !== null) + { + return $this->link; + } + else + { + return null; + } + } + + /** + * Author's email address + * + * @return string|null + */ + public function get_email() + { + if ($this->email !== null) + { + return $this->email; + } + else + { + return null; + } + } +} + diff --git a/wp-includes/SimplePie/Cache.php b/wp-includes/SimplePie/Cache.php new file mode 100644 index 0000000..75586d7 --- /dev/null +++ b/wp-includes/SimplePie/Cache.php @@ -0,0 +1,133 @@ + 'SimplePie_Cache_MySQL', + 'memcache' => 'SimplePie_Cache_Memcache', + ); + + /** + * Don't call the constructor. Please. + */ + private function __construct() { } + + /** + * Create a new SimplePie_Cache object + * + * @param string $location URL location (scheme is used to determine handler) + * @param string $filename Unique identifier for cache object + * @param string $extension 'spi' or 'spc' + * @return SimplePie_Cache_Base Type of object depends on scheme of `$location` + */ + public static function get_handler($location, $filename, $extension) + { + $type = explode(':', $location, 2); + $type = $type[0]; + if (!empty(self::$handlers[$type])) + { + $class = self::$handlers[$type]; + return new $class($location, $filename, $extension); + } + + return new SimplePie_Cache_File($location, $filename, $extension); + } + + /** + * Create a new SimplePie_Cache object + * + * @deprecated Use {@see get_handler} instead + */ + public function create($location, $filename, $extension) + { + trigger_error('Cache::create() has been replaced with Cache::get_handler(). Switch to the registry system to use this.', E_USER_DEPRECATED); + return self::get_handler($location, $filename, $extension); + } + + /** + * Register a handler + * + * @param string $type DSN type to register for + * @param string $class Name of handler class. Must implement SimplePie_Cache_Base + */ + public static function register($type, $class) + { + self::$handlers[$type] = $class; + } + + /** + * Parse a URL into an array + * + * @param string $url + * @return array + */ + public static function parse_URL($url) + { + $params = parse_url($url); + $params['extras'] = array(); + if (isset($params['query'])) + { + parse_str($params['query'], $params['extras']); + } + return $params; + } +} diff --git a/wp-includes/SimplePie/Cache/Base.php b/wp-includes/SimplePie/Cache/Base.php new file mode 100644 index 0000000..937e346 --- /dev/null +++ b/wp-includes/SimplePie/Cache/Base.php @@ -0,0 +1,114 @@ +get_items(); + $items_by_id = array(); + + if (!empty($items)) + { + foreach ($items as $item) + { + $items_by_id[$item->get_id()] = $item; + } + + if (count($items_by_id) !== count($items)) + { + $items_by_id = array(); + foreach ($items as $item) + { + $items_by_id[$item->get_id(true)] = $item; + } + } + + if (isset($data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0])) + { + $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]; + } + elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0])) + { + $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]; + } + elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0])) + { + $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]; + } + elseif (isset($data->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['channel'][0])) + { + $channel =& $data->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['channel'][0]; + } + else + { + $channel = null; + } + + if ($channel !== null) + { + if (isset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry'])) + { + unset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry']); + } + if (isset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['entry'])) + { + unset($channel['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['entry']); + } + if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item'])) + { + unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item']); + } + if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item'])) + { + unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item']); + } + if (isset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_20]['item'])) + { + unset($channel['child'][SIMPLEPIE_NAMESPACE_RSS_20]['item']); + } + } + if (isset($data->data['items'])) + { + unset($data->data['items']); + } + if (isset($data->data['ordered_items'])) + { + unset($data->data['ordered_items']); + } + } + return array(serialize($data->data), $items_by_id); + } +} diff --git a/wp-includes/SimplePie/Cache/File.php b/wp-includes/SimplePie/Cache/File.php new file mode 100644 index 0000000..5797b3a --- /dev/null +++ b/wp-includes/SimplePie/Cache/File.php @@ -0,0 +1,173 @@ +location = $location; + $this->filename = $name; + $this->extension = $type; + $this->name = "$this->location/$this->filename.$this->extension"; + } + + /** + * Save data to the cache + * + * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property + * @return bool Successfulness + */ + public function save($data) + { + if (file_exists($this->name) && is_writeable($this->name) || file_exists($this->location) && is_writeable($this->location)) + { + if ($data instanceof SimplePie) + { + $data = $data->data; + } + + $data = serialize($data); + return (bool) file_put_contents($this->name, $data); + } + return false; + } + + /** + * Retrieve the data saved to the cache + * + * @return array Data for SimplePie::$data + */ + public function load() + { + if (file_exists($this->name) && is_readable($this->name)) + { + return unserialize(file_get_contents($this->name)); + } + return false; + } + + /** + * Retrieve the last modified time for the cache + * + * @return int Timestamp + */ + public function mtime() + { + if (file_exists($this->name)) + { + return filemtime($this->name); + } + return false; + } + + /** + * Set the last modified time to the current time + * + * @return bool Success status + */ + public function touch() + { + if (file_exists($this->name)) + { + return touch($this->name); + } + return false; + } + + /** + * Remove the cache + * + * @return bool Success status + */ + public function unlink() + { + if (file_exists($this->name)) + { + return unlink($this->name); + } + return false; + } +} diff --git a/wp-includes/SimplePie/Cache/Memcache.php b/wp-includes/SimplePie/Cache/Memcache.php new file mode 100644 index 0000000..fd44780 --- /dev/null +++ b/wp-includes/SimplePie/Cache/Memcache.php @@ -0,0 +1,183 @@ +options = array( + 'host' => '127.0.0.1', + 'port' => 11211, + 'extras' => array( + 'timeout' => 3600, // one hour + 'prefix' => 'simplepie_', + ), + ); + $parsed = SimplePie_Cache::parse_URL($location); + $this->options['host'] = empty($parsed['host']) ? $this->options['host'] : $parsed['host']; + $this->options['port'] = empty($parsed['port']) ? $this->options['port'] : $parsed['port']; + $this->options['extras'] = array_merge($this->options['extras'], $parsed['extras']); + $this->name = $this->options['extras']['prefix'] . md5("$name:$type"); + + $this->cache = new Memcache(); + $this->cache->addServer($this->options['host'], (int) $this->options['port']); + } + + /** + * Save data to the cache + * + * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property + * @return bool Successfulness + */ + public function save($data) + { + if ($data instanceof SimplePie) + { + $data = $data->data; + } + return $this->cache->set($this->name, serialize($data), MEMCACHE_COMPRESSED, (int) $this->options['extras']['timeout']); + } + + /** + * Retrieve the data saved to the cache + * + * @return array Data for SimplePie::$data + */ + public function load() + { + $data = $this->cache->get($this->name); + + if ($data !== false) + { + return unserialize($data); + } + return false; + } + + /** + * Retrieve the last modified time for the cache + * + * @return int Timestamp + */ + public function mtime() + { + $data = $this->cache->get($this->name); + + if ($data !== false) + { + // essentially ignore the mtime because Memcache expires on it's own + return time(); + } + + return false; + } + + /** + * Set the last modified time to the current time + * + * @return bool Success status + */ + public function touch() + { + $data = $this->cache->get($this->name); + + if ($data !== false) + { + return $this->cache->set($this->name, $data, MEMCACHE_COMPRESSED, (int) $this->duration); + } + + return false; + } + + /** + * Remove the cache + * + * @return bool Success status + */ + public function unlink() + { + return $this->cache->delete($this->name, 0); + } +} diff --git a/wp-includes/SimplePie/Cache/MySQL.php b/wp-includes/SimplePie/Cache/MySQL.php new file mode 100644 index 0000000..d53ebc1 --- /dev/null +++ b/wp-includes/SimplePie/Cache/MySQL.php @@ -0,0 +1,438 @@ +options = array( + 'user' => null, + 'pass' => null, + 'host' => '127.0.0.1', + 'port' => '3306', + 'path' => '', + 'extras' => array( + 'prefix' => '', + ), + ); + $this->options = array_merge_recursive($this->options, SimplePie_Cache::parse_URL($location)); + + // Path is prefixed with a "/" + $this->options['dbname'] = substr($this->options['path'], 1); + + try + { + $this->mysql = new PDO("mysql:dbname={$this->options['dbname']};host={$this->options['host']};port={$this->options['port']}", $this->options['user'], $this->options['pass'], array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8')); + } + catch (PDOException $e) + { + $this->mysql = null; + return; + } + + $this->id = $name . $type; + + if (!$query = $this->mysql->query('SHOW TABLES')) + { + $this->mysql = null; + return; + } + + $db = array(); + while ($row = $query->fetchColumn()) + { + $db[] = $row; + } + + if (!in_array($this->options['extras']['prefix'] . 'cache_data', $db)) + { + $query = $this->mysql->exec('CREATE TABLE `' . $this->options['extras']['prefix'] . 'cache_data` (`id` TEXT CHARACTER SET utf8 NOT NULL, `items` SMALLINT NOT NULL DEFAULT 0, `data` BLOB NOT NULL, `mtime` INT UNSIGNED NOT NULL, UNIQUE (`id`(125)))'); + if ($query === false) + { + $this->mysql = null; + } + } + + if (!in_array($this->options['extras']['prefix'] . 'items', $db)) + { + $query = $this->mysql->exec('CREATE TABLE `' . $this->options['extras']['prefix'] . 'items` (`feed_id` TEXT CHARACTER SET utf8 NOT NULL, `id` TEXT CHARACTER SET utf8 NOT NULL, `data` TEXT CHARACTER SET utf8 NOT NULL, `posted` INT UNSIGNED NOT NULL, INDEX `feed_id` (`feed_id`(125)))'); + if ($query === false) + { + $this->mysql = null; + } + } + } + + /** + * Save data to the cache + * + * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property + * @return bool Successfulness + */ + public function save($data) + { + if ($this->mysql === null) + { + return false; + } + + if ($data instanceof SimplePie) + { + $data = clone $data; + + $prepared = self::prepare_simplepie_object_for_cache($data); + + $query = $this->mysql->prepare('SELECT COUNT(*) FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :feed'); + $query->bindValue(':feed', $this->id); + if ($query->execute()) + { + if ($query->fetchColumn() > 0) + { + $items = count($prepared[1]); + if ($items) + { + $sql = 'UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `items` = :items, `data` = :data, `mtime` = :time WHERE `id` = :feed'; + $query = $this->mysql->prepare($sql); + $query->bindValue(':items', $items); + } + else + { + $sql = 'UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `data` = :data, `mtime` = :time WHERE `id` = :feed'; + $query = $this->mysql->prepare($sql); + } + + $query->bindValue(':data', $prepared[0]); + $query->bindValue(':time', time()); + $query->bindValue(':feed', $this->id); + if (!$query->execute()) + { + return false; + } + } + else + { + $query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'cache_data` (`id`, `items`, `data`, `mtime`) VALUES(:feed, :count, :data, :time)'); + $query->bindValue(':feed', $this->id); + $query->bindValue(':count', count($prepared[1])); + $query->bindValue(':data', $prepared[0]); + $query->bindValue(':time', time()); + if (!$query->execute()) + { + return false; + } + } + + $ids = array_keys($prepared[1]); + if (!empty($ids)) + { + foreach ($ids as $id) + { + $database_ids[] = $this->mysql->quote($id); + } + + $query = $this->mysql->prepare('SELECT `id` FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `id` = ' . implode(' OR `id` = ', $database_ids) . ' AND `feed_id` = :feed'); + $query->bindValue(':feed', $this->id); + + if ($query->execute()) + { + $existing_ids = array(); + while ($row = $query->fetchColumn()) + { + $existing_ids[] = $row; + } + + $new_ids = array_diff($ids, $existing_ids); + + foreach ($new_ids as $new_id) + { + if (!($date = $prepared[1][$new_id]->get_date('U'))) + { + $date = time(); + } + + $query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'items` (`feed_id`, `id`, `data`, `posted`) VALUES(:feed, :id, :data, :date)'); + $query->bindValue(':feed', $this->id); + $query->bindValue(':id', $new_id); + $query->bindValue(':data', serialize($prepared[1][$new_id]->data)); + $query->bindValue(':date', $date); + if (!$query->execute()) + { + return false; + } + } + return true; + } + } + else + { + return true; + } + } + } + else + { + $query = $this->mysql->prepare('SELECT `id` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :feed'); + $query->bindValue(':feed', $this->id); + if ($query->execute()) + { + if ($query->rowCount() > 0) + { + $query = $this->mysql->prepare('UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `items` = 0, `data` = :data, `mtime` = :time WHERE `id` = :feed'); + $query->bindValue(':data', serialize($data)); + $query->bindValue(':time', time()); + $query->bindValue(':feed', $this->id); + if ($this->execute()) + { + return true; + } + } + else + { + $query = $this->mysql->prepare('INSERT INTO `' . $this->options['extras']['prefix'] . 'cache_data` (`id`, `items`, `data`, `mtime`) VALUES(:id, 0, :data, :time)'); + $query->bindValue(':id', $this->id); + $query->bindValue(':data', serialize($data)); + $query->bindValue(':time', time()); + if ($query->execute()) + { + return true; + } + } + } + } + return false; + } + + /** + * Retrieve the data saved to the cache + * + * @return array Data for SimplePie::$data + */ + public function load() + { + if ($this->mysql === null) + { + return false; + } + + $query = $this->mysql->prepare('SELECT `items`, `data` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id'); + $query->bindValue(':id', $this->id); + if ($query->execute() && ($row = $query->fetch())) + { + $data = unserialize($row[1]); + + if (isset($this->options['items'][0])) + { + $items = (int) $this->options['items'][0]; + } + else + { + $items = (int) $row[0]; + } + + if ($items !== 0) + { + if (isset($data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0])) + { + $feed =& $data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]; + } + elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0])) + { + $feed =& $data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]; + } + elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0])) + { + $feed =& $data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]; + } + elseif (isset($data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0])) + { + $feed =& $data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]; + } + else + { + $feed = null; + } + + if ($feed !== null) + { + $sql = 'SELECT `data` FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `feed_id` = :feed ORDER BY `posted` DESC'; + if ($items > 0) + { + $sql .= ' LIMIT ' . $items; + } + + $query = $this->mysql->prepare($sql); + $query->bindValue(':feed', $this->id); + if ($query->execute()) + { + while ($row = $query->fetchColumn()) + { + $feed['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['entry'][] = unserialize($row); + } + } + else + { + return false; + } + } + } + return $data; + } + return false; + } + + /** + * Retrieve the last modified time for the cache + * + * @return int Timestamp + */ + public function mtime() + { + if ($this->mysql === null) + { + return false; + } + + $query = $this->mysql->prepare('SELECT `mtime` FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id'); + $query->bindValue(':id', $this->id); + if ($query->execute() && ($time = $query->fetchColumn())) + { + return $time; + } + else + { + return false; + } + } + + /** + * Set the last modified time to the current time + * + * @return bool Success status + */ + public function touch() + { + if ($this->mysql === null) + { + return false; + } + + $query = $this->mysql->prepare('UPDATE `' . $this->options['extras']['prefix'] . 'cache_data` SET `mtime` = :time WHERE `id` = :id'); + $query->bindValue(':time', time()); + $query->bindValue(':id', $this->id); + if ($query->execute() && $query->rowCount() > 0) + { + return true; + } + else + { + return false; + } + } + + /** + * Remove the cache + * + * @return bool Success status + */ + public function unlink() + { + if ($this->mysql === null) + { + return false; + } + + $query = $this->mysql->prepare('DELETE FROM `' . $this->options['extras']['prefix'] . 'cache_data` WHERE `id` = :id'); + $query->bindValue(':id', $this->id); + $query2 = $this->mysql->prepare('DELETE FROM `' . $this->options['extras']['prefix'] . 'items` WHERE `feed_id` = :id'); + $query2->bindValue(':id', $this->id); + if ($query->execute() && $query2->execute()) + { + return true; + } + else + { + return false; + } + } +} diff --git a/wp-includes/SimplePie/Caption.php b/wp-includes/SimplePie/Caption.php new file mode 100644 index 0000000..52922c5 --- /dev/null +++ b/wp-includes/SimplePie/Caption.php @@ -0,0 +1,210 @@ +` captions as defined in Media RSS. + * + * Used by {@see SimplePie_Enclosure::get_caption()} and {@see SimplePie_Enclosure::get_captions()} + * + * This class can be overloaded with {@see SimplePie::set_caption_class()} + * + * @package SimplePie + * @subpackage API + */ +class SimplePie_Caption +{ + /** + * Content type + * + * @var string + * @see get_type() + */ + var $type; + + /** + * Language + * + * @var string + * @see get_language() + */ + var $lang; + + /** + * Start time + * + * @var string + * @see get_starttime() + */ + var $startTime; + + /** + * End time + * + * @var string + * @see get_endtime() + */ + var $endTime; + + /** + * Caption text + * + * @var string + * @see get_text() + */ + var $text; + + /** + * Constructor, used to input the data + * + * For documentation on all the parameters, see the corresponding + * properties and their accessors + */ + public function __construct($type = null, $lang = null, $startTime = null, $endTime = null, $text = null) + { + $this->type = $type; + $this->lang = $lang; + $this->startTime = $startTime; + $this->endTime = $endTime; + $this->text = $text; + } + + /** + * String-ified version + * + * @return string + */ + public function __toString() + { + // There is no $this->data here + return md5(serialize($this)); + } + + /** + * Get the end time + * + * @return string|null Time in the format 'hh:mm:ss.SSS' + */ + public function get_endtime() + { + if ($this->endTime !== null) + { + return $this->endTime; + } + else + { + return null; + } + } + + /** + * Get the language + * + * @link http://tools.ietf.org/html/rfc3066 + * @return string|null Language code as per RFC 3066 + */ + public function get_language() + { + if ($this->lang !== null) + { + return $this->lang; + } + else + { + return null; + } + } + + /** + * Get the start time + * + * @return string|null Time in the format 'hh:mm:ss.SSS' + */ + public function get_starttime() + { + if ($this->startTime !== null) + { + return $this->startTime; + } + else + { + return null; + } + } + + /** + * Get the text of the caption + * + * @return string|null + */ + public function get_text() + { + if ($this->text !== null) + { + return $this->text; + } + else + { + return null; + } + } + + /** + * Get the content type (not MIME type) + * + * @return string|null Either 'text' or 'html' + */ + public function get_type() + { + if ($this->type !== null) + { + return $this->type; + } + else + { + return null; + } + } +} + diff --git a/wp-includes/SimplePie/Category.php b/wp-includes/SimplePie/Category.php new file mode 100644 index 0000000..ad0407b --- /dev/null +++ b/wp-includes/SimplePie/Category.php @@ -0,0 +1,157 @@ +term = $term; + $this->scheme = $scheme; + $this->label = $label; + } + + /** + * String-ified version + * + * @return string + */ + public function __toString() + { + // There is no $this->data here + return md5(serialize($this)); + } + + /** + * Get the category identifier + * + * @return string|null + */ + public function get_term() + { + if ($this->term !== null) + { + return $this->term; + } + else + { + return null; + } + } + + /** + * Get the categorization scheme identifier + * + * @return string|null + */ + public function get_scheme() + { + if ($this->scheme !== null) + { + return $this->scheme; + } + else + { + return null; + } + } + + /** + * Get the human readable label + * + * @return string|null + */ + public function get_label() + { + if ($this->label !== null) + { + return $this->label; + } + else + { + return $this->get_term(); + } + } +} + diff --git a/wp-includes/SimplePie/Content/Type/Sniffer.php b/wp-includes/SimplePie/Content/Type/Sniffer.php new file mode 100644 index 0000000..20d053d --- /dev/null +++ b/wp-includes/SimplePie/Content/Type/Sniffer.php @@ -0,0 +1,332 @@ +file = $file; + } + + /** + * Get the Content-Type of the specified file + * + * @return string Actual Content-Type + */ + public function get_type() + { + if (isset($this->file->headers['content-type'])) + { + if (!isset($this->file->headers['content-encoding']) + && ($this->file->headers['content-type'] === 'text/plain' + || $this->file->headers['content-type'] === 'text/plain; charset=ISO-8859-1' + || $this->file->headers['content-type'] === 'text/plain; charset=iso-8859-1' + || $this->file->headers['content-type'] === 'text/plain; charset=UTF-8')) + { + return $this->text_or_binary(); + } + + if (($pos = strpos($this->file->headers['content-type'], ';')) !== false) + { + $official = substr($this->file->headers['content-type'], 0, $pos); + } + else + { + $official = $this->file->headers['content-type']; + } + $official = trim(strtolower($official)); + + if ($official === 'unknown/unknown' + || $official === 'application/unknown') + { + return $this->unknown(); + } + elseif (substr($official, -4) === '+xml' + || $official === 'text/xml' + || $official === 'application/xml') + { + return $official; + } + elseif (substr($official, 0, 6) === 'image/') + { + if ($return = $this->image()) + { + return $return; + } + else + { + return $official; + } + } + elseif ($official === 'text/html') + { + return $this->feed_or_html(); + } + else + { + return $official; + } + } + else + { + return $this->unknown(); + } + } + + /** + * Sniff text or binary + * + * @return string Actual Content-Type + */ + public function text_or_binary() + { + if (substr($this->file->body, 0, 2) === "\xFE\xFF" + || substr($this->file->body, 0, 2) === "\xFF\xFE" + || substr($this->file->body, 0, 4) === "\x00\x00\xFE\xFF" + || substr($this->file->body, 0, 3) === "\xEF\xBB\xBF") + { + return 'text/plain'; + } + elseif (preg_match('/[\x00-\x08\x0E-\x1A\x1C-\x1F]/', $this->file->body)) + { + return 'application/octect-stream'; + } + else + { + return 'text/plain'; + } + } + + /** + * Sniff unknown + * + * @return string Actual Content-Type + */ + public function unknown() + { + $ws = strspn($this->file->body, "\x09\x0A\x0B\x0C\x0D\x20"); + if (strtolower(substr($this->file->body, $ws, 14)) === 'file->body, $ws, 5)) === 'file->body, $ws, 7)) === 'file->body, 0, 5) === '%PDF-') + { + return 'application/pdf'; + } + elseif (substr($this->file->body, 0, 11) === '%!PS-Adobe-') + { + return 'application/postscript'; + } + elseif (substr($this->file->body, 0, 6) === 'GIF87a' + || substr($this->file->body, 0, 6) === 'GIF89a') + { + return 'image/gif'; + } + elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A") + { + return 'image/png'; + } + elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF") + { + return 'image/jpeg'; + } + elseif (substr($this->file->body, 0, 2) === "\x42\x4D") + { + return 'image/bmp'; + } + elseif (substr($this->file->body, 0, 4) === "\x00\x00\x01\x00") + { + return 'image/vnd.microsoft.icon'; + } + else + { + return $this->text_or_binary(); + } + } + + /** + * Sniff images + * + * @return string Actual Content-Type + */ + public function image() + { + if (substr($this->file->body, 0, 6) === 'GIF87a' + || substr($this->file->body, 0, 6) === 'GIF89a') + { + return 'image/gif'; + } + elseif (substr($this->file->body, 0, 8) === "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A") + { + return 'image/png'; + } + elseif (substr($this->file->body, 0, 3) === "\xFF\xD8\xFF") + { + return 'image/jpeg'; + } + elseif (substr($this->file->body, 0, 2) === "\x42\x4D") + { + return 'image/bmp'; + } + elseif (substr($this->file->body, 0, 4) === "\x00\x00\x01\x00") + { + return 'image/vnd.microsoft.icon'; + } + else + { + return false; + } + } + + /** + * Sniff HTML + * + * @return string Actual Content-Type + */ + public function feed_or_html() + { + $len = strlen($this->file->body); + $pos = strspn($this->file->body, "\x09\x0A\x0D\x20"); + + while ($pos < $len) + { + switch ($this->file->body[$pos]) + { + case "\x09": + case "\x0A": + case "\x0D": + case "\x20": + $pos += strspn($this->file->body, "\x09\x0A\x0D\x20", $pos); + continue 2; + + case '<': + $pos++; + break; + + default: + return 'text/html'; + } + + if (substr($this->file->body, $pos, 3) === '!--') + { + $pos += 3; + if ($pos < $len && ($pos = strpos($this->file->body, '-->', $pos)) !== false) + { + $pos += 3; + } + else + { + return 'text/html'; + } + } + elseif (substr($this->file->body, $pos, 1) === '!') + { + if ($pos < $len && ($pos = strpos($this->file->body, '>', $pos)) !== false) + { + $pos++; + } + else + { + return 'text/html'; + } + } + elseif (substr($this->file->body, $pos, 1) === '?') + { + if ($pos < $len && ($pos = strpos($this->file->body, '?>', $pos)) !== false) + { + $pos += 2; + } + else + { + return 'text/html'; + } + } + elseif (substr($this->file->body, $pos, 3) === 'rss' + || substr($this->file->body, $pos, 7) === 'rdf:RDF') + { + return 'application/rss+xml'; + } + elseif (substr($this->file->body, $pos, 4) === 'feed') + { + return 'application/atom+xml'; + } + else + { + return 'text/html'; + } + } + + return 'text/html'; + } +} + diff --git a/wp-includes/SimplePie/Copyright.php b/wp-includes/SimplePie/Copyright.php new file mode 100644 index 0000000..57c535a --- /dev/null +++ b/wp-includes/SimplePie/Copyright.php @@ -0,0 +1,130 @@ +` copyright tags as defined in Media RSS + * + * Used by {@see SimplePie_Enclosure::get_copyright()} + * + * This class can be overloaded with {@see SimplePie::set_copyright_class()} + * + * @package SimplePie + * @subpackage API + */ +class SimplePie_Copyright +{ + /** + * Copyright URL + * + * @var string + * @see get_url() + */ + var $url; + + /** + * Attribution + * + * @var string + * @see get_attribution() + */ + var $label; + + /** + * Constructor, used to input the data + * + * For documentation on all the parameters, see the corresponding + * properties and their accessors + */ + public function __construct($url = null, $label = null) + { + $this->url = $url; + $this->label = $label; + } + + /** + * String-ified version + * + * @return string + */ + public function __toString() + { + // There is no $this->data here + return md5(serialize($this)); + } + + /** + * Get the copyright URL + * + * @return string|null URL to copyright information + */ + public function get_url() + { + if ($this->url !== null) + { + return $this->url; + } + else + { + return null; + } + } + + /** + * Get the attribution text + * + * @return string|null + */ + public function get_attribution() + { + if ($this->label !== null) + { + return $this->label; + } + else + { + return null; + } + } +} + diff --git a/wp-includes/SimplePie/Core.php b/wp-includes/SimplePie/Core.php new file mode 100644 index 0000000..46d9966 --- /dev/null +++ b/wp-includes/SimplePie/Core.php @@ -0,0 +1,57 @@ +` as defined in Media RSS + * + * Used by {@see SimplePie_Enclosure::get_credit()} and {@see SimplePie_Enclosure::get_credits()} + * + * This class can be overloaded with {@see SimplePie::set_credit_class()} + * + * @package SimplePie + * @subpackage API + */ +class SimplePie_Credit +{ + /** + * Credited role + * + * @var string + * @see get_role() + */ + var $role; + + /** + * Organizational scheme + * + * @var string + * @see get_scheme() + */ + var $scheme; + + /** + * Credited name + * + * @var string + * @see get_name() + */ + var $name; + + /** + * Constructor, used to input the data + * + * For documentation on all the parameters, see the corresponding + * properties and their accessors + */ + public function __construct($role = null, $scheme = null, $name = null) + { + $this->role = $role; + $this->scheme = $scheme; + $this->name = $name; + } + + /** + * String-ified version + * + * @return string + */ + public function __toString() + { + // There is no $this->data here + return md5(serialize($this)); + } + + /** + * Get the role of the person receiving credit + * + * @return string|null + */ + public function get_role() + { + if ($this->role !== null) + { + return $this->role; + } + else + { + return null; + } + } + + /** + * Get the organizational scheme + * + * @return string|null + */ + public function get_scheme() + { + if ($this->scheme !== null) + { + return $this->scheme; + } + else + { + return null; + } + } + + /** + * Get the credited person/entity's name + * + * @return string|null + */ + public function get_name() + { + if ($this->name !== null) + { + return $this->name; + } + else + { + return null; + } + } +} + diff --git a/wp-includes/SimplePie/Decode/HTML/Entities.php b/wp-includes/SimplePie/Decode/HTML/Entities.php new file mode 100644 index 0000000..069e8d8 --- /dev/null +++ b/wp-includes/SimplePie/Decode/HTML/Entities.php @@ -0,0 +1,617 @@ +data = $data; + } + + /** + * Parse the input data + * + * @access public + * @return string Output data + */ + public function parse() + { + while (($this->position = strpos($this->data, '&', $this->position)) !== false) + { + $this->consume(); + $this->entity(); + $this->consumed = ''; + } + return $this->data; + } + + /** + * Consume the next byte + * + * @access private + * @return mixed The next byte, or false, if there is no more data + */ + public function consume() + { + if (isset($this->data[$this->position])) + { + $this->consumed .= $this->data[$this->position]; + return $this->data[$this->position++]; + } + else + { + return false; + } + } + + /** + * Consume a range of characters + * + * @access private + * @param string $chars Characters to consume + * @return mixed A series of characters that match the range, or false + */ + public function consume_range($chars) + { + if ($len = strspn($this->data, $chars, $this->position)) + { + $data = substr($this->data, $this->position, $len); + $this->consumed .= $data; + $this->position += $len; + return $data; + } + else + { + return false; + } + } + + /** + * Unconsume one byte + * + * @access private + */ + public function unconsume() + { + $this->consumed = substr($this->consumed, 0, -1); + $this->position--; + } + + /** + * Decode an entity + * + * @access private + */ + public function entity() + { + switch ($this->consume()) + { + case "\x09": + case "\x0A": + case "\x0B": + case "\x0B": + case "\x0C": + case "\x20": + case "\x3C": + case "\x26": + case false: + break; + + case "\x23": + switch ($this->consume()) + { + case "\x78": + case "\x58": + $range = '0123456789ABCDEFabcdef'; + $hex = true; + break; + + default: + $range = '0123456789'; + $hex = false; + $this->unconsume(); + break; + } + + if ($codepoint = $this->consume_range($range)) + { + static $windows_1252_specials = array(0x0D => "\x0A", 0x80 => "\xE2\x82\xAC", 0x81 => "\xEF\xBF\xBD", 0x82 => "\xE2\x80\x9A", 0x83 => "\xC6\x92", 0x84 => "\xE2\x80\x9E", 0x85 => "\xE2\x80\xA6", 0x86 => "\xE2\x80\xA0", 0x87 => "\xE2\x80\xA1", 0x88 => "\xCB\x86", 0x89 => "\xE2\x80\xB0", 0x8A => "\xC5\xA0", 0x8B => "\xE2\x80\xB9", 0x8C => "\xC5\x92", 0x8D => "\xEF\xBF\xBD", 0x8E => "\xC5\xBD", 0x8F => "\xEF\xBF\xBD", 0x90 => "\xEF\xBF\xBD", 0x91 => "\xE2\x80\x98", 0x92 => "\xE2\x80\x99", 0x93 => "\xE2\x80\x9C", 0x94 => "\xE2\x80\x9D", 0x95 => "\xE2\x80\xA2", 0x96 => "\xE2\x80\x93", 0x97 => "\xE2\x80\x94", 0x98 => "\xCB\x9C", 0x99 => "\xE2\x84\xA2", 0x9A => "\xC5\xA1", 0x9B => "\xE2\x80\xBA", 0x9C => "\xC5\x93", 0x9D => "\xEF\xBF\xBD", 0x9E => "\xC5\xBE", 0x9F => "\xC5\xB8"); + + if ($hex) + { + $codepoint = hexdec($codepoint); + } + else + { + $codepoint = intval($codepoint); + } + + if (isset($windows_1252_specials[$codepoint])) + { + $replacement = $windows_1252_specials[$codepoint]; + } + else + { + $replacement = SimplePie_Misc::codepoint_to_utf8($codepoint); + } + + if (!in_array($this->consume(), array(';', false), true)) + { + $this->unconsume(); + } + + $consumed_length = strlen($this->consumed); + $this->data = substr_replace($this->data, $replacement, $this->position - $consumed_length, $consumed_length); + $this->position += strlen($replacement) - $consumed_length; + } + break; + + default: + static $entities = array( + 'Aacute' => "\xC3\x81", + 'aacute' => "\xC3\xA1", + 'Aacute;' => "\xC3\x81", + 'aacute;' => "\xC3\xA1", + 'Acirc' => "\xC3\x82", + 'acirc' => "\xC3\xA2", + 'Acirc;' => "\xC3\x82", + 'acirc;' => "\xC3\xA2", + 'acute' => "\xC2\xB4", + 'acute;' => "\xC2\xB4", + 'AElig' => "\xC3\x86", + 'aelig' => "\xC3\xA6", + 'AElig;' => "\xC3\x86", + 'aelig;' => "\xC3\xA6", + 'Agrave' => "\xC3\x80", + 'agrave' => "\xC3\xA0", + 'Agrave;' => "\xC3\x80", + 'agrave;' => "\xC3\xA0", + 'alefsym;' => "\xE2\x84\xB5", + 'Alpha;' => "\xCE\x91", + 'alpha;' => "\xCE\xB1", + 'AMP' => "\x26", + 'amp' => "\x26", + 'AMP;' => "\x26", + 'amp;' => "\x26", + 'and;' => "\xE2\x88\xA7", + 'ang;' => "\xE2\x88\xA0", + 'apos;' => "\x27", + 'Aring' => "\xC3\x85", + 'aring' => "\xC3\xA5", + 'Aring;' => "\xC3\x85", + 'aring;' => "\xC3\xA5", + 'asymp;' => "\xE2\x89\x88", + 'Atilde' => "\xC3\x83", + 'atilde' => "\xC3\xA3", + 'Atilde;' => "\xC3\x83", + 'atilde;' => "\xC3\xA3", + 'Auml' => "\xC3\x84", + 'auml' => "\xC3\xA4", + 'Auml;' => "\xC3\x84", + 'auml;' => "\xC3\xA4", + 'bdquo;' => "\xE2\x80\x9E", + 'Beta;' => "\xCE\x92", + 'beta;' => "\xCE\xB2", + 'brvbar' => "\xC2\xA6", + 'brvbar;' => "\xC2\xA6", + 'bull;' => "\xE2\x80\xA2", + 'cap;' => "\xE2\x88\xA9", + 'Ccedil' => "\xC3\x87", + 'ccedil' => "\xC3\xA7", + 'Ccedil;' => "\xC3\x87", + 'ccedil;' => "\xC3\xA7", + 'cedil' => "\xC2\xB8", + 'cedil;' => "\xC2\xB8", + 'cent' => "\xC2\xA2", + 'cent;' => "\xC2\xA2", + 'Chi;' => "\xCE\xA7", + 'chi;' => "\xCF\x87", + 'circ;' => "\xCB\x86", + 'clubs;' => "\xE2\x99\xA3", + 'cong;' => "\xE2\x89\x85", + 'COPY' => "\xC2\xA9", + 'copy' => "\xC2\xA9", + 'COPY;' => "\xC2\xA9", + 'copy;' => "\xC2\xA9", + 'crarr;' => "\xE2\x86\xB5", + 'cup;' => "\xE2\x88\xAA", + 'curren' => "\xC2\xA4", + 'curren;' => "\xC2\xA4", + 'Dagger;' => "\xE2\x80\xA1", + 'dagger;' => "\xE2\x80\xA0", + 'dArr;' => "\xE2\x87\x93", + 'darr;' => "\xE2\x86\x93", + 'deg' => "\xC2\xB0", + 'deg;' => "\xC2\xB0", + 'Delta;' => "\xCE\x94", + 'delta;' => "\xCE\xB4", + 'diams;' => "\xE2\x99\xA6", + 'divide' => "\xC3\xB7", + 'divide;' => "\xC3\xB7", + 'Eacute' => "\xC3\x89", + 'eacute' => "\xC3\xA9", + 'Eacute;' => "\xC3\x89", + 'eacute;' => "\xC3\xA9", + 'Ecirc' => "\xC3\x8A", + 'ecirc' => "\xC3\xAA", + 'Ecirc;' => "\xC3\x8A", + 'ecirc;' => "\xC3\xAA", + 'Egrave' => "\xC3\x88", + 'egrave' => "\xC3\xA8", + 'Egrave;' => "\xC3\x88", + 'egrave;' => "\xC3\xA8", + 'empty;' => "\xE2\x88\x85", + 'emsp;' => "\xE2\x80\x83", + 'ensp;' => "\xE2\x80\x82", + 'Epsilon;' => "\xCE\x95", + 'epsilon;' => "\xCE\xB5", + 'equiv;' => "\xE2\x89\xA1", + 'Eta;' => "\xCE\x97", + 'eta;' => "\xCE\xB7", + 'ETH' => "\xC3\x90", + 'eth' => "\xC3\xB0", + 'ETH;' => "\xC3\x90", + 'eth;' => "\xC3\xB0", + 'Euml' => "\xC3\x8B", + 'euml' => "\xC3\xAB", + 'Euml;' => "\xC3\x8B", + 'euml;' => "\xC3\xAB", + 'euro;' => "\xE2\x82\xAC", + 'exist;' => "\xE2\x88\x83", + 'fnof;' => "\xC6\x92", + 'forall;' => "\xE2\x88\x80", + 'frac12' => "\xC2\xBD", + 'frac12;' => "\xC2\xBD", + 'frac14' => "\xC2\xBC", + 'frac14;' => "\xC2\xBC", + 'frac34' => "\xC2\xBE", + 'frac34;' => "\xC2\xBE", + 'frasl;' => "\xE2\x81\x84", + 'Gamma;' => "\xCE\x93", + 'gamma;' => "\xCE\xB3", + 'ge;' => "\xE2\x89\xA5", + 'GT' => "\x3E", + 'gt' => "\x3E", + 'GT;' => "\x3E", + 'gt;' => "\x3E", + 'hArr;' => "\xE2\x87\x94", + 'harr;' => "\xE2\x86\x94", + 'hearts;' => "\xE2\x99\xA5", + 'hellip;' => "\xE2\x80\xA6", + 'Iacute' => "\xC3\x8D", + 'iacute' => "\xC3\xAD", + 'Iacute;' => "\xC3\x8D", + 'iacute;' => "\xC3\xAD", + 'Icirc' => "\xC3\x8E", + 'icirc' => "\xC3\xAE", + 'Icirc;' => "\xC3\x8E", + 'icirc;' => "\xC3\xAE", + 'iexcl' => "\xC2\xA1", + 'iexcl;' => "\xC2\xA1", + 'Igrave' => "\xC3\x8C", + 'igrave' => "\xC3\xAC", + 'Igrave;' => "\xC3\x8C", + 'igrave;' => "\xC3\xAC", + 'image;' => "\xE2\x84\x91", + 'infin;' => "\xE2\x88\x9E", + 'int;' => "\xE2\x88\xAB", + 'Iota;' => "\xCE\x99", + 'iota;' => "\xCE\xB9", + 'iquest' => "\xC2\xBF", + 'iquest;' => "\xC2\xBF", + 'isin;' => "\xE2\x88\x88", + 'Iuml' => "\xC3\x8F", + 'iuml' => "\xC3\xAF", + 'Iuml;' => "\xC3\x8F", + 'iuml;' => "\xC3\xAF", + 'Kappa;' => "\xCE\x9A", + 'kappa;' => "\xCE\xBA", + 'Lambda;' => "\xCE\x9B", + 'lambda;' => "\xCE\xBB", + 'lang;' => "\xE3\x80\x88", + 'laquo' => "\xC2\xAB", + 'laquo;' => "\xC2\xAB", + 'lArr;' => "\xE2\x87\x90", + 'larr;' => "\xE2\x86\x90", + 'lceil;' => "\xE2\x8C\x88", + 'ldquo;' => "\xE2\x80\x9C", + 'le;' => "\xE2\x89\xA4", + 'lfloor;' => "\xE2\x8C\x8A", + 'lowast;' => "\xE2\x88\x97", + 'loz;' => "\xE2\x97\x8A", + 'lrm;' => "\xE2\x80\x8E", + 'lsaquo;' => "\xE2\x80\xB9", + 'lsquo;' => "\xE2\x80\x98", + 'LT' => "\x3C", + 'lt' => "\x3C", + 'LT;' => "\x3C", + 'lt;' => "\x3C", + 'macr' => "\xC2\xAF", + 'macr;' => "\xC2\xAF", + 'mdash;' => "\xE2\x80\x94", + 'micro' => "\xC2\xB5", + 'micro;' => "\xC2\xB5", + 'middot' => "\xC2\xB7", + 'middot;' => "\xC2\xB7", + 'minus;' => "\xE2\x88\x92", + 'Mu;' => "\xCE\x9C", + 'mu;' => "\xCE\xBC", + 'nabla;' => "\xE2\x88\x87", + 'nbsp' => "\xC2\xA0", + 'nbsp;' => "\xC2\xA0", + 'ndash;' => "\xE2\x80\x93", + 'ne;' => "\xE2\x89\xA0", + 'ni;' => "\xE2\x88\x8B", + 'not' => "\xC2\xAC", + 'not;' => "\xC2\xAC", + 'notin;' => "\xE2\x88\x89", + 'nsub;' => "\xE2\x8A\x84", + 'Ntilde' => "\xC3\x91", + 'ntilde' => "\xC3\xB1", + 'Ntilde;' => "\xC3\x91", + 'ntilde;' => "\xC3\xB1", + 'Nu;' => "\xCE\x9D", + 'nu;' => "\xCE\xBD", + 'Oacute' => "\xC3\x93", + 'oacute' => "\xC3\xB3", + 'Oacute;' => "\xC3\x93", + 'oacute;' => "\xC3\xB3", + 'Ocirc' => "\xC3\x94", + 'ocirc' => "\xC3\xB4", + 'Ocirc;' => "\xC3\x94", + 'ocirc;' => "\xC3\xB4", + 'OElig;' => "\xC5\x92", + 'oelig;' => "\xC5\x93", + 'Ograve' => "\xC3\x92", + 'ograve' => "\xC3\xB2", + 'Ograve;' => "\xC3\x92", + 'ograve;' => "\xC3\xB2", + 'oline;' => "\xE2\x80\xBE", + 'Omega;' => "\xCE\xA9", + 'omega;' => "\xCF\x89", + 'Omicron;' => "\xCE\x9F", + 'omicron;' => "\xCE\xBF", + 'oplus;' => "\xE2\x8A\x95", + 'or;' => "\xE2\x88\xA8", + 'ordf' => "\xC2\xAA", + 'ordf;' => "\xC2\xAA", + 'ordm' => "\xC2\xBA", + 'ordm;' => "\xC2\xBA", + 'Oslash' => "\xC3\x98", + 'oslash' => "\xC3\xB8", + 'Oslash;' => "\xC3\x98", + 'oslash;' => "\xC3\xB8", + 'Otilde' => "\xC3\x95", + 'otilde' => "\xC3\xB5", + 'Otilde;' => "\xC3\x95", + 'otilde;' => "\xC3\xB5", + 'otimes;' => "\xE2\x8A\x97", + 'Ouml' => "\xC3\x96", + 'ouml' => "\xC3\xB6", + 'Ouml;' => "\xC3\x96", + 'ouml;' => "\xC3\xB6", + 'para' => "\xC2\xB6", + 'para;' => "\xC2\xB6", + 'part;' => "\xE2\x88\x82", + 'permil;' => "\xE2\x80\xB0", + 'perp;' => "\xE2\x8A\xA5", + 'Phi;' => "\xCE\xA6", + 'phi;' => "\xCF\x86", + 'Pi;' => "\xCE\xA0", + 'pi;' => "\xCF\x80", + 'piv;' => "\xCF\x96", + 'plusmn' => "\xC2\xB1", + 'plusmn;' => "\xC2\xB1", + 'pound' => "\xC2\xA3", + 'pound;' => "\xC2\xA3", + 'Prime;' => "\xE2\x80\xB3", + 'prime;' => "\xE2\x80\xB2", + 'prod;' => "\xE2\x88\x8F", + 'prop;' => "\xE2\x88\x9D", + 'Psi;' => "\xCE\xA8", + 'psi;' => "\xCF\x88", + 'QUOT' => "\x22", + 'quot' => "\x22", + 'QUOT;' => "\x22", + 'quot;' => "\x22", + 'radic;' => "\xE2\x88\x9A", + 'rang;' => "\xE3\x80\x89", + 'raquo' => "\xC2\xBB", + 'raquo;' => "\xC2\xBB", + 'rArr;' => "\xE2\x87\x92", + 'rarr;' => "\xE2\x86\x92", + 'rceil;' => "\xE2\x8C\x89", + 'rdquo;' => "\xE2\x80\x9D", + 'real;' => "\xE2\x84\x9C", + 'REG' => "\xC2\xAE", + 'reg' => "\xC2\xAE", + 'REG;' => "\xC2\xAE", + 'reg;' => "\xC2\xAE", + 'rfloor;' => "\xE2\x8C\x8B", + 'Rho;' => "\xCE\xA1", + 'rho;' => "\xCF\x81", + 'rlm;' => "\xE2\x80\x8F", + 'rsaquo;' => "\xE2\x80\xBA", + 'rsquo;' => "\xE2\x80\x99", + 'sbquo;' => "\xE2\x80\x9A", + 'Scaron;' => "\xC5\xA0", + 'scaron;' => "\xC5\xA1", + 'sdot;' => "\xE2\x8B\x85", + 'sect' => "\xC2\xA7", + 'sect;' => "\xC2\xA7", + 'shy' => "\xC2\xAD", + 'shy;' => "\xC2\xAD", + 'Sigma;' => "\xCE\xA3", + 'sigma;' => "\xCF\x83", + 'sigmaf;' => "\xCF\x82", + 'sim;' => "\xE2\x88\xBC", + 'spades;' => "\xE2\x99\xA0", + 'sub;' => "\xE2\x8A\x82", + 'sube;' => "\xE2\x8A\x86", + 'sum;' => "\xE2\x88\x91", + 'sup;' => "\xE2\x8A\x83", + 'sup1' => "\xC2\xB9", + 'sup1;' => "\xC2\xB9", + 'sup2' => "\xC2\xB2", + 'sup2;' => "\xC2\xB2", + 'sup3' => "\xC2\xB3", + 'sup3;' => "\xC2\xB3", + 'supe;' => "\xE2\x8A\x87", + 'szlig' => "\xC3\x9F", + 'szlig;' => "\xC3\x9F", + 'Tau;' => "\xCE\xA4", + 'tau;' => "\xCF\x84", + 'there4;' => "\xE2\x88\xB4", + 'Theta;' => "\xCE\x98", + 'theta;' => "\xCE\xB8", + 'thetasym;' => "\xCF\x91", + 'thinsp;' => "\xE2\x80\x89", + 'THORN' => "\xC3\x9E", + 'thorn' => "\xC3\xBE", + 'THORN;' => "\xC3\x9E", + 'thorn;' => "\xC3\xBE", + 'tilde;' => "\xCB\x9C", + 'times' => "\xC3\x97", + 'times;' => "\xC3\x97", + 'TRADE;' => "\xE2\x84\xA2", + 'trade;' => "\xE2\x84\xA2", + 'Uacute' => "\xC3\x9A", + 'uacute' => "\xC3\xBA", + 'Uacute;' => "\xC3\x9A", + 'uacute;' => "\xC3\xBA", + 'uArr;' => "\xE2\x87\x91", + 'uarr;' => "\xE2\x86\x91", + 'Ucirc' => "\xC3\x9B", + 'ucirc' => "\xC3\xBB", + 'Ucirc;' => "\xC3\x9B", + 'ucirc;' => "\xC3\xBB", + 'Ugrave' => "\xC3\x99", + 'ugrave' => "\xC3\xB9", + 'Ugrave;' => "\xC3\x99", + 'ugrave;' => "\xC3\xB9", + 'uml' => "\xC2\xA8", + 'uml;' => "\xC2\xA8", + 'upsih;' => "\xCF\x92", + 'Upsilon;' => "\xCE\xA5", + 'upsilon;' => "\xCF\x85", + 'Uuml' => "\xC3\x9C", + 'uuml' => "\xC3\xBC", + 'Uuml;' => "\xC3\x9C", + 'uuml;' => "\xC3\xBC", + 'weierp;' => "\xE2\x84\x98", + 'Xi;' => "\xCE\x9E", + 'xi;' => "\xCE\xBE", + 'Yacute' => "\xC3\x9D", + 'yacute' => "\xC3\xBD", + 'Yacute;' => "\xC3\x9D", + 'yacute;' => "\xC3\xBD", + 'yen' => "\xC2\xA5", + 'yen;' => "\xC2\xA5", + 'yuml' => "\xC3\xBF", + 'Yuml;' => "\xC5\xB8", + 'yuml;' => "\xC3\xBF", + 'Zeta;' => "\xCE\x96", + 'zeta;' => "\xCE\xB6", + 'zwj;' => "\xE2\x80\x8D", + 'zwnj;' => "\xE2\x80\x8C" + ); + + for ($i = 0, $match = null; $i < 9 && $this->consume() !== false; $i++) + { + $consumed = substr($this->consumed, 1); + if (isset($entities[$consumed])) + { + $match = $consumed; + } + } + + if ($match !== null) + { + $this->data = substr_replace($this->data, $entities[$match], $this->position - strlen($consumed) - 1, strlen($match) + 1); + $this->position += strlen($entities[$match]) - strlen($consumed) - 1; + } + break; + } + } +} + diff --git a/wp-includes/SimplePie/Enclosure.php b/wp-includes/SimplePie/Enclosure.php new file mode 100644 index 0000000..5567437 --- /dev/null +++ b/wp-includes/SimplePie/Enclosure.php @@ -0,0 +1,1380 @@ +bitrate = $bitrate; + $this->captions = $captions; + $this->categories = $categories; + $this->channels = $channels; + $this->copyright = $copyright; + $this->credits = $credits; + $this->description = $description; + $this->duration = $duration; + $this->expression = $expression; + $this->framerate = $framerate; + $this->hashes = $hashes; + $this->height = $height; + $this->keywords = $keywords; + $this->lang = $lang; + $this->length = $length; + $this->link = $link; + $this->medium = $medium; + $this->player = $player; + $this->ratings = $ratings; + $this->restrictions = $restrictions; + $this->samplingrate = $samplingrate; + $this->thumbnails = $thumbnails; + $this->title = $title; + $this->type = $type; + $this->width = $width; + + if (class_exists('idna_convert')) + { + $idn = new idna_convert(); + $parsed = SimplePie_Misc::parse_url($link); + $this->link = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']); + } + $this->handler = $this->get_handler(); // Needs to load last + } + + /** + * String-ified version + * + * @return string + */ + public function __toString() + { + // There is no $this->data here + return md5(serialize($this)); + } + + /** + * Get the bitrate + * + * @return string|null + */ + public function get_bitrate() + { + if ($this->bitrate !== null) + { + return $this->bitrate; + } + else + { + return null; + } + } + + /** + * Get a single caption + * + * @param int $key + * @return SimplePie_Caption|null + */ + public function get_caption($key = 0) + { + $captions = $this->get_captions(); + if (isset($captions[$key])) + { + return $captions[$key]; + } + else + { + return null; + } + } + + /** + * Get all captions + * + * @return array|null Array of {@see SimplePie_Caption} objects + */ + public function get_captions() + { + if ($this->captions !== null) + { + return $this->captions; + } + else + { + return null; + } + } + + /** + * Get a single category + * + * @param int $key + * @return SimplePie_Category|null + */ + public function get_category($key = 0) + { + $categories = $this->get_categories(); + if (isset($categories[$key])) + { + return $categories[$key]; + } + else + { + return null; + } + } + + /** + * Get all categories + * + * @return array|null Array of {@see SimplePie_Category} objects + */ + public function get_categories() + { + if ($this->categories !== null) + { + return $this->categories; + } + else + { + return null; + } + } + + /** + * Get the number of audio channels + * + * @return int|null + */ + public function get_channels() + { + if ($this->channels !== null) + { + return $this->channels; + } + else + { + return null; + } + } + + /** + * Get the copyright information + * + * @return SimplePie_Copyright|null + */ + public function get_copyright() + { + if ($this->copyright !== null) + { + return $this->copyright; + } + else + { + return null; + } + } + + /** + * Get a single credit + * + * @param int $key + * @return SimplePie_Credit|null + */ + public function get_credit($key = 0) + { + $credits = $this->get_credits(); + if (isset($credits[$key])) + { + return $credits[$key]; + } + else + { + return null; + } + } + + /** + * Get all credits + * + * @return array|null Array of {@see SimplePie_Credit} objects + */ + public function get_credits() + { + if ($this->credits !== null) + { + return $this->credits; + } + else + { + return null; + } + } + + /** + * Get the description of the enclosure + * + * @return string|null + */ + public function get_description() + { + if ($this->description !== null) + { + return $this->description; + } + else + { + return null; + } + } + + /** + * Get the duration of the enclosure + * + * @param string $convert Convert seconds into hh:mm:ss + * @return string|int|null 'hh:mm:ss' string if `$convert` was specified, otherwise integer (or null if none found) + */ + public function get_duration($convert = false) + { + if ($this->duration !== null) + { + if ($convert) + { + $time = SimplePie_Misc::time_hms($this->duration); + return $time; + } + else + { + return $this->duration; + } + } + else + { + return null; + } + } + + /** + * Get the expression + * + * @return string Probably one of 'sample', 'full', 'nonstop', 'clip'. Defaults to 'full' + */ + public function get_expression() + { + if ($this->expression !== null) + { + return $this->expression; + } + else + { + return 'full'; + } + } + + /** + * Get the file extension + * + * @return string|null + */ + public function get_extension() + { + if ($this->link !== null) + { + $url = SimplePie_Misc::parse_url($this->link); + if ($url['path'] !== '') + { + return pathinfo($url['path'], PATHINFO_EXTENSION); + } + } + return null; + } + + /** + * Get the framerate (in frames-per-second) + * + * @return string|null + */ + public function get_framerate() + { + if ($this->framerate !== null) + { + return $this->framerate; + } + else + { + return null; + } + } + + /** + * Get the preferred handler + * + * @return string|null One of 'flash', 'fmedia', 'quicktime', 'wmedia', 'mp3' + */ + public function get_handler() + { + return $this->get_real_type(true); + } + + /** + * Get a single hash + * + * @link http://www.rssboard.org/media-rss#media-hash + * @param int $key + * @return string|null Hash as per `media:hash`, prefixed with "$algo:" + */ + public function get_hash($key = 0) + { + $hashes = $this->get_hashes(); + if (isset($hashes[$key])) + { + return $hashes[$key]; + } + else + { + return null; + } + } + + /** + * Get all credits + * + * @return array|null Array of strings, see {@see get_hash()} + */ + public function get_hashes() + { + if ($this->hashes !== null) + { + return $this->hashes; + } + else + { + return null; + } + } + + /** + * Get the height + * + * @return string|null + */ + public function get_height() + { + if ($this->height !== null) + { + return $this->height; + } + else + { + return null; + } + } + + /** + * Get the language + * + * @link http://tools.ietf.org/html/rfc3066 + * @return string|null Language code as per RFC 3066 + */ + public function get_language() + { + if ($this->lang !== null) + { + return $this->lang; + } + else + { + return null; + } + } + + /** + * Get a single keyword + * + * @param int $key + * @return string|null + */ + public function get_keyword($key = 0) + { + $keywords = $this->get_keywords(); + if (isset($keywords[$key])) + { + return $keywords[$key]; + } + else + { + return null; + } + } + + /** + * Get all keywords + * + * @return array|null Array of strings + */ + public function get_keywords() + { + if ($this->keywords !== null) + { + return $this->keywords; + } + else + { + return null; + } + } + + /** + * Get length + * + * @return float Length in bytes + */ + public function get_length() + { + if ($this->length !== null) + { + return $this->length; + } + else + { + return null; + } + } + + /** + * Get the URL + * + * @return string|null + */ + public function get_link() + { + if ($this->link !== null) + { + return urldecode($this->link); + } + else + { + return null; + } + } + + /** + * Get the medium + * + * @link http://www.rssboard.org/media-rss#media-content + * @return string|null Should be one of 'image', 'audio', 'video', 'document', 'executable' + */ + public function get_medium() + { + if ($this->medium !== null) + { + return $this->medium; + } + else + { + return null; + } + } + + /** + * Get the player URL + * + * Typically the same as {@see get_permalink()} + * @return string|null Player URL + */ + public function get_player() + { + if ($this->player !== null) + { + return $this->player; + } + else + { + return null; + } + } + + /** + * Get a single rating + * + * @param int $key + * @return SimplePie_Rating|null + */ + public function get_rating($key = 0) + { + $ratings = $this->get_ratings(); + if (isset($ratings[$key])) + { + return $ratings[$key]; + } + else + { + return null; + } + } + + /** + * Get all ratings + * + * @return array|null Array of {@see SimplePie_Rating} objects + */ + public function get_ratings() + { + if ($this->ratings !== null) + { + return $this->ratings; + } + else + { + return null; + } + } + + /** + * Get a single restriction + * + * @param int $key + * @return SimplePie_Restriction|null + */ + public function get_restriction($key = 0) + { + $restrictions = $this->get_restrictions(); + if (isset($restrictions[$key])) + { + return $restrictions[$key]; + } + else + { + return null; + } + } + + /** + * Get all restrictions + * + * @return array|null Array of {@see SimplePie_Restriction} objects + */ + public function get_restrictions() + { + if ($this->restrictions !== null) + { + return $this->restrictions; + } + else + { + return null; + } + } + + /** + * Get the sampling rate (in kHz) + * + * @return string|null + */ + public function get_sampling_rate() + { + if ($this->samplingrate !== null) + { + return $this->samplingrate; + } + else + { + return null; + } + } + + /** + * Get the file size (in MiB) + * + * @return float|null File size in mebibytes (1048 bytes) + */ + public function get_size() + { + $length = $this->get_length(); + if ($length !== null) + { + return round($length/1048576, 2); + } + else + { + return null; + } + } + + /** + * Get a single thumbnail + * + * @param int $key + * @return string|null Thumbnail URL + */ + public function get_thumbnail($key = 0) + { + $thumbnails = $this->get_thumbnails(); + if (isset($thumbnails[$key])) + { + return $thumbnails[$key]; + } + else + { + return null; + } + } + + /** + * Get all thumbnails + * + * @return array|null Array of thumbnail URLs + */ + public function get_thumbnails() + { + if ($this->thumbnails !== null) + { + return $this->thumbnails; + } + else + { + return null; + } + } + + /** + * Get the title + * + * @return string|null + */ + public function get_title() + { + if ($this->title !== null) + { + return $this->title; + } + else + { + return null; + } + } + + /** + * Get mimetype of the enclosure + * + * @see get_real_type() + * @return string|null MIME type + */ + public function get_type() + { + if ($this->type !== null) + { + return $this->type; + } + else + { + return null; + } + } + + /** + * Get the width + * + * @return string|null + */ + public function get_width() + { + if ($this->width !== null) + { + return $this->width; + } + else + { + return null; + } + } + + /** + * Embed the enclosure using `` + * + * @deprecated Use the second parameter to {@see embed} instead + * + * @param array|string $options See first paramter to {@see embed} + * @return string HTML string to output + */ + public function native_embed($options='') + { + return $this->embed($options, true); + } + + /** + * Embed the enclosure using Javascript + * + * `$options` is an array or comma-separated key:value string, with the + * following properties: + * + * - `alt` (string): Alternate content for when an end-user does not have + * the appropriate handler installed or when a file type is + * unsupported. Can be any text or HTML. Defaults to blank. + * - `altclass` (string): If a file type is unsupported, the end-user will + * see the alt text (above) linked directly to the content. That link + * will have this value as its class name. Defaults to blank. + * - `audio` (string): This is an image that should be used as a + * placeholder for audio files before they're loaded (QuickTime-only). + * Can be any relative or absolute URL. Defaults to blank. + * - `bgcolor` (string): The background color for the media, if not + * already transparent. Defaults to `#ffffff`. + * - `height` (integer): The height of the embedded media. Accepts any + * numeric pixel value (such as `360`) or `auto`. Defaults to `auto`, + * and it is recommended that you use this default. + * - `loop` (boolean): Do you want the media to loop when its done? + * Defaults to `false`. + * - `mediaplayer` (string): The location of the included + * `mediaplayer.swf` file. This allows for the playback of Flash Video + * (`.flv`) files, and is the default handler for non-Odeo MP3's. + * Defaults to blank. + * - `video` (string): This is an image that should be used as a + * placeholder for video files before they're loaded (QuickTime-only). + * Can be any relative or absolute URL. Defaults to blank. + * - `width` (integer): The width of the embedded media. Accepts any + * numeric pixel value (such as `480`) or `auto`. Defaults to `auto`, + * and it is recommended that you use this default. + * - `widescreen` (boolean): Is the enclosure widescreen or standard? + * This applies only to video enclosures, and will automatically resize + * the content appropriately. Defaults to `false`, implying 4:3 mode. + * + * Note: Non-widescreen (4:3) mode with `width` and `height` set to `auto` + * will default to 480x360 video resolution. Widescreen (16:9) mode with + * `width` and `height` set to `auto` will default to 480x270 video resolution. + * + * @todo If the dimensions for media:content are defined, use them when width/height are set to 'auto'. + * @param array|string $options Comma-separated key:value list, or array + * @param bool $native Use `` + * @return string HTML string to output + */ + public function embed($options = '', $native = false) + { + // Set up defaults + $audio = ''; + $video = ''; + $alt = ''; + $altclass = ''; + $loop = 'false'; + $width = 'auto'; + $height = 'auto'; + $bgcolor = '#ffffff'; + $mediaplayer = ''; + $widescreen = false; + $handler = $this->get_handler(); + $type = $this->get_real_type(); + + // Process options and reassign values as necessary + if (is_array($options)) + { + extract($options); + } + else + { + $options = explode(',', $options); + foreach($options as $option) + { + $opt = explode(':', $option, 2); + if (isset($opt[0], $opt[1])) + { + $opt[0] = trim($opt[0]); + $opt[1] = trim($opt[1]); + switch ($opt[0]) + { + case 'audio': + $audio = $opt[1]; + break; + + case 'video': + $video = $opt[1]; + break; + + case 'alt': + $alt = $opt[1]; + break; + + case 'altclass': + $altclass = $opt[1]; + break; + + case 'loop': + $loop = $opt[1]; + break; + + case 'width': + $width = $opt[1]; + break; + + case 'height': + $height = $opt[1]; + break; + + case 'bgcolor': + $bgcolor = $opt[1]; + break; + + case 'mediaplayer': + $mediaplayer = $opt[1]; + break; + + case 'widescreen': + $widescreen = $opt[1]; + break; + } + } + } + } + + $mime = explode('/', $type, 2); + $mime = $mime[0]; + + // Process values for 'auto' + if ($width === 'auto') + { + if ($mime === 'video') + { + if ($height === 'auto') + { + $width = 480; + } + elseif ($widescreen) + { + $width = round((intval($height)/9)*16); + } + else + { + $width = round((intval($height)/3)*4); + } + } + else + { + $width = '100%'; + } + } + + if ($height === 'auto') + { + if ($mime === 'audio') + { + $height = 0; + } + elseif ($mime === 'video') + { + if ($width === 'auto') + { + if ($widescreen) + { + $height = 270; + } + else + { + $height = 360; + } + } + elseif ($widescreen) + { + $height = round((intval($width)/16)*9); + } + else + { + $height = round((intval($width)/4)*3); + } + } + else + { + $height = 376; + } + } + elseif ($mime === 'audio') + { + $height = 0; + } + + // Set proper placeholder value + if ($mime === 'audio') + { + $placeholder = $audio; + } + elseif ($mime === 'video') + { + $placeholder = $video; + } + + $embed = ''; + + // Flash + if ($handler === 'flash') + { + if ($native) + { + $embed .= "get_link() . "\" pluginspage=\"http://adobe.com/go/getflashplayer\" type=\"$type\" quality=\"high\" width=\"$width\" height=\"$height\" bgcolor=\"$bgcolor\" loop=\"$loop\">"; + } + else + { + $embed .= ""; + } + } + + // Flash Media Player file types. + // Preferred handler for MP3 file types. + elseif ($handler === 'fmedia' || ($handler === 'mp3' && $mediaplayer !== '')) + { + $height += 20; + if ($native) + { + $embed .= "get_link().'?file_extension=.'.$this->get_extension()) . "&autostart=false&repeat=$loop&showdigits=true&showfsbutton=false\">"; + } + else + { + $embed .= ""; + } + } + + // QuickTime 7 file types. Need to test with QuickTime 6. + // Only handle MP3's if the Flash Media Player is not present. + elseif ($handler === 'quicktime' || ($handler === 'mp3' && $mediaplayer === '')) + { + $height += 16; + if ($native) + { + if ($placeholder !== '') + { + $embed .= "get_link() . "\" src=\"$placeholder\" width=\"$width\" height=\"$height\" autoplay=\"false\" target=\"myself\" controller=\"false\" loop=\"$loop\" scale=\"aspect\" bgcolor=\"$bgcolor\" pluginspage=\"http://apple.com/quicktime/download/\">"; + } + else + { + $embed .= "get_link() . "\" width=\"$width\" height=\"$height\" autoplay=\"false\" target=\"myself\" controller=\"true\" loop=\"$loop\" scale=\"aspect\" bgcolor=\"$bgcolor\" pluginspage=\"http://apple.com/quicktime/download/\">"; + } + } + else + { + $embed .= ""; + } + } + + // Windows Media + elseif ($handler === 'wmedia') + { + $height += 45; + if ($native) + { + $embed .= "get_link() . "\" autosize=\"1\" width=\"$width\" height=\"$height\" showcontrols=\"1\" showstatusbar=\"0\" showdisplay=\"0\" autostart=\"0\">"; + } + else + { + $embed .= ""; + } + } + + // Everything else + else $embed .= '' . $alt . ''; + + return $embed; + } + + /** + * Get the real media type + * + * Often, feeds lie to us, necessitating a bit of deeper inspection. This + * converts types to their canonical representations based on the file + * extension + * + * @see get_type() + * @param bool $find_handler Internal use only, use {@see get_handler()} instead + * @return string MIME type + */ + public function get_real_type($find_handler = false) + { + // Mime-types by handler. + $types_flash = array('application/x-shockwave-flash', 'application/futuresplash'); // Flash + $types_fmedia = array('video/flv', 'video/x-flv','flv-application/octet-stream'); // Flash Media Player + $types_quicktime = array('audio/3gpp', 'audio/3gpp2', 'audio/aac', 'audio/x-aac', 'audio/aiff', 'audio/x-aiff', 'audio/mid', 'audio/midi', 'audio/x-midi', 'audio/mp4', 'audio/m4a', 'audio/x-m4a', 'audio/wav', 'audio/x-wav', 'video/3gpp', 'video/3gpp2', 'video/m4v', 'video/x-m4v', 'video/mp4', 'video/mpeg', 'video/x-mpeg', 'video/quicktime', 'video/sd-video'); // QuickTime + $types_wmedia = array('application/asx', 'application/x-mplayer2', 'audio/x-ms-wma', 'audio/x-ms-wax', 'video/x-ms-asf-plugin', 'video/x-ms-asf', 'video/x-ms-wm', 'video/x-ms-wmv', 'video/x-ms-wvx'); // Windows Media + $types_mp3 = array('audio/mp3', 'audio/x-mp3', 'audio/mpeg', 'audio/x-mpeg'); // MP3 + + if ($this->get_type() !== null) + { + $type = strtolower($this->type); + } + else + { + $type = null; + } + + // If we encounter an unsupported mime-type, check the file extension and guess intelligently. + if (!in_array($type, array_merge($types_flash, $types_fmedia, $types_quicktime, $types_wmedia, $types_mp3))) + { + switch (strtolower($this->get_extension())) + { + // Audio mime-types + case 'aac': + case 'adts': + $type = 'audio/acc'; + break; + + case 'aif': + case 'aifc': + case 'aiff': + case 'cdda': + $type = 'audio/aiff'; + break; + + case 'bwf': + $type = 'audio/wav'; + break; + + case 'kar': + case 'mid': + case 'midi': + case 'smf': + $type = 'audio/midi'; + break; + + case 'm4a': + $type = 'audio/x-m4a'; + break; + + case 'mp3': + case 'swa': + $type = 'audio/mp3'; + break; + + case 'wav': + $type = 'audio/wav'; + break; + + case 'wax': + $type = 'audio/x-ms-wax'; + break; + + case 'wma': + $type = 'audio/x-ms-wma'; + break; + + // Video mime-types + case '3gp': + case '3gpp': + $type = 'video/3gpp'; + break; + + case '3g2': + case '3gp2': + $type = 'video/3gpp2'; + break; + + case 'asf': + $type = 'video/x-ms-asf'; + break; + + case 'flv': + $type = 'video/x-flv'; + break; + + case 'm1a': + case 'm1s': + case 'm1v': + case 'm15': + case 'm75': + case 'mp2': + case 'mpa': + case 'mpeg': + case 'mpg': + case 'mpm': + case 'mpv': + $type = 'video/mpeg'; + break; + + case 'm4v': + $type = 'video/x-m4v'; + break; + + case 'mov': + case 'qt': + $type = 'video/quicktime'; + break; + + case 'mp4': + case 'mpg4': + $type = 'video/mp4'; + break; + + case 'sdv': + $type = 'video/sd-video'; + break; + + case 'wm': + $type = 'video/x-ms-wm'; + break; + + case 'wmv': + $type = 'video/x-ms-wmv'; + break; + + case 'wvx': + $type = 'video/x-ms-wvx'; + break; + + // Flash mime-types + case 'spl': + $type = 'application/futuresplash'; + break; + + case 'swf': + $type = 'application/x-shockwave-flash'; + break; + } + } + + if ($find_handler) + { + if (in_array($type, $types_flash)) + { + return 'flash'; + } + elseif (in_array($type, $types_fmedia)) + { + return 'fmedia'; + } + elseif (in_array($type, $types_quicktime)) + { + return 'quicktime'; + } + elseif (in_array($type, $types_wmedia)) + { + return 'wmedia'; + } + elseif (in_array($type, $types_mp3)) + { + return 'mp3'; + } + else + { + return null; + } + } + else + { + return $type; + } + } +} + diff --git a/wp-includes/SimplePie/Exception.php b/wp-includes/SimplePie/Exception.php new file mode 100644 index 0000000..73e104d --- /dev/null +++ b/wp-includes/SimplePie/Exception.php @@ -0,0 +1,52 @@ +encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']); + } + $this->url = $url; + $this->useragent = $useragent; + if (preg_match('/^http(s)?:\/\//i', $url)) + { + if ($useragent === null) + { + $useragent = ini_get('user_agent'); + $this->useragent = $useragent; + } + if (!is_array($headers)) + { + $headers = array(); + } + if (!$force_fsockopen && function_exists('curl_exec')) + { + $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_CURL; + $fp = curl_init(); + $headers2 = array(); + foreach ($headers as $key => $value) + { + $headers2[] = "$key: $value"; + } + if (version_compare(SimplePie_Misc::get_curl_version(), '7.10.5', '>=')) + { + curl_setopt($fp, CURLOPT_ENCODING, ''); + } + curl_setopt($fp, CURLOPT_URL, $url); + curl_setopt($fp, CURLOPT_HEADER, 1); + curl_setopt($fp, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($fp, CURLOPT_TIMEOUT, $timeout); + curl_setopt($fp, CURLOPT_CONNECTTIMEOUT, $timeout); + curl_setopt($fp, CURLOPT_REFERER, $url); + curl_setopt($fp, CURLOPT_USERAGENT, $useragent); + curl_setopt($fp, CURLOPT_HTTPHEADER, $headers2); + if (!ini_get('open_basedir') && !ini_get('safe_mode') && version_compare(SimplePie_Misc::get_curl_version(), '7.15.2', '>=')) + { + curl_setopt($fp, CURLOPT_FOLLOWLOCATION, 1); + curl_setopt($fp, CURLOPT_MAXREDIRS, $redirects); + } + + $this->headers = curl_exec($fp); + if (curl_errno($fp) === 23 || curl_errno($fp) === 61) + { + curl_setopt($fp, CURLOPT_ENCODING, 'none'); + $this->headers = curl_exec($fp); + } + if (curl_errno($fp)) + { + $this->error = 'cURL error ' . curl_errno($fp) . ': ' . curl_error($fp); + $this->success = false; + } + else + { + $info = curl_getinfo($fp); + curl_close($fp); + $this->headers = explode("\r\n\r\n", $this->headers, $info['redirect_count'] + 1); + $this->headers = array_pop($this->headers); + $parser = new SimplePie_HTTP_Parser($this->headers); + if ($parser->parse()) + { + $this->headers = $parser->headers; + $this->body = $parser->body; + $this->status_code = $parser->status_code; + if ((in_array($this->status_code, array(300, 301, 302, 303, 307)) || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects) + { + $this->redirects++; + $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url); + return $this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen); + } + } + } + } + else + { + $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_FSOCKOPEN; + $url_parts = parse_url($url); + $socket_host = $url_parts['host']; + if (isset($url_parts['scheme']) && strtolower($url_parts['scheme']) === 'https') + { + $socket_host = "ssl://$url_parts[host]"; + $url_parts['port'] = 443; + } + if (!isset($url_parts['port'])) + { + $url_parts['port'] = 80; + } + $fp = @fsockopen($socket_host, $url_parts['port'], $errno, $errstr, $timeout); + if (!$fp) + { + $this->error = 'fsockopen error: ' . $errstr; + $this->success = false; + } + else + { + stream_set_timeout($fp, $timeout); + if (isset($url_parts['path'])) + { + if (isset($url_parts['query'])) + { + $get = "$url_parts[path]?$url_parts[query]"; + } + else + { + $get = $url_parts['path']; + } + } + else + { + $get = '/'; + } + $out = "GET $get HTTP/1.1\r\n"; + $out .= "Host: $url_parts[host]\r\n"; + $out .= "User-Agent: $useragent\r\n"; + if (extension_loaded('zlib')) + { + $out .= "Accept-Encoding: x-gzip,gzip,deflate\r\n"; + } + + if (isset($url_parts['user']) && isset($url_parts['pass'])) + { + $out .= "Authorization: Basic " . base64_encode("$url_parts[user]:$url_parts[pass]") . "\r\n"; + } + foreach ($headers as $key => $value) + { + $out .= "$key: $value\r\n"; + } + $out .= "Connection: Close\r\n\r\n"; + fwrite($fp, $out); + + $info = stream_get_meta_data($fp); + + $this->headers = ''; + while (!$info['eof'] && !$info['timed_out']) + { + $this->headers .= fread($fp, 1160); + $info = stream_get_meta_data($fp); + } + if (!$info['timed_out']) + { + $parser = new SimplePie_HTTP_Parser($this->headers); + if ($parser->parse()) + { + $this->headers = $parser->headers; + $this->body = $parser->body; + $this->status_code = $parser->status_code; + if ((in_array($this->status_code, array(300, 301, 302, 303, 307)) || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects) + { + $this->redirects++; + $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url); + return $this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen); + } + if (isset($this->headers['content-encoding'])) + { + // Hey, we act dumb elsewhere, so let's do that here too + switch (strtolower(trim($this->headers['content-encoding'], "\x09\x0A\x0D\x20"))) + { + case 'gzip': + case 'x-gzip': + $decoder = new SimplePie_gzdecode($this->body); + if (!$decoder->parse()) + { + $this->error = 'Unable to decode HTTP "gzip" stream'; + $this->success = false; + } + else + { + $this->body = $decoder->data; + } + break; + + case 'deflate': + if (($decompressed = gzinflate($this->body)) !== false) + { + $this->body = $decompressed; + } + else if (($decompressed = gzuncompress($this->body)) !== false) + { + $this->body = $decompressed; + } + else if (function_exists('gzdecode') && ($decompressed = gzdecode($this->body)) !== false) + { + $this->body = $decompressed; + } + else + { + $this->error = 'Unable to decode HTTP "deflate" stream'; + $this->success = false; + } + break; + + default: + $this->error = 'Unknown content coding'; + $this->success = false; + } + } + } + } + else + { + $this->error = 'fsocket timed out'; + $this->success = false; + } + fclose($fp); + } + } + } + else + { + $this->method = SIMPLEPIE_FILE_SOURCE_LOCAL | SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS; + if (!$this->body = file_get_contents($url)) + { + $this->error = 'file_get_contents could not read the file'; + $this->success = false; + } + } + } +} diff --git a/wp-includes/SimplePie/HTTP/Parser.php b/wp-includes/SimplePie/HTTP/Parser.php new file mode 100644 index 0000000..bff2222 --- /dev/null +++ b/wp-includes/SimplePie/HTTP/Parser.php @@ -0,0 +1,500 @@ +data = $data; + $this->data_length = strlen($this->data); + } + + /** + * Parse the input data + * + * @return bool true on success, false on failure + */ + public function parse() + { + while ($this->state && $this->state !== 'emit' && $this->has_data()) + { + $state = $this->state; + $this->$state(); + } + $this->data = ''; + if ($this->state === 'emit' || $this->state === 'body') + { + return true; + } + else + { + $this->http_version = ''; + $this->status_code = ''; + $this->reason = ''; + $this->headers = array(); + $this->body = ''; + return false; + } + } + + /** + * Check whether there is data beyond the pointer + * + * @return bool true if there is further data, false if not + */ + protected function has_data() + { + return (bool) ($this->position < $this->data_length); + } + + /** + * See if the next character is LWS + * + * @return bool true if the next character is LWS, false if not + */ + protected function is_linear_whitespace() + { + return (bool) ($this->data[$this->position] === "\x09" + || $this->data[$this->position] === "\x20" + || ($this->data[$this->position] === "\x0A" + && isset($this->data[$this->position + 1]) + && ($this->data[$this->position + 1] === "\x09" || $this->data[$this->position + 1] === "\x20"))); + } + + /** + * Parse the HTTP version + */ + protected function http_version() + { + if (strpos($this->data, "\x0A") !== false && strtoupper(substr($this->data, 0, 5)) === 'HTTP/') + { + $len = strspn($this->data, '0123456789.', 5); + $this->http_version = substr($this->data, 5, $len); + $this->position += 5 + $len; + if (substr_count($this->http_version, '.') <= 1) + { + $this->http_version = (float) $this->http_version; + $this->position += strspn($this->data, "\x09\x20", $this->position); + $this->state = 'status'; + } + else + { + $this->state = false; + } + } + else + { + $this->state = false; + } + } + + /** + * Parse the status code + */ + protected function status() + { + if ($len = strspn($this->data, '0123456789', $this->position)) + { + $this->status_code = (int) substr($this->data, $this->position, $len); + $this->position += $len; + $this->state = 'reason'; + } + else + { + $this->state = false; + } + } + + /** + * Parse the reason phrase + */ + protected function reason() + { + $len = strcspn($this->data, "\x0A", $this->position); + $this->reason = trim(substr($this->data, $this->position, $len), "\x09\x0D\x20"); + $this->position += $len + 1; + $this->state = 'new_line'; + } + + /** + * Deal with a new line, shifting data around as needed + */ + protected function new_line() + { + $this->value = trim($this->value, "\x0D\x20"); + if ($this->name !== '' && $this->value !== '') + { + $this->name = strtolower($this->name); + // We should only use the last Content-Type header. c.f. issue #1 + if (isset($this->headers[$this->name]) && $this->name !== 'content-type') + { + $this->headers[$this->name] .= ', ' . $this->value; + } + else + { + $this->headers[$this->name] = $this->value; + } + } + $this->name = ''; + $this->value = ''; + if (substr($this->data[$this->position], 0, 2) === "\x0D\x0A") + { + $this->position += 2; + $this->state = 'body'; + } + elseif ($this->data[$this->position] === "\x0A") + { + $this->position++; + $this->state = 'body'; + } + else + { + $this->state = 'name'; + } + } + + /** + * Parse a header name + */ + protected function name() + { + $len = strcspn($this->data, "\x0A:", $this->position); + if (isset($this->data[$this->position + $len])) + { + if ($this->data[$this->position + $len] === "\x0A") + { + $this->position += $len; + $this->state = 'new_line'; + } + else + { + $this->name = substr($this->data, $this->position, $len); + $this->position += $len + 1; + $this->state = 'value'; + } + } + else + { + $this->state = false; + } + } + + /** + * Parse LWS, replacing consecutive LWS characters with a single space + */ + protected function linear_whitespace() + { + do + { + if (substr($this->data, $this->position, 2) === "\x0D\x0A") + { + $this->position += 2; + } + elseif ($this->data[$this->position] === "\x0A") + { + $this->position++; + } + $this->position += strspn($this->data, "\x09\x20", $this->position); + } while ($this->has_data() && $this->is_linear_whitespace()); + $this->value .= "\x20"; + } + + /** + * See what state to move to while within non-quoted header values + */ + protected function value() + { + if ($this->is_linear_whitespace()) + { + $this->linear_whitespace(); + } + else + { + switch ($this->data[$this->position]) + { + case '"': + // Workaround for ETags: we have to include the quotes as + // part of the tag. + if (strtolower($this->name) === 'etag') + { + $this->value .= '"'; + $this->position++; + $this->state = 'value_char'; + break; + } + $this->position++; + $this->state = 'quote'; + break; + + case "\x0A": + $this->position++; + $this->state = 'new_line'; + break; + + default: + $this->state = 'value_char'; + break; + } + } + } + + /** + * Parse a header value while outside quotes + */ + protected function value_char() + { + $len = strcspn($this->data, "\x09\x20\x0A\"", $this->position); + $this->value .= substr($this->data, $this->position, $len); + $this->position += $len; + $this->state = 'value'; + } + + /** + * See what state to move to while within quoted header values + */ + protected function quote() + { + if ($this->is_linear_whitespace()) + { + $this->linear_whitespace(); + } + else + { + switch ($this->data[$this->position]) + { + case '"': + $this->position++; + $this->state = 'value'; + break; + + case "\x0A": + $this->position++; + $this->state = 'new_line'; + break; + + case '\\': + $this->position++; + $this->state = 'quote_escaped'; + break; + + default: + $this->state = 'quote_char'; + break; + } + } + } + + /** + * Parse a header value while within quotes + */ + protected function quote_char() + { + $len = strcspn($this->data, "\x09\x20\x0A\"\\", $this->position); + $this->value .= substr($this->data, $this->position, $len); + $this->position += $len; + $this->state = 'value'; + } + + /** + * Parse an escaped character within quotes + */ + protected function quote_escaped() + { + $this->value .= $this->data[$this->position]; + $this->position++; + $this->state = 'quote'; + } + + /** + * Parse the body + */ + protected function body() + { + $this->body = substr($this->data, $this->position); + if (!empty($this->headers['transfer-encoding'])) + { + unset($this->headers['transfer-encoding']); + $this->state = 'chunked'; + } + else + { + $this->state = 'emit'; + } + } + + /** + * Parsed a "Transfer-Encoding: chunked" body + */ + protected function chunked() + { + if (!preg_match('/^([0-9a-f]+)[^\r\n]*\r\n/i', trim($this->body))) + { + $this->state = 'emit'; + return; + } + + $decoded = ''; + $encoded = $this->body; + + while (true) + { + $is_chunked = (bool) preg_match( '/^([0-9a-f]+)[^\r\n]*\r\n/i', $encoded, $matches ); + if (!$is_chunked) + { + // Looks like it's not chunked after all + $this->state = 'emit'; + return; + } + + $length = hexdec(trim($matches[1])); + if ($length === 0) + { + // Ignore trailer headers + $this->state = 'emit'; + $this->body = $decoded; + return; + } + + $chunk_length = strlen($matches[0]); + $decoded .= $part = substr($encoded, $chunk_length, $length); + $encoded = substr($encoded, $chunk_length + $length + 2); + + if (trim($encoded) === '0' || empty($encoded)) + { + $this->state = 'emit'; + $this->body = $decoded; + return; + } + } + } +} diff --git a/wp-includes/SimplePie/IRI.php b/wp-includes/SimplePie/IRI.php new file mode 100644 index 0000000..d3198c0 --- /dev/null +++ b/wp-includes/SimplePie/IRI.php @@ -0,0 +1,1238 @@ + array( + 'port' => 674 + ), + 'dict' => array( + 'port' => 2628 + ), + 'file' => array( + 'ihost' => 'localhost' + ), + 'http' => array( + 'port' => 80, + 'ipath' => '/' + ), + 'https' => array( + 'port' => 443, + 'ipath' => '/' + ), + ); + + /** + * Return the entire IRI when you try and read the object as a string + * + * @return string + */ + public function __toString() + { + return $this->get_iri(); + } + + /** + * Overload __set() to provide access via properties + * + * @param string $name Property name + * @param mixed $value Property value + */ + public function __set($name, $value) + { + if (method_exists($this, 'set_' . $name)) + { + call_user_func(array($this, 'set_' . $name), $value); + } + elseif ( + $name === 'iauthority' + || $name === 'iuserinfo' + || $name === 'ihost' + || $name === 'ipath' + || $name === 'iquery' + || $name === 'ifragment' + ) + { + call_user_func(array($this, 'set_' . substr($name, 1)), $value); + } + } + + /** + * Overload __get() to provide access via properties + * + * @param string $name Property name + * @return mixed + */ + public function __get($name) + { + // isset() returns false for null, we don't want to do that + // Also why we use array_key_exists below instead of isset() + $props = get_object_vars($this); + + if ( + $name === 'iri' || + $name === 'uri' || + $name === 'iauthority' || + $name === 'authority' + ) + { + $return = $this->{"get_$name"}(); + } + elseif (array_key_exists($name, $props)) + { + $return = $this->$name; + } + // host -> ihost + elseif (($prop = 'i' . $name) && array_key_exists($prop, $props)) + { + $name = $prop; + $return = $this->$prop; + } + // ischeme -> scheme + elseif (($prop = substr($name, 1)) && array_key_exists($prop, $props)) + { + $name = $prop; + $return = $this->$prop; + } + else + { + trigger_error('Undefined property: ' . get_class($this) . '::' . $name, E_USER_NOTICE); + $return = null; + } + + if ($return === null && isset($this->normalization[$this->scheme][$name])) + { + return $this->normalization[$this->scheme][$name]; + } + else + { + return $return; + } + } + + /** + * Overload __isset() to provide access via properties + * + * @param string $name Property name + * @return bool + */ + public function __isset($name) + { + if (method_exists($this, 'get_' . $name) || isset($this->$name)) + { + return true; + } + else + { + return false; + } + } + + /** + * Overload __unset() to provide access via properties + * + * @param string $name Property name + */ + public function __unset($name) + { + if (method_exists($this, 'set_' . $name)) + { + call_user_func(array($this, 'set_' . $name), ''); + } + } + + /** + * Create a new IRI object, from a specified string + * + * @param string $iri + */ + public function __construct($iri = null) + { + $this->set_iri($iri); + } + + /** + * Create a new IRI object by resolving a relative IRI + * + * Returns false if $base is not absolute, otherwise an IRI. + * + * @param IRI|string $base (Absolute) Base IRI + * @param IRI|string $relative Relative IRI + * @return IRI|false + */ + public static function absolutize($base, $relative) + { + if (!($relative instanceof SimplePie_IRI)) + { + $relative = new SimplePie_IRI($relative); + } + if (!$relative->is_valid()) + { + return false; + } + elseif ($relative->scheme !== null) + { + return clone $relative; + } + else + { + if (!($base instanceof SimplePie_IRI)) + { + $base = new SimplePie_IRI($base); + } + if ($base->scheme !== null && $base->is_valid()) + { + if ($relative->get_iri() !== '') + { + if ($relative->iuserinfo !== null || $relative->ihost !== null || $relative->port !== null) + { + $target = clone $relative; + $target->scheme = $base->scheme; + } + else + { + $target = new SimplePie_IRI; + $target->scheme = $base->scheme; + $target->iuserinfo = $base->iuserinfo; + $target->ihost = $base->ihost; + $target->port = $base->port; + if ($relative->ipath !== '') + { + if ($relative->ipath[0] === '/') + { + $target->ipath = $relative->ipath; + } + elseif (($base->iuserinfo !== null || $base->ihost !== null || $base->port !== null) && $base->ipath === '') + { + $target->ipath = '/' . $relative->ipath; + } + elseif (($last_segment = strrpos($base->ipath, '/')) !== false) + { + $target->ipath = substr($base->ipath, 0, $last_segment + 1) . $relative->ipath; + } + else + { + $target->ipath = $relative->ipath; + } + $target->ipath = $target->remove_dot_segments($target->ipath); + $target->iquery = $relative->iquery; + } + else + { + $target->ipath = $base->ipath; + if ($relative->iquery !== null) + { + $target->iquery = $relative->iquery; + } + elseif ($base->iquery !== null) + { + $target->iquery = $base->iquery; + } + } + $target->ifragment = $relative->ifragment; + } + } + else + { + $target = clone $base; + $target->ifragment = null; + } + $target->scheme_normalization(); + return $target; + } + else + { + return false; + } + } + } + + /** + * Parse an IRI into scheme/authority/path/query/fragment segments + * + * @param string $iri + * @return array + */ + protected function parse_iri($iri) + { + $iri = trim($iri, "\x20\x09\x0A\x0C\x0D"); + if (preg_match('/^((?P[^:\/?#]+):)?(\/\/(?P[^\/?#]*))?(?P[^?#]*)(\?(?P[^#]*))?(#(?P.*))?$/', $iri, $match)) + { + if ($match[1] === '') + { + $match['scheme'] = null; + } + if (!isset($match[3]) || $match[3] === '') + { + $match['authority'] = null; + } + if (!isset($match[5])) + { + $match['path'] = ''; + } + if (!isset($match[6]) || $match[6] === '') + { + $match['query'] = null; + } + if (!isset($match[8]) || $match[8] === '') + { + $match['fragment'] = null; + } + return $match; + } + else + { + // This can occur when a paragraph is accidentally parsed as a URI + return false; + } + } + + /** + * Remove dot segments from a path + * + * @param string $input + * @return string + */ + protected function remove_dot_segments($input) + { + $output = ''; + while (strpos($input, './') !== false || strpos($input, '/.') !== false || $input === '.' || $input === '..') + { + // A: If the input buffer begins with a prefix of "../" or "./", then remove that prefix from the input buffer; otherwise, + if (strpos($input, '../') === 0) + { + $input = substr($input, 3); + } + elseif (strpos($input, './') === 0) + { + $input = substr($input, 2); + } + // B: if the input buffer begins with a prefix of "/./" or "/.", where "." is a complete path segment, then replace that prefix with "/" in the input buffer; otherwise, + elseif (strpos($input, '/./') === 0) + { + $input = substr($input, 2); + } + elseif ($input === '/.') + { + $input = '/'; + } + // C: if the input buffer begins with a prefix of "/../" or "/..", where ".." is a complete path segment, then replace that prefix with "/" in the input buffer and remove the last segment and its preceding "/" (if any) from the output buffer; otherwise, + elseif (strpos($input, '/../') === 0) + { + $input = substr($input, 3); + $output = substr_replace($output, '', strrpos($output, '/')); + } + elseif ($input === '/..') + { + $input = '/'; + $output = substr_replace($output, '', strrpos($output, '/')); + } + // D: if the input buffer consists only of "." or "..", then remove that from the input buffer; otherwise, + elseif ($input === '.' || $input === '..') + { + $input = ''; + } + // E: move the first path segment in the input buffer to the end of the output buffer, including the initial "/" character (if any) and any subsequent characters up to, but not including, the next "/" character or the end of the input buffer + elseif (($pos = strpos($input, '/', 1)) !== false) + { + $output .= substr($input, 0, $pos); + $input = substr_replace($input, '', 0, $pos); + } + else + { + $output .= $input; + $input = ''; + } + } + return $output . $input; + } + + /** + * Replace invalid character with percent encoding + * + * @param string $string Input string + * @param string $extra_chars Valid characters not in iunreserved or + * iprivate (this is ASCII-only) + * @param bool $iprivate Allow iprivate + * @return string + */ + protected function replace_invalid_with_pct_encoding($string, $extra_chars, $iprivate = false) + { + // Normalize as many pct-encoded sections as possible + $string = preg_replace_callback('/(?:%[A-Fa-f0-9]{2})+/', array($this, 'remove_iunreserved_percent_encoded'), $string); + + // Replace invalid percent characters + $string = preg_replace('/%(?![A-Fa-f0-9]{2})/', '%25', $string); + + // Add unreserved and % to $extra_chars (the latter is safe because all + // pct-encoded sections are now valid). + $extra_chars .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~%'; + + // Now replace any bytes that aren't allowed with their pct-encoded versions + $position = 0; + $strlen = strlen($string); + while (($position += strspn($string, $extra_chars, $position)) < $strlen) + { + $value = ord($string[$position]); + + // Start position + $start = $position; + + // By default we are valid + $valid = true; + + // No one byte sequences are valid due to the while. + // Two byte sequence: + if (($value & 0xE0) === 0xC0) + { + $character = ($value & 0x1F) << 6; + $length = 2; + $remaining = 1; + } + // Three byte sequence: + elseif (($value & 0xF0) === 0xE0) + { + $character = ($value & 0x0F) << 12; + $length = 3; + $remaining = 2; + } + // Four byte sequence: + elseif (($value & 0xF8) === 0xF0) + { + $character = ($value & 0x07) << 18; + $length = 4; + $remaining = 3; + } + // Invalid byte: + else + { + $valid = false; + $length = 1; + $remaining = 0; + } + + if ($remaining) + { + if ($position + $length <= $strlen) + { + for ($position++; $remaining; $position++) + { + $value = ord($string[$position]); + + // Check that the byte is valid, then add it to the character: + if (($value & 0xC0) === 0x80) + { + $character |= ($value & 0x3F) << (--$remaining * 6); + } + // If it is invalid, count the sequence as invalid and reprocess the current byte: + else + { + $valid = false; + $position--; + break; + } + } + } + else + { + $position = $strlen - 1; + $valid = false; + } + } + + // Percent encode anything invalid or not in ucschar + if ( + // Invalid sequences + !$valid + // Non-shortest form sequences are invalid + || $length > 1 && $character <= 0x7F + || $length > 2 && $character <= 0x7FF + || $length > 3 && $character <= 0xFFFF + // Outside of range of ucschar codepoints + // Noncharacters + || ($character & 0xFFFE) === 0xFFFE + || $character >= 0xFDD0 && $character <= 0xFDEF + || ( + // Everything else not in ucschar + $character > 0xD7FF && $character < 0xF900 + || $character < 0xA0 + || $character > 0xEFFFD + ) + && ( + // Everything not in iprivate, if it applies + !$iprivate + || $character < 0xE000 + || $character > 0x10FFFD + ) + ) + { + // If we were a character, pretend we weren't, but rather an error. + if ($valid) + $position--; + + for ($j = $start; $j <= $position; $j++) + { + $string = substr_replace($string, sprintf('%%%02X', ord($string[$j])), $j, 1); + $j += 2; + $position += 2; + $strlen += 2; + } + } + } + + return $string; + } + + /** + * Callback function for preg_replace_callback. + * + * Removes sequences of percent encoded bytes that represent UTF-8 + * encoded characters in iunreserved + * + * @param array $match PCRE match + * @return string Replacement + */ + protected function remove_iunreserved_percent_encoded($match) + { + // As we just have valid percent encoded sequences we can just explode + // and ignore the first member of the returned array (an empty string). + $bytes = explode('%', $match[0]); + + // Initialize the new string (this is what will be returned) and that + // there are no bytes remaining in the current sequence (unsurprising + // at the first byte!). + $string = ''; + $remaining = 0; + + // Loop over each and every byte, and set $value to its value + for ($i = 1, $len = count($bytes); $i < $len; $i++) + { + $value = hexdec($bytes[$i]); + + // If we're the first byte of sequence: + if (!$remaining) + { + // Start position + $start = $i; + + // By default we are valid + $valid = true; + + // One byte sequence: + if ($value <= 0x7F) + { + $character = $value; + $length = 1; + } + // Two byte sequence: + elseif (($value & 0xE0) === 0xC0) + { + $character = ($value & 0x1F) << 6; + $length = 2; + $remaining = 1; + } + // Three byte sequence: + elseif (($value & 0xF0) === 0xE0) + { + $character = ($value & 0x0F) << 12; + $length = 3; + $remaining = 2; + } + // Four byte sequence: + elseif (($value & 0xF8) === 0xF0) + { + $character = ($value & 0x07) << 18; + $length = 4; + $remaining = 3; + } + // Invalid byte: + else + { + $valid = false; + $remaining = 0; + } + } + // Continuation byte: + else + { + // Check that the byte is valid, then add it to the character: + if (($value & 0xC0) === 0x80) + { + $remaining--; + $character |= ($value & 0x3F) << ($remaining * 6); + } + // If it is invalid, count the sequence as invalid and reprocess the current byte as the start of a sequence: + else + { + $valid = false; + $remaining = 0; + $i--; + } + } + + // If we've reached the end of the current byte sequence, append it to Unicode::$data + if (!$remaining) + { + // Percent encode anything invalid or not in iunreserved + if ( + // Invalid sequences + !$valid + // Non-shortest form sequences are invalid + || $length > 1 && $character <= 0x7F + || $length > 2 && $character <= 0x7FF + || $length > 3 && $character <= 0xFFFF + // Outside of range of iunreserved codepoints + || $character < 0x2D + || $character > 0xEFFFD + // Noncharacters + || ($character & 0xFFFE) === 0xFFFE + || $character >= 0xFDD0 && $character <= 0xFDEF + // Everything else not in iunreserved (this is all BMP) + || $character === 0x2F + || $character > 0x39 && $character < 0x41 + || $character > 0x5A && $character < 0x61 + || $character > 0x7A && $character < 0x7E + || $character > 0x7E && $character < 0xA0 + || $character > 0xD7FF && $character < 0xF900 + ) + { + for ($j = $start; $j <= $i; $j++) + { + $string .= '%' . strtoupper($bytes[$j]); + } + } + else + { + for ($j = $start; $j <= $i; $j++) + { + $string .= chr(hexdec($bytes[$j])); + } + } + } + } + + // If we have any bytes left over they are invalid (i.e., we are + // mid-way through a multi-byte sequence) + if ($remaining) + { + for ($j = $start; $j < $len; $j++) + { + $string .= '%' . strtoupper($bytes[$j]); + } + } + + return $string; + } + + protected function scheme_normalization() + { + if (isset($this->normalization[$this->scheme]['iuserinfo']) && $this->iuserinfo === $this->normalization[$this->scheme]['iuserinfo']) + { + $this->iuserinfo = null; + } + if (isset($this->normalization[$this->scheme]['ihost']) && $this->ihost === $this->normalization[$this->scheme]['ihost']) + { + $this->ihost = null; + } + if (isset($this->normalization[$this->scheme]['port']) && $this->port === $this->normalization[$this->scheme]['port']) + { + $this->port = null; + } + if (isset($this->normalization[$this->scheme]['ipath']) && $this->ipath === $this->normalization[$this->scheme]['ipath']) + { + $this->ipath = ''; + } + if (isset($this->normalization[$this->scheme]['iquery']) && $this->iquery === $this->normalization[$this->scheme]['iquery']) + { + $this->iquery = null; + } + if (isset($this->normalization[$this->scheme]['ifragment']) && $this->ifragment === $this->normalization[$this->scheme]['ifragment']) + { + $this->ifragment = null; + } + } + + /** + * Check if the object represents a valid IRI. This needs to be done on each + * call as some things change depending on another part of the IRI. + * + * @return bool + */ + public function is_valid() + { + $isauthority = $this->iuserinfo !== null || $this->ihost !== null || $this->port !== null; + if ($this->ipath !== '' && + ( + $isauthority && ( + $this->ipath[0] !== '/' || + substr($this->ipath, 0, 2) === '//' + ) || + ( + $this->scheme === null && + !$isauthority && + strpos($this->ipath, ':') !== false && + (strpos($this->ipath, '/') === false ? true : strpos($this->ipath, ':') < strpos($this->ipath, '/')) + ) + ) + ) + { + return false; + } + + return true; + } + + /** + * Set the entire IRI. Returns true on success, false on failure (if there + * are any invalid characters). + * + * @param string $iri + * @return bool + */ + public function set_iri($iri) + { + static $cache; + if (!$cache) + { + $cache = array(); + } + + if ($iri === null) + { + return true; + } + elseif (isset($cache[$iri])) + { + list($this->scheme, + $this->iuserinfo, + $this->ihost, + $this->port, + $this->ipath, + $this->iquery, + $this->ifragment, + $return) = $cache[$iri]; + return $return; + } + else + { + $parsed = $this->parse_iri((string) $iri); + if (!$parsed) + { + return false; + } + + $return = $this->set_scheme($parsed['scheme']) + && $this->set_authority($parsed['authority']) + && $this->set_path($parsed['path']) + && $this->set_query($parsed['query']) + && $this->set_fragment($parsed['fragment']); + + $cache[$iri] = array($this->scheme, + $this->iuserinfo, + $this->ihost, + $this->port, + $this->ipath, + $this->iquery, + $this->ifragment, + $return); + return $return; + } + } + + /** + * Set the scheme. Returns true on success, false on failure (if there are + * any invalid characters). + * + * @param string $scheme + * @return bool + */ + public function set_scheme($scheme) + { + if ($scheme === null) + { + $this->scheme = null; + } + elseif (!preg_match('/^[A-Za-z][0-9A-Za-z+\-.]*$/', $scheme)) + { + $this->scheme = null; + return false; + } + else + { + $this->scheme = strtolower($scheme); + } + return true; + } + + /** + * Set the authority. Returns true on success, false on failure (if there are + * any invalid characters). + * + * @param string $authority + * @return bool + */ + public function set_authority($authority) + { + static $cache; + if (!$cache) + $cache = array(); + + if ($authority === null) + { + $this->iuserinfo = null; + $this->ihost = null; + $this->port = null; + return true; + } + elseif (isset($cache[$authority])) + { + list($this->iuserinfo, + $this->ihost, + $this->port, + $return) = $cache[$authority]; + + return $return; + } + else + { + $remaining = $authority; + if (($iuserinfo_end = strrpos($remaining, '@')) !== false) + { + $iuserinfo = substr($remaining, 0, $iuserinfo_end); + $remaining = substr($remaining, $iuserinfo_end + 1); + } + else + { + $iuserinfo = null; + } + if (($port_start = strpos($remaining, ':', strpos($remaining, ']'))) !== false) + { + if (($port = substr($remaining, $port_start + 1)) === false) + { + $port = null; + } + $remaining = substr($remaining, 0, $port_start); + } + else + { + $port = null; + } + + $return = $this->set_userinfo($iuserinfo) && + $this->set_host($remaining) && + $this->set_port($port); + + $cache[$authority] = array($this->iuserinfo, + $this->ihost, + $this->port, + $return); + + return $return; + } + } + + /** + * Set the iuserinfo. + * + * @param string $iuserinfo + * @return bool + */ + public function set_userinfo($iuserinfo) + { + if ($iuserinfo === null) + { + $this->iuserinfo = null; + } + else + { + $this->iuserinfo = $this->replace_invalid_with_pct_encoding($iuserinfo, '!$&\'()*+,;=:'); + $this->scheme_normalization(); + } + + return true; + } + + /** + * Set the ihost. Returns true on success, false on failure (if there are + * any invalid characters). + * + * @param string $ihost + * @return bool + */ + public function set_host($ihost) + { + if ($ihost === null) + { + $this->ihost = null; + return true; + } + elseif (substr($ihost, 0, 1) === '[' && substr($ihost, -1) === ']') + { + if (SimplePie_Net_IPv6::check_ipv6(substr($ihost, 1, -1))) + { + $this->ihost = '[' . SimplePie_Net_IPv6::compress(substr($ihost, 1, -1)) . ']'; + } + else + { + $this->ihost = null; + return false; + } + } + else + { + $ihost = $this->replace_invalid_with_pct_encoding($ihost, '!$&\'()*+,;='); + + // Lowercase, but ignore pct-encoded sections (as they should + // remain uppercase). This must be done after the previous step + // as that can add unescaped characters. + $position = 0; + $strlen = strlen($ihost); + while (($position += strcspn($ihost, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ%', $position)) < $strlen) + { + if ($ihost[$position] === '%') + { + $position += 3; + } + else + { + $ihost[$position] = strtolower($ihost[$position]); + $position++; + } + } + + $this->ihost = $ihost; + } + + $this->scheme_normalization(); + + return true; + } + + /** + * Set the port. Returns true on success, false on failure (if there are + * any invalid characters). + * + * @param string $port + * @return bool + */ + public function set_port($port) + { + if ($port === null) + { + $this->port = null; + return true; + } + elseif (strspn($port, '0123456789') === strlen($port)) + { + $this->port = (int) $port; + $this->scheme_normalization(); + return true; + } + else + { + $this->port = null; + return false; + } + } + + /** + * Set the ipath. + * + * @param string $ipath + * @return bool + */ + public function set_path($ipath) + { + static $cache; + if (!$cache) + { + $cache = array(); + } + + $ipath = (string) $ipath; + + if (isset($cache[$ipath])) + { + $this->ipath = $cache[$ipath][(int) ($this->scheme !== null)]; + } + else + { + $valid = $this->replace_invalid_with_pct_encoding($ipath, '!$&\'()*+,;=@:/'); + $removed = $this->remove_dot_segments($valid); + + $cache[$ipath] = array($valid, $removed); + $this->ipath = ($this->scheme !== null) ? $removed : $valid; + } + + $this->scheme_normalization(); + return true; + } + + /** + * Set the iquery. + * + * @param string $iquery + * @return bool + */ + public function set_query($iquery) + { + if ($iquery === null) + { + $this->iquery = null; + } + else + { + $this->iquery = $this->replace_invalid_with_pct_encoding($iquery, '!$&\'()*+,;=:@/?', true); + $this->scheme_normalization(); + } + return true; + } + + /** + * Set the ifragment. + * + * @param string $ifragment + * @return bool + */ + public function set_fragment($ifragment) + { + if ($ifragment === null) + { + $this->ifragment = null; + } + else + { + $this->ifragment = $this->replace_invalid_with_pct_encoding($ifragment, '!$&\'()*+,;=:@/?'); + $this->scheme_normalization(); + } + return true; + } + + /** + * Convert an IRI to a URI (or parts thereof) + * + * @return string + */ + public function to_uri($string) + { + static $non_ascii; + if (!$non_ascii) + { + $non_ascii = implode('', range("\x80", "\xFF")); + } + + $position = 0; + $strlen = strlen($string); + while (($position += strcspn($string, $non_ascii, $position)) < $strlen) + { + $string = substr_replace($string, sprintf('%%%02X', ord($string[$position])), $position, 1); + $position += 3; + $strlen += 2; + } + + return $string; + } + + /** + * Get the complete IRI + * + * @return string + */ + public function get_iri() + { + if (!$this->is_valid()) + { + return false; + } + + $iri = ''; + if ($this->scheme !== null) + { + $iri .= $this->scheme . ':'; + } + if (($iauthority = $this->get_iauthority()) !== null) + { + $iri .= '//' . $iauthority; + } + if ($this->ipath !== '') + { + $iri .= $this->ipath; + } + elseif (!empty($this->normalization[$this->scheme]['ipath']) && $iauthority !== null && $iauthority !== '') + { + $iri .= $this->normalization[$this->scheme]['ipath']; + } + if ($this->iquery !== null) + { + $iri .= '?' . $this->iquery; + } + if ($this->ifragment !== null) + { + $iri .= '#' . $this->ifragment; + } + + return $iri; + } + + /** + * Get the complete URI + * + * @return string + */ + public function get_uri() + { + return $this->to_uri($this->get_iri()); + } + + /** + * Get the complete iauthority + * + * @return string + */ + protected function get_iauthority() + { + if ($this->iuserinfo !== null || $this->ihost !== null || $this->port !== null) + { + $iauthority = ''; + if ($this->iuserinfo !== null) + { + $iauthority .= $this->iuserinfo . '@'; + } + if ($this->ihost !== null) + { + $iauthority .= $this->ihost; + } + if ($this->port !== null) + { + $iauthority .= ':' . $this->port; + } + return $iauthority; + } + else + { + return null; + } + } + + /** + * Get the complete authority + * + * @return string + */ + protected function get_authority() + { + $iauthority = $this->get_iauthority(); + if (is_string($iauthority)) + return $this->to_uri($iauthority); + else + return $iauthority; + } +} diff --git a/wp-includes/SimplePie/Item.php b/wp-includes/SimplePie/Item.php new file mode 100644 index 0000000..a77574b --- /dev/null +++ b/wp-includes/SimplePie/Item.php @@ -0,0 +1,2964 @@ +feed = $feed; + $this->data = $data; + } + + /** + * Set the registry handler + * + * This is usually used by {@see SimplePie_Registry::create} + * + * @since 1.3 + * @param SimplePie_Registry $registry + */ + public function set_registry(SimplePie_Registry $registry) + { + $this->registry = $registry; + } + + /** + * Get a string representation of the item + * + * @return string + */ + public function __toString() + { + return md5(serialize($this->data)); + } + + /** + * Remove items that link back to this before destroying this object + */ + public function __destruct() + { + if ((version_compare(PHP_VERSION, '5.3', '<') || !gc_enabled()) && !ini_get('zend.ze1_compatibility_mode')) + { + unset($this->feed); + } + } + + /** + * Get data for an item-level element + * + * This method allows you to get access to ANY element/attribute that is a + * sub-element of the item/entry tag. + * + * See {@see SimplePie::get_feed_tags()} for a description of the return value + * + * @since 1.0 + * @see http://simplepie.org/wiki/faq/supported_xml_namespaces + * @param string $namespace The URL of the XML namespace of the elements you're trying to access + * @param string $tag Tag name + * @return array + */ + public function get_item_tags($namespace, $tag) + { + if (isset($this->data['child'][$namespace][$tag])) + { + return $this->data['child'][$namespace][$tag]; + } + else + { + return null; + } + } + + /** + * Get the base URL value from the parent feed + * + * Uses `` + * + * @param array $element + * @return string + */ + public function get_base($element = array()) + { + return $this->feed->get_base($element); + } + + /** + * Sanitize feed data + * + * @access private + * @see SimplePie::sanitize() + * @param string $data Data to sanitize + * @param int $type One of the SIMPLEPIE_CONSTRUCT_* constants + * @param string $base Base URL to resolve URLs against + * @return string Sanitized data + */ + public function sanitize($data, $type, $base = '') + { + return $this->feed->sanitize($data, $type, $base); + } + + /** + * Get the parent feed + * + * Note: this may not work as you think for multifeeds! + * + * @link http://simplepie.org/faq/typical_multifeed_gotchas#missing_data_from_feed + * @since 1.0 + * @return SimplePie + */ + public function get_feed() + { + return $this->feed; + } + + /** + * Get the unique identifier for the item + * + * This is usually used when writing code to check for new items in a feed. + * + * Uses ``, ``, `` or the `about` attribute + * for RDF. If none of these are supplied (or `$hash` is true), creates an + * MD5 hash based on the permalink and title. If either of those are not + * supplied, creates a hash based on the full feed data. + * + * @since Beta 2 + * @param boolean $hash Should we force using a hash instead of the supplied ID? + * @return string + */ + public function get_id($hash = false) + { + if (!$hash) + { + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'id')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'id')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'guid')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'identifier')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'identifier')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (isset($this->data['attribs'][SIMPLEPIE_NAMESPACE_RDF]['about'])) + { + return $this->sanitize($this->data['attribs'][SIMPLEPIE_NAMESPACE_RDF]['about'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (($return = $this->get_permalink()) !== null) + { + return $return; + } + elseif (($return = $this->get_title()) !== null) + { + return $return; + } + } + if ($this->get_permalink() !== null || $this->get_title() !== null) + { + return md5($this->get_permalink() . $this->get_title()); + } + else + { + return md5(serialize($this->data)); + } + } + + /** + * Get the title of the item + * + * Uses ``, `` or `<dc:title>` + * + * @since Beta 2 (previously called `get_item_title` since 0.8) + * @return string|null + */ + public function get_title() + { + if (!isset($this->data['title'])) + { + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title')) + { + $this->data['title'] = $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title')) + { + $this->data['title'] = $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title')) + { + $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title')) + { + $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title')) + { + $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title')) + { + $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title')) + { + $this->data['title'] = $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $this->data['title'] = null; + } + } + return $this->data['title']; + } + + /** + * Get the content for the item + * + * Prefers summaries over full content , but will return full content if a + * summary does not exist. + * + * To prefer full content instead, use {@see get_content} + * + * Uses `<atom:summary>`, `<description>`, `<dc:description>` or + * `<itunes:subtitle>` + * + * @since 0.8 + * @param boolean $description_only Should we avoid falling back to the content? + * @return string|null + */ + public function get_description($description_only = false) + { + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'summary')) + { + return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'summary')) + { + return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML); + } + + elseif (!$description_only) + { + return $this->get_content(true); + } + else + { + return null; + } + } + + /** + * Get the content for the item + * + * Prefers full content over summaries, but will return a summary if full + * content does not exist. + * + * To prefer summaries instead, use {@see get_description} + * + * Uses `<atom:content>` or `<content:encoded>` (RSS 1.0 Content Module) + * + * @since 1.0 + * @param boolean $content_only Should we avoid falling back to the description? + * @return string|null + */ + public function get_content($content_only = false) + { + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'content')) + { + return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_content_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'content')) + { + return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10_MODULES_CONTENT, 'encoded')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); + } + elseif (!$content_only) + { + return $this->get_description(true); + } + else + { + return null; + } + } + + /** + * Get a category for the item + * + * @since Beta 3 (previously called `get_categories()` since Beta 2) + * @param int $key The category that you want to return. Remember that arrays begin with 0, not 1 + * @return SimplePie_Category|null + */ + public function get_category($key = 0) + { + $categories = $this->get_categories(); + if (isset($categories[$key])) + { + return $categories[$key]; + } + else + { + return null; + } + } + + /** + * Get all categories for the item + * + * Uses `<atom:category>`, `<category>` or `<dc:subject>` + * + * @since Beta 3 + * @return array|null List of {@see SimplePie_Category} objects + */ + public function get_categories() + { + $categories = array(); + + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category) + { + $term = null; + $scheme = null; + $label = null; + if (isset($category['attribs']['']['term'])) + { + $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['scheme'])) + { + $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['label'])) + { + $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories[] = $this->registry->create('Category', array($term, $scheme, $label)); + } + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category) + { + // This is really the label, but keep this as the term also for BC. + // Label will also work on retrieving because that falls back to term. + $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); + if (isset($category['attribs']['']['domain'])) + { + $scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $scheme = null; + } + $categories[] = $this->registry->create('Category', array($term, $scheme, null)); + } + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category) + { + $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); + } + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category) + { + $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); + } + + if (!empty($categories)) + { + return array_unique($categories); + } + else + { + return null; + } + } + + /** + * Get an author for the item + * + * @since Beta 2 + * @param int $key The author that you want to return. Remember that arrays begin with 0, not 1 + * @return SimplePie_Author|null + */ + public function get_author($key = 0) + { + $authors = $this->get_authors(); + if (isset($authors[$key])) + { + return $authors[$key]; + } + else + { + return null; + } + } + + /** + * Get a contributor for the item + * + * @since 1.1 + * @param int $key The contrbutor that you want to return. Remember that arrays begin with 0, not 1 + * @return SimplePie_Author|null + */ + public function get_contributor($key = 0) + { + $contributors = $this->get_contributors(); + if (isset($contributors[$key])) + { + return $contributors[$key]; + } + else + { + return null; + } + } + + /** + * Get all contributors for the item + * + * Uses `<atom:contributor>` + * + * @since 1.1 + * @return array|null List of {@see SimplePie_Author} objects + */ + public function get_contributors() + { + $contributors = array(); + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor) + { + $name = null; + $uri = null; + $email = null; + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) + { + $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) + { + $uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) + { + $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $uri !== null) + { + $contributors[] = $this->registry->create('Author', array($name, $uri, $email)); + } + } + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor) + { + $name = null; + $url = null; + $email = null; + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) + { + $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) + { + $url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) + { + $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $url !== null) + { + $contributors[] = $this->registry->create('Author', array($name, $url, $email)); + } + } + + if (!empty($contributors)) + { + return array_unique($contributors); + } + else + { + return null; + } + } + + /** + * Get all authors for the item + * + * Uses `<atom:author>`, `<author>`, `<dc:creator>` or `<itunes:author>` + * + * @since Beta 2 + * @return array|null List of {@see SimplePie_Author} objects + */ + public function get_authors() + { + $authors = array(); + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author) + { + $name = null; + $uri = null; + $email = null; + if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) + { + $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) + { + $uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); + } + if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) + { + $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $uri !== null) + { + $authors[] = $this->registry->create('Author', array($name, $uri, $email)); + } + } + if ($author = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author')) + { + $name = null; + $url = null; + $email = null; + if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) + { + $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) + { + $url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); + } + if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) + { + $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $url !== null) + { + $authors[] = $this->registry->create('Author', array($name, $url, $email)); + } + } + if ($author = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'author')) + { + $authors[] = $this->registry->create('Author', array(null, null, $this->sanitize($author[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT))); + } + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author) + { + $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); + } + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author) + { + $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); + } + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author) + { + $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); + } + + if (!empty($authors)) + { + return array_unique($authors); + } + elseif (($source = $this->get_source()) && ($authors = $source->get_authors())) + { + return $authors; + } + elseif ($authors = $this->feed->get_authors()) + { + return $authors; + } + else + { + return null; + } + } + + /** + * Get the copyright info for the item + * + * Uses `<atom:rights>` or `<dc:rights>` + * + * @since 1.1 + * @return string + */ + public function get_copyright() + { + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights')) + { + return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + return null; + } + } + + /** + * Get the posting date/time for the item + * + * Uses `<atom:published>`, `<atom:updated>`, `<atom:issued>`, + * `<atom:modified>`, `<pubDate>` or `<dc:date>` + * + * Note: obeys PHP's timezone setting. To get a UTC date/time, use + * {@see get_gmdate} + * + * @since Beta 2 (previously called `get_item_date` since 0.8) + * + * @param string $date_format Supports any PHP date format from {@see http://php.net/date} (empty for the raw data) + * @return int|string|null + */ + public function get_date($date_format = 'j F Y, g:i a') + { + if (!isset($this->data['date'])) + { + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'published')) + { + $this->data['date']['raw'] = $return[0]['data']; + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'updated')) + { + $this->data['date']['raw'] = $return[0]['data']; + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'issued')) + { + $this->data['date']['raw'] = $return[0]['data']; + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'created')) + { + $this->data['date']['raw'] = $return[0]['data']; + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'modified')) + { + $this->data['date']['raw'] = $return[0]['data']; + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'pubDate')) + { + $this->data['date']['raw'] = $return[0]['data']; + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_11, 'date')) + { + $this->data['date']['raw'] = $return[0]['data']; + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_DC_10, 'date')) + { + $this->data['date']['raw'] = $return[0]['data']; + } + + if (!empty($this->data['date']['raw'])) + { + $parser = $this->registry->call('Parse_Date', 'get'); + $this->data['date']['parsed'] = $parser->parse($this->data['date']['raw']); + } + else + { + $this->data['date'] = null; + } + } + if ($this->data['date']) + { + $date_format = (string) $date_format; + switch ($date_format) + { + case '': + return $this->sanitize($this->data['date']['raw'], SIMPLEPIE_CONSTRUCT_TEXT); + + case 'U': + return $this->data['date']['parsed']; + + default: + return date($date_format, $this->data['date']['parsed']); + } + } + else + { + return null; + } + } + + /** + * Get the update date/time for the item + * + * Uses `<atom:updated>` + * + * Note: obeys PHP's timezone setting. To get a UTC date/time, use + * {@see get_gmdate} + * + * @param string $date_format Supports any PHP date format from {@see http://php.net/date} (empty for the raw data) + * @return int|string|null + */ + public function get_updated_date($date_format = 'j F Y, g:i a') + { + if (!isset($this->data['updated'])) + { + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'updated')) + { + $this->data['updated']['raw'] = $return[0]['data']; + } + + if (!empty($this->data['updated']['raw'])) + { + $parser = $this->registry->call('Parse_Date', 'get'); + $this->data['updated']['parsed'] = $parser->parse($this->data['date']['raw']); + } + else + { + $this->data['updated'] = null; + } + } + if ($this->data['updated']) + { + $date_format = (string) $date_format; + switch ($date_format) + { + case '': + return $this->sanitize($this->data['updated']['raw'], SIMPLEPIE_CONSTRUCT_TEXT); + + case 'U': + return $this->data['updated']['parsed']; + + default: + return date($date_format, $this->data['updated']['parsed']); + } + } + else + { + return null; + } + } + + /** + * Get the localized posting date/time for the item + * + * Returns the date formatted in the localized language. To display in + * languages other than the server's default, you need to change the locale + * with {@link http://php.net/setlocale setlocale()}. The available + * localizations depend on which ones are installed on your web server. + * + * @since 1.0 + * + * @param string $date_format Supports any PHP date format from {@see http://php.net/strftime} (empty for the raw data) + * @return int|string|null + */ + public function get_local_date($date_format = '%c') + { + if (!$date_format) + { + return $this->sanitize($this->get_date(''), SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (($date = $this->get_date('U')) !== null && $date !== false) + { + return strftime($date_format, $date); + } + else + { + return null; + } + } + + /** + * Get the posting date/time for the item (UTC time) + * + * @see get_date + * @param string $date_format Supports any PHP date format from {@see http://php.net/date} + * @return int|string|null + */ + public function get_gmdate($date_format = 'j F Y, g:i a') + { + $date = $this->get_date('U'); + if ($date === null) + { + return null; + } + + return gmdate($date_format, $date); + } + + /** + * Get the update date/time for the item (UTC time) + * + * @see get_updated_date + * @param string $date_format Supports any PHP date format from {@see http://php.net/date} + * @return int|string|null + */ + public function get_updated_gmdate($date_format = 'j F Y, g:i a') + { + $date = $this->get_updated_date('U'); + if ($date === null) + { + return null; + } + + return gmdate($date_format, $date); + } + + /** + * Get the permalink for the item + * + * Returns the first link available with a relationship of "alternate". + * Identical to {@see get_link()} with key 0 + * + * @see get_link + * @since 0.8 + * @return string|null Permalink URL + */ + public function get_permalink() + { + $link = $this->get_link(); + $enclosure = $this->get_enclosure(0); + if ($link !== null) + { + return $link; + } + elseif ($enclosure !== null) + { + return $enclosure->get_link(); + } + else + { + return null; + } + } + + /** + * Get a single link for the item + * + * @since Beta 3 + * @param int $key The link that you want to return. Remember that arrays begin with 0, not 1 + * @param string $rel The relationship of the link to return + * @return string|null Link URL + */ + public function get_link($key = 0, $rel = 'alternate') + { + $links = $this->get_links($rel); + if ($links[$key] !== null) + { + return $links[$key]; + } + else + { + return null; + } + } + + /** + * Get all links for the item + * + * Uses `<atom:link>`, `<link>` or `<guid>` + * + * @since Beta 2 + * @param string $rel The relationship of links to return + * @return array|null Links found for the item (strings) + */ + public function get_links($rel = 'alternate') + { + if (!isset($this->data['links'])) + { + $this->data['links'] = array(); + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link') as $link) + { + if (isset($link['attribs']['']['href'])) + { + $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; + $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); + + } + } + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link') as $link) + { + if (isset($link['attribs']['']['href'])) + { + $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; + $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); + } + } + if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link')) + { + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + } + if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link')) + { + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + } + if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link')) + { + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + } + if ($links = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'guid')) + { + if (!isset($links[0]['attribs']['']['isPermaLink']) || strtolower(trim($links[0]['attribs']['']['isPermaLink'])) === 'true') + { + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + } + } + + $keys = array_keys($this->data['links']); + foreach ($keys as $key) + { + if ($this->registry->call('Misc', 'is_isegment_nz_nc', array($key))) + { + if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key])) + { + $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]); + $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]; + } + else + { + $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key]; + } + } + elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY) + { + $this->data['links'][substr($key, 41)] =& $this->data['links'][$key]; + } + $this->data['links'][$key] = array_unique($this->data['links'][$key]); + } + } + if (isset($this->data['links'][$rel])) + { + return $this->data['links'][$rel]; + } + else + { + return null; + } + } + + /** + * Get an enclosure from the item + * + * Supports the <enclosure> RSS tag, as well as Media RSS and iTunes RSS. + * + * @since Beta 2 + * @todo Add ability to prefer one type of content over another (in a media group). + * @param int $key The enclosure that you want to return. Remember that arrays begin with 0, not 1 + * @return SimplePie_Enclosure|null + */ + public function get_enclosure($key = 0, $prefer = null) + { + $enclosures = $this->get_enclosures(); + if (isset($enclosures[$key])) + { + return $enclosures[$key]; + } + else + { + return null; + } + } + + /** + * Get all available enclosures (podcasts, etc.) + * + * Supports the <enclosure> RSS tag, as well as Media RSS and iTunes RSS. + * + * At this point, we're pretty much assuming that all enclosures for an item + * are the same content. Anything else is too complicated to + * properly support. + * + * @since Beta 2 + * @todo Add support for end-user defined sorting of enclosures by type/handler (so we can prefer the faster-loading FLV over MP4). + * @todo If an element exists at a level, but it's value is empty, we should fall back to the value from the parent (if it exists). + * @return array|null List of SimplePie_Enclosure items + */ + public function get_enclosures() + { + if (!isset($this->data['enclosures'])) + { + $this->data['enclosures'] = array(); + + // Elements + $captions_parent = null; + $categories_parent = null; + $copyrights_parent = null; + $credits_parent = null; + $description_parent = null; + $duration_parent = null; + $hashes_parent = null; + $keywords_parent = null; + $player_parent = null; + $ratings_parent = null; + $restrictions_parent = null; + $thumbnails_parent = null; + $title_parent = null; + + // Let's do the channel and item-level ones first, and just re-use them if we need to. + $parent = $this->get_feed(); + + // CAPTIONS + if ($captions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'text')) + { + foreach ($captions as $caption) + { + $caption_type = null; + $caption_lang = null; + $caption_startTime = null; + $caption_endTime = null; + $caption_text = null; + if (isset($caption['attribs']['']['type'])) + { + $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['lang'])) + { + $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['start'])) + { + $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['end'])) + { + $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['data'])) + { + $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $captions_parent[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text)); + } + } + elseif ($captions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'text')) + { + foreach ($captions as $caption) + { + $caption_type = null; + $caption_lang = null; + $caption_startTime = null; + $caption_endTime = null; + $caption_text = null; + if (isset($caption['attribs']['']['type'])) + { + $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['lang'])) + { + $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['start'])) + { + $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['end'])) + { + $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['data'])) + { + $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $captions_parent[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text)); + } + } + if (is_array($captions_parent)) + { + $captions_parent = array_values(array_unique($captions_parent)); + } + + // CATEGORIES + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'category') as $category) + { + $term = null; + $scheme = null; + $label = null; + if (isset($category['data'])) + { + $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['scheme'])) + { + $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $scheme = 'http://search.yahoo.com/mrss/category_schema'; + } + if (isset($category['attribs']['']['label'])) + { + $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories_parent[] = $this->registry->create('Category', array($term, $scheme, $label)); + } + foreach ((array) $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'category') as $category) + { + $term = null; + $scheme = null; + $label = null; + if (isset($category['data'])) + { + $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['scheme'])) + { + $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $scheme = 'http://search.yahoo.com/mrss/category_schema'; + } + if (isset($category['attribs']['']['label'])) + { + $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories_parent[] = $this->registry->create('Category', array($term, $scheme, $label)); + } + foreach ((array) $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'category') as $category) + { + $term = null; + $scheme = 'http://www.itunes.com/dtds/podcast-1.0.dtd'; + $label = null; + if (isset($category['attribs']['']['text'])) + { + $label = $this->sanitize($category['attribs']['']['text'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories_parent[] = $this->registry->create('Category', array($term, $scheme, $label)); + + if (isset($category['child'][SIMPLEPIE_NAMESPACE_ITUNES]['category'])) + { + foreach ((array) $category['child'][SIMPLEPIE_NAMESPACE_ITUNES]['category'] as $subcategory) + { + if (isset($subcategory['attribs']['']['text'])) + { + $label = $this->sanitize($subcategory['attribs']['']['text'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories_parent[] = $this->registry->create('Category', array($term, $scheme, $label)); + } + } + } + if (is_array($categories_parent)) + { + $categories_parent = array_values(array_unique($categories_parent)); + } + + // COPYRIGHT + if ($copyright = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'copyright')) + { + $copyright_url = null; + $copyright_label = null; + if (isset($copyright[0]['attribs']['']['url'])) + { + $copyright_url = $this->sanitize($copyright[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($copyright[0]['data'])) + { + $copyright_label = $this->sanitize($copyright[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $copyrights_parent = $this->registry->create('Copyright', array($copyright_url, $copyright_label)); + } + elseif ($copyright = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'copyright')) + { + $copyright_url = null; + $copyright_label = null; + if (isset($copyright[0]['attribs']['']['url'])) + { + $copyright_url = $this->sanitize($copyright[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($copyright[0]['data'])) + { + $copyright_label = $this->sanitize($copyright[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $copyrights_parent = $this->registry->create('Copyright', array($copyright_url, $copyright_label)); + } + + // CREDITS + if ($credits = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'credit')) + { + foreach ($credits as $credit) + { + $credit_role = null; + $credit_scheme = null; + $credit_name = null; + if (isset($credit['attribs']['']['role'])) + { + $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($credit['attribs']['']['scheme'])) + { + $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $credit_scheme = 'urn:ebu'; + } + if (isset($credit['data'])) + { + $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $credits_parent[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name)); + } + } + elseif ($credits = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'credit')) + { + foreach ($credits as $credit) + { + $credit_role = null; + $credit_scheme = null; + $credit_name = null; + if (isset($credit['attribs']['']['role'])) + { + $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($credit['attribs']['']['scheme'])) + { + $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $credit_scheme = 'urn:ebu'; + } + if (isset($credit['data'])) + { + $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $credits_parent[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name)); + } + } + if (is_array($credits_parent)) + { + $credits_parent = array_values(array_unique($credits_parent)); + } + + // DESCRIPTION + if ($description_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'description')) + { + if (isset($description_parent[0]['data'])) + { + $description_parent = $this->sanitize($description_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + } + elseif ($description_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'description')) + { + if (isset($description_parent[0]['data'])) + { + $description_parent = $this->sanitize($description_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + } + + // DURATION + if ($duration_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'duration')) + { + $seconds = null; + $minutes = null; + $hours = null; + if (isset($duration_parent[0]['data'])) + { + $temp = explode(':', $this->sanitize($duration_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); + if (sizeof($temp) > 0) + { + $seconds = (int) array_pop($temp); + } + if (sizeof($temp) > 0) + { + $minutes = (int) array_pop($temp); + $seconds += $minutes * 60; + } + if (sizeof($temp) > 0) + { + $hours = (int) array_pop($temp); + $seconds += $hours * 3600; + } + unset($temp); + $duration_parent = $seconds; + } + } + + // HASHES + if ($hashes_iterator = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'hash')) + { + foreach ($hashes_iterator as $hash) + { + $value = null; + $algo = null; + if (isset($hash['data'])) + { + $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($hash['attribs']['']['algo'])) + { + $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $algo = 'md5'; + } + $hashes_parent[] = $algo.':'.$value; + } + } + elseif ($hashes_iterator = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'hash')) + { + foreach ($hashes_iterator as $hash) + { + $value = null; + $algo = null; + if (isset($hash['data'])) + { + $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($hash['attribs']['']['algo'])) + { + $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $algo = 'md5'; + } + $hashes_parent[] = $algo.':'.$value; + } + } + if (is_array($hashes_parent)) + { + $hashes_parent = array_values(array_unique($hashes_parent)); + } + + // KEYWORDS + if ($keywords = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'keywords')) + { + if (isset($keywords[0]['data'])) + { + $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); + foreach ($temp as $word) + { + $keywords_parent[] = trim($word); + } + } + unset($temp); + } + elseif ($keywords = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'keywords')) + { + if (isset($keywords[0]['data'])) + { + $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); + foreach ($temp as $word) + { + $keywords_parent[] = trim($word); + } + } + unset($temp); + } + elseif ($keywords = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'keywords')) + { + if (isset($keywords[0]['data'])) + { + $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); + foreach ($temp as $word) + { + $keywords_parent[] = trim($word); + } + } + unset($temp); + } + elseif ($keywords = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'keywords')) + { + if (isset($keywords[0]['data'])) + { + $temp = explode(',', $this->sanitize($keywords[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); + foreach ($temp as $word) + { + $keywords_parent[] = trim($word); + } + } + unset($temp); + } + if (is_array($keywords_parent)) + { + $keywords_parent = array_values(array_unique($keywords_parent)); + } + + // PLAYER + if ($player_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'player')) + { + if (isset($player_parent[0]['attribs']['']['url'])) + { + $player_parent = $this->sanitize($player_parent[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + } + elseif ($player_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'player')) + { + if (isset($player_parent[0]['attribs']['']['url'])) + { + $player_parent = $this->sanitize($player_parent[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + } + + // RATINGS + if ($ratings = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'rating')) + { + foreach ($ratings as $rating) + { + $rating_scheme = null; + $rating_value = null; + if (isset($rating['attribs']['']['scheme'])) + { + $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $rating_scheme = 'urn:simple'; + } + if (isset($rating['data'])) + { + $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $ratings_parent[] = $this->registry->create('Rating', array($rating_scheme, $rating_value)); + } + } + elseif ($ratings = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'explicit')) + { + foreach ($ratings as $rating) + { + $rating_scheme = 'urn:itunes'; + $rating_value = null; + if (isset($rating['data'])) + { + $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $ratings_parent[] = $this->registry->create('Rating', array($rating_scheme, $rating_value)); + } + } + elseif ($ratings = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'rating')) + { + foreach ($ratings as $rating) + { + $rating_scheme = null; + $rating_value = null; + if (isset($rating['attribs']['']['scheme'])) + { + $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $rating_scheme = 'urn:simple'; + } + if (isset($rating['data'])) + { + $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $ratings_parent[] = $this->registry->create('Rating', array($rating_scheme, $rating_value)); + } + } + elseif ($ratings = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'explicit')) + { + foreach ($ratings as $rating) + { + $rating_scheme = 'urn:itunes'; + $rating_value = null; + if (isset($rating['data'])) + { + $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $ratings_parent[] = $this->registry->create('Rating', array($rating_scheme, $rating_value)); + } + } + if (is_array($ratings_parent)) + { + $ratings_parent = array_values(array_unique($ratings_parent)); + } + + // RESTRICTIONS + if ($restrictions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'restriction')) + { + foreach ($restrictions as $restriction) + { + $restriction_relationship = null; + $restriction_type = null; + $restriction_value = null; + if (isset($restriction['attribs']['']['relationship'])) + { + $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['attribs']['']['type'])) + { + $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['data'])) + { + $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $restrictions_parent[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value)); + } + } + elseif ($restrictions = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'block')) + { + foreach ($restrictions as $restriction) + { + $restriction_relationship = 'allow'; + $restriction_type = null; + $restriction_value = 'itunes'; + if (isset($restriction['data']) && strtolower($restriction['data']) === 'yes') + { + $restriction_relationship = 'deny'; + } + $restrictions_parent[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value)); + } + } + elseif ($restrictions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'restriction')) + { + foreach ($restrictions as $restriction) + { + $restriction_relationship = null; + $restriction_type = null; + $restriction_value = null; + if (isset($restriction['attribs']['']['relationship'])) + { + $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['attribs']['']['type'])) + { + $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['data'])) + { + $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $restrictions_parent[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value)); + } + } + elseif ($restrictions = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'block')) + { + foreach ($restrictions as $restriction) + { + $restriction_relationship = 'allow'; + $restriction_type = null; + $restriction_value = 'itunes'; + if (isset($restriction['data']) && strtolower($restriction['data']) === 'yes') + { + $restriction_relationship = 'deny'; + } + $restrictions_parent[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value)); + } + } + if (is_array($restrictions_parent)) + { + $restrictions_parent = array_values(array_unique($restrictions_parent)); + } + else + { + $restrictions_parent = array(new SimplePie_Restriction('allow', null, 'default')); + } + + // THUMBNAILS + if ($thumbnails = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'thumbnail')) + { + foreach ($thumbnails as $thumbnail) + { + if (isset($thumbnail['attribs']['']['url'])) + { + $thumbnails_parent[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + } + } + elseif ($thumbnails = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'thumbnail')) + { + foreach ($thumbnails as $thumbnail) + { + if (isset($thumbnail['attribs']['']['url'])) + { + $thumbnails_parent[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + } + } + + // TITLES + if ($title_parent = $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'title')) + { + if (isset($title_parent[0]['data'])) + { + $title_parent = $this->sanitize($title_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + } + elseif ($title_parent = $parent->get_channel_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'title')) + { + if (isset($title_parent[0]['data'])) + { + $title_parent = $this->sanitize($title_parent[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + } + + // Clear the memory + unset($parent); + + // Attributes + $bitrate = null; + $channels = null; + $duration = null; + $expression = null; + $framerate = null; + $height = null; + $javascript = null; + $lang = null; + $length = null; + $medium = null; + $samplingrate = null; + $type = null; + $url = null; + $width = null; + + // Elements + $captions = null; + $categories = null; + $copyrights = null; + $credits = null; + $description = null; + $hashes = null; + $keywords = null; + $player = null; + $ratings = null; + $restrictions = null; + $thumbnails = null; + $title = null; + + // If we have media:group tags, loop through them. + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'group') as $group) + { + if(isset($group['child']) && isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'])) + { + // If we have media:content tags, loop through them. + foreach ((array) $group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'] as $content) + { + if (isset($content['attribs']['']['url'])) + { + // Attributes + $bitrate = null; + $channels = null; + $duration = null; + $expression = null; + $framerate = null; + $height = null; + $javascript = null; + $lang = null; + $length = null; + $medium = null; + $samplingrate = null; + $type = null; + $url = null; + $width = null; + + // Elements + $captions = null; + $categories = null; + $copyrights = null; + $credits = null; + $description = null; + $hashes = null; + $keywords = null; + $player = null; + $ratings = null; + $restrictions = null; + $thumbnails = null; + $title = null; + + // Start checking the attributes of media:content + if (isset($content['attribs']['']['bitrate'])) + { + $bitrate = $this->sanitize($content['attribs']['']['bitrate'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['channels'])) + { + $channels = $this->sanitize($content['attribs']['']['channels'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['duration'])) + { + $duration = $this->sanitize($content['attribs']['']['duration'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $duration = $duration_parent; + } + if (isset($content['attribs']['']['expression'])) + { + $expression = $this->sanitize($content['attribs']['']['expression'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['framerate'])) + { + $framerate = $this->sanitize($content['attribs']['']['framerate'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['height'])) + { + $height = $this->sanitize($content['attribs']['']['height'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['lang'])) + { + $lang = $this->sanitize($content['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['fileSize'])) + { + $length = ceil($content['attribs']['']['fileSize']); + } + if (isset($content['attribs']['']['medium'])) + { + $medium = $this->sanitize($content['attribs']['']['medium'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['samplingrate'])) + { + $samplingrate = $this->sanitize($content['attribs']['']['samplingrate'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['type'])) + { + $type = $this->sanitize($content['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['width'])) + { + $width = $this->sanitize($content['attribs']['']['width'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $url = $this->sanitize($content['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + + // Checking the other optional media: elements. Priority: media:content, media:group, item, channel + + // CAPTIONS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption) + { + $caption_type = null; + $caption_lang = null; + $caption_startTime = null; + $caption_endTime = null; + $caption_text = null; + if (isset($caption['attribs']['']['type'])) + { + $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['lang'])) + { + $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['start'])) + { + $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['end'])) + { + $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['data'])) + { + $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $captions[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text)); + } + if (is_array($captions)) + { + $captions = array_values(array_unique($captions)); + } + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'])) + { + foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption) + { + $caption_type = null; + $caption_lang = null; + $caption_startTime = null; + $caption_endTime = null; + $caption_text = null; + if (isset($caption['attribs']['']['type'])) + { + $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['lang'])) + { + $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['start'])) + { + $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['end'])) + { + $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['data'])) + { + $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $captions[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text)); + } + if (is_array($captions)) + { + $captions = array_values(array_unique($captions)); + } + } + else + { + $captions = $captions_parent; + } + + // CATEGORIES + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'])) + { + foreach ((array) $content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category) + { + $term = null; + $scheme = null; + $label = null; + if (isset($category['data'])) + { + $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['scheme'])) + { + $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $scheme = 'http://search.yahoo.com/mrss/category_schema'; + } + if (isset($category['attribs']['']['label'])) + { + $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories[] = $this->registry->create('Category', array($term, $scheme, $label)); + } + } + if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'])) + { + foreach ((array) $group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category) + { + $term = null; + $scheme = null; + $label = null; + if (isset($category['data'])) + { + $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['scheme'])) + { + $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $scheme = 'http://search.yahoo.com/mrss/category_schema'; + } + if (isset($category['attribs']['']['label'])) + { + $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories[] = $this->registry->create('Category', array($term, $scheme, $label)); + } + } + if (is_array($categories) && is_array($categories_parent)) + { + $categories = array_values(array_unique(array_merge($categories, $categories_parent))); + } + elseif (is_array($categories)) + { + $categories = array_values(array_unique($categories)); + } + elseif (is_array($categories_parent)) + { + $categories = array_values(array_unique($categories_parent)); + } + + // COPYRIGHTS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'])) + { + $copyright_url = null; + $copyright_label = null; + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'])) + { + $copyright_url = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'])) + { + $copyright_label = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $copyrights = $this->registry->create('Copyright', array($copyright_url, $copyright_label)); + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'])) + { + $copyright_url = null; + $copyright_label = null; + if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'])) + { + $copyright_url = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'])) + { + $copyright_label = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $copyrights = $this->registry->create('Copyright', array($copyright_url, $copyright_label)); + } + else + { + $copyrights = $copyrights_parent; + } + + // CREDITS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit) + { + $credit_role = null; + $credit_scheme = null; + $credit_name = null; + if (isset($credit['attribs']['']['role'])) + { + $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($credit['attribs']['']['scheme'])) + { + $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $credit_scheme = 'urn:ebu'; + } + if (isset($credit['data'])) + { + $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $credits[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name)); + } + if (is_array($credits)) + { + $credits = array_values(array_unique($credits)); + } + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'])) + { + foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit) + { + $credit_role = null; + $credit_scheme = null; + $credit_name = null; + if (isset($credit['attribs']['']['role'])) + { + $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($credit['attribs']['']['scheme'])) + { + $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $credit_scheme = 'urn:ebu'; + } + if (isset($credit['data'])) + { + $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $credits[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name)); + } + if (is_array($credits)) + { + $credits = array_values(array_unique($credits)); + } + } + else + { + $credits = $credits_parent; + } + + // DESCRIPTION + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'])) + { + $description = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'])) + { + $description = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $description = $description_parent; + } + + // HASHES + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash) + { + $value = null; + $algo = null; + if (isset($hash['data'])) + { + $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($hash['attribs']['']['algo'])) + { + $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $algo = 'md5'; + } + $hashes[] = $algo.':'.$value; + } + if (is_array($hashes)) + { + $hashes = array_values(array_unique($hashes)); + } + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'])) + { + foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash) + { + $value = null; + $algo = null; + if (isset($hash['data'])) + { + $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($hash['attribs']['']['algo'])) + { + $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $algo = 'md5'; + } + $hashes[] = $algo.':'.$value; + } + if (is_array($hashes)) + { + $hashes = array_values(array_unique($hashes)); + } + } + else + { + $hashes = $hashes_parent; + } + + // KEYWORDS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'])) + { + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'])) + { + $temp = explode(',', $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); + foreach ($temp as $word) + { + $keywords[] = trim($word); + } + unset($temp); + } + if (is_array($keywords)) + { + $keywords = array_values(array_unique($keywords)); + } + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'])) + { + if (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'])) + { + $temp = explode(',', $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); + foreach ($temp as $word) + { + $keywords[] = trim($word); + } + unset($temp); + } + if (is_array($keywords)) + { + $keywords = array_values(array_unique($keywords)); + } + } + else + { + $keywords = $keywords_parent; + } + + // PLAYER + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'])) + { + $player = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'])) + { + $player = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + else + { + $player = $player_parent; + } + + // RATINGS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating) + { + $rating_scheme = null; + $rating_value = null; + if (isset($rating['attribs']['']['scheme'])) + { + $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $rating_scheme = 'urn:simple'; + } + if (isset($rating['data'])) + { + $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $ratings[] = $this->registry->create('Rating', array($rating_scheme, $rating_value)); + } + if (is_array($ratings)) + { + $ratings = array_values(array_unique($ratings)); + } + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'])) + { + foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating) + { + $rating_scheme = null; + $rating_value = null; + if (isset($rating['attribs']['']['scheme'])) + { + $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $rating_scheme = 'urn:simple'; + } + if (isset($rating['data'])) + { + $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $ratings[] = $this->registry->create('Rating', array($rating_scheme, $rating_value)); + } + if (is_array($ratings)) + { + $ratings = array_values(array_unique($ratings)); + } + } + else + { + $ratings = $ratings_parent; + } + + // RESTRICTIONS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction) + { + $restriction_relationship = null; + $restriction_type = null; + $restriction_value = null; + if (isset($restriction['attribs']['']['relationship'])) + { + $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['attribs']['']['type'])) + { + $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['data'])) + { + $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $restrictions[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value)); + } + if (is_array($restrictions)) + { + $restrictions = array_values(array_unique($restrictions)); + } + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'])) + { + foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction) + { + $restriction_relationship = null; + $restriction_type = null; + $restriction_value = null; + if (isset($restriction['attribs']['']['relationship'])) + { + $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['attribs']['']['type'])) + { + $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['data'])) + { + $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $restrictions[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value)); + } + if (is_array($restrictions)) + { + $restrictions = array_values(array_unique($restrictions)); + } + } + else + { + $restrictions = $restrictions_parent; + } + + // THUMBNAILS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail) + { + $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + if (is_array($thumbnails)) + { + $thumbnails = array_values(array_unique($thumbnails)); + } + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'])) + { + foreach ($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail) + { + $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + if (is_array($thumbnails)) + { + $thumbnails = array_values(array_unique($thumbnails)); + } + } + else + { + $thumbnails = $thumbnails_parent; + } + + // TITLES + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'])) + { + $title = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (isset($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'])) + { + $title = $this->sanitize($group['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $title = $title_parent; + } + + $this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions, $categories, $channels, $copyrights, $credits, $description, $duration, $expression, $framerate, $hashes, $height, $keywords, $lang, $medium, $player, $ratings, $restrictions, $samplingrate, $thumbnails, $title, $width)); + } + } + } + } + + // If we have standalone media:content tags, loop through them. + if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'])) + { + foreach ((array) $this->data['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content'] as $content) + { + if (isset($content['attribs']['']['url']) || isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'])) + { + // Attributes + $bitrate = null; + $channels = null; + $duration = null; + $expression = null; + $framerate = null; + $height = null; + $javascript = null; + $lang = null; + $length = null; + $medium = null; + $samplingrate = null; + $type = null; + $url = null; + $width = null; + + // Elements + $captions = null; + $categories = null; + $copyrights = null; + $credits = null; + $description = null; + $hashes = null; + $keywords = null; + $player = null; + $ratings = null; + $restrictions = null; + $thumbnails = null; + $title = null; + + // Start checking the attributes of media:content + if (isset($content['attribs']['']['bitrate'])) + { + $bitrate = $this->sanitize($content['attribs']['']['bitrate'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['channels'])) + { + $channels = $this->sanitize($content['attribs']['']['channels'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['duration'])) + { + $duration = $this->sanitize($content['attribs']['']['duration'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $duration = $duration_parent; + } + if (isset($content['attribs']['']['expression'])) + { + $expression = $this->sanitize($content['attribs']['']['expression'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['framerate'])) + { + $framerate = $this->sanitize($content['attribs']['']['framerate'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['height'])) + { + $height = $this->sanitize($content['attribs']['']['height'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['lang'])) + { + $lang = $this->sanitize($content['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['fileSize'])) + { + $length = ceil($content['attribs']['']['fileSize']); + } + if (isset($content['attribs']['']['medium'])) + { + $medium = $this->sanitize($content['attribs']['']['medium'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['samplingrate'])) + { + $samplingrate = $this->sanitize($content['attribs']['']['samplingrate'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['type'])) + { + $type = $this->sanitize($content['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['width'])) + { + $width = $this->sanitize($content['attribs']['']['width'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['attribs']['']['url'])) + { + $url = $this->sanitize($content['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + // Checking the other optional media: elements. Priority: media:content, media:group, item, channel + + // CAPTIONS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['text'] as $caption) + { + $caption_type = null; + $caption_lang = null; + $caption_startTime = null; + $caption_endTime = null; + $caption_text = null; + if (isset($caption['attribs']['']['type'])) + { + $caption_type = $this->sanitize($caption['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['lang'])) + { + $caption_lang = $this->sanitize($caption['attribs']['']['lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['start'])) + { + $caption_startTime = $this->sanitize($caption['attribs']['']['start'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['attribs']['']['end'])) + { + $caption_endTime = $this->sanitize($caption['attribs']['']['end'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($caption['data'])) + { + $caption_text = $this->sanitize($caption['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $captions[] = $this->registry->create('Caption', array($caption_type, $caption_lang, $caption_startTime, $caption_endTime, $caption_text)); + } + if (is_array($captions)) + { + $captions = array_values(array_unique($captions)); + } + } + else + { + $captions = $captions_parent; + } + + // CATEGORIES + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'])) + { + foreach ((array) $content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['category'] as $category) + { + $term = null; + $scheme = null; + $label = null; + if (isset($category['data'])) + { + $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['scheme'])) + { + $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $scheme = 'http://search.yahoo.com/mrss/category_schema'; + } + if (isset($category['attribs']['']['label'])) + { + $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories[] = $this->registry->create('Category', array($term, $scheme, $label)); + } + } + if (is_array($categories) && is_array($categories_parent)) + { + $categories = array_values(array_unique(array_merge($categories, $categories_parent))); + } + elseif (is_array($categories)) + { + $categories = array_values(array_unique($categories)); + } + elseif (is_array($categories_parent)) + { + $categories = array_values(array_unique($categories_parent)); + } + else + { + $categories = null; + } + + // COPYRIGHTS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'])) + { + $copyright_url = null; + $copyright_label = null; + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'])) + { + $copyright_url = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'])) + { + $copyright_label = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['copyright'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $copyrights = $this->registry->create('Copyright', array($copyright_url, $copyright_label)); + } + else + { + $copyrights = $copyrights_parent; + } + + // CREDITS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['credit'] as $credit) + { + $credit_role = null; + $credit_scheme = null; + $credit_name = null; + if (isset($credit['attribs']['']['role'])) + { + $credit_role = $this->sanitize($credit['attribs']['']['role'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($credit['attribs']['']['scheme'])) + { + $credit_scheme = $this->sanitize($credit['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $credit_scheme = 'urn:ebu'; + } + if (isset($credit['data'])) + { + $credit_name = $this->sanitize($credit['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $credits[] = $this->registry->create('Credit', array($credit_role, $credit_scheme, $credit_name)); + } + if (is_array($credits)) + { + $credits = array_values(array_unique($credits)); + } + } + else + { + $credits = $credits_parent; + } + + // DESCRIPTION + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'])) + { + $description = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['description'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $description = $description_parent; + } + + // HASHES + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['hash'] as $hash) + { + $value = null; + $algo = null; + if (isset($hash['data'])) + { + $value = $this->sanitize($hash['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($hash['attribs']['']['algo'])) + { + $algo = $this->sanitize($hash['attribs']['']['algo'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $algo = 'md5'; + } + $hashes[] = $algo.':'.$value; + } + if (is_array($hashes)) + { + $hashes = array_values(array_unique($hashes)); + } + } + else + { + $hashes = $hashes_parent; + } + + // KEYWORDS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'])) + { + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'])) + { + $temp = explode(',', $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['keywords'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT)); + foreach ($temp as $word) + { + $keywords[] = trim($word); + } + unset($temp); + } + if (is_array($keywords)) + { + $keywords = array_values(array_unique($keywords)); + } + } + else + { + $keywords = $keywords_parent; + } + + // PLAYER + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'])) + { + $player = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + else + { + $player = $player_parent; + } + + // RATINGS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['rating'] as $rating) + { + $rating_scheme = null; + $rating_value = null; + if (isset($rating['attribs']['']['scheme'])) + { + $rating_scheme = $this->sanitize($rating['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $rating_scheme = 'urn:simple'; + } + if (isset($rating['data'])) + { + $rating_value = $this->sanitize($rating['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $ratings[] = $this->registry->create('Rating', array($rating_scheme, $rating_value)); + } + if (is_array($ratings)) + { + $ratings = array_values(array_unique($ratings)); + } + } + else + { + $ratings = $ratings_parent; + } + + // RESTRICTIONS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['restriction'] as $restriction) + { + $restriction_relationship = null; + $restriction_type = null; + $restriction_value = null; + if (isset($restriction['attribs']['']['relationship'])) + { + $restriction_relationship = $this->sanitize($restriction['attribs']['']['relationship'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['attribs']['']['type'])) + { + $restriction_type = $this->sanitize($restriction['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($restriction['data'])) + { + $restriction_value = $this->sanitize($restriction['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $restrictions[] = $this->registry->create('Restriction', array($restriction_relationship, $restriction_type, $restriction_value)); + } + if (is_array($restrictions)) + { + $restrictions = array_values(array_unique($restrictions)); + } + } + else + { + $restrictions = $restrictions_parent; + } + + // THUMBNAILS + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'])) + { + foreach ($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['thumbnail'] as $thumbnail) + { + $thumbnails[] = $this->sanitize($thumbnail['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } + if (is_array($thumbnails)) + { + $thumbnails = array_values(array_unique($thumbnails)); + } + } + else + { + $thumbnails = $thumbnails_parent; + } + + // TITLES + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'])) + { + $title = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['title'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $title = $title_parent; + } + + $this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions, $categories, $channels, $copyrights, $credits, $description, $duration, $expression, $framerate, $hashes, $height, $keywords, $lang, $medium, $player, $ratings, $restrictions, $samplingrate, $thumbnails, $title, $width)); + } + } + } + + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link') as $link) + { + if (isset($link['attribs']['']['href']) && !empty($link['attribs']['']['rel']) && $link['attribs']['']['rel'] === 'enclosure') + { + // Attributes + $bitrate = null; + $channels = null; + $duration = null; + $expression = null; + $framerate = null; + $height = null; + $javascript = null; + $lang = null; + $length = null; + $medium = null; + $samplingrate = null; + $type = null; + $url = null; + $width = null; + + $url = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); + if (isset($link['attribs']['']['type'])) + { + $type = $this->sanitize($link['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($link['attribs']['']['length'])) + { + $length = ceil($link['attribs']['']['length']); + } + + // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor + $this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width)); + } + } + + foreach ((array) $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link') as $link) + { + if (isset($link['attribs']['']['href']) && !empty($link['attribs']['']['rel']) && $link['attribs']['']['rel'] === 'enclosure') + { + // Attributes + $bitrate = null; + $channels = null; + $duration = null; + $expression = null; + $framerate = null; + $height = null; + $javascript = null; + $lang = null; + $length = null; + $medium = null; + $samplingrate = null; + $type = null; + $url = null; + $width = null; + + $url = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); + if (isset($link['attribs']['']['type'])) + { + $type = $this->sanitize($link['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($link['attribs']['']['length'])) + { + $length = ceil($link['attribs']['']['length']); + } + + // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor + $this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width)); + } + } + + if ($enclosure = $this->get_item_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'enclosure')) + { + if (isset($enclosure[0]['attribs']['']['url'])) + { + // Attributes + $bitrate = null; + $channels = null; + $duration = null; + $expression = null; + $framerate = null; + $height = null; + $javascript = null; + $lang = null; + $length = null; + $medium = null; + $samplingrate = null; + $type = null; + $url = null; + $width = null; + + $url = $this->sanitize($enclosure[0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($enclosure[0])); + if (isset($enclosure[0]['attribs']['']['type'])) + { + $type = $this->sanitize($enclosure[0]['attribs']['']['type'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($enclosure[0]['attribs']['']['length'])) + { + $length = ceil($enclosure[0]['attribs']['']['length']); + } + + // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor + $this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width)); + } + } + + if (sizeof($this->data['enclosures']) === 0 && ($url || $type || $length || $bitrate || $captions_parent || $categories_parent || $channels || $copyrights_parent || $credits_parent || $description_parent || $duration_parent || $expression || $framerate || $hashes_parent || $height || $keywords_parent || $lang || $medium || $player_parent || $ratings_parent || $restrictions_parent || $samplingrate || $thumbnails_parent || $title_parent || $width)) + { + // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor + $this->data['enclosures'][] = $this->registry->create('Enclosure', array($url, $type, $length, null, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width)); + } + + $this->data['enclosures'] = array_values(array_unique($this->data['enclosures'])); + } + if (!empty($this->data['enclosures'])) + { + return $this->data['enclosures']; + } + else + { + return null; + } + } + + /** + * Get the latitude coordinates for the item + * + * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications + * + * Uses `<geo:lat>` or `<georss:point>` + * + * @since 1.0 + * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo + * @link http://www.georss.org/ GeoRSS + * @return string|null + */ + public function get_latitude() + { + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat')) + { + return (float) $return[0]['data']; + } + elseif (($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) + { + return (float) $match[1]; + } + else + { + return null; + } + } + + /** + * Get the longitude coordinates for the item + * + * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications + * + * Uses `<geo:long>`, `<geo:lon>` or `<georss:point>` + * + * @since 1.0 + * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo + * @link http://www.georss.org/ GeoRSS + * @return string|null + */ + public function get_longitude() + { + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long')) + { + return (float) $return[0]['data']; + } + elseif ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon')) + { + return (float) $return[0]['data']; + } + elseif (($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) + { + return (float) $match[2]; + } + else + { + return null; + } + } + + /** + * Get the `<atom:source>` for the item + * + * @since 1.1 + * @return SimplePie_Source|null + */ + public function get_source() + { + if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'source')) + { + return $this->registry->create('Source', array($this, $return[0])); + } + else + { + return null; + } + } +} + diff --git a/wp-includes/SimplePie/Locator.php b/wp-includes/SimplePie/Locator.php new file mode 100644 index 0000000..da89514 --- /dev/null +++ b/wp-includes/SimplePie/Locator.php @@ -0,0 +1,372 @@ +<?php +/** + * SimplePie + * + * A PHP-Based RSS and Atom Feed Framework. + * Takes the hard work out of managing a complete RSS/Atom solution. + * + * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * * Neither the name of the SimplePie Team nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS + * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package SimplePie + * @version 1.3.1 + * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @author Ryan Parman + * @author Geoffrey Sneddon + * @author Ryan McCue + * @link http://simplepie.org/ SimplePie + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + */ + +/** + * Used for feed auto-discovery + * + * + * This class can be overloaded with {@see SimplePie::set_locator_class()} + * + * @package SimplePie + */ +class SimplePie_Locator +{ + var $useragent; + var $timeout; + var $file; + var $local = array(); + var $elsewhere = array(); + var $cached_entities = array(); + var $http_base; + var $base; + var $base_location = 0; + var $checked_feeds = 0; + var $max_checked_feeds = 10; + protected $registry; + + public function __construct(SimplePie_File $file, $timeout = 10, $useragent = null, $max_checked_feeds = 10) + { + $this->file = $file; + $this->useragent = $useragent; + $this->timeout = $timeout; + $this->max_checked_feeds = $max_checked_feeds; + + if (class_exists('DOMDocument')) + { + $this->dom = new DOMDocument(); + + set_error_handler(array('SimplePie_Misc', 'silence_errors')); + $this->dom->loadHTML($this->file->body); + restore_error_handler(); + } + else + { + $this->dom = null; + } + } + + public function set_registry(SimplePie_Registry $registry) + { + $this->registry = $registry; + } + + public function find($type = SIMPLEPIE_LOCATOR_ALL, &$working) + { + if ($this->is_feed($this->file)) + { + return $this->file; + } + + if ($this->file->method & SIMPLEPIE_FILE_SOURCE_REMOTE) + { + $sniffer = $this->registry->create('Content_Type_Sniffer', array($this->file)); + if ($sniffer->get_type() !== 'text/html') + { + return null; + } + } + + if ($type & ~SIMPLEPIE_LOCATOR_NONE) + { + $this->get_base(); + } + + if ($type & SIMPLEPIE_LOCATOR_AUTODISCOVERY && $working = $this->autodiscovery()) + { + return $working[0]; + } + + if ($type & (SIMPLEPIE_LOCATOR_LOCAL_EXTENSION | SIMPLEPIE_LOCATOR_LOCAL_BODY | SIMPLEPIE_LOCATOR_REMOTE_EXTENSION | SIMPLEPIE_LOCATOR_REMOTE_BODY) && $this->get_links()) + { + if ($type & SIMPLEPIE_LOCATOR_LOCAL_EXTENSION && $working = $this->extension($this->local)) + { + return $working; + } + + if ($type & SIMPLEPIE_LOCATOR_LOCAL_BODY && $working = $this->body($this->local)) + { + return $working; + } + + if ($type & SIMPLEPIE_LOCATOR_REMOTE_EXTENSION && $working = $this->extension($this->elsewhere)) + { + return $working; + } + + if ($type & SIMPLEPIE_LOCATOR_REMOTE_BODY && $working = $this->body($this->elsewhere)) + { + return $working; + } + } + return null; + } + + public function is_feed($file) + { + if ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE) + { + $sniffer = $this->registry->create('Content_Type_Sniffer', array($file)); + $sniffed = $sniffer->get_type(); + if (in_array($sniffed, array('application/rss+xml', 'application/rdf+xml', 'text/rdf', 'application/atom+xml', 'text/xml', 'application/xml'))) + { + return true; + } + else + { + return false; + } + } + elseif ($file->method & SIMPLEPIE_FILE_SOURCE_LOCAL) + { + return true; + } + else + { + return false; + } + } + + public function get_base() + { + if ($this->dom === null) + { + throw new SimplePie_Exception('DOMDocument not found, unable to use locator'); + } + $this->http_base = $this->file->url; + $this->base = $this->http_base; + $elements = $this->dom->getElementsByTagName('base'); + foreach ($elements as $element) + { + if ($element->hasAttribute('href')) + { + $base = $this->registry->call('Misc', 'absolutize_url', array(trim($element->getAttribute('href')), $this->http_base)); + if ($base === false) + { + continue; + } + $this->base = $base; + $this->base_location = method_exists($element, 'getLineNo') ? $element->getLineNo() : 0; + break; + } + } + } + + public function autodiscovery() + { + $done = array(); + $feeds = array(); + $feeds = array_merge($feeds, $this->search_elements_by_tag('link', $done, $feeds)); + $feeds = array_merge($feeds, $this->search_elements_by_tag('a', $done, $feeds)); + $feeds = array_merge($feeds, $this->search_elements_by_tag('area', $done, $feeds)); + + if (!empty($feeds)) + { + return array_values($feeds); + } + else + { + return null; + } + } + + protected function search_elements_by_tag($name, &$done, $feeds) + { + if ($this->dom === null) + { + throw new SimplePie_Exception('DOMDocument not found, unable to use locator'); + } + + $links = $this->dom->getElementsByTagName($name); + foreach ($links as $link) + { + if ($this->checked_feeds === $this->max_checked_feeds) + { + break; + } + if ($link->hasAttribute('href') && $link->hasAttribute('rel')) + { + $rel = array_unique($this->registry->call('Misc', 'space_seperated_tokens', array(strtolower($link->getAttribute('rel'))))); + $line = method_exists($link, 'getLineNo') ? $link->getLineNo() : 1; + + if ($this->base_location < $line) + { + $href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->base)); + } + else + { + $href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->http_base)); + } + if ($href === false) + { + continue; + } + + if (!in_array($href, $done) && in_array('feed', $rel) || (in_array('alternate', $rel) && !in_array('stylesheet', $rel) && $link->hasAttribute('type') && in_array(strtolower($this->registry->call('Misc', 'parse_mime', array($link->getAttribute('type')))), array('application/rss+xml', 'application/atom+xml'))) && !isset($feeds[$href])) + { + $this->checked_feeds++; + $headers = array( + 'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1', + ); + $feed = $this->registry->create('File', array($href, $this->timeout, 5, $headers, $this->useragent)); + if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed)) + { + $feeds[$href] = $feed; + } + } + $done[] = $href; + } + } + + return $feeds; + } + + public function get_links() + { + if ($this->dom === null) + { + throw new SimplePie_Exception('DOMDocument not found, unable to use locator'); + } + + $links = $this->dom->getElementsByTagName('a'); + foreach ($links as $link) + { + if ($link->hasAttribute('href')) + { + $href = trim($link->getAttribute('href')); + $parsed = $this->registry->call('Misc', 'parse_url', array($href)); + if ($parsed['scheme'] === '' || preg_match('/^(http(s)|feed)?$/i', $parsed['scheme'])) + { + if (method_exists($link, 'getLineNo') && $this->base_location < $link->getLineNo()) + { + $href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->base)); + } + else + { + $href = $this->registry->call('Misc', 'absolutize_url', array(trim($link->getAttribute('href')), $this->http_base)); + } + if ($href === false) + { + continue; + } + + $current = $this->registry->call('Misc', 'parse_url', array($this->file->url)); + + if ($parsed['authority'] === '' || $parsed['authority'] === $current['authority']) + { + $this->local[] = $href; + } + else + { + $this->elsewhere[] = $href; + } + } + } + } + $this->local = array_unique($this->local); + $this->elsewhere = array_unique($this->elsewhere); + if (!empty($this->local) || !empty($this->elsewhere)) + { + return true; + } + return null; + } + + public function extension(&$array) + { + foreach ($array as $key => $value) + { + if ($this->checked_feeds === $this->max_checked_feeds) + { + break; + } + if (in_array(strtolower(strrchr($value, '.')), array('.rss', '.rdf', '.atom', '.xml'))) + { + $this->checked_feeds++; + + $headers = array( + 'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1', + ); + $feed = $this->registry->create('File', array($value, $this->timeout, 5, $headers, $this->useragent)); + if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed)) + { + return $feed; + } + else + { + unset($array[$key]); + } + } + } + return null; + } + + public function body(&$array) + { + foreach ($array as $key => $value) + { + if ($this->checked_feeds === $this->max_checked_feeds) + { + break; + } + if (preg_match('/(rss|rdf|atom|xml)/i', $value)) + { + $this->checked_feeds++; + $headers = array( + 'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1', + ); + $feed = $this->registry->create('File', array($value, $this->timeout, 5, null, $this->useragent)); + if ($feed->success && ($feed->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($feed->status_code === 200 || $feed->status_code > 206 && $feed->status_code < 300)) && $this->is_feed($feed)) + { + return $feed; + } + else + { + unset($array[$key]); + } + } + } + return null; + } +} + diff --git a/wp-includes/SimplePie/Misc.php b/wp-includes/SimplePie/Misc.php new file mode 100644 index 0000000..1c1b618 --- /dev/null +++ b/wp-includes/SimplePie/Misc.php @@ -0,0 +1,2247 @@ +<?php +/** + * SimplePie + * + * A PHP-Based RSS and Atom Feed Framework. + * Takes the hard work out of managing a complete RSS/Atom solution. + * + * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * * Neither the name of the SimplePie Team nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS + * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package SimplePie + * @version 1.3.1 + * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @author Ryan Parman + * @author Geoffrey Sneddon + * @author Ryan McCue + * @link http://simplepie.org/ SimplePie + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + */ + +/** + * Miscellanous utilities + * + * @package SimplePie + */ +class SimplePie_Misc +{ + public static function time_hms($seconds) + { + $time = ''; + + $hours = floor($seconds / 3600); + $remainder = $seconds % 3600; + if ($hours > 0) + { + $time .= $hours.':'; + } + + $minutes = floor($remainder / 60); + $seconds = $remainder % 60; + if ($minutes < 10 && $hours > 0) + { + $minutes = '0' . $minutes; + } + if ($seconds < 10) + { + $seconds = '0' . $seconds; + } + + $time .= $minutes.':'; + $time .= $seconds; + + return $time; + } + + public static function absolutize_url($relative, $base) + { + $iri = SimplePie_IRI::absolutize(new SimplePie_IRI($base), $relative); + if ($iri === false) + { + return false; + } + return $iri->get_uri(); + } + + /** + * Get a HTML/XML element from a HTML string + * + * @deprecated Use DOMDocument instead (parsing HTML with regex is bad!) + * @param string $realname Element name (including namespace prefix if applicable) + * @param string $string HTML document + * @return array + */ + public static function get_element($realname, $string) + { + $return = array(); + $name = preg_quote($realname, '/'); + if (preg_match_all("/<($name)" . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . "(>(.*)<\/$name>|(\/)?>)/siU", $string, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) + { + for ($i = 0, $total_matches = count($matches); $i < $total_matches; $i++) + { + $return[$i]['tag'] = $realname; + $return[$i]['full'] = $matches[$i][0][0]; + $return[$i]['offset'] = $matches[$i][0][1]; + if (strlen($matches[$i][3][0]) <= 2) + { + $return[$i]['self_closing'] = true; + } + else + { + $return[$i]['self_closing'] = false; + $return[$i]['content'] = $matches[$i][4][0]; + } + $return[$i]['attribs'] = array(); + if (isset($matches[$i][2][0]) && preg_match_all('/[\x09\x0A\x0B\x0C\x0D\x20]+([^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3D\x3E]*)(?:[\x09\x0A\x0B\x0C\x0D\x20]*=[\x09\x0A\x0B\x0C\x0D\x20]*(?:"([^"]*)"|\'([^\']*)\'|([^\x09\x0A\x0B\x0C\x0D\x20\x22\x27\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x3E]*)?))?/', ' ' . $matches[$i][2][0] . ' ', $attribs, PREG_SET_ORDER)) + { + for ($j = 0, $total_attribs = count($attribs); $j < $total_attribs; $j++) + { + if (count($attribs[$j]) === 2) + { + $attribs[$j][2] = $attribs[$j][1]; + } + $return[$i]['attribs'][strtolower($attribs[$j][1])]['data'] = SimplePie_Misc::entities_decode(end($attribs[$j])); + } + } + } + } + return $return; + } + + public static function element_implode($element) + { + $full = "<$element[tag]"; + foreach ($element['attribs'] as $key => $value) + { + $key = strtolower($key); + $full .= " $key=\"" . htmlspecialchars($value['data']) . '"'; + } + if ($element['self_closing']) + { + $full .= ' />'; + } + else + { + $full .= ">$element[content]</$element[tag]>"; + } + return $full; + } + + public static function error($message, $level, $file, $line) + { + if ((ini_get('error_reporting') & $level) > 0) + { + switch ($level) + { + case E_USER_ERROR: + $note = 'PHP Error'; + break; + case E_USER_WARNING: + $note = 'PHP Warning'; + break; + case E_USER_NOTICE: + $note = 'PHP Notice'; + break; + default: + $note = 'Unknown Error'; + break; + } + + $log_error = true; + if (!function_exists('error_log')) + { + $log_error = false; + } + + $log_file = @ini_get('error_log'); + if (!empty($log_file) && ('syslog' !== $log_file) && !@is_writable($log_file)) + { + $log_error = false; + } + + if ($log_error) + { + @error_log("$note: $message in $file on line $line", 0); + } + } + + return $message; + } + + public static function fix_protocol($url, $http = 1) + { + $url = SimplePie_Misc::normalize_url($url); + $parsed = SimplePie_Misc::parse_url($url); + if ($parsed['scheme'] !== '' && $parsed['scheme'] !== 'http' && $parsed['scheme'] !== 'https') + { + return SimplePie_Misc::fix_protocol(SimplePie_Misc::compress_parse_url('http', $parsed['authority'], $parsed['path'], $parsed['query'], $parsed['fragment']), $http); + } + + if ($parsed['scheme'] === '' && $parsed['authority'] === '' && !file_exists($url)) + { + return SimplePie_Misc::fix_protocol(SimplePie_Misc::compress_parse_url('http', $parsed['path'], '', $parsed['query'], $parsed['fragment']), $http); + } + + if ($http === 2 && $parsed['scheme'] !== '') + { + return "feed:$url"; + } + elseif ($http === 3 && strtolower($parsed['scheme']) === 'http') + { + return substr_replace($url, 'podcast', 0, 4); + } + elseif ($http === 4 && strtolower($parsed['scheme']) === 'http') + { + return substr_replace($url, 'itpc', 0, 4); + } + else + { + return $url; + } + } + + public static function parse_url($url) + { + $iri = new SimplePie_IRI($url); + return array( + 'scheme' => (string) $iri->scheme, + 'authority' => (string) $iri->authority, + 'path' => (string) $iri->path, + 'query' => (string) $iri->query, + 'fragment' => (string) $iri->fragment + ); + } + + public static function compress_parse_url($scheme = '', $authority = '', $path = '', $query = '', $fragment = '') + { + $iri = new SimplePie_IRI(''); + $iri->scheme = $scheme; + $iri->authority = $authority; + $iri->path = $path; + $iri->query = $query; + $iri->fragment = $fragment; + return $iri->get_uri(); + } + + public static function normalize_url($url) + { + $iri = new SimplePie_IRI($url); + return $iri->get_uri(); + } + + public static function percent_encoding_normalization($match) + { + $integer = hexdec($match[1]); + if ($integer >= 0x41 && $integer <= 0x5A || $integer >= 0x61 && $integer <= 0x7A || $integer >= 0x30 && $integer <= 0x39 || $integer === 0x2D || $integer === 0x2E || $integer === 0x5F || $integer === 0x7E) + { + return chr($integer); + } + else + { + return strtoupper($match[0]); + } + } + + /** + * Converts a Windows-1252 encoded string to a UTF-8 encoded string + * + * @static + * @param string $string Windows-1252 encoded string + * @return string UTF-8 encoded string + */ + public static function windows_1252_to_utf8($string) + { + static $convert_table = array("\x80" => "\xE2\x82\xAC", "\x81" => "\xEF\xBF\xBD", "\x82" => "\xE2\x80\x9A", "\x83" => "\xC6\x92", "\x84" => "\xE2\x80\x9E", "\x85" => "\xE2\x80\xA6", "\x86" => "\xE2\x80\xA0", "\x87" => "\xE2\x80\xA1", "\x88" => "\xCB\x86", "\x89" => "\xE2\x80\xB0", "\x8A" => "\xC5\xA0", "\x8B" => "\xE2\x80\xB9", "\x8C" => "\xC5\x92", "\x8D" => "\xEF\xBF\xBD", "\x8E" => "\xC5\xBD", "\x8F" => "\xEF\xBF\xBD", "\x90" => "\xEF\xBF\xBD", "\x91" => "\xE2\x80\x98", "\x92" => "\xE2\x80\x99", "\x93" => "\xE2\x80\x9C", "\x94" => "\xE2\x80\x9D", "\x95" => "\xE2\x80\xA2", "\x96" => "\xE2\x80\x93", "\x97" => "\xE2\x80\x94", "\x98" => "\xCB\x9C", "\x99" => "\xE2\x84\xA2", "\x9A" => "\xC5\xA1", "\x9B" => "\xE2\x80\xBA", "\x9C" => "\xC5\x93", "\x9D" => "\xEF\xBF\xBD", "\x9E" => "\xC5\xBE", "\x9F" => "\xC5\xB8", "\xA0" => "\xC2\xA0", "\xA1" => "\xC2\xA1", "\xA2" => "\xC2\xA2", "\xA3" => "\xC2\xA3", "\xA4" => "\xC2\xA4", "\xA5" => "\xC2\xA5", "\xA6" => "\xC2\xA6", "\xA7" => "\xC2\xA7", "\xA8" => "\xC2\xA8", "\xA9" => "\xC2\xA9", "\xAA" => "\xC2\xAA", "\xAB" => "\xC2\xAB", "\xAC" => "\xC2\xAC", "\xAD" => "\xC2\xAD", "\xAE" => "\xC2\xAE", "\xAF" => "\xC2\xAF", "\xB0" => "\xC2\xB0", "\xB1" => "\xC2\xB1", "\xB2" => "\xC2\xB2", "\xB3" => "\xC2\xB3", "\xB4" => "\xC2\xB4", "\xB5" => "\xC2\xB5", "\xB6" => "\xC2\xB6", "\xB7" => "\xC2\xB7", "\xB8" => "\xC2\xB8", "\xB9" => "\xC2\xB9", "\xBA" => "\xC2\xBA", "\xBB" => "\xC2\xBB", "\xBC" => "\xC2\xBC", "\xBD" => "\xC2\xBD", "\xBE" => "\xC2\xBE", "\xBF" => "\xC2\xBF", "\xC0" => "\xC3\x80", "\xC1" => "\xC3\x81", "\xC2" => "\xC3\x82", "\xC3" => "\xC3\x83", "\xC4" => "\xC3\x84", "\xC5" => "\xC3\x85", "\xC6" => "\xC3\x86", "\xC7" => "\xC3\x87", "\xC8" => "\xC3\x88", "\xC9" => "\xC3\x89", "\xCA" => "\xC3\x8A", "\xCB" => "\xC3\x8B", "\xCC" => "\xC3\x8C", "\xCD" => "\xC3\x8D", "\xCE" => "\xC3\x8E", "\xCF" => "\xC3\x8F", "\xD0" => "\xC3\x90", "\xD1" => "\xC3\x91", "\xD2" => "\xC3\x92", "\xD3" => "\xC3\x93", "\xD4" => "\xC3\x94", "\xD5" => "\xC3\x95", "\xD6" => "\xC3\x96", "\xD7" => "\xC3\x97", "\xD8" => "\xC3\x98", "\xD9" => "\xC3\x99", "\xDA" => "\xC3\x9A", "\xDB" => "\xC3\x9B", "\xDC" => "\xC3\x9C", "\xDD" => "\xC3\x9D", "\xDE" => "\xC3\x9E", "\xDF" => "\xC3\x9F", "\xE0" => "\xC3\xA0", "\xE1" => "\xC3\xA1", "\xE2" => "\xC3\xA2", "\xE3" => "\xC3\xA3", "\xE4" => "\xC3\xA4", "\xE5" => "\xC3\xA5", "\xE6" => "\xC3\xA6", "\xE7" => "\xC3\xA7", "\xE8" => "\xC3\xA8", "\xE9" => "\xC3\xA9", "\xEA" => "\xC3\xAA", "\xEB" => "\xC3\xAB", "\xEC" => "\xC3\xAC", "\xED" => "\xC3\xAD", "\xEE" => "\xC3\xAE", "\xEF" => "\xC3\xAF", "\xF0" => "\xC3\xB0", "\xF1" => "\xC3\xB1", "\xF2" => "\xC3\xB2", "\xF3" => "\xC3\xB3", "\xF4" => "\xC3\xB4", "\xF5" => "\xC3\xB5", "\xF6" => "\xC3\xB6", "\xF7" => "\xC3\xB7", "\xF8" => "\xC3\xB8", "\xF9" => "\xC3\xB9", "\xFA" => "\xC3\xBA", "\xFB" => "\xC3\xBB", "\xFC" => "\xC3\xBC", "\xFD" => "\xC3\xBD", "\xFE" => "\xC3\xBE", "\xFF" => "\xC3\xBF"); + + return strtr($string, $convert_table); + } + + /** + * Change a string from one encoding to another + * + * @param string $data Raw data in $input encoding + * @param string $input Encoding of $data + * @param string $output Encoding you want + * @return string|boolean False if we can't convert it + */ + public static function change_encoding($data, $input, $output) + { + $input = SimplePie_Misc::encoding($input); + $output = SimplePie_Misc::encoding($output); + + // We fail to fail on non US-ASCII bytes + if ($input === 'US-ASCII') + { + static $non_ascii_octects = ''; + if (!$non_ascii_octects) + { + for ($i = 0x80; $i <= 0xFF; $i++) + { + $non_ascii_octects .= chr($i); + } + } + $data = substr($data, 0, strcspn($data, $non_ascii_octects)); + } + + // This is first, as behaviour of this is completely predictable + if ($input === 'windows-1252' && $output === 'UTF-8') + { + return SimplePie_Misc::windows_1252_to_utf8($data); + } + // This is second, as behaviour of this varies only with PHP version (the middle part of this expression checks the encoding is supported). + elseif (function_exists('mb_convert_encoding') && ($return = SimplePie_Misc::change_encoding_mbstring($data, $input, $output))) + { + return $return; + } + // This is last, as behaviour of this varies with OS userland and PHP version + elseif (function_exists('iconv') && ($return = SimplePie_Misc::change_encoding_iconv($data, $input, $output))) + { + return $return; + } + // If we can't do anything, just fail + else + { + return false; + } + } + + protected static function change_encoding_mbstring($data, $input, $output) + { + if ($input === 'windows-949') + { + $input = 'EUC-KR'; + } + if ($output === 'windows-949') + { + $output = 'EUC-KR'; + } + if ($input === 'Windows-31J') + { + $input = 'SJIS'; + } + if ($output === 'Windows-31J') + { + $output = 'SJIS'; + } + + // Check that the encoding is supported + if (@mb_convert_encoding("\x80", 'UTF-16BE', $input) === "\x00\x80") + { + return false; + } + if (!in_array($input, mb_list_encodings())) + { + return false; + } + + // Let's do some conversion + if ($return = @mb_convert_encoding($data, $output, $input)) + { + return $return; + } + + return false; + } + + protected static function change_encoding_iconv($data, $input, $output) + { + return @iconv($input, $output, $data); + } + + /** + * Normalize an encoding name + * + * This is automatically generated by create.php + * + * To generate it, run `php create.php` on the command line, and copy the + * output to replace this function. + * + * @param string $charset Character set to standardise + * @return string Standardised name + */ + public static function encoding($charset) + { + // Normalization from UTS #22 + switch (strtolower(preg_replace('/(?:[^a-zA-Z0-9]+|([^0-9])0+)/', '\1', $charset))) + { + case 'adobestandardencoding': + case 'csadobestandardencoding': + return 'Adobe-Standard-Encoding'; + + case 'adobesymbolencoding': + case 'cshppsmath': + return 'Adobe-Symbol-Encoding'; + + case 'ami1251': + case 'amiga1251': + return 'Amiga-1251'; + + case 'ansix31101983': + case 'csat5001983': + case 'csiso99naplps': + case 'isoir99': + case 'naplps': + return 'ANSI_X3.110-1983'; + + case 'arabic7': + case 'asmo449': + case 'csiso89asmo449': + case 'iso9036': + case 'isoir89': + return 'ASMO_449'; + + case 'big5': + case 'csbig5': + return 'Big5'; + + case 'big5hkscs': + return 'Big5-HKSCS'; + + case 'bocu1': + case 'csbocu1': + return 'BOCU-1'; + + case 'brf': + case 'csbrf': + return 'BRF'; + + case 'bs4730': + case 'csiso4unitedkingdom': + case 'gb': + case 'iso646gb': + case 'isoir4': + case 'uk': + return 'BS_4730'; + + case 'bsviewdata': + case 'csiso47bsviewdata': + case 'isoir47': + return 'BS_viewdata'; + + case 'cesu8': + case 'cscesu8': + return 'CESU-8'; + + case 'ca': + case 'csa71': + case 'csaz243419851': + case 'csiso121canadian1': + case 'iso646ca': + case 'isoir121': + return 'CSA_Z243.4-1985-1'; + + case 'csa72': + case 'csaz243419852': + case 'csiso122canadian2': + case 'iso646ca2': + case 'isoir122': + return 'CSA_Z243.4-1985-2'; + + case 'csaz24341985gr': + case 'csiso123csaz24341985gr': + case 'isoir123': + return 'CSA_Z243.4-1985-gr'; + + case 'csiso139csn369103': + case 'csn369103': + case 'isoir139': + return 'CSN_369103'; + + case 'csdecmcs': + case 'dec': + case 'decmcs': + return 'DEC-MCS'; + + case 'csiso21german': + case 'de': + case 'din66003': + case 'iso646de': + case 'isoir21': + return 'DIN_66003'; + + case 'csdkus': + case 'dkus': + return 'dk-us'; + + case 'csiso646danish': + case 'dk': + case 'ds2089': + case 'iso646dk': + return 'DS_2089'; + + case 'csibmebcdicatde': + case 'ebcdicatde': + return 'EBCDIC-AT-DE'; + + case 'csebcdicatdea': + case 'ebcdicatdea': + return 'EBCDIC-AT-DE-A'; + + case 'csebcdiccafr': + case 'ebcdiccafr': + return 'EBCDIC-CA-FR'; + + case 'csebcdicdkno': + case 'ebcdicdkno': + return 'EBCDIC-DK-NO'; + + case 'csebcdicdknoa': + case 'ebcdicdknoa': + return 'EBCDIC-DK-NO-A'; + + case 'csebcdices': + case 'ebcdices': + return 'EBCDIC-ES'; + + case 'csebcdicesa': + case 'ebcdicesa': + return 'EBCDIC-ES-A'; + + case 'csebcdicess': + case 'ebcdicess': + return 'EBCDIC-ES-S'; + + case 'csebcdicfise': + case 'ebcdicfise': + return 'EBCDIC-FI-SE'; + + case 'csebcdicfisea': + case 'ebcdicfisea': + return 'EBCDIC-FI-SE-A'; + + case 'csebcdicfr': + case 'ebcdicfr': + return 'EBCDIC-FR'; + + case 'csebcdicit': + case 'ebcdicit': + return 'EBCDIC-IT'; + + case 'csebcdicpt': + case 'ebcdicpt': + return 'EBCDIC-PT'; + + case 'csebcdicuk': + case 'ebcdicuk': + return 'EBCDIC-UK'; + + case 'csebcdicus': + case 'ebcdicus': + return 'EBCDIC-US'; + + case 'csiso111ecmacyrillic': + case 'ecmacyrillic': + case 'isoir111': + case 'koi8e': + return 'ECMA-cyrillic'; + + case 'csiso17spanish': + case 'es': + case 'iso646es': + case 'isoir17': + return 'ES'; + + case 'csiso85spanish2': + case 'es2': + case 'iso646es2': + case 'isoir85': + return 'ES2'; + + case 'cseucpkdfmtjapanese': + case 'eucjp': + case 'extendedunixcodepackedformatforjapanese': + return 'EUC-JP'; + + case 'cseucfixwidjapanese': + case 'extendedunixcodefixedwidthforjapanese': + return 'Extended_UNIX_Code_Fixed_Width_for_Japanese'; + + case 'gb18030': + return 'GB18030'; + + case 'chinese': + case 'cp936': + case 'csgb2312': + case 'csiso58gb231280': + case 'gb2312': + case 'gb231280': + case 'gbk': + case 'isoir58': + case 'ms936': + case 'windows936': + return 'GBK'; + + case 'cn': + case 'csiso57gb1988': + case 'gb198880': + case 'iso646cn': + case 'isoir57': + return 'GB_1988-80'; + + case 'csiso153gost1976874': + case 'gost1976874': + case 'isoir153': + case 'stsev35888': + return 'GOST_19768-74'; + + case 'csiso150': + case 'csiso150greekccitt': + case 'greekccitt': + case 'isoir150': + return 'greek-ccitt'; + + case 'csiso88greek7': + case 'greek7': + case 'isoir88': + return 'greek7'; + + case 'csiso18greek7old': + case 'greek7old': + case 'isoir18': + return 'greek7-old'; + + case 'cshpdesktop': + case 'hpdesktop': + return 'HP-DeskTop'; + + case 'cshplegal': + case 'hplegal': + return 'HP-Legal'; + + case 'cshpmath8': + case 'hpmath8': + return 'HP-Math8'; + + case 'cshppifont': + case 'hppifont': + return 'HP-Pi-font'; + + case 'cshproman8': + case 'hproman8': + case 'r8': + case 'roman8': + return 'hp-roman8'; + + case 'hzgb2312': + return 'HZ-GB-2312'; + + case 'csibmsymbols': + case 'ibmsymbols': + return 'IBM-Symbols'; + + case 'csibmthai': + case 'ibmthai': + return 'IBM-Thai'; + + case 'cp37': + case 'csibm37': + case 'ebcdiccpca': + case 'ebcdiccpnl': + case 'ebcdiccpus': + case 'ebcdiccpwt': + case 'ibm37': + return 'IBM037'; + + case 'cp38': + case 'csibm38': + case 'ebcdicint': + case 'ibm38': + return 'IBM038'; + + case 'cp273': + case 'csibm273': + case 'ibm273': + return 'IBM273'; + + case 'cp274': + case 'csibm274': + case 'ebcdicbe': + case 'ibm274': + return 'IBM274'; + + case 'cp275': + case 'csibm275': + case 'ebcdicbr': + case 'ibm275': + return 'IBM275'; + + case 'csibm277': + case 'ebcdiccpdk': + case 'ebcdiccpno': + case 'ibm277': + return 'IBM277'; + + case 'cp278': + case 'csibm278': + case 'ebcdiccpfi': + case 'ebcdiccpse': + case 'ibm278': + return 'IBM278'; + + case 'cp280': + case 'csibm280': + case 'ebcdiccpit': + case 'ibm280': + return 'IBM280'; + + case 'cp281': + case 'csibm281': + case 'ebcdicjpe': + case 'ibm281': + return 'IBM281'; + + case 'cp284': + case 'csibm284': + case 'ebcdiccpes': + case 'ibm284': + return 'IBM284'; + + case 'cp285': + case 'csibm285': + case 'ebcdiccpgb': + case 'ibm285': + return 'IBM285'; + + case 'cp290': + case 'csibm290': + case 'ebcdicjpkana': + case 'ibm290': + return 'IBM290'; + + case 'cp297': + case 'csibm297': + case 'ebcdiccpfr': + case 'ibm297': + return 'IBM297'; + + case 'cp420': + case 'csibm420': + case 'ebcdiccpar1': + case 'ibm420': + return 'IBM420'; + + case 'cp423': + case 'csibm423': + case 'ebcdiccpgr': + case 'ibm423': + return 'IBM423'; + + case 'cp424': + case 'csibm424': + case 'ebcdiccphe': + case 'ibm424': + return 'IBM424'; + + case '437': + case 'cp437': + case 'cspc8codepage437': + case 'ibm437': + return 'IBM437'; + + case 'cp500': + case 'csibm500': + case 'ebcdiccpbe': + case 'ebcdiccpch': + case 'ibm500': + return 'IBM500'; + + case 'cp775': + case 'cspc775baltic': + case 'ibm775': + return 'IBM775'; + + case '850': + case 'cp850': + case 'cspc850multilingual': + case 'ibm850': + return 'IBM850'; + + case '851': + case 'cp851': + case 'csibm851': + case 'ibm851': + return 'IBM851'; + + case '852': + case 'cp852': + case 'cspcp852': + case 'ibm852': + return 'IBM852'; + + case '855': + case 'cp855': + case 'csibm855': + case 'ibm855': + return 'IBM855'; + + case '857': + case 'cp857': + case 'csibm857': + case 'ibm857': + return 'IBM857'; + + case 'ccsid858': + case 'cp858': + case 'ibm858': + case 'pcmultilingual850euro': + return 'IBM00858'; + + case '860': + case 'cp860': + case 'csibm860': + case 'ibm860': + return 'IBM860'; + + case '861': + case 'cp861': + case 'cpis': + case 'csibm861': + case 'ibm861': + return 'IBM861'; + + case '862': + case 'cp862': + case 'cspc862latinhebrew': + case 'ibm862': + return 'IBM862'; + + case '863': + case 'cp863': + case 'csibm863': + case 'ibm863': + return 'IBM863'; + + case 'cp864': + case 'csibm864': + case 'ibm864': + return 'IBM864'; + + case '865': + case 'cp865': + case 'csibm865': + case 'ibm865': + return 'IBM865'; + + case '866': + case 'cp866': + case 'csibm866': + case 'ibm866': + return 'IBM866'; + + case 'cp868': + case 'cpar': + case 'csibm868': + case 'ibm868': + return 'IBM868'; + + case '869': + case 'cp869': + case 'cpgr': + case 'csibm869': + case 'ibm869': + return 'IBM869'; + + case 'cp870': + case 'csibm870': + case 'ebcdiccproece': + case 'ebcdiccpyu': + case 'ibm870': + return 'IBM870'; + + case 'cp871': + case 'csibm871': + case 'ebcdiccpis': + case 'ibm871': + return 'IBM871'; + + case 'cp880': + case 'csibm880': + case 'ebcdiccyrillic': + case 'ibm880': + return 'IBM880'; + + case 'cp891': + case 'csibm891': + case 'ibm891': + return 'IBM891'; + + case 'cp903': + case 'csibm903': + case 'ibm903': + return 'IBM903'; + + case '904': + case 'cp904': + case 'csibbm904': + case 'ibm904': + return 'IBM904'; + + case 'cp905': + case 'csibm905': + case 'ebcdiccptr': + case 'ibm905': + return 'IBM905'; + + case 'cp918': + case 'csibm918': + case 'ebcdiccpar2': + case 'ibm918': + return 'IBM918'; + + case 'ccsid924': + case 'cp924': + case 'ebcdiclatin9euro': + case 'ibm924': + return 'IBM00924'; + + case 'cp1026': + case 'csibm1026': + case 'ibm1026': + return 'IBM1026'; + + case 'ibm1047': + return 'IBM1047'; + + case 'ccsid1140': + case 'cp1140': + case 'ebcdicus37euro': + case 'ibm1140': + return 'IBM01140'; + + case 'ccsid1141': + case 'cp1141': + case 'ebcdicde273euro': + case 'ibm1141': + return 'IBM01141'; + + case 'ccsid1142': + case 'cp1142': + case 'ebcdicdk277euro': + case 'ebcdicno277euro': + case 'ibm1142': + return 'IBM01142'; + + case 'ccsid1143': + case 'cp1143': + case 'ebcdicfi278euro': + case 'ebcdicse278euro': + case 'ibm1143': + return 'IBM01143'; + + case 'ccsid1144': + case 'cp1144': + case 'ebcdicit280euro': + case 'ibm1144': + return 'IBM01144'; + + case 'ccsid1145': + case 'cp1145': + case 'ebcdices284euro': + case 'ibm1145': + return 'IBM01145'; + + case 'ccsid1146': + case 'cp1146': + case 'ebcdicgb285euro': + case 'ibm1146': + return 'IBM01146'; + + case 'ccsid1147': + case 'cp1147': + case 'ebcdicfr297euro': + case 'ibm1147': + return 'IBM01147'; + + case 'ccsid1148': + case 'cp1148': + case 'ebcdicinternational500euro': + case 'ibm1148': + return 'IBM01148'; + + case 'ccsid1149': + case 'cp1149': + case 'ebcdicis871euro': + case 'ibm1149': + return 'IBM01149'; + + case 'csiso143iecp271': + case 'iecp271': + case 'isoir143': + return 'IEC_P27-1'; + + case 'csiso49inis': + case 'inis': + case 'isoir49': + return 'INIS'; + + case 'csiso50inis8': + case 'inis8': + case 'isoir50': + return 'INIS-8'; + + case 'csiso51iniscyrillic': + case 'iniscyrillic': + case 'isoir51': + return 'INIS-cyrillic'; + + case 'csinvariant': + case 'invariant': + return 'INVARIANT'; + + case 'iso2022cn': + return 'ISO-2022-CN'; + + case 'iso2022cnext': + return 'ISO-2022-CN-EXT'; + + case 'csiso2022jp': + case 'iso2022jp': + return 'ISO-2022-JP'; + + case 'csiso2022jp2': + case 'iso2022jp2': + return 'ISO-2022-JP-2'; + + case 'csiso2022kr': + case 'iso2022kr': + return 'ISO-2022-KR'; + + case 'cswindows30latin1': + case 'iso88591windows30latin1': + return 'ISO-8859-1-Windows-3.0-Latin-1'; + + case 'cswindows31latin1': + case 'iso88591windows31latin1': + return 'ISO-8859-1-Windows-3.1-Latin-1'; + + case 'csisolatin2': + case 'iso88592': + case 'iso885921987': + case 'isoir101': + case 'l2': + case 'latin2': + return 'ISO-8859-2'; + + case 'cswindows31latin2': + case 'iso88592windowslatin2': + return 'ISO-8859-2-Windows-Latin-2'; + + case 'csisolatin3': + case 'iso88593': + case 'iso885931988': + case 'isoir109': + case 'l3': + case 'latin3': + return 'ISO-8859-3'; + + case 'csisolatin4': + case 'iso88594': + case 'iso885941988': + case 'isoir110': + case 'l4': + case 'latin4': + return 'ISO-8859-4'; + + case 'csisolatincyrillic': + case 'cyrillic': + case 'iso88595': + case 'iso885951988': + case 'isoir144': + return 'ISO-8859-5'; + + case 'arabic': + case 'asmo708': + case 'csisolatinarabic': + case 'ecma114': + case 'iso88596': + case 'iso885961987': + case 'isoir127': + return 'ISO-8859-6'; + + case 'csiso88596e': + case 'iso88596e': + return 'ISO-8859-6-E'; + + case 'csiso88596i': + case 'iso88596i': + return 'ISO-8859-6-I'; + + case 'csisolatingreek': + case 'ecma118': + case 'elot928': + case 'greek': + case 'greek8': + case 'iso88597': + case 'iso885971987': + case 'isoir126': + return 'ISO-8859-7'; + + case 'csisolatinhebrew': + case 'hebrew': + case 'iso88598': + case 'iso885981988': + case 'isoir138': + return 'ISO-8859-8'; + + case 'csiso88598e': + case 'iso88598e': + return 'ISO-8859-8-E'; + + case 'csiso88598i': + case 'iso88598i': + return 'ISO-8859-8-I'; + + case 'cswindows31latin5': + case 'iso88599windowslatin5': + return 'ISO-8859-9-Windows-Latin-5'; + + case 'csisolatin6': + case 'iso885910': + case 'iso8859101992': + case 'isoir157': + case 'l6': + case 'latin6': + return 'ISO-8859-10'; + + case 'iso885913': + return 'ISO-8859-13'; + + case 'iso885914': + case 'iso8859141998': + case 'isoceltic': + case 'isoir199': + case 'l8': + case 'latin8': + return 'ISO-8859-14'; + + case 'iso885915': + case 'latin9': + return 'ISO-8859-15'; + + case 'iso885916': + case 'iso8859162001': + case 'isoir226': + case 'l10': + case 'latin10': + return 'ISO-8859-16'; + + case 'iso10646j1': + return 'ISO-10646-J-1'; + + case 'csunicode': + case 'iso10646ucs2': + return 'ISO-10646-UCS-2'; + + case 'csucs4': + case 'iso10646ucs4': + return 'ISO-10646-UCS-4'; + + case 'csunicodeascii': + case 'iso10646ucsbasic': + return 'ISO-10646-UCS-Basic'; + + case 'csunicodelatin1': + case 'iso10646': + case 'iso10646unicodelatin1': + return 'ISO-10646-Unicode-Latin1'; + + case 'csiso10646utf1': + case 'iso10646utf1': + return 'ISO-10646-UTF-1'; + + case 'csiso115481': + case 'iso115481': + case 'isotr115481': + return 'ISO-11548-1'; + + case 'csiso90': + case 'isoir90': + return 'iso-ir-90'; + + case 'csunicodeibm1261': + case 'isounicodeibm1261': + return 'ISO-Unicode-IBM-1261'; + + case 'csunicodeibm1264': + case 'isounicodeibm1264': + return 'ISO-Unicode-IBM-1264'; + + case 'csunicodeibm1265': + case 'isounicodeibm1265': + return 'ISO-Unicode-IBM-1265'; + + case 'csunicodeibm1268': + case 'isounicodeibm1268': + return 'ISO-Unicode-IBM-1268'; + + case 'csunicodeibm1276': + case 'isounicodeibm1276': + return 'ISO-Unicode-IBM-1276'; + + case 'csiso646basic1983': + case 'iso646basic1983': + case 'ref': + return 'ISO_646.basic:1983'; + + case 'csiso2intlrefversion': + case 'irv': + case 'iso646irv1983': + case 'isoir2': + return 'ISO_646.irv:1983'; + + case 'csiso2033': + case 'e13b': + case 'iso20331983': + case 'isoir98': + return 'ISO_2033-1983'; + + case 'csiso5427cyrillic': + case 'iso5427': + case 'isoir37': + return 'ISO_5427'; + + case 'iso5427cyrillic1981': + case 'iso54271981': + case 'isoir54': + return 'ISO_5427:1981'; + + case 'csiso5428greek': + case 'iso54281980': + case 'isoir55': + return 'ISO_5428:1980'; + + case 'csiso6937add': + case 'iso6937225': + case 'isoir152': + return 'ISO_6937-2-25'; + + case 'csisotextcomm': + case 'iso69372add': + case 'isoir142': + return 'ISO_6937-2-add'; + + case 'csiso8859supp': + case 'iso8859supp': + case 'isoir154': + case 'latin125': + return 'ISO_8859-supp'; + + case 'csiso10367box': + case 'iso10367box': + case 'isoir155': + return 'ISO_10367-box'; + + case 'csiso15italian': + case 'iso646it': + case 'isoir15': + case 'it': + return 'IT'; + + case 'csiso13jisc6220jp': + case 'isoir13': + case 'jisc62201969': + case 'jisc62201969jp': + case 'katakana': + case 'x2017': + return 'JIS_C6220-1969-jp'; + + case 'csiso14jisc6220ro': + case 'iso646jp': + case 'isoir14': + case 'jisc62201969ro': + case 'jp': + return 'JIS_C6220-1969-ro'; + + case 'csiso42jisc62261978': + case 'isoir42': + case 'jisc62261978': + return 'JIS_C6226-1978'; + + case 'csiso87jisx208': + case 'isoir87': + case 'jisc62261983': + case 'jisx2081983': + case 'x208': + return 'JIS_C6226-1983'; + + case 'csiso91jisc62291984a': + case 'isoir91': + case 'jisc62291984a': + case 'jpocra': + return 'JIS_C6229-1984-a'; + + case 'csiso92jisc62991984b': + case 'iso646jpocrb': + case 'isoir92': + case 'jisc62291984b': + case 'jpocrb': + return 'JIS_C6229-1984-b'; + + case 'csiso93jis62291984badd': + case 'isoir93': + case 'jisc62291984badd': + case 'jpocrbadd': + return 'JIS_C6229-1984-b-add'; + + case 'csiso94jis62291984hand': + case 'isoir94': + case 'jisc62291984hand': + case 'jpocrhand': + return 'JIS_C6229-1984-hand'; + + case 'csiso95jis62291984handadd': + case 'isoir95': + case 'jisc62291984handadd': + case 'jpocrhandadd': + return 'JIS_C6229-1984-hand-add'; + + case 'csiso96jisc62291984kana': + case 'isoir96': + case 'jisc62291984kana': + return 'JIS_C6229-1984-kana'; + + case 'csjisencoding': + case 'jisencoding': + return 'JIS_Encoding'; + + case 'cshalfwidthkatakana': + case 'jisx201': + case 'x201': + return 'JIS_X0201'; + + case 'csiso159jisx2121990': + case 'isoir159': + case 'jisx2121990': + case 'x212': + return 'JIS_X0212-1990'; + + case 'csiso141jusib1002': + case 'iso646yu': + case 'isoir141': + case 'js': + case 'jusib1002': + case 'yu': + return 'JUS_I.B1.002'; + + case 'csiso147macedonian': + case 'isoir147': + case 'jusib1003mac': + case 'macedonian': + return 'JUS_I.B1.003-mac'; + + case 'csiso146serbian': + case 'isoir146': + case 'jusib1003serb': + case 'serbian': + return 'JUS_I.B1.003-serb'; + + case 'koi7switched': + return 'KOI7-switched'; + + case 'cskoi8r': + case 'koi8r': + return 'KOI8-R'; + + case 'koi8u': + return 'KOI8-U'; + + case 'csksc5636': + case 'iso646kr': + case 'ksc5636': + return 'KSC5636'; + + case 'cskz1048': + case 'kz1048': + case 'rk1048': + case 'strk10482002': + return 'KZ-1048'; + + case 'csiso19latingreek': + case 'isoir19': + case 'latingreek': + return 'latin-greek'; + + case 'csiso27latingreek1': + case 'isoir27': + case 'latingreek1': + return 'Latin-greek-1'; + + case 'csiso158lap': + case 'isoir158': + case 'lap': + case 'latinlap': + return 'latin-lap'; + + case 'csmacintosh': + case 'mac': + case 'macintosh': + return 'macintosh'; + + case 'csmicrosoftpublishing': + case 'microsoftpublishing': + return 'Microsoft-Publishing'; + + case 'csmnem': + case 'mnem': + return 'MNEM'; + + case 'csmnemonic': + case 'mnemonic': + return 'MNEMONIC'; + + case 'csiso86hungarian': + case 'hu': + case 'iso646hu': + case 'isoir86': + case 'msz77953': + return 'MSZ_7795.3'; + + case 'csnatsdano': + case 'isoir91': + case 'natsdano': + return 'NATS-DANO'; + + case 'csnatsdanoadd': + case 'isoir92': + case 'natsdanoadd': + return 'NATS-DANO-ADD'; + + case 'csnatssefi': + case 'isoir81': + case 'natssefi': + return 'NATS-SEFI'; + + case 'csnatssefiadd': + case 'isoir82': + case 'natssefiadd': + return 'NATS-SEFI-ADD'; + + case 'csiso151cuba': + case 'cuba': + case 'iso646cu': + case 'isoir151': + case 'ncnc1081': + return 'NC_NC00-10:81'; + + case 'csiso69french': + case 'fr': + case 'iso646fr': + case 'isoir69': + case 'nfz62010': + return 'NF_Z_62-010'; + + case 'csiso25french': + case 'iso646fr1': + case 'isoir25': + case 'nfz620101973': + return 'NF_Z_62-010_(1973)'; + + case 'csiso60danishnorwegian': + case 'csiso60norwegian1': + case 'iso646no': + case 'isoir60': + case 'no': + case 'ns45511': + return 'NS_4551-1'; + + case 'csiso61norwegian2': + case 'iso646no2': + case 'isoir61': + case 'no2': + case 'ns45512': + return 'NS_4551-2'; + + case 'osdebcdicdf3irv': + return 'OSD_EBCDIC_DF03_IRV'; + + case 'osdebcdicdf41': + return 'OSD_EBCDIC_DF04_1'; + + case 'osdebcdicdf415': + return 'OSD_EBCDIC_DF04_15'; + + case 'cspc8danishnorwegian': + case 'pc8danishnorwegian': + return 'PC8-Danish-Norwegian'; + + case 'cspc8turkish': + case 'pc8turkish': + return 'PC8-Turkish'; + + case 'csiso16portuguese': + case 'iso646pt': + case 'isoir16': + case 'pt': + return 'PT'; + + case 'csiso84portuguese2': + case 'iso646pt2': + case 'isoir84': + case 'pt2': + return 'PT2'; + + case 'cp154': + case 'csptcp154': + case 'cyrillicasian': + case 'pt154': + case 'ptcp154': + return 'PTCP154'; + + case 'scsu': + return 'SCSU'; + + case 'csiso10swedish': + case 'fi': + case 'iso646fi': + case 'iso646se': + case 'isoir10': + case 'se': + case 'sen850200b': + return 'SEN_850200_B'; + + case 'csiso11swedishfornames': + case 'iso646se2': + case 'isoir11': + case 'se2': + case 'sen850200c': + return 'SEN_850200_C'; + + case 'csiso102t617bit': + case 'isoir102': + case 't617bit': + return 'T.61-7bit'; + + case 'csiso103t618bit': + case 'isoir103': + case 't61': + case 't618bit': + return 'T.61-8bit'; + + case 'csiso128t101g2': + case 'isoir128': + case 't101g2': + return 'T.101-G2'; + + case 'cstscii': + case 'tscii': + return 'TSCII'; + + case 'csunicode11': + case 'unicode11': + return 'UNICODE-1-1'; + + case 'csunicode11utf7': + case 'unicode11utf7': + return 'UNICODE-1-1-UTF-7'; + + case 'csunknown8bit': + case 'unknown8bit': + return 'UNKNOWN-8BIT'; + + case 'ansix341968': + case 'ansix341986': + case 'ascii': + case 'cp367': + case 'csascii': + case 'ibm367': + case 'iso646irv1991': + case 'iso646us': + case 'isoir6': + case 'us': + case 'usascii': + return 'US-ASCII'; + + case 'csusdk': + case 'usdk': + return 'us-dk'; + + case 'utf7': + return 'UTF-7'; + + case 'utf8': + return 'UTF-8'; + + case 'utf16': + return 'UTF-16'; + + case 'utf16be': + return 'UTF-16BE'; + + case 'utf16le': + return 'UTF-16LE'; + + case 'utf32': + return 'UTF-32'; + + case 'utf32be': + return 'UTF-32BE'; + + case 'utf32le': + return 'UTF-32LE'; + + case 'csventurainternational': + case 'venturainternational': + return 'Ventura-International'; + + case 'csventuramath': + case 'venturamath': + return 'Ventura-Math'; + + case 'csventuraus': + case 'venturaus': + return 'Ventura-US'; + + case 'csiso70videotexsupp1': + case 'isoir70': + case 'videotexsuppl': + return 'videotex-suppl'; + + case 'csviqr': + case 'viqr': + return 'VIQR'; + + case 'csviscii': + case 'viscii': + return 'VISCII'; + + case 'csshiftjis': + case 'cswindows31j': + case 'mskanji': + case 'shiftjis': + case 'windows31j': + return 'Windows-31J'; + + case 'iso885911': + case 'tis620': + return 'windows-874'; + + case 'cseuckr': + case 'csksc56011987': + case 'euckr': + case 'isoir149': + case 'korean': + case 'ksc5601': + case 'ksc56011987': + case 'ksc56011989': + case 'windows949': + return 'windows-949'; + + case 'windows1250': + return 'windows-1250'; + + case 'windows1251': + return 'windows-1251'; + + case 'cp819': + case 'csisolatin1': + case 'ibm819': + case 'iso88591': + case 'iso885911987': + case 'isoir100': + case 'l1': + case 'latin1': + case 'windows1252': + return 'windows-1252'; + + case 'windows1253': + return 'windows-1253'; + + case 'csisolatin5': + case 'iso88599': + case 'iso885991989': + case 'isoir148': + case 'l5': + case 'latin5': + case 'windows1254': + return 'windows-1254'; + + case 'windows1255': + return 'windows-1255'; + + case 'windows1256': + return 'windows-1256'; + + case 'windows1257': + return 'windows-1257'; + + case 'windows1258': + return 'windows-1258'; + + default: + return $charset; + } + } + + public static function get_curl_version() + { + if (is_array($curl = curl_version())) + { + $curl = $curl['version']; + } + elseif (substr($curl, 0, 5) === 'curl/') + { + $curl = substr($curl, 5, strcspn($curl, "\x09\x0A\x0B\x0C\x0D", 5)); + } + elseif (substr($curl, 0, 8) === 'libcurl/') + { + $curl = substr($curl, 8, strcspn($curl, "\x09\x0A\x0B\x0C\x0D", 8)); + } + else + { + $curl = 0; + } + return $curl; + } + + /** + * Strip HTML comments + * + * @param string $data Data to strip comments from + * @return string Comment stripped string + */ + public static function strip_comments($data) + { + $output = ''; + while (($start = strpos($data, '<!--')) !== false) + { + $output .= substr($data, 0, $start); + if (($end = strpos($data, '-->', $start)) !== false) + { + $data = substr_replace($data, '', 0, $end + 3); + } + else + { + $data = ''; + } + } + return $output . $data; + } + + public static function parse_date($dt) + { + $parser = SimplePie_Parse_Date::get(); + return $parser->parse($dt); + } + + /** + * Decode HTML entities + * + * @deprecated Use DOMDocument instead + * @param string $data Input data + * @return string Output data + */ + public static function entities_decode($data) + { + $decoder = new SimplePie_Decode_HTML_Entities($data); + return $decoder->parse(); + } + + /** + * Remove RFC822 comments + * + * @param string $data Data to strip comments from + * @return string Comment stripped string + */ + public static function uncomment_rfc822($string) + { + $string = (string) $string; + $position = 0; + $length = strlen($string); + $depth = 0; + + $output = ''; + + while ($position < $length && ($pos = strpos($string, '(', $position)) !== false) + { + $output .= substr($string, $position, $pos - $position); + $position = $pos + 1; + if ($string[$pos - 1] !== '\\') + { + $depth++; + while ($depth && $position < $length) + { + $position += strcspn($string, '()', $position); + if ($string[$position - 1] === '\\') + { + $position++; + continue; + } + elseif (isset($string[$position])) + { + switch ($string[$position]) + { + case '(': + $depth++; + break; + + case ')': + $depth--; + break; + } + $position++; + } + else + { + break; + } + } + } + else + { + $output .= '('; + } + } + $output .= substr($string, $position); + + return $output; + } + + public static function parse_mime($mime) + { + if (($pos = strpos($mime, ';')) === false) + { + return trim($mime); + } + else + { + return trim(substr($mime, 0, $pos)); + } + } + + public static function atom_03_construct_type($attribs) + { + if (isset($attribs['']['mode']) && strtolower(trim($attribs['']['mode']) === 'base64')) + { + $mode = SIMPLEPIE_CONSTRUCT_BASE64; + } + else + { + $mode = SIMPLEPIE_CONSTRUCT_NONE; + } + if (isset($attribs['']['type'])) + { + switch (strtolower(trim($attribs['']['type']))) + { + case 'text': + case 'text/plain': + return SIMPLEPIE_CONSTRUCT_TEXT | $mode; + + case 'html': + case 'text/html': + return SIMPLEPIE_CONSTRUCT_HTML | $mode; + + case 'xhtml': + case 'application/xhtml+xml': + return SIMPLEPIE_CONSTRUCT_XHTML | $mode; + + default: + return SIMPLEPIE_CONSTRUCT_NONE | $mode; + } + } + else + { + return SIMPLEPIE_CONSTRUCT_TEXT | $mode; + } + } + + public static function atom_10_construct_type($attribs) + { + if (isset($attribs['']['type'])) + { + switch (strtolower(trim($attribs['']['type']))) + { + case 'text': + return SIMPLEPIE_CONSTRUCT_TEXT; + + case 'html': + return SIMPLEPIE_CONSTRUCT_HTML; + + case 'xhtml': + return SIMPLEPIE_CONSTRUCT_XHTML; + + default: + return SIMPLEPIE_CONSTRUCT_NONE; + } + } + return SIMPLEPIE_CONSTRUCT_TEXT; + } + + public static function atom_10_content_construct_type($attribs) + { + if (isset($attribs['']['type'])) + { + $type = strtolower(trim($attribs['']['type'])); + switch ($type) + { + case 'text': + return SIMPLEPIE_CONSTRUCT_TEXT; + + case 'html': + return SIMPLEPIE_CONSTRUCT_HTML; + + case 'xhtml': + return SIMPLEPIE_CONSTRUCT_XHTML; + } + if (in_array(substr($type, -4), array('+xml', '/xml')) || substr($type, 0, 5) === 'text/') + { + return SIMPLEPIE_CONSTRUCT_NONE; + } + else + { + return SIMPLEPIE_CONSTRUCT_BASE64; + } + } + else + { + return SIMPLEPIE_CONSTRUCT_TEXT; + } + } + + public static function is_isegment_nz_nc($string) + { + return (bool) preg_match('/^([A-Za-z0-9\-._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!$&\'()*+,;=@]|(%[0-9ABCDEF]{2}))+$/u', $string); + } + + public static function space_seperated_tokens($string) + { + $space_characters = "\x20\x09\x0A\x0B\x0C\x0D"; + $string_length = strlen($string); + + $position = strspn($string, $space_characters); + $tokens = array(); + + while ($position < $string_length) + { + $len = strcspn($string, $space_characters, $position); + $tokens[] = substr($string, $position, $len); + $position += $len; + $position += strspn($string, $space_characters, $position); + } + + return $tokens; + } + + /** + * Converts a unicode codepoint to a UTF-8 character + * + * @static + * @param int $codepoint Unicode codepoint + * @return string UTF-8 character + */ + public static function codepoint_to_utf8($codepoint) + { + $codepoint = (int) $codepoint; + if ($codepoint < 0) + { + return false; + } + else if ($codepoint <= 0x7f) + { + return chr($codepoint); + } + else if ($codepoint <= 0x7ff) + { + return chr(0xc0 | ($codepoint >> 6)) . chr(0x80 | ($codepoint & 0x3f)); + } + else if ($codepoint <= 0xffff) + { + return chr(0xe0 | ($codepoint >> 12)) . chr(0x80 | (($codepoint >> 6) & 0x3f)) . chr(0x80 | ($codepoint & 0x3f)); + } + else if ($codepoint <= 0x10ffff) + { + return chr(0xf0 | ($codepoint >> 18)) . chr(0x80 | (($codepoint >> 12) & 0x3f)) . chr(0x80 | (($codepoint >> 6) & 0x3f)) . chr(0x80 | ($codepoint & 0x3f)); + } + else + { + // U+FFFD REPLACEMENT CHARACTER + return "\xEF\xBF\xBD"; + } + } + + /** + * Similar to parse_str() + * + * Returns an associative array of name/value pairs, where the value is an + * array of values that have used the same name + * + * @static + * @param string $str The input string. + * @return array + */ + public static function parse_str($str) + { + $return = array(); + $str = explode('&', $str); + + foreach ($str as $section) + { + if (strpos($section, '=') !== false) + { + list($name, $value) = explode('=', $section, 2); + $return[urldecode($name)][] = urldecode($value); + } + else + { + $return[urldecode($section)][] = null; + } + } + + return $return; + } + + /** + * Detect XML encoding, as per XML 1.0 Appendix F.1 + * + * @todo Add support for EBCDIC + * @param string $data XML data + * @param SimplePie_Registry $registry Class registry + * @return array Possible encodings + */ + public static function xml_encoding($data, $registry) + { + // UTF-32 Big Endian BOM + if (substr($data, 0, 4) === "\x00\x00\xFE\xFF") + { + $encoding[] = 'UTF-32BE'; + } + // UTF-32 Little Endian BOM + elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00") + { + $encoding[] = 'UTF-32LE'; + } + // UTF-16 Big Endian BOM + elseif (substr($data, 0, 2) === "\xFE\xFF") + { + $encoding[] = 'UTF-16BE'; + } + // UTF-16 Little Endian BOM + elseif (substr($data, 0, 2) === "\xFF\xFE") + { + $encoding[] = 'UTF-16LE'; + } + // UTF-8 BOM + elseif (substr($data, 0, 3) === "\xEF\xBB\xBF") + { + $encoding[] = 'UTF-8'; + } + // UTF-32 Big Endian Without BOM + elseif (substr($data, 0, 20) === "\x00\x00\x00\x3C\x00\x00\x00\x3F\x00\x00\x00\x78\x00\x00\x00\x6D\x00\x00\x00\x6C") + { + if ($pos = strpos($data, "\x00\x00\x00\x3F\x00\x00\x00\x3E")) + { + $parser = $registry->create('XML_Declaration_Parser', array(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 20), 'UTF-32BE', 'UTF-8'))); + if ($parser->parse()) + { + $encoding[] = $parser->encoding; + } + } + $encoding[] = 'UTF-32BE'; + } + // UTF-32 Little Endian Without BOM + elseif (substr($data, 0, 20) === "\x3C\x00\x00\x00\x3F\x00\x00\x00\x78\x00\x00\x00\x6D\x00\x00\x00\x6C\x00\x00\x00") + { + if ($pos = strpos($data, "\x3F\x00\x00\x00\x3E\x00\x00\x00")) + { + $parser = $registry->create('XML_Declaration_Parser', array(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 20), 'UTF-32LE', 'UTF-8'))); + if ($parser->parse()) + { + $encoding[] = $parser->encoding; + } + } + $encoding[] = 'UTF-32LE'; + } + // UTF-16 Big Endian Without BOM + elseif (substr($data, 0, 10) === "\x00\x3C\x00\x3F\x00\x78\x00\x6D\x00\x6C") + { + if ($pos = strpos($data, "\x00\x3F\x00\x3E")) + { + $parser = $registry->create('XML_Declaration_Parser', array(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 10), 'UTF-16BE', 'UTF-8'))); + if ($parser->parse()) + { + $encoding[] = $parser->encoding; + } + } + $encoding[] = 'UTF-16BE'; + } + // UTF-16 Little Endian Without BOM + elseif (substr($data, 0, 10) === "\x3C\x00\x3F\x00\x78\x00\x6D\x00\x6C\x00") + { + if ($pos = strpos($data, "\x3F\x00\x3E\x00")) + { + $parser = $registry->create('XML_Declaration_Parser', array(SimplePie_Misc::change_encoding(substr($data, 20, $pos - 10), 'UTF-16LE', 'UTF-8'))); + if ($parser->parse()) + { + $encoding[] = $parser->encoding; + } + } + $encoding[] = 'UTF-16LE'; + } + // US-ASCII (or superset) + elseif (substr($data, 0, 5) === "\x3C\x3F\x78\x6D\x6C") + { + if ($pos = strpos($data, "\x3F\x3E")) + { + $parser = $registry->create('XML_Declaration_Parser', array(substr($data, 5, $pos - 5))); + if ($parser->parse()) + { + $encoding[] = $parser->encoding; + } + } + $encoding[] = 'UTF-8'; + } + // Fallback to UTF-8 + else + { + $encoding[] = 'UTF-8'; + } + return $encoding; + } + + public static function output_javascript() + { + if (function_exists('ob_gzhandler')) + { + ob_start('ob_gzhandler'); + } + header('Content-type: text/javascript; charset: UTF-8'); + header('Cache-Control: must-revalidate'); + header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 604800) . ' GMT'); // 7 days + ?> +function embed_quicktime(type, bgcolor, width, height, link, placeholder, loop) { + if (placeholder != '') { + document.writeln('<embed type="'+type+'" style="cursor:hand; cursor:pointer;" href="'+link+'" src="'+placeholder+'" width="'+width+'" height="'+height+'" autoplay="false" target="myself" controller="false" loop="'+loop+'" scale="aspect" bgcolor="'+bgcolor+'" pluginspage="http://www.apple.com/quicktime/download/"></embed>'); + } + else { + document.writeln('<embed type="'+type+'" style="cursor:hand; cursor:pointer;" src="'+link+'" width="'+width+'" height="'+height+'" autoplay="false" target="myself" controller="true" loop="'+loop+'" scale="aspect" bgcolor="'+bgcolor+'" pluginspage="http://www.apple.com/quicktime/download/"></embed>'); + } +} + +function embed_flash(bgcolor, width, height, link, loop, type) { + document.writeln('<embed src="'+link+'" pluginspage="http://www.macromedia.com/go/getflashplayer" type="'+type+'" quality="high" width="'+width+'" height="'+height+'" bgcolor="'+bgcolor+'" loop="'+loop+'"></embed>'); +} + +function embed_flv(width, height, link, placeholder, loop, player) { + document.writeln('<embed src="'+player+'" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" quality="high" width="'+width+'" height="'+height+'" wmode="transparent" flashvars="file='+link+'&autostart=false&repeat='+loop+'&showdigits=true&showfsbutton=false"></embed>'); +} + +function embed_wmedia(width, height, link) { + document.writeln('<embed type="application/x-mplayer2" src="'+link+'" autosize="1" width="'+width+'" height="'+height+'" showcontrols="1" showstatusbar="0" showdisplay="0" autostart="0"></embed>'); +} + <?php + } + + /** + * Get the SimplePie build timestamp + * + * Uses the git index if it exists, otherwise uses the modification time + * of the newest file. + */ + public static function get_build() + { + $root = dirname(dirname(__FILE__)); + if (file_exists($root . '/.git/index')) + { + return filemtime($root . '/.git/index'); + } + elseif (file_exists($root . '/SimplePie')) + { + $time = 0; + foreach (glob($root . '/SimplePie/*.php') as $file) + { + if (($mtime = filemtime($file)) > $time) + { + $time = $mtime; + } + } + return $time; + } + elseif (file_exists(dirname(__FILE__) . '/Core.php')) + { + return filemtime(dirname(__FILE__) . '/Core.php'); + } + else + { + return filemtime(__FILE__); + } + } + + /** + * Format debugging information + */ + public static function debug(&$sp) + { + $info = 'SimplePie ' . SIMPLEPIE_VERSION . ' Build ' . SIMPLEPIE_BUILD . "\n"; + $info .= 'PHP ' . PHP_VERSION . "\n"; + if ($sp->error() !== null) + { + $info .= 'Error occurred: ' . $sp->error() . "\n"; + } + else + { + $info .= "No error found.\n"; + } + $info .= "Extensions:\n"; + $extensions = array('pcre', 'curl', 'zlib', 'mbstring', 'iconv', 'xmlreader', 'xml'); + foreach ($extensions as $ext) + { + if (extension_loaded($ext)) + { + $info .= " $ext loaded\n"; + switch ($ext) + { + case 'pcre': + $info .= ' Version ' . PCRE_VERSION . "\n"; + break; + case 'curl': + $version = curl_version(); + $info .= ' Version ' . $version['version'] . "\n"; + break; + case 'mbstring': + $info .= ' Overloading: ' . mb_get_info('func_overload') . "\n"; + break; + case 'iconv': + $info .= ' Version ' . ICONV_VERSION . "\n"; + break; + case 'xml': + $info .= ' Version ' . LIBXML_DOTTED_VERSION . "\n"; + break; + } + } + else + { + $info .= " $ext not loaded\n"; + } + } + return $info; + } + + public static function silence_errors($num, $str) + { + // No-op + } +} + diff --git a/wp-includes/SimplePie/Net/IPv6.php b/wp-includes/SimplePie/Net/IPv6.php new file mode 100644 index 0000000..da80d8a --- /dev/null +++ b/wp-includes/SimplePie/Net/IPv6.php @@ -0,0 +1,276 @@ +<?php +/** + * SimplePie + * + * A PHP-Based RSS and Atom Feed Framework. + * Takes the hard work out of managing a complete RSS/Atom solution. + * + * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * * Neither the name of the SimplePie Team nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS + * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package SimplePie + * @version 1.3.1 + * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @author Ryan Parman + * @author Geoffrey Sneddon + * @author Ryan McCue + * @link http://simplepie.org/ SimplePie + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + */ + + +/** + * Class to validate and to work with IPv6 addresses. + * + * @package SimplePie + * @subpackage HTTP + * @copyright 2003-2005 The PHP Group + * @license http://www.opensource.org/licenses/bsd-license.php + * @link http://pear.php.net/package/Net_IPv6 + * @author Alexander Merz <alexander.merz@web.de> + * @author elfrink at introweb dot nl + * @author Josh Peck <jmp at joshpeck dot org> + * @author Geoffrey Sneddon <geoffers@gmail.com> + */ +class SimplePie_Net_IPv6 +{ + /** + * Uncompresses an IPv6 address + * + * RFC 4291 allows you to compress concecutive zero pieces in an address to + * '::'. This method expects a valid IPv6 address and expands the '::' to + * the required number of zero pieces. + * + * Example: FF01::101 -> FF01:0:0:0:0:0:0:101 + * ::1 -> 0:0:0:0:0:0:0:1 + * + * @author Alexander Merz <alexander.merz@web.de> + * @author elfrink at introweb dot nl + * @author Josh Peck <jmp at joshpeck dot org> + * @copyright 2003-2005 The PHP Group + * @license http://www.opensource.org/licenses/bsd-license.php + * @param string $ip An IPv6 address + * @return string The uncompressed IPv6 address + */ + public static function uncompress($ip) + { + $c1 = -1; + $c2 = -1; + if (substr_count($ip, '::') === 1) + { + list($ip1, $ip2) = explode('::', $ip); + if ($ip1 === '') + { + $c1 = -1; + } + else + { + $c1 = substr_count($ip1, ':'); + } + if ($ip2 === '') + { + $c2 = -1; + } + else + { + $c2 = substr_count($ip2, ':'); + } + if (strpos($ip2, '.') !== false) + { + $c2++; + } + // :: + if ($c1 === -1 && $c2 === -1) + { + $ip = '0:0:0:0:0:0:0:0'; + } + // ::xxx + else if ($c1 === -1) + { + $fill = str_repeat('0:', 7 - $c2); + $ip = str_replace('::', $fill, $ip); + } + // xxx:: + else if ($c2 === -1) + { + $fill = str_repeat(':0', 7 - $c1); + $ip = str_replace('::', $fill, $ip); + } + // xxx::xxx + else + { + $fill = ':' . str_repeat('0:', 6 - $c2 - $c1); + $ip = str_replace('::', $fill, $ip); + } + } + return $ip; + } + + /** + * Compresses an IPv6 address + * + * RFC 4291 allows you to compress concecutive zero pieces in an address to + * '::'. This method expects a valid IPv6 address and compresses consecutive + * zero pieces to '::'. + * + * Example: FF01:0:0:0:0:0:0:101 -> FF01::101 + * 0:0:0:0:0:0:0:1 -> ::1 + * + * @see uncompress() + * @param string $ip An IPv6 address + * @return string The compressed IPv6 address + */ + public static function compress($ip) + { + // Prepare the IP to be compressed + $ip = self::uncompress($ip); + $ip_parts = self::split_v6_v4($ip); + + // Replace all leading zeros + $ip_parts[0] = preg_replace('/(^|:)0+([0-9])/', '\1\2', $ip_parts[0]); + + // Find bunches of zeros + if (preg_match_all('/(?:^|:)(?:0(?::|$))+/', $ip_parts[0], $matches, PREG_OFFSET_CAPTURE)) + { + $max = 0; + $pos = null; + foreach ($matches[0] as $match) + { + if (strlen($match[0]) > $max) + { + $max = strlen($match[0]); + $pos = $match[1]; + } + } + + $ip_parts[0] = substr_replace($ip_parts[0], '::', $pos, $max); + } + + if ($ip_parts[1] !== '') + { + return implode(':', $ip_parts); + } + else + { + return $ip_parts[0]; + } + } + + /** + * Splits an IPv6 address into the IPv6 and IPv4 representation parts + * + * RFC 4291 allows you to represent the last two parts of an IPv6 address + * using the standard IPv4 representation + * + * Example: 0:0:0:0:0:0:13.1.68.3 + * 0:0:0:0:0:FFFF:129.144.52.38 + * + * @param string $ip An IPv6 address + * @return array [0] contains the IPv6 represented part, and [1] the IPv4 represented part + */ + private static function split_v6_v4($ip) + { + if (strpos($ip, '.') !== false) + { + $pos = strrpos($ip, ':'); + $ipv6_part = substr($ip, 0, $pos); + $ipv4_part = substr($ip, $pos + 1); + return array($ipv6_part, $ipv4_part); + } + else + { + return array($ip, ''); + } + } + + /** + * Checks an IPv6 address + * + * Checks if the given IP is a valid IPv6 address + * + * @param string $ip An IPv6 address + * @return bool true if $ip is a valid IPv6 address + */ + public static function check_ipv6($ip) + { + $ip = self::uncompress($ip); + list($ipv6, $ipv4) = self::split_v6_v4($ip); + $ipv6 = explode(':', $ipv6); + $ipv4 = explode('.', $ipv4); + if (count($ipv6) === 8 && count($ipv4) === 1 || count($ipv6) === 6 && count($ipv4) === 4) + { + foreach ($ipv6 as $ipv6_part) + { + // The section can't be empty + if ($ipv6_part === '') + return false; + + // Nor can it be over four characters + if (strlen($ipv6_part) > 4) + return false; + + // Remove leading zeros (this is safe because of the above) + $ipv6_part = ltrim($ipv6_part, '0'); + if ($ipv6_part === '') + $ipv6_part = '0'; + + // Check the value is valid + $value = hexdec($ipv6_part); + if (dechex($value) !== strtolower($ipv6_part) || $value < 0 || $value > 0xFFFF) + return false; + } + if (count($ipv4) === 4) + { + foreach ($ipv4 as $ipv4_part) + { + $value = (int) $ipv4_part; + if ((string) $value !== $ipv4_part || $value < 0 || $value > 0xFF) + return false; + } + } + return true; + } + else + { + return false; + } + } + + /** + * Checks if the given IP is a valid IPv6 address + * + * @codeCoverageIgnore + * @deprecated Use {@see SimplePie_Net_IPv6::check_ipv6()} instead + * @see check_ipv6 + * @param string $ip An IPv6 address + * @return bool true if $ip is a valid IPv6 address + */ + public static function checkIPv6($ip) + { + return self::check_ipv6($ip); + } +} diff --git a/wp-includes/SimplePie/Parse/Date.php b/wp-includes/SimplePie/Parse/Date.php new file mode 100644 index 0000000..d51f500 --- /dev/null +++ b/wp-includes/SimplePie/Parse/Date.php @@ -0,0 +1,983 @@ +<?php +/** + * SimplePie + * + * A PHP-Based RSS and Atom Feed Framework. + * Takes the hard work out of managing a complete RSS/Atom solution. + * + * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * * Neither the name of the SimplePie Team nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS + * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package SimplePie + * @version 1.3.1 + * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @author Ryan Parman + * @author Geoffrey Sneddon + * @author Ryan McCue + * @link http://simplepie.org/ SimplePie + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + */ + + +/** + * Date Parser + * + * @package SimplePie + * @subpackage Parsing + */ +class SimplePie_Parse_Date +{ + /** + * Input data + * + * @access protected + * @var string + */ + var $date; + + /** + * List of days, calendar day name => ordinal day number in the week + * + * @access protected + * @var array + */ + var $day = array( + // English + 'mon' => 1, + 'monday' => 1, + 'tue' => 2, + 'tuesday' => 2, + 'wed' => 3, + 'wednesday' => 3, + 'thu' => 4, + 'thursday' => 4, + 'fri' => 5, + 'friday' => 5, + 'sat' => 6, + 'saturday' => 6, + 'sun' => 7, + 'sunday' => 7, + // Dutch + 'maandag' => 1, + 'dinsdag' => 2, + 'woensdag' => 3, + 'donderdag' => 4, + 'vrijdag' => 5, + 'zaterdag' => 6, + 'zondag' => 7, + // French + 'lundi' => 1, + 'mardi' => 2, + 'mercredi' => 3, + 'jeudi' => 4, + 'vendredi' => 5, + 'samedi' => 6, + 'dimanche' => 7, + // German + 'montag' => 1, + 'dienstag' => 2, + 'mittwoch' => 3, + 'donnerstag' => 4, + 'freitag' => 5, + 'samstag' => 6, + 'sonnabend' => 6, + 'sonntag' => 7, + // Italian + 'lunedì' => 1, + 'martedì' => 2, + 'mercoledì' => 3, + 'giovedì' => 4, + 'venerdì' => 5, + 'sabato' => 6, + 'domenica' => 7, + // Spanish + 'lunes' => 1, + 'martes' => 2, + 'miércoles' => 3, + 'jueves' => 4, + 'viernes' => 5, + 'sábado' => 6, + 'domingo' => 7, + // Finnish + 'maanantai' => 1, + 'tiistai' => 2, + 'keskiviikko' => 3, + 'torstai' => 4, + 'perjantai' => 5, + 'lauantai' => 6, + 'sunnuntai' => 7, + // Hungarian + 'hétfÅ‘' => 1, + 'kedd' => 2, + 'szerda' => 3, + 'csütörtok' => 4, + 'péntek' => 5, + 'szombat' => 6, + 'vasárnap' => 7, + // Greek + 'Δευ' => 1, + 'ΤÏι' => 2, + 'Τετ' => 3, + 'Πεμ' => 4, + 'ΠαÏ' => 5, + 'Σαβ' => 6, + 'ΚυÏ' => 7, + ); + + /** + * List of months, calendar month name => calendar month number + * + * @access protected + * @var array + */ + var $month = array( + // English + 'jan' => 1, + 'january' => 1, + 'feb' => 2, + 'february' => 2, + 'mar' => 3, + 'march' => 3, + 'apr' => 4, + 'april' => 4, + 'may' => 5, + // No long form of May + 'jun' => 6, + 'june' => 6, + 'jul' => 7, + 'july' => 7, + 'aug' => 8, + 'august' => 8, + 'sep' => 9, + 'september' => 8, + 'oct' => 10, + 'october' => 10, + 'nov' => 11, + 'november' => 11, + 'dec' => 12, + 'december' => 12, + // Dutch + 'januari' => 1, + 'februari' => 2, + 'maart' => 3, + 'april' => 4, + 'mei' => 5, + 'juni' => 6, + 'juli' => 7, + 'augustus' => 8, + 'september' => 9, + 'oktober' => 10, + 'november' => 11, + 'december' => 12, + // French + 'janvier' => 1, + 'février' => 2, + 'mars' => 3, + 'avril' => 4, + 'mai' => 5, + 'juin' => 6, + 'juillet' => 7, + 'août' => 8, + 'septembre' => 9, + 'octobre' => 10, + 'novembre' => 11, + 'décembre' => 12, + // German + 'januar' => 1, + 'februar' => 2, + 'märz' => 3, + 'april' => 4, + 'mai' => 5, + 'juni' => 6, + 'juli' => 7, + 'august' => 8, + 'september' => 9, + 'oktober' => 10, + 'november' => 11, + 'dezember' => 12, + // Italian + 'gennaio' => 1, + 'febbraio' => 2, + 'marzo' => 3, + 'aprile' => 4, + 'maggio' => 5, + 'giugno' => 6, + 'luglio' => 7, + 'agosto' => 8, + 'settembre' => 9, + 'ottobre' => 10, + 'novembre' => 11, + 'dicembre' => 12, + // Spanish + 'enero' => 1, + 'febrero' => 2, + 'marzo' => 3, + 'abril' => 4, + 'mayo' => 5, + 'junio' => 6, + 'julio' => 7, + 'agosto' => 8, + 'septiembre' => 9, + 'setiembre' => 9, + 'octubre' => 10, + 'noviembre' => 11, + 'diciembre' => 12, + // Finnish + 'tammikuu' => 1, + 'helmikuu' => 2, + 'maaliskuu' => 3, + 'huhtikuu' => 4, + 'toukokuu' => 5, + 'kesäkuu' => 6, + 'heinäkuu' => 7, + 'elokuu' => 8, + 'suuskuu' => 9, + 'lokakuu' => 10, + 'marras' => 11, + 'joulukuu' => 12, + // Hungarian + 'január' => 1, + 'február' => 2, + 'március' => 3, + 'április' => 4, + 'május' => 5, + 'június' => 6, + 'július' => 7, + 'augusztus' => 8, + 'szeptember' => 9, + 'október' => 10, + 'november' => 11, + 'december' => 12, + // Greek + 'Ιαν' => 1, + 'Φεβ' => 2, + 'Μάώ' => 3, + 'Μαώ' => 3, + 'ΑπÏ' => 4, + 'Μάι' => 5, + 'Μαϊ' => 5, + 'Μαι' => 5, + 'ΙοÏν' => 6, + 'Ιον' => 6, + 'ΙοÏλ' => 7, + 'Ιολ' => 7, + 'ΑÏγ' => 8, + 'Αυγ' => 8, + 'Σεπ' => 9, + 'Οκτ' => 10, + 'Îοέ' => 11, + 'Δεκ' => 12, + ); + + /** + * List of timezones, abbreviation => offset from UTC + * + * @access protected + * @var array + */ + var $timezone = array( + 'ACDT' => 37800, + 'ACIT' => 28800, + 'ACST' => 34200, + 'ACT' => -18000, + 'ACWDT' => 35100, + 'ACWST' => 31500, + 'AEDT' => 39600, + 'AEST' => 36000, + 'AFT' => 16200, + 'AKDT' => -28800, + 'AKST' => -32400, + 'AMDT' => 18000, + 'AMT' => -14400, + 'ANAST' => 46800, + 'ANAT' => 43200, + 'ART' => -10800, + 'AZOST' => -3600, + 'AZST' => 18000, + 'AZT' => 14400, + 'BIOT' => 21600, + 'BIT' => -43200, + 'BOT' => -14400, + 'BRST' => -7200, + 'BRT' => -10800, + 'BST' => 3600, + 'BTT' => 21600, + 'CAST' => 18000, + 'CAT' => 7200, + 'CCT' => 23400, + 'CDT' => -18000, + 'CEDT' => 7200, + 'CET' => 3600, + 'CGST' => -7200, + 'CGT' => -10800, + 'CHADT' => 49500, + 'CHAST' => 45900, + 'CIST' => -28800, + 'CKT' => -36000, + 'CLDT' => -10800, + 'CLST' => -14400, + 'COT' => -18000, + 'CST' => -21600, + 'CVT' => -3600, + 'CXT' => 25200, + 'DAVT' => 25200, + 'DTAT' => 36000, + 'EADT' => -18000, + 'EAST' => -21600, + 'EAT' => 10800, + 'ECT' => -18000, + 'EDT' => -14400, + 'EEST' => 10800, + 'EET' => 7200, + 'EGT' => -3600, + 'EKST' => 21600, + 'EST' => -18000, + 'FJT' => 43200, + 'FKDT' => -10800, + 'FKST' => -14400, + 'FNT' => -7200, + 'GALT' => -21600, + 'GEDT' => 14400, + 'GEST' => 10800, + 'GFT' => -10800, + 'GILT' => 43200, + 'GIT' => -32400, + 'GST' => 14400, + 'GST' => -7200, + 'GYT' => -14400, + 'HAA' => -10800, + 'HAC' => -18000, + 'HADT' => -32400, + 'HAE' => -14400, + 'HAP' => -25200, + 'HAR' => -21600, + 'HAST' => -36000, + 'HAT' => -9000, + 'HAY' => -28800, + 'HKST' => 28800, + 'HMT' => 18000, + 'HNA' => -14400, + 'HNC' => -21600, + 'HNE' => -18000, + 'HNP' => -28800, + 'HNR' => -25200, + 'HNT' => -12600, + 'HNY' => -32400, + 'IRDT' => 16200, + 'IRKST' => 32400, + 'IRKT' => 28800, + 'IRST' => 12600, + 'JFDT' => -10800, + 'JFST' => -14400, + 'JST' => 32400, + 'KGST' => 21600, + 'KGT' => 18000, + 'KOST' => 39600, + 'KOVST' => 28800, + 'KOVT' => 25200, + 'KRAST' => 28800, + 'KRAT' => 25200, + 'KST' => 32400, + 'LHDT' => 39600, + 'LHST' => 37800, + 'LINT' => 50400, + 'LKT' => 21600, + 'MAGST' => 43200, + 'MAGT' => 39600, + 'MAWT' => 21600, + 'MDT' => -21600, + 'MESZ' => 7200, + 'MEZ' => 3600, + 'MHT' => 43200, + 'MIT' => -34200, + 'MNST' => 32400, + 'MSDT' => 14400, + 'MSST' => 10800, + 'MST' => -25200, + 'MUT' => 14400, + 'MVT' => 18000, + 'MYT' => 28800, + 'NCT' => 39600, + 'NDT' => -9000, + 'NFT' => 41400, + 'NMIT' => 36000, + 'NOVST' => 25200, + 'NOVT' => 21600, + 'NPT' => 20700, + 'NRT' => 43200, + 'NST' => -12600, + 'NUT' => -39600, + 'NZDT' => 46800, + 'NZST' => 43200, + 'OMSST' => 25200, + 'OMST' => 21600, + 'PDT' => -25200, + 'PET' => -18000, + 'PETST' => 46800, + 'PETT' => 43200, + 'PGT' => 36000, + 'PHOT' => 46800, + 'PHT' => 28800, + 'PKT' => 18000, + 'PMDT' => -7200, + 'PMST' => -10800, + 'PONT' => 39600, + 'PST' => -28800, + 'PWT' => 32400, + 'PYST' => -10800, + 'PYT' => -14400, + 'RET' => 14400, + 'ROTT' => -10800, + 'SAMST' => 18000, + 'SAMT' => 14400, + 'SAST' => 7200, + 'SBT' => 39600, + 'SCDT' => 46800, + 'SCST' => 43200, + 'SCT' => 14400, + 'SEST' => 3600, + 'SGT' => 28800, + 'SIT' => 28800, + 'SRT' => -10800, + 'SST' => -39600, + 'SYST' => 10800, + 'SYT' => 7200, + 'TFT' => 18000, + 'THAT' => -36000, + 'TJT' => 18000, + 'TKT' => -36000, + 'TMT' => 18000, + 'TOT' => 46800, + 'TPT' => 32400, + 'TRUT' => 36000, + 'TVT' => 43200, + 'TWT' => 28800, + 'UYST' => -7200, + 'UYT' => -10800, + 'UZT' => 18000, + 'VET' => -14400, + 'VLAST' => 39600, + 'VLAT' => 36000, + 'VOST' => 21600, + 'VUT' => 39600, + 'WAST' => 7200, + 'WAT' => 3600, + 'WDT' => 32400, + 'WEST' => 3600, + 'WFT' => 43200, + 'WIB' => 25200, + 'WIT' => 32400, + 'WITA' => 28800, + 'WKST' => 18000, + 'WST' => 28800, + 'YAKST' => 36000, + 'YAKT' => 32400, + 'YAPT' => 36000, + 'YEKST' => 21600, + 'YEKT' => 18000, + ); + + /** + * Cached PCRE for SimplePie_Parse_Date::$day + * + * @access protected + * @var string + */ + var $day_pcre; + + /** + * Cached PCRE for SimplePie_Parse_Date::$month + * + * @access protected + * @var string + */ + var $month_pcre; + + /** + * Array of user-added callback methods + * + * @access private + * @var array + */ + var $built_in = array(); + + /** + * Array of user-added callback methods + * + * @access private + * @var array + */ + var $user = array(); + + /** + * Create new SimplePie_Parse_Date object, and set self::day_pcre, + * self::month_pcre, and self::built_in + * + * @access private + */ + public function __construct() + { + $this->day_pcre = '(' . implode(array_keys($this->day), '|') . ')'; + $this->month_pcre = '(' . implode(array_keys($this->month), '|') . ')'; + + static $cache; + if (!isset($cache[get_class($this)])) + { + $all_methods = get_class_methods($this); + + foreach ($all_methods as $method) + { + if (strtolower(substr($method, 0, 5)) === 'date_') + { + $cache[get_class($this)][] = $method; + } + } + } + + foreach ($cache[get_class($this)] as $method) + { + $this->built_in[] = $method; + } + } + + /** + * Get the object + * + * @access public + */ + public static function get() + { + static $object; + if (!$object) + { + $object = new SimplePie_Parse_Date; + } + return $object; + } + + /** + * Parse a date + * + * @final + * @access public + * @param string $date Date to parse + * @return int Timestamp corresponding to date string, or false on failure + */ + public function parse($date) + { + foreach ($this->user as $method) + { + if (($returned = call_user_func($method, $date)) !== false) + { + return $returned; + } + } + + foreach ($this->built_in as $method) + { + if (($returned = call_user_func(array($this, $method), $date)) !== false) + { + return $returned; + } + } + + return false; + } + + /** + * Add a callback method to parse a date + * + * @final + * @access public + * @param callback $callback + */ + public function add_callback($callback) + { + if (is_callable($callback)) + { + $this->user[] = $callback; + } + else + { + trigger_error('User-supplied function must be a valid callback', E_USER_WARNING); + } + } + + /** + * Parse a superset of W3C-DTF (allows hyphens and colons to be omitted, as + * well as allowing any of upper or lower case "T", horizontal tabs, or + * spaces to be used as the time seperator (including more than one)) + * + * @access protected + * @return int Timestamp + */ + public function date_w3cdtf($date) + { + static $pcre; + if (!$pcre) + { + $year = '([0-9]{4})'; + $month = $day = $hour = $minute = $second = '([0-9]{2})'; + $decimal = '([0-9]*)'; + $zone = '(?:(Z)|([+\-])([0-9]{1,2}):?([0-9]{1,2}))'; + $pcre = '/^' . $year . '(?:-?' . $month . '(?:-?' . $day . '(?:[Tt\x09\x20]+' . $hour . '(?::?' . $minute . '(?::?' . $second . '(?:.' . $decimal . ')?)?)?' . $zone . ')?)?)?$/'; + } + if (preg_match($pcre, $date, $match)) + { + /* + Capturing subpatterns: + 1: Year + 2: Month + 3: Day + 4: Hour + 5: Minute + 6: Second + 7: Decimal fraction of a second + 8: Zulu + 9: Timezone ± + 10: Timezone hours + 11: Timezone minutes + */ + + // Fill in empty matches + for ($i = count($match); $i <= 3; $i++) + { + $match[$i] = '1'; + } + + for ($i = count($match); $i <= 7; $i++) + { + $match[$i] = '0'; + } + + // Numeric timezone + if (isset($match[9]) && $match[9] !== '') + { + $timezone = $match[10] * 3600; + $timezone += $match[11] * 60; + if ($match[9] === '-') + { + $timezone = 0 - $timezone; + } + } + else + { + $timezone = 0; + } + + // Convert the number of seconds to an integer, taking decimals into account + $second = round($match[6] + $match[7] / pow(10, strlen($match[7]))); + + return gmmktime($match[4], $match[5], $second, $match[2], $match[3], $match[1]) - $timezone; + } + else + { + return false; + } + } + + /** + * Remove RFC822 comments + * + * @access protected + * @param string $data Data to strip comments from + * @return string Comment stripped string + */ + public function remove_rfc2822_comments($string) + { + $string = (string) $string; + $position = 0; + $length = strlen($string); + $depth = 0; + + $output = ''; + + while ($position < $length && ($pos = strpos($string, '(', $position)) !== false) + { + $output .= substr($string, $position, $pos - $position); + $position = $pos + 1; + if ($string[$pos - 1] !== '\\') + { + $depth++; + while ($depth && $position < $length) + { + $position += strcspn($string, '()', $position); + if ($string[$position - 1] === '\\') + { + $position++; + continue; + } + elseif (isset($string[$position])) + { + switch ($string[$position]) + { + case '(': + $depth++; + break; + + case ')': + $depth--; + break; + } + $position++; + } + else + { + break; + } + } + } + else + { + $output .= '('; + } + } + $output .= substr($string, $position); + + return $output; + } + + /** + * Parse RFC2822's date format + * + * @access protected + * @return int Timestamp + */ + public function date_rfc2822($date) + { + static $pcre; + if (!$pcre) + { + $wsp = '[\x09\x20]'; + $fws = '(?:' . $wsp . '+|' . $wsp . '*(?:\x0D\x0A' . $wsp . '+)+)'; + $optional_fws = $fws . '?'; + $day_name = $this->day_pcre; + $month = $this->month_pcre; + $day = '([0-9]{1,2})'; + $hour = $minute = $second = '([0-9]{2})'; + $year = '([0-9]{2,4})'; + $num_zone = '([+\-])([0-9]{2})([0-9]{2})'; + $character_zone = '([A-Z]{1,5})'; + $zone = '(?:' . $num_zone . '|' . $character_zone . ')'; + $pcre = '/(?:' . $optional_fws . $day_name . $optional_fws . ',)?' . $optional_fws . $day . $fws . $month . $fws . $year . $fws . $hour . $optional_fws . ':' . $optional_fws . $minute . '(?:' . $optional_fws . ':' . $optional_fws . $second . ')?' . $fws . $zone . '/i'; + } + if (preg_match($pcre, $this->remove_rfc2822_comments($date), $match)) + { + /* + Capturing subpatterns: + 1: Day name + 2: Day + 3: Month + 4: Year + 5: Hour + 6: Minute + 7: Second + 8: Timezone ± + 9: Timezone hours + 10: Timezone minutes + 11: Alphabetic timezone + */ + + // Find the month number + $month = $this->month[strtolower($match[3])]; + + // Numeric timezone + if ($match[8] !== '') + { + $timezone = $match[9] * 3600; + $timezone += $match[10] * 60; + if ($match[8] === '-') + { + $timezone = 0 - $timezone; + } + } + // Character timezone + elseif (isset($this->timezone[strtoupper($match[11])])) + { + $timezone = $this->timezone[strtoupper($match[11])]; + } + // Assume everything else to be -0000 + else + { + $timezone = 0; + } + + // Deal with 2/3 digit years + if ($match[4] < 50) + { + $match[4] += 2000; + } + elseif ($match[4] < 1000) + { + $match[4] += 1900; + } + + // Second is optional, if it is empty set it to zero + if ($match[7] !== '') + { + $second = $match[7]; + } + else + { + $second = 0; + } + + return gmmktime($match[5], $match[6], $second, $month, $match[2], $match[4]) - $timezone; + } + else + { + return false; + } + } + + /** + * Parse RFC850's date format + * + * @access protected + * @return int Timestamp + */ + public function date_rfc850($date) + { + static $pcre; + if (!$pcre) + { + $space = '[\x09\x20]+'; + $day_name = $this->day_pcre; + $month = $this->month_pcre; + $day = '([0-9]{1,2})'; + $year = $hour = $minute = $second = '([0-9]{2})'; + $zone = '([A-Z]{1,5})'; + $pcre = '/^' . $day_name . ',' . $space . $day . '-' . $month . '-' . $year . $space . $hour . ':' . $minute . ':' . $second . $space . $zone . '$/i'; + } + if (preg_match($pcre, $date, $match)) + { + /* + Capturing subpatterns: + 1: Day name + 2: Day + 3: Month + 4: Year + 5: Hour + 6: Minute + 7: Second + 8: Timezone + */ + + // Month + $month = $this->month[strtolower($match[3])]; + + // Character timezone + if (isset($this->timezone[strtoupper($match[8])])) + { + $timezone = $this->timezone[strtoupper($match[8])]; + } + // Assume everything else to be -0000 + else + { + $timezone = 0; + } + + // Deal with 2 digit year + if ($match[4] < 50) + { + $match[4] += 2000; + } + else + { + $match[4] += 1900; + } + + return gmmktime($match[5], $match[6], $match[7], $month, $match[2], $match[4]) - $timezone; + } + else + { + return false; + } + } + + /** + * Parse C99's asctime()'s date format + * + * @access protected + * @return int Timestamp + */ + public function date_asctime($date) + { + static $pcre; + if (!$pcre) + { + $space = '[\x09\x20]+'; + $wday_name = $this->day_pcre; + $mon_name = $this->month_pcre; + $day = '([0-9]{1,2})'; + $hour = $sec = $min = '([0-9]{2})'; + $year = '([0-9]{4})'; + $terminator = '\x0A?\x00?'; + $pcre = '/^' . $wday_name . $space . $mon_name . $space . $day . $space . $hour . ':' . $min . ':' . $sec . $space . $year . $terminator . '$/i'; + } + if (preg_match($pcre, $date, $match)) + { + /* + Capturing subpatterns: + 1: Day name + 2: Month + 3: Day + 4: Hour + 5: Minute + 6: Second + 7: Year + */ + + $month = $this->month[strtolower($match[2])]; + return gmmktime($match[4], $match[5], $match[6], $month, $match[3], $match[7]); + } + else + { + return false; + } + } + + /** + * Parse dates using strtotime() + * + * @access protected + * @return int Timestamp + */ + public function date_strtotime($date) + { + $strtotime = strtotime($date); + if ($strtotime === -1 || $strtotime === false) + { + return false; + } + else + { + return $strtotime; + } + } +} + diff --git a/wp-includes/SimplePie/Parser.php b/wp-includes/SimplePie/Parser.php new file mode 100644 index 0000000..d698552 --- /dev/null +++ b/wp-includes/SimplePie/Parser.php @@ -0,0 +1,407 @@ +<?php +/** + * SimplePie + * + * A PHP-Based RSS and Atom Feed Framework. + * Takes the hard work out of managing a complete RSS/Atom solution. + * + * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * * Neither the name of the SimplePie Team nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS + * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package SimplePie + * @version 1.3.1 + * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @author Ryan Parman + * @author Geoffrey Sneddon + * @author Ryan McCue + * @link http://simplepie.org/ SimplePie + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + */ + +/** + * Parses XML into something sane + * + * + * This class can be overloaded with {@see SimplePie::set_parser_class()} + * + * @package SimplePie + * @subpackage Parsing + */ +class SimplePie_Parser +{ + var $error_code; + var $error_string; + var $current_line; + var $current_column; + var $current_byte; + var $separator = ' '; + var $namespace = array(''); + var $element = array(''); + var $xml_base = array(''); + var $xml_base_explicit = array(false); + var $xml_lang = array(''); + var $data = array(); + var $datas = array(array()); + var $current_xhtml_construct = -1; + var $encoding; + protected $registry; + + public function set_registry(SimplePie_Registry $registry) + { + $this->registry = $registry; + } + + public function parse(&$data, $encoding) + { + // Use UTF-8 if we get passed US-ASCII, as every US-ASCII character is a UTF-8 character + if (strtoupper($encoding) === 'US-ASCII') + { + $this->encoding = 'UTF-8'; + } + else + { + $this->encoding = $encoding; + } + + // Strip BOM: + // UTF-32 Big Endian BOM + if (substr($data, 0, 4) === "\x00\x00\xFE\xFF") + { + $data = substr($data, 4); + } + // UTF-32 Little Endian BOM + elseif (substr($data, 0, 4) === "\xFF\xFE\x00\x00") + { + $data = substr($data, 4); + } + // UTF-16 Big Endian BOM + elseif (substr($data, 0, 2) === "\xFE\xFF") + { + $data = substr($data, 2); + } + // UTF-16 Little Endian BOM + elseif (substr($data, 0, 2) === "\xFF\xFE") + { + $data = substr($data, 2); + } + // UTF-8 BOM + elseif (substr($data, 0, 3) === "\xEF\xBB\xBF") + { + $data = substr($data, 3); + } + + if (substr($data, 0, 5) === '<?xml' && strspn(substr($data, 5, 1), "\x09\x0A\x0D\x20") && ($pos = strpos($data, '?>')) !== false) + { + $declaration = $this->registry->create('XML_Declaration_Parser', array(substr($data, 5, $pos - 5))); + if ($declaration->parse()) + { + $data = substr($data, $pos + 2); + $data = '<?xml version="' . $declaration->version . '" encoding="' . $encoding . '" standalone="' . (($declaration->standalone) ? 'yes' : 'no') . '"?>' . $data; + } + else + { + $this->error_string = 'SimplePie bug! Please report this!'; + return false; + } + } + + $return = true; + + static $xml_is_sane = null; + if ($xml_is_sane === null) + { + $parser_check = xml_parser_create(); + xml_parse_into_struct($parser_check, '<foo>&</foo>', $values); + xml_parser_free($parser_check); + $xml_is_sane = isset($values[0]['value']); + } + + // Create the parser + if ($xml_is_sane) + { + $xml = xml_parser_create_ns($this->encoding, $this->separator); + xml_parser_set_option($xml, XML_OPTION_SKIP_WHITE, 1); + xml_parser_set_option($xml, XML_OPTION_CASE_FOLDING, 0); + xml_set_object($xml, $this); + xml_set_character_data_handler($xml, 'cdata'); + xml_set_element_handler($xml, 'tag_open', 'tag_close'); + + // Parse! + if (!xml_parse($xml, $data, true)) + { + $this->error_code = xml_get_error_code($xml); + $this->error_string = xml_error_string($this->error_code); + $return = false; + } + $this->current_line = xml_get_current_line_number($xml); + $this->current_column = xml_get_current_column_number($xml); + $this->current_byte = xml_get_current_byte_index($xml); + xml_parser_free($xml); + return $return; + } + else + { + libxml_clear_errors(); + $xml = new XMLReader(); + $xml->xml($data); + while (@$xml->read()) + { + switch ($xml->nodeType) + { + + case constant('XMLReader::END_ELEMENT'): + if ($xml->namespaceURI !== '') + { + $tagName = $xml->namespaceURI . $this->separator . $xml->localName; + } + else + { + $tagName = $xml->localName; + } + $this->tag_close(null, $tagName); + break; + case constant('XMLReader::ELEMENT'): + $empty = $xml->isEmptyElement; + if ($xml->namespaceURI !== '') + { + $tagName = $xml->namespaceURI . $this->separator . $xml->localName; + } + else + { + $tagName = $xml->localName; + } + $attributes = array(); + while ($xml->moveToNextAttribute()) + { + if ($xml->namespaceURI !== '') + { + $attrName = $xml->namespaceURI . $this->separator . $xml->localName; + } + else + { + $attrName = $xml->localName; + } + $attributes[$attrName] = $xml->value; + } + $this->tag_open(null, $tagName, $attributes); + if ($empty) + { + $this->tag_close(null, $tagName); + } + break; + case constant('XMLReader::TEXT'): + + case constant('XMLReader::CDATA'): + $this->cdata(null, $xml->value); + break; + } + } + if ($error = libxml_get_last_error()) + { + $this->error_code = $error->code; + $this->error_string = $error->message; + $this->current_line = $error->line; + $this->current_column = $error->column; + return false; + } + else + { + return true; + } + } + } + + public function get_error_code() + { + return $this->error_code; + } + + public function get_error_string() + { + return $this->error_string; + } + + public function get_current_line() + { + return $this->current_line; + } + + public function get_current_column() + { + return $this->current_column; + } + + public function get_current_byte() + { + return $this->current_byte; + } + + public function get_data() + { + return $this->data; + } + + public function tag_open($parser, $tag, $attributes) + { + list($this->namespace[], $this->element[]) = $this->split_ns($tag); + + $attribs = array(); + foreach ($attributes as $name => $value) + { + list($attrib_namespace, $attribute) = $this->split_ns($name); + $attribs[$attrib_namespace][$attribute] = $value; + } + + if (isset($attribs[SIMPLEPIE_NAMESPACE_XML]['base'])) + { + $base = $this->registry->call('Misc', 'absolutize_url', array($attribs[SIMPLEPIE_NAMESPACE_XML]['base'], end($this->xml_base))); + if ($base !== false) + { + $this->xml_base[] = $base; + $this->xml_base_explicit[] = true; + } + } + else + { + $this->xml_base[] = end($this->xml_base); + $this->xml_base_explicit[] = end($this->xml_base_explicit); + } + + if (isset($attribs[SIMPLEPIE_NAMESPACE_XML]['lang'])) + { + $this->xml_lang[] = $attribs[SIMPLEPIE_NAMESPACE_XML]['lang']; + } + else + { + $this->xml_lang[] = end($this->xml_lang); + } + + if ($this->current_xhtml_construct >= 0) + { + $this->current_xhtml_construct++; + if (end($this->namespace) === SIMPLEPIE_NAMESPACE_XHTML) + { + $this->data['data'] .= '<' . end($this->element); + if (isset($attribs[''])) + { + foreach ($attribs[''] as $name => $value) + { + $this->data['data'] .= ' ' . $name . '="' . htmlspecialchars($value, ENT_COMPAT, $this->encoding) . '"'; + } + } + $this->data['data'] .= '>'; + } + } + else + { + $this->datas[] =& $this->data; + $this->data =& $this->data['child'][end($this->namespace)][end($this->element)][]; + $this->data = array('data' => '', 'attribs' => $attribs, 'xml_base' => end($this->xml_base), 'xml_base_explicit' => end($this->xml_base_explicit), 'xml_lang' => end($this->xml_lang)); + if ((end($this->namespace) === SIMPLEPIE_NAMESPACE_ATOM_03 && in_array(end($this->element), array('title', 'tagline', 'copyright', 'info', 'summary', 'content')) && isset($attribs['']['mode']) && $attribs['']['mode'] === 'xml') + || (end($this->namespace) === SIMPLEPIE_NAMESPACE_ATOM_10 && in_array(end($this->element), array('rights', 'subtitle', 'summary', 'info', 'title', 'content')) && isset($attribs['']['type']) && $attribs['']['type'] === 'xhtml') + || (end($this->namespace) === SIMPLEPIE_NAMESPACE_RSS_20 && in_array(end($this->element), array('title'))) + || (end($this->namespace) === SIMPLEPIE_NAMESPACE_RSS_090 && in_array(end($this->element), array('title'))) + || (end($this->namespace) === SIMPLEPIE_NAMESPACE_RSS_10 && in_array(end($this->element), array('title')))) + { + $this->current_xhtml_construct = 0; + } + } + } + + public function cdata($parser, $cdata) + { + if ($this->current_xhtml_construct >= 0) + { + $this->data['data'] .= htmlspecialchars($cdata, ENT_QUOTES, $this->encoding); + } + else + { + $this->data['data'] .= $cdata; + } + } + + public function tag_close($parser, $tag) + { + if ($this->current_xhtml_construct >= 0) + { + $this->current_xhtml_construct--; + if (end($this->namespace) === SIMPLEPIE_NAMESPACE_XHTML && !in_array(end($this->element), array('area', 'base', 'basefont', 'br', 'col', 'frame', 'hr', 'img', 'input', 'isindex', 'link', 'meta', 'param'))) + { + $this->data['data'] .= '</' . end($this->element) . '>'; + } + } + if ($this->current_xhtml_construct === -1) + { + $this->data =& $this->datas[count($this->datas) - 1]; + array_pop($this->datas); + } + + array_pop($this->element); + array_pop($this->namespace); + array_pop($this->xml_base); + array_pop($this->xml_base_explicit); + array_pop($this->xml_lang); + } + + public function split_ns($string) + { + static $cache = array(); + if (!isset($cache[$string])) + { + if ($pos = strpos($string, $this->separator)) + { + static $separator_length; + if (!$separator_length) + { + $separator_length = strlen($this->separator); + } + $namespace = substr($string, 0, $pos); + $local_name = substr($string, $pos + $separator_length); + if (strtolower($namespace) === SIMPLEPIE_NAMESPACE_ITUNES) + { + $namespace = SIMPLEPIE_NAMESPACE_ITUNES; + } + + // Normalize the Media RSS namespaces + if ($namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG || + $namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG2 || + $namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG3 || + $namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG4 || + $namespace === SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG5 ) + { + $namespace = SIMPLEPIE_NAMESPACE_MEDIARSS; + } + $cache[$string] = array($namespace, $local_name); + } + else + { + $cache[$string] = array('', $string); + } + } + return $cache[$string]; + } +} diff --git a/wp-includes/SimplePie/Rating.php b/wp-includes/SimplePie/Rating.php new file mode 100644 index 0000000..8689e5d --- /dev/null +++ b/wp-includes/SimplePie/Rating.php @@ -0,0 +1,129 @@ +<?php +/** + * SimplePie + * + * A PHP-Based RSS and Atom Feed Framework. + * Takes the hard work out of managing a complete RSS/Atom solution. + * + * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * * Neither the name of the SimplePie Team nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS + * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package SimplePie + * @version 1.3.1 + * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @author Ryan Parman + * @author Geoffrey Sneddon + * @author Ryan McCue + * @link http://simplepie.org/ SimplePie + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + */ + +/** + * Handles `<media:rating>` or `<itunes:explicit>` tags as defined in Media RSS and iTunes RSS respectively + * + * Used by {@see SimplePie_Enclosure::get_rating()} and {@see SimplePie_Enclosure::get_ratings()} + * + * This class can be overloaded with {@see SimplePie::set_rating_class()} + * + * @package SimplePie + * @subpackage API + */ +class SimplePie_Rating +{ + /** + * Rating scheme + * + * @var string + * @see get_scheme() + */ + var $scheme; + + /** + * Rating value + * + * @var string + * @see get_value() + */ + var $value; + + /** + * Constructor, used to input the data + * + * For documentation on all the parameters, see the corresponding + * properties and their accessors + */ + public function __construct($scheme = null, $value = null) + { + $this->scheme = $scheme; + $this->value = $value; + } + + /** + * String-ified version + * + * @return string + */ + public function __toString() + { + // There is no $this->data here + return md5(serialize($this)); + } + + /** + * Get the organizational scheme for the rating + * + * @return string|null + */ + public function get_scheme() + { + if ($this->scheme !== null) + { + return $this->scheme; + } + else + { + return null; + } + } + + /** + * Get the value of the rating + * + * @return string|null + */ + public function get_value() + { + if ($this->value !== null) + { + return $this->value; + } + else + { + return null; + } + } +} diff --git a/wp-includes/SimplePie/Registry.php b/wp-includes/SimplePie/Registry.php new file mode 100644 index 0000000..1072cde --- /dev/null +++ b/wp-includes/SimplePie/Registry.php @@ -0,0 +1,225 @@ +<?php +/** + * SimplePie + * + * A PHP-Based RSS and Atom Feed Framework. + * Takes the hard work out of managing a complete RSS/Atom solution. + * + * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * * Neither the name of the SimplePie Team nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS + * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package SimplePie + * @version 1.3.1 + * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @author Ryan Parman + * @author Geoffrey Sneddon + * @author Ryan McCue + * @link http://simplepie.org/ SimplePie + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + */ + +/** + * Handles creating objects and calling methods + * + * Access this via {@see SimplePie::get_registry()} + * + * @package SimplePie + */ +class SimplePie_Registry +{ + /** + * Default class mapping + * + * Overriding classes *must* subclass these. + * + * @var array + */ + protected $default = array( + 'Cache' => 'SimplePie_Cache', + 'Locator' => 'SimplePie_Locator', + 'Parser' => 'SimplePie_Parser', + 'File' => 'SimplePie_File', + 'Sanitize' => 'SimplePie_Sanitize', + 'Item' => 'SimplePie_Item', + 'Author' => 'SimplePie_Author', + 'Category' => 'SimplePie_Category', + 'Enclosure' => 'SimplePie_Enclosure', + 'Caption' => 'SimplePie_Caption', + 'Copyright' => 'SimplePie_Copyright', + 'Credit' => 'SimplePie_Credit', + 'Rating' => 'SimplePie_Rating', + 'Restriction' => 'SimplePie_Restriction', + 'Content_Type_Sniffer' => 'SimplePie_Content_Type_Sniffer', + 'Source' => 'SimplePie_Source', + 'Misc' => 'SimplePie_Misc', + 'XML_Declaration_Parser' => 'SimplePie_XML_Declaration_Parser', + 'Parse_Date' => 'SimplePie_Parse_Date', + ); + + /** + * Class mapping + * + * @see register() + * @var array + */ + protected $classes = array(); + + /** + * Legacy classes + * + * @see register() + * @var array + */ + protected $legacy = array(); + + /** + * Constructor + * + * No-op + */ + public function __construct() { } + + /** + * Register a class + * + * @param string $type See {@see $default} for names + * @param string $class Class name, must subclass the corresponding default + * @param bool $legacy Whether to enable legacy support for this class + * @return bool Successfulness + */ + public function register($type, $class, $legacy = false) + { + if (!is_subclass_of($class, $this->default[$type])) + { + return false; + } + + $this->classes[$type] = $class; + + if ($legacy) + { + $this->legacy[] = $class; + } + + return true; + } + + /** + * Get the class registered for a type + * + * Where possible, use {@see create()} or {@see call()} instead + * + * @param string $type + * @return string|null + */ + public function get_class($type) + { + if (!empty($this->classes[$type])) + { + return $this->classes[$type]; + } + if (!empty($this->default[$type])) + { + return $this->default[$type]; + } + + return null; + } + + /** + * Create a new instance of a given type + * + * @param string $type + * @param array $parameters Parameters to pass to the constructor + * @return object Instance of class + */ + public function &create($type, $parameters = array()) + { + $class = $this->get_class($type); + + if (in_array($class, $this->legacy)) + { + switch ($type) + { + case 'locator': + // Legacy: file, timeout, useragent, file_class, max_checked_feeds, content_type_sniffer_class + // Specified: file, timeout, useragent, max_checked_feeds + $replacement = array($this->get_class('file'), $parameters[3], $this->get_class('content_type_sniffer')); + array_splice($parameters, 3, 1, $replacement); + break; + } + } + + if (!method_exists($class, '__construct')) + { + $instance = new $class; + } + else + { + $reflector = new ReflectionClass($class); + $instance = $reflector->newInstanceArgs($parameters); + } + + if (method_exists($instance, 'set_registry')) + { + $instance->set_registry($this); + } + return $instance; + } + + /** + * Call a static method for a type + * + * @param string $type + * @param string $method + * @param array $parameters + * @return mixed + */ + public function &call($type, $method, $parameters = array()) + { + $class = $this->get_class($type); + + if (in_array($class, $this->legacy)) + { + switch ($type) + { + case 'Cache': + // For backwards compatibility with old non-static + // Cache::create() methods + if ($method === 'get_handler') + { + $result = @call_user_func_array(array($class, 'create'), $parameters); + return $result; + } + break; + } + } + + $result = call_user_func_array(array($class, $method), $parameters); + return $result; + } +} \ No newline at end of file diff --git a/wp-includes/SimplePie/Restriction.php b/wp-includes/SimplePie/Restriction.php new file mode 100644 index 0000000..4ba371b --- /dev/null +++ b/wp-includes/SimplePie/Restriction.php @@ -0,0 +1,155 @@ +<?php +/** + * SimplePie + * + * A PHP-Based RSS and Atom Feed Framework. + * Takes the hard work out of managing a complete RSS/Atom solution. + * + * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * * Neither the name of the SimplePie Team nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS + * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package SimplePie + * @version 1.3.1 + * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @author Ryan Parman + * @author Geoffrey Sneddon + * @author Ryan McCue + * @link http://simplepie.org/ SimplePie + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + */ + +/** + * Handles `<media:restriction>` as defined in Media RSS + * + * Used by {@see SimplePie_Enclosure::get_restriction()} and {@see SimplePie_Enclosure::get_restrictions()} + * + * This class can be overloaded with {@see SimplePie::set_restriction_class()} + * + * @package SimplePie + * @subpackage API + */ +class SimplePie_Restriction +{ + /** + * Relationship ('allow'/'deny') + * + * @var string + * @see get_relationship() + */ + var $relationship; + + /** + * Type of restriction + * + * @var string + * @see get_type() + */ + var $type; + + /** + * Restricted values + * + * @var string + * @see get_value() + */ + var $value; + + /** + * Constructor, used to input the data + * + * For documentation on all the parameters, see the corresponding + * properties and their accessors + */ + public function __construct($relationship = null, $type = null, $value = null) + { + $this->relationship = $relationship; + $this->type = $type; + $this->value = $value; + } + + /** + * String-ified version + * + * @return string + */ + public function __toString() + { + // There is no $this->data here + return md5(serialize($this)); + } + + /** + * Get the relationship + * + * @return string|null Either 'allow' or 'deny' + */ + public function get_relationship() + { + if ($this->relationship !== null) + { + return $this->relationship; + } + else + { + return null; + } + } + + /** + * Get the type + * + * @return string|null + */ + public function get_type() + { + if ($this->type !== null) + { + return $this->type; + } + else + { + return null; + } + } + + /** + * Get the list of restricted things + * + * @return string|null + */ + public function get_value() + { + if ($this->value !== null) + { + return $this->value; + } + else + { + return null; + } + } +} diff --git a/wp-includes/SimplePie/Sanitize.php b/wp-includes/SimplePie/Sanitize.php new file mode 100644 index 0000000..1ce047a3 --- /dev/null +++ b/wp-includes/SimplePie/Sanitize.php @@ -0,0 +1,554 @@ +<?php +/** + * SimplePie + * + * A PHP-Based RSS and Atom Feed Framework. + * Takes the hard work out of managing a complete RSS/Atom solution. + * + * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * * Neither the name of the SimplePie Team nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS + * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package SimplePie + * @version 1.3.1 + * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @author Ryan Parman + * @author Geoffrey Sneddon + * @author Ryan McCue + * @link http://simplepie.org/ SimplePie + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + */ + +/** + * Used for data cleanup and post-processing + * + * + * This class can be overloaded with {@see SimplePie::set_sanitize_class()} + * + * @package SimplePie + * @todo Move to using an actual HTML parser (this will allow tags to be properly stripped, and to switch between HTML and XHTML), this will also make it easier to shorten a string while preserving HTML tags + */ +class SimplePie_Sanitize +{ + // Private vars + var $base; + + // Options + var $remove_div = true; + var $image_handler = ''; + var $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'); + var $encode_instead_of_strip = false; + var $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'); + var $strip_comments = false; + var $output_encoding = 'UTF-8'; + var $enable_cache = true; + var $cache_location = './cache'; + var $cache_name_function = 'md5'; + var $timeout = 10; + var $useragent = ''; + var $force_fsockopen = false; + var $replace_url_attributes = null; + + public function __construct() + { + // Set defaults + $this->set_url_replacements(null); + } + + public function remove_div($enable = true) + { + $this->remove_div = (bool) $enable; + } + + public function set_image_handler($page = false) + { + if ($page) + { + $this->image_handler = (string) $page; + } + else + { + $this->image_handler = false; + } + } + + public function set_registry(SimplePie_Registry $registry) + { + $this->registry = $registry; + } + + public function pass_cache_data($enable_cache = true, $cache_location = './cache', $cache_name_function = 'md5', $cache_class = 'SimplePie_Cache') + { + if (isset($enable_cache)) + { + $this->enable_cache = (bool) $enable_cache; + } + + if ($cache_location) + { + $this->cache_location = (string) $cache_location; + } + + if ($cache_name_function) + { + $this->cache_name_function = (string) $cache_name_function; + } + } + + public function pass_file_data($file_class = 'SimplePie_File', $timeout = 10, $useragent = '', $force_fsockopen = false) + { + if ($timeout) + { + $this->timeout = (string) $timeout; + } + + if ($useragent) + { + $this->useragent = (string) $useragent; + } + + if ($force_fsockopen) + { + $this->force_fsockopen = (string) $force_fsockopen; + } + } + + public function strip_htmltags($tags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style')) + { + if ($tags) + { + if (is_array($tags)) + { + $this->strip_htmltags = $tags; + } + else + { + $this->strip_htmltags = explode(',', $tags); + } + } + else + { + $this->strip_htmltags = false; + } + } + + public function encode_instead_of_strip($encode = false) + { + $this->encode_instead_of_strip = (bool) $encode; + } + + public function strip_attributes($attribs = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc')) + { + if ($attribs) + { + if (is_array($attribs)) + { + $this->strip_attributes = $attribs; + } + else + { + $this->strip_attributes = explode(',', $attribs); + } + } + else + { + $this->strip_attributes = false; + } + } + + public function strip_comments($strip = false) + { + $this->strip_comments = (bool) $strip; + } + + public function set_output_encoding($encoding = 'UTF-8') + { + $this->output_encoding = (string) $encoding; + } + + /** + * Set element/attribute key/value pairs of HTML attributes + * containing URLs that need to be resolved relative to the feed + * + * Defaults to |a|@href, |area|@href, |blockquote|@cite, |del|@cite, + * |form|@action, |img|@longdesc, |img|@src, |input|@src, |ins|@cite, + * |q|@cite + * + * @since 1.0 + * @param array|null $element_attribute Element/attribute key/value pairs, null for default + */ + public function set_url_replacements($element_attribute = null) + { + if ($element_attribute === null) + { + $element_attribute = array( + 'a' => 'href', + 'area' => 'href', + 'blockquote' => 'cite', + 'del' => 'cite', + 'form' => 'action', + 'img' => array( + 'longdesc', + 'src' + ), + 'input' => 'src', + 'ins' => 'cite', + 'q' => 'cite' + ); + } + $this->replace_url_attributes = (array) $element_attribute; + } + + public function sanitize($data, $type, $base = '') + { + $data = trim($data); + if ($data !== '' || $type & SIMPLEPIE_CONSTRUCT_IRI) + { + if ($type & SIMPLEPIE_CONSTRUCT_MAYBE_HTML) + { + if (preg_match('/(&(#(x[0-9a-fA-F]+|[0-9]+)|[a-zA-Z0-9]+)|<\/[A-Za-z][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E]*' . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>)/', $data)) + { + $type |= SIMPLEPIE_CONSTRUCT_HTML; + } + else + { + $type |= SIMPLEPIE_CONSTRUCT_TEXT; + } + } + + if ($type & SIMPLEPIE_CONSTRUCT_BASE64) + { + $data = base64_decode($data); + } + + if ($type & (SIMPLEPIE_CONSTRUCT_HTML | SIMPLEPIE_CONSTRUCT_XHTML)) + { + + if (!class_exists('DOMDocument')) + { + $this->registry->call('Misc', 'error', array('DOMDocument not found, unable to use sanitizer', E_USER_WARNING, __FILE__, __LINE__)); + return ''; + } + $document = new DOMDocument(); + $document->encoding = 'UTF-8'; + $data = $this->preprocess($data, $type); + + set_error_handler(array('SimplePie_Misc', 'silence_errors')); + $document->loadHTML($data); + restore_error_handler(); + + // Strip comments + if ($this->strip_comments) + { + $xpath = new DOMXPath($document); + $comments = $xpath->query('//comment()'); + + foreach ($comments as $comment) + { + $comment->parentNode->removeChild($comment); + } + } + + // Strip out HTML tags and attributes that might cause various security problems. + // Based on recommendations by Mark Pilgrim at: + // http://diveintomark.org/archives/2003/06/12/how_to_consume_rss_safely + if ($this->strip_htmltags) + { + foreach ($this->strip_htmltags as $tag) + { + $this->strip_tag($tag, $document, $type); + } + } + + if ($this->strip_attributes) + { + foreach ($this->strip_attributes as $attrib) + { + $this->strip_attr($attrib, $document); + } + } + + // Replace relative URLs + $this->base = $base; + foreach ($this->replace_url_attributes as $element => $attributes) + { + $this->replace_urls($document, $element, $attributes); + } + + // If image handling (caching, etc.) is enabled, cache and rewrite all the image tags. + if (isset($this->image_handler) && ((string) $this->image_handler) !== '' && $this->enable_cache) + { + $images = $document->getElementsByTagName('img'); + foreach ($images as $img) + { + if ($img->hasAttribute('src')) + { + $image_url = call_user_func($this->cache_name_function, $img->getAttribute('src')); + $cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, $image_url, 'spi')); + + if ($cache->load()) + { + $img->setAttribute('src', $this->image_handler . $image_url); + } + else + { + $file = $this->registry->create('File', array($img->getAttribute('src'), $this->timeout, 5, array('X-FORWARDED-FOR' => $_SERVER['REMOTE_ADDR']), $this->useragent, $this->force_fsockopen)); + $headers = $file->headers; + + if ($file->success && ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300))) + { + if ($cache->save(array('headers' => $file->headers, 'body' => $file->body))) + { + $img->setAttribute('src', $this->image_handler . $image_url); + } + else + { + trigger_error("$this->cache_location is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING); + } + } + } + } + } + } + + // Remove the DOCTYPE + // Seems to cause segfaulting if we don't do this + if ($document->firstChild instanceof DOMDocumentType) + { + $document->removeChild($document->firstChild); + } + + // Move everything from the body to the root + $real_body = $document->getElementsByTagName('body')->item(0)->childNodes->item(0); + $document->replaceChild($real_body, $document->firstChild); + + // Finally, convert to a HTML string + $data = trim($document->saveHTML()); + + if ($this->remove_div) + { + $data = preg_replace('/^<div' . SIMPLEPIE_PCRE_XML_ATTRIBUTE . '>/', '', $data); + $data = preg_replace('/<\/div>$/', '', $data); + } + else + { + $data = preg_replace('/^<div' . SIMPLEPIE_PCRE_XML_ATTRIBUTE . '>/', '<div>', $data); + } + } + + if ($type & SIMPLEPIE_CONSTRUCT_IRI) + { + $absolute = $this->registry->call('Misc', 'absolutize_url', array($data, $base)); + if ($absolute !== false) + { + $data = $absolute; + } + } + + if ($type & (SIMPLEPIE_CONSTRUCT_TEXT | SIMPLEPIE_CONSTRUCT_IRI)) + { + $data = htmlspecialchars($data, ENT_COMPAT, 'UTF-8'); + } + + if ($this->output_encoding !== 'UTF-8') + { + $data = $this->registry->call('Misc', 'change_encoding', array($data, 'UTF-8', $this->output_encoding)); + } + } + return $data; + } + + protected function preprocess($html, $type) + { + $ret = ''; + if ($type & ~SIMPLEPIE_CONSTRUCT_XHTML) + { + // Atom XHTML constructs are wrapped with a div by default + // Note: No protection if $html contains a stray </div>! + $html = '<div>' . $html . '</div>'; + $ret .= '<!DOCTYPE html>'; + $content_type = 'text/html'; + } + else + { + $ret .= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'; + $content_type = 'application/xhtml+xml'; + } + + $ret .= '<html><head>'; + $ret .= '<meta http-equiv="Content-Type" content="' . $content_type . '; charset=utf-8" />'; + $ret .= '</head><body>' . $html . '</body></html>'; + return $ret; + } + + public function replace_urls($document, $tag, $attributes) + { + if (!is_array($attributes)) + { + $attributes = array($attributes); + } + + if (!is_array($this->strip_htmltags) || !in_array($tag, $this->strip_htmltags)) + { + $elements = $document->getElementsByTagName($tag); + foreach ($elements as $element) + { + foreach ($attributes as $attribute) + { + if ($element->hasAttribute($attribute)) + { + $value = $this->registry->call('Misc', 'absolutize_url', array($element->getAttribute($attribute), $this->base)); + if ($value !== false) + { + $element->setAttribute($attribute, $value); + } + } + } + } + } + } + + public function do_strip_htmltags($match) + { + if ($this->encode_instead_of_strip) + { + if (isset($match[4]) && !in_array(strtolower($match[1]), array('script', 'style'))) + { + $match[1] = htmlspecialchars($match[1], ENT_COMPAT, 'UTF-8'); + $match[2] = htmlspecialchars($match[2], ENT_COMPAT, 'UTF-8'); + return "<$match[1]$match[2]>$match[3]</$match[1]>"; + } + else + { + return htmlspecialchars($match[0], ENT_COMPAT, 'UTF-8'); + } + } + elseif (isset($match[4]) && !in_array(strtolower($match[1]), array('script', 'style'))) + { + return $match[4]; + } + else + { + return ''; + } + } + + protected function strip_tag($tag, $document, $type) + { + $xpath = new DOMXPath($document); + $elements = $xpath->query('body//' . $tag); + if ($this->encode_instead_of_strip) + { + foreach ($elements as $element) + { + $fragment = $document->createDocumentFragment(); + + // For elements which aren't script or style, include the tag itself + if (!in_array($tag, array('script', 'style'))) + { + $text = '<' . $tag; + if ($element->hasAttributes()) + { + $attrs = array(); + foreach ($element->attributes as $name => $attr) + { + $value = $attr->value; + + // In XHTML, empty values should never exist, so we repeat the value + if (empty($value) && ($type & SIMPLEPIE_CONSTRUCT_XHTML)) + { + $value = $name; + } + // For HTML, empty is fine + elseif (empty($value) && ($type & SIMPLEPIE_CONSTRUCT_HTML)) + { + $attrs[] = $name; + continue; + } + + // Standard attribute text + $attrs[] = $name . '="' . $attr->value . '"'; + } + $text .= ' ' . implode(' ', $attrs); + } + $text .= '>'; + $fragment->appendChild(new DOMText($text)); + } + + $number = $element->childNodes->length; + for ($i = $number; $i > 0; $i--) + { + $child = $element->childNodes->item(0); + $fragment->appendChild($child); + } + + if (!in_array($tag, array('script', 'style'))) + { + $fragment->appendChild(new DOMText('</' . $tag . '>')); + } + + $element->parentNode->replaceChild($fragment, $element); + } + + return; + } + elseif (in_array($tag, array('script', 'style'))) + { + foreach ($elements as $element) + { + $element->parentNode->removeChild($element); + } + + return; + } + else + { + foreach ($elements as $element) + { + $fragment = $document->createDocumentFragment(); + $number = $element->childNodes->length; + for ($i = $number; $i > 0; $i--) + { + $child = $element->childNodes->item(0); + $fragment->appendChild($child); + } + + $element->parentNode->replaceChild($fragment, $element); + } + } + } + + protected function strip_attr($attrib, $document) + { + $xpath = new DOMXPath($document); + $elements = $xpath->query('//*[@' . $attrib . ']'); + + foreach ($elements as $element) + { + $element->removeAttribute($attrib); + } + } +} diff --git a/wp-includes/SimplePie/Source.php b/wp-includes/SimplePie/Source.php new file mode 100644 index 0000000..51d8e6c --- /dev/null +++ b/wp-includes/SimplePie/Source.php @@ -0,0 +1,611 @@ +<?php +/** + * SimplePie + * + * A PHP-Based RSS and Atom Feed Framework. + * Takes the hard work out of managing a complete RSS/Atom solution. + * + * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * * Neither the name of the SimplePie Team nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS + * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package SimplePie + * @version 1.3.1 + * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @author Ryan Parman + * @author Geoffrey Sneddon + * @author Ryan McCue + * @link http://simplepie.org/ SimplePie + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + */ + +/** + * Handles `<atom:source>` + * + * Used by {@see SimplePie_Item::get_source()} + * + * This class can be overloaded with {@see SimplePie::set_source_class()} + * + * @package SimplePie + * @subpackage API + */ +class SimplePie_Source +{ + var $item; + var $data = array(); + protected $registry; + + public function __construct($item, $data) + { + $this->item = $item; + $this->data = $data; + } + + public function set_registry(SimplePie_Registry $registry) + { + $this->registry = $registry; + } + + public function __toString() + { + return md5(serialize($this->data)); + } + + public function get_source_tags($namespace, $tag) + { + if (isset($this->data['child'][$namespace][$tag])) + { + return $this->data['child'][$namespace][$tag]; + } + else + { + return null; + } + } + + public function get_base($element = array()) + { + return $this->item->get_base($element); + } + + public function sanitize($data, $type, $base = '') + { + return $this->item->sanitize($data, $type, $base); + } + + public function get_item() + { + return $this->item; + } + + public function get_title() + { + if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title')) + { + return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title')) + { + return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + return null; + } + } + + public function get_category($key = 0) + { + $categories = $this->get_categories(); + if (isset($categories[$key])) + { + return $categories[$key]; + } + else + { + return null; + } + } + + public function get_categories() + { + $categories = array(); + + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category) + { + $term = null; + $scheme = null; + $label = null; + if (isset($category['attribs']['']['term'])) + { + $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['scheme'])) + { + $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['label'])) + { + $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories[] = $this->registry->create('Category', array($term, $scheme, $label)); + } + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category) + { + // This is really the label, but keep this as the term also for BC. + // Label will also work on retrieving because that falls back to term. + $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); + if (isset($category['attribs']['']['domain'])) + { + $scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $scheme = null; + } + $categories[] = $this->registry->create('Category', array($term, $scheme, null)); + } + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category) + { + $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); + } + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category) + { + $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); + } + + if (!empty($categories)) + { + return array_unique($categories); + } + else + { + return null; + } + } + + public function get_author($key = 0) + { + $authors = $this->get_authors(); + if (isset($authors[$key])) + { + return $authors[$key]; + } + else + { + return null; + } + } + + public function get_authors() + { + $authors = array(); + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author) + { + $name = null; + $uri = null; + $email = null; + if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) + { + $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) + { + $uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); + } + if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) + { + $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $uri !== null) + { + $authors[] = $this->registry->create('Author', array($name, $uri, $email)); + } + } + if ($author = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author')) + { + $name = null; + $url = null; + $email = null; + if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) + { + $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) + { + $url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); + } + if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) + { + $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $url !== null) + { + $authors[] = $this->registry->create('Author', array($name, $url, $email)); + } + } + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author) + { + $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); + } + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author) + { + $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); + } + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author) + { + $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); + } + + if (!empty($authors)) + { + return array_unique($authors); + } + else + { + return null; + } + } + + public function get_contributor($key = 0) + { + $contributors = $this->get_contributors(); + if (isset($contributors[$key])) + { + return $contributors[$key]; + } + else + { + return null; + } + } + + public function get_contributors() + { + $contributors = array(); + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor) + { + $name = null; + $uri = null; + $email = null; + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) + { + $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) + { + $uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) + { + $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $uri !== null) + { + $contributors[] = $this->registry->create('Author', array($name, $uri, $email)); + } + } + foreach ((array) $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor) + { + $name = null; + $url = null; + $email = null; + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) + { + $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) + { + $url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) + { + $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $url !== null) + { + $contributors[] = $this->registry->create('Author', array($name, $url, $email)); + } + } + + if (!empty($contributors)) + { + return array_unique($contributors); + } + else + { + return null; + } + } + + public function get_link($key = 0, $rel = 'alternate') + { + $links = $this->get_links($rel); + if (isset($links[$key])) + { + return $links[$key]; + } + else + { + return null; + } + } + + /** + * Added for parity between the parent-level and the item/entry-level. + */ + public function get_permalink() + { + return $this->get_link(0); + } + + public function get_links($rel = 'alternate') + { + if (!isset($this->data['links'])) + { + $this->data['links'] = array(); + if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link')) + { + foreach ($links as $link) + { + if (isset($link['attribs']['']['href'])) + { + $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; + $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); + } + } + } + if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link')) + { + foreach ($links as $link) + { + if (isset($link['attribs']['']['href'])) + { + $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; + $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); + + } + } + } + if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link')) + { + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + } + if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link')) + { + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + } + if ($links = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link')) + { + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + } + + $keys = array_keys($this->data['links']); + foreach ($keys as $key) + { + if ($this->registry->call('Misc', 'is_isegment_nz_nc', array($key))) + { + if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key])) + { + $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]); + $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]; + } + else + { + $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key]; + } + } + elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY) + { + $this->data['links'][substr($key, 41)] =& $this->data['links'][$key]; + } + $this->data['links'][$key] = array_unique($this->data['links'][$key]); + } + } + + if (isset($this->data['links'][$rel])) + { + return $this->data['links'][$rel]; + } + else + { + return null; + } + } + + public function get_description() + { + if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'subtitle')) + { + return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'tagline')) + { + return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); + } + else + { + return null; + } + } + + public function get_copyright() + { + if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights')) + { + return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'copyright')) + { + return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'copyright')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + return null; + } + } + + public function get_language() + { + if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'language')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_11, 'language')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_DC_10, 'language')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (isset($this->data['xml_lang'])) + { + return $this->sanitize($this->data['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + return null; + } + } + + public function get_latitude() + { + if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat')) + { + return (float) $return[0]['data']; + } + elseif (($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) + { + return (float) $match[1]; + } + else + { + return null; + } + } + + public function get_longitude() + { + if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long')) + { + return (float) $return[0]['data']; + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon')) + { + return (float) $return[0]['data']; + } + elseif (($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) + { + return (float) $match[2]; + } + else + { + return null; + } + } + + public function get_image_url() + { + if ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'image')) + { + return $this->sanitize($return[0]['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'logo')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); + } + elseif ($return = $this->get_source_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); + } + else + { + return null; + } + } +} + diff --git a/wp-includes/SimplePie/XML/Declaration/Parser.php b/wp-includes/SimplePie/XML/Declaration/Parser.php new file mode 100644 index 0000000..aec19f1 --- /dev/null +++ b/wp-includes/SimplePie/XML/Declaration/Parser.php @@ -0,0 +1,362 @@ +<?php +/** + * SimplePie + * + * A PHP-Based RSS and Atom Feed Framework. + * Takes the hard work out of managing a complete RSS/Atom solution. + * + * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * * Neither the name of the SimplePie Team nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS + * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package SimplePie + * @version 1.3.1 + * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @author Ryan Parman + * @author Geoffrey Sneddon + * @author Ryan McCue + * @link http://simplepie.org/ SimplePie + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + */ + + +/** + * Parses the XML Declaration + * + * @package SimplePie + * @subpackage Parsing + */ +class SimplePie_XML_Declaration_Parser +{ + /** + * XML Version + * + * @access public + * @var string + */ + var $version = '1.0'; + + /** + * Encoding + * + * @access public + * @var string + */ + var $encoding = 'UTF-8'; + + /** + * Standalone + * + * @access public + * @var bool + */ + var $standalone = false; + + /** + * Current state of the state machine + * + * @access private + * @var string + */ + var $state = 'before_version_name'; + + /** + * Input data + * + * @access private + * @var string + */ + var $data = ''; + + /** + * Input data length (to avoid calling strlen() everytime this is needed) + * + * @access private + * @var int + */ + var $data_length = 0; + + /** + * Current position of the pointer + * + * @var int + * @access private + */ + var $position = 0; + + /** + * Create an instance of the class with the input data + * + * @access public + * @param string $data Input data + */ + public function __construct($data) + { + $this->data = $data; + $this->data_length = strlen($this->data); + } + + /** + * Parse the input data + * + * @access public + * @return bool true on success, false on failure + */ + public function parse() + { + while ($this->state && $this->state !== 'emit' && $this->has_data()) + { + $state = $this->state; + $this->$state(); + } + $this->data = ''; + if ($this->state === 'emit') + { + return true; + } + else + { + $this->version = ''; + $this->encoding = ''; + $this->standalone = ''; + return false; + } + } + + /** + * Check whether there is data beyond the pointer + * + * @access private + * @return bool true if there is further data, false if not + */ + public function has_data() + { + return (bool) ($this->position < $this->data_length); + } + + /** + * Advance past any whitespace + * + * @return int Number of whitespace characters passed + */ + public function skip_whitespace() + { + $whitespace = strspn($this->data, "\x09\x0A\x0D\x20", $this->position); + $this->position += $whitespace; + return $whitespace; + } + + /** + * Read value + */ + public function get_value() + { + $quote = substr($this->data, $this->position, 1); + if ($quote === '"' || $quote === "'") + { + $this->position++; + $len = strcspn($this->data, $quote, $this->position); + if ($this->has_data()) + { + $value = substr($this->data, $this->position, $len); + $this->position += $len + 1; + return $value; + } + } + return false; + } + + public function before_version_name() + { + if ($this->skip_whitespace()) + { + $this->state = 'version_name'; + } + else + { + $this->state = false; + } + } + + public function version_name() + { + if (substr($this->data, $this->position, 7) === 'version') + { + $this->position += 7; + $this->skip_whitespace(); + $this->state = 'version_equals'; + } + else + { + $this->state = false; + } + } + + public function version_equals() + { + if (substr($this->data, $this->position, 1) === '=') + { + $this->position++; + $this->skip_whitespace(); + $this->state = 'version_value'; + } + else + { + $this->state = false; + } + } + + public function version_value() + { + if ($this->version = $this->get_value()) + { + $this->skip_whitespace(); + if ($this->has_data()) + { + $this->state = 'encoding_name'; + } + else + { + $this->state = 'emit'; + } + } + else + { + $this->state = false; + } + } + + public function encoding_name() + { + if (substr($this->data, $this->position, 8) === 'encoding') + { + $this->position += 8; + $this->skip_whitespace(); + $this->state = 'encoding_equals'; + } + else + { + $this->state = 'standalone_name'; + } + } + + public function encoding_equals() + { + if (substr($this->data, $this->position, 1) === '=') + { + $this->position++; + $this->skip_whitespace(); + $this->state = 'encoding_value'; + } + else + { + $this->state = false; + } + } + + public function encoding_value() + { + if ($this->encoding = $this->get_value()) + { + $this->skip_whitespace(); + if ($this->has_data()) + { + $this->state = 'standalone_name'; + } + else + { + $this->state = 'emit'; + } + } + else + { + $this->state = false; + } + } + + public function standalone_name() + { + if (substr($this->data, $this->position, 10) === 'standalone') + { + $this->position += 10; + $this->skip_whitespace(); + $this->state = 'standalone_equals'; + } + else + { + $this->state = false; + } + } + + public function standalone_equals() + { + if (substr($this->data, $this->position, 1) === '=') + { + $this->position++; + $this->skip_whitespace(); + $this->state = 'standalone_value'; + } + else + { + $this->state = false; + } + } + + public function standalone_value() + { + if ($standalone = $this->get_value()) + { + switch ($standalone) + { + case 'yes': + $this->standalone = true; + break; + + case 'no': + $this->standalone = false; + break; + + default: + $this->state = false; + return; + } + + $this->skip_whitespace(); + if ($this->has_data()) + { + $this->state = false; + } + else + { + $this->state = 'emit'; + } + } + else + { + $this->state = false; + } + } +} diff --git a/wp-includes/SimplePie/gzdecode.php b/wp-includes/SimplePie/gzdecode.php new file mode 100644 index 0000000..52e024e --- /dev/null +++ b/wp-includes/SimplePie/gzdecode.php @@ -0,0 +1,371 @@ +<?php +/** + * SimplePie + * + * A PHP-Based RSS and Atom Feed Framework. + * Takes the hard work out of managing a complete RSS/Atom solution. + * + * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * * Neither the name of the SimplePie Team nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS + * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package SimplePie + * @version 1.3.1 + * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @author Ryan Parman + * @author Geoffrey Sneddon + * @author Ryan McCue + * @link http://simplepie.org/ SimplePie + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + */ + + +/** + * Decode 'gzip' encoded HTTP data + * + * @package SimplePie + * @subpackage HTTP + * @link http://www.gzip.org/format.txt + */ +class SimplePie_gzdecode +{ + /** + * Compressed data + * + * @access private + * @var string + * @see gzdecode::$data + */ + var $compressed_data; + + /** + * Size of compressed data + * + * @access private + * @var int + */ + var $compressed_size; + + /** + * Minimum size of a valid gzip string + * + * @access private + * @var int + */ + var $min_compressed_size = 18; + + /** + * Current position of pointer + * + * @access private + * @var int + */ + var $position = 0; + + /** + * Flags (FLG) + * + * @access private + * @var int + */ + var $flags; + + /** + * Uncompressed data + * + * @access public + * @see gzdecode::$compressed_data + * @var string + */ + var $data; + + /** + * Modified time + * + * @access public + * @var int + */ + var $MTIME; + + /** + * Extra Flags + * + * @access public + * @var int + */ + var $XFL; + + /** + * Operating System + * + * @access public + * @var int + */ + var $OS; + + /** + * Subfield ID 1 + * + * @access public + * @see gzdecode::$extra_field + * @see gzdecode::$SI2 + * @var string + */ + var $SI1; + + /** + * Subfield ID 2 + * + * @access public + * @see gzdecode::$extra_field + * @see gzdecode::$SI1 + * @var string + */ + var $SI2; + + /** + * Extra field content + * + * @access public + * @see gzdecode::$SI1 + * @see gzdecode::$SI2 + * @var string + */ + var $extra_field; + + /** + * Original filename + * + * @access public + * @var string + */ + var $filename; + + /** + * Human readable comment + * + * @access public + * @var string + */ + var $comment; + + /** + * Don't allow anything to be set + * + * @param string $name + * @param mixed $value + */ + public function __set($name, $value) + { + trigger_error("Cannot write property $name", E_USER_ERROR); + } + + /** + * Set the compressed string and related properties + * + * @param string $data + */ + public function __construct($data) + { + $this->compressed_data = $data; + $this->compressed_size = strlen($data); + } + + /** + * Decode the GZIP stream + * + * @return bool Successfulness + */ + public function parse() + { + if ($this->compressed_size >= $this->min_compressed_size) + { + // Check ID1, ID2, and CM + if (substr($this->compressed_data, 0, 3) !== "\x1F\x8B\x08") + { + return false; + } + + // Get the FLG (FLaGs) + $this->flags = ord($this->compressed_data[3]); + + // FLG bits above (1 << 4) are reserved + if ($this->flags > 0x1F) + { + return false; + } + + // Advance the pointer after the above + $this->position += 4; + + // MTIME + $mtime = substr($this->compressed_data, $this->position, 4); + // Reverse the string if we're on a big-endian arch because l is the only signed long and is machine endianness + if (current(unpack('S', "\x00\x01")) === 1) + { + $mtime = strrev($mtime); + } + $this->MTIME = current(unpack('l', $mtime)); + $this->position += 4; + + // Get the XFL (eXtra FLags) + $this->XFL = ord($this->compressed_data[$this->position++]); + + // Get the OS (Operating System) + $this->OS = ord($this->compressed_data[$this->position++]); + + // Parse the FEXTRA + if ($this->flags & 4) + { + // Read subfield IDs + $this->SI1 = $this->compressed_data[$this->position++]; + $this->SI2 = $this->compressed_data[$this->position++]; + + // SI2 set to zero is reserved for future use + if ($this->SI2 === "\x00") + { + return false; + } + + // Get the length of the extra field + $len = current(unpack('v', substr($this->compressed_data, $this->position, 2))); + $this->position += 2; + + // Check the length of the string is still valid + $this->min_compressed_size += $len + 4; + if ($this->compressed_size >= $this->min_compressed_size) + { + // Set the extra field to the given data + $this->extra_field = substr($this->compressed_data, $this->position, $len); + $this->position += $len; + } + else + { + return false; + } + } + + // Parse the FNAME + if ($this->flags & 8) + { + // Get the length of the filename + $len = strcspn($this->compressed_data, "\x00", $this->position); + + // Check the length of the string is still valid + $this->min_compressed_size += $len + 1; + if ($this->compressed_size >= $this->min_compressed_size) + { + // Set the original filename to the given string + $this->filename = substr($this->compressed_data, $this->position, $len); + $this->position += $len + 1; + } + else + { + return false; + } + } + + // Parse the FCOMMENT + if ($this->flags & 16) + { + // Get the length of the comment + $len = strcspn($this->compressed_data, "\x00", $this->position); + + // Check the length of the string is still valid + $this->min_compressed_size += $len + 1; + if ($this->compressed_size >= $this->min_compressed_size) + { + // Set the original comment to the given string + $this->comment = substr($this->compressed_data, $this->position, $len); + $this->position += $len + 1; + } + else + { + return false; + } + } + + // Parse the FHCRC + if ($this->flags & 2) + { + // Check the length of the string is still valid + $this->min_compressed_size += $len + 2; + if ($this->compressed_size >= $this->min_compressed_size) + { + // Read the CRC + $crc = current(unpack('v', substr($this->compressed_data, $this->position, 2))); + + // Check the CRC matches + if ((crc32(substr($this->compressed_data, 0, $this->position)) & 0xFFFF) === $crc) + { + $this->position += 2; + } + else + { + return false; + } + } + else + { + return false; + } + } + + // Decompress the actual data + if (($this->data = gzinflate(substr($this->compressed_data, $this->position, -8))) === false) + { + return false; + } + else + { + $this->position = $this->compressed_size - 8; + } + + // Check CRC of data + $crc = current(unpack('V', substr($this->compressed_data, $this->position, 4))); + $this->position += 4; + /*if (extension_loaded('hash') && sprintf('%u', current(unpack('V', hash('crc32b', $this->data)))) !== sprintf('%u', $crc)) + { + return false; + }*/ + + // Check ISIZE of data + $isize = current(unpack('V', substr($this->compressed_data, $this->position, 4))); + $this->position += 4; + if (sprintf('%u', strlen($this->data) & 0xFFFFFFFF) !== sprintf('%u', $isize)) + { + return false; + } + + // Wow, against all odds, we've actually got a valid gzip string + return true; + } + else + { + return false; + } + } +} diff --git a/wp-includes/Text/Diff.php b/wp-includes/Text/Diff.php new file mode 100644 index 0000000..dc24b67 --- /dev/null +++ b/wp-includes/Text/Diff.php @@ -0,0 +1,450 @@ +<?php +/** + * General API for generating and formatting diffs - the differences between + * two sequences of strings. + * + * The original PHP version of this code was written by Geoffrey T. Dairiki + * <dairiki@dairiki.org>, and is used/adapted with his permission. + * + * Copyright 2004 Geoffrey T. Dairiki <dairiki@dairiki.org> + * Copyright 2004-2010 The Horde Project (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you did + * not receive this file, see http://opensource.org/licenses/lgpl-license.php. + * + * @package Text_Diff + * @author Geoffrey T. Dairiki <dairiki@dairiki.org> + */ +class Text_Diff { + + /** + * Array of changes. + * + * @var array + */ + var $_edits; + + /** + * Computes diffs between sequences of strings. + * + * @param string $engine Name of the diffing engine to use. 'auto' + * will automatically select the best. + * @param array $params Parameters to pass to the diffing engine. + * Normally an array of two arrays, each + * containing the lines from a file. + */ + function Text_Diff($engine, $params) + { + // Backward compatibility workaround. + if (!is_string($engine)) { + $params = array($engine, $params); + $engine = 'auto'; + } + + if ($engine == 'auto') { + $engine = extension_loaded('xdiff') ? 'xdiff' : 'native'; + } else { + $engine = basename($engine); + } + + // WP #7391 + require_once dirname(__FILE__).'/Diff/Engine/' . $engine . '.php'; + $class = 'Text_Diff_Engine_' . $engine; + $diff_engine = new $class(); + + $this->_edits = call_user_func_array(array($diff_engine, 'diff'), $params); + } + + /** + * Returns the array of differences. + */ + function getDiff() + { + return $this->_edits; + } + + /** + * returns the number of new (added) lines in a given diff. + * + * @since Text_Diff 1.1.0 + * + * @return integer The number of new lines + */ + function countAddedLines() + { + $count = 0; + foreach ($this->_edits as $edit) { + if (is_a($edit, 'Text_Diff_Op_add') || + is_a($edit, 'Text_Diff_Op_change')) { + $count += $edit->nfinal(); + } + } + return $count; + } + + /** + * Returns the number of deleted (removed) lines in a given diff. + * + * @since Text_Diff 1.1.0 + * + * @return integer The number of deleted lines + */ + function countDeletedLines() + { + $count = 0; + foreach ($this->_edits as $edit) { + if (is_a($edit, 'Text_Diff_Op_delete') || + is_a($edit, 'Text_Diff_Op_change')) { + $count += $edit->norig(); + } + } + return $count; + } + + /** + * Computes a reversed diff. + * + * Example: + * <code> + * $diff = new Text_Diff($lines1, $lines2); + * $rev = $diff->reverse(); + * </code> + * + * @return Text_Diff A Diff object representing the inverse of the + * original diff. Note that we purposely don't return a + * reference here, since this essentially is a clone() + * method. + */ + function reverse() + { + if (version_compare(zend_version(), '2', '>')) { + $rev = clone($this); + } else { + $rev = $this; + } + $rev->_edits = array(); + foreach ($this->_edits as $edit) { + $rev->_edits[] = $edit->reverse(); + } + return $rev; + } + + /** + * Checks for an empty diff. + * + * @return boolean True if two sequences were identical. + */ + function isEmpty() + { + foreach ($this->_edits as $edit) { + if (!is_a($edit, 'Text_Diff_Op_copy')) { + return false; + } + } + return true; + } + + /** + * Computes the length of the Longest Common Subsequence (LCS). + * + * This is mostly for diagnostic purposes. + * + * @return integer The length of the LCS. + */ + function lcs() + { + $lcs = 0; + foreach ($this->_edits as $edit) { + if (is_a($edit, 'Text_Diff_Op_copy')) { + $lcs += count($edit->orig); + } + } + return $lcs; + } + + /** + * Gets the original set of lines. + * + * This reconstructs the $from_lines parameter passed to the constructor. + * + * @return array The original sequence of strings. + */ + function getOriginal() + { + $lines = array(); + foreach ($this->_edits as $edit) { + if ($edit->orig) { + array_splice($lines, count($lines), 0, $edit->orig); + } + } + return $lines; + } + + /** + * Gets the final set of lines. + * + * This reconstructs the $to_lines parameter passed to the constructor. + * + * @return array The sequence of strings. + */ + function getFinal() + { + $lines = array(); + foreach ($this->_edits as $edit) { + if ($edit->final) { + array_splice($lines, count($lines), 0, $edit->final); + } + } + return $lines; + } + + /** + * Removes trailing newlines from a line of text. This is meant to be used + * with array_walk(). + * + * @param string $line The line to trim. + * @param integer $key The index of the line in the array. Not used. + */ + static function trimNewlines(&$line, $key) + { + $line = str_replace(array("\n", "\r"), '', $line); + } + + /** + * Determines the location of the system temporary directory. + * + * @static + * + * @access protected + * + * @return string A directory name which can be used for temp files. + * Returns false if one could not be found. + */ + function _getTempDir() + { + $tmp_locations = array('/tmp', '/var/tmp', 'c:\WUTemp', 'c:\temp', + 'c:\windows\temp', 'c:\winnt\temp'); + + /* Try PHP's upload_tmp_dir directive. */ + $tmp = ini_get('upload_tmp_dir'); + + /* Otherwise, try to determine the TMPDIR environment variable. */ + if (!strlen($tmp)) { + $tmp = getenv('TMPDIR'); + } + + /* If we still cannot determine a value, then cycle through a list of + * preset possibilities. */ + while (!strlen($tmp) && count($tmp_locations)) { + $tmp_check = array_shift($tmp_locations); + if (@is_dir($tmp_check)) { + $tmp = $tmp_check; + } + } + + /* If it is still empty, we have failed, so return false; otherwise + * return the directory determined. */ + return strlen($tmp) ? $tmp : false; + } + + /** + * Checks a diff for validity. + * + * This is here only for debugging purposes. + */ + function _check($from_lines, $to_lines) + { + if (serialize($from_lines) != serialize($this->getOriginal())) { + trigger_error("Reconstructed original doesn't match", E_USER_ERROR); + } + if (serialize($to_lines) != serialize($this->getFinal())) { + trigger_error("Reconstructed final doesn't match", E_USER_ERROR); + } + + $rev = $this->reverse(); + if (serialize($to_lines) != serialize($rev->getOriginal())) { + trigger_error("Reversed original doesn't match", E_USER_ERROR); + } + if (serialize($from_lines) != serialize($rev->getFinal())) { + trigger_error("Reversed final doesn't match", E_USER_ERROR); + } + + $prevtype = null; + foreach ($this->_edits as $edit) { + if ($prevtype == get_class($edit)) { + trigger_error("Edit sequence is non-optimal", E_USER_ERROR); + } + $prevtype = get_class($edit); + } + + return true; + } + +} + +/** + * @package Text_Diff + * @author Geoffrey T. Dairiki <dairiki@dairiki.org> + */ +class Text_MappedDiff extends Text_Diff { + + /** + * Computes a diff between sequences of strings. + * + * This can be used to compute things like case-insensitve diffs, or diffs + * which ignore changes in white-space. + * + * @param array $from_lines An array of strings. + * @param array $to_lines An array of strings. + * @param array $mapped_from_lines This array should have the same size + * number of elements as $from_lines. The + * elements in $mapped_from_lines and + * $mapped_to_lines are what is actually + * compared when computing the diff. + * @param array $mapped_to_lines This array should have the same number + * of elements as $to_lines. + */ + function Text_MappedDiff($from_lines, $to_lines, + $mapped_from_lines, $mapped_to_lines) + { + assert(count($from_lines) == count($mapped_from_lines)); + assert(count($to_lines) == count($mapped_to_lines)); + + parent::Text_Diff($mapped_from_lines, $mapped_to_lines); + + $xi = $yi = 0; + for ($i = 0; $i < count($this->_edits); $i++) { + $orig = &$this->_edits[$i]->orig; + if (is_array($orig)) { + $orig = array_slice($from_lines, $xi, count($orig)); + $xi += count($orig); + } + + $final = &$this->_edits[$i]->final; + if (is_array($final)) { + $final = array_slice($to_lines, $yi, count($final)); + $yi += count($final); + } + } + } + +} + +/** + * @package Text_Diff + * @author Geoffrey T. Dairiki <dairiki@dairiki.org> + * + * @access private + */ +class Text_Diff_Op { + + var $orig; + var $final; + + function &reverse() + { + trigger_error('Abstract method', E_USER_ERROR); + } + + function norig() + { + return $this->orig ? count($this->orig) : 0; + } + + function nfinal() + { + return $this->final ? count($this->final) : 0; + } + +} + +/** + * @package Text_Diff + * @author Geoffrey T. Dairiki <dairiki@dairiki.org> + * + * @access private + */ +class Text_Diff_Op_copy extends Text_Diff_Op { + + function Text_Diff_Op_copy($orig, $final = false) + { + if (!is_array($final)) { + $final = $orig; + } + $this->orig = $orig; + $this->final = $final; + } + + function &reverse() + { + $reverse = new Text_Diff_Op_copy($this->final, $this->orig); + return $reverse; + } + +} + +/** + * @package Text_Diff + * @author Geoffrey T. Dairiki <dairiki@dairiki.org> + * + * @access private + */ +class Text_Diff_Op_delete extends Text_Diff_Op { + + function Text_Diff_Op_delete($lines) + { + $this->orig = $lines; + $this->final = false; + } + + function &reverse() + { + $reverse = new Text_Diff_Op_add($this->orig); + return $reverse; + } + +} + +/** + * @package Text_Diff + * @author Geoffrey T. Dairiki <dairiki@dairiki.org> + * + * @access private + */ +class Text_Diff_Op_add extends Text_Diff_Op { + + function Text_Diff_Op_add($lines) + { + $this->final = $lines; + $this->orig = false; + } + + function &reverse() + { + $reverse = new Text_Diff_Op_delete($this->final); + return $reverse; + } + +} + +/** + * @package Text_Diff + * @author Geoffrey T. Dairiki <dairiki@dairiki.org> + * + * @access private + */ +class Text_Diff_Op_change extends Text_Diff_Op { + + function Text_Diff_Op_change($orig, $final) + { + $this->orig = $orig; + $this->final = $final; + } + + function &reverse() + { + $reverse = new Text_Diff_Op_change($this->final, $this->orig); + return $reverse; + } + +} diff --git a/wp-includes/Text/Diff/Engine/native.php b/wp-includes/Text/Diff/Engine/native.php new file mode 100644 index 0000000..e908cfe --- /dev/null +++ b/wp-includes/Text/Diff/Engine/native.php @@ -0,0 +1,436 @@ +<?php +/** + * Class used internally by Text_Diff to actually compute the diffs. + * + * This class is implemented using native PHP code. + * + * The algorithm used here is mostly lifted from the perl module + * Algorithm::Diff (version 1.06) by Ned Konz, which is available at: + * http://www.perl.com/CPAN/authors/id/N/NE/NEDKONZ/Algorithm-Diff-1.06.zip + * + * More ideas are taken from: http://www.ics.uci.edu/~eppstein/161/960229.html + * + * Some ideas (and a bit of code) are taken from analyze.c, of GNU + * diffutils-2.7, which can be found at: + * ftp://gnudist.gnu.org/pub/gnu/diffutils/diffutils-2.7.tar.gz + * + * Some ideas (subdivision by NCHUNKS > 2, and some optimizations) are from + * Geoffrey T. Dairiki <dairiki@dairiki.org>. The original PHP version of this + * code was written by him, and is used/adapted with his permission. + * + * Copyright 2004-2010 The Horde Project (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you did + * not receive this file, see http://opensource.org/licenses/lgpl-license.php. + * + * @author Geoffrey T. Dairiki <dairiki@dairiki.org> + * @package Text_Diff + */ +class Text_Diff_Engine_native { + + function diff($from_lines, $to_lines) + { + array_walk($from_lines, array('Text_Diff', 'trimNewlines')); + array_walk($to_lines, array('Text_Diff', 'trimNewlines')); + + $n_from = count($from_lines); + $n_to = count($to_lines); + + $this->xchanged = $this->ychanged = array(); + $this->xv = $this->yv = array(); + $this->xind = $this->yind = array(); + unset($this->seq); + unset($this->in_seq); + unset($this->lcs); + + // Skip leading common lines. + for ($skip = 0; $skip < $n_from && $skip < $n_to; $skip++) { + if ($from_lines[$skip] !== $to_lines[$skip]) { + break; + } + $this->xchanged[$skip] = $this->ychanged[$skip] = false; + } + + // Skip trailing common lines. + $xi = $n_from; $yi = $n_to; + for ($endskip = 0; --$xi > $skip && --$yi > $skip; $endskip++) { + if ($from_lines[$xi] !== $to_lines[$yi]) { + break; + } + $this->xchanged[$xi] = $this->ychanged[$yi] = false; + } + + // Ignore lines which do not exist in both files. + for ($xi = $skip; $xi < $n_from - $endskip; $xi++) { + $xhash[$from_lines[$xi]] = 1; + } + for ($yi = $skip; $yi < $n_to - $endskip; $yi++) { + $line = $to_lines[$yi]; + if (($this->ychanged[$yi] = empty($xhash[$line]))) { + continue; + } + $yhash[$line] = 1; + $this->yv[] = $line; + $this->yind[] = $yi; + } + for ($xi = $skip; $xi < $n_from - $endskip; $xi++) { + $line = $from_lines[$xi]; + if (($this->xchanged[$xi] = empty($yhash[$line]))) { + continue; + } + $this->xv[] = $line; + $this->xind[] = $xi; + } + + // Find the LCS. + $this->_compareseq(0, count($this->xv), 0, count($this->yv)); + + // Merge edits when possible. + $this->_shiftBoundaries($from_lines, $this->xchanged, $this->ychanged); + $this->_shiftBoundaries($to_lines, $this->ychanged, $this->xchanged); + + // Compute the edit operations. + $edits = array(); + $xi = $yi = 0; + while ($xi < $n_from || $yi < $n_to) { + assert($yi < $n_to || $this->xchanged[$xi]); + assert($xi < $n_from || $this->ychanged[$yi]); + + // Skip matching "snake". + $copy = array(); + while ($xi < $n_from && $yi < $n_to + && !$this->xchanged[$xi] && !$this->ychanged[$yi]) { + $copy[] = $from_lines[$xi++]; + ++$yi; + } + if ($copy) { + $edits[] = new Text_Diff_Op_copy($copy); + } + + // Find deletes & adds. + $delete = array(); + while ($xi < $n_from && $this->xchanged[$xi]) { + $delete[] = $from_lines[$xi++]; + } + + $add = array(); + while ($yi < $n_to && $this->ychanged[$yi]) { + $add[] = $to_lines[$yi++]; + } + + if ($delete && $add) { + $edits[] = new Text_Diff_Op_change($delete, $add); + } elseif ($delete) { + $edits[] = new Text_Diff_Op_delete($delete); + } elseif ($add) { + $edits[] = new Text_Diff_Op_add($add); + } + } + + return $edits; + } + + /** + * Divides the Largest Common Subsequence (LCS) of the sequences (XOFF, + * XLIM) and (YOFF, YLIM) into NCHUNKS approximately equally sized + * segments. + * + * Returns (LCS, PTS). LCS is the length of the LCS. PTS is an array of + * NCHUNKS+1 (X, Y) indexes giving the diving points between sub + * sequences. The first sub-sequence is contained in (X0, X1), (Y0, Y1), + * the second in (X1, X2), (Y1, Y2) and so on. Note that (X0, Y0) == + * (XOFF, YOFF) and (X[NCHUNKS], Y[NCHUNKS]) == (XLIM, YLIM). + * + * This function assumes that the first lines of the specified portions of + * the two files do not match, and likewise that the last lines do not + * match. The caller must trim matching lines from the beginning and end + * of the portions it is going to specify. + */ + function _diag ($xoff, $xlim, $yoff, $ylim, $nchunks) + { + $flip = false; + + if ($xlim - $xoff > $ylim - $yoff) { + /* Things seems faster (I'm not sure I understand why) when the + * shortest sequence is in X. */ + $flip = true; + list ($xoff, $xlim, $yoff, $ylim) + = array($yoff, $ylim, $xoff, $xlim); + } + + if ($flip) { + for ($i = $ylim - 1; $i >= $yoff; $i--) { + $ymatches[$this->xv[$i]][] = $i; + } + } else { + for ($i = $ylim - 1; $i >= $yoff; $i--) { + $ymatches[$this->yv[$i]][] = $i; + } + } + + $this->lcs = 0; + $this->seq[0]= $yoff - 1; + $this->in_seq = array(); + $ymids[0] = array(); + + $numer = $xlim - $xoff + $nchunks - 1; + $x = $xoff; + for ($chunk = 0; $chunk < $nchunks; $chunk++) { + if ($chunk > 0) { + for ($i = 0; $i <= $this->lcs; $i++) { + $ymids[$i][$chunk - 1] = $this->seq[$i]; + } + } + + $x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $chunk) / $nchunks); + for (; $x < $x1; $x++) { + $line = $flip ? $this->yv[$x] : $this->xv[$x]; + if (empty($ymatches[$line])) { + continue; + } + $matches = $ymatches[$line]; + reset($matches); + while (list(, $y) = each($matches)) { + if (empty($this->in_seq[$y])) { + $k = $this->_lcsPos($y); + assert($k > 0); + $ymids[$k] = $ymids[$k - 1]; + break; + } + } + while (list(, $y) = each($matches)) { + if ($y > $this->seq[$k - 1]) { + assert($y <= $this->seq[$k]); + /* Optimization: this is a common case: next match is + * just replacing previous match. */ + $this->in_seq[$this->seq[$k]] = false; + $this->seq[$k] = $y; + $this->in_seq[$y] = 1; + } elseif (empty($this->in_seq[$y])) { + $k = $this->_lcsPos($y); + assert($k > 0); + $ymids[$k] = $ymids[$k - 1]; + } + } + } + } + + $seps[] = $flip ? array($yoff, $xoff) : array($xoff, $yoff); + $ymid = $ymids[$this->lcs]; + for ($n = 0; $n < $nchunks - 1; $n++) { + $x1 = $xoff + (int)(($numer + ($xlim - $xoff) * $n) / $nchunks); + $y1 = $ymid[$n] + 1; + $seps[] = $flip ? array($y1, $x1) : array($x1, $y1); + } + $seps[] = $flip ? array($ylim, $xlim) : array($xlim, $ylim); + + return array($this->lcs, $seps); + } + + function _lcsPos($ypos) + { + $end = $this->lcs; + if ($end == 0 || $ypos > $this->seq[$end]) { + $this->seq[++$this->lcs] = $ypos; + $this->in_seq[$ypos] = 1; + return $this->lcs; + } + + $beg = 1; + while ($beg < $end) { + $mid = (int)(($beg + $end) / 2); + if ($ypos > $this->seq[$mid]) { + $beg = $mid + 1; + } else { + $end = $mid; + } + } + + assert($ypos != $this->seq[$end]); + + $this->in_seq[$this->seq[$end]] = false; + $this->seq[$end] = $ypos; + $this->in_seq[$ypos] = 1; + return $end; + } + + /** + * Finds LCS of two sequences. + * + * The results are recorded in the vectors $this->{x,y}changed[], by + * storing a 1 in the element for each line that is an insertion or + * deletion (ie. is not in the LCS). + * + * The subsequence of file 0 is (XOFF, XLIM) and likewise for file 1. + * + * Note that XLIM, YLIM are exclusive bounds. All line numbers are + * origin-0 and discarded lines are not counted. + */ + function _compareseq ($xoff, $xlim, $yoff, $ylim) + { + /* Slide down the bottom initial diagonal. */ + while ($xoff < $xlim && $yoff < $ylim + && $this->xv[$xoff] == $this->yv[$yoff]) { + ++$xoff; + ++$yoff; + } + + /* Slide up the top initial diagonal. */ + while ($xlim > $xoff && $ylim > $yoff + && $this->xv[$xlim - 1] == $this->yv[$ylim - 1]) { + --$xlim; + --$ylim; + } + + if ($xoff == $xlim || $yoff == $ylim) { + $lcs = 0; + } else { + /* This is ad hoc but seems to work well. $nchunks = + * sqrt(min($xlim - $xoff, $ylim - $yoff) / 2.5); $nchunks = + * max(2,min(8,(int)$nchunks)); */ + $nchunks = min(7, $xlim - $xoff, $ylim - $yoff) + 1; + list($lcs, $seps) + = $this->_diag($xoff, $xlim, $yoff, $ylim, $nchunks); + } + + if ($lcs == 0) { + /* X and Y sequences have no common subsequence: mark all + * changed. */ + while ($yoff < $ylim) { + $this->ychanged[$this->yind[$yoff++]] = 1; + } + while ($xoff < $xlim) { + $this->xchanged[$this->xind[$xoff++]] = 1; + } + } else { + /* Use the partitions to split this problem into subproblems. */ + reset($seps); + $pt1 = $seps[0]; + while ($pt2 = next($seps)) { + $this->_compareseq ($pt1[0], $pt2[0], $pt1[1], $pt2[1]); + $pt1 = $pt2; + } + } + } + + /** + * Adjusts inserts/deletes of identical lines to join changes as much as + * possible. + * + * We do something when a run of changed lines include a line at one end + * and has an excluded, identical line at the other. We are free to + * choose which identical line is included. `compareseq' usually chooses + * the one at the beginning, but usually it is cleaner to consider the + * following identical line to be the "change". + * + * This is extracted verbatim from analyze.c (GNU diffutils-2.7). + */ + function _shiftBoundaries($lines, &$changed, $other_changed) + { + $i = 0; + $j = 0; + + assert('count($lines) == count($changed)'); + $len = count($lines); + $other_len = count($other_changed); + + while (1) { + /* Scan forward to find the beginning of another run of + * changes. Also keep track of the corresponding point in the + * other file. + * + * Throughout this code, $i and $j are adjusted together so that + * the first $i elements of $changed and the first $j elements of + * $other_changed both contain the same number of zeros (unchanged + * lines). + * + * Furthermore, $j is always kept so that $j == $other_len or + * $other_changed[$j] == false. */ + while ($j < $other_len && $other_changed[$j]) { + $j++; + } + + while ($i < $len && ! $changed[$i]) { + assert('$j < $other_len && ! $other_changed[$j]'); + $i++; $j++; + while ($j < $other_len && $other_changed[$j]) { + $j++; + } + } + + if ($i == $len) { + break; + } + + $start = $i; + + /* Find the end of this run of changes. */ + while (++$i < $len && $changed[$i]) { + continue; + } + + do { + /* Record the length of this run of changes, so that we can + * later determine whether the run has grown. */ + $runlength = $i - $start; + + /* Move the changed region back, so long as the previous + * unchanged line matches the last changed one. This merges + * with previous changed regions. */ + while ($start > 0 && $lines[$start - 1] == $lines[$i - 1]) { + $changed[--$start] = 1; + $changed[--$i] = false; + while ($start > 0 && $changed[$start - 1]) { + $start--; + } + assert('$j > 0'); + while ($other_changed[--$j]) { + continue; + } + assert('$j >= 0 && !$other_changed[$j]'); + } + + /* Set CORRESPONDING to the end of the changed run, at the + * last point where it corresponds to a changed run in the + * other file. CORRESPONDING == LEN means no such point has + * been found. */ + $corresponding = $j < $other_len ? $i : $len; + + /* Move the changed region forward, so long as the first + * changed line matches the following unchanged one. This + * merges with following changed regions. Do this second, so + * that if there are no merges, the changed region is moved + * forward as far as possible. */ + while ($i < $len && $lines[$start] == $lines[$i]) { + $changed[$start++] = false; + $changed[$i++] = 1; + while ($i < $len && $changed[$i]) { + $i++; + } + + assert('$j < $other_len && ! $other_changed[$j]'); + $j++; + if ($j < $other_len && $other_changed[$j]) { + $corresponding = $i; + while ($j < $other_len && $other_changed[$j]) { + $j++; + } + } + } + } while ($runlength != $i - $start); + + /* If possible, move the fully-merged run of changes back to a + * corresponding run in the other file. */ + while ($corresponding < $i) { + $changed[--$start] = 1; + $changed[--$i] = 0; + assert('$j > 0'); + while ($other_changed[--$j]) { + continue; + } + assert('$j >= 0 && !$other_changed[$j]'); + } + } + } + +} diff --git a/wp-includes/Text/Diff/Engine/shell.php b/wp-includes/Text/Diff/Engine/shell.php new file mode 100644 index 0000000..faf3870 --- /dev/null +++ b/wp-includes/Text/Diff/Engine/shell.php @@ -0,0 +1,162 @@ +<?php +/** + * Class used internally by Diff to actually compute the diffs. + * + * This class uses the Unix `diff` program via shell_exec to compute the + * differences between the two input arrays. + * + * Copyright 2007-2010 The Horde Project (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you did + * not receive this file, see http://opensource.org/licenses/lgpl-license.php. + * + * @author Milian Wolff <mail@milianw.de> + * @package Text_Diff + * @since 0.3.0 + */ +class Text_Diff_Engine_shell { + + /** + * Path to the diff executable + * + * @var string + */ + var $_diffCommand = 'diff'; + + /** + * Returns the array of differences. + * + * @param array $from_lines lines of text from old file + * @param array $to_lines lines of text from new file + * + * @return array all changes made (array with Text_Diff_Op_* objects) + */ + function diff($from_lines, $to_lines) + { + array_walk($from_lines, array('Text_Diff', 'trimNewlines')); + array_walk($to_lines, array('Text_Diff', 'trimNewlines')); + + $temp_dir = Text_Diff::_getTempDir(); + + // Execute gnu diff or similar to get a standard diff file. + $from_file = tempnam($temp_dir, 'Text_Diff'); + $to_file = tempnam($temp_dir, 'Text_Diff'); + $fp = fopen($from_file, 'w'); + fwrite($fp, implode("\n", $from_lines)); + fclose($fp); + $fp = fopen($to_file, 'w'); + fwrite($fp, implode("\n", $to_lines)); + fclose($fp); + $diff = shell_exec($this->_diffCommand . ' ' . $from_file . ' ' . $to_file); + unlink($from_file); + unlink($to_file); + + if (is_null($diff)) { + // No changes were made + return array(new Text_Diff_Op_copy($from_lines)); + } + + $from_line_no = 1; + $to_line_no = 1; + $edits = array(); + + // Get changed lines by parsing something like: + // 0a1,2 + // 1,2c4,6 + // 1,5d6 + preg_match_all('#^(\d+)(?:,(\d+))?([adc])(\d+)(?:,(\d+))?$#m', $diff, + $matches, PREG_SET_ORDER); + + foreach ($matches as $match) { + if (!isset($match[5])) { + // This paren is not set every time (see regex). + $match[5] = false; + } + + if ($match[3] == 'a') { + $from_line_no--; + } + + if ($match[3] == 'd') { + $to_line_no--; + } + + if ($from_line_no < $match[1] || $to_line_no < $match[4]) { + // copied lines + assert('$match[1] - $from_line_no == $match[4] - $to_line_no'); + array_push($edits, + new Text_Diff_Op_copy( + $this->_getLines($from_lines, $from_line_no, $match[1] - 1), + $this->_getLines($to_lines, $to_line_no, $match[4] - 1))); + } + + switch ($match[3]) { + case 'd': + // deleted lines + array_push($edits, + new Text_Diff_Op_delete( + $this->_getLines($from_lines, $from_line_no, $match[2]))); + $to_line_no++; + break; + + case 'c': + // changed lines + array_push($edits, + new Text_Diff_Op_change( + $this->_getLines($from_lines, $from_line_no, $match[2]), + $this->_getLines($to_lines, $to_line_no, $match[5]))); + break; + + case 'a': + // added lines + array_push($edits, + new Text_Diff_Op_add( + $this->_getLines($to_lines, $to_line_no, $match[5]))); + $from_line_no++; + break; + } + } + + if (!empty($from_lines)) { + // Some lines might still be pending. Add them as copied + array_push($edits, + new Text_Diff_Op_copy( + $this->_getLines($from_lines, $from_line_no, + $from_line_no + count($from_lines) - 1), + $this->_getLines($to_lines, $to_line_no, + $to_line_no + count($to_lines) - 1))); + } + + return $edits; + } + + /** + * Get lines from either the old or new text + * + * @access private + * + * @param array &$text_lines Either $from_lines or $to_lines + * @param int &$line_no Current line number + * @param int $end Optional end line, when we want to chop more + * than one line. + * + * @return array The chopped lines + */ + function _getLines(&$text_lines, &$line_no, $end = false) + { + if (!empty($end)) { + $lines = array(); + // We can shift even more + while ($line_no <= $end) { + array_push($lines, array_shift($text_lines)); + $line_no++; + } + } else { + $lines = array(array_shift($text_lines)); + $line_no++; + } + + return $lines; + } + +} diff --git a/wp-includes/Text/Diff/Engine/string.php b/wp-includes/Text/Diff/Engine/string.php new file mode 100644 index 0000000..0f3b374 --- /dev/null +++ b/wp-includes/Text/Diff/Engine/string.php @@ -0,0 +1,248 @@ +<?php +/** + * Parses unified or context diffs output from eg. the diff utility. + * + * Example: + * <code> + * $patch = file_get_contents('example.patch'); + * $diff = new Text_Diff('string', array($patch)); + * $renderer = new Text_Diff_Renderer_inline(); + * echo $renderer->render($diff); + * </code> + * + * Copyright 2005 Örjan Persson <o@42mm.org> + * Copyright 2005-2010 The Horde Project (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you did + * not receive this file, see http://opensource.org/licenses/lgpl-license.php. + * + * @author Örjan Persson <o@42mm.org> + * @package Text_Diff + * @since 0.2.0 + */ +class Text_Diff_Engine_string { + + /** + * Parses a unified or context diff. + * + * First param contains the whole diff and the second can be used to force + * a specific diff type. If the second parameter is 'autodetect', the + * diff will be examined to find out which type of diff this is. + * + * @param string $diff The diff content. + * @param string $mode The diff mode of the content in $diff. One of + * 'context', 'unified', or 'autodetect'. + * + * @return array List of all diff operations. + */ + function diff($diff, $mode = 'autodetect') + { + // Detect line breaks. + $lnbr = "\n"; + if (strpos($diff, "\r\n") !== false) { + $lnbr = "\r\n"; + } elseif (strpos($diff, "\r") !== false) { + $lnbr = "\r"; + } + + // Make sure we have a line break at the EOF. + if (substr($diff, -strlen($lnbr)) != $lnbr) { + $diff .= $lnbr; + } + + if ($mode != 'autodetect' && $mode != 'context' && $mode != 'unified') { + return PEAR::raiseError('Type of diff is unsupported'); + } + + if ($mode == 'autodetect') { + $context = strpos($diff, '***'); + $unified = strpos($diff, '---'); + if ($context === $unified) { + return PEAR::raiseError('Type of diff could not be detected'); + } elseif ($context === false || $unified === false) { + $mode = $context !== false ? 'context' : 'unified'; + } else { + $mode = $context < $unified ? 'context' : 'unified'; + } + } + + // Split by new line and remove the diff header, if there is one. + $diff = explode($lnbr, $diff); + if (($mode == 'context' && strpos($diff[0], '***') === 0) || + ($mode == 'unified' && strpos($diff[0], '---') === 0)) { + array_shift($diff); + array_shift($diff); + } + + if ($mode == 'context') { + return $this->parseContextDiff($diff); + } else { + return $this->parseUnifiedDiff($diff); + } + } + + /** + * Parses an array containing the unified diff. + * + * @param array $diff Array of lines. + * + * @return array List of all diff operations. + */ + function parseUnifiedDiff($diff) + { + $edits = array(); + $end = count($diff) - 1; + for ($i = 0; $i < $end;) { + $diff1 = array(); + switch (substr($diff[$i], 0, 1)) { + case ' ': + do { + $diff1[] = substr($diff[$i], 1); + } while (++$i < $end && substr($diff[$i], 0, 1) == ' '); + $edits[] = new Text_Diff_Op_copy($diff1); + break; + + case '+': + // get all new lines + do { + $diff1[] = substr($diff[$i], 1); + } while (++$i < $end && substr($diff[$i], 0, 1) == '+'); + $edits[] = new Text_Diff_Op_add($diff1); + break; + + case '-': + // get changed or removed lines + $diff2 = array(); + do { + $diff1[] = substr($diff[$i], 1); + } while (++$i < $end && substr($diff[$i], 0, 1) == '-'); + + while ($i < $end && substr($diff[$i], 0, 1) == '+') { + $diff2[] = substr($diff[$i++], 1); + } + if (count($diff2) == 0) { + $edits[] = new Text_Diff_Op_delete($diff1); + } else { + $edits[] = new Text_Diff_Op_change($diff1, $diff2); + } + break; + + default: + $i++; + break; + } + } + + return $edits; + } + + /** + * Parses an array containing the context diff. + * + * @param array $diff Array of lines. + * + * @return array List of all diff operations. + */ + function parseContextDiff(&$diff) + { + $edits = array(); + $i = $max_i = $j = $max_j = 0; + $end = count($diff) - 1; + while ($i < $end && $j < $end) { + while ($i >= $max_i && $j >= $max_j) { + // Find the boundaries of the diff output of the two files + for ($i = $j; + $i < $end && substr($diff[$i], 0, 3) == '***'; + $i++); + for ($max_i = $i; + $max_i < $end && substr($diff[$max_i], 0, 3) != '---'; + $max_i++); + for ($j = $max_i; + $j < $end && substr($diff[$j], 0, 3) == '---'; + $j++); + for ($max_j = $j; + $max_j < $end && substr($diff[$max_j], 0, 3) != '***'; + $max_j++); + } + + // find what hasn't been changed + $array = array(); + while ($i < $max_i && + $j < $max_j && + strcmp($diff[$i], $diff[$j]) == 0) { + $array[] = substr($diff[$i], 2); + $i++; + $j++; + } + + while ($i < $max_i && ($max_j-$j) <= 1) { + if ($diff[$i] != '' && substr($diff[$i], 0, 1) != ' ') { + break; + } + $array[] = substr($diff[$i++], 2); + } + + while ($j < $max_j && ($max_i-$i) <= 1) { + if ($diff[$j] != '' && substr($diff[$j], 0, 1) != ' ') { + break; + } + $array[] = substr($diff[$j++], 2); + } + if (count($array) > 0) { + $edits[] = new Text_Diff_Op_copy($array); + } + + if ($i < $max_i) { + $diff1 = array(); + switch (substr($diff[$i], 0, 1)) { + case '!': + $diff2 = array(); + do { + $diff1[] = substr($diff[$i], 2); + if ($j < $max_j && substr($diff[$j], 0, 1) == '!') { + $diff2[] = substr($diff[$j++], 2); + } + } while (++$i < $max_i && substr($diff[$i], 0, 1) == '!'); + $edits[] = new Text_Diff_Op_change($diff1, $diff2); + break; + + case '+': + do { + $diff1[] = substr($diff[$i], 2); + } while (++$i < $max_i && substr($diff[$i], 0, 1) == '+'); + $edits[] = new Text_Diff_Op_add($diff1); + break; + + case '-': + do { + $diff1[] = substr($diff[$i], 2); + } while (++$i < $max_i && substr($diff[$i], 0, 1) == '-'); + $edits[] = new Text_Diff_Op_delete($diff1); + break; + } + } + + if ($j < $max_j) { + $diff2 = array(); + switch (substr($diff[$j], 0, 1)) { + case '+': + do { + $diff2[] = substr($diff[$j++], 2); + } while ($j < $max_j && substr($diff[$j], 0, 1) == '+'); + $edits[] = new Text_Diff_Op_add($diff2); + break; + + case '-': + do { + $diff2[] = substr($diff[$j++], 2); + } while ($j < $max_j && substr($diff[$j], 0, 1) == '-'); + $edits[] = new Text_Diff_Op_delete($diff2); + break; + } + } + } + + return $edits; + } + +} diff --git a/wp-includes/Text/Diff/Engine/xdiff.php b/wp-includes/Text/Diff/Engine/xdiff.php new file mode 100644 index 0000000..02ce848 --- /dev/null +++ b/wp-includes/Text/Diff/Engine/xdiff.php @@ -0,0 +1,64 @@ +<?php +/** + * Class used internally by Diff to actually compute the diffs. + * + * This class uses the xdiff PECL package (http://pecl.php.net/package/xdiff) + * to compute the differences between the two input arrays. + * + * Copyright 2004-2010 The Horde Project (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you did + * not receive this file, see http://opensource.org/licenses/lgpl-license.php. + * + * @author Jon Parise <jon@horde.org> + * @package Text_Diff + */ +class Text_Diff_Engine_xdiff { + + /** + */ + function diff($from_lines, $to_lines) + { + array_walk($from_lines, array('Text_Diff', 'trimNewlines')); + array_walk($to_lines, array('Text_Diff', 'trimNewlines')); + + /* Convert the two input arrays into strings for xdiff processing. */ + $from_string = implode("\n", $from_lines); + $to_string = implode("\n", $to_lines); + + /* Diff the two strings and convert the result to an array. */ + $diff = xdiff_string_diff($from_string, $to_string, count($to_lines)); + $diff = explode("\n", $diff); + + /* Walk through the diff one line at a time. We build the $edits + * array of diff operations by reading the first character of the + * xdiff output (which is in the "unified diff" format). + * + * Note that we don't have enough information to detect "changed" + * lines using this approach, so we can't add Text_Diff_Op_changed + * instances to the $edits array. The result is still perfectly + * valid, albeit a little less descriptive and efficient. */ + $edits = array(); + foreach ($diff as $line) { + if (!strlen($line)) { + continue; + } + switch ($line[0]) { + case ' ': + $edits[] = new Text_Diff_Op_copy(array(substr($line, 1))); + break; + + case '+': + $edits[] = new Text_Diff_Op_add(array(substr($line, 1))); + break; + + case '-': + $edits[] = new Text_Diff_Op_delete(array(substr($line, 1))); + break; + } + } + + return $edits; + } + +} diff --git a/wp-includes/Text/Diff/Renderer.php b/wp-includes/Text/Diff/Renderer.php new file mode 100644 index 0000000..95c6db4 --- /dev/null +++ b/wp-includes/Text/Diff/Renderer.php @@ -0,0 +1,235 @@ +<?php +/** + * A class to render Diffs in different formats. + * + * This class renders the diff in classic diff format. It is intended that + * this class be customized via inheritance, to obtain fancier outputs. + * + * Copyright 2004-2010 The Horde Project (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you did + * not receive this file, see http://opensource.org/licenses/lgpl-license.php. + * + * @package Text_Diff + */ +class Text_Diff_Renderer { + + /** + * Number of leading context "lines" to preserve. + * + * This should be left at zero for this class, but subclasses may want to + * set this to other values. + */ + var $_leading_context_lines = 0; + + /** + * Number of trailing context "lines" to preserve. + * + * This should be left at zero for this class, but subclasses may want to + * set this to other values. + */ + var $_trailing_context_lines = 0; + + /** + * Constructor. + */ + function Text_Diff_Renderer($params = array()) + { + foreach ($params as $param => $value) { + $v = '_' . $param; + if (isset($this->$v)) { + $this->$v = $value; + } + } + } + + /** + * Get any renderer parameters. + * + * @return array All parameters of this renderer object. + */ + function getParams() + { + $params = array(); + foreach (get_object_vars($this) as $k => $v) { + if ($k[0] == '_') { + $params[substr($k, 1)] = $v; + } + } + + return $params; + } + + /** + * Renders a diff. + * + * @param Text_Diff $diff A Text_Diff object. + * + * @return string The formatted output. + */ + function render($diff) + { + $xi = $yi = 1; + $block = false; + $context = array(); + + $nlead = $this->_leading_context_lines; + $ntrail = $this->_trailing_context_lines; + + $output = $this->_startDiff(); + + $diffs = $diff->getDiff(); + foreach ($diffs as $i => $edit) { + /* If these are unchanged (copied) lines, and we want to keep + * leading or trailing context lines, extract them from the copy + * block. */ + if (is_a($edit, 'Text_Diff_Op_copy')) { + /* Do we have any diff blocks yet? */ + if (is_array($block)) { + /* How many lines to keep as context from the copy + * block. */ + $keep = $i == count($diffs) - 1 ? $ntrail : $nlead + $ntrail; + if (count($edit->orig) <= $keep) { + /* We have less lines in the block than we want for + * context => keep the whole block. */ + $block[] = $edit; + } else { + if ($ntrail) { + /* Create a new block with as many lines as we need + * for the trailing context. */ + $context = array_slice($edit->orig, 0, $ntrail); + $block[] = new Text_Diff_Op_copy($context); + } + /* @todo */ + $output .= $this->_block($x0, $ntrail + $xi - $x0, + $y0, $ntrail + $yi - $y0, + $block); + $block = false; + } + } + /* Keep the copy block as the context for the next block. */ + $context = $edit->orig; + } else { + /* Don't we have any diff blocks yet? */ + if (!is_array($block)) { + /* Extract context lines from the preceding copy block. */ + $context = array_slice($context, count($context) - $nlead); + $x0 = $xi - count($context); + $y0 = $yi - count($context); + $block = array(); + if ($context) { + $block[] = new Text_Diff_Op_copy($context); + } + } + $block[] = $edit; + } + + if ($edit->orig) { + $xi += count($edit->orig); + } + if ($edit->final) { + $yi += count($edit->final); + } + } + + if (is_array($block)) { + $output .= $this->_block($x0, $xi - $x0, + $y0, $yi - $y0, + $block); + } + + return $output . $this->_endDiff(); + } + + function _block($xbeg, $xlen, $ybeg, $ylen, &$edits) + { + $output = $this->_startBlock($this->_blockHeader($xbeg, $xlen, $ybeg, $ylen)); + + foreach ($edits as $edit) { + switch (strtolower(get_class($edit))) { + case 'text_diff_op_copy': + $output .= $this->_context($edit->orig); + break; + + case 'text_diff_op_add': + $output .= $this->_added($edit->final); + break; + + case 'text_diff_op_delete': + $output .= $this->_deleted($edit->orig); + break; + + case 'text_diff_op_change': + $output .= $this->_changed($edit->orig, $edit->final); + break; + } + } + + return $output . $this->_endBlock(); + } + + function _startDiff() + { + return ''; + } + + function _endDiff() + { + return ''; + } + + function _blockHeader($xbeg, $xlen, $ybeg, $ylen) + { + if ($xlen > 1) { + $xbeg .= ',' . ($xbeg + $xlen - 1); + } + if ($ylen > 1) { + $ybeg .= ',' . ($ybeg + $ylen - 1); + } + + // this matches the GNU Diff behaviour + if ($xlen && !$ylen) { + $ybeg--; + } elseif (!$xlen) { + $xbeg--; + } + + return $xbeg . ($xlen ? ($ylen ? 'c' : 'd') : 'a') . $ybeg; + } + + function _startBlock($header) + { + return $header . "\n"; + } + + function _endBlock() + { + return ''; + } + + function _lines($lines, $prefix = ' ') + { + return $prefix . implode("\n$prefix", $lines) . "\n"; + } + + function _context($lines) + { + return $this->_lines($lines, ' '); + } + + function _added($lines) + { + return $this->_lines($lines, '> '); + } + + function _deleted($lines) + { + return $this->_lines($lines, '< '); + } + + function _changed($orig, $final) + { + return $this->_deleted($orig) . "---\n" . $this->_added($final); + } + +} diff --git a/wp-includes/Text/Diff/Renderer/inline.php b/wp-includes/Text/Diff/Renderer/inline.php new file mode 100644 index 0000000..392bd57 --- /dev/null +++ b/wp-includes/Text/Diff/Renderer/inline.php @@ -0,0 +1,206 @@ +<?php +/** + * "Inline" diff renderer. + * + * Copyright 2004-2010 The Horde Project (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (LGPL). If you did + * not receive this file, see http://opensource.org/licenses/lgpl-license.php. + * + * @author Ciprian Popovici + * @package Text_Diff + */ + +/** Text_Diff_Renderer */ + +// WP #7391 +require_once dirname(dirname(__FILE__)) . '/Renderer.php'; + +/** + * "Inline" diff renderer. + * + * This class renders diffs in the Wiki-style "inline" format. + * + * @author Ciprian Popovici + * @package Text_Diff + */ +class Text_Diff_Renderer_inline extends Text_Diff_Renderer { + + /** + * Number of leading context "lines" to preserve. + * + * @var integer + */ + var $_leading_context_lines = 10000; + + /** + * Number of trailing context "lines" to preserve. + * + * @var integer + */ + var $_trailing_context_lines = 10000; + + /** + * Prefix for inserted text. + * + * @var string + */ + var $_ins_prefix = '<ins>'; + + /** + * Suffix for inserted text. + * + * @var string + */ + var $_ins_suffix = '</ins>'; + + /** + * Prefix for deleted text. + * + * @var string + */ + var $_del_prefix = '<del>'; + + /** + * Suffix for deleted text. + * + * @var string + */ + var $_del_suffix = '</del>'; + + /** + * Header for each change block. + * + * @var string + */ + var $_block_header = ''; + + /** + * Whether to split down to character-level. + * + * @var boolean + */ + var $_split_characters = false; + + /** + * What are we currently splitting on? Used to recurse to show word-level + * or character-level changes. + * + * @var string + */ + var $_split_level = 'lines'; + + function _blockHeader($xbeg, $xlen, $ybeg, $ylen) + { + return $this->_block_header; + } + + function _startBlock($header) + { + return $header; + } + + function _lines($lines, $prefix = ' ', $encode = true) + { + if ($encode) { + array_walk($lines, array(&$this, '_encode')); + } + + if ($this->_split_level == 'lines') { + return implode("\n", $lines) . "\n"; + } else { + return implode('', $lines); + } + } + + function _added($lines) + { + array_walk($lines, array(&$this, '_encode')); + $lines[0] = $this->_ins_prefix . $lines[0]; + $lines[count($lines) - 1] .= $this->_ins_suffix; + return $this->_lines($lines, ' ', false); + } + + function _deleted($lines, $words = false) + { + array_walk($lines, array(&$this, '_encode')); + $lines[0] = $this->_del_prefix . $lines[0]; + $lines[count($lines) - 1] .= $this->_del_suffix; + return $this->_lines($lines, ' ', false); + } + + function _changed($orig, $final) + { + /* If we've already split on characters, just display. */ + if ($this->_split_level == 'characters') { + return $this->_deleted($orig) + . $this->_added($final); + } + + /* If we've already split on words, just display. */ + if ($this->_split_level == 'words') { + $prefix = ''; + while ($orig[0] !== false && $final[0] !== false && + substr($orig[0], 0, 1) == ' ' && + substr($final[0], 0, 1) == ' ') { + $prefix .= substr($orig[0], 0, 1); + $orig[0] = substr($orig[0], 1); + $final[0] = substr($final[0], 1); + } + return $prefix . $this->_deleted($orig) . $this->_added($final); + } + + $text1 = implode("\n", $orig); + $text2 = implode("\n", $final); + + /* Non-printing newline marker. */ + $nl = "\0"; + + if ($this->_split_characters) { + $diff = new Text_Diff('native', + array(preg_split('//', $text1), + preg_split('//', $text2))); + } else { + /* We want to split on word boundaries, but we need to preserve + * whitespace as well. Therefore we split on words, but include + * all blocks of whitespace in the wordlist. */ + $diff = new Text_Diff('native', + array($this->_splitOnWords($text1, $nl), + $this->_splitOnWords($text2, $nl))); + } + + /* Get the diff in inline format. */ + $renderer = new Text_Diff_Renderer_inline + (array_merge($this->getParams(), + array('split_level' => $this->_split_characters ? 'characters' : 'words'))); + + /* Run the diff and get the output. */ + return str_replace($nl, "\n", $renderer->render($diff)) . "\n"; + } + + function _splitOnWords($string, $newlineEscape = "\n") + { + // Ignore \0; otherwise the while loop will never finish. + $string = str_replace("\0", '', $string); + + $words = array(); + $length = strlen($string); + $pos = 0; + + while ($pos < $length) { + // Eat a word with any preceding whitespace. + $spaces = strspn(substr($string, $pos), " \n"); + $nextpos = strcspn(substr($string, $pos + $spaces), " \n"); + $words[] = str_replace("\n", $newlineEscape, substr($string, $pos, $spaces + $nextpos)); + $pos += $spaces + $nextpos; + } + + return $words; + } + + function _encode(&$string) + { + $string = htmlspecialchars($string); + } + +} diff --git a/wp-includes/admin-bar.php b/wp-includes/admin-bar.php new file mode 100644 index 0000000..32853c3 --- /dev/null +++ b/wp-includes/admin-bar.php @@ -0,0 +1,920 @@ +<?php +/** + * Admin Bar + * + * This code handles the building and rendering of the press bar. + */ + +/** + * Instantiate the admin bar object and set it up as a global for access elsewhere. + * + * UNHOOKING THIS FUNCTION WILL NOT PROPERLY REMOVE THE ADMIN BAR. + * For that, use show_admin_bar(false) or the 'show_admin_bar' filter. + * + * @since 3.1.0 + * @access private + * @return bool Whether the admin bar was successfully initialized. + */ +function _wp_admin_bar_init() { + global $wp_admin_bar; + + if ( ! is_admin_bar_showing() ) + return false; + + /* Load the admin bar class code ready for instantiation */ + require( ABSPATH . WPINC . '/class-wp-admin-bar.php' ); + + /* Instantiate the admin bar */ + + /** + * Filter the admin bar class to instantiate. + * + * @since 3.1.0 + * + * @param string $wp_admin_bar_class Admin bar class to use. Default 'WP_Admin_Bar'. + */ + $admin_bar_class = apply_filters( 'wp_admin_bar_class', 'WP_Admin_Bar' ); + if ( class_exists( $admin_bar_class ) ) + $wp_admin_bar = new $admin_bar_class; + else + return false; + + $wp_admin_bar->initialize(); + $wp_admin_bar->add_menus(); + + return true; +} +// Don't remove. Wrong way to disable. +add_action( 'template_redirect', '_wp_admin_bar_init', 0 ); +add_action( 'admin_init', '_wp_admin_bar_init' ); + +/** + * Render the admin bar to the page based on the $wp_admin_bar->menu member var. + * This is called very late on the footer actions so that it will render after anything else being + * added to the footer. + * + * It includes the action "admin_bar_menu" which should be used to hook in and + * add new menus to the admin bar. That way you can be sure that you are adding at most optimal point, + * right before the admin bar is rendered. This also gives you access to the $post global, among others. + * + * @since 3.1.0 + */ +function wp_admin_bar_render() { + global $wp_admin_bar; + + if ( ! is_admin_bar_showing() || ! is_object( $wp_admin_bar ) ) + return false; + + /** + * Load all necessary admin bar items. + * + * This is the hook used to add, remove, or manipulate admin bar items. + * + * @since 3.1.0 + * + * @param WP_Admin_Bar $wp_admin_bar WP_Admin_Bar instance, passed by reference + */ + do_action_ref_array( 'admin_bar_menu', array( &$wp_admin_bar ) ); + + /** + * Fires before the admin bar is rendered. + * + * @since 3.1.0 + */ + do_action( 'wp_before_admin_bar_render' ); + + $wp_admin_bar->render(); + + /** + * Fires after the admin bar is rendered. + * + * @since 3.1.0 + */ + do_action( 'wp_after_admin_bar_render' ); +} +add_action( 'wp_footer', 'wp_admin_bar_render', 1000 ); +add_action( 'in_admin_header', 'wp_admin_bar_render', 0 ); + +/** + * Add the WordPress logo menu. + * + * @since 3.3.0 + * + * @param WP_Admin_Bar $wp_admin_bar + */ +function wp_admin_bar_wp_menu( $wp_admin_bar ) { + $wp_admin_bar->add_menu( array( + 'id' => 'wp-logo', + 'title' => '<span class="ab-icon"></span>', + 'href' => self_admin_url( 'about.php' ), + 'meta' => array( + 'title' => __('About WordPress'), + ), + ) ); + + if ( is_user_logged_in() ) { + // Add "About WordPress" link + $wp_admin_bar->add_menu( array( + 'parent' => 'wp-logo', + 'id' => 'about', + 'title' => __('About WordPress'), + 'href' => self_admin_url( 'about.php' ), + ) ); + } + + // Add WordPress.org link + $wp_admin_bar->add_menu( array( + 'parent' => 'wp-logo-external', + 'id' => 'wporg', + 'title' => __('WordPress.org'), + 'href' => __('https://wordpress.org/'), + ) ); + + // Add codex link + $wp_admin_bar->add_menu( array( + 'parent' => 'wp-logo-external', + 'id' => 'documentation', + 'title' => __('Documentation'), + 'href' => __('http://codex.wordpress.org/'), + ) ); + + // Add forums link + $wp_admin_bar->add_menu( array( + 'parent' => 'wp-logo-external', + 'id' => 'support-forums', + 'title' => __('Support Forums'), + 'href' => __('https://wordpress.org/support/'), + ) ); + + // Add feedback link + $wp_admin_bar->add_menu( array( + 'parent' => 'wp-logo-external', + 'id' => 'feedback', + 'title' => __('Feedback'), + 'href' => __('https://wordpress.org/support/forum/requests-and-feedback'), + ) ); +} + +/** + * Add the sidebar toggle button. + * + * @since 3.8.0 + * + * @param WP_Admin_Bar $wp_admin_bar + */ +function wp_admin_bar_sidebar_toggle( $wp_admin_bar ) { + if ( is_admin() ) { + $wp_admin_bar->add_menu( array( + 'id' => 'menu-toggle', + 'title' => '<span class="ab-icon"></span><span class="screen-reader-text">' . __( 'Menu' ) . '</span>', + 'href' => '#', + ) ); + } +} + +/** + * Add the "My Account" item. + * + * @since 3.3.0 + * + * @param WP_Admin_Bar $wp_admin_bar + */ +function wp_admin_bar_my_account_item( $wp_admin_bar ) { + $user_id = get_current_user_id(); + $current_user = wp_get_current_user(); + $profile_url = get_edit_profile_url( $user_id ); + + if ( ! $user_id ) + return; + + $avatar = get_avatar( $user_id, 26 ); + $howdy = sprintf( __('Howdy, %1$s'), $current_user->display_name ); + $class = empty( $avatar ) ? '' : 'with-avatar'; + + $wp_admin_bar->add_menu( array( + 'id' => 'my-account', + 'parent' => 'top-secondary', + 'title' => $howdy . $avatar, + 'href' => $profile_url, + 'meta' => array( + 'class' => $class, + 'title' => __('My Account'), + ), + ) ); +} + +/** + * Add the "My Account" submenu items. + * + * @since 3.1.0 + * + * @param WP_Admin_Bar $wp_admin_bar + */ +function wp_admin_bar_my_account_menu( $wp_admin_bar ) { + $user_id = get_current_user_id(); + $current_user = wp_get_current_user(); + $profile_url = get_edit_profile_url( $user_id ); + + if ( ! $user_id ) + return; + + $wp_admin_bar->add_group( array( + 'parent' => 'my-account', + 'id' => 'user-actions', + ) ); + + $user_info = get_avatar( $user_id, 64 ); + $user_info .= "<span class='display-name'>{$current_user->display_name}</span>"; + + if ( $current_user->display_name !== $current_user->user_login ) + $user_info .= "<span class='username'>{$current_user->user_login}</span>"; + + $wp_admin_bar->add_menu( array( + 'parent' => 'user-actions', + 'id' => 'user-info', + 'title' => $user_info, + 'href' => $profile_url, + 'meta' => array( + 'tabindex' => -1, + ), + ) ); + $wp_admin_bar->add_menu( array( + 'parent' => 'user-actions', + 'id' => 'edit-profile', + 'title' => __( 'Edit My Profile' ), + 'href' => $profile_url, + ) ); + $wp_admin_bar->add_menu( array( + 'parent' => 'user-actions', + 'id' => 'logout', + 'title' => __( 'Log Out' ), + 'href' => wp_logout_url(), + ) ); +} + +/** + * Add the "Site Name" menu. + * + * @since 3.3.0 + * + * @param WP_Admin_Bar $wp_admin_bar + */ +function wp_admin_bar_site_menu( $wp_admin_bar ) { + // Don't show for logged out users. + if ( ! is_user_logged_in() ) + return; + + // Show only when the user is a member of this site, or they're a super admin. + if ( ! is_user_member_of_blog() && ! is_super_admin() ) + return; + + $blogname = get_bloginfo('name'); + + if ( ! $blogname ) { + $blogname = preg_replace( '#^(https?://)?(www.)?#', '', get_home_url() ); + } + + if ( is_network_admin() ) { + $blogname = sprintf( __('Network Admin: %s'), esc_html( get_current_site()->site_name ) ); + } elseif ( is_user_admin() ) { + $blogname = sprintf( __('Global Dashboard: %s'), esc_html( get_current_site()->site_name ) ); + } + + $title = wp_html_excerpt( $blogname, 40, '…' ); + + $wp_admin_bar->add_menu( array( + 'id' => 'site-name', + 'title' => $title, + 'href' => is_admin() ? home_url( '/' ) : admin_url(), + ) ); + + // Create submenu items. + + if ( is_admin() ) { + // Add an option to visit the site. + $wp_admin_bar->add_menu( array( + 'parent' => 'site-name', + 'id' => 'view-site', + 'title' => __( 'Visit Site' ), + 'href' => home_url( '/' ), + ) ); + + if ( is_blog_admin() && is_multisite() && current_user_can( 'manage_sites' ) ) { + $wp_admin_bar->add_menu( array( + 'parent' => 'site-name', + 'id' => 'edit-site', + 'title' => __( 'Edit Site' ), + 'href' => network_admin_url( 'site-info.php?id=' . get_current_blog_id() ), + ) ); + } + + } else { + // We're on the front end, link to the Dashboard. + $wp_admin_bar->add_menu( array( + 'parent' => 'site-name', + 'id' => 'dashboard', + 'title' => __( 'Dashboard' ), + 'href' => admin_url(), + ) ); + + // Add the appearance submenu items. + wp_admin_bar_appearance_menu( $wp_admin_bar ); + } +} + +/** + * Add the "My Sites/[Site Name]" menu and all submenus. + * + * @since 3.1.0 + * + * @param WP_Admin_Bar $wp_admin_bar + */ +function wp_admin_bar_my_sites_menu( $wp_admin_bar ) { + // Don't show for logged out users or single site mode. + if ( ! is_user_logged_in() || ! is_multisite() ) + return; + + // Show only when the user has at least one site, or they're a super admin. + if ( count( $wp_admin_bar->user->blogs ) < 1 && ! is_super_admin() ) + return; + + $wp_admin_bar->add_menu( array( + 'id' => 'my-sites', + 'title' => __( 'My Sites' ), + 'href' => admin_url( 'my-sites.php' ), + ) ); + + if ( is_super_admin() ) { + $wp_admin_bar->add_group( array( + 'parent' => 'my-sites', + 'id' => 'my-sites-super-admin', + ) ); + + $wp_admin_bar->add_menu( array( + 'parent' => 'my-sites-super-admin', + 'id' => 'network-admin', + 'title' => __('Network Admin'), + 'href' => network_admin_url(), + ) ); + + $wp_admin_bar->add_menu( array( + 'parent' => 'network-admin', + 'id' => 'network-admin-d', + 'title' => __( 'Dashboard' ), + 'href' => network_admin_url(), + ) ); + $wp_admin_bar->add_menu( array( + 'parent' => 'network-admin', + 'id' => 'network-admin-s', + 'title' => __( 'Sites' ), + 'href' => network_admin_url( 'sites.php' ), + ) ); + $wp_admin_bar->add_menu( array( + 'parent' => 'network-admin', + 'id' => 'network-admin-u', + 'title' => __( 'Users' ), + 'href' => network_admin_url( 'users.php' ), + ) ); + $wp_admin_bar->add_menu( array( + 'parent' => 'network-admin', + 'id' => 'network-admin-t', + 'title' => __( 'Themes' ), + 'href' => network_admin_url( 'themes.php' ), + ) ); + $wp_admin_bar->add_menu( array( + 'parent' => 'network-admin', + 'id' => 'network-admin-p', + 'title' => __( 'Plugins' ), + 'href' => network_admin_url( 'plugins.php' ), + ) ); + } + + // Add site links + $wp_admin_bar->add_group( array( + 'parent' => 'my-sites', + 'id' => 'my-sites-list', + 'meta' => array( + 'class' => is_super_admin() ? 'ab-sub-secondary' : '', + ), + ) ); + + foreach ( (array) $wp_admin_bar->user->blogs as $blog ) { + switch_to_blog( $blog->userblog_id ); + + $blavatar = '<div class="blavatar"></div>'; + + $blogname = $blog->blogname; + + if ( ! $blogname ) { + $blogname = preg_replace( '#^(https?://)?(www.)?#', '', get_home_url() ); + } + + $menu_id = 'blog-' . $blog->userblog_id; + + $wp_admin_bar->add_menu( array( + 'parent' => 'my-sites-list', + 'id' => $menu_id, + 'title' => $blavatar . $blogname, + 'href' => admin_url(), + ) ); + + $wp_admin_bar->add_menu( array( + 'parent' => $menu_id, + 'id' => $menu_id . '-d', + 'title' => __( 'Dashboard' ), + 'href' => admin_url(), + ) ); + + if ( current_user_can( get_post_type_object( 'post' )->cap->create_posts ) ) { + $wp_admin_bar->add_menu( array( + 'parent' => $menu_id, + 'id' => $menu_id . '-n', + 'title' => __( 'New Post' ), + 'href' => admin_url( 'post-new.php' ), + ) ); + } + + if ( current_user_can( 'edit_posts' ) ) { + $wp_admin_bar->add_menu( array( + 'parent' => $menu_id, + 'id' => $menu_id . '-c', + 'title' => __( 'Manage Comments' ), + 'href' => admin_url( 'edit-comments.php' ), + ) ); + } + + $wp_admin_bar->add_menu( array( + 'parent' => $menu_id, + 'id' => $menu_id . '-v', + 'title' => __( 'Visit Site' ), + 'href' => home_url( '/' ), + ) ); + + restore_current_blog(); + } +} + +/** + * Provide a shortlink. + * + * @since 3.1.0 + * + * @param WP_Admin_Bar $wp_admin_bar + */ +function wp_admin_bar_shortlink_menu( $wp_admin_bar ) { + $short = wp_get_shortlink( 0, 'query' ); + $id = 'get-shortlink'; + + if ( empty( $short ) ) + return; + + $html = '<input class="shortlink-input" type="text" readonly="readonly" value="' . esc_attr( $short ) . '" />'; + + $wp_admin_bar->add_menu( array( + 'id' => $id, + 'title' => __( 'Shortlink' ), + 'href' => $short, + 'meta' => array( 'html' => $html ), + ) ); +} + +/** + * Provide an edit link for posts and terms. + * + * @since 3.1.0 + * + * @param WP_Admin_Bar $wp_admin_bar + */ +function wp_admin_bar_edit_menu( $wp_admin_bar ) { + global $tag, $wp_the_query; + + if ( is_admin() ) { + $current_screen = get_current_screen(); + $post = get_post(); + + if ( 'post' == $current_screen->base + && 'add' != $current_screen->action + && ( $post_type_object = get_post_type_object( $post->post_type ) ) + && current_user_can( 'read_post', $post->ID ) + && ( $post_type_object->public ) + && ( $post_type_object->show_in_admin_bar ) ) + { + if( 'draft' == $post->post_status ) { + $preview_link = set_url_scheme( get_permalink( $post->ID ) ); + /** This filter is documented in wp-admin/includes/meta-boxes.php */ + $preview_link = apply_filters( 'preview_post_link', add_query_arg( 'preview', 'true', $preview_link ), $post ); + $wp_admin_bar->add_menu( array( + 'id' => 'preview', + 'title' => $post_type_object->labels->view_item, + 'href' => esc_url( $preview_link ), + 'meta' => array( 'target' => 'wp-preview-' . $post->ID ), + ) ); + } else { + $wp_admin_bar->add_menu( array( + 'id' => 'view', + 'title' => $post_type_object->labels->view_item, + 'href' => get_permalink( $post->ID ) + ) ); + } + } elseif ( 'edit-tags' == $current_screen->base + && isset( $tag ) && is_object( $tag ) + && ( $tax = get_taxonomy( $tag->taxonomy ) ) + && $tax->public ) + { + $wp_admin_bar->add_menu( array( + 'id' => 'view', + 'title' => $tax->labels->view_item, + 'href' => get_term_link( $tag ) + ) ); + } + } else { + $current_object = $wp_the_query->get_queried_object(); + + if ( empty( $current_object ) ) + return; + + if ( ! empty( $current_object->post_type ) + && ( $post_type_object = get_post_type_object( $current_object->post_type ) ) + && current_user_can( 'edit_post', $current_object->ID ) + && $post_type_object->show_ui && $post_type_object->show_in_admin_bar + && $edit_post_link = get_edit_post_link( $current_object->ID ) ) + { + $wp_admin_bar->add_menu( array( + 'id' => 'edit', + 'title' => $post_type_object->labels->edit_item, + 'href' => $edit_post_link + ) ); + } elseif ( ! empty( $current_object->taxonomy ) + && ( $tax = get_taxonomy( $current_object->taxonomy ) ) + && current_user_can( $tax->cap->edit_terms ) + && $tax->show_ui + && $edit_term_link = get_edit_term_link( $current_object->term_id, $current_object->taxonomy ) ) + { + $wp_admin_bar->add_menu( array( + 'id' => 'edit', + 'title' => $tax->labels->edit_item, + 'href' => $edit_term_link + ) ); + } + } +} + +/** + * Add "Add New" menu. + * + * @since 3.1.0 + * + * @param WP_Admin_Bar $wp_admin_bar + */ +function wp_admin_bar_new_content_menu( $wp_admin_bar ) { + $actions = array(); + + $cpts = (array) get_post_types( array( 'show_in_admin_bar' => true ), 'objects' ); + + if ( isset( $cpts['post'] ) && current_user_can( $cpts['post']->cap->create_posts ) ) + $actions[ 'post-new.php' ] = array( $cpts['post']->labels->name_admin_bar, 'new-post' ); + + if ( isset( $cpts['attachment'] ) && current_user_can( 'upload_files' ) ) + $actions[ 'media-new.php' ] = array( $cpts['attachment']->labels->name_admin_bar, 'new-media' ); + + if ( current_user_can( 'manage_links' ) ) + $actions[ 'link-add.php' ] = array( _x( 'Link', 'add new from admin bar' ), 'new-link' ); + + if ( isset( $cpts['page'] ) && current_user_can( $cpts['page']->cap->create_posts ) ) + $actions[ 'post-new.php?post_type=page' ] = array( $cpts['page']->labels->name_admin_bar, 'new-page' ); + + unset( $cpts['post'], $cpts['page'], $cpts['attachment'] ); + + // Add any additional custom post types. + foreach ( $cpts as $cpt ) { + if ( ! current_user_can( $cpt->cap->create_posts ) ) + continue; + + $key = 'post-new.php?post_type=' . $cpt->name; + $actions[ $key ] = array( $cpt->labels->name_admin_bar, 'new-' . $cpt->name ); + } + // Avoid clash with parent node and a 'content' post type. + if ( isset( $actions['post-new.php?post_type=content'] ) ) + $actions['post-new.php?post_type=content'][1] = 'add-new-content'; + + if ( current_user_can( 'create_users' ) || current_user_can( 'promote_users' ) ) + $actions[ 'user-new.php' ] = array( _x( 'User', 'add new from admin bar' ), 'new-user' ); + + if ( ! $actions ) + return; + + $title = '<span class="ab-icon"></span><span class="ab-label">' . _x( 'New', 'admin bar menu group label' ) . '</span>'; + + $wp_admin_bar->add_menu( array( + 'id' => 'new-content', + 'title' => $title, + 'href' => admin_url( current( array_keys( $actions ) ) ), + 'meta' => array( + 'title' => _x( 'Add New', 'admin bar menu group label' ), + ), + ) ); + + foreach ( $actions as $link => $action ) { + list( $title, $id ) = $action; + + $wp_admin_bar->add_menu( array( + 'parent' => 'new-content', + 'id' => $id, + 'title' => $title, + 'href' => admin_url( $link ) + ) ); + } +} + +/** + * Add edit comments link with awaiting moderation count bubble. + * + * @since 3.1.0 + * + * @param WP_Admin_Bar $wp_admin_bar + */ +function wp_admin_bar_comments_menu( $wp_admin_bar ) { + if ( !current_user_can('edit_posts') ) + return; + + $awaiting_mod = wp_count_comments(); + $awaiting_mod = $awaiting_mod->moderated; + $awaiting_title = esc_attr( sprintf( _n( '%s comment awaiting moderation', '%s comments awaiting moderation', $awaiting_mod ), number_format_i18n( $awaiting_mod ) ) ); + + $icon = '<span class="ab-icon"></span>'; + $title = '<span id="ab-awaiting-mod" class="ab-label awaiting-mod pending-count count-' . $awaiting_mod . '">' . number_format_i18n( $awaiting_mod ) . '</span>'; + + $wp_admin_bar->add_menu( array( + 'id' => 'comments', + 'title' => $icon . $title, + 'href' => admin_url('edit-comments.php'), + 'meta' => array( 'title' => $awaiting_title ), + ) ); +} + +/** + * Add appearance submenu items to the "Site Name" menu. + * + * @since 3.1.0 + * + * @param WP_Admin_Bar $wp_admin_bar + */ +function wp_admin_bar_appearance_menu( $wp_admin_bar ) { + $wp_admin_bar->add_group( array( 'parent' => 'site-name', 'id' => 'appearance' ) ); + + if ( current_user_can( 'switch_themes' ) || current_user_can( 'edit_theme_options' ) ) + $wp_admin_bar->add_menu( array( 'parent' => 'appearance', 'id' => 'themes', 'title' => __('Themes'), 'href' => admin_url('themes.php') ) ); + + if ( ! current_user_can( 'edit_theme_options' ) ) + return; + + $current_url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; + $customize_url = add_query_arg( 'url', urlencode( $current_url ), wp_customize_url() ); + if ( current_user_can( 'customize' ) ) { + $wp_admin_bar->add_menu( array( + 'parent' => 'appearance', + 'id' => 'customize', + 'title' => __('Customize'), + 'href' => $customize_url, + 'meta' => array( + 'class' => 'hide-if-no-customize', + ), + ) ); + add_action( 'wp_before_admin_bar_render', 'wp_customize_support_script' ); + } + + if ( current_theme_supports( 'widgets' ) ) + $wp_admin_bar->add_menu( array( 'parent' => 'appearance', 'id' => 'widgets', 'title' => __('Widgets'), 'href' => admin_url('widgets.php') ) ); + + if ( current_theme_supports( 'menus' ) || current_theme_supports( 'widgets' ) ) + $wp_admin_bar->add_menu( array( 'parent' => 'appearance', 'id' => 'menus', 'title' => __('Menus'), 'href' => admin_url('nav-menus.php') ) ); + + if ( current_theme_supports( 'custom-background' ) ) { + $wp_admin_bar->add_menu( array( + 'parent' => 'appearance', + 'id' => 'background', + 'title' => __( 'Background' ), + 'href' => admin_url( 'themes.php?page=custom-background' ), + 'meta' => array( + 'class' => 'hide-if-customize', + ), + ) ); + + if ( current_user_can( 'customize' ) ) { + $wp_admin_bar->add_menu( array( + 'parent' => 'appearance', + 'id' => 'customize-background', + 'title' => __( 'Background' ), + 'href' => add_query_arg( urlencode( 'autofocus[control]' ), 'background_image', $customize_url ), // urlencode() needed due to #16859 + 'meta' => array( + 'class' => 'hide-if-no-customize', + ), + ) ); + } + } + + if ( current_theme_supports( 'custom-header' ) ) { + $wp_admin_bar->add_menu( array( + 'parent' => 'appearance', + 'id' => 'header', + 'title' => __( 'Header' ), + 'href' => admin_url( 'themes.php?page=custom-header' ), + 'meta' => array( + 'class' => 'hide-if-customize', + ), + ) ); + + if ( current_user_can( 'customize' ) ) { + $wp_admin_bar->add_menu( array( + 'parent' => 'appearance', + 'id' => 'customize-header', + 'title' => __( 'Header' ), + 'href' => add_query_arg( urlencode( 'autofocus[control]' ), 'header_image', $customize_url ), // urlencode() needed due to #16859 + 'meta' => array( + 'class' => 'hide-if-no-customize', + ), + ) ); + } + } + +} + +/** + * Provide an update link if theme/plugin/core updates are available. + * + * @since 3.1.0 + * + * @param WP_Admin_Bar $wp_admin_bar + */ +function wp_admin_bar_updates_menu( $wp_admin_bar ) { + + $update_data = wp_get_update_data(); + + if ( !$update_data['counts']['total'] ) + return; + + $title = '<span class="ab-icon"></span><span class="ab-label">' . number_format_i18n( $update_data['counts']['total'] ) . '</span>'; + $title .= '<span class="screen-reader-text">' . $update_data['title'] . '</span>'; + + $wp_admin_bar->add_menu( array( + 'id' => 'updates', + 'title' => $title, + 'href' => network_admin_url( 'update-core.php' ), + 'meta' => array( + 'title' => $update_data['title'], + ), + ) ); +} + +/** + * Add search form. + * + * @since 3.3.0 + * + * @param WP_Admin_Bar $wp_admin_bar + */ +function wp_admin_bar_search_menu( $wp_admin_bar ) { + if ( is_admin() ) + return; + + $form = '<form action="' . esc_url( home_url( '/' ) ) . '" method="get" id="adminbarsearch">'; + $form .= '<input class="adminbar-input" name="s" id="adminbar-search" type="text" value="" maxlength="150" />'; + $form .= '<input type="submit" class="adminbar-button" value="' . __('Search') . '"/>'; + $form .= '</form>'; + + $wp_admin_bar->add_menu( array( + 'parent' => 'top-secondary', + 'id' => 'search', + 'title' => $form, + 'meta' => array( + 'class' => 'admin-bar-search', + 'tabindex' => -1, + ) + ) ); +} + +/** + * Add secondary menus. + * + * @since 3.3.0 + * + * @param WP_Admin_Bar $wp_admin_bar + */ +function wp_admin_bar_add_secondary_groups( $wp_admin_bar ) { + $wp_admin_bar->add_group( array( + 'id' => 'top-secondary', + 'meta' => array( + 'class' => 'ab-top-secondary', + ), + ) ); + + $wp_admin_bar->add_group( array( + 'parent' => 'wp-logo', + 'id' => 'wp-logo-external', + 'meta' => array( + 'class' => 'ab-sub-secondary', + ), + ) ); +} + +/** + * Style and scripts for the admin bar. + * + * @since 3.1.0 + */ +function wp_admin_bar_header() { ?> +<style type="text/css" media="print">#wpadminbar { display:none; }</style> +<?php +} + +/** + * Default admin bar callback. + * + * @since 3.1.0 + */ +function _admin_bar_bump_cb() { ?> +<style type="text/css" media="screen"> + html { margin-top: 32px !important; } + * html body { margin-top: 32px !important; } + @media screen and ( max-width: 782px ) { + html { margin-top: 46px !important; } + * html body { margin-top: 46px !important; } + } +</style> +<?php +} + +/** + * Set the display status of the admin bar. + * + * This can be called immediately upon plugin load. It does not need to be called from a function hooked to the init action. + * + * @since 3.1.0 + * + * @param bool $show Whether to allow the admin bar to show. + * @return void + */ +function show_admin_bar( $show ) { + global $show_admin_bar; + $show_admin_bar = (bool) $show; +} + +/** + * Determine whether the admin bar should be showing. + * + * @since 3.1.0 + * + * @return bool Whether the admin bar should be showing. + */ +function is_admin_bar_showing() { + global $show_admin_bar, $pagenow; + + // For all these types of requests, we never want an admin bar. + if ( defined('XMLRPC_REQUEST') || defined('DOING_AJAX') || defined('IFRAME_REQUEST') ) + return false; + + // Integrated into the admin. + if ( is_admin() ) + return true; + + if ( ! isset( $show_admin_bar ) ) { + if ( ! is_user_logged_in() || 'wp-login.php' == $pagenow ) { + $show_admin_bar = false; + } else { + $show_admin_bar = _get_admin_bar_pref(); + } + } + + /** + * Filter whether to show the admin bar. + * + * Returning false to this hook is the recommended way to hide the admin bar. + * The user's display preference is used for logged in users. + * + * @since 3.1.0 + * + * @param bool $show_admin_bar Whether the admin bar should be shown. Default false. + */ + $show_admin_bar = apply_filters( 'show_admin_bar', $show_admin_bar ); + + return $show_admin_bar; +} + +/** + * Retrieve the admin bar display preference of a user. + * + * @since 3.1.0 + * @access private + * + * @param string $context Context of this preference check. Defaults to 'front'. The 'admin' + * preference is no longer used. + * @param int $user Optional. ID of the user to check, defaults to 0 for current user. + * @return bool Whether the admin bar should be showing for this user. + */ +function _get_admin_bar_pref( $context = 'front', $user = 0 ) { + $pref = get_user_option( "show_admin_bar_{$context}", $user ); + if ( false === $pref ) + return true; + + return 'true' === $pref; +} diff --git a/wp-includes/atomlib.php b/wp-includes/atomlib.php new file mode 100644 index 0000000..9d34276 --- /dev/null +++ b/wp-includes/atomlib.php @@ -0,0 +1,352 @@ +<?php +/** + * Atom Syndication Format PHP Library + * + * @package AtomLib + * @link http://code.google.com/p/phpatomlib/ + * + * @author Elias Torres <elias@torrez.us> + * @version 0.4 + * @since 2.3.0 + */ + +/** + * Structure that store common Atom Feed Properties + * + * @package AtomLib + */ +class AtomFeed { + /** + * Stores Links + * @var array + * @access public + */ + var $links = array(); + /** + * Stores Categories + * @var array + * @access public + */ + var $categories = array(); + /** + * Stores Entries + * + * @var array + * @access public + */ + var $entries = array(); +} + +/** + * Structure that store Atom Entry Properties + * + * @package AtomLib + */ +class AtomEntry { + /** + * Stores Links + * @var array + * @access public + */ + var $links = array(); + /** + * Stores Categories + * @var array + * @access public + */ + var $categories = array(); +} + +/** + * AtomLib Atom Parser API + * + * @package AtomLib + */ +class AtomParser { + + var $NS = 'http://www.w3.org/2005/Atom'; + var $ATOM_CONTENT_ELEMENTS = array('content','summary','title','subtitle','rights'); + var $ATOM_SIMPLE_ELEMENTS = array('id','updated','published','draft'); + + var $debug = false; + + var $depth = 0; + var $indent = 2; + var $in_content; + var $ns_contexts = array(); + var $ns_decls = array(); + var $content_ns_decls = array(); + var $content_ns_contexts = array(); + var $is_xhtml = false; + var $is_html = false; + var $is_text = true; + var $skipped_div = false; + + var $FILE = "php://input"; + + var $feed; + var $current; + + function AtomParser() { + + $this->feed = new AtomFeed(); + $this->current = null; + $this->map_attrs_func = create_function('$k,$v', 'return "$k=\"$v\"";'); + $this->map_xmlns_func = create_function('$p,$n', '$xd = "xmlns"; if(strlen($n[0])>0) $xd .= ":{$n[0]}"; return "{$xd}=\"{$n[1]}\"";'); + } + + function _p($msg) { + if($this->debug) { + print str_repeat(" ", $this->depth * $this->indent) . $msg ."\n"; + } + } + + function error_handler($log_level, $log_text, $error_file, $error_line) { + $this->error = $log_text; + } + + function parse() { + + set_error_handler(array(&$this, 'error_handler')); + + array_unshift($this->ns_contexts, array()); + + $parser = xml_parser_create_ns(); + xml_set_object($parser, $this); + xml_set_element_handler($parser, "start_element", "end_element"); + xml_parser_set_option($parser,XML_OPTION_CASE_FOLDING,0); + xml_parser_set_option($parser,XML_OPTION_SKIP_WHITE,0); + xml_set_character_data_handler($parser, "cdata"); + xml_set_default_handler($parser, "_default"); + xml_set_start_namespace_decl_handler($parser, "start_ns"); + xml_set_end_namespace_decl_handler($parser, "end_ns"); + + $this->content = ''; + + $ret = true; + + $fp = fopen($this->FILE, "r"); + while ($data = fread($fp, 4096)) { + if($this->debug) $this->content .= $data; + + if(!xml_parse($parser, $data, feof($fp))) { + trigger_error(sprintf(__('XML error: %s at line %d')."\n", + xml_error_string(xml_get_error_code($parser)), + xml_get_current_line_number($parser))); + $ret = false; + break; + } + } + fclose($fp); + + xml_parser_free($parser); + + restore_error_handler(); + + return $ret; + } + + function start_element($parser, $name, $attrs) { + + $tag = array_pop(split(":", $name)); + + switch($name) { + case $this->NS . ':feed': + $this->current = $this->feed; + break; + case $this->NS . ':entry': + $this->current = new AtomEntry(); + break; + }; + + $this->_p("start_element('$name')"); + #$this->_p(print_r($this->ns_contexts,true)); + #$this->_p('current(' . $this->current . ')'); + + array_unshift($this->ns_contexts, $this->ns_decls); + + $this->depth++; + + if(!empty($this->in_content)) { + + $this->content_ns_decls = array(); + + if($this->is_html || $this->is_text) + trigger_error("Invalid content in element found. Content must not be of type text or html if it contains markup."); + + $attrs_prefix = array(); + + // resolve prefixes for attributes + foreach($attrs as $key => $value) { + $with_prefix = $this->ns_to_prefix($key, true); + $attrs_prefix[$with_prefix[1]] = $this->xml_escape($value); + } + + $attrs_str = join(' ', array_map($this->map_attrs_func, array_keys($attrs_prefix), array_values($attrs_prefix))); + if(strlen($attrs_str) > 0) { + $attrs_str = " " . $attrs_str; + } + + $with_prefix = $this->ns_to_prefix($name); + + if(!$this->is_declared_content_ns($with_prefix[0])) { + array_push($this->content_ns_decls, $with_prefix[0]); + } + + $xmlns_str = ''; + if(count($this->content_ns_decls) > 0) { + array_unshift($this->content_ns_contexts, $this->content_ns_decls); + $xmlns_str .= join(' ', array_map($this->map_xmlns_func, array_keys($this->content_ns_contexts[0]), array_values($this->content_ns_contexts[0]))); + if(strlen($xmlns_str) > 0) { + $xmlns_str = " " . $xmlns_str; + } + } + + array_push($this->in_content, array($tag, $this->depth, "<". $with_prefix[1] ."{$xmlns_str}{$attrs_str}" . ">")); + + } else if(in_array($tag, $this->ATOM_CONTENT_ELEMENTS) || in_array($tag, $this->ATOM_SIMPLE_ELEMENTS)) { + $this->in_content = array(); + $this->is_xhtml = $attrs['type'] == 'xhtml'; + $this->is_html = $attrs['type'] == 'html' || $attrs['type'] == 'text/html'; + $this->is_text = !in_array('type',array_keys($attrs)) || $attrs['type'] == 'text'; + $type = $this->is_xhtml ? 'XHTML' : ($this->is_html ? 'HTML' : ($this->is_text ? 'TEXT' : $attrs['type'])); + + if(in_array('src',array_keys($attrs))) { + $this->current->$tag = $attrs; + } else { + array_push($this->in_content, array($tag,$this->depth, $type)); + } + } else if($tag == 'link') { + array_push($this->current->links, $attrs); + } else if($tag == 'category') { + array_push($this->current->categories, $attrs); + } + + $this->ns_decls = array(); + } + + function end_element($parser, $name) { + + $tag = array_pop(split(":", $name)); + + $ccount = count($this->in_content); + + # if we are *in* content, then let's proceed to serialize it + if(!empty($this->in_content)) { + # if we are ending the original content element + # then let's finalize the content + if($this->in_content[0][0] == $tag && + $this->in_content[0][1] == $this->depth) { + $origtype = $this->in_content[0][2]; + array_shift($this->in_content); + $newcontent = array(); + foreach($this->in_content as $c) { + if(count($c) == 3) { + array_push($newcontent, $c[2]); + } else { + if($this->is_xhtml || $this->is_text) { + array_push($newcontent, $this->xml_escape($c)); + } else { + array_push($newcontent, $c); + } + } + } + if(in_array($tag, $this->ATOM_CONTENT_ELEMENTS)) { + $this->current->$tag = array($origtype, join('',$newcontent)); + } else { + $this->current->$tag = join('',$newcontent); + } + $this->in_content = array(); + } else if($this->in_content[$ccount-1][0] == $tag && + $this->in_content[$ccount-1][1] == $this->depth) { + $this->in_content[$ccount-1][2] = substr($this->in_content[$ccount-1][2],0,-1) . "/>"; + } else { + # else, just finalize the current element's content + $endtag = $this->ns_to_prefix($name); + array_push($this->in_content, array($tag, $this->depth, "</$endtag[1]>")); + } + } + + array_shift($this->ns_contexts); + + $this->depth--; + + if($name == ($this->NS . ':entry')) { + array_push($this->feed->entries, $this->current); + $this->current = null; + } + + $this->_p("end_element('$name')"); + } + + function start_ns($parser, $prefix, $uri) { + $this->_p("starting: " . $prefix . ":" . $uri); + array_push($this->ns_decls, array($prefix,$uri)); + } + + function end_ns($parser, $prefix) { + $this->_p("ending: #" . $prefix . "#"); + } + + function cdata($parser, $data) { + $this->_p("data: #" . str_replace(array("\n"), array("\\n"), trim($data)) . "#"); + if(!empty($this->in_content)) { + array_push($this->in_content, $data); + } + } + + function _default($parser, $data) { + # when does this gets called? + } + + + function ns_to_prefix($qname, $attr=false) { + # split 'http://www.w3.org/1999/xhtml:div' into ('http','//www.w3.org/1999/xhtml','div') + $components = split(":", $qname); + + # grab the last one (e.g 'div') + $name = array_pop($components); + + if(!empty($components)) { + # re-join back the namespace component + $ns = join(":",$components); + foreach($this->ns_contexts as $context) { + foreach($context as $mapping) { + if($mapping[1] == $ns && strlen($mapping[0]) > 0) { + return array($mapping, "$mapping[0]:$name"); + } + } + } + } + + if($attr) { + return array(null, $name); + } else { + foreach($this->ns_contexts as $context) { + foreach($context as $mapping) { + if(strlen($mapping[0]) == 0) { + return array($mapping, $name); + } + } + } + } + } + + function is_declared_content_ns($new_mapping) { + foreach($this->content_ns_contexts as $context) { + foreach($context as $mapping) { + if($new_mapping == $mapping) { + return true; + } + } + } + return false; + } + + function xml_escape($string) + { + return str_replace(array('&','"',"'",'<','>'), + array('&','"',''','<','>'), + $string ); + } +} diff --git a/wp-includes/author-template.php b/wp-includes/author-template.php new file mode 100644 index 0000000..2280487 --- /dev/null +++ b/wp-includes/author-template.php @@ -0,0 +1,454 @@ +<?php +/** + * Author Template functions for use in themes. + * + * These functions must be used within the WordPress Loop. + * + * @link http://codex.wordpress.org/Author_Templates + * + * @package WordPress + * @subpackage Template + */ + +/** + * Retrieve the author of the current post. + * + * @since 1.5.0 + * + * @uses $authordata The current author's DB object. + * + * @param string $deprecated Deprecated. + * @return string The author's display name. + */ +function get_the_author($deprecated = '') { + global $authordata; + + if ( !empty( $deprecated ) ) + _deprecated_argument( __FUNCTION__, '2.1' ); + + /** + * Filter the display name of the current post's author. + * + * @since 2.9.0 + * + * @param string $authordata->display_name The author's display name. + */ + return apply_filters('the_author', is_object($authordata) ? $authordata->display_name : null); +} + +/** + * Display the name of the author of the current post. + * + * The behavior of this function is based off of old functionality predating + * get_the_author(). This function is not deprecated, but is designed to echo + * the value from get_the_author() and as an result of any old theme that might + * still use the old behavior will also pass the value from get_the_author(). + * + * The normal, expected behavior of this function is to echo the author and not + * return it. However, backwards compatibility has to be maintained. + * + * @since 0.71 + * @see get_the_author() + * @link http://codex.wordpress.org/Template_Tags/the_author + * + * @param string $deprecated Deprecated. + * @param string $deprecated_echo Deprecated. Use get_the_author(). Echo the string or return it. + * @return string The author's display name, from get_the_author(). + */ +function the_author( $deprecated = '', $deprecated_echo = true ) { + if ( !empty( $deprecated ) ) + _deprecated_argument( __FUNCTION__, '2.1' ); + if ( $deprecated_echo !== true ) + _deprecated_argument( __FUNCTION__, '1.5', __('Use <code>get_the_author()</code> instead if you do not want the value echoed.') ); + if ( $deprecated_echo ) + echo get_the_author(); + return get_the_author(); +} + +/** + * Retrieve the author who last edited the current post. + * + * @since 2.8.0 + * + * @return string The author's display name. + */ +function get_the_modified_author() { + if ( $last_id = get_post_meta( get_post()->ID, '_edit_last', true) ) { + $last_user = get_userdata($last_id); + + /** + * Filter the display name of the author who last edited the current post. + * + * @since 2.8.0 + * + * @param string $last_user->display_name The author's display name. + */ + return apply_filters('the_modified_author', $last_user->display_name); + } +} + +/** + * Display the name of the author who last edited the current post. + * + * @since 2.8.0 + * + * @see get_the_author() + * @return string The author's display name, from get_the_modified_author(). + */ +function the_modified_author() { + echo get_the_modified_author(); +} + +/** + * Retrieve the requested data of the author of the current post. + * @link http://codex.wordpress.org/Template_Tags/the_author_meta + * @since 2.8.0 + * @param string $field selects the field of the users record. + * @param int $user_id Optional. User ID. + * @return string The author's field from the current author's DB object. + */ +function get_the_author_meta( $field = '', $user_id = false ) { + if ( ! $user_id ) { + global $authordata; + $user_id = isset( $authordata->ID ) ? $authordata->ID : 0; + } else { + $authordata = get_userdata( $user_id ); + } + + if ( in_array( $field, array( 'login', 'pass', 'nicename', 'email', 'url', 'registered', 'activation_key', 'status' ) ) ) + $field = 'user_' . $field; + + $value = isset( $authordata->$field ) ? $authordata->$field : ''; + + /** + * Filter the value of the requested user metadata. + * + * The filter name is dynamic and depends on the $field parameter of the function. + * + * @since 2.8.0 + * + * @param string $value The value of the metadata. + * @param int $user_id The user ID. + */ + return apply_filters( 'get_the_author_' . $field, $value, $user_id ); +} + +/** + * Retrieve the requested data of the author of the current post. + * @link http://codex.wordpress.org/Template_Tags/the_author_meta + * @since 2.8.0 + * @param string $field selects the field of the users record. + * @param int $user_id Optional. User ID. + * @echo string The author's field from the current author's DB object. + */ +function the_author_meta( $field = '', $user_id = false ) { + $author_meta = get_the_author_meta( $field, $user_id ); + + /** + * The value of the requested user metadata. + * + * The filter name is dynamic and depends on the $field parameter of the function. + * + * @since 2.8.0 + * + * @param string $author_meta The value of the metadata. + * @param int $user_id The user ID. + */ + echo apply_filters( 'the_author_' . $field, $author_meta, $user_id ); +} + +/** + * Retrieve either author's link or author's name. + * + * If the author has a home page set, return an HTML link, otherwise just return the + * author's name. + */ +function get_the_author_link() { + if ( get_the_author_meta('url') ) { + return '<a href="' . esc_url( get_the_author_meta('url') ) . '" title="' . esc_attr( sprintf(__("Visit %s’s website"), get_the_author()) ) . '" rel="author external">' . get_the_author() . '</a>'; + } else { + return get_the_author(); + } +} + +/** + * Display either author's link or author's name. + * + * If the author has a home page set, echo an HTML link, otherwise just echo the + * author's name. + * + * @link http://codex.wordpress.org/Template_Tags/the_author_link + * + * @since 2.1.0 + */ +function the_author_link() { + echo get_the_author_link(); +} + +/** + * Retrieve the number of posts by the author of the current post. + * + * @since 1.5.0 + * + * @return int The number of posts by the author. + */ +function get_the_author_posts() { + $post = get_post(); + if ( ! $post ) { + return 0; + } + return count_user_posts( $post->post_author ); +} + +/** + * Display the number of posts by the author of the current post. + * + * @link http://codex.wordpress.org/Template_Tags/the_author_posts + * @since 0.71 + */ +function the_author_posts() { + echo get_the_author_posts(); +} + +/** + * Display an HTML link to the author page of the author of the current post. + * + * Does just echo get_author_posts_url() function, like the others do. The + * reason for this, is that another function is used to help in printing the + * link to the author's posts. + * + * @link http://codex.wordpress.org/Template_Tags/the_author_posts_link + * @since 1.2.0 + * @param string $deprecated Deprecated. + */ +function the_author_posts_link($deprecated = '') { + if ( !empty( $deprecated ) ) + _deprecated_argument( __FUNCTION__, '2.1' ); + + global $authordata; + if ( !is_object( $authordata ) ) + return false; + $link = sprintf( + '<a href="%1$s" title="%2$s" rel="author">%3$s</a>', + esc_url( get_author_posts_url( $authordata->ID, $authordata->user_nicename ) ), + esc_attr( sprintf( __( 'Posts by %s' ), get_the_author() ) ), + get_the_author() + ); + + /** + * Filter the link to the author page of the author of the current post. + * + * @since 2.9.0 + * + * @param string $link HTML link. + */ + echo apply_filters( 'the_author_posts_link', $link ); +} + +/** + * Retrieve the URL to the author page for the user with the ID provided. + * + * @since 2.1.0 + * @uses $wp_rewrite WP_Rewrite + * @return string The URL to the author's page. + */ +function get_author_posts_url($author_id, $author_nicename = '') { + global $wp_rewrite; + $auth_ID = (int) $author_id; + $link = $wp_rewrite->get_author_permastruct(); + + if ( empty($link) ) { + $file = home_url( '/' ); + $link = $file . '?author=' . $auth_ID; + } else { + if ( '' == $author_nicename ) { + $user = get_userdata($author_id); + if ( !empty($user->user_nicename) ) + $author_nicename = $user->user_nicename; + } + $link = str_replace('%author%', $author_nicename, $link); + $link = home_url( user_trailingslashit( $link ) ); + } + + /** + * Filter the URL to the author's page. + * + * @since 2.1.0 + * + * @param string $link The URL to the author's page. + * @param int $author_id The author's id. + * @param string $author_nicename The author's nice name. + */ + $link = apply_filters( 'author_link', $link, $author_id, $author_nicename ); + + return $link; +} + +/** + * List all the authors of the blog, with several options available. + * + * @link http://codex.wordpress.org/Template_Tags/wp_list_authors + * + * @since 1.2.0 + * + * @param string|array $args { + * Optional. Array or string of default arguments. + * + * @type string $orderby How to sort the authors. Accepts 'nicename', 'email', 'url', 'registered', + * 'user_nicename', 'user_email', 'user_url', 'user_registered', 'name', + * 'display_name', 'post_count', 'ID', 'meta_value', 'user_login'. Default 'name'. + * @type string $order Sorting direction for $orderby. Accepts 'ASC', 'DESC'. Default 'ASC'. + * @type int $number Maximum authors to return or display. Default empty (all authors). + * @type bool $optioncount Show the count in parenthesis next to the author's name. Default false. + * @type bool $exclude_admin Whether to exclude the 'admin' account, if it exists. Default false. + * @type bool $show_fullname Whether to show the author's full name. Default false. + * @type bool $hide_empty Whether to hide any authors with no posts. Default true. + * @type string $feed If not empty, show a link to the author's feed and use this text as the alt + * parameter of the link. Default empty. + * @type string $feed_image If not empty, show a link to the author's feed and use this image URL as + * clickable anchor. Default empty. + * @type string $feed_type The feed type to link to, such as 'rss2'. Defaults to default feed type. + * @type bool $echo Whether to output the result or instead return it. Default true. + * @type string $style If 'list', each author is wrapped in an `<li>` element, otherwise the authors + * will be separated by commas. + * @type bool $html Whether to list the items in HTML form or plaintext. Default true. + * @type string $exclude An array, comma-, or space-separated list of author IDs to exclude. Default empty. + * @type string $exclude An array, comma-, or space-separated list of author IDs to include. Default empty. + * } + * @return null|string The output, if echo is set to false. Otherwise null. + */ +function wp_list_authors( $args = '' ) { + global $wpdb; + + $defaults = array( + 'orderby' => 'name', 'order' => 'ASC', 'number' => '', + 'optioncount' => false, 'exclude_admin' => true, + 'show_fullname' => false, 'hide_empty' => true, + 'feed' => '', 'feed_image' => '', 'feed_type' => '', 'echo' => true, + 'style' => 'list', 'html' => true, 'exclude' => '', 'include' => '' + ); + + $args = wp_parse_args( $args, $defaults ); + + $return = ''; + + $query_args = wp_array_slice_assoc( $args, array( 'orderby', 'order', 'number', 'exclude', 'include' ) ); + $query_args['fields'] = 'ids'; + $authors = get_users( $query_args ); + + $author_count = array(); + foreach ( (array) $wpdb->get_results( "SELECT DISTINCT post_author, COUNT(ID) AS count FROM $wpdb->posts WHERE post_type = 'post' AND " . get_private_posts_cap_sql( 'post' ) . " GROUP BY post_author" ) as $row ) { + $author_count[$row->post_author] = $row->count; + } + foreach ( $authors as $author_id ) { + $author = get_userdata( $author_id ); + + if ( $args['exclude_admin'] && 'admin' == $author->display_name ) { + continue; + } + + $posts = isset( $author_count[$author->ID] ) ? $author_count[$author->ID] : 0; + + if ( ! $posts && $args['hide_empty'] ) { + continue; + } + + if ( $args['show_fullname'] && $author->first_name && $author->last_name ) { + $name = "$author->first_name $author->last_name"; + } else { + $name = $author->display_name; + } + + if ( ! $args['html'] ) { + $return .= $name . ', '; + + continue; // No need to go further to process HTML. + } + + if ( 'list' == $args['style'] ) { + $return .= '<li>'; + } + + $link = '<a href="' . get_author_posts_url( $author->ID, $author->user_nicename ) . '" title="' . esc_attr( sprintf(__("Posts by %s"), $author->display_name) ) . '">' . $name . '</a>'; + + if ( ! empty( $args['feed_image'] ) || ! empty( $args['feed'] ) ) { + $link .= ' '; + if ( empty( $args['feed_image'] ) ) { + $link .= '('; + } + + $link .= '<a href="' . get_author_feed_link( $author->ID, $args['feed_type'] ) . '"'; + + $alt = ''; + if ( ! empty( $args['feed'] ) ) { + $alt = ' alt="' . esc_attr( $args['feed'] ) . '"'; + $name = $args['feed']; + } + + $link .= '>'; + + if ( ! empty( $args['feed_image'] ) ) { + $link .= '<img src="' . esc_url( $args['feed_image'] ) . '" style="border: none;"' . $alt . ' />'; + } else { + $link .= $name; + } + + $link .= '</a>'; + + if ( empty( $args['feed_image'] ) ) { + $link .= ')'; + } + } + + if ( $args['optioncount'] ) { + $link .= ' ('. $posts . ')'; + } + + $return .= $link; + $return .= ( 'list' == $args['style'] ) ? '</li>' : ', '; + } + + $return = rtrim( $return, ', ' ); + + if ( ! $args['echo'] ) { + return $return; + } + echo $return; +} + +/** + * Does this site have more than one author + * + * Checks to see if more than one author has published posts. + * + * @since 3.2.0 + * @return bool Whether or not we have more than one author + */ +function is_multi_author() { + global $wpdb; + + if ( false === ( $is_multi_author = get_transient( 'is_multi_author' ) ) ) { + $rows = (array) $wpdb->get_col("SELECT DISTINCT post_author FROM $wpdb->posts WHERE post_type = 'post' AND post_status = 'publish' LIMIT 2"); + $is_multi_author = 1 < count( $rows ) ? 1 : 0; + set_transient( 'is_multi_author', $is_multi_author ); + } + + /** + * Filter whether the site has more than one author with published posts. + * + * @since 3.2.0 + * + * @param bool $is_multi_author Whether $is_multi_author should evaluate as true. + */ + return apply_filters( 'is_multi_author', (bool) $is_multi_author ); +} + +/** + * Helper function to clear the cache for number of authors. + * + * @private + */ +function __clear_multi_author_cache() { + delete_transient( 'is_multi_author' ); +} +add_action('transition_post_status', '__clear_multi_author_cache'); diff --git a/wp-includes/bookmark-template.php b/wp-includes/bookmark-template.php new file mode 100644 index 0000000..54ad5c9 --- /dev/null +++ b/wp-includes/bookmark-template.php @@ -0,0 +1,298 @@ +<?php +/** + * Bookmark Template Functions for usage in Themes + * + * @package WordPress + * @subpackage Template + */ + +/** + * The formatted output of a list of bookmarks. + * + * The $bookmarks array must contain bookmark objects and will be iterated over + * to retrieve the bookmark to be used in the output. + * + * The output is formatted as HTML with no way to change that format. However, + * what is between, before, and after can be changed. The link itself will be + * HTML. + * + * This function is used internally by wp_list_bookmarks() and should not be + * used by themes. + * + * @since 2.1.0 + * @access private + * + * @param array $bookmarks List of bookmarks to traverse. + * @param string|array $args { + * Optional. Bookmarks arguments. + * + * @type int|bool $show_updated Whether to show the time the bookmark was last updated. + * Accepts 1|true or 0|false. Default 0|false. + * @type int|bool $show_description Whether to show the bookmakr description. Accepts 1|true, + * Accepts 1|true or 0|false. Default 0|false. + * @type int|bool $show_images Whether to show the link image if available. Accepts 1|true + * or 0|false. Default 1|true. + * @type int|bool $show_name Whether to show link name if available. Accepts 1|true or + * 0|false. Default 0|false. + * @type string $before The HTML or text to prepend to each bookmark. Default `<li>`. + * @type string $after The HTML or text to append to each bookmark. Default `</li>`. + * @type string $link_before The HTML or text to prepend to each bookmark inside the anchor + * tags. Default empty. + * @type string $link_after The HTML or text to append to each bookmark inside the anchor + * tags. Default empty. + * @type string $between The string for use in between the link, description, and image. + * Default "\n". + * @type int|bool $show_rating Whether to show the link rating. Accepts 1|true or 0|false. + * Default 0|false. + * + * } + * @return string Formatted output in HTML + */ +function _walk_bookmarks( $bookmarks, $args = '' ) { + $defaults = array( + 'show_updated' => 0, 'show_description' => 0, + 'show_images' => 1, 'show_name' => 0, + 'before' => '<li>', 'after' => '</li>', 'between' => "\n", + 'show_rating' => 0, 'link_before' => '', 'link_after' => '' + ); + + $r = wp_parse_args( $args, $defaults ); + + $output = ''; // Blank string to start with. + + foreach ( (array) $bookmarks as $bookmark ) { + if ( ! isset( $bookmark->recently_updated ) ) { + $bookmark->recently_updated = false; + } + $output .= $r['before']; + if ( $r['show_updated'] && $bookmark->recently_updated ) { + $output .= '<em>'; + } + $the_link = '#'; + if ( ! empty( $bookmark->link_url ) ) { + $the_link = esc_url( $bookmark->link_url ); + } + $desc = esc_attr( sanitize_bookmark_field( 'link_description', $bookmark->link_description, $bookmark->link_id, 'display' ) ); + $name = esc_attr( sanitize_bookmark_field( 'link_name', $bookmark->link_name, $bookmark->link_id, 'display' ) ); + $title = $desc; + + if ( $r['show_updated'] ) { + if ( '00' != substr( $bookmark->link_updated_f, 0, 2 ) ) { + $title .= ' ('; + $title .= sprintf( + __('Last updated: %s'), + date( + get_option( 'links_updated_date_format' ), + $bookmark->link_updated_f + ( get_option( 'gmt_offset' ) * HOUR_IN_SECONDS ) + ) + ); + $title .= ')'; + } + } + $alt = ' alt="' . $name . ( $r['show_description'] ? ' ' . $title : '' ) . '"'; + + if ( '' != $title ) { + $title = ' title="' . $title . '"'; + } + $rel = $bookmark->link_rel; + if ( '' != $rel ) { + $rel = ' rel="' . esc_attr($rel) . '"'; + } + $target = $bookmark->link_target; + if ( '' != $target ) { + $target = ' target="' . $target . '"'; + } + $output .= '<a href="' . $the_link . '"' . $rel . $title . $target . '>'; + + $output .= $r['link_before']; + + if ( $bookmark->link_image != null && $r['show_images'] ) { + if ( strpos( $bookmark->link_image, 'http' ) === 0 ) { + $output .= "<img src=\"$bookmark->link_image\" $alt $title />"; + } else { // If it's a relative path + $output .= "<img src=\"" . get_option('siteurl') . "$bookmark->link_image\" $alt $title />"; + } + if ( $r['show_name'] ) { + $output .= " $name"; + } + } else { + $output .= $name; + } + + $output .= $r['link_after']; + + $output .= '</a>'; + + if ( $r['show_updated'] && $bookmark->recently_updated ) { + $output .= '</em>'; + } + + if ( $r['show_description'] && '' != $desc ) { + $output .= $r['between'] . $desc; + } + + if ( $r['show_rating'] ) { + $output .= $r['between'] . sanitize_bookmark_field( + 'link_rating', + $bookmark->link_rating, + $bookmark->link_id, + 'display' + ); + } + $output .= $r['after'] . "\n"; + } // end while + + return $output; +} + +/** + * Retrieve or echo all of the bookmarks. + * + * List of default arguments are as follows: + * + * These options define how the Category name will appear before the category + * links are displayed, if 'categorize' is 1. If 'categorize' is 0, then it will + * display for only the 'title_li' string and only if 'title_li' is not empty. + * + * @since 2.1.0 + * + * @see _walk_bookmarks() + * + * @param string|array $args { + * Optional. String or array of arguments to list bookmarks. + * + * @type string $orderby How to order the links by. Accepts post fields. Default 'name'. + * @type string $order Whether to order bookmarks in ascending or descending order. + * Accepts 'ASC' (ascending) or 'DESC' (descending). Default 'ASC'. + * @type int $limit Amount of bookmarks to display. Accepts 1+ or -1 for all. + * Default -1. + * @type string $category Comma-separated list of category ids to include links from. + * Default empty. + * @type string $category_name Category to retrieve links for by name. Default empty. + * @type int|bool $hide_invisible Whether to show or hide links marked as 'invisible'. Accepts + * 1|true or 0|false. Default 1|true. + * @type int|bool $show_updated Whether to display the time the bookmark was last updated. + * Accepts 1|true or 0|false. Default 0|false. + * @type int|bool $echo Whether to echo or return the formatted bookmarks. Accepts + * 1|true (echo) or 0|false (return). Default 1|true. + * @type int|bool $categorize Whether to show links listed by category or in a single column. + * Accepts 1|true (by category) or 0|false (one column). Default 1|true. + * @type int|bool $show_description Whether to show the bookmark descriptions. Accepts 1|true or 0|false. + * Default 0|false. + * @type string $title_li What to show before the links appear. Default 'Bookmarks'. + * @type string $title_before The HTML or text to prepend to the $title_li string. Default '<h2>'. + * @type string $title_after The HTML or text to append to the $title_li string. Default '</h2>'. + * @type string $class The CSS class to use for the $title_li. Default 'linkcat'. + * @type string $category_before The HTML or text to prepend to $title_before if $categorize is true. + * String must contain '%id' and '%class' to inherit the category ID and + * the $class argument used for formatting in themes. + * Default '<li id="%id" class="%class">'. + * @type string $category_after The HTML or text to append to $title_after if $categorize is true. + * Default '</li>'. + * @type string $category_orderby How to order the bookmark category based on term scheme if $categorize + * is true. Default 'name'. + * @type string $category_order Whether to order categories in ascending or descending order if + * $categorize is true. Accepts 'ASC' (ascending) or 'DESC' (descending). + * Default 'ASC'. + * } + * @return string|null Will only return if echo option is set to not echo. Default is not return anything. + */ +function wp_list_bookmarks( $args = '' ) { + $defaults = array( + 'orderby' => 'name', 'order' => 'ASC', + 'limit' => -1, 'category' => '', 'exclude_category' => '', + 'category_name' => '', 'hide_invisible' => 1, + 'show_updated' => 0, 'echo' => 1, + 'categorize' => 1, 'title_li' => __('Bookmarks'), + 'title_before' => '<h2>', 'title_after' => '</h2>', + 'category_orderby' => 'name', 'category_order' => 'ASC', + 'class' => 'linkcat', 'category_before' => '<li id="%id" class="%class">', + 'category_after' => '</li>' + ); + + $r = wp_parse_args( $args, $defaults ); + + $output = ''; + + if ( $r['categorize'] ) { + $cats = get_terms( 'link_category', array( + 'name__like' => $r['category_name'], + 'include' => $r['category'], + 'exclude' => $r['exclude_category'], + 'orderby' => $r['category_orderby'], + 'order' => $r['category_order'], + 'hierarchical' => 0 + ) ); + if ( empty( $cats ) ) { + $r['categorize'] = false; + } + } + + if ( $r['categorize'] ) { + // Split the bookmarks into ul's for each category + foreach ( (array) $cats as $cat ) { + $params = array_merge( $r, array( 'category' => $cat->term_id ) ); + $bookmarks = get_bookmarks( $params ); + if ( empty( $bookmarks ) ) { + continue; + } + $output .= str_replace( + array( '%id', '%class' ), + array( "linkcat-$cat->term_id", $r['class'] ), + $r['category_before'] + ); + /** + * Filter the bookmarks category name. + * + * @since 2.2.0 + * + * @param string $cat_name The category name of bookmarks. + */ + $catname = apply_filters( 'link_category', $cat->name ); + + $output .= $r['title_before']; + $output .= $catname; + $output .= $r['title_after']; + $output .= "\n\t<ul class='xoxo blogroll'>\n"; + $output .= _walk_bookmarks( $bookmarks, $r ); + $output .= "\n\t</ul>\n"; + $output .= $r['category_after'] . "\n"; + } + } else { + //output one single list using title_li for the title + $bookmarks = get_bookmarks( $r ); + + if ( ! empty( $bookmarks ) ) { + if ( ! empty( $r['title_li'] ) ) { + $output .= str_replace( + array( '%id', '%class' ), + array( "linkcat-" . $r['category'], $r['class'] ), + $r['category_before'] + ); + $output .= $r['title_before']; + $output .= $r['title_li']; + $output .= $r['title_after']; + $output .= "\n\t<ul class='xoxo blogroll'>\n"; + $output .= _walk_bookmarks( $bookmarks, $r ); + $output .= "\n\t</ul>\n"; + $output .= $r['category_after'] . "\n"; + } else { + $output .= _walk_bookmarks( $bookmarks, $r ); + } + } + } + + /** + * Filter the bookmarks list before it is echoed or returned. + * + * @since 2.5.0 + * + * @param string $html The HTML list of bookmarks. + */ + $html = apply_filters( 'wp_list_bookmarks', $output ); + + if ( ! $r['echo'] ) { + return $html; + } + echo $html; +} diff --git a/wp-includes/bookmark.php b/wp-includes/bookmark.php new file mode 100644 index 0000000..08b2129 --- /dev/null +++ b/wp-includes/bookmark.php @@ -0,0 +1,414 @@ +<?php +/** + * Link/Bookmark API + * + * @package WordPress + * @subpackage Bookmark + */ + +/** + * Retrieve Bookmark data + * + * @since 2.1.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @param int|stdClass $bookmark + * @param string $output Optional. Either OBJECT, ARRAY_N, or ARRAY_A constant + * @param string $filter Optional, default is 'raw'. + * @return array|object Type returned depends on $output value. + */ +function get_bookmark($bookmark, $output = OBJECT, $filter = 'raw') { + global $wpdb; + + if ( empty($bookmark) ) { + if ( isset($GLOBALS['link']) ) + $_bookmark = & $GLOBALS['link']; + else + $_bookmark = null; + } elseif ( is_object($bookmark) ) { + wp_cache_add($bookmark->link_id, $bookmark, 'bookmark'); + $_bookmark = $bookmark; + } else { + if ( isset($GLOBALS['link']) && ($GLOBALS['link']->link_id == $bookmark) ) { + $_bookmark = & $GLOBALS['link']; + } elseif ( ! $_bookmark = wp_cache_get($bookmark, 'bookmark') ) { + $_bookmark = $wpdb->get_row($wpdb->prepare("SELECT * FROM $wpdb->links WHERE link_id = %d LIMIT 1", $bookmark)); + if ( $_bookmark ) { + $_bookmark->link_category = array_unique( wp_get_object_terms( $_bookmark->link_id, 'link_category', array( 'fields' => 'ids' ) ) ); + wp_cache_add( $_bookmark->link_id, $_bookmark, 'bookmark' ); + } + } + } + + if ( ! $_bookmark ) + return $_bookmark; + + $_bookmark = sanitize_bookmark($_bookmark, $filter); + + if ( $output == OBJECT ) { + return $_bookmark; + } elseif ( $output == ARRAY_A ) { + return get_object_vars($_bookmark); + } elseif ( $output == ARRAY_N ) { + return array_values(get_object_vars($_bookmark)); + } else { + return $_bookmark; + } +} + +/** + * Retrieve single bookmark data item or field. + * + * @since 2.3.0 + * + * @param string $field The name of the data field to return + * @param int $bookmark The bookmark ID to get field + * @param string $context Optional. The context of how the field will be used. + * @return string + */ +function get_bookmark_field( $field, $bookmark, $context = 'display' ) { + $bookmark = (int) $bookmark; + $bookmark = get_bookmark( $bookmark ); + + if ( is_wp_error($bookmark) ) + return $bookmark; + + if ( !is_object($bookmark) ) + return ''; + + if ( !isset($bookmark->$field) ) + return ''; + + return sanitize_bookmark_field($field, $bookmark->$field, $bookmark->link_id, $context); +} + +/** + * Retrieves the list of bookmarks + * + * Attempts to retrieve from the cache first based on MD5 hash of arguments. If + * that fails, then the query will be built from the arguments and executed. The + * results will be stored to the cache. + * + * @since 2.1.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @param string|array $args { + * Optional. String or array of arguments to retrieve bookmarks. + * + * @type string $orderby How to order the links by. Accepts post fields. Default 'name'. + * @type string $order Whether to order bookmarks in ascending or descending order. + * Accepts 'ASC' (ascending) or 'DESC' (descending). Default 'ASC'. + * @type int $limit Amount of bookmarks to display. Accepts 1+ or -1 for all. + * Default -1. + * @type string $category Comma-separated list of category ids to include links from. + * Default empty. + * @type string $category_name Category to retrieve links for by name. Default empty. + * @type int|bool $hide_invisible Whether to show or hide links marked as 'invisible'. Accepts + * 1|true or 0|false. Default 1|true. + * @type int|bool $show_updated Whether to display the time the bookmark was last updated. + * Accepts 1|true or 0|false. Default 0|false. + * @type string $include Comma-separated list of bookmark IDs to include. Default empty. + * @type string $exclude Comma-separated list of bookmark IDs to exclude. Default empty. + * } + * @return array List of bookmark row objects. + */ +function get_bookmarks( $args = '' ) { + global $wpdb; + + $defaults = array( + 'orderby' => 'name', 'order' => 'ASC', + 'limit' => -1, 'category' => '', + 'category_name' => '', 'hide_invisible' => 1, + 'show_updated' => 0, 'include' => '', + 'exclude' => '', 'search' => '' + ); + + $r = wp_parse_args( $args, $defaults ); + + $key = md5( serialize( $r ) ); + if ( $cache = wp_cache_get( 'get_bookmarks', 'bookmark' ) ) { + if ( is_array( $cache ) && isset( $cache[ $key ] ) ) { + $bookmarks = $cache[ $key ]; + /** + * Filter the returned list of bookmarks. + * + * The first time the hook is evaluated in this file, it returns the cached + * bookmarks list. The second evaluation returns a cached bookmarks list if the + * link category is passed but does not exist. The third evaluation returns + * the full cached results. + * + * @since 2.1.0 + * + * @see get_bookmarks() + * + * @param array $bookmarks List of the cached bookmarks. + * @param array $r An array of bookmark query arguments. + */ + return apply_filters( 'get_bookmarks', $bookmarks, $r ); + } + } + + if ( ! is_array( $cache ) ) { + $cache = array(); + } + + $inclusions = ''; + if ( ! empty( $r['include'] ) ) { + $r['exclude'] = ''; //ignore exclude, category, and category_name params if using include + $r['category'] = ''; + $r['category_name'] = ''; + $inclinks = preg_split( '/[\s,]+/', $r['include'] ); + if ( count( $inclinks ) ) { + foreach ( $inclinks as $inclink ) { + if ( empty( $inclusions ) ) { + $inclusions = ' AND ( link_id = ' . intval( $inclink ) . ' '; + } else { + $inclusions .= ' OR link_id = ' . intval( $inclink ) . ' '; + } + } + } + } + if (! empty( $inclusions ) ) { + $inclusions .= ')'; + } + + $exclusions = ''; + if ( ! empty( $r['exclude'] ) ) { + $exlinks = preg_split( '/[\s,]+/', $r['exclude'] ); + if ( count( $exlinks ) ) { + foreach ( $exlinks as $exlink ) { + if ( empty( $exclusions ) ) { + $exclusions = ' AND ( link_id <> ' . intval( $exlink ) . ' '; + } else { + $exclusions .= ' AND link_id <> ' . intval( $exlink ) . ' '; + } + } + } + } + if ( ! empty( $exclusions ) ) { + $exclusions .= ')'; + } + + if ( ! empty( $r['category_name'] ) ) { + if ( $r['category'] = get_term_by('name', $r['category_name'], 'link_category') ) { + $r['category'] = $r['category']->term_id; + } else { + $cache[ $key ] = array(); + wp_cache_set( 'get_bookmarks', $cache, 'bookmark' ); + /** This filter is documented in wp-includes/bookmark.php */ + return apply_filters( 'get_bookmarks', array(), $r ); + } + } + + $search = ''; + if ( ! empty( $r['search'] ) ) { + $like = '%' . $wpdb->esc_like( $r['search'] ) . '%'; + $search = $wpdb->prepare(" AND ( (link_url LIKE %s) OR (link_name LIKE %s) OR (link_description LIKE %s) ) ", $like, $like, $like ); + } + + $category_query = ''; + $join = ''; + if ( ! empty( $r['category'] ) ) { + $incategories = preg_split( '/[\s,]+/', $r['category'] ); + if ( count($incategories) ) { + foreach ( $incategories as $incat ) { + if ( empty( $category_query ) ) { + $category_query = ' AND ( tt.term_id = ' . intval( $incat ) . ' '; + } else { + $category_query .= ' OR tt.term_id = ' . intval( $incat ) . ' '; + } + } + } + } + if ( ! empty( $category_query ) ) { + $category_query .= ") AND taxonomy = 'link_category'"; + $join = " INNER JOIN $wpdb->term_relationships AS tr ON ($wpdb->links.link_id = tr.object_id) INNER JOIN $wpdb->term_taxonomy as tt ON tt.term_taxonomy_id = tr.term_taxonomy_id"; + } + + if ( $r['show_updated'] ) { + $recently_updated_test = ", IF (DATE_ADD(link_updated, INTERVAL 120 MINUTE) >= NOW(), 1,0) as recently_updated "; + } else { + $recently_updated_test = ''; + } + + $get_updated = ( $r['show_updated'] ) ? ', UNIX_TIMESTAMP(link_updated) AS link_updated_f ' : ''; + + $orderby = strtolower( $r['orderby'] ); + $length = ''; + switch ( $orderby ) { + case 'length': + $length = ", CHAR_LENGTH(link_name) AS length"; + break; + case 'rand': + $orderby = 'rand()'; + break; + case 'link_id': + $orderby = "$wpdb->links.link_id"; + break; + default: + $orderparams = array(); + $keys = array( 'link_id', 'link_name', 'link_url', 'link_visible', 'link_rating', 'link_owner', 'link_updated', 'link_notes', 'link_description' ); + foreach ( explode( ',', $orderby ) as $ordparam ) { + $ordparam = trim( $ordparam ); + + if ( in_array( 'link_' . $ordparam, $keys ) ) { + $orderparams[] = 'link_' . $ordparam; + } elseif ( in_array( $ordparam, $keys ) ) { + $orderparams[] = $ordparam; + } + } + $orderby = implode( ',', $orderparams ); + } + + if ( empty( $orderby ) ) { + $orderby = 'link_name'; + } + + $order = strtoupper( $r['order'] ); + if ( '' !== $order && ! in_array( $order, array( 'ASC', 'DESC' ) ) ) { + $order = 'ASC'; + } + + $visible = ''; + if ( $r['hide_invisible'] ) { + $visible = "AND link_visible = 'Y'"; + } + + $query = "SELECT * $length $recently_updated_test $get_updated FROM $wpdb->links $join WHERE 1=1 $visible $category_query"; + $query .= " $exclusions $inclusions $search"; + $query .= " ORDER BY $orderby $order"; + if ( $r['limit'] != -1 ) { + $query .= ' LIMIT ' . $r['limit']; + } + + $results = $wpdb->get_results( $query ); + + $cache[ $key ] = $results; + wp_cache_set( 'get_bookmarks', $cache, 'bookmark' ); + + /** This filter is documented in wp-includes/bookmark.php */ + return apply_filters( 'get_bookmarks', $results, $r ); +} + +/** + * Sanitizes all bookmark fields + * + * @since 2.3.0 + * + * @param object|array $bookmark Bookmark row + * @param string $context Optional, default is 'display'. How to filter the + * fields + * @return object|array Same type as $bookmark but with fields sanitized. + */ +function sanitize_bookmark($bookmark, $context = 'display') { + $fields = array('link_id', 'link_url', 'link_name', 'link_image', 'link_target', 'link_category', + 'link_description', 'link_visible', 'link_owner', 'link_rating', 'link_updated', + 'link_rel', 'link_notes', 'link_rss', ); + + if ( is_object($bookmark) ) { + $do_object = true; + $link_id = $bookmark->link_id; + } else { + $do_object = false; + $link_id = $bookmark['link_id']; + } + + foreach ( $fields as $field ) { + if ( $do_object ) { + if ( isset($bookmark->$field) ) + $bookmark->$field = sanitize_bookmark_field($field, $bookmark->$field, $link_id, $context); + } else { + if ( isset($bookmark[$field]) ) + $bookmark[$field] = sanitize_bookmark_field($field, $bookmark[$field], $link_id, $context); + } + } + + return $bookmark; +} + +/** + * Sanitizes a bookmark field + * + * Sanitizes the bookmark fields based on what the field name is. If the field + * has a strict value set, then it will be tested for that, else a more generic + * filtering is applied. After the more strict filter is applied, if the + * $context is 'raw' then the value is immediately return. + * + * Hooks exist for the more generic cases. With the 'edit' context, the + * 'edit_$field' filter will be called and passed the $value and $bookmark_id + * respectively. With the 'db' context, the 'pre_$field' filter is called and + * passed the value. The 'display' context is the final context and has the + * $field has the filter name and is passed the $value, $bookmark_id, and + * $context respectively. + * + * @since 2.3.0 + * + * @param string $field The bookmark field + * @param mixed $value The bookmark field value + * @param int $bookmark_id Bookmark ID + * @param string $context How to filter the field value. Either 'raw', 'edit', + * 'attribute', 'js', 'db', or 'display' + * @return mixed The filtered value + */ +function sanitize_bookmark_field($field, $value, $bookmark_id, $context) { + switch ( $field ) { + case 'link_id' : // ints + case 'link_rating' : + $value = (int) $value; + break; + case 'link_category' : // array( ints ) + $value = array_map('absint', (array) $value); + // We return here so that the categories aren't filtered. + // The 'link_category' filter is for the name of a link category, not an array of a link's link categories + return $value; + + case 'link_visible' : // bool stored as Y|N + $value = preg_replace('/[^YNyn]/', '', $value); + break; + case 'link_target' : // "enum" + $targets = array('_top', '_blank'); + if ( ! in_array($value, $targets) ) + $value = ''; + break; + } + + if ( 'raw' == $context ) + return $value; + + if ( 'edit' == $context ) { + /** This filter is documented in wp-includes/post.php */ + $value = apply_filters( "edit_$field", $value, $bookmark_id ); + + if ( 'link_notes' == $field ) { + $value = esc_html( $value ); // textarea_escaped + } else { + $value = esc_attr($value); + } + } else if ( 'db' == $context ) { + /** This filter is documented in wp-includes/post.php */ + $value = apply_filters( "pre_$field", $value ); + } else { + /** This filter is documented in wp-includes/post.php */ + $value = apply_filters( $field, $value, $bookmark_id, $context ); + + if ( 'attribute' == $context ) + $value = esc_attr($value); + else if ( 'js' == $context ) + $value = esc_js($value); + } + + return $value; +} + +/** + * Deletes bookmark cache + * + * @since 2.7.0 + */ +function clean_bookmark_cache( $bookmark_id ) { + wp_cache_delete( $bookmark_id, 'bookmark' ); + wp_cache_delete( 'get_bookmarks', 'bookmark' ); + clean_object_term_cache( $bookmark_id, 'link'); +} diff --git a/wp-includes/cache.php b/wp-includes/cache.php new file mode 100644 index 0000000..3cd344b --- /dev/null +++ b/wp-includes/cache.php @@ -0,0 +1,707 @@ +<?php +/** + * Object Cache API + * + * @link http://codex.wordpress.org/Function_Reference/WP_Cache + * + * @package WordPress + * @subpackage Cache + */ + +/** + * Adds data to the cache, if the cache key doesn't already exist. + * + * @since 2.0.0 + * @uses $wp_object_cache Object Cache Class + * @see WP_Object_Cache::add() + * + * @param int|string $key The cache key to use for retrieval later + * @param mixed $data The data to add to the cache store + * @param string $group The group to add the cache to + * @param int $expire When the cache data should be expired + * @return bool False if cache key and group already exist, true on success + */ +function wp_cache_add( $key, $data, $group = '', $expire = 0 ) { + global $wp_object_cache; + + return $wp_object_cache->add( $key, $data, $group, (int) $expire ); +} + +/** + * Closes the cache. + * + * This function has ceased to do anything since WordPress 2.5. The + * functionality was removed along with the rest of the persistent cache. This + * does not mean that plugins can't implement this function when they need to + * make sure that the cache is cleaned up after WordPress no longer needs it. + * + * @since 2.0.0 + * + * @return bool Always returns True + */ +function wp_cache_close() { + return true; +} + +/** + * Decrement numeric cache item's value + * + * @since 3.3.0 + * @uses $wp_object_cache Object Cache Class + * @see WP_Object_Cache::decr() + * + * @param int|string $key The cache key to increment + * @param int $offset The amount by which to decrement the item's value. Default is 1. + * @param string $group The group the key is in. + * @return false|int False on failure, the item's new value on success. + */ +function wp_cache_decr( $key, $offset = 1, $group = '' ) { + global $wp_object_cache; + + return $wp_object_cache->decr( $key, $offset, $group ); +} + +/** + * Removes the cache contents matching key and group. + * + * @since 2.0.0 + * @uses $wp_object_cache Object Cache Class + * @see WP_Object_Cache::delete() + * + * @param int|string $key What the contents in the cache are called + * @param string $group Where the cache contents are grouped + * @return bool True on successful removal, false on failure + */ +function wp_cache_delete($key, $group = '') { + global $wp_object_cache; + + return $wp_object_cache->delete($key, $group); +} + +/** + * Removes all cache items. + * + * @since 2.0.0 + * @uses $wp_object_cache Object Cache Class + * @see WP_Object_Cache::flush() + * + * @return bool False on failure, true on success + */ +function wp_cache_flush() { + global $wp_object_cache; + + return $wp_object_cache->flush(); +} + +/** + * Retrieves the cache contents from the cache by key and group. + * + * @since 2.0.0 + * @uses $wp_object_cache Object Cache Class + * @see WP_Object_Cache::get() + * + * @param int|string $key What the contents in the cache are called + * @param string $group Where the cache contents are grouped + * @param bool $force Whether to force an update of the local cache from the persistent cache (default is false) + * @param &bool $found Whether key was found in the cache. Disambiguates a return of false, a storable value. + * @return bool|mixed False on failure to retrieve contents or the cache + * contents on success + */ +function wp_cache_get( $key, $group = '', $force = false, &$found = null ) { + global $wp_object_cache; + + return $wp_object_cache->get( $key, $group, $force, $found ); +} + +/** + * Increment numeric cache item's value + * + * @since 3.3.0 + * @uses $wp_object_cache Object Cache Class + * @see WP_Object_Cache::incr() + * + * @param int|string $key The cache key to increment + * @param int $offset The amount by which to increment the item's value. Default is 1. + * @param string $group The group the key is in. + * @return false|int False on failure, the item's new value on success. + */ +function wp_cache_incr( $key, $offset = 1, $group = '' ) { + global $wp_object_cache; + + return $wp_object_cache->incr( $key, $offset, $group ); +} + +/** + * Sets up Object Cache Global and assigns it. + * + * @since 2.0.0 + * @global WP_Object_Cache $wp_object_cache WordPress Object Cache + */ +function wp_cache_init() { + $GLOBALS['wp_object_cache'] = new WP_Object_Cache(); +} + +/** + * Replaces the contents of the cache with new data. + * + * @since 2.0.0 + * @uses $wp_object_cache Object Cache Class + * @see WP_Object_Cache::replace() + * + * @param int|string $key What to call the contents in the cache + * @param mixed $data The contents to store in the cache + * @param string $group Where to group the cache contents + * @param int $expire When to expire the cache contents + * @return bool False if not exists, true if contents were replaced + */ +function wp_cache_replace( $key, $data, $group = '', $expire = 0 ) { + global $wp_object_cache; + + return $wp_object_cache->replace( $key, $data, $group, (int) $expire ); +} + +/** + * Saves the data to the cache. + * + * @since 2.0.0 + * + * @uses $wp_object_cache Object Cache Class + * @see WP_Object_Cache::set() + * + * @param int|string $key What to call the contents in the cache + * @param mixed $data The contents to store in the cache + * @param string $group Where to group the cache contents + * @param int $expire When to expire the cache contents + * @return bool False on failure, true on success + */ +function wp_cache_set( $key, $data, $group = '', $expire = 0 ) { + global $wp_object_cache; + + return $wp_object_cache->set( $key, $data, $group, (int) $expire ); +} + +/** + * Switch the interal blog id. + * + * This changes the blog id used to create keys in blog specific groups. + * + * @since 3.5.0 + * + * @param int $blog_id Blog ID + */ +function wp_cache_switch_to_blog( $blog_id ) { + global $wp_object_cache; + + return $wp_object_cache->switch_to_blog( $blog_id ); +} + +/** + * Adds a group or set of groups to the list of global groups. + * + * @since 2.6.0 + * + * @param string|array $groups A group or an array of groups to add + */ +function wp_cache_add_global_groups( $groups ) { + global $wp_object_cache; + + return $wp_object_cache->add_global_groups( $groups ); +} + +/** + * Adds a group or set of groups to the list of non-persistent groups. + * + * @since 2.6.0 + * + * @param string|array $groups A group or an array of groups to add + */ +function wp_cache_add_non_persistent_groups( $groups ) { + // Default cache doesn't persist so nothing to do here. + return; +} + +/** + * Reset internal cache keys and structures. If the cache backend uses global + * blog or site IDs as part of its cache keys, this function instructs the + * backend to reset those keys and perform any cleanup since blog or site IDs + * have changed since cache init. + * + * This function is deprecated. Use wp_cache_switch_to_blog() instead of this + * function when preparing the cache for a blog switch. For clearing the cache + * during unit tests, consider using wp_cache_init(). wp_cache_init() is not + * recommended outside of unit tests as the performance penality for using it is + * high. + * + * @since 2.6.0 + * @deprecated 3.5.0 + */ +function wp_cache_reset() { + _deprecated_function( __FUNCTION__, '3.5' ); + + global $wp_object_cache; + + return $wp_object_cache->reset(); +} + +/** + * WordPress Object Cache + * + * The WordPress Object Cache is used to save on trips to the database. The + * Object Cache stores all of the cache data to memory and makes the cache + * contents available by using a key, which is used to name and later retrieve + * the cache contents. + * + * The Object Cache can be replaced by other caching mechanisms by placing files + * in the wp-content folder which is looked at in wp-settings. If that file + * exists, then this file will not be included. + * + * @package WordPress + * @subpackage Cache + * @since 2.0.0 + */ +class WP_Object_Cache { + + /** + * Holds the cached objects + * + * @var array + * @access private + * @since 2.0.0 + */ + private $cache = array(); + + /** + * The amount of times the cache data was already stored in the cache. + * + * @since 2.5.0 + * @access private + * @var int + */ + private $cache_hits = 0; + + /** + * Amount of times the cache did not have the request in cache + * + * @var int + * @access public + * @since 2.0.0 + */ + public $cache_misses = 0; + + /** + * List of global groups + * + * @var array + * @access protected + * @since 3.0.0 + */ + protected $global_groups = array(); + + /** + * The blog prefix to prepend to keys in non-global groups. + * + * @var int + * @access private + * @since 3.5.0 + */ + private $blog_prefix; + + /** + * Make private properties readable for backwards compatibility. + * + * @since 4.0.0 + * @access public + * + * @param string $name Property to get. + * @return mixed Property. + */ + public function __get( $name ) { + return $this->$name; + } + + /** + * Make private properties settable for backwards compatibility. + * + * @since 4.0.0 + * @access public + * + * @param string $name Property to set. + * @param mixed $value Property value. + * @return mixed Newly-set property. + */ + public function __set( $name, $value ) { + return $this->$name = $value; + } + + /** + * Make private properties checkable for backwards compatibility. + * + * @since 4.0.0 + * @access public + * + * @param string $name Property to check if set. + * @return bool Whether the property is set. + */ + public function __isset( $name ) { + return isset( $this->$name ); + } + + /** + * Make private properties un-settable for backwards compatibility. + * + * @since 4.0.0 + * @access public + * + * @param string $name Property to unset. + */ + public function __unset( $name ) { + unset( $this->$name ); + } + + /** + * Adds data to the cache if it doesn't already exist. + * + * @uses WP_Object_Cache::_exists Checks to see if the cache already has data. + * @uses WP_Object_Cache::set Sets the data after the checking the cache + * contents existence. + * + * @since 2.0.0 + * + * @param int|string $key What to call the contents in the cache + * @param mixed $data The contents to store in the cache + * @param string $group Where to group the cache contents + * @param int $expire When to expire the cache contents + * @return bool False if cache key and group already exist, true on success + */ + public function add( $key, $data, $group = 'default', $expire = 0 ) { + if ( wp_suspend_cache_addition() ) + return false; + + if ( empty( $group ) ) + $group = 'default'; + + $id = $key; + if ( $this->multisite && ! isset( $this->global_groups[ $group ] ) ) + $id = $this->blog_prefix . $key; + + if ( $this->_exists( $id, $group ) ) + return false; + + return $this->set( $key, $data, $group, (int) $expire ); + } + + /** + * Sets the list of global groups. + * + * @since 3.0.0 + * + * @param array $groups List of groups that are global. + */ + public function add_global_groups( $groups ) { + $groups = (array) $groups; + + $groups = array_fill_keys( $groups, true ); + $this->global_groups = array_merge( $this->global_groups, $groups ); + } + + /** + * Decrement numeric cache item's value + * + * @since 3.3.0 + * + * @param int|string $key The cache key to increment + * @param int $offset The amount by which to decrement the item's value. Default is 1. + * @param string $group The group the key is in. + * @return false|int False on failure, the item's new value on success. + */ + public function decr( $key, $offset = 1, $group = 'default' ) { + if ( empty( $group ) ) + $group = 'default'; + + if ( $this->multisite && ! isset( $this->global_groups[ $group ] ) ) + $key = $this->blog_prefix . $key; + + if ( ! $this->_exists( $key, $group ) ) + return false; + + if ( ! is_numeric( $this->cache[ $group ][ $key ] ) ) + $this->cache[ $group ][ $key ] = 0; + + $offset = (int) $offset; + + $this->cache[ $group ][ $key ] -= $offset; + + if ( $this->cache[ $group ][ $key ] < 0 ) + $this->cache[ $group ][ $key ] = 0; + + return $this->cache[ $group ][ $key ]; + } + + /** + * Remove the contents of the cache key in the group + * + * If the cache key does not exist in the group, then nothing will happen. + * + * @since 2.0.0 + * + * @param int|string $key What the contents in the cache are called + * @param string $group Where the cache contents are grouped + * @param bool $deprecated Deprecated. + * + * @return bool False if the contents weren't deleted and true on success + */ + public function delete( $key, $group = 'default', $deprecated = false ) { + if ( empty( $group ) ) + $group = 'default'; + + if ( $this->multisite && ! isset( $this->global_groups[ $group ] ) ) + $key = $this->blog_prefix . $key; + + if ( ! $this->_exists( $key, $group ) ) + return false; + + unset( $this->cache[$group][$key] ); + return true; + } + + /** + * Clears the object cache of all data + * + * @since 2.0.0 + * + * @return bool Always returns true + */ + public function flush() { + $this->cache = array (); + + return true; + } + + /** + * Retrieves the cache contents, if it exists + * + * The contents will be first attempted to be retrieved by searching by the + * key in the cache group. If the cache is hit (success) then the contents + * are returned. + * + * On failure, the number of cache misses will be incremented. + * + * @since 2.0.0 + * + * @param int|string $key What the contents in the cache are called + * @param string $group Where the cache contents are grouped + * @param string $force Whether to force a refetch rather than relying on the local cache (default is false) + * @return bool|mixed False on failure to retrieve contents or the cache + * contents on success + */ + public function get( $key, $group = 'default', $force = false, &$found = null ) { + if ( empty( $group ) ) + $group = 'default'; + + if ( $this->multisite && ! isset( $this->global_groups[ $group ] ) ) + $key = $this->blog_prefix . $key; + + if ( $this->_exists( $key, $group ) ) { + $found = true; + $this->cache_hits += 1; + if ( is_object($this->cache[$group][$key]) ) + return clone $this->cache[$group][$key]; + else + return $this->cache[$group][$key]; + } + + $found = false; + $this->cache_misses += 1; + return false; + } + + /** + * Increment numeric cache item's value + * + * @since 3.3.0 + * + * @param int|string $key The cache key to increment + * @param int $offset The amount by which to increment the item's value. Default is 1. + * @param string $group The group the key is in. + * @return false|int False on failure, the item's new value on success. + */ + public function incr( $key, $offset = 1, $group = 'default' ) { + if ( empty( $group ) ) + $group = 'default'; + + if ( $this->multisite && ! isset( $this->global_groups[ $group ] ) ) + $key = $this->blog_prefix . $key; + + if ( ! $this->_exists( $key, $group ) ) + return false; + + if ( ! is_numeric( $this->cache[ $group ][ $key ] ) ) + $this->cache[ $group ][ $key ] = 0; + + $offset = (int) $offset; + + $this->cache[ $group ][ $key ] += $offset; + + if ( $this->cache[ $group ][ $key ] < 0 ) + $this->cache[ $group ][ $key ] = 0; + + return $this->cache[ $group ][ $key ]; + } + + /** + * Replace the contents in the cache, if contents already exist + * + * @since 2.0.0 + * @see WP_Object_Cache::set() + * + * @param int|string $key What to call the contents in the cache + * @param mixed $data The contents to store in the cache + * @param string $group Where to group the cache contents + * @param int $expire When to expire the cache contents + * @return bool False if not exists, true if contents were replaced + */ + public function replace( $key, $data, $group = 'default', $expire = 0 ) { + if ( empty( $group ) ) + $group = 'default'; + + $id = $key; + if ( $this->multisite && ! isset( $this->global_groups[ $group ] ) ) + $id = $this->blog_prefix . $key; + + if ( ! $this->_exists( $id, $group ) ) + return false; + + return $this->set( $key, $data, $group, (int) $expire ); + } + + /** + * Reset keys + * + * @since 3.0.0 + * @deprecated 3.5.0 + */ + public function reset() { + _deprecated_function( __FUNCTION__, '3.5', 'switch_to_blog()' ); + + // Clear out non-global caches since the blog ID has changed. + foreach ( array_keys( $this->cache ) as $group ) { + if ( ! isset( $this->global_groups[ $group ] ) ) + unset( $this->cache[ $group ] ); + } + } + + /** + * Sets the data contents into the cache + * + * The cache contents is grouped by the $group parameter followed by the + * $key. This allows for duplicate ids in unique groups. Therefore, naming of + * the group should be used with care and should follow normal function + * naming guidelines outside of core WordPress usage. + * + * The $expire parameter is not used, because the cache will automatically + * expire for each time a page is accessed and PHP finishes. The method is + * more for cache plugins which use files. + * + * @since 2.0.0 + * + * @param int|string $key What to call the contents in the cache + * @param mixed $data The contents to store in the cache + * @param string $group Where to group the cache contents + * @param int $expire Not Used + * @return bool Always returns true + */ + public function set( $key, $data, $group = 'default', $expire = 0 ) { + if ( empty( $group ) ) + $group = 'default'; + + if ( $this->multisite && ! isset( $this->global_groups[ $group ] ) ) + $key = $this->blog_prefix . $key; + + if ( is_object( $data ) ) + $data = clone $data; + + $this->cache[$group][$key] = $data; + return true; + } + + /** + * Echoes the stats of the caching. + * + * Gives the cache hits, and cache misses. Also prints every cached group, + * key and the data. + * + * @since 2.0.0 + */ + public function stats() { + echo "<p>"; + echo "<strong>Cache Hits:</strong> {$this->cache_hits}<br />"; + echo "<strong>Cache Misses:</strong> {$this->cache_misses}<br />"; + echo "</p>"; + echo '<ul>'; + foreach ($this->cache as $group => $cache) { + echo "<li><strong>Group:</strong> $group - ( " . number_format( strlen( serialize( $cache ) ) / 1024, 2 ) . 'k )</li>'; + } + echo '</ul>'; + } + + /** + * Switch the interal blog id. + * + * This changes the blog id used to create keys in blog specific groups. + * + * @since 3.5.0 + * + * @param int $blog_id Blog ID + */ + public function switch_to_blog( $blog_id ) { + $blog_id = (int) $blog_id; + $this->blog_prefix = $this->multisite ? $blog_id . ':' : ''; + } + + /** + * Utility function to determine whether a key exists in the cache. + * + * @since 3.4.0 + * + * @access protected + * @param string $key + * @param string $group + * @return bool + */ + protected function _exists( $key, $group ) { + return isset( $this->cache[ $group ] ) && ( isset( $this->cache[ $group ][ $key ] ) || array_key_exists( $key, $this->cache[ $group ] ) ); + } + + /** + * Sets up object properties; PHP 5 style constructor + * + * @since 2.0.8 + * @return null|WP_Object_Cache If cache is disabled, returns null. + */ + public function __construct() { + global $blog_id; + + $this->multisite = is_multisite(); + $this->blog_prefix = $this->multisite ? $blog_id . ':' : ''; + + + /** + * @todo This should be moved to the PHP4 style constructor, PHP5 + * already calls __destruct() + */ + register_shutdown_function( array( $this, '__destruct' ) ); + } + + /** + * Will save the object cache before object is completely destroyed. + * + * Called upon object destruction, which should be when PHP ends. + * + * @since 2.0.8 + * + * @return bool True value. Won't be used by PHP + */ + public function __destruct() { + return true; + } +} diff --git a/wp-includes/canonical.php b/wp-includes/canonical.php new file mode 100644 index 0000000..5b2f84d --- /dev/null +++ b/wp-includes/canonical.php @@ -0,0 +1,586 @@ +<?php +/** + * Canonical API to handle WordPress Redirecting + * + * Based on "Permalink Redirect" from Scott Yang and "Enforce www. Preference" + * by Mark Jaquith + * + * @package WordPress + * @since 2.3.0 + */ + +/** + * Redirects incoming links to the proper URL based on the site url. + * + * Search engines consider www.somedomain.com and somedomain.com to be two + * different URLs when they both go to the same location. This SEO enhancement + * prevents penalty for duplicate content by redirecting all incoming links to + * one or the other. + * + * Prevents redirection for feeds, trackbacks, searches, comment popup, and + * admin URLs. Does not redirect on non-pretty-permalink-supporting IIS 7+, + * page/post previews, WP admin, Trackbacks, robots.txt, searches, or on POST + * requests. + * + * Will also attempt to find the correct link when a user enters a URL that does + * not exist based on exact WordPress query. Will instead try to parse the URL + * or query in an attempt to figure the correct page to go to. + * + * @since 2.3.0 + * @uses $wp_rewrite + * @uses $is_IIS + * + * @param string $requested_url Optional. The URL that was requested, used to + * figure if redirect is needed. + * @param bool $do_redirect Optional. Redirect to the new URL. + * @return null|false|string Null, if redirect not needed. False, if redirect + * not needed or the string of the URL + */ +function redirect_canonical( $requested_url = null, $do_redirect = true ) { + global $wp_rewrite, $is_IIS, $wp_query, $wpdb; + + if ( isset( $_SERVER['REQUEST_METHOD'] ) && ! in_array( strtoupper( $_SERVER['REQUEST_METHOD'] ), array( 'GET', 'HEAD' ) ) ) { + return; + } + + // If we're not in wp-admin and the post has been published and preview nonce + // is non-existent or invalid then no need for preview in query + if ( is_preview() && get_query_var( 'p' ) && 'publish' == get_post_status( get_query_var( 'p' ) ) ) { + if ( ! isset( $_GET['preview_id'] ) + || ! isset( $_GET['preview_nonce'] ) + || ! wp_verify_nonce( $_GET['preview_nonce'], 'post_preview_' . (int) $_GET['preview_id'] ) ) { + $wp_query->is_preview = false; + } + } + + if ( is_trackback() || is_search() || is_comments_popup() || is_admin() || is_preview() || is_robots() || ( $is_IIS && !iis7_supports_permalinks() ) ) { + return; + } + + if ( !$requested_url ) { + // build the URL in the address bar + $requested_url = is_ssl() ? 'https://' : 'http://'; + $requested_url .= $_SERVER['HTTP_HOST']; + $requested_url .= $_SERVER['REQUEST_URI']; + } + + $original = @parse_url($requested_url); + if ( false === $original ) + return; + + // Some PHP setups turn requests for / into /index.php in REQUEST_URI + // See: https://core.trac.wordpress.org/ticket/5017 + // See: https://core.trac.wordpress.org/ticket/7173 + // Disabled, for now: + // $original['path'] = preg_replace('|/index\.php$|', '/', $original['path']); + + $redirect = $original; + $redirect_url = false; + + // Notice fixing + if ( !isset($redirect['path']) ) + $redirect['path'] = ''; + if ( !isset($redirect['query']) ) + $redirect['query'] = ''; + + // It's not a preview, so remove it from URL + if ( get_query_var( 'preview' ) ) { + $redirect['query'] = remove_query_arg( 'preview', $redirect['query'] ); + } + + if ( is_feed() && ( $id = get_query_var( 'p' ) ) ) { + if ( $redirect_url = get_post_comments_feed_link( $id, get_query_var( 'feed' ) ) ) { + $redirect['query'] = _remove_qs_args_if_not_in_url( $redirect['query'], array( 'p', 'page_id', 'attachment_id', 'pagename', 'name', 'post_type', 'feed'), $redirect_url ); + $redirect['path'] = parse_url( $redirect_url, PHP_URL_PATH ); + } + } + + if ( is_singular() && 1 > $wp_query->post_count && ($id = get_query_var('p')) ) { + + $vars = $wpdb->get_results( $wpdb->prepare("SELECT post_type, post_parent FROM $wpdb->posts WHERE ID = %d", $id) ); + + if ( isset($vars[0]) && $vars = $vars[0] ) { + if ( 'revision' == $vars->post_type && $vars->post_parent > 0 ) + $id = $vars->post_parent; + + if ( $redirect_url = get_permalink($id) ) + $redirect['query'] = _remove_qs_args_if_not_in_url( $redirect['query'], array( 'p', 'page_id', 'attachment_id', 'pagename', 'name', 'post_type' ), $redirect_url ); + } + } + + // These tests give us a WP-generated permalink + if ( is_404() ) { + + // Redirect ?page_id, ?p=, ?attachment_id= to their respective url's + $id = max( get_query_var('p'), get_query_var('page_id'), get_query_var('attachment_id') ); + if ( $id && $redirect_post = get_post($id) ) { + $post_type_obj = get_post_type_object($redirect_post->post_type); + if ( $post_type_obj->public ) { + $redirect_url = get_permalink($redirect_post); + $redirect['query'] = _remove_qs_args_if_not_in_url( $redirect['query'], array( 'p', 'page_id', 'attachment_id', 'pagename', 'name', 'post_type' ), $redirect_url ); + } + } + + if ( get_query_var( 'day' ) && get_query_var( 'monthnum' ) && get_query_var( 'year' ) ) { + $year = get_query_var( 'year' ); + $month = get_query_var( 'monthnum' ); + $day = get_query_var( 'day' ); + $date = sprintf( '%04d-%02d-%02d', $year, $month, $day ); + if ( ! wp_checkdate( $month, $day, $year, $date ) ) { + $redirect_url = get_month_link( $year, $month ); + $redirect['query'] = _remove_qs_args_if_not_in_url( $redirect['query'], array( 'year', 'monthnum', 'day' ), $redirect_url ); + } + } elseif ( get_query_var( 'monthnum' ) && get_query_var( 'year' ) && 12 < get_query_var( 'monthnum' ) ) { + $redirect_url = get_year_link( get_query_var( 'year' ) ); + $redirect['query'] = _remove_qs_args_if_not_in_url( $redirect['query'], array( 'year', 'monthnum' ), $redirect_url ); + } + + if ( ! $redirect_url ) { + if ( $redirect_url = redirect_guess_404_permalink() ) { + $redirect['query'] = _remove_qs_args_if_not_in_url( $redirect['query'], array( 'page', 'feed', 'p', 'page_id', 'attachment_id', 'pagename', 'name', 'post_type' ), $redirect_url ); + } + } + + } elseif ( is_object($wp_rewrite) && $wp_rewrite->using_permalinks() ) { + // rewriting of old ?p=X, ?m=2004, ?m=200401, ?m=20040101 + if ( is_attachment() && !empty($_GET['attachment_id']) && ! $redirect_url ) { + if ( $redirect_url = get_attachment_link(get_query_var('attachment_id')) ) + $redirect['query'] = remove_query_arg('attachment_id', $redirect['query']); + } elseif ( is_single() && !empty($_GET['p']) && ! $redirect_url ) { + if ( $redirect_url = get_permalink(get_query_var('p')) ) + $redirect['query'] = remove_query_arg(array('p', 'post_type'), $redirect['query']); + } elseif ( is_single() && !empty($_GET['name']) && ! $redirect_url ) { + if ( $redirect_url = get_permalink( $wp_query->get_queried_object_id() ) ) + $redirect['query'] = remove_query_arg('name', $redirect['query']); + } elseif ( is_page() && !empty($_GET['page_id']) && ! $redirect_url ) { + if ( $redirect_url = get_permalink(get_query_var('page_id')) ) + $redirect['query'] = remove_query_arg('page_id', $redirect['query']); + } elseif ( is_page() && !is_feed() && isset($wp_query->queried_object) && 'page' == get_option('show_on_front') && $wp_query->queried_object->ID == get_option('page_on_front') && ! $redirect_url ) { + $redirect_url = home_url('/'); + } elseif ( is_home() && !empty($_GET['page_id']) && 'page' == get_option('show_on_front') && get_query_var('page_id') == get_option('page_for_posts') && ! $redirect_url ) { + if ( $redirect_url = get_permalink(get_option('page_for_posts')) ) + $redirect['query'] = remove_query_arg('page_id', $redirect['query']); + } elseif ( !empty($_GET['m']) && ( is_year() || is_month() || is_day() ) ) { + $m = get_query_var('m'); + switch ( strlen($m) ) { + case 4: // Yearly + $redirect_url = get_year_link($m); + break; + case 6: // Monthly + $redirect_url = get_month_link( substr($m, 0, 4), substr($m, 4, 2) ); + break; + case 8: // Daily + $redirect_url = get_day_link(substr($m, 0, 4), substr($m, 4, 2), substr($m, 6, 2)); + break; + } + if ( $redirect_url ) + $redirect['query'] = remove_query_arg('m', $redirect['query']); + // now moving on to non ?m=X year/month/day links + } elseif ( is_day() && get_query_var('year') && get_query_var('monthnum') && !empty($_GET['day']) ) { + if ( $redirect_url = get_day_link(get_query_var('year'), get_query_var('monthnum'), get_query_var('day')) ) + $redirect['query'] = remove_query_arg(array('year', 'monthnum', 'day'), $redirect['query']); + } elseif ( is_month() && get_query_var('year') && !empty($_GET['monthnum']) ) { + if ( $redirect_url = get_month_link(get_query_var('year'), get_query_var('monthnum')) ) + $redirect['query'] = remove_query_arg(array('year', 'monthnum'), $redirect['query']); + } elseif ( is_year() && !empty($_GET['year']) ) { + if ( $redirect_url = get_year_link(get_query_var('year')) ) + $redirect['query'] = remove_query_arg('year', $redirect['query']); + } elseif ( is_author() && !empty($_GET['author']) && preg_match( '|^[0-9]+$|', $_GET['author'] ) ) { + $author = get_userdata(get_query_var('author')); + if ( ( false !== $author ) && $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE $wpdb->posts.post_author = %d AND $wpdb->posts.post_status = 'publish' LIMIT 1", $author->ID ) ) ) { + if ( $redirect_url = get_author_posts_url($author->ID, $author->user_nicename) ) + $redirect['query'] = remove_query_arg('author', $redirect['query']); + } + } elseif ( is_category() || is_tag() || is_tax() ) { // Terms (Tags/categories) + + $term_count = 0; + foreach ( $wp_query->tax_query->queried_terms as $tax_query ) + $term_count += count( $tax_query['terms'] ); + + $obj = $wp_query->get_queried_object(); + if ( $term_count <= 1 && !empty($obj->term_id) && ( $tax_url = get_term_link((int)$obj->term_id, $obj->taxonomy) ) && !is_wp_error($tax_url) ) { + if ( !empty($redirect['query']) ) { + // Strip taxonomy query vars off the url. + $qv_remove = array( 'term', 'taxonomy'); + if ( is_category() ) { + $qv_remove[] = 'category_name'; + $qv_remove[] = 'cat'; + } elseif ( is_tag() ) { + $qv_remove[] = 'tag'; + $qv_remove[] = 'tag_id'; + } else { // Custom taxonomies will have a custom query var, remove those too: + $tax_obj = get_taxonomy( $obj->taxonomy ); + if ( false !== $tax_obj->query_var ) + $qv_remove[] = $tax_obj->query_var; + } + + $rewrite_vars = array_diff( array_keys($wp_query->query), array_keys($_GET) ); + + if ( !array_diff($rewrite_vars, array_keys($_GET)) ) { // Check to see if all the Query vars are coming from the rewrite, none are set via $_GET + $redirect['query'] = remove_query_arg($qv_remove, $redirect['query']); //Remove all of the per-tax qv's + + // Create the destination url for this taxonomy + $tax_url = parse_url($tax_url); + if ( ! empty($tax_url['query']) ) { // Taxonomy accessible via ?taxonomy=..&term=.. or any custom qv.. + parse_str($tax_url['query'], $query_vars); + $redirect['query'] = add_query_arg($query_vars, $redirect['query']); + } else { // Taxonomy is accessible via a "pretty-URL" + $redirect['path'] = $tax_url['path']; + } + + } else { // Some query vars are set via $_GET. Unset those from $_GET that exist via the rewrite + foreach ( $qv_remove as $_qv ) { + if ( isset($rewrite_vars[$_qv]) ) + $redirect['query'] = remove_query_arg($_qv, $redirect['query']); + } + } + } + + } + } elseif ( is_single() && strpos($wp_rewrite->permalink_structure, '%category%') !== false && $cat = get_query_var( 'category_name' ) ) { + $category = get_category_by_path( $cat ); + $post_terms = wp_get_object_terms($wp_query->get_queried_object_id(), 'category', array('fields' => 'tt_ids')); + if ( (!$category || is_wp_error($category)) || ( !is_wp_error($post_terms) && !empty($post_terms) && !in_array($category->term_taxonomy_id, $post_terms) ) ) + $redirect_url = get_permalink($wp_query->get_queried_object_id()); + } + + // Post Paging + if ( is_singular() && ! is_front_page() && get_query_var('page') ) { + if ( !$redirect_url ) + $redirect_url = get_permalink( get_queried_object_id() ); + $redirect_url = trailingslashit( $redirect_url ) . user_trailingslashit( get_query_var( 'page' ), 'single_paged' ); + $redirect['query'] = remove_query_arg( 'page', $redirect['query'] ); + } + + // paging and feeds + if ( get_query_var('paged') || is_feed() || get_query_var('cpage') ) { + while ( preg_match( "#/$wp_rewrite->pagination_base/?[0-9]+?(/+)?$#", $redirect['path'] ) || preg_match( '#/(comments/?)?(feed|rss|rdf|atom|rss2)(/+)?$#', $redirect['path'] ) || preg_match( '#/comment-page-[0-9]+(/+)?$#', $redirect['path'] ) ) { + // Strip off paging and feed + $redirect['path'] = preg_replace("#/$wp_rewrite->pagination_base/?[0-9]+?(/+)?$#", '/', $redirect['path']); // strip off any existing paging + $redirect['path'] = preg_replace('#/(comments/?)?(feed|rss2?|rdf|atom)(/+|$)#', '/', $redirect['path']); // strip off feed endings + $redirect['path'] = preg_replace('#/comment-page-[0-9]+?(/+)?$#', '/', $redirect['path']); // strip off any existing comment paging + } + + $addl_path = ''; + if ( is_feed() && in_array( get_query_var('feed'), $wp_rewrite->feeds ) ) { + $addl_path = !empty( $addl_path ) ? trailingslashit($addl_path) : ''; + if ( !is_singular() && get_query_var( 'withcomments' ) ) + $addl_path .= 'comments/'; + if ( ( 'rss' == get_default_feed() && 'feed' == get_query_var('feed') ) || 'rss' == get_query_var('feed') ) + $addl_path .= user_trailingslashit( 'feed/' . ( ( get_default_feed() == 'rss2' ) ? '' : 'rss2' ), 'feed' ); + else + $addl_path .= user_trailingslashit( 'feed/' . ( ( get_default_feed() == get_query_var('feed') || 'feed' == get_query_var('feed') ) ? '' : get_query_var('feed') ), 'feed' ); + $redirect['query'] = remove_query_arg( 'feed', $redirect['query'] ); + } elseif ( is_feed() && 'old' == get_query_var('feed') ) { + $old_feed_files = array( + 'wp-atom.php' => 'atom', + 'wp-commentsrss2.php' => 'comments_rss2', + 'wp-feed.php' => get_default_feed(), + 'wp-rdf.php' => 'rdf', + 'wp-rss.php' => 'rss2', + 'wp-rss2.php' => 'rss2', + ); + if ( isset( $old_feed_files[ basename( $redirect['path'] ) ] ) ) { + $redirect_url = get_feed_link( $old_feed_files[ basename( $redirect['path'] ) ] ); + wp_redirect( $redirect_url, 301 ); + die(); + } + } + + if ( get_query_var('paged') > 0 ) { + $paged = get_query_var('paged'); + $redirect['query'] = remove_query_arg( 'paged', $redirect['query'] ); + if ( !is_feed() ) { + if ( $paged > 1 && !is_single() ) { + $addl_path = ( !empty( $addl_path ) ? trailingslashit($addl_path) : '' ) . user_trailingslashit("$wp_rewrite->pagination_base/$paged", 'paged'); + } elseif ( !is_single() ) { + $addl_path = !empty( $addl_path ) ? trailingslashit($addl_path) : ''; + } + } elseif ( $paged > 1 ) { + $redirect['query'] = add_query_arg( 'paged', $paged, $redirect['query'] ); + } + } + + if ( get_option('page_comments') && ( ( 'newest' == get_option('default_comments_page') && get_query_var('cpage') > 0 ) || ( 'newest' != get_option('default_comments_page') && get_query_var('cpage') > 1 ) ) ) { + $addl_path = ( !empty( $addl_path ) ? trailingslashit($addl_path) : '' ) . user_trailingslashit( 'comment-page-' . get_query_var('cpage'), 'commentpaged' ); + $redirect['query'] = remove_query_arg( 'cpage', $redirect['query'] ); + } + + $redirect['path'] = user_trailingslashit( preg_replace('|/' . preg_quote( $wp_rewrite->index, '|' ) . '/?$|', '/', $redirect['path']) ); // strip off trailing /index.php/ + if ( !empty( $addl_path ) && $wp_rewrite->using_index_permalinks() && strpos($redirect['path'], '/' . $wp_rewrite->index . '/') === false ) + $redirect['path'] = trailingslashit($redirect['path']) . $wp_rewrite->index . '/'; + if ( !empty( $addl_path ) ) + $redirect['path'] = trailingslashit($redirect['path']) . $addl_path; + $redirect_url = $redirect['scheme'] . '://' . $redirect['host'] . $redirect['path']; + } + + if ( 'wp-register.php' == basename( $redirect['path'] ) ) { + if ( is_multisite() ) { + /** This filter is documented in wp-login.php */ + $redirect_url = apply_filters( 'wp_signup_location', network_site_url( 'wp-signup.php' ) ); + } else { + $redirect_url = site_url( 'wp-login.php?action=register' ); + } + + wp_redirect( $redirect_url, 301 ); + die(); + } + } + + // tack on any additional query vars + $redirect['query'] = preg_replace( '#^\??&*?#', '', $redirect['query'] ); + if ( $redirect_url && !empty($redirect['query']) ) { + parse_str( $redirect['query'], $_parsed_query ); + $redirect = @parse_url($redirect_url); + + if ( ! empty( $_parsed_query['name'] ) && ! empty( $redirect['query'] ) ) { + parse_str( $redirect['query'], $_parsed_redirect_query ); + + if ( empty( $_parsed_redirect_query['name'] ) ) + unset( $_parsed_query['name'] ); + } + + $_parsed_query = rawurlencode_deep( $_parsed_query ); + $redirect_url = add_query_arg( $_parsed_query, $redirect_url ); + } + + if ( $redirect_url ) + $redirect = @parse_url($redirect_url); + + // www.example.com vs example.com + $user_home = @parse_url(home_url()); + if ( !empty($user_home['host']) ) + $redirect['host'] = $user_home['host']; + if ( empty($user_home['path']) ) + $user_home['path'] = '/'; + + // Handle ports + if ( !empty($user_home['port']) ) + $redirect['port'] = $user_home['port']; + else + unset($redirect['port']); + + // trailing /index.php + $redirect['path'] = preg_replace('|/' . preg_quote( $wp_rewrite->index, '|' ) . '/*?$|', '/', $redirect['path']); + + // Remove trailing spaces from the path + $redirect['path'] = preg_replace( '#(%20| )+$#', '', $redirect['path'] ); + + if ( !empty( $redirect['query'] ) ) { + // Remove trailing spaces from certain terminating query string args + $redirect['query'] = preg_replace( '#((p|page_id|cat|tag)=[^&]*?)(%20| )+$#', '$1', $redirect['query'] ); + + // Clean up empty query strings + $redirect['query'] = trim(preg_replace( '#(^|&)(p|page_id|cat|tag)=?(&|$)#', '&', $redirect['query']), '&'); + + // Redirect obsolete feeds + $redirect['query'] = preg_replace( '#(^|&)feed=rss(&|$)#', '$1feed=rss2$2', $redirect['query'] ); + + // Remove redundant leading ampersands + $redirect['query'] = preg_replace( '#^\??&*?#', '', $redirect['query'] ); + } + + // strip /index.php/ when we're not using PATHINFO permalinks + if ( !$wp_rewrite->using_index_permalinks() ) + $redirect['path'] = str_replace( '/' . $wp_rewrite->index . '/', '/', $redirect['path'] ); + + // trailing slashes + if ( is_object($wp_rewrite) && $wp_rewrite->using_permalinks() && !is_404() && (!is_front_page() || ( is_front_page() && (get_query_var('paged') > 1) ) ) ) { + $user_ts_type = ''; + if ( get_query_var('paged') > 0 ) { + $user_ts_type = 'paged'; + } else { + foreach ( array('single', 'category', 'page', 'day', 'month', 'year', 'home') as $type ) { + $func = 'is_' . $type; + if ( call_user_func($func) ) { + $user_ts_type = $type; + break; + } + } + } + $redirect['path'] = user_trailingslashit($redirect['path'], $user_ts_type); + } elseif ( is_front_page() ) { + $redirect['path'] = trailingslashit($redirect['path']); + } + + // Strip multiple slashes out of the URL + if ( strpos($redirect['path'], '//') > -1 ) + $redirect['path'] = preg_replace('|/+|', '/', $redirect['path']); + + // Always trailing slash the Front Page URL + if ( trailingslashit( $redirect['path'] ) == trailingslashit( $user_home['path'] ) ) + $redirect['path'] = trailingslashit($redirect['path']); + + // Ignore differences in host capitalization, as this can lead to infinite redirects + // Only redirect no-www <=> yes-www + if ( strtolower($original['host']) == strtolower($redirect['host']) || + ( strtolower($original['host']) != 'www.' . strtolower($redirect['host']) && 'www.' . strtolower($original['host']) != strtolower($redirect['host']) ) ) + $redirect['host'] = $original['host']; + + $compare_original = array( $original['host'], $original['path'] ); + + if ( !empty( $original['port'] ) ) + $compare_original[] = $original['port']; + + if ( !empty( $original['query'] ) ) + $compare_original[] = $original['query']; + + $compare_redirect = array( $redirect['host'], $redirect['path'] ); + + if ( !empty( $redirect['port'] ) ) + $compare_redirect[] = $redirect['port']; + + if ( !empty( $redirect['query'] ) ) + $compare_redirect[] = $redirect['query']; + + if ( $compare_original !== $compare_redirect ) { + $redirect_url = $redirect['scheme'] . '://' . $redirect['host']; + if ( !empty($redirect['port']) ) + $redirect_url .= ':' . $redirect['port']; + $redirect_url .= $redirect['path']; + if ( !empty($redirect['query']) ) + $redirect_url .= '?' . $redirect['query']; + } + + if ( !$redirect_url || $redirect_url == $requested_url ) + return false; + + // Hex encoded octets are case-insensitive. + if ( false !== strpos($requested_url, '%') ) { + if ( !function_exists('lowercase_octets') ) { + function lowercase_octets($matches) { + return strtolower( $matches[0] ); + } + } + $requested_url = preg_replace_callback('|%[a-fA-F0-9][a-fA-F0-9]|', 'lowercase_octets', $requested_url); + } + + /** + * Filter the canonical redirect URL. + * + * Returning false to this filter will cancel the redirect. + * + * @since 2.3.0 + * + * @param string $redirect_url The redirect URL. + * @param string $requested_url The requested URL. + */ + $redirect_url = apply_filters( 'redirect_canonical', $redirect_url, $requested_url ); + + if ( !$redirect_url || $redirect_url == $requested_url ) // yes, again -- in case the filter aborted the request + return false; + + if ( $do_redirect ) { + // protect against chained redirects + if ( !redirect_canonical($redirect_url, false) ) { + wp_redirect($redirect_url, 301); + exit(); + } else { + // Debug + // die("1: $redirect_url<br />2: " . redirect_canonical( $redirect_url, false ) ); + return false; + } + } else { + return $redirect_url; + } +} + +/** + * Removes arguments from a query string if they are not present in a URL + * DO NOT use this in plugin code. + * + * @since 3.4.0 + * @access private + * + * @param string $query_string + * @param array $args_to_check + * @param string $url + * @return string The altered query string + */ +function _remove_qs_args_if_not_in_url( $query_string, Array $args_to_check, $url ) { + $parsed_url = @parse_url( $url ); + if ( ! empty( $parsed_url['query'] ) ) { + parse_str( $parsed_url['query'], $parsed_query ); + foreach ( $args_to_check as $qv ) { + if ( !isset( $parsed_query[$qv] ) ) + $query_string = remove_query_arg( $qv, $query_string ); + } + } else { + $query_string = remove_query_arg( $args_to_check, $query_string ); + } + return $query_string; +} + +/** + * Attempts to guess the correct URL based on query vars + * + * @since 2.3.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @return bool|string The correct URL if one is found. False on failure. + */ +function redirect_guess_404_permalink() { + global $wpdb, $wp_rewrite; + + if ( get_query_var('name') ) { + $where = $wpdb->prepare("post_name LIKE %s", $wpdb->esc_like( get_query_var('name') ) . '%'); + + // if any of post_type, year, monthnum, or day are set, use them to refine the query + if ( get_query_var('post_type') ) + $where .= $wpdb->prepare(" AND post_type = %s", get_query_var('post_type')); + else + $where .= " AND post_type IN ('" . implode( "', '", get_post_types( array( 'public' => true ) ) ) . "')"; + + if ( get_query_var('year') ) + $where .= $wpdb->prepare(" AND YEAR(post_date) = %d", get_query_var('year')); + if ( get_query_var('monthnum') ) + $where .= $wpdb->prepare(" AND MONTH(post_date) = %d", get_query_var('monthnum')); + if ( get_query_var('day') ) + $where .= $wpdb->prepare(" AND DAYOFMONTH(post_date) = %d", get_query_var('day')); + + $post_id = $wpdb->get_var("SELECT ID FROM $wpdb->posts WHERE $where AND post_status = 'publish'"); + if ( ! $post_id ) + return false; + if ( get_query_var( 'feed' ) ) + return get_post_comments_feed_link( $post_id, get_query_var( 'feed' ) ); + elseif ( get_query_var( 'page' ) ) + return trailingslashit( get_permalink( $post_id ) ) . user_trailingslashit( get_query_var( 'page' ), 'single_paged' ); + else + return get_permalink( $post_id ); + } + + return false; +} + +add_action('template_redirect', 'redirect_canonical'); + +function wp_redirect_admin_locations() { + global $wp_rewrite; + if ( ! ( is_404() && $wp_rewrite->using_permalinks() ) ) + return; + + $admins = array( + home_url( 'wp-admin', 'relative' ), + home_url( 'dashboard', 'relative' ), + home_url( 'admin', 'relative' ), + site_url( 'dashboard', 'relative' ), + site_url( 'admin', 'relative' ), + ); + if ( in_array( untrailingslashit( $_SERVER['REQUEST_URI'] ), $admins ) ) { + wp_redirect( admin_url() ); + exit; + } + + $logins = array( + home_url( 'wp-login.php', 'relative' ), + home_url( 'login', 'relative' ), + site_url( 'login', 'relative' ), + ); + if ( in_array( untrailingslashit( $_SERVER['REQUEST_URI'] ), $logins ) ) { + wp_redirect( site_url( 'wp-login.php', 'login' ) ); + exit; + } +} + +add_action( 'template_redirect', 'wp_redirect_admin_locations', 1000 ); diff --git a/wp-includes/capabilities.php b/wp-includes/capabilities.php new file mode 100644 index 0000000..894a149 --- /dev/null +++ b/wp-includes/capabilities.php @@ -0,0 +1,1542 @@ +<?php +/** + * WordPress Roles and Capabilities. + * + * @package WordPress + * @subpackage User + */ + +/** + * WordPress User Roles. + * + * The role option is simple, the structure is organized by role name that store + * the name in value of the 'name' key. The capabilities are stored as an array + * in the value of the 'capability' key. + * + * array ( + * 'rolename' => array ( + * 'name' => 'rolename', + * 'capabilities' => array() + * ) + * ) + * + * @since 2.0.0 + * @package WordPress + * @subpackage User + */ +class WP_Roles { + /** + * List of roles and capabilities. + * + * @since 2.0.0 + * @access public + * @var array + */ + public $roles; + + /** + * List of the role objects. + * + * @since 2.0.0 + * @access public + * @var array + */ + public $role_objects = array(); + + /** + * List of role names. + * + * @since 2.0.0 + * @access public + * @var array + */ + public $role_names = array(); + + /** + * Option name for storing role list. + * + * @since 2.0.0 + * @access public + * @var string + */ + public $role_key; + + /** + * Whether to use the database for retrieval and storage. + * + * @since 2.1.0 + * @access public + * @var bool + */ + public $use_db = true; + + /** + * Constructor + * + * @since 2.0.0 + */ + public function __construct() { + $this->_init(); + } + + /** + * Make private/protected methods readable for backwards compatibility. + * + * @since 4.0.0 + * @access public + * + * @param callable $name Method to call. + * @param array $arguments Arguments to pass when calling. + * @return mixed|bool Return value of the callback, false otherwise. + */ + public function __call( $name, $arguments ) { + return call_user_func_array( array( $this, $name ), $arguments ); + } + + /** + * Set up the object properties. + * + * The role key is set to the current prefix for the $wpdb object with + * 'user_roles' appended. If the $wp_user_roles global is set, then it will + * be used and the role option will not be updated or used. + * + * @since 2.1.0 + * @access protected + * + * @global wpdb $wpdb WordPress database abstraction object. + * @global array $wp_user_roles Used to set the 'roles' property value. + */ + protected function _init() { + global $wpdb, $wp_user_roles; + $this->role_key = $wpdb->get_blog_prefix() . 'user_roles'; + if ( ! empty( $wp_user_roles ) ) { + $this->roles = $wp_user_roles; + $this->use_db = false; + } else { + $this->roles = get_option( $this->role_key ); + } + + if ( empty( $this->roles ) ) + return; + + $this->role_objects = array(); + $this->role_names = array(); + foreach ( array_keys( $this->roles ) as $role ) { + $this->role_objects[$role] = new WP_Role( $role, $this->roles[$role]['capabilities'] ); + $this->role_names[$role] = $this->roles[$role]['name']; + } + } + + /** + * Reinitialize the object + * + * Recreates the role objects. This is typically called only by switch_to_blog() + * after switching wpdb to a new blog ID. + * + * @since 3.5.0 + * @access public + */ + public function reinit() { + // There is no need to reinit if using the wp_user_roles global. + if ( ! $this->use_db ) + return; + + global $wpdb; + + // Duplicated from _init() to avoid an extra function call. + $this->role_key = $wpdb->get_blog_prefix() . 'user_roles'; + $this->roles = get_option( $this->role_key ); + if ( empty( $this->roles ) ) + return; + + $this->role_objects = array(); + $this->role_names = array(); + foreach ( array_keys( $this->roles ) as $role ) { + $this->role_objects[$role] = new WP_Role( $role, $this->roles[$role]['capabilities'] ); + $this->role_names[$role] = $this->roles[$role]['name']; + } + } + + /** + * Add role name with capabilities to list. + * + * Updates the list of roles, if the role doesn't already exist. + * + * The capabilities are defined in the following format `array( 'read' => true );` + * To explicitly deny a role a capability you set the value for that capability to false. + * + * @since 2.0.0 + * @access public + * + * @param string $role Role name. + * @param string $display_name Role display name. + * @param array $capabilities List of role capabilities in the above format. + * @return WP_Role|null WP_Role object if role is added, null if already exists. + */ + public function add_role( $role, $display_name, $capabilities = array() ) { + if ( isset( $this->roles[$role] ) ) + return; + + $this->roles[$role] = array( + 'name' => $display_name, + 'capabilities' => $capabilities + ); + if ( $this->use_db ) + update_option( $this->role_key, $this->roles ); + $this->role_objects[$role] = new WP_Role( $role, $capabilities ); + $this->role_names[$role] = $display_name; + return $this->role_objects[$role]; + } + + /** + * Remove role by name. + * + * @since 2.0.0 + * @access public + * + * @param string $role Role name. + */ + public function remove_role( $role ) { + if ( ! isset( $this->role_objects[$role] ) ) + return; + + unset( $this->role_objects[$role] ); + unset( $this->role_names[$role] ); + unset( $this->roles[$role] ); + + if ( $this->use_db ) + update_option( $this->role_key, $this->roles ); + + if ( get_option( 'default_role' ) == $role ) + update_option( 'default_role', 'subscriber' ); + } + + /** + * Add capability to role. + * + * @since 2.0.0 + * @access public + * + * @param string $role Role name. + * @param string $cap Capability name. + * @param bool $grant Optional, default is true. Whether role is capable of performing capability. + */ + public function add_cap( $role, $cap, $grant = true ) { + if ( ! isset( $this->roles[$role] ) ) + return; + + $this->roles[$role]['capabilities'][$cap] = $grant; + if ( $this->use_db ) + update_option( $this->role_key, $this->roles ); + } + + /** + * Remove capability from role. + * + * @since 2.0.0 + * @access public + * + * @param string $role Role name. + * @param string $cap Capability name. + */ + public function remove_cap( $role, $cap ) { + if ( ! isset( $this->roles[$role] ) ) + return; + + unset( $this->roles[$role]['capabilities'][$cap] ); + if ( $this->use_db ) + update_option( $this->role_key, $this->roles ); + } + + /** + * Retrieve role object by name. + * + * @since 2.0.0 + * @access public + * + * @param string $role Role name. + * @return WP_Role|null WP_Role object if found, null if the role does not exist. + */ + public function get_role( $role ) { + if ( isset( $this->role_objects[$role] ) ) + return $this->role_objects[$role]; + else + return null; + } + + /** + * Retrieve list of role names. + * + * @since 2.0.0 + * @access public + * + * @return array List of role names. + */ + public function get_names() { + return $this->role_names; + } + + /** + * Whether role name is currently in the list of available roles. + * + * @since 2.0.0 + * @access public + * + * @param string $role Role name to look up. + * @return bool + */ + public function is_role( $role ) { + return isset( $this->role_names[$role] ); + } +} + +/** + * WordPress Role class. + * + * @since 2.0.0 + * @package WordPress + * @subpackage User + */ +class WP_Role { + /** + * Role name. + * + * @since 2.0.0 + * @access public + * @var string + */ + public $name; + + /** + * List of capabilities the role contains. + * + * @since 2.0.0 + * @access public + * @var array + */ + public $capabilities; + + /** + * Constructor - Set up object properties. + * + * The list of capabilities, must have the key as the name of the capability + * and the value a boolean of whether it is granted to the role. + * + * @since 2.0.0 + * @access public + * + * @param string $role Role name. + * @param array $capabilities List of capabilities. + */ + public function __construct( $role, $capabilities ) { + $this->name = $role; + $this->capabilities = $capabilities; + } + + /** + * Assign role a capability. + * + * @see WP_Roles::add_cap() Method uses implementation for role. + * @since 2.0.0 + * @access public + * + * @param string $cap Capability name. + * @param bool $grant Whether role has capability privilege. + */ + public function add_cap( $cap, $grant = true ) { + global $wp_roles; + + if ( ! isset( $wp_roles ) ) + $wp_roles = new WP_Roles(); + + $this->capabilities[$cap] = $grant; + $wp_roles->add_cap( $this->name, $cap, $grant ); + } + + /** + * Remove capability from role. + * + * This is a container for {@link WP_Roles::remove_cap()} to remove the + * capability from the role. That is to say, that {@link + * WP_Roles::remove_cap()} implements the functionality, but it also makes + * sense to use this class, because you don't need to enter the role name. + * + * @since 2.0.0 + * @access public + * + * @param string $cap Capability name. + */ + public function remove_cap( $cap ) { + global $wp_roles; + + if ( ! isset( $wp_roles ) ) + $wp_roles = new WP_Roles(); + + unset( $this->capabilities[$cap] ); + $wp_roles->remove_cap( $this->name, $cap ); + } + + /** + * Whether role has capability. + * + * The capabilities is passed through the 'role_has_cap' filter. The first + * parameter for the hook is the list of capabilities the class has + * assigned. The second parameter is the capability name to look for. The + * third and final parameter for the hook is the role name. + * + * @since 2.0.0 + * @access public + * + * @param string $cap Capability name. + * @return bool True, if user has capability. False, if doesn't have capability. + */ + public function has_cap( $cap ) { + /** + * Filter which capabilities a role has. + * + * @since 2.0.0 + * + * @param array $capabilities Array of role capabilities. + * @param string $cap Capability name. + * @param string $name Role name. + */ + $capabilities = apply_filters( 'role_has_cap', $this->capabilities, $cap, $this->name ); + if ( !empty( $capabilities[$cap] ) ) + return $capabilities[$cap]; + else + return false; + } + +} + +/** + * WordPress User class. + * + * @since 2.0.0 + * @package WordPress + * @subpackage User + */ +class WP_User { + /** + * User data container. + * + * @since 2.0.0 + * @access private + * @var array + */ + var $data; + + /** + * The user's ID. + * + * @since 2.1.0 + * @access public + * @var int + */ + public $ID = 0; + + /** + * The individual capabilities the user has been given. + * + * @since 2.0.0 + * @access public + * @var array + */ + public $caps = array(); + + /** + * User metadata option name. + * + * @since 2.0.0 + * @access public + * @var string + */ + public $cap_key; + + /** + * The roles the user is part of. + * + * @since 2.0.0 + * @access public + * @var array + */ + public $roles = array(); + + /** + * All capabilities the user has, including individual and role based. + * + * @since 2.0.0 + * @access public + * @var array + */ + public $allcaps = array(); + + /** + * The filter context applied to user data fields. + * + * @since 2.9.0 + * @access private + * @var string + */ + var $filter = null; + + private static $back_compat_keys; + + /** + * Constructor + * + * Retrieves the userdata and passes it to {@link WP_User::init()}. + * + * @since 2.0.0 + * @access public + * + * @param int|string|stdClass|WP_User $id User's ID, a WP_User object, or a user object from the DB. + * @param string $name Optional. User's username + * @param int $blog_id Optional Blog ID, defaults to current blog. + * @return WP_User + */ + public function __construct( $id = 0, $name = '', $blog_id = '' ) { + if ( ! isset( self::$back_compat_keys ) ) { + $prefix = $GLOBALS['wpdb']->prefix; + self::$back_compat_keys = array( + 'user_firstname' => 'first_name', + 'user_lastname' => 'last_name', + 'user_description' => 'description', + 'user_level' => $prefix . 'user_level', + $prefix . 'usersettings' => $prefix . 'user-settings', + $prefix . 'usersettingstime' => $prefix . 'user-settings-time', + ); + } + + if ( is_a( $id, 'WP_User' ) ) { + $this->init( $id->data, $blog_id ); + return; + } elseif ( is_object( $id ) ) { + $this->init( $id, $blog_id ); + return; + } + + if ( ! empty( $id ) && ! is_numeric( $id ) ) { + $name = $id; + $id = 0; + } + + if ( $id ) + $data = self::get_data_by( 'id', $id ); + else + $data = self::get_data_by( 'login', $name ); + + if ( $data ) + $this->init( $data, $blog_id ); + } + + /** + * Sets up object properties, including capabilities. + * + * @param object $data User DB row object + * @param int $blog_id Optional. The blog id to initialize for + */ + public function init( $data, $blog_id = '' ) { + $this->data = $data; + $this->ID = (int) $data->ID; + + $this->for_blog( $blog_id ); + } + + /** + * Return only the main user fields + * + * @since 3.3.0 + * + * @param string $field The field to query against: 'id', 'slug', 'email' or 'login' + * @param string|int $value The field value + * @return object Raw user object + */ + public static function get_data_by( $field, $value ) { + global $wpdb; + + if ( 'id' == $field ) { + // Make sure the value is numeric to avoid casting objects, for example, + // to int 1. + if ( ! is_numeric( $value ) ) + return false; + $value = intval( $value ); + if ( $value < 1 ) + return false; + } else { + $value = trim( $value ); + } + + if ( !$value ) + return false; + + switch ( $field ) { + case 'id': + $user_id = $value; + $db_field = 'ID'; + break; + case 'slug': + $user_id = wp_cache_get($value, 'userslugs'); + $db_field = 'user_nicename'; + break; + case 'email': + $user_id = wp_cache_get($value, 'useremail'); + $db_field = 'user_email'; + break; + case 'login': + $value = sanitize_user( $value ); + $user_id = wp_cache_get($value, 'userlogins'); + $db_field = 'user_login'; + break; + default: + return false; + } + + if ( false !== $user_id ) { + if ( $user = wp_cache_get( $user_id, 'users' ) ) + return $user; + } + + if ( !$user = $wpdb->get_row( $wpdb->prepare( + "SELECT * FROM $wpdb->users WHERE $db_field = %s", $value + ) ) ) + return false; + + update_user_caches( $user ); + + return $user; + } + + /** + * Magic method for checking the existence of a certain custom field + * + * @since 3.3.0 + * @param string $key + * @return bool + */ + public function __isset( $key ) { + if ( 'id' == $key ) { + _deprecated_argument( 'WP_User->id', '2.1', __( 'Use <code>WP_User->ID</code> instead.' ) ); + $key = 'ID'; + } + + if ( isset( $this->data->$key ) ) + return true; + + if ( isset( self::$back_compat_keys[ $key ] ) ) + $key = self::$back_compat_keys[ $key ]; + + return metadata_exists( 'user', $this->ID, $key ); + } + + /** + * Magic method for accessing custom fields + * + * @since 3.3.0 + * @param string $key + * @return mixed + */ + public function __get( $key ) { + if ( 'id' == $key ) { + _deprecated_argument( 'WP_User->id', '2.1', __( 'Use <code>WP_User->ID</code> instead.' ) ); + return $this->ID; + } + + if ( isset( $this->data->$key ) ) { + $value = $this->data->$key; + } else { + if ( isset( self::$back_compat_keys[ $key ] ) ) + $key = self::$back_compat_keys[ $key ]; + $value = get_user_meta( $this->ID, $key, true ); + } + + if ( $this->filter ) { + $value = sanitize_user_field( $key, $value, $this->ID, $this->filter ); + } + + return $value; + } + + /** + * Magic method for setting custom fields + * + * @since 3.3.0 + */ + public function __set( $key, $value ) { + if ( 'id' == $key ) { + _deprecated_argument( 'WP_User->id', '2.1', __( 'Use <code>WP_User->ID</code> instead.' ) ); + $this->ID = $value; + return; + } + + $this->data->$key = $value; + } + + /** + * Determine whether the user exists in the database. + * + * @since 3.4.0 + * @access public + * + * @return bool True if user exists in the database, false if not. + */ + public function exists() { + return ! empty( $this->ID ); + } + + /** + * Retrieve the value of a property or meta key. + * + * Retrieves from the users and usermeta table. + * + * @since 3.3.0 + * + * @param string $key Property + */ + public function get( $key ) { + return $this->__get( $key ); + } + + /** + * Determine whether a property or meta key is set + * + * Consults the users and usermeta tables. + * + * @since 3.3.0 + * + * @param string $key Property + */ + public function has_prop( $key ) { + return $this->__isset( $key ); + } + + /** + * Return an array representation. + * + * @since 3.5.0 + * + * @return array Array representation. + */ + public function to_array() { + return get_object_vars( $this->data ); + } + + /** + * Set up capability object properties. + * + * Will set the value for the 'cap_key' property to current database table + * prefix, followed by 'capabilities'. Will then check to see if the + * property matching the 'cap_key' exists and is an array. If so, it will be + * used. + * + * @access protected + * @since 2.1.0 + * + * @param string $cap_key Optional capability key + */ + function _init_caps( $cap_key = '' ) { + global $wpdb; + + if ( empty($cap_key) ) + $this->cap_key = $wpdb->get_blog_prefix() . 'capabilities'; + else + $this->cap_key = $cap_key; + + $this->caps = get_user_meta( $this->ID, $this->cap_key, true ); + + if ( ! is_array( $this->caps ) ) + $this->caps = array(); + + $this->get_role_caps(); + } + + /** + * Retrieve all of the role capabilities and merge with individual capabilities. + * + * All of the capabilities of the roles the user belongs to are merged with + * the users individual roles. This also means that the user can be denied + * specific roles that their role might have, but the specific user isn't + * granted permission to. + * + * @since 2.0.0 + * @uses $wp_roles + * @access public + * + * @return array List of all capabilities for the user. + */ + public function get_role_caps() { + global $wp_roles; + + if ( ! isset( $wp_roles ) ) + $wp_roles = new WP_Roles(); + + //Filter out caps that are not role names and assign to $this->roles + if ( is_array( $this->caps ) ) + $this->roles = array_filter( array_keys( $this->caps ), array( $wp_roles, 'is_role' ) ); + + //Build $allcaps from role caps, overlay user's $caps + $this->allcaps = array(); + foreach ( (array) $this->roles as $role ) { + $the_role = $wp_roles->get_role( $role ); + $this->allcaps = array_merge( (array) $this->allcaps, (array) $the_role->capabilities ); + } + $this->allcaps = array_merge( (array) $this->allcaps, (array) $this->caps ); + + return $this->allcaps; + } + + /** + * Add role to user. + * + * Updates the user's meta data option with capabilities and roles. + * + * @since 2.0.0 + * @access public + * + * @param string $role Role name. + */ + public function add_role( $role ) { + $this->caps[$role] = true; + update_user_meta( $this->ID, $this->cap_key, $this->caps ); + $this->get_role_caps(); + $this->update_user_level_from_caps(); + } + + /** + * Remove role from user. + * + * @since 2.0.0 + * @access public + * + * @param string $role Role name. + */ + public function remove_role( $role ) { + if ( !in_array($role, $this->roles) ) + return; + unset( $this->caps[$role] ); + update_user_meta( $this->ID, $this->cap_key, $this->caps ); + $this->get_role_caps(); + $this->update_user_level_from_caps(); + } + + /** + * Set the role of the user. + * + * This will remove the previous roles of the user and assign the user the + * new one. You can set the role to an empty string and it will remove all + * of the roles from the user. + * + * @since 2.0.0 + * @access public + * + * @param string $role Role name. + */ + public function set_role( $role ) { + if ( 1 == count( $this->roles ) && $role == current( $this->roles ) ) + return; + + foreach ( (array) $this->roles as $oldrole ) + unset( $this->caps[$oldrole] ); + + $old_roles = $this->roles; + if ( !empty( $role ) ) { + $this->caps[$role] = true; + $this->roles = array( $role => true ); + } else { + $this->roles = false; + } + update_user_meta( $this->ID, $this->cap_key, $this->caps ); + $this->get_role_caps(); + $this->update_user_level_from_caps(); + + /** + * Fires after the user's role has changed. + * + * @since 2.9.0 + * @since 3.6.0 Added $old_roles to include an array of the user's previous roles. + * + * @param int $user_id The user ID. + * @param string $role The new role. + * @param array $old_roles An array of the user's previous roles. + */ + do_action( 'set_user_role', $this->ID, $role, $old_roles ); + } + + /** + * Choose the maximum level the user has. + * + * Will compare the level from the $item parameter against the $max + * parameter. If the item is incorrect, then just the $max parameter value + * will be returned. + * + * Used to get the max level based on the capabilities the user has. This + * is also based on roles, so if the user is assigned the Administrator role + * then the capability 'level_10' will exist and the user will get that + * value. + * + * @since 2.0.0 + * @access public + * + * @param int $max Max level of user. + * @param string $item Level capability name. + * @return int Max Level. + */ + public function level_reduction( $max, $item ) { + if ( preg_match( '/^level_(10|[0-9])$/i', $item, $matches ) ) { + $level = intval( $matches[1] ); + return max( $max, $level ); + } else { + return $max; + } + } + + /** + * Update the maximum user level for the user. + * + * Updates the 'user_level' user metadata (includes prefix that is the + * database table prefix) with the maximum user level. Gets the value from + * the all of the capabilities that the user has. + * + * @since 2.0.0 + * @access public + */ + public function update_user_level_from_caps() { + global $wpdb; + $this->user_level = array_reduce( array_keys( $this->allcaps ), array( $this, 'level_reduction' ), 0 ); + update_user_meta( $this->ID, $wpdb->get_blog_prefix() . 'user_level', $this->user_level ); + } + + /** + * Add capability and grant or deny access to capability. + * + * @since 2.0.0 + * @access public + * + * @param string $cap Capability name. + * @param bool $grant Whether to grant capability to user. + */ + public function add_cap( $cap, $grant = true ) { + $this->caps[$cap] = $grant; + update_user_meta( $this->ID, $this->cap_key, $this->caps ); + } + + /** + * Remove capability from user. + * + * @since 2.0.0 + * @access public + * + * @param string $cap Capability name. + */ + public function remove_cap( $cap ) { + if ( ! isset( $this->caps[$cap] ) ) + return; + unset( $this->caps[$cap] ); + update_user_meta( $this->ID, $this->cap_key, $this->caps ); + } + + /** + * Remove all of the capabilities of the user. + * + * @since 2.1.0 + * @access public + */ + public function remove_all_caps() { + global $wpdb; + $this->caps = array(); + delete_user_meta( $this->ID, $this->cap_key ); + delete_user_meta( $this->ID, $wpdb->get_blog_prefix() . 'user_level' ); + $this->get_role_caps(); + } + + /** + * Whether user has capability or role name. + * + * This is useful for looking up whether the user has a specific role + * assigned to the user. The second optional parameter can also be used to + * check for capabilities against a specific object, such as a post or user. + * + * @since 2.0.0 + * @access public + * + * @param string|int $cap Capability or role name to search. + * @return bool True, if user has capability; false, if user does not have capability. + */ + public function has_cap( $cap ) { + if ( is_numeric( $cap ) ) { + _deprecated_argument( __FUNCTION__, '2.0', __('Usage of user levels by plugins and themes is deprecated. Use roles and capabilities instead.') ); + $cap = $this->translate_level_to_cap( $cap ); + } + + $args = array_slice( func_get_args(), 1 ); + $args = array_merge( array( $cap, $this->ID ), $args ); + $caps = call_user_func_array( 'map_meta_cap', $args ); + + // Multisite super admin has all caps by definition, Unless specifically denied. + if ( is_multisite() && is_super_admin( $this->ID ) ) { + if ( in_array('do_not_allow', $caps) ) + return false; + return true; + } + + /** + * Dynamically filter a user's capabilities. + * + * @since 2.0.0 + * @since 3.7.0 Added the user object. + * + * @param array $allcaps An array of all the role's capabilities. + * @param array $caps Actual capabilities for meta capability. + * @param array $args Optional parameters passed to has_cap(), typically object ID. + * @param WP_User $user The user object. + */ + // Must have ALL requested caps + $capabilities = apply_filters( 'user_has_cap', $this->allcaps, $caps, $args, $this ); + $capabilities['exist'] = true; // Everyone is allowed to exist + foreach ( (array) $caps as $cap ) { + if ( empty( $capabilities[ $cap ] ) ) + return false; + } + + return true; + } + + /** + * Convert numeric level to level capability name. + * + * Prepends 'level_' to level number. + * + * @since 2.0.0 + * @access public + * + * @param int $level Level number, 1 to 10. + * @return string + */ + public function translate_level_to_cap( $level ) { + return 'level_' . $level; + } + + /** + * Set the blog to operate on. Defaults to the current blog. + * + * @since 3.0.0 + * + * @param int $blog_id Optional Blog ID, defaults to current blog. + */ + public function for_blog( $blog_id = '' ) { + global $wpdb; + if ( ! empty( $blog_id ) ) + $cap_key = $wpdb->get_blog_prefix( $blog_id ) . 'capabilities'; + else + $cap_key = ''; + $this->_init_caps( $cap_key ); + } +} + +/** + * Map meta capabilities to primitive capabilities. + * + * This does not actually compare whether the user ID has the actual capability, + * just what the capability or capabilities are. Meta capability list value can + * be 'delete_user', 'edit_user', 'remove_user', 'promote_user', 'delete_post', + * 'delete_page', 'edit_post', 'edit_page', 'read_post', or 'read_page'. + * + * @since 2.0.0 + * + * @param string $cap Capability name. + * @param int $user_id User ID. + * @return array Actual capabilities for meta capability. + */ +function map_meta_cap( $cap, $user_id ) { + $args = array_slice( func_get_args(), 2 ); + $caps = array(); + + switch ( $cap ) { + case 'remove_user': + $caps[] = 'remove_users'; + break; + case 'promote_user': + $caps[] = 'promote_users'; + break; + case 'edit_user': + case 'edit_users': + // Allow user to edit itself + if ( 'edit_user' == $cap && isset( $args[0] ) && $user_id == $args[0] ) + break; + + // If multisite these caps are allowed only for super admins. + if ( is_multisite() && !is_super_admin( $user_id ) ) + $caps[] = 'do_not_allow'; + else + $caps[] = 'edit_users'; // edit_user maps to edit_users. + break; + case 'delete_post': + case 'delete_page': + $post = get_post( $args[0] ); + + if ( 'revision' == $post->post_type ) { + $post = get_post( $post->post_parent ); + } + + $post_type = get_post_type_object( $post->post_type ); + + if ( ! $post_type->map_meta_cap ) { + $caps[] = $post_type->cap->$cap; + // Prior to 3.1 we would re-call map_meta_cap here. + if ( 'delete_post' == $cap ) + $cap = $post_type->cap->$cap; + break; + } + + // If the post author is set and the user is the author... + if ( $post->post_author && $user_id == $post->post_author ) { + // If the post is published... + if ( 'publish' == $post->post_status ) { + $caps[] = $post_type->cap->delete_published_posts; + } elseif ( 'trash' == $post->post_status ) { + if ( 'publish' == get_post_meta( $post->ID, '_wp_trash_meta_status', true ) ) { + $caps[] = $post_type->cap->delete_published_posts; + } + } else { + // If the post is draft... + $caps[] = $post_type->cap->delete_posts; + } + } else { + // The user is trying to edit someone else's post. + $caps[] = $post_type->cap->delete_others_posts; + // The post is published, extra cap required. + if ( 'publish' == $post->post_status ) { + $caps[] = $post_type->cap->delete_published_posts; + } elseif ( 'private' == $post->post_status ) { + $caps[] = $post_type->cap->delete_private_posts; + } + } + break; + // edit_post breaks down to edit_posts, edit_published_posts, or + // edit_others_posts + case 'edit_post': + case 'edit_page': + $post = get_post( $args[0] ); + if ( empty( $post ) ) + break; + + if ( 'revision' == $post->post_type ) { + $post = get_post( $post->post_parent ); + } + + $post_type = get_post_type_object( $post->post_type ); + + if ( ! $post_type->map_meta_cap ) { + $caps[] = $post_type->cap->$cap; + // Prior to 3.1 we would re-call map_meta_cap here. + if ( 'edit_post' == $cap ) + $cap = $post_type->cap->$cap; + break; + } + + // If the post author is set and the user is the author... + if ( $post->post_author && $user_id == $post->post_author ) { + // If the post is published... + if ( 'publish' == $post->post_status ) { + $caps[] = $post_type->cap->edit_published_posts; + } elseif ( 'trash' == $post->post_status ) { + if ( 'publish' == get_post_meta( $post->ID, '_wp_trash_meta_status', true ) ) { + $caps[] = $post_type->cap->edit_published_posts; + } + } else { + // If the post is draft... + $caps[] = $post_type->cap->edit_posts; + } + } else { + // The user is trying to edit someone else's post. + $caps[] = $post_type->cap->edit_others_posts; + // The post is published, extra cap required. + if ( 'publish' == $post->post_status ) { + $caps[] = $post_type->cap->edit_published_posts; + } elseif ( 'private' == $post->post_status ) { + $caps[] = $post_type->cap->edit_private_posts; + } + } + break; + case 'read_post': + case 'read_page': + $post = get_post( $args[0] ); + + if ( 'revision' == $post->post_type ) { + $post = get_post( $post->post_parent ); + } + + $post_type = get_post_type_object( $post->post_type ); + + if ( ! $post_type->map_meta_cap ) { + $caps[] = $post_type->cap->$cap; + // Prior to 3.1 we would re-call map_meta_cap here. + if ( 'read_post' == $cap ) + $cap = $post_type->cap->$cap; + break; + } + + $status_obj = get_post_status_object( $post->post_status ); + if ( $status_obj->public ) { + $caps[] = $post_type->cap->read; + break; + } + + if ( $post->post_author && $user_id == $post->post_author ) { + $caps[] = $post_type->cap->read; + } elseif ( $status_obj->private ) { + $caps[] = $post_type->cap->read_private_posts; + } else { + $caps = map_meta_cap( 'edit_post', $user_id, $post->ID ); + } + break; + case 'publish_post': + $post = get_post( $args[0] ); + $post_type = get_post_type_object( $post->post_type ); + + $caps[] = $post_type->cap->publish_posts; + break; + case 'edit_post_meta': + case 'delete_post_meta': + case 'add_post_meta': + $post = get_post( $args[0] ); + $caps = map_meta_cap( 'edit_post', $user_id, $post->ID ); + + $meta_key = isset( $args[ 1 ] ) ? $args[ 1 ] : false; + + if ( $meta_key && has_filter( "auth_post_meta_{$meta_key}" ) ) { + /** + * Filter whether the user is allowed to add post meta to a post. + * + * The dynamic portion of the hook name, `$meta_key`, refers to the + * meta key passed to {@see map_meta_cap()}. + * + * @since 3.3.0 + * + * @param bool $allowed Whether the user can add the post meta. Default false. + * @param string $meta_key The meta key. + * @param int $post_id Post ID. + * @param int $user_id User ID. + * @param string $cap Capability name. + * @param array $caps User capabilities. + */ + $allowed = apply_filters( "auth_post_meta_{$meta_key}", false, $meta_key, $post->ID, $user_id, $cap, $caps ); + if ( ! $allowed ) + $caps[] = $cap; + } elseif ( $meta_key && is_protected_meta( $meta_key, 'post' ) ) { + $caps[] = $cap; + } + break; + case 'edit_comment': + $comment = get_comment( $args[0] ); + if ( empty( $comment ) ) + break; + $post = get_post( $comment->comment_post_ID ); + $caps = map_meta_cap( 'edit_post', $user_id, $post->ID ); + break; + case 'unfiltered_upload': + if ( defined('ALLOW_UNFILTERED_UPLOADS') && ALLOW_UNFILTERED_UPLOADS && ( !is_multisite() || is_super_admin( $user_id ) ) ) + $caps[] = $cap; + else + $caps[] = 'do_not_allow'; + break; + case 'unfiltered_html' : + // Disallow unfiltered_html for all users, even admins and super admins. + if ( defined( 'DISALLOW_UNFILTERED_HTML' ) && DISALLOW_UNFILTERED_HTML ) + $caps[] = 'do_not_allow'; + elseif ( is_multisite() && ! is_super_admin( $user_id ) ) + $caps[] = 'do_not_allow'; + else + $caps[] = $cap; + break; + case 'edit_files': + case 'edit_plugins': + case 'edit_themes': + // Disallow the file editors. + if ( defined( 'DISALLOW_FILE_EDIT' ) && DISALLOW_FILE_EDIT ) + $caps[] = 'do_not_allow'; + elseif ( defined( 'DISALLOW_FILE_MODS' ) && DISALLOW_FILE_MODS ) + $caps[] = 'do_not_allow'; + elseif ( is_multisite() && ! is_super_admin( $user_id ) ) + $caps[] = 'do_not_allow'; + else + $caps[] = $cap; + break; + case 'update_plugins': + case 'delete_plugins': + case 'install_plugins': + case 'upload_plugins': + case 'update_themes': + case 'delete_themes': + case 'install_themes': + case 'upload_themes': + case 'update_core': + // Disallow anything that creates, deletes, or updates core, plugin, or theme files. + // Files in uploads are excepted. + if ( defined( 'DISALLOW_FILE_MODS' ) && DISALLOW_FILE_MODS ) { + $caps[] = 'do_not_allow'; + } elseif ( is_multisite() && ! is_super_admin( $user_id ) ) { + $caps[] = 'do_not_allow'; + } elseif ( 'upload_themes' === $cap ) { + $caps[] = 'install_themes'; + } elseif ( 'upload_plugins' === $cap ) { + $caps[] = 'install_plugins'; + } else { + $caps[] = $cap; + } + break; + case 'activate_plugins': + $caps[] = $cap; + if ( is_multisite() ) { + // update_, install_, and delete_ are handled above with is_super_admin(). + $menu_perms = get_site_option( 'menu_items', array() ); + if ( empty( $menu_perms['plugins'] ) ) + $caps[] = 'manage_network_plugins'; + } + break; + case 'delete_user': + case 'delete_users': + // If multisite only super admins can delete users. + if ( is_multisite() && ! is_super_admin( $user_id ) ) + $caps[] = 'do_not_allow'; + else + $caps[] = 'delete_users'; // delete_user maps to delete_users. + break; + case 'create_users': + if ( !is_multisite() ) + $caps[] = $cap; + elseif ( is_super_admin() || get_site_option( 'add_new_users' ) ) + $caps[] = $cap; + else + $caps[] = 'do_not_allow'; + break; + case 'manage_links' : + if ( get_option( 'link_manager_enabled' ) ) + $caps[] = $cap; + else + $caps[] = 'do_not_allow'; + break; + case 'customize' : + $caps[] = 'edit_theme_options'; + break; + default: + // Handle meta capabilities for custom post types. + $post_type_meta_caps = _post_type_meta_capabilities(); + if ( isset( $post_type_meta_caps[ $cap ] ) ) { + $args = array_merge( array( $post_type_meta_caps[ $cap ], $user_id ), $args ); + return call_user_func_array( 'map_meta_cap', $args ); + } + + // If no meta caps match, return the original cap. + $caps[] = $cap; + } + + /** + * Filter a user's capabilities depending on specific context and/or privilege. + * + * @since 2.8.0 + * + * @param array $caps Returns the user's actual capabilities. + * @param string $cap Capability name. + * @param int $user_id The user ID. + * @param array $args Adds the context to the cap. Typically the object ID. + */ + return apply_filters( 'map_meta_cap', $caps, $cap, $user_id, $args ); +} + +/** + * Whether current user has capability or role. + * + * @since 2.0.0 + * + * @param string $capability Capability or role name. + * @return bool + */ +function current_user_can( $capability ) { + $current_user = wp_get_current_user(); + + if ( empty( $current_user ) ) + return false; + + $args = array_slice( func_get_args(), 1 ); + $args = array_merge( array( $capability ), $args ); + + return call_user_func_array( array( $current_user, 'has_cap' ), $args ); +} + +/** + * Whether current user has a capability or role for a given blog. + * + * @since 3.0.0 + * + * @param int $blog_id Blog ID + * @param string $capability Capability or role name. + * @return bool + */ +function current_user_can_for_blog( $blog_id, $capability ) { + if ( is_multisite() ) + switch_to_blog( $blog_id ); + + $current_user = wp_get_current_user(); + + if ( empty( $current_user ) ) + return false; + + $args = array_slice( func_get_args(), 2 ); + $args = array_merge( array( $capability ), $args ); + + $can = call_user_func_array( array( $current_user, 'has_cap' ), $args ); + + if ( is_multisite() ) + restore_current_blog(); + + return $can; +} + +/** + * Whether author of supplied post has capability or role. + * + * @since 2.9.0 + * + * @param int|object $post Post ID or post object. + * @param string $capability Capability or role name. + * @return bool + */ +function author_can( $post, $capability ) { + if ( !$post = get_post($post) ) + return false; + + $author = get_userdata( $post->post_author ); + + if ( ! $author ) + return false; + + $args = array_slice( func_get_args(), 2 ); + $args = array_merge( array( $capability ), $args ); + + return call_user_func_array( array( $author, 'has_cap' ), $args ); +} + +/** + * Whether a particular user has capability or role. + * + * @since 3.1.0 + * + * @param int|object $user User ID or object. + * @param string $capability Capability or role name. + * @return bool + */ +function user_can( $user, $capability ) { + if ( ! is_object( $user ) ) + $user = get_userdata( $user ); + + if ( ! $user || ! $user->exists() ) + return false; + + $args = array_slice( func_get_args(), 2 ); + $args = array_merge( array( $capability ), $args ); + + return call_user_func_array( array( $user, 'has_cap' ), $args ); +} + +/** + * Retrieve role object. + * + * @see WP_Roles::get_role() Uses method to retrieve role object. + * @since 2.0.0 + * + * @param string $role Role name. + * @return WP_Role|null WP_Role object if found, null if the role does not exist. + */ +function get_role( $role ) { + global $wp_roles; + + if ( ! isset( $wp_roles ) ) + $wp_roles = new WP_Roles(); + + return $wp_roles->get_role( $role ); +} + +/** + * Add role, if it does not exist. + * + * @see WP_Roles::add_role() Uses method to add role. + * @since 2.0.0 + * + * @param string $role Role name. + * @param string $display_name Display name for role. + * @param array $capabilities List of capabilities, e.g. array( 'edit_posts' => true, 'delete_posts' => false ); + * @return WP_Role|null WP_Role object if role is added, null if already exists. + */ +function add_role( $role, $display_name, $capabilities = array() ) { + global $wp_roles; + + if ( ! isset( $wp_roles ) ) + $wp_roles = new WP_Roles(); + + return $wp_roles->add_role( $role, $display_name, $capabilities ); +} + +/** + * Remove role, if it exists. + * + * @see WP_Roles::remove_role() Uses method to remove role. + * @since 2.0.0 + * + * @param string $role Role name. + */ +function remove_role( $role ) { + global $wp_roles; + + if ( ! isset( $wp_roles ) ) + $wp_roles = new WP_Roles(); + + $wp_roles->remove_role( $role ); +} + +/** + * Retrieve a list of super admins. + * + * @since 3.0.0 + * + * @uses $super_admins Super admins global variable, if set. + * + * @return array List of super admin logins + */ +function get_super_admins() { + global $super_admins; + + if ( isset($super_admins) ) + return $super_admins; + else + return get_site_option( 'site_admins', array('admin') ); +} + +/** + * Determine if user is a site admin. + * + * @since 3.0.0 + * + * @param int $user_id (Optional) The ID of a user. Defaults to the current user. + * @return bool True if the user is a site admin. + */ +function is_super_admin( $user_id = false ) { + if ( ! $user_id || $user_id == get_current_user_id() ) + $user = wp_get_current_user(); + else + $user = get_userdata( $user_id ); + + if ( ! $user || ! $user->exists() ) + return false; + + if ( is_multisite() ) { + $super_admins = get_super_admins(); + if ( is_array( $super_admins ) && in_array( $user->user_login, $super_admins ) ) + return true; + } else { + if ( $user->has_cap('delete_users') ) + return true; + } + + return false; +} diff --git a/wp-includes/category-template.php b/wp-includes/category-template.php new file mode 100644 index 0000000..0f97068 --- /dev/null +++ b/wp-includes/category-template.php @@ -0,0 +1,1405 @@ +<?php +/** + * Category Template Tags and API. + * + * @package WordPress + * @subpackage Template + */ + +/** + * Retrieve category link URL. + * + * @since 1.0.0 + * @see get_term_link() + * + * @param int|object $category Category ID or object. + * @return string Link on success, empty string if category does not exist. + */ +function get_category_link( $category ) { + if ( ! is_object( $category ) ) + $category = (int) $category; + + $category = get_term_link( $category, 'category' ); + + if ( is_wp_error( $category ) ) + return ''; + + return $category; +} + +/** + * Retrieve category parents with separator. + * + * @since 1.2.0 + * + * @param int $id Category ID. + * @param bool $link Optional, default is false. Whether to format with link. + * @param string $separator Optional, default is '/'. How to separate categories. + * @param bool $nicename Optional, default is false. Whether to use nice name for display. + * @param array $visited Optional. Already linked to categories to prevent duplicates. + * @return string|WP_Error A list of category parents on success, WP_Error on failure. + */ +function get_category_parents( $id, $link = false, $separator = '/', $nicename = false, $visited = array() ) { + $chain = ''; + $parent = get_term( $id, 'category' ); + if ( is_wp_error( $parent ) ) + return $parent; + + if ( $nicename ) + $name = $parent->slug; + else + $name = $parent->name; + + if ( $parent->parent && ( $parent->parent != $parent->term_id ) && !in_array( $parent->parent, $visited ) ) { + $visited[] = $parent->parent; + $chain .= get_category_parents( $parent->parent, $link, $separator, $nicename, $visited ); + } + + if ( $link ) + $chain .= '<a href="' . esc_url( get_category_link( $parent->term_id ) ) . '">'.$name.'</a>' . $separator; + else + $chain .= $name.$separator; + return $chain; +} + +/** + * Retrieve post categories. + * + * @since 0.71 + * + * @param int $id Optional, default to current post ID. The post ID. + * @return array + */ +function get_the_category( $id = false ) { + $categories = get_the_terms( $id, 'category' ); + if ( ! $categories || is_wp_error( $categories ) ) + $categories = array(); + + $categories = array_values( $categories ); + + foreach ( array_keys( $categories ) as $key ) { + _make_cat_compat( $categories[$key] ); + } + + /** + * Filter the array of categories to return for a post. + * + * @since 3.1.0 + * + * @param array $categories An array of categories to return for the post. + */ + return apply_filters( 'get_the_categories', $categories ); +} + +/** + * Sort categories by name. + * + * Used by usort() as a callback, should not be used directly. Can actually be + * used to sort any term object. + * + * @since 2.3.0 + * @access private + * + * @param object $a + * @param object $b + * @return int + */ +function _usort_terms_by_name( $a, $b ) { + return strcmp( $a->name, $b->name ); +} + +/** + * Sort categories by ID. + * + * Used by usort() as a callback, should not be used directly. Can actually be + * used to sort any term object. + * + * @since 2.3.0 + * @access private + * + * @param object $a + * @param object $b + * @return int + */ +function _usort_terms_by_ID( $a, $b ) { + if ( $a->term_id > $b->term_id ) + return 1; + elseif ( $a->term_id < $b->term_id ) + return -1; + else + return 0; +} + +/** + * Retrieve category name based on category ID. + * + * @since 0.71 + * + * @param int $cat_ID Category ID. + * @return string|WP_Error Category name on success, WP_Error on failure. + */ +function get_the_category_by_ID( $cat_ID ) { + $cat_ID = (int) $cat_ID; + $category = get_term( $cat_ID, 'category' ); + + if ( is_wp_error( $category ) ) + return $category; + + return ( $category ) ? $category->name : ''; +} + +/** + * Retrieve category list in either HTML list or custom format. + * + * @since 1.5.1 + * + * @param string $separator Optional, default is empty string. Separator for between the categories. + * @param string $parents Optional. How to display the parents. + * @param int $post_id Optional. Post ID to retrieve categories. + * @return string + */ +function get_the_category_list( $separator = '', $parents='', $post_id = false ) { + global $wp_rewrite; + if ( ! is_object_in_taxonomy( get_post_type( $post_id ), 'category' ) ) { + /** This filter is documented in wp-includes/category-template.php */ + return apply_filters( 'the_category', '', $separator, $parents ); + } + + $categories = get_the_category( $post_id ); + if ( empty( $categories ) ) { + /** This filter is documented in wp-includes/category-template.php */ + return apply_filters( 'the_category', __( 'Uncategorized' ), $separator, $parents ); + } + + $rel = ( is_object( $wp_rewrite ) && $wp_rewrite->using_permalinks() ) ? 'rel="category tag"' : 'rel="category"'; + + $thelist = ''; + if ( '' == $separator ) { + $thelist .= '<ul class="post-categories">'; + foreach ( $categories as $category ) { + $thelist .= "\n\t<li>"; + switch ( strtolower( $parents ) ) { + case 'multiple': + if ( $category->parent ) + $thelist .= get_category_parents( $category->parent, true, $separator ); + $thelist .= '<a href="' . esc_url( get_category_link( $category->term_id ) ) . '" ' . $rel . '>' . $category->name.'</a></li>'; + break; + case 'single': + $thelist .= '<a href="' . esc_url( get_category_link( $category->term_id ) ) . '" ' . $rel . '>'; + if ( $category->parent ) + $thelist .= get_category_parents( $category->parent, false, $separator ); + $thelist .= $category->name.'</a></li>'; + break; + case '': + default: + $thelist .= '<a href="' . esc_url( get_category_link( $category->term_id ) ) . '" ' . $rel . '>' . $category->name.'</a></li>'; + } + } + $thelist .= '</ul>'; + } else { + $i = 0; + foreach ( $categories as $category ) { + if ( 0 < $i ) + $thelist .= $separator; + switch ( strtolower( $parents ) ) { + case 'multiple': + if ( $category->parent ) + $thelist .= get_category_parents( $category->parent, true, $separator ); + $thelist .= '<a href="' . esc_url( get_category_link( $category->term_id ) ) . '" ' . $rel . '>' . $category->name.'</a>'; + break; + case 'single': + $thelist .= '<a href="' . esc_url( get_category_link( $category->term_id ) ) . '" ' . $rel . '>'; + if ( $category->parent ) + $thelist .= get_category_parents( $category->parent, false, $separator ); + $thelist .= "$category->name</a>"; + break; + case '': + default: + $thelist .= '<a href="' . esc_url( get_category_link( $category->term_id ) ) . '" ' . $rel . '>' . $category->name.'</a>'; + } + ++$i; + } + } + + /** + * Filter the category or list of categories. + * + * @since 1.2.0 + * + * @param array $thelist List of categories for the current post. + * @param string $separator Separator used between the categories. + * @param string $parents How to display the category parents. Accepts 'multiple', + * 'single', or empty. + */ + return apply_filters( 'the_category', $thelist, $separator, $parents ); +} + +/** + * Check if the current post in within any of the given categories. + * + * The given categories are checked against the post's categories' term_ids, names and slugs. + * Categories given as integers will only be checked against the post's categories' term_ids. + * + * Prior to v2.5 of WordPress, category names were not supported. + * Prior to v2.7, category slugs were not supported. + * Prior to v2.7, only one category could be compared: in_category( $single_category ). + * Prior to v2.7, this function could only be used in the WordPress Loop. + * As of 2.7, the function can be used anywhere if it is provided a post ID or post object. + * + * @since 1.2.0 + * + * @param int|string|array $category Category ID, name or slug, or array of said. + * @param int|object $post Optional. Post to check instead of the current post. (since 2.7.0) + * @return bool True if the current post is in any of the given categories. + */ +function in_category( $category, $post = null ) { + if ( empty( $category ) ) + return false; + + return has_category( $category, $post ); +} + +/** + * Display the category list for the post. + * + * @since 0.71 + * + * @param string $separator Optional, default is empty string. Separator for between the categories. + * @param string $parents Optional. How to display the parents. + * @param int $post_id Optional. Post ID to retrieve categories. + */ +function the_category( $separator = '', $parents='', $post_id = false ) { + echo get_the_category_list( $separator, $parents, $post_id ); +} + +/** + * Retrieve category description. + * + * @since 1.0.0 + * + * @param int $category Optional. Category ID. Will use global category ID by default. + * @return string Category description, available. + */ +function category_description( $category = 0 ) { + return term_description( $category, 'category' ); +} + +/** + * Display or retrieve the HTML dropdown list of categories. + * + * The list of arguments is below: + * 'show_option_all' (string) - Text to display for showing all categories. + * 'show_option_none' (string) - Text to display for showing no categories. + * 'option_none_value' (mixed) - Value to use when no category is selected. + * 'orderby' (string) default is 'ID' - What column to use for ordering the + * categories. + * 'order' (string) default is 'ASC' - What direction to order categories. + * 'show_count' (bool|int) default is 0 - Whether to show how many posts are + * in the category. + * 'hide_empty' (bool|int) default is 1 - Whether to hide categories that + * don't have any posts attached to them. + * 'child_of' (int) default is 0 - See {@link get_categories()}. + * 'exclude' (string) - See {@link get_categories()}. + * 'echo' (bool|int) default is 1 - Whether to display or retrieve content. + * 'depth' (int) - The max depth. + * 'tab_index' (int) - Tab index for select element. + * 'name' (string) - The name attribute value for select element. + * 'id' (string) - The ID attribute value for select element. Defaults to name if omitted. + * 'class' (string) - The class attribute value for select element. + * 'selected' (int) - Which category ID is selected. + * 'taxonomy' (string) - The name of the taxonomy to retrieve. Defaults to category. + * + * The 'hierarchical' argument, which is disabled by default, will override the + * depth argument, unless it is true. When the argument is false, it will + * display all of the categories. When it is enabled it will use the value in + * the 'depth' argument. + * + * @since 2.1.0 + * + * @param string|array $args Optional. Override default arguments. + * @return string HTML content only if 'echo' argument is 0. + */ +function wp_dropdown_categories( $args = '' ) { + $defaults = array( + 'show_option_all' => '', 'show_option_none' => '', + 'orderby' => 'id', 'order' => 'ASC', + 'show_count' => 0, + 'hide_empty' => 1, 'child_of' => 0, + 'exclude' => '', 'echo' => 1, + 'selected' => 0, 'hierarchical' => 0, + 'name' => 'cat', 'id' => '', + 'class' => 'postform', 'depth' => 0, + 'tab_index' => 0, 'taxonomy' => 'category', + 'hide_if_empty' => false, 'option_none_value' => -1 + ); + + $defaults['selected'] = ( is_category() ) ? get_query_var( 'cat' ) : 0; + + // Back compat. + if ( isset( $args['type'] ) && 'link' == $args['type'] ) { + _deprecated_argument( __FUNCTION__, '3.0', '' ); + $args['taxonomy'] = 'link_category'; + } + + $r = wp_parse_args( $args, $defaults ); + $option_none_value = $r['option_none_value']; + + if ( ! isset( $r['pad_counts'] ) && $r['show_count'] && $r['hierarchical'] ) { + $r['pad_counts'] = true; + } + + $tab_index = $r['tab_index']; + + $tab_index_attribute = ''; + if ( (int) $tab_index > 0 ) { + $tab_index_attribute = " tabindex=\"$tab_index\""; + } + $categories = get_terms( $r['taxonomy'], $r ); + $name = esc_attr( $r['name'] ); + $class = esc_attr( $r['class'] ); + $id = $r['id'] ? esc_attr( $r['id'] ) : $name; + + if ( ! $r['hide_if_empty'] || ! empty( $categories ) ) { + $output = "<select name='$name' id='$id' class='$class' $tab_index_attribute>\n"; + } else { + $output = ''; + } + if ( empty( $categories ) && ! $r['hide_if_empty'] && ! empty( $r['show_option_none'] ) ) { + + /** + * Filter a taxonomy drop-down display element. + * + * A variety of taxonomy drop-down display elements can be modified + * just prior to display via this filter. Filterable arguments include + * 'show_option_none', 'show_option_all', and various forms of the + * term name. + * + * @since 1.2.0 + * + * @see wp_dropdown_categories() + * + * @param string $element Taxonomy element to list. + */ + $show_option_none = apply_filters( 'list_cats', $r['show_option_none'] ); + $output .= "\t<option value='" . esc_attr( $option_none_value ) . "' selected='selected'>$show_option_none</option>\n"; + } + + if ( ! empty( $categories ) ) { + + if ( $r['show_option_all'] ) { + + /** This filter is documented in wp-includes/category-template.php */ + $show_option_all = apply_filters( 'list_cats', $r['show_option_all'] ); + $selected = ( '0' === strval($r['selected']) ) ? " selected='selected'" : ''; + $output .= "\t<option value='0'$selected>$show_option_all</option>\n"; + } + + if ( $r['show_option_none'] ) { + + /** This filter is documented in wp-includes/category-template.php */ + $show_option_none = apply_filters( 'list_cats', $r['show_option_none'] ); + $selected = selected( $option_none_value, $r['selected'], false ); + $output .= "\t<option value='" . esc_attr( $option_none_value ) . "'$selected>$show_option_none</option>\n"; + } + + if ( $r['hierarchical'] ) { + $depth = $r['depth']; // Walk the full depth. + } else { + $depth = -1; // Flat. + } + $output .= walk_category_dropdown_tree( $categories, $depth, $r ); + } + + if ( ! $r['hide_if_empty'] || ! empty( $categories ) ) { + $output .= "</select>\n"; + } + /** + * Filter the taxonomy drop-down output. + * + * @since 2.1.0 + * + * @param string $output HTML output. + * @param array $r Arguments used to build the drop-down. + */ + $output = apply_filters( 'wp_dropdown_cats', $output, $r ); + + if ( $r['echo'] ) { + echo $output; + } + return $output; +} + +/** + * Display or retrieve the HTML list of categories. + * + * The list of arguments is below: + * 'show_option_all' (string) - Text to display for showing all categories. + * 'orderby' (string) default is 'ID' - What column to use for ordering the + * categories. + * 'order' (string) default is 'ASC' - What direction to order categories. + * 'show_count' (bool|int) default is 0 - Whether to show how many posts are + * in the category. + * 'hide_empty' (bool|int) default is 1 - Whether to hide categories that + * don't have any posts attached to them. + * 'use_desc_for_title' (bool|int) default is 1 - Whether to use the + * category description as the title attribute. + * 'feed' - See {@link get_categories()}. + * 'feed_type' - See {@link get_categories()}. + * 'feed_image' - See {@link get_categories()}. + * 'child_of' (int) default is 0 - See {@link get_categories()}. + * 'exclude' (string) - See {@link get_categories()}. + * 'exclude_tree' (string) - See {@link get_categories()}. + * 'echo' (bool|int) default is 1 - Whether to display or retrieve content. + * 'current_category' (int) - See {@link get_categories()}. + * 'hierarchical' (bool) - See {@link get_categories()}. + * 'title_li' (string) - See {@link get_categories()}. + * 'depth' (int) - The max depth. + * + * @since 2.1.0 + * + * @param string|array $args Optional. Override default arguments. + * @return false|null|string HTML content only if 'echo' argument is 0. + */ +function wp_list_categories( $args = '' ) { + $defaults = array( + 'show_option_all' => '', 'show_option_none' => __('No categories'), + 'orderby' => 'name', 'order' => 'ASC', + 'style' => 'list', + 'show_count' => 0, 'hide_empty' => 1, + 'use_desc_for_title' => 1, 'child_of' => 0, + 'feed' => '', 'feed_type' => '', + 'feed_image' => '', 'exclude' => '', + 'exclude_tree' => '', 'current_category' => 0, + 'hierarchical' => true, 'title_li' => __( 'Categories' ), + 'echo' => 1, 'depth' => 0, + 'taxonomy' => 'category' + ); + + $r = wp_parse_args( $args, $defaults ); + + if ( !isset( $r['pad_counts'] ) && $r['show_count'] && $r['hierarchical'] ) + $r['pad_counts'] = true; + + if ( true == $r['hierarchical'] ) { + $r['exclude_tree'] = $r['exclude']; + $r['exclude'] = ''; + } + + if ( ! isset( $r['class'] ) ) + $r['class'] = ( 'category' == $r['taxonomy'] ) ? 'categories' : $r['taxonomy']; + + if ( ! taxonomy_exists( $r['taxonomy'] ) ) { + return false; + } + + $show_option_all = $r['show_option_all']; + $show_option_none = $r['show_option_none']; + + $categories = get_categories( $r ); + + $output = ''; + if ( $r['title_li'] && 'list' == $r['style'] ) { + $output = '<li class="' . esc_attr( $r['class'] ) . '">' . $r['title_li'] . '<ul>'; + } + if ( empty( $categories ) ) { + if ( ! empty( $show_option_none ) ) { + if ( 'list' == $r['style'] ) { + $output .= '<li class="cat-item-none">' . $show_option_none . '</li>'; + } else { + $output .= $show_option_none; + } + } + } else { + if ( ! empty( $show_option_all ) ) { + $posts_page = ( 'page' == get_option( 'show_on_front' ) && get_option( 'page_for_posts' ) ) ? get_permalink( get_option( 'page_for_posts' ) ) : home_url( '/' ); + $posts_page = esc_url( $posts_page ); + if ( 'list' == $r['style'] ) { + $output .= "<li class='cat-item-all'><a href='$posts_page'>$show_option_all</a></li>"; + } else { + $output .= "<a href='$posts_page'>$show_option_all</a>"; + } + } + + if ( empty( $r['current_category'] ) && ( is_category() || is_tax() || is_tag() ) ) { + $current_term_object = get_queried_object(); + if ( $current_term_object && $r['taxonomy'] === $current_term_object->taxonomy ) { + $r['current_category'] = get_queried_object_id(); + } + } + + if ( $r['hierarchical'] ) { + $depth = $r['depth']; + } else { + $depth = -1; // Flat. + } + $output .= walk_category_tree( $categories, $depth, $r ); + } + + if ( $r['title_li'] && 'list' == $r['style'] ) + $output .= '</ul></li>'; + + /** + * Filter the HTML output of a taxonomy list. + * + * @since 2.1.0 + * + * @param string $output HTML output. + * @param array $args An array of taxonomy-listing arguments. + */ + $html = apply_filters( 'wp_list_categories', $output, $args ); + + if ( $r['echo'] ) { + echo $html; + } else { + return $html; + } +} + +/** + * Display tag cloud. + * + * The text size is set by the 'smallest' and 'largest' arguments, which will + * use the 'unit' argument value for the CSS text size unit. The 'format' + * argument can be 'flat' (default), 'list', or 'array'. The flat value for the + * 'format' argument will separate tags with spaces. The list value for the + * 'format' argument will format the tags in a UL HTML list. The array value for + * the 'format' argument will return in PHP array type format. + * + * The 'orderby' argument will accept 'name' or 'count' and defaults to 'name'. + * The 'order' is the direction to sort, defaults to 'ASC' and can be 'DESC'. + * + * The 'number' argument is how many tags to return. By default, the limit will + * be to return the top 45 tags in the tag cloud list. + * + * The 'topic_count_text' argument is a nooped plural from _n_noop() to generate the + * text for the tooltip of the tag link. + * + * The 'topic_count_text_callback' argument is a function, which given the count + * of the posts with that tag returns a text for the tooltip of the tag link. + * + * The 'post_type' argument is used only when 'link' is set to 'edit'. It determines the post_type + * passed to edit.php for the popular tags edit links. + * + * The 'exclude' and 'include' arguments are used for the {@link get_tags()} + * function. Only one should be used, because only one will be used and the + * other ignored, if they are both set. + * + * @since 2.3.0 + * + * @param array|string|null $args Optional. Override default arguments. + * @return null|false Generated tag cloud, only if no failures and 'array' is set for the 'format' argument. + */ +function wp_tag_cloud( $args = '' ) { + $defaults = array( + 'smallest' => 8, 'largest' => 22, 'unit' => 'pt', 'number' => 45, + 'format' => 'flat', 'separator' => "\n", 'orderby' => 'name', 'order' => 'ASC', + 'exclude' => '', 'include' => '', 'link' => 'view', 'taxonomy' => 'post_tag', 'post_type' => '', 'echo' => true + ); + $args = wp_parse_args( $args, $defaults ); + + $tags = get_terms( $args['taxonomy'], array_merge( $args, array( 'orderby' => 'count', 'order' => 'DESC' ) ) ); // Always query top tags + + if ( empty( $tags ) || is_wp_error( $tags ) ) + return; + + foreach ( $tags as $key => $tag ) { + if ( 'edit' == $args['link'] ) + $link = get_edit_term_link( $tag->term_id, $tag->taxonomy, $args['post_type'] ); + else + $link = get_term_link( intval($tag->term_id), $tag->taxonomy ); + if ( is_wp_error( $link ) ) + return false; + + $tags[ $key ]->link = $link; + $tags[ $key ]->id = $tag->term_id; + } + + $return = wp_generate_tag_cloud( $tags, $args ); // Here's where those top tags get sorted according to $args + + /** + * Filter the tag cloud output. + * + * @since 2.3.0 + * + * @param string $return HTML output of the tag cloud. + * @param array $args An array of tag cloud arguments. + */ + $return = apply_filters( 'wp_tag_cloud', $return, $args ); + + if ( 'array' == $args['format'] || empty($args['echo']) ) + return $return; + + echo $return; +} + +/** + * Default topic count scaling for tag links + * + * @param integer $count number of posts with that tag + * @return integer scaled count + */ +function default_topic_count_scale( $count ) { + return round(log10($count + 1) * 100); +} + +/** + * Generates a tag cloud (heatmap) from provided data. + * + * The text size is set by the 'smallest' and 'largest' arguments, which will + * use the 'unit' argument value for the CSS text size unit. The 'format' + * argument can be 'flat' (default), 'list', or 'array'. The flat value for the + * 'format' argument will separate tags with spaces. The list value for the + * 'format' argument will format the tags in a UL HTML list. The array value for + * the 'format' argument will return in PHP array type format. + * + * The 'tag_cloud_sort' filter allows you to override the sorting. + * Passed to the filter: $tags array and $args array, has to return the $tags array + * after sorting it. + * + * The 'orderby' argument will accept 'name' or 'count' and defaults to 'name'. + * The 'order' is the direction to sort, defaults to 'ASC' and can be 'DESC' or + * 'RAND'. + * + * The 'number' argument is how many tags to return. By default, the limit will + * be to return the entire tag cloud list. + * + * The 'topic_count_text' argument is a nooped plural from _n_noop() to generate the + * text for the tooltip of the tag link. + * + * The 'topic_count_text_callback' argument is a function, which given the count + * of the posts with that tag returns a text for the tooltip of the tag link. + * + * @todo Complete functionality. + * @since 2.3.0 + * + * @param array $tags List of tags. + * @param string|array $args Optional, override default arguments. + * @return string|array Tag cloud as a string or an array, depending on 'format' argument. + */ +function wp_generate_tag_cloud( $tags, $args = '' ) { + $defaults = array( + 'smallest' => 8, 'largest' => 22, 'unit' => 'pt', 'number' => 0, + 'format' => 'flat', 'separator' => "\n", 'orderby' => 'name', 'order' => 'ASC', + 'topic_count_text' => null, 'topic_count_text_callback' => null, + 'topic_count_scale_callback' => 'default_topic_count_scale', 'filter' => 1, + ); + + $args = wp_parse_args( $args, $defaults ); + + $return = ( 'array' === $args['format'] ) ? array() : ''; + + if ( empty( $tags ) ) { + return $return; + } + + // Juggle topic count tooltips: + if ( isset( $args['topic_count_text'] ) ) { + // First look for nooped plural support via topic_count_text. + $translate_nooped_plural = $args['topic_count_text']; + } elseif ( ! empty( $args['topic_count_text_callback'] ) ) { + // Look for the alternative callback style. Ignore the previous default. + if ( $args['topic_count_text_callback'] === 'default_topic_count_text' ) { + $translate_nooped_plural = _n_noop( '%s topic', '%s topics' ); + } else { + $translate_nooped_plural = false; + } + } elseif ( isset( $args['single_text'] ) && isset( $args['multiple_text'] ) ) { + // If no callback exists, look for the old-style single_text and multiple_text arguments. + $translate_nooped_plural = _n_noop( $args['single_text'], $args['multiple_text'] ); + } else { + // This is the default for when no callback, plural, or argument is passed in. + $translate_nooped_plural = _n_noop( '%s topic', '%s topics' ); + } + + /** + * Filter how the items in a tag cloud are sorted. + * + * @since 2.8.0 + * + * @param array $tags Ordered array of terms. + * @param array $args An array of tag cloud arguments. + */ + $tags_sorted = apply_filters( 'tag_cloud_sort', $tags, $args ); + if ( empty( $tags_sorted ) ) { + return $return; + } + + if ( $tags_sorted !== $tags ) { + $tags = $tags_sorted; + unset( $tags_sorted ); + } else { + if ( 'RAND' === $args['order'] ) { + shuffle( $tags ); + } else { + // SQL cannot save you; this is a second (potentially different) sort on a subset of data. + if ( 'name' === $args['orderby'] ) { + uasort( $tags, '_wp_object_name_sort_cb' ); + } else { + uasort( $tags, '_wp_object_count_sort_cb' ); + } + + if ( 'DESC' === $args['order'] ) { + $tags = array_reverse( $tags, true ); + } + } + } + + if ( $args['number'] > 0 ) + $tags = array_slice( $tags, 0, $args['number'] ); + + $counts = array(); + $real_counts = array(); // For the alt tag + foreach ( (array) $tags as $key => $tag ) { + $real_counts[ $key ] = $tag->count; + $counts[ $key ] = call_user_func( $args['topic_count_scale_callback'], $tag->count ); + } + + $min_count = min( $counts ); + $spread = max( $counts ) - $min_count; + if ( $spread <= 0 ) + $spread = 1; + $font_spread = $args['largest'] - $args['smallest']; + if ( $font_spread < 0 ) + $font_spread = 1; + $font_step = $font_spread / $spread; + + $a = array(); + + foreach ( $tags as $key => $tag ) { + $count = $counts[ $key ]; + $real_count = $real_counts[ $key ]; + $tag_link = '#' != $tag->link ? esc_url( $tag->link ) : '#'; + $tag_id = isset($tags[ $key ]->id) ? $tags[ $key ]->id : $key; + $tag_name = $tags[ $key ]->name; + + if ( $translate_nooped_plural ) { + $title_attribute = sprintf( translate_nooped_plural( $translate_nooped_plural, $real_count ), number_format_i18n( $real_count ) ); + } else { + $title_attribute = call_user_func( $args['topic_count_text_callback'], $real_count, $tag, $args ); + } + + $a[] = "<a href='$tag_link' class='tag-link-$tag_id' title='" . esc_attr( $title_attribute ) . "' style='font-size: " . + str_replace( ',', '.', ( $args['smallest'] + ( ( $count - $min_count ) * $font_step ) ) ) + . $args['unit'] . ";'>$tag_name</a>"; + } + + switch ( $args['format'] ) { + case 'array' : + $return =& $a; + break; + case 'list' : + $return = "<ul class='wp-tag-cloud'>\n\t<li>"; + $return .= join( "</li>\n\t<li>", $a ); + $return .= "</li>\n</ul>\n"; + break; + default : + $return = join( $args['separator'], $a ); + break; + } + + if ( $args['filter'] ) { + /** + * Filter the generated output of a tag cloud. + * + * The filter is only evaluated if a true value is passed + * to the $filter argument in wp_generate_tag_cloud(). + * + * @since 2.3.0 + * + * @see wp_generate_tag_cloud() + * + * @param array|string $return String containing the generated HTML tag cloud output + * or an array of tag links if the 'format' argument + * equals 'array'. + * @param array $tags An array of terms used in the tag cloud. + * @param array $args An array of wp_generate_tag_cloud() arguments. + */ + return apply_filters( 'wp_generate_tag_cloud', $return, $tags, $args ); + } + + else + return $return; +} + +/** + * Callback for comparing objects based on name + * + * @since 3.1.0 + * @access private + */ +function _wp_object_name_sort_cb( $a, $b ) { + return strnatcasecmp( $a->name, $b->name ); +} + +/** + * Callback for comparing objects based on count + * + * @since 3.1.0 + * @access private + */ +function _wp_object_count_sort_cb( $a, $b ) { + return ( $a->count > $b->count ); +} + +// +// Helper functions +// + +/** + * Retrieve HTML list content for category list. + * + * @uses Walker_Category to create HTML list content. + * @since 2.1.0 + * @see Walker_Category::walk() for parameters and return description. + */ +function walk_category_tree() { + $args = func_get_args(); + // the user's options are the third parameter + if ( empty($args[2]['walker']) || !is_a($args[2]['walker'], 'Walker') ) + $walker = new Walker_Category; + else + $walker = $args[2]['walker']; + + return call_user_func_array(array( &$walker, 'walk' ), $args ); +} + +/** + * Retrieve HTML dropdown (select) content for category list. + * + * @uses Walker_CategoryDropdown to create HTML dropdown content. + * @since 2.1.0 + * @see Walker_CategoryDropdown::walk() for parameters and return description. + */ +function walk_category_dropdown_tree() { + $args = func_get_args(); + // the user's options are the third parameter + if ( empty($args[2]['walker']) || !is_a($args[2]['walker'], 'Walker') ) + $walker = new Walker_CategoryDropdown; + else + $walker = $args[2]['walker']; + + return call_user_func_array(array( &$walker, 'walk' ), $args ); +} + +/** + * Create HTML list of categories. + * + * @package WordPress + * @since 2.1.0 + * @uses Walker + */ +class Walker_Category extends Walker { + /** + * What the class handles. + * + * @see Walker::$tree_type + * @since 2.1.0 + * @var string + */ + public $tree_type = 'category'; + + /** + * Database fields to use. + * + * @see Walker::$db_fields + * @since 2.1.0 + * @todo Decouple this + * @var array + */ + public $db_fields = array ('parent' => 'parent', 'id' => 'term_id'); + + /** + * Starts the list before the elements are added. + * + * @see Walker::start_lvl() + * + * @since 2.1.0 + * + * @param string $output Passed by reference. Used to append additional content. + * @param int $depth Depth of category. Used for tab indentation. + * @param array $args An array of arguments. Will only append content if style argument value is 'list'. + * @see wp_list_categories() + */ + public function start_lvl( &$output, $depth = 0, $args = array() ) { + if ( 'list' != $args['style'] ) + return; + + $indent = str_repeat("\t", $depth); + $output .= "$indent<ul class='children'>\n"; + } + + /** + * Ends the list of after the elements are added. + * + * @see Walker::end_lvl() + * + * @since 2.1.0 + * + * @param string $output Passed by reference. Used to append additional content. + * @param int $depth Depth of category. Used for tab indentation. + * @param array $args An array of arguments. Will only append content if style argument value is 'list'. + * @wsee wp_list_categories() + */ + public function end_lvl( &$output, $depth = 0, $args = array() ) { + if ( 'list' != $args['style'] ) + return; + + $indent = str_repeat("\t", $depth); + $output .= "$indent</ul>\n"; + } + + /** + * Start the element output. + * + * @see Walker::start_el() + * + * @since 2.1.0 + * + * @param string $output Passed by reference. Used to append additional content. + * @param object $category Category data object. + * @param int $depth Depth of category in reference to parents. Default 0. + * @param array $args An array of arguments. @see wp_list_categories() + * @param int $id ID of the current category. + */ + public function start_el( &$output, $category, $depth = 0, $args = array(), $id = 0 ) { + /** This filter is documented in wp-includes/category-template.php */ + $cat_name = apply_filters( + 'list_cats', + esc_attr( $category->name ), + $category + ); + + $link = '<a href="' . esc_url( get_term_link( $category ) ) . '" '; + if ( $args['use_desc_for_title'] && ! empty( $category->description ) ) { + /** + * Filter the category description for display. + * + * @since 1.2.0 + * + * @param string $description Category description. + * @param object $category Category object. + */ + $link .= 'title="' . esc_attr( strip_tags( apply_filters( 'category_description', $category->description, $category ) ) ) . '"'; + } + + $link .= '>'; + $link .= $cat_name . '</a>'; + + if ( ! empty( $args['feed_image'] ) || ! empty( $args['feed'] ) ) { + $link .= ' '; + + if ( empty( $args['feed_image'] ) ) { + $link .= '('; + } + + $link .= '<a href="' . esc_url( get_term_feed_link( $category->term_id, $category->taxonomy, $args['feed_type'] ) ) . '"'; + + if ( empty( $args['feed'] ) ) { + $alt = ' alt="' . sprintf(__( 'Feed for all posts filed under %s' ), $cat_name ) . '"'; + } else { + $alt = ' alt="' . $args['feed'] . '"'; + $name = $args['feed']; + $link .= empty( $args['title'] ) ? '' : $args['title']; + } + + $link .= '>'; + + if ( empty( $args['feed_image'] ) ) { + $link .= $name; + } else { + $link .= "<img src='" . $args['feed_image'] . "'$alt" . ' />'; + } + $link .= '</a>'; + + if ( empty( $args['feed_image'] ) ) { + $link .= ')'; + } + } + + if ( ! empty( $args['show_count'] ) ) { + $link .= ' (' . number_format_i18n( $category->count ) . ')'; + } + if ( 'list' == $args['style'] ) { + $output .= "\t<li"; + $class = 'cat-item cat-item-' . $category->term_id; + if ( ! empty( $args['current_category'] ) ) { + $_current_category = get_term( $args['current_category'], $category->taxonomy ); + if ( $category->term_id == $args['current_category'] ) { + $class .= ' current-cat'; + } elseif ( $category->term_id == $_current_category->parent ) { + $class .= ' current-cat-parent'; + } + } + $output .= ' class="' . $class . '"'; + $output .= ">$link\n"; + } else { + $output .= "\t$link<br />\n"; + } + } + + /** + * Ends the element output, if needed. + * + * @see Walker::end_el() + * + * @since 2.1.0 + * + * @param string $output Passed by reference. Used to append additional content. + * @param object $page Not used. + * @param int $depth Depth of category. Not used. + * @param array $args An array of arguments. Only uses 'list' for whether should append to output. @see wp_list_categories() + */ + public function end_el( &$output, $page, $depth = 0, $args = array() ) { + if ( 'list' != $args['style'] ) + return; + + $output .= "</li>\n"; + } + +} + +/** + * Create HTML dropdown list of Categories. + * + * @package WordPress + * @since 2.1.0 + * @uses Walker + */ +class Walker_CategoryDropdown extends Walker { + /** + * @see Walker::$tree_type + * @since 2.1.0 + * @var string + */ + public $tree_type = 'category'; + + /** + * @see Walker::$db_fields + * @since 2.1.0 + * @todo Decouple this + * @var array + */ + public $db_fields = array ('parent' => 'parent', 'id' => 'term_id'); + + /** + * Start the element output. + * + * @see Walker::start_el() + * @since 2.1.0 + * + * @param string $output Passed by reference. Used to append additional content. + * @param object $category Category data object. + * @param int $depth Depth of category. Used for padding. + * @param array $args Uses 'selected' and 'show_count' keys, if they exist. @see wp_dropdown_categories() + */ + public function start_el( &$output, $category, $depth = 0, $args = array(), $id = 0 ) { + $pad = str_repeat(' ', $depth * 3); + + /** This filter is documented in wp-includes/category-template.php */ + $cat_name = apply_filters( 'list_cats', $category->name, $category ); + + $output .= "\t<option class=\"level-$depth\" value=\"".$category->term_id."\""; + if ( $category->term_id == $args['selected'] ) + $output .= ' selected="selected"'; + $output .= '>'; + $output .= $pad.$cat_name; + if ( $args['show_count'] ) + $output .= '  ('. number_format_i18n( $category->count ) .')'; + $output .= "</option>\n"; + } +} + +// +// Tags +// + +/** + * Retrieve the link to the tag. + * + * @since 2.3.0 + * @see get_term_link() + * + * @param int|object $tag Tag ID or object. + * @return string Link on success, empty string if tag does not exist. + */ +function get_tag_link( $tag ) { + if ( ! is_object( $tag ) ) + $tag = (int) $tag; + + $tag = get_term_link( $tag, 'post_tag' ); + + if ( is_wp_error( $tag ) ) + return ''; + + return $tag; +} + +/** + * Retrieve the tags for a post. + * + * @since 2.3.0 + * + * @param int $id Post ID. + * @return array|bool Array of tag objects on success, false on failure. + */ +function get_the_tags( $id = 0 ) { + + /** + * Filter the array of tags for the given post. + * + * @since 2.3.0 + * + * @see get_the_terms() + * + * @param array $terms An array of tags for the given post. + */ + return apply_filters( 'get_the_tags', get_the_terms( $id, 'post_tag' ) ); +} + +/** + * Retrieve the tags for a post formatted as a string. + * + * @since 2.3.0 + * + * @param string $before Optional. Before tags. + * @param string $sep Optional. Between tags. + * @param string $after Optional. After tags. + * @param int $id Optional. Post ID. Defaults to the current post. + * @return string|bool|WP_Error A list of tags on success, false if there are no terms, WP_Error on failure. + */ +function get_the_tag_list( $before = '', $sep = '', $after = '', $id = 0 ) { + + /** + * Filter the tags list for a given post. + * + * @since 2.3.0 + * + * @param string $tag_list List of tags. + * @param string $before String to use before tags. + * @param string $sep String to use between the tags. + * @param string $after String to use after tags. + * @param int $id Post ID. + */ + return apply_filters( 'the_tags', get_the_term_list( $id, 'post_tag', $before, $sep, $after ), $before, $sep, $after, $id ); +} + +/** + * Retrieve the tags for a post. + * + * @since 2.3.0 + * + * @param string $before Optional. Before list. + * @param string $sep Optional. Separate items using this. + * @param string $after Optional. After list. + */ +function the_tags( $before = null, $sep = ', ', $after = '' ) { + if ( null === $before ) + $before = __('Tags: '); + echo get_the_tag_list($before, $sep, $after); +} + +/** + * Retrieve tag description. + * + * @since 2.8.0 + * + * @param int $tag Optional. Tag ID. Will use global tag ID by default. + * @return string Tag description, available. + */ +function tag_description( $tag = 0 ) { + return term_description( $tag ); +} + +/** + * Retrieve term description. + * + * @since 2.8.0 + * + * @param int $term Optional. Term ID. Will use global term ID by default. + * @param string $taxonomy Optional taxonomy name. Defaults to 'post_tag'. + * @return string Term description, available. + */ +function term_description( $term = 0, $taxonomy = 'post_tag' ) { + if ( ! $term && ( is_tax() || is_tag() || is_category() ) ) { + $term = get_queried_object(); + if ( $term ) { + $taxonomy = $term->taxonomy; + $term = $term->term_id; + } + } + $description = get_term_field( 'description', $term, $taxonomy ); + return is_wp_error( $description ) ? '' : $description; +} + +/** + * Retrieve the terms of the taxonomy that are attached to the post. + * + * @since 2.5.0 + * + * @param int|object $post Post ID or object. + * @param string $taxonomy Taxonomy name. + * @return array|bool|WP_Error Array of term objects on success, false if there are no terms + * or the post does not exist, WP_Error on failure. + */ +function get_the_terms( $post, $taxonomy ) { + if ( ! $post = get_post( $post ) ) + return false; + + $terms = get_object_term_cache( $post->ID, $taxonomy ); + if ( false === $terms ) { + $terms = wp_get_object_terms( $post->ID, $taxonomy ); + wp_cache_add($post->ID, $terms, $taxonomy . '_relationships'); + } + + /** + * Filter the list of terms attached to the given post. + * + * @since 3.1.0 + * + * @param array|WP_Error $terms List of attached terms, or WP_Error on failure. + * @param int $post_id Post ID. + * @param string $taxonomy Name of the taxonomy. + */ + $terms = apply_filters( 'get_the_terms', $terms, $post->ID, $taxonomy ); + + if ( empty( $terms ) ) + return false; + + return $terms; +} + +/** + * Retrieve a post's terms as a list with specified format. + * + * @since 2.5.0 + * + * @param int $id Post ID. + * @param string $taxonomy Taxonomy name. + * @param string $before Optional. Before list. + * @param string $sep Optional. Separate items using this. + * @param string $after Optional. After list. + * @return string|bool|WP_Error A list of terms on success, false if there are no terms, WP_Error on failure. + */ +function get_the_term_list( $id, $taxonomy, $before = '', $sep = '', $after = '' ) { + $terms = get_the_terms( $id, $taxonomy ); + + if ( is_wp_error( $terms ) ) + return $terms; + + if ( empty( $terms ) ) + return false; + + foreach ( $terms as $term ) { + $link = get_term_link( $term, $taxonomy ); + if ( is_wp_error( $link ) ) + return $link; + $term_links[] = '<a href="' . esc_url( $link ) . '" rel="tag">' . $term->name . '</a>'; + } + + /** + * Filter the term links for a given taxonomy. + * + * The dynamic portion of the filter name, `$taxonomy`, refers + * to the taxonomy slug. + * + * @since 2.5.0 + * + * @param array $term_links An array of term links. + */ + $term_links = apply_filters( "term_links-$taxonomy", $term_links ); + + return $before . join( $sep, $term_links ) . $after; +} + +/** + * Display the terms in a list. + * + * @since 2.5.0 + * + * @param int $id Post ID. + * @param string $taxonomy Taxonomy name. + * @param string $before Optional. Before list. + * @param string $sep Optional. Separate items using this. + * @param string $after Optional. After list. + * @return false|null False on WordPress error. Returns null when displaying. + */ +function the_terms( $id, $taxonomy, $before = '', $sep = ', ', $after = '' ) { + $term_list = get_the_term_list( $id, $taxonomy, $before, $sep, $after ); + + if ( is_wp_error( $term_list ) ) + return false; + + /** + * Filter the list of terms to display. + * + * @since 2.9.0 + * + * @param array $term_list List of terms to display. + * @param string $taxonomy The taxonomy name. + * @param string $before String to use before the terms. + * @param string $sep String to use between the terms. + * @param string $after String to use after the terms. + */ + echo apply_filters( 'the_terms', $term_list, $taxonomy, $before, $sep, $after ); +} + +/** + * Check if the current post has any of given category. + * + * @since 3.1.0 + * + * @param string|int|array $category Optional. The category name/term_id/slug or array of them to check for. + * @param int|object $post Optional. Post to check instead of the current post. + * @return bool True if the current post has any of the given categories (or any category, if no category specified). + */ +function has_category( $category = '', $post = null ) { + return has_term( $category, 'category', $post ); +} + +/** + * Check if the current post has any of given tags. + * + * The given tags are checked against the post's tags' term_ids, names and slugs. + * Tags given as integers will only be checked against the post's tags' term_ids. + * If no tags are given, determines if post has any tags. + * + * Prior to v2.7 of WordPress, tags given as integers would also be checked against the post's tags' names and slugs (in addition to term_ids) + * Prior to v2.7, this function could only be used in the WordPress Loop. + * As of 2.7, the function can be used anywhere if it is provided a post ID or post object. + * + * @since 2.6.0 + * + * @param string|int|array $tag Optional. The tag name/term_id/slug or array of them to check for. + * @param int|object $post Optional. Post to check instead of the current post. (since 2.7.0) + * @return bool True if the current post has any of the given tags (or any tag, if no tag specified). + */ +function has_tag( $tag = '', $post = null ) { + return has_term( $tag, 'post_tag', $post ); +} + +/** + * Check if the current post has any of given terms. + * + * The given terms are checked against the post's terms' term_ids, names and slugs. + * Terms given as integers will only be checked against the post's terms' term_ids. + * If no terms are given, determines if post has any terms. + * + * @since 3.1.0 + * + * @param string|int|array $term Optional. The term name/term_id/slug or array of them to check for. + * @param string $taxonomy Taxonomy name + * @param int|object $post Optional. Post to check instead of the current post. + * @return bool True if the current post has any of the given tags (or any tag, if no tag specified). + */ +function has_term( $term = '', $taxonomy = '', $post = null ) { + $post = get_post($post); + + if ( !$post ) + return false; + + $r = is_object_in_term( $post->ID, $taxonomy, $term ); + if ( is_wp_error( $r ) ) + return false; + + return $r; +} diff --git a/wp-includes/category.php b/wp-includes/category.php new file mode 100644 index 0000000..6d2ad99 --- /dev/null +++ b/wp-includes/category.php @@ -0,0 +1,339 @@ +<?php +/** + * WordPress Category API + * + * @package WordPress + */ + +/** + * Retrieve list of category objects. + * + * If you change the type to 'link' in the arguments, then the link categories + * will be returned instead. Also all categories will be updated to be backwards + * compatible with pre-2.3 plugins and themes. + * + * @since 2.1.0 + * @see get_terms() Type of arguments that can be changed. + * @link http://codex.wordpress.org/Function_Reference/get_categories + * + * @param string|array $args Optional. Change the defaults retrieving categories. + * @return array List of categories. + */ +function get_categories( $args = '' ) { + $defaults = array( 'taxonomy' => 'category' ); + $args = wp_parse_args( $args, $defaults ); + + $taxonomy = $args['taxonomy']; + + /** + * Filter the taxonomy used to retrieve terms when calling {@see get_categories()}. + * + * @since 2.7.0 + * + * @param string $taxonomy Taxonomy to retrieve terms from. + * @param array $args An array of arguments. See {@see get_terms()}. + */ + $taxonomy = apply_filters( 'get_categories_taxonomy', $taxonomy, $args ); + + // Back compat + if ( isset($args['type']) && 'link' == $args['type'] ) { + _deprecated_argument( __FUNCTION__, '3.0', '' ); + $taxonomy = $args['taxonomy'] = 'link_category'; + } + + $categories = (array) get_terms( $taxonomy, $args ); + + foreach ( array_keys( $categories ) as $k ) + _make_cat_compat( $categories[$k] ); + + return $categories; +} + +/** + * Retrieves category data given a category ID or category object. + * + * If you pass the $category parameter an object, which is assumed to be the + * category row object retrieved the database. It will cache the category data. + * + * If you pass $category an integer of the category ID, then that category will + * be retrieved from the database, if it isn't already cached, and pass it back. + * + * If you look at get_term(), then both types will be passed through several + * filters and finally sanitized based on the $filter parameter value. + * + * The category will converted to maintain backwards compatibility. + * + * @since 1.5.1 + * + * @param int|object $category Category ID or Category row object + * @param string $output Optional. Constant OBJECT, ARRAY_A, or ARRAY_N + * @param string $filter Optional. Default is raw or no WordPress defined filter will applied. + * @return object|array|WP_Error|null Category data in type defined by $output parameter. WP_Error if $category is empty, null if it does not exist. + */ +function get_category( $category, $output = OBJECT, $filter = 'raw' ) { + $category = get_term( $category, 'category', $output, $filter ); + + if ( is_wp_error( $category ) ) + return $category; + + _make_cat_compat( $category ); + + return $category; +} + +/** + * Retrieve category based on URL containing the category slug. + * + * Breaks the $category_path parameter up to get the category slug. + * + * Tries to find the child path and will return it. If it doesn't find a + * match, then it will return the first category matching slug, if $full_match, + * is set to false. If it does not, then it will return null. + * + * It is also possible that it will return a WP_Error object on failure. Check + * for it when using this function. + * + * @since 2.1.0 + * + * @param string $category_path URL containing category slugs. + * @param bool $full_match Optional. Whether full path should be matched. + * @param string $output Optional. Constant OBJECT, ARRAY_A, or ARRAY_N + * @return null|object|array Null on failure. Type is based on $output value. + */ +function get_category_by_path( $category_path, $full_match = true, $output = OBJECT ) { + $category_path = rawurlencode( urldecode( $category_path ) ); + $category_path = str_replace( '%2F', '/', $category_path ); + $category_path = str_replace( '%20', ' ', $category_path ); + $category_paths = '/' . trim( $category_path, '/' ); + $leaf_path = sanitize_title( basename( $category_paths ) ); + $category_paths = explode( '/', $category_paths ); + $full_path = ''; + foreach ( (array) $category_paths as $pathdir ) + $full_path .= ( $pathdir != '' ? '/' : '' ) . sanitize_title( $pathdir ); + + $categories = get_terms( 'category', array('get' => 'all', 'slug' => $leaf_path) ); + + if ( empty( $categories ) ) + return null; + + foreach ( $categories as $category ) { + $path = '/' . $leaf_path; + $curcategory = $category; + while ( ( $curcategory->parent != 0 ) && ( $curcategory->parent != $curcategory->term_id ) ) { + $curcategory = get_term( $curcategory->parent, 'category' ); + if ( is_wp_error( $curcategory ) ) + return $curcategory; + $path = '/' . $curcategory->slug . $path; + } + + if ( $path == $full_path ) { + $category = get_term( $category->term_id, 'category', $output ); + _make_cat_compat( $category ); + return $category; + } + } + + // If full matching is not required, return the first cat that matches the leaf. + if ( ! $full_match ) { + $category = get_term( reset( $categories )->term_id, 'category', $output ); + _make_cat_compat( $category ); + return $category; + } + + return null; +} + +/** + * Retrieve category object by category slug. + * + * @since 2.3.0 + * + * @param string $slug The category slug. + * @return object Category data object + */ +function get_category_by_slug( $slug ) { + $category = get_term_by( 'slug', $slug, 'category' ); + if ( $category ) + _make_cat_compat( $category ); + + return $category; +} + +/** + * Retrieve the ID of a category from its name. + * + * @since 1.0.0 + * + * @param string $cat_name Category name. + * @return int 0, if failure and ID of category on success. + */ +function get_cat_ID( $cat_name ) { + $cat = get_term_by( 'name', $cat_name, 'category' ); + if ( $cat ) + return $cat->term_id; + return 0; +} + +/** + * Retrieve the name of a category from its ID. + * + * @since 1.0.0 + * + * @param int $cat_id Category ID + * @return string Category name, or an empty string if category doesn't exist. + */ +function get_cat_name( $cat_id ) { + $cat_id = (int) $cat_id; + $category = get_term( $cat_id, 'category' ); + if ( ! $category || is_wp_error( $category ) ) + return ''; + return $category->name; +} + +/** + * Check if a category is an ancestor of another category. + * + * You can use either an id or the category object for both parameters. If you + * use an integer the category will be retrieved. + * + * @since 2.1.0 + * + * @param int|object $cat1 ID or object to check if this is the parent category. + * @param int|object $cat2 The child category. + * @return bool Whether $cat2 is child of $cat1 + */ +function cat_is_ancestor_of( $cat1, $cat2 ) { + return term_is_ancestor_of( $cat1, $cat2, 'category' ); +} + +/** + * Sanitizes category data based on context. + * + * @since 2.3.0 + * + * @param object|array $category Category data + * @param string $context Optional. Default is 'display'. + * @return object|array Same type as $category with sanitized data for safe use. + */ +function sanitize_category( $category, $context = 'display' ) { + return sanitize_term( $category, 'category', $context ); +} + +/** + * Sanitizes data in single category key field. + * + * @since 2.3.0 + * + * @param string $field Category key to sanitize + * @param mixed $value Category value to sanitize + * @param int $cat_id Category ID + * @param string $context What filter to use, 'raw', 'display', etc. + * @return mixed Same type as $value after $value has been sanitized. + */ +function sanitize_category_field( $field, $value, $cat_id, $context ) { + return sanitize_term_field( $field, $value, $cat_id, 'category', $context ); +} + +/* Tags */ + +/** + * Retrieves all post tags. + * + * @since 2.3.0 + * @see get_terms() For list of arguments to pass. + * + * @param string|array $args Tag arguments to use when retrieving tags. + * @return array List of tags. + */ +function get_tags( $args = '' ) { + $tags = get_terms( 'post_tag', $args ); + + if ( empty( $tags ) ) { + $return = array(); + return $return; + } + + /** + * Filter the array of term objects returned for the 'post_tag' taxonomy. + * + * @since 2.3.0 + * + * @param array $tags Array of 'post_tag' term objects. + * @param array $args An array of arguments. @see get_terms() + */ + $tags = apply_filters( 'get_tags', $tags, $args ); + return $tags; +} + +/** + * Retrieve post tag by tag ID or tag object. + * + * If you pass the $tag parameter an object, which is assumed to be the tag row + * object retrieved the database. It will cache the tag data. + * + * If you pass $tag an integer of the tag ID, then that tag will + * be retrieved from the database, if it isn't already cached, and pass it back. + * + * If you look at get_term(), then both types will be passed through several + * filters and finally sanitized based on the $filter parameter value. + * + * @since 2.3.0 + * + * @param int|object $tag + * @param string $output Optional. Constant OBJECT, ARRAY_A, or ARRAY_N + * @param string $filter Optional. Default is raw or no WordPress defined filter will applied. + * @return object|array|WP_Error|null Tag data in type defined by $output parameter. WP_Error if $tag is empty, null if it does not exist. + */ +function get_tag( $tag, $output = OBJECT, $filter = 'raw' ) { + return get_term( $tag, 'post_tag', $output, $filter ); +} + +/* Cache */ + +/** + * Remove the category cache data based on ID. + * + * @since 2.1.0 + * + * @param int $id Category ID + */ +function clean_category_cache( $id ) { + clean_term_cache( $id, 'category' ); +} + +/** + * Update category structure to old pre 2.3 from new taxonomy structure. + * + * This function was added for the taxonomy support to update the new category + * structure with the old category one. This will maintain compatibility with + * plugins and themes which depend on the old key or property names. + * + * The parameter should only be passed a variable and not create the array or + * object inline to the parameter. The reason for this is that parameter is + * passed by reference and PHP will fail unless it has the variable. + * + * There is no return value, because everything is updated on the variable you + * pass to it. This is one of the features with using pass by reference in PHP. + * + * @since 2.3.0 + * @access private + * + * @param array|object $category Category Row object or array + */ +function _make_cat_compat( &$category ) { + if ( is_object( $category ) ) { + $category->cat_ID = &$category->term_id; + $category->category_count = &$category->count; + $category->category_description = &$category->description; + $category->cat_name = &$category->name; + $category->category_nicename = &$category->slug; + $category->category_parent = &$category->parent; + } elseif ( is_array( $category ) && isset( $category['term_id'] ) ) { + $category['cat_ID'] = &$category['term_id']; + $category['category_count'] = &$category['count']; + $category['category_description'] = &$category['description']; + $category['cat_name'] = &$category['name']; + $category['category_nicename'] = &$category['slug']; + $category['category_parent'] = &$category['parent']; + } +} diff --git a/wp-includes/certificates/ca-bundle.crt b/wp-includes/certificates/ca-bundle.crt new file mode 100644 index 0000000..d89dd2c --- /dev/null +++ b/wp-includes/certificates/ca-bundle.crt @@ -0,0 +1,3785 @@ +## +## src/wp-includes/certificates/ca-bundle.crt -- Bundle of CA Root Certificates +## +## Certificate data from Mozilla as of: Tue Jan 28 09:38:07 2014 +## +## This is a bundle of X.509 certificates of public Certificate Authorities +## (CA). These were automatically extracted from Mozilla's root certificates +## file (certdata.txt). This file can be found in the mozilla source tree: +## http://mxr.mozilla.org/mozilla-release/source/security/nss/lib/ckfw/builtins/certdata.txt?raw=1 +## +## It contains the certificates in PEM format and therefore +## can be directly used with curl / libcurl / php_curl, or with +## an Apache+mod_ssl webserver for SSL client authentication. +## Just configure this file as the SSLCACertificateFile. +## + + +EE Certification Centre Root CA +=============================== +-----BEGIN CERTIFICATE----- +MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQG +EwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1czEoMCYGA1UEAwwfRUUgQ2Vy +dGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYGCSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIw +MTAxMDMwMTAxMDMwWhgPMjAzMDEyMTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlB +UyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRy +ZSBSb290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUyeuuOF0+W2Ap7kaJjbMeM +TC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvObntl8jixwKIy72KyaOBhU8E2lf/slLo2 +rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIwWFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw +93X2PaRka9ZP585ArQ/dMtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtN +P2MbRMNE1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/zQas8fElyalL1BSZ +MEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEF +BQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEFBQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+Rj +xY6hUFaTlrg4wCQiZrxTFGGVv9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqM +lIpPnTX/dqQGE5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u +uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIWiAYLtqZLICjU +3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/vGVCJYMzpJJUPwssd8m92kMfM +dcGWxZ0= +-----END CERTIFICATE----- + +GTE CyberTrust Global Root +========================== +-----BEGIN CERTIFICATE----- +MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYDVQQKEw9HVEUg +Q29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNvbHV0aW9ucywgSW5jLjEjMCEG +A1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJvb3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEz +MjM1OTAwWjB1MQswCQYDVQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQL +Ex5HVEUgQ3liZXJUcnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0 +IEdsb2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrHiM3dFw4u +sJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTSr41tiGeA5u2ylc9yMcql +HHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X404Wqk2kmhXBIgD8SFcd5tB8FLztimQID +AQABMA0GCSqGSIb3DQEBBAUAA4GBAG3rGwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMW +M4ETCJ57NE7fQMh017l93PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OF +NMQkpw0PlZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/ +-----END CERTIFICATE----- + +Thawte Server CA +================ +-----BEGIN CERTIFICATE----- +MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMCWkExFTATBgNVBAgT +DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3Vs +dGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UE +AxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5j +b20wHhcNOTYwODAxMDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkGA1UEBhMCWkExFTATBgNV +BAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29u +c3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcG +A1UEAxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0 +ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANOkUG7I/1Zr5s9dtuoMaHVHoqrC2oQl +/Kj0R1HahbUgdJSGHg91yekIYfUGbTBuFRkC6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg7 +1CcEJRCXL+eQbcAoQpnXTEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGjEzAR +MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG7oWDTSEwjsrZqG9J +GubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6eQNuozDJ0uW8NxuOzRAvZim+aKZuZ +GCg70eNAKJpaPNW15yAbi8qkq43pUdniTCxZqdq5snUb9kLy78fyGPmJvKP/iiMucEc= +-----END CERTIFICATE----- + +Thawte Premium Server CA +======================== +-----BEGIN CERTIFICATE----- +MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMCWkExFTATBgNVBAgT +DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3Vs +dGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UE +AxMYVGhhd3RlIFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNlcnZl +ckB0aGF3dGUuY29tMB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgc4xCzAJBgNVBAYT +AlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEdMBsGA1UEChMU +VGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2 +aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNlcnZlciBDQTEoMCYGCSqGSIb3DQEJARYZ +cHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2 +aovXwlue2oFBYo847kkEVdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIh +Udib0GfQug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMRuHM/ +qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQQFAAOBgQAm +SCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUIhfzJATj/Tb7yFkJD57taRvvBxhEf +8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZa4JMpAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7t +UCemDaYj+bvLpgcUQg== +-----END CERTIFICATE----- + +Equifax Secure CA +================= +-----BEGIN CERTIFICATE----- +MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJVUzEQMA4GA1UE +ChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 +MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoT +B0VxdWlmYXgxLTArBgNVBAsTJEVxdWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCB +nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPR +fM6fBeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+AcJkVV5MW +8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kCAwEAAaOCAQkwggEFMHAG +A1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UE +CxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoG +A1UdEAQTMBGBDzIwMTgwODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvS +spXXR9gjIBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQFMAMB +Af8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GBAFjOKer89961 +zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y7qj/WsjTVbJmcVfewCHrPSqnI0kB +BIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee95 +70+sB3c4 +-----END CERTIFICATE----- + +Verisign Class 3 Public Primary Certification Authority +======================================================= +-----BEGIN CERTIFICATE----- +MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkGA1UEBhMCVVMx +FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5 +IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVow +XzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAz +IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhEBarsAx94 +f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Ol +hec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0GCSqGSIb3DQEBAgUAA4GBALtMEivPLCYA +TxQT3ab7/AoRhIzzKBxnki98tsX63/Dolbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59Ah +WM1pF+NEHJwZRDmJXNycAA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2Omuf +Tqj/ZA1k +-----END CERTIFICATE----- + +Verisign Class 3 Public Primary Certification Authority - G2 +============================================================ +-----BEGIN CERTIFICATE----- +MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJBgNVBAYTAlVT +MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFy +eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz +dCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVT +MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFy +eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz +dCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCO +FoUgRm1HP9SFIIThbbP4pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71 +lSk8UOg013gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwIDAQAB +MA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSkU01UbSuvDV1Ai2TT +1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7iF6YM40AIOw7n60RzKprxaZLvcRTD +Oaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpYoJ2daZH9 +-----END CERTIFICATE----- + +GlobalSign Root CA +================== +-----BEGIN CERTIFICATE----- +MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx +GTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds +b2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV +BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD +VQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa +DuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc +THAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb +Kk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP +c1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX +gzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF +AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj +Y1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG +j/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH +hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC +X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== +-----END CERTIFICATE----- + +GlobalSign Root CA - R2 +======================= +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4GA1UECxMXR2xv +YmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh +bFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT +aWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln +bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6 +ErPLv4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8eoLrvozp +s6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklqtTleiDTsvHgMCJiEbKjN +S7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzdC9XZzPnqJworc5HGnRusyMvo4KD0L5CL +TfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pazq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6C +ygPCm48CAwEAAaOBnDCBmTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQUm+IHV2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5nbG9i +YWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG3lm0mi3f3BmGLjAN +BgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4GsJ0/WwbgcQ3izDJr86iw8bmEbTUsp +9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu +01yiPqFbQfXf5WRDLenVOavSot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG7 +9G+dwfCMNYxdAfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 +TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== +-----END CERTIFICATE----- + +ValiCert Class 1 VA +=================== +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp +b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh +bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNTIy +MjM0OFoXDTE5MDYyNTIyMjM0OFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0 +d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEg +UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0 +LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDYWYJ6ibiWuqYvaG9YLqdUHAZu9OqNSLwxlBfw8068srg1knaw0KWlAdcAAxIi +GQj4/xEjm84H9b9pGib+TunRf50sQB1ZaG6m+FiwnRqP0z/x3BkGgagO4DrdyFNFCQbmD3DD+kCm +DuJWBQ8YTfwggtFzVXSNdnKgHZ0dwN0/cQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFBoPUn0LBwG +lN+VYH+Wexf+T3GtZMjdd9LvWVXoP+iOBSoh8gfStadS/pyxtuJbdxdA6nLWI8sogTLDAHkY7FkX +icnGah5xyf23dKUlRWnFSKsZ4UWKJWsZ7uW7EvV/96aNUcPwnXS3qT6gpf+2SQMT2iLM7XGCK5nP +Orf1LXLI +-----END CERTIFICATE----- + +ValiCert Class 2 VA +=================== +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp +b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh +bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAw +MTk1NFoXDTE5MDYyNjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0 +d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIg +UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0 +LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDOOnHK5avIWZJV16vYdA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVC +CSRrCl6zfN1SLUzm1NZ9WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7Rf +ZHM047QSv4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9vUJSZ +SWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTuIYEZoDJJKPTEjlbV +UjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwCW/POuZ6lcg5Ktz885hZo+L7tdEy8 +W9ViH0Pd +-----END CERTIFICATE----- + +RSA Root Certificate 1 +====================== +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp +b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh +bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAw +MjIzM1oXDTE5MDYyNjAwMjIzM1owgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0 +d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMg +UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0 +LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDjmFGWHOjVsQaBalfDcnWTq8+epvzzFlLWLU2fNUSoLgRNB0mKOCn1dzfnt6td +3zZxFJmP3MKS8edgkpfs2Ejcv8ECIMYkpChMMFp2bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89H +BFx1cQqYJJgpp0lZpd34t0NiYfPT4tBVPwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFa7AliEZwgs +3x/be0kz9dNnnfS0ChCzycUs4pJqcXgn8nCDQtM+z6lU9PHYkhaM0QTLS6vJn0WuPIqpsHEzXcjF +V9+vqDWzf4mH6eglkrh/hXqu1rweN1gqZ8mRzyqBPu3GOd/APhmcGcwTTYJBtYze4D1gCCAPRX5r +on+jjBXu +-----END CERTIFICATE----- + +Verisign Class 3 Public Primary Certification Authority - G3 +============================================================ +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV +UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv +cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy +dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAMu6nFL8eB8aHm8bN3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1 +EUGO+i2tKmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGukxUc +cLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBmCC+Vk7+qRy+oRpfw +EuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJXwzw3sJ2zq/3avL6QaaiMxTJ5Xpj +055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWuimi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA +ERSWwauSCPc/L8my/uRan2Te2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5f +j267Cz3qWhMeDGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC +/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565pF4ErWjfJXir0 +xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGtTxzhT5yvDwyd93gN2PQ1VoDa +t20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ== +-----END CERTIFICATE----- + +Verisign Class 4 Public Primary Certification Authority - G3 +============================================================ +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQDsoKeLbnVqAc/EfMwvlF7XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV +UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv +cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy +dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFByaW1hcnkg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAK3LpRFpxlmr8Y+1GQ9Wzsy1HyDkniYlS+BzZYlZ3tCD5PUPtbut8XzoIfzk6AzufEUiGXaS +tBO3IFsJ+mGuqPKljYXCKtbeZjbSmwL0qJJgfJxptI8kHtCGUvYynEFYHiK9zUVilQhu0GbdU6LM +8BDcVHOLBKFGMzNcF0C5nk3T875Vg+ixiY5afJqWIpA7iCXy0lOIAgwLePLmNxdLMEYH5IBtptiW +Lugs+BGzOA1mppvqySNb247i8xOOGlktqgLw7KSHZtzBP/XYufTsgsbSPZUd5cBPhMnZo0QoBmrX +Razwa2rvTl/4EYIeOGM0ZlDUPpNz+jDDZq3/ky2X7wMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA +j/ola09b5KROJ1WrIhVZPMq1CtRK26vdoV9TxaBXOcLORyu+OshWv8LZJxA6sQU8wHcxuzrTBXtt +mhwwjIDLk5Mqg6sFUYICABFna/OIYUdfA5PVWw3g8dShMjWFsjrbsIKr0csKvE+MW8VLADsfKoKm +fjaF3H48ZwC15DtS4KjrXRX5xm3wrR0OhbepmnMUWluPQSjA1egtTaRezarZ7c7c2NU8Qh0XwRJd +RTjDOPP8hS6DRkiy1yBfkjaP53kPmF6Z6PDQpLv1U70qzlmwr25/bLvSHgCwIe34QWKCudiyxLtG +UPMxxY8BqHTr9Xgn2uf3ZkPznoM+IKrDNWCRzg== +-----END CERTIFICATE----- + +Entrust.net Secure Server CA +============================ +-----BEGIN CERTIFICATE----- +MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMCVVMxFDASBgNV +BAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5uZXQvQ1BTIGluY29ycC4gYnkg +cmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRl +ZDE6MDgGA1UEAxMxRW50cnVzdC5uZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhv +cml0eTAeFw05OTA1MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIG +A1UEChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBi +eSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1p +dGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQ +aO2f55M28Qpku0f1BBc/I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5 +gXpa0zf3wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OCAdcw +ggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHboIHYpIHVMIHSMQsw +CQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5l +dC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF +bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu +dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0MFqBDzIwMTkw +NTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8BdiE1U9s/8KAGv7UISX8+1i0Bow +HQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAaMAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EA +BAwwChsEVjQuMAMCBJAwDQYJKoZIhvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyN +Ewr75Ji174z4xRAN95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9 +n9cd2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI= +-----END CERTIFICATE----- + +Entrust.net Premium 2048 Secure Server CA +========================================= +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChMLRW50cnVzdC5u +ZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBpbmNvcnAuIGJ5IHJlZi4gKGxp +bWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNV +BAMTKkVudHJ1c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQx +NzUwNTFaFw0yOTA3MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3 +d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTEl +MCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5u +ZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOL +Gp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSr +hRSGlVuXMlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzW +nLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZdenoVve8AjhUi +VBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH4QIDAQABo0IwQDAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJ +KoZIhvcNAQEFBQADggEBADubj1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPy +T/4xmf3IDExoU8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf +zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5bu/8j72gZyxKT +J1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+bYQLCIt+jerXmCHG8+c8eS9e +nNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/ErfF6adulZkMV8gzURZVE= +-----END CERTIFICATE----- + +Baltimore CyberTrust Root +========================= +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJRTESMBAGA1UE +ChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3li +ZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoXDTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMC +SUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFs +dGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKME +uyKrmD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsB +UnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/C +G9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9 +XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjpr +l3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoI +VDaGezq1BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEB +BQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT929hkTI7gQCvlYpNRh +cL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3WgxjkzSswF07r51XgdIGn9w/xZchMB5 +hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsa +Y71k5h+3zvDyny67G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9H +RCwBXbsdtTLSR9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- + +Equifax Secure Global eBusiness CA +================================== +-----BEGIN CERTIFICATE----- +MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT +RXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1aWZheCBTZWN1cmUgR2xvYmFsIGVCdXNp +bmVzcyBDQS0xMB4XDTk5MDYyMTA0MDAwMFoXDTIwMDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMx +HDAaBgNVBAoTE0VxdWlmYXggU2VjdXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJlIEds +b2JhbCBlQnVzaW5lc3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuucXkAJlsTRV +PEnCUdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQytd4zjTov2/KaelpzmKNc6fuKcxtc58O/gGzN +qfTWK8D3+ZmqY6KxRwIP1ORROhI8bIpaVIRw28HFkM9yRcuoWcDNM50/o5brhTMhHD4ePmBudpxn +hcXIw2ECAwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAHMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0j +BBgwFoAUvqigdHJQa0S3ySPY+6j/s1draGwwHQYDVR0OBBYEFL6ooHRyUGtEt8kj2Puo/7NXa2hs +MA0GCSqGSIb3DQEBBAUAA4GBADDiAVGqx+pf2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okEN +I7SS+RkAZ70Br83gcfxaz2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv8qIY +NMR1pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV +-----END CERTIFICATE----- + +Equifax Secure eBusiness CA 1 +============================= +-----BEGIN CERTIFICATE----- +MIICgjCCAeugAwIBAgIBBDANBgkqhkiG9w0BAQQFADBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT +RXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNzIENB +LTEwHhcNOTkwNjIxMDQwMDAwWhcNMjAwNjIxMDQwMDAwWjBTMQswCQYDVQQGEwJVUzEcMBoGA1UE +ChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNz +IENBLTEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAM4vGbwXt3fek6lfWg0XTzQaDJj0ItlZ +1MRoRvC0NcWFAyDGr0WlIVFFQesWWDYyb+JQYmT5/VGcqiTZ9J2DKocKIdMSODRsjQBuWqDZQu4a +IZX5UkxVWsUPOE9G+m34LjXWHXzr4vCwdYDIqROsvojvOm6rXyo4YgKwEnv+j6YDAgMBAAGjZjBk +MBEGCWCGSAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFEp4MlIR21kW +Nl7fwRQ2QGpHfEyhMB0GA1UdDgQWBBRKeDJSEdtZFjZe38EUNkBqR3xMoTANBgkqhkiG9w0BAQQF +AAOBgQB1W6ibAxHm6VZMzfmpTMANmvPMZWnmJXbMWbfWVMMdzZmsGd20hdXgPfxiIKeES1hl8eL5 +lSE/9dR+WB5Hh1Q+WKG1tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN/Bf+ +KpYrtWKmpj29f5JZzVoqgrI3eQ== +-----END CERTIFICATE----- + +AddTrust Low-Value Services Root +================================ +-----BEGIN CERTIFICATE----- +MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRU +cnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMwMTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQsw +CQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBO +ZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ulCDtbKRY6 +54eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6ntGO0/7Gcrjyvd7ZWxbWr +oulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyldI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1 +Zmne3yzxbrww2ywkEtvrNTVokMsAsJchPXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJui +GMx1I4S+6+JNM3GOGvDC+Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8w +HQYDVR0OBBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8EBTAD +AQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBlMQswCQYDVQQGEwJT +RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEw +HwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxt +ZBsfzQ3duQH6lmM0MkhHma6X7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0Ph +iVYrqW9yTkkz43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY +eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJlpz/+0WatC7xr +mYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOAWiFeIc9TVPC6b4nbqKqVz4vj +ccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk= +-----END CERTIFICATE----- + +AddTrust External Root +====================== +-----BEGIN CERTIFICATE----- +MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYD +VQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEw +NDgzOFowbzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRU +cnVzdCBFeHRlcm5hbCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0Eg +Um9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvtH7xsD821 ++iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9uMq/NzgtHj6RQa1wVsfw +Tz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzXmk6vBbOmcZSccbNQYArHE504B4YCqOmo +aSYYkKtMsE8jqzpPhNjfzp/haW+710LXa0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy +2xSoRcRdKn23tNbE7qzNE0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv7 +7+ldU9U0WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYDVR0P +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0Jvf6xCZU7wO94CTL +VBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRk +VHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENB +IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZl +j7DYd7usQWxHYINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 +6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvCNr4TDea9Y355 +e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEXc4g/VhsxOBi0cQ+azcgOno4u +G+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5amnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= +-----END CERTIFICATE----- + +AddTrust Public Services Root +============================= +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSAwHgYDVQQDExdBZGRU +cnVzdCBQdWJsaWMgQ0EgUm9vdDAeFw0wMDA1MzAxMDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJ +BgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5l +dHdvcmsxIDAeBgNVBAMTF0FkZFRydXN0IFB1YmxpYyBDQSBSb290MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+A3S72mnTRqX4jsIMEZBRpS9mVEBV6tsfSlbu +nyNu9DnLoblv8n75XYcmYZ4c+OLspoH4IcUkzBEMP9smcnrHAZcHF/nXGCwwfQ56HmIexkvA/X1i +d9NEHif2P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnPdzRGULD4EfL+OHn3Bzn+UZKXC1sIXzSG +Aa2Il+tmzV7R/9x98oTaunet3IAIx6eH1lWfl2royBFkuucZKT8Rs3iQhCBSWxHveNCD9tVIkNAw +HM+A+WD+eeSI8t0A65RF62WUaUC6wNW0uLp9BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0G +A1UdDgQWBBSBPjfYkrAfd59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zCBjgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDELMAkGA1UEBhMCU0Ux +FDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRUcnVzdCBUVFAgTmV0d29yazEgMB4G +A1UEAxMXQWRkVHJ1c3QgUHVibGljIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4 +JNojVhaTdt02KLmuG7jD8WS6IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL ++YPoRNWyQSW/iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbjPGsye/Kf8Lb93/Ao +GEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bYGozH7ZxOmuASu7VqTITh4SINhwBk/ox9 +Yjllpu9CtoAlEmEBqCQTcAARJl/6NVDFSMwGR+gn2HCNX2TmoUQmXiLsks3/QppEIW1cxeMiHV9H +EufOX1362KqxMy3ZdvJOOjMMK7MtkAY= +-----END CERTIFICATE----- + +AddTrust Qualified Certificates Root +==================================== +-----BEGIN CERTIFICATE----- +MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSMwIQYDVQQDExpBZGRU +cnVzdCBRdWFsaWZpZWQgQ0EgUm9vdDAeFw0wMDA1MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcx +CzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQ +IE5ldHdvcmsxIzAhBgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBSb290MIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoekn0e+EV+vhDTbYjx5eLfpMLXsDBwqxBb/4Oxx +64r1EW7tTw2R0hIYLUkVAcKkIhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G87B4pfYOQnrjfxvM0PC3 +KP0q6p6zsLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i2O+tCBGaKZnhqkRFmhJePp1tUvznoD1o +L/BLcHwTOK28FSXx1s6rosAx1i+f4P8UWfyEk9mHfExUE+uf0S0R+Bg6Ot4l2ffTQO2kBhLEO+GR +wVY18BTcZTYJbqukB8c10cIDMzZbdSZtQvESa0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HU +MIHRMB0GA1UdDgQWBBQ5lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6FrpGkwZzELMAkGA1UE +BhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRUcnVzdCBUVFAgTmV0d29y +azEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVhbGlmaWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQAD +ggEBABmrder4i2VhlRO6aQTvhsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxG +GuoYQ992zPlmhpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx95dr6h+sNNVJn0J6X +dgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKFYqa0p9m9N5xotS1WfbC3P6CxB9bpT9ze +RXEwMn8bLgn5v1Kh7sKAPgZcLlVAwRv1cEWw3F369nJad9Jjzc9YiQBCYz95OdBEsIJuQRno3eDB +iFrRHnGTHyQwdOUeqN48Jzd/g66ed8/wMLH/S5noxqE= +-----END CERTIFICATE----- + +Entrust Root Certification Authority +==================================== +-----BEGIN CERTIFICATE----- +MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMCVVMxFjAUBgNV +BAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9DUFMgaXMgaW5jb3Jw +b3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsG +A1UEAxMkRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0 +MloXDTI2MTEyNzIwNTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMu +MTkwNwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSByZWZlcmVu +Y2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNVBAMTJEVudHJ1c3QgUm9v +dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ALaVtkNC+sZtKm9I35RMOVcF7sN5EUFoNu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYsz +A9u3g3s+IIRe7bJWKKf44LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOww +Cj0Yzfv9KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGIrb68 +j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi94DkZfs0Nw4pgHBN +rziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOBsDCBrTAOBgNVHQ8BAf8EBAMCAQYw +DwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAigA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1 +MzQyWjAfBgNVHSMEGDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DH +hmak8fdLQ/uEvW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA +A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9tO1KzKtvn1ISM +Y/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6ZuaAGAT/3B+XxFNSRuzFVJ7yVTa +v52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTS +W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0 +tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8 +-----END CERTIFICATE----- + +RSA Security 2048 v3 +==================== +-----BEGIN CERTIFICATE----- +MIIDYTCCAkmgAwIBAgIQCgEBAQAAAnwAAAAKAAAAAjANBgkqhkiG9w0BAQUFADA6MRkwFwYDVQQK +ExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0EgU2VjdXJpdHkgMjA0OCBWMzAeFw0wMTAy +MjIyMDM5MjNaFw0yNjAyMjIyMDM5MjNaMDoxGTAXBgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAb +BgNVBAsTFFJTQSBTZWN1cml0eSAyMDQ4IFYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAt49VcdKA3XtpeafwGFAyPGJn9gqVB93mG/Oe2dJBVGutn3y+Gc37RqtBaB4Y6lXIL5F4iSj7 +Jylg/9+PjDvJSZu1pJTOAeo+tWN7fyb9Gd3AIb2E0S1PRsNO3Ng3OTsor8udGuorryGlwSMiuLgb +WhOHV4PR8CDn6E8jQrAApX2J6elhc5SYcSa8LWrg903w8bYqODGBDSnhAMFRD0xS+ARaqn1y07iH +KrtjEAMqs6FPDVpeRrc9DvV07Jmf+T0kgYim3WBU6JU2PcYJk5qjEoAAVZkZR73QpXzDuvsf9/UP ++Ky5tfQ3mBMY3oVbtwyCO4dvlTlYMNpuAWgXIszACwIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/ +MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQHw1EwpKrpRa41JPr/JCwz0LGdjDAdBgNVHQ4E +FgQUB8NRMKSq6UWuNST6/yQsM9CxnYwwDQYJKoZIhvcNAQEFBQADggEBAF8+hnZuuDU8TjYcHnmY +v/3VEhF5Ug7uMYm83X/50cYVIeiKAVQNOvtUudZj1LGqlk2iQk3UUx+LEN5/Zb5gEydxiKRz44Rj +0aRV4VCT5hsOedBnvEbIvz8XDZXmxpBp3ue0L96VfdASPz0+f00/FGj1EVDVwfSQpQgdMWD/YIwj +VAqv/qFuxdF6Kmh4zx6CCiC0H63lhbJqaHVOrSU3lIW+vaHU6rcMSzyd6BIA8F+sDeGscGNz9395 +nzIlQnQFgCi/vcEkllgVsRch6YlL2weIZ/QVrXA+L02FO8K32/6YaCOJ4XQP3vTFhGMpG8zLB8kA +pKnXwiJPZ9d37CAFYd4= +-----END CERTIFICATE----- + +GeoTrust Global CA +================== +-----BEGIN CERTIFICATE----- +MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVTMRYwFAYDVQQK +Ew1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9iYWwgQ0EwHhcNMDIwNTIxMDQw +MDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j +LjEbMBkGA1UEAxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjo +BbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDviS2Aelet +8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU1XupGc1V3sjs0l44U+Vc +T4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagU +vTLrGAMoUgRx5aszPeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVk +DBF9qn1luMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKInZ57Q +zxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfStQWVYrmm3ok9Nns4 +d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcFPseKUgzbFbS9bZvlxrFUaKnjaZC2 +mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Unhw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6p +XE0zX5IJL4hmXXeXxx12E6nV5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvm +Mw== +-----END CERTIFICATE----- + +GeoTrust Global CA 2 +==================== +-----BEGIN CERTIFICATE----- +MIIDZjCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN +R2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFsIENBIDIwHhcNMDQwMzA0MDUw +MDAwWhcNMTkwMzA0MDUwMDAwWjBEMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j +LjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFsIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDvPE1APRDfO1MA4Wf+lGAVPoWI8YkNkMgoI5kF6CsgncbzYEbYwbLVjDHZ3CB5JIG/ +NTL8Y2nbsSpr7iFY8gjpeMtvy/wWUsiRxP89c96xPqfCfWbB9X5SJBri1WeR0IIQ13hLTytCOb1k +LUCgsBDTOEhGiKEMuzozKmKY+wCdE1l/bztyqu6mD4b5BWHqZ38MN5aL5mkWRxHCJ1kDs6ZgwiFA +Vvqgx306E+PsV8ez1q6diYD3Aecs9pYrEw15LNnA5IZ7S4wMcoKK+xfNAGw6EzywhIdLFnopsk/b +HdQL82Y3vdj2V7teJHq4PIu5+pIaGoSe2HSPqht/XvT+RSIhAgMBAAGjYzBhMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFHE4NvICMVNHK266ZUapEBVYIAUJMB8GA1UdIwQYMBaAFHE4NvICMVNH +K266ZUapEBVYIAUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAQEAA/e1K6tdEPx7 +srJerJsOflN4WT5CBP51o62sgU7XAotexC3IUnbHLB/8gTKY0UvGkpMzNTEv/NgdRN3ggX+d6Yvh +ZJFiCzkIjKx0nVnZellSlxG5FntvRdOW2TF9AjYPnDtuzywNA0ZF66D0f0hExghAzN4bcLUprbqL +OzRldRtxIR0sFAqwlpW41uryZfspuk/qkZN0abby/+Ea0AzRdoXLiiW9l14sbxWZJue2Kf8i7MkC +x1YAzUm5s2x7UwQa4qjJqhIFI8LO57sEAszAR6LkxCkvW0VXiVHuPOtSCP8HNR6fNWpHSlaY0VqF +H4z1Ir+rzoPz4iIprn2DQKi6bA== +-----END CERTIFICATE----- + +GeoTrust Universal CA +===================== +-----BEGIN CERTIFICATE----- +MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN +R2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVyc2FsIENBMB4XDTA0MDMwNDA1 +MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IElu +Yy4xHjAcBgNVBAMTFUdlb1RydXN0IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBAKYVVaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9t +JPi8cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTTQjOgNB0e +RXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFhF7em6fgemdtzbvQKoiFs +7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2vc7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d +8Lsrlh/eezJS/R27tQahsiFepdaVaH/wmZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7V +qnJNk22CDtucvc+081xdVHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3Cga +Rr0BHdCXteGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZf9hB +Z3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfReBi9Fi1jUIxaS5BZu +KGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+nhutxx9z3SxPGWX9f5NAEC7S8O08 +ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0 +XG0D08DYj3rWMB8GA1UdIwQYMBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIB +hjANBgkqhkiG9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc +aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fXIwjhmF7DWgh2 +qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzynANXH/KttgCJwpQzgXQQpAvvL +oJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0zuzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsK +xr2EoyNB3tZ3b4XUhRxQ4K5RirqNPnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxF +KyDuSN/n3QmOGKjaQI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2 +DFKWkoRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9ER/frslK +xfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQtDF4JbAiXfKM9fJP/P6EU +p8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/SfuvmbJxPgWp6ZKy7PtXny3YuxadIwVyQD8vI +P/rmMuGNG2+k5o7Y+SlIis5z/iw= +-----END CERTIFICATE----- + +GeoTrust Universal CA 2 +======================= +-----BEGIN CERTIFICATE----- +MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN +R2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwHhcNMDQwMzA0 +MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3Qg +SW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0 +DE81WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUGFF+3Qs17 +j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdqXbboW0W63MOhBW9Wjo8Q +JqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxLse4YuU6W3Nx2/zu+z18DwPw76L5GG//a +QMJS9/7jOvdqdzXQ2o3rXhhqMcceujwbKNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2 +WP0+GfPtDCapkzj4T8FdIgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP +20gaXT73y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRthAAn +ZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgocQIgfksILAAX/8sgC +SqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4Lt1ZrtmhN79UNdxzMk+MBB4zsslG +8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2 ++/CfXGJx7Tz0RzgQKzAfBgNVHSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8E +BAMCAYYwDQYJKoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z +dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQL1EuxBRa3ugZ +4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgrFg5fNuH8KrUwJM/gYwx7WBr+ +mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSoag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpq +A1Ihn0CoZ1Dy81of398j9tx4TuaYT1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpg +Y+RdM4kX2TGq2tbzGDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiP +pm8m1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJVOCiNUW7d +FGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH6aLcr34YEoP9VhdBLtUp +gn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwXQMAJKOSLakhT2+zNVVXxxvjpoixMptEm +X36vWkzaH6byHCx+rgIW0lbQL1dTR+iS +-----END CERTIFICATE----- + +America Online Root Certification Authority 1 +============================================= +-----BEGIN CERTIFICATE----- +MIIDpDCCAoygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT +QW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBPbmxpbmUgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSAxMB4XDTAyMDUyODA2MDAwMFoXDTM3MTExOTIwNDMwMFowYzELMAkG +A1UEBhMCVVMxHDAaBgNVBAoTE0FtZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2Eg +T25saW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAKgv6KRpBgNHw+kqmP8ZonCaxlCyfqXfaE0bfA+2l2h9LaaLl+lkhsmj76CG +v2BlnEtUiMJIxUo5vxTjWVXlGbR0yLQFOVwWpeKVBeASrlmLojNoWBym1BW32J/X3HGrfpq/m44z +DyL9Hy7nBzbvYjnF3cu6JRQj3gzGPTzOggjmZj7aUTsWOqMFf6Dch9Wc/HKpoH145LcxVR5lu9Rh +sCFg7RAycsWSJR74kEoYeEfffjA3PlAb2xzTa5qGUwew76wGePiEmf4hjUyAtgyC9mZweRrTT6PP +8c9GsEsPPt2IYriMqQkoO3rHl+Ee5fSfwMCuJKDIodkP1nsmgmkyPacCAwEAAaNjMGEwDwYDVR0T +AQH/BAUwAwEB/zAdBgNVHQ4EFgQUAK3Zo/Z59m50qX8zPYEX10zPM94wHwYDVR0jBBgwFoAUAK3Z +o/Z59m50qX8zPYEX10zPM94wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBBQUAA4IBAQB8itEf +GDeC4Liwo+1WlchiYZwFos3CYiZhzRAW18y0ZTTQEYqtqKkFZu90821fnZmv9ov761KyBZiibyrF +VL0lvV+uyIbqRizBs73B6UlwGBaXCBOMIOAbLjpHyx7kADCVW/RFo8AasAFOq73AI25jP4BKxQft +3OJvx8Fi8eNy1gTIdGcL+oiroQHIb/AUr9KZzVGTfu0uOMe9zkZQPXLjeSWdm4grECDdpbgyn43g +Kd8hdIaC2y+CMMbHNYaz+ZZfRtsMRf3zUMNvxsNIrUam4SdHCh0Om7bCd39j8uB9Gr784N/Xx6ds +sPmuujz9dLQR6FgNgLzTqIA6me11zEZ7 +-----END CERTIFICATE----- + +America Online Root Certification Authority 2 +============================================= +-----BEGIN CERTIFICATE----- +MIIFpDCCA4ygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT +QW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBPbmxpbmUgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSAyMB4XDTAyMDUyODA2MDAwMFoXDTM3MDkyOTE0MDgwMFowYzELMAkG +A1UEBhMCVVMxHDAaBgNVBAoTE0FtZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2Eg +T25saW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMjCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAMxBRR3pPU0Q9oyxQcngXssNt79Hc9PwVU3dxgz6sWYFas14tNwC206B89en +fHG8dWOgXeMHDEjsJcQDIPT/DjsS/5uN4cbVG7RtIuOx238hZK+GvFciKtZHgVdEglZTvYYUAQv8 +f3SkWq7xuhG1m1hagLQ3eAkzfDJHA1zEpYNI9FdWboE2JxhP7JsowtS013wMPgwr38oE18aO6lhO +qKSlGBxsRZijQdEt0sdtjRnxrXm3gT+9BoInLRBYBbV4Bbkv2wxrkJB+FFk4u5QkE+XRnRTf04JN +RvCAOVIyD+OEsnpD8l7eXz8d3eOyG6ChKiMDbi4BFYdcpnV1x5dhvt6G3NRI270qv0pV2uh9UPu0 +gBe4lL8BPeraunzgWGcXuVjgiIZGZ2ydEEdYMtA1fHkqkKJaEBEjNa0vzORKW6fIJ/KD3l67Xnfn +6KVuY8INXWHQjNJsWiEOyiijzirplcdIz5ZvHZIlyMbGwcEMBawmxNJ10uEqZ8A9W6Wa6897Gqid +FEXlD6CaZd4vKL3Ob5Rmg0gp2OpljK+T2WSfVVcmv2/LNzGZo2C7HK2JNDJiuEMhBnIMoVxtRsX6 +Kc8w3onccVvdtjc+31D1uAclJuW8tf48ArO3+L5DwYcRlJ4jbBeKuIonDFRH8KmzwICMoCfrHRnj +B453cMor9H124HhnAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFE1FwWg4u3Op +aaEg5+31IqEjFNeeMB8GA1UdIwQYMBaAFE1FwWg4u3OpaaEg5+31IqEjFNeeMA4GA1UdDwEB/wQE +AwIBhjANBgkqhkiG9w0BAQUFAAOCAgEAZ2sGuV9FOypLM7PmG2tZTiLMubekJcmnxPBUlgtk87FY +T15R/LKXeydlwuXK5w0MJXti4/qftIe3RUavg6WXSIylvfEWK5t2LHo1YGwRgJfMqZJS5ivmae2p ++DYtLHe/YUjRYwu5W1LtGLBDQiKmsXeu3mnFzcccobGlHBD7GL4acN3Bkku+KVqdPzW+5X1R+FXg +JXUjhx5c3LqdsKyzadsXg8n33gy8CNyRnqjQ1xU3c6U1uPx+xURABsPr+CKAXEfOAuMRn0T//Zoy +zH1kUQ7rVyZ2OuMeIjzCpjbdGe+n/BLzJsBZMYVMnNjP36TMzCmT/5RtdlwTCJfy7aULTd3oyWgO +ZtMADjMSW7yV5TKQqLPGbIOtd+6Lfn6xqavT4fG2wLHqiMDn05DpKJKUe2h7lyoKZy2FAjgQ5ANh +1NolNscIWC2hp1GvMApJ9aZphwctREZ2jirlmjvXGKL8nDgQzMY70rUXOm/9riW99XJZZLF0Kjhf +GEzfz3EEWjbUvy+ZnOjZurGV5gJLIaFb1cFPj65pbVPbAZO1XB4Y3WRayhgoPmMEEf0cjQAPuDff +Z4qdZqkCapH/E8ovXYO8h5Ns3CRRFgQlZvqz2cK6Kb6aSDiCmfS/O0oxGfm/jiEzFMpPVF/7zvuP +cX/9XhmgD0uRuMRUvAawRY8mkaKO/qk= +-----END CERTIFICATE----- + +Visa eCommerce Root +=================== +-----BEGIN CERTIFICATE----- +MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUFADBrMQswCQYDVQQG +EwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2Ug +QXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNvbW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2 +WhcNMjIwNjI0MDAxNjEyWjBrMQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMm +VmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNv +bW1lcmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvV95WHm6h2mCxlCfL +F9sHP4CFT8icttD0b0/Pmdjh28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4ElpF7sDPwsRROEW+1QK8b +RaVK7362rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdVZqW1LS7YgFmypw23RuwhY/81q6UCzyr0 +TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq299yOIzzlr3xF7zSujtFWsan9sYXiwGd/BmoKoMWuDpI +/k4+oKsGGelT84ATB+0tvz8KPFUgOSwsAGl0lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzs +GHxBvfaLdXe6YJ2E5/4tAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEG +MB0GA1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUFAAOCAQEAX/FBfXxc +CLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcRzCSs00Rsca4BIGsDoo8Ytyk6feUW +YFN4PMCvFYP3j1IzJL1kk5fui/fbGKhtcbP3LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pz +zkWKsKZJ/0x9nXGIxHYdkFsd7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBu +YQa7FkKMcPcw++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/hC3euiInlhBx6yLt +398znM/jra6O1I7mT1GvFpLgXPYHDw== +-----END CERTIFICATE----- + +Certum Root CA +============== +-----BEGIN CERTIFICATE----- +MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQK +ExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBDQTAeFw0wMjA2MTExMDQ2Mzla +Fw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8u +by4xEjAQBgNVBAMTCUNlcnR1bSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6x +wS7TT3zNJc4YPk/EjG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdL +kKWoePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GIULdtlkIJ +89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapuOb7kky/ZR6By6/qmW6/K +Uz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUgAKpoC6EahQGcxEZjgoi2IrHu/qpGWX7P +NSzVttpd90gzFFS269lvzs2I1qsb2pY7HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkq +hkiG9w0BAQUFAAOCAQEAuI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+ +GXYkHAQaTOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTgxSvg +GrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1qCjqTE5s7FCMTY5w/ +0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5xO/fIR/RpbxXyEV6DHpx8Uq79AtoS +qFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs6GAqm4VKQPNriiTsBhYscw== +-----END CERTIFICATE----- + +Comodo AAA Services root +======================== +-----BEGIN CERTIFICATE----- +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS +R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg +TGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAw +MFoXDTI4MTIzMTIzNTk1OVowezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hl +c3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV +BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQuaBtDFcCLNSS1UY8y2bmhG +C1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe3M/vg4aijJRPn2jymJBGhCfHdr/jzDUs +i14HZGWCwEiwqJH5YZ92IFCokcdmtet4YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszW +Y19zjNoFmag4qMsXeDZRrOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjH +Ypy+g8cmez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQUoBEK +Iz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wewYDVR0f +BHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNl +cy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29tb2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2Vz +LmNybDANBgkqhkiG9w0BAQUFAAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm +7l3sAg9g1o1QGE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz +Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z +8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsil2D4kF501KKaU73yqWjgom7C +12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== +-----END CERTIFICATE----- + +Comodo Secure Services root +=========================== +-----BEGIN CERTIFICATE----- +MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS +R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg +TGltaXRlZDEkMCIGA1UEAwwbU2VjdXJlIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAw +MDAwMFoXDTI4MTIzMTIzNTk1OVowfjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFu +Y2hlc3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxJDAi +BgNVBAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAMBxM4KK0HDrc4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPMcm3ye5drswfxdySRXyWP +9nQ95IDC+DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3SHpR7LZQdqnXXs5jLrLxkU0C8j6ysNstc +rbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996CF23uPJAGysnnlDOXmWCiIxe004MeuoIkbY2qitC++rC +oznl2yY4rYsK7hljxxwk3wN42ubqwUcaCwtGCd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3V +p6ea5EQz6YiO/O1R65NxTq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNVHQ4E +FgQUPNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w +gYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL1NlY3VyZUNlcnRpZmlj +YXRlU2VydmljZXMuY3JsMDmgN6A1hjNodHRwOi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlm +aWNhdGVTZXJ2aWNlcy5jcmwwDQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm +4J4oqF7Tt/Q05qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiFGv45jN5bBAS0VPmj +Z55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXGDe+X3EyrEeFryzHRbPtIgKvcnDe4IRRL +DXE97IMzbtFuMhbsmMcWi1mmNKsFVy2T96oTy9IT4rcuO81rUBcJaD61JlfutuC23bkpgHl9j6Pw +pCikFcSF9CfUa7/lXORlAnZUtOM3ZiTTGWHIUhDlizeauan5Hb/qmZJhlv8BzaFfDbxxvA6sCx1H +RR3B7Hzs/Sk= +-----END CERTIFICATE----- + +Comodo Trusted Services root +============================ +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS +R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg +TGltaXRlZDElMCMGA1UEAwwcVHJ1c3RlZCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEw +MDAwMDBaFw0yODEyMzEyMzU5NTlaMH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1h +bmNoZXN0ZXIxEDAOBgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVkMSUw +IwYDVQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNlcnZpY2VzMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA33FvNlhTWvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWWfnJSoBVC21ndZHoa0Lh7 +3TkVvFVIxO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMtTGo87IvDktJTdyR0nAducPy9C1t2ul/y +/9c3S0pgePfw+spwtOpZqqPOSC+pw7ILfhdyFgymBwwbOM/JYrc/oJOlh0Hyt3BAd9i+FHzjqMB6 +juljatEPmsbS9Is6FARW1O24zG71++IsWL1/T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsS +ivnkBbA7kUlcsutT6vifR4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0GA1Ud +DgQWBBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21vZG9jYS5jb20vVHJ1c3RlZENlcnRp +ZmljYXRlU2VydmljZXMuY3JsMDqgOKA2hjRodHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENl +cnRpZmljYXRlU2VydmljZXMuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8Ntw +uleGFTQQuS9/HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdTmw7pSqBYaWcOrp32 +pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+Cl5EfKNsYEYwq5GWDVxISjBc/lDb+XbDA +BHcTuPQV1T84zJQ6VdCsmPW6AF/ghhmBeC8owH7TzEIK9a5QoNE+xqFx7D+gIIxmOom0jtTYsU0l +R+4viMi14QVFwL4Ucd56/Y57fU0IlqUSc/AtyjcndBInTMu2l+nZrghtWjlA3QVHdWpaIbOjGM9O +9y5Xt5hwXsjEeLBi +-----END CERTIFICATE----- + +QuoVadis Root CA +================ +-----BEGIN CERTIFICATE----- +MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJCTTEZMBcGA1UE +ChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 +eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAz +MTkxODMzMzNaFw0yMTAzMTcxODMzMzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRp +cyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQD +EyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Ypli4kVEAkOPcahdxYTMuk +J0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2DrOpm2RgbaIr1VxqYuvXtdj182d6UajtL +F8HVj71lODqV0D1VNk7feVcxKh7YWWVJWCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeL +YzcS19Dsw3sgQUSj7cugF+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWen +AScOospUxbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCCAk4w +PQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVvdmFkaXNvZmZzaG9y +ZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREwggENMIIBCQYJKwYBBAG+WAABMIH7 +MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNlIG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmlj +YXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJs +ZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh +Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYIKwYBBQUHAgEW +Fmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3TKbkGGew5Oanwl4Rqy+/fMIGu +BgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rqy+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkw +FwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6 +tlCLMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSkfnIYj9lo +fFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf87C9TqnN7Az10buYWnuul +LsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1RcHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2x +gI4JVrmcGmD+XcHXetwReNDWXcG31a0ymQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi +5upZIof4l/UO/erMkqQWxFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi +5nrQNiOKSnQ2+Q== +-----END CERTIFICATE----- + +QuoVadis Root CA 2 +================== +-----BEGIN CERTIFICATE----- +MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT +EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMjAeFw0wNjExMjQx +ODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQCaGMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6 +XJxgFyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55JWpzmM+Yk +lvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bBrrcCaoF6qUWD4gXmuVbB +lDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp+ARz8un+XJiM9XOva7R+zdRcAitMOeGy +lZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt +66/3FsvbzSUr5R/7mp/iUcw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1Jdxn +wQ5hYIizPtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og/zOh +D7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UHoycR7hYQe7xFSkyy +BNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuIyV77zGHcizN300QyNQliBJIWENie +J0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1Ud +DgQWBBQahGK8SEwzJQTU7tD2A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGU +a6FJpEcwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT +ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2fBluornFdLwUv +Z+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzng/iN/Ae42l9NLmeyhP3ZRPx3 +UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2BlfF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodm +VjB3pjd4M1IQWK4/YY7yarHvGH5KWWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK ++JDSV6IZUaUtl0HaB0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrW +IozchLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPRTUIZ3Ph1 +WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWDmbA4CD/pXvk1B+TJYm5X +f6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0ZohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II +4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8 +VCLAAVBpQ570su9t+Oza8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u +-----END CERTIFICATE----- + +QuoVadis Root CA 3 +================== +-----BEGIN CERTIFICATE----- +MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT +EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMzAeFw0wNjExMjQx +OTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQDMV0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNgg +DhoB4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUrH556VOij +KTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd8lyyBTNvijbO0BNO/79K +DDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9CabwvvWhDFlaJKjdhkf2mrk7AyxRllDdLkgbv +BNDInIjbC3uBr7E9KsRlOni27tyAsdLTmZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwp +p5ijJUMv7/FfJuGITfhebtfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8 +nT8KKdjcT5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDtWAEX +MJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZc6tsgLjoC2SToJyM +Gf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A4iLItLRkT9a6fUg+qGkM17uGcclz +uD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYDVR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHT +BgkrBgEEAb5YAAMwgcUwgZMGCCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmlj +YXRlIGNvbnN0aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0 +aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVudC4wLQYIKwYB +BQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2NwczALBgNVHQ8EBAMCAQYwHQYD +VR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4GA1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4 +ywLQoUmkRzBFMQswCQYDVQQGEwJCTTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UE +AxMSUXVvVmFkaXMgUm9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZV +qyM07ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSemd1o417+s +hvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd+LJ2w/w4E6oM3kJpK27z +POuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2 +Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadNt54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp +8kokUvd0/bpO5qgdAm6xDYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBC +bjPsMZ57k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6szHXu +g/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0jWy10QJLZYxkNc91p +vGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeTmJlglFwjz1onl14LBQaTNx47aTbr +qZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK4SVhM7JZG+Ju1zdXtg2pEto= +-----END CERTIFICATE----- + +Security Communication Root CA +============================== +-----BEGIN CERTIFICATE----- +MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP +U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw +HhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP +U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw +8yl89f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJDKaVv0uM +DPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9Ms+k2Y7CI9eNqPPYJayX +5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/NQV3Is00qVUarH9oe4kA92819uZKAnDfd +DJZkndwi92SL32HeFZRSFaB9UslLqCHJxrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2 +JChzAgMBAAGjPzA9MB0GA1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYw +DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vGkl3g +0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfrUj94nK9NrvjVT8+a +mCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5Bw+SUEmK3TGXX8npN6o7WWWXlDLJ +s58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJUJRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ +6rBK+1YWc26sTfcioU+tHXotRSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAi +FL39vmwLAw== +-----END CERTIFICATE----- + +Sonera Class 2 Root CA +====================== +-----BEGIN CERTIFICATE----- +MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEPMA0GA1UEChMG +U29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAxMDQwNjA3Mjk0MFoXDTIxMDQw +NjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNVBAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJh +IENsYXNzMiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3 +/Ei9vX+ALTU74W+oZ6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybT +dXnt5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s3TmVToMG +f+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2EjvOr7nQKV0ba5cTppCD8P +tOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu8nYybieDwnPz3BjotJPqdURrBGAgcVeH +nfO+oJAjPYok4doh28MCAwEAAaMzMDEwDwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITT +XjwwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt +0jSv9zilzqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/3DEI +cbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvDFNr450kkkdAdavph +Oe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6Tk6ezAyNlNzZRZxe7EJQY670XcSx +EtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLH +llpwrN9M +-----END CERTIFICATE----- + +Staat der Nederlanden Root CA +============================= +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgIEAJiWijANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJOTDEeMBwGA1UE +ChMVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSYwJAYDVQQDEx1TdGFhdCBkZXIgTmVkZXJsYW5kZW4g +Um9vdCBDQTAeFw0wMjEyMTcwOTIzNDlaFw0xNTEyMTYwOTE1MzhaMFUxCzAJBgNVBAYTAk5MMR4w +HAYDVQQKExVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xJjAkBgNVBAMTHVN0YWF0IGRlciBOZWRlcmxh +bmRlbiBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmNK1URF6gaYUmHFt +vsznExvWJw56s2oYHLZhWtVhCb/ekBPHZ+7d89rFDBKeNVU+LCeIQGv33N0iYfXCxw719tV2U02P +jLwYdjeFnejKScfST5gTCaI+Ioicf9byEGW07l8Y1Rfj+MX94p2i71MOhXeiD+EwR+4A5zN9RGca +C1Hoi6CeUJhoNFIfLm0B8mBF8jHrqTFoKbt6QZ7GGX+UtFE5A3+y3qcym7RHjm+0Sq7lr7HcsBth +vJly3uSJt3omXdozSVtSnA71iq3DuD3oBmrC1SoLbHuEvVYFy4ZlkuxEK7COudxwC0barbxjiDn6 +22r+I/q85Ej0ZytqERAhSQIDAQABo4GRMIGOMAwGA1UdEwQFMAMBAf8wTwYDVR0gBEgwRjBEBgRV +HSAAMDwwOgYIKwYBBQUHAgEWLmh0dHA6Ly93d3cucGtpb3ZlcmhlaWQubmwvcG9saWNpZXMvcm9v +dC1wb2xpY3kwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSofeu8Y6R0E3QA7Jbg0zTBLL9s+DAN +BgkqhkiG9w0BAQUFAAOCAQEABYSHVXQ2YcG70dTGFagTtJ+k/rvuFbQvBgwp8qiSpGEN/KtcCFtR +EytNwiphyPgJWPwtArI5fZlmgb9uXJVFIGzmeafR2Bwp/MIgJ1HI8XxdNGdphREwxgDS1/PTfLbw +MVcoEoJz6TMvplW0C5GUR5z6u3pCMuiufi3IvKwUv9kP2Vv8wfl6leF9fpb8cbDCTMjfRTTJzg3y +nGQI0DvDKcWy7ZAEwbEpkcUwb8GpcjPM/l0WFywRaed+/sWDCN+83CI6LiBpIzlWYGeQiy52OfsR +iJf2fL1LuCAWZwWN4jvBcj+UlTfHXbme2JOhF4//DGYVwSR8MnwDHTuhWEUykw== +-----END CERTIFICATE----- + +TDC Internet Root CA +==================== +-----BEGIN CERTIFICATE----- +MIIEKzCCAxOgAwIBAgIEOsylTDANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJESzEVMBMGA1UE +ChMMVERDIEludGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQgUm9vdCBDQTAeFw0wMTA0MDUx +NjMzMTdaFw0yMTA0MDUxNzAzMTdaMEMxCzAJBgNVBAYTAkRLMRUwEwYDVQQKEwxUREMgSW50ZXJu +ZXQxHTAbBgNVBAsTFFREQyBJbnRlcm5ldCBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAxLhAvJHVYx/XmaCLDEAedLdInUaMArLgJF/wGROnN4NrXceO+YQwzho7+vvOi20j +xsNuZp+Jpd/gQlBn+h9sHvTQBda/ytZO5GhgbEaqHF1j4QeGDmUApy6mcca8uYGoOn0a0vnRrEvL +znWv3Hv6gXPU/Lq9QYjUdLP5Xjg6PEOo0pVOd20TDJ2PeAG3WiAfAzc14izbSysseLlJ28TQx5yc +5IogCSEWVmb/Bexb4/DPqyQkXsN/cHoSxNK1EKC2IeGNeGlVRGn1ypYcNIUXJXfi9i8nmHj9eQY6 +otZaQ8H/7AQ77hPv01ha/5Lr7K7a8jcDR0G2l8ktCkEiu7vmpwIDAQABo4IBJTCCASEwEQYJYIZI +AYb4QgEBBAQDAgAHMGUGA1UdHwReMFwwWqBYoFakVDBSMQswCQYDVQQGEwJESzEVMBMGA1UEChMM +VERDIEludGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQgUm9vdCBDQTENMAsGA1UEAxMEQ1JM +MTArBgNVHRAEJDAigA8yMDAxMDQwNTE2MzMxN1qBDzIwMjEwNDA1MTcwMzE3WjALBgNVHQ8EBAMC +AQYwHwYDVR0jBBgwFoAUbGQBx/2FbazI2p5QCIUItTxWqFAwHQYDVR0OBBYEFGxkAcf9hW2syNqe +UAiFCLU8VqhQMAwGA1UdEwQFMAMBAf8wHQYJKoZIhvZ9B0EABBAwDhsIVjUuMDo0LjADAgSQMA0G +CSqGSIb3DQEBBQUAA4IBAQBOQ8zR3R0QGwZ/t6T609lN+yOfI1Rb5osvBCiLtSdtiaHsmGnc540m +gwV5dOy0uaOXwTUA/RXaOYE6lTGQ3pfphqiZdwzlWqCE/xIWrG64jcN7ksKsLtB9KOy282A4aW8+ +2ARVPp7MVdK6/rtHBNcK2RYKNCn1WBPVT8+PVkuzHu7TmHnaCB4Mb7j4Fifvwm899qNLPg7kbWzb +O0ESm70NRyN/PErQr8Cv9u8btRXE64PECV90i9kR+8JWsTz4cMo0jUNAE4z9mQNUecYu6oah9jrU +Cbz0vGbMPVjQV0kK7iXiQe4T+Zs4NNEA9X7nlB38aQNiuJkFBT1reBK9sG9l +-----END CERTIFICATE----- + +UTN DATACorp SGC Root CA +======================== +-----BEGIN CERTIFICATE----- +MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCBkzELMAkGA1UE +BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl +IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZ +BgNVBAMTElVUTiAtIERBVEFDb3JwIFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBa +MIGTMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4w +HAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRy +dXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ys +raP6LnD43m77VkIVni5c7yPeIbkFdicZD0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlo +wHDyUwDAXlCCpVZvNvlK4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA +9P4yPykqlXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulWbfXv +33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQABo4GrMIGoMAsGA1Ud +DwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRTMtGzz3/64PGgXYVOktKeRR20TzA9 +BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3JsLnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dD +LmNybDAqBgNVHSUEIzAhBggrBgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3 +DQEBBQUAA4IBAQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowft +Gzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyjj98C5OBxOvG0 +I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVHKWss5nbZqSl9Mt3JNjy9rjXx +EZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwP +DPafepE39peC4N1xaf92P2BNPM/3mfnGV/TJVTl4uix5yaaIK/QI +-----END CERTIFICATE----- + +UTN USERFirst Hardware Root CA +============================== +-----BEGIN CERTIFICATE----- +MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UE +BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl +IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAd +BgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgx +OTIyWjCBlzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0 +eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVz +ZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlI +wrthdBKWHTxqctU8EGc6Oe0rE81m65UJM6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFd +tqdt++BxF2uiiPsA3/4aMXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8 +i4fDidNdoI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqIDsjf +Pe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9KsyoUhbAgMBAAGjgbkw +gbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFKFyXyYbKJhDlV0HN9WF +lp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNF +UkZpcnN0LUhhcmR3YXJlLmNybDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUF +BwMGBggrBgEFBQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM +//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28GpgoiskliCE7/yMgUsogW +XecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gECJChicsZUN/KHAG8HQQZexB2 +lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kn +iCrVWFCVH/A7HFe7fRQ5YiuayZSSKqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67 +nfhmqA== +-----END CERTIFICATE----- + +Camerfirma Chambers of Commerce Root +==================================== +-----BEGIN CERTIFICATE----- +MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe +QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i +ZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAx +NjEzNDNaFw0zNzA5MzAxNjEzNDRaMH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZp +cm1hIFNBIENJRiBBODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3Jn +MSIwIAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0BAQEFAAOC +AQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtbunXF/KGIJPov7coISjlU +xFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0dBmpAPrMMhe5cG3nCYsS4No41XQEMIwRH +NaqbYE6gZj3LJgqcQKH0XZi/caulAGgq7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jW +DA+wWFjbw2Y3npuRVDM30pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFV +d9oKDMyXroDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIGA1Ud +EwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5jaGFtYmVyc2lnbi5v +cmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p26EpW1eLTXYGduHRooowDgYDVR0P +AQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hh +bWJlcnNpZ24ub3JnMCcGA1UdEgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYD +VR0gBFEwTzBNBgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz +aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEBAAxBl8IahsAi +fJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZdp0AJPaxJRUXcLo0waLIJuvvD +L8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wN +UPf6s+xCX6ndbcj0dc97wXImsQEcXCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/n +ADydb47kMgkdTXg0eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1 +erfutGWaIZDgqtCYvDi1czyL+Nw= +-----END CERTIFICATE----- + +Camerfirma Global Chambersign Root +================================== +-----BEGIN CERTIFICATE----- +MIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe +QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i +ZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYx +NDE4WhcNMzcwOTMwMTYxNDE4WjB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJt +YSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEg +MB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUAA4IBDQAw +ggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0Mi+ITaFgCPS3CU6gSS9J +1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/sQJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8O +by4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpVeAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl +6DJWk0aJqCWKZQbua795B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c +8lCrEqWhz0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0TAQH/ +BAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1iZXJzaWduLm9yZy9j +aGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4wTcbOX60Qq+UDpfqpFDAOBgNVHQ8B +Af8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAHMCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBj +aGFtYmVyc2lnbi5vcmcwKgYDVR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9y +ZzBbBgNVHSAEVDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh +bWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0BAQUFAAOCAQEA +PDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUMbKGKfKX0j//U2K0X1S0E0T9Y +gOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXiryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJ +PJ7oKXqJ1/6v/2j1pReQvayZzKWGVwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4 +IBHNfTIzSJRUTN3cecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREes +t2d/AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A== +-----END CERTIFICATE----- + +NetLock Notary (Class A) Root +============================= +-----BEGIN CERTIFICATE----- +MIIGfTCCBWWgAwIBAgICAQMwDQYJKoZIhvcNAQEEBQAwga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQI +EwdIdW5nYXJ5MREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6 +dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9j +ayBLb3pqZWd5em9pIChDbGFzcyBBKSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNDIzMTQ0N1oX +DTE5MDIxOTIzMTQ0N1owga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQH +EwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQuMRowGAYD +VQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBLb3pqZWd5em9pIChDbGFz +cyBBKSBUYW51c2l0dmFueWtpYWRvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvHSM +D7tM9DceqQWC2ObhbHDqeLVu0ThEDaiDzl3S1tWBxdRL51uUcCbbO51qTGL3cfNk1mE7PetzozfZ +z+qMkjvN9wfcZnSX9EUi3fRc4L9t875lM+QVOr/bmJBVOMTtplVjC7B4BPTjbsE/jvxReB+SnoPC +/tmwqcm8WgD/qaiYdPv2LD4VOQ22BFWoDpggQrOxJa1+mm9dU7GrDPzr4PN6s6iz/0b2Y6LYOph7 +tqyF/7AlT3Rj5xMHpQqPBffAZG9+pyeAlt7ULoZgx2srXnN7F+eRP2QM2EsiNCubMvJIH5+hCoR6 +4sKtlz2O1cH5VqNQ6ca0+pii7pXmKgOM3wIDAQABo4ICnzCCApswDgYDVR0PAQH/BAQDAgAGMBIG +A1UdEwEB/wQIMAYBAf8CAQQwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaC +Ak1GSUdZRUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pv +bGdhbHRhdGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQu +IEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2Vn +LWJpenRvc2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0 +ZXRlbGUgYXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFz +IGxlaXJhc2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBh +IGh0dHBzOi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVu +b3J6ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1YW5jZSBh +bmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sg +Q1BTIGF2YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFp +bCBhdCBjcHNAbmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4IBAQBIJEb3ulZv+sgoA0BO5TE5 +ayZrU3/b39/zcT0mwBQOxmd7I6gMc90Bu8bKbjc5VdXHjFYgDigKDtIqpLBJUsY4B/6+CgmM0ZjP +ytoUMaFP0jn8DxEsQ8Pdq5PHVT5HfBgaANzze9jyf1JsIPQLX2lS9O74silg6+NJMSEN1rUQQeJB +CWziGppWS3cC9qCbmieH6FUpccKQn0V4GuEVZD3QDtigdp+uxdAu6tYPVuxkf1qbFFgBJ34TUMdr +KuZoPL9coAob4Q566eKAw+np9v1sEZ7Q5SgnK1QyQhSCdeZK8CtmdWOMovsEPoMOmzbwGOQmIMOM +8CgHrTwXZoi1/baI +-----END CERTIFICATE----- + +NetLock Business (Class B) Root +=============================== +-----BEGIN CERTIFICATE----- +MIIFSzCCBLSgAwIBAgIBaTANBgkqhkiG9w0BAQQFADCBmTELMAkGA1UEBhMCSFUxETAPBgNVBAcT +CEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0b25zYWdpIEtmdC4xGjAYBgNV +BAsTEVRhbnVzaXR2YW55a2lhZG9rMTIwMAYDVQQDEylOZXRMb2NrIFV6bGV0aSAoQ2xhc3MgQikg +VGFudXNpdHZhbnlraWFkbzAeFw05OTAyMjUxNDEwMjJaFw0xOTAyMjAxNDEwMjJaMIGZMQswCQYD +VQQGEwJIVTERMA8GA1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRv +bnNhZ2kgS2Z0LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxMjAwBgNVBAMTKU5ldExvY2sg +VXpsZXRpIChDbGFzcyBCKSBUYW51c2l0dmFueWtpYWRvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB +iQKBgQCx6gTsIKAjwo84YM/HRrPVG/77uZmeBNwcf4xKgZjupNTKihe5In+DCnVMm8Bp2GQ5o+2S +o/1bXHQawEfKOml2mrriRBf8TKPV/riXiK+IA4kfpPIEPsgHC+b5sy96YhQJRhTKZPWLgLViqNhr +1nGTLbO/CVRY7QbrqHvcQ7GhaQIDAQABo4ICnzCCApswEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNV +HQ8BAf8EBAMCAAYwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1GSUdZ +RUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pvbGdhbHRh +dGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQuIEEgaGl0 +ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2VnLWJpenRv +c2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUg +YXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJh +c2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBhIGh0dHBz +Oi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVub3J6ZXNA +bmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1YW5jZSBhbmQgdGhl +IHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sgQ1BTIGF2 +YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBj +cHNAbmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4GBAATbrowXr/gOkDFOzT4JwG06sPgzTEdM +43WIEJessDgVkcYplswhwG08pXTP2IKlOcNl40JwuyKQ433bNXbhoLXan3BukxowOR0w2y7jfLKR +stE3Kfq51hdcR0/jHTjrn9V7lagonhVK0dHQKwCXoOKSNitjrFgBazMpUIaD8QFI +-----END CERTIFICATE----- + +NetLock Express (Class C) Root +============================== +-----BEGIN CERTIFICATE----- +MIIFTzCCBLigAwIBAgIBaDANBgkqhkiG9w0BAQQFADCBmzELMAkGA1UEBhMCSFUxETAPBgNVBAcT +CEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0b25zYWdpIEtmdC4xGjAYBgNV +BAsTEVRhbnVzaXR2YW55a2lhZG9rMTQwMgYDVQQDEytOZXRMb2NrIEV4cHJlc3N6IChDbGFzcyBD +KSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNTE0MDgxMVoXDTE5MDIyMDE0MDgxMVowgZsxCzAJ +BgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6 +dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE0MDIGA1UEAxMrTmV0TG9j +ayBFeHByZXNzeiAoQ2xhc3MgQykgVGFudXNpdHZhbnlraWFkbzCBnzANBgkqhkiG9w0BAQEFAAOB +jQAwgYkCgYEA6+ywbGGKIyWvYCDj2Z/8kwvbXY2wobNAOoLO/XXgeDIDhlqGlZHtU/qdQPzm6N3Z +W3oDvV3zOwzDUXmbrVWg6dADEK8KuhRC2VImESLH0iDMgqSaqf64gXadarfSNnU+sYYJ9m5tfk63 +euyucYT2BDMIJTLrdKwWRMbkQJMdf60CAwEAAaOCAp8wggKbMBIGA1UdEwEB/wQIMAYBAf8CAQQw +DgYDVR0PAQH/BAQDAgAGMBEGCWCGSAGG+EIBAQQEAwIABzCCAmAGCWCGSAGG+EIBDQSCAlEWggJN +RklHWUVMRU0hIEV6ZW4gdGFudXNpdHZhbnkgYSBOZXRMb2NrIEtmdC4gQWx0YWxhbm9zIFN6b2xn +YWx0YXRhc2kgRmVsdGV0ZWxlaWJlbiBsZWlydCBlbGphcmFzb2sgYWxhcGphbiBrZXN6dWx0LiBB +IGhpdGVsZXNpdGVzIGZvbHlhbWF0YXQgYSBOZXRMb2NrIEtmdC4gdGVybWVrZmVsZWxvc3NlZy1i +aXp0b3NpdGFzYSB2ZWRpLiBBIGRpZ2l0YWxpcyBhbGFpcmFzIGVsZm9nYWRhc2FuYWsgZmVsdGV0 +ZWxlIGF6IGVsb2lydCBlbGxlbm9yemVzaSBlbGphcmFzIG1lZ3RldGVsZS4gQXogZWxqYXJhcyBs +ZWlyYXNhIG1lZ3RhbGFsaGF0byBhIE5ldExvY2sgS2Z0LiBJbnRlcm5ldCBob25sYXBqYW4gYSBo +dHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIGNpbWVuIHZhZ3kga2VyaGV0byBheiBlbGxlbm9y +emVzQG5ldGxvY2submV0IGUtbWFpbCBjaW1lbi4gSU1QT1JUQU5UISBUaGUgaXNzdWFuY2UgYW5k +IHRoZSB1c2Ugb2YgdGhpcyBjZXJ0aWZpY2F0ZSBpcyBzdWJqZWN0IHRvIHRoZSBOZXRMb2NrIENQ +UyBhdmFpbGFibGUgYXQgaHR0cHM6Ly93d3cubmV0bG9jay5uZXQvZG9jcyBvciBieSBlLW1haWwg +YXQgY3BzQG5ldGxvY2submV0LjANBgkqhkiG9w0BAQQFAAOBgQAQrX/XDDKACtiG8XmYta3UzbM2 +xJZIwVzNmtkFLp++UOv0JhQQLdRmF/iewSf98e3ke0ugbLWrmldwpu2gpO0u9f38vf5NNwgMvOOW +gyL1SRt/Syu0VMGAfJlOHdCM7tCs5ZL6dVb+ZKATj7i4Fp1hBWeAyNDYpQcCNJgEjTME1A== +-----END CERTIFICATE----- + +XRamp Global CA Root +==================== +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UE +BhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2Vj +dXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwHhcNMDQxMTAxMTcxNDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMx +HjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkg +U2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS638eMpSe2OAtp87ZOqCwu +IR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCPKZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMx +foArtYzAQDsRhtDLooY2YKTVMIJt2W7QDxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FE +zG+gSqmUsE3a56k0enI4qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqs +AxcZZPRaJSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNViPvry +xS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASsjVy16bYbMDYGA1UdHwQvMC0wK6Ap +oCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMC +AQEwDQYJKoZIhvcNAQEFBQADggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc +/Kh4ZzXxHfARvbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt +qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLaIR9NmXmd4c8n +nxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSyi6mx5O+aGtA9aZnuqCij4Tyz +8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQO+7ETPTsJ3xCwnR8gooJybQDJbw= +-----END CERTIFICATE----- + +Go Daddy Class 2 CA +=================== +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMY +VGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkG +A1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28g +RGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQAD +ggENADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCAPVYYYwhv +2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6wwdhFJ2+qN1j3hybX2C32 +qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXiEqITLdiOr18SPaAIBQi2XKVlOARFmR6j +YGB0xUGlcmIbYsUfb18aQr4CUWWoriMYavx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmY +vLEHZ6IVDd2gWMZEewo+YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0O +BBYEFNLEsNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h/t2o +atTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMu +MTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwG +A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wim +PQoZ+YeAEW5p5JYXMP80kWNyOO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKt +I3lpjbi2Tc7PTMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ +HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mERdEr/VxqHD3VI +Ls9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5CufReYNnyicsbkqWletNw+vHX/b +vZ8= +-----END CERTIFICATE----- + +Starfield Class 2 CA +==================== +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzElMCMGA1UEChMc +U3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZpZWxkIENsYXNzIDIg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBo +MQswCQYDVQQGEwJVUzElMCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAG +A1UECxMpU3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqG +SIb3DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf8MOh2tTY +bitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN+lq2cwQlZut3f+dZxkqZ +JRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVm +epsZGD3/cVE8MC5fvj13c7JdBmzDI1aaK4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSN +F4Azbl5KXZnJHoe0nRrA1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HF +MIHCMB0GA1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fRzt0f +hvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNo +bm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBDbGFzcyAyIENlcnRpZmljYXRpb24g +QXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGs +afPzWdqbAYcaT1epoXkJKtv3L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLM +PUxA2IGvd56Deruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl +xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynpVSJYACPq4xJD +KVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEYWQPJIrSPnNVeKtelttQKbfi3 +QBFGmh95DmK/D5fs4C8fF5Q= +-----END CERTIFICATE----- + +StartCom Certification Authority +================================ +-----BEGIN CERTIFICATE----- +MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMN +U3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmlu +ZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0 +NjM2WhcNMzYwOTE3MTk0NjM2WjB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRk +LjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMg +U3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw +ggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZkpMyONvg45iPwbm2xPN1y +o4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rfOQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/ +Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/CJi/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/d +eMotHweXMAEtcnn6RtYTKqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt +2PZE4XNiHzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMMAv+Z +6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w+2OqqGwaVLRcJXrJ +osmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/ +untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVc +UjyJthkqcwEKDwOzEmDyei+B26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT +37uMdBNSSwIDAQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE +FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9jZXJ0LnN0YXJ0 +Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3JsLnN0YXJ0Y29tLm9yZy9zZnNj +YS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFMBgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUH +AgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRw +Oi8vY2VydC5zdGFydGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYg +U3RhcnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlhYmlsaXR5 +LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2YgdGhlIFN0YXJ0Q29tIENl +cnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFpbGFibGUgYXQgaHR0cDovL2NlcnQuc3Rh +cnRjb20ub3JnL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilT +dGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOC +AgEAFmyZ9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8jhvh +3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUWFjgKXlf2Ysd6AgXm +vB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJzewT4F+irsfMuXGRuczE6Eri8sxHk +fY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3 +fsNrarnDy0RLrHiQi+fHLB5LEUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZ +EoalHmdkrQYuL6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq +yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuCO3NJo2pXh5Tl +1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6Vum0ABj6y6koQOdjQK/W/7HW/ +lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkyShNOsF/5oirpt9P/FlUQqmMGqz9IgcgA38coro +g14= +-----END CERTIFICATE----- + +Taiwan GRCA +=========== +-----BEGIN CERTIFICATE----- +MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/MQswCQYDVQQG +EwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4X +DTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1owPzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dv +dmVybm1lbnQgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qN +w8XRIePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1qgQdW8or5 +BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKyyhwOeYHWtXBiCAEuTk8O +1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAtsF/tnyMKtsc2AtJfcdgEWFelq16TheEfO +htX7MfP6Mb40qij7cEwdScevLJ1tZqa2jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wov +J5pGfaENda1UhhXcSTvxls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7 +Q3hub/FCVGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHKYS1t +B6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoHEgKXTiCQ8P8NHuJB +O9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThNXo+EHWbNxWCWtFJaBYmOlXqYwZE8 +lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1UdDgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNV +HRMEBTADAQH/MDkGBGcqBwAEMTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg2 +09yewDL7MTqKUWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ +TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyfqzvS/3WXy6Tj +Zwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaKZEk9GhiHkASfQlK3T8v+R0F2 +Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFEJPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlU +D7gsL0u8qV1bYH+Mh6XgUmMqvtg7hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6Qz +DxARvBMB1uUO07+1EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+Hbk +Z6MmnD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WXudpVBrkk +7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44VbnzssQwmSNOXfJIoRIM3BKQ +CZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDeLMDDav7v3Aun+kbfYNucpllQdSNpc5Oy ++fwC00fmcc4QAu4njIT/rEUNE1yDMuAlpYYsfPQS +-----END CERTIFICATE----- + +Firmaprofesional Root CA +======================== +-----BEGIN CERTIFICATE----- +MIIEVzCCAz+gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBnTELMAkGA1UEBhMCRVMxIjAgBgNVBAcT +GUMvIE11bnRhbmVyIDI0NCBCYXJjZWxvbmExQjBABgNVBAMTOUF1dG9yaWRhZCBkZSBDZXJ0aWZp +Y2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODEmMCQGCSqGSIb3DQEJARYXY2FA +ZmlybWFwcm9mZXNpb25hbC5jb20wHhcNMDExMDI0MjIwMDAwWhcNMTMxMDI0MjIwMDAwWjCBnTEL +MAkGA1UEBhMCRVMxIjAgBgNVBAcTGUMvIE11bnRhbmVyIDI0NCBCYXJjZWxvbmExQjBABgNVBAMT +OUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2 +ODEmMCQGCSqGSIb3DQEJARYXY2FAZmlybWFwcm9mZXNpb25hbC5jb20wggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDnIwNvbyOlXnjOlSztlB5uCp4Bx+ow0Syd3Tfom5h5VtP8c9/Qit5V +j1H5WuretXDE7aTt/6MNbg9kUDGvASdYrv5sp0ovFy3Tc9UTHI9ZpTQsHVQERc1ouKDAA6XPhUJH +lShbz++AbOCQl4oBPB3zhxAwJkh91/zpnZFx/0GaqUC1N5wpIE8fUuOgfRNtVLcK3ulqTgesrBlf +3H5idPayBQC6haD9HThuy1q7hryUZzM1gywfI834yJFxzJeL764P3CkDG8A563DtwW4O2GcLiam8 +NeTvtjS0pbbELaW+0MOUJEjb35bTALVmGotmBQ/dPz/LP6pemkr4tErvlTcbAgMBAAGjgZ8wgZww +KgYDVR0RBCMwIYYfaHR0cDovL3d3dy5maXJtYXByb2Zlc2lvbmFsLmNvbTASBgNVHRMBAf8ECDAG +AQH/AgEBMCsGA1UdEAQkMCKADzIwMDExMDI0MjIwMDAwWoEPMjAxMzEwMjQyMjAwMDBaMA4GA1Ud +DwEB/wQEAwIBBjAdBgNVHQ4EFgQUMwugZtHq2s7eYpMEKFK1FH84aLcwDQYJKoZIhvcNAQEFBQAD +ggEBAEdz/o0nVPD11HecJ3lXV7cVVuzH2Fi3AQL0M+2TUIiefEaxvT8Ub/GzR0iLjJcG1+p+o1wq +u00vR+L4OQbJnC4xGgN49Lw4xiKLMzHwFgQEffl25EvXwOaD7FnMP97/T2u3Z36mhoEyIwOdyPdf +wUpgpZKpsaSgYMN4h7Mi8yrrW6ntBas3D7Hi05V2Y1Z0jFhyGzflZKG+TQyTmAyX9odtsz/ny4Cm +7YjHX1BiAuiZdBbQ5rQ58SfLyEDW44YQqSMSkuBpQWOnryULwMWSyx6Yo1q6xTMPoJcB3X/ge9YG +VM+h4k0460tQtcsm9MracEpqoeJ5quGnM/b9Sh/22WA= +-----END CERTIFICATE----- + +Swisscom Root CA 1 +================== +-----BEGIN CERTIFICATE----- +MIIF2TCCA8GgAwIBAgIQXAuFXAvnWUHfV8w/f52oNjANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQG +EwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0YWwgQ2VydGlmaWNhdGUgU2Vy +dmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3QgQ0EgMTAeFw0wNTA4MTgxMjA2MjBaFw0yNTA4 +MTgyMjA2MjBaMGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGln +aXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAxMIIC +IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0LmwqAzZuz8h+BvVM5OAFmUgdbI9m2BtRsiM +MW8Xw/qabFbtPMWRV8PNq5ZJkCoZSx6jbVfd8StiKHVFXqrWW/oLJdihFvkcxC7mlSpnzNApbjyF +NDhhSbEAn9Y6cV9Nbc5fuankiX9qUvrKm/LcqfmdmUc/TilftKaNXXsLmREDA/7n29uj/x2lzZAe +AR81sH8A25Bvxn570e56eqeqDFdvpG3FEzuwpdntMhy0XmeLVNxzh+XTF3xmUHJd1BpYwdnP2IkC +b6dJtDZd0KTeByy2dbcokdaXvij1mB7qWybJvbCXc9qukSbraMH5ORXWZ0sKbU/Lz7DkQnGMU3nn +7uHbHaBuHYwadzVcFh4rUx80i9Fs/PJnB3r1re3WmquhsUvhzDdf/X/NTa64H5xD+SpYVUNFvJbN +cA78yeNmuk6NO4HLFWR7uZToXTNShXEuT46iBhFRyePLoW4xCGQMwtI89Tbo19AOeCMgkckkKmUp +WyL3Ic6DXqTz3kvTaI9GdVyDCW4pa8RwjPWd1yAv/0bSKzjCL3UcPX7ape8eYIVpQtPM+GP+HkM5 +haa2Y0EQs3MevNP6yn0WR+Kn1dCjigoIlmJWbjTb2QK5MHXjBNLnj8KwEUAKrNVxAmKLMb7dxiNY +MUJDLXT5xp6mig/p/r+D5kNXJLrvRjSq1xIBOO0CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYw +HQYDVR0hBBYwFDASBgdghXQBUwABBgdghXQBUwABMBIGA1UdEwEB/wQIMAYBAf8CAQcwHwYDVR0j +BBgwFoAUAyUv3m+CATpcLNwroWm1Z9SM0/0wHQYDVR0OBBYEFAMlL95vggE6XCzcK6FptWfUjNP9 +MA0GCSqGSIb3DQEBBQUAA4ICAQA1EMvspgQNDQ/NwNurqPKIlwzfky9NfEBWMXrrpA9gzXrzvsMn +jgM+pN0S734edAY8PzHyHHuRMSG08NBsl9Tpl7IkVh5WwzW9iAUPWxAaZOHHgjD5Mq2eUCzneAXQ +MbFamIp1TpBcahQq4FJHgmDmHtqBsfsUC1rxn9KVuj7QG9YVHaO+htXbD8BJZLsuUBlL0iT43R4H +VtA4oJVwIHaM190e3p9xxCPvgxNcoyQVTSlAPGrEqdi3pkSlDfTgnXceQHAm/NrZNuR55LU/vJtl +vrsRls/bxig5OgjOR1tTWsWZ/l2p3e9M1MalrQLmjAcSHm8D0W+go/MpvRLHUKKwf4ipmXeascCl +OS5cfGniLLDqN2qk4Vrh9VDlg++luyqI54zb/W1elxmofmZ1a3Hqv7HHb6D0jqTsNFFbjCYDcKF3 +1QESVwA12yPeDooomf2xEG9L/zgtYE4snOtnta1J7ksfrK/7DZBaZmBwXarNeNQk7shBoJMBkpxq +nvy5JMWzFYJ+vq6VK+uxwNrjAWALXmmshFZhvnEX/h0TD/7Gh0Xp/jKgGg0TpJRVcaUWi7rKibCy +x/yP2FS1k2Kdzs9Z+z0YzirLNRWCXf9UIltxUvu3yf5gmwBBZPCqKuy2QkPOiWaByIufOVQDJdMW +NY6E0F/6MBr1mmz0DlP5OlvRHA== +-----END CERTIFICATE----- + +DigiCert Assured ID Root CA +=========================== +-----BEGIN CERTIFICATE----- +MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw +IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzEx +MTEwMDAwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL +ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0Ew +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7cJpSIqvTO +9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYPmDI2dsze3Tyoou9q+yHy +UmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW +/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpy +oeb6pNnVFzF1roV9Iq4/AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whf +GHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRF +66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkq +hkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2Bc +EkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38Fn +SbNd67IJKusm7Xi+fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i +8b5QZ7dsvfPxH2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe ++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== +-----END CERTIFICATE----- + +DigiCert Global Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw +HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAw +MDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 +dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsBCSDMAZOn +TjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5 +BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H +4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y +7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQAB +o2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm +8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEF +BQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmr +EbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIt +tep3Sp+dWOIrWcBAI+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886 +UAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- + +DigiCert High Assurance EV Root CA +================================== +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSsw +KQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAw +MFoXDTMxMTExMDAwMDAwMFowbDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ +MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFu +Y2UgRVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm+9S75S0t +Mqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTWPNt0OKRKzE0lgvdKpVMS +OO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEMxChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3 +MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFBIk5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQ +NAQTXKFx01p8VdteZOE3hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUe +h10aUAsgEsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB +Af8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSY +JhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3NecnzyIZgYIVyHbIUf4KmeqvxgydkAQ +V8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6zeM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFp +myPInngiK3BD41VHMWEZ71jFhS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkK +mNEVX58Svnw2Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K +-----END CERTIFICATE----- + +Certplus Class 2 Primary CA +=========================== +-----BEGIN CERTIFICATE----- +MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAwPTELMAkGA1UE +BhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFzcyAyIFByaW1hcnkgQ0EwHhcN +OTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2Vy +dHBsdXMxGzAZBgNVBAMTEkNsYXNzIDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBANxQltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR +5aiRVhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyLkcAbmXuZ +Vg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCdEgETjdyAYveVqUSISnFO +YFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yasH7WLO7dDWWuwJKZtkIvEcupdM5i3y95e +e++U8Rs+yskhwcWYAqqi9lt3m/V+llU0HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRME +CDAGAQH/AgEKMAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJ +YIZIAYb4QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMuY29t +L0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/AN9WM2K191EBkOvD +P9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8yfFC82x/xXp8HVGIutIKPidd3i1R +TtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMRFcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+ +7UCmnYR0ObncHoUW2ikbhiMAybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW +//1IMwrh3KWBkJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7 +l7+ijrRU +-----END CERTIFICATE----- + +DST Root CA X3 +============== +-----BEGIN CERTIFICATE----- +MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/MSQwIgYDVQQK +ExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMTDkRTVCBSb290IENBIFgzMB4X +DTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVowPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1 +cmUgVHJ1c3QgQ28uMRcwFQYDVQQDEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmT +rE4Orz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEqOLl5CjH9 +UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9bxiqKqy69cK3FCxolkHRy +xXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40d +utolucbY38EVAjqr2m7xPi71XAicPNaDaeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQ +MA0GCSqGSIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69ikug +dB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXrAvHRAosZy5Q6XkjE +GB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZzR8srzJmwN0jP41ZL9c8PDHIyh8bw +RLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubS +fZGL+T0yjWW06XyxV3bqxbYoOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ +-----END CERTIFICATE----- + +DST ACES CA X6 +============== +-----BEGIN CERTIFICATE----- +MIIECTCCAvGgAwIBAgIQDV6ZCtadt3js2AdWO4YV2TANBgkqhkiG9w0BAQUFADBbMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QxETAPBgNVBAsTCERTVCBBQ0VT +MRcwFQYDVQQDEw5EU1QgQUNFUyBDQSBYNjAeFw0wMzExMjAyMTE5NThaFw0xNzExMjAyMTE5NTha +MFsxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdDERMA8GA1UE +CxMIRFNUIEFDRVMxFzAVBgNVBAMTDkRTVCBBQ0VTIENBIFg2MIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAuT31LMmU3HWKlV1j6IR3dma5WZFcRt2SPp/5DgO0PWGSvSMmtWPuktKe1jzI +DZBfZIGxqAgNTNj50wUoUrQBJcWVHAx+PhCEdc/BGZFjz+iokYi5Q1K7gLFViYsx+tC3dr5BPTCa +pCIlF3PoHuLTrCq9Wzgh1SpL11V94zpVvddtawJXa+ZHfAjIgrrep4c9oW24MFbCswKBXy314pow +GCi4ZtPLAZZv6opFVdbgnf9nKxcCpk4aahELfrd755jWjHZvwTvbUJN+5dCOHze4vbrGn2zpfDPy +MjwmR/onJALJfh1biEITajV8fTXpLmaRcpPVMibEdPVTo7NdmvYJywIDAQABo4HIMIHFMA8GA1Ud +EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgHGMB8GA1UdEQQYMBaBFHBraS1vcHNAdHJ1c3Rkc3Qu +Y29tMGIGA1UdIARbMFkwVwYKYIZIAWUDAgEBATBJMEcGCCsGAQUFBwIBFjtodHRwOi8vd3d3LnRy +dXN0ZHN0LmNvbS9jZXJ0aWZpY2F0ZXMvcG9saWN5L0FDRVMtaW5kZXguaHRtbDAdBgNVHQ4EFgQU +CXIGThhDD+XWzMNqizF7eI+og7gwDQYJKoZIhvcNAQEFBQADggEBAKPYjtay284F5zLNAdMEA+V2 +5FYrnJmQ6AgwbN99Pe7lv7UkQIRJ4dEorsTCOlMwiPH1d25Ryvr/ma8kXxug/fKshMrfqfBfBC6t +Fr8hlxCBPeP/h40y3JTlR4peahPJlJU90u7INJXQgNStMgiAVDzgvVJT11J8smk/f3rPanTK+gQq +nExaBqXpIK1FZg9p8d2/6eMyi/rgwYZNcjwu2JN4Cir42NInPRmJX1p7ijvMDNpRrscL9yuwNwXs +vFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf29w4LTJxoeHtxMcfrHuBnQfO3 +oKfN5XozNmr6mis= +-----END CERTIFICATE----- + +TURKTRUST Certificate Services Provider Root 1 +============================================== +-----BEGIN CERTIFICATE----- +MIID+zCCAuOgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBtzE/MD0GA1UEAww2VMOcUktUUlVTVCBF +bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGDAJUUjEP +MA0GA1UEBwwGQU5LQVJBMVYwVAYDVQQKDE0oYykgMjAwNSBUw5xSS1RSVVNUIEJpbGdpIMSwbGV0 +acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjAeFw0wNTA1MTMx +MDI3MTdaFw0xNTAzMjIxMDI3MTdaMIG3MT8wPQYDVQQDDDZUw5xSS1RSVVNUIEVsZWt0cm9uaWsg +U2VydGlmaWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLExCzAJBgNVBAYMAlRSMQ8wDQYDVQQHDAZB +TktBUkExVjBUBgNVBAoMTShjKSAyMDA1IFTDnFJLVFJVU1QgQmlsZ2kgxLBsZXRpxZ9pbSB2ZSBC +aWxpxZ9pbSBHw7x2ZW5sacSfaSBIaXptZXRsZXJpIEEuxZ4uMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAylIF1mMD2Bxf3dJ7XfIMYGFbazt0K3gNfUW9InTojAPBxhEqPZW8qZSwu5GX +yGl8hMW0kWxsE2qkVa2kheiVfrMArwDCBRj1cJ02i67L5BuBf5OI+2pVu32Fks66WJ/bMsW9Xe8i +Si9BB35JYbOG7E6mQW6EvAPs9TscyB/C7qju6hJKjRTP8wrgUDn5CDX4EVmt5yLqS8oUBt5CurKZ +8y1UiBAG6uEaPj1nH/vO+3yC6BFdSsG5FOpU2WabfIl9BJpiyelSPJ6c79L1JuTm5Rh8i27fbMx4 +W09ysstcP4wFjdFMjK2Sx+F4f2VsSQZQLJ4ywtdKxnWKWU51b0dewQIDAQABoxAwDjAMBgNVHRME +BTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAV9VX/N5aAWSGk/KEVTCD21F/aAyT8z5Aa9CEKmu46 +sWrv7/hg0Uw2ZkUd82YCdAR7kjCo3gp2D++Vbr3JN+YaDayJSFvMgzbC9UZcWYJWtNX+I7TYVBxE +q8Sn5RTOPEFhfEPmzcSBCYsk+1Ql1haolgxnB2+zUEfjHCQo3SqYpGH+2+oSN7wBGjSFvW5P55Fy +B0SFHljKVETd96y5y4khctuPwGkplyqjrhgjlxxBKot8KsF8kOipKMDTkcatKIdAaLX/7KfS0zgY +nNN9aV3wxqUeJBujR/xpB2jn5Jq07Q+hh4cCzofSSE7hvP/L8XKSRGQDJereW26fyfJOrN3H +-----END CERTIFICATE----- + +TURKTRUST Certificate Services Provider Root 2 +============================================== +-----BEGIN CERTIFICATE----- +MIIEPDCCAySgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBF +bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEP +MA0GA1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUg +QmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwHhcN +MDUxMTA3MTAwNzU3WhcNMTUwOTE2MTAwNzU3WjCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBFbGVr +dHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEPMA0G +A1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmls +acWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpNn7DkUNMwxmYCMjHWHtPFoylzkkBH3MOrHUTpvqe +LCDe2JAOCtFp0if7qnefJ1Il4std2NiDUBd9irWCPwSOtNXwSadktx4uXyCcUHVPr+G1QRT0mJKI +x+XlZEdhR3n9wFHxwZnn3M5q+6+1ATDcRhzviuyV79z/rxAc653YsKpqhRgNF8k+v/Gb0AmJQv2g +QrSdiVFVKc8bcLyEVK3BEx+Y9C52YItdP5qtygy/p1Zbj3e41Z55SZI/4PGXJHpsmxcPbe9TmJEr +5A++WXkHeLuXlfSfadRYhwqp48y2WBmfJiGxxFmNskF1wK1pzpwACPI2/z7woQ8arBT9pmAPAgMB +AAGjQzBBMB0GA1UdDgQWBBTZN7NOBf3Zz58SFq62iS/rJTqIHDAPBgNVHQ8BAf8EBQMDBwYAMA8G +A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAHJglrfJ3NgpXiOFX7KzLXb7iNcX/ntt +Rbj2hWyfIvwqECLsqrkw9qtY1jkQMZkpAL2JZkH7dN6RwRgLn7Vhy506vvWolKMiVW4XSf/SKfE4 +Jl3vpao6+XF75tpYHdN0wgH6PmlYX63LaL4ULptswLbcoCb6dxriJNoaN+BnrdFzgw2lGh1uEpJ+ +hGIAF728JRhX8tepb1mIvDS3LoV4nZbcFMMsilKbloxSZj2GFotHuFEJjOp9zYhys2AzsfAKRO8P +9Qk3iCQOLGsgOqL6EfJANZxEaGM7rDNvY7wsu/LSy3Z9fYjYHcgFHW68lKlmjHdxx/qR+i9Rnuk5 +UrbnBEI= +-----END CERTIFICATE----- + +SwissSign Gold CA - G2 +====================== +-----BEGIN CERTIFICATE----- +MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAkNIMRUw +EwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2lnbiBHb2xkIENBIC0gRzIwHhcN +MDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBFMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dp +c3NTaWduIEFHMR8wHQYDVQQDExZTd2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUq +t2/876LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+bbqBHH5C +jCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c6bM8K8vzARO/Ws/BtQpg +vd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqEemA8atufK+ze3gE/bk3lUIbLtK/tREDF +ylqM2tIrfKjuvqblCqoOpd8FUrdVxyJdMmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvR +AiTysybUa9oEVeXBCsdtMDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuend +jIj3o02yMszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69yFGkO +peUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPiaG59je883WX0XaxR +7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxMgI93e2CaHt+28kgeDrpOVG2Y4OGi +GqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUWyV7lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64 +OfPAeGZe6Drn8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov +L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe645R88a7A3hfm +5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczOUYrHUDFu4Up+GC9pWbY9ZIEr +44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOf +Mke6UiI0HTJ6CVanfCU2qT1L2sCCbwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6m +Gu6uLftIdxf+u+yvGPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxp +mo/a77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCChdiDyyJk +vC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid392qgQmwLOM7XdVAyksLf +KzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEppLd6leNcG2mqeSz53OiATIgHQv2ieY2Br +NU0LbbqhPcCT4H8js1WtciVORvnSFu+wZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6Lqj +viOvrv1vA+ACOzB2+httQc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ +-----END CERTIFICATE----- + +SwissSign Silver CA - G2 +======================== +-----BEGIN CERTIFICATE----- +MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCQ0gxFTAT +BgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMB4X +DTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0NlowRzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3 +aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG +9w0BAQEFAAOCAg8AMIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644 +N0MvFz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7brYT7QbNHm ++/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieFnbAVlDLaYQ1HTWBCrpJH +6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH6ATK72oxh9TAtvmUcXtnZLi2kUpCe2Uu +MGoM9ZDulebyzYLs2aFK7PayS+VFheZteJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5h +qAaEuSh6XzjZG6k4sIN/c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5 +FZGkECwJMoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRHHTBs +ROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTfjNFusB3hB48IHpmc +celM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb65i/4z3GcRm25xBWNOHkDRUjvxF3X +CO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUF6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRB +tjpbO8tFnb0cwpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0 +cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBAHPGgeAn0i0P +4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShpWJHckRE1qTodvBqlYJ7YH39F +kWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L +3XWgwF15kIwb4FDm3jH+mHtwX6WQ2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx +/uNncqCxv1yL5PqZIseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFa +DGi8aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2Xem1ZqSqP +e97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQRdAtq/gsD/KNVV4n+Ssuu +WxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJ +DIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ub +DgEj8Z+7fNzcbBGXJbLytGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u +-----END CERTIFICATE----- + +GeoTrust Primary Certification Authority +======================================== +-----BEGIN CERTIFICATE----- +MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMoR2VvVHJ1c3QgUHJpbWFyeSBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjExMjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgx +CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQ +cmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9AWbK7hWN +b6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjAZIVcFU2Ix7e64HXprQU9 +nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE07e9GceBrAqg1cmuXm2bgyxx5X9gaBGge +RwLmnWDiNpcB3841kt++Z8dtd1k7j53WkBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGt +tm/81w7a4DSwDRp35+MImO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJKoZI +hvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ16CePbJC/kRYkRj5K +Ts4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl4b7UVXGYNTq+k+qurUKykG/g/CFN +NWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6KoKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHa +Floxt/m0cYASSJlyc1pZU8FjUjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG +1riR/aYNKxoUAT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk= +-----END CERTIFICATE----- + +thawte Primary Root CA +====================== +-----BEGIN CERTIFICATE----- +MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCBqTELMAkGA1UE +BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2 +aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3 +MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwg +SW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMv +KGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMT +FnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCs +oPD7gFnUnMekz52hWXMJEEUMDSxuaPFsW0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ +1CRfBsDMRJSUjQJib+ta3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGc +q/gcfomk6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6Sk/K +aAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94JNqR32HuHUETVPm4p +afs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD +VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XPr87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUF +AAOCAQEAeRHAS7ORtvzw6WfUDW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeE +uzLlQRHAd9mzYJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX +xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2/qxAeeWsEG89 +jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/LHbTY5xZ3Y+m4Q6gLkH3LpVH +z7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7jVaMaA== +-----END CERTIFICATE----- + +VeriSign Class 3 Public Primary Certification Authority - G5 +============================================================ +-----BEGIN CERTIFICATE----- +MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCByjELMAkGA1UE +BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO +ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk +IHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCB +yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2ln +biBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBh +dXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmlt +YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKz +j/i5Vbext0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhD +Y2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/ +Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNHiDxpg8v+R70r +fk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/ +BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2Uv +Z2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy +aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKvMzEzMA0GCSqG +SIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzEp6B4Eq1iDkVwZMXnl2YtmAl+ +X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKE +KQsTb47bDN0lAtukixlE0kF6BWlKWE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiC +Km0oHw0LxOXnGiYZ4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vE +ZV8NhnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq +-----END CERTIFICATE----- + +SecureTrust CA +============== +-----BEGIN CERTIFICATE----- +MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBIMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xFzAVBgNVBAMTDlNlY3VyZVRy +dXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIzMTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAe +BgNVBAoTF1NlY3VyZVRydXN0IENvcnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQX +OZEzZum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO0gMdA+9t +DWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIaowW8xQmxSPmjL8xk037uH +GFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b +01k/unK8RCSc43Oz969XL0Imnal0ugBS8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmH +ursCAwEAAaOBnTCBmjATBgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCegJYYj +aHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ +KoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt36Z3q059c4EVlew3KW+JwULKUBRSu +SceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHf +mbx8IVQr5Fiiu1cprp6poxkmD5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZ +nMUFdAvnZyPSCPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR +3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= +-----END CERTIFICATE----- + +Secure Global CA +================ +-----BEGIN CERTIFICATE----- +MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBH +bG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkxMjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEg +MB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwg +Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jx +YDiJiQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa/FHtaMbQ +bqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJjnIFHovdRIWCQtBJwB1g +8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnIHmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYV +HDGA76oYa8J719rO+TMg1fW9ajMtgQT7sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi +0XPnj3pDAgMBAAGjgZ0wgZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCswKaAn +oCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsGAQQBgjcVAQQDAgEA +MA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0LURYD7xh8yOOvaliTFGCRsoTciE6+ +OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXOH0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cn +CDpOGR86p1hcF895P4vkp9MmI50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/5 +3CYNv6ZHdAbYiNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc +f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW +-----END CERTIFICATE----- + +COMODO Certification Authority +============================== +-----BEGIN CERTIFICATE----- +MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UE +BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG +A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNVBAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eTAeFw0wNjEyMDEwMDAwMDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEb +MBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFD +T01PRE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3UcEbVASY06m/weaKXTuH ++7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI2GqGd0S7WWaXUF601CxwRM/aN5VCaTww +xHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV +4EajcNxo2f8ESIl33rXp+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA +1KGzqSX+DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5OnKVI +rLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9k +b2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOC +AQEAPpiem/Yb6dc5t3iuHXIYSdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CP +OGEIqB6BCsAvIC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ +RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4zJVSk/BwJVmc +IGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5ddBA6+C4OmF4O5MBKgxTMVBbkN ++8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IBZQ== +-----END CERTIFICATE----- + +Network Solutions Certificate Authority +======================================= +-----BEGIN CERTIFICATE----- +MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQG +EwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydOZXR3b3Jr +IFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMx +MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu +MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwzc7MEL7xx +jOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPPOCwGJgl6cvf6UDL4wpPT +aaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rlmGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXT +crA/vGp97Eh/jcOrqnErU2lBUzS1sLnFBgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc +/Qzpf14Dl847ABSHJ3A4qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMB +AAGjgZcwgZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwubmV0c29sc3NsLmNv +bS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3JpdHkuY3JsMA0GCSqGSIb3DQEBBQUA +A4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc86fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q +4LqILPxFzBiwmZVRDuwduIj/h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/ +GGUsyfJj4akH/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv +wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHNpGxlaKFJdlxD +ydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey +-----END CERTIFICATE----- + +WellsSecure Public Root Certificate Authority +============================================= +-----BEGIN CERTIFICATE----- +MIIEvTCCA6WgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoM +F1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYw +NAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcN +MDcxMjEzMTcwNzU0WhcNMjIxMjE0MDAwNzU0WjCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dl +bGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYD +VQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDub7S9eeKPCCGeOARBJe+rWxxTkqxtnt3CxC5FlAM1 +iGd0V+PfjLindo8796jE2yljDpFoNoqXjopxaAkH5OjUDk/41itMpBb570OYj7OeUt9tkTmPOL13 +i0Nj67eT/DBMHAGTthP796EfvyXhdDcsHqRePGj4S78NuR4uNuip5Kf4D8uCdXw1LSLWwr8L87T8 +bJVhHlfXBIEyg1J55oNjz7fLY4sR4r1e6/aN7ZVyKLSsEmLpSjPmgzKuBXWVvYSV2ypcm44uDLiB +K0HmOFafSZtsdvqKXfcBeYF8wYNABf5x/Qw/zE5gCQ5lRxAvAcAFP4/4s0HvWkJ+We/SlwxlAgMB +AAGjggE0MIIBMDAPBgNVHRMBAf8EBTADAQH/MDkGA1UdHwQyMDAwLqAsoCqGKGh0dHA6Ly9jcmwu +cGtpLndlbGxzZmFyZ28uY29tL3dzcHJjYS5jcmwwDgYDVR0PAQH/BAQDAgHGMB0GA1UdDgQWBBQm +lRkQ2eihl5H/3BnZtQQ+0nMKajCBsgYDVR0jBIGqMIGngBQmlRkQ2eihl5H/3BnZtQQ+0nMKaqGB +i6SBiDCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRww +GgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMg +Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCAQEwDQYJKoZIhvcNAQEFBQADggEBALkVsUSRzCPI +K0134/iaeycNzXK7mQDKfGYZUMbVmO2rvwNa5U3lHshPcZeG1eMd/ZDJPHV3V3p9+N701NX3leZ0 +bh08rnyd2wIDBSxxSyU+B+NemvVmFymIGjifz6pBA4SXa5M4esowRBskRDPQ5NHcKDj0E0M1NSlj +qHyita04pO2t/caaH/+Xc/77szWnk4bGdpEA5qxRFsQnMlzbc9qlk1eOPm01JghZ1edE13YgY+es +E2fDbbFwRnzVlhE9iW9dqKHrjQrawx0zbKPqZxmamX9LPYNRKh3KL4YMon4QLSvUFpULB6ouFJJJ +tylv2G0xffX8oRAHh84vWdw+WNs= +-----END CERTIFICATE----- + +COMODO ECC Certification Authority +================================== +-----BEGIN CERTIFICATE----- +MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTELMAkGA1UEBhMC +R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE +ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwHhcNMDgwMzA2MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0Ix +GzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRo +b3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSRFtSrYpn1PlILBs5BAH+X +4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0JcfRK9ChQtP6IHG4/bC8vCVlbpVsLM5ni +wz2J+Wos77LTBumjQjBAMB0GA1UdDgQWBBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VG +FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA +U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= +-----END CERTIFICATE----- + +IGC/A +===== +-----BEGIN CERTIFICATE----- +MIIEAjCCAuqgAwIBAgIFORFFEJQwDQYJKoZIhvcNAQEFBQAwgYUxCzAJBgNVBAYTAkZSMQ8wDQYD +VQQIEwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVE +Q1NTSTEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZy +MB4XDTAyMTIxMzE0MjkyM1oXDTIwMTAxNzE0MjkyMlowgYUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQI +EwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVEQ1NT +STEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZyMIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsh/R0GLFMzvABIaIs9z4iPf930Pfeo2aSVz2 +TqrMHLmh6yeJ8kbpO0px1R2OLc/mratjUMdUC24SyZA2xtgv2pGqaMVy/hcKshd+ebUyiHDKcMCW +So7kVc0dJ5S/znIq7Fz5cyD+vfcuiWe4u0dzEvfRNWk68gq5rv9GQkaiv6GFGvm/5P9JhfejcIYy +HF2fYPepraX/z9E0+X1bF8bc1g4oa8Ld8fUzaJ1O/Id8NhLWo4DoQw1VYZTqZDdH6nfK0LJYBcNd +frGoRpAxVs5wKpayMLh35nnAvSk7/ZR3TL0gzUEl4C7HG7vupARB0l2tEmqKm0f7yd1GQOGdPDPQ +tQIDAQABo3cwdTAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBRjAVBgNVHSAEDjAMMAoGCCqB +egF5AQEBMB0GA1UdDgQWBBSjBS8YYFDCiQrdKyFP/45OqDAxNjAfBgNVHSMEGDAWgBSjBS8YYFDC +iQrdKyFP/45OqDAxNjANBgkqhkiG9w0BAQUFAAOCAQEABdwm2Pp3FURo/C9mOnTgXeQp/wYHE4RK +q89toB9RlPhJy3Q2FLwV3duJL92PoF189RLrn544pEfMs5bZvpwlqwN+Mw+VgQ39FuCIvjfwbF3Q +MZsyK10XZZOYYLxuj7GoPB7ZHPOpJkL5ZB3C55L29B5aqhlSXa/oovdgoPaN8In1buAKBQGVyYsg +Crpa/JosPL3Dt8ldeCUFP1YUmwza+zpI/pdpXsoQhvdOlgQITeywvl3cO45Pwf2aNjSaTFR+FwNI +lQgRHAdvhQh+XU3Endv7rs6y0bO4g2wdsrN58dhwmX7wEwLOXt1R0982gaEbeC9xs/FZTEYYKKuF +0mBWWg== +-----END CERTIFICATE----- + +Security Communication EV RootCA1 +================================= +-----BEGIN CERTIFICATE----- +MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJKUDElMCMGA1UEChMc +U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEqMCgGA1UECxMhU2VjdXJpdHkgQ29tbXVuaWNh +dGlvbiBFViBSb290Q0ExMB4XDTA3MDYwNjAyMTIzMloXDTM3MDYwNjAyMTIzMlowYDELMAkGA1UE +BhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKjAoBgNVBAsTIVNl +Y3VyaXR5IENvbW11bmljYXRpb24gRVYgUm9vdENBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBALx/7FebJOD+nLpCeamIivqA4PUHKUPqjgo0No0c+qe1OXj/l3X3L+SqawSERMqm4miO +/VVQYg+kcQ7OBzgtQoVQrTyWb4vVog7P3kmJPdZkLjjlHmy1V4qe70gOzXppFodEtZDkBp2uoQSX +WHnvIEqCa4wiv+wfD+mEce3xDuS4GBPMVjZd0ZoeUWs5bmB2iDQL87PRsJ3KYeJkHcFGB7hj3R4z +ZbOOCVVSPbW9/wfrrWFVGCypaZhKqkDFMxRldAD5kd6vA0jFQFTcD4SQaCDFkpbcLuUCRarAX1T4 +bepJz11sS6/vmsJWXMY1VkJqMF/Cq/biPT+zyRGPMUzXn0kCAwEAAaNCMEAwHQYDVR0OBBYEFDVK +9U2vP9eCOKyrcWUXdYydVZPmMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqG +SIb3DQEBBQUAA4IBAQCoh+ns+EBnXcPBZsdAS5f8hxOQWsTvoMpfi7ent/HWtWS3irO4G8za+6xm +iEHO6Pzk2x6Ipu0nUBsCMCRGef4Eh3CXQHPRwMFXGZpppSeZq51ihPZRwSzJIxXYKLerJRO1RuGG +Av8mjMSIkh1W/hln8lXkgKNrnKt34VFxDSDbEJrbvXZ5B3eZKK2aXtqxT0QsNY6llsf9g/BYxnnW +mHyojf6GPgcWkuF75x3sM3Z+Qi5KhfmRiWiEA4Glm5q+4zfFVKtWOxgtQaQM+ELbmaDgcm+7XeEW +T1MKZPlO9L9OVL14bIjqv5wTJMJwaaJ/D8g8rQjJsJhAoyrniIPtd490 +-----END CERTIFICATE----- + +OISTE WISeKey Global Root GA CA +=============================== +-----BEGIN CERTIFICATE----- +MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCBijELMAkGA1UE +BhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHlyaWdodCAoYykgMjAwNTEiMCAG +A1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBH +bG9iYWwgUm9vdCBHQSBDQTAeFw0wNTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYD +VQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIw +IAYDVQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5 +IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy0+zAJs9 +Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxRVVuuk+g3/ytr6dTqvirdqFEr12bDYVxg +Asj1znJ7O7jyTmUIms2kahnBAbtzptf2w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbD +d50kc3vkDIzh2TbhmYsFmQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ +/yxViJGg4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t94B3R +LoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ +KoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOxSPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vIm +MMkQyh2I+3QZH4VFvbBsUfk2ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4 ++vg1YFkCExh8vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa +hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZiFj4A4xylNoEY +okxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ/L7fCg0= +-----END CERTIFICATE----- + +Microsec e-Szigno Root CA +========================= +-----BEGIN CERTIFICATE----- +MIIHqDCCBpCgAwIBAgIRAMy4579OKRr9otxmpRwsDxEwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UE +BhMCSFUxETAPBgNVBAcTCEJ1ZGFwZXN0MRYwFAYDVQQKEw1NaWNyb3NlYyBMdGQuMRQwEgYDVQQL +EwtlLVN6aWdubyBDQTEiMCAGA1UEAxMZTWljcm9zZWMgZS1Temlnbm8gUm9vdCBDQTAeFw0wNTA0 +MDYxMjI4NDRaFw0xNzA0MDYxMjI4NDRaMHIxCzAJBgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVz +dDEWMBQGA1UEChMNTWljcm9zZWMgTHRkLjEUMBIGA1UECxMLZS1Temlnbm8gQ0ExIjAgBgNVBAMT +GU1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQDtyADVgXvNOABHzNuEwSFpLHSQDCHZU4ftPkNEU6+r+ICbPHiN1I2uuO/TEdyB5s87lozWbxXG +d36hL+BfkrYn13aaHUM86tnsL+4582pnS4uCzyL4ZVX+LMsvfUh6PXX5qqAnu3jCBspRwn5mS6/N +oqdNAoI/gqyFxuEPkEeZlApxcpMqyabAvjxWTHOSJ/FrtfX9/DAFYJLG65Z+AZHCabEeHXtTRbjc +QR/Ji3HWVBTji1R4P770Yjtb9aPs1ZJ04nQw7wHb4dSrmZsqa/i9phyGI0Jf7Enemotb9HI6QMVJ +PqW+jqpx62z69Rrkav17fVVA71hu5tnVvCSrwe+3AgMBAAGjggQ3MIIEMzBnBggrBgEFBQcBAQRb +MFkwKAYIKwYBBQUHMAGGHGh0dHBzOi8vcmNhLmUtc3ppZ25vLmh1L29jc3AwLQYIKwYBBQUHMAKG +IWh0dHA6Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNydDAPBgNVHRMBAf8EBTADAQH/MIIBcwYD +VR0gBIIBajCCAWYwggFiBgwrBgEEAYGoGAIBAQEwggFQMCgGCCsGAQUFBwIBFhxodHRwOi8vd3d3 +LmUtc3ppZ25vLmh1L1NaU1ovMIIBIgYIKwYBBQUHAgIwggEUHoIBEABBACAAdABhAG4A+gBzAO0A +dAB2AOEAbgB5ACAA6QByAHQAZQBsAG0AZQB6AOkAcwDpAGgAZQB6ACAA6QBzACAAZQBsAGYAbwBn +AGEAZADhAHMA4QBoAG8AegAgAGEAIABTAHoAbwBsAGcA4QBsAHQAYQB0APMAIABTAHoAbwBsAGcA +4QBsAHQAYQB0AOEAcwBpACAAUwB6AGEAYgDhAGwAeQB6AGEAdABhACAAcwB6AGUAcgBpAG4AdAAg +AGsAZQBsAGwAIABlAGwAagDhAHIAbgBpADoAIABoAHQAdABwADoALwAvAHcAdwB3AC4AZQAtAHMA +egBpAGcAbgBvAC4AaAB1AC8AUwBaAFMAWgAvMIHIBgNVHR8EgcAwgb0wgbqggbeggbSGIWh0dHA6 +Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNybIaBjmxkYXA6Ly9sZGFwLmUtc3ppZ25vLmh1L0NO +PU1pY3Jvc2VjJTIwZS1Temlnbm8lMjBSb290JTIwQ0EsT1U9ZS1Temlnbm8lMjBDQSxPPU1pY3Jv +c2VjJTIwTHRkLixMPUJ1ZGFwZXN0LEM9SFU/Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdDtiaW5h +cnkwDgYDVR0PAQH/BAQDAgEGMIGWBgNVHREEgY4wgYuBEGluZm9AZS1zemlnbm8uaHWkdzB1MSMw +IQYDVQQDDBpNaWNyb3NlYyBlLVN6aWduw7MgUm9vdCBDQTEWMBQGA1UECwwNZS1TemlnbsOzIEhT +WjEWMBQGA1UEChMNTWljcm9zZWMgS2Z0LjERMA8GA1UEBxMIQnVkYXBlc3QxCzAJBgNVBAYTAkhV +MIGsBgNVHSMEgaQwgaGAFMegSXUWYYTbMUuE0vE3QJDvTtz3oXakdDByMQswCQYDVQQGEwJIVTER +MA8GA1UEBxMIQnVkYXBlc3QxFjAUBgNVBAoTDU1pY3Jvc2VjIEx0ZC4xFDASBgNVBAsTC2UtU3pp +Z25vIENBMSIwIAYDVQQDExlNaWNyb3NlYyBlLVN6aWdubyBSb290IENBghEAzLjnv04pGv2i3Gal +HCwPETAdBgNVHQ4EFgQUx6BJdRZhhNsxS4TS8TdAkO9O3PcwDQYJKoZIhvcNAQEFBQADggEBANMT +nGZjWS7KXHAM/IO8VbH0jgdsZifOwTsgqRy7RlRw7lrMoHfqaEQn6/Ip3Xep1fvj1KcExJW4C+FE +aGAHQzAxQmHl7tnlJNUb3+FKG6qfx1/4ehHqE5MAyopYse7tDk2016g2JnzgOsHVV4Lxdbb9iV/a +86g4nzUGCM4ilb7N1fy+W955a9x6qWVmvrElWl/tftOsRm1M9DKHtCAE4Gx4sHfRhUZLphK3dehK +yVZs15KrnfVJONJPU+NVkBHbmJbGSfI+9J8b4PeI3CVimUTYc78/MPMMNz7UwiiAc7EBt51alhQB +S6kRnSlqLtBdgcDPsiBDxwPgN05dCtxZICU= +-----END CERTIFICATE----- + +Certigna +======== +-----BEGIN CERTIFICATE----- +MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNVBAYTAkZSMRIw +EAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4XDTA3MDYyOTE1MTMwNVoXDTI3 +MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwI +Q2VydGlnbmEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7q +XOEm7RFHYeGifBZ4QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyH +GxnygQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbwzBfsV1/p +ogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q130yGLMLLGq/jj8UEYkg +DncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKf +Irjxwo1p3Po6WAbfAgMBAAGjgbwwgbkwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQ +tCRZvgHyUtVF9lo53BEwZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJ +BgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzjAQ/J +SP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQUFAAOCAQEA +hQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8hbV6lUmPOEvjvKtpv6zf+EwLHyzs+ +ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFncfca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1klu +PBS1xp81HlDQwY9qcEQCYsuuHWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY +1gkIl2PlwS6wt0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw +WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== +-----END CERTIFICATE----- + +AC Ra\xC3\xADz Certic\xC3\xA1mara S.A. +====================================== +-----BEGIN CERTIFICATE----- +MIIGZjCCBE6gAwIBAgIPB35Sk3vgFeNX8GmMy+wMMA0GCSqGSIb3DQEBBQUAMHsxCzAJBgNVBAYT +AkNPMUcwRQYDVQQKDD5Tb2NpZWRhZCBDYW1lcmFsIGRlIENlcnRpZmljYWNpw7NuIERpZ2l0YWwg +LSBDZXJ0aWPDoW1hcmEgUy5BLjEjMCEGA1UEAwwaQUMgUmHDrXogQ2VydGljw6FtYXJhIFMuQS4w +HhcNMDYxMTI3MjA0NjI5WhcNMzAwNDAyMjE0MjAyWjB7MQswCQYDVQQGEwJDTzFHMEUGA1UECgw+ +U29jaWVkYWQgQ2FtZXJhbCBkZSBDZXJ0aWZpY2FjacOzbiBEaWdpdGFsIC0gQ2VydGljw6FtYXJh +IFMuQS4xIzAhBgNVBAMMGkFDIFJhw616IENlcnRpY8OhbWFyYSBTLkEuMIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEAq2uJo1PMSCMI+8PPUZYILrgIem08kBeGqentLhM0R7LQcNzJPNCN +yu5LF6vQhbCnIwTLqKL85XXbQMpiiY9QngE9JlsYhBzLfDe3fezTf3MZsGqy2IiKLUV0qPezuMDU +2s0iiXRNWhU5cxh0T7XrmafBHoi0wpOQY5fzp6cSsgkiBzPZkc0OnB8OIMfuuzONj8LSWKdf/WU3 +4ojC2I+GdV75LaeHM/J4Ny+LvB2GNzmxlPLYvEqcgxhaBvzz1NS6jBUJJfD5to0EfhcSM2tXSExP +2yYe68yQ54v5aHxwD6Mq0Do43zeX4lvegGHTgNiRg0JaTASJaBE8rF9ogEHMYELODVoqDA+bMMCm +8Ibbq0nXl21Ii/kDwFJnmxL3wvIumGVC2daa49AZMQyth9VXAnow6IYm+48jilSH5L887uvDdUhf +HjlvgWJsxS3EF1QZtzeNnDeRyPYL1epjb4OsOMLzP96a++EjYfDIJss2yKHzMI+ko6Kh3VOz3vCa +Mh+DkXkwwakfU5tTohVTP92dsxA7SH2JD/ztA/X7JWR1DhcZDY8AFmd5ekD8LVkH2ZD6mq093ICK +5lw1omdMEWux+IBkAC1vImHFrEsm5VoQgpukg3s0956JkSCXjrdCx2bD0Omk1vUgjcTDlaxECp1b +czwmPS9KvqfJpxAe+59QafMCAwEAAaOB5jCB4zAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE +AwIBBjAdBgNVHQ4EFgQU0QnQ6dfOeXRU+Tows/RtLAMDG2gwgaAGA1UdIASBmDCBlTCBkgYEVR0g +ADCBiTArBggrBgEFBQcCARYfaHR0cDovL3d3dy5jZXJ0aWNhbWFyYS5jb20vZHBjLzBaBggrBgEF +BQcCAjBOGkxMaW1pdGFjaW9uZXMgZGUgZ2FyYW507WFzIGRlIGVzdGUgY2VydGlmaWNhZG8gc2Ug +cHVlZGVuIGVuY29udHJhciBlbiBsYSBEUEMuMA0GCSqGSIb3DQEBBQUAA4ICAQBclLW4RZFNjmEf +AygPU3zmpFmps4p6xbD/CHwso3EcIRNnoZUSQDWDg4902zNc8El2CoFS3UnUmjIz75uny3XlesuX +EpBcunvFm9+7OSPI/5jOCk0iAUgHforA1SBClETvv3eiiWdIG0ADBaGJ7M9i4z0ldma/Jre7Ir5v +/zlXdLp6yQGVwZVR6Kss+LGGIOk/yzVb0hfpKv6DExdA7ohiZVvVO2Dpezy4ydV/NgIlqmjCMRW3 +MGXrfx1IebHPOeJCgBbT9ZMj/EyXyVo3bHwi2ErN0o42gzmRkBDI8ck1fj+404HGIGQatlDCIaR4 +3NAvO2STdPCWkPHv+wlaNECW8DYSwaN0jJN+Qd53i+yG2dIPPy3RzECiiWZIHiCznCNZc6lEc7wk +eZBWN7PGKX6jD/EpOe9+XCgycDWs2rjIdWb8m0w5R44bb5tNAlQiM+9hup4phO9OSzNHdpdqy35f +/RWmnkJDW2ZaiogN9xa5P1FlK2Zqi9E4UqLWRhH6/JocdJ6PlwsCT2TG9WjTSy3/pDceiz+/RL5h +RqGEPQgnTIEgd4kI6mdAXmwIUV80WoyWaM3X94nCHNMyAK9Sy9NgWyo6R35rMDOhYil/SrnhLecU +Iw4OGEfhefwVVdCx/CVxY3UzHCMrr1zZ7Ud3YA47Dx7SwNxkBYn8eNZcLCZDqQ== +-----END CERTIFICATE----- + +TC TrustCenter Class 2 CA II +============================ +-----BEGIN CERTIFICATE----- +MIIEqjCCA5KgAwIBAgIOLmoAAQACH9dSISwRXDswDQYJKoZIhvcNAQEFBQAwdjELMAkGA1UEBhMC +REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNVBAsTGVRDIFRydXN0Q2VudGVy +IENsYXNzIDIgQ0ExJTAjBgNVBAMTHFRDIFRydXN0Q2VudGVyIENsYXNzIDIgQ0EgSUkwHhcNMDYw +MTEyMTQzODQzWhcNMjUxMjMxMjI1OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1 +c3RDZW50ZXIgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQTElMCMGA1UE +AxMcVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAKuAh5uO8MN8h9foJIIRszzdQ2Lu+MNF2ujhoF/RKrLqk2jftMjWQ+nEdVl//OEd+DFw +IxuInie5e/060smp6RQvkL4DUsFJzfb95AhmC1eKokKguNV/aVyQMrKXDcpK3EY+AlWJU+MaWss2 +xgdW94zPEfRMuzBwBJWl9jmM/XOBCH2JXjIeIqkiRUuwZi4wzJ9l/fzLganx4Duvo4bRierERXlQ +Xa7pIXSSTYtZgo+U4+lK8edJsBTj9WLL1XK9H7nSn6DNqPoByNkN39r8R52zyFTfSUrxIan+GE7u +SNQZu+995OKdy1u2bv/jzVrndIIFuoAlOMvkaZ6vQaoahPUCAwEAAaOCATQwggEwMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTjq1RMgKHbVkO3kUrL84J6E1wIqzCB +7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRydXN0Y2VudGVyLmRlL2NybC92Mi90 +Y19jbGFzc18yX2NhX0lJLmNybIaBn2xkYXA6Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBU +cnVzdENlbnRlciUyMENsYXNzJTIwMiUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21i +SCxPVT1yb290Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u +TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEAjNfffu4bgBCzg/XbEeprS6iSGNn3Bzn1LL4G +dXpoUxUc6krtXvwjshOg0wn/9vYua0Fxec3ibf2uWWuFHbhOIprtZjluS5TmVfwLG4t3wVMTZonZ +KNaL80VKY7f9ewthXbhtvsPcW3nS7Yblok2+XnR8au0WOB9/WIFaGusyiC2y8zl3gK9etmF1Kdsj +TYjKUCjLhdLTEKJZbtOTVAB6okaVhgWcqRmY5TFyDADiZ9lA4CQze28suVyrZZ0srHbqNZn1l7kP +JOzHdiEoZa5X6AeIdUpWoNIFOqTmjZKILPPy4cHGYdtBxceb9w4aUUXCYWvcZCcXjFq32nQozZfk +vQ== +-----END CERTIFICATE----- + +TC TrustCenter Class 3 CA II +============================ +-----BEGIN CERTIFICATE----- +MIIEqjCCA5KgAwIBAgIOSkcAAQAC5aBd1j8AUb8wDQYJKoZIhvcNAQEFBQAwdjELMAkGA1UEBhMC +REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNVBAsTGVRDIFRydXN0Q2VudGVy +IENsYXNzIDMgQ0ExJTAjBgNVBAMTHFRDIFRydXN0Q2VudGVyIENsYXNzIDMgQ0EgSUkwHhcNMDYw +MTEyMTQ0MTU3WhcNMjUxMjMxMjI1OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1 +c3RDZW50ZXIgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQTElMCMGA1UE +AxMcVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBALTgu1G7OVyLBMVMeRwjhjEQY0NVJz/GRcekPewJDRoeIMJWHt4bNwcwIi9v8Qbxq63W +yKthoy9DxLCyLfzDlml7forkzMA5EpBCYMnMNWju2l+QVl/NHE1bWEnrDgFPZPosPIlY2C8u4rBo +6SI7dYnWRBpl8huXJh0obazovVkdKyT21oQDZogkAHhg8fir/gKya/si+zXmFtGt9i4S5Po1auUZ +uV3bOx4a+9P/FRQI2AlqukWdFHlgfa9Aigdzs5OW03Q0jTo3Kd5c7PXuLjHCINy+8U9/I1LZW+Jk +2ZyqBwi1Rb3R0DHBq1SfqdLDYmAD8bs5SpJKPQq5ncWg/jcCAwEAAaOCATQwggEwMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTUovyfs8PYA9NXXAek0CSnwPIA1DCB +7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRydXN0Y2VudGVyLmRlL2NybC92Mi90 +Y19jbGFzc18zX2NhX0lJLmNybIaBn2xkYXA6Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBU +cnVzdENlbnRlciUyMENsYXNzJTIwMyUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21i +SCxPVT1yb290Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u +TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEANmDkcPcGIEPZIxpC8vijsrlNirTzwppVMXzE +O2eatN9NDoqTSheLG43KieHPOh6sHfGcMrSOWXaiQYUlN6AT0PV8TtXqluJucsG7Kv5sbviRmEb8 +yRtXW+rIGjs/sFGYPAfaLFkB2otE6OF0/ado3VS6g0bsyEa1+K+XwDsJHI/OcpY9M1ZwvJbL2NV9 +IJqDnxrcOfHFcqMRA/07QlIp2+gB95tejNaNhk4Z+rwcvsUhpYeeeC422wlxo3I0+GzjBgnyXlal +092Y+tTmBvTwtiBjS+opvaqCZh77gaqnN60TGOaSw4HBM7uIHqHn4rS9MWwOUT1v+5ZWgOI2F9Hc +5A== +-----END CERTIFICATE----- + +TC TrustCenter Universal CA I +============================= +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIOHaIAAQAC7LdggHiNtgYwDQYJKoZIhvcNAQEFBQAweTELMAkGA1UEBhMC +REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxJDAiBgNVBAsTG1RDIFRydXN0Q2VudGVy +IFVuaXZlcnNhbCBDQTEmMCQGA1UEAxMdVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBIEkwHhcN +MDYwMzIyMTU1NDI4WhcNMjUxMjMxMjI1OTU5WjB5MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMg +VHJ1c3RDZW50ZXIgR21iSDEkMCIGA1UECxMbVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBMSYw +JAYDVQQDEx1UQyBUcnVzdENlbnRlciBVbml2ZXJzYWwgQ0EgSTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAKR3I5ZEr5D0MacQ9CaHnPM42Q9e3s9B6DGtxnSRJJZ4Hgmgm5qVSkr1YnwC +qMqs+1oEdjneX/H5s7/zA1hV0qq34wQi0fiU2iIIAI3TfCZdzHd55yx4Oagmcw6iXSVphU9VDprv +xrlE4Vc93x9UIuVvZaozhDrzznq+VZeujRIPFDPiUHDDSYcTvFHe15gSWu86gzOSBnWLknwSaHtw +ag+1m7Z3W0hZneTvWq3zwZ7U10VOylY0Ibw+F1tvdwxIAUMpsN0/lm7mlaoMwCC2/T42J5zjXM9O +gdwZu5GQfezmlwQek8wiSdeXhrYTCjxDI3d+8NzmzSQfO4ObNDqDNOMCAwEAAaNjMGEwHwYDVR0j +BBgwFoAUkqR1LKSevoFE63n8isWVpesQdXMwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0OBBYEFJKkdSyknr6BROt5/IrFlaXrEHVzMA0GCSqGSIb3DQEBBQUAA4IBAQAo0uCG +1eb4e/CX3CJrO5UUVg8RMKWaTzqwOuAGy2X17caXJ/4l8lfmXpWMPmRgFVp/Lw0BxbFg/UU1z/Cy +vwbZ71q+s2IhtNerNXxTPqYn8aEt2hojnczd7Dwtnic0XQ/CNnm8yUpiLe1r2X1BQ3y2qsrtYbE3 +ghUJGooWMNjsydZHcnhLEEYUjl8Or+zHL6sQ17bxbuyGssLoDZJz3KL0Dzq/YSMQiZxIQG5wALPT +ujdEWBF6AmqI8Dc08BnprNRlc/ZpjGSUOnmFKbAWKwyCPwacx/0QK54PLLae4xW/2TYcuiUaUj0a +7CIMHOCkoj3w6DnPgcB77V0fb8XQC9eY +-----END CERTIFICATE----- + +Deutsche Telekom Root CA 2 +========================== +-----BEGIN CERTIFICATE----- +MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMT +RGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEG +A1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENBIDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5 +MjM1OTAwWjBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0G +A1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBS +b290IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEUha88EOQ5 +bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhCQN/Po7qCWWqSG6wcmtoI +KyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1MjwrrFDa1sPeg5TKqAyZMg4ISFZbavva4VhY +AUlfckE8FQYBjl2tqriTtM2e66foai1SNNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aK +Se5TBY8ZTNXeWHmb0mocQqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTV +jlsB9WoHtxa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAPBgNV +HRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAlGRZrTlk5ynr +E/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756AbrsptJh6sTtU6zkXR34ajgv8HzFZMQSy +zhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpaIzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8 +rZ7/gFnkm0W09juwzTkZmDLl6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4G +dyd1Lx+4ivn+xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU +Cm26OWMohpLzGITY+9HPBVZkVw== +-----END CERTIFICATE----- + +ComSign Secured CA +================== +-----BEGIN CERTIFICATE----- +MIIDqzCCApOgAwIBAgIRAMcoRwmzuGxFjB36JPU2TukwDQYJKoZIhvcNAQEFBQAwPDEbMBkGA1UE +AxMSQ29tU2lnbiBTZWN1cmVkIENBMRAwDgYDVQQKEwdDb21TaWduMQswCQYDVQQGEwJJTDAeFw0w +NDAzMjQxMTM3MjBaFw0yOTAzMTYxNTA0NTZaMDwxGzAZBgNVBAMTEkNvbVNpZ24gU2VjdXJlZCBD +QTEQMA4GA1UEChMHQ29tU2lnbjELMAkGA1UEBhMCSUwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDGtWhfHZQVw6QIVS3joFd67+l0Kru5fFdJGhFeTymHDEjWaueP1H5XJLkGieQcPOqs +49ohgHMhCu95mGwfCP+hUH3ymBvJVG8+pSjsIQQPRbsHPaHA+iqYHU4Gk/v1iDurX8sWv+bznkqH +7Rnqwp9D5PGBpX8QTz7RSmKtUxvLg/8HZaWSLWapW7ha9B20IZFKF3ueMv5WJDmyVIRD9YTC2LxB +kMyd1mja6YJQqTtoz7VdApRgFrFD2UNd3V2Hbuq7s8lr9gOUCXDeFhF6K+h2j0kQmHe5Y1yLM5d1 +9guMsqtb3nQgJT/j8xH5h2iGNXHDHYwt6+UarA9z1YJZQIDTAgMBAAGjgacwgaQwDAYDVR0TBAUw +AwEB/zBEBgNVHR8EPTA7MDmgN6A1hjNodHRwOi8vZmVkaXIuY29tc2lnbi5jby5pbC9jcmwvQ29t +U2lnblNlY3VyZWRDQS5jcmwwDgYDVR0PAQH/BAQDAgGGMB8GA1UdIwQYMBaAFMFL7XC29z58ADsA +j8c+DkWfHl3sMB0GA1UdDgQWBBTBS+1wtvc+fAA7AI/HPg5Fnx5d7DANBgkqhkiG9w0BAQUFAAOC +AQEAFs/ukhNQq3sUnjO2QiBq1BW9Cav8cujvR3qQrFHBZE7piL1DRYHjZiM/EoZNGeQFsOY3wo3a +BijJD4mkU6l1P7CW+6tMM1X5eCZGbxs2mPtCdsGCuY7e+0X5YxtiOzkGynd6qDwJz2w2PQ8KRUtp +FhpFfTMDZflScZAmlaxMDPWLkz/MdXSFmLr/YnpNH4n+rr2UAJm/EaXc4HnFFgt9AmEd6oX5AhVP +51qJThRv4zdLhfXBPGHg/QVBspJ/wx2g0K5SZGBrGMYmnNj1ZOQ2GmKfig8+/21OGVZOIJFsnzQz +OjRXUDpvgV4GxvU+fE6OK85lBi5d0ipTdF7Tbieejw== +-----END CERTIFICATE----- + +Cybertrust Global Root +====================== +-----BEGIN CERTIFICATE----- +MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYGA1UEChMPQ3li +ZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBSb290MB4XDTA2MTIxNTA4 +MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQD +ExZDeWJlcnRydXN0IEdsb2JhbCBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA ++Mi8vRRQZhP/8NN57CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW +0ozSJ8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2yHLtgwEZL +AfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iPt3sMpTjr3kfb1V05/Iin +89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNzFtApD0mpSPCzqrdsxacwOUBdrsTiXSZT +8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAYXSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2 +MDSgMqAwhi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3JsMB8G +A1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUAA4IBAQBW7wojoFRO +lZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMjWqd8BfP9IjsO0QbE2zZMcwSO5bAi +5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUxXOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2 +hO0j9n0Hq0V+09+zv+mKts2oomcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+T +X3EJIrduPuocA06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW +WL1WMRJOEcgh4LMRkWXbtKaIOM5V +-----END CERTIFICATE----- + +ePKI Root Certification Authority +================================= +-----BEGIN CERTIFICATE----- +MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQG +EwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0ZC4xKjAoBgNVBAsMIWVQS0kg +Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMx +MjdaMF4xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEq +MCgGA1UECwwhZVBLSSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAHSyZbCUNs +IZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAhijHyl3SJCRImHJ7K2RKi +lTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3XDZoTM1PRYfl61dd4s5oz9wCGzh1NlDiv +qOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX +12ruOzjjK9SXDrkb5wdJfzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0O +WQqraffAsgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uUWH1+ +ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLSnT0IFaUQAS2zMnao +lQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pHdmX2Os+PYhcZewoozRrSgx4hxyy/ +vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJipNiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXi +Zo1jDiVN1Rmy5nk3pyKdVDECAwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/Qkqi +MAwGA1UdEwQFMAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH +ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGBuvl2ICO1J2B0 +1GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6YlPwZpVnPDimZI+ymBV3QGypzq +KOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkPJXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdV +xrsStZf0X4OFunHB2WyBEXYKCrC/gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEP +NXubrjlpC2JgQCA2j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+r +GNm65ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUBo2M3IUxE +xJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS/jQ6fbjpKdx2qcgw+BRx +gMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2zGp1iro2C6pSe3VkQw63d4k3jMdXH7Ojy +sP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTEW9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmOD +BCEIZ43ygknQW/2xzQ+DhNQ+IIX3Sj0rnP0qCglN6oH4EZw= +-----END CERTIFICATE----- + +T\xc3\x9c\x42\xC4\xB0TAK UEKAE K\xC3\xB6k Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1 - S\xC3\xBCr\xC3\xBCm 3 +============================================================================================================================= +-----BEGIN CERTIFICATE----- +MIIFFzCCA/+gAwIBAgIBETANBgkqhkiG9w0BAQUFADCCASsxCzAJBgNVBAYTAlRSMRgwFgYDVQQH +DA9HZWJ6ZSAtIEtvY2FlbGkxRzBFBgNVBAoMPlTDvHJraXllIEJpbGltc2VsIHZlIFRla25vbG9q +aWsgQXJhxZ90xLFybWEgS3VydW11IC0gVMOcQsSwVEFLMUgwRgYDVQQLDD9VbHVzYWwgRWxla3Ry +b25payB2ZSBLcmlwdG9sb2ppIEFyYcWfdMSxcm1hIEVuc3RpdMO8c8O8IC0gVUVLQUUxIzAhBgNV +BAsMGkthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppMUowSAYDVQQDDEFUw5xCxLBUQUsgVUVLQUUg +S8O2ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSAtIFPDvHLDvG0gMzAeFw0wNzA4 +MjQxMTM3MDdaFw0xNzA4MjExMTM3MDdaMIIBKzELMAkGA1UEBhMCVFIxGDAWBgNVBAcMD0dlYnpl +IC0gS29jYWVsaTFHMEUGA1UECgw+VMO8cmtpeWUgQmlsaW1zZWwgdmUgVGVrbm9sb2ppayBBcmHF +n3TEsXJtYSBLdXJ1bXUgLSBUw5xCxLBUQUsxSDBGBgNVBAsMP1VsdXNhbCBFbGVrdHJvbmlrIHZl +IEtyaXB0b2xvamkgQXJhxZ90xLFybWEgRW5zdGl0w7xzw7wgLSBVRUtBRTEjMCEGA1UECwwaS2Ft +dSBTZXJ0aWZpa2FzeW9uIE1lcmtlemkxSjBIBgNVBAMMQVTDnELEsFRBSyBVRUtBRSBLw7ZrIFNl +cnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIC0gU8O8csO8bSAzMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEAim1L/xCIOsP2fpTo6iBkcK4hgb46ezzb8R1Sf1n68yJMlaCQvEhO +Eav7t7WNeoMojCZG2E6VQIdhn8WebYGHV2yKO7Rm6sxA/OOqbLLLAdsyv9Lrhc+hDVXDWzhXcLh1 +xnnRFDDtG1hba+818qEhTsXOfJlfbLm4IpNQp81McGq+agV/E5wrHur+R84EpW+sky58K5+eeROR +6Oqeyjh1jmKwlZMq5d/pXpduIF9fhHpEORlAHLpVK/swsoHvhOPc7Jg4OQOFCKlUAwUp8MmPi+oL +hmUZEdPpCSPeaJMDyTYcIW7OjGbxmTDY17PDHfiBLqi9ggtm/oLL4eAagsNAgQIDAQABo0IwQDAd +BgNVHQ4EFgQUvYiHyY/2pAoLquvF/pEjnatKijIwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAB18+kmPNOm3JpIWmgV050vQbTlswyb2zrgxvMTfvCr4 +N5EY3ATIZJkrGG2AA1nJrvhY0D7twyOfaTyGOBye79oneNGEN3GKPEs5z35FBtYt2IpNeBLWrcLT +y9LQQfMmNkqblWwM7uXRQydmwYj3erMgbOqwaSvHIOgMA8RBBZniP+Rr+KCGgceExh/VS4ESshYh +LBOhgLJeDEoTniDYYkCrkOpkSi+sDQESeUWoL4cZaMjihccwsnX5OD+ywJO0a+IDRM5noN+J1q2M +dqMTw5RhK2vZbMEHCiIHhWyFJEapvj+LeISCfiQMnf2BN+MlqO02TpUsyZyQ2uypQjyttgI= +-----END CERTIFICATE----- + +Buypass Class 2 CA 1 +==================== +-----BEGIN CERTIFICATE----- +MIIDUzCCAjugAwIBAgIBATANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3MgQ2xhc3MgMiBDQSAxMB4XDTA2 +MTAxMzEwMjUwOVoXDTE2MTAxMzEwMjUwOVowSzELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBh +c3MgQVMtOTgzMTYzMzI3MR0wGwYDVQQDDBRCdXlwYXNzIENsYXNzIDIgQ0EgMTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAIs8B0XY9t/mx8q6jUPFR42wWsE425KEHK8T1A9vNkYgxC7M +cXA0ojTTNy7Y3Tp3L8DrKehc0rWpkTSHIln+zNvnma+WwajHQN2lFYxuyHyXA8vmIPLXl18xoS83 +0r7uvqmtqEyeIWZDO6i88wmjONVZJMHCR3axiFyCO7srpgTXjAePzdVBHfCuuCkslFJgNJQ72uA4 +0Z0zPhX0kzLFANq1KWYOOngPIVJfAuWSeyXTkh4vFZ2B5J2O6O+JzhRMVB0cgRJNcKi+EAUXfh/R +uFdV7c27UsKwHnjCTTZoy1YmwVLBvXb3WNVyfh9EdrsAiR0WnVE1703CVu9r4Iw7DekCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUP42aWYv8e3uco684sDntkHGA1sgwDgYDVR0P +AQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAVGn4TirnoB6NLJzKyQJHyIdFkhb5jatLPgcIV +1Xp+DCmsNx4cfHZSldq1fyOhKXdlyTKdqC5Wq2B2zha0jX94wNWZUYN/Xtm+DKhQ7SLHrQVMdvvt +7h5HZPb3J31cKA9FxVxiXqaakZG3Uxcu3K1gnZZkOb1naLKuBctN518fV4bVIJwo+28TOPX2EZL2 +fZleHwzoq0QkKXJAPTZSr4xYkHPB7GEseaHsh7U/2k3ZIQAw3pDaDtMaSKk+hQsUi4y8QZ5q9w5w +wDX3OaJdZtB7WZ+oRxKaJyOkLY4ng5IgodcVf/EuGO70SH8vf/GhGLWhC5SgYiAynB321O+/TIho +-----END CERTIFICATE----- + +Buypass Class 3 CA 1 +==================== +-----BEGIN CERTIFICATE----- +MIIDUzCCAjugAwIBAgIBAjANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3MgQ2xhc3MgMyBDQSAxMB4XDTA1 +MDUwOTE0MTMwM1oXDTE1MDUwOTE0MTMwM1owSzELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBh +c3MgQVMtOTgzMTYzMzI3MR0wGwYDVQQDDBRCdXlwYXNzIENsYXNzIDMgQ0EgMTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAKSO13TZKWTeXx+HgJHqTjnmGcZEC4DVC69TB4sSveZn8AKx +ifZgisRbsELRwCGoy+Gb72RRtqfPFfV0gGgEkKBYouZ0plNTVUhjP5JW3SROjvi6K//zNIqeKNc0 +n6wv1g/xpC+9UrJJhW05NfBEMJNGJPO251P7vGGvqaMU+8IXF4Rs4HyI+MkcVyzwPX6UvCWThOia +AJpFBUJXgPROztmuOfbIUxAMZTpHe2DC1vqRycZxbL2RhzyRhkmr8w+gbCZ2Xhysm3HljbybIR6c +1jh+JIAVMYKWsUnTYjdbiAwKYjT+p0h+mbEwi5A3lRyoH6UsjfRVyNvdWQrCrXig9IsCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUOBTmyPCppAP0Tj4io1vy1uCtQHQwDgYDVR0P +AQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQABZ6OMySU9E2NdFm/soT4JXJEVKirZgCFPBdy7 +pYmrEzMqnji3jG8CcmPHc3ceCQa6Oyh7pEfJYWsICCD8igWKH7y6xsL+z27sEzNxZy5p+qksP2bA +EllNC1QCkoS72xLvg3BweMhT+t/Gxv/ciC8HwEmdMldg0/L2mSlf56oBzKwzqBwKu5HEA6BvtjT5 +htOzdlSY9EqBs1OdTUDs5XcTRa9bqh/YL0yCe/4qxFi7T/ye/QNlGioOw6UgFpRreaaiErS7GqQj +el/wroQk5PMr+4okoyeYZdowdXb8GZHo2+ubPzK/QJcHJrrM85SFSnonk8+QQtS4Wxam58tAA915 +-----END CERTIFICATE----- + +EBG Elektronik Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1 +========================================================================== +-----BEGIN CERTIFICATE----- +MIIF5zCCA8+gAwIBAgIITK9zQhyOdAIwDQYJKoZIhvcNAQEFBQAwgYAxODA2BgNVBAMML0VCRyBF +bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMTcwNQYDVQQKDC5FQkcg +QmlsacWfaW0gVGVrbm9sb2ppbGVyaSB2ZSBIaXptZXRsZXJpIEEuxZ4uMQswCQYDVQQGEwJUUjAe +Fw0wNjA4MTcwMDIxMDlaFw0xNjA4MTQwMDMxMDlaMIGAMTgwNgYDVQQDDC9FQkcgRWxla3Ryb25p +ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTE3MDUGA1UECgwuRUJHIEJpbGnFn2lt +IFRla25vbG9qaWxlcmkgdmUgSGl6bWV0bGVyaSBBLsWeLjELMAkGA1UEBhMCVFIwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQDuoIRh0DpqZhAy2DE4f6en5f2h4fuXd7hxlugTlkaDT7by +X3JWbhNgpQGR4lvFzVcfd2NR/y8927k/qqk153nQ9dAktiHq6yOU/im/+4mRDGSaBUorzAzu8T2b +gmmkTPiab+ci2hC6X5L8GCcKqKpE+i4stPtGmggDg3KriORqcsnlZR9uKg+ds+g75AxuetpX/dfr +eYteIAbTdgtsApWjluTLdlHRKJ2hGvxEok3MenaoDT2/F08iiFD9rrbskFBKW5+VQarKD7JK/oCZ +TqNGFav4c0JqwmZ2sQomFd2TkuzbqV9UIlKRcF0T6kjsbgNs2d1s/OsNA/+mgxKb8amTD8UmTDGy +Y5lhcucqZJnSuOl14nypqZoaqsNW2xCaPINStnuWt6yHd6i58mcLlEOzrz5z+kI2sSXFCjEmN1Zn +uqMLfdb3ic1nobc6HmZP9qBVFCVMLDMNpkGMvQQxahByCp0OLna9XvNRiYuoP1Vzv9s6xiQFlpJI +qkuNKgPlV5EQ9GooFW5Hd4RcUXSfGenmHmMWOeMRFeNYGkS9y8RsZteEBt8w9DeiQyJ50hBs37vm +ExH8nYQKE3vwO9D8owrXieqWfo1IhR5kX9tUoqzVegJ5a9KK8GfaZXINFHDk6Y54jzJ0fFfy1tb0 +Nokb+Clsi7n2l9GkLqq+CxnCRelwXQIDAJ3Zo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB +/wQEAwIBBjAdBgNVHQ4EFgQU587GT/wWZ5b6SqMHwQSny2re2kcwHwYDVR0jBBgwFoAU587GT/wW +Z5b6SqMHwQSny2re2kcwDQYJKoZIhvcNAQEFBQADggIBAJuYml2+8ygjdsZs93/mQJ7ANtyVDR2t +FcU22NU57/IeIl6zgrRdu0waypIN30ckHrMk2pGI6YNw3ZPX6bqz3xZaPt7gyPvT/Wwp+BVGoGgm +zJNSroIBk5DKd8pNSe/iWtkqvTDOTLKBtjDOWU/aWR1qeqRFsIImgYZ29fUQALjuswnoT4cCB64k +XPBfrAowzIpAoHMEwfuJJPaaHFy3PApnNgUIMbOv2AFoKuB4j3TeuFGkjGwgPaL7s9QJ/XvCgKqT +bCmYIai7FvOpEl90tYeY8pUm3zTvilORiF0alKM/fCL414i6poyWqD1SNGKfAB5UVUJnxk1Gj7sU +RT0KlhaOEKGXmdXTMIXM3rRyt7yKPBgpaP3ccQfuJDlq+u2lrDgv+R4QDgZxGhBM/nV+/x5XOULK +1+EVoVZVWRvRo68R2E7DpSvvkL/A7IITW43WciyTTo9qKd+FPNMN4KIYEsxVL0e3p5sC/kH2iExt +2qkBR4NkJ2IQgtYSe14DHzSpyZH+r11thie3I6p1GMog57AP14kOpmciY/SDQSsGS7tY1dHXt7kQ +Y9iJSrSq3RZj9W6+YKH47ejWkE8axsWgKdOnIaj1Wjz3x0miIZpKlVIglnKaZsv30oZDfCK+lvm9 +AahH3eU7QPl1K5srRmSGjR70j/sHd9DqSaIcjVIUpgqT +-----END CERTIFICATE----- + +certSIGN ROOT CA +================ +-----BEGIN CERTIFICATE----- +MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYTAlJPMREwDwYD +VQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTAeFw0wNjA3MDQxNzIwMDRa +Fw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UE +CxMQY2VydFNJR04gUk9PVCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7I +JUqOtdu0KBuqV5Do0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHH +rfAQUySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5dRdY4zTW2 +ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQOA7+j0xbm0bqQfWwCHTD +0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwvJoIQ4uNllAoEwF73XVv4EOLQunpL+943 +AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B +Af8EBAMCAcYwHQYDVR0OBBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IB +AQA+0hyJLjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecYMnQ8 +SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ44gx+FkagQnIl6Z0 +x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6IJd1hJyMctTEHBDa0GpC9oHRxUIlt +vBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNwi/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7Nz +TogVZ96edhBiIL5VaZVDADlN9u6wWk5JRFRYX0KD +-----END CERTIFICATE----- + +CNNIC ROOT +========== +-----BEGIN CERTIFICATE----- +MIIDVTCCAj2gAwIBAgIESTMAATANBgkqhkiG9w0BAQUFADAyMQswCQYDVQQGEwJDTjEOMAwGA1UE +ChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1QwHhcNMDcwNDE2MDcwOTE0WhcNMjcwNDE2MDcw +OTE0WjAyMQswCQYDVQQGEwJDTjEOMAwGA1UEChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1Qw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDTNfc/c3et6FtzF8LRb+1VvG7q6KR5smzD +o+/hn7E7SIX1mlwhIhAsxYLO2uOabjfhhyzcuQxauohV3/2q2x8x6gHx3zkBwRP9SFIhxFXf2tiz +VHa6dLG3fdfA6PZZxU3Iva0fFNrfWEQlMhkqx35+jq44sDB7R3IJMfAw28Mbdim7aXZOV/kbZKKT +VrdvmW7bCgScEeOAH8tjlBAKqeFkgjH5jCftppkA9nCTGPihNIaj3XrCGHn2emU1z5DrvTOTn1Or +czvmmzQgLx3vqR1jGqCA2wMv+SYahtKNu6m+UjqHZ0gNv7Sg2Ca+I19zN38m5pIEo3/PIKe38zrK +y5nLAgMBAAGjczBxMBEGCWCGSAGG+EIBAQQEAwIABzAfBgNVHSMEGDAWgBRl8jGtKvf33VKWCscC +wQ7vptU7ETAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIB/jAdBgNVHQ4EFgQUZfIxrSr3991S +lgrHAsEO76bVOxEwDQYJKoZIhvcNAQEFBQADggEBAEs17szkrr/Dbq2flTtLP1se31cpolnKOOK5 +Gv+e5m4y3R6u6jW39ZORTtpC4cMXYFDy0VwmuYK36m3knITnA3kXr5g9lNvHugDnuL8BV8F3RTIM +O/G0HAiw/VGgod2aHRM2mm23xzy54cXZF/qD1T0VoDy7HgviyJA/qIYM/PmLXoXLT1tLYhFHxUV8 +BS9BsZ4QaRuZluBVeftOhpm4lNqGOGqTo+fLbuXf6iFViZx9fX+Y9QCJ7uOEwFyWtcVG6kbghVW2 +G8kS1sHNzYDzAgE8yGnLRUhj2JTQ7IUOO04RZfSCjKY9ri4ilAnIXOo8gV0WKgOXFlUJ24pBgp5m +mxE= +-----END CERTIFICATE----- + +ApplicationCA - Japanese Government +=================================== +-----BEGIN CERTIFICATE----- +MIIDoDCCAoigAwIBAgIBMTANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJKUDEcMBoGA1UEChMT +SmFwYW5lc2UgR292ZXJubWVudDEWMBQGA1UECxMNQXBwbGljYXRpb25DQTAeFw0wNzEyMTIxNTAw +MDBaFw0xNzEyMTIxNTAwMDBaMEMxCzAJBgNVBAYTAkpQMRwwGgYDVQQKExNKYXBhbmVzZSBHb3Zl +cm5tZW50MRYwFAYDVQQLEw1BcHBsaWNhdGlvbkNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAp23gdE6Hj6UG3mii24aZS2QNcfAKBZuOquHMLtJqO8F6tJdhjYq+xpqcBrSGUeQ3DnR4 +fl+Kf5Sk10cI/VBaVuRorChzoHvpfxiSQE8tnfWuREhzNgaeZCw7NCPbXCbkcXmP1G55IrmTwcrN +wVbtiGrXoDkhBFcsovW8R0FPXjQilbUfKW1eSvNNcr5BViCH/OlQR9cwFO5cjFW6WY2H/CPek9AE +jP3vbb3QesmlOmpyM8ZKDQUXKi17safY1vC+9D/qDihtQWEjdnjDuGWk81quzMKq2edY3rZ+nYVu +nyoKb58DKTCXKB28t89UKU5RMfkntigm/qJj5kEW8DOYRwIDAQABo4GeMIGbMB0GA1UdDgQWBBRU +WssmP3HMlEYNllPqa0jQk/5CdTAOBgNVHQ8BAf8EBAMCAQYwWQYDVR0RBFIwUKROMEwxCzAJBgNV +BAYTAkpQMRgwFgYDVQQKDA/ml6XmnKzlm73mlL/lupwxIzAhBgNVBAsMGuOCouODl+ODquOCseOD +vOOCt+ODp+ODs0NBMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADlqRHZ3ODrs +o2dGD/mLBqj7apAxzn7s2tGJfHrrLgy9mTLnsCTWw//1sogJhyzjVOGjprIIC8CFqMjSnHH2HZ9g +/DgzE+Ge3Atf2hZQKXsvcJEPmbo0NI2VdMV+eKlmXb3KIXdCEKxmJj3ekav9FfBv7WxfEPjzFvYD +io+nEhEMy/0/ecGc/WLuo89UDNErXxc+4z6/wCs+CZv+iKZ+tJIX/COUgb1up8WMwusRRdv4QcmW +dupwX3kSa+SjB1oF7ydJzyGfikwJcGapJsErEU4z0g781mzSDjJkaP+tBXhfAx2o45CsJOAPQKdL +rosot4LKGAfmt1t06SAZf7IbiVQ= +-----END CERTIFICATE----- + +GeoTrust Primary Certification Authority - G3 +============================================= +-----BEGIN CERTIFICATE----- +MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UE +BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA4IEdlb1RydXN0 +IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFy +eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIz +NTk1OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAo +YykgMjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMT +LUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz+uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5j +K/BGvESyiaHAKAxJcCGVn2TAppMSAmUmhsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdE +c5IiaacDiGydY8hS2pgn5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3C +IShwiP/WJmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exALDmKu +dlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZChuOl1UcCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMR5yo6hTgMdHNxr +2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IBAQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9 +cr5HqQ6XErhK8WTTOd8lNNTBzU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbE +Ap7aDHdlDkQNkv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD +AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUHSJsMC8tJP33s +t/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2Gspki4cErx5z481+oghLrGREt +-----END CERTIFICATE----- + +thawte Primary Root CA - G2 +=========================== +-----BEGIN CERTIFICATE----- +MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDELMAkGA1UEBhMC +VVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMpIDIwMDcgdGhhd3RlLCBJbmMu +IC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3Qg +Q0EgLSBHMjAeFw0wNzExMDUwMDAwMDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEV +MBMGA1UEChMMdGhhd3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBG +b3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAt +IEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/BebfowJPDQfGAFG6DAJS +LSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6papu+7qzcMBniKI11KOasf2twu8x+qi5 +8/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU +mtgAMADna3+FGO6Lts6KDPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUN +G4k8VIZ3KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41oxXZ3K +rr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg== +-----END CERTIFICATE----- + +thawte Primary Root CA - G3 +=========================== +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCBrjELMAkGA1UE +BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2 +aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0w +ODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh +d3RlLCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9uMTgwNgYD +VQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIG +A1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAsr8nLPvb2FvdeHsbnndmgcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2At +P0LMqmsywCPLLEHd5N/8YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC ++BsUa0Lfb1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS99irY +7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2SzhkGcuYMXDhpxwTW +vGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUkOQIDAQABo0IwQDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJ +KoZIhvcNAQELBQADggEBABpA2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweK +A3rD6z8KLFIWoCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu +t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7cKUGRIjxpp7sC +8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fMm7v/OeZWYdMKp8RcTGB7BXcm +er/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZuMdRAGmI0Nj81Aa6sY6A= +-----END CERTIFICATE----- + +GeoTrust Primary Certification Authority - G2 +============================================= +-----BEGIN CERTIFICATE----- +MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA3IEdlb1RydXN0IElu +Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1 +OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg +MjAwNyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMTLUdl +b1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjB2MBAGByqGSM49AgEG +BSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcLSo17VDs6bl8VAsBQps8lL33KSLjHUGMc +KiEIfJo22Av+0SbFWDEwKCXzXV2juLaltJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+ +EVXVMAoGCCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGTqQ7m +ndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBuczrD6ogRLQy7rQkgu2 +npaqBA+K +-----END CERTIFICATE----- + +VeriSign Universal Root Certification Authority +=============================================== +-----BEGIN CERTIFICATE----- +MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCBvTELMAkGA1UE +BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO +ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk +IHVzZSBvbmx5MTgwNgYDVQQDEy9WZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9u +IEF1dGhvcml0eTAeFw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJV +UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv +cmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj +1mCOkdeQmIN65lgZOIzF9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGP +MiJhgsWHH26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+HLL72 +9fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN/BMReYTtXlT2NJ8I +AfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPTrJ9VAMf2CGqUuV/c4DPxhGD5WycR +tPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0G +CCsGAQUFBwEMBGEwX6FdoFswWTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2O +a8PPgGrUSBgsexkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud +DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4sAPmLGd75JR3 +Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+seQxIcaBlVZaDrHC1LGmWazx +Y8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTx +P/jgdFcrGJ2BtMQo2pSXpXDrrB2+BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+P +wGZsY6rp2aQW9IHRlRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4 +mJO37M2CYfE45k+XmCpajQ== +-----END CERTIFICATE----- + +VeriSign Class 3 Public Primary Certification Authority - G4 +============================================================ +-----BEGIN CERTIFICATE----- +MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjELMAkGA1UEBhMC +VVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3 +b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVz +ZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBU +cnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRo +b3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5 +IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8 +Utpkmw4tXNherJI9/gHmGUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGz +rl0Bp3vefLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEw +HzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24u +Y29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMWkf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMD +A2gAMGUCMGYhDBgmYFo4e1ZC4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIx +AJw9SDkjOVgaFRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA== +-----END CERTIFICATE----- + +NetLock Arany (Class Gold) FÅ‘tanúsítvány +============================================ +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQGEwJIVTERMA8G +A1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610 +dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBB +cmFueSAoQ2xhc3MgR29sZCkgRsWRdGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgx +MjA2MTUwODIxWjCBpzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxO +ZXRMb2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlmaWNhdGlv +biBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNzIEdvbGQpIEbFkXRhbsO6 +c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxCRec75LbRTDofTjl5Bu +0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrTlF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw +/HpYzY6b7cNGbIRwXdrzAZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAk +H3B5r9s5VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRGILdw +fzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2BJtr+UBdADTHLpl1 +neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2MU9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwW +qZw8UQCgwBEIBaeZ5m8BiFRhbvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTta +YtOUZcTh5m2C+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC +bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2FuLjbvrW5Kfna +NwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2XjG4Kvte9nHfRCaexOYNkbQu +dZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= +-----END CERTIFICATE----- + +Staat der Nederlanden Root CA - G2 +================================== +-----BEGIN CERTIFICATE----- +MIIFyjCCA7KgAwIBAgIEAJiWjDANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJOTDEeMBwGA1UE +CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFhdCBkZXIgTmVkZXJsYW5kZW4g +Um9vdCBDQSAtIEcyMB4XDTA4MDMyNjExMTgxN1oXDTIwMDMyNTExMDMxMFowWjELMAkGA1UEBhMC +TkwxHjAcBgNVBAoMFVN0YWF0IGRlciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5l +ZGVybGFuZGVuIFJvb3QgQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMVZ +5291qj5LnLW4rJ4L5PnZyqtdj7U5EILXr1HgO+EASGrP2uEGQxGZqhQlEq0i6ABtQ8SpuOUfiUtn +vWFI7/3S4GCI5bkYYCjDdyutsDeqN95kWSpGV+RLufg3fNU254DBtvPUZ5uW6M7XxgpT0GtJlvOj +CwV3SPcl5XCsMBQgJeN/dVrlSPhOewMHBPqCYYdu8DvEpMfQ9XQ+pV0aCPKbJdL2rAQmPlU6Yiil +e7Iwr/g3wtG61jj99O9JMDeZJiFIhQGp5Rbn3JBV3w/oOM2ZNyFPXfUib2rFEhZgF1XyZWampzCR +OME4HYYEhLoaJXhena/MUGDWE4dS7WMfbWV9whUYdMrhfmQpjHLYFhN9C0lK8SgbIHRrxT3dsKpI +CT0ugpTNGmXZK4iambwYfp/ufWZ8Pr2UuIHOzZgweMFvZ9C+X+Bo7d7iscksWXiSqt8rYGPy5V65 +48r6f1CGPqI0GAwJaCgRHOThuVw+R7oyPxjMW4T182t0xHJ04eOLoEq9jWYv6q012iDTiIJh8BIi +trzQ1aTsr1SIJSQ8p22xcik/Plemf1WvbibG/ufMQFxRRIEKeN5KzlW/HdXZt1bv8Hb/C3m1r737 +qWmRRpdogBQ2HbN/uymYNqUg+oJgYjOk7Na6B6duxc8UpufWkjTYgfX8HV2qXB72o007uPc5AgMB +AAGjgZcwgZQwDwYDVR0TAQH/BAUwAwEB/zBSBgNVHSAESzBJMEcGBFUdIAAwPzA9BggrBgEFBQcC +ARYxaHR0cDovL3d3dy5wa2lvdmVyaGVpZC5ubC9wb2xpY2llcy9yb290LXBvbGljeS1HMjAOBgNV +HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJFoMocVHYnitfGsNig0jQt8YojrMA0GCSqGSIb3DQEBCwUA +A4ICAQCoQUpnKpKBglBu4dfYszk78wIVCVBR7y29JHuIhjv5tLySCZa59sCrI2AGeYwRTlHSeYAz ++51IvuxBQ4EffkdAHOV6CMqqi3WtFMTC6GY8ggen5ieCWxjmD27ZUD6KQhgpxrRW/FYQoAUXvQwj +f/ST7ZwaUb7dRUG/kSS0H4zpX897IZmflZ85OkYcbPnNe5yQzSipx6lVu6xiNGI1E0sUOlWDuYaN +kqbG9AclVMwWVxJKgnjIFNkXgiYtXSAfea7+1HAWFpWD2DU5/1JddRwWxRNVz0fMdWVSSt7wsKfk +CpYL+63C4iWEst3kvX5ZbJvw8NjnyvLplzh+ib7M+zkXYT9y2zqR2GUBGR2tUKRXCnxLvJxxcypF +URmFzI79R6d0lR2o0a9OF7FpJsKqeFdbxU2n5Z4FF5TKsl+gSRiNNOkmbEgeqmiSBeGCc1qb3Adb +CG19ndeNIdn8FCCqwkXfP+cAslHkwvgFuXkajDTznlvkN1trSt8sV4pAWja63XVECDdCcAz+3F4h +oKOKwJCcaNpQ5kUQR3i2TtJlycM33+FCY7BXN0Ute4qcvwXqZVUz9zkQxSgqIXobisQk+T8VyJoV +IPVVYpbtbZNQvOSqeK3Zywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm +66+KAQ== +-----END CERTIFICATE----- + +CA Disig +======== +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBATANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQGEwJTSzETMBEGA1UEBxMK +QnJhdGlzbGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwHhcNMDYw +MzIyMDEzOTM0WhcNMTYwMzIyMDEzOTM0WjBKMQswCQYDVQQGEwJTSzETMBEGA1UEBxMKQnJhdGlz +bGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCS9jHBfYj9mQGp2HvycXXxMcbzdWb6UShGhJd4NLxs/LxFWYgm +GErENx+hSkS943EE9UQX4j/8SFhvXJ56CbpRNyIjZkMhsDxkovhqFQ4/61HhVKndBpnXmjxUizkD +Pw/Fzsbrg3ICqB9x8y34dQjbYkzo+s7552oftms1grrijxaSfQUMbEYDXcDtab86wYqg6I7ZuUUo +hwjstMoVvoLdtUSLLa2GDGhibYVW8qwUYzrG0ZmsNHhWS8+2rT+MitcE5eN4TPWGqvWP+j1scaMt +ymfraHtuM6kMgiioTGohQBUgDCZbg8KpFhXAJIJdKxatymP2dACw30PEEGBWZ2NFAgMBAAGjgf8w +gfwwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUjbJJaJ1yCCW5wCf1UJNWSEZx+Y8wDgYDVR0P +AQH/BAQDAgEGMDYGA1UdEQQvMC2BE2Nhb3BlcmF0b3JAZGlzaWcuc2uGFmh0dHA6Ly93d3cuZGlz +aWcuc2svY2EwZgYDVR0fBF8wXTAtoCugKYYnaHR0cDovL3d3dy5kaXNpZy5zay9jYS9jcmwvY2Ff +ZGlzaWcuY3JsMCygKqAohiZodHRwOi8vY2EuZGlzaWcuc2svY2EvY3JsL2NhX2Rpc2lnLmNybDAa +BgNVHSAEEzARMA8GDSuBHpGT5goAAAABAQEwDQYJKoZIhvcNAQEFBQADggEBAF00dGFMrzvY/59t +WDYcPQuBDRIrRhCA/ec8J9B6yKm2fnQwM6M6int0wHl5QpNt/7EpFIKrIYwvF/k/Ji/1WcbvgAa3 +mkkp7M5+cTxqEEHA9tOasnxakZzArFvITV734VP/Q3f8nktnbNfzg9Gg4H8l37iYC5oyOGwwoPP/ +CBUz91BKez6jPiCp3C9WgArtQVCwyfTssuMmRAAOb54GvCKWU3BlxFAKRmukLyeBEicTXxChds6K +ezfqwzlhA5WYOudsiCUI/HloDYd9Yvi0X/vF2Ey9WLw/Q1vUHgFNPGO+I++MzVpQuGhU+QqZMxEA +4Z7CRneC9VkGjCFMhwnN5ag= +-----END CERTIFICATE----- + +Juur-SK +======= +-----BEGIN CERTIFICATE----- +MIIE5jCCA86gAwIBAgIEO45L/DANBgkqhkiG9w0BAQUFADBdMRgwFgYJKoZIhvcNAQkBFglwa2lA +c2suZWUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKExlBUyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMRAw +DgYDVQQDEwdKdXVyLVNLMB4XDTAxMDgzMDE0MjMwMVoXDTE2MDgyNjE0MjMwMVowXTEYMBYGCSqG +SIb3DQEJARYJcGtpQHNrLmVlMQswCQYDVQQGEwJFRTEiMCAGA1UEChMZQVMgU2VydGlmaXRzZWVy +aW1pc2tlc2t1czEQMA4GA1UEAxMHSnV1ci1TSzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAIFxNj4zB9bjMI0TfncyRsvPGbJgMUaXhvSYRqTCZUXP00B841oiqBB4M8yIsdOBSvZiF3tf +TQou0M+LI+5PAk676w7KvRhj6IAcjeEcjT3g/1tf6mTll+g/mX8MCgkzABpTpyHhOEvWgxutr2TC ++Rx6jGZITWYfGAriPrsfB2WThbkasLnE+w0R9vXW+RvHLCu3GFH+4Hv2qEivbDtPL+/40UceJlfw +UR0zlv/vWT3aTdEVNMfqPxZIe5EcgEMPPbgFPtGzlc3Yyg/CQ2fbt5PgIoIuvvVoKIO5wTtpeyDa +Tpxt4brNj3pssAki14sL2xzVWiZbDcDq5WDQn/413z8CAwEAAaOCAawwggGoMA8GA1UdEwEB/wQF +MAMBAf8wggEWBgNVHSAEggENMIIBCTCCAQUGCisGAQQBzh8BAQEwgfYwgdAGCCsGAQUFBwICMIHD +HoHAAFMAZQBlACAAcwBlAHIAdABpAGYAaQBrAGEAYQB0ACAAbwBuACAAdgDkAGwAagBhAHMAdABh +AHQAdQBkACAAQQBTAC0AaQBzACAAUwBlAHIAdABpAGYAaQB0AHMAZQBlAHIAaQBtAGkAcwBrAGUA +cwBrAHUAcwAgAGEAbABhAG0ALQBTAEsAIABzAGUAcgB0AGkAZgBpAGsAYQBhAHQAaQBkAGUAIABr +AGkAbgBuAGkAdABhAG0AaQBzAGUAawBzMCEGCCsGAQUFBwIBFhVodHRwOi8vd3d3LnNrLmVlL2Nw +cy8wKwYDVR0fBCQwIjAgoB6gHIYaaHR0cDovL3d3dy5zay5lZS9qdXVyL2NybC8wHQYDVR0OBBYE +FASqekej5ImvGs8KQKcYP2/v6X2+MB8GA1UdIwQYMBaAFASqekej5ImvGs8KQKcYP2/v6X2+MA4G +A1UdDwEB/wQEAwIB5jANBgkqhkiG9w0BAQUFAAOCAQEAe8EYlFOiCfP+JmeaUOTDBS8rNXiRTHyo +ERF5TElZrMj3hWVcRrs7EKACr81Ptcw2Kuxd/u+gkcm2k298gFTsxwhwDY77guwqYHhpNjbRxZyL +abVAyJRld/JXIWY7zoVAtjNjGr95HvxcHdMdkxuLDF2FvZkwMhgJkVLpfKG6/2SSmuz+Ne6ML678 +IIbsSt4beDI3poHSna9aEhbKmVv8b20OxaAehsmR0FyYgl9jDIpaq9iVpszLita/ZEuOyoqysOkh +Mp6qqIWYNIE5ITuoOlIyPfZrN4YGWhWY3PARZv40ILcD9EEQfTmEeZZyY7aWAuVrua0ZTbvGRNs2 +yyqcjg== +-----END CERTIFICATE----- + +Hongkong Post Root CA 1 +======================= +-----BEGIN CERTIFICATE----- +MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoT +DUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMB4XDTAzMDUx +NTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25n +IFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1 +ApzQjVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEnPzlTCeqr +auh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjhZY4bXSNmO7ilMlHIhqqh +qZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9nnV0ttgCXjqQesBCNnLsak3c78QA3xMY +V18meMjWCnl3v/evt3a5pQuEF10Q6m/hq5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNV +HRMBAf8ECDAGAQH/AgEDMA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7i +h9legYsCmEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI37pio +l7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clBoiMBdDhViw+5Lmei +IAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJsEhTkYY2sEJCehFC78JZvRZ+K88ps +T/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpOfMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilT +c4afU9hDDl3WY4JxHYB0yvbiAmvZWg== +-----END CERTIFICATE----- + +SecureSign RootCA11 +=================== +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDErMCkGA1UEChMi +SmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoGA1UEAxMTU2VjdXJlU2lnbiBS +b290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSsw +KQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1 +cmVTaWduIFJvb3RDQTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvL +TJszi1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8h9uuywGO +wvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOVMdrAG/LuYpmGYz+/3ZMq +g6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rP +O7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitA +bpSACW22s293bzUIUPsCh8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZX +t94wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAKCh +OBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xmKbabfSVSSUOrTC4r +bnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQX5Ucv+2rIrVls4W6ng+4reV6G4pQ +Oh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWrQbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01 +y8hSyn+B/tlr0/cR7SXf+Of5pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061 +lgeLKBObjBmNQSdJQO7e5iNEOdyhIta6A/I= +-----END CERTIFICATE----- + +ACEDICOM Root +============= +-----BEGIN CERTIFICATE----- +MIIFtTCCA52gAwIBAgIIYY3HhjsBggUwDQYJKoZIhvcNAQEFBQAwRDEWMBQGA1UEAwwNQUNFRElD +T00gUm9vdDEMMAoGA1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00xCzAJBgNVBAYTAkVTMB4XDTA4 +MDQxODE2MjQyMloXDTI4MDQxMzE2MjQyMlowRDEWMBQGA1UEAwwNQUNFRElDT00gUm9vdDEMMAoG +A1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00xCzAJBgNVBAYTAkVTMIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA/5KV4WgGdrQsyFhIyv2AVClVYyT/kGWbEHV7w2rbYgIB8hiGtXxaOLHk +WLn709gtn70yN78sFW2+tfQh0hOR2QetAQXW8713zl9CgQr5auODAKgrLlUTY4HKRxx7XBZXehuD +YAQ6PmXDzQHe3qTWDLqO3tkE7hdWIpuPY/1NFgu3e3eM+SW10W2ZEi5PGrjm6gSSrj0RuVFCPYew +MYWveVqc/udOXpJPQ/yrOq2lEiZmueIM15jO1FillUAKt0SdE3QrwqXrIhWYENiLxQSfHY9g5QYb +m8+5eaA9oiM/Qj9r+hwDezCNzmzAv+YbX79nuIQZ1RXve8uQNjFiybwCq0Zfm/4aaJQ0PZCOrfbk +HQl/Sog4P75n/TSW9R28MHTLOO7VbKvU/PQAtwBbhTIWdjPp2KOZnQUAqhbm84F9b32qhm2tFXTT +xKJxqvQUfecyuB+81fFOvW8XAjnXDpVCOscAPukmYxHqC9FK/xidstd7LzrZlvvoHpKuE1XI2Sf2 +3EgbsCTBheN3nZqk8wwRHQ3ItBTutYJXCb8gWH8vIiPYcMt5bMlL8qkqyPyHK9caUPgn6C9D4zq9 +2Fdx/c6mUlv53U3t5fZvie27k5x2IXXwkkwp9y+cAS7+UEaeZAwUswdbxcJzbPEHXEUkFDWug/Fq +TYl6+rPYLWbwNof1K1MCAwEAAaOBqjCBpzAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKaz +4SsrSbbXc6GqlPUB53NlTKxQMA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUprPhKytJttdzoaqU +9QHnc2VMrFAwRAYDVR0gBD0wOzA5BgRVHSAAMDEwLwYIKwYBBQUHAgEWI2h0dHA6Ly9hY2VkaWNv +bS5lZGljb21ncm91cC5jb20vZG9jMA0GCSqGSIb3DQEBBQUAA4ICAQDOLAtSUWImfQwng4/F9tqg +aHtPkl7qpHMyEVNEskTLnewPeUKzEKbHDZ3Ltvo/Onzqv4hTGzz3gvoFNTPhNahXwOf9jU8/kzJP +eGYDdwdY6ZXIfj7QeQCM8htRM5u8lOk6e25SLTKeI6RF+7YuE7CLGLHdztUdp0J/Vb77W7tH1Pwk +zQSulgUV1qzOMPPKC8W64iLgpq0i5ALudBF/TP94HTXa5gI06xgSYXcGCRZj6hitoocf8seACQl1 +ThCojz2GuHURwCRiipZ7SkXp7FnFvmuD5uHorLUwHv4FB4D54SMNUI8FmP8sX+g7tq3PgbUhh8oI +KiMnMCArz+2UW6yyetLHKKGKC5tNSixthT8Jcjxn4tncB7rrZXtaAWPWkFtPF2Y9fwsZo5NjEFIq +nxQWWOLcpfShFosOkYuByptZ+thrkQdlVV9SH686+5DdaaVbnG0OLLb6zqylfDJKZ0DcMDQj3dcE +I2bw/FWAp/tmGYI1Z2JwOV5vx+qQQEQIHriy1tvuWacNGHk0vFQYXlPKNFHtRQrmjseCNj6nOGOp +MCwXEGCSn1WHElkQwg9naRHMTh5+Spqtr0CodaxWkHS4oJyleW/c6RrIaQXpuvoDs3zk4E7Czp3o +tkYNbn5XOmeUwssfnHdKZ05phkOTOPu220+DkdRgfks+KzgHVZhepA== +-----END CERTIFICATE----- + +Verisign Class 3 Public Primary Certification Authority +======================================================= +-----BEGIN CERTIFICATE----- +MIICPDCCAaUCEDyRMcsf9tAbDpq40ES/Er4wDQYJKoZIhvcNAQEFBQAwXzELMAkGA1UEBhMCVVMx +FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5 +IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVow +XzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAz +IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhEBarsAx94 +f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Ol +hec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBABByUqkFFBky +CEHwxWsKzH4PIRnN5GfcX6kb5sroc50i2JhucwNhkcV8sEVAbkSdjbCxlnRhLQ2pRdKkkirWmnWX +bj9T/UWZYB2oK0z5XqcJ2HUw19JlYD1n1khVdWk/kfVIC0dpImmClr7JyDiGSnoscxlIaU5rfGW/ +D/xwzoiQ +-----END CERTIFICATE----- + +Microsec e-Szigno Root CA 2009 +============================== +-----BEGIN CERTIFICATE----- +MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYDVQQGEwJIVTER +MA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jv +c2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o +dTAeFw0wOTA2MTYxMTMwMThaFw0yOTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UE +BwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUt +U3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvPkd6mJviZpWNwrZuuyjNA +fW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tccbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG +0IMZfcChEhyVbUr02MelTTMuhTlAdX4UfIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKA +pxn1ntxVUwOXewdI/5n7N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm +1HxdrtbCxkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1+rUC +AwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTLD8bf +QkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAbBgNVHREE +FDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqGSIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0o +lZMEyL/azXm4Q5DwpL7v8u8hmLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfX +I/OMn74dseGkddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 +tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c2Pm2G2JwCz02 +yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5tHMN1Rq41Bab2XD0h7lbwyYIi +LXpUq3DDfSJlgnCW +-----END CERTIFICATE----- + +E-Guven Kok Elektronik Sertifika Hizmet Saglayicisi +=================================================== +-----BEGIN CERTIFICATE----- +MIIDtjCCAp6gAwIBAgIQRJmNPMADJ72cdpW56tustTANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQG +EwJUUjEoMCYGA1UEChMfRWxla3Ryb25payBCaWxnaSBHdXZlbmxpZ2kgQS5TLjE8MDoGA1UEAxMz +ZS1HdXZlbiBLb2sgRWxla3Ryb25payBTZXJ0aWZpa2EgSGl6bWV0IFNhZ2xheWljaXNpMB4XDTA3 +MDEwNDExMzI0OFoXDTE3MDEwNDExMzI0OFowdTELMAkGA1UEBhMCVFIxKDAmBgNVBAoTH0VsZWt0 +cm9uaWsgQmlsZ2kgR3V2ZW5saWdpIEEuUy4xPDA6BgNVBAMTM2UtR3V2ZW4gS29rIEVsZWt0cm9u +aWsgU2VydGlmaWthIEhpem1ldCBTYWdsYXlpY2lzaTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAMMSIJ6wXgBljU5Gu4Bc6SwGl9XzcslwuedLZYDBS75+PNdUMZTe1RK6UxYC6lhj71vY +8+0qGqpxSKPcEC1fX+tcS5yWCEIlKBHMilpiAVDV6wlTL/jDj/6z/P2douNffb7tC+Bg62nsM+3Y +jfsSSYMAyYuXjDtzKjKzEve5TfL0TW3H5tYmNwjy2f1rXKPlSFxYvEK+A1qBuhw1DADT9SN+cTAI +JjjcJRFHLfO6IxClv7wC90Nex/6wN1CZew+TzuZDLMN+DfIcQ2Zgy2ExR4ejT669VmxMvLz4Bcpk +9Ok0oSy1c+HCPujIyTQlCFzz7abHlJ+tiEMl1+E5YP6sOVkCAwEAAaNCMEAwDgYDVR0PAQH/BAQD +AgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFJ/uRLOU1fqRTy7ZVZoEVtstxNulMA0GCSqG +SIb3DQEBBQUAA4IBAQB/X7lTW2M9dTLn+sR0GstG30ZpHFLPqk/CaOv/gKlR6D1id4k9CnU58W5d +F4dvaAXBlGzZXd/aslnLpRCKysw5zZ/rTt5S/wzw9JKp8mxTq5vSR6AfdPebmvEvFZ96ZDAYBzwq +D2fK/A+JYZ1lpTzlvBNbCNvj/+27BrtqBrF6T2XGgv0enIu1De5Iu7i9qgi0+6N8y5/NkHZchpZ4 +Vwpm+Vganf2XKWDeEaaQHBkc7gGWIjQ0LpH5t8Qn0Xvmv/uARFoW5evg1Ao4vOSR49XrXMGs3xtq +fJ7lddK2l4fbzIcrQzqECK+rPNv3PGYxhrCdU3nt+CPeQuMtgvEP5fqX +-----END CERTIFICATE----- + +GlobalSign Root CA - R3 +======================= +-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4GA1UECxMXR2xv +YmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh +bFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT +aWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln +bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWt +iHL8RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsTgHeMCOFJ +0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmmKPZpO/bLyCiR5Z2KYVc3 +rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zdQQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjl +OCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZXriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2 +xmmFghcCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE +FI/wS3+oLkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZURUm7 +lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMpjjM5RcOO5LlXbKr8 +EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK6fBdRoyV3XpYKBovHd7NADdBj+1E +bddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQXmcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18 +YIvDQVETI53O9zJrlAGomecsMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7r +kpeDMdmztcpHWD9f +-----END CERTIFICATE----- + +Autoridad de Certificacion Firmaprofesional CIF A62634068 +========================================================= +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UEBhMCRVMxQjBA +BgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2 +MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEyMzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIw +QAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBB +NjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDD +Utd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQMcas9UX4P +B99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefGL9ItWY16Ck6WaVICqjaY +7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15iNA9wBj4gGFrO93IbJWyTdBSTo3OxDqqH +ECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyI +plD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctX +MbScyJCyZ/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsX +LZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLK +bpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgU +vzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1Ud +EwEB/wQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNH +DhpkLzCBpgYDVR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp +cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBvACAAZABlACAA +bABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBlAGwAbwBuAGEAIAAwADgAMAAx +ADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx +51tkljYyGOylMnfX40S2wBEqgLk9am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qk +R71kMrv2JYSiJ0L1ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaP +T481PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS3a/DTg4f +Jl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5kSeTy36LssUzAKh3ntLFl +osS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF3dvd6qJ2gHN99ZwExEWN57kci57q13XR +crHedUTnQn3iV2t93Jm8PYMo6oCTjcVMZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoR +saS8I8nkvof/uZS2+F0gStRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTD +KCOM/iczQ0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQBjLMi +6Et8Vcad+qMUu2WFbm5PEn4KPJ2V +-----END CERTIFICATE----- + +Izenpe.com +========== +-----BEGIN CERTIFICATE----- +MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4MQswCQYDVQQG +EwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wHhcNMDcxMjEz +MTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMu +QS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ +03rKDx6sp4boFmVqscIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAK +ClaOxdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6HLmYRY2xU ++zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFXuaOKmMPsOzTFlUFpfnXC +PCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQDyCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxT +OTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbK +F7jJeodWLBoBHmy+E60QrLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK +0GqfvEyNBjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8Lhij+ +0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIBQFqNeb+Lz0vPqhbB +leStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+HMh3/1uaD7euBUbl8agW7EekFwID +AQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2luZm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+ +SVpFTlBFIFMuQS4gLSBDSUYgQTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBG +NjIgUzgxQzBBBgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx +MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O +BBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUAA4ICAQB4pgwWSp9MiDrAyw6l +Fn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWblaQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbga +kEyrkgPH7UIBzg/YsfqikuFgba56awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8q +hT/AQKM6WfxZSzwoJNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Cs +g1lwLDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCTVyvehQP5 +aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGkLhObNA5me0mrZJfQRsN5 +nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJbUjWumDqtujWTI6cfSN01RpiyEGjkpTHC +ClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZo +Q0iy2+tzJOeRf1SktoA+naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1Z +WrOZyGlsQyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== +-----END CERTIFICATE----- + +Chambers of Commerce Root - 2008 +================================ +-----BEGIN CERTIFICATE----- +MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYDVQQGEwJFVTFD +MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv +bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu +QS4xKTAnBgNVBAMTIENoYW1iZXJzIG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEy +Mjk1MFoXDTM4MDczMTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNl +ZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQF +EwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJl +cnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW928sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKA +XuFixrYp4YFs8r/lfTJqVKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorj +h40G072QDuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR5gN/ +ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfLZEFHcpOrUMPrCXZk +NNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05aSd+pZgvMPMZ4fKecHePOjlO+Bd5g +D2vlGts/4+EhySnB8esHnFIbAURRPHsl18TlUlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331 +lubKgdaX8ZSD6e2wsWsSaR6s+12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ +0wlf2eOKNcx5Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj +ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAxhduub+84Mxh2 +EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNVHQ4EFgQU+SSsD7K1+HnA+mCI +G8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1+HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJ +BgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNh +bWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENh +bWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDiC +CQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUH +AgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAJASryI1 +wqM58C7e6bXpeHxIvj99RZJe6dqxGfwWPJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH +3qLPaYRgM+gQDROpI9CF5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbU +RWpGqOt1glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaHFoI6 +M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2pSB7+R5KBWIBpih1 +YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MDxvbxrN8y8NmBGuScvfaAFPDRLLmF +9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QGtjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcK +zBIKinmwPQN/aUv0NCB9szTqjktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvG +nrDQWzilm1DefhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg +OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZd0jQ +-----END CERTIFICATE----- + +Global Chambersign Root - 2008 +============================== +-----BEGIN CERTIFICATE----- +MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYDVQQGEwJFVTFD +MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv +bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu +QS4xJzAlBgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMx +NDBaFw0zODA3MzExMjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUg +Y3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJ +QTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD +aGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDf +VtPkOpt2RbQT2//BthmLN0EYlVJH6xedKYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXf +XjaOcNFccUMd2drvXNL7G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0 +ZJJ0YPP2zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4ddPB +/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyGHoiMvvKRhI9lNNgA +TH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2Id3UwD2ln58fQ1DJu7xsepeY7s2M +H/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3VyJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfe +Ox2YItaswTXbo6Al/3K1dh3ebeksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSF +HTynyQbehP9r6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh +wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsogzCtLkykPAgMB +AAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQWBBS5CcqcHtvTbDprru1U8VuT +BjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDprru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UE +BhMCRVUxQzBBBgNVBAcTOk1hZHJpZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJm +aXJtYS5jb20vYWRkcmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJm +aXJtYSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiCCQDJzdPp +1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0 +dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAICIf3DekijZBZRG +/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZUohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6 +ReAJ3spED8IXDneRRXozX1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/s +dZ7LoR/xfxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVza2Mg +9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yydYhz2rXzdpjEetrHH +foUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMdSqlapskD7+3056huirRXhOukP9Du +qqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9OAP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETr +P3iZ8ntxPjzxmKfFGBI/5rsoM0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVq +c5iJWzouE4gev8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z +09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B +-----END CERTIFICATE----- + +Go Daddy Root Certificate Authority - G2 +======================================== +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHkuY29tLCBJbmMu +MTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 +MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8G +A1UEAxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKDE6bFIEMBO4Tx5oVJnyfq +9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD ++qK+ihVqf94Lw7YZFAXK6sOoBJQ7RnwyDfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutd +fMh8+7ArU6SSYmlRJQVhGkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMl +NAJWJwGRtDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDqahQcQZyi27/a9 +BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmXWWcDYfF+OwYxdS2hII5PZYe096ac +vNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r +5N9ss4UXnT3ZJE95kTXWXwTrgIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYV +N8Gb5DKj7Tjo2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO +LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI4uJEvlz36hz1 +-----END CERTIFICATE----- + +Starfield Root Certificate Authority - G2 +========================================= +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s +b2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVsZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0 +eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAw +DgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQg +VGVjaG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZpY2F0ZSBB +dXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3twQP89o/8ArFv +W59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMgnLRJdzIpVv257IzdIvpy3Cdhl+72WoTs +bhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNk +N3mSwOxGXn/hbVNMYq/NHwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7Nf +ZTD4p7dNdloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0HZbU +JtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0GCSqGSIb3DQEBCwUAA4IBAQARWfol +TwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjUsHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx +4mcujJUDJi5DnUox9g61DLu34jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUw +F5okxBDgBPfg8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1mMpYjn0q7pBZ +c2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- + +Starfield Services Root Certificate Authority - G2 +================================================== +-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s +b2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRl +IEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNV +BAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxT +dGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2VydmljZXMg +Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20pOsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2 +h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm28xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4Pa +hHQUw2eeBGg6345AWh1KTs9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLP +LJGmpufehRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk6mFB +rMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMA0GCSqG +SIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMIbw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPP +E95Dz+I0swSdHynVv/heyNXBve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTy +xQGjhdByPq1zqwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd +iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn0q23KXB56jza +YyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCNsSi6 +-----END CERTIFICATE----- + +AffirmTrust Commercial +====================== +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMB4XDTEw +MDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly +bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6Eqdb +DuKPHx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yrba0F8PrV +C8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPALMeIrJmqbTFeurCA+ukV6 +BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1yHp52UKqK39c/s4mT6NmgTWvRLpUHhww +MmWd5jyTXlBOeuM61G7MGvv50jeuJCqrVwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNV +HQ4EFgQUnZPGU4teyq8/nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYGXUPG +hi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNjvbz4YYCanrHOQnDi +qX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivtZ8SOyUOyXGsViQK8YvxO8rUzqrJv +0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9gN53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0kh +sUlHRUe072o0EclNmsxZt9YCnlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= +-----END CERTIFICATE----- + +AffirmTrust Networking +====================== +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMB4XDTEw +MDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly +bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SE +Hi3yYJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbuakCNrmreI +dIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRLQESxG9fhwoXA3hA/Pe24 +/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gb +h+0t+nvujArjqWaJGctB+d1ENmHP4ndGyH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNV +HQ4EFgQUBx/S55zawm6iQLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfOtDIu +UFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzuQY0x2+c06lkh1QF6 +12S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZLgo/bNjR9eUJtGxUAArgFU2HdW23 +WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4uolu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9 +/ZFvgrG+CJPbFEfxojfHRZ48x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= +-----END CERTIFICATE----- + +AffirmTrust Premium +=================== +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMB4XDTEwMDEy +OTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRy +dXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAxBLfqV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtn +BKAQJG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ+jjeRFcV +5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrSs8PhaJyJ+HoAVt70VZVs ++7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmd +GPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d770O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5R +p9EixAqnOEhss/n/fauGV+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NI +S+LI+H+SqHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S5u04 +6uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4IaC1nEWTJ3s7xgaVY5 +/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TXOwF0lkLgAOIua+rF7nKsu7/+6qqo ++Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYEFJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByv +MiPIs0laUZx2KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg +Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B8OWycvpEgjNC +6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQMKSOyARiqcTtNd56l+0OOF6S +L5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK ++4w1IX2COPKpVJEZNZOUbWo6xbLQu4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmV +BtWVyuEklut89pMFu+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFg +IxpHYoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8GKa1qF60 +g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaORtGdFNrHF+QFlozEJLUb +zxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6eKeC2uAloGRwYQw== +-----END CERTIFICATE----- + +AffirmTrust Premium ECC +======================= +-----BEGIN CERTIFICATE----- +MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMCVVMxFDASBgNV +BAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQcmVtaXVtIEVDQzAeFw0xMDAx +MjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1U +cnVzdDEgMB4GA1UEAwwXQWZmaXJtVHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAQNMF4bFZ0D0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQ +N8O9ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0GA1UdDgQW +BBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAK +BggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/VsaobgxCd05DhT1wV/GzTjxi+zygk8N53X +57hG8f2h4nECMEJZh0PUUd+60wkyWs6Iflc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKM +eQ== +-----END CERTIFICATE----- + +Certum Trusted Network CA +========================= +-----BEGIN CERTIFICATE----- +MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBMMSIwIAYDVQQK +ExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIy +MTIwNzM3WhcNMjkxMjMxMTIwNzM3WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBU +ZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MSIwIAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rHUV+rpDKmYYe2bg+G0jAC +l/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LMTXPb865Px1bVWqeWifrzq2jUI4ZZJ88J +J7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVUBBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4 +fOQtf/WsX+sWn7Et0brMkUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0 +cvW0QM8xAcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNVHRMB +Af8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNVHQ8BAf8EBAMCAQYw +DQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15ysHhE49wcrwn9I0j6vSrEuVUEtRCj +jSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfLI9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1 +mS1FhIrlQgnXdAIv94nYmem8J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5aj +Zt3hrvJBW8qYVoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI +03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= +-----END CERTIFICATE----- + +Certinomis - Autorité Racine +============================= +-----BEGIN CERTIFICATE----- +MIIFnDCCA4SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJGUjETMBEGA1UEChMK +Q2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxJjAkBgNVBAMMHUNlcnRpbm9taXMg +LSBBdXRvcml0w6kgUmFjaW5lMB4XDTA4MDkxNzA4Mjg1OVoXDTI4MDkxNzA4Mjg1OVowYzELMAkG +A1UEBhMCRlIxEzARBgNVBAoTCkNlcnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMSYw +JAYDVQQDDB1DZXJ0aW5vbWlzIC0gQXV0b3JpdMOpIFJhY2luZTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAJ2Fn4bT46/HsmtuM+Cet0I0VZ35gb5j2CN2DpdUzZlMGvE5x4jYF1AMnmHa +wE5V3udauHpOd4cN5bjr+p5eex7Ezyh0x5P1FMYiKAT5kcOrJ3NqDi5N8y4oH3DfVS9O7cdxbwly +Lu3VMpfQ8Vh30WC8Tl7bmoT2R2FFK/ZQpn9qcSdIhDWerP5pqZ56XjUl+rSnSTV3lqc2W+HN3yNw +2F1MpQiD8aYkOBOo7C+ooWfHpi2GR+6K/OybDnT0K0kCe5B1jPyZOQE51kqJ5Z52qz6WKDgmi92N +jMD2AR5vpTESOH2VwnHu7XSu5DaiQ3XV8QCb4uTXzEIDS3h65X27uK4uIJPT5GHfceF2Z5c/tt9q +c1pkIuVC28+BA5PY9OMQ4HL2AHCs8MF6DwV/zzRpRbWT5BnbUhYjBYkOjUjkJW+zeL9i9Qf6lSTC +lrLooyPCXQP8w9PlfMl1I9f09bze5N/NgL+RiH2nE7Q5uiy6vdFrzPOlKO1Enn1So2+WLhl+HPNb +xxaOu2B9d2ZHVIIAEWBsMsGoOBvrbpgT1u449fCfDu/+MYHB0iSVL1N6aaLwD4ZFjliCK0wi1F6g +530mJ0jfJUaNSih8hp75mxpZuWW/Bd22Ql095gBIgl4g9xGC3srYn+Y3RyYe63j3YcNBZFgCQfna +4NH4+ej9Uji29YnfAgMBAAGjWzBZMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBQNjLZh2kS40RR9w759XkjwzspqsDAXBgNVHSAEEDAOMAwGCiqBegFWAgIAAQEwDQYJ +KoZIhvcNAQEFBQADggIBACQ+YAZ+He86PtvqrxyaLAEL9MW12Ukx9F1BjYkMTv9sov3/4gbIOZ/x +WqndIlgVqIrTseYyCYIDbNc/CMf4uboAbbnW/FIyXaR/pDGUu7ZMOH8oMDX/nyNTt7buFHAAQCva +R6s0fl6nVjBhK4tDrP22iCj1a7Y+YEq6QpA0Z43q619FVDsXrIvkxmUP7tCMXWY5zjKn2BCXwH40 +nJ+U8/aGH88bc62UeYdocMMzpXDn2NU4lG9jeeu/Cg4I58UvD0KgKxRA/yHgBcUn4YQRE7rWhh1B +CxMjidPJC+iKunqjo3M3NYB9Ergzd0A4wPpeMNLytqOx1qKVl4GbUu1pTP+A5FPbVFsDbVRfsbjv +JL1vnxHDx2TCDyhihWZeGnuyt++uNckZM6i4J9szVb9o4XVIRFb7zdNIu0eJOqxp9YDG5ERQL1TE +qkPFMTFYvZbF6nVsmnWxTfj3l/+WFvKXTej28xH5On2KOG4Ey+HTRRWqpdEdnV1j6CTmNhTih60b +WfVEm/vXd3wfAXBioSAaosUaKPQhA+4u2cGA6rnZgtZbdsLLO7XSAPCjDuGtbkD326C00EauFddE +wk01+dIL8hf2rGbVJLJP0RyZwG71fet0BLj5TXcJ17TPBzAJ8bgAVtkXFhYKK4bfjwEZGuW7gmP/ +vgt2Fl43N+bYdJeimUV5 +-----END CERTIFICATE----- + +Root CA Generalitat Valenciana +============================== +-----BEGIN CERTIFICATE----- +MIIGizCCBXOgAwIBAgIEO0XlaDANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJFUzEfMB0GA1UE +ChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290 +IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwHhcNMDEwNzA2MTYyMjQ3WhcNMjEwNzAxMTUyMjQ3 +WjBoMQswCQYDVQQGEwJFUzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UE +CxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGKqtXETcvIorKA3Qdyu0togu8M1JAJke+WmmmO3I2 +F0zo37i7L3bhQEZ0ZQKQUgi0/6iMweDHiVYQOTPvaLRfX9ptI6GJXiKjSgbwJ/BXufjpTjJ3Cj9B +ZPPrZe52/lSqfR0grvPXdMIKX/UIKFIIzFVd0g/bmoGlu6GzwZTNVOAydTGRGmKy3nXiz0+J2ZGQ +D0EbtFpKd71ng+CT516nDOeB0/RSrFOyA8dEJvt55cs0YFAQexvba9dHq198aMpunUEDEO5rmXte +JajCq+TA81yc477OMUxkHl6AovWDfgzWyoxVjr7gvkkHD6MkQXpYHYTqWBLI4bft75PelAgxAgMB +AAGjggM7MIIDNzAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnBraS5n +dmEuZXMwEgYDVR0TAQH/BAgwBgEB/wIBAjCCAjQGA1UdIASCAiswggInMIICIwYKKwYBBAG/VQIB +ADCCAhMwggHoBggrBgEFBQcCAjCCAdoeggHWAEEAdQB0AG8AcgBpAGQAYQBkACAAZABlACAAQwBl +AHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAFIAYQDtAHoAIABkAGUAIABsAGEAIABHAGUAbgBlAHIA +YQBsAGkAdABhAHQAIABWAGEAbABlAG4AYwBpAGEAbgBhAC4ADQAKAEwAYQAgAEQAZQBjAGwAYQBy +AGEAYwBpAPMAbgAgAGQAZQAgAFAAcgDhAGMAdABpAGMAYQBzACAAZABlACAAQwBlAHIAdABpAGYA +aQBjAGEAYwBpAPMAbgAgAHEAdQBlACAAcgBpAGcAZQAgAGUAbAAgAGYAdQBuAGMAaQBvAG4AYQBt +AGkAZQBuAHQAbwAgAGQAZQAgAGwAYQAgAHAAcgBlAHMAZQBuAHQAZQAgAEEAdQB0AG8AcgBpAGQA +YQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAHMAZQAgAGUAbgBjAHUAZQBu +AHQAcgBhACAAZQBuACAAbABhACAAZABpAHIAZQBjAGMAaQDzAG4AIAB3AGUAYgAgAGgAdAB0AHAA +OgAvAC8AdwB3AHcALgBwAGsAaQAuAGcAdgBhAC4AZQBzAC8AYwBwAHMwJQYIKwYBBQUHAgEWGWh0 +dHA6Ly93d3cucGtpLmd2YS5lcy9jcHMwHQYDVR0OBBYEFHs100DSHHgZZu90ECjcPk+yeAT8MIGV +BgNVHSMEgY0wgYqAFHs100DSHHgZZu90ECjcPk+yeAT8oWykajBoMQswCQYDVQQGEwJFUzEfMB0G +A1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScwJQYDVQQDEx5S +b290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmGCBDtF5WgwDQYJKoZIhvcNAQEFBQADggEBACRh +TvW1yEICKrNcda3FbcrnlD+laJWIwVTAEGmiEi8YPyVQqHxK6sYJ2fR1xkDar1CdPaUWu20xxsdz +Ckj+IHLtb8zog2EWRpABlUt9jppSCS/2bxzkoXHPjCpaF3ODR00PNvsETUlR4hTJZGH71BTg9J63 +NI8KJr2XXPR5OkowGcytT6CYirQxlyric21+eLj4iIlPsSKRZEv1UN4D2+XFducTZnV+ZfsBn5OH +iJ35Rld8TWCvmHMTI6QgkYH60GFmuH3Rr9ZvHmw96RH9qfmCIoaZM3Fa6hlXPZHNqcCjbgcTpsnt ++GijnsNacgmHKNHEc8RzGF9QdRYxn7fofMM= +-----END CERTIFICATE----- + +A-Trust-nQual-03 +================ +-----BEGIN CERTIFICATE----- +MIIDzzCCAregAwIBAgIDAWweMA0GCSqGSIb3DQEBBQUAMIGNMQswCQYDVQQGEwJBVDFIMEYGA1UE +Cgw/QS1UcnVzdCBHZXMuIGYuIFNpY2hlcmhlaXRzc3lzdGVtZSBpbSBlbGVrdHIuIERhdGVudmVy +a2VociBHbWJIMRkwFwYDVQQLDBBBLVRydXN0LW5RdWFsLTAzMRkwFwYDVQQDDBBBLVRydXN0LW5R +dWFsLTAzMB4XDTA1MDgxNzIyMDAwMFoXDTE1MDgxNzIyMDAwMFowgY0xCzAJBgNVBAYTAkFUMUgw +RgYDVQQKDD9BLVRydXN0IEdlcy4gZi4gU2ljaGVyaGVpdHNzeXN0ZW1lIGltIGVsZWt0ci4gRGF0 +ZW52ZXJrZWhyIEdtYkgxGTAXBgNVBAsMEEEtVHJ1c3QtblF1YWwtMDMxGTAXBgNVBAMMEEEtVHJ1 +c3QtblF1YWwtMDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtPWFuA/OQO8BBC4SA +zewqo51ru27CQoT3URThoKgtUaNR8t4j8DRE/5TrzAUjlUC5B3ilJfYKvUWG6Nm9wASOhURh73+n +yfrBJcyFLGM/BWBzSQXgYHiVEEvc+RFZznF/QJuKqiTfC0Li21a8StKlDJu3Qz7dg9MmEALP6iPE +SU7l0+m0iKsMrmKS1GWH2WrX9IWf5DMiJaXlyDO6w8dB3F/GaswADm0yqLaHNgBid5seHzTLkDx4 +iHQF63n1k3Flyp3HaxgtPVxO59X4PzF9j4fsCiIvI+n+u33J4PTs63zEsMMtYrWacdaxaujs2e3V +cuy+VwHOBVWf3tFgiBCzAgMBAAGjNjA0MA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0OBAoECERqlWdV +eRFPMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAVdRU0VlIXLOThaq/Yy/kgM40 +ozRiPvbY7meIMQQDbwvUB/tOdQ/TLtPAF8fGKOwGDREkDg6lXb+MshOWcdzUzg4NCmgybLlBMRmr +sQd7TZjTXLDR8KdCoLXEjq/+8T/0709GAHbrAvv5ndJAlseIOrifEXnzgGWovR/TeIGgUUw3tKZd +JXDRZslo+S4RFGjxVJgIrCaSD96JntT6s3kr0qN51OyLrIdTaEJMUVF0HhsnLuP1Hyl0Te2v9+GS +mYHovjrHF1D2t8b8m7CKa9aIA5GPBnc6hQLdmNVDeD/GMBWsm2vLV7eJUYs66MmEDNuxUCAKGkq6 +ahq97BvIxYSazQ== +-----END CERTIFICATE----- + +TWCA Root Certification Authority +================================= +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJ +VEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMzWhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQG +EwJUVzESMBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NB +IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFEAcK0HMMx +QhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HHK3XLfJ+utdGdIzdjp9xC +oi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeXRfwZVzsrb+RH9JlF/h3x+JejiB03HFyP +4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/zrX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1r +y+UPizgN7gr8/g+YnzAx3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIB +BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkqhkiG +9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeCMErJk/9q56YAf4lC +mtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdlsXebQ79NqZp4VKIV66IIArB6nCWlW +QtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62Dlhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVY +T0bf+215WfKEIlKuD8z7fDvnaspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocny +Yh0igzyXxfkZYiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== +-----END CERTIFICATE----- + +Security Communication RootCA2 +============================== +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMc +U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMeU2VjdXJpdHkgQ29tbXVuaWNh +dGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoXDTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMC +SlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3Vy +aXR5IENvbW11bmljYXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ANAVOVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGrzbl+dp++ ++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVMVAX3NuRFg3sUZdbcDE3R +3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQhNBqyjoGADdH5H5XTz+L62e4iKrFvlNV +spHEfbmwhRkGeC7bYRr6hfVKkaHnFtWOojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1K +EOtOghY6rCcMU/Gt1SSwawNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8 +QIH4D5csOPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB +CwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpFcoJxDjrSzG+ntKEj +u/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXcokgfGT+Ok+vx+hfuzU7jBBJV1uXk +3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6q +tnRGEmyR7jTV7JqR50S+kDFy1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29 +mvVXIwAHIRc/SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 +-----END CERTIFICATE----- + +EC-ACC +====== +-----BEGIN CERTIFICATE----- +MIIFVjCCBD6gAwIBAgIQ7is969Qh3hSoYqwE893EATANBgkqhkiG9w0BAQUFADCB8zELMAkGA1UE +BhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2VydGlmaWNhY2lvIChOSUYgUS0w +ODAxMTc2LUkpMSgwJgYDVQQLEx9TZXJ2ZWlzIFB1YmxpY3MgZGUgQ2VydGlmaWNhY2lvMTUwMwYD +VQQLEyxWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAoYykwMzE1MDMGA1UE +CxMsSmVyYXJxdWlhIEVudGl0YXRzIGRlIENlcnRpZmljYWNpbyBDYXRhbGFuZXMxDzANBgNVBAMT +BkVDLUFDQzAeFw0wMzAxMDcyMzAwMDBaFw0zMTAxMDcyMjU5NTlaMIHzMQswCQYDVQQGEwJFUzE7 +MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYt +SSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZl +Z2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJh +cnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUND +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyLHT+KXQpWIR4NA9h0X84NzJB5R85iK +w5K4/0CQBXCHYMkAqbWUZRkiFRfCQ2xmRJoNBD45b6VLeqpjt4pEndljkYRm4CgPukLjbo73FCeT +ae6RDqNfDrHrZqJyTxIThmV6PttPB/SnCWDaOkKZx7J/sxaVHMf5NLWUhdWZXqBIoH7nF2W4onW4 +HvPlQn2v7fOKSGRdghST2MDk/7NQcvJ29rNdQlB50JQ+awwAvthrDk4q7D7SzIKiGGUzE3eeml0a +E9jD2z3Il3rucO2n5nzbcc8tlGLfbdb1OL4/pYUKGbio2Al1QnDE6u/LDsg0qBIimAy4E5S2S+zw +0JDnJwIDAQABo4HjMIHgMB0GA1UdEQQWMBSBEmVjX2FjY0BjYXRjZXJ0Lm5ldDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUoMOLRKo3pUW/l4Ba0fF4opvpXY0wfwYD +VR0gBHgwdjB0BgsrBgEEAfV4AQMBCjBlMCwGCCsGAQUFBwIBFiBodHRwczovL3d3dy5jYXRjZXJ0 +Lm5ldC92ZXJhcnJlbDA1BggrBgEFBQcCAjApGidWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5l +dC92ZXJhcnJlbCAwDQYJKoZIhvcNAQEFBQADggEBAKBIW4IB9k1IuDlVNZyAelOZ1Vr/sXE7zDkJ +lF7W2u++AVtd0x7Y/X1PzaBB4DSTv8vihpw3kpBWHNzrKQXlxJ7HNd+KDM3FIUPpqojlNcAZQmNa +Al6kSBg6hW/cnbw/nZzBh7h6YQjpdwt/cKt63dmXLGQehb+8dJahw3oS7AwaboMMPOhyRp/7SNVe +l+axofjk70YllJyJ22k4vuxcDlbHZVHlUIiIv0LVKz3l+bqeLrPK9HOSAgu+TGbrIP65y7WZf+a2 +E/rKS03Z7lNGBjvGTq2TWoF+bCpLagVFjPIhpDGQh2xlnJ2lYJU6Un/10asIbvPuW/mIPX64b24D +5EI= +-----END CERTIFICATE----- + +Hellenic Academic and Research Institutions RootCA 2011 +======================================================= +-----BEGIN CERTIFICATE----- +MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1IxRDBCBgNVBAoT +O0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9y +aXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z +IFJvb3RDQSAyMDExMB4XDTExMTIwNjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYT +AkdSMUQwQgYDVQQKEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z +IENlcnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNo +IEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPzdYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI +1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJfel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa +71HFK9+WXesyHgLacEnsbgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u +8yBRQlqD75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSPFEDH +3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNVHRMBAf8EBTADAQH/ +MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp5dgTBCPuQSUwRwYDVR0eBEAwPqA8 +MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQub3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQu +b3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVt +XdMiKahsog2p6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8 +TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7dIsXRSZMFpGD +/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8AcysNnq/onN694/BtZqhFLKPM58N +7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXIl7WdmplNsDz4SgCbZN2fOUvRJ9e4 +-----END CERTIFICATE----- + +Actalis Authentication Root CA +============================== +-----BEGIN CERTIFICATE----- +MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCSVQxDjAM +BgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UE +AwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDky +MjExMjIwMlowazELMAkGA1UEBhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlz +IFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 +IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNvUTufClrJ +wkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX4ay8IMKx4INRimlNAJZa +by/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9KK3giq0itFZljoZUj5NDKd45RnijMCO6 +zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1f +YVEiVRvjRuPjPdA1YprbrxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2 +oxgkg4YQ51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2Fbe8l +EfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxeKF+w6D9Fz8+vm2/7 +hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4Fv6MGn8i1zeQf1xcGDXqVdFUNaBr8 +EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbnfpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5 +jF66CyCU3nuDuP/jVo23Eek7jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLY +iDrIn3hm7YnzezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt +ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQALe3KHwGCmSUyI +WOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70jsNjLiNmsGe+b7bAEzlgqqI0 +JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDzWochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKx +K3JCaKygvU5a2hi/a5iB0P2avl4VSM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+ +Xlff1ANATIGk0k9jpwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC +4yyXX04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+OkfcvHlXHo +2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7RK4X9p2jIugErsWx0Hbhz +lefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btUZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXem +OR/qnuOf0GZvBeyqdn6/axag67XH/JJULysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9 +vwGYT7JZVEc+NHt4bVaTLnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== +-----END CERTIFICATE----- + +Trustis FPS Root CA +=================== +-----BEGIN CERTIFICATE----- +MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQG +EwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQLExNUcnVzdGlzIEZQUyBSb290 +IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTExMzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNV +BAoTD1RydXN0aXMgTGltaXRlZDEcMBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQ +RUN+AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihHiTHcDnlk +H5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjjvSkCqPoc4Vu5g6hBSLwa +cY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zt +o3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlBOrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEA +AaNTMFEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAd +BgNVHQ4EFgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01GX2c +GE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmWzaD+vkAMXBJV+JOC +yinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP41BIy+Q7DsdwyhEQsb8tGD+pmQQ9P +8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZEf1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHV +l/9D7S3B2l0pKoU/rGXuhg8FjZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYl +iB6XzCGcKQENZetX2fNXlrtIzYE= +-----END CERTIFICATE----- + +StartCom Certification Authority +================================ +-----BEGIN CERTIFICATE----- +MIIHhzCCBW+gAwIBAgIBLTANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMN +U3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmlu +ZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0 +NjM3WhcNMzYwOTE3MTk0NjM2WjB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRk +LjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMg +U3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw +ggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZkpMyONvg45iPwbm2xPN1y +o4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rfOQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/ +Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/CJi/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/d +eMotHweXMAEtcnn6RtYTKqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt +2PZE4XNiHzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMMAv+Z +6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w+2OqqGwaVLRcJXrJ +osmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/ +untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVc +UjyJthkqcwEKDwOzEmDyei+B26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT +37uMdBNSSwIDAQABo4ICEDCCAgwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0OBBYEFE4L7xqkQFulF2mHMMo0aEPQQa7yMB8GA1UdIwQYMBaAFE4L7xqkQFulF2mHMMo0aEPQ +Qa7yMIIBWgYDVR0gBIIBUTCCAU0wggFJBgsrBgEEAYG1NwEBATCCATgwLgYIKwYBBQUHAgEWImh0 +dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cu +c3RhcnRzc2wuY29tL2ludGVybWVkaWF0ZS5wZGYwgc8GCCsGAQUFBwICMIHCMCcWIFN0YXJ0IENv +bW1lcmNpYWwgKFN0YXJ0Q29tKSBMdGQuMAMCAQEagZZMaW1pdGVkIExpYWJpbGl0eSwgcmVhZCB0 +aGUgc2VjdGlvbiAqTGVnYWwgTGltaXRhdGlvbnMqIG9mIHRoZSBTdGFydENvbSBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eSBQb2xpY3kgYXZhaWxhYmxlIGF0IGh0dHA6Ly93d3cuc3RhcnRzc2wuY29t +L3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBG +cmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAgEAjo/n3JR5 +fPGFf59Jb2vKXfuM/gTFwWLRfUKKvFO3lANmMD+x5wqnUCBVJX92ehQN6wQOQOY+2IirByeDqXWm +N3PH/UvSTa0XQMhGvjt/UfzDtgUx3M2FIk5xt/JxXrAaxrqTi3iSSoX4eA+D/i+tLPfkpLst0OcN +Org+zvZ49q5HJMqjNTbOx8aHmNrs++myziebiMMEofYLWWivydsQD032ZGNcpRJvkrKTlMeIFw6T +tn5ii5B/q06f/ON1FE8qMt9bDeD1e5MNq6HPh+GlBEXoPBKlCcWw0bdT82AUuoVpaiF8H3VhFyAX +e2w7QSlc4axa0c2Mm+tgHRns9+Ww2vl5GKVFP0lDV9LdJNUso/2RjSe15esUBppMeyG7Oq0wBhjA +2MFrLH9ZXF2RsXAiV+uKa0hK1Q8p7MZAwC+ITGgBF3f0JBlPvfrhsiAhS90a2Cl9qrjeVOwhVYBs +HvUwyKMQ5bLmKhQxw4UtjJixhlpPiVktucf3HMiKf8CdBUrmQk9io20ppB+Fq9vlgcitKj1MXVuE +JnHEhV5xJMqlG2zYYdMa4FTbzrqpMrUi9nNBCV24F10OD5mQ1kfabwo6YigUZ4LZ8dCAWZvLMdib +D4x3TrVoivJs9iQOLWxwxXPR3hTQcY+203sC9uO41Alua551hDnmfyWl8kgAwKQB2j8= +-----END CERTIFICATE----- + +StartCom Certification Authority G2 +=================================== +-----BEGIN CERTIFICATE----- +MIIFYzCCA0ugAwIBAgIBOzANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJJTDEWMBQGA1UEChMN +U3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +RzIwHhcNMTAwMTAxMDEwMDAxWhcNMzkxMjMxMjM1OTAxWjBTMQswCQYDVQQGEwJJTDEWMBQGA1UE +ChMNU3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkgRzIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2iTZbB7cgNr2Cu+EWIAOVeq8O +o1XJJZlKxdBWQYeQTSFgpBSHO839sj60ZwNq7eEPS8CRhXBF4EKe3ikj1AENoBB5uNsDvfOpL9HG +4A/LnooUCri99lZi8cVytjIl2bLzvWXFDSxu1ZJvGIsAQRSCb0AgJnooD/Uefyf3lLE3PbfHkffi +Aez9lInhzG7TNtYKGXmu1zSCZf98Qru23QumNK9LYP5/Q0kGi4xDuFby2X8hQxfqp0iVAXV16iul +Q5XqFYSdCI0mblWbq9zSOdIxHWDirMxWRST1HFSr7obdljKF+ExP6JV2tgXdNiNnvP8V4so75qbs +O+wmETRIjfaAKxojAuuKHDp2KntWFhxyKrOq42ClAJ8Em+JvHhRYW6Vsi1g8w7pOOlz34ZYrPu8H +vKTlXcxNnw3h3Kq74W4a7I/htkxNeXJdFzULHdfBR9qWJODQcqhaX2YtENwvKhOuJv4KHBnM0D4L +nMgJLvlblnpHnOl68wVQdJVznjAJ85eCXuaPOQgeWeU1FEIT/wCc976qUM/iUUjXuG+v+E5+M5iS +FGI6dWPPe/regjupuznixL0sAA7IF6wT700ljtizkC+p2il9Ha90OrInwMEePnWjFqmveiJdnxMa +z6eg6+OGCtP95paV1yPIN93EfKo2rJgaErHgTuixO/XWb/Ew1wIDAQABo0IwQDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUS8W0QGutHLOlHGVuRjaJhwUMDrYwDQYJ +KoZIhvcNAQELBQADggIBAHNXPyzVlTJ+N9uWkusZXn5T50HsEbZH77Xe7XRcxfGOSeD8bpkTzZ+K +2s06Ctg6Wgk/XzTQLwPSZh0avZyQN8gMjgdalEVGKua+etqhqaRpEpKwfTbURIfXUfEpY9Z1zRbk +J4kd+MIySP3bmdCPX1R0zKxnNBFi2QwKN4fRoxdIjtIXHfbX/dtl6/2o1PXWT6RbdejF0mCy2wl+ +JYt7ulKSnj7oxXehPOBKc2thz4bcQ///If4jXSRK9dNtD2IEBVeC2m6kMyV5Sy5UGYvMLD0w6dEG +/+gyRr61M3Z3qAFdlsHB1b6uJcDJHgoJIIihDsnzb02CVAAgp9KP5DlUFy6NHrgbuxu9mk47EDTc +nIhT76IxW1hPkWLIwpqazRVdOKnWvvgTtZ8SafJQYqz7Fzf07rh1Z2AQ+4NQ+US1dZxAF7L+/Xld +blhYXzD8AK6vM8EOTmy6p6ahfzLbOOCxchcKK5HsamMm7YnUeMx0HgX4a/6ManY5Ka5lIxKVCCIc +l85bBu4M4ru8H0ST9tg4RQUh7eStqxK2A6RCLi3ECToDZ2mEmuFZkIoohdVddLHRDiBYmxOlsGOm +7XtH/UVVMKTumtTm4ofvmMkyghEpIrwACjFeLQ/Ajulrso8uBtjRkcfGEvRM/TAXw8HaOFvjqerm +obp573PYtlNXLfbQ4ddI +-----END CERTIFICATE----- + +Buypass Class 2 Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMiBSb290IENBMB4X +DTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1owTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 +eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1 +g1Lr6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPVL4O2fuPn +9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC911K2GScuVr1QGbNgGE41b +/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHxMlAQTn/0hpPshNOOvEu/XAFOBz3cFIqU +CqTqc/sLUegTBxj6DvEr0VQVfTzh97QZQmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeff +awrbD02TTqigzXsu8lkBarcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgI +zRFo1clrUs3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLiFRhn +Bkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRSP/TizPJhk9H9Z2vX +Uq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN9SG9dKpN6nIDSdvHXx1iY8f93ZHs +M+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxPAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFMmAd+BikoL1RpzzuvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF +AAOCAgEAU18h9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s +A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3tOluwlN5E40EI +osHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo+fsicdl9sz1Gv7SEr5AcD48S +aq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYd +DnkM/crqJIByw5c/8nerQyIKx+u2DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWD +LfJ6v9r9jv6ly0UsH8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0 +oyLQI+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK75t98biGC +wWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h3PFaTWwyI0PurKju7koS +CTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPzY11aWOIv4x3kqdbQCtCev9eBCfHJxyYN +rJgWVqA= +-----END CERTIFICATE----- + +Buypass Class 3 Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMyBSb290IENBMB4X +DTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFowTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 +eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRH +sJ8YZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3EN3coTRiR +5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9tznDDgFHmV0ST9tD+leh +7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX0DJq1l1sDPGzbjniazEuOQAnFN44wOwZ +ZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH +2xc519woe2v1n/MuwU8XKhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV +/afmiSTYzIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvSO1UQ +RwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D34xFMFbG02SrZvPA +Xpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgPK9Dx2hzLabjKSWJtyNBjYt1gD1iq +j6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFEe4zf/lb+74suwvTg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF +AAOCAgEAACAjQTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV +cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXSIGrs/CIBKM+G +uIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2HJLw5QY33KbmkJs4j1xrG0aG +Q0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsaO5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8 +ZORK15FTAaggiG6cX0S5y2CBNOxv033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2 +KSb12tjE8nVhz36udmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz +6MkEkbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg413OEMXbug +UZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvDu79leNKGef9JOxqDDPDe +eOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq4/g7u9xN12TyUb7mqqta6THuBrxzvxNi +Cp/HuZc= +-----END CERTIFICATE----- + +T-TeleSec GlobalRoot Class 3 +============================ +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM +IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU +cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgx +MDAxMTAyOTU2WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz +dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD +ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN8ELg63iIVl6bmlQdTQyK +9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/RLyTPWGrTs0NvvAgJ1gORH8EGoel15YU +NpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZF +iP0Zf3WHHx+xGwpzJFu5ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W +0eDrXltMEnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1A/d2O2GCahKqGFPr +AyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOyWL6ukK2YJ5f+AbGwUgC4TeQbIXQb +fsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzT +ucpH9sry9uetuUg/vBa3wW306gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7h +P0HHRwA11fXT91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml +e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4pTpPDpFQUWw== +-----END CERTIFICATE----- + +TURKTRUST Certificate Services Provider Root 2007 +================================================= +-----BEGIN CERTIFICATE----- +MIIEPTCCAyWgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvzE/MD0GA1UEAww2VMOcUktUUlVTVCBF +bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEP +MA0GA1UEBwwGQW5rYXJhMV4wXAYDVQQKDFVUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUg +QmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgQXJhbMSxayAyMDA3MB4X +DTA3MTIyNTE4MzcxOVoXDTE3MTIyMjE4MzcxOVowgb8xPzA9BgNVBAMMNlTDnFJLVFJVU1QgRWxl +a3Ryb25payBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTELMAkGA1UEBhMCVFIxDzAN +BgNVBAcMBkFua2FyYTFeMFwGA1UECgxVVMOcUktUUlVTVCBCaWxnaSDEsGxldGnFn2ltIHZlIEJp +bGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkgQS7Fni4gKGMpIEFyYWzEsWsgMjAwNzCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKu3PgqMyKVYFeaK7yc9SrToJdPNM8Ig3BnuiD9N +YvDdE3ePYakqtdTyuTFYKTsvP2qcb3N2Je40IIDu6rfwxArNK4aUyeNgsURSsloptJGXg9i3phQv +KUmi8wUG+7RP2qFsmmaf8EMJyupyj+sA1zU511YXRxcw9L6/P8JorzZAwan0qafoEGsIiveGHtya +KhUG9qPw9ODHFNRRf8+0222vR5YXm3dx2KdxnSQM9pQ/hTEST7ruToK4uT6PIzdezKKqdfcYbwnT +rqdUKDT74eA7YH2gvnmJhsifLfkKS8RQouf9eRbHegsYz85M733WB2+Y8a+xwXrXgTW4qhe04MsC +AwEAAaNCMEAwHQYDVR0OBBYEFCnFkKslrxHkYb+j/4hhkeYO/pyBMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAQDdr4Ouwo0RSVgrESLFF6QSU2TJ/s +Px+EnWVUXKgWAkD6bho3hO9ynYYKVZ1WKKxmLNA6VpM0ByWtCLCPyA8JWcqdmBzlVPi5RX9ql2+I +aE1KBiY3iAIOtsbWcpnOa3faYjGkVh+uX4132l32iPwa2Z61gfAyuOOI0JzzaqC5mxRZNTZPz/OO +Xl0XrRWV2N2y1RVuAE6zS89mlOTgzbUF2mNXi+WzqtvALhyQRNsaXRik7r4EW5nVcV9VZWRi1aKb +BFmGyGJ353yCRWo9F7/snXUMrqNvWtMvmDb08PUZqxFdyKbjKlhqQgnDvZImZjINXQhVdP+MmNAK +poRq0Tl9 +-----END CERTIFICATE----- + +D-TRUST Root Class 3 CA 2 2009 +============================== +-----BEGIN CERTIFICATE----- +MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTAe +Fw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NThaME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxE +LVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOAD +ER03UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42tSHKXzlA +BF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9RySPocq60vFYJfxLLHLGv +KZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsMlFqVlNpQmvH/pStmMaTJOKDfHR+4CS7z +p+hnUquVH+BGPtikw8paxTGA6Eian5Rp/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUC +AwEAAaOCARowggEWMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ +4PGEMA4GA1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVjdG9y +eS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUyMENBJTIwMiUyMDIw +MDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRlcmV2b2NhdGlvbmxpc3QwQ6BBoD+G +PWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAw +OS5jcmwwDQYJKoZIhvcNAQELBQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm +2H6NMLVwMeniacfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 +o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4KzCUqNQT4YJEV +dT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8PIWmawomDeCTmGCufsYkl4ph +X5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3YJohw1+qRzT65ysCQblrGXnRl11z+o+I= +-----END CERTIFICATE----- + +D-TRUST Root Class 3 CA 2 EV 2009 +================================= +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw +OTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUwNDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw +OTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfS +egpnljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM03TP1YtHh +zRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6ZqQTMFexgaDbtCHu39b+T +7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lRp75mpoo6Kr3HGrHhFPC+Oh25z1uxav60 +sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure35 +11H3a6UCAwEAAaOCASQwggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyv +cop9NteaHNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFwOi8v +ZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xhc3MlMjAzJTIwQ0El +MjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1ERT9jZXJ0aWZpY2F0ZXJldm9jYXRp +b25saXN0MEagRKBChkBodHRwOi8vd3d3LmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xh +c3NfM19jYV8yX2V2XzIwMDkuY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+ +PPoeUSbrh/Yp3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 +nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNFCSuGdXzfX2lX +ANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7naxpeG0ILD5EJt/rDiZE4OJudA +NCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqXKVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVv +w9y4AyHqnxbxLFS1 +-----END CERTIFICATE----- + +PSCProcert +========== +-----BEGIN CERTIFICATE----- +MIIJhjCCB26gAwIBAgIBCzANBgkqhkiG9w0BAQsFADCCAR4xPjA8BgNVBAMTNUF1dG9yaWRhZCBk +ZSBDZXJ0aWZpY2FjaW9uIFJhaXogZGVsIEVzdGFkbyBWZW5lem9sYW5vMQswCQYDVQQGEwJWRTEQ +MA4GA1UEBxMHQ2FyYWNhczEZMBcGA1UECBMQRGlzdHJpdG8gQ2FwaXRhbDE2MDQGA1UEChMtU2lz +dGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMUMwQQYDVQQLEzpTdXBl +cmludGVuZGVuY2lhIGRlIFNlcnZpY2lvcyBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMSUw +IwYJKoZIhvcNAQkBFhZhY3JhaXpAc3VzY2VydGUuZ29iLnZlMB4XDTEwMTIyODE2NTEwMFoXDTIw +MTIyNTIzNTk1OVowgdExJjAkBgkqhkiG9w0BCQEWF2NvbnRhY3RvQHByb2NlcnQubmV0LnZlMQ8w +DQYDVQQHEwZDaGFjYW8xEDAOBgNVBAgTB01pcmFuZGExKjAoBgNVBAsTIVByb3ZlZWRvciBkZSBD +ZXJ0aWZpY2Fkb3MgUFJPQ0VSVDE2MDQGA1UEChMtU2lzdGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZp +Y2FjaW9uIEVsZWN0cm9uaWNhMQswCQYDVQQGEwJWRTETMBEGA1UEAxMKUFNDUHJvY2VydDCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANW39KOUM6FGqVVhSQ2oh3NekS1wwQYalNo97BVC +wfWMrmoX8Yqt/ICV6oNEolt6Vc5Pp6XVurgfoCfAUFM+jbnADrgV3NZs+J74BCXfgI8Qhd19L3uA +3VcAZCP4bsm+lU/hdezgfl6VzbHvvnpC2Mks0+saGiKLt38GieU89RLAu9MLmV+QfI4tL3czkkoh +RqipCKzx9hEC2ZUWno0vluYC3XXCFCpa1sl9JcLB/KpnheLsvtF8PPqv1W7/U0HU9TI4seJfxPmO +EO8GqQKJ/+MMbpfg353bIdD0PghpbNjU5Db4g7ayNo+c7zo3Fn2/omnXO1ty0K+qP1xmk6wKImG2 +0qCZyFSTXai20b1dCl53lKItwIKOvMoDKjSuc/HUtQy9vmebVOvh+qBa7Dh+PsHMosdEMXXqP+UH +0quhJZb25uSgXTcYOWEAM11G1ADEtMo88aKjPvM6/2kwLkDd9p+cJsmWN63nOaK/6mnbVSKVUyqU +td+tFjiBdWbjxywbk5yqjKPK2Ww8F22c3HxT4CAnQzb5EuE8XL1mv6JpIzi4mWCZDlZTOpx+FIyw +Bm/xhnaQr/2v/pDGj59/i5IjnOcVdo/Vi5QTcmn7K2FjiO/mpF7moxdqWEfLcU8UC17IAggmosvp +r2uKGcfLFFb14dq12fy/czja+eevbqQ34gcnAgMBAAGjggMXMIIDEzASBgNVHRMBAf8ECDAGAQH/ +AgEBMDcGA1UdEgQwMC6CD3N1c2NlcnRlLmdvYi52ZaAbBgVghl4CAqASDBBSSUYtRy0yMDAwNDAz +Ni0wMB0GA1UdDgQWBBRBDxk4qpl/Qguk1yeYVKIXTC1RVDCCAVAGA1UdIwSCAUcwggFDgBStuyId +xuDSAaj9dlBSk+2YwU2u06GCASakggEiMIIBHjE+MDwGA1UEAxM1QXV0b3JpZGFkIGRlIENlcnRp +ZmljYWNpb24gUmFpeiBkZWwgRXN0YWRvIFZlbmV6b2xhbm8xCzAJBgNVBAYTAlZFMRAwDgYDVQQH +EwdDYXJhY2FzMRkwFwYDVQQIExBEaXN0cml0byBDYXBpdGFsMTYwNAYDVQQKEy1TaXN0ZW1hIE5h +Y2lvbmFsIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExQzBBBgNVBAsTOlN1cGVyaW50ZW5k +ZW5jaWEgZGUgU2VydmljaW9zIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExJTAjBgkqhkiG +9w0BCQEWFmFjcmFpekBzdXNjZXJ0ZS5nb2IudmWCAQowDgYDVR0PAQH/BAQDAgEGME0GA1UdEQRG +MESCDnByb2NlcnQubmV0LnZloBUGBWCGXgIBoAwMClBTQy0wMDAwMDKgGwYFYIZeAgKgEgwQUklG +LUotMzE2MzUzNzMtNzB2BgNVHR8EbzBtMEagRKBChkBodHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52 +ZS9sY3IvQ0VSVElGSUNBRE8tUkFJWi1TSEEzODRDUkxERVIuY3JsMCOgIaAfhh1sZGFwOi8vYWNy +YWl6LnN1c2NlcnRlLmdvYi52ZTA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9v +Y3NwLnN1c2NlcnRlLmdvYi52ZTBBBgNVHSAEOjA4MDYGBmCGXgMBAjAsMCoGCCsGAQUFBwIBFh5o +dHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52ZS9kcGMwDQYJKoZIhvcNAQELBQADggIBACtZ6yKZu4Sq +T96QxtGGcSOeSwORR3C7wJJg7ODU523G0+1ng3dS1fLld6c2suNUvtm7CpsR72H0xpkzmfWvADmN +g7+mvTV+LFwxNG9s2/NkAZiqlCxB3RWGymspThbASfzXg0gTB1GEMVKIu4YXx2sviiCtxQuPcD4q +uxtxj7mkoP3YldmvWb8lK5jpY5MvYB7Eqvh39YtsL+1+LrVPQA3uvFd359m21D+VJzog1eWuq2w1 +n8GhHVnchIHuTQfiSLaeS5UtQbHh6N5+LwUeaO6/u5BlOsju6rEYNxxik6SgMexxbJHmpHmJWhSn +FFAFTKQAVzAswbVhltw+HoSvOULP5dAssSS830DD7X9jSr3hTxJkhpXzsOfIt+FTvZLm8wyWuevo +5pLtp4EJFAv8lXrPj9Y0TzYS3F7RNHXGRoAvlQSMx4bEqCaJqD8Zm4G7UaRKhqsLEQ+xrmNTbSjq +3TNWOByyrYDT13K9mmyZY+gAu0F2BbdbmRiKw7gSXFbPVgx96OLP7bx0R/vu0xdOIk9W/1DzLuY5 +poLWccret9W6aAjtmcz9opLLabid+Qqkpj5PkygqYWwHJgD/ll9ohri4zspV4KuxPX+Y1zMOWj3Y +eMLEYC/HYvBhkdI4sPaeVdtAgAUSM84dkpvRabP/v/GSCmE1P93+hvS84Bpxs2Km +-----END CERTIFICATE----- + +China Internet Network Information Center EV Certificates Root +============================================================== +-----BEGIN CERTIFICATE----- +MIID9zCCAt+gAwIBAgIESJ8AATANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMCQ04xMjAwBgNV +BAoMKUNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyMUcwRQYDVQQDDD5D +aGluYSBJbnRlcm5ldCBOZXR3b3JrIEluZm9ybWF0aW9uIENlbnRlciBFViBDZXJ0aWZpY2F0ZXMg +Um9vdDAeFw0xMDA4MzEwNzExMjVaFw0zMDA4MzEwNzExMjVaMIGKMQswCQYDVQQGEwJDTjEyMDAG +A1UECgwpQ2hpbmEgSW50ZXJuZXQgTmV0d29yayBJbmZvcm1hdGlvbiBDZW50ZXIxRzBFBgNVBAMM +PkNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyIEVWIENlcnRpZmljYXRl +cyBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm35z7r07eKpkQ0H1UN+U8i6y +jUqORlTSIRLIOTJCBumD1Z9S7eVnAztUwYyZmczpwA//DdmEEbK40ctb3B75aDFk4Zv6dOtouSCV +98YPjUesWgbdYavi7NifFy2cyjw1l1VxzUOFsUcW9SxTgHbP0wBkvUCZ3czY28Sf1hNfQYOL+Q2H +klY0bBoQCxfVWhyXWIQ8hBouXJE0bhlffxdpxWXvayHG1VA6v2G5BY3vbzQ6sm8UY78WO5upKv23 +KzhmBsUs4qpnHkWnjQRmQvaPK++IIGmPMowUc9orhpFjIpryp9vOiYurXccUwVswah+xt54ugQEC +7c+WXmPbqOY4twIDAQABo2MwYTAfBgNVHSMEGDAWgBR8cks5x8DbYqVPm6oYNJKiyoOCWTAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUfHJLOcfA22KlT5uqGDSSosqD +glkwDQYJKoZIhvcNAQEFBQADggEBACrDx0M3j92tpLIM7twUbY8opJhJywyA6vPtI2Z1fcXTIWd5 +0XPFtQO3WKwMVC/GVhMPMdoG52U7HW8228gd+f2ABsqjPWYWqJ1MFn3AlUa1UeTiH9fqBk1jjZaM +7+czV0I664zBechNdn3e9rG3geCg+aF4RhcaVpjwTj2rHO3sOdwHSPdj/gauwqRcalsyiMXHM4Ws +ZkJHwlgkmeHlPuV1LI5D1l08eB6olYIpUNHRFrrvwb562bTYzB5MRuF3sTGrvSrIzo9uoV1/A3U0 +5K2JRVRevq4opbs/eHnrc7MKDf2+yfdWrPa37S+bISnHOLaVxATywy39FCqQmbkHzJ8= +-----END CERTIFICATE----- + +Swisscom Root CA 2 +================== +-----BEGIN CERTIFICATE----- +MIIF2TCCA8GgAwIBAgIQHp4o6Ejy5e/DfEoeWhhntjANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQG +EwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0YWwgQ2VydGlmaWNhdGUgU2Vy +dmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3QgQ0EgMjAeFw0xMTA2MjQwODM4MTRaFw0zMTA2 +MjUwNzM4MTRaMGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGln +aXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAyMIIC +IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAlUJOhJ1R5tMJ6HJaI2nbeHCOFvErjw0DzpPM +LgAIe6szjPTpQOYXTKueuEcUMncy3SgM3hhLX3af+Dk7/E6J2HzFZ++r0rk0X2s682Q2zsKwzxNo +ysjL67XiPS4h3+os1OD5cJZM/2pYmLcX5BtS5X4HAB1f2uY+lQS3aYg5oUFgJWFLlTloYhyxCwWJ +wDaCFCE/rtuh/bxvHGCGtlOUSbkrRsVPACu/obvLP+DHVxxX6NZp+MEkUp2IVd3Chy50I9AU/SpH +Wrumnf2U5NGKpV+GY3aFy6//SSj8gO1MedK75MDvAe5QQQg1I3ArqRa0jG6F6bYRzzHdUyYb3y1a +SgJA/MTAtukxGggo5WDDH8SQjhBiYEQN7Aq+VRhxLKX0srwVYv8c474d2h5Xszx+zYIdkeNL6yxS +NLCK/RJOlrDrcH+eOfdmQrGrrFLadkBXeyq96G4DsguAhYidDMfCd7Camlf0uPoTXGiTOmekl9Ab +mbeGMktg2M7v0Ax/lZ9vh0+Hio5fCHyqW/xavqGRn1V9TrALacywlKinh/LTSlDcX3KwFnUey7QY +Ypqwpzmqm59m2I2mbJYV4+by+PGDYmy7Velhk6M99bFXi08jsJvllGov34zflVEpYKELKeRcVVi3 +qPyZ7iVNTA6z00yPhOgpD/0QVAKFyPnlw4vP5w8CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYw +HQYDVR0hBBYwFDASBgdghXQBUwIBBgdghXQBUwIBMBIGA1UdEwEB/wQIMAYBAf8CAQcwHQYDVR0O +BBYEFE0mICKJS9PVpAqhb97iEoHF8TwuMB8GA1UdIwQYMBaAFE0mICKJS9PVpAqhb97iEoHF8Twu +MA0GCSqGSIb3DQEBCwUAA4ICAQAyCrKkG8t9voJXiblqf/P0wS4RfbgZPnm3qKhyN2abGu2sEzsO +v2LwnN+ee6FTSA5BesogpxcbtnjsQJHzQq0Qw1zv/2BZf82Fo4s9SBwlAjxnffUy6S8w5X2lejjQ +82YqZh6NM4OKb3xuqFp1mrjX2lhIREeoTPpMSQpKwhI3qEAMw8jh0FcNlzKVxzqfl9NX+Ave5XLz +o9v/tdhZsnPdTSpxsrpJ9csc1fV5yJmz/MFMdOO0vSk3FQQoHt5FRnDsr7p4DooqzgB53MBfGWcs +a0vvaGgLQ+OswWIJ76bdZWGgr4RVSJFSHMYlkSrQwSIjYVmvRRGFHQEkNI/Ps/8XciATwoCqISxx +OQ7Qj1zB09GOInJGTB2Wrk9xseEFKZZZ9LuedT3PDTcNYtsmjGOpI99nBjx8Oto0QuFmtEYE3saW +mA9LSHokMnWRn6z3aOkquVVlzl1h0ydw2Df+n7mvoC5Wt6NlUe07qxS/TFED6F+KBZvuim6c779o ++sjaC+NCydAXFJy3SuCvkychVSa1ZC+N8f+mQAWFBVzKBxlcCxMoTFh/wqXvRdpg065lYZ1Tg3TC +rvJcwhbtkj6EPnNgiLx29CzP0H1907he0ZESEOnN3col49XtmS++dYFLJPlFRpTJKSFTnCZFqhMX +5OfNeOI5wSsSnqaeG8XmDtkx2Q== +-----END CERTIFICATE----- + +Swisscom Root EV CA 2 +===================== +-----BEGIN CERTIFICATE----- +MIIF4DCCA8igAwIBAgIRAPL6ZOJ0Y9ON/RAdBB92ylgwDQYJKoZIhvcNAQELBQAwZzELMAkGA1UE +BhMCY2gxETAPBgNVBAoTCFN3aXNzY29tMSUwIwYDVQQLExxEaWdpdGFsIENlcnRpZmljYXRlIFNl +cnZpY2VzMR4wHAYDVQQDExVTd2lzc2NvbSBSb290IEVWIENBIDIwHhcNMTEwNjI0MDk0NTA4WhcN +MzEwNjI1MDg0NTA4WjBnMQswCQYDVQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsT +HERpZ2l0YWwgQ2VydGlmaWNhdGUgU2VydmljZXMxHjAcBgNVBAMTFVN3aXNzY29tIFJvb3QgRVYg +Q0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMT3HS9X6lds93BdY7BxUglgRCgz +o3pOCvrY6myLURYaVa5UJsTMRQdBTxB5f3HSek4/OE6zAMaVylvNwSqD1ycfMQ4jFrclyxy0uYAy +Xhqdk/HoPGAsp15XGVhRXrwsVgu42O+LgrQ8uMIkqBPHoCE2G3pXKSinLr9xJZDzRINpUKTk4Rti +GZQJo/PDvO/0vezbE53PnUgJUmfANykRHvvSEaeFGHR55E+FFOtSN+KxRdjMDUN/rhPSays/p8Li +qG12W0OfvrSdsyaGOx9/5fLoZigWJdBLlzin5M8J0TbDC77aO0RYjb7xnglrPvMyxyuHxuxenPaH +Za0zKcQvidm5y8kDnftslFGXEBuGCxobP/YCfnvUxVFkKJ3106yDgYjTdLRZncHrYTNaRdHLOdAG +alNgHa/2+2m8atwBz735j9m9W8E6X47aD0upm50qKGsaCnw8qyIL5XctcfaCNYGu+HuB5ur+rPQa +m3Rc6I8k9l2dRsQs0h4rIWqDJ2dVSqTjyDKXZpBy2uPUZC5f46Fq9mDU5zXNysRojddxyNMkM3Ox +bPlq4SjbX8Y96L5V5jcb7STZDxmPX2MYWFCBUWVv8p9+agTnNCRxunZLWB4ZvRVgRaoMEkABnRDi +xzgHcgplwLa7JSnaFp6LNYth7eVxV4O1PHGf40+/fh6Bn0GXAgMBAAGjgYYwgYMwDgYDVR0PAQH/ +BAQDAgGGMB0GA1UdIQQWMBQwEgYHYIV0AVMCAgYHYIV0AVMCAjASBgNVHRMBAf8ECDAGAQH/AgED +MB0GA1UdDgQWBBRF2aWBbj2ITY1x0kbBbkUe88SAnTAfBgNVHSMEGDAWgBRF2aWBbj2ITY1x0kbB +bkUe88SAnTANBgkqhkiG9w0BAQsFAAOCAgEAlDpzBp9SSzBc1P6xXCX5145v9Ydkn+0UjrgEjihL +j6p7jjm02Vj2e6E1CqGdivdj5eu9OYLU43otb98TPLr+flaYC/NUn81ETm484T4VvwYmneTwkLbU +wp4wLh/vx3rEUMfqe9pQy3omywC0Wqu1kx+AiYQElY2NfwmTv9SoqORjbdlk5LgpWgi/UOGED1V7 +XwgiG/W9mR4U9s70WBCCswo9GcG/W6uqmdjyMb3lOGbcWAXH7WMaLgqXfIeTK7KK4/HsGOV1timH +59yLGn602MnTihdsfSlEvoqq9X46Lmgxk7lq2prg2+kupYTNHAq4Sgj5nPFhJpiTt3tm7JFe3VE/ +23MPrQRYCd0EApUKPtN236YQHoA96M2kZNEzx5LH4k5E4wnJTsJdhw4Snr8PyQUQ3nqjsTzyP6Wq +J3mtMX0f/fwZacXduT98zca0wjAefm6S139hdlqP65VNvBFuIXxZN5nQBrz5Bm0yFqXZaajh3DyA +HmBR3NdUIR7KYndP+tiPsys6DXhyyWhBWkdKwqPrGtcKqzwyVcgKEZzfdNbwQBUdyLmPtTbFr/gi +uMod89a2GQ+fYWVq6nTIfI/DT11lgh/ZDYnadXL77/FHZxOzyNEZiCcmmpl5fx7kLD977vHeTYuW +l8PVP3wbI+2ksx0WckNLIOFZfsLorSa/ovc= +-----END CERTIFICATE----- + +CA Disig Root R1 +================ +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAMMDmu5QkG4oMA0GCSqGSIb3DQEBBQUAMFIxCzAJBgNVBAYTAlNLMRMw +EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp +ZyBSb290IFIxMB4XDTEyMDcxOTA5MDY1NloXDTQyMDcxOTA5MDY1NlowUjELMAkGA1UEBhMCU0sx +EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp +c2lnIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCqw3j33Jijp1pedxiy +3QRkD2P9m5YJgNXoqqXinCaUOuiZc4yd39ffg/N4T0Dhf9Kn0uXKE5Pn7cZ3Xza1lK/oOI7bm+V8 +u8yN63Vz4STN5qctGS7Y1oprFOsIYgrY3LMATcMjfF9DCCMyEtztDK3AfQ+lekLZWnDZv6fXARz2 +m6uOt0qGeKAeVjGu74IKgEH3G8muqzIm1Cxr7X1r5OJeIgpFy4QxTaz+29FHuvlglzmxZcfe+5nk +CiKxLU3lSCZpq+Kq8/v8kiky6bM+TR8noc2OuRf7JT7JbvN32g0S9l3HuzYQ1VTW8+DiR0jm3hTa +YVKvJrT1cU/J19IG32PK/yHoWQbgCNWEFVP3Q+V8xaCJmGtzxmjOZd69fwX3se72V6FglcXM6pM6 +vpmumwKjrckWtc7dXpl4fho5frLABaTAgqWjR56M6ly2vGfb5ipN0gTco65F97yLnByn1tUD3AjL +LhbKXEAz6GfDLuemROoRRRw1ZS0eRWEkG4IupZ0zXWX4Qfkuy5Q/H6MMMSRE7cderVC6xkGbrPAX +ZcD4XW9boAo0PO7X6oifmPmvTiT6l7Jkdtqr9O3jw2Dv1fkCyC2fg69naQanMVXVz0tv/wQFx1is +XxYb5dKj6zHbHzMVTdDypVP1y+E9Tmgt2BLdqvLmTZtJ5cUoobqwWsagtQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUiQq0OJMa5qvum5EY+fU8PjXQ +04IwDQYJKoZIhvcNAQEFBQADggIBADKL9p1Kyb4U5YysOMo6CdQbzoaz3evUuii+Eq5FLAR0rBNR +xVgYZk2C2tXck8An4b58n1KeElb21Zyp9HWc+jcSjxyT7Ff+Bw+r1RL3D65hXlaASfX8MPWbTx9B +LxyE04nH4toCdu0Jz2zBuByDHBb6lM19oMgY0sidbvW9adRtPTXoHqJPYNcHKfyyo6SdbhWSVhlM +CrDpfNIZTUJG7L399ldb3Zh+pE3McgODWF3vkzpBemOqfDqo9ayk0d2iLbYq/J8BjuIQscTK5Gfb +VSUZP/3oNn6z4eGBrxEWi1CXYBmCAMBrTXO40RMHPuq2MU/wQppt4hF05ZSsjYSVPCGvxdpHyN85 +YmLLW1AL14FABZyb7bq2ix4Eb5YgOe2kfSnbSM6C3NQCjR0EMVrHS/BsYVLXtFHCgWzN4funodKS +ds+xDzdYpPJScWc/DIh4gInByLUfkmO+p3qKViwaqKactV2zY9ATIKHrkWzQjX2v3wvkF7mGnjix +lAxYjOBVqjtjbZqJYLhkKpLGN/R+Q0O3c+gB53+XD9fyexn9GtePyfqFa3qdnom2piiZk4hA9z7N +UaPK6u95RyG1/jLix8NRb76AdPCkwzryT+lf3xkK8jsTQ6wxpLPn6/wY1gGp8yqPNg7rtLG8t0zJ +a7+h89n07eLw4+1knj0vllJPgFOL +-----END CERTIFICATE----- + +CA Disig Root R2 +================ +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNVBAYTAlNLMRMw +EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp +ZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQyMDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sx +EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp +c2lnIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbC +w3OeNcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNHPWSb6Wia +xswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3Ix2ymrdMxp7zo5eFm1tL7 +A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbeQTg06ov80egEFGEtQX6sx3dOy1FU+16S +GBsEWmjGycT6txOgmLcRK7fWV8x8nhfRyyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqV +g8NTEQxzHQuyRpDRQjrOQG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa +5Beny912H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJQfYE +koopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUDi/ZnWejBBhG93c+A +Ak9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORsnLMOPReisjQS1n6yqEm70XooQL6i +Fh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5u +Qu0wDQYJKoZIhvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM +tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqfGopTpti72TVV +sRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkblvdhuDvEK7Z4bLQjb/D907Je +dR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka+elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W8 +1k/BfDxujRNt+3vrMNDcTa/F1balTFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjx +mHHEt38OFdAlab0inSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01 +utI3gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18DrG5gPcFw0 +sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3OszMOl6W8KjptlwlCFtaOg +UxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8xL4ysEr3vQCj8KWefshNPZiTEUxnpHikV +7+ZtsH8tZ/3zbBt1RqPlShfppNcL +-----END CERTIFICATE----- + +ACCVRAIZ1 +========= +-----BEGIN CERTIFICATE----- +MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UEAwwJQUNDVlJB +SVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQswCQYDVQQGEwJFUzAeFw0xMTA1 +MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQBgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwH +UEtJQUNDVjENMAsGA1UECgwEQUNDVjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQCbqau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gM +jmoYHtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWoG2ioPej0 +RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpAlHPrzg5XPAOBOp0KoVdD +aaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhrIA8wKFSVf+DuzgpmndFALW4ir50awQUZ +0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDG +WuzndN9wrqODJerWx5eHk6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs7 +8yM2x/474KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMOm3WR +5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpacXpkatcnYGMN285J +9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPluUsXQA+xtrn13k/c4LOsOxFwYIRK +Q26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYIKwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRw +Oi8vd3d3LmFjY3YuZXMvZmlsZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEu +Y3J0MB8GCCsGAQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 +VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeTVfZW6oHlNsyM +Hj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIGCCsGAQUFBwICMIIBFB6CARAA +QQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUAcgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBh +AO0AegAgAGQAZQAgAGwAYQAgAEEAQwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUA +YwBuAG8AbABvAGcA7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBj +AHQAcgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAAQwBQAFMA +IABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUAczAwBggrBgEFBQcCARYk +aHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2MuaHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0 +dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRtaW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2 +MV9kZXIuY3JsMA4GA1UdDwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZI +hvcNAQEFBQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdpD70E +R9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gUJyCpZET/LtZ1qmxN +YEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+mAM/EKXMRNt6GGT6d7hmKG9Ww7Y49 +nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepDvV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJ +TS+xJlsndQAJxGJ3KQhfnlmstn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3 +sCPdK6jT2iWH7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h +I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szAh1xA2syVP1Xg +Nce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xFd3+YJ5oyXSrjhO7FmGYvliAd +3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2HpPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3p +EfbRD0tVNEYqi4Y7 +-----END CERTIFICATE----- + +TWCA Global Root CA +=================== +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcxEjAQBgNVBAoT +CVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMTVFdDQSBHbG9iYWwgUm9vdCBD +QTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQK +EwlUQUlXQU4tQ0ExEDAOBgNVBAsTB1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3Qg +Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2C +nJfF10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz0ALfUPZV +r2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfChMBwqoJimFb3u/Rk28OKR +Q4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbHzIh1HrtsBv+baz4X7GGqcXzGHaL3SekV +tTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1W +KKD+u4ZqyPpcC1jcxkt2yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99 +sy2sbZCilaLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYPoA/p +yJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQABDzfuBSO6N+pjWxn +kjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcEqYSjMq+u7msXi7Kx/mzhkIyIqJdI +zshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMC +AQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6g +cFGn90xHNcgL1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn +LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WFH6vPNOw/KP4M +8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNoRI2T9GRwoD2dKAXDOXC4Ynsg +/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlg +lPx4mI88k1HtQJAH32RjJMtOcQWh15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryP +A9gK8kxkRr05YuWW6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3m +i4TWnsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5jwa19hAM8 +EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWzaGHQRiapIVJpLesux+t3 +zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmyKwbQBM0= +-----END CERTIFICATE----- diff --git a/wp-includes/class-IXR.php b/wp-includes/class-IXR.php new file mode 100644 index 0000000..91b65e9 --- /dev/null +++ b/wp-includes/class-IXR.php @@ -0,0 +1,1107 @@ +<?php +/** + * IXR - The Incutio XML-RPC Library + * + * Copyright (c) 2010, Incutio Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - Neither the name of Incutio Ltd. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @package IXR + * @since 1.5.0 + * + * @copyright Incutio Ltd 2010 (http://www.incutio.com) + * @version 1.7.4 7th September 2010 + * @author Simon Willison + * @link http://scripts.incutio.com/xmlrpc/ Site/manual + * @license http://www.opensource.org/licenses/bsd-license.php BSD + */ + +/** + * IXR_Value + * + * @package IXR + * @since 1.5.0 + */ +class IXR_Value { + var $data; + var $type; + + function IXR_Value($data, $type = false) + { + $this->data = $data; + if (!$type) { + $type = $this->calculateType(); + } + $this->type = $type; + if ($type == 'struct') { + // Turn all the values in the array in to new IXR_Value objects + foreach ($this->data as $key => $value) { + $this->data[$key] = new IXR_Value($value); + } + } + if ($type == 'array') { + for ($i = 0, $j = count($this->data); $i < $j; $i++) { + $this->data[$i] = new IXR_Value($this->data[$i]); + } + } + } + + function calculateType() + { + if ($this->data === true || $this->data === false) { + return 'boolean'; + } + if (is_integer($this->data)) { + return 'int'; + } + if (is_double($this->data)) { + return 'double'; + } + + // Deal with IXR object types base64 and date + if (is_object($this->data) && is_a($this->data, 'IXR_Date')) { + return 'date'; + } + if (is_object($this->data) && is_a($this->data, 'IXR_Base64')) { + return 'base64'; + } + + // If it is a normal PHP object convert it in to a struct + if (is_object($this->data)) { + $this->data = get_object_vars($this->data); + return 'struct'; + } + if (!is_array($this->data)) { + return 'string'; + } + + // We have an array - is it an array or a struct? + if ($this->isStruct($this->data)) { + return 'struct'; + } else { + return 'array'; + } + } + + function getXml() + { + // Return XML for this value + switch ($this->type) { + case 'boolean': + return '<boolean>'.(($this->data) ? '1' : '0').'</boolean>'; + break; + case 'int': + return '<int>'.$this->data.'</int>'; + break; + case 'double': + return '<double>'.$this->data.'</double>'; + break; + case 'string': + return '<string>'.htmlspecialchars($this->data).'</string>'; + break; + case 'array': + $return = '<array><data>'."\n"; + foreach ($this->data as $item) { + $return .= ' <value>'.$item->getXml()."</value>\n"; + } + $return .= '</data></array>'; + return $return; + break; + case 'struct': + $return = '<struct>'."\n"; + foreach ($this->data as $name => $value) { + $name = htmlspecialchars($name); + $return .= " <member><name>$name</name><value>"; + $return .= $value->getXml()."</value></member>\n"; + } + $return .= '</struct>'; + return $return; + break; + case 'date': + case 'base64': + return $this->data->getXml(); + break; + } + return false; + } + + /** + * Checks whether or not the supplied array is a struct or not + * + * @param array $array + * @return boolean + */ + function isStruct($array) + { + $expected = 0; + foreach ($array as $key => $value) { + if ((string)$key != (string)$expected) { + return true; + } + $expected++; + } + return false; + } +} + +/** + * IXR_MESSAGE + * + * @package IXR + * @since 1.5.0 + * + */ +class IXR_Message +{ + var $message; + var $messageType; // methodCall / methodResponse / fault + var $faultCode; + var $faultString; + var $methodName; + var $params; + + // Current variable stacks + var $_arraystructs = array(); // The stack used to keep track of the current array/struct + var $_arraystructstypes = array(); // Stack keeping track of if things are structs or array + var $_currentStructName = array(); // A stack as well + var $_param; + var $_value; + var $_currentTag; + var $_currentTagContents; + // The XML parser + var $_parser; + + function IXR_Message($message) + { + $this->message =& $message; + } + + function parse() + { + // first remove the XML declaration + // merged from WP #10698 - this method avoids the RAM usage of preg_replace on very large messages + $header = preg_replace( '/<\?xml.*?\?'.'>/s', '', substr( $this->message, 0, 100 ), 1 ); + $this->message = trim( substr_replace( $this->message, $header, 0, 100 ) ); + if ( '' == $this->message ) { + return false; + } + + // Then remove the DOCTYPE + $header = preg_replace( '/^<!DOCTYPE[^>]*+>/i', '', substr( $this->message, 0, 200 ), 1 ); + $this->message = trim( substr_replace( $this->message, $header, 0, 200 ) ); + if ( '' == $this->message ) { + return false; + } + + // Check that the root tag is valid + $root_tag = substr( $this->message, 0, strcspn( substr( $this->message, 0, 20 ), "> \t\r\n" ) ); + if ( '<!DOCTYPE' === strtoupper( $root_tag ) ) { + return false; + } + if ( ! in_array( $root_tag, array( '<methodCall', '<methodResponse', '<fault' ) ) ) { + return false; + } + + // Bail if there are too many elements to parse + $element_limit = 30000; + if ( function_exists( 'apply_filters' ) ) { + /** + * Filter the number of elements to parse in an XML-RPC response. + * + * @since 4.0.0 + * + * @param int $element_limit Default elements limit. + */ + $element_limit = apply_filters( 'xmlrpc_element_limit', $element_limit ); + } + if ( $element_limit && 2 * $element_limit < substr_count( $this->message, '<' ) ) { + return false; + } + + $this->_parser = xml_parser_create(); + // Set XML parser to take the case of tags in to account + xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false); + // Set XML parser callback functions + xml_set_object($this->_parser, $this); + xml_set_element_handler($this->_parser, 'tag_open', 'tag_close'); + xml_set_character_data_handler($this->_parser, 'cdata'); + $chunk_size = 262144; // 256Kb, parse in chunks to avoid the RAM usage on very large messages + $final = false; + do { + if (strlen($this->message) <= $chunk_size) { + $final = true; + } + $part = substr($this->message, 0, $chunk_size); + $this->message = substr($this->message, $chunk_size); + if (!xml_parse($this->_parser, $part, $final)) { + return false; + } + if ($final) { + break; + } + } while (true); + xml_parser_free($this->_parser); + + // Grab the error messages, if any + if ($this->messageType == 'fault') { + $this->faultCode = $this->params[0]['faultCode']; + $this->faultString = $this->params[0]['faultString']; + } + return true; + } + + function tag_open($parser, $tag, $attr) + { + $this->_currentTagContents = ''; + $this->currentTag = $tag; + switch($tag) { + case 'methodCall': + case 'methodResponse': + case 'fault': + $this->messageType = $tag; + break; + /* Deal with stacks of arrays and structs */ + case 'data': // data is to all intents and puposes more interesting than array + $this->_arraystructstypes[] = 'array'; + $this->_arraystructs[] = array(); + break; + case 'struct': + $this->_arraystructstypes[] = 'struct'; + $this->_arraystructs[] = array(); + break; + } + } + + function cdata($parser, $cdata) + { + $this->_currentTagContents .= $cdata; + } + + function tag_close($parser, $tag) + { + $valueFlag = false; + switch($tag) { + case 'int': + case 'i4': + $value = (int)trim($this->_currentTagContents); + $valueFlag = true; + break; + case 'double': + $value = (double)trim($this->_currentTagContents); + $valueFlag = true; + break; + case 'string': + $value = (string)trim($this->_currentTagContents); + $valueFlag = true; + break; + case 'dateTime.iso8601': + $value = new IXR_Date(trim($this->_currentTagContents)); + $valueFlag = true; + break; + case 'value': + // "If no type is indicated, the type is string." + if (trim($this->_currentTagContents) != '') { + $value = (string)$this->_currentTagContents; + $valueFlag = true; + } + break; + case 'boolean': + $value = (boolean)trim($this->_currentTagContents); + $valueFlag = true; + break; + case 'base64': + $value = base64_decode($this->_currentTagContents); + $valueFlag = true; + break; + /* Deal with stacks of arrays and structs */ + case 'data': + case 'struct': + $value = array_pop($this->_arraystructs); + array_pop($this->_arraystructstypes); + $valueFlag = true; + break; + case 'member': + array_pop($this->_currentStructName); + break; + case 'name': + $this->_currentStructName[] = trim($this->_currentTagContents); + break; + case 'methodName': + $this->methodName = trim($this->_currentTagContents); + break; + } + + if ($valueFlag) { + if (count($this->_arraystructs) > 0) { + // Add value to struct or array + if ($this->_arraystructstypes[count($this->_arraystructstypes)-1] == 'struct') { + // Add to struct + $this->_arraystructs[count($this->_arraystructs)-1][$this->_currentStructName[count($this->_currentStructName)-1]] = $value; + } else { + // Add to array + $this->_arraystructs[count($this->_arraystructs)-1][] = $value; + } + } else { + // Just add as a parameter + $this->params[] = $value; + } + } + $this->_currentTagContents = ''; + } +} + +/** + * IXR_Server + * + * @package IXR + * @since 1.5.0 + */ +class IXR_Server +{ + var $data; + var $callbacks = array(); + var $message; + var $capabilities; + + function IXR_Server($callbacks = false, $data = false, $wait = false) + { + $this->setCapabilities(); + if ($callbacks) { + $this->callbacks = $callbacks; + } + $this->setCallbacks(); + if (!$wait) { + $this->serve($data); + } + } + + function serve($data = false) + { + if (!$data) { + if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] !== 'POST') { + header('Content-Type: text/plain'); // merged from WP #9093 + die('XML-RPC server accepts POST requests only.'); + } + + global $HTTP_RAW_POST_DATA; + if (empty($HTTP_RAW_POST_DATA)) { + // workaround for a bug in PHP 5.2.2 - http://bugs.php.net/bug.php?id=41293 + $data = file_get_contents('php://input'); + } else { + $data =& $HTTP_RAW_POST_DATA; + } + } + $this->message = new IXR_Message($data); + if (!$this->message->parse()) { + $this->error(-32700, 'parse error. not well formed'); + } + if ($this->message->messageType != 'methodCall') { + $this->error(-32600, 'server error. invalid xml-rpc. not conforming to spec. Request must be a methodCall'); + } + $result = $this->call($this->message->methodName, $this->message->params); + + // Is the result an error? + if (is_a($result, 'IXR_Error')) { + $this->error($result); + } + + // Encode the result + $r = new IXR_Value($result); + $resultxml = $r->getXml(); + + // Create the XML + $xml = <<<EOD +<methodResponse> + <params> + <param> + <value> + $resultxml + </value> + </param> + </params> +</methodResponse> + +EOD; + // Send it + $this->output($xml); + } + + function call($methodname, $args) + { + if (!$this->hasMethod($methodname)) { + return new IXR_Error(-32601, 'server error. requested method '.$methodname.' does not exist.'); + } + $method = $this->callbacks[$methodname]; + + // Perform the callback and send the response + if (count($args) == 1) { + // If only one parameter just send that instead of the whole array + $args = $args[0]; + } + + // Are we dealing with a function or a method? + if (is_string($method) && substr($method, 0, 5) == 'this:') { + // It's a class method - check it exists + $method = substr($method, 5); + if (!method_exists($this, $method)) { + return new IXR_Error(-32601, 'server error. requested class method "'.$method.'" does not exist.'); + } + + //Call the method + $result = $this->$method($args); + } else { + // It's a function - does it exist? + if (is_array($method)) { + if (!is_callable(array($method[0], $method[1]))) { + return new IXR_Error(-32601, 'server error. requested object method "'.$method[1].'" does not exist.'); + } + } else if (!function_exists($method)) { + return new IXR_Error(-32601, 'server error. requested function "'.$method.'" does not exist.'); + } + + // Call the function + $result = call_user_func($method, $args); + } + return $result; + } + + function error($error, $message = false) + { + // Accepts either an error object or an error code and message + if ($message && !is_object($error)) { + $error = new IXR_Error($error, $message); + } + $this->output($error->getXml()); + } + + function output($xml) + { + $charset = function_exists('get_option') ? get_option('blog_charset') : ''; + if ($charset) + $xml = '<?xml version="1.0" encoding="'.$charset.'"?>'."\n".$xml; + else + $xml = '<?xml version="1.0"?>'."\n".$xml; + $length = strlen($xml); + header('Connection: close'); + header('Content-Length: '.$length); + if ($charset) + header('Content-Type: text/xml; charset='.$charset); + else + header('Content-Type: text/xml'); + header('Date: '.date('r')); + echo $xml; + exit; + } + + function hasMethod($method) + { + return in_array($method, array_keys($this->callbacks)); + } + + function setCapabilities() + { + // Initialises capabilities array + $this->capabilities = array( + 'xmlrpc' => array( + 'specUrl' => 'http://www.xmlrpc.com/spec', + 'specVersion' => 1 + ), + 'faults_interop' => array( + 'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php', + 'specVersion' => 20010516 + ), + 'system.multicall' => array( + 'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208', + 'specVersion' => 1 + ), + ); + } + + function getCapabilities($args) + { + return $this->capabilities; + } + + function setCallbacks() + { + $this->callbacks['system.getCapabilities'] = 'this:getCapabilities'; + $this->callbacks['system.listMethods'] = 'this:listMethods'; + $this->callbacks['system.multicall'] = 'this:multiCall'; + } + + function listMethods($args) + { + // Returns a list of methods - uses array_reverse to ensure user defined + // methods are listed before server defined methods + return array_reverse(array_keys($this->callbacks)); + } + + function multiCall($methodcalls) + { + // See http://www.xmlrpc.com/discuss/msgReader$1208 + $return = array(); + foreach ($methodcalls as $call) { + $method = $call['methodName']; + $params = $call['params']; + if ($method == 'system.multicall') { + $result = new IXR_Error(-32600, 'Recursive calls to system.multicall are forbidden'); + } else { + $result = $this->call($method, $params); + } + if (is_a($result, 'IXR_Error')) { + $return[] = array( + 'faultCode' => $result->code, + 'faultString' => $result->message + ); + } else { + $return[] = array($result); + } + } + return $return; + } +} + +/** + * IXR_Request + * + * @package IXR + * @since 1.5.0 + */ +class IXR_Request +{ + var $method; + var $args; + var $xml; + + function IXR_Request($method, $args) + { + $this->method = $method; + $this->args = $args; + $this->xml = <<<EOD +<?xml version="1.0"?> +<methodCall> +<methodName>{$this->method}</methodName> +<params> + +EOD; + foreach ($this->args as $arg) { + $this->xml .= '<param><value>'; + $v = new IXR_Value($arg); + $this->xml .= $v->getXml(); + $this->xml .= "</value></param>\n"; + } + $this->xml .= '</params></methodCall>'; + } + + function getLength() + { + return strlen($this->xml); + } + + function getXml() + { + return $this->xml; + } +} + +/** + * IXR_Client + * + * @package IXR + * @since 1.5.0 + * + */ +class IXR_Client +{ + var $server; + var $port; + var $path; + var $useragent; + var $response; + var $message = false; + var $debug = false; + var $timeout; + var $headers = array(); + + // Storage place for an error message + var $error = false; + + function IXR_Client($server, $path = false, $port = 80, $timeout = 15) + { + if (!$path) { + // Assume we have been given a URL instead + $bits = parse_url($server); + $this->server = $bits['host']; + $this->port = isset($bits['port']) ? $bits['port'] : 80; + $this->path = isset($bits['path']) ? $bits['path'] : '/'; + + // Make absolutely sure we have a path + if (!$this->path) { + $this->path = '/'; + } + + if ( ! empty( $bits['query'] ) ) { + $this->path .= '?' . $bits['query']; + } + } else { + $this->server = $server; + $this->path = $path; + $this->port = $port; + } + $this->useragent = 'The Incutio XML-RPC PHP Library'; + $this->timeout = $timeout; + } + + function query() + { + $args = func_get_args(); + $method = array_shift($args); + $request = new IXR_Request($method, $args); + $length = $request->getLength(); + $xml = $request->getXml(); + $r = "\r\n"; + $request = "POST {$this->path} HTTP/1.0$r"; + + // Merged from WP #8145 - allow custom headers + $this->headers['Host'] = $this->server; + $this->headers['Content-Type'] = 'text/xml'; + $this->headers['User-Agent'] = $this->useragent; + $this->headers['Content-Length']= $length; + + foreach( $this->headers as $header => $value ) { + $request .= "{$header}: {$value}{$r}"; + } + $request .= $r; + + $request .= $xml; + + // Now send the request + if ($this->debug) { + echo '<pre class="ixr_request">'.htmlspecialchars($request)."\n</pre>\n\n"; + } + + if ($this->timeout) { + $fp = @fsockopen($this->server, $this->port, $errno, $errstr, $this->timeout); + } else { + $fp = @fsockopen($this->server, $this->port, $errno, $errstr); + } + if (!$fp) { + $this->error = new IXR_Error(-32300, 'transport error - could not open socket'); + return false; + } + fputs($fp, $request); + $contents = ''; + $debugContents = ''; + $gotFirstLine = false; + $gettingHeaders = true; + while (!feof($fp)) { + $line = fgets($fp, 4096); + if (!$gotFirstLine) { + // Check line for '200' + if (strstr($line, '200') === false) { + $this->error = new IXR_Error(-32300, 'transport error - HTTP status code was not 200'); + return false; + } + $gotFirstLine = true; + } + if (trim($line) == '') { + $gettingHeaders = false; + } + if (!$gettingHeaders) { + // merged from WP #12559 - remove trim + $contents .= $line; + } + if ($this->debug) { + $debugContents .= $line; + } + } + if ($this->debug) { + echo '<pre class="ixr_response">'.htmlspecialchars($debugContents)."\n</pre>\n\n"; + } + + // Now parse what we've got back + $this->message = new IXR_Message($contents); + if (!$this->message->parse()) { + // XML error + $this->error = new IXR_Error(-32700, 'parse error. not well formed'); + return false; + } + + // Is the message a fault? + if ($this->message->messageType == 'fault') { + $this->error = new IXR_Error($this->message->faultCode, $this->message->faultString); + return false; + } + + // Message must be OK + return true; + } + + function getResponse() + { + // methodResponses can only have one param - return that + return $this->message->params[0]; + } + + function isError() + { + return (is_object($this->error)); + } + + function getErrorCode() + { + return $this->error->code; + } + + function getErrorMessage() + { + return $this->error->message; + } +} + + +/** + * IXR_Error + * + * @package IXR + * @since 1.5.0 + */ +class IXR_Error +{ + var $code; + var $message; + + function IXR_Error($code, $message) + { + $this->code = $code; + $this->message = htmlspecialchars($message); + } + + function getXml() + { + $xml = <<<EOD +<methodResponse> + <fault> + <value> + <struct> + <member> + <name>faultCode</name> + <value><int>{$this->code}</int></value> + </member> + <member> + <name>faultString</name> + <value><string>{$this->message}</string></value> + </member> + </struct> + </value> + </fault> +</methodResponse> + +EOD; + return $xml; + } +} + +/** + * IXR_Date + * + * @package IXR + * @since 1.5.0 + */ +class IXR_Date { + var $year; + var $month; + var $day; + var $hour; + var $minute; + var $second; + var $timezone; + + function IXR_Date($time) + { + // $time can be a PHP timestamp or an ISO one + if (is_numeric($time)) { + $this->parseTimestamp($time); + } else { + $this->parseIso($time); + } + } + + function parseTimestamp($timestamp) + { + $this->year = date('Y', $timestamp); + $this->month = date('m', $timestamp); + $this->day = date('d', $timestamp); + $this->hour = date('H', $timestamp); + $this->minute = date('i', $timestamp); + $this->second = date('s', $timestamp); + $this->timezone = ''; + } + + function parseIso($iso) + { + $this->year = substr($iso, 0, 4); + $this->month = substr($iso, 4, 2); + $this->day = substr($iso, 6, 2); + $this->hour = substr($iso, 9, 2); + $this->minute = substr($iso, 12, 2); + $this->second = substr($iso, 15, 2); + $this->timezone = substr($iso, 17); + } + + function getIso() + { + return $this->year.$this->month.$this->day.'T'.$this->hour.':'.$this->minute.':'.$this->second.$this->timezone; + } + + function getXml() + { + return '<dateTime.iso8601>'.$this->getIso().'</dateTime.iso8601>'; + } + + function getTimestamp() + { + return mktime($this->hour, $this->minute, $this->second, $this->month, $this->day, $this->year); + } +} + +/** + * IXR_Base64 + * + * @package IXR + * @since 1.5.0 + */ +class IXR_Base64 +{ + var $data; + + function IXR_Base64($data) + { + $this->data = $data; + } + + function getXml() + { + return '<base64>'.base64_encode($this->data).'</base64>'; + } +} + +/** + * IXR_IntrospectionServer + * + * @package IXR + * @since 1.5.0 + */ +class IXR_IntrospectionServer extends IXR_Server +{ + var $signatures; + var $help; + + function IXR_IntrospectionServer() + { + $this->setCallbacks(); + $this->setCapabilities(); + $this->capabilities['introspection'] = array( + 'specUrl' => 'http://xmlrpc.usefulinc.com/doc/reserved.html', + 'specVersion' => 1 + ); + $this->addCallback( + 'system.methodSignature', + 'this:methodSignature', + array('array', 'string'), + 'Returns an array describing the return type and required parameters of a method' + ); + $this->addCallback( + 'system.getCapabilities', + 'this:getCapabilities', + array('struct'), + 'Returns a struct describing the XML-RPC specifications supported by this server' + ); + $this->addCallback( + 'system.listMethods', + 'this:listMethods', + array('array'), + 'Returns an array of available methods on this server' + ); + $this->addCallback( + 'system.methodHelp', + 'this:methodHelp', + array('string', 'string'), + 'Returns a documentation string for the specified method' + ); + } + + function addCallback($method, $callback, $args, $help) + { + $this->callbacks[$method] = $callback; + $this->signatures[$method] = $args; + $this->help[$method] = $help; + } + + function call($methodname, $args) + { + // Make sure it's in an array + if ($args && !is_array($args)) { + $args = array($args); + } + + // Over-rides default call method, adds signature check + if (!$this->hasMethod($methodname)) { + return new IXR_Error(-32601, 'server error. requested method "'.$this->message->methodName.'" not specified.'); + } + $method = $this->callbacks[$methodname]; + $signature = $this->signatures[$methodname]; + $returnType = array_shift($signature); + + // Check the number of arguments + if (count($args) != count($signature)) { + return new IXR_Error(-32602, 'server error. wrong number of method parameters'); + } + + // Check the argument types + $ok = true; + $argsbackup = $args; + for ($i = 0, $j = count($args); $i < $j; $i++) { + $arg = array_shift($args); + $type = array_shift($signature); + switch ($type) { + case 'int': + case 'i4': + if (is_array($arg) || !is_int($arg)) { + $ok = false; + } + break; + case 'base64': + case 'string': + if (!is_string($arg)) { + $ok = false; + } + break; + case 'boolean': + if ($arg !== false && $arg !== true) { + $ok = false; + } + break; + case 'float': + case 'double': + if (!is_float($arg)) { + $ok = false; + } + break; + case 'date': + case 'dateTime.iso8601': + if (!is_a($arg, 'IXR_Date')) { + $ok = false; + } + break; + } + if (!$ok) { + return new IXR_Error(-32602, 'server error. invalid method parameters'); + } + } + // It passed the test - run the "real" method call + return parent::call($methodname, $argsbackup); + } + + function methodSignature($method) + { + if (!$this->hasMethod($method)) { + return new IXR_Error(-32601, 'server error. requested method "'.$method.'" not specified.'); + } + // We should be returning an array of types + $types = $this->signatures[$method]; + $return = array(); + foreach ($types as $type) { + switch ($type) { + case 'string': + $return[] = 'string'; + break; + case 'int': + case 'i4': + $return[] = 42; + break; + case 'double': + $return[] = 3.1415; + break; + case 'dateTime.iso8601': + $return[] = new IXR_Date(time()); + break; + case 'boolean': + $return[] = true; + break; + case 'base64': + $return[] = new IXR_Base64('base64'); + break; + case 'array': + $return[] = array('array'); + break; + case 'struct': + $return[] = array('struct' => 'struct'); + break; + } + } + return $return; + } + + function methodHelp($method) + { + return $this->help[$method]; + } +} + +/** + * IXR_ClientMulticall + * + * @package IXR + * @since 1.5.0 + */ +class IXR_ClientMulticall extends IXR_Client +{ + var $calls = array(); + + function IXR_ClientMulticall($server, $path = false, $port = 80) + { + parent::IXR_Client($server, $path, $port); + $this->useragent = 'The Incutio XML-RPC PHP Library (multicall client)'; + } + + function addCall() + { + $args = func_get_args(); + $methodName = array_shift($args); + $struct = array( + 'methodName' => $methodName, + 'params' => $args + ); + $this->calls[] = $struct; + } + + function query() + { + // Prepare multicall, then call the parent::query() method + return parent::query('system.multicall', $this->calls); + } +} diff --git a/wp-includes/class-feed.php b/wp-includes/class-feed.php new file mode 100644 index 0000000..9aa144a --- /dev/null +++ b/wp-includes/class-feed.php @@ -0,0 +1,140 @@ +<?php + +if ( !class_exists('SimplePie') ) + require_once( ABSPATH . WPINC . '/class-simplepie.php' ); + +class WP_Feed_Cache extends SimplePie_Cache { + /** + * Create a new SimplePie_Cache object + * + * @static + * @access public + */ + public function create($location, $filename, $extension) { + return new WP_Feed_Cache_Transient($location, $filename, $extension); + } +} + +class WP_Feed_Cache_Transient { + public $name; + public $mod_name; + public $lifetime = 43200; //Default lifetime in cache of 12 hours + + public function __construct($location, $filename, $extension) { + $this->name = 'feed_' . $filename; + $this->mod_name = 'feed_mod_' . $filename; + + $lifetime = $this->lifetime; + /** + * Filter the transient lifetime of the feed cache. + * + * @since 2.8.0 + * + * @param int $lifetime Cache duration in seconds. Default is 43200 seconds (12 hours). + * @param string $filename Unique identifier for the cache object. + */ + $this->lifetime = apply_filters( 'wp_feed_cache_transient_lifetime', $lifetime, $filename); + } + + public function save($data) { + if ( is_a($data, 'SimplePie') ) + $data = $data->data; + + set_transient($this->name, $data, $this->lifetime); + set_transient($this->mod_name, time(), $this->lifetime); + return true; + } + + public function load() { + return get_transient($this->name); + } + + public function mtime() { + return get_transient($this->mod_name); + } + + public function touch() { + return set_transient($this->mod_name, time(), $this->lifetime); + } + + public function unlink() { + delete_transient($this->name); + delete_transient($this->mod_name); + return true; + } +} + +class WP_SimplePie_File extends SimplePie_File { + + public function __construct($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false) { + $this->url = $url; + $this->timeout = $timeout; + $this->redirects = $redirects; + $this->headers = $headers; + $this->useragent = $useragent; + + $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE; + + if ( preg_match('/^http(s)?:\/\//i', $url) ) { + $args = array( + 'timeout' => $this->timeout, + 'redirection' => $this->redirects, + ); + + if ( !empty($this->headers) ) + $args['headers'] = $this->headers; + + if ( SIMPLEPIE_USERAGENT != $this->useragent ) //Use default WP user agent unless custom has been specified + $args['user-agent'] = $this->useragent; + + $res = wp_safe_remote_request($url, $args); + + if ( is_wp_error($res) ) { + $this->error = 'WP HTTP Error: ' . $res->get_error_message(); + $this->success = false; + } else { + $this->headers = wp_remote_retrieve_headers( $res ); + $this->body = wp_remote_retrieve_body( $res ); + $this->status_code = wp_remote_retrieve_response_code( $res ); + } + } else { + $this->error = ''; + $this->success = false; + } + } +} + +/** + * WordPress SimplePie Sanitization Class + * + * Extension of the SimplePie_Sanitize class to use KSES, because + * we cannot universally count on DOMDocument being available + * + * @package WordPress + * @since 3.5.0 + */ +class WP_SimplePie_Sanitize_KSES extends SimplePie_Sanitize { + public function sanitize( $data, $type, $base = '' ) { + $data = trim( $data ); + if ( $type & SIMPLEPIE_CONSTRUCT_MAYBE_HTML ) { + if (preg_match('/(&(#(x[0-9a-fA-F]+|[0-9]+)|[a-zA-Z0-9]+)|<\/[A-Za-z][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E]*' . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>)/', $data)) { + $type |= SIMPLEPIE_CONSTRUCT_HTML; + } + else { + $type |= SIMPLEPIE_CONSTRUCT_TEXT; + } + } + if ( $type & SIMPLEPIE_CONSTRUCT_BASE64 ) { + $data = base64_decode( $data ); + } + if ( $type & ( SIMPLEPIE_CONSTRUCT_HTML | SIMPLEPIE_CONSTRUCT_XHTML ) ) { + $data = wp_kses_post( $data ); + if ( $this->output_encoding !== 'UTF-8' ) { + $data = $this->registry->call( 'Misc', 'change_encoding', array( $data, 'UTF-8', $this->output_encoding ) ); + } + return $data; + } else { + return parent::sanitize( $data, $type, $base ); + } + } +} diff --git a/wp-includes/class-http.php b/wp-includes/class-http.php new file mode 100644 index 0000000..8f7351d --- /dev/null +++ b/wp-includes/class-http.php @@ -0,0 +1,2268 @@ +<?php +/** + * Simple and uniform HTTP request API. + * + * Standardizes the HTTP requests for WordPress. Handles cookies, gzip encoding and decoding, chunk + * decoding, if HTTP 1.1 and various other difficult HTTP protocol implementations. + * + * @link https://core.trac.wordpress.org/ticket/4779 HTTP API Proposal + * + * @package WordPress + * @subpackage HTTP + * @since 2.7.0 + */ + +/** + * WordPress HTTP Class for managing HTTP Transports and making HTTP requests. + * + * This class is used to consistently make outgoing HTTP requests easy for developers + * while still being compatible with the many PHP configurations under which + * WordPress runs. + * + * Debugging includes several actions, which pass different variables for debugging the HTTP API. + * + * @package WordPress + * @subpackage HTTP + * @since 2.7.0 + */ +class WP_Http { + + /** + * Send an HTTP request to a URI. + * + * Please note: The only URI that are supported in the HTTP Transport implementation + * are the HTTP and HTTPS protocols. + * + * @access public + * @since 2.7.0 + * + * @param string $url The request URL. + * @param string|array $args { + * Optional. Array or string of HTTP request arguments. + * + * @type string $method Request method. Accepts 'GET', 'POST', 'HEAD', or 'PUT'. + * Some transports technically allow others, but should not be + * assumed. Default 'GET'. + * @type int $timeout How long the connection should stay open in seconds. Default 5. + * @type int $redirection Number of allowed redirects. Not supported by all transports + * Default 5. + * @type string $httpversion Version of the HTTP protocol to use. Accepts '1.0' and '1.1'. + * Default '1.0'. + * @type string $user-agent User-agent value sent. + * Default WordPress/' . $wp_version . '; ' . get_bloginfo( 'url' ). + * @type bool $reject_unsafe_urls Whether to pass URLs through {@see wp_http_validate_url()}. + * Default false. + * @type bool $blocking Whether the calling code requires the result of the request. + * If set to false, the request will be sent to the remote server, + * and processing returned to the calling code immediately, the caller + * will know if the request succeeded or failed, but will not receive + * any response from the remote server. Default true. + * @type string|array $headers Array or string of headers to send with the request. + * Default empty array. + * @type array $cookies List of cookies to send with the request. Default empty array. + * @type string|array $body Body to send with the request. Default null. + * @type bool $compress Whether to compress the $body when sending the request. + * Default false. + * @type bool $decompress Whether to decompress a compressed response. If set to false and + * compressed content is returned in the response anyway, it will + * need to be separately decompressed. Default true. + * @type bool $sslverify Whether to verify SSL for the request. Default true. + * @type string sslcertificates Absolute path to an SSL certificate .crt file. + * Default ABSPATH . WPINC . '/certificates/ca-bundle.crt'. + * @type bool $stream Whether to stream to a file. If set to true and no filename was + * given, it will be droped it in the WP temp dir and its name will + * be set using the basename of the URL. Default false. + * @type string $filename Filename of the file to write to when streaming. $stream must be + * set to true. Default null. + * @type int $limit_response_size Size in bytes to limit the response to. Default null. + * + * } + * @return array|WP_Error Array containing 'headers', 'body', 'response', 'cookies', 'filename'. + * A WP_Error instance upon error. + */ + public function request( $url, $args = array() ) { + global $wp_version; + + $defaults = array( + 'method' => 'GET', + /** + * Filter the timeout value for an HTTP request. + * + * @since 2.7.0 + * + * @param int $timeout_value Time in seconds until a request times out. + * Default 5. + */ + 'timeout' => apply_filters( 'http_request_timeout', 5 ), + /** + * Filter the number of redirects allowed during an HTTP request. + * + * @since 2.7.0 + * + * @param int $redirect_count Number of redirects allowed. Default 5. + */ + 'redirection' => apply_filters( 'http_request_redirection_count', 5 ), + /** + * Filter the version of the HTTP protocol used in a request. + * + * @since 2.7.0 + * + * @param string $version Version of HTTP used. Accepts '1.0' and '1.1'. + * Default '1.0'. + */ + 'httpversion' => apply_filters( 'http_request_version', '1.0' ), + /** + * Filter the user agent value sent with an HTTP request. + * + * @since 2.7.0 + * + * @param string $user_agent WordPress user agent string. + */ + 'user-agent' => apply_filters( 'http_headers_useragent', 'WordPress/' . $wp_version . '; ' . get_bloginfo( 'url' ) ), + /** + * Filter whether to pass URLs through wp_http_validate_url() in an HTTP request. + * + * @since 3.6.0 + * + * @param bool $pass_url Whether to pass URLs through wp_http_validate_url(). + * Default false. + */ + 'reject_unsafe_urls' => apply_filters( 'http_request_reject_unsafe_urls', false ), + 'blocking' => true, + 'headers' => array(), + 'cookies' => array(), + 'body' => null, + 'compress' => false, + 'decompress' => true, + 'sslverify' => true, + 'sslcertificates' => ABSPATH . WPINC . '/certificates/ca-bundle.crt', + 'stream' => false, + 'filename' => null, + 'limit_response_size' => null, + ); + + // Pre-parse for the HEAD checks. + $args = wp_parse_args( $args ); + + // By default, Head requests do not cause redirections. + if ( isset($args['method']) && 'HEAD' == $args['method'] ) + $defaults['redirection'] = 0; + + $r = wp_parse_args( $args, $defaults ); + /** + * Filter the arguments used in an HTTP request. + * + * @since 2.7.0 + * + * @param array $r An array of HTTP request arguments. + * @param string $url The request URL. + */ + $r = apply_filters( 'http_request_args', $r, $url ); + + // The transports decrement this, store a copy of the original value for loop purposes. + if ( ! isset( $r['_redirection'] ) ) + $r['_redirection'] = $r['redirection']; + + /** + * Filter whether to preempt an HTTP request's return. + * + * Returning a truthy value to the filter will short-circuit + * the HTTP request and return early with that value. + * + * @since 2.9.0 + * + * @param bool $preempt Whether to preempt an HTTP request return. Default false. + * @param array $r HTTP request arguments. + * @param string $url The request URL. + */ + $pre = apply_filters( 'pre_http_request', false, $r, $url ); + if ( false !== $pre ) + return $pre; + + if ( function_exists( 'wp_kses_bad_protocol' ) ) { + if ( $r['reject_unsafe_urls'] ) + $url = wp_http_validate_url( $url ); + $url = wp_kses_bad_protocol( $url, array( 'http', 'https', 'ssl' ) ); + } + + $arrURL = @parse_url( $url ); + + if ( empty( $url ) || empty( $arrURL['scheme'] ) ) + return new WP_Error('http_request_failed', __('A valid URL was not provided.')); + + if ( $this->block_request( $url ) ) + return new WP_Error( 'http_request_failed', __( 'User has blocked requests through HTTP.' ) ); + + /* + * Determine if this is a https call and pass that on to the transport functions + * so that we can blacklist the transports that do not support ssl verification + */ + $r['ssl'] = $arrURL['scheme'] == 'https' || $arrURL['scheme'] == 'ssl'; + + // Determine if this request is to OUR install of WordPress. + $homeURL = parse_url( get_bloginfo( 'url' ) ); + $r['local'] = 'localhost' == $arrURL['host'] || ( isset( $homeURL['host'] ) && $homeURL['host'] == $arrURL['host'] ); + unset( $homeURL ); + + /* + * If we are streaming to a file but no filename was given drop it in the WP temp dir + * and pick its name using the basename of the $url. + */ + if ( $r['stream'] && empty( $r['filename'] ) ) { + $r['filename'] = wp_unique_filename( get_temp_dir(), basename( $url ) ); + } + + /* + * Force some settings if we are streaming to a file and check for existence and perms + * of destination directory. + */ + if ( $r['stream'] ) { + $r['blocking'] = true; + if ( ! wp_is_writable( dirname( $r['filename'] ) ) ) + return new WP_Error( 'http_request_failed', __( 'Destination directory for file streaming does not exist or is not writable.' ) ); + } + + if ( is_null( $r['headers'] ) ) + $r['headers'] = array(); + + if ( ! is_array( $r['headers'] ) ) { + $processedHeaders = WP_Http::processHeaders( $r['headers'], $url ); + $r['headers'] = $processedHeaders['headers']; + } + + if ( isset( $r['headers']['User-Agent'] ) ) { + $r['user-agent'] = $r['headers']['User-Agent']; + unset( $r['headers']['User-Agent'] ); + } + + if ( isset( $r['headers']['user-agent'] ) ) { + $r['user-agent'] = $r['headers']['user-agent']; + unset( $r['headers']['user-agent'] ); + } + + if ( '1.1' == $r['httpversion'] && !isset( $r['headers']['connection'] ) ) { + $r['headers']['connection'] = 'close'; + } + + // Construct Cookie: header if any cookies are set. + WP_Http::buildCookieHeader( $r ); + + // Avoid issues where mbstring.func_overload is enabled. + mbstring_binary_safe_encoding(); + + if ( ! isset( $r['headers']['Accept-Encoding'] ) ) { + if ( $encoding = WP_Http_Encoding::accept_encoding( $url, $r ) ) + $r['headers']['Accept-Encoding'] = $encoding; + } + + if ( ( ! is_null( $r['body'] ) && '' != $r['body'] ) || 'POST' == $r['method'] || 'PUT' == $r['method'] ) { + if ( is_array( $r['body'] ) || is_object( $r['body'] ) ) { + $r['body'] = http_build_query( $r['body'], null, '&' ); + + if ( ! isset( $r['headers']['Content-Type'] ) ) + $r['headers']['Content-Type'] = 'application/x-www-form-urlencoded; charset=' . get_option( 'blog_charset' ); + } + + if ( '' === $r['body'] ) + $r['body'] = null; + + if ( ! isset( $r['headers']['Content-Length'] ) && ! isset( $r['headers']['content-length'] ) ) + $r['headers']['Content-Length'] = strlen( $r['body'] ); + } + + $response = $this->_dispatch_request( $url, $r ); + + reset_mbstring_encoding(); + + if ( is_wp_error( $response ) ) + return $response; + + // Append cookies that were used in this request to the response + if ( ! empty( $r['cookies'] ) ) { + $cookies_set = wp_list_pluck( $response['cookies'], 'name' ); + foreach ( $r['cookies'] as $cookie ) { + if ( ! in_array( $cookie->name, $cookies_set ) && $cookie->test( $url ) ) { + $response['cookies'][] = $cookie; + } + } + } + + return $response; + } + + /** + * Tests which transports are capable of supporting the request. + * + * @since 3.2.0 + * @access private + * + * @param array $args Request arguments + * @param string $url URL to Request + * + * @return string|false Class name for the first transport that claims to support the request. False if no transport claims to support the request. + */ + public function _get_first_available_transport( $args, $url = null ) { + /** + * Filter which HTTP transports are available and in what order. + * + * @since 3.7.0 + * + * @param array $value Array of HTTP transports to check. Default array contains + * 'curl', and 'streams', in that order. + * @param array $args HTTP request arguments. + * @param string $url The URL to request. + */ + $request_order = apply_filters( 'http_api_transports', array( 'curl', 'streams' ), $args, $url ); + + // Loop over each transport on each HTTP request looking for one which will serve this request's needs. + foreach ( $request_order as $transport ) { + $class = 'WP_HTTP_' . $transport; + + // Check to see if this transport is a possibility, calls the transport statically. + if ( !call_user_func( array( $class, 'test' ), $args, $url ) ) + continue; + + return $class; + } + + return false; + } + + /** + * Dispatches a HTTP request to a supporting transport. + * + * Tests each transport in order to find a transport which matches the request arguments. + * Also caches the transport instance to be used later. + * + * The order for requests is cURL, and then PHP Streams. + * + * @since 3.2.0 + * @access private + * + * @param string $url URL to Request + * @param array $args Request arguments + * @return array|WP_Error Array containing 'headers', 'body', 'response', 'cookies', 'filename'. A WP_Error instance upon error + */ + private function _dispatch_request( $url, $args ) { + static $transports = array(); + + $class = $this->_get_first_available_transport( $args, $url ); + if ( !$class ) + return new WP_Error( 'http_failure', __( 'There are no HTTP transports available which can complete the requested request.' ) ); + + // Transport claims to support request, instantiate it and give it a whirl. + if ( empty( $transports[$class] ) ) + $transports[$class] = new $class; + + $response = $transports[$class]->request( $url, $args ); + + /** + * Fires after an HTTP API response is received and before the response is returned. + * + * @since 2.8.0 + * + * @param array|WP_Error $response HTTP response or WP_Error object. + * @param string $context Context under which the hook is fired. + * @param string $class HTTP transport used. + * @param array $args HTTP request arguments. + * @param string $url The request URL. + */ + do_action( 'http_api_debug', $response, 'response', $class, $args, $url ); + + if ( is_wp_error( $response ) ) + return $response; + + /** + * Filter the HTTP API response immediately before the response is returned. + * + * @since 2.9.0 + * + * @param array $response HTTP response. + * @param array $args HTTP request arguments. + * @param string $url The request URL. + */ + return apply_filters( 'http_response', $response, $args, $url ); + } + + /** + * Uses the POST HTTP method. + * + * Used for sending data that is expected to be in the body. + * + * @access public + * @since 2.7.0 + * + * @param string $url The request URL. + * @param string|array $args Optional. Override the defaults. + * @return array|WP_Error Array containing 'headers', 'body', 'response', 'cookies', 'filename'. A WP_Error instance upon error + */ + public function post($url, $args = array()) { + $defaults = array('method' => 'POST'); + $r = wp_parse_args( $args, $defaults ); + return $this->request($url, $r); + } + + /** + * Uses the GET HTTP method. + * + * Used for sending data that is expected to be in the body. + * + * @access public + * @since 2.7.0 + * + * @param string $url The request URL. + * @param string|array $args Optional. Override the defaults. + * @return array|WP_Error Array containing 'headers', 'body', 'response', 'cookies', 'filename'. A WP_Error instance upon error + */ + public function get($url, $args = array()) { + $defaults = array('method' => 'GET'); + $r = wp_parse_args( $args, $defaults ); + return $this->request($url, $r); + } + + /** + * Uses the HEAD HTTP method. + * + * Used for sending data that is expected to be in the body. + * + * @access public + * @since 2.7.0 + * + * @param string $url The request URL. + * @param string|array $args Optional. Override the defaults. + * @return array|WP_Error Array containing 'headers', 'body', 'response', 'cookies', 'filename'. A WP_Error instance upon error + */ + public function head($url, $args = array()) { + $defaults = array('method' => 'HEAD'); + $r = wp_parse_args( $args, $defaults ); + return $this->request($url, $r); + } + + /** + * Parses the responses and splits the parts into headers and body. + * + * @access public + * @static + * @since 2.7.0 + * + * @param string $strResponse The full response string + * @return array Array with 'headers' and 'body' keys. + */ + public static function processResponse($strResponse) { + $res = explode("\r\n\r\n", $strResponse, 2); + + return array('headers' => $res[0], 'body' => isset($res[1]) ? $res[1] : ''); + } + + /** + * Transform header string into an array. + * + * If an array is given then it is assumed to be raw header data with numeric keys with the + * headers as the values. No headers must be passed that were already processed. + * + * @access public + * @static + * @since 2.7.0 + * + * @param string|array $headers + * @param string $url The URL that was requested + * @return array Processed string headers. If duplicate headers are encountered, + * Then a numbered array is returned as the value of that header-key. + */ + public static function processHeaders( $headers, $url = '' ) { + // Split headers, one per array element. + if ( is_string($headers) ) { + // Tolerate line terminator: CRLF = LF (RFC 2616 19.3). + $headers = str_replace("\r\n", "\n", $headers); + /* + * Unfold folded header fields. LWS = [CRLF] 1*( SP | HT ) <US-ASCII SP, space (32)>, + * <US-ASCII HT, horizontal-tab (9)> (RFC 2616 2.2). + */ + $headers = preg_replace('/\n[ \t]/', ' ', $headers); + // Create the headers array. + $headers = explode("\n", $headers); + } + + $response = array('code' => 0, 'message' => ''); + + /* + * If a redirection has taken place, The headers for each page request may have been passed. + * In this case, determine the final HTTP header and parse from there. + */ + for ( $i = count($headers)-1; $i >= 0; $i-- ) { + if ( !empty($headers[$i]) && false === strpos($headers[$i], ':') ) { + $headers = array_splice($headers, $i); + break; + } + } + + $cookies = array(); + $newheaders = array(); + foreach ( (array) $headers as $tempheader ) { + if ( empty($tempheader) ) + continue; + + if ( false === strpos($tempheader, ':') ) { + $stack = explode(' ', $tempheader, 3); + $stack[] = ''; + list( , $response['code'], $response['message']) = $stack; + continue; + } + + list($key, $value) = explode(':', $tempheader, 2); + + $key = strtolower( $key ); + $value = trim( $value ); + + if ( isset( $newheaders[ $key ] ) ) { + if ( ! is_array( $newheaders[ $key ] ) ) + $newheaders[$key] = array( $newheaders[ $key ] ); + $newheaders[ $key ][] = $value; + } else { + $newheaders[ $key ] = $value; + } + if ( 'set-cookie' == $key ) + $cookies[] = new WP_Http_Cookie( $value, $url ); + } + + // Cast the Response Code to an int + $response['code'] = intval( $response['code'] ); + + return array('response' => $response, 'headers' => $newheaders, 'cookies' => $cookies); + } + + /** + * Takes the arguments for a ::request() and checks for the cookie array. + * + * If it's found, then it upgrades any basic name => value pairs to WP_Http_Cookie instances, + * which are each parsed into strings and added to the Cookie: header (within the arguments array). + * Edits the array by reference. + * + * @access public + * @version 2.8.0 + * @static + * + * @param array $r Full array of args passed into ::request() + */ + public static function buildCookieHeader( &$r ) { + if ( ! empty($r['cookies']) ) { + // Upgrade any name => value cookie pairs to WP_HTTP_Cookie instances. + foreach ( $r['cookies'] as $name => $value ) { + if ( ! is_object( $value ) ) + $r['cookies'][ $name ] = new WP_HTTP_Cookie( array( 'name' => $name, 'value' => $value ) ); + } + + $cookies_header = ''; + foreach ( (array) $r['cookies'] as $cookie ) { + $cookies_header .= $cookie->getHeaderValue() . '; '; + } + + $cookies_header = substr( $cookies_header, 0, -2 ); + $r['headers']['cookie'] = $cookies_header; + } + } + + /** + * Decodes chunk transfer-encoding, based off the HTTP 1.1 specification. + * + * Based off the HTTP http_encoding_dechunk function. + * + * @link http://tools.ietf.org/html/rfc2616#section-19.4.6 Process for chunked decoding. + * + * @access public + * @since 2.7.0 + * @static + * + * @param string $body Body content + * @return string Chunked decoded body on success or raw body on failure. + */ + public static function chunkTransferDecode( $body ) { + // The body is not chunked encoded or is malformed. + if ( ! preg_match( '/^([0-9a-f]+)[^\r\n]*\r\n/i', trim( $body ) ) ) + return $body; + + $parsed_body = ''; + + // We'll be altering $body, so need a backup in case of error. + $body_original = $body; + + while ( true ) { + $has_chunk = (bool) preg_match( '/^([0-9a-f]+)[^\r\n]*\r\n/i', $body, $match ); + if ( ! $has_chunk || empty( $match[1] ) ) + return $body_original; + + $length = hexdec( $match[1] ); + $chunk_length = strlen( $match[0] ); + + // Parse out the chunk of data. + $parsed_body .= substr( $body, $chunk_length, $length ); + + // Remove the chunk from the raw data. + $body = substr( $body, $length + $chunk_length ); + + // End of the document. + if ( '0' === trim( $body ) ) + return $parsed_body; + } + } + + /** + * Block requests through the proxy. + * + * Those who are behind a proxy and want to prevent access to certain hosts may do so. This will + * prevent plugins from working and core functionality, if you don't include api.wordpress.org. + * + * You block external URL requests by defining WP_HTTP_BLOCK_EXTERNAL as true in your wp-config.php + * file and this will only allow localhost and your blog to make requests. The constant + * WP_ACCESSIBLE_HOSTS will allow additional hosts to go through for requests. The format of the + * WP_ACCESSIBLE_HOSTS constant is a comma separated list of hostnames to allow, wildcard domains + * are supported, eg *.wordpress.org will allow for all subdomains of wordpress.org to be contacted. + * + * @since 2.8.0 + * @link https://core.trac.wordpress.org/ticket/8927 Allow preventing external requests. + * @link https://core.trac.wordpress.org/ticket/14636 Allow wildcard domains in WP_ACCESSIBLE_HOSTS + * + * @param string $uri URI of url. + * @return bool True to block, false to allow. + */ + public function block_request($uri) { + // We don't need to block requests, because nothing is blocked. + if ( ! defined( 'WP_HTTP_BLOCK_EXTERNAL' ) || ! WP_HTTP_BLOCK_EXTERNAL ) + return false; + + $check = parse_url($uri); + if ( ! $check ) + return true; + + $home = parse_url( get_option('siteurl') ); + + // Don't block requests back to ourselves by default. + if ( 'localhost' == $check['host'] || ( isset( $home['host'] ) && $home['host'] == $check['host'] ) ) { + /** + * Filter whether to block local requests through the proxy. + * + * @since 2.8.0 + * + * @param bool $block Whether to block local requests through proxy. + * Default false. + */ + return apply_filters( 'block_local_requests', false ); + } + + if ( !defined('WP_ACCESSIBLE_HOSTS') ) + return true; + + static $accessible_hosts; + static $wildcard_regex = false; + if ( null == $accessible_hosts ) { + $accessible_hosts = preg_split('|,\s*|', WP_ACCESSIBLE_HOSTS); + + if ( false !== strpos(WP_ACCESSIBLE_HOSTS, '*') ) { + $wildcard_regex = array(); + foreach ( $accessible_hosts as $host ) + $wildcard_regex[] = str_replace( '\*', '.+', preg_quote( $host, '/' ) ); + $wildcard_regex = '/^(' . implode('|', $wildcard_regex) . ')$/i'; + } + } + + if ( !empty($wildcard_regex) ) + return !preg_match($wildcard_regex, $check['host']); + else + return !in_array( $check['host'], $accessible_hosts ); //Inverse logic, If it's in the array, then we can't access it. + + } + + /** + * A wrapper for PHP's parse_url() function that handles edgecases in < PHP 5.4.7 + * + * PHP 5.4.7 expanded parse_url()'s ability to handle non-absolute url's, including + * schemeless and relative url's with :// in the path, this works around those + * limitations providing a standard output on PHP 5.2~5.4+. + * + * Error suppression is used as prior to PHP 5.3.3, an E_WARNING would be generated + * when URL parsing failed. + * + * @since 4.1.0 + * @access protected + * + * @param string $url The URL to parse. + * @return bool|array False on failure; Array of URL components on success; + * See parse_url()'s return values. + */ + protected static function parse_url( $url ) { + $parts = @parse_url( $url ); + if ( ! $parts ) { + // < PHP 5.4.7 compat, trouble with relative paths including a scheme break in the path + if ( '/' == $url[0] && false !== strpos( $url, '://' ) ) { + // Since we know it's a relative path, prefix with a scheme/host placeholder and try again + if ( ! $parts = @parse_url( 'placeholder://placeholder' . $url ) ) { + return $parts; + } + // Remove the placeholder values + unset( $parts['scheme'], $parts['host'] ); + } else { + return $parts; + } + } + + // < PHP 5.4.7 compat, doesn't detect schemeless URL's host field + if ( '//' == substr( $url, 0, 2 ) && ! isset( $parts['host'] ) ) { + list( $parts['host'], $slashless_path ) = explode( '/', substr( $parts['path'], 2 ), 2 ); + $parts['path'] = "/{$slashless_path}"; + } + + return $parts; + } + + /** + * Converts a relative URL to an absolute URL relative to a given URL. + * + * If an Absolute URL is provided, no processing of that URL is done. + * + * @since 3.4.0 + * + * @access public + * @param string $maybe_relative_path The URL which might be relative + * @param string $url The URL which $maybe_relative_path is relative to + * @return string An Absolute URL, in a failure condition where the URL cannot be parsed, the relative URL will be returned. + */ + public static function make_absolute_url( $maybe_relative_path, $url ) { + if ( empty( $url ) ) + return $maybe_relative_path; + + if ( ! $url_parts = WP_HTTP::parse_url( $url ) ) { + return $maybe_relative_path; + } + + if ( ! $relative_url_parts = WP_HTTP::parse_url( $maybe_relative_path ) ) { + return $maybe_relative_path; + } + + // Check for a scheme on the 'relative' url + if ( ! empty( $relative_url_parts['scheme'] ) ) { + return $maybe_relative_path; + } + + $absolute_path = $url_parts['scheme'] . '://'; + + // Schemeless URL's will make it this far, so we check for a host in the relative url and convert it to a protocol-url + if ( isset( $relative_url_parts['host'] ) ) { + $absolute_path .= $relative_url_parts['host']; + if ( isset( $relative_url_parts['port'] ) ) + $absolute_path .= ':' . $relative_url_parts['port']; + } else { + $absolute_path .= $url_parts['host']; + if ( isset( $url_parts['port'] ) ) + $absolute_path .= ':' . $url_parts['port']; + } + + // Start off with the Absolute URL path. + $path = ! empty( $url_parts['path'] ) ? $url_parts['path'] : '/'; + + // If it's a root-relative path, then great. + if ( ! empty( $relative_url_parts['path'] ) && '/' == $relative_url_parts['path'][0] ) { + $path = $relative_url_parts['path']; + + // Else it's a relative path. + } elseif ( ! empty( $relative_url_parts['path'] ) ) { + // Strip off any file components from the absolute path. + $path = substr( $path, 0, strrpos( $path, '/' ) + 1 ); + + // Build the new path. + $path .= $relative_url_parts['path']; + + // Strip all /path/../ out of the path. + while ( strpos( $path, '../' ) > 1 ) { + $path = preg_replace( '![^/]+/\.\./!', '', $path ); + } + + // Strip any final leading ../ from the path. + $path = preg_replace( '!^/(\.\./)+!', '', $path ); + } + + // Add the Query string. + if ( ! empty( $relative_url_parts['query'] ) ) + $path .= '?' . $relative_url_parts['query']; + + return $absolute_path . '/' . ltrim( $path, '/' ); + } + + /** + * Handles HTTP Redirects and follows them if appropriate. + * + * @since 3.7.0 + * + * @param string $url The URL which was requested. + * @param array $args The Arguments which were used to make the request. + * @param array $response The Response of the HTTP request. + * @return false|object False if no redirect is present, a WP_HTTP or WP_Error result otherwise. + */ + public static function handle_redirects( $url, $args, $response ) { + // If no redirects are present, or, redirects were not requested, perform no action. + if ( ! isset( $response['headers']['location'] ) || 0 === $args['_redirection'] ) + return false; + + // Only perform redirections on redirection http codes. + if ( $response['response']['code'] > 399 || $response['response']['code'] < 300 ) + return false; + + // Don't redirect if we've run out of redirects. + if ( $args['redirection']-- <= 0 ) + return new WP_Error( 'http_request_failed', __('Too many redirects.') ); + + $redirect_location = $response['headers']['location']; + + // If there were multiple Location headers, use the last header specified. + if ( is_array( $redirect_location ) ) + $redirect_location = array_pop( $redirect_location ); + + $redirect_location = WP_HTTP::make_absolute_url( $redirect_location, $url ); + + // POST requests should not POST to a redirected location. + if ( 'POST' == $args['method'] ) { + if ( in_array( $response['response']['code'], array( 302, 303 ) ) ) + $args['method'] = 'GET'; + } + + // Include valid cookies in the redirect process. + if ( ! empty( $response['cookies'] ) ) { + foreach ( $response['cookies'] as $cookie ) { + if ( $cookie->test( $redirect_location ) ) + $args['cookies'][] = $cookie; + } + } + + return wp_remote_request( $redirect_location, $args ); + } + + /** + * Determines if a specified string represents an IP address or not. + * + * This function also detects the type of the IP address, returning either + * '4' or '6' to represent a IPv4 and IPv6 address respectively. + * This does not verify if the IP is a valid IP, only that it appears to be + * an IP address. + * + * @see http://home.deds.nl/~aeron/regex/ for IPv6 regex + * + * @since 3.7.0 + * @static + * + * @param string $maybe_ip A suspected IP address + * @return integer|bool Upon success, '4' or '6' to represent a IPv4 or IPv6 address, false upon failure + */ + public static function is_ip_address( $maybe_ip ) { + if ( preg_match( '/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/', $maybe_ip ) ) + return 4; + + if ( false !== strpos( $maybe_ip, ':' ) && preg_match( '/^(((?=.*(::))(?!.*\3.+\3))\3?|([\dA-F]{1,4}(\3|:\b|$)|\2))(?4){5}((?4){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4})$/i', trim( $maybe_ip, ' []' ) ) ) + return 6; + + return false; + } + +} + +/** + * HTTP request method uses PHP Streams to retrieve the url. + * + * @since 2.7.0 + * @since 3.7.0 Combined with the fsockopen transport and switched to stream_socket_client(). + */ +class WP_Http_Streams { + /** + * Send a HTTP request to a URI using PHP Streams. + * + * @see WP_Http::request For default options descriptions. + * + * @since 2.7.0 + * @since 3.7.0 Combined with the fsockopen transport and switched to stream_socket_client(). + * + * @access public + * @param string $url The request URL. + * @param string|array $args Optional. Override the defaults. + * @return array|WP_Error Array containing 'headers', 'body', 'response', 'cookies', 'filename'. A WP_Error instance upon error + */ + public function request($url, $args = array()) { + $defaults = array( + 'method' => 'GET', 'timeout' => 5, + 'redirection' => 5, 'httpversion' => '1.0', + 'blocking' => true, + 'headers' => array(), 'body' => null, 'cookies' => array() + ); + + $r = wp_parse_args( $args, $defaults ); + + if ( isset($r['headers']['User-Agent']) ) { + $r['user-agent'] = $r['headers']['User-Agent']; + unset($r['headers']['User-Agent']); + } else if ( isset($r['headers']['user-agent']) ) { + $r['user-agent'] = $r['headers']['user-agent']; + unset($r['headers']['user-agent']); + } + + // Construct Cookie: header if any cookies are set. + WP_Http::buildCookieHeader( $r ); + + $arrURL = parse_url($url); + + $connect_host = $arrURL['host']; + + $secure_transport = ( $arrURL['scheme'] == 'ssl' || $arrURL['scheme'] == 'https' ); + if ( ! isset( $arrURL['port'] ) ) { + if ( $arrURL['scheme'] == 'ssl' || $arrURL['scheme'] == 'https' ) { + $arrURL['port'] = 443; + $secure_transport = true; + } else { + $arrURL['port'] = 80; + } + } + + // Always pass a Path, defaulting to the root in cases such as http://example.com + if ( ! isset( $arrURL['path'] ) ) { + $arrURL['path'] = '/'; + } + + if ( isset( $r['headers']['Host'] ) || isset( $r['headers']['host'] ) ) { + if ( isset( $r['headers']['Host'] ) ) + $arrURL['host'] = $r['headers']['Host']; + else + $arrURL['host'] = $r['headers']['host']; + unset( $r['headers']['Host'], $r['headers']['host'] ); + } + + /* + * Certain versions of PHP have issues with 'localhost' and IPv6, It attempts to connect + * to ::1, which fails when the server is not set up for it. For compatibility, always + * connect to the IPv4 address. + */ + if ( 'localhost' == strtolower( $connect_host ) ) + $connect_host = '127.0.0.1'; + + $connect_host = $secure_transport ? 'ssl://' . $connect_host : 'tcp://' . $connect_host; + + $is_local = isset( $r['local'] ) && $r['local']; + $ssl_verify = isset( $r['sslverify'] ) && $r['sslverify']; + if ( $is_local ) { + /** + * Filter whether SSL should be verified for local requests. + * + * @since 2.8.0 + * + * @param bool $ssl_verify Whether to verify the SSL connection. Default true. + */ + $ssl_verify = apply_filters( 'https_local_ssl_verify', $ssl_verify ); + } elseif ( ! $is_local ) { + /** + * Filter whether SSL should be verified for non-local requests. + * + * @since 2.8.0 + * + * @param bool $ssl_verify Whether to verify the SSL connection. Default true. + */ + $ssl_verify = apply_filters( 'https_ssl_verify', $ssl_verify ); + } + + $proxy = new WP_HTTP_Proxy(); + + $context = stream_context_create( array( + 'ssl' => array( + 'verify_peer' => $ssl_verify, + //'CN_match' => $arrURL['host'], // This is handled by self::verify_ssl_certificate() + 'capture_peer_cert' => $ssl_verify, + 'SNI_enabled' => true, + 'cafile' => $r['sslcertificates'], + 'allow_self_signed' => ! $ssl_verify, + ) + ) ); + + $timeout = (int) floor( $r['timeout'] ); + $utimeout = $timeout == $r['timeout'] ? 0 : 1000000 * $r['timeout'] % 1000000; + $connect_timeout = max( $timeout, 1 ); + + // Store error number. + $connection_error = null; + + // Store error string. + $connection_error_str = null; + + if ( !WP_DEBUG ) { + // In the event that the SSL connection fails, silence the many PHP Warnings. + if ( $secure_transport ) + $error_reporting = error_reporting(0); + + if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) + $handle = @stream_socket_client( 'tcp://' . $proxy->host() . ':' . $proxy->port(), $connection_error, $connection_error_str, $connect_timeout, STREAM_CLIENT_CONNECT, $context ); + else + $handle = @stream_socket_client( $connect_host . ':' . $arrURL['port'], $connection_error, $connection_error_str, $connect_timeout, STREAM_CLIENT_CONNECT, $context ); + + if ( $secure_transport ) + error_reporting( $error_reporting ); + + } else { + if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) + $handle = stream_socket_client( 'tcp://' . $proxy->host() . ':' . $proxy->port(), $connection_error, $connection_error_str, $connect_timeout, STREAM_CLIENT_CONNECT, $context ); + else + $handle = stream_socket_client( $connect_host . ':' . $arrURL['port'], $connection_error, $connection_error_str, $connect_timeout, STREAM_CLIENT_CONNECT, $context ); + } + + if ( false === $handle ) { + // SSL connection failed due to expired/invalid cert, or, OpenSSL configuration is broken. + if ( $secure_transport && 0 === $connection_error && '' === $connection_error_str ) + return new WP_Error( 'http_request_failed', __( 'The SSL certificate for the host could not be verified.' ) ); + + return new WP_Error('http_request_failed', $connection_error . ': ' . $connection_error_str ); + } + + // Verify that the SSL certificate is valid for this request. + if ( $secure_transport && $ssl_verify && ! $proxy->is_enabled() ) { + if ( ! self::verify_ssl_certificate( $handle, $arrURL['host'] ) ) + return new WP_Error( 'http_request_failed', __( 'The SSL certificate for the host could not be verified.' ) ); + } + + stream_set_timeout( $handle, $timeout, $utimeout ); + + if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) //Some proxies require full URL in this field. + $requestPath = $url; + else + $requestPath = $arrURL['path'] . ( isset($arrURL['query']) ? '?' . $arrURL['query'] : '' ); + + $strHeaders = strtoupper($r['method']) . ' ' . $requestPath . ' HTTP/' . $r['httpversion'] . "\r\n"; + + $include_port_in_host_header = ( + ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) || + ( 'http' == $arrURL['scheme'] && 80 != $arrURL['port'] ) || + ( 'https' == $arrURL['scheme'] && 443 != $arrURL['port'] ) + ); + + if ( $include_port_in_host_header ) { + $strHeaders .= 'Host: ' . $arrURL['host'] . ':' . $arrURL['port'] . "\r\n"; + } else { + $strHeaders .= 'Host: ' . $arrURL['host'] . "\r\n"; + } + + if ( isset($r['user-agent']) ) + $strHeaders .= 'User-agent: ' . $r['user-agent'] . "\r\n"; + + if ( is_array($r['headers']) ) { + foreach ( (array) $r['headers'] as $header => $headerValue ) + $strHeaders .= $header . ': ' . $headerValue . "\r\n"; + } else { + $strHeaders .= $r['headers']; + } + + if ( $proxy->use_authentication() ) + $strHeaders .= $proxy->authentication_header() . "\r\n"; + + $strHeaders .= "\r\n"; + + if ( ! is_null($r['body']) ) + $strHeaders .= $r['body']; + + fwrite($handle, $strHeaders); + + if ( ! $r['blocking'] ) { + stream_set_blocking( $handle, 0 ); + fclose( $handle ); + return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() ); + } + + $strResponse = ''; + $bodyStarted = false; + $keep_reading = true; + $block_size = 4096; + if ( isset( $r['limit_response_size'] ) ) + $block_size = min( $block_size, $r['limit_response_size'] ); + + // If streaming to a file setup the file handle. + if ( $r['stream'] ) { + if ( ! WP_DEBUG ) + $stream_handle = @fopen( $r['filename'], 'w+' ); + else + $stream_handle = fopen( $r['filename'], 'w+' ); + if ( ! $stream_handle ) + return new WP_Error( 'http_request_failed', sprintf( __( 'Could not open handle for fopen() to %s' ), $r['filename'] ) ); + + $bytes_written = 0; + while ( ! feof($handle) && $keep_reading ) { + $block = fread( $handle, $block_size ); + if ( ! $bodyStarted ) { + $strResponse .= $block; + if ( strpos( $strResponse, "\r\n\r\n" ) ) { + $process = WP_Http::processResponse( $strResponse ); + $bodyStarted = true; + $block = $process['body']; + unset( $strResponse ); + $process['body'] = ''; + } + } + + $this_block_size = strlen( $block ); + + if ( isset( $r['limit_response_size'] ) && ( $bytes_written + $this_block_size ) > $r['limit_response_size'] ) { + $this_block_size = ( $r['limit_response_size'] - $bytes_written ); + $block = substr( $block, 0, $this_block_size ); + } + + $bytes_written_to_file = fwrite( $stream_handle, $block ); + + if ( $bytes_written_to_file != $this_block_size ) { + fclose( $handle ); + fclose( $stream_handle ); + return new WP_Error( 'http_request_failed', __( 'Failed to write request to temporary file.' ) ); + } + + $bytes_written += $bytes_written_to_file; + + $keep_reading = !isset( $r['limit_response_size'] ) || $bytes_written < $r['limit_response_size']; + } + + fclose( $stream_handle ); + + } else { + $header_length = 0; + while ( ! feof( $handle ) && $keep_reading ) { + $block = fread( $handle, $block_size ); + $strResponse .= $block; + if ( ! $bodyStarted && strpos( $strResponse, "\r\n\r\n" ) ) { + $header_length = strpos( $strResponse, "\r\n\r\n" ) + 4; + $bodyStarted = true; + } + $keep_reading = ( ! $bodyStarted || !isset( $r['limit_response_size'] ) || strlen( $strResponse ) < ( $header_length + $r['limit_response_size'] ) ); + } + + $process = WP_Http::processResponse( $strResponse ); + unset( $strResponse ); + + } + + fclose( $handle ); + + $arrHeaders = WP_Http::processHeaders( $process['headers'], $url ); + + $response = array( + 'headers' => $arrHeaders['headers'], + // Not yet processed. + 'body' => null, + 'response' => $arrHeaders['response'], + 'cookies' => $arrHeaders['cookies'], + 'filename' => $r['filename'] + ); + + // Handle redirects. + if ( false !== ( $redirect_response = WP_HTTP::handle_redirects( $url, $r, $response ) ) ) + return $redirect_response; + + // If the body was chunk encoded, then decode it. + if ( ! empty( $process['body'] ) && isset( $arrHeaders['headers']['transfer-encoding'] ) && 'chunked' == $arrHeaders['headers']['transfer-encoding'] ) + $process['body'] = WP_Http::chunkTransferDecode($process['body']); + + if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($arrHeaders['headers']) ) + $process['body'] = WP_Http_Encoding::decompress( $process['body'] ); + + if ( isset( $r['limit_response_size'] ) && strlen( $process['body'] ) > $r['limit_response_size'] ) + $process['body'] = substr( $process['body'], 0, $r['limit_response_size'] ); + + $response['body'] = $process['body']; + + return $response; + } + + /** + * Verifies the received SSL certificate against it's Common Names and subjectAltName fields + * + * PHP's SSL verifications only verify that it's a valid Certificate, it doesn't verify if + * the certificate is valid for the hostname which was requested. + * This function verifies the requested hostname against certificate's subjectAltName field, + * if that is empty, or contains no DNS entries, a fallback to the Common Name field is used. + * + * IP Address support is included if the request is being made to an IP address. + * + * @since 3.7.0 + * @static + * + * @param stream $stream The PHP Stream which the SSL request is being made over + * @param string $host The hostname being requested + * @return bool If the cerficiate presented in $stream is valid for $host + */ + public static function verify_ssl_certificate( $stream, $host ) { + $context_options = stream_context_get_options( $stream ); + + if ( empty( $context_options['ssl']['peer_certificate'] ) ) + return false; + + $cert = openssl_x509_parse( $context_options['ssl']['peer_certificate'] ); + if ( ! $cert ) + return false; + + /* + * If the request is being made to an IP address, we'll validate against IP fields + * in the cert (if they exist) + */ + $host_type = ( WP_HTTP::is_ip_address( $host ) ? 'ip' : 'dns' ); + + $certificate_hostnames = array(); + if ( ! empty( $cert['extensions']['subjectAltName'] ) ) { + $match_against = preg_split( '/,\s*/', $cert['extensions']['subjectAltName'] ); + foreach ( $match_against as $match ) { + list( $match_type, $match_host ) = explode( ':', $match ); + if ( $host_type == strtolower( trim( $match_type ) ) ) // IP: or DNS: + $certificate_hostnames[] = strtolower( trim( $match_host ) ); + } + } elseif ( !empty( $cert['subject']['CN'] ) ) { + // Only use the CN when the certificate includes no subjectAltName extension. + $certificate_hostnames[] = strtolower( $cert['subject']['CN'] ); + } + + // Exact hostname/IP matches. + if ( in_array( strtolower( $host ), $certificate_hostnames ) ) + return true; + + // IP's can't be wildcards, Stop processing. + if ( 'ip' == $host_type ) + return false; + + // Test to see if the domain is at least 2 deep for wildcard support. + if ( substr_count( $host, '.' ) < 2 ) + return false; + + // Wildcard subdomains certs (*.example.com) are valid for a.example.com but not a.b.example.com. + $wildcard_host = preg_replace( '/^[^.]+\./', '*.', $host ); + + return in_array( strtolower( $wildcard_host ), $certificate_hostnames ); + } + + /** + * Whether this class can be used for retrieving a URL. + * + * @static + * @access public + * @since 2.7.0 + * @since 3.7.0 Combined with the fsockopen transport and switched to stream_socket_client(). + * + * @return boolean False means this class can not be used, true means it can. + */ + public static function test( $args = array() ) { + if ( ! function_exists( 'stream_socket_client' ) ) + return false; + + $is_ssl = isset( $args['ssl'] ) && $args['ssl']; + + if ( $is_ssl ) { + if ( ! extension_loaded( 'openssl' ) ) + return false; + if ( ! function_exists( 'openssl_x509_parse' ) ) + return false; + } + + /** + * Filter whether streams can be used as a transport for retrieving a URL. + * + * @since 2.7.0 + * + * @param bool $use_class Whether the class can be used. Default true. + * @param array $args Request arguments. + */ + return apply_filters( 'use_streams_transport', true, $args ); + } +} + +/** + * Deprecated HTTP Transport method which used fsockopen. + * + * This class is not used, and is included for backwards compatibility only. + * All code should make use of WP_HTTP directly through it's API. + * + * @see WP_HTTP::request + * + * @since 2.7.0 + * @deprecated 3.7.0 Please use WP_HTTP::request() directly + */ +class WP_HTTP_Fsockopen extends WP_HTTP_Streams { + // For backwards compatibility for users who are using the class directly. +} + +/** + * HTTP request method uses Curl extension to retrieve the url. + * + * Requires the Curl extension to be installed. + * + * @package WordPress + * @subpackage HTTP + * @since 2.7.0 + */ +class WP_Http_Curl { + + /** + * Temporary header storage for during requests. + * + * @since 3.2.0 + * @access private + * @var string + */ + private $headers = ''; + + /** + * Temporary body storage for during requests. + * + * @since 3.6.0 + * @access private + * @var string + */ + private $body = ''; + + /** + * The maximum amount of data to receive from the remote server. + * + * @since 3.6.0 + * @access private + * @var int + */ + private $max_body_length = false; + + /** + * The file resource used for streaming to file. + * + * @since 3.6.0 + * @access private + * @var resource + */ + private $stream_handle = false; + + /** + * The total bytes written in the current request. + * + * @since 4.1.0 + * @access private + * @var int + */ + private $bytes_written_total = 0; + + /** + * Send a HTTP request to a URI using cURL extension. + * + * @access public + * @since 2.7.0 + * + * @param string $url The request URL. + * @param string|array $args Optional. Override the defaults. + * @return array|WP_Error Array containing 'headers', 'body', 'response', 'cookies', 'filename'. A WP_Error instance upon error + */ + public function request($url, $args = array()) { + $defaults = array( + 'method' => 'GET', 'timeout' => 5, + 'redirection' => 5, 'httpversion' => '1.0', + 'blocking' => true, + 'headers' => array(), 'body' => null, 'cookies' => array() + ); + + $r = wp_parse_args( $args, $defaults ); + + if ( isset($r['headers']['User-Agent']) ) { + $r['user-agent'] = $r['headers']['User-Agent']; + unset($r['headers']['User-Agent']); + } else if ( isset($r['headers']['user-agent']) ) { + $r['user-agent'] = $r['headers']['user-agent']; + unset($r['headers']['user-agent']); + } + + // Construct Cookie: header if any cookies are set. + WP_Http::buildCookieHeader( $r ); + + $handle = curl_init(); + + // cURL offers really easy proxy support. + $proxy = new WP_HTTP_Proxy(); + + if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) { + + curl_setopt( $handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP ); + curl_setopt( $handle, CURLOPT_PROXY, $proxy->host() ); + curl_setopt( $handle, CURLOPT_PROXYPORT, $proxy->port() ); + + if ( $proxy->use_authentication() ) { + curl_setopt( $handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY ); + curl_setopt( $handle, CURLOPT_PROXYUSERPWD, $proxy->authentication() ); + } + } + + $is_local = isset($r['local']) && $r['local']; + $ssl_verify = isset($r['sslverify']) && $r['sslverify']; + if ( $is_local ) { + /** This filter is documented in wp-includes/class-http.php */ + $ssl_verify = apply_filters( 'https_local_ssl_verify', $ssl_verify ); + } elseif ( ! $is_local ) { + /** This filter is documented in wp-includes/class-http.php */ + $ssl_verify = apply_filters( 'https_ssl_verify', $ssl_verify ); + } + + /* + * CURLOPT_TIMEOUT and CURLOPT_CONNECTTIMEOUT expect integers. Have to use ceil since. + * a value of 0 will allow an unlimited timeout. + */ + $timeout = (int) ceil( $r['timeout'] ); + curl_setopt( $handle, CURLOPT_CONNECTTIMEOUT, $timeout ); + curl_setopt( $handle, CURLOPT_TIMEOUT, $timeout ); + + curl_setopt( $handle, CURLOPT_URL, $url); + curl_setopt( $handle, CURLOPT_RETURNTRANSFER, true ); + curl_setopt( $handle, CURLOPT_SSL_VERIFYHOST, ( $ssl_verify === true ) ? 2 : false ); + curl_setopt( $handle, CURLOPT_SSL_VERIFYPEER, $ssl_verify ); + curl_setopt( $handle, CURLOPT_CAINFO, $r['sslcertificates'] ); + curl_setopt( $handle, CURLOPT_USERAGENT, $r['user-agent'] ); + + /* + * The option doesn't work with safe mode or when open_basedir is set, and there's + * a bug #17490 with redirected POST requests, so handle redirections outside Curl. + */ + curl_setopt( $handle, CURLOPT_FOLLOWLOCATION, false ); + if ( defined( 'CURLOPT_PROTOCOLS' ) ) // PHP 5.2.10 / cURL 7.19.4 + curl_setopt( $handle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS ); + + switch ( $r['method'] ) { + case 'HEAD': + curl_setopt( $handle, CURLOPT_NOBODY, true ); + break; + case 'POST': + curl_setopt( $handle, CURLOPT_POST, true ); + curl_setopt( $handle, CURLOPT_POSTFIELDS, $r['body'] ); + break; + case 'PUT': + curl_setopt( $handle, CURLOPT_CUSTOMREQUEST, 'PUT' ); + curl_setopt( $handle, CURLOPT_POSTFIELDS, $r['body'] ); + break; + default: + curl_setopt( $handle, CURLOPT_CUSTOMREQUEST, $r['method'] ); + if ( ! is_null( $r['body'] ) ) + curl_setopt( $handle, CURLOPT_POSTFIELDS, $r['body'] ); + break; + } + + if ( true === $r['blocking'] ) { + curl_setopt( $handle, CURLOPT_HEADERFUNCTION, array( $this, 'stream_headers' ) ); + curl_setopt( $handle, CURLOPT_WRITEFUNCTION, array( $this, 'stream_body' ) ); + } + + curl_setopt( $handle, CURLOPT_HEADER, false ); + + if ( isset( $r['limit_response_size'] ) ) + $this->max_body_length = intval( $r['limit_response_size'] ); + else + $this->max_body_length = false; + + // If streaming to a file open a file handle, and setup our curl streaming handler. + if ( $r['stream'] ) { + if ( ! WP_DEBUG ) + $this->stream_handle = @fopen( $r['filename'], 'w+' ); + else + $this->stream_handle = fopen( $r['filename'], 'w+' ); + if ( ! $this->stream_handle ) + return new WP_Error( 'http_request_failed', sprintf( __( 'Could not open handle for fopen() to %s' ), $r['filename'] ) ); + } else { + $this->stream_handle = false; + } + + if ( !empty( $r['headers'] ) ) { + // cURL expects full header strings in each element. + $headers = array(); + foreach ( $r['headers'] as $name => $value ) { + $headers[] = "{$name}: $value"; + } + curl_setopt( $handle, CURLOPT_HTTPHEADER, $headers ); + } + + if ( $r['httpversion'] == '1.0' ) + curl_setopt( $handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0 ); + else + curl_setopt( $handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1 ); + + /** + * Fires before the cURL request is executed. + * + * Cookies are not currently handled by the HTTP API. This action allows + * plugins to handle cookies themselves. + * + * @since 2.8.0 + * + * @param resource &$handle The cURL handle returned by curl_init(). + * @param array $r The HTTP request arguments. + * @param string $url The request URL. + */ + do_action_ref_array( 'http_api_curl', array( &$handle, $r, $url ) ); + + // We don't need to return the body, so don't. Just execute request and return. + if ( ! $r['blocking'] ) { + curl_exec( $handle ); + + if ( $curl_error = curl_error( $handle ) ) { + curl_close( $handle ); + return new WP_Error( 'http_request_failed', $curl_error ); + } + if ( in_array( curl_getinfo( $handle, CURLINFO_HTTP_CODE ), array( 301, 302 ) ) ) { + curl_close( $handle ); + return new WP_Error( 'http_request_failed', __( 'Too many redirects.' ) ); + } + + curl_close( $handle ); + return array( 'headers' => array(), 'body' => '', 'response' => array('code' => false, 'message' => false), 'cookies' => array() ); + } + + curl_exec( $handle ); + $theHeaders = WP_Http::processHeaders( $this->headers, $url ); + $theBody = $this->body; + $bytes_written_total = $this->bytes_written_total; + + $this->headers = ''; + $this->body = ''; + $this->bytes_written_total = 0; + + $curl_error = curl_errno( $handle ); + + // If an error occurred, or, no response. + if ( $curl_error || ( 0 == strlen( $theBody ) && empty( $theHeaders['headers'] ) ) ) { + if ( CURLE_WRITE_ERROR /* 23 */ == $curl_error && $r['stream'] ) { + if ( ! $this->max_body_length || $this->max_body_length != $bytes_written_total ) { + fclose( $this->stream_handle ); + return new WP_Error( 'http_request_failed', __( 'Failed to write request to temporary file.' ) ); + } + } else { + if ( $curl_error = curl_error( $handle ) ) { + curl_close( $handle ); + return new WP_Error( 'http_request_failed', $curl_error ); + } + } + if ( in_array( curl_getinfo( $handle, CURLINFO_HTTP_CODE ), array( 301, 302 ) ) ) { + curl_close( $handle ); + return new WP_Error( 'http_request_failed', __( 'Too many redirects.' ) ); + } + } + + curl_close( $handle ); + + if ( $r['stream'] ) + fclose( $this->stream_handle ); + + $response = array( + 'headers' => $theHeaders['headers'], + 'body' => null, + 'response' => $theHeaders['response'], + 'cookies' => $theHeaders['cookies'], + 'filename' => $r['filename'] + ); + + // Handle redirects. + if ( false !== ( $redirect_response = WP_HTTP::handle_redirects( $url, $r, $response ) ) ) + return $redirect_response; + + if ( true === $r['decompress'] && true === WP_Http_Encoding::should_decode($theHeaders['headers']) ) + $theBody = WP_Http_Encoding::decompress( $theBody ); + + $response['body'] = $theBody; + + return $response; + } + + /** + * Grab the headers of the cURL request + * + * Each header is sent individually to this callback, so we append to the $header property for temporary storage + * + * @since 3.2.0 + * @access private + * @return int + */ + private function stream_headers( $handle, $headers ) { + $this->headers .= $headers; + return strlen( $headers ); + } + + /** + * Grab the body of the cURL request + * + * The contents of the document are passed in chunks, so we append to the $body property for temporary storage. + * Returning a length shorter than the length of $data passed in will cause cURL to abort the request with CURLE_WRITE_ERROR + * + * @since 3.6.0 + * @access private + * @return int + */ + private function stream_body( $handle, $data ) { + $data_length = strlen( $data ); + + if ( $this->max_body_length && ( $this->bytes_written_total + $data_length ) > $this->max_body_length ) { + $data_length = ( $this->max_body_length - $this->bytes_written_total ); + $data = substr( $data, 0, $data_length ); + } + + if ( $this->stream_handle ) { + $bytes_written = fwrite( $this->stream_handle, $data ); + } else { + $this->body .= $data; + $bytes_written = $data_length; + } + + $this->bytes_written_total += $bytes_written; + + // Upon event of this function returning less than strlen( $data ) curl will error with CURLE_WRITE_ERROR. + return $bytes_written; + } + + /** + * Whether this class can be used for retrieving an URL. + * + * @static + * @since 2.7.0 + * + * @return boolean False means this class can not be used, true means it can. + */ + public static function test( $args = array() ) { + if ( ! function_exists( 'curl_init' ) || ! function_exists( 'curl_exec' ) ) + return false; + + $is_ssl = isset( $args['ssl'] ) && $args['ssl']; + + if ( $is_ssl ) { + $curl_version = curl_version(); + // Check whether this cURL version support SSL requests. + if ( ! (CURL_VERSION_SSL & $curl_version['features']) ) + return false; + } + + /** + * Filter whether cURL can be used as a transport for retrieving a URL. + * + * @since 2.7.0 + * + * @param bool $use_class Whether the class can be used. Default true. + * @param array $args An array of request arguments. + */ + return apply_filters( 'use_curl_transport', true, $args ); + } +} + +/** + * Adds Proxy support to the WordPress HTTP API. + * + * There are caveats to proxy support. It requires that defines be made in the wp-config.php file to + * enable proxy support. There are also a few filters that plugins can hook into for some of the + * constants. + * + * Please note that only BASIC authentication is supported by most transports. + * cURL MAY support more methods (such as NTLM authentication) depending on your environment. + * + * The constants are as follows: + * <ol> + * <li>WP_PROXY_HOST - Enable proxy support and host for connecting.</li> + * <li>WP_PROXY_PORT - Proxy port for connection. No default, must be defined.</li> + * <li>WP_PROXY_USERNAME - Proxy username, if it requires authentication.</li> + * <li>WP_PROXY_PASSWORD - Proxy password, if it requires authentication.</li> + * <li>WP_PROXY_BYPASS_HOSTS - Will prevent the hosts in this list from going through the proxy. + * You do not need to have localhost and the blog host in this list, because they will not be passed + * through the proxy. The list should be presented in a comma separated list, wildcards using * are supported, eg. *.wordpress.org</li> + * </ol> + * + * An example can be as seen below. + * + * define('WP_PROXY_HOST', '192.168.84.101'); + * define('WP_PROXY_PORT', '8080'); + * define('WP_PROXY_BYPASS_HOSTS', 'localhost, www.example.com, *.wordpress.org'); + * + * @link https://core.trac.wordpress.org/ticket/4011 Proxy support ticket in WordPress. + * @link https://core.trac.wordpress.org/ticket/14636 Allow wildcard domains in WP_PROXY_BYPASS_HOSTS + * @since 2.8.0 + */ +class WP_HTTP_Proxy { + + /** + * Whether proxy connection should be used. + * + * @since 2.8.0 + * + * @use WP_PROXY_HOST + * @use WP_PROXY_PORT + * + * @return bool + */ + public function is_enabled() { + return defined('WP_PROXY_HOST') && defined('WP_PROXY_PORT'); + } + + /** + * Whether authentication should be used. + * + * @since 2.8.0 + * + * @use WP_PROXY_USERNAME + * @use WP_PROXY_PASSWORD + * + * @return bool + */ + public function use_authentication() { + return defined('WP_PROXY_USERNAME') && defined('WP_PROXY_PASSWORD'); + } + + /** + * Retrieve the host for the proxy server. + * + * @since 2.8.0 + * + * @return string + */ + public function host() { + if ( defined('WP_PROXY_HOST') ) + return WP_PROXY_HOST; + + return ''; + } + + /** + * Retrieve the port for the proxy server. + * + * @since 2.8.0 + * + * @return string + */ + public function port() { + if ( defined('WP_PROXY_PORT') ) + return WP_PROXY_PORT; + + return ''; + } + + /** + * Retrieve the username for proxy authentication. + * + * @since 2.8.0 + * + * @return string + */ + public function username() { + if ( defined('WP_PROXY_USERNAME') ) + return WP_PROXY_USERNAME; + + return ''; + } + + /** + * Retrieve the password for proxy authentication. + * + * @since 2.8.0 + * + * @return string + */ + public function password() { + if ( defined('WP_PROXY_PASSWORD') ) + return WP_PROXY_PASSWORD; + + return ''; + } + + /** + * Retrieve authentication string for proxy authentication. + * + * @since 2.8.0 + * + * @return string + */ + public function authentication() { + return $this->username() . ':' . $this->password(); + } + + /** + * Retrieve header string for proxy authentication. + * + * @since 2.8.0 + * + * @return string + */ + public function authentication_header() { + return 'Proxy-Authorization: Basic ' . base64_encode( $this->authentication() ); + } + + /** + * Whether URL should be sent through the proxy server. + * + * We want to keep localhost and the blog URL from being sent through the proxy server, because + * some proxies can not handle this. We also have the constant available for defining other + * hosts that won't be sent through the proxy. + * + * @since 2.8.0 + * + * @param string $uri URI to check. + * @return bool True, to send through the proxy and false if, the proxy should not be used. + */ + public function send_through_proxy( $uri ) { + /* + * parse_url() only handles http, https type URLs, and will emit E_WARNING on failure. + * This will be displayed on blogs, which is not reasonable. + */ + $check = @parse_url($uri); + + // Malformed URL, can not process, but this could mean ssl, so let through anyway. + if ( $check === false ) + return true; + + $home = parse_url( get_option('siteurl') ); + + /** + * Filter whether to preempt sending the request through the proxy server. + * + * Returning false will bypass the proxy; returning true will send + * the request through the proxy. Returning null bypasses the filter. + * + * @since 3.5.0 + * + * @param null $override Whether to override the request result. Default null. + * @param string $uri URL to check. + * @param array $check Associative array result of parsing the URI. + * @param array $home Associative array result of parsing the site URL. + */ + $result = apply_filters( 'pre_http_send_through_proxy', null, $uri, $check, $home ); + if ( ! is_null( $result ) ) + return $result; + + if ( 'localhost' == $check['host'] || ( isset( $home['host'] ) && $home['host'] == $check['host'] ) ) + return false; + + if ( !defined('WP_PROXY_BYPASS_HOSTS') ) + return true; + + static $bypass_hosts; + static $wildcard_regex = false; + if ( null == $bypass_hosts ) { + $bypass_hosts = preg_split('|,\s*|', WP_PROXY_BYPASS_HOSTS); + + if ( false !== strpos(WP_PROXY_BYPASS_HOSTS, '*') ) { + $wildcard_regex = array(); + foreach ( $bypass_hosts as $host ) + $wildcard_regex[] = str_replace( '\*', '.+', preg_quote( $host, '/' ) ); + $wildcard_regex = '/^(' . implode('|', $wildcard_regex) . ')$/i'; + } + } + + if ( !empty($wildcard_regex) ) + return !preg_match($wildcard_regex, $check['host']); + else + return !in_array( $check['host'], $bypass_hosts ); + } +} +/** + * Internal representation of a single cookie. + * + * Returned cookies are represented using this class, and when cookies are set, if they are not + * already a WP_Http_Cookie() object, then they are turned into one. + * + * @todo The WordPress convention is to use underscores instead of camelCase for function and method + * names. Need to switch to use underscores instead for the methods. + * + * @package WordPress + * @subpackage HTTP + * @since 2.8.0 + */ +class WP_Http_Cookie { + + /** + * Cookie name. + * + * @since 2.8.0 + * @var string + */ + public $name; + + /** + * Cookie value. + * + * @since 2.8.0 + * @var string + */ + public $value; + + /** + * When the cookie expires. + * + * @since 2.8.0 + * @var string + */ + public $expires; + + /** + * Cookie URL path. + * + * @since 2.8.0 + * @var string + */ + public $path; + + /** + * Cookie Domain. + * + * @since 2.8.0 + * @var string + */ + public $domain; + + /** + * Sets up this cookie object. + * + * The parameter $data should be either an associative array containing the indices names below + * or a header string detailing it. + * + * @since 2.8.0 + * @access public + * + * @param string|array $data { + * Raw cookie data as header string or data array. + * + * @type string $name Cookie name. + * @type mixed $value Value. Should NOT already be urlencoded. + * @type string|int $expires Optional. Unix timestamp or formatted date. Default null. + * @type string $path Optional. Path. Default '/'. + * @type string $domain Optional. Domain. Default host of parsed $requested_url. + * @type int $port Optional. Port. Default null. + * } + * @param string $requested_url The URL which the cookie was set on, used for default $domain + * and $port values. + */ + public function __construct( $data, $requested_url = '' ) { + if ( $requested_url ) + $arrURL = @parse_url( $requested_url ); + if ( isset( $arrURL['host'] ) ) + $this->domain = $arrURL['host']; + $this->path = isset( $arrURL['path'] ) ? $arrURL['path'] : '/'; + if ( '/' != substr( $this->path, -1 ) ) + $this->path = dirname( $this->path ) . '/'; + + if ( is_string( $data ) ) { + // Assume it's a header string direct from a previous request. + $pairs = explode( ';', $data ); + + // Special handling for first pair; name=value. Also be careful of "=" in value. + $name = trim( substr( $pairs[0], 0, strpos( $pairs[0], '=' ) ) ); + $value = substr( $pairs[0], strpos( $pairs[0], '=' ) + 1 ); + $this->name = $name; + $this->value = urldecode( $value ); + + // Removes name=value from items. + array_shift( $pairs ); + + // Set everything else as a property. + foreach ( $pairs as $pair ) { + $pair = rtrim($pair); + + // Handle the cookie ending in ; which results in a empty final pair. + if ( empty($pair) ) + continue; + + list( $key, $val ) = strpos( $pair, '=' ) ? explode( '=', $pair ) : array( $pair, '' ); + $key = strtolower( trim( $key ) ); + if ( 'expires' == $key ) + $val = strtotime( $val ); + $this->$key = $val; + } + } else { + if ( !isset( $data['name'] ) ) + return false; + + // Set properties based directly on parameters. + foreach ( array( 'name', 'value', 'path', 'domain', 'port' ) as $field ) { + if ( isset( $data[ $field ] ) ) + $this->$field = $data[ $field ]; + } + + if ( isset( $data['expires'] ) ) + $this->expires = is_int( $data['expires'] ) ? $data['expires'] : strtotime( $data['expires'] ); + else + $this->expires = null; + } + } + + /** + * Confirms that it's OK to send this cookie to the URL checked against. + * + * Decision is based on RFC 2109/2965, so look there for details on validity. + * + * @access public + * @since 2.8.0 + * + * @param string $url URL you intend to send this cookie to + * @return boolean true if allowed, false otherwise. + */ + public function test( $url ) { + if ( is_null( $this->name ) ) + return false; + + // Expires - if expired then nothing else matters. + if ( isset( $this->expires ) && time() > $this->expires ) + return false; + + // Get details on the URL we're thinking about sending to. + $url = parse_url( $url ); + $url['port'] = isset( $url['port'] ) ? $url['port'] : ( 'https' == $url['scheme'] ? 443 : 80 ); + $url['path'] = isset( $url['path'] ) ? $url['path'] : '/'; + + // Values to use for comparison against the URL. + $path = isset( $this->path ) ? $this->path : '/'; + $port = isset( $this->port ) ? $this->port : null; + $domain = isset( $this->domain ) ? strtolower( $this->domain ) : strtolower( $url['host'] ); + if ( false === stripos( $domain, '.' ) ) + $domain .= '.local'; + + // Host - very basic check that the request URL ends with the domain restriction (minus leading dot). + $domain = substr( $domain, 0, 1 ) == '.' ? substr( $domain, 1 ) : $domain; + if ( substr( $url['host'], -strlen( $domain ) ) != $domain ) + return false; + + // Port - supports "port-lists" in the format: "80,8000,8080". + if ( !empty( $port ) && !in_array( $url['port'], explode( ',', $port) ) ) + return false; + + // Path - request path must start with path restriction. + if ( substr( $url['path'], 0, strlen( $path ) ) != $path ) + return false; + + return true; + } + + /** + * Convert cookie name and value back to header string. + * + * @access public + * @since 2.8.0 + * + * @return string Header encoded cookie name and value. + */ + public function getHeaderValue() { + if ( ! isset( $this->name ) || ! isset( $this->value ) ) + return ''; + + /** + * Filter the header-encoded cookie value. + * + * @since 3.4.0 + * + * @param string $value The cookie value. + * @param string $name The cookie name. + */ + return $this->name . '=' . apply_filters( 'wp_http_cookie_value', $this->value, $this->name ); + } + + /** + * Retrieve cookie header for usage in the rest of the WordPress HTTP API. + * + * @access public + * @since 2.8.0 + * + * @return string + */ + public function getFullHeader() { + return 'Cookie: ' . $this->getHeaderValue(); + } +} + +/** + * Implementation for deflate and gzip transfer encodings. + * + * Includes RFC 1950, RFC 1951, and RFC 1952. + * + * @since 2.8.0 + * @package WordPress + * @subpackage HTTP + */ +class WP_Http_Encoding { + + /** + * Compress raw string using the deflate format. + * + * Supports the RFC 1951 standard. + * + * @since 2.8.0 + * + * @param string $raw String to compress. + * @param int $level Optional, default is 9. Compression level, 9 is highest. + * @param string $supports Optional, not used. When implemented it will choose the right compression based on what the server supports. + * @return string|false False on failure. + */ + public static function compress( $raw, $level = 9, $supports = null ) { + return gzdeflate( $raw, $level ); + } + + /** + * Decompression of deflated string. + * + * Will attempt to decompress using the RFC 1950 standard, and if that fails + * then the RFC 1951 standard deflate will be attempted. Finally, the RFC + * 1952 standard gzip decode will be attempted. If all fail, then the + * original compressed string will be returned. + * + * @since 2.8.0 + * + * @param string $compressed String to decompress. + * @param int $length The optional length of the compressed data. + * @return string|bool False on failure. + */ + public static function decompress( $compressed, $length = null ) { + + if ( empty($compressed) ) + return $compressed; + + if ( false !== ( $decompressed = @gzinflate( $compressed ) ) ) + return $decompressed; + + if ( false !== ( $decompressed = WP_Http_Encoding::compatible_gzinflate( $compressed ) ) ) + return $decompressed; + + if ( false !== ( $decompressed = @gzuncompress( $compressed ) ) ) + return $decompressed; + + if ( function_exists('gzdecode') ) { + $decompressed = @gzdecode( $compressed ); + + if ( false !== $decompressed ) + return $decompressed; + } + + return $compressed; + } + + /** + * Decompression of deflated string while staying compatible with the majority of servers. + * + * Certain Servers will return deflated data with headers which PHP's gzinflate() + * function cannot handle out of the box. The following function has been created from + * various snippets on the gzinflate() PHP documentation. + * + * Warning: Magic numbers within. Due to the potential different formats that the compressed + * data may be returned in, some "magic offsets" are needed to ensure proper decompression + * takes place. For a simple progmatic way to determine the magic offset in use, see: + * https://core.trac.wordpress.org/ticket/18273 + * + * @since 2.8.1 + * @link https://core.trac.wordpress.org/ticket/18273 + * @link http://au2.php.net/manual/en/function.gzinflate.php#70875 + * @link http://au2.php.net/manual/en/function.gzinflate.php#77336 + * + * @param string $gzData String to decompress. + * @return string|bool False on failure. + */ + public static function compatible_gzinflate($gzData) { + + // Compressed data might contain a full header, if so strip it for gzinflate(). + if ( substr($gzData, 0, 3) == "\x1f\x8b\x08" ) { + $i = 10; + $flg = ord( substr($gzData, 3, 1) ); + if ( $flg > 0 ) { + if ( $flg & 4 ) { + list($xlen) = unpack('v', substr($gzData, $i, 2) ); + $i = $i + 2 + $xlen; + } + if ( $flg & 8 ) + $i = strpos($gzData, "\0", $i) + 1; + if ( $flg & 16 ) + $i = strpos($gzData, "\0", $i) + 1; + if ( $flg & 2 ) + $i = $i + 2; + } + $decompressed = @gzinflate( substr($gzData, $i, -8) ); + if ( false !== $decompressed ) + return $decompressed; + } + + // Compressed data from java.util.zip.Deflater amongst others. + $decompressed = @gzinflate( substr($gzData, 2) ); + if ( false !== $decompressed ) + return $decompressed; + + return false; + } + + /** + * What encoding types to accept and their priority values. + * + * @since 2.8.0 + * + * @param string $url + * @param array $args + * @return string Types of encoding to accept. + */ + public static function accept_encoding( $url, $args ) { + $type = array(); + $compression_enabled = WP_Http_Encoding::is_available(); + + if ( ! $args['decompress'] ) // Decompression specifically disabled. + $compression_enabled = false; + elseif ( $args['stream'] ) // Disable when streaming to file. + $compression_enabled = false; + elseif ( isset( $args['limit_response_size'] ) ) // If only partial content is being requested, we won't be able to decompress it. + $compression_enabled = false; + + if ( $compression_enabled ) { + if ( function_exists( 'gzinflate' ) ) + $type[] = 'deflate;q=1.0'; + + if ( function_exists( 'gzuncompress' ) ) + $type[] = 'compress;q=0.5'; + + if ( function_exists( 'gzdecode' ) ) + $type[] = 'gzip;q=0.5'; + } + + /** + * Filter the allowed encoding types. + * + * @since 3.6.0 + * + * @param array $type Encoding types allowed. Accepts 'gzinflate', + * 'gzuncompress', 'gzdecode'. + * @param string $url URL of the HTTP request. + * @param array $args HTTP request arguments. + */ + $type = apply_filters( 'wp_http_accept_encoding', $type, $url, $args ); + + return implode(', ', $type); + } + + /** + * What encoding the content used when it was compressed to send in the headers. + * + * @since 2.8.0 + * + * @return string Content-Encoding string to send in the header. + */ + public static function content_encoding() { + return 'deflate'; + } + + /** + * Whether the content be decoded based on the headers. + * + * @since 2.8.0 + * + * @param array|string $headers All of the available headers. + * @return bool + */ + public static function should_decode($headers) { + if ( is_array( $headers ) ) { + if ( array_key_exists('content-encoding', $headers) && ! empty( $headers['content-encoding'] ) ) + return true; + } else if ( is_string( $headers ) ) { + return ( stripos($headers, 'content-encoding:') !== false ); + } + + return false; + } + + /** + * Whether decompression and compression are supported by the PHP version. + * + * Each function is tested instead of checking for the zlib extension, to + * ensure that the functions all exist in the PHP version and aren't + * disabled. + * + * @since 2.8.0 + * + * @return bool + */ + public static function is_available() { + return ( function_exists('gzuncompress') || function_exists('gzdeflate') || function_exists('gzinflate') ); + } +} diff --git a/wp-includes/class-json.php b/wp-includes/class-json.php new file mode 100644 index 0000000..417592f --- /dev/null +++ b/wp-includes/class-json.php @@ -0,0 +1,936 @@ +<?php +if ( ! class_exists( 'Services_JSON' ) ) : +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ +/** + * Converts to and from JSON format. + * + * JSON (JavaScript Object Notation) is a lightweight data-interchange + * format. It is easy for humans to read and write. It is easy for machines + * to parse and generate. It is based on a subset of the JavaScript + * Programming Language, Standard ECMA-262 3rd Edition - December 1999. + * This feature can also be found in Python. JSON is a text format that is + * completely language independent but uses conventions that are familiar + * to programmers of the C-family of languages, including C, C++, C#, Java, + * JavaScript, Perl, TCL, and many others. These properties make JSON an + * ideal data-interchange language. + * + * This package provides a simple encoder and decoder for JSON notation. It + * is intended for use with client-side Javascript applications that make + * use of HTTPRequest to perform server communication functions - data can + * be encoded into JSON notation for use in a client-side javascript, or + * decoded from incoming Javascript requests. JSON format is native to + * Javascript, and can be directly eval()'ed with no further parsing + * overhead + * + * All strings should be in ASCII or UTF-8 format! + * + * LICENSE: Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: Redistributions of source code must retain the + * above copyright notice, this list of conditions and the following + * disclaimer. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * @category + * @package Services_JSON + * @author Michal Migurski <mike-json@teczno.com> + * @author Matt Knapp <mdknapp[at]gmail[dot]com> + * @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com> + * @copyright 2005 Michal Migurski + * @version CVS: $Id: JSON.php 305040 2010-11-02 23:19:03Z alan_k $ + * @license http://www.opensource.org/licenses/bsd-license.php + * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198 + */ + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_SLICE', 1); + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_IN_STR', 2); + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_IN_ARR', 3); + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_IN_OBJ', 4); + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_IN_CMT', 5); + +/** + * Behavior switch for Services_JSON::decode() + */ +define('SERVICES_JSON_LOOSE_TYPE', 16); + +/** + * Behavior switch for Services_JSON::decode() + */ +define('SERVICES_JSON_SUPPRESS_ERRORS', 32); + +/** + * Behavior switch for Services_JSON::decode() + */ +define('SERVICES_JSON_USE_TO_JSON', 64); + +/** + * Converts to and from JSON format. + * + * Brief example of use: + * + * <code> + * // create a new instance of Services_JSON + * $json = new Services_JSON(); + * + * // convert a complexe value to JSON notation, and send it to the browser + * $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4))); + * $output = $json->encode($value); + * + * print($output); + * // prints: ["foo","bar",[1,2,"baz"],[3,[4]]] + * + * // accept incoming POST data, assumed to be in JSON notation + * $input = file_get_contents('php://input', 1000000); + * $value = $json->decode($input); + * </code> + */ +class Services_JSON +{ + /** + * constructs a new JSON instance + * + * @param int $use object behavior flags; combine with boolean-OR + * + * possible values: + * - SERVICES_JSON_LOOSE_TYPE: loose typing. + * "{...}" syntax creates associative arrays + * instead of objects in decode(). + * - SERVICES_JSON_SUPPRESS_ERRORS: error suppression. + * Values which can't be encoded (e.g. resources) + * appear as NULL instead of throwing errors. + * By default, a deeply-nested resource will + * bubble up with an error, so all return values + * from encode() should be checked with isError() + * - SERVICES_JSON_USE_TO_JSON: call toJSON when serializing objects + * It serializes the return value from the toJSON call rather + * than the object it'self, toJSON can return associative arrays, + * strings or numbers, if you return an object, make sure it does + * not have a toJSON method, otherwise an error will occur. + */ + function Services_JSON($use = 0) + { + $this->use = $use; + $this->_mb_strlen = function_exists('mb_strlen'); + $this->_mb_convert_encoding = function_exists('mb_convert_encoding'); + $this->_mb_substr = function_exists('mb_substr'); + } + // private - cache the mbstring lookup results.. + var $_mb_strlen = false; + var $_mb_substr = false; + var $_mb_convert_encoding = false; + + /** + * convert a string from one UTF-16 char to one UTF-8 char + * + * Normally should be handled by mb_convert_encoding, but + * provides a slower PHP-only method for installations + * that lack the multibye string extension. + * + * @param string $utf16 UTF-16 character + * @return string UTF-8 character + * @access private + */ + function utf162utf8($utf16) + { + // oh please oh please oh please oh please oh please + if($this->_mb_convert_encoding) { + return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16'); + } + + $bytes = (ord($utf16{0}) << 8) | ord($utf16{1}); + + switch(true) { + case ((0x7F & $bytes) == $bytes): + // this case should never be reached, because we are in ASCII range + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0x7F & $bytes); + + case (0x07FF & $bytes) == $bytes: + // return a 2-byte UTF-8 character + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0xC0 | (($bytes >> 6) & 0x1F)) + . chr(0x80 | ($bytes & 0x3F)); + + case (0xFFFF & $bytes) == $bytes: + // return a 3-byte UTF-8 character + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0xE0 | (($bytes >> 12) & 0x0F)) + . chr(0x80 | (($bytes >> 6) & 0x3F)) + . chr(0x80 | ($bytes & 0x3F)); + } + + // ignoring UTF-32 for now, sorry + return ''; + } + + /** + * convert a string from one UTF-8 char to one UTF-16 char + * + * Normally should be handled by mb_convert_encoding, but + * provides a slower PHP-only method for installations + * that lack the multibye string extension. + * + * @param string $utf8 UTF-8 character + * @return string UTF-16 character + * @access private + */ + function utf82utf16($utf8) + { + // oh please oh please oh please oh please oh please + if($this->_mb_convert_encoding) { + return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); + } + + switch($this->strlen8($utf8)) { + case 1: + // this case should never be reached, because we are in ASCII range + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return $utf8; + + case 2: + // return a UTF-16 character from a 2-byte UTF-8 char + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0x07 & (ord($utf8{0}) >> 2)) + . chr((0xC0 & (ord($utf8{0}) << 6)) + | (0x3F & ord($utf8{1}))); + + case 3: + // return a UTF-16 character from a 3-byte UTF-8 char + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr((0xF0 & (ord($utf8{0}) << 4)) + | (0x0F & (ord($utf8{1}) >> 2))) + . chr((0xC0 & (ord($utf8{1}) << 6)) + | (0x7F & ord($utf8{2}))); + } + + // ignoring UTF-32 for now, sorry + return ''; + } + + /** + * encodes an arbitrary variable into JSON format (and sends JSON Header) + * + * @param mixed $var any number, boolean, string, array, or object to be encoded. + * see argument 1 to Services_JSON() above for array-parsing behavior. + * if var is a strng, note that encode() always expects it + * to be in ASCII or UTF-8 format! + * + * @return mixed JSON string representation of input var or an error if a problem occurs + * @access public + */ + function encode($var) + { + header('Content-type: application/json'); + return $this->encodeUnsafe($var); + } + /** + * encodes an arbitrary variable into JSON format without JSON Header - warning - may allow XSS!!!!) + * + * @param mixed $var any number, boolean, string, array, or object to be encoded. + * see argument 1 to Services_JSON() above for array-parsing behavior. + * if var is a strng, note that encode() always expects it + * to be in ASCII or UTF-8 format! + * + * @return mixed JSON string representation of input var or an error if a problem occurs + * @access public + */ + function encodeUnsafe($var) + { + // see bug #16908 - regarding numeric locale printing + $lc = setlocale(LC_NUMERIC, 0); + setlocale(LC_NUMERIC, 'C'); + $ret = $this->_encode($var); + setlocale(LC_NUMERIC, $lc); + return $ret; + + } + /** + * PRIVATE CODE that does the work of encodes an arbitrary variable into JSON format + * + * @param mixed $var any number, boolean, string, array, or object to be encoded. + * see argument 1 to Services_JSON() above for array-parsing behavior. + * if var is a strng, note that encode() always expects it + * to be in ASCII or UTF-8 format! + * + * @return mixed JSON string representation of input var or an error if a problem occurs + * @access public + */ + function _encode($var) + { + + switch (gettype($var)) { + case 'boolean': + return $var ? 'true' : 'false'; + + case 'NULL': + return 'null'; + + case 'integer': + return (int) $var; + + case 'double': + case 'float': + return (float) $var; + + case 'string': + // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT + $ascii = ''; + $strlen_var = $this->strlen8($var); + + /* + * Iterate over every character in the string, + * escaping with a slash or encoding to UTF-8 where necessary + */ + for ($c = 0; $c < $strlen_var; ++$c) { + + $ord_var_c = ord($var{$c}); + + switch (true) { + case $ord_var_c == 0x08: + $ascii .= '\b'; + break; + case $ord_var_c == 0x09: + $ascii .= '\t'; + break; + case $ord_var_c == 0x0A: + $ascii .= '\n'; + break; + case $ord_var_c == 0x0C: + $ascii .= '\f'; + break; + case $ord_var_c == 0x0D: + $ascii .= '\r'; + break; + + case $ord_var_c == 0x22: + case $ord_var_c == 0x2F: + case $ord_var_c == 0x5C: + // double quote, slash, slosh + $ascii .= '\\'.$var{$c}; + break; + + case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): + // characters U-00000000 - U-0000007F (same as ASCII) + $ascii .= $var{$c}; + break; + + case (($ord_var_c & 0xE0) == 0xC0): + // characters U-00000080 - U-000007FF, mask 110XXXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + if ($c+1 >= $strlen_var) { + $c += 1; + $ascii .= '?'; + break; + } + + $char = pack('C*', $ord_var_c, ord($var{$c + 1})); + $c += 1; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF0) == 0xE0): + if ($c+2 >= $strlen_var) { + $c += 2; + $ascii .= '?'; + break; + } + // characters U-00000800 - U-0000FFFF, mask 1110XXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + @ord($var{$c + 1}), + @ord($var{$c + 2})); + $c += 2; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF8) == 0xF0): + if ($c+3 >= $strlen_var) { + $c += 3; + $ascii .= '?'; + break; + } + // characters U-00010000 - U-001FFFFF, mask 11110XXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3})); + $c += 3; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFC) == 0xF8): + // characters U-00200000 - U-03FFFFFF, mask 111110XX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + if ($c+4 >= $strlen_var) { + $c += 4; + $ascii .= '?'; + break; + } + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3}), + ord($var{$c + 4})); + $c += 4; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFE) == 0xFC): + if ($c+5 >= $strlen_var) { + $c += 5; + $ascii .= '?'; + break; + } + // characters U-04000000 - U-7FFFFFFF, mask 1111110X + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3}), + ord($var{$c + 4}), + ord($var{$c + 5})); + $c += 5; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + } + } + return '"'.$ascii.'"'; + + case 'array': + /* + * As per JSON spec if any array key is not an integer + * we must treat the the whole array as an object. We + * also try to catch a sparsely populated associative + * array with numeric keys here because some JS engines + * will create an array with empty indexes up to + * max_index which can cause memory issues and because + * the keys, which may be relevant, will be remapped + * otherwise. + * + * As per the ECMA and JSON specification an object may + * have any string as a property. Unfortunately due to + * a hole in the ECMA specification if the key is a + * ECMA reserved word or starts with a digit the + * parameter is only accessible using ECMAScript's + * bracket notation. + */ + + // treat as a JSON object + if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) { + $properties = array_map(array($this, 'name_value'), + array_keys($var), + array_values($var)); + + foreach($properties as $property) { + if(Services_JSON::isError($property)) { + return $property; + } + } + + return '{' . join(',', $properties) . '}'; + } + + // treat it like a regular array + $elements = array_map(array($this, '_encode'), $var); + + foreach($elements as $element) { + if(Services_JSON::isError($element)) { + return $element; + } + } + + return '[' . join(',', $elements) . ']'; + + case 'object': + + // support toJSON methods. + if (($this->use & SERVICES_JSON_USE_TO_JSON) && method_exists($var, 'toJSON')) { + // this may end up allowing unlimited recursion + // so we check the return value to make sure it's not got the same method. + $recode = $var->toJSON(); + + if (method_exists($recode, 'toJSON')) { + + return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS) + ? 'null' + : new Services_JSON_Error(get_class($var). + " toJSON returned an object with a toJSON method."); + + } + + return $this->_encode( $recode ); + } + + $vars = get_object_vars($var); + + $properties = array_map(array($this, 'name_value'), + array_keys($vars), + array_values($vars)); + + foreach($properties as $property) { + if(Services_JSON::isError($property)) { + return $property; + } + } + + return '{' . join(',', $properties) . '}'; + + default: + return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS) + ? 'null' + : new Services_JSON_Error(gettype($var)." can not be encoded as JSON string"); + } + } + + /** + * array-walking function for use in generating JSON-formatted name-value pairs + * + * @param string $name name of key to use + * @param mixed $value reference to an array element to be encoded + * + * @return string JSON-formatted name-value pair, like '"name":value' + * @access private + */ + function name_value($name, $value) + { + $encoded_value = $this->_encode($value); + + if(Services_JSON::isError($encoded_value)) { + return $encoded_value; + } + + return $this->_encode(strval($name)) . ':' . $encoded_value; + } + + /** + * reduce a string by removing leading and trailing comments and whitespace + * + * @param $str string string value to strip of comments and whitespace + * + * @return string string value stripped of comments and whitespace + * @access private + */ + function reduce_string($str) + { + $str = preg_replace(array( + + // eliminate single line comments in '// ...' form + '#^\s*//(.+)$#m', + + // eliminate multi-line comments in '/* ... */' form, at start of string + '#^\s*/\*(.+)\*/#Us', + + // eliminate multi-line comments in '/* ... */' form, at end of string + '#/\*(.+)\*/\s*$#Us' + + ), '', $str); + + // eliminate extraneous space + return trim($str); + } + + /** + * decodes a JSON string into appropriate variable + * + * @param string $str JSON-formatted string + * + * @return mixed number, boolean, string, array, or object + * corresponding to given JSON input string. + * See argument 1 to Services_JSON() above for object-output behavior. + * Note that decode() always returns strings + * in ASCII or UTF-8 format! + * @access public + */ + function decode($str) + { + $str = $this->reduce_string($str); + + switch (strtolower($str)) { + case 'true': + return true; + + case 'false': + return false; + + case 'null': + return null; + + default: + $m = array(); + + if (is_numeric($str)) { + // Lookie-loo, it's a number + + // This would work on its own, but I'm trying to be + // good about returning integers where appropriate: + // return (float)$str; + + // Return float or int, as appropriate + return ((float)$str == (integer)$str) + ? (integer)$str + : (float)$str; + + } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) { + // STRINGS RETURNED IN UTF-8 FORMAT + $delim = $this->substr8($str, 0, 1); + $chrs = $this->substr8($str, 1, -1); + $utf8 = ''; + $strlen_chrs = $this->strlen8($chrs); + + for ($c = 0; $c < $strlen_chrs; ++$c) { + + $substr_chrs_c_2 = $this->substr8($chrs, $c, 2); + $ord_chrs_c = ord($chrs{$c}); + + switch (true) { + case $substr_chrs_c_2 == '\b': + $utf8 .= chr(0x08); + ++$c; + break; + case $substr_chrs_c_2 == '\t': + $utf8 .= chr(0x09); + ++$c; + break; + case $substr_chrs_c_2 == '\n': + $utf8 .= chr(0x0A); + ++$c; + break; + case $substr_chrs_c_2 == '\f': + $utf8 .= chr(0x0C); + ++$c; + break; + case $substr_chrs_c_2 == '\r': + $utf8 .= chr(0x0D); + ++$c; + break; + + case $substr_chrs_c_2 == '\\"': + case $substr_chrs_c_2 == '\\\'': + case $substr_chrs_c_2 == '\\\\': + case $substr_chrs_c_2 == '\\/': + if (($delim == '"' && $substr_chrs_c_2 != '\\\'') || + ($delim == "'" && $substr_chrs_c_2 != '\\"')) { + $utf8 .= $chrs{++$c}; + } + break; + + case preg_match('/\\\u[0-9A-F]{4}/i', $this->substr8($chrs, $c, 6)): + // single, escaped unicode character + $utf16 = chr(hexdec($this->substr8($chrs, ($c + 2), 2))) + . chr(hexdec($this->substr8($chrs, ($c + 4), 2))); + $utf8 .= $this->utf162utf8($utf16); + $c += 5; + break; + + case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F): + $utf8 .= $chrs{$c}; + break; + + case ($ord_chrs_c & 0xE0) == 0xC0: + // characters U-00000080 - U-000007FF, mask 110XXXXX + //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= $this->substr8($chrs, $c, 2); + ++$c; + break; + + case ($ord_chrs_c & 0xF0) == 0xE0: + // characters U-00000800 - U-0000FFFF, mask 1110XXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= $this->substr8($chrs, $c, 3); + $c += 2; + break; + + case ($ord_chrs_c & 0xF8) == 0xF0: + // characters U-00010000 - U-001FFFFF, mask 11110XXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= $this->substr8($chrs, $c, 4); + $c += 3; + break; + + case ($ord_chrs_c & 0xFC) == 0xF8: + // characters U-00200000 - U-03FFFFFF, mask 111110XX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= $this->substr8($chrs, $c, 5); + $c += 4; + break; + + case ($ord_chrs_c & 0xFE) == 0xFC: + // characters U-04000000 - U-7FFFFFFF, mask 1111110X + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= $this->substr8($chrs, $c, 6); + $c += 5; + break; + + } + + } + + return $utf8; + + } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) { + // array, or object notation + + if ($str{0} == '[') { + $stk = array(SERVICES_JSON_IN_ARR); + $arr = array(); + } else { + if ($this->use & SERVICES_JSON_LOOSE_TYPE) { + $stk = array(SERVICES_JSON_IN_OBJ); + $obj = array(); + } else { + $stk = array(SERVICES_JSON_IN_OBJ); + $obj = new stdClass(); + } + } + + array_push($stk, array('what' => SERVICES_JSON_SLICE, + 'where' => 0, + 'delim' => false)); + + $chrs = $this->substr8($str, 1, -1); + $chrs = $this->reduce_string($chrs); + + if ($chrs == '') { + if (reset($stk) == SERVICES_JSON_IN_ARR) { + return $arr; + + } else { + return $obj; + + } + } + + //print("\nparsing {$chrs}\n"); + + $strlen_chrs = $this->strlen8($chrs); + + for ($c = 0; $c <= $strlen_chrs; ++$c) { + + $top = end($stk); + $substr_chrs_c_2 = $this->substr8($chrs, $c, 2); + + if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) { + // found a comma that is not inside a string, array, etc., + // OR we've reached the end of the character list + $slice = $this->substr8($chrs, $top['where'], ($c - $top['where'])); + array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false)); + //print("Found split at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + if (reset($stk) == SERVICES_JSON_IN_ARR) { + // we are in an array, so just push an element onto the stack + array_push($arr, $this->decode($slice)); + + } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { + // we are in an object, so figure + // out the property name and set an + // element in an associative array, + // for now + $parts = array(); + + if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:/Uis', $slice, $parts)) { + // "name":value pair + $key = $this->decode($parts[1]); + $val = $this->decode(trim(substr($slice, strlen($parts[0])), ", \t\n\r\0\x0B")); + if ($this->use & SERVICES_JSON_LOOSE_TYPE) { + $obj[$key] = $val; + } else { + $obj->$key = $val; + } + } elseif (preg_match('/^\s*(\w+)\s*:/Uis', $slice, $parts)) { + // name:value pair, where name is unquoted + $key = $parts[1]; + $val = $this->decode(trim(substr($slice, strlen($parts[0])), ", \t\n\r\0\x0B")); + + if ($this->use & SERVICES_JSON_LOOSE_TYPE) { + $obj[$key] = $val; + } else { + $obj->$key = $val; + } + } + + } + + } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) { + // found a quote, and we are not inside a string + array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c})); + //print("Found start of string at {$c}\n"); + + } elseif (($chrs{$c} == $top['delim']) && + ($top['what'] == SERVICES_JSON_IN_STR) && + (($this->strlen8($this->substr8($chrs, 0, $c)) - $this->strlen8(rtrim($this->substr8($chrs, 0, $c), '\\'))) % 2 != 1)) { + // found a quote, we're in a string, and it's not escaped + // we know that it's not escaped becase there is _not_ an + // odd number of backslashes at the end of the string so far + array_pop($stk); + //print("Found end of string at {$c}: ".$this->substr8($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n"); + + } elseif (($chrs{$c} == '[') && + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { + // found a left-bracket, and we are in an array, object, or slice + array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false)); + //print("Found start of array at {$c}\n"); + + } elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) { + // found a right-bracket, and we're in an array + array_pop($stk); + //print("Found end of array at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } elseif (($chrs{$c} == '{') && + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { + // found a left-brace, and we are in an array, object, or slice + array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false)); + //print("Found start of object at {$c}\n"); + + } elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) { + // found a right-brace, and we're in an object + array_pop($stk); + //print("Found end of object at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } elseif (($substr_chrs_c_2 == '/*') && + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { + // found a comment start, and we are in an array, object, or slice + array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false)); + $c++; + //print("Found start of comment at {$c}\n"); + + } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) { + // found a comment end, and we're in one now + array_pop($stk); + $c++; + + for ($i = $top['where']; $i <= $c; ++$i) + $chrs = substr_replace($chrs, ' ', $i, 1); + + //print("Found end of comment at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } + + } + + if (reset($stk) == SERVICES_JSON_IN_ARR) { + return $arr; + + } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { + return $obj; + + } + + } + } + } + + /** + * @todo Ultimately, this should just call PEAR::isError() + */ + function isError($data, $code = null) + { + if (class_exists('pear')) { + return PEAR::isError($data, $code); + } elseif (is_object($data) && (get_class($data) == 'services_json_error' || + is_subclass_of($data, 'services_json_error'))) { + return true; + } + + return false; + } + + /** + * Calculates length of string in bytes + * @param string + * @return integer length + */ + function strlen8( $str ) + { + if ( $this->_mb_strlen ) { + return mb_strlen( $str, "8bit" ); + } + return strlen( $str ); + } + + /** + * Returns part of a string, interpreting $start and $length as number of bytes. + * @param string + * @param integer start + * @param integer length + * @return integer length + */ + function substr8( $string, $start, $length=false ) + { + if ( $length === false ) { + $length = $this->strlen8( $string ) - $start; + } + if ( $this->_mb_substr ) { + return mb_substr( $string, $start, $length, "8bit" ); + } + return substr( $string, $start, $length ); + } + +} + +if (class_exists('PEAR_Error')) { + + class Services_JSON_Error extends PEAR_Error + { + function Services_JSON_Error($message = 'unknown error', $code = null, + $mode = null, $options = null, $userinfo = null) + { + parent::PEAR_Error($message, $code, $mode, $options, $userinfo); + } + } + +} else { + + /** + * @todo Ultimately, this class shall be descended from PEAR_Error + */ + class Services_JSON_Error + { + function Services_JSON_Error($message = 'unknown error', $code = null, + $mode = null, $options = null, $userinfo = null) + { + + } + } + +} + +endif; diff --git a/wp-includes/class-oembed.php b/wp-includes/class-oembed.php new file mode 100644 index 0000000..6042fd4 --- /dev/null +++ b/wp-includes/class-oembed.php @@ -0,0 +1,581 @@ +<?php +/** + * API for fetching the HTML to embed remote content based on a provided URL. + * Used internally by the {@link WP_Embed} class, but is designed to be generic. + * + * @link http://codex.wordpress.org/oEmbed oEmbed Codex Article + * @link http://oembed.com/ oEmbed Homepage + * + * @package WordPress + * @subpackage oEmbed + */ + +/** + * oEmbed class. + * + * @package WordPress + * @subpackage oEmbed + * @since 2.9.0 + */ +class WP_oEmbed { + public $providers = array(); + public static $early_providers = array(); + + /** + * Constructor + * + * @since 2.9.0 + */ + public function __construct() { + $providers = array( + '#http://(www\.)?youtube\.com/watch.*#i' => array( 'http://www.youtube.com/oembed', true ), + '#https://(www\.)?youtube\.com/watch.*#i' => array( 'http://www.youtube.com/oembed?scheme=https', true ), + '#http://(www\.)?youtube\.com/playlist.*#i' => array( 'http://www.youtube.com/oembed', true ), + '#https://(www\.)?youtube\.com/playlist.*#i' => array( 'http://www.youtube.com/oembed?scheme=https', true ), + '#http://youtu\.be/.*#i' => array( 'http://www.youtube.com/oembed', true ), + '#https://youtu\.be/.*#i' => array( 'http://www.youtube.com/oembed?scheme=https', true ), + 'http://blip.tv/*' => array( 'http://blip.tv/oembed/', false ), + '#https?://(.+\.)?vimeo\.com/.*#i' => array( 'http://vimeo.com/api/oembed.{format}', true ), + '#https?://(www\.)?dailymotion\.com/.*#i' => array( 'http://www.dailymotion.com/services/oembed', true ), + 'http://dai.ly/*' => array( 'http://www.dailymotion.com/services/oembed', false ), + '#https?://(www\.)?flickr\.com/.*#i' => array( 'https://www.flickr.com/services/oembed/', true ), + '#https?://flic\.kr/.*#i' => array( 'https://www.flickr.com/services/oembed/', true ), + '#https?://(.+\.)?smugmug\.com/.*#i' => array( 'http://api.smugmug.com/services/oembed/', true ), + '#https?://(www\.)?hulu\.com/watch/.*#i' => array( 'http://www.hulu.com/api/oembed.{format}', true ), + 'http://revision3.com/*' => array( 'http://revision3.com/api/oembed/', false ), + 'http://i*.photobucket.com/albums/*' => array( 'http://photobucket.com/oembed', false ), + 'http://gi*.photobucket.com/groups/*' => array( 'http://photobucket.com/oembed', false ), + '#https?://(www\.)?scribd\.com/doc/.*#i' => array( 'http://www.scribd.com/services/oembed', true ), + '#https?://wordpress.tv/.*#i' => array( 'http://wordpress.tv/oembed/', true ), + '#https?://(.+\.)?polldaddy\.com/.*#i' => array( 'https://polldaddy.com/oembed/', true ), + '#https?://poll\.fm/.*#i' => array( 'https://polldaddy.com/oembed/', true ), + '#https?://(www\.)?funnyordie\.com/videos/.*#i' => array( 'http://www.funnyordie.com/oembed', true ), + '#https?://(www\.)?twitter\.com/.+?/status(es)?/.*#i' => array( 'https://api.twitter.com/1/statuses/oembed.{format}', true ), + '#https?://vine.co/v/.*#i' => array( 'https://vine.co/oembed.{format}', true ), + '#https?://(www\.)?soundcloud\.com/.*#i' => array( 'http://soundcloud.com/oembed', true ), + '#https?://(.+?\.)?slideshare\.net/.*#i' => array( 'https://www.slideshare.net/api/oembed/2', true ), + '#http://instagr(\.am|am\.com)/p/.*#i' => array( 'http://api.instagram.com/oembed', true ), + '#https?://(www\.)?rdio\.com/.*#i' => array( 'http://www.rdio.com/api/oembed/', true ), + '#https?://rd\.io/x/.*#i' => array( 'http://www.rdio.com/api/oembed/', true ), + '#https?://(open|play)\.spotify\.com/.*#i' => array( 'https://embed.spotify.com/oembed/', true ), + '#https?://(.+\.)?imgur\.com/.*#i' => array( 'http://api.imgur.com/oembed', true ), + '#https?://(www\.)?meetu(\.ps|p\.com)/.*#i' => array( 'http://api.meetup.com/oembed', true ), + '#https?://(www\.)?issuu\.com/.+/docs/.+#i' => array( 'http://issuu.com/oembed_wp', true ), + '#https?://(www\.)?collegehumor\.com/video/.*#i' => array( 'http://www.collegehumor.com/oembed.{format}', true ), + '#https?://(www\.)?mixcloud\.com/.*#i' => array( 'http://www.mixcloud.com/oembed', true ), + '#https?://(www\.|embed\.)?ted\.com/talks/.*#i' => array( 'http://www.ted.com/talks/oembed.{format}', true ), + '#https?://(www\.)?(animoto|video214)\.com/play/.*#i' => array( 'http://animoto.com/oembeds/create', true ), + ); + + if ( ! empty( self::$early_providers['add'] ) ) { + foreach ( self::$early_providers['add'] as $format => $data ) { + $providers[ $format ] = $data; + } + } + + if ( ! empty( self::$early_providers['remove'] ) ) { + foreach ( self::$early_providers['remove'] as $format ) { + unset( $providers[ $format ] ); + } + } + + self::$early_providers = array(); + + /** + * Filter the list of oEmbed providers. + * + * Discovery is disabled for users lacking the unfiltered_html capability. + * Only providers in this array will be used for those users. + * + * Supported providers: + * + * | ------------ | -------------------- | ----- | --------- | + * | Provider | Flavor | SSL | Since | + * | ------------ | -------------------- | ----- | --------- | + * | Blip | blip.tv | | 2.9.0 | + * | Dailymotion | dailymotion.com | Yes | 2.9.0 | + * | Flickr | flickr.com | Yes | 2.9.0 | + * | Hulu | hulu.com | Yes | 2.9.0 | + * | Photobucket | photobucket.com | | 2.9.0 | + * | Revision3 | revision3.com | | 2.9.0 | + * | Scribd | scribd.com | Yes | 2.9.0 | + * | Vimeo | vimeo.com | Yes | 2.9.0 | + * | WordPress.tv | wordpress.tv | Yes | 2.9.0 | + * | YouTube | youtube.com/watch | Yes | 2.9.0 | + * | ------------ | -------------------- | ----- | --------- | + * | Funny or Die | funnyordie.com | Yes | 3.0.0 | + * | Polldaddy | polldaddy.com | Yes | 3.0.0 | + * | SmugMug | smugmug.com | Yes | 3.0.0 | + * | YouTube | youtu.be | Yes | 3.0.0 | + * | ------------ | -------------------- | ----- | --------- | + * | Twitter | twitter.com | Yes | 3.4.0 | + * | ------------ | -------------------- | ----- | --------- | + * | Instagram | instagram.com | | 3.5.0 | + * | Instagram | instagr.am | | 3.5.0 | + * | Slideshare | slideshare.net | Yes | 3.5.0 | + * | SoundCloud | soundcloud.com | Yes | 3.5.0 | + * | ------------ | -------------------- | ----- | --------- | + * | Dailymotion | dai.ly | | 3.6.0 | + * | Flickr | flic.kr | Yes | 3.6.0 | + * | Rdio | rdio.com | Yes | 3.6.0 | + * | Rdio | rd.io | Yes | 3.6.0 | + * | Spotify | spotify.com | Yes | 3.6.0 | + * | ------------ | -------------------- | ----- | --------- | + * | Imgur | imgur.com | Yes | 3.9.0 | + * | Meetup.com | meetup.com | Yes | 3.9.0 | + * | Meetup.com | meetu.ps | Yes | 3.9.0 | + * | ------------ | -------------------- | ----- | --------- | + * | Animoto | animoto.com | Yes | 4.0.0 | + * | Animoto | video214.com | Yes | 4.0.0 | + * | CollegeHumor | collegehumor.com | Yes | 4.0.0 | + * | Issuu | issuu.com | Yes | 4.0.0 | + * | Mixcloud | mixcloud.com | Yes | 4.0.0 | + * | Polldaddy | poll.fm | Yes | 4.0.0 | + * | TED | ted.com | Yes | 4.0.0 | + * | YouTube | youtube.com/playlist | Yes | 4.0.0 | + * | ------------ | -------------------- | ----- | --------- | + * | Vine | vine.co | Yes | 4.1.0 | + * | ------------ | -------------------- | ----- | --------- | + * + * No longer supported providers: + * + * | ------------ | -------------------- | ----- | --------- | --------- | + * | Provider | Flavor | SSL | Since | Removed | + * | ------------ | -------------------- | ----- | --------- | --------- | + * | Qik | qik.com | Yes | 2.9.0 | 3.9.0 | + * | ------------ | -------------------- | ----- | --------- | --------- | + * | Viddler | viddler.com | Yes | 2.9.0 | 4.0.0 | + * | ------------ | -------------------- | ----- | --------- | --------- | + * + * @see wp_oembed_add_provider() + * + * @since 2.9.0 + * + * @param array $providers An array of popular oEmbed providers. + */ + $this->providers = apply_filters( 'oembed_providers', $providers ); + + // Fix any embeds that contain new lines in the middle of the HTML which breaks wpautop(). + add_filter( 'oembed_dataparse', array($this, '_strip_newlines'), 10, 3 ); + } + + /** + * Make private/protected methods readable for backwards compatibility. + * + * @since 4.0.0 + * @access public + * + * @param callable $name Method to call. + * @param array $arguments Arguments to pass when calling. + * @return mixed|bool Return value of the callback, false otherwise. + */ + public function __call( $name, $arguments ) { + return call_user_func_array( array( $this, $name ), $arguments ); + } + + /** + * Takes a URL and returns the corresponding oEmbed provider's URL, if there is one. + * + * @since 4.0.0 + * @access public + * + * @see WP_oEmbed::discover() + * + * @param string $url The URL to the content. + * @param string|array $args Optional provider arguments. + * @return bool|string False on failure, otherwise the oEmbed provider URL. + */ + public function get_provider( $url, $args = '' ) { + + $provider = false; + + if ( !isset($args['discover']) ) + $args['discover'] = true; + + foreach ( $this->providers as $matchmask => $data ) { + list( $providerurl, $regex ) = $data; + + // Turn the asterisk-type provider URLs into regex + if ( !$regex ) { + $matchmask = '#' . str_replace( '___wildcard___', '(.+)', preg_quote( str_replace( '*', '___wildcard___', $matchmask ), '#' ) ) . '#i'; + $matchmask = preg_replace( '|^#http\\\://|', '#https?\://', $matchmask ); + } + + if ( preg_match( $matchmask, $url ) ) { + $provider = str_replace( '{format}', 'json', $providerurl ); // JSON is easier to deal with than XML + break; + } + } + + if ( !$provider && $args['discover'] ) + $provider = $this->discover( $url ); + + return $provider; + } + + /** + * Add an oEmbed provider just-in-time when wp_oembed_add_provider() is called + * before the 'plugins_loaded' hook. + * + * The just-in-time addition is for the benefit of the 'oembed_providers' filter. + * + * @since 4.0.0 + * @access public + * @static + * + * @see wp_oembed_add_provider() + * + * @param string $format Format of URL that this provider can handle. You can use + * asterisks as wildcards. + * @param string $provider The URL to the oEmbed provider.. + * @param bool $regex Optional. Whether the $format parameter is in a regex format. + * Default false. + */ + public static function _add_provider_early( $format, $provider, $regex = false ) { + if ( empty( self::$early_providers['add'] ) ) { + self::$early_providers['add'] = array(); + } + + self::$early_providers['add'][ $format ] = array( $provider, $regex ); + } + + /** + * Remove an oEmbed provider just-in-time when wp_oembed_remove_provider() is called + * before the 'plugins_loaded' hook. + * + * The just-in-time removal is for the benefit of the 'oembed_providers' filter. + * + * @since 4.0.0 + * @access public + * @static + * + * @see wp_oembed_remove_provider() + * + * @param string $format The format of URL that this provider can handle. You can use + * asterisks as wildcards. + */ + public static function _remove_provider_early( $format ) { + if ( empty( self::$early_providers['remove'] ) ) { + self::$early_providers['remove'] = array(); + } + + self::$early_providers['remove'][] = $format; + } + + /** + * The do-it-all function that takes a URL and attempts to return the HTML. + * + * @see WP_oEmbed::fetch() + * @see WP_oEmbed::data2html() + * + * @param string $url The URL to the content that should be attempted to be embedded. + * @param array $args Optional arguments. Usually passed from a shortcode. + * @return false|string False on failure, otherwise the UNSANITIZED (and potentially unsafe) HTML that should be used to embed. + */ + function get_html( $url, $args = '' ) { + $provider = $this->get_provider( $url, $args ); + + if ( !$provider || false === $data = $this->fetch( $provider, $url, $args ) ) + return false; + + /** + * Filter the HTML returned by the oEmbed provider. + * + * @since 2.9.0 + * + * @param string $data The returned oEmbed HTML. + * @param string $url URL of the content to be embedded. + * @param array $args Optional arguments, usually passed from a shortcode. + */ + return apply_filters( 'oembed_result', $this->data2html( $data, $url ), $url, $args ); + } + + /** + * Attempts to discover link tags at the given URL for an oEmbed provider. + * + * @param string $url The URL that should be inspected for discovery `<link>` tags. + * @return bool|string False on failure, otherwise the oEmbed provider URL. + */ + public function discover( $url ) { + $providers = array(); + + /** + * Filter oEmbed remote get arguments. + * + * @since 4.0.0 + * + * @see WP_Http::request() + * + * @param array $args oEmbed remote get arguments. + * @param string $url URL to be inspected. + */ + $args = apply_filters( 'oembed_remote_get_args', array(), $url ); + + // Fetch URL content + $request = wp_safe_remote_get( $url, $args ); + if ( $html = wp_remote_retrieve_body( $request ) ) { + + /** + * Filter the link types that contain oEmbed provider URLs. + * + * @since 2.9.0 + * + * @param array $format Array of oEmbed link types. Accepts 'application/json+oembed', + * 'text/xml+oembed', and 'application/xml+oembed' (incorrect, + * used by at least Vimeo). + */ + $linktypes = apply_filters( 'oembed_linktypes', array( + 'application/json+oembed' => 'json', + 'text/xml+oembed' => 'xml', + 'application/xml+oembed' => 'xml', + ) ); + + // Strip <body> + $html = substr( $html, 0, stripos( $html, '</head>' ) ); + + // Do a quick check + $tagfound = false; + foreach ( $linktypes as $linktype => $format ) { + if ( stripos($html, $linktype) ) { + $tagfound = true; + break; + } + } + + if ( $tagfound && preg_match_all( '/<link([^<>]+)>/i', $html, $links ) ) { + foreach ( $links[1] as $link ) { + $atts = shortcode_parse_atts( $link ); + + if ( !empty($atts['type']) && !empty($linktypes[$atts['type']]) && !empty($atts['href']) ) { + $providers[$linktypes[$atts['type']]] = $atts['href']; + + // Stop here if it's JSON (that's all we need) + if ( 'json' == $linktypes[$atts['type']] ) + break; + } + } + } + } + + // JSON is preferred to XML + if ( !empty($providers['json']) ) + return $providers['json']; + elseif ( !empty($providers['xml']) ) + return $providers['xml']; + else + return false; + } + + /** + * Connects to a oEmbed provider and returns the result. + * + * @param string $provider The URL to the oEmbed provider. + * @param string $url The URL to the content that is desired to be embedded. + * @param array $args Optional arguments. Usually passed from a shortcode. + * @return bool|object False on failure, otherwise the result in the form of an object. + */ + public function fetch( $provider, $url, $args = '' ) { + $args = wp_parse_args( $args, wp_embed_defaults( $url ) ); + + $provider = add_query_arg( 'maxwidth', (int) $args['width'], $provider ); + $provider = add_query_arg( 'maxheight', (int) $args['height'], $provider ); + $provider = add_query_arg( 'url', urlencode($url), $provider ); + + /** + * Filter the oEmbed URL to be fetched. + * + * @since 2.9.0 + * + * @param string $provider URL of the oEmbed provider. + * @param string $url URL of the content to be embedded. + * @param array $args Optional arguments, usually passed from a shortcode. + */ + $provider = apply_filters( 'oembed_fetch_url', $provider, $url, $args ); + + foreach( array( 'json', 'xml' ) as $format ) { + $result = $this->_fetch_with_format( $provider, $format ); + if ( is_wp_error( $result ) && 'not-implemented' == $result->get_error_code() ) + continue; + return ( $result && ! is_wp_error( $result ) ) ? $result : false; + } + return false; + } + + /** + * Fetches result from an oEmbed provider for a specific format and complete provider URL + * + * @since 3.0.0 + * @access private + * @param string $provider_url_with_args URL to the provider with full arguments list (url, maxheight, etc.) + * @param string $format Format to use + * @return bool|object False on failure, otherwise the result in the form of an object. + */ + private function _fetch_with_format( $provider_url_with_args, $format ) { + $provider_url_with_args = add_query_arg( 'format', $format, $provider_url_with_args ); + + /** This filter is documented in wp-includes/class-oembed.php */ + $args = apply_filters( 'oembed_remote_get_args', array(), $provider_url_with_args ); + + $response = wp_safe_remote_get( $provider_url_with_args, $args ); + if ( 501 == wp_remote_retrieve_response_code( $response ) ) + return new WP_Error( 'not-implemented' ); + if ( ! $body = wp_remote_retrieve_body( $response ) ) + return false; + $parse_method = "_parse_$format"; + return $this->$parse_method( $body ); + } + + /** + * Parses a json response body. + * + * @since 3.0.0 + * @access private + */ + private function _parse_json( $response_body ) { + return ( ( $data = json_decode( trim( $response_body ) ) ) && is_object( $data ) ) ? $data : false; + } + + /** + * Parses an XML response body. + * + * @since 3.0.0 + * @access private + */ + private function _parse_xml( $response_body ) { + if ( ! function_exists( 'libxml_disable_entity_loader' ) ) + return false; + + $loader = libxml_disable_entity_loader( true ); + $errors = libxml_use_internal_errors( true ); + + $return = $this->_parse_xml_body( $response_body ); + + libxml_use_internal_errors( $errors ); + libxml_disable_entity_loader( $loader ); + + return $return; + } + + /** + * Helper function for parsing an XML response body. + * + * @since 3.6.0 + * @access private + */ + private function _parse_xml_body( $response_body ) { + if ( ! function_exists( 'simplexml_import_dom' ) || ! class_exists( 'DOMDocument' ) ) + return false; + + $dom = new DOMDocument; + $success = $dom->loadXML( $response_body ); + if ( ! $success ) + return false; + + if ( isset( $dom->doctype ) ) + return false; + + foreach ( $dom->childNodes as $child ) { + if ( XML_DOCUMENT_TYPE_NODE === $child->nodeType ) + return false; + } + + $xml = simplexml_import_dom( $dom ); + if ( ! $xml ) + return false; + + $return = new stdClass; + foreach ( $xml as $key => $value ) { + $return->$key = (string) $value; + } + + return $return; + } + + /** + * Converts a data object from {@link WP_oEmbed::fetch()} and returns the HTML. + * + * @param object $data A data object result from an oEmbed provider. + * @param string $url The URL to the content that is desired to be embedded. + * @return false|string False on error, otherwise the HTML needed to embed. + */ + public function data2html( $data, $url ) { + if ( ! is_object( $data ) || empty( $data->type ) ) + return false; + + $return = false; + + switch ( $data->type ) { + case 'photo': + if ( empty( $data->url ) || empty( $data->width ) || empty( $data->height ) ) + break; + if ( ! is_string( $data->url ) || ! is_numeric( $data->width ) || ! is_numeric( $data->height ) ) + break; + + $title = ! empty( $data->title ) && is_string( $data->title ) ? $data->title : ''; + $return = '<a href="' . esc_url( $url ) . '"><img src="' . esc_url( $data->url ) . '" alt="' . esc_attr($title) . '" width="' . esc_attr($data->width) . '" height="' . esc_attr($data->height) . '" /></a>'; + break; + + case 'video': + case 'rich': + if ( ! empty( $data->html ) && is_string( $data->html ) ) + $return = $data->html; + break; + + case 'link': + if ( ! empty( $data->title ) && is_string( $data->title ) ) + $return = '<a href="' . esc_url( $url ) . '">' . esc_html( $data->title ) . '</a>'; + break; + + default: + $return = false; + } + + /** + * Filter the returned oEmbed HTML. + * + * Use this filter to add support for custom data types, or to filter the result. + * + * @since 2.9.0 + * + * @param string $return The returned oEmbed HTML. + * @param object $data A data object result from an oEmbed provider. + * @param string $url The URL of the content to be embedded. + */ + return apply_filters( 'oembed_dataparse', $return, $data, $url ); + } + + /** + * Strip any new lines from the HTML. + * + * @access public + * @param string $html Existing HTML. + * @param object $data Data object from WP_oEmbed::data2html() + * @param string $url The original URL passed to oEmbed. + * @return string Possibly modified $html + */ + public function _strip_newlines( $html, $data, $url ) { + if ( false !== strpos( $html, "\n" ) ) + $html = str_replace( array( "\r\n", "\n" ), '', $html ); + + return $html; + } +} + +/** + * Returns the initialized {@link WP_oEmbed} object + * + * @since 2.9.0 + * @access private + * + * @see WP_oEmbed + * + * @return WP_oEmbed object. + */ +function _wp_oembed_get_object() { + static $wp_oembed; + + if ( is_null($wp_oembed) ) + $wp_oembed = new WP_oEmbed(); + + return $wp_oembed; +} diff --git a/wp-includes/class-phpass.php b/wp-includes/class-phpass.php new file mode 100644 index 0000000..f2dadae --- /dev/null +++ b/wp-includes/class-phpass.php @@ -0,0 +1,268 @@ +<?php +/** + * Portable PHP password hashing framework. + * @package phpass + * @since 2.5.0 + * @version 0.3 / WordPress + * @link http://www.openwall.com/phpass/ + */ + +# +# Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in +# the public domain. Revised in subsequent years, still public domain. +# +# There's absolutely no warranty. +# +# Please be sure to update the Version line if you edit this file in any way. +# It is suggested that you leave the main version number intact, but indicate +# your project name (after the slash) and add your own revision information. +# +# Please do not change the "private" password hashing method implemented in +# here, thereby making your hashes incompatible. However, if you must, please +# change the hash type identifier (the "$P$") to something different. +# +# Obviously, since this code is in the public domain, the above are not +# requirements (there can be none), but merely suggestions. +# + +/** + * Portable PHP password hashing framework. + * + * @package phpass + * @version 0.3 / WordPress + * @link http://www.openwall.com/phpass/ + * @since 2.5.0 + */ +class PasswordHash { + var $itoa64; + var $iteration_count_log2; + var $portable_hashes; + var $random_state; + + function PasswordHash($iteration_count_log2, $portable_hashes) + { + $this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; + + if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31) + $iteration_count_log2 = 8; + $this->iteration_count_log2 = $iteration_count_log2; + + $this->portable_hashes = $portable_hashes; + + $this->random_state = microtime() . uniqid(rand(), TRUE); // removed getmypid() for compatibility reasons + } + + function get_random_bytes($count) + { + $output = ''; + if ( @is_readable('/dev/urandom') && + ($fh = @fopen('/dev/urandom', 'rb'))) { + $output = fread($fh, $count); + fclose($fh); + } + + if (strlen($output) < $count) { + $output = ''; + for ($i = 0; $i < $count; $i += 16) { + $this->random_state = + md5(microtime() . $this->random_state); + $output .= + pack('H*', md5($this->random_state)); + } + $output = substr($output, 0, $count); + } + + return $output; + } + + function encode64($input, $count) + { + $output = ''; + $i = 0; + do { + $value = ord($input[$i++]); + $output .= $this->itoa64[$value & 0x3f]; + if ($i < $count) + $value |= ord($input[$i]) << 8; + $output .= $this->itoa64[($value >> 6) & 0x3f]; + if ($i++ >= $count) + break; + if ($i < $count) + $value |= ord($input[$i]) << 16; + $output .= $this->itoa64[($value >> 12) & 0x3f]; + if ($i++ >= $count) + break; + $output .= $this->itoa64[($value >> 18) & 0x3f]; + } while ($i < $count); + + return $output; + } + + function gensalt_private($input) + { + $output = '$P$'; + $output .= $this->itoa64[min($this->iteration_count_log2 + + ((PHP_VERSION >= '5') ? 5 : 3), 30)]; + $output .= $this->encode64($input, 6); + + return $output; + } + + function crypt_private($password, $setting) + { + $output = '*0'; + if (substr($setting, 0, 2) == $output) + $output = '*1'; + + $id = substr($setting, 0, 3); + # We use "$P$", phpBB3 uses "$H$" for the same thing + if ($id != '$P$' && $id != '$H$') + return $output; + + $count_log2 = strpos($this->itoa64, $setting[3]); + if ($count_log2 < 7 || $count_log2 > 30) + return $output; + + $count = 1 << $count_log2; + + $salt = substr($setting, 4, 8); + if (strlen($salt) != 8) + return $output; + + # We're kind of forced to use MD5 here since it's the only + # cryptographic primitive available in all versions of PHP + # currently in use. To implement our own low-level crypto + # in PHP would result in much worse performance and + # consequently in lower iteration counts and hashes that are + # quicker to crack (by non-PHP code). + if (PHP_VERSION >= '5') { + $hash = md5($salt . $password, TRUE); + do { + $hash = md5($hash . $password, TRUE); + } while (--$count); + } else { + $hash = pack('H*', md5($salt . $password)); + do { + $hash = pack('H*', md5($hash . $password)); + } while (--$count); + } + + $output = substr($setting, 0, 12); + $output .= $this->encode64($hash, 16); + + return $output; + } + + function gensalt_extended($input) + { + $count_log2 = min($this->iteration_count_log2 + 8, 24); + # This should be odd to not reveal weak DES keys, and the + # maximum valid value is (2**24 - 1) which is odd anyway. + $count = (1 << $count_log2) - 1; + + $output = '_'; + $output .= $this->itoa64[$count & 0x3f]; + $output .= $this->itoa64[($count >> 6) & 0x3f]; + $output .= $this->itoa64[($count >> 12) & 0x3f]; + $output .= $this->itoa64[($count >> 18) & 0x3f]; + + $output .= $this->encode64($input, 3); + + return $output; + } + + function gensalt_blowfish($input) + { + # This one needs to use a different order of characters and a + # different encoding scheme from the one in encode64() above. + # We care because the last character in our encoded string will + # only represent 2 bits. While two known implementations of + # bcrypt will happily accept and correct a salt string which + # has the 4 unused bits set to non-zero, we do not want to take + # chances and we also do not want to waste an additional byte + # of entropy. + $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + + $output = '$2a$'; + $output .= chr(ord('0') + $this->iteration_count_log2 / 10); + $output .= chr(ord('0') + $this->iteration_count_log2 % 10); + $output .= '$'; + + $i = 0; + do { + $c1 = ord($input[$i++]); + $output .= $itoa64[$c1 >> 2]; + $c1 = ($c1 & 0x03) << 4; + if ($i >= 16) { + $output .= $itoa64[$c1]; + break; + } + + $c2 = ord($input[$i++]); + $c1 |= $c2 >> 4; + $output .= $itoa64[$c1]; + $c1 = ($c2 & 0x0f) << 2; + + $c2 = ord($input[$i++]); + $c1 |= $c2 >> 6; + $output .= $itoa64[$c1]; + $output .= $itoa64[$c2 & 0x3f]; + } while (1); + + return $output; + } + + function HashPassword($password) + { + if ( strlen( $password ) > 4096 ) { + return '*'; + } + + $random = ''; + + if (CRYPT_BLOWFISH == 1 && !$this->portable_hashes) { + $random = $this->get_random_bytes(16); + $hash = + crypt($password, $this->gensalt_blowfish($random)); + if (strlen($hash) == 60) + return $hash; + } + + if (CRYPT_EXT_DES == 1 && !$this->portable_hashes) { + if (strlen($random) < 3) + $random = $this->get_random_bytes(3); + $hash = + crypt($password, $this->gensalt_extended($random)); + if (strlen($hash) == 20) + return $hash; + } + + if (strlen($random) < 6) + $random = $this->get_random_bytes(6); + $hash = + $this->crypt_private($password, + $this->gensalt_private($random)); + if (strlen($hash) == 34) + return $hash; + + # Returning '*' on error is safe here, but would _not_ be safe + # in a crypt(3)-like function used _both_ for generating new + # hashes and for validating passwords against existing hashes. + return '*'; + } + + function CheckPassword($password, $stored_hash) + { + if ( strlen( $password ) > 4096 ) { + return false; + } + + $hash = $this->crypt_private($password, $stored_hash); + if ($hash[0] == '*') + $hash = crypt($password, $stored_hash); + + return $hash === $stored_hash; + } +} + +?> diff --git a/wp-includes/class-phpmailer.php b/wp-includes/class-phpmailer.php new file mode 100644 index 0000000..c38632a --- /dev/null +++ b/wp-includes/class-phpmailer.php @@ -0,0 +1,3265 @@ +<?php +/** + * PHPMailer - PHP email creation and transport class. + * PHP Version 5.0.0 + * Version 5.2.7 + * @package PHPMailer + * @link https://github.com/PHPMailer/PHPMailer/ + * @author Marcus Bointon (coolbru) <phpmailer@synchromedia.co.uk> + * @author Jim Jagielski (jimjag) <jimjag@gmail.com> + * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> + * @author Brent R. Matzelle (original founder) + * @copyright 2013 Marcus Bointon + * @copyright 2010 - 2012 Jim Jagielski + * @copyright 2004 - 2009 Andy Prevost + * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License + * @note This program is distributed in the hope that it will be useful - WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +if (version_compare(PHP_VERSION, '5.0.0', '<')) { + exit("Sorry, PHPMailer will only run on PHP version 5 or greater!\n"); +} + +/** + * PHPMailer - PHP email creation and transport class. + * PHP Version 5.0.0 + * @package PHPMailer + * @author Marcus Bointon (coolbru) <phpmailer@synchromedia.co.uk> + * @author Jim Jagielski (jimjag) <jimjag@gmail.com> + * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> + * @author Brent R. Matzelle (original founder) + * @copyright 2013 Marcus Bointon + * @copyright 2010 - 2012 Jim Jagielski + * @copyright 2004 - 2009 Andy Prevost + */ +class PHPMailer +{ + /** + * The PHPMailer Version number. + * @type string + */ + public $Version = '5.2.7'; + + /** + * Email priority. + * Options: 1 = High, 3 = Normal, 5 = low. + * @type int + */ + public $Priority = 3; + + /** + * The character set of the message. + * @type string + */ + public $CharSet = 'iso-8859-1'; + + /** + * The MIME Content-type of the message. + * @type string + */ + public $ContentType = 'text/plain'; + + /** + * The message encoding. + * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable". + * @type string + */ + public $Encoding = '8bit'; + + /** + * Holds the most recent mailer error message. + * @type string + */ + public $ErrorInfo = ''; + + /** + * The From email address for the message. + * @type string + */ + public $From = 'root@localhost'; + + /** + * The From name of the message. + * @type string + */ + public $FromName = 'Root User'; + + /** + * The Sender email (Return-Path) of the message. + * If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode. + * @type string + */ + public $Sender = ''; + + /** + * The Return-Path of the message. + * If empty, it will be set to either From or Sender. + * @type string + */ + public $ReturnPath = ''; + + /** + * The Subject of the message. + * @type string + */ + public $Subject = ''; + + /** + * An HTML or plain text message body. + * If HTML then call isHTML(true). + * @type string + */ + public $Body = ''; + + /** + * The plain-text message body. + * This body can be read by mail clients that do not have HTML email + * capability such as mutt & Eudora. + * Clients that can read HTML will view the normal Body. + * @type string + */ + public $AltBody = ''; + + /** + * An iCal message part body. + * Only supported in simple alt or alt_inline message types + * To generate iCal events, use the bundled extras/EasyPeasyICS.php class or iCalcreator + * @link http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/ + * @link http://kigkonsult.se/iCalcreator/ + * @type string + */ + public $Ical = ''; + + /** + * The complete compiled MIME message body. + * @access protected + * @type string + */ + protected $MIMEBody = ''; + + /** + * The complete compiled MIME message headers. + * @type string + * @access protected + */ + protected $MIMEHeader = ''; + + /** + * Extra headers that createHeader() doesn't fold in. + * @type string + * @access protected + */ + protected $mailHeader = ''; + + /** + * Word-wrap the message body to this number of chars. + * @type int + */ + public $WordWrap = 0; + + /** + * Which method to use to send mail. + * Options: "mail", "sendmail", or "smtp". + * @type string + */ + public $Mailer = 'mail'; + + /** + * The path to the sendmail program. + * @type string + */ + public $Sendmail = '/usr/sbin/sendmail'; + + /** + * Whether mail() uses a fully sendmail-compatible MTA. + * One which supports sendmail's "-oi -f" options. + * @type bool + */ + public $UseSendmailOptions = true; + + /** + * Path to PHPMailer plugins. + * Useful if the SMTP class is not in the PHP include path. + * @type string + * @deprecated Should not be needed now there is an autoloader. + */ + public $PluginDir = ''; + + /** + * The email address that a reading confirmation should be sent to. + * @type string + */ + public $ConfirmReadingTo = ''; + + /** + * The hostname to use in Message-Id and Received headers + * and as default HELO string. + * If empty, the value returned + * by SERVER_NAME is used or 'localhost.localdomain'. + * @type string + */ + public $Hostname = ''; + + /** + * An ID to be used in the Message-Id header. + * If empty, a unique id will be generated. + * @type string + */ + public $MessageID = ''; + + /** + * The message Date to be used in the Date header. + * If empty, the current date will be added. + * @type string + */ + public $MessageDate = ''; + + /** + * SMTP hosts. + * Either a single hostname or multiple semicolon-delimited hostnames. + * You can also specify a different port + * for each host by using this format: [hostname:port] + * (e.g. "smtp1.example.com:25;smtp2.example.com"). + * Hosts will be tried in order. + * @type string + */ + public $Host = 'localhost'; + + /** + * The default SMTP server port. + * @type int + * @Todo Why is this needed when the SMTP class takes care of it? + */ + public $Port = 25; + + /** + * The SMTP HELO of the message. + * Default is $Hostname. + * @type string + * @see PHPMailer::$Hostname + */ + public $Helo = ''; + + /** + * The secure connection prefix. + * Options: "", "ssl" or "tls" + * @type string + */ + public $SMTPSecure = ''; + + /** + * Whether to use SMTP authentication. + * Uses the Username and Password properties. + * @type bool + * @see PHPMailer::$Username + * @see PHPMailer::$Password + */ + public $SMTPAuth = false; + + /** + * SMTP username. + * @type string + */ + public $Username = ''; + + /** + * SMTP password. + * @type string + */ + public $Password = ''; + + /** + * SMTP auth type. + * Options are LOGIN (default), PLAIN, NTLM, CRAM-MD5 + * @type string + */ + public $AuthType = ''; + + /** + * SMTP realm. + * Used for NTLM auth + * @type string + */ + public $Realm = ''; + + /** + * SMTP workstation. + * Used for NTLM auth + * @type string + */ + public $Workstation = ''; + + /** + * The SMTP server timeout in seconds. + * @type int + */ + public $Timeout = 10; + + /** + * SMTP class debug output mode. + * Options: 0 = off, 1 = commands, 2 = commands and data + * @type int + * @see SMTP::$do_debug + */ + public $SMTPDebug = 0; + + /** + * The function/method to use for debugging output. + * Options: "echo" or "error_log" + * @type string + * @see SMTP::$Debugoutput + */ + public $Debugoutput = "echo"; + + /** + * Whether to keep SMTP connection open after each message. + * If this is set to true then to close the connection + * requires an explicit call to smtpClose(). + * @type bool + */ + public $SMTPKeepAlive = false; + + /** + * Whether to split multiple to addresses into multiple messages + * or send them all in one message. + * @type bool + */ + public $SingleTo = false; + + /** + * Storage for addresses when SingleTo is enabled. + * @type array + * @todo This should really not be public + */ + public $SingleToArray = array(); + + /** + * Whether to generate VERP addresses on send. + * Only applicable when sending via SMTP. + * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path + * @type bool + */ + public $do_verp = false; + + /** + * Whether to allow sending messages with an empty body. + * @type bool + */ + public $AllowEmpty = false; + + /** + * The default line ending. + * @note The default remains "\n". We force CRLF where we know + * it must be used via self::CRLF. + * @type string + */ + public $LE = "\n"; + + /** + * DKIM selector. + * @type string + */ + public $DKIM_selector = ''; + + /** + * DKIM Identity. + * Usually the email address used as the source of the email + * @type string + */ + public $DKIM_identity = ''; + + /** + * DKIM passphrase. + * Used if your key is encrypted. + * @type string + */ + public $DKIM_passphrase = ''; + + /** + * DKIM signing domain name. + * @example 'example.com' + * @type string + */ + public $DKIM_domain = ''; + + /** + * DKIM private key file path. + * @type string + */ + public $DKIM_private = ''; + + /** + * Callback Action function name. + * + * The function that handles the result of the send email action. + * It is called out by send() for each email sent. + * + * Value can be: + * - 'function_name' for function names + * - 'Class::Method' for static method calls + * - array($object, 'Method') for calling methods on $object + * See http://php.net/is_callable manual page for more details. + * + * Parameters: + * bool $result result of the send action + * string $to email address of the recipient + * string $cc cc email addresses + * string $bcc bcc email addresses + * string $subject the subject + * string $body the email body + * string $from email address of sender + * + * @type string + */ + public $action_function = ''; + + /** + * What to use in the X-Mailer header. + * Options: null for default, whitespace for none, or a string to use + * @type string + */ + public $XMailer = ''; + + /** + * An instance of the SMTP sender class. + * @type SMTP + * @access protected + */ + protected $smtp = null; + + /** + * The array of 'to' addresses. + * @type array + * @access protected + */ + protected $to = array(); + + /** + * The array of 'cc' addresses. + * @type array + * @access protected + */ + protected $cc = array(); + + /** + * The array of 'bcc' addresses. + * @type array + * @access protected + */ + protected $bcc = array(); + + /** + * The array of reply-to names and addresses. + * @type array + * @access protected + */ + protected $ReplyTo = array(); + + /** + * An array of all kinds of addresses. + * Includes all of $to, $cc, $bcc, $replyto + * @type array + * @access protected + */ + protected $all_recipients = array(); + + /** + * The array of attachments. + * @type array + * @access protected + */ + protected $attachment = array(); + + /** + * The array of custom headers. + * @type array + * @access protected + */ + protected $CustomHeader = array(); + + /** + * The most recent Message-ID (including angular brackets). + * @type string + * @access protected + */ + protected $lastMessageID = ''; + + /** + * The message's MIME type. + * @type string + * @access protected + */ + protected $message_type = ''; + + /** + * The array of MIME boundary strings. + * @type array + * @access protected + */ + protected $boundary = array(); + + /** + * The array of available languages. + * @type array + * @access protected + */ + protected $language = array(); + + /** + * The number of errors encountered. + * @type integer + * @access protected + */ + protected $error_count = 0; + + /** + * The S/MIME certificate file path. + * @type string + * @access protected + */ + protected $sign_cert_file = ''; + + /** + * The S/MIME key file path. + * @type string + * @access protected + */ + protected $sign_key_file = ''; + + /** + * The S/MIME password for the key. + * Used only if the key is encrypted. + * @type string + * @access protected + */ + protected $sign_key_pass = ''; + + /** + * Whether to throw exceptions for errors. + * @type bool + * @access protected + */ + protected $exceptions = false; + + /** + * Error severity: message only, continue processing + */ + const STOP_MESSAGE = 0; + + /** + * Error severity: message, likely ok to continue processing + */ + const STOP_CONTINUE = 1; + + /** + * Error severity: message, plus full stop, critical error reached + */ + const STOP_CRITICAL = 2; + + /** + * SMTP RFC standard line ending + */ + const CRLF = "\r\n"; + + /** + * Constructor + * @param bool $exceptions Should we throw external exceptions? + */ + public function __construct($exceptions = false) + { + $this->exceptions = ($exceptions == true); + } + + /** + * Destructor. + */ + public function __destruct() + { + if ($this->Mailer == 'smtp') { //close any open SMTP connection nicely + $this->smtpClose(); + } + } + + /** + * Call mail() in a safe_mode-aware fashion. + * Also, unless sendmail_path points to sendmail (or something that + * claims to be sendmail), don't pass params (not a perfect fix, + * but it will do) + * @param string $to To + * @param string $subject Subject + * @param string $body Message Body + * @param string $header Additional Header(s) + * @param string $params Params + * @access private + * @return bool + */ + private function mailPassthru($to, $subject, $body, $header, $params) + { + if (ini_get('safe_mode') || !($this->UseSendmailOptions)) { + $rt = @mail($to, $this->encodeHeader($this->secureHeader($subject)), $body, $header); + } else { + $rt = @mail($to, $this->encodeHeader($this->secureHeader($subject)), $body, $header, $params); + } + return $rt; + } + + /** + * Output debugging info via user-defined method. + * Only if debug output is enabled. + * @see PHPMailer::$Debugoutput + * @see PHPMailer::$SMTPDebug + * @param string $str + */ + protected function edebug($str) + { + if (!$this->SMTPDebug) { + return; + } + switch ($this->Debugoutput) { + case 'error_log': + error_log($str); + break; + case 'html': + //Cleans up output a bit for a better looking display that's HTML-safe + echo htmlentities(preg_replace('/[\r\n]+/', '', $str), ENT_QUOTES, $this->CharSet) . "<br>\n"; + break; + case 'echo': + default: + //Just echoes exactly what was received + echo $str; + } + } + + /** + * Sets message type to HTML or plain. + * @param bool $ishtml True for HTML mode. + * @return void + */ + public function isHTML($ishtml = true) + { + if ($ishtml) { + $this->ContentType = 'text/html'; + } else { + $this->ContentType = 'text/plain'; + } + } + + /** + * Send messages using SMTP. + * @return void + */ + public function isSMTP() + { + $this->Mailer = 'smtp'; + } + + /** + * Send messages using PHP's mail() function. + * @return void + */ + public function isMail() + { + $this->Mailer = 'mail'; + } + + /** + * Send messages using $Sendmail. + * @return void + */ + public function isSendmail() + { + if (!stristr(ini_get('sendmail_path'), 'sendmail')) { + $this->Sendmail = '/var/qmail/bin/sendmail'; + } + $this->Mailer = 'sendmail'; + } + + /** + * Send messages using qmail. + * @return void + */ + public function isQmail() + { + if (stristr(ini_get('sendmail_path'), 'qmail')) { + $this->Sendmail = '/var/qmail/bin/sendmail'; + } + $this->Mailer = 'sendmail'; + } + + /** + * Add a "To" address. + * @param string $address + * @param string $name + * @return bool true on success, false if address already used + */ + public function addAddress($address, $name = '') + { + return $this->addAnAddress('to', $address, $name); + } + + /** + * Add a "CC" address. + * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer. + * @param string $address + * @param string $name + * @return bool true on success, false if address already used + */ + public function addCC($address, $name = '') + { + return $this->addAnAddress('cc', $address, $name); + } + + /** + * Add a "BCC" address. + * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer. + * @param string $address + * @param string $name + * @return bool true on success, false if address already used + */ + public function addBCC($address, $name = '') + { + return $this->addAnAddress('bcc', $address, $name); + } + + /** + * Add a "Reply-to" address. + * @param string $address + * @param string $name + * @return bool + */ + public function addReplyTo($address, $name = '') + { + return $this->addAnAddress('Reply-To', $address, $name); + } + + /** + * Add an address to one of the recipient arrays. + * Addresses that have been added already return false, but do not throw exceptions + * @param string $kind One of 'to', 'cc', 'bcc', 'ReplyTo' + * @param string $address The email address to send to + * @param string $name + * @throws phpmailerException + * @return bool true on success, false if address already used or invalid in some way + * @access protected + */ + protected function addAnAddress($kind, $address, $name = '') + { + if (!preg_match('/^(to|cc|bcc|Reply-To)$/', $kind)) { + $this->setError($this->lang('Invalid recipient array') . ': ' . $kind); + if ($this->exceptions) { + throw new phpmailerException('Invalid recipient array: ' . $kind); + } + $this->edebug($this->lang('Invalid recipient array') . ': ' . $kind); + return false; + } + $address = trim($address); + $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim + if (!$this->validateAddress($address)) { + $this->setError($this->lang('invalid_address') . ': ' . $address); + if ($this->exceptions) { + throw new phpmailerException($this->lang('invalid_address') . ': ' . $address); + } + $this->edebug($this->lang('invalid_address') . ': ' . $address); + return false; + } + if ($kind != 'Reply-To') { + if (!isset($this->all_recipients[strtolower($address)])) { + array_push($this->$kind, array($address, $name)); + $this->all_recipients[strtolower($address)] = true; + return true; + } + } else { + if (!array_key_exists(strtolower($address), $this->ReplyTo)) { + $this->ReplyTo[strtolower($address)] = array($address, $name); + return true; + } + } + return false; + } + + /** + * Set the From and FromName properties. + * @param string $address + * @param string $name + * @param bool $auto Whether to also set the Sender address, defaults to true + * @throws phpmailerException + * @return bool + */ + public function setFrom($address, $name = '', $auto = true) + { + $address = trim($address); + $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim + if (!$this->validateAddress($address)) { + $this->setError($this->lang('invalid_address') . ': ' . $address); + if ($this->exceptions) { + throw new phpmailerException($this->lang('invalid_address') . ': ' . $address); + } + $this->edebug($this->lang('invalid_address') . ': ' . $address); + return false; + } + $this->From = $address; + $this->FromName = $name; + if ($auto) { + if (empty($this->Sender)) { + $this->Sender = $address; + } + } + return true; + } + + /** + * Return the Message-ID header of the last email. + * Technically this is the value from the last time the headers were created, + * but it's also the message ID of the last sent message except in + * pathological cases. + * @return string + */ + public function getLastMessageID() + { + return $this->lastMessageID; + } + + /** + * Check that a string looks like an email address. + * @param string $address The email address to check + * @param string $patternselect A selector for the validation pattern to use : + * 'auto' - pick best one automatically; + * 'pcre8' - use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14; + * 'pcre' - use old PCRE implementation; + * 'php' - use PHP built-in FILTER_VALIDATE_EMAIL; faster, less thorough; + * 'noregex' - super fast, really dumb. + * @return bool + * @static + * @access public + */ + public static function validateAddress($address, $patternselect = 'auto') + { + if ($patternselect == 'auto') { + if (defined( + 'PCRE_VERSION' + ) + ) { //Check this instead of extension_loaded so it works when that function is disabled + if (version_compare(PCRE_VERSION, '8.0') >= 0) { + $patternselect = 'pcre8'; + } else { + $patternselect = 'pcre'; + } + } else { + //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension + if (version_compare(PHP_VERSION, '5.2.0') >= 0) { + $patternselect = 'php'; + } else { + $patternselect = 'noregex'; + } + } + } + switch ($patternselect) { + case 'pcre8': + /** + * Conforms to RFC5322: Uses *correct* regex on which FILTER_VALIDATE_EMAIL is + * based; So why not use FILTER_VALIDATE_EMAIL? Because it was broken to + * not allow a@b type valid addresses :( + * @link http://squiloople.com/2009/12/20/email-address-validation/ + * @copyright 2009-2010 Michael Rushton + * Feel free to use and redistribute this code. But please keep this copyright notice. + */ + return (bool)preg_match( + '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' . + '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' . + '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' . + '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' . + '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' . + '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' . + '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' . + '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' . + '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD', + $address + ); + break; + case 'pcre': + //An older regex that doesn't need a recent PCRE + return (bool)preg_match( + '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' . + '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' . + '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' . + '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' . + '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' . + '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' . + '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' . + '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' . + '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' . + '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD', + $address + ); + break; + case 'php': + default: + return (bool)filter_var($address, FILTER_VALIDATE_EMAIL); + break; + case 'noregex': + //No PCRE! Do something _very_ approximate! + //Check the address is 3 chars or longer and contains an @ that's not the first or last char + return (strlen($address) >= 3 + and strpos($address, '@') >= 1 + and strpos($address, '@') != strlen($address) - 1); + break; + } + } + + /** + * Create a message and send it. + * Uses the sending method specified by $Mailer. + * Returns false on error - Use the ErrorInfo variable to view description of the error. + * @throws phpmailerException + * @return bool + */ + public function send() + { + try { + if (!$this->preSend()) { + return false; + } + return $this->postSend(); + } catch (phpmailerException $e) { + $this->mailHeader = ''; + $this->setError($e->getMessage()); + if ($this->exceptions) { + throw $e; + } + return false; + } + } + + /** + * Prepare a message for sending. + * @throws phpmailerException + * @return bool + */ + public function preSend() + { + try { + $this->mailHeader = ""; + if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) { + throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL); + } + + // Set whether the message is multipart/alternative + if (!empty($this->AltBody)) { + $this->ContentType = 'multipart/alternative'; + } + + $this->error_count = 0; // reset errors + $this->setMessageType(); + // Refuse to send an empty message unless we are specifically allowing it + if (!$this->AllowEmpty and empty($this->Body)) { + throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL); + } + + $this->MIMEHeader = $this->createHeader(); + $this->MIMEBody = $this->createBody(); + + // To capture the complete message when using mail(), create + // an extra header list which createHeader() doesn't fold in + if ($this->Mailer == 'mail') { + if (count($this->to) > 0) { + $this->mailHeader .= $this->addrAppend("To", $this->to); + } else { + $this->mailHeader .= $this->headerLine("To", "undisclosed-recipients:;"); + } + $this->mailHeader .= $this->headerLine( + 'Subject', + $this->encodeHeader($this->secureHeader(trim($this->Subject))) + ); + } + + // Sign with DKIM if enabled + if (!empty($this->DKIM_domain) + && !empty($this->DKIM_private) + && !empty($this->DKIM_selector) + && !empty($this->DKIM_domain) + && file_exists($this->DKIM_private)) { + $header_dkim = $this->DKIM_Add( + $this->MIMEHeader . $this->mailHeader, + $this->encodeHeader($this->secureHeader($this->Subject)), + $this->MIMEBody + ); + $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . self::CRLF . + str_replace("\r\n", "\n", $header_dkim) . self::CRLF; + } + return true; + + } catch (phpmailerException $e) { + $this->setError($e->getMessage()); + if ($this->exceptions) { + throw $e; + } + return false; + } + } + + /** + * Actually send a message. + * Send the email via the selected mechanism + * @throws phpmailerException + * @return bool + */ + public function postSend() + { + try { + // Choose the mailer and send through it + switch ($this->Mailer) { + case 'sendmail': + return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody); + case 'smtp': + return $this->smtpSend($this->MIMEHeader, $this->MIMEBody); + case 'mail': + return $this->mailSend($this->MIMEHeader, $this->MIMEBody); + default: + return $this->mailSend($this->MIMEHeader, $this->MIMEBody); + } + } catch (phpmailerException $e) { + $this->setError($e->getMessage()); + if ($this->exceptions) { + throw $e; + } + $this->edebug($e->getMessage() . "\n"); + } + return false; + } + + /** + * Send mail using the $Sendmail program. + * @param string $header The message headers + * @param string $body The message body + * @see PHPMailer::$Sendmail + * @throws phpmailerException + * @access protected + * @return bool + */ + protected function sendmailSend($header, $body) + { + if ($this->Sender != '') { + $sendmail = sprintf("%s -oi -f%s -t", escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender)); + } else { + $sendmail = sprintf("%s -oi -t", escapeshellcmd($this->Sendmail)); + } + if ($this->SingleTo === true) { + foreach ($this->SingleToArray as $val) { + if (!@$mail = popen($sendmail, 'w')) { + throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + fputs($mail, "To: " . $val . "\n"); + fputs($mail, $header); + fputs($mail, $body); + $result = pclose($mail); + // implement call back function if it exists + $isSent = ($result == 0) ? 1 : 0; + $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body, $this->From); + if ($result != 0) { + throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + } + } else { + if (!@$mail = popen($sendmail, 'w')) { + throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + fputs($mail, $header); + fputs($mail, $body); + $result = pclose($mail); + // implement call back function if it exists + $isSent = ($result == 0) ? 1 : 0; + $this->doCallback($isSent, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From); + if ($result != 0) { + throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL); + } + } + return true; + } + + /** + * Send mail using the PHP mail() function. + * @param string $header The message headers + * @param string $body The message body + * @link http://www.php.net/manual/en/book.mail.php + * @throws phpmailerException + * @access protected + * @return bool + */ + protected function mailSend($header, $body) + { + $toArr = array(); + foreach ($this->to as $t) { + $toArr[] = $this->addrFormat($t); + } + $to = implode(', ', $toArr); + + if (empty($this->Sender)) { + $params = " "; + } else { + $params = sprintf("-f%s", $this->Sender); + } + if ($this->Sender != '' and !ini_get('safe_mode')) { + $old_from = ini_get('sendmail_from'); + ini_set('sendmail_from', $this->Sender); + } + $rt = false; + if ($this->SingleTo === true && count($toArr) > 1) { + foreach ($toArr as $val) { + $rt = $this->mailPassthru($val, $this->Subject, $body, $header, $params); + // implement call back function if it exists + $isSent = ($rt == 1) ? 1 : 0; + $this->doCallback($isSent, $val, $this->cc, $this->bcc, $this->Subject, $body, $this->From); + } + } else { + $rt = $this->mailPassthru($to, $this->Subject, $body, $header, $params); + // implement call back function if it exists + $isSent = ($rt == 1) ? 1 : 0; + $this->doCallback($isSent, $to, $this->cc, $this->bcc, $this->Subject, $body, $this->From); + } + if (isset($old_from)) { + ini_set('sendmail_from', $old_from); + } + if (!$rt) { + throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL); + } + return true; + } + + /** + * Get an instance to use for SMTP operations. + * Override this function to load your own SMTP implementation + * @return SMTP + */ + public function getSMTPInstance() + { + if (!is_object($this->smtp)) { + require_once 'class-smtp.php'; + $this->smtp = new SMTP; + } + return $this->smtp; + } + + /** + * Send mail via SMTP. + * Returns false if there is a bad MAIL FROM, RCPT, or DATA input. + * Uses the PHPMailerSMTP class by default. + * @see PHPMailer::getSMTPInstance() to use a different class. + * @param string $header The message headers + * @param string $body The message body + * @throws phpmailerException + * @uses SMTP + * @access protected + * @return bool + */ + protected function smtpSend($header, $body) + { + $bad_rcpt = array(); + + if (!$this->smtpConnect()) { + throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL); + } + $smtp_from = ($this->Sender == '') ? $this->From : $this->Sender; + if (!$this->smtp->mail($smtp_from)) { + $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError())); + throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL); + } + + // Attempt to send attach all recipients + foreach ($this->to as $to) { + if (!$this->smtp->recipient($to[0])) { + $bad_rcpt[] = $to[0]; + $isSent = 0; + } else { + $isSent = 1; + } + $this->doCallback($isSent, $to[0], '', '', $this->Subject, $body, $this->From); + } + foreach ($this->cc as $cc) { + if (!$this->smtp->recipient($cc[0])) { + $bad_rcpt[] = $cc[0]; + $isSent = 0; + } else { + $isSent = 1; + } + $this->doCallback($isSent, '', $cc[0], '', $this->Subject, $body, $this->From); + } + foreach ($this->bcc as $bcc) { + if (!$this->smtp->recipient($bcc[0])) { + $bad_rcpt[] = $bcc[0]; + $isSent = 0; + } else { + $isSent = 1; + } + $this->doCallback($isSent, '', '', $bcc[0], $this->Subject, $body, $this->From); + } + + if (count($bad_rcpt) > 0) { //Create error message for any bad addresses + throw new phpmailerException($this->lang('recipients_failed') . implode(', ', $bad_rcpt)); + } + if (!$this->smtp->data($header . $body)) { + throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL); + } + if ($this->SMTPKeepAlive == true) { + $this->smtp->reset(); + } else { + $this->smtp->quit(); + $this->smtp->close(); + } + return true; + } + + /** + * Initiate a connection to an SMTP server. + * Returns false if the operation failed. + * @param array $options An array of options compatible with stream_context_create() + * @uses SMTP + * @access public + * @throws phpmailerException + * @return bool + */ + public function smtpConnect($options = array()) + { + if (is_null($this->smtp)) { + $this->smtp = $this->getSMTPInstance(); + } + + //Already connected? + if ($this->smtp->connected()) { + return true; + } + + $this->smtp->setTimeout($this->Timeout); + $this->smtp->setDebugLevel($this->SMTPDebug); + $this->smtp->setDebugOutput($this->Debugoutput); + $this->smtp->setVerp($this->do_verp); + $tls = ($this->SMTPSecure == 'tls'); + $ssl = ($this->SMTPSecure == 'ssl'); + $hosts = explode(';', $this->Host); + $lastexception = null; + + foreach ($hosts as $hostentry) { + $hostinfo = array(); + $host = $hostentry; + $port = $this->Port; + if (preg_match( + '/^(.+):([0-9]+)$/', + $hostentry, + $hostinfo + ) + ) { //If $hostentry contains 'address:port', override default + $host = $hostinfo[1]; + $port = $hostinfo[2]; + } + if ($this->smtp->connect(($ssl ? 'ssl://' : '') . $host, $port, $this->Timeout, $options)) { + try { + if ($this->Helo) { + $hello = $this->Helo; + } else { + $hello = $this->serverHostname(); + } + $this->smtp->hello($hello); + + if ($tls) { + if (!$this->smtp->startTLS()) { + throw new phpmailerException($this->lang('connect_host')); + } + //We must resend HELO after tls negotiation + $this->smtp->hello($hello); + } + if ($this->SMTPAuth) { + if (!$this->smtp->authenticate( + $this->Username, + $this->Password, + $this->AuthType, + $this->Realm, + $this->Workstation + ) + ) { + throw new phpmailerException($this->lang('authenticate')); + } + } + return true; + } catch (phpmailerException $e) { + $lastexception = $e; + //We must have connected, but then failed TLS or Auth, so close connection nicely + $this->smtp->quit(); + } + } + } + //If we get here, all connection attempts have failed, so close connection hard + $this->smtp->close(); + //As we've caught all exceptions, just report whatever the last one was + if ($this->exceptions and !is_null($lastexception)) { + throw $lastexception; + } + return false; + } + + /** + * Close the active SMTP session if one exists. + * @return void + */ + public function smtpClose() + { + if ($this->smtp !== null) { + if ($this->smtp->connected()) { + $this->smtp->quit(); + $this->smtp->close(); + } + } + } + + /** + * Set the language for error messages. + * Returns false if it cannot load the language file. + * The default language is English. + * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr") + * @param string $lang_path Path to the language file directory, with trailing separator (slash) + * @return bool + * @access public + */ + public function setLanguage($langcode = 'en', $lang_path = 'language/') + { + //Define full set of translatable strings + $PHPMAILER_LANG = array( + 'authenticate' => 'SMTP Error: Could not authenticate.', + 'connect_host' => 'SMTP Error: Could not connect to SMTP host.', + 'data_not_accepted' => 'SMTP Error: data not accepted.', + 'empty_message' => 'Message body empty', + 'encoding' => 'Unknown encoding: ', + 'execute' => 'Could not execute: ', + 'file_access' => 'Could not access file: ', + 'file_open' => 'File Error: Could not open file: ', + 'from_failed' => 'The following From address failed: ', + 'instantiate' => 'Could not instantiate mail function.', + 'invalid_address' => 'Invalid address', + 'mailer_not_supported' => ' mailer is not supported.', + 'provide_address' => 'You must provide at least one recipient email address.', + 'recipients_failed' => 'SMTP Error: The following recipients failed: ', + 'signing' => 'Signing Error: ', + 'smtp_connect_failed' => 'SMTP connect() failed.', + 'smtp_error' => 'SMTP server error: ', + 'variable_set' => 'Cannot set or reset variable: ' + ); + //Overwrite language-specific strings. + //This way we'll never have missing translations - no more "language string failed to load"! + $l = true; + if ($langcode != 'en') { //There is no English translation file + $l = @include $lang_path . 'phpmailer.lang-' . $langcode . '.php'; + } + $this->language = $PHPMAILER_LANG; + return ($l == true); //Returns false if language not found + } + + /** + * Get the array of strings for the current language. + * @return array + */ + public function getTranslations() + { + return $this->language; + } + + /** + * Create recipient headers. + * @access public + * @param string $type + * @param array $addr An array of recipient, + * where each recipient is a 2-element indexed array with element 0 containing an address + * and element 1 containing a name, like: + * array(array('joe@example.com', 'Joe User'), array('zoe@example.com', 'Zoe User')) + * @return string + */ + public function addrAppend($type, $addr) + { + $addresses = array(); + foreach ($addr as $a) { + $addresses[] = $this->addrFormat($a); + } + return $type . ': ' . implode(', ', $addresses) . $this->LE; + } + + /** + * Format an address for use in a message header. + * @access public + * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name + * like array('joe@example.com', 'Joe User') + * @return string + */ + public function addrFormat($addr) + { + if (empty($addr[1])) { // No name provided + return $this->secureHeader($addr[0]); + } else { + return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . " <" . $this->secureHeader( + $addr[0] + ) . ">"; + } + } + + /** + * Word-wrap message. + * For use with mailers that do not automatically perform wrapping + * and for quoted-printable encoded messages. + * Original written by philippe. + * @param string $message The message to wrap + * @param integer $length The line length to wrap to + * @param bool $qp_mode Whether to run in Quoted-Printable mode + * @access public + * @return string + */ + public function wrapText($message, $length, $qp_mode = false) + { + $soft_break = ($qp_mode) ? sprintf(" =%s", $this->LE) : $this->LE; + // If utf-8 encoding is used, we will need to make sure we don't + // split multibyte characters when we wrap + $is_utf8 = (strtolower($this->CharSet) == "utf-8"); + $lelen = strlen($this->LE); + $crlflen = strlen(self::CRLF); + + $message = $this->fixEOL($message); + if (substr($message, -$lelen) == $this->LE) { + $message = substr($message, 0, -$lelen); + } + + $line = explode($this->LE, $message); // Magic. We know fixEOL uses $LE + $message = ''; + for ($i = 0; $i < count($line); $i++) { + $line_part = explode(' ', $line[$i]); + $buf = ''; + for ($e = 0; $e < count($line_part); $e++) { + $word = $line_part[$e]; + if ($qp_mode and (strlen($word) > $length)) { + $space_left = $length - strlen($buf) - $crlflen; + if ($e != 0) { + if ($space_left > 20) { + $len = $space_left; + if ($is_utf8) { + $len = $this->utf8CharBoundary($word, $len); + } elseif (substr($word, $len - 1, 1) == "=") { + $len--; + } elseif (substr($word, $len - 2, 1) == "=") { + $len -= 2; + } + $part = substr($word, 0, $len); + $word = substr($word, $len); + $buf .= ' ' . $part; + $message .= $buf . sprintf("=%s", self::CRLF); + } else { + $message .= $buf . $soft_break; + } + $buf = ''; + } + while (strlen($word) > 0) { + if ($length <= 0) { + break; + } + $len = $length; + if ($is_utf8) { + $len = $this->utf8CharBoundary($word, $len); + } elseif (substr($word, $len - 1, 1) == "=") { + $len--; + } elseif (substr($word, $len - 2, 1) == "=") { + $len -= 2; + } + $part = substr($word, 0, $len); + $word = substr($word, $len); + + if (strlen($word) > 0) { + $message .= $part . sprintf("=%s", self::CRLF); + } else { + $buf = $part; + } + } + } else { + $buf_o = $buf; + $buf .= ($e == 0) ? $word : (' ' . $word); + + if (strlen($buf) > $length and $buf_o != '') { + $message .= $buf_o . $soft_break; + $buf = $word; + } + } + } + $message .= $buf . self::CRLF; + } + + return $message; + } + + /** + * Find the last character boundary prior to $maxLength in a utf-8 + * quoted (printable) encoded string. + * Original written by Colin Brown. + * @access public + * @param string $encodedText utf-8 QP text + * @param int $maxLength find last character boundary prior to this length + * @return int + */ + public function utf8CharBoundary($encodedText, $maxLength) + { + $foundSplitPos = false; + $lookBack = 3; + while (!$foundSplitPos) { + $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack); + $encodedCharPos = strpos($lastChunk, "="); + if ($encodedCharPos !== false) { + // Found start of encoded character byte within $lookBack block. + // Check the encoded byte value (the 2 chars after the '=') + $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2); + $dec = hexdec($hex); + if ($dec < 128) { // Single byte character. + // If the encoded char was found at pos 0, it will fit + // otherwise reduce maxLength to start of the encoded char + $maxLength = ($encodedCharPos == 0) ? $maxLength : + $maxLength - ($lookBack - $encodedCharPos); + $foundSplitPos = true; + } elseif ($dec >= 192) { // First byte of a multi byte character + // Reduce maxLength to split at start of character + $maxLength = $maxLength - ($lookBack - $encodedCharPos); + $foundSplitPos = true; + } elseif ($dec < 192) { // Middle byte of a multi byte character, look further back + $lookBack += 3; + } + } else { + // No encoded character found + $foundSplitPos = true; + } + } + return $maxLength; + } + + + /** + * Set the body wrapping. + * @access public + * @return void + */ + public function setWordWrap() + { + if ($this->WordWrap < 1) { + return; + } + + switch ($this->message_type) { + case 'alt': + case 'alt_inline': + case 'alt_attach': + case 'alt_inline_attach': + $this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap); + break; + default: + $this->Body = $this->wrapText($this->Body, $this->WordWrap); + break; + } + } + + /** + * Assemble message headers. + * @access public + * @return string The assembled headers + */ + public function createHeader() + { + $result = ''; + + // Set the boundaries + $uniq_id = md5(uniqid(time())); + $this->boundary[1] = 'b1_' . $uniq_id; + $this->boundary[2] = 'b2_' . $uniq_id; + $this->boundary[3] = 'b3_' . $uniq_id; + + if ($this->MessageDate == '') { + $result .= $this->headerLine('Date', self::rfcDate()); + } else { + $result .= $this->headerLine('Date', $this->MessageDate); + } + + if ($this->ReturnPath) { + $result .= $this->headerLine('Return-Path', '<' . trim($this->ReturnPath) . '>'); + } elseif ($this->Sender == '') { + $result .= $this->headerLine('Return-Path', '<' . trim($this->From) . '>'); + } else { + $result .= $this->headerLine('Return-Path', '<' . trim($this->Sender) . '>'); + } + + // To be created automatically by mail() + if ($this->Mailer != 'mail') { + if ($this->SingleTo === true) { + foreach ($this->to as $t) { + $this->SingleToArray[] = $this->addrFormat($t); + } + } else { + if (count($this->to) > 0) { + $result .= $this->addrAppend('To', $this->to); + } elseif (count($this->cc) == 0) { + $result .= $this->headerLine('To', 'undisclosed-recipients:;'); + } + } + } + + $result .= $this->addrAppend('From', array(array(trim($this->From), $this->FromName))); + + // sendmail and mail() extract Cc from the header before sending + if (count($this->cc) > 0) { + $result .= $this->addrAppend('Cc', $this->cc); + } + + // sendmail and mail() extract Bcc from the header before sending + if ((($this->Mailer == 'sendmail') || ($this->Mailer == 'mail')) && (count($this->bcc) > 0)) { + $result .= $this->addrAppend('Bcc', $this->bcc); + } + + if (count($this->ReplyTo) > 0) { + $result .= $this->addrAppend('Reply-To', $this->ReplyTo); + } + + // mail() sets the subject itself + if ($this->Mailer != 'mail') { + $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject))); + } + + if ($this->MessageID != '') { + $this->lastMessageID = $this->MessageID; + } else { + $this->lastMessageID = sprintf("<%s@%s>", $uniq_id, $this->ServerHostname()); + } + $result .= $this->HeaderLine('Message-ID', $this->lastMessageID); + $result .= $this->headerLine('X-Priority', $this->Priority); + if ($this->XMailer == '') { + $result .= $this->headerLine( + 'X-Mailer', + 'PHPMailer ' . $this->Version . ' (https://github.com/PHPMailer/PHPMailer/)' + ); + } else { + $myXmailer = trim($this->XMailer); + if ($myXmailer) { + $result .= $this->headerLine('X-Mailer', $myXmailer); + } + } + + if ($this->ConfirmReadingTo != '') { + $result .= $this->headerLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>'); + } + + // Add custom headers + for ($index = 0; $index < count($this->CustomHeader); $index++) { + $result .= $this->headerLine( + trim($this->CustomHeader[$index][0]), + $this->encodeHeader(trim($this->CustomHeader[$index][1])) + ); + } + if (!$this->sign_key_file) { + $result .= $this->headerLine('MIME-Version', '1.0'); + $result .= $this->getMailMIME(); + } + + return $result; + } + + /** + * Get the message MIME type headers. + * @access public + * @return string + */ + public function getMailMIME() + { + $result = ''; + switch ($this->message_type) { + case 'inline': + $result .= $this->headerLine('Content-Type', 'multipart/related;'); + $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"'); + break; + case 'attach': + case 'inline_attach': + case 'alt_attach': + case 'alt_inline_attach': + $result .= $this->headerLine('Content-Type', 'multipart/mixed;'); + $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"'); + break; + case 'alt': + case 'alt_inline': + $result .= $this->headerLine('Content-Type', 'multipart/alternative;'); + $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"'); + break; + default: + // Catches case 'plain': and case '': + $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet); + break; + } + //RFC1341 part 5 says 7bit is assumed if not specified + if ($this->Encoding != '7bit') { + $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding); + } + + if ($this->Mailer != 'mail') { + $result .= $this->LE; + } + + return $result; + } + + /** + * Returns the whole MIME message. + * Includes complete headers and body. + * Only valid post PreSend(). + * @see PHPMailer::PreSend() + * @access public + * @return string + */ + public function getSentMIMEMessage() + { + return $this->MIMEHeader . $this->mailHeader . self::CRLF . $this->MIMEBody; + } + + + /** + * Assemble the message body. + * Returns an empty string on failure. + * @access public + * @throws phpmailerException + * @return string The assembled message body + */ + public function createBody() + { + $body = ''; + + if ($this->sign_key_file) { + $body .= $this->getMailMIME() . $this->LE; + } + + $this->setWordWrap(); + + switch ($this->message_type) { + case 'inline': + $body .= $this->getBoundary($this->boundary[1], '', '', ''); + $body .= $this->encodeString($this->Body, $this->Encoding); + $body .= $this->LE . $this->LE; + $body .= $this->attachAll('inline', $this->boundary[1]); + break; + case 'attach': + $body .= $this->getBoundary($this->boundary[1], '', '', ''); + $body .= $this->encodeString($this->Body, $this->Encoding); + $body .= $this->LE . $this->LE; + $body .= $this->attachAll('attachment', $this->boundary[1]); + break; + case 'inline_attach': + $body .= $this->textLine('--' . $this->boundary[1]); + $body .= $this->headerLine('Content-Type', 'multipart/related;'); + $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); + $body .= $this->LE; + $body .= $this->getBoundary($this->boundary[2], '', '', ''); + $body .= $this->encodeString($this->Body, $this->Encoding); + $body .= $this->LE . $this->LE; + $body .= $this->attachAll('inline', $this->boundary[2]); + $body .= $this->LE; + $body .= $this->attachAll('attachment', $this->boundary[1]); + break; + case 'alt': + $body .= $this->getBoundary($this->boundary[1], '', 'text/plain', ''); + $body .= $this->encodeString($this->AltBody, $this->Encoding); + $body .= $this->LE . $this->LE; + $body .= $this->getBoundary($this->boundary[1], '', 'text/html', ''); + $body .= $this->encodeString($this->Body, $this->Encoding); + $body .= $this->LE . $this->LE; + if (!empty($this->Ical)) { + $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', ''); + $body .= $this->encodeString($this->Ical, $this->Encoding); + $body .= $this->LE . $this->LE; + } + $body .= $this->endBoundary($this->boundary[1]); + break; + case 'alt_inline': + $body .= $this->getBoundary($this->boundary[1], '', 'text/plain', ''); + $body .= $this->encodeString($this->AltBody, $this->Encoding); + $body .= $this->LE . $this->LE; + $body .= $this->textLine('--' . $this->boundary[1]); + $body .= $this->headerLine('Content-Type', 'multipart/related;'); + $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); + $body .= $this->LE; + $body .= $this->getBoundary($this->boundary[2], '', 'text/html', ''); + $body .= $this->encodeString($this->Body, $this->Encoding); + $body .= $this->LE . $this->LE; + $body .= $this->attachAll('inline', $this->boundary[2]); + $body .= $this->LE; + $body .= $this->endBoundary($this->boundary[1]); + break; + case 'alt_attach': + $body .= $this->textLine('--' . $this->boundary[1]); + $body .= $this->headerLine('Content-Type', 'multipart/alternative;'); + $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); + $body .= $this->LE; + $body .= $this->getBoundary($this->boundary[2], '', 'text/plain', ''); + $body .= $this->encodeString($this->AltBody, $this->Encoding); + $body .= $this->LE . $this->LE; + $body .= $this->getBoundary($this->boundary[2], '', 'text/html', ''); + $body .= $this->encodeString($this->Body, $this->Encoding); + $body .= $this->LE . $this->LE; + $body .= $this->endBoundary($this->boundary[2]); + $body .= $this->LE; + $body .= $this->attachAll('attachment', $this->boundary[1]); + break; + case 'alt_inline_attach': + $body .= $this->textLine('--' . $this->boundary[1]); + $body .= $this->headerLine('Content-Type', 'multipart/alternative;'); + $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"'); + $body .= $this->LE; + $body .= $this->getBoundary($this->boundary[2], '', 'text/plain', ''); + $body .= $this->encodeString($this->AltBody, $this->Encoding); + $body .= $this->LE . $this->LE; + $body .= $this->textLine('--' . $this->boundary[2]); + $body .= $this->headerLine('Content-Type', 'multipart/related;'); + $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"'); + $body .= $this->LE; + $body .= $this->getBoundary($this->boundary[3], '', 'text/html', ''); + $body .= $this->encodeString($this->Body, $this->Encoding); + $body .= $this->LE . $this->LE; + $body .= $this->attachAll('inline', $this->boundary[3]); + $body .= $this->LE; + $body .= $this->endBoundary($this->boundary[2]); + $body .= $this->LE; + $body .= $this->attachAll('attachment', $this->boundary[1]); + break; + default: + // catch case 'plain' and case '' + $body .= $this->encodeString($this->Body, $this->Encoding); + break; + } + + if ($this->isError()) { + $body = ''; + } elseif ($this->sign_key_file) { + try { + if (!defined('PKCS7_TEXT')) { + throw new phpmailerException($this->lang('signing') . ' OpenSSL extension missing.'); + } + $file = tempnam(sys_get_temp_dir(), 'mail'); + file_put_contents($file, $body); //TODO check this worked + $signed = tempnam(sys_get_temp_dir(), 'signed'); + if (@openssl_pkcs7_sign( + $file, + $signed, + 'file://' . realpath($this->sign_cert_file), + array('file://' . realpath($this->sign_key_file), $this->sign_key_pass), + null + ) + ) { + @unlink($file); + $body = file_get_contents($signed); + @unlink($signed); + } else { + @unlink($file); + @unlink($signed); + throw new phpmailerException($this->lang('signing') . openssl_error_string()); + } + } catch (phpmailerException $e) { + $body = ''; + if ($this->exceptions) { + throw $e; + } + } + } + return $body; + } + + /** + * Return the start of a message boundary. + * @access protected + * @param string $boundary + * @param string $charSet + * @param string $contentType + * @param string $encoding + * @return string + */ + protected function getBoundary($boundary, $charSet, $contentType, $encoding) + { + $result = ''; + if ($charSet == '') { + $charSet = $this->CharSet; + } + if ($contentType == '') { + $contentType = $this->ContentType; + } + if ($encoding == '') { + $encoding = $this->Encoding; + } + $result .= $this->textLine('--' . $boundary); + $result .= sprintf("Content-Type: %s; charset=%s", $contentType, $charSet); + $result .= $this->LE; + $result .= $this->headerLine('Content-Transfer-Encoding', $encoding); + $result .= $this->LE; + + return $result; + } + + /** + * Return the end of a message boundary. + * @access protected + * @param string $boundary + * @return string + */ + protected function endBoundary($boundary) + { + return $this->LE . '--' . $boundary . '--' . $this->LE; + } + + /** + * Set the message type. + * PHPMailer only supports some preset message types, + * not arbitrary MIME structures. + * @access protected + * @return void + */ + protected function setMessageType() + { + $this->message_type = array(); + if ($this->alternativeExists()) { + $this->message_type[] = "alt"; + } + if ($this->inlineImageExists()) { + $this->message_type[] = "inline"; + } + if ($this->attachmentExists()) { + $this->message_type[] = "attach"; + } + $this->message_type = implode("_", $this->message_type); + if ($this->message_type == "") { + $this->message_type = "plain"; + } + } + + /** + * Format a header line. + * @access public + * @param string $name + * @param string $value + * @return string + */ + public function headerLine($name, $value) + { + return $name . ': ' . $value . $this->LE; + } + + /** + * Return a formatted mail line. + * @access public + * @param string $value + * @return string + */ + public function textLine($value) + { + return $value . $this->LE; + } + + /** + * Add an attachment from a path on the filesystem. + * Returns false if the file could not be found or read. + * @param string $path Path to the attachment. + * @param string $name Overrides the attachment name. + * @param string $encoding File encoding (see $Encoding). + * @param string $type File extension (MIME) type. + * @param string $disposition Disposition to use + * @throws phpmailerException + * @return bool + */ + public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment') + { + try { + if (!@is_file($path)) { + throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE); + } + + //If a MIME type is not specified, try to work it out from the file name + if ($type == '') { + $type = self::filenameToType($path); + } + + $filename = basename($path); + if ($name == '') { + $name = $filename; + } + + $this->attachment[] = array( + 0 => $path, + 1 => $filename, + 2 => $name, + 3 => $encoding, + 4 => $type, + 5 => false, // isStringAttachment + 6 => $disposition, + 7 => 0 + ); + + } catch (phpmailerException $e) { + $this->setError($e->getMessage()); + if ($this->exceptions) { + throw $e; + } + $this->edebug($e->getMessage() . "\n"); + return false; + } + return true; + } + + /** + * Return the array of attachments. + * @return array + */ + public function getAttachments() + { + return $this->attachment; + } + + /** + * Attach all file, string, and binary attachments to the message. + * Returns an empty string on failure. + * @access protected + * @param string $disposition_type + * @param string $boundary + * @return string + */ + protected function attachAll($disposition_type, $boundary) + { + // Return text of body + $mime = array(); + $cidUniq = array(); + $incl = array(); + + // Add all attachments + foreach ($this->attachment as $attachment) { + // Check if it is a valid disposition_filter + if ($attachment[6] == $disposition_type) { + // Check for string attachment + $string = ''; + $path = ''; + $bString = $attachment[5]; + if ($bString) { + $string = $attachment[0]; + } else { + $path = $attachment[0]; + } + + $inclhash = md5(serialize($attachment)); + if (in_array($inclhash, $incl)) { + continue; + } + $incl[] = $inclhash; + $name = $attachment[2]; + $encoding = $attachment[3]; + $type = $attachment[4]; + $disposition = $attachment[6]; + $cid = $attachment[7]; + if ($disposition == 'inline' && isset($cidUniq[$cid])) { + continue; + } + $cidUniq[$cid] = true; + + $mime[] = sprintf("--%s%s", $boundary, $this->LE); + $mime[] = sprintf( + "Content-Type: %s; name=\"%s\"%s", + $type, + $this->encodeHeader($this->secureHeader($name)), + $this->LE + ); + $mime[] = sprintf("Content-Transfer-Encoding: %s%s", $encoding, $this->LE); + + if ($disposition == 'inline') { + $mime[] = sprintf("Content-ID: <%s>%s", $cid, $this->LE); + } + + // If a filename contains any of these chars, it should be quoted, + // but not otherwise: RFC2183 & RFC2045 5.1 + // Fixes a warning in IETF's msglint MIME checker + // Allow for bypassing the Content-Disposition header totally + if (!(empty($disposition))) { + if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $name)) { + $mime[] = sprintf( + "Content-Disposition: %s; filename=\"%s\"%s", + $disposition, + $this->encodeHeader($this->secureHeader($name)), + $this->LE . $this->LE + ); + } else { + $mime[] = sprintf( + "Content-Disposition: %s; filename=%s%s", + $disposition, + $this->encodeHeader($this->secureHeader($name)), + $this->LE . $this->LE + ); + } + } else { + $mime[] = $this->LE; + } + + // Encode as string attachment + if ($bString) { + $mime[] = $this->encodeString($string, $encoding); + if ($this->isError()) { + return ''; + } + $mime[] = $this->LE . $this->LE; + } else { + $mime[] = $this->encodeFile($path, $encoding); + if ($this->isError()) { + return ''; + } + $mime[] = $this->LE . $this->LE; + } + } + } + + $mime[] = sprintf("--%s--%s", $boundary, $this->LE); + + return implode("", $mime); + } + + /** + * Encode a file attachment in requested format. + * Returns an empty string on failure. + * @param string $path The full path to the file + * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' + * @throws phpmailerException + * @see EncodeFile(encodeFile + * @access protected + * @return string + */ + protected function encodeFile($path, $encoding = 'base64') + { + try { + if (!is_readable($path)) { + throw new phpmailerException($this->lang('file_open') . $path, self::STOP_CONTINUE); + } + $magic_quotes = get_magic_quotes_runtime(); + if ($magic_quotes) { + if (version_compare(PHP_VERSION, '5.3.0', '<')) { + set_magic_quotes_runtime(0); + } else { + ini_set('magic_quotes_runtime', 0); + } + } + $file_buffer = file_get_contents($path); + $file_buffer = $this->encodeString($file_buffer, $encoding); + if ($magic_quotes) { + if (version_compare(PHP_VERSION, '5.3.0', '<')) { + set_magic_quotes_runtime($magic_quotes); + } else { + ini_set('magic_quotes_runtime', $magic_quotes); + } + } + return $file_buffer; + } catch (Exception $e) { + $this->setError($e->getMessage()); + return ''; + } + } + + /** + * Encode a string in requested format. + * Returns an empty string on failure. + * @param string $str The text to encode + * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable' + * @access public + * @return string + */ + public function encodeString($str, $encoding = 'base64') + { + $encoded = ''; + switch (strtolower($encoding)) { + case 'base64': + $encoded = chunk_split(base64_encode($str), 76, $this->LE); + break; + case '7bit': + case '8bit': + $encoded = $this->fixEOL($str); + //Make sure it ends with a line break + if (substr($encoded, -(strlen($this->LE))) != $this->LE) { + $encoded .= $this->LE; + } + break; + case 'binary': + $encoded = $str; + break; + case 'quoted-printable': + $encoded = $this->encodeQP($str); + break; + default: + $this->setError($this->lang('encoding') . $encoding); + break; + } + return $encoded; + } + + /** + * Encode a header string optimally. + * Picks shortest of Q, B, quoted-printable or none. + * @access public + * @param string $str + * @param string $position + * @return string + */ + public function encodeHeader($str, $position = 'text') + { + $x = 0; + switch (strtolower($position)) { + case 'phrase': + if (!preg_match('/[\200-\377]/', $str)) { + // Can't use addslashes as we don't know what value has magic_quotes_sybase + $encoded = addcslashes($str, "\0..\37\177\\\""); + if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) { + return ($encoded); + } else { + return ("\"$encoded\""); + } + } + $x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches); + break; + /** @noinspection PhpMissingBreakStatementInspection */ + case 'comment': + $x = preg_match_all('/[()"]/', $str, $matches); + // Intentional fall-through + case 'text': + default: + $x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches); + break; + } + + if ($x == 0) { //There are no chars that need encoding + return ($str); + } + + $maxlen = 75 - 7 - strlen($this->CharSet); + // Try to select the encoding which should produce the shortest output + if ($x > strlen($str) / 3) { + //More than a third of the content will need encoding, so B encoding will be most efficient + $encoding = 'B'; + if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) { + // Use a custom function which correctly encodes and wraps long + // multibyte strings without breaking lines within a character + $encoded = $this->base64EncodeWrapMB($str, "\n"); + } else { + $encoded = base64_encode($str); + $maxlen -= $maxlen % 4; + $encoded = trim(chunk_split($encoded, $maxlen, "\n")); + } + } else { + $encoding = 'Q'; + $encoded = $this->encodeQ($str, $position); + $encoded = $this->wrapText($encoded, $maxlen, true); + $encoded = str_replace('=' . self::CRLF, "\n", trim($encoded)); + } + + $encoded = preg_replace('/^(.*)$/m', " =?" . $this->CharSet . "?$encoding?\\1?=", $encoded); + $encoded = trim(str_replace("\n", $this->LE, $encoded)); + + return $encoded; + } + + /** + * Check if a string contains multi-byte characters. + * @access public + * @param string $str multi-byte text to wrap encode + * @return bool + */ + public function hasMultiBytes($str) + { + if (function_exists('mb_strlen')) { + return (strlen($str) > mb_strlen($str, $this->CharSet)); + } else { // Assume no multibytes (we can't handle without mbstring functions anyway) + return false; + } + } + + /** + * Encode and wrap long multibyte strings for mail headers + * without breaking lines within a character. + * Adapted from a function by paravoid at http://uk.php.net/manual/en/function.mb-encode-mimeheader.php + * @access public + * @param string $str multi-byte text to wrap encode + * @param string $lf string to use as linefeed/end-of-line + * @return string + */ + public function base64EncodeWrapMB($str, $lf = null) + { + $start = "=?" . $this->CharSet . "?B?"; + $end = "?="; + $encoded = ""; + if ($lf === null) { + $lf = $this->LE; + } + + $mb_length = mb_strlen($str, $this->CharSet); + // Each line must have length <= 75, including $start and $end + $length = 75 - strlen($start) - strlen($end); + // Average multi-byte ratio + $ratio = $mb_length / strlen($str); + // Base64 has a 4:3 ratio + $avgLength = floor($length * $ratio * .75); + + for ($i = 0; $i < $mb_length; $i += $offset) { + $lookBack = 0; + do { + $offset = $avgLength - $lookBack; + $chunk = mb_substr($str, $i, $offset, $this->CharSet); + $chunk = base64_encode($chunk); + $lookBack++; + } while (strlen($chunk) > $length); + $encoded .= $chunk . $lf; + } + + // Chomp the last linefeed + $encoded = substr($encoded, 0, -strlen($lf)); + return $encoded; + } + + /** + * Encode a string in quoted-printable format. + * According to RFC2045 section 6.7. + * @access public + * @param string $string The text to encode + * @param integer $line_max Number of chars allowed on a line before wrapping + * @return string + * @link PHP version adapted from http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 + */ + public function encodeQP($string, $line_max = 76) + { + if (function_exists('quoted_printable_encode')) { //Use native function if it's available (>= PHP5.3) + return quoted_printable_encode($string); + } + //Fall back to a pure PHP implementation + $string = str_replace( + array('%20', '%0D%0A.', '%0D%0A', '%'), + array(' ', "\r\n=2E", "\r\n", '='), + rawurlencode($string) + ); + $string = preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string); + return $string; + } + + /** + * Backward compatibility wrapper for an old QP encoding function that was removed. + * @see PHPMailer::encodeQP() + * @access public + * @param string $string + * @param integer $line_max + * @param bool $space_conv + * @return string + * @deprecated Use encodeQP instead. + */ + public function encodeQPphp( + $string, + $line_max = 76, + /** @noinspection PhpUnusedParameterInspection */ $space_conv = false + ) { + return $this->encodeQP($string, $line_max); + } + + /** + * Encode a string using Q encoding. + * @link http://tools.ietf.org/html/rfc2047 + * @param string $str the text to encode + * @param string $position Where the text is going to be used, see the RFC for what that means + * @access public + * @return string + */ + public function encodeQ($str, $position = 'text') + { + //There should not be any EOL in the string + $pattern = ''; + $encoded = str_replace(array("\r", "\n"), '', $str); + switch (strtolower($position)) { + case 'phrase': + //RFC 2047 section 5.3 + $pattern = '^A-Za-z0-9!*+\/ -'; + break; + /** @noinspection PhpMissingBreakStatementInspection */ + case 'comment': + //RFC 2047 section 5.2 + $pattern = '\(\)"'; + //intentional fall-through + //for this reason we build the $pattern without including delimiters and [] + case 'text': + default: + //RFC 2047 section 5.1 + //Replace every high ascii, control, =, ? and _ characters + $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern; + break; + } + $matches = array(); + if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) { + //If the string contains an '=', make sure it's the first thing we replace + //so as to avoid double-encoding + $s = array_search('=', $matches[0]); + if ($s !== false) { + unset($matches[0][$s]); + array_unshift($matches[0], '='); + } + foreach (array_unique($matches[0]) as $char) { + $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded); + } + } + //Replace every spaces to _ (more readable than =20) + return str_replace(' ', '_', $encoded); + } + + + /** + * Add a string or binary attachment (non-filesystem). + * This method can be used to attach ascii or binary data, + * such as a BLOB record from a database. + * @param string $string String attachment data. + * @param string $filename Name of the attachment. + * @param string $encoding File encoding (see $Encoding). + * @param string $type File extension (MIME) type. + * @param string $disposition Disposition to use + * @return void + */ + public function addStringAttachment( + $string, + $filename, + $encoding = 'base64', + $type = '', + $disposition = 'attachment' + ) { + //If a MIME type is not specified, try to work it out from the file name + if ($type == '') { + $type = self::filenameToType($filename); + } + // Append to $attachment array + $this->attachment[] = array( + 0 => $string, + 1 => $filename, + 2 => basename($filename), + 3 => $encoding, + 4 => $type, + 5 => true, // isStringAttachment + 6 => $disposition, + 7 => 0 + ); + } + + /** + * Add an embedded (inline) attachment from a file. + * This can include images, sounds, and just about any other document type. + * These differ from 'regular' attachmants in that they are intended to be + * displayed inline with the message, not just attached for download. + * This is used in HTML messages that embed the images + * the HTML refers to using the $cid value. + * @param string $path Path to the attachment. + * @param string $cid Content ID of the attachment; Use this to reference + * the content when using an embedded image in HTML. + * @param string $name Overrides the attachment name. + * @param string $encoding File encoding (see $Encoding). + * @param string $type File MIME type. + * @param string $disposition Disposition to use + * @return bool True on successfully adding an attachment + */ + public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline') + { + if (!@is_file($path)) { + $this->setError($this->lang('file_access') . $path); + return false; + } + + //If a MIME type is not specified, try to work it out from the file name + if ($type == '') { + $type = self::filenameToType($path); + } + + $filename = basename($path); + if ($name == '') { + $name = $filename; + } + + // Append to $attachment array + $this->attachment[] = array( + 0 => $path, + 1 => $filename, + 2 => $name, + 3 => $encoding, + 4 => $type, + 5 => false, // isStringAttachment + 6 => $disposition, + 7 => $cid + ); + return true; + } + + /** + * Add an embedded stringified attachment. + * This can include images, sounds, and just about any other document type. + * Be sure to set the $type to an image type for images: + * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'. + * @param string $string The attachment binary data. + * @param string $cid Content ID of the attachment; Use this to reference + * the content when using an embedded image in HTML. + * @param string $name + * @param string $encoding File encoding (see $Encoding). + * @param string $type MIME type. + * @param string $disposition Disposition to use + * @return bool True on successfully adding an attachment + */ + public function addStringEmbeddedImage( + $string, + $cid, + $name = '', + $encoding = 'base64', + $type = '', + $disposition = 'inline' + ) { + //If a MIME type is not specified, try to work it out from the name + if ($type == '') { + $type = self::filenameToType($name); + } + + // Append to $attachment array + $this->attachment[] = array( + 0 => $string, + 1 => $name, + 2 => $name, + 3 => $encoding, + 4 => $type, + 5 => true, // isStringAttachment + 6 => $disposition, + 7 => $cid + ); + return true; + } + + /** + * Check if an inline attachment is present. + * @access public + * @return bool + */ + public function inlineImageExists() + { + foreach ($this->attachment as $attachment) { + if ($attachment[6] == 'inline') { + return true; + } + } + return false; + } + + /** + * Check if an attachment (non-inline) is present. + * @return bool + */ + public function attachmentExists() + { + foreach ($this->attachment as $attachment) { + if ($attachment[6] == 'attachment') { + return true; + } + } + return false; + } + + /** + * Check if this message has an alternative body set. + * @return bool + */ + public function alternativeExists() + { + return !empty($this->AltBody); + } + + /** + * Clear all To recipients. + * @return void + */ + public function clearAddresses() + { + foreach ($this->to as $to) { + unset($this->all_recipients[strtolower($to[0])]); + } + $this->to = array(); + } + + /** + * Clear all CC recipients. + * @return void + */ + public function clearCCs() + { + foreach ($this->cc as $cc) { + unset($this->all_recipients[strtolower($cc[0])]); + } + $this->cc = array(); + } + + /** + * Clear all BCC recipients. + * @return void + */ + public function clearBCCs() + { + foreach ($this->bcc as $bcc) { + unset($this->all_recipients[strtolower($bcc[0])]); + } + $this->bcc = array(); + } + + /** + * Clear all ReplyTo recipients. + * @return void + */ + public function clearReplyTos() + { + $this->ReplyTo = array(); + } + + /** + * Clear all recipient types. + * @return void + */ + public function clearAllRecipients() + { + $this->to = array(); + $this->cc = array(); + $this->bcc = array(); + $this->all_recipients = array(); + } + + /** + * Clear all filesystem, string, and binary attachments. + * @return void + */ + public function clearAttachments() + { + $this->attachment = array(); + } + + /** + * Clear all custom headers. + * @return void + */ + public function clearCustomHeaders() + { + $this->CustomHeader = array(); + } + + /** + * Add an error message to the error container. + * @access protected + * @param string $msg + * @return void + */ + protected function setError($msg) + { + $this->error_count++; + if ($this->Mailer == 'smtp' and !is_null($this->smtp)) { + $lasterror = $this->smtp->getError(); + if (!empty($lasterror) and array_key_exists('smtp_msg', $lasterror)) { + $msg .= '<p>' . $this->lang('smtp_error') . $lasterror['smtp_msg'] . "</p>\n"; + } + } + $this->ErrorInfo = $msg; + } + + /** + * Return an RFC 822 formatted date. + * @access public + * @return string + * @static + */ + public static function rfcDate() + { + //Set the time zone to whatever the default is to avoid 500 errors + //Will default to UTC if it's not set properly in php.ini + date_default_timezone_set(@date_default_timezone_get()); + return date('D, j M Y H:i:s O'); + } + + /** + * Get the server hostname. + * Returns 'localhost.localdomain' if unknown. + * @access protected + * @return string + */ + protected function serverHostname() + { + if (!empty($this->Hostname)) { + $result = $this->Hostname; + } elseif (isset($_SERVER['SERVER_NAME'])) { + $result = $_SERVER['SERVER_NAME']; + } else { + $result = 'localhost.localdomain'; + } + + return $result; + } + + /** + * Get an error message in the current language. + * @access protected + * @param string $key + * @return string + */ + protected function lang($key) + { + if (count($this->language) < 1) { + $this->setLanguage('en'); // set the default language + } + + if (isset($this->language[$key])) { + return $this->language[$key]; + } else { + return 'Language string failed to load: ' . $key; + } + } + + /** + * Check if an error occurred. + * @access public + * @return bool True if an error did occur. + */ + public function isError() + { + return ($this->error_count > 0); + } + + /** + * Ensure consistent line endings in a string. + * Changes every end of line from CRLF, CR or LF to $this->LE. + * @access public + * @param string $str String to fixEOL + * @return string + */ + public function fixEOL($str) + { + // Normalise to \n + $nstr = str_replace(array("\r\n", "\r"), "\n", $str); + // Now convert LE as needed + if ($this->LE !== "\n") { + $nstr = str_replace("\n", $this->LE, $nstr); + } + return $nstr; + } + + /** + * Add a custom header. + * $name value can be overloaded to contain + * both header name and value (name:value) + * @access public + * @param string $name Custom header name + * @param string $value Header value + * @return void + */ + public function addCustomHeader($name, $value = null) + { + if ($value === null) { + // Value passed in as name:value + $this->CustomHeader[] = explode(':', $name, 2); + } else { + $this->CustomHeader[] = array($name, $value); + } + } + + /** + * Create a message from an HTML string. + * Automatically makes modifications for inline images and backgrounds + * and creates a plain-text version by converting the HTML. + * Overwrites any existing values in $this->Body and $this->AltBody + * @access public + * @param string $message HTML message string + * @param string $basedir baseline directory for path + * @param bool $advanced Whether to use the advanced HTML to text converter + * @return string $message + */ + public function msgHTML($message, $basedir = '', $advanced = false) + { + preg_match_all("/(src|background)=[\"'](.*)[\"']/Ui", $message, $images); + if (isset($images[2])) { + foreach ($images[2] as $i => $url) { + // do not change urls for absolute images (thanks to corvuscorax) + if (!preg_match('#^[A-z]+://#', $url)) { + $filename = basename($url); + $directory = dirname($url); + if ($directory == '.') { + $directory = ''; + } + $cid = md5($url) . '@phpmailer.0'; //RFC2392 S 2 + if (strlen($basedir) > 1 && substr($basedir, -1) != '/') { + $basedir .= '/'; + } + if (strlen($directory) > 1 && substr($directory, -1) != '/') { + $directory .= '/'; + } + if ($this->addEmbeddedImage( + $basedir . $directory . $filename, + $cid, + $filename, + 'base64', + self::_mime_types(self::mb_pathinfo($filename, PATHINFO_EXTENSION)) + ) + ) { + $message = preg_replace( + "/" . $images[1][$i] . "=[\"']" . preg_quote($url, '/') . "[\"']/Ui", + $images[1][$i] . "=\"cid:" . $cid . "\"", + $message + ); + } + } + } + } + $this->isHTML(true); + if (empty($this->AltBody)) { + $this->AltBody = 'To view this email message, open it in a program that understands HTML!' . "\n\n"; + } + //Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better + $this->Body = $this->normalizeBreaks($message); + $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced)); + return $this->Body; + } + + /** + * Convert an HTML string into plain text. + * @param string $html The HTML text to convert + * @param bool $advanced Should this use the more complex html2text converter or just a simple one? + * @return string + */ + public function html2text($html, $advanced = false) + { + if ($advanced) { + require_once 'extras/class.html2text.php'; + $h = new html2text($html); + return $h->get_text(); + } + return html_entity_decode( + trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))), + ENT_QUOTES, + $this->CharSet + ); + } + + /** + * Get the MIME type for a file extension. + * @param string $ext File extension + * @access public + * @return string MIME type of file. + * @static + */ + public static function _mime_types($ext = '') + { + $mimes = array( + 'xl' => 'application/excel', + 'hqx' => 'application/mac-binhex40', + 'cpt' => 'application/mac-compactpro', + 'bin' => 'application/macbinary', + 'doc' => 'application/msword', + 'word' => 'application/msword', + 'class' => 'application/octet-stream', + 'dll' => 'application/octet-stream', + 'dms' => 'application/octet-stream', + 'exe' => 'application/octet-stream', + 'lha' => 'application/octet-stream', + 'lzh' => 'application/octet-stream', + 'psd' => 'application/octet-stream', + 'sea' => 'application/octet-stream', + 'so' => 'application/octet-stream', + 'oda' => 'application/oda', + 'pdf' => 'application/pdf', + 'ai' => 'application/postscript', + 'eps' => 'application/postscript', + 'ps' => 'application/postscript', + 'smi' => 'application/smil', + 'smil' => 'application/smil', + 'mif' => 'application/vnd.mif', + 'xls' => 'application/vnd.ms-excel', + 'ppt' => 'application/vnd.ms-powerpoint', + 'wbxml' => 'application/vnd.wap.wbxml', + 'wmlc' => 'application/vnd.wap.wmlc', + 'dcr' => 'application/x-director', + 'dir' => 'application/x-director', + 'dxr' => 'application/x-director', + 'dvi' => 'application/x-dvi', + 'gtar' => 'application/x-gtar', + 'php3' => 'application/x-httpd-php', + 'php4' => 'application/x-httpd-php', + 'php' => 'application/x-httpd-php', + 'phtml' => 'application/x-httpd-php', + 'phps' => 'application/x-httpd-php-source', + 'js' => 'application/x-javascript', + 'swf' => 'application/x-shockwave-flash', + 'sit' => 'application/x-stuffit', + 'tar' => 'application/x-tar', + 'tgz' => 'application/x-tar', + 'xht' => 'application/xhtml+xml', + 'xhtml' => 'application/xhtml+xml', + 'zip' => 'application/zip', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mp2' => 'audio/mpeg', + 'mp3' => 'audio/mpeg', + 'mpga' => 'audio/mpeg', + 'aif' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'ram' => 'audio/x-pn-realaudio', + 'rm' => 'audio/x-pn-realaudio', + 'rpm' => 'audio/x-pn-realaudio-plugin', + 'ra' => 'audio/x-realaudio', + 'wav' => 'audio/x-wav', + 'bmp' => 'image/bmp', + 'gif' => 'image/gif', + 'jpeg' => 'image/jpeg', + 'jpe' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'png' => 'image/png', + 'tiff' => 'image/tiff', + 'tif' => 'image/tiff', + 'eml' => 'message/rfc822', + 'css' => 'text/css', + 'html' => 'text/html', + 'htm' => 'text/html', + 'shtml' => 'text/html', + 'log' => 'text/plain', + 'text' => 'text/plain', + 'txt' => 'text/plain', + 'rtx' => 'text/richtext', + 'rtf' => 'text/rtf', + 'xml' => 'text/xml', + 'xsl' => 'text/xml', + 'mpeg' => 'video/mpeg', + 'mpe' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mov' => 'video/quicktime', + 'qt' => 'video/quicktime', + 'rv' => 'video/vnd.rn-realvideo', + 'avi' => 'video/x-msvideo', + 'movie' => 'video/x-sgi-movie' + ); + return (array_key_exists(strtolower($ext), $mimes) ? $mimes[strtolower($ext)]: 'application/octet-stream'); + } + + /** + * Map a file name to a MIME type. + * Defaults to 'application/octet-stream', i.e.. arbitrary binary data. + * @param string $filename A file name or full path, does not need to exist as a file + * @return string + * @static + */ + public static function filenameToType($filename) + { + //In case the path is a URL, strip any query string before getting extension + $qpos = strpos($filename, '?'); + if ($qpos !== false) { + $filename = substr($filename, 0, $qpos); + } + $pathinfo = self::mb_pathinfo($filename); + return self::_mime_types($pathinfo['extension']); + } + + /** + * Multi-byte-safe pathinfo replacement. + * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe. + * Works similarly to the one in PHP >= 5.2.0 + * @link http://www.php.net/manual/en/function.pathinfo.php#107461 + * @param string $path A filename or path, does not need to exist as a file + * @param integer|string $options Either a PATHINFO_* constant, + * or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2 + * @return string|array + * @static + */ + public static function mb_pathinfo($path, $options = null) + { + $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => ''); + $m = array(); + preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $m); + if (array_key_exists(1, $m)) { + $ret['dirname'] = $m[1]; + } + if (array_key_exists(2, $m)) { + $ret['basename'] = $m[2]; + } + if (array_key_exists(5, $m)) { + $ret['extension'] = $m[5]; + } + if (array_key_exists(3, $m)) { + $ret['filename'] = $m[3]; + } + switch ($options) { + case PATHINFO_DIRNAME: + case 'dirname': + return $ret['dirname']; + break; + case PATHINFO_BASENAME: + case 'basename': + return $ret['basename']; + break; + case PATHINFO_EXTENSION: + case 'extension': + return $ret['extension']; + break; + case PATHINFO_FILENAME: + case 'filename': + return $ret['filename']; + break; + default: + return $ret; + } + } + + /** + * Set or reset instance properties. + * + * Usage Example: + * $page->set('X-Priority', '3'); + * + * @access public + * @param string $name + * @param mixed $value + * NOTE: will not work with arrays, there are no arrays to set/reset + * @throws phpmailerException + * @return bool + * @todo Should this not be using __set() magic function? + */ + public function set($name, $value = '') + { + try { + if (isset($this->$name)) { + $this->$name = $value; + } else { + throw new phpmailerException($this->lang('variable_set') . $name, self::STOP_CRITICAL); + } + } catch (Exception $e) { + $this->setError($e->getMessage()); + if ($e->getCode() == self::STOP_CRITICAL) { + return false; + } + } + return true; + } + + /** + * Strip newlines to prevent header injection. + * @access public + * @param string $str + * @return string + */ + public function secureHeader($str) + { + return trim(str_replace(array("\r", "\n"), '', $str)); + } + + /** + * Normalize line breaks in a string. + * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format. + * Defaults to CRLF (for message bodies) and preserves consecutive breaks. + * @param string $text + * @param string $breaktype What kind of line break to use, defaults to CRLF + * @return string + * @access public + * @static + */ + public static function normalizeBreaks($text, $breaktype = "\r\n") + { + return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text); + } + + + /** + * Set the private key file and password for S/MIME signing. + * @access public + * @param string $cert_filename + * @param string $key_filename + * @param string $key_pass Password for private key + */ + public function sign($cert_filename, $key_filename, $key_pass) + { + $this->sign_cert_file = $cert_filename; + $this->sign_key_file = $key_filename; + $this->sign_key_pass = $key_pass; + } + + /** + * Quoted-Printable-encode a DKIM header. + * @access public + * @param string $txt + * @return string + */ + public function DKIM_QP($txt) + { + $line = ''; + for ($i = 0; $i < strlen($txt); $i++) { + $ord = ord($txt[$i]); + if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) { + $line .= $txt[$i]; + } else { + $line .= "=" . sprintf("%02X", $ord); + } + } + return $line; + } + + /** + * Generate a DKIM signature. + * @access public + * @param string $s Header + * @throws phpmailerException + * @return string + */ + public function DKIM_Sign($s) + { + if (!defined('PKCS7_TEXT')) { + if ($this->exceptions) { + throw new phpmailerException($this->lang("signing") . ' OpenSSL extension missing.'); + } + return ''; + } + $privKeyStr = file_get_contents($this->DKIM_private); + if ($this->DKIM_passphrase != '') { + $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase); + } else { + $privKey = $privKeyStr; + } + if (openssl_sign($s, $signature, $privKey)) { + return base64_encode($signature); + } + return ''; + } + + /** + * Generate a DKIM canonicalization header. + * @access public + * @param string $s Header + * @return string + */ + public function DKIM_HeaderC($s) + { + $s = preg_replace("/\r\n\s+/", " ", $s); + $lines = explode("\r\n", $s); + foreach ($lines as $key => $line) { + list($heading, $value) = explode(":", $line, 2); + $heading = strtolower($heading); + $value = preg_replace("/\s+/", " ", $value); // Compress useless spaces + $lines[$key] = $heading . ":" . trim($value); // Don't forget to remove WSP around the value + } + $s = implode("\r\n", $lines); + return $s; + } + + /** + * Generate a DKIM canonicalization body. + * @access public + * @param string $body Message Body + * @return string + */ + public function DKIM_BodyC($body) + { + if ($body == '') { + return "\r\n"; + } + // stabilize line endings + $body = str_replace("\r\n", "\n", $body); + $body = str_replace("\n", "\r\n", $body); + // END stabilize line endings + while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") { + $body = substr($body, 0, strlen($body) - 2); + } + return $body; + } + + /** + * Create the DKIM header and body in a new message header. + * @access public + * @param string $headers_line Header lines + * @param string $subject Subject + * @param string $body Body + * @return string + */ + public function DKIM_Add($headers_line, $subject, $body) + { + $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms + $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body + $DKIMquery = 'dns/txt'; // Query method + $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone) + $subject_header = "Subject: $subject"; + $headers = explode($this->LE, $headers_line); + $from_header = ''; + $to_header = ''; + $current = ''; + foreach ($headers as $header) { + if (strpos($header, 'From:') === 0) { + $from_header = $header; + $current = 'from_header'; + } elseif (strpos($header, 'To:') === 0) { + $to_header = $header; + $current = 'to_header'; + } else { + if ($current && strpos($header, ' =?') === 0) { + $current .= $header; + } else { + $current = ''; + } + } + } + $from = str_replace('|', '=7C', $this->DKIM_QP($from_header)); + $to = str_replace('|', '=7C', $this->DKIM_QP($to_header)); + $subject = str_replace( + '|', + '=7C', + $this->DKIM_QP($subject_header) + ); // Copied header fields (dkim-quoted-printable) + $body = $this->DKIM_BodyC($body); + $DKIMlen = strlen($body); // Length of body + $DKIMb64 = base64_encode(pack("H*", sha1($body))); // Base64 of packed binary SHA-1 hash of body + $ident = ($this->DKIM_identity == '') ? '' : " i=" . $this->DKIM_identity . ";"; + $dkimhdrs = "DKIM-Signature: v=1; a=" . + $DKIMsignatureType . "; q=" . + $DKIMquery . "; l=" . + $DKIMlen . "; s=" . + $this->DKIM_selector . + ";\r\n" . + "\tt=" . $DKIMtime . "; c=" . $DKIMcanonicalization . ";\r\n" . + "\th=From:To:Subject;\r\n" . + "\td=" . $this->DKIM_domain . ";" . $ident . "\r\n" . + "\tz=$from\r\n" . + "\t|$to\r\n" . + "\t|$subject;\r\n" . + "\tbh=" . $DKIMb64 . ";\r\n" . + "\tb="; + $toSign = $this->DKIM_HeaderC( + $from_header . "\r\n" . $to_header . "\r\n" . $subject_header . "\r\n" . $dkimhdrs + ); + $signed = $this->DKIM_Sign($toSign); + return $dkimhdrs . $signed . "\r\n"; + } + + /** + * Perform a callback. + * @param bool $isSent + * @param string $to + * @param string $cc + * @param string $bcc + * @param string $subject + * @param string $body + * @param string $from + */ + protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from = null) + { + if (!empty($this->action_function) && is_callable($this->action_function)) { + $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from); + call_user_func_array($this->action_function, $params); + } + } +} + +/** + * PHPMailer exception handler + * @package PHPMailer + */ +class phpmailerException extends Exception +{ + /** + * Prettify error message output + * @return string + */ + public function errorMessage() + { + $errorMsg = '<strong>' . $this->getMessage() . "</strong><br />\n"; + return $errorMsg; + } +} diff --git a/wp-includes/class-pop3.php b/wp-includes/class-pop3.php new file mode 100644 index 0000000..d0455d7 --- /dev/null +++ b/wp-includes/class-pop3.php @@ -0,0 +1,652 @@ +<?php +/** + * mail_fetch/setup.php + * + * Copyright (c) 1999-2011 CDI (cdi@thewebmasters.net) All Rights Reserved + * Modified by Philippe Mingo 2001-2009 mingo@rotedic.com + * An RFC 1939 compliant wrapper class for the POP3 protocol. + * + * Licensed under the GNU GPL. For full terms see the file COPYING. + * + * POP3 class + * + * @copyright 1999-2011 The SquirrelMail Project Team + * @license http://opensource.org/licenses/gpl-license.php GNU Public License + * @package plugins + * @subpackage mail_fetch + */ + +class POP3 { + var $ERROR = ''; // Error string. + + var $TIMEOUT = 60; // Default timeout before giving up on a + // network operation. + + var $COUNT = -1; // Mailbox msg count + + var $BUFFER = 512; // Socket buffer for socket fgets() calls. + // Per RFC 1939 the returned line a POP3 + // server can send is 512 bytes. + + var $FP = ''; // The connection to the server's + // file descriptor + + var $MAILSERVER = ''; // Set this to hard code the server name + + var $DEBUG = FALSE; // set to true to echo pop3 + // commands and responses to error_log + // this WILL log passwords! + + var $BANNER = ''; // Holds the banner returned by the + // pop server - used for apop() + + var $ALLOWAPOP = FALSE; // Allow or disallow apop() + // This must be set to true + // manually + + function POP3 ( $server = '', $timeout = '' ) { + settype($this->BUFFER,"integer"); + if( !empty($server) ) { + // Do not allow programs to alter MAILSERVER + // if it is already specified. They can get around + // this if they -really- want to, so don't count on it. + if(empty($this->MAILSERVER)) + $this->MAILSERVER = $server; + } + if(!empty($timeout)) { + settype($timeout,"integer"); + $this->TIMEOUT = $timeout; + if (!ini_get('safe_mode')) + set_time_limit($timeout); + } + return true; + } + + function update_timer () { + if (!ini_get('safe_mode')) + set_time_limit($this->TIMEOUT); + return true; + } + + function connect ($server, $port = 110) { + // Opens a socket to the specified server. Unless overridden, + // port defaults to 110. Returns true on success, false on fail + + // If MAILSERVER is set, override $server with it's value + + if (!isset($port) || !$port) {$port = 110;} + if(!empty($this->MAILSERVER)) + $server = $this->MAILSERVER; + + if(empty($server)){ + $this->ERROR = "POP3 connect: " . _("No server specified"); + unset($this->FP); + return false; + } + + $fp = @fsockopen("$server", $port, $errno, $errstr); + + if(!$fp) { + $this->ERROR = "POP3 connect: " . _("Error ") . "[$errno] [$errstr]"; + unset($this->FP); + return false; + } + + socket_set_blocking($fp,-1); + $this->update_timer(); + $reply = fgets($fp,$this->BUFFER); + $reply = $this->strip_clf($reply); + if($this->DEBUG) + error_log("POP3 SEND [connect: $server] GOT [$reply]",0); + if(!$this->is_ok($reply)) { + $this->ERROR = "POP3 connect: " . _("Error ") . "[$reply]"; + unset($this->FP); + return false; + } + $this->FP = $fp; + $this->BANNER = $this->parse_banner($reply); + return true; + } + + function user ($user = "") { + // Sends the USER command, returns true or false + + if( empty($user) ) { + $this->ERROR = "POP3 user: " . _("no login ID submitted"); + return false; + } elseif(!isset($this->FP)) { + $this->ERROR = "POP3 user: " . _("connection not established"); + return false; + } else { + $reply = $this->send_cmd("USER $user"); + if(!$this->is_ok($reply)) { + $this->ERROR = "POP3 user: " . _("Error ") . "[$reply]"; + return false; + } else + return true; + } + } + + function pass ($pass = "") { + // Sends the PASS command, returns # of msgs in mailbox, + // returns false (undef) on Auth failure + + if(empty($pass)) { + $this->ERROR = "POP3 pass: " . _("No password submitted"); + return false; + } elseif(!isset($this->FP)) { + $this->ERROR = "POP3 pass: " . _("connection not established"); + return false; + } else { + $reply = $this->send_cmd("PASS $pass"); + if(!$this->is_ok($reply)) { + $this->ERROR = "POP3 pass: " . _("Authentication failed") . " [$reply]"; + $this->quit(); + return false; + } else { + // Auth successful. + $count = $this->last("count"); + $this->COUNT = $count; + return $count; + } + } + } + + function apop ($login,$pass) { + // Attempts an APOP login. If this fails, it'll + // try a standard login. YOUR SERVER MUST SUPPORT + // THE USE OF THE APOP COMMAND! + // (apop is optional per rfc1939) + + if(!isset($this->FP)) { + $this->ERROR = "POP3 apop: " . _("No connection to server"); + return false; + } elseif(!$this->ALLOWAPOP) { + $retVal = $this->login($login,$pass); + return $retVal; + } elseif(empty($login)) { + $this->ERROR = "POP3 apop: " . _("No login ID submitted"); + return false; + } elseif(empty($pass)) { + $this->ERROR = "POP3 apop: " . _("No password submitted"); + return false; + } else { + $banner = $this->BANNER; + if( (!$banner) or (empty($banner)) ) { + $this->ERROR = "POP3 apop: " . _("No server banner") . ' - ' . _("abort"); + $retVal = $this->login($login,$pass); + return $retVal; + } else { + $AuthString = $banner; + $AuthString .= $pass; + $APOPString = md5($AuthString); + $cmd = "APOP $login $APOPString"; + $reply = $this->send_cmd($cmd); + if(!$this->is_ok($reply)) { + $this->ERROR = "POP3 apop: " . _("apop authentication failed") . ' - ' . _("abort"); + $retVal = $this->login($login,$pass); + return $retVal; + } else { + // Auth successful. + $count = $this->last("count"); + $this->COUNT = $count; + return $count; + } + } + } + } + + function login ($login = "", $pass = "") { + // Sends both user and pass. Returns # of msgs in mailbox or + // false on failure (or -1, if the error occurs while getting + // the number of messages.) + + if( !isset($this->FP) ) { + $this->ERROR = "POP3 login: " . _("No connection to server"); + return false; + } else { + $fp = $this->FP; + if( !$this->user( $login ) ) { + // Preserve the error generated by user() + return false; + } else { + $count = $this->pass($pass); + if( (!$count) || ($count == -1) ) { + // Preserve the error generated by last() and pass() + return false; + } else + return $count; + } + } + } + + function top ($msgNum, $numLines = "0") { + // Gets the header and first $numLines of the msg body + // returns data in an array with each returned line being + // an array element. If $numLines is empty, returns + // only the header information, and none of the body. + + if(!isset($this->FP)) { + $this->ERROR = "POP3 top: " . _("No connection to server"); + return false; + } + $this->update_timer(); + + $fp = $this->FP; + $buffer = $this->BUFFER; + $cmd = "TOP $msgNum $numLines"; + fwrite($fp, "TOP $msgNum $numLines\r\n"); + $reply = fgets($fp, $buffer); + $reply = $this->strip_clf($reply); + if($this->DEBUG) { + @error_log("POP3 SEND [$cmd] GOT [$reply]",0); + } + if(!$this->is_ok($reply)) + { + $this->ERROR = "POP3 top: " . _("Error ") . "[$reply]"; + return false; + } + + $count = 0; + $MsgArray = array(); + + $line = fgets($fp,$buffer); + while ( !preg_match('/^\.\r\n/',$line)) + { + $MsgArray[$count] = $line; + $count++; + $line = fgets($fp,$buffer); + if(empty($line)) { break; } + } + + return $MsgArray; + } + + function pop_list ($msgNum = "") { + // If called with an argument, returns that msgs' size in octets + // No argument returns an associative array of undeleted + // msg numbers and their sizes in octets + + if(!isset($this->FP)) + { + $this->ERROR = "POP3 pop_list: " . _("No connection to server"); + return false; + } + $fp = $this->FP; + $Total = $this->COUNT; + if( (!$Total) or ($Total == -1) ) + { + return false; + } + if($Total == 0) + { + return array("0","0"); + // return -1; // mailbox empty + } + + $this->update_timer(); + + if(!empty($msgNum)) + { + $cmd = "LIST $msgNum"; + fwrite($fp,"$cmd\r\n"); + $reply = fgets($fp,$this->BUFFER); + $reply = $this->strip_clf($reply); + if($this->DEBUG) { + @error_log("POP3 SEND [$cmd] GOT [$reply]",0); + } + if(!$this->is_ok($reply)) + { + $this->ERROR = "POP3 pop_list: " . _("Error ") . "[$reply]"; + return false; + } + list($junk,$num,$size) = preg_split('/\s+/',$reply); + return $size; + } + $cmd = "LIST"; + $reply = $this->send_cmd($cmd); + if(!$this->is_ok($reply)) + { + $reply = $this->strip_clf($reply); + $this->ERROR = "POP3 pop_list: " . _("Error ") . "[$reply]"; + return false; + } + $MsgArray = array(); + $MsgArray[0] = $Total; + for($msgC=1;$msgC <= $Total; $msgC++) + { + if($msgC > $Total) { break; } + $line = fgets($fp,$this->BUFFER); + $line = $this->strip_clf($line); + if(strpos($line, '.') === 0) + { + $this->ERROR = "POP3 pop_list: " . _("Premature end of list"); + return false; + } + list($thisMsg,$msgSize) = preg_split('/\s+/',$line); + settype($thisMsg,"integer"); + if($thisMsg != $msgC) + { + $MsgArray[$msgC] = "deleted"; + } + else + { + $MsgArray[$msgC] = $msgSize; + } + } + return $MsgArray; + } + + function get ($msgNum) { + // Retrieve the specified msg number. Returns an array + // where each line of the msg is an array element. + + if(!isset($this->FP)) + { + $this->ERROR = "POP3 get: " . _("No connection to server"); + return false; + } + + $this->update_timer(); + + $fp = $this->FP; + $buffer = $this->BUFFER; + $cmd = "RETR $msgNum"; + $reply = $this->send_cmd($cmd); + + if(!$this->is_ok($reply)) + { + $this->ERROR = "POP3 get: " . _("Error ") . "[$reply]"; + return false; + } + + $count = 0; + $MsgArray = array(); + + $line = fgets($fp,$buffer); + while ( !preg_match('/^\.\r\n/',$line)) + { + if ( $line{0} == '.' ) { $line = substr($line,1); } + $MsgArray[$count] = $line; + $count++; + $line = fgets($fp,$buffer); + if(empty($line)) { break; } + } + return $MsgArray; + } + + function last ( $type = "count" ) { + // Returns the highest msg number in the mailbox. + // returns -1 on error, 0+ on success, if type != count + // results in a popstat() call (2 element array returned) + + $last = -1; + if(!isset($this->FP)) + { + $this->ERROR = "POP3 last: " . _("No connection to server"); + return $last; + } + + $reply = $this->send_cmd("STAT"); + if(!$this->is_ok($reply)) + { + $this->ERROR = "POP3 last: " . _("Error ") . "[$reply]"; + return $last; + } + + $Vars = preg_split('/\s+/',$reply); + $count = $Vars[1]; + $size = $Vars[2]; + settype($count,"integer"); + settype($size,"integer"); + if($type != "count") + { + return array($count,$size); + } + return $count; + } + + function reset () { + // Resets the status of the remote server. This includes + // resetting the status of ALL msgs to not be deleted. + // This method automatically closes the connection to the server. + + if(!isset($this->FP)) + { + $this->ERROR = "POP3 reset: " . _("No connection to server"); + return false; + } + $reply = $this->send_cmd("RSET"); + if(!$this->is_ok($reply)) + { + // The POP3 RSET command -never- gives a -ERR + // response - if it ever does, something truely + // wild is going on. + + $this->ERROR = "POP3 reset: " . _("Error ") . "[$reply]"; + @error_log("POP3 reset: ERROR [$reply]",0); + } + $this->quit(); + return true; + } + + function send_cmd ( $cmd = "" ) + { + // Sends a user defined command string to the + // POP server and returns the results. Useful for + // non-compliant or custom POP servers. + // Do NOT includ the \r\n as part of your command + // string - it will be appended automatically. + + // The return value is a standard fgets() call, which + // will read up to $this->BUFFER bytes of data, until it + // encounters a new line, or EOF, whichever happens first. + + // This method works best if $cmd responds with only + // one line of data. + + if(!isset($this->FP)) + { + $this->ERROR = "POP3 send_cmd: " . _("No connection to server"); + return false; + } + + if(empty($cmd)) + { + $this->ERROR = "POP3 send_cmd: " . _("Empty command string"); + return ""; + } + + $fp = $this->FP; + $buffer = $this->BUFFER; + $this->update_timer(); + fwrite($fp,"$cmd\r\n"); + $reply = fgets($fp,$buffer); + $reply = $this->strip_clf($reply); + if($this->DEBUG) { @error_log("POP3 SEND [$cmd] GOT [$reply]",0); } + return $reply; + } + + function quit() { + // Closes the connection to the POP3 server, deleting + // any msgs marked as deleted. + + if(!isset($this->FP)) + { + $this->ERROR = "POP3 quit: " . _("connection does not exist"); + return false; + } + $fp = $this->FP; + $cmd = "QUIT"; + fwrite($fp,"$cmd\r\n"); + $reply = fgets($fp,$this->BUFFER); + $reply = $this->strip_clf($reply); + if($this->DEBUG) { @error_log("POP3 SEND [$cmd] GOT [$reply]",0); } + fclose($fp); + unset($this->FP); + return true; + } + + function popstat () { + // Returns an array of 2 elements. The number of undeleted + // msgs in the mailbox, and the size of the mbox in octets. + + $PopArray = $this->last("array"); + + if($PopArray == -1) { return false; } + + if( (!$PopArray) or (empty($PopArray)) ) + { + return false; + } + return $PopArray; + } + + function uidl ($msgNum = "") + { + // Returns the UIDL of the msg specified. If called with + // no arguments, returns an associative array where each + // undeleted msg num is a key, and the msg's uidl is the element + // Array element 0 will contain the total number of msgs + + if(!isset($this->FP)) { + $this->ERROR = "POP3 uidl: " . _("No connection to server"); + return false; + } + + $fp = $this->FP; + $buffer = $this->BUFFER; + + if(!empty($msgNum)) { + $cmd = "UIDL $msgNum"; + $reply = $this->send_cmd($cmd); + if(!$this->is_ok($reply)) + { + $this->ERROR = "POP3 uidl: " . _("Error ") . "[$reply]"; + return false; + } + list ($ok,$num,$myUidl) = preg_split('/\s+/',$reply); + return $myUidl; + } else { + $this->update_timer(); + + $UIDLArray = array(); + $Total = $this->COUNT; + $UIDLArray[0] = $Total; + + if ($Total < 1) + { + return $UIDLArray; + } + $cmd = "UIDL"; + fwrite($fp, "UIDL\r\n"); + $reply = fgets($fp, $buffer); + $reply = $this->strip_clf($reply); + if($this->DEBUG) { @error_log("POP3 SEND [$cmd] GOT [$reply]",0); } + if(!$this->is_ok($reply)) + { + $this->ERROR = "POP3 uidl: " . _("Error ") . "[$reply]"; + return false; + } + + $line = ""; + $count = 1; + $line = fgets($fp,$buffer); + while ( !preg_match('/^\.\r\n/',$line)) { + list ($msg,$msgUidl) = preg_split('/\s+/',$line); + $msgUidl = $this->strip_clf($msgUidl); + if($count == $msg) { + $UIDLArray[$msg] = $msgUidl; + } + else + { + $UIDLArray[$count] = 'deleted'; + } + $count++; + $line = fgets($fp,$buffer); + } + } + return $UIDLArray; + } + + function delete ($msgNum = "") { + // Flags a specified msg as deleted. The msg will not + // be deleted until a quit() method is called. + + if(!isset($this->FP)) + { + $this->ERROR = "POP3 delete: " . _("No connection to server"); + return false; + } + if(empty($msgNum)) + { + $this->ERROR = "POP3 delete: " . _("No msg number submitted"); + return false; + } + $reply = $this->send_cmd("DELE $msgNum"); + if(!$this->is_ok($reply)) + { + $this->ERROR = "POP3 delete: " . _("Command failed ") . "[$reply]"; + return false; + } + return true; + } + + // ********************************************************* + + // The following methods are internal to the class. + + function is_ok ($cmd = "") { + // Return true or false on +OK or -ERR + + if( empty($cmd) ) + return false; + else + return( stripos($cmd, '+OK') !== false ); + } + + function strip_clf ($text = "") { + // Strips \r\n from server responses + + if(empty($text)) + return $text; + else { + $stripped = str_replace(array("\r","\n"),'',$text); + return $stripped; + } + } + + function parse_banner ( $server_text ) { + $outside = true; + $banner = ""; + $length = strlen($server_text); + for($count =0; $count < $length; $count++) + { + $digit = substr($server_text,$count,1); + if(!empty($digit)) { + if( (!$outside) && ($digit != '<') && ($digit != '>') ) + { + $banner .= $digit; + } + if ($digit == '<') + { + $outside = false; + } + if($digit == '>') + { + $outside = true; + } + } + } + $banner = $this->strip_clf($banner); // Just in case + return "<$banner>"; + } + +} // End class + +// For php4 compatibility +if (!function_exists("stripos")) { + function stripos($haystack, $needle){ + return strpos($haystack, stristr( $haystack, $needle )); + } +} diff --git a/wp-includes/class-simplepie.php b/wp-includes/class-simplepie.php new file mode 100644 index 0000000..64f9fa9 --- /dev/null +++ b/wp-includes/class-simplepie.php @@ -0,0 +1,3119 @@ +<?php +if ( ! class_exists( 'SimplePie' ) ) : + +// Load classes we will need. +require ABSPATH . WPINC . '/SimplePie/Misc.php'; +require ABSPATH . WPINC . '/SimplePie/Cache.php'; +require ABSPATH . WPINC . '/SimplePie/File.php'; +require ABSPATH . WPINC . '/SimplePie/Sanitize.php'; +require ABSPATH . WPINC . '/SimplePie/Registry.php'; +require ABSPATH . WPINC . '/SimplePie/IRI.php'; +require ABSPATH . WPINC . '/SimplePie/Locator.php'; +require ABSPATH . WPINC . '/SimplePie/Content/Type/Sniffer.php'; +require ABSPATH . WPINC . '/SimplePie/XML/Declaration/Parser.php'; +require ABSPATH . WPINC . '/SimplePie/Parser.php'; +require ABSPATH . WPINC . '/SimplePie/Item.php'; +require ABSPATH . WPINC . '/SimplePie/Parse/Date.php'; +require ABSPATH . WPINC . '/SimplePie/Author.php'; + +/** + * WordPress autoloader for SimplePie. + * + * @since 3.5.0 + */ +function wp_simplepie_autoload( $class ) { + if ( 0 !== strpos( $class, 'SimplePie_' ) ) + return; + + $file = ABSPATH . WPINC . '/' . str_replace( '_', '/', $class ) . '.php'; + include( $file ); +} + +if ( function_exists( 'spl_autoload_register' ) ) { + /** + * We autoload classes we may not need. + * + * If SPL is disabled, we load all of SimplePie manually. + * + * Core.php is not loaded manually, because SimplePie_Core (a deprecated class) + * was never included in WordPress core. + */ + spl_autoload_register( 'wp_simplepie_autoload' ); +} else { + require ABSPATH . WPINC . '/SimplePie/Cache/Base.php'; + require ABSPATH . WPINC . '/SimplePie/Cache/DB.php'; + require ABSPATH . WPINC . '/SimplePie/Cache/File.php'; + require ABSPATH . WPINC . '/SimplePie/Cache/Memcache.php'; + require ABSPATH . WPINC . '/SimplePie/Cache/MySQL.php'; + require ABSPATH . WPINC . '/SimplePie/Caption.php'; + require ABSPATH . WPINC . '/SimplePie/Category.php'; + require ABSPATH . WPINC . '/SimplePie/Copyright.php'; + require ABSPATH . WPINC . '/SimplePie/Credit.php'; + require ABSPATH . WPINC . '/SimplePie/Decode/HTML/Entities.php'; + require ABSPATH . WPINC . '/SimplePie/Enclosure.php'; + require ABSPATH . WPINC . '/SimplePie/gzdecode.php'; + require ABSPATH . WPINC . '/SimplePie/HTTP/Parser.php'; + require ABSPATH . WPINC . '/SimplePie/Net/IPv6.php'; + require ABSPATH . WPINC . '/SimplePie/Rating.php'; + require ABSPATH . WPINC . '/SimplePie/Restriction.php'; + require ABSPATH . WPINC . '/SimplePie/Source.php'; +} + +/** + * SimplePie + * + * A PHP-Based RSS and Atom Feed Framework. + * Takes the hard work out of managing a complete RSS/Atom solution. + * + * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * * Neither the name of the SimplePie Team nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS + * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package SimplePie + * @version 1.3.1 + * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @author Ryan Parman + * @author Geoffrey Sneddon + * @author Ryan McCue + * @link http://simplepie.org/ SimplePie + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + */ + +/** + * SimplePie Name + */ +define('SIMPLEPIE_NAME', 'SimplePie'); + +/** + * SimplePie Version + */ +define('SIMPLEPIE_VERSION', '1.3.1'); + +/** + * SimplePie Build + * @todo Hardcode for release (there's no need to have to call SimplePie_Misc::get_build() only every load of simplepie.inc) + */ +define('SIMPLEPIE_BUILD', gmdate('YmdHis', SimplePie_Misc::get_build())); + +/** + * SimplePie Website URL + */ +define('SIMPLEPIE_URL', 'http://simplepie.org'); + +/** + * SimplePie Useragent + * @see SimplePie::set_useragent() + */ +define('SIMPLEPIE_USERAGENT', SIMPLEPIE_NAME . '/' . SIMPLEPIE_VERSION . ' (Feed Parser; ' . SIMPLEPIE_URL . '; Allow like Gecko) Build/' . SIMPLEPIE_BUILD); + +/** + * SimplePie Linkback + */ +define('SIMPLEPIE_LINKBACK', '<a href="' . SIMPLEPIE_URL . '" title="' . SIMPLEPIE_NAME . ' ' . SIMPLEPIE_VERSION . '">' . SIMPLEPIE_NAME . '</a>'); + +/** + * No Autodiscovery + * @see SimplePie::set_autodiscovery_level() + */ +define('SIMPLEPIE_LOCATOR_NONE', 0); + +/** + * Feed Link Element Autodiscovery + * @see SimplePie::set_autodiscovery_level() + */ +define('SIMPLEPIE_LOCATOR_AUTODISCOVERY', 1); + +/** + * Local Feed Extension Autodiscovery + * @see SimplePie::set_autodiscovery_level() + */ +define('SIMPLEPIE_LOCATOR_LOCAL_EXTENSION', 2); + +/** + * Local Feed Body Autodiscovery + * @see SimplePie::set_autodiscovery_level() + */ +define('SIMPLEPIE_LOCATOR_LOCAL_BODY', 4); + +/** + * Remote Feed Extension Autodiscovery + * @see SimplePie::set_autodiscovery_level() + */ +define('SIMPLEPIE_LOCATOR_REMOTE_EXTENSION', 8); + +/** + * Remote Feed Body Autodiscovery + * @see SimplePie::set_autodiscovery_level() + */ +define('SIMPLEPIE_LOCATOR_REMOTE_BODY', 16); + +/** + * All Feed Autodiscovery + * @see SimplePie::set_autodiscovery_level() + */ +define('SIMPLEPIE_LOCATOR_ALL', 31); + +/** + * No known feed type + */ +define('SIMPLEPIE_TYPE_NONE', 0); + +/** + * RSS 0.90 + */ +define('SIMPLEPIE_TYPE_RSS_090', 1); + +/** + * RSS 0.91 (Netscape) + */ +define('SIMPLEPIE_TYPE_RSS_091_NETSCAPE', 2); + +/** + * RSS 0.91 (Userland) + */ +define('SIMPLEPIE_TYPE_RSS_091_USERLAND', 4); + +/** + * RSS 0.91 (both Netscape and Userland) + */ +define('SIMPLEPIE_TYPE_RSS_091', 6); + +/** + * RSS 0.92 + */ +define('SIMPLEPIE_TYPE_RSS_092', 8); + +/** + * RSS 0.93 + */ +define('SIMPLEPIE_TYPE_RSS_093', 16); + +/** + * RSS 0.94 + */ +define('SIMPLEPIE_TYPE_RSS_094', 32); + +/** + * RSS 1.0 + */ +define('SIMPLEPIE_TYPE_RSS_10', 64); + +/** + * RSS 2.0 + */ +define('SIMPLEPIE_TYPE_RSS_20', 128); + +/** + * RDF-based RSS + */ +define('SIMPLEPIE_TYPE_RSS_RDF', 65); + +/** + * Non-RDF-based RSS (truly intended as syndication format) + */ +define('SIMPLEPIE_TYPE_RSS_SYNDICATION', 190); + +/** + * All RSS + */ +define('SIMPLEPIE_TYPE_RSS_ALL', 255); + +/** + * Atom 0.3 + */ +define('SIMPLEPIE_TYPE_ATOM_03', 256); + +/** + * Atom 1.0 + */ +define('SIMPLEPIE_TYPE_ATOM_10', 512); + +/** + * All Atom + */ +define('SIMPLEPIE_TYPE_ATOM_ALL', 768); + +/** + * All feed types + */ +define('SIMPLEPIE_TYPE_ALL', 1023); + +/** + * No construct + */ +define('SIMPLEPIE_CONSTRUCT_NONE', 0); + +/** + * Text construct + */ +define('SIMPLEPIE_CONSTRUCT_TEXT', 1); + +/** + * HTML construct + */ +define('SIMPLEPIE_CONSTRUCT_HTML', 2); + +/** + * XHTML construct + */ +define('SIMPLEPIE_CONSTRUCT_XHTML', 4); + +/** + * base64-encoded construct + */ +define('SIMPLEPIE_CONSTRUCT_BASE64', 8); + +/** + * IRI construct + */ +define('SIMPLEPIE_CONSTRUCT_IRI', 16); + +/** + * A construct that might be HTML + */ +define('SIMPLEPIE_CONSTRUCT_MAYBE_HTML', 32); + +/** + * All constructs + */ +define('SIMPLEPIE_CONSTRUCT_ALL', 63); + +/** + * Don't change case + */ +define('SIMPLEPIE_SAME_CASE', 1); + +/** + * Change to lowercase + */ +define('SIMPLEPIE_LOWERCASE', 2); + +/** + * Change to uppercase + */ +define('SIMPLEPIE_UPPERCASE', 4); + +/** + * PCRE for HTML attributes + */ +define('SIMPLEPIE_PCRE_HTML_ATTRIBUTE', '((?:[\x09\x0A\x0B\x0C\x0D\x20]+[^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3D\x3E]*(?:[\x09\x0A\x0B\x0C\x0D\x20]*=[\x09\x0A\x0B\x0C\x0D\x20]*(?:"(?:[^"]*)"|\'(?:[^\']*)\'|(?:[^\x09\x0A\x0B\x0C\x0D\x20\x22\x27\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x3E]*)?))?)*)[\x09\x0A\x0B\x0C\x0D\x20]*'); + +/** + * PCRE for XML attributes + */ +define('SIMPLEPIE_PCRE_XML_ATTRIBUTE', '((?:\s+(?:(?:[^\s:]+:)?[^\s:]+)\s*=\s*(?:"(?:[^"]*)"|\'(?:[^\']*)\'))*)\s*'); + +/** + * XML Namespace + */ +define('SIMPLEPIE_NAMESPACE_XML', 'http://www.w3.org/XML/1998/namespace'); + +/** + * Atom 1.0 Namespace + */ +define('SIMPLEPIE_NAMESPACE_ATOM_10', 'http://www.w3.org/2005/Atom'); + +/** + * Atom 0.3 Namespace + */ +define('SIMPLEPIE_NAMESPACE_ATOM_03', 'http://purl.org/atom/ns#'); + +/** + * RDF Namespace + */ +define('SIMPLEPIE_NAMESPACE_RDF', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'); + +/** + * RSS 0.90 Namespace + */ +define('SIMPLEPIE_NAMESPACE_RSS_090', 'http://my.netscape.com/rdf/simple/0.9/'); + +/** + * RSS 1.0 Namespace + */ +define('SIMPLEPIE_NAMESPACE_RSS_10', 'http://purl.org/rss/1.0/'); + +/** + * RSS 1.0 Content Module Namespace + */ +define('SIMPLEPIE_NAMESPACE_RSS_10_MODULES_CONTENT', 'http://purl.org/rss/1.0/modules/content/'); + +/** + * RSS 2.0 Namespace + * (Stupid, I know, but I'm certain it will confuse people less with support.) + */ +define('SIMPLEPIE_NAMESPACE_RSS_20', ''); + +/** + * DC 1.0 Namespace + */ +define('SIMPLEPIE_NAMESPACE_DC_10', 'http://purl.org/dc/elements/1.0/'); + +/** + * DC 1.1 Namespace + */ +define('SIMPLEPIE_NAMESPACE_DC_11', 'http://purl.org/dc/elements/1.1/'); + +/** + * W3C Basic Geo (WGS84 lat/long) Vocabulary Namespace + */ +define('SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO', 'http://www.w3.org/2003/01/geo/wgs84_pos#'); + +/** + * GeoRSS Namespace + */ +define('SIMPLEPIE_NAMESPACE_GEORSS', 'http://www.georss.org/georss'); + +/** + * Media RSS Namespace + */ +define('SIMPLEPIE_NAMESPACE_MEDIARSS', 'http://search.yahoo.com/mrss/'); + +/** + * Wrong Media RSS Namespace. Caused by a long-standing typo in the spec. + */ +define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG', 'http://search.yahoo.com/mrss'); + +/** + * Wrong Media RSS Namespace #2. New namespace introduced in Media RSS 1.5. + */ +define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG2', 'http://video.search.yahoo.com/mrss'); + +/** + * Wrong Media RSS Namespace #3. A possible typo of the Media RSS 1.5 namespace. + */ +define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG3', 'http://video.search.yahoo.com/mrss/'); + +/** + * Wrong Media RSS Namespace #4. New spec location after the RSS Advisory Board takes it over, but not a valid namespace. + */ +define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG4', 'http://www.rssboard.org/media-rss'); + +/** + * Wrong Media RSS Namespace #5. A possible typo of the RSS Advisory Board URL. + */ +define('SIMPLEPIE_NAMESPACE_MEDIARSS_WRONG5', 'http://www.rssboard.org/media-rss/'); + +/** + * iTunes RSS Namespace + */ +define('SIMPLEPIE_NAMESPACE_ITUNES', 'http://www.itunes.com/dtds/podcast-1.0.dtd'); + +/** + * XHTML Namespace + */ +define('SIMPLEPIE_NAMESPACE_XHTML', 'http://www.w3.org/1999/xhtml'); + +/** + * IANA Link Relations Registry + */ +define('SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY', 'http://www.iana.org/assignments/relation/'); + +/** + * No file source + */ +define('SIMPLEPIE_FILE_SOURCE_NONE', 0); + +/** + * Remote file source + */ +define('SIMPLEPIE_FILE_SOURCE_REMOTE', 1); + +/** + * Local file source + */ +define('SIMPLEPIE_FILE_SOURCE_LOCAL', 2); + +/** + * fsockopen() file source + */ +define('SIMPLEPIE_FILE_SOURCE_FSOCKOPEN', 4); + +/** + * cURL file source + */ +define('SIMPLEPIE_FILE_SOURCE_CURL', 8); + +/** + * file_get_contents() file source + */ +define('SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS', 16); + + + +/** + * SimplePie + * + * @package SimplePie + * @subpackage API + */ +class SimplePie +{ + /** + * @var array Raw data + * @access private + */ + public $data = array(); + + /** + * @var mixed Error string + * @access private + */ + public $error; + + /** + * @var object Instance of SimplePie_Sanitize (or other class) + * @see SimplePie::set_sanitize_class() + * @access private + */ + public $sanitize; + + /** + * @var string SimplePie Useragent + * @see SimplePie::set_useragent() + * @access private + */ + public $useragent = SIMPLEPIE_USERAGENT; + + /** + * @var string Feed URL + * @see SimplePie::set_feed_url() + * @access private + */ + public $feed_url; + + /** + * @var object Instance of SimplePie_File to use as a feed + * @see SimplePie::set_file() + * @access private + */ + public $file; + + /** + * @var string Raw feed data + * @see SimplePie::set_raw_data() + * @access private + */ + public $raw_data; + + /** + * @var int Timeout for fetching remote files + * @see SimplePie::set_timeout() + * @access private + */ + public $timeout = 10; + + /** + * @var bool Forces fsockopen() to be used for remote files instead + * of cURL, even if a new enough version is installed + * @see SimplePie::force_fsockopen() + * @access private + */ + public $force_fsockopen = false; + + /** + * @var bool Force the given data/URL to be treated as a feed no matter what + * it appears like + * @see SimplePie::force_feed() + * @access private + */ + public $force_feed = false; + + /** + * @var bool Enable/Disable Caching + * @see SimplePie::enable_cache() + * @access private + */ + public $cache = true; + + /** + * @var int Cache duration (in seconds) + * @see SimplePie::set_cache_duration() + * @access private + */ + public $cache_duration = 3600; + + /** + * @var int Auto-discovery cache duration (in seconds) + * @see SimplePie::set_autodiscovery_cache_duration() + * @access private + */ + public $autodiscovery_cache_duration = 604800; // 7 Days. + + /** + * @var string Cache location (relative to executing script) + * @see SimplePie::set_cache_location() + * @access private + */ + public $cache_location = './cache'; + + /** + * @var string Function that creates the cache filename + * @see SimplePie::set_cache_name_function() + * @access private + */ + public $cache_name_function = 'md5'; + + /** + * @var bool Reorder feed by date descending + * @see SimplePie::enable_order_by_date() + * @access private + */ + public $order_by_date = true; + + /** + * @var mixed Force input encoding to be set to the follow value + * (false, or anything type-cast to false, disables this feature) + * @see SimplePie::set_input_encoding() + * @access private + */ + public $input_encoding = false; + + /** + * @var int Feed Autodiscovery Level + * @see SimplePie::set_autodiscovery_level() + * @access private + */ + public $autodiscovery = SIMPLEPIE_LOCATOR_ALL; + + /** + * Class registry object + * + * @var SimplePie_Registry + */ + public $registry; + + /** + * @var int Maximum number of feeds to check with autodiscovery + * @see SimplePie::set_max_checked_feeds() + * @access private + */ + public $max_checked_feeds = 10; + + /** + * @var array All the feeds found during the autodiscovery process + * @see SimplePie::get_all_discovered_feeds() + * @access private + */ + public $all_discovered_feeds = array(); + + /** + * @var string Web-accessible path to the handler_image.php file. + * @see SimplePie::set_image_handler() + * @access private + */ + public $image_handler = ''; + + /** + * @var array Stores the URLs when multiple feeds are being initialized. + * @see SimplePie::set_feed_url() + * @access private + */ + public $multifeed_url = array(); + + /** + * @var array Stores SimplePie objects when multiple feeds initialized. + * @access private + */ + public $multifeed_objects = array(); + + /** + * @var array Stores the get_object_vars() array for use with multifeeds. + * @see SimplePie::set_feed_url() + * @access private + */ + public $config_settings = null; + + /** + * @var integer Stores the number of items to return per-feed with multifeeds. + * @see SimplePie::set_item_limit() + * @access private + */ + public $item_limit = 0; + + /** + * @var array Stores the default attributes to be stripped by strip_attributes(). + * @see SimplePie::strip_attributes() + * @access private + */ + public $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'); + + /** + * @var array Stores the default tags to be stripped by strip_htmltags(). + * @see SimplePie::strip_htmltags() + * @access private + */ + public $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'); + + /** + * The SimplePie class contains feed level data and options + * + * To use SimplePie, create the SimplePie object with no parameters. You can + * then set configuration options using the provided methods. After setting + * them, you must initialise the feed using $feed->init(). At that point the + * object's methods and properties will be available to you. + * + * Previously, it was possible to pass in the feed URL along with cache + * options directly into the constructor. This has been removed as of 1.3 as + * it caused a lot of confusion. + * + * @since 1.0 Preview Release + */ + public function __construct() + { + if (version_compare(PHP_VERSION, '5.2', '<')) + { + trigger_error('PHP 4.x, 5.0 and 5.1 are no longer supported. Please upgrade to PHP 5.2 or newer.'); + die(); + } + + // Other objects, instances created here so we can set options on them + $this->sanitize = new SimplePie_Sanitize(); + $this->registry = new SimplePie_Registry(); + + if (func_num_args() > 0) + { + $level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING; + trigger_error('Passing parameters to the constructor is no longer supported. Please use set_feed_url(), set_cache_location(), and set_cache_location() directly.', $level); + + $args = func_get_args(); + switch (count($args)) { + case 3: + $this->set_cache_duration($args[2]); + case 2: + $this->set_cache_location($args[1]); + case 1: + $this->set_feed_url($args[0]); + $this->init(); + } + } + } + + /** + * Used for converting object to a string + */ + public function __toString() + { + return md5(serialize($this->data)); + } + + /** + * Remove items that link back to this before destroying this object + */ + public function __destruct() + { + if ((version_compare(PHP_VERSION, '5.3', '<') || !gc_enabled()) && !ini_get('zend.ze1_compatibility_mode')) + { + if (!empty($this->data['items'])) + { + foreach ($this->data['items'] as $item) + { + $item->__destruct(); + } + unset($item, $this->data['items']); + } + if (!empty($this->data['ordered_items'])) + { + foreach ($this->data['ordered_items'] as $item) + { + $item->__destruct(); + } + unset($item, $this->data['ordered_items']); + } + } + } + + /** + * Force the given data/URL to be treated as a feed + * + * This tells SimplePie to ignore the content-type provided by the server. + * Be careful when using this option, as it will also disable autodiscovery. + * + * @since 1.1 + * @param bool $enable Force the given data/URL to be treated as a feed + */ + public function force_feed($enable = false) + { + $this->force_feed = (bool) $enable; + } + + /** + * Set the URL of the feed you want to parse + * + * This allows you to enter the URL of the feed you want to parse, or the + * website you want to try to use auto-discovery on. This takes priority + * over any set raw data. + * + * You can set multiple feeds to mash together by passing an array instead + * of a string for the $url. Remember that with each additional feed comes + * additional processing and resources. + * + * @since 1.0 Preview Release + * @see set_raw_data() + * @param string|array $url This is the URL (or array of URLs) that you want to parse. + */ + public function set_feed_url($url) + { + $this->multifeed_url = array(); + if (is_array($url)) + { + foreach ($url as $value) + { + $this->multifeed_url[] = $this->registry->call('Misc', 'fix_protocol', array($value, 1)); + } + } + else + { + $this->feed_url = $this->registry->call('Misc', 'fix_protocol', array($url, 1)); + } + } + + /** + * Set an instance of {@see SimplePie_File} to use as a feed + * + * @param SimplePie_File &$file + * @return bool True on success, false on failure + */ + public function set_file(&$file) + { + if ($file instanceof SimplePie_File) + { + $this->feed_url = $file->url; + $this->file =& $file; + return true; + } + return false; + } + + /** + * Set the raw XML data to parse + * + * Allows you to use a string of RSS/Atom data instead of a remote feed. + * + * If you have a feed available as a string in PHP, you can tell SimplePie + * to parse that data string instead of a remote feed. Any set feed URL + * takes precedence. + * + * @since 1.0 Beta 3 + * @param string $data RSS or Atom data as a string. + * @see set_feed_url() + */ + public function set_raw_data($data) + { + $this->raw_data = $data; + } + + /** + * Set the the default timeout for fetching remote feeds + * + * This allows you to change the maximum time the feed's server to respond + * and send the feed back. + * + * @since 1.0 Beta 3 + * @param int $timeout The maximum number of seconds to spend waiting to retrieve a feed. + */ + public function set_timeout($timeout = 10) + { + $this->timeout = (int) $timeout; + } + + /** + * Force SimplePie to use fsockopen() instead of cURL + * + * @since 1.0 Beta 3 + * @param bool $enable Force fsockopen() to be used + */ + public function force_fsockopen($enable = false) + { + $this->force_fsockopen = (bool) $enable; + } + + /** + * Enable/disable caching in SimplePie. + * + * This option allows you to disable caching all-together in SimplePie. + * However, disabling the cache can lead to longer load times. + * + * @since 1.0 Preview Release + * @param bool $enable Enable caching + */ + public function enable_cache($enable = true) + { + $this->cache = (bool) $enable; + } + + /** + * Set the length of time (in seconds) that the contents of a feed will be + * cached + * + * @param int $seconds The feed content cache duration + */ + public function set_cache_duration($seconds = 3600) + { + $this->cache_duration = (int) $seconds; + } + + /** + * Set the length of time (in seconds) that the autodiscovered feed URL will + * be cached + * + * @param int $seconds The autodiscovered feed URL cache duration. + */ + public function set_autodiscovery_cache_duration($seconds = 604800) + { + $this->autodiscovery_cache_duration = (int) $seconds; + } + + /** + * Set the file system location where the cached files should be stored + * + * @param string $location The file system location. + */ + public function set_cache_location($location = './cache') + { + $this->cache_location = (string) $location; + } + + /** + * Set whether feed items should be sorted into reverse chronological order + * + * @param bool $enable Sort as reverse chronological order. + */ + public function enable_order_by_date($enable = true) + { + $this->order_by_date = (bool) $enable; + } + + /** + * Set the character encoding used to parse the feed + * + * This overrides the encoding reported by the feed, however it will fall + * back to the normal encoding detection if the override fails + * + * @param string $encoding Character encoding + */ + public function set_input_encoding($encoding = false) + { + if ($encoding) + { + $this->input_encoding = (string) $encoding; + } + else + { + $this->input_encoding = false; + } + } + + /** + * Set how much feed autodiscovery to do + * + * @see SIMPLEPIE_LOCATOR_NONE + * @see SIMPLEPIE_LOCATOR_AUTODISCOVERY + * @see SIMPLEPIE_LOCATOR_LOCAL_EXTENSION + * @see SIMPLEPIE_LOCATOR_LOCAL_BODY + * @see SIMPLEPIE_LOCATOR_REMOTE_EXTENSION + * @see SIMPLEPIE_LOCATOR_REMOTE_BODY + * @see SIMPLEPIE_LOCATOR_ALL + * @param int $level Feed Autodiscovery Level (level can be a combination of the above constants, see bitwise OR operator) + */ + public function set_autodiscovery_level($level = SIMPLEPIE_LOCATOR_ALL) + { + $this->autodiscovery = (int) $level; + } + + /** + * Get the class registry + * + * Use this to override SimplePie's default classes + * @see SimplePie_Registry + * @return SimplePie_Registry + */ + public function &get_registry() + { + return $this->registry; + } + + /**#@+ + * Useful when you are overloading or extending SimplePie's default classes. + * + * @deprecated Use {@see get_registry()} instead + * @link http://php.net/manual/en/language.oop5.basic.php#language.oop5.basic.extends PHP5 extends documentation + * @param string $class Name of custom class + * @return boolean True on success, false otherwise + */ + /** + * Set which class SimplePie uses for caching + */ + public function set_cache_class($class = 'SimplePie_Cache') + { + return $this->registry->register('Cache', $class, true); + } + + /** + * Set which class SimplePie uses for auto-discovery + */ + public function set_locator_class($class = 'SimplePie_Locator') + { + return $this->registry->register('Locator', $class, true); + } + + /** + * Set which class SimplePie uses for XML parsing + */ + public function set_parser_class($class = 'SimplePie_Parser') + { + return $this->registry->register('Parser', $class, true); + } + + /** + * Set which class SimplePie uses for remote file fetching + */ + public function set_file_class($class = 'SimplePie_File') + { + return $this->registry->register('File', $class, true); + } + + /** + * Set which class SimplePie uses for data sanitization + */ + public function set_sanitize_class($class = 'SimplePie_Sanitize') + { + return $this->registry->register('Sanitize', $class, true); + } + + /** + * Set which class SimplePie uses for handling feed items + */ + public function set_item_class($class = 'SimplePie_Item') + { + return $this->registry->register('Item', $class, true); + } + + /** + * Set which class SimplePie uses for handling author data + */ + public function set_author_class($class = 'SimplePie_Author') + { + return $this->registry->register('Author', $class, true); + } + + /** + * Set which class SimplePie uses for handling category data + */ + public function set_category_class($class = 'SimplePie_Category') + { + return $this->registry->register('Category', $class, true); + } + + /** + * Set which class SimplePie uses for feed enclosures + */ + public function set_enclosure_class($class = 'SimplePie_Enclosure') + { + return $this->registry->register('Enclosure', $class, true); + } + + /** + * Set which class SimplePie uses for `<media:text>` captions + */ + public function set_caption_class($class = 'SimplePie_Caption') + { + return $this->registry->register('Caption', $class, true); + } + + /** + * Set which class SimplePie uses for `<media:copyright>` + */ + public function set_copyright_class($class = 'SimplePie_Copyright') + { + return $this->registry->register('Copyright', $class, true); + } + + /** + * Set which class SimplePie uses for `<media:credit>` + */ + public function set_credit_class($class = 'SimplePie_Credit') + { + return $this->registry->register('Credit', $class, true); + } + + /** + * Set which class SimplePie uses for `<media:rating>` + */ + public function set_rating_class($class = 'SimplePie_Rating') + { + return $this->registry->register('Rating', $class, true); + } + + /** + * Set which class SimplePie uses for `<media:restriction>` + */ + public function set_restriction_class($class = 'SimplePie_Restriction') + { + return $this->registry->register('Restriction', $class, true); + } + + /** + * Set which class SimplePie uses for content-type sniffing + */ + public function set_content_type_sniffer_class($class = 'SimplePie_Content_Type_Sniffer') + { + return $this->registry->register('Content_Type_Sniffer', $class, true); + } + + /** + * Set which class SimplePie uses item sources + */ + public function set_source_class($class = 'SimplePie_Source') + { + return $this->registry->register('Source', $class, true); + } + /**#@-*/ + + /** + * Set the user agent string + * + * @param string $ua New user agent string. + */ + public function set_useragent($ua = SIMPLEPIE_USERAGENT) + { + $this->useragent = (string) $ua; + } + + /** + * Set callback function to create cache filename with + * + * @param mixed $function Callback function + */ + public function set_cache_name_function($function = 'md5') + { + if (is_callable($function)) + { + $this->cache_name_function = $function; + } + } + + /** + * Set options to make SP as fast as possible + * + * Forgoes a substantial amount of data sanitization in favor of speed. This + * turns SimplePie into a dumb parser of feeds. + * + * @param bool $set Whether to set them or not + */ + public function set_stupidly_fast($set = false) + { + if ($set) + { + $this->enable_order_by_date(false); + $this->remove_div(false); + $this->strip_comments(false); + $this->strip_htmltags(false); + $this->strip_attributes(false); + $this->set_image_handler(false); + } + } + + /** + * Set maximum number of feeds to check with autodiscovery + * + * @param int $max Maximum number of feeds to check + */ + public function set_max_checked_feeds($max = 10) + { + $this->max_checked_feeds = (int) $max; + } + + public function remove_div($enable = true) + { + $this->sanitize->remove_div($enable); + } + + public function strip_htmltags($tags = '', $encode = null) + { + if ($tags === '') + { + $tags = $this->strip_htmltags; + } + $this->sanitize->strip_htmltags($tags); + if ($encode !== null) + { + $this->sanitize->encode_instead_of_strip($tags); + } + } + + public function encode_instead_of_strip($enable = true) + { + $this->sanitize->encode_instead_of_strip($enable); + } + + public function strip_attributes($attribs = '') + { + if ($attribs === '') + { + $attribs = $this->strip_attributes; + } + $this->sanitize->strip_attributes($attribs); + } + + /** + * Set the output encoding + * + * Allows you to override SimplePie's output to match that of your webpage. + * This is useful for times when your webpages are not being served as + * UTF-8. This setting will be obeyed by {@see handle_content_type()}, and + * is similar to {@see set_input_encoding()}. + * + * It should be noted, however, that not all character encodings can support + * all characters. If your page is being served as ISO-8859-1 and you try + * to display a Japanese feed, you'll likely see garbled characters. + * Because of this, it is highly recommended to ensure that your webpages + * are served as UTF-8. + * + * The number of supported character encodings depends on whether your web + * host supports {@link http://php.net/mbstring mbstring}, + * {@link http://php.net/iconv iconv}, or both. See + * {@link http://simplepie.org/wiki/faq/Supported_Character_Encodings} for + * more information. + * + * @param string $encoding + */ + public function set_output_encoding($encoding = 'UTF-8') + { + $this->sanitize->set_output_encoding($encoding); + } + + public function strip_comments($strip = false) + { + $this->sanitize->strip_comments($strip); + } + + /** + * Set element/attribute key/value pairs of HTML attributes + * containing URLs that need to be resolved relative to the feed + * + * Defaults to |a|@href, |area|@href, |blockquote|@cite, |del|@cite, + * |form|@action, |img|@longdesc, |img|@src, |input|@src, |ins|@cite, + * |q|@cite + * + * @since 1.0 + * @param array|null $element_attribute Element/attribute key/value pairs, null for default + */ + public function set_url_replacements($element_attribute = null) + { + $this->sanitize->set_url_replacements($element_attribute); + } + + /** + * Set the handler to enable the display of cached images. + * + * @param str $page Web-accessible path to the handler_image.php file. + * @param str $qs The query string that the value should be passed to. + */ + public function set_image_handler($page = false, $qs = 'i') + { + if ($page !== false) + { + $this->sanitize->set_image_handler($page . '?' . $qs . '='); + } + else + { + $this->image_handler = ''; + } + } + + /** + * Set the limit for items returned per-feed with multifeeds + * + * @param integer $limit The maximum number of items to return. + */ + public function set_item_limit($limit = 0) + { + $this->item_limit = (int) $limit; + } + + /** + * Initialize the feed object + * + * This is what makes everything happen. Period. This is where all of the + * configuration options get processed, feeds are fetched, cached, and + * parsed, and all of that other good stuff. + * + * @return boolean True if successful, false otherwise + */ + public function init() + { + // Check absolute bare minimum requirements. + if (!extension_loaded('xml') || !extension_loaded('pcre')) + { + return false; + } + // Then check the xml extension is sane (i.e., libxml 2.7.x issue on PHP < 5.2.9 and libxml 2.7.0 to 2.7.2 on any version) if we don't have xmlreader. + elseif (!extension_loaded('xmlreader')) + { + static $xml_is_sane = null; + if ($xml_is_sane === null) + { + $parser_check = xml_parser_create(); + xml_parse_into_struct($parser_check, '<foo>&</foo>', $values); + xml_parser_free($parser_check); + $xml_is_sane = isset($values[0]['value']); + } + if (!$xml_is_sane) + { + return false; + } + } + + if (method_exists($this->sanitize, 'set_registry')) + { + $this->sanitize->set_registry($this->registry); + } + + // Pass whatever was set with config options over to the sanitizer. + // Pass the classes in for legacy support; new classes should use the registry instead + $this->sanitize->pass_cache_data($this->cache, $this->cache_location, $this->cache_name_function, $this->registry->get_class('Cache')); + $this->sanitize->pass_file_data($this->registry->get_class('File'), $this->timeout, $this->useragent, $this->force_fsockopen); + + if (!empty($this->multifeed_url)) + { + $i = 0; + $success = 0; + $this->multifeed_objects = array(); + $this->error = array(); + foreach ($this->multifeed_url as $url) + { + $this->multifeed_objects[$i] = clone $this; + $this->multifeed_objects[$i]->set_feed_url($url); + $single_success = $this->multifeed_objects[$i]->init(); + $success |= $single_success; + if (!$single_success) + { + $this->error[$i] = $this->multifeed_objects[$i]->error(); + } + $i++; + } + return (bool) $success; + } + elseif ($this->feed_url === null && $this->raw_data === null) + { + return false; + } + + $this->error = null; + $this->data = array(); + $this->multifeed_objects = array(); + $cache = false; + + if ($this->feed_url !== null) + { + $parsed_feed_url = $this->registry->call('Misc', 'parse_url', array($this->feed_url)); + + // Decide whether to enable caching + if ($this->cache && $parsed_feed_url['scheme'] !== '') + { + $cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, call_user_func($this->cache_name_function, $this->feed_url), 'spc')); + } + + // Fetch the data via SimplePie_File into $this->raw_data + if (($fetched = $this->fetch_data($cache)) === true) + { + return true; + } + elseif ($fetched === false) { + return false; + } + + list($headers, $sniffed) = $fetched; + } + + // Set up array of possible encodings + $encodings = array(); + + // First check to see if input has been overridden. + if ($this->input_encoding !== false) + { + $encodings[] = $this->input_encoding; + } + + $application_types = array('application/xml', 'application/xml-dtd', 'application/xml-external-parsed-entity'); + $text_types = array('text/xml', 'text/xml-external-parsed-entity'); + + // RFC 3023 (only applies to sniffed content) + if (isset($sniffed)) + { + if (in_array($sniffed, $application_types) || substr($sniffed, 0, 12) === 'application/' && substr($sniffed, -4) === '+xml') + { + if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset)) + { + $encodings[] = strtoupper($charset[1]); + } + $encodings = array_merge($encodings, $this->registry->call('Misc', 'xml_encoding', array($this->raw_data, &$this->registry))); + $encodings[] = 'UTF-8'; + } + elseif (in_array($sniffed, $text_types) || substr($sniffed, 0, 5) === 'text/' && substr($sniffed, -4) === '+xml') + { + if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset)) + { + $encodings[] = $charset[1]; + } + $encodings[] = 'US-ASCII'; + } + // Text MIME-type default + elseif (substr($sniffed, 0, 5) === 'text/') + { + $encodings[] = 'US-ASCII'; + } + } + + // Fallback to XML 1.0 Appendix F.1/UTF-8/ISO-8859-1 + $encodings = array_merge($encodings, $this->registry->call('Misc', 'xml_encoding', array($this->raw_data, &$this->registry))); + $encodings[] = 'UTF-8'; + $encodings[] = 'ISO-8859-1'; + + // There's no point in trying an encoding twice + $encodings = array_unique($encodings); + + // Loop through each possible encoding, till we return something, or run out of possibilities + foreach ($encodings as $encoding) + { + // Change the encoding to UTF-8 (as we always use UTF-8 internally) + if ($utf8_data = $this->registry->call('Misc', 'change_encoding', array($this->raw_data, $encoding, 'UTF-8'))) + { + // Create new parser + $parser = $this->registry->create('Parser'); + + // If it's parsed fine + if ($parser->parse($utf8_data, 'UTF-8')) + { + $this->data = $parser->get_data(); + if (!($this->get_type() & ~SIMPLEPIE_TYPE_NONE)) + { + $this->error = "A feed could not be found at $this->feed_url. This does not appear to be a valid RSS or Atom feed."; + $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__)); + return false; + } + + if (isset($headers)) + { + $this->data['headers'] = $headers; + } + $this->data['build'] = SIMPLEPIE_BUILD; + + // Cache the file if caching is enabled + if ($cache && !$cache->save($this)) + { + trigger_error("$this->cache_location is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING); + } + return true; + } + } + } + + if (isset($parser)) + { + // We have an error, just set SimplePie_Misc::error to it and quit + $this->error = sprintf('This XML document is invalid, likely due to invalid characters. XML error: %s at line %d, column %d', $parser->get_error_string(), $parser->get_current_line(), $parser->get_current_column()); + } + else + { + $this->error = 'The data could not be converted to UTF-8. You MUST have either the iconv or mbstring extension installed. Upgrading to PHP 5.x (which includes iconv) is highly recommended.'; + } + + $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__)); + + return false; + } + + /** + * Fetch the data via SimplePie_File + * + * If the data is already cached, attempt to fetch it from there instead + * @param SimplePie_Cache|false $cache Cache handler, or false to not load from the cache + * @return array|true Returns true if the data was loaded from the cache, or an array of HTTP headers and sniffed type + */ + protected function fetch_data(&$cache) + { + // If it's enabled, use the cache + if ($cache) + { + // Load the Cache + $this->data = $cache->load(); + if (!empty($this->data)) + { + // If the cache is for an outdated build of SimplePie + if (!isset($this->data['build']) || $this->data['build'] !== SIMPLEPIE_BUILD) + { + $cache->unlink(); + $this->data = array(); + } + // If we've hit a collision just rerun it with caching disabled + elseif (isset($this->data['url']) && $this->data['url'] !== $this->feed_url) + { + $cache = false; + $this->data = array(); + } + // If we've got a non feed_url stored (if the page isn't actually a feed, or is a redirect) use that URL. + elseif (isset($this->data['feed_url'])) + { + // If the autodiscovery cache is still valid use it. + if ($cache->mtime() + $this->autodiscovery_cache_duration > time()) + { + // Do not need to do feed autodiscovery yet. + if ($this->data['feed_url'] !== $this->data['url']) + { + $this->set_feed_url($this->data['feed_url']); + return $this->init(); + } + + $cache->unlink(); + $this->data = array(); + } + } + // Check if the cache has been updated + elseif ($cache->mtime() + $this->cache_duration < time()) + { + // If we have last-modified and/or etag set + if (isset($this->data['headers']['last-modified']) || isset($this->data['headers']['etag'])) + { + $headers = array( + 'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1', + ); + if (isset($this->data['headers']['last-modified'])) + { + $headers['if-modified-since'] = $this->data['headers']['last-modified']; + } + if (isset($this->data['headers']['etag'])) + { + $headers['if-none-match'] = $this->data['headers']['etag']; + } + + $file = $this->registry->create('File', array($this->feed_url, $this->timeout/10, 5, $headers, $this->useragent, $this->force_fsockopen)); + + if ($file->success) + { + if ($file->status_code === 304) + { + $cache->touch(); + return true; + } + } + else + { + unset($file); + } + } + } + // If the cache is still valid, just return true + else + { + $this->raw_data = false; + return true; + } + } + // If the cache is empty, delete it + else + { + $cache->unlink(); + $this->data = array(); + } + } + // If we don't already have the file (it'll only exist if we've opened it to check if the cache has been modified), open it. + if (!isset($file)) + { + if ($this->file instanceof SimplePie_File && $this->file->url === $this->feed_url) + { + $file =& $this->file; + } + else + { + $headers = array( + 'Accept' => 'application/atom+xml, application/rss+xml, application/rdf+xml;q=0.9, application/xml;q=0.8, text/xml;q=0.8, text/html;q=0.7, unknown/unknown;q=0.1, application/unknown;q=0.1, */*;q=0.1', + ); + $file = $this->registry->create('File', array($this->feed_url, $this->timeout, 5, $headers, $this->useragent, $this->force_fsockopen)); + } + } + // If the file connection has an error, set SimplePie::error to that and quit + if (!$file->success && !($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300))) + { + $this->error = $file->error; + return !empty($this->data); + } + + if (!$this->force_feed) + { + // Check if the supplied URL is a feed, if it isn't, look for it. + $locate = $this->registry->create('Locator', array(&$file, $this->timeout, $this->useragent, $this->max_checked_feeds)); + + if (!$locate->is_feed($file)) + { + // We need to unset this so that if SimplePie::set_file() has been called that object is untouched + unset($file); + try + { + if (!($file = $locate->find($this->autodiscovery, $this->all_discovered_feeds))) + { + $this->error = "A feed could not be found at $this->feed_url. A feed with an invalid mime type may fall victim to this error, or " . SIMPLEPIE_NAME . " was unable to auto-discover it.. Use force_feed() if you are certain this URL is a real feed."; + $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__)); + return false; + } + } + catch (SimplePie_Exception $e) + { + // This is usually because DOMDocument doesn't exist + $this->error = $e->getMessage(); + $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, $e->getFile(), $e->getLine())); + return false; + } + if ($cache) + { + $this->data = array('url' => $this->feed_url, 'feed_url' => $file->url, 'build' => SIMPLEPIE_BUILD); + if (!$cache->save($this)) + { + trigger_error("$this->cache_location is not writeable. Make sure you've set the correct relative or absolute path, and that the location is server-writable.", E_USER_WARNING); + } + $cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, call_user_func($this->cache_name_function, $file->url), 'spc')); + } + $this->feed_url = $file->url; + } + $locate = null; + } + + $this->raw_data = $file->body; + + $headers = $file->headers; + $sniffer = $this->registry->create('Content_Type_Sniffer', array(&$file)); + $sniffed = $sniffer->get_type(); + + return array($headers, $sniffed); + } + + /** + * Get the error message for the occurred error. + * + * @return string|array Error message, or array of messages for multifeeds + */ + public function error() + { + return $this->error; + } + + /** + * Get the raw XML + * + * This is the same as the old `$feed->enable_xml_dump(true)`, but returns + * the data instead of printing it. + * + * @return string|boolean Raw XML data, false if the cache is used + */ + public function get_raw_data() + { + return $this->raw_data; + } + + /** + * Get the character encoding used for output + * + * @since Preview Release + * @return string + */ + public function get_encoding() + { + return $this->sanitize->output_encoding; + } + + /** + * Send the content-type header with correct encoding + * + * This method ensures that the SimplePie-enabled page is being served with + * the correct {@link http://www.iana.org/assignments/media-types/ mime-type} + * and character encoding HTTP headers (character encoding determined by the + * {@see set_output_encoding} config option). + * + * This won't work properly if any content or whitespace has already been + * sent to the browser, because it relies on PHP's + * {@link http://php.net/header header()} function, and these are the + * circumstances under which the function works. + * + * Because it's setting these settings for the entire page (as is the nature + * of HTTP headers), this should only be used once per page (again, at the + * top). + * + * @param string $mime MIME type to serve the page as + */ + public function handle_content_type($mime = 'text/html') + { + if (!headers_sent()) + { + $header = "Content-type: $mime;"; + if ($this->get_encoding()) + { + $header .= ' charset=' . $this->get_encoding(); + } + else + { + $header .= ' charset=UTF-8'; + } + header($header); + } + } + + /** + * Get the type of the feed + * + * This returns a SIMPLEPIE_TYPE_* constant, which can be tested against + * using {@link http://php.net/language.operators.bitwise bitwise operators} + * + * @since 0.8 (usage changed to using constants in 1.0) + * @see SIMPLEPIE_TYPE_NONE Unknown. + * @see SIMPLEPIE_TYPE_RSS_090 RSS 0.90. + * @see SIMPLEPIE_TYPE_RSS_091_NETSCAPE RSS 0.91 (Netscape). + * @see SIMPLEPIE_TYPE_RSS_091_USERLAND RSS 0.91 (Userland). + * @see SIMPLEPIE_TYPE_RSS_091 RSS 0.91. + * @see SIMPLEPIE_TYPE_RSS_092 RSS 0.92. + * @see SIMPLEPIE_TYPE_RSS_093 RSS 0.93. + * @see SIMPLEPIE_TYPE_RSS_094 RSS 0.94. + * @see SIMPLEPIE_TYPE_RSS_10 RSS 1.0. + * @see SIMPLEPIE_TYPE_RSS_20 RSS 2.0.x. + * @see SIMPLEPIE_TYPE_RSS_RDF RDF-based RSS. + * @see SIMPLEPIE_TYPE_RSS_SYNDICATION Non-RDF-based RSS (truly intended as syndication format). + * @see SIMPLEPIE_TYPE_RSS_ALL Any version of RSS. + * @see SIMPLEPIE_TYPE_ATOM_03 Atom 0.3. + * @see SIMPLEPIE_TYPE_ATOM_10 Atom 1.0. + * @see SIMPLEPIE_TYPE_ATOM_ALL Any version of Atom. + * @see SIMPLEPIE_TYPE_ALL Any known/supported feed type. + * @return int SIMPLEPIE_TYPE_* constant + */ + public function get_type() + { + if (!isset($this->data['type'])) + { + $this->data['type'] = SIMPLEPIE_TYPE_ALL; + if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'])) + { + $this->data['type'] &= SIMPLEPIE_TYPE_ATOM_10; + } + elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'])) + { + $this->data['type'] &= SIMPLEPIE_TYPE_ATOM_03; + } + elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'])) + { + if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['channel']) + || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['image']) + || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['item']) + || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_10]['textinput'])) + { + $this->data['type'] &= SIMPLEPIE_TYPE_RSS_10; + } + if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['channel']) + || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['image']) + || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['item']) + || isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_090]['textinput'])) + { + $this->data['type'] &= SIMPLEPIE_TYPE_RSS_090; + } + } + elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'])) + { + $this->data['type'] &= SIMPLEPIE_TYPE_RSS_ALL; + if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['attribs']['']['version'])) + { + switch (trim($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['attribs']['']['version'])) + { + case '0.91': + $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091; + if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['skiphours']['hour'][0]['data'])) + { + switch (trim($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][SIMPLEPIE_NAMESPACE_RSS_20]['skiphours']['hour'][0]['data'])) + { + case '0': + $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091_NETSCAPE; + break; + + case '24': + $this->data['type'] &= SIMPLEPIE_TYPE_RSS_091_USERLAND; + break; + } + } + break; + + case '0.92': + $this->data['type'] &= SIMPLEPIE_TYPE_RSS_092; + break; + + case '0.93': + $this->data['type'] &= SIMPLEPIE_TYPE_RSS_093; + break; + + case '0.94': + $this->data['type'] &= SIMPLEPIE_TYPE_RSS_094; + break; + + case '2.0': + $this->data['type'] &= SIMPLEPIE_TYPE_RSS_20; + break; + } + } + } + else + { + $this->data['type'] = SIMPLEPIE_TYPE_NONE; + } + } + return $this->data['type']; + } + + /** + * Get the URL for the feed + * + * May or may not be different from the URL passed to {@see set_feed_url()}, + * depending on whether auto-discovery was used. + * + * @since Preview Release (previously called `get_feed_url()` since SimplePie 0.8.) + * @todo If we have a perm redirect we should return the new URL + * @todo When we make the above change, let's support <itunes:new-feed-url> as well + * @todo Also, |atom:link|@rel=self + * @return string|null + */ + public function subscribe_url() + { + if ($this->feed_url !== null) + { + return $this->sanitize($this->feed_url, SIMPLEPIE_CONSTRUCT_IRI); + } + else + { + return null; + } + } + + /** + * Get data for an feed-level element + * + * This method allows you to get access to ANY element/attribute that is a + * sub-element of the opening feed tag. + * + * The return value is an indexed array of elements matching the given + * namespace and tag name. Each element has `attribs`, `data` and `child` + * subkeys. For `attribs` and `child`, these contain namespace subkeys. + * `attribs` then has one level of associative name => value data (where + * `value` is a string) after the namespace. `child` has tag-indexed keys + * after the namespace, each member of which is an indexed array matching + * this same format. + * + * For example: + * <pre> + * // This is probably a bad example because we already support + * // <media:content> natively, but it shows you how to parse through + * // the nodes. + * $group = $item->get_item_tags(SIMPLEPIE_NAMESPACE_MEDIARSS, 'group'); + * $content = $group[0]['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['content']; + * $file = $content[0]['attribs']['']['url']; + * echo $file; + * </pre> + * + * @since 1.0 + * @see http://simplepie.org/wiki/faq/supported_xml_namespaces + * @param string $namespace The URL of the XML namespace of the elements you're trying to access + * @param string $tag Tag name + * @return array + */ + public function get_feed_tags($namespace, $tag) + { + $type = $this->get_type(); + if ($type & SIMPLEPIE_TYPE_ATOM_10) + { + if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['child'][$namespace][$tag])) + { + return $this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['child'][$namespace][$tag]; + } + } + if ($type & SIMPLEPIE_TYPE_ATOM_03) + { + if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['child'][$namespace][$tag])) + { + return $this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['child'][$namespace][$tag]; + } + } + if ($type & SIMPLEPIE_TYPE_RSS_RDF) + { + if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][$namespace][$tag])) + { + return $this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['child'][$namespace][$tag]; + } + } + if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION) + { + if (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][$namespace][$tag])) + { + return $this->data['child'][SIMPLEPIE_NAMESPACE_RSS_20]['rss'][0]['child'][$namespace][$tag]; + } + } + return null; + } + + /** + * Get data for an channel-level element + * + * This method allows you to get access to ANY element/attribute in the + * channel/header section of the feed. + * + * See {@see SimplePie::get_feed_tags()} for a description of the return value + * + * @since 1.0 + * @see http://simplepie.org/wiki/faq/supported_xml_namespaces + * @param string $namespace The URL of the XML namespace of the elements you're trying to access + * @param string $tag Tag name + * @return array + */ + public function get_channel_tags($namespace, $tag) + { + $type = $this->get_type(); + if ($type & SIMPLEPIE_TYPE_ATOM_ALL) + { + if ($return = $this->get_feed_tags($namespace, $tag)) + { + return $return; + } + } + if ($type & SIMPLEPIE_TYPE_RSS_10) + { + if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'channel')) + { + if (isset($channel[0]['child'][$namespace][$tag])) + { + return $channel[0]['child'][$namespace][$tag]; + } + } + } + if ($type & SIMPLEPIE_TYPE_RSS_090) + { + if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'channel')) + { + if (isset($channel[0]['child'][$namespace][$tag])) + { + return $channel[0]['child'][$namespace][$tag]; + } + } + } + if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION) + { + if ($channel = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'channel')) + { + if (isset($channel[0]['child'][$namespace][$tag])) + { + return $channel[0]['child'][$namespace][$tag]; + } + } + } + return null; + } + + /** + * Get data for an channel-level element + * + * This method allows you to get access to ANY element/attribute in the + * image/logo section of the feed. + * + * See {@see SimplePie::get_feed_tags()} for a description of the return value + * + * @since 1.0 + * @see http://simplepie.org/wiki/faq/supported_xml_namespaces + * @param string $namespace The URL of the XML namespace of the elements you're trying to access + * @param string $tag Tag name + * @return array + */ + public function get_image_tags($namespace, $tag) + { + $type = $this->get_type(); + if ($type & SIMPLEPIE_TYPE_RSS_10) + { + if ($image = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'image')) + { + if (isset($image[0]['child'][$namespace][$tag])) + { + return $image[0]['child'][$namespace][$tag]; + } + } + } + if ($type & SIMPLEPIE_TYPE_RSS_090) + { + if ($image = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'image')) + { + if (isset($image[0]['child'][$namespace][$tag])) + { + return $image[0]['child'][$namespace][$tag]; + } + } + } + if ($type & SIMPLEPIE_TYPE_RSS_SYNDICATION) + { + if ($image = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'image')) + { + if (isset($image[0]['child'][$namespace][$tag])) + { + return $image[0]['child'][$namespace][$tag]; + } + } + } + return null; + } + + /** + * Get the base URL value from the feed + * + * Uses `<xml:base>` if available, otherwise uses the first link in the + * feed, or failing that, the URL of the feed itself. + * + * @see get_link + * @see subscribe_url + * + * @param array $element + * @return string + */ + public function get_base($element = array()) + { + if (!($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION) && !empty($element['xml_base_explicit']) && isset($element['xml_base'])) + { + return $element['xml_base']; + } + elseif ($this->get_link() !== null) + { + return $this->get_link(); + } + else + { + return $this->subscribe_url(); + } + } + + /** + * Sanitize feed data + * + * @access private + * @see SimplePie_Sanitize::sanitize() + * @param string $data Data to sanitize + * @param int $type One of the SIMPLEPIE_CONSTRUCT_* constants + * @param string $base Base URL to resolve URLs against + * @return string Sanitized data + */ + public function sanitize($data, $type, $base = '') + { + return $this->sanitize->sanitize($data, $type, $base); + } + + /** + * Get the title of the feed + * + * Uses `<atom:title>`, `<title>` or `<dc:title>` + * + * @since 1.0 (previously called `get_feed_title` since 0.8) + * @return string|null + */ + public function get_title() + { + if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'title')) + { + return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'title')) + { + return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + return null; + } + } + + /** + * Get a category for the feed + * + * @since Unknown + * @param int $key The category that you want to return. Remember that arrays begin with 0, not 1 + * @return SimplePie_Category|null + */ + public function get_category($key = 0) + { + $categories = $this->get_categories(); + if (isset($categories[$key])) + { + return $categories[$key]; + } + else + { + return null; + } + } + + /** + * Get all categories for the feed + * + * Uses `<atom:category>`, `<category>` or `<dc:subject>` + * + * @since Unknown + * @return array|null List of {@see SimplePie_Category} objects + */ + public function get_categories() + { + $categories = array(); + + foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'category') as $category) + { + $term = null; + $scheme = null; + $label = null; + if (isset($category['attribs']['']['term'])) + { + $term = $this->sanitize($category['attribs']['']['term'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['scheme'])) + { + $scheme = $this->sanitize($category['attribs']['']['scheme'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($category['attribs']['']['label'])) + { + $label = $this->sanitize($category['attribs']['']['label'], SIMPLEPIE_CONSTRUCT_TEXT); + } + $categories[] = $this->registry->create('Category', array($term, $scheme, $label)); + } + foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'category') as $category) + { + // This is really the label, but keep this as the term also for BC. + // Label will also work on retrieving because that falls back to term. + $term = $this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT); + if (isset($category['attribs']['']['domain'])) + { + $scheme = $this->sanitize($category['attribs']['']['domain'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + $scheme = null; + } + $categories[] = $this->registry->create('Category', array($term, $scheme, null)); + } + foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'subject') as $category) + { + $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); + } + foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'subject') as $category) + { + $categories[] = $this->registry->create('Category', array($this->sanitize($category['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); + } + + if (!empty($categories)) + { + return array_unique($categories); + } + else + { + return null; + } + } + + /** + * Get an author for the feed + * + * @since 1.1 + * @param int $key The author that you want to return. Remember that arrays begin with 0, not 1 + * @return SimplePie_Author|null + */ + public function get_author($key = 0) + { + $authors = $this->get_authors(); + if (isset($authors[$key])) + { + return $authors[$key]; + } + else + { + return null; + } + } + + /** + * Get all authors for the feed + * + * Uses `<atom:author>`, `<author>`, `<dc:creator>` or `<itunes:author>` + * + * @since 1.1 + * @return array|null List of {@see SimplePie_Author} objects + */ + public function get_authors() + { + $authors = array(); + foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'author') as $author) + { + $name = null; + $uri = null; + $email = null; + if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) + { + $name = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) + { + $uri = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); + } + if (isset($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) + { + $email = $this->sanitize($author['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $uri !== null) + { + $authors[] = $this->registry->create('Author', array($name, $uri, $email)); + } + } + if ($author = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'author')) + { + $name = null; + $url = null; + $email = null; + if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) + { + $name = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) + { + $url = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); + } + if (isset($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) + { + $email = $this->sanitize($author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $url !== null) + { + $authors[] = $this->registry->create('Author', array($name, $url, $email)); + } + } + foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'creator') as $author) + { + $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); + } + foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'creator') as $author) + { + $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); + } + foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'author') as $author) + { + $authors[] = $this->registry->create('Author', array($this->sanitize($author['data'], SIMPLEPIE_CONSTRUCT_TEXT), null, null)); + } + + if (!empty($authors)) + { + return array_unique($authors); + } + else + { + return null; + } + } + + /** + * Get a contributor for the feed + * + * @since 1.1 + * @param int $key The contrbutor that you want to return. Remember that arrays begin with 0, not 1 + * @return SimplePie_Author|null + */ + public function get_contributor($key = 0) + { + $contributors = $this->get_contributors(); + if (isset($contributors[$key])) + { + return $contributors[$key]; + } + else + { + return null; + } + } + + /** + * Get all contributors for the feed + * + * Uses `<atom:contributor>` + * + * @since 1.1 + * @return array|null List of {@see SimplePie_Author} objects + */ + public function get_contributors() + { + $contributors = array(); + foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'contributor') as $contributor) + { + $name = null; + $uri = null; + $email = null; + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])) + { + $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])) + { + $uri = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0])); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'])) + { + $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $uri !== null) + { + $contributors[] = $this->registry->create('Author', array($name, $uri, $email)); + } + } + foreach ((array) $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'contributor') as $contributor) + { + $name = null; + $url = null; + $email = null; + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'])) + { + $name = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['name'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'])) + { + $url = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['url'][0])); + } + if (isset($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'])) + { + $email = $this->sanitize($contributor['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['email'][0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + if ($name !== null || $email !== null || $url !== null) + { + $contributors[] = $this->registry->create('Author', array($name, $url, $email)); + } + } + + if (!empty($contributors)) + { + return array_unique($contributors); + } + else + { + return null; + } + } + + /** + * Get a single link for the feed + * + * @since 1.0 (previously called `get_feed_link` since Preview Release, `get_feed_permalink()` since 0.8) + * @param int $key The link that you want to return. Remember that arrays begin with 0, not 1 + * @param string $rel The relationship of the link to return + * @return string|null Link URL + */ + public function get_link($key = 0, $rel = 'alternate') + { + $links = $this->get_links($rel); + if (isset($links[$key])) + { + return $links[$key]; + } + else + { + return null; + } + } + + /** + * Get the permalink for the item + * + * Returns the first link available with a relationship of "alternate". + * Identical to {@see get_link()} with key 0 + * + * @see get_link + * @since 1.0 (previously called `get_feed_link` since Preview Release, `get_feed_permalink()` since 0.8) + * @internal Added for parity between the parent-level and the item/entry-level. + * @return string|null Link URL + */ + public function get_permalink() + { + return $this->get_link(0); + } + + /** + * Get all links for the feed + * + * Uses `<atom:link>` or `<link>` + * + * @since Beta 2 + * @param string $rel The relationship of links to return + * @return array|null Links found for the feed (strings) + */ + public function get_links($rel = 'alternate') + { + if (!isset($this->data['links'])) + { + $this->data['links'] = array(); + if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'link')) + { + foreach ($links as $link) + { + if (isset($link['attribs']['']['href'])) + { + $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; + $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); + } + } + } + if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'link')) + { + foreach ($links as $link) + { + if (isset($link['attribs']['']['href'])) + { + $link_rel = (isset($link['attribs']['']['rel'])) ? $link['attribs']['']['rel'] : 'alternate'; + $this->data['links'][$link_rel][] = $this->sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); + + } + } + } + if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link')) + { + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + } + if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link')) + { + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + } + if ($links = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link')) + { + $this->data['links']['alternate'][] = $this->sanitize($links[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($links[0])); + } + + $keys = array_keys($this->data['links']); + foreach ($keys as $key) + { + if ($this->registry->call('Misc', 'is_isegment_nz_nc', array($key))) + { + if (isset($this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key])) + { + $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] = array_merge($this->data['links'][$key], $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]); + $this->data['links'][$key] =& $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key]; + } + else + { + $this->data['links'][SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY . $key] =& $this->data['links'][$key]; + } + } + elseif (substr($key, 0, 41) === SIMPLEPIE_IANA_LINK_RELATIONS_REGISTRY) + { + $this->data['links'][substr($key, 41)] =& $this->data['links'][$key]; + } + $this->data['links'][$key] = array_unique($this->data['links'][$key]); + } + } + + if (isset($this->data['links'][$rel])) + { + return $this->data['links'][$rel]; + } + else + { + return null; + } + } + + public function get_all_discovered_feeds() + { + return $this->all_discovered_feeds; + } + + /** + * Get the content for the item + * + * Uses `<atom:subtitle>`, `<atom:tagline>`, `<description>`, + * `<dc:description>`, `<itunes:summary>` or `<itunes:subtitle>` + * + * @since 1.0 (previously called `get_feed_description()` since 0.8) + * @return string|null + */ + public function get_description() + { + if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'subtitle')) + { + return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'tagline')) + { + return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_MAYBE_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'description')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'summary')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'subtitle')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_HTML, $this->get_base($return[0])); + } + else + { + return null; + } + } + + /** + * Get the copyright info for the feed + * + * Uses `<atom:rights>`, `<atom:copyright>` or `<dc:rights>` + * + * @since 1.0 (previously called `get_feed_copyright()` since 0.8) + * @return string|null + */ + public function get_copyright() + { + if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'rights')) + { + return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_10_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'copyright')) + { + return $this->sanitize($return[0]['data'], $this->registry->call('Misc', 'atom_03_construct_type', array($return[0]['attribs'])), $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'copyright')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'rights')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'rights')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + return null; + } + } + + /** + * Get the language for the feed + * + * Uses `<language>`, `<dc:language>`, or @xml_lang + * + * @since 1.0 (previously called `get_feed_language()` since 0.8) + * @return string|null + */ + public function get_language() + { + if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'language')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_11, 'language')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_DC_10, 'language')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['xml_lang'])) + { + return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['feed'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['xml_lang'])) + { + return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_ATOM_03]['feed'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (isset($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['xml_lang'])) + { + return $this->sanitize($this->data['child'][SIMPLEPIE_NAMESPACE_RDF]['RDF'][0]['xml_lang'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif (isset($this->data['headers']['content-language'])) + { + return $this->sanitize($this->data['headers']['content-language'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + return null; + } + } + + /** + * Get the latitude coordinates for the item + * + * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications + * + * Uses `<geo:lat>` or `<georss:point>` + * + * @since 1.0 + * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo + * @link http://www.georss.org/ GeoRSS + * @return string|null + */ + public function get_latitude() + { + + if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lat')) + { + return (float) $return[0]['data']; + } + elseif (($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) + { + return (float) $match[1]; + } + else + { + return null; + } + } + + /** + * Get the longitude coordinates for the feed + * + * Compatible with the W3C WGS84 Basic Geo and GeoRSS specifications + * + * Uses `<geo:long>`, `<geo:lon>` or `<georss:point>` + * + * @since 1.0 + * @link http://www.w3.org/2003/01/geo/ W3C WGS84 Basic Geo + * @link http://www.georss.org/ GeoRSS + * @return string|null + */ + public function get_longitude() + { + if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'long')) + { + return (float) $return[0]['data']; + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_W3C_BASIC_GEO, 'lon')) + { + return (float) $return[0]['data']; + } + elseif (($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_GEORSS, 'point')) && preg_match('/^((?:-)?[0-9]+(?:\.[0-9]+)) ((?:-)?[0-9]+(?:\.[0-9]+))$/', trim($return[0]['data']), $match)) + { + return (float) $match[2]; + } + else + { + return null; + } + } + + /** + * Get the feed logo's title + * + * RSS 0.9.0, 1.0 and 2.0 feeds are allowed to have a "feed logo" title. + * + * Uses `<image><title>` or `<image><dc:title>` + * + * @return string|null + */ + public function get_image_title() + { + if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_DC_11, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_DC_10, 'title')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_TEXT); + } + else + { + return null; + } + } + + /** + * Get the feed logo's URL + * + * RSS 0.9.0, 2.0, Atom 1.0, and feeds with iTunes RSS tags are allowed to + * have a "feed logo" URL. This points directly to the image itself. + * + * Uses `<itunes:image>`, `<atom:logo>`, `<atom:icon>`, + * `<image><title>` or `<image><dc:title>` + * + * @return string|null + */ + public function get_image_url() + { + if ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ITUNES, 'image')) + { + return $this->sanitize($return[0]['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'logo')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); + } + elseif ($return = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'icon')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); + } + elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'url')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); + } + elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'url')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); + } + elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'url')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); + } + else + { + return null; + } + } + + + /** + * Get the feed logo's link + * + * RSS 0.9.0, 1.0 and 2.0 feeds are allowed to have a "feed logo" link. This + * points to a human-readable page that the image should link to. + * + * Uses `<itunes:image>`, `<atom:logo>`, `<atom:icon>`, + * `<image><title>` or `<image><dc:title>` + * + * @return string|null + */ + public function get_image_link() + { + if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'link')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); + } + elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'link')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); + } + elseif ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'link')) + { + return $this->sanitize($return[0]['data'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($return[0])); + } + else + { + return null; + } + } + + /** + * Get the feed logo's link + * + * RSS 2.0 feeds are allowed to have a "feed logo" width. + * + * Uses `<image><width>` or defaults to 88.0 if no width is specified and + * the feed is an RSS 2.0 feed. + * + * @return int|float|null + */ + public function get_image_width() + { + if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'width')) + { + return round($return[0]['data']); + } + elseif ($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION && $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'url')) + { + return 88.0; + } + else + { + return null; + } + } + + /** + * Get the feed logo's height + * + * RSS 2.0 feeds are allowed to have a "feed logo" height. + * + * Uses `<image><height>` or defaults to 31.0 if no height is specified and + * the feed is an RSS 2.0 feed. + * + * @return int|float|null + */ + public function get_image_height() + { + if ($return = $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'height')) + { + return round($return[0]['data']); + } + elseif ($this->get_type() & SIMPLEPIE_TYPE_RSS_SYNDICATION && $this->get_image_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'url')) + { + return 31.0; + } + else + { + return null; + } + } + + /** + * Get the number of items in the feed + * + * This is well-suited for {@link http://php.net/for for()} loops with + * {@see get_item()} + * + * @param int $max Maximum value to return. 0 for no limit + * @return int Number of items in the feed + */ + public function get_item_quantity($max = 0) + { + $max = (int) $max; + $qty = count($this->get_items()); + if ($max === 0) + { + return $qty; + } + else + { + return ($qty > $max) ? $max : $qty; + } + } + + /** + * Get a single item from the feed + * + * This is better suited for {@link http://php.net/for for()} loops, whereas + * {@see get_items()} is better suited for + * {@link http://php.net/foreach foreach()} loops. + * + * @see get_item_quantity() + * @since Beta 2 + * @param int $key The item that you want to return. Remember that arrays begin with 0, not 1 + * @return SimplePie_Item|null + */ + public function get_item($key = 0) + { + $items = $this->get_items(); + if (isset($items[$key])) + { + return $items[$key]; + } + else + { + return null; + } + } + + /** + * Get all items from the feed + * + * This is better suited for {@link http://php.net/for for()} loops, whereas + * {@see get_items()} is better suited for + * {@link http://php.net/foreach foreach()} loops. + * + * @see get_item_quantity + * @since Beta 2 + * @param int $start Index to start at + * @param int $end Number of items to return. 0 for all items after `$start` + * @return array|null List of {@see SimplePie_Item} objects + */ + public function get_items($start = 0, $end = 0) + { + if (!isset($this->data['items'])) + { + if (!empty($this->multifeed_objects)) + { + $this->data['items'] = SimplePie::merge_items($this->multifeed_objects, $start, $end, $this->item_limit); + } + else + { + $this->data['items'] = array(); + if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'entry')) + { + $keys = array_keys($items); + foreach ($keys as $key) + { + $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); + } + } + if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_03, 'entry')) + { + $keys = array_keys($items); + foreach ($keys as $key) + { + $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); + } + } + if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_10, 'item')) + { + $keys = array_keys($items); + foreach ($keys as $key) + { + $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); + } + } + if ($items = $this->get_feed_tags(SIMPLEPIE_NAMESPACE_RSS_090, 'item')) + { + $keys = array_keys($items); + foreach ($keys as $key) + { + $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); + } + } + if ($items = $this->get_channel_tags(SIMPLEPIE_NAMESPACE_RSS_20, 'item')) + { + $keys = array_keys($items); + foreach ($keys as $key) + { + $this->data['items'][] = $this->registry->create('Item', array($this, $items[$key])); + } + } + } + } + + if (!empty($this->data['items'])) + { + // If we want to order it by date, check if all items have a date, and then sort it + if ($this->order_by_date && empty($this->multifeed_objects)) + { + if (!isset($this->data['ordered_items'])) + { + $do_sort = true; + foreach ($this->data['items'] as $item) + { + if (!$item->get_date('U')) + { + $do_sort = false; + break; + } + } + $item = null; + $this->data['ordered_items'] = $this->data['items']; + if ($do_sort) + { + usort($this->data['ordered_items'], array(get_class($this), 'sort_items')); + } + } + $items = $this->data['ordered_items']; + } + else + { + $items = $this->data['items']; + } + + // Slice the data as desired + if ($end === 0) + { + return array_slice($items, $start); + } + else + { + return array_slice($items, $start, $end); + } + } + else + { + return array(); + } + } + + /** + * Set the favicon handler + * + * @deprecated Use your own favicon handling instead + */ + public function set_favicon_handler($page = false, $qs = 'i') + { + $level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING; + trigger_error('Favicon handling has been removed, please use your own handling', $level); + return false; + } + + /** + * Get the favicon for the current feed + * + * @deprecated Use your own favicon handling instead + */ + public function get_favicon() + { + $level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING; + trigger_error('Favicon handling has been removed, please use your own handling', $level); + + if (($url = $this->get_link()) !== null) + { + return 'http://g.etfv.co/' . urlencode($url); + } + + return false; + } + + /** + * Magic method handler + * + * @param string $method Method name + * @param array $args Arguments to the method + * @return mixed + */ + public function __call($method, $args) + { + if (strpos($method, 'subscribe_') === 0) + { + $level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING; + trigger_error('subscribe_*() has been deprecated, implement the callback yourself', $level); + return ''; + } + if ($method === 'enable_xml_dump') + { + $level = defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_WARNING; + trigger_error('enable_xml_dump() has been deprecated, use get_raw_data() instead', $level); + return false; + } + + $class = get_class($this); + $trace = debug_backtrace(); + $file = $trace[0]['file']; + $line = $trace[0]['line']; + trigger_error("Call to undefined method $class::$method() in $file on line $line", E_USER_ERROR); + } + + /** + * Sorting callback for items + * + * @access private + * @param SimplePie $a + * @param SimplePie $b + * @return boolean + */ + public static function sort_items($a, $b) + { + return $a->get_date('U') <= $b->get_date('U'); + } + + /** + * Merge items from several feeds into one + * + * If you're merging multiple feeds together, they need to all have dates + * for the items or else SimplePie will refuse to sort them. + * + * @link http://simplepie.org/wiki/tutorial/sort_multiple_feeds_by_time_and_date#if_feeds_require_separate_per-feed_settings + * @param array $urls List of SimplePie feed objects to merge + * @param int $start Starting item + * @param int $end Number of items to return + * @param int $limit Maximum number of items per feed + * @return array + */ + public static function merge_items($urls, $start = 0, $end = 0, $limit = 0) + { + if (is_array($urls) && sizeof($urls) > 0) + { + $items = array(); + foreach ($urls as $arg) + { + if ($arg instanceof SimplePie) + { + $items = array_merge($items, $arg->get_items(0, $limit)); + } + else + { + trigger_error('Arguments must be SimplePie objects', E_USER_WARNING); + } + } + + $do_sort = true; + foreach ($items as $item) + { + if (!$item->get_date('U')) + { + $do_sort = false; + break; + } + } + $item = null; + if ($do_sort) + { + usort($items, array(get_class($urls[0]), 'sort_items')); + } + + if ($end === 0) + { + return array_slice($items, $start); + } + else + { + return array_slice($items, $start, $end); + } + } + else + { + trigger_error('Cannot merge zero SimplePie objects', E_USER_WARNING); + return array(); + } + } +} +endif; \ No newline at end of file diff --git a/wp-includes/class-smtp.php b/wp-includes/class-smtp.php new file mode 100644 index 0000000..eddc991 --- /dev/null +++ b/wp-includes/class-smtp.php @@ -0,0 +1,943 @@ +<?php +/** + * PHPMailer RFC821 SMTP email transport class. + * Version 5.2.7 + * PHP version 5.0.0 + * @category PHP + * @package PHPMailer + * @link https://github.com/PHPMailer/PHPMailer/ + * @author Marcus Bointon (coolbru) <phpmailer@synchromedia.co.uk> + * @author Jim Jagielski (jimjag) <jimjag@gmail.com> + * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> + * @copyright 2013 Marcus Bointon + * @copyright 2004 - 2008 Andy Prevost + * @copyright 2010 - 2012 Jim Jagielski + * @license http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL) + */ + +/** + * PHPMailer RFC821 SMTP email transport class. + * + * Implements RFC 821 SMTP commands + * and provides some utility methods for sending mail to an SMTP server. + * + * PHP Version 5.0.0 + * + * @category PHP + * @package PHPMailer + * @link https://github.com/PHPMailer/PHPMailer/blob/master/class.smtp.php + * @author Chris Ryan <unknown@example.com> + * @author Marcus Bointon <phpmailer@synchromedia.co.uk> + * @license http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL) + */ + +class SMTP +{ + /** + * The PHPMailer SMTP Version number. + */ + const VERSION = '5.2.7'; + + /** + * SMTP line break constant. + */ + const CRLF = "\r\n"; + + /** + * The SMTP port to use if one is not specified. + */ + const DEFAULT_SMTP_PORT = 25; + + /** + * The PHPMailer SMTP Version number. + * @type string + * @deprecated This should be a constant + * @see SMTP::VERSION + */ + public $Version = '5.2.7'; + + /** + * SMTP server port number. + * @type int + * @deprecated This is only ever ued as default value, so should be a constant + * @see SMTP::DEFAULT_SMTP_PORT + */ + public $SMTP_PORT = 25; + + /** + * SMTP reply line ending + * @type string + * @deprecated Use the class constant instead + * @see SMTP::CRLF + */ + public $CRLF = "\r\n"; + + /** + * Debug output level. + * Options: 0 for no output, 1 for commands, 2 for data and commands + * @type int + */ + public $do_debug = 0; + + /** + * The function/method to use for debugging output. + * Options: 'echo', 'html' or 'error_log' + * @type string + */ + public $Debugoutput = 'echo'; + + /** + * Whether to use VERP. + * @type bool + */ + public $do_verp = false; + + /** + * The SMTP timeout value for reads, in seconds. + * @type int + */ + public $Timeout = 15; + + /** + * The SMTP timelimit value for reads, in seconds. + * @type int + */ + public $Timelimit = 30; + + /** + * The socket for the server connection. + * @type resource + */ + protected $smtp_conn; + + /** + * Error message, if any, for the last call. + * @type string + */ + protected $error = ''; + + /** + * The reply the server sent to us for HELO. + * @type string + */ + protected $helo_rply = ''; + + /** + * The most recent reply received from the server. + * @type string + */ + protected $last_reply = ''; + + /** + * Constructor. + * @access public + */ + public function __construct() + { + $this->smtp_conn = 0; + $this->error = null; + $this->helo_rply = null; + + $this->do_debug = 0; + } + + /** + * Output debugging info via a user-selected method. + * @param string $str Debug string to output + * @return void + */ + protected function edebug($str) + { + switch ($this->Debugoutput) { + case 'error_log': + //Don't output, just log + error_log($str); + break; + case 'html': + //Cleans up output a bit for a better looking, HTML-safe output + echo htmlentities( + preg_replace('/[\r\n]+/', '', $str), + ENT_QUOTES, + 'UTF-8' + ) + . "<br>\n"; + break; + case 'echo': + default: + //Just echoes whatever was received + echo $str; + } + } + + /** + * Connect to an SMTP server. + * @param string $host SMTP server IP or host name + * @param int $port The port number to connect to + * @param int $timeout How long to wait for the connection to open + * @param array $options An array of options for stream_context_create() + * @access public + * @return bool + */ + public function connect($host, $port = null, $timeout = 30, $options = array()) + { + // Clear errors to avoid confusion + $this->error = null; + + // Make sure we are __not__ connected + if ($this->connected()) { + // Already connected, generate error + $this->error = array('error' => 'Already connected to a server'); + return false; + } + + if (empty($port)) { + $port = self::DEFAULT_SMTP_PORT; + } + + // Connect to the SMTP server + $errno = 0; + $errstr = ''; + $socket_context = stream_context_create($options); + //Suppress errors; connection failures are handled at a higher level + $this->smtp_conn = @stream_socket_client( + $host . ":" . $port, + $errno, + $errstr, + $timeout, + STREAM_CLIENT_CONNECT, + $socket_context + ); + + // Verify we connected properly + if (empty($this->smtp_conn)) { + $this->error = array( + 'error' => 'Failed to connect to server', + 'errno' => $errno, + 'errstr' => $errstr + ); + if ($this->do_debug >= 1) { + $this->edebug( + 'SMTP -> ERROR: ' . $this->error['error'] + . ": $errstr ($errno)" + ); + } + return false; + } + + // SMTP server can take longer to respond, give longer timeout for first read + // Windows does not have support for this timeout function + if (substr(PHP_OS, 0, 3) != 'WIN') { + $max = ini_get('max_execution_time'); + if ($max != 0 && $timeout > $max) { // Don't bother if unlimited + @set_time_limit($timeout); + } + stream_set_timeout($this->smtp_conn, $timeout, 0); + } + + // Get any announcement + $announce = $this->get_lines(); + + if ($this->do_debug >= 2) { + $this->edebug('SMTP -> FROM SERVER:' . $announce); + } + + return true; + } + + /** + * Initiate a TLS (encrypted) session. + * @access public + * @return bool + */ + public function startTLS() + { + if (!$this->sendCommand("STARTTLS", "STARTTLS", 220)) { + return false; + } + // Begin encrypted connection + if (!stream_socket_enable_crypto( + $this->smtp_conn, + true, + STREAM_CRYPTO_METHOD_TLS_CLIENT + ) + ) { + return false; + } + return true; + } + + /** + * Perform SMTP authentication. + * Must be run after hello(). + * @see hello() + * @param string $username The user name + * @param string $password The password + * @param string $authtype The auth type (PLAIN, LOGIN, NTLM, CRAM-MD5) + * @param string $realm The auth realm for NTLM + * @param string $workstation The auth workstation for NTLM + * @access public + * @return bool True if successfully authenticated. + */ + public function authenticate( + $username, + $password, + $authtype = 'LOGIN', + $realm = '', + $workstation = '' + ) { + if (empty($authtype)) { + $authtype = 'LOGIN'; + } + + switch ($authtype) { + case 'PLAIN': + // Start authentication + if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) { + return false; + } + // Send encoded username and password + if (!$this->sendCommand( + 'User & Password', + base64_encode("\0" . $username . "\0" . $password), + 235 + ) + ) { + return false; + } + break; + case 'LOGIN': + // Start authentication + if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) { + return false; + } + if (!$this->sendCommand("Username", base64_encode($username), 334)) { + return false; + } + if (!$this->sendCommand("Password", base64_encode($password), 235)) { + return false; + } + break; + case 'NTLM': + /* + * ntlm_sasl_client.php + * Bundled with Permission + * + * How to telnet in windows: + * http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx + * PROTOCOL Docs http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication + */ + require_once 'extras/ntlm_sasl_client.php'; + $temp = new stdClass(); + $ntlm_client = new ntlm_sasl_client_class; + //Check that functions are available + if (!$ntlm_client->Initialize($temp)) { + $this->error = array('error' => $temp->error); + if ($this->do_debug >= 1) { + $this->edebug( + 'You need to enable some modules in your php.ini file: ' + . $this->error['error'] + ); + } + return false; + } + //msg1 + $msg1 = $ntlm_client->TypeMsg1($realm, $workstation); //msg1 + + if (!$this->sendCommand( + 'AUTH NTLM', + 'AUTH NTLM ' . base64_encode($msg1), + 334 + ) + ) { + return false; + } + + //Though 0 based, there is a white space after the 3 digit number + //msg2 + $challenge = substr($this->last_reply, 3); + $challenge = base64_decode($challenge); + $ntlm_res = $ntlm_client->NTLMResponse( + substr($challenge, 24, 8), + $password + ); + //msg3 + $msg3 = $ntlm_client->TypeMsg3( + $ntlm_res, + $username, + $realm, + $workstation + ); + // send encoded username + return $this->sendCommand('Username', base64_encode($msg3), 235); + break; + case 'CRAM-MD5': + // Start authentication + if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) { + return false; + } + // Get the challenge + $challenge = base64_decode(substr($this->last_reply, 4)); + + // Build the response + $response = $username . ' ' . $this->hmac($challenge, $password); + + // send encoded credentials + return $this->sendCommand('Username', base64_encode($response), 235); + break; + } + return true; + } + + /** + * Calculate an MD5 HMAC hash. + * Works like hash_hmac('md5', $data, $key) + * in case that function is not available + * @param string $data The data to hash + * @param string $key The key to hash with + * @access protected + * @return string + */ + protected function hmac($data, $key) + { + if (function_exists('hash_hmac')) { + return hash_hmac('md5', $data, $key); + } + + // The following borrowed from + // http://php.net/manual/en/function.mhash.php#27225 + + // RFC 2104 HMAC implementation for php. + // Creates an md5 HMAC. + // Eliminates the need to install mhash to compute a HMAC + // by Lance Rushing + + $b = 64; // byte length for md5 + if (strlen($key) > $b) { + $key = pack('H*', md5($key)); + } + $key = str_pad($key, $b, chr(0x00)); + $ipad = str_pad('', $b, chr(0x36)); + $opad = str_pad('', $b, chr(0x5c)); + $k_ipad = $key ^ $ipad; + $k_opad = $key ^ $opad; + + return md5($k_opad . pack('H*', md5($k_ipad . $data))); + } + + /** + * Check connection state. + * @access public + * @return bool True if connected. + */ + public function connected() + { + if (!empty($this->smtp_conn)) { + $sock_status = stream_get_meta_data($this->smtp_conn); + if ($sock_status['eof']) { + // the socket is valid but we are not connected + if ($this->do_debug >= 1) { + $this->edebug( + 'SMTP -> NOTICE: EOF caught while checking if connected' + ); + } + $this->close(); + return false; + } + return true; // everything looks good + } + return false; + } + + /** + * Close the socket and clean up the state of the class. + * Don't use this function without first trying to use QUIT. + * @see quit() + * @access public + * @return void + */ + public function close() + { + $this->error = null; // so there is no confusion + $this->helo_rply = null; + if (!empty($this->smtp_conn)) { + // close the connection and cleanup + fclose($this->smtp_conn); + $this->smtp_conn = 0; + } + } + + /** + * Send an SMTP DATA command. + * Issues a data command and sends the msg_data to the server, + * finializing the mail transaction. $msg_data is the message + * that is to be send with the headers. Each header needs to be + * on a single line followed by a <CRLF> with the message headers + * and the message body being separated by and additional <CRLF>. + * Implements rfc 821: DATA <CRLF> + * @param string $msg_data Message data to send + * @access public + * @return bool + */ + public function data($msg_data) + { + if (!$this->sendCommand('DATA', 'DATA', 354)) { + return false; + } + + /* The server is ready to accept data! + * according to rfc821 we should not send more than 1000 + * including the CRLF + * characters on a single line so we will break the data up + * into lines by \r and/or \n then if needed we will break + * each of those into smaller lines to fit within the limit. + * in addition we will be looking for lines that start with + * a period '.' and append and additional period '.' to that + * line. NOTE: this does not count towards limit. + */ + + // Normalize the line breaks before exploding + $msg_data = str_replace("\r\n", "\n", $msg_data); + $msg_data = str_replace("\r", "\n", $msg_data); + $lines = explode("\n", $msg_data); + + /* We need to find a good way to determine if headers are + * in the msg_data or if it is a straight msg body + * currently I am assuming rfc822 definitions of msg headers + * and if the first field of the first line (':' separated) + * does not contain a space then it _should_ be a header + * and we can process all lines before a blank "" line as + * headers. + */ + + $field = substr($lines[0], 0, strpos($lines[0], ':')); + $in_headers = false; + if (!empty($field) && !strstr($field, ' ')) { + $in_headers = true; + } + + //RFC 2822 section 2.1.1 limit + $max_line_length = 998; + + foreach ($lines as $line) { + $lines_out = null; + if ($line == '' && $in_headers) { + $in_headers = false; + } + // ok we need to break this line up into several smaller lines + while (strlen($line) > $max_line_length) { + $pos = strrpos(substr($line, 0, $max_line_length), ' '); + + // Patch to fix DOS attack + if (!$pos) { + $pos = $max_line_length - 1; + $lines_out[] = substr($line, 0, $pos); + $line = substr($line, $pos); + } else { + $lines_out[] = substr($line, 0, $pos); + $line = substr($line, $pos + 1); + } + + /* If processing headers add a LWSP-char to the front of new line + * rfc822 on long msg headers + */ + if ($in_headers) { + $line = "\t" . $line; + } + } + $lines_out[] = $line; + + // send the lines to the server + while (list(, $line_out) = @each($lines_out)) { + if (strlen($line_out) > 0) { + if (substr($line_out, 0, 1) == '.') { + $line_out = '.' . $line_out; + } + } + $this->client_send($line_out . self::CRLF); + } + } + + // Message data has been sent, complete the command + return $this->sendCommand('DATA END', '.', 250); + } + + /** + * Send an SMTP HELO or EHLO command. + * Used to identify the sending server to the receiving server. + * This makes sure that client and server are in a known state. + * Implements from RFC 821: HELO <SP> <domain> <CRLF> + * and RFC 2821 EHLO. + * @param string $host The host name or IP to connect to + * @access public + * @return bool + */ + public function hello($host = '') + { + // Try extended hello first (RFC 2821) + if (!$this->sendHello('EHLO', $host)) { + if (!$this->sendHello('HELO', $host)) { + return false; + } + } + + return true; + } + + /** + * Send an SMTP HELO or EHLO command. + * Low-level implementation used by hello() + * @see hello() + * @param string $hello The HELO string + * @param string $host The hostname to say we are + * @access protected + * @return bool + */ + protected function sendHello($hello, $host) + { + $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250); + $this->helo_rply = $this->last_reply; + return $noerror; + } + + /** + * Send an SMTP MAIL command. + * Starts a mail transaction from the email address specified in + * $from. Returns true if successful or false otherwise. If True + * the mail transaction is started and then one or more recipient + * commands may be called followed by a data command. + * Implements rfc 821: MAIL <SP> FROM:<reverse-path> <CRLF> + * @param string $from Source address of this message + * @access public + * @return bool + */ + public function mail($from) + { + $useVerp = ($this->do_verp ? ' XVERP' : ''); + return $this->sendCommand( + 'MAIL FROM', + 'MAIL FROM:<' . $from . '>' . $useVerp, + 250 + ); + } + + /** + * Send an SMTP QUIT command. + * Closes the socket if there is no error or the $close_on_error argument is true. + * Implements from rfc 821: QUIT <CRLF> + * @param bool $close_on_error Should the connection close if an error occurs? + * @access public + * @return bool + */ + public function quit($close_on_error = true) + { + $noerror = $this->sendCommand('QUIT', 'QUIT', 221); + $e = $this->error; //Save any error + if ($noerror or $close_on_error) { + $this->close(); + $this->error = $e; //Restore any error from the quit command + } + return $noerror; + } + + /** + * Send an SMTP RCPT command. + * Sets the TO argument to $to. + * Returns true if the recipient was accepted false if it was rejected. + * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF> + * @param string $to The address the message is being sent to + * @access public + * @return bool + */ + public function recipient($to) + { + return $this->sendCommand( + 'RCPT TO ', + 'RCPT TO:<' . $to . '>', + array(250, 251) + ); + } + + /** + * Send an SMTP RSET command. + * Abort any transaction that is currently in progress. + * Implements rfc 821: RSET <CRLF> + * @access public + * @return bool True on success. + */ + public function reset() + { + return $this->sendCommand('RSET', 'RSET', 250); + } + + /** + * Send a command to an SMTP server and check its return code. + * @param string $command The command name - not sent to the server + * @param string $commandstring The actual command to send + * @param int|array $expect One or more expected integer success codes + * @access protected + * @return bool True on success. + */ + protected function sendCommand($command, $commandstring, $expect) + { + if (!$this->connected()) { + $this->error = array( + "error" => "Called $command without being connected" + ); + return false; + } + $this->client_send($commandstring . self::CRLF); + + $reply = $this->get_lines(); + $code = substr($reply, 0, 3); + + if ($this->do_debug >= 2) { + $this->edebug('SMTP -> FROM SERVER:' . $reply); + } + + if (!in_array($code, (array)$expect)) { + $this->last_reply = null; + $this->error = array( + "error" => "$command command failed", + "smtp_code" => $code, + "detail" => substr($reply, 4) + ); + if ($this->do_debug >= 1) { + $this->edebug( + 'SMTP -> ERROR: ' . $this->error['error'] . ': ' . $reply + ); + } + return false; + } + + $this->last_reply = $reply; + $this->error = null; + return true; + } + + /** + * Send an SMTP SAML command. + * Starts a mail transaction from the email address specified in $from. + * Returns true if successful or false otherwise. If True + * the mail transaction is started and then one or more recipient + * commands may be called followed by a data command. This command + * will send the message to the users terminal if they are logged + * in and send them an email. + * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF> + * @param string $from The address the message is from + * @access public + * @return bool + */ + public function sendAndMail($from) + { + return $this->sendCommand("SAML", "SAML FROM:$from", 250); + } + + /** + * Send an SMTP VRFY command. + * @param string $name The name to verify + * @access public + * @return bool + */ + public function verify($name) + { + return $this->sendCommand("VRFY", "VRFY $name", array(250, 251)); + } + + /** + * Send an SMTP NOOP command. + * Used to keep keep-alives alive, doesn't actually do anything + * @access public + * @return bool + */ + public function noop() + { + return $this->sendCommand("NOOP", "NOOP", 250); + } + + /** + * Send an SMTP TURN command. + * This is an optional command for SMTP that this class does not support. + * This method is here to make the RFC821 Definition + * complete for this class and __may__ be implemented in future + * Implements from rfc 821: TURN <CRLF> + * @access public + * @return bool + */ + public function turn() + { + $this->error = array( + 'error' => 'The SMTP TURN command is not implemented' + ); + if ($this->do_debug >= 1) { + $this->edebug('SMTP -> NOTICE: ' . $this->error['error']); + } + return false; + } + + /** + * Send raw data to the server. + * @param string $data The data to send + * @access public + * @return int|bool The number of bytes sent to the server or FALSE on error + */ + public function client_send($data) + { + if ($this->do_debug >= 1) { + $this->edebug("CLIENT -> SMTP: $data"); + } + return fwrite($this->smtp_conn, $data); + } + + /** + * Get the latest error. + * @access public + * @return array + */ + public function getError() + { + return $this->error; + } + + /** + * Get the last reply from the server. + * @access public + * @return string + */ + public function getLastReply() + { + return $this->last_reply; + } + + /** + * Read the SMTP server's response. + * Either before eof or socket timeout occurs on the operation. + * With SMTP we can tell if we have more lines to read if the + * 4th character is '-' symbol. If it is a space then we don't + * need to read anything else. + * @access protected + * @return string + */ + protected function get_lines() + { + $data = ''; + $endtime = 0; + // If the connection is bad, give up now + if (!is_resource($this->smtp_conn)) { + return $data; + } + stream_set_timeout($this->smtp_conn, $this->Timeout); + if ($this->Timelimit > 0) { + $endtime = time() + $this->Timelimit; + } + while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) { + $str = @fgets($this->smtp_conn, 515); + if ($this->do_debug >= 4) { + $this->edebug("SMTP -> get_lines(): \$data was \"$data\""); + $this->edebug("SMTP -> get_lines(): \$str is \"$str\""); + } + $data .= $str; + if ($this->do_debug >= 4) { + $this->edebug("SMTP -> get_lines(): \$data is \"$data\""); + } + // if 4th character is a space, we are done reading, break the loop + if (substr($str, 3, 1) == ' ') { + break; + } + // Timed-out? Log and break + $info = stream_get_meta_data($this->smtp_conn); + if ($info['timed_out']) { + if ($this->do_debug >= 4) { + $this->edebug( + 'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)' + ); + } + break; + } + // Now check if reads took too long + if ($endtime) { + if (time() > $endtime) { + if ($this->do_debug >= 4) { + $this->edebug( + 'SMTP -> get_lines(): timelimit reached (' + . $this->Timelimit . ' sec)' + ); + } + break; + } + } + } + return $data; + } + + /** + * Enable or disable VERP address generation. + * @param bool $enabled + */ + public function setVerp($enabled = false) + { + $this->do_verp = $enabled; + } + + /** + * Get VERP address generation mode. + * @return bool + */ + public function getVerp() + { + return $this->do_verp; + } + + /** + * Set debug output method. + * @param string $method The function/method to use for debugging output. + */ + public function setDebugOutput($method = 'echo') + { + $this->Debugoutput = $method; + } + + /** + * Get debug output method. + * @return string + */ + public function getDebugOutput() + { + return $this->Debugoutput; + } + + /** + * Set debug output level. + * @param int $level + */ + public function setDebugLevel($level = 0) + { + $this->do_debug = $level; + } + + /** + * Get debug output level. + * @return int + */ + public function getDebugLevel() + { + return $this->do_debug; + } + + /** + * Set SMTP timeout. + * @param int $timeout + */ + public function setTimeout($timeout = 0) + { + $this->Timeout = $timeout; + } + + /** + * Get SMTP timeout. + * @return int + */ + public function getTimeout() + { + return $this->Timeout; + } +} diff --git a/wp-includes/class-snoopy.php b/wp-includes/class-snoopy.php new file mode 100644 index 0000000..2c59c48 --- /dev/null +++ b/wp-includes/class-snoopy.php @@ -0,0 +1,1256 @@ +<?php + +/** + * Deprecated. Use WP_HTTP (http.php, class-http.php) instead. + */ +_deprecated_file( basename( __FILE__ ), '3.0', WPINC . '/http.php' ); + +if ( !class_exists( 'Snoopy' ) ) : +/************************************************* + +Snoopy - the PHP net client +Author: Monte Ohrt <monte@ispi.net> +Copyright (c): 1999-2008 New Digital Group, all rights reserved +Version: 1.2.4 + + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +You may contact the author of Snoopy by e-mail at: +monte@ohrt.com + +The latest version of Snoopy can be obtained from: +http://snoopy.sourceforge.net/ + +*************************************************/ + +class Snoopy +{ + /**** Public variables ****/ + + /* user definable vars */ + + var $host = "www.php.net"; // host name we are connecting to + var $port = 80; // port we are connecting to + var $proxy_host = ""; // proxy host to use + var $proxy_port = ""; // proxy port to use + var $proxy_user = ""; // proxy user to use + var $proxy_pass = ""; // proxy password to use + + var $agent = "Snoopy v1.2.4"; // agent we masquerade as + var $referer = ""; // referer info to pass + var $cookies = array(); // array of cookies to pass + // $cookies["username"]="joe"; + var $rawheaders = array(); // array of raw headers to send + // $rawheaders["Content-type"]="text/html"; + + var $maxredirs = 5; // http redirection depth maximum. 0 = disallow + var $lastredirectaddr = ""; // contains address of last redirected address + var $offsiteok = true; // allows redirection off-site + var $maxframes = 0; // frame content depth maximum. 0 = disallow + var $expandlinks = true; // expand links to fully qualified URLs. + // this only applies to fetchlinks() + // submitlinks(), and submittext() + var $passcookies = true; // pass set cookies back through redirects + // NOTE: this currently does not respect + // dates, domains or paths. + + var $user = ""; // user for http authentication + var $pass = ""; // password for http authentication + + // http accept types + var $accept = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*"; + + var $results = ""; // where the content is put + + var $error = ""; // error messages sent here + var $response_code = ""; // response code returned from server + var $headers = array(); // headers returned from server sent here + var $maxlength = 500000; // max return data length (body) + var $read_timeout = 0; // timeout on read operations, in seconds + // supported only since PHP 4 Beta 4 + // set to 0 to disallow timeouts + var $timed_out = false; // if a read operation timed out + var $status = 0; // http request status + + var $temp_dir = "/tmp"; // temporary directory that the webserver + // has permission to write to. + // under Windows, this should be C:\temp + + var $curl_path = "/usr/local/bin/curl"; + // Snoopy will use cURL for fetching + // SSL content if a full system path to + // the cURL binary is supplied here. + // set to false if you do not have + // cURL installed. See http://curl.haxx.se + // for details on installing cURL. + // Snoopy does *not* use the cURL + // library functions built into php, + // as these functions are not stable + // as of this Snoopy release. + + /**** Private variables ****/ + + var $_maxlinelen = 4096; // max line length (headers) + + var $_httpmethod = "GET"; // default http request method + var $_httpversion = "HTTP/1.0"; // default http request version + var $_submit_method = "POST"; // default submit method + var $_submit_type = "application/x-www-form-urlencoded"; // default submit type + var $_mime_boundary = ""; // MIME boundary for multipart/form-data submit type + var $_redirectaddr = false; // will be set if page fetched is a redirect + var $_redirectdepth = 0; // increments on an http redirect + var $_frameurls = array(); // frame src urls + var $_framedepth = 0; // increments on frame depth + + var $_isproxy = false; // set if using a proxy server + var $_fp_timeout = 30; // timeout for socket connection + +/*======================================================================*\ + Function: fetch + Purpose: fetch the contents of a web page + (and possibly other protocols in the + future like ftp, nntp, gopher, etc.) + Input: $URI the location of the page to fetch + Output: $this->results the output text from the fetch +\*======================================================================*/ + + function fetch($URI) + { + + //preg_match("|^([^:]+)://([^:/]+)(:[\d]+)*(.*)|",$URI,$URI_PARTS); + $URI_PARTS = parse_url($URI); + if (!empty($URI_PARTS["user"])) + $this->user = $URI_PARTS["user"]; + if (!empty($URI_PARTS["pass"])) + $this->pass = $URI_PARTS["pass"]; + if (empty($URI_PARTS["query"])) + $URI_PARTS["query"] = ''; + if (empty($URI_PARTS["path"])) + $URI_PARTS["path"] = ''; + + switch(strtolower($URI_PARTS["scheme"])) + { + case "http": + $this->host = $URI_PARTS["host"]; + if(!empty($URI_PARTS["port"])) + $this->port = $URI_PARTS["port"]; + if($this->_connect($fp)) + { + if($this->_isproxy) + { + // using proxy, send entire URI + $this->_httprequest($URI,$fp,$URI,$this->_httpmethod); + } + else + { + $path = $URI_PARTS["path"].($URI_PARTS["query"] ? "?".$URI_PARTS["query"] : ""); + // no proxy, send only the path + $this->_httprequest($path, $fp, $URI, $this->_httpmethod); + } + + $this->_disconnect($fp); + + if($this->_redirectaddr) + { + /* url was redirected, check if we've hit the max depth */ + if($this->maxredirs > $this->_redirectdepth) + { + // only follow redirect if it's on this site, or offsiteok is true + if(preg_match("|^http://".preg_quote($this->host)."|i",$this->_redirectaddr) || $this->offsiteok) + { + /* follow the redirect */ + $this->_redirectdepth++; + $this->lastredirectaddr=$this->_redirectaddr; + $this->fetch($this->_redirectaddr); + } + } + } + + if($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0) + { + $frameurls = $this->_frameurls; + $this->_frameurls = array(); + + while(list(,$frameurl) = each($frameurls)) + { + if($this->_framedepth < $this->maxframes) + { + $this->fetch($frameurl); + $this->_framedepth++; + } + else + break; + } + } + } + else + { + return false; + } + return true; + break; + case "https": + if(!$this->curl_path) + return false; + if(function_exists("is_executable")) + if (!is_executable($this->curl_path)) + return false; + $this->host = $URI_PARTS["host"]; + if(!empty($URI_PARTS["port"])) + $this->port = $URI_PARTS["port"]; + if($this->_isproxy) + { + // using proxy, send entire URI + $this->_httpsrequest($URI,$URI,$this->_httpmethod); + } + else + { + $path = $URI_PARTS["path"].($URI_PARTS["query"] ? "?".$URI_PARTS["query"] : ""); + // no proxy, send only the path + $this->_httpsrequest($path, $URI, $this->_httpmethod); + } + + if($this->_redirectaddr) + { + /* url was redirected, check if we've hit the max depth */ + if($this->maxredirs > $this->_redirectdepth) + { + // only follow redirect if it's on this site, or offsiteok is true + if(preg_match("|^http://".preg_quote($this->host)."|i",$this->_redirectaddr) || $this->offsiteok) + { + /* follow the redirect */ + $this->_redirectdepth++; + $this->lastredirectaddr=$this->_redirectaddr; + $this->fetch($this->_redirectaddr); + } + } + } + + if($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0) + { + $frameurls = $this->_frameurls; + $this->_frameurls = array(); + + while(list(,$frameurl) = each($frameurls)) + { + if($this->_framedepth < $this->maxframes) + { + $this->fetch($frameurl); + $this->_framedepth++; + } + else + break; + } + } + return true; + break; + default: + // not a valid protocol + $this->error = 'Invalid protocol "'.$URI_PARTS["scheme"].'"\n'; + return false; + break; + } + return true; + } + +/*======================================================================*\ + Function: submit + Purpose: submit an http form + Input: $URI the location to post the data + $formvars the formvars to use. + format: $formvars["var"] = "val"; + $formfiles an array of files to submit + format: $formfiles["var"] = "/dir/filename.ext"; + Output: $this->results the text output from the post +\*======================================================================*/ + + function submit($URI, $formvars="", $formfiles="") + { + unset($postdata); + + $postdata = $this->_prepare_post_body($formvars, $formfiles); + + $URI_PARTS = parse_url($URI); + if (!empty($URI_PARTS["user"])) + $this->user = $URI_PARTS["user"]; + if (!empty($URI_PARTS["pass"])) + $this->pass = $URI_PARTS["pass"]; + if (empty($URI_PARTS["query"])) + $URI_PARTS["query"] = ''; + if (empty($URI_PARTS["path"])) + $URI_PARTS["path"] = ''; + + switch(strtolower($URI_PARTS["scheme"])) + { + case "http": + $this->host = $URI_PARTS["host"]; + if(!empty($URI_PARTS["port"])) + $this->port = $URI_PARTS["port"]; + if($this->_connect($fp)) + { + if($this->_isproxy) + { + // using proxy, send entire URI + $this->_httprequest($URI,$fp,$URI,$this->_submit_method,$this->_submit_type,$postdata); + } + else + { + $path = $URI_PARTS["path"].($URI_PARTS["query"] ? "?".$URI_PARTS["query"] : ""); + // no proxy, send only the path + $this->_httprequest($path, $fp, $URI, $this->_submit_method, $this->_submit_type, $postdata); + } + + $this->_disconnect($fp); + + if($this->_redirectaddr) + { + /* url was redirected, check if we've hit the max depth */ + if($this->maxredirs > $this->_redirectdepth) + { + if(!preg_match("|^".$URI_PARTS["scheme"]."://|", $this->_redirectaddr)) + $this->_redirectaddr = $this->_expandlinks($this->_redirectaddr,$URI_PARTS["scheme"]."://".$URI_PARTS["host"]); + + // only follow redirect if it's on this site, or offsiteok is true + if(preg_match("|^http://".preg_quote($this->host)."|i",$this->_redirectaddr) || $this->offsiteok) + { + /* follow the redirect */ + $this->_redirectdepth++; + $this->lastredirectaddr=$this->_redirectaddr; + if( strpos( $this->_redirectaddr, "?" ) > 0 ) + $this->fetch($this->_redirectaddr); // the redirect has changed the request method from post to get + else + $this->submit($this->_redirectaddr,$formvars, $formfiles); + } + } + } + + if($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0) + { + $frameurls = $this->_frameurls; + $this->_frameurls = array(); + + while(list(,$frameurl) = each($frameurls)) + { + if($this->_framedepth < $this->maxframes) + { + $this->fetch($frameurl); + $this->_framedepth++; + } + else + break; + } + } + + } + else + { + return false; + } + return true; + break; + case "https": + if(!$this->curl_path) + return false; + if(function_exists("is_executable")) + if (!is_executable($this->curl_path)) + return false; + $this->host = $URI_PARTS["host"]; + if(!empty($URI_PARTS["port"])) + $this->port = $URI_PARTS["port"]; + if($this->_isproxy) + { + // using proxy, send entire URI + $this->_httpsrequest($URI, $URI, $this->_submit_method, $this->_submit_type, $postdata); + } + else + { + $path = $URI_PARTS["path"].($URI_PARTS["query"] ? "?".$URI_PARTS["query"] : ""); + // no proxy, send only the path + $this->_httpsrequest($path, $URI, $this->_submit_method, $this->_submit_type, $postdata); + } + + if($this->_redirectaddr) + { + /* url was redirected, check if we've hit the max depth */ + if($this->maxredirs > $this->_redirectdepth) + { + if(!preg_match("|^".$URI_PARTS["scheme"]."://|", $this->_redirectaddr)) + $this->_redirectaddr = $this->_expandlinks($this->_redirectaddr,$URI_PARTS["scheme"]."://".$URI_PARTS["host"]); + + // only follow redirect if it's on this site, or offsiteok is true + if(preg_match("|^http://".preg_quote($this->host)."|i",$this->_redirectaddr) || $this->offsiteok) + { + /* follow the redirect */ + $this->_redirectdepth++; + $this->lastredirectaddr=$this->_redirectaddr; + if( strpos( $this->_redirectaddr, "?" ) > 0 ) + $this->fetch($this->_redirectaddr); // the redirect has changed the request method from post to get + else + $this->submit($this->_redirectaddr,$formvars, $formfiles); + } + } + } + + if($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0) + { + $frameurls = $this->_frameurls; + $this->_frameurls = array(); + + while(list(,$frameurl) = each($frameurls)) + { + if($this->_framedepth < $this->maxframes) + { + $this->fetch($frameurl); + $this->_framedepth++; + } + else + break; + } + } + return true; + break; + + default: + // not a valid protocol + $this->error = 'Invalid protocol "'.$URI_PARTS["scheme"].'"\n'; + return false; + break; + } + return true; + } + +/*======================================================================*\ + Function: fetchlinks + Purpose: fetch the links from a web page + Input: $URI where you are fetching from + Output: $this->results an array of the URLs +\*======================================================================*/ + + function fetchlinks($URI) + { + if ($this->fetch($URI)) + { + if($this->lastredirectaddr) + $URI = $this->lastredirectaddr; + if(is_array($this->results)) + { + for($x=0;$x<count($this->results);$x++) + $this->results[$x] = $this->_striplinks($this->results[$x]); + } + else + $this->results = $this->_striplinks($this->results); + + if($this->expandlinks) + $this->results = $this->_expandlinks($this->results, $URI); + return true; + } + else + return false; + } + +/*======================================================================*\ + Function: fetchform + Purpose: fetch the form elements from a web page + Input: $URI where you are fetching from + Output: $this->results the resulting html form +\*======================================================================*/ + + function fetchform($URI) + { + + if ($this->fetch($URI)) + { + + if(is_array($this->results)) + { + for($x=0;$x<count($this->results);$x++) + $this->results[$x] = $this->_stripform($this->results[$x]); + } + else + $this->results = $this->_stripform($this->results); + + return true; + } + else + return false; + } + + +/*======================================================================*\ + Function: fetchtext + Purpose: fetch the text from a web page, stripping the links + Input: $URI where you are fetching from + Output: $this->results the text from the web page +\*======================================================================*/ + + function fetchtext($URI) + { + if($this->fetch($URI)) + { + if(is_array($this->results)) + { + for($x=0;$x<count($this->results);$x++) + $this->results[$x] = $this->_striptext($this->results[$x]); + } + else + $this->results = $this->_striptext($this->results); + return true; + } + else + return false; + } + +/*======================================================================*\ + Function: submitlinks + Purpose: grab links from a form submission + Input: $URI where you are submitting from + Output: $this->results an array of the links from the post +\*======================================================================*/ + + function submitlinks($URI, $formvars="", $formfiles="") + { + if($this->submit($URI,$formvars, $formfiles)) + { + if($this->lastredirectaddr) + $URI = $this->lastredirectaddr; + if(is_array($this->results)) + { + for($x=0;$x<count($this->results);$x++) + { + $this->results[$x] = $this->_striplinks($this->results[$x]); + if($this->expandlinks) + $this->results[$x] = $this->_expandlinks($this->results[$x],$URI); + } + } + else + { + $this->results = $this->_striplinks($this->results); + if($this->expandlinks) + $this->results = $this->_expandlinks($this->results,$URI); + } + return true; + } + else + return false; + } + +/*======================================================================*\ + Function: submittext + Purpose: grab text from a form submission + Input: $URI where you are submitting from + Output: $this->results the text from the web page +\*======================================================================*/ + + function submittext($URI, $formvars = "", $formfiles = "") + { + if($this->submit($URI,$formvars, $formfiles)) + { + if($this->lastredirectaddr) + $URI = $this->lastredirectaddr; + if(is_array($this->results)) + { + for($x=0;$x<count($this->results);$x++) + { + $this->results[$x] = $this->_striptext($this->results[$x]); + if($this->expandlinks) + $this->results[$x] = $this->_expandlinks($this->results[$x],$URI); + } + } + else + { + $this->results = $this->_striptext($this->results); + if($this->expandlinks) + $this->results = $this->_expandlinks($this->results,$URI); + } + return true; + } + else + return false; + } + + + +/*======================================================================*\ + Function: set_submit_multipart + Purpose: Set the form submission content type to + multipart/form-data +\*======================================================================*/ + function set_submit_multipart() + { + $this->_submit_type = "multipart/form-data"; + } + + +/*======================================================================*\ + Function: set_submit_normal + Purpose: Set the form submission content type to + application/x-www-form-urlencoded +\*======================================================================*/ + function set_submit_normal() + { + $this->_submit_type = "application/x-www-form-urlencoded"; + } + + + + +/*======================================================================*\ + Private functions +\*======================================================================*/ + + +/*======================================================================*\ + Function: _striplinks + Purpose: strip the hyperlinks from an html document + Input: $document document to strip. + Output: $match an array of the links +\*======================================================================*/ + + function _striplinks($document) + { + preg_match_all("'<\s*a\s.*?href\s*=\s* # find <a href= + ([\"\'])? # find single or double quote + (?(1) (.*?)\\1 | ([^\s\>]+)) # if quote found, match up to next matching + # quote, otherwise match up to next space + 'isx",$document,$links); + + + // catenate the non-empty matches from the conditional subpattern + + while(list($key,$val) = each($links[2])) + { + if(!empty($val)) + $match[] = $val; + } + + while(list($key,$val) = each($links[3])) + { + if(!empty($val)) + $match[] = $val; + } + + // return the links + return $match; + } + +/*======================================================================*\ + Function: _stripform + Purpose: strip the form elements from an html document + Input: $document document to strip. + Output: $match an array of the links +\*======================================================================*/ + + function _stripform($document) + { + preg_match_all("'<\/?(FORM|INPUT|SELECT|TEXTAREA|(OPTION))[^<>]*>(?(2)(.*(?=<\/?(option|select)[^<>]*>[\r\n]*)|(?=[\r\n]*))|(?=[\r\n]*))'Usi",$document,$elements); + + // catenate the matches + $match = implode("\r\n",$elements[0]); + + // return the links + return $match; + } + + + +/*======================================================================*\ + Function: _striptext + Purpose: strip the text from an html document + Input: $document document to strip. + Output: $text the resulting text +\*======================================================================*/ + + function _striptext($document) + { + + // I didn't use preg eval (//e) since that is only available in PHP 4.0. + // so, list your entities one by one here. I included some of the + // more common ones. + + $search = array("'<script[^>]*?>.*?</script>'si", // strip out javascript + "'<[\/\!]*?[^<>]*?>'si", // strip out html tags + "'([\r\n])[\s]+'", // strip out white space + "'&(quot|#34|#034|#x22);'i", // replace html entities + "'&(amp|#38|#038|#x26);'i", // added hexadecimal values + "'&(lt|#60|#060|#x3c);'i", + "'&(gt|#62|#062|#x3e);'i", + "'&(nbsp|#160|#xa0);'i", + "'&(iexcl|#161);'i", + "'&(cent|#162);'i", + "'&(pound|#163);'i", + "'&(copy|#169);'i", + "'&(reg|#174);'i", + "'&(deg|#176);'i", + "'&(#39|#039|#x27);'", + "'&(euro|#8364);'i", // europe + "'&a(uml|UML);'", // german + "'&o(uml|UML);'", + "'&u(uml|UML);'", + "'&A(uml|UML);'", + "'&O(uml|UML);'", + "'&U(uml|UML);'", + "'ß'i", + ); + $replace = array( "", + "", + "\\1", + "\"", + "&", + "<", + ">", + " ", + chr(161), + chr(162), + chr(163), + chr(169), + chr(174), + chr(176), + chr(39), + chr(128), + chr(0xE4), // ANSI ä + chr(0xF6), // ANSI ö + chr(0xFC), // ANSI ü + chr(0xC4), // ANSI Ä + chr(0xD6), // ANSI Ö + chr(0xDC), // ANSI Ü + chr(0xDF), // ANSI ß + ); + + $text = preg_replace($search,$replace,$document); + + return $text; + } + +/*======================================================================*\ + Function: _expandlinks + Purpose: expand each link into a fully qualified URL + Input: $links the links to qualify + $URI the full URI to get the base from + Output: $expandedLinks the expanded links +\*======================================================================*/ + + function _expandlinks($links,$URI) + { + + preg_match("/^[^\?]+/",$URI,$match); + + $match = preg_replace("|/[^\/\.]+\.[^\/\.]+$|","",$match[0]); + $match = preg_replace("|/$|","",$match); + $match_part = parse_url($match); + $match_root = + $match_part["scheme"]."://".$match_part["host"]; + + $search = array( "|^http://".preg_quote($this->host)."|i", + "|^(\/)|i", + "|^(?!http://)(?!mailto:)|i", + "|/\./|", + "|/[^\/]+/\.\./|" + ); + + $replace = array( "", + $match_root."/", + $match."/", + "/", + "/" + ); + + $expandedLinks = preg_replace($search,$replace,$links); + + return $expandedLinks; + } + +/*======================================================================*\ + Function: _httprequest + Purpose: go get the http data from the server + Input: $url the url to fetch + $fp the current open file pointer + $URI the full URI + $body body contents to send if any (POST) + Output: +\*======================================================================*/ + + function _httprequest($url,$fp,$URI,$http_method,$content_type="",$body="") + { + $cookie_headers = ''; + if($this->passcookies && $this->_redirectaddr) + $this->setcookies(); + + $URI_PARTS = parse_url($URI); + if(empty($url)) + $url = "/"; + $headers = $http_method." ".$url." ".$this->_httpversion."\r\n"; + if(!empty($this->agent)) + $headers .= "User-Agent: ".$this->agent."\r\n"; + if(!empty($this->host) && !isset($this->rawheaders['Host'])) { + $headers .= "Host: ".$this->host; + if(!empty($this->port) && $this->port != 80) + $headers .= ":".$this->port; + $headers .= "\r\n"; + } + if(!empty($this->accept)) + $headers .= "Accept: ".$this->accept."\r\n"; + if(!empty($this->referer)) + $headers .= "Referer: ".$this->referer."\r\n"; + if(!empty($this->cookies)) + { + if(!is_array($this->cookies)) + $this->cookies = (array)$this->cookies; + + reset($this->cookies); + if ( count($this->cookies) > 0 ) { + $cookie_headers .= 'Cookie: '; + foreach ( $this->cookies as $cookieKey => $cookieVal ) { + $cookie_headers .= $cookieKey."=".urlencode($cookieVal)."; "; + } + $headers .= substr($cookie_headers,0,-2) . "\r\n"; + } + } + if(!empty($this->rawheaders)) + { + if(!is_array($this->rawheaders)) + $this->rawheaders = (array)$this->rawheaders; + while(list($headerKey,$headerVal) = each($this->rawheaders)) + $headers .= $headerKey.": ".$headerVal."\r\n"; + } + if(!empty($content_type)) { + $headers .= "Content-type: $content_type"; + if ($content_type == "multipart/form-data") + $headers .= "; boundary=".$this->_mime_boundary; + $headers .= "\r\n"; + } + if(!empty($body)) + $headers .= "Content-length: ".strlen($body)."\r\n"; + if(!empty($this->user) || !empty($this->pass)) + $headers .= "Authorization: Basic ".base64_encode($this->user.":".$this->pass)."\r\n"; + + //add proxy auth headers + if(!empty($this->proxy_user)) + $headers .= 'Proxy-Authorization: ' . 'Basic ' . base64_encode($this->proxy_user . ':' . $this->proxy_pass)."\r\n"; + + + $headers .= "\r\n"; + + // set the read timeout if needed + if ($this->read_timeout > 0) + socket_set_timeout($fp, $this->read_timeout); + $this->timed_out = false; + + fwrite($fp,$headers.$body,strlen($headers.$body)); + + $this->_redirectaddr = false; + unset($this->headers); + + while($currentHeader = fgets($fp,$this->_maxlinelen)) + { + if ($this->read_timeout > 0 && $this->_check_timeout($fp)) + { + $this->status=-100; + return false; + } + + if($currentHeader == "\r\n") + break; + + // if a header begins with Location: or URI:, set the redirect + if(preg_match("/^(Location:|URI:)/i",$currentHeader)) + { + // get URL portion of the redirect + preg_match("/^(Location:|URI:)[ ]+(.*)/i",chop($currentHeader),$matches); + // look for :// in the Location header to see if hostname is included + if(!preg_match("|\:\/\/|",$matches[2])) + { + // no host in the path, so prepend + $this->_redirectaddr = $URI_PARTS["scheme"]."://".$this->host.":".$this->port; + // eliminate double slash + if(!preg_match("|^/|",$matches[2])) + $this->_redirectaddr .= "/".$matches[2]; + else + $this->_redirectaddr .= $matches[2]; + } + else + $this->_redirectaddr = $matches[2]; + } + + if(preg_match("|^HTTP/|",$currentHeader)) + { + if(preg_match("|^HTTP/[^\s]*\s(.*?)\s|",$currentHeader, $status)) + { + $this->status= $status[1]; + } + $this->response_code = $currentHeader; + } + + $this->headers[] = $currentHeader; + } + + $results = ''; + do { + $_data = fread($fp, $this->maxlength); + if (strlen($_data) == 0) { + break; + } + $results .= $_data; + } while(true); + + if ($this->read_timeout > 0 && $this->_check_timeout($fp)) + { + $this->status=-100; + return false; + } + + // check if there is a redirect meta tag + + if(preg_match("'<meta[\s]*http-equiv[^>]*?content[\s]*=[\s]*[\"\']?\d+;[\s]*URL[\s]*=[\s]*([^\"\']*?)[\"\']?>'i",$results,$match)) + + { + $this->_redirectaddr = $this->_expandlinks($match[1],$URI); + } + + // have we hit our frame depth and is there frame src to fetch? + if(($this->_framedepth < $this->maxframes) && preg_match_all("'<frame\s+.*src[\s]*=[\'\"]?([^\'\"\>]+)'i",$results,$match)) + { + $this->results[] = $results; + for($x=0; $x<count($match[1]); $x++) + $this->_frameurls[] = $this->_expandlinks($match[1][$x],$URI_PARTS["scheme"]."://".$this->host); + } + // have we already fetched framed content? + elseif(is_array($this->results)) + $this->results[] = $results; + // no framed content + else + $this->results = $results; + + return true; + } + +/*======================================================================*\ + Function: _httpsrequest + Purpose: go get the https data from the server using curl + Input: $url the url to fetch + $URI the full URI + $body body contents to send if any (POST) + Output: +\*======================================================================*/ + + function _httpsrequest($url,$URI,$http_method,$content_type="",$body="") + { + if($this->passcookies && $this->_redirectaddr) + $this->setcookies(); + + $headers = array(); + + $URI_PARTS = parse_url($URI); + if(empty($url)) + $url = "/"; + // GET ... header not needed for curl + //$headers[] = $http_method." ".$url." ".$this->_httpversion; + if(!empty($this->agent)) + $headers[] = "User-Agent: ".$this->agent; + if(!empty($this->host)) + if(!empty($this->port)) + $headers[] = "Host: ".$this->host.":".$this->port; + else + $headers[] = "Host: ".$this->host; + if(!empty($this->accept)) + $headers[] = "Accept: ".$this->accept; + if(!empty($this->referer)) + $headers[] = "Referer: ".$this->referer; + if(!empty($this->cookies)) + { + if(!is_array($this->cookies)) + $this->cookies = (array)$this->cookies; + + reset($this->cookies); + if ( count($this->cookies) > 0 ) { + $cookie_str = 'Cookie: '; + foreach ( $this->cookies as $cookieKey => $cookieVal ) { + $cookie_str .= $cookieKey."=".urlencode($cookieVal)."; "; + } + $headers[] = substr($cookie_str,0,-2); + } + } + if(!empty($this->rawheaders)) + { + if(!is_array($this->rawheaders)) + $this->rawheaders = (array)$this->rawheaders; + while(list($headerKey,$headerVal) = each($this->rawheaders)) + $headers[] = $headerKey.": ".$headerVal; + } + if(!empty($content_type)) { + if ($content_type == "multipart/form-data") + $headers[] = "Content-type: $content_type; boundary=".$this->_mime_boundary; + else + $headers[] = "Content-type: $content_type"; + } + if(!empty($body)) + $headers[] = "Content-length: ".strlen($body); + if(!empty($this->user) || !empty($this->pass)) + $headers[] = "Authorization: BASIC ".base64_encode($this->user.":".$this->pass); + + for($curr_header = 0; $curr_header < count($headers); $curr_header++) { + $safer_header = strtr( $headers[$curr_header], "\"", " " ); + $cmdline_params .= " -H \"".$safer_header."\""; + } + + if(!empty($body)) + $cmdline_params .= " -d \"$body\""; + + if($this->read_timeout > 0) + $cmdline_params .= " -m ".$this->read_timeout; + + $headerfile = tempnam($this->temp_dir, "sno"); + + exec($this->curl_path." -k -D \"$headerfile\"".$cmdline_params." \"".escapeshellcmd($URI)."\"",$results,$return); + + if($return) + { + $this->error = "Error: cURL could not retrieve the document, error $return."; + return false; + } + + + $results = implode("\r\n",$results); + + $result_headers = file("$headerfile"); + + $this->_redirectaddr = false; + unset($this->headers); + + for($currentHeader = 0; $currentHeader < count($result_headers); $currentHeader++) + { + + // if a header begins with Location: or URI:, set the redirect + if(preg_match("/^(Location: |URI: )/i",$result_headers[$currentHeader])) + { + // get URL portion of the redirect + preg_match("/^(Location: |URI:)\s+(.*)/",chop($result_headers[$currentHeader]),$matches); + // look for :// in the Location header to see if hostname is included + if(!preg_match("|\:\/\/|",$matches[2])) + { + // no host in the path, so prepend + $this->_redirectaddr = $URI_PARTS["scheme"]."://".$this->host.":".$this->port; + // eliminate double slash + if(!preg_match("|^/|",$matches[2])) + $this->_redirectaddr .= "/".$matches[2]; + else + $this->_redirectaddr .= $matches[2]; + } + else + $this->_redirectaddr = $matches[2]; + } + + if(preg_match("|^HTTP/|",$result_headers[$currentHeader])) + $this->response_code = $result_headers[$currentHeader]; + + $this->headers[] = $result_headers[$currentHeader]; + } + + // check if there is a redirect meta tag + + if(preg_match("'<meta[\s]*http-equiv[^>]*?content[\s]*=[\s]*[\"\']?\d+;[\s]*URL[\s]*=[\s]*([^\"\']*?)[\"\']?>'i",$results,$match)) + { + $this->_redirectaddr = $this->_expandlinks($match[1],$URI); + } + + // have we hit our frame depth and is there frame src to fetch? + if(($this->_framedepth < $this->maxframes) && preg_match_all("'<frame\s+.*src[\s]*=[\'\"]?([^\'\"\>]+)'i",$results,$match)) + { + $this->results[] = $results; + for($x=0; $x<count($match[1]); $x++) + $this->_frameurls[] = $this->_expandlinks($match[1][$x],$URI_PARTS["scheme"]."://".$this->host); + } + // have we already fetched framed content? + elseif(is_array($this->results)) + $this->results[] = $results; + // no framed content + else + $this->results = $results; + + unlink("$headerfile"); + + return true; + } + +/*======================================================================*\ + Function: setcookies() + Purpose: set cookies for a redirection +\*======================================================================*/ + + function setcookies() + { + for($x=0; $x<count($this->headers); $x++) + { + if(preg_match('/^set-cookie:[\s]+([^=]+)=([^;]+)/i', $this->headers[$x],$match)) + $this->cookies[$match[1]] = urldecode($match[2]); + } + } + + +/*======================================================================*\ + Function: _check_timeout + Purpose: checks whether timeout has occurred + Input: $fp file pointer +\*======================================================================*/ + + function _check_timeout($fp) + { + if ($this->read_timeout > 0) { + $fp_status = socket_get_status($fp); + if ($fp_status["timed_out"]) { + $this->timed_out = true; + return true; + } + } + return false; + } + +/*======================================================================*\ + Function: _connect + Purpose: make a socket connection + Input: $fp file pointer +\*======================================================================*/ + + function _connect(&$fp) + { + if(!empty($this->proxy_host) && !empty($this->proxy_port)) + { + $this->_isproxy = true; + + $host = $this->proxy_host; + $port = $this->proxy_port; + } + else + { + $host = $this->host; + $port = $this->port; + } + + $this->status = 0; + + if($fp = fsockopen( + $host, + $port, + $errno, + $errstr, + $this->_fp_timeout + )) + { + // socket connection succeeded + + return true; + } + else + { + // socket connection failed + $this->status = $errno; + switch($errno) + { + case -3: + $this->error="socket creation failed (-3)"; + case -4: + $this->error="dns lookup failure (-4)"; + case -5: + $this->error="connection refused or timed out (-5)"; + default: + $this->error="connection failed (".$errno.")"; + } + return false; + } + } +/*======================================================================*\ + Function: _disconnect + Purpose: disconnect a socket connection + Input: $fp file pointer +\*======================================================================*/ + + function _disconnect($fp) + { + return(fclose($fp)); + } + + +/*======================================================================*\ + Function: _prepare_post_body + Purpose: Prepare post body according to encoding type + Input: $formvars - form variables + $formfiles - form upload files + Output: post body +\*======================================================================*/ + + function _prepare_post_body($formvars, $formfiles) + { + settype($formvars, "array"); + settype($formfiles, "array"); + $postdata = ''; + + if (count($formvars) == 0 && count($formfiles) == 0) + return; + + switch ($this->_submit_type) { + case "application/x-www-form-urlencoded": + reset($formvars); + while(list($key,$val) = each($formvars)) { + if (is_array($val) || is_object($val)) { + while (list($cur_key, $cur_val) = each($val)) { + $postdata .= urlencode($key)."[]=".urlencode($cur_val)."&"; + } + } else + $postdata .= urlencode($key)."=".urlencode($val)."&"; + } + break; + + case "multipart/form-data": + $this->_mime_boundary = "Snoopy".md5(uniqid(microtime())); + + reset($formvars); + while(list($key,$val) = each($formvars)) { + if (is_array($val) || is_object($val)) { + while (list($cur_key, $cur_val) = each($val)) { + $postdata .= "--".$this->_mime_boundary."\r\n"; + $postdata .= "Content-Disposition: form-data; name=\"$key\[\]\"\r\n\r\n"; + $postdata .= "$cur_val\r\n"; + } + } else { + $postdata .= "--".$this->_mime_boundary."\r\n"; + $postdata .= "Content-Disposition: form-data; name=\"$key\"\r\n\r\n"; + $postdata .= "$val\r\n"; + } + } + + reset($formfiles); + while (list($field_name, $file_names) = each($formfiles)) { + settype($file_names, "array"); + while (list(, $file_name) = each($file_names)) { + if (!is_readable($file_name)) continue; + + $fp = fopen($file_name, "r"); + $file_content = fread($fp, filesize($file_name)); + fclose($fp); + $base_name = basename($file_name); + + $postdata .= "--".$this->_mime_boundary."\r\n"; + $postdata .= "Content-Disposition: form-data; name=\"$field_name\"; filename=\"$base_name\"\r\n\r\n"; + $postdata .= "$file_content\r\n"; + } + } + $postdata .= "--".$this->_mime_boundary."--\r\n"; + break; + } + + return $postdata; + } +} +endif; +?> diff --git a/wp-includes/class-wp-admin-bar.php b/wp-includes/class-wp-admin-bar.php new file mode 100644 index 0000000..749e48d --- /dev/null +++ b/wp-includes/class-wp-admin-bar.php @@ -0,0 +1,517 @@ +<?php +/** + * The WordPress Toolbar + * + * @since 3.1.0 + * + * @package WordPress + * @subpackage Toolbar + */ +class WP_Admin_Bar { + private $nodes = array(); + private $bound = false; + public $user; + + public function __get( $name ) { + switch ( $name ) { + case 'proto' : + return is_ssl() ? 'https://' : 'http://'; + + case 'menu' : + _deprecated_argument( 'WP_Admin_Bar', '3.3', 'Modify admin bar nodes with WP_Admin_Bar::get_node(), WP_Admin_Bar::add_node(), and WP_Admin_Bar::remove_node(), not the <code>menu</code> property.' ); + return array(); // Sorry, folks. + } + } + + public function initialize() { + $this->user = new stdClass; + + if ( is_user_logged_in() ) { + /* Populate settings we need for the menu based on the current user. */ + $this->user->blogs = get_blogs_of_user( get_current_user_id() ); + if ( is_multisite() ) { + $this->user->active_blog = get_active_blog_for_user( get_current_user_id() ); + $this->user->domain = empty( $this->user->active_blog ) ? user_admin_url() : trailingslashit( get_home_url( $this->user->active_blog->blog_id ) ); + $this->user->account_domain = $this->user->domain; + } else { + $this->user->active_blog = $this->user->blogs[get_current_blog_id()]; + $this->user->domain = trailingslashit( home_url() ); + $this->user->account_domain = $this->user->domain; + } + } + + add_action( 'wp_head', 'wp_admin_bar_header' ); + + add_action( 'admin_head', 'wp_admin_bar_header' ); + + if ( current_theme_supports( 'admin-bar' ) ) { + /** + * To remove the default padding styles from WordPress for the Toolbar, use the following code: + * add_theme_support( 'admin-bar', array( 'callback' => '__return_false' ) ); + */ + $admin_bar_args = get_theme_support( 'admin-bar' ); + $header_callback = $admin_bar_args[0]['callback']; + } + + if ( empty($header_callback) ) + $header_callback = '_admin_bar_bump_cb'; + + add_action('wp_head', $header_callback); + + wp_enqueue_script( 'admin-bar' ); + wp_enqueue_style( 'admin-bar' ); + + /** + * Fires after WP_Admin_Bar is initialized. + * + * @since 3.1.0 + */ + do_action( 'admin_bar_init' ); + } + + public function add_menu( $node ) { + $this->add_node( $node ); + } + + public function remove_menu( $id ) { + $this->remove_node( $id ); + } + + /** + * Add a node to the menu. + * + * @param array $args { + * Arguments for adding a node. + * + * @type string $id ID of the item. + * @type string $title Title of the node. + * @type string $parent Optional. ID of the parent node. + * @type string $href Optional. Link for the item. + * @type bool $group Optional. Whether or not the node is a group. Default false. + * @type array $meta Meta data including the following keys: 'html', 'class', 'rel', + * 'onclick', 'target', 'title', 'tabindex'. Default empty. + * } + */ + public function add_node( $args ) { + // Shim for old method signature: add_node( $parent_id, $menu_obj, $args ) + if ( func_num_args() >= 3 && is_string( func_get_arg(0) ) ) + $args = array_merge( array( 'parent' => func_get_arg(0) ), func_get_arg(2) ); + + if ( is_object( $args ) ) + $args = get_object_vars( $args ); + + // Ensure we have a valid title. + if ( empty( $args['id'] ) ) { + if ( empty( $args['title'] ) ) + return; + + _doing_it_wrong( __METHOD__, __( 'The menu ID should not be empty.' ), '3.3' ); + // Deprecated: Generate an ID from the title. + $args['id'] = esc_attr( sanitize_title( trim( $args['title'] ) ) ); + } + + $defaults = array( + 'id' => false, + 'title' => false, + 'parent' => false, + 'href' => false, + 'group' => false, + 'meta' => array(), + ); + + // If the node already exists, keep any data that isn't provided. + if ( $maybe_defaults = $this->get_node( $args['id'] ) ) + $defaults = get_object_vars( $maybe_defaults ); + + // Do the same for 'meta' items. + if ( ! empty( $defaults['meta'] ) && ! empty( $args['meta'] ) ) + $args['meta'] = wp_parse_args( $args['meta'], $defaults['meta'] ); + + $args = wp_parse_args( $args, $defaults ); + + $back_compat_parents = array( + 'my-account-with-avatar' => array( 'my-account', '3.3' ), + 'my-blogs' => array( 'my-sites', '3.3' ), + ); + + if ( isset( $back_compat_parents[ $args['parent'] ] ) ) { + list( $new_parent, $version ) = $back_compat_parents[ $args['parent'] ]; + _deprecated_argument( __METHOD__, $version, sprintf( 'Use <code>%s</code> as the parent for the <code>%s</code> admin bar node instead of <code>%s</code>.', $new_parent, $args['id'], $args['parent'] ) ); + $args['parent'] = $new_parent; + } + + $this->_set_node( $args ); + } + + final protected function _set_node( $args ) { + $this->nodes[ $args['id'] ] = (object) $args; + } + + /** + * Gets a node. + * + * @return object Node. + */ + final public function get_node( $id ) { + if ( $node = $this->_get_node( $id ) ) + return clone $node; + } + + final protected function _get_node( $id ) { + if ( $this->bound ) + return; + + if ( empty( $id ) ) + $id = 'root'; + + if ( isset( $this->nodes[ $id ] ) ) + return $this->nodes[ $id ]; + } + + final public function get_nodes() { + if ( ! $nodes = $this->_get_nodes() ) + return; + + foreach ( $nodes as &$node ) { + $node = clone $node; + } + return $nodes; + } + + final protected function _get_nodes() { + if ( $this->bound ) + return; + + return $this->nodes; + } + + /** + * Add a group to a menu node. + * + * @since 3.3.0 + * + * @param array $args { + * Array of arguments for adding a group. + * + * @type string $id ID of the item. + * @type string $parent Optional. ID of the parent node. Default 'root'. + * @type array $meta Meta data for the group including the following keys: + * 'class', 'onclick', 'target', and 'title'. + * } + */ + final public function add_group( $args ) { + $args['group'] = true; + + $this->add_node( $args ); + } + + /** + * Remove a node. + * + * @param string The ID of the item. + */ + public function remove_node( $id ) { + $this->_unset_node( $id ); + } + + final protected function _unset_node( $id ) { + unset( $this->nodes[ $id ] ); + } + + public function render() { + $root = $this->_bind(); + if ( $root ) + $this->_render( $root ); + } + + final protected function _bind() { + if ( $this->bound ) + return; + + // Add the root node. + // Clear it first, just in case. Don't mess with The Root. + $this->remove_node( 'root' ); + $this->add_node( array( + 'id' => 'root', + 'group' => false, + ) ); + + // Normalize nodes: define internal 'children' and 'type' properties. + foreach ( $this->_get_nodes() as $node ) { + $node->children = array(); + $node->type = ( $node->group ) ? 'group' : 'item'; + unset( $node->group ); + + // The Root wants your orphans. No lonely items allowed. + if ( ! $node->parent ) + $node->parent = 'root'; + } + + foreach ( $this->_get_nodes() as $node ) { + if ( 'root' == $node->id ) + continue; + + // Fetch the parent node. If it isn't registered, ignore the node. + if ( ! $parent = $this->_get_node( $node->parent ) ) { + continue; + } + + // Generate the group class (we distinguish between top level and other level groups). + $group_class = ( $node->parent == 'root' ) ? 'ab-top-menu' : 'ab-submenu'; + + if ( $node->type == 'group' ) { + if ( empty( $node->meta['class'] ) ) + $node->meta['class'] = $group_class; + else + $node->meta['class'] .= ' ' . $group_class; + } + + // Items in items aren't allowed. Wrap nested items in 'default' groups. + if ( $parent->type == 'item' && $node->type == 'item' ) { + $default_id = $parent->id . '-default'; + $default = $this->_get_node( $default_id ); + + // The default group is added here to allow groups that are + // added before standard menu items to render first. + if ( ! $default ) { + // Use _set_node because add_node can be overloaded. + // Make sure to specify default settings for all properties. + $this->_set_node( array( + 'id' => $default_id, + 'parent' => $parent->id, + 'type' => 'group', + 'children' => array(), + 'meta' => array( + 'class' => $group_class, + ), + 'title' => false, + 'href' => false, + ) ); + $default = $this->_get_node( $default_id ); + $parent->children[] = $default; + } + $parent = $default; + + // Groups in groups aren't allowed. Add a special 'container' node. + // The container will invisibly wrap both groups. + } elseif ( $parent->type == 'group' && $node->type == 'group' ) { + $container_id = $parent->id . '-container'; + $container = $this->_get_node( $container_id ); + + // We need to create a container for this group, life is sad. + if ( ! $container ) { + // Use _set_node because add_node can be overloaded. + // Make sure to specify default settings for all properties. + $this->_set_node( array( + 'id' => $container_id, + 'type' => 'container', + 'children' => array( $parent ), + 'parent' => false, + 'title' => false, + 'href' => false, + 'meta' => array(), + ) ); + + $container = $this->_get_node( $container_id ); + + // Link the container node if a grandparent node exists. + $grandparent = $this->_get_node( $parent->parent ); + + if ( $grandparent ) { + $container->parent = $grandparent->id; + + $index = array_search( $parent, $grandparent->children, true ); + if ( $index === false ) + $grandparent->children[] = $container; + else + array_splice( $grandparent->children, $index, 1, array( $container ) ); + } + + $parent->parent = $container->id; + } + + $parent = $container; + } + + // Update the parent ID (it might have changed). + $node->parent = $parent->id; + + // Add the node to the tree. + $parent->children[] = $node; + } + + $root = $this->_get_node( 'root' ); + $this->bound = true; + return $root; + } + + final protected function _render( $root ) { + global $is_IE; + + // Add browser classes. + // We have to do this here since admin bar shows on the front end. + $class = 'nojq nojs'; + if ( $is_IE ) { + if ( strpos( $_SERVER['HTTP_USER_AGENT'], 'MSIE 7' ) ) + $class .= ' ie7'; + elseif ( strpos( $_SERVER['HTTP_USER_AGENT'], 'MSIE 8' ) ) + $class .= ' ie8'; + elseif ( strpos( $_SERVER['HTTP_USER_AGENT'], 'MSIE 9' ) ) + $class .= ' ie9'; + } elseif ( wp_is_mobile() ) { + $class .= ' mobile'; + } + + ?> + <div id="wpadminbar" class="<?php echo $class; ?>" role="navigation"> + <a class="screen-reader-shortcut" href="#wp-toolbar" tabindex="1"><?php _e('Skip to toolbar'); ?></a> + <div class="quicklinks" id="wp-toolbar" role="navigation" aria-label="<?php esc_attr_e('Top navigation toolbar.'); ?>" tabindex="0"> + <?php foreach ( $root->children as $group ) { + $this->_render_group( $group ); + } ?> + </div> + <?php if ( is_user_logged_in() ) : ?> + <a class="screen-reader-shortcut" href="<?php echo esc_url( wp_logout_url() ); ?>"><?php _e('Log Out'); ?></a> + <?php endif; ?> + </div> + + <?php + } + + final protected function _render_container( $node ) { + if ( $node->type != 'container' || empty( $node->children ) ) + return; + + ?><div id="<?php echo esc_attr( 'wp-admin-bar-' . $node->id ); ?>" class="ab-group-container"><?php + foreach ( $node->children as $group ) { + $this->_render_group( $group ); + } + ?></div><?php + } + + final protected function _render_group( $node ) { + if ( $node->type == 'container' ) + return $this->_render_container( $node ); + + if ( $node->type != 'group' || empty( $node->children ) ) + return; + + if ( ! empty( $node->meta['class'] ) ) + $class = ' class="' . esc_attr( trim( $node->meta['class'] ) ) . '"'; + else + $class = ''; + + ?><ul id="<?php echo esc_attr( 'wp-admin-bar-' . $node->id ); ?>"<?php echo $class; ?>><?php + foreach ( $node->children as $item ) { + $this->_render_item( $item ); + } + ?></ul><?php + } + + final protected function _render_item( $node ) { + if ( $node->type != 'item' ) + return; + + $is_parent = ! empty( $node->children ); + $has_link = ! empty( $node->href ); + + $tabindex = isset( $node->meta['tabindex'] ) ? (int) $node->meta['tabindex'] : ''; + $aria_attributes = $tabindex ? 'tabindex="' . $tabindex . '"' : ''; + + $menuclass = ''; + + if ( $is_parent ) { + $menuclass = 'menupop '; + $aria_attributes .= ' aria-haspopup="true"'; + } + + if ( ! empty( $node->meta['class'] ) ) + $menuclass .= $node->meta['class']; + + if ( $menuclass ) + $menuclass = ' class="' . esc_attr( trim( $menuclass ) ) . '"'; + + ?> + + <li id="<?php echo esc_attr( 'wp-admin-bar-' . $node->id ); ?>"<?php echo $menuclass; ?>><?php + if ( $has_link ): + ?><a class="ab-item" <?php echo $aria_attributes; ?> href="<?php echo esc_url( $node->href ) ?>"<?php + if ( ! empty( $node->meta['onclick'] ) ) : + ?> onclick="<?php echo esc_js( $node->meta['onclick'] ); ?>"<?php + endif; + if ( ! empty( $node->meta['target'] ) ) : + ?> target="<?php echo esc_attr( $node->meta['target'] ); ?>"<?php + endif; + if ( ! empty( $node->meta['title'] ) ) : + ?> title="<?php echo esc_attr( $node->meta['title'] ); ?>"<?php + endif; + if ( ! empty( $node->meta['rel'] ) ) : + ?> rel="<?php echo esc_attr( $node->meta['rel'] ); ?>"<?php + endif; + ?>><?php + else: + ?><div class="ab-item ab-empty-item" <?php echo $aria_attributes; + if ( ! empty( $node->meta['title'] ) ) : + ?> title="<?php echo esc_attr( $node->meta['title'] ); ?>"<?php + endif; + ?>><?php + endif; + + echo $node->title; + + if ( $has_link ) : + ?></a><?php + else: + ?></div><?php + endif; + + if ( $is_parent ) : + ?><div class="ab-sub-wrapper"><?php + foreach ( $node->children as $group ) { + $this->_render_group( $group ); + } + ?></div><?php + endif; + + if ( ! empty( $node->meta['html'] ) ) + echo $node->meta['html']; + + ?> + </li><?php + } + + public function recursive_render( $id, $node ) { + _deprecated_function( __METHOD__, '3.3', 'WP_Admin_bar::render(), WP_Admin_Bar::_render_item()' ); + $this->_render_item( $node ); + } + + public function add_menus() { + // User related, aligned right. + add_action( 'admin_bar_menu', 'wp_admin_bar_my_account_menu', 0 ); + add_action( 'admin_bar_menu', 'wp_admin_bar_search_menu', 4 ); + add_action( 'admin_bar_menu', 'wp_admin_bar_my_account_item', 7 ); + + // Site related. + add_action( 'admin_bar_menu', 'wp_admin_bar_sidebar_toggle', 0 ); + add_action( 'admin_bar_menu', 'wp_admin_bar_wp_menu', 10 ); + add_action( 'admin_bar_menu', 'wp_admin_bar_my_sites_menu', 20 ); + add_action( 'admin_bar_menu', 'wp_admin_bar_site_menu', 30 ); + add_action( 'admin_bar_menu', 'wp_admin_bar_updates_menu', 40 ); + + // Content related. + if ( ! is_network_admin() && ! is_user_admin() ) { + add_action( 'admin_bar_menu', 'wp_admin_bar_comments_menu', 60 ); + add_action( 'admin_bar_menu', 'wp_admin_bar_new_content_menu', 70 ); + } + add_action( 'admin_bar_menu', 'wp_admin_bar_edit_menu', 80 ); + + add_action( 'admin_bar_menu', 'wp_admin_bar_add_secondary_groups', 200 ); + + /** + * Fires after menus are added to the menu bar. + * + * @since 3.1.0 + */ + do_action( 'add_admin_bar_menus' ); + } +} diff --git a/wp-includes/class-wp-ajax-response.php b/wp-includes/class-wp-ajax-response.php new file mode 100644 index 0000000..7bed81c --- /dev/null +++ b/wp-includes/class-wp-ajax-response.php @@ -0,0 +1,199 @@ +<?php +/** + * Send XML response back to AJAX request. + * + * @package WordPress + * @since 2.1.0 + */ +class WP_Ajax_Response { + /** + * Store XML responses to send. + * + * @since 2.1.0 + * @var array + * @access private + */ + private $responses = array(); + + /** + * Constructor - Passes args to {@link WP_Ajax_Response::add()}. + * + * @since 2.1.0 + * @see WP_Ajax_Response::add() + * + * @param string|array $args Optional. Will be passed to add() method. + * @return WP_Ajax_Response + */ + public function __construct( $args = '' ) { + if ( !empty($args) ) + $this->add($args); + } + + /** + * Make private properties readable for backwards compatibility. + * + * @since 4.0.0 + * @access public + * + * @param string $name Property to get. + * @return mixed Property. + */ + public function __get( $name ) { + return $this->$name; + } + + /** + * Make private properties settable for backwards compatibility. + * + * @since 4.0.0 + * @access public + * + * @param string $name Property to set. + * @param mixed $value Property value. + * @return mixed Newly-set property. + */ + public function __set( $name, $value ) { + return $this->$name = $value; + } + + /** + * Make private properties checkable for backwards compatibility. + * + * @since 4.0.0 + * @access public + * + * @param string $name Property to check if set. + * @return bool Whether the property is set. + */ + public function __isset( $name ) { + return isset( $this->$name ); + } + + /** + * Make private properties un-settable for backwards compatibility. + * + * @since 4.0.0 + * @access public + * + * @param string $name Property to unset. + */ + public function __unset( $name ) { + unset( $this->$name ); + } + + /** + * Append to XML response based on given arguments. + * + * The arguments that can be passed in the $args parameter are below. It is + * also possible to pass a WP_Error object in either the 'id' or 'data' + * argument. The parameter isn't actually optional, content should be given + * in order to send the correct response. + * + * 'what' argument is a string that is the XMLRPC response type. + * 'action' argument is a boolean or string that acts like a nonce. + * 'id' argument can be WP_Error or an integer. + * 'old_id' argument is false by default or an integer of the previous ID. + * 'position' argument is an integer or a string with -1 = top, 1 = bottom, + * html ID = after, -html ID = before. + * 'data' argument is a string with the content or message. + * 'supplemental' argument is an array of strings that will be children of + * the supplemental element. + * + * @since 2.1.0 + * + * @param string|array $args Override defaults. + * @return string XML response. + */ + public function add( $args = '' ) { + $defaults = array( + 'what' => 'object', 'action' => false, + 'id' => '0', 'old_id' => false, + 'position' => 1, + 'data' => '', 'supplemental' => array() + ); + + $r = wp_parse_args( $args, $defaults ); + + $position = preg_replace( '/[^a-z0-9:_-]/i', '', $r['position'] ); + $id = $r['id']; + $what = $r['what']; + $action = $r['action']; + $old_id = $r['old_id']; + $data = $r['data']; + + if ( is_wp_error( $id ) ) { + $data = $id; + $id = 0; + } + + $response = ''; + if ( is_wp_error( $data ) ) { + foreach ( (array) $data->get_error_codes() as $code ) { + $response .= "<wp_error code='$code'><![CDATA[" . $data->get_error_message( $code ) . "]]></wp_error>"; + if ( ! $error_data = $data->get_error_data( $code ) ) { + continue; + } + $class = ''; + if ( is_object( $error_data ) ) { + $class = ' class="' . get_class( $error_data ) . '"'; + $error_data = get_object_vars( $error_data ); + } + + $response .= "<wp_error_data code='$code'$class>"; + + if ( is_scalar( $error_data ) ) { + $response .= "<![CDATA[$error_data]]>"; + } elseif ( is_array( $error_data ) ) { + foreach ( $error_data as $k => $v ) { + $response .= "<$k><![CDATA[$v]]></$k>"; + } + } + + $response .= "</wp_error_data>"; + } + } else { + $response = "<response_data><![CDATA[$data]]></response_data>"; + } + + $s = ''; + if ( is_array( $r['supplemental'] ) ) { + foreach ( $r['supplemental'] as $k => $v ) { + $s .= "<$k><![CDATA[$v]]></$k>"; + } + $s = "<supplemental>$s</supplemental>"; + } + + if ( false === $action ) { + $action = $_POST['action']; + } + $x = ''; + $x .= "<response action='{$action}_$id'>"; // The action attribute in the xml output is formatted like a nonce action + $x .= "<$what id='$id' " . ( false === $old_id ? '' : "old_id='$old_id' " ) . "position='$position'>"; + $x .= $response; + $x .= $s; + $x .= "</$what>"; + $x .= "</response>"; + + $this->responses[] = $x; + return $x; + } + + /** + * Display XML formatted responses. + * + * Sets the content type header to text/xml. + * + * @since 2.1.0 + */ + public function send() { + header( 'Content-Type: text/xml; charset=' . get_option( 'blog_charset' ) ); + echo "<?xml version='1.0' encoding='" . get_option( 'blog_charset' ) . "' standalone='yes'?><wp_ajax>"; + foreach ( (array) $this->responses as $response ) + echo $response; + echo '</wp_ajax>'; + if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) + wp_die(); + else + die(); + } +} diff --git a/wp-includes/class-wp-customize-control.php b/wp-includes/class-wp-customize-control.php new file mode 100644 index 0000000..950b0d5 --- /dev/null +++ b/wp-includes/class-wp-customize-control.php @@ -0,0 +1,1192 @@ +<?php +/** + * Customize Control Class + * + * @package WordPress + * @subpackage Customize + * @since 3.4.0 + */ +class WP_Customize_Control { + + /** + * Incremented with each new class instantiation, then stored in $instance_number. + * + * Used when sorting two instances whose priorities are equal. + * + * @since 4.1.0 + * @access protected + * @var int + */ + protected static $instance_count = 0; + + /** + * Order in which this instance was created in relation to other instances. + * + * @since 4.1.0 + * @access public + * @var int + */ + public $instance_number; + + /** + * @access public + * @var WP_Customize_Manager + */ + public $manager; + + /** + * @access public + * @var string + */ + public $id; + + /** + * All settings tied to the control. + * + * @access public + * @var array + */ + public $settings; + + /** + * The primary setting for the control (if there is one). + * + * @access public + * @var string + */ + public $setting = 'default'; + + /** + * @access public + * @var int + */ + public $priority = 10; + + /** + * @access public + * @var string + */ + public $section = ''; + + /** + * @access public + * @var string + */ + public $label = ''; + + /** + * @access public + * @var string + */ + public $description = ''; + + /** + * @todo: Remove choices + * + * @access public + * @var array + */ + public $choices = array(); + + /** + * @access public + * @var array + */ + public $input_attrs = array(); + + /** + * @deprecated It is better to just call the json() method + * @access public + * @var array + */ + public $json = array(); + + /** + * @access public + * @var string + */ + public $type = 'text'; + + /** + * Callback. + * + * @since 4.0.0 + * @access public + * + * @see WP_Customize_Control::active() + * + * @var callable Callback is called with one argument, the instance of + * WP_Customize_Control, and returns bool to indicate whether + * the control is active (such as it relates to the URL + * currently being previewed). + */ + public $active_callback = ''; + + /** + * Constructor. + * + * Supplied $args override class property defaults. + * + * If $args['settings'] is not defined, use the $id as the setting ID. + * + * @since 3.4.0 + * + * @param WP_Customize_Manager $manager + * @param string $id + * @param array $args + */ + public function __construct( $manager, $id, $args = array() ) { + $keys = array_keys( get_object_vars( $this ) ); + foreach ( $keys as $key ) { + if ( isset( $args[ $key ] ) ) { + $this->$key = $args[ $key ]; + } + } + + $this->manager = $manager; + $this->id = $id; + if ( empty( $this->active_callback ) ) { + $this->active_callback = array( $this, 'active_callback' ); + } + self::$instance_count += 1; + $this->instance_number = self::$instance_count; + + // Process settings. + if ( empty( $this->settings ) ) { + $this->settings = $id; + } + + $settings = array(); + if ( is_array( $this->settings ) ) { + foreach ( $this->settings as $key => $setting ) { + $settings[ $key ] = $this->manager->get_setting( $setting ); + } + } else { + $this->setting = $this->manager->get_setting( $this->settings ); + $settings['default'] = $this->setting; + } + $this->settings = $settings; + } + + /** + * Enqueue control related scripts/styles. + * + * @since 3.4.0 + */ + public function enqueue() {} + + /** + * Check whether control is active to current Customizer preview. + * + * @since 4.0.0 + * @access public + * + * @return bool Whether the control is active to the current preview. + */ + public final function active() { + $control = $this; + $active = call_user_func( $this->active_callback, $this ); + + /** + * Filter response of WP_Customize_Control::active(). + * + * @since 4.0.0 + * + * @param bool $active Whether the Customizer control is active. + * @param WP_Customize_Control $control WP_Customize_Control instance. + */ + $active = apply_filters( 'customize_control_active', $active, $control ); + + return $active; + } + + /** + * Default callback used when invoking WP_Customize_Control::active(). + * + * Subclasses can override this with their specific logic, or they may + * provide an 'active_callback' argument to the constructor. + * + * @since 4.0.0 + * @access public + * + * @return bool Always true. + */ + public function active_callback() { + return true; + } + + /** + * Fetch a setting's value. + * Grabs the main setting by default. + * + * @since 3.4.0 + * + * @param string $setting_key + * @return mixed The requested setting's value, if the setting exists. + */ + public final function value( $setting_key = 'default' ) { + if ( isset( $this->settings[ $setting_key ] ) ) { + return $this->settings[ $setting_key ]->value(); + } + } + + /** + * Refresh the parameters passed to the JavaScript via JSON. + * + * @since 3.4.0 + */ + public function to_json() { + $this->json['settings'] = array(); + foreach ( $this->settings as $key => $setting ) { + $this->json['settings'][ $key ] = $setting->id; + } + + $this->json['type'] = $this->type; + $this->json['priority'] = $this->priority; + $this->json['active'] = $this->active(); + $this->json['section'] = $this->section; + $this->json['content'] = $this->get_content(); + $this->json['label'] = $this->label; + $this->json['description'] = $this->description; + $this->json['instanceNumber'] = $this->instance_number; + } + + /** + * Get the data to export to the client via JSON. + * + * @since 4.1.0 + * + * @return array Array of parameters passed to the JavaScript. + */ + public function json() { + $this->to_json(); + return $this->json; + } + + /** + * Check if the theme supports the control and check user capabilities. + * + * @since 3.4.0 + * + * @return bool False if theme doesn't support the control or user doesn't have the required permissions, otherwise true. + */ + public final function check_capabilities() { + foreach ( $this->settings as $setting ) { + if ( ! $setting->check_capabilities() ) + return false; + } + + $section = $this->manager->get_section( $this->section ); + if ( isset( $section ) && ! $section->check_capabilities() ) + return false; + + return true; + } + + /** + * Get the control's content for insertion into the Customizer pane. + * + * @since 4.1.0 + * + * @return string Contents of the control. + */ + public final function get_content() { + ob_start(); + $this->maybe_render(); + $template = trim( ob_get_contents() ); + ob_end_clean(); + return $template; + } + + /** + * Check capabilities and render the control. + * + * @since 3.4.0 + * @uses WP_Customize_Control::render() + */ + public final function maybe_render() { + if ( ! $this->check_capabilities() ) + return; + + /** + * Fires just before the current Customizer control is rendered. + * + * @since 3.4.0 + * + * @param WP_Customize_Control $this WP_Customize_Control instance. + */ + do_action( 'customize_render_control', $this ); + + /** + * Fires just before a specific Customizer control is rendered. + * + * The dynamic portion of the hook name, `$this->id`, refers to + * the control ID. + * + * @since 3.4.0 + * + * @param WP_Customize_Control $this {@see WP_Customize_Control} instance. + */ + do_action( 'customize_render_control_' . $this->id, $this ); + + $this->render(); + } + + /** + * Renders the control wrapper and calls $this->render_content() for the internals. + * + * @since 3.4.0 + */ + protected function render() { + $id = 'customize-control-' . str_replace( '[', '-', str_replace( ']', '', $this->id ) ); + $class = 'customize-control customize-control-' . $this->type; + + ?><li id="<?php echo esc_attr( $id ); ?>" class="<?php echo esc_attr( $class ); ?>"> + <?php $this->render_content(); ?> + </li><?php + } + + /** + * Get the data link attribute for a setting. + * + * @since 3.4.0 + * + * @param string $setting_key + * @return string Data link parameter, if $setting_key is a valid setting, empty string otherwise. + */ + public function get_link( $setting_key = 'default' ) { + if ( ! isset( $this->settings[ $setting_key ] ) ) + return ''; + + return 'data-customize-setting-link="' . esc_attr( $this->settings[ $setting_key ]->id ) . '"'; + } + + /** + * Render the data link attribute for the control's input element. + * + * @since 3.4.0 + * @uses WP_Customize_Control::get_link() + * + * @param string $setting_key + */ + public function link( $setting_key = 'default' ) { + echo $this->get_link( $setting_key ); + } + + /** + * Render the custom attributes for the control's input element. + * + * @since 4.0.0 + * @access public + */ + public function input_attrs() { + foreach( $this->input_attrs as $attr => $value ) { + echo $attr . '="' . esc_attr( $value ) . '" '; + } + } + + /** + * Render the control's content. + * + * Allows the content to be overriden without having to rewrite the wrapper in $this->render(). + * + * Supports basic input types `text`, `checkbox`, `textarea`, `radio`, `select` and `dropdown-pages`. + * Additional input types such as `email`, `url`, `number`, `hidden` and `date` are supported implicitly. + * + * Control content can alternately be rendered in JS. See {@see WP_Customize_Control::print_template()}. + * + * @since 3.4.0 + */ + protected function render_content() { + switch( $this->type ) { + case 'checkbox': + ?> + <label> + <input type="checkbox" value="<?php echo esc_attr( $this->value() ); ?>" <?php $this->link(); checked( $this->value() ); ?> /> + <?php echo esc_html( $this->label ); ?> + <?php if ( ! empty( $this->description ) ) : ?> + <span class="description customize-control-description"><?php echo $this->description; ?></span> + <?php endif; ?> + </label> + <?php + break; + case 'radio': + if ( empty( $this->choices ) ) + return; + + $name = '_customize-radio-' . $this->id; + + if ( ! empty( $this->label ) ) : ?> + <span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span> + <?php endif; + if ( ! empty( $this->description ) ) : ?> + <span class="description customize-control-description"><?php echo $this->description ; ?></span> + <?php endif; + + foreach ( $this->choices as $value => $label ) : + ?> + <label> + <input type="radio" value="<?php echo esc_attr( $value ); ?>" name="<?php echo esc_attr( $name ); ?>" <?php $this->link(); checked( $this->value(), $value ); ?> /> + <?php echo esc_html( $label ); ?><br/> + </label> + <?php + endforeach; + break; + case 'select': + if ( empty( $this->choices ) ) + return; + + ?> + <label> + <?php if ( ! empty( $this->label ) ) : ?> + <span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span> + <?php endif; + if ( ! empty( $this->description ) ) : ?> + <span class="description customize-control-description"><?php echo $this->description; ?></span> + <?php endif; ?> + + <select <?php $this->link(); ?>> + <?php + foreach ( $this->choices as $value => $label ) + echo '<option value="' . esc_attr( $value ) . '"' . selected( $this->value(), $value, false ) . '>' . $label . '</option>'; + ?> + </select> + </label> + <?php + break; + case 'textarea': + ?> + <label> + <?php if ( ! empty( $this->label ) ) : ?> + <span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span> + <?php endif; + if ( ! empty( $this->description ) ) : ?> + <span class="description customize-control-description"><?php echo $this->description; ?></span> + <?php endif; ?> + <textarea rows="5" <?php $this->link(); ?>><?php echo esc_textarea( $this->value() ); ?></textarea> + </label> + <?php + break; + case 'dropdown-pages': + $dropdown = wp_dropdown_pages( + array( + 'name' => '_customize-dropdown-pages-' . $this->id, + 'echo' => 0, + 'show_option_none' => __( '— Select —' ), + 'option_none_value' => '0', + 'selected' => $this->value(), + ) + ); + + // Hackily add in the data link parameter. + $dropdown = str_replace( '<select', '<select ' . $this->get_link(), $dropdown ); + + printf( + '<label class="customize-control-select"><span class="customize-control-title">%s</span> %s</label>', + $this->label, + $dropdown + ); + break; + default: + ?> + <label> + <?php if ( ! empty( $this->label ) ) : ?> + <span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span> + <?php endif; + if ( ! empty( $this->description ) ) : ?> + <span class="description customize-control-description"><?php echo $this->description; ?></span> + <?php endif; ?> + <input type="<?php echo esc_attr( $this->type ); ?>" <?php $this->input_attrs(); ?> value="<?php echo esc_attr( $this->value() ); ?>" <?php $this->link(); ?> /> + </label> + <?php + break; + } + } + + /** + * Render the control's JS template. + * + * This function is only run for control types that have been registered with + * {@see WP_Customize_Manager::register_control_type()}. + * + * In the future, this will also print the template for the control's container + * element and be override-able. + * + * @since 4.1.0 + */ + final public function print_template() { + ?> + <script type="text/html" id="tmpl-customize-control-<?php echo $this->type; ?>-content"> + <?php $this->content_template(); ?> + </script> + <?php + } + + /** + * An Underscore (JS) template for this control's content (but not its container). + * + * Class variables for this control class are available in the `data` JS object; + * export custom variables by overriding {@see WP_Customize_Control::to_json()}. + * + * @see WP_Customize_Control::print_template() + * + * @since 4.1.0 + */ + protected function content_template() {} + +} + +/** + * Customize Color Control Class + * + * @package WordPress + * @subpackage Customize + * @since 3.4.0 + */ +class WP_Customize_Color_Control extends WP_Customize_Control { + /** + * @access public + * @var string + */ + public $type = 'color'; + + /** + * @access public + * @var array + */ + public $statuses; + + /** + * Constructor. + * + * @since 3.4.0 + * @uses WP_Customize_Control::__construct() + * + * @param WP_Customize_Manager $manager + * @param string $id + * @param array $args + */ + public function __construct( $manager, $id, $args = array() ) { + $this->statuses = array( '' => __('Default') ); + parent::__construct( $manager, $id, $args ); + } + + /** + * Enqueue scripts/styles for the color picker. + * + * @since 3.4.0 + */ + public function enqueue() { + wp_enqueue_script( 'wp-color-picker' ); + wp_enqueue_style( 'wp-color-picker' ); + } + + /** + * Refresh the parameters passed to the JavaScript via JSON. + * + * @since 3.4.0 + * @uses WP_Customize_Control::to_json() + */ + public function to_json() { + parent::to_json(); + $this->json['statuses'] = $this->statuses; + $this->json['defaultValue'] = $this->setting->default; + } + + /** + * Don't render the control content from PHP, as it's rendered via JS on load. + * + * @since 3.4.0 + */ + public function render_content() {} + + /** + * Render a JS template for the content of the color picker control. + * + * @since 4.1.0 + */ + public function content_template() { + ?> + <# var defaultValue = ''; + if ( data.defaultValue ) { + if ( '#' !== data.defaultValue.substring( 0, 1 ) ) { + defaultValue = '#' + data.defaultValue; + } else { + defaultValue = data.defaultValue; + } + defaultValue = ' data-default-color=' + defaultValue; // Quotes added automatically. + } #> + <label> + <# if ( data.label ) { #> + <span class="customize-control-title">{{{ data.label }}}</span> + <# } #> + <# if ( data.description ) { #> + <span class="description customize-control-description">{{{ data.description }}}</span> + <# } #> + <div class="customize-control-content"> + <input class="color-picker-hex" type="text" maxlength="7" placeholder="<?php esc_attr_e( 'Hex Value' ); ?>" {{ defaultValue }} /> + </div> + </label> + <?php + } +} + +/** + * Customize Upload Control Class + * + * @package WordPress + * @subpackage Customize + * @since 3.4.0 + */ +class WP_Customize_Upload_Control extends WP_Customize_Control { + public $type = 'upload'; + public $mime_type = ''; + public $button_labels = array(); + public $removed = ''; // unused + public $context; // unused + public $extensions = array(); // unused + + /** + * Constructor. + * + * @since 4.1.0 + * + * @param WP_Customize_Manager $manager {@see WP_Customize_Manager} instance. + */ + public function __construct( $manager, $id, $args = array() ) { + parent::__construct( $manager, $id, $args ); + + $this->button_labels = array( + 'select' => __( 'Select File' ), + 'change' => __( 'Change File' ), + 'default' => __( 'Default' ), + 'remove' => __( 'Remove' ), + 'placeholder' => __( 'No file selected' ), + 'frame_title' => __( 'Select File' ), + 'frame_button' => __( 'Choose File' ), + ); + } + + /** + * Enqueue control related scripts/styles. + * + * @since 3.4.0 + */ + public function enqueue() { + wp_enqueue_media(); + } + + /** + * Refresh the parameters passed to the JavaScript via JSON. + * + * @since 3.4.0 + * @uses WP_Customize_Control::to_json() + */ + public function to_json() { + parent::to_json(); + $this->json['mime_type'] = $this->mime_type; + $this->json['button_labels'] = $this->button_labels; + + $value = $this->value(); + + if ( is_object( $this->setting ) ) { + if ( $this->setting->default ) { + // Fake an attachment model - needs all fields used by template. + $type = in_array( substr( $this->setting->default, -3 ), array( 'jpg', 'png', 'gif', 'bmp' ) ) ? 'image' : 'document'; + $default_attachment = array( + 'id' => 1, + 'url' => $this->setting->default, + 'type' => $type, + 'icon' => wp_mime_type_icon( $type ), + 'title' => basename( $this->setting->default ), + ); + + if ( 'image' === $type ) { + $default_attachment['sizes'] = array( + 'full' => array( 'url' => $this->setting->default ), + ); + } + + $this->json['defaultAttachment'] = $default_attachment; + } + + if ( $value && $this->setting->default && $value === $this->setting->default ) { + // Set the default as the attachment. + $this->json['attachment'] = $this->json['defaultAttachment']; + } elseif ( $value ) { + // Get the attachment model for the existing file. + $attachment_id = attachment_url_to_postid( $value ); + if ( $attachment_id ) { + $this->json['attachment'] = wp_prepare_attachment_for_js( $attachment_id ); + } + } + } + } + + /** + * Don't render any content for this control from PHP. + * + * @see WP_Customize_Upload_Control::content_template() + * @since 3.4.0 + */ + public function render_content() {} + + /** + * Render a JS template for the content of the upload control. + * + * @since 4.1.0 + */ + public function content_template() { + ?> + <label for="{{ data.settings['default'] }}-button"> + <# if ( data.label ) { #> + <span class="customize-control-title">{{ data.label }}</span> + <# } #> + <# if ( data.description ) { #> + <span class="description customize-control-description">{{{ data.description }}}</span> + <# } #> + </label> + + <# if ( data.attachment && data.attachment.id ) { #> + <div class="current"> + <div class="container"> + <div class="attachment-media-view attachment-media-view-{{ data.attachment.type }} {{ data.attachment.orientation }}"> + <div class="thumbnail thumbnail-{{ data.attachment.type }}"> + <# if ( 'image' === data.attachment.type && data.attachment.sizes && data.attachment.sizes.medium ) { #> + <img class="attachment-thumb" src="{{ data.attachment.sizes.medium.url }}" draggable="false" /> + <# } else if ( 'image' === data.attachment.type && data.attachment.sizes && data.attachment.sizes.full ) { #> + <img class="attachment-thumb" src="{{ data.attachment.sizes.full.url }}" draggable="false" /> + <# } else if ( 'audio' === data.attachment.type ) { #> + <img class="attachment-thumb type-icon" src="{{ data.attachment.icon }}" class="icon" draggable="false" /> + <p class="attachment-meta attachment-meta-title">“{{ data.attachment.title }}”</p> + <# if ( data.attachment.album || data.attachment.meta.album ) { #> + <p class="attachment-meta"><em>{{ data.attachment.album || data.attachment.meta.album }}</em></p> + <# } #> + <# if ( data.attachment.artist || data.attachment.meta.artist ) { #> + <p class="attachment-meta">{{ data.attachment.artist || data.attachment.meta.artist }}</p> + <# } #> + <# } else { #> + <img class="attachment-thumb type-icon" src="{{ data.attachment.icon }}" class="icon" draggable="false" /> + <p class="attachment-title">{{ data.attachment.title }}</p> + <# } #> + </div> + </div> + </div> + </div> + <div class="actions"> + <button type="button" class="button remove-button"><?php echo $this->button_labels['remove']; ?></button> + <button type="button" class="button upload-button" id="{{ data.settings['default'] }}-button"><?php echo $this->button_labels['change']; ?></button> + <div style="clear:both"></div> + </div> + <# } else { #> + <div class="current"> + <div class="container"> + <div class="placeholder"> + <div class="inner"> + <span> + <?php echo $this->button_labels['placeholder']; ?> + </span> + </div> + </div> + </div> + </div> + <div class="actions"> + <# if ( data.defaultAttachment ) { #> + <button type="button" class="button default-button"><?php echo $this->button_labels['default']; ?></button> + <# } #> + <button type="button" class="button upload-button" id="{{ data.settings['default'] }}-button"><?php echo $this->button_labels['select']; ?></button> + <div style="clear:both"></div> + </div> + <# } #> + <?php + } +} + +/** + * Customize Image Control Class + * + * @package WordPress + * @subpackage Customize + * @since 3.4.0 + */ +class WP_Customize_Image_Control extends WP_Customize_Upload_Control { + public $type = 'image'; + public $mime_type = 'image'; + + /** + * Constructor. + * + * @since 3.4.0 + * @uses WP_Customize_Upload_Control::__construct() + * + * @param WP_Customize_Manager $manager + * @param string $id + * @param array $args + */ + public function __construct( $manager, $id, $args = array() ) { + parent::__construct( $manager, $id, $args ); + + $this->button_labels = array( + 'select' => __( 'Select Image' ), + 'change' => __( 'Change Image' ), + 'remove' => __( 'Remove' ), + 'default' => __( 'Default' ), + 'placeholder' => __( 'No image selected' ), + 'frame_title' => __( 'Select Image' ), + 'frame_button' => __( 'Choose Image' ), + ); + } + + /** + * @since 3.4.2 + * @deprecated 4.1.0 + */ + public function prepare_control() {} + + /** + * @since 3.4.0 + * @deprecated 4.1.0 + * + * @param string $id + * @param string $label + * @param mixed $callback + */ + public function add_tab( $id, $label, $callback ) {} + + /** + * @since 3.4.0 + * @deprecated 4.1.0 + * + * @param string $id + */ + public function remove_tab( $id ) {} + + /** + * @since 3.4.0 + * @deprecated 4.1.0 + * + * @param string $url + * @param string $thumbnail_url + */ + public function print_tab_image( $url, $thumbnail_url = null ) {} +} + +/** + * Customize Background Image Control Class + * + * @package WordPress + * @subpackage Customize + * @since 3.4.0 + */ +class WP_Customize_Background_Image_Control extends WP_Customize_Image_Control { + public $type = 'background'; + + /** + * Constructor. + * + * @since 3.4.0 + * @uses WP_Customize_Image_Control::__construct() + * + * @param WP_Customize_Manager $manager + */ + public function __construct( $manager ) { + parent::__construct( $manager, 'background_image', array( + 'label' => __( 'Background Image' ), + 'section' => 'background_image', + ) ); + } + + /** + * Enqueue control related scripts/styles. + * + * @since 4.1.0 + */ + public function enqueue() { + parent::enqueue(); + + wp_localize_script( 'customize-controls', '_wpCustomizeBackground', array( + 'nonces' => array( + 'add' => wp_create_nonce( 'background-add' ), + ), + ) ); + } +} + +class WP_Customize_Header_Image_Control extends WP_Customize_Image_Control { + public $type = 'header'; + public $uploaded_headers; + public $default_headers; + + /** + * @param WP_Customize_Manager $manager + */ + public function __construct( $manager ) { + parent::__construct( $manager, 'header_image', array( + 'label' => __( 'Header Image' ), + 'settings' => array( + 'default' => 'header_image', + 'data' => 'header_image_data', + ), + 'section' => 'header_image', + 'removed' => 'remove-header', + 'get_url' => 'get_header_image', + ) ); + + } + + public function to_json() { + parent::to_json(); + } + + public function enqueue() { + wp_enqueue_media(); + wp_enqueue_script( 'customize-views' ); + + $this->prepare_control(); + + wp_localize_script( 'customize-views', '_wpCustomizeHeader', array( + 'data' => array( + 'width' => absint( get_theme_support( 'custom-header', 'width' ) ), + 'height' => absint( get_theme_support( 'custom-header', 'height' ) ), + 'flex-width' => absint( get_theme_support( 'custom-header', 'flex-width' ) ), + 'flex-height' => absint( get_theme_support( 'custom-header', 'flex-height' ) ), + 'currentImgSrc' => $this->get_current_image_src(), + ), + 'nonces' => array( + 'add' => wp_create_nonce( 'header-add' ), + 'remove' => wp_create_nonce( 'header-remove' ), + ), + 'uploads' => $this->uploaded_headers, + 'defaults' => $this->default_headers + ) ); + + parent::enqueue(); + } + + public function prepare_control() { + global $custom_image_header; + if ( empty( $custom_image_header ) ) { + return; + } + + // Process default headers and uploaded headers. + $custom_image_header->process_default_headers(); + $this->default_headers = $custom_image_header->get_default_header_images(); + $this->uploaded_headers = $custom_image_header->get_uploaded_header_images(); + } + + function print_header_image_template() { + ?> + <script type="text/template" id="tmpl-header-choice"> + <# if (data.random) { #> + <button type="button" class="button display-options random"> + <span class="dashicons dashicons-randomize dice"></span> + <# if ( data.type === 'uploaded' ) { #> + <?php _e( 'Randomize uploaded headers' ); ?> + <# } else if ( data.type === 'default' ) { #> + <?php _e( 'Randomize suggested headers' ); ?> + <# } #> + </button> + + <# } else { #> + + <# if (data.type === 'uploaded') { #> + <div class="dashicons dashicons-no close"></div> + <# } #> + + <button type="button" class="choice thumbnail" + data-customize-image-value="{{{data.header.url}}}" + data-customize-header-image-data="{{JSON.stringify(data.header)}}"> + <span class="screen-reader-text"><?php _e( 'Set image' ); ?></span> + <img src="{{{data.header.thumbnail_url}}}" alt="{{{data.header.alt_text || data.header.description}}}"> + </button> + + <# } #> + </script> + + <script type="text/template" id="tmpl-header-current"> + <# if (data.choice) { #> + <# if (data.random) { #> + + <div class="placeholder"> + <div class="inner"> + <span><span class="dashicons dashicons-randomize dice"></span> + <# if ( data.type === 'uploaded' ) { #> + <?php _e( 'Randomizing uploaded headers' ); ?> + <# } else if ( data.type === 'default' ) { #> + <?php _e( 'Randomizing suggested headers' ); ?> + <# } #> + </span> + </div> + </div> + + <# } else { #> + + <img src="{{{data.header.thumbnail_url}}}" alt="{{{data.header.alt_text || data.header.description}}}" tabindex="0"/> + + <# } #> + <# } else { #> + + <div class="placeholder"> + <div class="inner"> + <span> + <?php _e( 'No image set' ); ?> + </span> + </div> + </div> + + <# } #> + </script> + <?php + } + + public function get_current_image_src() { + $src = $this->value(); + if ( isset( $this->get_url ) ) { + $src = call_user_func( $this->get_url, $src ); + return $src; + } + return null; + } + + public function render_content() { + $this->print_header_image_template(); + $visibility = $this->get_current_image_src() ? '' : ' style="display:none" '; + $width = absint( get_theme_support( 'custom-header', 'width' ) ); + $height = absint( get_theme_support( 'custom-header', 'height' ) ); + ?> + + + <div class="customize-control-content"> + <p class="customizer-section-intro"> + <?php + if ( $width && $height ) { + printf( __( 'While you can crop images to your liking after clicking <strong>Add new image</strong>, your theme recommends a header size of <strong>%s × %s</strong> pixels.' ), $width, $height ); + } elseif ( $width ) { + printf( __( 'While you can crop images to your liking after clicking <strong>Add new image</strong>, your theme recommends a header width of <strong>%s</strong> pixels.' ), $width ); + } else { + printf( __( 'While you can crop images to your liking after clicking <strong>Add new image</strong>, your theme recommends a header height of <strong>%s</strong> pixels.' ), $height ); + } + ?> + </p> + <div class="current"> + <span class="customize-control-title"> + <?php _e( 'Current header' ); ?> + </span> + <div class="container"> + </div> + </div> + <div class="actions"> + <?php /* translators: Hide as in hide header image via the Customizer */ ?> + <button type="button"<?php echo $visibility ?> class="button remove"><?php _ex( 'Hide image', 'custom header' ); ?></button> + <?php /* translators: New as in add new header image via the Customizer */ ?> + <button type="button" class="button new"><?php _ex( 'Add new image', 'header image' ); ?></button> + <div style="clear:both"></div> + </div> + <div class="choices"> + <span class="customize-control-title header-previously-uploaded"> + <?php _ex( 'Previously uploaded', 'custom headers' ); ?> + </span> + <div class="uploaded"> + <div class="list"> + </div> + </div> + <span class="customize-control-title header-default"> + <?php _ex( 'Suggested', 'custom headers' ); ?> + </span> + <div class="default"> + <div class="list"> + </div> + </div> + </div> + </div> + <?php + } +} + +/** + * Widget Area Customize Control Class + * + * @since 3.9.0 + */ +class WP_Widget_Area_Customize_Control extends WP_Customize_Control { + public $type = 'sidebar_widgets'; + public $sidebar_id; + + public function to_json() { + parent::to_json(); + $exported_properties = array( 'sidebar_id' ); + foreach ( $exported_properties as $key ) { + $this->json[ $key ] = $this->$key; + } + } + + public function render_content() { + ?> + <span class="button-secondary add-new-widget" tabindex="0"> + <?php _e( 'Add a Widget' ); ?> + </span> + + <span class="reorder-toggle" tabindex="0"> + <span class="reorder"><?php _ex( 'Reorder', 'Reorder widgets in Customizer' ); ?></span> + <span class="reorder-done"><?php _ex( 'Done', 'Cancel reordering widgets in Customizer' ); ?></span> + </span> + <?php + } + +} + +/** + * Widget Form Customize Control Class + * + * @since 3.9.0 + */ +class WP_Widget_Form_Customize_Control extends WP_Customize_Control { + public $type = 'widget_form'; + public $widget_id; + public $widget_id_base; + public $sidebar_id; + public $is_new = false; + public $width; + public $height; + public $is_wide = false; + + public function to_json() { + parent::to_json(); + $exported_properties = array( 'widget_id', 'widget_id_base', 'sidebar_id', 'width', 'height', 'is_wide' ); + foreach ( $exported_properties as $key ) { + $this->json[ $key ] = $this->$key; + } + } + + public function render_content() { + global $wp_registered_widgets; + require_once ABSPATH . '/wp-admin/includes/widgets.php'; + + $widget = $wp_registered_widgets[ $this->widget_id ]; + if ( ! isset( $widget['params'][0] ) ) { + $widget['params'][0] = array(); + } + + $args = array( + 'widget_id' => $widget['id'], + 'widget_name' => $widget['name'], + ); + + $args = wp_list_widget_controls_dynamic_sidebar( array( 0 => $args, 1 => $widget['params'][0] ) ); + echo $this->manager->widgets->get_widget_control( $args ); + } + + /** + * Whether the current widget is rendered on the page. + * + * @since 4.0.0 + * @access public + * + * @return bool Whether the widget is rendered. + */ + function active_callback() { + return $this->manager->widgets->is_widget_rendered( $this->widget_id ); + } +} + diff --git a/wp-includes/class-wp-customize-manager.php b/wp-includes/class-wp-customize-manager.php new file mode 100644 index 0000000..c14d616 --- /dev/null +++ b/wp-includes/class-wp-customize-manager.php @@ -0,0 +1,1343 @@ +<?php +/** + * Customize Manager. + * + * Bootstraps the Customize experience on the server-side. + * + * Sets up the theme-switching process if a theme other than the active one is + * being previewed and customized. + * + * Serves as a factory for Customize Controls and Settings, and + * instantiates default Customize Controls and Settings. + * + * @package WordPress + * @subpackage Customize + * @since 3.4.0 + */ +final class WP_Customize_Manager { + /** + * An instance of the theme being previewed. + * + * @var WP_Theme + */ + protected $theme; + + /** + * The directory name of the previously active theme (within the theme_root). + * + * @var string + */ + protected $original_stylesheet; + + /** + * Whether this is a Customizer pageload. + * + * @var boolean + */ + protected $previewing = false; + + /** + * Methods and properties deailing with managing widgets in the Customizer. + * + * @var WP_Customize_Widgets + */ + public $widgets; + + protected $settings = array(); + protected $containers = array(); + protected $panels = array(); + protected $sections = array(); + protected $controls = array(); + + protected $nonce_tick; + + protected $customized; + + /** + * Controls that may be rendered from JS templates. + * + * @since 4.1.0 + * @access protected + * @var array + */ + protected $registered_control_types = array(); + + /** + * Unsanitized values for Customize Settings parsed from $_POST['customized']. + * + * @var array|false + */ + private $_post_values; + + /** + * Constructor. + * + * @since 3.4.0 + */ + public function __construct() { + require_once( ABSPATH . WPINC . '/class-wp-customize-setting.php' ); + require_once( ABSPATH . WPINC . '/class-wp-customize-panel.php' ); + require_once( ABSPATH . WPINC . '/class-wp-customize-section.php' ); + require_once( ABSPATH . WPINC . '/class-wp-customize-control.php' ); + require_once( ABSPATH . WPINC . '/class-wp-customize-widgets.php' ); + + $this->widgets = new WP_Customize_Widgets( $this ); + + add_filter( 'wp_die_handler', array( $this, 'wp_die_handler' ) ); + + add_action( 'setup_theme', array( $this, 'setup_theme' ) ); + add_action( 'wp_loaded', array( $this, 'wp_loaded' ) ); + + // Run wp_redirect_status late to make sure we override the status last. + add_action( 'wp_redirect_status', array( $this, 'wp_redirect_status' ), 1000 ); + + // Do not spawn cron (especially the alternate cron) while running the Customizer. + remove_action( 'init', 'wp_cron' ); + + // Do not run update checks when rendering the controls. + remove_action( 'admin_init', '_maybe_update_core' ); + remove_action( 'admin_init', '_maybe_update_plugins' ); + remove_action( 'admin_init', '_maybe_update_themes' ); + + add_action( 'wp_ajax_customize_save', array( $this, 'save' ) ); + + add_action( 'customize_register', array( $this, 'register_controls' ) ); + add_action( 'customize_controls_init', array( $this, 'prepare_controls' ) ); + add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_control_scripts' ) ); + } + + /** + * Return true if it's an AJAX request. + * + * @since 3.4.0 + * + * @return bool + */ + public function doing_ajax() { + return isset( $_POST['customized'] ) || ( defined( 'DOING_AJAX' ) && DOING_AJAX ); + } + + /** + * Custom wp_die wrapper. Returns either the standard message for UI + * or the AJAX message. + * + * @since 3.4.0 + * + * @param mixed $ajax_message AJAX return + * @param mixed $message UI message + */ + protected function wp_die( $ajax_message, $message = null ) { + if ( $this->doing_ajax() ) + wp_die( $ajax_message ); + + if ( ! $message ) + $message = __( 'Cheatin’ uh?' ); + + wp_die( $message ); + } + + /** + * Return the AJAX wp_die() handler if it's a customized request. + * + * @since 3.4.0 + * + * @return string + */ + public function wp_die_handler() { + if ( $this->doing_ajax() ) + return '_ajax_wp_die_handler'; + + return '_default_wp_die_handler'; + } + + /** + * Start preview and customize theme. + * + * Check if customize query variable exist. Init filters to filter the current theme. + * + * @since 3.4.0 + */ + public function setup_theme() { + send_origin_headers(); + + if ( is_admin() && ! $this->doing_ajax() ) + auth_redirect(); + elseif ( $this->doing_ajax() && ! is_user_logged_in() ) + $this->wp_die( 0 ); + + show_admin_bar( false ); + + if ( ! current_user_can( 'customize' ) ) { + $this->wp_die( -1 ); + } + + $this->original_stylesheet = get_stylesheet(); + + $this->theme = wp_get_theme( isset( $_REQUEST['theme'] ) ? $_REQUEST['theme'] : null ); + + if ( $this->is_theme_active() ) { + // Once the theme is loaded, we'll validate it. + add_action( 'after_setup_theme', array( $this, 'after_setup_theme' ) ); + } else { + // If the requested theme is not the active theme and the user doesn't have the + // switch_themes cap, bail. + if ( ! current_user_can( 'switch_themes' ) ) + $this->wp_die( -1 ); + + // If the theme has errors while loading, bail. + if ( $this->theme()->errors() ) + $this->wp_die( -1 ); + + // If the theme isn't allowed per multisite settings, bail. + if ( ! $this->theme()->is_allowed() ) + $this->wp_die( -1 ); + } + + $this->start_previewing_theme(); + } + + /** + * Callback to validate a theme once it is loaded + * + * @since 3.4.0 + */ + public function after_setup_theme() { + if ( ! $this->doing_ajax() && ! validate_current_theme() ) { + wp_redirect( 'themes.php?broken=true' ); + exit; + } + } + + /** + * If the theme to be previewed isn't the active theme, add filter callbacks + * to swap it out at runtime. + * + * @since 3.4.0 + */ + public function start_previewing_theme() { + // Bail if we're already previewing. + if ( $this->is_preview() ) + return; + + $this->previewing = true; + + if ( ! $this->is_theme_active() ) { + add_filter( 'template', array( $this, 'get_template' ) ); + add_filter( 'stylesheet', array( $this, 'get_stylesheet' ) ); + add_filter( 'pre_option_current_theme', array( $this, 'current_theme' ) ); + + // @link: https://core.trac.wordpress.org/ticket/20027 + add_filter( 'pre_option_stylesheet', array( $this, 'get_stylesheet' ) ); + add_filter( 'pre_option_template', array( $this, 'get_template' ) ); + + // Handle custom theme roots. + add_filter( 'pre_option_stylesheet_root', array( $this, 'get_stylesheet_root' ) ); + add_filter( 'pre_option_template_root', array( $this, 'get_template_root' ) ); + } + + /** + * Fires once the Customizer theme preview has started. + * + * @since 3.4.0 + * + * @param WP_Customize_Manager $this WP_Customize_Manager instance. + */ + do_action( 'start_previewing_theme', $this ); + } + + /** + * Stop previewing the selected theme. + * + * Removes filters to change the current theme. + * + * @since 3.4.0 + */ + public function stop_previewing_theme() { + if ( ! $this->is_preview() ) + return; + + $this->previewing = false; + + if ( ! $this->is_theme_active() ) { + remove_filter( 'template', array( $this, 'get_template' ) ); + remove_filter( 'stylesheet', array( $this, 'get_stylesheet' ) ); + remove_filter( 'pre_option_current_theme', array( $this, 'current_theme' ) ); + + // @link: https://core.trac.wordpress.org/ticket/20027 + remove_filter( 'pre_option_stylesheet', array( $this, 'get_stylesheet' ) ); + remove_filter( 'pre_option_template', array( $this, 'get_template' ) ); + + // Handle custom theme roots. + remove_filter( 'pre_option_stylesheet_root', array( $this, 'get_stylesheet_root' ) ); + remove_filter( 'pre_option_template_root', array( $this, 'get_template_root' ) ); + } + + /** + * Fires once the Customizer theme preview has stopped. + * + * @since 3.4.0 + * + * @param WP_Customize_Manager $this WP_Customize_Manager instance. + */ + do_action( 'stop_previewing_theme', $this ); + } + + /** + * Get the theme being customized. + * + * @since 3.4.0 + * + * @return WP_Theme + */ + public function theme() { + return $this->theme; + } + + /** + * Get the registered settings. + * + * @since 3.4.0 + * + * @return array + */ + public function settings() { + return $this->settings; + } + + /** + * Get the registered controls. + * + * @since 3.4.0 + * + * @return array + */ + public function controls() { + return $this->controls; + } + + /** + * Get the registered containers. + * + * @since 4.0.0 + * + * @return array + */ + public function containers() { + return $this->containers; + } + + /** + * Get the registered sections. + * + * @since 3.4.0 + * + * @return array + */ + public function sections() { + return $this->sections; + } + + /** + * Get the registered panels. + * + * @since 4.0.0 + * @access public + * + * @return array Panels. + */ + public function panels() { + return $this->panels; + } + + /** + * Checks if the current theme is active. + * + * @since 3.4.0 + * + * @return bool + */ + public function is_theme_active() { + return $this->get_stylesheet() == $this->original_stylesheet; + } + + /** + * Register styles/scripts and initialize the preview of each setting + * + * @since 3.4.0 + */ + public function wp_loaded() { + + /** + * Fires once WordPress has loaded, allowing scripts and styles to be initialized. + * + * @since 3.4.0 + * + * @param WP_Customize_Manager $this WP_Customize_Manager instance. + */ + do_action( 'customize_register', $this ); + + if ( $this->is_preview() && ! is_admin() ) + $this->customize_preview_init(); + } + + /** + * Prevents AJAX requests from following redirects when previewing a theme + * by issuing a 200 response instead of a 30x. + * + * Instead, the JS will sniff out the location header. + * + * @since 3.4.0 + * + * @param $status + * @return int + */ + public function wp_redirect_status( $status ) { + if ( $this->is_preview() && ! is_admin() ) + return 200; + + return $status; + } + + /** + * Parse the incoming $_POST['customized'] JSON data and store the unsanitized + * settings for subsequent post_value() lookups. + * + * @since 4.1.1 + * + * @return array + */ + public function unsanitized_post_values() { + if ( ! isset( $this->_post_values ) ) { + if ( isset( $_POST['customized'] ) ) { + $this->_post_values = json_decode( wp_unslash( $_POST['customized'] ), true ); + } + if ( empty( $this->_post_values ) ) { // if not isset or of JSON error + $this->_post_values = false; + } + } + if ( empty( $this->_post_values ) ) { + return array(); + } else { + return $this->_post_values; + } + } + + /** + * Return the sanitized value for a given setting from the request's POST data. + * + * @since 3.4.0 + * @since 4.1.1 Introduced 'default' parameter. + * + * @param WP_Customize_Setting $setting A WP_Customize_Setting derived object + * @param mixed $default value returned $setting has no post value (added in 4.2.0). + * @return string|mixed $post_value Sanitized value or the $default provided + */ + public function post_value( $setting, $default = null ) { + $post_values = $this->unsanitized_post_values(); + if ( array_key_exists( $setting->id, $post_values ) ) { + return $setting->sanitize( $post_values[ $setting->id ] ); + } else { + return $default; + } + } + + /** + * Print JavaScript settings. + * + * @since 3.4.0 + */ + public function customize_preview_init() { + $this->nonce_tick = check_ajax_referer( 'preview-customize_' . $this->get_stylesheet(), 'nonce' ); + + $this->prepare_controls(); + + wp_enqueue_script( 'customize-preview' ); + add_action( 'wp', array( $this, 'customize_preview_override_404_status' ) ); + add_action( 'wp_head', array( $this, 'customize_preview_base' ) ); + add_action( 'wp_head', array( $this, 'customize_preview_html5' ) ); + add_action( 'wp_footer', array( $this, 'customize_preview_settings' ), 20 ); + add_action( 'shutdown', array( $this, 'customize_preview_signature' ), 1000 ); + add_filter( 'wp_die_handler', array( $this, 'remove_preview_signature' ) ); + + foreach ( $this->settings as $setting ) { + $setting->preview(); + } + + /** + * Fires once the Customizer preview has initialized and JavaScript + * settings have been printed. + * + * @since 3.4.0 + * + * @param WP_Customize_Manager $this WP_Customize_Manager instance. + */ + do_action( 'customize_preview_init', $this ); + } + + /** + * Prevent sending a 404 status when returning the response for the customize + * preview, since it causes the jQuery AJAX to fail. Send 200 instead. + * + * @since 4.0.0 + * @access public + */ + public function customize_preview_override_404_status() { + if ( is_404() ) { + status_header( 200 ); + } + } + + /** + * Print base element for preview frame. + * + * @since 3.4.0 + */ + public function customize_preview_base() { + ?><base href="<?php echo home_url( '/' ); ?>" /><?php + } + + /** + * Print a workaround to handle HTML5 tags in IE < 9 + * + * @since 3.4.0 + */ + public function customize_preview_html5() { ?> + <!--[if lt IE 9]> + <script type="text/javascript"> + var e = [ 'abbr', 'article', 'aside', 'audio', 'canvas', 'datalist', 'details', + 'figure', 'footer', 'header', 'hgroup', 'mark', 'menu', 'meter', 'nav', + 'output', 'progress', 'section', 'time', 'video' ]; + for ( var i = 0; i < e.length; i++ ) { + document.createElement( e[i] ); + } + </script> + <![endif]--><?php + } + + /** + * Print JavaScript settings for preview frame. + * + * @since 3.4.0 + */ + public function customize_preview_settings() { + $settings = array( + 'values' => array(), + 'channel' => wp_unslash( $_POST['customize_messenger_channel'] ), + 'activePanels' => array(), + 'activeSections' => array(), + 'activeControls' => array(), + ); + + if ( 2 == $this->nonce_tick ) { + $settings['nonce'] = array( + 'save' => wp_create_nonce( 'save-customize_' . $this->get_stylesheet() ), + 'preview' => wp_create_nonce( 'preview-customize_' . $this->get_stylesheet() ) + ); + } + + foreach ( $this->settings as $id => $setting ) { + $settings['values'][ $id ] = $setting->js_value(); + } + foreach ( $this->panels as $id => $panel ) { + $settings['activePanels'][ $id ] = $panel->active(); + foreach ( $panel->sections as $id => $section ) { + $settings['activeSections'][ $id ] = $section->active(); + } + } + foreach ( $this->sections as $id => $section ) { + $settings['activeSections'][ $id ] = $section->active(); + } + foreach ( $this->controls as $id => $control ) { + $settings['activeControls'][ $id ] = $control->active(); + } + + ?> + <script type="text/javascript"> + var _wpCustomizeSettings = <?php echo wp_json_encode( $settings ); ?>; + </script> + <?php + } + + /** + * Prints a signature so we can ensure the Customizer was properly executed. + * + * @since 3.4.0 + */ + public function customize_preview_signature() { + echo 'WP_CUSTOMIZER_SIGNATURE'; + } + + /** + * Removes the signature in case we experience a case where the Customizer was not properly executed. + * + * @since 3.4.0 + */ + public function remove_preview_signature( $return = null ) { + remove_action( 'shutdown', array( $this, 'customize_preview_signature' ), 1000 ); + + return $return; + } + + /** + * Is it a theme preview? + * + * @since 3.4.0 + * + * @return bool True if it's a preview, false if not. + */ + public function is_preview() { + return (bool) $this->previewing; + } + + /** + * Retrieve the template name of the previewed theme. + * + * @since 3.4.0 + * + * @return string Template name. + */ + public function get_template() { + return $this->theme()->get_template(); + } + + /** + * Retrieve the stylesheet name of the previewed theme. + * + * @since 3.4.0 + * + * @return string Stylesheet name. + */ + public function get_stylesheet() { + return $this->theme()->get_stylesheet(); + } + + /** + * Retrieve the template root of the previewed theme. + * + * @since 3.4.0 + * + * @return string Theme root. + */ + public function get_template_root() { + return get_raw_theme_root( $this->get_template(), true ); + } + + /** + * Retrieve the stylesheet root of the previewed theme. + * + * @since 3.4.0 + * + * @return string Theme root. + */ + public function get_stylesheet_root() { + return get_raw_theme_root( $this->get_stylesheet(), true ); + } + + /** + * Filter the current theme and return the name of the previewed theme. + * + * @since 3.4.0 + * + * @param $current_theme {@internal Parameter is not used} + * @return string Theme name. + */ + public function current_theme( $current_theme ) { + return $this->theme()->display('Name'); + } + + /** + * Switch the theme and trigger the save() method on each setting. + * + * @since 3.4.0 + */ + public function save() { + if ( ! $this->is_preview() ) + die; + + check_ajax_referer( 'save-customize_' . $this->get_stylesheet(), 'nonce' ); + + // Do we have to switch themes? + if ( ! $this->is_theme_active() ) { + // Temporarily stop previewing the theme to allow switch_themes() + // to operate properly. + $this->stop_previewing_theme(); + switch_theme( $this->get_stylesheet() ); + update_option( 'theme_switched_via_customizer', true ); + $this->start_previewing_theme(); + } + + /** + * Fires once the theme has switched in the Customizer, but before settings + * have been saved. + * + * @since 3.4.0 + * + * @param WP_Customize_Manager $this WP_Customize_Manager instance. + */ + do_action( 'customize_save', $this ); + + foreach ( $this->settings as $setting ) { + $setting->save(); + } + + /** + * Fires after Customize settings have been saved. + * + * @since 3.6.0 + * + * @param WP_Customize_Manager $this WP_Customize_Manager instance. + */ + do_action( 'customize_save_after', $this ); + + die; + } + + /** + * Add a customize setting. + * + * @since 3.4.0 + * + * @param WP_Customize_Setting|string $id Customize Setting object, or ID. + * @param array $args Setting arguments; passed to WP_Customize_Setting + * constructor. + */ + public function add_setting( $id, $args = array() ) { + if ( is_a( $id, 'WP_Customize_Setting' ) ) + $setting = $id; + else + $setting = new WP_Customize_Setting( $this, $id, $args ); + + $this->settings[ $setting->id ] = $setting; + } + + /** + * Retrieve a customize setting. + * + * @since 3.4.0 + * + * @param string $id Customize Setting ID. + * @return WP_Customize_Setting + */ + public function get_setting( $id ) { + if ( isset( $this->settings[ $id ] ) ) + return $this->settings[ $id ]; + } + + /** + * Remove a customize setting. + * + * @since 3.4.0 + * + * @param string $id Customize Setting ID. + */ + public function remove_setting( $id ) { + unset( $this->settings[ $id ] ); + } + + /** + * Add a customize panel. + * + * @since 4.0.0 + * @access public + * + * @param WP_Customize_Panel|string $id Customize Panel object, or Panel ID. + * @param array $args Optional. Panel arguments. Default empty array. + */ + public function add_panel( $id, $args = array() ) { + if ( is_a( $id, 'WP_Customize_Panel' ) ) { + $panel = $id; + } + else { + $panel = new WP_Customize_Panel( $this, $id, $args ); + } + + $this->panels[ $panel->id ] = $panel; + } + + /** + * Retrieve a customize panel. + * + * @since 4.0.0 + * @access public + * + * @param string $id Panel ID to get. + * @return WP_Customize_Panel Requested panel instance. + */ + public function get_panel( $id ) { + if ( isset( $this->panels[ $id ] ) ) { + return $this->panels[ $id ]; + } + } + + /** + * Remove a customize panel. + * + * @since 4.0.0 + * @access public + * + * @param string $id Panel ID to remove. + */ + public function remove_panel( $id ) { + unset( $this->panels[ $id ] ); + } + + /** + * Add a customize section. + * + * @since 3.4.0 + * + * @param WP_Customize_Section|string $id Customize Section object, or Section ID. + * @param array $args Section arguments. + */ + public function add_section( $id, $args = array() ) { + if ( is_a( $id, 'WP_Customize_Section' ) ) + $section = $id; + else + $section = new WP_Customize_Section( $this, $id, $args ); + + $this->sections[ $section->id ] = $section; + } + + /** + * Retrieve a customize section. + * + * @since 3.4.0 + * + * @param string $id Section ID. + * @return WP_Customize_Section + */ + public function get_section( $id ) { + if ( isset( $this->sections[ $id ] ) ) + return $this->sections[ $id ]; + } + + /** + * Remove a customize section. + * + * @since 3.4.0 + * + * @param string $id Section ID. + */ + public function remove_section( $id ) { + unset( $this->sections[ $id ] ); + } + + /** + * Add a customize control. + * + * @since 3.4.0 + * + * @param WP_Customize_Control|string $id Customize Control object, or ID. + * @param array $args Control arguments; passed to WP_Customize_Control + * constructor. + */ + public function add_control( $id, $args = array() ) { + if ( is_a( $id, 'WP_Customize_Control' ) ) + $control = $id; + else + $control = new WP_Customize_Control( $this, $id, $args ); + + $this->controls[ $control->id ] = $control; + } + + /** + * Retrieve a customize control. + * + * @since 3.4.0 + * + * @param string $id ID of the control. + * @return WP_Customize_Control $control The control object. + */ + public function get_control( $id ) { + if ( isset( $this->controls[ $id ] ) ) + return $this->controls[ $id ]; + } + + /** + * Remove a customize control. + * + * @since 3.4.0 + * + * @param string $id ID of the control. + */ + public function remove_control( $id ) { + unset( $this->controls[ $id ] ); + } + + /** + * Register a customize control type. + * + * Registered types are eligible to be rendered via JS and created dynamically. + * + * @since 4.1.0 + * @access public + * + * @param string $control Name of a custom control which is a subclass of + * {@see WP_Customize_Control}. + */ + public function register_control_type( $control ) { + $this->registered_control_types[] = $control; + } + + /** + * Render JS templates for all registered control types. + * + * @since 4.1.0 + * @access public + */ + public function render_control_templates() { + foreach ( $this->registered_control_types as $control_type ) { + $control = new $control_type( $this, 'temp', array() ); + $control->print_template(); + } + } + + /** + * Helper function to compare two objects by priority, ensuring sort stability via instance_number. + * + * @since 3.4.0 + * + * @param {WP_Customize_Panel|WP_Customize_Section|WP_Customize_Control} $a Object A. + * @param {WP_Customize_Panel|WP_Customize_Section|WP_Customize_Control} $b Object B. + * @return int + */ + protected final function _cmp_priority( $a, $b ) { + if ( $a->priority === $b->priority ) { + return $a->instance_number - $a->instance_number; + } else { + return $a->priority - $b->priority; + } + } + + /** + * Prepare panels, sections, and controls. + * + * For each, check if required related components exist, + * whether the user has the necessary capabilities, + * and sort by priority. + * + * @since 3.4.0 + */ + public function prepare_controls() { + + $controls = array(); + uasort( $this->controls, array( $this, '_cmp_priority' ) ); + + foreach ( $this->controls as $id => $control ) { + if ( ! isset( $this->sections[ $control->section ] ) || ! $control->check_capabilities() ) { + continue; + } + + $this->sections[ $control->section ]->controls[] = $control; + $controls[ $id ] = $control; + } + $this->controls = $controls; + + // Prepare sections. + uasort( $this->sections, array( $this, '_cmp_priority' ) ); + $sections = array(); + + foreach ( $this->sections as $section ) { + if ( ! $section->check_capabilities() || ! $section->controls ) { + continue; + } + + usort( $section->controls, array( $this, '_cmp_priority' ) ); + + if ( ! $section->panel ) { + // Top-level section. + $sections[ $section->id ] = $section; + } else { + // This section belongs to a panel. + if ( isset( $this->panels [ $section->panel ] ) ) { + $this->panels[ $section->panel ]->sections[ $section->id ] = $section; + } + } + } + $this->sections = $sections; + + // Prepare panels. + uasort( $this->panels, array( $this, '_cmp_priority' ) ); + $panels = array(); + + foreach ( $this->panels as $panel ) { + if ( ! $panel->check_capabilities() || ! $panel->sections ) { + continue; + } + + uasort( $panel->sections, array( $this, '_cmp_priority' ) ); + $panels[ $panel->id ] = $panel; + } + $this->panels = $panels; + + // Sort panels and top-level sections together. + $this->containers = array_merge( $this->panels, $this->sections ); + uasort( $this->containers, array( $this, '_cmp_priority' ) ); + } + + /** + * Enqueue scripts for customize controls. + * + * @since 3.4.0 + */ + public function enqueue_control_scripts() { + foreach ( $this->controls as $control ) { + $control->enqueue(); + } + } + + /** + * Register some default controls. + * + * @since 3.4.0 + */ + public function register_controls() { + + /* Control Types (custom control classes) */ + $this->register_control_type( 'WP_Customize_Color_Control' ); + $this->register_control_type( 'WP_Customize_Upload_Control' ); + $this->register_control_type( 'WP_Customize_Image_Control' ); + $this->register_control_type( 'WP_Customize_Background_Image_Control' ); + + /* Site Title & Tagline */ + + $this->add_section( 'title_tagline', array( + 'title' => __( 'Site Title & Tagline' ), + 'priority' => 20, + ) ); + + $this->add_setting( 'blogname', array( + 'default' => get_option( 'blogname' ), + 'type' => 'option', + 'capability' => 'manage_options', + ) ); + + $this->add_control( 'blogname', array( + 'label' => __( 'Site Title' ), + 'section' => 'title_tagline', + ) ); + + $this->add_setting( 'blogdescription', array( + 'default' => get_option( 'blogdescription' ), + 'type' => 'option', + 'capability' => 'manage_options', + ) ); + + $this->add_control( 'blogdescription', array( + 'label' => __( 'Tagline' ), + 'section' => 'title_tagline', + ) ); + + /* Colors */ + + $this->add_section( 'colors', array( + 'title' => __( 'Colors' ), + 'priority' => 40, + ) ); + + $this->add_setting( 'header_textcolor', array( + 'theme_supports' => array( 'custom-header', 'header-text' ), + 'default' => get_theme_support( 'custom-header', 'default-text-color' ), + + 'sanitize_callback' => array( $this, '_sanitize_header_textcolor' ), + 'sanitize_js_callback' => 'maybe_hash_hex_color', + ) ); + + // Input type: checkbox + // With custom value + $this->add_control( 'display_header_text', array( + 'settings' => 'header_textcolor', + 'label' => __( 'Display Header Text' ), + 'section' => 'title_tagline', + 'type' => 'checkbox', + ) ); + + $this->add_control( new WP_Customize_Color_Control( $this, 'header_textcolor', array( + 'label' => __( 'Header Text Color' ), + 'section' => 'colors', + ) ) ); + + // Input type: Color + // With sanitize_callback + $this->add_setting( 'background_color', array( + 'default' => get_theme_support( 'custom-background', 'default-color' ), + 'theme_supports' => 'custom-background', + + 'sanitize_callback' => 'sanitize_hex_color_no_hash', + 'sanitize_js_callback' => 'maybe_hash_hex_color', + ) ); + + $this->add_control( new WP_Customize_Color_Control( $this, 'background_color', array( + 'label' => __( 'Background Color' ), + 'section' => 'colors', + ) ) ); + + + /* Custom Header */ + + $this->add_section( 'header_image', array( + 'title' => __( 'Header Image' ), + 'theme_supports' => 'custom-header', + 'priority' => 60, + ) ); + + $this->add_setting( new WP_Customize_Filter_Setting( $this, 'header_image', array( + 'default' => get_theme_support( 'custom-header', 'default-image' ), + 'theme_supports' => 'custom-header', + ) ) ); + + $this->add_setting( new WP_Customize_Header_Image_Setting( $this, 'header_image_data', array( + // 'default' => get_theme_support( 'custom-header', 'default-image' ), + 'theme_supports' => 'custom-header', + ) ) ); + + $this->add_control( new WP_Customize_Header_Image_Control( $this ) ); + + /* Custom Background */ + + $this->add_section( 'background_image', array( + 'title' => __( 'Background Image' ), + 'theme_supports' => 'custom-background', + 'priority' => 80, + ) ); + + $this->add_setting( 'background_image', array( + 'default' => get_theme_support( 'custom-background', 'default-image' ), + 'theme_supports' => 'custom-background', + ) ); + + $this->add_setting( new WP_Customize_Background_Image_Setting( $this, 'background_image_thumb', array( + 'theme_supports' => 'custom-background', + ) ) ); + + $this->add_control( new WP_Customize_Background_Image_Control( $this ) ); + + $this->add_setting( 'background_repeat', array( + 'default' => get_theme_support( 'custom-background', 'default-repeat' ), + 'theme_supports' => 'custom-background', + ) ); + + $this->add_control( 'background_repeat', array( + 'label' => __( 'Background Repeat' ), + 'section' => 'background_image', + 'type' => 'radio', + 'choices' => array( + 'no-repeat' => __('No Repeat'), + 'repeat' => __('Tile'), + 'repeat-x' => __('Tile Horizontally'), + 'repeat-y' => __('Tile Vertically'), + ), + ) ); + + $this->add_setting( 'background_position_x', array( + 'default' => get_theme_support( 'custom-background', 'default-position-x' ), + 'theme_supports' => 'custom-background', + ) ); + + $this->add_control( 'background_position_x', array( + 'label' => __( 'Background Position' ), + 'section' => 'background_image', + 'type' => 'radio', + 'choices' => array( + 'left' => __('Left'), + 'center' => __('Center'), + 'right' => __('Right'), + ), + ) ); + + $this->add_setting( 'background_attachment', array( + 'default' => get_theme_support( 'custom-background', 'default-attachment' ), + 'theme_supports' => 'custom-background', + ) ); + + $this->add_control( 'background_attachment', array( + 'label' => __( 'Background Attachment' ), + 'section' => 'background_image', + 'type' => 'radio', + 'choices' => array( + 'scroll' => __('Scroll'), + 'fixed' => __('Fixed'), + ), + ) ); + + // If the theme is using the default background callback, we can update + // the background CSS using postMessage. + if ( get_theme_support( 'custom-background', 'wp-head-callback' ) === '_custom_background_cb' ) { + foreach ( array( 'color', 'image', 'position_x', 'repeat', 'attachment' ) as $prop ) { + $this->get_setting( 'background_' . $prop )->transport = 'postMessage'; + } + } + + /* Nav Menus */ + + $locations = get_registered_nav_menus(); + $menus = wp_get_nav_menus(); + $num_locations = count( array_keys( $locations ) ); + + $this->add_section( 'nav', array( + 'title' => __( 'Navigation' ), + 'theme_supports' => 'menus', + 'priority' => 100, + 'description' => sprintf( _n('Your theme supports %s menu. Select which menu you would like to use.', 'Your theme supports %s menus. Select which menu appears in each location.', $num_locations ), number_format_i18n( $num_locations ) ) . "\n\n" . __('You can edit your menu content on the Menus screen in the Appearance section.'), + ) ); + + if ( $menus ) { + $choices = array( 0 => __( '— Select —' ) ); + foreach ( $menus as $menu ) { + $choices[ $menu->term_id ] = wp_html_excerpt( $menu->name, 40, '…' ); + } + + foreach ( $locations as $location => $description ) { + $menu_setting_id = "nav_menu_locations[{$location}]"; + + $this->add_setting( $menu_setting_id, array( + 'sanitize_callback' => 'absint', + 'theme_supports' => 'menus', + ) ); + + $this->add_control( $menu_setting_id, array( + 'label' => $description, + 'section' => 'nav', + 'type' => 'select', + 'choices' => $choices, + ) ); + } + } + + /* Static Front Page */ + // #WP19627 + + $this->add_section( 'static_front_page', array( + 'title' => __( 'Static Front Page' ), + // 'theme_supports' => 'static-front-page', + 'priority' => 120, + 'description' => __( 'Your theme supports a static front page.' ), + ) ); + + $this->add_setting( 'show_on_front', array( + 'default' => get_option( 'show_on_front' ), + 'capability' => 'manage_options', + 'type' => 'option', + // 'theme_supports' => 'static-front-page', + ) ); + + $this->add_control( 'show_on_front', array( + 'label' => __( 'Front page displays' ), + 'section' => 'static_front_page', + 'type' => 'radio', + 'choices' => array( + 'posts' => __( 'Your latest posts' ), + 'page' => __( 'A static page' ), + ), + ) ); + + $this->add_setting( 'page_on_front', array( + 'type' => 'option', + 'capability' => 'manage_options', + // 'theme_supports' => 'static-front-page', + ) ); + + $this->add_control( 'page_on_front', array( + 'label' => __( 'Front page' ), + 'section' => 'static_front_page', + 'type' => 'dropdown-pages', + ) ); + + $this->add_setting( 'page_for_posts', array( + 'type' => 'option', + 'capability' => 'manage_options', + // 'theme_supports' => 'static-front-page', + ) ); + + $this->add_control( 'page_for_posts', array( + 'label' => __( 'Posts page' ), + 'section' => 'static_front_page', + 'type' => 'dropdown-pages', + ) ); + } + + /** + * Callback for validating the header_textcolor value. + * + * Accepts 'blank', and otherwise uses sanitize_hex_color_no_hash(). + * Returns default text color if hex color is empty. + * + * @since 3.4.0 + * + * @param string $color + * @return string + */ + public function _sanitize_header_textcolor( $color ) { + if ( 'blank' === $color ) + return 'blank'; + + $color = sanitize_hex_color_no_hash( $color ); + if ( empty( $color ) ) + $color = get_theme_support( 'custom-header', 'default-text-color' ); + + return $color; + } +} + +/** + * Sanitizes a hex color. + * + * Returns either '', a 3 or 6 digit hex color (with #), or null. + * For sanitizing values without a #, see sanitize_hex_color_no_hash(). + * + * @since 3.4.0 + * + * @param string $color + * @return string|null + */ +function sanitize_hex_color( $color ) { + if ( '' === $color ) + return ''; + + // 3 or 6 hex digits, or the empty string. + if ( preg_match('|^#([A-Fa-f0-9]{3}){1,2}$|', $color ) ) + return $color; + + return null; +} + +/** + * Sanitizes a hex color without a hash. Use sanitize_hex_color() when possible. + * + * Saving hex colors without a hash puts the burden of adding the hash on the + * UI, which makes it difficult to use or upgrade to other color types such as + * rgba, hsl, rgb, and html color names. + * + * Returns either '', a 3 or 6 digit hex color (without a #), or null. + * + * @since 3.4.0 + * + * @param string $color + * @return string|null + */ +function sanitize_hex_color_no_hash( $color ) { + $color = ltrim( $color, '#' ); + + if ( '' === $color ) + return ''; + + return sanitize_hex_color( '#' . $color ) ? $color : null; +} + +/** + * Ensures that any hex color is properly hashed. + * Otherwise, returns value untouched. + * + * This method should only be necessary if using sanitize_hex_color_no_hash(). + * + * @since 3.4.0 + * + * @param string $color + * @return string + */ +function maybe_hash_hex_color( $color ) { + if ( $unhashed = sanitize_hex_color_no_hash( $color ) ) + return '#' . $unhashed; + + return $color; +} diff --git a/wp-includes/class-wp-customize-panel.php b/wp-includes/class-wp-customize-panel.php new file mode 100644 index 0000000..f5da18e --- /dev/null +++ b/wp-includes/class-wp-customize-panel.php @@ -0,0 +1,327 @@ +<?php +/** + * Customize Panel Class. + * + * A UI container for sections, managed by the WP_Customize_Manager. + * + * @package WordPress + * @subpackage Customize + * @since 4.0.0 + */ +class WP_Customize_Panel { + + /** + * Incremented with each new class instantiation, then stored in $instance_number. + * + * Used when sorting two instances whose priorities are equal. + * + * @since 4.1.0 + * @access protected + * @var int + */ + protected static $instance_count = 0; + + /** + * Order in which this instance was created in relation to other instances. + * + * @since 4.1.0 + * @access public + * @var int + */ + public $instance_number; + + /** + * WP_Customize_Manager instance. + * + * @since 4.0.0 + * @access public + * @var WP_Customize_Manager + */ + public $manager; + + /** + * Unique identifier. + * + * @since 4.0.0 + * @access public + * @var string + */ + public $id; + + /** + * Priority of the panel, defining the display order of panels and sections. + * + * @since 4.0.0 + * @access public + * @var integer + */ + public $priority = 160; + + /** + * Capability required for the panel. + * + * @since 4.0.0 + * @access public + * @var string + */ + public $capability = 'edit_theme_options'; + + /** + * Theme feature support for the panel. + * + * @since 4.0.0 + * @access public + * @var string|array + */ + public $theme_supports = ''; + + /** + * Title of the panel to show in UI. + * + * @since 4.0.0 + * @access public + * @var string + */ + public $title = ''; + + /** + * Description to show in the UI. + * + * @since 4.0.0 + * @access public + * @var string + */ + public $description = ''; + + /** + * Customizer sections for this panel. + * + * @since 4.0.0 + * @access public + * @var array + */ + public $sections; + + /** + * Type of this panel. + * + * @since 4.1.0 + * @access public + * @var string + */ + public $type = 'default'; + + /** + * Active callback. + * + * @since 4.1.0 + * @access public + * + * @see WP_Customize_Section::active() + * + * @var callable Callback is called with one argument, the instance of + * {@see WP_Customize_Section}, and returns bool to indicate + * whether the section is active (such as it relates to the URL + * currently being previewed). + */ + public $active_callback = ''; + + /** + * Constructor. + * + * Any supplied $args override class property defaults. + * + * @since 4.0.0 + * + * @param WP_Customize_Manager $manager Customizer bootstrap instance. + * @param string $id An specific ID for the panel. + * @param array $args Panel arguments. + */ + public function __construct( $manager, $id, $args = array() ) { + $keys = array_keys( get_object_vars( $this ) ); + foreach ( $keys as $key ) { + if ( isset( $args[ $key ] ) ) { + $this->$key = $args[ $key ]; + } + } + + $this->manager = $manager; + $this->id = $id; + if ( empty( $this->active_callback ) ) { + $this->active_callback = array( $this, 'active_callback' ); + } + self::$instance_count += 1; + $this->instance_number = self::$instance_count; + + $this->sections = array(); // Users cannot customize the $sections array. + + return $this; + } + + /** + * Check whether panel is active to current Customizer preview. + * + * @since 4.1.0 + * @access public + * + * @return bool Whether the panel is active to the current preview. + */ + public final function active() { + $panel = $this; + $active = call_user_func( $this->active_callback, $this ); + + /** + * Filter response of WP_Customize_Panel::active(). + * + * @since 4.1.0 + * + * @param bool $active Whether the Customizer panel is active. + * @param WP_Customize_Panel $panel {@see WP_Customize_Panel} instance. + */ + $active = apply_filters( 'customize_panel_active', $active, $panel ); + + return $active; + } + + /** + * Default callback used when invoking {@see WP_Customize_Panel::active()}. + * + * Subclasses can override this with their specific logic, or they may + * provide an 'active_callback' argument to the constructor. + * + * @since 4.1.0 + * @access public + * + * @return bool Always true. + */ + public function active_callback() { + return true; + } + + /** + * Gather the parameters passed to client JavaScript via JSON. + * + * @since 4.1.0 + * + * @return array The array to be exported to the client as JSON. + */ + public function json() { + $array = wp_array_slice_assoc( (array) $this, array( 'title', 'description', 'priority', 'type' ) ); + $array['content'] = $this->get_content(); + $array['active'] = $this->active(); + $array['instanceNumber'] = $this->instance_number; + return $array; + } + + /** + * Checks required user capabilities and whether the theme has the + * feature support required by the panel. + * + * @since 4.0.0 + * + * @return bool False if theme doesn't support the panel or the user doesn't have the capability. + */ + public final function check_capabilities() { + if ( $this->capability && ! call_user_func_array( 'current_user_can', (array) $this->capability ) ) { + return false; + } + + if ( $this->theme_supports && ! call_user_func_array( 'current_theme_supports', (array) $this->theme_supports ) ) { + return false; + } + + return true; + } + + /** + * Get the panel's content template for insertion into the Customizer pane. + * + * @since 4.1.0 + * + * @return string Content for the panel. + */ + public final function get_content() { + ob_start(); + $this->maybe_render(); + $template = trim( ob_get_contents() ); + ob_end_clean(); + return $template; + } + + /** + * Check capabilities and render the panel. + * + * @since 4.0.0 + */ + public final function maybe_render() { + if ( ! $this->check_capabilities() ) { + return; + } + + /** + * Fires before rendering a Customizer panel. + * + * @since 4.0.0 + * + * @param WP_Customize_Panel $this WP_Customize_Panel instance. + */ + do_action( 'customize_render_panel', $this ); + + /** + * Fires before rendering a specific Customizer panel. + * + * The dynamic portion of the hook name, `$this->id`, refers to + * the ID of the specific Customizer panel to be rendered. + * + * @since 4.0.0 + */ + do_action( "customize_render_panel_{$this->id}" ); + + $this->render(); + } + + /** + * Render the panel container, and then its contents. + * + * @since 4.0.0 + * @access protected + */ + protected function render() { + $classes = 'accordion-section control-section control-panel control-panel-' . $this->type; + ?> + <li id="accordion-panel-<?php echo esc_attr( $this->id ); ?>" class="<?php echo esc_attr( $classes ); ?>"> + <h3 class="accordion-section-title" tabindex="0"> + <?php echo esc_html( $this->title ); ?> + <span class="screen-reader-text"><?php _e( 'Press return or enter to open this panel' ); ?></span> + </h3> + <ul class="accordion-sub-container control-panel-content"> + <?php $this->render_content(); ?> + </ul> + </li> + <?php + } + + /** + * Render the sections that have been added to the panel. + * + * @since 4.1.0 + * @access protected + */ + protected function render_content() { + ?> + <li class="panel-meta accordion-section control-section<?php if ( empty( $this->description ) ) { echo ' cannot-expand'; } ?>"> + <div class="accordion-section-title" tabindex="0"> + <span class="preview-notice"><?php + /* translators: %s is the site/panel title in the Customizer */ + echo sprintf( __( 'You are customizing %s' ), '<strong class="panel-title">' . esc_html( $this->title ) . '</strong>' ); + ?></span> + </div> + <?php if ( ! empty( $this->description ) ) : ?> + <div class="accordion-section-content description"> + <?php echo $this->description; ?> + </div> + <?php endif; ?> + </li> + <?php + } +} diff --git a/wp-includes/class-wp-customize-section.php b/wp-includes/class-wp-customize-section.php new file mode 100644 index 0000000..b033203 --- /dev/null +++ b/wp-includes/class-wp-customize-section.php @@ -0,0 +1,370 @@ +<?php +/** + * Customize Section Class. + * + * A UI container for controls, managed by the WP_Customize_Manager. + * + * @package WordPress + * @subpackage Customize + * @since 3.4.0 + */ +class WP_Customize_Section { + + /** + * Incremented with each new class instantiation, then stored in $instance_number. + * + * Used when sorting two instances whose priorities are equal. + * + * @since 4.1.0 + * @access protected + * @var int + */ + protected static $instance_count = 0; + + /** + * Order in which this instance was created in relation to other instances. + * + * @since 4.1.0 + * @access public + * @var int + */ + public $instance_number; + + /** + * WP_Customize_Manager instance. + * + * @since 3.4.0 + * @access public + * @var WP_Customize_Manager + */ + public $manager; + + /** + * Unique identifier. + * + * @since 3.4.0 + * @access public + * @var string + */ + public $id; + + /** + * Priority of the section which informs load order of sections. + * + * @since 3.4.0 + * @access public + * @var integer + */ + public $priority = 160; + + /** + * Panel in which to show the section, making it a sub-section. + * + * @since 4.0.0 + * @access public + * @var string + */ + public $panel = ''; + + /** + * Capability required for the section. + * + * @since 3.4.0 + * @access public + * @var string + */ + public $capability = 'edit_theme_options'; + + /** + * Theme feature support for the section. + * + * @since 3.4.0 + * @access public + * @var string|array + */ + public $theme_supports = ''; + + /** + * Title of the section to show in UI. + * + * @since 3.4.0 + * @access public + * @var string + */ + public $title = ''; + + /** + * Description to show in the UI. + * + * @since 3.4.0 + * @access public + * @var string + */ + public $description = ''; + + /** + * Customizer controls for this section. + * + * @since 3.4.0 + * @access public + * @var array + */ + public $controls; + + /** + * Type of this section. + * + * @since 4.1.0 + * @access public + * @var string + */ + public $type = 'default'; + + /** + * Active callback. + * + * @since 4.1.0 + * @access public + * + * @see WP_Customize_Section::active() + * + * @var callable Callback is called with one argument, the instance of + * {@see WP_Customize_Section}, and returns bool to indicate + * whether the section is active (such as it relates to the URL + * currently being previewed). + */ + public $active_callback = ''; + + /** + * Constructor. + * + * Any supplied $args override class property defaults. + * + * @since 3.4.0 + * + * @param WP_Customize_Manager $manager Customizer bootstrap instance. + * @param string $id An specific ID of the section. + * @param array $args Section arguments. + */ + public function __construct( $manager, $id, $args = array() ) { + $keys = array_keys( get_object_vars( $this ) ); + foreach ( $keys as $key ) { + if ( isset( $args[ $key ] ) ) { + $this->$key = $args[ $key ]; + } + } + + $this->manager = $manager; + $this->id = $id; + if ( empty( $this->active_callback ) ) { + $this->active_callback = array( $this, 'active_callback' ); + } + self::$instance_count += 1; + $this->instance_number = self::$instance_count; + + $this->controls = array(); // Users cannot customize the $controls array. + + return $this; + } + + /** + * Check whether section is active to current Customizer preview. + * + * @since 4.1.0 + * @access public + * + * @return bool Whether the section is active to the current preview. + */ + public final function active() { + $section = $this; + $active = call_user_func( $this->active_callback, $this ); + + /** + * Filter response of {@see WP_Customize_Section::active()}. + * + * @since 4.1.0 + * + * @param bool $active Whether the Customizer section is active. + * @param WP_Customize_Section $section {@see WP_Customize_Section} instance. + */ + $active = apply_filters( 'customize_section_active', $active, $section ); + + return $active; + } + + /** + * Default callback used when invoking {@see WP_Customize_Section::active()}. + * + * Subclasses can override this with their specific logic, or they may provide + * an 'active_callback' argument to the constructor. + * + * @since 4.1.0 + * @access public + * + * @return bool Always true. + */ + public function active_callback() { + return true; + } + + /** + * Gather the parameters passed to client JavaScript via JSON. + * + * @since 4.1.0 + * + * @return array The array to be exported to the client as JSON. + */ + public function json() { + $array = wp_array_slice_assoc( (array) $this, array( 'title', 'description', 'priority', 'panel', 'type' ) ); + $array['content'] = $this->get_content(); + $array['active'] = $this->active(); + $array['instanceNumber'] = $this->instance_number; + return $array; + } + + /** + * Checks required user capabilities and whether the theme has the + * feature support required by the section. + * + * @since 3.4.0 + * + * @return bool False if theme doesn't support the section or user doesn't have the capability. + */ + public final function check_capabilities() { + if ( $this->capability && ! call_user_func_array( 'current_user_can', (array) $this->capability ) ) { + return false; + } + + if ( $this->theme_supports && ! call_user_func_array( 'current_theme_supports', (array) $this->theme_supports ) ) { + return false; + } + + return true; + } + + /** + * Get the section's content template for insertion into the Customizer pane. + * + * @since 4.1.0 + * + * @return string Contents of the section. + */ + public final function get_content() { + ob_start(); + $this->maybe_render(); + $template = trim( ob_get_contents() ); + ob_end_clean(); + return $template; + } + + /** + * Check capabilities and render the section. + * + * @since 3.4.0 + */ + public final function maybe_render() { + if ( ! $this->check_capabilities() ) { + return; + } + + /** + * Fires before rendering a Customizer section. + * + * @since 3.4.0 + * + * @param WP_Customize_Section $this WP_Customize_Section instance. + */ + do_action( 'customize_render_section', $this ); + /** + * Fires before rendering a specific Customizer section. + * + * The dynamic portion of the hook name, `$this->id`, refers to the ID + * of the specific Customizer section to be rendered. + * + * @since 3.4.0 + */ + do_action( "customize_render_section_{$this->id}" ); + + $this->render(); + } + + /** + * Render the section, and the controls that have been added to it. + * + * @since 3.4.0 + */ + protected function render() { + $classes = 'accordion-section control-section control-section-' . $this->type; + ?> + <li id="accordion-section-<?php echo esc_attr( $this->id ); ?>" class="<?php echo esc_attr( $classes ); ?>"> + <h3 class="accordion-section-title" tabindex="0"> + <?php echo esc_html( $this->title ); ?> + <span class="screen-reader-text"><?php _e( 'Press return or enter to expand' ); ?></span> + </h3> + <ul class="accordion-section-content"> + <?php if ( ! empty( $this->description ) ) : ?> + <li class="customize-section-description-container"> + <p class="description customize-section-description"><?php echo $this->description; ?></p> + </li> + <?php endif; ?> + </ul> + </li> + <?php + } +} + +/** + * Customizer section representing widget area (sidebar). + * + * @package WordPress + * @subpackage Customize + * + * @since 4.1.0 + * + * @see WP_Customize_Section + */ +class WP_Customize_Sidebar_Section extends WP_Customize_Section { + + /** + * Type of this section. + * + * @since 4.1.0 + * @access public + * @var string + */ + public $type = 'sidebar'; + + /** + * Unique identifier. + * + * @since 4.1.0 + * @access public + * @var string + */ + public $sidebar_id; + + /** + * Gather the parameters passed to client JavaScript via JSON. + * + * @since 4.1.0 + * + * @return array The array to be exported to the client as JSON. + */ + public function json() { + $json = parent::json(); + $json['sidebarId'] = $this->sidebar_id; + return $json; + } + + /** + * Whether the current sidebar is rendered on the page. + * + * @since 4.1.0 + * @access public + * + * @return bool Whether sidebar is rendered. + */ + public function active_callback() { + return $this->manager->widgets->is_sidebar_rendered( $this->sidebar_id ); + } +} diff --git a/wp-includes/class-wp-customize-setting.php b/wp-includes/class-wp-customize-setting.php new file mode 100644 index 0000000..d8bf1e2 --- /dev/null +++ b/wp-includes/class-wp-customize-setting.php @@ -0,0 +1,586 @@ +<?php +/** + * Customize Setting Class. + * + * Handles saving and sanitizing of settings. + * + * @package WordPress + * @subpackage Customize + * @since 3.4.0 + */ +class WP_Customize_Setting { + /** + * @access public + * @var WP_Customize_Manager + */ + public $manager; + + /** + * @access public + * @var string + */ + public $id; + + /** + * @access public + * @var string + */ + public $type = 'theme_mod'; + + /** + * Capability required to edit this setting. + * + * @var string + */ + public $capability = 'edit_theme_options'; + + /** + * Feature a theme is required to support to enable this setting. + * + * @access public + * @var string + */ + public $theme_supports = ''; + public $default = ''; + public $transport = 'refresh'; + + /** + * Server-side sanitization callback for the setting's value. + * + * @var callback + */ + public $sanitize_callback = ''; + public $sanitize_js_callback = ''; + + protected $id_data = array(); + + /** + * Cached and sanitized $_POST value for the setting. + * + * @access private + * @var mixed + */ + private $_post_value; + + /** + * Constructor. + * + * Any supplied $args override class property defaults. + * + * @since 3.4.0 + * + * @param WP_Customize_Manager $manager + * @param string $id An specific ID of the setting. Can be a + * theme mod or option name. + * @param array $args Setting arguments. + * @return WP_Customize_Setting $setting + */ + public function __construct( $manager, $id, $args = array() ) { + $keys = array_keys( get_object_vars( $this ) ); + foreach ( $keys as $key ) { + if ( isset( $args[ $key ] ) ) + $this->$key = $args[ $key ]; + } + + $this->manager = $manager; + $this->id = $id; + + // Parse the ID for array keys. + $this->id_data[ 'keys' ] = preg_split( '/\[/', str_replace( ']', '', $this->id ) ); + $this->id_data[ 'base' ] = array_shift( $this->id_data[ 'keys' ] ); + + // Rebuild the ID. + $this->id = $this->id_data[ 'base' ]; + if ( ! empty( $this->id_data[ 'keys' ] ) ) + $this->id .= '[' . implode( '][', $this->id_data[ 'keys' ] ) . ']'; + + if ( $this->sanitize_callback ) + add_filter( "customize_sanitize_{$this->id}", $this->sanitize_callback, 10, 2 ); + + if ( $this->sanitize_js_callback ) + add_filter( "customize_sanitize_js_{$this->id}", $this->sanitize_js_callback, 10, 2 ); + + return $this; + } + + protected $_original_value; + + /** + * Handle previewing the setting. + * + * @since 3.4.0 + */ + public function preview() { + if ( ! isset( $this->_original_value ) ) { + $this->_original_value = $this->value(); + } + + switch( $this->type ) { + case 'theme_mod' : + add_filter( 'theme_mod_' . $this->id_data[ 'base' ], array( $this, '_preview_filter' ) ); + break; + case 'option' : + if ( empty( $this->id_data[ 'keys' ] ) ) + add_filter( 'pre_option_' . $this->id_data[ 'base' ], array( $this, '_preview_filter' ) ); + else { + add_filter( 'option_' . $this->id_data[ 'base' ], array( $this, '_preview_filter' ) ); + add_filter( 'default_option_' . $this->id_data[ 'base' ], array( $this, '_preview_filter' ) ); + } + break; + default : + + /** + * Fires when the {@see WP_Customize_Setting::preview()} method is called for settings + * not handled as theme_mods or options. + * + * The dynamic portion of the hook name, `$this->id`, refers to the setting ID. + * + * @since 3.4.0 + * + * @param WP_Customize_Setting $this {@see WP_Customize_Setting} instance. + */ + do_action( "customize_preview_{$this->id}", $this ); + + /** + * Fires when the {@see WP_Customize_Setting::preview()} method is called for settings + * not handled as theme_mods or options. + * + * The dynamic portion of the hook name, `$this->type`, refers to the setting type. + * + * @since 4.1.0 + * + * @param WP_Customize_Setting $this {@see WP_Customize_Setting} instance. + */ + do_action( "customize_preview_{$this->type}", $this ); + } + } + + /** + * Callback function to filter the theme mods and options. + * + * @since 3.4.0 + * @uses WP_Customize_Setting::multidimensional_replace() + * + * @param mixed $original Old value. + * @return mixed New or old value. + */ + public function _preview_filter( $original ) { + $undefined = new stdClass(); // symbol hack + $post_value = $this->manager->post_value( $this, $undefined ); + if ( $undefined === $post_value ) { + $value = $this->_original_value; + } else { + $value = $post_value; + } + + return $this->multidimensional_replace( $original, $this->id_data['keys'], $value ); + } + + /** + * Check user capabilities and theme supports, and then save + * the value of the setting. + * + * @since 3.4.0 + * + * @return false|null False if cap check fails or value isn't set. + */ + public final function save() { + $value = $this->post_value(); + + if ( ! $this->check_capabilities() || ! isset( $value ) ) + return false; + + /** + * Fires when the WP_Customize_Setting::save() method is called. + * + * The dynamic portion of the hook name, `$this->id_data['base']` refers to + * the base slug of the setting name. + * + * @since 3.4.0 + * + * @param WP_Customize_Setting $this {@see WP_Customize_Setting} instance. + */ + do_action( 'customize_save_' . $this->id_data[ 'base' ], $this ); + + $this->update( $value ); + } + + /** + * Fetch and sanitize the $_POST value for the setting. + * + * @since 3.4.0 + * + * @param mixed $default A default value which is used as a fallback. Default is null. + * @return mixed The default value on failure, otherwise the sanitized value. + */ + public final function post_value( $default = null ) { + // Check for a cached value + if ( isset( $this->_post_value ) ) + return $this->_post_value; + + // Call the manager for the post value + $result = $this->manager->post_value( $this ); + + if ( isset( $result ) ) + return $this->_post_value = $result; + else + return $default; + } + + /** + * Sanitize an input. + * + * @since 3.4.0 + * + * @param mixed $value The value to sanitize. + * @return mixed Null if an input isn't valid, otherwise the sanitized value. + */ + public function sanitize( $value ) { + $value = wp_unslash( $value ); + + /** + * Filter a Customize setting value in un-slashed form. + * + * @since 3.4.0 + * + * @param mixed $value Value of the setting. + * @param WP_Customize_Setting $this WP_Customize_Setting instance. + */ + return apply_filters( "customize_sanitize_{$this->id}", $value, $this ); + } + + /** + * Save the value of the setting, using the related API. + * + * @since 3.4.0 + * + * @param mixed $value The value to update. + * @return mixed The result of saving the value. + */ + protected function update( $value ) { + switch( $this->type ) { + case 'theme_mod' : + return $this->_update_theme_mod( $value ); + + case 'option' : + return $this->_update_option( $value ); + + default : + + /** + * Fires when the {@see WP_Customize_Setting::update()} method is called for settings + * not handled as theme_mods or options. + * + * The dynamic portion of the hook name, `$this->type`, refers to the type of setting. + * + * @since 3.4.0 + * + * @param mixed $value Value of the setting. + * @param WP_Customize_Setting $this WP_Customize_Setting instance. + */ + return do_action( 'customize_update_' . $this->type, $value, $this ); + } + } + + /** + * Update the theme mod from the value of the parameter. + * + * @since 3.4.0 + * + * @param mixed $value The value to update. + * @return mixed The result of saving the value. + */ + protected function _update_theme_mod( $value ) { + // Handle non-array theme mod. + if ( empty( $this->id_data[ 'keys' ] ) ) + return set_theme_mod( $this->id_data[ 'base' ], $value ); + + // Handle array-based theme mod. + $mods = get_theme_mod( $this->id_data[ 'base' ] ); + $mods = $this->multidimensional_replace( $mods, $this->id_data[ 'keys' ], $value ); + if ( isset( $mods ) ) + return set_theme_mod( $this->id_data[ 'base' ], $mods ); + } + + /** + * Update the option from the value of the setting. + * + * @since 3.4.0 + * + * @param mixed $value The value to update. + * @return bool|null The result of saving the value. + */ + protected function _update_option( $value ) { + // Handle non-array option. + if ( empty( $this->id_data[ 'keys' ] ) ) + return update_option( $this->id_data[ 'base' ], $value ); + + // Handle array-based options. + $options = get_option( $this->id_data[ 'base' ] ); + $options = $this->multidimensional_replace( $options, $this->id_data[ 'keys' ], $value ); + if ( isset( $options ) ) + return update_option( $this->id_data[ 'base' ], $options ); + } + + /** + * Fetch the value of the setting. + * + * @since 3.4.0 + * + * @return mixed The value. + */ + public function value() { + // Get the callback that corresponds to the setting type. + switch( $this->type ) { + case 'theme_mod' : + $function = 'get_theme_mod'; + break; + case 'option' : + $function = 'get_option'; + break; + default : + + /** + * Filter a Customize setting value not handled as a theme_mod or option. + * + * The dynamic portion of the hook name, `$this->id_date['base']`, refers to + * the base slug of the setting name. + * + * For settings handled as theme_mods or options, see those corresponding + * functions for available hooks. + * + * @since 3.4.0 + * + * @param mixed $default The setting default value. Default empty. + */ + return apply_filters( 'customize_value_' . $this->id_data[ 'base' ], $this->default ); + } + + // Handle non-array value + if ( empty( $this->id_data[ 'keys' ] ) ) + return $function( $this->id_data[ 'base' ], $this->default ); + + // Handle array-based value + $values = $function( $this->id_data[ 'base' ] ); + return $this->multidimensional_get( $values, $this->id_data[ 'keys' ], $this->default ); + } + + /** + * Sanitize the setting's value for use in JavaScript. + * + * @since 3.4.0 + * + * @return mixed The requested escaped value. + */ + public function js_value() { + + /** + * Filter a Customize setting value for use in JavaScript. + * + * The dynamic portion of the hook name, `$this->id`, refers to the setting ID. + * + * @since 3.4.0 + * + * @param mixed $value The setting value. + * @param WP_Customize_Setting $this {@see WP_Customize_Setting} instance. + */ + $value = apply_filters( "customize_sanitize_js_{$this->id}", $this->value(), $this ); + + if ( is_string( $value ) ) + return html_entity_decode( $value, ENT_QUOTES, 'UTF-8'); + + return $value; + } + + /** + * Validate user capabilities whether the theme supports the setting. + * + * @since 3.4.0 + * + * @return bool False if theme doesn't support the setting or user can't change setting, otherwise true. + */ + public final function check_capabilities() { + if ( $this->capability && ! call_user_func_array( 'current_user_can', (array) $this->capability ) ) + return false; + + if ( $this->theme_supports && ! call_user_func_array( 'current_theme_supports', (array) $this->theme_supports ) ) + return false; + + return true; + } + + /** + * Multidimensional helper function. + * + * @since 3.4.0 + * + * @param $root + * @param $keys + * @param bool $create Default is false. + * @return null|array Keys are 'root', 'node', and 'key'. + */ + final protected function multidimensional( &$root, $keys, $create = false ) { + if ( $create && empty( $root ) ) + $root = array(); + + if ( ! isset( $root ) || empty( $keys ) ) + return; + + $last = array_pop( $keys ); + $node = &$root; + + foreach ( $keys as $key ) { + if ( $create && ! isset( $node[ $key ] ) ) + $node[ $key ] = array(); + + if ( ! is_array( $node ) || ! isset( $node[ $key ] ) ) + return; + + $node = &$node[ $key ]; + } + + if ( $create ) { + if ( ! is_array( $node ) ) { + // account for an array overriding a string or object value + $node = array(); + } + if ( ! isset( $node[ $last ] ) ) { + $node[ $last ] = array(); + } + } + + if ( ! isset( $node[ $last ] ) ) + return; + + return array( + 'root' => &$root, + 'node' => &$node, + 'key' => $last, + ); + } + + /** + * Will attempt to replace a specific value in a multidimensional array. + * + * @since 3.4.0 + * + * @param $root + * @param $keys + * @param mixed $value The value to update. + * @return + */ + final protected function multidimensional_replace( $root, $keys, $value ) { + if ( ! isset( $value ) ) + return $root; + elseif ( empty( $keys ) ) // If there are no keys, we're replacing the root. + return $value; + + $result = $this->multidimensional( $root, $keys, true ); + + if ( isset( $result ) ) + $result['node'][ $result['key'] ] = $value; + + return $root; + } + + /** + * Will attempt to fetch a specific value from a multidimensional array. + * + * @since 3.4.0 + * + * @param $root + * @param $keys + * @param mixed $default A default value which is used as a fallback. Default is null. + * @return mixed The requested value or the default value. + */ + final protected function multidimensional_get( $root, $keys, $default = null ) { + if ( empty( $keys ) ) // If there are no keys, test the root. + return isset( $root ) ? $root : $default; + + $result = $this->multidimensional( $root, $keys ); + return isset( $result ) ? $result['node'][ $result['key'] ] : $default; + } + + /** + * Will attempt to check if a specific value in a multidimensional array is set. + * + * @since 3.4.0 + * + * @param $root + * @param $keys + * @return bool True if value is set, false if not. + */ + final protected function multidimensional_isset( $root, $keys ) { + $result = $this->multidimensional_get( $root, $keys ); + return isset( $result ); + } +} + +/** + * A setting that is used to filter a value, but will not save the results. + * + * Results should be properly handled using another setting or callback. + * + * @package WordPress + * @subpackage Customize + * @since 3.4.0 + */ +class WP_Customize_Filter_Setting extends WP_Customize_Setting { + + /** + * @since 3.4.0 + */ + public function update( $value ) {} +} + +/** + * A setting that is used to filter a value, but will not save the results. + * + * Results should be properly handled using another setting or callback. + * + * @package WordPress + * @subpackage Customize + * @since 3.4.0 + */ +final class WP_Customize_Header_Image_Setting extends WP_Customize_Setting { + public $id = 'header_image_data'; + + /** + * @since 3.4.0 + * + * @param $value + */ + public function update( $value ) { + global $custom_image_header; + + // If the value doesn't exist (removed or random), + // use the header_image value. + if ( ! $value ) + $value = $this->manager->get_setting('header_image')->post_value(); + + if ( is_array( $value ) && isset( $value['choice'] ) ) + $custom_image_header->set_header_image( $value['choice'] ); + else + $custom_image_header->set_header_image( $value ); + } +} + +/** + * Class WP_Customize_Background_Image_Setting + * + * @package WordPress + * @subpackage Customize + * @since 3.4.0 + */ +final class WP_Customize_Background_Image_Setting extends WP_Customize_Setting { + public $id = 'background_image_thumb'; + + /** + * @since 3.4.0 + * + * @param $value + */ + public function update( $value ) { + remove_theme_mod( 'background_image_thumb' ); + } +} diff --git a/wp-includes/class-wp-customize-widgets.php b/wp-includes/class-wp-customize-widgets.php new file mode 100644 index 0000000..ad5742f --- /dev/null +++ b/wp-includes/class-wp-customize-widgets.php @@ -0,0 +1,1560 @@ +<?php +/** + * Customize Widgets Class + * + * Implements widget management in the Customizer. + * + * @package WordPress + * @subpackage Customize + * @since 3.9.0 + */ +final class WP_Customize_Widgets { + + /** + * WP_Customize_Manager instance. + * + * @since 3.9.0 + * @access public + * @var WP_Customize_Manager + */ + public $manager; + + /** + * All id_bases for widgets defined in core. + * + * @since 3.9.0 + * @access protected + * @var array + */ + protected $core_widget_id_bases = array( + 'archives', 'calendar', 'categories', 'links', 'meta', + 'nav_menu', 'pages', 'recent-comments', 'recent-posts', + 'rss', 'search', 'tag_cloud', 'text', + ); + + /** + * @since 3.9.0 + * @access protected + * @var + */ + protected $_customized; + + /** + * @since 3.9.0 + * @access protected + * @var array + */ + protected $_prepreview_added_filters = array(); + + /** + * @since 3.9.0 + * @access protected + * @var array + */ + protected $rendered_sidebars = array(); + + /** + * @since 3.9.0 + * @access protected + * @var array + */ + protected $rendered_widgets = array(); + + /** + * @since 3.9.0 + * @access protected + * @var array + */ + protected $old_sidebars_widgets = array(); + + /** + * Initial loader. + * + * @since 3.9.0 + * @access public + * + * @param WP_Customize_Manager $manager Customize manager bootstrap instance. + */ + public function __construct( $manager ) { + $this->manager = $manager; + + add_action( 'after_setup_theme', array( $this, 'setup_widget_addition_previews' ) ); + add_action( 'wp_loaded', array( $this, 'override_sidebars_widgets_for_theme_switch' ) ); + add_action( 'customize_controls_init', array( $this, 'customize_controls_init' ) ); + add_action( 'customize_register', array( $this, 'schedule_customize_register' ), 1 ); + add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_scripts' ) ); + add_action( 'customize_controls_print_styles', array( $this, 'print_styles' ) ); + add_action( 'customize_controls_print_scripts', array( $this, 'print_scripts' ) ); + add_action( 'customize_controls_print_footer_scripts', array( $this, 'print_footer_scripts' ) ); + add_action( 'customize_controls_print_footer_scripts', array( $this, 'output_widget_control_templates' ) ); + add_action( 'customize_preview_init', array( $this, 'customize_preview_init' ) ); + + add_action( 'dynamic_sidebar', array( $this, 'tally_rendered_widgets' ) ); + add_filter( 'is_active_sidebar', array( $this, 'tally_sidebars_via_is_active_sidebar_calls' ), 10, 2 ); + add_filter( 'dynamic_sidebar_has_widgets', array( $this, 'tally_sidebars_via_dynamic_sidebar_calls' ), 10, 2 ); + } + + /** + * Get an unslashed post value or return a default. + * + * @since 3.9.0 + * + * @access protected + * + * @param string $name Post value. + * @param mixed $default Default post value. + * @return mixed Unslashed post value or default value. + */ + protected function get_post_value( $name, $default = null ) { + if ( ! isset( $_POST[ $name ] ) ) { + return $default; + } + + return wp_unslash( $_POST[$name] ); + } + + /** + * Set up widget addition previews. + * + * Since the widgets get registered on 'widgets_init' before the Customizer + * settings are set up on 'customize_register', we have to filter the options + * similarly to how the setting previewer will filter the options later. + * + * @since 3.9.0 + * + * @access public + */ + public function setup_widget_addition_previews() { + $is_customize_preview = false; + + if ( ! empty( $this->manager ) && ! is_admin() && 'on' === $this->get_post_value( 'wp_customize' ) ) { + $is_customize_preview = check_ajax_referer( 'preview-customize_' . $this->manager->get_stylesheet(), 'nonce', false ); + } + + $is_ajax_widget_update = false; + if ( $this->manager->doing_ajax() && 'update-widget' === $this->get_post_value( 'action' ) ) { + $is_ajax_widget_update = check_ajax_referer( 'update-widget', 'nonce', false ); + } + + $is_ajax_customize_save = false; + if ( $this->manager->doing_ajax() && 'customize_save' === $this->get_post_value( 'action' ) ) { + $is_ajax_customize_save = check_ajax_referer( 'save-customize_' . $this->manager->get_stylesheet(), 'nonce', false ); + } + + $is_valid_request = ( $is_ajax_widget_update || $is_customize_preview || $is_ajax_customize_save ); + if ( ! $is_valid_request ) { + return; + } + + // Input from Customizer preview. + if ( isset( $_POST['customized'] ) ) { + $this->_customized = json_decode( $this->get_post_value( 'customized' ), true ); + } else { // Input from ajax widget update request. + $this->_customized = array(); + $id_base = $this->get_post_value( 'id_base' ); + $widget_number = $this->get_post_value( 'widget_number', false ); + $option_name = 'widget_' . $id_base; + $this->_customized[ $option_name ] = array(); + if ( preg_match( '/^[0-9]+$/', $widget_number ) ) { + $option_name .= '[' . $widget_number . ']'; + $this->_customized[ $option_name ][ $widget_number ] = array(); + } + } + + $function = array( $this, 'prepreview_added_sidebars_widgets' ); + + $hook = 'option_sidebars_widgets'; + add_filter( $hook, $function ); + $this->_prepreview_added_filters[] = compact( 'hook', 'function' ); + + $hook = 'default_option_sidebars_widgets'; + add_filter( $hook, $function ); + $this->_prepreview_added_filters[] = compact( 'hook', 'function' ); + + $function = array( $this, 'prepreview_added_widget_instance' ); + foreach ( $this->_customized as $setting_id => $value ) { + if ( preg_match( '/^(widget_.+?)(?:\[(\d+)\])?$/', $setting_id, $matches ) ) { + $option = $matches[1]; + + $hook = sprintf( 'option_%s', $option ); + if ( ! has_filter( $hook, $function ) ) { + add_filter( $hook, $function ); + $this->_prepreview_added_filters[] = compact( 'hook', 'function' ); + } + + $hook = sprintf( 'default_option_%s', $option ); + if ( ! has_filter( $hook, $function ) ) { + add_filter( $hook, $function ); + $this->_prepreview_added_filters[] = compact( 'hook', 'function' ); + } + + /* + * Make sure the option is registered so that the update_option() + * won't fail due to the filters providing a default value, which + * causes the update_option() to get confused. + */ + add_option( $option, array() ); + } + } + } + + /** + * Ensure that newly-added widgets will appear in the widgets_sidebars. + * + * This is necessary because the Customizer's setting preview filters + * are added after the widgets_init action, which is too late for the + * widgets to be set up properly. + * + * @since 3.9.0 + * @access public + * + * @param array $sidebars_widgets Associative array of sidebars and their widgets. + * @return array Filtered array of sidebars and their widgets. + */ + public function prepreview_added_sidebars_widgets( $sidebars_widgets ) { + foreach ( $this->_customized as $setting_id => $value ) { + if ( preg_match( '/^sidebars_widgets\[(.+?)\]$/', $setting_id, $matches ) ) { + $sidebar_id = $matches[1]; + $sidebars_widgets[ $sidebar_id ] = $value; + } + } + return $sidebars_widgets; + } + + /** + * Ensure newly-added widgets have empty instances so they + * will be recognized. + * + * This is necessary because the Customizer's setting preview + * filters are added after the widgets_init action, which is + * too late for the widgets to be set up properly. + * + * @since 3.9.0 + * @access public + * + * @param array|bool|mixed $value Widget instance(s), false if open was empty. + * @return array|mixed Widget instance(s) with additions. + */ + public function prepreview_added_widget_instance( $value = false ) { + if ( ! preg_match( '/^(?:default_)?option_(widget_(.+))/', current_filter(), $matches ) ) { + return $value; + } + $id_base = $matches[2]; + + foreach ( $this->_customized as $setting_id => $setting ) { + $parsed_setting_id = $this->parse_widget_setting_id( $setting_id ); + if ( is_wp_error( $parsed_setting_id ) || $id_base !== $parsed_setting_id['id_base'] ) { + continue; + } + $widget_number = $parsed_setting_id['number']; + + if ( is_null( $widget_number ) ) { + // Single widget. + if ( false === $value ) { + $value = array(); + } + } else { + // Multi widget. + if ( empty( $value ) ) { + $value = array( '_multiwidget' => 1 ); + } + if ( ! isset( $value[ $widget_number ] ) ) { + $value[ $widget_number ] = array(); + } + } + } + + return $value; + } + + /** + * Remove pre-preview filters. + * + * Removes filters added in setup_widget_addition_previews() + * to ensure widgets are populating the options during + * 'widgets_init'. + * + * @since 3.9.0 + * @access public + */ + public function remove_prepreview_filters() { + foreach ( $this->_prepreview_added_filters as $prepreview_added_filter ) { + remove_filter( $prepreview_added_filter['hook'], $prepreview_added_filter['function'] ); + } + $this->_prepreview_added_filters = array(); + } + + /** + * Override sidebars_widgets for theme switch. + * + * When switching a theme via the Customizer, supply any previously-configured + * sidebars_widgets from the target theme as the initial sidebars_widgets + * setting. Also store the old theme's existing settings so that they can + * be passed along for storing in the sidebars_widgets theme_mod when the + * theme gets switched. + * + * @since 3.9.0 + * @access public + */ + public function override_sidebars_widgets_for_theme_switch() { + global $sidebars_widgets; + + if ( $this->manager->doing_ajax() || $this->manager->is_theme_active() ) { + return; + } + + $this->old_sidebars_widgets = wp_get_sidebars_widgets(); + add_filter( 'customize_value_old_sidebars_widgets_data', array( $this, 'filter_customize_value_old_sidebars_widgets_data' ) ); + + // retrieve_widgets() looks at the global $sidebars_widgets + $sidebars_widgets = $this->old_sidebars_widgets; + $sidebars_widgets = retrieve_widgets( 'customize' ); + add_filter( 'option_sidebars_widgets', array( $this, 'filter_option_sidebars_widgets_for_theme_switch' ), 1 ); + } + + /** + * Filter old_sidebars_widgets_data Customizer setting. + * + * When switching themes, filter the Customizer setting + * old_sidebars_widgets_data to supply initial $sidebars_widgets before they + * were overridden by retrieve_widgets(). The value for + * old_sidebars_widgets_data gets set in the old theme's sidebars_widgets + * theme_mod. + * + * @see WP_Customize_Widgets::handle_theme_switch() + * @since 3.9.0 + * @access public + * + * @param array $old_sidebars_widgets + */ + public function filter_customize_value_old_sidebars_widgets_data( $old_sidebars_widgets ) { + return $this->old_sidebars_widgets; + } + + /** + * Filter sidebars_widgets option for theme switch. + * + * When switching themes, the retrieve_widgets() function is run when the + * Customizer initializes, and then the new sidebars_widgets here get + * supplied as the default value for the sidebars_widgets option. + * + * @see WP_Customize_Widgets::handle_theme_switch() + * @since 3.9.0 + * @access public + * + * @param array $sidebars_widgets + */ + public function filter_option_sidebars_widgets_for_theme_switch( $sidebars_widgets ) { + $sidebars_widgets = $GLOBALS['sidebars_widgets']; + $sidebars_widgets['array_version'] = 3; + return $sidebars_widgets; + } + + /** + * Make sure all widgets get loaded into the Customizer. + * + * Note: these actions are also fired in wp_ajax_update_widget(). + * + * @since 3.9.0 + * @access public + */ + public function customize_controls_init() { + /** This action is documented in wp-admin/includes/ajax-actions.php */ + do_action( 'load-widgets.php' ); + + /** This action is documented in wp-admin/includes/ajax-actions.php */ + do_action( 'widgets.php' ); + + /** This action is documented in wp-admin/widgets.php */ + do_action( 'sidebar_admin_setup' ); + } + + /** + * Ensure widgets are available for all types of previews. + * + * When in preview, hook to 'customize_register' for settings + * after WordPress is loaded so that all filters have been + * initialized (e.g. Widget Visibility). + * + * @since 3.9.0 + * @access public + */ + public function schedule_customize_register() { + if ( is_admin() ) { // @todo for some reason, $wp_customize->is_preview() is true here? + $this->customize_register(); + } else { + add_action( 'wp', array( $this, 'customize_register' ) ); + } + } + + /** + * Register Customizer settings and controls for all sidebars and widgets. + * + * @since 3.9.0 + * @access public + */ + public function customize_register() { + global $wp_registered_widgets, $wp_registered_widget_controls, $wp_registered_sidebars; + + $sidebars_widgets = array_merge( + array( 'wp_inactive_widgets' => array() ), + array_fill_keys( array_keys( $GLOBALS['wp_registered_sidebars'] ), array() ), + wp_get_sidebars_widgets() + ); + + $new_setting_ids = array(); + + /* + * Register a setting for all widgets, including those which are active, + * inactive, and orphaned since a widget may get suppressed from a sidebar + * via a plugin (like Widget Visibility). + */ + foreach ( array_keys( $wp_registered_widgets ) as $widget_id ) { + $setting_id = $this->get_setting_id( $widget_id ); + $setting_args = $this->get_setting_args( $setting_id ); + + $setting_args['sanitize_callback'] = array( $this, 'sanitize_widget_instance' ); + $setting_args['sanitize_js_callback'] = array( $this, 'sanitize_widget_js_instance' ); + + $this->manager->add_setting( $setting_id, $setting_args ); + + $new_setting_ids[] = $setting_id; + } + + /* + * Add a setting which will be supplied for the theme's sidebars_widgets + * theme_mod when the the theme is switched. + */ + if ( ! $this->manager->is_theme_active() ) { + $setting_id = 'old_sidebars_widgets_data'; + $setting_args = $this->get_setting_args( $setting_id, array( + 'type' => 'global_variable', + ) ); + $this->manager->add_setting( $setting_id, $setting_args ); + } + + $this->manager->add_panel( 'widgets', array( + 'title' => __( 'Widgets' ), + 'description' => __( 'Widgets are independent sections of content that can be placed into widgetized areas provided by your theme (commonly called sidebars).' ), + 'priority' => 110, + ) ); + + foreach ( $sidebars_widgets as $sidebar_id => $sidebar_widget_ids ) { + if ( empty( $sidebar_widget_ids ) ) { + $sidebar_widget_ids = array(); + } + + $is_registered_sidebar = isset( $GLOBALS['wp_registered_sidebars'][$sidebar_id] ); + $is_inactive_widgets = ( 'wp_inactive_widgets' === $sidebar_id ); + $is_active_sidebar = ( $is_registered_sidebar && ! $is_inactive_widgets ); + + // Add setting for managing the sidebar's widgets. + if ( $is_registered_sidebar || $is_inactive_widgets ) { + $setting_id = sprintf( 'sidebars_widgets[%s]', $sidebar_id ); + $setting_args = $this->get_setting_args( $setting_id ); + + $setting_args['sanitize_callback'] = array( $this, 'sanitize_sidebar_widgets' ); + $setting_args['sanitize_js_callback'] = array( $this, 'sanitize_sidebar_widgets_js_instance' ); + + $this->manager->add_setting( $setting_id, $setting_args ); + $new_setting_ids[] = $setting_id; + + // Add section to contain controls. + $section_id = sprintf( 'sidebar-widgets-%s', $sidebar_id ); + if ( $is_active_sidebar ) { + + $section_args = array( + 'title' => $GLOBALS['wp_registered_sidebars'][ $sidebar_id ]['name'], + 'description' => $GLOBALS['wp_registered_sidebars'][ $sidebar_id ]['description'], + 'priority' => array_search( $sidebar_id, array_keys( $wp_registered_sidebars ) ), + 'panel' => 'widgets', + 'sidebar_id' => $sidebar_id, + ); + + /** + * Filter Customizer widget section arguments for a given sidebar. + * + * @since 3.9.0 + * + * @param array $section_args Array of Customizer widget section arguments. + * @param string $section_id Customizer section ID. + * @param int|string $sidebar_id Sidebar ID. + */ + $section_args = apply_filters( 'customizer_widgets_section_args', $section_args, $section_id, $sidebar_id ); + + $section = new WP_Customize_Sidebar_Section( $this->manager, $section_id, $section_args ); + $this->manager->add_section( $section ); + + $control = new WP_Widget_Area_Customize_Control( $this->manager, $setting_id, array( + 'section' => $section_id, + 'sidebar_id' => $sidebar_id, + 'priority' => count( $sidebar_widget_ids ), // place 'Add Widget' and 'Reorder' buttons at end. + ) ); + $new_setting_ids[] = $setting_id; + + $this->manager->add_control( $control ); + } + } + + // Add a control for each active widget (located in a sidebar). + foreach ( $sidebar_widget_ids as $i => $widget_id ) { + + // Skip widgets that may have gone away due to a plugin being deactivated. + if ( ! $is_active_sidebar || ! isset( $GLOBALS['wp_registered_widgets'][$widget_id] ) ) { + continue; + } + + $registered_widget = $GLOBALS['wp_registered_widgets'][$widget_id]; + $setting_id = $this->get_setting_id( $widget_id ); + $id_base = $GLOBALS['wp_registered_widget_controls'][$widget_id]['id_base']; + + $control = new WP_Widget_Form_Customize_Control( $this->manager, $setting_id, array( + 'label' => $registered_widget['name'], + 'section' => $section_id, + 'sidebar_id' => $sidebar_id, + 'widget_id' => $widget_id, + 'widget_id_base' => $id_base, + 'priority' => $i, + 'width' => $wp_registered_widget_controls[$widget_id]['width'], + 'height' => $wp_registered_widget_controls[$widget_id]['height'], + 'is_wide' => $this->is_wide_widget( $widget_id ), + ) ); + $this->manager->add_control( $control ); + } + } + + /* + * We have to register these settings later than customize_preview_init + * so that other filters have had a chance to run. + */ + if ( did_action( 'customize_preview_init' ) ) { + foreach ( $new_setting_ids as $new_setting_id ) { + $this->manager->get_setting( $new_setting_id )->preview(); + } + } + $this->remove_prepreview_filters(); + } + + /** + * Covert a widget_id into its corresponding Customizer setting ID (option name). + * + * @since 3.9.0 + * @access public + * + * @param string $widget_id Widget ID. + * @return string Maybe-parsed widget ID. + */ + public function get_setting_id( $widget_id ) { + $parsed_widget_id = $this->parse_widget_id( $widget_id ); + $setting_id = sprintf( 'widget_%s', $parsed_widget_id['id_base'] ); + + if ( ! is_null( $parsed_widget_id['number'] ) ) { + $setting_id .= sprintf( '[%d]', $parsed_widget_id['number'] ); + } + return $setting_id; + } + + /** + * Determine whether the widget is considered "wide". + * + * Core widgets which may have controls wider than 250, but can + * still be shown in the narrow Customizer panel. The RSS and Text + * widgets in Core, for example, have widths of 400 and yet they + * still render fine in the Customizer panel. This method will + * return all Core widgets as being not wide, but this can be + * overridden with the is_wide_widget_in_customizer filter. + * + * @since 3.9.0 + * @access public + * + * @param string $widget_id Widget ID. + * @return bool Whether or not the widget is a "wide" widget. + */ + public function is_wide_widget( $widget_id ) { + global $wp_registered_widget_controls; + + $parsed_widget_id = $this->parse_widget_id( $widget_id ); + $width = $wp_registered_widget_controls[$widget_id]['width']; + $is_core = in_array( $parsed_widget_id['id_base'], $this->core_widget_id_bases ); + $is_wide = ( $width > 250 && ! $is_core ); + + /** + * Filter whether the given widget is considered "wide". + * + * @since 3.9.0 + * + * @param bool $is_wide Whether the widget is wide, Default false. + * @param string $widget_id Widget ID. + */ + return apply_filters( 'is_wide_widget_in_customizer', $is_wide, $widget_id ); + } + + /** + * Covert a widget ID into its id_base and number components. + * + * @since 3.9.0 + * @access public + * + * @param string $widget_id Widget ID. + * @return array Array containing a widget's id_base and number components. + */ + public function parse_widget_id( $widget_id ) { + $parsed = array( + 'number' => null, + 'id_base' => null, + ); + + if ( preg_match( '/^(.+)-(\d+)$/', $widget_id, $matches ) ) { + $parsed['id_base'] = $matches[1]; + $parsed['number'] = intval( $matches[2] ); + } else { + // likely an old single widget + $parsed['id_base'] = $widget_id; + } + return $parsed; + } + + /** + * Convert a widget setting ID (option path) to its id_base and number components. + * + * @since 3.9.0 + * @access public + * + * @param string $setting_id Widget setting ID. + * @return WP_Error|array Array containing a widget's id_base and number components, + * or a WP_Error object. + */ + public function parse_widget_setting_id( $setting_id ) { + if ( ! preg_match( '/^(widget_(.+?))(?:\[(\d+)\])?$/', $setting_id, $matches ) ) { + return new WP_Error( 'widget_setting_invalid_id' ); + } + + $id_base = $matches[2]; + $number = isset( $matches[3] ) ? intval( $matches[3] ) : null; + + return compact( 'id_base', 'number' ); + } + + /** + * Call admin_print_styles-widgets.php and admin_print_styles hooks to + * allow custom styles from plugins. + * + * @since 3.9.0 + * @access public + */ + public function print_styles() { + /** This action is documented in wp-admin/admin-header.php */ + do_action( 'admin_print_styles-widgets.php' ); + + /** This action is documented in wp-admin/admin-header.php */ + do_action( 'admin_print_styles' ); + } + + /** + * Call admin_print_scripts-widgets.php and admin_print_scripts hooks to + * allow custom scripts from plugins. + * + * @since 3.9.0 + * @access public + */ + public function print_scripts() { + /** This action is documented in wp-admin/admin-header.php */ + do_action( 'admin_print_scripts-widgets.php' ); + + /** This action is documented in wp-admin/admin-header.php */ + do_action( 'admin_print_scripts' ); + } + + /** + * Enqueue scripts and styles for Customizer panel and export data to JavaScript. + * + * @since 3.9.0 + * @access public + */ + public function enqueue_scripts() { + wp_enqueue_style( 'customize-widgets' ); + wp_enqueue_script( 'customize-widgets' ); + + /** This action is documented in wp-admin/admin-header.php */ + do_action( 'admin_enqueue_scripts', 'widgets.php' ); + + /* + * Export available widgets with control_tpl removed from model + * since plugins need templates to be in the DOM. + */ + $available_widgets = array(); + + foreach ( $this->get_available_widgets() as $available_widget ) { + unset( $available_widget['control_tpl'] ); + $available_widgets[] = $available_widget; + } + + $widget_reorder_nav_tpl = sprintf( + '<div class="widget-reorder-nav"><span class="move-widget" tabindex="0">%1$s</span><span class="move-widget-down" tabindex="0">%2$s</span><span class="move-widget-up" tabindex="0">%3$s</span></div>', + __( 'Move to another area…' ), + __( 'Move down' ), + __( 'Move up' ) + ); + + $move_widget_area_tpl = str_replace( + array( '{description}', '{btn}' ), + array( + __( 'Select an area to move this widget into:' ), + _x( 'Move', 'Move widget' ), + ), + '<div class="move-widget-area"> + <p class="description">{description}</p> + <ul class="widget-area-select"> + <% _.each( sidebars, function ( sidebar ){ %> + <li class="" data-id="<%- sidebar.id %>" title="<%- sidebar.description %>" tabindex="0"><%- sidebar.name %></li> + <% }); %> + </ul> + <div class="move-widget-actions"> + <button class="move-widget-btn button-secondary" type="button">{btn}</button> + </div> + </div>' + ); + + global $wp_scripts; + + $settings = array( + 'nonce' => wp_create_nonce( 'update-widget' ), + 'registeredSidebars' => array_values( $GLOBALS['wp_registered_sidebars'] ), + 'registeredWidgets' => $GLOBALS['wp_registered_widgets'], + 'availableWidgets' => $available_widgets, // @todo Merge this with registered_widgets + 'l10n' => array( + 'saveBtnLabel' => __( 'Apply' ), + 'saveBtnTooltip' => __( 'Save and preview changes before publishing them.' ), + 'removeBtnLabel' => __( 'Remove' ), + 'removeBtnTooltip' => __( 'Trash widget by moving it to the inactive widgets sidebar.' ), + 'error' => __( 'An error has occurred. Please reload the page and try again.' ), + 'widgetMovedUp' => __( 'Widget moved up' ), + 'widgetMovedDown' => __( 'Widget moved down' ), + ), + 'tpl' => array( + 'widgetReorderNav' => $widget_reorder_nav_tpl, + 'moveWidgetArea' => $move_widget_area_tpl, + ), + ); + + foreach ( $settings['registeredWidgets'] as &$registered_widget ) { + unset( $registered_widget['callback'] ); // may not be JSON-serializeable + } + + $wp_scripts->add_data( + 'customize-widgets', + 'data', + sprintf( 'var _wpCustomizeWidgetsSettings = %s;', wp_json_encode( $settings ) ) + ); + } + + /** + * Render the widget form control templates into the DOM. + * + * @since 3.9.0 + * @access public + */ + public function output_widget_control_templates() { + ?> + <div id="widgets-left"><!-- compatibility with JS which looks for widget templates here --> + <div id="available-widgets"> + <div id="available-widgets-filter"> + <label class="screen-reader-text" for="widgets-search"><?php _e( 'Search Widgets' ); ?></label> + <input type="search" id="widgets-search" placeholder="<?php esc_attr_e( 'Search widgets…' ) ?>" /> + </div> + <?php foreach ( $this->get_available_widgets() as $available_widget ): ?> + <div id="widget-tpl-<?php echo esc_attr( $available_widget['id'] ) ?>" data-widget-id="<?php echo esc_attr( $available_widget['id'] ) ?>" class="widget-tpl <?php echo esc_attr( $available_widget['id'] ) ?>" tabindex="0"> + <?php echo $available_widget['control_tpl']; ?> + </div> + <?php endforeach; ?> + </div><!-- #available-widgets --> + </div><!-- #widgets-left --> + <?php + } + + /** + * Call admin_print_footer_scripts and admin_print_scripts hooks to + * allow custom scripts from plugins. + * + * @since 3.9.0 + * @access public + */ + public function print_footer_scripts() { + /** This action is documented in wp-admin/admin-footer.php */ + do_action( 'admin_print_footer_scripts' ); + + /** This action is documented in wp-admin/admin-footer.php */ + do_action( 'admin_footer-widgets.php' ); + } + + /** + * Get common arguments to supply when constructing a Customizer setting. + * + * @since 3.9.0 + * @access public + * + * @param string $id Widget setting ID. + * @param array $overrides Array of setting overrides. + * @return array Possibly modified setting arguments. + */ + public function get_setting_args( $id, $overrides = array() ) { + $args = array( + 'type' => 'option', + 'capability' => 'edit_theme_options', + 'transport' => 'refresh', + 'default' => array(), + ); + $args = array_merge( $args, $overrides ); + + /** + * Filter the common arguments supplied when constructing a Customizer setting. + * + * @since 3.9.0 + * + * @see WP_Customize_Setting + * + * @param array $args Array of Customizer setting arguments. + * @param string $id Widget setting ID. + */ + return apply_filters( 'widget_customizer_setting_args', $args, $id ); + } + + /** + * Make sure that sidebar widget arrays only ever contain widget IDS. + * + * Used as the 'sanitize_callback' for each $sidebars_widgets setting. + * + * @since 3.9.0 + * @access public + * + * @param array $widget_ids Array of widget IDs. + * @return array Array of sanitized widget IDs. + */ + public function sanitize_sidebar_widgets( $widget_ids ) { + global $wp_registered_widgets; + + $widget_ids = array_map( 'strval', (array) $widget_ids ); + $sanitized_widget_ids = array(); + + foreach ( $widget_ids as $widget_id ) { + if ( array_key_exists( $widget_id, $wp_registered_widgets ) ) { + $sanitized_widget_ids[] = $widget_id; + } + } + return $sanitized_widget_ids; + } + + /** + * Build up an index of all available widgets for use in Backbone models. + * + * @since 3.9.0 + * @access public + * + * @see wp_list_widgets() + * + * @return array List of available widgets. + */ + public function get_available_widgets() { + static $available_widgets = array(); + if ( ! empty( $available_widgets ) ) { + return $available_widgets; + } + + global $wp_registered_widgets, $wp_registered_widget_controls; + require_once ABSPATH . '/wp-admin/includes/widgets.php'; // for next_widget_id_number() + + $sort = $wp_registered_widgets; + usort( $sort, array( $this, '_sort_name_callback' ) ); + $done = array(); + + foreach ( $sort as $widget ) { + if ( in_array( $widget['callback'], $done, true ) ) { // We already showed this multi-widget + continue; + } + + $sidebar = is_active_widget( $widget['callback'], $widget['id'], false, false ); + $done[] = $widget['callback']; + + if ( ! isset( $widget['params'][0] ) ) { + $widget['params'][0] = array(); + } + + $available_widget = $widget; + unset( $available_widget['callback'] ); // not serializable to JSON + + $args = array( + 'widget_id' => $widget['id'], + 'widget_name' => $widget['name'], + '_display' => 'template', + ); + + $is_disabled = false; + $is_multi_widget = ( isset( $wp_registered_widget_controls[$widget['id']]['id_base'] ) && isset( $widget['params'][0]['number'] ) ); + if ( $is_multi_widget ) { + $id_base = $wp_registered_widget_controls[$widget['id']]['id_base']; + $args['_temp_id'] = "$id_base-__i__"; + $args['_multi_num'] = next_widget_id_number( $id_base ); + $args['_add'] = 'multi'; + } else { + $args['_add'] = 'single'; + + if ( $sidebar && 'wp_inactive_widgets' !== $sidebar ) { + $is_disabled = true; + } + $id_base = $widget['id']; + } + + $list_widget_controls_args = wp_list_widget_controls_dynamic_sidebar( array( 0 => $args, 1 => $widget['params'][0] ) ); + $control_tpl = $this->get_widget_control( $list_widget_controls_args ); + + // The properties here are mapped to the Backbone Widget model. + $available_widget = array_merge( $available_widget, array( + 'temp_id' => isset( $args['_temp_id'] ) ? $args['_temp_id'] : null, + 'is_multi' => $is_multi_widget, + 'control_tpl' => $control_tpl, + 'multi_number' => ( $args['_add'] === 'multi' ) ? $args['_multi_num'] : false, + 'is_disabled' => $is_disabled, + 'id_base' => $id_base, + 'transport' => 'refresh', + 'width' => $wp_registered_widget_controls[$widget['id']]['width'], + 'height' => $wp_registered_widget_controls[$widget['id']]['height'], + 'is_wide' => $this->is_wide_widget( $widget['id'] ), + ) ); + + $available_widgets[] = $available_widget; + } + + return $available_widgets; + } + + /** + * Naturally order available widgets by name. + * + * @since 3.9.0 + * @static + * @access protected + * + * @param array $widget_a The first widget to compare. + * @param array $widget_b The second widget to compare. + * @return int Reorder position for the current widget comparison. + */ + protected function _sort_name_callback( $widget_a, $widget_b ) { + return strnatcasecmp( $widget_a['name'], $widget_b['name'] ); + } + + /** + * Get the widget control markup. + * + * @since 3.9.0 + * @access public + * + * @param array $args Widget control arguments. + * @return string Widget control form HTML markup. + */ + public function get_widget_control( $args ) { + ob_start(); + + call_user_func_array( 'wp_widget_control', $args ); + $replacements = array( + '<form action="" method="post">' => '<div class="form">', + '</form>' => '</div><!-- .form -->', + ); + + $control_tpl = ob_get_clean(); + + $control_tpl = str_replace( array_keys( $replacements ), array_values( $replacements ), $control_tpl ); + + return $control_tpl; + } + + /** + * Add hooks for the Customizer preview. + * + * @since 3.9.0 + * @access public + */ + public function customize_preview_init() { + add_filter( 'sidebars_widgets', array( $this, 'preview_sidebars_widgets' ), 1 ); + add_action( 'wp_enqueue_scripts', array( $this, 'customize_preview_enqueue' ) ); + add_action( 'wp_print_styles', array( $this, 'print_preview_css' ), 1 ); + add_action( 'wp_footer', array( $this, 'export_preview_data' ), 20 ); + } + + /** + * When previewing, make sure the proper previewing widgets are used. + * + * Because wp_get_sidebars_widgets() gets called early at init + * (via wp_convert_widget_settings()) and can set global variable + * $_wp_sidebars_widgets to the value of get_option( 'sidebars_widgets' ) + * before the Customizer preview filter is added, we have to reset + * it after the filter has been added. + * + * @since 3.9.0 + * @access public + * + * @param array $sidebars_widgets List of widgets for the current sidebar. + */ + public function preview_sidebars_widgets( $sidebars_widgets ) { + $sidebars_widgets = get_option( 'sidebars_widgets' ); + + unset( $sidebars_widgets['array_version'] ); + return $sidebars_widgets; + } + + /** + * Enqueue scripts for the Customizer preview. + * + * @since 3.9.0 + * @access public + */ + public function customize_preview_enqueue() { + wp_enqueue_script( 'customize-preview-widgets' ); + } + + /** + * Insert default style for highlighted widget at early point so theme + * stylesheet can override. + * + * @since 3.9.0 + * @access public + * + * @action wp_print_styles + */ + public function print_preview_css() { + ?> + <style> + .widget-customizer-highlighted-widget { + outline: none; + -webkit-box-shadow: 0 0 2px rgba(30,140,190,0.8); + box-shadow: 0 0 2px rgba(30,140,190,0.8); + position: relative; + z-index: 1; + } + </style> + <?php + } + + /** + * At the very end of the page, at the very end of the wp_footer, + * communicate the sidebars that appeared on the page. + * + * @since 3.9.0 + * @access public + */ + public function export_preview_data() { + + // Prepare Customizer settings to pass to JavaScript. + $settings = array( + 'renderedSidebars' => array_fill_keys( array_unique( $this->rendered_sidebars ), true ), + 'renderedWidgets' => array_fill_keys( array_keys( $this->rendered_widgets ), true ), + 'registeredSidebars' => array_values( $GLOBALS['wp_registered_sidebars'] ), + 'registeredWidgets' => $GLOBALS['wp_registered_widgets'], + 'l10n' => array( + 'widgetTooltip' => __( 'Shift-click to edit this widget.' ), + ), + ); + foreach ( $settings['registeredWidgets'] as &$registered_widget ) { + unset( $registered_widget['callback'] ); // may not be JSON-serializeable + } + + ?> + <script type="text/javascript"> + var _wpWidgetCustomizerPreviewSettings = <?php echo wp_json_encode( $settings ); ?>; + </script> + <?php + } + + /** + * Keep track of the widgets that were rendered. + * + * @since 3.9.0 + * @access public + * + * @param array $widget Rendered widget to tally. + */ + public function tally_rendered_widgets( $widget ) { + $this->rendered_widgets[ $widget['id'] ] = true; + } + + /** + * Determine if a widget is rendered on the page. + * + * @since 4.0.0 + * @access public + * + * @param string $widget_id Widget ID to check. + * @return bool Whether the widget is rendered. + */ + public function is_widget_rendered( $widget_id ) { + return in_array( $widget_id, $this->rendered_widgets ); + } + + /** + * Determine if a sidebar is rendered on the page. + * + * @since 4.0.0 + * @access public + * + * @param string $sidebar_id Sidebar ID to check. + * @return bool Whether the sidebar is rendered. + */ + public function is_sidebar_rendered( $sidebar_id ) { + return in_array( $sidebar_id, $this->rendered_sidebars ); + } + + /** + * Tally the sidebars rendered via is_active_sidebar(). + * + * Keep track of the times that is_active_sidebar() is called + * in the template, and assume that this means that the sidebar + * would be rendered on the template if there were widgets + * populating it. + * + * @since 3.9.0 + * @access public + * + * @param bool $is_active Whether the sidebar is active. + * @param string $sidebar_id Sidebar ID. + */ + public function tally_sidebars_via_is_active_sidebar_calls( $is_active, $sidebar_id ) { + if ( isset( $GLOBALS['wp_registered_sidebars'][$sidebar_id] ) ) { + $this->rendered_sidebars[] = $sidebar_id; + } + /* + * We may need to force this to true, and also force-true the value + * for 'dynamic_sidebar_has_widgets' if we want to ensure that there + * is an area to drop widgets into, if the sidebar is empty. + */ + return $is_active; + } + + /** + * Tally the sidebars rendered via dynamic_sidebar(). + * + * Keep track of the times that dynamic_sidebar() is called in the template, + * and assume this means the sidebar would be rendered on the template if + * there were widgets populating it. + * + * @since 3.9.0 + * @access public + * + * @param bool $has_widgets Whether the current sidebar has widgets. + * @param string $sidebar_id Sidebar ID. + */ + public function tally_sidebars_via_dynamic_sidebar_calls( $has_widgets, $sidebar_id ) { + if ( isset( $GLOBALS['wp_registered_sidebars'][$sidebar_id] ) ) { + $this->rendered_sidebars[] = $sidebar_id; + } + + /* + * We may need to force this to true, and also force-true the value + * for 'is_active_sidebar' if we want to ensure there is an area to + * drop widgets into, if the sidebar is empty. + */ + return $has_widgets; + } + + /** + * Get MAC for a serialized widget instance string. + * + * Allows values posted back from JS to be rejected if any tampering of the + * data has occurred. + * + * @since 3.9.0 + * @access protected + * + * @param string $serialized_instance Widget instance. + * @return string MAC for serialized widget instance. + */ + protected function get_instance_hash_key( $serialized_instance ) { + return wp_hash( $serialized_instance ); + } + + /** + * Sanitize a widget instance. + * + * Unserialize the JS-instance for storing in the options. It's important + * that this filter only get applied to an instance once. + * + * @since 3.9.0 + * @access public + * + * @param array $value Widget instance to sanitize. + * @return array Sanitized widget instance. + */ + public function sanitize_widget_instance( $value ) { + if ( $value === array() ) { + return $value; + } + + if ( empty( $value['is_widget_customizer_js_value'] ) + || empty( $value['instance_hash_key'] ) + || empty( $value['encoded_serialized_instance'] ) ) + { + return null; + } + + $decoded = base64_decode( $value['encoded_serialized_instance'], true ); + if ( false === $decoded ) { + return null; + } + + if ( $this->get_instance_hash_key( $decoded ) !== $value['instance_hash_key'] ) { + return null; + } + + $instance = unserialize( $decoded ); + if ( false === $instance ) { + return null; + } + + return $instance; + } + + /** + * Convert widget instance into JSON-representable format. + * + * @since 3.9.0 + * @access public + * + * @param array $value Widget instance to convert to JSON. + * @return array JSON-converted widget instance. + */ + public function sanitize_widget_js_instance( $value ) { + if ( empty( $value['is_widget_customizer_js_value'] ) ) { + $serialized = serialize( $value ); + + $value = array( + 'encoded_serialized_instance' => base64_encode( $serialized ), + 'title' => empty( $value['title'] ) ? '' : $value['title'], + 'is_widget_customizer_js_value' => true, + 'instance_hash_key' => $this->get_instance_hash_key( $serialized ), + ); + } + return $value; + } + + /** + * Strip out widget IDs for widgets which are no longer registered. + * + * One example where this might happen is when a plugin orphans a widget + * in a sidebar upon deactivation. + * + * @since 3.9.0 + * @access public + * + * @param array $widget_ids List of widget IDs. + * @return array Parsed list of widget IDs. + */ + public function sanitize_sidebar_widgets_js_instance( $widget_ids ) { + global $wp_registered_widgets; + $widget_ids = array_values( array_intersect( $widget_ids, array_keys( $wp_registered_widgets ) ) ); + return $widget_ids; + } + + /** + * Find and invoke the widget update and control callbacks. + * + * Requires that $_POST be populated with the instance data. + * + * @since 3.9.0 + * @access public + * + * @param string $widget_id Widget ID. + * @return WP_Error|array Array containing the updated widget information. + * A WP_Error object, otherwise. + */ + public function call_widget_update( $widget_id ) { + global $wp_registered_widget_updates, $wp_registered_widget_controls; + + $this->start_capturing_option_updates(); + $parsed_id = $this->parse_widget_id( $widget_id ); + $option_name = 'widget_' . $parsed_id['id_base']; + + /* + * If a previously-sanitized instance is provided, populate the input vars + * with its values so that the widget update callback will read this instance + */ + $added_input_vars = array(); + if ( ! empty( $_POST['sanitized_widget_setting'] ) ) { + $sanitized_widget_setting = json_decode( $this->get_post_value( 'sanitized_widget_setting' ), true ); + if ( false === $sanitized_widget_setting ) { + $this->stop_capturing_option_updates(); + return new WP_Error( 'widget_setting_malformed' ); + } + + $instance = $this->sanitize_widget_instance( $sanitized_widget_setting ); + if ( is_null( $instance ) ) { + $this->stop_capturing_option_updates(); + return new WP_Error( 'widget_setting_unsanitized' ); + } + + if ( ! is_null( $parsed_id['number'] ) ) { + $value = array(); + $value[$parsed_id['number']] = $instance; + $key = 'widget-' . $parsed_id['id_base']; + $_REQUEST[$key] = $_POST[$key] = wp_slash( $value ); + $added_input_vars[] = $key; + } else { + foreach ( $instance as $key => $value ) { + $_REQUEST[$key] = $_POST[$key] = wp_slash( $value ); + $added_input_vars[] = $key; + } + } + } + + // Invoke the widget update callback. + foreach ( (array) $wp_registered_widget_updates as $name => $control ) { + if ( $name === $parsed_id['id_base'] && is_callable( $control['callback'] ) ) { + ob_start(); + call_user_func_array( $control['callback'], $control['params'] ); + ob_end_clean(); + break; + } + } + + // Clean up any input vars that were manually added + foreach ( $added_input_vars as $key ) { + unset( $_POST[$key] ); + unset( $_REQUEST[$key] ); + } + + // Make sure the expected option was updated. + if ( 0 !== $this->count_captured_options() ) { + if ( $this->count_captured_options() > 1 ) { + $this->stop_capturing_option_updates(); + return new WP_Error( 'widget_setting_too_many_options' ); + } + + $updated_option_name = key( $this->get_captured_options() ); + if ( $updated_option_name !== $option_name ) { + $this->stop_capturing_option_updates(); + return new WP_Error( 'widget_setting_unexpected_option' ); + } + } + + // Obtain the widget control with the updated instance in place. + ob_start(); + + $form = $wp_registered_widget_controls[$widget_id]; + if ( $form ) { + call_user_func_array( $form['callback'], $form['params'] ); + } + + $form = ob_get_clean(); + + // Obtain the widget instance. + $option = get_option( $option_name ); + + if ( null !== $parsed_id['number'] ) { + $instance = $option[$parsed_id['number']]; + } else { + $instance = $option; + } + + $this->stop_capturing_option_updates(); + + return compact( 'instance', 'form' ); + } + + /** + * Update widget settings asynchronously. + * + * Allows the Customizer to update a widget using its form, but return the new + * instance info via Ajax instead of saving it to the options table. + * + * Most code here copied from wp_ajax_save_widget() + * + * @since 3.9.0 + * @access public + * + * @see wp_ajax_save_widget() + * + */ + public function wp_ajax_update_widget() { + + if ( ! is_user_logged_in() ) { + wp_die( 0 ); + } + + check_ajax_referer( 'update-widget', 'nonce' ); + + if ( ! current_user_can( 'edit_theme_options' ) ) { + wp_die( -1 ); + } + + if ( ! isset( $_POST['widget-id'] ) ) { + wp_send_json_error(); + } + + /** This action is documented in wp-admin/includes/ajax-actions.php */ + do_action( 'load-widgets.php' ); + + /** This action is documented in wp-admin/includes/ajax-actions.php */ + do_action( 'widgets.php' ); + + /** This action is documented in wp-admin/widgets.php */ + do_action( 'sidebar_admin_setup' ); + + $widget_id = $this->get_post_value( 'widget-id' ); + $parsed_id = $this->parse_widget_id( $widget_id ); + $id_base = $parsed_id['id_base']; + + if ( isset( $_POST['widget-' . $id_base] ) && is_array( $_POST['widget-' . $id_base] ) && preg_match( '/__i__|%i%/', key( $_POST['widget-' . $id_base] ) ) ) { + wp_send_json_error(); + } + + $updated_widget = $this->call_widget_update( $widget_id ); // => {instance,form} + if ( is_wp_error( $updated_widget ) ) { + wp_send_json_error(); + } + + $form = $updated_widget['form']; + $instance = $this->sanitize_widget_js_instance( $updated_widget['instance'] ); + + wp_send_json_success( compact( 'form', 'instance' ) ); + } + + /*************************************************************************** + * Option Update Capturing + ***************************************************************************/ + + /** + * List of captured widget option updates. + * + * @since 3.9.0 + * @access protected + * @var array $_captured_options Values updated while option capture is happening. + */ + protected $_captured_options = array(); + + /** + * Whether option capture is currently happening. + * + * @since 3.9.0 + * @access protected + * @var bool $_is_current Whether option capture is currently happening or not. + */ + protected $_is_capturing_option_updates = false; + + /** + * Determine whether the captured option update should be ignored. + * + * @since 3.9.0 + * @access protected + * + * @param string $option_name Option name. + * @return boolean Whether the option capture is ignored. + */ + protected function is_option_capture_ignored( $option_name ) { + return ( 0 === strpos( $option_name, '_transient_' ) ); + } + + /** + * Retrieve captured widget option updates. + * + * @since 3.9.0 + * @access protected + * + * @return array Array of captured options. + */ + protected function get_captured_options() { + return $this->_captured_options; + } + + /** + * Get the number of captured widget option updates. + * + * @since 3.9.0 + * @access protected + * + * @return int Number of updated options. + */ + protected function count_captured_options() { + return count( $this->_captured_options ); + } + + /** + * Start keeping track of changes to widget options, caching new values. + * + * @since 3.9.0 + * @access protected + */ + protected function start_capturing_option_updates() { + if ( $this->_is_capturing_option_updates ) { + return; + } + + $this->_is_capturing_option_updates = true; + + add_filter( 'pre_update_option', array( $this, 'capture_filter_pre_update_option' ), 10, 3 ); + } + + /** + * Pre-filter captured option values before updating. + * + * @since 3.9.0 + * @access public + * + * @param mixed $new_value + * @param string $option_name + * @param mixed $old_value + * @return mixed + */ + public function capture_filter_pre_update_option( $new_value, $option_name, $old_value ) { + if ( $this->is_option_capture_ignored( $option_name ) ) { + return; + } + + if ( ! isset( $this->_captured_options[$option_name] ) ) { + add_filter( "pre_option_{$option_name}", array( $this, 'capture_filter_pre_get_option' ) ); + } + + $this->_captured_options[$option_name] = $new_value; + + return $old_value; + } + + /** + * Pre-filter captured option values before retrieving. + * + * @since 3.9.0 + * @access public + * + * @param mixed $value Option + * @return mixed + */ + public function capture_filter_pre_get_option( $value ) { + $option_name = preg_replace( '/^pre_option_/', '', current_filter() ); + + if ( isset( $this->_captured_options[$option_name] ) ) { + $value = $this->_captured_options[$option_name]; + + /** This filter is documented in wp-includes/option.php */ + $value = apply_filters( 'option_' . $option_name, $value ); + } + + return $value; + } + + /** + * Undo any changes to the options since options capture began. + * + * @since 3.9.0 + * @access protected + */ + protected function stop_capturing_option_updates() { + if ( ! $this->_is_capturing_option_updates ) { + return; + } + + remove_filter( 'pre_update_option', array( $this, 'capture_filter_pre_update_option' ), 10, 3 ); + + foreach ( array_keys( $this->_captured_options ) as $option_name ) { + remove_filter( "pre_option_{$option_name}", array( $this, 'capture_filter_pre_get_option' ) ); + } + + $this->_captured_options = array(); + $this->_is_capturing_option_updates = false; + } +} diff --git a/wp-includes/class-wp-editor.php b/wp-includes/class-wp-editor.php new file mode 100644 index 0000000..521c0ee --- /dev/null +++ b/wp-includes/class-wp-editor.php @@ -0,0 +1,1499 @@ +<?php +/** + * Facilitates adding of the WordPress editor as used on the Write and Edit screens. + * + * @package WordPress + * @since 3.3.0 + * + * Private, not included by default. See wp_editor() in wp-includes/general-template.php. + */ + +final class _WP_Editors { + public static $mce_locale; + + private static $mce_settings = array(); + private static $qt_settings = array(); + private static $plugins = array(); + private static $qt_buttons = array(); + private static $ext_plugins; + private static $baseurl; + private static $first_init; + private static $this_tinymce = false; + private static $this_quicktags = false; + private static $has_tinymce = false; + private static $has_quicktags = false; + private static $has_medialib = false; + private static $editor_buttons_css = true; + private static $drag_drop_upload = false; + + private function __construct() {} + + /** + * Parse default arguments for the editor instance. + * + * @param string $editor_id ID for the current editor instance. + * @param array $settings { + * Array of editor arguments. + * + * @type bool $wpautop Whether to use wpautop(). Default true. + * @type bool $media_buttons Whether to show the Add Media/other media buttons. + * @type string $default_editor When both TinyMCE and Quicktags are used, set which + * editor is shown on page load. Default empty. + * @type bool $drag_drop_upload Whether to enable drag & drop on the editor uploading. Default false. + * Requires the media modal. + * @type string $textarea_name Give the textarea a unique name here. Square brackets + * can be used here. Default $editor_id. + * @type int $textarea_rows Number rows in the editor textarea. Default 20. + * @type string|int $tabindex Tabindex value to use. Default empty. + * @type string $tabfocus_elements The previous and next element ID to move the focus to + * when pressing the Tab key in TinyMCE. Defualt ':prev,:next'. + * @type string $editor_css Intended for extra styles for both Visual and Text editors. + * Should include `<style>` tags, and can use "scoped". Default empty. + * @type string $editor_class Extra classes to add to the editor textarea elemen. Default empty. + * @type bool $teeny Whether to output the minimal editor config. Examples include + * Press This and the Comment editor. Default false. + * @type bool $dfw Whether to replace the default fullscreen with "Distraction Free + * Writing". DFW requires specific DOM elements and css). Default false. + * @type bool|array $tinymce Whether to load TinyMCE. Can be used to pass settings directly to + * TinyMCE using an array. Default true. + * @type bool|array $quicktags Whether to load Quicktags. Can be used to pass settings directly to + * Quicktags using an array. Default true. + * } + * @return array Parsed arguments array. + */ + public static function parse_settings( $editor_id, $settings ) { + + /** + * Filter the wp_editor() settings. + * + * @since 4.0.0 + * + * @see _WP_Editors()::parse_settings() + * + * @param array $settings Array of editor arguments. + * @param string $editor_id ID for the current editor instance. + */ + $settings = apply_filters( 'wp_editor_settings', $settings, $editor_id ); + + $set = wp_parse_args( $settings, array( + 'wpautop' => true, + 'media_buttons' => true, + 'default_editor' => '', + 'drag_drop_upload' => false, + 'textarea_name' => $editor_id, + 'textarea_rows' => 20, + 'tabindex' => '', + 'tabfocus_elements' => ':prev,:next', + 'editor_css' => '', + 'editor_class' => '', + 'teeny' => false, + 'dfw' => false, + '_content_editor_dfw' => false, + 'tinymce' => true, + 'quicktags' => true + ) ); + + self::$this_tinymce = ( $set['tinymce'] && user_can_richedit() ); + + if ( self::$this_tinymce ) { + if ( false !== strpos( $editor_id, '[' ) ) { + self::$this_tinymce = false; + _deprecated_argument( 'wp_editor()', '3.9', 'TinyMCE editor IDs cannot have brackets.' ); + } + } + + self::$this_quicktags = (bool) $set['quicktags']; + + if ( self::$this_tinymce ) + self::$has_tinymce = true; + + if ( self::$this_quicktags ) + self::$has_quicktags = true; + + if ( empty( $set['editor_height'] ) ) + return $set; + + if ( 'content' === $editor_id && empty( $set['tinymce']['wp_autoresize_on'] ) ) { + // A cookie (set when a user resizes the editor) overrides the height. + $cookie = (int) get_user_setting( 'ed_size' ); + + // Upgrade an old TinyMCE cookie if it is still around, and the new one isn't. + if ( ! $cookie && isset( $_COOKIE['TinyMCE_content_size'] ) ) { + parse_str( $_COOKIE['TinyMCE_content_size'], $cookie ); + $cookie = $cookie['ch']; + } + + if ( $cookie ) + $set['editor_height'] = $cookie; + } + + if ( $set['editor_height'] < 50 ) + $set['editor_height'] = 50; + elseif ( $set['editor_height'] > 5000 ) + $set['editor_height'] = 5000; + + return $set; + } + + /** + * Outputs the HTML for a single instance of the editor. + * + * @param string $content The initial content of the editor. + * @param string $editor_id ID for the textarea and TinyMCE and Quicktags instances (can contain only ASCII letters and numbers). + * @param array $settings See the _parse_settings() method for description. + */ + public static function editor( $content, $editor_id, $settings = array() ) { + + $set = self::parse_settings( $editor_id, $settings ); + $editor_class = ' class="' . trim( $set['editor_class'] . ' wp-editor-area' ) . '"'; + $tabindex = $set['tabindex'] ? ' tabindex="' . (int) $set['tabindex'] . '"' : ''; + $switch_class = 'html-active'; + $toolbar = $buttons = $autocomplete = ''; + + if ( $set['drag_drop_upload'] ) { + self::$drag_drop_upload = true; + } + + if ( ! empty( $set['editor_height'] ) ) + $height = ' style="height: ' . $set['editor_height'] . 'px"'; + else + $height = ' rows="' . $set['textarea_rows'] . '"'; + + if ( !current_user_can( 'upload_files' ) ) + $set['media_buttons'] = false; + + if ( ! self::$this_quicktags && self::$this_tinymce ) { + $switch_class = 'tmce-active'; + $autocomplete = ' autocomplete="off"'; + } elseif ( self::$this_quicktags && self::$this_tinymce ) { + $default_editor = $set['default_editor'] ? $set['default_editor'] : wp_default_editor(); + $autocomplete = ' autocomplete="off"'; + + // 'html' is used for the "Text" editor tab. + if ( 'html' === $default_editor ) { + add_filter('the_editor_content', 'wp_htmledit_pre'); + $switch_class = 'html-active'; + } else { + add_filter('the_editor_content', 'wp_richedit_pre'); + $switch_class = 'tmce-active'; + } + + $buttons .= '<button type="button" id="' . $editor_id . '-tmce" class="wp-switch-editor switch-tmce" onclick="switchEditors.switchto(this);">' . __('Visual') . "</button>\n"; + $buttons .= '<button type="button" id="' . $editor_id . '-html" class="wp-switch-editor switch-html" onclick="switchEditors.switchto(this);">' . _x( 'Text', 'Name for the Text editor tab (formerly HTML)' ) . "</button>\n"; + } + + $wrap_class = 'wp-core-ui wp-editor-wrap ' . $switch_class; + + if ( $set['_content_editor_dfw'] ) { + $wrap_class .= ' has-dfw'; + } + + echo '<div id="wp-' . $editor_id . '-wrap" class="' . $wrap_class . '">'; + + if ( self::$editor_buttons_css ) { + wp_print_styles('editor-buttons'); + self::$editor_buttons_css = false; + } + + if ( !empty($set['editor_css']) ) + echo $set['editor_css'] . "\n"; + + if ( !empty($buttons) || $set['media_buttons'] ) { + echo '<div id="wp-' . $editor_id . '-editor-tools" class="wp-editor-tools hide-if-no-js">'; + + if ( $set['media_buttons'] ) { + self::$has_medialib = true; + + if ( !function_exists('media_buttons') ) + include(ABSPATH . 'wp-admin/includes/media.php'); + + echo '<div id="wp-' . $editor_id . '-media-buttons" class="wp-media-buttons">'; + + /** + * Fires after the default media button(s) are displayed. + * + * @since 2.5.0 + * + * @param string $editor_id Unique editor identifier, e.g. 'content'. + */ + do_action( 'media_buttons', $editor_id ); + echo "</div>\n"; + } + + echo '<div class="wp-editor-tabs">' . $buttons . "</div>\n"; + echo "</div>\n"; + } + + /** + * Filter the HTML markup output that displays the editor. + * + * @since 2.1.0 + * + * @param string $output Editor's HTML markup. + */ + $the_editor = apply_filters( 'the_editor', '<div id="wp-' . $editor_id . '-editor-container" class="wp-editor-container">' . + '<textarea' . $editor_class . $height . $tabindex . $autocomplete . ' cols="40" name="' . $set['textarea_name'] . '" ' . + 'id="' . $editor_id . '">%s</textarea></div>' ); + + /** + * Filter the default editor content. + * + * @since 2.1.0 + * + * @param string $content Default editor content. + */ + $content = apply_filters( 'the_editor_content', $content ); + + printf( $the_editor, $content ); + echo "\n</div>\n\n"; + + self::editor_settings($editor_id, $set); + } + + /** + * @param string $editor_id + * @param array $set + */ + public static function editor_settings($editor_id, $set) { + $first_run = false; + + if ( empty(self::$first_init) ) { + if ( is_admin() ) { + add_action( 'admin_print_footer_scripts', array( __CLASS__, 'editor_js' ), 50 ); + add_action( 'admin_print_footer_scripts', array( __CLASS__, 'enqueue_scripts' ), 1 ); + } else { + add_action( 'wp_print_footer_scripts', array( __CLASS__, 'editor_js' ), 50 ); + add_action( 'wp_print_footer_scripts', array( __CLASS__, 'enqueue_scripts' ), 1 ); + } + } + + if ( self::$this_quicktags ) { + + $qtInit = array( + 'id' => $editor_id, + 'buttons' => '' + ); + + if ( is_array($set['quicktags']) ) + $qtInit = array_merge($qtInit, $set['quicktags']); + + if ( empty($qtInit['buttons']) ) + $qtInit['buttons'] = 'strong,em,link,block,del,ins,img,ul,ol,li,code,more,close'; + + if ( $set['dfw'] ) + $qtInit['buttons'] .= ',fullscreen'; + + if ( $set['_content_editor_dfw'] ) { + $qtInit['buttons'] .= ',dfw'; + } + + /** + * Filter the Quicktags settings. + * + * @since 3.3.0 + * + * @param array $qtInit Quicktags settings. + * @param string $editor_id The unique editor ID, e.g. 'content'. + */ + $qtInit = apply_filters( 'quicktags_settings', $qtInit, $editor_id ); + + self::$qt_settings[$editor_id] = $qtInit; + + self::$qt_buttons = array_merge( self::$qt_buttons, explode(',', $qtInit['buttons']) ); + } + + if ( self::$this_tinymce ) { + + if ( empty( self::$first_init ) ) { + self::$baseurl = includes_url( 'js/tinymce' ); + + $mce_locale = get_locale(); + self::$mce_locale = $mce_locale = empty( $mce_locale ) ? 'en' : strtolower( substr( $mce_locale, 0, 2 ) ); // ISO 639-1 + + /** This filter is documented in wp-admin/includes/media.php */ + $no_captions = (bool) apply_filters( 'disable_captions', '' ); + $first_run = true; + $ext_plugins = ''; + + if ( $set['teeny'] ) { + + /** + * Filter the list of teenyMCE plugins. + * + * @since 2.7.0 + * + * @param array $plugins An array of teenyMCE plugins. + * @param string $editor_id Unique editor identifier, e.g. 'content'. + */ + self::$plugins = $plugins = apply_filters( 'teeny_mce_plugins', array( 'colorpicker', 'lists', 'fullscreen', 'image', 'wordpress', 'wpeditimage', 'wplink' ), $editor_id ); + } else { + + /** + * Filter the list of TinyMCE external plugins. + * + * The filter takes an associative array of external plugins for + * TinyMCE in the form 'plugin_name' => 'url'. + * + * The url should be absolute, and should include the js filename + * to be loaded. For example: + * 'myplugin' => 'http://mysite.com/wp-content/plugins/myfolder/mce_plugin.js'. + * + * If the external plugin adds a button, it should be added with + * one of the 'mce_buttons' filters. + * + * @since 2.5.0 + * + * @param array $external_plugins An array of external TinyMCE plugins. + */ + $mce_external_plugins = apply_filters( 'mce_external_plugins', array() ); + + $plugins = array( + 'charmap', + 'colorpicker', + 'hr', + 'lists', + 'media', + 'paste', + 'tabfocus', + 'textcolor', + 'fullscreen', + 'wordpress', + 'wpautoresize', + 'wpeditimage', + 'wpgallery', + 'wplink', + 'wpdialogs', + 'wpview', + ); + + if ( ! self::$has_medialib ) { + $plugins[] = 'image'; + } + + /** + * Filter the list of default TinyMCE plugins. + * + * The filter specifies which of the default plugins included + * in WordPress should be added to the TinyMCE instance. + * + * @since 3.3.0 + * + * @param array $plugins An array of default TinyMCE plugins. + */ + $plugins = array_unique( apply_filters( 'tiny_mce_plugins', $plugins ) ); + + if ( ( $key = array_search( 'spellchecker', $plugins ) ) !== false ) { + // Remove 'spellchecker' from the internal plugins if added with 'tiny_mce_plugins' filter to prevent errors. + // It can be added with 'mce_external_plugins'. + unset( $plugins[$key] ); + } + + if ( ! empty( $mce_external_plugins ) ) { + + /** + * Filter the translations loaded for external TinyMCE 3.x plugins. + * + * The filter takes an associative array ('plugin_name' => 'path') + * where 'path' is the include path to the file. + * + * The language file should follow the same format as wp_mce_translation(), + * and should define a variable ($strings) that holds all translated strings. + * + * @since 2.5.0 + * + * @param array $translations Translations for external TinyMCE plugins. + */ + $mce_external_languages = apply_filters( 'mce_external_languages', array() ); + + $loaded_langs = array(); + $strings = ''; + + if ( ! empty( $mce_external_languages ) ) { + foreach ( $mce_external_languages as $name => $path ) { + if ( @is_file( $path ) && @is_readable( $path ) ) { + include_once( $path ); + $ext_plugins .= $strings . "\n"; + $loaded_langs[] = $name; + } + } + } + + foreach ( $mce_external_plugins as $name => $url ) { + if ( in_array( $name, $plugins, true ) ) { + unset( $mce_external_plugins[ $name ] ); + continue; + } + + $url = set_url_scheme( $url ); + $mce_external_plugins[ $name ] = $url; + $plugurl = dirname( $url ); + $strings = ''; + + // Try to load langs/[locale].js and langs/[locale]_dlg.js + if ( ! in_array( $name, $loaded_langs, true ) ) { + $path = str_replace( content_url(), '', $plugurl ); + $path = WP_CONTENT_DIR . $path . '/langs/'; + + if ( function_exists('realpath') ) + $path = trailingslashit( realpath($path) ); + + if ( @is_file( $path . $mce_locale . '.js' ) ) + $strings .= @file_get_contents( $path . $mce_locale . '.js' ) . "\n"; + + if ( @is_file( $path . $mce_locale . '_dlg.js' ) ) + $strings .= @file_get_contents( $path . $mce_locale . '_dlg.js' ) . "\n"; + + if ( 'en' != $mce_locale && empty( $strings ) ) { + if ( @is_file( $path . 'en.js' ) ) { + $str1 = @file_get_contents( $path . 'en.js' ); + $strings .= preg_replace( '/([\'"])en\./', '$1' . $mce_locale . '.', $str1, 1 ) . "\n"; + } + + if ( @is_file( $path . 'en_dlg.js' ) ) { + $str2 = @file_get_contents( $path . 'en_dlg.js' ); + $strings .= preg_replace( '/([\'"])en\./', '$1' . $mce_locale . '.', $str2, 1 ) . "\n"; + } + } + + if ( ! empty( $strings ) ) + $ext_plugins .= "\n" . $strings . "\n"; + } + + $ext_plugins .= 'tinyMCEPreInit.load_ext("' . $plugurl . '", "' . $mce_locale . '");' . "\n"; + $ext_plugins .= 'tinymce.PluginManager.load("' . $name . '", "' . $url . '");' . "\n"; + } + } + } + + if ( $set['dfw'] ) + $plugins[] = 'wpfullscreen'; + + self::$plugins = $plugins; + self::$ext_plugins = $ext_plugins; + + self::$first_init = array( + 'theme' => 'modern', + 'skin' => 'lightgray', + 'language' => self::$mce_locale, + 'formats' => "{ + alignleft: [ + {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles: {textAlign:'left'}}, + {selector: 'img,table,dl.wp-caption', classes: 'alignleft'} + ], + aligncenter: [ + {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles: {textAlign:'center'}}, + {selector: 'img,table,dl.wp-caption', classes: 'aligncenter'} + ], + alignright: [ + {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles: {textAlign:'right'}}, + {selector: 'img,table,dl.wp-caption', classes: 'alignright'} + ], + strikethrough: {inline: 'del'} + }", + 'block_formats' => + 'Paragraph=p;' . + 'Pre=pre;' . + 'Heading 1=h1;' . + 'Heading 2=h2;' . + 'Heading 3=h3;' . + 'Heading 4=h4;' . + 'Heading 5=h5;' . + 'Heading 6=h6', + 'relative_urls' => false, + 'remove_script_host' => false, + 'convert_urls' => false, + 'browser_spellcheck' => true, + 'fix_list_elements' => true, + 'entities' => '38,amp,60,lt,62,gt', + 'entity_encoding' => 'raw', + 'keep_styles' => false, + 'cache_suffix' => 'wp-mce-' . $GLOBALS['tinymce_version'], + + // Limit the preview styles in the menu/toolbar + 'preview_styles' => 'font-family font-size font-weight font-style text-decoration text-transform', + + 'wpeditimage_disable_captions' => $no_captions, + 'wpeditimage_html5_captions' => current_theme_supports( 'html5', 'caption' ), + 'plugins' => implode( ',', $plugins ), + ); + + if ( ! empty( $mce_external_plugins ) ) { + self::$first_init['external_plugins'] = wp_json_encode( $mce_external_plugins ); + } + + $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min'; + $version = 'ver=' . $GLOBALS['wp_version']; + $dashicons = includes_url( "css/dashicons$suffix.css?$version" ); + + // WordPress default stylesheet and dashicons + $mce_css = array( + $dashicons, + self::$baseurl . '/skins/wordpress/wp-content.css?' . $version + ); + + $editor_styles = get_editor_stylesheets(); + if ( ! empty( $editor_styles ) ) { + foreach ( $editor_styles as $style ) { + $mce_css[] = $style; + } + } + + /** + * Filter the comma-delimited list of stylesheets to load in TinyMCE. + * + * @since 2.1.0 + * + * @param array $stylesheets Comma-delimited list of stylesheets. + */ + $mce_css = trim( apply_filters( 'mce_css', implode( ',', $mce_css ) ), ' ,' ); + + if ( ! empty($mce_css) ) + self::$first_init['content_css'] = $mce_css; + } + + if ( $set['teeny'] ) { + + /** + * Filter the list of teenyMCE buttons (Text tab). + * + * @since 2.7.0 + * + * @param array $buttons An array of teenyMCE buttons. + * @param string $editor_id Unique editor identifier, e.g. 'content'. + */ + $mce_buttons = apply_filters( 'teeny_mce_buttons', array('bold', 'italic', 'underline', 'blockquote', 'strikethrough', 'bullist', 'numlist', 'alignleft', 'aligncenter', 'alignright', 'undo', 'redo', 'link', 'unlink', 'fullscreen'), $editor_id ); + $mce_buttons_2 = $mce_buttons_3 = $mce_buttons_4 = array(); + } else { + $mce_buttons = array( 'bold', 'italic', 'strikethrough', 'bullist', 'numlist', 'blockquote', 'hr', 'alignleft', 'aligncenter', 'alignright', 'link', 'unlink', 'wp_more', 'spellchecker' ); + + if ( $set['_content_editor_dfw'] ) { + $mce_buttons[] = 'dfw'; + } else { + $mce_buttons[] = 'fullscreen'; + } + + $mce_buttons[] = 'wp_adv'; + + /** + * Filter the first-row list of TinyMCE buttons (Visual tab). + * + * @since 2.0.0 + * + * @param array $buttons First-row list of buttons. + * @param string $editor_id Unique editor identifier, e.g. 'content'. + */ + $mce_buttons = apply_filters( 'mce_buttons', $mce_buttons, $editor_id ); + + /** + * Filter the second-row list of TinyMCE buttons (Visual tab). + * + * @since 2.0.0 + * + * @param array $buttons Second-row list of buttons. + * @param string $editor_id Unique editor identifier, e.g. 'content'. + */ + $mce_buttons_2 = apply_filters( 'mce_buttons_2', array( 'formatselect', 'underline', 'alignjustify', 'forecolor', 'pastetext', 'removeformat', 'charmap', 'outdent', 'indent', 'undo', 'redo', 'wp_help' ), $editor_id ); + + /** + * Filter the third-row list of TinyMCE buttons (Visual tab). + * + * @since 2.0.0 + * + * @param array $buttons Third-row list of buttons. + * @param string $editor_id Unique editor identifier, e.g. 'content'. + */ + $mce_buttons_3 = apply_filters( 'mce_buttons_3', array(), $editor_id ); + + /** + * Filter the fourth-row list of TinyMCE buttons (Visual tab). + * + * @since 2.5.0 + * + * @param array $buttons Fourth-row list of buttons. + * @param string $editor_id Unique editor identifier, e.g. 'content'. + */ + $mce_buttons_4 = apply_filters( 'mce_buttons_4', array(), $editor_id ); + } + + $body_class = $editor_id; + + if ( $post = get_post() ) { + $body_class .= ' post-type-' . sanitize_html_class( $post->post_type ) . ' post-status-' . sanitize_html_class( $post->post_status ); + if ( post_type_supports( $post->post_type, 'post-formats' ) ) { + $post_format = get_post_format( $post ); + if ( $post_format && ! is_wp_error( $post_format ) ) + $body_class .= ' post-format-' . sanitize_html_class( $post_format ); + else + $body_class .= ' post-format-standard'; + } + } + + $body_class .= ' locale-' . sanitize_html_class( strtolower( str_replace( '_', '-', get_locale() ) ) ); + + if ( !empty($set['tinymce']['body_class']) ) { + $body_class .= ' ' . $set['tinymce']['body_class']; + unset($set['tinymce']['body_class']); + } + + if ( $set['dfw'] ) { + // replace the first 'fullscreen' with 'wp_fullscreen' + if ( ($key = array_search('fullscreen', $mce_buttons)) !== false ) + $mce_buttons[$key] = 'wp_fullscreen'; + elseif ( ($key = array_search('fullscreen', $mce_buttons_2)) !== false ) + $mce_buttons_2[$key] = 'wp_fullscreen'; + elseif ( ($key = array_search('fullscreen', $mce_buttons_3)) !== false ) + $mce_buttons_3[$key] = 'wp_fullscreen'; + elseif ( ($key = array_search('fullscreen', $mce_buttons_4)) !== false ) + $mce_buttons_4[$key] = 'wp_fullscreen'; + } + + $mceInit = array ( + 'selector' => "#$editor_id", + 'resize' => 'vertical', + 'menubar' => false, + 'wpautop' => (bool) $set['wpautop'], + 'indent' => ! $set['wpautop'], + 'toolbar1' => implode($mce_buttons, ','), + 'toolbar2' => implode($mce_buttons_2, ','), + 'toolbar3' => implode($mce_buttons_3, ','), + 'toolbar4' => implode($mce_buttons_4, ','), + 'tabfocus_elements' => $set['tabfocus_elements'], + 'body_class' => $body_class + ); + + if ( $first_run ) + $mceInit = array_merge( self::$first_init, $mceInit ); + + if ( is_array( $set['tinymce'] ) ) + $mceInit = array_merge( $mceInit, $set['tinymce'] ); + + /* + * For people who really REALLY know what they're doing with TinyMCE + * You can modify $mceInit to add, remove, change elements of the config + * before tinyMCE.init. Setting "valid_elements", "invalid_elements" + * and "extended_valid_elements" can be done through this filter. Best + * is to use the default cleanup by not specifying valid_elements, + * as TinyMCE contains full set of XHTML 1.0. + */ + if ( $set['teeny'] ) { + + /** + * Filter the teenyMCE config before init. + * + * @since 2.7.0 + * + * @param array $mceInit An array with teenyMCE config. + * @param string $editor_id Unique editor identifier, e.g. 'content'. + */ + $mceInit = apply_filters( 'teeny_mce_before_init', $mceInit, $editor_id ); + } else { + + /** + * Filter the TinyMCE config before init. + * + * @since 2.5.0 + * + * @param array $mceInit An array with TinyMCE config. + * @param string $editor_id Unique editor identifier, e.g. 'content'. + */ + $mceInit = apply_filters( 'tiny_mce_before_init', $mceInit, $editor_id ); + } + + if ( empty( $mceInit['toolbar3'] ) && ! empty( $mceInit['toolbar4'] ) ) { + $mceInit['toolbar3'] = $mceInit['toolbar4']; + $mceInit['toolbar4'] = ''; + } + + self::$mce_settings[$editor_id] = $mceInit; + } // end if self::$this_tinymce + } + + private static function _parse_init($init) { + $options = ''; + + foreach ( $init as $k => $v ) { + if ( is_bool($v) ) { + $val = $v ? 'true' : 'false'; + $options .= $k . ':' . $val . ','; + continue; + } elseif ( !empty($v) && is_string($v) && ( ('{' == $v{0} && '}' == $v{strlen($v) - 1}) || ('[' == $v{0} && ']' == $v{strlen($v) - 1}) || preg_match('/^\(?function ?\(/', $v) ) ) { + $options .= $k . ':' . $v . ','; + continue; + } + $options .= $k . ':"' . $v . '",'; + } + + return '{' . trim( $options, ' ,' ) . '}'; + } + + public static function enqueue_scripts() { + wp_enqueue_script('word-count'); + + if ( self::$has_tinymce ) + wp_enqueue_script('editor'); + + if ( self::$has_quicktags ) { + wp_enqueue_script( 'quicktags' ); + wp_enqueue_style( 'buttons' ); + } + + if ( in_array('wplink', self::$plugins, true) || in_array('link', self::$qt_buttons, true) ) { + wp_enqueue_script('wplink'); + } + + if ( in_array('wpfullscreen', self::$plugins, true) || in_array('fullscreen', self::$qt_buttons, true) ) + wp_enqueue_script('wp-fullscreen'); + + if ( self::$has_medialib ) { + add_thickbox(); + wp_enqueue_script('media-upload'); + } + + /** + * Fires when scripts and styles are enqueued for the editor. + * + * @since 3.9.0 + * + * @param array $to_load An array containing boolean values whether TinyMCE + * and Quicktags are being loaded. + */ + do_action( 'wp_enqueue_editor', array( + 'tinymce' => self::$has_tinymce, + 'quicktags' => self::$has_quicktags, + ) ); + } + + /** + * Translates the default TinyMCE strings and returns them as JSON encoded object ready to be loaded with tinymce.addI18n(). + * Can be used directly (_WP_Editors::wp_mce_translation()) by passing the same locale as set in the TinyMCE init object. + * + * @param string $mce_locale The locale used for the editor. + * @param bool $json_only optional Whether to include the JavaScript calls to tinymce.addI18n() and tinymce.ScriptLoader.markDone(). + * @return string Translation object, JSON encoded. + */ + public static function wp_mce_translation( $mce_locale = '', $json_only = false ) { + + $mce_translation = array( + // Default TinyMCE strings + 'New document' => __( 'New document' ), + 'Formats' => _x( 'Formats', 'TinyMCE' ), + + 'Headings' => _x( 'Headings', 'TinyMCE' ), + 'Heading 1' => __( 'Heading 1' ), + 'Heading 2' => __( 'Heading 2' ), + 'Heading 3' => __( 'Heading 3' ), + 'Heading 4' => __( 'Heading 4' ), + 'Heading 5' => __( 'Heading 5' ), + 'Heading 6' => __( 'Heading 6' ), + + /* translators: block tags */ + 'Blocks' => _x( 'Blocks', 'TinyMCE' ), + 'Paragraph' => __( 'Paragraph' ), + 'Blockquote' => __( 'Blockquote' ), + 'Div' => _x( 'Div', 'HTML tag' ), + 'Pre' => _x( 'Pre', 'HTML tag' ), + 'Address' => _x( 'Address', 'HTML tag' ), + + 'Inline' => _x( 'Inline', 'HTML elements' ), + 'Underline' => __( 'Underline' ), + 'Strikethrough' => __( 'Strikethrough' ), + 'Subscript' => __( 'Subscript' ), + 'Superscript' => __( 'Superscript' ), + 'Clear formatting' => __( 'Clear formatting' ), + 'Bold' => __( 'Bold' ), + 'Italic' => __( 'Italic' ), + 'Code' => _x( 'Code', 'editor button' ), + 'Source code' => __( 'Source code' ), + 'Font Family' => __( 'Font Family' ), + 'Font Sizes' => __( 'Font Sizes' ), + + 'Align center' => __( 'Align center' ), + 'Align right' => __( 'Align right' ), + 'Align left' => __( 'Align left' ), + 'Justify' => __( 'Justify' ), + 'Increase indent' => __( 'Increase indent' ), + 'Decrease indent' => __( 'Decrease indent' ), + + 'Cut' => __( 'Cut' ), + 'Copy' => __( 'Copy' ), + 'Paste' => __( 'Paste' ), + 'Select all' => __( 'Select all' ), + 'Undo' => __( 'Undo' ), + 'Redo' => __( 'Redo' ), + + 'Ok' => __( 'OK' ), + 'Cancel' => __( 'Cancel' ), + 'Close' => __( 'Close' ), + 'Visual aids' => __( 'Visual aids' ), + + 'Bullet list' => __( 'Bulleted list' ), + 'Numbered list' => __( 'Numbered list' ), + 'Square' => _x( 'Square', 'list style' ), + 'Default' => _x( 'Default', 'list style' ), + 'Circle' => _x( 'Circle', 'list style' ), + 'Disc' => _x('Disc', 'list style' ), + 'Lower Greek' => _x( 'Lower Greek', 'list style' ), + 'Lower Alpha' => _x( 'Lower Alpha', 'list style' ), + 'Upper Alpha' => _x( 'Upper Alpha', 'list style' ), + 'Upper Roman' => _x( 'Upper Roman', 'list style' ), + 'Lower Roman' => _x( 'Lower Roman', 'list style' ), + + // Anchor plugin + 'Name' => _x( 'Name', 'Name of link anchor (TinyMCE)' ), + 'Anchor' => _x( 'Anchor', 'Link anchor (TinyMCE)' ), + 'Anchors' => _x( 'Anchors', 'Link anchors (TinyMCE)' ), + + // Fullpage plugin + 'Document properties' => __( 'Document properties' ), + 'Robots' => __( 'Robots' ), + 'Title' => __( 'Title' ), + 'Keywords' => __( 'Keywords' ), + 'Encoding' => __( 'Encoding' ), + 'Description' => __( 'Description' ), + 'Author' => __( 'Author' ), + + // Media, image plugins + 'Insert/edit image' => __( 'Insert/edit image' ), + 'General' => __( 'General' ), + 'Advanced' => __( 'Advanced' ), + 'Source' => __( 'Source' ), + 'Border' => __( 'Border' ), + 'Constrain proportions' => __( 'Constrain proportions' ), + 'Vertical space' => __( 'Vertical space' ), + 'Image description' => __( 'Image description' ), + 'Style' => __( 'Style' ), + 'Dimensions' => __( 'Dimensions' ), + 'Insert image' => __( 'Insert image' ), + 'Insert date/time' => __( 'Insert date/time' ), + 'Insert/edit video' => __( 'Insert/edit video' ), + 'Poster' => __( 'Poster' ), + 'Alternative source' => __( 'Alternative source' ), + 'Paste your embed code below:' => __( 'Paste your embed code below:' ), + 'Insert video' => __( 'Insert video' ), + 'Embed' => __( 'Embed' ), + + // Each of these have a corresponding plugin + 'Special character' => __( 'Special character' ), + 'Right to left' => _x( 'Right to left', 'editor button' ), + 'Left to right' => _x( 'Left to right', 'editor button' ), + 'Emoticons' => __( 'Emoticons' ), + 'Nonbreaking space' => __( 'Nonbreaking space' ), + 'Page break' => __( 'Page break' ), + 'Paste as text' => __( 'Paste as text' ), + 'Preview' => __( 'Preview' ), + 'Print' => __( 'Print' ), + 'Save' => __( 'Save' ), + 'Fullscreen' => __( 'Fullscreen' ), + 'Horizontal line' => __( 'Horizontal line' ), + 'Horizontal space' => __( 'Horizontal space' ), + 'Restore last draft' => __( 'Restore last draft' ), + 'Insert/edit link' => __( 'Insert/edit link' ), + 'Remove link' => __( 'Remove link' ), + + 'Color' => __( 'Color' ), + 'Custom color' => __( 'Custom color' ), + 'Custom...' => _x( 'Custom...', 'label for custom color' ), + 'No color' => __( 'No color' ), + + // Spelling, search/replace plugins + 'Could not find the specified string.' => __( 'Could not find the specified string.' ), + 'Replace' => _x( 'Replace', 'find/replace' ), + 'Next' => _x( 'Next', 'find/replace' ), + /* translators: previous */ + 'Prev' => _x( 'Prev', 'find/replace' ), + 'Whole words' => _x( 'Whole words', 'find/replace' ), + 'Find and replace' => __( 'Find and replace' ), + 'Replace with' => _x('Replace with', 'find/replace' ), + 'Find' => _x( 'Find', 'find/replace' ), + 'Replace all' => _x( 'Replace all', 'find/replace' ), + 'Match case' => __( 'Match case' ), + 'Spellcheck' => __( 'Check Spelling' ), + 'Finish' => _x( 'Finish', 'spellcheck' ), + 'Ignore all' => _x( 'Ignore all', 'spellcheck' ), + 'Ignore' => _x( 'Ignore', 'spellcheck' ), + 'Add to Dictionary' => __( 'Add to Dictionary' ), + + // TinyMCE tables + 'Insert table' => __( 'Insert table' ), + 'Delete table' => __( 'Delete table' ), + 'Table properties' => __( 'Table properties' ), + 'Row properties' => __( 'Table row properties' ), + 'Cell properties' => __( 'Table cell properties' ), + 'Border color' => __( 'Border color' ), + + 'Row' => __( 'Row' ), + 'Rows' => __( 'Rows' ), + 'Column' => _x( 'Column', 'table column' ), + 'Cols' => _x( 'Cols', 'table columns' ), + 'Cell' => _x( 'Cell', 'table cell' ), + 'Header cell' => __( 'Header cell' ), + 'Header' => _x( 'Header', 'table header' ), + 'Body' => _x( 'Body', 'table body' ), + 'Footer' => _x( 'Footer', 'table footer' ), + + 'Insert row before' => __( 'Insert row before' ), + 'Insert row after' => __( 'Insert row after' ), + 'Insert column before' => __( 'Insert column before' ), + 'Insert column after' => __( 'Insert column after' ), + 'Paste row before' => __( 'Paste table row before' ), + 'Paste row after' => __( 'Paste table row after' ), + 'Delete row' => __( 'Delete row' ), + 'Delete column' => __( 'Delete column' ), + 'Cut row' => __( 'Cut table row' ), + 'Copy row' => __( 'Copy table row' ), + 'Merge cells' => __( 'Merge table cells' ), + 'Split cell' => __( 'Split table cell' ), + + 'Height' => __( 'Height' ), + 'Width' => __( 'Width' ), + 'Caption' => __( 'Caption' ), + 'Alignment' => __( 'Alignment' ), + 'H Align' => _x( 'H Align', 'horizontal table cell alignment' ), + 'Left' => __( 'Left' ), + 'Center' => __( 'Center' ), + 'Right' => __( 'Right' ), + 'None' => _x( 'None', 'table cell alignment attribute' ), + 'V Align' => _x( 'V Align', 'vertical table cell alignment' ), + 'Top' => __( 'Top' ), + 'Middle' => __( 'Middle' ), + 'Bottom' => __( 'Bottom' ), + + 'Row group' => __( 'Row group' ), + 'Column group' => __( 'Column group' ), + 'Row type' => __( 'Row type' ), + 'Cell type' => __( 'Cell type' ), + 'Cell padding' => __( 'Cell padding' ), + 'Cell spacing' => __( 'Cell spacing' ), + 'Scope' => _x( 'Scope', 'table cell scope attribute' ), + + 'Insert template' => _x( 'Insert template', 'TinyMCE' ), + 'Templates' => _x( 'Templates', 'TinyMCE' ), + + 'Background color' => __( 'Background color' ), + 'Text color' => __( 'Text color' ), + 'Show blocks' => _x( 'Show blocks', 'editor button' ), + 'Show invisible characters' => __( 'Show invisible characters' ), + + /* translators: word count */ + 'Words: {0}' => sprintf( __( 'Words: %s' ), '{0}' ), + 'Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.' => __( 'Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.' ) . "\n\n" . __( 'If you’re looking to paste rich content from Microsoft Word, try turning this option off. The editor will clean up text pasted from Word automatically.' ), + 'Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help' => __( 'Rich Text Area. Press Alt-Shift-H for help' ), + 'You have unsaved changes are you sure you want to navigate away?' => __( 'The changes you made will be lost if you navigate away from this page.' ), + 'Your browser doesn\'t support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.' => __( 'Your browser does not support direct access to the clipboard. Please use keyboard shortcuts or your browser’s edit menu instead.' ), + + // TinyMCE menus + 'Insert' => _x( 'Insert', 'TinyMCE menu' ), + 'File' => _x( 'File', 'TinyMCE menu' ), + 'Edit' => _x( 'Edit', 'TinyMCE menu' ), + 'Tools' => _x( 'Tools', 'TinyMCE menu' ), + 'View' => _x( 'View', 'TinyMCE menu' ), + 'Table' => _x( 'Table', 'TinyMCE menu' ), + 'Format' => _x( 'Format', 'TinyMCE menu' ), + + // WordPress strings + 'Keyboard Shortcuts' => __( 'Keyboard Shortcuts' ), + 'Toolbar Toggle' => __( 'Toolbar Toggle' ), + 'Insert Read More tag' => __( 'Insert Read More tag' ), + 'Read more...' => __( 'Read more...' ), // Title on the placeholder inside the editor + 'Distraction-free writing mode' => __( 'Distraction-free writing mode' ), + 'No alignment' => __( 'No alignment' ), // Tooltip for the 'alignnone' button in the image toolbar + 'Remove' => __( 'Remove' ), // Tooltip for the 'remove' button in the image toolbar + 'Edit ' => __( 'Edit' ), // Tooltip for the 'edit' button in the image toolbar + ); + + /** + * Link plugin (not included): + * Insert link + * Target + * New window + * Text to display + * The URL you entered seems to be an email address. Do you want to add the required mailto: prefix? + * The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix? + * Url + */ + + if ( ! $mce_locale ) { + $mce_locale = self::$mce_locale; + } + + /** + * Filter translated strings prepared for TinyMCE. + * + * @since 3.9.0 + * + * @param array $mce_translation Key/value pairs of strings. + * @param string $mce_locale Locale. + */ + $mce_translation = apply_filters( 'wp_mce_translation', $mce_translation, $mce_locale ); + + foreach ( $mce_translation as $key => $value ) { + // Remove strings that are not translated. + if ( $key === $value ) { + unset( $mce_translation[$key] ); + continue; + } + + if ( false !== strpos( $value, '&' ) ) { + $mce_translation[$key] = html_entity_decode( $value, ENT_QUOTES, 'UTF-8' ); + } + } + + // Set direction + if ( is_rtl() ) { + $mce_translation['_dir'] = 'rtl'; + } + + if ( $json_only ) { + return wp_json_encode( $mce_translation ); + } + + $baseurl = self::$baseurl ? self::$baseurl : includes_url( 'js/tinymce' ); + + return "tinymce.addI18n( '$mce_locale', " . wp_json_encode( $mce_translation ) . ");\n" . + "tinymce.ScriptLoader.markDone( '$baseurl/langs/$mce_locale.js' );\n"; + } + + public static function editor_js() { + global $tinymce_version, $concatenate_scripts, $compress_scripts; + + /** + * Filter "tiny_mce_version" is deprecated + * + * The tiny_mce_version filter is not needed since external plugins are loaded directly by TinyMCE. + * These plugins can be refreshed by appending query string to the URL passed to "mce_external_plugins" filter. + * If the plugin has a popup dialog, a query string can be added to the button action that opens it (in the plugin's code). + */ + $version = 'ver=' . $tinymce_version; + $tmce_on = !empty(self::$mce_settings); + + if ( ! isset($concatenate_scripts) ) + script_concat_settings(); + + $compressed = $compress_scripts && $concatenate_scripts && isset($_SERVER['HTTP_ACCEPT_ENCODING']) + && false !== stripos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip'); + + $mceInit = $qtInit = ''; + if ( $tmce_on ) { + foreach ( self::$mce_settings as $editor_id => $init ) { + $options = self::_parse_init( $init ); + $mceInit .= "'$editor_id':{$options},"; + } + $mceInit = '{' . trim($mceInit, ',') . '}'; + } else { + $mceInit = '{}'; + } + + if ( !empty(self::$qt_settings) ) { + foreach ( self::$qt_settings as $editor_id => $init ) { + $options = self::_parse_init( $init ); + $qtInit .= "'$editor_id':{$options},"; + } + $qtInit = '{' . trim($qtInit, ',') . '}'; + } else { + $qtInit = '{}'; + } + + $ref = array( + 'plugins' => implode( ',', self::$plugins ), + 'theme' => 'modern', + 'language' => self::$mce_locale + ); + + $suffix = ( defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ) ? '' : '.min'; + + /** + * Fires immediately before the TinyMCE settings are printed. + * + * @since 3.2.0 + * + * @param array $mce_settings TinyMCE settings array. + */ + do_action( 'before_wp_tiny_mce', self::$mce_settings ); + ?> + + <script type="text/javascript"> + tinyMCEPreInit = { + baseURL: "<?php echo self::$baseurl; ?>", + suffix: "<?php echo $suffix; ?>", + <?php + + if ( self::$drag_drop_upload ) { + echo 'dragDropUpload: true,'; + } + + ?> + mceInit: <?php echo $mceInit; ?>, + qtInit: <?php echo $qtInit; ?>, + ref: <?php echo self::_parse_init( $ref ); ?>, + load_ext: function(url,lang){var sl=tinymce.ScriptLoader;sl.markDone(url+'/langs/'+lang+'.js');sl.markDone(url+'/langs/'+lang+'_dlg.js');} + }; + </script> + <?php + + $baseurl = self::$baseurl; + // Load tinymce.js when running from /src, else load wp-tinymce.js.gz (production) or tinymce.min.js (SCRIPT_DEBUG) + $mce_suffix = false !== strpos( $GLOBALS['wp_version'], '-src' ) ? '' : '.min'; + + if ( $tmce_on ) { + if ( $compressed ) { + echo "<script type='text/javascript' src='{$baseurl}/wp-tinymce.php?c=1&$version'></script>\n"; + } else { + echo "<script type='text/javascript' src='{$baseurl}/tinymce{$mce_suffix}.js?$version'></script>\n"; + echo "<script type='text/javascript' src='{$baseurl}/plugins/compat3x/plugin{$suffix}.js?$version'></script>\n"; + } + + echo "<script type='text/javascript'>\n" . self::wp_mce_translation() . "</script>\n"; + + if ( self::$ext_plugins ) { + // Load the old-format English strings to prevent unsightly labels in old style popups + echo "<script type='text/javascript' src='{$baseurl}/langs/wp-langs-en.js?$version'></script>\n"; + } + } + + /** + * Fires after tinymce.js is loaded, but before any TinyMCE editor + * instances are created. + * + * @since 3.9.0 + * + * @param array $mce_settings TinyMCE settings array. + */ + do_action( 'wp_tiny_mce_init', self::$mce_settings ); + + ?> + <script type="text/javascript"> + <?php + + if ( self::$ext_plugins ) + echo self::$ext_plugins . "\n"; + + if ( ! is_admin() ) + echo 'var ajaxurl = "' . admin_url( 'admin-ajax.php', 'relative' ) . '";'; + + ?> + + ( function() { + var init, edId, qtId, firstInit, wrapper; + + if ( typeof tinymce !== 'undefined' ) { + for ( edId in tinyMCEPreInit.mceInit ) { + if ( firstInit ) { + init = tinyMCEPreInit.mceInit[edId] = tinymce.extend( {}, firstInit, tinyMCEPreInit.mceInit[edId] ); + } else { + init = firstInit = tinyMCEPreInit.mceInit[edId]; + } + + wrapper = tinymce.DOM.select( '#wp-' + edId + '-wrap' )[0]; + + if ( ( tinymce.DOM.hasClass( wrapper, 'tmce-active' ) || ! tinyMCEPreInit.qtInit.hasOwnProperty( edId ) ) && + ! init.wp_skip_init ) { + + try { + tinymce.init( init ); + + if ( ! window.wpActiveEditor ) { + window.wpActiveEditor = edId; + } + } catch(e){} + } + } + } + + if ( typeof quicktags !== 'undefined' ) { + for ( qtId in tinyMCEPreInit.qtInit ) { + try { + quicktags( tinyMCEPreInit.qtInit[qtId] ); + + if ( ! window.wpActiveEditor ) { + window.wpActiveEditor = qtId; + } + } catch(e){}; + } + } + + if ( typeof jQuery !== 'undefined' ) { + jQuery('.wp-editor-wrap').on( 'click.wp-editor', function() { + if ( this.id ) { + window.wpActiveEditor = this.id.slice( 3, -5 ); + } + }); + } else { + for ( qtId in tinyMCEPreInit.qtInit ) { + document.getElementById( 'wp-' + qtId + '-wrap' ).onclick = function() { + window.wpActiveEditor = this.id.slice( 3, -5 ); + } + } + } + }()); + </script> + <?php + + if ( in_array( 'wplink', self::$plugins, true ) || in_array( 'link', self::$qt_buttons, true ) ) + self::wp_link_dialog(); + + if ( in_array( 'wpfullscreen', self::$plugins, true ) || in_array( 'fullscreen', self::$qt_buttons, true ) ) + self::wp_fullscreen_html(); + + /** + * Fires after any core TinyMCE editor instances are created. + * + * @since 3.2.0 + * + * @param array $mce_settings TinyMCE settings array. + */ + do_action( 'after_wp_tiny_mce', self::$mce_settings ); + } + + public static function wp_fullscreen_html() { + global $content_width; + $post = get_post(); + + $width = isset( $content_width ) && 800 > $content_width ? $content_width : 800; + $width = $width + 22; // compensate for the padding and border + $dfw_width = get_user_setting( 'dfw_width', $width ); + $save = isset( $post->post_status ) && $post->post_status == 'publish' ? __('Update') : __('Save'); + + ?> + <div id="wp-fullscreen-body" class="wp-core-ui<?php if ( is_rtl() ) echo ' rtl'; ?>" data-theme-width="<?php echo (int) $width; ?>" data-dfw-width="<?php echo (int) $dfw_width; ?>"> + <div id="fullscreen-topbar"> + <div id="wp-fullscreen-toolbar"> + <div id="wp-fullscreen-close"><a href="#" onclick="wp.editor.fullscreen.off();return false;"><?php _e('Exit fullscreen'); ?></a></div> + <div id="wp-fullscreen-central-toolbar" style="width:<?php echo $width; ?>px;"> + + <div id="wp-fullscreen-mode-bar"> + <div id="wp-fullscreen-modes" class="button-group"> + <a class="button wp-fullscreen-mode-tinymce" href="#" onclick="wp.editor.fullscreen.switchmode( 'tinymce' ); return false;"><?php _e( 'Visual' ); ?></a> + <a class="button wp-fullscreen-mode-html" href="#" onclick="wp.editor.fullscreen.switchmode( 'html' ); return false;"><?php _ex( 'Text', 'Name for the Text editor tab (formerly HTML)' ); ?></a> + </div> + </div> + + <div id="wp-fullscreen-button-bar"><div id="wp-fullscreen-buttons" class="mce-toolbar"> + <?php + + $buttons = array( + // format: title, onclick, show in both editors + 'bold' => array( 'title' => __('Bold (Ctrl + B)'), 'both' => false ), + 'italic' => array( 'title' => __('Italic (Ctrl + I)'), 'both' => false ), + 'bullist' => array( 'title' => __('Unordered list (Alt + Shift + U)'), 'both' => false ), + 'numlist' => array( 'title' => __('Ordered list (Alt + Shift + O)'), 'both' => false ), + 'blockquote' => array( 'title' => __('Blockquote (Alt + Shift + Q)'), 'both' => false ), + 'wp-media-library' => array( 'title' => __('Media library (Alt + Shift + M)'), 'both' => true ), + 'link' => array( 'title' => __('Insert/edit link (Alt + Shift + A)'), 'both' => true ), + 'unlink' => array( 'title' => __('Unlink (Alt + Shift + S)'), 'both' => false ), + 'help' => array( 'title' => __('Help (Alt + Shift + H)'), 'both' => false ), + ); + + /** + * Filter the list of TinyMCE buttons for the fullscreen + * 'Distraction-Free Writing' editor. + * + * @since 3.2.0 + * + * @param array $buttons An array of TinyMCE buttons for the DFW editor. + */ + $buttons = apply_filters( 'wp_fullscreen_buttons', $buttons ); + + foreach ( $buttons as $button => $args ) { + if ( 'separator' == $args ) { + continue; + } + + $onclick = ! empty( $args['onclick'] ) ? ' onclick="' . $args['onclick'] . '"' : ''; + $title = esc_attr( $args['title'] ); + ?> + + <div class="mce-widget mce-btn<?php if ( $args['both'] ) { ?> wp-fullscreen-both<?php } ?>"> + <button type="button" aria-label="<?php echo $title; ?>" title="<?php echo $title; ?>"<?php echo $onclick; ?> id="wp_fs_<?php echo $button; ?>"> + <i class="mce-ico mce-i-<?php echo $button; ?>"></i> + </button> + </div> + <?php + } + + ?> + + </div></div> + + <div id="wp-fullscreen-save"> + <input type="button" class="button button-primary right" value="<?php echo $save; ?>" onclick="wp.editor.fullscreen.save();" /> + <span class="wp-fullscreen-saved-message"><?php if ( $post->post_status == 'publish' ) _e('Updated.'); else _e('Saved.'); ?></span> + <span class="wp-fullscreen-error-message"><?php _e('Save failed.'); ?></span> + <span class="spinner"></span> + </div> + + </div> + </div> + </div> + <div id="wp-fullscreen-statusbar"> + <div id="wp-fullscreen-status"> + <div id="wp-fullscreen-count"><?php printf( __( 'Word count: %s' ), '<span class="word-count">0</span>' ); ?></div> + <div id="wp-fullscreen-tagline"><?php _e('Just write.'); ?></div> + </div> + </div> + </div> + + <div class="fullscreen-overlay" id="fullscreen-overlay"></div> + <div class="fullscreen-overlay fullscreen-fader fade-300" id="fullscreen-fader"></div> + <?php + } + + /** + * Performs post queries for internal linking. + * + * @since 3.1.0 + * + * @param array $args Optional. Accepts 'pagenum' and 's' (search) arguments. + * @return false|array Results. + */ + public static function wp_link_query( $args = array() ) { + $pts = get_post_types( array( 'public' => true ), 'objects' ); + $pt_names = array_keys( $pts ); + + $query = array( + 'post_type' => $pt_names, + 'suppress_filters' => true, + 'update_post_term_cache' => false, + 'update_post_meta_cache' => false, + 'post_status' => 'publish', + 'posts_per_page' => 20, + ); + + $args['pagenum'] = isset( $args['pagenum'] ) ? absint( $args['pagenum'] ) : 1; + + if ( isset( $args['s'] ) ) + $query['s'] = $args['s']; + + $query['offset'] = $args['pagenum'] > 1 ? $query['posts_per_page'] * ( $args['pagenum'] - 1 ) : 0; + + /** + * Filter the link query arguments. + * + * Allows modification of the link query arguments before querying. + * + * @see WP_Query for a full list of arguments + * + * @since 3.7.0 + * + * @param array $query An array of WP_Query arguments. + */ + $query = apply_filters( 'wp_link_query_args', $query ); + + // Do main query. + $get_posts = new WP_Query; + $posts = $get_posts->query( $query ); + // Check if any posts were found. + if ( ! $get_posts->post_count ) + return false; + + // Build results. + $results = array(); + foreach ( $posts as $post ) { + if ( 'post' == $post->post_type ) + $info = mysql2date( __( 'Y/m/d' ), $post->post_date ); + else + $info = $pts[ $post->post_type ]->labels->singular_name; + + $results[] = array( + 'ID' => $post->ID, + 'title' => trim( esc_html( strip_tags( get_the_title( $post ) ) ) ), + 'permalink' => get_permalink( $post->ID ), + 'info' => $info, + ); + } + + /** + * Filter the link query results. + * + * Allows modification of the returned link query results. + * + * @since 3.7.0 + * + * @see 'wp_link_query_args' filter + * + * @param array $results { + * An associative array of query results. + * + * @type array { + * @type int $ID Post ID. + * @type string $title The trimmed, escaped post title. + * @type string $permalink Post permalink. + * @type string $info A 'Y/m/d'-formatted date for 'post' post type, + * the 'singular_name' post type label otherwise. + * } + * } + * @param array $query An array of WP_Query arguments. + */ + return apply_filters( 'wp_link_query', $results, $query ); + } + + /** + * Dialog for internal linking. + * + * @since 3.1.0 + */ + public static function wp_link_dialog() { + $search_panel_visible = '1' == get_user_setting( 'wplink', '0' ) ? ' search-panel-visible' : ''; + + // display: none is required here, see #WP27605 + ?> + <div id="wp-link-backdrop" style="display: none"></div> + <div id="wp-link-wrap" class="wp-core-ui<?php echo $search_panel_visible; ?>" style="display: none"> + <form id="wp-link" tabindex="-1"> + <?php wp_nonce_field( 'internal-linking', '_ajax_linking_nonce', false ); ?> + <div id="link-modal-title"> + <?php _e( 'Insert/edit link' ) ?> + <button type="button" id="wp-link-close"><span class="screen-reader-text"><?php _e( 'Close' ); ?></span></button> + </div> + <div id="link-selector"> + <div id="link-options"> + <p class="howto"><?php _e( 'Enter the destination URL' ); ?></p> + <div> + <label><span><?php _e( 'URL' ); ?></span><input id="url-field" type="text" name="href" /></label> + </div> + <div> + <label><span><?php _e( 'Title' ); ?></span><input id="link-title-field" type="text" name="linktitle" /></label> + </div> + <div class="link-target"> + <label><span> </span><input type="checkbox" id="link-target-checkbox" /> <?php _e( 'Open link in a new window/tab' ); ?></label> + </div> + </div> + <p class="howto"><a href="#" id="wp-link-search-toggle"><?php _e( 'Or link to existing content' ); ?></a></p> + <div id="search-panel"> + <div class="link-search-wrapper"> + <label> + <span class="search-label"><?php _e( 'Search' ); ?></span> + <input type="search" id="search-field" class="link-search-field" autocomplete="off" /> + <span class="spinner"></span> + </label> + </div> + <div id="search-results" class="query-results" tabindex="0"> + <ul></ul> + <div class="river-waiting"> + <span class="spinner"></span> + </div> + </div> + <div id="most-recent-results" class="query-results" tabindex="0"> + <div class="query-notice" id="query-notice-message"> + <em class="query-notice-default"><?php _e( 'No search term specified. Showing recent items.' ); ?></em> + <em class="query-notice-hint screen-reader-text"><?php _e( 'Search or use up and down arrow keys to select an item.' ); ?></em> + </div> + <ul></ul> + <div class="river-waiting"> + <span class="spinner"></span> + </div> + </div> + </div> + </div> + <div class="submitbox"> + <div id="wp-link-cancel"> + <a class="submitdelete deletion" href="#"><?php _e( 'Cancel' ); ?></a> + </div> + <div id="wp-link-update"> + <input type="submit" value="<?php esc_attr_e( 'Add Link' ); ?>" class="button button-primary" id="wp-link-submit" name="wp-link-submit"> + </div> + </div> + </form> + </div> + <?php + } +} diff --git a/wp-includes/class-wp-embed.php b/wp-includes/class-wp-embed.php new file mode 100644 index 0000000..a10024a --- /dev/null +++ b/wp-includes/class-wp-embed.php @@ -0,0 +1,357 @@ +<?php +/** + * API for easily embedding rich media such as videos and images into content. + * + * @package WordPress + * @subpackage Embed + * @since 2.9.0 + */ +class WP_Embed { + public $handlers = array(); + public $post_ID; + public $usecache = true; + public $linkifunknown = true; + + /** + * When an URL cannot be embedded, return false instead of returning a link + * or the URL. Bypasses the 'embed_maybe_make_link' filter. + */ + public $return_false_on_fail = false; + + /** + * Constructor + */ + public function __construct() { + // Hack to get the [embed] shortcode to run before wpautop() + add_filter( 'the_content', array( $this, 'run_shortcode' ), 8 ); + + // Shortcode placeholder for strip_shortcodes() + add_shortcode( 'embed', '__return_false' ); + + // Attempts to embed all URLs in a post + add_filter( 'the_content', array( $this, 'autoembed' ), 8 ); + + // After a post is saved, cache oEmbed items via AJAX + add_action( 'edit_form_advanced', array( $this, 'maybe_run_ajax_cache' ) ); + } + + /** + * Process the [embed] shortcode. + * + * Since the [embed] shortcode needs to be run earlier than other shortcodes, + * this function removes all existing shortcodes, registers the [embed] shortcode, + * calls {@link do_shortcode()}, and then re-registers the old shortcodes. + * + * @uses $shortcode_tags + * + * @param string $content Content to parse + * @return string Content with shortcode parsed + */ + public function run_shortcode( $content ) { + global $shortcode_tags; + + // Back up current registered shortcodes and clear them all out + $orig_shortcode_tags = $shortcode_tags; + remove_all_shortcodes(); + + add_shortcode( 'embed', array( $this, 'shortcode' ) ); + + // Do the shortcode (only the [embed] one is registered) + $content = do_shortcode( $content ); + + // Put the original shortcodes back + $shortcode_tags = $orig_shortcode_tags; + + return $content; + } + + /** + * If a post/page was saved, then output JavaScript to make + * an AJAX request that will call WP_Embed::cache_oembed(). + */ + public function maybe_run_ajax_cache() { + $post = get_post(); + + if ( ! $post || empty( $_GET['message'] ) ) + return; + +?> +<script type="text/javascript"> +/* <![CDATA[ */ + jQuery(document).ready(function($){ + $.get("<?php echo admin_url( 'admin-ajax.php?action=oembed-cache&post=' . $post->ID, 'relative' ); ?>"); + }); +/* ]]> */ +</script> +<?php + } + + /** + * Register an embed handler. Do not use this function directly, use {@link wp_embed_register_handler()} instead. + * This function should probably also only be used for sites that do not support oEmbed. + * + * @param string $id An internal ID/name for the handler. Needs to be unique. + * @param string $regex The regex that will be used to see if this handler should be used for a URL. + * @param callback $callback The callback function that will be called if the regex is matched. + * @param int $priority Optional. Used to specify the order in which the registered handlers will be tested (default: 10). Lower numbers correspond with earlier testing, and handlers with the same priority are tested in the order in which they were added to the action. + */ + public function register_handler( $id, $regex, $callback, $priority = 10 ) { + $this->handlers[$priority][$id] = array( + 'regex' => $regex, + 'callback' => $callback, + ); + } + + /** + * Unregister a previously registered embed handler. Do not use this function directly, use {@link wp_embed_unregister_handler()} instead. + * + * @param string $id The handler ID that should be removed. + * @param int $priority Optional. The priority of the handler to be removed (default: 10). + */ + public function unregister_handler( $id, $priority = 10 ) { + if ( isset($this->handlers[$priority][$id]) ) + unset($this->handlers[$priority][$id]); + } + + /** + * The {@link do_shortcode()} callback function. + * + * Attempts to convert a URL into embed HTML. Starts by checking the URL against the regex of the registered embed handlers. + * If none of the regex matches and it's enabled, then the URL will be given to the {@link WP_oEmbed} class. + * + * @param array $attr { + * Shortcode attributes. Optional. + * + * @type int $width Width of the embed in pixels. + * @type int $height Height of the embed in pixels. + * } + * @param string $url The URL attempting to be embedded. + * @return string The embed HTML on success, otherwise the original URL. + */ + public function shortcode( $attr, $url = '' ) { + $post = get_post(); + + if ( empty( $url ) && ! empty( $attr['src'] ) ) { + $url = $attr['src']; + } + + if ( empty( $url ) ) + return ''; + + $rawattr = $attr; + $attr = wp_parse_args( $attr, wp_embed_defaults( $url ) ); + + // kses converts & into & and we need to undo this + // See https://core.trac.wordpress.org/ticket/11311 + $url = str_replace( '&', '&', $url ); + + // Look for known internal handlers + ksort( $this->handlers ); + foreach ( $this->handlers as $priority => $handlers ) { + foreach ( $handlers as $id => $handler ) { + if ( preg_match( $handler['regex'], $url, $matches ) && is_callable( $handler['callback'] ) ) { + if ( false !== $return = call_user_func( $handler['callback'], $matches, $attr, $url, $rawattr ) ) + /** + * Filter the returned embed handler. + * + * @since 2.9.0 + * + * @see WP_Embed::shortcode() + * + * @param mixed $return The shortcode callback function to call. + * @param string $url The attempted embed URL. + * @param array $attr An array of shortcode attributes. + */ + return apply_filters( 'embed_handler_html', $return, $url, $attr ); + } + } + } + + $post_ID = ( ! empty( $post->ID ) ) ? $post->ID : null; + if ( ! empty( $this->post_ID ) ) // Potentially set by WP_Embed::cache_oembed() + $post_ID = $this->post_ID; + + // Unknown URL format. Let oEmbed have a go. + if ( $post_ID ) { + + // Check for a cached result (stored in the post meta) + $key_suffix = md5( $url . serialize( $attr ) ); + $cachekey = '_oembed_' . $key_suffix; + $cachekey_time = '_oembed_time_' . $key_suffix; + + /** + * Filter the oEmbed TTL value (time to live). + * + * @since 4.0.0 + * + * @param int $time Time to live (in seconds). + * @param string $url The attempted embed URL. + * @param array $attr An array of shortcode attributes. + * @param int $post_ID Post ID. + */ + $ttl = apply_filters( 'oembed_ttl', DAY_IN_SECONDS, $url, $attr, $post_ID ); + + $cache = get_post_meta( $post_ID, $cachekey, true ); + $cache_time = get_post_meta( $post_ID, $cachekey_time, true ); + + if ( ! $cache_time ) { + $cache_time = 0; + } + + $cached_recently = ( time() - $cache_time ) < $ttl; + + if ( $this->usecache || $cached_recently ) { + // Failures are cached. Serve one if we're using the cache. + if ( '{{unknown}}' === $cache ) + return $this->maybe_make_link( $url ); + + if ( ! empty( $cache ) ) { + /** + * Filter the cached oEmbed HTML. + * + * @since 2.9.0 + * + * @see WP_Embed::shortcode() + * + * @param mixed $cache The cached HTML result, stored in post meta. + * @param string $url The attempted embed URL. + * @param array $attr An array of shortcode attributes. + * @param int $post_ID Post ID. + */ + return apply_filters( 'embed_oembed_html', $cache, $url, $attr, $post_ID ); + } + } + + /** + * Filter whether to inspect the given URL for discoverable link tags. + * + * @since 2.9.0 + * + * @see WP_oEmbed::discover() + * + * @param bool $enable Whether to enable `<link>` tag discovery. Default false. + */ + $attr['discover'] = ( apply_filters( 'embed_oembed_discover', false ) && author_can( $post_ID, 'unfiltered_html' ) ); + + // Use oEmbed to get the HTML + $html = wp_oembed_get( $url, $attr ); + + // Maybe cache the result + if ( $html ) { + update_post_meta( $post_ID, $cachekey, $html ); + update_post_meta( $post_ID, $cachekey_time, time() ); + } elseif ( ! $cache ) { + update_post_meta( $post_ID, $cachekey, '{{unknown}}' ); + } + + // If there was a result, return it + if ( $html ) { + /** This filter is documented in wp-includes/class-wp-embed.php */ + return apply_filters( 'embed_oembed_html', $html, $url, $attr, $post_ID ); + } + } + + // Still unknown + return $this->maybe_make_link( $url ); + } + + /** + * Delete all oEmbed caches. Unused by core as of 4.0.0. + * + * @param int $post_ID Post ID to delete the caches for. + */ + public function delete_oembed_caches( $post_ID ) { + $post_metas = get_post_custom_keys( $post_ID ); + if ( empty($post_metas) ) + return; + + foreach( $post_metas as $post_meta_key ) { + if ( '_oembed_' == substr( $post_meta_key, 0, 8 ) ) + delete_post_meta( $post_ID, $post_meta_key ); + } + } + + /** + * Triggers a caching of all oEmbed results. + * + * @param int $post_ID Post ID to do the caching for. + */ + public function cache_oembed( $post_ID ) { + $post = get_post( $post_ID ); + + $post_types = get_post_types( array( 'show_ui' => true ) ); + /** + * Filter the array of post types to cache oEmbed results for. + * + * @since 2.9.0 + * + * @param array $post_types Array of post types to cache oEmbed results for. Defaults to post types with `show_ui` set to true. + */ + if ( empty( $post->ID ) || ! in_array( $post->post_type, apply_filters( 'embed_cache_oembed_types', $post_types ) ) ){ + return; + } + + // Trigger a caching + if ( ! empty( $post->post_content ) ) { + $this->post_ID = $post->ID; + $this->usecache = false; + + $content = $this->run_shortcode( $post->post_content ); + $this->autoembed( $content ); + + $this->usecache = true; + } + } + + /** + * Passes any unlinked URLs that are on their own line to {@link WP_Embed::shortcode()} for potential embedding. + * + * @uses WP_Embed::autoembed_callback() + * + * @param string $content The content to be searched. + * @return string Potentially modified $content. + */ + public function autoembed( $content ) { + return preg_replace_callback( '|^\s*(https?://[^\s"]+)\s*$|im', array( $this, 'autoembed_callback' ), $content ); + } + + /** + * Callback function for {@link WP_Embed::autoembed()}. + * + * @param array $match A regex match array. + * @return string The embed HTML on success, otherwise the original URL. + */ + public function autoembed_callback( $match ) { + $oldval = $this->linkifunknown; + $this->linkifunknown = false; + $return = $this->shortcode( array(), $match[1] ); + $this->linkifunknown = $oldval; + + return "\n$return\n"; + } + + /** + * Conditionally makes a hyperlink based on an internal class variable. + * + * @param string $url URL to potentially be linked. + * @return false|string Linked URL or the original URL. False if 'return_false_on_fail' is true. + */ + public function maybe_make_link( $url ) { + if ( $this->return_false_on_fail ) { + return false; + } + + $output = ( $this->linkifunknown ) ? '<a href="' . esc_url($url) . '">' . esc_html($url) . '</a>' : $url; + + /** + * Filter the returned, maybe-linked embed URL. + * + * @since 2.9.0 + * + * @param string $output The linked or original URL. + * @param string $url The original URL. + */ + return apply_filters( 'embed_maybe_make_link', $output, $url ); + } +} +$GLOBALS['wp_embed'] = new WP_Embed(); diff --git a/wp-includes/class-wp-error.php b/wp-includes/class-wp-error.php new file mode 100644 index 0000000..cee2aff --- /dev/null +++ b/wp-includes/class-wp-error.php @@ -0,0 +1,276 @@ +<?php +/** + * WordPress Error API. + * + * Contains the WP_Error class and the is_wp_error() function. + * + * @package WordPress + */ + +/** + * WordPress Error class. + * + * Container for checking for WordPress errors and error messages. Return + * WP_Error and use {@link is_wp_error()} to check if this class is returned. + * Many core WordPress functions pass this class in the event of an error and + * if not handled properly will result in code errors. + * + * @package WordPress + * @since 2.1.0 + */ +class WP_Error { + /** + * Stores the list of errors. + * + * @since 2.1.0 + * @var array + * @access private + */ + private $errors = array(); + + /** + * Stores the list of data for error codes. + * + * @since 2.1.0 + * @var array + * @access private + */ + private $error_data = array(); + + /** + * Initialize the error. + * + * If `$code` is empty, the other parameters will be ignored. + * When `$code` is not empty, `$message` will be used even if + * it is empty. The `$data` parameter will be used only if it + * is not empty. + * + * Though the class is constructed with a single error code and + * message, multiple codes can be added using the `add()` method. + * + * @since 2.1.0 + * + * @param string|int $code Error code + * @param string $message Error message + * @param mixed $data Optional. Error data. + * @return WP_Error + */ + public function __construct( $code = '', $message = '', $data = '' ) { + if ( empty($code) ) + return; + + $this->errors[$code][] = $message; + + if ( ! empty($data) ) + $this->error_data[$code] = $data; + } + + /** + * Make private properties readable for backwards compatibility. + * + * @since 4.0.0 + * @access public + * + * @param string $name Property to get. + * @return mixed Property. + */ + public function __get( $name ) { + return $this->$name; + } + + /** + * Make private properties settable for backwards compatibility. + * + * @since 4.0.0 + * @access public + * + * @param string $name Property to set. + * @param mixed $value Property value. + * @return mixed Newly-set property. + */ + public function __set( $name, $value ) { + return $this->$name = $value; + } + + /** + * Make private properties checkable for backwards compatibility. + * + * @since 4.0.0 + * @access public + * + * @param string $name Property to check if set. + * @return bool Whether the property is set. + */ + public function __isset( $name ) { + return isset( $this->$name ); + } + + /** + * Make private properties un-settable for backwards compatibility. + * + * @since 4.0.0 + * @access public + * + * @param string $name Property to unset. + */ + public function __unset( $name ) { + unset( $this->$name ); + } + + /** + * Retrieve all error codes. + * + * @since 2.1.0 + * @access public + * + * @return array List of error codes, if available. + */ + public function get_error_codes() { + if ( empty($this->errors) ) + return array(); + + return array_keys($this->errors); + } + + /** + * Retrieve first error code available. + * + * @since 2.1.0 + * @access public + * + * @return string|int Empty string, if no error codes. + */ + public function get_error_code() { + $codes = $this->get_error_codes(); + + if ( empty($codes) ) + return ''; + + return $codes[0]; + } + + /** + * Retrieve all error messages or error messages matching code. + * + * @since 2.1.0 + * + * @param string|int $code Optional. Retrieve messages matching code, if exists. + * @return array Error strings on success, or empty array on failure (if using code parameter). + */ + public function get_error_messages($code = '') { + // Return all messages if no code specified. + if ( empty($code) ) { + $all_messages = array(); + foreach ( (array) $this->errors as $code => $messages ) + $all_messages = array_merge($all_messages, $messages); + + return $all_messages; + } + + if ( isset($this->errors[$code]) ) + return $this->errors[$code]; + else + return array(); + } + + /** + * Get single error message. + * + * This will get the first message available for the code. If no code is + * given then the first code available will be used. + * + * @since 2.1.0 + * + * @param string|int $code Optional. Error code to retrieve message. + * @return string + */ + public function get_error_message($code = '') { + if ( empty($code) ) + $code = $this->get_error_code(); + $messages = $this->get_error_messages($code); + if ( empty($messages) ) + return ''; + return $messages[0]; + } + + /** + * Retrieve error data for error code. + * + * @since 2.1.0 + * + * @param string|int $code Optional. Error code. + * @return mixed Null, if no errors. + */ + public function get_error_data($code = '') { + if ( empty($code) ) + $code = $this->get_error_code(); + + if ( isset($this->error_data[$code]) ) + return $this->error_data[$code]; + return null; + } + + /** + * Add an error or append additional message to an existing error. + * + * @since 2.1.0 + * @access public + * + * @param string|int $code Error code. + * @param string $message Error message. + * @param mixed $data Optional. Error data. + */ + public function add($code, $message, $data = '') { + $this->errors[$code][] = $message; + if ( ! empty($data) ) + $this->error_data[$code] = $data; + } + + /** + * Add data for error code. + * + * The error code can only contain one error data. + * + * @since 2.1.0 + * + * @param mixed $data Error data. + * @param string|int $code Error code. + */ + public function add_data($data, $code = '') { + if ( empty($code) ) + $code = $this->get_error_code(); + + $this->error_data[$code] = $data; + } + + /** + * Removes the specified error. + * + * This function removes all error messages associated with the specified + * error code, along with any error data for that code. + * + * @since 4.1.0 + * + * @param string|int $code Error code. + */ + public function remove( $code ) { + unset( $this->errors[ $code ] ); + unset( $this->error_data[ $code ] ); + } +} + +/** + * Check whether variable is a WordPress Error. + * + * Returns true if $thing is an object of the WP_Error class. + * + * @since 2.1.0 + * + * @param mixed $thing Check if unknown variable is a WP_Error object. + * @return bool True, if WP_Error. False, if not WP_Error. + */ +function is_wp_error($thing) { + if ( is_object($thing) && is_a($thing, 'WP_Error') ) + return true; + return false; +} diff --git a/wp-includes/class-wp-http-ixr-client.php b/wp-includes/class-wp-http-ixr-client.php new file mode 100644 index 0000000..0605955 --- /dev/null +++ b/wp-includes/class-wp-http-ixr-client.php @@ -0,0 +1,108 @@ +<?php +/** + * WP_HTTP_IXR_Client + * + * @package WordPress + * @since 3.1.0 + * + */ +class WP_HTTP_IXR_Client extends IXR_Client { + public $scheme; + /** + * @var IXR_Error + */ + public $error; + + /** + * @param string $server + * @param string|bool $path + * @param int|bool $port + * @param int $timeout + */ + public function __construct($server, $path = false, $port = false, $timeout = 15) { + if ( ! $path ) { + // Assume we have been given a URL instead + $bits = parse_url($server); + $this->scheme = $bits['scheme']; + $this->server = $bits['host']; + $this->port = isset($bits['port']) ? $bits['port'] : $port; + $this->path = !empty($bits['path']) ? $bits['path'] : '/'; + + // Make absolutely sure we have a path + if ( ! $this->path ) { + $this->path = '/'; + } + + if ( ! empty( $bits['query'] ) ) { + $this->path .= '?' . $bits['query']; + } + } else { + $this->scheme = 'http'; + $this->server = $server; + $this->path = $path; + $this->port = $port; + } + $this->useragent = 'The Incutio XML-RPC PHP Library'; + $this->timeout = $timeout; + } + + public function query() { + $args = func_get_args(); + $method = array_shift($args); + $request = new IXR_Request($method, $args); + $xml = $request->getXml(); + + $port = $this->port ? ":$this->port" : ''; + $url = $this->scheme . '://' . $this->server . $port . $this->path; + $args = array( + 'headers' => array('Content-Type' => 'text/xml'), + 'user-agent' => $this->useragent, + 'body' => $xml, + ); + + // Merge Custom headers ala #8145 + foreach ( $this->headers as $header => $value ) + $args['headers'][$header] = $value; + + if ( $this->timeout !== false ) + $args['timeout'] = $this->timeout; + + // Now send the request + if ( $this->debug ) + echo '<pre class="ixr_request">' . htmlspecialchars($xml) . "\n</pre>\n\n"; + + $response = wp_remote_post($url, $args); + + if ( is_wp_error($response) ) { + $errno = $response->get_error_code(); + $errorstr = $response->get_error_message(); + $this->error = new IXR_Error(-32300, "transport error: $errno $errorstr"); + return false; + } + + if ( 200 != wp_remote_retrieve_response_code( $response ) ) { + $this->error = new IXR_Error(-32301, 'transport error - HTTP status code was not 200 (' . wp_remote_retrieve_response_code( $response ) . ')'); + return false; + } + + if ( $this->debug ) + echo '<pre class="ixr_response">' . htmlspecialchars( wp_remote_retrieve_body( $response ) ) . "\n</pre>\n\n"; + + // Now parse what we've got back + $this->message = new IXR_Message( wp_remote_retrieve_body( $response ) ); + if ( ! $this->message->parse() ) { + // XML error + $this->error = new IXR_Error(-32700, 'parse error. not well formed'); + return false; + } + + // Is the message a fault? + if ( $this->message->messageType == 'fault' ) { + $this->error = new IXR_Error($this->message->faultCode, $this->message->faultString); + return false; + } + + // Message must be OK + return true; + } +} diff --git a/wp-includes/class-wp-image-editor-gd.php b/wp-includes/class-wp-image-editor-gd.php new file mode 100644 index 0000000..ddba1ff --- /dev/null +++ b/wp-includes/class-wp-image-editor-gd.php @@ -0,0 +1,466 @@ +<?php +/** + * WordPress GD Image Editor + * + * @package WordPress + * @subpackage Image_Editor + */ + +/** + * WordPress Image Editor Class for Image Manipulation through GD + * + * @since 3.5.0 + * @package WordPress + * @subpackage Image_Editor + * @uses WP_Image_Editor Extends class + */ +class WP_Image_Editor_GD extends WP_Image_Editor { + /** + * @var resource + */ + protected $image; // GD Resource + + public function __destruct() { + if ( $this->image ) { + // we don't need the original in memory anymore + imagedestroy( $this->image ); + } + } + + /** + * Checks to see if current environment supports GD. + * + * @since 3.5.0 + * @access public + * + * @return boolean + */ + public static function test( $args = array() ) { + if ( ! extension_loaded('gd') || ! function_exists('gd_info') ) + return false; + + // On some setups GD library does not provide imagerotate() - Ticket #11536 + if ( isset( $args['methods'] ) && + in_array( 'rotate', $args['methods'] ) && + ! function_exists('imagerotate') ){ + + return false; + } + + return true; + } + + /** + * Checks to see if editor supports the mime-type specified. + * + * @since 3.5.0 + * @access public + * + * @param string $mime_type + * @return boolean + */ + public static function supports_mime_type( $mime_type ) { + $image_types = imagetypes(); + switch( $mime_type ) { + case 'image/jpeg': + return ($image_types & IMG_JPG) != 0; + case 'image/png': + return ($image_types & IMG_PNG) != 0; + case 'image/gif': + return ($image_types & IMG_GIF) != 0; + } + + return false; + } + + /** + * Loads image from $this->file into new GD Resource. + * + * @since 3.5.0 + * @access protected + * + * @return boolean|WP_Error True if loaded successfully; WP_Error on failure. + */ + public function load() { + if ( $this->image ) + return true; + + if ( ! is_file( $this->file ) && ! preg_match( '|^https?://|', $this->file ) ) + return new WP_Error( 'error_loading_image', __('File doesn’t exist?'), $this->file ); + + /** + * Filter the memory limit allocated for image manipulation. + * + * @since 3.5.0 + * + * @param int|string $limit Maximum memory limit to allocate for images. Default WP_MAX_MEMORY_LIMIT. + * Accepts an integer (bytes), or a shorthand string notation, such as '256M'. + */ + // Set artificially high because GD uses uncompressed images in memory + @ini_set( 'memory_limit', apply_filters( 'image_memory_limit', WP_MAX_MEMORY_LIMIT ) ); + + $this->image = @imagecreatefromstring( file_get_contents( $this->file ) ); + + if ( ! is_resource( $this->image ) ) + return new WP_Error( 'invalid_image', __('File is not an image.'), $this->file ); + + $size = @getimagesize( $this->file ); + if ( ! $size ) + return new WP_Error( 'invalid_image', __('Could not read image size.'), $this->file ); + + if ( function_exists( 'imagealphablending' ) && function_exists( 'imagesavealpha' ) ) { + imagealphablending( $this->image, false ); + imagesavealpha( $this->image, true ); + } + + $this->update_size( $size[0], $size[1] ); + $this->mime_type = $size['mime']; + + return $this->set_quality(); + } + + /** + * Sets or updates current image size. + * + * @since 3.5.0 + * @access protected + * + * @param int $width + * @param int $height + */ + protected function update_size( $width = false, $height = false ) { + if ( ! $width ) + $width = imagesx( $this->image ); + + if ( ! $height ) + $height = imagesy( $this->image ); + + return parent::update_size( $width, $height ); + } + + /** + * Resizes current image. + * Wraps _resize, since _resize returns a GD Resource. + * + * At minimum, either a height or width must be provided. + * If one of the two is set to null, the resize will + * maintain aspect ratio according to the provided dimension. + * + * @since 3.5.0 + * @access public + * + * @param int|null $max_w Image width. + * @param int|null $max_h Image height. + * @param boolean $crop + * @return boolean|WP_Error + */ + public function resize( $max_w, $max_h, $crop = false ) { + if ( ( $this->size['width'] == $max_w ) && ( $this->size['height'] == $max_h ) ) + return true; + + $resized = $this->_resize( $max_w, $max_h, $crop ); + + if ( is_resource( $resized ) ) { + imagedestroy( $this->image ); + $this->image = $resized; + return true; + + } elseif ( is_wp_error( $resized ) ) + return $resized; + + return new WP_Error( 'image_resize_error', __('Image resize failed.'), $this->file ); + } + + protected function _resize( $max_w, $max_h, $crop = false ) { + $dims = image_resize_dimensions( $this->size['width'], $this->size['height'], $max_w, $max_h, $crop ); + if ( ! $dims ) { + return new WP_Error( 'error_getting_dimensions', __('Could not calculate resized image dimensions'), $this->file ); + } + list( $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h ) = $dims; + + $resized = wp_imagecreatetruecolor( $dst_w, $dst_h ); + imagecopyresampled( $resized, $this->image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h ); + + if ( is_resource( $resized ) ) { + $this->update_size( $dst_w, $dst_h ); + return $resized; + } + + return new WP_Error( 'image_resize_error', __('Image resize failed.'), $this->file ); + } + + /** + * Resize multiple images from a single source. + * + * @since 3.5.0 + * @access public + * + * @param array $sizes { + * An array of image size arrays. Default sizes are 'small', 'medium', 'large'. + * + * Either a height or width must be provided. + * If one of the two is set to null, the resize will + * maintain aspect ratio according to the provided dimension. + * + * @type array $size { + * @type int ['width'] Optional. Image width. + * @type int ['height'] Optional. Image height. + * @type bool ['crop'] Optional. Whether to crop the image. Default false. + * } + * } + * @return array An array of resized images' metadata by size. + */ + public function multi_resize( $sizes ) { + $metadata = array(); + $orig_size = $this->size; + + foreach ( $sizes as $size => $size_data ) { + if ( ! isset( $size_data['width'] ) && ! isset( $size_data['height'] ) ) { + continue; + } + + if ( ! isset( $size_data['width'] ) ) { + $size_data['width'] = null; + } + if ( ! isset( $size_data['height'] ) ) { + $size_data['height'] = null; + } + + if ( ! isset( $size_data['crop'] ) ) { + $size_data['crop'] = false; + } + + $image = $this->_resize( $size_data['width'], $size_data['height'], $size_data['crop'] ); + + if( ! is_wp_error( $image ) ) { + $resized = $this->_save( $image ); + + imagedestroy( $image ); + + if ( ! is_wp_error( $resized ) && $resized ) { + unset( $resized['path'] ); + $metadata[$size] = $resized; + } + } + + $this->size = $orig_size; + } + + return $metadata; + } + + /** + * Crops Image. + * + * @since 3.5.0 + * @access public + * + * @param int $src_x The start x position to crop from. + * @param int $src_y The start y position to crop from. + * @param int $src_w The width to crop. + * @param int $src_h The height to crop. + * @param int $dst_w Optional. The destination width. + * @param int $dst_h Optional. The destination height. + * @param boolean $src_abs Optional. If the source crop points are absolute. + * @return boolean|WP_Error + */ + public function crop( $src_x, $src_y, $src_w, $src_h, $dst_w = null, $dst_h = null, $src_abs = false ) { + // If destination width/height isn't specified, use same as + // width/height from source. + if ( ! $dst_w ) + $dst_w = $src_w; + if ( ! $dst_h ) + $dst_h = $src_h; + + $dst = wp_imagecreatetruecolor( $dst_w, $dst_h ); + + if ( $src_abs ) { + $src_w -= $src_x; + $src_h -= $src_y; + } + + if ( function_exists( 'imageantialias' ) ) + imageantialias( $dst, true ); + + imagecopyresampled( $dst, $this->image, 0, 0, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h ); + + if ( is_resource( $dst ) ) { + imagedestroy( $this->image ); + $this->image = $dst; + $this->update_size(); + return true; + } + + return new WP_Error( 'image_crop_error', __('Image crop failed.'), $this->file ); + } + + /** + * Rotates current image counter-clockwise by $angle. + * Ported from image-edit.php + * + * @since 3.5.0 + * @access public + * + * @param float $angle + * @return boolean|WP_Error + */ + public function rotate( $angle ) { + if ( function_exists('imagerotate') ) { + $rotated = imagerotate( $this->image, $angle, 0 ); + + if ( is_resource( $rotated ) ) { + imagedestroy( $this->image ); + $this->image = $rotated; + $this->update_size(); + return true; + } + } + return new WP_Error( 'image_rotate_error', __('Image rotate failed.'), $this->file ); + } + + /** + * Flips current image. + * + * @since 3.5.0 + * @access public + * + * @param boolean $horz Flip along Horizontal Axis + * @param boolean $vert Flip along Vertical Axis + * @returns boolean|WP_Error + */ + public function flip( $horz, $vert ) { + $w = $this->size['width']; + $h = $this->size['height']; + $dst = wp_imagecreatetruecolor( $w, $h ); + + if ( is_resource( $dst ) ) { + $sx = $vert ? ($w - 1) : 0; + $sy = $horz ? ($h - 1) : 0; + $sw = $vert ? -$w : $w; + $sh = $horz ? -$h : $h; + + if ( imagecopyresampled( $dst, $this->image, 0, 0, $sx, $sy, $w, $h, $sw, $sh ) ) { + imagedestroy( $this->image ); + $this->image = $dst; + return true; + } + } + return new WP_Error( 'image_flip_error', __('Image flip failed.'), $this->file ); + } + + /** + * Saves current in-memory image to file. + * + * @since 3.5.0 + * @access public + * + * @param string|null $filename + * @param string|null $mime_type + * @return array|WP_Error {'path'=>string, 'file'=>string, 'width'=>int, 'height'=>int, 'mime-type'=>string} + */ + public function save( $filename = null, $mime_type = null ) { + $saved = $this->_save( $this->image, $filename, $mime_type ); + + if ( ! is_wp_error( $saved ) ) { + $this->file = $saved['path']; + $this->mime_type = $saved['mime-type']; + } + + return $saved; + } + + /** + * @param resource $image + * @param string|null $filename + * @param string|null $mime_type + * @return WP_Error|array + */ + protected function _save( $image, $filename = null, $mime_type = null ) { + list( $filename, $extension, $mime_type ) = $this->get_output_format( $filename, $mime_type ); + + if ( ! $filename ) + $filename = $this->generate_filename( null, null, $extension ); + + if ( 'image/gif' == $mime_type ) { + if ( ! $this->make_image( $filename, 'imagegif', array( $image, $filename ) ) ) + return new WP_Error( 'image_save_error', __('Image Editor Save Failed') ); + } + elseif ( 'image/png' == $mime_type ) { + // convert from full colors to index colors, like original PNG. + if ( function_exists('imageistruecolor') && ! imageistruecolor( $image ) ) + imagetruecolortopalette( $image, false, imagecolorstotal( $image ) ); + + if ( ! $this->make_image( $filename, 'imagepng', array( $image, $filename ) ) ) + return new WP_Error( 'image_save_error', __('Image Editor Save Failed') ); + } + elseif ( 'image/jpeg' == $mime_type ) { + if ( ! $this->make_image( $filename, 'imagejpeg', array( $image, $filename, $this->get_quality() ) ) ) + return new WP_Error( 'image_save_error', __('Image Editor Save Failed') ); + } + else { + return new WP_Error( 'image_save_error', __('Image Editor Save Failed') ); + } + + // Set correct file permissions + $stat = stat( dirname( $filename ) ); + $perms = $stat['mode'] & 0000666; //same permissions as parent folder, strip off the executable bits + @ chmod( $filename, $perms ); + + /** + * Filter the name of the saved image file. + * + * @since 2.6.0 + * + * @param string $filename Name of the file. + */ + return array( + 'path' => $filename, + 'file' => wp_basename( apply_filters( 'image_make_intermediate_size', $filename ) ), + 'width' => $this->size['width'], + 'height' => $this->size['height'], + 'mime-type' => $mime_type, + ); + } + + /** + * Returns stream of current image. + * + * @since 3.5.0 + * @access public + * + * @param string $mime_type + */ + public function stream( $mime_type = null ) { + list( $filename, $extension, $mime_type ) = $this->get_output_format( null, $mime_type ); + + switch ( $mime_type ) { + case 'image/png': + header( 'Content-Type: image/png' ); + return imagepng( $this->image ); + case 'image/gif': + header( 'Content-Type: image/gif' ); + return imagegif( $this->image ); + default: + header( 'Content-Type: image/jpeg' ); + return imagejpeg( $this->image, null, $this->get_quality() ); + } + } + + /** + * Either calls editor's save function or handles file as a stream. + * + * @since 3.5.0 + * @access protected + * + * @param string|stream $filename + * @param callable $function + * @param array $arguments + * @return boolean + */ + protected function make_image( $filename, $function, $arguments ) { + if ( wp_is_stream( $filename ) ) + $arguments[1] = null; + + return parent::make_image( $filename, $function, $arguments ); + } +} diff --git a/wp-includes/class-wp-image-editor-imagick.php b/wp-includes/class-wp-image-editor-imagick.php new file mode 100644 index 0000000..843b85c --- /dev/null +++ b/wp-includes/class-wp-image-editor-imagick.php @@ -0,0 +1,514 @@ +<?php +/** + * WordPress Imagick Image Editor + * + * @package WordPress + * @subpackage Image_Editor + */ + +/** + * WordPress Image Editor Class for Image Manipulation through Imagick PHP Module + * + * @since 3.5.0 + * @package WordPress + * @subpackage Image_Editor + * @uses WP_Image_Editor Extends class + */ +class WP_Image_Editor_Imagick extends WP_Image_Editor { + /** + * @var Imagick + */ + protected $image; // Imagick Object + + public function __destruct() { + if ( $this->image instanceof Imagick ) { + // we don't need the original in memory anymore + $this->image->clear(); + $this->image->destroy(); + } + } + + /** + * Checks to see if current environment supports Imagick. + * + * We require Imagick 2.2.0 or greater, based on whether the queryFormats() + * method can be called statically. + * + * @since 3.5.0 + * @access public + * + * @return boolean + */ + public static function test( $args = array() ) { + + // First, test Imagick's extension and classes. + if ( ! extension_loaded( 'imagick' ) || ! class_exists( 'Imagick' ) || ! class_exists( 'ImagickPixel' ) ) + return false; + + if ( version_compare( phpversion( 'imagick' ), '2.2.0', '<' ) ) + return false; + + $required_methods = array( + 'clear', + 'destroy', + 'valid', + 'getimage', + 'writeimage', + 'getimageblob', + 'getimagegeometry', + 'getimageformat', + 'setimageformat', + 'setimagecompression', + 'setimagecompressionquality', + 'setimagepage', + 'scaleimage', + 'cropimage', + 'rotateimage', + 'flipimage', + 'flopimage', + ); + + // Now, test for deep requirements within Imagick. + if ( ! defined( 'imagick::COMPRESSION_JPEG' ) ) + return false; + + if ( array_diff( $required_methods, get_class_methods( 'Imagick' ) ) ) + return false; + + return true; + } + + /** + * Checks to see if editor supports the mime-type specified. + * + * @since 3.5.0 + * @access public + * + * @param string $mime_type + * @return boolean + */ + public static function supports_mime_type( $mime_type ) { + $imagick_extension = strtoupper( self::get_extension( $mime_type ) ); + + if ( ! $imagick_extension ) + return false; + + // setIteratorIndex is optional unless mime is an animated format. + // Here, we just say no if you are missing it and aren't loading a jpeg. + if ( ! method_exists( 'Imagick', 'setIteratorIndex' ) && $mime_type != 'image/jpeg' ) + return false; + + try { + return ( (bool) @Imagick::queryFormats( $imagick_extension ) ); + } + catch ( Exception $e ) { + return false; + } + } + + /** + * Loads image from $this->file into new Imagick Object. + * + * @since 3.5.0 + * @access protected + * + * @return boolean|WP_Error True if loaded; WP_Error on failure. + */ + public function load() { + if ( $this->image instanceof Imagick ) + return true; + + if ( ! is_file( $this->file ) && ! preg_match( '|^https?://|', $this->file ) ) + return new WP_Error( 'error_loading_image', __('File doesn’t exist?'), $this->file ); + + /** This filter is documented in wp-includes/class-wp-image-editor-imagick.php */ + // Even though Imagick uses less PHP memory than GD, set higher limit for users that have low PHP.ini limits + @ini_set( 'memory_limit', apply_filters( 'image_memory_limit', WP_MAX_MEMORY_LIMIT ) ); + + try { + $this->image = new Imagick( $this->file ); + + if( ! $this->image->valid() ) + return new WP_Error( 'invalid_image', __('File is not an image.'), $this->file); + + // Select the first frame to handle animated images properly + if ( is_callable( array( $this->image, 'setIteratorIndex' ) ) ) + $this->image->setIteratorIndex(0); + + $this->mime_type = $this->get_mime_type( $this->image->getImageFormat() ); + } + catch ( Exception $e ) { + return new WP_Error( 'invalid_image', $e->getMessage(), $this->file ); + } + + $updated_size = $this->update_size(); + if ( is_wp_error( $updated_size ) ) { + return $updated_size; + } + + return $this->set_quality(); + } + + /** + * Sets Image Compression quality on a 1-100% scale. + * + * @since 3.5.0 + * @access public + * + * @param int $quality Compression Quality. Range: [1,100] + * @return boolean|WP_Error True if set successfully; WP_Error on failure. + */ + public function set_quality( $quality = null ) { + $quality_result = parent::set_quality( $quality ); + if ( is_wp_error( $quality_result ) ) { + return $quality_result; + } else { + $quality = $this->get_quality(); + } + + try { + if ( 'image/jpeg' == $this->mime_type ) { + $this->image->setImageCompressionQuality( $quality ); + $this->image->setImageCompression( imagick::COMPRESSION_JPEG ); + } + else { + $this->image->setImageCompressionQuality( $quality ); + } + } + catch ( Exception $e ) { + return new WP_Error( 'image_quality_error', $e->getMessage() ); + } + + return true; + } + + /** + * Sets or updates current image size. + * + * @since 3.5.0 + * @access protected + * + * @param int $width + * @param int $height + */ + protected function update_size( $width = null, $height = null ) { + $size = null; + if ( !$width || !$height ) { + try { + $size = $this->image->getImageGeometry(); + } + catch ( Exception $e ) { + return new WP_Error( 'invalid_image', __('Could not read image size'), $this->file ); + } + } + + if ( ! $width ) + $width = $size['width']; + + if ( ! $height ) + $height = $size['height']; + + return parent::update_size( $width, $height ); + } + + /** + * Resizes current image. + * + * At minimum, either a height or width must be provided. + * If one of the two is set to null, the resize will + * maintain aspect ratio according to the provided dimension. + * + * @since 3.5.0 + * @access public + * + * @param int|null $max_w Image width. + * @param int|null $max_h Image height. + * @param boolean $crop + * @return boolean|WP_Error + */ + public function resize( $max_w, $max_h, $crop = false ) { + if ( ( $this->size['width'] == $max_w ) && ( $this->size['height'] == $max_h ) ) + return true; + + $dims = image_resize_dimensions( $this->size['width'], $this->size['height'], $max_w, $max_h, $crop ); + if ( ! $dims ) + return new WP_Error( 'error_getting_dimensions', __('Could not calculate resized image dimensions') ); + list( $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h ) = $dims; + + if ( $crop ) { + return $this->crop( $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h ); + } + + try { + /** + * @TODO: Thumbnail is more efficient, given a newer version of Imagemagick. + * $this->image->thumbnailImage( $dst_w, $dst_h ); + */ + $this->image->scaleImage( $dst_w, $dst_h ); + } + catch ( Exception $e ) { + return new WP_Error( 'image_resize_error', $e->getMessage() ); + } + + return $this->update_size( $dst_w, $dst_h ); + } + + /** + * Resize multiple images from a single source. + * + * @since 3.5.0 + * @access public + * + * @param array $sizes { + * An array of image size arrays. Default sizes are 'small', 'medium', 'large'. + * + * Either a height or width must be provided. + * If one of the two is set to null, the resize will + * maintain aspect ratio according to the provided dimension. + * + * @type array $size { + * @type int ['width'] Optional. Image width. + * @type int ['height'] Optional. Image height. + * @type bool $crop Optional. Whether to crop the image. Default false. + * } + * } + * @return array An array of resized images' metadata by size. + */ + public function multi_resize( $sizes ) { + $metadata = array(); + $orig_size = $this->size; + $orig_image = $this->image->getImage(); + + foreach ( $sizes as $size => $size_data ) { + if ( ! $this->image ) + $this->image = $orig_image->getImage(); + + if ( ! isset( $size_data['width'] ) && ! isset( $size_data['height'] ) ) { + continue; + } + + if ( ! isset( $size_data['width'] ) ) { + $size_data['width'] = null; + } + if ( ! isset( $size_data['height'] ) ) { + $size_data['height'] = null; + } + + if ( ! isset( $size_data['crop'] ) ) { + $size_data['crop'] = false; + } + + $resize_result = $this->resize( $size_data['width'], $size_data['height'], $size_data['crop'] ); + + if( ! is_wp_error( $resize_result ) ) { + $resized = $this->_save( $this->image ); + + $this->image->clear(); + $this->image->destroy(); + $this->image = null; + + if ( ! is_wp_error( $resized ) && $resized ) { + unset( $resized['path'] ); + $metadata[$size] = $resized; + } + } + + $this->size = $orig_size; + } + + $this->image = $orig_image; + + return $metadata; + } + + /** + * Crops Image. + * + * @since 3.5.0 + * @access public + * + * @param string|int $src The source file or Attachment ID. + * @param int $src_x The start x position to crop from. + * @param int $src_y The start y position to crop from. + * @param int $src_w The width to crop. + * @param int $src_h The height to crop. + * @param int $dst_w Optional. The destination width. + * @param int $dst_h Optional. The destination height. + * @param boolean $src_abs Optional. If the source crop points are absolute. + * @return boolean|WP_Error + */ + public function crop( $src_x, $src_y, $src_w, $src_h, $dst_w = null, $dst_h = null, $src_abs = false ) { + if ( $src_abs ) { + $src_w -= $src_x; + $src_h -= $src_y; + } + + try { + $this->image->cropImage( $src_w, $src_h, $src_x, $src_y ); + $this->image->setImagePage( $src_w, $src_h, 0, 0); + + if ( $dst_w || $dst_h ) { + // If destination width/height isn't specified, use same as + // width/height from source. + if ( ! $dst_w ) + $dst_w = $src_w; + if ( ! $dst_h ) + $dst_h = $src_h; + + $this->image->scaleImage( $dst_w, $dst_h ); + return $this->update_size(); + } + } + catch ( Exception $e ) { + return new WP_Error( 'image_crop_error', $e->getMessage() ); + } + return $this->update_size(); + } + + /** + * Rotates current image counter-clockwise by $angle. + * + * @since 3.5.0 + * @access public + * + * @param float $angle + * @return boolean|WP_Error + */ + public function rotate( $angle ) { + /** + * $angle is 360-$angle because Imagick rotates clockwise + * (GD rotates counter-clockwise) + */ + try { + $this->image->rotateImage( new ImagickPixel('none'), 360-$angle ); + + // Since this changes the dimensions of the image, update the size. + $result = $this->update_size(); + if ( is_wp_error( $result ) ) + return $result; + + $this->image->setImagePage( $this->size['width'], $this->size['height'], 0, 0 ); + } + catch ( Exception $e ) { + return new WP_Error( 'image_rotate_error', $e->getMessage() ); + } + return true; + } + + /** + * Flips current image. + * + * @since 3.5.0 + * @access public + * + * @param boolean $horz Flip along Horizontal Axis + * @param boolean $vert Flip along Vertical Axis + * @returns boolean|WP_Error + */ + public function flip( $horz, $vert ) { + try { + if ( $horz ) + $this->image->flipImage(); + + if ( $vert ) + $this->image->flopImage(); + } + catch ( Exception $e ) { + return new WP_Error( 'image_flip_error', $e->getMessage() ); + } + return true; + } + + /** + * Saves current image to file. + * + * @since 3.5.0 + * @access public + * + * @param string $destfilename + * @param string $mime_type + * @return array|WP_Error {'path'=>string, 'file'=>string, 'width'=>int, 'height'=>int, 'mime-type'=>string} + */ + public function save( $destfilename = null, $mime_type = null ) { + $saved = $this->_save( $this->image, $destfilename, $mime_type ); + + if ( ! is_wp_error( $saved ) ) { + $this->file = $saved['path']; + $this->mime_type = $saved['mime-type']; + + try { + $this->image->setImageFormat( strtoupper( $this->get_extension( $this->mime_type ) ) ); + } + catch ( Exception $e ) { + return new WP_Error( 'image_save_error', $e->getMessage(), $this->file ); + } + } + + return $saved; + } + + protected function _save( $image, $filename = null, $mime_type = null ) { + list( $filename, $extension, $mime_type ) = $this->get_output_format( $filename, $mime_type ); + + if ( ! $filename ) + $filename = $this->generate_filename( null, null, $extension ); + + try { + // Store initial Format + $orig_format = $this->image->getImageFormat(); + + $this->image->setImageFormat( strtoupper( $this->get_extension( $mime_type ) ) ); + $this->make_image( $filename, array( $image, 'writeImage' ), array( $filename ) ); + + // Reset original Format + $this->image->setImageFormat( $orig_format ); + } + catch ( Exception $e ) { + return new WP_Error( 'image_save_error', $e->getMessage(), $filename ); + } + + // Set correct file permissions + $stat = stat( dirname( $filename ) ); + $perms = $stat['mode'] & 0000666; //same permissions as parent folder, strip off the executable bits + @ chmod( $filename, $perms ); + + /** This filter is documented in wp-includes/class-wp-image-editor-gd.php */ + return array( + 'path' => $filename, + 'file' => wp_basename( apply_filters( 'image_make_intermediate_size', $filename ) ), + 'width' => $this->size['width'], + 'height' => $this->size['height'], + 'mime-type' => $mime_type, + ); + } + + /** + * Streams current image to browser. + * + * @since 3.5.0 + * @access public + * + * @param string $mime_type + * @return boolean|WP_Error + */ + public function stream( $mime_type = null ) { + list( $filename, $extension, $mime_type ) = $this->get_output_format( null, $mime_type ); + + try { + // Temporarily change format for stream + $this->image->setImageFormat( strtoupper( $extension ) ); + + // Output stream of image content + header( "Content-Type: $mime_type" ); + print $this->image->getImageBlob(); + + // Reset Image to original Format + $this->image->setImageFormat( $this->get_extension( $this->mime_type ) ); + } + catch ( Exception $e ) { + return new WP_Error( 'image_stream_error', $e->getMessage() ); + } + + return true; + } +} diff --git a/wp-includes/class-wp-image-editor.php b/wp-includes/class-wp-image-editor.php new file mode 100644 index 0000000..fcca557 --- /dev/null +++ b/wp-includes/class-wp-image-editor.php @@ -0,0 +1,485 @@ +<?php +/** + * Base WordPress Image Editor + * + * @package WordPress + * @subpackage Image_Editor + */ + +/** + * Base image editor class from which implementations extend + * + * @since 3.5.0 + */ +abstract class WP_Image_Editor { + protected $file = null; + protected $size = null; + protected $mime_type = null; + protected $default_mime_type = 'image/jpeg'; + protected $quality = false; + protected $default_quality = 90; + + /** + * Each instance handles a single file. + */ + public function __construct( $file ) { + $this->file = $file; + } + + /** + * Checks to see if current environment supports the editor chosen. + * Must be overridden in a sub-class. + * + * @since 3.5.0 + * @access public + * @abstract + * + * @param array $args + * @return boolean + */ + public static function test( $args = array() ) { + return false; + } + + /** + * Checks to see if editor supports the mime-type specified. + * Must be overridden in a sub-class. + * + * @since 3.5.0 + * @access public + * @abstract + * + * @param string $mime_type + * @return boolean + */ + public static function supports_mime_type( $mime_type ) { + return false; + } + + /** + * Loads image from $this->file into editor. + * + * @since 3.5.0 + * @access protected + * @abstract + * + * @return boolean|WP_Error True if loaded; WP_Error on failure. + */ + abstract public function load(); + + /** + * Saves current image to file. + * + * @since 3.5.0 + * @access public + * @abstract + * + * @param string $destfilename + * @param string $mime_type + * @return array|WP_Error {'path'=>string, 'file'=>string, 'width'=>int, 'height'=>int, 'mime-type'=>string} + */ + abstract public function save( $destfilename = null, $mime_type = null ); + + /** + * Resizes current image. + * + * At minimum, either a height or width must be provided. + * If one of the two is set to null, the resize will + * maintain aspect ratio according to the provided dimension. + * + * @since 3.5.0 + * @access public + * @abstract + * + * @param int|null $max_w Image width. + * @param int|null $max_h Image height. + * @param boolean $crop + * @return boolean|WP_Error + */ + abstract public function resize( $max_w, $max_h, $crop = false ); + + /** + * Resize multiple images from a single source. + * + * @since 3.5.0 + * @access public + * @abstract + * + * @param array $sizes { + * An array of image size arrays. Default sizes are 'small', 'medium', 'large'. + * + * @type array $size { + * @type int $width Image width. + * @type int $height Image height. + * @type bool $crop Optional. Whether to crop the image. Default false. + * } + * } + * @return array An array of resized images metadata by size. + */ + abstract public function multi_resize( $sizes ); + + /** + * Crops Image. + * + * @since 3.5.0 + * @access public + * @abstract + * + * @param string|int $src The source file or Attachment ID. + * @param int $src_x The start x position to crop from. + * @param int $src_y The start y position to crop from. + * @param int $src_w The width to crop. + * @param int $src_h The height to crop. + * @param int $dst_w Optional. The destination width. + * @param int $dst_h Optional. The destination height. + * @param boolean $src_abs Optional. If the source crop points are absolute. + * @return boolean|WP_Error + */ + abstract public function crop( $src_x, $src_y, $src_w, $src_h, $dst_w = null, $dst_h = null, $src_abs = false ); + + /** + * Rotates current image counter-clockwise by $angle. + * + * @since 3.5.0 + * @access public + * @abstract + * + * @param float $angle + * @return boolean|WP_Error + */ + abstract public function rotate( $angle ); + + /** + * Flips current image. + * + * @since 3.5.0 + * @access public + * @abstract + * + * @param boolean $horz Flip along Horizontal Axis + * @param boolean $vert Flip along Vertical Axis + * @return boolean|WP_Error + */ + abstract public function flip( $horz, $vert ); + + /** + * Streams current image to browser. + * + * @since 3.5.0 + * @access public + * @abstract + * + * @param string $mime_type + * @return boolean|WP_Error + */ + abstract public function stream( $mime_type = null ); + + /** + * Gets dimensions of image. + * + * @since 3.5.0 + * @access public + * + * @return array {'width'=>int, 'height'=>int} + */ + public function get_size() { + return $this->size; + } + + /** + * Sets current image size. + * + * @since 3.5.0 + * @access protected + * + * @param int $width + * @param int $height + */ + protected function update_size( $width = null, $height = null ) { + $this->size = array( + 'width' => (int) $width, + 'height' => (int) $height + ); + return true; + } + + /** + * Gets the Image Compression quality on a 1-100% scale. + * + * @since 4.0.0 + * @access public + * + * @return int $quality Compression Quality. Range: [1,100] + */ + public function get_quality() { + if ( ! $this->quality ) { + $this->set_quality(); + } + + return $this->quality; + } + + /** + * Sets Image Compression quality on a 1-100% scale. + * + * @since 3.5.0 + * @access public + * + * @param int $quality Compression Quality. Range: [1,100] + * @return boolean|WP_Error True if set successfully; WP_Error on failure. + */ + public function set_quality( $quality = null ) { + if ( null === $quality ) { + /** + * Filter the default image compression quality setting. + * + * Applies only during initial editor instantiation, or when set_quality() is run + * manually without the `$quality` argument. + * + * set_quality() has priority over the filter. + * + * @since 3.5.0 + * + * @param int $quality Quality level between 1 (low) and 100 (high). + * @param string $mime_type Image mime type. + */ + $quality = apply_filters( 'wp_editor_set_quality', $this->default_quality, $this->mime_type ); + + if ( 'image/jpeg' == $this->mime_type ) { + /** + * Filter the JPEG compression quality for backward-compatibility. + * + * Applies only during initial editor instantiation, or when set_quality() is run + * manually without the `$quality` argument. + * + * set_quality() has priority over the filter. + * + * The filter is evaluated under two contexts: 'image_resize', and 'edit_image', + * (when a JPEG image is saved to file). + * + * @since 2.5.0 + * + * @param int $quality Quality level between 0 (low) and 100 (high) of the JPEG. + * @param string $context Context of the filter. + */ + $quality = apply_filters( 'jpeg_quality', $quality, 'image_resize' ); + } + + if ( $quality < 0 || $quality > 100 ) { + $quality = $this->default_quality; + } + } + + // Allow 0, but squash to 1 due to identical images in GD, and for backwards compatibility. + if ( 0 === $quality ) { + $quality = 1; + } + + if ( ( $quality >= 1 ) && ( $quality <= 100 ) ) { + $this->quality = $quality; + return true; + } else { + return new WP_Error( 'invalid_image_quality', __('Attempted to set image quality outside of the range [1,100].') ); + } + } + + /** + * Returns preferred mime-type and extension based on provided + * file's extension and mime, or current file's extension and mime. + * + * Will default to $this->default_mime_type if requested is not supported. + * + * Provides corrected filename only if filename is provided. + * + * @since 3.5.0 + * @access protected + * + * @param string $filename + * @param string $mime_type + * @return array { filename|null, extension, mime-type } + */ + protected function get_output_format( $filename = null, $mime_type = null ) { + $new_ext = null; + + // By default, assume specified type takes priority + if ( $mime_type ) { + $new_ext = $this->get_extension( $mime_type ); + } + + if ( $filename ) { + $file_ext = strtolower( pathinfo( $filename, PATHINFO_EXTENSION ) ); + $file_mime = $this->get_mime_type( $file_ext ); + } + else { + // If no file specified, grab editor's current extension and mime-type. + $file_ext = strtolower( pathinfo( $this->file, PATHINFO_EXTENSION ) ); + $file_mime = $this->mime_type; + } + + // Check to see if specified mime-type is the same as type implied by + // file extension. If so, prefer extension from file. + if ( ! $mime_type || ( $file_mime == $mime_type ) ) { + $mime_type = $file_mime; + $new_ext = $file_ext; + } + + // Double-check that the mime-type selected is supported by the editor. + // If not, choose a default instead. + if ( ! $this->supports_mime_type( $mime_type ) ) { + /** + * Filter default mime type prior to getting the file extension. + * + * @see wp_get_mime_types() + * + * @since 3.5.0 + * + * @param string $mime_type Mime type string. + */ + $mime_type = apply_filters( 'image_editor_default_mime_type', $this->default_mime_type ); + $new_ext = $this->get_extension( $mime_type ); + } + + if ( $filename ) { + $ext = ''; + $info = pathinfo( $filename ); + $dir = $info['dirname']; + + if( isset( $info['extension'] ) ) + $ext = $info['extension']; + + $filename = trailingslashit( $dir ) . wp_basename( $filename, ".$ext" ) . ".{$new_ext}"; + } + + return array( $filename, $new_ext, $mime_type ); + } + + /** + * Builds an output filename based on current file, and adding proper suffix + * + * @since 3.5.0 + * @access public + * + * @param string $suffix + * @param string $dest_path + * @param string $extension + * @return string filename + */ + public function generate_filename( $suffix = null, $dest_path = null, $extension = null ) { + // $suffix will be appended to the destination filename, just before the extension + if ( ! $suffix ) + $suffix = $this->get_suffix(); + + $info = pathinfo( $this->file ); + $dir = $info['dirname']; + $ext = $info['extension']; + + $name = wp_basename( $this->file, ".$ext" ); + $new_ext = strtolower( $extension ? $extension : $ext ); + + if ( ! is_null( $dest_path ) && $_dest_path = realpath( $dest_path ) ) + $dir = $_dest_path; + + return trailingslashit( $dir ) . "{$name}-{$suffix}.{$new_ext}"; + } + + /** + * Builds and returns proper suffix for file based on height and width. + * + * @since 3.5.0 + * @access public + * + * @return string suffix + */ + public function get_suffix() { + if ( ! $this->get_size() ) + return false; + + return "{$this->size['width']}x{$this->size['height']}"; + } + + /** + * Either calls editor's save function or handles file as a stream. + * + * @since 3.5.0 + * @access protected + * + * @param string|stream $filename + * @param callable $function + * @param array $arguments + * @return boolean + */ + protected function make_image( $filename, $function, $arguments ) { + if ( $stream = wp_is_stream( $filename ) ) { + ob_start(); + } else { + // The directory containing the original file may no longer exist when using a replication plugin. + wp_mkdir_p( dirname( $filename ) ); + } + + $result = call_user_func_array( $function, $arguments ); + + if ( $result && $stream ) { + $contents = ob_get_contents(); + + $fp = fopen( $filename, 'w' ); + + if ( ! $fp ) + return false; + + fwrite( $fp, $contents ); + fclose( $fp ); + } + + if ( $stream ) { + ob_end_clean(); + } + + return $result; + } + + /** + * Returns first matched mime-type from extension, + * as mapped from wp_get_mime_types() + * + * @since 3.5.0 + * @access protected + * + * @param string $extension + * @return string|boolean + */ + protected static function get_mime_type( $extension = null ) { + if ( ! $extension ) + return false; + + $mime_types = wp_get_mime_types(); + $extensions = array_keys( $mime_types ); + + foreach( $extensions as $_extension ) { + if ( preg_match( "/{$extension}/i", $_extension ) ) { + return $mime_types[$_extension]; + } + } + + return false; + } + + /** + * Returns first matched extension from Mime-type, + * as mapped from wp_get_mime_types() + * + * @since 3.5.0 + * @access protected + * + * @param string $mime_type + * @return string|boolean + */ + protected static function get_extension( $mime_type = null ) { + $extensions = explode( '|', array_search( $mime_type, wp_get_mime_types() ) ); + + if ( empty( $extensions[0] ) ) + return false; + + return $extensions[0]; + } +} + diff --git a/wp-includes/class-wp-theme.php b/wp-includes/class-wp-theme.php new file mode 100644 index 0000000..a02d5f6 --- /dev/null +++ b/wp-includes/class-wp-theme.php @@ -0,0 +1,1234 @@ +<?php +/** + * WP_Theme Class + * + * @package WordPress + * @subpackage Theme + */ + +final class WP_Theme implements ArrayAccess { + + /** + * Headers for style.css files. + * + * @static + * @access private + * @var array + */ + private static $file_headers = array( + 'Name' => 'Theme Name', + 'ThemeURI' => 'Theme URI', + 'Description' => 'Description', + 'Author' => 'Author', + 'AuthorURI' => 'Author URI', + 'Version' => 'Version', + 'Template' => 'Template', + 'Status' => 'Status', + 'Tags' => 'Tags', + 'TextDomain' => 'Text Domain', + 'DomainPath' => 'Domain Path', + ); + + /** + * Default themes. + * + * @static + * @access private + * @var array + */ + private static $default_themes = array( + 'classic' => 'WordPress Classic', + 'default' => 'WordPress Default', + 'twentyten' => 'Twenty Ten', + 'twentyeleven' => 'Twenty Eleven', + 'twentytwelve' => 'Twenty Twelve', + 'twentythirteen' => 'Twenty Thirteen', + 'twentyfourteen' => 'Twenty Fourteen', + 'twentyfifteen' => 'Twenty Fifteen', + ); + + /** + * Renamed theme tags. + */ + private static $tag_map = array( + 'fixed-width' => 'fixed-layout', + 'flexible-width' => 'fluid-layout', + ); + + /** + * Absolute path to the theme root, usually wp-content/themes + * + * @access private + * @var string + */ + private $theme_root; + + /** + * Header data from the theme's style.css file. + * + * @access private + * @var array + */ + private $headers = array(); + + /** + * Header data from the theme's style.css file after being sanitized. + * + * @access private + * @var array + */ + private $headers_sanitized; + + /** + * Header name from the theme's style.css after being translated. + * + * Cached due to sorting functions running over the translated name. + */ + private $name_translated; + + /** + * Errors encountered when initializing the theme. + * + * @access private + * @var WP_Error + */ + private $errors; + + /** + * The directory name of the theme's files, inside the theme root. + * + * In the case of a child theme, this is directory name of the child theme. + * Otherwise, 'stylesheet' is the same as 'template'. + * + * @access private + * @var string + */ + private $stylesheet; + + /** + * The directory name of the theme's files, inside the theme root. + * + * In the case of a child theme, this is the directory name of the parent theme. + * Otherwise, 'template' is the same as 'stylesheet'. + * + * @access private + * @var string + */ + private $template; + + /** + * A reference to the parent theme, in the case of a child theme. + * + * @access private + * @var WP_Theme + */ + private $parent; + + /** + * URL to the theme root, usually an absolute URL to wp-content/themes + * + * @access private + * var string + */ + private $theme_root_uri; + + /** + * Flag for whether the theme's textdomain is loaded. + * + * @access private + * @var bool + */ + private $textdomain_loaded; + + /** + * Stores an md5 hash of the theme root, to function as the cache key. + * + * @access private + * @var string + */ + private $cache_hash; + + /** + * Flag for whether the themes cache bucket should be persistently cached. + * + * Default is false. Can be set with the wp_cache_themes_persistently filter. + * + * @access private + * @var bool + */ + private static $persistently_cache; + + /** + * Expiration time for the themes cache bucket. + * + * By default the bucket is not cached, so this value is useless. + * + * @access private + * @var bool + */ + private static $cache_expiration = 1800; + + /** + * Constructor for WP_Theme. + * + * @param string $theme_dir Directory of the theme within the theme_root. + * @param string $theme_root Theme root. + * @param WP_Error|null $_child If this theme is a parent theme, the child may be passed for validation purposes. + */ + public function __construct( $theme_dir, $theme_root, $_child = null ) { + global $wp_theme_directories; + + // Initialize caching on first run. + if ( ! isset( self::$persistently_cache ) ) { + /** This action is documented in wp-includes/theme.php */ + self::$persistently_cache = apply_filters( 'wp_cache_themes_persistently', false, 'WP_Theme' ); + if ( self::$persistently_cache ) { + wp_cache_add_global_groups( 'themes' ); + if ( is_int( self::$persistently_cache ) ) + self::$cache_expiration = self::$persistently_cache; + } else { + wp_cache_add_non_persistent_groups( 'themes' ); + } + } + + $this->theme_root = $theme_root; + $this->stylesheet = $theme_dir; + + // Correct a situation where the theme is 'some-directory/some-theme' but 'some-directory' was passed in as part of the theme root instead. + if ( ! in_array( $theme_root, (array) $wp_theme_directories ) && in_array( dirname( $theme_root ), (array) $wp_theme_directories ) ) { + $this->stylesheet = basename( $this->theme_root ) . '/' . $this->stylesheet; + $this->theme_root = dirname( $theme_root ); + } + + $this->cache_hash = md5( $this->theme_root . '/' . $this->stylesheet ); + $theme_file = $this->stylesheet . '/style.css'; + + $cache = $this->cache_get( 'theme' ); + + if ( is_array( $cache ) ) { + foreach ( array( 'errors', 'headers', 'template' ) as $key ) { + if ( isset( $cache[ $key ] ) ) + $this->$key = $cache[ $key ]; + } + if ( $this->errors ) + return; + if ( isset( $cache['theme_root_template'] ) ) + $theme_root_template = $cache['theme_root_template']; + } elseif ( ! file_exists( $this->theme_root . '/' . $theme_file ) ) { + $this->headers['Name'] = $this->stylesheet; + if ( ! file_exists( $this->theme_root . '/' . $this->stylesheet ) ) + $this->errors = new WP_Error( 'theme_not_found', sprintf( __( 'The theme directory "%s" does not exist.' ), $this->stylesheet ) ); + else + $this->errors = new WP_Error( 'theme_no_stylesheet', __( 'Stylesheet is missing.' ) ); + $this->template = $this->stylesheet; + $this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template ) ); + if ( ! file_exists( $this->theme_root ) ) // Don't cache this one. + $this->errors->add( 'theme_root_missing', __( 'ERROR: The themes directory is either empty or doesn’t exist. Please check your installation.' ) ); + return; + } elseif ( ! is_readable( $this->theme_root . '/' . $theme_file ) ) { + $this->headers['Name'] = $this->stylesheet; + $this->errors = new WP_Error( 'theme_stylesheet_not_readable', __( 'Stylesheet is not readable.' ) ); + $this->template = $this->stylesheet; + $this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template ) ); + return; + } else { + $this->headers = get_file_data( $this->theme_root . '/' . $theme_file, self::$file_headers, 'theme' ); + // Default themes always trump their pretenders. + // Properly identify default themes that are inside a directory within wp-content/themes. + if ( $default_theme_slug = array_search( $this->headers['Name'], self::$default_themes ) ) { + if ( basename( $this->stylesheet ) != $default_theme_slug ) + $this->headers['Name'] .= '/' . $this->stylesheet; + } + } + + // (If template is set from cache [and there are no errors], we know it's good.) + if ( ! $this->template && ! ( $this->template = $this->headers['Template'] ) ) { + $this->template = $this->stylesheet; + if ( ! file_exists( $this->theme_root . '/' . $this->stylesheet . '/index.php' ) ) { + $this->errors = new WP_Error( 'theme_no_index', __( 'Template is missing.' ) ); + $this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template ) ); + return; + } + } + + // If we got our data from cache, we can assume that 'template' is pointing to the right place. + if ( ! is_array( $cache ) && $this->template != $this->stylesheet && ! file_exists( $this->theme_root . '/' . $this->template . '/index.php' ) ) { + // If we're in a directory of themes inside /themes, look for the parent nearby. + // wp-content/themes/directory-of-themes/* + $parent_dir = dirname( $this->stylesheet ); + if ( '.' != $parent_dir && file_exists( $this->theme_root . '/' . $parent_dir . '/' . $this->template . '/index.php' ) ) { + $this->template = $parent_dir . '/' . $this->template; + } elseif ( ( $directories = search_theme_directories() ) && isset( $directories[ $this->template ] ) ) { + // Look for the template in the search_theme_directories() results, in case it is in another theme root. + // We don't look into directories of themes, just the theme root. + $theme_root_template = $directories[ $this->template ]['theme_root']; + } else { + // Parent theme is missing. + $this->errors = new WP_Error( 'theme_no_parent', sprintf( __( 'The parent theme is missing. Please install the "%s" parent theme.' ), $this->template ) ); + $this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template ) ); + $this->parent = new WP_Theme( $this->template, $this->theme_root, $this ); + return; + } + } + + // Set the parent, if we're a child theme. + if ( $this->template != $this->stylesheet ) { + // If we are a parent, then there is a problem. Only two generations allowed! Cancel things out. + if ( is_a( $_child, 'WP_Theme' ) && $_child->template == $this->stylesheet ) { + $_child->parent = null; + $_child->errors = new WP_Error( 'theme_parent_invalid', sprintf( __( 'The "%s" theme is not a valid parent theme.' ), $_child->template ) ); + $_child->cache_add( 'theme', array( 'headers' => $_child->headers, 'errors' => $_child->errors, 'stylesheet' => $_child->stylesheet, 'template' => $_child->template ) ); + // The two themes actually reference each other with the Template header. + if ( $_child->stylesheet == $this->template ) { + $this->errors = new WP_Error( 'theme_parent_invalid', sprintf( __( 'The "%s" theme is not a valid parent theme.' ), $this->template ) ); + $this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template ) ); + } + return; + } + // Set the parent. Pass the current instance so we can do the crazy checks above and assess errors. + $this->parent = new WP_Theme( $this->template, isset( $theme_root_template ) ? $theme_root_template : $this->theme_root, $this ); + } + + // We're good. If we didn't retrieve from cache, set it. + if ( ! is_array( $cache ) ) { + $cache = array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template ); + // If the parent theme is in another root, we'll want to cache this. Avoids an entire branch of filesystem calls above. + if ( isset( $theme_root_template ) ) + $cache['theme_root_template'] = $theme_root_template; + $this->cache_add( 'theme', $cache ); + } + } + + /** + * When converting the object to a string, the theme name is returned. + * + * @return string Theme name, ready for display (translated) + */ + public function __toString() { + return (string) $this->display('Name'); + } + + /** + * __isset() magic method for properties formerly returned by current_theme_info() + */ + public function __isset( $offset ) { + static $properties = array( + 'name', 'title', 'version', 'parent_theme', 'template_dir', 'stylesheet_dir', 'template', 'stylesheet', + 'screenshot', 'description', 'author', 'tags', 'theme_root', 'theme_root_uri', + ); + + return in_array( $offset, $properties ); + } + + /** + * __get() magic method for properties formerly returned by current_theme_info() + */ + public function __get( $offset ) { + switch ( $offset ) { + case 'name' : + case 'title' : + return $this->get('Name'); + case 'version' : + return $this->get('Version'); + case 'parent_theme' : + return $this->parent() ? $this->parent()->get('Name') : ''; + case 'template_dir' : + return $this->get_template_directory(); + case 'stylesheet_dir' : + return $this->get_stylesheet_directory(); + case 'template' : + return $this->get_template(); + case 'stylesheet' : + return $this->get_stylesheet(); + case 'screenshot' : + return $this->get_screenshot( 'relative' ); + // 'author' and 'description' did not previously return translated data. + case 'description' : + return $this->display('Description'); + case 'author' : + return $this->display('Author'); + case 'tags' : + return $this->get( 'Tags' ); + case 'theme_root' : + return $this->get_theme_root(); + case 'theme_root_uri' : + return $this->get_theme_root_uri(); + // For cases where the array was converted to an object. + default : + return $this->offsetGet( $offset ); + } + } + + /** + * Method to implement ArrayAccess for keys formerly returned by get_themes() + */ + public function offsetSet( $offset, $value ) {} + + /** + * Method to implement ArrayAccess for keys formerly returned by get_themes() + */ + public function offsetUnset( $offset ) {} + + /** + * Method to implement ArrayAccess for keys formerly returned by get_themes() + */ + public function offsetExists( $offset ) { + static $keys = array( + 'Name', 'Version', 'Status', 'Title', 'Author', 'Author Name', 'Author URI', 'Description', + 'Template', 'Stylesheet', 'Template Files', 'Stylesheet Files', 'Template Dir', 'Stylesheet Dir', + 'Screenshot', 'Tags', 'Theme Root', 'Theme Root URI', 'Parent Theme', + ); + + return in_array( $offset, $keys ); + } + + /** + * Method to implement ArrayAccess for keys formerly returned by get_themes(). + * + * Author, Author Name, Author URI, and Description did not previously return + * translated data. We are doing so now as it is safe to do. However, as + * Name and Title could have been used as the key for get_themes(), both remain + * untranslated for back compatibility. This means that ['Name'] is not ideal, + * and care should be taken to use $theme->display('Name') to get a properly + * translated header. + */ + public function offsetGet( $offset ) { + switch ( $offset ) { + case 'Name' : + case 'Title' : + // See note above about using translated data. get() is not ideal. + // It is only for backwards compatibility. Use display(). + return $this->get('Name'); + case 'Author' : + return $this->display( 'Author'); + case 'Author Name' : + return $this->display( 'Author', false); + case 'Author URI' : + return $this->display('AuthorURI'); + case 'Description' : + return $this->display( 'Description'); + case 'Version' : + case 'Status' : + return $this->get( $offset ); + case 'Template' : + return $this->get_template(); + case 'Stylesheet' : + return $this->get_stylesheet(); + case 'Template Files' : + return $this->get_files( 'php', 1, true ); + case 'Stylesheet Files' : + return $this->get_files( 'css', 0, false ); + case 'Template Dir' : + return $this->get_template_directory(); + case 'Stylesheet Dir' : + return $this->get_stylesheet_directory(); + case 'Screenshot' : + return $this->get_screenshot( 'relative' ); + case 'Tags' : + return $this->get('Tags'); + case 'Theme Root' : + return $this->get_theme_root(); + case 'Theme Root URI' : + return $this->get_theme_root_uri(); + case 'Parent Theme' : + return $this->parent() ? $this->parent()->get('Name') : ''; + default : + return null; + } + } + + /** + * Returns errors property. + * + * @since 3.4.0 + * @access public + * + * @return WP_Error|bool WP_Error if there are errors, or false. + */ + public function errors() { + return is_wp_error( $this->errors ) ? $this->errors : false; + } + + /** + * Whether the theme exists. + * + * A theme with errors exists. A theme with the error of 'theme_not_found', + * meaning that the theme's directory was not found, does not exist. + * + * @since 3.4.0 + * @access public + * + * @return bool Whether the theme exists. + */ + public function exists() { + return ! ( $this->errors() && in_array( 'theme_not_found', $this->errors()->get_error_codes() ) ); + } + + /** + * Returns reference to the parent theme. + * + * @since 3.4.0 + * @access public + * + * @return WP_Theme|bool Parent theme, or false if the current theme is not a child theme. + */ + public function parent() { + return isset( $this->parent ) ? $this->parent : false; + } + + /** + * Adds theme data to cache. + * + * Cache entries keyed by the theme and the type of data. + * + * @access private + * @since 3.4.0 + * + * @param string $key Type of data to store (theme, screenshot, headers, page_templates) + * @param string $data Data to store + * @return bool Return value from wp_cache_add() + */ + private function cache_add( $key, $data ) { + return wp_cache_add( $key . '-' . $this->cache_hash, $data, 'themes', self::$cache_expiration ); + } + + /** + * Gets theme data from cache. + * + * Cache entries are keyed by the theme and the type of data. + * + * @access private + * @since 3.4.0 + * + * @param string $key Type of data to retrieve (theme, screenshot, headers, page_templates) + * @return mixed Retrieved data + */ + private function cache_get( $key ) { + return wp_cache_get( $key . '-' . $this->cache_hash, 'themes' ); + } + + /** + * Clears the cache for the theme. + * + * @access public + * @since 3.4.0 + */ + public function cache_delete() { + foreach ( array( 'theme', 'screenshot', 'headers', 'page_templates' ) as $key ) + wp_cache_delete( $key . '-' . $this->cache_hash, 'themes' ); + $this->template = $this->textdomain_loaded = $this->theme_root_uri = $this->parent = $this->errors = $this->headers_sanitized = $this->name_translated = null; + $this->headers = array(); + $this->__construct( $this->stylesheet, $this->theme_root ); + } + + /** + * Get a raw, unformatted theme header. + * + * The header is sanitized, but is not translated, and is not marked up for display. + * To get a theme header for display, use the display() method. + * + * Use the get_template() method, not the 'Template' header, for finding the template. + * The 'Template' header is only good for what was written in the style.css, while + * get_template() takes into account where WordPress actually located the theme and + * whether it is actually valid. + * + * @access public + * @since 3.4.0 + * + * @param string $header Theme header. Name, Description, Author, Version, ThemeURI, AuthorURI, Status, Tags. + * @return string|bool String on success, false on failure. + */ + public function get( $header ) { + if ( ! isset( $this->headers[ $header ] ) ) + return false; + + if ( ! isset( $this->headers_sanitized ) ) { + $this->headers_sanitized = $this->cache_get( 'headers' ); + if ( ! is_array( $this->headers_sanitized ) ) + $this->headers_sanitized = array(); + } + + if ( isset( $this->headers_sanitized[ $header ] ) ) + return $this->headers_sanitized[ $header ]; + + // If themes are a persistent group, sanitize everything and cache it. One cache add is better than many cache sets. + if ( self::$persistently_cache ) { + foreach ( array_keys( $this->headers ) as $_header ) + $this->headers_sanitized[ $_header ] = $this->sanitize_header( $_header, $this->headers[ $_header ] ); + $this->cache_add( 'headers', $this->headers_sanitized ); + } else { + $this->headers_sanitized[ $header ] = $this->sanitize_header( $header, $this->headers[ $header ] ); + } + + return $this->headers_sanitized[ $header ]; + } + + /** + * Gets a theme header, formatted and translated for display. + * + * @access public + * @since 3.4.0 + * + * @param string $header Theme header. Name, Description, Author, Version, ThemeURI, AuthorURI, Status, Tags. + * @param bool $markup Optional. Whether to mark up the header. Defaults to true. + * @param bool $translate Optional. Whether to translate the header. Defaults to true. + * @return string|bool Processed header, false on failure. + */ + public function display( $header, $markup = true, $translate = true ) { + $value = $this->get( $header ); + if ( false === $value ) { + return false; + } + + if ( $translate && ( empty( $value ) || ! $this->load_textdomain() ) ) + $translate = false; + + if ( $translate ) + $value = $this->translate_header( $header, $value ); + + if ( $markup ) + $value = $this->markup_header( $header, $value, $translate ); + + return $value; + } + + /** + * Sanitize a theme header. + * + * @param string $header Theme header. Name, Description, Author, Version, ThemeURI, AuthorURI, Status, Tags. + * @param string $value Value to sanitize. + */ + private function sanitize_header( $header, $value ) { + switch ( $header ) { + case 'Status' : + if ( ! $value ) { + $value = 'publish'; + break; + } + // Fall through otherwise. + case 'Name' : + static $header_tags = array( + 'abbr' => array( 'title' => true ), + 'acronym' => array( 'title' => true ), + 'code' => true, + 'em' => true, + 'strong' => true, + ); + $value = wp_kses( $value, $header_tags ); + break; + case 'Author' : + // There shouldn't be anchor tags in Author, but some themes like to be challenging. + case 'Description' : + static $header_tags_with_a = array( + 'a' => array( 'href' => true, 'title' => true ), + 'abbr' => array( 'title' => true ), + 'acronym' => array( 'title' => true ), + 'code' => true, + 'em' => true, + 'strong' => true, + ); + $value = wp_kses( $value, $header_tags_with_a ); + break; + case 'ThemeURI' : + case 'AuthorURI' : + $value = esc_url_raw( $value ); + break; + case 'Tags' : + $value = array_filter( array_map( 'trim', explode( ',', strip_tags( $value ) ) ) ); + break; + } + + return $value; + } + + /** + * Mark up a theme header. + * + * @access private + * @since 3.4.0 + * + * @param string $header Theme header. Name, Description, Author, Version, ThemeURI, AuthorURI, Status, Tags. + * @param string $value Value to mark up. + * @param string $translate Whether the header has been translated. + * @return string Value, marked up. + */ + private function markup_header( $header, $value, $translate ) { + switch ( $header ) { + case 'Name' : + if ( empty( $value ) ) + $value = $this->get_stylesheet(); + break; + case 'Description' : + $value = wptexturize( $value ); + break; + case 'Author' : + if ( $this->get('AuthorURI') ) { + $value = sprintf( '<a href="%1$s">%2$s</a>', $this->display( 'AuthorURI', true, $translate ), $value ); + } elseif ( ! $value ) { + $value = __( 'Anonymous' ); + } + break; + case 'Tags' : + static $comma = null; + if ( ! isset( $comma ) ) { + /* translators: used between list items, there is a space after the comma */ + $comma = __( ', ' ); + } + $value = implode( $comma, $value ); + break; + case 'ThemeURI' : + case 'AuthorURI' : + $value = esc_url( $value ); + break; + } + + return $value; + } + + /** + * Translate a theme header. + * + * @access private + * @since 3.4.0 + * + * @param string $header Theme header. Name, Description, Author, Version, ThemeURI, AuthorURI, Status, Tags. + * @param string $value Value to translate. + * @return string Translated value. + */ + private function translate_header( $header, $value ) { + switch ( $header ) { + case 'Name' : + // Cached for sorting reasons. + if ( isset( $this->name_translated ) ) + return $this->name_translated; + $this->name_translated = translate( $value, $this->get('TextDomain' ) ); + return $this->name_translated; + case 'Tags' : + if ( empty( $value ) || ! function_exists( 'get_theme_feature_list' ) ) + return $value; + + static $tags_list; + if ( ! isset( $tags_list ) ) { + $tags_list = array(); + $feature_list = get_theme_feature_list( false ); // No API + foreach ( $feature_list as $tags ) + $tags_list += $tags; + } + + foreach ( $value as &$tag ) { + if ( isset( $tags_list[ $tag ] ) ) { + $tag = $tags_list[ $tag ]; + } elseif ( isset( self::$tag_map[ $tag ] ) ) { + $tag = $tags_list[ self::$tag_map[ $tag ] ]; + } + } + + return $value; + + default : + $value = translate( $value, $this->get('TextDomain') ); + } + return $value; + } + + /** + * The directory name of the theme's "stylesheet" files, inside the theme root. + * + * In the case of a child theme, this is directory name of the child theme. + * Otherwise, get_stylesheet() is the same as get_template(). + * + * @since 3.4.0 + * @access public + * + * @return string Stylesheet + */ + public function get_stylesheet() { + return $this->stylesheet; + } + + /** + * The directory name of the theme's "template" files, inside the theme root. + * + * In the case of a child theme, this is the directory name of the parent theme. + * Otherwise, the get_template() is the same as get_stylesheet(). + * + * @since 3.4.0 + * @access public + * + * @return string Template + */ + public function get_template() { + return $this->template; + } + + /** + * Returns the absolute path to the directory of a theme's "stylesheet" files. + * + * In the case of a child theme, this is the absolute path to the directory + * of the child theme's files. + * + * @since 3.4.0 + * @access public + * + * @return string Absolute path of the stylesheet directory. + */ + public function get_stylesheet_directory() { + if ( $this->errors() && in_array( 'theme_root_missing', $this->errors()->get_error_codes() ) ) + return ''; + + return $this->theme_root . '/' . $this->stylesheet; + } + + /** + * Returns the absolute path to the directory of a theme's "template" files. + * + * In the case of a child theme, this is the absolute path to the directory + * of the parent theme's files. + * + * @since 3.4.0 + * @access public + * + * @return string Absolute path of the template directory. + */ + public function get_template_directory() { + if ( $this->parent() ) + $theme_root = $this->parent()->theme_root; + else + $theme_root = $this->theme_root; + + return $theme_root . '/' . $this->template; + } + + /** + * Returns the URL to the directory of a theme's "stylesheet" files. + * + * In the case of a child theme, this is the URL to the directory of the + * child theme's files. + * + * @since 3.4.0 + * @access public + * + * @return string URL to the stylesheet directory. + */ + public function get_stylesheet_directory_uri() { + return $this->get_theme_root_uri() . '/' . str_replace( '%2F', '/', rawurlencode( $this->stylesheet ) ); + } + + /** + * Returns the URL to the directory of a theme's "template" files. + * + * In the case of a child theme, this is the URL to the directory of the + * parent theme's files. + * + * @since 3.4.0 + * @access public + * + * @return string URL to the template directory. + */ + public function get_template_directory_uri() { + if ( $this->parent() ) + $theme_root_uri = $this->parent()->get_theme_root_uri(); + else + $theme_root_uri = $this->get_theme_root_uri(); + + return $theme_root_uri . '/' . str_replace( '%2F', '/', rawurlencode( $this->template ) ); + } + + /** + * The absolute path to the directory of the theme root. + * + * This is typically the absolute path to wp-content/themes. + * + * @since 3.4.0 + * @access public + * + * @return string Theme root. + */ + public function get_theme_root() { + return $this->theme_root; + } + + /** + * Returns the URL to the directory of the theme root. + * + * This is typically the absolute URL to wp-content/themes. This forms the basis + * for all other URLs returned by WP_Theme, so we pass it to the public function + * get_theme_root_uri() and allow it to run the theme_root_uri filter. + * + * @since 3.4.0 + * @access public + * + * @return string Theme root URI. + */ + public function get_theme_root_uri() { + if ( ! isset( $this->theme_root_uri ) ) + $this->theme_root_uri = get_theme_root_uri( $this->stylesheet, $this->theme_root ); + return $this->theme_root_uri; + } + + /** + * Returns the main screenshot file for the theme. + * + * The main screenshot is called screenshot.png. gif and jpg extensions are also allowed. + * + * Screenshots for a theme must be in the stylesheet directory. (In the case of child + * themes, parent theme screenshots are not inherited.) + * + * @since 3.4.0 + * @access public + * + * @param string $uri Type of URL to return, either 'relative' or an absolute URI. Defaults to absolute URI. + * @return mixed Screenshot file. False if the theme does not have a screenshot. + */ + public function get_screenshot( $uri = 'uri' ) { + $screenshot = $this->cache_get( 'screenshot' ); + if ( $screenshot ) { + if ( 'relative' == $uri ) + return $screenshot; + return $this->get_stylesheet_directory_uri() . '/' . $screenshot; + } elseif ( 0 === $screenshot ) { + return false; + } + + foreach ( array( 'png', 'gif', 'jpg', 'jpeg' ) as $ext ) { + if ( file_exists( $this->get_stylesheet_directory() . "/screenshot.$ext" ) ) { + $this->cache_add( 'screenshot', 'screenshot.' . $ext ); + if ( 'relative' == $uri ) + return 'screenshot.' . $ext; + return $this->get_stylesheet_directory_uri() . '/' . 'screenshot.' . $ext; + } + } + + $this->cache_add( 'screenshot', 0 ); + return false; + } + + /** + * Return files in the theme's directory. + * + * @since 3.4.0 + * @access public + * + * @param mixed $type Optional. Array of extensions to return. Defaults to all files (null). + * @param int $depth Optional. How deep to search for files. Defaults to a flat scan (0 depth). -1 depth is infinite. + * @param bool $search_parent Optional. Whether to return parent files. Defaults to false. + * @return array Array of files, keyed by the path to the file relative to the theme's directory, with the values + * being absolute paths. + */ + public function get_files( $type = null, $depth = 0, $search_parent = false ) { + $files = (array) self::scandir( $this->get_stylesheet_directory(), $type, $depth ); + + if ( $search_parent && $this->parent() ) + $files += (array) self::scandir( $this->get_template_directory(), $type, $depth ); + + return $files; + } + + /** + * Returns the theme's page templates. + * + * @since 3.4.0 + * @access public + * + * @param WP_Post|null $post Optional. The post being edited, provided for context. + * @return array Array of page templates, keyed by filename, with the value of the translated header name. + */ + public function get_page_templates( $post = null ) { + // If you screw up your current theme and we invalidate your parent, most things still work. Let it slide. + if ( $this->errors() && $this->errors()->get_error_codes() !== array( 'theme_parent_invalid' ) ) + return array(); + + $page_templates = $this->cache_get( 'page_templates' ); + + if ( ! is_array( $page_templates ) ) { + $page_templates = array(); + + $files = (array) $this->get_files( 'php', 1 ); + + foreach ( $files as $file => $full_path ) { + if ( ! preg_match( '|Template Name:(.*)$|mi', file_get_contents( $full_path ), $header ) ) + continue; + $page_templates[ $file ] = _cleanup_header_comment( $header[1] ); + } + + $this->cache_add( 'page_templates', $page_templates ); + } + + if ( $this->load_textdomain() ) { + foreach ( $page_templates as &$page_template ) { + $page_template = $this->translate_header( 'Template Name', $page_template ); + } + } + + if ( $this->parent() ) + $page_templates += $this->parent()->get_page_templates( $post ); + + /** + * Filter list of page templates for a theme. + * + * This filter does not currently allow for page templates to be added. + * + * @since 3.9.0 + * + * @param array $page_templates Array of page templates. Keys are filenames, + * values are translated names. + * @param WP_Theme $this The theme object. + * @param WP_Post|null $post The post being edited, provided for context, or null. + */ + $return = apply_filters( 'theme_page_templates', $page_templates, $this, $post ); + + return array_intersect_assoc( $return, $page_templates ); + } + + /** + * Scans a directory for files of a certain extension. + * + * @since 3.4.0 + * @access private + * + * @param string $path Absolute path to search. + * @param mixed Array of extensions to find, string of a single extension, or null for all extensions. + * @param int $depth How deep to search for files. Optional, defaults to a flat scan (0 depth). -1 depth is infinite. + * @param string $relative_path The basename of the absolute path. Used to control the returned path + * for the found files, particularly when this function recurses to lower depths. + */ + private static function scandir( $path, $extensions = null, $depth = 0, $relative_path = '' ) { + if ( ! is_dir( $path ) ) + return false; + + if ( $extensions ) { + $extensions = (array) $extensions; + $_extensions = implode( '|', $extensions ); + } + + $relative_path = trailingslashit( $relative_path ); + if ( '/' == $relative_path ) + $relative_path = ''; + + $results = scandir( $path ); + $files = array(); + + foreach ( $results as $result ) { + if ( '.' == $result[0] ) + continue; + if ( is_dir( $path . '/' . $result ) ) { + if ( ! $depth || 'CVS' == $result ) + continue; + $found = self::scandir( $path . '/' . $result, $extensions, $depth - 1 , $relative_path . $result ); + $files = array_merge_recursive( $files, $found ); + } elseif ( ! $extensions || preg_match( '~\.(' . $_extensions . ')$~', $result ) ) { + $files[ $relative_path . $result ] = $path . '/' . $result; + } + } + + return $files; + } + + /** + * Loads the theme's textdomain. + * + * Translation files are not inherited from the parent theme. Todo: if this fails for the + * child theme, it should probably try to load the parent theme's translations. + * + * @since 3.4.0 + * @access public + * + * @return bool If the textdomain was successfully loaded or has already been loaded. False if + * no textdomain was specified in the file headers, or if the domain could not be loaded. + */ + public function load_textdomain() { + if ( isset( $this->textdomain_loaded ) ) + return $this->textdomain_loaded; + + $textdomain = $this->get('TextDomain'); + if ( ! $textdomain ) { + $this->textdomain_loaded = false; + return false; + } + + if ( is_textdomain_loaded( $textdomain ) ) { + $this->textdomain_loaded = true; + return true; + } + + $path = $this->get_stylesheet_directory(); + if ( $domainpath = $this->get('DomainPath') ) + $path .= $domainpath; + else + $path .= '/languages'; + + $this->textdomain_loaded = load_theme_textdomain( $textdomain, $path ); + return $this->textdomain_loaded; + } + + /** + * Whether the theme is allowed (multisite only). + * + * @since 3.4.0 + * @access public + * + * @param string $check Optional. Whether to check only the 'network'-wide settings, the 'site' + * settings, or 'both'. Defaults to 'both'. + * @param int $blog_id Optional. Ignored if only network-wide settings are checked. Defaults to current blog. + * @return bool Whether the theme is allowed for the network. Returns true in single-site. + */ + public function is_allowed( $check = 'both', $blog_id = null ) { + if ( ! is_multisite() ) + return true; + + if ( 'both' == $check || 'network' == $check ) { + $allowed = self::get_allowed_on_network(); + if ( ! empty( $allowed[ $this->get_stylesheet() ] ) ) + return true; + } + + if ( 'both' == $check || 'site' == $check ) { + $allowed = self::get_allowed_on_site( $blog_id ); + if ( ! empty( $allowed[ $this->get_stylesheet() ] ) ) + return true; + } + + return false; + } + + /** + * Returns array of stylesheet names of themes allowed on the site or network. + * + * @since 3.4.0 + * @access public + * + * @param int $blog_id Optional. Defaults to current blog. + * @return array Array of stylesheet names. + */ + public static function get_allowed( $blog_id = null ) { + /** + * Filter the array of themes allowed on the site or network. + * + * @since MU + * + * @param array $allowed_themes An array of theme stylesheet names. + */ + $network = (array) apply_filters( 'allowed_themes', self::get_allowed_on_network() ); + return $network + self::get_allowed_on_site( $blog_id ); + } + + /** + * Returns array of stylesheet names of themes allowed on the network. + * + * @since 3.4.0 + * @access public + * + * @return array Array of stylesheet names. + */ + public static function get_allowed_on_network() { + static $allowed_themes; + if ( ! isset( $allowed_themes ) ) + $allowed_themes = (array) get_site_option( 'allowedthemes' ); + return $allowed_themes; + } + + /** + * Returns array of stylesheet names of themes allowed on the site. + * + * @since 3.4.0 + * @access public + * + * @param int $blog_id Optional. Defaults to current blog. + * @return array Array of stylesheet names. + */ + public static function get_allowed_on_site( $blog_id = null ) { + static $allowed_themes = array(); + + if ( ! $blog_id || ! is_multisite() ) + $blog_id = get_current_blog_id(); + + if ( isset( $allowed_themes[ $blog_id ] ) ) + return $allowed_themes[ $blog_id ]; + + $current = $blog_id == get_current_blog_id(); + + if ( $current ) { + $allowed_themes[ $blog_id ] = get_option( 'allowedthemes' ); + } else { + switch_to_blog( $blog_id ); + $allowed_themes[ $blog_id ] = get_option( 'allowedthemes' ); + restore_current_blog(); + } + + // This is all super old MU back compat joy. + // 'allowedthemes' keys things by stylesheet. 'allowed_themes' keyed things by name. + if ( false === $allowed_themes[ $blog_id ] ) { + if ( $current ) { + $allowed_themes[ $blog_id ] = get_option( 'allowed_themes' ); + } else { + switch_to_blog( $blog_id ); + $allowed_themes[ $blog_id ] = get_option( 'allowed_themes' ); + restore_current_blog(); + } + + if ( ! is_array( $allowed_themes[ $blog_id ] ) || empty( $allowed_themes[ $blog_id ] ) ) { + $allowed_themes[ $blog_id ] = array(); + } else { + $converted = array(); + $themes = wp_get_themes(); + foreach ( $themes as $stylesheet => $theme_data ) { + if ( isset( $allowed_themes[ $blog_id ][ $theme_data->get('Name') ] ) ) + $converted[ $stylesheet ] = true; + } + $allowed_themes[ $blog_id ] = $converted; + } + // Set the option so we never have to go through this pain again. + if ( is_admin() && $allowed_themes[ $blog_id ] ) { + if ( $current ) { + update_option( 'allowedthemes', $allowed_themes[ $blog_id ] ); + delete_option( 'allowed_themes' ); + } else { + switch_to_blog( $blog_id ); + update_option( 'allowedthemes', $allowed_themes[ $blog_id ] ); + delete_option( 'allowed_themes' ); + restore_current_blog(); + } + } + } + + return (array) $allowed_themes[ $blog_id ]; + } + + /** + * Sort themes by name. + * + * @since 3.4.0 + * @access public + */ + public static function sort_by_name( &$themes ) { + if ( 0 === strpos( get_locale(), 'en_' ) ) { + uasort( $themes, array( 'WP_Theme', '_name_sort' ) ); + } else { + uasort( $themes, array( 'WP_Theme', '_name_sort_i18n' ) ); + } + } + + /** + * Callback function for usort() to naturally sort themes by name. + * + * Accesses the Name header directly from the class for maximum speed. + * Would choke on HTML but we don't care enough to slow it down with strip_tags(). + * + * @since 3.4.0 + * @access private + */ + private static function _name_sort( $a, $b ) { + return strnatcasecmp( $a->headers['Name'], $b->headers['Name'] ); + } + + /** + * Name sort (with translation). + * + * @since 3.4.0 + * @access private + */ + private static function _name_sort_i18n( $a, $b ) { + // Don't mark up; Do translate. + return strnatcasecmp( $a->display( 'Name', false, true ), $b->display( 'Name', false, true ) ); + } +} diff --git a/wp-includes/class-wp-walker.php b/wp-includes/class-wp-walker.php new file mode 100644 index 0000000..2519164 --- /dev/null +++ b/wp-includes/class-wp-walker.php @@ -0,0 +1,471 @@ +<?php +/** + * A class for displaying various tree-like structures. + * + * Extend the Walker class to use it, see examples below. Child classes + * do not need to implement all of the abstract methods in the class. The child + * only needs to implement the methods that are needed. + * + * @since 2.1.0 + * + * @package WordPress + * @abstract + */ +class Walker { + /** + * What the class handles. + * + * @since 2.1.0 + * @access public + * @var string + */ + public $tree_type; + + /** + * DB fields to use. + * + * @since 2.1.0 + * @access protected + * @var array + */ + protected $db_fields; + + /** + * Max number of pages walked by the paged walker + * + * @since 2.7.0 + * @access protected + * @var int + */ + protected $max_pages = 1; + + /** + * Whether the current element has children or not. + * + * To be used in start_el(). + * + * @since 4.0.0 + * @access protected + * @var bool + */ + protected $has_children; + + /** + * Make private properties readable for backwards compatibility. + * + * @since 4.0.0 + * @access public + * + * @param string $name Property to get. + * @return mixed Property. + */ + public function __get( $name ) { + return $this->$name; + } + + /** + * Make private properties settable for backwards compatibility. + * + * @since 4.0.0 + * @access public + * + * @param string $name Property to set. + * @param mixed $value Property value. + * @return mixed Newly-set property. + */ + public function __set( $name, $value ) { + return $this->$name = $value; + } + + /** + * Make private properties checkable for backwards compatibility. + * + * @since 4.0.0 + * @access public + * + * @param string $name Property to check if set. + * @return bool Whether the property is set. + */ + public function __isset( $name ) { + return isset( $this->$name ); + } + + /** + * Make private properties un-settable for backwards compatibility. + * + * @since 4.0.0 + * @access public + * + * @param string $name Property to unset. + */ + public function __unset( $name ) { + unset( $this->$name ); + } + + /** + * Starts the list before the elements are added. + * + * The $args parameter holds additional values that may be used with the child + * class methods. This method is called at the start of the output list. + * + * @since 2.1.0 + * @abstract + * + * @param string $output Passed by reference. Used to append additional content. + * @param int $depth Depth of the item. + * @param array $args An array of additional arguments. + */ + public function start_lvl( &$output, $depth = 0, $args = array() ) {} + + /** + * Ends the list of after the elements are added. + * + * The $args parameter holds additional values that may be used with the child + * class methods. This method finishes the list at the end of output of the elements. + * + * @since 2.1.0 + * @abstract + * + * @param string $output Passed by reference. Used to append additional content. + * @param int $depth Depth of the item. + * @param array $args An array of additional arguments. + */ + public function end_lvl( &$output, $depth = 0, $args = array() ) {} + + /** + * Start the element output. + * + * The $args parameter holds additional values that may be used with the child + * class methods. Includes the element output also. + * + * @since 2.1.0 + * @abstract + * + * @param string $output Passed by reference. Used to append additional content. + * @param object $object The data object. + * @param int $depth Depth of the item. + * @param array $args An array of additional arguments. + * @param int $current_object_id ID of the current item. + */ + public function start_el( &$output, $object, $depth = 0, $args = array(), $current_object_id = 0 ) {} + + /** + * Ends the element output, if needed. + * + * The $args parameter holds additional values that may be used with the child class methods. + * + * @since 2.1.0 + * @abstract + * + * @param string $output Passed by reference. Used to append additional content. + * @param object $object The data object. + * @param int $depth Depth of the item. + * @param array $args An array of additional arguments. + */ + public function end_el( &$output, $object, $depth = 0, $args = array() ) {} + + /** + * Traverse elements to create list from elements. + * + * Display one element if the element doesn't have any children otherwise, + * display the element and its children. Will only traverse up to the max + * depth and no ignore elements under that depth. It is possible to set the + * max depth to include all depths, see walk() method. + * + * This method should not be called directly, use the walk() method instead. + * + * @since 2.5.0 + * + * @param object $element Data object. + * @param array $children_elements List of elements to continue traversing. + * @param int $max_depth Max depth to traverse. + * @param int $depth Depth of current element. + * @param array $args An array of arguments. + * @param string $output Passed by reference. Used to append additional content. + * @return null Null on failure with no changes to parameters. + */ + public function display_element( $element, &$children_elements, $max_depth, $depth, $args, &$output ) { + + if ( !$element ) + return; + + $id_field = $this->db_fields['id']; + $id = $element->$id_field; + + //display this element + $this->has_children = ! empty( $children_elements[ $id ] ); + if ( isset( $args[0] ) && is_array( $args[0] ) ) { + $args[0]['has_children'] = $this->has_children; // Backwards compatibility. + } + + $cb_args = array_merge( array(&$output, $element, $depth), $args); + call_user_func_array(array($this, 'start_el'), $cb_args); + + // descend only when the depth is right and there are childrens for this element + if ( ($max_depth == 0 || $max_depth > $depth+1 ) && isset( $children_elements[$id]) ) { + + foreach( $children_elements[ $id ] as $child ){ + + if ( !isset($newlevel) ) { + $newlevel = true; + //start the child delimiter + $cb_args = array_merge( array(&$output, $depth), $args); + call_user_func_array(array($this, 'start_lvl'), $cb_args); + } + $this->display_element( $child, $children_elements, $max_depth, $depth + 1, $args, $output ); + } + unset( $children_elements[ $id ] ); + } + + if ( isset($newlevel) && $newlevel ){ + //end the child delimiter + $cb_args = array_merge( array(&$output, $depth), $args); + call_user_func_array(array($this, 'end_lvl'), $cb_args); + } + + //end this element + $cb_args = array_merge( array(&$output, $element, $depth), $args); + call_user_func_array(array($this, 'end_el'), $cb_args); + } + + /** + * Display array of elements hierarchically. + * + * Does not assume any existing order of elements. + * + * $max_depth = -1 means flatly display every element. + * $max_depth = 0 means display all levels. + * $max_depth > 0 specifies the number of display levels. + * + * @since 2.1.0 + * + * @param array $elements An array of elements. + * @param int $max_depth The maximum hierarchical depth. + * @return string The hierarchical item output. + */ + public function walk( $elements, $max_depth) { + + $args = array_slice(func_get_args(), 2); + $output = ''; + + if ($max_depth < -1) //invalid parameter + return $output; + + if (empty($elements)) //nothing to walk + return $output; + + $parent_field = $this->db_fields['parent']; + + // flat display + if ( -1 == $max_depth ) { + $empty_array = array(); + foreach ( $elements as $e ) + $this->display_element( $e, $empty_array, 1, 0, $args, $output ); + return $output; + } + + /* + * Need to display in hierarchical order. + * Separate elements into two buckets: top level and children elements. + * Children_elements is two dimensional array, eg. + * Children_elements[10][] contains all sub-elements whose parent is 10. + */ + $top_level_elements = array(); + $children_elements = array(); + foreach ( $elements as $e) { + if ( 0 == $e->$parent_field ) + $top_level_elements[] = $e; + else + $children_elements[ $e->$parent_field ][] = $e; + } + + /* + * When none of the elements is top level. + * Assume the first one must be root of the sub elements. + */ + if ( empty($top_level_elements) ) { + + $first = array_slice( $elements, 0, 1 ); + $root = $first[0]; + + $top_level_elements = array(); + $children_elements = array(); + foreach ( $elements as $e) { + if ( $root->$parent_field == $e->$parent_field ) + $top_level_elements[] = $e; + else + $children_elements[ $e->$parent_field ][] = $e; + } + } + + foreach ( $top_level_elements as $e ) + $this->display_element( $e, $children_elements, $max_depth, 0, $args, $output ); + + /* + * If we are displaying all levels, and remaining children_elements is not empty, + * then we got orphans, which should be displayed regardless. + */ + if ( ( $max_depth == 0 ) && count( $children_elements ) > 0 ) { + $empty_array = array(); + foreach ( $children_elements as $orphans ) + foreach( $orphans as $op ) + $this->display_element( $op, $empty_array, 1, 0, $args, $output ); + } + + return $output; + } + + /** + * paged_walk() - produce a page of nested elements + * + * Given an array of hierarchical elements, the maximum depth, a specific page number, + * and number of elements per page, this function first determines all top level root elements + * belonging to that page, then lists them and all of their children in hierarchical order. + * + * $max_depth = 0 means display all levels. + * $max_depth > 0 specifies the number of display levels. + * + * @since 2.7.0 + * + * @param int $max_depth The maximum hierarchical depth. + * @param int $page_num The specific page number, beginning with 1. + * @return string XHTML of the specified page of elements + */ + public function paged_walk( $elements, $max_depth, $page_num, $per_page ) { + + /* sanity check */ + if ( empty($elements) || $max_depth < -1 ) + return ''; + + $args = array_slice( func_get_args(), 4 ); + $output = ''; + + $parent_field = $this->db_fields['parent']; + + $count = -1; + if ( -1 == $max_depth ) + $total_top = count( $elements ); + if ( $page_num < 1 || $per_page < 0 ) { + // No paging + $paging = false; + $start = 0; + if ( -1 == $max_depth ) + $end = $total_top; + $this->max_pages = 1; + } else { + $paging = true; + $start = ( (int)$page_num - 1 ) * (int)$per_page; + $end = $start + $per_page; + if ( -1 == $max_depth ) + $this->max_pages = ceil($total_top / $per_page); + } + + // flat display + if ( -1 == $max_depth ) { + if ( !empty($args[0]['reverse_top_level']) ) { + $elements = array_reverse( $elements ); + $oldstart = $start; + $start = $total_top - $end; + $end = $total_top - $oldstart; + } + + $empty_array = array(); + foreach ( $elements as $e ) { + $count++; + if ( $count < $start ) + continue; + if ( $count >= $end ) + break; + $this->display_element( $e, $empty_array, 1, 0, $args, $output ); + } + return $output; + } + + /* + * Separate elements into two buckets: top level and children elements. + * Children_elements is two dimensional array, e.g. + * $children_elements[10][] contains all sub-elements whose parent is 10. + */ + $top_level_elements = array(); + $children_elements = array(); + foreach ( $elements as $e) { + if ( 0 == $e->$parent_field ) + $top_level_elements[] = $e; + else + $children_elements[ $e->$parent_field ][] = $e; + } + + $total_top = count( $top_level_elements ); + if ( $paging ) + $this->max_pages = ceil($total_top / $per_page); + else + $end = $total_top; + + if ( !empty($args[0]['reverse_top_level']) ) { + $top_level_elements = array_reverse( $top_level_elements ); + $oldstart = $start; + $start = $total_top - $end; + $end = $total_top - $oldstart; + } + if ( !empty($args[0]['reverse_children']) ) { + foreach ( $children_elements as $parent => $children ) + $children_elements[$parent] = array_reverse( $children ); + } + + foreach ( $top_level_elements as $e ) { + $count++; + + // For the last page, need to unset earlier children in order to keep track of orphans. + if ( $end >= $total_top && $count < $start ) + $this->unset_children( $e, $children_elements ); + + if ( $count < $start ) + continue; + + if ( $count >= $end ) + break; + + $this->display_element( $e, $children_elements, $max_depth, 0, $args, $output ); + } + + if ( $end >= $total_top && count( $children_elements ) > 0 ) { + $empty_array = array(); + foreach ( $children_elements as $orphans ) + foreach( $orphans as $op ) + $this->display_element( $op, $empty_array, 1, 0, $args, $output ); + } + + return $output; + } + + public function get_number_of_root_elements( $elements ){ + + $num = 0; + $parent_field = $this->db_fields['parent']; + + foreach ( $elements as $e) { + if ( 0 == $e->$parent_field ) + $num++; + } + return $num; + } + + // Unset all the children for a given top level element. + public function unset_children( $e, &$children_elements ){ + + if ( !$e || !$children_elements ) + return; + + $id_field = $this->db_fields['id']; + $id = $e->$id_field; + + if ( !empty($children_elements[$id]) && is_array($children_elements[$id]) ) + foreach ( (array) $children_elements[$id] as $child ) + $this->unset_children( $child, $children_elements ); + + if ( isset($children_elements[$id]) ) + unset( $children_elements[$id] ); + + } + +} // Walker diff --git a/wp-includes/class-wp-xmlrpc-server.php b/wp-includes/class-wp-xmlrpc-server.php new file mode 100644 index 0000000..89bf512 --- /dev/null +++ b/wp-includes/class-wp-xmlrpc-server.php @@ -0,0 +1,5935 @@ +<?php +/** + * XML-RPC protocol support for WordPress + * + * @package WordPress + * @subpackage Publishing + */ + +/** + * WordPress XMLRPC server implementation. + * + * Implements compatibility for Blogger API, MetaWeblog API, MovableType, and + * pingback. Additional WordPress API for managing comments, pages, posts, + * options, etc. + * + * As of WordPress 3.5.0, XML-RPC is enabled by default. It can be disabled + * via the xmlrpc_enabled filter found in wp_xmlrpc_server::login(). + * + * @package WordPress + * @subpackage Publishing + * @since 1.5.0 + */ +class wp_xmlrpc_server extends IXR_Server { + /** + * @var array + */ + public $methods; + /** + * @var array + */ + public $blog_options; + /** + * @var IXR_Error + */ + public $error; + /** + * Register all of the XMLRPC methods that XMLRPC server understands. + * + * Sets up server and method property. Passes XMLRPC + * methods through the 'xmlrpc_methods' filter to allow plugins to extend + * or replace XMLRPC methods. + * + * @since 1.5.0 + * + * @return wp_xmlrpc_server + */ + public function __construct() { + $this->methods = array( + // WordPress API + 'wp.getUsersBlogs' => 'this:wp_getUsersBlogs', + 'wp.newPost' => 'this:wp_newPost', + 'wp.editPost' => 'this:wp_editPost', + 'wp.deletePost' => 'this:wp_deletePost', + 'wp.getPost' => 'this:wp_getPost', + 'wp.getPosts' => 'this:wp_getPosts', + 'wp.newTerm' => 'this:wp_newTerm', + 'wp.editTerm' => 'this:wp_editTerm', + 'wp.deleteTerm' => 'this:wp_deleteTerm', + 'wp.getTerm' => 'this:wp_getTerm', + 'wp.getTerms' => 'this:wp_getTerms', + 'wp.getTaxonomy' => 'this:wp_getTaxonomy', + 'wp.getTaxonomies' => 'this:wp_getTaxonomies', + 'wp.getUser' => 'this:wp_getUser', + 'wp.getUsers' => 'this:wp_getUsers', + 'wp.getProfile' => 'this:wp_getProfile', + 'wp.editProfile' => 'this:wp_editProfile', + 'wp.getPage' => 'this:wp_getPage', + 'wp.getPages' => 'this:wp_getPages', + 'wp.newPage' => 'this:wp_newPage', + 'wp.deletePage' => 'this:wp_deletePage', + 'wp.editPage' => 'this:wp_editPage', + 'wp.getPageList' => 'this:wp_getPageList', + 'wp.getAuthors' => 'this:wp_getAuthors', + 'wp.getCategories' => 'this:mw_getCategories', // Alias + 'wp.getTags' => 'this:wp_getTags', + 'wp.newCategory' => 'this:wp_newCategory', + 'wp.deleteCategory' => 'this:wp_deleteCategory', + 'wp.suggestCategories' => 'this:wp_suggestCategories', + 'wp.uploadFile' => 'this:mw_newMediaObject', // Alias + 'wp.deleteFile' => 'this:wp_deletePost', // Alias + 'wp.getCommentCount' => 'this:wp_getCommentCount', + 'wp.getPostStatusList' => 'this:wp_getPostStatusList', + 'wp.getPageStatusList' => 'this:wp_getPageStatusList', + 'wp.getPageTemplates' => 'this:wp_getPageTemplates', + 'wp.getOptions' => 'this:wp_getOptions', + 'wp.setOptions' => 'this:wp_setOptions', + 'wp.getComment' => 'this:wp_getComment', + 'wp.getComments' => 'this:wp_getComments', + 'wp.deleteComment' => 'this:wp_deleteComment', + 'wp.editComment' => 'this:wp_editComment', + 'wp.newComment' => 'this:wp_newComment', + 'wp.getCommentStatusList' => 'this:wp_getCommentStatusList', + 'wp.getMediaItem' => 'this:wp_getMediaItem', + 'wp.getMediaLibrary' => 'this:wp_getMediaLibrary', + 'wp.getPostFormats' => 'this:wp_getPostFormats', + 'wp.getPostType' => 'this:wp_getPostType', + 'wp.getPostTypes' => 'this:wp_getPostTypes', + 'wp.getRevisions' => 'this:wp_getRevisions', + 'wp.restoreRevision' => 'this:wp_restoreRevision', + + // Blogger API + 'blogger.getUsersBlogs' => 'this:blogger_getUsersBlogs', + 'blogger.getUserInfo' => 'this:blogger_getUserInfo', + 'blogger.getPost' => 'this:blogger_getPost', + 'blogger.getRecentPosts' => 'this:blogger_getRecentPosts', + 'blogger.newPost' => 'this:blogger_newPost', + 'blogger.editPost' => 'this:blogger_editPost', + 'blogger.deletePost' => 'this:blogger_deletePost', + + // MetaWeblog API (with MT extensions to structs) + 'metaWeblog.newPost' => 'this:mw_newPost', + 'metaWeblog.editPost' => 'this:mw_editPost', + 'metaWeblog.getPost' => 'this:mw_getPost', + 'metaWeblog.getRecentPosts' => 'this:mw_getRecentPosts', + 'metaWeblog.getCategories' => 'this:mw_getCategories', + 'metaWeblog.newMediaObject' => 'this:mw_newMediaObject', + + // MetaWeblog API aliases for Blogger API + // see http://www.xmlrpc.com/stories/storyReader$2460 + 'metaWeblog.deletePost' => 'this:blogger_deletePost', + 'metaWeblog.getUsersBlogs' => 'this:blogger_getUsersBlogs', + + // MovableType API + 'mt.getCategoryList' => 'this:mt_getCategoryList', + 'mt.getRecentPostTitles' => 'this:mt_getRecentPostTitles', + 'mt.getPostCategories' => 'this:mt_getPostCategories', + 'mt.setPostCategories' => 'this:mt_setPostCategories', + 'mt.supportedMethods' => 'this:mt_supportedMethods', + 'mt.supportedTextFilters' => 'this:mt_supportedTextFilters', + 'mt.getTrackbackPings' => 'this:mt_getTrackbackPings', + 'mt.publishPost' => 'this:mt_publishPost', + + // PingBack + 'pingback.ping' => 'this:pingback_ping', + 'pingback.extensions.getPingbacks' => 'this:pingback_extensions_getPingbacks', + + 'demo.sayHello' => 'this:sayHello', + 'demo.addTwoNumbers' => 'this:addTwoNumbers' + ); + + $this->initialise_blog_option_info(); + + /** + * Filter the methods exposed by the XML-RPC server. + * + * This filter can be used to add new methods, and remove built-in methods. + * + * @since 1.5.0 + * + * @param array $methods An array of XML-RPC methods. + */ + $this->methods = apply_filters( 'xmlrpc_methods', $this->methods ); + } + + /** + * Make private/protected methods readable for backwards compatibility. + * + * @since 4.0.0 + * @access public + * + * @param callable $name Method to call. + * @param array $arguments Arguments to pass when calling. + * @return mixed|bool Return value of the callback, false otherwise. + */ + public function __call( $name, $arguments ) { + return call_user_func_array( array( $this, $name ), $arguments ); + } + + public function serve_request() { + $this->IXR_Server($this->methods); + } + + /** + * Test XMLRPC API by saying, "Hello!" to client. + * + * @since 1.5.0 + * + * @param array $args Method Parameters. + * @return string + */ + public function sayHello($args) { + return 'Hello!'; + } + + /** + * Test XMLRPC API by adding two numbers for client. + * + * @since 1.5.0 + * + * @param array $args Method Parameters. + * @return int + */ + public function addTwoNumbers($args) { + $number1 = $args[0]; + $number2 = $args[1]; + return $number1 + $number2; + } + + /** + * Log user in. + * + * @since 2.8.0 + * + * @param string $username User's username. + * @param string $password User's password. + * @return WP_User|bool WP_User object if authentication passed, false otherwise + */ + public function login( $username, $password ) { + /* + * Respect old get_option() filters left for back-compat when the 'enable_xmlrpc' + * option was deprecated in 3.5.0. Use the 'xmlrpc_enabled' hook instead. + */ + $enabled = apply_filters( 'pre_option_enable_xmlrpc', false ); + if ( false === $enabled ) { + $enabled = apply_filters( 'option_enable_xmlrpc', true ); + } + + /** + * Filter whether XML-RPC is enabled. + * + * This is the proper filter for turning off XML-RPC. + * + * @since 3.5.0 + * + * @param bool $enabled Whether XML-RPC is enabled. Default true. + */ + $enabled = apply_filters( 'xmlrpc_enabled', $enabled ); + + if ( ! $enabled ) { + $this->error = new IXR_Error( 405, sprintf( __( 'XML-RPC services are disabled on this site.' ) ) ); + return false; + } + + $user = wp_authenticate($username, $password); + + if (is_wp_error($user)) { + $this->error = new IXR_Error( 403, __( 'Incorrect username or password.' ) ); + + /** + * Filter the XML-RPC user login error message. + * + * @since 3.5.0 + * + * @param string $error The XML-RPC error message. + * @param WP_User $user WP_User object. + */ + $this->error = apply_filters( 'xmlrpc_login_error', $this->error, $user ); + return false; + } + + wp_set_current_user( $user->ID ); + return $user; + } + + /** + * Check user's credentials. Deprecated. + * + * @since 1.5.0 + * @deprecated 2.8.0 + * @deprecated use wp_xmlrpc_server::login + * @see wp_xmlrpc_server::login + * + * @param string $username User's username. + * @param string $password User's password. + * @return bool Whether authentication passed. + */ + public function login_pass_ok( $username, $password ) { + return (bool) $this->login( $username, $password ); + } + + /** + * Escape string or array of strings for database. + * + * @since 1.5.2 + * + * @param string|array $data Escape single string or array of strings. + * @return string|array Type matches $data and sanitized for the database. + */ + public function escape( &$data ) { + if ( ! is_array( $data ) ) + return wp_slash( $data ); + + foreach ( $data as &$v ) { + if ( is_array( $v ) ) + $this->escape( $v ); + elseif ( ! is_object( $v ) ) + $v = wp_slash( $v ); + } + } + + /** + * Retrieve custom fields for post. + * + * @since 2.5.0 + * + * @param int $post_id Post ID. + * @return array Custom fields, if exist. + */ + public function get_custom_fields($post_id) { + $post_id = (int) $post_id; + + $custom_fields = array(); + + foreach ( (array) has_meta($post_id) as $meta ) { + // Don't expose protected fields. + if ( ! current_user_can( 'edit_post_meta', $post_id , $meta['meta_key'] ) ) + continue; + + $custom_fields[] = array( + "id" => $meta['meta_id'], + "key" => $meta['meta_key'], + "value" => $meta['meta_value'] + ); + } + + return $custom_fields; + } + + /** + * Set custom fields for post. + * + * @since 2.5.0 + * + * @param int $post_id Post ID. + * @param array $fields Custom fields. + */ + public function set_custom_fields($post_id, $fields) { + $post_id = (int) $post_id; + + foreach ( (array) $fields as $meta ) { + if ( isset($meta['id']) ) { + $meta['id'] = (int) $meta['id']; + $pmeta = get_metadata_by_mid( 'post', $meta['id'] ); + if ( isset($meta['key']) ) { + $meta['key'] = wp_unslash( $meta['key'] ); + if ( $meta['key'] !== $pmeta->meta_key ) + continue; + $meta['value'] = wp_unslash( $meta['value'] ); + if ( current_user_can( 'edit_post_meta', $post_id, $meta['key'] ) ) + update_metadata_by_mid( 'post', $meta['id'], $meta['value'] ); + } elseif ( current_user_can( 'delete_post_meta', $post_id, $pmeta->meta_key ) ) { + delete_metadata_by_mid( 'post', $meta['id'] ); + } + } elseif ( current_user_can( 'add_post_meta', $post_id, wp_unslash( $meta['key'] ) ) ) { + add_post_meta( $post_id, $meta['key'], $meta['value'] ); + } + } + } + + /** + * Set up blog options property. + * + * Passes property through 'xmlrpc_blog_options' filter. + * + * @since 2.6.0 + */ + public function initialise_blog_option_info() { + global $wp_version; + + $this->blog_options = array( + // Read only options + 'software_name' => array( + 'desc' => __( 'Software Name' ), + 'readonly' => true, + 'value' => 'WordPress' + ), + 'software_version' => array( + 'desc' => __( 'Software Version' ), + 'readonly' => true, + 'value' => $wp_version + ), + 'blog_url' => array( + 'desc' => __( 'WordPress Address (URL)' ), + 'readonly' => true, + 'option' => 'siteurl' + ), + 'home_url' => array( + 'desc' => __( 'Site Address (URL)' ), + 'readonly' => true, + 'option' => 'home' + ), + 'login_url' => array( + 'desc' => __( 'Login Address (URL)' ), + 'readonly' => true, + 'value' => wp_login_url( ) + ), + 'admin_url' => array( + 'desc' => __( 'The URL to the admin area' ), + 'readonly' => true, + 'value' => get_admin_url( ) + ), + 'image_default_link_type' => array( + 'desc' => __( 'Image default link type' ), + 'readonly' => true, + 'option' => 'image_default_link_type' + ), + 'image_default_size' => array( + 'desc' => __( 'Image default size' ), + 'readonly' => true, + 'option' => 'image_default_size' + ), + 'image_default_align' => array( + 'desc' => __( 'Image default align' ), + 'readonly' => true, + 'option' => 'image_default_align' + ), + 'template' => array( + 'desc' => __( 'Template' ), + 'readonly' => true, + 'option' => 'template' + ), + 'stylesheet' => array( + 'desc' => __( 'Stylesheet' ), + 'readonly' => true, + 'option' => 'stylesheet' + ), + 'post_thumbnail' => array( + 'desc' => __('Post Thumbnail'), + 'readonly' => true, + 'value' => current_theme_supports( 'post-thumbnails' ) + ), + + // Updatable options + 'time_zone' => array( + 'desc' => __( 'Time Zone' ), + 'readonly' => false, + 'option' => 'gmt_offset' + ), + 'blog_title' => array( + 'desc' => __( 'Site Title' ), + 'readonly' => false, + 'option' => 'blogname' + ), + 'blog_tagline' => array( + 'desc' => __( 'Site Tagline' ), + 'readonly' => false, + 'option' => 'blogdescription' + ), + 'date_format' => array( + 'desc' => __( 'Date Format' ), + 'readonly' => false, + 'option' => 'date_format' + ), + 'time_format' => array( + 'desc' => __( 'Time Format' ), + 'readonly' => false, + 'option' => 'time_format' + ), + 'users_can_register' => array( + 'desc' => __( 'Allow new users to sign up' ), + 'readonly' => false, + 'option' => 'users_can_register' + ), + 'thumbnail_size_w' => array( + 'desc' => __( 'Thumbnail Width' ), + 'readonly' => false, + 'option' => 'thumbnail_size_w' + ), + 'thumbnail_size_h' => array( + 'desc' => __( 'Thumbnail Height' ), + 'readonly' => false, + 'option' => 'thumbnail_size_h' + ), + 'thumbnail_crop' => array( + 'desc' => __( 'Crop thumbnail to exact dimensions' ), + 'readonly' => false, + 'option' => 'thumbnail_crop' + ), + 'medium_size_w' => array( + 'desc' => __( 'Medium size image width' ), + 'readonly' => false, + 'option' => 'medium_size_w' + ), + 'medium_size_h' => array( + 'desc' => __( 'Medium size image height' ), + 'readonly' => false, + 'option' => 'medium_size_h' + ), + 'large_size_w' => array( + 'desc' => __( 'Large size image width' ), + 'readonly' => false, + 'option' => 'large_size_w' + ), + 'large_size_h' => array( + 'desc' => __( 'Large size image height' ), + 'readonly' => false, + 'option' => 'large_size_h' + ), + 'default_comment_status' => array( + 'desc' => __( 'Allow people to post comments on new articles' ), + 'readonly' => false, + 'option' => 'default_comment_status' + ), + 'default_ping_status' => array( + 'desc' => __( 'Allow link notifications from other blogs (pingbacks and trackbacks)' ), + 'readonly' => false, + 'option' => 'default_ping_status' + ) + ); + + /** + * Filter the XML-RPC blog options property. + * + * @since 2.6.0 + * + * @param array $blog_options An array of XML-RPC blog options. + */ + $this->blog_options = apply_filters( 'xmlrpc_blog_options', $this->blog_options ); + } + + /** + * Retrieve the blogs of the user. + * + * @since 2.6.0 + * + * @param array $args Method parameters. Contains: + * - username + * - password + * @return array|IXR_Error Array contains: + * - 'isAdmin' + * - 'url' + * - 'blogid' + * - 'blogName' + * - 'xmlrpc' - url of xmlrpc endpoint + */ + public function wp_getUsersBlogs( $args ) { + // If this isn't on WPMU then just use blogger_getUsersBlogs + if ( !is_multisite() ) { + array_unshift( $args, 1 ); + return $this->blogger_getUsersBlogs( $args ); + } + + $this->escape( $args ); + + $username = $args[0]; + $password = $args[1]; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + /** + * Fires after the XML-RPC user has been authenticated but before the rest of + * the method logic begins. + * + * All built-in XML-RPC methods use the action xmlrpc_call, with a parameter + * equal to the method's name, e.g., wp.getUsersBlogs, wp.newPost, etc. + * + * @since 2.5.0 + * + * @param method $name The method name. + */ + do_action( 'xmlrpc_call', 'wp.getUsersBlogs' ); + + $blogs = (array) get_blogs_of_user( $user->ID ); + $struct = array(); + + foreach ( $blogs as $blog ) { + // Don't include blogs that aren't hosted at this site + if ( $blog->site_id != get_current_site()->id ) + continue; + + $blog_id = $blog->userblog_id; + + switch_to_blog( $blog_id ); + + $is_admin = current_user_can( 'manage_options' ); + + $struct[] = array( + 'isAdmin' => $is_admin, + 'url' => home_url( '/' ), + 'blogid' => (string) $blog_id, + 'blogName' => get_option( 'blogname' ), + 'xmlrpc' => site_url( 'xmlrpc.php', 'rpc' ), + ); + + restore_current_blog(); + } + + return $struct; + } + + /** + * Checks if the method received at least the minimum number of arguments. + * + * @since 3.4.0 + * + * @param string|array $args Sanitize single string or array of strings. + * @param int $count Minimum number of arguments. + * @return boolean if $args contains at least $count arguments. + */ + protected function minimum_args( $args, $count ) { + if ( count( $args ) < $count ) { + $this->error = new IXR_Error( 400, __( 'Insufficient arguments passed to this XML-RPC method.' ) ); + return false; + } + + return true; + } + + /** + * Prepares taxonomy data for return in an XML-RPC object. + * + * @access protected + * + * @param object $taxonomy The unprepared taxonomy data + * @param array $fields The subset of taxonomy fields to return + * @return array The prepared taxonomy data + */ + protected function _prepare_taxonomy( $taxonomy, $fields ) { + $_taxonomy = array( + 'name' => $taxonomy->name, + 'label' => $taxonomy->label, + 'hierarchical' => (bool) $taxonomy->hierarchical, + 'public' => (bool) $taxonomy->public, + 'show_ui' => (bool) $taxonomy->show_ui, + '_builtin' => (bool) $taxonomy->_builtin, + ); + + if ( in_array( 'labels', $fields ) ) + $_taxonomy['labels'] = (array) $taxonomy->labels; + + if ( in_array( 'cap', $fields ) ) + $_taxonomy['cap'] = (array) $taxonomy->cap; + + if ( in_array( 'menu', $fields ) ) + $_taxonomy['show_in_menu'] = (bool) $_taxonomy->show_in_menu; + + if ( in_array( 'object_type', $fields ) ) + $_taxonomy['object_type'] = array_unique( (array) $taxonomy->object_type ); + + /** + * Filter XML-RPC-prepared data for the given taxonomy. + * + * @since 3.4.0 + * + * @param array $_taxonomy An array of taxonomy data. + * @param object $taxonomy Taxonomy object. + * @param array $fields The subset of taxonomy fields to return. + */ + return apply_filters( 'xmlrpc_prepare_taxonomy', $_taxonomy, $taxonomy, $fields ); + } + + /** + * Prepares term data for return in an XML-RPC object. + * + * @access protected + * + * @param array|object $term The unprepared term data + * @return array The prepared term data + */ + protected function _prepare_term( $term ) { + $_term = $term; + if ( ! is_array( $_term) ) + $_term = get_object_vars( $_term ); + + // For integers which may be larger than XML-RPC supports ensure we return strings. + $_term['term_id'] = strval( $_term['term_id'] ); + $_term['term_group'] = strval( $_term['term_group'] ); + $_term['term_taxonomy_id'] = strval( $_term['term_taxonomy_id'] ); + $_term['parent'] = strval( $_term['parent'] ); + + // Count we are happy to return as an integer because people really shouldn't use terms that much. + $_term['count'] = intval( $_term['count'] ); + + /** + * Filter XML-RPC-prepared data for the given term. + * + * @since 3.4.0 + * + * @param array $_term An array of term data. + * @param array|object $term Term object or array. + */ + return apply_filters( 'xmlrpc_prepare_term', $_term, $term ); + } + + /** + * Convert a WordPress date string to an IXR_Date object. + * + * @access protected + * + * @param string $date + * @return IXR_Date + */ + protected function _convert_date( $date ) { + if ( $date === '0000-00-00 00:00:00' ) { + return new IXR_Date( '00000000T00:00:00Z' ); + } + return new IXR_Date( mysql2date( 'Ymd\TH:i:s', $date, false ) ); + } + + /** + * Convert a WordPress GMT date string to an IXR_Date object. + * + * @access protected + * + * @param string $date_gmt + * @param string $date + * @return IXR_Date + */ + protected function _convert_date_gmt( $date_gmt, $date ) { + if ( $date !== '0000-00-00 00:00:00' && $date_gmt === '0000-00-00 00:00:00' ) { + return new IXR_Date( get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $date, false ), 'Ymd\TH:i:s' ) ); + } + return $this->_convert_date( $date_gmt ); + } + + /** + * Prepares post data for return in an XML-RPC object. + * + * @access protected + * + * @param array $post The unprepared post data + * @param array $fields The subset of post type fields to return + * @return array The prepared post data + */ + protected function _prepare_post( $post, $fields ) { + // holds the data for this post. built up based on $fields + $_post = array( 'post_id' => strval( $post['ID'] ) ); + + // prepare common post fields + $post_fields = array( + 'post_title' => $post['post_title'], + 'post_date' => $this->_convert_date( $post['post_date'] ), + 'post_date_gmt' => $this->_convert_date_gmt( $post['post_date_gmt'], $post['post_date'] ), + 'post_modified' => $this->_convert_date( $post['post_modified'] ), + 'post_modified_gmt' => $this->_convert_date_gmt( $post['post_modified_gmt'], $post['post_modified'] ), + 'post_status' => $post['post_status'], + 'post_type' => $post['post_type'], + 'post_name' => $post['post_name'], + 'post_author' => $post['post_author'], + 'post_password' => $post['post_password'], + 'post_excerpt' => $post['post_excerpt'], + 'post_content' => $post['post_content'], + 'post_parent' => strval( $post['post_parent'] ), + 'post_mime_type' => $post['post_mime_type'], + 'link' => post_permalink( $post['ID'] ), + 'guid' => $post['guid'], + 'menu_order' => intval( $post['menu_order'] ), + 'comment_status' => $post['comment_status'], + 'ping_status' => $post['ping_status'], + 'sticky' => ( $post['post_type'] === 'post' && is_sticky( $post['ID'] ) ), + ); + + // Thumbnail + $post_fields['post_thumbnail'] = array(); + $thumbnail_id = get_post_thumbnail_id( $post['ID'] ); + if ( $thumbnail_id ) { + $thumbnail_size = current_theme_supports('post-thumbnail') ? 'post-thumbnail' : 'thumbnail'; + $post_fields['post_thumbnail'] = $this->_prepare_media_item( get_post( $thumbnail_id ), $thumbnail_size ); + } + + // Consider future posts as published + if ( $post_fields['post_status'] === 'future' ) + $post_fields['post_status'] = 'publish'; + + // Fill in blank post format + $post_fields['post_format'] = get_post_format( $post['ID'] ); + if ( empty( $post_fields['post_format'] ) ) + $post_fields['post_format'] = 'standard'; + + // Merge requested $post_fields fields into $_post + if ( in_array( 'post', $fields ) ) { + $_post = array_merge( $_post, $post_fields ); + } else { + $requested_fields = array_intersect_key( $post_fields, array_flip( $fields ) ); + $_post = array_merge( $_post, $requested_fields ); + } + + $all_taxonomy_fields = in_array( 'taxonomies', $fields ); + + if ( $all_taxonomy_fields || in_array( 'terms', $fields ) ) { + $post_type_taxonomies = get_object_taxonomies( $post['post_type'], 'names' ); + $terms = wp_get_object_terms( $post['ID'], $post_type_taxonomies ); + $_post['terms'] = array(); + foreach ( $terms as $term ) { + $_post['terms'][] = $this->_prepare_term( $term ); + } + } + + if ( in_array( 'custom_fields', $fields ) ) + $_post['custom_fields'] = $this->get_custom_fields( $post['ID'] ); + + if ( in_array( 'enclosure', $fields ) ) { + $_post['enclosure'] = array(); + $enclosures = (array) get_post_meta( $post['ID'], 'enclosure' ); + if ( ! empty( $enclosures ) ) { + $encdata = explode( "\n", $enclosures[0] ); + $_post['enclosure']['url'] = trim( htmlspecialchars( $encdata[0] ) ); + $_post['enclosure']['length'] = (int) trim( $encdata[1] ); + $_post['enclosure']['type'] = trim( $encdata[2] ); + } + } + + /** + * Filter XML-RPC-prepared date for the given post. + * + * @since 3.4.0 + * + * @param array $_post An array of modified post data. + * @param array $post An array of post data. + * @param array $fields An array of post fields. + */ + return apply_filters( 'xmlrpc_prepare_post', $_post, $post, $fields ); + } + + /** + * Prepares post data for return in an XML-RPC object. + * + * @access protected + * + * @param object $post_type Post type object + * @param array $fields The subset of post fields to return + * @return array The prepared post type data + */ + protected function _prepare_post_type( $post_type, $fields ) { + $_post_type = array( + 'name' => $post_type->name, + 'label' => $post_type->label, + 'hierarchical' => (bool) $post_type->hierarchical, + 'public' => (bool) $post_type->public, + 'show_ui' => (bool) $post_type->show_ui, + '_builtin' => (bool) $post_type->_builtin, + 'has_archive' => (bool) $post_type->has_archive, + 'supports' => get_all_post_type_supports( $post_type->name ), + ); + + if ( in_array( 'labels', $fields ) ) { + $_post_type['labels'] = (array) $post_type->labels; + } + + if ( in_array( 'cap', $fields ) ) { + $_post_type['cap'] = (array) $post_type->cap; + $_post_type['map_meta_cap'] = (bool) $post_type->map_meta_cap; + } + + if ( in_array( 'menu', $fields ) ) { + $_post_type['menu_position'] = (int) $post_type->menu_position; + $_post_type['menu_icon'] = $post_type->menu_icon; + $_post_type['show_in_menu'] = (bool) $post_type->show_in_menu; + } + + if ( in_array( 'taxonomies', $fields ) ) + $_post_type['taxonomies'] = get_object_taxonomies( $post_type->name, 'names' ); + + /** + * Filter XML-RPC-prepared date for the given post type. + * + * @since 3.4.0 + * + * @param array $_post_type An array of post type data. + * @param object $post_type Post type object. + */ + return apply_filters( 'xmlrpc_prepare_post_type', $_post_type, $post_type ); + } + + /** + * Prepares media item data for return in an XML-RPC object. + * + * @access protected + * + * @param object $media_item The unprepared media item data + * @param string $thumbnail_size The image size to use for the thumbnail URL + * @return array The prepared media item data + */ + protected function _prepare_media_item( $media_item, $thumbnail_size = 'thumbnail' ) { + $_media_item = array( + 'attachment_id' => strval( $media_item->ID ), + 'date_created_gmt' => $this->_convert_date_gmt( $media_item->post_date_gmt, $media_item->post_date ), + 'parent' => $media_item->post_parent, + 'link' => wp_get_attachment_url( $media_item->ID ), + 'title' => $media_item->post_title, + 'caption' => $media_item->post_excerpt, + 'description' => $media_item->post_content, + 'metadata' => wp_get_attachment_metadata( $media_item->ID ), + ); + + $thumbnail_src = image_downsize( $media_item->ID, $thumbnail_size ); + if ( $thumbnail_src ) + $_media_item['thumbnail'] = $thumbnail_src[0]; + else + $_media_item['thumbnail'] = $_media_item['link']; + + /** + * Filter XML-RPC-prepared data for the given media item. + * + * @since 3.4.0 + * + * @param array $_media_item An array of media item data. + * @param object $media_item Media item object. + * @param string $thumbnail_size Image size. + */ + return apply_filters( 'xmlrpc_prepare_media_item', $_media_item, $media_item, $thumbnail_size ); + } + + /** + * Prepares page data for return in an XML-RPC object. + * + * @access protected + * + * @param object $page The unprepared page data + * @return array The prepared page data + */ + protected function _prepare_page( $page ) { + // Get all of the page content and link. + $full_page = get_extended( $page->post_content ); + $link = post_permalink( $page->ID ); + + // Get info the page parent if there is one. + $parent_title = ""; + if ( ! empty( $page->post_parent ) ) { + $parent = get_post( $page->post_parent ); + $parent_title = $parent->post_title; + } + + // Determine comment and ping settings. + $allow_comments = comments_open( $page->ID ) ? 1 : 0; + $allow_pings = pings_open( $page->ID ) ? 1 : 0; + + // Format page date. + $page_date = $this->_convert_date( $page->post_date ); + $page_date_gmt = $this->_convert_date_gmt( $page->post_date_gmt, $page->post_date ); + + // Pull the categories info together. + $categories = array(); + if ( is_object_in_taxonomy( 'page', 'category' ) ) { + foreach ( wp_get_post_categories( $page->ID ) as $cat_id ) { + $categories[] = get_cat_name( $cat_id ); + } + } + + // Get the author info. + $author = get_userdata( $page->post_author ); + + $page_template = get_page_template_slug( $page->ID ); + if ( empty( $page_template ) ) + $page_template = 'default'; + + $_page = array( + 'dateCreated' => $page_date, + 'userid' => $page->post_author, + 'page_id' => $page->ID, + 'page_status' => $page->post_status, + 'description' => $full_page['main'], + 'title' => $page->post_title, + 'link' => $link, + 'permaLink' => $link, + 'categories' => $categories, + 'excerpt' => $page->post_excerpt, + 'text_more' => $full_page['extended'], + 'mt_allow_comments' => $allow_comments, + 'mt_allow_pings' => $allow_pings, + 'wp_slug' => $page->post_name, + 'wp_password' => $page->post_password, + 'wp_author' => $author->display_name, + 'wp_page_parent_id' => $page->post_parent, + 'wp_page_parent_title' => $parent_title, + 'wp_page_order' => $page->menu_order, + 'wp_author_id' => (string) $author->ID, + 'wp_author_display_name' => $author->display_name, + 'date_created_gmt' => $page_date_gmt, + 'custom_fields' => $this->get_custom_fields( $page->ID ), + 'wp_page_template' => $page_template + ); + + /** + * Filter XML-RPC-prepared data for the given page. + * + * @since 3.4.0 + * + * @param array $_page An array of page data. + * @param WP_Post $page Page object. + */ + return apply_filters( 'xmlrpc_prepare_page', $_page, $page ); + } + + /** + * Prepares comment data for return in an XML-RPC object. + * + * @access protected + * + * @param object $comment The unprepared comment data + * @return array The prepared comment data + */ + protected function _prepare_comment( $comment ) { + // Format page date. + $comment_date_gmt = $this->_convert_date_gmt( $comment->comment_date_gmt, $comment->comment_date ); + + if ( '0' == $comment->comment_approved ) { + $comment_status = 'hold'; + } elseif ( 'spam' == $comment->comment_approved ) { + $comment_status = 'spam'; + } elseif ( '1' == $comment->comment_approved ) { + $comment_status = 'approve'; + } else { + $comment_status = $comment->comment_approved; + } + $_comment = array( + 'date_created_gmt' => $comment_date_gmt, + 'user_id' => $comment->user_id, + 'comment_id' => $comment->comment_ID, + 'parent' => $comment->comment_parent, + 'status' => $comment_status, + 'content' => $comment->comment_content, + 'link' => get_comment_link($comment), + 'post_id' => $comment->comment_post_ID, + 'post_title' => get_the_title($comment->comment_post_ID), + 'author' => $comment->comment_author, + 'author_url' => $comment->comment_author_url, + 'author_email' => $comment->comment_author_email, + 'author_ip' => $comment->comment_author_IP, + 'type' => $comment->comment_type, + ); + + /** + * Filter XML-RPC-prepared data for the given comment. + * + * @since 3.4.0 + * + * @param array $_comment An array of prepared comment data. + * @param object $comment Comment object. + */ + return apply_filters( 'xmlrpc_prepare_comment', $_comment, $comment ); + } + + /** + * Prepares user data for return in an XML-RPC object. + * + * @access protected + * + * @param WP_User $user The unprepared user object + * @param array $fields The subset of user fields to return + * @return array The prepared user data + */ + protected function _prepare_user( $user, $fields ) { + $_user = array( 'user_id' => strval( $user->ID ) ); + + $user_fields = array( + 'username' => $user->user_login, + 'first_name' => $user->user_firstname, + 'last_name' => $user->user_lastname, + 'registered' => $this->_convert_date( $user->user_registered ), + 'bio' => $user->user_description, + 'email' => $user->user_email, + 'nickname' => $user->nickname, + 'nicename' => $user->user_nicename, + 'url' => $user->user_url, + 'display_name' => $user->display_name, + 'roles' => $user->roles, + ); + + if ( in_array( 'all', $fields ) ) { + $_user = array_merge( $_user, $user_fields ); + } else { + if ( in_array( 'basic', $fields ) ) { + $basic_fields = array( 'username', 'email', 'registered', 'display_name', 'nicename' ); + $fields = array_merge( $fields, $basic_fields ); + } + $requested_fields = array_intersect_key( $user_fields, array_flip( $fields ) ); + $_user = array_merge( $_user, $requested_fields ); + } + + /** + * Filter XML-RPC-prepared data for the given user. + * + * @since 3.5.0 + * + * @param array $_user An array of user data. + * @param WP_User $user User object. + * @param array $fields An array of user fields. + */ + return apply_filters( 'xmlrpc_prepare_user', $_user, $user, $fields ); + } + + /** + * Create a new post for any registered post type. + * + * @since 3.4.0 + * + * @param array $args Method parameters. Contains: + * - int $blog_id (unused) + * - string $username + * - string $password + * - array $content_struct + * $content_struct can contain: + * - post_type (default: 'post') + * - post_status (default: 'draft') + * - post_title + * - post_author + * - post_excerpt + * - post_content + * - post_date_gmt | post_date + * - post_format + * - post_password + * - comment_status - can be 'open' | 'closed' + * - ping_status - can be 'open' | 'closed' + * - sticky + * - post_thumbnail - ID of a media item to use as the post thumbnail/featured image + * - custom_fields - array, with each element containing 'key' and 'value' + * - terms - array, with taxonomy names as keys and arrays of term IDs as values + * - terms_names - array, with taxonomy names as keys and arrays of term names as values + * - enclosure + * - any other fields supported by wp_insert_post() + * @return string|IXR_Error post_id + */ + public function wp_newPost( $args ) { + if ( ! $this->minimum_args( $args, 4 ) ) + return $this->error; + + $this->escape( $args ); + + $username = $args[1]; + $password = $args[2]; + $content_struct = $args[3]; + + if ( ! $user = $this->login( $username, $password ) ) + return $this->error; + + // convert the date field back to IXR form + if ( isset( $content_struct['post_date'] ) && ! is_a( $content_struct['post_date'], 'IXR_Date' ) ) { + $content_struct['post_date'] = $this->_convert_date( $content_struct['post_date'] ); + } + + // ignore the existing GMT date if it is empty or a non-GMT date was supplied in $content_struct, + // since _insert_post will ignore the non-GMT date if the GMT date is set + if ( isset( $content_struct['post_date_gmt'] ) && ! is_a( $content_struct['post_date_gmt'], 'IXR_Date' ) ) { + if ( $content_struct['post_date_gmt'] == '0000-00-00 00:00:00' || isset( $content_struct['post_date'] ) ) { + unset( $content_struct['post_date_gmt'] ); + } else { + $content_struct['post_date_gmt'] = $this->_convert_date( $content_struct['post_date_gmt'] ); + } + } + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.newPost' ); + + unset( $content_struct['ID'] ); + + return $this->_insert_post( $user, $content_struct ); + } + + /** + * Helper method for filtering out elements from an array. + * + * @since 3.4.0 + * + * @param int $count Number to compare to one. + */ + private function _is_greater_than_one( $count ) { + return $count > 1; + } + + /** + * Helper method for wp_newPost and wp_editPost, containing shared logic. + * + * @since 3.4.0 + * @uses wp_insert_post() + * + * @param WP_User $user The post author if post_author isn't set in $content_struct. + * @param array|IXR_Error $content_struct Post data to insert. + */ + protected function _insert_post( $user, $content_struct ) { + $defaults = array( 'post_status' => 'draft', 'post_type' => 'post', 'post_author' => 0, + 'post_password' => '', 'post_excerpt' => '', 'post_content' => '', 'post_title' => '' ); + + $post_data = wp_parse_args( $content_struct, $defaults ); + + $post_type = get_post_type_object( $post_data['post_type'] ); + if ( ! $post_type ) + return new IXR_Error( 403, __( 'Invalid post type' ) ); + + $update = ! empty( $post_data['ID'] ); + + if ( $update ) { + if ( ! get_post( $post_data['ID'] ) ) + return new IXR_Error( 401, __( 'Invalid post ID.' ) ); + if ( ! current_user_can( 'edit_post', $post_data['ID'] ) ) + return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); + if ( $post_data['post_type'] != get_post_type( $post_data['ID'] ) ) + return new IXR_Error( 401, __( 'The post type may not be changed.' ) ); + } else { + if ( ! current_user_can( $post_type->cap->create_posts ) || ! current_user_can( $post_type->cap->edit_posts ) ) + return new IXR_Error( 401, __( 'Sorry, you are not allowed to post on this site.' ) ); + } + + switch ( $post_data['post_status'] ) { + case 'draft': + case 'pending': + break; + case 'private': + if ( ! current_user_can( $post_type->cap->publish_posts ) ) + return new IXR_Error( 401, __( 'Sorry, you are not allowed to create private posts in this post type' ) ); + break; + case 'publish': + case 'future': + if ( ! current_user_can( $post_type->cap->publish_posts ) ) + return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish posts in this post type' ) ); + break; + default: + if ( ! get_post_status_object( $post_data['post_status'] ) ) + $post_data['post_status'] = 'draft'; + break; + } + + if ( ! empty( $post_data['post_password'] ) && ! current_user_can( $post_type->cap->publish_posts ) ) + return new IXR_Error( 401, __( 'Sorry, you are not allowed to create password protected posts in this post type' ) ); + + $post_data['post_author'] = absint( $post_data['post_author'] ); + if ( ! empty( $post_data['post_author'] ) && $post_data['post_author'] != $user->ID ) { + if ( ! current_user_can( $post_type->cap->edit_others_posts ) ) + return new IXR_Error( 401, __( 'You are not allowed to create posts as this user.' ) ); + + $author = get_userdata( $post_data['post_author'] ); + + if ( ! $author ) + return new IXR_Error( 404, __( 'Invalid author ID.' ) ); + } else { + $post_data['post_author'] = $user->ID; + } + + if ( isset( $post_data['comment_status'] ) && $post_data['comment_status'] != 'open' && $post_data['comment_status'] != 'closed' ) + unset( $post_data['comment_status'] ); + + if ( isset( $post_data['ping_status'] ) && $post_data['ping_status'] != 'open' && $post_data['ping_status'] != 'closed' ) + unset( $post_data['ping_status'] ); + + // Do some timestamp voodoo + if ( ! empty( $post_data['post_date_gmt'] ) ) { + // We know this is supposed to be GMT, so we're going to slap that Z on there by force + $dateCreated = rtrim( $post_data['post_date_gmt']->getIso(), 'Z' ) . 'Z'; + } elseif ( ! empty( $post_data['post_date'] ) ) { + $dateCreated = $post_data['post_date']->getIso(); + } + + if ( ! empty( $dateCreated ) ) { + $post_data['post_date'] = get_date_from_gmt( iso8601_to_datetime( $dateCreated ) ); + $post_data['post_date_gmt'] = iso8601_to_datetime( $dateCreated, 'GMT' ); + } + + if ( ! isset( $post_data['ID'] ) ) + $post_data['ID'] = get_default_post_to_edit( $post_data['post_type'], true )->ID; + $post_ID = $post_data['ID']; + + if ( $post_data['post_type'] == 'post' ) { + // Private and password-protected posts cannot be stickied. + if ( $post_data['post_status'] == 'private' || ! empty( $post_data['post_password'] ) ) { + // Error if the client tried to stick the post, otherwise, silently unstick. + if ( ! empty( $post_data['sticky'] ) ) + return new IXR_Error( 401, __( 'Sorry, you cannot stick a private post.' ) ); + if ( $update ) + unstick_post( $post_ID ); + } elseif ( isset( $post_data['sticky'] ) ) { + if ( ! current_user_can( $post_type->cap->edit_others_posts ) ) + return new IXR_Error( 401, __( 'Sorry, you are not allowed to stick this post.' ) ); + if ( $post_data['sticky'] ) + stick_post( $post_ID ); + else + unstick_post( $post_ID ); + } + } + + if ( isset( $post_data['post_thumbnail'] ) ) { + // empty value deletes, non-empty value adds/updates + if ( ! $post_data['post_thumbnail'] ) + delete_post_thumbnail( $post_ID ); + elseif ( ! get_post( absint( $post_data['post_thumbnail'] ) ) ) + return new IXR_Error( 404, __( 'Invalid attachment ID.' ) ); + set_post_thumbnail( $post_ID, $post_data['post_thumbnail'] ); + unset( $content_struct['post_thumbnail'] ); + } + + if ( isset( $post_data['custom_fields'] ) ) + $this->set_custom_fields( $post_ID, $post_data['custom_fields'] ); + + if ( isset( $post_data['terms'] ) || isset( $post_data['terms_names'] ) ) { + $post_type_taxonomies = get_object_taxonomies( $post_data['post_type'], 'objects' ); + + // accumulate term IDs from terms and terms_names + $terms = array(); + + // first validate the terms specified by ID + if ( isset( $post_data['terms'] ) && is_array( $post_data['terms'] ) ) { + $taxonomies = array_keys( $post_data['terms'] ); + + // validating term ids + foreach ( $taxonomies as $taxonomy ) { + if ( ! array_key_exists( $taxonomy , $post_type_taxonomies ) ) + return new IXR_Error( 401, __( 'Sorry, one of the given taxonomies is not supported by the post type.' ) ); + + if ( ! current_user_can( $post_type_taxonomies[$taxonomy]->cap->assign_terms ) ) + return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign a term to one of the given taxonomies.' ) ); + + $term_ids = $post_data['terms'][$taxonomy]; + $terms[ $taxonomy ] = array(); + foreach ( $term_ids as $term_id ) { + $term = get_term_by( 'id', $term_id, $taxonomy ); + + if ( ! $term ) + return new IXR_Error( 403, __( 'Invalid term ID' ) ); + + $terms[$taxonomy][] = (int) $term_id; + } + } + } + + // now validate terms specified by name + if ( isset( $post_data['terms_names'] ) && is_array( $post_data['terms_names'] ) ) { + $taxonomies = array_keys( $post_data['terms_names'] ); + + foreach ( $taxonomies as $taxonomy ) { + if ( ! array_key_exists( $taxonomy , $post_type_taxonomies ) ) + return new IXR_Error( 401, __( 'Sorry, one of the given taxonomies is not supported by the post type.' ) ); + + if ( ! current_user_can( $post_type_taxonomies[$taxonomy]->cap->assign_terms ) ) + return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign a term to one of the given taxonomies.' ) ); + + // for hierarchical taxonomies, we can't assign a term when multiple terms in the hierarchy share the same name + $ambiguous_terms = array(); + if ( is_taxonomy_hierarchical( $taxonomy ) ) { + $tax_term_names = get_terms( $taxonomy, array( 'fields' => 'names', 'hide_empty' => false ) ); + + // count the number of terms with the same name + $tax_term_names_count = array_count_values( $tax_term_names ); + + // filter out non-ambiguous term names + $ambiguous_tax_term_counts = array_filter( $tax_term_names_count, array( $this, '_is_greater_than_one') ); + + $ambiguous_terms = array_keys( $ambiguous_tax_term_counts ); + } + + $term_names = $post_data['terms_names'][$taxonomy]; + foreach ( $term_names as $term_name ) { + if ( in_array( $term_name, $ambiguous_terms ) ) + return new IXR_Error( 401, __( 'Ambiguous term name used in a hierarchical taxonomy. Please use term ID instead.' ) ); + + $term = get_term_by( 'name', $term_name, $taxonomy ); + + if ( ! $term ) { + // term doesn't exist, so check that the user is allowed to create new terms + if ( ! current_user_can( $post_type_taxonomies[$taxonomy]->cap->edit_terms ) ) + return new IXR_Error( 401, __( 'Sorry, you are not allowed to add a term to one of the given taxonomies.' ) ); + + // create the new term + $term_info = wp_insert_term( $term_name, $taxonomy ); + if ( is_wp_error( $term_info ) ) + return new IXR_Error( 500, $term_info->get_error_message() ); + + $terms[$taxonomy][] = (int) $term_info['term_id']; + } else { + $terms[$taxonomy][] = (int) $term->term_id; + } + } + } + } + + $post_data['tax_input'] = $terms; + unset( $post_data['terms'], $post_data['terms_names'] ); + } else { + // do not allow direct submission of 'tax_input', clients must use 'terms' and/or 'terms_names' + unset( $post_data['tax_input'], $post_data['post_category'], $post_data['tags_input'] ); + } + + if ( isset( $post_data['post_format'] ) ) { + $format = set_post_format( $post_ID, $post_data['post_format'] ); + + if ( is_wp_error( $format ) ) + return new IXR_Error( 500, $format->get_error_message() ); + + unset( $post_data['post_format'] ); + } + + // Handle enclosures + $enclosure = isset( $post_data['enclosure'] ) ? $post_data['enclosure'] : null; + $this->add_enclosure_if_new( $post_ID, $enclosure ); + + $this->attach_uploads( $post_ID, $post_data['post_content'] ); + + /** + * Filter post data array to be inserted via XML-RPC. + * + * @since 3.4.0 + * + * @param array $post_data Parsed array of post data. + * @param array $content_struct Post data array. + */ + $post_data = apply_filters( 'xmlrpc_wp_insert_post_data', $post_data, $content_struct ); + + $post_ID = $update ? wp_update_post( $post_data, true ) : wp_insert_post( $post_data, true ); + if ( is_wp_error( $post_ID ) ) + return new IXR_Error( 500, $post_ID->get_error_message() ); + + if ( ! $post_ID ) + return new IXR_Error( 401, __( 'Sorry, your entry could not be posted. Something wrong happened.' ) ); + + return strval( $post_ID ); + } + + /** + * Edit a post for any registered post type. + * + * The $content_struct parameter only needs to contain fields that + * should be changed. All other fields will retain their existing values. + * + * @since 3.4.0 + * + * @param array $args Method parameters. Contains: + * - int $blog_id (unused) + * - string $username + * - string $password + * - int $post_id + * - array $content_struct + * @return bool|IXR_Error true on success + */ + public function wp_editPost( $args ) { + if ( ! $this->minimum_args( $args, 5 ) ) + return $this->error; + + $this->escape( $args ); + + $username = $args[1]; + $password = $args[2]; + $post_id = (int) $args[3]; + $content_struct = $args[4]; + + if ( ! $user = $this->login( $username, $password ) ) + return $this->error; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.editPost' ); + + $post = get_post( $post_id, ARRAY_A ); + + if ( empty( $post['ID'] ) ) + return new IXR_Error( 404, __( 'Invalid post ID.' ) ); + + if ( isset( $content_struct['if_not_modified_since'] ) ) { + // If the post has been modified since the date provided, return an error. + if ( mysql2date( 'U', $post['post_modified_gmt'] ) > $content_struct['if_not_modified_since']->getTimestamp() ) { + return new IXR_Error( 409, __( 'There is a revision of this post that is more recent.' ) ); + } + } + + // convert the date field back to IXR form + $post['post_date'] = $this->_convert_date( $post['post_date'] ); + + // ignore the existing GMT date if it is empty or a non-GMT date was supplied in $content_struct, + // since _insert_post will ignore the non-GMT date if the GMT date is set + if ( $post['post_date_gmt'] == '0000-00-00 00:00:00' || isset( $content_struct['post_date'] ) ) + unset( $post['post_date_gmt'] ); + else + $post['post_date_gmt'] = $this->_convert_date( $post['post_date_gmt'] ); + + $this->escape( $post ); + $merged_content_struct = array_merge( $post, $content_struct ); + + $retval = $this->_insert_post( $user, $merged_content_struct ); + if ( $retval instanceof IXR_Error ) + return $retval; + + return true; + } + + /** + * Delete a post for any registered post type. + * + * @since 3.4.0 + * + * @uses wp_delete_post() + * @param array $args Method parameters. Contains: + * - int $blog_id (unused) + * - string $username + * - string $password + * - int $post_id + * @return bool|IXR_Error true on success + */ + public function wp_deletePost( $args ) { + if ( ! $this->minimum_args( $args, 4 ) ) + return $this->error; + + $this->escape( $args ); + + $username = $args[1]; + $password = $args[2]; + $post_id = (int) $args[3]; + + if ( ! $user = $this->login( $username, $password ) ) + return $this->error; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.deletePost' ); + + $post = get_post( $post_id, ARRAY_A ); + if ( empty( $post['ID'] ) ) + return new IXR_Error( 404, __( 'Invalid post ID.' ) ); + + if ( ! current_user_can( 'delete_post', $post_id ) ) + return new IXR_Error( 401, __( 'Sorry, you are not allowed to delete this post.' ) ); + + $result = wp_delete_post( $post_id ); + + if ( ! $result ) + return new IXR_Error( 500, __( 'The post cannot be deleted.' ) ); + + return true; + } + + /** + * Retrieve a post. + * + * @since 3.4.0 + * + * The optional $fields parameter specifies what fields will be included + * in the response array. This should be a list of field names. 'post_id' will + * always be included in the response regardless of the value of $fields. + * + * Instead of, or in addition to, individual field names, conceptual group + * names can be used to specify multiple fields. The available conceptual + * groups are 'post' (all basic fields), 'taxonomies', 'custom_fields', + * and 'enclosure'. + * + * @uses get_post() + * @param array $args Method parameters. Contains: + * - int $blog_id (unset) + * - string $username + * - string $password + * - int $post_id + * - array $fields optional + * @return array|IXR_Error Array contains (based on $fields parameter): + * - 'post_id' + * - 'post_title' + * - 'post_date' + * - 'post_date_gmt' + * - 'post_modified' + * - 'post_modified_gmt' + * - 'post_status' + * - 'post_type' + * - 'post_name' + * - 'post_author' + * - 'post_password' + * - 'post_excerpt' + * - 'post_content' + * - 'link' + * - 'comment_status' + * - 'ping_status' + * - 'sticky' + * - 'custom_fields' + * - 'terms' + * - 'categories' + * - 'tags' + * - 'enclosure' + */ + public function wp_getPost( $args ) { + if ( ! $this->minimum_args( $args, 4 ) ) + return $this->error; + + $this->escape( $args ); + + $username = $args[1]; + $password = $args[2]; + $post_id = (int) $args[3]; + + if ( isset( $args[4] ) ) { + $fields = $args[4]; + } else { + /** + * Filter the list of post query fields used by the given XML-RPC method. + * + * @since 3.4.0 + * + * @param array $fields Array of post fields. + * @param string $method Method name. + */ + $fields = apply_filters( 'xmlrpc_default_post_fields', array( 'post', 'terms', 'custom_fields' ), 'wp.getPost' ); + } + + if ( ! $user = $this->login( $username, $password ) ) + return $this->error; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.getPost' ); + + $post = get_post( $post_id, ARRAY_A ); + + if ( empty( $post['ID'] ) ) + return new IXR_Error( 404, __( 'Invalid post ID.' ) ); + + if ( ! current_user_can( 'edit_post', $post_id ) ) + return new IXR_Error( 401, __( 'Sorry, you cannot edit this post.' ) ); + + return $this->_prepare_post( $post, $fields ); + } + + /** + * Retrieve posts. + * + * @since 3.4.0 + * + * The optional $filter parameter modifies the query used to retrieve posts. + * Accepted keys are 'post_type', 'post_status', 'number', 'offset', + * 'orderby', and 'order'. + * + * The optional $fields parameter specifies what fields will be included + * in the response array. + * + * @uses wp_get_recent_posts() + * @see wp_getPost() for more on $fields + * @see get_posts() for more on $filter values + * + * @param array $args Method parameters. Contains: + * - int $blog_id (unused) + * - string $username + * - string $password + * - array $filter optional + * - array $fields optional + * @return array|IXR_Error Array contains a collection of posts. + */ + public function wp_getPosts( $args ) { + if ( ! $this->minimum_args( $args, 3 ) ) + return $this->error; + + $this->escape( $args ); + + $username = $args[1]; + $password = $args[2]; + $filter = isset( $args[3] ) ? $args[3] : array(); + + if ( isset( $args[4] ) ) { + $fields = $args[4]; + } else { + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + $fields = apply_filters( 'xmlrpc_default_post_fields', array( 'post', 'terms', 'custom_fields' ), 'wp.getPosts' ); + } + + if ( ! $user = $this->login( $username, $password ) ) + return $this->error; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.getPosts' ); + + $query = array(); + + if ( isset( $filter['post_type'] ) ) { + $post_type = get_post_type_object( $filter['post_type'] ); + if ( ! ( (bool) $post_type ) ) + return new IXR_Error( 403, __( 'The post type specified is not valid' ) ); + } else { + $post_type = get_post_type_object( 'post' ); + } + + if ( ! current_user_can( $post_type->cap->edit_posts ) ) + return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts in this post type' )); + + $query['post_type'] = $post_type->name; + + if ( isset( $filter['post_status'] ) ) + $query['post_status'] = $filter['post_status']; + + if ( isset( $filter['number'] ) ) + $query['numberposts'] = absint( $filter['number'] ); + + if ( isset( $filter['offset'] ) ) + $query['offset'] = absint( $filter['offset'] ); + + if ( isset( $filter['orderby'] ) ) { + $query['orderby'] = $filter['orderby']; + + if ( isset( $filter['order'] ) ) + $query['order'] = $filter['order']; + } + + if ( isset( $filter['s'] ) ) { + $query['s'] = $filter['s']; + } + + $posts_list = wp_get_recent_posts( $query ); + + if ( ! $posts_list ) + return array(); + + // holds all the posts data + $struct = array(); + + foreach ( $posts_list as $post ) { + if ( ! current_user_can( 'edit_post', $post['ID'] ) ) + continue; + + $struct[] = $this->_prepare_post( $post, $fields ); + } + + return $struct; + } + + /** + * Create a new term. + * + * @since 3.4.0 + * + * @uses wp_insert_term() + * @param array $args Method parameters. Contains: + * - int $blog_id (unused) + * - string $username + * - string $password + * - array $content_struct + * The $content_struct must contain: + * - 'name' + * - 'taxonomy' + * Also, it can optionally contain: + * - 'parent' + * - 'description' + * - 'slug' + * @return string|IXR_Error term_id + */ + public function wp_newTerm( $args ) { + if ( ! $this->minimum_args( $args, 4 ) ) + return $this->error; + + $this->escape( $args ); + + $username = $args[1]; + $password = $args[2]; + $content_struct = $args[3]; + + if ( ! $user = $this->login( $username, $password ) ) + return $this->error; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.newTerm' ); + + if ( ! taxonomy_exists( $content_struct['taxonomy'] ) ) + return new IXR_Error( 403, __( 'Invalid taxonomy' ) ); + + $taxonomy = get_taxonomy( $content_struct['taxonomy'] ); + + if ( ! current_user_can( $taxonomy->cap->manage_terms ) ) + return new IXR_Error( 401, __( 'You are not allowed to create terms in this taxonomy.' ) ); + + $taxonomy = (array) $taxonomy; + + // hold the data of the term + $term_data = array(); + + $term_data['name'] = trim( $content_struct['name'] ); + if ( empty( $term_data['name'] ) ) + return new IXR_Error( 403, __( 'The term name cannot be empty.' ) ); + + if ( isset( $content_struct['parent'] ) ) { + if ( ! $taxonomy['hierarchical'] ) + return new IXR_Error( 403, __( 'This taxonomy is not hierarchical.' ) ); + + $parent_term_id = (int) $content_struct['parent']; + $parent_term = get_term( $parent_term_id , $taxonomy['name'] ); + + if ( is_wp_error( $parent_term ) ) + return new IXR_Error( 500, $parent_term->get_error_message() ); + + if ( ! $parent_term ) + return new IXR_Error( 403, __( 'Parent term does not exist.' ) ); + + $term_data['parent'] = $content_struct['parent']; + } + + if ( isset( $content_struct['description'] ) ) + $term_data['description'] = $content_struct['description']; + + if ( isset( $content_struct['slug'] ) ) + $term_data['slug'] = $content_struct['slug']; + + $term = wp_insert_term( $term_data['name'] , $taxonomy['name'] , $term_data ); + + if ( is_wp_error( $term ) ) + return new IXR_Error( 500, $term->get_error_message() ); + + if ( ! $term ) + return new IXR_Error( 500, __( 'Sorry, your term could not be created. Something wrong happened.' ) ); + + return strval( $term['term_id'] ); + } + + /** + * Edit a term. + * + * @since 3.4.0 + * + * @uses wp_update_term() + * @param array $args Method parameters. Contains: + * - int $blog_id (unused) + * - string $username + * - string $password + * - string $term_id + * - array $content_struct + * The $content_struct must contain: + * - 'taxonomy' + * Also, it can optionally contain: + * - 'name' + * - 'parent' + * - 'description' + * - 'slug' + * @return bool|IXR_Error True, on success. + */ + public function wp_editTerm( $args ) { + if ( ! $this->minimum_args( $args, 5 ) ) + return $this->error; + + $this->escape( $args ); + + $username = $args[1]; + $password = $args[2]; + $term_id = (int) $args[3]; + $content_struct = $args[4]; + + if ( ! $user = $this->login( $username, $password ) ) + return $this->error; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.editTerm' ); + + if ( ! taxonomy_exists( $content_struct['taxonomy'] ) ) + return new IXR_Error( 403, __( 'Invalid taxonomy' ) ); + + $taxonomy = get_taxonomy( $content_struct['taxonomy'] ); + + if ( ! current_user_can( $taxonomy->cap->edit_terms ) ) + return new IXR_Error( 401, __( 'You are not allowed to edit terms in this taxonomy.' ) ); + + $taxonomy = (array) $taxonomy; + + // hold the data of the term + $term_data = array(); + + $term = get_term( $term_id , $content_struct['taxonomy'] ); + + if ( is_wp_error( $term ) ) + return new IXR_Error( 500, $term->get_error_message() ); + + if ( ! $term ) + return new IXR_Error( 404, __( 'Invalid term ID' ) ); + + if ( isset( $content_struct['name'] ) ) { + $term_data['name'] = trim( $content_struct['name'] ); + + if ( empty( $term_data['name'] ) ) + return new IXR_Error( 403, __( 'The term name cannot be empty.' ) ); + } + + if ( isset( $content_struct['parent'] ) ) { + if ( ! $taxonomy['hierarchical'] ) + return new IXR_Error( 403, __( "This taxonomy is not hierarchical so you can't set a parent." ) ); + + $parent_term_id = (int) $content_struct['parent']; + $parent_term = get_term( $parent_term_id , $taxonomy['name'] ); + + if ( is_wp_error( $parent_term ) ) + return new IXR_Error( 500, $parent_term->get_error_message() ); + + if ( ! $parent_term ) + return new IXR_Error( 403, __( 'Parent term does not exist.' ) ); + + $term_data['parent'] = $content_struct['parent']; + } + + if ( isset( $content_struct['description'] ) ) + $term_data['description'] = $content_struct['description']; + + if ( isset( $content_struct['slug'] ) ) + $term_data['slug'] = $content_struct['slug']; + + $term = wp_update_term( $term_id , $taxonomy['name'] , $term_data ); + + if ( is_wp_error( $term ) ) + return new IXR_Error( 500, $term->get_error_message() ); + + if ( ! $term ) + return new IXR_Error( 500, __( 'Sorry, editing the term failed.' ) ); + + return true; + } + + /** + * Delete a term. + * + * @since 3.4.0 + * + * @uses wp_delete_term() + * @param array $args Method parameters. Contains: + * - int $blog_id (unused) + * - string $username + * - string $password + * - string $taxnomy_name + * - string $term_id + * @return boolean|IXR_Error If it suceeded true else a reason why not + */ + public function wp_deleteTerm( $args ) { + if ( ! $this->minimum_args( $args, 5 ) ) + return $this->error; + + $this->escape( $args ); + + $username = $args[1]; + $password = $args[2]; + $taxonomy = $args[3]; + $term_id = (int) $args[4]; + + if ( ! $user = $this->login( $username, $password ) ) + return $this->error; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.deleteTerm' ); + + if ( ! taxonomy_exists( $taxonomy ) ) + return new IXR_Error( 403, __( 'Invalid taxonomy' ) ); + + $taxonomy = get_taxonomy( $taxonomy ); + + if ( ! current_user_can( $taxonomy->cap->delete_terms ) ) + return new IXR_Error( 401, __( 'You are not allowed to delete terms in this taxonomy.' ) ); + + $term = get_term( $term_id, $taxonomy->name ); + + if ( is_wp_error( $term ) ) + return new IXR_Error( 500, $term->get_error_message() ); + + if ( ! $term ) + return new IXR_Error( 404, __( 'Invalid term ID' ) ); + + $result = wp_delete_term( $term_id, $taxonomy->name ); + + if ( is_wp_error( $result ) ) + return new IXR_Error( 500, $term->get_error_message() ); + + if ( ! $result ) + return new IXR_Error( 500, __( 'Sorry, deleting the term failed.' ) ); + + return $result; + } + + /** + * Retrieve a term. + * + * @since 3.4.0 + * + * @uses get_term() + * @param array $args Method parameters. Contains: + * - int $blog_id (unused) + * - string $username + * - string $password + * - string $taxonomy + * - string $term_id + * @return array|IXR_Error Array contains: + * - 'term_id' + * - 'name' + * - 'slug' + * - 'term_group' + * - 'term_taxonomy_id' + * - 'taxonomy' + * - 'description' + * - 'parent' + * - 'count' + */ + public function wp_getTerm( $args ) { + if ( ! $this->minimum_args( $args, 5 ) ) + return $this->error; + + $this->escape( $args ); + + $username = $args[1]; + $password = $args[2]; + $taxonomy = $args[3]; + $term_id = (int) $args[4]; + + if ( ! $user = $this->login( $username, $password ) ) + return $this->error; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.getTerm' ); + + if ( ! taxonomy_exists( $taxonomy ) ) + return new IXR_Error( 403, __( 'Invalid taxonomy' ) ); + + $taxonomy = get_taxonomy( $taxonomy ); + + if ( ! current_user_can( $taxonomy->cap->assign_terms ) ) + return new IXR_Error( 401, __( 'You are not allowed to assign terms in this taxonomy.' ) ); + + $term = get_term( $term_id , $taxonomy->name, ARRAY_A ); + + if ( is_wp_error( $term ) ) + return new IXR_Error( 500, $term->get_error_message() ); + + if ( ! $term ) + return new IXR_Error( 404, __( 'Invalid term ID' ) ); + + return $this->_prepare_term( $term ); + } + + /** + * Retrieve all terms for a taxonomy. + * + * @since 3.4.0 + * + * The optional $filter parameter modifies the query used to retrieve terms. + * Accepted keys are 'number', 'offset', 'orderby', 'order', 'hide_empty', and 'search'. + * + * @uses get_terms() + * @param array $args Method parameters. Contains: + * - int $blog_id (unused) + * - string $username + * - string $password + * - string $taxonomy + * - array $filter optional + * @return array|IXR_Error terms + */ + public function wp_getTerms( $args ) { + if ( ! $this->minimum_args( $args, 4 ) ) + return $this->error; + + $this->escape( $args ); + + $username = $args[1]; + $password = $args[2]; + $taxonomy = $args[3]; + $filter = isset( $args[4] ) ? $args[4] : array(); + + if ( ! $user = $this->login( $username, $password ) ) + return $this->error; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.getTerms' ); + + if ( ! taxonomy_exists( $taxonomy ) ) + return new IXR_Error( 403, __( 'Invalid taxonomy' ) ); + + $taxonomy = get_taxonomy( $taxonomy ); + + if ( ! current_user_can( $taxonomy->cap->assign_terms ) ) + return new IXR_Error( 401, __( 'You are not allowed to assign terms in this taxonomy.' ) ); + + $query = array(); + + if ( isset( $filter['number'] ) ) + $query['number'] = absint( $filter['number'] ); + + if ( isset( $filter['offset'] ) ) + $query['offset'] = absint( $filter['offset'] ); + + if ( isset( $filter['orderby'] ) ) { + $query['orderby'] = $filter['orderby']; + + if ( isset( $filter['order'] ) ) + $query['order'] = $filter['order']; + } + + if ( isset( $filter['hide_empty'] ) ) + $query['hide_empty'] = $filter['hide_empty']; + else + $query['get'] = 'all'; + + if ( isset( $filter['search'] ) ) + $query['search'] = $filter['search']; + + $terms = get_terms( $taxonomy->name, $query ); + + if ( is_wp_error( $terms ) ) + return new IXR_Error( 500, $terms->get_error_message() ); + + $struct = array(); + + foreach ( $terms as $term ) { + $struct[] = $this->_prepare_term( $term ); + } + + return $struct; + } + + /** + * Retrieve a taxonomy. + * + * @since 3.4.0 + * + * @uses get_taxonomy() + * @param array $args Method parameters. Contains: + * - int $blog_id (unused) + * - string $username + * - string $password + * - string $taxonomy + * @return array|IXR_Error (@see get_taxonomy()) + */ + public function wp_getTaxonomy( $args ) { + if ( ! $this->minimum_args( $args, 4 ) ) + return $this->error; + + $this->escape( $args ); + + $username = $args[1]; + $password = $args[2]; + $taxonomy = $args[3]; + + if ( isset( $args[4] ) ) { + $fields = $args[4]; + } else { + /** + * Filter the taxonomy query fields used by the given XML-RPC method. + * + * @since 3.4.0 + * + * @param array $fields An array of taxonomy fields to retrieve. + * @param string $method The method name. + */ + $fields = apply_filters( 'xmlrpc_default_taxonomy_fields', array( 'labels', 'cap', 'object_type' ), 'wp.getTaxonomy' ); + } + + if ( ! $user = $this->login( $username, $password ) ) + return $this->error; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.getTaxonomy' ); + + if ( ! taxonomy_exists( $taxonomy ) ) + return new IXR_Error( 403, __( 'Invalid taxonomy' ) ); + + $taxonomy = get_taxonomy( $taxonomy ); + + if ( ! current_user_can( $taxonomy->cap->assign_terms ) ) + return new IXR_Error( 401, __( 'You are not allowed to assign terms in this taxonomy.' ) ); + + return $this->_prepare_taxonomy( $taxonomy, $fields ); + } + + /** + * Retrieve all taxonomies. + * + * @since 3.4.0 + * + * @uses get_taxonomies() + * @param array $args Method parameters. Contains: + * - int $blog_id (unused) + * - string $username + * - string $password + * @return array taxonomies + */ + public function wp_getTaxonomies( $args ) { + if ( ! $this->minimum_args( $args, 3 ) ) + return $this->error; + + $this->escape( $args ); + + $username = $args[1]; + $password = $args[2]; + $filter = isset( $args[3] ) ? $args[3] : array( 'public' => true ); + + if ( isset( $args[4] ) ) { + $fields = $args[4]; + } else { + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + $fields = apply_filters( 'xmlrpc_default_taxonomy_fields', array( 'labels', 'cap', 'object_type' ), 'wp.getTaxonomies' ); + } + + if ( ! $user = $this->login( $username, $password ) ) + return $this->error; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.getTaxonomies' ); + + $taxonomies = get_taxonomies( $filter, 'objects' ); + + // holds all the taxonomy data + $struct = array(); + + foreach ( $taxonomies as $taxonomy ) { + // capability check for post_types + if ( ! current_user_can( $taxonomy->cap->assign_terms ) ) + continue; + + $struct[] = $this->_prepare_taxonomy( $taxonomy, $fields ); + } + + return $struct; + } + + /** + * Retrieve a user. + * + * The optional $fields parameter specifies what fields will be included + * in the response array. This should be a list of field names. 'user_id' will + * always be included in the response regardless of the value of $fields. + * + * Instead of, or in addition to, individual field names, conceptual group + * names can be used to specify multiple fields. The available conceptual + * groups are 'basic' and 'all'. + * + * @uses get_userdata() + * @param array $args Method parameters. Contains: + * - int $blog_id (unused) + * - string $username + * - string $password + * - int $user_id + * - array $fields optional + * @return array|IXR_Error Array contains (based on $fields parameter): + * - 'user_id' + * - 'username' + * - 'first_name' + * - 'last_name' + * - 'registered' + * - 'bio' + * - 'email' + * - 'nickname' + * - 'nicename' + * - 'url' + * - 'display_name' + * - 'roles' + */ + public function wp_getUser( $args ) { + if ( ! $this->minimum_args( $args, 4 ) ) + return $this->error; + + $this->escape( $args ); + + $username = $args[1]; + $password = $args[2]; + $user_id = (int) $args[3]; + + if ( isset( $args[4] ) ) { + $fields = $args[4]; + } else { + /** + * Filter the default user query fields used by the given XML-RPC method. + * + * @since 3.5.0 + * + * @param array $fields User query fields for given method. Default 'all'. + * @param string $method The method name. + */ + $fields = apply_filters( 'xmlrpc_default_user_fields', array( 'all' ), 'wp.getUser' ); + } + + if ( ! $user = $this->login( $username, $password ) ) + return $this->error; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.getUser' ); + + if ( ! current_user_can( 'edit_user', $user_id ) ) + return new IXR_Error( 401, __( 'Sorry, you cannot edit users.' ) ); + + $user_data = get_userdata( $user_id ); + + if ( ! $user_data ) + return new IXR_Error( 404, __( 'Invalid user ID' ) ); + + return $this->_prepare_user( $user_data, $fields ); + } + + /** + * Retrieve users. + * + * The optional $filter parameter modifies the query used to retrieve users. + * Accepted keys are 'number' (default: 50), 'offset' (default: 0), 'role', + * 'who', 'orderby', and 'order'. + * + * The optional $fields parameter specifies what fields will be included + * in the response array. + * + * @uses get_users() + * @see wp_getUser() for more on $fields and return values + * + * @param array $args Method parameters. Contains: + * - int $blog_id (unused) + * - string $username + * - string $password + * - array $filter optional + * - array $fields optional + * @return array|IXR_Error users data + */ + public function wp_getUsers( $args ) { + if ( ! $this->minimum_args( $args, 3 ) ) + return $this->error; + + $this->escape( $args ); + + $username = $args[1]; + $password = $args[2]; + $filter = isset( $args[3] ) ? $args[3] : array(); + + if ( isset( $args[4] ) ) { + $fields = $args[4]; + } else { + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + $fields = apply_filters( 'xmlrpc_default_user_fields', array( 'all' ), 'wp.getUsers' ); + } + + if ( ! $user = $this->login( $username, $password ) ) + return $this->error; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.getUsers' ); + + if ( ! current_user_can( 'list_users' ) ) + return new IXR_Error( 401, __( 'Sorry, you cannot list users.' ) ); + + $query = array( 'fields' => 'all_with_meta' ); + + $query['number'] = ( isset( $filter['number'] ) ) ? absint( $filter['number'] ) : 50; + $query['offset'] = ( isset( $filter['offset'] ) ) ? absint( $filter['offset'] ) : 0; + + if ( isset( $filter['orderby'] ) ) { + $query['orderby'] = $filter['orderby']; + + if ( isset( $filter['order'] ) ) + $query['order'] = $filter['order']; + } + + if ( isset( $filter['role'] ) ) { + if ( get_role( $filter['role'] ) === null ) + return new IXR_Error( 403, __( 'The role specified is not valid' ) ); + + $query['role'] = $filter['role']; + } + + if ( isset( $filter['who'] ) ) { + $query['who'] = $filter['who']; + } + + $users = get_users( $query ); + + $_users = array(); + foreach ( $users as $user_data ) { + if ( current_user_can( 'edit_user', $user_data->ID ) ) + $_users[] = $this->_prepare_user( $user_data, $fields ); + } + return $_users; + } + + /** + * Retrieve information about the requesting user. + * + * @uses get_userdata() + * @param array $args Method parameters. Contains: + * - int $blog_id (unused) + * - string $username + * - string $password + * - array $fields optional + * @return array|IXR_Error (@see wp_getUser) + */ + public function wp_getProfile( $args ) { + if ( ! $this->minimum_args( $args, 3 ) ) + return $this->error; + + $this->escape( $args ); + + $username = $args[1]; + $password = $args[2]; + + if ( isset( $args[3] ) ) { + $fields = $args[3]; + } else { + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + $fields = apply_filters( 'xmlrpc_default_user_fields', array( 'all' ), 'wp.getProfile' ); + } + + if ( ! $user = $this->login( $username, $password ) ) + return $this->error; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.getProfile' ); + + if ( ! current_user_can( 'edit_user', $user->ID ) ) + return new IXR_Error( 401, __( 'Sorry, you cannot edit your profile.' ) ); + + $user_data = get_userdata( $user->ID ); + + return $this->_prepare_user( $user_data, $fields ); + } + + /** + * Edit user's profile. + * + * @uses wp_update_user() + * @param array $args Method parameters. Contains: + * - int $blog_id (unused) + * - string $username + * - string $password + * - array $content_struct + * It can optionally contain: + * - 'first_name' + * - 'last_name' + * - 'website' + * - 'display_name' + * - 'nickname' + * - 'nicename' + * - 'bio' + * @return bool|IXR_Error True, on success. + */ + public function wp_editProfile( $args ) { + if ( ! $this->minimum_args( $args, 4 ) ) + return $this->error; + + $this->escape( $args ); + + $username = $args[1]; + $password = $args[2]; + $content_struct = $args[3]; + + if ( ! $user = $this->login( $username, $password ) ) + return $this->error; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.editProfile' ); + + if ( ! current_user_can( 'edit_user', $user->ID ) ) + return new IXR_Error( 401, __( 'Sorry, you cannot edit your profile.' ) ); + + // holds data of the user + $user_data = array(); + $user_data['ID'] = $user->ID; + + // only set the user details if it was given + if ( isset( $content_struct['first_name'] ) ) + $user_data['first_name'] = $content_struct['first_name']; + + if ( isset( $content_struct['last_name'] ) ) + $user_data['last_name'] = $content_struct['last_name']; + + if ( isset( $content_struct['url'] ) ) + $user_data['user_url'] = $content_struct['url']; + + if ( isset( $content_struct['display_name'] ) ) + $user_data['display_name'] = $content_struct['display_name']; + + if ( isset( $content_struct['nickname'] ) ) + $user_data['nickname'] = $content_struct['nickname']; + + if ( isset( $content_struct['nicename'] ) ) + $user_data['user_nicename'] = $content_struct['nicename']; + + if ( isset( $content_struct['bio'] ) ) + $user_data['description'] = $content_struct['bio']; + + $result = wp_update_user( $user_data ); + + if ( is_wp_error( $result ) ) + return new IXR_Error( 500, $result->get_error_message() ); + + if ( ! $result ) + return new IXR_Error( 500, __( 'Sorry, the user cannot be updated.' ) ); + + return true; + } + + /** + * Retrieve page. + * + * @since 2.2.0 + * + * @param array $args Method parameters. Contains: + * - blog_id (unused) + * - page_id + * - username + * - password + * @return array|IXR_Error + */ + public function wp_getPage($args) { + $this->escape($args); + + $page_id = (int) $args[1]; + $username = $args[2]; + $password = $args[3]; + + if ( !$user = $this->login($username, $password) ) { + return $this->error; + } + + $page = get_post($page_id); + if ( ! $page ) + return new IXR_Error( 404, __( 'Invalid post ID.' ) ); + + if ( !current_user_can( 'edit_page', $page_id ) ) + return new IXR_Error( 401, __( 'Sorry, you cannot edit this page.' ) ); + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.getPage' ); + + // If we found the page then format the data. + if ( $page->ID && ($page->post_type == 'page') ) { + return $this->_prepare_page( $page ); + } + // If the page doesn't exist indicate that. + else { + return new IXR_Error( 404, __( 'Sorry, no such page.' ) ); + } + } + + /** + * Retrieve Pages. + * + * @since 2.2.0 + * + * @param array $args Method parameters. Contains: + * - blog_id (unused) + * - username + * - password + * - num_pages + * @return array|IXR_Error + */ + public function wp_getPages($args) { + $this->escape($args); + + $username = $args[1]; + $password = $args[2]; + $num_pages = isset($args[3]) ? (int) $args[3] : 10; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + if ( !current_user_can( 'edit_pages' ) ) + return new IXR_Error( 401, __( 'Sorry, you cannot edit pages.' ) ); + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.getPages' ); + + $pages = get_posts( array('post_type' => 'page', 'post_status' => 'any', 'numberposts' => $num_pages) ); + $num_pages = count($pages); + + // If we have pages, put together their info. + if ( $num_pages >= 1 ) { + $pages_struct = array(); + + foreach ($pages as $page) { + if ( current_user_can( 'edit_page', $page->ID ) ) + $pages_struct[] = $this->_prepare_page( $page ); + } + + return $pages_struct; + } + + return array(); + } + + /** + * Create new page. + * + * @since 2.2.0 + * + * @param array $args Method parameters. See {@link wp_xmlrpc_server::mw_newPost()} + * @return int|IXR_Error + */ + public function wp_newPage($args) { + // Items not escaped here will be escaped in newPost. + $username = $this->escape($args[1]); + $password = $this->escape($args[2]); + $page = $args[3]; + $publish = $args[4]; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.newPage' ); + + // Mark this as content for a page. + $args[3]["post_type"] = 'page'; + + // Let mw_newPost do all of the heavy lifting. + return $this->mw_newPost( $args ); + } + + /** + * Delete page. + * + * @since 2.2.0 + * + * @param array $args Method parameters. + * @return bool|IXR_Error True, if success. + */ + public function wp_deletePage($args) { + $this->escape($args); + + $username = $args[1]; + $password = $args[2]; + $page_id = (int) $args[3]; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.deletePage' ); + + // Get the current page based on the page_id and + // make sure it is a page and not a post. + $actual_page = get_post($page_id, ARRAY_A); + if ( !$actual_page || ($actual_page['post_type'] != 'page') ) + return new IXR_Error( 404, __( 'Sorry, no such page.' ) ); + + // Make sure the user can delete pages. + if ( !current_user_can('delete_page', $page_id) ) + return new IXR_Error( 401, __( 'Sorry, you do not have the right to delete this page.' ) ); + + // Attempt to delete the page. + $result = wp_delete_post($page_id); + if ( !$result ) + return new IXR_Error( 500, __( 'Failed to delete the page.' ) ); + + /** + * Fires after a page has been successfully deleted via XML-RPC. + * + * @since 3.4.0 + * + * @param int $page_id ID of the deleted page. + * @param array $args An array of arguments to delete the page. + */ + do_action( 'xmlrpc_call_success_wp_deletePage', $page_id, $args ); + + return true; + } + + /** + * Edit page. + * + * @since 2.2.0 + * + * @param array $args Method parameters. + * @return array|IXR_Error + */ + public function wp_editPage($args) { + // Items not escaped here will be escaped in editPost. + $page_id = (int) $this->escape($args[1]); + $username = $this->escape($args[2]); + $password = $this->escape($args[3]); + $content = $args[4]; + $publish = $args[5]; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.editPage' ); + + // Get the page data and make sure it is a page. + $actual_page = get_post($page_id, ARRAY_A); + if ( !$actual_page || ($actual_page['post_type'] != 'page') ) + return new IXR_Error( 404, __( 'Sorry, no such page.' ) ); + + // Make sure the user is allowed to edit pages. + if ( !current_user_can('edit_page', $page_id) ) + return new IXR_Error( 401, __( 'Sorry, you do not have the right to edit this page.' ) ); + + // Mark this as content for a page. + $content['post_type'] = 'page'; + + // Arrange args in the way mw_editPost understands. + $args = array( + $page_id, + $username, + $password, + $content, + $publish + ); + + // Let mw_editPost do all of the heavy lifting. + return $this->mw_editPost( $args ); + } + + /** + * Retrieve page list. + * + * @since 2.2.0 + * + * @param array $args Method parameters. + * @return array|IXR_Error + */ + public function wp_getPageList($args) { + global $wpdb; + + $this->escape($args); + + $username = $args[1]; + $password = $args[2]; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + if ( !current_user_can( 'edit_pages' ) ) + return new IXR_Error( 401, __( 'Sorry, you cannot edit pages.' ) ); + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.getPageList' ); + + // Get list of pages ids and titles + $page_list = $wpdb->get_results(" + SELECT ID page_id, + post_title page_title, + post_parent page_parent_id, + post_date_gmt, + post_date, + post_status + FROM {$wpdb->posts} + WHERE post_type = 'page' + ORDER BY ID + "); + + // The date needs to be formatted properly. + $num_pages = count($page_list); + for ( $i = 0; $i < $num_pages; $i++ ) { + $page_list[$i]->dateCreated = $this->_convert_date( $page_list[$i]->post_date ); + $page_list[$i]->date_created_gmt = $this->_convert_date_gmt( $page_list[$i]->post_date_gmt, $page_list[$i]->post_date ); + + unset($page_list[$i]->post_date_gmt); + unset($page_list[$i]->post_date); + unset($page_list[$i]->post_status); + } + + return $page_list; + } + + /** + * Retrieve authors list. + * + * @since 2.2.0 + * + * @param array $args Method parameters. + * @return array|IXR_Error + */ + public function wp_getAuthors($args) { + + $this->escape($args); + + $username = $args[1]; + $password = $args[2]; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + if ( !current_user_can('edit_posts') ) + return new IXR_Error( 401, __( 'Sorry, you cannot edit posts on this site.' ) ); + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.getAuthors' ); + + $authors = array(); + foreach ( get_users( array( 'fields' => array('ID','user_login','display_name') ) ) as $user ) { + $authors[] = array( + 'user_id' => $user->ID, + 'user_login' => $user->user_login, + 'display_name' => $user->display_name + ); + } + + return $authors; + } + + /** + * Get list of all tags + * + * @since 2.7.0 + * + * @param array $args Method parameters. + * @return array|IXR_Error + */ + public function wp_getTags( $args ) { + $this->escape( $args ); + + $username = $args[1]; + $password = $args[2]; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + if ( !current_user_can( 'edit_posts' ) ) + return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view tags.' ) ); + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.getKeywords' ); + + $tags = array(); + + if ( $all_tags = get_tags() ) { + foreach( (array) $all_tags as $tag ) { + $struct = array(); + $struct['tag_id'] = $tag->term_id; + $struct['name'] = $tag->name; + $struct['count'] = $tag->count; + $struct['slug'] = $tag->slug; + $struct['html_url'] = esc_html( get_tag_link( $tag->term_id ) ); + $struct['rss_url'] = esc_html( get_tag_feed_link( $tag->term_id ) ); + + $tags[] = $struct; + } + } + + return $tags; + } + + /** + * Create new category. + * + * @since 2.2.0 + * + * @param array $args Method parameters. + * @return int|IXR_Error Category ID. + */ + public function wp_newCategory($args) { + $this->escape($args); + + $username = $args[1]; + $password = $args[2]; + $category = $args[3]; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.newCategory' ); + + // Make sure the user is allowed to add a category. + if ( !current_user_can('manage_categories') ) + return new IXR_Error(401, __('Sorry, you do not have the right to add a category.')); + + // If no slug was provided make it empty so that + // WordPress will generate one. + if ( empty($category['slug']) ) + $category['slug'] = ''; + + // If no parent_id was provided make it empty + // so that it will be a top level page (no parent). + if ( !isset($category['parent_id']) ) + $category['parent_id'] = ''; + + // If no description was provided make it empty. + if ( empty($category["description"]) ) + $category["description"] = ""; + + $new_category = array( + 'cat_name' => $category['name'], + 'category_nicename' => $category['slug'], + 'category_parent' => $category['parent_id'], + 'category_description' => $category['description'] + ); + + $cat_id = wp_insert_category($new_category, true); + if ( is_wp_error( $cat_id ) ) { + if ( 'term_exists' == $cat_id->get_error_code() ) + return (int) $cat_id->get_error_data(); + else + return new IXR_Error(500, __('Sorry, the new category failed.')); + } elseif ( ! $cat_id ) { + return new IXR_Error(500, __('Sorry, the new category failed.')); + } + + /** + * Fires after a new category has been successfully created via XML-RPC. + * + * @since 3.4.0 + * + * @param int $cat_id ID of the new category. + * @param array $args An array of new category arguments. + */ + do_action( 'xmlrpc_call_success_wp_newCategory', $cat_id, $args ); + + return $cat_id; + } + + /** + * Remove category. + * + * @since 2.5.0 + * + * @param array $args Method parameters. + * @return bool|IXR_Error See {@link wp_delete_term()} for return info. + */ + public function wp_deleteCategory($args) { + $this->escape($args); + + $username = $args[1]; + $password = $args[2]; + $category_id = (int) $args[3]; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.deleteCategory' ); + + if ( !current_user_can('manage_categories') ) + return new IXR_Error( 401, __( 'Sorry, you do not have the right to delete a category.' ) ); + + $status = wp_delete_term( $category_id, 'category' ); + + if ( true == $status ) { + /** + * Fires after a category has been successfully deleted via XML-RPC. + * + * @since 3.4.0 + * + * @param int $category_id ID of the deleted category. + * @param array $args An array of arguments to delete the category. + */ + do_action( 'xmlrpc_call_success_wp_deleteCategory', $category_id, $args ); + } + + return $status; + } + + /** + * Retrieve category list. + * + * @since 2.2.0 + * + * @param array $args Method parameters. + * @return array|IXR_Error + */ + public function wp_suggestCategories($args) { + $this->escape($args); + + $username = $args[1]; + $password = $args[2]; + $category = $args[3]; + $max_results = (int) $args[4]; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + if ( !current_user_can( 'edit_posts' ) ) + return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts to this site in order to view categories.' ) ); + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.suggestCategories' ); + + $category_suggestions = array(); + $args = array('get' => 'all', 'number' => $max_results, 'name__like' => $category); + foreach ( (array) get_categories($args) as $cat ) { + $category_suggestions[] = array( + 'category_id' => $cat->term_id, + 'category_name' => $cat->name + ); + } + + return $category_suggestions; + } + + /** + * Retrieve comment. + * + * @since 2.7.0 + * + * @param array $args Method parameters. + * @return array|IXR_Error + */ + public function wp_getComment($args) { + $this->escape($args); + + $username = $args[1]; + $password = $args[2]; + $comment_id = (int) $args[3]; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + if ( !current_user_can( 'moderate_comments' ) ) + return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) ); + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.getComment' ); + + if ( ! $comment = get_comment($comment_id) ) + return new IXR_Error( 404, __( 'Invalid comment ID.' ) ); + + return $this->_prepare_comment( $comment ); + } + + /** + * Retrieve comments. + * + * Besides the common blog_id (unused), username, and password arguments, it takes a filter + * array as last argument. + * + * Accepted 'filter' keys are 'status', 'post_id', 'offset', and 'number'. + * + * The defaults are as follows: + * - 'status' - Default is ''. Filter by status (e.g., 'approve', 'hold') + * - 'post_id' - Default is ''. The post where the comment is posted. Empty string shows all comments. + * - 'number' - Default is 10. Total number of media items to retrieve. + * - 'offset' - Default is 0. See {@link WP_Query::query()} for more. + * + * @since 2.7.0 + * + * @param array $args Method parameters. + * @return array|IXR_Error Contains a collection of comments. See {@link wp_xmlrpc_server::wp_getComment()} for a description of each item contents + */ + public function wp_getComments($args) { + $this->escape($args); + + $username = $args[1]; + $password = $args[2]; + $struct = isset( $args[3] ) ? $args[3] : array(); + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + if ( !current_user_can( 'moderate_comments' ) ) + return new IXR_Error( 401, __( 'Sorry, you cannot edit comments.' ) ); + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.getComments' ); + + if ( isset($struct['status']) ) + $status = $struct['status']; + else + $status = ''; + + $post_id = ''; + if ( isset($struct['post_id']) ) + $post_id = absint($struct['post_id']); + + $offset = 0; + if ( isset($struct['offset']) ) + $offset = absint($struct['offset']); + + $number = 10; + if ( isset($struct['number']) ) + $number = absint($struct['number']); + + $comments = get_comments( array('status' => $status, 'post_id' => $post_id, 'offset' => $offset, 'number' => $number ) ); + + $comments_struct = array(); + + foreach ( $comments as $comment ) { + $comments_struct[] = $this->_prepare_comment( $comment ); + } + + return $comments_struct; + } + + /** + * Delete a comment. + * + * By default, the comment will be moved to the trash instead of deleted. + * See {@link wp_delete_comment()} for more information on + * this behavior. + * + * @since 2.7.0 + * + * @param array $args Method parameters. Contains: + * - blog_id (unused) + * - username + * - password + * - comment_id + * @return bool|IXR_Error {@link wp_delete_comment()} + */ + public function wp_deleteComment($args) { + $this->escape($args); + + $username = $args[1]; + $password = $args[2]; + $comment_ID = (int) $args[3]; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + if ( !current_user_can( 'moderate_comments' ) ) + return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) ); + + if ( ! get_comment($comment_ID) ) + return new IXR_Error( 404, __( 'Invalid comment ID.' ) ); + + if ( !current_user_can( 'edit_comment', $comment_ID ) ) + return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) ); + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.deleteComment' ); + + $status = wp_delete_comment( $comment_ID ); + + if ( true == $status ) { + /** + * Fires after a comment has been successfully deleted via XML-RPC. + * + * @since 3.4.0 + * + * @param int $comment_ID ID of the deleted comment. + * @param array $args An array of arguments to delete the comment. + */ + do_action( 'xmlrpc_call_success_wp_deleteComment', $comment_ID, $args ); + } + + return $status; + } + + /** + * Edit comment. + * + * Besides the common blog_id (unused), username, and password arguments, it takes a + * comment_id integer and a content_struct array as last argument. + * + * The allowed keys in the content_struct array are: + * - 'author' + * - 'author_url' + * - 'author_email' + * - 'content' + * - 'date_created_gmt' + * - 'status'. Common statuses are 'approve', 'hold', 'spam'. See {@link get_comment_statuses()} for more details + * + * @since 2.7.0 + * + * @param array $args. Contains: + * - blog_id (unused) + * - username + * - password + * - comment_id + * - content_struct + * @return bool|IXR_Error True, on success. + */ + public function wp_editComment($args) { + $this->escape($args); + + $username = $args[1]; + $password = $args[2]; + $comment_ID = (int) $args[3]; + $content_struct = $args[4]; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + if ( !current_user_can( 'moderate_comments' ) ) + return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) ); + + if ( ! get_comment($comment_ID) ) + return new IXR_Error( 404, __( 'Invalid comment ID.' ) ); + + if ( !current_user_can( 'edit_comment', $comment_ID ) ) + return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) ); + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.editComment' ); + + if ( isset($content_struct['status']) ) { + $statuses = get_comment_statuses(); + $statuses = array_keys($statuses); + + if ( ! in_array($content_struct['status'], $statuses) ) + return new IXR_Error( 401, __( 'Invalid comment status.' ) ); + $comment_approved = $content_struct['status']; + } + + // Do some timestamp voodoo + if ( !empty( $content_struct['date_created_gmt'] ) ) { + // We know this is supposed to be GMT, so we're going to slap that Z on there by force + $dateCreated = rtrim( $content_struct['date_created_gmt']->getIso(), 'Z' ) . 'Z'; + $comment_date = get_date_from_gmt(iso8601_to_datetime($dateCreated)); + $comment_date_gmt = iso8601_to_datetime($dateCreated, 'GMT'); + } + + if ( isset($content_struct['content']) ) + $comment_content = $content_struct['content']; + + if ( isset($content_struct['author']) ) + $comment_author = $content_struct['author']; + + if ( isset($content_struct['author_url']) ) + $comment_author_url = $content_struct['author_url']; + + if ( isset($content_struct['author_email']) ) + $comment_author_email = $content_struct['author_email']; + + // We've got all the data -- post it: + $comment = compact('comment_ID', 'comment_content', 'comment_approved', 'comment_date', 'comment_date_gmt', 'comment_author', 'comment_author_email', 'comment_author_url'); + + $result = wp_update_comment($comment); + if ( is_wp_error( $result ) ) + return new IXR_Error(500, $result->get_error_message()); + + if ( !$result ) + return new IXR_Error(500, __('Sorry, the comment could not be edited. Something wrong happened.')); + + /** + * Fires after a comment has been successfully updated via XML-RPC. + * + * @since 3.4.0 + * + * @param int $comment_ID ID of the updated comment. + * @param array $args An array of arguments to update the comment. + */ + do_action( 'xmlrpc_call_success_wp_editComment', $comment_ID, $args ); + + return true; + } + + /** + * Create new comment. + * + * @since 2.7.0 + * + * @param array $args Method parameters. + * @return int|IXR_Error {@link wp_new_comment()} + */ + public function wp_newComment($args) { + $this->escape($args); + + $username = $args[1]; + $password = $args[2]; + $post = $args[3]; + $content_struct = $args[4]; + + /** + * Filter whether to allow anonymous comments over XML-RPC. + * + * @since 2.7.0 + * + * @param bool $allow Whether to allow anonymous commenting via XML-RPC. + * Default false. + */ + $allow_anon = apply_filters( 'xmlrpc_allow_anonymous_comments', false ); + + $user = $this->login($username, $password); + + if ( !$user ) { + $logged_in = false; + if ( $allow_anon && get_option('comment_registration') ) + return new IXR_Error( 403, __( 'You must be registered to comment' ) ); + else if ( !$allow_anon ) + return $this->error; + } else { + $logged_in = true; + } + + if ( is_numeric($post) ) + $post_id = absint($post); + else + $post_id = url_to_postid($post); + + if ( ! $post_id ) + return new IXR_Error( 404, __( 'Invalid post ID.' ) ); + + if ( ! get_post($post_id) ) + return new IXR_Error( 404, __( 'Invalid post ID.' ) ); + + $comment = array(); + $comment['comment_post_ID'] = $post_id; + + if ( $logged_in ) { + $comment['comment_author'] = $this->escape( $user->display_name ); + $comment['comment_author_email'] = $this->escape( $user->user_email ); + $comment['comment_author_url'] = $this->escape( $user->user_url ); + $comment['user_ID'] = $user->ID; + } else { + $comment['comment_author'] = ''; + if ( isset($content_struct['author']) ) + $comment['comment_author'] = $content_struct['author']; + + $comment['comment_author_email'] = ''; + if ( isset($content_struct['author_email']) ) + $comment['comment_author_email'] = $content_struct['author_email']; + + $comment['comment_author_url'] = ''; + if ( isset($content_struct['author_url']) ) + $comment['comment_author_url'] = $content_struct['author_url']; + + $comment['user_ID'] = 0; + + if ( get_option('require_name_email') ) { + if ( 6 > strlen($comment['comment_author_email']) || '' == $comment['comment_author'] ) + return new IXR_Error( 403, __( 'Comment author name and email are required' ) ); + elseif ( !is_email($comment['comment_author_email']) ) + return new IXR_Error( 403, __( 'A valid email address is required' ) ); + } + } + + $comment['comment_parent'] = isset($content_struct['comment_parent']) ? absint($content_struct['comment_parent']) : 0; + + $comment['comment_content'] = isset($content_struct['content']) ? $content_struct['content'] : null; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.newComment' ); + + $comment_ID = wp_new_comment( $comment ); + + /** + * Fires after a new comment has been successfully created via XML-RPC. + * + * @since 3.4.0 + * + * @param int $comment_ID ID of the new comment. + * @param array $args An array of new comment arguments. + */ + do_action( 'xmlrpc_call_success_wp_newComment', $comment_ID, $args ); + + return $comment_ID; + } + + /** + * Retrieve all of the comment status. + * + * @since 2.7.0 + * + * @param array $args Method parameters. + * @return array|IXR_Error + */ + public function wp_getCommentStatusList($args) { + $this->escape( $args ); + + $username = $args[1]; + $password = $args[2]; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + if ( !current_user_can( 'moderate_comments' ) ) + return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) ); + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.getCommentStatusList' ); + + return get_comment_statuses(); + } + + /** + * Retrieve comment count. + * + * @since 2.5.0 + * + * @param array $args Method parameters. + * @return array|IXR_Error + */ + public function wp_getCommentCount( $args ) { + $this->escape($args); + + $username = $args[1]; + $password = $args[2]; + $post_id = (int) $args[3]; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + if ( !current_user_can( 'edit_posts' ) ) + return new IXR_Error( 403, __( 'You are not allowed access to details about comments.' ) ); + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.getCommentCount' ); + + $count = wp_count_comments( $post_id ); + return array( + 'approved' => $count->approved, + 'awaiting_moderation' => $count->moderated, + 'spam' => $count->spam, + 'total_comments' => $count->total_comments + ); + } + + /** + * Retrieve post statuses. + * + * @since 2.5.0 + * + * @param array $args Method parameters. + * @return array|IXR_Error + */ + public function wp_getPostStatusList( $args ) { + $this->escape( $args ); + + $username = $args[1]; + $password = $args[2]; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + if ( !current_user_can( 'edit_posts' ) ) + return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) ); + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.getPostStatusList' ); + + return get_post_statuses(); + } + + /** + * Retrieve page statuses. + * + * @since 2.5.0 + * + * @param array $args Method parameters. + * @return array|IXR_Error + */ + public function wp_getPageStatusList( $args ) { + $this->escape( $args ); + + $username = $args[1]; + $password = $args[2]; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + if ( !current_user_can( 'edit_pages' ) ) + return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) ); + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.getPageStatusList' ); + + return get_page_statuses(); + } + + /** + * Retrieve page templates. + * + * @since 2.6.0 + * + * @param array $args Method parameters. + * @return array|IXR_Error + */ + public function wp_getPageTemplates( $args ) { + $this->escape( $args ); + + $username = $args[1]; + $password = $args[2]; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + if ( !current_user_can( 'edit_pages' ) ) + return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) ); + + $templates = get_page_templates(); + $templates['Default'] = 'default'; + + return $templates; + } + + /** + * Retrieve blog options. + * + * @since 2.6.0 + * + * @param array $args Method parameters. + * @return array|IXR_Error + */ + public function wp_getOptions( $args ) { + $this->escape( $args ); + + $username = $args[1]; + $password = $args[2]; + $options = isset( $args[3] ) ? (array) $args[3] : array(); + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + // If no specific options where asked for, return all of them + if ( count( $options ) == 0 ) + $options = array_keys($this->blog_options); + + return $this->_getOptions($options); + } + + /** + * Retrieve blog options value from list. + * + * @since 2.6.0 + * + * @param array $options Options to retrieve. + * @return array + */ + public function _getOptions($options) { + $data = array(); + $can_manage = current_user_can( 'manage_options' ); + foreach ( $options as $option ) { + if ( array_key_exists( $option, $this->blog_options ) ) { + $data[$option] = $this->blog_options[$option]; + //Is the value static or dynamic? + if ( isset( $data[$option]['option'] ) ) { + $data[$option]['value'] = get_option( $data[$option]['option'] ); + unset($data[$option]['option']); + } + + if ( ! $can_manage ) + $data[$option]['readonly'] = true; + } + } + + return $data; + } + + /** + * Update blog options. + * + * @since 2.6.0 + * + * @param array $args Method parameters. + * @return array|IXR_Error + */ + public function wp_setOptions( $args ) { + $this->escape( $args ); + + $username = $args[1]; + $password = $args[2]; + $options = (array) $args[3]; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + if ( !current_user_can( 'manage_options' ) ) + return new IXR_Error( 403, __( 'You are not allowed to update options.' ) ); + + $option_names = array(); + foreach ( $options as $o_name => $o_value ) { + $option_names[] = $o_name; + if ( !array_key_exists( $o_name, $this->blog_options ) ) + continue; + + if ( $this->blog_options[$o_name]['readonly'] == true ) + continue; + + update_option( $this->blog_options[$o_name]['option'], wp_unslash( $o_value ) ); + } + + //Now return the updated values + return $this->_getOptions($option_names); + } + + /** + * Retrieve a media item by ID + * + * @since 3.1.0 + * + * @param array $args Method parameters. Contains: + * - blog_id (unused) + * - username + * - password + * - attachment_id + * @return array|IXR_Error Associative array contains: + * - 'date_created_gmt' + * - 'parent' + * - 'link' + * - 'thumbnail' + * - 'title' + * - 'caption' + * - 'description' + * - 'metadata' + */ + public function wp_getMediaItem($args) { + $this->escape($args); + + $username = $args[1]; + $password = $args[2]; + $attachment_id = (int) $args[3]; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + if ( !current_user_can( 'upload_files' ) ) + return new IXR_Error( 403, __( 'You do not have permission to upload files.' ) ); + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.getMediaItem' ); + + if ( ! $attachment = get_post($attachment_id) ) + return new IXR_Error( 404, __( 'Invalid attachment ID.' ) ); + + return $this->_prepare_media_item( $attachment ); + } + + /** + * Retrieves a collection of media library items (or attachments) + * + * Besides the common blog_id (unused), username, and password arguments, it takes a filter + * array as last argument. + * + * Accepted 'filter' keys are 'parent_id', 'mime_type', 'offset', and 'number'. + * + * The defaults are as follows: + * - 'number' - Default is 5. Total number of media items to retrieve. + * - 'offset' - Default is 0. See {@link WP_Query::query()} for more. + * - 'parent_id' - Default is ''. The post where the media item is attached. Empty string shows all media items. 0 shows unattached media items. + * - 'mime_type' - Default is ''. Filter by mime type (e.g., 'image/jpeg', 'application/pdf') + * + * @since 3.1.0 + * + * @param array $args Method parameters. Contains: + * - blog_id (unused) + * - username + * - password + * - filter + * @return array|IXR_Error Contains a collection of media items. See {@link wp_xmlrpc_server::wp_getMediaItem()} for a description of each item contents + */ + public function wp_getMediaLibrary($args) { + $this->escape($args); + + $username = $args[1]; + $password = $args[2]; + $struct = isset( $args[3] ) ? $args[3] : array() ; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + if ( !current_user_can( 'upload_files' ) ) + return new IXR_Error( 401, __( 'You do not have permission to upload files.' ) ); + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.getMediaLibrary' ); + + $parent_id = ( isset($struct['parent_id']) ) ? absint($struct['parent_id']) : '' ; + $mime_type = ( isset($struct['mime_type']) ) ? $struct['mime_type'] : '' ; + $offset = ( isset($struct['offset']) ) ? absint($struct['offset']) : 0 ; + $number = ( isset($struct['number']) ) ? absint($struct['number']) : -1 ; + + $attachments = get_posts( array('post_type' => 'attachment', 'post_parent' => $parent_id, 'offset' => $offset, 'numberposts' => $number, 'post_mime_type' => $mime_type ) ); + + $attachments_struct = array(); + + foreach ($attachments as $attachment ) + $attachments_struct[] = $this->_prepare_media_item( $attachment ); + + return $attachments_struct; + } + + /** + * Retrieves a list of post formats used by the site + * + * @since 3.1.0 + * + * @param array $args Method parameters. Contains: + * - blog_id (unused) + * - username + * - password + * @return array|IXR_Error + */ + public function wp_getPostFormats( $args ) { + $this->escape( $args ); + + $username = $args[1]; + $password = $args[2]; + + if ( !$user = $this->login( $username, $password ) ) + return $this->error; + + if ( !current_user_can( 'edit_posts' ) ) + return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) ); + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.getPostFormats' ); + + $formats = get_post_format_strings(); + + # find out if they want a list of currently supports formats + if ( isset( $args[3] ) && is_array( $args[3] ) ) { + if ( $args[3]['show-supported'] ) { + if ( current_theme_supports( 'post-formats' ) ) { + $supported = get_theme_support( 'post-formats' ); + + $data = array(); + $data['all'] = $formats; + $data['supported'] = $supported[0]; + + $formats = $data; + } + } + } + + return $formats; + } + + /** + * Retrieves a post type + * + * @since 3.4.0 + * + * @uses get_post_type_object() + * @param array $args Method parameters. Contains: + * - int $blog_id (unused) + * - string $username + * - string $password + * - string $post_type_name + * - array $fields + * @return array|IXR_Error Array contains: + * - 'labels' + * - 'description' + * - 'capability_type' + * - 'cap' + * - 'map_meta_cap' + * - 'hierarchical' + * - 'menu_position' + * - 'taxonomies' + * - 'supports' + */ + public function wp_getPostType( $args ) { + if ( ! $this->minimum_args( $args, 4 ) ) + return $this->error; + + $this->escape( $args ); + + $username = $args[1]; + $password = $args[2]; + $post_type_name = $args[3]; + + if ( isset( $args[4] ) ) { + $fields = $args[4]; + } else { + /** + * Filter the default query fields used by the given XML-RPC method. + * + * @since 3.4.0 + * + * @param array $fields An array of post type query fields for the given method. + * @param string $method The method name. + */ + $fields = apply_filters( 'xmlrpc_default_posttype_fields', array( 'labels', 'cap', 'taxonomies' ), 'wp.getPostType' ); + } + + if ( !$user = $this->login( $username, $password ) ) + return $this->error; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.getPostType' ); + + if( ! post_type_exists( $post_type_name ) ) + return new IXR_Error( 403, __( 'Invalid post type' ) ); + + $post_type = get_post_type_object( $post_type_name ); + + if( ! current_user_can( $post_type->cap->edit_posts ) ) + return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post type.' ) ); + + return $this->_prepare_post_type( $post_type, $fields ); + } + + /** + * Retrieves a post types + * + * @since 3.4.0 + * + * @uses get_post_types() + * @param array $args Method parameters. Contains: + * - int $blog_id (unused) + * - string $username + * - string $password + * - array $filter + * - array $fields + * @return array|IXR_Error + */ + public function wp_getPostTypes( $args ) { + if ( ! $this->minimum_args( $args, 3 ) ) + return $this->error; + + $this->escape( $args ); + + $username = $args[1]; + $password = $args[2]; + $filter = isset( $args[3] ) ? $args[3] : array( 'public' => true ); + + if ( isset( $args[4] ) ) { + $fields = $args[4]; + } else { + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + $fields = apply_filters( 'xmlrpc_default_posttype_fields', array( 'labels', 'cap', 'taxonomies' ), 'wp.getPostTypes' ); + } + + if ( ! $user = $this->login( $username, $password ) ) + return $this->error; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.getPostTypes' ); + + $post_types = get_post_types( $filter, 'objects' ); + + $struct = array(); + + foreach( $post_types as $post_type ) { + if( ! current_user_can( $post_type->cap->edit_posts ) ) + continue; + + $struct[$post_type->name] = $this->_prepare_post_type( $post_type, $fields ); + } + + return $struct; + } + + /** + * Retrieve revisions for a specific post. + * + * @since 3.5.0 + * + * The optional $fields parameter specifies what fields will be included + * in the response array. + * + * @uses wp_get_post_revisions() + * @see wp_getPost() for more on $fields + * + * @param array $args Method parameters. Contains: + * - int $blog_id (unused) + * - string $username + * - string $password + * - int $post_id + * - array $fields + * @return array|IXR_Error contains a collection of posts. + */ + public function wp_getRevisions( $args ) { + if ( ! $this->minimum_args( $args, 4 ) ) + return $this->error; + + $this->escape( $args ); + + $username = $args[1]; + $password = $args[2]; + $post_id = (int) $args[3]; + + if ( isset( $args[4] ) ) { + $fields = $args[4]; + } else { + /** + * Filter the default revision query fields used by the given XML-RPC method. + * + * @since 3.5.0 + * + * @param array $field An array of revision query fields. + * @param string $method The method name. + */ + $fields = apply_filters( 'xmlrpc_default_revision_fields', array( 'post_date', 'post_date_gmt' ), 'wp.getRevisions' ); + } + + if ( ! $user = $this->login( $username, $password ) ) + return $this->error; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.getRevisions' ); + + if ( ! $post = get_post( $post_id ) ) + return new IXR_Error( 404, __( 'Invalid post ID' ) ); + + if ( ! current_user_can( 'edit_post', $post_id ) ) + return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts.' ) ); + + // Check if revisions are enabled. + if ( ! wp_revisions_enabled( $post ) ) + return new IXR_Error( 401, __( 'Sorry, revisions are disabled.' ) ); + + $revisions = wp_get_post_revisions( $post_id ); + + if ( ! $revisions ) + return array(); + + $struct = array(); + + foreach ( $revisions as $revision ) { + if ( ! current_user_can( 'read_post', $revision->ID ) ) + continue; + + // Skip autosaves + if ( wp_is_post_autosave( $revision ) ) + continue; + + $struct[] = $this->_prepare_post( get_object_vars( $revision ), $fields ); + } + + return $struct; + } + + /** + * Restore a post revision + * + * @since 3.5.0 + * + * @uses wp_restore_post_revision() + * + * @param array $args Method parameters. Contains: + * - int $blog_id (unused) + * - string $username + * - string $password + * - int $post_id + * @return bool|IXR_Error false if there was an error restoring, true if success. + */ + public function wp_restoreRevision( $args ) { + if ( ! $this->minimum_args( $args, 3 ) ) + return $this->error; + + $this->escape( $args ); + + $username = $args[1]; + $password = $args[2]; + $revision_id = (int) $args[3]; + + if ( ! $user = $this->login( $username, $password ) ) + return $this->error; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'wp.restoreRevision' ); + + if ( ! $revision = wp_get_post_revision( $revision_id ) ) + return new IXR_Error( 404, __( 'Invalid post ID' ) ); + + if ( wp_is_post_autosave( $revision ) ) + return new IXR_Error( 404, __( 'Invalid post ID' ) ); + + if ( ! $post = get_post( $revision->post_parent ) ) + return new IXR_Error( 404, __( 'Invalid post ID' ) ); + + if ( ! current_user_can( 'edit_post', $revision->post_parent ) ) + return new IXR_Error( 401, __( 'Sorry, you cannot edit this post.' ) ); + + // Check if revisions are disabled. + if ( ! wp_revisions_enabled( $post ) ) + return new IXR_Error( 401, __( 'Sorry, revisions are disabled.' ) ); + + $post = wp_restore_post_revision( $revision_id ); + + return (bool) $post; + } + + /* Blogger API functions. + * specs on http://plant.blogger.com/api and http://groups.yahoo.com/group/bloggerDev/ + */ + + /** + * Retrieve blogs that user owns. + * + * Will make more sense once we support multiple blogs. + * + * @since 1.5.0 + * + * @param array $args Method parameters. + * @return array|IXR_Error + */ + public function blogger_getUsersBlogs($args) { + if ( is_multisite() ) + return $this->_multisite_getUsersBlogs($args); + + $this->escape($args); + + $username = $args[1]; + $password = $args[2]; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'blogger.getUsersBlogs' ); + + $is_admin = current_user_can('manage_options'); + + $struct = array( + 'isAdmin' => $is_admin, + 'url' => get_option('home') . '/', + 'blogid' => '1', + 'blogName' => get_option('blogname'), + 'xmlrpc' => site_url( 'xmlrpc.php', 'rpc' ), + ); + + return array($struct); + } + + /** + * Private function for retrieving a users blogs for multisite setups + * + * @access protected + * + * @return array|IXR_Error + */ + protected function _multisite_getUsersBlogs($args) { + $current_blog = get_blog_details(); + + $domain = $current_blog->domain; + $path = $current_blog->path . 'xmlrpc.php'; + + $rpc = new IXR_Client( set_url_scheme( "http://{$domain}{$path}" ) ); + $rpc->query('wp.getUsersBlogs', $args[1], $args[2]); + $blogs = $rpc->getResponse(); + + if ( isset($blogs['faultCode']) ) + return new IXR_Error($blogs['faultCode'], $blogs['faultString']); + + if ( $_SERVER['HTTP_HOST'] == $domain && $_SERVER['REQUEST_URI'] == $path ) { + return $blogs; + } else { + foreach ( (array) $blogs as $blog ) { + if ( strpos($blog['url'], $_SERVER['HTTP_HOST']) ) + return array($blog); + } + return array(); + } + } + + /** + * Retrieve user's data. + * + * Gives your client some info about you, so you don't have to. + * + * @since 1.5.0 + * + * @param array $args Method parameters. + * @return array|IXR_Error + */ + public function blogger_getUserInfo($args) { + + $this->escape($args); + + $username = $args[1]; + $password = $args[2]; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + if ( !current_user_can( 'edit_posts' ) ) + return new IXR_Error( 401, __( 'Sorry, you do not have access to user data on this site.' ) ); + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'blogger.getUserInfo' ); + + $struct = array( + 'nickname' => $user->nickname, + 'userid' => $user->ID, + 'url' => $user->user_url, + 'lastname' => $user->last_name, + 'firstname' => $user->first_name + ); + + return $struct; + } + + /** + * Retrieve post. + * + * @since 1.5.0 + * + * @param array $args Method parameters. + * @return array|IXR_Error + */ + public function blogger_getPost($args) { + + $this->escape($args); + + $post_ID = (int) $args[1]; + $username = $args[2]; + $password = $args[3]; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + $post_data = get_post($post_ID, ARRAY_A); + if ( ! $post_data ) + return new IXR_Error( 404, __( 'Invalid post ID.' ) ); + + if ( !current_user_can( 'edit_post', $post_ID ) ) + return new IXR_Error( 401, __( 'Sorry, you cannot edit this post.' ) ); + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'blogger.getPost' ); + + $categories = implode(',', wp_get_post_categories($post_ID)); + + $content = '<title>'.wp_unslash($post_data['post_title']).''; + $content .= ''.$categories.''; + $content .= wp_unslash($post_data['post_content']); + + $struct = array( + 'userid' => $post_data['post_author'], + 'dateCreated' => $this->_convert_date( $post_data['post_date'] ), + 'content' => $content, + 'postid' => (string) $post_data['ID'] + ); + + return $struct; + } + + /** + * Retrieve list of recent posts. + * + * @since 1.5.0 + * + * @param array $args Method parameters. + * @return array|IXR_Error + */ + public function blogger_getRecentPosts($args) { + + $this->escape($args); + + // $args[0] = appkey - ignored + $username = $args[2]; + $password = $args[3]; + if ( isset( $args[4] ) ) + $query = array( 'numberposts' => absint( $args[4] ) ); + else + $query = array(); + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + if ( ! current_user_can( 'edit_posts' ) ) + return new IXR_Error( 401, __( 'Sorry, you cannot edit posts on this site.' ) ); + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'blogger.getRecentPosts' ); + + $posts_list = wp_get_recent_posts( $query ); + + if ( !$posts_list ) { + $this->error = new IXR_Error(500, __('Either there are no posts, or something went wrong.')); + return $this->error; + } + + $recent_posts = array(); + foreach ($posts_list as $entry) { + if ( !current_user_can( 'edit_post', $entry['ID'] ) ) + continue; + + $post_date = $this->_convert_date( $entry['post_date'] ); + $categories = implode(',', wp_get_post_categories($entry['ID'])); + + $content = ''.wp_unslash($entry['post_title']).''; + $content .= ''.$categories.''; + $content .= wp_unslash($entry['post_content']); + + $recent_posts[] = array( + 'userid' => $entry['post_author'], + 'dateCreated' => $post_date, + 'content' => $content, + 'postid' => (string) $entry['ID'], + ); + } + + return $recent_posts; + } + + /** + * Deprecated. + * + * @since 1.5.0 + * @deprecated 3.5.0 + * @return IXR_Error + */ + public function blogger_getTemplate($args) { + return new IXR_Error( 403, __('Sorry, that file cannot be edited.' ) ); + } + + /** + * Deprecated. + * + * @since 1.5.0 + * @deprecated 3.5.0 + * @return IXR_Error + */ + public function blogger_setTemplate($args) { + return new IXR_Error( 403, __('Sorry, that file cannot be edited.' ) ); + } + + /** + * Create new post. + * + * @since 1.5.0 + * + * @param array $args Method parameters. + * @return int|IXR_Error + */ + public function blogger_newPost($args) { + + $this->escape($args); + + $username = $args[2]; + $password = $args[3]; + $content = $args[4]; + $publish = $args[5]; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'blogger.newPost' ); + + $cap = ($publish) ? 'publish_posts' : 'edit_posts'; + if ( ! current_user_can( get_post_type_object( 'post' )->cap->create_posts ) || !current_user_can($cap) ) + return new IXR_Error(401, __('Sorry, you are not allowed to post on this site.')); + + $post_status = ($publish) ? 'publish' : 'draft'; + + $post_author = $user->ID; + + $post_title = xmlrpc_getposttitle($content); + $post_category = xmlrpc_getpostcategory($content); + $post_content = xmlrpc_removepostdata($content); + + $post_date = current_time('mysql'); + $post_date_gmt = current_time('mysql', 1); + + $post_data = compact('post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_category', 'post_status'); + + $post_ID = wp_insert_post($post_data); + if ( is_wp_error( $post_ID ) ) + return new IXR_Error(500, $post_ID->get_error_message()); + + if ( !$post_ID ) + return new IXR_Error(500, __('Sorry, your entry could not be posted. Something wrong happened.')); + + $this->attach_uploads( $post_ID, $post_content ); + + /** + * Fires after a new post has been successfully created via the XML-RPC Blogger API. + * + * @since 3.4.0 + * + * @param int $post_ID ID of the new post. + * @param array $args An array of new post arguments. + */ + do_action( 'xmlrpc_call_success_blogger_newPost', $post_ID, $args ); + + return $post_ID; + } + + /** + * Edit a post. + * + * @since 1.5.0 + * + * @param array $args Method parameters. + * @return bool|IXR_Error true when done. + */ + public function blogger_editPost( $args ) { + + $this->escape($args); + + $post_ID = (int) $args[1]; + $username = $args[2]; + $password = $args[3]; + $content = $args[4]; + $publish = $args[5]; + + if ( ! $user = $this->login( $username, $password ) ) { + return $this->error; + } + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'blogger.editPost' ); + + $actual_post = get_post( $post_ID, ARRAY_A ); + + if ( ! $actual_post || $actual_post['post_type'] != 'post' ) { + return new IXR_Error( 404, __( 'Sorry, no such post.' ) ); + } + + $this->escape($actual_post); + + if ( ! current_user_can( 'edit_post', $post_ID ) ) { + return new IXR_Error(401, __('Sorry, you do not have the right to edit this post.')); + } + if ( 'publish' == $actual_post['post_status'] && ! current_user_can( 'publish_posts' ) ) { + return new IXR_Error( 401, __( 'Sorry, you do not have the right to publish this post.' ) ); + } + + $postdata = array(); + $postdata['ID'] = $actual_post['ID']; + $postdata['post_content'] = xmlrpc_removepostdata( $content ); + $postdata['post_title'] = xmlrpc_getposttitle( $content ); + $postdata['post_category'] = xmlrpc_getpostcategory( $content ); + $postdata['post_status'] = $actual_post['post_status']; + $postdata['post_excerpt'] = $actual_post['post_excerpt']; + + $result = wp_update_post( $postdata ); + + if ( ! $result ) { + return new IXR_Error(500, __('For some strange yet very annoying reason, this post could not be edited.')); + } + $this->attach_uploads( $actual_post['ID'], $postdata['post_content'] ); + + /** + * Fires after a post has been successfully updated via the XML-RPC Blogger API. + * + * @since 3.4.0 + * + * @param int $post_ID ID of the updated post. + * @param array $args An array of arguments for the post to edit. + */ + do_action( 'xmlrpc_call_success_blogger_editPost', $post_ID, $args ); + + return true; + } + + /** + * Remove a post. + * + * @since 1.5.0 + * + * @param array $args Method parameters. + * @return bool|IXR_Error True when post is deleted. + */ + public function blogger_deletePost($args) { + $this->escape($args); + + $post_ID = (int) $args[1]; + $username = $args[2]; + $password = $args[3]; + $publish = $args[4]; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'blogger.deletePost' ); + + $actual_post = get_post($post_ID,ARRAY_A); + + if ( !$actual_post || $actual_post['post_type'] != 'post' ) + return new IXR_Error(404, __('Sorry, no such post.')); + + if ( !current_user_can('delete_post', $post_ID) ) + return new IXR_Error(401, __('Sorry, you do not have the right to delete this post.')); + + $result = wp_delete_post($post_ID); + + if ( !$result ) + return new IXR_Error(500, __('For some strange yet very annoying reason, this post could not be deleted.')); + + /** + * Fires after a post has been successfully deleted via the XML-RPC Blogger API. + * + * @since 3.4.0 + * + * @param int $post_ID ID of the deleted post. + * @param array $args An array of arguments to delete the post. + */ + do_action( 'xmlrpc_call_success_blogger_deletePost', $post_ID, $args ); + + return true; + } + + /* MetaWeblog API functions + * specs on wherever Dave Winer wants them to be + */ + + /** + * Create a new post. + * + * The 'content_struct' argument must contain: + * - title + * - description + * - mt_excerpt + * - mt_text_more + * - mt_keywords + * - mt_tb_ping_urls + * - categories + * + * Also, it can optionally contain: + * - wp_slug + * - wp_password + * - wp_page_parent_id + * - wp_page_order + * - wp_author_id + * - post_status | page_status - can be 'draft', 'private', 'publish', or 'pending' + * - mt_allow_comments - can be 'open' or 'closed' + * - mt_allow_pings - can be 'open' or 'closed' + * - date_created_gmt + * - dateCreated + * - wp_post_thumbnail + * + * @since 1.5.0 + * + * @param array $args Method parameters. Contains: + * - blog_id (unused) + * - username + * - password + * - content_struct + * - publish + * @return int|IXR_Error + */ + public function mw_newPost($args) { + $this->escape($args); + + $username = $args[1]; + $password = $args[2]; + $content_struct = $args[3]; + $publish = isset( $args[4] ) ? $args[4] : 0; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'metaWeblog.newPost' ); + + $page_template = ''; + if ( !empty( $content_struct['post_type'] ) ) { + if ( $content_struct['post_type'] == 'page' ) { + if ( $publish ) + $cap = 'publish_pages'; + elseif ( isset( $content_struct['page_status'] ) && 'publish' == $content_struct['page_status'] ) + $cap = 'publish_pages'; + else + $cap = 'edit_pages'; + $error_message = __( 'Sorry, you are not allowed to publish pages on this site.' ); + $post_type = 'page'; + if ( !empty( $content_struct['wp_page_template'] ) ) + $page_template = $content_struct['wp_page_template']; + } elseif ( $content_struct['post_type'] == 'post' ) { + if ( $publish ) + $cap = 'publish_posts'; + elseif ( isset( $content_struct['post_status'] ) && 'publish' == $content_struct['post_status'] ) + $cap = 'publish_posts'; + else + $cap = 'edit_posts'; + $error_message = __( 'Sorry, you are not allowed to publish posts on this site.' ); + $post_type = 'post'; + } else { + // No other post_type values are allowed here + return new IXR_Error( 401, __( 'Invalid post type' ) ); + } + } else { + if ( $publish ) + $cap = 'publish_posts'; + elseif ( isset( $content_struct['post_status'] ) && 'publish' == $content_struct['post_status']) + $cap = 'publish_posts'; + else + $cap = 'edit_posts'; + $error_message = __( 'Sorry, you are not allowed to publish posts on this site.' ); + $post_type = 'post'; + } + + if ( ! current_user_can( get_post_type_object( $post_type )->cap->create_posts ) ) + return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish posts on this site.' ) ); + if ( !current_user_can( $cap ) ) + return new IXR_Error( 401, $error_message ); + + // Check for a valid post format if one was given + if ( isset( $content_struct['wp_post_format'] ) ) { + $content_struct['wp_post_format'] = sanitize_key( $content_struct['wp_post_format'] ); + if ( !array_key_exists( $content_struct['wp_post_format'], get_post_format_strings() ) ) { + return new IXR_Error( 404, __( 'Invalid post format' ) ); + } + } + + // Let WordPress generate the post_name (slug) unless + // one has been provided. + $post_name = ""; + if ( isset($content_struct['wp_slug']) ) + $post_name = $content_struct['wp_slug']; + + // Only use a password if one was given. + if ( isset($content_struct['wp_password']) ) + $post_password = $content_struct['wp_password']; + + // Only set a post parent if one was provided. + if ( isset($content_struct['wp_page_parent_id']) ) + $post_parent = $content_struct['wp_page_parent_id']; + + // Only set the menu_order if it was provided. + if ( isset($content_struct['wp_page_order']) ) + $menu_order = $content_struct['wp_page_order']; + + $post_author = $user->ID; + + // If an author id was provided then use it instead. + if ( isset( $content_struct['wp_author_id'] ) && ( $user->ID != $content_struct['wp_author_id'] ) ) { + switch ( $post_type ) { + case "post": + if ( !current_user_can( 'edit_others_posts' ) ) + return new IXR_Error( 401, __( 'You are not allowed to create posts as this user.' ) ); + break; + case "page": + if ( !current_user_can( 'edit_others_pages' ) ) + return new IXR_Error( 401, __( 'You are not allowed to create pages as this user.' ) ); + break; + default: + return new IXR_Error( 401, __( 'Invalid post type' ) ); + break; + } + $author = get_userdata( $content_struct['wp_author_id'] ); + if ( ! $author ) + return new IXR_Error( 404, __( 'Invalid author ID.' ) ); + $post_author = $content_struct['wp_author_id']; + } + + $post_title = isset( $content_struct['title'] ) ? $content_struct['title'] : null; + $post_content = isset( $content_struct['description'] ) ? $content_struct['description'] : null; + + $post_status = $publish ? 'publish' : 'draft'; + + if ( isset( $content_struct["{$post_type}_status"] ) ) { + switch ( $content_struct["{$post_type}_status"] ) { + case 'draft': + case 'pending': + case 'private': + case 'publish': + $post_status = $content_struct["{$post_type}_status"]; + break; + default: + $post_status = $publish ? 'publish' : 'draft'; + break; + } + } + + $post_excerpt = isset($content_struct['mt_excerpt']) ? $content_struct['mt_excerpt'] : null; + $post_more = isset($content_struct['mt_text_more']) ? $content_struct['mt_text_more'] : null; + + $tags_input = isset($content_struct['mt_keywords']) ? $content_struct['mt_keywords'] : null; + + if ( isset($content_struct['mt_allow_comments']) ) { + if ( !is_numeric($content_struct['mt_allow_comments']) ) { + switch ( $content_struct['mt_allow_comments'] ) { + case 'closed': + $comment_status = 'closed'; + break; + case 'open': + $comment_status = 'open'; + break; + default: + $comment_status = get_option('default_comment_status'); + break; + } + } else { + switch ( (int) $content_struct['mt_allow_comments'] ) { + case 0: + case 2: + $comment_status = 'closed'; + break; + case 1: + $comment_status = 'open'; + break; + default: + $comment_status = get_option('default_comment_status'); + break; + } + } + } else { + $comment_status = get_option('default_comment_status'); + } + + if ( isset($content_struct['mt_allow_pings']) ) { + if ( !is_numeric($content_struct['mt_allow_pings']) ) { + switch ( $content_struct['mt_allow_pings'] ) { + case 'closed': + $ping_status = 'closed'; + break; + case 'open': + $ping_status = 'open'; + break; + default: + $ping_status = get_option('default_ping_status'); + break; + } + } else { + switch ( (int) $content_struct['mt_allow_pings'] ) { + case 0: + $ping_status = 'closed'; + break; + case 1: + $ping_status = 'open'; + break; + default: + $ping_status = get_option('default_ping_status'); + break; + } + } + } else { + $ping_status = get_option('default_ping_status'); + } + + if ( $post_more ) + $post_content = $post_content . '' . $post_more; + + $to_ping = null; + if ( isset( $content_struct['mt_tb_ping_urls'] ) ) { + $to_ping = $content_struct['mt_tb_ping_urls']; + if ( is_array($to_ping) ) + $to_ping = implode(' ', $to_ping); + } + + // Do some timestamp voodoo + if ( !empty( $content_struct['date_created_gmt'] ) ) + // We know this is supposed to be GMT, so we're going to slap that Z on there by force + $dateCreated = rtrim( $content_struct['date_created_gmt']->getIso(), 'Z' ) . 'Z'; + elseif ( !empty( $content_struct['dateCreated']) ) + $dateCreated = $content_struct['dateCreated']->getIso(); + + if ( !empty( $dateCreated ) ) { + $post_date = get_date_from_gmt(iso8601_to_datetime($dateCreated)); + $post_date_gmt = iso8601_to_datetime($dateCreated, 'GMT'); + } else { + $post_date = current_time('mysql'); + $post_date_gmt = current_time('mysql', 1); + } + + $post_category = array(); + if ( isset( $content_struct['categories'] ) ) { + $catnames = $content_struct['categories']; + + if ( is_array($catnames) ) { + foreach ($catnames as $cat) { + $post_category[] = get_cat_ID($cat); + } + } + } + + $postdata = compact('post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt', 'comment_status', 'ping_status', 'to_ping', 'post_type', 'post_name', 'post_password', 'post_parent', 'menu_order', 'tags_input', 'page_template'); + + $post_ID = $postdata['ID'] = get_default_post_to_edit( $post_type, true )->ID; + + // Only posts can be sticky + if ( $post_type == 'post' && isset( $content_struct['sticky'] ) ) { + if ( $content_struct['sticky'] == true ) + stick_post( $post_ID ); + elseif ( $content_struct['sticky'] == false ) + unstick_post( $post_ID ); + } + + if ( isset($content_struct['custom_fields']) ) + $this->set_custom_fields($post_ID, $content_struct['custom_fields']); + + if ( isset ( $content_struct['wp_post_thumbnail'] ) ) { + if ( set_post_thumbnail( $post_ID, $content_struct['wp_post_thumbnail'] ) === false ) + return new IXR_Error( 404, __( 'Invalid attachment ID.' ) ); + + unset( $content_struct['wp_post_thumbnail'] ); + } + + // Handle enclosures + $thisEnclosure = isset($content_struct['enclosure']) ? $content_struct['enclosure'] : null; + $this->add_enclosure_if_new($post_ID, $thisEnclosure); + + $this->attach_uploads( $post_ID, $post_content ); + + // Handle post formats if assigned, value is validated earlier + // in this function + if ( isset( $content_struct['wp_post_format'] ) ) + set_post_format( $post_ID, $content_struct['wp_post_format'] ); + + $post_ID = wp_insert_post( $postdata, true ); + if ( is_wp_error( $post_ID ) ) + return new IXR_Error(500, $post_ID->get_error_message()); + + if ( !$post_ID ) + return new IXR_Error(500, __('Sorry, your entry could not be posted. Something wrong happened.')); + + /** + * Fires after a new post has been successfully created via the XML-RPC MovableType API. + * + * @since 3.4.0 + * + * @param int $post_ID ID of the new post. + * @param array $args An array of arguments to create the new post. + */ + do_action( 'xmlrpc_call_success_mw_newPost', $post_ID, $args ); + + return strval($post_ID); + } + + /** + * @param integer $post_ID + * @param array $enclosure + */ + public function add_enclosure_if_new( $post_ID, $enclosure ) { + if ( is_array( $enclosure ) && isset( $enclosure['url'] ) && isset( $enclosure['length'] ) && isset( $enclosure['type'] ) ) { + $encstring = $enclosure['url'] . "\n" . $enclosure['length'] . "\n" . $enclosure['type'] . "\n"; + $found = false; + if ( $enclosures = get_post_meta( $post_ID, 'enclosure' ) ) { + foreach ( $enclosures as $enc ) { + // This method used to omit the trailing new line. #23219 + if ( rtrim( $enc, "\n" ) == rtrim( $encstring, "\n" ) ) { + $found = true; + break; + } + } + } + if ( ! $found ) + add_post_meta( $post_ID, 'enclosure', $encstring ); + } + } + + /** + * Attach upload to a post. + * + * @since 2.1.0 + * + * @param int $post_ID Post ID. + * @param string $post_content Post Content for attachment. + */ + public function attach_uploads( $post_ID, $post_content ) { + global $wpdb; + + // find any unattached files + $attachments = $wpdb->get_results( "SELECT ID, guid FROM {$wpdb->posts} WHERE post_parent = '0' AND post_type = 'attachment'" ); + if ( is_array( $attachments ) ) { + foreach ( $attachments as $file ) { + if ( ! empty( $file->guid ) && strpos( $post_content, $file->guid ) !== false ) + $wpdb->update($wpdb->posts, array('post_parent' => $post_ID), array('ID' => $file->ID) ); + } + } + } + + /** + * Edit a post. + * + * @since 1.5.0 + * + * @param array $args Method parameters. + * @return bool|IXR_Error True on success. + */ + public function mw_editPost($args) { + + $this->escape($args); + + $post_ID = (int) $args[0]; + $username = $args[1]; + $password = $args[2]; + $content_struct = $args[3]; + $publish = isset( $args[4] ) ? $args[4] : 0; + + if ( ! $user = $this->login($username, $password) ) + return $this->error; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'metaWeblog.editPost' ); + + $postdata = get_post( $post_ID, ARRAY_A ); + + // If there is no post data for the give post id, stop + // now and return an error. Other wise a new post will be + // created (which was the old behavior). + if ( ! $postdata || empty( $postdata[ 'ID' ] ) ) + return new IXR_Error( 404, __( 'Invalid post ID.' ) ); + + if ( ! current_user_can( 'edit_post', $post_ID ) ) + return new IXR_Error( 401, __( 'Sorry, you do not have the right to edit this post.' ) ); + + // Use wp.editPost to edit post types other than post and page. + if ( ! in_array( $postdata[ 'post_type' ], array( 'post', 'page' ) ) ) + return new IXR_Error( 401, __( 'Invalid post type' ) ); + + // Thwart attempt to change the post type. + if ( ! empty( $content_struct[ 'post_type' ] ) && ( $content_struct['post_type'] != $postdata[ 'post_type' ] ) ) + return new IXR_Error( 401, __( 'The post type may not be changed.' ) ); + + // Check for a valid post format if one was given + if ( isset( $content_struct['wp_post_format'] ) ) { + $content_struct['wp_post_format'] = sanitize_key( $content_struct['wp_post_format'] ); + if ( !array_key_exists( $content_struct['wp_post_format'], get_post_format_strings() ) ) { + return new IXR_Error( 404, __( 'Invalid post format' ) ); + } + } + + $this->escape($postdata); + + $ID = $postdata['ID']; + $post_content = $postdata['post_content']; + $post_title = $postdata['post_title']; + $post_excerpt = $postdata['post_excerpt']; + $post_password = $postdata['post_password']; + $post_parent = $postdata['post_parent']; + $post_type = $postdata['post_type']; + $menu_order = $postdata['menu_order']; + + // Let WordPress manage slug if none was provided. + $post_name = ""; + $post_name = $postdata['post_name']; + if ( isset($content_struct['wp_slug']) ) + $post_name = $content_struct['wp_slug']; + + // Only use a password if one was given. + if ( isset($content_struct['wp_password']) ) + $post_password = $content_struct['wp_password']; + + // Only set a post parent if one was given. + if ( isset($content_struct['wp_page_parent_id']) ) + $post_parent = $content_struct['wp_page_parent_id']; + + // Only set the menu_order if it was given. + if ( isset($content_struct['wp_page_order']) ) + $menu_order = $content_struct['wp_page_order']; + + $page_template = null; + if ( ! empty( $content_struct['wp_page_template'] ) && 'page' == $post_type ) + $page_template = $content_struct['wp_page_template']; + + $post_author = $postdata['post_author']; + + // Only set the post_author if one is set. + if ( isset($content_struct['wp_author_id']) && ($user->ID != $content_struct['wp_author_id']) ) { + switch ( $post_type ) { + case 'post': + if ( !current_user_can('edit_others_posts') ) + return new IXR_Error( 401, __( 'You are not allowed to change the post author as this user.' ) ); + break; + case 'page': + if ( !current_user_can('edit_others_pages') ) + return new IXR_Error( 401, __( 'You are not allowed to change the page author as this user.' ) ); + break; + default: + return new IXR_Error( 401, __( 'Invalid post type' ) ); + break; + } + $post_author = $content_struct['wp_author_id']; + } + + if ( isset($content_struct['mt_allow_comments']) ) { + if ( !is_numeric($content_struct['mt_allow_comments']) ) { + switch ( $content_struct['mt_allow_comments'] ) { + case 'closed': + $comment_status = 'closed'; + break; + case 'open': + $comment_status = 'open'; + break; + default: + $comment_status = get_option('default_comment_status'); + break; + } + } else { + switch ( (int) $content_struct['mt_allow_comments'] ) { + case 0: + case 2: + $comment_status = 'closed'; + break; + case 1: + $comment_status = 'open'; + break; + default: + $comment_status = get_option('default_comment_status'); + break; + } + } + } + + if ( isset($content_struct['mt_allow_pings']) ) { + if ( !is_numeric($content_struct['mt_allow_pings']) ) { + switch ( $content_struct['mt_allow_pings'] ) { + case 'closed': + $ping_status = 'closed'; + break; + case 'open': + $ping_status = 'open'; + break; + default: + $ping_status = get_option('default_ping_status'); + break; + } + } else { + switch ( (int) $content_struct["mt_allow_pings"] ) { + case 0: + $ping_status = 'closed'; + break; + case 1: + $ping_status = 'open'; + break; + default: + $ping_status = get_option('default_ping_status'); + break; + } + } + } + + if ( isset( $content_struct['title'] ) ) + $post_title = $content_struct['title']; + + if ( isset( $content_struct['description'] ) ) + $post_content = $content_struct['description']; + + $post_category = array(); + if ( isset( $content_struct['categories'] ) ) { + $catnames = $content_struct['categories']; + if ( is_array($catnames) ) { + foreach ($catnames as $cat) { + $post_category[] = get_cat_ID($cat); + } + } + } + + if ( isset( $content_struct['mt_excerpt'] ) ) + $post_excerpt = $content_struct['mt_excerpt']; + + $post_more = isset( $content_struct['mt_text_more'] ) ? $content_struct['mt_text_more'] : null; + + $post_status = $publish ? 'publish' : 'draft'; + if ( isset( $content_struct["{$post_type}_status"] ) ) { + switch( $content_struct["{$post_type}_status"] ) { + case 'draft': + case 'pending': + case 'private': + case 'publish': + $post_status = $content_struct["{$post_type}_status"]; + break; + default: + $post_status = $publish ? 'publish' : 'draft'; + break; + } + } + + $tags_input = isset( $content_struct['mt_keywords'] ) ? $content_struct['mt_keywords'] : null; + + if ( ('publish' == $post_status) ) { + if ( ( 'page' == $post_type ) && !current_user_can('publish_pages') ) + return new IXR_Error(401, __('Sorry, you do not have the right to publish this page.')); + else if ( !current_user_can('publish_posts') ) + return new IXR_Error(401, __('Sorry, you do not have the right to publish this post.')); + } + + if ( $post_more ) + $post_content = $post_content . "" . $post_more; + + $to_ping = null; + if ( isset( $content_struct['mt_tb_ping_urls'] ) ) { + $to_ping = $content_struct['mt_tb_ping_urls']; + if ( is_array($to_ping) ) + $to_ping = implode(' ', $to_ping); + } + + // Do some timestamp voodoo + if ( !empty( $content_struct['date_created_gmt'] ) ) + // We know this is supposed to be GMT, so we're going to slap that Z on there by force + $dateCreated = rtrim( $content_struct['date_created_gmt']->getIso(), 'Z' ) . 'Z'; + elseif ( !empty( $content_struct['dateCreated']) ) + $dateCreated = $content_struct['dateCreated']->getIso(); + + if ( !empty( $dateCreated ) ) { + $post_date = get_date_from_gmt(iso8601_to_datetime($dateCreated)); + $post_date_gmt = iso8601_to_datetime($dateCreated, 'GMT'); + } else { + $post_date = $postdata['post_date']; + $post_date_gmt = $postdata['post_date_gmt']; + } + + // We've got all the data -- post it: + $newpost = compact('ID', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt', 'comment_status', 'ping_status', 'post_date', 'post_date_gmt', 'to_ping', 'post_name', 'post_password', 'post_parent', 'menu_order', 'post_author', 'tags_input', 'page_template'); + + $result = wp_update_post($newpost, true); + if ( is_wp_error( $result ) ) + return new IXR_Error(500, $result->get_error_message()); + + if ( !$result ) + return new IXR_Error(500, __('Sorry, your entry could not be edited. Something wrong happened.')); + + // Only posts can be sticky + if ( $post_type == 'post' && isset( $content_struct['sticky'] ) ) { + if ( $content_struct['sticky'] == true ) + stick_post( $post_ID ); + elseif ( $content_struct['sticky'] == false ) + unstick_post( $post_ID ); + } + + if ( isset($content_struct['custom_fields']) ) + $this->set_custom_fields($post_ID, $content_struct['custom_fields']); + + if ( isset ( $content_struct['wp_post_thumbnail'] ) ) { + // empty value deletes, non-empty value adds/updates + if ( empty( $content_struct['wp_post_thumbnail'] ) ) { + delete_post_thumbnail( $post_ID ); + } else { + if ( set_post_thumbnail( $post_ID, $content_struct['wp_post_thumbnail'] ) === false ) + return new IXR_Error( 404, __( 'Invalid attachment ID.' ) ); + } + unset( $content_struct['wp_post_thumbnail'] ); + } + + // Handle enclosures + $thisEnclosure = isset($content_struct['enclosure']) ? $content_struct['enclosure'] : null; + $this->add_enclosure_if_new($post_ID, $thisEnclosure); + + $this->attach_uploads( $ID, $post_content ); + + // Handle post formats if assigned, validation is handled + // earlier in this function + if ( isset( $content_struct['wp_post_format'] ) ) + set_post_format( $post_ID, $content_struct['wp_post_format'] ); + + /** + * Fires after a post has been successfully updated via the XML-RPC MovableType API. + * + * @since 3.4.0 + * + * @param int $post_ID ID of the updated post. + * @param array $args An array of arguments to update the post. + */ + do_action( 'xmlrpc_call_success_mw_editPost', $post_ID, $args ); + + return true; + } + + /** + * Retrieve post. + * + * @since 1.5.0 + * + * @param array $args Method parameters. + * @return array|IXR_Error + */ + public function mw_getPost($args) { + + $this->escape($args); + + $post_ID = (int) $args[0]; + $username = $args[1]; + $password = $args[2]; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + $postdata = get_post($post_ID, ARRAY_A); + if ( ! $postdata ) + return new IXR_Error( 404, __( 'Invalid post ID.' ) ); + + if ( !current_user_can( 'edit_post', $post_ID ) ) + return new IXR_Error( 401, __( 'Sorry, you cannot edit this post.' ) ); + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'metaWeblog.getPost' ); + + if ($postdata['post_date'] != '') { + $post_date = $this->_convert_date( $postdata['post_date'] ); + $post_date_gmt = $this->_convert_date_gmt( $postdata['post_date_gmt'], $postdata['post_date'] ); + $post_modified = $this->_convert_date( $postdata['post_modified'] ); + $post_modified_gmt = $this->_convert_date_gmt( $postdata['post_modified_gmt'], $postdata['post_modified'] ); + + $categories = array(); + $catids = wp_get_post_categories($post_ID); + foreach($catids as $catid) + $categories[] = get_cat_name($catid); + + $tagnames = array(); + $tags = wp_get_post_tags( $post_ID ); + if ( !empty( $tags ) ) { + foreach ( $tags as $tag ) + $tagnames[] = $tag->name; + $tagnames = implode( ', ', $tagnames ); + } else { + $tagnames = ''; + } + + $post = get_extended($postdata['post_content']); + $link = post_permalink($postdata['ID']); + + // Get the author info. + $author = get_userdata($postdata['post_author']); + + $allow_comments = ('open' == $postdata['comment_status']) ? 1 : 0; + $allow_pings = ('open' == $postdata['ping_status']) ? 1 : 0; + + // Consider future posts as published + if ( $postdata['post_status'] === 'future' ) + $postdata['post_status'] = 'publish'; + + // Get post format + $post_format = get_post_format( $post_ID ); + if ( empty( $post_format ) ) + $post_format = 'standard'; + + $sticky = false; + if ( is_sticky( $post_ID ) ) + $sticky = true; + + $enclosure = array(); + foreach ( (array) get_post_custom($post_ID) as $key => $val) { + if ($key == 'enclosure') { + foreach ( (array) $val as $enc ) { + $encdata = explode("\n", $enc); + $enclosure['url'] = trim(htmlspecialchars($encdata[0])); + $enclosure['length'] = (int) trim($encdata[1]); + $enclosure['type'] = trim($encdata[2]); + break 2; + } + } + } + + $resp = array( + 'dateCreated' => $post_date, + 'userid' => $postdata['post_author'], + 'postid' => $postdata['ID'], + 'description' => $post['main'], + 'title' => $postdata['post_title'], + 'link' => $link, + 'permaLink' => $link, + // commented out because no other tool seems to use this + // 'content' => $entry['post_content'], + 'categories' => $categories, + 'mt_excerpt' => $postdata['post_excerpt'], + 'mt_text_more' => $post['extended'], + 'wp_more_text' => $post['more_text'], + 'mt_allow_comments' => $allow_comments, + 'mt_allow_pings' => $allow_pings, + 'mt_keywords' => $tagnames, + 'wp_slug' => $postdata['post_name'], + 'wp_password' => $postdata['post_password'], + 'wp_author_id' => (string) $author->ID, + 'wp_author_display_name' => $author->display_name, + 'date_created_gmt' => $post_date_gmt, + 'post_status' => $postdata['post_status'], + 'custom_fields' => $this->get_custom_fields($post_ID), + 'wp_post_format' => $post_format, + 'sticky' => $sticky, + 'date_modified' => $post_modified, + 'date_modified_gmt' => $post_modified_gmt + ); + + if ( !empty($enclosure) ) $resp['enclosure'] = $enclosure; + + $resp['wp_post_thumbnail'] = get_post_thumbnail_id( $postdata['ID'] ); + + return $resp; + } else { + return new IXR_Error(404, __('Sorry, no such post.')); + } + } + + /** + * Retrieve list of recent posts. + * + * @since 1.5.0 + * + * @param array $args Method parameters. + * @return array|IXR_Error + */ + public function mw_getRecentPosts($args) { + + $this->escape($args); + + $username = $args[1]; + $password = $args[2]; + if ( isset( $args[3] ) ) + $query = array( 'numberposts' => absint( $args[3] ) ); + else + $query = array(); + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + if ( ! current_user_can( 'edit_posts' ) ) + return new IXR_Error( 401, __( 'Sorry, you cannot edit posts on this site.' ) ); + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'metaWeblog.getRecentPosts' ); + + $posts_list = wp_get_recent_posts( $query ); + + if ( !$posts_list ) + return array(); + + $recent_posts = array(); + foreach ($posts_list as $entry) { + if ( !current_user_can( 'edit_post', $entry['ID'] ) ) + continue; + + $post_date = $this->_convert_date( $entry['post_date'] ); + $post_date_gmt = $this->_convert_date_gmt( $entry['post_date_gmt'], $entry['post_date'] ); + $post_modified = $this->_convert_date( $entry['post_modified'] ); + $post_modified_gmt = $this->_convert_date_gmt( $entry['post_modified_gmt'], $entry['post_modified'] ); + + $categories = array(); + $catids = wp_get_post_categories($entry['ID']); + foreach( $catids as $catid ) + $categories[] = get_cat_name($catid); + + $tagnames = array(); + $tags = wp_get_post_tags( $entry['ID'] ); + if ( !empty( $tags ) ) { + foreach ( $tags as $tag ) { + $tagnames[] = $tag->name; + } + $tagnames = implode( ', ', $tagnames ); + } else { + $tagnames = ''; + } + + $post = get_extended($entry['post_content']); + $link = post_permalink($entry['ID']); + + // Get the post author info. + $author = get_userdata($entry['post_author']); + + $allow_comments = ('open' == $entry['comment_status']) ? 1 : 0; + $allow_pings = ('open' == $entry['ping_status']) ? 1 : 0; + + // Consider future posts as published + if ( $entry['post_status'] === 'future' ) + $entry['post_status'] = 'publish'; + + // Get post format + $post_format = get_post_format( $entry['ID'] ); + if ( empty( $post_format ) ) + $post_format = 'standard'; + + $recent_posts[] = array( + 'dateCreated' => $post_date, + 'userid' => $entry['post_author'], + 'postid' => (string) $entry['ID'], + 'description' => $post['main'], + 'title' => $entry['post_title'], + 'link' => $link, + 'permaLink' => $link, + // commented out because no other tool seems to use this + // 'content' => $entry['post_content'], + 'categories' => $categories, + 'mt_excerpt' => $entry['post_excerpt'], + 'mt_text_more' => $post['extended'], + 'wp_more_text' => $post['more_text'], + 'mt_allow_comments' => $allow_comments, + 'mt_allow_pings' => $allow_pings, + 'mt_keywords' => $tagnames, + 'wp_slug' => $entry['post_name'], + 'wp_password' => $entry['post_password'], + 'wp_author_id' => (string) $author->ID, + 'wp_author_display_name' => $author->display_name, + 'date_created_gmt' => $post_date_gmt, + 'post_status' => $entry['post_status'], + 'custom_fields' => $this->get_custom_fields($entry['ID']), + 'wp_post_format' => $post_format, + 'date_modified' => $post_modified, + 'date_modified_gmt' => $post_modified_gmt, + 'sticky' => ( $entry['post_type'] === 'post' && is_sticky( $entry['ID'] ) ), + 'wp_post_thumbnail' => get_post_thumbnail_id( $entry['ID'] ) + ); + } + + return $recent_posts; + } + + /** + * Retrieve the list of categories on a given blog. + * + * @since 1.5.0 + * + * @param array $args Method parameters. + * @return array|IXR_Error + */ + public function mw_getCategories($args) { + + $this->escape($args); + + $username = $args[1]; + $password = $args[2]; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + if ( !current_user_can( 'edit_posts' ) ) + return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) ); + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'metaWeblog.getCategories' ); + + $categories_struct = array(); + + if ( $cats = get_categories(array('get' => 'all')) ) { + foreach ( $cats as $cat ) { + $struct = array(); + $struct['categoryId'] = $cat->term_id; + $struct['parentId'] = $cat->parent; + $struct['description'] = $cat->name; + $struct['categoryDescription'] = $cat->description; + $struct['categoryName'] = $cat->name; + $struct['htmlUrl'] = esc_html(get_category_link($cat->term_id)); + $struct['rssUrl'] = esc_html(get_category_feed_link($cat->term_id, 'rss2')); + + $categories_struct[] = $struct; + } + } + + return $categories_struct; + } + + /** + * Uploads a file, following your settings. + * + * Adapted from a patch by Johann Richard. + * + * @link http://mycvs.org/archives/2004/06/30/file-upload-to-wordpress-in-ecto/ + * + * @since 1.5.0 + * + * @param array $args Method parameters. + * @return array|IXR_Error + */ + public function mw_newMediaObject($args) { + global $wpdb; + + $username = $this->escape($args[1]); + $password = $this->escape($args[2]); + $data = $args[3]; + + $name = sanitize_file_name( $data['name'] ); + $type = $data['type']; + $bits = $data['bits']; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'metaWeblog.newMediaObject' ); + + if ( !current_user_can('upload_files') ) { + $this->error = new IXR_Error( 401, __( 'You do not have permission to upload files.' ) ); + return $this->error; + } + + /** + * Filter whether to preempt the XML-RPC media upload. + * + * Passing a truthy value will effectively short-circuit the media upload, + * returning that value as a 500 error instead. + * + * @since 2.1.0 + * + * @param bool $error Whether to pre-empt the media upload. Default false. + */ + if ( $upload_err = apply_filters( 'pre_upload_error', false ) ) { + return new IXR_Error( 500, $upload_err ); + } + + if ( !empty($data['overwrite']) && ($data['overwrite'] == true) ) { + // Get postmeta info on the object. + $old_file = $wpdb->get_row(" + SELECT ID + FROM {$wpdb->posts} + WHERE post_title = '{$name}' + AND post_type = 'attachment' + "); + + // Delete previous file. + wp_delete_attachment($old_file->ID); + + // Make sure the new name is different by pre-pending the + // previous post id. + $filename = preg_replace('/^wpid\d+-/', '', $name); + $name = "wpid{$old_file->ID}-{$filename}"; + } + + $upload = wp_upload_bits($name, null, $bits); + if ( ! empty($upload['error']) ) { + $errorString = sprintf(__('Could not write file %1$s (%2$s)'), $name, $upload['error']); + return new IXR_Error(500, $errorString); + } + // Construct the attachment array + $post_id = 0; + if ( ! empty( $data['post_id'] ) ) { + $post_id = (int) $data['post_id']; + + if ( ! current_user_can( 'edit_post', $post_id ) ) + return new IXR_Error( 401, __( 'Sorry, you cannot edit this post.' ) ); + } + $attachment = array( + 'post_title' => $name, + 'post_content' => '', + 'post_type' => 'attachment', + 'post_parent' => $post_id, + 'post_mime_type' => $type, + 'guid' => $upload[ 'url' ] + ); + + // Save the data + $id = wp_insert_attachment( $attachment, $upload[ 'file' ], $post_id ); + wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $upload['file'] ) ); + + /** + * Fires after a new attachment has been added via the XML-RPC MovableType API. + * + * @since 3.4.0 + * + * @param int $id ID of the new attachment. + * @param array $args An array of arguments to add the attachment. + */ + do_action( 'xmlrpc_call_success_mw_newMediaObject', $id, $args ); + + $struct = array( + 'id' => strval( $id ), + 'file' => $name, + 'url' => $upload[ 'url' ], + 'type' => $type + ); + + /** This filter is documented in wp-admin/includes/file.php */ + return apply_filters( 'wp_handle_upload', $struct, 'upload' ); + } + + /* MovableType API functions + * specs on http://www.movabletype.org/docs/mtmanual_programmatic.html + */ + + /** + * Retrieve the post titles of recent posts. + * + * @since 1.5.0 + * + * @param array $args Method parameters. + * @return array|IXR_Error + */ + public function mt_getRecentPostTitles($args) { + + $this->escape($args); + + $username = $args[1]; + $password = $args[2]; + if ( isset( $args[3] ) ) + $query = array( 'numberposts' => absint( $args[3] ) ); + else + $query = array(); + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'mt.getRecentPostTitles' ); + + $posts_list = wp_get_recent_posts( $query ); + + if ( !$posts_list ) { + $this->error = new IXR_Error(500, __('Either there are no posts, or something went wrong.')); + return $this->error; + } + + $recent_posts = array(); + + foreach ($posts_list as $entry) { + if ( !current_user_can( 'edit_post', $entry['ID'] ) ) + continue; + + $post_date = $this->_convert_date( $entry['post_date'] ); + $post_date_gmt = $this->_convert_date_gmt( $entry['post_date_gmt'], $entry['post_date'] ); + + $recent_posts[] = array( + 'dateCreated' => $post_date, + 'userid' => $entry['post_author'], + 'postid' => (string) $entry['ID'], + 'title' => $entry['post_title'], + 'post_status' => $entry['post_status'], + 'date_created_gmt' => $post_date_gmt + ); + } + + return $recent_posts; + } + + /** + * Retrieve list of all categories on blog. + * + * @since 1.5.0 + * + * @param array $args Method parameters. + * @return array|IXR_Error + */ + public function mt_getCategoryList($args) { + + $this->escape($args); + + $username = $args[1]; + $password = $args[2]; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + if ( !current_user_can( 'edit_posts' ) ) + return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) ); + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'mt.getCategoryList' ); + + $categories_struct = array(); + + if ( $cats = get_categories(array('hide_empty' => 0, 'hierarchical' => 0)) ) { + foreach ( $cats as $cat ) { + $struct = array(); + $struct['categoryId'] = $cat->term_id; + $struct['categoryName'] = $cat->name; + + $categories_struct[] = $struct; + } + } + + return $categories_struct; + } + + /** + * Retrieve post categories. + * + * @since 1.5.0 + * + * @param array $args Method parameters. + * @return array|IXR_Error + */ + public function mt_getPostCategories($args) { + + $this->escape($args); + + $post_ID = (int) $args[0]; + $username = $args[1]; + $password = $args[2]; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + if ( ! get_post( $post_ID ) ) + return new IXR_Error( 404, __( 'Invalid post ID.' ) ); + + if ( !current_user_can( 'edit_post', $post_ID ) ) + return new IXR_Error( 401, __( 'Sorry, you can not edit this post.' ) ); + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'mt.getPostCategories' ); + + $categories = array(); + $catids = wp_get_post_categories(intval($post_ID)); + // first listed category will be the primary category + $isPrimary = true; + foreach ( $catids as $catid ) { + $categories[] = array( + 'categoryName' => get_cat_name($catid), + 'categoryId' => (string) $catid, + 'isPrimary' => $isPrimary + ); + $isPrimary = false; + } + + return $categories; + } + + /** + * Sets categories for a post. + * + * @since 1.5.0 + * + * @param array $args Method parameters. + * @return bool|IXR_Error True on success. + */ + public function mt_setPostCategories($args) { + + $this->escape($args); + + $post_ID = (int) $args[0]; + $username = $args[1]; + $password = $args[2]; + $categories = $args[3]; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'mt.setPostCategories' ); + + if ( ! get_post( $post_ID ) ) + return new IXR_Error( 404, __( 'Invalid post ID.' ) ); + + if ( !current_user_can('edit_post', $post_ID) ) + return new IXR_Error(401, __('Sorry, you cannot edit this post.')); + + $catids = array(); + foreach ( $categories as $cat ) { + $catids[] = $cat['categoryId']; + } + + wp_set_post_categories($post_ID, $catids); + + return true; + } + + /** + * Retrieve an array of methods supported by this server. + * + * @since 1.5.0 + * + * @param array $args Method parameters. + * @return array + */ + public function mt_supportedMethods($args) { + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'mt.supportedMethods' ); + + $supported_methods = array(); + foreach ( $this->methods as $key => $value ) { + $supported_methods[] = $key; + } + + return $supported_methods; + } + + /** + * Retrieve an empty array because we don't support per-post text filters. + * + * @since 1.5.0 + * + * @param array $args Method parameters. + */ + public function mt_supportedTextFilters($args) { + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'mt.supportedTextFilters' ); + + /** + * Filter the MoveableType text filters list for XML-RPC. + * + * @since 2.2.0 + * + * @param array $filters An array of text filters. + */ + return apply_filters( 'xmlrpc_text_filters', array() ); + } + + /** + * Retrieve trackbacks sent to a given post. + * + * @since 1.5.0 + * + * @param array $args Method parameters. + * @return array|IXR_Error + */ + public function mt_getTrackbackPings($args) { + + global $wpdb; + + $post_ID = intval($args); + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'mt.getTrackbackPings' ); + + $actual_post = get_post($post_ID, ARRAY_A); + + if ( !$actual_post ) + return new IXR_Error(404, __('Sorry, no such post.')); + + $comments = $wpdb->get_results( $wpdb->prepare("SELECT comment_author_url, comment_content, comment_author_IP, comment_type FROM $wpdb->comments WHERE comment_post_ID = %d", $post_ID) ); + + if ( !$comments ) + return array(); + + $trackback_pings = array(); + foreach ( $comments as $comment ) { + if ( 'trackback' == $comment->comment_type ) { + $content = $comment->comment_content; + $title = substr($content, 8, (strpos($content, '') - 8)); + $trackback_pings[] = array( + 'pingTitle' => $title, + 'pingURL' => $comment->comment_author_url, + 'pingIP' => $comment->comment_author_IP + ); + } + } + + return $trackback_pings; + } + + /** + * Sets a post's publish status to 'publish'. + * + * @since 1.5.0 + * + * @param array $args Method parameters. + * @return int|IXR_Error + */ + public function mt_publishPost($args) { + + $this->escape($args); + + $post_ID = (int) $args[0]; + $username = $args[1]; + $password = $args[2]; + + if ( !$user = $this->login($username, $password) ) + return $this->error; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'mt.publishPost' ); + + $postdata = get_post($post_ID, ARRAY_A); + if ( ! $postdata ) + return new IXR_Error( 404, __( 'Invalid post ID.' ) ); + + if ( !current_user_can('publish_posts') || !current_user_can('edit_post', $post_ID) ) + return new IXR_Error(401, __('Sorry, you cannot publish this post.')); + + $postdata['post_status'] = 'publish'; + + // retain old cats + $cats = wp_get_post_categories($post_ID); + $postdata['post_category'] = $cats; + $this->escape($postdata); + + $result = wp_update_post($postdata); + + return $result; + } + + /* PingBack functions + * specs on www.hixie.ch/specs/pingback/pingback + */ + + /** + * Retrieves a pingback and registers it. + * + * @since 1.5.0 + * + * @param array $args Method parameters. + * @return string|IXR_Error + */ + public function pingback_ping($args) { + global $wpdb; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'pingback.ping' ); + + $this->escape($args); + + $pagelinkedfrom = $args[0]; + $pagelinkedto = $args[1]; + + $title = ''; + + $pagelinkedfrom = str_replace('&', '&', $pagelinkedfrom); + $pagelinkedto = str_replace('&', '&', $pagelinkedto); + $pagelinkedto = str_replace('&', '&', $pagelinkedto); + + /** + * Filter the pingback source URI. + * + * @since 3.6.0 + * + * @param string $pagelinkedfrom URI of the page linked from. + * @param string $pagelinkedto URI of the page linked to. + */ + $pagelinkedfrom = apply_filters( 'pingback_ping_source_uri', $pagelinkedfrom, $pagelinkedto ); + + if ( ! $pagelinkedfrom ) + return $this->pingback_error( 0, __( 'A valid URL was not provided.' ) ); + + // Check if the page linked to is in our site + $pos1 = strpos($pagelinkedto, str_replace(array('http://www.','http://','https://www.','https://'), '', get_option('home'))); + if ( !$pos1 ) + return $this->pingback_error( 0, __( 'Is there no link to us?' ) ); + + // let's find which post is linked to + // FIXME: does url_to_postid() cover all these cases already? + // if so, then let's use it and drop the old code. + $urltest = parse_url($pagelinkedto); + if ( $post_ID = url_to_postid($pagelinkedto) ) { + // $way + } elseif ( isset( $urltest['path'] ) && preg_match('#p/[0-9]{1,}#', $urltest['path'], $match) ) { + // the path defines the post_ID (archives/p/XXXX) + $blah = explode('/', $match[0]); + $post_ID = (int) $blah[1]; + } elseif ( isset( $urltest['query'] ) && preg_match('#p=[0-9]{1,}#', $urltest['query'], $match) ) { + // the querystring defines the post_ID (?p=XXXX) + $blah = explode('=', $match[0]); + $post_ID = (int) $blah[1]; + } elseif ( isset($urltest['fragment']) ) { + // an #anchor is there, it's either... + if ( intval($urltest['fragment']) ) { + // ...an integer #XXXX (simplest case) + $post_ID = (int) $urltest['fragment']; + } elseif ( preg_match('/post-[0-9]+/',$urltest['fragment']) ) { + // ...a post id in the form 'post-###' + $post_ID = preg_replace('/[^0-9]+/', '', $urltest['fragment']); + } elseif ( is_string($urltest['fragment']) ) { + // ...or a string #title, a little more complicated + $title = preg_replace('/[^a-z0-9]/i', '.', $urltest['fragment']); + $sql = $wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE post_title RLIKE %s", $title ); + if (! ($post_ID = $wpdb->get_var($sql)) ) { + // returning unknown error '0' is better than die()ing + return $this->pingback_error( 0, '' ); + } + } + } else { + // TODO: Attempt to extract a post ID from the given URL + return $this->pingback_error( 33, __('The specified target URL cannot be used as a target. It either doesn’t exist, or it is not a pingback-enabled resource.' ) ); + } + $post_ID = (int) $post_ID; + + $post = get_post($post_ID); + + if ( !$post ) // Post_ID not found + return $this->pingback_error( 33, __( 'The specified target URL cannot be used as a target. It either doesn’t exist, or it is not a pingback-enabled resource.' ) ); + + if ( $post_ID == url_to_postid($pagelinkedfrom) ) + return $this->pingback_error( 0, __( 'The source URL and the target URL cannot both point to the same resource.' ) ); + + // Check if pings are on + if ( !pings_open($post) ) + return $this->pingback_error( 33, __( 'The specified target URL cannot be used as a target. It either doesn’t exist, or it is not a pingback-enabled resource.' ) ); + + // Let's check that the remote site didn't already pingback this entry + if ( $wpdb->get_results( $wpdb->prepare("SELECT * FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_author_url = %s", $post_ID, $pagelinkedfrom) ) ) + return $this->pingback_error( 48, __( 'The pingback has already been registered.' ) ); + + // very stupid, but gives time to the 'from' server to publish ! + sleep(1); + + $remote_ip = preg_replace( '/[^0-9a-fA-F:., ]/', '', $_SERVER['REMOTE_ADDR'] ); + + /** This filter is documented in wp-includes/class-http.php */ + $user_agent = apply_filters( 'http_headers_useragent', 'WordPress/' . $GLOBALS['wp_version'] . '; ' . get_bloginfo( 'url' ) ); + + // Let's check the remote site + $http_api_args = array( + 'timeout' => 10, + 'redirection' => 0, + 'limit_response_size' => 153600, // 150 KB + 'user-agent' => "$user_agent; verifying pingback from $remote_ip", + 'headers' => array( + 'X-Pingback-Forwarded-For' => $remote_ip, + ), + ); + $request = wp_safe_remote_get( $pagelinkedfrom, $http_api_args ); + $linea = wp_remote_retrieve_body( $request ); + + if ( !$linea ) + return $this->pingback_error( 16, __( 'The source URL does not exist.' ) ); + + /** + * Filter the pingback remote source. + * + * @since 2.5.0 + * + * @param string $linea Response object for the page linked from. + * @param string $pagelinkedto URL of the page linked to. + */ + $linea = apply_filters( 'pre_remote_source', $linea, $pagelinkedto ); + + // Work around bug in strip_tags(): + $linea = str_replace(']*>/", "\n\n", $linea ); + + preg_match('|([^<]*?)|is', $linea, $matchtitle); + $title = $matchtitle[1]; + if ( empty( $title ) ) + return $this->pingback_error( 32, __('We cannot find a title on that page.' ) ); + + $linea = strip_tags( $linea, '' ); // just keep the tag we need + + $p = explode( "\n\n", $linea ); + + $preg_target = preg_quote($pagelinkedto, '|'); + + foreach ( $p as $para ) { + if ( strpos($para, $pagelinkedto) !== false ) { // it exists, but is it a link? + preg_match("|]+?".$preg_target."[^>]*>([^>]+?)|", $para, $context); + + // If the URL isn't in a link context, keep looking + if ( empty($context) ) + continue; + + // We're going to use this fake tag to mark the context in a bit + // the marker is needed in case the link text appears more than once in the paragraph + $excerpt = preg_replace('|\|', '', $para); + + // prevent really long link text + if ( strlen($context[1]) > 100 ) + $context[1] = substr($context[1], 0, 100) . '…'; + + $marker = ''.$context[1].''; // set up our marker + $excerpt= str_replace($context[0], $marker, $excerpt); // swap out the link for our marker + $excerpt = strip_tags($excerpt, ''); // strip all tags but our context marker + $excerpt = trim($excerpt); + $preg_marker = preg_quote($marker, '|'); + $excerpt = preg_replace("|.*?\s(.{0,100}$preg_marker.{0,100})\s.*|s", '$1', $excerpt); + $excerpt = strip_tags($excerpt); // YES, again, to remove the marker wrapper + break; + } + } + + if ( empty($context) ) // Link to target not found + return $this->pingback_error( 17, __( 'The source URL does not contain a link to the target URL, and so cannot be used as a source.' ) ); + + $pagelinkedfrom = str_replace('&', '&', $pagelinkedfrom); + + $context = '[…] ' . esc_html( $excerpt ) . ' […]'; + $pagelinkedfrom = $this->escape( $pagelinkedfrom ); + + $comment_post_ID = (int) $post_ID; + $comment_author = $title; + $comment_author_email = ''; + $this->escape($comment_author); + $comment_author_url = $pagelinkedfrom; + $comment_content = $context; + $this->escape($comment_content); + $comment_type = 'pingback'; + + $commentdata = compact('comment_post_ID', 'comment_author', 'comment_author_url', 'comment_author_email', 'comment_content', 'comment_type'); + + $comment_ID = wp_new_comment($commentdata); + + /** + * Fires after a post pingback has been sent. + * + * @since 0.71 + * + * @param int $comment_ID Comment ID. + */ + do_action( 'pingback_post', $comment_ID ); + + return sprintf(__('Pingback from %1$s to %2$s registered. Keep the web talking! :-)'), $pagelinkedfrom, $pagelinkedto); + } + + /** + * Retrieve array of URLs that pingbacked the given URL. + * + * Specs on http://www.aquarionics.com/misc/archives/blogite/0198.html + * + * @since 1.5.0 + * + * @param array $args Method parameters. + * @return array|IXR_Error + */ + public function pingback_extensions_getPingbacks($args) { + + global $wpdb; + + /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ + do_action( 'xmlrpc_call', 'pingback.extensions.getPingbacks' ); + + $this->escape($args); + + $url = $args; + + $post_ID = url_to_postid($url); + if ( !$post_ID ) { + // We aren't sure that the resource is available and/or pingback enabled + return $this->pingback_error( 33, __( 'The specified target URL cannot be used as a target. It either doesn’t exist, or it is not a pingback-enabled resource.' ) ); + } + + $actual_post = get_post($post_ID, ARRAY_A); + + if ( !$actual_post ) { + // No such post = resource not found + return $this->pingback_error( 32, __('The specified target URL does not exist.' ) ); + } + + $comments = $wpdb->get_results( $wpdb->prepare("SELECT comment_author_url, comment_content, comment_author_IP, comment_type FROM $wpdb->comments WHERE comment_post_ID = %d", $post_ID) ); + + if ( !$comments ) + return array(); + + $pingbacks = array(); + foreach ( $comments as $comment ) { + if ( 'pingback' == $comment->comment_type ) + $pingbacks[] = $comment->comment_author_url; + } + + return $pingbacks; + } + + /** + * @param integer $code + * @param string $message + */ + protected function pingback_error( $code, $message ) { + /** + * Filter the XML-RPC pingback error return. + * + * @since 3.5.1 + * + * @param IXR_Error $error An IXR_Error object containing the error code and message. + */ + return apply_filters( 'xmlrpc_pingback_error', new IXR_Error( $code, $message ) ); + } +} diff --git a/wp-includes/class-wp.php b/wp-includes/class-wp.php new file mode 100644 index 0000000..f8b3f82 --- /dev/null +++ b/wp-includes/class-wp.php @@ -0,0 +1,781 @@ +public_query_vars) ) + $this->public_query_vars[] = $qv; + } + + /** + * Set the value of a query variable. + * + * @since 2.3.0 + * + * @param string $key Query variable name. + * @param mixed $value Query variable value. + */ + public function set_query_var($key, $value) { + $this->query_vars[$key] = $value; + } + + /** + * Parse request to find correct WordPress query. + * + * Sets up the query variables based on the request. There are also many + * filters and actions that can be used to further manipulate the result. + * + * @since 2.0.0 + * + * @param array|string $extra_query_vars Set the extra query variables. + */ + public function parse_request($extra_query_vars = '') { + global $wp_rewrite; + + /** + * Filter whether to parse the request. + * + * @since 3.5.0 + * + * @param bool $bool Whether or not to parse the request. Default true. + * @param WP $this Current WordPress environment instance. + * @param array|string $extra_query_vars Extra passed query variables. + */ + if ( ! apply_filters( 'do_parse_request', true, $this, $extra_query_vars ) ) + return; + + $this->query_vars = array(); + $post_type_query_vars = array(); + + if ( is_array($extra_query_vars) ) + $this->extra_query_vars = & $extra_query_vars; + else if (! empty($extra_query_vars)) + parse_str($extra_query_vars, $this->extra_query_vars); + + // Process PATH_INFO, REQUEST_URI, and 404 for permalinks. + + // Fetch the rewrite rules. + $rewrite = $wp_rewrite->wp_rewrite_rules(); + + if ( ! empty($rewrite) ) { + // If we match a rewrite rule, this will be cleared. + $error = '404'; + $this->did_permalink = true; + + $pathinfo = isset( $_SERVER['PATH_INFO'] ) ? $_SERVER['PATH_INFO'] : ''; + list( $pathinfo ) = explode( '?', $pathinfo ); + $pathinfo = str_replace( "%", "%25", $pathinfo ); + + list( $req_uri ) = explode( '?', $_SERVER['REQUEST_URI'] ); + $self = $_SERVER['PHP_SELF']; + $home_path = trim( parse_url( home_url(), PHP_URL_PATH ), '/' ); + + // Trim path info from the end and the leading home path from the + // front. For path info requests, this leaves us with the requesting + // filename, if any. For 404 requests, this leaves us with the + // requested permalink. + $req_uri = str_replace($pathinfo, '', $req_uri); + $req_uri = trim($req_uri, '/'); + $req_uri = preg_replace("|^$home_path|i", '', $req_uri); + $req_uri = trim($req_uri, '/'); + $pathinfo = trim($pathinfo, '/'); + $pathinfo = preg_replace("|^$home_path|i", '', $pathinfo); + $pathinfo = trim($pathinfo, '/'); + $self = trim($self, '/'); + $self = preg_replace("|^$home_path|i", '', $self); + $self = trim($self, '/'); + + // The requested permalink is in $pathinfo for path info requests and + // $req_uri for other requests. + if ( ! empty($pathinfo) && !preg_match('|^.*' . $wp_rewrite->index . '$|', $pathinfo) ) { + $request = $pathinfo; + } else { + // If the request uri is the index, blank it out so that we don't try to match it against a rule. + if ( $req_uri == $wp_rewrite->index ) + $req_uri = ''; + $request = $req_uri; + } + + $this->request = $request; + + // Look for matches. + $request_match = $request; + if ( empty( $request_match ) ) { + // An empty request could only match against ^$ regex + if ( isset( $rewrite['$'] ) ) { + $this->matched_rule = '$'; + $query = $rewrite['$']; + $matches = array(''); + } + } else { + foreach ( (array) $rewrite as $match => $query ) { + // If the requesting file is the anchor of the match, prepend it to the path info. + if ( ! empty($req_uri) && strpos($match, $req_uri) === 0 && $req_uri != $request ) + $request_match = $req_uri . '/' . $request; + + if ( preg_match("#^$match#", $request_match, $matches) || + preg_match("#^$match#", urldecode($request_match), $matches) ) { + + if ( $wp_rewrite->use_verbose_page_rules && preg_match( '/pagename=\$matches\[([0-9]+)\]/', $query, $varmatch ) ) { + // This is a verbose page match, let's check to be sure about it. + if ( ! get_page_by_path( $matches[ $varmatch[1] ] ) ) + continue; + } + + // Got a match. + $this->matched_rule = $match; + break; + } + } + } + + if ( isset( $this->matched_rule ) ) { + // Trim the query of everything up to the '?'. + $query = preg_replace("!^.+\?!", '', $query); + + // Substitute the substring matches into the query. + $query = addslashes(WP_MatchesMapRegex::apply($query, $matches)); + + $this->matched_query = $query; + + // Parse the query. + parse_str($query, $perma_query_vars); + + // If we're processing a 404 request, clear the error var since we found something. + if ( '404' == $error ) + unset( $error, $_GET['error'] ); + } + + // If req_uri is empty or if it is a request for ourself, unset error. + if ( empty($request) || $req_uri == $self || strpos($_SERVER['PHP_SELF'], 'wp-admin/') !== false ) { + unset( $error, $_GET['error'] ); + + if ( isset($perma_query_vars) && strpos($_SERVER['PHP_SELF'], 'wp-admin/') !== false ) + unset( $perma_query_vars ); + + $this->did_permalink = false; + } + } + + /** + * Filter the query variables whitelist before processing. + * + * Allows (publicly allowed) query vars to be added, removed, or changed prior + * to executing the query. Needed to allow custom rewrite rules using your own arguments + * to work, or any other custom query variables you want to be publicly available. + * + * @since 1.5.0 + * + * @param array $public_query_vars The array of whitelisted query variables. + */ + $this->public_query_vars = apply_filters( 'query_vars', $this->public_query_vars ); + + foreach ( get_post_types( array(), 'objects' ) as $post_type => $t ) + if ( $t->query_var ) + $post_type_query_vars[$t->query_var] = $post_type; + + foreach ( $this->public_query_vars as $wpvar ) { + if ( isset( $this->extra_query_vars[$wpvar] ) ) + $this->query_vars[$wpvar] = $this->extra_query_vars[$wpvar]; + elseif ( isset( $_POST[$wpvar] ) ) + $this->query_vars[$wpvar] = $_POST[$wpvar]; + elseif ( isset( $_GET[$wpvar] ) ) + $this->query_vars[$wpvar] = $_GET[$wpvar]; + elseif ( isset( $perma_query_vars[$wpvar] ) ) + $this->query_vars[$wpvar] = $perma_query_vars[$wpvar]; + + if ( !empty( $this->query_vars[$wpvar] ) ) { + if ( ! is_array( $this->query_vars[$wpvar] ) ) { + $this->query_vars[$wpvar] = (string) $this->query_vars[$wpvar]; + } else { + foreach ( $this->query_vars[$wpvar] as $vkey => $v ) { + if ( !is_object( $v ) ) { + $this->query_vars[$wpvar][$vkey] = (string) $v; + } + } + } + + if ( isset($post_type_query_vars[$wpvar] ) ) { + $this->query_vars['post_type'] = $post_type_query_vars[$wpvar]; + $this->query_vars['name'] = $this->query_vars[$wpvar]; + } + } + } + + // Convert urldecoded spaces back into + + foreach ( get_taxonomies( array() , 'objects' ) as $taxonomy => $t ) + if ( $t->query_var && isset( $this->query_vars[$t->query_var] ) ) + $this->query_vars[$t->query_var] = str_replace( ' ', '+', $this->query_vars[$t->query_var] ); + + // Limit publicly queried post_types to those that are publicly_queryable + if ( isset( $this->query_vars['post_type']) ) { + $queryable_post_types = get_post_types( array('publicly_queryable' => true) ); + if ( ! is_array( $this->query_vars['post_type'] ) ) { + if ( ! in_array( $this->query_vars['post_type'], $queryable_post_types ) ) + unset( $this->query_vars['post_type'] ); + } else { + $this->query_vars['post_type'] = array_intersect( $this->query_vars['post_type'], $queryable_post_types ); + } + } + + foreach ( (array) $this->private_query_vars as $var) { + if ( isset($this->extra_query_vars[$var]) ) + $this->query_vars[$var] = $this->extra_query_vars[$var]; + } + + if ( isset($error) ) + $this->query_vars['error'] = $error; + + /** + * Filter the array of parsed query variables. + * + * @since 2.1.0 + * + * @param array $query_vars The array of requested query variables. + */ + $this->query_vars = apply_filters( 'request', $this->query_vars ); + + /** + * Fires once all query variables for the current request have been parsed. + * + * @since 2.1.0 + * + * @param WP &$this Current WordPress environment instance (passed by reference). + */ + do_action_ref_array( 'parse_request', array( &$this ) ); + } + + /** + * Send additional HTTP headers for caching, content type, etc. + * + * Sets the X-Pingback header, 404 status (if 404), Content-type. If showing + * a feed, it will also send last-modified, etag, and 304 status if needed. + * + * @since 2.0.0 + */ + public function send_headers() { + $headers = array('X-Pingback' => get_bloginfo('pingback_url')); + $status = null; + $exit_required = false; + + if ( is_user_logged_in() ) + $headers = array_merge($headers, wp_get_nocache_headers()); + if ( ! empty( $this->query_vars['error'] ) ) { + $status = (int) $this->query_vars['error']; + if ( 404 === $status ) { + if ( ! is_user_logged_in() ) + $headers = array_merge($headers, wp_get_nocache_headers()); + $headers['Content-Type'] = get_option('html_type') . '; charset=' . get_option('blog_charset'); + } elseif ( in_array( $status, array( 403, 500, 502, 503 ) ) ) { + $exit_required = true; + } + } else if ( empty($this->query_vars['feed']) ) { + $headers['Content-Type'] = get_option('html_type') . '; charset=' . get_option('blog_charset'); + } else { + // We're showing a feed, so WP is indeed the only thing that last changed + if ( !empty($this->query_vars['withcomments']) + || false !== strpos( $this->query_vars['feed'], 'comments-' ) + || ( empty($this->query_vars['withoutcomments']) + && ( !empty($this->query_vars['p']) + || !empty($this->query_vars['name']) + || !empty($this->query_vars['page_id']) + || !empty($this->query_vars['pagename']) + || !empty($this->query_vars['attachment']) + || !empty($this->query_vars['attachment_id']) + ) + ) + ) + $wp_last_modified = mysql2date('D, d M Y H:i:s', get_lastcommentmodified('GMT'), 0).' GMT'; + else + $wp_last_modified = mysql2date('D, d M Y H:i:s', get_lastpostmodified('GMT'), 0).' GMT'; + $wp_etag = '"' . md5($wp_last_modified) . '"'; + $headers['Last-Modified'] = $wp_last_modified; + $headers['ETag'] = $wp_etag; + + // Support for Conditional GET + if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) + $client_etag = wp_unslash( $_SERVER['HTTP_IF_NONE_MATCH'] ); + else $client_etag = false; + + $client_last_modified = empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? '' : trim($_SERVER['HTTP_IF_MODIFIED_SINCE']); + // If string is empty, return 0. If not, attempt to parse into a timestamp + $client_modified_timestamp = $client_last_modified ? strtotime($client_last_modified) : 0; + + // Make a timestamp for our most recent modification... + $wp_modified_timestamp = strtotime($wp_last_modified); + + if ( ($client_last_modified && $client_etag) ? + (($client_modified_timestamp >= $wp_modified_timestamp) && ($client_etag == $wp_etag)) : + (($client_modified_timestamp >= $wp_modified_timestamp) || ($client_etag == $wp_etag)) ) { + $status = 304; + $exit_required = true; + } + } + + /** + * Filter the HTTP headers before they're sent to the browser. + * + * @since 2.8.0 + * + * @param array $headers The list of headers to be sent. + * @param WP $this Current WordPress environment instance. + */ + $headers = apply_filters( 'wp_headers', $headers, $this ); + + if ( ! empty( $status ) ) + status_header( $status ); + + // If Last-Modified is set to false, it should not be sent (no-cache situation). + if ( isset( $headers['Last-Modified'] ) && false === $headers['Last-Modified'] ) { + unset( $headers['Last-Modified'] ); + + // In PHP 5.3+, make sure we are not sending a Last-Modified header. + if ( function_exists( 'header_remove' ) ) { + @header_remove( 'Last-Modified' ); + } else { + // In PHP 5.2, send an empty Last-Modified header, but only as a + // last resort to override a header already sent. #WP23021 + foreach ( headers_list() as $header ) { + if ( 0 === stripos( $header, 'Last-Modified' ) ) { + $headers['Last-Modified'] = ''; + break; + } + } + } + } + + foreach( (array) $headers as $name => $field_value ) + @header("{$name}: {$field_value}"); + + if ( $exit_required ) + exit(); + + /** + * Fires once the requested HTTP headers for caching, content type, etc. have been sent. + * + * @since 2.1.0 + * + * @param WP &$this Current WordPress environment instance (passed by reference). + */ + do_action_ref_array( 'send_headers', array( &$this ) ); + } + + /** + * Sets the query string property based off of the query variable property. + * + * The 'query_string' filter is deprecated, but still works. Plugins should + * use the 'request' filter instead. + * + * @since 2.0.0 + */ + public function build_query_string() { + $this->query_string = ''; + foreach ( (array) array_keys($this->query_vars) as $wpvar) { + if ( '' != $this->query_vars[$wpvar] ) { + $this->query_string .= (strlen($this->query_string) < 1) ? '' : '&'; + if ( !is_scalar($this->query_vars[$wpvar]) ) // Discard non-scalars. + continue; + $this->query_string .= $wpvar . '=' . rawurlencode($this->query_vars[$wpvar]); + } + } + + if ( has_filter( 'query_string' ) ) { // Don't bother filtering and parsing if no plugins are hooked in. + /** + * Filter the query string before parsing. + * + * @since 1.5.0 + * @deprecated 2.1.0 Use 'query_vars' or 'request' filters instead. + * + * @param string $query_string The query string to modify. + */ + $this->query_string = apply_filters( 'query_string', $this->query_string ); + parse_str($this->query_string, $this->query_vars); + } + } + + /** + * Set up the WordPress Globals. + * + * The query_vars property will be extracted to the GLOBALS. So care should + * be taken when naming global variables that might interfere with the + * WordPress environment. + * + * @global string $query_string Query string for the loop. + * @global array $posts The found posts. + * @global WP_Post|null $post The current post, if available. + * @global string $request The SQL statement for the request. + * @global int $more Only set, if single page or post. + * @global int $single If single page or post. Only set, if single page or post. + * @global WP_User $authordata Only set, if author archive. + * + * @since 2.0.0 + */ + public function register_globals() { + global $wp_query; + + // Extract updated query vars back into global namespace. + foreach ( (array) $wp_query->query_vars as $key => $value ) { + $GLOBALS[ $key ] = $value; + } + + $GLOBALS['query_string'] = $this->query_string; + $GLOBALS['posts'] = & $wp_query->posts; + $GLOBALS['post'] = isset( $wp_query->post ) ? $wp_query->post : null; + $GLOBALS['request'] = $wp_query->request; + + if ( $wp_query->is_single() || $wp_query->is_page() ) { + $GLOBALS['more'] = 1; + $GLOBALS['single'] = 1; + } + + if ( $wp_query->is_author() && isset( $wp_query->post ) ) + $GLOBALS['authordata'] = get_userdata( $wp_query->post->post_author ); + } + + /** + * Set up the current user. + * + * @since 2.0.0 + */ + public function init() { + wp_get_current_user(); + } + + /** + * Set up the Loop based on the query variables. + * + * @since 2.0.0 + */ + public function query_posts() { + global $wp_the_query; + $this->build_query_string(); + $wp_the_query->query($this->query_vars); + } + + /** + * Set the Headers for 404, if nothing is found for requested URL. + * + * Issue a 404 if a request doesn't match any posts and doesn't match + * any object (e.g. an existing-but-empty category, tag, author) and a 404 was not already + * issued, and if the request was not a search or the homepage. + * + * Otherwise, issue a 200. + * + * @since 2.0.0 + */ + public function handle_404() { + global $wp_query; + + // If we've already issued a 404, bail. + if ( is_404() ) + return; + + // Never 404 for the admin, robots, or if we found posts. + if ( is_admin() || is_robots() || $wp_query->posts ) { + status_header( 200 ); + return; + } + + // We will 404 for paged queries, as no posts were found. + if ( ! is_paged() ) { + + // Don't 404 for authors without posts as long as they matched an author on this site. + $author = get_query_var( 'author' ); + if ( is_author() && is_numeric( $author ) && $author > 0 && is_user_member_of_blog( $author ) ) { + status_header( 200 ); + return; + } + + // Don't 404 for these queries if they matched an object. + if ( ( is_tag() || is_category() || is_tax() || is_post_type_archive() ) && get_queried_object() ) { + status_header( 200 ); + return; + } + + // Don't 404 for these queries either. + if ( is_home() || is_search() || is_feed() ) { + status_header( 200 ); + return; + } + } + + // Guess it's time to 404. + $wp_query->set_404(); + status_header( 404 ); + nocache_headers(); + } + + /** + * Sets up all of the variables required by the WordPress environment. + * + * The action 'wp' has one parameter that references the WP object. It + * allows for accessing the properties and methods to further manipulate the + * object. + * + * @since 2.0.0 + * + * @param string|array $query_args Passed to {@link parse_request()} + */ + public function main($query_args = '') { + $this->init(); + $this->parse_request($query_args); + $this->send_headers(); + $this->query_posts(); + $this->handle_404(); + $this->register_globals(); + + /** + * Fires once the WordPress environment has been set up. + * + * @since 2.1.0 + * + * @param WP &$this Current WordPress environment instance (passed by reference). + */ + do_action_ref_array( 'wp', array( &$this ) ); + } + +} + +/** + * Helper class to remove the need to use eval to replace $matches[] in query strings. + * + * @since 2.9.0 + */ +class WP_MatchesMapRegex { + /** + * store for matches + * + * @access private + * @var array + */ + private $_matches; + + /** + * store for mapping result + * + * @access public + * @var string + */ + public $output; + + /** + * subject to perform mapping on (query string containing $matches[] references + * + * @access private + * @var string + */ + private $_subject; + + /** + * regexp pattern to match $matches[] references + * + * @var string + */ + public $_pattern = '(\$matches\[[1-9]+[0-9]*\])'; // magic number + + /** + * Make private properties readable for backwards compatibility. + * + * @since 4.0.0 + * @access public + * + * @param string $name Property to get. + * @return mixed Property. + */ + public function __get( $name ) { + return $this->$name; + } + + /** + * Make private properties settable for backwards compatibility. + * + * @since 4.0.0 + * @access public + * + * @param string $name Property to set. + * @param mixed $value Property value. + * @return mixed Newly-set property. + */ + public function __set( $name, $value ) { + return $this->$name = $value; + } + + /** + * Make private properties checkable for backwards compatibility. + * + * @since 4.0.0 + * @access public + * + * @param string $name Property to check if set. + * @return bool Whether the property is set. + */ + public function __isset( $name ) { + return isset( $this->$name ); + } + + /** + * Make private properties un-settable for backwards compatibility. + * + * @since 4.0.0 + * @access public + * + * @param string $name Property to unset. + */ + public function __unset( $name ) { + unset( $this->$name ); + } + + /** + * Make private/protected methods readable for backwards compatibility. + * + * @since 4.0.0 + * @access public + * + * @param callable $name Method to call. + * @param array $arguments Arguments to pass when calling. + * @return mixed|bool Return value of the callback, false otherwise. + */ + public function __call( $name, $arguments ) { + return call_user_func_array( array( $this, $name ), $arguments ); + } + + /** + * constructor + * + * @param string $subject subject if regex + * @param array $matches data to use in map + * @return self + */ + public function WP_MatchesMapRegex($subject, $matches) { + $this->_subject = $subject; + $this->_matches = $matches; + $this->output = $this->_map(); + } + + /** + * Substitute substring matches in subject. + * + * static helper function to ease use + * + * @access public + * @param string $subject subject + * @param array $matches data used for substitution + * @return string + */ + public static function apply($subject, $matches) { + $oSelf = new WP_MatchesMapRegex($subject, $matches); + return $oSelf->output; + } + + /** + * do the actual mapping + * + * @access private + * @return string + */ + private function _map() { + $callback = array($this, 'callback'); + return preg_replace_callback($this->_pattern, $callback, $this->_subject); + } + + /** + * preg_replace_callback hook + * + * @access public + * @param array $matches preg_replace regexp matches + * @return string + */ + public function callback($matches) { + $index = intval(substr($matches[0], 9, -1)); + return ( isset( $this->_matches[$index] ) ? urlencode($this->_matches[$index]) : '' ); + } + +} diff --git a/wp-includes/class.wp-dependencies.php b/wp-includes/class.wp-dependencies.php new file mode 100644 index 0000000..7995a90 --- /dev/null +++ b/wp-includes/class.wp-dependencies.php @@ -0,0 +1,509 @@ +queue : (array) $handles; + $this->all_deps( $handles ); + + foreach( $this->to_do as $key => $handle ) { + if ( !in_array($handle, $this->done, true) && isset($this->registered[$handle]) ) { + + /* + * A single item may alias a set of items, by having dependencies, + * but no source. Queuing the item queues the dependencies. + * + * Example: The extending class WP_Scripts is used to register 'scriptaculous' as a set of registered handles: + * add( 'scriptaculous', false, array( 'scriptaculous-dragdrop', 'scriptaculous-slider', 'scriptaculous-controls' ) ); + * + * The src property is false. + */ + if ( ! $this->registered[$handle]->src ) { + $this->done[] = $handle; + continue; + } + + /* + * Attempt to process the item. If successful, + * add the handle to the done array. + * + * Unset the item from the to_do array. + */ + if ( $this->do_item( $handle, $group ) ) + $this->done[] = $handle; + + unset( $this->to_do[$key] ); + } + } + + return $this->done; + } + + /** + * Process a dependency. + * + * @access public + * @since 2.6.0 + * + * @param string $handle Name of the item. Should be unique. + * @return bool True on success, false if not set. + */ + public function do_item( $handle ) { + return isset($this->registered[$handle]); + } + + /** + * Determine dependencies. + * + * Recursively builds an array of items to process taking + * dependencies into account. Does NOT catch infinite loops. + * + * @access public + * @since 2.1.0 + * + * @param mixed $handles Item handle and argument (string) or item handles and arguments (array of strings). + * @param bool $recursion Internal flag that function is calling itself. + * @param mixed $group Group level: (int) level, (false) no groups. + * @return bool True on success, false on failure. + */ + public function all_deps( $handles, $recursion = false, $group = false ) { + if ( !$handles = (array) $handles ) + return false; + + foreach ( $handles as $handle ) { + $handle_parts = explode('?', $handle); + $handle = $handle_parts[0]; + $queued = in_array($handle, $this->to_do, true); + + if ( in_array($handle, $this->done, true) ) // Already done + continue; + + $moved = $this->set_group( $handle, $recursion, $group ); + + if ( $queued && !$moved ) // already queued and in the right group + continue; + + $keep_going = true; + if ( !isset($this->registered[$handle]) ) + $keep_going = false; // Item doesn't exist. + elseif ( $this->registered[$handle]->deps && array_diff($this->registered[$handle]->deps, array_keys($this->registered)) ) + $keep_going = false; // Item requires dependencies that don't exist. + elseif ( $this->registered[$handle]->deps && !$this->all_deps( $this->registered[$handle]->deps, true, $group ) ) + $keep_going = false; // Item requires dependencies that don't exist. + + if ( ! $keep_going ) { // Either item or its dependencies don't exist. + if ( $recursion ) + return false; // Abort this branch. + else + continue; // We're at the top level. Move on to the next one. + } + + if ( $queued ) // Already grabbed it and its dependencies. + continue; + + if ( isset($handle_parts[1]) ) + $this->args[$handle] = $handle_parts[1]; + + $this->to_do[] = $handle; + } + + return true; + } + + /** + * Register an item. + * + * Registers the item if no item of that name already exists. + * + * @access public + * @since 2.1.0 + * + * @param string $handle Unique item name. + * @param string $src The item url. + * @param array $deps Optional. An array of item handle strings on which this item depends. + * @param string $ver Optional. Version (used for cache busting). + * @param mixed $args Optional. Custom property of the item. NOT the class property $args. Examples: $media, $in_footer. + * @return bool True on success, false on failure. + */ + public function add( $handle, $src, $deps = array(), $ver = false, $args = null ) { + if ( isset($this->registered[$handle]) ) + return false; + $this->registered[$handle] = new _WP_Dependency( $handle, $src, $deps, $ver, $args ); + return true; + } + + /** + * Add extra item data. + * + * Adds data to a registered item. + * + * @access public + * @since 2.6.0 + * + * @param string $handle Name of the item. Should be unique. + * @param string $key The data key. + * @param mixed $value The data value. + * @return bool True on success, false on failure. + */ + public function add_data( $handle, $key, $value ) { + if ( !isset( $this->registered[$handle] ) ) + return false; + + return $this->registered[$handle]->add_data( $key, $value ); + } + + /** + * Get extra item data. + * + * Gets data associated with a registered item. + * + * @access public + * @since 3.3.0 + * + * @param string $handle Name of the item. Should be unique. + * @param string $key The data key. + * @return mixed Extra item data (string), false otherwise. + */ + public function get_data( $handle, $key ) { + if ( !isset( $this->registered[$handle] ) ) + return false; + + if ( !isset( $this->registered[$handle]->extra[$key] ) ) + return false; + + return $this->registered[$handle]->extra[$key]; + } + + /** + * Un-register an item or items. + * + * @access public + * @since 2.1.0 + * + * @param mixed $handles Item handle and argument (string) or item handles and arguments (array of strings). + * @return void + */ + public function remove( $handles ) { + foreach ( (array) $handles as $handle ) + unset($this->registered[$handle]); + } + + /** + * Queue an item or items. + * + * Decodes handles and arguments, then queues handles and stores + * arguments in the class property $args. For example in extending + * classes, $args is appended to the item url as a query string. + * Note $args is NOT the $args property of items in the $registered array. + * + * @access public + * @since 2.1.0 + * + * @param mixed $handles Item handle and argument (string) or item handles and arguments (array of strings). + */ + public function enqueue( $handles ) { + foreach ( (array) $handles as $handle ) { + $handle = explode('?', $handle); + if ( !in_array($handle[0], $this->queue) && isset($this->registered[$handle[0]]) ) { + $this->queue[] = $handle[0]; + if ( isset($handle[1]) ) + $this->args[$handle[0]] = $handle[1]; + } + } + } + + /** + * Dequeue an item or items. + * + * Decodes handles and arguments, then dequeues handles + * and removes arguments from the class property $args. + * + * @access public + * @since 2.1.0 + * + * @param mixed $handles Item handle and argument (string) or item handles and arguments (array of strings). + */ + public function dequeue( $handles ) { + foreach ( (array) $handles as $handle ) { + $handle = explode('?', $handle); + $key = array_search($handle[0], $this->queue); + if ( false !== $key ) { + unset($this->queue[$key]); + unset($this->args[$handle[0]]); + } + } + } + + /** + * Recursively search the passed dependency tree for $handle + * + * @since 4.0.0 + * + * @param array $queue An array of queued _WP_Dependency handle objects. + * @param string $handle Name of the item. Should be unique. + * @return boolean Whether the handle is found after recursively searching the dependency tree. + */ + protected function recurse_deps( $queue, $handle ) { + foreach ( $queue as $queued ) { + if ( ! isset( $this->registered[ $queued ] ) ) { + continue; + } + + if ( in_array( $handle, $this->registered[ $queued ]->deps ) ) { + return true; + } elseif ( $this->recurse_deps( $this->registered[ $queued ]->deps, $handle ) ) { + return true; + } + } + + return false; + } + + /** + * Query list for an item. + * + * @access public + * @since 2.1.0 + * + * @param string $handle Name of the item. Should be unique. + * @param string $list Property name of list array. + * @return bool Found, or object Item data. + */ + public function query( $handle, $list = 'registered' ) { + switch ( $list ) { + case 'registered' : + case 'scripts': // back compat + if ( isset( $this->registered[ $handle ] ) ) + return $this->registered[ $handle ]; + return false; + + case 'enqueued' : + case 'queue' : + if ( in_array( $handle, $this->queue ) ) { + return true; + } + return $this->recurse_deps( $this->queue, $handle ); + + case 'to_do' : + case 'to_print': // back compat + return in_array( $handle, $this->to_do ); + + case 'done' : + case 'printed': // back compat + return in_array( $handle, $this->done ); + } + return false; + } + + /** + * Set item group, unless already in a lower group. + * + * @access public + * @since 2.8.0 + * + * @param string $handle Name of the item. Should be unique. + * @param bool $recursion Internal flag that calling function was called recursively. + * @param mixed $group Group level. + * @return bool Not already in the group or a lower group + */ + public function set_group( $handle, $recursion, $group ) { + $group = (int) $group; + + if ( $recursion ) + $group = min($this->group, $group); + else + $this->group = $group; + + if ( isset($this->groups[$handle]) && $this->groups[$handle] <= $group ) + return false; + + $this->groups[$handle] = $group; + return true; + } + +} // WP_Dependencies + +/** + * Class _WP_Dependency + * + * Helper class to register a handle and associated data. + * + * @access private + * @since 2.6.0 + */ +class _WP_Dependency { + /** + * The handle name. + * + * @access public + * @since 2.6.0 + * @var null + */ + public $handle; + + /** + * The handle source. + * + * @access public + * @since 2.6.0 + * @var null + */ + public $src; + + /** + * An array of handle dependencies. + * + * @access public + * @since 2.6.0 + * @var array + */ + public $deps = array(); + + /** + * The handle version. + * + * Used for cache-busting. + * + * @access public + * @since 2.6.0 + * @var bool|string + */ + public $ver = false; + + /** + * Additional arguments for the handle. + * + * @access public + * @since 2.6.0 + * @var null + */ + public $args = null; // Custom property, such as $in_footer or $media. + + /** + * Extra data to supply to the handle. + * + * @access public + * @since 2.6.0 + * @var array + */ + public $extra = array(); + + /** + * Setup dependencies. + * + * @since 2.6.0 + */ + public function __construct() { + @list( $this->handle, $this->src, $this->deps, $this->ver, $this->args ) = func_get_args(); + if ( ! is_array($this->deps) ) + $this->deps = array(); + } + + /** + * Add handle data. + * + * @access public + * @since 2.6.0 + * + * @param string $name The data key to add. + * @param mixed $data The data value to add. + * @return bool False if not scalar, true otherwise. + */ + public function add_data( $name, $data ) { + if ( !is_scalar($name) ) + return false; + $this->extra[$name] = $data; + return true; + } + +} // _WP_Dependencies diff --git a/wp-includes/class.wp-scripts.php b/wp-includes/class.wp-scripts.php new file mode 100644 index 0000000..fbb8b22 --- /dev/null +++ b/wp-includes/class.wp-scripts.php @@ -0,0 +1,263 @@ +init(); + add_action( 'init', array( $this, 'init' ), 0 ); + } + + public function init() { + /** + * Fires when the WP_Scripts instance is initialized. + * + * @since 2.6.0 + * + * @param WP_Scripts &$this WP_Scripts instance, passed by reference. + */ + do_action_ref_array( 'wp_default_scripts', array(&$this) ); + } + + /** + * Prints scripts. + * + * Prints the scripts passed to it or the print queue. Also prints all necessary dependencies. + * + * @param mixed $handles Optional. Scripts to be printed. (void) prints queue, (string) prints + * that script, (array of strings) prints those scripts. Default false. + * @param int $group Optional. If scripts were queued in groups prints this group number. + * Default false. + * @return array Scripts that have been printed. + */ + public function print_scripts( $handles = false, $group = false ) { + return $this->do_items( $handles, $group ); + } + + // Deprecated since 3.3, see print_extra_script() + public function print_scripts_l10n( $handle, $echo = true ) { + _deprecated_function( __FUNCTION__, '3.3', 'print_extra_script()' ); + return $this->print_extra_script( $handle, $echo ); + } + + public function print_extra_script( $handle, $echo = true ) { + if ( !$output = $this->get_data( $handle, 'data' ) ) + return; + + if ( !$echo ) + return $output; + + echo "\n"; + + return true; + } + + public function do_item( $handle, $group = false ) { + if ( !parent::do_item($handle) ) + return false; + + if ( 0 === $group && $this->groups[$handle] > 0 ) { + $this->in_footer[] = $handle; + return false; + } + + if ( false === $group && in_array($handle, $this->in_footer, true) ) + $this->in_footer = array_diff( $this->in_footer, (array) $handle ); + + if ( null === $this->registered[$handle]->ver ) + $ver = ''; + else + $ver = $this->registered[$handle]->ver ? $this->registered[$handle]->ver : $this->default_version; + + if ( isset($this->args[$handle]) ) + $ver = $ver ? $ver . '&' . $this->args[$handle] : $this->args[$handle]; + + $src = $this->registered[$handle]->src; + + if ( $this->do_concat ) { + /** + * Filter the script loader source. + * + * @since 2.2.0 + * + * @param string $src Script loader source path. + * @param string $handle Script handle. + */ + $srce = apply_filters( 'script_loader_src', $src, $handle ); + if ( $this->in_default_dir($srce) ) { + $this->print_code .= $this->print_extra_script( $handle, false ); + $this->concat .= "$handle,"; + $this->concat_version .= "$handle$ver"; + return true; + } else { + $this->ext_handles .= "$handle,"; + $this->ext_version .= "$handle$ver"; + } + } + + $this->print_extra_script( $handle ); + if ( !preg_match('|^(https?:)?//|', $src) && ! ( $this->content_url && 0 === strpos($src, $this->content_url) ) ) { + $src = $this->base_url . $src; + } + + if ( !empty($ver) ) + $src = add_query_arg('ver', $ver, $src); + + /** This filter is documented in wp-includes/class.wp-scripts.php */ + $src = esc_url( apply_filters( 'script_loader_src', $src, $handle ) ); + + if ( ! $src ) + return true; + + $tag = "\n"; + + /** + * Filter the HTML script tag of an enqueued script. + * + * @since 4.1.0 + * + * @param string $tag The `\n"; + } +} + +/** + * Load the comment template specified in $file. + * + * Will not display the comments template if not on single post or page, or if + * the post does not have comments. + * + * Uses the WordPress database object to query for the comments. The comments + * are passed through the 'comments_array' filter hook with the list of comments + * and the post ID respectively. + * + * The $file path is passed through a filter hook called, 'comments_template' + * which includes the TEMPLATEPATH and $file combined. Tries the $filtered path + * first and if it fails it will require the default comment template from the + * default theme. If either does not exist, then the WordPress process will be + * halted. It is advised for that reason, that the default theme is not deleted. + * + * @todo Document globals + * @uses $withcomments Will not try to get the comments if the post has none. + * + * @since 1.5.0 + * + * @param string $file Optional. The file to load. Default '/comments.php'. + * @param bool $separate_comments Optional. Whether to separate the comments by comment type. + * Default false. + * @return null Returns null if no comments appear. + */ +function comments_template( $file = '/comments.php', $separate_comments = false ) { + global $wp_query, $withcomments, $post, $wpdb, $id, $comment, $user_login, $user_ID, $user_identity, $overridden_cpage; + + if ( !(is_single() || is_page() || $withcomments) || empty($post) ) + return; + + if ( empty($file) ) + $file = '/comments.php'; + + $req = get_option('require_name_email'); + + /* + * Comment author information fetched from the comment cookies. + * Uuses wp_get_current_commenter(). + */ + $commenter = wp_get_current_commenter(); + + /* + * The name of the current comment author escaped for use in attributes. + * Escaped by sanitize_comment_cookies(). + */ + $comment_author = $commenter['comment_author']; + + /* + * The email address of the current comment author escaped for use in attributes. + * Escaped by sanitize_comment_cookies(). + */ + $comment_author_email = $commenter['comment_author_email']; + + /* + * The url of the current comment author escaped for use in attributes. + */ + $comment_author_url = esc_url($commenter['comment_author_url']); + + $comment_args = array( + 'order' => 'ASC', + 'orderby' => 'comment_date_gmt', + 'status' => 'approve', + 'post_id' => $post->ID, + ); + + if ( $user_ID ) { + $comment_args['include_unapproved'] = array( $user_ID ); + } else if ( ! empty( $comment_author_email ) ) { + $comment_args['include_unapproved'] = array( $comment_author_email ); + } + + $comments = get_comments( $comment_args ); + + /** + * Filter the comments array. + * + * @since 2.1.0 + * + * @param array $comments Array of comments supplied to the comments template. + * @param int $post_ID Post ID. + */ + $wp_query->comments = apply_filters( 'comments_array', $comments, $post->ID ); + $comments = &$wp_query->comments; + $wp_query->comment_count = count($wp_query->comments); + update_comment_cache($wp_query->comments); + + if ( $separate_comments ) { + $wp_query->comments_by_type = separate_comments($comments); + $comments_by_type = &$wp_query->comments_by_type; + } + + $overridden_cpage = false; + if ( '' == get_query_var('cpage') && get_option('page_comments') ) { + set_query_var( 'cpage', 'newest' == get_option('default_comments_page') ? get_comment_pages_count() : 1 ); + $overridden_cpage = true; + } + + if ( !defined('COMMENTS_TEMPLATE') ) + define('COMMENTS_TEMPLATE', true); + + $theme_template = STYLESHEETPATH . $file; + /** + * Filter the path to the theme template file used for the comments template. + * + * @since 1.5.1 + * + * @param string $theme_template The path to the theme template file. + */ + $include = apply_filters( 'comments_template', $theme_template ); + if ( file_exists( $include ) ) + require( $include ); + elseif ( file_exists( TEMPLATEPATH . $file ) ) + require( TEMPLATEPATH . $file ); + else // Backward compat code will be removed in a future release + require( ABSPATH . WPINC . '/theme-compat/comments.php'); +} + +/** + * Display the JS popup script to show a comment. + * + * If the $file parameter is empty, then the home page is assumed. The defaults + * for the window are 400px by 400px. + * + * For the comment link popup to work, this function has to be called or the + * normal comment link will be assumed. + * + * @global string $wpcommentspopupfile The URL to use for the popup window. + * @global int $wpcommentsjavascript Whether to use JavaScript. Set when function is called. + * + * @since 0.71 + * + * @param int $width Optional. The width of the popup window. Default 400. + * @param int $height Optional. The height of the popup window. Default 400. + * @param string $file Optional. Sets the location of the popup window. + */ +function comments_popup_script( $width = 400, $height = 400, $file = '' ) { + global $wpcommentspopupfile, $wpcommentsjavascript; + + if (empty ($file)) { + $wpcommentspopupfile = ''; // Use the index. + } else { + $wpcommentspopupfile = $file; + } + + $wpcommentsjavascript = 1; + $javascript = "\n"; + echo $javascript; +} + +/** + * Displays the link to the comments popup window for the current post ID. + * + * Is not meant to be displayed on single posts and pages. Should be used + * on the lists of posts + * + * @global string $wpcommentspopupfile The URL to use for the popup window. + * @global int $wpcommentsjavascript Whether to use JavaScript. Set when function is called. + * + * @since 0.71 + * + * @param string $zero Optional. String to display when no comments. Default false. + * @param string $one Optional. String to display when only one comment is available. + * Default false. + * @param string $more Optional. String to display when there are more than one comment. + * Default false. + * @param string $css_class Optional. CSS class to use for comments. Default empty. + * @param string $none Optional. String to display when comments have been turned off. + * Default false. + * @return null Returns null on single posts and pages. + */ +function comments_popup_link( $zero = false, $one = false, $more = false, $css_class = '', $none = false ) { + global $wpcommentspopupfile, $wpcommentsjavascript; + + $id = get_the_ID(); + + if ( false === $zero ) $zero = __( 'No Comments' ); + if ( false === $one ) $one = __( '1 Comment' ); + if ( false === $more ) $more = __( '% Comments' ); + if ( false === $none ) $none = __( 'Comments Off' ); + + $number = get_comments_number( $id ); + + if ( 0 == $number && !comments_open() && !pings_open() ) { + echo '' . $none . ''; + return; + } + + if ( post_password_required() ) { + echo __('Enter your password to view comments.'); + return; + } + + echo ' 0 ) ); + + $attributes = ''; + /** + * Filter the comments popup link attributes for display. + * + * @since 2.5.0 + * + * @param string $attributes The comments popup link attributes. Default empty. + */ + echo apply_filters( 'comments_popup_link_attributes', $attributes ); + + echo ' title="' . esc_attr( sprintf( __('Comment on %s'), $title ) ) . '">'; + comments_number( $zero, $one, $more ); + echo ''; +} + +/** + * Retrieve HTML content for reply to comment link. + * + * @since 2.7.0 + * + * @param array $args { + * Optional. Override default arguments. + * + * @type string $add_below The first part of the selector used to identify the comment to respond below. + * The resulting value is passed as the first parameter to addComment.moveForm(), + * concatenated as $add_below-$comment->comment_ID. Default 'comment'. + * @type string $respond_id The selector identifying the responding comment. Passed as the third parameter + * to addComment.moveForm(), and appended to the link URL as a hash value. + * Default 'respond'. + * @type string $reply_text The text of the Reply link. Default 'Reply'. + * @type string $login_text The text of the link to reply if logged out. Default 'Log in to Reply'. + * @type int $depth' The depth of the new comment. Must be greater than 0 and less than the value + * of the 'thread_comments_depth' option set in Settings > Discussion. Default 0. + * @type string $before The text or HTML to add before the reply link. Default empty. + * @type string $after The text or HTML to add after the reply link. Default empty. + * } + * @param int $comment Comment being replied to. Default current comment. + * @param int|WP_Post $post Post ID or WP_Post object the comment is going to be displayed on. + * Default current post. + * @return null|false|string Link to show comment form, if successful. False, if comments are closed. + */ +function get_comment_reply_link( $args = array(), $comment = null, $post = null ) { + + $defaults = array( + 'add_below' => 'comment', + 'respond_id' => 'respond', + 'reply_text' => __( 'Reply' ), + 'reply_to_text' => __( 'Reply to %s' ), + 'login_text' => __( 'Log in to Reply' ), + 'depth' => 0, + 'before' => '', + 'after' => '' + ); + + $args = wp_parse_args( $args, $defaults ); + + if ( 0 == $args['depth'] || $args['max_depth'] <= $args['depth'] ) { + return; + } + + $comment = get_comment( $comment ); + + if ( empty( $post ) ) { + $post = $comment->comment_post_ID; + } + + $post = get_post( $post ); + + if ( ! comments_open( $post->ID ) ) { + return false; + } + + /** + * Filter the comment reply link arguments. + * + * @since 4.1.0 + * + * @param array $args Comment reply link arguments. See {@see get_comment_reply_link()} + * for more information on accepted arguments. + * @param object $comment The object of the comment being replied to. + * @param WP_Post $post The {@see WP_Post} object. + */ + $args = apply_filters( 'comment_reply_link_args', $args, $comment, $post ); + + if ( get_option( 'comment_registration' ) && ! is_user_logged_in() ) { + $link = sprintf( '', + esc_url( wp_login_url( get_permalink() ) ), + $args['login_text'] + ); + } else { + $onclick = sprintf( 'return addComment.moveForm( "%1$s-%2$s", "%2$s", "%3$s", "%4$s" )', + $args['add_below'], $comment->comment_ID, $args['respond_id'], $post->ID + ); + + $link = sprintf( "%s", + esc_url( add_query_arg( 'replytocom', $comment->comment_ID ) ) . "#" . $args['respond_id'], + $onclick, + esc_attr( sprintf( $args['reply_to_text'], $comment->comment_author ) ), + $args['reply_text'] + ); + } + /** + * Filter the comment reply link. + * + * @since 2.7.0 + * + * @param string $link The HTML markup for the comment reply link. + * @param array $args An array of arguments overriding the defaults. + * @param object $comment The object of the comment being replied. + * @param WP_Post $post The WP_Post object. + */ + return apply_filters( 'comment_reply_link', $args['before'] . $link . $args['after'], $args, $comment, $post ); +} + +/** + * Displays the HTML content for reply to comment link. + * + * @since 2.7.0 + * + * @see get_comment_reply_link() + * + * @param array $args Optional. Override default options. + * @param int $comment Comment being replied to. Default current comment. + * @param int|WP_Post $post Post ID or WP_Post object the comment is going to be displayed on. + * Default current post. + * @return mixed Link to show comment form, if successful. False, if comments are closed. + */ +function comment_reply_link($args = array(), $comment = null, $post = null) { + echo get_comment_reply_link($args, $comment, $post); +} + +/** + * Retrieve HTML content for reply to post link. + * + * @since 2.7.0 + * + * @param array $args { + * Optional. Override default arguments. + * + * @type string $add_below The first part of the selector used to identify the comment to respond below. + * The resulting value is passed as the first parameter to addComment.moveForm(), + * concatenated as $add_below-$comment->comment_ID. Default is 'post'. + * @type string $respond_id The selector identifying the responding comment. Passed as the third parameter + * to addComment.moveForm(), and appended to the link URL as a hash value. + * Default 'respond'. + * @type string $reply_text Text of the Reply link. Default is 'Leave a Comment'. + * @type string $login_text Text of the link to reply if logged out. Default is 'Log in to leave a Comment'. + * @type string $before Text or HTML to add before the reply link. Default empty. + * @type string $after Text or HTML to add after the reply link. Default empty. + * } + * @param int|WP_Post $post Optional. Post ID or WP_Post object the comment is going to be displayed on. + * Default current post. + * @return false|null|string Link to show comment form, if successful. False, if comments are closed. + */ +function get_post_reply_link($args = array(), $post = null) { + $defaults = array( + 'add_below' => 'post', + 'respond_id' => 'respond', + 'reply_text' => __('Leave a Comment'), + 'login_text' => __('Log in to leave a Comment'), + 'before' => '', + 'after' => '', + ); + + $args = wp_parse_args($args, $defaults); + + $post = get_post($post); + + if ( ! comments_open( $post->ID ) ) { + return false; + } + + if ( get_option('comment_registration') && ! is_user_logged_in() ) { + $link = sprintf( '%s', + wp_login_url( get_permalink() ), + $args['login_text'] + ); + } else { + $onclick = sprintf( 'return addComment.moveForm( "%1$s-%2$s", "0", "%3$s", "%2$s" )', + $args['add_below'], $post->ID, $args['respond_id'] + ); + + $link = sprintf( "%s", + get_permalink( $post->ID ) . '#' . $args['respond_id'], + $onclick, + $args['reply_text'] + ); + } + $formatted_link = $args['before'] . $link . $args['after']; + /** + * Filter the formatted post comments link HTML. + * + * @since 2.7.0 + * + * @param string $formatted The HTML-formatted post comments link. + * @param int|WP_Post $post The post ID or WP_Post object. + */ + return apply_filters( 'post_comments_link', $formatted_link, $post ); +} + +/** + * Displays the HTML content for reply to post link. + * + * @since 2.7.0 + * + * @see get_post_reply_link() + * + * @param array $args Optional. Override default options, + * @param int|WP_Post $post Post ID or WP_Post object the comment is going to be displayed on. + * Default current post. + * @return string|bool|null Link to show comment form, if successful. False, if comments are closed. + */ +function post_reply_link($args = array(), $post = null) { + echo get_post_reply_link($args, $post); +} + +/** + * Retrieve HTML content for cancel comment reply link. + * + * @since 2.7.0 + * + * @param string $text Optional. Text to display for cancel reply link. Default empty. + */ +function get_cancel_comment_reply_link( $text = '' ) { + if ( empty($text) ) + $text = __('Click here to cancel reply.'); + + $style = isset($_GET['replytocom']) ? '' : ' style="display:none;"'; + $link = esc_html( remove_query_arg('replytocom') ) . '#respond'; + + $formatted_link = '' . $text . ''; + /** + * Filter the cancel comment reply link HTML. + * + * @since 2.7.0 + * + * @param string $formatted_link The HTML-formatted cancel comment reply link. + * @param string $link Cancel comment reply link URL. + * @param string $text Cancel comment reply link text. + */ + return apply_filters( 'cancel_comment_reply_link', $formatted_link, $link, $text ); +} + +/** + * Display HTML content for cancel comment reply link. + * + * @since 2.7.0 + * + * @param string $text Optional. Text to display for cancel reply link. Default empty. + */ +function cancel_comment_reply_link( $text = '' ) { + echo get_cancel_comment_reply_link($text); +} + +/** + * Retrieve hidden input HTML for replying to comments. + * + * @since 3.0.0 + * + * @param int $id Optional. Post ID. Default current post ID. + * @return string Hidden input HTML for replying to comments + */ +function get_comment_id_fields( $id = 0 ) { + if ( empty( $id ) ) + $id = get_the_ID(); + + $replytoid = isset($_GET['replytocom']) ? (int) $_GET['replytocom'] : 0; + $result = "\n"; + $result .= "\n"; + + /** + * Filter the returned comment id fields. + * + * @since 3.0.0 + * + * @param string $result The HTML-formatted hidden id field comment elements. + * @param int $id The post ID. + * @param int $replytoid The id of the comment being replied to. + */ + return apply_filters( 'comment_id_fields', $result, $id, $replytoid ); +} + +/** + * Output hidden input HTML for replying to comments. + * + * @since 2.7.0 + * + * @param int $id Optional. Post ID. Default current post ID. + */ +function comment_id_fields( $id = 0 ) { + echo get_comment_id_fields( $id ); +} + +/** + * Display text based on comment reply status. + * + * Only affects users with JavaScript disabled. + * + * @since 2.7.0 + * + * @param string $noreplytext Optional. Text to display when not replying to a comment. + * Default false. + * @param string $replytext Optional. Text to display when replying to a comment. + * Default false. Accepts "%s" for the author of the comment + * being replied to. + * @param string $linktoparent Optional. Boolean to control making the author's name a link + * to their comment. Default true. + */ +function comment_form_title( $noreplytext = false, $replytext = false, $linktoparent = true ) { + global $comment; + + if ( false === $noreplytext ) $noreplytext = __( 'Leave a Reply' ); + if ( false === $replytext ) $replytext = __( 'Leave a Reply to %s' ); + + $replytoid = isset($_GET['replytocom']) ? (int) $_GET['replytocom'] : 0; + + if ( 0 == $replytoid ) + echo $noreplytext; + else { + $comment = get_comment($replytoid); + $author = ( $linktoparent ) ? '' . get_comment_author() . '' : get_comment_author(); + printf( $replytext, $author ); + } +} + +/** + * HTML comment list class. + * + * @uses Walker + * @since 2.7.0 + */ +class Walker_Comment extends Walker { + /** + * What the class handles. + * + * @see Walker::$tree_type + * + * @since 2.7.0 + * @var string + */ + public $tree_type = 'comment'; + + /** + * DB fields to use. + * + * @see Walker::$db_fields + * + * @since 2.7.0 + * @var array + */ + public $db_fields = array ('parent' => 'comment_parent', 'id' => 'comment_ID'); + + /** + * Start the list before the elements are added. + * + * @see Walker::start_lvl() + * + * @since 2.7.0 + * + * @param string $output Passed by reference. Used to append additional content. + * @param int $depth Depth of comment. + * @param array $args Uses 'style' argument for type of HTML list. + */ + public function start_lvl( &$output, $depth = 0, $args = array() ) { + $GLOBALS['comment_depth'] = $depth + 1; + + switch ( $args['style'] ) { + case 'div': + break; + case 'ol': + $output .= '
          ' . "\n"; + break; + case 'ul': + default: + $output .= '
            ' . "\n"; + break; + } + } + + /** + * End the list of items after the elements are added. + * + * @see Walker::end_lvl() + * + * @since 2.7.0 + * + * @param string $output Passed by reference. Used to append additional content. + * @param int $depth Depth of comment. + * @param array $args Will only append content if style argument value is 'ol' or 'ul'. + */ + public function end_lvl( &$output, $depth = 0, $args = array() ) { + $GLOBALS['comment_depth'] = $depth + 1; + + switch ( $args['style'] ) { + case 'div': + break; + case 'ol': + $output .= "
        \n"; + break; + case 'ul': + default: + $output .= "\n"; + break; + } + } + + /** + * Traverse elements to create list from elements. + * + * This function is designed to enhance Walker::display_element() to + * display children of higher nesting levels than selected inline on + * the highest depth level displayed. This prevents them being orphaned + * at the end of the comment list. + * + * Example: max_depth = 2, with 5 levels of nested content. + * 1 + * 1.1 + * 1.1.1 + * 1.1.1.1 + * 1.1.1.1.1 + * 1.1.2 + * 1.1.2.1 + * 2 + * 2.2 + * + * @see Walker::display_element() + * @see wp_list_comments() + * + * @since 2.7.0 + * + * @param object $element Data object. + * @param array $children_elements List of elements to continue traversing. + * @param int $max_depth Max depth to traverse. + * @param int $depth Depth of current element. + * @param array $args An array of arguments. + * @param string $output Passed by reference. Used to append additional content. + * @return null Null on failure with no changes to parameters. + */ + public function display_element( $element, &$children_elements, $max_depth, $depth, $args, &$output ) { + + if ( !$element ) + return; + + $id_field = $this->db_fields['id']; + $id = $element->$id_field; + + parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output ); + + // If we're at the max depth, and the current element still has children, loop over those and display them at this level + // This is to prevent them being orphaned to the end of the list. + if ( $max_depth <= $depth + 1 && isset( $children_elements[$id]) ) { + foreach ( $children_elements[ $id ] as $child ) + $this->display_element( $child, $children_elements, $max_depth, $depth, $args, $output ); + + unset( $children_elements[ $id ] ); + } + + } + + /** + * Start the element output. + * + * @since 2.7.0 + * + * @see Walker::start_el() + * @see wp_list_comments() + * + * @param string $output Passed by reference. Used to append additional content. + * @param object $comment Comment data object. + * @param int $depth Depth of comment in reference to parents. + * @param array $args An array of arguments. + */ + public function start_el( &$output, $comment, $depth = 0, $args = array(), $id = 0 ) { + $depth++; + $GLOBALS['comment_depth'] = $depth; + $GLOBALS['comment'] = $comment; + + if ( !empty( $args['callback'] ) ) { + ob_start(); + call_user_func( $args['callback'], $comment, $args, $depth ); + $output .= ob_get_clean(); + return; + } + + if ( ( 'pingback' == $comment->comment_type || 'trackback' == $comment->comment_type ) && $args['short_ping'] ) { + ob_start(); + $this->ping( $comment, $depth, $args ); + $output .= ob_get_clean(); + } elseif ( 'html5' === $args['format'] ) { + ob_start(); + $this->html5_comment( $comment, $depth, $args ); + $output .= ob_get_clean(); + } else { + ob_start(); + $this->comment( $comment, $depth, $args ); + $output .= ob_get_clean(); + } + } + + /** + * Ends the element output, if needed. + * + * @since 2.7.0 + * + * @see Walker::end_el() + * @see wp_list_comments() + * + * @param string $output Passed by reference. Used to append additional content. + * @param object $comment The comment object. Default current comment. + * @param int $depth Depth of comment. + * @param array $args An array of arguments. + */ + public function end_el( &$output, $comment, $depth = 0, $args = array() ) { + if ( !empty( $args['end-callback'] ) ) { + ob_start(); + call_user_func( $args['end-callback'], $comment, $args, $depth ); + $output .= ob_get_clean(); + return; + } + if ( 'div' == $args['style'] ) + $output .= "
        \n"; + else + $output .= "\n"; + } + + /** + * Output a pingback comment. + * + * @access protected + * @since 3.6.0 + * + * @see wp_list_comments() + * + * @param object $comment The comment object. + * @param int $depth Depth of comment. + * @param array $args An array of arguments. + */ + protected function ping( $comment, $depth, $args ) { + $tag = ( 'div' == $args['style'] ) ? 'div' : 'li'; +?> + < id="comment-" > +
        + ', '' ); ?> +
        + + < has_children ? 'parent' : '' ); ?> id="comment-"> + +
        + +
        + + %s says:' ), get_comment_author_link() ); ?> +
        + comment_approved ) : ?> + +
        + + + + + $add_below, 'depth' => $depth, 'max_depth' => $args['max_depth'] ) ) ); ?> + + $add_below, + 'depth' => $depth, + 'max_depth' => $args['max_depth'], + 'before' => '
        ', + 'after' => '
        ' + ) ) ); + ?> + + +
        + + + < id="comment-" has_children ? 'parent' : '' ); ?>> +
        +
        +
        + + says:' ), sprintf( '%s', get_comment_author_link() ) ); ?> +
        + + + + comment_approved ) : ?> +

        + +
        + +
        + +
        + + 'div-comment', + 'depth' => $depth, + 'max_depth' => $args['max_depth'], + 'before' => '
        ', + 'after' => '
        ' + ) ) ); + ?> +
        +comments + * + * @param string|array $args { + * Optional. Formatting options. + * + * @type object $walker Instance of a Walker class to list comments. Default null. + * @type int $max_depth The maximum comments depth. Default empty. + * @type string $style The style of list ordering. Default 'ul'. Accepts 'ul', 'ol'. + * @type string $callback Callback function to use. Default null. + * @type string $end-callback Callback function to use at the end. Default null. + * @type string $type Type of comments to list. + * Default 'all'. Accepts 'all', 'comment', 'pingback', 'trackback', 'pings'. + * @type int $page Page ID to list comments for. Default empty. + * @type int $per_page Number of comments to list per page. Default empty. + * @type int $avatar_size Height and width dimensions of the avatar size. Default 32. + * @type string $reverse_top_level Ordering of the listed comments. Default null. Accepts 'desc', 'asc'. + * @type bool $reverse_children Whether to reverse child comments in the list. Default null. + * @type string $format How to format the comments list. + * Default 'html5' if the theme supports it. Accepts 'html5', 'xhtml'. + * @type bool $short_ping Whether to output short pings. Default false. + * @type bool $echo Whether to echo the output or return it. Default true. + * } + * @param array $comments Optional. Array of comment objects. + */ +function wp_list_comments( $args = array(), $comments = null ) { + global $wp_query, $comment_alt, $comment_depth, $comment_thread_alt, $overridden_cpage, $in_comment_loop; + + $in_comment_loop = true; + + $comment_alt = $comment_thread_alt = 0; + $comment_depth = 1; + + $defaults = array( + 'walker' => null, + 'max_depth' => '', + 'style' => 'ul', + 'callback' => null, + 'end-callback' => null, + 'type' => 'all', + 'page' => '', + 'per_page' => '', + 'avatar_size' => 32, + 'reverse_top_level' => null, + 'reverse_children' => '', + 'format' => current_theme_supports( 'html5', 'comment-list' ) ? 'html5' : 'xhtml', + 'short_ping' => false, + 'echo' => true, + ); + + $r = wp_parse_args( $args, $defaults ); + + /** + * Filter the arguments used in retrieving the comment list. + * + * @since 4.0.0 + * + * @see wp_list_comments() + * + * @param array $r An array of arguments for displaying comments. + */ + $r = apply_filters( 'wp_list_comments_args', $r ); + + // Figure out what comments we'll be looping through ($_comments) + if ( null !== $comments ) { + $comments = (array) $comments; + if ( empty($comments) ) + return; + if ( 'all' != $r['type'] ) { + $comments_by_type = separate_comments($comments); + if ( empty($comments_by_type[$r['type']]) ) + return; + $_comments = $comments_by_type[$r['type']]; + } else { + $_comments = $comments; + } + } else { + if ( empty($wp_query->comments) ) + return; + if ( 'all' != $r['type'] ) { + if ( empty($wp_query->comments_by_type) ) + $wp_query->comments_by_type = separate_comments($wp_query->comments); + if ( empty($wp_query->comments_by_type[$r['type']]) ) + return; + $_comments = $wp_query->comments_by_type[$r['type']]; + } else { + $_comments = $wp_query->comments; + } + } + + if ( '' === $r['per_page'] && get_option('page_comments') ) + $r['per_page'] = get_query_var('comments_per_page'); + + if ( empty($r['per_page']) ) { + $r['per_page'] = 0; + $r['page'] = 0; + } + + if ( '' === $r['max_depth'] ) { + if ( get_option('thread_comments') ) + $r['max_depth'] = get_option('thread_comments_depth'); + else + $r['max_depth'] = -1; + } + + if ( '' === $r['page'] ) { + if ( empty($overridden_cpage) ) { + $r['page'] = get_query_var('cpage'); + } else { + $threaded = ( -1 != $r['max_depth'] ); + $r['page'] = ( 'newest' == get_option('default_comments_page') ) ? get_comment_pages_count($_comments, $r['per_page'], $threaded) : 1; + set_query_var( 'cpage', $r['page'] ); + } + } + // Validation check + $r['page'] = intval($r['page']); + if ( 0 == $r['page'] && 0 != $r['per_page'] ) + $r['page'] = 1; + + if ( null === $r['reverse_top_level'] ) + $r['reverse_top_level'] = ( 'desc' == get_option('comment_order') ); + + if ( empty( $r['walker'] ) ) { + $walker = new Walker_Comment; + } else { + $walker = $r['walker']; + } + + $output = $walker->paged_walk( $_comments, $r['max_depth'], $r['page'], $r['per_page'], $r ); + $wp_query->max_num_comment_pages = $walker->max_pages; + + $in_comment_loop = false; + + if ( $r['echo'] ) { + echo $output; + } else { + return $output; + } +} + +/** + * Output a complete commenting form for use within a template. + * + * Most strings and form fields may be controlled through the $args array passed + * into the function, while you may also choose to use the comment_form_default_fields + * filter to modify the array of default fields if you'd just like to add a new + * one or remove a single field. All fields are also individually passed through + * a filter of the form comment_form_field_$name where $name is the key used + * in the array of fields. + * + * @since 3.0.0 + * + * @param array $args { + * Optional. Default arguments and form fields to override. + * + * @type array $fields { + * Default comment fields, filterable by default via the 'comment_form_default_fields' hook. + * + * @type string $author Comment author field HTML. + * @type string $email Comment author email field HTML. + * @type string $url Comment author URL field HTML. + * } + * @type string $comment_field The comment textarea field HTML. + * @type string $must_log_in HTML element for a 'must be logged in to comment' message. + * @type string $logged_in_as HTML element for a 'logged in as [user]' message. + * @type string $comment_notes_before HTML element for a message displayed before the comment form. + * Default 'Your email address will not be published.'. + * @type string $comment_notes_after HTML element for a message displayed after the comment form. + * Default 'You may use these HTML tags and attributes ...'. + * @type string $id_form The comment form element id attribute. Default 'commentform'. + * @type string $id_submit The comment submit element id attribute. Default 'submit'. + * @type string $class_submit The comment submit element class attribute. Default 'submit'. + * @type string $name_submit The comment submit element name attribute. Default 'submit'. + * @type string $title_reply The translatable 'reply' button label. Default 'Leave a Reply'. + * @type string $title_reply_to The translatable 'reply-to' button label. Default 'Leave a Reply to %s', + * where %s is the author of the comment being replied to. + * @type string $cancel_reply_link The translatable 'cancel reply' button label. Default 'Cancel reply'. + * @type string $label_submit The translatable 'submit' button label. Default 'Post a comment'. + * @type string $format The comment form format. Default 'xhtml'. Accepts 'xhtml', 'html5'. + * } + * @param int|WP_Post $post_id Post ID or WP_Post object to generate the form for. Default current post. + */ +function comment_form( $args = array(), $post_id = null ) { + if ( null === $post_id ) + $post_id = get_the_ID(); + + $commenter = wp_get_current_commenter(); + $user = wp_get_current_user(); + $user_identity = $user->exists() ? $user->display_name : ''; + + $args = wp_parse_args( $args ); + if ( ! isset( $args['format'] ) ) + $args['format'] = current_theme_supports( 'html5', 'comment-form' ) ? 'html5' : 'xhtml'; + + $req = get_option( 'require_name_email' ); + $aria_req = ( $req ? " aria-required='true'" : '' ); + $html5 = 'html5' === $args['format']; + $fields = array( + 'author' => '

        ' . ' ' . + '

        ', + 'email' => '', + 'url' => '

        ' . + '

        ', + ); + + $required_text = sprintf( ' ' . __('Required fields are marked %s'), '*' ); + + /** + * Filter the default comment form fields. + * + * @since 3.0.0 + * + * @param array $fields The default comment fields. + */ + $fields = apply_filters( 'comment_form_default_fields', $fields ); + $defaults = array( + 'fields' => $fields, + 'comment_field' => '

        ', + /** This filter is documented in wp-includes/link-template.php */ + 'must_log_in' => '', + /** This filter is documented in wp-includes/link-template.php */ + 'logged_in_as' => '

        ' . sprintf( __( 'Logged in as %2$s. Log out?' ), get_edit_user_link(), $user_identity, wp_logout_url( apply_filters( 'the_permalink', get_permalink( $post_id ) ) ) ) . '

        ', + 'comment_notes_before' => '

        ' . __( 'Your email address will not be published.' ) . ''. ( $req ? $required_text : '' ) . '

        ', + 'comment_notes_after' => '

        ' . sprintf( __( 'You may use these HTML tags and attributes: %s' ), ' ' . allowed_tags() . '' ) . '

        ', + 'id_form' => 'commentform', + 'id_submit' => 'submit', + 'class_submit' => 'submit', + 'name_submit' => 'submit', + 'title_reply' => __( 'Leave a Reply' ), + 'title_reply_to' => __( 'Leave a Reply to %s' ), + 'cancel_reply_link' => __( 'Cancel reply' ), + 'label_submit' => __( 'Post Comment' ), + 'format' => 'xhtml', + ); + + /** + * Filter the comment form default arguments. + * + * Use 'comment_form_default_fields' to filter the comment fields. + * + * @since 3.0.0 + * + * @param array $defaults The default comment form arguments. + */ + $args = wp_parse_args( $args, apply_filters( 'comment_form_defaults', $defaults ) ); + + ?> + + +
        +

        + + + + +
        > + + + + + + + $field ) { + /** + * Filter a comment form field for display. + * + * The dynamic portion of the filter hook, `$name`, refers to the name + * of the comment form field. Such as 'author', 'email', or 'url'. + * + * @since 3.0.0 + * + * @param string $field The HTML-formatted output of the comment form field. + */ + echo apply_filters( "comment_form_field_{$name}", $field ) . "\n"; + } + /** + * Fires after the comment fields in the comment form. + * + * @since 3.0.0 + */ + do_action( 'comment_form_after_fields' ); + ?> + + + +

        + + +

        + tag. + * + * @since 1.5.0 + * + * @param int $post_id The post ID. + */ + do_action( 'comment_form', $post_id ); + ?> +
        + +
        + ]*href/i', $comment, $out ); + + /** + * Filter the maximum number of links allowed in a comment. + * + * @since 3.0.0 + * + * @param int $num_links The number of links allowed. + * @param string $url Comment author's URL. Included in allowed links total. + */ + $num_links = apply_filters( 'comment_max_links_url', $num_links, $url ); + + /* + * If the number of links in the comment exceeds the allowed amount, + * fail the check by returning false. + */ + if ( $num_links >= $max_links ) + return false; + } + + $mod_keys = trim(get_option('moderation_keys')); + + // If moderation 'keys' (keywords) are set, process them. + if ( !empty($mod_keys) ) { + $words = explode("\n", $mod_keys ); + + foreach ( (array) $words as $word) { + $word = trim($word); + + // Skip empty lines. + if ( empty($word) ) + continue; + + /* + * Do some escaping magic so that '#' (number of) characters in the spam + * words don't break things: + */ + $word = preg_quote($word, '#'); + + /* + * Check the comment fields for moderation keywords. If any are found, + * fail the check for the given field by returning false. + */ + $pattern = "#$word#i"; + if ( preg_match($pattern, $author) ) return false; + if ( preg_match($pattern, $email) ) return false; + if ( preg_match($pattern, $url) ) return false; + if ( preg_match($pattern, $comment) ) return false; + if ( preg_match($pattern, $user_ip) ) return false; + if ( preg_match($pattern, $user_agent) ) return false; + } + } + + /* + * Check if the option to approve comments by previously-approved authors is enabled. + * + * If it is enabled, check whether the comment author has a previously-approved comment, + * as well as whether there are any moderation keywords (if set) present in the author + * email address. If both checks pass, return true. Otherwise, return false. + */ + if ( 1 == get_option('comment_whitelist')) { + if ( 'trackback' != $comment_type && 'pingback' != $comment_type && $author != '' && $email != '' ) { + // expected_slashed ($author, $email) + $ok_to_comment = $wpdb->get_var("SELECT comment_approved FROM $wpdb->comments WHERE comment_author = '$author' AND comment_author_email = '$email' and comment_approved = '1' LIMIT 1"); + if ( ( 1 == $ok_to_comment ) && + ( empty($mod_keys) || false === strpos( $email, $mod_keys) ) ) + return true; + else + return false; + } else { + return false; + } + } + return true; +} + +/** + * Retrieve the approved comments for post $post_id. + * + * @since 2.0.0 + * @since 4.1.0 Refactored to leverage {@see WP_Comment_Query} over a direct query. + * + * @param int $post_id The ID of the post. + * @param array $args Optional. See {@see WP_Comment_Query::query()} for information + * on accepted arguments. + * @return int|array $comments The approved comments, or number of comments if `$count` + * argument is true. + */ +function get_approved_comments( $post_id, $args = array() ) { + if ( ! $post_id ) { + return array(); + } + + $defaults = array( + 'status' => 1, + 'post_id' => $post_id, + 'order' => 'ASC', + ); + $r = wp_parse_args( $args, $defaults ); + + $query = new WP_Comment_Query; + return $query->query( $r ); +} + +/** + * Retrieves comment data given a comment ID or comment object. + * + * If an object is passed then the comment data will be cached and then returned + * after being passed through a filter. If the comment is empty, then the global + * comment variable will be used, if it is set. + * + * @since 2.0.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @param object|string|int $comment Comment to retrieve. + * @param string $output Optional. OBJECT or ARRAY_A or ARRAY_N constants. + * @return object|array|null Depends on $output value. + */ +function get_comment(&$comment, $output = OBJECT) { + global $wpdb; + + if ( empty($comment) ) { + if ( isset($GLOBALS['comment']) ) + $_comment = & $GLOBALS['comment']; + else + $_comment = null; + } elseif ( is_object($comment) ) { + wp_cache_add($comment->comment_ID, $comment, 'comment'); + $_comment = $comment; + } else { + if ( isset($GLOBALS['comment']) && ($GLOBALS['comment']->comment_ID == $comment) ) { + $_comment = & $GLOBALS['comment']; + } elseif ( ! $_comment = wp_cache_get($comment, 'comment') ) { + $_comment = $wpdb->get_row($wpdb->prepare("SELECT * FROM $wpdb->comments WHERE comment_ID = %d LIMIT 1", $comment)); + if ( ! $_comment ) + return null; + wp_cache_add($_comment->comment_ID, $_comment, 'comment'); + } + } + + /** + * Fires after a comment is retrieved. + * + * @since 2.3.0 + * + * @param mixed $_comment Comment data. + */ + $_comment = apply_filters( 'get_comment', $_comment ); + + if ( $output == OBJECT ) { + return $_comment; + } elseif ( $output == ARRAY_A ) { + $__comment = get_object_vars($_comment); + return $__comment; + } elseif ( $output == ARRAY_N ) { + $__comment = array_values(get_object_vars($_comment)); + return $__comment; + } else { + return $_comment; + } +} + +/** + * Retrieve a list of comments. + * + * The comment list can be for the blog as a whole or for an individual post. + * + * @since 2.7.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @param string|array $args Optional. Array or string of arguments. See {@see WP_Comment_Query::query()} + * for information on accepted arguments. Default empty. + * @return int|array List of comments or number of found comments if `$count` argument is true. + */ +function get_comments( $args = '' ) { + $query = new WP_Comment_Query; + return $query->query( $args ); +} + +/** + * WordPress Comment Query class. + * + * See {@see WP_Comment_Query::query()} for accepted arguments. + * + * @since 3.1.0 + */ +class WP_Comment_Query { + /** + * SQL for database query. + * + * @since 4.0.1 + * @access public + * @var string + */ + public $request; + + /** + * Metadata query container + * + * @since 3.5.0 + * @access public + * @var object WP_Meta_Query + */ + public $meta_query = false; + + /** + * Date query container + * + * @since 3.7.0 + * @access public + * @var object WP_Date_Query + */ + public $date_query = false; + + /** + * @var array + */ + public $query_vars; + + /** + * @var array + */ + public $comments; + + /** + * Make private/protected methods readable for backwards compatibility. + * + * @since 4.0.0 + * @access public + * + * @param callable $name Method to call. + * @param array $arguments Arguments to pass when calling. + * @return mixed|bool Return value of the callback, false otherwise. + */ + public function __call( $name, $arguments ) { + return call_user_func_array( array( $this, $name ), $arguments ); + } + + /** + * Execute the query + * + * @since 3.1.0 + * @since 4.1.0 Introduced 'comment__in', 'comment__not_in', 'post_author__in', + * 'post_author__not_in', 'author__in', 'author__not_in', 'post__in', + * 'post__not_in', 'include_unapproved', 'type__in', and 'type__not_in' + * arguments to $query_vars. + * + * @param string|array $query_vars { + * Optional. Array or query string of comment query parameters. + * + * @type string $author_email Comment author email address. Default empty. + * @type array $author__in Array of author IDs to include comments for. Default empty. + * @type array $author__not_in Array of author IDs to exclude comments for. Default empty. + * @type array $comment__in Array of comment IDs to include. Default empty. + * @type array $comment__not_in Array of comment IDs to exclude. Default empty. + * @type bool $count Whether to return a comment count (true) or array of comment + * objects (false). Default false. + * @type array $date_query Date query clauses to limit comments by. See {@see WP_Date_Query}. + * Default null. + * @type string $fields Comment fields to return. Accepts 'ids' for comment IDs only or + * empty for all fields. Default empty. + * @type int $ID Currently unused. + * @type array $include_unapproved Array of IDs or email addresses of users whose unapproved comments + * will be returned by the query regardless of `$status`. Default empty. + * @type int $karma Karma score to retrieve matching comments for. Default empty. + * @type string $meta_key Include comments with a matching comment meta key. Default empty. + * @type string $meta_value Include comments with a matching comment meta value. Requires + * `$meta_key` to be set. Default empty. + * @type array $meta_query Meta query clauses to limit retrieved comments by. + * See {@see WP_Meta_Query}. Default empty. + * @type int $number Maximum number of comments to retrieve. Default null (no limit). + * @type int $offset Number of comments to offset the query. Used to build LIMIT clause. + * Default 0. + * @type string|array $orderby Comment status or array of statuses. Accepts 'comment_agent', + * 'comment_approved', 'comment_author', 'comment_author_email', + * 'comment_author_IP', 'comment_author_url', 'comment_content', + * 'comment_date', 'comment_date_gmt', 'comment_ID', 'comment_karma', + * 'comment_parent', 'comment_post_ID', 'comment_type', 'user_id', + * 'meta_value', 'meta_value_num', or value of $meta_key. + * Also accepts false, empty array, or 'none' to disable `ORDER BY` + * clause. Default: 'comment_date_gmt'. + * @type string $order How to order retrieved comments. Accepts 'ASC', 'DESC'. + * Default: 'DESC'. + * @type int $parent Parent ID of comment to retrieve children of. Default empty. + * @type array $post_author__in Array of author IDs to retrieve comments for. Default empty. + * @type array $post_author__not_in Array of author IDs *not* to retrieve comments for. Default empty. + * @type int $post_ID Currently unused. + * @type int $post_id Limit results to those affiliated with a given post ID. Default 0. + * @type array $post__in Array of post IDs to include affiliated comments for. Default empty. + * @type array $post__not_in Array of post IDs to exclude affiliated comments for. Default empty. + * @type int $post_author Comment author ID to limit results by. Default empty. + * @type string $post_status Post status to retrieve affiliated comments for. Default empty. + * @type string $post_type Post type to retrieve affiliated comments for. Default empty. + * @type string $post_name Post name to retrieve affiliated comments for. Default empty. + * @type int $post_parent Post parent ID to retrieve affiliated comments for. Default empty. + * @type string $search Search term(s) to retrieve matching comments for. Default empty. + * @type string $status Comment status to limit results by. Accepts 'hold' + * (`comment_status=0`), 'approve' (`comment_status=1`), 'all', or a + * custom comment status. Default 'all'. + * @type string|array $type Include comments of a given type, or array of types. Accepts + * 'comment', 'pings' (includes 'pingback' and 'trackback'), or any + * custom type string. Default empty. + * @type array $type__in Include comments from a given array of comment types. Default empty. + * @type array $type__not_in Exclude comments from a given array of comment types. Default empty. + * @type int $user_id Include comments for a specific user ID. Default empty. + * } + * @return int|array Array of comments or number of found comments if `$count` is set to true. + */ + public function query( $query_vars ) { + global $wpdb; + + $defaults = array( + 'author_email' => '', + 'author__in' => '', + 'author__not_in' => '', + 'include_unapproved' => '', + 'fields' => '', + 'ID' => '', + 'comment__in' => '', + 'comment__not_in' => '', + 'karma' => '', + 'number' => '', + 'offset' => '', + 'orderby' => '', + 'order' => 'DESC', + 'parent' => '', + 'post_author__in' => '', + 'post_author__not_in' => '', + 'post_ID' => '', + 'post_id' => 0, + 'post__in' => '', + 'post__not_in' => '', + 'post_author' => '', + 'post_name' => '', + 'post_parent' => '', + 'post_status' => '', + 'post_type' => '', + 'status' => 'all', + 'type' => '', + 'type__in' => '', + 'type__not_in' => '', + 'user_id' => '', + 'search' => '', + 'count' => false, + 'meta_key' => '', + 'meta_value' => '', + 'meta_query' => '', + 'date_query' => null, // See WP_Date_Query + ); + + $groupby = ''; + + $this->query_vars = wp_parse_args( $query_vars, $defaults ); + + // Parse meta query + $this->meta_query = new WP_Meta_Query(); + $this->meta_query->parse_query_vars( $this->query_vars ); + + /** + * Fires before comments are retrieved. + * + * @since 3.1.0 + * + * @param WP_Comment_Query &$this Current instance of WP_Comment_Query, passed by reference. + */ + do_action_ref_array( 'pre_get_comments', array( &$this ) ); + + // $args can be whatever, only use the args defined in defaults to compute the key + $key = md5( serialize( wp_array_slice_assoc( $this->query_vars, array_keys( $defaults ) ) ) ); + $last_changed = wp_cache_get( 'last_changed', 'comment' ); + if ( ! $last_changed ) { + $last_changed = microtime(); + wp_cache_set( 'last_changed', $last_changed, 'comment' ); + } + $cache_key = "get_comments:$key:$last_changed"; + + if ( $cache = wp_cache_get( $cache_key, 'comment' ) ) { + return $cache; + } + + $where = array(); + + // Assemble clauses related to 'comment_approved'. + $approved_clauses = array(); + + // 'status' accepts an array or a comma-separated string. + $status_clauses = array(); + $statuses = $this->query_vars['status']; + if ( ! is_array( $statuses ) ) { + $statuses = preg_split( '/[\s,]+/', $statuses ); + } + + // 'any' overrides other statuses. + if ( ! in_array( 'any', $statuses ) ) { + foreach ( $statuses as $status ) { + switch ( $status ) { + case 'hold' : + $status_clauses[] = "comment_approved = '0'"; + break; + + case 'approve' : + $status_clauses[] = "comment_approved = '1'"; + break; + + case 'all' : + case '' : + $status_clauses[] = "( comment_approved = '0' OR comment_approved = '1' )"; + break; + + default : + $status_clauses[] = $wpdb->prepare( "comment_approved = %s", $status ); + break; + } + } + + if ( ! empty( $status_clauses ) ) { + $approved_clauses[] = '( ' . implode( ' OR ', $status_clauses ) . ' )'; + } + } + + // User IDs or emails whose unapproved comments are included, regardless of $status. + if ( ! empty( $this->query_vars['include_unapproved'] ) ) { + $include_unapproved = $this->query_vars['include_unapproved']; + + // Accepts arrays or comma-separated strings. + if ( ! is_array( $include_unapproved ) ) { + $include_unapproved = preg_split( '/[\s,]+/', $include_unapproved ); + } + + $unapproved_ids = $unapproved_emails = array(); + foreach ( $include_unapproved as $unapproved_identifier ) { + // Numeric values are assumed to be user ids. + if ( is_numeric( $unapproved_identifier ) ) { + $approved_clauses[] = $wpdb->prepare( "( user_id = %d AND comment_approved = '0' )", $unapproved_identifier ); + + // Otherwise we match against email addresses. + } else { + $approved_clauses[] = $wpdb->prepare( "( comment_author_email = %s AND comment_approved = '0' )", $unapproved_identifier ); + } + } + } + + // Collapse comment_approved clauses into a single OR-separated clause. + if ( ! empty( $approved_clauses ) ) { + if ( 1 === count( $approved_clauses ) ) { + $where[] = $approved_clauses[0]; + } else { + $where[] = '( ' . implode( ' OR ', $approved_clauses ) . ' )'; + } + } + + $order = ( 'ASC' == strtoupper( $this->query_vars['order'] ) ) ? 'ASC' : 'DESC'; + + // Disable ORDER BY with 'none', an empty array, or boolean false. + if ( in_array( $this->query_vars['orderby'], array( 'none', array(), false ), true ) ) { + $orderby = ''; + } else if ( ! empty( $this->query_vars['orderby'] ) ) { + $ordersby = is_array( $this->query_vars['orderby'] ) ? + $this->query_vars['orderby'] : + preg_split( '/[,\s]/', $this->query_vars['orderby'] ); + + $allowed_keys = array( + 'comment_agent', + 'comment_approved', + 'comment_author', + 'comment_author_email', + 'comment_author_IP', + 'comment_author_url', + 'comment_content', + 'comment_date', + 'comment_date_gmt', + 'comment_ID', + 'comment_karma', + 'comment_parent', + 'comment_post_ID', + 'comment_type', + 'user_id', + ); + if ( ! empty( $this->query_vars['meta_key'] ) ) { + $allowed_keys[] = $this->query_vars['meta_key']; + $allowed_keys[] = 'meta_value'; + $allowed_keys[] = 'meta_value_num'; + } + $ordersby = array_intersect( $ordersby, $allowed_keys ); + foreach ( $ordersby as $key => $value ) { + if ( $value == $this->query_vars['meta_key'] || $value == 'meta_value' ) { + $ordersby[ $key ] = "$wpdb->commentmeta.meta_value"; + } elseif ( $value == 'meta_value_num' ) { + $ordersby[ $key ] = "$wpdb->commentmeta.meta_value+0"; + } + } + $orderby = empty( $ordersby ) ? 'comment_date_gmt' : implode(', ', $ordersby); + } else { + $orderby = 'comment_date_gmt'; + } + + $number = absint( $this->query_vars['number'] ); + $offset = absint( $this->query_vars['offset'] ); + + if ( ! empty( $number ) ) { + if ( $offset ) { + $limits = 'LIMIT ' . $offset . ',' . $number; + } else { + $limits = 'LIMIT ' . $number; + } + } else { + $limits = ''; + } + + if ( $this->query_vars['count'] ) { + $fields = 'COUNT(*)'; + } else { + switch ( strtolower( $this->query_vars['fields'] ) ) { + case 'ids': + $fields = "$wpdb->comments.comment_ID"; + break; + default: + $fields = "*"; + break; + } + } + + $join = ''; + + $post_id = absint( $this->query_vars['post_id'] ); + if ( ! empty( $post_id ) ) { + $where[] = $wpdb->prepare( 'comment_post_ID = %d', $post_id ); + } + + // Parse comment IDs for an IN clause. + if ( ! empty( $this->query_vars['comment__in'] ) ) { + $where[] = 'comment_ID IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['comment__in'] ) ) . ' )'; + } + + // Parse comment IDs for a NOT IN clause. + if ( ! empty( $this->query_vars['comment__not_in'] ) ) { + $where[] = 'comment_ID NOT IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['comment__not_in'] ) ) . ' )'; + } + + // Parse comment post IDs for an IN clause. + if ( ! empty( $this->query_vars['post__in'] ) ) { + $where[] = 'comment_post_ID IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['post__in'] ) ) . ' )'; + } + + // Parse comment post IDs for a NOT IN clause. + if ( ! empty( $this->query_vars['post__not_in'] ) ) { + $where[] = 'comment_post_ID NOT IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['post__not_in'] ) ) . ' )'; + } + + if ( '' !== $this->query_vars['author_email'] ) { + $where[] = $wpdb->prepare( 'comment_author_email = %s', $this->query_vars['author_email'] ); + } + + if ( '' !== $this->query_vars['karma'] ) { + $where[] = $wpdb->prepare( 'comment_karma = %d', $this->query_vars['karma'] ); + } + + // Filtering by comment_type: 'type', 'type__in', 'type__not_in'. + $raw_types = array( + 'IN' => array_merge( (array) $this->query_vars['type'], (array) $this->query_vars['type__in'] ), + 'NOT IN' => (array) $this->query_vars['type__not_in'], + ); + + $comment_types = array(); + foreach ( $raw_types as $operator => $_raw_types ) { + $_raw_types = array_unique( $_raw_types ); + + foreach ( $_raw_types as $type ) { + switch ( $type ) { + // An empty translates to 'all', for backward compatibility + case '': + case 'all' : + break; + + case 'comment': + case 'comments': + $comment_types[ $operator ][] = "''"; + break; + + case 'pings': + $comment_types[ $operator ][] = "'pingback'"; + $comment_types[ $operator ][] = "'trackback'"; + break; + + default: + $comment_types[ $operator ][] = $wpdb->prepare( '%s', $type ); + break; + } + } + + if ( ! empty( $comment_types[ $operator ] ) ) { + $types_sql = implode( ', ', $comment_types[ $operator ] ); + $where[] = "comment_type $operator ($types_sql)"; + } + } + + if ( '' !== $this->query_vars['parent'] ) { + $where[] = $wpdb->prepare( 'comment_parent = %d', $this->query_vars['parent'] ); + } + + if ( is_array( $this->query_vars['user_id'] ) ) { + $where[] = 'user_id IN (' . implode( ',', array_map( 'absint', $this->query_vars['user_id'] ) ) . ')'; + } elseif ( '' !== $this->query_vars['user_id'] ) { + $where[] = $wpdb->prepare( 'user_id = %d', $this->query_vars['user_id'] ); + } + + if ( '' !== $this->query_vars['search'] ) { + $search_sql = $this->get_search_sql( + $this->query_vars['search'], + array( 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_author_IP', 'comment_content' ) + ); + + // Strip leading 'AND'. + $where[] = preg_replace( '/^\s*AND\s*/', '', $search_sql ); + } + + // If any post-related query vars are passed, join the posts table. + $join_posts_table = false; + $plucked = wp_array_slice_assoc( $this->query_vars, array( 'post_author', 'post_name', 'post_parent', 'post_status', 'post_type' ) ); + $post_fields = array_filter( $plucked ); + + if ( ! empty( $post_fields ) ) { + $join_posts_table = true; + foreach ( $post_fields as $field_name => $field_value ) { + $where[] = $wpdb->prepare( " {$wpdb->posts}.{$field_name} = %s", $field_value ); + } + } + + // Comment author IDs for an IN clause. + if ( ! empty( $this->query_vars['author__in'] ) ) { + $where[] = 'user_id IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['author__in'] ) ) . ' )'; + } + + // Comment author IDs for a NOT IN clause. + if ( ! empty( $this->query_vars['author__not_in'] ) ) { + $where[] = 'user_id NOT IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['author__not_in'] ) ) . ' )'; + } + + // Post author IDs for an IN clause. + if ( ! empty( $this->query_vars['post_author__in'] ) ) { + $join_posts_table = true; + $where[] = 'post_author IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['post_author__in'] ) ) . ' )'; + } + + // Post author IDs for a NOT IN clause. + if ( ! empty( $this->query_vars['post_author__not_in'] ) ) { + $join_posts_table = true; + $where[] = 'post_author NOT IN ( ' . implode( ',', wp_parse_id_list( $this->query_vars['post_author__not_in'] ) ) . ' )'; + } + + if ( $join_posts_table ) { + $join = "JOIN $wpdb->posts ON $wpdb->posts.ID = $wpdb->comments.comment_post_ID"; + } + + if ( ! empty( $this->meta_query->queries ) ) { + $clauses = $this->meta_query->get_sql( 'comment', $wpdb->comments, 'comment_ID', $this ); + $join .= $clauses['join']; + + // Strip leading 'AND'. + $where[] = preg_replace( '/^\s*AND\s*/', '', $clauses['where'] ); + + if ( ! $this->query_vars['count'] ) { + $groupby = "{$wpdb->comments}.comment_ID"; + } + } + + $date_query = $this->query_vars['date_query']; + if ( ! empty( $date_query ) && is_array( $date_query ) ) { + $date_query_object = new WP_Date_Query( $date_query, 'comment_date' ); + $where[] = preg_replace( '/^\s*AND\s*/', '', $date_query_object->get_sql() ); + } + + $where = implode( ' AND ', $where ); + + $pieces = array( 'fields', 'join', 'where', 'orderby', 'order', 'limits', 'groupby' ); + /** + * Filter the comment query clauses. + * + * @since 3.1.0 + * + * @param array $pieces A compacted array of comment query clauses. + * @param WP_Comment_Query &$this Current instance of WP_Comment_Query, passed by reference. + */ + $clauses = apply_filters_ref_array( 'comments_clauses', array( compact( $pieces ), &$this ) ); + + $fields = isset( $clauses[ 'fields' ] ) ? $clauses[ 'fields' ] : ''; + $join = isset( $clauses[ 'join' ] ) ? $clauses[ 'join' ] : ''; + $where = isset( $clauses[ 'where' ] ) ? $clauses[ 'where' ] : ''; + $orderby = isset( $clauses[ 'orderby' ] ) ? $clauses[ 'orderby' ] : ''; + $order = isset( $clauses[ 'order' ] ) ? $clauses[ 'order' ] : ''; + $limits = isset( $clauses[ 'limits' ] ) ? $clauses[ 'limits' ] : ''; + $groupby = isset( $clauses[ 'groupby' ] ) ? $clauses[ 'groupby' ] : ''; + + if ( $where ) { + $where = 'WHERE ' . $where; + } + + if ( $groupby ) { + $groupby = 'GROUP BY ' . $groupby; + } + + if ( $orderby ) { + $orderby = "ORDER BY $orderby $order"; + } + + $this->request = "SELECT $fields FROM $wpdb->comments $join $where $groupby $orderby $limits"; + + if ( $this->query_vars['count'] ) { + return $wpdb->get_var( $this->request ); + } + + if ( 'ids' == $this->query_vars['fields'] ) { + $this->comments = $wpdb->get_col( $this->request ); + return array_map( 'intval', $this->comments ); + } + + $results = $wpdb->get_results( $this->request ); + /** + * Filter the comment query results. + * + * @since 3.1.0 + * + * @param array $results An array of comments. + * @param WP_Comment_Query &$this Current instance of WP_Comment_Query, passed by reference. + */ + $comments = apply_filters_ref_array( 'the_comments', array( $results, &$this ) ); + + wp_cache_add( $cache_key, $comments, 'comment' ); + + return $comments; + } + + /** + * Used internally to generate an SQL string for searching across multiple columns + * + * @access protected + * @since 3.1.0 + * + * @param string $string + * @param array $cols + * @return string + */ + protected function get_search_sql( $string, $cols ) { + global $wpdb; + + $like = '%' . $wpdb->esc_like( $string ) . '%'; + + $searches = array(); + foreach ( $cols as $col ) { + $searches[] = $wpdb->prepare( "$col LIKE %s", $like ); + } + + return ' AND (' . implode(' OR ', $searches) . ')'; + } +} + +/** + * Retrieve all of the WordPress supported comment statuses. + * + * Comments have a limited set of valid status values, this provides the comment + * status values and descriptions. + * + * @since 2.7.0 + * + * @return array List of comment statuses. + */ +function get_comment_statuses() { + $status = array( + 'hold' => __('Unapproved'), + /* translators: comment status */ + 'approve' => _x('Approved', 'adjective'), + /* translators: comment status */ + 'spam' => _x('Spam', 'adjective'), + ); + + return $status; +} + +/** + * The date the last comment was modified. + * + * @since 1.5.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @param string $timezone Which timezone to use in reference to 'gmt', 'blog', + * or 'server' locations. + * @return string Last comment modified date. + */ +function get_lastcommentmodified($timezone = 'server') { + global $wpdb; + static $cache_lastcommentmodified = array(); + + if ( isset($cache_lastcommentmodified[$timezone]) ) + return $cache_lastcommentmodified[$timezone]; + + $add_seconds_server = date('Z'); + + switch ( strtolower($timezone)) { + case 'gmt': + $lastcommentmodified = $wpdb->get_var("SELECT comment_date_gmt FROM $wpdb->comments WHERE comment_approved = '1' ORDER BY comment_date_gmt DESC LIMIT 1"); + break; + case 'blog': + $lastcommentmodified = $wpdb->get_var("SELECT comment_date FROM $wpdb->comments WHERE comment_approved = '1' ORDER BY comment_date_gmt DESC LIMIT 1"); + break; + case 'server': + $lastcommentmodified = $wpdb->get_var($wpdb->prepare("SELECT DATE_ADD(comment_date_gmt, INTERVAL %s SECOND) FROM $wpdb->comments WHERE comment_approved = '1' ORDER BY comment_date_gmt DESC LIMIT 1", $add_seconds_server)); + break; + } + + $cache_lastcommentmodified[$timezone] = $lastcommentmodified; + + return $lastcommentmodified; +} + +/** + * The amount of comments in a post or total comments. + * + * A lot like {@link wp_count_comments()}, in that they both return comment + * stats (albeit with different types). The {@link wp_count_comments()} actual + * caches, but this function does not. + * + * @since 2.0.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @param int $post_id Optional. Comment amount in post if > 0, else total comments blog wide. + * @return array The amount of spam, approved, awaiting moderation, and total comments. + */ +function get_comment_count( $post_id = 0 ) { + global $wpdb; + + $post_id = (int) $post_id; + + $where = ''; + if ( $post_id > 0 ) { + $where = $wpdb->prepare("WHERE comment_post_ID = %d", $post_id); + } + + $totals = (array) $wpdb->get_results(" + SELECT comment_approved, COUNT( * ) AS total + FROM {$wpdb->comments} + {$where} + GROUP BY comment_approved + ", ARRAY_A); + + $comment_count = array( + "approved" => 0, + "awaiting_moderation" => 0, + "spam" => 0, + "total_comments" => 0 + ); + + foreach ( $totals as $row ) { + switch ( $row['comment_approved'] ) { + case 'spam': + $comment_count['spam'] = $row['total']; + $comment_count["total_comments"] += $row['total']; + break; + case 1: + $comment_count['approved'] = $row['total']; + $comment_count['total_comments'] += $row['total']; + break; + case 0: + $comment_count['awaiting_moderation'] = $row['total']; + $comment_count['total_comments'] += $row['total']; + break; + default: + break; + } + } + + return $comment_count; +} + +// +// Comment meta functions +// + +/** + * Add meta data field to a comment. + * + * @since 2.9.0 + * @link http://codex.wordpress.org/Function_Reference/add_comment_meta + * + * @param int $comment_id Comment ID. + * @param string $meta_key Metadata name. + * @param mixed $meta_value Metadata value. + * @param bool $unique Optional, default is false. Whether the same key should not be added. + * @return int|bool Meta ID on success, false on failure. + */ +function add_comment_meta($comment_id, $meta_key, $meta_value, $unique = false) { + return add_metadata('comment', $comment_id, $meta_key, $meta_value, $unique); +} + +/** + * Remove metadata matching criteria from a comment. + * + * You can match based on the key, or key and value. Removing based on key and + * value, will keep from removing duplicate metadata with the same key. It also + * allows removing all metadata matching key, if needed. + * + * @since 2.9.0 + * @link http://codex.wordpress.org/Function_Reference/delete_comment_meta + * + * @param int $comment_id comment ID + * @param string $meta_key Metadata name. + * @param mixed $meta_value Optional. Metadata value. + * @return bool True on success, false on failure. + */ +function delete_comment_meta($comment_id, $meta_key, $meta_value = '') { + return delete_metadata('comment', $comment_id, $meta_key, $meta_value); +} + +/** + * Retrieve comment meta field for a comment. + * + * @since 2.9.0 + * @link http://codex.wordpress.org/Function_Reference/get_comment_meta + * + * @param int $comment_id Comment ID. + * @param string $key Optional. The meta key to retrieve. By default, returns data for all keys. + * @param bool $single Whether to return a single value. + * @return mixed Will be an array if $single is false. Will be value of meta data field if $single + * is true. + */ +function get_comment_meta($comment_id, $key = '', $single = false) { + return get_metadata('comment', $comment_id, $key, $single); +} + +/** + * Update comment meta field based on comment ID. + * + * Use the $prev_value parameter to differentiate between meta fields with the + * same key and comment ID. + * + * If the meta field for the comment does not exist, it will be added. + * + * @since 2.9.0 + * @link http://codex.wordpress.org/Function_Reference/update_comment_meta + * + * @param int $comment_id Comment ID. + * @param string $meta_key Metadata key. + * @param mixed $meta_value Metadata value. + * @param mixed $prev_value Optional. Previous value to check before removing. + * @return int|bool Meta ID if the key didn't exist, true on successful update, false on failure. + */ +function update_comment_meta($comment_id, $meta_key, $meta_value, $prev_value = '') { + return update_metadata('comment', $comment_id, $meta_key, $meta_value, $prev_value); +} + +/** + * Sets the cookies used to store an unauthenticated commentator's identity. Typically used + * to recall previous comments by this commentator that are still held in moderation. + * + * @param object $comment Comment object. + * @param object $user Comment author's object. + * + * @since 3.4.0 + */ +function wp_set_comment_cookies($comment, $user) { + if ( $user->exists() ) + return; + + /** + * Filter the lifetime of the comment cookie in seconds. + * + * @since 2.8.0 + * + * @param int $seconds Comment cookie lifetime. Default 30000000. + */ + $comment_cookie_lifetime = apply_filters( 'comment_cookie_lifetime', 30000000 ); + $secure = ( 'https' === parse_url( home_url(), PHP_URL_SCHEME ) ); + setcookie( 'comment_author_' . COOKIEHASH, $comment->comment_author, time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN, $secure ); + setcookie( 'comment_author_email_' . COOKIEHASH, $comment->comment_author_email, time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN, $secure ); + setcookie( 'comment_author_url_' . COOKIEHASH, esc_url($comment->comment_author_url), time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN, $secure ); +} + +/** + * Sanitizes the cookies sent to the user already. + * + * Will only do anything if the cookies have already been created for the user. + * Mostly used after cookies had been sent to use elsewhere. + * + * @since 2.0.4 + */ +function sanitize_comment_cookies() { + if ( isset( $_COOKIE['comment_author_' . COOKIEHASH] ) ) { + /** + * Filter the comment author's name cookie before it is set. + * + * When this filter hook is evaluated in wp_filter_comment(), + * the comment author's name string is passed. + * + * @since 1.5.0 + * + * @param string $author_cookie The comment author name cookie. + */ + $comment_author = apply_filters( 'pre_comment_author_name', $_COOKIE['comment_author_' . COOKIEHASH] ); + $comment_author = wp_unslash($comment_author); + $comment_author = esc_attr($comment_author); + $_COOKIE['comment_author_' . COOKIEHASH] = $comment_author; + } + + if ( isset( $_COOKIE['comment_author_email_' . COOKIEHASH] ) ) { + /** + * Filter the comment author's email cookie before it is set. + * + * When this filter hook is evaluated in wp_filter_comment(), + * the comment author's email string is passed. + * + * @since 1.5.0 + * + * @param string $author_email_cookie The comment author email cookie. + */ + $comment_author_email = apply_filters( 'pre_comment_author_email', $_COOKIE['comment_author_email_' . COOKIEHASH] ); + $comment_author_email = wp_unslash($comment_author_email); + $comment_author_email = esc_attr($comment_author_email); + $_COOKIE['comment_author_email_'.COOKIEHASH] = $comment_author_email; + } + + if ( isset( $_COOKIE['comment_author_url_' . COOKIEHASH] ) ) { + /** + * Filter the comment author's URL cookie before it is set. + * + * When this filter hook is evaluated in wp_filter_comment(), + * the comment author's URL string is passed. + * + * @since 1.5.0 + * + * @param string $author_url_cookie The comment author URL cookie. + */ + $comment_author_url = apply_filters( 'pre_comment_author_url', $_COOKIE['comment_author_url_' . COOKIEHASH] ); + $comment_author_url = wp_unslash($comment_author_url); + $_COOKIE['comment_author_url_'.COOKIEHASH] = $comment_author_url; + } +} + +/** + * Validates whether this comment is allowed to be made. + * + * @since 2.0.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @param array $commentdata Contains information on the comment + * @return mixed Signifies the approval status (0|1|'spam') + */ +function wp_allow_comment( $commentdata ) { + global $wpdb; + + // Simple duplicate check + // expected_slashed ($comment_post_ID, $comment_author, $comment_author_email, $comment_content) + $dupe = $wpdb->prepare( + "SELECT comment_ID FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_parent = %s AND comment_approved != 'trash' AND ( comment_author = %s ", + wp_unslash( $commentdata['comment_post_ID'] ), + wp_unslash( $commentdata['comment_parent'] ), + wp_unslash( $commentdata['comment_author'] ) + ); + if ( $commentdata['comment_author_email'] ) { + $dupe .= $wpdb->prepare( + "OR comment_author_email = %s ", + wp_unslash( $commentdata['comment_author_email'] ) + ); + } + $dupe .= $wpdb->prepare( + ") AND comment_content = %s LIMIT 1", + wp_unslash( $commentdata['comment_content'] ) + ); + if ( $wpdb->get_var( $dupe ) ) { + /** + * Fires immediately after a duplicate comment is detected. + * + * @since 3.0.0 + * + * @param array $commentdata Comment data. + */ + do_action( 'comment_duplicate_trigger', $commentdata ); + if ( defined( 'DOING_AJAX' ) ) { + die( __('Duplicate comment detected; it looks as though you’ve already said that!') ); + } + wp_die( __( 'Duplicate comment detected; it looks as though you’ve already said that!' ), 409 ); + } + + /** + * Fires immediately before a comment is marked approved. + * + * Allows checking for comment flooding. + * + * @since 2.3.0 + * + * @param string $comment_author_IP Comment author's IP address. + * @param string $comment_author_email Comment author's email. + * @param string $comment_date_gmt GMT date the comment was posted. + */ + do_action( + 'check_comment_flood', + $commentdata['comment_author_IP'], + $commentdata['comment_author_email'], + $commentdata['comment_date_gmt'] + ); + + if ( ! empty( $commentdata['user_id'] ) ) { + $user = get_userdata( $commentdata['user_id'] ); + $post_author = $wpdb->get_var( $wpdb->prepare( + "SELECT post_author FROM $wpdb->posts WHERE ID = %d LIMIT 1", + $commentdata['comment_post_ID'] + ) ); + } + + if ( isset( $user ) && ( $commentdata['user_id'] == $post_author || $user->has_cap( 'moderate_comments' ) ) ) { + // The author and the admins get respect. + $approved = 1; + } else { + // Everyone else's comments will be checked. + if ( check_comment( + $commentdata['comment_author'], + $commentdata['comment_author_email'], + $commentdata['comment_author_url'], + $commentdata['comment_content'], + $commentdata['comment_author_IP'], + $commentdata['comment_agent'], + $commentdata['comment_type'] + ) ) { + $approved = 1; + } else { + $approved = 0; + } + + if ( wp_blacklist_check( + $commentdata['comment_author'], + $commentdata['comment_author_email'], + $commentdata['comment_author_url'], + $commentdata['comment_content'], + $commentdata['comment_author_IP'], + $commentdata['comment_agent'] + ) ) { + $approved = 'spam'; + } + } + + /** + * Filter a comment's approval status before it is set. + * + * @since 2.1.0 + * + * @param bool|string $approved The approval status. Accepts 1, 0, or 'spam'. + * @param array $commentdata Comment data. + */ + $approved = apply_filters( 'pre_comment_approved', $approved, $commentdata ); + return $approved; +} + +/** + * Check whether comment flooding is occurring. + * + * Won't run, if current user can manage options, so to not block + * administrators. + * + * @since 2.3.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @param string $ip Comment IP. + * @param string $email Comment author email address. + * @param string $date MySQL time string. + */ +function check_comment_flood_db( $ip, $email, $date ) { + global $wpdb; + if ( current_user_can( 'manage_options' ) ) + return; // don't throttle admins + $hour_ago = gmdate( 'Y-m-d H:i:s', time() - HOUR_IN_SECONDS ); + if ( $lasttime = $wpdb->get_var( $wpdb->prepare( "SELECT `comment_date_gmt` FROM `$wpdb->comments` WHERE `comment_date_gmt` >= %s AND ( `comment_author_IP` = %s OR `comment_author_email` = %s ) ORDER BY `comment_date_gmt` DESC LIMIT 1", $hour_ago, $ip, $email ) ) ) { + $time_lastcomment = mysql2date('U', $lasttime, false); + $time_newcomment = mysql2date('U', $date, false); + /** + * Filter the comment flood status. + * + * @since 2.1.0 + * + * @param bool $bool Whether a comment flood is occurring. Default false. + * @param int $time_lastcomment Timestamp of when the last comment was posted. + * @param int $time_newcomment Timestamp of when the new comment was posted. + */ + $flood_die = apply_filters( 'comment_flood_filter', false, $time_lastcomment, $time_newcomment ); + if ( $flood_die ) { + /** + * Fires before the comment flood message is triggered. + * + * @since 1.5.0 + * + * @param int $time_lastcomment Timestamp of when the last comment was posted. + * @param int $time_newcomment Timestamp of when the new comment was posted. + */ + do_action( 'comment_flood_trigger', $time_lastcomment, $time_newcomment ); + + if ( defined('DOING_AJAX') ) + die( __('You are posting comments too quickly. Slow down.') ); + + wp_die( __( 'You are posting comments too quickly. Slow down.' ), 429 ); + } + } +} + +/** + * Separates an array of comments into an array keyed by comment_type. + * + * @since 2.7.0 + * + * @param array $comments Array of comments + * @return array Array of comments keyed by comment_type. + */ +function separate_comments(&$comments) { + $comments_by_type = array('comment' => array(), 'trackback' => array(), 'pingback' => array(), 'pings' => array()); + $count = count($comments); + for ( $i = 0; $i < $count; $i++ ) { + $type = $comments[$i]->comment_type; + if ( empty($type) ) + $type = 'comment'; + $comments_by_type[$type][] = &$comments[$i]; + if ( 'trackback' == $type || 'pingback' == $type ) + $comments_by_type['pings'][] = &$comments[$i]; + } + + return $comments_by_type; +} + +/** + * Calculate the total number of comment pages. + * + * @since 2.7.0 + * + * @uses Walker_Comment + * + * @param array $comments Optional array of comment objects. Defaults to $wp_query->comments + * @param int $per_page Optional comments per page. + * @param boolean $threaded Optional control over flat or threaded comments. + * @return int Number of comment pages. + */ +function get_comment_pages_count( $comments = null, $per_page = null, $threaded = null ) { + global $wp_query; + + if ( null === $comments && null === $per_page && null === $threaded && !empty($wp_query->max_num_comment_pages) ) + return $wp_query->max_num_comment_pages; + + if ( ( ! $comments || ! is_array( $comments ) ) && ! empty( $wp_query->comments ) ) + $comments = $wp_query->comments; + + if ( empty($comments) ) + return 0; + + if ( ! get_option( 'page_comments' ) ) + return 1; + + if ( !isset($per_page) ) + $per_page = (int) get_query_var('comments_per_page'); + if ( 0 === $per_page ) + $per_page = (int) get_option('comments_per_page'); + if ( 0 === $per_page ) + return 1; + + if ( !isset($threaded) ) + $threaded = get_option('thread_comments'); + + if ( $threaded ) { + $walker = new Walker_Comment; + $count = ceil( $walker->get_number_of_root_elements( $comments ) / $per_page ); + } else { + $count = ceil( count( $comments ) / $per_page ); + } + + return $count; +} + +/** + * Calculate what page number a comment will appear on for comment paging. + * + * @since 2.7.0 + * + * @param int $comment_ID Comment ID. + * @param array $args Optional args. + * @return int|null Comment page number or null on error. + */ +function get_page_of_comment( $comment_ID, $args = array() ) { + global $wpdb; + + if ( !$comment = get_comment( $comment_ID ) ) + return; + + $defaults = array( 'type' => 'all', 'page' => '', 'per_page' => '', 'max_depth' => '' ); + $args = wp_parse_args( $args, $defaults ); + + if ( '' === $args['per_page'] && get_option('page_comments') ) + $args['per_page'] = get_query_var('comments_per_page'); + if ( empty($args['per_page']) ) { + $args['per_page'] = 0; + $args['page'] = 0; + } + if ( $args['per_page'] < 1 ) + return 1; + + if ( '' === $args['max_depth'] ) { + if ( get_option('thread_comments') ) + $args['max_depth'] = get_option('thread_comments_depth'); + else + $args['max_depth'] = -1; + } + + // Find this comment's top level parent if threading is enabled + if ( $args['max_depth'] > 1 && 0 != $comment->comment_parent ) + return get_page_of_comment( $comment->comment_parent, $args ); + + $allowedtypes = array( + 'comment' => '', + 'pingback' => 'pingback', + 'trackback' => 'trackback', + ); + + $comtypewhere = ( 'all' != $args['type'] && isset($allowedtypes[$args['type']]) ) ? " AND comment_type = '" . $allowedtypes[$args['type']] . "'" : ''; + + // Count comments older than this one + $oldercoms = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(comment_ID) FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_parent = 0 AND comment_approved = '1' AND comment_date_gmt < '%s'" . $comtypewhere, $comment->comment_post_ID, $comment->comment_date_gmt ) ); + + // No older comments? Then it's page #1. + if ( 0 == $oldercoms ) + return 1; + + // Divide comments older than this one by comments per page to get this comment's page number + return ceil( ( $oldercoms + 1 ) / $args['per_page'] ); +} + +/** + * Does comment contain blacklisted characters or words. + * + * @since 1.5.0 + * + * @param string $author The author of the comment + * @param string $email The email of the comment + * @param string $url The url used in the comment + * @param string $comment The comment content + * @param string $user_ip The comment author IP address + * @param string $user_agent The author's browser user agent + * @return bool True if comment contains blacklisted content, false if comment does not + */ +function wp_blacklist_check($author, $email, $url, $comment, $user_ip, $user_agent) { + /** + * Fires before the comment is tested for blacklisted characters or words. + * + * @since 1.5.0 + * + * @param string $author Comment author. + * @param string $email Comment author's email. + * @param string $url Comment author's URL. + * @param string $comment Comment content. + * @param string $user_ip Comment author's IP address. + * @param string $user_agent Comment author's browser user agent. + */ + do_action( 'wp_blacklist_check', $author, $email, $url, $comment, $user_ip, $user_agent ); + + $mod_keys = trim( get_option('blacklist_keys') ); + if ( '' == $mod_keys ) + return false; // If moderation keys are empty + $words = explode("\n", $mod_keys ); + + foreach ( (array) $words as $word ) { + $word = trim($word); + + // Skip empty lines + if ( empty($word) ) { continue; } + + // Do some escaping magic so that '#' chars in the + // spam words don't break things: + $word = preg_quote($word, '#'); + + $pattern = "#$word#i"; + if ( + preg_match($pattern, $author) + || preg_match($pattern, $email) + || preg_match($pattern, $url) + || preg_match($pattern, $comment) + || preg_match($pattern, $user_ip) + || preg_match($pattern, $user_agent) + ) + return true; + } + return false; +} + +/** + * Retrieve total comments for blog or single post. + * + * The properties of the returned object contain the 'moderated', 'approved', + * and spam comments for either the entire blog or single post. Those properties + * contain the amount of comments that match the status. The 'total_comments' + * property contains the integer of total comments. + * + * The comment stats are cached and then retrieved, if they already exist in the + * cache. + * + * @since 2.5.0 + * + * @param int $post_id Optional. Post ID. + * @return object Comment stats. + */ +function wp_count_comments( $post_id = 0 ) { + global $wpdb; + + $post_id = (int) $post_id; + + /** + * Filter the comments count for a given post. + * + * @since 2.7.0 + * + * @param array $count An empty array. + * @param int $post_id The post ID. + */ + $stats = apply_filters( 'wp_count_comments', array(), $post_id ); + if ( !empty($stats) ) + return $stats; + + $count = wp_cache_get("comments-{$post_id}", 'counts'); + + if ( false !== $count ) + return $count; + + $where = ''; + if ( $post_id > 0 ) + $where = $wpdb->prepare( "WHERE comment_post_ID = %d", $post_id ); + + $count = $wpdb->get_results( "SELECT comment_approved, COUNT( * ) AS num_comments FROM {$wpdb->comments} {$where} GROUP BY comment_approved", ARRAY_A ); + + $total = 0; + $approved = array('0' => 'moderated', '1' => 'approved', 'spam' => 'spam', 'trash' => 'trash', 'post-trashed' => 'post-trashed'); + foreach ( (array) $count as $row ) { + // Don't count post-trashed toward totals + if ( 'post-trashed' != $row['comment_approved'] && 'trash' != $row['comment_approved'] ) + $total += $row['num_comments']; + if ( isset( $approved[$row['comment_approved']] ) ) + $stats[$approved[$row['comment_approved']]] = $row['num_comments']; + } + + $stats['total_comments'] = $total; + foreach ( $approved as $key ) { + if ( empty($stats[$key]) ) + $stats[$key] = 0; + } + + $stats = (object) $stats; + wp_cache_set("comments-{$post_id}", $stats, 'counts'); + + return $stats; +} + +/** + * Trashes or deletes a comment. + * + * The comment is moved to trash instead of permanently deleted unless trash is + * disabled, item is already in the trash, or $force_delete is true. + * + * The post comment count will be updated if the comment was approved and has a + * post ID available. + * + * @since 2.0.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @param int $comment_id Comment ID + * @param bool $force_delete Whether to bypass trash and force deletion. Default is false. + * @return bool True on success, false on failure. + */ +function wp_delete_comment($comment_id, $force_delete = false) { + global $wpdb; + if (!$comment = get_comment($comment_id)) + return false; + + if ( !$force_delete && EMPTY_TRASH_DAYS && !in_array( wp_get_comment_status($comment_id), array( 'trash', 'spam' ) ) ) + return wp_trash_comment($comment_id); + + /** + * Fires immediately before a comment is deleted from the database. + * + * @since 1.2.0 + * + * @param int $comment_id The comment ID. + */ + do_action( 'delete_comment', $comment_id ); + + // Move children up a level. + $children = $wpdb->get_col( $wpdb->prepare("SELECT comment_ID FROM $wpdb->comments WHERE comment_parent = %d", $comment_id) ); + if ( !empty($children) ) { + $wpdb->update($wpdb->comments, array('comment_parent' => $comment->comment_parent), array('comment_parent' => $comment_id)); + clean_comment_cache($children); + } + + // Delete metadata + $meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT meta_id FROM $wpdb->commentmeta WHERE comment_id = %d", $comment_id ) ); + foreach ( $meta_ids as $mid ) + delete_metadata_by_mid( 'comment', $mid ); + + if ( ! $wpdb->delete( $wpdb->comments, array( 'comment_ID' => $comment_id ) ) ) + return false; + + /** + * Fires immediately after a comment is deleted from the database. + * + * @since 2.9.0 + * + * @param int $comment_id The comment ID. + */ + do_action( 'deleted_comment', $comment_id ); + + $post_id = $comment->comment_post_ID; + if ( $post_id && $comment->comment_approved == 1 ) + wp_update_comment_count($post_id); + + clean_comment_cache($comment_id); + + /** This action is documented in wp-includes/comment.php */ + do_action( 'wp_set_comment_status', $comment_id, 'delete' ); + + wp_transition_comment_status('delete', $comment->comment_approved, $comment); + return true; +} + +/** + * Moves a comment to the Trash + * + * If trash is disabled, comment is permanently deleted. + * + * @since 2.9.0 + * + * @param int $comment_id Comment ID. + * @return bool True on success, false on failure. + */ +function wp_trash_comment($comment_id) { + if ( !EMPTY_TRASH_DAYS ) + return wp_delete_comment($comment_id, true); + + if ( !$comment = get_comment($comment_id) ) + return false; + + /** + * Fires immediately before a comment is sent to the Trash. + * + * @since 2.9.0 + * + * @param int $comment_id The comment ID. + */ + do_action( 'trash_comment', $comment_id ); + + if ( wp_set_comment_status($comment_id, 'trash') ) { + add_comment_meta($comment_id, '_wp_trash_meta_status', $comment->comment_approved); + add_comment_meta($comment_id, '_wp_trash_meta_time', time() ); + + /** + * Fires immediately after a comment is sent to Trash. + * + * @since 2.9.0 + * + * @param int $comment_id The comment ID. + */ + do_action( 'trashed_comment', $comment_id ); + return true; + } + + return false; +} + +/** + * Removes a comment from the Trash + * + * @since 2.9.0 + * + * @param int $comment_id Comment ID. + * @return bool True on success, false on failure. + */ +function wp_untrash_comment($comment_id) { + if ( ! (int)$comment_id ) + return false; + + /** + * Fires immediately before a comment is restored from the Trash. + * + * @since 2.9.0 + * + * @param int $comment_id The comment ID. + */ + do_action( 'untrash_comment', $comment_id ); + + $status = (string) get_comment_meta($comment_id, '_wp_trash_meta_status', true); + if ( empty($status) ) + $status = '0'; + + if ( wp_set_comment_status($comment_id, $status) ) { + delete_comment_meta($comment_id, '_wp_trash_meta_time'); + delete_comment_meta($comment_id, '_wp_trash_meta_status'); + /** + * Fires immediately after a comment is restored from the Trash. + * + * @since 2.9.0 + * + * @param int $comment_id The comment ID. + */ + do_action( 'untrashed_comment', $comment_id ); + return true; + } + + return false; +} + +/** + * Marks a comment as Spam + * + * @since 2.9.0 + * + * @param int $comment_id Comment ID. + * @return bool True on success, false on failure. + */ +function wp_spam_comment($comment_id) { + if ( !$comment = get_comment($comment_id) ) + return false; + + /** + * Fires immediately before a comment is marked as Spam. + * + * @since 2.9.0 + * + * @param int $comment_id The comment ID. + */ + do_action( 'spam_comment', $comment_id ); + + if ( wp_set_comment_status($comment_id, 'spam') ) { + add_comment_meta($comment_id, '_wp_trash_meta_status', $comment->comment_approved); + /** + * Fires immediately after a comment is marked as Spam. + * + * @since 2.9.0 + * + * @param int $comment_id The comment ID. + */ + do_action( 'spammed_comment', $comment_id ); + return true; + } + + return false; +} + +/** + * Removes a comment from the Spam + * + * @since 2.9.0 + * + * @param int $comment_id Comment ID. + * @return bool True on success, false on failure. + */ +function wp_unspam_comment($comment_id) { + if ( ! (int)$comment_id ) + return false; + + /** + * Fires immediately before a comment is unmarked as Spam. + * + * @since 2.9.0 + * + * @param int $comment_id The comment ID. + */ + do_action( 'unspam_comment', $comment_id ); + + $status = (string) get_comment_meta($comment_id, '_wp_trash_meta_status', true); + if ( empty($status) ) + $status = '0'; + + if ( wp_set_comment_status($comment_id, $status) ) { + delete_comment_meta($comment_id, '_wp_trash_meta_status'); + /** + * Fires immediately after a comment is unmarked as Spam. + * + * @since 2.9.0 + * + * @param int $comment_id The comment ID. + */ + do_action( 'unspammed_comment', $comment_id ); + return true; + } + + return false; +} + +/** + * The status of a comment by ID. + * + * @since 1.0.0 + * + * @param int $comment_id Comment ID + * @return false|string Status might be 'trash', 'approved', 'unapproved', 'spam'. False on failure. + */ +function wp_get_comment_status($comment_id) { + $comment = get_comment($comment_id); + if ( !$comment ) + return false; + + $approved = $comment->comment_approved; + + if ( $approved == null ) + return false; + elseif ( $approved == '1' ) + return 'approved'; + elseif ( $approved == '0' ) + return 'unapproved'; + elseif ( $approved == 'spam' ) + return 'spam'; + elseif ( $approved == 'trash' ) + return 'trash'; + else + return false; +} + +/** + * Call hooks for when a comment status transition occurs. + * + * Calls hooks for comment status transitions. If the new comment status is not the same + * as the previous comment status, then two hooks will be ran, the first is + * 'transition_comment_status' with new status, old status, and comment data. The + * next action called is 'comment_OLDSTATUS_to_NEWSTATUS' the NEWSTATUS is the + * $new_status parameter and the OLDSTATUS is $old_status parameter; it has the + * comment data. + * + * The final action will run whether or not the comment statuses are the same. The + * action is named 'comment_NEWSTATUS_COMMENTTYPE', NEWSTATUS is from the $new_status + * parameter and COMMENTTYPE is comment_type comment data. + * + * @since 2.7.0 + * + * @param string $new_status New comment status. + * @param string $old_status Previous comment status. + * @param object $comment Comment data. + */ +function wp_transition_comment_status($new_status, $old_status, $comment) { + /* + * Translate raw statuses to human readable formats for the hooks. + * This is not a complete list of comment status, it's only the ones + * that need to be renamed + */ + $comment_statuses = array( + 0 => 'unapproved', + 'hold' => 'unapproved', // wp_set_comment_status() uses "hold" + 1 => 'approved', + 'approve' => 'approved', // wp_set_comment_status() uses "approve" + ); + if ( isset($comment_statuses[$new_status]) ) $new_status = $comment_statuses[$new_status]; + if ( isset($comment_statuses[$old_status]) ) $old_status = $comment_statuses[$old_status]; + + // Call the hooks + if ( $new_status != $old_status ) { + /** + * Fires when the comment status is in transition. + * + * @since 2.7.0 + * + * @param int|string $new_status The new comment status. + * @param int|string $old_status The old comment status. + * @param object $comment The comment data. + */ + do_action( 'transition_comment_status', $new_status, $old_status, $comment ); + /** + * Fires when the comment status is in transition from one specific status to another. + * + * The dynamic portions of the hook name, `$old_status`, and `$new_status`, + * refer to the old and new comment statuses, respectively. + * + * @since 2.7.0 + * + * @param object $comment Comment object. + */ + do_action( "comment_{$old_status}_to_{$new_status}", $comment ); + } + /** + * Fires when the status of a specific comment type is in transition. + * + * The dynamic portions of the hook name, `$new_status`, and `$comment->comment_type`, + * refer to the new comment status, and the type of comment, respectively. + * + * Typical comment types include an empty string (standard comment), 'pingback', + * or 'trackback'. + * + * @since 2.7.0 + * + * @param int $comment_ID The comment ID. + * @param obj $comment Comment object. + */ + do_action( "comment_{$new_status}_{$comment->comment_type}", $comment->comment_ID, $comment ); +} + +/** + * Get current commenter's name, email, and URL. + * + * Expects cookies content to already be sanitized. User of this function might + * wish to recheck the returned array for validity. + * + * @see sanitize_comment_cookies() Use to sanitize cookies + * + * @since 2.0.4 + * + * @return array Comment author, email, url respectively. + */ +function wp_get_current_commenter() { + // Cookies should already be sanitized. + + $comment_author = ''; + if ( isset($_COOKIE['comment_author_'.COOKIEHASH]) ) + $comment_author = $_COOKIE['comment_author_'.COOKIEHASH]; + + $comment_author_email = ''; + if ( isset($_COOKIE['comment_author_email_'.COOKIEHASH]) ) + $comment_author_email = $_COOKIE['comment_author_email_'.COOKIEHASH]; + + $comment_author_url = ''; + if ( isset($_COOKIE['comment_author_url_'.COOKIEHASH]) ) + $comment_author_url = $_COOKIE['comment_author_url_'.COOKIEHASH]; + + /** + * Filter the current commenter's name, email, and URL. + * + * @since 3.1.0 + * + * @param string $comment_author Comment author's name. + * @param string $comment_author_email Comment author's email. + * @param string $comment_author_url Comment author's URL. + */ + return apply_filters( 'wp_get_current_commenter', compact('comment_author', 'comment_author_email', 'comment_author_url') ); +} + +/** + * Inserts a comment to the database. + * + * The available comment data key names are 'comment_author_IP', 'comment_date', + * 'comment_date_gmt', 'comment_parent', 'comment_approved', and 'user_id'. + * + * @since 2.0.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @param array $commentdata Contains information on the comment. + * @return int|bool The new comment's ID on success, false on failure. + */ +function wp_insert_comment( $commentdata ) { + global $wpdb; + $data = wp_unslash( $commentdata ); + + $comment_author = ! isset( $data['comment_author'] ) ? '' : $data['comment_author']; + $comment_author_email = ! isset( $data['comment_author_email'] ) ? '' : $data['comment_author_email']; + $comment_author_url = ! isset( $data['comment_author_url'] ) ? '' : $data['comment_author_url']; + $comment_author_IP = ! isset( $data['comment_author_IP'] ) ? '' : $data['comment_author_IP']; + + $comment_date = ! isset( $data['comment_date'] ) ? current_time( 'mysql' ) : $data['comment_date']; + $comment_date_gmt = ! isset( $data['comment_date_gmt'] ) ? get_gmt_from_date( $comment_date ) : $data['comment_date_gmt']; + + $comment_post_ID = ! isset( $data['comment_post_ID'] ) ? '' : $data['comment_post_ID']; + $comment_content = ! isset( $data['comment_content'] ) ? '' : $data['comment_content']; + $comment_karma = ! isset( $data['comment_karma'] ) ? 0 : $data['comment_karma']; + $comment_approved = ! isset( $data['comment_approved'] ) ? 1 : $data['comment_approved']; + $comment_agent = ! isset( $data['comment_agent'] ) ? '' : $data['comment_agent']; + $comment_type = ! isset( $data['comment_type'] ) ? '' : $data['comment_type']; + $comment_parent = ! isset( $data['comment_parent'] ) ? 0 : $data['comment_parent']; + + $user_id = ! isset( $data['user_id'] ) ? 0 : $data['user_id']; + + $compacted = compact( 'comment_post_ID', 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_author_IP', 'comment_date', 'comment_date_gmt', 'comment_content', 'comment_karma', 'comment_approved', 'comment_agent', 'comment_type', 'comment_parent', 'user_id' ); + if ( ! $wpdb->insert( $wpdb->comments, $compacted ) ) { + return false; + } + + $id = (int) $wpdb->insert_id; + + if ( $comment_approved == 1 ) { + wp_update_comment_count( $comment_post_ID ); + } + $comment = get_comment( $id ); + + /** + * Fires immediately after a comment is inserted into the database. + * + * @since 2.8.0 + * + * @param int $id The comment ID. + * @param obj $comment Comment object. + */ + do_action( 'wp_insert_comment', $id, $comment ); + + wp_cache_set( 'last_changed', microtime(), 'comment' ); + + return $id; +} + +/** + * Filters and sanitizes comment data. + * + * Sets the comment data 'filtered' field to true when finished. This can be + * checked as to whether the comment should be filtered and to keep from + * filtering the same comment more than once. + * + * @since 2.0.0 + * + * @param array $commentdata Contains information on the comment. + * @return array Parsed comment information. + */ +function wp_filter_comment($commentdata) { + if ( isset( $commentdata['user_ID'] ) ) { + /** + * Filter the comment author's user id before it is set. + * + * The first time this filter is evaluated, 'user_ID' is checked + * (for back-compat), followed by the standard 'user_id' value. + * + * @since 1.5.0 + * + * @param int $user_ID The comment author's user ID. + */ + $commentdata['user_id'] = apply_filters( 'pre_user_id', $commentdata['user_ID'] ); + } elseif ( isset( $commentdata['user_id'] ) ) { + /** This filter is documented in wp-includes/comment.php */ + $commentdata['user_id'] = apply_filters( 'pre_user_id', $commentdata['user_id'] ); + } + + /** + * Filter the comment author's browser user agent before it is set. + * + * @since 1.5.0 + * + * @param int $comment_agent The comment author's browser user agent. + */ + $commentdata['comment_agent'] = apply_filters( 'pre_comment_user_agent', ( isset( $commentdata['comment_agent'] ) ? $commentdata['comment_agent'] : '' ) ); + /** This filter is documented in wp-includes/comment.php */ + $commentdata['comment_author'] = apply_filters( 'pre_comment_author_name', $commentdata['comment_author'] ); + /** + * Filter the comment content before it is set. + * + * @since 1.5.0 + * + * @param int $comment_content The comment content. + */ + $commentdata['comment_content'] = apply_filters( 'pre_comment_content', $commentdata['comment_content'] ); + /** + * Filter the comment author's IP before it is set. + * + * @since 1.5.0 + * + * @param int $comment_author_ip The comment author's IP. + */ + $commentdata['comment_author_IP'] = apply_filters( 'pre_comment_user_ip', $commentdata['comment_author_IP'] ); + /** This filter is documented in wp-includes/comment.php */ + $commentdata['comment_author_url'] = apply_filters( 'pre_comment_author_url', $commentdata['comment_author_url'] ); + /** This filter is documented in wp-includes/comment.php */ + $commentdata['comment_author_email'] = apply_filters( 'pre_comment_author_email', $commentdata['comment_author_email'] ); + $commentdata['filtered'] = true; + return $commentdata; +} + +/** + * Whether a comment should be blocked because of comment flood. + * + * @since 2.1.0 + * + * @param bool $block Whether plugin has already blocked comment. + * @param int $time_lastcomment Timestamp for last comment. + * @param int $time_newcomment Timestamp for new comment. + * @return bool Whether comment should be blocked. + */ +function wp_throttle_comment_flood($block, $time_lastcomment, $time_newcomment) { + if ( $block ) // a plugin has already blocked... we'll let that decision stand + return $block; + if ( ($time_newcomment - $time_lastcomment) < 15 ) + return true; + return false; +} + +/** + * Adds a new comment to the database. + * + * Filters new comment to ensure that the fields are sanitized and valid before + * inserting comment into database. Calls 'comment_post' action with comment ID + * and whether comment is approved by WordPress. Also has 'preprocess_comment' + * filter for processing the comment data before the function handles it. + * + * We use REMOTE_ADDR here directly. If you are behind a proxy, you should ensure + * that it is properly set, such as in wp-config.php, for your environment. + * See {@link https://core.trac.wordpress.org/ticket/9235} + * + * @since 1.5.0 + * @param array $commentdata Contains information on the comment. + * @return int|bool The ID of the comment on success, false on failure. + */ +function wp_new_comment( $commentdata ) { + if ( isset( $commentdata['user_ID'] ) ) { + $commentdata['user_id'] = $commentdata['user_ID'] = (int) $commentdata['user_ID']; + } + + $prefiltered_user_id = ( isset( $commentdata['user_id'] ) ) ? (int) $commentdata['user_id'] : 0; + + /** + * Filter a comment's data before it is sanitized and inserted into the database. + * + * @since 1.5.0 + * + * @param array $commentdata Comment data. + */ + $commentdata = apply_filters( 'preprocess_comment', $commentdata ); + + $commentdata['comment_post_ID'] = (int) $commentdata['comment_post_ID']; + if ( isset( $commentdata['user_ID'] ) && $prefiltered_user_id !== (int) $commentdata['user_ID'] ) { + $commentdata['user_id'] = $commentdata['user_ID'] = (int) $commentdata['user_ID']; + } elseif ( isset( $commentdata['user_id'] ) ) { + $commentdata['user_id'] = (int) $commentdata['user_id']; + } + + $commentdata['comment_parent'] = isset($commentdata['comment_parent']) ? absint($commentdata['comment_parent']) : 0; + $parent_status = ( 0 < $commentdata['comment_parent'] ) ? wp_get_comment_status($commentdata['comment_parent']) : ''; + $commentdata['comment_parent'] = ( 'approved' == $parent_status || 'unapproved' == $parent_status ) ? $commentdata['comment_parent'] : 0; + + $commentdata['comment_author_IP'] = preg_replace( '/[^0-9a-fA-F:., ]/', '',$_SERVER['REMOTE_ADDR'] ); + $commentdata['comment_agent'] = isset( $_SERVER['HTTP_USER_AGENT'] ) ? substr( $_SERVER['HTTP_USER_AGENT'], 0, 254 ) : ''; + + $commentdata['comment_date'] = current_time('mysql'); + $commentdata['comment_date_gmt'] = current_time('mysql', 1); + + $commentdata = wp_filter_comment($commentdata); + + $commentdata['comment_approved'] = wp_allow_comment($commentdata); + + $comment_ID = wp_insert_comment($commentdata); + if ( ! $comment_ID ) { + return false; + } + + /** + * Fires immediately after a comment is inserted into the database. + * + * @since 1.2.0 + * + * @param int $comment_ID The comment ID. + * @param int $comment_approved 1 (true) if the comment is approved, 0 (false) if not. + */ + do_action( 'comment_post', $comment_ID, $commentdata['comment_approved'] ); + + if ( 'spam' !== $commentdata['comment_approved'] ) { // If it's spam save it silently for later crunching + if ( '0' == $commentdata['comment_approved'] ) { + wp_notify_moderator( $comment_ID ); + } + + // wp_notify_postauthor() checks if notifying the author of their own comment. + // By default, it won't, but filters can override this. + if ( get_option( 'comments_notify' ) && $commentdata['comment_approved'] ) { + wp_notify_postauthor( $comment_ID ); + } + } + + return $comment_ID; +} + +/** + * Sets the status of a comment. + * + * The 'wp_set_comment_status' action is called after the comment is handled. + * If the comment status is not in the list, then false is returned. + * + * @since 1.0.0 + * + * @param int $comment_id Comment ID. + * @param string $comment_status New comment status, either 'hold', 'approve', 'spam', or 'trash'. + * @param bool $wp_error Whether to return a WP_Error object if there is a failure. Default is false. + * @return bool|WP_Error True on success, false or WP_Error on failure. + */ +function wp_set_comment_status($comment_id, $comment_status, $wp_error = false) { + global $wpdb; + + switch ( $comment_status ) { + case 'hold': + case '0': + $status = '0'; + break; + case 'approve': + case '1': + $status = '1'; + if ( get_option('comments_notify') ) { + wp_notify_postauthor( $comment_id ); + } + break; + case 'spam': + $status = 'spam'; + break; + case 'trash': + $status = 'trash'; + break; + default: + return false; + } + + $comment_old = clone get_comment($comment_id); + + if ( !$wpdb->update( $wpdb->comments, array('comment_approved' => $status), array('comment_ID' => $comment_id) ) ) { + if ( $wp_error ) + return new WP_Error('db_update_error', __('Could not update comment status'), $wpdb->last_error); + else + return false; + } + + clean_comment_cache($comment_id); + + $comment = get_comment($comment_id); + + /** + * Fires immediately before transitioning a comment's status from one to another + * in the database. + * + * @since 1.5.0 + * + * @param int $comment_id Comment ID. + * @param string|bool $comment_status Current comment status. Possible values include + * 'hold', 'approve', 'spam', 'trash', or false. + */ + do_action( 'wp_set_comment_status', $comment_id, $comment_status ); + + wp_transition_comment_status($comment_status, $comment_old->comment_approved, $comment); + + wp_update_comment_count($comment->comment_post_ID); + + return true; +} + +/** + * Updates an existing comment in the database. + * + * Filters the comment and makes sure certain fields are valid before updating. + * + * @since 2.0.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @param array $commentarr Contains information on the comment. + * @return int Comment was updated if value is 1, or was not updated if value is 0. + */ +function wp_update_comment($commentarr) { + global $wpdb; + + // First, get all of the original fields + $comment = get_comment($commentarr['comment_ID'], ARRAY_A); + if ( empty( $comment ) ) { + return 0; + } + // Escape data pulled from DB. + $comment = wp_slash($comment); + + $old_status = $comment['comment_approved']; + + // Merge old and new fields with new fields overwriting old ones. + $commentarr = array_merge($comment, $commentarr); + + $commentarr = wp_filter_comment( $commentarr ); + + // Now extract the merged array. + $data = wp_unslash( $commentarr ); + + /** + * Filter the comment content before it is updated in the database. + * + * @since 1.5.0 + * + * @param string $comment_content The comment data. + */ + $data['comment_content'] = apply_filters( 'comment_save_pre', $data['comment_content'] ); + + $data['comment_date_gmt'] = get_gmt_from_date( $data['comment_date'] ); + + if ( ! isset( $data['comment_approved'] ) ) { + $data['comment_approved'] = 1; + } else if ( 'hold' == $data['comment_approved'] ) { + $data['comment_approved'] = 0; + } else if ( 'approve' == $data['comment_approved'] ) { + $data['comment_approved'] = 1; + } + + $comment_ID = $data['comment_ID']; + $comment_post_ID = $data['comment_post_ID']; + $keys = array( 'comment_content', 'comment_author', 'comment_author_email', 'comment_approved', 'comment_karma', 'comment_author_url', 'comment_date', 'comment_date_gmt', 'comment_parent' ); + $data = wp_array_slice_assoc( $data, $keys ); + $rval = $wpdb->update( $wpdb->comments, $data, compact( 'comment_ID' ) ); + + clean_comment_cache( $comment_ID ); + wp_update_comment_count( $comment_post_ID ); + /** + * Fires immediately after a comment is updated in the database. + * + * The hook also fires immediately before comment status transition hooks are fired. + * + * @since 1.2.0 + * + * @param int $comment_ID The comment ID. + */ + do_action( 'edit_comment', $comment_ID ); + $comment = get_comment($comment_ID); + wp_transition_comment_status($comment->comment_approved, $old_status, $comment); + return $rval; +} + +/** + * Whether to defer comment counting. + * + * When setting $defer to true, all post comment counts will not be updated + * until $defer is set to false. When $defer is set to false, then all + * previously deferred updated post comment counts will then be automatically + * updated without having to call wp_update_comment_count() after. + * + * @since 2.5.0 + * @staticvar bool $_defer + * + * @param bool $defer + * @return bool + */ +function wp_defer_comment_counting($defer=null) { + static $_defer = false; + + if ( is_bool($defer) ) { + $_defer = $defer; + // flush any deferred counts + if ( !$defer ) + wp_update_comment_count( null, true ); + } + + return $_defer; +} + +/** + * Updates the comment count for post(s). + * + * When $do_deferred is false (is by default) and the comments have been set to + * be deferred, the post_id will be added to a queue, which will be updated at a + * later date and only updated once per post ID. + * + * If the comments have not be set up to be deferred, then the post will be + * updated. When $do_deferred is set to true, then all previous deferred post + * IDs will be updated along with the current $post_id. + * + * @since 2.1.0 + * @see wp_update_comment_count_now() For what could cause a false return value + * + * @param int $post_id Post ID + * @param bool $do_deferred Whether to process previously deferred post comment counts + * @return bool|null True on success, false on failure + */ +function wp_update_comment_count($post_id, $do_deferred=false) { + static $_deferred = array(); + + if ( $do_deferred ) { + $_deferred = array_unique($_deferred); + foreach ( $_deferred as $i => $_post_id ) { + wp_update_comment_count_now($_post_id); + unset( $_deferred[$i] ); /** @todo Move this outside of the foreach and reset $_deferred to an array instead */ + } + } + + if ( wp_defer_comment_counting() ) { + $_deferred[] = $post_id; + return true; + } + elseif ( $post_id ) { + return wp_update_comment_count_now($post_id); + } + +} + +/** + * Updates the comment count for the post. + * + * @since 2.5.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @param int $post_id Post ID + * @return bool True on success, false on '0' $post_id or if post with ID does not exist. + */ +function wp_update_comment_count_now($post_id) { + global $wpdb; + $post_id = (int) $post_id; + if ( !$post_id ) + return false; + if ( !$post = get_post($post_id) ) + return false; + + $old = (int) $post->comment_count; + $new = (int) $wpdb->get_var( $wpdb->prepare("SELECT COUNT(*) FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_approved = '1'", $post_id) ); + $wpdb->update( $wpdb->posts, array('comment_count' => $new), array('ID' => $post_id) ); + + clean_post_cache( $post ); + + /** + * Fires immediately after a post's comment count is updated in the database. + * + * @since 2.3.0 + * + * @param int $post_id Post ID. + * @param int $new The new comment count. + * @param int $old The old comment count. + */ + do_action( 'wp_update_comment_count', $post_id, $new, $old ); + /** This action is documented in wp-includes/post.php */ + do_action( 'edit_post', $post_id, $post ); + + return true; +} + +// +// Ping and trackback functions. +// + +/** + * Finds a pingback server URI based on the given URL. + * + * Checks the HTML for the rel="pingback" link and x-pingback headers. It does + * a check for the x-pingback headers first and returns that, if available. The + * check for the rel="pingback" has more overhead than just the header. + * + * @since 1.5.0 + * + * @param string $url URL to ping. + * @param int $deprecated Not Used. + * @return false|string False on failure, string containing URI on success. + */ +function discover_pingback_server_uri( $url, $deprecated = '' ) { + if ( !empty( $deprecated ) ) + _deprecated_argument( __FUNCTION__, '2.7' ); + + $pingback_str_dquote = 'rel="pingback"'; + $pingback_str_squote = 'rel=\'pingback\''; + + /** @todo Should use Filter Extension or custom preg_match instead. */ + $parsed_url = parse_url($url); + + if ( ! isset( $parsed_url['host'] ) ) // Not an URL. This should never happen. + return false; + + //Do not search for a pingback server on our own uploads + $uploads_dir = wp_upload_dir(); + if ( 0 === strpos($url, $uploads_dir['baseurl']) ) + return false; + + $response = wp_safe_remote_head( $url, array( 'timeout' => 2, 'httpversion' => '1.0' ) ); + + if ( is_wp_error( $response ) ) + return false; + + if ( wp_remote_retrieve_header( $response, 'x-pingback' ) ) + return wp_remote_retrieve_header( $response, 'x-pingback' ); + + // Not an (x)html, sgml, or xml page, no use going further. + if ( preg_match('#(image|audio|video|model)/#is', wp_remote_retrieve_header( $response, 'content-type' )) ) + return false; + + // Now do a GET since we're going to look in the html headers (and we're sure it's not a binary file) + $response = wp_safe_remote_get( $url, array( 'timeout' => 2, 'httpversion' => '1.0' ) ); + + if ( is_wp_error( $response ) ) + return false; + + $contents = wp_remote_retrieve_body( $response ); + + $pingback_link_offset_dquote = strpos($contents, $pingback_str_dquote); + $pingback_link_offset_squote = strpos($contents, $pingback_str_squote); + if ( $pingback_link_offset_dquote || $pingback_link_offset_squote ) { + $quote = ($pingback_link_offset_dquote) ? '"' : '\''; + $pingback_link_offset = ($quote=='"') ? $pingback_link_offset_dquote : $pingback_link_offset_squote; + $pingback_href_pos = @strpos($contents, 'href=', $pingback_link_offset); + $pingback_href_start = $pingback_href_pos+6; + $pingback_href_end = @strpos($contents, $quote, $pingback_href_start); + $pingback_server_url_len = $pingback_href_end - $pingback_href_start; + $pingback_server_url = substr($contents, $pingback_href_start, $pingback_server_url_len); + + // We may find rel="pingback" but an incomplete pingback URL + if ( $pingback_server_url_len > 0 ) { // We got it! + return $pingback_server_url; + } + } + + return false; +} + +/** + * Perform all pingbacks, enclosures, trackbacks, and send to pingback services. + * + * @since 2.1.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + */ +function do_all_pings() { + global $wpdb; + + // Do pingbacks + while ($ping = $wpdb->get_row("SELECT ID, post_content, meta_id FROM {$wpdb->posts}, {$wpdb->postmeta} WHERE {$wpdb->posts}.ID = {$wpdb->postmeta}.post_id AND {$wpdb->postmeta}.meta_key = '_pingme' LIMIT 1")) { + delete_metadata_by_mid( 'post', $ping->meta_id ); + pingback( $ping->post_content, $ping->ID ); + } + + // Do Enclosures + while ($enclosure = $wpdb->get_row("SELECT ID, post_content, meta_id FROM {$wpdb->posts}, {$wpdb->postmeta} WHERE {$wpdb->posts}.ID = {$wpdb->postmeta}.post_id AND {$wpdb->postmeta}.meta_key = '_encloseme' LIMIT 1")) { + delete_metadata_by_mid( 'post', $enclosure->meta_id ); + do_enclose( $enclosure->post_content, $enclosure->ID ); + } + + // Do Trackbacks + $trackbacks = $wpdb->get_col("SELECT ID FROM $wpdb->posts WHERE to_ping <> '' AND post_status = 'publish'"); + if ( is_array($trackbacks) ) + foreach ( $trackbacks as $trackback ) + do_trackbacks($trackback); + + //Do Update Services/Generic Pings + generic_ping(); +} + +/** + * Perform trackbacks. + * + * @since 1.5.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @param int $post_id Post ID to do trackbacks on. + */ +function do_trackbacks($post_id) { + global $wpdb; + + $post = get_post( $post_id ); + $to_ping = get_to_ping($post_id); + $pinged = get_pung($post_id); + if ( empty($to_ping) ) { + $wpdb->update($wpdb->posts, array('to_ping' => ''), array('ID' => $post_id) ); + return; + } + + if ( empty($post->post_excerpt) ) { + /** This filter is documented in wp-includes/post-template.php */ + $excerpt = apply_filters( 'the_content', $post->post_content, $post->ID ); + } else { + /** This filter is documented in wp-includes/post-template.php */ + $excerpt = apply_filters( 'the_excerpt', $post->post_excerpt ); + } + + $excerpt = str_replace(']]>', ']]>', $excerpt); + $excerpt = wp_html_excerpt($excerpt, 252, '…'); + + /** This filter is documented in wp-includes/post-template.php */ + $post_title = apply_filters( 'the_title', $post->post_title, $post->ID ); + $post_title = strip_tags($post_title); + + if ( $to_ping ) { + foreach ( (array) $to_ping as $tb_ping ) { + $tb_ping = trim($tb_ping); + if ( !in_array($tb_ping, $pinged) ) { + trackback($tb_ping, $post_title, $excerpt, $post_id); + $pinged[] = $tb_ping; + } else { + $wpdb->query( $wpdb->prepare("UPDATE $wpdb->posts SET to_ping = TRIM(REPLACE(to_ping, %s, '')) WHERE ID = %d", $tb_ping, $post_id) ); + } + } + } +} + +/** + * Sends pings to all of the ping site services. + * + * @since 1.2.0 + * + * @param int $post_id Post ID. + * @return int Same as Post ID from parameter + */ +function generic_ping( $post_id = 0 ) { + $services = get_option('ping_sites'); + + $services = explode("\n", $services); + foreach ( (array) $services as $service ) { + $service = trim($service); + if ( '' != $service ) + weblog_ping($service); + } + + return $post_id; +} + +/** + * Pings back the links found in a post. + * + * @since 0.71 + * @uses $wp_version + * + * @param string $content Post content to check for links. + * @param int $post_ID Post ID. + */ +function pingback($content, $post_ID) { + global $wp_version; + include_once(ABSPATH . WPINC . '/class-IXR.php'); + include_once(ABSPATH . WPINC . '/class-wp-http-ixr-client.php'); + + // original code by Mort (http://mort.mine.nu:8080) + $post_links = array(); + + $pung = get_pung($post_ID); + + // Step 1 + // Parsing the post, external links (if any) are stored in the $post_links array + $post_links_temp = wp_extract_urls( $content ); + + // Step 2. + // Walking thru the links array + // first we get rid of links pointing to sites, not to specific files + // Example: + // http://dummy-weblog.org + // http://dummy-weblog.org/ + // http://dummy-weblog.org/post.php + // We don't wanna ping first and second types, even if they have a valid + + foreach ( (array) $post_links_temp as $link_test ) : + if ( !in_array($link_test, $pung) && (url_to_postid($link_test) != $post_ID) // If we haven't pung it already and it isn't a link to itself + && !is_local_attachment($link_test) ) : // Also, let's never ping local attachments. + if ( $test = @parse_url($link_test) ) { + if ( isset($test['query']) ) + $post_links[] = $link_test; + elseif ( isset( $test['path'] ) && ( $test['path'] != '/' ) && ( $test['path'] != '' ) ) + $post_links[] = $link_test; + } + endif; + endforeach; + + $post_links = array_unique( $post_links ); + /** + * Fires just before pinging back links found in a post. + * + * @since 2.0.0 + * + * @param array &$post_links An array of post links to be checked, passed by reference. + * @param array &$pung Whether a link has already been pinged, passed by reference. + * @param int $post_ID The post ID. + */ + do_action_ref_array( 'pre_ping', array( &$post_links, &$pung, $post_ID ) ); + + foreach ( (array) $post_links as $pagelinkedto ) { + $pingback_server_url = discover_pingback_server_uri( $pagelinkedto ); + + if ( $pingback_server_url ) { + @ set_time_limit( 60 ); + // Now, the RPC call + $pagelinkedfrom = get_permalink($post_ID); + + // using a timeout of 3 seconds should be enough to cover slow servers + $client = new WP_HTTP_IXR_Client($pingback_server_url); + $client->timeout = 3; + /** + * Filter the user agent sent when pinging-back a URL. + * + * @since 2.9.0 + * + * @param string $concat_useragent The user agent concatenated with ' -- WordPress/' + * and the WordPress version. + * @param string $useragent The useragent. + * @param string $pingback_server_url The server URL being linked to. + * @param string $pagelinkedto URL of page linked to. + * @param string $pagelinkedfrom URL of page linked from. + */ + $client->useragent = apply_filters( 'pingback_useragent', $client->useragent . ' -- WordPress/' . $wp_version, $client->useragent, $pingback_server_url, $pagelinkedto, $pagelinkedfrom ); + // when set to true, this outputs debug messages by itself + $client->debug = false; + + if ( $client->query('pingback.ping', $pagelinkedfrom, $pagelinkedto) || ( isset($client->error->code) && 48 == $client->error->code ) ) // Already registered + add_ping( $post_ID, $pagelinkedto ); + } + } +} + +/** + * Check whether blog is public before returning sites. + * + * @since 2.1.0 + * + * @param mixed $sites Will return if blog is public, will not return if not public. + * @return mixed Empty string if blog is not public, returns $sites, if site is public. + */ +function privacy_ping_filter($sites) { + if ( '0' != get_option('blog_public') ) + return $sites; + else + return ''; +} + +/** + * Send a Trackback. + * + * Updates database when sending trackback to prevent duplicates. + * + * @since 0.71 + * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @param string $trackback_url URL to send trackbacks. + * @param string $title Title of post. + * @param string $excerpt Excerpt of post. + * @param int $ID Post ID. + * @return mixed Database query from update. + */ +function trackback($trackback_url, $title, $excerpt, $ID) { + global $wpdb; + + if ( empty($trackback_url) ) + return; + + $options = array(); + $options['timeout'] = 4; + $options['body'] = array( + 'title' => $title, + 'url' => get_permalink($ID), + 'blog_name' => get_option('blogname'), + 'excerpt' => $excerpt + ); + + $response = wp_safe_remote_post( $trackback_url, $options ); + + if ( is_wp_error( $response ) ) + return; + + $wpdb->query( $wpdb->prepare("UPDATE $wpdb->posts SET pinged = CONCAT(pinged, '\n', %s) WHERE ID = %d", $trackback_url, $ID) ); + return $wpdb->query( $wpdb->prepare("UPDATE $wpdb->posts SET to_ping = TRIM(REPLACE(to_ping, %s, '')) WHERE ID = %d", $trackback_url, $ID) ); +} + +/** + * Send a pingback. + * + * @since 1.2.0 + * @uses $wp_version + * + * @param string $server Host of blog to connect to. + * @param string $path Path to send the ping. + */ +function weblog_ping($server = '', $path = '') { + global $wp_version; + include_once(ABSPATH . WPINC . '/class-IXR.php'); + include_once(ABSPATH . WPINC . '/class-wp-http-ixr-client.php'); + + // using a timeout of 3 seconds should be enough to cover slow servers + $client = new WP_HTTP_IXR_Client($server, ((!strlen(trim($path)) || ('/' == $path)) ? false : $path)); + $client->timeout = 3; + $client->useragent .= ' -- WordPress/'.$wp_version; + + // when set to true, this outputs debug messages by itself + $client->debug = false; + $home = trailingslashit( home_url() ); + if ( !$client->query('weblogUpdates.extendedPing', get_option('blogname'), $home, get_bloginfo('rss2_url') ) ) // then try a normal ping + $client->query('weblogUpdates.ping', get_option('blogname'), $home); +} + +/** + * Default filter attached to pingback_ping_source_uri to validate the pingback's Source URI + * + * @since 3.5.1 + * @see wp_http_validate_url() + * + * @param string $source_uri + * @return string + */ +function pingback_ping_source_uri( $source_uri ) { + return (string) wp_http_validate_url( $source_uri ); +} + +/** + * Default filter attached to xmlrpc_pingback_error. + * + * Returns a generic pingback error code unless the error code is 48, + * which reports that the pingback is already registered. + * + * @since 3.5.1 + * @link http://www.hixie.ch/specs/pingback/pingback#TOC3 + * + * @param IXR_Error $ixr_error + * @return IXR_Error + */ +function xmlrpc_pingback_error( $ixr_error ) { + if ( $ixr_error->code === 48 ) + return $ixr_error; + return new IXR_Error( 0, '' ); +} + +// +// Cache +// + +/** + * Removes comment ID from the comment cache. + * + * @since 2.3.0 + * + * @param int|array $ids Comment ID or array of comment IDs to remove from cache + */ +function clean_comment_cache($ids) { + foreach ( (array) $ids as $id ) + wp_cache_delete($id, 'comment'); + + wp_cache_set( 'last_changed', microtime(), 'comment' ); +} + +/** + * Updates the comment cache of given comments. + * + * Will add the comments in $comments to the cache. If comment ID already exists + * in the comment cache then it will not be updated. The comment is added to the + * cache using the comment group with the key using the ID of the comments. + * + * @since 2.3.0 + * + * @param array $comments Array of comment row objects + */ +function update_comment_cache($comments) { + foreach ( (array) $comments as $comment ) + wp_cache_add($comment->comment_ID, $comment, 'comment'); +} + +// +// Internal +// + +/** + * Close comments on old posts on the fly, without any extra DB queries. Hooked to the_posts. + * + * @access private + * @since 2.7.0 + * + * @param object $posts Post data object. + * @param object $query Query object. + * @return object + */ +function _close_comments_for_old_posts( $posts, $query ) { + if ( empty( $posts ) || ! $query->is_singular() || ! get_option( 'close_comments_for_old_posts' ) ) + return $posts; + + /** + * Filter the list of post types to automatically close comments for. + * + * @since 3.2.0 + * + * @param array $post_types An array of registered post types. Default array with 'post'. + */ + $post_types = apply_filters( 'close_comments_for_post_types', array( 'post' ) ); + if ( ! in_array( $posts[0]->post_type, $post_types ) ) + return $posts; + + $days_old = (int) get_option( 'close_comments_days_old' ); + if ( ! $days_old ) + return $posts; + + if ( time() - strtotime( $posts[0]->post_date_gmt ) > ( $days_old * DAY_IN_SECONDS ) ) { + $posts[0]->comment_status = 'closed'; + $posts[0]->ping_status = 'closed'; + } + + return $posts; +} + +/** + * Close comments on an old post. Hooked to comments_open and pings_open. + * + * @access private + * @since 2.7.0 + * + * @param bool $open Comments open or closed + * @param int $post_id Post ID + * @return bool $open + */ +function _close_comments_for_old_post( $open, $post_id ) { + if ( ! $open ) + return $open; + + if ( !get_option('close_comments_for_old_posts') ) + return $open; + + $days_old = (int) get_option('close_comments_days_old'); + if ( !$days_old ) + return $open; + + $post = get_post($post_id); + + /** This filter is documented in wp-includes/comment.php */ + $post_types = apply_filters( 'close_comments_for_post_types', array( 'post' ) ); + if ( ! in_array( $post->post_type, $post_types ) ) + return $open; + + if ( time() - strtotime( $post->post_date_gmt ) > ( $days_old * DAY_IN_SECONDS ) ) + return false; + + return $open; +} diff --git a/wp-includes/compat.php b/wp-includes/compat.php new file mode 100644 index 0000000..ab40452 --- /dev/null +++ b/wp-includes/compat.php @@ -0,0 +1,131 @@ + 'H32', 'sha1' => 'H40'); + + if ( !isset($packs[$algo]) ) + return false; + + $pack = $packs[$algo]; + + if (strlen($key) > 64) + $key = pack($pack, $algo($key)); + + $key = str_pad($key, 64, chr(0)); + + $ipad = (substr($key, 0, 64) ^ str_repeat(chr(0x36), 64)); + $opad = (substr($key, 0, 64) ^ str_repeat(chr(0x5C), 64)); + + $hmac = $algo($opad . pack($pack, $algo($ipad . $data))); + + if ( $raw_output ) + return pack( $pack, $hmac ); + return $hmac; +} + +if ( !function_exists('json_encode') ) { + function json_encode( $string ) { + global $wp_json; + + if ( !is_a($wp_json, 'Services_JSON') ) { + require_once( ABSPATH . WPINC . '/class-json.php' ); + $wp_json = new Services_JSON(); + } + + return $wp_json->encodeUnsafe( $string ); + } +} + +if ( !function_exists('json_decode') ) { + function json_decode( $string, $assoc_array = false ) { + global $wp_json; + + if ( !is_a($wp_json, 'Services_JSON') ) { + require_once( ABSPATH . WPINC . '/class-json.php' ); + $wp_json = new Services_JSON(); + } + + $res = $wp_json->decode( $string ); + if ( $assoc_array ) + $res = _json_decode_object_helper( $res ); + return $res; + } + function _json_decode_object_helper($data) { + if ( is_object($data) ) + $data = get_object_vars($data); + return is_array($data) ? array_map(__FUNCTION__, $data) : $data; + } +} + +if ( ! function_exists( 'hash_equals' ) ) : +/** + * Compare two strings in constant time. + * + * This function was added in PHP 5.6. + * It can leak the length of a string. + * + * @since 3.9.2 + * + * @param string $a Expected string. + * @param string $b Actual string. + * @return bool Whether strings are equal. + */ +function hash_equals( $a, $b ) { + $a_length = strlen( $a ); + if ( $a_length !== strlen( $b ) ) { + return false; + } + $result = 0; + + // Do not attempt to "optimize" this. + for ( $i = 0; $i < $a_length; $i++ ) { + $result |= ord( $a[ $i ] ) ^ ord( $b[ $i ] ); + } + + return $result === 0; +} +endif; + +// JSON_PRETTY_PRINT was introduced in PHP 5.4 +// Defined here to prevent a notice when using it with wp_json_encode() +if ( ! defined( 'JSON_PRETTY_PRINT' ) ) { + define( 'JSON_PRETTY_PRINT', 128 ); +} diff --git a/wp-includes/cron.php b/wp-includes/cron.php new file mode 100644 index 0000000..6455acf --- /dev/null +++ b/wp-includes/cron.php @@ -0,0 +1,470 @@ + $hook, 'timestamp' => $timestamp, 'schedule' => false, 'args' => $args ); + /** + * Filter a single event before it is scheduled. + * + * @since 3.1.0 + * + * @param object $event An object containing an event's data. + */ + $event = apply_filters( 'schedule_event', $event ); + + // A plugin disallowed this event + if ( ! $event ) + return false; + + $key = md5(serialize($event->args)); + + $crons[$event->timestamp][$event->hook][$key] = array( 'schedule' => $event->schedule, 'args' => $event->args ); + uksort( $crons, "strnatcasecmp" ); + _set_cron_array( $crons ); +} + +/** + * Schedule a periodic event. + * + * Schedules a hook which will be executed by the WordPress actions core on a + * specific interval, specified by you. The action will trigger when someone + * visits your WordPress site, if the scheduled time has passed. + * + * Valid values for the recurrence are hourly, daily and twicedaily. These can + * be extended using the cron_schedules filter in wp_get_schedules(). + * + * Use wp_next_scheduled() to prevent duplicates + * + * @since 2.1.0 + * + * @param int $timestamp Timestamp for when to run the event. + * @param string $recurrence How often the event should recur. + * @param string $hook Action hook to execute when cron is run. + * @param array $args Optional. Arguments to pass to the hook's callback function. + * @return false|null False on failure, null when complete with scheduling event. + */ +function wp_schedule_event( $timestamp, $recurrence, $hook, $args = array()) { + $crons = _get_cron_array(); + $schedules = wp_get_schedules(); + + if ( !isset( $schedules[$recurrence] ) ) + return false; + + $event = (object) array( 'hook' => $hook, 'timestamp' => $timestamp, 'schedule' => $recurrence, 'args' => $args, 'interval' => $schedules[$recurrence]['interval'] ); + /** This filter is documented in wp-includes/cron.php */ + $event = apply_filters( 'schedule_event', $event ); + + // A plugin disallowed this event + if ( ! $event ) + return false; + + $key = md5(serialize($event->args)); + + $crons[$event->timestamp][$event->hook][$key] = array( 'schedule' => $event->schedule, 'args' => $event->args, 'interval' => $event->interval ); + uksort( $crons, "strnatcasecmp" ); + _set_cron_array( $crons ); +} + +/** + * Reschedule a recurring event. + * + * @since 2.1.0 + * + * @param int $timestamp Timestamp for when to run the event. + * @param string $recurrence How often the event should recur. + * @param string $hook Action hook to execute when cron is run. + * @param array $args Optional. Arguments to pass to the hook's callback function. + * @return false|null False on failure. Null when event is rescheduled. + */ +function wp_reschedule_event( $timestamp, $recurrence, $hook, $args = array() ) { + $crons = _get_cron_array(); + $schedules = wp_get_schedules(); + $key = md5( serialize( $args ) ); + $interval = 0; + + // First we try to get it from the schedule + if ( isset( $schedules[ $recurrence ] ) ) { + $interval = $schedules[ $recurrence ]['interval']; + } + // Now we try to get it from the saved interval in case the schedule disappears + if ( 0 == $interval ) { + $interval = $crons[ $timestamp ][ $hook ][ $key ]['interval']; + } + // Now we assume something is wrong and fail to schedule + if ( 0 == $interval ) { + return false; + } + + $now = time(); + + if ( $timestamp >= $now ) { + $timestamp = $now + $interval; + } else { + $timestamp = $now + ( $interval - ( ( $now - $timestamp ) % $interval ) ); + } + + wp_schedule_event( $timestamp, $recurrence, $hook, $args ); +} + +/** + * Unschedule a previously scheduled cron job. + * + * The $timestamp and $hook parameters are required, so that the event can be + * identified. + * + * @since 2.1.0 + * + * @param int $timestamp Timestamp for when to run the event. + * @param string $hook Action hook, the execution of which will be unscheduled. + * @param array $args Arguments to pass to the hook's callback function. + * Although not passed to a callback function, these arguments are used + * to uniquely identify the scheduled event, so they should be the same + * as those used when originally scheduling the event. + */ +function wp_unschedule_event( $timestamp, $hook, $args = array() ) { + $crons = _get_cron_array(); + $key = md5(serialize($args)); + unset( $crons[$timestamp][$hook][$key] ); + if ( empty($crons[$timestamp][$hook]) ) + unset( $crons[$timestamp][$hook] ); + if ( empty($crons[$timestamp]) ) + unset( $crons[$timestamp] ); + _set_cron_array( $crons ); +} + +/** + * Unschedule all cron jobs attached to a specific hook. + * + * @since 2.1.0 + * + * @param string $hook Action hook, the execution of which will be unscheduled. + * @param array $args Optional. Arguments that were to be pass to the hook's callback function. + */ +function wp_clear_scheduled_hook( $hook, $args = array() ) { + // Backward compatibility + // Previously this function took the arguments as discrete vars rather than an array like the rest of the API + if ( !is_array($args) ) { + _deprecated_argument( __FUNCTION__, '3.0', __('This argument has changed to an array to match the behavior of the other cron functions.') ); + $args = array_slice( func_get_args(), 1 ); + } + + // This logic duplicates wp_next_scheduled() + // It's required due to a scenario where wp_unschedule_event() fails due to update_option() failing, + // and, wp_next_scheduled() returns the same schedule in an infinite loop. + $crons = _get_cron_array(); + if ( empty( $crons ) ) + return; + + $key = md5( serialize( $args ) ); + foreach ( $crons as $timestamp => $cron ) { + if ( isset( $cron[ $hook ][ $key ] ) ) { + wp_unschedule_event( $timestamp, $hook, $args ); + } + } +} + +/** + * Retrieve the next timestamp for a cron event. + * + * @since 2.1.0 + * + * @param string $hook Action hook to execute when cron is run. + * @param array $args Optional. Arguments to pass to the hook's callback function. + * @return bool|int The UNIX timestamp of the next time the scheduled event will occur. + */ +function wp_next_scheduled( $hook, $args = array() ) { + $crons = _get_cron_array(); + $key = md5(serialize($args)); + if ( empty($crons) ) + return false; + foreach ( $crons as $timestamp => $cron ) { + if ( isset( $cron[$hook][$key] ) ) + return $timestamp; + } + return false; +} + +/** + * Send request to run cron through HTTP request that doesn't halt page loading. + * + * @since 2.1.0 + * + * @return null Cron could not be spawned, because it is not needed to run. + */ +function spawn_cron( $gmt_time = 0 ) { + + if ( ! $gmt_time ) + $gmt_time = microtime( true ); + + if ( defined('DOING_CRON') || isset($_GET['doing_wp_cron']) ) + return; + + /* + * multiple processes on multiple web servers can run this code concurrently + * try to make this as atomic as possible by setting doing_cron switch + */ + $lock = get_transient('doing_cron'); + + if ( $lock > $gmt_time + 10 * MINUTE_IN_SECONDS ) + $lock = 0; + + // don't run if another process is currently running it or more than once every 60 sec. + if ( $lock + WP_CRON_LOCK_TIMEOUT > $gmt_time ) + return; + + //sanity check + $crons = _get_cron_array(); + if ( !is_array($crons) ) + return; + + $keys = array_keys( $crons ); + if ( isset($keys[0]) && $keys[0] > $gmt_time ) + return; + + if ( defined( 'ALTERNATE_WP_CRON' ) && ALTERNATE_WP_CRON ) { + if ( ! empty( $_POST ) || defined( 'DOING_AJAX' ) || defined( 'XMLRPC_REQUEST' ) ) { + return; + } + + $doing_wp_cron = sprintf( '%.22F', $gmt_time ); + set_transient( 'doing_cron', $doing_wp_cron ); + + ob_start(); + wp_redirect( add_query_arg( 'doing_wp_cron', $doing_wp_cron, wp_unslash( $_SERVER['REQUEST_URI'] ) ) ); + echo ' '; + + // flush any buffers and send the headers + while ( @ob_end_flush() ); + flush(); + + WP_DEBUG ? include_once( ABSPATH . 'wp-cron.php' ) : @include_once( ABSPATH . 'wp-cron.php' ); + return; + } + + $doing_wp_cron = sprintf( '%.22F', $gmt_time ); + set_transient( 'doing_cron', $doing_wp_cron ); + + /** + * Filter the cron request arguments. + * + * @since 3.5.0 + * + * @param array $cron_request_array { + * An array of cron request URL arguments. + * + * @type string $url The cron request URL. + * @type int $key The 22 digit GMT microtime. + * @type array $args { + * An array of cron request arguments. + * + * @type int $timeout The request timeout in seconds. Default .01 seconds. + * @type bool $blocking Whether to set blocking for the request. Default false. + * @type bool $sslverify Whether SSL should be verified for the request. Default false. + * } + * } + */ + $cron_request = apply_filters( 'cron_request', array( + 'url' => add_query_arg( 'doing_wp_cron', $doing_wp_cron, site_url( 'wp-cron.php' ) ), + 'key' => $doing_wp_cron, + 'args' => array( + 'timeout' => 0.01, + 'blocking' => false, + /** This filter is documented in wp-includes/class-http.php */ + 'sslverify' => apply_filters( 'https_local_ssl_verify', false ) + ) + ) ); + + wp_remote_post( $cron_request['url'], $cron_request['args'] ); +} + +/** + * Run scheduled callbacks or spawn cron for all scheduled events. + * + * @since 2.1.0 + * + * @return null When doesn't need to run Cron. + */ +function wp_cron() { + + // Prevent infinite loops caused by lack of wp-cron.php + if ( strpos($_SERVER['REQUEST_URI'], '/wp-cron.php') !== false || ( defined('DISABLE_WP_CRON') && DISABLE_WP_CRON ) ) + return; + + if ( false === $crons = _get_cron_array() ) + return; + + $gmt_time = microtime( true ); + $keys = array_keys( $crons ); + if ( isset($keys[0]) && $keys[0] > $gmt_time ) + return; + + $schedules = wp_get_schedules(); + foreach ( $crons as $timestamp => $cronhooks ) { + if ( $timestamp > $gmt_time ) break; + foreach ( (array) $cronhooks as $hook => $args ) { + if ( isset($schedules[$hook]['callback']) && !call_user_func( $schedules[$hook]['callback'] ) ) + continue; + spawn_cron( $gmt_time ); + break 2; + } + } +} + +/** + * Retrieve supported and filtered Cron recurrences. + * + * The supported recurrences are 'hourly' and 'daily'. A plugin may add more by + * hooking into the 'cron_schedules' filter. The filter accepts an array of + * arrays. The outer array has a key that is the name of the schedule or for + * example 'weekly'. The value is an array with two keys, one is 'interval' and + * the other is 'display'. + * + * The 'interval' is a number in seconds of when the cron job should run. So for + * 'hourly', the time is 3600 or 60*60. For weekly, the value would be + * 60*60*24*7 or 604800. The value of 'interval' would then be 604800. + * + * The 'display' is the description. For the 'weekly' key, the 'display' would + * be `__( 'Once Weekly' )`. + * + * For your plugin, you will be passed an array. you can easily add your + * schedule by doing the following. + * + * // Filter parameter variable name is 'array'. + * $array['weekly'] = array( + * 'interval' => 604800, + * 'display' => __( 'Once Weekly' ) + * ); + * + * + * @since 2.1.0 + * + * @return array + */ +function wp_get_schedules() { + $schedules = array( + 'hourly' => array( 'interval' => HOUR_IN_SECONDS, 'display' => __( 'Once Hourly' ) ), + 'twicedaily' => array( 'interval' => 12 * HOUR_IN_SECONDS, 'display' => __( 'Twice Daily' ) ), + 'daily' => array( 'interval' => DAY_IN_SECONDS, 'display' => __( 'Once Daily' ) ), + ); + /** + * Filter the non-default cron schedules. + * + * @since 2.1.0 + * + * @param array $new_schedules An array of non-default cron schedules. Default empty. + */ + return array_merge( apply_filters( 'cron_schedules', array() ), $schedules ); +} + +/** + * Retrieve Cron schedule for hook with arguments. + * + * @since 2.1.0 + * + * @param string $hook Action hook to execute when cron is run. + * @param array $args Optional. Arguments to pass to the hook's callback function. + * @return string|bool False, if no schedule. Schedule on success. + */ +function wp_get_schedule($hook, $args = array()) { + $crons = _get_cron_array(); + $key = md5(serialize($args)); + if ( empty($crons) ) + return false; + foreach ( $crons as $timestamp => $cron ) { + if ( isset( $cron[$hook][$key] ) ) + return $cron[$hook][$key]['schedule']; + } + return false; +} + +// +// Private functions +// + +/** + * Retrieve cron info array option. + * + * @since 2.1.0 + * @access private + * + * @return array CRON info array. + */ +function _get_cron_array() { + $cron = get_option('cron'); + if ( ! is_array($cron) ) + return false; + + if ( !isset($cron['version']) ) + $cron = _upgrade_cron_array($cron); + + unset($cron['version']); + + return $cron; +} + +/** + * Updates the CRON option with the new CRON array. + * + * @since 2.1.0 + * @access private + * + * @param array $cron Cron info array from {@link _get_cron_array()}. + */ +function _set_cron_array($cron) { + $cron['version'] = 2; + update_option( 'cron', $cron ); +} + +/** + * Upgrade a Cron info array. + * + * This function upgrades the Cron info array to version 2. + * + * @since 2.1.0 + * @access private + * + * @param array $cron Cron info array from {@link _get_cron_array()}. + * @return array An upgraded Cron info array. + */ +function _upgrade_cron_array($cron) { + if ( isset($cron['version']) && 2 == $cron['version']) + return $cron; + + $new_cron = array(); + + foreach ( (array) $cron as $timestamp => $hooks) { + foreach ( (array) $hooks as $hook => $args ) { + $key = md5(serialize($args['args'])); + $new_cron[$timestamp][$hook][$key] = $args; + } + } + + $new_cron['version'] = 2; + update_option( 'cron', $new_cron ); + return $new_cron; +} diff --git a/wp-includes/css/admin-bar-rtl.css b/wp-includes/css/admin-bar-rtl.css new file mode 100644 index 0000000..db2f983 --- /dev/null +++ b/wp-includes/css/admin-bar-rtl.css @@ -0,0 +1,1098 @@ +#wpadminbar * { + height: auto; + width: auto; + margin: 0; + padding: 0; + position: static; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + font: normal 13px/32px "Open Sans", sans-serif; + -webkit-border-radius: 0; + border-radius: 0; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + -webkit-transition: none; + transition: none; + -webkit-font-smoothing: subpixel-antialiased; /* Prevent Safari from switching to standard antialiasing on hover */ +} + +.rtl #wpadminbar * { + font-family: Tahoma, sans-serif; +} + +html:lang(he-il) .rtl #wpadminbar * { + font-family: Arial, sans-serif; +} + +#wpadminbar a.ab-item, +#wpadminbar > #wp-toolbar span.ab-label, +#wpadminbar > #wp-toolbar span.noticon { + color: #eee; +} + +#wpadminbar #wp-admin-bar-site-name a.ab-item, +#wpadminbar #wp-admin-bar-my-sites a.ab-item { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +#wpadminbar ul li:before, +#wpadminbar ul li:after { + content: normal; +} + +#wpadminbar a, +#wpadminbar a:hover, +#wpadminbar a img, +#wpadminbar a img:hover { + outline: none; + border: none; + text-decoration: none; + background: none; +} + +#wpadminbar a:focus, +#wpadminbar a:active, +#wpadminbar input[type="text"], +#wpadminbar input[type="password"], +#wpadminbar input[type="number"], +#wpadminbar input[type="search"], +#wpadminbar input[type="email"], +#wpadminbar input[type="url"], +#wpadminbar select, +#wpadminbar textarea, +#wpadminbar div { + -webkit-box-shadow: none; + box-shadow: none; + outline: none; +} + +#wpadminbar { + direction: rtl; + color: #ccc; + font: normal 13px/32px "Open Sans", sans-serif; + height: 32px; + position: fixed; + top: 0; + right: 0; + width: 100%; + min-width: 600px; /* match the min-width of the body in wp-admin.css */ + z-index: 99999; + background: #222; +} + +#wpadminbar .ab-sub-wrapper, +#wpadminbar ul, +#wpadminbar ul li { + background: none; + clear: none; + list-style: none; + margin: 0; + padding: 0; + position: relative; + text-indent: 0; + z-index: 99999; +} + +#wpadminbar ul#wp-admin-bar-root-default>li { + margin-left: 0; +} + +#wpadminbar .quicklinks ul { + text-align: right; +} + +#wpadminbar li { + float: right; +} + +#wpadminbar .ab-empty-item { + outline: none; +} + +#wpadminbar .quicklinks .ab-top-secondary > li { + float: left; +} + +#wpadminbar .quicklinks a, +#wpadminbar .quicklinks .ab-empty-item, +#wpadminbar .shortlink-input { + height: 32px; + display: block; + padding: 0 10px; + margin: 0; +} + +#wpadminbar .quicklinks > ul > li > a { + padding: 0 7px 0 8px; +} + +#wpadminbar .menupop .ab-sub-wrapper, +#wpadminbar .shortlink-input { + margin: 0; + padding: 0; + -webkit-box-shadow: 0 3px 5px rgba(0,0,0,0.2); + box-shadow: 0 3px 5px rgba(0,0,0,0.2); + background: #333; + display: none; + position: absolute; + float: none; +} + +#wpadminbar.ie7 .menupop .ab-sub-wrapper, +#wpadminbar.ie7 .shortlink-input { + top: 32px; + right: 0; +} + +#wpadminbar .ab-top-menu > .menupop > .ab-sub-wrapper { + min-width: 100%; +} + +#wpadminbar .ab-top-secondary .menupop .ab-sub-wrapper { + left: 0; + right: auto; +} + +#wpadminbar .ab-submenu { + padding: 6px 0; +} + +#wpadminbar .selected .shortlink-input { + display: block; +} + +#wpadminbar .quicklinks .menupop ul li { + float: none; +} + +#wpadminbar .quicklinks .menupop ul li a strong { + font-weight: bold; +} + +#wpadminbar .quicklinks .menupop ul li .ab-item, +#wpadminbar .quicklinks .menupop ul li a strong, +#wpadminbar .quicklinks .menupop.hover ul li .ab-item, +#wpadminbar.nojs .quicklinks .menupop:hover ul li .ab-item, +#wpadminbar .shortlink-input { + line-height: 26px; + height: 26px; + white-space: nowrap; + min-width: 140px; +} + +#wpadminbar .shortlink-input { + width: 200px; +} + +#wpadminbar.nojs li:hover > .ab-sub-wrapper, +#wpadminbar li.hover > .ab-sub-wrapper { + display: block; +} + +#wpadminbar .menupop li:hover > .ab-sub-wrapper, +#wpadminbar .menupop li.hover > .ab-sub-wrapper { + margin-right: 100%; + margin-top: -32px; +} + +#wpadminbar .ab-top-secondary .menupop li:hover > .ab-sub-wrapper, +#wpadminbar .ab-top-secondary .menupop li.hover > .ab-sub-wrapper { + margin-right: 0; + right: inherit; + left: 100%; +} + +#wpadminbar .ab-top-menu > li > .ab-item:focus, +#wpadminbar.nojq .quicklinks .ab-top-menu > li > .ab-item:focus, +#wpadminbar .ab-top-menu > li:hover > .ab-item, +#wpadminbar .ab-top-menu > li.hover > .ab-item { + background: #333; + color: #45bbe6; +} + +#wpadminbar > #wp-toolbar li:hover span.ab-label, +#wpadminbar > #wp-toolbar li.hover span.ab-label, +#wpadminbar > #wp-toolbar a:focus span.ab-label { + color: #45bbe6; +} + +#wpadminbar > #wp-toolbar > #wp-admin-bar-root-default .ab-icon, +#wpadminbar .ab-icon, +#wpadminbar .ab-item:before { + position: relative; + float: right; + font: normal 20px/1 'dashicons'; + speak: none; + padding: 4px 0; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + background-image: none !important; + margin-left: 6px; +} + +#wpadminbar .ab-icon:before, +#wpadminbar .ab-item:before, +#wpadminbar #adminbarsearch:before { + color: #999; +} + +#wpadminbar .ab-icon:before, +#wpadminbar .ab-item:before, +#wpadminbar #adminbarsearch:before { + position: relative; + -webkit-transition: all .1s ease-in-out; + transition: all .1s ease-in-out; +} + +#wpadminbar .ab-label { + display: inline-block; + height: 32px; +} + +#wpadminbar .ab-submenu .ab-item { + color: #eee; +} + +#wpadminbar .quicklinks .menupop ul li a, +#wpadminbar .quicklinks .menupop ul li a strong, +#wpadminbar .quicklinks .menupop.hover ul li a, +#wpadminbar.nojs .quicklinks .menupop:hover ul li a { + color: #eee; +} + +#wpadminbar .quicklinks .menupop ul li a:hover, +#wpadminbar .quicklinks .menupop ul li a:focus, +#wpadminbar .quicklinks .menupop ul li a:hover strong, +#wpadminbar .quicklinks .menupop ul li a:focus strong, +#wpadminbar .quicklinks .menupop.hover ul li a:hover, +#wpadminbar .quicklinks .menupop.hover ul li a:focus, +#wpadminbar.nojs .quicklinks .menupop:hover ul li a:hover, +#wpadminbar.nojs .quicklinks .menupop:hover ul li a:focus, +#wpadminbar li:hover .ab-icon:before, +#wpadminbar li:hover .ab-item:before, +#wpadminbar li a:focus .ab-icon:before, +#wpadminbar li .ab-item:focus:before, +#wpadminbar li.hover .ab-icon:before, +#wpadminbar li.hover .ab-item:before, +#wpadminbar li:hover #adminbarsearch:before { + color: #45bbe6; +} + +#wpadminbar .menupop .menupop > .ab-item:before, +#wpadminbar .ab-top-secondary .menupop .menupop > .ab-item:before { + position: absolute; + font: normal 17px/1 'dashicons'; + speak: none; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +#wpadminbar .menupop .menupop > .ab-item { + display: block; + padding-left: 2em; +} + +#wpadminbar .menupop .menupop > .ab-item:before { + top: 1px; + left: 4px; + content: '\f139'; + color: inherit; +} + +#wpadminbar .ab-top-secondary .menupop .menupop > .ab-item { + padding-right: 2em; + padding-left: 1em; +} + +#wpadminbar .ab-top-secondary .menupop .menupop > .ab-item:before { + top: 5px; + right: 3px; + content: '\f141'; +} + +#wpadminbar .quicklinks .menupop ul.ab-sub-secondary { + display: block; + position: relative; + left: auto; + margin: 0; + -webkit-box-shadow: none; + box-shadow: none; +} + +#wpadminbar .quicklinks .menupop ul.ab-sub-secondary, +#wpadminbar .quicklinks .menupop ul.ab-sub-secondary .ab-submenu { + background: #4b4b4b; +} + +#wpadminbar .quicklinks .menupop .ab-sub-secondary > li > a:hover, +#wpadminbar .quicklinks .menupop .ab-sub-secondary > li .ab-item:focus a { + color: #45bbe6; +} + +#wpadminbar .quicklinks a span#ab-updates { + background: #eee; + color: #333; + display: inline; + padding: 2px 5px; + font-size: 10px; + font-weight: bold; + -webkit-border-radius: 10px; + border-radius: 10px; +} + +#wpadminbar .quicklinks a:hover span#ab-updates { + background: #fff; + color: #000; +} + +#wpadminbar .ab-top-secondary { + float: left; +} + +#wpadminbar ul li:last-child, +#wpadminbar ul li:last-child .ab-item { + -webkit-box-shadow: none; + box-shadow: none; +} + +/** + * My Account + */ +#wp-admin-bar-my-account > ul { + min-width: 198px; +} + +#wp-admin-bar-my-account > .ab-item:before { + content: "\f110"; + top: 2px; + float: left; + margin-right: 6px; + margin-left: 0; +} + +#wp-admin-bar-my-account.with-avatar > .ab-item:before { + display: none; + content: none; +} + +#wp-admin-bar-my-account.with-avatar > ul { + min-width: 270px; +} + +#wpadminbar #wp-admin-bar-user-actions > li { + margin-right: 16px; + margin-left: 16px; +} + +#wpadminbar #wp-admin-bar-user-actions.ab-submenu { + padding: 6px 0 12px; +} + +#wpadminbar #wp-admin-bar-my-account.with-avatar #wp-admin-bar-user-actions > li { + margin-right: 88px; +} + +#wpadminbar #wp-admin-bar-user-info { + margin-top: 6px; + margin-bottom: 15px; + height: auto; + background: none; +} + +#wp-admin-bar-user-info .avatar { + position: absolute; + right: -72px; + top: 4px; + width: 64px; + height: 64px; +} + +#wpadminbar #wp-admin-bar-user-info a { + background: none; + height: auto; +} + +#wpadminbar #wp-admin-bar-user-info span { + background: none; + padding: 0; + height: 18px; +} + +#wpadminbar #wp-admin-bar-user-info .display-name, +#wpadminbar #wp-admin-bar-user-info .username { + display: block; +} + +#wpadminbar #wp-admin-bar-user-info .username { + color: #999; + font-size: 11px; +} + +#wpadminbar .quicklinks li#wp-admin-bar-my-account.with-avatar > a img { + width: 16px; + height: 16px; + padding: 0; + border: 1px solid #888; + background: #eee; + line-height: 24px; + vertical-align: middle; + margin: -4px 6px 0 0; + float: none; + display: inline; +} + +/** + * WP Logo + */ +#wpadminbar #wp-admin-bar-wp-logo > .ab-item .ab-icon { + width: 15px; + height: 20px; + margin-left: 0; + padding: 6px 0 5px; +} + +#wpadminbar #wp-admin-bar-wp-logo > .ab-item { + padding: 0 7px; +} + +#wpadminbar #wp-admin-bar-wp-logo > .ab-item .ab-icon:before { + content: '\f120'; + top: 2px; +} + +/* + * My Sites & Site Title + */ +#wpadminbar .quicklinks li .blavatar { + float: right; + font: normal 16px/1 'dashicons' !important; + speak: none; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + color: #eee; +} + +#wpadminbar .quicklinks li a:hover .blavatar { + color: #45bbe6; +} + +#wpadminbar .quicklinks li .blavatar:before { + content: '\f120'; + height: 16px; + width: 16px; + display: inline-block; + margin: 6px -2px 0 8px; +} + +#wpadminbar #wp-admin-bar-appearance { + margin-top: -12px; +} + +#wpadminbar #wp-admin-bar-my-sites > .ab-item:before, +#wpadminbar #wp-admin-bar-site-name > .ab-item:before { + content: '\f112'; + top: 2px; +} + +#wpadminbar #wp-admin-bar-edit > .ab-item:before { + content: '\f464'; + top: 2px; +} + +#wpadminbar #wp-admin-bar-site-name > .ab-item:before { + content: "\f226"; +} + +.wp-admin #wpadminbar #wp-admin-bar-site-name > .ab-item:before { + content: "\f102"; +} + + + +/** + * Comments + */ +#wpadminbar #wp-admin-bar-comments .ab-icon { + margin-left: 6px; +} + +#wpadminbar #wp-admin-bar-comments .ab-icon:before { + content: '\f101'; + top: 3px; +} + +#wpadminbar #wp-admin-bar-comments .count-0 { + opacity: .5; +} + +/** + * New Content + */ +#wpadminbar #wp-admin-bar-new-content .ab-icon:before { + content: '\f132'; + top: 4px; +} + +/** + * Updates + */ +#wpadminbar #wp-admin-bar-updates .ab-icon:before { + content: '\f463'; + top: 2px; +} + +/** + * Search + */ +#wpadminbar #wp-admin-bar-search .ab-item { + padding: 0; + background: transparent; +} + +#wpadminbar #adminbarsearch { + position: relative; + height: 32px; + padding: 0 2px; +} + +#wpadminbar #adminbarsearch:before { + position: absolute; + top: 6px; + right: 5px; + z-index: 20; + font: normal 20px/1 'dashicons' !important; + content: '\f179'; + speak: none; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +#wpadminbar > #wp-toolbar > #wp-admin-bar-top-secondary > #wp-admin-bar-search #adminbarsearch input.adminbar-input { + position: relative; + z-index: 30; + font: 13px/24px "Open Sans", sans-serif; + height: 24px; + width: 24px; + padding: 0 24px 0 3px; + margin: 0; + color: #ccc; + background-color: rgba( 255, 255, 255, 0 ); + border: none; + outline: none; + cursor: pointer; + -webkit-box-shadow: none; + box-shadow: none; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + -webkit-transition-duration: 400ms; + transition-duration: 400ms; + -webkit-transition-property: width, background; + transition-property: width, background; + -webkit-transition-timing-function: ease; + transition-timing-function: ease; +} + +#wpadminbar > #wp-toolbar > #wp-admin-bar-top-secondary > #wp-admin-bar-search #adminbarsearch input.adminbar-input:focus { + z-index: 10; + color: #000; + width: 200px; + background-color: rgba( 255, 255, 255, 0.9 ); + cursor: text; + border: 0; +} + +#wpadminbar.ie7 > #wp-toolbar > #wp-admin-bar-top-secondary > #wp-admin-bar-search #adminbarsearch input.adminbar-input { + margin-top: 3px; + width: 120px; +} + +#wpadminbar.ie8 > #wp-toolbar > #wp-admin-bar-top-secondary > #wp-admin-bar-search #adminbarsearch input.adminbar-input { + margin-top: 4px; + background-color: #464646; +} + +#wpadminbar.ie8 > #wp-toolbar > #wp-admin-bar-top-secondary > #wp-admin-bar-search #adminbarsearch input.adminbar-input:focus { + background-color: #fff; +} + +#wpadminbar #adminbarsearch .adminbar-input::-webkit-input-placeholder { + color: #999; +} +#wpadminbar #adminbarsearch .adminbar-input:-moz-placeholder { + color: #999; +} +#wpadminbar #adminbarsearch .adminbar-input::-moz-placeholder { + color: #999; +} +#wpadminbar #adminbarsearch .adminbar-input:-ms-input-placeholder { + color: #999; +} + +#wpadminbar #adminbarsearch .adminbar-button { + display: none; +} + +/** + * Customize support classes + */ +.no-customize-support .hide-if-no-customize, +.customize-support .hide-if-customize, +.no-customize-support #wpadminbar .hide-if-no-customize, +.no-customize-support.wp-core-ui .hide-if-no-customize, +.no-customize-support .wp-core-ui .hide-if-no-customize, +.customize-support #wpadminbar .hide-if-customize, +.customize-support.wp-core-ui .hide-if-customize, +.customize-support .wp-core-ui .hide-if-customize { + display: none; +} + +/* Skip link */ +#wpadminbar .screen-reader-text, +#wpadminbar .screen-reader-text span { + position: absolute; + right: -1000em; + top: -1000em; + height: 1px; + width: 1px; + overflow: hidden; +} + +#wpadminbar .screen-reader-shortcut { + position: absolute; + top: -1000em; +} + +#wpadminbar .screen-reader-shortcut:focus { + right: 6px; + top: 7px; + height: auto; + width: auto; + display: block; + font-size: 14px; + font-weight: bold; + padding: 15px 23px 14px; + background: #f1f1f1; + color: #21759b; + z-index: 100000; + line-height: normal; + text-decoration: none; + -webkit-box-shadow: 0 0 2px 2px rgba(0,0,0,.6); + box-shadow: 0 0 2px 2px rgba(0,0,0,.6); +} + +/** + * IE 6-targeted rules + */ +* html #wpadminbar { + overflow: hidden; + position: absolute; +} + +* html #wpadminbar .quicklinks ul li a { + float: right; +} + +* html #wpadminbar .menupop a span { + background-image: none; +} + +/* No @font-face support */ +.no-font-face #wpadminbar ul.ab-top-menu > li > a.ab-item { + display: block; + width: 45px; + text-align: center; + overflow: hidden; + margin: 0 3px; +} + +.no-font-face #wpadminbar #wp-admin-bar-my-sites > .ab-item, +.no-font-face #wpadminbar #wp-admin-bar-site-name > .ab-item, +.no-font-face #wpadminbar #wp-admin-bar-edit > .ab-item { + text-indent: 0; +} + +.no-font-face #wpadminbar .ab-icon, +.no-font-face #wpadminbar .ab-icon:before, +.no-font-face #wpadminbar a.ab-item:before, +.no-font-face #wpadminbar #wp-admin-bar-wp-logo > .ab-item { + display: none !important; +} + +.no-font-face #wpadminbar ul.ab-top-menu > li > a > span.ab-label { + display: inline; +} + +.no-font-face #wpadminbar #wp-admin-bar-menu-toggle span.ab-icon { + display: inline !important; +} + +.no-font-face #wpadminbar #wp-admin-bar-menu-toggle span.ab-icon:before { + content: "Menu"; + font: 14px/45px sans-serif !important; + display: inline-block !important; + color: #fff; +} + +.no-font-face #wpadminbar #wp-admin-bar-site-name a.ab-item { + color: #fff; +} +/* End no @font-face */ + +@media screen and ( max-width: 782px ) { + /* Toolbar Touchification*/ + html #wpadminbar { + height: 46px; + min-width: 300px; + + /* These rules break dropdown tappability on Chrome/Android. + -webkit-transform: translate3d(0, 0, 0); + -webkit-backface-visibility: hidden; + -webkit-transition: 0; + transform: translate3d(0, 0, 0); + backface-visibility: hidden; + transition: 0; + */ + } + + #wpadminbar * { + font: normal 14px/32px "Open Sans", sans-serif; + } + + #wpadminbar .quicklinks > ul > li > a, + #wpadminbar .quicklinks .ab-empty-item { + padding: 0; + height: 46px; + line-height: 46px; + width: auto; + } + + #wpadminbar .ab-icon { + font: 40px/1 dashicons !important; + margin: 0; + padding: 0; + width: 52px; + height: 46px; + text-align: center; + } + + #wpadminbar .ab-icon:before { + text-align: center; + } + + #wpadminbar .ab-submenu { + padding: 0; + } + + #wpadminbar #wp-admin-bar-site-name a.ab-item, + #wpadminbar #wp-admin-bar-my-sites a.ab-item, + #wpadminbar #wp-admin-bar-my-account a.ab-item { + text-overflow: clip; + } + + #wpadminbar .ab-label { + display: none; + } + + #wpadminbar .menupop li:hover > .ab-sub-wrapper, + #wpadminbar .menupop li.hover > .ab-sub-wrapper { + margin-top: -46px; + } + + #wpadminbar .ab-top-menu .menupop .ab-sub-wrapper .menupop > .ab-item { + padding-left: 30px; + } + + #wpadminbar .menupop .menupop > .ab-item:before { + top: 10px; + left: 6px; + } + + #wpadminbar .ab-top-menu > .menupop > .ab-sub-wrapper .ab-item { + font-size: 16px; + padding: 6px 15px 12px; + } + + #wpadminbar .ab-top-menu > .menupop > .ab-sub-wrapper a:empty { + display: none; + } + + /* WP logo */ + #wpadminbar #wp-admin-bar-wp-logo > .ab-item { + padding: 0; + } + + #wpadminbar #wp-admin-bar-wp-logo > .ab-item .ab-icon { + padding: 0; + width: 52px; + height: 46px; + text-align: center; + vertical-align: top; + } + + #wpadminbar #wp-admin-bar-wp-logo > .ab-item .ab-icon:before { + font: 28px/1 'dashicons' !important; + top: -3px; + } + + #wpadminbar .ab-icon, + #wpadminbar .ab-item:before { + padding: 0; + } + + /* My Sites and "Site Title" menu */ + #wpadminbar #wp-admin-bar-my-sites > .ab-item, + #wpadminbar #wp-admin-bar-site-name > .ab-item, + #wpadminbar #wp-admin-bar-edit > .ab-item, + #wpadminbar #wp-admin-bar-my-account > .ab-item { + text-indent: 100%; + white-space: nowrap; + overflow: hidden; + width: 52px; + padding: 0; + color: #999; + position: relative; + } + + #wpadminbar > #wp-toolbar > #wp-admin-bar-root-default .ab-icon, + #wpadminbar .ab-icon, + #wpadminbar .ab-item:before { + padding: 0; + margin-left: 0; + } + + #wpadminbar #wp-admin-bar-edit > .ab-item:before, + #wpadminbar #wp-admin-bar-my-sites > .ab-item:before, + #wpadminbar #wp-admin-bar-site-name > .ab-item:before, + #wpadminbar #wp-admin-bar-my-account > .ab-item:before { + display: block; + text-indent: 0; + font: normal 32px/1 'dashicons'; + speak: none; + top: 7px; + width: 52px; + text-align: center; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + } + + #wpadminbar #wp-admin-bar-appearance { + margin-top: 0; + } + + #wpadminbar .quicklinks li .blavatar:before { + display: none; + } + + /* Search */ + #wpadminbar #wp-admin-bar-search { + display: none; + } + + /* New Content */ + #wpadminbar #wp-admin-bar-new-content .ab-icon:before { + top: 0; + line-height: 53px; + height: 46px !important; + text-align: center; + width: 52px; + display: block; + } + + /* Updates */ + #wpadminbar #wp-admin-bar-updates { + text-align: center; + } + + #wpadminbar #wp-admin-bar-updates .ab-icon:before { + top: 3px; + } + + /* Comments */ + #wpadminbar #wp-admin-bar-comments .ab-icon { + margin: 0; + } + + #wpadminbar #wp-admin-bar-comments .ab-icon:before { + display: block; + font-size: 34px; + height: 46px; + line-height: 47px; + top: 0; + } + + /* My Account */ + #wpadminbar #wp-admin-bar-my-account > a { + position: relative; + white-space: nowrap; + text-indent: 150%; /* More than 100% indention is needed since this element has padding */ + width: 28px; + padding: 0 10px; + overflow: hidden; /* Prevent link text from forcing horizontal scrolling on mobile */ + } + + #wpadminbar .quicklinks li#wp-admin-bar-my-account.with-avatar > a img { + position: absolute; + top: 13px; + left: 10px; + width: 26px; + height: 26px; + } + + #wpadminbar #wp-admin-bar-user-actions.ab-submenu { + padding: 0; + } + + #wpadminbar #wp-admin-bar-user-actions.ab-submenu img.avatar { + display: none; + } + + #wpadminbar #wp-admin-bar-my-account.with-avatar #wp-admin-bar-user-actions > li { + margin: 0; + } + + #wpadminbar #wp-admin-bar-user-info .display-name { + height: auto; + font-size: 16px; + line-height: 24px; + color: #eee; + } + + #wpadminbar #wp-admin-bar-user-info a { + padding-top: 4px; + } + + #wpadminbar #wp-admin-bar-user-info .username { + line-height: 0.8 !important; + margin-bottom: -2px; + } + + /* Show only default top level items */ + #wp-toolbar > ul > li { + display: none; + } + + #wpadminbar li#wp-admin-bar-menu-toggle, + #wpadminbar li#wp-admin-bar-wp-logo, + #wpadminbar li#wp-admin-bar-my-sites, + #wpadminbar li#wp-admin-bar-updates, + #wpadminbar li#wp-admin-bar-site-name, + #wpadminbar li#wp-admin-bar-new-content, + #wpadminbar li#wp-admin-bar-edit, + #wpadminbar li#wp-admin-bar-comments, + #wpadminbar li#wp-admin-bar-new-content, + #wpadminbar li#wp-admin-bar-my-account { + display: block; + } + + /* Allow dropdown list items to appear normally */ + #wpadminbar li:hover ul li, + #wpadminbar li.hover ul li, + #wpadminbar li:hover ul li:hover ul li { + display: list-item; + } + + /* Override default min-width so dropdown lists aren't stretched + to 100% viewport width at responsive sizes. */ + #wpadminbar .ab-top-menu > .menupop > .ab-sub-wrapper { + min-width: -webkit-fit-content; + min-width: -moz-fit-content; + min-width: fit-content; + } + + #wpadminbar ul#wp-admin-bar-root-default > li { + margin-left: 0; + } + + /* Experimental fix for touch toolbar dropdown positioning */ + #wpadminbar .ab-top-menu, + #wpadminbar .ab-top-secondary, + #wpadminbar #wp-admin-bar-wp-logo, + #wpadminbar #wp-admin-bar-my-sites, + #wpadminbar #wp-admin-bar-site-name, + #wpadminbar #wp-admin-bar-updates, + #wpadminbar #wp-admin-bar-comments, + #wpadminbar #wp-admin-bar-new-content, + #wpadminbar #wp-admin-bar-edit, + #wpadminbar #wp-admin-bar-my-account { + position: static; + } + + #wpadminbar #wp-admin-bar-my-account { + float: left; + } + + .network-admin #wpadminbar ul#wp-admin-bar-top-secondary > li#wp-admin-bar-my-account { + margin-left: 0; + } + + /* Realign arrows on taller responsive submenus */ + + #wpadminbar .ab-top-secondary .menupop .menupop > .ab-item:before { + top: 10px; + right: 0; + } +} + +/* Smartphone */ +@media screen and (max-width: 600px) { + #wpadminbar { + position: absolute; + } + + #wp-responsive-overlay { + position: fixed; + top: 0; + right: 0; + width: 100%; + height: 100%; + z-index: 400; + } + + #wpadminbar .ab-top-menu > .menupop > .ab-sub-wrapper { + width: 100%; + right: 0; + } + + #wpadminbar .menupop .menupop > .ab-item:before { + display: none; + } + + #wpadminbar #wp-admin-bar-wp-logo.menupop .ab-sub-wrapper { + margin-right: 0; + } + + #wpadminbar .ab-top-menu > .menupop li > .ab-sub-wrapper { + margin: 0; + width: 100%; + top: auto; + right: auto; + position: relative; + } + + #wpadminbar .ab-top-menu > .menupop li > .ab-sub-wrapper .ab-item { + font-size: 16px; + padding: 6px 30px 19px 15px; + } + + #wpadminbar li:hover ul li ul li { + display: list-item; + } + + #wpadminbar li#wp-admin-bar-wp-logo, + #wpadminbar li#wp-admin-bar-updates { + display: none; + } + + /* Make submenus full-width at this size */ + + #wpadminbar .ab-top-menu > .menupop li > .ab-sub-wrapper { + position: static; + -webkit-box-shadow: none; + box-shadow: none; + } +} diff --git a/wp-includes/css/admin-bar-rtl.min.css b/wp-includes/css/admin-bar-rtl.min.css new file mode 100644 index 0000000..2d7250a --- /dev/null +++ b/wp-includes/css/admin-bar-rtl.min.css @@ -0,0 +1 @@ +#wpadminbar *{height:auto;width:auto;margin:0;padding:0;position:static;text-shadow:none;text-transform:none;letter-spacing:normal;font:400 13px/32px "Open Sans",sans-serif;-webkit-border-radius:0;border-radius:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-transition:none;transition:none;-webkit-font-smoothing:subpixel-antialiased}.rtl #wpadminbar *{font-family:Tahoma,sans-serif}html:lang(he-il) .rtl #wpadminbar *{font-family:Arial,sans-serif}#wpadminbar a.ab-item,#wpadminbar>#wp-toolbar span.ab-label,#wpadminbar>#wp-toolbar span.noticon{color:#eee}#wpadminbar #wp-admin-bar-my-sites a.ab-item,#wpadminbar #wp-admin-bar-site-name a.ab-item{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}#wpadminbar ul li:after,#wpadminbar ul li:before{content:normal}#wpadminbar a,#wpadminbar a img,#wpadminbar a img:hover,#wpadminbar a:hover{outline:0;border:none;text-decoration:none;background:0 0}#wpadminbar a:active,#wpadminbar a:focus,#wpadminbar div,#wpadminbar input[type=email],#wpadminbar input[type=number],#wpadminbar input[type=password],#wpadminbar input[type=search],#wpadminbar input[type=text],#wpadminbar input[type=url],#wpadminbar select,#wpadminbar textarea{-webkit-box-shadow:none;box-shadow:none;outline:0}#wpadminbar{direction:rtl;color:#ccc;font:400 13px/32px "Open Sans",sans-serif;height:32px;position:fixed;top:0;right:0;width:100%;min-width:600px;z-index:99999;background:#222}#wpadminbar .ab-sub-wrapper,#wpadminbar ul,#wpadminbar ul li{background:0 0;clear:none;list-style:none;margin:0;padding:0;position:relative;text-indent:0;z-index:99999}#wpadminbar ul#wp-admin-bar-root-default>li{margin-left:0}#wpadminbar .quicklinks ul{text-align:right}#wpadminbar li{float:right}#wpadminbar .ab-empty-item{outline:0}#wpadminbar .quicklinks .ab-top-secondary>li{float:left}#wpadminbar .quicklinks .ab-empty-item,#wpadminbar .quicklinks a,#wpadminbar .shortlink-input{height:32px;display:block;padding:0 10px;margin:0}#wpadminbar .quicklinks>ul>li>a{padding:0 7px 0 8px}#wpadminbar .menupop .ab-sub-wrapper,#wpadminbar .shortlink-input{margin:0;padding:0;-webkit-box-shadow:0 3px 5px rgba(0,0,0,.2);box-shadow:0 3px 5px rgba(0,0,0,.2);background:#333;display:none;position:absolute;float:none}#wpadminbar.ie7 .menupop .ab-sub-wrapper,#wpadminbar.ie7 .shortlink-input{top:32px;right:0}#wpadminbar .ab-top-menu>.menupop>.ab-sub-wrapper{min-width:100%}#wpadminbar .ab-top-secondary .menupop .ab-sub-wrapper{left:0;right:auto}#wpadminbar .ab-submenu{padding:6px 0}#wpadminbar .selected .shortlink-input{display:block}#wpadminbar .quicklinks .menupop ul li{float:none}#wpadminbar .quicklinks .menupop ul li a strong{font-weight:700}#wpadminbar .quicklinks .menupop ul li .ab-item,#wpadminbar .quicklinks .menupop ul li a strong,#wpadminbar .quicklinks .menupop.hover ul li .ab-item,#wpadminbar .shortlink-input,#wpadminbar.nojs .quicklinks .menupop:hover ul li .ab-item{line-height:26px;height:26px;white-space:nowrap;min-width:140px}#wpadminbar .shortlink-input{width:200px}#wpadminbar li.hover>.ab-sub-wrapper,#wpadminbar.nojs li:hover>.ab-sub-wrapper{display:block}#wpadminbar .menupop li.hover>.ab-sub-wrapper,#wpadminbar .menupop li:hover>.ab-sub-wrapper{margin-right:100%;margin-top:-32px}#wpadminbar .ab-top-secondary .menupop li.hover>.ab-sub-wrapper,#wpadminbar .ab-top-secondary .menupop li:hover>.ab-sub-wrapper{margin-right:0;right:inherit;left:100%}#wpadminbar .ab-top-menu>li.hover>.ab-item,#wpadminbar .ab-top-menu>li:hover>.ab-item,#wpadminbar .ab-top-menu>li>.ab-item:focus,#wpadminbar.nojq .quicklinks .ab-top-menu>li>.ab-item:focus{background:#333;color:#45bbe6}#wpadminbar>#wp-toolbar a:focus span.ab-label,#wpadminbar>#wp-toolbar li.hover span.ab-label,#wpadminbar>#wp-toolbar li:hover span.ab-label{color:#45bbe6}#wpadminbar .ab-icon,#wpadminbar .ab-item:before,#wpadminbar>#wp-toolbar>#wp-admin-bar-root-default .ab-icon{position:relative;float:right;font:400 20px/1 dashicons;speak:none;padding:4px 0;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;background-image:none!important;margin-left:6px}#wpadminbar #adminbarsearch:before,#wpadminbar .ab-icon:before,#wpadminbar .ab-item:before{color:#999;position:relative;-webkit-transition:all .1s ease-in-out;transition:all .1s ease-in-out}#wpadminbar .ab-label{display:inline-block;height:32px}#wpadminbar .ab-submenu .ab-item,#wpadminbar .quicklinks .menupop ul li a,#wpadminbar .quicklinks .menupop ul li a strong,#wpadminbar .quicklinks .menupop.hover ul li a,#wpadminbar.nojs .quicklinks .menupop:hover ul li a{color:#eee}#wpadminbar .quicklinks .menupop ul li a:focus,#wpadminbar .quicklinks .menupop ul li a:focus strong,#wpadminbar .quicklinks .menupop ul li a:hover,#wpadminbar .quicklinks .menupop ul li a:hover strong,#wpadminbar .quicklinks .menupop.hover ul li a:focus,#wpadminbar .quicklinks .menupop.hover ul li a:hover,#wpadminbar li .ab-item:focus:before,#wpadminbar li a:focus .ab-icon:before,#wpadminbar li.hover .ab-icon:before,#wpadminbar li.hover .ab-item:before,#wpadminbar li:hover #adminbarsearch:before,#wpadminbar li:hover .ab-icon:before,#wpadminbar li:hover .ab-item:before,#wpadminbar.nojs .quicklinks .menupop:hover ul li a:focus,#wpadminbar.nojs .quicklinks .menupop:hover ul li a:hover{color:#45bbe6}#wpadminbar .ab-top-secondary .menupop .menupop>.ab-item:before,#wpadminbar .menupop .menupop>.ab-item:before{position:absolute;font:400 17px/1 dashicons;speak:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}#wpadminbar .menupop .menupop>.ab-item{display:block;padding-left:2em}#wpadminbar .menupop .menupop>.ab-item:before{top:1px;left:4px;content:'\f139';color:inherit}#wpadminbar .ab-top-secondary .menupop .menupop>.ab-item{padding-right:2em;padding-left:1em}#wpadminbar .ab-top-secondary .menupop .menupop>.ab-item:before{top:5px;right:3px;content:'\f141'}#wpadminbar .quicklinks .menupop ul.ab-sub-secondary{display:block;position:relative;left:auto;margin:0;-webkit-box-shadow:none;box-shadow:none}#wpadminbar .quicklinks .menupop ul.ab-sub-secondary,#wpadminbar .quicklinks .menupop ul.ab-sub-secondary .ab-submenu{background:#4b4b4b}#wpadminbar .quicklinks .menupop .ab-sub-secondary>li .ab-item:focus a,#wpadminbar .quicklinks .menupop .ab-sub-secondary>li>a:hover{color:#45bbe6}#wpadminbar .quicklinks a span#ab-updates{background:#eee;color:#333;display:inline;padding:2px 5px;font-size:10px;font-weight:700;-webkit-border-radius:10px;border-radius:10px}#wpadminbar .quicklinks a:hover span#ab-updates{background:#fff;color:#000}#wpadminbar .ab-top-secondary{float:left}#wpadminbar ul li:last-child,#wpadminbar ul li:last-child .ab-item{-webkit-box-shadow:none;box-shadow:none}#wp-admin-bar-my-account>ul{min-width:198px}#wp-admin-bar-my-account>.ab-item:before{content:"\f110";top:2px;float:left;margin-right:6px;margin-left:0}#wp-admin-bar-my-account.with-avatar>.ab-item:before{display:none;content:none}#wp-admin-bar-my-account.with-avatar>ul{min-width:270px}#wpadminbar #wp-admin-bar-user-actions>li{margin-right:16px;margin-left:16px}#wpadminbar #wp-admin-bar-user-actions.ab-submenu{padding:6px 0 12px}#wpadminbar #wp-admin-bar-my-account.with-avatar #wp-admin-bar-user-actions>li{margin-right:88px}#wpadminbar #wp-admin-bar-user-info{margin-top:6px;margin-bottom:15px;height:auto;background:0 0}#wp-admin-bar-user-info .avatar{position:absolute;right:-72px;top:4px;width:64px;height:64px}#wpadminbar #wp-admin-bar-user-info a{background:0 0;height:auto}#wpadminbar #wp-admin-bar-user-info span{background:0 0;padding:0;height:18px}#wpadminbar #wp-admin-bar-user-info .display-name,#wpadminbar #wp-admin-bar-user-info .username{display:block}#wpadminbar #wp-admin-bar-user-info .username{color:#999;font-size:11px}#wpadminbar .quicklinks li#wp-admin-bar-my-account.with-avatar>a img{width:16px;height:16px;padding:0;border:1px solid #888;background:#eee;line-height:24px;vertical-align:middle;margin:-4px 6px 0 0;float:none;display:inline}#wpadminbar #wp-admin-bar-wp-logo>.ab-item .ab-icon{width:15px;height:20px;margin-left:0;padding:6px 0 5px}#wpadminbar #wp-admin-bar-wp-logo>.ab-item{padding:0 7px}#wpadminbar #wp-admin-bar-wp-logo>.ab-item .ab-icon:before{content:'\f120';top:2px}#wpadminbar .quicklinks li .blavatar{float:right;font:400 16px/1 dashicons!important;speak:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:#eee}#wpadminbar .quicklinks li a:hover .blavatar{color:#45bbe6}#wpadminbar .quicklinks li .blavatar:before{content:'\f120';height:16px;width:16px;display:inline-block;margin:6px -2px 0 8px}#wpadminbar #wp-admin-bar-appearance{margin-top:-12px}#wpadminbar #wp-admin-bar-my-sites>.ab-item:before,#wpadminbar #wp-admin-bar-site-name>.ab-item:before{content:'\f112';top:2px}#wpadminbar #wp-admin-bar-edit>.ab-item:before{content:'\f464';top:2px}#wpadminbar #wp-admin-bar-site-name>.ab-item:before{content:"\f226"}.wp-admin #wpadminbar #wp-admin-bar-site-name>.ab-item:before{content:"\f102"}#wpadminbar #wp-admin-bar-comments .ab-icon{margin-left:6px}#wpadminbar #wp-admin-bar-comments .ab-icon:before{content:'\f101';top:3px}#wpadminbar #wp-admin-bar-comments .count-0{opacity:.5}#wpadminbar #wp-admin-bar-new-content .ab-icon:before{content:'\f132';top:4px}#wpadminbar #wp-admin-bar-updates .ab-icon:before{content:'\f463';top:2px}#wpadminbar #wp-admin-bar-search .ab-item{padding:0;background:0 0}#wpadminbar #adminbarsearch{position:relative;height:32px;padding:0 2px}#wpadminbar #adminbarsearch:before{position:absolute;top:6px;right:5px;z-index:20;font:400 20px/1 dashicons!important;content:'\f179';speak:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}#wpadminbar>#wp-toolbar>#wp-admin-bar-top-secondary>#wp-admin-bar-search #adminbarsearch input.adminbar-input{position:relative;z-index:30;font:13px/24px "Open Sans",sans-serif;height:24px;width:24px;padding:0 24px 0 3px;margin:0;color:#ccc;background-color:rgba(255,255,255,0);border:none;outline:0;cursor:pointer;-webkit-box-shadow:none;box-shadow:none;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition-duration:400ms;transition-duration:400ms;-webkit-transition-property:width,background;transition-property:width,background;-webkit-transition-timing-function:ease;transition-timing-function:ease}#wpadminbar>#wp-toolbar>#wp-admin-bar-top-secondary>#wp-admin-bar-search #adminbarsearch input.adminbar-input:focus{z-index:10;color:#000;width:200px;background-color:rgba(255,255,255,.9);cursor:text;border:0}#wpadminbar.ie7>#wp-toolbar>#wp-admin-bar-top-secondary>#wp-admin-bar-search #adminbarsearch input.adminbar-input{margin-top:3px;width:120px}#wpadminbar.ie8>#wp-toolbar>#wp-admin-bar-top-secondary>#wp-admin-bar-search #adminbarsearch input.adminbar-input{margin-top:4px;background-color:#464646}#wpadminbar.ie8>#wp-toolbar>#wp-admin-bar-top-secondary>#wp-admin-bar-search #adminbarsearch input.adminbar-input:focus{background-color:#fff}#wpadminbar #adminbarsearch .adminbar-input::-webkit-input-placeholder{color:#999}#wpadminbar #adminbarsearch .adminbar-input:-moz-placeholder{color:#999}#wpadminbar #adminbarsearch .adminbar-input::-moz-placeholder{color:#999}#wpadminbar #adminbarsearch .adminbar-input:-ms-input-placeholder{color:#999}#wpadminbar #adminbarsearch .adminbar-button,.customize-support #wpadminbar .hide-if-customize,.customize-support .hide-if-customize,.customize-support .wp-core-ui .hide-if-customize,.customize-support.wp-core-ui .hide-if-customize,.no-customize-support #wpadminbar .hide-if-no-customize,.no-customize-support .hide-if-no-customize,.no-customize-support .wp-core-ui .hide-if-no-customize,.no-customize-support.wp-core-ui .hide-if-no-customize{display:none}#wpadminbar .screen-reader-text,#wpadminbar .screen-reader-text span{position:absolute;right:-1000em;top:-1000em;height:1px;width:1px;overflow:hidden}#wpadminbar .screen-reader-shortcut{position:absolute;top:-1000em}#wpadminbar .screen-reader-shortcut:focus{right:6px;top:7px;height:auto;width:auto;display:block;font-size:14px;font-weight:700;padding:15px 23px 14px;background:#f1f1f1;color:#21759b;z-index:100000;line-height:normal;text-decoration:none;-webkit-box-shadow:0 0 2px 2px rgba(0,0,0,.6);box-shadow:0 0 2px 2px rgba(0,0,0,.6)}* html #wpadminbar{overflow:hidden;position:absolute}* html #wpadminbar .quicklinks ul li a{float:right}* html #wpadminbar .menupop a span{background-image:none}.no-font-face #wpadminbar ul.ab-top-menu>li>a.ab-item{display:block;width:45px;text-align:center;overflow:hidden;margin:0 3px}.no-font-face #wpadminbar #wp-admin-bar-edit>.ab-item,.no-font-face #wpadminbar #wp-admin-bar-my-sites>.ab-item,.no-font-face #wpadminbar #wp-admin-bar-site-name>.ab-item{text-indent:0}.no-font-face #wpadminbar #wp-admin-bar-wp-logo>.ab-item,.no-font-face #wpadminbar .ab-icon,.no-font-face #wpadminbar .ab-icon:before,.no-font-face #wpadminbar a.ab-item:before{display:none!important}.no-font-face #wpadminbar ul.ab-top-menu>li>a>span.ab-label{display:inline}.no-font-face #wpadminbar #wp-admin-bar-menu-toggle span.ab-icon{display:inline!important}.no-font-face #wpadminbar #wp-admin-bar-menu-toggle span.ab-icon:before{content:"Menu";font:14px/45px sans-serif!important;display:inline-block!important;color:#fff}.no-font-face #wpadminbar #wp-admin-bar-site-name a.ab-item{color:#fff}@media screen and (max-width:782px){html #wpadminbar{height:46px;min-width:300px}#wpadminbar *{font:400 14px/32px "Open Sans",sans-serif}#wpadminbar .quicklinks .ab-empty-item,#wpadminbar .quicklinks>ul>li>a{padding:0;height:46px;line-height:46px;width:auto}#wpadminbar .ab-icon{font:40px/1 dashicons!important;margin:0;width:52px;height:46px;text-align:center}#wpadminbar .ab-icon:before{text-align:center}#wpadminbar .ab-submenu{padding:0}#wpadminbar #wp-admin-bar-my-account a.ab-item,#wpadminbar #wp-admin-bar-my-sites a.ab-item,#wpadminbar #wp-admin-bar-site-name a.ab-item{text-overflow:clip}#wpadminbar .ab-label{display:none}#wpadminbar .menupop li.hover>.ab-sub-wrapper,#wpadminbar .menupop li:hover>.ab-sub-wrapper{margin-top:-46px}#wpadminbar .ab-top-menu .menupop .ab-sub-wrapper .menupop>.ab-item{padding-left:30px}#wpadminbar .menupop .menupop>.ab-item:before{top:10px;left:6px}#wpadminbar .ab-top-menu>.menupop>.ab-sub-wrapper .ab-item{font-size:16px;padding:6px 15px 12px}#wpadminbar .ab-top-menu>.menupop>.ab-sub-wrapper a:empty{display:none}#wpadminbar #wp-admin-bar-wp-logo>.ab-item{padding:0}#wpadminbar #wp-admin-bar-wp-logo>.ab-item .ab-icon{padding:0;width:52px;height:46px;text-align:center;vertical-align:top}#wpadminbar #wp-admin-bar-wp-logo>.ab-item .ab-icon:before{font:28px/1 dashicons!important;top:-3px}#wpadminbar #wp-admin-bar-edit>.ab-item,#wpadminbar #wp-admin-bar-my-account>.ab-item,#wpadminbar #wp-admin-bar-my-sites>.ab-item,#wpadminbar #wp-admin-bar-site-name>.ab-item{text-indent:100%;white-space:nowrap;overflow:hidden;width:52px;padding:0;color:#999;position:relative}#wpadminbar .ab-icon,#wpadminbar .ab-item:before,#wpadminbar>#wp-toolbar>#wp-admin-bar-root-default .ab-icon{padding:0;margin-left:0}#wpadminbar #wp-admin-bar-edit>.ab-item:before,#wpadminbar #wp-admin-bar-my-account>.ab-item:before,#wpadminbar #wp-admin-bar-my-sites>.ab-item:before,#wpadminbar #wp-admin-bar-site-name>.ab-item:before{display:block;text-indent:0;font:400 32px/1 dashicons;speak:none;top:7px;width:52px;text-align:center;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}#wpadminbar #wp-admin-bar-appearance{margin-top:0}#wpadminbar #wp-admin-bar-search,#wpadminbar .quicklinks li .blavatar:before{display:none}#wpadminbar #wp-admin-bar-new-content .ab-icon:before{top:0;line-height:53px;height:46px!important;text-align:center;width:52px;display:block}#wpadminbar #wp-admin-bar-updates{text-align:center}#wpadminbar #wp-admin-bar-updates .ab-icon:before{top:3px}#wpadminbar #wp-admin-bar-comments .ab-icon{margin:0}#wpadminbar #wp-admin-bar-comments .ab-icon:before{display:block;font-size:34px;height:46px;line-height:47px;top:0}#wpadminbar #wp-admin-bar-my-account>a{position:relative;white-space:nowrap;text-indent:150%;width:28px;padding:0 10px;overflow:hidden}#wpadminbar .quicklinks li#wp-admin-bar-my-account.with-avatar>a img{position:absolute;top:13px;left:10px;width:26px;height:26px}#wpadminbar #wp-admin-bar-user-actions.ab-submenu{padding:0}#wpadminbar #wp-admin-bar-user-actions.ab-submenu img.avatar{display:none}#wpadminbar #wp-admin-bar-my-account.with-avatar #wp-admin-bar-user-actions>li{margin:0}#wpadminbar #wp-admin-bar-user-info .display-name{height:auto;font-size:16px;line-height:24px;color:#eee}#wpadminbar #wp-admin-bar-user-info a{padding-top:4px}#wpadminbar #wp-admin-bar-user-info .username{line-height:.8!important;margin-bottom:-2px}#wp-toolbar>ul>li{display:none}#wpadminbar li#wp-admin-bar-comments,#wpadminbar li#wp-admin-bar-edit,#wpadminbar li#wp-admin-bar-menu-toggle,#wpadminbar li#wp-admin-bar-my-account,#wpadminbar li#wp-admin-bar-my-sites,#wpadminbar li#wp-admin-bar-new-content,#wpadminbar li#wp-admin-bar-site-name,#wpadminbar li#wp-admin-bar-updates,#wpadminbar li#wp-admin-bar-wp-logo{display:block}#wpadminbar li.hover ul li,#wpadminbar li:hover ul li,#wpadminbar li:hover ul li:hover ul li{display:list-item}#wpadminbar .ab-top-menu>.menupop>.ab-sub-wrapper{min-width:-webkit-fit-content;min-width:-moz-fit-content;min-width:fit-content}#wpadminbar ul#wp-admin-bar-root-default>li{margin-left:0}#wpadminbar #wp-admin-bar-comments,#wpadminbar #wp-admin-bar-edit,#wpadminbar #wp-admin-bar-my-account,#wpadminbar #wp-admin-bar-my-sites,#wpadminbar #wp-admin-bar-new-content,#wpadminbar #wp-admin-bar-site-name,#wpadminbar #wp-admin-bar-updates,#wpadminbar #wp-admin-bar-wp-logo,#wpadminbar .ab-top-menu,#wpadminbar .ab-top-secondary{position:static}#wpadminbar #wp-admin-bar-my-account{float:left}.network-admin #wpadminbar ul#wp-admin-bar-top-secondary>li#wp-admin-bar-my-account{margin-left:0}#wpadminbar .ab-top-secondary .menupop .menupop>.ab-item:before{top:10px;right:0}}@media screen and (max-width:600px){#wpadminbar{position:absolute}#wp-responsive-overlay{position:fixed;top:0;right:0;width:100%;height:100%;z-index:400}#wpadminbar .ab-top-menu>.menupop>.ab-sub-wrapper{width:100%;right:0}#wpadminbar .menupop .menupop>.ab-item:before{display:none}#wpadminbar #wp-admin-bar-wp-logo.menupop .ab-sub-wrapper{margin-right:0}#wpadminbar .ab-top-menu>.menupop li>.ab-sub-wrapper{margin:0;width:100%;top:auto;right:auto}#wpadminbar .ab-top-menu>.menupop li>.ab-sub-wrapper .ab-item{font-size:16px;padding:6px 30px 19px 15px}#wpadminbar li:hover ul li ul li{display:list-item}#wpadminbar li#wp-admin-bar-updates,#wpadminbar li#wp-admin-bar-wp-logo{display:none}#wpadminbar .ab-top-menu>.menupop li>.ab-sub-wrapper{position:static;-webkit-box-shadow:none;box-shadow:none}} \ No newline at end of file diff --git a/wp-includes/css/admin-bar.css b/wp-includes/css/admin-bar.css new file mode 100644 index 0000000..f48f6a1 --- /dev/null +++ b/wp-includes/css/admin-bar.css @@ -0,0 +1,1098 @@ +#wpadminbar * { + height: auto; + width: auto; + margin: 0; + padding: 0; + position: static; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + font: normal 13px/32px "Open Sans", sans-serif; + -webkit-border-radius: 0; + border-radius: 0; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + -webkit-transition: none; + transition: none; + -webkit-font-smoothing: subpixel-antialiased; /* Prevent Safari from switching to standard antialiasing on hover */ +} + +.rtl #wpadminbar * { + font-family: Tahoma, sans-serif; +} + +html:lang(he-il) .rtl #wpadminbar * { + font-family: Arial, sans-serif; +} + +#wpadminbar a.ab-item, +#wpadminbar > #wp-toolbar span.ab-label, +#wpadminbar > #wp-toolbar span.noticon { + color: #eee; +} + +#wpadminbar #wp-admin-bar-site-name a.ab-item, +#wpadminbar #wp-admin-bar-my-sites a.ab-item { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +#wpadminbar ul li:before, +#wpadminbar ul li:after { + content: normal; +} + +#wpadminbar a, +#wpadminbar a:hover, +#wpadminbar a img, +#wpadminbar a img:hover { + outline: none; + border: none; + text-decoration: none; + background: none; +} + +#wpadminbar a:focus, +#wpadminbar a:active, +#wpadminbar input[type="text"], +#wpadminbar input[type="password"], +#wpadminbar input[type="number"], +#wpadminbar input[type="search"], +#wpadminbar input[type="email"], +#wpadminbar input[type="url"], +#wpadminbar select, +#wpadminbar textarea, +#wpadminbar div { + -webkit-box-shadow: none; + box-shadow: none; + outline: none; +} + +#wpadminbar { + direction: ltr; + color: #ccc; + font: normal 13px/32px "Open Sans", sans-serif; + height: 32px; + position: fixed; + top: 0; + left: 0; + width: 100%; + min-width: 600px; /* match the min-width of the body in wp-admin.css */ + z-index: 99999; + background: #222; +} + +#wpadminbar .ab-sub-wrapper, +#wpadminbar ul, +#wpadminbar ul li { + background: none; + clear: none; + list-style: none; + margin: 0; + padding: 0; + position: relative; + text-indent: 0; + z-index: 99999; +} + +#wpadminbar ul#wp-admin-bar-root-default>li { + margin-right: 0; +} + +#wpadminbar .quicklinks ul { + text-align: left; +} + +#wpadminbar li { + float: left; +} + +#wpadminbar .ab-empty-item { + outline: none; +} + +#wpadminbar .quicklinks .ab-top-secondary > li { + float: right; +} + +#wpadminbar .quicklinks a, +#wpadminbar .quicklinks .ab-empty-item, +#wpadminbar .shortlink-input { + height: 32px; + display: block; + padding: 0 10px; + margin: 0; +} + +#wpadminbar .quicklinks > ul > li > a { + padding: 0 8px 0 7px; +} + +#wpadminbar .menupop .ab-sub-wrapper, +#wpadminbar .shortlink-input { + margin: 0; + padding: 0; + -webkit-box-shadow: 0 3px 5px rgba(0,0,0,0.2); + box-shadow: 0 3px 5px rgba(0,0,0,0.2); + background: #333; + display: none; + position: absolute; + float: none; +} + +#wpadminbar.ie7 .menupop .ab-sub-wrapper, +#wpadminbar.ie7 .shortlink-input { + top: 32px; + left: 0; +} + +#wpadminbar .ab-top-menu > .menupop > .ab-sub-wrapper { + min-width: 100%; +} + +#wpadminbar .ab-top-secondary .menupop .ab-sub-wrapper { + right: 0; + left: auto; +} + +#wpadminbar .ab-submenu { + padding: 6px 0; +} + +#wpadminbar .selected .shortlink-input { + display: block; +} + +#wpadminbar .quicklinks .menupop ul li { + float: none; +} + +#wpadminbar .quicklinks .menupop ul li a strong { + font-weight: bold; +} + +#wpadminbar .quicklinks .menupop ul li .ab-item, +#wpadminbar .quicklinks .menupop ul li a strong, +#wpadminbar .quicklinks .menupop.hover ul li .ab-item, +#wpadminbar.nojs .quicklinks .menupop:hover ul li .ab-item, +#wpadminbar .shortlink-input { + line-height: 26px; + height: 26px; + white-space: nowrap; + min-width: 140px; +} + +#wpadminbar .shortlink-input { + width: 200px; +} + +#wpadminbar.nojs li:hover > .ab-sub-wrapper, +#wpadminbar li.hover > .ab-sub-wrapper { + display: block; +} + +#wpadminbar .menupop li:hover > .ab-sub-wrapper, +#wpadminbar .menupop li.hover > .ab-sub-wrapper { + margin-left: 100%; + margin-top: -32px; +} + +#wpadminbar .ab-top-secondary .menupop li:hover > .ab-sub-wrapper, +#wpadminbar .ab-top-secondary .menupop li.hover > .ab-sub-wrapper { + margin-left: 0; + left: inherit; + right: 100%; +} + +#wpadminbar .ab-top-menu > li > .ab-item:focus, +#wpadminbar.nojq .quicklinks .ab-top-menu > li > .ab-item:focus, +#wpadminbar .ab-top-menu > li:hover > .ab-item, +#wpadminbar .ab-top-menu > li.hover > .ab-item { + background: #333; + color: #45bbe6; +} + +#wpadminbar > #wp-toolbar li:hover span.ab-label, +#wpadminbar > #wp-toolbar li.hover span.ab-label, +#wpadminbar > #wp-toolbar a:focus span.ab-label { + color: #45bbe6; +} + +#wpadminbar > #wp-toolbar > #wp-admin-bar-root-default .ab-icon, +#wpadminbar .ab-icon, +#wpadminbar .ab-item:before { + position: relative; + float: left; + font: normal 20px/1 'dashicons'; + speak: none; + padding: 4px 0; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + background-image: none !important; + margin-right: 6px; +} + +#wpadminbar .ab-icon:before, +#wpadminbar .ab-item:before, +#wpadminbar #adminbarsearch:before { + color: #999; +} + +#wpadminbar .ab-icon:before, +#wpadminbar .ab-item:before, +#wpadminbar #adminbarsearch:before { + position: relative; + -webkit-transition: all .1s ease-in-out; + transition: all .1s ease-in-out; +} + +#wpadminbar .ab-label { + display: inline-block; + height: 32px; +} + +#wpadminbar .ab-submenu .ab-item { + color: #eee; +} + +#wpadminbar .quicklinks .menupop ul li a, +#wpadminbar .quicklinks .menupop ul li a strong, +#wpadminbar .quicklinks .menupop.hover ul li a, +#wpadminbar.nojs .quicklinks .menupop:hover ul li a { + color: #eee; +} + +#wpadminbar .quicklinks .menupop ul li a:hover, +#wpadminbar .quicklinks .menupop ul li a:focus, +#wpadminbar .quicklinks .menupop ul li a:hover strong, +#wpadminbar .quicklinks .menupop ul li a:focus strong, +#wpadminbar .quicklinks .menupop.hover ul li a:hover, +#wpadminbar .quicklinks .menupop.hover ul li a:focus, +#wpadminbar.nojs .quicklinks .menupop:hover ul li a:hover, +#wpadminbar.nojs .quicklinks .menupop:hover ul li a:focus, +#wpadminbar li:hover .ab-icon:before, +#wpadminbar li:hover .ab-item:before, +#wpadminbar li a:focus .ab-icon:before, +#wpadminbar li .ab-item:focus:before, +#wpadminbar li.hover .ab-icon:before, +#wpadminbar li.hover .ab-item:before, +#wpadminbar li:hover #adminbarsearch:before { + color: #45bbe6; +} + +#wpadminbar .menupop .menupop > .ab-item:before, +#wpadminbar .ab-top-secondary .menupop .menupop > .ab-item:before { + position: absolute; + font: normal 17px/1 'dashicons'; + speak: none; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +#wpadminbar .menupop .menupop > .ab-item { + display: block; + padding-right: 2em; +} + +#wpadminbar .menupop .menupop > .ab-item:before { + top: 1px; + right: 4px; + content: '\f139'; + color: inherit; +} + +#wpadminbar .ab-top-secondary .menupop .menupop > .ab-item { + padding-left: 2em; + padding-right: 1em; +} + +#wpadminbar .ab-top-secondary .menupop .menupop > .ab-item:before { + top: 5px; + left: 3px; + content: '\f141'; +} + +#wpadminbar .quicklinks .menupop ul.ab-sub-secondary { + display: block; + position: relative; + right: auto; + margin: 0; + -webkit-box-shadow: none; + box-shadow: none; +} + +#wpadminbar .quicklinks .menupop ul.ab-sub-secondary, +#wpadminbar .quicklinks .menupop ul.ab-sub-secondary .ab-submenu { + background: #4b4b4b; +} + +#wpadminbar .quicklinks .menupop .ab-sub-secondary > li > a:hover, +#wpadminbar .quicklinks .menupop .ab-sub-secondary > li .ab-item:focus a { + color: #45bbe6; +} + +#wpadminbar .quicklinks a span#ab-updates { + background: #eee; + color: #333; + display: inline; + padding: 2px 5px; + font-size: 10px; + font-weight: bold; + -webkit-border-radius: 10px; + border-radius: 10px; +} + +#wpadminbar .quicklinks a:hover span#ab-updates { + background: #fff; + color: #000; +} + +#wpadminbar .ab-top-secondary { + float: right; +} + +#wpadminbar ul li:last-child, +#wpadminbar ul li:last-child .ab-item { + -webkit-box-shadow: none; + box-shadow: none; +} + +/** + * My Account + */ +#wp-admin-bar-my-account > ul { + min-width: 198px; +} + +#wp-admin-bar-my-account > .ab-item:before { + content: "\f110"; + top: 2px; + float: right; + margin-left: 6px; + margin-right: 0; +} + +#wp-admin-bar-my-account.with-avatar > .ab-item:before { + display: none; + content: none; +} + +#wp-admin-bar-my-account.with-avatar > ul { + min-width: 270px; +} + +#wpadminbar #wp-admin-bar-user-actions > li { + margin-left: 16px; + margin-right: 16px; +} + +#wpadminbar #wp-admin-bar-user-actions.ab-submenu { + padding: 6px 0 12px; +} + +#wpadminbar #wp-admin-bar-my-account.with-avatar #wp-admin-bar-user-actions > li { + margin-left: 88px; +} + +#wpadminbar #wp-admin-bar-user-info { + margin-top: 6px; + margin-bottom: 15px; + height: auto; + background: none; +} + +#wp-admin-bar-user-info .avatar { + position: absolute; + left: -72px; + top: 4px; + width: 64px; + height: 64px; +} + +#wpadminbar #wp-admin-bar-user-info a { + background: none; + height: auto; +} + +#wpadminbar #wp-admin-bar-user-info span { + background: none; + padding: 0; + height: 18px; +} + +#wpadminbar #wp-admin-bar-user-info .display-name, +#wpadminbar #wp-admin-bar-user-info .username { + display: block; +} + +#wpadminbar #wp-admin-bar-user-info .username { + color: #999; + font-size: 11px; +} + +#wpadminbar .quicklinks li#wp-admin-bar-my-account.with-avatar > a img { + width: 16px; + height: 16px; + padding: 0; + border: 1px solid #888; + background: #eee; + line-height: 24px; + vertical-align: middle; + margin: -4px 0 0 6px; + float: none; + display: inline; +} + +/** + * WP Logo + */ +#wpadminbar #wp-admin-bar-wp-logo > .ab-item .ab-icon { + width: 15px; + height: 20px; + margin-right: 0; + padding: 6px 0 5px; +} + +#wpadminbar #wp-admin-bar-wp-logo > .ab-item { + padding: 0 7px; +} + +#wpadminbar #wp-admin-bar-wp-logo > .ab-item .ab-icon:before { + content: '\f120'; + top: 2px; +} + +/* + * My Sites & Site Title + */ +#wpadminbar .quicklinks li .blavatar { + float: left; + font: normal 16px/1 'dashicons' !important; + speak: none; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + color: #eee; +} + +#wpadminbar .quicklinks li a:hover .blavatar { + color: #45bbe6; +} + +#wpadminbar .quicklinks li .blavatar:before { + content: '\f120'; + height: 16px; + width: 16px; + display: inline-block; + margin: 6px 8px 0 -2px; +} + +#wpadminbar #wp-admin-bar-appearance { + margin-top: -12px; +} + +#wpadminbar #wp-admin-bar-my-sites > .ab-item:before, +#wpadminbar #wp-admin-bar-site-name > .ab-item:before { + content: '\f112'; + top: 2px; +} + +#wpadminbar #wp-admin-bar-edit > .ab-item:before { + content: '\f464'; + top: 2px; +} + +#wpadminbar #wp-admin-bar-site-name > .ab-item:before { + content: "\f226"; +} + +.wp-admin #wpadminbar #wp-admin-bar-site-name > .ab-item:before { + content: "\f102"; +} + + + +/** + * Comments + */ +#wpadminbar #wp-admin-bar-comments .ab-icon { + margin-right: 6px; +} + +#wpadminbar #wp-admin-bar-comments .ab-icon:before { + content: '\f101'; + top: 3px; +} + +#wpadminbar #wp-admin-bar-comments .count-0 { + opacity: .5; +} + +/** + * New Content + */ +#wpadminbar #wp-admin-bar-new-content .ab-icon:before { + content: '\f132'; + top: 4px; +} + +/** + * Updates + */ +#wpadminbar #wp-admin-bar-updates .ab-icon:before { + content: '\f463'; + top: 2px; +} + +/** + * Search + */ +#wpadminbar #wp-admin-bar-search .ab-item { + padding: 0; + background: transparent; +} + +#wpadminbar #adminbarsearch { + position: relative; + height: 32px; + padding: 0 2px; +} + +#wpadminbar #adminbarsearch:before { + position: absolute; + top: 6px; + left: 5px; + z-index: 20; + font: normal 20px/1 'dashicons' !important; + content: '\f179'; + speak: none; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +#wpadminbar > #wp-toolbar > #wp-admin-bar-top-secondary > #wp-admin-bar-search #adminbarsearch input.adminbar-input { + position: relative; + z-index: 30; + font: 13px/24px "Open Sans", sans-serif; + height: 24px; + width: 24px; + padding: 0 3px 0 24px; + margin: 0; + color: #ccc; + background-color: rgba( 255, 255, 255, 0 ); + border: none; + outline: none; + cursor: pointer; + -webkit-box-shadow: none; + box-shadow: none; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + -webkit-transition-duration: 400ms; + transition-duration: 400ms; + -webkit-transition-property: width, background; + transition-property: width, background; + -webkit-transition-timing-function: ease; + transition-timing-function: ease; +} + +#wpadminbar > #wp-toolbar > #wp-admin-bar-top-secondary > #wp-admin-bar-search #adminbarsearch input.adminbar-input:focus { + z-index: 10; + color: #000; + width: 200px; + background-color: rgba( 255, 255, 255, 0.9 ); + cursor: text; + border: 0; +} + +#wpadminbar.ie7 > #wp-toolbar > #wp-admin-bar-top-secondary > #wp-admin-bar-search #adminbarsearch input.adminbar-input { + margin-top: 3px; + width: 120px; +} + +#wpadminbar.ie8 > #wp-toolbar > #wp-admin-bar-top-secondary > #wp-admin-bar-search #adminbarsearch input.adminbar-input { + margin-top: 4px; + background-color: #464646; +} + +#wpadminbar.ie8 > #wp-toolbar > #wp-admin-bar-top-secondary > #wp-admin-bar-search #adminbarsearch input.adminbar-input:focus { + background-color: #fff; +} + +#wpadminbar #adminbarsearch .adminbar-input::-webkit-input-placeholder { + color: #999; +} +#wpadminbar #adminbarsearch .adminbar-input:-moz-placeholder { + color: #999; +} +#wpadminbar #adminbarsearch .adminbar-input::-moz-placeholder { + color: #999; +} +#wpadminbar #adminbarsearch .adminbar-input:-ms-input-placeholder { + color: #999; +} + +#wpadminbar #adminbarsearch .adminbar-button { + display: none; +} + +/** + * Customize support classes + */ +.no-customize-support .hide-if-no-customize, +.customize-support .hide-if-customize, +.no-customize-support #wpadminbar .hide-if-no-customize, +.no-customize-support.wp-core-ui .hide-if-no-customize, +.no-customize-support .wp-core-ui .hide-if-no-customize, +.customize-support #wpadminbar .hide-if-customize, +.customize-support.wp-core-ui .hide-if-customize, +.customize-support .wp-core-ui .hide-if-customize { + display: none; +} + +/* Skip link */ +#wpadminbar .screen-reader-text, +#wpadminbar .screen-reader-text span { + position: absolute; + left: -1000em; + top: -1000em; + height: 1px; + width: 1px; + overflow: hidden; +} + +#wpadminbar .screen-reader-shortcut { + position: absolute; + top: -1000em; +} + +#wpadminbar .screen-reader-shortcut:focus { + left: 6px; + top: 7px; + height: auto; + width: auto; + display: block; + font-size: 14px; + font-weight: bold; + padding: 15px 23px 14px; + background: #f1f1f1; + color: #21759b; + z-index: 100000; + line-height: normal; + text-decoration: none; + -webkit-box-shadow: 0 0 2px 2px rgba(0,0,0,.6); + box-shadow: 0 0 2px 2px rgba(0,0,0,.6); +} + +/** + * IE 6-targeted rules + */ +* html #wpadminbar { + overflow: hidden; + position: absolute; +} + +* html #wpadminbar .quicklinks ul li a { + float: left; +} + +* html #wpadminbar .menupop a span { + background-image: none; +} + +/* No @font-face support */ +.no-font-face #wpadminbar ul.ab-top-menu > li > a.ab-item { + display: block; + width: 45px; + text-align: center; + overflow: hidden; + margin: 0 3px; +} + +.no-font-face #wpadminbar #wp-admin-bar-my-sites > .ab-item, +.no-font-face #wpadminbar #wp-admin-bar-site-name > .ab-item, +.no-font-face #wpadminbar #wp-admin-bar-edit > .ab-item { + text-indent: 0; +} + +.no-font-face #wpadminbar .ab-icon, +.no-font-face #wpadminbar .ab-icon:before, +.no-font-face #wpadminbar a.ab-item:before, +.no-font-face #wpadminbar #wp-admin-bar-wp-logo > .ab-item { + display: none !important; +} + +.no-font-face #wpadminbar ul.ab-top-menu > li > a > span.ab-label { + display: inline; +} + +.no-font-face #wpadminbar #wp-admin-bar-menu-toggle span.ab-icon { + display: inline !important; +} + +.no-font-face #wpadminbar #wp-admin-bar-menu-toggle span.ab-icon:before { + content: "Menu"; + font: 14px/45px sans-serif !important; + display: inline-block !important; + color: #fff; +} + +.no-font-face #wpadminbar #wp-admin-bar-site-name a.ab-item { + color: #fff; +} +/* End no @font-face */ + +@media screen and ( max-width: 782px ) { + /* Toolbar Touchification*/ + html #wpadminbar { + height: 46px; + min-width: 300px; + + /* These rules break dropdown tappability on Chrome/Android. + -webkit-transform: translate3d(0, 0, 0); + -webkit-backface-visibility: hidden; + -webkit-transition: 0; + transform: translate3d(0, 0, 0); + backface-visibility: hidden; + transition: 0; + */ + } + + #wpadminbar * { + font: normal 14px/32px "Open Sans", sans-serif; + } + + #wpadminbar .quicklinks > ul > li > a, + #wpadminbar .quicklinks .ab-empty-item { + padding: 0; + height: 46px; + line-height: 46px; + width: auto; + } + + #wpadminbar .ab-icon { + font: 40px/1 dashicons !important; + margin: 0; + padding: 0; + width: 52px; + height: 46px; + text-align: center; + } + + #wpadminbar .ab-icon:before { + text-align: center; + } + + #wpadminbar .ab-submenu { + padding: 0; + } + + #wpadminbar #wp-admin-bar-site-name a.ab-item, + #wpadminbar #wp-admin-bar-my-sites a.ab-item, + #wpadminbar #wp-admin-bar-my-account a.ab-item { + text-overflow: clip; + } + + #wpadminbar .ab-label { + display: none; + } + + #wpadminbar .menupop li:hover > .ab-sub-wrapper, + #wpadminbar .menupop li.hover > .ab-sub-wrapper { + margin-top: -46px; + } + + #wpadminbar .ab-top-menu .menupop .ab-sub-wrapper .menupop > .ab-item { + padding-right: 30px; + } + + #wpadminbar .menupop .menupop > .ab-item:before { + top: 10px; + right: 6px; + } + + #wpadminbar .ab-top-menu > .menupop > .ab-sub-wrapper .ab-item { + font-size: 16px; + padding: 6px 15px 12px; + } + + #wpadminbar .ab-top-menu > .menupop > .ab-sub-wrapper a:empty { + display: none; + } + + /* WP logo */ + #wpadminbar #wp-admin-bar-wp-logo > .ab-item { + padding: 0; + } + + #wpadminbar #wp-admin-bar-wp-logo > .ab-item .ab-icon { + padding: 0; + width: 52px; + height: 46px; + text-align: center; + vertical-align: top; + } + + #wpadminbar #wp-admin-bar-wp-logo > .ab-item .ab-icon:before { + font: 28px/1 'dashicons' !important; + top: -3px; + } + + #wpadminbar .ab-icon, + #wpadminbar .ab-item:before { + padding: 0; + } + + /* My Sites and "Site Title" menu */ + #wpadminbar #wp-admin-bar-my-sites > .ab-item, + #wpadminbar #wp-admin-bar-site-name > .ab-item, + #wpadminbar #wp-admin-bar-edit > .ab-item, + #wpadminbar #wp-admin-bar-my-account > .ab-item { + text-indent: 100%; + white-space: nowrap; + overflow: hidden; + width: 52px; + padding: 0; + color: #999; + position: relative; + } + + #wpadminbar > #wp-toolbar > #wp-admin-bar-root-default .ab-icon, + #wpadminbar .ab-icon, + #wpadminbar .ab-item:before { + padding: 0; + margin-right: 0; + } + + #wpadminbar #wp-admin-bar-edit > .ab-item:before, + #wpadminbar #wp-admin-bar-my-sites > .ab-item:before, + #wpadminbar #wp-admin-bar-site-name > .ab-item:before, + #wpadminbar #wp-admin-bar-my-account > .ab-item:before { + display: block; + text-indent: 0; + font: normal 32px/1 'dashicons'; + speak: none; + top: 7px; + width: 52px; + text-align: center; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + } + + #wpadminbar #wp-admin-bar-appearance { + margin-top: 0; + } + + #wpadminbar .quicklinks li .blavatar:before { + display: none; + } + + /* Search */ + #wpadminbar #wp-admin-bar-search { + display: none; + } + + /* New Content */ + #wpadminbar #wp-admin-bar-new-content .ab-icon:before { + top: 0; + line-height: 53px; + height: 46px !important; + text-align: center; + width: 52px; + display: block; + } + + /* Updates */ + #wpadminbar #wp-admin-bar-updates { + text-align: center; + } + + #wpadminbar #wp-admin-bar-updates .ab-icon:before { + top: 3px; + } + + /* Comments */ + #wpadminbar #wp-admin-bar-comments .ab-icon { + margin: 0; + } + + #wpadminbar #wp-admin-bar-comments .ab-icon:before { + display: block; + font-size: 34px; + height: 46px; + line-height: 47px; + top: 0; + } + + /* My Account */ + #wpadminbar #wp-admin-bar-my-account > a { + position: relative; + white-space: nowrap; + text-indent: 150%; /* More than 100% indention is needed since this element has padding */ + width: 28px; + padding: 0 10px; + overflow: hidden; /* Prevent link text from forcing horizontal scrolling on mobile */ + } + + #wpadminbar .quicklinks li#wp-admin-bar-my-account.with-avatar > a img { + position: absolute; + top: 13px; + right: 10px; + width: 26px; + height: 26px; + } + + #wpadminbar #wp-admin-bar-user-actions.ab-submenu { + padding: 0; + } + + #wpadminbar #wp-admin-bar-user-actions.ab-submenu img.avatar { + display: none; + } + + #wpadminbar #wp-admin-bar-my-account.with-avatar #wp-admin-bar-user-actions > li { + margin: 0; + } + + #wpadminbar #wp-admin-bar-user-info .display-name { + height: auto; + font-size: 16px; + line-height: 24px; + color: #eee; + } + + #wpadminbar #wp-admin-bar-user-info a { + padding-top: 4px; + } + + #wpadminbar #wp-admin-bar-user-info .username { + line-height: 0.8 !important; + margin-bottom: -2px; + } + + /* Show only default top level items */ + #wp-toolbar > ul > li { + display: none; + } + + #wpadminbar li#wp-admin-bar-menu-toggle, + #wpadminbar li#wp-admin-bar-wp-logo, + #wpadminbar li#wp-admin-bar-my-sites, + #wpadminbar li#wp-admin-bar-updates, + #wpadminbar li#wp-admin-bar-site-name, + #wpadminbar li#wp-admin-bar-new-content, + #wpadminbar li#wp-admin-bar-edit, + #wpadminbar li#wp-admin-bar-comments, + #wpadminbar li#wp-admin-bar-new-content, + #wpadminbar li#wp-admin-bar-my-account { + display: block; + } + + /* Allow dropdown list items to appear normally */ + #wpadminbar li:hover ul li, + #wpadminbar li.hover ul li, + #wpadminbar li:hover ul li:hover ul li { + display: list-item; + } + + /* Override default min-width so dropdown lists aren't stretched + to 100% viewport width at responsive sizes. */ + #wpadminbar .ab-top-menu > .menupop > .ab-sub-wrapper { + min-width: -webkit-fit-content; + min-width: -moz-fit-content; + min-width: fit-content; + } + + #wpadminbar ul#wp-admin-bar-root-default > li { + margin-right: 0; + } + + /* Experimental fix for touch toolbar dropdown positioning */ + #wpadminbar .ab-top-menu, + #wpadminbar .ab-top-secondary, + #wpadminbar #wp-admin-bar-wp-logo, + #wpadminbar #wp-admin-bar-my-sites, + #wpadminbar #wp-admin-bar-site-name, + #wpadminbar #wp-admin-bar-updates, + #wpadminbar #wp-admin-bar-comments, + #wpadminbar #wp-admin-bar-new-content, + #wpadminbar #wp-admin-bar-edit, + #wpadminbar #wp-admin-bar-my-account { + position: static; + } + + #wpadminbar #wp-admin-bar-my-account { + float: right; + } + + .network-admin #wpadminbar ul#wp-admin-bar-top-secondary > li#wp-admin-bar-my-account { + margin-right: 0; + } + + /* Realign arrows on taller responsive submenus */ + + #wpadminbar .ab-top-secondary .menupop .menupop > .ab-item:before { + top: 10px; + left: 0; + } +} + +/* Smartphone */ +@media screen and (max-width: 600px) { + #wpadminbar { + position: absolute; + } + + #wp-responsive-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 400; + } + + #wpadminbar .ab-top-menu > .menupop > .ab-sub-wrapper { + width: 100%; + left: 0; + } + + #wpadminbar .menupop .menupop > .ab-item:before { + display: none; + } + + #wpadminbar #wp-admin-bar-wp-logo.menupop .ab-sub-wrapper { + margin-left: 0; + } + + #wpadminbar .ab-top-menu > .menupop li > .ab-sub-wrapper { + margin: 0; + width: 100%; + top: auto; + left: auto; + position: relative; + } + + #wpadminbar .ab-top-menu > .menupop li > .ab-sub-wrapper .ab-item { + font-size: 16px; + padding: 6px 15px 19px 30px; + } + + #wpadminbar li:hover ul li ul li { + display: list-item; + } + + #wpadminbar li#wp-admin-bar-wp-logo, + #wpadminbar li#wp-admin-bar-updates { + display: none; + } + + /* Make submenus full-width at this size */ + + #wpadminbar .ab-top-menu > .menupop li > .ab-sub-wrapper { + position: static; + -webkit-box-shadow: none; + box-shadow: none; + } +} diff --git a/wp-includes/css/admin-bar.min.css b/wp-includes/css/admin-bar.min.css new file mode 100644 index 0000000..440613d --- /dev/null +++ b/wp-includes/css/admin-bar.min.css @@ -0,0 +1 @@ +#wpadminbar *{height:auto;width:auto;margin:0;padding:0;position:static;text-shadow:none;text-transform:none;letter-spacing:normal;font:400 13px/32px "Open Sans",sans-serif;-webkit-border-radius:0;border-radius:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-transition:none;transition:none;-webkit-font-smoothing:subpixel-antialiased}.rtl #wpadminbar *{font-family:Tahoma,sans-serif}html:lang(he-il) .rtl #wpadminbar *{font-family:Arial,sans-serif}#wpadminbar a.ab-item,#wpadminbar>#wp-toolbar span.ab-label,#wpadminbar>#wp-toolbar span.noticon{color:#eee}#wpadminbar #wp-admin-bar-my-sites a.ab-item,#wpadminbar #wp-admin-bar-site-name a.ab-item{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}#wpadminbar ul li:after,#wpadminbar ul li:before{content:normal}#wpadminbar a,#wpadminbar a img,#wpadminbar a img:hover,#wpadminbar a:hover{outline:0;border:none;text-decoration:none;background:0 0}#wpadminbar a:active,#wpadminbar a:focus,#wpadminbar div,#wpadminbar input[type=email],#wpadminbar input[type=number],#wpadminbar input[type=password],#wpadminbar input[type=search],#wpadminbar input[type=text],#wpadminbar input[type=url],#wpadminbar select,#wpadminbar textarea{-webkit-box-shadow:none;box-shadow:none;outline:0}#wpadminbar{direction:ltr;color:#ccc;font:400 13px/32px "Open Sans",sans-serif;height:32px;position:fixed;top:0;left:0;width:100%;min-width:600px;z-index:99999;background:#222}#wpadminbar .ab-sub-wrapper,#wpadminbar ul,#wpadminbar ul li{background:0 0;clear:none;list-style:none;margin:0;padding:0;position:relative;text-indent:0;z-index:99999}#wpadminbar ul#wp-admin-bar-root-default>li{margin-right:0}#wpadminbar .quicklinks ul{text-align:left}#wpadminbar li{float:left}#wpadminbar .ab-empty-item{outline:0}#wpadminbar .quicklinks .ab-top-secondary>li{float:right}#wpadminbar .quicklinks .ab-empty-item,#wpadminbar .quicklinks a,#wpadminbar .shortlink-input{height:32px;display:block;padding:0 10px;margin:0}#wpadminbar .quicklinks>ul>li>a{padding:0 8px 0 7px}#wpadminbar .menupop .ab-sub-wrapper,#wpadminbar .shortlink-input{margin:0;padding:0;-webkit-box-shadow:0 3px 5px rgba(0,0,0,.2);box-shadow:0 3px 5px rgba(0,0,0,.2);background:#333;display:none;position:absolute;float:none}#wpadminbar.ie7 .menupop .ab-sub-wrapper,#wpadminbar.ie7 .shortlink-input{top:32px;left:0}#wpadminbar .ab-top-menu>.menupop>.ab-sub-wrapper{min-width:100%}#wpadminbar .ab-top-secondary .menupop .ab-sub-wrapper{right:0;left:auto}#wpadminbar .ab-submenu{padding:6px 0}#wpadminbar .selected .shortlink-input{display:block}#wpadminbar .quicklinks .menupop ul li{float:none}#wpadminbar .quicklinks .menupop ul li a strong{font-weight:700}#wpadminbar .quicklinks .menupop ul li .ab-item,#wpadminbar .quicklinks .menupop ul li a strong,#wpadminbar .quicklinks .menupop.hover ul li .ab-item,#wpadminbar .shortlink-input,#wpadminbar.nojs .quicklinks .menupop:hover ul li .ab-item{line-height:26px;height:26px;white-space:nowrap;min-width:140px}#wpadminbar .shortlink-input{width:200px}#wpadminbar li.hover>.ab-sub-wrapper,#wpadminbar.nojs li:hover>.ab-sub-wrapper{display:block}#wpadminbar .menupop li.hover>.ab-sub-wrapper,#wpadminbar .menupop li:hover>.ab-sub-wrapper{margin-left:100%;margin-top:-32px}#wpadminbar .ab-top-secondary .menupop li.hover>.ab-sub-wrapper,#wpadminbar .ab-top-secondary .menupop li:hover>.ab-sub-wrapper{margin-left:0;left:inherit;right:100%}#wpadminbar .ab-top-menu>li.hover>.ab-item,#wpadminbar .ab-top-menu>li:hover>.ab-item,#wpadminbar .ab-top-menu>li>.ab-item:focus,#wpadminbar.nojq .quicklinks .ab-top-menu>li>.ab-item:focus{background:#333;color:#45bbe6}#wpadminbar>#wp-toolbar a:focus span.ab-label,#wpadminbar>#wp-toolbar li.hover span.ab-label,#wpadminbar>#wp-toolbar li:hover span.ab-label{color:#45bbe6}#wpadminbar .ab-icon,#wpadminbar .ab-item:before,#wpadminbar>#wp-toolbar>#wp-admin-bar-root-default .ab-icon{position:relative;float:left;font:400 20px/1 dashicons;speak:none;padding:4px 0;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;background-image:none!important;margin-right:6px}#wpadminbar #adminbarsearch:before,#wpadminbar .ab-icon:before,#wpadminbar .ab-item:before{color:#999;position:relative;-webkit-transition:all .1s ease-in-out;transition:all .1s ease-in-out}#wpadminbar .ab-label{display:inline-block;height:32px}#wpadminbar .ab-submenu .ab-item,#wpadminbar .quicklinks .menupop ul li a,#wpadminbar .quicklinks .menupop ul li a strong,#wpadminbar .quicklinks .menupop.hover ul li a,#wpadminbar.nojs .quicklinks .menupop:hover ul li a{color:#eee}#wpadminbar .quicklinks .menupop ul li a:focus,#wpadminbar .quicklinks .menupop ul li a:focus strong,#wpadminbar .quicklinks .menupop ul li a:hover,#wpadminbar .quicklinks .menupop ul li a:hover strong,#wpadminbar .quicklinks .menupop.hover ul li a:focus,#wpadminbar .quicklinks .menupop.hover ul li a:hover,#wpadminbar li .ab-item:focus:before,#wpadminbar li a:focus .ab-icon:before,#wpadminbar li.hover .ab-icon:before,#wpadminbar li.hover .ab-item:before,#wpadminbar li:hover #adminbarsearch:before,#wpadminbar li:hover .ab-icon:before,#wpadminbar li:hover .ab-item:before,#wpadminbar.nojs .quicklinks .menupop:hover ul li a:focus,#wpadminbar.nojs .quicklinks .menupop:hover ul li a:hover{color:#45bbe6}#wpadminbar .ab-top-secondary .menupop .menupop>.ab-item:before,#wpadminbar .menupop .menupop>.ab-item:before{position:absolute;font:400 17px/1 dashicons;speak:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}#wpadminbar .menupop .menupop>.ab-item{display:block;padding-right:2em}#wpadminbar .menupop .menupop>.ab-item:before{top:1px;right:4px;content:'\f139';color:inherit}#wpadminbar .ab-top-secondary .menupop .menupop>.ab-item{padding-left:2em;padding-right:1em}#wpadminbar .ab-top-secondary .menupop .menupop>.ab-item:before{top:5px;left:3px;content:'\f141'}#wpadminbar .quicklinks .menupop ul.ab-sub-secondary{display:block;position:relative;right:auto;margin:0;-webkit-box-shadow:none;box-shadow:none}#wpadminbar .quicklinks .menupop ul.ab-sub-secondary,#wpadminbar .quicklinks .menupop ul.ab-sub-secondary .ab-submenu{background:#4b4b4b}#wpadminbar .quicklinks .menupop .ab-sub-secondary>li .ab-item:focus a,#wpadminbar .quicklinks .menupop .ab-sub-secondary>li>a:hover{color:#45bbe6}#wpadminbar .quicklinks a span#ab-updates{background:#eee;color:#333;display:inline;padding:2px 5px;font-size:10px;font-weight:700;-webkit-border-radius:10px;border-radius:10px}#wpadminbar .quicklinks a:hover span#ab-updates{background:#fff;color:#000}#wpadminbar .ab-top-secondary{float:right}#wpadminbar ul li:last-child,#wpadminbar ul li:last-child .ab-item{-webkit-box-shadow:none;box-shadow:none}#wp-admin-bar-my-account>ul{min-width:198px}#wp-admin-bar-my-account>.ab-item:before{content:"\f110";top:2px;float:right;margin-left:6px;margin-right:0}#wp-admin-bar-my-account.with-avatar>.ab-item:before{display:none;content:none}#wp-admin-bar-my-account.with-avatar>ul{min-width:270px}#wpadminbar #wp-admin-bar-user-actions>li{margin-left:16px;margin-right:16px}#wpadminbar #wp-admin-bar-user-actions.ab-submenu{padding:6px 0 12px}#wpadminbar #wp-admin-bar-my-account.with-avatar #wp-admin-bar-user-actions>li{margin-left:88px}#wpadminbar #wp-admin-bar-user-info{margin-top:6px;margin-bottom:15px;height:auto;background:0 0}#wp-admin-bar-user-info .avatar{position:absolute;left:-72px;top:4px;width:64px;height:64px}#wpadminbar #wp-admin-bar-user-info a{background:0 0;height:auto}#wpadminbar #wp-admin-bar-user-info span{background:0 0;padding:0;height:18px}#wpadminbar #wp-admin-bar-user-info .display-name,#wpadminbar #wp-admin-bar-user-info .username{display:block}#wpadminbar #wp-admin-bar-user-info .username{color:#999;font-size:11px}#wpadminbar .quicklinks li#wp-admin-bar-my-account.with-avatar>a img{width:16px;height:16px;padding:0;border:1px solid #888;background:#eee;line-height:24px;vertical-align:middle;margin:-4px 0 0 6px;float:none;display:inline}#wpadminbar #wp-admin-bar-wp-logo>.ab-item .ab-icon{width:15px;height:20px;margin-right:0;padding:6px 0 5px}#wpadminbar #wp-admin-bar-wp-logo>.ab-item{padding:0 7px}#wpadminbar #wp-admin-bar-wp-logo>.ab-item .ab-icon:before{content:'\f120';top:2px}#wpadminbar .quicklinks li .blavatar{float:left;font:400 16px/1 dashicons!important;speak:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:#eee}#wpadminbar .quicklinks li a:hover .blavatar{color:#45bbe6}#wpadminbar .quicklinks li .blavatar:before{content:'\f120';height:16px;width:16px;display:inline-block;margin:6px 8px 0 -2px}#wpadminbar #wp-admin-bar-appearance{margin-top:-12px}#wpadminbar #wp-admin-bar-my-sites>.ab-item:before,#wpadminbar #wp-admin-bar-site-name>.ab-item:before{content:'\f112';top:2px}#wpadminbar #wp-admin-bar-edit>.ab-item:before{content:'\f464';top:2px}#wpadminbar #wp-admin-bar-site-name>.ab-item:before{content:"\f226"}.wp-admin #wpadminbar #wp-admin-bar-site-name>.ab-item:before{content:"\f102"}#wpadminbar #wp-admin-bar-comments .ab-icon{margin-right:6px}#wpadminbar #wp-admin-bar-comments .ab-icon:before{content:'\f101';top:3px}#wpadminbar #wp-admin-bar-comments .count-0{opacity:.5}#wpadminbar #wp-admin-bar-new-content .ab-icon:before{content:'\f132';top:4px}#wpadminbar #wp-admin-bar-updates .ab-icon:before{content:'\f463';top:2px}#wpadminbar #wp-admin-bar-search .ab-item{padding:0;background:0 0}#wpadminbar #adminbarsearch{position:relative;height:32px;padding:0 2px}#wpadminbar #adminbarsearch:before{position:absolute;top:6px;left:5px;z-index:20;font:400 20px/1 dashicons!important;content:'\f179';speak:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}#wpadminbar>#wp-toolbar>#wp-admin-bar-top-secondary>#wp-admin-bar-search #adminbarsearch input.adminbar-input{position:relative;z-index:30;font:13px/24px "Open Sans",sans-serif;height:24px;width:24px;padding:0 3px 0 24px;margin:0;color:#ccc;background-color:rgba(255,255,255,0);border:none;outline:0;cursor:pointer;-webkit-box-shadow:none;box-shadow:none;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition-duration:400ms;transition-duration:400ms;-webkit-transition-property:width,background;transition-property:width,background;-webkit-transition-timing-function:ease;transition-timing-function:ease}#wpadminbar>#wp-toolbar>#wp-admin-bar-top-secondary>#wp-admin-bar-search #adminbarsearch input.adminbar-input:focus{z-index:10;color:#000;width:200px;background-color:rgba(255,255,255,.9);cursor:text;border:0}#wpadminbar.ie7>#wp-toolbar>#wp-admin-bar-top-secondary>#wp-admin-bar-search #adminbarsearch input.adminbar-input{margin-top:3px;width:120px}#wpadminbar.ie8>#wp-toolbar>#wp-admin-bar-top-secondary>#wp-admin-bar-search #adminbarsearch input.adminbar-input{margin-top:4px;background-color:#464646}#wpadminbar.ie8>#wp-toolbar>#wp-admin-bar-top-secondary>#wp-admin-bar-search #adminbarsearch input.adminbar-input:focus{background-color:#fff}#wpadminbar #adminbarsearch .adminbar-input::-webkit-input-placeholder{color:#999}#wpadminbar #adminbarsearch .adminbar-input:-moz-placeholder{color:#999}#wpadminbar #adminbarsearch .adminbar-input::-moz-placeholder{color:#999}#wpadminbar #adminbarsearch .adminbar-input:-ms-input-placeholder{color:#999}#wpadminbar #adminbarsearch .adminbar-button,.customize-support #wpadminbar .hide-if-customize,.customize-support .hide-if-customize,.customize-support .wp-core-ui .hide-if-customize,.customize-support.wp-core-ui .hide-if-customize,.no-customize-support #wpadminbar .hide-if-no-customize,.no-customize-support .hide-if-no-customize,.no-customize-support .wp-core-ui .hide-if-no-customize,.no-customize-support.wp-core-ui .hide-if-no-customize{display:none}#wpadminbar .screen-reader-text,#wpadminbar .screen-reader-text span{position:absolute;left:-1000em;top:-1000em;height:1px;width:1px;overflow:hidden}#wpadminbar .screen-reader-shortcut{position:absolute;top:-1000em}#wpadminbar .screen-reader-shortcut:focus{left:6px;top:7px;height:auto;width:auto;display:block;font-size:14px;font-weight:700;padding:15px 23px 14px;background:#f1f1f1;color:#21759b;z-index:100000;line-height:normal;text-decoration:none;-webkit-box-shadow:0 0 2px 2px rgba(0,0,0,.6);box-shadow:0 0 2px 2px rgba(0,0,0,.6)}* html #wpadminbar{overflow:hidden;position:absolute}* html #wpadminbar .quicklinks ul li a{float:left}* html #wpadminbar .menupop a span{background-image:none}.no-font-face #wpadminbar ul.ab-top-menu>li>a.ab-item{display:block;width:45px;text-align:center;overflow:hidden;margin:0 3px}.no-font-face #wpadminbar #wp-admin-bar-edit>.ab-item,.no-font-face #wpadminbar #wp-admin-bar-my-sites>.ab-item,.no-font-face #wpadminbar #wp-admin-bar-site-name>.ab-item{text-indent:0}.no-font-face #wpadminbar #wp-admin-bar-wp-logo>.ab-item,.no-font-face #wpadminbar .ab-icon,.no-font-face #wpadminbar .ab-icon:before,.no-font-face #wpadminbar a.ab-item:before{display:none!important}.no-font-face #wpadminbar ul.ab-top-menu>li>a>span.ab-label{display:inline}.no-font-face #wpadminbar #wp-admin-bar-menu-toggle span.ab-icon{display:inline!important}.no-font-face #wpadminbar #wp-admin-bar-menu-toggle span.ab-icon:before{content:"Menu";font:14px/45px sans-serif!important;display:inline-block!important;color:#fff}.no-font-face #wpadminbar #wp-admin-bar-site-name a.ab-item{color:#fff}@media screen and (max-width:782px){html #wpadminbar{height:46px;min-width:300px}#wpadminbar *{font:400 14px/32px "Open Sans",sans-serif}#wpadminbar .quicklinks .ab-empty-item,#wpadminbar .quicklinks>ul>li>a{padding:0;height:46px;line-height:46px;width:auto}#wpadminbar .ab-icon{font:40px/1 dashicons!important;margin:0;width:52px;height:46px;text-align:center}#wpadminbar .ab-icon:before{text-align:center}#wpadminbar .ab-submenu{padding:0}#wpadminbar #wp-admin-bar-my-account a.ab-item,#wpadminbar #wp-admin-bar-my-sites a.ab-item,#wpadminbar #wp-admin-bar-site-name a.ab-item{text-overflow:clip}#wpadminbar .ab-label{display:none}#wpadminbar .menupop li.hover>.ab-sub-wrapper,#wpadminbar .menupop li:hover>.ab-sub-wrapper{margin-top:-46px}#wpadminbar .ab-top-menu .menupop .ab-sub-wrapper .menupop>.ab-item{padding-right:30px}#wpadminbar .menupop .menupop>.ab-item:before{top:10px;right:6px}#wpadminbar .ab-top-menu>.menupop>.ab-sub-wrapper .ab-item{font-size:16px;padding:6px 15px 12px}#wpadminbar .ab-top-menu>.menupop>.ab-sub-wrapper a:empty{display:none}#wpadminbar #wp-admin-bar-wp-logo>.ab-item{padding:0}#wpadminbar #wp-admin-bar-wp-logo>.ab-item .ab-icon{padding:0;width:52px;height:46px;text-align:center;vertical-align:top}#wpadminbar #wp-admin-bar-wp-logo>.ab-item .ab-icon:before{font:28px/1 dashicons!important;top:-3px}#wpadminbar #wp-admin-bar-edit>.ab-item,#wpadminbar #wp-admin-bar-my-account>.ab-item,#wpadminbar #wp-admin-bar-my-sites>.ab-item,#wpadminbar #wp-admin-bar-site-name>.ab-item{text-indent:100%;white-space:nowrap;overflow:hidden;width:52px;padding:0;color:#999;position:relative}#wpadminbar .ab-icon,#wpadminbar .ab-item:before,#wpadminbar>#wp-toolbar>#wp-admin-bar-root-default .ab-icon{padding:0;margin-right:0}#wpadminbar #wp-admin-bar-edit>.ab-item:before,#wpadminbar #wp-admin-bar-my-account>.ab-item:before,#wpadminbar #wp-admin-bar-my-sites>.ab-item:before,#wpadminbar #wp-admin-bar-site-name>.ab-item:before{display:block;text-indent:0;font:400 32px/1 dashicons;speak:none;top:7px;width:52px;text-align:center;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}#wpadminbar #wp-admin-bar-appearance{margin-top:0}#wpadminbar #wp-admin-bar-search,#wpadminbar .quicklinks li .blavatar:before{display:none}#wpadminbar #wp-admin-bar-new-content .ab-icon:before{top:0;line-height:53px;height:46px!important;text-align:center;width:52px;display:block}#wpadminbar #wp-admin-bar-updates{text-align:center}#wpadminbar #wp-admin-bar-updates .ab-icon:before{top:3px}#wpadminbar #wp-admin-bar-comments .ab-icon{margin:0}#wpadminbar #wp-admin-bar-comments .ab-icon:before{display:block;font-size:34px;height:46px;line-height:47px;top:0}#wpadminbar #wp-admin-bar-my-account>a{position:relative;white-space:nowrap;text-indent:150%;width:28px;padding:0 10px;overflow:hidden}#wpadminbar .quicklinks li#wp-admin-bar-my-account.with-avatar>a img{position:absolute;top:13px;right:10px;width:26px;height:26px}#wpadminbar #wp-admin-bar-user-actions.ab-submenu{padding:0}#wpadminbar #wp-admin-bar-user-actions.ab-submenu img.avatar{display:none}#wpadminbar #wp-admin-bar-my-account.with-avatar #wp-admin-bar-user-actions>li{margin:0}#wpadminbar #wp-admin-bar-user-info .display-name{height:auto;font-size:16px;line-height:24px;color:#eee}#wpadminbar #wp-admin-bar-user-info a{padding-top:4px}#wpadminbar #wp-admin-bar-user-info .username{line-height:.8!important;margin-bottom:-2px}#wp-toolbar>ul>li{display:none}#wpadminbar li#wp-admin-bar-comments,#wpadminbar li#wp-admin-bar-edit,#wpadminbar li#wp-admin-bar-menu-toggle,#wpadminbar li#wp-admin-bar-my-account,#wpadminbar li#wp-admin-bar-my-sites,#wpadminbar li#wp-admin-bar-new-content,#wpadminbar li#wp-admin-bar-site-name,#wpadminbar li#wp-admin-bar-updates,#wpadminbar li#wp-admin-bar-wp-logo{display:block}#wpadminbar li.hover ul li,#wpadminbar li:hover ul li,#wpadminbar li:hover ul li:hover ul li{display:list-item}#wpadminbar .ab-top-menu>.menupop>.ab-sub-wrapper{min-width:-webkit-fit-content;min-width:-moz-fit-content;min-width:fit-content}#wpadminbar ul#wp-admin-bar-root-default>li{margin-right:0}#wpadminbar #wp-admin-bar-comments,#wpadminbar #wp-admin-bar-edit,#wpadminbar #wp-admin-bar-my-account,#wpadminbar #wp-admin-bar-my-sites,#wpadminbar #wp-admin-bar-new-content,#wpadminbar #wp-admin-bar-site-name,#wpadminbar #wp-admin-bar-updates,#wpadminbar #wp-admin-bar-wp-logo,#wpadminbar .ab-top-menu,#wpadminbar .ab-top-secondary{position:static}#wpadminbar #wp-admin-bar-my-account{float:right}.network-admin #wpadminbar ul#wp-admin-bar-top-secondary>li#wp-admin-bar-my-account{margin-right:0}#wpadminbar .ab-top-secondary .menupop .menupop>.ab-item:before{top:10px;left:0}}@media screen and (max-width:600px){#wpadminbar{position:absolute}#wp-responsive-overlay{position:fixed;top:0;left:0;width:100%;height:100%;z-index:400}#wpadminbar .ab-top-menu>.menupop>.ab-sub-wrapper{width:100%;left:0}#wpadminbar .menupop .menupop>.ab-item:before{display:none}#wpadminbar #wp-admin-bar-wp-logo.menupop .ab-sub-wrapper{margin-left:0}#wpadminbar .ab-top-menu>.menupop li>.ab-sub-wrapper{margin:0;width:100%;top:auto;left:auto}#wpadminbar .ab-top-menu>.menupop li>.ab-sub-wrapper .ab-item{font-size:16px;padding:6px 15px 19px 30px}#wpadminbar li:hover ul li ul li{display:list-item}#wpadminbar li#wp-admin-bar-updates,#wpadminbar li#wp-admin-bar-wp-logo{display:none}#wpadminbar .ab-top-menu>.menupop li>.ab-sub-wrapper{position:static;-webkit-box-shadow:none;box-shadow:none}} \ No newline at end of file diff --git a/wp-includes/css/buttons-rtl.css b/wp-includes/css/buttons-rtl.css new file mode 100644 index 0000000..d4e0b53 --- /dev/null +++ b/wp-includes/css/buttons-rtl.css @@ -0,0 +1,364 @@ +/* ---------------------------------------------------------------------------- + +NOTE: If you edit this file, you should make sure that the CSS rules for +buttons in the following files are updated. + +* jquery-ui-dialog.css +* editor.css + +WordPress-style Buttons +======================= +Create a button by adding the %60.button` class to an element. For backwards +compatibility, we support several other classes (such as `.button-secondary`), +but these will *not* work with the stackable classes described below. + +Button Styles +------------- +To display a primary button style, add the `.button-primary` class to a button. + +Button Sizes +------------ +Adjust a button's size by adding the `.button-large` or `.button-small` class. + +Button States +------------- +Lock the state of a button by adding the name of the pseudoclass as +an actual class (e.g. `.hover` for `:hover`). + + +TABLE OF CONTENTS: +------------------ + 1.0 - Button Layouts + 2.0 - Default Button Style + 3.0 - Primary Button Style + 4.0 - Button Groups + 5.0 - Responsive Button Styles + +---------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------------- + 1.0 - Button Layouts +---------------------------------------------------------------------------- */ + +.wp-core-ui .button, +.wp-core-ui .button-primary, +.wp-core-ui .button-secondary { + display: inline-block; + text-decoration: none; + font-size: 13px; + line-height: 26px; + height: 28px; + margin: 0; + padding: 0 10px 1px; + cursor: pointer; + border-width: 1px; + border-style: solid; + -webkit-appearance: none; + -webkit-border-radius: 3px; + border-radius: 3px; + white-space: nowrap; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +/* Remove the dotted border on :focus and the extra padding in Firefox */ +.wp-core-ui button::-moz-focus-inner, +.wp-core-ui input[type="reset"]::-moz-focus-inner, +.wp-core-ui input[type="button"]::-moz-focus-inner, +.wp-core-ui input[type="submit"]::-moz-focus-inner { + border-width: 0; + border-style: none; + padding: 0; +} + +.wp-core-ui .button.button-large, +.wp-core-ui .button-group.button-large .button { + height: 30px; + line-height: 28px; + padding: 0 12px 2px; +} + +.wp-core-ui .button.button-small, +.wp-core-ui .button-group.button-small .button { + height: 24px; + line-height: 22px; + padding: 0 8px 1px; + font-size: 11px; +} + +.wp-core-ui .button.button-hero, +.wp-core-ui .button-group.button-hero .button { + font-size: 14px; + height: 46px; + line-height: 44px; + padding: 0 36px; +} + +.wp-core-ui .button:active, +.wp-core-ui .button:focus { + outline: none; +} + +.wp-core-ui .button.hidden { + display: none; +} + +/* Style Reset buttons as simple text links */ + +.wp-core-ui input[type="reset"], +.wp-core-ui input[type="reset"]:hover, +.wp-core-ui input[type="reset"]:active, +.wp-core-ui input[type="reset"]:focus { + background: none; + border: none; + -webkit-box-shadow: none; + box-shadow: none; + padding: 0 2px 1px; + width: auto; +} + +/* ---------------------------------------------------------------------------- + 2.0 - Default Button Style +---------------------------------------------------------------------------- */ + +.wp-core-ui .button, +.wp-core-ui .button-secondary { + color: #555; + border-color: #cccccc; + background: #f7f7f7; + -webkit-box-shadow: inset 0 1px 0 #fff, 0 1px 0 rgba( 0, 0, 0, 0.08 ); + box-shadow: inset 0 1px 0 #fff, 0 1px 0 rgba( 0, 0, 0, 0.08 ); + vertical-align: top; +} + +.wp-core-ui p .button { + vertical-align: baseline; +} + +.wp-core-ui .button.hover, +.wp-core-ui .button:hover, +.wp-core-ui .button-secondary:hover, +.wp-core-ui .button.focus, +.wp-core-ui .button:focus, +.wp-core-ui .button-secondary:focus { + background: #fafafa; + border-color: #999; + color: #222; +} + +.wp-core-ui .button.focus, +.wp-core-ui .button:focus, +.wp-core-ui .button-secondary:focus { + -webkit-box-shadow: + 0 0 0 1px #5b9dd9, + 0 0 2px 1px rgba(30, 140, 190, .8); + box-shadow: + 0 0 0 1px #5b9dd9, + 0 0 2px 1px rgba(30, 140, 190, .8); +} + +.wp-core-ui .button.active, +.wp-core-ui .button.active:hover, +.wp-core-ui .button:active, +.wp-core-ui .button-secondary:active { + background: #eee; + border-color: #999; + color: #333; + -webkit-box-shadow: inset 0 2px 5px -3px rgba( 0, 0, 0, 0.5 ); + box-shadow: inset 0 2px 5px -3px rgba( 0, 0, 0, 0.5 ); +} + +.wp-core-ui .button.active:focus { + -webkit-box-shadow: + inset 0 2px 5px -3px rgba( 0, 0, 0, 0.5 ), + 0 0 0 1px #5b9dd9, + 0 0 2px 1px rgba(30, 140, 190, .8); + box-shadow: + inset 0 2px 5px -3px rgba( 0, 0, 0, 0.5 ), + 0 0 0 1px #5b9dd9, + 0 0 2px 1px rgba(30, 140, 190, .8); +} + +.wp-core-ui .button[disabled], +.wp-core-ui .button:disabled, +.wp-core-ui .button.disabled, +.wp-core-ui .button-secondary[disabled], +.wp-core-ui .button-secondary:disabled, +.wp-core-ui .button-secondary.disabled, +.wp-core-ui .button-disabled { + color: #aaa !important; + border-color: #ddd !important; + background: #f7f7f7 !important; + -webkit-box-shadow: none !important; + box-shadow: none !important; + text-shadow: 0 1px 0 #fff !important; + cursor: default; +} + +/* ---------------------------------------------------------------------------- + 3.0 - Primary Button Style +---------------------------------------------------------------------------- */ + +.wp-core-ui .button-primary { + background: #2ea2cc; + border-color: #0074a2; + -webkit-box-shadow: inset 0 1px 0 rgba( 120, 200, 230, 0.5), 0 1px 0 rgba( 0, 0, 0, 0.15 ); + box-shadow: inset 0 1px 0 rgba( 120, 200, 230, 0.5 ), 0 1px 0 rgba( 0, 0, 0, 0.15 ); + color: #fff; + text-decoration: none; +} + +.wp-core-ui .button-primary.hover, +.wp-core-ui .button-primary:hover, +.wp-core-ui .button-primary.focus, +.wp-core-ui .button-primary:focus { + background: #1e8cbe; + border-color: #0074a2; + -webkit-box-shadow: inset 0 1px 0 rgba( 120, 200, 230, 0.6 ); + box-shadow: inset 0 1px 0 rgba( 120, 200, 230, 0.6 ); + color: #fff; +} + +.wp-core-ui .button-primary.focus, +.wp-core-ui .button-primary:focus { + border-color: #0e3950; + -webkit-box-shadow: + inset 0 1px 0 rgba( 120, 200, 230, 0.6 ), + 0 0 0 1px #5b9dd9, + 0 0 2px 1px rgba(30, 140, 190, .8); + box-shadow: + inset 0 1px 0 rgba( 120, 200, 230, 0.6 ), + 0 0 0 1px #5b9dd9, + 0 0 2px 1px rgba(30, 140, 190, .8); +} + +.wp-core-ui .button-primary.active, +.wp-core-ui .button-primary.active:hover, +.wp-core-ui .button-primary.active:focus, +.wp-core-ui .button-primary:active { + background: #1b7aa6; + border-color: #005684; + color: rgba( 255, 255, 255, 0.95 ); + -webkit-box-shadow: inset 0 1px 0 rgba( 0, 0, 0, 0.1 ); + box-shadow: inset 0 1px 0 rgba( 0, 0, 0, 0.1 ); + vertical-align: top; +} + +.wp-core-ui .button-primary[disabled], +.wp-core-ui .button-primary:disabled, +.wp-core-ui .button-primary-disabled, +.wp-core-ui .button-primary.disabled { + color: #94cde7 !important; + background: #298cba !important; + border-color: #1b607f !important; + -webkit-box-shadow: none !important; + box-shadow: none !important; + text-shadow: 0 -1px 0 rgba( 0, 0, 0, 0.1 ) !important; + cursor: default; +} + +/* ---------------------------------------------------------------------------- + 4.0 - Button Groups +---------------------------------------------------------------------------- */ + +.wp-core-ui .button-group { + position: relative; + display: inline-block; + white-space: nowrap; + font-size: 0; + vertical-align: middle; +} + +.wp-core-ui .button-group > .button { + display: inline-block; + -webkit-border-radius: 0; + border-radius: 0; + margin-left: -1px; + z-index: 10; +} + +.wp-core-ui .button-group > .button-primary { + z-index: 100; +} + +.wp-core-ui .button-group > .button:hover { + z-index: 20; +} + +.wp-core-ui .button-group > .button:first-child { + -webkit-border-radius: 0 3px 3px 0; + border-radius: 0 3px 3px 0; +} + +.wp-core-ui .button-group > .button:last-child { + -webkit-border-radius: 3px 0 0 3px; + border-radius: 3px 0 0 3px; +} + +.wp-core-ui .button-group > .button:focus { + position: relative; + z-index: 1; +} + +/* ---------------------------------------------------------------------------- + 5.0 - Responsive Button Styles +---------------------------------------------------------------------------- */ + +@media screen and ( max-width: 782px ) { + + .wp-core-ui .button, + .wp-core-ui .button.button-large, + .wp-core-ui .button.button-small, + input#publish, + input#save-post, + a.preview { + padding: 6px 14px; + line-height: normal; + font-size: 14px; + vertical-align: middle; + height: auto; + margin-bottom: 4px; + } + + #media-upload.wp-core-ui .button { + padding: 0 10px 1px; + height: 24px; + line-height: 22px; + font-size: 13px; + } + + .media-frame.mode-grid .bulk-select .button { + margin-bottom: 0; + } + + /* Publish Metabox Options */ + .wp-core-ui .save-post-status.button { + position: relative; + margin: 0 10px 0 14px; /* 14px right margin to match all other buttons */ + } + + /* Reset responsive styles in Press This, Customizer */ + + .wp-core-ui.wp-customizer .button, + .press-this.wp-core-ui .button, + .press-this input#publish, + .press-this input#save-post, + .press-this a.preview { + padding: 0 10px 1px; + font-size: 13px; + line-height: 26px; + height: 28px; + margin: 0; + vertical-align: inherit; + } + + /* Reset responsive styles on Log in button on iframed login form */ + + .interim-login .button.button-large { + height: 30px; + line-height: 28px; + padding: 0 12px 2px; + } + +} diff --git a/wp-includes/css/buttons-rtl.min.css b/wp-includes/css/buttons-rtl.min.css new file mode 100644 index 0000000..f37e371 --- /dev/null +++ b/wp-includes/css/buttons-rtl.min.css @@ -0,0 +1 @@ +.wp-core-ui .button,.wp-core-ui .button-primary,.wp-core-ui .button-secondary{display:inline-block;text-decoration:none;font-size:13px;line-height:26px;height:28px;margin:0;padding:0 10px 1px;cursor:pointer;border-width:1px;border-style:solid;-webkit-appearance:none;-webkit-border-radius:3px;border-radius:3px;white-space:nowrap;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.wp-core-ui button::-moz-focus-inner,.wp-core-ui input[type=button]::-moz-focus-inner,.wp-core-ui input[type=reset]::-moz-focus-inner,.wp-core-ui input[type=submit]::-moz-focus-inner{border-width:0;border-style:none;padding:0}.wp-core-ui .button-group.button-large .button,.wp-core-ui .button.button-large{height:30px;line-height:28px;padding:0 12px 2px}.wp-core-ui .button-group.button-small .button,.wp-core-ui .button.button-small{height:24px;line-height:22px;padding:0 8px 1px;font-size:11px}.wp-core-ui .button-group.button-hero .button,.wp-core-ui .button.button-hero{font-size:14px;height:46px;line-height:44px;padding:0 36px}.wp-core-ui .button:active,.wp-core-ui .button:focus{outline:0}.wp-core-ui .button.hidden{display:none}.wp-core-ui input[type=reset],.wp-core-ui input[type=reset]:active,.wp-core-ui input[type=reset]:focus,.wp-core-ui input[type=reset]:hover{background:0 0;border:none;-webkit-box-shadow:none;box-shadow:none;padding:0 2px 1px;width:auto}.wp-core-ui .button,.wp-core-ui .button-secondary{color:#555;border-color:#ccc;background:#f7f7f7;-webkit-box-shadow:inset 0 1px 0 #fff,0 1px 0 rgba(0,0,0,.08);box-shadow:inset 0 1px 0 #fff,0 1px 0 rgba(0,0,0,.08);vertical-align:top}.wp-core-ui p .button{vertical-align:baseline}.wp-core-ui .button-secondary:focus,.wp-core-ui .button-secondary:hover,.wp-core-ui .button.focus,.wp-core-ui .button.hover,.wp-core-ui .button:focus,.wp-core-ui .button:hover{background:#fafafa;border-color:#999;color:#222}.wp-core-ui .button-secondary:focus,.wp-core-ui .button.focus,.wp-core-ui .button:focus{-webkit-box-shadow:0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8);box-shadow:0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8)}.wp-core-ui .button-secondary:active,.wp-core-ui .button.active,.wp-core-ui .button.active:hover,.wp-core-ui .button:active{background:#eee;border-color:#999;color:#333;-webkit-box-shadow:inset 0 2px 5px -3px rgba(0,0,0,.5);box-shadow:inset 0 2px 5px -3px rgba(0,0,0,.5)}.wp-core-ui .button.active:focus{-webkit-box-shadow:inset 0 2px 5px -3px rgba(0,0,0,.5),0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8);box-shadow:inset 0 2px 5px -3px rgba(0,0,0,.5),0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8)}.wp-core-ui .button-disabled,.wp-core-ui .button-secondary.disabled,.wp-core-ui .button-secondary:disabled,.wp-core-ui .button-secondary[disabled],.wp-core-ui .button.disabled,.wp-core-ui .button:disabled,.wp-core-ui .button[disabled]{color:#aaa!important;border-color:#ddd!important;background:#f7f7f7!important;-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:0 1px 0 #fff!important;cursor:default}.wp-core-ui .button-primary{background:#2ea2cc;border-color:#0074a2;-webkit-box-shadow:inset 0 1px 0 rgba(120,200,230,.5),0 1px 0 rgba(0,0,0,.15);box-shadow:inset 0 1px 0 rgba(120,200,230,.5),0 1px 0 rgba(0,0,0,.15);color:#fff;text-decoration:none}.wp-core-ui .button-primary.focus,.wp-core-ui .button-primary.hover,.wp-core-ui .button-primary:focus,.wp-core-ui .button-primary:hover{background:#1e8cbe;border-color:#0074a2;-webkit-box-shadow:inset 0 1px 0 rgba(120,200,230,.6);box-shadow:inset 0 1px 0 rgba(120,200,230,.6);color:#fff}.wp-core-ui .button-primary.focus,.wp-core-ui .button-primary:focus{border-color:#0e3950;-webkit-box-shadow:inset 0 1px 0 rgba(120,200,230,.6),0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8);box-shadow:inset 0 1px 0 rgba(120,200,230,.6),0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8)}.wp-core-ui .button-primary.active,.wp-core-ui .button-primary.active:focus,.wp-core-ui .button-primary.active:hover,.wp-core-ui .button-primary:active{background:#1b7aa6;border-color:#005684;color:rgba(255,255,255,.95);-webkit-box-shadow:inset 0 1px 0 rgba(0,0,0,.1);box-shadow:inset 0 1px 0 rgba(0,0,0,.1);vertical-align:top}.wp-core-ui .button-primary-disabled,.wp-core-ui .button-primary.disabled,.wp-core-ui .button-primary:disabled,.wp-core-ui .button-primary[disabled]{color:#94cde7!important;background:#298cba!important;border-color:#1b607f!important;-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:0 -1px 0 rgba(0,0,0,.1)!important;cursor:default}.wp-core-ui .button-group{position:relative;display:inline-block;white-space:nowrap;font-size:0;vertical-align:middle}.wp-core-ui .button-group>.button{display:inline-block;-webkit-border-radius:0;border-radius:0;margin-left:-1px;z-index:10}.wp-core-ui .button-group>.button-primary{z-index:100}.wp-core-ui .button-group>.button:hover{z-index:20}.wp-core-ui .button-group>.button:first-child{-webkit-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.wp-core-ui .button-group>.button:last-child{-webkit-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.wp-core-ui .button-group>.button:focus{position:relative;z-index:1}@media screen and (max-width:782px){.wp-core-ui .button,.wp-core-ui .button.button-large,.wp-core-ui .button.button-small,a.preview,input#publish,input#save-post{padding:6px 14px;line-height:normal;font-size:14px;vertical-align:middle;height:auto;margin-bottom:4px}#media-upload.wp-core-ui .button{padding:0 10px 1px;height:24px;line-height:22px;font-size:13px}.media-frame.mode-grid .bulk-select .button{margin-bottom:0}.wp-core-ui .save-post-status.button{position:relative;margin:0 10px 0 14px}.press-this a.preview,.press-this input#publish,.press-this input#save-post,.press-this.wp-core-ui .button,.wp-core-ui.wp-customizer .button{padding:0 10px 1px;font-size:13px;line-height:26px;height:28px;margin:0;vertical-align:inherit}.interim-login .button.button-large{height:30px;line-height:28px;padding:0 12px 2px}} \ No newline at end of file diff --git a/wp-includes/css/buttons.css b/wp-includes/css/buttons.css new file mode 100644 index 0000000..e84a0b5 --- /dev/null +++ b/wp-includes/css/buttons.css @@ -0,0 +1,364 @@ +/* ---------------------------------------------------------------------------- + +NOTE: If you edit this file, you should make sure that the CSS rules for +buttons in the following files are updated. + +* jquery-ui-dialog.css +* editor.css + +WordPress-style Buttons +======================= +Create a button by adding the `.button` class to an element. For backwards +compatibility, we support several other classes (such as `.button-secondary`), +but these will *not* work with the stackable classes described below. + +Button Styles +------------- +To display a primary button style, add the `.button-primary` class to a button. + +Button Sizes +------------ +Adjust a button's size by adding the `.button-large` or `.button-small` class. + +Button States +------------- +Lock the state of a button by adding the name of the pseudoclass as +an actual class (e.g. `.hover` for `:hover`). + + +TABLE OF CONTENTS: +------------------ + 1.0 - Button Layouts + 2.0 - Default Button Style + 3.0 - Primary Button Style + 4.0 - Button Groups + 5.0 - Responsive Button Styles + +---------------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------------- + 1.0 - Button Layouts +---------------------------------------------------------------------------- */ + +.wp-core-ui .button, +.wp-core-ui .button-primary, +.wp-core-ui .button-secondary { + display: inline-block; + text-decoration: none; + font-size: 13px; + line-height: 26px; + height: 28px; + margin: 0; + padding: 0 10px 1px; + cursor: pointer; + border-width: 1px; + border-style: solid; + -webkit-appearance: none; + -webkit-border-radius: 3px; + border-radius: 3px; + white-space: nowrap; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +/* Remove the dotted border on :focus and the extra padding in Firefox */ +.wp-core-ui button::-moz-focus-inner, +.wp-core-ui input[type="reset"]::-moz-focus-inner, +.wp-core-ui input[type="button"]::-moz-focus-inner, +.wp-core-ui input[type="submit"]::-moz-focus-inner { + border-width: 0; + border-style: none; + padding: 0; +} + +.wp-core-ui .button.button-large, +.wp-core-ui .button-group.button-large .button { + height: 30px; + line-height: 28px; + padding: 0 12px 2px; +} + +.wp-core-ui .button.button-small, +.wp-core-ui .button-group.button-small .button { + height: 24px; + line-height: 22px; + padding: 0 8px 1px; + font-size: 11px; +} + +.wp-core-ui .button.button-hero, +.wp-core-ui .button-group.button-hero .button { + font-size: 14px; + height: 46px; + line-height: 44px; + padding: 0 36px; +} + +.wp-core-ui .button:active, +.wp-core-ui .button:focus { + outline: none; +} + +.wp-core-ui .button.hidden { + display: none; +} + +/* Style Reset buttons as simple text links */ + +.wp-core-ui input[type="reset"], +.wp-core-ui input[type="reset"]:hover, +.wp-core-ui input[type="reset"]:active, +.wp-core-ui input[type="reset"]:focus { + background: none; + border: none; + -webkit-box-shadow: none; + box-shadow: none; + padding: 0 2px 1px; + width: auto; +} + +/* ---------------------------------------------------------------------------- + 2.0 - Default Button Style +---------------------------------------------------------------------------- */ + +.wp-core-ui .button, +.wp-core-ui .button-secondary { + color: #555; + border-color: #cccccc; + background: #f7f7f7; + -webkit-box-shadow: inset 0 1px 0 #fff, 0 1px 0 rgba( 0, 0, 0, 0.08 ); + box-shadow: inset 0 1px 0 #fff, 0 1px 0 rgba( 0, 0, 0, 0.08 ); + vertical-align: top; +} + +.wp-core-ui p .button { + vertical-align: baseline; +} + +.wp-core-ui .button.hover, +.wp-core-ui .button:hover, +.wp-core-ui .button-secondary:hover, +.wp-core-ui .button.focus, +.wp-core-ui .button:focus, +.wp-core-ui .button-secondary:focus { + background: #fafafa; + border-color: #999; + color: #222; +} + +.wp-core-ui .button.focus, +.wp-core-ui .button:focus, +.wp-core-ui .button-secondary:focus { + -webkit-box-shadow: + 0 0 0 1px #5b9dd9, + 0 0 2px 1px rgba(30, 140, 190, .8); + box-shadow: + 0 0 0 1px #5b9dd9, + 0 0 2px 1px rgba(30, 140, 190, .8); +} + +.wp-core-ui .button.active, +.wp-core-ui .button.active:hover, +.wp-core-ui .button:active, +.wp-core-ui .button-secondary:active { + background: #eee; + border-color: #999; + color: #333; + -webkit-box-shadow: inset 0 2px 5px -3px rgba( 0, 0, 0, 0.5 ); + box-shadow: inset 0 2px 5px -3px rgba( 0, 0, 0, 0.5 ); +} + +.wp-core-ui .button.active:focus { + -webkit-box-shadow: + inset 0 2px 5px -3px rgba( 0, 0, 0, 0.5 ), + 0 0 0 1px #5b9dd9, + 0 0 2px 1px rgba(30, 140, 190, .8); + box-shadow: + inset 0 2px 5px -3px rgba( 0, 0, 0, 0.5 ), + 0 0 0 1px #5b9dd9, + 0 0 2px 1px rgba(30, 140, 190, .8); +} + +.wp-core-ui .button[disabled], +.wp-core-ui .button:disabled, +.wp-core-ui .button.disabled, +.wp-core-ui .button-secondary[disabled], +.wp-core-ui .button-secondary:disabled, +.wp-core-ui .button-secondary.disabled, +.wp-core-ui .button-disabled { + color: #aaa !important; + border-color: #ddd !important; + background: #f7f7f7 !important; + -webkit-box-shadow: none !important; + box-shadow: none !important; + text-shadow: 0 1px 0 #fff !important; + cursor: default; +} + +/* ---------------------------------------------------------------------------- + 3.0 - Primary Button Style +---------------------------------------------------------------------------- */ + +.wp-core-ui .button-primary { + background: #2ea2cc; + border-color: #0074a2; + -webkit-box-shadow: inset 0 1px 0 rgba( 120, 200, 230, 0.5), 0 1px 0 rgba( 0, 0, 0, 0.15 ); + box-shadow: inset 0 1px 0 rgba( 120, 200, 230, 0.5 ), 0 1px 0 rgba( 0, 0, 0, 0.15 ); + color: #fff; + text-decoration: none; +} + +.wp-core-ui .button-primary.hover, +.wp-core-ui .button-primary:hover, +.wp-core-ui .button-primary.focus, +.wp-core-ui .button-primary:focus { + background: #1e8cbe; + border-color: #0074a2; + -webkit-box-shadow: inset 0 1px 0 rgba( 120, 200, 230, 0.6 ); + box-shadow: inset 0 1px 0 rgba( 120, 200, 230, 0.6 ); + color: #fff; +} + +.wp-core-ui .button-primary.focus, +.wp-core-ui .button-primary:focus { + border-color: #0e3950; + -webkit-box-shadow: + inset 0 1px 0 rgba( 120, 200, 230, 0.6 ), + 0 0 0 1px #5b9dd9, + 0 0 2px 1px rgba(30, 140, 190, .8); + box-shadow: + inset 0 1px 0 rgba( 120, 200, 230, 0.6 ), + 0 0 0 1px #5b9dd9, + 0 0 2px 1px rgba(30, 140, 190, .8); +} + +.wp-core-ui .button-primary.active, +.wp-core-ui .button-primary.active:hover, +.wp-core-ui .button-primary.active:focus, +.wp-core-ui .button-primary:active { + background: #1b7aa6; + border-color: #005684; + color: rgba( 255, 255, 255, 0.95 ); + -webkit-box-shadow: inset 0 1px 0 rgba( 0, 0, 0, 0.1 ); + box-shadow: inset 0 1px 0 rgba( 0, 0, 0, 0.1 ); + vertical-align: top; +} + +.wp-core-ui .button-primary[disabled], +.wp-core-ui .button-primary:disabled, +.wp-core-ui .button-primary-disabled, +.wp-core-ui .button-primary.disabled { + color: #94cde7 !important; + background: #298cba !important; + border-color: #1b607f !important; + -webkit-box-shadow: none !important; + box-shadow: none !important; + text-shadow: 0 -1px 0 rgba( 0, 0, 0, 0.1 ) !important; + cursor: default; +} + +/* ---------------------------------------------------------------------------- + 4.0 - Button Groups +---------------------------------------------------------------------------- */ + +.wp-core-ui .button-group { + position: relative; + display: inline-block; + white-space: nowrap; + font-size: 0; + vertical-align: middle; +} + +.wp-core-ui .button-group > .button { + display: inline-block; + -webkit-border-radius: 0; + border-radius: 0; + margin-right: -1px; + z-index: 10; +} + +.wp-core-ui .button-group > .button-primary { + z-index: 100; +} + +.wp-core-ui .button-group > .button:hover { + z-index: 20; +} + +.wp-core-ui .button-group > .button:first-child { + -webkit-border-radius: 3px 0 0 3px; + border-radius: 3px 0 0 3px; +} + +.wp-core-ui .button-group > .button:last-child { + -webkit-border-radius: 0 3px 3px 0; + border-radius: 0 3px 3px 0; +} + +.wp-core-ui .button-group > .button:focus { + position: relative; + z-index: 1; +} + +/* ---------------------------------------------------------------------------- + 5.0 - Responsive Button Styles +---------------------------------------------------------------------------- */ + +@media screen and ( max-width: 782px ) { + + .wp-core-ui .button, + .wp-core-ui .button.button-large, + .wp-core-ui .button.button-small, + input#publish, + input#save-post, + a.preview { + padding: 6px 14px; + line-height: normal; + font-size: 14px; + vertical-align: middle; + height: auto; + margin-bottom: 4px; + } + + #media-upload.wp-core-ui .button { + padding: 0 10px 1px; + height: 24px; + line-height: 22px; + font-size: 13px; + } + + .media-frame.mode-grid .bulk-select .button { + margin-bottom: 0; + } + + /* Publish Metabox Options */ + .wp-core-ui .save-post-status.button { + position: relative; + margin: 0 14px 0 10px; /* 14px right margin to match all other buttons */ + } + + /* Reset responsive styles in Press This, Customizer */ + + .wp-core-ui.wp-customizer .button, + .press-this.wp-core-ui .button, + .press-this input#publish, + .press-this input#save-post, + .press-this a.preview { + padding: 0 10px 1px; + font-size: 13px; + line-height: 26px; + height: 28px; + margin: 0; + vertical-align: inherit; + } + + /* Reset responsive styles on Log in button on iframed login form */ + + .interim-login .button.button-large { + height: 30px; + line-height: 28px; + padding: 0 12px 2px; + } + +} diff --git a/wp-includes/css/buttons.min.css b/wp-includes/css/buttons.min.css new file mode 100644 index 0000000..9680480 --- /dev/null +++ b/wp-includes/css/buttons.min.css @@ -0,0 +1 @@ +.wp-core-ui .button,.wp-core-ui .button-primary,.wp-core-ui .button-secondary{display:inline-block;text-decoration:none;font-size:13px;line-height:26px;height:28px;margin:0;padding:0 10px 1px;cursor:pointer;border-width:1px;border-style:solid;-webkit-appearance:none;-webkit-border-radius:3px;border-radius:3px;white-space:nowrap;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.wp-core-ui button::-moz-focus-inner,.wp-core-ui input[type=button]::-moz-focus-inner,.wp-core-ui input[type=reset]::-moz-focus-inner,.wp-core-ui input[type=submit]::-moz-focus-inner{border-width:0;border-style:none;padding:0}.wp-core-ui .button-group.button-large .button,.wp-core-ui .button.button-large{height:30px;line-height:28px;padding:0 12px 2px}.wp-core-ui .button-group.button-small .button,.wp-core-ui .button.button-small{height:24px;line-height:22px;padding:0 8px 1px;font-size:11px}.wp-core-ui .button-group.button-hero .button,.wp-core-ui .button.button-hero{font-size:14px;height:46px;line-height:44px;padding:0 36px}.wp-core-ui .button:active,.wp-core-ui .button:focus{outline:0}.wp-core-ui .button.hidden{display:none}.wp-core-ui input[type=reset],.wp-core-ui input[type=reset]:active,.wp-core-ui input[type=reset]:focus,.wp-core-ui input[type=reset]:hover{background:0 0;border:none;-webkit-box-shadow:none;box-shadow:none;padding:0 2px 1px;width:auto}.wp-core-ui .button,.wp-core-ui .button-secondary{color:#555;border-color:#ccc;background:#f7f7f7;-webkit-box-shadow:inset 0 1px 0 #fff,0 1px 0 rgba(0,0,0,.08);box-shadow:inset 0 1px 0 #fff,0 1px 0 rgba(0,0,0,.08);vertical-align:top}.wp-core-ui p .button{vertical-align:baseline}.wp-core-ui .button-secondary:focus,.wp-core-ui .button-secondary:hover,.wp-core-ui .button.focus,.wp-core-ui .button.hover,.wp-core-ui .button:focus,.wp-core-ui .button:hover{background:#fafafa;border-color:#999;color:#222}.wp-core-ui .button-secondary:focus,.wp-core-ui .button.focus,.wp-core-ui .button:focus{-webkit-box-shadow:0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8);box-shadow:0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8)}.wp-core-ui .button-secondary:active,.wp-core-ui .button.active,.wp-core-ui .button.active:hover,.wp-core-ui .button:active{background:#eee;border-color:#999;color:#333;-webkit-box-shadow:inset 0 2px 5px -3px rgba(0,0,0,.5);box-shadow:inset 0 2px 5px -3px rgba(0,0,0,.5)}.wp-core-ui .button.active:focus{-webkit-box-shadow:inset 0 2px 5px -3px rgba(0,0,0,.5),0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8);box-shadow:inset 0 2px 5px -3px rgba(0,0,0,.5),0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8)}.wp-core-ui .button-disabled,.wp-core-ui .button-secondary.disabled,.wp-core-ui .button-secondary:disabled,.wp-core-ui .button-secondary[disabled],.wp-core-ui .button.disabled,.wp-core-ui .button:disabled,.wp-core-ui .button[disabled]{color:#aaa!important;border-color:#ddd!important;background:#f7f7f7!important;-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:0 1px 0 #fff!important;cursor:default}.wp-core-ui .button-primary{background:#2ea2cc;border-color:#0074a2;-webkit-box-shadow:inset 0 1px 0 rgba(120,200,230,.5),0 1px 0 rgba(0,0,0,.15);box-shadow:inset 0 1px 0 rgba(120,200,230,.5),0 1px 0 rgba(0,0,0,.15);color:#fff;text-decoration:none}.wp-core-ui .button-primary.focus,.wp-core-ui .button-primary.hover,.wp-core-ui .button-primary:focus,.wp-core-ui .button-primary:hover{background:#1e8cbe;border-color:#0074a2;-webkit-box-shadow:inset 0 1px 0 rgba(120,200,230,.6);box-shadow:inset 0 1px 0 rgba(120,200,230,.6);color:#fff}.wp-core-ui .button-primary.focus,.wp-core-ui .button-primary:focus{border-color:#0e3950;-webkit-box-shadow:inset 0 1px 0 rgba(120,200,230,.6),0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8);box-shadow:inset 0 1px 0 rgba(120,200,230,.6),0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8)}.wp-core-ui .button-primary.active,.wp-core-ui .button-primary.active:focus,.wp-core-ui .button-primary.active:hover,.wp-core-ui .button-primary:active{background:#1b7aa6;border-color:#005684;color:rgba(255,255,255,.95);-webkit-box-shadow:inset 0 1px 0 rgba(0,0,0,.1);box-shadow:inset 0 1px 0 rgba(0,0,0,.1);vertical-align:top}.wp-core-ui .button-primary-disabled,.wp-core-ui .button-primary.disabled,.wp-core-ui .button-primary:disabled,.wp-core-ui .button-primary[disabled]{color:#94cde7!important;background:#298cba!important;border-color:#1b607f!important;-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:0 -1px 0 rgba(0,0,0,.1)!important;cursor:default}.wp-core-ui .button-group{position:relative;display:inline-block;white-space:nowrap;font-size:0;vertical-align:middle}.wp-core-ui .button-group>.button{display:inline-block;-webkit-border-radius:0;border-radius:0;margin-right:-1px;z-index:10}.wp-core-ui .button-group>.button-primary{z-index:100}.wp-core-ui .button-group>.button:hover{z-index:20}.wp-core-ui .button-group>.button:first-child{-webkit-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.wp-core-ui .button-group>.button:last-child{-webkit-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.wp-core-ui .button-group>.button:focus{position:relative;z-index:1}@media screen and (max-width:782px){.wp-core-ui .button,.wp-core-ui .button.button-large,.wp-core-ui .button.button-small,a.preview,input#publish,input#save-post{padding:6px 14px;line-height:normal;font-size:14px;vertical-align:middle;height:auto;margin-bottom:4px}#media-upload.wp-core-ui .button{padding:0 10px 1px;height:24px;line-height:22px;font-size:13px}.media-frame.mode-grid .bulk-select .button{margin-bottom:0}.wp-core-ui .save-post-status.button{position:relative;margin:0 14px 0 10px}.press-this a.preview,.press-this input#publish,.press-this input#save-post,.press-this.wp-core-ui .button,.wp-core-ui.wp-customizer .button{padding:0 10px 1px;font-size:13px;line-height:26px;height:28px;margin:0;vertical-align:inherit}.interim-login .button.button-large{height:30px;line-height:28px;padding:0 12px 2px}} \ No newline at end of file diff --git a/wp-includes/css/dashicons.css b/wp-includes/css/dashicons.css new file mode 100644 index 0000000..03f7aad --- /dev/null +++ b/wp-includes/css/dashicons.css @@ -0,0 +1,957 @@ +@font-face { + font-family: "dashicons"; + src: url(../fonts/dashicons.eot); +} + +@font-face { + font-family: "dashicons"; + src: url(data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAGBQAA4AAAAAm3wAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAABRAAAABwAAAAcbYyDmkdERUYAAAFgAAAAHgAAACABIwAET1MvMgAAAYAAAABAAAAAYJYFaatjbWFwAAABwAAAASoAAAKC/cQq02dhc3AAAALsAAAACAAAAAj//wADZ2x5ZgAAAvQAAFSXAACESOAO2gZoZWFkAABXjAAAAC4AAAA2CEgozmhoZWEAAFe8AAAAGgAAACQPogifaG10eAAAV9gAAAEcAAAD2GOq3ltsb2NhAABY9AAAAe4AAAHu4pbA6m1heHAAAFrkAAAAHwAAACABSQC1bmFtZQAAWwQAAAGKAAADLCbHbA5wb3N0AABckAAAA7UAAAmnz3C/rndlYmYAAGBIAAAABgAAAAY81VSHAAAAAQAAAADMPaLPAAAAANCh83cAAAAA0KztU3jaY2BkYGDgA2IJBhBgYmAEwq9AzALmMQAADtEBKAAAeNpjYGY/yTiBgZWBhVWEZQMDA8M0CM20h8GIKQLIB0phB6He4X4MDqp/vqqzXwDxgaQGkGJEUqLAwAgAMrcKp3ja3ZA7SwNRFITPNTGy7N3jglosWCxIkHRBVAw2q6JJQEWMILGQ9dHEKjZCwCKNhZ2t/8hGG1EwWKuVOvehjbouCVhYWzlw5jAwfMUQUYZ6N0oidRLHaRLdnBVx+jcoon4azn/AwRACjKGAIqZRwgIWUUEdO2ighRMllKMCVVAlFal57ehAF3RJV03VbJq6iU3DtMypObdZ69jAjicJUUol+BhBiHxKncAMopRaRg0x9nCItiKVUb4KVbFLFdrXoS7qyFTMWpe6a5qmbc4s2Zz1bZgknYHBLH/xJ7/zG7/yCz/zEz/yA9/zLd/wFV/wKq9wmed4lqd40jvymt6Bt+9ty1huybqsyXW5LJdk5HbcO/favewt8/cSOfpBi77U+n4X6N/rG5Q9gGkAAAAAAAH//wACeNqsvQd8FGX6OD7vzM7Mbtpmsy1tN9lsS9nUbSFlEyD00EKLBaQsPWAUaRJQMSIqJTZsiA0Re1TkLBxnO107clFPDz3Uk+PUO/WOrwdJ9vX3PO/sJhvE+973//lnM/O+887MO++85enPM5zIwR85xR/iBE7iNFwKp+W4Sp1NJ+hterOO2JKJTk9O9T5CH4s8QneQ6Y9EHuEPRZvIDdzPtOdnQqMfcj8TD+W5nznCJfxVchzPhbmo/ILUC3X6OU5DgiFiMluJ2SoEghoiS4Y8YjLIabwswc7Kh0gwEAzxwUAVlFcFxf3R+l1Z626pKX1gWknd7JYVNdGHovVPWSzLLJacUYtNI+y+KRXy2CWXXOIr8KY2+3It0+DUNIuwiX91V26q3WXdUpaZa0slKdGH+FefYmenW3ICY9Mr7b5LLlkyVq6Y7Cto0q0cmcPq5AjxcBGpUVZzGdAntiqTUWeQSgjR2Qtcfp0vQE4JnS1r1rREaEoEUlm9piXa1LKGppBTa1r4Qy1r4LUF7t9QxyfSR9CXaawes6whbg3xe0UC3Yqb1HjHsuj66Ppl/I+kKxLdz7f2TRHyabtq0x3L+GtZOW2P3Bl9OPooP4NqySmaAvVGuF1ym1zDZXIurg7qNaVrSRpxkwYS8LldBVpCXAHI52vYsSQTyWA2BWRikrREyne70htIiJhZaZX0j02b7mj5chmRmpsbGhqebphIn7AMa7n9CrKw/1velJdX0JoX/RaTMrLHiifo7iuUW+j8xsaGp6VDWHJ7yzArESc2QwWNjc30iWVfttyxqW8WVHB+Xj5vjX7D0jfIPezEpk1k0Sa4xULnNTzd2MBxKpgfYXinNk7P5XBFOEeIzucqIW6bbC+QoPdNXltVgKsyGaQCl0+06Vga8FaZdTbo0IFD0VBb9NA1tP2ah4pqa4uEk0W10cYjN9105CbhIDkFyU1LzDm0B/pUyQud/KGi2kiktijaBDfwz2DxEVXfiSV4j0jOUHkpvZ8dcPzAnBBgRDkcPtFmtOm8fKtwsu+AkN+fGSGnREPvd+GIaMDx/4Y7If1FOsmp4Z0cOOvdJICzXgwSOY3YbbJU4Hb5GuBNAxrljEzEPHroRdKUfaHLndH/+gXCNWurvp1HOzo8HR6Pp6NekEhTFp7LpoekXOVSvdt1wdvt/RumV42Ai0o2ejpI59xTdC+cyoBT2fRFWH8R7nUpKvVzRs4GbZdUboI9FiSujGDAYdbwJgkXGps/sBqlzgX0H5dHvxp/9Jbx/Dafb8EcolpD/0yyibWoune3dbrFarVMt1qEnxf4fNHLxt9ydBxvvZxkLJhT7aF/pidI3uqfo3kWS+xCTiAe4pHV0l5Y/yXcGGV07bbBkSVeHNoSYsSBrCd2HM1a7BhYevEjPx5Z4leyIffL6tqi3u9wrEVDUW1/ZhizYUuxZSBDTp2jrEvVjfm+KTjoltqW2l1Gi8W4CzK8C3P0/sSy6KeYY+CR6+Lul0fLrTCiHIFhdEm49MwydKcKupK4g6YAri6V4SDf8ZtJr4ya0KT3/eZHejJAPwy85SfmH6GwacIovXT/weg1B336URNGvTz54I/0b4G3AqQULvgnK2x6BeabAebbIZhvmVwFPLugTFVOXCHBWwXzJE0UnJBaSJrKXlDGl0PnhIhXZbpwasCTpRMESSppWBRavLUsjah0mSX+TST9sj9s1Pof3PvlwkVfPHGj233ZB5ervK76cePq9frg8gUTStc8MlvOGjN8yjD614Nbjq44I2rd1kyzrSAl+Uz7e5sVGB6W1dAeN1cOb2+XZHhvSTba/C637A663HadNxB0B82BoN9mNJmDZtlk9nJVAZ+rQDLI6k+zg3cevbN1Me1Z3Hpnz53BzE8/MQ+DkhltxMNKhpk/6a863d19ulvIXz4TzsAln2YG4UzrYlK0cDaUBHJiJbMX0uHdeC1rV0QukKcCjIVR4UwZgEVUbkEUbG5lemdUBfysDVLDS6NGzW2nb259kX64i/aEhZwxlavmEr585aRJKyfJZS9RSn+e2+5vpM/m0W0RgdxAGlbNHRU9Pqk6CFfgs0ih3AbzWIcrmlSpzHIZzE9J9mqIN5Dh0Bc43EErzNFA0K4h9jRBOHkn0c1/NPTCsrt/LDhJ76f3H8l8hZQ+fhf9YaCUzCFzjmRsOPi9tHdT+6stc1+7lbaTrv0kY09vZ6yAdNH2TZ/dO5tDWBmB91XWUhas5qJfriYuX2WWVPkOd0DF3ttURQBS+eHH1stWtl7WwXoJkhbi27CBvkOfoO9s2MA/uGbvmjV7Yf56ercSj5CPoA0v7s+EvTBuz44de2A7jhetie4Ph8PQnlQuAmu7DeaFFqC3GcYgD2ZICcyRIFfLNcBqn3CO9W7T2fTQJG9si0P2ILTTCeeGHMN5VcKx3Abv4GDvcKyottfBQC7xKP99K4otsMIj2GF8KyLM/mhCyaGhV5AuBBsxEKL6BAo8EfZHUyzsqnAYcLonnocTsSy8N+KDsLSX0QjWoVSCLUYluIOwMGCVwFIAiuFkjGKgPZAR9vb2huP/SD/0H4/TD0J+y5r+JxJOIy7hYMyPwLNk6OU8xCV6ux5oCQcb3YBPcJrMLnuBSnY57AVyICicjJrKIhW3CCeFk/3DW2oBioX50vZMX9Eh+vLs2fTlQ4X+zHbhJCnuO6haXYt9EWavFv39n299Vn5+9+7n5WdvVXCePFJeA9SgDikYINIQ8dmAfkN0Jkt3bLdYaEvfAVXJAeu06F/5rB3Rk2Pk1u1AY9Gp/cPEYwcspP8mPnNH9K89HNCV8AfvcQRmip1rhZW7jGuHonS+jDjyVWmET8+wEpi5IQJz2JSRzuM0dkFpgNGMLlmyF7gDDi8sPJdkNAAFaTKbAGkAzRMiCHjZFS4AlJIpj+g1JKRyw3leQ1y830f0eLt8ZOHzP9H36Qv0/Z+eXwh5UklGk8qfnu97nawkE3/cseNH+gy9iT6DOVJP319qSNMbr5pu068k89+7k5jbfLPNpmxRqGrIz6c/Gl1qtUFrMGyebl9qdxggO72dPEdEQW3SyElzPuw9Rja99T9C8derNy5eLIxSHrXwrCaIs4Y8dCJrSF+R4Ffz3nKSRPa9snKBPGG8ZXhhkVqctaXueEsL/w+iVgl80EeSJV4g/iBR0yPRXP55Oaul5cphj/zhT3SbcFPfyQXk2m+foW9HdxZbOTE2Z9tgRslcMluxHK5FAhB7yOYR1/VuVTX3Hwe6hm18a3S/3Hb6tLgO1kTP4AYwcQbpkkU5DWiLEqjN5y4DvJgRdKepzAgJgwHnL4uEmWOrHh+X9d59D8x/ds+KqvTsrx65bd584clzlfIfbb6wwZW+kwyf+pfyLQfePXPlH/pbW3edqxCnlzhAS6qA8krh0vEd9UBv2wgAErbBC2qIDZBZdH/fFOJRdfdNUXVH90f3C/kIMeS2vinR/eIxeMF2BACYklNYN59Ap7oQBxZIlkGuwMtAwAAc0JFYuaq5Zep1tAc5hRa+tWUNbggJesKQiut2jH+O9hDPmhZyCstpO2wpAAwGymDJMByHz9UAvNVBT2dxFi4f3wuf5oSHiIJXb7MLXhLfYJlqeCBYi2pPn64lz0fCAKbDkTDQQ/EdAvlTyrjWFp15iZdCRbVhfOzgBnTtyf5MoZO203ZG/yjvH0YYRJwBIMfgVfNd6YwSF3Vp+MrKgS/Av6iaVFj7Lv383XdvHDexvyMj0nkjy1ZLuyNSQ0mk7x9wTAretfOrIv2dVz7G8kJtoT+BH9AyjmB4/E0Nkj0OXOH9uDh284Vk78C5KqsqD9Z9lZU3fyGl+caN8535J+zFf/du5Q8pCO5+IiYZLWU1LRUKbiuaWOOzZ0mSKr26cUn74pHeZLktdpsPqzjzEi4HoZchPtqjzm6Z0jZ7zshihheL+EOeMTNmjDGZUooWTx4OJcb4XIkwvKhn1OJZ7fcCnuPyA+mufCldPEZcnlDIc+Yd3BMXUMtd9CbS/M039MA3UmPI07egtKmpVHW3JxTp/e4b5QQ+Q4Jn7Brgm+yw/ryAczmAnVbekMYD7CzjfSE+I0aDqc5K41hWiqVCDrmT1Lx5pc935Zv0dbqIvq7k5Y7W1o5WPjsxiUos+R3DLXLbyFUPv/DlCw+vGhnPRO2t7IKE/+hmlrTX4j3AY3eRUwx/JivcVPwn7UUcibgQoJAhnmO8r8KDKfeY8S67H1kwYML8QaPdaPfb/V6gJ6S9sLj6O1TNgLIBaYdFA+D1cK9D6Iyw40gE+XPkSaS9QEcpsBBGyG/DdgiYKvCvG3g6zIjHIv2ZuFKAc2Qw5qx7Y3BUuR9THeYH6hE6sTGDa2pIlYzxx///VK8uVncRieVZvVibuK6/A2kUzLFaWWVYcazOX3vPrIT2wRr/xWsSJP/EdeI6mMmchscjAIrr6Aq6iiP8ITh3TNWtnBOPYSmCUL41ds4Qv080YCmQYjeT7ezcIfEYsMfsHJBjUIrXR/f/x3OyQvPKNdAfeYy29DLqkuEu6A+FM1RSo82IJFE9wSkuJuSx/5w6O+yVDVad58xLxAOTknTBrr9DIe74Q0pKuqCHuyJ8K/5LjdA968Kh1tZQmO17kSccuutdhQMdRqFYfEOYCW+/QHKKjyD1Q8zEToJm8Z4t0b9uoUe3fCs+MidqJjduRB79j9wfpdPSaQVjAW8xSP/4fRkOwCfCyXtJwdGOjqP0c/oS/fxoRw9ZSu6KviCdHizqOEoK7v2QLD3a1yao6RUc4/0jjG/TMv7obLqKBzJS1hCzhjDipPCjK674iP4RSJM/Yk74HU5aWE6Av3CSRRROPvESUshu6/fiqcjQy4fgTA2+l4aIRIBnESfRi0TvFPXEKZykPbAQ7r4EJ0o72Q0TsOcb8gF9ZRb1UM8s+gr5QG6L7p9Aq6JLsVb+NvLeBLKOltTQ3dHoN98A+ROu4RLwEz6LPQkoQFY/8fCtNNyOOP4SuoBv/WV12E4sZ/NPxJlFiggBCigToQ+0MAVmMowp60/kg9u4NIUHNKUzqhInYojoY7ym0WBGwYAJOUFiEl/bcvjwFptzGeDTFzQHrpl99dUvXJ1DD9uvyRDk/KespKlCbvsdPXVYvazvK/HYjXdF/9R54ezOztmBSrjotQzVBrgEAR7ZT/ZLhVIRW8eKODGoQVme/k/0EXrLe6SNPvIpmUXa3qW38GveIyvow+zwPXoLmfkpfZj7X+cDlx8i6WUkP42km841IchIkn5iy5YT9AfYn3Mi9C3fcmLgGoQ/XTG5mRnWbim02zsUEzI5T77JKOhYx+mIO0RifWgyy7DoBjjQojDivgjgYOJxuMthzZa7HarmmPSuCGV5xLPvm2/24SRktFRPtOnjjHEeXLCecRkfD6UPkYZyck3cxKFtqieJ9BxCmAE6IyR5YZiBssjDLFIYgILYbUap8bWS+vqS3lMl9SwjppTU936nau47QE4xtEfyebUlPzBsYlF/R3WLy0QIEVIzbO7agvIJRbki/1K4vqT/SqlRqaO+5DWljvqSvumqZhphOJT2JDmWXLRq2jCkx+Gl8vJU2iJXmc2MREe0AmbrlSX1wJxqEugnM6MM3AxmIj/OBYH/9ifw4xkx7I98Nv9f5BlNiQQj7CMM/Yvr+g78+B9z4dqiwbukRkYB1AKb3f6fcoljNfgenPNsaUe8bTA0fEJeoWrxofjoM33naFbiq0ii8lxEV/3HB/Mcp/Df2I4jwOsbucyh9AqyTzAPYKKY8aFAKvf0H0fZAgCdQ0Bzo7wZGAnSJeRjFlNG4LWzc9EmoGzwLMcpcnCFP06CJ2VyuchjwET0F3BGuyb+UJElKCbhD7268/aT9IeniSRNpylIaQj5AEkBENMUoNtPiseuu+Mo/dtn9HH+q96tNEU4CTRQtAlYD8Cqv/I8DTySsxurTCT2dnqWCEia1b5KGon0NP3h5O07e4uwNqyVIYUwPo0+TqZ9RsxH7yByfweCTiShsB/6O+J9qWJ9eQTGNJnpRYBYJkYFjePeadOgsEq0EfkI7YHae5BG6TvBH6L384cQCdP7oc/mqJojyJhGkDeNkK7omnAvY1Fi74RjhfSTUn8M5wOdCK9h1wkaAQYK7mM1VOGdMConabu0N/5AxAv4MCACs1FqhzI6hs4Bx0S4o3IStN/MdC+yEyC+24U/uw7GqApFEtKLkyfTb4JB3/x5Gzu346u/88LBrdcLn79Al7ywunP7js0b58/zB+FZr5BX6EeTJ16/Van7dahbHas7EISfF+sskCX8AVIRXenECFcffOEdpIy2d3bMn+8LBuk3k4XP4QETJ5NiGqIh4gn6583fuHnH9s7VL5DbGT8C3Q/ELMDhdDaDDZKsQ8GJrgwl6/kmFRMkuQrEdQ9fHa6quvphhLXR/WwhiOuu/6ggeqcnnJsrlBV8dD3A2Cy2QNjaUNaoekAaacNFqEB3JrpHOArUGbHFV6cjlvKtQmccuEcwQzxFtUDyt/d3tDN4JxxTWI5D2AqYbizB+duewFbE+KDIWTJI+7l5RiZ1RCo9lg4FAb1bUYwo5KNYAJKh4lKFkBfywwqT3K6kMboDqSLxGOP0rKjGYM/0BYK4A344xDCMQXz3moPv3tifeeO73Zulm5ZUllX97tJ93whfp71+LfLG17ymy85dcpN+wbpv9lUofUuuBZrkDwDRzShdAZ7fLMHOHRB9OOXS4FFIY3D5jmAZQVEXcON+XzCNyCb+Fb6Z/OXyy2lu9ADNvfxy4X98hbr8yqZmf2ACtZGT5K6lo37YveLv9Om/r9j9w6il4h/o0dOn6VFSdvq02tOUwvOqcV5/c3P0tn8e2vBo0ep7Hvr73x+6Z3XRoxsOKXh0kI7QM86z8ZdyXjcsOpENv13nNeI2OBoc431N+YLZquh9sLuG9ngYl2BfUJWd5wnDnyev70SexyO0Il8M03PfxGATEANNQVyxTI4buxOu3AgUKO3ZCJfn4e1RPsZO84dWZYbrkTioD2eydUEMhEiHpMOch6vGFZ0hK2LF+E+GzpSUBR77SQ63Kz0YcKiEQLrblS9L6WZTvurwDa6lQpa6oDykqrdV2h1mk0oYUVFZ5fN5K20evjQ/M9NwJ7198733LiM5JMe+aNFi+vmixYsXkQKp7Aa6/h7BJOXkl6hKbZVINZZWjBAEk9Flr7TVCQ1l+VbDNN9V95E37102YUI0ezGxLYY/+ufFixHm/YJvHMBQQjztRO6NcYWYqJoRfOCmiPYYmwXrCHgdmMfrEvhPs1KXE2AnIgKg1uwDUsJTwsne7yIAg1Fk3qPUAosaQHI4XqnC4uJSAYQuDehQtQCX8xg+L1EghsK+eYGErjKJtjLoE2KLL91Erk3VjUCi9zsEFrqdB3bSq4gnfcb8GXQuEoL9HUgCAjbFf6AzisJAPc689NKZJcOG0YdjRORQBk09MI/xndNjsjUbUIacM8bNw/srCmazHxaxM8aPxzdZjeIhFCH2dxAPKqWOo4wPcUp8w5Pw3nCdAcmBng5UckUia1pQtAgUDwoauThtzsZRUvAWMoFGfPwgge4QTu4kwosXw7gduvhFGqVf0uiLF1/8IhGEk0rJTujzpp3xUgBHcDXH7AYG3zNZebt4v8pq1g78x2Yi2YqjyGgf4N094jGpEXBpLvYKNKcqqOA8P1LqbpdN0AGIQ+Bjl71VRoMslQDXS15/f+06r2/VjEWrV9Ho+q0rfN45S3bd+0dv1fL9wE5/F17w9KYJzTka7a7LHps0ORoltnzb+Cmj/3TfBSUI6Qh5B+a0Cp6LvI6deGVb0EvsQZv4+of0ZE8oOjv0Ecn8MMQ/gCIQmISdOL+62DpoZG+oZxyHHXWnLpQMGFB7xP9aPszQSRhB/KFzZkWDIr4C0unsDJPFwbOz2dihjNYHsGQxt5zJqGQzIHIZKBujTfDb/UGG1/0oFLcriN2MeJ51IB5qiZFdCh0L55VzRqYcj18D7KbLbTR5B4YBrjHjO7SOuqqwLCWDJ7nY4A2PeavTjJqk9LQ6u8WkyzLmlGYajZnmlFRJTkkuX0B24lVXOZ0Nk4KFOXqDscZTkZfnzTQbjMU51qycqqYpxSXZWZWFWearlA4gXYu9zqSMYvqvSDj6+GhVhU+fbTbn2mCThJQUc0CfnJSckqnVputSK8M1r4bpJ2VZ2YV1GaJclpc5IiXFkq/VquXU8SabrbYwM1PiNbm5TWGAwQ7SJX0K42ZgFMSgwoDnf6lEGCgTnq0vuSVkfPL6G5a9eMtYnenVG6+cPk1wDhbeDIWvYCG/75KJflva5SRj1Kve3W/T6NYDH08Yf9UlEwP5ZxdyvwJPz9KQ4GqPy/ZisjNcPQPbf1MHTNzjqmZFz4Lp/5c6pL1AknQinoylZ9dxbpkiQjY/UwqzjRkj7VUkiijth60H9QM45Mi9KFpRhOSMyI7L49I5K+fiarhRwEO3cnNQGgIYM+hS5jUjTYJ+SZmwQJUEiFewC96gV68bMncxY5f9A9NbtMMKgYrEeEkJYWIqqcJhz3bZJ10wc8TwgoJtna1j84tdjvNKyysq+59YcnzZ8cU1Xx/9ZGF1tTV3eFVubnVg45QZTZY8S249nfZEwKzRqjVky4LGgvz8vNASmoZWXGFk1aRDyWrdVLfTOabpolv3ZE9P0qiHBdqX1tVFmXpkH3k6ur/M01Jnt6eoNHand5LDcXqfPqOkzGBYtHt4qS87a5vZXObNzIy+ACxWfhg5MKSPYjYvIut1I6MNZb1T1hURnVvUMwsis1MwF5GgIAZ1fOvGL+h1GwHBdW2k133R/wbf2ndgI1n7xUbarure+AVZu1Hai2c24ijgcf8Zprhlt8Uu5ACDxWnhRByeM4Bt47LThtgsYLQwytGHbuc+Z/MLbL4ox6gPlPaiDDVCTiX8d51VDIxvShinOB6g1g2wY4rCAUcG/oBhhQmMWA0wc+tAaY+SIp12CvkXoLFNXDZiBKQOGI+hM1iFqpDg17ElFdm2dNmtkazhC+6J3LdgRDaszmPR3++8a/c2vj76bP3aDbNrhl1w+dr6KAoYVANjNLC+9PCSiT+GD4dseyPxJmGbyCnxbwzfJKEUCfGU3ua26cWSw3QBLMBDy8n9JfTlW4ETRlGQ505yYtCGA8ZIBFxhR/seDdNMxjU1ivWbGYqCCfZvRLAno24vh8C/6iMiAW+tfpUxYkJnbdGdgb4pwbvitDP2XDh85KUIKkHCgOB7wmFc3oyh6zOiBVTD3/8RillBCeOYICIF2OwUTBV9Z2I7tYD7SxPaGRPROX8hzUOLIIHpZby6oY3kr7lrkEq/Kwg0Uf9xmAKDbaspxjPFxdio4pqaYqWRf49ejdMDmhSzwZO6YQ64mT2ChRhkIhWU86564gsSRKNo9lhOEAYRZldaJd66sr4+urTuibqLIcPfVjdrZj3l+c0Wy0eWkmJLdBNmZkjixXWzembVRZfW169k2Xp+V319XwQunG79yALXWafDhdgvOkZnNDI91K+vr5Ex2xcGYnUM1MZXE0qL5VhePCv9T+ekRiSzETYjVdafGRZOhsNoiQMpEN0D+3OVyepwuNcRDqMGP8wMW06xTGRgd64ytIVl+GMlw8wKQE5jg2xD0A0MkA04IJ0B4DW+KHChDNQLJw8vDI+yrZgCa33nTSPGP7gPCNUvHnxwXOgWehPfOn2ldWR4gXigre3dl64NLfBGIguvueMgSb7rrrt309PP3r55WSQSDIeu+e17y9uQRg8n4DCUGiEMsHIFCj4UYZPtfjStdSagRnsshTndjugxHEFaPCKc7O9AIMOUNYDq8mmP1Bhm1j0RFPXEwY6ygBjfomZaj7Zfez7auxEvPt+e+HzvgDEF4BkEd8zmKIL6dGQ+ENxBMS4BRNph9qxfacAQPZ4W5lg9s5px2AtUEjAFJpW3ykHs2O/BOD4tkN0JRCVgYyEmimAlBdLeQ/Tw4QEuYedhMoKO3p1pGlFvMOTmelvbG6++ad3YMZac6cF07c05Pm95bk5OzlyhmHSQkS8OMh4vkhUl1cXuqpLcbIfTqB925fiamnkjSkvrC80mO307u7yqKju7ojwrJyYz4A9IfxTNXBM3iTuPmw00MswpaLNRIWrtMXTvt5vtbrtsD9r9QC6YvTHaQEu8ilCsyiy73AqZAZMOmHQie9049dyS0+iNdwFUYxTddqNCbEhlhQW2wtGtV4+u1czJCA5b8PH22Ytr/1KzZPbsq2cvu/Dpp1aFgjnqq9RmR6hx1uxmpDQc0yrsGuKkn2jspX0nMhYb+WRVCp9Gnl7rzsq1WkfSix7nfyu8tKQuP0+QZTl1evbkke355VPKSnt7H3qoN3zmTG+RY6LPrglVjLE7XVNTU5JrPSPDE4urybhAvViQUpRUW/uAoNWXF+sz6GlCCL/fnFlVhn2l8KWKniIXoEsBF+CGcXUMb6PFtyS7G3jF4FtwK/bevBdVa04SNMsxO2+zVjHzDooaQE16DRGZwXdpw4cPx+29m/cOzytDe+/eVpQdR5g+mln8HG8cf71i7n3Dt6NuR2PvBuEMiguQsEQhuZAv/YNZf5flDd8bN/5ufvijBs8dmxBOKbZDjH6MvjHq2xsUw+/rxzfewS6drNQBC6ALHhp7Z9LF8HEq0EuonSmDtw5xoxGKBnQ+3uXIhwlvTI/L/FVnSRQ1MbvggYJ4OqDCWac63j2otOL/zRATfVRBohcoRxPIVLTzGMOOVN8qhYpagnT9iWQ9EFeKPUD/KnzIyrcrCgP2T9PJG9H9KGKa8XFi8Ym4yDRux8DgiQ45cTvQWiHg1ANBXRop45Fs8kzr+eQ4XTntuz///E5EPNbrQMFKLrFY+Jz+43Z1QZ5agQldjN8d0O2jKF3ZFN1+fJMaI0wiFok9n2+V9jIdvYg4XQdTA8siSO8KX5Ob2cChqh+u7SKE4Tqk9VFfyKNizioCwA8EQ2IQZQC8Kt+UjoZAqhugY7+7b9slKreh0OJMv8ZiuSbdaSk0uFWXbLsv+iLJfest+pe3ZPV99LsHdpyZL9gynJZi4xPz5j1hLLY4M2zC/DM7HiCGi/Cqt0jugNxC+ohLAe4QLS31CfrBuPKKOHWiU9Q5Rb1oWF037qYjN42rCzLenYwgY2lmdDczI3n0cf4JvsW2evSSm25aMnq1jVzKOHi67pr+jrffRhnC9qhrkL9B/I6wHuVBJTATkZtnWgQmQYYW2HwuLCKQ2tHCwK5zDFBGxF9QJsK5rggQOpG+KWjKRbpW9/8YFvIhnYb2nmHh1n2rUZ/ZtfDhdStXrnt4YUzb3EN7UHGi6l7TMm01rBAkyTzTVq+JzsRsGO+GM9G+iM3f2Oi30ZS4DBzmVDLTlqF9mItZSNiNdiTG/MwmLC4LAQpNh/oKNP3DNzgFlHOkd2skIhr6pggfMUuncN+BMLamP5M/BFlpb9+BSERRvADuJF3MmCpaBe+DuAzokhSWVeTWuI4bmf1dPsyamFiY0bEw3Yh8lqBYyK8toilFtfVucqqIv5nxNbVF/ceZG0d+Ua34DIr+i2sQg0PHoFkGnoruhz2zMSCn5DbgDWLc7dDKgdUi2JPoo6P45yjiyUOKJR5bq6SZhqDfQyiDi+F6LfPaOasuJ7S9bU1L73d4p2hoWdN3AO2MFeve/kzYH0SQp+iJItJHMIeczEpAgUtBp2SPea44XcxeBbkYt2gyyGJAfIQJBjV0/HmdkOvYfAE5qEGBIuToeI2GHDxPyGfHBfYTCyCz4IS9gN0CmSYoUmSIb3Bvsrmbx/wGUM9jI8qzALma8dEkRgoEjSZBR0yK9D3oxpYQWe0t6s0h+4ywMsSu0hFAYi+qbQ1l5YpF2+lvsJQ8UFiRmUNfku69cIKu9wv+PqcbFew6Q0OVhr/KMz7UmqRJ1vWOUJ0ffUY5JY+1JFFB0c0pvCnCEoTtnDNxNSPVDngtMFgQB/RnA3xZTUoDMP8avHSBrzEUNn+Utnrfan8p/UgyMzDbd4PCcnQqymTVOzkXBHCIaudlRle6mqrvKHBOW716WuD8nOj34jEGoKNXsORHxT4N57AG5sEbTG+OA2pKIwlaAIWqitFWQIbobIHgwC+uO4ijHdGFDm8Dv2DCpf5AEO5FHUP8F3+CNNNuXrLebixw5DkLi4vPv7CkqNBpsxWYMnUkJZl6SZJGKK701jUMHzP6tttGjxneUOetpAeZD1O0GO69vEC511Ex66IKB96bl5WthXvJu/QnXo7d3DjxttsmNrKbxW2+9aPNvjxHgTErXS8b9JqM9CyTrcDmKOR5azHxpKtCld7Ckhxran5+qjWnpNBbGT3IHKwe8V0+OjN2Z3qSXp+UjnfmOYrseCft4dNit+ak5+en57Bb2VpT8H0K6hkAMKSp5AzAhwQYX1RbqGQbcIcpM6+588rrQiYynzShJV2k1yE8Sf/xED2q6gaAUMKnuRraxpNUUmEkVQCj7o0eUTVX3I5rcBnRyYukw+iECBQTzPQkIqns+U6TN58pzIA0LCP1MDaoBQ2aVObYyggEJTkgL5rQRmZ2n6KPvEqP0GhFIfdzZO2LDre1vPKKh6bMnNBadT257oukj2+/t23T8pK1y6WM9kna/JvpJ/SfRzoeFO/md1wsp2a/1akqEbz3zQlPf/jt5FL3bR9flt3QOTqZ6Q3J9TF9SB5KqRHIyIYMcxWDNAzu+HWxHODYA09sIN9+wScJvGZmR8fM6HErH2KZV2T1hln9l0nCB/RniXQ8scFNyjc80fHeLMV2QcGf3wPfkoMYwWnzJRHOQ+y6qhSSTUSbj+cccKAymzLEAexlEnuIb9X78C730XmfHCXXk9D3V0ePE99++jVd/9Eukr582SXRv7Vu3Ni9sSPyEbmdnEfs71xCj171LX2Dbnj/D+QqYn6QHm1fsoT+86bLZ83csGHmrMtj+nMFtzoSrWJ1XqD1mZuXi1lVA9U+4EdhE3c6vV4nXXIk7/uq0VeOWLfj4Q8+iPJOH4ABr7O33eHj2368s7b2j5qHbn/6x+gtPofY5fQqtndcRLwNnmXA99Z59Tb2DF3sGTqvMIk4/8dJT9S23HZRx0Nv/PRThP+U5B4cNYr7Ofn3zxylf8H+A9yiyIg0zAtCkZfCiucG9Ni0RzzW3xGhKYKaOTAhQ9efiaiyQ4HYpzmJaGCsX5eeBYyYC7x0NTeWa8VZaRW8VSGeaQMkUbKSqhBxo/4L8974gaAPsAvMeuZdRCCDiMQtuHC2Ss4qkyyg9bVbHwDQERDssuioGF9U1VphTk2/ZGRTO21JLTjv0vMKUtPmXjpXWAIH1cOUo4vGbXSGDl77/ivDNuk2Nk/YGH1y+fB2/8jhS7XLK/Z2F7mShcruByuXa5cOH+m/eOSKVK1Lyii8YPaE8rHL1lWMveCC+4NbFy3aGqyfNKl+INfXTJ7Y9PT8T5+jLf6mJqHi5h56Td6wMrJi93NSsu653fTmsmF5ZP3HN2VoJTYnJnIH5bXScmZXbiboTaZBsQ5hPopOh9mqMUvfbKcP7pgemEe90X9aplnXWSyqcX+l3WT3T1lVbm+uTx9MrhHE1h300R0tVy2KrqDFFst6dAM+fqOw8aKkHHOJqVo/MnWCAHDndlIt3SI9xhVzNUxjnyYAWA6pgkiEm62ER3isKsNRCcl6Bq/jMN9sEmYZ+ExHbrLFZW4ef0HzlCz3BQtm59nSSy968pLVNPrTZ91eizHdM2bW4pWXXfyEde4FLXMFkpI9/8Lpc2Re2iNbiyqC/mqzLrNq4piRaab09Ekjxx6j0f6TY8Y3Zs5+bNWIXTfuuvaKcEtxanRxU0rK2JkLC/KG51umzZmYrugIyU5GW6EkYsA6WG9DQxEb6VKYKmDk0JgSDR6BsOub/CbKi2gKUpGK/J6UsDpSAe7auHLgLEcyO7ghXBZRaAaY4uJAjgQcDSSN8Irz6mC58xy5s5mtQworRFOUlLzmJKO7uqyzf+ts6G4IUZNSzLcOTUnJZyTzflLEWK6P76cnhXYmJayp2RNL6UfOJ0eNanyZ3uVsaAg9NT9WXHxWqsgzFdvNDOaTi7pnLzMfR/fJKqSNrMSIkhsfyl2RMCkhQAARABZQJu0Nn3kpvOXE1qUXXbR064ktfVMii/iObuFkdwe/iPaEgdDvOwCLHk4VF8Nlkcjerd30UKSje+veCNl3fmck0smJJMBNke6Udcz+0gltqGS+4XnAtjfAzC8nslsmBQoewn50F8QJRVF264NeYg4Kbjs5HAi8ueHEiQ1vBgKRjhNfbyQLHvrm230PfvPNQx1PPnn6qW4iXHkkuq237+2NH/Zt6xXfOfF1B1z6ZsfXJzreDAYi0aRvHoKLH9z3bfjJjYyGXHm8PPr8Md74hTf622MczK1EfXESHOsAisYlnS6AYB70A9Wj+RtqMoIscbqBPja7ZWY0VgQlonJOCLpFs05LbBFF9AYMhXUX7UGHG8iQU7t29e4inki0SWpksrFMmqLahC6au3aROeycrI5r7Xt3KTJqSHuIZ9euql3IoOAZJsDctYu274I/4undpcjNYj5PcftT41meGDj4+VYiGm0M9xF/AkVqFI8NEPYtil1rtIlxPiQzMAKSEQFYX4msBY4+G3so6Hv8k8wFdUhn1i3I/ITx0oADhjM/Ply7jI/06qCPgBHTieuHLlToCkUQ0xrBdYzrmUusQ1LqUO6NqfJRmolKCRS/RFRPvYk3KWAgEgcMCg5meo9G5snODETM0BB0Xz/zEn8IhiEsGvozRUMEublBGy+0w6zlJjHZowIkUPaY7wgQhzvAOdN4k1kqUyGMhJ9VJfNioEx0hwRiVaUJWlKmAhDatjPu6bZzZ9zTrX8k3fLIeHID4WxlGcU+e1Ol31VhaC2rejo0dt61k4pTiETb+ZzK+hG1lclJOtcIYXNeuTVdVolatVo2hmqrS5PcQiWra+eQ+nvdh38g1cnBHbvv8IomV7FV1I2a2lptSNWV+8aNqqIfPTZzy/SGIluhudg7to68VbFg5oWjp3pH5GRmes+rq24suG6oL48tTrUM+qDHzJaVvL2gjMB00vB8If9XZm0yIAEq2XBk/fojwtvMxUwNBYrjeoIrehE/ff2Rn46s75uCEjH23JXc27IoiwAnqhieykD61YGiziBDVCGhDPC+bCaCOUQcKsbY+e05hDiDLnHGje9daLrlND1OD994/etVewvuWXXxT8d+e2nG2Ls/h3TYIapzPz3+DLGR4fSYRUXmzSPlNMSnSt3jT9M/09/Rz9+dbRo/5i/bF697rW6YbsweuOvwpZBePI++w4++AKo39++3FKtSBPIFzaMv9/AywFgNzK2XgV5CX406bjz2FxIuGYBcvTYU28KhLNpCqlpgi80EFmJVIKgPMCKXndTyeAWgGKJchHbWXfSwtUS1+3mHepiNV42W9bX02dxSmbwOE0PSG3JSPzRWaaP3VU6UnIVB9X6xMIf+1p1Dt5o9SUlj6JisQtU9qTrVh3Q8r8nOcmi/NhQYtYJ4rNzZn8Uff8pRcMQ8qsC6VZVWkG2qyurbPMHjFprtrpu1Los2bUeOOTq/4UJhOTtttFiSuUR/eBVwL7iKNCTu7K5qRhAWbVK9wuQoCLoQRMUkJigHj/vBZgAVOIO7EG1WYU1xsKY43mhAJ1hvlYNzntPpVfWfXF7FkOBjLq9yBnFxOl8QhoPdnyEfuYeeojvoMrqdntrD/F4feJtcSlL6PqGPrs5MN2XeeKHTuJ7c8NfHSPDymuUadWaSXRUcbbfTDzKL4Aiu6JrjvsztzjSlZ164SUhLSTbJmqX/eOvffa98Sb8fS6aQfxD+muvXt+U+LFjILqz7HvbMtx9gjq57SIpTuFktVPtJMnnhj5e3a2a0ZLizy9NrpLm7RvReeKEwiqhVKj5US1IkQSB1IaKmj5blWy+YvLHp5ZP/Qy+6jL812ryCHCTSaw/0Lyb3RkeX2qaRvys2mHEf6Rnn8jFAfcx/VYZGaP4hHp3M9gZzspr5IlBGf6Bj6K8eoeMmOyZd7Bhlf0D+FNOPimtri0lxMQYd+fUjwCJKTikF/KLMmTCTS5m5bKBbJ3OzFA9UWbAJXpQhJHot2PXi2cavfngtpG0AbgCngNFGYNkBN+EK8Q1Ab7hDJCi73F6YWC5V821ls7LPp198//B4koPeqApuow1vhcmrmIthPMFNX72MvnqlZoS/8dp8UUwmoedaph1qICpJEnjbtuHe4Zr/ac76IHu8ShAN7jySFm2KoIerUgcw786XE5EnTWn75JO2Eduqsx3Z2WNNw6uqhqe5XbbkZGd29bbhL4/e0DFK4BEf3ch1yQ1yY9yeRUNMkkxEPRcMmNCXiXcTMUImFRFnvyvaslN4uMK+mX4SnXId/wjV8t2d0cmygT7lWefqd+zku4XHKp0quik68Tr++f7t/DNwGp/xFLdSzhMjgGUtqPvkmPwMOUctUTQMiYdcPqd3M9NcOYNfum917tiwZ6bRmMu/O5in1/Bl5PXzO5vp1dRDr27uPF+MrJ7mqzAkiWKFD8mEgXxfDmki6rLP6Q8k/fMyehrpGLQ1OCYeG5D1J1oSHUNrf2UDrD1o/aPw2wrNq1eo3fg0gZyEMk2pMdL7HYobUajLMFIzWm90d6i6O7p7t6LKYtDnSQd9wbQMQ2vRx5GgCXhTFgQHK8QUKvwx5k/niSTUiRb86DjXdwD2CToRWL8Wzou4TuU1JJE0HsgGmfcDhxzUAzbwAw2hs1mFPCKcTCm9+c4T7RM7br21w+3S5M29cNOKlZMq27964GpbATnFYLVh1G9uuzWXpuR2bL2yuFhWq3NG+UuO00vp30/cMFevV2lDEzpv+9O/yIin0IOm/5QqY9KiF9pV2tLShtxok1JVTPd6Stp77r5Hu6kBTctZfZ/wTud6o//4Dv9ba1n9Z8gZ8WPxY2yThh9wCBM/pnvI/O10D71nB5nHdmS+OB6O92yPH8/bQe8h8xReUJHVC0AdVwL0RL3svFhcgJiQsgFYQT9jgWMmi/DLS5RoVjE6381OWgbMp1HxipG8UG2rOEnYxZi+nuns1WhS0/e8Pi0tKVUlihKRk1MK7F5LTrZWl5TEE57nVYC+klNTtHp9hfATTYmeuL7e78+1GLItRe6CEUFf1bCKqkBuuoNPVefl+/w1wpa4AQt6b6q6aW1yanpGdnKqMZOXSGlpCWDvlAxDZmZOhlOTnGYVMvRADKo1LlQ5TGvNt/l8wY2iWkqSZVmSRE2SLCSp+I1Bn99me5/ZRERY2BOgnAf5I+w3f6zfFnJLmazp/9B3cRuG/0v/kSF92G/5b/uwTOke+sSQfqz2nt2P/OF4FAA0XgZOF7iRj/+7riQS66Tf/OfOXM4uQssfpUMH138RNx1mn9sVl5B7mUsNarwHfsxYIPYjzAgoLkCPn8GQSErXmWWdOahzB6EW/lMB6AqTyWqr8tUvbhplMqUKJElOTTVmWLKK3eVlRcVZWZmmlDRZI9xaZZHrrCuDM1a2L1x4yfltnvaShpyy4TPKn5780MhF85se/GDqXHGdPuivrqhyuI3m2rrpM2brk1w2RwEMdLZJrzdacl0OpzvXFr1vxlVnVBpeBxSVNiUlRa1XZyfpk89smbrKb8l+6Bra09ZGPNc85Ktn/XAQ+NUcgLU2hINcvirDaECgkUbgPWMeij6Xu0zl92XoYQYgeYIbQGQgW4TJpIoItxXX1hmNWXaFILFnqXNG+z272vnW/JKsYHmkzJdVki+33UbpHb7OVWGL1basSImOVbTMluxetexa3x2E7w3PmuWvKwv4y+sSeMa9nJp5bDGnYSKrkVkJK8x3u6qZmQYcAhZSiRfBbMfXMRtIBT6J65iNEV4vHkM2l13NDYGVUL/TrkNQJpMhNUZQ3M/M92P+cmRAj83qhzsSKlXC/4RjbthD2w/9hnDSTYZUKXQy64cmoFFi9Q9tP9yRUCniY3KKXf6L9gOeRY/qIBlS5dDO+mX74Y6EShO6KkGOz3wnzOf03I3pmvQoBjiH5y5fHnOsPKfXLn2dnVS8QWI+j9BfRwb9BQYsQtE+7cjpiviGfvTMf3tgD9cnxvlBz6DxwONcokSmINDoNAzyU4bhfkIkJkdIIfkOLiM9CY+HvJdYIKODkMykMqIS8AhyREYqwOsrA3bRnYa8jy0AxMGvRdjhb6Wb6L0/79rF/Uzmks1kLgf5n6NV5HxS+5ctW/5Cf0/30d9jjt89beyau4ILNj9K71j/6KP/fOxRUu4dMb/cwgtXmpyVfn+lM/mznp6mlWMAXgoIXqcn5dY0Tx5p/NXIQt5dP9N7f/H8qsSnklrWEmdp7ksbw7NM9yxZ/2j4sX8++uj6yx+lybX68xbMzrM2b5w+wpGlFkjLBx+oXLUTpk4dG9SlL9q7cEKBgcTs1mNz0Az9Xs4i7dmU6HkupvhgNCuLoCcOeOn5Avpf5EuYUpjBFJSEQrk4OtqEqtx7NK2hcKhVcw8c9KckCC+2J4QK+1HRzq+IXbsCDpj3hMWIVRgt6FIRkwX3JKTx08xKBmW178qL5ZnAXWdzZWj3xLkKYsEsgyRgTjzw6QmqYjAiFQtriOJSmfgwMl4amqvAmwdUja0dnuhbHk/ZhhI+4Pnes7Fk4Kikgdzvad1YQuc85elo9cBp2Pc9UlBZqdNpPRMmDssPjZVoK97e4fEQvH+VB45KNno8fDHcvzG6jc7Bm8j9T0FFHt7vwaf1lK4Kt2Zn5y34Y+D8lefButgDdHSV9CU3hTufm89dyl3FbefuQN2yAbUdhjRJLhP9yksw30GU8gBjptgMxCMgxlJloJQLcFwhR+Kh7kzxyGBEMloJvn4Q6AcYUNHHBDCypJgziHFFIxybvZiHayAvfdrafaa1QJ9d7qq312mSJ/omlBUUtJ7pbl3UtlWd0bnIf7NTh1wbhlKDDbM6xx2Vizoz1Fsrllj6b3GFcGTJJmV4D+r5XH1Yn8vrtdqwlmj02nTak67VW4zEY7TQ+3FvMdIe2K8PYxZ3wtv4QN+Iy6Y1l1aa7JK6wjH1/Jtbfdi0kQ3PfVXuv/rrltwaac3eNVV4T5Wyh0OpPqvl66v95V89lzs8g1xWV1jEIJ+i5qe6bH1Ojt5oSu41AzrWAXfytcKixMK7xWxAkN+p5UYyOS2KBYwBry4WfdKiyP7qiWSPh6yMpQVuY0wSqGga4yIhIT/y3GUXrMCQTy04972VsQZVejE4W3dHhC+PxdIBXLHq+ZkzaQ+a4BCaikvp0wzmIBtR9hmfKrKFcEc3YiLFYIH5IMVl3UmcESBAJTeLWS74BhxFnYNZZtkSM2xBu5bEI/Qujb1HAbTeHntbdqvZyleFUDDFsUQIwkmE1X6dFR1zhM5wYR12ODRPydB2VL/gRroGc4fvBSigLkgvUEN67+G79+bf0dnReUf+A3ui4qbrbnM2LRxbbHmR/pZ20t++6ByxcZb9DmnvhW2Fl3mcDXWF8Uz0CJmDVkP0fmY7NJivX/Gc/ycEJ6SEEPoh5n7yP7di5ZO+pEaXa3iS90k62R1K0leO8JfSnhXPLV/+3AriqRk3MSspxKmZr8Ex5ittg14czo3ixnETuanYlwyoqFhgGWfigWKzr3QhLjjnWcckaJSdsImxVFju8PoyMuTMujLXzKdn8q1DDqMvwzizwS5CTdhAHl3d0D0iul9JxZ3uZXNnZGUnFcw7r809f8KE+Wcd9/2M9lOJtlSxPH8VRkdiVt5KinzuK4A/0mQjZ+ByuUKUq6TxBWVAJ8GmNyGg0QPiRb7ELQkAU9xM5yybVOPJaxWHjh45eqiCvDLs810/vTHfu3rRb16igdljpr2xe+HW2Tf3Tpw/sffmWec13C4W9781d1tj47a5QtXsZSri2H54yiIr/bKMZjycftHBadHPwjO7Zy40rpoa97ePSPfAWDCpAHABGOvYTiQggZHFMksYSFqS9Tq0/w0RFh6VyDo9k2gF3AFJc97E/MbaN2fS00vpv2d8UN+YP+m8pgm8xnDPcmvNm4ue0RvGdp/qHmvQP7Po/QbHivsNGn6ceFHZ/lfOn7ZYTVPJv1KWzDr/lf2lBUKo9qqfQjPd9Go+kHuys/Mv27b9pbPzZG70d+RK27z6M5trG/iE+CqpGE+BY9FCgGQKCUCgynqn4I5F27ll/1dvP+MPnHnJMObdQ8JpegNZXfJlZv/NZW/YyGq6v5Bfy2+X1YtvvtnrQ62ioemxzt7HiINkOjeTJ3205qI8epJ+VUJup/O4s+LsaH8ZZ+c/xdj5T8F1ME6X0Ck1qpoZRYh2pV0sYF5zBHF0N/cps2Mt42q40dwFMe/3NB71IH6fA0CJF7GY5EKJKipZbbLdFPMhgFXjNgWY2TaOoZgQwYvhMXeZBLgKRd4m4e7W0XUrHl2y6egVRPVcmi+l3qzOlW57+eLfLCCvLeyaZii50m+b/FbrIq22qxX3N6qmsFBf/fc/tltUpZozXEZfSnX11a8/KjSFOheEVs0ONmyYfOXvSf3wkhIivLJt1j3zwrMsDVOvXVBc5cyd8YawZ9Wl9wv8nZeu2jdciRkWrbliuSkrPSdXb0lzqDP47XPD21m/TyFj5SulrZyO2VtWqTLMGA1WiQvrDjj0BWW8205MGWa7EhnWLKmE/sdJ6SuZR9Dc/2TBj3cveyH06Hyiu3Tz9wc3ZABIq/1bzUAx/UHauof+Yz+aPt762tyWV9s39b40+97PNpGukfSnkbEiWLtVxCvtke47l0xUiMlEVxBtAbH2/znacxl/2m1Z+n30g4v5YrqCL1se/UDaSH+wz8/r//wyvozvK7QK9I3o+xfz/qiPr2iLHlFsC4GLaWM+uUpMOwBuzErJpyHMUgXmtgu4cJOZXC2WHjrU+4dDQgt/vzHFYByTFX09+kbWGKMhxSg1PtN/8JlnhPHP9O/li3TDbFbZQDeRzUDD2IbpEp+jZbGGzzYWRSgAr8cgAcZeBn4feDMFHGhIQG5rDfVeiyBfXB9qvVorpibRZWRLtkHj0tAJH31IJ0DGkE220GVJqaK2MMi385cEeZfQiff0d8BeeFdrz0yi2+gyqzGZV5PVr75Kb1DzyUBM7SKXJWXatYXlVE1Ol7P1vkLWyHOgrTalrTIiSmhrEPoE1mAZD8gyj4gcJGaTrFnT0jcZhb2qp6at3hndG10sp2iSNbaUZH4N+So3xZFlEy4VFpw31UbttifO799z/hQb+cz2uNDUfwF5R9RatOnJ6Ot9uUObnGpGn/0BvtDEbOY9TG5wtoZWL8nOECGiy623krgVsOg0mYUyIgqBoAhA85xxvcbfOq+U3uA/rNLTG0rn3brhMFUxrVb/s2Q1OxYWwBVktf/wubhO1aT4XW/EroruZ1qwV2PV/itWy9k8bg7in19Gp3LAIbwXHBCMDkWSidcAEOKcoarwBQz33Ue/Yzbp95H2NA1PXqEfzJtHVOcOXSXfRwyJN5H2JJHvIQ2o3iTpfDGTBcdpA1xlpl9GdmRRRmwEzfUHce3X0f3MBWhofIu+KbHwmimKnYLCu/3neocEL1et7s9ktsxnRypRQDSrd6BPLYPaa6Ve1H4pcRdQFzb0KbGIGjQFaY5MID3QOrtWrO+bwpzh8XksbIsSYiGiBNtXHKsw9sGArQ3ghri31LliJGMIfTOL96MgFkYe0xRmkq3ag3gvjPJHDOcIWGjv2d9ZiBEuZECGrY5Z9KAtlNQY6TuA1yL+6u+gPahVj8Wki18vxiXnKgAg6PmM8pn9KPZhJuixmIftzOGFb0XLc+Yfg/EKB2JZlDEuG4WP6AQHtJ6OudxgkK1gAP2WBo7jXs/isaipY8nSuvr6uqWLN5ZY8qbkWXFnseJuYaHVas2T1XWhxYtPL15SVx99rBCD6RepuoE9seQVwtBbMRUN1txiyA6BAQXcRdwKbj235ZyrJ5ARhwtG1lhUrqcjukZiSUFPAWaErQ+gtVsaMQUDVmYNZ2S6LjkjyE4EHMQbsBJ2Ah3LOBb6FdXR3ioxIW8SRpOmuO0WPUQ/ZqutCn7GN9eufZN+S9+j37659vKiwBLy+HW9B5YvP9B73Xv7J93kN6y7YPNn2ZZLN5Yuci/hk1OrH81I1WUAAlZJyQDjh11VttAdFoTk1OLr5hANXSxl56QJqclkYS3vKls7PdikK9CuqGvhy03r8MCubatrCbJVP9iOIta23kvWvkmMg026488v1lcvFbOhKfQf0KRLVM31NdO7O654rDiP/DlNq9LqeGsuISqduSxQIpB/zYbydzNS01XJ2hUb6MdEbS4eWcETWrn5pvfpzUe2tkx4qHHmN0uUdDOLV3S237ESZyUWoNh2VooyfgQUUiNlIQxhNh/HoCLoiqz45g/ugfMLM9y595z+3wNxYeKp/qznxf28MfIh2i3gWpTVfQfC/cdlNSyidgwihpHUI+hWKe0dCHOSMhA1WGlLTzj2F4tj5GH4PC5LhCfoY3Gn0Ys8rEQdwVfDW9FYAm9UA007W35Xfp3L4oqYjmUYV8+4MfT9DRGcmezLMQRlP4rgxyyTIFMdlOHXZCSVYqIbEGX8XsPQTbVvW9XNy9c2m7VaaZvF0r/UOs3S/5PFItxumTarhnxfk6kRpGRVxcJxpeXLSXFNzfSamugHY/iNo/t/Gs13jOn/ieX/PSaeHyM/vE3Sas3Na5ffXLXNArUtgdqSLNOswm0WWkN1NROWl5eOW1ihSpYETSHWN71m+Ojo1WPI92OinaPJ9wN5tr96jGLvBd0DdH0K44AGNK3ItBs42egdFN6h9TQXNxzEC5S8qnv1vtVrWqZfBsP01ccRNIlkjjNFkY/ol8iNf/vgvm8wxahA01avnYrgd+3Uz+jTjg8U8dwHDjLpM7wDoysylxto13RilTqkd2BE3Uw7rjhi+RwNRMXsUyr0GbKEDHKFzLuZlYFDeESJY8iT8Q8+yN9yvP3i2gs8usmzWnJy5j5VqlO7SnQ62ii909px3nkdtMPiElXJwy2jjQb6XjSqkxfddXd19cv0kfS0B6PfzZgxgYvZsiqyGpSL4xypZ/EouLP58Xj0MYWaNMZVKYl0JayH+BcTHHHPDVN+MI3ku1Tdgzz4jzGXewwwOX8U/WnUfBbTvwXRV7h3K9/KxEhknBJR/83byDe3pX9J3/kSJeUMqfbAnsxQPPHdGO/fNmr+/FE25SsAa/hHEAP9OOg5WGucPLkZAz2pYC2nyb+TD3KZTDtUx3F6lHyHCBeCLua0pEwtnuMzM2d/ZUZveuvidkuYpJ74dkO6lWae+FqlrSodFWgqLhW/p79/ltRmned0ZfR/1iq0XVb5lzn02vUl62aWrK/lf4idyqa/F6/MOH3ZKqhF+8/+ezU6euNLvxPUlkxnljVFvZz+/iBcmeFynvf7Ff23Tq1sXF8yc51nHVk35+/0cVKbDWdas+hrMH4psXiAaAuVz42PSUY3cTdxt3P3cPu5p2AkBb/CVBgFaHqIAAlLAsg2qmQJY3U5BJMZP3QkAXUN805vBq4SDaLSUMuaR2SlwI0CATEQ1JcRokfFIvIPgt5gRo0h2tLVIkxkAlS7ziuLyHfm45HTLCn1CGiNZWfWVLIP63EqTslBA+LLIGDTHGIwmeEC+cjCSbssuWMmL+y1Lpi0q3rMpEXCKwX2Gxbtph9WY3oX8USuaVRllmqStBqtpnq8ujBNnTbM3ioniSoJiNs7oEBOq7YL+jHddGeWXzW2lPzmaKVBJafl2W5+gCd1dZVFZMLRjGUjyZmXpsHyXppLHluKQfv46Kpbkklqhr560tVFGkmtqXVq1frJ+Y9eeDF54LGUXPuBeS0rZdkn0Kq2iwmpqy0XD9ITJHfspEm7cgk9wWcTc+6Y3bfmkrz+W9ve3OcM7rir7a19juAOfnXFBl6TlZnbECrOGbeQ3J4s5Kq0qQ5B0qQIavUDr5G7lBIiB3JPjwvQlLFv0BuIHByWlHX+rAs3kAp6WMUbM6z0gQmNUwFZFKIjI6mYcvuqOxCyqFL+7hcyCU/u/I5sEYg2TSSWr5pH0+LSJ34KWfJzW9d9On9fOQkZsvU6uovU0A+IQDBQFvdbki/rpcdY1KwAcLwZbhdKgXCSyFKG2cQJJ38gSxfP00/v+nrB4fHjDy/4umu6fsFCspTkLyTjfvcb0rzyWVmY3Ng4WZCfXUkP/OZ39DeAre6DuWmW5wMXOpLZWynR4Oz4s+lhtgEMAd5TsLslpnVGbX9c749LTbDHfNGRNZYUw1ghoHy0Ao+Vm0JENo9oyls/KT1TSpHSo62fC7pUIz1qTNUJ4ct5Z4t9apYtL2cyL3QaVBqtPm/iIxOa137F15TNzK26smZzzeUVFYHa9Zs6rfkjHMUp1urshqwaQ2Z2Upm46W8fz73KLPF89NOM9PR0nY538SqbbdLKlSvnOHg+N0UlSUlqk39kUyTqS6teErlw2RsbqyvSbQ/t/mNP+1r+Kykpd+z0OR7H1FS1Oavmwmnn2b2J/rFDaQxmDZ5FbDqMiKBPoDJsSqTFMy8B9cz+kZLGDV1hMfiYElqI0Q3MPCFFiRuHIWKAYlcNxBBWwfNylC+gsOBsRNDFFRqMzPZibDJRJxsxjA5+TA3vDwOtTHuQhs4rJB5IlaAj7UBBNeITMDCLcjZ2VR05heJQTp3w3KGxPpTnmxkZwyzHSYJdi8jiQ+HzgYFggU2QKcIXjttoKSyb0oKwEkdy0Bhi0MZEGrC7Uvo5HvU8wfrIKaL/oY7ZliBJz0xGdM54ASMCsIsSrJUc6E2OhuWSJro/Izk9LzR1WHX1sKnjqqs15F/FRRcOG3bp1CmXpqf3VytjEKczcXutHWjxTOpPG1bdMrV6WEUlEC0X0736YdVwy6VT+Kez0jOiD8doUfms9uewmBmus97gbD63lJzdWOVrPrFgoR0MeZKJ9BmYJ+1nty+SKOmONsUiaSjmWP+/8H3pQ+YE+jcbWOTcfOCtCzkPV8Fiv9VhNExNXF0uMpLazsjr/0temUTAUzO1OnPPZ9GE/6ts4gyHTggPBnY5ZxKLYZewzjLiMU7ia40FApa9QbPXHRcgOWOsQ+JyY1F9lJppVHHh6cIZPjjllUXHPlIAf8SjhFBQmAZsR9KvrjvGw5xj7Vl4GwtZ5UxYiudegughxfwr0FOK+Y2ceyHGjlg2bqXED+mfgX5JfPnEV0yIfa4Cviw9foc76NYQmwagvqzcCuAmwp6GcYXQkWhg8NDjnoWMZCGWcRYLQ9pQmjg6sMxl1S+ggR86xcVkE3g0BC6mJOnoqlxHumeYu7DQPWx4XiPxzBrmdhc2QcGQwaK8qEtJ4cfIcil9UnYXB4KFhdnDgqS57kT2MHd7odtdfNa4oc9QKvuWgQm4N5cS40ppqWgMmmWzM2EvVKEeoJxHKi/uPeb3JbaVth9Yd+AA7Rnc72q8SMNnJSe/USqLcyyNHk+jJ7HJVZFwVTicuKft80bopuTnr3y1uXbJ96V5+aWl+XkAE97n3peBKFbi4RPl+3EOt4vHb8ephnyZxHN3e/vde+j59Pw9LEf2kX30BxYij0X2ljxnncRc7xkc1fhFzJ9U4f1xRmRhr7BvhXhhE21m2W3HAnfQjHw/QiDkrCNoFYRBiVe1L47g+9EUyJEucS39EJYbi8D6yYHfR0fCIaRKbLJ4vKDYV0x1bph5xKbXMc9qGUPCwm3RI9v5eXRJJIIRPyMIS/uPh4kv+sF2fj5GoIhEm1TNsEuwn8Je0uuEIKB50eY062xmQaeH+mSnTXTrbJL7C4xEt5GsBXS/FnNfYMi6jfQ6aPh1kBNLydrerUNPAbDFUxvZrUP8jcyA834hzf7vuM1z+xq9+9+woOdyJlKd/G/Z0tQY3quB1ruHRAObzE0DOvIC7qLBb5cwGSETwyrSQptRERfWE+VrnOJZxwNR+bxGO349TzwrL9r9XrbFvm/iYSYrXSzp79CmFadptameVC1/KC29MD0tTVusTYt96wT+cWMR1eM5VDLjkdSIuDhcV+bxlNWFY2mvUQsVGHtNqVCnqdcA1aUbeo3whLRerSLbZrHywgn7s+L6D1BU54rWnRjBODFSN38i+pC4HqMZs2RoqP/+LwZiG/95MLaxOODDNfgFLU6vDEGMgiRKoDadEENqYRR82QsqaUVlgR2DdqFJXkRxQ5Ru7vWL66ZV2AsKCugB9PZGsViM1ECd9fXyKHkL8LJc0BSSgsiuBqqs6GDsZi5xKEZNkzCKFTIUVpXMxFWMPU9jcawS8kkkktvZ/eEnH3Z35jiyLhibP6pu+LBQwFpWakypLGvxzE11tM4dToQbRmV5HDm56Vliektw0XhCCmsaSpJNk+65fdjo2fu3a+XkJKf2+sfGNNx9uVZKSnKmr75z5/V35+hql1y+vfOKsrq77hpvtFf43WnazA2l2W5ThqQhGuewKcWjNqoFU4l7pGtC6p/HliRNC+Y1VNYFxjnrxmkLSjuenpPs0KbLyXOeWrpmzzQlP+V2eoKi+hvoJQ8QFnuBv/9f41GivyiJz17Y/qs8RhiHVId55atIytRlG8zf/zWPzAeSBjg18ZsDsWiU50j6O3AxwIFCS3LcQia3sMGq5vRMwh8T9Mcxb3zh4rfnDLHPKQAryPsN/6+tq49powzj995de9dee9xde9eOftJ2bQ9KKR+FFSjjI2yUj8k2BgPGNuYYDFyczCFOJ4sogU0jJoaQLJoxp3+YqPMjBrPETf8wuCVmyeb4zzDjEhIX58yMZsDp+9616BbT8ObN2/ejLc897/s8z+/5vepV177/rR5Mfpo8mC66dqerys2Hd9GuBveBafCgJBb0O0xeh6u8u7syU93TlakWFhPLeXmNL42lHi1TYxrSdWy1RON6pAIObyLsjgRtYi4eebReHvageoDGtHsbtdwqFMvxYVGozToxLJ4Tl8qqCDwUJam4mukLVHnlq5A7RSehWILql5FQexSgKKikgrKRaOttamPZY63E8v1fTnw3sZXOz33r0o/Lo0iHrC7paooX+6Le5CdtfQ3KJU42QzuZD8MSJNuv7bVKxoDInx1vyGXCAm9kEsKRhydFO0n6JenGxb56o8wLBqZ5IXtzR/657+8CcOLYlfHtlDNbJZhfvdrXUCRtBMdrfHLxogXODOc363lOVl5JBOy6QFjUkY72wnreYGRkvmsxFbeSfqiVSdI+UjcEZzbKwomMfZHBGCOmCJ6C2gVu5+qeKxqACA96ZLNqYaDQlHr/qHJzBj+9oNyAOzc65s+sjSxACyhvIX0fBHid0uuTqrUCePTy88U8ID/SqNsR9zr5AxiAR4MBZRYsKV7youIFS2m8s2br/Du2GDE9oRxgFJxETwH+lzILR8zC0XAUnAXO8MhY+J+3aGMXiGWVHAyWqL+24uN90+tQ9KpdXWQZfqWBzBJpDDZKL0J4Z3h4h4fBBSTPyJBDVMPo4U3Hgde5c6MIdaX1BW69ldVpGH8oL/p4SZRQxcpiRenFmXZSa1aj7TZpfQHyD9pVV50qDQRKU9V1Lppm/AzNWYxCRawq151IdURoC8cFZwribdkiMBh8Zoq36F2l+T5ffqlLb4HfKmh5M+HOfFBiG8FGyroHZ96fGewui7AE6bEEaR0dirR0nKzff2FfykbQwSC3dmvLC/VVZmhmeuH7OCuXdg10lcosDqeDk1aPb8EQ7Hta5VhX7z5DdFIo2qPam/j6e0h/aihsFDl5kLEW0ngc+LeeV/oUfEplaIPuwQbXvae2tPs0zWVhASpTCEJrEAiugRwFBuQvRTwiCO9VZkHxOyBpXtGydbeoBm1A7vIk/H11atJk5sIL6npfy6zT6Q+ofs+W5kaXM+B74hDx+5QgSbE1LraPnxIiMfw3SQzVrDXh91ysmZkyJlxrvMvBTFFhG4N/GUcOzbZEherR9ISKUiWEtyS1U1j5LOLZSdgjntUlT0S3Q/NYtjQ3rbss4VItzSkX8Kx8CEYLYlEouHykQJks3Msrv9JRwqPcY5xOlzLpdDJAgKuCUcmxkpSRH7Ii0aY6Ig+ZamTlKLr38WN3TSuY9kSUO1c8kQiSyUPYJLWDeg6e2hNYA8pQQYATUKVzQwVmAG5ST0H9jQNYsABKoBqhQvjIKjwE+6KLGMtUFUh+IWabLA7AMTms3SwK9iwRqoJ5UY5v3KCz5JR0OH9STssHdjXnDfQf2F/tBvMjXE2d13am96jLm5dlX7GIWTwumXKNQJDzZatu1OoIBxzAwvhZkSCNHGtVWDDv3NzT2384v3lnf1D5Wznv6ih187gjEA+KYP68zRzx2l7rfdbjra3hV18VOfsG1mkOGwEXovyixtWJYbep6yoPWgWUpCHsGIYhjgEVJ6oFg3PA47lM6YyldOLTI3lPCL6PeL7Xk5yK/jsEdtalydCQqIWqAKaRXvio602XJ/a/Ew7LDcfsq0ai3ghwAr5wmjAZWLMgSKLAsSajAe4dOMABPOV4vAUum501gc/LYwVlL57aFCtwOARQFPZ5KhO54fJyD7QyGVA3PDc3PNS6MR7NmVAi/dP9/dP6CyutE5cPt3NttcTzdpvDRnMUbWOkLC6LNhho1mTNEuD5hqZInSwHnQ4jw2etnRkuPOD2tO1wuwtixcM61qDXUzqopwiziaDmfp5r2VSXeNLdCC1DtEQ/ls6nZPS3sRospbLqqiju4iI3ga5xplDQzoZATlEc/XZGeBgtEaD4BP1eHboBXrJFCdQtkMn5ILoKfLu3g00Lu+Rtu7Ym+Xevfjte3vT01wGfobKC6n27va72yEydYuo5u3BrsKdTuaz86Tx46kxjzwcJe+fw8epn9tSCXDymkiHqb7OhxTdaZi1F28a27+3k6qvK3zsbGPhqJFirLCov3zkHiu5fm/BnfTN0fl+0vrL1eNLp75lcLVbNKuwfErMzKQB42mNgZGBgYGTs9NeexxjPb/OVgZv9AlCE4cKatyHINPsFsDgHAxOIBwBGoQtZAAB42mNgZGBgv/D/BohkYACTjAyo4BsAdroFVQAAeNptU6FuwzAQPacgKl4+oKBwaGB/UWmglfIB0cBAP2TIY1XBpLLClhRtpKAJqLTAfUOxpWps5+ScnF8S6enu7PO7d2fHOGq+ZEtk2E9rKtLaPLK9MyoV39q48S3brN3rUKl9AruCXICxbR5iMkvr5CuGr5HkbH97fVqD2Yu1sEcjvj9/Eb5ihCsT/VVvA/wZcxXtHvOh/i7f9+iEx8WzCr2HOWOdiNPGnDh7KmHtjc+8x5obzinbBWMDtZ5Bl4Vaa8ZUzTDgg3Ec6S3gD9ZPaoaqZ4wDfxd/Kg6uZc4wY6/3wHhh/0nmUUKPbjjjwf3i21zK+oNo+mbs+ncW3YtaGwP9QL0Caqp/qIG8UdTb9eIk7zXkJznRP0LGRxcAAAAmACYAJgAuAIYAqADUAT4BkAGoAe4CLgKSAsgDEANcA5ID1AQcBJgEzgUKBTIF8gYcBmQGkgbOBxIHRgeoB9oIOAhSCHgIlgjCCOwJCAkWCSQJMglACU4JrAnACewKLApiCoAKlArSCvQLLAt0C+YMSgyODMIM+g00DWQNlA3CDfAOHA5eDp4Oyg8YD3wP3hACEDIQfBDCEPARDBFIEWIRoBI+EoYSqBLKEuwTFhOoE+QUUBR6FJoUthUKFVIVlhYMFk4WjhbQFzIXyBhCGLYY2hj2GQwZTBmGGeAaJhpeGoQaqBrkGzIbiBw6HGocuhzsHTQdah2MHbAePh52HtQe9h9yH7QgCCBsILIg1CD2IQ4hjiHKIiQimCK2I2Aj0CRWJIgk0CTsJQ4lQCWOJaol2iX8JpgnQCfEKBAoKihAKFoocCiKKKAouijQKQgpJiniKkgqsiuGK+Ishi0CLUwtpC3gLgwuGi6cLuAvEi9GL5wv3DBCMJQwwDDsMSgxXjF2MZgx3jK2MuQzLjNKM8w0GDRcNNI1PDZeNoo3FjdON4o3yjgqOHI4lDkCOUY5kjmqOdQ6Ijp8OrQ66DsQO0Y7pjw2PHA8pj0sPZg+Dj6iPso+6D8GPxw/Mj9GP74/zD/iQJBBCEG2QiRCJAAAeNpjYGRgYPjGsIlBkAEEmICYkQEk5gDmMwAALxQB/wB42o1Sy07CQBQ9bdGEhLhw4cK4aHSjJhQQRYQt6kJiiC/cFiiPiLSUCpj4HX6T7ty68RuMH2A8Mx0a0m7MZGbOPT33MfcWQAavMKCl0gC+uEOsYZ1WiHWs4VthA2X8KpzCtlZSeAVz7V7hVfIfCqexr/0onMGmvqXwGzb0he878no9xJ8GNS84RwN1mJjCgY8JBnAxon3A7ZIxYdN+5j0kCqQqqZ4RBegTdSUTEDmYo83To7XQ7VITcHmoIMc1k8tCj1+feIuMPfJDegjfEXM43DmyHtks49sYUyniPJLZwZnKeJrIt4ca1RNqRTRXRruiosdc4jU+CoyU5yqhiltcoIlLoqRXNuaXVJgxxV2sQ8uZGrgmI6xltk9loOJNIw8LxzyrfKuNB8YUmi5Z0aEWp2ThSO4yirRO/lF7U3a5wyp82VtRe0eigZyDKadsM+NMKb1IuZhQk3ZradZhrTfM4dCq8WzzNlmP+FbktAt8R4V1luS/JV5+GE3OZD/G9B0wtsg0/AP9pH7rAAB42m2UZZAdRRhF9wRJcHd3hzf9dc/MwyGwwd3dAgmEJUgIwd3d3d0tuLu7u7tD8Rfbs/94VVu3pnb69Ntb92zPoJ7/Pn+N70k9//f5858fegYxiAmYkImYmMEMYRImZTImZwqmZCqmZhqmZTqmZwZmZCZmZhZmZTZmZw7mZC7mZh7mZT7mZwEWZCEWZhEWZTEWZwmWZCk6VCSCTKGmoaXL0izDsizH8qzAiqzEygxlFVall2GsxuqswZqsxdqsw7qsx/pswIZsxMZswqZsxuZswZZsxdZsw7Zsx/bswI7sxHB2ZhdGMJJd2Y1R7E4fezCaPdmLvdmHMezLWPZjHPtzAAdyEAdzCIdyGIdzBEdyFEdzDMdyHMdzAidyEidzCqdyGqdzBmdyFmdzDudyHudzARdyERdzCZdyGZdzBVdyFVdzDddyHddzAzdyEzdzC7dyG7dzB3cynru4m3u4l/u4nwd4kId4mEd4lMd4nCd4kqd4mmd4lud4nhd4kZd4mVd4ldd4nTd4k7d4m3d4l/d4nw/4kI/4mE/4lM/4nC/4kq/4mm/4lu/4nh/4kZ/4mV/4ld/4nT8Gj+kbmUrv0H+zt+p0zMpMZpjZLGZtNmZrdvuzklfJq+RV8io5lZxKTiWnkpPkJDlJTpKT5CQ5SU6Sk+SEnPB8eD78u0JOyAnPh+ez57PfI8vJcrLns/dnzxd/X7yn+F7xnuL7ZeB976u9r/a+Wk4tp5ZTy6nl1HJqOY3nG79vI6eR08hp5DRyGjmNnNbv08pr5bXyWnltPy+5p+SekjtK7ih1Bt6rzcZszf57kztK7ii5o+SOUiXPPSX3lNxTck/JPSX3lNxTck/JPaUkz10ld5XcVXJXyV0ld5VCnvtK7iu5r+S+kvtKIc+dJXeW3FlyX2F/0Rl4DjObxazNxmzNfm7YY9hj2GPYY9hj2GPYY9hj2GPYY9hj2GPYY9hj2GPYY9hj2GPYY9hj2GPYY9hj2GPYY9hj2GPYY9hj2GPYY+hrDPSpr5Hl6W1kefob+hu5n5d9zgPPpWNWZjLDzGYxa7Mx5eh5rj2v51nPs55nPc96nvU863lu5Oh71ves71nfs75nfc/6nvU963vW96zvWd+zvmd9z/qeW3mtvFZeK68rryuvK68rryuvK68rryuvK6/bzyv+fyn6UfSj6EfRj6IXRS+KXhS9KHpR9KLoRdGLohdFL4peFL0oelH0ouhF0YuiF0Uvil4UvSh6UVIzpG/42GGjxo0e8TfXWJDWAAAAAAFUhzzUAAA=) format('woff'), + url(../fonts/dashicons.ttf) format("truetype"), + url(../fonts/dashicons.svg#dashicons) format("svg"); + font-weight: normal; + font-style: normal; +} + +.dashicons, +.dashicons-before:before { + display: inline-block; + width: 20px; + height: 20px; + font-size: 20px; + line-height: 1; + font-family: "dashicons"; + text-decoration: inherit; + font-weight: normal; + font-style: normal; + vertical-align: top; + text-align: center; + -webkit-transition: color .1s ease-in 0; + transition: color .1s ease-in 0; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +/* Admin Menu Icons */ + +.dashicons-menu:before { + content: "\f333"; +} + +.dashicons-admin-site:before { + content: "\f319"; +} + +.dashicons-dashboard:before { + content: "\f226"; +} + +.dashicons-admin-media:before { + content: "\f104"; +} + +.dashicons-admin-page:before { + content: "\f105"; +} + +.dashicons-admin-comments:before { + content: "\f101"; +} + +.dashicons-admin-appearance:before { + content: "\f100"; +} + +.dashicons-admin-plugins:before { + content: "\f106"; +} + +.dashicons-admin-users:before { + content: "\f110"; +} + +.dashicons-admin-tools:before { + content: "\f107"; +} + +.dashicons-admin-settings:before { + content: "\f108"; +} + +.dashicons-admin-network:before { + content: "\f112"; +} + +.dashicons-admin-generic:before { + content: "\f111"; +} + +.dashicons-admin-home:before { + content: "\f102"; +} + +.dashicons-admin-collapse:before { + content: "\f148"; +} + + +/* Both Admin Menu and Post Formats */ + +.dashicons-admin-links:before, +.dashicons-format-links:before { + content: "\f103"; +} + +.dashicons-admin-post:before, +.dashicons-format-standard:before { + content: "\f109"; +} + + +/* Post Format Icons */ + +.dashicons-format-image:before { + content: "\f128"; +} + +.dashicons-format-gallery:before { + content: "\f161"; +} + +.dashicons-format-audio:before { + content: "\f127"; +} + +.dashicons-format-video:before { + content: "\f126"; +} + +.dashicons-format-chat:before { + content: "\f125"; +} + +.dashicons-format-status:before { + content: "\f130"; +} + +.dashicons-format-aside:before { + content: "\f123"; +} + +.dashicons-format-quote:before { + content: "\f122"; +} + + +/* Welcome Screen Icons */ + +.dashicons-welcome-write-blog:before, +.dashicons-welcome-edit-page:before { + content: "\f119"; +} + +.dashicons-welcome-add-page:before { + content: "\f133"; +} + +.dashicons-welcome-view-site:before { + content: "\f115"; +} + +.dashicons-welcome-widgets-menus:before { + content: "\f116"; +} + +.dashicons-welcome-comments:before { + content: "\f117"; +} + +.dashicons-welcome-learn-more:before { + content: "\f118"; +} + + +/* Image Editing Icons */ + +.dashicons-image-crop:before { + content: "\f165"; +} + +.dashicons-image-rotate-left:before { + content: "\f166"; +} + +.dashicons-image-rotate-right:before { + content: "\f167"; +} + +.dashicons-image-flip-vertical:before { + content: "\f168"; +} + +.dashicons-image-flip-horizontal:before { + content: "\f169"; +} + + +/* Both Image Editing and TinyMCE */ + +.dashicons-undo:before { + content: "\f171"; +} + +.dashicons-redo:before { + content: "\f172"; +} + +/* TinyMCE Icons */ + +.dashicons-editor-bold:before { + content: "\f200"; +} + +.dashicons-editor-italic:before { + content: "\f201"; +} + +.dashicons-editor-ul:before { + content: "\f203"; +} + +.dashicons-editor-ol:before { + content: "\f204"; +} + +.dashicons-editor-quote:before { + content: "\f205"; +} + +.dashicons-editor-alignleft:before { + content: "\f206"; +} + +.dashicons-editor-aligncenter:before { + content: "\f207"; +} + +.dashicons-editor-alignright:before { + content: "\f208"; +} + +.dashicons-editor-insertmore:before { + content: "\f209"; +} + +.dashicons-editor-spellcheck:before { + content: "\f210"; +} + +.dashicons-editor-distractionfree:before, +.dashicons-editor-expand:before { + content: "\f211"; +} + +.dashicons-editor-contract:before { + content: "\f506"; +} + +.dashicons-editor-kitchensink:before { + content: "\f212"; +} + +.dashicons-editor-underline:before { + content: "\f213"; +} + +.dashicons-editor-justify:before { + content: "\f214"; +} + +.dashicons-editor-textcolor:before { + content: "\f215"; +} + +.dashicons-editor-paste-word:before { + content: "\f216"; +} + +.dashicons-editor-paste-text:before { + content: "\f217"; +} + +.dashicons-editor-removeformatting:before { + content: "\f218"; +} + +.dashicons-editor-video:before { + content: "\f219"; +} + +.dashicons-editor-customchar:before { + content: "\f220"; +} + +.dashicons-editor-outdent:before { + content: "\f221"; +} + +.dashicons-editor-indent:before { + content: "\f222"; +} + +.dashicons-editor-help:before { + content: "\f223"; +} + +.dashicons-editor-strikethrough:before { + content: "\f224"; +} + +.dashicons-editor-unlink:before { + content: "\f225"; +} + +.dashicons-editor-rtl:before { + content: "\f320"; +} + +.dashicons-editor-break:before { + content: "\f474"; +} + +.dashicons-editor-code:before { + content: "\f475"; +} + +.dashicons-editor-paragraph:before { + content: "\f476"; +} + +/* Post Icons */ + +.dashicons-align-left:before { + content: "\f135"; +} + +.dashicons-align-right:before { + content: "\f136"; +} + +.dashicons-align-center:before { + content: "\f134"; +} + +.dashicons-align-none:before { + content: "\f138"; +} + +.dashicons-lock:before { + content: "\f160"; +} + +.dashicons-calendar:before { + content: "\f145"; +} + +.dashicons-calendar-alt:before { + content: "\f508"; +} + +.dashicons-visibility:before { + content: "\f177"; +} + +.dashicons-post-status:before { + content: "\f173"; +} + +.dashicons-edit:before { + content: "\f464"; +} + +.dashicons-post-trash:before, +.dashicons-trash:before { + content: "\f182"; +} + + +/* Sorting */ + +.dashicons-external:before { + content: "\f504"; +} + +.dashicons-arrow-up:before { + content: "\f142"; +} + +.dashicons-arrow-down:before { + content: "\f140"; +} + +.dashicons-arrow-left:before { + content: "\f141"; +} + +.dashicons-arrow-right:before { + content: "\f139"; +} + +.dashicons-arrow-up-alt:before { + content: "\f342"; +} + +.dashicons-arrow-down-alt:before { + content: "\f346"; +} + +.dashicons-arrow-left-alt:before { + content: "\f340"; +} + +.dashicons-arrow-right-alt:before { + content: "\f344"; +} + +.dashicons-arrow-up-alt2:before { + content: "\f343"; +} + +.dashicons-arrow-down-alt2:before { + content: "\f347"; +} + +.dashicons-arrow-left-alt2:before { + content: "\f341"; +} + +.dashicons-arrow-right-alt2:before { + content: "\f345"; +} + +.dashicons-leftright:before { + content: "\f229"; +} + +.dashicons-sort:before { + content: "\f156"; +} + +.dashicons-randomize:before { + content: "\f503" +} + +.dashicons-list-view:before { + content: "\f163"; +} + +.dashicons-exerpt-view:before { + content: "\f164"; +} + +.dashicons-grid-view:before { + content: "\f509"; +} + + +/* WPorg specific icons: Jobs, Profiles, WordCamps */ + +.dashicons-hammer:before { + content: "\f308"; +} + +.dashicons-art:before { + content: "\f309"; +} + +.dashicons-migrate:before { + content: "\f310"; +} + +.dashicons-performance:before { + content: "\f311"; +} + +.dashicons-universal-access:before { + content: "\f483"; +} + +.dashicons-universal-access-alt:before { + content: "\f507"; +} + +.dashicons-tickets:before { + content: "\f486"; +} + +.dashicons-nametag:before { + content: "\f484"; +} + +.dashicons-clipboard:before { + content: "\f481"; +} + +.dashicons-heart:before { + content: "\f487"; +} + +.dashicons-megaphone:before { + content: "\f488"; +} + +.dashicons-schedule:before { + content: "\f489"; +} + + +/* Internal/Products */ + +.dashicons-wordpress:before { + content: "\f120"; +} + +.dashicons-wordpress-alt:before { + content: "\f324"; +} + +.dashicons-pressthis:before { + content: "\f157"; +} + +.dashicons-update:before { + content: "\f463"; +} + +.dashicons-screenoptions:before { + content: "\f180"; +} + +.dashicons-info:before { + content: "\f348"; +} + +.dashicons-cart:before { + content: "\f174"; +} + +.dashicons-feedback:before { + content: "\f175"; +} + +.dashicons-cloud:before { + content: "\f176"; +} + +.dashicons-translation:before { + content: "\f326"; +} + + +/* Taxonomies */ + +.dashicons-tag:before { + content: "\f323"; +} + +.dashicons-category:before { + content: "\f318"; +} + + +/* Widget icons */ + +.dashicons-archive:before { + content: "\f480"; +} + +.dashicons-tagcloud:before { + content: "\f479"; +} + +.dashicons-text:before { + content: "\f478"; +} + + +/* Media icons */ + +.dashicons-media-archive:before { + content: "\f501" +} + +.dashicons-media-audio:before { + content: "\f500" +} + +.dashicons-media-code:before { + content: "\f499" +} + +.dashicons-media-default:before { + content: "\f498" +} + +.dashicons-media-document:before { + content: "\f497" +} + +.dashicons-media-interactive:before { + content: "\f496" +} + +.dashicons-media-spreadsheet:before { + content: "\f495" +} + +.dashicons-media-text:before { + content: "\f491" +} + +.dashicons-media-video:before { + content: "\f490" +} + +.dashicons-playlist-audio:before { + content: "\f492" +} + +.dashicons-playlist-video:before { + content: "\f493" +} + +.dashicons-controls-play:before { + content: "\f522" +} + +.dashicons-controls-pause:before { + content: "\f523" +} + +.dashicons-controls-forward:before { + content: "\f519" +} + +.dashicons-controls-skipforward:before { + content: "\f517" +} + +.dashicons-controls-back:before { + content: "\f518" +} + +.dashicons-controls-skipback:before { + content: "\f516" +} + +.dashicons-controls-repeat:before { + content: "\f515" +} + +.dashicons-controls-volumeon:before { + content: "\f521" +} + +.dashicons-controls-volumeoff:before { + content: "\f520" +} + + +/* Alerts/Notifications/Flags */ + +.dashicons-yes:before { + content: "\f147"; +} + +.dashicons-no:before { + content: "\f158"; +} + +.dashicons-no-alt:before { + content: "\f335"; +} + +.dashicons-plus:before { + content: "\f132"; +} + +.dashicons-plus-alt:before { + content: "\f502" +} + +.dashicons-minus:before { + content: "\f460"; +} + +.dashicons-dismiss:before { + content: "\f153"; +} + +.dashicons-marker:before { + content: "\f159"; +} + +.dashicons-star-filled:before { + content: "\f155"; +} + +.dashicons-star-half:before { + content: "\f459"; +} + +.dashicons-star-empty:before { + content: "\f154"; +} + +.dashicons-flag:before { + content: "\f227"; +} + + +/* Social Icons */ + +.dashicons-share:before { + content: "\f237"; +} + +.dashicons-share1:before { + content: "\f237"; +} + +.dashicons-share-alt:before { + content: "\f240"; +} + +.dashicons-share-alt2:before { + content: "\f242"; +} + +.dashicons-twitter:before { + content: "\f301"; +} + +.dashicons-rss:before { + content: "\f303"; +} + +.dashicons-email:before { + content: "\f465"; +} + +.dashicons-email-alt:before { + content: "\f466"; +} + +.dashicons-facebook:before { + content: "\f304"; +} + +.dashicons-facebook-alt:before { + content: "\f305"; +} + +.dashicons-networking:before { + content: "\f325"; +} + +.dashicons-googleplus:before { + content: "\f462"; +} + + +/* Misc/CPT */ + +.dashicons-location:before { + content: "\f230"; +} + +.dashicons-location-alt:before { + content: "\f231"; +} + +.dashicons-camera:before { + content: "\f306"; +} + +.dashicons-images-alt:before { + content: "\f232"; +} + +.dashicons-images-alt2:before { + content: "\f233"; +} + +.dashicons-video-alt:before { + content: "\f234"; +} + +.dashicons-video-alt2:before { + content: "\f235"; +} + +.dashicons-video-alt3:before { + content: "\f236"; +} + +.dashicons-vault:before { + content: "\f178"; +} + +.dashicons-shield:before { + content: "\f332"; +} + +.dashicons-shield-alt:before { + content: "\f334"; +} + +.dashicons-sos:before { + content: "\f468"; +} + +.dashicons-search:before { + content: "\f179"; +} + +.dashicons-slides:before { + content: "\f181"; +} + +.dashicons-analytics:before { + content: "\f183"; +} + +.dashicons-chart-pie:before { + content: "\f184"; +} + +.dashicons-chart-bar:before { + content: "\f185"; +} + +.dashicons-chart-line:before { + content: "\f238"; +} + +.dashicons-chart-area:before { + content: "\f239"; +} + +.dashicons-groups:before { + content: "\f307"; +} + +.dashicons-businessman:before { + content: "\f338"; +} + +.dashicons-id:before { + content: "\f336"; +} + +.dashicons-id-alt:before { + content: "\f337"; +} + +.dashicons-products:before { + content: "\f312"; +} + +.dashicons-awards:before { + content: "\f313"; +} + +.dashicons-forms:before { + content: "\f314"; +} + +.dashicons-testimonial:before { + content: "\f473"; +} + +.dashicons-portfolio:before { + content: "\f322"; +} + +.dashicons-book:before { + content: "\f330"; +} + +.dashicons-book-alt:before { + content: "\f331"; +} + +.dashicons-download:before { + content: "\f316"; +} + +.dashicons-upload:before { + content: "\f317"; +} + +.dashicons-backup:before { + content: "\f321"; +} + +.dashicons-clock:before { + content: "\f469"; +} + +.dashicons-lightbulb:before { + content: "\f339"; +} + +.dashicons-microphone:before { + content: "\f482"; +} + +.dashicons-desktop:before { + content: "\f472"; +} + +.dashicons-tablet:before { + content: "\f471"; +} + +.dashicons-smartphone:before { + content: "\f470"; +} + +.dashicons-phone:before { + content: "\f525"; +} + +.dashicons-smiley:before { + content: "\f328"; +} + +.dashicons-index-card:before { + content: "\f510"; +} + +.dashicons-carrot:before { + content: "\f511"; +} + +.dashicons-building:before { + content: "\f512"; +} + +.dashicons-store:before { + content: "\f513"; +} + +.dashicons-album:before { + content: "\f514"; +} + +.dashicons-palmtree:before { + content: "\f527"; +} + +.dashicons-tickets-alt:before { + content: "\f524"; +} + +.dashicons-money:before { + content: "\f526"; +} diff --git a/wp-includes/css/dashicons.min.css b/wp-includes/css/dashicons.min.css new file mode 100644 index 0000000..f494aaf --- /dev/null +++ b/wp-includes/css/dashicons.min.css @@ -0,0 +1 @@ +@font-face{font-family:dashicons;src:url(../fonts/dashicons.eot)}@font-face{font-family:dashicons;src:url(data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAGBQAA4AAAAAm3wAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAABRAAAABwAAAAcbYyDmkdERUYAAAFgAAAAHgAAACABIwAET1MvMgAAAYAAAABAAAAAYJYFaatjbWFwAAABwAAAASoAAAKC/cQq02dhc3AAAALsAAAACAAAAAj//wADZ2x5ZgAAAvQAAFSXAACESOAO2gZoZWFkAABXjAAAAC4AAAA2CEgozmhoZWEAAFe8AAAAGgAAACQPogifaG10eAAAV9gAAAEcAAAD2GOq3ltsb2NhAABY9AAAAe4AAAHu4pbA6m1heHAAAFrkAAAAHwAAACABSQC1bmFtZQAAWwQAAAGKAAADLCbHbA5wb3N0AABckAAAA7UAAAmnz3C/rndlYmYAAGBIAAAABgAAAAY81VSHAAAAAQAAAADMPaLPAAAAANCh83cAAAAA0KztU3jaY2BkYGDgA2IJBhBgYmAEwq9AzALmMQAADtEBKAAAeNpjYGY/yTiBgZWBhVWEZQMDA8M0CM20h8GIKQLIB0phB6He4X4MDqp/vqqzXwDxgaQGkGJEUqLAwAgAMrcKp3ja3ZA7SwNRFITPNTGy7N3jglosWCxIkHRBVAw2q6JJQEWMILGQ9dHEKjZCwCKNhZ2t/8hGG1EwWKuVOvehjbouCVhYWzlw5jAwfMUQUYZ6N0oidRLHaRLdnBVx+jcoon4azn/AwRACjKGAIqZRwgIWUUEdO2ighRMllKMCVVAlFal57ehAF3RJV03VbJq6iU3DtMypObdZ69jAjicJUUol+BhBiHxKncAMopRaRg0x9nCItiKVUb4KVbFLFdrXoS7qyFTMWpe6a5qmbc4s2Zz1bZgknYHBLH/xJ7/zG7/yCz/zEz/yA9/zLd/wFV/wKq9wmed4lqd40jvymt6Bt+9ty1huybqsyXW5LJdk5HbcO/favewt8/cSOfpBi77U+n4X6N/rG5Q9gGkAAAAAAAH//wACeNqsvQd8FGX6OD7vzM7Mbtpmsy1tN9lsS9nUbSFlEyD00EKLBaQsPWAUaRJQMSIqJTZsiA0Re1TkLBxnO107clFPDz3Uk+PUO/WOrwdJ9vX3PO/sJhvE+973//lnM/O+887MO++85enPM5zIwR85xR/iBE7iNFwKp+W4Sp1NJ+hterOO2JKJTk9O9T5CH4s8QneQ6Y9EHuEPRZvIDdzPtOdnQqMfcj8TD+W5nznCJfxVchzPhbmo/ILUC3X6OU5DgiFiMluJ2SoEghoiS4Y8YjLIabwswc7Kh0gwEAzxwUAVlFcFxf3R+l1Z626pKX1gWknd7JYVNdGHovVPWSzLLJacUYtNI+y+KRXy2CWXXOIr8KY2+3It0+DUNIuwiX91V26q3WXdUpaZa0slKdGH+FefYmenW3ICY9Mr7b5LLlkyVq6Y7Cto0q0cmcPq5AjxcBGpUVZzGdAntiqTUWeQSgjR2Qtcfp0vQE4JnS1r1rREaEoEUlm9piXa1LKGppBTa1r4Qy1r4LUF7t9QxyfSR9CXaawes6whbg3xe0UC3Yqb1HjHsuj66Ppl/I+kKxLdz7f2TRHyabtq0x3L+GtZOW2P3Bl9OPooP4NqySmaAvVGuF1ym1zDZXIurg7qNaVrSRpxkwYS8LldBVpCXAHI52vYsSQTyWA2BWRikrREyne70htIiJhZaZX0j02b7mj5chmRmpsbGhqebphIn7AMa7n9CrKw/1velJdX0JoX/RaTMrLHiifo7iuUW+j8xsaGp6VDWHJ7yzArESc2QwWNjc30iWVfttyxqW8WVHB+Xj5vjX7D0jfIPezEpk1k0Sa4xULnNTzd2MBxKpgfYXinNk7P5XBFOEeIzucqIW6bbC+QoPdNXltVgKsyGaQCl0+06Vga8FaZdTbo0IFD0VBb9NA1tP2ah4pqa4uEk0W10cYjN9105CbhIDkFyU1LzDm0B/pUyQud/KGi2kiktijaBDfwz2DxEVXfiSV4j0jOUHkpvZ8dcPzAnBBgRDkcPtFmtOm8fKtwsu+AkN+fGSGnREPvd+GIaMDx/4Y7If1FOsmp4Z0cOOvdJICzXgwSOY3YbbJU4Hb5GuBNAxrljEzEPHroRdKUfaHLndH/+gXCNWurvp1HOzo8HR6Pp6NekEhTFp7LpoekXOVSvdt1wdvt/RumV42Ai0o2ejpI59xTdC+cyoBT2fRFWH8R7nUpKvVzRs4GbZdUboI9FiSujGDAYdbwJgkXGps/sBqlzgX0H5dHvxp/9Jbx/Dafb8EcolpD/0yyibWoune3dbrFarVMt1qEnxf4fNHLxt9ydBxvvZxkLJhT7aF/pidI3uqfo3kWS+xCTiAe4pHV0l5Y/yXcGGV07bbBkSVeHNoSYsSBrCd2HM1a7BhYevEjPx5Z4leyIffL6tqi3u9wrEVDUW1/ZhizYUuxZSBDTp2jrEvVjfm+KTjoltqW2l1Gi8W4CzK8C3P0/sSy6KeYY+CR6+Lul0fLrTCiHIFhdEm49MwydKcKupK4g6YAri6V4SDf8ZtJr4ya0KT3/eZHejJAPwy85SfmH6GwacIovXT/weg1B336URNGvTz54I/0b4G3AqQULvgnK2x6BeabAebbIZhvmVwFPLugTFVOXCHBWwXzJE0UnJBaSJrKXlDGl0PnhIhXZbpwasCTpRMESSppWBRavLUsjah0mSX+TST9sj9s1Pof3PvlwkVfPHGj233ZB5ervK76cePq9frg8gUTStc8MlvOGjN8yjD614Nbjq44I2rd1kyzrSAl+Uz7e5sVGB6W1dAeN1cOb2+XZHhvSTba/C637A663HadNxB0B82BoN9mNJmDZtlk9nJVAZ+rQDLI6k+zg3cevbN1Me1Z3Hpnz53BzE8/MQ+DkhltxMNKhpk/6a863d19ulvIXz4TzsAln2YG4UzrYlK0cDaUBHJiJbMX0uHdeC1rV0QukKcCjIVR4UwZgEVUbkEUbG5lemdUBfysDVLDS6NGzW2nb259kX64i/aEhZwxlavmEr585aRJKyfJZS9RSn+e2+5vpM/m0W0RgdxAGlbNHRU9Pqk6CFfgs0ih3AbzWIcrmlSpzHIZzE9J9mqIN5Dh0Bc43EErzNFA0K4h9jRBOHkn0c1/NPTCsrt/LDhJ76f3H8l8hZQ+fhf9YaCUzCFzjmRsOPi9tHdT+6stc1+7lbaTrv0kY09vZ6yAdNH2TZ/dO5tDWBmB91XWUhas5qJfriYuX2WWVPkOd0DF3ttURQBS+eHH1stWtl7WwXoJkhbi27CBvkOfoO9s2MA/uGbvmjV7Yf56ercSj5CPoA0v7s+EvTBuz44de2A7jhetie4Ph8PQnlQuAmu7DeaFFqC3GcYgD2ZICcyRIFfLNcBqn3CO9W7T2fTQJG9si0P2ILTTCeeGHMN5VcKx3Abv4GDvcKyottfBQC7xKP99K4otsMIj2GF8KyLM/mhCyaGhV5AuBBsxEKL6BAo8EfZHUyzsqnAYcLonnocTsSy8N+KDsLSX0QjWoVSCLUYluIOwMGCVwFIAiuFkjGKgPZAR9vb2huP/SD/0H4/TD0J+y5r+JxJOIy7hYMyPwLNk6OU8xCV6ux5oCQcb3YBPcJrMLnuBSnY57AVyICicjJrKIhW3CCeFk/3DW2oBioX50vZMX9Eh+vLs2fTlQ4X+zHbhJCnuO6haXYt9EWavFv39n299Vn5+9+7n5WdvVXCePFJeA9SgDikYINIQ8dmAfkN0Jkt3bLdYaEvfAVXJAeu06F/5rB3Rk2Pk1u1AY9Gp/cPEYwcspP8mPnNH9K89HNCV8AfvcQRmip1rhZW7jGuHonS+jDjyVWmET8+wEpi5IQJz2JSRzuM0dkFpgNGMLlmyF7gDDi8sPJdkNAAFaTKbAGkAzRMiCHjZFS4AlJIpj+g1JKRyw3leQ1y830f0eLt8ZOHzP9H36Qv0/Z+eXwh5UklGk8qfnu97nawkE3/cseNH+gy9iT6DOVJP319qSNMbr5pu068k89+7k5jbfLPNpmxRqGrIz6c/Gl1qtUFrMGyebl9qdxggO72dPEdEQW3SyElzPuw9Rja99T9C8derNy5eLIxSHrXwrCaIs4Y8dCJrSF+R4Ffz3nKSRPa9snKBPGG8ZXhhkVqctaXueEsL/w+iVgl80EeSJV4g/iBR0yPRXP55Oaul5cphj/zhT3SbcFPfyQXk2m+foW9HdxZbOTE2Z9tgRslcMluxHK5FAhB7yOYR1/VuVTX3Hwe6hm18a3S/3Hb6tLgO1kTP4AYwcQbpkkU5DWiLEqjN5y4DvJgRdKepzAgJgwHnL4uEmWOrHh+X9d59D8x/ds+KqvTsrx65bd584clzlfIfbb6wwZW+kwyf+pfyLQfePXPlH/pbW3edqxCnlzhAS6qA8krh0vEd9UBv2wgAErbBC2qIDZBZdH/fFOJRdfdNUXVH90f3C/kIMeS2vinR/eIxeMF2BACYklNYN59Ap7oQBxZIlkGuwMtAwAAc0JFYuaq5Zep1tAc5hRa+tWUNbggJesKQiut2jH+O9hDPmhZyCstpO2wpAAwGymDJMByHz9UAvNVBT2dxFi4f3wuf5oSHiIJXb7MLXhLfYJlqeCBYi2pPn64lz0fCAKbDkTDQQ/EdAvlTyrjWFp15iZdCRbVhfOzgBnTtyf5MoZO203ZG/yjvH0YYRJwBIMfgVfNd6YwSF3Vp+MrKgS/Av6iaVFj7Lv383XdvHDexvyMj0nkjy1ZLuyNSQ0mk7x9wTAretfOrIv2dVz7G8kJtoT+BH9AyjmB4/E0Nkj0OXOH9uDh284Vk78C5KqsqD9Z9lZU3fyGl+caN8535J+zFf/du5Q8pCO5+IiYZLWU1LRUKbiuaWOOzZ0mSKr26cUn74pHeZLktdpsPqzjzEi4HoZchPtqjzm6Z0jZ7zshihheL+EOeMTNmjDGZUooWTx4OJcb4XIkwvKhn1OJZ7fcCnuPyA+mufCldPEZcnlDIc+Yd3BMXUMtd9CbS/M039MA3UmPI07egtKmpVHW3JxTp/e4b5QQ+Q4Jn7Brgm+yw/ryAczmAnVbekMYD7CzjfSE+I0aDqc5K41hWiqVCDrmT1Lx5pc935Zv0dbqIvq7k5Y7W1o5WPjsxiUos+R3DLXLbyFUPv/DlCw+vGhnPRO2t7IKE/+hmlrTX4j3AY3eRUwx/JivcVPwn7UUcibgQoJAhnmO8r8KDKfeY8S67H1kwYML8QaPdaPfb/V6gJ6S9sLj6O1TNgLIBaYdFA+D1cK9D6Iyw40gE+XPkSaS9QEcpsBBGyG/DdgiYKvCvG3g6zIjHIv2ZuFKAc2Qw5qx7Y3BUuR9THeYH6hE6sTGDa2pIlYzxx///VK8uVncRieVZvVibuK6/A2kUzLFaWWVYcazOX3vPrIT2wRr/xWsSJP/EdeI6mMmchscjAIrr6Aq6iiP8ITh3TNWtnBOPYSmCUL41ds4Qv080YCmQYjeT7ezcIfEYsMfsHJBjUIrXR/f/x3OyQvPKNdAfeYy29DLqkuEu6A+FM1RSo82IJFE9wSkuJuSx/5w6O+yVDVad58xLxAOTknTBrr9DIe74Q0pKuqCHuyJ8K/5LjdA968Kh1tZQmO17kSccuutdhQMdRqFYfEOYCW+/QHKKjyD1Q8zEToJm8Z4t0b9uoUe3fCs+MidqJjduRB79j9wfpdPSaQVjAW8xSP/4fRkOwCfCyXtJwdGOjqP0c/oS/fxoRw9ZSu6KviCdHizqOEoK7v2QLD3a1yao6RUc4/0jjG/TMv7obLqKBzJS1hCzhjDipPCjK674iP4RSJM/Yk74HU5aWE6Av3CSRRROPvESUshu6/fiqcjQy4fgTA2+l4aIRIBnESfRi0TvFPXEKZykPbAQ7r4EJ0o72Q0TsOcb8gF9ZRb1UM8s+gr5QG6L7p9Aq6JLsVb+NvLeBLKOltTQ3dHoN98A+ROu4RLwEz6LPQkoQFY/8fCtNNyOOP4SuoBv/WV12E4sZ/NPxJlFiggBCigToQ+0MAVmMowp60/kg9u4NIUHNKUzqhInYojoY7ym0WBGwYAJOUFiEl/bcvjwFptzGeDTFzQHrpl99dUvXJ1DD9uvyRDk/KespKlCbvsdPXVYvazvK/HYjXdF/9R54ezOztmBSrjotQzVBrgEAR7ZT/ZLhVIRW8eKODGoQVme/k/0EXrLe6SNPvIpmUXa3qW38GveIyvow+zwPXoLmfkpfZj7X+cDlx8i6WUkP42km841IchIkn5iy5YT9AfYn3Mi9C3fcmLgGoQ/XTG5mRnWbim02zsUEzI5T77JKOhYx+mIO0RifWgyy7DoBjjQojDivgjgYOJxuMthzZa7HarmmPSuCGV5xLPvm2/24SRktFRPtOnjjHEeXLCecRkfD6UPkYZyck3cxKFtqieJ9BxCmAE6IyR5YZiBssjDLFIYgILYbUap8bWS+vqS3lMl9SwjppTU936nau47QE4xtEfyebUlPzBsYlF/R3WLy0QIEVIzbO7agvIJRbki/1K4vqT/SqlRqaO+5DWljvqSvumqZhphOJT2JDmWXLRq2jCkx+Gl8vJU2iJXmc2MREe0AmbrlSX1wJxqEugnM6MM3AxmIj/OBYH/9ifw4xkx7I98Nv9f5BlNiQQj7CMM/Yvr+g78+B9z4dqiwbukRkYB1AKb3f6fcoljNfgenPNsaUe8bTA0fEJeoWrxofjoM33naFbiq0ii8lxEV/3HB/Mcp/Df2I4jwOsbucyh9AqyTzAPYKKY8aFAKvf0H0fZAgCdQ0Bzo7wZGAnSJeRjFlNG4LWzc9EmoGzwLMcpcnCFP06CJ2VyuchjwET0F3BGuyb+UJElKCbhD7268/aT9IeniSRNpylIaQj5AEkBENMUoNtPiseuu+Mo/dtn9HH+q96tNEU4CTRQtAlYD8Cqv/I8DTySsxurTCT2dnqWCEia1b5KGon0NP3h5O07e4uwNqyVIYUwPo0+TqZ9RsxH7yByfweCTiShsB/6O+J9qWJ9eQTGNJnpRYBYJkYFjePeadOgsEq0EfkI7YHae5BG6TvBH6L384cQCdP7oc/mqJojyJhGkDeNkK7omnAvY1Fi74RjhfSTUn8M5wOdCK9h1wkaAQYK7mM1VOGdMConabu0N/5AxAv4MCACs1FqhzI6hs4Bx0S4o3IStN/MdC+yEyC+24U/uw7GqApFEtKLkyfTb4JB3/x5Gzu346u/88LBrdcLn79Al7ywunP7js0b58/zB+FZr5BX6EeTJ16/Van7dahbHas7EISfF+sskCX8AVIRXenECFcffOEdpIy2d3bMn+8LBuk3k4XP4QETJ5NiGqIh4gn6583fuHnH9s7VL5DbGT8C3Q/ELMDhdDaDDZKsQ8GJrgwl6/kmFRMkuQrEdQ9fHa6quvphhLXR/WwhiOuu/6ggeqcnnJsrlBV8dD3A2Cy2QNjaUNaoekAaacNFqEB3JrpHOArUGbHFV6cjlvKtQmccuEcwQzxFtUDyt/d3tDN4JxxTWI5D2AqYbizB+duewFbE+KDIWTJI+7l5RiZ1RCo9lg4FAb1bUYwo5KNYAJKh4lKFkBfywwqT3K6kMboDqSLxGOP0rKjGYM/0BYK4A344xDCMQXz3moPv3tifeeO73Zulm5ZUllX97tJ93whfp71+LfLG17ymy85dcpN+wbpv9lUofUuuBZrkDwDRzShdAZ7fLMHOHRB9OOXS4FFIY3D5jmAZQVEXcON+XzCNyCb+Fb6Z/OXyy2lu9ADNvfxy4X98hbr8yqZmf2ACtZGT5K6lo37YveLv9Om/r9j9w6il4h/o0dOn6VFSdvq02tOUwvOqcV5/c3P0tn8e2vBo0ep7Hvr73x+6Z3XRoxsOKXh0kI7QM86z8ZdyXjcsOpENv13nNeI2OBoc431N+YLZquh9sLuG9ngYl2BfUJWd5wnDnyev70SexyO0Il8M03PfxGATEANNQVyxTI4buxOu3AgUKO3ZCJfn4e1RPsZO84dWZYbrkTioD2eydUEMhEiHpMOch6vGFZ0hK2LF+E+GzpSUBR77SQ63Kz0YcKiEQLrblS9L6WZTvurwDa6lQpa6oDykqrdV2h1mk0oYUVFZ5fN5K20evjQ/M9NwJ7198733LiM5JMe+aNFi+vmixYsXkQKp7Aa6/h7BJOXkl6hKbZVINZZWjBAEk9Flr7TVCQ1l+VbDNN9V95E37102YUI0ezGxLYY/+ufFixHm/YJvHMBQQjztRO6NcYWYqJoRfOCmiPYYmwXrCHgdmMfrEvhPs1KXE2AnIgKg1uwDUsJTwsne7yIAg1Fk3qPUAosaQHI4XqnC4uJSAYQuDehQtQCX8xg+L1EghsK+eYGErjKJtjLoE2KLL91Erk3VjUCi9zsEFrqdB3bSq4gnfcb8GXQuEoL9HUgCAjbFf6AzisJAPc689NKZJcOG0YdjRORQBk09MI/xndNjsjUbUIacM8bNw/srCmazHxaxM8aPxzdZjeIhFCH2dxAPKqWOo4wPcUp8w5Pw3nCdAcmBng5UckUia1pQtAgUDwoauThtzsZRUvAWMoFGfPwgge4QTu4kwosXw7gduvhFGqVf0uiLF1/8IhGEk0rJTujzpp3xUgBHcDXH7AYG3zNZebt4v8pq1g78x2Yi2YqjyGgf4N094jGpEXBpLvYKNKcqqOA8P1LqbpdN0AGIQ+Bjl71VRoMslQDXS15/f+06r2/VjEWrV9Ho+q0rfN45S3bd+0dv1fL9wE5/F17w9KYJzTka7a7LHps0ORoltnzb+Cmj/3TfBSUI6Qh5B+a0Cp6LvI6deGVb0EvsQZv4+of0ZE8oOjv0Ecn8MMQ/gCIQmISdOL+62DpoZG+oZxyHHXWnLpQMGFB7xP9aPszQSRhB/KFzZkWDIr4C0unsDJPFwbOz2dihjNYHsGQxt5zJqGQzIHIZKBujTfDb/UGG1/0oFLcriN2MeJ51IB5qiZFdCh0L55VzRqYcj18D7KbLbTR5B4YBrjHjO7SOuqqwLCWDJ7nY4A2PeavTjJqk9LQ6u8WkyzLmlGYajZnmlFRJTkkuX0B24lVXOZ0Nk4KFOXqDscZTkZfnzTQbjMU51qycqqYpxSXZWZWFWearlA4gXYu9zqSMYvqvSDj6+GhVhU+fbTbn2mCThJQUc0CfnJSckqnVputSK8M1r4bpJ2VZ2YV1GaJclpc5IiXFkq/VquXU8SabrbYwM1PiNbm5TWGAwQ7SJX0K42ZgFMSgwoDnf6lEGCgTnq0vuSVkfPL6G5a9eMtYnenVG6+cPk1wDhbeDIWvYCG/75KJflva5SRj1Kve3W/T6NYDH08Yf9UlEwP5ZxdyvwJPz9KQ4GqPy/ZisjNcPQPbf1MHTNzjqmZFz4Lp/5c6pL1AknQinoylZ9dxbpkiQjY/UwqzjRkj7VUkiijth60H9QM45Mi9KFpRhOSMyI7L49I5K+fiarhRwEO3cnNQGgIYM+hS5jUjTYJ+SZmwQJUEiFewC96gV68bMncxY5f9A9NbtMMKgYrEeEkJYWIqqcJhz3bZJ10wc8TwgoJtna1j84tdjvNKyysq+59YcnzZ8cU1Xx/9ZGF1tTV3eFVubnVg45QZTZY8S249nfZEwKzRqjVky4LGgvz8vNASmoZWXGFk1aRDyWrdVLfTOabpolv3ZE9P0qiHBdqX1tVFmXpkH3k6ur/M01Jnt6eoNHand5LDcXqfPqOkzGBYtHt4qS87a5vZXObNzIy+ACxWfhg5MKSPYjYvIut1I6MNZb1T1hURnVvUMwsis1MwF5GgIAZ1fOvGL+h1GwHBdW2k133R/wbf2ndgI1n7xUbarure+AVZu1Hai2c24ijgcf8Zprhlt8Uu5ACDxWnhRByeM4Bt47LThtgsYLQwytGHbuc+Z/MLbL4ox6gPlPaiDDVCTiX8d51VDIxvShinOB6g1g2wY4rCAUcG/oBhhQmMWA0wc+tAaY+SIp12CvkXoLFNXDZiBKQOGI+hM1iFqpDg17ElFdm2dNmtkazhC+6J3LdgRDaszmPR3++8a/c2vj76bP3aDbNrhl1w+dr6KAoYVANjNLC+9PCSiT+GD4dseyPxJmGbyCnxbwzfJKEUCfGU3ua26cWSw3QBLMBDy8n9JfTlW4ETRlGQ505yYtCGA8ZIBFxhR/seDdNMxjU1ivWbGYqCCfZvRLAno24vh8C/6iMiAW+tfpUxYkJnbdGdgb4pwbvitDP2XDh85KUIKkHCgOB7wmFc3oyh6zOiBVTD3/8RillBCeOYICIF2OwUTBV9Z2I7tYD7SxPaGRPROX8hzUOLIIHpZby6oY3kr7lrkEq/Kwg0Uf9xmAKDbaspxjPFxdio4pqaYqWRf49ejdMDmhSzwZO6YQ64mT2ChRhkIhWU86564gsSRKNo9lhOEAYRZldaJd66sr4+urTuibqLIcPfVjdrZj3l+c0Wy0eWkmJLdBNmZkjixXWzembVRZfW169k2Xp+V319XwQunG79yALXWafDhdgvOkZnNDI91K+vr5Ex2xcGYnUM1MZXE0qL5VhePCv9T+ekRiSzETYjVdafGRZOhsNoiQMpEN0D+3OVyepwuNcRDqMGP8wMW06xTGRgd64ytIVl+GMlw8wKQE5jg2xD0A0MkA04IJ0B4DW+KHChDNQLJw8vDI+yrZgCa33nTSPGP7gPCNUvHnxwXOgWehPfOn2ldWR4gXigre3dl64NLfBGIguvueMgSb7rrrt309PP3r55WSQSDIeu+e17y9uQRg8n4DCUGiEMsHIFCj4UYZPtfjStdSagRnsshTndjugxHEFaPCKc7O9AIMOUNYDq8mmP1Bhm1j0RFPXEwY6ygBjfomZaj7Zfez7auxEvPt+e+HzvgDEF4BkEd8zmKIL6dGQ+ENxBMS4BRNph9qxfacAQPZ4W5lg9s5px2AtUEjAFJpW3ykHs2O/BOD4tkN0JRCVgYyEmimAlBdLeQ/Tw4QEuYedhMoKO3p1pGlFvMOTmelvbG6++ad3YMZac6cF07c05Pm95bk5OzlyhmHSQkS8OMh4vkhUl1cXuqpLcbIfTqB925fiamnkjSkvrC80mO307u7yqKju7ojwrJyYz4A9IfxTNXBM3iTuPmw00MswpaLNRIWrtMXTvt5vtbrtsD9r9QC6YvTHaQEu8ilCsyiy73AqZAZMOmHQie9049dyS0+iNdwFUYxTddqNCbEhlhQW2wtGtV4+u1czJCA5b8PH22Ytr/1KzZPbsq2cvu/Dpp1aFgjnqq9RmR6hx1uxmpDQc0yrsGuKkn2jspX0nMhYb+WRVCp9Gnl7rzsq1WkfSix7nfyu8tKQuP0+QZTl1evbkke355VPKSnt7H3qoN3zmTG+RY6LPrglVjLE7XVNTU5JrPSPDE4urybhAvViQUpRUW/uAoNWXF+sz6GlCCL/fnFlVhn2l8KWKniIXoEsBF+CGcXUMb6PFtyS7G3jF4FtwK/bevBdVa04SNMsxO2+zVjHzDooaQE16DRGZwXdpw4cPx+29m/cOzytDe+/eVpQdR5g+mln8HG8cf71i7n3Dt6NuR2PvBuEMiguQsEQhuZAv/YNZf5flDd8bN/5ufvijBs8dmxBOKbZDjH6MvjHq2xsUw+/rxzfewS6drNQBC6ALHhp7Z9LF8HEq0EuonSmDtw5xoxGKBnQ+3uXIhwlvTI/L/FVnSRQ1MbvggYJ4OqDCWac63j2otOL/zRATfVRBohcoRxPIVLTzGMOOVN8qhYpagnT9iWQ9EFeKPUD/KnzIyrcrCgP2T9PJG9H9KGKa8XFi8Ym4yDRux8DgiQ45cTvQWiHg1ANBXRop45Fs8kzr+eQ4XTntuz///E5EPNbrQMFKLrFY+Jz+43Z1QZ5agQldjN8d0O2jKF3ZFN1+fJMaI0wiFok9n2+V9jIdvYg4XQdTA8siSO8KX5Ob2cChqh+u7SKE4Tqk9VFfyKNizioCwA8EQ2IQZQC8Kt+UjoZAqhugY7+7b9slKreh0OJMv8ZiuSbdaSk0uFWXbLsv+iLJfest+pe3ZPV99LsHdpyZL9gynJZi4xPz5j1hLLY4M2zC/DM7HiCGi/Cqt0jugNxC+ohLAe4QLS31CfrBuPKKOHWiU9Q5Rb1oWF037qYjN42rCzLenYwgY2lmdDczI3n0cf4JvsW2evSSm25aMnq1jVzKOHi67pr+jrffRhnC9qhrkL9B/I6wHuVBJTATkZtnWgQmQYYW2HwuLCKQ2tHCwK5zDFBGxF9QJsK5rggQOpG+KWjKRbpW9/8YFvIhnYb2nmHh1n2rUZ/ZtfDhdStXrnt4YUzb3EN7UHGi6l7TMm01rBAkyTzTVq+JzsRsGO+GM9G+iM3f2Oi30ZS4DBzmVDLTlqF9mItZSNiNdiTG/MwmLC4LAQpNh/oKNP3DNzgFlHOkd2skIhr6pggfMUuncN+BMLamP5M/BFlpb9+BSERRvADuJF3MmCpaBe+DuAzokhSWVeTWuI4bmf1dPsyamFiY0bEw3Yh8lqBYyK8toilFtfVucqqIv5nxNbVF/ceZG0d+Ua34DIr+i2sQg0PHoFkGnoruhz2zMSCn5DbgDWLc7dDKgdUi2JPoo6P45yjiyUOKJR5bq6SZhqDfQyiDi+F6LfPaOasuJ7S9bU1L73d4p2hoWdN3AO2MFeve/kzYH0SQp+iJItJHMIeczEpAgUtBp2SPea44XcxeBbkYt2gyyGJAfIQJBjV0/HmdkOvYfAE5qEGBIuToeI2GHDxPyGfHBfYTCyCz4IS9gN0CmSYoUmSIb3Bvsrmbx/wGUM9jI8qzALma8dEkRgoEjSZBR0yK9D3oxpYQWe0t6s0h+4ywMsSu0hFAYi+qbQ1l5YpF2+lvsJQ8UFiRmUNfku69cIKu9wv+PqcbFew6Q0OVhr/KMz7UmqRJ1vWOUJ0ffUY5JY+1JFFB0c0pvCnCEoTtnDNxNSPVDngtMFgQB/RnA3xZTUoDMP8avHSBrzEUNn+Utnrfan8p/UgyMzDbd4PCcnQqymTVOzkXBHCIaudlRle6mqrvKHBOW716WuD8nOj34jEGoKNXsORHxT4N57AG5sEbTG+OA2pKIwlaAIWqitFWQIbobIHgwC+uO4ijHdGFDm8Dv2DCpf5AEO5FHUP8F3+CNNNuXrLebixw5DkLi4vPv7CkqNBpsxWYMnUkJZl6SZJGKK701jUMHzP6tttGjxneUOetpAeZD1O0GO69vEC511Ex66IKB96bl5WthXvJu/QnXo7d3DjxttsmNrKbxW2+9aPNvjxHgTErXS8b9JqM9CyTrcDmKOR5azHxpKtCld7Ckhxran5+qjWnpNBbGT3IHKwe8V0+OjN2Z3qSXp+UjnfmOYrseCft4dNit+ak5+en57Bb2VpT8H0K6hkAMKSp5AzAhwQYX1RbqGQbcIcpM6+588rrQiYynzShJV2k1yE8Sf/xED2q6gaAUMKnuRraxpNUUmEkVQCj7o0eUTVX3I5rcBnRyYukw+iECBQTzPQkIqns+U6TN58pzIA0LCP1MDaoBQ2aVObYyggEJTkgL5rQRmZ2n6KPvEqP0GhFIfdzZO2LDre1vPKKh6bMnNBadT257oukj2+/t23T8pK1y6WM9kna/JvpJ/SfRzoeFO/md1wsp2a/1akqEbz3zQlPf/jt5FL3bR9flt3QOTqZ6Q3J9TF9SB5KqRHIyIYMcxWDNAzu+HWxHODYA09sIN9+wScJvGZmR8fM6HErH2KZV2T1hln9l0nCB/RniXQ8scFNyjc80fHeLMV2QcGf3wPfkoMYwWnzJRHOQ+y6qhSSTUSbj+cccKAymzLEAexlEnuIb9X78C730XmfHCXXk9D3V0ePE99++jVd/9Eukr582SXRv7Vu3Ni9sSPyEbmdnEfs71xCj171LX2Dbnj/D+QqYn6QHm1fsoT+86bLZ83csGHmrMtj+nMFtzoSrWJ1XqD1mZuXi1lVA9U+4EdhE3c6vV4nXXIk7/uq0VeOWLfj4Q8+iPJOH4ABr7O33eHj2368s7b2j5qHbn/6x+gtPofY5fQqtndcRLwNnmXA99Z59Tb2DF3sGTqvMIk4/8dJT9S23HZRx0Nv/PRThP+U5B4cNYr7Ofn3zxylf8H+A9yiyIg0zAtCkZfCiucG9Ni0RzzW3xGhKYKaOTAhQ9efiaiyQ4HYpzmJaGCsX5eeBYyYC7x0NTeWa8VZaRW8VSGeaQMkUbKSqhBxo/4L8974gaAPsAvMeuZdRCCDiMQtuHC2Ss4qkyyg9bVbHwDQERDssuioGF9U1VphTk2/ZGRTO21JLTjv0vMKUtPmXjpXWAIH1cOUo4vGbXSGDl77/ivDNuk2Nk/YGH1y+fB2/8jhS7XLK/Z2F7mShcruByuXa5cOH+m/eOSKVK1Lyii8YPaE8rHL1lWMveCC+4NbFy3aGqyfNKl+INfXTJ7Y9PT8T5+jLf6mJqHi5h56Td6wMrJi93NSsu653fTmsmF5ZP3HN2VoJTYnJnIH5bXScmZXbiboTaZBsQ5hPopOh9mqMUvfbKcP7pgemEe90X9aplnXWSyqcX+l3WT3T1lVbm+uTx9MrhHE1h300R0tVy2KrqDFFst6dAM+fqOw8aKkHHOJqVo/MnWCAHDndlIt3SI9xhVzNUxjnyYAWA6pgkiEm62ER3isKsNRCcl6Bq/jMN9sEmYZ+ExHbrLFZW4ef0HzlCz3BQtm59nSSy968pLVNPrTZ91eizHdM2bW4pWXXfyEde4FLXMFkpI9/8Lpc2Re2iNbiyqC/mqzLrNq4piRaab09Ekjxx6j0f6TY8Y3Zs5+bNWIXTfuuvaKcEtxanRxU0rK2JkLC/KG51umzZmYrugIyU5GW6EkYsA6WG9DQxEb6VKYKmDk0JgSDR6BsOub/CbKi2gKUpGK/J6UsDpSAe7auHLgLEcyO7ghXBZRaAaY4uJAjgQcDSSN8Irz6mC58xy5s5mtQworRFOUlLzmJKO7uqyzf+ts6G4IUZNSzLcOTUnJZyTzflLEWK6P76cnhXYmJayp2RNL6UfOJ0eNanyZ3uVsaAg9NT9WXHxWqsgzFdvNDOaTi7pnLzMfR/fJKqSNrMSIkhsfyl2RMCkhQAARABZQJu0Nn3kpvOXE1qUXXbR064ktfVMii/iObuFkdwe/iPaEgdDvOwCLHk4VF8Nlkcjerd30UKSje+veCNl3fmck0smJJMBNke6Udcz+0gltqGS+4XnAtjfAzC8nslsmBQoewn50F8QJRVF264NeYg4Kbjs5HAi8ueHEiQ1vBgKRjhNfbyQLHvrm230PfvPNQx1PPnn6qW4iXHkkuq237+2NH/Zt6xXfOfF1B1z6ZsfXJzreDAYi0aRvHoKLH9z3bfjJjYyGXHm8PPr8Md74hTf622MczK1EfXESHOsAisYlnS6AYB70A9Wj+RtqMoIscbqBPja7ZWY0VgQlonJOCLpFs05LbBFF9AYMhXUX7UGHG8iQU7t29e4inki0SWpksrFMmqLahC6au3aROeycrI5r7Xt3KTJqSHuIZ9euql3IoOAZJsDctYu274I/4undpcjNYj5PcftT41meGDj4+VYiGm0M9xF/AkVqFI8NEPYtil1rtIlxPiQzMAKSEQFYX4msBY4+G3so6Hv8k8wFdUhn1i3I/ITx0oADhjM/Ply7jI/06qCPgBHTieuHLlToCkUQ0xrBdYzrmUusQ1LqUO6NqfJRmolKCRS/RFRPvYk3KWAgEgcMCg5meo9G5snODETM0BB0Xz/zEn8IhiEsGvozRUMEublBGy+0w6zlJjHZowIkUPaY7wgQhzvAOdN4k1kqUyGMhJ9VJfNioEx0hwRiVaUJWlKmAhDatjPu6bZzZ9zTrX8k3fLIeHID4WxlGcU+e1Ol31VhaC2rejo0dt61k4pTiETb+ZzK+hG1lclJOtcIYXNeuTVdVolatVo2hmqrS5PcQiWra+eQ+nvdh38g1cnBHbvv8IomV7FV1I2a2lptSNWV+8aNqqIfPTZzy/SGIluhudg7to68VbFg5oWjp3pH5GRmes+rq24suG6oL48tTrUM+qDHzJaVvL2gjMB00vB8If9XZm0yIAEq2XBk/fojwtvMxUwNBYrjeoIrehE/ff2Rn46s75uCEjH23JXc27IoiwAnqhieykD61YGiziBDVCGhDPC+bCaCOUQcKsbY+e05hDiDLnHGje9daLrlND1OD994/etVewvuWXXxT8d+e2nG2Ls/h3TYIapzPz3+DLGR4fSYRUXmzSPlNMSnSt3jT9M/09/Rz9+dbRo/5i/bF697rW6YbsweuOvwpZBePI++w4++AKo39++3FKtSBPIFzaMv9/AywFgNzK2XgV5CX406bjz2FxIuGYBcvTYU28KhLNpCqlpgi80EFmJVIKgPMCKXndTyeAWgGKJchHbWXfSwtUS1+3mHepiNV42W9bX02dxSmbwOE0PSG3JSPzRWaaP3VU6UnIVB9X6xMIf+1p1Dt5o9SUlj6JisQtU9qTrVh3Q8r8nOcmi/NhQYtYJ4rNzZn8Uff8pRcMQ8qsC6VZVWkG2qyurbPMHjFprtrpu1Los2bUeOOTq/4UJhOTtttFiSuUR/eBVwL7iKNCTu7K5qRhAWbVK9wuQoCLoQRMUkJigHj/vBZgAVOIO7EG1WYU1xsKY43mhAJ1hvlYNzntPpVfWfXF7FkOBjLq9yBnFxOl8QhoPdnyEfuYeeojvoMrqdntrD/F4feJtcSlL6PqGPrs5MN2XeeKHTuJ7c8NfHSPDymuUadWaSXRUcbbfTDzKL4Aiu6JrjvsztzjSlZ164SUhLSTbJmqX/eOvffa98Sb8fS6aQfxD+muvXt+U+LFjILqz7HvbMtx9gjq57SIpTuFktVPtJMnnhj5e3a2a0ZLizy9NrpLm7RvReeKEwiqhVKj5US1IkQSB1IaKmj5blWy+YvLHp5ZP/Qy+6jL812ryCHCTSaw/0Lyb3RkeX2qaRvys2mHEf6Rnn8jFAfcx/VYZGaP4hHp3M9gZzspr5IlBGf6Bj6K8eoeMmOyZd7Bhlf0D+FNOPimtri0lxMQYd+fUjwCJKTikF/KLMmTCTS5m5bKBbJ3OzFA9UWbAJXpQhJHot2PXi2cavfngtpG0AbgCngNFGYNkBN+EK8Q1Ab7hDJCi73F6YWC5V821ls7LPp198//B4koPeqApuow1vhcmrmIthPMFNX72MvnqlZoS/8dp8UUwmoedaph1qICpJEnjbtuHe4Zr/ac76IHu8ShAN7jySFm2KoIerUgcw786XE5EnTWn75JO2Eduqsx3Z2WNNw6uqhqe5XbbkZGd29bbhL4/e0DFK4BEf3ch1yQ1yY9yeRUNMkkxEPRcMmNCXiXcTMUImFRFnvyvaslN4uMK+mX4SnXId/wjV8t2d0cmygT7lWefqd+zku4XHKp0quik68Tr++f7t/DNwGp/xFLdSzhMjgGUtqPvkmPwMOUctUTQMiYdcPqd3M9NcOYNfum917tiwZ6bRmMu/O5in1/Bl5PXzO5vp1dRDr27uPF+MrJ7mqzAkiWKFD8mEgXxfDmki6rLP6Q8k/fMyehrpGLQ1OCYeG5D1J1oSHUNrf2UDrD1o/aPw2wrNq1eo3fg0gZyEMk2pMdL7HYobUajLMFIzWm90d6i6O7p7t6LKYtDnSQd9wbQMQ2vRx5GgCXhTFgQHK8QUKvwx5k/niSTUiRb86DjXdwD2CToRWL8Wzou4TuU1JJE0HsgGmfcDhxzUAzbwAw2hs1mFPCKcTCm9+c4T7RM7br21w+3S5M29cNOKlZMq27964GpbATnFYLVh1G9uuzWXpuR2bL2yuFhWq3NG+UuO00vp30/cMFevV2lDEzpv+9O/yIin0IOm/5QqY9KiF9pV2tLShtxok1JVTPd6Stp77r5Hu6kBTctZfZ/wTud6o//4Dv9ba1n9Z8gZ8WPxY2yThh9wCBM/pnvI/O10D71nB5nHdmS+OB6O92yPH8/bQe8h8xReUJHVC0AdVwL0RL3svFhcgJiQsgFYQT9jgWMmi/DLS5RoVjE6381OWgbMp1HxipG8UG2rOEnYxZi+nuns1WhS0/e8Pi0tKVUlihKRk1MK7F5LTrZWl5TEE57nVYC+klNTtHp9hfATTYmeuL7e78+1GLItRe6CEUFf1bCKqkBuuoNPVefl+/w1wpa4AQt6b6q6aW1yanpGdnKqMZOXSGlpCWDvlAxDZmZOhlOTnGYVMvRADKo1LlQ5TGvNt/l8wY2iWkqSZVmSRE2SLCSp+I1Bn99me5/ZRERY2BOgnAf5I+w3f6zfFnJLmazp/9B3cRuG/0v/kSF92G/5b/uwTOke+sSQfqz2nt2P/OF4FAA0XgZOF7iRj/+7riQS66Tf/OfOXM4uQssfpUMH138RNx1mn9sVl5B7mUsNarwHfsxYIPYjzAgoLkCPn8GQSErXmWWdOahzB6EW/lMB6AqTyWqr8tUvbhplMqUKJElOTTVmWLKK3eVlRcVZWZmmlDRZI9xaZZHrrCuDM1a2L1x4yfltnvaShpyy4TPKn5780MhF85se/GDqXHGdPuivrqhyuI3m2rrpM2brk1w2RwEMdLZJrzdacl0OpzvXFr1vxlVnVBpeBxSVNiUlRa1XZyfpk89smbrKb8l+6Bra09ZGPNc85Ktn/XAQ+NUcgLU2hINcvirDaECgkUbgPWMeij6Xu0zl92XoYQYgeYIbQGQgW4TJpIoItxXX1hmNWXaFILFnqXNG+z272vnW/JKsYHmkzJdVki+33UbpHb7OVWGL1basSImOVbTMluxetexa3x2E7w3PmuWvKwv4y+sSeMa9nJp5bDGnYSKrkVkJK8x3u6qZmQYcAhZSiRfBbMfXMRtIBT6J65iNEV4vHkM2l13NDYGVUL/TrkNQJpMhNUZQ3M/M92P+cmRAj83qhzsSKlXC/4RjbthD2w/9hnDSTYZUKXQy64cmoFFi9Q9tP9yRUCniY3KKXf6L9gOeRY/qIBlS5dDO+mX74Y6EShO6KkGOz3wnzOf03I3pmvQoBjiH5y5fHnOsPKfXLn2dnVS8QWI+j9BfRwb9BQYsQtE+7cjpiviGfvTMf3tgD9cnxvlBz6DxwONcokSmINDoNAzyU4bhfkIkJkdIIfkOLiM9CY+HvJdYIKODkMykMqIS8AhyREYqwOsrA3bRnYa8jy0AxMGvRdjhb6Wb6L0/79rF/Uzmks1kLgf5n6NV5HxS+5ctW/5Cf0/30d9jjt89beyau4ILNj9K71j/6KP/fOxRUu4dMb/cwgtXmpyVfn+lM/mznp6mlWMAXgoIXqcn5dY0Tx5p/NXIQt5dP9N7f/H8qsSnklrWEmdp7ksbw7NM9yxZ/2j4sX8++uj6yx+lybX68xbMzrM2b5w+wpGlFkjLBx+oXLUTpk4dG9SlL9q7cEKBgcTs1mNz0Az9Xs4i7dmU6HkupvhgNCuLoCcOeOn5Avpf5EuYUpjBFJSEQrk4OtqEqtx7NK2hcKhVcw8c9KckCC+2J4QK+1HRzq+IXbsCDpj3hMWIVRgt6FIRkwX3JKTx08xKBmW178qL5ZnAXWdzZWj3xLkKYsEsgyRgTjzw6QmqYjAiFQtriOJSmfgwMl4amqvAmwdUja0dnuhbHk/ZhhI+4Pnes7Fk4Kikgdzvad1YQuc85elo9cBp2Pc9UlBZqdNpPRMmDssPjZVoK97e4fEQvH+VB45KNno8fDHcvzG6jc7Bm8j9T0FFHt7vwaf1lK4Kt2Zn5y34Y+D8lefButgDdHSV9CU3hTufm89dyl3FbefuQN2yAbUdhjRJLhP9yksw30GU8gBjptgMxCMgxlJloJQLcFwhR+Kh7kzxyGBEMloJvn4Q6AcYUNHHBDCypJgziHFFIxybvZiHayAvfdrafaa1QJ9d7qq312mSJ/omlBUUtJ7pbl3UtlWd0bnIf7NTh1wbhlKDDbM6xx2Vizoz1Fsrllj6b3GFcGTJJmV4D+r5XH1Yn8vrtdqwlmj02nTak67VW4zEY7TQ+3FvMdIe2K8PYxZ3wtv4QN+Iy6Y1l1aa7JK6wjH1/Jtbfdi0kQ3PfVXuv/rrltwaac3eNVV4T5Wyh0OpPqvl66v95V89lzs8g1xWV1jEIJ+i5qe6bH1Ojt5oSu41AzrWAXfytcKixMK7xWxAkN+p5UYyOS2KBYwBry4WfdKiyP7qiWSPh6yMpQVuY0wSqGga4yIhIT/y3GUXrMCQTy04972VsQZVejE4W3dHhC+PxdIBXLHq+ZkzaQ+a4BCaikvp0wzmIBtR9hmfKrKFcEc3YiLFYIH5IMVl3UmcESBAJTeLWS74BhxFnYNZZtkSM2xBu5bEI/Qujb1HAbTeHntbdqvZyleFUDDFsUQIwkmE1X6dFR1zhM5wYR12ODRPydB2VL/gRroGc4fvBSigLkgvUEN67+G79+bf0dnReUf+A3ui4qbrbnM2LRxbbHmR/pZ20t++6ByxcZb9DmnvhW2Fl3mcDXWF8Uz0CJmDVkP0fmY7NJivX/Gc/ycEJ6SEEPoh5n7yP7di5ZO+pEaXa3iS90k62R1K0leO8JfSnhXPLV/+3AriqRk3MSspxKmZr8Ex5ittg14czo3ixnETuanYlwyoqFhgGWfigWKzr3QhLjjnWcckaJSdsImxVFju8PoyMuTMujLXzKdn8q1DDqMvwzizwS5CTdhAHl3d0D0iul9JxZ3uZXNnZGUnFcw7r809f8KE+Wcd9/2M9lOJtlSxPH8VRkdiVt5KinzuK4A/0mQjZ+ByuUKUq6TxBWVAJ8GmNyGg0QPiRb7ELQkAU9xM5yybVOPJaxWHjh45eqiCvDLs810/vTHfu3rRb16igdljpr2xe+HW2Tf3Tpw/sffmWec13C4W9781d1tj47a5QtXsZSri2H54yiIr/bKMZjycftHBadHPwjO7Zy40rpoa97ePSPfAWDCpAHABGOvYTiQggZHFMksYSFqS9Tq0/w0RFh6VyDo9k2gF3AFJc97E/MbaN2fS00vpv2d8UN+YP+m8pgm8xnDPcmvNm4ue0RvGdp/qHmvQP7Po/QbHivsNGn6ceFHZ/lfOn7ZYTVPJv1KWzDr/lf2lBUKo9qqfQjPd9Go+kHuys/Mv27b9pbPzZG70d+RK27z6M5trG/iE+CqpGE+BY9FCgGQKCUCgynqn4I5F27ll/1dvP+MPnHnJMObdQ8JpegNZXfJlZv/NZW/YyGq6v5Bfy2+X1YtvvtnrQ62ioemxzt7HiINkOjeTJ3205qI8epJ+VUJup/O4s+LsaH8ZZ+c/xdj5T8F1ME6X0Ck1qpoZRYh2pV0sYF5zBHF0N/cps2Mt42q40dwFMe/3NB71IH6fA0CJF7GY5EKJKipZbbLdFPMhgFXjNgWY2TaOoZgQwYvhMXeZBLgKRd4m4e7W0XUrHl2y6egVRPVcmi+l3qzOlW57+eLfLCCvLeyaZii50m+b/FbrIq22qxX3N6qmsFBf/fc/tltUpZozXEZfSnX11a8/KjSFOheEVs0ONmyYfOXvSf3wkhIivLJt1j3zwrMsDVOvXVBc5cyd8YawZ9Wl9wv8nZeu2jdciRkWrbliuSkrPSdXb0lzqDP47XPD21m/TyFj5SulrZyO2VtWqTLMGA1WiQvrDjj0BWW8205MGWa7EhnWLKmE/sdJ6SuZR9Dc/2TBj3cveyH06Hyiu3Tz9wc3ZABIq/1bzUAx/UHauof+Yz+aPt762tyWV9s39b40+97PNpGukfSnkbEiWLtVxCvtke47l0xUiMlEVxBtAbH2/znacxl/2m1Z+n30g4v5YrqCL1se/UDaSH+wz8/r//wyvozvK7QK9I3o+xfz/qiPr2iLHlFsC4GLaWM+uUpMOwBuzErJpyHMUgXmtgu4cJOZXC2WHjrU+4dDQgt/vzHFYByTFX09+kbWGKMhxSg1PtN/8JlnhPHP9O/li3TDbFbZQDeRzUDD2IbpEp+jZbGGzzYWRSgAr8cgAcZeBn4feDMFHGhIQG5rDfVeiyBfXB9qvVorpibRZWRLtkHj0tAJH31IJ0DGkE220GVJqaK2MMi385cEeZfQiff0d8BeeFdrz0yi2+gyqzGZV5PVr75Kb1DzyUBM7SKXJWXatYXlVE1Ol7P1vkLWyHOgrTalrTIiSmhrEPoE1mAZD8gyj4gcJGaTrFnT0jcZhb2qp6at3hndG10sp2iSNbaUZH4N+So3xZFlEy4VFpw31UbttifO799z/hQb+cz2uNDUfwF5R9RatOnJ6Ot9uUObnGpGn/0BvtDEbOY9TG5wtoZWL8nOECGiy623krgVsOg0mYUyIgqBoAhA85xxvcbfOq+U3uA/rNLTG0rn3brhMFUxrVb/s2Q1OxYWwBVktf/wubhO1aT4XW/EroruZ1qwV2PV/itWy9k8bg7in19Gp3LAIbwXHBCMDkWSidcAEOKcoarwBQz33Ue/Yzbp95H2NA1PXqEfzJtHVOcOXSXfRwyJN5H2JJHvIQ2o3iTpfDGTBcdpA1xlpl9GdmRRRmwEzfUHce3X0f3MBWhofIu+KbHwmimKnYLCu/3neocEL1et7s9ktsxnRypRQDSrd6BPLYPaa6Ve1H4pcRdQFzb0KbGIGjQFaY5MID3QOrtWrO+bwpzh8XksbIsSYiGiBNtXHKsw9sGArQ3ghri31LliJGMIfTOL96MgFkYe0xRmkq3ag3gvjPJHDOcIWGjv2d9ZiBEuZECGrY5Z9KAtlNQY6TuA1yL+6u+gPahVj8Wki18vxiXnKgAg6PmM8pn9KPZhJuixmIftzOGFb0XLc+Yfg/EKB2JZlDEuG4WP6AQHtJ6OudxgkK1gAP2WBo7jXs/isaipY8nSuvr6uqWLN5ZY8qbkWXFnseJuYaHVas2T1XWhxYtPL15SVx99rBCD6RepuoE9seQVwtBbMRUN1txiyA6BAQXcRdwKbj235ZyrJ5ARhwtG1lhUrqcjukZiSUFPAWaErQ+gtVsaMQUDVmYNZ2S6LjkjyE4EHMQbsBJ2Ah3LOBb6FdXR3ioxIW8SRpOmuO0WPUQ/ZqutCn7GN9eufZN+S9+j37659vKiwBLy+HW9B5YvP9B73Xv7J93kN6y7YPNn2ZZLN5Yuci/hk1OrH81I1WUAAlZJyQDjh11VttAdFoTk1OLr5hANXSxl56QJqclkYS3vKls7PdikK9CuqGvhy03r8MCubatrCbJVP9iOIta23kvWvkmMg026488v1lcvFbOhKfQf0KRLVM31NdO7O654rDiP/DlNq9LqeGsuISqduSxQIpB/zYbydzNS01XJ2hUb6MdEbS4eWcETWrn5pvfpzUe2tkx4qHHmN0uUdDOLV3S237ESZyUWoNh2VooyfgQUUiNlIQxhNh/HoCLoiqz45g/ugfMLM9y595z+3wNxYeKp/qznxf28MfIh2i3gWpTVfQfC/cdlNSyidgwihpHUI+hWKe0dCHOSMhA1WGlLTzj2F4tj5GH4PC5LhCfoY3Gn0Ys8rEQdwVfDW9FYAm9UA007W35Xfp3L4oqYjmUYV8+4MfT9DRGcmezLMQRlP4rgxyyTIFMdlOHXZCSVYqIbEGX8XsPQTbVvW9XNy9c2m7VaaZvF0r/UOs3S/5PFItxumTarhnxfk6kRpGRVxcJxpeXLSXFNzfSamugHY/iNo/t/Gs13jOn/ieX/PSaeHyM/vE3Sas3Na5ffXLXNArUtgdqSLNOswm0WWkN1NROWl5eOW1ihSpYETSHWN71m+Ojo1WPI92OinaPJ9wN5tr96jGLvBd0DdH0K44AGNK3ItBs42egdFN6h9TQXNxzEC5S8qnv1vtVrWqZfBsP01ccRNIlkjjNFkY/ol8iNf/vgvm8wxahA01avnYrgd+3Uz+jTjg8U8dwHDjLpM7wDoysylxto13RilTqkd2BE3Uw7rjhi+RwNRMXsUyr0GbKEDHKFzLuZlYFDeESJY8iT8Q8+yN9yvP3i2gs8usmzWnJy5j5VqlO7SnQ62ii909px3nkdtMPiElXJwy2jjQb6XjSqkxfddXd19cv0kfS0B6PfzZgxgYvZsiqyGpSL4xypZ/EouLP58Xj0MYWaNMZVKYl0JayH+BcTHHHPDVN+MI3ku1Tdgzz4jzGXewwwOX8U/WnUfBbTvwXRV7h3K9/KxEhknBJR/83byDe3pX9J3/kSJeUMqfbAnsxQPPHdGO/fNmr+/FE25SsAa/hHEAP9OOg5WGucPLkZAz2pYC2nyb+TD3KZTDtUx3F6lHyHCBeCLua0pEwtnuMzM2d/ZUZveuvidkuYpJ74dkO6lWae+FqlrSodFWgqLhW/p79/ltRmned0ZfR/1iq0XVb5lzn02vUl62aWrK/lf4idyqa/F6/MOH3ZKqhF+8/+ezU6euNLvxPUlkxnljVFvZz+/iBcmeFynvf7Ff23Tq1sXF8yc51nHVk35+/0cVKbDWdas+hrMH4psXiAaAuVz42PSUY3cTdxt3P3cPu5p2AkBb/CVBgFaHqIAAlLAsg2qmQJY3U5BJMZP3QkAXUN805vBq4SDaLSUMuaR2SlwI0CATEQ1JcRokfFIvIPgt5gRo0h2tLVIkxkAlS7ziuLyHfm45HTLCn1CGiNZWfWVLIP63EqTslBA+LLIGDTHGIwmeEC+cjCSbssuWMmL+y1Lpi0q3rMpEXCKwX2Gxbtph9WY3oX8USuaVRllmqStBqtpnq8ujBNnTbM3ioniSoJiNs7oEBOq7YL+jHddGeWXzW2lPzmaKVBJafl2W5+gCd1dZVFZMLRjGUjyZmXpsHyXppLHluKQfv46Kpbkklqhr560tVFGkmtqXVq1frJ+Y9eeDF54LGUXPuBeS0rZdkn0Kq2iwmpqy0XD9ITJHfspEm7cgk9wWcTc+6Y3bfmkrz+W9ve3OcM7rir7a19juAOfnXFBl6TlZnbECrOGbeQ3J4s5Kq0qQ5B0qQIavUDr5G7lBIiB3JPjwvQlLFv0BuIHByWlHX+rAs3kAp6WMUbM6z0gQmNUwFZFKIjI6mYcvuqOxCyqFL+7hcyCU/u/I5sEYg2TSSWr5pH0+LSJ34KWfJzW9d9On9fOQkZsvU6uovU0A+IQDBQFvdbki/rpcdY1KwAcLwZbhdKgXCSyFKG2cQJJ38gSxfP00/v+nrB4fHjDy/4umu6fsFCspTkLyTjfvcb0rzyWVmY3Ng4WZCfXUkP/OZ39DeAre6DuWmW5wMXOpLZWynR4Oz4s+lhtgEMAd5TsLslpnVGbX9c749LTbDHfNGRNZYUw1ghoHy0Ao+Vm0JENo9oyls/KT1TSpHSo62fC7pUIz1qTNUJ4ct5Z4t9apYtL2cyL3QaVBqtPm/iIxOa137F15TNzK26smZzzeUVFYHa9Zs6rfkjHMUp1urshqwaQ2Z2Upm46W8fz73KLPF89NOM9PR0nY538SqbbdLKlSvnOHg+N0UlSUlqk39kUyTqS6teErlw2RsbqyvSbQ/t/mNP+1r+Kykpd+z0OR7H1FS1Oavmwmnn2b2J/rFDaQxmDZ5FbDqMiKBPoDJsSqTFMy8B9cz+kZLGDV1hMfiYElqI0Q3MPCFFiRuHIWKAYlcNxBBWwfNylC+gsOBsRNDFFRqMzPZibDJRJxsxjA5+TA3vDwOtTHuQhs4rJB5IlaAj7UBBNeITMDCLcjZ2VR05heJQTp3w3KGxPpTnmxkZwyzHSYJdi8jiQ+HzgYFggU2QKcIXjttoKSyb0oKwEkdy0Bhi0MZEGrC7Uvo5HvU8wfrIKaL/oY7ZliBJz0xGdM54ASMCsIsSrJUc6E2OhuWSJro/Izk9LzR1WHX1sKnjqqs15F/FRRcOG3bp1CmXpqf3VytjEKczcXutHWjxTOpPG1bdMrV6WEUlEC0X0736YdVwy6VT+Kez0jOiD8doUfms9uewmBmus97gbD63lJzdWOVrPrFgoR0MeZKJ9BmYJ+1nty+SKOmONsUiaSjmWP+/8H3pQ+YE+jcbWOTcfOCtCzkPV8Fiv9VhNExNXF0uMpLazsjr/0temUTAUzO1OnPPZ9GE/6ts4gyHTggPBnY5ZxKLYZewzjLiMU7ia40FApa9QbPXHRcgOWOsQ+JyY1F9lJppVHHh6cIZPjjllUXHPlIAf8SjhFBQmAZsR9KvrjvGw5xj7Vl4GwtZ5UxYiudegughxfwr0FOK+Y2ceyHGjlg2bqXED+mfgX5JfPnEV0yIfa4Cviw9foc76NYQmwagvqzcCuAmwp6GcYXQkWhg8NDjnoWMZCGWcRYLQ9pQmjg6sMxl1S+ggR86xcVkE3g0BC6mJOnoqlxHumeYu7DQPWx4XiPxzBrmdhc2QcGQwaK8qEtJ4cfIcil9UnYXB4KFhdnDgqS57kT2MHd7odtdfNa4oc9QKvuWgQm4N5cS40ppqWgMmmWzM2EvVKEeoJxHKi/uPeb3JbaVth9Yd+AA7Rnc72q8SMNnJSe/USqLcyyNHk+jJ7HJVZFwVTicuKft80bopuTnr3y1uXbJ96V5+aWl+XkAE97n3peBKFbi4RPl+3EOt4vHb8ephnyZxHN3e/vde+j59Pw9LEf2kX30BxYij0X2ljxnncRc7xkc1fhFzJ9U4f1xRmRhr7BvhXhhE21m2W3HAnfQjHw/QiDkrCNoFYRBiVe1L47g+9EUyJEucS39EJYbi8D6yYHfR0fCIaRKbLJ4vKDYV0x1bph5xKbXMc9qGUPCwm3RI9v5eXRJJIIRPyMIS/uPh4kv+sF2fj5GoIhEm1TNsEuwn8Je0uuEIKB50eY062xmQaeH+mSnTXTrbJL7C4xEt5GsBXS/FnNfYMi6jfQ6aPh1kBNLydrerUNPAbDFUxvZrUP8jcyA834hzf7vuM1z+xq9+9+woOdyJlKd/G/Z0tQY3quB1ruHRAObzE0DOvIC7qLBb5cwGSETwyrSQptRERfWE+VrnOJZxwNR+bxGO349TzwrL9r9XrbFvm/iYSYrXSzp79CmFadptameVC1/KC29MD0tTVusTYt96wT+cWMR1eM5VDLjkdSIuDhcV+bxlNWFY2mvUQsVGHtNqVCnqdcA1aUbeo3whLRerSLbZrHywgn7s+L6D1BU54rWnRjBODFSN38i+pC4HqMZs2RoqP/+LwZiG/95MLaxOODDNfgFLU6vDEGMgiRKoDadEENqYRR82QsqaUVlgR2DdqFJXkRxQ5Ru7vWL66ZV2AsKCugB9PZGsViM1ECd9fXyKHkL8LJc0BSSgsiuBqqs6GDsZi5xKEZNkzCKFTIUVpXMxFWMPU9jcawS8kkkktvZ/eEnH3Z35jiyLhibP6pu+LBQwFpWakypLGvxzE11tM4dToQbRmV5HDm56Vliektw0XhCCmsaSpJNk+65fdjo2fu3a+XkJKf2+sfGNNx9uVZKSnKmr75z5/V35+hql1y+vfOKsrq77hpvtFf43WnazA2l2W5ThqQhGuewKcWjNqoFU4l7pGtC6p/HliRNC+Y1VNYFxjnrxmkLSjuenpPs0KbLyXOeWrpmzzQlP+V2eoKi+hvoJQ8QFnuBv/9f41GivyiJz17Y/qs8RhiHVId55atIytRlG8zf/zWPzAeSBjg18ZsDsWiU50j6O3AxwIFCS3LcQia3sMGq5vRMwh8T9Mcxb3zh4rfnDLHPKQAryPsN/6+tq49powzj995de9dee9xde9eOftJ2bQ9KKR+FFSjjI2yUj8k2BgPGNuYYDFyczCFOJ4sogU0jJoaQLJoxp3+YqPMjBrPETf8wuCVmyeb4zzDjEhIX58yMZsDp+9616BbT8ObN2/ejLc897/s8z+/5vepV177/rR5Mfpo8mC66dqerys2Hd9GuBveBafCgJBb0O0xeh6u8u7syU93TlakWFhPLeXmNL42lHi1TYxrSdWy1RON6pAIObyLsjgRtYi4eebReHvageoDGtHsbtdwqFMvxYVGozToxLJ4Tl8qqCDwUJam4mukLVHnlq5A7RSehWILql5FQexSgKKikgrKRaOttamPZY63E8v1fTnw3sZXOz33r0o/Lo0iHrC7paooX+6Le5CdtfQ3KJU42QzuZD8MSJNuv7bVKxoDInx1vyGXCAm9kEsKRhydFO0n6JenGxb56o8wLBqZ5IXtzR/657+8CcOLYlfHtlDNbJZhfvdrXUCRtBMdrfHLxogXODOc363lOVl5JBOy6QFjUkY72wnreYGRkvmsxFbeSfqiVSdI+UjcEZzbKwomMfZHBGCOmCJ6C2gVu5+qeKxqACA96ZLNqYaDQlHr/qHJzBj+9oNyAOzc65s+sjSxACyhvIX0fBHid0uuTqrUCePTy88U8ID/SqNsR9zr5AxiAR4MBZRYsKV7youIFS2m8s2br/Du2GDE9oRxgFJxETwH+lzILR8zC0XAUnAXO8MhY+J+3aGMXiGWVHAyWqL+24uN90+tQ9KpdXWQZfqWBzBJpDDZKL0J4Z3h4h4fBBSTPyJBDVMPo4U3Hgde5c6MIdaX1BW69ldVpGH8oL/p4SZRQxcpiRenFmXZSa1aj7TZpfQHyD9pVV50qDQRKU9V1Lppm/AzNWYxCRawq151IdURoC8cFZwribdkiMBh8Zoq36F2l+T5ffqlLb4HfKmh5M+HOfFBiG8FGyroHZ96fGewui7AE6bEEaR0dirR0nKzff2FfykbQwSC3dmvLC/VVZmhmeuH7OCuXdg10lcosDqeDk1aPb8EQ7Hta5VhX7z5DdFIo2qPam/j6e0h/aihsFDl5kLEW0ngc+LeeV/oUfEplaIPuwQbXvae2tPs0zWVhASpTCEJrEAiugRwFBuQvRTwiCO9VZkHxOyBpXtGydbeoBm1A7vIk/H11atJk5sIL6npfy6zT6Q+ofs+W5kaXM+B74hDx+5QgSbE1LraPnxIiMfw3SQzVrDXh91ysmZkyJlxrvMvBTFFhG4N/GUcOzbZEherR9ISKUiWEtyS1U1j5LOLZSdgjntUlT0S3Q/NYtjQ3rbss4VItzSkX8Kx8CEYLYlEouHykQJks3Msrv9JRwqPcY5xOlzLpdDJAgKuCUcmxkpSRH7Ii0aY6Ig+ZamTlKLr38WN3TSuY9kSUO1c8kQiSyUPYJLWDeg6e2hNYA8pQQYATUKVzQwVmAG5ST0H9jQNYsABKoBqhQvjIKjwE+6KLGMtUFUh+IWabLA7AMTms3SwK9iwRqoJ5UY5v3KCz5JR0OH9STssHdjXnDfQf2F/tBvMjXE2d13am96jLm5dlX7GIWTwumXKNQJDzZatu1OoIBxzAwvhZkSCNHGtVWDDv3NzT2384v3lnf1D5Wznv6ih187gjEA+KYP68zRzx2l7rfdbjra3hV18VOfsG1mkOGwEXovyixtWJYbep6yoPWgWUpCHsGIYhjgEVJ6oFg3PA47lM6YyldOLTI3lPCL6PeL7Xk5yK/jsEdtalydCQqIWqAKaRXvio602XJ/a/Ew7LDcfsq0ai3ghwAr5wmjAZWLMgSKLAsSajAe4dOMABPOV4vAUum501gc/LYwVlL57aFCtwOARQFPZ5KhO54fJyD7QyGVA3PDc3PNS6MR7NmVAi/dP9/dP6CyutE5cPt3NttcTzdpvDRnMUbWOkLC6LNhho1mTNEuD5hqZInSwHnQ4jw2etnRkuPOD2tO1wuwtixcM61qDXUzqopwiziaDmfp5r2VSXeNLdCC1DtEQ/ls6nZPS3sRospbLqqiju4iI3ga5xplDQzoZATlEc/XZGeBgtEaD4BP1eHboBXrJFCdQtkMn5ILoKfLu3g00Lu+Rtu7Ym+Xevfjte3vT01wGfobKC6n27va72yEydYuo5u3BrsKdTuaz86Tx46kxjzwcJe+fw8epn9tSCXDymkiHqb7OhxTdaZi1F28a27+3k6qvK3zsbGPhqJFirLCov3zkHiu5fm/BnfTN0fl+0vrL1eNLp75lcLVbNKuwfErMzKQB42mNgZGBgYGTs9NeexxjPb/OVgZv9AlCE4cKatyHINPsFsDgHAxOIBwBGoQtZAAB42mNgZGBgv/D/BohkYACTjAyo4BsAdroFVQAAeNptU6FuwzAQPacgKl4+oKBwaGB/UWmglfIB0cBAP2TIY1XBpLLClhRtpKAJqLTAfUOxpWps5+ScnF8S6enu7PO7d2fHOGq+ZEtk2E9rKtLaPLK9MyoV39q48S3brN3rUKl9AruCXICxbR5iMkvr5CuGr5HkbH97fVqD2Yu1sEcjvj9/Eb5ihCsT/VVvA/wZcxXtHvOh/i7f9+iEx8WzCr2HOWOdiNPGnDh7KmHtjc+8x5obzinbBWMDtZ5Bl4Vaa8ZUzTDgg3Ec6S3gD9ZPaoaqZ4wDfxd/Kg6uZc4wY6/3wHhh/0nmUUKPbjjjwf3i21zK+oNo+mbs+ncW3YtaGwP9QL0Caqp/qIG8UdTb9eIk7zXkJznRP0LGRxcAAAAmACYAJgAuAIYAqADUAT4BkAGoAe4CLgKSAsgDEANcA5ID1AQcBJgEzgUKBTIF8gYcBmQGkgbOBxIHRgeoB9oIOAhSCHgIlgjCCOwJCAkWCSQJMglACU4JrAnACewKLApiCoAKlArSCvQLLAt0C+YMSgyODMIM+g00DWQNlA3CDfAOHA5eDp4Oyg8YD3wP3hACEDIQfBDCEPARDBFIEWIRoBI+EoYSqBLKEuwTFhOoE+QUUBR6FJoUthUKFVIVlhYMFk4WjhbQFzIXyBhCGLYY2hj2GQwZTBmGGeAaJhpeGoQaqBrkGzIbiBw6HGocuhzsHTQdah2MHbAePh52HtQe9h9yH7QgCCBsILIg1CD2IQ4hjiHKIiQimCK2I2Aj0CRWJIgk0CTsJQ4lQCWOJaol2iX8JpgnQCfEKBAoKihAKFoocCiKKKAouijQKQgpJiniKkgqsiuGK+Ishi0CLUwtpC3gLgwuGi6cLuAvEi9GL5wv3DBCMJQwwDDsMSgxXjF2MZgx3jK2MuQzLjNKM8w0GDRcNNI1PDZeNoo3FjdON4o3yjgqOHI4lDkCOUY5kjmqOdQ6Ijp8OrQ66DsQO0Y7pjw2PHA8pj0sPZg+Dj6iPso+6D8GPxw/Mj9GP74/zD/iQJBBCEG2QiRCJAAAeNpjYGRgYPjGsIlBkAEEmICYkQEk5gDmMwAALxQB/wB42o1Sy07CQBQ9bdGEhLhw4cK4aHSjJhQQRYQt6kJiiC/cFiiPiLSUCpj4HX6T7ty68RuMH2A8Mx0a0m7MZGbOPT33MfcWQAavMKCl0gC+uEOsYZ1WiHWs4VthA2X8KpzCtlZSeAVz7V7hVfIfCqexr/0onMGmvqXwGzb0he878no9xJ8GNS84RwN1mJjCgY8JBnAxon3A7ZIxYdN+5j0kCqQqqZ4RBegTdSUTEDmYo83To7XQ7VITcHmoIMc1k8tCj1+feIuMPfJDegjfEXM43DmyHtks49sYUyniPJLZwZnKeJrIt4ca1RNqRTRXRruiosdc4jU+CoyU5yqhiltcoIlLoqRXNuaXVJgxxV2sQ8uZGrgmI6xltk9loOJNIw8LxzyrfKuNB8YUmi5Z0aEWp2ThSO4yirRO/lF7U3a5wyp82VtRe0eigZyDKadsM+NMKb1IuZhQk3ZradZhrTfM4dCq8WzzNlmP+FbktAt8R4V1luS/JV5+GE3OZD/G9B0wtsg0/AP9pH7rAAB42m2UZZAdRRhF9wRJcHd3hzf9dc/MwyGwwd3dAgmEJUgIwd3d3d0tuLu7u7tD8Rfbs/94VVu3pnb69Ntb92zPoJ7/Pn+N70k9//f5858fegYxiAmYkImYmMEMYRImZTImZwqmZCqmZhqmZTqmZwZmZCZmZhZmZTZmZw7mZC7mZh7mZT7mZwEWZCEWZhEWZTEWZwmWZCk6VCSCTKGmoaXL0izDsizH8qzAiqzEygxlFVall2GsxuqswZqsxdqsw7qsx/pswIZsxMZswqZsxuZswZZsxdZsw7Zsx/bswI7sxHB2ZhdGMJJd2Y1R7E4fezCaPdmLvdmHMezLWPZjHPtzAAdyEAdzCIdyGIdzBEdyFEdzDMdyHMdzAidyEidzCqdyGqdzBmdyFmdzDudyHudzARdyERdzCZdyGZdzBVdyFVdzDddyHddzAzdyEzdzC7dyG7dzB3cynru4m3u4l/u4nwd4kId4mEd4lMd4nCd4kqd4mmd4lud4nhd4kZd4mVd4ldd4nTd4k7d4m3d4l/d4nw/4kI/4mE/4lM/4nC/4kq/4mm/4lu/4nh/4kZ/4mV/4ld/4nT8Gj+kbmUrv0H+zt+p0zMpMZpjZLGZtNmZrdvuzklfJq+RV8io5lZxKTiWnkpPkJDlJTpKT5CQ5SU6Sk+SEnPB8eD78u0JOyAnPh+ez57PfI8vJcrLns/dnzxd/X7yn+F7xnuL7ZeB976u9r/a+Wk4tp5ZTy6nl1HJqOY3nG79vI6eR08hp5DRyGjmNnNbv08pr5bXyWnltPy+5p+SekjtK7ih1Bt6rzcZszf57kztK7ii5o+SOUiXPPSX3lNxTck/JPSX3lNxTck/JPaUkz10ld5XcVXJXyV0ld5VCnvtK7iu5r+S+kvtKIc+dJXeW3FlyX2F/0Rl4DjObxazNxmzNfm7YY9hj2GPYY9hj2GPYY9hj2GPYY9hj2GPYY9hj2GPYY9hj2GPYY9hj2GPYY9hj2GPYY9hj2GPYY9hj2GPYY+hrDPSpr5Hl6W1kefob+hu5n5d9zgPPpWNWZjLDzGYxa7Mx5eh5rj2v51nPs55nPc96nvU863lu5Oh71ves71nfs75nfc/6nvU963vW96zvWd+zvmd9z/qeW3mtvFZeK68rryuvK68rryuvK68rryuvK6/bzyv+fyn6UfSj6EfRj6IXRS+KXhS9KHpR9KLoRdGLohdFL4peFL0oelH0ouhF0YuiF0Uvil4UvSh6UVIzpG/42GGjxo0e8TfXWJDWAAAAAAFUhzzUAAA=) format('woff'),url(../fonts/dashicons.ttf) format("truetype"),url(../fonts/dashicons.svg#dashicons) format("svg");font-weight:400;font-style:normal}.dashicons,.dashicons-before:before{display:inline-block;width:20px;height:20px;font-size:20px;line-height:1;font-family:dashicons;text-decoration:inherit;font-weight:400;font-style:normal;vertical-align:top;text-align:center;-webkit-transition:color .1s ease-in 0;transition:color .1s ease-in 0;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.dashicons-menu:before{content:"\f333"}.dashicons-admin-site:before{content:"\f319"}.dashicons-dashboard:before{content:"\f226"}.dashicons-admin-media:before{content:"\f104"}.dashicons-admin-page:before{content:"\f105"}.dashicons-admin-comments:before{content:"\f101"}.dashicons-admin-appearance:before{content:"\f100"}.dashicons-admin-plugins:before{content:"\f106"}.dashicons-admin-users:before{content:"\f110"}.dashicons-admin-tools:before{content:"\f107"}.dashicons-admin-settings:before{content:"\f108"}.dashicons-admin-network:before{content:"\f112"}.dashicons-admin-generic:before{content:"\f111"}.dashicons-admin-home:before{content:"\f102"}.dashicons-admin-collapse:before{content:"\f148"}.dashicons-admin-links:before,.dashicons-format-links:before{content:"\f103"}.dashicons-admin-post:before,.dashicons-format-standard:before{content:"\f109"}.dashicons-format-image:before{content:"\f128"}.dashicons-format-gallery:before{content:"\f161"}.dashicons-format-audio:before{content:"\f127"}.dashicons-format-video:before{content:"\f126"}.dashicons-format-chat:before{content:"\f125"}.dashicons-format-status:before{content:"\f130"}.dashicons-format-aside:before{content:"\f123"}.dashicons-format-quote:before{content:"\f122"}.dashicons-welcome-edit-page:before,.dashicons-welcome-write-blog:before{content:"\f119"}.dashicons-welcome-add-page:before{content:"\f133"}.dashicons-welcome-view-site:before{content:"\f115"}.dashicons-welcome-widgets-menus:before{content:"\f116"}.dashicons-welcome-comments:before{content:"\f117"}.dashicons-welcome-learn-more:before{content:"\f118"}.dashicons-image-crop:before{content:"\f165"}.dashicons-image-rotate-left:before{content:"\f166"}.dashicons-image-rotate-right:before{content:"\f167"}.dashicons-image-flip-vertical:before{content:"\f168"}.dashicons-image-flip-horizontal:before{content:"\f169"}.dashicons-undo:before{content:"\f171"}.dashicons-redo:before{content:"\f172"}.dashicons-editor-bold:before{content:"\f200"}.dashicons-editor-italic:before{content:"\f201"}.dashicons-editor-ul:before{content:"\f203"}.dashicons-editor-ol:before{content:"\f204"}.dashicons-editor-quote:before{content:"\f205"}.dashicons-editor-alignleft:before{content:"\f206"}.dashicons-editor-aligncenter:before{content:"\f207"}.dashicons-editor-alignright:before{content:"\f208"}.dashicons-editor-insertmore:before{content:"\f209"}.dashicons-editor-spellcheck:before{content:"\f210"}.dashicons-editor-distractionfree:before,.dashicons-editor-expand:before{content:"\f211"}.dashicons-editor-contract:before{content:"\f506"}.dashicons-editor-kitchensink:before{content:"\f212"}.dashicons-editor-underline:before{content:"\f213"}.dashicons-editor-justify:before{content:"\f214"}.dashicons-editor-textcolor:before{content:"\f215"}.dashicons-editor-paste-word:before{content:"\f216"}.dashicons-editor-paste-text:before{content:"\f217"}.dashicons-editor-removeformatting:before{content:"\f218"}.dashicons-editor-video:before{content:"\f219"}.dashicons-editor-customchar:before{content:"\f220"}.dashicons-editor-outdent:before{content:"\f221"}.dashicons-editor-indent:before{content:"\f222"}.dashicons-editor-help:before{content:"\f223"}.dashicons-editor-strikethrough:before{content:"\f224"}.dashicons-editor-unlink:before{content:"\f225"}.dashicons-editor-rtl:before{content:"\f320"}.dashicons-editor-break:before{content:"\f474"}.dashicons-editor-code:before{content:"\f475"}.dashicons-editor-paragraph:before{content:"\f476"}.dashicons-align-left:before{content:"\f135"}.dashicons-align-right:before{content:"\f136"}.dashicons-align-center:before{content:"\f134"}.dashicons-align-none:before{content:"\f138"}.dashicons-lock:before{content:"\f160"}.dashicons-calendar:before{content:"\f145"}.dashicons-calendar-alt:before{content:"\f508"}.dashicons-visibility:before{content:"\f177"}.dashicons-post-status:before{content:"\f173"}.dashicons-edit:before{content:"\f464"}.dashicons-post-trash:before,.dashicons-trash:before{content:"\f182"}.dashicons-external:before{content:"\f504"}.dashicons-arrow-up:before{content:"\f142"}.dashicons-arrow-down:before{content:"\f140"}.dashicons-arrow-left:before{content:"\f141"}.dashicons-arrow-right:before{content:"\f139"}.dashicons-arrow-up-alt:before{content:"\f342"}.dashicons-arrow-down-alt:before{content:"\f346"}.dashicons-arrow-left-alt:before{content:"\f340"}.dashicons-arrow-right-alt:before{content:"\f344"}.dashicons-arrow-up-alt2:before{content:"\f343"}.dashicons-arrow-down-alt2:before{content:"\f347"}.dashicons-arrow-left-alt2:before{content:"\f341"}.dashicons-arrow-right-alt2:before{content:"\f345"}.dashicons-leftright:before{content:"\f229"}.dashicons-sort:before{content:"\f156"}.dashicons-randomize:before{content:"\f503"}.dashicons-list-view:before{content:"\f163"}.dashicons-exerpt-view:before{content:"\f164"}.dashicons-grid-view:before{content:"\f509"}.dashicons-hammer:before{content:"\f308"}.dashicons-art:before{content:"\f309"}.dashicons-migrate:before{content:"\f310"}.dashicons-performance:before{content:"\f311"}.dashicons-universal-access:before{content:"\f483"}.dashicons-universal-access-alt:before{content:"\f507"}.dashicons-tickets:before{content:"\f486"}.dashicons-nametag:before{content:"\f484"}.dashicons-clipboard:before{content:"\f481"}.dashicons-heart:before{content:"\f487"}.dashicons-megaphone:before{content:"\f488"}.dashicons-schedule:before{content:"\f489"}.dashicons-wordpress:before{content:"\f120"}.dashicons-wordpress-alt:before{content:"\f324"}.dashicons-pressthis:before{content:"\f157"}.dashicons-update:before{content:"\f463"}.dashicons-screenoptions:before{content:"\f180"}.dashicons-info:before{content:"\f348"}.dashicons-cart:before{content:"\f174"}.dashicons-feedback:before{content:"\f175"}.dashicons-cloud:before{content:"\f176"}.dashicons-translation:before{content:"\f326"}.dashicons-tag:before{content:"\f323"}.dashicons-category:before{content:"\f318"}.dashicons-archive:before{content:"\f480"}.dashicons-tagcloud:before{content:"\f479"}.dashicons-text:before{content:"\f478"}.dashicons-media-archive:before{content:"\f501"}.dashicons-media-audio:before{content:"\f500"}.dashicons-media-code:before{content:"\f499"}.dashicons-media-default:before{content:"\f498"}.dashicons-media-document:before{content:"\f497"}.dashicons-media-interactive:before{content:"\f496"}.dashicons-media-spreadsheet:before{content:"\f495"}.dashicons-media-text:before{content:"\f491"}.dashicons-media-video:before{content:"\f490"}.dashicons-playlist-audio:before{content:"\f492"}.dashicons-playlist-video:before{content:"\f493"}.dashicons-controls-play:before{content:"\f522"}.dashicons-controls-pause:before{content:"\f523"}.dashicons-controls-forward:before{content:"\f519"}.dashicons-controls-skipforward:before{content:"\f517"}.dashicons-controls-back:before{content:"\f518"}.dashicons-controls-skipback:before{content:"\f516"}.dashicons-controls-repeat:before{content:"\f515"}.dashicons-controls-volumeon:before{content:"\f521"}.dashicons-controls-volumeoff:before{content:"\f520"}.dashicons-yes:before{content:"\f147"}.dashicons-no:before{content:"\f158"}.dashicons-no-alt:before{content:"\f335"}.dashicons-plus:before{content:"\f132"}.dashicons-plus-alt:before{content:"\f502"}.dashicons-minus:before{content:"\f460"}.dashicons-dismiss:before{content:"\f153"}.dashicons-marker:before{content:"\f159"}.dashicons-star-filled:before{content:"\f155"}.dashicons-star-half:before{content:"\f459"}.dashicons-star-empty:before{content:"\f154"}.dashicons-flag:before{content:"\f227"}.dashicons-share1:before,.dashicons-share:before{content:"\f237"}.dashicons-share-alt:before{content:"\f240"}.dashicons-share-alt2:before{content:"\f242"}.dashicons-twitter:before{content:"\f301"}.dashicons-rss:before{content:"\f303"}.dashicons-email:before{content:"\f465"}.dashicons-email-alt:before{content:"\f466"}.dashicons-facebook:before{content:"\f304"}.dashicons-facebook-alt:before{content:"\f305"}.dashicons-networking:before{content:"\f325"}.dashicons-googleplus:before{content:"\f462"}.dashicons-location:before{content:"\f230"}.dashicons-location-alt:before{content:"\f231"}.dashicons-camera:before{content:"\f306"}.dashicons-images-alt:before{content:"\f232"}.dashicons-images-alt2:before{content:"\f233"}.dashicons-video-alt:before{content:"\f234"}.dashicons-video-alt2:before{content:"\f235"}.dashicons-video-alt3:before{content:"\f236"}.dashicons-vault:before{content:"\f178"}.dashicons-shield:before{content:"\f332"}.dashicons-shield-alt:before{content:"\f334"}.dashicons-sos:before{content:"\f468"}.dashicons-search:before{content:"\f179"}.dashicons-slides:before{content:"\f181"}.dashicons-analytics:before{content:"\f183"}.dashicons-chart-pie:before{content:"\f184"}.dashicons-chart-bar:before{content:"\f185"}.dashicons-chart-line:before{content:"\f238"}.dashicons-chart-area:before{content:"\f239"}.dashicons-groups:before{content:"\f307"}.dashicons-businessman:before{content:"\f338"}.dashicons-id:before{content:"\f336"}.dashicons-id-alt:before{content:"\f337"}.dashicons-products:before{content:"\f312"}.dashicons-awards:before{content:"\f313"}.dashicons-forms:before{content:"\f314"}.dashicons-testimonial:before{content:"\f473"}.dashicons-portfolio:before{content:"\f322"}.dashicons-book:before{content:"\f330"}.dashicons-book-alt:before{content:"\f331"}.dashicons-download:before{content:"\f316"}.dashicons-upload:before{content:"\f317"}.dashicons-backup:before{content:"\f321"}.dashicons-clock:before{content:"\f469"}.dashicons-lightbulb:before{content:"\f339"}.dashicons-microphone:before{content:"\f482"}.dashicons-desktop:before{content:"\f472"}.dashicons-tablet:before{content:"\f471"}.dashicons-smartphone:before{content:"\f470"}.dashicons-phone:before{content:"\f525"}.dashicons-smiley:before{content:"\f328"}.dashicons-index-card:before{content:"\f510"}.dashicons-carrot:before{content:"\f511"}.dashicons-building:before{content:"\f512"}.dashicons-store:before{content:"\f513"}.dashicons-album:before{content:"\f514"}.dashicons-palmtree:before{content:"\f527"}.dashicons-tickets-alt:before{content:"\f524"}.dashicons-money:before{content:"\f526"} \ No newline at end of file diff --git a/wp-includes/css/editor-rtl.css b/wp-includes/css/editor-rtl.css new file mode 100644 index 0000000..7d59c9d --- /dev/null +++ b/wp-includes/css/editor-rtl.css @@ -0,0 +1,2125 @@ +/*------------------------------------------------------------------------------ + TinyMCE and Quicklinks toolbars +------------------------------------------------------------------------------*/ + +/* TinyMCE widgets/containers */ + +.mce-container, +.mce-container *, +.mce-widget, +.mce-widget * { + color: inherit; + font-family: inherit; +} + +/* TinyMCE windows */ +#mce-modal-block.mce-in { + opacity: 0.7; + filter: alpha(opacity=70); +} + +.mce-window { + -webkit-border-radius: 0; + border-radius: 0; + -webkit-box-shadow: 0 3px 6px rgba( 0, 0, 0, 0.3 ); + box-shadow: 0 3px 6px rgba( 0, 0, 0, 0.3 ); + -webkit-font-smoothing: subpixel-antialiased; +} + +.mce-window .mce-container-body.mce-abs-layout { + overflow: visible; +} + +.mce-window .mce-window-head { + background: #fcfcfc; + border-bottom: 1px solid #dfdfdf; + padding: 0; + min-height: 36px; +} + +.mce-window .mce-window-head .mce-title { + color: #444; + font-size: 18px; + font-weight: 600; + line-height: 36px; + margin: 0; + padding: 0 16px 0 36px; +} + +.mce-window .mce-window-head .mce-close { + color: transparent; + top: 0; + left: 0; + width: 36px; + height: 36px; + line-height: 36px; + text-align: center; +} + +.mce-window .mce-window-head .mce-close:before { + font: normal 20px/36px 'dashicons'; + text-align: center; + color: #666; + width: 36px; + height: 36px; + display: block; +} + +.mce-window .mce-window-head .mce-close:hover:before { + color: #2ea2cc; +} + +.mce-window .mce-window-head .mce-dragh { + width: -webkit-calc( 100% - 36px ); + width: calc( 100% - 36px ); +} + +.mce-wp-help .mce-window-head { + border-bottom: none; +} + +.mce-textbox, +.mce-checkbox i.mce-i-checkbox, +#wp-link .query-results { + border: 1px solid #ddd; + -webkit-border-radius: 0; + border-radius: 0; + -webkit-box-shadow: inset 0 1px 2px rgba(0,0,0,0.07); + box-shadow: inset 0 1px 2px rgba(0,0,0,0.07); + -webkit-transition: .05s all ease-in-out; + transition: .05s all ease-in-out; +} + +.mce-textbox:focus, +.mce-textbox.mce-focus, +.mce-checkbox:focus i.mce-i-checkbox, +#wp-link .query-results:focus { + border-color: #5b9dd9; + -webkit-box-shadow: 0 0 2px rgba(30,140,190,0.8); + box-shadow: 0 0 2px rgba(30,140,190,0.8); +} + +/* TinyMCE menus */ +.mce-menu, +.mce-floatpanel.mce-popover { + border-color: rgba(0,0,0,0.15); + -webkit-border-radius: 0; + border-radius: 0; + -webkit-box-shadow: 0 3px 5px rgba( 0, 0, 0, 0.2 ); + box-shadow: 0 3px 5px rgba( 0, 0, 0, 0.2 ); +} + +.mce-floatpanel.mce-popover.mce-bottom { + margin-top: 2px; +} + +.mce-floatpanel .mce-arrow { + display: none; +} + +.mce-menu .mce-container-body { + min-width: 160px; +} + +.mce-menu-item { + border: none; + margin-bottom: 2px; +} + +.mce-menu-has-icons i.mce-ico { + line-height: 20px; +} + +/* TinyMCE panel */ +div.mce-panel { + border: 0; + background: #fff; + -webkit-filter: none; + filter: none; +} + +.mce-panel.mce-menu { + border: 1px solid #ddd; +} + +div.mce-tab { + line-height: 13px; +} + +/* TinyMCE toolbars */ +div.mce-toolbar-grp { + border-bottom: 1px solid #dedede; + background: #f5f5f5; + padding: 0; + position: relative; +} + +div.mce-inline-toolbar-grp { + border: 1px solid #aaa; + -webkit-border-radius: 2px; + border-radius: 2px; + -webkit-box-shadow: 0 1px 3px rgba( 0, 0, 0, 0.15 ); + box-shadow: 0 1px 3px rgba( 0, 0, 0, 0.15 ); + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + margin-bottom: 8px; + position: absolute; + visibility: hidden; + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + z-index: 100100; /* Same as the other TinyMCE "panels" */ +} + +div.mce-wp-image-toolbar > div.mce-stack-layout { + padding: 1px; +} + +div.mce-inline-toolbar-grp.mce-arrow-up { + margin-bottom: 0; + margin-top: 8px; +} + +div.mce-inline-toolbar-grp:before, +div.mce-inline-toolbar-grp:after { + position: absolute; + right: 50%; + display: block; + width: 0; + height: 0; + border-style: solid; + border-color: transparent; + content: ''; +} + +div.mce-inline-toolbar-grp.mce-arrow-up:before { + top: -18px; + border-bottom-color: #aaa; + border-width: 9px; + margin-right: -9px; +} + +div.mce-inline-toolbar-grp.mce-arrow-down:before { + bottom: -18px; + border-top-color: #aaa; + border-width: 9px; + margin-right: -9px; +} + +div.mce-inline-toolbar-grp.mce-arrow-up:after { + top: -16px; + border-bottom-color: #f5f5f5; + border-width: 8px; + margin-right: -8px; +} + +div.mce-inline-toolbar-grp.mce-arrow-down:after { + bottom: -16px; + border-top-color: #f5f5f5; + border-width: 8px; + margin-right: -8px; +} + +div.mce-inline-toolbar-grp.mce-arrow-left:before, +div.mce-inline-toolbar-grp.mce-arrow-left:after { + margin: 0; +} + +div.mce-inline-toolbar-grp.mce-arrow-left:before { + right: 20px; +} +div.mce-inline-toolbar-grp.mce-arrow-left:after { + right: 21px; +} + +div.mce-inline-toolbar-grp.mce-arrow-right:before, +div.mce-inline-toolbar-grp.mce-arrow-right:after { + right: auto; + margin: 0; +} + +div.mce-inline-toolbar-grp.mce-arrow-right:before { + left: 20px; +} + +div.mce-inline-toolbar-grp.mce-arrow-right:after { + left: 21px; +} + +div.mce-inline-toolbar-grp.mce-arrow-full { + left: 0; +} + +div.mce-inline-toolbar-grp.mce-arrow-full > div { + width: 100%; + overflow-x: auto; +} + +div.mce-inline-toolbar-grp-active { + visibility: visible; +} + +div.mce-toolbar-grp > div { + padding: 3px; +} + +.has-dfw div.mce-toolbar-grp .mce-toolbar.mce-first { + padding-left: 32px; +} + +.mce-toolbar .mce-btn-group { + margin: 0; +} + +div.mce-statusbar { + border-top: 1px solid #e5e5e5; +} + +div.mce-path { + padding: 2px 10px; + margin: 0; +} + +.mce-path, +.mce-path-item, +.mce-path .mce-divider { + font-size: 12px; + line-height: 18px; +} + +.mce-toolbar .mce-btn, +.qt-dfw { + border-color: transparent; + background: transparent; + -webkit-box-shadow: none; + box-shadow: none; + text-shadow: none; + cursor: pointer; +} + +#wp-fullscreen-buttons .mce-btn, +.mce-toolbar .mce-btn-group .mce-btn, +.qt-dfw { + border: 1px solid transparent; + margin: 2px; + background-image: none; + -webkit-border-radius: 2px; + border-radius: 2px; + -webkit-filter: none; + filter: none; +} + +#wp-fullscreen-buttons .mce-btn:hover, +.mce-toolbar .mce-btn-group .mce-btn:hover, +#wp-fullscreen-buttons .mce-btn:focus, +.mce-toolbar .mce-btn-group .mce-btn:focus, +.qt-dfw:hover, +.qt-dfw:focus { + background: #fafafa; + border-color: #999; + color: #222; + -webkit-box-shadow: inset 0 1px 0 #fff, 0 1px 0 rgba( 0, 0, 0, 0.08 ); + box-shadow: inset 0 1px 0 #fff, 0 1px 0 rgba( 0, 0, 0, 0.08 ); + outline: none; +} + +.mce-toolbar .mce-btn-group .mce-btn.mce-active, +#wp-fullscreen-buttons .mce-btn.mce-active, +.mce-toolbar .mce-btn-group .mce-btn:active, +#wp-fullscreen-buttons .mce-btn:active, +.qt-dfw.active { + background: #ebebeb; + border-color: #999; + -webkit-box-shadow: inset 0 2px 5px -3px rgba( 0, 0, 0, 0.3 ); + box-shadow: inset 0 2px 5px -3px rgba( 0, 0, 0, 0.3 ); +} + +.mce-toolbar .mce-btn-group .mce-btn.mce-active:hover { + border-color: #555; +} + +.mce-toolbar .mce-btn-group .mce-btn.mce-active:hover i.mce-ico { + color: #555; +} + +.mce-toolbar .mce-btn-group .mce-btn.mce-disabled:hover, +#wp-fullscreen-buttons .mce-btn.mce-disabled:hover, +.mce-toolbar .mce-btn-group .mce-btn.mce-disabled:focus, +#wp-fullscreen-buttons .mce-btn.mce-disabled:focus { + color: #aaa; + background: none; + border-color: #ddd; + text-shadow: 0 1px 0 #fff; + -webkit-box-shadow: none; + box-shadow: none; +} + +.mce-toolbar .mce-btn-group .mce-first, +.mce-toolbar .mce-btn-group .mce-last { + border-color: transparent; +} + +.mce-toolbar .mce-btn button, +.qt-dfw { + padding: 2px 3px; + line-height: normal; +} + +.mce-toolbar .mce-btn i { + text-shadow: none; +} + +.mce-toolbar .mce-btn-group > div { + white-space: normal; +} + +.mce-toolbar .mce-colorbutton .mce-open { + border-left: 0; +} + +.mce-toolbar .mce-colorbutton .mce-preview { + margin: 0; + padding: 0; + top: auto; + bottom: 2px; + right: 3px; + height: 3px; + width: 20px; +} + +/* mce listbox */ +.mce-toolbar .mce-btn-group .mce-btn.mce-listbox { + -webkit-border-radius: 0; + border-radius: 0; + direction: rtl; + background: #fff; + border: 1px solid #ddd; + -webkit-box-shadow: inset 0 1px 1px -1px rgba(0, 0, 0, .2); + box-shadow: inset 0 1px 1px -1px rgba(0, 0, 0, .2); +} + +.mce-toolbar .mce-btn-group .mce-btn.mce-listbox:hover { + background-image: none; + border-color: #bbb; +} + +.mce-toolbar .mce-btn-group .mce-btn.mce-listbox span { + font-size: 13px; +} + +.mce-panel .mce-btn i.mce-caret { + border-top: 6px solid #777; + margin-right: 2px; + margin-left: 2px; +} + +.mce-listbox i.mce-caret { + left: 6px; +} + +.mce-panel .mce-btn button.mce-open i.mce-caret, +.mce-panel .mce-btn.mce-listbox i.mce-caret { + margin-right: 0; + margin-left: 0; +} + +.mce-panel .mce-btn:hover i.mce-caret { + border-top-color: #333; +} + +.mce-panel .mce-active i.mce-caret { + border-top: 0; + border-bottom: 6px solid #333; + margin-top: 7px; +} + +.mce-listbox.mce-active i.mce-caret { + margin-top: -3px; +} + +.mce-toolbar .mce-splitbtn:hover .mce-open { + border-left-color: transparent; +} + +.mce-toolbar .mce-splitbtn .mce-open.mce-active{ + -webkit-box-shadow: none; + box-shadow: none; +} + +.mce-menu .mce-menu-item-normal.mce-active { + background-color: #e5e5e5; + -webkit-filter: none; + filter: none; +} + +.mce-menu .mce-menu-item:hover, +.mce-menu .mce-menu-item.mce-selected, +.mce-menu .mce-menu-item:focus { + color: #000; + background-color: #bbb; + background-image: none; + -webkit-filter: none; + filter: none; +} + +.mce-menu .mce-menu-item:hover .mce-ico, +.mce-menu .mce-menu-item.mce-selected .mce-ico, +.mce-menu .mce-menu-item:focus .mce-ico, +.mce-menu .mce-menu-item:hover .mce-text, +.mce-menu .mce-menu-item.mce-active:hover .mce-text, +.mce-menu .mce-menu-item.mce-selected .mce-text { + color: #000; +} + +/* Menubar */ +.mce-menubar { + border-color: #e5e5e5; + background: #fff; + border-width: 0px 0px 1px; +} + +.mce-menubar .mce-btn:focus { + outline: 0; +} + +div.mce-menu .mce-menu-item-sep, +.mce-menu-item-sep:hover { + margin: 5px 0 4px; +} + +.mce-menubtn span { + margin-left: 0; + padding-right: 3px; +} + +.mce-menu-has-icons i.mce-ico:before { + margin-right: -2px; +} + +/* Buttons in modals */ +.mce-primary button, +.mce-primary button i { + text-align: center; + color: #fff; + text-shadow: none; + padding: 0; + line-height: 26px; +} + +.mce-window .mce-btn { + color: #555; + background: #f7f7f7; + text-decoration: none; + font-size: 13px; + line-height: 26px; + height: 28px; + margin: 0; + padding: 0; + cursor: pointer; + border: 1px solid #ccc; + -webkit-appearance: none; + -webkit-border-radius: 3px; + border-radius: 3px; + white-space: nowrap; + -webkit-box-shadow: inset 0 1px 0 #fff, 0 1px 0 rgba( 0, 0, 0, 0.08 ); + box-shadow: inset 0 1px 0 #fff, 0 1px 0 rgba( 0, 0, 0, 0.08 ); + background-image: none; +} + +/* Remove the dotted border on :focus and the extra padding in Firefox */ +.mce-window .mce-btn::-moz-focus-inner { + border-width: 1px 0; + border-style: solid none; + border-color: transparent; + padding: 0; +} + +.mce-window .mce-btn:hover, +.mce-window .mce-btn:focus { + background: #fafafa; + border-color: #999; + color: #222; +} + +.mce-window .mce-btn:focus { + -webkit-box-shadow: + 0 0 0 1px #5b9dd9, + 0 0 2px 1px rgba( 30, 140, 190, 0.8 ); + box-shadow: + 0 0 0 1px #5b9dd9, + 0 0 2px 1px rgba( 30, 140, 190, 0.8 ); +} + +.mce-window .mce-btn:active { + background: #eee; + border-color: #999; + color: #333; + -webkit-box-shadow: inset 0 2px 5px -3px rgba( 0, 0, 0, 0.5 ); + box-shadow: inset 0 2px 5px -3px rgba( 0, 0, 0, 0.5 ); +} + +.mce-window .mce-btn.mce-disabled { + color: #aaa; + border-color: #ddd; + background: #f7f7f7; + -webkit-box-shadow: none; + box-shadow: none; + text-shadow: 0 1px 0 #fff; + cursor: default; +} + +.mce-window .mce-btn.mce-primary { + background: #2ea2cc; + border-color: #0074a2; + -webkit-box-shadow: inset 0 1px 0 rgba( 120, 200, 230, 0.5), 0 1px 0 rgba( 0, 0, 0, 0.15 ); + box-shadow: inset 0 1px 0 rgba( 120, 200, 230, 0.5 ), 0 1px 0 rgba( 0, 0, 0, 0.15 ); + color: #fff; + text-decoration: none; +} + +.mce-window .mce-btn.mce-primary:hover, +.mce-window .mce-btn.mce-primary:focus { + background: #1e8cbe; + border-color: #0074a2; + -webkit-box-shadow: inset 0 1px 0 rgba( 120, 200, 230, 0.6 ); + box-shadow: inset 0 1px 0 rgba( 120, 200, 230, 0.6 ); + color: #fff; +} + +.mce-window .mce-btn.mce-primary:focus { + border-color: #0e3950; + -webkit-box-shadow: + inset 0 1px 0 rgba( 120, 200, 230, 0.6 ), + 0 0 0 1px #5b9dd9, + 0 0 2px 1px rgba( 30, 140, 190, 0.8 ); + box-shadow: + inset 0 1px 0 rgba( 120, 200, 230, 0.6 ), + 0 0 0 1px #5b9dd9, + 0 0 2px 1px rgba( 30, 140, 190, 0.8 ); +} + +.mce-window .mce-btn.mce-primary:active { + background: #1b7aa6; + border-color: #005684; + color: rgba( 255, 255, 255, 0.95 ); + -webkit-box-shadow: inset 0 1px 0 rgba( 0, 0, 0, 0.1 ); + box-shadow: inset 0 1px 0 rgba( 0, 0, 0, 0.1 ); + vertical-align: top; +} + +.mce-window .mce-btn.mce-primary.mce-disabled { + color: #94cde7; + background: #298cba; + border-color: #1b607f; + -webkit-box-shadow: none; + box-shadow: none; + text-shadow: 0 -1px 0 rgba( 0, 0, 0, 0.1 ); + cursor: default; +} + +.mce-menubtn.mce-fixed-width button span { + max-width: 80px; + padding-left: 16px; +} + +/* Charmap modal */ +.mce-charmap { + margin: 3px; +} + +.mce-charmap td { + padding: 0; + border-color: #dfdfdf; + cursor: pointer; +} + +.mce-charmap td:hover { + background: #f3f3f3; +} + +.mce-charmap td div { + width: 18px; + height: 22px; + line-height: 22px; +} + +/* TinyMCE tooltips */ +.mce-tooltip { + margin-top: 2px; +} + +.mce-tooltip-inner { + -webkit-box-shadow: 0 3px 5px rgba( 0, 0, 0, 0.2 ); + box-shadow: 0 3px 5px rgba( 0, 0, 0, 0.2 ); + color: #fff; + font-size: 12px; +} + +/* TinyMCE icons */ +.mce-ico { + font-family: 'tinymce', Arial; +} + +.mce-btn-small .mce-ico { + font-family: 'tinymce-small', Arial; +} + +.mce-toolbar .mce-ico { + color: #777; + line-height: 20px; + width: 20px; + height: 20px; + text-align: center; + text-shadow: none; + margin: 0; + padding: 0; +} + +.qt-dfw { + color: #777; + line-height: 20px; + width: 28px; + height: 26px; + text-align: center; + text-shadow: none; +} + +.mce-toolbar .mce-btn .mce-open { + line-height: 20px; +} + +.mce-toolbar .mce-btn:hover .mce-open, +.mce-toolbar .mce-btn:focus .mce-open, +.mce-toolbar .mce-btn.mce-active .mce-open { + border-right-color: #999; +} + +i.mce-i-bold, +i.mce-i-italic, +i.mce-i-bullist, +i.mce-i-numlist, +i.mce-i-blockquote, +i.mce-i-alignleft, +i.mce-i-aligncenter, +i.mce-i-alignright, +i.mce-i-link, +i.mce-i-unlink, +i.mce-i-wp_more, +i.mce-i-strikethrough, +i.mce-i-spellchecker, +i.mce-i-fullscreen, +i.mce-i-wp_fullscreen, +i.mce-i-dfw, +i.mce-i-wp_adv, +i.mce-i-underline, +i.mce-i-alignjustify, +i.mce-i-forecolor, +i.mce-i-backcolor, +i.mce-i-pastetext, +i.mce-i-pasteword, +i.mce-i-removeformat, +i.mce-i-charmap, +i.mce-i-outdent, +i.mce-i-indent, +i.mce-i-undo, +i.mce-i-redo, +i.mce-i-help, +i.mce-i-wp_help, +i.mce-i-wp-media-library, +i.mce-i-ltr, +i.mce-i-wp_page, +i.mce-i-hr, +i.mce-i-dashicon, +.mce-close { + font: normal 20px/1 'dashicons'; + padding: 0; + vertical-align: top; + speak: none; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + margin-right: -2px; + padding-left: 2px; +} + +.qt-dfw { + font: normal 20px/1 'dashicons'; + vertical-align: top; + speak: none; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +i.mce-i-bold:before { + content: '\f200'; +} + +i.mce-i-italic:before { + content: '\f201'; +} + +i.mce-i-bullist:before { + content: '\f203'; +} + +i.mce-i-numlist:before { + content: '\f204'; +} + +i.mce-i-blockquote:before { + content: '\f205'; +} + +i.mce-i-alignleft:before { + content: '\f206'; +} + +i.mce-i-aligncenter:before { + content: '\f207'; +} + +i.mce-i-alignright:before { + content: '\f208'; +} + +i.mce-i-link:before { + content: '\f103'; +} + +i.mce-i-unlink:before { + content: '\f225'; +} + +i.mce-i-wp_more:before { + content: '\f209'; +} + +i.mce-i-strikethrough:before { + content: '\f224'; +} + +i.mce-i-spellchecker:before { + content: '\f210'; +} + +i.mce-i-fullscreen:before, +i.mce-i-wp_fullscreen:before, +i.mce-i-dfw:before, +.qt-dfw:before { + content: '\f211'; +} + +i.mce-i-wp_adv:before { + content: '\f212'; +} + +i.mce-i-underline:before { + content: '\f213'; +} + +i.mce-i-alignjustify:before { + content: '\f214'; +} + +i.mce-i-forecolor:before, +i.mce-i-backcolor:before { + content: '\f215'; +} + +i.mce-i-pastetext:before { + content: '\f217'; +} + +i.mce-i-removeformat:before { + content: '\f218'; +} + +i.mce-i-charmap:before { + content: '\f220'; +} + +i.mce-i-outdent:before { + content: '\f221'; +} + +i.mce-i-indent:before { + content: '\f222'; +} + +i.mce-i-undo:before { + content: '\f171'; +} + +i.mce-i-redo:before { + content: '\f172'; +} + +i.mce-i-help:before, +i.mce-i-wp_help:before { + content: '\f223'; +} + +i.mce-i-wp-media-library:before { + content: '\f104'; +} + +i.mce-i-ltr:before { + content: '\f320'; +} + +i.mce-i-wp_page:before { + content: '\f105'; +} + +i.mce-i-hr:before { + content: '\f460'; +} + +.mce-close:before { + content: '\f158'; +} + +.mce-i-wp_code:before { + content: '\f475'; +} + +/* Editors */ +.wp-editor-wrap { + position: relative; +} + +.wp-editor-tools { + position: relative; + z-index: 1; +} + +.wp-editor-tools:after { + clear: both; + content: ''; + display: table; +} + +.wp-editor-container { + clear: both; +} + +.wp-editor-area { + font-family: Consolas, Monaco, monospace; + font-size: 13px; + padding: 10px; + margin: 1px 0 0; + line-height: 150%; + border: 0 none; + outline: none; + display: block; + resize: vertical; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.rtl .wp-editor-area { + font-family: Tahoma, Monaco, monospace; +} + +.locale-he-il .wp-editor-area { + font-family: Arial, Monaco, monospace; +} + +.wp-editor-container textarea.wp-editor-area { + width: 100%; + margin: 0; + -webkit-box-shadow: none; + box-shadow: none; +} + +.wp-editor-tabs { + float: left; +} + +.wp-switch-editor { + float: right; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + position: relative; + top: 1px; + background: #ebebeb; + color: #777; + cursor: pointer; + font: 13px/19px "Open Sans", sans-serif; + height: 20px; + margin: 5px 5px 0 0; + padding: 3px 8px 4px; + border: 1px solid #e5e5e5; +} + +.wp-switch-editor:focus { + -webkit-box-shadow: + 0 0 0 1px #5b9dd9, + 0 0 2px 1px rgba(30, 140, 190, .8); + box-shadow: + 0 0 0 1px #5b9dd9, + 0 0 2px 1px rgba(30, 140, 190, .8); + outline: none; + color: #222; +} + +.wp-switch-editor:active, +.html-active .switch-html:focus, +.tmce-active .switch-tmce:focus { + -webkit-box-shadow: none; + box-shadow: none; +} + +.wp-switch-editor:active { + background-color: #f5f5f5; + -webkit-box-shadow: none; + box-shadow: none; +} + +.js .tmce-active .wp-editor-area { + color: white; +} + +.tmce-active .quicktags-toolbar { + display: none; +} + +.tmce-active .switch-tmce, +.html-active .switch-html { + background: #f5f5f5; + color: #555; + border-bottom-color: #f5f5f5; +} + +.wp-media-buttons { + float: right; +} + +.wp-media-buttons .button { + margin-left: 5px; + margin-bottom: 4px; + padding-right: 7px; + padding-left: 7px; +} + +.wp-media-buttons .button:active { + position: relative; + top: 1px; + margin-top: -1px; + margin-bottom: 1px; +} + +.wp-media-buttons .insert-media { + padding-right: 5px; +} + +.wp-media-buttons a { + text-decoration: none; + color: #464646; + font-size: 12px; +} + +.wp-media-buttons img { + padding: 0 4px; + vertical-align: middle; +} + +.wp-media-buttons span.wp-media-buttons-icon { + display: inline-block; + width: 18px; + height: 18px; + vertical-align: text-top; + margin: 0 2px; +} + +.wp-media-buttons .add_media span.wp-media-buttons-icon { + background: none; +} + +.wp-media-buttons .add_media span.wp-media-buttons-icon:before { + font: normal 18px/1 'dashicons'; + speak: none; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.wp-media-buttons .add_media span.wp-media-buttons-icon:before { + content: '\f104'; +} + +/* Quicktags */ +.quicktags-toolbar { + padding: 3px; + position: relative; + border-bottom: 1px solid #dedede; + background: #f5f5f5; +} + +.has-dfw .quicktags-toolbar { + padding-left: 35px; +} + +.wp-core-ui .quicktags-toolbar input.button.button-small { + margin: 2px; +} + +.quicktags-toolbar input[value="link"] { + text-decoration: underline; +} + +.quicktags-toolbar input[value="del"] { + text-decoration: line-through; +} + +.quicktags-toolbar input[value="i"] { + font-style: italic; +} + +.quicktags-toolbar input[value="b"] { + font-weight: bold; +} + +.mce-toolbar .mce-btn-group .mce-btn.mce-wp-dfw, +.qt-dfw { + position: absolute; + top: 0; + left: 0; + margin: 5px 0 0 5px; +} + +.qt-fullscreen { + position: static; + margin: 2px; +} + +@media screen and ( max-width: 782px ) { + .mce-toolbar .mce-btn button, + .qt-dfw { + padding: 6px 7px; + } + + #wp-fullscreen-buttons .mce-btn, + .mce-toolbar .mce-btn-group .mce-btn { + margin: 1px; + } + + .qt-dfw { + width: 36px; + height: 34px; + } + + .mce-toolbar .mce-btn-group .mce-btn.mce-wp-dfw { + margin: 4px 0 0 4px; + } + + .mce-toolbar .mce-colorbutton .mce-preview { + right: 8px; + bottom: 6px; + } + + .mce-window .mce-btn { + padding: 2px 0; + } + + .has-dfw div.mce-toolbar-grp .mce-toolbar.mce-first, + .has-dfw .quicktags-toolbar { + padding-left: 40px; + } +} + +@media screen and ( min-width: 782px ) { + .wp-core-ui .quicktags-toolbar input.button.button-small { + /* .button-small is normaly 11px, but a bit too small for these buttons. */ + font-size: 12px; + height: 26px; + line-height: 24px; + } +} + +#wp_editbtns, +#wp_gallerybtns { + padding: 2px; + position: absolute; + display: none; + z-index: 100020; +} + +#wp_editimgbtn, +#wp_delimgbtn, +#wp_editgallery, +#wp_delgallery { + border-color: #999; + background-color: #eee; + margin: 2px; + padding: 2px; + border-width: 1px; + border-style: solid; + -webkit-border-radius: 3px; + border-radius: 3px; +} + +#wp_editimgbtn:hover, +#wp_delimgbtn:hover, +#wp_editgallery:hover, +#wp_delgallery:hover { + border-color: #555; + background-color: #ccc; +} + +/*------------------------------------------------------------------------------ + wp-link +------------------------------------------------------------------------------*/ + +#wp-link-wrap { + display: none; + background-color: #fff; + -webkit-box-shadow: 0 3px 6px rgba( 0, 0, 0, 0.3 ); + box-shadow: 0 3px 6px rgba( 0, 0, 0, 0.3 ); + width: 500px; + height: 250px; + overflow: hidden; + margin-right: -250px; + margin-top: -125px; + position: fixed; + top: 50%; + right: 50%; + z-index: 100105; + -webkit-transition: height 0.2s, margin-top 0.2s; + transition: height 0.2s, margin-top 0.2s; +} + +#wp-link-backdrop { + display: none; + position: fixed; + top: 0; + right: 0; + left: 0; + bottom: 0; + min-height: 360px; + background: #000; + opacity: 0.7; + filter: alpha(opacity=70); + z-index: 100100; +} + +#wp-link { + position: relative; + height: 100%; +} + +#wp-link-wrap.search-panel-visible { + height: 500px; + margin-top: -250px; +} + +#link-modal-title { + background: #fcfcfc; + border-bottom: 1px solid #dfdfdf; + height: 36px; + font-size: 18px; + font-weight: 600; + line-height: 36px; + padding: 0 16px 0 36px; + top: 0; + left: 0; + right: 0; +} + +#wp-link-close { + color: #666; + padding: 0; + position: absolute; + top: 0; + left: 0; + width: 36px; + height: 36px; + text-align: center; + background: none; + border: none; + cursor: pointer; +} + +#wp-link-close:before { + font: normal 20px/36px 'dashicons'; + vertical-align: top; + speak: none; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + width: 36px; + height: 36px; + content: '\f158'; +} + +#wp-link-close:hover, +#wp-link-close:focus { + color: #2ea2cc; +} + +#wp-link-close:focus { + outline: none; + -webkit-box-shadow: + 0 0 0 1px #5b9dd9, + 0 0 2px 1px rgba(30, 140, 190, .8); + box-shadow: + 0 0 0 1px #5b9dd9, + 0 0 2px 1px rgba(30, 140, 190, .8); +} + +#link-selector { + padding: 0 16px 50px; +} + +#wp-link-wrap.search-panel-visible #link-selector { + padding: 0 16px; + position: absolute; + top: 36px; + right: 0; + left: 0; + bottom: 44px; +} + +#wp-link ol, +#wp-link ul { + list-style: none; + margin: 0; + padding: 0; +} + +#wp-link-search-toggle:after { + display: inline-block; + font: normal 20px/1 'dashicons'; + vertical-align: top; + speak: none; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + content: '\f140'; +} + +.search-panel-visible #wp-link-search-toggle:after { + content: '\f142'; +} + +#wp-link input[type="text"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +#wp-link #link-options { + padding: 8px 0 12px; +} + +#wp-link p.howto { + margin: 3px 0; +} + +#wp-link p.howto a { + text-decoration: none; + color: inherit; +} + +#wp-link-search-toggle { + cursor: pointer; +} + +#wp-link label input[type="text"] { + margin-top: 5px; + width: 70%; +} + +#wp-link #link-options label span, +#wp-link #search-panel label span.search-label { + display: inline-block; + width: 80px; + text-align: left; + padding-left: 5px; + max-width: 24%; +} + +#wp-link .link-search-field { + float: right; + width: 250px; + max-width: 70%; +} + +#wp-link .link-search-wrapper { + margin: 5px 0 9px; + display: block; + overflow: hidden; +} + +#wp-link .link-search-wrapper span { + float: right; + margin-top: 4px; +} + +#wp-link .link-search-wrapper .spinner { + display: none; + vertical-align: text-bottom; +} + +#wp-link .link-target { + padding: 3px 0 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +#wp-link .link-target label { + max-width: 70%; +} + +#wp-link .query-results { + border: 1px #dfdfdf solid; + margin: 0; + background: #fff; + overflow: auto; + position: absolute; + right: 16px; + left: 16px; + bottom: 16px; + top: 205px; +} + +#wp-link li { + clear: both; + margin-bottom: 0; + border-bottom: 1px solid #f1f1f1; + color: #333; + padding: 4px 10px 4px 6px; + cursor: pointer; + position: relative; +} + +#wp-link .query-notice { + padding: 0; + border-bottom: 1px solid #dfdfdf; + background-color: #f7fcfe; + color: #000; +} + +#wp-link .query-notice .query-notice-default, +#wp-link .query-notice .query-notice-hint { + display: block; + padding: 6px; + border-right: 4px solid #2ea2cc; +} + +#wp-link .unselectable.no-matches-found { + padding: 0; + border-bottom: 1px solid #dfdfdf; + background-color: #fef7f1; +} + +#wp-link .no-matches-found .item-title { + display: block; + padding: 6px; + border-right: 4px solid #d54e21; +} + +#wp-link .query-results em { + font-style: normal; +} + +#wp-link li:hover { + background: #eaf2fa; + color: #151515; +} + +#wp-link li.unselectable { + border-bottom: 1px solid #dfdfdf; +} + +#wp-link li.unselectable:hover { + background: #fff; + cursor: auto; + color: #333; +} + +#wp-link li.selected { + background: #ddd; + color: #333; +} + +#wp-link li.selected .item-title { + font-weight: bold; +} + +#wp-link li:last-child { + border: none; +} + +#wp-link .item-title { + display: inline-block; + width: 80%; + width: -webkit-calc(100% - 68px); + width: calc(100% - 68px); +} + +#wp-link .item-info { + text-transform: uppercase; + color: #666; + font-size: 11px; + position: absolute; + left: 5px; + top: 5px; +} + +#wp-link #search-results, +#wp-link #search-panel { + display: none; +} + +#wp-link-wrap.search-panel-visible #search-panel { + display: block; +} + +#wp-link .river-waiting { + display: none; + padding: 10px 0; +} + +#wp-link .river-waiting .spinner { + margin: 0 auto; + display: block; + float: none; +} + +#wp-link .submitbox { + padding: 8px 16px; + background: #fcfcfc; + border-top: 1px solid #dfdfdf; + position: absolute; + bottom: 0; + right: 0; + left: 0; +} + +#wp-link-cancel { + line-height: 25px; + float: right; +} + +#wp-link-update { + line-height: 23px; + float: left; +} + +#wp-link-submit { + float: left; + margin-bottom: 0; +} + +@media screen and ( max-width: 782px ) { + #wp-link-wrap { + height: 280px; + margin-top: -140px; + } + + #wp-link-wrap.search-panel-visible .query-results { + top: 235px; + } + + #link-selector { + padding: 0 16px 60px; + } + + #wp-link-wrap.search-panel-visible #link-selector { + bottom: 52px; + } + + #wp-link-cancel { + line-height: 32px; + } +} + +@media screen and ( max-width: 520px ) { + #wp-link-wrap { + width: auto; + margin-right: 0; + right: 10px; + left: 10px; + max-width: 500px; + } +} + +@media screen and ( max-height: 520px ) { + #wp-link-wrap { + -webkit-transition: none; + transition: none; + } + + #wp-link-wrap.search-panel-visible { + height: auto; + margin-top: 0; + top: 10px; + bottom: 10px; + } + + .search-panel-visible #link-selector { + overflow: auto; + } + + .search-panel-visible #search-panel .query-results { + position: static; + } +} + +@media screen and ( max-height: 290px ) { + #wp-link-wrap { + height: auto; + margin-top: 0; + top: 10px; + bottom: 10px; + } + + #link-selector { + overflow: auto; + height: -webkit-calc(100% - 92px); + height: calc(100% - 92px); + padding-bottom: 2px; + } + + #search-panel .query-results { + position: static; + } +} + +/* Old TinyMCE 3.x modal */ +/* +.clearlooks2 .mceTop { + border-bottom: 1px solid #ccc; +} + +.clearlooks2 .mceTop span { + font: 13px/24px "Open Sans", sans-serif; + color: #e5e5e5; +} + +.clearlooks2 .mceTop .mceLeft { + background: #444444; + border-color: transparent; +} + +.clearlooks2 .mceTop .mceRight { + background: #444444; + border-color: transparent; +} + +.clearlooks2 .mceMiddle { + clip: rect(24px auto auto auto); +} + +.clearlooks2 .mceMiddle .mceLeft { + background: #f1f1f1; + border-color: transparent; +} + +.clearlooks2 .mceMiddle .mceRight { + background: #f1f1f1; + border-color: transparent; +} + +.clearlooks2 .mceBottom { + background: #f1f1f1; + border-color: transparent; +} + +.clearlooks2 .mceBottom .mceLeft { + background: #f1f1f1; + border-color: transparent; +} + +.clearlooks2 .mceBottom .mceCenter { + background: #f1f1f1; + border-color: transparent; +} + +.clearlooks2 .mceBottom .mceRight { + background: #f1f1f1; + border-color: transparent; +} + +.clearlooks2 .mceClose, +.clearlooks2 .mceFocus .mceClose, +.clearlooks2 .mceFocus .mceClose:hover { + background-image: none; +} +.clearlooks2 .mceClose:before { + content: '\f158'; + font: normal 20px/1 'dashicons'; + speak: none; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + color: #999; + padding-left: 12px; +} + +/* from colors.css +.clearlooks2 { + box-shadow: 0 5px 15px rgba(0,0,0,0.7); +} + +.clearlooks2 .mceMiddle span, +.clearlooks2 .mceMiddle .mceLeft, +.clearlooks2 .mceMiddle .mceRight, +.clearlooks2 .mceBottom, +.clearlooks2 .mceBottom .mceLeft, +.clearlooks2 .mceBottom .mceCenter, +.clearlooks2 .mceBottom .mceRight { + background-color: #fcfcfc; +} + +.clearlooks2 .mceTop span, +.clearlooks2 .mceFocus .mceTop span { + color: #222; +} + +.clearlooks2 .mceClose:before { + color: #999; +} + +.clearlooks2 .mceClose:hover:before { + color: #2ea2cc; +} +*/ +/* Distraction-Free Writing mode + * =Overlay Styles +-------------------------------------------------------------- */ +.fullscreen-overlay { + z-index: 100005; + display: none; + position: fixed; + top: 0; + bottom: 0; + right: 0; + left: 0; + -webkit-filter: inherit; + filter: inherit; +} + +.wp-fullscreen-active .fullscreen-overlay, +.wp-fullscreen-active #wp-fullscreen-body { + display: block; +} + +.fullscreen-fader { + z-index: 200000; +} + +.wp-fullscreen-active .fullscreen-fader, +.wp-core-ui.wp-fullscreen-active .postbox-container { + display: none; +} + +/* =Overlay Body +-------------------------------------------------------------- */ + +#wp-fullscreen-body, +.mce-fullscreen { + z-index: 100010; +} + +#wp-fullscreen-body { + display: none; +} + +.wp-fullscreen-wrap { + margin: 0; + padding: 0; + position: absolute; + right: 0; + left: 0; + bottom: 30px; + top: 60px; + z-index: 100015; +} + +.wp-fullscreen-wrap .wp-editor-container, +.wp-fullscreen-title, +#wp-fullscreen-central-toolbar { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + max-width: 100%; +} + +.wp-fullscreen-active .wp-editor-tools, +.wp-fullscreen-active .quicktags-toolbar, +.wp-fullscreen-active .mce-toolbar-grp, +.wp-fullscreen-active .mce-statusbar { + display: none; +} + +#wp-fullscreen-statusbar { + position: fixed; + right: 0; + left: 0; + bottom: 0; + height: 30px; + z-index: 100020; + background: #fff; + -webkit-transition: height 0.2s; + transition: height 0.2s; +} + +#wp-fullscreen-status { + margin: 0 auto; + padding: 0; +} + +.wp-fullscreen-active .wp-fullscreen-title, +.wp-fullscreen-active .wp-fullscreen-title:focus, +.wp-fullscreen-active .wp-editor-container { + -webkit-border-radius: 0; + border-radius: 0; + border: 1px dashed transparent; + background: transparent; + -webkit-box-shadow: none; + box-shadow: none; + -webkit-transition: border-color 0.4s; + transition: border-color 0.4s; +} + +.wp-fullscreen-active .wp-editor-container { + margin: 0 auto 40px; +} + +.wp-fullscreen-active .wp-fullscreen-title { + font-size: 1.7em; + line-height: 100%; + outline: medium none; + padding: 3px 7px; + margin: 10px auto 30px; + display: block; +} + +#wp-fullscreen-tagline { + color: #888; + font-size: 18px; + float: left; + padding: 4px 0 0; +} + +/* =Top bar +-------------------------------------------------------------- */ +#fullscreen-topbar { + background: #f5f5f5; + border-bottom: 1px solid #dedede; + height: 45px; + position: fixed; + right: 0; + left: 0; + top: 0; + width: 100%; + z-index: 100020; + -webkit-transition: opacity 0.4s; + transition: opacity 0.4s; +} + +#wp-fullscreen-toolbar { + padding: 6px 10px 0; + clear: both; + max-width: 1100px; + margin: 0 auto; +} + +#wp-fullscreen-mode-bar, +#wp-fullscreen-button-bar, +#wp-fullscreen-close { + float: right; +} + +#wp-fullscreen-count, +#wp-fullscreen-tagline { + display: inline-block; +} + +#wp-fullscreen-button-bar { + margin-top: 2px; +} + +#wp-fullscreen-save { + float: left; + padding: 2px 0 0; + min-width: 95px; +} + +#wp-fullscreen-count, +#wp-fullscreen-close { + padding: 5px 0 0; +} + +#wp-fullscreen-central-toolbar { + margin: auto; + padding: 0; + min-width: 620px; +} + +#wp-fullscreen-buttons > div { + float: right; +} + +#wp-fullscreen-mode-bar { + padding: 3px 0 0 14px; +} + +#wp-fullscreen-buttons .hidden { + display: none; +} + +#wp-fullscreen-buttons .disabled { + opacity: 0.5; +} + +#wp-fullscreen-buttons .mce-btn button { + margin: 0; + outline: 0 none; + border: 0 none; + white-space: nowrap; + width: auto; + background: none; + color: #333333; + cursor: pointer; + font-size: 18px; + line-height: 20px; + overflow: visible; + text-align: center; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.wp-html-mode #wp-fullscreen-buttons div { + display: none; +} + +.wp-html-mode #wp-fullscreen-buttons div.wp-fullscreen-both { + display: block; +} + +#wp-fullscreen-save img { + vertical-align: middle; +} + +#wp-fullscreen-save span { + display: none; + margin: 5px 6px 0; + float: right; +} + +/* =Thickbox Adjustments +-------------------------------------------------------------- */ +.wp-fullscreen-active #TB_overlay { + z-index: 100050; +} + +.wp-fullscreen-active #TB_window { + z-index: 100051; +} + +/* Colors */ +.fullscreen-overlay { + background: #fff; +} + +/* =CSS 3 transitions +-------------------------------------------------------------- */ + +.wp-fullscreen-active #fullscreen-topbar { + -webkit-transition-duration: 0.8s; + transition-duration: 0.8s; + opacity: 0; + filter: alpha(opacity=0); +} + +.wp-fullscreen-active #wp-fullscreen-statusbar { + height: 0; +} + +.wp-fullscreen-active.wp-dfw-show-ui #fullscreen-topbar { + -webkit-transition-duration: 0.4s; + transition-duration: 0.4s; + opacity: 1; + filter: alpha(opacity=100); +} + +.wp-fullscreen-active.wp-dfw-show-ui #wp-fullscreen-statusbar { + height: 29px; + background: #f8f8f8; + border-top: 1px solid #eee; +} + +.wp-fullscreen-active .wp-fullscreen-title, +.wp-fullscreen-active .wp-editor-container { + -webkit-transition-duration: 0.8s; + transition-duration: 0.8s; + border-color: transparent; +} + +.wp-fullscreen-active.wp-dfw-show-ui .wp-fullscreen-title, +.wp-fullscreen-active.wp-dfw-show-ui .wp-editor-container { + -webkit-transition-duration: 0.4s; + transition-duration: 0.4s; + border-color: #ccc; +} + +.fade-1000, +.fade-600, +.fade-400, +.fade-300 { + opacity: 0; + -webkit-transition-property: opacity; + transition-property: opacity; +} + +.fade-1000 { + -webkit-transition-duration: 1s; + transition-duration: 1s; +} + +.fade-600 { + -webkit-transition-duration: 0.6s; + transition-duration: 0.6s; +} + +.fade-400 { + -webkit-transition-duration: 0.4s; + transition-duration: 0.4s; +} + +.fade-300 { + -webkit-transition-duration: 0.3s; + transition-duration: 0.3s; +} + +.fade-trigger { + opacity: 1; +} + +/* DFW on touch screen devices */ +.wp-dfw-touch #fullscreen-topbar { + position: absolute; + opacity: 1; +} + +.wp-dfw-touch .wp-fullscreen-wrap .wp-editor-container, +.wp-dfw-touch .wp-fullscreen-title { + max-width: 700px; +} + +.wp-fullscreen-active.wp-dfw-touch .wp-fullscreen-title, +.wp-fullscreen-active.wp-dfw-touch .wp-editor-container { + border-color: #ccc; +} + +.wp-dfw-touch #wp-fullscreen-statusbar { + height: 30px; +} + +@media screen and ( max-width: 782px ) { + #wp-fullscreen-close, + #wp-fullscreen-central-toolbar, + #wp-fullscreen-mode-bar, + #wp-fullscreen-button-bar, + #wp-fullscreen-save { + display: inline-block; + } + + #fullscreen-topbar { + height: 85px; + } + + #wp-fullscreen-central-toolbar { + width: auto !important; + min-width: 0; + } + + #wp-fullscreen-close { + line-height: 30px; + vertical-align: top; + padding: 0 12px; + } + + #wp-fullscreen-button-bar { + position: absolute; + top: 45px; + right: 0; + } + + .wp-fullscreen-wrap { + top: 95px; + } + + #wp-fullscreen-save { + position: absolute; + left: 10px; + } +} + +@media screen and ( max-width: 480px ) { + #wp_fs_help { + display: none; + } + + .wp-fullscreen-wrap .wp-editor-container, + .wp-fullscreen-title { + width: 480px !important; + } + + body.wp-fullscreen-active { + width: 480px; + overflow: auto; + } + + #fullscreen-topbar, + .wp-fullscreen-wrap { + width: 480px; + } + + #fullscreen-topbar { + position: absolute; + } + + #wp-fullscreen-status { + width: auto !important; + max-width: 100%; + padding: 0 10px; + } +} + +/* =Localization +-------------------------------------------------------------- */ +.rtl .wp-switch-editor, +.rtl .quicktags-toolbar input { + font-family: Tahoma, sans-serif; +} + +/* @noflip */ +.mce-rtl .mce-flow-layout .mce-flow-layout-item > div { + direction: rtl; +} + +/* @noflip */ +.mce-rtl .mce-listbox i.mce-caret { + left: 6px; +} + +html:lang(he-il) .rtl .wp-switch-editor, +html:lang(he-il) .rtl .quicktags-toolbar input { + font-family: Arial, sans-serif; +} + +/* HiDPI */ +@media print, + (-o-min-device-pixel-ratio: 5/4), + (-webkit-min-device-pixel-ratio: 1.25), + (min-resolution: 120dpi) { + .wp-media-buttons .add_media span.wp-media-buttons-icon, + #wp-fullscreen-buttons #wp_fs_image span.mce_image { + background: none; + } +} diff --git a/wp-includes/css/editor-rtl.min.css b/wp-includes/css/editor-rtl.min.css new file mode 100644 index 0000000..6f1853d --- /dev/null +++ b/wp-includes/css/editor-rtl.min.css @@ -0,0 +1 @@ +.mce-container,.mce-container *,.mce-widget,.mce-widget *{color:inherit;font-family:inherit}#mce-modal-block.mce-in{opacity:.7;filter:alpha(opacity=70)}.mce-window{-webkit-border-radius:0;border-radius:0;-webkit-box-shadow:0 3px 6px rgba(0,0,0,.3);box-shadow:0 3px 6px rgba(0,0,0,.3);-webkit-font-smoothing:subpixel-antialiased}.mce-window .mce-container-body.mce-abs-layout{overflow:visible}.mce-window .mce-window-head{background:#fcfcfc;border-bottom:1px solid #dfdfdf;padding:0;min-height:36px}.mce-window .mce-window-head .mce-title{color:#444;font-size:18px;font-weight:600;line-height:36px;margin:0;padding:0 16px 0 36px}.mce-window .mce-window-head .mce-close{color:transparent;top:0;left:0;width:36px;height:36px;line-height:36px;text-align:center}.mce-window .mce-window-head .mce-close:before{font:400 20px/36px dashicons;text-align:center;color:#666;width:36px;height:36px;display:block}.mce-window .mce-window-head .mce-close:hover:before{color:#2ea2cc}.mce-window .mce-window-head .mce-dragh{width:-webkit-calc(100% - 36px);width:calc(100% - 36px)}.mce-wp-help .mce-window-head{border-bottom:none}#wp-link .query-results,.mce-checkbox i.mce-i-checkbox,.mce-textbox{border:1px solid #ddd;-webkit-border-radius:0;border-radius:0;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.07);box-shadow:inset 0 1px 2px rgba(0,0,0,.07);-webkit-transition:.05s all ease-in-out;transition:.05s all ease-in-out}#wp-link .query-results:focus,.mce-checkbox:focus i.mce-i-checkbox,.mce-textbox.mce-focus,.mce-textbox:focus{border-color:#5b9dd9;-webkit-box-shadow:0 0 2px rgba(30,140,190,.8);box-shadow:0 0 2px rgba(30,140,190,.8)}.mce-floatpanel.mce-popover,.mce-menu{border-color:rgba(0,0,0,.15);-webkit-border-radius:0;border-radius:0;-webkit-box-shadow:0 3px 5px rgba(0,0,0,.2);box-shadow:0 3px 5px rgba(0,0,0,.2)}.mce-floatpanel.mce-popover.mce-bottom{margin-top:2px}.mce-floatpanel .mce-arrow{display:none}.mce-menu .mce-container-body{min-width:160px}.mce-menu-item{border:none;margin-bottom:2px}.mce-menu-has-icons i.mce-ico{line-height:20px}div.mce-panel{border:0;background:#fff;-webkit-filter:none;filter:none}.mce-panel.mce-menu{border:1px solid #ddd}div.mce-tab{line-height:13px}div.mce-toolbar-grp{border-bottom:1px solid #dedede;background:#f5f5f5;padding:0;position:relative}div.mce-inline-toolbar-grp{border:1px solid #aaa;-webkit-border-radius:2px;border-radius:2px;-webkit-box-shadow:0 1px 3px rgba(0,0,0,.15);box-shadow:0 1px 3px rgba(0,0,0,.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin-bottom:8px;position:absolute;visibility:hidden;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none;z-index:100100}div.mce-wp-image-toolbar>div.mce-stack-layout{padding:1px}div.mce-inline-toolbar-grp.mce-arrow-up{margin-bottom:0;margin-top:8px}div.mce-inline-toolbar-grp:after,div.mce-inline-toolbar-grp:before{position:absolute;right:50%;display:block;width:0;height:0;border-style:solid;border-color:transparent;content:''}div.mce-inline-toolbar-grp.mce-arrow-up:before{top:-18px;border-bottom-color:#aaa;border-width:9px;margin-right:-9px}div.mce-inline-toolbar-grp.mce-arrow-down:before{bottom:-18px;border-top-color:#aaa;border-width:9px;margin-right:-9px}div.mce-inline-toolbar-grp.mce-arrow-up:after{top:-16px;border-bottom-color:#f5f5f5;border-width:8px;margin-right:-8px}div.mce-inline-toolbar-grp.mce-arrow-down:after{bottom:-16px;border-top-color:#f5f5f5;border-width:8px;margin-right:-8px}div.mce-inline-toolbar-grp.mce-arrow-left:after,div.mce-inline-toolbar-grp.mce-arrow-left:before{margin:0}div.mce-inline-toolbar-grp.mce-arrow-left:before{right:20px}div.mce-inline-toolbar-grp.mce-arrow-left:after{right:21px}div.mce-inline-toolbar-grp.mce-arrow-right:after,div.mce-inline-toolbar-grp.mce-arrow-right:before{right:auto;margin:0}div.mce-inline-toolbar-grp.mce-arrow-right:before{left:20px}div.mce-inline-toolbar-grp.mce-arrow-right:after{left:21px}div.mce-inline-toolbar-grp.mce-arrow-full{left:0}div.mce-inline-toolbar-grp.mce-arrow-full>div{width:100%;overflow-x:auto}div.mce-inline-toolbar-grp-active{visibility:visible}div.mce-toolbar-grp>div{padding:3px}.has-dfw div.mce-toolbar-grp .mce-toolbar.mce-first{padding-left:32px}.mce-toolbar .mce-btn-group{margin:0}div.mce-statusbar{border-top:1px solid #e5e5e5}div.mce-path{padding:2px 10px;margin:0}.mce-path,.mce-path .mce-divider,.mce-path-item{font-size:12px;line-height:18px}.mce-toolbar .mce-btn,.qt-dfw{border-color:transparent;background:0 0;-webkit-box-shadow:none;box-shadow:none;text-shadow:none;cursor:pointer}#wp-fullscreen-buttons .mce-btn,.mce-toolbar .mce-btn-group .mce-btn,.qt-dfw{border:1px solid transparent;margin:2px;background-image:none;-webkit-border-radius:2px;border-radius:2px;-webkit-filter:none;filter:none}#wp-fullscreen-buttons .mce-btn:focus,#wp-fullscreen-buttons .mce-btn:hover,.mce-toolbar .mce-btn-group .mce-btn:focus,.mce-toolbar .mce-btn-group .mce-btn:hover,.qt-dfw:focus,.qt-dfw:hover{background:#fafafa;border-color:#999;color:#222;-webkit-box-shadow:inset 0 1px 0 #fff,0 1px 0 rgba(0,0,0,.08);box-shadow:inset 0 1px 0 #fff,0 1px 0 rgba(0,0,0,.08);outline:0}#wp-fullscreen-buttons .mce-btn.mce-active,#wp-fullscreen-buttons .mce-btn:active,.mce-toolbar .mce-btn-group .mce-btn.mce-active,.mce-toolbar .mce-btn-group .mce-btn:active,.qt-dfw.active{background:#ebebeb;border-color:#999;-webkit-box-shadow:inset 0 2px 5px -3px rgba(0,0,0,.3);box-shadow:inset 0 2px 5px -3px rgba(0,0,0,.3)}.mce-toolbar .mce-btn-group .mce-btn.mce-active:hover{border-color:#555}.mce-toolbar .mce-btn-group .mce-btn.mce-active:hover i.mce-ico{color:#555}#wp-fullscreen-buttons .mce-btn.mce-disabled:focus,#wp-fullscreen-buttons .mce-btn.mce-disabled:hover,.mce-toolbar .mce-btn-group .mce-btn.mce-disabled:focus,.mce-toolbar .mce-btn-group .mce-btn.mce-disabled:hover{color:#aaa;background:0 0;border-color:#ddd;text-shadow:0 1px 0 #fff;-webkit-box-shadow:none;box-shadow:none}.mce-toolbar .mce-btn-group .mce-first,.mce-toolbar .mce-btn-group .mce-last{border-color:transparent}.mce-toolbar .mce-btn button,.qt-dfw{padding:2px 3px;line-height:normal}.mce-toolbar .mce-btn i{text-shadow:none}.mce-toolbar .mce-btn-group>div{white-space:normal}.mce-toolbar .mce-colorbutton .mce-open{border-left:0}.mce-toolbar .mce-colorbutton .mce-preview{margin:0;padding:0;top:auto;bottom:2px;right:3px;height:3px;width:20px}.mce-toolbar .mce-btn-group .mce-btn.mce-listbox{-webkit-border-radius:0;border-radius:0;direction:rtl;background:#fff;border:1px solid #ddd;-webkit-box-shadow:inset 0 1px 1px -1px rgba(0,0,0,.2);box-shadow:inset 0 1px 1px -1px rgba(0,0,0,.2)}.mce-toolbar .mce-btn-group .mce-btn.mce-listbox:hover{background-image:none;border-color:#bbb}.mce-toolbar .mce-btn-group .mce-btn.mce-listbox span{font-size:13px}.mce-panel .mce-btn i.mce-caret{border-top:6px solid #777;margin-right:2px;margin-left:2px}.mce-listbox i.mce-caret{left:6px}.mce-panel .mce-btn button.mce-open i.mce-caret,.mce-panel .mce-btn.mce-listbox i.mce-caret{margin-right:0;margin-left:0}.mce-panel .mce-btn:hover i.mce-caret{border-top-color:#333}.mce-panel .mce-active i.mce-caret{border-top:0;border-bottom:6px solid #333;margin-top:7px}.mce-listbox.mce-active i.mce-caret{margin-top:-3px}.mce-toolbar .mce-splitbtn:hover .mce-open{border-left-color:transparent}.mce-toolbar .mce-splitbtn .mce-open.mce-active{-webkit-box-shadow:none;box-shadow:none}.mce-menu .mce-menu-item-normal.mce-active{background-color:#e5e5e5;-webkit-filter:none;filter:none}.mce-menu .mce-menu-item.mce-selected,.mce-menu .mce-menu-item:focus,.mce-menu .mce-menu-item:hover{color:#000;background-color:#bbb;background-image:none;-webkit-filter:none;filter:none}.mce-menu .mce-menu-item.mce-active:hover .mce-text,.mce-menu .mce-menu-item.mce-selected .mce-ico,.mce-menu .mce-menu-item.mce-selected .mce-text,.mce-menu .mce-menu-item:focus .mce-ico,.mce-menu .mce-menu-item:hover .mce-ico,.mce-menu .mce-menu-item:hover .mce-text{color:#000}.mce-menubar{border-color:#e5e5e5;background:#fff;border-width:0 0 1px}.mce-menubar .mce-btn:focus{outline:0}.mce-menu-item-sep:hover,div.mce-menu .mce-menu-item-sep{margin:5px 0 4px}.mce-menubtn span{margin-left:0;padding-right:3px}.mce-menu-has-icons i.mce-ico:before{margin-right:-2px}.mce-primary button,.mce-primary button i{text-align:center;color:#fff;text-shadow:none;padding:0;line-height:26px}.mce-window .mce-btn{color:#555;background:#f7f7f7;text-decoration:none;font-size:13px;line-height:26px;height:28px;margin:0;padding:0;cursor:pointer;border:1px solid #ccc;-webkit-appearance:none;-webkit-border-radius:3px;border-radius:3px;white-space:nowrap;-webkit-box-shadow:inset 0 1px 0 #fff,0 1px 0 rgba(0,0,0,.08);box-shadow:inset 0 1px 0 #fff,0 1px 0 rgba(0,0,0,.08)}.mce-window .mce-btn::-moz-focus-inner{border-width:1px 0;border-style:solid none;border-color:transparent;padding:0}.mce-window .mce-btn:focus,.mce-window .mce-btn:hover{background:#fafafa;border-color:#999;color:#222}.mce-window .mce-btn:focus{-webkit-box-shadow:0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8);box-shadow:0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8)}.mce-window .mce-btn:active{background:#eee;border-color:#999;color:#333;-webkit-box-shadow:inset 0 2px 5px -3px rgba(0,0,0,.5);box-shadow:inset 0 2px 5px -3px rgba(0,0,0,.5)}.mce-window .mce-btn.mce-disabled{color:#aaa;border-color:#ddd;background:#f7f7f7;-webkit-box-shadow:none;box-shadow:none;text-shadow:0 1px 0 #fff;cursor:default}.mce-window .mce-btn.mce-primary{background:#2ea2cc;border-color:#0074a2;-webkit-box-shadow:inset 0 1px 0 rgba(120,200,230,.5),0 1px 0 rgba(0,0,0,.15);box-shadow:inset 0 1px 0 rgba(120,200,230,.5),0 1px 0 rgba(0,0,0,.15);color:#fff;text-decoration:none}.mce-window .mce-btn.mce-primary:focus,.mce-window .mce-btn.mce-primary:hover{background:#1e8cbe;border-color:#0074a2;-webkit-box-shadow:inset 0 1px 0 rgba(120,200,230,.6);box-shadow:inset 0 1px 0 rgba(120,200,230,.6);color:#fff}.mce-window .mce-btn.mce-primary:focus{border-color:#0e3950;-webkit-box-shadow:inset 0 1px 0 rgba(120,200,230,.6),0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8);box-shadow:inset 0 1px 0 rgba(120,200,230,.6),0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8)}.mce-window .mce-btn.mce-primary:active{background:#1b7aa6;border-color:#005684;color:rgba(255,255,255,.95);-webkit-box-shadow:inset 0 1px 0 rgba(0,0,0,.1);box-shadow:inset 0 1px 0 rgba(0,0,0,.1);vertical-align:top}.mce-window .mce-btn.mce-primary.mce-disabled{color:#94cde7;background:#298cba;border-color:#1b607f;-webkit-box-shadow:none;box-shadow:none;text-shadow:0 -1px 0 rgba(0,0,0,.1);cursor:default}.mce-menubtn.mce-fixed-width button span{max-width:80px;padding-left:16px}.mce-charmap{margin:3px}.mce-charmap td{padding:0;border-color:#dfdfdf;cursor:pointer}.mce-charmap td:hover{background:#f3f3f3}.mce-charmap td div{width:18px;height:22px;line-height:22px}.mce-tooltip{margin-top:2px}.mce-tooltip-inner{-webkit-box-shadow:0 3px 5px rgba(0,0,0,.2);box-shadow:0 3px 5px rgba(0,0,0,.2);color:#fff;font-size:12px}.mce-ico{font-family:tinymce,Arial}.mce-btn-small .mce-ico{font-family:tinymce-small,Arial}.mce-toolbar .mce-ico{color:#777;line-height:20px;width:20px;height:20px;text-align:center;text-shadow:none;margin:0;padding:0}.qt-dfw{color:#777;line-height:20px;width:28px;height:26px;text-align:center;text-shadow:none}.mce-toolbar .mce-btn .mce-open{line-height:20px}.mce-toolbar .mce-btn.mce-active .mce-open,.mce-toolbar .mce-btn:focus .mce-open,.mce-toolbar .mce-btn:hover .mce-open{border-right-color:#999}.mce-close,i.mce-i-aligncenter,i.mce-i-alignjustify,i.mce-i-alignleft,i.mce-i-alignright,i.mce-i-backcolor,i.mce-i-blockquote,i.mce-i-bold,i.mce-i-bullist,i.mce-i-charmap,i.mce-i-dashicon,i.mce-i-dfw,i.mce-i-forecolor,i.mce-i-fullscreen,i.mce-i-help,i.mce-i-hr,i.mce-i-indent,i.mce-i-italic,i.mce-i-link,i.mce-i-ltr,i.mce-i-numlist,i.mce-i-outdent,i.mce-i-pastetext,i.mce-i-pasteword,i.mce-i-redo,i.mce-i-removeformat,i.mce-i-spellchecker,i.mce-i-strikethrough,i.mce-i-underline,i.mce-i-undo,i.mce-i-unlink,i.mce-i-wp-media-library,i.mce-i-wp_adv,i.mce-i-wp_fullscreen,i.mce-i-wp_help,i.mce-i-wp_more,i.mce-i-wp_page{font:400 20px/1 dashicons;padding:0 0 0 2px;vertical-align:top;speak:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;margin-right:-2px}.qt-dfw{font:400 20px/1 dashicons;vertical-align:top;speak:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}i.mce-i-bold:before{content:'\f200'}i.mce-i-italic:before{content:'\f201'}i.mce-i-bullist:before{content:'\f203'}i.mce-i-numlist:before{content:'\f204'}i.mce-i-blockquote:before{content:'\f205'}i.mce-i-alignleft:before{content:'\f206'}i.mce-i-aligncenter:before{content:'\f207'}i.mce-i-alignright:before{content:'\f208'}i.mce-i-link:before{content:'\f103'}i.mce-i-unlink:before{content:'\f225'}i.mce-i-wp_more:before{content:'\f209'}i.mce-i-strikethrough:before{content:'\f224'}i.mce-i-spellchecker:before{content:'\f210'}.qt-dfw:before,i.mce-i-dfw:before,i.mce-i-fullscreen:before,i.mce-i-wp_fullscreen:before{content:'\f211'}i.mce-i-wp_adv:before{content:'\f212'}i.mce-i-underline:before{content:'\f213'}i.mce-i-alignjustify:before{content:'\f214'}i.mce-i-backcolor:before,i.mce-i-forecolor:before{content:'\f215'}i.mce-i-pastetext:before{content:'\f217'}i.mce-i-removeformat:before{content:'\f218'}i.mce-i-charmap:before{content:'\f220'}i.mce-i-outdent:before{content:'\f221'}i.mce-i-indent:before{content:'\f222'}i.mce-i-undo:before{content:'\f171'}i.mce-i-redo:before{content:'\f172'}i.mce-i-help:before,i.mce-i-wp_help:before{content:'\f223'}i.mce-i-wp-media-library:before{content:'\f104'}i.mce-i-ltr:before{content:'\f320'}i.mce-i-wp_page:before{content:'\f105'}i.mce-i-hr:before{content:'\f460'}.mce-close:before{content:'\f158'}.mce-i-wp_code:before{content:'\f475'}.wp-editor-wrap{position:relative}.wp-editor-tools{position:relative;z-index:1}.wp-editor-tools:after{clear:both;content:'';display:table}.wp-editor-container{clear:both}.wp-editor-area{font-family:Consolas,Monaco,monospace;font-size:13px;padding:10px;margin:1px 0 0;line-height:150%;border:0;outline:0;display:block;resize:vertical;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.rtl .wp-editor-area{font-family:Tahoma,Monaco,monospace}.locale-he-il .wp-editor-area{font-family:Arial,Monaco,monospace}.wp-editor-container textarea.wp-editor-area{width:100%;margin:0;-webkit-box-shadow:none;box-shadow:none}.wp-editor-tabs{float:left}.wp-switch-editor{float:right;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;position:relative;top:1px;background:#ebebeb;color:#777;cursor:pointer;font:13px/19px "Open Sans",sans-serif;height:20px;margin:5px 5px 0 0;padding:3px 8px 4px;border:1px solid #e5e5e5}.wp-switch-editor:focus{-webkit-box-shadow:0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8);box-shadow:0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8);outline:0;color:#222}.html-active .switch-html:focus,.tmce-active .switch-tmce:focus,.wp-switch-editor:active{-webkit-box-shadow:none;box-shadow:none}.wp-switch-editor:active{background-color:#f5f5f5;-webkit-box-shadow:none;box-shadow:none}.js .tmce-active .wp-editor-area{color:#fff}.tmce-active .quicktags-toolbar{display:none}.html-active .switch-html,.tmce-active .switch-tmce{background:#f5f5f5;color:#555;border-bottom-color:#f5f5f5}.wp-media-buttons{float:right}.wp-media-buttons .button{margin-left:5px;margin-bottom:4px;padding-right:7px;padding-left:7px}.wp-media-buttons .button:active{position:relative;top:1px;margin-top:-1px;margin-bottom:1px}.wp-media-buttons .insert-media{padding-right:5px}.wp-media-buttons a{text-decoration:none;color:#464646;font-size:12px}.wp-media-buttons img{padding:0 4px;vertical-align:middle}.wp-media-buttons span.wp-media-buttons-icon{display:inline-block;width:18px;height:18px;vertical-align:text-top;margin:0 2px}.wp-media-buttons .add_media span.wp-media-buttons-icon{background:0 0}.wp-media-buttons .add_media span.wp-media-buttons-icon:before{font:400 18px/1 dashicons;speak:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f104'}.quicktags-toolbar{padding:3px;position:relative;border-bottom:1px solid #dedede;background:#f5f5f5}.has-dfw .quicktags-toolbar{padding-left:35px}.wp-core-ui .quicktags-toolbar input.button.button-small{margin:2px}.quicktags-toolbar input[value=link]{text-decoration:underline}.quicktags-toolbar input[value=del]{text-decoration:line-through}.quicktags-toolbar input[value="i"]{font-style:italic}.quicktags-toolbar input[value="b"]{font-weight:700}.mce-toolbar .mce-btn-group .mce-btn.mce-wp-dfw,.qt-dfw{position:absolute;top:0;left:0;margin:5px 0 0 5px}.qt-fullscreen{position:static;margin:2px}@media screen and (max-width:782px){.mce-toolbar .mce-btn button,.qt-dfw{padding:6px 7px}#wp-fullscreen-buttons .mce-btn,.mce-toolbar .mce-btn-group .mce-btn{margin:1px}.qt-dfw{width:36px;height:34px}.mce-toolbar .mce-btn-group .mce-btn.mce-wp-dfw{margin:4px 0 0 4px}.mce-toolbar .mce-colorbutton .mce-preview{right:8px;bottom:6px}.mce-window .mce-btn{padding:2px 0}.has-dfw .quicktags-toolbar,.has-dfw div.mce-toolbar-grp .mce-toolbar.mce-first{padding-left:40px}}@media screen and (min-width:782px){.wp-core-ui .quicktags-toolbar input.button.button-small{font-size:12px;height:26px;line-height:24px}}#wp_editbtns,#wp_gallerybtns{padding:2px;position:absolute;display:none;z-index:100020}#wp_delgallery,#wp_delimgbtn,#wp_editgallery,#wp_editimgbtn{border-color:#999;background-color:#eee;margin:2px;padding:2px;border-width:1px;border-style:solid;-webkit-border-radius:3px;border-radius:3px}#wp_delgallery:hover,#wp_delimgbtn:hover,#wp_editgallery:hover,#wp_editimgbtn:hover{border-color:#555;background-color:#ccc}#wp-link-wrap{display:none;background-color:#fff;-webkit-box-shadow:0 3px 6px rgba(0,0,0,.3);box-shadow:0 3px 6px rgba(0,0,0,.3);width:500px;height:250px;overflow:hidden;margin-right:-250px;margin-top:-125px;position:fixed;top:50%;right:50%;z-index:100105;-webkit-transition:height .2s,margin-top .2s;transition:height .2s,margin-top .2s}#wp-link-backdrop{display:none;position:fixed;top:0;right:0;left:0;bottom:0;min-height:360px;background:#000;opacity:.7;filter:alpha(opacity=70);z-index:100100}#wp-link{position:relative;height:100%}#wp-link-wrap.search-panel-visible{height:500px;margin-top:-250px}#link-modal-title{background:#fcfcfc;border-bottom:1px solid #dfdfdf;height:36px;font-size:18px;font-weight:600;line-height:36px;padding:0 16px 0 36px;top:0;left:0;right:0}#wp-link-close{color:#666;padding:0;position:absolute;top:0;left:0;width:36px;height:36px;text-align:center;background:0 0;border:none;cursor:pointer}#wp-link-close:before{font:400 20px/36px dashicons;vertical-align:top;speak:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;width:36px;height:36px;content:'\f158'}#wp-link-close:focus,#wp-link-close:hover{color:#2ea2cc}#wp-link-close:focus{outline:0;-webkit-box-shadow:0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8);box-shadow:0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8)}#link-selector{padding:0 16px 50px}#wp-link-wrap.search-panel-visible #link-selector{padding:0 16px;position:absolute;top:36px;right:0;left:0;bottom:44px}#wp-link ol,#wp-link ul{list-style:none;margin:0;padding:0}#wp-link-search-toggle:after{display:inline-block;font:400 20px/1 dashicons;vertical-align:top;speak:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f140'}.search-panel-visible #wp-link-search-toggle:after{content:'\f142'}#wp-link input[type=text]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}#wp-link #link-options{padding:8px 0 12px}#wp-link p.howto{margin:3px 0}#wp-link p.howto a{text-decoration:none;color:inherit}#wp-link-search-toggle{cursor:pointer}#wp-link label input[type=text]{margin-top:5px;width:70%}#wp-link #link-options label span,#wp-link #search-panel label span.search-label{display:inline-block;width:80px;text-align:left;padding-left:5px;max-width:24%}#wp-link .link-search-field{float:right;width:250px;max-width:70%}#wp-link .link-search-wrapper{margin:5px 0 9px;display:block;overflow:hidden}#wp-link .link-search-wrapper span{float:right;margin-top:4px}#wp-link .link-search-wrapper .spinner{display:none;vertical-align:text-bottom}#wp-link .link-target{padding:3px 0 0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}#wp-link .link-target label{max-width:70%}#wp-link .query-results{border:1px solid #dfdfdf;margin:0;background:#fff;overflow:auto;position:absolute;right:16px;left:16px;bottom:16px;top:205px}#wp-link li{clear:both;margin-bottom:0;border-bottom:1px solid #f1f1f1;color:#333;padding:4px 10px 4px 6px;cursor:pointer;position:relative}#wp-link .query-notice{padding:0;border-bottom:1px solid #dfdfdf;background-color:#f7fcfe;color:#000}#wp-link .query-notice .query-notice-default,#wp-link .query-notice .query-notice-hint{display:block;padding:6px;border-right:4px solid #2ea2cc}#wp-link .unselectable.no-matches-found{padding:0;border-bottom:1px solid #dfdfdf;background-color:#fef7f1}#wp-link .no-matches-found .item-title{display:block;padding:6px;border-right:4px solid #d54e21}#wp-link .query-results em{font-style:normal}#wp-link li:hover{background:#eaf2fa;color:#151515}#wp-link li.unselectable{border-bottom:1px solid #dfdfdf}#wp-link li.unselectable:hover{background:#fff;cursor:auto;color:#333}#wp-link li.selected{background:#ddd;color:#333}#wp-link li.selected .item-title{font-weight:700}#wp-link li:last-child{border:none}#wp-link .item-title{display:inline-block;width:80%;width:-webkit-calc(100% - 68px);width:calc(100% - 68px)}#wp-link .item-info{text-transform:uppercase;color:#666;font-size:11px;position:absolute;left:5px;top:5px}#wp-link #search-panel,#wp-link #search-results{display:none}#wp-link-wrap.search-panel-visible #search-panel{display:block}#wp-link .river-waiting{display:none;padding:10px 0}#wp-link .river-waiting .spinner{margin:0 auto;display:block;float:none}#wp-link .submitbox{padding:8px 16px;background:#fcfcfc;border-top:1px solid #dfdfdf;position:absolute;bottom:0;right:0;left:0}#wp-link-cancel{line-height:25px;float:right}#wp-link-update{line-height:23px;float:left}#wp-link-submit{float:left;margin-bottom:0}@media screen and (max-width:782px){#wp-link-wrap{height:280px;margin-top:-140px}#wp-link-wrap.search-panel-visible .query-results{top:235px}#link-selector{padding:0 16px 60px}#wp-link-wrap.search-panel-visible #link-selector{bottom:52px}#wp-link-cancel{line-height:32px}}@media screen and (max-width:520px){#wp-link-wrap{width:auto;margin-right:0;right:10px;left:10px;max-width:500px}}@media screen and (max-height:520px){#wp-link-wrap{-webkit-transition:none;transition:none}#wp-link-wrap.search-panel-visible{height:auto;margin-top:0;top:10px;bottom:10px}.search-panel-visible #link-selector{overflow:auto}.search-panel-visible #search-panel .query-results{position:static}}@media screen and (max-height:290px){#wp-link-wrap{height:auto;margin-top:0;top:10px;bottom:10px}#link-selector{overflow:auto;height:-webkit-calc(100% - 92px);height:calc(100% - 92px);padding-bottom:2px}#search-panel .query-results{position:static}}.fullscreen-overlay{z-index:100005;display:none;position:fixed;top:0;bottom:0;right:0;left:0;-webkit-filter:inherit;filter:inherit}.wp-fullscreen-active #wp-fullscreen-body,.wp-fullscreen-active .fullscreen-overlay{display:block}.fullscreen-fader{z-index:200000}.wp-core-ui.wp-fullscreen-active .postbox-container,.wp-fullscreen-active .fullscreen-fader{display:none}#wp-fullscreen-body,.mce-fullscreen{z-index:100010}#wp-fullscreen-body{display:none}.wp-fullscreen-wrap{margin:0;padding:0;position:absolute;right:0;left:0;bottom:30px;top:60px;z-index:100015}#wp-fullscreen-central-toolbar,.wp-fullscreen-title,.wp-fullscreen-wrap .wp-editor-container{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;max-width:100%}.wp-fullscreen-active .mce-statusbar,.wp-fullscreen-active .mce-toolbar-grp,.wp-fullscreen-active .quicktags-toolbar,.wp-fullscreen-active .wp-editor-tools{display:none}#wp-fullscreen-statusbar{position:fixed;right:0;left:0;bottom:0;height:30px;z-index:100020;background:#fff;-webkit-transition:height .2s;transition:height .2s}#wp-fullscreen-status{margin:0 auto;padding:0}.wp-fullscreen-active .wp-editor-container,.wp-fullscreen-active .wp-fullscreen-title,.wp-fullscreen-active .wp-fullscreen-title:focus{-webkit-border-radius:0;border-radius:0;border:1px dashed transparent;background:0 0;-webkit-box-shadow:none;box-shadow:none;-webkit-transition:border-color .4s;transition:border-color .4s}.wp-fullscreen-active .wp-editor-container{margin:0 auto 40px}.wp-fullscreen-active .wp-fullscreen-title{font-size:1.7em;line-height:100%;outline:0;padding:3px 7px;margin:10px auto 30px;display:block}#wp-fullscreen-tagline{color:#888;font-size:18px;float:left;padding:4px 0 0}#fullscreen-topbar{background:#f5f5f5;border-bottom:1px solid #dedede;height:45px;position:fixed;right:0;left:0;top:0;width:100%;z-index:100020;-webkit-transition:opacity .4s;transition:opacity .4s}#wp-fullscreen-toolbar{padding:6px 10px 0;clear:both;max-width:1100px;margin:0 auto}#wp-fullscreen-button-bar,#wp-fullscreen-close,#wp-fullscreen-mode-bar{float:right}#wp-fullscreen-count,#wp-fullscreen-tagline{display:inline-block}#wp-fullscreen-button-bar{margin-top:2px}#wp-fullscreen-save{float:left;padding:2px 0 0;min-width:95px}#wp-fullscreen-close,#wp-fullscreen-count{padding:5px 0 0}#wp-fullscreen-central-toolbar{margin:auto;padding:0;min-width:620px}#wp-fullscreen-buttons>div{float:right}#wp-fullscreen-mode-bar{padding:3px 0 0 14px}#wp-fullscreen-buttons .hidden{display:none}#wp-fullscreen-buttons .disabled{opacity:.5}#wp-fullscreen-buttons .mce-btn button{margin:0;outline:0;border:0;white-space:nowrap;width:auto;background:0 0;color:#333;cursor:pointer;font-size:18px;line-height:20px;overflow:visible;text-align:center;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.wp-html-mode #wp-fullscreen-buttons div{display:none}.wp-html-mode #wp-fullscreen-buttons div.wp-fullscreen-both{display:block}#wp-fullscreen-save img{vertical-align:middle}#wp-fullscreen-save span{display:none;margin:5px 6px 0;float:right}.wp-fullscreen-active #TB_overlay{z-index:100050}.wp-fullscreen-active #TB_window{z-index:100051}.fullscreen-overlay{background:#fff}.wp-fullscreen-active #fullscreen-topbar{-webkit-transition-duration:.8s;transition-duration:.8s;opacity:0;filter:alpha(opacity=0)}.wp-fullscreen-active #wp-fullscreen-statusbar{height:0}.wp-fullscreen-active.wp-dfw-show-ui #fullscreen-topbar{-webkit-transition-duration:.4s;transition-duration:.4s;opacity:1;filter:alpha(opacity=100)}.wp-fullscreen-active.wp-dfw-show-ui #wp-fullscreen-statusbar{height:29px;background:#f8f8f8;border-top:1px solid #eee}.wp-fullscreen-active .wp-editor-container,.wp-fullscreen-active .wp-fullscreen-title{-webkit-transition-duration:.8s;transition-duration:.8s;border-color:transparent}.wp-fullscreen-active.wp-dfw-show-ui .wp-editor-container,.wp-fullscreen-active.wp-dfw-show-ui .wp-fullscreen-title{-webkit-transition-duration:.4s;transition-duration:.4s;border-color:#ccc}.fade-1000,.fade-300,.fade-400,.fade-600{opacity:0;-webkit-transition-property:opacity;transition-property:opacity}.fade-1000{-webkit-transition-duration:1s;transition-duration:1s}.fade-600{-webkit-transition-duration:.6s;transition-duration:.6s}.fade-400{-webkit-transition-duration:.4s;transition-duration:.4s}.fade-300{-webkit-transition-duration:.3s;transition-duration:.3s}.fade-trigger{opacity:1}.wp-dfw-touch #fullscreen-topbar{position:absolute;opacity:1}.wp-dfw-touch .wp-fullscreen-title,.wp-dfw-touch .wp-fullscreen-wrap .wp-editor-container{max-width:700px}.wp-fullscreen-active.wp-dfw-touch .wp-editor-container,.wp-fullscreen-active.wp-dfw-touch .wp-fullscreen-title{border-color:#ccc}.wp-dfw-touch #wp-fullscreen-statusbar{height:30px}@media screen and (max-width:782px){#wp-fullscreen-button-bar,#wp-fullscreen-central-toolbar,#wp-fullscreen-close,#wp-fullscreen-mode-bar,#wp-fullscreen-save{display:inline-block}#fullscreen-topbar{height:85px}#wp-fullscreen-central-toolbar{width:auto!important;min-width:0}#wp-fullscreen-close{line-height:30px;vertical-align:top;padding:0 12px}#wp-fullscreen-button-bar{position:absolute;top:45px;right:0}.wp-fullscreen-wrap{top:95px}#wp-fullscreen-save{position:absolute;left:10px}}@media screen and (max-width:480px){#wp_fs_help{display:none}.wp-fullscreen-title,.wp-fullscreen-wrap .wp-editor-container{width:480px!important}body.wp-fullscreen-active{width:480px;overflow:auto}#fullscreen-topbar,.wp-fullscreen-wrap{width:480px}#fullscreen-topbar{position:absolute}#wp-fullscreen-status{width:auto!important;max-width:100%;padding:0 10px}}.rtl .quicktags-toolbar input,.rtl .wp-switch-editor{font-family:Tahoma,sans-serif}.mce-rtl .mce-flow-layout .mce-flow-layout-item>div{direction:rtl}.mce-rtl .mce-listbox i.mce-caret{left:6px}html:lang(he-il) .rtl .quicktags-toolbar input,html:lang(he-il) .rtl .wp-switch-editor{font-family:Arial,sans-serif}@media print,(-o-min-device-pixel-ratio:5/4),(-webkit-min-device-pixel-ratio:1.25),(min-resolution:120dpi){#wp-fullscreen-buttons #wp_fs_image span.mce_image,.wp-media-buttons .add_media span.wp-media-buttons-icon{background:0 0}} \ No newline at end of file diff --git a/wp-includes/css/editor.css b/wp-includes/css/editor.css new file mode 100644 index 0000000..b90bdf8 --- /dev/null +++ b/wp-includes/css/editor.css @@ -0,0 +1,2125 @@ +/*------------------------------------------------------------------------------ + TinyMCE and Quicklinks toolbars +------------------------------------------------------------------------------*/ + +/* TinyMCE widgets/containers */ + +.mce-container, +.mce-container *, +.mce-widget, +.mce-widget * { + color: inherit; + font-family: inherit; +} + +/* TinyMCE windows */ +#mce-modal-block.mce-in { + opacity: 0.7; + filter: alpha(opacity=70); +} + +.mce-window { + -webkit-border-radius: 0; + border-radius: 0; + -webkit-box-shadow: 0 3px 6px rgba( 0, 0, 0, 0.3 ); + box-shadow: 0 3px 6px rgba( 0, 0, 0, 0.3 ); + -webkit-font-smoothing: subpixel-antialiased; +} + +.mce-window .mce-container-body.mce-abs-layout { + overflow: visible; +} + +.mce-window .mce-window-head { + background: #fcfcfc; + border-bottom: 1px solid #dfdfdf; + padding: 0; + min-height: 36px; +} + +.mce-window .mce-window-head .mce-title { + color: #444; + font-size: 18px; + font-weight: 600; + line-height: 36px; + margin: 0; + padding: 0 36px 0 16px; +} + +.mce-window .mce-window-head .mce-close { + color: transparent; + top: 0; + right: 0; + width: 36px; + height: 36px; + line-height: 36px; + text-align: center; +} + +.mce-window .mce-window-head .mce-close:before { + font: normal 20px/36px 'dashicons'; + text-align: center; + color: #666; + width: 36px; + height: 36px; + display: block; +} + +.mce-window .mce-window-head .mce-close:hover:before { + color: #2ea2cc; +} + +.mce-window .mce-window-head .mce-dragh { + width: -webkit-calc( 100% - 36px ); + width: calc( 100% - 36px ); +} + +.mce-wp-help .mce-window-head { + border-bottom: none; +} + +.mce-textbox, +.mce-checkbox i.mce-i-checkbox, +#wp-link .query-results { + border: 1px solid #ddd; + -webkit-border-radius: 0; + border-radius: 0; + -webkit-box-shadow: inset 0 1px 2px rgba(0,0,0,0.07); + box-shadow: inset 0 1px 2px rgba(0,0,0,0.07); + -webkit-transition: .05s all ease-in-out; + transition: .05s all ease-in-out; +} + +.mce-textbox:focus, +.mce-textbox.mce-focus, +.mce-checkbox:focus i.mce-i-checkbox, +#wp-link .query-results:focus { + border-color: #5b9dd9; + -webkit-box-shadow: 0 0 2px rgba(30,140,190,0.8); + box-shadow: 0 0 2px rgba(30,140,190,0.8); +} + +/* TinyMCE menus */ +.mce-menu, +.mce-floatpanel.mce-popover { + border-color: rgba(0,0,0,0.15); + -webkit-border-radius: 0; + border-radius: 0; + -webkit-box-shadow: 0 3px 5px rgba( 0, 0, 0, 0.2 ); + box-shadow: 0 3px 5px rgba( 0, 0, 0, 0.2 ); +} + +.mce-floatpanel.mce-popover.mce-bottom { + margin-top: 2px; +} + +.mce-floatpanel .mce-arrow { + display: none; +} + +.mce-menu .mce-container-body { + min-width: 160px; +} + +.mce-menu-item { + border: none; + margin-bottom: 2px; +} + +.mce-menu-has-icons i.mce-ico { + line-height: 20px; +} + +/* TinyMCE panel */ +div.mce-panel { + border: 0; + background: #fff; + -webkit-filter: none; + filter: none; +} + +.mce-panel.mce-menu { + border: 1px solid #ddd; +} + +div.mce-tab { + line-height: 13px; +} + +/* TinyMCE toolbars */ +div.mce-toolbar-grp { + border-bottom: 1px solid #dedede; + background: #f5f5f5; + padding: 0; + position: relative; +} + +div.mce-inline-toolbar-grp { + border: 1px solid #aaa; + -webkit-border-radius: 2px; + border-radius: 2px; + -webkit-box-shadow: 0 1px 3px rgba( 0, 0, 0, 0.15 ); + box-shadow: 0 1px 3px rgba( 0, 0, 0, 0.15 ); + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + margin-bottom: 8px; + position: absolute; + visibility: hidden; + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + z-index: 100100; /* Same as the other TinyMCE "panels" */ +} + +div.mce-wp-image-toolbar > div.mce-stack-layout { + padding: 1px; +} + +div.mce-inline-toolbar-grp.mce-arrow-up { + margin-bottom: 0; + margin-top: 8px; +} + +div.mce-inline-toolbar-grp:before, +div.mce-inline-toolbar-grp:after { + position: absolute; + left: 50%; + display: block; + width: 0; + height: 0; + border-style: solid; + border-color: transparent; + content: ''; +} + +div.mce-inline-toolbar-grp.mce-arrow-up:before { + top: -18px; + border-bottom-color: #aaa; + border-width: 9px; + margin-left: -9px; +} + +div.mce-inline-toolbar-grp.mce-arrow-down:before { + bottom: -18px; + border-top-color: #aaa; + border-width: 9px; + margin-left: -9px; +} + +div.mce-inline-toolbar-grp.mce-arrow-up:after { + top: -16px; + border-bottom-color: #f5f5f5; + border-width: 8px; + margin-left: -8px; +} + +div.mce-inline-toolbar-grp.mce-arrow-down:after { + bottom: -16px; + border-top-color: #f5f5f5; + border-width: 8px; + margin-left: -8px; +} + +div.mce-inline-toolbar-grp.mce-arrow-left:before, +div.mce-inline-toolbar-grp.mce-arrow-left:after { + margin: 0; +} + +div.mce-inline-toolbar-grp.mce-arrow-left:before { + left: 20px; +} +div.mce-inline-toolbar-grp.mce-arrow-left:after { + left: 21px; +} + +div.mce-inline-toolbar-grp.mce-arrow-right:before, +div.mce-inline-toolbar-grp.mce-arrow-right:after { + left: auto; + margin: 0; +} + +div.mce-inline-toolbar-grp.mce-arrow-right:before { + right: 20px; +} + +div.mce-inline-toolbar-grp.mce-arrow-right:after { + right: 21px; +} + +div.mce-inline-toolbar-grp.mce-arrow-full { + right: 0; +} + +div.mce-inline-toolbar-grp.mce-arrow-full > div { + width: 100%; + overflow-x: auto; +} + +div.mce-inline-toolbar-grp-active { + visibility: visible; +} + +div.mce-toolbar-grp > div { + padding: 3px; +} + +.has-dfw div.mce-toolbar-grp .mce-toolbar.mce-first { + padding-right: 32px; +} + +.mce-toolbar .mce-btn-group { + margin: 0; +} + +div.mce-statusbar { + border-top: 1px solid #e5e5e5; +} + +div.mce-path { + padding: 2px 10px; + margin: 0; +} + +.mce-path, +.mce-path-item, +.mce-path .mce-divider { + font-size: 12px; + line-height: 18px; +} + +.mce-toolbar .mce-btn, +.qt-dfw { + border-color: transparent; + background: transparent; + -webkit-box-shadow: none; + box-shadow: none; + text-shadow: none; + cursor: pointer; +} + +#wp-fullscreen-buttons .mce-btn, +.mce-toolbar .mce-btn-group .mce-btn, +.qt-dfw { + border: 1px solid transparent; + margin: 2px; + background-image: none; + -webkit-border-radius: 2px; + border-radius: 2px; + -webkit-filter: none; + filter: none; +} + +#wp-fullscreen-buttons .mce-btn:hover, +.mce-toolbar .mce-btn-group .mce-btn:hover, +#wp-fullscreen-buttons .mce-btn:focus, +.mce-toolbar .mce-btn-group .mce-btn:focus, +.qt-dfw:hover, +.qt-dfw:focus { + background: #fafafa; + border-color: #999; + color: #222; + -webkit-box-shadow: inset 0 1px 0 #fff, 0 1px 0 rgba( 0, 0, 0, 0.08 ); + box-shadow: inset 0 1px 0 #fff, 0 1px 0 rgba( 0, 0, 0, 0.08 ); + outline: none; +} + +.mce-toolbar .mce-btn-group .mce-btn.mce-active, +#wp-fullscreen-buttons .mce-btn.mce-active, +.mce-toolbar .mce-btn-group .mce-btn:active, +#wp-fullscreen-buttons .mce-btn:active, +.qt-dfw.active { + background: #ebebeb; + border-color: #999; + -webkit-box-shadow: inset 0 2px 5px -3px rgba( 0, 0, 0, 0.3 ); + box-shadow: inset 0 2px 5px -3px rgba( 0, 0, 0, 0.3 ); +} + +.mce-toolbar .mce-btn-group .mce-btn.mce-active:hover { + border-color: #555; +} + +.mce-toolbar .mce-btn-group .mce-btn.mce-active:hover i.mce-ico { + color: #555; +} + +.mce-toolbar .mce-btn-group .mce-btn.mce-disabled:hover, +#wp-fullscreen-buttons .mce-btn.mce-disabled:hover, +.mce-toolbar .mce-btn-group .mce-btn.mce-disabled:focus, +#wp-fullscreen-buttons .mce-btn.mce-disabled:focus { + color: #aaa; + background: none; + border-color: #ddd; + text-shadow: 0 1px 0 #fff; + -webkit-box-shadow: none; + box-shadow: none; +} + +.mce-toolbar .mce-btn-group .mce-first, +.mce-toolbar .mce-btn-group .mce-last { + border-color: transparent; +} + +.mce-toolbar .mce-btn button, +.qt-dfw { + padding: 2px 3px; + line-height: normal; +} + +.mce-toolbar .mce-btn i { + text-shadow: none; +} + +.mce-toolbar .mce-btn-group > div { + white-space: normal; +} + +.mce-toolbar .mce-colorbutton .mce-open { + border-right: 0; +} + +.mce-toolbar .mce-colorbutton .mce-preview { + margin: 0; + padding: 0; + top: auto; + bottom: 2px; + left: 3px; + height: 3px; + width: 20px; +} + +/* mce listbox */ +.mce-toolbar .mce-btn-group .mce-btn.mce-listbox { + -webkit-border-radius: 0; + border-radius: 0; + direction: ltr; + background: #fff; + border: 1px solid #ddd; + -webkit-box-shadow: inset 0 1px 1px -1px rgba(0, 0, 0, .2); + box-shadow: inset 0 1px 1px -1px rgba(0, 0, 0, .2); +} + +.mce-toolbar .mce-btn-group .mce-btn.mce-listbox:hover { + background-image: none; + border-color: #bbb; +} + +.mce-toolbar .mce-btn-group .mce-btn.mce-listbox span { + font-size: 13px; +} + +.mce-panel .mce-btn i.mce-caret { + border-top: 6px solid #777; + margin-left: 2px; + margin-right: 2px; +} + +.mce-listbox i.mce-caret { + right: 6px; +} + +.mce-panel .mce-btn button.mce-open i.mce-caret, +.mce-panel .mce-btn.mce-listbox i.mce-caret { + margin-left: 0; + margin-right: 0; +} + +.mce-panel .mce-btn:hover i.mce-caret { + border-top-color: #333; +} + +.mce-panel .mce-active i.mce-caret { + border-top: 0; + border-bottom: 6px solid #333; + margin-top: 7px; +} + +.mce-listbox.mce-active i.mce-caret { + margin-top: -3px; +} + +.mce-toolbar .mce-splitbtn:hover .mce-open { + border-right-color: transparent; +} + +.mce-toolbar .mce-splitbtn .mce-open.mce-active{ + -webkit-box-shadow: none; + box-shadow: none; +} + +.mce-menu .mce-menu-item-normal.mce-active { + background-color: #e5e5e5; + -webkit-filter: none; + filter: none; +} + +.mce-menu .mce-menu-item:hover, +.mce-menu .mce-menu-item.mce-selected, +.mce-menu .mce-menu-item:focus { + color: #000; + background-color: #bbb; + background-image: none; + -webkit-filter: none; + filter: none; +} + +.mce-menu .mce-menu-item:hover .mce-ico, +.mce-menu .mce-menu-item.mce-selected .mce-ico, +.mce-menu .mce-menu-item:focus .mce-ico, +.mce-menu .mce-menu-item:hover .mce-text, +.mce-menu .mce-menu-item.mce-active:hover .mce-text, +.mce-menu .mce-menu-item.mce-selected .mce-text { + color: #000; +} + +/* Menubar */ +.mce-menubar { + border-color: #e5e5e5; + background: #fff; + border-width: 0px 0px 1px; +} + +.mce-menubar .mce-btn:focus { + outline: 0; +} + +div.mce-menu .mce-menu-item-sep, +.mce-menu-item-sep:hover { + margin: 5px 0 4px; +} + +.mce-menubtn span { + margin-right: 0; + padding-left: 3px; +} + +.mce-menu-has-icons i.mce-ico:before { + margin-left: -2px; +} + +/* Buttons in modals */ +.mce-primary button, +.mce-primary button i { + text-align: center; + color: #fff; + text-shadow: none; + padding: 0; + line-height: 26px; +} + +.mce-window .mce-btn { + color: #555; + background: #f7f7f7; + text-decoration: none; + font-size: 13px; + line-height: 26px; + height: 28px; + margin: 0; + padding: 0; + cursor: pointer; + border: 1px solid #ccc; + -webkit-appearance: none; + -webkit-border-radius: 3px; + border-radius: 3px; + white-space: nowrap; + -webkit-box-shadow: inset 0 1px 0 #fff, 0 1px 0 rgba( 0, 0, 0, 0.08 ); + box-shadow: inset 0 1px 0 #fff, 0 1px 0 rgba( 0, 0, 0, 0.08 ); + background-image: none; +} + +/* Remove the dotted border on :focus and the extra padding in Firefox */ +.mce-window .mce-btn::-moz-focus-inner { + border-width: 1px 0; + border-style: solid none; + border-color: transparent; + padding: 0; +} + +.mce-window .mce-btn:hover, +.mce-window .mce-btn:focus { + background: #fafafa; + border-color: #999; + color: #222; +} + +.mce-window .mce-btn:focus { + -webkit-box-shadow: + 0 0 0 1px #5b9dd9, + 0 0 2px 1px rgba( 30, 140, 190, 0.8 ); + box-shadow: + 0 0 0 1px #5b9dd9, + 0 0 2px 1px rgba( 30, 140, 190, 0.8 ); +} + +.mce-window .mce-btn:active { + background: #eee; + border-color: #999; + color: #333; + -webkit-box-shadow: inset 0 2px 5px -3px rgba( 0, 0, 0, 0.5 ); + box-shadow: inset 0 2px 5px -3px rgba( 0, 0, 0, 0.5 ); +} + +.mce-window .mce-btn.mce-disabled { + color: #aaa; + border-color: #ddd; + background: #f7f7f7; + -webkit-box-shadow: none; + box-shadow: none; + text-shadow: 0 1px 0 #fff; + cursor: default; +} + +.mce-window .mce-btn.mce-primary { + background: #2ea2cc; + border-color: #0074a2; + -webkit-box-shadow: inset 0 1px 0 rgba( 120, 200, 230, 0.5), 0 1px 0 rgba( 0, 0, 0, 0.15 ); + box-shadow: inset 0 1px 0 rgba( 120, 200, 230, 0.5 ), 0 1px 0 rgba( 0, 0, 0, 0.15 ); + color: #fff; + text-decoration: none; +} + +.mce-window .mce-btn.mce-primary:hover, +.mce-window .mce-btn.mce-primary:focus { + background: #1e8cbe; + border-color: #0074a2; + -webkit-box-shadow: inset 0 1px 0 rgba( 120, 200, 230, 0.6 ); + box-shadow: inset 0 1px 0 rgba( 120, 200, 230, 0.6 ); + color: #fff; +} + +.mce-window .mce-btn.mce-primary:focus { + border-color: #0e3950; + -webkit-box-shadow: + inset 0 1px 0 rgba( 120, 200, 230, 0.6 ), + 0 0 0 1px #5b9dd9, + 0 0 2px 1px rgba( 30, 140, 190, 0.8 ); + box-shadow: + inset 0 1px 0 rgba( 120, 200, 230, 0.6 ), + 0 0 0 1px #5b9dd9, + 0 0 2px 1px rgba( 30, 140, 190, 0.8 ); +} + +.mce-window .mce-btn.mce-primary:active { + background: #1b7aa6; + border-color: #005684; + color: rgba( 255, 255, 255, 0.95 ); + -webkit-box-shadow: inset 0 1px 0 rgba( 0, 0, 0, 0.1 ); + box-shadow: inset 0 1px 0 rgba( 0, 0, 0, 0.1 ); + vertical-align: top; +} + +.mce-window .mce-btn.mce-primary.mce-disabled { + color: #94cde7; + background: #298cba; + border-color: #1b607f; + -webkit-box-shadow: none; + box-shadow: none; + text-shadow: 0 -1px 0 rgba( 0, 0, 0, 0.1 ); + cursor: default; +} + +.mce-menubtn.mce-fixed-width button span { + max-width: 80px; + padding-right: 16px; +} + +/* Charmap modal */ +.mce-charmap { + margin: 3px; +} + +.mce-charmap td { + padding: 0; + border-color: #dfdfdf; + cursor: pointer; +} + +.mce-charmap td:hover { + background: #f3f3f3; +} + +.mce-charmap td div { + width: 18px; + height: 22px; + line-height: 22px; +} + +/* TinyMCE tooltips */ +.mce-tooltip { + margin-top: 2px; +} + +.mce-tooltip-inner { + -webkit-box-shadow: 0 3px 5px rgba( 0, 0, 0, 0.2 ); + box-shadow: 0 3px 5px rgba( 0, 0, 0, 0.2 ); + color: #fff; + font-size: 12px; +} + +/* TinyMCE icons */ +.mce-ico { + font-family: 'tinymce', Arial; +} + +.mce-btn-small .mce-ico { + font-family: 'tinymce-small', Arial; +} + +.mce-toolbar .mce-ico { + color: #777; + line-height: 20px; + width: 20px; + height: 20px; + text-align: center; + text-shadow: none; + margin: 0; + padding: 0; +} + +.qt-dfw { + color: #777; + line-height: 20px; + width: 28px; + height: 26px; + text-align: center; + text-shadow: none; +} + +.mce-toolbar .mce-btn .mce-open { + line-height: 20px; +} + +.mce-toolbar .mce-btn:hover .mce-open, +.mce-toolbar .mce-btn:focus .mce-open, +.mce-toolbar .mce-btn.mce-active .mce-open { + border-left-color: #999; +} + +i.mce-i-bold, +i.mce-i-italic, +i.mce-i-bullist, +i.mce-i-numlist, +i.mce-i-blockquote, +i.mce-i-alignleft, +i.mce-i-aligncenter, +i.mce-i-alignright, +i.mce-i-link, +i.mce-i-unlink, +i.mce-i-wp_more, +i.mce-i-strikethrough, +i.mce-i-spellchecker, +i.mce-i-fullscreen, +i.mce-i-wp_fullscreen, +i.mce-i-dfw, +i.mce-i-wp_adv, +i.mce-i-underline, +i.mce-i-alignjustify, +i.mce-i-forecolor, +i.mce-i-backcolor, +i.mce-i-pastetext, +i.mce-i-pasteword, +i.mce-i-removeformat, +i.mce-i-charmap, +i.mce-i-outdent, +i.mce-i-indent, +i.mce-i-undo, +i.mce-i-redo, +i.mce-i-help, +i.mce-i-wp_help, +i.mce-i-wp-media-library, +i.mce-i-ltr, +i.mce-i-wp_page, +i.mce-i-hr, +i.mce-i-dashicon, +.mce-close { + font: normal 20px/1 'dashicons'; + padding: 0; + vertical-align: top; + speak: none; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + margin-left: -2px; + padding-right: 2px; +} + +.qt-dfw { + font: normal 20px/1 'dashicons'; + vertical-align: top; + speak: none; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +i.mce-i-bold:before { + content: '\f200'; +} + +i.mce-i-italic:before { + content: '\f201'; +} + +i.mce-i-bullist:before { + content: '\f203'; +} + +i.mce-i-numlist:before { + content: '\f204'; +} + +i.mce-i-blockquote:before { + content: '\f205'; +} + +i.mce-i-alignleft:before { + content: '\f206'; +} + +i.mce-i-aligncenter:before { + content: '\f207'; +} + +i.mce-i-alignright:before { + content: '\f208'; +} + +i.mce-i-link:before { + content: '\f103'; +} + +i.mce-i-unlink:before { + content: '\f225'; +} + +i.mce-i-wp_more:before { + content: '\f209'; +} + +i.mce-i-strikethrough:before { + content: '\f224'; +} + +i.mce-i-spellchecker:before { + content: '\f210'; +} + +i.mce-i-fullscreen:before, +i.mce-i-wp_fullscreen:before, +i.mce-i-dfw:before, +.qt-dfw:before { + content: '\f211'; +} + +i.mce-i-wp_adv:before { + content: '\f212'; +} + +i.mce-i-underline:before { + content: '\f213'; +} + +i.mce-i-alignjustify:before { + content: '\f214'; +} + +i.mce-i-forecolor:before, +i.mce-i-backcolor:before { + content: '\f215'; +} + +i.mce-i-pastetext:before { + content: '\f217'; +} + +i.mce-i-removeformat:before { + content: '\f218'; +} + +i.mce-i-charmap:before { + content: '\f220'; +} + +i.mce-i-outdent:before { + content: '\f221'; +} + +i.mce-i-indent:before { + content: '\f222'; +} + +i.mce-i-undo:before { + content: '\f171'; +} + +i.mce-i-redo:before { + content: '\f172'; +} + +i.mce-i-help:before, +i.mce-i-wp_help:before { + content: '\f223'; +} + +i.mce-i-wp-media-library:before { + content: '\f104'; +} + +i.mce-i-ltr:before { + content: '\f320'; +} + +i.mce-i-wp_page:before { + content: '\f105'; +} + +i.mce-i-hr:before { + content: '\f460'; +} + +.mce-close:before { + content: '\f158'; +} + +.mce-i-wp_code:before { + content: '\f475'; +} + +/* Editors */ +.wp-editor-wrap { + position: relative; +} + +.wp-editor-tools { + position: relative; + z-index: 1; +} + +.wp-editor-tools:after { + clear: both; + content: ''; + display: table; +} + +.wp-editor-container { + clear: both; +} + +.wp-editor-area { + font-family: Consolas, Monaco, monospace; + font-size: 13px; + padding: 10px; + margin: 1px 0 0; + line-height: 150%; + border: 0 none; + outline: none; + display: block; + resize: vertical; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.rtl .wp-editor-area { + font-family: Tahoma, Monaco, monospace; +} + +.locale-he-il .wp-editor-area { + font-family: Arial, Monaco, monospace; +} + +.wp-editor-container textarea.wp-editor-area { + width: 100%; + margin: 0; + -webkit-box-shadow: none; + box-shadow: none; +} + +.wp-editor-tabs { + float: right; +} + +.wp-switch-editor { + float: left; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + position: relative; + top: 1px; + background: #ebebeb; + color: #777; + cursor: pointer; + font: 13px/19px "Open Sans", sans-serif; + height: 20px; + margin: 5px 0 0 5px; + padding: 3px 8px 4px; + border: 1px solid #e5e5e5; +} + +.wp-switch-editor:focus { + -webkit-box-shadow: + 0 0 0 1px #5b9dd9, + 0 0 2px 1px rgba(30, 140, 190, .8); + box-shadow: + 0 0 0 1px #5b9dd9, + 0 0 2px 1px rgba(30, 140, 190, .8); + outline: none; + color: #222; +} + +.wp-switch-editor:active, +.html-active .switch-html:focus, +.tmce-active .switch-tmce:focus { + -webkit-box-shadow: none; + box-shadow: none; +} + +.wp-switch-editor:active { + background-color: #f5f5f5; + -webkit-box-shadow: none; + box-shadow: none; +} + +.js .tmce-active .wp-editor-area { + color: white; +} + +.tmce-active .quicktags-toolbar { + display: none; +} + +.tmce-active .switch-tmce, +.html-active .switch-html { + background: #f5f5f5; + color: #555; + border-bottom-color: #f5f5f5; +} + +.wp-media-buttons { + float: left; +} + +.wp-media-buttons .button { + margin-right: 5px; + margin-bottom: 4px; + padding-left: 7px; + padding-right: 7px; +} + +.wp-media-buttons .button:active { + position: relative; + top: 1px; + margin-top: -1px; + margin-bottom: 1px; +} + +.wp-media-buttons .insert-media { + padding-left: 5px; +} + +.wp-media-buttons a { + text-decoration: none; + color: #464646; + font-size: 12px; +} + +.wp-media-buttons img { + padding: 0 4px; + vertical-align: middle; +} + +.wp-media-buttons span.wp-media-buttons-icon { + display: inline-block; + width: 18px; + height: 18px; + vertical-align: text-top; + margin: 0 2px; +} + +.wp-media-buttons .add_media span.wp-media-buttons-icon { + background: none; +} + +.wp-media-buttons .add_media span.wp-media-buttons-icon:before { + font: normal 18px/1 'dashicons'; + speak: none; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.wp-media-buttons .add_media span.wp-media-buttons-icon:before { + content: '\f104'; +} + +/* Quicktags */ +.quicktags-toolbar { + padding: 3px; + position: relative; + border-bottom: 1px solid #dedede; + background: #f5f5f5; +} + +.has-dfw .quicktags-toolbar { + padding-right: 35px; +} + +.wp-core-ui .quicktags-toolbar input.button.button-small { + margin: 2px; +} + +.quicktags-toolbar input[value="link"] { + text-decoration: underline; +} + +.quicktags-toolbar input[value="del"] { + text-decoration: line-through; +} + +.quicktags-toolbar input[value="i"] { + font-style: italic; +} + +.quicktags-toolbar input[value="b"] { + font-weight: bold; +} + +.mce-toolbar .mce-btn-group .mce-btn.mce-wp-dfw, +.qt-dfw { + position: absolute; + top: 0; + right: 0; + margin: 5px 5px 0 0; +} + +.qt-fullscreen { + position: static; + margin: 2px; +} + +@media screen and ( max-width: 782px ) { + .mce-toolbar .mce-btn button, + .qt-dfw { + padding: 6px 7px; + } + + #wp-fullscreen-buttons .mce-btn, + .mce-toolbar .mce-btn-group .mce-btn { + margin: 1px; + } + + .qt-dfw { + width: 36px; + height: 34px; + } + + .mce-toolbar .mce-btn-group .mce-btn.mce-wp-dfw { + margin: 4px 4px 0 0; + } + + .mce-toolbar .mce-colorbutton .mce-preview { + left: 8px; + bottom: 6px; + } + + .mce-window .mce-btn { + padding: 2px 0; + } + + .has-dfw div.mce-toolbar-grp .mce-toolbar.mce-first, + .has-dfw .quicktags-toolbar { + padding-right: 40px; + } +} + +@media screen and ( min-width: 782px ) { + .wp-core-ui .quicktags-toolbar input.button.button-small { + /* .button-small is normaly 11px, but a bit too small for these buttons. */ + font-size: 12px; + height: 26px; + line-height: 24px; + } +} + +#wp_editbtns, +#wp_gallerybtns { + padding: 2px; + position: absolute; + display: none; + z-index: 100020; +} + +#wp_editimgbtn, +#wp_delimgbtn, +#wp_editgallery, +#wp_delgallery { + border-color: #999; + background-color: #eee; + margin: 2px; + padding: 2px; + border-width: 1px; + border-style: solid; + -webkit-border-radius: 3px; + border-radius: 3px; +} + +#wp_editimgbtn:hover, +#wp_delimgbtn:hover, +#wp_editgallery:hover, +#wp_delgallery:hover { + border-color: #555; + background-color: #ccc; +} + +/*------------------------------------------------------------------------------ + wp-link +------------------------------------------------------------------------------*/ + +#wp-link-wrap { + display: none; + background-color: #fff; + -webkit-box-shadow: 0 3px 6px rgba( 0, 0, 0, 0.3 ); + box-shadow: 0 3px 6px rgba( 0, 0, 0, 0.3 ); + width: 500px; + height: 250px; + overflow: hidden; + margin-left: -250px; + margin-top: -125px; + position: fixed; + top: 50%; + left: 50%; + z-index: 100105; + -webkit-transition: height 0.2s, margin-top 0.2s; + transition: height 0.2s, margin-top 0.2s; +} + +#wp-link-backdrop { + display: none; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + min-height: 360px; + background: #000; + opacity: 0.7; + filter: alpha(opacity=70); + z-index: 100100; +} + +#wp-link { + position: relative; + height: 100%; +} + +#wp-link-wrap.search-panel-visible { + height: 500px; + margin-top: -250px; +} + +#link-modal-title { + background: #fcfcfc; + border-bottom: 1px solid #dfdfdf; + height: 36px; + font-size: 18px; + font-weight: 600; + line-height: 36px; + padding: 0 36px 0 16px; + top: 0; + right: 0; + left: 0; +} + +#wp-link-close { + color: #666; + padding: 0; + position: absolute; + top: 0; + right: 0; + width: 36px; + height: 36px; + text-align: center; + background: none; + border: none; + cursor: pointer; +} + +#wp-link-close:before { + font: normal 20px/36px 'dashicons'; + vertical-align: top; + speak: none; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + width: 36px; + height: 36px; + content: '\f158'; +} + +#wp-link-close:hover, +#wp-link-close:focus { + color: #2ea2cc; +} + +#wp-link-close:focus { + outline: none; + -webkit-box-shadow: + 0 0 0 1px #5b9dd9, + 0 0 2px 1px rgba(30, 140, 190, .8); + box-shadow: + 0 0 0 1px #5b9dd9, + 0 0 2px 1px rgba(30, 140, 190, .8); +} + +#link-selector { + padding: 0 16px 50px; +} + +#wp-link-wrap.search-panel-visible #link-selector { + padding: 0 16px; + position: absolute; + top: 36px; + left: 0; + right: 0; + bottom: 44px; +} + +#wp-link ol, +#wp-link ul { + list-style: none; + margin: 0; + padding: 0; +} + +#wp-link-search-toggle:after { + display: inline-block; + font: normal 20px/1 'dashicons'; + vertical-align: top; + speak: none; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + content: '\f140'; +} + +.search-panel-visible #wp-link-search-toggle:after { + content: '\f142'; +} + +#wp-link input[type="text"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +#wp-link #link-options { + padding: 8px 0 12px; +} + +#wp-link p.howto { + margin: 3px 0; +} + +#wp-link p.howto a { + text-decoration: none; + color: inherit; +} + +#wp-link-search-toggle { + cursor: pointer; +} + +#wp-link label input[type="text"] { + margin-top: 5px; + width: 70%; +} + +#wp-link #link-options label span, +#wp-link #search-panel label span.search-label { + display: inline-block; + width: 80px; + text-align: right; + padding-right: 5px; + max-width: 24%; +} + +#wp-link .link-search-field { + float: left; + width: 250px; + max-width: 70%; +} + +#wp-link .link-search-wrapper { + margin: 5px 0 9px; + display: block; + overflow: hidden; +} + +#wp-link .link-search-wrapper span { + float: left; + margin-top: 4px; +} + +#wp-link .link-search-wrapper .spinner { + display: none; + vertical-align: text-bottom; +} + +#wp-link .link-target { + padding: 3px 0 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +#wp-link .link-target label { + max-width: 70%; +} + +#wp-link .query-results { + border: 1px #dfdfdf solid; + margin: 0; + background: #fff; + overflow: auto; + position: absolute; + left: 16px; + right: 16px; + bottom: 16px; + top: 205px; +} + +#wp-link li { + clear: both; + margin-bottom: 0; + border-bottom: 1px solid #f1f1f1; + color: #333; + padding: 4px 6px 4px 10px; + cursor: pointer; + position: relative; +} + +#wp-link .query-notice { + padding: 0; + border-bottom: 1px solid #dfdfdf; + background-color: #f7fcfe; + color: #000; +} + +#wp-link .query-notice .query-notice-default, +#wp-link .query-notice .query-notice-hint { + display: block; + padding: 6px; + border-left: 4px solid #2ea2cc; +} + +#wp-link .unselectable.no-matches-found { + padding: 0; + border-bottom: 1px solid #dfdfdf; + background-color: #fef7f1; +} + +#wp-link .no-matches-found .item-title { + display: block; + padding: 6px; + border-left: 4px solid #d54e21; +} + +#wp-link .query-results em { + font-style: normal; +} + +#wp-link li:hover { + background: #eaf2fa; + color: #151515; +} + +#wp-link li.unselectable { + border-bottom: 1px solid #dfdfdf; +} + +#wp-link li.unselectable:hover { + background: #fff; + cursor: auto; + color: #333; +} + +#wp-link li.selected { + background: #ddd; + color: #333; +} + +#wp-link li.selected .item-title { + font-weight: bold; +} + +#wp-link li:last-child { + border: none; +} + +#wp-link .item-title { + display: inline-block; + width: 80%; + width: -webkit-calc(100% - 68px); + width: calc(100% - 68px); +} + +#wp-link .item-info { + text-transform: uppercase; + color: #666; + font-size: 11px; + position: absolute; + right: 5px; + top: 5px; +} + +#wp-link #search-results, +#wp-link #search-panel { + display: none; +} + +#wp-link-wrap.search-panel-visible #search-panel { + display: block; +} + +#wp-link .river-waiting { + display: none; + padding: 10px 0; +} + +#wp-link .river-waiting .spinner { + margin: 0 auto; + display: block; + float: none; +} + +#wp-link .submitbox { + padding: 8px 16px; + background: #fcfcfc; + border-top: 1px solid #dfdfdf; + position: absolute; + bottom: 0; + left: 0; + right: 0; +} + +#wp-link-cancel { + line-height: 25px; + float: left; +} + +#wp-link-update { + line-height: 23px; + float: right; +} + +#wp-link-submit { + float: right; + margin-bottom: 0; +} + +@media screen and ( max-width: 782px ) { + #wp-link-wrap { + height: 280px; + margin-top: -140px; + } + + #wp-link-wrap.search-panel-visible .query-results { + top: 235px; + } + + #link-selector { + padding: 0 16px 60px; + } + + #wp-link-wrap.search-panel-visible #link-selector { + bottom: 52px; + } + + #wp-link-cancel { + line-height: 32px; + } +} + +@media screen and ( max-width: 520px ) { + #wp-link-wrap { + width: auto; + margin-left: 0; + left: 10px; + right: 10px; + max-width: 500px; + } +} + +@media screen and ( max-height: 520px ) { + #wp-link-wrap { + -webkit-transition: none; + transition: none; + } + + #wp-link-wrap.search-panel-visible { + height: auto; + margin-top: 0; + top: 10px; + bottom: 10px; + } + + .search-panel-visible #link-selector { + overflow: auto; + } + + .search-panel-visible #search-panel .query-results { + position: static; + } +} + +@media screen and ( max-height: 290px ) { + #wp-link-wrap { + height: auto; + margin-top: 0; + top: 10px; + bottom: 10px; + } + + #link-selector { + overflow: auto; + height: -webkit-calc(100% - 92px); + height: calc(100% - 92px); + padding-bottom: 2px; + } + + #search-panel .query-results { + position: static; + } +} + +/* Old TinyMCE 3.x modal */ +/* +.clearlooks2 .mceTop { + border-bottom: 1px solid #ccc; +} + +.clearlooks2 .mceTop span { + font: 13px/24px "Open Sans", sans-serif; + color: #e5e5e5; +} + +.clearlooks2 .mceTop .mceLeft { + background: #444444; + border-color: transparent; +} + +.clearlooks2 .mceTop .mceRight { + background: #444444; + border-color: transparent; +} + +.clearlooks2 .mceMiddle { + clip: rect(24px auto auto auto); +} + +.clearlooks2 .mceMiddle .mceLeft { + background: #f1f1f1; + border-color: transparent; +} + +.clearlooks2 .mceMiddle .mceRight { + background: #f1f1f1; + border-color: transparent; +} + +.clearlooks2 .mceBottom { + background: #f1f1f1; + border-color: transparent; +} + +.clearlooks2 .mceBottom .mceLeft { + background: #f1f1f1; + border-color: transparent; +} + +.clearlooks2 .mceBottom .mceCenter { + background: #f1f1f1; + border-color: transparent; +} + +.clearlooks2 .mceBottom .mceRight { + background: #f1f1f1; + border-color: transparent; +} + +.clearlooks2 .mceClose, +.clearlooks2 .mceFocus .mceClose, +.clearlooks2 .mceFocus .mceClose:hover { + background-image: none; +} +.clearlooks2 .mceClose:before { + content: '\f158'; + font: normal 20px/1 'dashicons'; + speak: none; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + color: #999; + padding-left: 12px; +} + +/* from colors.css +.clearlooks2 { + box-shadow: 0 5px 15px rgba(0,0,0,0.7); +} + +.clearlooks2 .mceMiddle span, +.clearlooks2 .mceMiddle .mceLeft, +.clearlooks2 .mceMiddle .mceRight, +.clearlooks2 .mceBottom, +.clearlooks2 .mceBottom .mceLeft, +.clearlooks2 .mceBottom .mceCenter, +.clearlooks2 .mceBottom .mceRight { + background-color: #fcfcfc; +} + +.clearlooks2 .mceTop span, +.clearlooks2 .mceFocus .mceTop span { + color: #222; +} + +.clearlooks2 .mceClose:before { + color: #999; +} + +.clearlooks2 .mceClose:hover:before { + color: #2ea2cc; +} +*/ +/* Distraction-Free Writing mode + * =Overlay Styles +-------------------------------------------------------------- */ +.fullscreen-overlay { + z-index: 100005; + display: none; + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; + -webkit-filter: inherit; + filter: inherit; +} + +.wp-fullscreen-active .fullscreen-overlay, +.wp-fullscreen-active #wp-fullscreen-body { + display: block; +} + +.fullscreen-fader { + z-index: 200000; +} + +.wp-fullscreen-active .fullscreen-fader, +.wp-core-ui.wp-fullscreen-active .postbox-container { + display: none; +} + +/* =Overlay Body +-------------------------------------------------------------- */ + +#wp-fullscreen-body, +.mce-fullscreen { + z-index: 100010; +} + +#wp-fullscreen-body { + display: none; +} + +.wp-fullscreen-wrap { + margin: 0; + padding: 0; + position: absolute; + left: 0; + right: 0; + bottom: 30px; + top: 60px; + z-index: 100015; +} + +.wp-fullscreen-wrap .wp-editor-container, +.wp-fullscreen-title, +#wp-fullscreen-central-toolbar { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + max-width: 100%; +} + +.wp-fullscreen-active .wp-editor-tools, +.wp-fullscreen-active .quicktags-toolbar, +.wp-fullscreen-active .mce-toolbar-grp, +.wp-fullscreen-active .mce-statusbar { + display: none; +} + +#wp-fullscreen-statusbar { + position: fixed; + left: 0; + right: 0; + bottom: 0; + height: 30px; + z-index: 100020; + background: #fff; + -webkit-transition: height 0.2s; + transition: height 0.2s; +} + +#wp-fullscreen-status { + margin: 0 auto; + padding: 0; +} + +.wp-fullscreen-active .wp-fullscreen-title, +.wp-fullscreen-active .wp-fullscreen-title:focus, +.wp-fullscreen-active .wp-editor-container { + -webkit-border-radius: 0; + border-radius: 0; + border: 1px dashed transparent; + background: transparent; + -webkit-box-shadow: none; + box-shadow: none; + -webkit-transition: border-color 0.4s; + transition: border-color 0.4s; +} + +.wp-fullscreen-active .wp-editor-container { + margin: 0 auto 40px; +} + +.wp-fullscreen-active .wp-fullscreen-title { + font-size: 1.7em; + line-height: 100%; + outline: medium none; + padding: 3px 7px; + margin: 10px auto 30px; + display: block; +} + +#wp-fullscreen-tagline { + color: #888; + font-size: 18px; + float: right; + padding: 4px 0 0; +} + +/* =Top bar +-------------------------------------------------------------- */ +#fullscreen-topbar { + background: #f5f5f5; + border-bottom: 1px solid #dedede; + height: 45px; + position: fixed; + left: 0; + right: 0; + top: 0; + width: 100%; + z-index: 100020; + -webkit-transition: opacity 0.4s; + transition: opacity 0.4s; +} + +#wp-fullscreen-toolbar { + padding: 6px 10px 0; + clear: both; + max-width: 1100px; + margin: 0 auto; +} + +#wp-fullscreen-mode-bar, +#wp-fullscreen-button-bar, +#wp-fullscreen-close { + float: left; +} + +#wp-fullscreen-count, +#wp-fullscreen-tagline { + display: inline-block; +} + +#wp-fullscreen-button-bar { + margin-top: 2px; +} + +#wp-fullscreen-save { + float: right; + padding: 2px 0 0; + min-width: 95px; +} + +#wp-fullscreen-count, +#wp-fullscreen-close { + padding: 5px 0 0; +} + +#wp-fullscreen-central-toolbar { + margin: auto; + padding: 0; + min-width: 620px; +} + +#wp-fullscreen-buttons > div { + float: left; +} + +#wp-fullscreen-mode-bar { + padding: 3px 14px 0 0; +} + +#wp-fullscreen-buttons .hidden { + display: none; +} + +#wp-fullscreen-buttons .disabled { + opacity: 0.5; +} + +#wp-fullscreen-buttons .mce-btn button { + margin: 0; + outline: 0 none; + border: 0 none; + white-space: nowrap; + width: auto; + background: none; + color: #333333; + cursor: pointer; + font-size: 18px; + line-height: 20px; + overflow: visible; + text-align: center; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.wp-html-mode #wp-fullscreen-buttons div { + display: none; +} + +.wp-html-mode #wp-fullscreen-buttons div.wp-fullscreen-both { + display: block; +} + +#wp-fullscreen-save img { + vertical-align: middle; +} + +#wp-fullscreen-save span { + display: none; + margin: 5px 6px 0; + float: left; +} + +/* =Thickbox Adjustments +-------------------------------------------------------------- */ +.wp-fullscreen-active #TB_overlay { + z-index: 100050; +} + +.wp-fullscreen-active #TB_window { + z-index: 100051; +} + +/* Colors */ +.fullscreen-overlay { + background: #fff; +} + +/* =CSS 3 transitions +-------------------------------------------------------------- */ + +.wp-fullscreen-active #fullscreen-topbar { + -webkit-transition-duration: 0.8s; + transition-duration: 0.8s; + opacity: 0; + filter: alpha(opacity=0); +} + +.wp-fullscreen-active #wp-fullscreen-statusbar { + height: 0; +} + +.wp-fullscreen-active.wp-dfw-show-ui #fullscreen-topbar { + -webkit-transition-duration: 0.4s; + transition-duration: 0.4s; + opacity: 1; + filter: alpha(opacity=100); +} + +.wp-fullscreen-active.wp-dfw-show-ui #wp-fullscreen-statusbar { + height: 29px; + background: #f8f8f8; + border-top: 1px solid #eee; +} + +.wp-fullscreen-active .wp-fullscreen-title, +.wp-fullscreen-active .wp-editor-container { + -webkit-transition-duration: 0.8s; + transition-duration: 0.8s; + border-color: transparent; +} + +.wp-fullscreen-active.wp-dfw-show-ui .wp-fullscreen-title, +.wp-fullscreen-active.wp-dfw-show-ui .wp-editor-container { + -webkit-transition-duration: 0.4s; + transition-duration: 0.4s; + border-color: #ccc; +} + +.fade-1000, +.fade-600, +.fade-400, +.fade-300 { + opacity: 0; + -webkit-transition-property: opacity; + transition-property: opacity; +} + +.fade-1000 { + -webkit-transition-duration: 1s; + transition-duration: 1s; +} + +.fade-600 { + -webkit-transition-duration: 0.6s; + transition-duration: 0.6s; +} + +.fade-400 { + -webkit-transition-duration: 0.4s; + transition-duration: 0.4s; +} + +.fade-300 { + -webkit-transition-duration: 0.3s; + transition-duration: 0.3s; +} + +.fade-trigger { + opacity: 1; +} + +/* DFW on touch screen devices */ +.wp-dfw-touch #fullscreen-topbar { + position: absolute; + opacity: 1; +} + +.wp-dfw-touch .wp-fullscreen-wrap .wp-editor-container, +.wp-dfw-touch .wp-fullscreen-title { + max-width: 700px; +} + +.wp-fullscreen-active.wp-dfw-touch .wp-fullscreen-title, +.wp-fullscreen-active.wp-dfw-touch .wp-editor-container { + border-color: #ccc; +} + +.wp-dfw-touch #wp-fullscreen-statusbar { + height: 30px; +} + +@media screen and ( max-width: 782px ) { + #wp-fullscreen-close, + #wp-fullscreen-central-toolbar, + #wp-fullscreen-mode-bar, + #wp-fullscreen-button-bar, + #wp-fullscreen-save { + display: inline-block; + } + + #fullscreen-topbar { + height: 85px; + } + + #wp-fullscreen-central-toolbar { + width: auto !important; + min-width: 0; + } + + #wp-fullscreen-close { + line-height: 30px; + vertical-align: top; + padding: 0 12px; + } + + #wp-fullscreen-button-bar { + position: absolute; + top: 45px; + left: 0; + } + + .wp-fullscreen-wrap { + top: 95px; + } + + #wp-fullscreen-save { + position: absolute; + right: 10px; + } +} + +@media screen and ( max-width: 480px ) { + #wp_fs_help { + display: none; + } + + .wp-fullscreen-wrap .wp-editor-container, + .wp-fullscreen-title { + width: 480px !important; + } + + body.wp-fullscreen-active { + width: 480px; + overflow: auto; + } + + #fullscreen-topbar, + .wp-fullscreen-wrap { + width: 480px; + } + + #fullscreen-topbar { + position: absolute; + } + + #wp-fullscreen-status { + width: auto !important; + max-width: 100%; + padding: 0 10px; + } +} + +/* =Localization +-------------------------------------------------------------- */ +.rtl .wp-switch-editor, +.rtl .quicktags-toolbar input { + font-family: Tahoma, sans-serif; +} + +/* @noflip */ +.mce-rtl .mce-flow-layout .mce-flow-layout-item > div { + direction: rtl; +} + +/* @noflip */ +.mce-rtl .mce-listbox i.mce-caret { + left: 6px; +} + +html:lang(he-il) .rtl .wp-switch-editor, +html:lang(he-il) .rtl .quicktags-toolbar input { + font-family: Arial, sans-serif; +} + +/* HiDPI */ +@media print, + (-o-min-device-pixel-ratio: 5/4), + (-webkit-min-device-pixel-ratio: 1.25), + (min-resolution: 120dpi) { + .wp-media-buttons .add_media span.wp-media-buttons-icon, + #wp-fullscreen-buttons #wp_fs_image span.mce_image { + background: none; + } +} diff --git a/wp-includes/css/editor.min.css b/wp-includes/css/editor.min.css new file mode 100644 index 0000000..4706e05 --- /dev/null +++ b/wp-includes/css/editor.min.css @@ -0,0 +1 @@ +.mce-container,.mce-container *,.mce-widget,.mce-widget *{color:inherit;font-family:inherit}#mce-modal-block.mce-in{opacity:.7;filter:alpha(opacity=70)}.mce-window{-webkit-border-radius:0;border-radius:0;-webkit-box-shadow:0 3px 6px rgba(0,0,0,.3);box-shadow:0 3px 6px rgba(0,0,0,.3);-webkit-font-smoothing:subpixel-antialiased}.mce-window .mce-container-body.mce-abs-layout{overflow:visible}.mce-window .mce-window-head{background:#fcfcfc;border-bottom:1px solid #dfdfdf;padding:0;min-height:36px}.mce-window .mce-window-head .mce-title{color:#444;font-size:18px;font-weight:600;line-height:36px;margin:0;padding:0 36px 0 16px}.mce-window .mce-window-head .mce-close{color:transparent;top:0;right:0;width:36px;height:36px;line-height:36px;text-align:center}.mce-window .mce-window-head .mce-close:before{font:400 20px/36px dashicons;text-align:center;color:#666;width:36px;height:36px;display:block}.mce-window .mce-window-head .mce-close:hover:before{color:#2ea2cc}.mce-window .mce-window-head .mce-dragh{width:-webkit-calc(100% - 36px);width:calc(100% - 36px)}.mce-wp-help .mce-window-head{border-bottom:none}#wp-link .query-results,.mce-checkbox i.mce-i-checkbox,.mce-textbox{border:1px solid #ddd;-webkit-border-radius:0;border-radius:0;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.07);box-shadow:inset 0 1px 2px rgba(0,0,0,.07);-webkit-transition:.05s all ease-in-out;transition:.05s all ease-in-out}#wp-link .query-results:focus,.mce-checkbox:focus i.mce-i-checkbox,.mce-textbox.mce-focus,.mce-textbox:focus{border-color:#5b9dd9;-webkit-box-shadow:0 0 2px rgba(30,140,190,.8);box-shadow:0 0 2px rgba(30,140,190,.8)}.mce-floatpanel.mce-popover,.mce-menu{border-color:rgba(0,0,0,.15);-webkit-border-radius:0;border-radius:0;-webkit-box-shadow:0 3px 5px rgba(0,0,0,.2);box-shadow:0 3px 5px rgba(0,0,0,.2)}.mce-floatpanel.mce-popover.mce-bottom{margin-top:2px}.mce-floatpanel .mce-arrow{display:none}.mce-menu .mce-container-body{min-width:160px}.mce-menu-item{border:none;margin-bottom:2px}.mce-menu-has-icons i.mce-ico{line-height:20px}div.mce-panel{border:0;background:#fff;-webkit-filter:none;filter:none}.mce-panel.mce-menu{border:1px solid #ddd}div.mce-tab{line-height:13px}div.mce-toolbar-grp{border-bottom:1px solid #dedede;background:#f5f5f5;padding:0;position:relative}div.mce-inline-toolbar-grp{border:1px solid #aaa;-webkit-border-radius:2px;border-radius:2px;-webkit-box-shadow:0 1px 3px rgba(0,0,0,.15);box-shadow:0 1px 3px rgba(0,0,0,.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin-bottom:8px;position:absolute;visibility:hidden;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none;z-index:100100}div.mce-wp-image-toolbar>div.mce-stack-layout{padding:1px}div.mce-inline-toolbar-grp.mce-arrow-up{margin-bottom:0;margin-top:8px}div.mce-inline-toolbar-grp:after,div.mce-inline-toolbar-grp:before{position:absolute;left:50%;display:block;width:0;height:0;border-style:solid;border-color:transparent;content:''}div.mce-inline-toolbar-grp.mce-arrow-up:before{top:-18px;border-bottom-color:#aaa;border-width:9px;margin-left:-9px}div.mce-inline-toolbar-grp.mce-arrow-down:before{bottom:-18px;border-top-color:#aaa;border-width:9px;margin-left:-9px}div.mce-inline-toolbar-grp.mce-arrow-up:after{top:-16px;border-bottom-color:#f5f5f5;border-width:8px;margin-left:-8px}div.mce-inline-toolbar-grp.mce-arrow-down:after{bottom:-16px;border-top-color:#f5f5f5;border-width:8px;margin-left:-8px}div.mce-inline-toolbar-grp.mce-arrow-left:after,div.mce-inline-toolbar-grp.mce-arrow-left:before{margin:0}div.mce-inline-toolbar-grp.mce-arrow-left:before{left:20px}div.mce-inline-toolbar-grp.mce-arrow-left:after{left:21px}div.mce-inline-toolbar-grp.mce-arrow-right:after,div.mce-inline-toolbar-grp.mce-arrow-right:before{left:auto;margin:0}div.mce-inline-toolbar-grp.mce-arrow-right:before{right:20px}div.mce-inline-toolbar-grp.mce-arrow-right:after{right:21px}div.mce-inline-toolbar-grp.mce-arrow-full{right:0}div.mce-inline-toolbar-grp.mce-arrow-full>div{width:100%;overflow-x:auto}div.mce-inline-toolbar-grp-active{visibility:visible}div.mce-toolbar-grp>div{padding:3px}.has-dfw div.mce-toolbar-grp .mce-toolbar.mce-first{padding-right:32px}.mce-toolbar .mce-btn-group{margin:0}div.mce-statusbar{border-top:1px solid #e5e5e5}div.mce-path{padding:2px 10px;margin:0}.mce-path,.mce-path .mce-divider,.mce-path-item{font-size:12px;line-height:18px}.mce-toolbar .mce-btn,.qt-dfw{border-color:transparent;background:0 0;-webkit-box-shadow:none;box-shadow:none;text-shadow:none;cursor:pointer}#wp-fullscreen-buttons .mce-btn,.mce-toolbar .mce-btn-group .mce-btn,.qt-dfw{border:1px solid transparent;margin:2px;background-image:none;-webkit-border-radius:2px;border-radius:2px;-webkit-filter:none;filter:none}#wp-fullscreen-buttons .mce-btn:focus,#wp-fullscreen-buttons .mce-btn:hover,.mce-toolbar .mce-btn-group .mce-btn:focus,.mce-toolbar .mce-btn-group .mce-btn:hover,.qt-dfw:focus,.qt-dfw:hover{background:#fafafa;border-color:#999;color:#222;-webkit-box-shadow:inset 0 1px 0 #fff,0 1px 0 rgba(0,0,0,.08);box-shadow:inset 0 1px 0 #fff,0 1px 0 rgba(0,0,0,.08);outline:0}#wp-fullscreen-buttons .mce-btn.mce-active,#wp-fullscreen-buttons .mce-btn:active,.mce-toolbar .mce-btn-group .mce-btn.mce-active,.mce-toolbar .mce-btn-group .mce-btn:active,.qt-dfw.active{background:#ebebeb;border-color:#999;-webkit-box-shadow:inset 0 2px 5px -3px rgba(0,0,0,.3);box-shadow:inset 0 2px 5px -3px rgba(0,0,0,.3)}.mce-toolbar .mce-btn-group .mce-btn.mce-active:hover{border-color:#555}.mce-toolbar .mce-btn-group .mce-btn.mce-active:hover i.mce-ico{color:#555}#wp-fullscreen-buttons .mce-btn.mce-disabled:focus,#wp-fullscreen-buttons .mce-btn.mce-disabled:hover,.mce-toolbar .mce-btn-group .mce-btn.mce-disabled:focus,.mce-toolbar .mce-btn-group .mce-btn.mce-disabled:hover{color:#aaa;background:0 0;border-color:#ddd;text-shadow:0 1px 0 #fff;-webkit-box-shadow:none;box-shadow:none}.mce-toolbar .mce-btn-group .mce-first,.mce-toolbar .mce-btn-group .mce-last{border-color:transparent}.mce-toolbar .mce-btn button,.qt-dfw{padding:2px 3px;line-height:normal}.mce-toolbar .mce-btn i{text-shadow:none}.mce-toolbar .mce-btn-group>div{white-space:normal}.mce-toolbar .mce-colorbutton .mce-open{border-right:0}.mce-toolbar .mce-colorbutton .mce-preview{margin:0;padding:0;top:auto;bottom:2px;left:3px;height:3px;width:20px}.mce-toolbar .mce-btn-group .mce-btn.mce-listbox{-webkit-border-radius:0;border-radius:0;direction:ltr;background:#fff;border:1px solid #ddd;-webkit-box-shadow:inset 0 1px 1px -1px rgba(0,0,0,.2);box-shadow:inset 0 1px 1px -1px rgba(0,0,0,.2)}.mce-toolbar .mce-btn-group .mce-btn.mce-listbox:hover{background-image:none;border-color:#bbb}.mce-toolbar .mce-btn-group .mce-btn.mce-listbox span{font-size:13px}.mce-panel .mce-btn i.mce-caret{border-top:6px solid #777;margin-left:2px;margin-right:2px}.mce-listbox i.mce-caret{right:6px}.mce-panel .mce-btn button.mce-open i.mce-caret,.mce-panel .mce-btn.mce-listbox i.mce-caret{margin-left:0;margin-right:0}.mce-panel .mce-btn:hover i.mce-caret{border-top-color:#333}.mce-panel .mce-active i.mce-caret{border-top:0;border-bottom:6px solid #333;margin-top:7px}.mce-listbox.mce-active i.mce-caret{margin-top:-3px}.mce-toolbar .mce-splitbtn:hover .mce-open{border-right-color:transparent}.mce-toolbar .mce-splitbtn .mce-open.mce-active{-webkit-box-shadow:none;box-shadow:none}.mce-menu .mce-menu-item-normal.mce-active{background-color:#e5e5e5;-webkit-filter:none;filter:none}.mce-menu .mce-menu-item.mce-selected,.mce-menu .mce-menu-item:focus,.mce-menu .mce-menu-item:hover{color:#000;background-color:#bbb;background-image:none;-webkit-filter:none;filter:none}.mce-menu .mce-menu-item.mce-active:hover .mce-text,.mce-menu .mce-menu-item.mce-selected .mce-ico,.mce-menu .mce-menu-item.mce-selected .mce-text,.mce-menu .mce-menu-item:focus .mce-ico,.mce-menu .mce-menu-item:hover .mce-ico,.mce-menu .mce-menu-item:hover .mce-text{color:#000}.mce-menubar{border-color:#e5e5e5;background:#fff;border-width:0 0 1px}.mce-menubar .mce-btn:focus{outline:0}.mce-menu-item-sep:hover,div.mce-menu .mce-menu-item-sep{margin:5px 0 4px}.mce-menubtn span{margin-right:0;padding-left:3px}.mce-menu-has-icons i.mce-ico:before{margin-left:-2px}.mce-primary button,.mce-primary button i{text-align:center;color:#fff;text-shadow:none;padding:0;line-height:26px}.mce-window .mce-btn{color:#555;background:#f7f7f7;text-decoration:none;font-size:13px;line-height:26px;height:28px;margin:0;padding:0;cursor:pointer;border:1px solid #ccc;-webkit-appearance:none;-webkit-border-radius:3px;border-radius:3px;white-space:nowrap;-webkit-box-shadow:inset 0 1px 0 #fff,0 1px 0 rgba(0,0,0,.08);box-shadow:inset 0 1px 0 #fff,0 1px 0 rgba(0,0,0,.08)}.mce-window .mce-btn::-moz-focus-inner{border-width:1px 0;border-style:solid none;border-color:transparent;padding:0}.mce-window .mce-btn:focus,.mce-window .mce-btn:hover{background:#fafafa;border-color:#999;color:#222}.mce-window .mce-btn:focus{-webkit-box-shadow:0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8);box-shadow:0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8)}.mce-window .mce-btn:active{background:#eee;border-color:#999;color:#333;-webkit-box-shadow:inset 0 2px 5px -3px rgba(0,0,0,.5);box-shadow:inset 0 2px 5px -3px rgba(0,0,0,.5)}.mce-window .mce-btn.mce-disabled{color:#aaa;border-color:#ddd;background:#f7f7f7;-webkit-box-shadow:none;box-shadow:none;text-shadow:0 1px 0 #fff;cursor:default}.mce-window .mce-btn.mce-primary{background:#2ea2cc;border-color:#0074a2;-webkit-box-shadow:inset 0 1px 0 rgba(120,200,230,.5),0 1px 0 rgba(0,0,0,.15);box-shadow:inset 0 1px 0 rgba(120,200,230,.5),0 1px 0 rgba(0,0,0,.15);color:#fff;text-decoration:none}.mce-window .mce-btn.mce-primary:focus,.mce-window .mce-btn.mce-primary:hover{background:#1e8cbe;border-color:#0074a2;-webkit-box-shadow:inset 0 1px 0 rgba(120,200,230,.6);box-shadow:inset 0 1px 0 rgba(120,200,230,.6);color:#fff}.mce-window .mce-btn.mce-primary:focus{border-color:#0e3950;-webkit-box-shadow:inset 0 1px 0 rgba(120,200,230,.6),0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8);box-shadow:inset 0 1px 0 rgba(120,200,230,.6),0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8)}.mce-window .mce-btn.mce-primary:active{background:#1b7aa6;border-color:#005684;color:rgba(255,255,255,.95);-webkit-box-shadow:inset 0 1px 0 rgba(0,0,0,.1);box-shadow:inset 0 1px 0 rgba(0,0,0,.1);vertical-align:top}.mce-window .mce-btn.mce-primary.mce-disabled{color:#94cde7;background:#298cba;border-color:#1b607f;-webkit-box-shadow:none;box-shadow:none;text-shadow:0 -1px 0 rgba(0,0,0,.1);cursor:default}.mce-menubtn.mce-fixed-width button span{max-width:80px;padding-right:16px}.mce-charmap{margin:3px}.mce-charmap td{padding:0;border-color:#dfdfdf;cursor:pointer}.mce-charmap td:hover{background:#f3f3f3}.mce-charmap td div{width:18px;height:22px;line-height:22px}.mce-tooltip{margin-top:2px}.mce-tooltip-inner{-webkit-box-shadow:0 3px 5px rgba(0,0,0,.2);box-shadow:0 3px 5px rgba(0,0,0,.2);color:#fff;font-size:12px}.mce-ico{font-family:tinymce,Arial}.mce-btn-small .mce-ico{font-family:tinymce-small,Arial}.mce-toolbar .mce-ico{color:#777;line-height:20px;width:20px;height:20px;text-align:center;text-shadow:none;margin:0;padding:0}.qt-dfw{color:#777;line-height:20px;width:28px;height:26px;text-align:center;text-shadow:none}.mce-toolbar .mce-btn .mce-open{line-height:20px}.mce-toolbar .mce-btn.mce-active .mce-open,.mce-toolbar .mce-btn:focus .mce-open,.mce-toolbar .mce-btn:hover .mce-open{border-left-color:#999}.mce-close,i.mce-i-aligncenter,i.mce-i-alignjustify,i.mce-i-alignleft,i.mce-i-alignright,i.mce-i-backcolor,i.mce-i-blockquote,i.mce-i-bold,i.mce-i-bullist,i.mce-i-charmap,i.mce-i-dashicon,i.mce-i-dfw,i.mce-i-forecolor,i.mce-i-fullscreen,i.mce-i-help,i.mce-i-hr,i.mce-i-indent,i.mce-i-italic,i.mce-i-link,i.mce-i-ltr,i.mce-i-numlist,i.mce-i-outdent,i.mce-i-pastetext,i.mce-i-pasteword,i.mce-i-redo,i.mce-i-removeformat,i.mce-i-spellchecker,i.mce-i-strikethrough,i.mce-i-underline,i.mce-i-undo,i.mce-i-unlink,i.mce-i-wp-media-library,i.mce-i-wp_adv,i.mce-i-wp_fullscreen,i.mce-i-wp_help,i.mce-i-wp_more,i.mce-i-wp_page{font:400 20px/1 dashicons;padding:0 2px 0 0;vertical-align:top;speak:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;margin-left:-2px}.qt-dfw{font:400 20px/1 dashicons;vertical-align:top;speak:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}i.mce-i-bold:before{content:'\f200'}i.mce-i-italic:before{content:'\f201'}i.mce-i-bullist:before{content:'\f203'}i.mce-i-numlist:before{content:'\f204'}i.mce-i-blockquote:before{content:'\f205'}i.mce-i-alignleft:before{content:'\f206'}i.mce-i-aligncenter:before{content:'\f207'}i.mce-i-alignright:before{content:'\f208'}i.mce-i-link:before{content:'\f103'}i.mce-i-unlink:before{content:'\f225'}i.mce-i-wp_more:before{content:'\f209'}i.mce-i-strikethrough:before{content:'\f224'}i.mce-i-spellchecker:before{content:'\f210'}.qt-dfw:before,i.mce-i-dfw:before,i.mce-i-fullscreen:before,i.mce-i-wp_fullscreen:before{content:'\f211'}i.mce-i-wp_adv:before{content:'\f212'}i.mce-i-underline:before{content:'\f213'}i.mce-i-alignjustify:before{content:'\f214'}i.mce-i-backcolor:before,i.mce-i-forecolor:before{content:'\f215'}i.mce-i-pastetext:before{content:'\f217'}i.mce-i-removeformat:before{content:'\f218'}i.mce-i-charmap:before{content:'\f220'}i.mce-i-outdent:before{content:'\f221'}i.mce-i-indent:before{content:'\f222'}i.mce-i-undo:before{content:'\f171'}i.mce-i-redo:before{content:'\f172'}i.mce-i-help:before,i.mce-i-wp_help:before{content:'\f223'}i.mce-i-wp-media-library:before{content:'\f104'}i.mce-i-ltr:before{content:'\f320'}i.mce-i-wp_page:before{content:'\f105'}i.mce-i-hr:before{content:'\f460'}.mce-close:before{content:'\f158'}.mce-i-wp_code:before{content:'\f475'}.wp-editor-wrap{position:relative}.wp-editor-tools{position:relative;z-index:1}.wp-editor-tools:after{clear:both;content:'';display:table}.wp-editor-container{clear:both}.wp-editor-area{font-family:Consolas,Monaco,monospace;font-size:13px;padding:10px;margin:1px 0 0;line-height:150%;border:0;outline:0;display:block;resize:vertical;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.rtl .wp-editor-area{font-family:Tahoma,Monaco,monospace}.locale-he-il .wp-editor-area{font-family:Arial,Monaco,monospace}.wp-editor-container textarea.wp-editor-area{width:100%;margin:0;-webkit-box-shadow:none;box-shadow:none}.wp-editor-tabs{float:right}.wp-switch-editor{float:left;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;position:relative;top:1px;background:#ebebeb;color:#777;cursor:pointer;font:13px/19px "Open Sans",sans-serif;height:20px;margin:5px 0 0 5px;padding:3px 8px 4px;border:1px solid #e5e5e5}.wp-switch-editor:focus{-webkit-box-shadow:0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8);box-shadow:0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8);outline:0;color:#222}.html-active .switch-html:focus,.tmce-active .switch-tmce:focus,.wp-switch-editor:active{-webkit-box-shadow:none;box-shadow:none}.wp-switch-editor:active{background-color:#f5f5f5;-webkit-box-shadow:none;box-shadow:none}.js .tmce-active .wp-editor-area{color:#fff}.tmce-active .quicktags-toolbar{display:none}.html-active .switch-html,.tmce-active .switch-tmce{background:#f5f5f5;color:#555;border-bottom-color:#f5f5f5}.wp-media-buttons{float:left}.wp-media-buttons .button{margin-right:5px;margin-bottom:4px;padding-left:7px;padding-right:7px}.wp-media-buttons .button:active{position:relative;top:1px;margin-top:-1px;margin-bottom:1px}.wp-media-buttons .insert-media{padding-left:5px}.wp-media-buttons a{text-decoration:none;color:#464646;font-size:12px}.wp-media-buttons img{padding:0 4px;vertical-align:middle}.wp-media-buttons span.wp-media-buttons-icon{display:inline-block;width:18px;height:18px;vertical-align:text-top;margin:0 2px}.wp-media-buttons .add_media span.wp-media-buttons-icon{background:0 0}.wp-media-buttons .add_media span.wp-media-buttons-icon:before{font:400 18px/1 dashicons;speak:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f104'}.quicktags-toolbar{padding:3px;position:relative;border-bottom:1px solid #dedede;background:#f5f5f5}.has-dfw .quicktags-toolbar{padding-right:35px}.wp-core-ui .quicktags-toolbar input.button.button-small{margin:2px}.quicktags-toolbar input[value=link]{text-decoration:underline}.quicktags-toolbar input[value=del]{text-decoration:line-through}.quicktags-toolbar input[value="i"]{font-style:italic}.quicktags-toolbar input[value="b"]{font-weight:700}.mce-toolbar .mce-btn-group .mce-btn.mce-wp-dfw,.qt-dfw{position:absolute;top:0;right:0;margin:5px 5px 0 0}.qt-fullscreen{position:static;margin:2px}@media screen and (max-width:782px){.mce-toolbar .mce-btn button,.qt-dfw{padding:6px 7px}#wp-fullscreen-buttons .mce-btn,.mce-toolbar .mce-btn-group .mce-btn{margin:1px}.qt-dfw{width:36px;height:34px}.mce-toolbar .mce-btn-group .mce-btn.mce-wp-dfw{margin:4px 4px 0 0}.mce-toolbar .mce-colorbutton .mce-preview{left:8px;bottom:6px}.mce-window .mce-btn{padding:2px 0}.has-dfw .quicktags-toolbar,.has-dfw div.mce-toolbar-grp .mce-toolbar.mce-first{padding-right:40px}}@media screen and (min-width:782px){.wp-core-ui .quicktags-toolbar input.button.button-small{font-size:12px;height:26px;line-height:24px}}#wp_editbtns,#wp_gallerybtns{padding:2px;position:absolute;display:none;z-index:100020}#wp_delgallery,#wp_delimgbtn,#wp_editgallery,#wp_editimgbtn{border-color:#999;background-color:#eee;margin:2px;padding:2px;border-width:1px;border-style:solid;-webkit-border-radius:3px;border-radius:3px}#wp_delgallery:hover,#wp_delimgbtn:hover,#wp_editgallery:hover,#wp_editimgbtn:hover{border-color:#555;background-color:#ccc}#wp-link-wrap{display:none;background-color:#fff;-webkit-box-shadow:0 3px 6px rgba(0,0,0,.3);box-shadow:0 3px 6px rgba(0,0,0,.3);width:500px;height:250px;overflow:hidden;margin-left:-250px;margin-top:-125px;position:fixed;top:50%;left:50%;z-index:100105;-webkit-transition:height .2s,margin-top .2s;transition:height .2s,margin-top .2s}#wp-link-backdrop{display:none;position:fixed;top:0;left:0;right:0;bottom:0;min-height:360px;background:#000;opacity:.7;filter:alpha(opacity=70);z-index:100100}#wp-link{position:relative;height:100%}#wp-link-wrap.search-panel-visible{height:500px;margin-top:-250px}#link-modal-title{background:#fcfcfc;border-bottom:1px solid #dfdfdf;height:36px;font-size:18px;font-weight:600;line-height:36px;padding:0 36px 0 16px;top:0;right:0;left:0}#wp-link-close{color:#666;padding:0;position:absolute;top:0;right:0;width:36px;height:36px;text-align:center;background:0 0;border:none;cursor:pointer}#wp-link-close:before{font:400 20px/36px dashicons;vertical-align:top;speak:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;width:36px;height:36px;content:'\f158'}#wp-link-close:focus,#wp-link-close:hover{color:#2ea2cc}#wp-link-close:focus{outline:0;-webkit-box-shadow:0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8);box-shadow:0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8)}#link-selector{padding:0 16px 50px}#wp-link-wrap.search-panel-visible #link-selector{padding:0 16px;position:absolute;top:36px;left:0;right:0;bottom:44px}#wp-link ol,#wp-link ul{list-style:none;margin:0;padding:0}#wp-link-search-toggle:after{display:inline-block;font:400 20px/1 dashicons;vertical-align:top;speak:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f140'}.search-panel-visible #wp-link-search-toggle:after{content:'\f142'}#wp-link input[type=text]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}#wp-link #link-options{padding:8px 0 12px}#wp-link p.howto{margin:3px 0}#wp-link p.howto a{text-decoration:none;color:inherit}#wp-link-search-toggle{cursor:pointer}#wp-link label input[type=text]{margin-top:5px;width:70%}#wp-link #link-options label span,#wp-link #search-panel label span.search-label{display:inline-block;width:80px;text-align:right;padding-right:5px;max-width:24%}#wp-link .link-search-field{float:left;width:250px;max-width:70%}#wp-link .link-search-wrapper{margin:5px 0 9px;display:block;overflow:hidden}#wp-link .link-search-wrapper span{float:left;margin-top:4px}#wp-link .link-search-wrapper .spinner{display:none;vertical-align:text-bottom}#wp-link .link-target{padding:3px 0 0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}#wp-link .link-target label{max-width:70%}#wp-link .query-results{border:1px solid #dfdfdf;margin:0;background:#fff;overflow:auto;position:absolute;left:16px;right:16px;bottom:16px;top:205px}#wp-link li{clear:both;margin-bottom:0;border-bottom:1px solid #f1f1f1;color:#333;padding:4px 6px 4px 10px;cursor:pointer;position:relative}#wp-link .query-notice{padding:0;border-bottom:1px solid #dfdfdf;background-color:#f7fcfe;color:#000}#wp-link .query-notice .query-notice-default,#wp-link .query-notice .query-notice-hint{display:block;padding:6px;border-left:4px solid #2ea2cc}#wp-link .unselectable.no-matches-found{padding:0;border-bottom:1px solid #dfdfdf;background-color:#fef7f1}#wp-link .no-matches-found .item-title{display:block;padding:6px;border-left:4px solid #d54e21}#wp-link .query-results em{font-style:normal}#wp-link li:hover{background:#eaf2fa;color:#151515}#wp-link li.unselectable{border-bottom:1px solid #dfdfdf}#wp-link li.unselectable:hover{background:#fff;cursor:auto;color:#333}#wp-link li.selected{background:#ddd;color:#333}#wp-link li.selected .item-title{font-weight:700}#wp-link li:last-child{border:none}#wp-link .item-title{display:inline-block;width:80%;width:-webkit-calc(100% - 68px);width:calc(100% - 68px)}#wp-link .item-info{text-transform:uppercase;color:#666;font-size:11px;position:absolute;right:5px;top:5px}#wp-link #search-panel,#wp-link #search-results{display:none}#wp-link-wrap.search-panel-visible #search-panel{display:block}#wp-link .river-waiting{display:none;padding:10px 0}#wp-link .river-waiting .spinner{margin:0 auto;display:block;float:none}#wp-link .submitbox{padding:8px 16px;background:#fcfcfc;border-top:1px solid #dfdfdf;position:absolute;bottom:0;left:0;right:0}#wp-link-cancel{line-height:25px;float:left}#wp-link-update{line-height:23px;float:right}#wp-link-submit{float:right;margin-bottom:0}@media screen and (max-width:782px){#wp-link-wrap{height:280px;margin-top:-140px}#wp-link-wrap.search-panel-visible .query-results{top:235px}#link-selector{padding:0 16px 60px}#wp-link-wrap.search-panel-visible #link-selector{bottom:52px}#wp-link-cancel{line-height:32px}}@media screen and (max-width:520px){#wp-link-wrap{width:auto;margin-left:0;left:10px;right:10px;max-width:500px}}@media screen and (max-height:520px){#wp-link-wrap{-webkit-transition:none;transition:none}#wp-link-wrap.search-panel-visible{height:auto;margin-top:0;top:10px;bottom:10px}.search-panel-visible #link-selector{overflow:auto}.search-panel-visible #search-panel .query-results{position:static}}@media screen and (max-height:290px){#wp-link-wrap{height:auto;margin-top:0;top:10px;bottom:10px}#link-selector{overflow:auto;height:-webkit-calc(100% - 92px);height:calc(100% - 92px);padding-bottom:2px}#search-panel .query-results{position:static}}.fullscreen-overlay{z-index:100005;display:none;position:fixed;top:0;bottom:0;left:0;right:0;-webkit-filter:inherit;filter:inherit}.wp-fullscreen-active #wp-fullscreen-body,.wp-fullscreen-active .fullscreen-overlay{display:block}.fullscreen-fader{z-index:200000}.wp-core-ui.wp-fullscreen-active .postbox-container,.wp-fullscreen-active .fullscreen-fader{display:none}#wp-fullscreen-body,.mce-fullscreen{z-index:100010}#wp-fullscreen-body{display:none}.wp-fullscreen-wrap{margin:0;padding:0;position:absolute;left:0;right:0;bottom:30px;top:60px;z-index:100015}#wp-fullscreen-central-toolbar,.wp-fullscreen-title,.wp-fullscreen-wrap .wp-editor-container{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;max-width:100%}.wp-fullscreen-active .mce-statusbar,.wp-fullscreen-active .mce-toolbar-grp,.wp-fullscreen-active .quicktags-toolbar,.wp-fullscreen-active .wp-editor-tools{display:none}#wp-fullscreen-statusbar{position:fixed;left:0;right:0;bottom:0;height:30px;z-index:100020;background:#fff;-webkit-transition:height .2s;transition:height .2s}#wp-fullscreen-status{margin:0 auto;padding:0}.wp-fullscreen-active .wp-editor-container,.wp-fullscreen-active .wp-fullscreen-title,.wp-fullscreen-active .wp-fullscreen-title:focus{-webkit-border-radius:0;border-radius:0;border:1px dashed transparent;background:0 0;-webkit-box-shadow:none;box-shadow:none;-webkit-transition:border-color .4s;transition:border-color .4s}.wp-fullscreen-active .wp-editor-container{margin:0 auto 40px}.wp-fullscreen-active .wp-fullscreen-title{font-size:1.7em;line-height:100%;outline:0;padding:3px 7px;margin:10px auto 30px;display:block}#wp-fullscreen-tagline{color:#888;font-size:18px;float:right;padding:4px 0 0}#fullscreen-topbar{background:#f5f5f5;border-bottom:1px solid #dedede;height:45px;position:fixed;left:0;right:0;top:0;width:100%;z-index:100020;-webkit-transition:opacity .4s;transition:opacity .4s}#wp-fullscreen-toolbar{padding:6px 10px 0;clear:both;max-width:1100px;margin:0 auto}#wp-fullscreen-button-bar,#wp-fullscreen-close,#wp-fullscreen-mode-bar{float:left}#wp-fullscreen-count,#wp-fullscreen-tagline{display:inline-block}#wp-fullscreen-button-bar{margin-top:2px}#wp-fullscreen-save{float:right;padding:2px 0 0;min-width:95px}#wp-fullscreen-close,#wp-fullscreen-count{padding:5px 0 0}#wp-fullscreen-central-toolbar{margin:auto;padding:0;min-width:620px}#wp-fullscreen-buttons>div{float:left}#wp-fullscreen-mode-bar{padding:3px 14px 0 0}#wp-fullscreen-buttons .hidden{display:none}#wp-fullscreen-buttons .disabled{opacity:.5}#wp-fullscreen-buttons .mce-btn button{margin:0;outline:0;border:0;white-space:nowrap;width:auto;background:0 0;color:#333;cursor:pointer;font-size:18px;line-height:20px;overflow:visible;text-align:center;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.wp-html-mode #wp-fullscreen-buttons div{display:none}.wp-html-mode #wp-fullscreen-buttons div.wp-fullscreen-both{display:block}#wp-fullscreen-save img{vertical-align:middle}#wp-fullscreen-save span{display:none;margin:5px 6px 0;float:left}.wp-fullscreen-active #TB_overlay{z-index:100050}.wp-fullscreen-active #TB_window{z-index:100051}.fullscreen-overlay{background:#fff}.wp-fullscreen-active #fullscreen-topbar{-webkit-transition-duration:.8s;transition-duration:.8s;opacity:0;filter:alpha(opacity=0)}.wp-fullscreen-active #wp-fullscreen-statusbar{height:0}.wp-fullscreen-active.wp-dfw-show-ui #fullscreen-topbar{-webkit-transition-duration:.4s;transition-duration:.4s;opacity:1;filter:alpha(opacity=100)}.wp-fullscreen-active.wp-dfw-show-ui #wp-fullscreen-statusbar{height:29px;background:#f8f8f8;border-top:1px solid #eee}.wp-fullscreen-active .wp-editor-container,.wp-fullscreen-active .wp-fullscreen-title{-webkit-transition-duration:.8s;transition-duration:.8s;border-color:transparent}.wp-fullscreen-active.wp-dfw-show-ui .wp-editor-container,.wp-fullscreen-active.wp-dfw-show-ui .wp-fullscreen-title{-webkit-transition-duration:.4s;transition-duration:.4s;border-color:#ccc}.fade-1000,.fade-300,.fade-400,.fade-600{opacity:0;-webkit-transition-property:opacity;transition-property:opacity}.fade-1000{-webkit-transition-duration:1s;transition-duration:1s}.fade-600{-webkit-transition-duration:.6s;transition-duration:.6s}.fade-400{-webkit-transition-duration:.4s;transition-duration:.4s}.fade-300{-webkit-transition-duration:.3s;transition-duration:.3s}.fade-trigger{opacity:1}.wp-dfw-touch #fullscreen-topbar{position:absolute;opacity:1}.wp-dfw-touch .wp-fullscreen-title,.wp-dfw-touch .wp-fullscreen-wrap .wp-editor-container{max-width:700px}.wp-fullscreen-active.wp-dfw-touch .wp-editor-container,.wp-fullscreen-active.wp-dfw-touch .wp-fullscreen-title{border-color:#ccc}.wp-dfw-touch #wp-fullscreen-statusbar{height:30px}@media screen and (max-width:782px){#wp-fullscreen-button-bar,#wp-fullscreen-central-toolbar,#wp-fullscreen-close,#wp-fullscreen-mode-bar,#wp-fullscreen-save{display:inline-block}#fullscreen-topbar{height:85px}#wp-fullscreen-central-toolbar{width:auto!important;min-width:0}#wp-fullscreen-close{line-height:30px;vertical-align:top;padding:0 12px}#wp-fullscreen-button-bar{position:absolute;top:45px;left:0}.wp-fullscreen-wrap{top:95px}#wp-fullscreen-save{position:absolute;right:10px}}@media screen and (max-width:480px){#wp_fs_help{display:none}.wp-fullscreen-title,.wp-fullscreen-wrap .wp-editor-container{width:480px!important}body.wp-fullscreen-active{width:480px;overflow:auto}#fullscreen-topbar,.wp-fullscreen-wrap{width:480px}#fullscreen-topbar{position:absolute}#wp-fullscreen-status{width:auto!important;max-width:100%;padding:0 10px}}.rtl .quicktags-toolbar input,.rtl .wp-switch-editor{font-family:Tahoma,sans-serif}.mce-rtl .mce-flow-layout .mce-flow-layout-item>div{direction:rtl}.mce-rtl .mce-listbox i.mce-caret{left:6px}html:lang(he-il) .rtl .quicktags-toolbar input,html:lang(he-il) .rtl .wp-switch-editor{font-family:Arial,sans-serif}@media print,(-o-min-device-pixel-ratio:5/4),(-webkit-min-device-pixel-ratio:1.25),(min-resolution:120dpi){#wp-fullscreen-buttons #wp_fs_image span.mce_image,.wp-media-buttons .add_media span.wp-media-buttons-icon{background:0 0}} \ No newline at end of file diff --git a/wp-includes/css/jquery-ui-dialog-rtl.css b/wp-includes/css/jquery-ui-dialog-rtl.css new file mode 100644 index 0000000..885f001 --- /dev/null +++ b/wp-includes/css/jquery-ui-dialog-rtl.css @@ -0,0 +1,362 @@ +/*! + * jQuery UI CSS Framework 1.11.2 + * http://jqueryui.com + * + * Copyright 2014 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/category/theming/ + */ + +/* Layout helpers +----------------------------------*/ +.ui-helper-hidden { + display: none; +} +.ui-helper-hidden-accessible { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} +.ui-helper-reset { + margin: 0; + padding: 0; + border: 0; + outline: 0; + line-height: 1.3; + text-decoration: none; + font-size: 100%; + list-style: none; +} +.ui-helper-clearfix:before, +.ui-helper-clearfix:after { + content: ""; + display: table; + border-collapse: collapse; +} +.ui-helper-clearfix:after { + clear: both; +} +.ui-helper-clearfix { + min-height: 0; /* support: IE7 */ +} +.ui-helper-zfix { + width: 100%; + height: 100%; + top: 0; + right: 0; + position: absolute; + opacity: 0; + filter:Alpha(Opacity=0); /* support: IE8 */ +} + +.ui-front { + z-index: 100; +} + + +/* Interaction Cues +----------------------------------*/ +.ui-state-disabled { + cursor: default !important; +} + + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { + display: block; + text-indent: -99999px; + overflow: hidden; + background-repeat: no-repeat; +} + + +/* Misc visuals +----------------------------------*/ + +/* Overlays */ +.ui-widget-overlay { + position: fixed; + top: 0; + right: 0; + width: 100%; + height: 100%; +} + +/*! + * jQuery UI Resizable 1.11.2 + * http://jqueryui.com + * + * Copyright 2014 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ +.ui-resizable { + position: relative; +} +.ui-resizable-handle { + position: absolute; + font-size: 0.1px; + display: block; + -ms-touch-action: none; + touch-action: none; +} +.ui-resizable-disabled .ui-resizable-handle, +.ui-resizable-autohide .ui-resizable-handle { + display: none; +} +.ui-resizable-n { + cursor: n-resize; + height: 7px; + width: 100%; + top: -5px; + right: 0; +} +.ui-resizable-s { + cursor: s-resize; + height: 7px; + width: 100%; + bottom: -5px; + right: 0; +} +/* @noflip */ +.ui-resizable-e { + cursor: e-resize; + width: 7px; + right: -5px; + top: 0; + height: 100%; +} +/* @noflip */ +.ui-resizable-w { + cursor: w-resize; + width: 7px; + left: -5px; + top: 0; + height: 100%; +} +/* @noflip */ +.ui-resizable-se { + cursor: se-resize; + width: 12px; + height: 12px; + right: 1px; + bottom: 1px; +} +/* @noflip */ +.ui-resizable-sw { + cursor: sw-resize; + width: 9px; + height: 9px; + left: -5px; + bottom: -5px; +} +/* @noflip */ +.ui-resizable-nw { + cursor: nw-resize; + width: 9px; + height: 9px; + left: -5px; + top: -5px; +} +/* @noflip */ +.ui-resizable-ne { + cursor: ne-resize; + width: 9px; + height: 9px; + right: -5px; + top: -5px; +} + +/* WP buttons: see buttons.css. */ + +.ui-button { + display: inline-block; + text-decoration: none; + font-size: 13px; + line-height: 26px; + height: 28px; + margin: 0; + padding: 0 10px 1px; + cursor: pointer; + border-width: 1px; + border-style: solid; + -webkit-appearance: none; + -webkit-border-radius: 3px; + border-radius: 3px; + white-space: nowrap; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + color: #555; + border-color: #cccccc; + background: #f7f7f7; + -webkit-box-shadow: inset 0 1px 0 #fff, 0 1px 0 rgba( 0, 0, 0, 0.08 ); + box-shadow: inset 0 1px 0 #fff, 0 1px 0 rgba( 0, 0, 0, 0.08 ); + vertical-align: top; +} + +.ui-button:active, +.ui-button:focus { + outline: none; +} + +/* Remove the dotted border on :focus and the extra padding in Firefox */ +.ui-button::-moz-focus-inner { + border-width: 1px 0; + border-style: solid none; + border-color: transparent; + padding: 0; +} + +.ui-button:hover, +.ui-button:focus { + background: #fafafa; + border-color: #999; + color: #222; +} + +.ui-button:focus { + -webkit-box-shadow: + 0 0 0 1px #5b9dd9, + 0 0 2px 1px rgba( 30, 140, 190, 0.8 ); + box-shadow: + 0 0 0 1px #5b9dd9, + 0 0 2px 1px rgba( 30, 140, 190, 0.8 ); +} + +.ui-button:active { + background: #eee; + border-color: #999; + color: #333; + -webkit-box-shadow: inset 0 2px 5px -3px rgba( 0, 0, 0, 0.5 ); + box-shadow: inset 0 2px 5px -3px rgba( 0, 0, 0, 0.5 ); +} + +.ui-button[disabled], +.ui-button:disabled { + color: #aaa !important; + border-color: #ddd !important; + background: #f7f7f7 !important; + -webkit-box-shadow: none !important; + box-shadow: none !important; + text-shadow: 0 1px 0 #fff !important; + cursor: default; +} + +@media screen and ( max-width: 782px ) { + + .ui-button { + padding: 10px 14px; + line-height: 1; + font-size: 14px; + vertical-align: middle; + height: auto; + margin-bottom: 4px; + } + +} + +/* WP Theme */ + +.ui-dialog { + position: absolute; + top: 0; + right: 0; + z-index: 100102; + background-color: #fff; + -webkit-box-shadow: 0 3px 6px rgba( 0, 0, 0, 0.3 ); + box-shadow: 0 3px 6px rgba( 0, 0, 0, 0.3 ); +} + +.ui-dialog-titlebar { + background: #fcfcfc; + border-bottom: 1px solid #dfdfdf; + height: 36px; + font-size: 18px; + font-weight: 600; + line-height: 36px; + padding: 0 16px 0 36px; +} + +.ui-button.ui-dialog-titlebar-close { + background: none; + border: none; + -webkit-box-shadow: none; + box-shadow: none; + color: #666; + cursor: pointer; + display: block; + padding: 0; + position: absolute; + top: 0; + left: 0; + width: 36px; + height: 36px; + text-align: center; +} + +.ui-dialog-titlebar-close:before { + font: normal 20px/1 'dashicons'; + vertical-align: top; + speak: none; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + line-height: 36px; + width: 36px; + height: 36px; + content: '\f158'; +} + +.ui-button.ui-dialog-titlebar-close:hover { + color: #2ea2cc; +} + +.ui-dialog-titlebar-close .ui-button-text { + display: none; +} + +.ui-dialog-content { + padding: 16px; + overflow: auto; +} + +.ui-dialog-buttonpane { + background: #fcfcfc; + border-top: 1px solid #dfdfdf; + padding: 16px; +} + +.ui-dialog-buttonpane .ui-button { + margin-right: 16px; +} + +.ui-dialog-buttonpane .ui-dialog-buttonset { + float: left; +} + +.ui-draggable .ui-dialog-titlebar { + cursor: move; +} + +.ui-widget-overlay { + position: fixed; + top: 0; + right: 0; + left: 0; + bottom: 0; + min-height: 360px; + background: #000; + opacity: 0.7; + filter: alpha(opacity=70); + z-index: 100101; +} diff --git a/wp-includes/css/jquery-ui-dialog-rtl.min.css b/wp-includes/css/jquery-ui-dialog-rtl.min.css new file mode 100644 index 0000000..5ac7519 --- /dev/null +++ b/wp-includes/css/jquery-ui-dialog-rtl.min.css @@ -0,0 +1,17 @@ +/*! + * jQuery UI CSS Framework 1.11.2 + * http://jqueryui.com + * + * Copyright 2014 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/category/theming/ + */.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:after,.ui-helper-clearfix:before{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{min-height:0}.ui-helper-zfix{width:100%;height:100%;top:0;right:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{width:100%;height:100%}/*! + * jQuery UI Resizable 1.11.2 + * http://jqueryui.com + * + * Copyright 2014 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:.1px;display:block;-ms-touch-action:none;touch-action:none}.ui-resizable-autohide .ui-resizable-handle,.ui-resizable-disabled .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;right:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;right:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}.ui-button{display:inline-block;text-decoration:none;font-size:13px;line-height:26px;height:28px;margin:0;padding:0 10px 1px;cursor:pointer;border-width:1px;border-style:solid;-webkit-appearance:none;-webkit-border-radius:3px;border-radius:3px;white-space:nowrap;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;color:#555;border-color:#ccc;background:#f7f7f7;-webkit-box-shadow:inset 0 1px 0 #fff,0 1px 0 rgba(0,0,0,.08);box-shadow:inset 0 1px 0 #fff,0 1px 0 rgba(0,0,0,.08);vertical-align:top}.ui-button:active,.ui-button:focus{outline:0}.ui-button::-moz-focus-inner{border-width:1px 0;border-style:solid none;border-color:transparent;padding:0}.ui-button:focus,.ui-button:hover{background:#fafafa;border-color:#999;color:#222}.ui-button:focus{-webkit-box-shadow:0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8);box-shadow:0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8)}.ui-button:active{background:#eee;border-color:#999;color:#333;-webkit-box-shadow:inset 0 2px 5px -3px rgba(0,0,0,.5);box-shadow:inset 0 2px 5px -3px rgba(0,0,0,.5)}.ui-button:disabled,.ui-button[disabled]{color:#aaa!important;border-color:#ddd!important;background:#f7f7f7!important;-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:0 1px 0 #fff!important;cursor:default}@media screen and (max-width:782px){.ui-button{padding:10px 14px;line-height:1;font-size:14px;vertical-align:middle;height:auto;margin-bottom:4px}}.ui-dialog{position:absolute;top:0;right:0;z-index:100102;background-color:#fff;-webkit-box-shadow:0 3px 6px rgba(0,0,0,.3);box-shadow:0 3px 6px rgba(0,0,0,.3)}.ui-dialog-titlebar{background:#fcfcfc;border-bottom:1px solid #dfdfdf;height:36px;font-size:18px;font-weight:600;line-height:36px;padding:0 16px 0 36px}.ui-button.ui-dialog-titlebar-close{background:0 0;border:none;-webkit-box-shadow:none;box-shadow:none;color:#666;cursor:pointer;display:block;padding:0;position:absolute;top:0;left:0;width:36px;height:36px;text-align:center}.ui-dialog-titlebar-close:before{font:400 20px/1 dashicons;vertical-align:top;speak:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;line-height:36px;width:36px;height:36px;content:'\f158'}.ui-button.ui-dialog-titlebar-close:hover{color:#2ea2cc}.ui-dialog-titlebar-close .ui-button-text{display:none}.ui-dialog-content{padding:16px;overflow:auto}.ui-dialog-buttonpane{background:#fcfcfc;border-top:1px solid #dfdfdf;padding:16px}.ui-dialog-buttonpane .ui-button{margin-right:16px}.ui-dialog-buttonpane .ui-dialog-buttonset{float:left}.ui-draggable .ui-dialog-titlebar{cursor:move}.ui-widget-overlay{position:fixed;top:0;right:0;left:0;bottom:0;min-height:360px;background:#000;opacity:.7;filter:alpha(opacity=70);z-index:100101} \ No newline at end of file diff --git a/wp-includes/css/jquery-ui-dialog.css b/wp-includes/css/jquery-ui-dialog.css new file mode 100644 index 0000000..5fdfee4 --- /dev/null +++ b/wp-includes/css/jquery-ui-dialog.css @@ -0,0 +1,362 @@ +/*! + * jQuery UI CSS Framework 1.11.2 + * http://jqueryui.com + * + * Copyright 2014 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/category/theming/ + */ + +/* Layout helpers +----------------------------------*/ +.ui-helper-hidden { + display: none; +} +.ui-helper-hidden-accessible { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} +.ui-helper-reset { + margin: 0; + padding: 0; + border: 0; + outline: 0; + line-height: 1.3; + text-decoration: none; + font-size: 100%; + list-style: none; +} +.ui-helper-clearfix:before, +.ui-helper-clearfix:after { + content: ""; + display: table; + border-collapse: collapse; +} +.ui-helper-clearfix:after { + clear: both; +} +.ui-helper-clearfix { + min-height: 0; /* support: IE7 */ +} +.ui-helper-zfix { + width: 100%; + height: 100%; + top: 0; + left: 0; + position: absolute; + opacity: 0; + filter:Alpha(Opacity=0); /* support: IE8 */ +} + +.ui-front { + z-index: 100; +} + + +/* Interaction Cues +----------------------------------*/ +.ui-state-disabled { + cursor: default !important; +} + + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { + display: block; + text-indent: -99999px; + overflow: hidden; + background-repeat: no-repeat; +} + + +/* Misc visuals +----------------------------------*/ + +/* Overlays */ +.ui-widget-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; +} + +/*! + * jQuery UI Resizable 1.11.2 + * http://jqueryui.com + * + * Copyright 2014 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ +.ui-resizable { + position: relative; +} +.ui-resizable-handle { + position: absolute; + font-size: 0.1px; + display: block; + -ms-touch-action: none; + touch-action: none; +} +.ui-resizable-disabled .ui-resizable-handle, +.ui-resizable-autohide .ui-resizable-handle { + display: none; +} +.ui-resizable-n { + cursor: n-resize; + height: 7px; + width: 100%; + top: -5px; + left: 0; +} +.ui-resizable-s { + cursor: s-resize; + height: 7px; + width: 100%; + bottom: -5px; + left: 0; +} +/* @noflip */ +.ui-resizable-e { + cursor: e-resize; + width: 7px; + right: -5px; + top: 0; + height: 100%; +} +/* @noflip */ +.ui-resizable-w { + cursor: w-resize; + width: 7px; + left: -5px; + top: 0; + height: 100%; +} +/* @noflip */ +.ui-resizable-se { + cursor: se-resize; + width: 12px; + height: 12px; + right: 1px; + bottom: 1px; +} +/* @noflip */ +.ui-resizable-sw { + cursor: sw-resize; + width: 9px; + height: 9px; + left: -5px; + bottom: -5px; +} +/* @noflip */ +.ui-resizable-nw { + cursor: nw-resize; + width: 9px; + height: 9px; + left: -5px; + top: -5px; +} +/* @noflip */ +.ui-resizable-ne { + cursor: ne-resize; + width: 9px; + height: 9px; + right: -5px; + top: -5px; +} + +/* WP buttons: see buttons.css. */ + +.ui-button { + display: inline-block; + text-decoration: none; + font-size: 13px; + line-height: 26px; + height: 28px; + margin: 0; + padding: 0 10px 1px; + cursor: pointer; + border-width: 1px; + border-style: solid; + -webkit-appearance: none; + -webkit-border-radius: 3px; + border-radius: 3px; + white-space: nowrap; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + color: #555; + border-color: #cccccc; + background: #f7f7f7; + -webkit-box-shadow: inset 0 1px 0 #fff, 0 1px 0 rgba( 0, 0, 0, 0.08 ); + box-shadow: inset 0 1px 0 #fff, 0 1px 0 rgba( 0, 0, 0, 0.08 ); + vertical-align: top; +} + +.ui-button:active, +.ui-button:focus { + outline: none; +} + +/* Remove the dotted border on :focus and the extra padding in Firefox */ +.ui-button::-moz-focus-inner { + border-width: 1px 0; + border-style: solid none; + border-color: transparent; + padding: 0; +} + +.ui-button:hover, +.ui-button:focus { + background: #fafafa; + border-color: #999; + color: #222; +} + +.ui-button:focus { + -webkit-box-shadow: + 0 0 0 1px #5b9dd9, + 0 0 2px 1px rgba( 30, 140, 190, 0.8 ); + box-shadow: + 0 0 0 1px #5b9dd9, + 0 0 2px 1px rgba( 30, 140, 190, 0.8 ); +} + +.ui-button:active { + background: #eee; + border-color: #999; + color: #333; + -webkit-box-shadow: inset 0 2px 5px -3px rgba( 0, 0, 0, 0.5 ); + box-shadow: inset 0 2px 5px -3px rgba( 0, 0, 0, 0.5 ); +} + +.ui-button[disabled], +.ui-button:disabled { + color: #aaa !important; + border-color: #ddd !important; + background: #f7f7f7 !important; + -webkit-box-shadow: none !important; + box-shadow: none !important; + text-shadow: 0 1px 0 #fff !important; + cursor: default; +} + +@media screen and ( max-width: 782px ) { + + .ui-button { + padding: 10px 14px; + line-height: 1; + font-size: 14px; + vertical-align: middle; + height: auto; + margin-bottom: 4px; + } + +} + +/* WP Theme */ + +.ui-dialog { + position: absolute; + top: 0; + left: 0; + z-index: 100102; + background-color: #fff; + -webkit-box-shadow: 0 3px 6px rgba( 0, 0, 0, 0.3 ); + box-shadow: 0 3px 6px rgba( 0, 0, 0, 0.3 ); +} + +.ui-dialog-titlebar { + background: #fcfcfc; + border-bottom: 1px solid #dfdfdf; + height: 36px; + font-size: 18px; + font-weight: 600; + line-height: 36px; + padding: 0 36px 0 16px; +} + +.ui-button.ui-dialog-titlebar-close { + background: none; + border: none; + -webkit-box-shadow: none; + box-shadow: none; + color: #666; + cursor: pointer; + display: block; + padding: 0; + position: absolute; + top: 0; + right: 0; + width: 36px; + height: 36px; + text-align: center; +} + +.ui-dialog-titlebar-close:before { + font: normal 20px/1 'dashicons'; + vertical-align: top; + speak: none; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + line-height: 36px; + width: 36px; + height: 36px; + content: '\f158'; +} + +.ui-button.ui-dialog-titlebar-close:hover { + color: #2ea2cc; +} + +.ui-dialog-titlebar-close .ui-button-text { + display: none; +} + +.ui-dialog-content { + padding: 16px; + overflow: auto; +} + +.ui-dialog-buttonpane { + background: #fcfcfc; + border-top: 1px solid #dfdfdf; + padding: 16px; +} + +.ui-dialog-buttonpane .ui-button { + margin-left: 16px; +} + +.ui-dialog-buttonpane .ui-dialog-buttonset { + float: right; +} + +.ui-draggable .ui-dialog-titlebar { + cursor: move; +} + +.ui-widget-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + min-height: 360px; + background: #000; + opacity: 0.7; + filter: alpha(opacity=70); + z-index: 100101; +} diff --git a/wp-includes/css/jquery-ui-dialog.min.css b/wp-includes/css/jquery-ui-dialog.min.css new file mode 100644 index 0000000..b5a74d7 --- /dev/null +++ b/wp-includes/css/jquery-ui-dialog.min.css @@ -0,0 +1,17 @@ +/*! + * jQuery UI CSS Framework 1.11.2 + * http://jqueryui.com + * + * Copyright 2014 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/category/theming/ + */.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:after,.ui-helper-clearfix:before{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{min-height:0}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{width:100%;height:100%}/*! + * jQuery UI Resizable 1.11.2 + * http://jqueryui.com + * + * Copyright 2014 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:.1px;display:block;-ms-touch-action:none;touch-action:none}.ui-resizable-autohide .ui-resizable-handle,.ui-resizable-disabled .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}.ui-button{display:inline-block;text-decoration:none;font-size:13px;line-height:26px;height:28px;margin:0;padding:0 10px 1px;cursor:pointer;border-width:1px;border-style:solid;-webkit-appearance:none;-webkit-border-radius:3px;border-radius:3px;white-space:nowrap;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;color:#555;border-color:#ccc;background:#f7f7f7;-webkit-box-shadow:inset 0 1px 0 #fff,0 1px 0 rgba(0,0,0,.08);box-shadow:inset 0 1px 0 #fff,0 1px 0 rgba(0,0,0,.08);vertical-align:top}.ui-button:active,.ui-button:focus{outline:0}.ui-button::-moz-focus-inner{border-width:1px 0;border-style:solid none;border-color:transparent;padding:0}.ui-button:focus,.ui-button:hover{background:#fafafa;border-color:#999;color:#222}.ui-button:focus{-webkit-box-shadow:0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8);box-shadow:0 0 0 1px #5b9dd9,0 0 2px 1px rgba(30,140,190,.8)}.ui-button:active{background:#eee;border-color:#999;color:#333;-webkit-box-shadow:inset 0 2px 5px -3px rgba(0,0,0,.5);box-shadow:inset 0 2px 5px -3px rgba(0,0,0,.5)}.ui-button:disabled,.ui-button[disabled]{color:#aaa!important;border-color:#ddd!important;background:#f7f7f7!important;-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:0 1px 0 #fff!important;cursor:default}@media screen and (max-width:782px){.ui-button{padding:10px 14px;line-height:1;font-size:14px;vertical-align:middle;height:auto;margin-bottom:4px}}.ui-dialog{position:absolute;top:0;left:0;z-index:100102;background-color:#fff;-webkit-box-shadow:0 3px 6px rgba(0,0,0,.3);box-shadow:0 3px 6px rgba(0,0,0,.3)}.ui-dialog-titlebar{background:#fcfcfc;border-bottom:1px solid #dfdfdf;height:36px;font-size:18px;font-weight:600;line-height:36px;padding:0 36px 0 16px}.ui-button.ui-dialog-titlebar-close{background:0 0;border:none;-webkit-box-shadow:none;box-shadow:none;color:#666;cursor:pointer;display:block;padding:0;position:absolute;top:0;right:0;width:36px;height:36px;text-align:center}.ui-dialog-titlebar-close:before{font:400 20px/1 dashicons;vertical-align:top;speak:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;line-height:36px;width:36px;height:36px;content:'\f158'}.ui-button.ui-dialog-titlebar-close:hover{color:#2ea2cc}.ui-dialog-titlebar-close .ui-button-text{display:none}.ui-dialog-content{padding:16px;overflow:auto}.ui-dialog-buttonpane{background:#fcfcfc;border-top:1px solid #dfdfdf;padding:16px}.ui-dialog-buttonpane .ui-button{margin-left:16px}.ui-dialog-buttonpane .ui-dialog-buttonset{float:right}.ui-draggable .ui-dialog-titlebar{cursor:move}.ui-widget-overlay{position:fixed;top:0;left:0;right:0;bottom:0;min-height:360px;background:#000;opacity:.7;filter:alpha(opacity=70);z-index:100101} \ No newline at end of file diff --git a/wp-includes/css/media-views-rtl.css b/wp-includes/css/media-views-rtl.css new file mode 100644 index 0000000..2ad671a --- /dev/null +++ b/wp-includes/css/media-views-rtl.css @@ -0,0 +1,2555 @@ +/** + * Base Styles + */ +.media-modal * { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} + +.media-frame input, +.media-frame select, +.media-frame textarea { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.media-modal, +.media-frame { + font-family: "Open Sans", sans-serif; + font-size: 12px; +} + +.media-frame input, +.media-frame textarea { + padding: 6px 8px; +} + +.media-frame select, +.wp-admin .media-frame select { + line-height: 28px; + margin-top: 3px; +} + +.media-frame a { + border-bottom: none; + color: #21759b; +} + +.media-frame a:hover { + color: #d54e21; +} + +.media-frame a.button { + color: #333; +} + +.media-frame a.button:hover { + color: #222; +} + +.media-frame a.button-primary, +.media-frame a.button-primary:hover { + color: #fff; +} + +.media-frame input[type="text"], +.media-frame input[type="password"], +.media-frame input[type="number"], +.media-frame input[type="search"], +.media-frame input[type="email"], +.media-frame input[type="url"], +.media-frame textarea, +.media-frame select { + font-family: "Open Sans", sans-serif; + font-size: 12px; + border-width: 1px; + border-style: solid; + border-color: #dfdfdf; +} + +.media-frame input[type="text"]:focus, +.media-frame input[type="password"]:focus, +.media-frame input[type="number"]:focus, +.media-frame input[type="search"]:focus, +.media-frame input[type="email"]:focus, +.media-frame input[type="url"]:focus, +.media-frame textarea:focus, +.media-frame select:focus { + border-color: #5b9dd9; +} + +.media-frame select { + height: 24px; + padding: 2px; +} + +.media-frame input:disabled, +.media-frame textarea:disabled, +.media-frame input[readonly], +.media-frame textarea[readonly] { + background-color: #eee; +} + +.media-frame input[type="search"] { + -webkit-appearance: textfield; +} + +.media-frame :-moz-placeholder { + color: #a9a9a9; +} + +.media-frame .hidden { + display: none; +} + +/*! + * jQuery UI Draggable/Sortable 1.11.2 + * http://jqueryui.com + * + * Copyright 2014 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ +.ui-draggable-handle, +.ui-sortable-handle { + -ms-touch-action: none; + touch-action: none; +} + +/** + * Modal + */ +.media-modal { + position: fixed; + top: 30px; + right: 30px; + left: 30px; + bottom: 30px; + z-index: 160000; +} + +.wp-customizer .media-modal { + z-index: 560000; +} + +.media-modal-backdrop { + position: fixed; + top: 0; + right: 0; + left: 0; + bottom: 0; + min-height: 360px; + background: #000; + opacity: 0.7; + z-index: 159900; +} + +.wp-customizer .media-modal-backdrop { + z-index: 559900; +} + +.media-modal-close { + position: absolute; + text-decoration: none; + top: 10px; + left: 10px; + width: 30px; + height: 30px; + z-index: 1000; + -webkit-transition: color .1s ease-in-out, background .1s ease-in-out; + transition: color .1s ease-in-out, background .1s ease-in-out; +} + +.media-modal-close:active { + -webkit-box-shadow: none; + box-shadow: none; +} + +.media-modal-close span.media-modal-icon { + display: block; + margin-top: 5px; + width: 30px; + height: 15px; + background-image: none; + text-align: center; +} + +.media-modal-close .media-modal-icon:before { + content: '\f158'; + font: normal 20px/1 'dashicons'; + speak: none; + vertical-align: middle; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + color: #666; +} + +.media-modal-close:hover .media-modal-icon:before { + color: #2ea2cc; +} + +.media-modal-close:active { + outline: 0; +} + +.media-modal-content { + position: absolute; + top: 0; + right: 0; + left: 0; + bottom: 0; + overflow: auto; + min-height: 300px; + -webkit-box-shadow: 0 5px 15px rgba(0,0,0,0.7); + box-shadow: 0 5px 15px rgba(0,0,0,0.7); + background: #fcfcfc; + -webkit-font-smoothing: subpixel-antialiased; +} + +.media-modal-icon { + background-image: url(../images/uploader-icons.png); + background-repeat: no-repeat; +} + +/** + * Toolbar + */ +.media-toolbar { + position: absolute; + top: 0; + right: 0; + left: 0; + z-index: 100; + height: 60px; + padding: 0 16px; + border: 0 solid #dfdfdf; + overflow: hidden; +} + +.media-toolbar-primary { + float: left; + height: 100%; + max-width: 33%; +} + +.media-toolbar-secondary { + float: right; + height: 100%; + max-width: 66%; +} + +.media-toolbar-primary > .media-button, +.media-toolbar-primary > .media-button-group { + margin-right: 10px; + float: right; + margin-top: 15px; +} + +.media-toolbar-secondary > .media-button, +.media-toolbar-secondary > .media-button-group { + margin-left: 10px; + margin-top: 15px; +} + +/** + * Sidebar + */ +.media-sidebar { + position: absolute; + top: 0; + left: 0; + bottom: 0; + width: 267px; + padding: 0 16px 24px; + z-index: 75; + background: #f3f3f3; + border-right: 1px solid #ddd; + overflow: auto; + -webkit-overflow-scrolling: touch; +} + +.hide-toolbar .media-sidebar { + bottom: 0; +} + +.media-sidebar .sidebar-title { + font-size: 20px; + margin: 0; + padding: 12px 10px 10px; + line-height: 28px; +} + +.media-sidebar .sidebar-content { + padding: 0 10px; + margin-bottom: 130px; +} + +.media-sidebar .search { + display: block; + width: 100%; +} + +.media-sidebar h3, +.image-details h3 { + position: relative; + font-weight: bold; + text-transform: uppercase; + font-size: 12px; + color: #666; + margin: 24px 0 8px; +} + +.media-sidebar .setting, +.attachment-details .setting { + display: block; + float: right; + width: 100%; + margin: 1px 0; +} + +.media-sidebar .setting label, +.attachment-details .setting label { + display: block; +} + +.media-sidebar .setting .link-to-custom, +.attachment-details .setting .link-to-custom { + margin: 3px 2px 0; +} + +.media-sidebar .setting span, +.attachment-details .setting span { + min-width: 30%; + margin-left: 4%; + font-size: 12px; + text-align: left; + word-wrap: break-word; +} + +.media-sidebar .setting .name { + max-width: 80px; +} + +.media-sidebar .setting select, +.attachment-details .setting select { + max-width: 65%; +} + +.media-sidebar .setting input[type="checkbox"], +.media-sidebar .field input[type="checkbox"], +.media-sidebar .setting input[type="radio"], +.media-sidebar .field input[type="radio"], +.attachment-details .setting input[type="checkbox"], +.attachment-details .field input[type="checkbox"], +.attachment-details .setting input[type="radio"], +.attachment-details .field input[type="radio"] { + float: none; + margin: 8px 3px 0; + padding: 0; +} + +.media-sidebar .setting span, +.attachment-details .setting span, +.compat-item label span { + float: right; + min-height: 22px; + padding-top: 8px; + line-height: 16px; + font-weight: normal; + color: #666; +} + +.compat-item label span { + text-align: left; +} + +.media-sidebar .setting input[type="text"], +.media-sidebar .setting input[type="password"], +.media-sidebar .setting input[type="email"], +.media-sidebar .setting input[type="number"], +.media-sidebar .setting input[type="search"], +.media-sidebar .setting input[type="tel"], +.media-sidebar .setting input[type="url"], +.media-sidebar .setting textarea, +.media-sidebar .setting .value, +.attachment-details .setting input[type="text"], +.attachment-details .setting input[type="password"], +.attachment-details .setting input[type="email"], +.attachment-details .setting input[type="number"], +.attachment-details .setting input[type="search"], +.attachment-details .setting input[type="tel"], +.attachment-details .setting input[type="url"], +.attachment-details .setting textarea, +.attachment-details .setting .value { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + margin: 1px; + width: 65%; + float: left; +} + +.media-sidebar .setting .value, +.attachment-details .setting .value { + margin: 0 1px; + text-align: right; +} + +.media-sidebar .setting textarea, +.attachment-details .setting textarea, +.compat-item .field textarea { + height: 62px; + resize: vertical; +} + +.media-sidebar select, +.attachment-details select { + margin-top: 3px; +} + +.compat-item { + float: right; + width: 100%; + overflow: hidden; +} + +.compat-item table { + width: 100%; + table-layout: fixed; + border-spacing: 0; + border: 0; +} + +.compat-item tr { + padding: 2px 0; + display: block; + overflow: hidden; +} + +.compat-item .label, +.compat-item .field { + display: block; + margin: 0; + padding: 0; +} + +.compat-item .label { + min-width: 30%; + margin-left: 4%; + float: right; + text-align: left; +} + +.compat-item .label span { + display: block; + width: 100%; +} + +.compat-item .field { + float: left; + width: 66%; +} + +.compat-item .field input[type="text"], +.compat-item .field input[type="password"], +.compat-item .field input[type="email"], +.compat-item .field input[type="number"], +.compat-item .field input[type="search"], +.compat-item .field input[type="tel"], +.compat-item .field input[type="url"] { + width: 100%; + margin: 0; +} + +.sidebar-for-errors .attachment-details, +.sidebar-for-errors .compat-item, +.sidebar-for-errors .media-sidebar .media-progress-bar, +.sidebar-for-errors .upload-details { + display: none !important; +} + +/** + * Menu + */ +.media-menu { + position: absolute; + top: 0; + right: 0; + left: 0; + bottom: 0; + margin: 0; + padding: 10px 0; + background: #f3f3f3; + border-left-width: 1px; + border-left-style: solid; + border-left-color: #ccc; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.media-menu > a { + display: block; + position: relative; + padding: 8px 20px; + margin: 0; + line-height: 18px; + font-size: 14px; + color: #0074a2; + text-decoration: none; +} + +.media-menu > a:hover { + color: #21759B; + background: rgba( 0, 0, 0, 0.04 ); +} + +.media-menu > a:active { + outline: none; +} + +.media-menu .active, +.media-menu .active:hover { + color: #222; + font-weight: bold; +} + +.media-menu .separator { + height: 0; + margin: 12px 20px; + padding: 0; + border-top: 1px solid #ddd; +} + +/** + * Menu + */ +.media-router { + position: relative; + padding: 0 6px; + margin: 0; + clear: both; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.media-router a { + -webkit-transition: none; + transition: none; +} + +.media-router > a { + position: relative; + float: right; + padding: 8px 10px 9px; + margin: 0; + height: 18px; + line-height: 18px; + font-size: 14px; + text-decoration: none; +} + +.media-router > a:last-child { + border-left: 0; +} + +.media-router > a:active { + outline: none; +} + +.media-router .active, +.media-router .active:hover { + color: #333; +} + +.media-router .active, +.media-router > a.active:last-child { + margin: -1px -1px 0; + background: #fff; + border: 1px solid #ddd; + border-bottom: none; +} + +.media-router .active:after { + display: none; +} + +/** + * Frame + */ +.media-frame { + overflow: hidden; + position: absolute; + top: 0; + right: 0; + left: 0; + bottom: 0; +} + +.media-frame-menu { + position: absolute; + top: 0; + right: 0; + bottom: 0; + width: 200px; + z-index: 150; +} + +.media-frame-title { + position: absolute; + top: 0; + right: 200px; + left: 0; + height: 50px; + z-index: 200; +} + +.media-frame-router { + position: absolute; + top: 50px; + right: 200px; + left: 0; + height: 36px; + z-index: 200; +} + +.media-frame-content { + position: absolute; + top: 84px; + right: 200px; + left: 0; + bottom: 61px; + height: auto; + width: auto; + margin: 0; + overflow: auto; + background: #fff; + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; +} + +.media-frame-toolbar { + position: absolute; + right: 200px; + left: 0; + bottom: 0; + height: 60px; + z-index: 100; +} + +.media-frame.hide-menu .media-frame-title, +.media-frame.hide-menu .media-frame-router, +.media-frame.hide-menu .media-frame-toolbar, +.media-frame.hide-menu .media-frame-content { + right: 0; +} + +.media-frame.hide-menu .media-frame-menu { + right: -200px; +} + +.media-frame.hide-toolbar .media-frame-content { + bottom: 0; +} + +.media-frame.hide-toolbar .media-frame-toolbar { + bottom: -61px; +} + +.media-frame.hide-router .media-frame-content { + top: 50px; +} + +.media-frame.hide-router .media-frame-router { + display: none; +} + +.media-frame.hide-router .media-frame-title { + border-bottom: 1px solid #dfdfdf; + -webkit-box-shadow: 0 4px 4px -4px rgba( 0, 0, 0, 0.1 ); + box-shadow: 0 4px 4px -4px rgba( 0, 0, 0, 0.1 ); +} + +.media-frame-title .dashicons { + display: none; +} + +.media-frame-title h1 { + padding: 0 16px; + font-size: 22px; + line-height: 50px; + margin: 0; +} + +.media-frame-title .suggested-dimensions { + font-size: 14px; + float: left; + margin-left: 20px; +} + +.media-frame-content .crop-content { + height: 100%; +} + +.media-frame-content .crop-content .crop-image { + display: block; + margin: auto; + max-width: 100%; + max-height: 100%; +} + +.media-frame-content .crop-content .upload-errors +{ + position: absolute; + width: 300px; + top: 50%; + right: 50%; + margin-right: -150px; + margin-left: -150px; + z-index: 600000; +} + +/** + * Iframes + */ +.media-frame .media-iframe { + overflow: hidden; +} + +.media-frame .media-iframe, +.media-frame .media-iframe iframe { + height: 100%; + width: 100%; + border: 0; +} + +/** + * Attachment Browser Filters + */ +.media-frame select.attachment-filters { + margin-top: 11px; + margin-left: 2%; + max-width: 47%; +} + +/** + * Search + */ +.media-frame .search { + margin-top: 11px; + padding: 4px; + font-size: 13px; + color: #464646; + font-family: "Open Sans", sans-serif; + -webkit-appearance: none; +} + +.media-toolbar-primary .search { + max-width: 100%; +} + +/** + * Attachments + */ +.attachments { + margin: 0; + -webkit-overflow-scrolling: touch; +} + +/** + * Attachment + */ +.attachment { + position: relative; + float: right; + padding: 8px; + margin: 0; + color: #464646; + cursor: pointer; + list-style: none; + text-align: center; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + width: 25%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.attachment:focus, +.selected.attachment:focus, +.attachment.details:focus { + -webkit-box-shadow: + inset 0 0 2px 3px #fff, + inset 0 0 0 7px #5b9dd9; + box-shadow: + inset 0 0 2px 3px #fff, + inset 0 0 0 7px #5b9dd9; + outline: none; +} + +.selected.attachment { + -webkit-box-shadow: + inset 0 0 0 5px #fff, + inset 0 0 0 7px #ccc; + box-shadow: + inset 0 0 0 5px #fff, + inset 0 0 0 7px #ccc; +} + +.attachment.details { + -webkit-box-shadow: + inset 0 0 0 3px #fff, + inset 0 0 0 7px #1e8cbe; + box-shadow: + inset 0 0 0 3px #fff, + inset 0 0 0 7px #1e8cbe; +} + +.attachment-preview { + position: relative; + -webkit-box-shadow: + inset 0 0 15px rgba( 0, 0, 0, 0.1 ), + inset 0 0 0 1px rgba( 0, 0, 0, 0.05 ); + box-shadow: + inset 0 0 15px rgba( 0, 0, 0, 0.1 ), + inset 0 0 0 1px rgba( 0, 0, 0, 0.05 ); + background: #eee; + cursor: pointer; +} + +.attachment-preview:before { + content: ''; + display: block; + padding-top: 100%; +} + +.attachment .icon { + margin: 0 auto; + overflow: hidden; +} + +.attachment .thumbnail { + overflow: hidden; + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + opacity: 1; + -webkit-transition: opacity .1s; + transition: opacity .1s; +} + +.attachment .portrait img { + max-width: 100%; +} + +.attachment .landscape img { + max-height: 100%; +} + +.attachment .thumbnail:after { + content: ''; + display: block; + position: absolute; + top: 0; + right: 0; + left: 0; + bottom: 0; + -webkit-box-shadow: inset 0 0 0 1px rgba( 0, 0, 0, 0.1 ); + box-shadow: inset 0 0 0 1px rgba( 0, 0, 0, 0.1 ); + overflow: hidden; +} + +/* @noflip */ +.attachment .thumbnail img { + top: 0; + left: 0; +} + +/* @noflip */ +.attachment .thumbnail .centered { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + -webkit-transform: translate( 50%, 50% ); + -ms-transform: translate(50%,50%); /* Fails with spaces?? Weird! */ + transform: translate( 50%, 50% ); +} + +.attachment .thumbnail .centered img { + -webkit-transform: translate( -50%, -50% ); + -ms-transform: translate(-50%,-50%); + transform: translate( -50%, -50% ); +} + +.attachment .thumbnail .centered img.icon { + -webkit-transform: translate( -50%, -70% ); + -ms-transform: translate(-50%,-70%); + transform: translate( -50%, -70% ); +} + +.ie8 .attachment img.icon { + top: 20%; + position: relative; +} + +.attachment .filename { + position: absolute; + right: 0; + left: 0; + bottom: 0; + overflow: hidden; + max-height: 100%; + word-wrap: break-word; + text-align: center; + font-weight: bold; + background: rgba( 255, 255, 255, 0.8 ); + -webkit-box-shadow: inset 0 0 0 1px rgba( 0, 0, 0, 0.15 ); + box-shadow: inset 0 0 0 1px rgba( 0, 0, 0, 0.15 ); +} + +.attachment .filename div { + padding: 5px 10px; +} + +.attachment .thumbnail img { + position: absolute; +} + +.attachment .close { + display: block; + position: absolute; + top: 5px; + left: 5px; + height: 22px; + width: 22px; + padding: 0; + font-size: 20px; + line-height: 20px; + text-align: center; + text-decoration: none; + color: #464646; + background-color: #fff; + background-position: -96px 4px; + border-width: 0; + -webkit-border-radius: 3px; + border-radius: 3px; + -webkit-box-shadow: 0 0 0 1px rgba( 0, 0, 0, 0.3 ); + box-shadow: 0 0 0 1px rgba( 0, 0, 0, 0.3 ); + -webkit-transition-duration: none; + transition-duration: none; + -webkit-transition-property: none; + transition-property: none; +} + +.attachment a.close:hover, +.attachment a.close:focus { + -webkit-box-shadow: 0 0 0 1px rgba( 0, 0, 0, 0.6 ); + box-shadow: 0 0 0 1px rgba( 0, 0, 0, 0.6 ); + background-position: -36px 4px; + +} + +.attachment .check { + display: none; + height: 24px; + width: 24px; + position: absolute; + z-index: 10; + top: 0; + left: 0; + outline: none; + background: #eee; + -webkit-box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba( 0, 0, 0, 0.15 ); + box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba( 0, 0, 0, 0.15 ); +} + +.attachment .check div { + background-position: -1px 0; + height: 15px; + width: 15px; + margin: 5px; +} + +.attachment .check:hover div { + background-position: -40px 0; +} + +.attachment.selected .check { + display: block; +} + +.attachment.details .check, +.attachment.selected .check:focus, +.media-frame.mode-grid .attachment.selected .check { + background-color: #1e8cbe; + -webkit-box-shadow: + 0 0 0 1px #fff, + 0 0 0 2px #1e8cbe; + box-shadow: + 0 0 0 1px #fff, + 0 0 0 2px #1e8cbe; +} + +.attachment.details .check div, +.media-frame.mode-grid .attachment.selected .check div { + background-position: -21px 0; +} + +.attachment.details .check:hover div, +.attachment.selected .check:focus div, +.media-frame.mode-grid .attachment.selected .check:hover div { + background-position: -60px 0; +} + +.media-frame .attachment .describe { + position: relative; + display: block; + width: 100%; + margin: 0; + padding: 8px; + font-size: 12px; + -webkit-border-radius: 0; + border-radius: 0; +} + +/** + * Attachments Browser + */ +.media-frame .attachments-browser { + position: relative; + width: 100%; + height: 100%; + overflow: hidden; +} + +.attachments-browser .media-toolbar { + left: 300px; + height: 50px; +} + +.attachments-browser.hide-sidebar .media-toolbar { + left: 0; +} + +.attachments-browser .media-toolbar-primary > .media-button, +.attachments-browser .media-toolbar-primary > .media-button-group, +.attachments-browser .media-toolbar-secondary > .media-button, +.attachments-browser .media-toolbar-secondary > .media-button-group { + margin: 11px 0; +} + +.attachments-browser .attachments { + padding: 2px 8px 8px; +} + +.attachments-browser .attachments, +.attachments-browser .uploader-inline { + position: absolute; + top: 50px; + right: 0; + left: 300px; + bottom: 0; + overflow: auto; + outline: none; +} + +.attachments-browser .uploader-inline.hidden { + display: none; +} + +.uploader-inline .close { + background-color: transparent; + border: 0; + cursor: pointer; + height: 48px; + position: absolute; + left: 0; + text-align: center; + top: 0; + width: 50px; + z-index: 1; +} + +.uploader-inline .close:before { + font: normal 30px/50px 'dashicons' !important; + color: #777; + display: inline-block; + content: '\f335'; + font-weight: 300; +} + +.attachments-browser.hide-sidebar .attachments, +.attachments-browser.hide-sidebar .uploader-inline { + left: 0; + margin-left: 0; +} + +.attachments-browser .instructions { + display: inline-block; + margin-top: 16px; + line-height: 18px; + font-size: 13px; + color: #666; + margin-left: 0.5em; +} + +.attachments-browser .no-media { + padding: 2em 2em 0 0; +} + +/** + * Progress Bar + */ +.media-progress-bar { + position: relative; + height: 10px; + width: 70%; + margin: 10px auto; + -webkit-border-radius: 10px; + border-radius: 10px; + background: #dfdfdf; + background: rgba( 0, 0, 0, 0.1 ); +} + +.media-progress-bar div { + height: 10px; + min-width: 20px; + width: 0; + background: #1e8cbe; + -webkit-border-radius: 10px; + border-radius: 10px; + -webkit-transition: width 300ms; + transition: width 300ms; +} + +.media-uploader-status .media-progress-bar { + display: none; + width: 100%; +} + +.uploading.media-uploader-status .media-progress-bar { + display: block; +} + +.attachment-preview .media-progress-bar { + position: absolute; + top: 50%; + right: 15%; + width: 70%; + margin: -5px 0 0 0; +} + +.media-uploader-status { + position: relative; + margin: 0 auto; + padding-bottom: 10px; + max-width: 400px; +} + +.media-sidebar .media-uploader-status { + border-bottom: 1px solid #dfdfdf; +} + +.uploader-inline .media-uploader-status h3 { + display: none; +} + +.media-uploader-status .upload-details { + display: none; + font-size: 12px; + color: #666; +} + +.uploading.media-uploader-status .upload-details { + display: block; +} + +.media-uploader-status .upload-detail-separator { + padding: 0 4px; +} + +.media-uploader-status .upload-count { + color: #464646; +} + +.media-uploader-status .upload-dismiss-errors, +.media-uploader-status .upload-errors { + display: none; +} + +.errors.media-uploader-status .upload-dismiss-errors, +.errors.media-uploader-status .upload-errors { + display: block; +} + +.media-uploader-status .upload-dismiss-errors { + text-decoration: none; +} + +.media-sidebar .media-uploader-status .upload-dismiss-errors { + position: absolute; + top: 0; + left: 0; +} + +.upload-errors .upload-error { + margin: 8px auto 0 auto; + padding: 8px; + border: 1px #c00 solid; + background: #ffebe8; + -webkit-border-radius: 3px; + border-radius: 3px; +} + +.upload-errors .upload-error-label { + padding: 2px 4px; + margin-left: 8px; + font-weight: bold; + color: #fff; + background: #e00; + background-image: -webkit-gradient(linear, right top, right bottom, from(#e00), to(#a00)); + background-image: -webkit-linear-gradient(top, #e00, #a00); + background-image: linear-gradient(to bottom, #e00, #a00); + -webkit-border-radius: 3px; + border-radius: 3px; +} + +.upload-errors .upload-error-message { + display: block; + padding-top: 8px; + color: #b44; + word-wrap: break-word; +} + +.uploader-window { + position: fixed; + top: 0; + right: 0; + left: 0; + bottom: 0; + background: rgba( 0, 86, 132, 0.9 ); + z-index: 250000; + display: none; + text-align: center; + opacity: 0; + -webkit-transition: opacity 250ms; + transition: opacity 250ms; +} + +.uploader-window-content { + position: absolute; + top: 10px; + right: 10px; + left: 10px; + bottom: 10px; + border: 1px dashed #fff; +} + +.uploader-window h3 { + margin: -0.5em 0 0; + position: absolute; + top: 50%; + right: 0; + left: 0; + -webkit-transform: translateY( -50% ); + -ms-transform: translateY(-50%); + transform: translateY( -50% ); + font-size: 40px; + color: #fff; + padding: 0; +} + +.uploader-window .media-progress-bar { + margin-top: 20px; + max-width: 300px; + background: transparent; + border-color: #fff; + display: none; +} + +.uploader-window .media-progress-bar div { + background: #fff; +} + +.uploading .uploader-window .media-progress-bar { + display: block; +} + +.media-frame .uploader-inline { + margin-bottom: 20px; + padding: 0; + text-align: center; +} + +.uploader-inline-content { + position: absolute; + top: 30%; + right: 0; + left: 0; +} + +.uploader-inline-content .upload-ui { + margin: 2em 0; +} + +.uploader-inline-content .post-upload-ui { + margin-bottom: 2em; +} + +.uploader-inline .has-upload-message .upload-ui { + margin: 0 0 4em; +} + +.uploader-inline h3 { + font-size: 20px; + line-height: 28px; + font-weight: 400; + margin: 0; +} + +.uploader-inline .has-upload-message .upload-instructions { + font-size: 14px; + color: #464646; + font-weight: normal; +} + +.uploader-inline .drop-instructions { + display: none; +} + +.supports-drag-drop .uploader-inline .drop-instructions { + display: block; +} + +.uploader-inline p { + font-size: 12px; + margin: 0.5em 0; +} + +.uploader-inline .media-progress-bar { + display: none; +} + +.uploading.uploader-inline .media-progress-bar { + display: block; +} + +.uploader-inline .browser { + display: inline-block !important; +} + +/** + * Selection + */ +.media-selection { + position: absolute; + top: 0; + right: 0; + left: 350px; + height: 60px; + padding: 0 16px 0 0; + overflow: hidden; + white-space: nowrap; +} + +.media-selection .selection-info { + display: inline-block; + font-size: 12px; + height: 60px; + margin-left: 10px; + vertical-align: top; +} + +.media-selection.empty, +.media-selection.editing { + display: none; +} + +.media-selection.one .edit-selection { + display: none; +} + +.media-selection .count { + display: block; + padding-top: 12px; + font-size: 14px; + line-height: 20px; + font-weight: bold; +} + +.media-selection .selection-info a { + display: block; + float: right; + padding: 1px 8px; + margin: 1px -8px 1px 8px; + line-height: 16px; + text-decoration: none; + border-left: 1px solid #dfdfdf; + color: #21759B; +} + +.media-selection .selection-info a:hover { + background: #21759B; + color: #fff; + border-color: transparent; +} + +.media-selection .selection-info a:last-child { + border-left: 0; + margin-left: 0; +} + +.media-selection .selection-info .clear-selection { + color: red; +} + +.media-selection .selection-info .clear-selection:hover { + background: red; +} + +.media-selection .selection-view { + display: inline-block; + vertical-align: top; +} + +.media-selection .attachments { + display: inline-block; + height: 48px; + margin: 6px; + padding: 0; + overflow: hidden; + vertical-align: top; +} + +.media-selection .attachment { + width: 40px; + padding: 0; + margin: 4px; + -webkit-box-shadow: none; + box-shadow: none; +} + +.media-selection .attachment .thumbnail { + top: 0; + left: 0; + bottom: 0; + right: 0; +} + +.media-selection .attachment .icon { + width: 50%; +} + +.media-selection .attachment-preview { + -webkit-box-shadow: none; + box-shadow: none; + background: none; +} + +.media-selection .attachment.selection.details .thumbnail { + -webkit-box-shadow: + 0 0 0 1px #fff, + 0 0 0 3px #1e8cbe; + box-shadow: + 0 0 0 1px #fff, + 0 0 0 3px #1e8cbe; +} + +.media-selection:after { + content: ''; + display: block; + position: absolute; + top: 0; + left: 0; + bottom: 0; + width: 25px; + background-image: -webkit-gradient(linear, left top, right top, from(rgba( 255, 255, 255, 1 )), to(rgba( 255, 255, 255, 0 ))); + background-image: -webkit-linear-gradient(left, rgba( 255, 255, 255, 1 ), rgba( 255, 255, 255, 0 )); + background-image: linear-gradient(to right, rgba( 255, 255, 255, 1 ) , rgba( 255, 255, 255, 0 ) ); +} + +.media-selection .attachment .filename { + display: none; +} + +/** + * Spinner + */ +.media-frame .spinner { + background: url(../images/spinner.gif) no-repeat; + -webkit-background-size: 20px 20px; + background-size: 20px 20px; + display: none; + opacity: 0.7; + filter: alpha(opacity=70); + width: 20px; + height: 20px; + margin: 0; +} + +.media-toolbar .spinner { + margin-top: 14px; +} + +/** + * Attachment Details + */ +.attachment-details { + position: relative; + overflow: auto; +} + +.attachment-details .settings-save-status { + float: left; + text-transform: none; + z-index: 10; +} + +.attachment-details .settings-save-status .spinner { + margin: 0 5px 0; +} + +.attachment-details .settings-save-status .saved { + float: left; + display: none; +} + +.attachment-details.save-waiting .settings-save-status .spinner, +.attachment-details.save-complete .settings-save-status .saved { + display: block; +} + +.attachment-info { + overflow: hidden; + min-height: 60px; + margin-bottom: 16px; + line-height: 18px; + color: #666; + border-bottom: 1px solid #ddd; + padding-bottom: 11px; +} + +.attachment-info .filename { + font-weight: bold; + color: #464646; + word-wrap: break-word; +} + +.attachment-info .thumbnail { + position: relative; + float: right; + max-width: 120px; + max-height: 120px; + margin-top: 5px; + margin-left: 10px; + margin-bottom: 5px; +} + +.uploading .attachment-info .thumbnail { + width: 120px; + height: 80px; + -webkit-box-shadow: inset 0 0 15px rgba( 0, 0, 0, 0.1 ); + box-shadow: inset 0 0 15px rgba( 0, 0, 0, 0.1 ); +} + +.uploading .attachment-info .media-progress-bar { + margin-top: 35px; +} + +.attachment-info .thumbnail-image:after { + content: ''; + display: block; + position: absolute; + top: 0; + right: 0; + left: 0; + bottom: 0; + -webkit-box-shadow: inset 0 0 0 1px rgba( 0, 0, 0, 0.15 ); + box-shadow: inset 0 0 0 1px rgba( 0, 0, 0, 0.15 ); + overflow: hidden; +} + +.attachment-info .thumbnail img { + display: block; + max-width: 120px; + max-height: 120px; + margin: 0 auto; +} + +.attachment-info .details { + float: right; + font-size: 12px; + max-width: 100%; +} + +.attachment-info .edit-attachment, +.attachment-info .refresh-attachment, +.attachment-info .delete-attachment, +.attachment-info .trash-attachment, +.attachment-info .untrash-attachment { + display: block; + text-decoration: none; + white-space: nowrap; +} + +.attachment-info .refresh-attachment, +.attachment-details.needs-refresh .attachment-info .edit-attachment { + display: none; +} + +.attachment-details.needs-refresh .attachment-info .refresh-attachment, +.attachment-info .edit-attachment { + display: block; +} + +.media-modal .delete-attachment, +.media-modal .trash-attachment, +.media-modal .untrash-attachment { + color: #bc0b0b; +} + +.media-modal .delete-attachment:hover, +.media-modal .trash-attachment:hover, +.media-modal .untrash-attachment:hover { + color: red; +} + +/** + * Attachment Display Settings + */ +.attachment-display-settings { + width: 100%; + float: right; + overflow: hidden; +} + +.attachment-display-settings h4 { + margin: 1.4em 0 0.4em; +} + +.collection-settings { + overflow: hidden; +} + +.collection-settings .setting input[type="checkbox"] { + float: right; + margin-left: 8px; +} + +.collection-settings .setting span { + min-width: inherit; +} + +/** + * Image Editor + */ +.media-modal .imgedit-wrap { + position: static; +} + +.media-modal .imgedit-wait { + height: auto !important; + left: 0; + bottom: 0; + right: 0; +} + +.media-modal .imgedit-wrap .imgedit-panel-content { + padding: 16px; + position: absolute; + top: 0; + left: 282px; + bottom: 0; + right: 0; + overflow: auto; +} + +.media-modal .imgedit-wrap .imgedit-settings { + background: #f3f3f3; + border-right: 1px solid #ddd; + padding: 0 16px 16px; + position: absolute; + top: 0; + left: 0; + bottom: 0; + width: 250px; + overflow: auto; +} + +.media-modal .imgedit-group { + background: none; + border: none; + border-bottom: 1px solid #ddd; + -webkit-box-shadow: none; + box-shadow: none; + margin: 0; + margin-bottom: 16px; + padding: 0; + padding-bottom: 16px; + position: relative; /* RTL fix, #WP29352 */ +} + +.media-modal .imgedit-group:last-of-type { + border: none; + margin: 0; + padding: 0; +} + +.media-modal .imgedit-group-top h3 { + text-transform: uppercase; + font-size: 12px; + color: #666; + margin: 0; + margin-top: 24px; +} + +.media-modal .imgedit-group-top h3 a { + text-decoration: none; + color: #666; +} + +.media-modal .imgedit-help-toggle { + margin-top: -2px; + cursor: pointer; + color: #666; +} + +.media-modal .imgedit-help-toggled span.dashicons:before { + content: '\f142'; +} + +.media-modal .imgedit-group img { + margin-top: 5px; +} + +.media-modal .imgedit-wrap div.updated { + margin: 0; + margin-bottom: 16px; +} + + +/** + * Embed from URL and Image Details + */ +.embed-url { + display: block; + position: relative; + padding: 16px; + margin: 0; + z-index: 250; + background: #fff; + font-size: 18px; +} + +.media-frame .embed-url input { + font-size: 18px; + padding: 12px 14px; + width: 100%; + min-width: 200px; + -webkit-box-shadow: inset 2px 2px 4px -2px rgba( 0, 0, 0, 0.1 ); + box-shadow: inset 2px 2px 4px -2px rgba( 0, 0, 0, 0.1 ); +} + +.media-frame .embed-url .spinner { + position: absolute; + top: 32px; + left: 26px; +} + +.media-frame .embed-loading .embed-url .spinner { + display: block; +} + +.embed-link-settings, +.embed-media-settings { + position: absolute; + top: 70px; + right: 0; + left: 0; + bottom: 0; + padding: 16px 16px 32px; + overflow: auto; +} + +.embed-preview img, .embed-preview iframe, .embed-preview embed { + max-width: 100%; +} + +.embed-preview img { + height: auto; +} + +.image-details .media-modal { + right: 140px; + left: 140px; +} + +.image-details .media-frame-title, +.image-details .media-frame-content, +.image-details .media-frame-router { + right: 0; +} + +.image-details .embed-media-settings { + top: 0; + overflow: visible; + padding: 0; +} + +.image-details .embed-media-settings, +.image-details .embed-media-settings div { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.image-details .column-settings { + background: #f3f3f3; + border-left: 1px solid #ddd; + min-height: 100%; + width: 55%; + position: absolute; + top: 0; + right: 0; +} + +.image-details .column-settings h3 { + margin: 20px; + padding-top: 20px; + border-top: 1px solid #ddd; +} + +.image-details .column-image { + width: 45%; + position: absolute; + right: 55%; + top: 0; +} + +.image-details .image { + margin: 20px; +} + +.image-details .image img { + max-width: 100%; + max-height: 500px; +} + +.image-details .advanced-toggle { + color: #666; + text-decoration: none; + display: block; +} + +.image-details .advanced-toggle:after { + font: normal 20px/1 'dashicons'; + speak: none; + vertical-align: top; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + content: '\f140'; + display: inline-block; + margin-top: -2px; +} + +.image-details .advanced-visible .advanced-toggle:after { + content: '\f142'; +} + +.image-details .embed-media-settings .size { + margin-bottom: 4px; +} + +.image-details .custom-size span { + display: block; +} + +.image-details .custom-size label { + display: block; + float: right; +} + +.image-details .custom-size span small { + color: #999; + font-size: inherit; +} + +.image-details .custom-size input { + width: 5em; +} + +.image-details .custom-size .sep { + float: right; + margin: 26px 6px 0 6px; +} + +.image-details .custom-size:after { + content: ''; + display: table; + clear: both; +} + +.media-embed .thumbnail { + max-width: 100%; + max-height: 200px; + position: relative; + float: right; +} + +.media-embed .thumbnail img { + max-height: 200px; + display: block; +} + +.media-embed .thumbnail:after { + content: ''; + display: block; + position: absolute; + top: 0; + right: 0; + left: 0; + bottom: 0; + -webkit-box-shadow: inset 0 0 0 1px rgba( 0, 0, 0, 0.1 ); + box-shadow: inset 0 0 0 1px rgba( 0, 0, 0, 0.1 ); + overflow: hidden; +} + +.media-embed .setting { + width: 100%; + margin: 10px 0; + float: right; + display: block; + clear: both; +} + +.image-details .embed-media-settings .setting { + float: none; + width: auto; +} + +.image-details .actions { + margin: 10px 0; +} + +.image-details .hidden { + display: none; +} + +.media-embed .setting input[type="text"], +.media-embed .setting textarea { + display: block; + width: 100%; + max-width: 400px; + margin: 1px 0; +} + +.image-details .embed-media-settings .setting input[type="text"], +.image-details .embed-media-settings .setting textarea { + max-width: inherit; + width: 70%; +} + +.image-details .embed-media-settings .setting input.link-to-custom, +.image-details .embed-media-settings .link-target, +.image-details .embed-media-settings .custom-size { + margin-right: 27%; + width: 70%; +} + +.image-details .embed-media-settings .link-target { + margin-top: 24px; +} + +.media-embed .setting input.hidden, +.media-embed .setting textarea.hidden { + display: none; +} + +.media-embed .setting span { + display: block; + width: 200px; + font-size: 13px; + line-height: 24px; + color: #666; +} + +.image-details .embed-media-settings .setting span { + float: right; + width: 25%; + text-align: left; + margin: 8px 1% 0 1%; + line-height: 1.1; +} + +.media-embed .setting .button-group { + margin: 2px 0; +} + +.media-embed-sidebar { + position: absolute; + top: 0; + right: 440px; +} + +.advanced-section, +.link-settings { + margin-top: 10px; +} + +/* Drag & drop on the editor upload */ +#wp-fullscreen-body .uploader-editor, +.wp-editor-wrap .uploader-editor { + background: rgba( 150, 150, 150, 0.9 ); + position: absolute; + top: 0; + right: 0; + width: 100%; + height: 100%; + z-index: 99998; /* under the toolbar */ + display: none; + text-align: center; +} + +#wp-fullscreen-body .uploader-editor { + background: rgba( 0, 86, 132, 0.9 ); + position: fixed; + z-index: 100050; /* above the editor toolbar */ +} + +.wp-editor-wrap.wp-fullscreen-wrap .uploader-editor { + opacity: 0; +} + +#wp-fullscreen-body .uploader-editor-content, +.wp-editor-wrap .uploader-editor-content { + border: 1px dashed #fff; + position: absolute; + top: 10px; + right: 10px; + left: 10px; + bottom: 10px; +} + +#wp-fullscreen-body .uploader-editor .uploader-editor-title, +.wp-editor-wrap .uploader-editor .uploader-editor-title { + position: absolute; + top: 50%; + right: 0; + left: 0; + -webkit-transform: translateY( -50% ); + -ms-transform: translateY(-50%); + transform: translateY( -50% ); + font-size: 3em; + line-height: 1.3; + font-weight: bold; + color: #fff; + padding: 0; + margin: 0; + display: none; +} + +.wp-editor-wrap .uploader-editor.droppable { + background: rgba( 0, 86, 132, 0.9 ); +} + +#wp-fullscreen-body .uploader-editor .uploader-editor-title, +.wp-editor-wrap .uploader-editor.droppable .uploader-editor-title { + display: block; +} + +/** + * IE7 Fixes + */ +.ie7 .media-frame .attachments-browser { + position: static; +} + +.ie7 .media-frame .embed-url input { + margin-top: 4px; + width: 90%; +} + +.ie7 .compat-item { + width: 99%; +} + +.ie7 .attachment-display-settings { + width: auto; +} + +.ie7 .attachment-preview, +.ie7 .attachment-preview .thumbnail { + width: 120px; + height: 120px; +} + +.ie7 .media-frame .attachment .describe { + width: 102px; +} + +.ie7 .media-sidebar .setting select { + max-width: 55%; +} + +.ie7 .media-sidebar .setting input[type="text"], +.ie7 .media-sidebar .setting input[type="password"], +.ie7 .media-sidebar .setting input[type="email"], +.ie7 .media-sidebar .setting input[type="number"], +.ie7 .media-sidebar .setting input[type="search"], +.ie7 .media-sidebar .setting input[type="tel"], +.ie7 .media-sidebar .setting input[type="url"], +.ie7 .media-sidebar .setting textarea { + width: 55%; +} + +.ie7 .media-sidebar .setting .link-to-custom { + float: right; +} + +/** + * Localization + */ +.rtl .media-modal, +.rtl .media-frame, +.rtl .media-frame .search, +.rtl .media-frame input[type="text"], +.rtl .media-frame input[type="password"], +.rtl .media-frame input[type="number"], +.rtl .media-frame input[type="search"], +.rtl .media-frame input[type="email"], +.rtl .media-frame input[type="url"], +.rtl .media-frame input[type="tel"], +.rtl .media-frame textarea, +.rtl .media-frame select { + font-family: Tahoma, sans-serif; +} + +:lang(he-il) .rtl .media-modal, +:lang(he-il) .rtl .media-frame, +:lang(he-il) .rtl .media-frame .search, +:lang(he-il) .rtl .media-frame input[type="text"], +:lang(he-il) .rtl .media-frame input[type="password"], +:lang(he-il) .rtl .media-frame input[type="number"], +:lang(he-il) .rtl .media-frame input[type="search"], +:lang(he-il) .rtl .media-frame input[type="email"], +:lang(he-il) .rtl .media-frame input[type="url"], +:lang(he-il) .rtl .media-frame textarea, +:lang(he-il) .rtl .media-frame select { + font-family: Arial, sans-serif; +} + +/** + * Responsive layout + */ +@media only screen and (max-width: 900px) { + + /* Drop-down menu */ + .media-frame:not(.hide-menu) .media-frame-title, + .media-frame:not(.hide-menu) .media-frame-router, + .media-frame:not(.hide-menu) .media-frame-content, + .media-frame:not(.hide-menu) .media-frame-toolbar { + right: 0; + } + + .media-frame:not(.hide-menu) .media-frame-menu { + position: static; + width: 0; + } + + .media-frame:not(.hide-menu) .media-menu { + width: auto; + max-width: 80%; + overflow: auto; + z-index: 2000; + top: 50px; + right: -300px; + left: auto; + bottom: auto; + padding: 5px 0; + border: 1px solid #ccc; + } + + .media-frame:not(.hide-menu) .media-menu.visible { + right: 0; + } + + .media-frame:not(.hide-menu) .media-menu > a { + padding: 12px 16px; + font-size: 16px; + } + + .media-frame:not(.hide-menu) .media-menu > a.active { + display: none; + } + + .media-frame:not(.hide-menu) .media-menu .separator { + margin: 5px 10px; + } + + .media-frame:not(.hide-menu) .media-frame-title { + right: 0; + color: #21759b; + } + + .media-frame:not(.hide-menu) .media-frame-title .dashicons { + display: inline-block; + line-height: 50px; + } + + .media-frame:not(.hide-menu) .media-frame-title h1 { + line-height: 3; + font-size: 18px; + float: right; + cursor: pointer; + } + /* End drop-down menu */ + + .media-sidebar { + width: 230px; + } + + .attachments-browser .attachments, + .attachments-browser .uploader-inline, + .attachments-browser .media-toolbar { + left: 262px; + } + + .media-sidebar .setting, + .attachment-details .setting { + margin: 6px 0px; + } + + .media-sidebar .setting input, + .media-sidebar .setting textarea, + .media-sidebar .setting span, + .attachment-details .setting input, + .attachment-details .setting textarea, + .attachment-details .setting span, + .compat-item label span { + float: none; + } + + .media-sidebar .setting span, + .attachment-details .setting span, + .compat-item label span { + text-align: inherit; + min-height: 16px; + margin: 0; + padding: 8px 2px 0; + } + + .media-sidebar .setting .value, + .attachment-details .setting .value { + float: none; + width: auto; + } + + .media-sidebar .setting input[type="text"], + .media-sidebar .setting input[type="password"], + .media-sidebar .setting input[type="email"], + .media-sidebar .setting input[type="number"], + .media-sidebar .setting input[type="search"], + .media-sidebar .setting input[type="tel"], + .media-sidebar .setting input[type="url"], + .media-sidebar .setting textarea, + .media-sidebar .setting select, + .attachment-details .setting input[type="text"], + .attachment-details .setting input[type="password"], + .attachment-details .setting input[type="email"], + .attachment-details .setting input[type="number"], + .attachment-details .setting input[type="search"], + .attachment-details .setting input[type="tel"], + .attachment-details .setting input[type="url"], + .attachment-details .setting textarea, + .attachment-details .setting select { + float: none; + width: 98%; + max-width: none; + height: auto; + } + + .media-sidebar .setting select.columns, + .attachment-details .setting select.columns { + width: auto; + } + + .media-frame input, + .media-frame textarea, + .media-frame .search { + padding: 3px 6px; + } + + .image-details .column-image { + width: 30%; + right: 70%; + } + + .image-details .column-settings { + width: 70%; + } + + .image-details .media-modal { + right: 30px; + left: 30px; + } + + .image-details .embed-media-settings .setting { + margin: 20px; + } + + .image-details .embed-media-settings .setting span { + float: none; + text-align: right; + width: 100%; + margin-bottom: 4px; + } + + .image-details .embed-media-settings .setting input.link-to-custom, + .image-details .embed-media-settings .setting input[type="text"], + .image-details .embed-media-settings .setting textarea { + width: 100%; + margin-right: 0; + } + + .image-details .embed-media-settings .custom-size { + margin-right: 20px; + } + + .collection-settings .setting input[type="checkbox"] { + margin-top: 0; + } + + .media-selection { + min-width: 120px; + } + + .media-selection:after { + background: none; + } + + .media-selection .attachments { + display: none; + } + + .media-modal .attachments-browser .media-toolbar .search { + max-width: 100%; + height: auto; + float: left; + } + + .media-modal .attachments-browser .media-toolbar .attachment-filters { + height: auto; + } + + .media-modal .attachments-browser .media-toolbar .spinner { + margin: 14px 8px 0; + } + + /* Text inputs need to be 16px, or they force zooming on iOS */ + .media-frame input[type="text"], + .media-frame input[type="password"], + .media-frame input[type="number"], + .media-frame input[type="search"], + .media-frame input[type="email"], + .media-frame input[type="url"], + .media-frame textarea, + .media-frame select { + font-size: 16px; + } +} + +/* Responsive on portrait and landscape */ +@media only screen and (max-width: 640px), screen and (max-height: 400px) { + /* Full-bleed modal */ + .media-modal, + .image-details .media-modal { + position: fixed; + top: 0; + right: 0; + left: 0; + bottom: 0; + } + + .media-modal-backdrop { + position: fixed; + } + + .media-sidebar { + z-index: 1900; + max-width: 70%; + bottom: 120%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding-bottom: 0; + } + + .media-sidebar.visible { + bottom: 0; + } + + .attachments-browser .attachments, + .attachments-browser .uploader-inline, + .attachments-browser .media-toolbar { + left: 0; + } + + .image-details .media-frame-title { + display: block; + top: 0; + font-size: 14px; + } + + .image-details .column-image, + .image-details .column-settings { + width: 100%; + position: relative; + right: 0; + } + + .image-details .column-settings { + padding: 4px 0; + } + + /* Media tabs on the top */ + .media-frame-content .media-toolbar .instructions { + display: none; + } +} + +/* Landscape specific header override */ +@media screen and (max-height: 400px) { + .media-menu { + padding: 0; + } + + .media-frame-router { + top: 44px; + } + + .media-frame-content { + top: 78px; + } + + .attachments-browser .attachments { + top: 40px; + } + + /* Prevent unnecessary scrolling on title input */ + .embed-link-settings { + overflow: visible; + } +} + +@media only screen and (max-width: 480px) { + .media-modal-close { + top: 5px; + left: 5px; + } + + .media-modal .media-frame-title { + height: 40px; + } + + .media-modal .media-frame-title h1, + .media-frame:not(.hide-menu) .media-frame-title h1 { + font-size: 18px; + line-height: 40px; + } + + .media-frame:not(.hide-menu) .media-frame-title .dashicons { + line-height: 40px; + } + + .media-frame-router, + .media-frame:not(.hide-menu) .media-menu { + top: 40px; + } + + .media-frame-content { + top: 74px; + } + + .media-frame.hide-router .media-frame-content { + top: 40px; + } +} + +/** + * HiDPI Displays + */ +@media print, + (-o-min-device-pixel-ratio: 5/4), + (-webkit-min-device-pixel-ratio: 1.25), + (min-resolution: 120dpi) { + + .media-modal-icon { + background-image: url(../images/uploader-icons-2x.png); + -webkit-background-size: 134px 15px; + background-size: 134px 15px; + } + + .media-frame .spinner { + background-image: url(../images/spinner-2x.gif); + } +} + +.media-frame-content[data-columns="1"] .attachment { + width: 100%; +} + +.media-frame-content[data-columns="2"] .attachment { + width: 50%; +} + +.media-frame-content[data-columns="3"] .attachment { + width: 33.33%; +} + +.media-frame-content[data-columns="4"] .attachment { + width: 25%; +} + +.media-frame-content[data-columns="5"] .attachment { + width: 20%; +} + +.media-frame-content[data-columns="6"] .attachment { + width: 16.66%; +} + +.media-frame-content[data-columns="7"] .attachment { + width: 14.28%; +} + +.media-frame-content[data-columns="8"] .attachment { + width: 12.5%; +} + +.media-frame-content[data-columns="9"] .attachment { + width: 11.11%; +} + +.media-frame-content[data-columns="10"] .attachment { + width: 10%; +} + +.media-frame-content[data-columns="11"] .attachment { + width: 9.09%; +} + +.media-frame-content[data-columns="12"] .attachment { + width: 8.33%; +} diff --git a/wp-includes/css/media-views-rtl.min.css b/wp-includes/css/media-views-rtl.min.css new file mode 100644 index 0000000..6868c39 --- /dev/null +++ b/wp-includes/css/media-views-rtl.min.css @@ -0,0 +1,8 @@ +.media-modal *{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.media-frame input,.media-frame select,.media-frame textarea{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.media-frame,.media-modal{font-family:"Open Sans",sans-serif;font-size:12px}.media-frame input,.media-frame textarea{padding:6px 8px}.media-frame select,.wp-admin .media-frame select{line-height:28px;margin-top:3px}.media-frame a{border-bottom:none;color:#21759b}.media-frame a:hover{color:#d54e21}.media-frame a.button{color:#333}.media-frame a.button:hover{color:#222}.media-frame a.button-primary,.media-frame a.button-primary:hover{color:#fff}.media-frame input[type=email],.media-frame input[type=number],.media-frame input[type=password],.media-frame input[type=search],.media-frame input[type=text],.media-frame input[type=url],.media-frame select,.media-frame textarea{font-family:"Open Sans",sans-serif;font-size:12px;border-width:1px;border-style:solid;border-color:#dfdfdf}.media-frame input[type=email]:focus,.media-frame input[type=number]:focus,.media-frame input[type=password]:focus,.media-frame input[type=search]:focus,.media-frame input[type=text]:focus,.media-frame input[type=url]:focus,.media-frame select:focus,.media-frame textarea:focus{border-color:#5b9dd9}.media-frame select{height:24px;padding:2px}.media-frame input:disabled,.media-frame input[readonly],.media-frame textarea:disabled,.media-frame textarea[readonly]{background-color:#eee}.media-frame input[type=search]{-webkit-appearance:textfield}.media-frame :-moz-placeholder{color:#a9a9a9}.media-frame .hidden{display:none}/*! + * jQuery UI Draggable/Sortable 1.11.2 + * http://jqueryui.com + * + * Copyright 2014 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */.ui-draggable-handle,.ui-sortable-handle{-ms-touch-action:none;touch-action:none}.media-modal{position:fixed;top:30px;right:30px;left:30px;bottom:30px;z-index:160000}.wp-customizer .media-modal{z-index:560000}.media-modal-backdrop{position:fixed;top:0;right:0;left:0;bottom:0;min-height:360px;background:#000;opacity:.7;z-index:159900}.wp-customizer .media-modal-backdrop{z-index:559900}.media-modal-close{position:absolute;text-decoration:none;top:10px;left:10px;width:30px;height:30px;z-index:1000;-webkit-transition:color .1s ease-in-out,background .1s ease-in-out;transition:color .1s ease-in-out,background .1s ease-in-out}.media-modal-close:active{-webkit-box-shadow:none;box-shadow:none}.media-modal-close span.media-modal-icon{display:block;margin-top:5px;width:30px;height:15px;background-image:none;text-align:center}.media-modal-close .media-modal-icon:before{content:'\f158';font:400 20px/1 dashicons;speak:none;vertical-align:middle;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:#666}.media-modal-close:hover .media-modal-icon:before{color:#2ea2cc}.media-modal-close:active{outline:0}.media-modal-content{position:absolute;top:0;right:0;left:0;bottom:0;overflow:auto;min-height:300px;-webkit-box-shadow:0 5px 15px rgba(0,0,0,.7);box-shadow:0 5px 15px rgba(0,0,0,.7);background:#fcfcfc;-webkit-font-smoothing:subpixel-antialiased}.media-modal-icon{background-image:url(../images/uploader-icons.png);background-repeat:no-repeat}.media-toolbar{position:absolute;top:0;right:0;left:0;z-index:100;height:60px;padding:0 16px;border:0 solid #dfdfdf;overflow:hidden}.media-toolbar-primary{float:left;height:100%;max-width:33%}.media-toolbar-secondary{float:right;height:100%;max-width:66%}.media-toolbar-primary>.media-button,.media-toolbar-primary>.media-button-group{margin-right:10px;float:right;margin-top:15px}.media-toolbar-secondary>.media-button,.media-toolbar-secondary>.media-button-group{margin-left:10px;margin-top:15px}.media-sidebar{position:absolute;top:0;left:0;bottom:0;width:267px;padding:0 16px 24px;z-index:75;background:#f3f3f3;border-right:1px solid #ddd;overflow:auto;-webkit-overflow-scrolling:touch}.hide-toolbar .media-sidebar{bottom:0}.media-sidebar .sidebar-title{font-size:20px;margin:0;padding:12px 10px 10px;line-height:28px}.media-sidebar .sidebar-content{padding:0 10px;margin-bottom:130px}.media-sidebar .search{display:block;width:100%}.image-details h3,.media-sidebar h3{position:relative;font-weight:700;text-transform:uppercase;font-size:12px;color:#666;margin:24px 0 8px}.attachment-details .setting,.media-sidebar .setting{display:block;float:right;width:100%;margin:1px 0}.attachment-details .setting label,.media-sidebar .setting label{display:block}.attachment-details .setting .link-to-custom,.media-sidebar .setting .link-to-custom{margin:3px 2px 0}.attachment-details .setting span,.media-sidebar .setting span{min-width:30%;margin-left:4%;font-size:12px;text-align:left;word-wrap:break-word}.media-sidebar .setting .name{max-width:80px}.attachment-details .setting select,.media-sidebar .setting select{max-width:65%}.attachment-details .field input[type=checkbox],.attachment-details .field input[type=radio],.attachment-details .setting input[type=checkbox],.attachment-details .setting input[type=radio],.media-sidebar .field input[type=checkbox],.media-sidebar .field input[type=radio],.media-sidebar .setting input[type=checkbox],.media-sidebar .setting input[type=radio]{float:none;margin:8px 3px 0;padding:0}.attachment-details .setting span,.compat-item label span,.media-sidebar .setting span{float:right;min-height:22px;padding-top:8px;line-height:16px;font-weight:400;color:#666}.compat-item label span{text-align:left}.attachment-details .setting .value,.attachment-details .setting input[type=email],.attachment-details .setting input[type=number],.attachment-details .setting input[type=password],.attachment-details .setting input[type=search],.attachment-details .setting input[type=tel],.attachment-details .setting input[type=text],.attachment-details .setting input[type=url],.attachment-details .setting textarea,.media-sidebar .setting .value,.media-sidebar .setting input[type=email],.media-sidebar .setting input[type=number],.media-sidebar .setting input[type=password],.media-sidebar .setting input[type=search],.media-sidebar .setting input[type=tel],.media-sidebar .setting input[type=text],.media-sidebar .setting input[type=url],.media-sidebar .setting textarea{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:1px;width:65%;float:left}.attachment-details .setting .value,.media-sidebar .setting .value{margin:0 1px;text-align:right}.attachment-details .setting textarea,.compat-item .field textarea,.media-sidebar .setting textarea{height:62px;resize:vertical}.attachment-details select,.media-sidebar select{margin-top:3px}.compat-item{float:right;width:100%;overflow:hidden}.compat-item table{width:100%;table-layout:fixed;border-spacing:0;border:0}.compat-item tr{padding:2px 0;display:block;overflow:hidden}.compat-item .field,.compat-item .label{display:block;margin:0;padding:0}.compat-item .label{min-width:30%;margin-left:4%;float:right;text-align:left}.compat-item .label span{display:block;width:100%}.compat-item .field{float:left;width:66%}.compat-item .field input[type=email],.compat-item .field input[type=number],.compat-item .field input[type=password],.compat-item .field input[type=search],.compat-item .field input[type=tel],.compat-item .field input[type=text],.compat-item .field input[type=url]{width:100%;margin:0}.sidebar-for-errors .attachment-details,.sidebar-for-errors .compat-item,.sidebar-for-errors .media-sidebar .media-progress-bar,.sidebar-for-errors .upload-details{display:none!important}.media-menu{position:absolute;top:0;right:0;left:0;bottom:0;margin:0;padding:10px 0;background:#f3f3f3;border-left-width:1px;border-left-style:solid;border-left-color:#ccc;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.media-menu>a{display:block;position:relative;padding:8px 20px;margin:0;line-height:18px;font-size:14px;color:#0074a2;text-decoration:none}.media-menu>a:hover{color:#21759B;background:rgba(0,0,0,.04)}.media-menu>a:active{outline:0}.media-menu .active,.media-menu .active:hover{color:#222;font-weight:700}.media-menu .separator{height:0;margin:12px 20px;padding:0;border-top:1px solid #ddd}.media-router{position:relative;padding:0 6px;margin:0;clear:both;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.media-router a{-webkit-transition:none;transition:none}.media-router>a{position:relative;float:right;padding:8px 10px 9px;margin:0;height:18px;line-height:18px;font-size:14px;text-decoration:none}.media-router>a:last-child{border-left:0}.media-router>a:active{outline:0}.media-router .active,.media-router .active:hover{color:#333}.media-router .active,.media-router>a.active:last-child{margin:-1px -1px 0;background:#fff;border:1px solid #ddd;border-bottom:none}.media-router .active:after{display:none}.media-frame{overflow:hidden;position:absolute;top:0;right:0;left:0;bottom:0}.media-frame-menu{position:absolute;top:0;right:0;bottom:0;width:200px;z-index:150}.media-frame-title{position:absolute;top:0;right:200px;left:0;height:50px;z-index:200}.media-frame-router{position:absolute;top:50px;right:200px;left:0;height:36px;z-index:200}.media-frame-content{position:absolute;top:84px;right:200px;left:0;bottom:61px;height:auto;width:auto;margin:0;overflow:auto;background:#fff;border-top:1px solid #ddd;border-bottom:1px solid #ddd}.media-frame-toolbar{position:absolute;right:200px;left:0;bottom:0;height:60px;z-index:100}.media-frame.hide-menu .media-frame-content,.media-frame.hide-menu .media-frame-router,.media-frame.hide-menu .media-frame-title,.media-frame.hide-menu .media-frame-toolbar{right:0}.media-frame.hide-menu .media-frame-menu{right:-200px}.media-frame.hide-toolbar .media-frame-content{bottom:0}.media-frame.hide-toolbar .media-frame-toolbar{bottom:-61px}.media-frame.hide-router .media-frame-content{top:50px}.media-frame.hide-router .media-frame-router{display:none}.media-frame.hide-router .media-frame-title{border-bottom:1px solid #dfdfdf;-webkit-box-shadow:0 4px 4px -4px rgba(0,0,0,.1);box-shadow:0 4px 4px -4px rgba(0,0,0,.1)}.media-frame-title .dashicons{display:none}.media-frame-title h1{padding:0 16px;font-size:22px;line-height:50px;margin:0}.media-frame-title .suggested-dimensions{font-size:14px;float:left;margin-left:20px}.media-frame-content .crop-content{height:100%}.media-frame-content .crop-content .crop-image{display:block;margin:auto;max-width:100%;max-height:100%}.media-frame-content .crop-content .upload-errors{position:absolute;width:300px;top:50%;right:50%;margin-right:-150px;margin-left:-150px;z-index:600000}.media-frame .media-iframe{overflow:hidden}.media-frame .media-iframe,.media-frame .media-iframe iframe{height:100%;width:100%;border:0}.media-frame select.attachment-filters{margin-top:11px;margin-left:2%;max-width:47%}.media-frame .search{margin-top:11px;padding:4px;font-size:13px;color:#464646;font-family:"Open Sans",sans-serif;-webkit-appearance:none}.media-toolbar-primary .search{max-width:100%}.attachments{margin:0;-webkit-overflow-scrolling:touch}.attachment{position:relative;float:right;padding:8px;margin:0;color:#464646;cursor:pointer;list-style:none;text-align:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;width:25%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.attachment.details:focus,.attachment:focus,.selected.attachment:focus{-webkit-box-shadow:inset 0 0 2px 3px #fff,inset 0 0 0 7px #5b9dd9;box-shadow:inset 0 0 2px 3px #fff,inset 0 0 0 7px #5b9dd9;outline:0}.selected.attachment{-webkit-box-shadow:inset 0 0 0 5px #fff,inset 0 0 0 7px #ccc;box-shadow:inset 0 0 0 5px #fff,inset 0 0 0 7px #ccc}.attachment.details{-webkit-box-shadow:inset 0 0 0 3px #fff,inset 0 0 0 7px #1e8cbe;box-shadow:inset 0 0 0 3px #fff,inset 0 0 0 7px #1e8cbe}.attachment-preview{position:relative;-webkit-box-shadow:inset 0 0 15px rgba(0,0,0,.1),inset 0 0 0 1px rgba(0,0,0,.05);box-shadow:inset 0 0 15px rgba(0,0,0,.1),inset 0 0 0 1px rgba(0,0,0,.05);background:#eee;cursor:pointer}.attachment-preview:before{content:'';display:block;padding-top:100%}.attachment .icon{margin:0 auto;overflow:hidden}.attachment .thumbnail{overflow:hidden;position:absolute;top:0;left:0;bottom:0;right:0;opacity:1;-webkit-transition:opacity .1s;transition:opacity .1s}.attachment .portrait img{max-width:100%}.attachment .landscape img{max-height:100%}.attachment .thumbnail:after{content:'';display:block;position:absolute;top:0;right:0;left:0;bottom:0;-webkit-box-shadow:inset 0 0 0 1px rgba(0,0,0,.1);box-shadow:inset 0 0 0 1px rgba(0,0,0,.1);overflow:hidden}.attachment .thumbnail img{top:0;left:0}.attachment .thumbnail .centered{position:absolute;top:0;left:0;width:100%;height:100%;-webkit-transform:translate(50%,50%);-ms-transform:translate(50%,50%);transform:translate(50%,50%)}.attachment .thumbnail .centered img{-webkit-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.attachment .thumbnail .centered img.icon{-webkit-transform:translate(-50%,-70%);-ms-transform:translate(-50%,-70%);transform:translate(-50%,-70%)}.ie8 .attachment img.icon{top:20%;position:relative}.attachment .filename{position:absolute;right:0;left:0;bottom:0;overflow:hidden;max-height:100%;word-wrap:break-word;text-align:center;font-weight:700;background:rgba(255,255,255,.8);-webkit-box-shadow:inset 0 0 0 1px rgba(0,0,0,.15);box-shadow:inset 0 0 0 1px rgba(0,0,0,.15)}.attachment .filename div{padding:5px 10px}.attachment .thumbnail img{position:absolute}.attachment .close{display:block;position:absolute;top:5px;left:5px;height:22px;width:22px;padding:0;font-size:20px;line-height:20px;text-align:center;text-decoration:none;color:#464646;background-color:#fff;background-position:-96px 4px;border-width:0;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.3);box-shadow:0 0 0 1px rgba(0,0,0,.3);-webkit-transition-duration:none;transition-duration:none;-webkit-transition-property:none;transition-property:none}.attachment a.close:focus,.attachment a.close:hover{-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.6);box-shadow:0 0 0 1px rgba(0,0,0,.6);background-position:-36px 4px}.attachment .check{display:none;height:24px;width:24px;position:absolute;z-index:10;top:0;left:0;outline:0;background:#eee;-webkit-box-shadow:0 0 0 1px #fff,0 0 0 2px rgba(0,0,0,.15);box-shadow:0 0 0 1px #fff,0 0 0 2px rgba(0,0,0,.15)}.attachment .check div{background-position:-1px 0;height:15px;width:15px;margin:5px}.attachment .check:hover div{background-position:-40px 0}.attachment.selected .check{display:block}.attachment.details .check,.attachment.selected .check:focus,.media-frame.mode-grid .attachment.selected .check{background-color:#1e8cbe;-webkit-box-shadow:0 0 0 1px #fff,0 0 0 2px #1e8cbe;box-shadow:0 0 0 1px #fff,0 0 0 2px #1e8cbe}.attachment.details .check div,.media-frame.mode-grid .attachment.selected .check div{background-position:-21px 0}.attachment.details .check:hover div,.attachment.selected .check:focus div,.media-frame.mode-grid .attachment.selected .check:hover div{background-position:-60px 0}.media-frame .attachment .describe{position:relative;display:block;width:100%;margin:0;padding:8px;font-size:12px;-webkit-border-radius:0;border-radius:0}.media-frame .attachments-browser{position:relative;width:100%;height:100%;overflow:hidden}.attachments-browser .media-toolbar{left:300px;height:50px}.attachments-browser.hide-sidebar .media-toolbar{left:0}.attachments-browser .media-toolbar-primary>.media-button,.attachments-browser .media-toolbar-primary>.media-button-group,.attachments-browser .media-toolbar-secondary>.media-button,.attachments-browser .media-toolbar-secondary>.media-button-group{margin:11px 0}.attachments-browser .attachments{padding:2px 8px 8px}.attachments-browser .attachments,.attachments-browser .uploader-inline{position:absolute;top:50px;right:0;left:300px;bottom:0;overflow:auto;outline:0}.attachments-browser .uploader-inline.hidden{display:none}.uploader-inline .close{background-color:transparent;border:0;cursor:pointer;height:48px;position:absolute;left:0;text-align:center;top:0;width:50px;z-index:1}.uploader-inline .close:before{font:400 30px/50px dashicons!important;color:#777;display:inline-block;content:'\f335';font-weight:300}.attachments-browser.hide-sidebar .attachments,.attachments-browser.hide-sidebar .uploader-inline{left:0;margin-left:0}.attachments-browser .instructions{display:inline-block;margin-top:16px;line-height:18px;font-size:13px;color:#666;margin-left:.5em}.attachments-browser .no-media{padding:2em 2em 0 0}.media-progress-bar{position:relative;height:10px;width:70%;margin:10px auto;-webkit-border-radius:10px;border-radius:10px;background:#dfdfdf;background:rgba(0,0,0,.1)}.media-progress-bar div{height:10px;min-width:20px;width:0;background:#1e8cbe;-webkit-border-radius:10px;border-radius:10px;-webkit-transition:width 300ms;transition:width 300ms}.media-uploader-status .media-progress-bar{display:none;width:100%}.uploading.media-uploader-status .media-progress-bar{display:block}.attachment-preview .media-progress-bar{position:absolute;top:50%;right:15%;width:70%;margin:-5px 0 0}.media-uploader-status{position:relative;margin:0 auto;padding-bottom:10px;max-width:400px}.media-sidebar .media-uploader-status{border-bottom:1px solid #dfdfdf}.uploader-inline .media-uploader-status h3{display:none}.media-uploader-status .upload-details{display:none;font-size:12px;color:#666}.uploading.media-uploader-status .upload-details{display:block}.media-uploader-status .upload-detail-separator{padding:0 4px}.media-uploader-status .upload-count{color:#464646}.media-uploader-status .upload-dismiss-errors,.media-uploader-status .upload-errors{display:none}.errors.media-uploader-status .upload-dismiss-errors,.errors.media-uploader-status .upload-errors{display:block}.media-uploader-status .upload-dismiss-errors{text-decoration:none}.media-sidebar .media-uploader-status .upload-dismiss-errors{position:absolute;top:0;left:0}.upload-errors .upload-error{margin:8px auto 0;padding:8px;border:1px solid #c00;background:#ffebe8;-webkit-border-radius:3px;border-radius:3px}.upload-errors .upload-error-label{padding:2px 4px;margin-left:8px;font-weight:700;color:#fff;background:#e00;background:-webkit-gradient(linear,right top,right bottom,from(#e00),to(#a00)) #e00;background:-webkit-linear-gradient(top,#e00,#a00) #e00;background:linear-gradient(to bottom,#e00,#a00) #e00;-webkit-border-radius:3px;border-radius:3px}.upload-errors .upload-error-message{display:block;padding-top:8px;color:#b44;word-wrap:break-word}.uploader-window{position:fixed;top:0;right:0;left:0;bottom:0;background:rgba(0,86,132,.9);z-index:250000;display:none;text-align:center;opacity:0;-webkit-transition:opacity 250ms;transition:opacity 250ms}.uploader-window-content{position:absolute;top:10px;right:10px;left:10px;bottom:10px;border:1px dashed #fff}.uploader-window h3{margin:-.5em 0 0;position:absolute;top:50%;right:0;left:0;-webkit-transform:translateY(-50%);-ms-transform:translateY(-50%);transform:translateY(-50%);font-size:40px;color:#fff;padding:0}.uploader-window .media-progress-bar{margin-top:20px;max-width:300px;background:0 0;border-color:#fff;display:none}.uploader-window .media-progress-bar div{background:#fff}.uploading .uploader-window .media-progress-bar{display:block}.media-frame .uploader-inline{margin-bottom:20px;padding:0;text-align:center}.uploader-inline-content{position:absolute;top:30%;right:0;left:0}.uploader-inline-content .upload-ui{margin:2em 0}.uploader-inline-content .post-upload-ui{margin-bottom:2em}.uploader-inline .has-upload-message .upload-ui{margin:0 0 4em}.uploader-inline h3{font-size:20px;line-height:28px;font-weight:400;margin:0}.uploader-inline .has-upload-message .upload-instructions{font-size:14px;color:#464646;font-weight:400}.uploader-inline .drop-instructions{display:none}.supports-drag-drop .uploader-inline .drop-instructions{display:block}.uploader-inline p{font-size:12px;margin:.5em 0}.uploader-inline .media-progress-bar{display:none}.uploading.uploader-inline .media-progress-bar{display:block}.uploader-inline .browser{display:inline-block!important}.media-selection{position:absolute;top:0;right:0;left:350px;height:60px;padding:0 16px 0 0;overflow:hidden;white-space:nowrap}.media-selection .selection-info{display:inline-block;font-size:12px;height:60px;margin-left:10px;vertical-align:top}.media-selection.editing,.media-selection.empty,.media-selection.one .edit-selection{display:none}.media-selection .count{display:block;padding-top:12px;font-size:14px;line-height:20px;font-weight:700}.media-selection .selection-info a{display:block;float:right;padding:1px 8px;margin:1px -8px 1px 8px;line-height:16px;text-decoration:none;border-left:1px solid #dfdfdf;color:#21759B}.media-selection .selection-info a:hover{background:#21759B;color:#fff;border-color:transparent}.media-selection .selection-info a:last-child{border-left:0;margin-left:0}.media-selection .selection-info .clear-selection{color:red}.media-selection .selection-info .clear-selection:hover{background:red}.media-selection .selection-view{display:inline-block;vertical-align:top}.media-selection .attachments{display:inline-block;height:48px;margin:6px;padding:0;overflow:hidden;vertical-align:top}.media-selection .attachment{width:40px;padding:0;margin:4px;-webkit-box-shadow:none;box-shadow:none}.media-selection .attachment .thumbnail{top:0;left:0;bottom:0;right:0}.media-selection .attachment .icon{width:50%}.media-selection .attachment-preview{-webkit-box-shadow:none;box-shadow:none;background:0 0}.media-selection .attachment.selection.details .thumbnail{-webkit-box-shadow:0 0 0 1px #fff,0 0 0 3px #1e8cbe;box-shadow:0 0 0 1px #fff,0 0 0 3px #1e8cbe}.media-selection:after{content:'';display:block;position:absolute;top:0;left:0;bottom:0;width:25px;background-image:-webkit-gradient(linear,left top,right top,from(rgba(255,255,255,1)),to(rgba(255,255,255,0)));background-image:-webkit-linear-gradient(left,rgba(255,255,255,1),rgba(255,255,255,0));background-image:linear-gradient(to right,rgba(255,255,255,1),rgba(255,255,255,0))}.media-selection .attachment .filename{display:none}.media-frame .spinner{background:url(../images/spinner.gif) 0 0/20px 20px no-repeat;-webkit-background-size:20px 20px;display:none;opacity:.7;filter:alpha(opacity=70);width:20px;height:20px;margin:0}.media-toolbar .spinner{margin-top:14px}.attachment-details{position:relative;overflow:auto}.attachment-details .settings-save-status{float:left;text-transform:none;z-index:10}.attachment-details .settings-save-status .spinner{margin:0 5px}.attachment-details .settings-save-status .saved{float:left;display:none}.attachment-details.save-complete .settings-save-status .saved,.attachment-details.save-waiting .settings-save-status .spinner{display:block}.attachment-info{overflow:hidden;min-height:60px;margin-bottom:16px;line-height:18px;color:#666;border-bottom:1px solid #ddd;padding-bottom:11px}.attachment-info .filename{font-weight:700;color:#464646;word-wrap:break-word}.attachment-info .thumbnail{position:relative;float:right;max-width:120px;max-height:120px;margin-top:5px;margin-left:10px;margin-bottom:5px}.uploading .attachment-info .thumbnail{width:120px;height:80px;-webkit-box-shadow:inset 0 0 15px rgba(0,0,0,.1);box-shadow:inset 0 0 15px rgba(0,0,0,.1)}.uploading .attachment-info .media-progress-bar{margin-top:35px}.attachment-info .thumbnail-image:after{content:'';display:block;position:absolute;top:0;right:0;left:0;bottom:0;-webkit-box-shadow:inset 0 0 0 1px rgba(0,0,0,.15);box-shadow:inset 0 0 0 1px rgba(0,0,0,.15);overflow:hidden}.attachment-info .thumbnail img{display:block;max-width:120px;max-height:120px;margin:0 auto}.attachment-info .details{float:right;font-size:12px;max-width:100%}.attachment-info .delete-attachment,.attachment-info .edit-attachment,.attachment-info .refresh-attachment,.attachment-info .trash-attachment,.attachment-info .untrash-attachment{display:block;text-decoration:none;white-space:nowrap}.attachment-details.needs-refresh .attachment-info .edit-attachment,.attachment-info .refresh-attachment{display:none}.attachment-details.needs-refresh .attachment-info .refresh-attachment,.attachment-info .edit-attachment{display:block}.media-modal .delete-attachment,.media-modal .trash-attachment,.media-modal .untrash-attachment{color:#bc0b0b}.media-modal .delete-attachment:hover,.media-modal .trash-attachment:hover,.media-modal .untrash-attachment:hover{color:red}.attachment-display-settings{width:100%;float:right;overflow:hidden}.attachment-display-settings h4{margin:1.4em 0 .4em}.collection-settings{overflow:hidden}.collection-settings .setting input[type=checkbox]{float:right;margin-left:8px}.collection-settings .setting span{min-width:inherit}.media-modal .imgedit-wrap{position:static}.media-modal .imgedit-wait{height:auto!important;left:0;bottom:0;right:0}.media-modal .imgedit-wrap .imgedit-panel-content{padding:16px;position:absolute;top:0;left:282px;bottom:0;right:0;overflow:auto}.media-modal .imgedit-wrap .imgedit-settings{background:#f3f3f3;border-right:1px solid #ddd;padding:0 16px 16px;position:absolute;top:0;left:0;bottom:0;width:250px;overflow:auto}.media-modal .imgedit-group{background:0 0;border:none;border-bottom:1px solid #ddd;-webkit-box-shadow:none;box-shadow:none;margin:0 0 16px;padding:0 0 16px;position:relative}.media-modal .imgedit-group:last-of-type{border:none;margin:0;padding:0}.media-modal .imgedit-group-top h3{text-transform:uppercase;font-size:12px;color:#666;margin:24px 0 0}.media-modal .imgedit-group-top h3 a{text-decoration:none;color:#666}.media-modal .imgedit-help-toggle{margin-top:-2px;cursor:pointer;color:#666}.media-modal .imgedit-help-toggled span.dashicons:before{content:'\f142'}.media-modal .imgedit-group img{margin-top:5px}.media-modal .imgedit-wrap div.updated{margin:0 0 16px}.embed-url{display:block;position:relative;padding:16px;margin:0;z-index:250;background:#fff;font-size:18px}.media-frame .embed-url input{font-size:18px;padding:12px 14px;width:100%;min-width:200px;-webkit-box-shadow:inset 2px 2px 4px -2px rgba(0,0,0,.1);box-shadow:inset 2px 2px 4px -2px rgba(0,0,0,.1)}.media-frame .embed-url .spinner{position:absolute;top:32px;left:26px}.media-frame .embed-loading .embed-url .spinner{display:block}.embed-link-settings,.embed-media-settings{position:absolute;top:70px;right:0;left:0;bottom:0;padding:16px 16px 32px;overflow:auto}.embed-preview embed,.embed-preview iframe,.embed-preview img{max-width:100%}.embed-preview img{height:auto}.image-details .media-modal{right:140px;left:140px}.image-details .media-frame-content,.image-details .media-frame-router,.image-details .media-frame-title{right:0}.image-details .embed-media-settings{top:0;overflow:visible;padding:0}.image-details .embed-media-settings,.image-details .embed-media-settings div{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.image-details .column-settings{background:#f3f3f3;border-left:1px solid #ddd;min-height:100%;width:55%;position:absolute;top:0;right:0}.image-details .column-settings h3{margin:20px;padding-top:20px;border-top:1px solid #ddd}.image-details .column-image{width:45%;position:absolute;right:55%;top:0}.image-details .image{margin:20px}.image-details .image img{max-width:100%;max-height:500px}.image-details .advanced-toggle{color:#666;text-decoration:none;display:block}.image-details .advanced-toggle:after{font:400 20px/1 dashicons;speak:none;vertical-align:top;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f140';display:inline-block;margin-top:-2px}.image-details .advanced-visible .advanced-toggle:after{content:'\f142'}.image-details .embed-media-settings .size{margin-bottom:4px}.image-details .custom-size span{display:block}.image-details .custom-size label{display:block;float:right}.image-details .custom-size span small{color:#999;font-size:inherit}.image-details .custom-size input{width:5em}.image-details .custom-size .sep{float:right;margin:26px 6px 0}.image-details .custom-size:after{content:'';display:table;clear:both}.media-embed .thumbnail{max-width:100%;max-height:200px;position:relative;float:right}.media-embed .thumbnail img{max-height:200px;display:block}.media-embed .thumbnail:after{content:'';display:block;position:absolute;top:0;right:0;left:0;bottom:0;-webkit-box-shadow:inset 0 0 0 1px rgba(0,0,0,.1);box-shadow:inset 0 0 0 1px rgba(0,0,0,.1);overflow:hidden}.media-embed .setting{width:100%;margin:10px 0;float:right;display:block;clear:both}.image-details .embed-media-settings .setting{float:none;width:auto}.image-details .actions{margin:10px 0}.image-details .hidden{display:none}.media-embed .setting input[type=text],.media-embed .setting textarea{display:block;width:100%;max-width:400px;margin:1px 0}.image-details .embed-media-settings .setting input[type=text],.image-details .embed-media-settings .setting textarea{max-width:inherit;width:70%}.image-details .embed-media-settings .custom-size,.image-details .embed-media-settings .link-target,.image-details .embed-media-settings .setting input.link-to-custom{margin-right:27%;width:70%}.image-details .embed-media-settings .link-target{margin-top:24px}.media-embed .setting input.hidden,.media-embed .setting textarea.hidden{display:none}.media-embed .setting span{display:block;width:200px;font-size:13px;line-height:24px;color:#666}.image-details .embed-media-settings .setting span{float:right;width:25%;text-align:left;margin:8px 1% 0;line-height:1.1}.media-embed .setting .button-group{margin:2px 0}.media-embed-sidebar{position:absolute;top:0;right:440px}.advanced-section,.link-settings{margin-top:10px}#wp-fullscreen-body .uploader-editor,.wp-editor-wrap .uploader-editor{background:rgba(150,150,150,.9);position:absolute;top:0;right:0;width:100%;height:100%;z-index:99998;display:none;text-align:center}#wp-fullscreen-body .uploader-editor{background:rgba(0,86,132,.9);position:fixed;z-index:100050}.wp-editor-wrap.wp-fullscreen-wrap .uploader-editor{opacity:0}#wp-fullscreen-body .uploader-editor-content,.wp-editor-wrap .uploader-editor-content{border:1px dashed #fff;position:absolute;top:10px;right:10px;left:10px;bottom:10px}#wp-fullscreen-body .uploader-editor .uploader-editor-title,.wp-editor-wrap .uploader-editor .uploader-editor-title{position:absolute;top:50%;right:0;left:0;-webkit-transform:translateY(-50%);-ms-transform:translateY(-50%);transform:translateY(-50%);font-size:3em;line-height:1.3;font-weight:700;color:#fff;padding:0;margin:0;display:none}.wp-editor-wrap .uploader-editor.droppable{background:rgba(0,86,132,.9)}#wp-fullscreen-body .uploader-editor .uploader-editor-title,.wp-editor-wrap .uploader-editor.droppable .uploader-editor-title{display:block}.ie7 .media-frame .attachments-browser{position:static}.ie7 .media-frame .embed-url input{margin-top:4px;width:90%}.ie7 .compat-item{width:99%}.ie7 .attachment-display-settings{width:auto}.ie7 .attachment-preview,.ie7 .attachment-preview .thumbnail{width:120px;height:120px}.ie7 .media-frame .attachment .describe{width:102px}.ie7 .media-sidebar .setting select{max-width:55%}.ie7 .media-sidebar .setting input[type=email],.ie7 .media-sidebar .setting input[type=number],.ie7 .media-sidebar .setting input[type=password],.ie7 .media-sidebar .setting input[type=search],.ie7 .media-sidebar .setting input[type=tel],.ie7 .media-sidebar .setting input[type=text],.ie7 .media-sidebar .setting input[type=url],.ie7 .media-sidebar .setting textarea{width:55%}.ie7 .media-sidebar .setting .link-to-custom{float:right}.rtl .media-frame,.rtl .media-frame .search,.rtl .media-frame input[type=email],.rtl .media-frame input[type=number],.rtl .media-frame input[type=password],.rtl .media-frame input[type=search],.rtl .media-frame input[type=tel],.rtl .media-frame input[type=text],.rtl .media-frame input[type=url],.rtl .media-frame select,.rtl .media-frame textarea,.rtl .media-modal{font-family:Tahoma,sans-serif}:lang(he-il) .rtl .media-frame,:lang(he-il) .rtl .media-frame .search,:lang(he-il) .rtl .media-frame input[type=email],:lang(he-il) .rtl .media-frame input[type=number],:lang(he-il) .rtl .media-frame input[type=password],:lang(he-il) .rtl .media-frame input[type=search],:lang(he-il) .rtl .media-frame input[type=text],:lang(he-il) .rtl .media-frame input[type=url],:lang(he-il) .rtl .media-frame select,:lang(he-il) .rtl .media-frame textarea,:lang(he-il) .rtl .media-modal{font-family:Arial,sans-serif}@media only screen and (max-width:900px){.media-frame:not(.hide-menu) .media-frame-content,.media-frame:not(.hide-menu) .media-frame-router,.media-frame:not(.hide-menu) .media-frame-title,.media-frame:not(.hide-menu) .media-frame-toolbar{right:0}.media-frame:not(.hide-menu) .media-frame-menu{position:static;width:0}.media-frame:not(.hide-menu) .media-menu{width:auto;max-width:80%;overflow:auto;z-index:2000;top:50px;right:-300px;left:auto;bottom:auto;padding:5px 0;border:1px solid #ccc}.media-frame:not(.hide-menu) .media-menu.visible{right:0}.media-frame:not(.hide-menu) .media-menu>a{padding:12px 16px;font-size:16px}.media-frame:not(.hide-menu) .media-menu>a.active{display:none}.media-frame:not(.hide-menu) .media-menu .separator{margin:5px 10px}.media-frame:not(.hide-menu) .media-frame-title{right:0;color:#21759b}.media-frame:not(.hide-menu) .media-frame-title .dashicons{display:inline-block;line-height:50px}.media-frame:not(.hide-menu) .media-frame-title h1{line-height:3;font-size:18px;float:right;cursor:pointer}.media-sidebar{width:230px}.attachments-browser .attachments,.attachments-browser .media-toolbar,.attachments-browser .uploader-inline{left:262px}.attachment-details .setting,.media-sidebar .setting{margin:6px 0}.attachment-details .setting input,.attachment-details .setting span,.attachment-details .setting textarea,.compat-item label span,.media-sidebar .setting input,.media-sidebar .setting span,.media-sidebar .setting textarea{float:none}.attachment-details .setting span,.compat-item label span,.media-sidebar .setting span{text-align:inherit;min-height:16px;margin:0;padding:8px 2px 0}.attachment-details .setting .value,.media-sidebar .setting .value{float:none;width:auto}.attachment-details .setting input[type=email],.attachment-details .setting input[type=number],.attachment-details .setting input[type=password],.attachment-details .setting input[type=search],.attachment-details .setting input[type=tel],.attachment-details .setting input[type=text],.attachment-details .setting input[type=url],.attachment-details .setting select,.attachment-details .setting textarea,.media-sidebar .setting input[type=email],.media-sidebar .setting input[type=number],.media-sidebar .setting input[type=password],.media-sidebar .setting input[type=search],.media-sidebar .setting input[type=tel],.media-sidebar .setting input[type=text],.media-sidebar .setting input[type=url],.media-sidebar .setting select,.media-sidebar .setting textarea{float:none;width:98%;max-width:none;height:auto}.attachment-details .setting select.columns,.media-sidebar .setting select.columns{width:auto}.media-frame .search,.media-frame input,.media-frame textarea{padding:3px 6px}.image-details .column-image{width:30%;right:70%}.image-details .column-settings{width:70%}.image-details .media-modal{right:30px;left:30px}.image-details .embed-media-settings .setting{margin:20px}.image-details .embed-media-settings .setting span{float:none;text-align:right;width:100%;margin-bottom:4px}.image-details .embed-media-settings .setting input.link-to-custom,.image-details .embed-media-settings .setting input[type=text],.image-details .embed-media-settings .setting textarea{width:100%;margin-right:0}.image-details .embed-media-settings .custom-size{margin-right:20px}.collection-settings .setting input[type=checkbox]{margin-top:0}.media-selection{min-width:120px}.media-selection:after{background:0 0}.media-selection .attachments{display:none}.media-modal .attachments-browser .media-toolbar .search{max-width:100%;height:auto;float:left}.media-modal .attachments-browser .media-toolbar .attachment-filters{height:auto}.media-modal .attachments-browser .media-toolbar .spinner{margin:14px 8px 0}.media-frame input[type=email],.media-frame input[type=number],.media-frame input[type=password],.media-frame input[type=search],.media-frame input[type=text],.media-frame input[type=url],.media-frame select,.media-frame textarea{font-size:16px}}@media only screen and (max-width:640px),screen and (max-height:400px){.image-details .media-modal,.media-modal{position:fixed;top:0;right:0;left:0;bottom:0}.media-modal-backdrop{position:fixed}.media-sidebar{z-index:1900;max-width:70%;bottom:120%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding-bottom:0}.media-sidebar.visible{bottom:0}.attachments-browser .attachments,.attachments-browser .media-toolbar,.attachments-browser .uploader-inline{left:0}.image-details .media-frame-title{display:block;top:0;font-size:14px}.image-details .column-image,.image-details .column-settings{width:100%;position:relative;right:0}.image-details .column-settings{padding:4px 0}.media-frame-content .media-toolbar .instructions{display:none}}@media screen and (max-height:400px){.media-menu{padding:0}.media-frame-router{top:44px}.media-frame-content{top:78px}.attachments-browser .attachments{top:40px}.embed-link-settings{overflow:visible}}@media only screen and (max-width:480px){.media-modal-close{top:5px;left:5px}.media-modal .media-frame-title{height:40px}.media-frame:not(.hide-menu) .media-frame-title h1,.media-modal .media-frame-title h1{font-size:18px;line-height:40px}.media-frame:not(.hide-menu) .media-frame-title .dashicons{line-height:40px}.media-frame-router,.media-frame:not(.hide-menu) .media-menu{top:40px}.media-frame-content{top:74px}.media-frame.hide-router .media-frame-content{top:40px}}@media print,(-o-min-device-pixel-ratio:5/4),(-webkit-min-device-pixel-ratio:1.25),(min-resolution:120dpi){.media-modal-icon{background-image:url(../images/uploader-icons-2x.png);-webkit-background-size:134px 15px;background-size:134px 15px}.media-frame .spinner{background-image:url(../images/spinner-2x.gif)}}.media-frame-content[data-columns="1"] .attachment{width:100%}.media-frame-content[data-columns="2"] .attachment{width:50%}.media-frame-content[data-columns="3"] .attachment{width:33.33%}.media-frame-content[data-columns="4"] .attachment{width:25%}.media-frame-content[data-columns="5"] .attachment{width:20%}.media-frame-content[data-columns="6"] .attachment{width:16.66%}.media-frame-content[data-columns="7"] .attachment{width:14.28%}.media-frame-content[data-columns="8"] .attachment{width:12.5%}.media-frame-content[data-columns="9"] .attachment{width:11.11%}.media-frame-content[data-columns="10"] .attachment{width:10%}.media-frame-content[data-columns="11"] .attachment{width:9.09%}.media-frame-content[data-columns="12"] .attachment{width:8.33%} \ No newline at end of file diff --git a/wp-includes/css/media-views.css b/wp-includes/css/media-views.css new file mode 100644 index 0000000..ec69650 --- /dev/null +++ b/wp-includes/css/media-views.css @@ -0,0 +1,2555 @@ +/** + * Base Styles + */ +.media-modal * { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} + +.media-frame input, +.media-frame select, +.media-frame textarea { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.media-modal, +.media-frame { + font-family: "Open Sans", sans-serif; + font-size: 12px; +} + +.media-frame input, +.media-frame textarea { + padding: 6px 8px; +} + +.media-frame select, +.wp-admin .media-frame select { + line-height: 28px; + margin-top: 3px; +} + +.media-frame a { + border-bottom: none; + color: #21759b; +} + +.media-frame a:hover { + color: #d54e21; +} + +.media-frame a.button { + color: #333; +} + +.media-frame a.button:hover { + color: #222; +} + +.media-frame a.button-primary, +.media-frame a.button-primary:hover { + color: #fff; +} + +.media-frame input[type="text"], +.media-frame input[type="password"], +.media-frame input[type="number"], +.media-frame input[type="search"], +.media-frame input[type="email"], +.media-frame input[type="url"], +.media-frame textarea, +.media-frame select { + font-family: "Open Sans", sans-serif; + font-size: 12px; + border-width: 1px; + border-style: solid; + border-color: #dfdfdf; +} + +.media-frame input[type="text"]:focus, +.media-frame input[type="password"]:focus, +.media-frame input[type="number"]:focus, +.media-frame input[type="search"]:focus, +.media-frame input[type="email"]:focus, +.media-frame input[type="url"]:focus, +.media-frame textarea:focus, +.media-frame select:focus { + border-color: #5b9dd9; +} + +.media-frame select { + height: 24px; + padding: 2px; +} + +.media-frame input:disabled, +.media-frame textarea:disabled, +.media-frame input[readonly], +.media-frame textarea[readonly] { + background-color: #eee; +} + +.media-frame input[type="search"] { + -webkit-appearance: textfield; +} + +.media-frame :-moz-placeholder { + color: #a9a9a9; +} + +.media-frame .hidden { + display: none; +} + +/*! + * jQuery UI Draggable/Sortable 1.11.2 + * http://jqueryui.com + * + * Copyright 2014 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ +.ui-draggable-handle, +.ui-sortable-handle { + -ms-touch-action: none; + touch-action: none; +} + +/** + * Modal + */ +.media-modal { + position: fixed; + top: 30px; + left: 30px; + right: 30px; + bottom: 30px; + z-index: 160000; +} + +.wp-customizer .media-modal { + z-index: 560000; +} + +.media-modal-backdrop { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + min-height: 360px; + background: #000; + opacity: 0.7; + z-index: 159900; +} + +.wp-customizer .media-modal-backdrop { + z-index: 559900; +} + +.media-modal-close { + position: absolute; + text-decoration: none; + top: 10px; + right: 10px; + width: 30px; + height: 30px; + z-index: 1000; + -webkit-transition: color .1s ease-in-out, background .1s ease-in-out; + transition: color .1s ease-in-out, background .1s ease-in-out; +} + +.media-modal-close:active { + -webkit-box-shadow: none; + box-shadow: none; +} + +.media-modal-close span.media-modal-icon { + display: block; + margin-top: 5px; + width: 30px; + height: 15px; + background-image: none; + text-align: center; +} + +.media-modal-close .media-modal-icon:before { + content: '\f158'; + font: normal 20px/1 'dashicons'; + speak: none; + vertical-align: middle; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + color: #666; +} + +.media-modal-close:hover .media-modal-icon:before { + color: #2ea2cc; +} + +.media-modal-close:active { + outline: 0; +} + +.media-modal-content { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + overflow: auto; + min-height: 300px; + -webkit-box-shadow: 0 5px 15px rgba(0,0,0,0.7); + box-shadow: 0 5px 15px rgba(0,0,0,0.7); + background: #fcfcfc; + -webkit-font-smoothing: subpixel-antialiased; +} + +.media-modal-icon { + background-image: url(../images/uploader-icons.png); + background-repeat: no-repeat; +} + +/** + * Toolbar + */ +.media-toolbar { + position: absolute; + top: 0; + left: 0; + right: 0; + z-index: 100; + height: 60px; + padding: 0 16px; + border: 0 solid #dfdfdf; + overflow: hidden; +} + +.media-toolbar-primary { + float: right; + height: 100%; + max-width: 33%; +} + +.media-toolbar-secondary { + float: left; + height: 100%; + max-width: 66%; +} + +.media-toolbar-primary > .media-button, +.media-toolbar-primary > .media-button-group { + margin-left: 10px; + float: left; + margin-top: 15px; +} + +.media-toolbar-secondary > .media-button, +.media-toolbar-secondary > .media-button-group { + margin-right: 10px; + margin-top: 15px; +} + +/** + * Sidebar + */ +.media-sidebar { + position: absolute; + top: 0; + right: 0; + bottom: 0; + width: 267px; + padding: 0 16px 24px; + z-index: 75; + background: #f3f3f3; + border-left: 1px solid #ddd; + overflow: auto; + -webkit-overflow-scrolling: touch; +} + +.hide-toolbar .media-sidebar { + bottom: 0; +} + +.media-sidebar .sidebar-title { + font-size: 20px; + margin: 0; + padding: 12px 10px 10px; + line-height: 28px; +} + +.media-sidebar .sidebar-content { + padding: 0 10px; + margin-bottom: 130px; +} + +.media-sidebar .search { + display: block; + width: 100%; +} + +.media-sidebar h3, +.image-details h3 { + position: relative; + font-weight: bold; + text-transform: uppercase; + font-size: 12px; + color: #666; + margin: 24px 0 8px; +} + +.media-sidebar .setting, +.attachment-details .setting { + display: block; + float: left; + width: 100%; + margin: 1px 0; +} + +.media-sidebar .setting label, +.attachment-details .setting label { + display: block; +} + +.media-sidebar .setting .link-to-custom, +.attachment-details .setting .link-to-custom { + margin: 3px 2px 0; +} + +.media-sidebar .setting span, +.attachment-details .setting span { + min-width: 30%; + margin-right: 4%; + font-size: 12px; + text-align: right; + word-wrap: break-word; +} + +.media-sidebar .setting .name { + max-width: 80px; +} + +.media-sidebar .setting select, +.attachment-details .setting select { + max-width: 65%; +} + +.media-sidebar .setting input[type="checkbox"], +.media-sidebar .field input[type="checkbox"], +.media-sidebar .setting input[type="radio"], +.media-sidebar .field input[type="radio"], +.attachment-details .setting input[type="checkbox"], +.attachment-details .field input[type="checkbox"], +.attachment-details .setting input[type="radio"], +.attachment-details .field input[type="radio"] { + float: none; + margin: 8px 3px 0; + padding: 0; +} + +.media-sidebar .setting span, +.attachment-details .setting span, +.compat-item label span { + float: left; + min-height: 22px; + padding-top: 8px; + line-height: 16px; + font-weight: normal; + color: #666; +} + +.compat-item label span { + text-align: right; +} + +.media-sidebar .setting input[type="text"], +.media-sidebar .setting input[type="password"], +.media-sidebar .setting input[type="email"], +.media-sidebar .setting input[type="number"], +.media-sidebar .setting input[type="search"], +.media-sidebar .setting input[type="tel"], +.media-sidebar .setting input[type="url"], +.media-sidebar .setting textarea, +.media-sidebar .setting .value, +.attachment-details .setting input[type="text"], +.attachment-details .setting input[type="password"], +.attachment-details .setting input[type="email"], +.attachment-details .setting input[type="number"], +.attachment-details .setting input[type="search"], +.attachment-details .setting input[type="tel"], +.attachment-details .setting input[type="url"], +.attachment-details .setting textarea, +.attachment-details .setting .value { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + margin: 1px; + width: 65%; + float: right; +} + +.media-sidebar .setting .value, +.attachment-details .setting .value { + margin: 0 1px; + text-align: left; +} + +.media-sidebar .setting textarea, +.attachment-details .setting textarea, +.compat-item .field textarea { + height: 62px; + resize: vertical; +} + +.media-sidebar select, +.attachment-details select { + margin-top: 3px; +} + +.compat-item { + float: left; + width: 100%; + overflow: hidden; +} + +.compat-item table { + width: 100%; + table-layout: fixed; + border-spacing: 0; + border: 0; +} + +.compat-item tr { + padding: 2px 0; + display: block; + overflow: hidden; +} + +.compat-item .label, +.compat-item .field { + display: block; + margin: 0; + padding: 0; +} + +.compat-item .label { + min-width: 30%; + margin-right: 4%; + float: left; + text-align: right; +} + +.compat-item .label span { + display: block; + width: 100%; +} + +.compat-item .field { + float: right; + width: 66%; +} + +.compat-item .field input[type="text"], +.compat-item .field input[type="password"], +.compat-item .field input[type="email"], +.compat-item .field input[type="number"], +.compat-item .field input[type="search"], +.compat-item .field input[type="tel"], +.compat-item .field input[type="url"] { + width: 100%; + margin: 0; +} + +.sidebar-for-errors .attachment-details, +.sidebar-for-errors .compat-item, +.sidebar-for-errors .media-sidebar .media-progress-bar, +.sidebar-for-errors .upload-details { + display: none !important; +} + +/** + * Menu + */ +.media-menu { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + margin: 0; + padding: 10px 0; + background: #f3f3f3; + border-right-width: 1px; + border-right-style: solid; + border-right-color: #ccc; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.media-menu > a { + display: block; + position: relative; + padding: 8px 20px; + margin: 0; + line-height: 18px; + font-size: 14px; + color: #0074a2; + text-decoration: none; +} + +.media-menu > a:hover { + color: #21759B; + background: rgba( 0, 0, 0, 0.04 ); +} + +.media-menu > a:active { + outline: none; +} + +.media-menu .active, +.media-menu .active:hover { + color: #222; + font-weight: bold; +} + +.media-menu .separator { + height: 0; + margin: 12px 20px; + padding: 0; + border-top: 1px solid #ddd; +} + +/** + * Menu + */ +.media-router { + position: relative; + padding: 0 6px; + margin: 0; + clear: both; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.media-router a { + -webkit-transition: none; + transition: none; +} + +.media-router > a { + position: relative; + float: left; + padding: 8px 10px 9px; + margin: 0; + height: 18px; + line-height: 18px; + font-size: 14px; + text-decoration: none; +} + +.media-router > a:last-child { + border-right: 0; +} + +.media-router > a:active { + outline: none; +} + +.media-router .active, +.media-router .active:hover { + color: #333; +} + +.media-router .active, +.media-router > a.active:last-child { + margin: -1px -1px 0; + background: #fff; + border: 1px solid #ddd; + border-bottom: none; +} + +.media-router .active:after { + display: none; +} + +/** + * Frame + */ +.media-frame { + overflow: hidden; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; +} + +.media-frame-menu { + position: absolute; + top: 0; + left: 0; + bottom: 0; + width: 200px; + z-index: 150; +} + +.media-frame-title { + position: absolute; + top: 0; + left: 200px; + right: 0; + height: 50px; + z-index: 200; +} + +.media-frame-router { + position: absolute; + top: 50px; + left: 200px; + right: 0; + height: 36px; + z-index: 200; +} + +.media-frame-content { + position: absolute; + top: 84px; + left: 200px; + right: 0; + bottom: 61px; + height: auto; + width: auto; + margin: 0; + overflow: auto; + background: #fff; + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; +} + +.media-frame-toolbar { + position: absolute; + left: 200px; + right: 0; + bottom: 0; + height: 60px; + z-index: 100; +} + +.media-frame.hide-menu .media-frame-title, +.media-frame.hide-menu .media-frame-router, +.media-frame.hide-menu .media-frame-toolbar, +.media-frame.hide-menu .media-frame-content { + left: 0; +} + +.media-frame.hide-menu .media-frame-menu { + left: -200px; +} + +.media-frame.hide-toolbar .media-frame-content { + bottom: 0; +} + +.media-frame.hide-toolbar .media-frame-toolbar { + bottom: -61px; +} + +.media-frame.hide-router .media-frame-content { + top: 50px; +} + +.media-frame.hide-router .media-frame-router { + display: none; +} + +.media-frame.hide-router .media-frame-title { + border-bottom: 1px solid #dfdfdf; + -webkit-box-shadow: 0 4px 4px -4px rgba( 0, 0, 0, 0.1 ); + box-shadow: 0 4px 4px -4px rgba( 0, 0, 0, 0.1 ); +} + +.media-frame-title .dashicons { + display: none; +} + +.media-frame-title h1 { + padding: 0 16px; + font-size: 22px; + line-height: 50px; + margin: 0; +} + +.media-frame-title .suggested-dimensions { + font-size: 14px; + float: right; + margin-right: 20px; +} + +.media-frame-content .crop-content { + height: 100%; +} + +.media-frame-content .crop-content .crop-image { + display: block; + margin: auto; + max-width: 100%; + max-height: 100%; +} + +.media-frame-content .crop-content .upload-errors +{ + position: absolute; + width: 300px; + top: 50%; + left: 50%; + margin-left: -150px; + margin-right: -150px; + z-index: 600000; +} + +/** + * Iframes + */ +.media-frame .media-iframe { + overflow: hidden; +} + +.media-frame .media-iframe, +.media-frame .media-iframe iframe { + height: 100%; + width: 100%; + border: 0; +} + +/** + * Attachment Browser Filters + */ +.media-frame select.attachment-filters { + margin-top: 11px; + margin-right: 2%; + max-width: 47%; +} + +/** + * Search + */ +.media-frame .search { + margin-top: 11px; + padding: 4px; + font-size: 13px; + color: #464646; + font-family: "Open Sans", sans-serif; + -webkit-appearance: none; +} + +.media-toolbar-primary .search { + max-width: 100%; +} + +/** + * Attachments + */ +.attachments { + margin: 0; + -webkit-overflow-scrolling: touch; +} + +/** + * Attachment + */ +.attachment { + position: relative; + float: left; + padding: 8px; + margin: 0; + color: #464646; + cursor: pointer; + list-style: none; + text-align: center; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + width: 25%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.attachment:focus, +.selected.attachment:focus, +.attachment.details:focus { + -webkit-box-shadow: + inset 0 0 2px 3px #fff, + inset 0 0 0 7px #5b9dd9; + box-shadow: + inset 0 0 2px 3px #fff, + inset 0 0 0 7px #5b9dd9; + outline: none; +} + +.selected.attachment { + -webkit-box-shadow: + inset 0 0 0 5px #fff, + inset 0 0 0 7px #ccc; + box-shadow: + inset 0 0 0 5px #fff, + inset 0 0 0 7px #ccc; +} + +.attachment.details { + -webkit-box-shadow: + inset 0 0 0 3px #fff, + inset 0 0 0 7px #1e8cbe; + box-shadow: + inset 0 0 0 3px #fff, + inset 0 0 0 7px #1e8cbe; +} + +.attachment-preview { + position: relative; + -webkit-box-shadow: + inset 0 0 15px rgba( 0, 0, 0, 0.1 ), + inset 0 0 0 1px rgba( 0, 0, 0, 0.05 ); + box-shadow: + inset 0 0 15px rgba( 0, 0, 0, 0.1 ), + inset 0 0 0 1px rgba( 0, 0, 0, 0.05 ); + background: #eee; + cursor: pointer; +} + +.attachment-preview:before { + content: ''; + display: block; + padding-top: 100%; +} + +.attachment .icon { + margin: 0 auto; + overflow: hidden; +} + +.attachment .thumbnail { + overflow: hidden; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + opacity: 1; + -webkit-transition: opacity .1s; + transition: opacity .1s; +} + +.attachment .portrait img { + max-width: 100%; +} + +.attachment .landscape img { + max-height: 100%; +} + +.attachment .thumbnail:after { + content: ''; + display: block; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + -webkit-box-shadow: inset 0 0 0 1px rgba( 0, 0, 0, 0.1 ); + box-shadow: inset 0 0 0 1px rgba( 0, 0, 0, 0.1 ); + overflow: hidden; +} + +/* @noflip */ +.attachment .thumbnail img { + top: 0; + left: 0; +} + +/* @noflip */ +.attachment .thumbnail .centered { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + -webkit-transform: translate( 50%, 50% ); + -ms-transform: translate(50%,50%); /* Fails with spaces?? Weird! */ + transform: translate( 50%, 50% ); +} + +.attachment .thumbnail .centered img { + -webkit-transform: translate( -50%, -50% ); + -ms-transform: translate(-50%,-50%); + transform: translate( -50%, -50% ); +} + +.attachment .thumbnail .centered img.icon { + -webkit-transform: translate( -50%, -70% ); + -ms-transform: translate(-50%,-70%); + transform: translate( -50%, -70% ); +} + +.ie8 .attachment img.icon { + top: 20%; + position: relative; +} + +.attachment .filename { + position: absolute; + left: 0; + right: 0; + bottom: 0; + overflow: hidden; + max-height: 100%; + word-wrap: break-word; + text-align: center; + font-weight: bold; + background: rgba( 255, 255, 255, 0.8 ); + -webkit-box-shadow: inset 0 0 0 1px rgba( 0, 0, 0, 0.15 ); + box-shadow: inset 0 0 0 1px rgba( 0, 0, 0, 0.15 ); +} + +.attachment .filename div { + padding: 5px 10px; +} + +.attachment .thumbnail img { + position: absolute; +} + +.attachment .close { + display: block; + position: absolute; + top: 5px; + right: 5px; + height: 22px; + width: 22px; + padding: 0; + font-size: 20px; + line-height: 20px; + text-align: center; + text-decoration: none; + color: #464646; + background-color: #fff; + background-position: -96px 4px; + border-width: 0; + -webkit-border-radius: 3px; + border-radius: 3px; + -webkit-box-shadow: 0 0 0 1px rgba( 0, 0, 0, 0.3 ); + box-shadow: 0 0 0 1px rgba( 0, 0, 0, 0.3 ); + -webkit-transition-duration: none; + transition-duration: none; + -webkit-transition-property: none; + transition-property: none; +} + +.attachment a.close:hover, +.attachment a.close:focus { + -webkit-box-shadow: 0 0 0 1px rgba( 0, 0, 0, 0.6 ); + box-shadow: 0 0 0 1px rgba( 0, 0, 0, 0.6 ); + background-position: -36px 4px; + +} + +.attachment .check { + display: none; + height: 24px; + width: 24px; + position: absolute; + z-index: 10; + top: 0; + right: 0; + outline: none; + background: #eee; + -webkit-box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba( 0, 0, 0, 0.15 ); + box-shadow: 0 0 0 1px #fff, 0 0 0 2px rgba( 0, 0, 0, 0.15 ); +} + +.attachment .check div { + background-position: -1px 0; + height: 15px; + width: 15px; + margin: 5px; +} + +.attachment .check:hover div { + background-position: -40px 0; +} + +.attachment.selected .check { + display: block; +} + +.attachment.details .check, +.attachment.selected .check:focus, +.media-frame.mode-grid .attachment.selected .check { + background-color: #1e8cbe; + -webkit-box-shadow: + 0 0 0 1px #fff, + 0 0 0 2px #1e8cbe; + box-shadow: + 0 0 0 1px #fff, + 0 0 0 2px #1e8cbe; +} + +.attachment.details .check div, +.media-frame.mode-grid .attachment.selected .check div { + background-position: -21px 0; +} + +.attachment.details .check:hover div, +.attachment.selected .check:focus div, +.media-frame.mode-grid .attachment.selected .check:hover div { + background-position: -60px 0; +} + +.media-frame .attachment .describe { + position: relative; + display: block; + width: 100%; + margin: 0; + padding: 8px; + font-size: 12px; + -webkit-border-radius: 0; + border-radius: 0; +} + +/** + * Attachments Browser + */ +.media-frame .attachments-browser { + position: relative; + width: 100%; + height: 100%; + overflow: hidden; +} + +.attachments-browser .media-toolbar { + right: 300px; + height: 50px; +} + +.attachments-browser.hide-sidebar .media-toolbar { + right: 0; +} + +.attachments-browser .media-toolbar-primary > .media-button, +.attachments-browser .media-toolbar-primary > .media-button-group, +.attachments-browser .media-toolbar-secondary > .media-button, +.attachments-browser .media-toolbar-secondary > .media-button-group { + margin: 11px 0; +} + +.attachments-browser .attachments { + padding: 2px 8px 8px; +} + +.attachments-browser .attachments, +.attachments-browser .uploader-inline { + position: absolute; + top: 50px; + left: 0; + right: 300px; + bottom: 0; + overflow: auto; + outline: none; +} + +.attachments-browser .uploader-inline.hidden { + display: none; +} + +.uploader-inline .close { + background-color: transparent; + border: 0; + cursor: pointer; + height: 48px; + position: absolute; + right: 0; + text-align: center; + top: 0; + width: 50px; + z-index: 1; +} + +.uploader-inline .close:before { + font: normal 30px/50px 'dashicons' !important; + color: #777; + display: inline-block; + content: '\f335'; + font-weight: 300; +} + +.attachments-browser.hide-sidebar .attachments, +.attachments-browser.hide-sidebar .uploader-inline { + right: 0; + margin-right: 0; +} + +.attachments-browser .instructions { + display: inline-block; + margin-top: 16px; + line-height: 18px; + font-size: 13px; + color: #666; + margin-right: 0.5em; +} + +.attachments-browser .no-media { + padding: 2em 0 0 2em; +} + +/** + * Progress Bar + */ +.media-progress-bar { + position: relative; + height: 10px; + width: 70%; + margin: 10px auto; + -webkit-border-radius: 10px; + border-radius: 10px; + background: #dfdfdf; + background: rgba( 0, 0, 0, 0.1 ); +} + +.media-progress-bar div { + height: 10px; + min-width: 20px; + width: 0; + background: #1e8cbe; + -webkit-border-radius: 10px; + border-radius: 10px; + -webkit-transition: width 300ms; + transition: width 300ms; +} + +.media-uploader-status .media-progress-bar { + display: none; + width: 100%; +} + +.uploading.media-uploader-status .media-progress-bar { + display: block; +} + +.attachment-preview .media-progress-bar { + position: absolute; + top: 50%; + left: 15%; + width: 70%; + margin: -5px 0 0 0; +} + +.media-uploader-status { + position: relative; + margin: 0 auto; + padding-bottom: 10px; + max-width: 400px; +} + +.media-sidebar .media-uploader-status { + border-bottom: 1px solid #dfdfdf; +} + +.uploader-inline .media-uploader-status h3 { + display: none; +} + +.media-uploader-status .upload-details { + display: none; + font-size: 12px; + color: #666; +} + +.uploading.media-uploader-status .upload-details { + display: block; +} + +.media-uploader-status .upload-detail-separator { + padding: 0 4px; +} + +.media-uploader-status .upload-count { + color: #464646; +} + +.media-uploader-status .upload-dismiss-errors, +.media-uploader-status .upload-errors { + display: none; +} + +.errors.media-uploader-status .upload-dismiss-errors, +.errors.media-uploader-status .upload-errors { + display: block; +} + +.media-uploader-status .upload-dismiss-errors { + text-decoration: none; +} + +.media-sidebar .media-uploader-status .upload-dismiss-errors { + position: absolute; + top: 0; + right: 0; +} + +.upload-errors .upload-error { + margin: 8px auto 0 auto; + padding: 8px; + border: 1px #c00 solid; + background: #ffebe8; + -webkit-border-radius: 3px; + border-radius: 3px; +} + +.upload-errors .upload-error-label { + padding: 2px 4px; + margin-right: 8px; + font-weight: bold; + color: #fff; + background: #e00; + background-image: -webkit-gradient(linear, left top, left bottom, from(#e00), to(#a00)); + background-image: -webkit-linear-gradient(top, #e00, #a00); + background-image: linear-gradient(to bottom, #e00, #a00); + -webkit-border-radius: 3px; + border-radius: 3px; +} + +.upload-errors .upload-error-message { + display: block; + padding-top: 8px; + color: #b44; + word-wrap: break-word; +} + +.uploader-window { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba( 0, 86, 132, 0.9 ); + z-index: 250000; + display: none; + text-align: center; + opacity: 0; + -webkit-transition: opacity 250ms; + transition: opacity 250ms; +} + +.uploader-window-content { + position: absolute; + top: 10px; + left: 10px; + right: 10px; + bottom: 10px; + border: 1px dashed #fff; +} + +.uploader-window h3 { + margin: -0.5em 0 0; + position: absolute; + top: 50%; + left: 0; + right: 0; + -webkit-transform: translateY( -50% ); + -ms-transform: translateY(-50%); + transform: translateY( -50% ); + font-size: 40px; + color: #fff; + padding: 0; +} + +.uploader-window .media-progress-bar { + margin-top: 20px; + max-width: 300px; + background: transparent; + border-color: #fff; + display: none; +} + +.uploader-window .media-progress-bar div { + background: #fff; +} + +.uploading .uploader-window .media-progress-bar { + display: block; +} + +.media-frame .uploader-inline { + margin-bottom: 20px; + padding: 0; + text-align: center; +} + +.uploader-inline-content { + position: absolute; + top: 30%; + left: 0; + right: 0; +} + +.uploader-inline-content .upload-ui { + margin: 2em 0; +} + +.uploader-inline-content .post-upload-ui { + margin-bottom: 2em; +} + +.uploader-inline .has-upload-message .upload-ui { + margin: 0 0 4em; +} + +.uploader-inline h3 { + font-size: 20px; + line-height: 28px; + font-weight: 400; + margin: 0; +} + +.uploader-inline .has-upload-message .upload-instructions { + font-size: 14px; + color: #464646; + font-weight: normal; +} + +.uploader-inline .drop-instructions { + display: none; +} + +.supports-drag-drop .uploader-inline .drop-instructions { + display: block; +} + +.uploader-inline p { + font-size: 12px; + margin: 0.5em 0; +} + +.uploader-inline .media-progress-bar { + display: none; +} + +.uploading.uploader-inline .media-progress-bar { + display: block; +} + +.uploader-inline .browser { + display: inline-block !important; +} + +/** + * Selection + */ +.media-selection { + position: absolute; + top: 0; + left: 0; + right: 350px; + height: 60px; + padding: 0 0 0 16px; + overflow: hidden; + white-space: nowrap; +} + +.media-selection .selection-info { + display: inline-block; + font-size: 12px; + height: 60px; + margin-right: 10px; + vertical-align: top; +} + +.media-selection.empty, +.media-selection.editing { + display: none; +} + +.media-selection.one .edit-selection { + display: none; +} + +.media-selection .count { + display: block; + padding-top: 12px; + font-size: 14px; + line-height: 20px; + font-weight: bold; +} + +.media-selection .selection-info a { + display: block; + float: left; + padding: 1px 8px; + margin: 1px 8px 1px -8px; + line-height: 16px; + text-decoration: none; + border-right: 1px solid #dfdfdf; + color: #21759B; +} + +.media-selection .selection-info a:hover { + background: #21759B; + color: #fff; + border-color: transparent; +} + +.media-selection .selection-info a:last-child { + border-right: 0; + margin-right: 0; +} + +.media-selection .selection-info .clear-selection { + color: red; +} + +.media-selection .selection-info .clear-selection:hover { + background: red; +} + +.media-selection .selection-view { + display: inline-block; + vertical-align: top; +} + +.media-selection .attachments { + display: inline-block; + height: 48px; + margin: 6px; + padding: 0; + overflow: hidden; + vertical-align: top; +} + +.media-selection .attachment { + width: 40px; + padding: 0; + margin: 4px; + -webkit-box-shadow: none; + box-shadow: none; +} + +.media-selection .attachment .thumbnail { + top: 0; + right: 0; + bottom: 0; + left: 0; +} + +.media-selection .attachment .icon { + width: 50%; +} + +.media-selection .attachment-preview { + -webkit-box-shadow: none; + box-shadow: none; + background: none; +} + +.media-selection .attachment.selection.details .thumbnail { + -webkit-box-shadow: + 0 0 0 1px #fff, + 0 0 0 3px #1e8cbe; + box-shadow: + 0 0 0 1px #fff, + 0 0 0 3px #1e8cbe; +} + +.media-selection:after { + content: ''; + display: block; + position: absolute; + top: 0; + right: 0; + bottom: 0; + width: 25px; + background-image: -webkit-gradient(linear, right top, left top, from(rgba( 255, 255, 255, 1 )), to(rgba( 255, 255, 255, 0 ))); + background-image: -webkit-linear-gradient(right, rgba( 255, 255, 255, 1 ), rgba( 255, 255, 255, 0 )); + background-image: linear-gradient(to left, rgba( 255, 255, 255, 1 ) , rgba( 255, 255, 255, 0 ) ); +} + +.media-selection .attachment .filename { + display: none; +} + +/** + * Spinner + */ +.media-frame .spinner { + background: url(../images/spinner.gif) no-repeat; + -webkit-background-size: 20px 20px; + background-size: 20px 20px; + display: none; + opacity: 0.7; + filter: alpha(opacity=70); + width: 20px; + height: 20px; + margin: 0; +} + +.media-toolbar .spinner { + margin-top: 14px; +} + +/** + * Attachment Details + */ +.attachment-details { + position: relative; + overflow: auto; +} + +.attachment-details .settings-save-status { + float: right; + text-transform: none; + z-index: 10; +} + +.attachment-details .settings-save-status .spinner { + margin: 0 5px 0; +} + +.attachment-details .settings-save-status .saved { + float: right; + display: none; +} + +.attachment-details.save-waiting .settings-save-status .spinner, +.attachment-details.save-complete .settings-save-status .saved { + display: block; +} + +.attachment-info { + overflow: hidden; + min-height: 60px; + margin-bottom: 16px; + line-height: 18px; + color: #666; + border-bottom: 1px solid #ddd; + padding-bottom: 11px; +} + +.attachment-info .filename { + font-weight: bold; + color: #464646; + word-wrap: break-word; +} + +.attachment-info .thumbnail { + position: relative; + float: left; + max-width: 120px; + max-height: 120px; + margin-top: 5px; + margin-right: 10px; + margin-bottom: 5px; +} + +.uploading .attachment-info .thumbnail { + width: 120px; + height: 80px; + -webkit-box-shadow: inset 0 0 15px rgba( 0, 0, 0, 0.1 ); + box-shadow: inset 0 0 15px rgba( 0, 0, 0, 0.1 ); +} + +.uploading .attachment-info .media-progress-bar { + margin-top: 35px; +} + +.attachment-info .thumbnail-image:after { + content: ''; + display: block; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + -webkit-box-shadow: inset 0 0 0 1px rgba( 0, 0, 0, 0.15 ); + box-shadow: inset 0 0 0 1px rgba( 0, 0, 0, 0.15 ); + overflow: hidden; +} + +.attachment-info .thumbnail img { + display: block; + max-width: 120px; + max-height: 120px; + margin: 0 auto; +} + +.attachment-info .details { + float: left; + font-size: 12px; + max-width: 100%; +} + +.attachment-info .edit-attachment, +.attachment-info .refresh-attachment, +.attachment-info .delete-attachment, +.attachment-info .trash-attachment, +.attachment-info .untrash-attachment { + display: block; + text-decoration: none; + white-space: nowrap; +} + +.attachment-info .refresh-attachment, +.attachment-details.needs-refresh .attachment-info .edit-attachment { + display: none; +} + +.attachment-details.needs-refresh .attachment-info .refresh-attachment, +.attachment-info .edit-attachment { + display: block; +} + +.media-modal .delete-attachment, +.media-modal .trash-attachment, +.media-modal .untrash-attachment { + color: #bc0b0b; +} + +.media-modal .delete-attachment:hover, +.media-modal .trash-attachment:hover, +.media-modal .untrash-attachment:hover { + color: red; +} + +/** + * Attachment Display Settings + */ +.attachment-display-settings { + width: 100%; + float: left; + overflow: hidden; +} + +.attachment-display-settings h4 { + margin: 1.4em 0 0.4em; +} + +.collection-settings { + overflow: hidden; +} + +.collection-settings .setting input[type="checkbox"] { + float: left; + margin-right: 8px; +} + +.collection-settings .setting span { + min-width: inherit; +} + +/** + * Image Editor + */ +.media-modal .imgedit-wrap { + position: static; +} + +.media-modal .imgedit-wait { + height: auto !important; + right: 0; + bottom: 0; + left: 0; +} + +.media-modal .imgedit-wrap .imgedit-panel-content { + padding: 16px; + position: absolute; + top: 0; + right: 282px; + bottom: 0; + left: 0; + overflow: auto; +} + +.media-modal .imgedit-wrap .imgedit-settings { + background: #f3f3f3; + border-left: 1px solid #ddd; + padding: 0 16px 16px; + position: absolute; + top: 0; + right: 0; + bottom: 0; + width: 250px; + overflow: auto; +} + +.media-modal .imgedit-group { + background: none; + border: none; + border-bottom: 1px solid #ddd; + -webkit-box-shadow: none; + box-shadow: none; + margin: 0; + margin-bottom: 16px; + padding: 0; + padding-bottom: 16px; + position: relative; /* RTL fix, #WP29352 */ +} + +.media-modal .imgedit-group:last-of-type { + border: none; + margin: 0; + padding: 0; +} + +.media-modal .imgedit-group-top h3 { + text-transform: uppercase; + font-size: 12px; + color: #666; + margin: 0; + margin-top: 24px; +} + +.media-modal .imgedit-group-top h3 a { + text-decoration: none; + color: #666; +} + +.media-modal .imgedit-help-toggle { + margin-top: -2px; + cursor: pointer; + color: #666; +} + +.media-modal .imgedit-help-toggled span.dashicons:before { + content: '\f142'; +} + +.media-modal .imgedit-group img { + margin-top: 5px; +} + +.media-modal .imgedit-wrap div.updated { + margin: 0; + margin-bottom: 16px; +} + + +/** + * Embed from URL and Image Details + */ +.embed-url { + display: block; + position: relative; + padding: 16px; + margin: 0; + z-index: 250; + background: #fff; + font-size: 18px; +} + +.media-frame .embed-url input { + font-size: 18px; + padding: 12px 14px; + width: 100%; + min-width: 200px; + -webkit-box-shadow: inset 2px 2px 4px -2px rgba( 0, 0, 0, 0.1 ); + box-shadow: inset 2px 2px 4px -2px rgba( 0, 0, 0, 0.1 ); +} + +.media-frame .embed-url .spinner { + position: absolute; + top: 32px; + right: 26px; +} + +.media-frame .embed-loading .embed-url .spinner { + display: block; +} + +.embed-link-settings, +.embed-media-settings { + position: absolute; + top: 70px; + left: 0; + right: 0; + bottom: 0; + padding: 16px 16px 32px; + overflow: auto; +} + +.embed-preview img, .embed-preview iframe, .embed-preview embed { + max-width: 100%; +} + +.embed-preview img { + height: auto; +} + +.image-details .media-modal { + left: 140px; + right: 140px; +} + +.image-details .media-frame-title, +.image-details .media-frame-content, +.image-details .media-frame-router { + left: 0; +} + +.image-details .embed-media-settings { + top: 0; + overflow: visible; + padding: 0; +} + +.image-details .embed-media-settings, +.image-details .embed-media-settings div { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.image-details .column-settings { + background: #f3f3f3; + border-right: 1px solid #ddd; + min-height: 100%; + width: 55%; + position: absolute; + top: 0; + left: 0; +} + +.image-details .column-settings h3 { + margin: 20px; + padding-top: 20px; + border-top: 1px solid #ddd; +} + +.image-details .column-image { + width: 45%; + position: absolute; + left: 55%; + top: 0; +} + +.image-details .image { + margin: 20px; +} + +.image-details .image img { + max-width: 100%; + max-height: 500px; +} + +.image-details .advanced-toggle { + color: #666; + text-decoration: none; + display: block; +} + +.image-details .advanced-toggle:after { + font: normal 20px/1 'dashicons'; + speak: none; + vertical-align: top; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + content: '\f140'; + display: inline-block; + margin-top: -2px; +} + +.image-details .advanced-visible .advanced-toggle:after { + content: '\f142'; +} + +.image-details .embed-media-settings .size { + margin-bottom: 4px; +} + +.image-details .custom-size span { + display: block; +} + +.image-details .custom-size label { + display: block; + float: left; +} + +.image-details .custom-size span small { + color: #999; + font-size: inherit; +} + +.image-details .custom-size input { + width: 5em; +} + +.image-details .custom-size .sep { + float: left; + margin: 26px 6px 0 6px; +} + +.image-details .custom-size:after { + content: ''; + display: table; + clear: both; +} + +.media-embed .thumbnail { + max-width: 100%; + max-height: 200px; + position: relative; + float: left; +} + +.media-embed .thumbnail img { + max-height: 200px; + display: block; +} + +.media-embed .thumbnail:after { + content: ''; + display: block; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + -webkit-box-shadow: inset 0 0 0 1px rgba( 0, 0, 0, 0.1 ); + box-shadow: inset 0 0 0 1px rgba( 0, 0, 0, 0.1 ); + overflow: hidden; +} + +.media-embed .setting { + width: 100%; + margin: 10px 0; + float: left; + display: block; + clear: both; +} + +.image-details .embed-media-settings .setting { + float: none; + width: auto; +} + +.image-details .actions { + margin: 10px 0; +} + +.image-details .hidden { + display: none; +} + +.media-embed .setting input[type="text"], +.media-embed .setting textarea { + display: block; + width: 100%; + max-width: 400px; + margin: 1px 0; +} + +.image-details .embed-media-settings .setting input[type="text"], +.image-details .embed-media-settings .setting textarea { + max-width: inherit; + width: 70%; +} + +.image-details .embed-media-settings .setting input.link-to-custom, +.image-details .embed-media-settings .link-target, +.image-details .embed-media-settings .custom-size { + margin-left: 27%; + width: 70%; +} + +.image-details .embed-media-settings .link-target { + margin-top: 24px; +} + +.media-embed .setting input.hidden, +.media-embed .setting textarea.hidden { + display: none; +} + +.media-embed .setting span { + display: block; + width: 200px; + font-size: 13px; + line-height: 24px; + color: #666; +} + +.image-details .embed-media-settings .setting span { + float: left; + width: 25%; + text-align: right; + margin: 8px 1% 0 1%; + line-height: 1.1; +} + +.media-embed .setting .button-group { + margin: 2px 0; +} + +.media-embed-sidebar { + position: absolute; + top: 0; + left: 440px; +} + +.advanced-section, +.link-settings { + margin-top: 10px; +} + +/* Drag & drop on the editor upload */ +#wp-fullscreen-body .uploader-editor, +.wp-editor-wrap .uploader-editor { + background: rgba( 150, 150, 150, 0.9 ); + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 99998; /* under the toolbar */ + display: none; + text-align: center; +} + +#wp-fullscreen-body .uploader-editor { + background: rgba( 0, 86, 132, 0.9 ); + position: fixed; + z-index: 100050; /* above the editor toolbar */ +} + +.wp-editor-wrap.wp-fullscreen-wrap .uploader-editor { + opacity: 0; +} + +#wp-fullscreen-body .uploader-editor-content, +.wp-editor-wrap .uploader-editor-content { + border: 1px dashed #fff; + position: absolute; + top: 10px; + left: 10px; + right: 10px; + bottom: 10px; +} + +#wp-fullscreen-body .uploader-editor .uploader-editor-title, +.wp-editor-wrap .uploader-editor .uploader-editor-title { + position: absolute; + top: 50%; + left: 0; + right: 0; + -webkit-transform: translateY( -50% ); + -ms-transform: translateY(-50%); + transform: translateY( -50% ); + font-size: 3em; + line-height: 1.3; + font-weight: bold; + color: #fff; + padding: 0; + margin: 0; + display: none; +} + +.wp-editor-wrap .uploader-editor.droppable { + background: rgba( 0, 86, 132, 0.9 ); +} + +#wp-fullscreen-body .uploader-editor .uploader-editor-title, +.wp-editor-wrap .uploader-editor.droppable .uploader-editor-title { + display: block; +} + +/** + * IE7 Fixes + */ +.ie7 .media-frame .attachments-browser { + position: static; +} + +.ie7 .media-frame .embed-url input { + margin-top: 4px; + width: 90%; +} + +.ie7 .compat-item { + width: 99%; +} + +.ie7 .attachment-display-settings { + width: auto; +} + +.ie7 .attachment-preview, +.ie7 .attachment-preview .thumbnail { + width: 120px; + height: 120px; +} + +.ie7 .media-frame .attachment .describe { + width: 102px; +} + +.ie7 .media-sidebar .setting select { + max-width: 55%; +} + +.ie7 .media-sidebar .setting input[type="text"], +.ie7 .media-sidebar .setting input[type="password"], +.ie7 .media-sidebar .setting input[type="email"], +.ie7 .media-sidebar .setting input[type="number"], +.ie7 .media-sidebar .setting input[type="search"], +.ie7 .media-sidebar .setting input[type="tel"], +.ie7 .media-sidebar .setting input[type="url"], +.ie7 .media-sidebar .setting textarea { + width: 55%; +} + +.ie7 .media-sidebar .setting .link-to-custom { + float: left; +} + +/** + * Localization + */ +.rtl .media-modal, +.rtl .media-frame, +.rtl .media-frame .search, +.rtl .media-frame input[type="text"], +.rtl .media-frame input[type="password"], +.rtl .media-frame input[type="number"], +.rtl .media-frame input[type="search"], +.rtl .media-frame input[type="email"], +.rtl .media-frame input[type="url"], +.rtl .media-frame input[type="tel"], +.rtl .media-frame textarea, +.rtl .media-frame select { + font-family: Tahoma, sans-serif; +} + +:lang(he-il) .rtl .media-modal, +:lang(he-il) .rtl .media-frame, +:lang(he-il) .rtl .media-frame .search, +:lang(he-il) .rtl .media-frame input[type="text"], +:lang(he-il) .rtl .media-frame input[type="password"], +:lang(he-il) .rtl .media-frame input[type="number"], +:lang(he-il) .rtl .media-frame input[type="search"], +:lang(he-il) .rtl .media-frame input[type="email"], +:lang(he-il) .rtl .media-frame input[type="url"], +:lang(he-il) .rtl .media-frame textarea, +:lang(he-il) .rtl .media-frame select { + font-family: Arial, sans-serif; +} + +/** + * Responsive layout + */ +@media only screen and (max-width: 900px) { + + /* Drop-down menu */ + .media-frame:not(.hide-menu) .media-frame-title, + .media-frame:not(.hide-menu) .media-frame-router, + .media-frame:not(.hide-menu) .media-frame-content, + .media-frame:not(.hide-menu) .media-frame-toolbar { + left: 0; + } + + .media-frame:not(.hide-menu) .media-frame-menu { + position: static; + width: 0; + } + + .media-frame:not(.hide-menu) .media-menu { + width: auto; + max-width: 80%; + overflow: auto; + z-index: 2000; + top: 50px; + left: -300px; + right: auto; + bottom: auto; + padding: 5px 0; + border: 1px solid #ccc; + } + + .media-frame:not(.hide-menu) .media-menu.visible { + left: 0; + } + + .media-frame:not(.hide-menu) .media-menu > a { + padding: 12px 16px; + font-size: 16px; + } + + .media-frame:not(.hide-menu) .media-menu > a.active { + display: none; + } + + .media-frame:not(.hide-menu) .media-menu .separator { + margin: 5px 10px; + } + + .media-frame:not(.hide-menu) .media-frame-title { + left: 0; + color: #21759b; + } + + .media-frame:not(.hide-menu) .media-frame-title .dashicons { + display: inline-block; + line-height: 50px; + } + + .media-frame:not(.hide-menu) .media-frame-title h1 { + line-height: 3; + font-size: 18px; + float: left; + cursor: pointer; + } + /* End drop-down menu */ + + .media-sidebar { + width: 230px; + } + + .attachments-browser .attachments, + .attachments-browser .uploader-inline, + .attachments-browser .media-toolbar { + right: 262px; + } + + .media-sidebar .setting, + .attachment-details .setting { + margin: 6px 0px; + } + + .media-sidebar .setting input, + .media-sidebar .setting textarea, + .media-sidebar .setting span, + .attachment-details .setting input, + .attachment-details .setting textarea, + .attachment-details .setting span, + .compat-item label span { + float: none; + } + + .media-sidebar .setting span, + .attachment-details .setting span, + .compat-item label span { + text-align: inherit; + min-height: 16px; + margin: 0; + padding: 8px 2px 0; + } + + .media-sidebar .setting .value, + .attachment-details .setting .value { + float: none; + width: auto; + } + + .media-sidebar .setting input[type="text"], + .media-sidebar .setting input[type="password"], + .media-sidebar .setting input[type="email"], + .media-sidebar .setting input[type="number"], + .media-sidebar .setting input[type="search"], + .media-sidebar .setting input[type="tel"], + .media-sidebar .setting input[type="url"], + .media-sidebar .setting textarea, + .media-sidebar .setting select, + .attachment-details .setting input[type="text"], + .attachment-details .setting input[type="password"], + .attachment-details .setting input[type="email"], + .attachment-details .setting input[type="number"], + .attachment-details .setting input[type="search"], + .attachment-details .setting input[type="tel"], + .attachment-details .setting input[type="url"], + .attachment-details .setting textarea, + .attachment-details .setting select { + float: none; + width: 98%; + max-width: none; + height: auto; + } + + .media-sidebar .setting select.columns, + .attachment-details .setting select.columns { + width: auto; + } + + .media-frame input, + .media-frame textarea, + .media-frame .search { + padding: 3px 6px; + } + + .image-details .column-image { + width: 30%; + left: 70%; + } + + .image-details .column-settings { + width: 70%; + } + + .image-details .media-modal { + left: 30px; + right: 30px; + } + + .image-details .embed-media-settings .setting { + margin: 20px; + } + + .image-details .embed-media-settings .setting span { + float: none; + text-align: left; + width: 100%; + margin-bottom: 4px; + } + + .image-details .embed-media-settings .setting input.link-to-custom, + .image-details .embed-media-settings .setting input[type="text"], + .image-details .embed-media-settings .setting textarea { + width: 100%; + margin-left: 0; + } + + .image-details .embed-media-settings .custom-size { + margin-left: 20px; + } + + .collection-settings .setting input[type="checkbox"] { + margin-top: 0; + } + + .media-selection { + min-width: 120px; + } + + .media-selection:after { + background: none; + } + + .media-selection .attachments { + display: none; + } + + .media-modal .attachments-browser .media-toolbar .search { + max-width: 100%; + height: auto; + float: right; + } + + .media-modal .attachments-browser .media-toolbar .attachment-filters { + height: auto; + } + + .media-modal .attachments-browser .media-toolbar .spinner { + margin: 14px 8px 0; + } + + /* Text inputs need to be 16px, or they force zooming on iOS */ + .media-frame input[type="text"], + .media-frame input[type="password"], + .media-frame input[type="number"], + .media-frame input[type="search"], + .media-frame input[type="email"], + .media-frame input[type="url"], + .media-frame textarea, + .media-frame select { + font-size: 16px; + } +} + +/* Responsive on portrait and landscape */ +@media only screen and (max-width: 640px), screen and (max-height: 400px) { + /* Full-bleed modal */ + .media-modal, + .image-details .media-modal { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + } + + .media-modal-backdrop { + position: fixed; + } + + .media-sidebar { + z-index: 1900; + max-width: 70%; + bottom: 120%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding-bottom: 0; + } + + .media-sidebar.visible { + bottom: 0; + } + + .attachments-browser .attachments, + .attachments-browser .uploader-inline, + .attachments-browser .media-toolbar { + right: 0; + } + + .image-details .media-frame-title { + display: block; + top: 0; + font-size: 14px; + } + + .image-details .column-image, + .image-details .column-settings { + width: 100%; + position: relative; + left: 0; + } + + .image-details .column-settings { + padding: 4px 0; + } + + /* Media tabs on the top */ + .media-frame-content .media-toolbar .instructions { + display: none; + } +} + +/* Landscape specific header override */ +@media screen and (max-height: 400px) { + .media-menu { + padding: 0; + } + + .media-frame-router { + top: 44px; + } + + .media-frame-content { + top: 78px; + } + + .attachments-browser .attachments { + top: 40px; + } + + /* Prevent unnecessary scrolling on title input */ + .embed-link-settings { + overflow: visible; + } +} + +@media only screen and (max-width: 480px) { + .media-modal-close { + top: 5px; + right: 5px; + } + + .media-modal .media-frame-title { + height: 40px; + } + + .media-modal .media-frame-title h1, + .media-frame:not(.hide-menu) .media-frame-title h1 { + font-size: 18px; + line-height: 40px; + } + + .media-frame:not(.hide-menu) .media-frame-title .dashicons { + line-height: 40px; + } + + .media-frame-router, + .media-frame:not(.hide-menu) .media-menu { + top: 40px; + } + + .media-frame-content { + top: 74px; + } + + .media-frame.hide-router .media-frame-content { + top: 40px; + } +} + +/** + * HiDPI Displays + */ +@media print, + (-o-min-device-pixel-ratio: 5/4), + (-webkit-min-device-pixel-ratio: 1.25), + (min-resolution: 120dpi) { + + .media-modal-icon { + background-image: url(../images/uploader-icons-2x.png); + -webkit-background-size: 134px 15px; + background-size: 134px 15px; + } + + .media-frame .spinner { + background-image: url(../images/spinner-2x.gif); + } +} + +.media-frame-content[data-columns="1"] .attachment { + width: 100%; +} + +.media-frame-content[data-columns="2"] .attachment { + width: 50%; +} + +.media-frame-content[data-columns="3"] .attachment { + width: 33.33%; +} + +.media-frame-content[data-columns="4"] .attachment { + width: 25%; +} + +.media-frame-content[data-columns="5"] .attachment { + width: 20%; +} + +.media-frame-content[data-columns="6"] .attachment { + width: 16.66%; +} + +.media-frame-content[data-columns="7"] .attachment { + width: 14.28%; +} + +.media-frame-content[data-columns="8"] .attachment { + width: 12.5%; +} + +.media-frame-content[data-columns="9"] .attachment { + width: 11.11%; +} + +.media-frame-content[data-columns="10"] .attachment { + width: 10%; +} + +.media-frame-content[data-columns="11"] .attachment { + width: 9.09%; +} + +.media-frame-content[data-columns="12"] .attachment { + width: 8.33%; +} diff --git a/wp-includes/css/media-views.min.css b/wp-includes/css/media-views.min.css new file mode 100644 index 0000000..4692a0f --- /dev/null +++ b/wp-includes/css/media-views.min.css @@ -0,0 +1,8 @@ +.media-modal *{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.media-frame input,.media-frame select,.media-frame textarea{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.media-frame,.media-modal{font-family:"Open Sans",sans-serif;font-size:12px}.media-frame input,.media-frame textarea{padding:6px 8px}.media-frame select,.wp-admin .media-frame select{line-height:28px;margin-top:3px}.media-frame a{border-bottom:none;color:#21759b}.media-frame a:hover{color:#d54e21}.media-frame a.button{color:#333}.media-frame a.button:hover{color:#222}.media-frame a.button-primary,.media-frame a.button-primary:hover{color:#fff}.media-frame input[type=email],.media-frame input[type=number],.media-frame input[type=password],.media-frame input[type=search],.media-frame input[type=text],.media-frame input[type=url],.media-frame select,.media-frame textarea{font-family:"Open Sans",sans-serif;font-size:12px;border-width:1px;border-style:solid;border-color:#dfdfdf}.media-frame input[type=email]:focus,.media-frame input[type=number]:focus,.media-frame input[type=password]:focus,.media-frame input[type=search]:focus,.media-frame input[type=text]:focus,.media-frame input[type=url]:focus,.media-frame select:focus,.media-frame textarea:focus{border-color:#5b9dd9}.media-frame select{height:24px;padding:2px}.media-frame input:disabled,.media-frame input[readonly],.media-frame textarea:disabled,.media-frame textarea[readonly]{background-color:#eee}.media-frame input[type=search]{-webkit-appearance:textfield}.media-frame :-moz-placeholder{color:#a9a9a9}.media-frame .hidden{display:none}/*! + * jQuery UI Draggable/Sortable 1.11.2 + * http://jqueryui.com + * + * Copyright 2014 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */.ui-draggable-handle,.ui-sortable-handle{-ms-touch-action:none;touch-action:none}.media-modal{position:fixed;top:30px;left:30px;right:30px;bottom:30px;z-index:160000}.wp-customizer .media-modal{z-index:560000}.media-modal-backdrop{position:fixed;top:0;left:0;right:0;bottom:0;min-height:360px;background:#000;opacity:.7;z-index:159900}.wp-customizer .media-modal-backdrop{z-index:559900}.media-modal-close{position:absolute;text-decoration:none;top:10px;right:10px;width:30px;height:30px;z-index:1000;-webkit-transition:color .1s ease-in-out,background .1s ease-in-out;transition:color .1s ease-in-out,background .1s ease-in-out}.media-modal-close:active{-webkit-box-shadow:none;box-shadow:none}.media-modal-close span.media-modal-icon{display:block;margin-top:5px;width:30px;height:15px;background-image:none;text-align:center}.media-modal-close .media-modal-icon:before{content:'\f158';font:400 20px/1 dashicons;speak:none;vertical-align:middle;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:#666}.media-modal-close:hover .media-modal-icon:before{color:#2ea2cc}.media-modal-close:active{outline:0}.media-modal-content{position:absolute;top:0;left:0;right:0;bottom:0;overflow:auto;min-height:300px;-webkit-box-shadow:0 5px 15px rgba(0,0,0,.7);box-shadow:0 5px 15px rgba(0,0,0,.7);background:#fcfcfc;-webkit-font-smoothing:subpixel-antialiased}.media-modal-icon{background-image:url(../images/uploader-icons.png);background-repeat:no-repeat}.media-toolbar{position:absolute;top:0;left:0;right:0;z-index:100;height:60px;padding:0 16px;border:0 solid #dfdfdf;overflow:hidden}.media-toolbar-primary{float:right;height:100%;max-width:33%}.media-toolbar-secondary{float:left;height:100%;max-width:66%}.media-toolbar-primary>.media-button,.media-toolbar-primary>.media-button-group{margin-left:10px;float:left;margin-top:15px}.media-toolbar-secondary>.media-button,.media-toolbar-secondary>.media-button-group{margin-right:10px;margin-top:15px}.media-sidebar{position:absolute;top:0;right:0;bottom:0;width:267px;padding:0 16px 24px;z-index:75;background:#f3f3f3;border-left:1px solid #ddd;overflow:auto;-webkit-overflow-scrolling:touch}.hide-toolbar .media-sidebar{bottom:0}.media-sidebar .sidebar-title{font-size:20px;margin:0;padding:12px 10px 10px;line-height:28px}.media-sidebar .sidebar-content{padding:0 10px;margin-bottom:130px}.media-sidebar .search{display:block;width:100%}.image-details h3,.media-sidebar h3{position:relative;font-weight:700;text-transform:uppercase;font-size:12px;color:#666;margin:24px 0 8px}.attachment-details .setting,.media-sidebar .setting{display:block;float:left;width:100%;margin:1px 0}.attachment-details .setting label,.media-sidebar .setting label{display:block}.attachment-details .setting .link-to-custom,.media-sidebar .setting .link-to-custom{margin:3px 2px 0}.attachment-details .setting span,.media-sidebar .setting span{min-width:30%;margin-right:4%;font-size:12px;text-align:right;word-wrap:break-word}.media-sidebar .setting .name{max-width:80px}.attachment-details .setting select,.media-sidebar .setting select{max-width:65%}.attachment-details .field input[type=checkbox],.attachment-details .field input[type=radio],.attachment-details .setting input[type=checkbox],.attachment-details .setting input[type=radio],.media-sidebar .field input[type=checkbox],.media-sidebar .field input[type=radio],.media-sidebar .setting input[type=checkbox],.media-sidebar .setting input[type=radio]{float:none;margin:8px 3px 0;padding:0}.attachment-details .setting span,.compat-item label span,.media-sidebar .setting span{float:left;min-height:22px;padding-top:8px;line-height:16px;font-weight:400;color:#666}.compat-item label span{text-align:right}.attachment-details .setting .value,.attachment-details .setting input[type=email],.attachment-details .setting input[type=number],.attachment-details .setting input[type=password],.attachment-details .setting input[type=search],.attachment-details .setting input[type=tel],.attachment-details .setting input[type=text],.attachment-details .setting input[type=url],.attachment-details .setting textarea,.media-sidebar .setting .value,.media-sidebar .setting input[type=email],.media-sidebar .setting input[type=number],.media-sidebar .setting input[type=password],.media-sidebar .setting input[type=search],.media-sidebar .setting input[type=tel],.media-sidebar .setting input[type=text],.media-sidebar .setting input[type=url],.media-sidebar .setting textarea{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:1px;width:65%;float:right}.attachment-details .setting .value,.media-sidebar .setting .value{margin:0 1px;text-align:left}.attachment-details .setting textarea,.compat-item .field textarea,.media-sidebar .setting textarea{height:62px;resize:vertical}.attachment-details select,.media-sidebar select{margin-top:3px}.compat-item{float:left;width:100%;overflow:hidden}.compat-item table{width:100%;table-layout:fixed;border-spacing:0;border:0}.compat-item tr{padding:2px 0;display:block;overflow:hidden}.compat-item .field,.compat-item .label{display:block;margin:0;padding:0}.compat-item .label{min-width:30%;margin-right:4%;float:left;text-align:right}.compat-item .label span{display:block;width:100%}.compat-item .field{float:right;width:66%}.compat-item .field input[type=email],.compat-item .field input[type=number],.compat-item .field input[type=password],.compat-item .field input[type=search],.compat-item .field input[type=tel],.compat-item .field input[type=text],.compat-item .field input[type=url]{width:100%;margin:0}.sidebar-for-errors .attachment-details,.sidebar-for-errors .compat-item,.sidebar-for-errors .media-sidebar .media-progress-bar,.sidebar-for-errors .upload-details{display:none!important}.media-menu{position:absolute;top:0;left:0;right:0;bottom:0;margin:0;padding:10px 0;background:#f3f3f3;border-right-width:1px;border-right-style:solid;border-right-color:#ccc;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.media-menu>a{display:block;position:relative;padding:8px 20px;margin:0;line-height:18px;font-size:14px;color:#0074a2;text-decoration:none}.media-menu>a:hover{color:#21759B;background:rgba(0,0,0,.04)}.media-menu>a:active{outline:0}.media-menu .active,.media-menu .active:hover{color:#222;font-weight:700}.media-menu .separator{height:0;margin:12px 20px;padding:0;border-top:1px solid #ddd}.media-router{position:relative;padding:0 6px;margin:0;clear:both;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.media-router a{-webkit-transition:none;transition:none}.media-router>a{position:relative;float:left;padding:8px 10px 9px;margin:0;height:18px;line-height:18px;font-size:14px;text-decoration:none}.media-router>a:last-child{border-right:0}.media-router>a:active{outline:0}.media-router .active,.media-router .active:hover{color:#333}.media-router .active,.media-router>a.active:last-child{margin:-1px -1px 0;background:#fff;border:1px solid #ddd;border-bottom:none}.media-router .active:after{display:none}.media-frame{overflow:hidden;position:absolute;top:0;left:0;right:0;bottom:0}.media-frame-menu{position:absolute;top:0;left:0;bottom:0;width:200px;z-index:150}.media-frame-title{position:absolute;top:0;left:200px;right:0;height:50px;z-index:200}.media-frame-router{position:absolute;top:50px;left:200px;right:0;height:36px;z-index:200}.media-frame-content{position:absolute;top:84px;left:200px;right:0;bottom:61px;height:auto;width:auto;margin:0;overflow:auto;background:#fff;border-top:1px solid #ddd;border-bottom:1px solid #ddd}.media-frame-toolbar{position:absolute;left:200px;right:0;bottom:0;height:60px;z-index:100}.media-frame.hide-menu .media-frame-content,.media-frame.hide-menu .media-frame-router,.media-frame.hide-menu .media-frame-title,.media-frame.hide-menu .media-frame-toolbar{left:0}.media-frame.hide-menu .media-frame-menu{left:-200px}.media-frame.hide-toolbar .media-frame-content{bottom:0}.media-frame.hide-toolbar .media-frame-toolbar{bottom:-61px}.media-frame.hide-router .media-frame-content{top:50px}.media-frame.hide-router .media-frame-router{display:none}.media-frame.hide-router .media-frame-title{border-bottom:1px solid #dfdfdf;-webkit-box-shadow:0 4px 4px -4px rgba(0,0,0,.1);box-shadow:0 4px 4px -4px rgba(0,0,0,.1)}.media-frame-title .dashicons{display:none}.media-frame-title h1{padding:0 16px;font-size:22px;line-height:50px;margin:0}.media-frame-title .suggested-dimensions{font-size:14px;float:right;margin-right:20px}.media-frame-content .crop-content{height:100%}.media-frame-content .crop-content .crop-image{display:block;margin:auto;max-width:100%;max-height:100%}.media-frame-content .crop-content .upload-errors{position:absolute;width:300px;top:50%;left:50%;margin-left:-150px;margin-right:-150px;z-index:600000}.media-frame .media-iframe{overflow:hidden}.media-frame .media-iframe,.media-frame .media-iframe iframe{height:100%;width:100%;border:0}.media-frame select.attachment-filters{margin-top:11px;margin-right:2%;max-width:47%}.media-frame .search{margin-top:11px;padding:4px;font-size:13px;color:#464646;font-family:"Open Sans",sans-serif;-webkit-appearance:none}.media-toolbar-primary .search{max-width:100%}.attachments{margin:0;-webkit-overflow-scrolling:touch}.attachment{position:relative;float:left;padding:8px;margin:0;color:#464646;cursor:pointer;list-style:none;text-align:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;width:25%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.attachment.details:focus,.attachment:focus,.selected.attachment:focus{-webkit-box-shadow:inset 0 0 2px 3px #fff,inset 0 0 0 7px #5b9dd9;box-shadow:inset 0 0 2px 3px #fff,inset 0 0 0 7px #5b9dd9;outline:0}.selected.attachment{-webkit-box-shadow:inset 0 0 0 5px #fff,inset 0 0 0 7px #ccc;box-shadow:inset 0 0 0 5px #fff,inset 0 0 0 7px #ccc}.attachment.details{-webkit-box-shadow:inset 0 0 0 3px #fff,inset 0 0 0 7px #1e8cbe;box-shadow:inset 0 0 0 3px #fff,inset 0 0 0 7px #1e8cbe}.attachment-preview{position:relative;-webkit-box-shadow:inset 0 0 15px rgba(0,0,0,.1),inset 0 0 0 1px rgba(0,0,0,.05);box-shadow:inset 0 0 15px rgba(0,0,0,.1),inset 0 0 0 1px rgba(0,0,0,.05);background:#eee;cursor:pointer}.attachment-preview:before{content:'';display:block;padding-top:100%}.attachment .icon{margin:0 auto;overflow:hidden}.attachment .thumbnail{overflow:hidden;position:absolute;top:0;right:0;bottom:0;left:0;opacity:1;-webkit-transition:opacity .1s;transition:opacity .1s}.attachment .portrait img{max-width:100%}.attachment .landscape img{max-height:100%}.attachment .thumbnail:after{content:'';display:block;position:absolute;top:0;left:0;right:0;bottom:0;-webkit-box-shadow:inset 0 0 0 1px rgba(0,0,0,.1);box-shadow:inset 0 0 0 1px rgba(0,0,0,.1);overflow:hidden}.attachment .thumbnail img{top:0;left:0}.attachment .thumbnail .centered{position:absolute;top:0;left:0;width:100%;height:100%;-webkit-transform:translate(50%,50%);-ms-transform:translate(50%,50%);transform:translate(50%,50%)}.attachment .thumbnail .centered img{-webkit-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.attachment .thumbnail .centered img.icon{-webkit-transform:translate(-50%,-70%);-ms-transform:translate(-50%,-70%);transform:translate(-50%,-70%)}.ie8 .attachment img.icon{top:20%;position:relative}.attachment .filename{position:absolute;left:0;right:0;bottom:0;overflow:hidden;max-height:100%;word-wrap:break-word;text-align:center;font-weight:700;background:rgba(255,255,255,.8);-webkit-box-shadow:inset 0 0 0 1px rgba(0,0,0,.15);box-shadow:inset 0 0 0 1px rgba(0,0,0,.15)}.attachment .filename div{padding:5px 10px}.attachment .thumbnail img{position:absolute}.attachment .close{display:block;position:absolute;top:5px;right:5px;height:22px;width:22px;padding:0;font-size:20px;line-height:20px;text-align:center;text-decoration:none;color:#464646;background-color:#fff;background-position:-96px 4px;border-width:0;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.3);box-shadow:0 0 0 1px rgba(0,0,0,.3);-webkit-transition-duration:none;transition-duration:none;-webkit-transition-property:none;transition-property:none}.attachment a.close:focus,.attachment a.close:hover{-webkit-box-shadow:0 0 0 1px rgba(0,0,0,.6);box-shadow:0 0 0 1px rgba(0,0,0,.6);background-position:-36px 4px}.attachment .check{display:none;height:24px;width:24px;position:absolute;z-index:10;top:0;right:0;outline:0;background:#eee;-webkit-box-shadow:0 0 0 1px #fff,0 0 0 2px rgba(0,0,0,.15);box-shadow:0 0 0 1px #fff,0 0 0 2px rgba(0,0,0,.15)}.attachment .check div{background-position:-1px 0;height:15px;width:15px;margin:5px}.attachment .check:hover div{background-position:-40px 0}.attachment.selected .check{display:block}.attachment.details .check,.attachment.selected .check:focus,.media-frame.mode-grid .attachment.selected .check{background-color:#1e8cbe;-webkit-box-shadow:0 0 0 1px #fff,0 0 0 2px #1e8cbe;box-shadow:0 0 0 1px #fff,0 0 0 2px #1e8cbe}.attachment.details .check div,.media-frame.mode-grid .attachment.selected .check div{background-position:-21px 0}.attachment.details .check:hover div,.attachment.selected .check:focus div,.media-frame.mode-grid .attachment.selected .check:hover div{background-position:-60px 0}.media-frame .attachment .describe{position:relative;display:block;width:100%;margin:0;padding:8px;font-size:12px;-webkit-border-radius:0;border-radius:0}.media-frame .attachments-browser{position:relative;width:100%;height:100%;overflow:hidden}.attachments-browser .media-toolbar{right:300px;height:50px}.attachments-browser.hide-sidebar .media-toolbar{right:0}.attachments-browser .media-toolbar-primary>.media-button,.attachments-browser .media-toolbar-primary>.media-button-group,.attachments-browser .media-toolbar-secondary>.media-button,.attachments-browser .media-toolbar-secondary>.media-button-group{margin:11px 0}.attachments-browser .attachments{padding:2px 8px 8px}.attachments-browser .attachments,.attachments-browser .uploader-inline{position:absolute;top:50px;left:0;right:300px;bottom:0;overflow:auto;outline:0}.attachments-browser .uploader-inline.hidden{display:none}.uploader-inline .close{background-color:transparent;border:0;cursor:pointer;height:48px;position:absolute;right:0;text-align:center;top:0;width:50px;z-index:1}.uploader-inline .close:before{font:400 30px/50px dashicons!important;color:#777;display:inline-block;content:'\f335';font-weight:300}.attachments-browser.hide-sidebar .attachments,.attachments-browser.hide-sidebar .uploader-inline{right:0;margin-right:0}.attachments-browser .instructions{display:inline-block;margin-top:16px;line-height:18px;font-size:13px;color:#666;margin-right:.5em}.attachments-browser .no-media{padding:2em 0 0 2em}.media-progress-bar{position:relative;height:10px;width:70%;margin:10px auto;-webkit-border-radius:10px;border-radius:10px;background:#dfdfdf;background:rgba(0,0,0,.1)}.media-progress-bar div{height:10px;min-width:20px;width:0;background:#1e8cbe;-webkit-border-radius:10px;border-radius:10px;-webkit-transition:width 300ms;transition:width 300ms}.media-uploader-status .media-progress-bar{display:none;width:100%}.uploading.media-uploader-status .media-progress-bar{display:block}.attachment-preview .media-progress-bar{position:absolute;top:50%;left:15%;width:70%;margin:-5px 0 0}.media-uploader-status{position:relative;margin:0 auto;padding-bottom:10px;max-width:400px}.media-sidebar .media-uploader-status{border-bottom:1px solid #dfdfdf}.uploader-inline .media-uploader-status h3{display:none}.media-uploader-status .upload-details{display:none;font-size:12px;color:#666}.uploading.media-uploader-status .upload-details{display:block}.media-uploader-status .upload-detail-separator{padding:0 4px}.media-uploader-status .upload-count{color:#464646}.media-uploader-status .upload-dismiss-errors,.media-uploader-status .upload-errors{display:none}.errors.media-uploader-status .upload-dismiss-errors,.errors.media-uploader-status .upload-errors{display:block}.media-uploader-status .upload-dismiss-errors{text-decoration:none}.media-sidebar .media-uploader-status .upload-dismiss-errors{position:absolute;top:0;right:0}.upload-errors .upload-error{margin:8px auto 0;padding:8px;border:1px solid #c00;background:#ffebe8;-webkit-border-radius:3px;border-radius:3px}.upload-errors .upload-error-label{padding:2px 4px;margin-right:8px;font-weight:700;color:#fff;background:#e00;background:-webkit-gradient(linear,left top,left bottom,from(#e00),to(#a00)) #e00;background:-webkit-linear-gradient(top,#e00,#a00) #e00;background:linear-gradient(to bottom,#e00,#a00) #e00;-webkit-border-radius:3px;border-radius:3px}.upload-errors .upload-error-message{display:block;padding-top:8px;color:#b44;word-wrap:break-word}.uploader-window{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,86,132,.9);z-index:250000;display:none;text-align:center;opacity:0;-webkit-transition:opacity 250ms;transition:opacity 250ms}.uploader-window-content{position:absolute;top:10px;left:10px;right:10px;bottom:10px;border:1px dashed #fff}.uploader-window h3{margin:-.5em 0 0;position:absolute;top:50%;left:0;right:0;-webkit-transform:translateY(-50%);-ms-transform:translateY(-50%);transform:translateY(-50%);font-size:40px;color:#fff;padding:0}.uploader-window .media-progress-bar{margin-top:20px;max-width:300px;background:0 0;border-color:#fff;display:none}.uploader-window .media-progress-bar div{background:#fff}.uploading .uploader-window .media-progress-bar{display:block}.media-frame .uploader-inline{margin-bottom:20px;padding:0;text-align:center}.uploader-inline-content{position:absolute;top:30%;left:0;right:0}.uploader-inline-content .upload-ui{margin:2em 0}.uploader-inline-content .post-upload-ui{margin-bottom:2em}.uploader-inline .has-upload-message .upload-ui{margin:0 0 4em}.uploader-inline h3{font-size:20px;line-height:28px;font-weight:400;margin:0}.uploader-inline .has-upload-message .upload-instructions{font-size:14px;color:#464646;font-weight:400}.uploader-inline .drop-instructions{display:none}.supports-drag-drop .uploader-inline .drop-instructions{display:block}.uploader-inline p{font-size:12px;margin:.5em 0}.uploader-inline .media-progress-bar{display:none}.uploading.uploader-inline .media-progress-bar{display:block}.uploader-inline .browser{display:inline-block!important}.media-selection{position:absolute;top:0;left:0;right:350px;height:60px;padding:0 0 0 16px;overflow:hidden;white-space:nowrap}.media-selection .selection-info{display:inline-block;font-size:12px;height:60px;margin-right:10px;vertical-align:top}.media-selection.editing,.media-selection.empty,.media-selection.one .edit-selection{display:none}.media-selection .count{display:block;padding-top:12px;font-size:14px;line-height:20px;font-weight:700}.media-selection .selection-info a{display:block;float:left;padding:1px 8px;margin:1px 8px 1px -8px;line-height:16px;text-decoration:none;border-right:1px solid #dfdfdf;color:#21759B}.media-selection .selection-info a:hover{background:#21759B;color:#fff;border-color:transparent}.media-selection .selection-info a:last-child{border-right:0;margin-right:0}.media-selection .selection-info .clear-selection{color:red}.media-selection .selection-info .clear-selection:hover{background:red}.media-selection .selection-view{display:inline-block;vertical-align:top}.media-selection .attachments{display:inline-block;height:48px;margin:6px;padding:0;overflow:hidden;vertical-align:top}.media-selection .attachment{width:40px;padding:0;margin:4px;-webkit-box-shadow:none;box-shadow:none}.media-selection .attachment .thumbnail{top:0;right:0;bottom:0;left:0}.media-selection .attachment .icon{width:50%}.media-selection .attachment-preview{-webkit-box-shadow:none;box-shadow:none;background:0 0}.media-selection .attachment.selection.details .thumbnail{-webkit-box-shadow:0 0 0 1px #fff,0 0 0 3px #1e8cbe;box-shadow:0 0 0 1px #fff,0 0 0 3px #1e8cbe}.media-selection:after{content:'';display:block;position:absolute;top:0;right:0;bottom:0;width:25px;background-image:-webkit-gradient(linear,right top,left top,from(rgba(255,255,255,1)),to(rgba(255,255,255,0)));background-image:-webkit-linear-gradient(right,rgba(255,255,255,1),rgba(255,255,255,0));background-image:linear-gradient(to left,rgba(255,255,255,1),rgba(255,255,255,0))}.media-selection .attachment .filename{display:none}.media-frame .spinner{background:url(../images/spinner.gif) 0 0/20px 20px no-repeat;-webkit-background-size:20px 20px;display:none;opacity:.7;filter:alpha(opacity=70);width:20px;height:20px;margin:0}.media-toolbar .spinner{margin-top:14px}.attachment-details{position:relative;overflow:auto}.attachment-details .settings-save-status{float:right;text-transform:none;z-index:10}.attachment-details .settings-save-status .spinner{margin:0 5px}.attachment-details .settings-save-status .saved{float:right;display:none}.attachment-details.save-complete .settings-save-status .saved,.attachment-details.save-waiting .settings-save-status .spinner{display:block}.attachment-info{overflow:hidden;min-height:60px;margin-bottom:16px;line-height:18px;color:#666;border-bottom:1px solid #ddd;padding-bottom:11px}.attachment-info .filename{font-weight:700;color:#464646;word-wrap:break-word}.attachment-info .thumbnail{position:relative;float:left;max-width:120px;max-height:120px;margin-top:5px;margin-right:10px;margin-bottom:5px}.uploading .attachment-info .thumbnail{width:120px;height:80px;-webkit-box-shadow:inset 0 0 15px rgba(0,0,0,.1);box-shadow:inset 0 0 15px rgba(0,0,0,.1)}.uploading .attachment-info .media-progress-bar{margin-top:35px}.attachment-info .thumbnail-image:after{content:'';display:block;position:absolute;top:0;left:0;right:0;bottom:0;-webkit-box-shadow:inset 0 0 0 1px rgba(0,0,0,.15);box-shadow:inset 0 0 0 1px rgba(0,0,0,.15);overflow:hidden}.attachment-info .thumbnail img{display:block;max-width:120px;max-height:120px;margin:0 auto}.attachment-info .details{float:left;font-size:12px;max-width:100%}.attachment-info .delete-attachment,.attachment-info .edit-attachment,.attachment-info .refresh-attachment,.attachment-info .trash-attachment,.attachment-info .untrash-attachment{display:block;text-decoration:none;white-space:nowrap}.attachment-details.needs-refresh .attachment-info .edit-attachment,.attachment-info .refresh-attachment{display:none}.attachment-details.needs-refresh .attachment-info .refresh-attachment,.attachment-info .edit-attachment{display:block}.media-modal .delete-attachment,.media-modal .trash-attachment,.media-modal .untrash-attachment{color:#bc0b0b}.media-modal .delete-attachment:hover,.media-modal .trash-attachment:hover,.media-modal .untrash-attachment:hover{color:red}.attachment-display-settings{width:100%;float:left;overflow:hidden}.attachment-display-settings h4{margin:1.4em 0 .4em}.collection-settings{overflow:hidden}.collection-settings .setting input[type=checkbox]{float:left;margin-right:8px}.collection-settings .setting span{min-width:inherit}.media-modal .imgedit-wrap{position:static}.media-modal .imgedit-wait{height:auto!important;right:0;bottom:0;left:0}.media-modal .imgedit-wrap .imgedit-panel-content{padding:16px;position:absolute;top:0;right:282px;bottom:0;left:0;overflow:auto}.media-modal .imgedit-wrap .imgedit-settings{background:#f3f3f3;border-left:1px solid #ddd;padding:0 16px 16px;position:absolute;top:0;right:0;bottom:0;width:250px;overflow:auto}.media-modal .imgedit-group{background:0 0;border:none;border-bottom:1px solid #ddd;-webkit-box-shadow:none;box-shadow:none;margin:0 0 16px;padding:0 0 16px;position:relative}.media-modal .imgedit-group:last-of-type{border:none;margin:0;padding:0}.media-modal .imgedit-group-top h3{text-transform:uppercase;font-size:12px;color:#666;margin:24px 0 0}.media-modal .imgedit-group-top h3 a{text-decoration:none;color:#666}.media-modal .imgedit-help-toggle{margin-top:-2px;cursor:pointer;color:#666}.media-modal .imgedit-help-toggled span.dashicons:before{content:'\f142'}.media-modal .imgedit-group img{margin-top:5px}.media-modal .imgedit-wrap div.updated{margin:0 0 16px}.embed-url{display:block;position:relative;padding:16px;margin:0;z-index:250;background:#fff;font-size:18px}.media-frame .embed-url input{font-size:18px;padding:12px 14px;width:100%;min-width:200px;-webkit-box-shadow:inset 2px 2px 4px -2px rgba(0,0,0,.1);box-shadow:inset 2px 2px 4px -2px rgba(0,0,0,.1)}.media-frame .embed-url .spinner{position:absolute;top:32px;right:26px}.media-frame .embed-loading .embed-url .spinner{display:block}.embed-link-settings,.embed-media-settings{position:absolute;top:70px;left:0;right:0;bottom:0;padding:16px 16px 32px;overflow:auto}.embed-preview embed,.embed-preview iframe,.embed-preview img{max-width:100%}.embed-preview img{height:auto}.image-details .media-modal{left:140px;right:140px}.image-details .media-frame-content,.image-details .media-frame-router,.image-details .media-frame-title{left:0}.image-details .embed-media-settings{top:0;overflow:visible;padding:0}.image-details .embed-media-settings,.image-details .embed-media-settings div{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.image-details .column-settings{background:#f3f3f3;border-right:1px solid #ddd;min-height:100%;width:55%;position:absolute;top:0;left:0}.image-details .column-settings h3{margin:20px;padding-top:20px;border-top:1px solid #ddd}.image-details .column-image{width:45%;position:absolute;left:55%;top:0}.image-details .image{margin:20px}.image-details .image img{max-width:100%;max-height:500px}.image-details .advanced-toggle{color:#666;text-decoration:none;display:block}.image-details .advanced-toggle:after{font:400 20px/1 dashicons;speak:none;vertical-align:top;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f140';display:inline-block;margin-top:-2px}.image-details .advanced-visible .advanced-toggle:after{content:'\f142'}.image-details .embed-media-settings .size{margin-bottom:4px}.image-details .custom-size span{display:block}.image-details .custom-size label{display:block;float:left}.image-details .custom-size span small{color:#999;font-size:inherit}.image-details .custom-size input{width:5em}.image-details .custom-size .sep{float:left;margin:26px 6px 0}.image-details .custom-size:after{content:'';display:table;clear:both}.media-embed .thumbnail{max-width:100%;max-height:200px;position:relative;float:left}.media-embed .thumbnail img{max-height:200px;display:block}.media-embed .thumbnail:after{content:'';display:block;position:absolute;top:0;left:0;right:0;bottom:0;-webkit-box-shadow:inset 0 0 0 1px rgba(0,0,0,.1);box-shadow:inset 0 0 0 1px rgba(0,0,0,.1);overflow:hidden}.media-embed .setting{width:100%;margin:10px 0;float:left;display:block;clear:both}.image-details .embed-media-settings .setting{float:none;width:auto}.image-details .actions{margin:10px 0}.image-details .hidden{display:none}.media-embed .setting input[type=text],.media-embed .setting textarea{display:block;width:100%;max-width:400px;margin:1px 0}.image-details .embed-media-settings .setting input[type=text],.image-details .embed-media-settings .setting textarea{max-width:inherit;width:70%}.image-details .embed-media-settings .custom-size,.image-details .embed-media-settings .link-target,.image-details .embed-media-settings .setting input.link-to-custom{margin-left:27%;width:70%}.image-details .embed-media-settings .link-target{margin-top:24px}.media-embed .setting input.hidden,.media-embed .setting textarea.hidden{display:none}.media-embed .setting span{display:block;width:200px;font-size:13px;line-height:24px;color:#666}.image-details .embed-media-settings .setting span{float:left;width:25%;text-align:right;margin:8px 1% 0;line-height:1.1}.media-embed .setting .button-group{margin:2px 0}.media-embed-sidebar{position:absolute;top:0;left:440px}.advanced-section,.link-settings{margin-top:10px}#wp-fullscreen-body .uploader-editor,.wp-editor-wrap .uploader-editor{background:rgba(150,150,150,.9);position:absolute;top:0;left:0;width:100%;height:100%;z-index:99998;display:none;text-align:center}#wp-fullscreen-body .uploader-editor{background:rgba(0,86,132,.9);position:fixed;z-index:100050}.wp-editor-wrap.wp-fullscreen-wrap .uploader-editor{opacity:0}#wp-fullscreen-body .uploader-editor-content,.wp-editor-wrap .uploader-editor-content{border:1px dashed #fff;position:absolute;top:10px;left:10px;right:10px;bottom:10px}#wp-fullscreen-body .uploader-editor .uploader-editor-title,.wp-editor-wrap .uploader-editor .uploader-editor-title{position:absolute;top:50%;left:0;right:0;-webkit-transform:translateY(-50%);-ms-transform:translateY(-50%);transform:translateY(-50%);font-size:3em;line-height:1.3;font-weight:700;color:#fff;padding:0;margin:0;display:none}.wp-editor-wrap .uploader-editor.droppable{background:rgba(0,86,132,.9)}#wp-fullscreen-body .uploader-editor .uploader-editor-title,.wp-editor-wrap .uploader-editor.droppable .uploader-editor-title{display:block}.ie7 .media-frame .attachments-browser{position:static}.ie7 .media-frame .embed-url input{margin-top:4px;width:90%}.ie7 .compat-item{width:99%}.ie7 .attachment-display-settings{width:auto}.ie7 .attachment-preview,.ie7 .attachment-preview .thumbnail{width:120px;height:120px}.ie7 .media-frame .attachment .describe{width:102px}.ie7 .media-sidebar .setting select{max-width:55%}.ie7 .media-sidebar .setting input[type=email],.ie7 .media-sidebar .setting input[type=number],.ie7 .media-sidebar .setting input[type=password],.ie7 .media-sidebar .setting input[type=search],.ie7 .media-sidebar .setting input[type=tel],.ie7 .media-sidebar .setting input[type=text],.ie7 .media-sidebar .setting input[type=url],.ie7 .media-sidebar .setting textarea{width:55%}.ie7 .media-sidebar .setting .link-to-custom{float:left}.rtl .media-frame,.rtl .media-frame .search,.rtl .media-frame input[type=email],.rtl .media-frame input[type=number],.rtl .media-frame input[type=password],.rtl .media-frame input[type=search],.rtl .media-frame input[type=tel],.rtl .media-frame input[type=text],.rtl .media-frame input[type=url],.rtl .media-frame select,.rtl .media-frame textarea,.rtl .media-modal{font-family:Tahoma,sans-serif}:lang(he-il) .rtl .media-frame,:lang(he-il) .rtl .media-frame .search,:lang(he-il) .rtl .media-frame input[type=email],:lang(he-il) .rtl .media-frame input[type=number],:lang(he-il) .rtl .media-frame input[type=password],:lang(he-il) .rtl .media-frame input[type=search],:lang(he-il) .rtl .media-frame input[type=text],:lang(he-il) .rtl .media-frame input[type=url],:lang(he-il) .rtl .media-frame select,:lang(he-il) .rtl .media-frame textarea,:lang(he-il) .rtl .media-modal{font-family:Arial,sans-serif}@media only screen and (max-width:900px){.media-frame:not(.hide-menu) .media-frame-content,.media-frame:not(.hide-menu) .media-frame-router,.media-frame:not(.hide-menu) .media-frame-title,.media-frame:not(.hide-menu) .media-frame-toolbar{left:0}.media-frame:not(.hide-menu) .media-frame-menu{position:static;width:0}.media-frame:not(.hide-menu) .media-menu{width:auto;max-width:80%;overflow:auto;z-index:2000;top:50px;left:-300px;right:auto;bottom:auto;padding:5px 0;border:1px solid #ccc}.media-frame:not(.hide-menu) .media-menu.visible{left:0}.media-frame:not(.hide-menu) .media-menu>a{padding:12px 16px;font-size:16px}.media-frame:not(.hide-menu) .media-menu>a.active{display:none}.media-frame:not(.hide-menu) .media-menu .separator{margin:5px 10px}.media-frame:not(.hide-menu) .media-frame-title{left:0;color:#21759b}.media-frame:not(.hide-menu) .media-frame-title .dashicons{display:inline-block;line-height:50px}.media-frame:not(.hide-menu) .media-frame-title h1{line-height:3;font-size:18px;float:left;cursor:pointer}.media-sidebar{width:230px}.attachments-browser .attachments,.attachments-browser .media-toolbar,.attachments-browser .uploader-inline{right:262px}.attachment-details .setting,.media-sidebar .setting{margin:6px 0}.attachment-details .setting input,.attachment-details .setting span,.attachment-details .setting textarea,.compat-item label span,.media-sidebar .setting input,.media-sidebar .setting span,.media-sidebar .setting textarea{float:none}.attachment-details .setting span,.compat-item label span,.media-sidebar .setting span{text-align:inherit;min-height:16px;margin:0;padding:8px 2px 0}.attachment-details .setting .value,.media-sidebar .setting .value{float:none;width:auto}.attachment-details .setting input[type=email],.attachment-details .setting input[type=number],.attachment-details .setting input[type=password],.attachment-details .setting input[type=search],.attachment-details .setting input[type=tel],.attachment-details .setting input[type=text],.attachment-details .setting input[type=url],.attachment-details .setting select,.attachment-details .setting textarea,.media-sidebar .setting input[type=email],.media-sidebar .setting input[type=number],.media-sidebar .setting input[type=password],.media-sidebar .setting input[type=search],.media-sidebar .setting input[type=tel],.media-sidebar .setting input[type=text],.media-sidebar .setting input[type=url],.media-sidebar .setting select,.media-sidebar .setting textarea{float:none;width:98%;max-width:none;height:auto}.attachment-details .setting select.columns,.media-sidebar .setting select.columns{width:auto}.media-frame .search,.media-frame input,.media-frame textarea{padding:3px 6px}.image-details .column-image{width:30%;left:70%}.image-details .column-settings{width:70%}.image-details .media-modal{left:30px;right:30px}.image-details .embed-media-settings .setting{margin:20px}.image-details .embed-media-settings .setting span{float:none;text-align:left;width:100%;margin-bottom:4px}.image-details .embed-media-settings .setting input.link-to-custom,.image-details .embed-media-settings .setting input[type=text],.image-details .embed-media-settings .setting textarea{width:100%;margin-left:0}.image-details .embed-media-settings .custom-size{margin-left:20px}.collection-settings .setting input[type=checkbox]{margin-top:0}.media-selection{min-width:120px}.media-selection:after{background:0 0}.media-selection .attachments{display:none}.media-modal .attachments-browser .media-toolbar .search{max-width:100%;height:auto;float:right}.media-modal .attachments-browser .media-toolbar .attachment-filters{height:auto}.media-modal .attachments-browser .media-toolbar .spinner{margin:14px 8px 0}.media-frame input[type=email],.media-frame input[type=number],.media-frame input[type=password],.media-frame input[type=search],.media-frame input[type=text],.media-frame input[type=url],.media-frame select,.media-frame textarea{font-size:16px}}@media only screen and (max-width:640px),screen and (max-height:400px){.image-details .media-modal,.media-modal{position:fixed;top:0;left:0;right:0;bottom:0}.media-modal-backdrop{position:fixed}.media-sidebar{z-index:1900;max-width:70%;bottom:120%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding-bottom:0}.media-sidebar.visible{bottom:0}.attachments-browser .attachments,.attachments-browser .media-toolbar,.attachments-browser .uploader-inline{right:0}.image-details .media-frame-title{display:block;top:0;font-size:14px}.image-details .column-image,.image-details .column-settings{width:100%;position:relative;left:0}.image-details .column-settings{padding:4px 0}.media-frame-content .media-toolbar .instructions{display:none}}@media screen and (max-height:400px){.media-menu{padding:0}.media-frame-router{top:44px}.media-frame-content{top:78px}.attachments-browser .attachments{top:40px}.embed-link-settings{overflow:visible}}@media only screen and (max-width:480px){.media-modal-close{top:5px;right:5px}.media-modal .media-frame-title{height:40px}.media-frame:not(.hide-menu) .media-frame-title h1,.media-modal .media-frame-title h1{font-size:18px;line-height:40px}.media-frame:not(.hide-menu) .media-frame-title .dashicons{line-height:40px}.media-frame-router,.media-frame:not(.hide-menu) .media-menu{top:40px}.media-frame-content{top:74px}.media-frame.hide-router .media-frame-content{top:40px}}@media print,(-o-min-device-pixel-ratio:5/4),(-webkit-min-device-pixel-ratio:1.25),(min-resolution:120dpi){.media-modal-icon{background-image:url(../images/uploader-icons-2x.png);-webkit-background-size:134px 15px;background-size:134px 15px}.media-frame .spinner{background-image:url(../images/spinner-2x.gif)}}.media-frame-content[data-columns="1"] .attachment{width:100%}.media-frame-content[data-columns="2"] .attachment{width:50%}.media-frame-content[data-columns="3"] .attachment{width:33.33%}.media-frame-content[data-columns="4"] .attachment{width:25%}.media-frame-content[data-columns="5"] .attachment{width:20%}.media-frame-content[data-columns="6"] .attachment{width:16.66%}.media-frame-content[data-columns="7"] .attachment{width:14.28%}.media-frame-content[data-columns="8"] .attachment{width:12.5%}.media-frame-content[data-columns="9"] .attachment{width:11.11%}.media-frame-content[data-columns="10"] .attachment{width:10%}.media-frame-content[data-columns="11"] .attachment{width:9.09%}.media-frame-content[data-columns="12"] .attachment{width:8.33%} \ No newline at end of file diff --git a/wp-includes/css/wp-auth-check-rtl.css b/wp-includes/css/wp-auth-check-rtl.css new file mode 100644 index 0000000..568fa74 --- /dev/null +++ b/wp-includes/css/wp-auth-check-rtl.css @@ -0,0 +1,100 @@ +/*------------------------------------------------------------------------------ + Interim login dialog +------------------------------------------------------------------------------*/ + +#wp-auth-check-wrap.hidden { + display: none; +} + +#wp-auth-check-wrap #wp-auth-check-bg { + position: fixed; + top: 0; + bottom: 0; + right: 0; + left: 0; + background: #000; + opacity: 0.7; + filter: alpha(opacity=70); + z-index: 1000010; /* needs to appear above .notification-dialog */ +} + +#wp-auth-check-wrap #wp-auth-check { + position: fixed; + right: 50%; + overflow: hidden; + top: 40px; + bottom: 20px; + max-height: 415px; + width: 380px; + margin: 0 -190px 0 0; + padding: 30px 0 0; + background-color: #eee; + z-index: 1000011; /* needs to appear above #wp-auth-check-bg */ + -webkit-box-shadow: 0 3px 6px rgba( 0, 0, 0, 0.3 ); + box-shadow: 0 3px 6px rgba( 0, 0, 0, 0.3 ); +} + +#wp-auth-check-wrap.fallback #wp-auth-check { + max-height: 180px; + overflow: auto; +} + +#wp-auth-check-wrap #wp-auth-check-form { + background: url(../images/wpspin-2x.gif) no-repeat center center; + -webkit-background-size: 16px 16px; + background-size: 16px 16px; + height: 100%; +} + +#wp-auth-check-wrap #wp-auth-check-form iframe { + height: 100%; + width: 100%; + overflow: auto; +} + +#wp-auth-check-wrap .wp-auth-check-close { + position: absolute; + top: 8px; + left: 8px; + height: 22px; + width: 22px; + cursor: pointer; +} + +#wp-auth-check-wrap .wp-auth-check-close:before { + content: '\f158'; + display: block !important; + font: normal 20px/1 'dashicons'; + speak: none; + height: 22px; + margin: 2px 0; + text-align: center; + width: 22px; + color: #777; + -webkit-font-smoothing: antialiased !important; + -moz-osx-font-smoothing: grayscale; +} + +#wp-auth-check-wrap .wp-auth-check-close:hover:before { + color: #0074a2; +} + +#wp-auth-check-wrap .wp-auth-check-close:focus { + outline: 1px dotted #888; +} + +#wp-auth-check-wrap .wp-auth-fallback-expired { + outline: 0; +} + +#wp-auth-check-wrap .wp-auth-fallback { + font-size: 14px; + line-height: 21px; + padding: 0 25px; + display: none; +} + +#wp-auth-check-wrap.fallback .wp-auth-fallback, +#wp-auth-check-wrap.fallback .wp-auth-check-close { + display: block; +} diff --git a/wp-includes/css/wp-auth-check-rtl.min.css b/wp-includes/css/wp-auth-check-rtl.min.css new file mode 100644 index 0000000..9dd449f --- /dev/null +++ b/wp-includes/css/wp-auth-check-rtl.min.css @@ -0,0 +1 @@ +#wp-auth-check-wrap.hidden{display:none}#wp-auth-check-wrap #wp-auth-check-bg{position:fixed;top:0;bottom:0;right:0;left:0;background:#000;opacity:.7;filter:alpha(opacity=70);z-index:1000010}#wp-auth-check-wrap #wp-auth-check{position:fixed;right:50%;overflow:hidden;top:40px;bottom:20px;max-height:415px;width:380px;margin:0 -190px 0 0;padding:30px 0 0;background-color:#eee;z-index:1000011;-webkit-box-shadow:0 3px 6px rgba(0,0,0,.3);box-shadow:0 3px 6px rgba(0,0,0,.3)}#wp-auth-check-wrap.fallback #wp-auth-check{max-height:180px;overflow:auto}#wp-auth-check-wrap #wp-auth-check-form{background:url(../images/wpspin-2x.gif) center center/16px 16px no-repeat;-webkit-background-size:16px 16px;height:100%}#wp-auth-check-wrap #wp-auth-check-form iframe{height:100%;width:100%;overflow:auto}#wp-auth-check-wrap .wp-auth-check-close{position:absolute;top:8px;left:8px;height:22px;width:22px;cursor:pointer}#wp-auth-check-wrap .wp-auth-check-close:before{content:'\f158';display:block!important;font:400 20px/1 dashicons;speak:none;height:22px;margin:2px 0;text-align:center;width:22px;color:#777;-webkit-font-smoothing:antialiased!important;-moz-osx-font-smoothing:grayscale}#wp-auth-check-wrap .wp-auth-check-close:hover:before{color:#0074a2}#wp-auth-check-wrap .wp-auth-check-close:focus{outline:#888 dotted 1px}#wp-auth-check-wrap .wp-auth-fallback-expired{outline:0}#wp-auth-check-wrap .wp-auth-fallback{font-size:14px;line-height:21px;padding:0 25px;display:none}#wp-auth-check-wrap.fallback .wp-auth-check-close,#wp-auth-check-wrap.fallback .wp-auth-fallback{display:block} \ No newline at end of file diff --git a/wp-includes/css/wp-auth-check.css b/wp-includes/css/wp-auth-check.css new file mode 100644 index 0000000..cd42ed9 --- /dev/null +++ b/wp-includes/css/wp-auth-check.css @@ -0,0 +1,100 @@ +/*------------------------------------------------------------------------------ + Interim login dialog +------------------------------------------------------------------------------*/ + +#wp-auth-check-wrap.hidden { + display: none; +} + +#wp-auth-check-wrap #wp-auth-check-bg { + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; + background: #000; + opacity: 0.7; + filter: alpha(opacity=70); + z-index: 1000010; /* needs to appear above .notification-dialog */ +} + +#wp-auth-check-wrap #wp-auth-check { + position: fixed; + left: 50%; + overflow: hidden; + top: 40px; + bottom: 20px; + max-height: 415px; + width: 380px; + margin: 0 0 0 -190px; + padding: 30px 0 0; + background-color: #eee; + z-index: 1000011; /* needs to appear above #wp-auth-check-bg */ + -webkit-box-shadow: 0 3px 6px rgba( 0, 0, 0, 0.3 ); + box-shadow: 0 3px 6px rgba( 0, 0, 0, 0.3 ); +} + +#wp-auth-check-wrap.fallback #wp-auth-check { + max-height: 180px; + overflow: auto; +} + +#wp-auth-check-wrap #wp-auth-check-form { + background: url(../images/wpspin-2x.gif) no-repeat center center; + -webkit-background-size: 16px 16px; + background-size: 16px 16px; + height: 100%; +} + +#wp-auth-check-wrap #wp-auth-check-form iframe { + height: 100%; + width: 100%; + overflow: auto; +} + +#wp-auth-check-wrap .wp-auth-check-close { + position: absolute; + top: 8px; + right: 8px; + height: 22px; + width: 22px; + cursor: pointer; +} + +#wp-auth-check-wrap .wp-auth-check-close:before { + content: '\f158'; + display: block !important; + font: normal 20px/1 'dashicons'; + speak: none; + height: 22px; + margin: 2px 0; + text-align: center; + width: 22px; + color: #777; + -webkit-font-smoothing: antialiased !important; + -moz-osx-font-smoothing: grayscale; +} + +#wp-auth-check-wrap .wp-auth-check-close:hover:before { + color: #0074a2; +} + +#wp-auth-check-wrap .wp-auth-check-close:focus { + outline: 1px dotted #888; +} + +#wp-auth-check-wrap .wp-auth-fallback-expired { + outline: 0; +} + +#wp-auth-check-wrap .wp-auth-fallback { + font-size: 14px; + line-height: 21px; + padding: 0 25px; + display: none; +} + +#wp-auth-check-wrap.fallback .wp-auth-fallback, +#wp-auth-check-wrap.fallback .wp-auth-check-close { + display: block; +} diff --git a/wp-includes/css/wp-auth-check.min.css b/wp-includes/css/wp-auth-check.min.css new file mode 100644 index 0000000..8193f8b --- /dev/null +++ b/wp-includes/css/wp-auth-check.min.css @@ -0,0 +1 @@ +#wp-auth-check-wrap.hidden{display:none}#wp-auth-check-wrap #wp-auth-check-bg{position:fixed;top:0;bottom:0;left:0;right:0;background:#000;opacity:.7;filter:alpha(opacity=70);z-index:1000010}#wp-auth-check-wrap #wp-auth-check{position:fixed;left:50%;overflow:hidden;top:40px;bottom:20px;max-height:415px;width:380px;margin:0 0 0 -190px;padding:30px 0 0;background-color:#eee;z-index:1000011;-webkit-box-shadow:0 3px 6px rgba(0,0,0,.3);box-shadow:0 3px 6px rgba(0,0,0,.3)}#wp-auth-check-wrap.fallback #wp-auth-check{max-height:180px;overflow:auto}#wp-auth-check-wrap #wp-auth-check-form{background:url(../images/wpspin-2x.gif) center center/16px 16px no-repeat;-webkit-background-size:16px 16px;height:100%}#wp-auth-check-wrap #wp-auth-check-form iframe{height:100%;width:100%;overflow:auto}#wp-auth-check-wrap .wp-auth-check-close{position:absolute;top:8px;right:8px;height:22px;width:22px;cursor:pointer}#wp-auth-check-wrap .wp-auth-check-close:before{content:'\f158';display:block!important;font:400 20px/1 dashicons;speak:none;height:22px;margin:2px 0;text-align:center;width:22px;color:#777;-webkit-font-smoothing:antialiased!important;-moz-osx-font-smoothing:grayscale}#wp-auth-check-wrap .wp-auth-check-close:hover:before{color:#0074a2}#wp-auth-check-wrap .wp-auth-check-close:focus{outline:#888 dotted 1px}#wp-auth-check-wrap .wp-auth-fallback-expired{outline:0}#wp-auth-check-wrap .wp-auth-fallback{font-size:14px;line-height:21px;padding:0 25px;display:none}#wp-auth-check-wrap.fallback .wp-auth-check-close,#wp-auth-check-wrap.fallback .wp-auth-fallback{display:block} \ No newline at end of file diff --git a/wp-includes/css/wp-pointer-rtl.css b/wp-includes/css/wp-pointer-rtl.css new file mode 100644 index 0000000..75088cb --- /dev/null +++ b/wp-includes/css/wp-pointer-rtl.css @@ -0,0 +1,212 @@ +.wp-pointer-content { + padding: 0 0 10px; + position: relative; + font-size: 13px; + background: #fff; + border: 1px solid #dfdfdf; + -webkit-box-shadow: 0 3px 6px rgba(0,0,0,0.075); + box-shadow: 0 3px 6px rgba(0,0,0,0.075); +} + +.wp-pointer-content h3 { + position: relative; + margin: -1px -1px 5px; + padding: 15px 60px 14px 18px; + border: 1px solid #3592b6; + border-bottom: none; + line-height: 1.4em; + font-size: 14px; + color: #fff; + background: #2ea2cc; +} + +.wp-pointer-content h3:before { + background: #fff; + -webkit-border-radius: 50%; + border-radius: 50%; + color: #2ea2cc; + content: '\f227'; + font: normal 20px/1.6 'dashicons'; + position: absolute; + top: 8px; + right: 15px; + speak: none; + text-align: center; + width: 32px; + height: 32px; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.wp-pointer-content p { + padding: 0 15px; +} + +.wp-pointer-buttons { + margin: 0; + padding: 5px 15px; + overflow: auto; +} + +.wp-pointer-buttons a { + float: left; + display: inline-block; + text-decoration: none; +} + +.wp-pointer-buttons a.close { + padding-right: 3px; + position: relative; +} + +.wp-pointer-buttons a.close:before { + background: none; + color: #bbb; + content: '\f153'; + display: block !important; + font: normal 13px/1 'dashicons'; + speak: none; + margin: 1px 0; + text-align: center; + -webkit-font-smoothing: antialiased !important; + width: 10px; + height: 100%; + position: absolute; + right: -12px; + top: 1px; +} + +.wp-pointer-buttons a.close:hover:before { + color: #c00; +} + +/* The arrow base class must take up no space, even with transparent borders. */ +.wp-pointer-arrow, +.wp-pointer-arrow-inner { + position: absolute; + width: 0; + height: 0; +} + +.wp-pointer-arrow { + z-index: 10; + width: 0; + height: 0; + border: 0 solid transparent; +} + +.wp-pointer-arrow-inner { + z-index: 20; +} + +/* Make Room for the Arrow! */ +.wp-pointer-top, +.wp-pointer-undefined { + padding-top: 13px; +} + +.wp-pointer-bottom { + margin-top: -13px; + padding-bottom: 13px; +} + +/* @noflip */ +.wp-pointer-left { + padding-left: 13px; +} +/* @noflip */ +.wp-pointer-right { + margin-left: -13px; + padding-right: 13px; +} + +/* Base Size & Positioning */ +.wp-pointer-top .wp-pointer-arrow, +.wp-pointer-bottom .wp-pointer-arrow, +.wp-pointer-undefined .wp-pointer-arrow { + right: 50px; +} + +.wp-pointer-left .wp-pointer-arrow, +.wp-pointer-right .wp-pointer-arrow { + top: 50%; + margin-top: -15px; +} + +/* Arrow Sprite */ +.wp-pointer-top .wp-pointer-arrow, +.wp-pointer-undefined .wp-pointer-arrow { + top: 0; + border-width: 0 13px 13px 13px; + border-bottom-color: #3592b6; +} + +.wp-pointer-top .wp-pointer-arrow-inner, +.wp-pointer-undefined .wp-pointer-arrow-inner { + top: 1px; + margin-right: -13px; + margin-top: -13px; + border: 13px solid transparent; + border-bottom-color: #2ea2cc; + display: block; + content: ' '; +} + +.wp-pointer-bottom .wp-pointer-arrow { + bottom: 0; + border-width: 13px 13px 0 13px; + border-top-color: #ccc; +} + +.wp-pointer-bottom .wp-pointer-arrow-inner { + bottom: 1px; + margin-right: -13px; + margin-bottom: -13px; + border: 13px solid transparent; + border-top-color: #fff; + display: block; + content: ' '; +} + +/* @noflip */ +.wp-pointer-left .wp-pointer-arrow { + left: 0; + border-width: 13px 13px 13px 0; + border-right-color: #ccc; +} + +/* @noflip */ +.wp-pointer-left .wp-pointer-arrow-inner { + left: 1px; + margin-left: -13px; + margin-top: -13px; + border: 13px solid transparent; + border-right-color: #fff; + display: block; + content: ' '; +} + +/* @noflip */ +.wp-pointer-right .wp-pointer-arrow { + right: 0; + border-width: 13px 0 13px 13px; + border-left-color: #ccc; +} + +/* @noflip */ +.wp-pointer-right .wp-pointer-arrow-inner { + right: 1px; + margin-right: -13px; + margin-top: -13px; + border: 13px solid transparent; + border-left-color: #fff; + display: block; + content: ' '; +} + +/* Disable pointers at responsive sizes */ +@media screen and ( max-width: 782px ) { + .wp-pointer { + display: none; + } +} diff --git a/wp-includes/css/wp-pointer-rtl.min.css b/wp-includes/css/wp-pointer-rtl.min.css new file mode 100644 index 0000000..fe9343d --- /dev/null +++ b/wp-includes/css/wp-pointer-rtl.min.css @@ -0,0 +1 @@ +.wp-pointer-content{padding:0 0 10px;position:relative;font-size:13px;background:#fff;border:1px solid #dfdfdf;-webkit-box-shadow:0 3px 6px rgba(0,0,0,.075);box-shadow:0 3px 6px rgba(0,0,0,.075)}.wp-pointer-content h3{position:relative;margin:-1px -1px 5px;padding:15px 60px 14px 18px;border:1px solid #3592b6;border-bottom:none;line-height:1.4em;font-size:14px;color:#fff;background:#2ea2cc}.wp-pointer-content h3:before{background:#fff;-webkit-border-radius:50%;border-radius:50%;color:#2ea2cc;content:'\f227';font:400 20px/1.6 dashicons;position:absolute;top:8px;right:15px;speak:none;text-align:center;width:32px;height:32px;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.wp-pointer-content p{padding:0 15px}.wp-pointer-buttons{margin:0;padding:5px 15px;overflow:auto}.wp-pointer-buttons a{float:left;display:inline-block;text-decoration:none}.wp-pointer-buttons a.close{padding-right:3px;position:relative}.wp-pointer-buttons a.close:before{background:0 0;color:#bbb;content:'\f153';display:block!important;font:400 13px/1 dashicons;speak:none;margin:1px 0;text-align:center;-webkit-font-smoothing:antialiased!important;width:10px;height:100%;position:absolute;right:-12px;top:1px}.wp-pointer-buttons a.close:hover:before{color:#c00}.wp-pointer-arrow,.wp-pointer-arrow-inner{position:absolute;width:0;height:0}.wp-pointer-arrow{z-index:10;width:0;height:0;border:0 solid transparent}.wp-pointer-arrow-inner{z-index:20}.wp-pointer-top,.wp-pointer-undefined{padding-top:13px}.wp-pointer-bottom{margin-top:-13px;padding-bottom:13px}.wp-pointer-left{padding-left:13px}.wp-pointer-right{margin-left:-13px;padding-right:13px}.wp-pointer-bottom .wp-pointer-arrow,.wp-pointer-top .wp-pointer-arrow,.wp-pointer-undefined .wp-pointer-arrow{right:50px}.wp-pointer-left .wp-pointer-arrow,.wp-pointer-right .wp-pointer-arrow{top:50%;margin-top:-15px}.wp-pointer-top .wp-pointer-arrow,.wp-pointer-undefined .wp-pointer-arrow{top:0;border-width:0 13px 13px;border-bottom-color:#3592b6}.wp-pointer-top .wp-pointer-arrow-inner,.wp-pointer-undefined .wp-pointer-arrow-inner{top:1px;margin-right:-13px;margin-top:-13px;border:13px solid transparent;border-bottom-color:#2ea2cc;display:block;content:' '}.wp-pointer-bottom .wp-pointer-arrow{bottom:0;border-width:13px 13px 0;border-top-color:#ccc}.wp-pointer-bottom .wp-pointer-arrow-inner{bottom:1px;margin-right:-13px;margin-bottom:-13px;border:13px solid transparent;border-top-color:#fff;display:block;content:' '}.wp-pointer-left .wp-pointer-arrow{left:0;border-width:13px 13px 13px 0;border-right-color:#ccc}.wp-pointer-left .wp-pointer-arrow-inner{left:1px;margin-left:-13px;margin-top:-13px;border:13px solid transparent;border-right-color:#fff;display:block;content:' '}.wp-pointer-right .wp-pointer-arrow{right:0;border-width:13px 0 13px 13px;border-left-color:#ccc}.wp-pointer-right .wp-pointer-arrow-inner{right:1px;margin-right:-13px;margin-top:-13px;border:13px solid transparent;border-left-color:#fff;display:block;content:' '}@media screen and (max-width:782px){.wp-pointer{display:none}} \ No newline at end of file diff --git a/wp-includes/css/wp-pointer.css b/wp-includes/css/wp-pointer.css new file mode 100644 index 0000000..1d69823 --- /dev/null +++ b/wp-includes/css/wp-pointer.css @@ -0,0 +1,212 @@ +.wp-pointer-content { + padding: 0 0 10px; + position: relative; + font-size: 13px; + background: #fff; + border: 1px solid #dfdfdf; + -webkit-box-shadow: 0 3px 6px rgba(0,0,0,0.075); + box-shadow: 0 3px 6px rgba(0,0,0,0.075); +} + +.wp-pointer-content h3 { + position: relative; + margin: -1px -1px 5px; + padding: 15px 18px 14px 60px; + border: 1px solid #3592b6; + border-bottom: none; + line-height: 1.4em; + font-size: 14px; + color: #fff; + background: #2ea2cc; +} + +.wp-pointer-content h3:before { + background: #fff; + -webkit-border-radius: 50%; + border-radius: 50%; + color: #2ea2cc; + content: '\f227'; + font: normal 20px/1.6 'dashicons'; + position: absolute; + top: 8px; + left: 15px; + speak: none; + text-align: center; + width: 32px; + height: 32px; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.wp-pointer-content p { + padding: 0 15px; +} + +.wp-pointer-buttons { + margin: 0; + padding: 5px 15px; + overflow: auto; +} + +.wp-pointer-buttons a { + float: right; + display: inline-block; + text-decoration: none; +} + +.wp-pointer-buttons a.close { + padding-left: 3px; + position: relative; +} + +.wp-pointer-buttons a.close:before { + background: none; + color: #bbb; + content: '\f153'; + display: block !important; + font: normal 13px/1 'dashicons'; + speak: none; + margin: 1px 0; + text-align: center; + -webkit-font-smoothing: antialiased !important; + width: 10px; + height: 100%; + position: absolute; + left: -12px; + top: 1px; +} + +.wp-pointer-buttons a.close:hover:before { + color: #c00; +} + +/* The arrow base class must take up no space, even with transparent borders. */ +.wp-pointer-arrow, +.wp-pointer-arrow-inner { + position: absolute; + width: 0; + height: 0; +} + +.wp-pointer-arrow { + z-index: 10; + width: 0; + height: 0; + border: 0 solid transparent; +} + +.wp-pointer-arrow-inner { + z-index: 20; +} + +/* Make Room for the Arrow! */ +.wp-pointer-top, +.wp-pointer-undefined { + padding-top: 13px; +} + +.wp-pointer-bottom { + margin-top: -13px; + padding-bottom: 13px; +} + +/* @noflip */ +.wp-pointer-left { + padding-left: 13px; +} +/* @noflip */ +.wp-pointer-right { + margin-left: -13px; + padding-right: 13px; +} + +/* Base Size & Positioning */ +.wp-pointer-top .wp-pointer-arrow, +.wp-pointer-bottom .wp-pointer-arrow, +.wp-pointer-undefined .wp-pointer-arrow { + left: 50px; +} + +.wp-pointer-left .wp-pointer-arrow, +.wp-pointer-right .wp-pointer-arrow { + top: 50%; + margin-top: -15px; +} + +/* Arrow Sprite */ +.wp-pointer-top .wp-pointer-arrow, +.wp-pointer-undefined .wp-pointer-arrow { + top: 0; + border-width: 0 13px 13px 13px; + border-bottom-color: #3592b6; +} + +.wp-pointer-top .wp-pointer-arrow-inner, +.wp-pointer-undefined .wp-pointer-arrow-inner { + top: 1px; + margin-left: -13px; + margin-top: -13px; + border: 13px solid transparent; + border-bottom-color: #2ea2cc; + display: block; + content: ' '; +} + +.wp-pointer-bottom .wp-pointer-arrow { + bottom: 0; + border-width: 13px 13px 0 13px; + border-top-color: #ccc; +} + +.wp-pointer-bottom .wp-pointer-arrow-inner { + bottom: 1px; + margin-left: -13px; + margin-bottom: -13px; + border: 13px solid transparent; + border-top-color: #fff; + display: block; + content: ' '; +} + +/* @noflip */ +.wp-pointer-left .wp-pointer-arrow { + left: 0; + border-width: 13px 13px 13px 0; + border-right-color: #ccc; +} + +/* @noflip */ +.wp-pointer-left .wp-pointer-arrow-inner { + left: 1px; + margin-left: -13px; + margin-top: -13px; + border: 13px solid transparent; + border-right-color: #fff; + display: block; + content: ' '; +} + +/* @noflip */ +.wp-pointer-right .wp-pointer-arrow { + right: 0; + border-width: 13px 0 13px 13px; + border-left-color: #ccc; +} + +/* @noflip */ +.wp-pointer-right .wp-pointer-arrow-inner { + right: 1px; + margin-right: -13px; + margin-top: -13px; + border: 13px solid transparent; + border-left-color: #fff; + display: block; + content: ' '; +} + +/* Disable pointers at responsive sizes */ +@media screen and ( max-width: 782px ) { + .wp-pointer { + display: none; + } +} diff --git a/wp-includes/css/wp-pointer.min.css b/wp-includes/css/wp-pointer.min.css new file mode 100644 index 0000000..01f27d6 --- /dev/null +++ b/wp-includes/css/wp-pointer.min.css @@ -0,0 +1 @@ +.wp-pointer-content{padding:0 0 10px;position:relative;font-size:13px;background:#fff;border:1px solid #dfdfdf;-webkit-box-shadow:0 3px 6px rgba(0,0,0,.075);box-shadow:0 3px 6px rgba(0,0,0,.075)}.wp-pointer-content h3{position:relative;margin:-1px -1px 5px;padding:15px 18px 14px 60px;border:1px solid #3592b6;border-bottom:none;line-height:1.4em;font-size:14px;color:#fff;background:#2ea2cc}.wp-pointer-content h3:before{background:#fff;-webkit-border-radius:50%;border-radius:50%;color:#2ea2cc;content:'\f227';font:400 20px/1.6 dashicons;position:absolute;top:8px;left:15px;speak:none;text-align:center;width:32px;height:32px;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.wp-pointer-content p{padding:0 15px}.wp-pointer-buttons{margin:0;padding:5px 15px;overflow:auto}.wp-pointer-buttons a{float:right;display:inline-block;text-decoration:none}.wp-pointer-buttons a.close{padding-left:3px;position:relative}.wp-pointer-buttons a.close:before{background:0 0;color:#bbb;content:'\f153';display:block!important;font:400 13px/1 dashicons;speak:none;margin:1px 0;text-align:center;-webkit-font-smoothing:antialiased!important;width:10px;height:100%;position:absolute;left:-12px;top:1px}.wp-pointer-buttons a.close:hover:before{color:#c00}.wp-pointer-arrow,.wp-pointer-arrow-inner{position:absolute;width:0;height:0}.wp-pointer-arrow{z-index:10;width:0;height:0;border:0 solid transparent}.wp-pointer-arrow-inner{z-index:20}.wp-pointer-top,.wp-pointer-undefined{padding-top:13px}.wp-pointer-bottom{margin-top:-13px;padding-bottom:13px}.wp-pointer-left{padding-left:13px}.wp-pointer-right{margin-left:-13px;padding-right:13px}.wp-pointer-bottom .wp-pointer-arrow,.wp-pointer-top .wp-pointer-arrow,.wp-pointer-undefined .wp-pointer-arrow{left:50px}.wp-pointer-left .wp-pointer-arrow,.wp-pointer-right .wp-pointer-arrow{top:50%;margin-top:-15px}.wp-pointer-top .wp-pointer-arrow,.wp-pointer-undefined .wp-pointer-arrow{top:0;border-width:0 13px 13px;border-bottom-color:#3592b6}.wp-pointer-top .wp-pointer-arrow-inner,.wp-pointer-undefined .wp-pointer-arrow-inner{top:1px;margin-left:-13px;margin-top:-13px;border:13px solid transparent;border-bottom-color:#2ea2cc;display:block;content:' '}.wp-pointer-bottom .wp-pointer-arrow{bottom:0;border-width:13px 13px 0;border-top-color:#ccc}.wp-pointer-bottom .wp-pointer-arrow-inner{bottom:1px;margin-left:-13px;margin-bottom:-13px;border:13px solid transparent;border-top-color:#fff;display:block;content:' '}.wp-pointer-left .wp-pointer-arrow{left:0;border-width:13px 13px 13px 0;border-right-color:#ccc}.wp-pointer-left .wp-pointer-arrow-inner{left:1px;margin-left:-13px;margin-top:-13px;border:13px solid transparent;border-right-color:#fff;display:block;content:' '}.wp-pointer-right .wp-pointer-arrow{right:0;border-width:13px 0 13px 13px;border-left-color:#ccc}.wp-pointer-right .wp-pointer-arrow-inner{right:1px;margin-right:-13px;margin-top:-13px;border:13px solid transparent;border-left-color:#fff;display:block;content:' '}@media screen and (max-width:782px){.wp-pointer{display:none}} \ No newline at end of file diff --git a/wp-includes/date.php b/wp-includes/date.php new file mode 100644 index 0000000..9869611 --- /dev/null +++ b/wp-includes/date.php @@ -0,0 +1,1005 @@ +', '>=', '<', '<=', 'IN', 'NOT IN'. Default '='. + * 'BETWEEN', 'NOT BETWEEN'. + * @type string $relation Optional. The boolean relationship between the date queries. + * Accepts 'OR', 'AND'. Default 'OR'. + * @type array { + * Optional. An array of first-order clause parameters, or another fully-formed date query. + * + * @type string|array $before Optional. Date to retrieve posts before. Accepts strtotime()-compatible + * string, or array of 'year', 'month', 'day' values. { + * + * @type string $year The four-digit year. Default empty. Accepts any four-digit year. + * @type string $month Optional when passing array.The month of the year. + * Default (string:empty)|(array:1). Accepts numbers 1-12. + * @type string $day Optional when passing array.The day of the month. + * Default (string:empty)|(array:1). Accepts numbers 1-31. + * } + * @type string|array $after Optional. Date to retrieve posts after. Accepts strtotime()-compatible + * string, or array of 'year', 'month', 'day' values. { + * + * @type string $year The four-digit year. Default empty. Accepts any four-digit year. + * @type string $month Optional when passing array.The month of the year. + * Default (string:empty)|(array:12). Accepts numbers 1-12. + * @type string $day Optional when passing array.The day of the month. + * Default (string:empty)|(array:last day of month). Accepts numbers 1-31. + * } + * @type string $column Optional. Used to add a clause comparing a column other than the column + * specified in the top-level $column parameter. Default is the value + * of top-level $column. Accepts 'post_date', 'post_date_gmt', + * 'post_modified', 'post_modified_gmt', 'comment_date', 'comment_date_gmt'. + * @type string $compare Optional. The comparison operator. Default '='. + * Accepts '=', '!=', '>', '>=', '<', '<=', 'IN', 'NOT IN', + * 'BETWEEN', 'NOT BETWEEN'. + * @type bool $inclusive Optional. Include results from dates specified in 'before' or + * 'after'. Default false. + * @type int $year Optional. The four-digit year number. Default empty. Accepts + * any four-digit year. + * @type int $month Optional. The two-digit month number. Default empty. + * Accepts numbers 1-12. + * @type int $week Optional. The week number of the year. Default empty. + * Accepts numbers 0-53. + * @type int $dayofyear Optional. The day number of the year. Default empty. + * Accepts numbers 1-366. + * @type int $day Optional. The day of the month. Default empty. + * Accepts numbers 1-31. + * @type int $dayofweek Optional. The day number of the week. Default empty. + * Accepts numbers 1-7 (1 is Sunday). + * @type int $dayofweek_iso Optional. The day number of the week (ISO). Accepts numbers 1-7 + * (1 is Monday). Default empty. + * @type int $hour Optional. The hour of the day. Default empty. Accepts numbers 0-23. + * @type int $minute Optional. The minute of the hour. Default empty. Accepts + * numbers 0-60. + * @type int $second Optional. The second of the minute. Default empty. + * Accepts numbers 0-60. + * } + * } + * } + * @param array $default_column Optional. Default column to query against. Default 'post_date'. + * Accepts 'post_date', 'post_date_gmt', 'post_modified', 'post_modified_gmt', + * 'comment_date', 'comment_date_gmt'. + */ + public function __construct( $date_query, $default_column = 'post_date' ) { + + if ( isset( $date_query['relation'] ) && 'OR' === strtoupper( $date_query['relation'] ) ) { + $this->relation = 'OR'; + } else { + $this->relation = 'AND'; + } + + if ( ! is_array( $date_query ) ) { + return; + } + + // Support for passing time-based keys in the top level of the $date_query array. + if ( ! isset( $date_query[0] ) && ! empty( $date_query ) ) { + $date_query = array( $date_query ); + } + + if ( empty( $date_query ) ) { + return; + } + + if ( ! empty( $date_query['column'] ) ) { + $date_query['column'] = esc_sql( $date_query['column'] ); + } else { + $date_query['column'] = esc_sql( $default_column ); + } + + $this->column = $this->validate_column( $this->column ); + + $this->compare = $this->get_compare( $date_query ); + + $this->queries = $this->sanitize_query( $date_query ); + + return; + } + + /** + * Recursive-friendly query sanitizer. + * + * Ensures that each query-level clause has a 'relation' key, and that + * each first-order clause contains all the necessary keys from + * `$defaults`. + * + * @since 4.1.0 + * @access public + * + * @param array $queries + * @param array $parent_query + * + * @return array Sanitized queries. + */ + public function sanitize_query( $queries, $parent_query = null ) { + $cleaned_query = array(); + + $defaults = array( + 'column' => 'post_date', + 'compare' => '=', + 'relation' => 'AND', + ); + + // Numeric keys should always have array values. + foreach ( $queries as $qkey => $qvalue ) { + if ( is_numeric( $qkey ) && ! is_array( $qvalue ) ) { + unset( $queries[ $qkey ] ); + } + } + + // Each query should have a value for each default key. Inherit from the parent when possible. + foreach ( $defaults as $dkey => $dvalue ) { + if ( isset( $queries[ $dkey ] ) ) { + continue; + } + + if ( isset( $parent_query[ $dkey ] ) ) { + $queries[ $dkey ] = $parent_query[ $dkey ]; + } else { + $queries[ $dkey ] = $dvalue; + } + } + + // Validate the dates passed in the query. + if ( $this->is_first_order_clause( $queries ) ) { + $this->validate_date_values( $queries ); + } + + foreach ( $queries as $key => $q ) { + if ( ! is_array( $q ) || in_array( $key, $this->time_keys, true ) ) { + // This is a first-order query. Trust the values and sanitize when building SQL. + $cleaned_query[ $key ] = $q; + } else { + // Any array without a time key is another query, so we recurse. + $cleaned_query[] = $this->sanitize_query( $q, $queries ); + } + } + + return $cleaned_query; + } + + /** + * Determine whether this is a first-order clause. + * + * Checks to see if the current clause has any time-related keys. + * If so, it's first-order. + * + * @param array $query Query clause. + * @return bool True if this is a first-order clause. + */ + protected function is_first_order_clause( $query ) { + $time_keys = array_intersect( $this->time_keys, array_keys( $query ) ); + return ! empty( $time_keys ); + } + + /** + * Determines and validates what comparison operator to use. + * + * @since 3.7.0 + * @access public + * + * @param array $query A date query or a date subquery. + * @return string The comparison operator. + */ + public function get_compare( $query ) { + if ( ! empty( $query['compare'] ) && in_array( $query['compare'], array( '=', '!=', '>', '>=', '<', '<=', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN' ) ) ) + return strtoupper( $query['compare'] ); + + return $this->compare; + } + + /** + * Validates the given date_query values and triggers errors if something is not valid. + * + * Note that date queries with invalid date ranges are allowed to + * continue (though of course no items will be found for impossible dates). + * This method only generates debug notices for these cases. + * + * @since 4.1.0 + * @access public + * + * @param array $date_query The date_query array. + * @return bool True if all values in the query are valid, false if one or more fail. + */ + public function validate_date_values( $date_query = array() ) { + if ( empty( $date_query ) ) { + return false; + } + + $valid = true; + + /* + * Validate 'before' and 'after' up front, then let the + * validation routine continue to be sure that all invalid + * values generate errors too. + */ + if ( array_key_exists( 'before', $date_query ) && is_array( $date_query['before'] ) ){ + $valid = $this->validate_date_values( $date_query['before'] ); + } + + if ( array_key_exists( 'after', $date_query ) && is_array( $date_query['after'] ) ){ + $valid = $this->validate_date_values( $date_query['after'] ); + } + + // Array containing all min-max checks. + $min_max_checks = array(); + + // Days per year. + if ( array_key_exists( 'year', $date_query ) ) { + /* + * If a year exists in the date query, we can use it to get the days. + * If multiple years are provided (as in a BETWEEN), use the first one. + */ + if ( is_array( $date_query['year'] ) ) { + $_year = reset( $date_query['year'] ); + } else { + $_year = $date_query['year']; + } + + $max_days_of_year = date( 'z', mktime( 0, 0, 0, 12, 31, $_year ) ) + 1; + } else { + // otherwise we use the max of 366 (leap-year) + $max_days_of_year = 366; + } + + $min_max_checks['dayofyear'] = array( + 'min' => 1, + 'max' => $max_days_of_year + ); + + // Days per week. + $min_max_checks['dayofweek'] = array( + 'min' => 1, + 'max' => 7 + ); + + // Days per week. + $min_max_checks['dayofweek_iso'] = array( + 'min' => 1, + 'max' => 7 + ); + + // Months per year. + $min_max_checks['month'] = array( + 'min' => 1, + 'max' => 12 + ); + + // Weeks per year. + if ( isset( $_year ) ) { + // If we have a specific year, use it to calculate number of weeks. + $date = new DateTime(); + $date->setISODate( $_year, 53 ); + $week_count = $date->format( "W" ) === "53" ? 53 : 52; + + } else { + // Otherwise set the week-count to a maximum of 53. + $week_count = 53; + } + + $min_max_checks['week'] = array( + 'min' => 1, + 'max' => $week_count + ); + + // Days per month. + $min_max_checks['day'] = array( + 'min' => 1, + 'max' => 31 + ); + + // Hours per day. + $min_max_checks['hour'] = array( + 'min' => 0, + 'max' => 23 + ); + + // Minutes per hour. + $min_max_checks['minute'] = array( + 'min' => 0, + 'max' => 59 + ); + + // Seconds per minute. + $min_max_checks['second'] = array( + 'min' => 0, + 'max' => 59 + ); + + // Concatenate and throw a notice for each invalid value. + foreach ( $min_max_checks as $key => $check ) { + if ( ! array_key_exists( $key, $date_query ) ) { + continue; + } + + // Throw a notice for each failing value. + $is_between = true; + foreach ( (array) $date_query[ $key ] as $_value ) { + $is_between = $_value >= $check['min'] && $_value <= $check['max']; + + if ( ! is_numeric( $_value ) || ! $is_between ) { + $error = sprintf( + /* translators: Date query invalid date message: 1: invalid value, 2: type of value, 3: minimum valid value, 4: maximum valid value */ + __( 'Invalid value %1$s for %2$s. Expected value should be between %3$s and %4$s.' ), + '' . esc_html( $_value ) . '', + '' . esc_html( $key ) . '', + '' . esc_html( $check['min'] ) . '', + '' . esc_html( $check['max'] ) . '' + ); + + _doing_it_wrong( __CLASS__, $error, '4.1.0' ); + + $valid = false; + } + } + } + + // If we already have invalid date messages, don't bother running through checkdate(). + if ( ! $valid ) { + return $valid; + } + + $day_month_year_error_msg = ''; + + $day_exists = array_key_exists( 'day', $date_query ) && is_numeric( $date_query['day'] ); + $month_exists = array_key_exists( 'month', $date_query ) && is_numeric( $date_query['month'] ); + $year_exists = array_key_exists( 'year', $date_query ) && is_numeric( $date_query['year'] ); + + if ( $day_exists && $month_exists && $year_exists ) { + // 1. Checking day, month, year combination. + if ( ! wp_checkdate( $date_query['month'], $date_query['day'], $date_query['year'], sprintf( '%s-%s-%s', $date_query['year'], $date_query['month'], $date_query['day'] ) ) ) { + /* translators: 1: year, 2: month, 3: day of month */ + $day_month_year_error_msg = sprintf( + __( 'The following values do not describe a valid date: year %1$s, month %2$s, day %3$s.' ), + '' . esc_html( $date_query['year'] ) . '', + '' . esc_html( $date_query['month'] ) . '', + '' . esc_html( $date_query['day'] ) . '' + ); + + $valid = false; + } + + } else if ( $day_exists && $month_exists ) { + /* + * 2. checking day, month combination + * We use 2012 because, as a leap year, it's the most permissive. + */ + if ( ! wp_checkdate( $date_query['month'], $date_query['day'], 2012, sprintf( '2012-%s-%s', $date_query['month'], $date_query['day'] ) ) ) { + /* translators: 1: month, 2: day of month */ + $day_month_year_error_msg = sprintf( + __( 'The following values do not describe a valid date: month %1$s, day %2$s.' ), + '' . esc_html( $date_query['month'] ) . '', + '' . esc_html( $date_query['day'] ) . '' + ); + + $valid = false; + } + } + + if ( ! empty( $day_month_year_error_msg ) ) { + _doing_it_wrong( __CLASS__, $day_month_year_error_msg, '4.1.0' ); + } + + return $valid; + } + + /** + * Validates a column name parameter. + * + * Column names without a table prefix (like 'post_date') are checked against a whitelist of + * known tables, and then, if found, have a table prefix (such as 'wp_posts.') prepended. + * Prefixed column names (such as 'wp_posts.post_date') bypass this whitelist check, + * and are only sanitized to remove illegal characters. + * + * @since 3.7.0 + * @access public + * + * @param string $column The user-supplied column name. + * @return string A validated column name value. + */ + public function validate_column( $column ) { + global $wpdb; + + $valid_columns = array( + 'post_date', 'post_date_gmt', 'post_modified', + 'post_modified_gmt', 'comment_date', 'comment_date_gmt', + 'user_registered', + ); + + // Attempt to detect a table prefix. + if ( false === strpos( $column, '.' ) ) { + /** + * Filter the list of valid date query columns. + * + * @since 3.7.0 + * @since 4.1.0 Added 'user_registered' to the default recognized columns. + * + * @param array $valid_columns An array of valid date query columns. Defaults + * are 'post_date', 'post_date_gmt', 'post_modified', + * 'post_modified_gmt', 'comment_date', 'comment_date_gmt', + * 'user_registered' + */ + if ( ! in_array( $column, apply_filters( 'date_query_valid_columns', $valid_columns ) ) ) { + $column = 'post_date'; + } + + $known_columns = array( + $wpdb->posts => array( + 'post_date', + 'post_date_gmt', + 'post_modified', + 'post_modified_gmt', + ), + $wpdb->comments => array( + 'comment_date', + 'comment_date_gmt', + ), + $wpdb->users => array( + 'user_registered', + ), + ); + + // If it's a known column name, add the appropriate table prefix. + foreach ( $known_columns as $table_name => $table_columns ) { + if ( in_array( $column, $table_columns ) ) { + $column = $table_name . '.' . $column; + break; + } + } + + } + + // Remove unsafe characters. + return preg_replace( '/[^a-zA-Z0-9_$\.]/', '', $column ); + } + + /** + * Generate WHERE clause to be appended to a main query. + * + * @since 3.7.0 + * @access public + * + * @return string MySQL WHERE clause. + */ + public function get_sql() { + $sql = $this->get_sql_clauses(); + + $where = $sql['where']; + + /** + * Filter the date query WHERE clause. + * + * @since 3.7.0 + * + * @param string $where WHERE clause of the date query. + * @param WP_Date_Query $this The WP_Date_Query instance. + */ + return apply_filters( 'get_date_sql', $where, $this ); + } + + /** + * Generate SQL clauses to be appended to a main query. + * + * Called by the public {@see WP_Date_Query::get_sql()}, this method + * is abstracted out to maintain parity with the other Query classes. + * + * @since 4.1.0 + * @access protected + * + * @return array { + * Array containing JOIN and WHERE SQL clauses to append to the main query. + * + * @type string $join SQL fragment to append to the main JOIN clause. + * @type string $where SQL fragment to append to the main WHERE clause. + * } + */ + protected function get_sql_clauses() { + $sql = $this->get_sql_for_query( $this->queries ); + + if ( ! empty( $sql['where'] ) ) { + $sql['where'] = ' AND ' . $sql['where']; + } + + return $sql; + } + + /** + * Generate SQL clauses for a single query array. + * + * If nested subqueries are found, this method recurses the tree to + * produce the properly nested SQL. + * + * @since 4.1.0 + * @access protected + * + * @param array $query Query to parse. + * @param int $depth Optional. Number of tree levels deep we currently are. + * Used to calculate indentation. Default 0. + * @return array { + * Array containing JOIN and WHERE SQL clauses to append to a single query array. + * + * @type string $join SQL fragment to append to the main JOIN clause. + * @type string $where SQL fragment to append to the main WHERE clause. + * } + */ + protected function get_sql_for_query( $query, $depth = 0 ) { + $sql_chunks = array( + 'join' => array(), + 'where' => array(), + ); + + $sql = array( + 'join' => '', + 'where' => '', + ); + + $indent = ''; + for ( $i = 0; $i < $depth; $i++ ) { + $indent .= " "; + } + + foreach ( $query as $key => $clause ) { + if ( 'relation' === $key ) { + $relation = $query['relation']; + } else if ( is_array( $clause ) ) { + + // This is a first-order clause. + if ( $this->is_first_order_clause( $clause ) ) { + $clause_sql = $this->get_sql_for_clause( $clause, $query ); + + $where_count = count( $clause_sql['where'] ); + if ( ! $where_count ) { + $sql_chunks['where'][] = ''; + } else if ( 1 === $where_count ) { + $sql_chunks['where'][] = $clause_sql['where'][0]; + } else { + $sql_chunks['where'][] = '( ' . implode( ' AND ', $clause_sql['where'] ) . ' )'; + } + + $sql_chunks['join'] = array_merge( $sql_chunks['join'], $clause_sql['join'] ); + // This is a subquery, so we recurse. + } else { + $clause_sql = $this->get_sql_for_query( $clause, $depth + 1 ); + + $sql_chunks['where'][] = $clause_sql['where']; + $sql_chunks['join'][] = $clause_sql['join']; + } + } + } + + // Filter to remove empties. + $sql_chunks['join'] = array_filter( $sql_chunks['join'] ); + $sql_chunks['where'] = array_filter( $sql_chunks['where'] ); + + if ( empty( $relation ) ) { + $relation = 'AND'; + } + + // Filter duplicate JOIN clauses and combine into a single string. + if ( ! empty( $sql_chunks['join'] ) ) { + $sql['join'] = implode( ' ', array_unique( $sql_chunks['join'] ) ); + } + + // Generate a single WHERE clause with proper brackets and indentation. + if ( ! empty( $sql_chunks['where'] ) ) { + $sql['where'] = '( ' . "\n " . $indent . implode( ' ' . "\n " . $indent . $relation . ' ' . "\n " . $indent, $sql_chunks['where'] ) . "\n" . $indent . ')'; + } + + return $sql; + } + + /** + * Turns a single date clause into pieces for a WHERE clause. + * + * A wrapper for get_sql_for_clause(), included here for backward + * compatibility while retaining the naming convention across Query classes. + * + * @since 3.7.0 + * @access protected + * + * @param array $query Date query arguments. + * @return array { + * Array containing JOIN and WHERE SQL clauses to append to the main query. + * + * @type string $join SQL fragment to append to the main JOIN clause. + * @type string $where SQL fragment to append to the main WHERE clause. + * } + */ + protected function get_sql_for_subquery( $query ) { + return $this->get_sql_for_clause( $query, '' ); + } + + /** + * Turns a first-order date query into SQL for a WHERE clause. + * + * @since 4.1.0 + * @access protected + * + * @param array $query Date query clause. + * @param array $parent_query Parent query of the current date query. + * @return array { + * Array containing JOIN and WHERE SQL clauses to append to the main query. + * + * @type string $join SQL fragment to append to the main JOIN clause. + * @type string $where SQL fragment to append to the main WHERE clause. + * } + */ + protected function get_sql_for_clause( $query, $parent_query ) { + global $wpdb; + + // The sub-parts of a $where part. + $where_parts = array(); + + $column = ( ! empty( $query['column'] ) ) ? esc_sql( $query['column'] ) : $this->column; + + $column = $this->validate_column( $column ); + + $compare = $this->get_compare( $query ); + + $inclusive = ! empty( $query['inclusive'] ); + + // Assign greater- and less-than values. + $lt = '<'; + $gt = '>'; + + if ( $inclusive ) { + $lt .= '='; + $gt .= '='; + } + + // Range queries. + if ( ! empty( $query['after'] ) ) + $where_parts[] = $wpdb->prepare( "$column $gt %s", $this->build_mysql_datetime( $query['after'], ! $inclusive ) ); + + if ( ! empty( $query['before'] ) ) + $where_parts[] = $wpdb->prepare( "$column $lt %s", $this->build_mysql_datetime( $query['before'], $inclusive ) ); + + // Specific value queries. + + if ( isset( $query['year'] ) && $value = $this->build_value( $compare, $query['year'] ) ) + $where_parts[] = "YEAR( $column ) $compare $value"; + + if ( isset( $query['month'] ) && $value = $this->build_value( $compare, $query['month'] ) ) + $where_parts[] = "MONTH( $column ) $compare $value"; + else if ( isset( $query['monthnum'] ) && $value = $this->build_value( $compare, $query['monthnum'] ) ) + $where_parts[] = "MONTH( $column ) $compare $value"; + + if ( isset( $query['week'] ) && false !== ( $value = $this->build_value( $compare, $query['week'] ) ) ) + $where_parts[] = _wp_mysql_week( $column ) . " $compare $value"; + else if ( isset( $query['w'] ) && false !== ( $value = $this->build_value( $compare, $query['w'] ) ) ) + $where_parts[] = _wp_mysql_week( $column ) . " $compare $value"; + + if ( isset( $query['dayofyear'] ) && $value = $this->build_value( $compare, $query['dayofyear'] ) ) + $where_parts[] = "DAYOFYEAR( $column ) $compare $value"; + + if ( isset( $query['day'] ) && $value = $this->build_value( $compare, $query['day'] ) ) + $where_parts[] = "DAYOFMONTH( $column ) $compare $value"; + + if ( isset( $query['dayofweek'] ) && $value = $this->build_value( $compare, $query['dayofweek'] ) ) + $where_parts[] = "DAYOFWEEK( $column ) $compare $value"; + + if ( isset( $query['dayofweek_iso'] ) && $value = $this->build_value( $compare, $query['dayofweek_iso'] ) ) + $where_parts[] = "WEEKDAY( $column ) + 1 $compare $value"; + + if ( isset( $query['hour'] ) || isset( $query['minute'] ) || isset( $query['second'] ) ) { + // Avoid notices. + foreach ( array( 'hour', 'minute', 'second' ) as $unit ) { + if ( ! isset( $query[ $unit ] ) ) { + $query[ $unit ] = null; + } + } + + if ( $time_query = $this->build_time_query( $column, $compare, $query['hour'], $query['minute'], $query['second'] ) ) { + $where_parts[] = $time_query; + } + } + + /* + * Return an array of 'join' and 'where' for compatibility + * with other query classes. + */ + return array( + 'where' => $where_parts, + 'join' => array(), + ); + } + + /** + * Builds and validates a value string based on the comparison operator. + * + * @since 3.7.0 + * @access public + * + * @param string $compare The compare operator to use + * @param string|array $value The value + * @return string|false|int The value to be used in SQL or false on error. + */ + public function build_value( $compare, $value ) { + if ( ! isset( $value ) ) + return false; + + switch ( $compare ) { + case 'IN': + case 'NOT IN': + $value = (array) $value; + + // Remove non-numeric values. + $value = array_filter( $value, 'is_numeric' ); + + if ( empty( $value ) ) { + return false; + } + + return '(' . implode( ',', array_map( 'intval', $value ) ) . ')'; + + case 'BETWEEN': + case 'NOT BETWEEN': + if ( ! is_array( $value ) || 2 != count( $value ) ) { + $value = array( $value, $value ); + } else { + $value = array_values( $value ); + } + + // If either value is non-numeric, bail. + foreach ( $value as $v ) { + if ( ! is_numeric( $v ) ) { + return false; + } + } + + $value = array_map( 'intval', $value ); + + return $value[0] . ' AND ' . $value[1]; + + default; + if ( ! is_numeric( $value ) ) { + return false; + } + + return (int) $value; + } + } + + /** + * Builds a MySQL format date/time based on some query parameters. + * + * You can pass an array of values (year, month, etc.) with missing parameter values being defaulted to + * either the maximum or minimum values (controlled by the $default_to parameter). Alternatively you can + * pass a string that that will be run through strtotime(). + * + * @since 3.7.0 + * @access public + * + * @param string|array $datetime An array of parameters or a strotime() string + * @param bool $default_to_max Whether to round up incomplete dates. Supported by values + * of $datetime that are arrays, or string values that are a + * subset of MySQL date format ('Y', 'Y-m', 'Y-m-d', 'Y-m-d H:i'). + * Default: false. + * @return string|false A MySQL format date/time or false on failure + */ + public function build_mysql_datetime( $datetime, $default_to_max = false ) { + $now = current_time( 'timestamp' ); + + if ( ! is_array( $datetime ) ) { + + /* + * Try to parse some common date formats, so we can detect + * the level of precision and support the 'inclusive' parameter. + */ + if ( preg_match( '/^(\d{4})$/', $datetime, $matches ) ) { + // Y + $datetime = array( + 'year' => intval( $matches[1] ), + ); + + } else if ( preg_match( '/^(\d{4})\-(\d{2})$/', $datetime, $matches ) ) { + // Y-m + $datetime = array( + 'year' => intval( $matches[1] ), + 'month' => intval( $matches[2] ), + ); + + } else if ( preg_match( '/^(\d{4})\-(\d{2})\-(\d{2})$/', $datetime, $matches ) ) { + // Y-m-d + $datetime = array( + 'year' => intval( $matches[1] ), + 'month' => intval( $matches[2] ), + 'day' => intval( $matches[3] ), + ); + + } else if ( preg_match( '/^(\d{4})\-(\d{2})\-(\d{2}) (\d{2}):(\d{2})$/', $datetime, $matches ) ) { + // Y-m-d H:i + $datetime = array( + 'year' => intval( $matches[1] ), + 'month' => intval( $matches[2] ), + 'day' => intval( $matches[3] ), + 'hour' => intval( $matches[4] ), + 'minute' => intval( $matches[5] ), + ); + } + + // If no match is found, we don't support default_to_max. + if ( ! is_array( $datetime ) ) { + // @todo Timezone issues here possibly + return gmdate( 'Y-m-d H:i:s', strtotime( $datetime, $now ) ); + } + } + + $datetime = array_map( 'absint', $datetime ); + + if ( ! isset( $datetime['year'] ) ) + $datetime['year'] = gmdate( 'Y', $now ); + + if ( ! isset( $datetime['month'] ) ) + $datetime['month'] = ( $default_to_max ) ? 12 : 1; + + if ( ! isset( $datetime['day'] ) ) + $datetime['day'] = ( $default_to_max ) ? (int) date( 't', mktime( 0, 0, 0, $datetime['month'], 1, $datetime['year'] ) ) : 1; + + if ( ! isset( $datetime['hour'] ) ) + $datetime['hour'] = ( $default_to_max ) ? 23 : 0; + + if ( ! isset( $datetime['minute'] ) ) + $datetime['minute'] = ( $default_to_max ) ? 59 : 0; + + if ( ! isset( $datetime['second'] ) ) + $datetime['second'] = ( $default_to_max ) ? 59 : 0; + + return sprintf( '%04d-%02d-%02d %02d:%02d:%02d', $datetime['year'], $datetime['month'], $datetime['day'], $datetime['hour'], $datetime['minute'], $datetime['second'] ); + } + + /** + * Builds a query string for comparing time values (hour, minute, second). + * + * If just hour, minute, or second is set than a normal comparison will be done. + * However if multiple values are passed, a pseudo-decimal time will be created + * in order to be able to accurately compare against. + * + * @since 3.7.0 + * @access public + * + * @param string $column The column to query against. Needs to be pre-validated! + * @param string $compare The comparison operator. Needs to be pre-validated! + * @param int|null $hour Optional. An hour value (0-23). + * @param int|null $minute Optional. A minute value (0-59). + * @param int|null $second Optional. A second value (0-59). + * @return string|false A query part or false on failure. + */ + public function build_time_query( $column, $compare, $hour = null, $minute = null, $second = null ) { + global $wpdb; + + // Have to have at least one + if ( ! isset( $hour ) && ! isset( $minute ) && ! isset( $second ) ) + return false; + + // Complex combined queries aren't supported for multi-value queries + if ( in_array( $compare, array( 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN' ) ) ) { + $return = array(); + + if ( isset( $hour ) && false !== ( $value = $this->build_value( $compare, $hour ) ) ) + $return[] = "HOUR( $column ) $compare $value"; + + if ( isset( $minute ) && false !== ( $value = $this->build_value( $compare, $minute ) ) ) + $return[] = "MINUTE( $column ) $compare $value"; + + if ( isset( $second ) && false !== ( $value = $this->build_value( $compare, $second ) ) ) + $return[] = "SECOND( $column ) $compare $value"; + + return implode( ' AND ', $return ); + } + + // Cases where just one unit is set + if ( isset( $hour ) && ! isset( $minute ) && ! isset( $second ) && false !== ( $value = $this->build_value( $compare, $hour ) ) ) { + return "HOUR( $column ) $compare $value"; + } elseif ( ! isset( $hour ) && isset( $minute ) && ! isset( $second ) && false !== ( $value = $this->build_value( $compare, $minute ) ) ) { + return "MINUTE( $column ) $compare $value"; + } elseif ( ! isset( $hour ) && ! isset( $minute ) && isset( $second ) && false !== ( $value = $this->build_value( $compare, $second ) ) ) { + return "SECOND( $column ) $compare $value"; + } + + // Single units were already handled. Since hour & second isn't allowed, minute must to be set. + if ( ! isset( $minute ) ) + return false; + + $format = $time = ''; + + // Hour + if ( $hour ) { + $format .= '%H.'; + $time .= sprintf( '%02d', $hour ) . '.'; + } else { + $format .= '0.'; + $time .= '0.'; + } + + // Minute + $format .= '%i'; + $time .= sprintf( '%02d', $minute ); + + if ( isset( $second ) ) { + $format .= '%s'; + $time .= sprintf( '%02d', $second ); + } + + return $wpdb->prepare( "DATE_FORMAT( $column, %s ) $compare %f", $format, $time ); + } +} diff --git a/wp-includes/default-constants.php b/wp-includes/default-constants.php new file mode 100644 index 0000000..ae97efd --- /dev/null +++ b/wp-includes/default-constants.php @@ -0,0 +1,323 @@ + 'widget_pages', 'description' => __( 'A list of your site’s Pages.') ); + parent::__construct('pages', __('Pages'), $widget_ops); + } + + public function widget( $args, $instance ) { + + /** + * Filter the widget title. + * + * @since 2.6.0 + * + * @param string $title The widget title. Default 'Pages'. + * @param array $instance An array of the widget's settings. + * @param mixed $id_base The widget ID. + */ + $title = apply_filters( 'widget_title', empty( $instance['title'] ) ? __( 'Pages' ) : $instance['title'], $instance, $this->id_base ); + + $sortby = empty( $instance['sortby'] ) ? 'menu_order' : $instance['sortby']; + $exclude = empty( $instance['exclude'] ) ? '' : $instance['exclude']; + + if ( $sortby == 'menu_order' ) + $sortby = 'menu_order, post_title'; + + /** + * Filter the arguments for the Pages widget. + * + * @since 2.8.0 + * + * @see wp_list_pages() + * + * @param array $args An array of arguments to retrieve the pages list. + */ + $out = wp_list_pages( apply_filters( 'widget_pages_args', array( + 'title_li' => '', + 'echo' => 0, + 'sort_column' => $sortby, + 'exclude' => $exclude + ) ) ); + + if ( ! empty( $out ) ) { + echo $args['before_widget']; + if ( $title ) { + echo $args['before_title'] . $title . $args['after_title']; + } + ?> +
          + +
        + 'post_title', 'title' => '', 'exclude' => '') ); + $title = esc_attr( $instance['title'] ); + $exclude = esc_attr( $instance['exclude'] ); + ?> +

        +

        + + +

        +

        + +
        + +

        + __( "Your blogroll" ) ); + parent::__construct('links', __('Links'), $widget_ops); + } + + public function widget( $args, $instance ) { + + $show_description = isset($instance['description']) ? $instance['description'] : false; + $show_name = isset($instance['name']) ? $instance['name'] : false; + $show_rating = isset($instance['rating']) ? $instance['rating'] : false; + $show_images = isset($instance['images']) ? $instance['images'] : true; + $category = isset($instance['category']) ? $instance['category'] : false; + $orderby = isset( $instance['orderby'] ) ? $instance['orderby'] : 'name'; + $order = $orderby == 'rating' ? 'DESC' : 'ASC'; + $limit = isset( $instance['limit'] ) ? $instance['limit'] : -1; + + $before_widget = preg_replace( '/id="[^"]*"/', 'id="%id"', $args['before_widget'] ); + + /** + * Filter the arguments for the Links widget. + * + * @since 2.6.0 + * + * @see wp_list_bookmarks() + * + * @param array $args An array of arguments to retrieve the links list. + */ + wp_list_bookmarks( apply_filters( 'widget_links_args', array( + 'title_before' => $args['before_title'], 'title_after' => $args['after_title'], + 'category_before' => $before_widget, 'category_after' => $args['after_widget'], + 'show_images' => $show_images, 'show_description' => $show_description, + 'show_name' => $show_name, 'show_rating' => $show_rating, + 'category' => $category, 'class' => 'linkcat widget', + 'orderby' => $orderby, 'order' => $order, + 'limit' => $limit, + ) ) ); + } + + public function update( $new_instance, $old_instance ) { + $new_instance = (array) $new_instance; + $instance = array( 'images' => 0, 'name' => 0, 'description' => 0, 'rating' => 0 ); + foreach ( $instance as $field => $val ) { + if ( isset($new_instance[$field]) ) + $instance[$field] = 1; + } + + $instance['orderby'] = 'name'; + if ( in_array( $new_instance['orderby'], array( 'name', 'rating', 'id', 'rand' ) ) ) + $instance['orderby'] = $new_instance['orderby']; + + $instance['category'] = intval( $new_instance['category'] ); + $instance['limit'] = ! empty( $new_instance['limit'] ) ? intval( $new_instance['limit'] ) : -1; + + return $instance; + } + + public function form( $instance ) { + + //Defaults + $instance = wp_parse_args( (array) $instance, array( 'images' => true, 'name' => true, 'description' => false, 'rating' => false, 'category' => false, 'orderby' => 'name', 'limit' => -1 ) ); + $link_cats = get_terms( 'link_category' ); + if ( ! $limit = intval( $instance['limit'] ) ) + $limit = -1; +?> +

        + + + + +

        +

        + id="get_field_id('images'); ?>" name="get_field_name('images'); ?>" /> +
        + id="get_field_id('name'); ?>" name="get_field_name('name'); ?>" /> +
        + id="get_field_id('description'); ?>" name="get_field_name('description'); ?>" /> +
        + id="get_field_id('rating'); ?>" name="get_field_name('rating'); ?>" /> + +

        +

        + + +

        + 'widget_search', 'description' => __( "A search form for your site.") ); + parent::__construct( 'search', _x( 'Search', 'Search widget' ), $widget_ops ); + } + + public function widget( $args, $instance ) { + + /** This filter is documented in wp-includes/default-widgets.php */ + $title = apply_filters( 'widget_title', empty( $instance['title'] ) ? '' : $instance['title'], $instance, $this->id_base ); + + echo $args['before_widget']; + if ( $title ) { + echo $args['before_title'] . $title . $args['after_title']; + } + + // Use current theme search form if it exists + get_search_form(); + + echo $args['after_widget']; + } + + public function form( $instance ) { + $instance = wp_parse_args( (array) $instance, array( 'title' => '') ); + $title = $instance['title']; +?> +

        + '')); + $instance['title'] = strip_tags($new_instance['title']); + return $instance; + } + +} + +/** + * Archives widget class + * + * @since 2.8.0 + */ +class WP_Widget_Archives extends WP_Widget { + + public function __construct() { + $widget_ops = array('classname' => 'widget_archive', 'description' => __( 'A monthly archive of your site’s Posts.') ); + parent::__construct('archives', __('Archives'), $widget_ops); + } + + public function widget( $args, $instance ) { + $c = ! empty( $instance['count'] ) ? '1' : '0'; + $d = ! empty( $instance['dropdown'] ) ? '1' : '0'; + + /** This filter is documented in wp-includes/default-widgets.php */ + $title = apply_filters( 'widget_title', empty( $instance['title'] ) ? __( 'Archives' ) : $instance['title'], $instance, $this->id_base ); + + echo $args['before_widget']; + if ( $title ) { + echo $args['before_title'] . $title . $args['after_title']; + } + + if ( $d ) { +?> + + +
          + 'monthly', + 'show_post_count' => $c + ) ) ); +?> +
        + '', 'count' => 0, 'dropdown' => '') ); + $instance['title'] = strip_tags($new_instance['title']); + $instance['count'] = $new_instance['count'] ? 1 : 0; + $instance['dropdown'] = $new_instance['dropdown'] ? 1 : 0; + + return $instance; + } + + public function form( $instance ) { + $instance = wp_parse_args( (array) $instance, array( 'title' => '', 'count' => 0, 'dropdown' => '') ); + $title = strip_tags($instance['title']); + $count = $instance['count'] ? 'checked="checked"' : ''; + $dropdown = $instance['dropdown'] ? 'checked="checked"' : ''; +?> +

        +

        + id="get_field_id('dropdown'); ?>" name="get_field_name('dropdown'); ?>" /> +
        + id="get_field_id('count'); ?>" name="get_field_name('count'); ?>" /> +

        + 'widget_meta', 'description' => __( "Login, RSS, & WordPress.org links.") ); + parent::__construct('meta', __('Meta'), $widget_ops); + } + + public function widget( $args, $instance ) { + + /** This filter is documented in wp-includes/default-widgets.php */ + $title = apply_filters( 'widget_title', empty($instance['title']) ? __( 'Meta' ) : $instance['title'], $instance, $this->id_base ); + + echo $args['before_widget']; + if ( $title ) { + echo $args['before_title'] . $title . $args['after_title']; + } +?> +
          + +
        • +
        • RSS'); ?>
        • +
        • RSS'); ?>
        • +%s', + esc_url( __( 'https://wordpress.org/' ) ), + esc_attr__( 'Powered by WordPress, state-of-the-art semantic personal publishing platform.' ), + _x( 'WordPress.org', 'meta widget link text' ) + ) ); + + wp_meta(); +?> +
        + '' ) ); + $title = strip_tags($instance['title']); +?> +

        + 'widget_calendar', 'description' => __( 'A calendar of your site’s Posts.') ); + parent::__construct('calendar', __('Calendar'), $widget_ops); + } + + public function widget( $args, $instance ) { + + /** This filter is documented in wp-includes/default-widgets.php */ + $title = apply_filters( 'widget_title', empty( $instance['title'] ) ? '' : $instance['title'], $instance, $this->id_base ); + + echo $args['before_widget']; + if ( $title ) { + echo $args['before_title'] . $title . $args['after_title']; + } + echo '
        '; + get_calendar(); + echo '
        '; + echo $args['after_widget']; + } + + public function update( $new_instance, $old_instance ) { + $instance = $old_instance; + $instance['title'] = strip_tags($new_instance['title']); + + return $instance; + } + + public function form( $instance ) { + $instance = wp_parse_args( (array) $instance, array( 'title' => '' ) ); + $title = strip_tags($instance['title']); +?> +

        +

        + 'widget_text', 'description' => __('Arbitrary text or HTML.')); + $control_ops = array('width' => 400, 'height' => 350); + parent::__construct('text', __('Text'), $widget_ops, $control_ops); + } + + public function widget( $args, $instance ) { + + /** This filter is documented in wp-includes/default-widgets.php */ + $title = apply_filters( 'widget_title', empty( $instance['title'] ) ? '' : $instance['title'], $instance, $this->id_base ); + + /** + * Filter the content of the Text widget. + * + * @since 2.3.0 + * + * @param string $widget_text The widget content. + * @param WP_Widget $instance WP_Widget instance. + */ + $text = apply_filters( 'widget_text', empty( $instance['text'] ) ? '' : $instance['text'], $instance ); + echo $args['before_widget']; + if ( ! empty( $title ) ) { + echo $args['before_title'] . $title . $args['after_title']; + } ?> +
        + '', 'text' => '' ) ); + $title = strip_tags($instance['title']); + $text = esc_textarea($instance['text']); +?> +

        +

        + + + +

        /> 

        + 'widget_categories', 'description' => __( "A list or dropdown of categories." ) ); + parent::__construct('categories', __('Categories'), $widget_ops); + } + + public function widget( $args, $instance ) { + + /** This filter is documented in wp-includes/default-widgets.php */ + $title = apply_filters( 'widget_title', empty( $instance['title'] ) ? __( 'Categories' ) : $instance['title'], $instance, $this->id_base ); + + $c = ! empty( $instance['count'] ) ? '1' : '0'; + $h = ! empty( $instance['hierarchical'] ) ? '1' : '0'; + $d = ! empty( $instance['dropdown'] ) ? '1' : '0'; + + echo $args['before_widget']; + if ( $title ) { + echo $args['before_title'] . $title . $args['after_title']; + } + + $cat_args = array('orderby' => 'name', 'show_count' => $c, 'hierarchical' => $h); + + if ( $d ) { + $cat_args['show_option_none'] = __('Select Category'); + + /** + * Filter the arguments for the Categories widget drop-down. + * + * @since 2.8.0 + * + * @see wp_dropdown_categories() + * + * @param array $cat_args An array of Categories widget drop-down arguments. + */ + wp_dropdown_categories( apply_filters( 'widget_categories_dropdown_args', $cat_args ) ); +?> + + + + +
          + +
        + '') ); + $title = esc_attr( $instance['title'] ); + $count = isset($instance['count']) ? (bool) $instance['count'] :false; + $hierarchical = isset( $instance['hierarchical'] ) ? (bool) $instance['hierarchical'] : false; + $dropdown = isset( $instance['dropdown'] ) ? (bool) $instance['dropdown'] : false; +?> +

        +

        + +

        /> +
        + + /> +
        + + /> +

        + 'widget_recent_entries', 'description' => __( "Your site’s most recent Posts.") ); + parent::__construct('recent-posts', __('Recent Posts'), $widget_ops); + $this->alt_option_name = 'widget_recent_entries'; + + add_action( 'save_post', array($this, 'flush_widget_cache') ); + add_action( 'deleted_post', array($this, 'flush_widget_cache') ); + add_action( 'switch_theme', array($this, 'flush_widget_cache') ); + } + + public function widget($args, $instance) { + $cache = array(); + if ( ! $this->is_preview() ) { + $cache = wp_cache_get( 'widget_recent_posts', 'widget' ); + } + + if ( ! is_array( $cache ) ) { + $cache = array(); + } + + if ( ! isset( $args['widget_id'] ) ) { + $args['widget_id'] = $this->id; + } + + if ( isset( $cache[ $args['widget_id'] ] ) ) { + echo $cache[ $args['widget_id'] ]; + return; + } + + ob_start(); + + $title = ( ! empty( $instance['title'] ) ) ? $instance['title'] : __( 'Recent Posts' ); + + /** This filter is documented in wp-includes/default-widgets.php */ + $title = apply_filters( 'widget_title', $title, $instance, $this->id_base ); + + $number = ( ! empty( $instance['number'] ) ) ? absint( $instance['number'] ) : 5; + if ( ! $number ) + $number = 5; + $show_date = isset( $instance['show_date'] ) ? $instance['show_date'] : false; + + /** + * Filter the arguments for the Recent Posts widget. + * + * @since 3.4.0 + * + * @see WP_Query::get_posts() + * + * @param array $args An array of arguments used to retrieve the recent posts. + */ + $r = new WP_Query( apply_filters( 'widget_posts_args', array( + 'posts_per_page' => $number, + 'no_found_rows' => true, + 'post_status' => 'publish', + 'ignore_sticky_posts' => true + ) ) ); + + if ($r->have_posts()) : +?> + + +
          + have_posts() ) : $r->the_post(); ?> +
        • + + + + +
        • + +
        + +is_preview() ) { + $cache[ $args['widget_id'] ] = ob_get_flush(); + wp_cache_set( 'widget_recent_posts', $cache, 'widget' ); + } else { + ob_end_flush(); + } + } + + public function update( $new_instance, $old_instance ) { + $instance = $old_instance; + $instance['title'] = strip_tags($new_instance['title']); + $instance['number'] = (int) $new_instance['number']; + $instance['show_date'] = isset( $new_instance['show_date'] ) ? (bool) $new_instance['show_date'] : false; + $this->flush_widget_cache(); + + $alloptions = wp_cache_get( 'alloptions', 'options' ); + if ( isset($alloptions['widget_recent_entries']) ) + delete_option('widget_recent_entries'); + + return $instance; + } + + public function flush_widget_cache() { + wp_cache_delete('widget_recent_posts', 'widget'); + } + + public function form( $instance ) { + $title = isset( $instance['title'] ) ? esc_attr( $instance['title'] ) : ''; + $number = isset( $instance['number'] ) ? absint( $instance['number'] ) : 5; + $show_date = isset( $instance['show_date'] ) ? (bool) $instance['show_date'] : false; +?> +

        +

        + +

        +

        + +

        id="get_field_id( 'show_date' ); ?>" name="get_field_name( 'show_date' ); ?>" /> +

        + 'widget_recent_comments', 'description' => __( 'Your site’s most recent comments.' ) ); + parent::__construct('recent-comments', __('Recent Comments'), $widget_ops); + $this->alt_option_name = 'widget_recent_comments'; + + if ( is_active_widget(false, false, $this->id_base) ) + add_action( 'wp_head', array($this, 'recent_comments_style') ); + + add_action( 'comment_post', array($this, 'flush_widget_cache') ); + add_action( 'edit_comment', array($this, 'flush_widget_cache') ); + add_action( 'transition_comment_status', array($this, 'flush_widget_cache') ); + } + + public function recent_comments_style() { + + /** + * Filter the Recent Comments default widget styles. + * + * @since 3.1.0 + * + * @param bool $active Whether the widget is active. Default true. + * @param string $id_base The widget ID. + */ + if ( ! current_theme_supports( 'widgets' ) // Temp hack #14876 + || ! apply_filters( 'show_recent_comments_widget_style', true, $this->id_base ) ) + return; + ?> + +is_preview() ) { + $cache = wp_cache_get('widget_recent_comments', 'widget'); + } + if ( ! is_array( $cache ) ) { + $cache = array(); + } + + if ( ! isset( $args['widget_id'] ) ) + $args['widget_id'] = $this->id; + + if ( isset( $cache[ $args['widget_id'] ] ) ) { + echo $cache[ $args['widget_id'] ]; + return; + } + + $output = ''; + + $title = ( ! empty( $instance['title'] ) ) ? $instance['title'] : __( 'Recent Comments' ); + + /** This filter is documented in wp-includes/default-widgets.php */ + $title = apply_filters( 'widget_title', $title, $instance, $this->id_base ); + + $number = ( ! empty( $instance['number'] ) ) ? absint( $instance['number'] ) : 5; + if ( ! $number ) + $number = 5; + + /** + * Filter the arguments for the Recent Comments widget. + * + * @since 3.4.0 + * + * @see WP_Comment_Query::query() for information on accepted arguments. + * + * @param array $comment_args An array of arguments used to retrieve the recent comments. + */ + $comments = get_comments( apply_filters( 'widget_comments_args', array( + 'number' => $number, + 'status' => 'approve', + 'post_status' => 'publish' + ) ) ); + + $output .= $args['before_widget']; + if ( $title ) { + $output .= $args['before_title'] . $title . $args['after_title']; + } + + $output .= '
          '; + if ( $comments ) { + // Prime cache for associated posts. (Prime post term cache if we need it for permalinks.) + $post_ids = array_unique( wp_list_pluck( $comments, 'comment_post_ID' ) ); + _prime_post_caches( $post_ids, strpos( get_option( 'permalink_structure' ), '%category%' ), false ); + + foreach ( (array) $comments as $comment) { + $output .= '
        • '; + /* translators: comments widget: 1: comment author, 2: post link */ + $output .= sprintf( _x( '%1$s on %2$s', 'widgets' ), + '' . get_comment_author_link() . '', + '' . get_the_title( $comment->comment_post_ID ) . '' + ); + $output .= '
        • '; + } + } + $output .= '
        '; + $output .= $args['after_widget']; + + echo $output; + + if ( ! $this->is_preview() ) { + $cache[ $args['widget_id'] ] = $output; + wp_cache_set( 'widget_recent_comments', $cache, 'widget' ); + } + } + + public function update( $new_instance, $old_instance ) { + $instance = $old_instance; + $instance['title'] = strip_tags($new_instance['title']); + $instance['number'] = absint( $new_instance['number'] ); + $this->flush_widget_cache(); + + $alloptions = wp_cache_get( 'alloptions', 'options' ); + if ( isset($alloptions['widget_recent_comments']) ) + delete_option('widget_recent_comments'); + + return $instance; + } + + public function form( $instance ) { + $title = isset( $instance['title'] ) ? esc_attr( $instance['title'] ) : ''; + $number = isset( $instance['number'] ) ? absint( $instance['number'] ) : 5; +?> +

        +

        + +

        +

        + __('Entries from any RSS or Atom feed.') ); + $control_ops = array( 'width' => 400, 'height' => 200 ); + parent::__construct( 'rss', __('RSS'), $widget_ops, $control_ops ); + } + + public function widget($args, $instance) { + + if ( isset($instance['error']) && $instance['error'] ) + return; + + $url = ! empty( $instance['url'] ) ? $instance['url'] : ''; + while ( stristr($url, 'http') != $url ) + $url = substr($url, 1); + + if ( empty($url) ) + return; + + // self-url destruction sequence + if ( in_array( untrailingslashit( $url ), array( site_url(), home_url() ) ) ) + return; + + $rss = fetch_feed($url); + $title = $instance['title']; + $desc = ''; + $link = ''; + + if ( ! is_wp_error($rss) ) { + $desc = esc_attr(strip_tags(@html_entity_decode($rss->get_description(), ENT_QUOTES, get_option('blog_charset')))); + if ( empty($title) ) + $title = esc_html(strip_tags($rss->get_title())); + $link = esc_url(strip_tags($rss->get_permalink())); + while ( stristr($link, 'http') != $link ) + $link = substr($link, 1); + } + + if ( empty($title) ) + $title = empty($desc) ? __('Unknown Feed') : $desc; + + /** This filter is documented in wp-includes/default-widgets.php */ + $title = apply_filters( 'widget_title', $title, $instance, $this->id_base ); + + $url = esc_url(strip_tags($url)); + $icon = includes_url('images/rss.png'); + if ( $title ) + $title = "RSS $title"; + + echo $args['before_widget']; + if ( $title ) { + echo $args['before_title'] . $title . $args['after_title']; + } + wp_widget_rss_output( $rss, $instance ); + echo $args['after_widget']; + + if ( ! is_wp_error($rss) ) + $rss->__destruct(); + unset($rss); + } + + public function update($new_instance, $old_instance) { + $testurl = ( isset( $new_instance['url'] ) && ( !isset( $old_instance['url'] ) || ( $new_instance['url'] != $old_instance['url'] ) ) ); + return wp_widget_rss_process( $new_instance, $testurl ); + } + + public function form($instance) { + + if ( empty($instance) ) + $instance = array( 'title' => '', 'url' => '', 'items' => 10, 'error' => false, 'show_summary' => 0, 'show_author' => 0, 'show_date' => 0 ); + $instance['number'] = $this->number; + + wp_widget_rss_form( $instance ); + } +} + +/** + * Display the RSS entries in a list. + * + * @since 2.5.0 + * + * @param string|array|object $rss RSS url. + * @param array $args Widget arguments. + */ +function wp_widget_rss_output( $rss, $args = array() ) { + if ( is_string( $rss ) ) { + $rss = fetch_feed($rss); + } elseif ( is_array($rss) && isset($rss['url']) ) { + $args = $rss; + $rss = fetch_feed($rss['url']); + } elseif ( !is_object($rss) ) { + return; + } + + if ( is_wp_error($rss) ) { + if ( is_admin() || current_user_can('manage_options') ) + echo '

        ' . sprintf( __('RSS Error: %s'), $rss->get_error_message() ) . '

        '; + return; + } + + $default_args = array( 'show_author' => 0, 'show_date' => 0, 'show_summary' => 0, 'items' => 0 ); + $args = wp_parse_args( $args, $default_args ); + + $items = (int) $args['items']; + if ( $items < 1 || 20 < $items ) + $items = 10; + $show_summary = (int) $args['show_summary']; + $show_author = (int) $args['show_author']; + $show_date = (int) $args['show_date']; + + if ( !$rss->get_item_quantity() ) { + echo '
        • ' . __( 'An error has occurred, which probably means the feed is down. Try again later.' ) . '
        '; + $rss->__destruct(); + unset($rss); + return; + } + + echo '
          '; + foreach ( $rss->get_items( 0, $items ) as $item ) { + $link = $item->get_link(); + while ( stristr( $link, 'http' ) != $link ) { + $link = substr( $link, 1 ); + } + $link = esc_url( strip_tags( $link ) ); + + $title = esc_html( trim( strip_tags( $item->get_title() ) ) ); + if ( empty( $title ) ) { + $title = __( 'Untitled' ); + } + + $desc = @html_entity_decode( $item->get_description(), ENT_QUOTES, get_option( 'blog_charset' ) ); + $desc = esc_attr( wp_trim_words( $desc, 55, ' […]' ) ); + + $summary = ''; + if ( $show_summary ) { + $summary = $desc; + + // Change existing [...] to […]. + if ( '[...]' == substr( $summary, -5 ) ) { + $summary = substr( $summary, 0, -5 ) . '[…]'; + } + + $summary = '
          ' . esc_html( $summary ) . '
          '; + } + + $date = ''; + if ( $show_date ) { + $date = $item->get_date( 'U' ); + + if ( $date ) { + $date = ' ' . date_i18n( get_option( 'date_format' ), $date ) . ''; + } + } + + $author = ''; + if ( $show_author ) { + $author = $item->get_author(); + if ( is_object($author) ) { + $author = $author->get_name(); + $author = ' ' . esc_html( strip_tags( $author ) ) . ''; + } + } + + if ( $link == '' ) { + echo "
        • $title{$date}{$summary}{$author}
        • "; + } elseif ( $show_summary ) { + echo "
        • $title{$date}{$summary}{$author}
        • "; + } else { + echo "
        • $title{$date}{$author}
        • "; + } + } + echo '
        '; + $rss->__destruct(); + unset($rss); +} + +/** + * Display RSS widget options form. + * + * The options for what fields are displayed for the RSS form are all booleans + * and are as follows: 'url', 'title', 'items', 'show_summary', 'show_author', + * 'show_date'. + * + * @since 2.5.0 + * + * @param array|string $args Values for input fields. + * @param array $inputs Override default display options. + */ +function wp_widget_rss_form( $args, $inputs = null ) { + $default_inputs = array( 'url' => true, 'title' => true, 'items' => true, 'show_summary' => true, 'show_author' => true, 'show_date' => true ); + $inputs = wp_parse_args( $inputs, $default_inputs ); + + $args['number'] = esc_attr( $args['number'] ); + $args['title'] = isset( $args['title'] ) ? esc_attr( $args['title'] ) : ''; + $args['url'] = isset( $args['url'] ) ? esc_url( $args['url'] ) : ''; + $args['items'] = isset( $args['items'] ) ? (int) $args['items'] : 0; + + if ( $args['items'] < 1 || 20 < $args['items'] ) { + $args['items'] = 10; + } + + $args['show_summary'] = isset( $args['show_summary'] ) ? (int) $args['show_summary'] : (int) $inputs['show_summary']; + $args['show_author'] = isset( $args['show_author'] ) ? (int) $args['show_author'] : (int) $inputs['show_author']; + $args['show_date'] = isset( $args['show_date'] ) ? (int) $args['show_date'] : (int) $inputs['show_date']; + + if ( ! empty( $args['error'] ) ) { + echo '

        ' . sprintf( __( 'RSS Error: %s' ), $args['error'] ) . '

        '; + } + + if ( $inputs['url'] ) : +?> +

        +

        + +

        +

        + +

        +

        + +

        /> +

        + +

        /> +

        + +

        /> +

        + + +get_error_message(); + } else { + $link = esc_url(strip_tags($rss->get_permalink())); + while ( stristr($link, 'http') != $link ) + $link = substr($link, 1); + + $rss->__destruct(); + unset($rss); + } + } + + return compact( 'title', 'url', 'link', 'items', 'error', 'show_summary', 'show_author', 'show_date' ); +} + +/** + * Tag cloud widget class + * + * @since 2.8.0 + */ +class WP_Widget_Tag_Cloud extends WP_Widget { + + public function __construct() { + $widget_ops = array( 'description' => __( "A cloud of your most used tags.") ); + parent::__construct('tag_cloud', __('Tag Cloud'), $widget_ops); + } + + public function widget( $args, $instance ) { + $current_taxonomy = $this->_get_current_taxonomy($instance); + if ( !empty($instance['title']) ) { + $title = $instance['title']; + } else { + if ( 'post_tag' == $current_taxonomy ) { + $title = __('Tags'); + } else { + $tax = get_taxonomy($current_taxonomy); + $title = $tax->labels->name; + } + } + + /** This filter is documented in wp-includes/default-widgets.php */ + $title = apply_filters( 'widget_title', $title, $instance, $this->id_base ); + + echo $args['before_widget']; + if ( $title ) { + echo $args['before_title'] . $title . $args['after_title']; + } + echo '
        '; + + /** + * Filter the taxonomy used in the Tag Cloud widget. + * + * @since 2.8.0 + * @since 3.0.0 Added taxonomy drop-down. + * + * @see wp_tag_cloud() + * + * @param array $current_taxonomy The taxonomy to use in the tag cloud. Default 'tags'. + */ + wp_tag_cloud( apply_filters( 'widget_tag_cloud_args', array( + 'taxonomy' => $current_taxonomy + ) ) ); + + echo "
        \n"; + echo $args['after_widget']; + } + + public function update( $new_instance, $old_instance ) { + $instance['title'] = strip_tags(stripslashes($new_instance['title'])); + $instance['taxonomy'] = stripslashes($new_instance['taxonomy']); + return $instance; + } + + public function form( $instance ) { + $current_taxonomy = $this->_get_current_taxonomy($instance); +?> +

        +

        +

        +

        __('Add a custom menu to your sidebar.') ); + parent::__construct( 'nav_menu', __('Custom Menu'), $widget_ops ); + } + + public function widget($args, $instance) { + // Get menu + $nav_menu = ! empty( $instance['nav_menu'] ) ? wp_get_nav_menu_object( $instance['nav_menu'] ) : false; + + if ( !$nav_menu ) + return; + + /** This filter is documented in wp-includes/default-widgets.php */ + $instance['title'] = apply_filters( 'widget_title', empty( $instance['title'] ) ? '' : $instance['title'], $instance, $this->id_base ); + + echo $args['before_widget']; + + if ( !empty($instance['title']) ) + echo $args['before_title'] . $instance['title'] . $args['after_title']; + + wp_nav_menu( array( 'fallback_cb' => '', 'menu' => $nav_menu ) ); + + echo $args['after_widget']; + } + + public function update( $new_instance, $old_instance ) { + $instance = array(); + if ( ! empty( $new_instance['title'] ) ) { + $instance['title'] = strip_tags( stripslashes($new_instance['title']) ); + } + if ( ! empty( $new_instance['nav_menu'] ) ) { + $instance['nav_menu'] = (int) $new_instance['nav_menu']; + } + return $instance; + } + + public function form( $instance ) { + $title = isset( $instance['title'] ) ? $instance['title'] : ''; + $nav_menu = isset( $instance['nav_menu'] ) ? $instance['nav_menu'] : ''; + + // Get menus + $menus = wp_get_nav_menus(); + + // If no menus exists, direct the user to go and create some. + if ( !$menus ) { + echo '

        '. sprintf( __('No menus have been created yet. Create some.'), admin_url('nav-menus.php') ) .'

        '; + return; + } + ?> +

        + + +

        +

        + + +

        + $post->ID, + 'Author_ID' => $post->post_author, + 'Date' => $post->post_date, + 'Content' => $post->post_content, + 'Excerpt' => $post->post_excerpt, + 'Title' => $post->post_title, + 'Category' => $post->post_category, + 'post_status' => $post->post_status, + 'comment_status' => $post->comment_status, + 'ping_status' => $post->ping_status, + 'post_password' => $post->post_password, + 'to_ping' => $post->to_ping, + 'pinged' => $post->pinged, + 'post_type' => $post->post_type, + 'post_name' => $post->post_name + ); + + return $postdata; +} + +/** + * Sets up the WordPress Loop. + * + * @since 1.0.1 + * @deprecated 1.5.0 + * @deprecated Use The Loop - {@link http://codex.wordpress.org/The_Loop Use new WordPress Loop} + */ +function start_wp() { + global $wp_query; + + _deprecated_function( __FUNCTION__, '1.5', __('new WordPress Loop') ); + + // Since the old style loop is being used, advance the query iterator here. + $wp_query->next_post(); + + setup_postdata( get_post() ); +} + +/** + * Return or Print Category ID. + * + * @since 0.71 + * @deprecated 0.71 + * @deprecated use get_the_category() + * @see get_the_category() + * + * @param bool $echo + * @return null|int + */ +function the_category_ID($echo = true) { + _deprecated_function( __FUNCTION__, '0.71', 'get_the_category()' ); + + // Grab the first cat in the list. + $categories = get_the_category(); + $cat = $categories[0]->term_id; + + if ( $echo ) + echo $cat; + + return $cat; +} + +/** + * Print category with optional text before and after. + * + * @since 0.71 + * @deprecated 0.71 + * @deprecated use get_the_category_by_ID() + * @see get_the_category_by_ID() + * + * @param string $before + * @param string $after + */ +function the_category_head($before='', $after='') { + global $currentcat, $previouscat; + + _deprecated_function( __FUNCTION__, '0.71', 'get_the_category_by_ID()' ); + + // Grab the first cat in the list. + $categories = get_the_category(); + $currentcat = $categories[0]->category_id; + if ( $currentcat != $previouscat ) { + echo $before; + echo get_the_category_by_ID($currentcat); + echo $after; + $previouscat = $currentcat; + } +} + +/** + * Prints link to the previous post. + * + * @since 1.5.0 + * @deprecated 2.0.0 + * @deprecated Use previous_post_link() + * @see previous_post_link() + * + * @param string $format + * @param string $previous + * @param string $title + * @param string $in_same_cat + * @param int $limitprev + * @param string $excluded_categories + */ +function previous_post($format='%', $previous='previous post: ', $title='yes', $in_same_cat='no', $limitprev=1, $excluded_categories='') { + + _deprecated_function( __FUNCTION__, '2.0', 'previous_post_link()' ); + + if ( empty($in_same_cat) || 'no' == $in_same_cat ) + $in_same_cat = false; + else + $in_same_cat = true; + + $post = get_previous_post($in_same_cat, $excluded_categories); + + if ( !$post ) + return; + + $string = ''.$previous; + if ( 'yes' == $title ) + $string .= apply_filters('the_title', $post->post_title, $post->ID); + $string .= ''; + $format = str_replace('%', $string, $format); + echo $format; +} + +/** + * Prints link to the next post. + * + * @since 0.71 + * @deprecated 2.0.0 + * @deprecated Use next_post_link() + * @see next_post_link() + * + * @param string $format + * @param string $next + * @param string $title + * @param string $in_same_cat + * @param int $limitnext + * @param string $excluded_categories + */ +function next_post($format='%', $next='next post: ', $title='yes', $in_same_cat='no', $limitnext=1, $excluded_categories='') { + _deprecated_function( __FUNCTION__, '2.0', 'next_post_link()' ); + + if ( empty($in_same_cat) || 'no' == $in_same_cat ) + $in_same_cat = false; + else + $in_same_cat = true; + + $post = get_next_post($in_same_cat, $excluded_categories); + + if ( !$post ) + return; + + $string = ''.$next; + if ( 'yes' == $title ) + $string .= apply_filters('the_title', $post->post_title, $post->ID); + $string .= ''; + $format = str_replace('%', $string, $format); + echo $format; +} + +/** + * Whether user can create a post. + * + * @since 1.5.0 + * @deprecated 2.0.0 + * @deprecated Use current_user_can() + * @see current_user_can() + * + * @param int $user_id + * @param int $blog_id Not Used + * @param int $category_id Not Used + * @return bool + */ +function user_can_create_post($user_id, $blog_id = 1, $category_id = 'None') { + _deprecated_function( __FUNCTION__, '2.0', 'current_user_can()' ); + + $author_data = get_userdata($user_id); + return ($author_data->user_level > 1); +} + +/** + * Whether user can create a post. + * + * @since 1.5.0 + * @deprecated 2.0.0 + * @deprecated Use current_user_can() + * @see current_user_can() + * + * @param int $user_id + * @param int $blog_id Not Used + * @param int $category_id Not Used + * @return bool + */ +function user_can_create_draft($user_id, $blog_id = 1, $category_id = 'None') { + _deprecated_function( __FUNCTION__, '2.0', 'current_user_can()' ); + + $author_data = get_userdata($user_id); + return ($author_data->user_level >= 1); +} + +/** + * Whether user can edit a post. + * + * @since 1.5.0 + * @deprecated 2.0.0 + * @deprecated Use current_user_can() + * @see current_user_can() + * + * @param int $user_id + * @param int $post_id + * @param int $blog_id Not Used + * @return bool + */ +function user_can_edit_post($user_id, $post_id, $blog_id = 1) { + _deprecated_function( __FUNCTION__, '2.0', 'current_user_can()' ); + + $author_data = get_userdata($user_id); + $post = get_post($post_id); + $post_author_data = get_userdata($post->post_author); + + if ( (($user_id == $post_author_data->ID) && !($post->post_status == 'publish' && $author_data->user_level < 2)) + || ($author_data->user_level > $post_author_data->user_level) + || ($author_data->user_level >= 10) ) { + return true; + } else { + return false; + } +} + +/** + * Whether user can delete a post. + * + * @since 1.5.0 + * @deprecated 2.0.0 + * @deprecated Use current_user_can() + * @see current_user_can() + * + * @param int $user_id + * @param int $post_id + * @param int $blog_id Not Used + * @return bool + */ +function user_can_delete_post($user_id, $post_id, $blog_id = 1) { + _deprecated_function( __FUNCTION__, '2.0', 'current_user_can()' ); + + // right now if one can edit, one can delete + return user_can_edit_post($user_id, $post_id, $blog_id); +} + +/** + * Whether user can set new posts' dates. + * + * @since 1.5.0 + * @deprecated 2.0.0 + * @deprecated Use current_user_can() + * @see current_user_can() + * + * @param int $user_id + * @param int $blog_id Not Used + * @param int $category_id Not Used + * @return bool + */ +function user_can_set_post_date($user_id, $blog_id = 1, $category_id = 'None') { + _deprecated_function( __FUNCTION__, '2.0', 'current_user_can()' ); + + $author_data = get_userdata($user_id); + return (($author_data->user_level > 4) && user_can_create_post($user_id, $blog_id, $category_id)); +} + +/** + * Whether user can delete a post. + * + * @since 1.5.0 + * @deprecated 2.0.0 + * @deprecated Use current_user_can() + * @see current_user_can() + * + * @param int $user_id + * @param int $post_id + * @param int $blog_id Not Used + * @return bool returns true if $user_id can edit $post_id's date + */ +function user_can_edit_post_date($user_id, $post_id, $blog_id = 1) { + _deprecated_function( __FUNCTION__, '2.0', 'current_user_can()' ); + + $author_data = get_userdata($user_id); + return (($author_data->user_level > 4) && user_can_edit_post($user_id, $post_id, $blog_id)); +} + +/** + * Whether user can delete a post. + * + * @since 1.5.0 + * @deprecated 2.0.0 + * @deprecated Use current_user_can() + * @see current_user_can() + * + * @param int $user_id + * @param int $post_id + * @param int $blog_id Not Used + * @return bool returns true if $user_id can edit $post_id's comments + */ +function user_can_edit_post_comments($user_id, $post_id, $blog_id = 1) { + _deprecated_function( __FUNCTION__, '2.0', 'current_user_can()' ); + + // right now if one can edit a post, one can edit comments made on it + return user_can_edit_post($user_id, $post_id, $blog_id); +} + +/** + * Whether user can delete a post. + * + * @since 1.5.0 + * @deprecated 2.0.0 + * @deprecated Use current_user_can() + * @see current_user_can() + * + * @param int $user_id + * @param int $post_id + * @param int $blog_id Not Used + * @return bool returns true if $user_id can delete $post_id's comments + */ +function user_can_delete_post_comments($user_id, $post_id, $blog_id = 1) { + _deprecated_function( __FUNCTION__, '2.0', 'current_user_can()' ); + + // right now if one can edit comments, one can delete comments + return user_can_edit_post_comments($user_id, $post_id, $blog_id); +} + +/** + * Can user can edit other user. + * + * @since 1.5.0 + * @deprecated 2.0.0 + * @deprecated Use current_user_can() + * @see current_user_can() + * + * @param int $user_id + * @param int $other_user + * @return bool + */ +function user_can_edit_user($user_id, $other_user) { + _deprecated_function( __FUNCTION__, '2.0', 'current_user_can()' ); + + $user = get_userdata($user_id); + $other = get_userdata($other_user); + if ( $user->user_level > $other->user_level || $user->user_level > 8 || $user->ID == $other->ID ) + return true; + else + return false; +} + +/** + * Gets the links associated with category $cat_name. + * + * @since 0.71 + * @deprecated 2.1.0 + * @deprecated Use get_bookmarks() + * @see get_bookmarks() + * + * @param string $cat_name Optional. The category name to use. If no match is found uses all. + * @param string $before Optional. The html to output before the link. + * @param string $after Optional. The html to output after the link. + * @param string $between Optional. The html to output between the link/image and its description. Not used if no image or $show_images is true. + * @param bool $show_images Optional. Whether to show images (if defined). + * @param string $orderby Optional. The order to output the links. E.g. 'id', 'name', 'url', 'description' or 'rating'. Or maybe owner. + * If you start the name with an underscore the order will be reversed. You can also specify 'rand' as the order which will return links in a + * random order. + * @param bool $show_description Optional. Whether to show the description if show_images=false/not defined. + * @param bool $show_rating Optional. Show rating stars/chars. + * @param int $limit Optional. Limit to X entries. If not specified, all entries are shown. + * @param int $show_updated Optional. Whether to show last updated timestamp + */ +function get_linksbyname($cat_name = "noname", $before = '', $after = '
        ', $between = " ", $show_images = true, $orderby = 'id', + $show_description = true, $show_rating = false, + $limit = -1, $show_updated = 0) { + _deprecated_function( __FUNCTION__, '2.1', 'get_bookmarks()' ); + + $cat_id = -1; + $cat = get_term_by('name', $cat_name, 'link_category'); + if ( $cat ) + $cat_id = $cat->term_id; + + get_links($cat_id, $before, $after, $between, $show_images, $orderby, $show_description, $show_rating, $limit, $show_updated); +} + +/** + * Gets the links associated with the named category. + * + * @since 1.0.1 + * @deprecated 2.1.0 + * @deprecated Use wp_list_bookmarks() + * @see wp_list_bookmarks() + * + * @param string $category The category to use. + * @param string $args + * @return string|null + */ +function wp_get_linksbyname($category, $args = '') { + _deprecated_function(__FUNCTION__, '2.1', 'wp_list_bookmarks()'); + + $defaults = array( + 'after' => '
        ', + 'before' => '', + 'categorize' => 0, + 'category_after' => '', + 'category_before' => '', + 'category_name' => $category, + 'show_description' => 1, + 'title_li' => '', + ); + + $r = wp_parse_args( $args, $defaults ); + + return wp_list_bookmarks($r); +} + +/** + * Gets an array of link objects associated with category $cat_name. + * + * $links = get_linkobjectsbyname( 'fred' ); + * foreach ( $links as $link ) { + * echo '
      5. ' . $link->link_name . '
      6. '; + * } + * + * @since 1.0.1 + * @deprecated 2.1.0 + * @deprecated Use get_bookmarks() + * @see get_bookmarks() + * + * @param string $cat_name The category name to use. If no match is found uses all. + * @param string $orderby The order to output the links. E.g. 'id', 'name', 'url', 'description', or 'rating'. + * Or maybe owner. If you start the name with an underscore the order will be reversed. You can also + * specify 'rand' as the order which will return links in a random order. + * @param int $limit Limit to X entries. If not specified, all entries are shown. + * @return array + */ +function get_linkobjectsbyname($cat_name = "noname" , $orderby = 'name', $limit = -1) { + _deprecated_function( __FUNCTION__, '2.1', 'get_bookmarks()' ); + + $cat_id = -1; + $cat = get_term_by('name', $cat_name, 'link_category'); + if ( $cat ) + $cat_id = $cat->term_id; + + return get_linkobjects($cat_id, $orderby, $limit); +} + +/** + * Gets an array of link objects associated with category n. + * + * Usage: + * + * $links = get_linkobjects(1); + * if ($links) { + * foreach ($links as $link) { + * echo '
      7. '.$link->link_name.'
        '.$link->link_description.'
      8. '; + * } + * } + * + * Fields are: + * + * - link_id + * - link_url + * - link_name + * - link_image + * - link_target + * - link_category + * - link_description + * - link_visible + * - link_owner + * - link_rating + * - link_updated + * - link_rel + * - link_notes + * + * @since 1.0.1 + * @deprecated 2.1.0 + * @deprecated Use get_bookmarks() + * @see get_bookmarks() + * + * @param int $category The category to use. If no category supplied uses all + * @param string $orderby the order to output the links. E.g. 'id', 'name', 'url', + * 'description', or 'rating'. Or maybe owner. If you start the name with an + * underscore the order will be reversed. You can also specify 'rand' as the + * order which will return links in a random order. + * @param int $limit Limit to X entries. If not specified, all entries are shown. + * @return array + */ +function get_linkobjects($category = 0, $orderby = 'name', $limit = 0) { + _deprecated_function( __FUNCTION__, '2.1', 'get_bookmarks()' ); + + $links = get_bookmarks( array( 'category' => $category, 'orderby' => $orderby, 'limit' => $limit ) ) ; + + $links_array = array(); + foreach ($links as $link) + $links_array[] = $link; + + return $links_array; +} + +/** + * Gets the links associated with category 'cat_name' and display rating stars/chars. + * + * @since 0.71 + * @deprecated 2.1.0 + * @deprecated Use get_bookmarks() + * @see get_bookmarks() + * + * @param string $cat_name The category name to use. If no match is found uses all + * @param string $before The html to output before the link + * @param string $after The html to output after the link + * @param string $between The html to output between the link/image and its description. Not used if no image or show_images is true + * @param bool $show_images Whether to show images (if defined). + * @param string $orderby the order to output the links. E.g. 'id', 'name', 'url', + * 'description', or 'rating'. Or maybe owner. If you start the name with an + * underscore the order will be reversed. You can also specify 'rand' as the + * order which will return links in a random order. + * @param bool $show_description Whether to show the description if show_images=false/not defined + * @param int $limit Limit to X entries. If not specified, all entries are shown. + * @param int $show_updated Whether to show last updated timestamp + */ +function get_linksbyname_withrating($cat_name = "noname", $before = '', $after = '
        ', $between = " ", + $show_images = true, $orderby = 'id', $show_description = true, $limit = -1, $show_updated = 0) { + _deprecated_function( __FUNCTION__, '2.1', 'get_bookmarks()' ); + + get_linksbyname($cat_name, $before, $after, $between, $show_images, $orderby, $show_description, true, $limit, $show_updated); +} + +/** + * Gets the links associated with category n and display rating stars/chars. + * + * @since 0.71 + * @deprecated 2.1.0 + * @deprecated Use get_bookmarks() + * @see get_bookmarks() + * + * @param int $category The category to use. If no category supplied uses all + * @param string $before The html to output before the link + * @param string $after The html to output after the link + * @param string $between The html to output between the link/image and its description. Not used if no image or show_images == true + * @param bool $show_images Whether to show images (if defined). + * @param string $orderby The order to output the links. E.g. 'id', 'name', 'url', + * 'description', or 'rating'. Or maybe owner. If you start the name with an + * underscore the order will be reversed. You can also specify 'rand' as the + * order which will return links in a random order. + * @param bool $show_description Whether to show the description if show_images=false/not defined. + * @param int $limit Limit to X entries. If not specified, all entries are shown. + * @param int $show_updated Whether to show last updated timestamp + */ +function get_links_withrating($category = -1, $before = '', $after = '
        ', $between = " ", $show_images = true, + $orderby = 'id', $show_description = true, $limit = -1, $show_updated = 0) { + _deprecated_function( __FUNCTION__, '2.1', 'get_bookmarks()' ); + + get_links($category, $before, $after, $between, $show_images, $orderby, $show_description, true, $limit, $show_updated); +} + +/** + * Gets the auto_toggle setting. + * + * @since 0.71 + * @deprecated 2.1.0 + * @deprecated No alternative function available + * + * @param int $id The category to get. If no category supplied uses 0 + * @return int Only returns 0. + */ +function get_autotoggle($id = 0) { + _deprecated_function( __FUNCTION__, '2.1' ); + return 0; +} + +/** + * @since 0.71 + * @deprecated 2.1.0 + * @deprecated Use wp_list_categories() + * @see wp_list_categories() + * + * @param int $optionall + * @param string $all + * @param string $sort_column + * @param string $sort_order + * @param string $file + * @param bool $list + * @param int $optiondates + * @param int $optioncount + * @param int $hide_empty + * @param int $use_desc_for_title + * @param bool $children + * @param int $child_of + * @param int $categories + * @param int $recurse + * @param string $feed + * @param string $feed_image + * @param string $exclude + * @param bool $hierarchical + * @return false|null + */ +function list_cats($optionall = 1, $all = 'All', $sort_column = 'ID', $sort_order = 'asc', $file = '', $list = true, $optiondates = 0, + $optioncount = 0, $hide_empty = 1, $use_desc_for_title = 1, $children=false, $child_of=0, $categories=0, + $recurse=0, $feed = '', $feed_image = '', $exclude = '', $hierarchical=false) { + _deprecated_function( __FUNCTION__, '2.1', 'wp_list_categories()' ); + + $query = compact('optionall', 'all', 'sort_column', 'sort_order', 'file', 'list', 'optiondates', 'optioncount', 'hide_empty', 'use_desc_for_title', 'children', + 'child_of', 'categories', 'recurse', 'feed', 'feed_image', 'exclude', 'hierarchical'); + return wp_list_cats($query); +} + +/** + * @since 1.2.0 + * @deprecated 2.1.0 + * @deprecated Use wp_list_categories() + * @see wp_list_categories() + * + * @param string|array $args + * @return false|null|string + */ +function wp_list_cats($args = '') { + _deprecated_function( __FUNCTION__, '2.1', 'wp_list_categories()' ); + + $r = wp_parse_args( $args ); + + // Map to new names. + if ( isset($r['optionall']) && isset($r['all'])) + $r['show_option_all'] = $r['all']; + if ( isset($r['sort_column']) ) + $r['orderby'] = $r['sort_column']; + if ( isset($r['sort_order']) ) + $r['order'] = $r['sort_order']; + if ( isset($r['optiondates']) ) + $r['show_last_update'] = $r['optiondates']; + if ( isset($r['optioncount']) ) + $r['show_count'] = $r['optioncount']; + if ( isset($r['list']) ) + $r['style'] = $r['list'] ? 'list' : 'break'; + $r['title_li'] = ''; + + return wp_list_categories($r); +} + +/** + * @since 0.71 + * @deprecated 2.1.0 + * @deprecated Use wp_dropdown_categories() + * @see wp_dropdown_categories() + * + * @param int $optionall + * @param string $all + * @param string $orderby + * @param string $order + * @param int $show_last_update + * @param int $show_count + * @param int $hide_empty + * @param bool $optionnone + * @param int $selected + * @param int $exclude + * @return string + */ +function dropdown_cats($optionall = 1, $all = 'All', $orderby = 'ID', $order = 'asc', + $show_last_update = 0, $show_count = 0, $hide_empty = 1, $optionnone = false, + $selected = 0, $exclude = 0) { + _deprecated_function( __FUNCTION__, '2.1', 'wp_dropdown_categories()' ); + + $show_option_all = ''; + if ( $optionall ) + $show_option_all = $all; + + $show_option_none = ''; + if ( $optionnone ) + $show_option_none = __('None'); + + $vars = compact('show_option_all', 'show_option_none', 'orderby', 'order', + 'show_last_update', 'show_count', 'hide_empty', 'selected', 'exclude'); + $query = add_query_arg($vars, ''); + return wp_dropdown_categories($query); +} + +/** + * List authors. + * + * @since 1.2.0 + * @deprecated 2.1.0 + * @deprecated Use wp_list_authors() + * @see wp_list_authors() + * + * @param bool $optioncount + * @param bool $exclude_admin + * @param bool $show_fullname + * @param bool $hide_empty + * @param string $feed + * @param string $feed_image + * @return null|string + */ +function list_authors($optioncount = false, $exclude_admin = true, $show_fullname = false, $hide_empty = true, $feed = '', $feed_image = '') { + _deprecated_function( __FUNCTION__, '2.1', 'wp_list_authors()' ); + + $args = compact('optioncount', 'exclude_admin', 'show_fullname', 'hide_empty', 'feed', 'feed_image'); + return wp_list_authors($args); +} + +/** + * @since 1.0.1 + * @deprecated 2.1.0 + * @deprecated Use wp_get_post_categories() + * @see wp_get_post_categories() + * + * @param int $blogid Not Used + * @param int $post_ID + * @return array + */ +function wp_get_post_cats($blogid = '1', $post_ID = 0) { + _deprecated_function( __FUNCTION__, '2.1', 'wp_get_post_categories()' ); + return wp_get_post_categories($post_ID); +} + +/** + * Sets the categories that the post id belongs to. + * + * @since 1.0.1 + * @deprecated 2.1.0 + * @deprecated Use wp_set_post_categories() + * @see wp_set_post_categories() + * + * @param int $blogid Not used + * @param int $post_ID + * @param array $post_categories + * @return bool|mixed + */ +function wp_set_post_cats($blogid = '1', $post_ID = 0, $post_categories = array()) { + _deprecated_function( __FUNCTION__, '2.1', 'wp_set_post_categories()' ); + return wp_set_post_categories($post_ID, $post_categories); +} + +/** + * @since 0.71 + * @deprecated 2.1.0 + * @deprecated Use wp_get_archives() + * @see wp_get_archives() + * + * @param string $type + * @param string $limit + * @param string $format + * @param string $before + * @param string $after + * @param bool $show_post_count + * @return string|null + */ +function get_archives($type='', $limit='', $format='html', $before = '', $after = '', $show_post_count = false) { + _deprecated_function( __FUNCTION__, '2.1', 'wp_get_archives()' ); + $args = compact('type', 'limit', 'format', 'before', 'after', 'show_post_count'); + return wp_get_archives($args); +} + +/** + * Returns or Prints link to the author's posts. + * + * @since 1.2.0 + * @deprecated 2.1.0 + * @deprecated Use get_author_posts_url() + * @see get_author_posts_url() + * + * @param bool $echo + * @param int $author_id + * @param string $author_nicename Optional. + * @return string|null + */ +function get_author_link($echo, $author_id, $author_nicename = '') { + _deprecated_function( __FUNCTION__, '2.1', 'get_author_posts_url()' ); + + $link = get_author_posts_url($author_id, $author_nicename); + + if ( $echo ) + echo $link; + return $link; +} + +/** + * Print list of pages based on arguments. + * + * @since 0.71 + * @deprecated 2.1.0 + * @deprecated Use wp_link_pages() + * @see wp_link_pages() + * + * @param string $before + * @param string $after + * @param string $next_or_number + * @param string $nextpagelink + * @param string $previouspagelink + * @param string $pagelink + * @param string $more_file + * @return string + */ +function link_pages($before='
        ', $after='
        ', $next_or_number='number', $nextpagelink='next page', $previouspagelink='previous page', + $pagelink='%', $more_file='') { + _deprecated_function( __FUNCTION__, '2.1', 'wp_link_pages()' ); + + $args = compact('before', 'after', 'next_or_number', 'nextpagelink', 'previouspagelink', 'pagelink', 'more_file'); + return wp_link_pages($args); +} + +/** + * Get value based on option. + * + * @since 0.71 + * @deprecated 2.1.0 + * @deprecated Use get_option() + * @see get_option() + * + * @param string $option + * @return string + */ +function get_settings($option) { + _deprecated_function( __FUNCTION__, '2.1', 'get_option()' ); + + return get_option($option); +} + +/** + * Print the permalink of the current post in the loop. + * + * @since 0.71 + * @deprecated 1.2.0 + * @deprecated Use the_permalink() + * @see the_permalink() + */ +function permalink_link() { + _deprecated_function( __FUNCTION__, '1.2', 'the_permalink()' ); + the_permalink(); +} + +/** + * Print the permalink to the RSS feed. + * + * @since 0.71 + * @deprecated 2.3.0 + * @deprecated Use the_permalink_rss() + * @see the_permalink_rss() + * + * @param string $deprecated + */ +function permalink_single_rss($deprecated = '') { + _deprecated_function( __FUNCTION__, '2.3', 'the_permalink_rss()' ); + the_permalink_rss(); +} + +/** + * Gets the links associated with category. + * + * @see get_links() for argument information that can be used in $args + * @since 1.0.1 + * @deprecated 2.1.0 + * @deprecated Use wp_list_bookmarks() + * @see wp_list_bookmarks() + * + * @param string $args a query string + * @return null|string + */ +function wp_get_links($args = '') { + _deprecated_function( __FUNCTION__, '2.1', 'wp_list_bookmarks()' ); + + if ( strpos( $args, '=' ) === false ) { + $cat_id = $args; + $args = add_query_arg( 'category', $cat_id, $args ); + } + + $defaults = array( + 'after' => '
        ', + 'before' => '', + 'between' => ' ', + 'categorize' => 0, + 'category' => '', + 'echo' => true, + 'limit' => -1, + 'orderby' => 'name', + 'show_description' => true, + 'show_images' => true, + 'show_rating' => false, + 'show_updated' => true, + 'title_li' => '', + ); + + $r = wp_parse_args( $args, $defaults ); + + return wp_list_bookmarks($r); +} + +/** + * Gets the links associated with category by id. + * + * @since 0.71 + * @deprecated 2.1.0 + * @deprecated Use get_bookmarks() + * @see get_bookmarks() + * + * @param int $category The category to use. If no category supplied uses all + * @param string $before the html to output before the link + * @param string $after the html to output after the link + * @param string $between the html to output between the link/image and its description. + * Not used if no image or show_images == true + * @param bool $show_images whether to show images (if defined). + * @param string $orderby the order to output the links. E.g. 'id', 'name', 'url', + * 'description', or 'rating'. Or maybe owner. If you start the name with an + * underscore the order will be reversed. You can also specify 'rand' as the order + * which will return links in a random order. + * @param bool $show_description whether to show the description if show_images=false/not defined. + * @param bool $show_rating show rating stars/chars + * @param int $limit Limit to X entries. If not specified, all entries are shown. + * @param int $show_updated whether to show last updated timestamp + * @param bool $echo whether to echo the results, or return them instead + * @return null|string + */ +function get_links($category = -1, $before = '', $after = '
        ', $between = ' ', $show_images = true, $orderby = 'name', + $show_description = true, $show_rating = false, $limit = -1, $show_updated = 1, $echo = true) { + _deprecated_function( __FUNCTION__, '2.1', 'get_bookmarks()' ); + + $order = 'ASC'; + if ( substr($orderby, 0, 1) == '_' ) { + $order = 'DESC'; + $orderby = substr($orderby, 1); + } + + if ( $category == -1 ) //get_bookmarks uses '' to signify all categories + $category = ''; + + $results = get_bookmarks(array('category' => $category, 'orderby' => $orderby, 'order' => $order, 'show_updated' => $show_updated, 'limit' => $limit)); + + if ( !$results ) + return; + + $output = ''; + + foreach ( (array) $results as $row ) { + if ( !isset($row->recently_updated) ) + $row->recently_updated = false; + $output .= $before; + if ( $show_updated && $row->recently_updated ) + $output .= get_option('links_recently_updated_prepend'); + $the_link = '#'; + if ( !empty($row->link_url) ) + $the_link = esc_url($row->link_url); + $rel = $row->link_rel; + if ( '' != $rel ) + $rel = ' rel="' . $rel . '"'; + + $desc = esc_attr(sanitize_bookmark_field('link_description', $row->link_description, $row->link_id, 'display')); + $name = esc_attr(sanitize_bookmark_field('link_name', $row->link_name, $row->link_id, 'display')); + $title = $desc; + + if ( $show_updated ) + if (substr($row->link_updated_f, 0, 2) != '00') + $title .= ' ('.__('Last updated') . ' ' . date(get_option('links_updated_date_format'), $row->link_updated_f + (get_option('gmt_offset') * HOUR_IN_SECONDS)) . ')'; + + if ( '' != $title ) + $title = ' title="' . $title . '"'; + + $alt = ' alt="' . $name . '"'; + + $target = $row->link_target; + if ( '' != $target ) + $target = ' target="' . $target . '"'; + + $output .= ''; + + if ( $row->link_image != null && $show_images ) { + if ( strpos($row->link_image, 'http') !== false ) + $output .= "link_image\" $alt $title />"; + else // If it's a relative path + $output .= "link_image\" $alt $title />"; + } else { + $output .= $name; + } + + $output .= ''; + + if ( $show_updated && $row->recently_updated ) + $output .= get_option('links_recently_updated_append'); + + if ( $show_description && '' != $desc ) + $output .= $between . $desc; + + if ($show_rating) { + $output .= $between . get_linkrating($row); + } + + $output .= "$after\n"; + } // end while + + if ( !$echo ) + return $output; + echo $output; +} + +/** + * Output entire list of links by category. + * + * Output a list of all links, listed by category, using the settings in + * $wpdb->linkcategories and output it as a nested HTML unordered list. + * + * @since 1.0.1 + * @deprecated 2.1.0 + * @deprecated Use wp_list_bookmarks() + * @see wp_list_bookmarks() + * + * @param string $order Sort link categories by 'name' or 'id' + */ +function get_links_list($order = 'name') { + _deprecated_function( __FUNCTION__, '2.1', 'wp_list_bookmarks()' ); + + $order = strtolower($order); + + // Handle link category sorting + $direction = 'ASC'; + if ( '_' == substr($order,0,1) ) { + $direction = 'DESC'; + $order = substr($order,1); + } + + if ( !isset($direction) ) + $direction = ''; + + $cats = get_categories(array('type' => 'link', 'orderby' => $order, 'order' => $direction, 'hierarchical' => 0)); + + // Display each category + if ( $cats ) { + foreach ( (array) $cats as $cat ) { + // Handle each category. + + // Display the category name + echo '
      9. ' . apply_filters('link_category', $cat->name ) . "

        \n\t
          \n"; + // Call get_links() with all the appropriate params + get_links($cat->term_id, '
        • ', "
        • ", "\n", true, 'name', false); + + // Close the last category + echo "\n\t
        \n
      10. \n"; + } + } +} + +/** + * Show the link to the links popup and the number of links. + * + * @since 0.71 + * @deprecated 2.1.0 + * @deprecated {@internal Use function instead is unknown}} + * + * @param string $text the text of the link + * @param int $width the width of the popup window + * @param int $height the height of the popup window + * @param string $file the page to open in the popup window + * @param bool $count the number of links in the db + */ +function links_popup_script($text = 'Links', $width=400, $height=400, $file='links.all.php', $count = true) { + _deprecated_function( __FUNCTION__, '2.1' ); +} + +/** + * @since 1.0.1 + * @deprecated 2.1.0 + * @deprecated Use sanitize_bookmark_field() + * @see sanitize_bookmark_field() + * + * @param object $link + * @return mixed + */ +function get_linkrating($link) { + _deprecated_function( __FUNCTION__, '2.1', 'sanitize_bookmark_field()' ); + return sanitize_bookmark_field('link_rating', $link->link_rating, $link->link_id, 'display'); +} + +/** + * Gets the name of category by id. + * + * @since 0.71 + * @deprecated 2.1.0 + * @deprecated Use get_category() + * @see get_category() + * + * @param int $id The category to get. If no category supplied uses 0 + * @return string + */ +function get_linkcatname($id = 0) { + _deprecated_function( __FUNCTION__, '2.1', 'get_category()' ); + + $id = (int) $id; + + if ( empty($id) ) + return ''; + + $cats = wp_get_link_cats($id); + + if ( empty($cats) || ! is_array($cats) ) + return ''; + + $cat_id = (int) $cats[0]; // Take the first cat. + + $cat = get_category($cat_id); + return $cat->name; +} + +/** + * Print RSS comment feed link. + * + * @since 1.0.1 + * @deprecated 2.5.0 + * @deprecated Use post_comments_feed_link() + * @see post_comments_feed_link() + * + * @param string $link_text + */ +function comments_rss_link($link_text = 'Comments RSS') { + _deprecated_function( __FUNCTION__, '2.5', 'post_comments_feed_link()' ); + post_comments_feed_link($link_text); +} + +/** + * Print/Return link to category RSS2 feed. + * + * @since 1.2.0 + * @deprecated 2.5.0 + * @deprecated Use get_category_feed_link() + * @see get_category_feed_link() + * + * @param bool $echo + * @param int $cat_ID + * @return string + */ +function get_category_rss_link($echo = false, $cat_ID = 1) { + _deprecated_function( __FUNCTION__, '2.5', 'get_category_feed_link()' ); + + $link = get_category_feed_link($cat_ID, 'rss2'); + + if ( $echo ) + echo $link; + return $link; +} + +/** + * Print/Return link to author RSS feed. + * + * @since 1.2.0 + * @deprecated 2.5.0 + * @deprecated Use get_author_feed_link() + * @see get_author_feed_link() + * + * @param bool $echo + * @param int $author_id + * @return string + */ +function get_author_rss_link($echo = false, $author_id = 1) { + _deprecated_function( __FUNCTION__, '2.5', 'get_author_feed_link()' ); + + $link = get_author_feed_link($author_id); + if ( $echo ) + echo $link; + return $link; +} + +/** + * Return link to the post RSS feed. + * + * @since 1.5.0 + * @deprecated 2.2.0 + * @deprecated Use get_post_comments_feed_link() + * @see get_post_comments_feed_link() + * + * @return string + */ +function comments_rss() { + _deprecated_function( __FUNCTION__, '2.2', 'get_post_comments_feed_link()' ); + return esc_url( get_post_comments_feed_link() ); +} + +/** + * An alias of wp_create_user(). + * + * @since 2.0.0 + * @deprecated 2.0.0 + * @deprecated Use wp_create_user() + * @see wp_create_user() + * + * @param string $username The user's username. + * @param string $password The user's password. + * @param string $email The user's email. + * @return int The new user's ID. + */ +function create_user($username, $password, $email) { + _deprecated_function( __FUNCTION__, '2.0', 'wp_create_user()' ); + return wp_create_user($username, $password, $email); +} + +/** + * Unused function. + * + * @deprecated 2.5.0 +*/ +function gzip_compression() { + _deprecated_function( __FUNCTION__, '2.5' ); + return false; +} + +/** + * Retrieve an array of comment data about comment $comment_ID. + * + * @since 0.71 + * @deprecated 2.7.0 + * @deprecated Use get_comment() + * @see get_comment() + * + * @param int $comment_ID The ID of the comment + * @param int $no_cache Whether to use the cache (cast to bool) + * @param bool $include_unapproved Whether to include unapproved comments + * @return array The comment data + */ +function get_commentdata( $comment_ID, $no_cache = 0, $include_unapproved = false ) { + _deprecated_function( __FUNCTION__, '2.7', 'get_comment()' ); + return get_comment($comment_ID, ARRAY_A); +} + +/** + * Retrieve the category name by the category ID. + * + * @since 0.71 + * @deprecated 2.8.0 + * @deprecated Use get_cat_name() + * @see get_cat_name() + * + * @param int $cat_ID Category ID + * @return string category name + */ +function get_catname( $cat_ID ) { + _deprecated_function( __FUNCTION__, '2.8', 'get_cat_name()' ); + return get_cat_name( $cat_ID ); +} + +/** + * Retrieve category children list separated before and after the term IDs. + * + * @since 1.2.0 + * @deprecated 2.8.0 + * @deprecated Use get_term_children() + * @see get_term_children() + * + * @param int $id Category ID to retrieve children. + * @param string $before Optional. Prepend before category term ID. + * @param string $after Optional, default is empty string. Append after category term ID. + * @param array $visited Optional. Category Term IDs that have already been added. + * @return string + */ +function get_category_children( $id, $before = '/', $after = '', $visited = array() ) { + _deprecated_function( __FUNCTION__, '2.8', 'get_term_children()' ); + if ( 0 == $id ) + return ''; + + $chain = ''; + /** TODO: consult hierarchy */ + $cat_ids = get_all_category_ids(); + foreach ( (array) $cat_ids as $cat_id ) { + if ( $cat_id == $id ) + continue; + + $category = get_category( $cat_id ); + if ( is_wp_error( $category ) ) + return $category; + if ( $category->parent == $id && !in_array( $category->term_id, $visited ) ) { + $visited[] = $category->term_id; + $chain .= $before.$category->term_id.$after; + $chain .= get_category_children( $category->term_id, $before, $after ); + } + } + return $chain; +} + +/** + * Retrieves all category IDs. + * + * @since 2.0.0 + * @deprecated 4.0.0 Use get_terms() instead. + * @see get_terms() + * @link http://codex.wordpress.org/Function_Reference/get_all_category_ids + * + * @return object List of all of the category IDs. + */ +function get_all_category_ids() { + _deprecated_function( __FUNCTION__, '4.0', 'get_terms()' ); + + if ( ! $cat_ids = wp_cache_get( 'all_category_ids', 'category' ) ) { + $cat_ids = get_terms( 'category', array('fields' => 'ids', 'get' => 'all') ); + wp_cache_add( 'all_category_ids', $cat_ids, 'category' ); + } + + return $cat_ids; +} + +/** + * Retrieve the description of the author of the current post. + * + * @since 1.5.0 + * @deprecated 2.8.0 + * @deprecated Use get_the_author_meta('description') + * @see get_the_author_meta() + * + * @return string The author's description. + */ +function get_the_author_description() { + _deprecated_function( __FUNCTION__, '2.8', 'get_the_author_meta(\'description\')' ); + return get_the_author_meta('description'); +} + +/** + * Display the description of the author of the current post. + * + * @since 1.0.0 + * @deprecated 2.8.0 + * @deprecated Use the_author_meta('description') + * @see the_author_meta() + */ +function the_author_description() { + _deprecated_function( __FUNCTION__, '2.8', 'the_author_meta(\'description\')' ); + the_author_meta('description'); +} + +/** + * Retrieve the login name of the author of the current post. + * + * @since 1.5.0 + * @deprecated 2.8.0 + * @deprecated Use get_the_author_meta('login') + * @see get_the_author_meta() + * + * @return string The author's login name (username). + */ +function get_the_author_login() { + _deprecated_function( __FUNCTION__, '2.8', 'get_the_author_meta(\'login\')' ); + return get_the_author_meta('login'); +} + +/** + * Display the login name of the author of the current post. + * + * @since 0.71 + * @deprecated 2.8.0 + * @deprecated Use the_author_meta('login') + * @see the_author_meta() + */ +function the_author_login() { + _deprecated_function( __FUNCTION__, '2.8', 'the_author_meta(\'login\')' ); + the_author_meta('login'); +} + +/** + * Retrieve the first name of the author of the current post. + * + * @since 1.5.0 + * @deprecated 2.8.0 + * @deprecated Use get_the_author_meta('first_name') + * @see get_the_author_meta() + * + * @return string The author's first name. + */ +function get_the_author_firstname() { + _deprecated_function( __FUNCTION__, '2.8', 'get_the_author_meta(\'first_name\')' ); + return get_the_author_meta('first_name'); +} + +/** + * Display the first name of the author of the current post. + * + * @since 0.71 + * @deprecated 2.8.0 + * @deprecated Use the_author_meta('first_name') + * @see the_author_meta() + */ +function the_author_firstname() { + _deprecated_function( __FUNCTION__, '2.8', 'the_author_meta(\'first_name\')' ); + the_author_meta('first_name'); +} + +/** + * Retrieve the last name of the author of the current post. + * + * @since 1.5.0 + * @deprecated 2.8.0 + * @deprecated Use get_the_author_meta('last_name') + * @see get_the_author_meta() + * + * @return string The author's last name. + */ +function get_the_author_lastname() { + _deprecated_function( __FUNCTION__, '2.8', 'get_the_author_meta(\'last_name\')' ); + return get_the_author_meta('last_name'); +} + +/** + * Display the last name of the author of the current post. + * + * @since 0.71 + * @deprecated 2.8.0 + * @deprecated Use the_author_meta('last_name') + * @see the_author_meta() + */ +function the_author_lastname() { + _deprecated_function( __FUNCTION__, '2.8', 'the_author_meta(\'last_name\')' ); + the_author_meta('last_name'); +} + +/** + * Retrieve the nickname of the author of the current post. + * + * @since 1.5.0 + * @deprecated 2.8.0 + * @deprecated Use get_the_author_meta('nickname') + * @see get_the_author_meta() + * + * @return string The author's nickname. + */ +function get_the_author_nickname() { + _deprecated_function( __FUNCTION__, '2.8', 'get_the_author_meta(\'nickname\')' ); + return get_the_author_meta('nickname'); +} + +/** + * Display the nickname of the author of the current post. + * + * @since 0.71 + * @deprecated 2.8.0 + * @deprecated Use the_author_meta('nickname') + * @see the_author_meta() + */ +function the_author_nickname() { + _deprecated_function( __FUNCTION__, '2.8', 'the_author_meta(\'nickname\')' ); + the_author_meta('nickname'); +} + +/** + * Retrieve the email of the author of the current post. + * + * @since 1.5.0 + * @deprecated 2.8.0 + * @deprecated Use get_the_author_meta('email') + * @see get_the_author_meta() + * + * @return string The author's username. + */ +function get_the_author_email() { + _deprecated_function( __FUNCTION__, '2.8', 'get_the_author_meta(\'email\')' ); + return get_the_author_meta('email'); +} + +/** + * Display the email of the author of the current post. + * + * @since 0.71 + * @deprecated 2.8.0 + * @deprecated Use the_author_meta('email') + * @see the_author_meta() + */ +function the_author_email() { + _deprecated_function( __FUNCTION__, '2.8', 'the_author_meta(\'email\')' ); + the_author_meta('email'); +} + +/** + * Retrieve the ICQ number of the author of the current post. + * + * @since 1.5.0 + * @deprecated 2.8.0 + * @deprecated Use get_the_author_meta('icq') + * @see get_the_author_meta() + * + * @return string The author's ICQ number. + */ +function get_the_author_icq() { + _deprecated_function( __FUNCTION__, '2.8', 'get_the_author_meta(\'icq\')' ); + return get_the_author_meta('icq'); +} + +/** + * Display the ICQ number of the author of the current post. + * + * @since 0.71 + * @deprecated 2.8.0 + * @deprecated Use the_author_meta('icq') + * @see the_author_meta() + */ +function the_author_icq() { + _deprecated_function( __FUNCTION__, '2.8', 'the_author_meta(\'icq\')' ); + the_author_meta('icq'); +} + +/** + * Retrieve the Yahoo! IM name of the author of the current post. + * + * @since 1.5.0 + * @deprecated 2.8.0 + * @deprecated Use get_the_author_meta('yim') + * @see get_the_author_meta() + * + * @return string The author's Yahoo! IM name. + */ +function get_the_author_yim() { + _deprecated_function( __FUNCTION__, '2.8', 'get_the_author_meta(\'yim\')' ); + return get_the_author_meta('yim'); +} + +/** + * Display the Yahoo! IM name of the author of the current post. + * + * @since 0.71 + * @deprecated 2.8.0 + * @deprecated Use the_author_meta('yim') + * @see the_author_meta() + */ +function the_author_yim() { + _deprecated_function( __FUNCTION__, '2.8', 'the_author_meta(\'yim\')' ); + the_author_meta('yim'); +} + +/** + * Retrieve the MSN address of the author of the current post. + * + * @since 1.5.0 + * @deprecated 2.8.0 + * @deprecated Use get_the_author_meta('msn') + * @see get_the_author_meta() + * + * @return string The author's MSN address. + */ +function get_the_author_msn() { + _deprecated_function( __FUNCTION__, '2.8', 'get_the_author_meta(\'msn\')' ); + return get_the_author_meta('msn'); +} + +/** + * Display the MSN address of the author of the current post. + * + * @since 0.71 + * @deprecated 2.8.0 + * @deprecated Use the_author_meta('msn') + * @see the_author_meta() + */ +function the_author_msn() { + _deprecated_function( __FUNCTION__, '2.8', 'the_author_meta(\'msn\')' ); + the_author_meta('msn'); +} + +/** + * Retrieve the AIM address of the author of the current post. + * + * @since 1.5.0 + * @deprecated 2.8.0 + * @deprecated Use get_the_author_meta('aim') + * @see get_the_author_meta() + * + * @return string The author's AIM address. + */ +function get_the_author_aim() { + _deprecated_function( __FUNCTION__, '2.8', 'get_the_author_meta(\'aim\')' ); + return get_the_author_meta('aim'); +} + +/** + * Display the AIM address of the author of the current post. + * + * @since 0.71 + * @see the_author_meta() + * @deprecated 2.8.0 + * @deprecated Use the_author_meta('aim') + */ +function the_author_aim() { + _deprecated_function( __FUNCTION__, '2.8', 'the_author_meta(\'aim\')' ); + the_author_meta('aim'); +} + +/** + * Retrieve the specified author's preferred display name. + * + * @since 1.0.0 + * @deprecated 2.8.0 + * @deprecated Use get_the_author_meta('display_name') + * @see get_the_author_meta() + * + * @param int $auth_id The ID of the author. + * @return string The author's display name. + */ +function get_author_name( $auth_id = false ) { + _deprecated_function( __FUNCTION__, '2.8', 'get_the_author_meta(\'display_name\')' ); + return get_the_author_meta('display_name', $auth_id); +} + +/** + * Retrieve the URL to the home page of the author of the current post. + * + * @since 1.5.0 + * @deprecated 2.8.0 + * @deprecated Use get_the_author_meta('url') + * @see get_the_author_meta() + * + * @return string The URL to the author's page. + */ +function get_the_author_url() { + _deprecated_function( __FUNCTION__, '2.8', 'get_the_author_meta(\'url\')' ); + return get_the_author_meta('url'); +} + +/** + * Display the URL to the home page of the author of the current post. + * + * @since 0.71 + * @deprecated 2.8.0 + * @deprecated Use the_author_meta('url') + * @see the_author_meta() + */ +function the_author_url() { + _deprecated_function( __FUNCTION__, '2.8', 'the_author_meta(\'url\')' ); + the_author_meta('url'); +} + +/** + * Retrieve the ID of the author of the current post. + * + * @since 1.5.0 + * @deprecated 2.8.0 + * @deprecated Use get_the_author_meta('ID') + * @see get_the_author_meta() + * + * @return string|int The author's ID. + */ +function get_the_author_ID() { + _deprecated_function( __FUNCTION__, '2.8', 'get_the_author_meta(\'ID\')' ); + return get_the_author_meta('ID'); +} + +/** + * Display the ID of the author of the current post. + * + * @since 0.71 + * @deprecated 2.8.0 + * @deprecated Use the_author_meta('ID') + * @see the_author_meta() +*/ +function the_author_ID() { + _deprecated_function( __FUNCTION__, '2.8', 'the_author_meta(\'ID\')' ); + the_author_meta('ID'); +} + +/** + * Display the post content for the feed. + * + * For encoding the html or the $encode_html parameter, there are three possible + * values. '0' will make urls footnotes and use make_url_footnote(). '1' will + * encode special characters and automatically display all of the content. The + * value of '2' will strip all HTML tags from the content. + * + * Also note that you cannot set the amount of words and not set the html + * encoding. If that is the case, then the html encoding will default to 2, + * which will strip all HTML tags. + * + * To restrict the amount of words of the content, you can use the cut + * parameter. If the content is less than the amount, then there won't be any + * dots added to the end. If there is content left over, then dots will be added + * and the rest of the content will be removed. + * + * @since 0.71 + * + * @deprecated 2.9.0 + * @deprecated Use the_content_feed() + * @see the_content_feed() + * + * @param string $more_link_text Optional. Text to display when more content is available but not displayed. + * @param int $stripteaser Optional. Default is 0. + * @param string $more_file Optional. + * @param int $cut Optional. Amount of words to keep for the content. + * @param int $encode_html Optional. How to encode the content. + */ +function the_content_rss($more_link_text='(more...)', $stripteaser=0, $more_file='', $cut = 0, $encode_html = 0) { + _deprecated_function( __FUNCTION__, '2.9', 'the_content_feed' ); + $content = get_the_content($more_link_text, $stripteaser); + $content = apply_filters('the_content_rss', $content); + if ( $cut && !$encode_html ) + $encode_html = 2; + if ( 1== $encode_html ) { + $content = esc_html($content); + $cut = 0; + } elseif ( 0 == $encode_html ) { + $content = make_url_footnote($content); + } elseif ( 2 == $encode_html ) { + $content = strip_tags($content); + } + if ( $cut ) { + $blah = explode(' ', $content); + if ( count($blah) > $cut ) { + $k = $cut; + $use_dotdotdot = 1; + } else { + $k = count($blah); + $use_dotdotdot = 0; + } + + /** @todo Check performance, might be faster to use array slice instead. */ + for ( $i=0; $i<$k; $i++ ) + $excerpt .= $blah[$i].' '; + $excerpt .= ($use_dotdotdot) ? '...' : ''; + $content = $excerpt; + } + $content = str_replace(']]>', ']]>', $content); + echo $content; +} + +/** + * Strip HTML and put links at the bottom of stripped content. + * + * Searches for all of the links, strips them out of the content, and places + * them at the bottom of the content with numbers. + * + * @since 0.71 + * @deprecated 2.9.0 + * + * @param string $content Content to get links + * @return string HTML stripped out of content with links at the bottom. + */ +function make_url_footnote( $content ) { + _deprecated_function( __FUNCTION__, '2.9', '' ); + preg_match_all( '/(.+?)<\/a>/', $content, $matches ); + $links_summary = "\n"; + for ( $i=0; $ipost_type) || !$url = wp_get_attachment_url($_post->ID) ) + return __('Missing Attachment'); + + if ( $permalink ) + $url = get_attachment_link($_post->ID); + + $post_title = esc_attr($_post->post_title); + + $innerHTML = get_attachment_innerHTML($_post->ID, $fullsize, $max_dims); + return "$innerHTML"; +} + +/** + * Retrieve icon URL and Path. + * + * @since 2.1.0 + * @deprecated 2.5.0 + * @deprecated Use wp_get_attachment_image_src() + * @see wp_get_attachment_image_src() + * + * @param int $id Optional. Post ID. + * @param bool $fullsize Optional, default to false. Whether to have full image. + * @return array Icon URL and full path to file, respectively. + */ +function get_attachment_icon_src( $id = 0, $fullsize = false ) { + _deprecated_function( __FUNCTION__, '2.5', 'wp_get_attachment_image_src()' ); + $id = (int) $id; + if ( !$post = get_post($id) ) + return false; + + $file = get_attached_file( $post->ID ); + + if ( !$fullsize && $src = wp_get_attachment_thumb_url( $post->ID ) ) { + // We have a thumbnail desired, specified and existing + + $src_file = basename($src); + } elseif ( wp_attachment_is_image( $post->ID ) ) { + // We have an image without a thumbnail + + $src = wp_get_attachment_url( $post->ID ); + $src_file = & $file; + } elseif ( $src = wp_mime_type_icon( $post->ID ) ) { + // No thumb, no image. We'll look for a mime-related icon instead. + + $icon_dir = apply_filters( 'icon_dir', get_template_directory() . '/images' ); + $src_file = $icon_dir . '/' . basename($src); + } + + if ( !isset($src) || !$src ) + return false; + + return array($src, $src_file); +} + +/** + * Retrieve HTML content of icon attachment image element. + * + * @since 2.0.0 + * @deprecated 2.5.0 + * @deprecated Use wp_get_attachment_image() + * @see wp_get_attachment_image() + * + * @param int $id Optional. Post ID. + * @param bool $fullsize Optional, default to false. Whether to have full size image. + * @param array $max_dims Optional. Dimensions of image. + * @return false|string HTML content. + */ +function get_attachment_icon( $id = 0, $fullsize = false, $max_dims = false ) { + _deprecated_function( __FUNCTION__, '2.5', 'wp_get_attachment_image()' ); + $id = (int) $id; + if ( !$post = get_post($id) ) + return false; + + if ( !$src = get_attachment_icon_src( $post->ID, $fullsize ) ) + return false; + + list($src, $src_file) = $src; + + // Do we need to constrain the image? + if ( ($max_dims = apply_filters('attachment_max_dims', $max_dims)) && file_exists($src_file) ) { + + $imagesize = getimagesize($src_file); + + if (($imagesize[0] > $max_dims[0]) || $imagesize[1] > $max_dims[1] ) { + $actual_aspect = $imagesize[0] / $imagesize[1]; + $desired_aspect = $max_dims[0] / $max_dims[1]; + + if ( $actual_aspect >= $desired_aspect ) { + $height = $actual_aspect * $max_dims[0]; + $constraint = "width='{$max_dims[0]}' "; + $post->iconsize = array($max_dims[0], $height); + } else { + $width = $max_dims[1] / $actual_aspect; + $constraint = "height='{$max_dims[1]}' "; + $post->iconsize = array($width, $max_dims[1]); + } + } else { + $post->iconsize = array($imagesize[0], $imagesize[1]); + $constraint = ''; + } + } else { + $constraint = ''; + } + + $post_title = esc_attr($post->post_title); + + $icon = "$post_title"; + + return apply_filters( 'attachment_icon', $icon, $post->ID ); +} + +/** + * Retrieve HTML content of image element. + * + * @since 2.0.0 + * @deprecated 2.5.0 + * @deprecated Use wp_get_attachment_image() + * @see wp_get_attachment_image() + * + * @param int $id Optional. Post ID. + * @param bool $fullsize Optional, default to false. Whether to have full size image. + * @param array $max_dims Optional. Dimensions of image. + * @return false|string + */ +function get_attachment_innerHTML($id = 0, $fullsize = false, $max_dims = false) { + _deprecated_function( __FUNCTION__, '2.5', 'wp_get_attachment_image()' ); + $id = (int) $id; + if ( !$post = get_post($id) ) + return false; + + if ( $innerHTML = get_attachment_icon($post->ID, $fullsize, $max_dims)) + return $innerHTML; + + $innerHTML = esc_attr($post->post_title); + + return apply_filters('attachment_innerHTML', $innerHTML, $post->ID); +} + +/** + * Retrieve bookmark data based on ID. + * + * @since 2.0.0 + * @deprecated 2.1.0 + * @deprecated Use get_bookmark() + * @see get_bookmark() + * + * @param int $bookmark_id ID of link + * @param string $output OBJECT, ARRAY_N, or ARRAY_A + * @return object|array + */ +function get_link($bookmark_id, $output = OBJECT, $filter = 'raw') { + _deprecated_function( __FUNCTION__, '2.1', 'get_bookmark()' ); + return get_bookmark($bookmark_id, $output, $filter); +} + +/** + * Performs esc_url() for database or redirect usage. + * + * @since 2.3.1 + * @deprecated 2.8.0 + * @deprecated Use esc_url_raw() + * @see esc_url_raw() + * + * @param string $url The URL to be cleaned. + * @param array $protocols An array of acceptable protocols. + * @return string The cleaned URL. + */ +function sanitize_url( $url, $protocols = null ) { + _deprecated_function( __FUNCTION__, '2.8', 'esc_url_raw()' ); + return esc_url_raw( $url, $protocols ); +} + +/** + * Checks and cleans a URL. + * + * A number of characters are removed from the URL. If the URL is for displaying + * (the default behaviour) ampersands are also replaced. The 'clean_url' filter + * is applied to the returned cleaned URL. + * + * @since 1.2.0 + * @deprecated 3.0.0 + * @deprecated Use esc_url() + * @see Alias for esc_url() + * + * @param string $url The URL to be cleaned. + * @param array $protocols Optional. An array of acceptable protocols. + * @param string $context Optional. How the URL will be used. Default is 'display'. + * @return string The cleaned $url after the 'clean_url' filter is applied. + */ +function clean_url( $url, $protocols = null, $context = 'display' ) { + if ( $context == 'db' ) + _deprecated_function( 'clean_url( $context = \'db\' )', '3.0', 'esc_url_raw()' ); + else + _deprecated_function( __FUNCTION__, '3.0', 'esc_url()' ); + return esc_url( $url, $protocols, $context ); +} + +/** + * Escape single quotes, specialchar double quotes, and fix line endings. + * + * The filter 'js_escape' is also applied by esc_js() + * + * @since 2.0.4 + * @deprecated 2.8.0 + * @deprecated Use esc_js() + * @see esc_js() + * + * @param string $text The text to be escaped. + * @return string Escaped text. + */ +function js_escape( $text ) { + _deprecated_function( __FUNCTION__, '2.8', 'esc_js()' ); + return esc_js( $text ); +} + +/** + * Escaping for HTML blocks. + * + * @deprecated 2.8.0 + * @deprecated Use esc_html() + * @see esc_html() + */ +function wp_specialchars( $string, $quote_style = ENT_NOQUOTES, $charset = false, $double_encode = false ) { + _deprecated_function( __FUNCTION__, '2.8', 'esc_html()' ); + if ( func_num_args() > 1 ) { // Maintain backwards compat for people passing additional args + $args = func_get_args(); + return call_user_func_array( '_wp_specialchars', $args ); + } else { + return esc_html( $string ); + } +} + +/** + * Escaping for HTML attributes. + * + * @since 2.0.6 + * @deprecated 2.8.0 + * @deprecated Use esc_attr() + * @see esc_attr() + * + * @param string $text + * @return string + */ +function attribute_escape( $text ) { + _deprecated_function( __FUNCTION__, '2.8', 'esc_attr()' ); + return esc_attr( $text ); +} + +/** + * Register widget for sidebar with backwards compatibility. + * + * Allows $name to be an array that accepts either three elements to grab the + * first element and the third for the name or just uses the first element of + * the array for the name. + * + * Passes to {@link wp_register_sidebar_widget()} after argument list and + * backwards compatibility is complete. + * + * @since 2.2.0 + * @deprecated 2.8.0 + * @deprecated Use wp_register_sidebar_widget() + * @see wp_register_sidebar_widget() + * + * @param string|int $name Widget ID. + * @param callback $output_callback Run when widget is called. + * @param string $classname Classname widget option. + * @param mixed $params,... Widget parameters. + */ +function register_sidebar_widget($name, $output_callback, $classname = '') { + _deprecated_function( __FUNCTION__, '2.8', 'wp_register_sidebar_widget()' ); + // Compat + if ( is_array($name) ) { + if ( count($name) == 3 ) + $name = sprintf($name[0], $name[2]); + else + $name = $name[0]; + } + + $id = sanitize_title($name); + $options = array(); + if ( !empty($classname) && is_string($classname) ) + $options['classname'] = $classname; + $params = array_slice(func_get_args(), 2); + $args = array($id, $name, $output_callback, $options); + if ( !empty($params) ) + $args = array_merge($args, $params); + + call_user_func_array('wp_register_sidebar_widget', $args); +} + +/** + * Alias of {@link wp_unregister_sidebar_widget()}. + * + * @since 2.2.0 + * @deprecated 2.8.0 + * @deprecated Use wp_unregister_sidebar_widget() + * @see wp_unregister_sidebar_widget() + * + * @param int|string $id Widget ID. + */ +function unregister_sidebar_widget($id) { + _deprecated_function( __FUNCTION__, '2.8', 'wp_unregister_sidebar_widget()' ); + return wp_unregister_sidebar_widget($id); +} + +/** + * Registers widget control callback for customizing options. + * + * Allows $name to be an array that accepts either three elements to grab the + * first element and the third for the name or just uses the first element of + * the array for the name. + * + * Passes to {@link wp_register_widget_control()} after the argument list has + * been compiled. + * + * @since 2.2.0 + * @deprecated 2.8.0 + * @deprecated Use wp_register_widget_control() + * @see wp_register_widget_control() + * + * @param int|string $name Sidebar ID. + * @param callback $control_callback Widget control callback to display and process form. + * @param int $width Widget width. + * @param int $height Widget height. + */ +function register_widget_control($name, $control_callback, $width = '', $height = '') { + _deprecated_function( __FUNCTION__, '2.8', 'wp_register_widget_control()' ); + // Compat + if ( is_array($name) ) { + if ( count($name) == 3 ) + $name = sprintf($name[0], $name[2]); + else + $name = $name[0]; + } + + $id = sanitize_title($name); + $options = array(); + if ( !empty($width) ) + $options['width'] = $width; + if ( !empty($height) ) + $options['height'] = $height; + $params = array_slice(func_get_args(), 4); + $args = array($id, $name, $control_callback, $options); + if ( !empty($params) ) + $args = array_merge($args, $params); + + call_user_func_array('wp_register_widget_control', $args); +} + +/** + * Alias of {@link wp_unregister_widget_control()}. + * + * @since 2.2.0 + * @deprecated 2.8.0 + * @deprecated Use wp_unregister_widget_control() + * @see wp_unregister_widget_control() + * + * @param int|string $id Widget ID. + */ +function unregister_widget_control($id) { + _deprecated_function( __FUNCTION__, '2.8', 'wp_unregister_widget_control()' ); + return wp_unregister_widget_control($id); +} + +/** + * Remove user meta data. + * + * @since 2.0.0 + * @deprecated 3.0.0 + * @deprecated Use delete_user_meta() + * @see delete_user_meta() + * + * @param int $user_id User ID. + * @param string $meta_key Metadata key. + * @param mixed $meta_value Metadata value. + * @return bool True deletion completed and false if user_id is not a number. + */ +function delete_usermeta( $user_id, $meta_key, $meta_value = '' ) { + _deprecated_function( __FUNCTION__, '3.0', 'delete_user_meta()' ); + global $wpdb; + if ( !is_numeric( $user_id ) ) + return false; + $meta_key = preg_replace('|[^a-z0-9_]|i', '', $meta_key); + + if ( is_array($meta_value) || is_object($meta_value) ) + $meta_value = serialize($meta_value); + $meta_value = trim( $meta_value ); + + $cur = $wpdb->get_row( $wpdb->prepare("SELECT * FROM $wpdb->usermeta WHERE user_id = %d AND meta_key = %s", $user_id, $meta_key) ); + + if ( $cur && $cur->umeta_id ) + do_action( 'delete_usermeta', $cur->umeta_id, $user_id, $meta_key, $meta_value ); + + if ( ! empty($meta_value) ) + $wpdb->query( $wpdb->prepare("DELETE FROM $wpdb->usermeta WHERE user_id = %d AND meta_key = %s AND meta_value = %s", $user_id, $meta_key, $meta_value) ); + else + $wpdb->query( $wpdb->prepare("DELETE FROM $wpdb->usermeta WHERE user_id = %d AND meta_key = %s", $user_id, $meta_key) ); + + clean_user_cache( $user_id ); + wp_cache_delete( $user_id, 'user_meta' ); + + if ( $cur && $cur->umeta_id ) + do_action( 'deleted_usermeta', $cur->umeta_id, $user_id, $meta_key, $meta_value ); + + return true; +} + +/** + * Retrieve user metadata. + * + * If $user_id is not a number, then the function will fail over with a 'false' + * boolean return value. Other returned values depend on whether there is only + * one item to be returned, which be that single item type. If there is more + * than one metadata value, then it will be list of metadata values. + * + * @since 2.0.0 + * @deprecated 3.0.0 + * @deprecated Use get_user_meta() + * @see get_user_meta() + * + * @param int $user_id User ID + * @param string $meta_key Optional. Metadata key. + * @return mixed + */ +function get_usermeta( $user_id, $meta_key = '' ) { + _deprecated_function( __FUNCTION__, '3.0', 'get_user_meta()' ); + global $wpdb; + $user_id = (int) $user_id; + + if ( !$user_id ) + return false; + + if ( !empty($meta_key) ) { + $meta_key = preg_replace('|[^a-z0-9_]|i', '', $meta_key); + $user = wp_cache_get($user_id, 'users'); + // Check the cached user object + if ( false !== $user && isset($user->$meta_key) ) + $metas = array($user->$meta_key); + else + $metas = $wpdb->get_col( $wpdb->prepare("SELECT meta_value FROM $wpdb->usermeta WHERE user_id = %d AND meta_key = %s", $user_id, $meta_key) ); + } else { + $metas = $wpdb->get_col( $wpdb->prepare("SELECT meta_value FROM $wpdb->usermeta WHERE user_id = %d", $user_id) ); + } + + if ( empty($metas) ) { + if ( empty($meta_key) ) + return array(); + else + return ''; + } + + $metas = array_map('maybe_unserialize', $metas); + + if ( count($metas) == 1 ) + return $metas[0]; + else + return $metas; +} + +/** + * Update metadata of user. + * + * There is no need to serialize values, they will be serialized if it is + * needed. The metadata key can only be a string with underscores. All else will + * be removed. + * + * Will remove the metadata, if the meta value is empty. + * + * @since 2.0.0 + * @deprecated 3.0.0 + * @deprecated Use update_user_meta() + * @see update_user_meta() + * + * @param int $user_id User ID + * @param string $meta_key Metadata key. + * @param mixed $meta_value Metadata value. + * @return bool True on successful update, false on failure. + */ +function update_usermeta( $user_id, $meta_key, $meta_value ) { + _deprecated_function( __FUNCTION__, '3.0', 'update_user_meta()' ); + global $wpdb; + if ( !is_numeric( $user_id ) ) + return false; + $meta_key = preg_replace('|[^a-z0-9_]|i', '', $meta_key); + + /** @todo Might need fix because usermeta data is assumed to be already escaped */ + if ( is_string($meta_value) ) + $meta_value = stripslashes($meta_value); + $meta_value = maybe_serialize($meta_value); + + if (empty($meta_value)) { + return delete_usermeta($user_id, $meta_key); + } + + $cur = $wpdb->get_row( $wpdb->prepare("SELECT * FROM $wpdb->usermeta WHERE user_id = %d AND meta_key = %s", $user_id, $meta_key) ); + + if ( $cur ) + do_action( 'update_usermeta', $cur->umeta_id, $user_id, $meta_key, $meta_value ); + + if ( !$cur ) + $wpdb->insert($wpdb->usermeta, compact('user_id', 'meta_key', 'meta_value') ); + else if ( $cur->meta_value != $meta_value ) + $wpdb->update($wpdb->usermeta, compact('meta_value'), compact('user_id', 'meta_key') ); + else + return false; + + clean_user_cache( $user_id ); + wp_cache_delete( $user_id, 'user_meta' ); + + if ( !$cur ) + do_action( 'added_usermeta', $wpdb->insert_id, $user_id, $meta_key, $meta_value ); + else + do_action( 'updated_usermeta', $cur->umeta_id, $user_id, $meta_key, $meta_value ); + + return true; +} + +/** + * Get users for the blog. + * + * For setups that use the multi-blog feature. Can be used outside of the + * multi-blog feature. + * + * @since 2.2.0 + * @deprecated 3.1.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + * @uses $blog_id The Blog id of the blog for those that use more than one blog + * + * @param int $id Blog ID. + * @return array List of users that are part of that Blog ID + */ +function get_users_of_blog( $id = '' ) { + _deprecated_function( __FUNCTION__, '3.1', 'get_users()' ); + + global $wpdb, $blog_id; + if ( empty($id) ) + $id = (int) $blog_id; + $blog_prefix = $wpdb->get_blog_prefix($id); + $users = $wpdb->get_results( "SELECT user_id, user_id AS ID, user_login, display_name, user_email, meta_value FROM $wpdb->users, $wpdb->usermeta WHERE {$wpdb->users}.ID = {$wpdb->usermeta}.user_id AND meta_key = '{$blog_prefix}capabilities' ORDER BY {$wpdb->usermeta}.user_id" ); + return $users; +} + +/** + * Enable/disable automatic general feed link outputting. + * + * @since 2.8.0 + * @deprecated 3.0.0 + * @deprecated Use add_theme_support( 'automatic-feed-links' ) + * + * @param boolean $add Optional, default is true. Add or remove links. Defaults to true. + */ +function automatic_feed_links( $add = true ) { + _deprecated_function( __FUNCTION__, '3.0', "add_theme_support( 'automatic-feed-links' )" ); + + if ( $add ) + add_theme_support( 'automatic-feed-links' ); + else + remove_action( 'wp_head', 'feed_links_extra', 3 ); // Just do this yourself in 3.0+ +} + +/** + * Retrieve user data based on field. + * + * @since 1.5.0 + * @deprecated 3.0.0 + * @deprecated Use get_the_author_meta() + * @see get_the_author_meta() + */ +function get_profile( $field, $user = false ) { + _deprecated_function( __FUNCTION__, '3.0', 'get_the_author_meta()' ); + if ( $user ) { + $user = get_user_by( 'login', $user ); + $user = $user->ID; + } + return get_the_author_meta( $field, $user ); +} + +/** + * Number of posts user has written. + * + * @since 0.71 + * @deprecated 3.0.0 + * @deprecated Use count_user_posts() + * @see count_user_posts() + */ +function get_usernumposts( $userid ) { + _deprecated_function( __FUNCTION__, '3.0', 'count_user_posts()' ); + return count_user_posts( $userid ); +} + +/** + * Callback used to change %uXXXX to &#YYY; syntax + * + * @since 2.8.0 + * @access private + * @deprecated 3.0.0 + * + * @param array $matches Single Match + * @return string An HTML entity + */ +function funky_javascript_callback($matches) { + return "&#".base_convert($matches[1],16,10).";"; +} + +/** + * Fixes JavaScript bugs in browsers. + * + * Converts unicode characters to HTML numbered entities. + * + * @since 1.5.0 + * @uses $is_macIE + * @uses $is_winIE + * @deprecated 3.0.0 + * + * @param string $text Text to be made safe. + * @return string Fixed text. + */ +function funky_javascript_fix($text) { + _deprecated_function( __FUNCTION__, '3.0' ); + // Fixes for browsers' JavaScript bugs. + global $is_macIE, $is_winIE; + + if ( $is_winIE || $is_macIE ) + $text = preg_replace_callback("/\%u([0-9A-F]{4,4})/", + "funky_javascript_callback", + $text); + + return $text; +} + +/** + * Checks that the taxonomy name exists. + * + * @since 2.3.0 + * @deprecated 3.0.0 + * @deprecated Use taxonomy_exists() + * @see taxonomy_exists() + * + * @param string $taxonomy Name of taxonomy object + * @return bool Whether the taxonomy exists. + */ +function is_taxonomy( $taxonomy ) { + _deprecated_function( __FUNCTION__, '3.0', 'taxonomy_exists()' ); + return taxonomy_exists( $taxonomy ); +} + +/** + * Check if Term exists. + * + * @since 2.3.0 + * @deprecated 3.0.0 + * @deprecated Use term_exists() + * @see term_exists() + * + * @param int|string $term The term to check + * @param string $taxonomy The taxonomy name to use + * @param int $parent ID of parent term under which to confine the exists search. + * @return mixed Get the term id or Term Object, if exists. + */ +function is_term( $term, $taxonomy = '', $parent = 0 ) { + _deprecated_function( __FUNCTION__, '3.0', 'term_exists()' ); + return term_exists( $term, $taxonomy, $parent ); +} + +/** + * Is the current admin page generated by a plugin? + * + * @since 1.5.0 + * @deprecated 3.1.0 + * @deprecated Use global $plugin_page and/or get_plugin_page_hookname() hooks. + * + * @global $plugin_page + * + * @return bool + */ +function is_plugin_page() { + _deprecated_function( __FUNCTION__, '3.1' ); + + global $plugin_page; + + if ( isset($plugin_page) ) + return true; + + return false; +} + +/** + * Update the categories cache. + * + * This function does not appear to be used anymore or does not appear to be + * needed. It might be a legacy function left over from when there was a need + * for updating the category cache. + * + * @since 1.5.0 + * @deprecated 3.1.0 + * + * @return bool Always return True + */ +function update_category_cache() { + _deprecated_function( __FUNCTION__, '3.1' ); + + return true; +} + +/** + * Check for PHP timezone support + * + * @since 2.9.0 + * @deprecated 3.2.0 + * + * @return bool + */ +function wp_timezone_supported() { + _deprecated_function( __FUNCTION__, '3.2' ); + + return true; +} + +/** + * Display editor: TinyMCE, HTML, or both. + * + * @since 2.1.0 + * @deprecated 3.3.0 + * @deprecated Use wp_editor() + * @see wp_editor() + * + * @param string $content Textarea content. + * @param string $id Optional, default is 'content'. HTML ID attribute value. + * @param string $prev_id Optional, not used + * @param bool $media_buttons Optional, default is true. Whether to display media buttons. + * @param int $tab_index Optional, not used + */ +function the_editor($content, $id = 'content', $prev_id = 'title', $media_buttons = true, $tab_index = 2, $extended = true) { + _deprecated_function( __FUNCTION__, '3.3', 'wp_editor()' ); + + wp_editor( $content, $id, array( 'media_buttons' => $media_buttons ) ); + return; +} + +/** + * Perform the query to get the $metavalues array(s) needed by _fill_user and _fill_many_users + * + * @since 3.0.0 + * @deprecated 3.3.0 + * + * @param array $ids User ID numbers list. + * @return array of arrays. The array is indexed by user_id, containing $metavalues object arrays. + */ +function get_user_metavalues($ids) { + _deprecated_function( __FUNCTION__, '3.3' ); + + $objects = array(); + + $ids = array_map('intval', $ids); + foreach ( $ids as $id ) + $objects[$id] = array(); + + $metas = update_meta_cache('user', $ids); + + foreach ( $metas as $id => $meta ) { + foreach ( $meta as $key => $metavalues ) { + foreach ( $metavalues as $value ) { + $objects[$id][] = (object)array( 'user_id' => $id, 'meta_key' => $key, 'meta_value' => $value); + } + } + } + + return $objects; +} + +/** + * Sanitize every user field. + * + * If the context is 'raw', then the user object or array will get minimal santization of the int fields. + * + * @since 2.3.0 + * @deprecated 3.3.0 + * + * @param object|array $user The User Object or Array + * @param string $context Optional, default is 'display'. How to sanitize user fields. + * @return object|array The now sanitized User Object or Array (will be the same type as $user) + */ +function sanitize_user_object($user, $context = 'display') { + _deprecated_function( __FUNCTION__, '3.3' ); + + if ( is_object($user) ) { + if ( !isset($user->ID) ) + $user->ID = 0; + if ( !is_a( $user, 'WP_User' ) ) { + $vars = get_object_vars($user); + foreach ( array_keys($vars) as $field ) { + if ( is_string($user->$field) || is_numeric($user->$field) ) + $user->$field = sanitize_user_field($field, $user->$field, $user->ID, $context); + } + } + $user->filter = $context; + } else { + if ( !isset($user['ID']) ) + $user['ID'] = 0; + foreach ( array_keys($user) as $field ) + $user[$field] = sanitize_user_field($field, $user[$field], $user['ID'], $context); + $user['filter'] = $context; + } + + return $user; +} + +/** + * Get boundary post relational link. + * + * Can either be start or end post relational link. + * + * @since 2.8.0 + * @deprecated 3.3.0 + * + * @param string $title Optional. Link title format. + * @param bool $in_same_cat Optional. Whether link should be in a same category. + * @param string $excluded_categories Optional. Excluded categories IDs. + * @param bool $start Optional, default is true. Whether to display link to first or last post. + * @return string + */ +function get_boundary_post_rel_link($title = '%title', $in_same_cat = false, $excluded_categories = '', $start = true) { + _deprecated_function( __FUNCTION__, '3.3' ); + + $posts = get_boundary_post($in_same_cat, $excluded_categories, $start); + // If there is no post stop. + if ( empty($posts) ) + return; + + // Even though we limited get_posts to return only 1 item it still returns an array of objects. + $post = $posts[0]; + + if ( empty($post->post_title) ) + $post->post_title = $start ? __('First Post') : __('Last Post'); + + $date = mysql2date(get_option('date_format'), $post->post_date); + + $title = str_replace('%title', $post->post_title, $title); + $title = str_replace('%date', $date, $title); + $title = apply_filters('the_title', $title, $post->ID); + + $link = $start ? "\n"; + + $boundary = $start ? 'start' : 'end'; + return apply_filters( "{$boundary}_post_rel_link", $link ); +} + +/** + * Display relational link for the first post. + * + * @since 2.8.0 + * @deprecated 3.3.0 + * + * @param string $title Optional. Link title format. + * @param bool $in_same_cat Optional. Whether link should be in a same category. + * @param string $excluded_categories Optional. Excluded categories IDs. + */ +function start_post_rel_link($title = '%title', $in_same_cat = false, $excluded_categories = '') { + _deprecated_function( __FUNCTION__, '3.3' ); + + echo get_boundary_post_rel_link($title, $in_same_cat, $excluded_categories, true); +} + +/** + * Get site index relational link. + * + * @since 2.8.0 + * @deprecated 3.3.0 + * + * @return string + */ +function get_index_rel_link() { + _deprecated_function( __FUNCTION__, '3.3' ); + + $link = "\n"; + return apply_filters( "index_rel_link", $link ); +} + +/** + * Display relational link for the site index. + * + * @since 2.8.0 + * @deprecated 3.3.0 + */ +function index_rel_link() { + _deprecated_function( __FUNCTION__, '3.3' ); + + echo get_index_rel_link(); +} + +/** + * Get parent post relational link. + * + * @since 2.8.0 + * @deprecated 3.3.0 + * + * @param string $title Optional. Link title format. + * @return string + */ +function get_parent_post_rel_link($title = '%title') { + _deprecated_function( __FUNCTION__, '3.3' ); + + if ( ! empty( $GLOBALS['post'] ) && ! empty( $GLOBALS['post']->post_parent ) ) + $post = get_post($GLOBALS['post']->post_parent); + + if ( empty($post) ) + return; + + $date = mysql2date(get_option('date_format'), $post->post_date); + + $title = str_replace('%title', $post->post_title, $title); + $title = str_replace('%date', $date, $title); + $title = apply_filters('the_title', $title, $post->ID); + + $link = "\n"; + + return apply_filters( "parent_post_rel_link", $link ); +} + +/** + * Display relational link for parent item + * + * @since 2.8.0 + * @deprecated 3.3.0 + */ +function parent_post_rel_link($title = '%title') { + _deprecated_function( __FUNCTION__, '3.3' ); + + echo get_parent_post_rel_link($title); +} + +/** + * Add the "Dashboard"/"Visit Site" menu. + * + * @since 3.2.0 + * @deprecated 3.3.0 + */ +function wp_admin_bar_dashboard_view_site_menu( $wp_admin_bar ) { + _deprecated_function( __FUNCTION__, '3.3' ); + + $user_id = get_current_user_id(); + + if ( 0 != $user_id ) { + if ( is_admin() ) + $wp_admin_bar->add_menu( array( 'id' => 'view-site', 'title' => __( 'Visit Site' ), 'href' => home_url() ) ); + elseif ( is_multisite() ) + $wp_admin_bar->add_menu( array( 'id' => 'dashboard', 'title' => __( 'Dashboard' ), 'href' => get_dashboard_url( $user_id ) ) ); + else + $wp_admin_bar->add_menu( array( 'id' => 'dashboard', 'title' => __( 'Dashboard' ), 'href' => admin_url() ) ); + } +} + +/** + * Checks if the current user belong to a given blog. + * + * @since MU + * @deprecated 3.3.0 + * @deprecated Use is_user_member_of_blog() + * @see is_user_member_of_blog() + * + * @param int $blog_id Blog ID + * @return bool True if the current users belong to $blog_id, false if not. + */ +function is_blog_user( $blog_id = 0 ) { + _deprecated_function( __FUNCTION__, '3.3', 'is_user_member_of_blog()' ); + + return is_user_member_of_blog( get_current_user_id(), $blog_id ); +} + +/** + * Open the file handle for debugging. + * + * @since 0.71 + * @deprecated Use error_log() + * @link http://www.php.net/manual/en/function.error-log.php + * @deprecated 3.4.0 + */ +function debug_fopen( $filename, $mode ) { + _deprecated_function( __FUNCTION__, 'error_log()' ); + return false; +} + +/** + * Write contents to the file used for debugging. + * + * @since 0.71 + * @deprecated Use error_log() instead. + * @link http://www.php.net/manual/en/function.error-log.php + * @deprecated 3.4.0 + */ +function debug_fwrite( $fp, $string ) { + _deprecated_function( __FUNCTION__, 'error_log()' ); + if ( ! empty( $GLOBALS['debug'] ) ) + error_log( $string ); +} + +/** + * Close the debugging file handle. + * + * @since 0.71 + * @deprecated Use error_log() + * @link http://www.php.net/manual/en/function.error-log.php + * @deprecated 3.4.0 + */ +function debug_fclose( $fp ) { + _deprecated_function( __FUNCTION__, 'error_log()' ); +} + +/** + * Retrieve list of themes with theme data in theme directory. + * + * The theme is broken, if it doesn't have a parent theme and is missing either + * style.css and, or index.php. If the theme has a parent theme then it is + * broken, if it is missing style.css; index.php is optional. + * + * @since 1.5.0 + * @deprecated 3.4.0 + * @deprecated Use wp_get_themes() + * @see wp_get_themes() + * + * @return array Theme list with theme data. + */ +function get_themes() { + _deprecated_function( __FUNCTION__, '3.4', 'wp_get_themes()' ); + + global $wp_themes; + if ( isset( $wp_themes ) ) + return $wp_themes; + + $themes = wp_get_themes(); + $wp_themes = array(); + + foreach ( $themes as $theme ) { + $name = $theme->get('Name'); + if ( isset( $wp_themes[ $name ] ) ) + $wp_themes[ $name . '/' . $theme->get_stylesheet() ] = $theme; + else + $wp_themes[ $name ] = $theme; + } + + return $wp_themes; +} + +/** + * Retrieve theme data. + * + * @since 1.5.0 + * @deprecated 3.4.0 + * @deprecated Use wp_get_theme() + * @see wp_get_theme() + * + * @param string $theme Theme name. + * @return array|null Null, if theme name does not exist. Theme data, if exists. + */ +function get_theme( $theme ) { + _deprecated_function( __FUNCTION__, '3.4', 'wp_get_theme( $stylesheet )' ); + + $themes = get_themes(); + if ( is_array( $themes ) && array_key_exists( $theme, $themes ) ) + return $themes[ $theme ]; + return null; +} + +/** + * Retrieve current theme name. + * + * @since 1.5.0 + * @deprecated 3.4.0 + * @deprecated Use (string) wp_get_theme() + * @see wp_get_theme() + * + * @return string + */ +function get_current_theme() { + _deprecated_function( __FUNCTION__, '3.4', 'wp_get_theme()' ); + + if ( $theme = get_option( 'current_theme' ) ) + return $theme; + + return wp_get_theme()->get('Name'); +} + +/** + * Accepts matches array from preg_replace_callback in wpautop() or a string. + * + * Ensures that the contents of a `
        ...
        ` HTML block are not + * converted into paragraphs or line-breaks. + * + * @since 1.2.0 + * @deprecated 3.4.0 + * + * @param array|string $matches The array or string + * @return string The pre block without paragraph/line-break conversion. + */ +function clean_pre($matches) { + _deprecated_function( __FUNCTION__, '3.4' ); + + if ( is_array($matches) ) + $text = $matches[1] . $matches[2] . ""; + else + $text = $matches; + + $text = str_replace(array('
        ', '
        ', '
        '), array('', '', ''), $text); + $text = str_replace('

        ', "\n", $text); + $text = str_replace('

        ', '', $text); + + return $text; +} + + +/** + * Add callbacks for image header display. + * + * @since 2.1.0 + * @deprecated 3.4.0 + * @deprecated Use add_theme_support('custom-header', $args) + * @see add_theme_support() + * + * @param callback $wp_head_callback Call on 'wp_head' action. + * @param callback $admin_head_callback Call on custom header administration screen. + * @param callback $admin_preview_callback Output a custom header image div on the custom header administration screen. Optional. + */ +function add_custom_image_header( $wp_head_callback, $admin_head_callback, $admin_preview_callback = '' ) { + _deprecated_function( __FUNCTION__, '3.4', 'add_theme_support( \'custom-header\', $args )' ); + $args = array( + 'wp-head-callback' => $wp_head_callback, + 'admin-head-callback' => $admin_head_callback, + ); + if ( $admin_preview_callback ) + $args['admin-preview-callback'] = $admin_preview_callback; + return add_theme_support( 'custom-header', $args ); +} + +/** + * Remove image header support. + * + * @since 3.1.0 + * @deprecated 3.4.0 + * @deprecated Use remove_theme_support('custom-header') + * @see remove_theme_support() + * + * @return null|bool Whether support was removed. + */ +function remove_custom_image_header() { + _deprecated_function( __FUNCTION__, '3.4', 'remove_theme_support( \'custom-header\' )' ); + return remove_theme_support( 'custom-header' ); +} + +/** + * Add callbacks for background image display. + * + * @since 3.0.0 + * @deprecated 3.4.0 + * @deprecated Use add_theme_support('custom-background, $args) + * @see add_theme_support() + * + * @param callback $wp_head_callback Call on 'wp_head' action. + * @param callback $admin_head_callback Call on custom background administration screen. + * @param callback $admin_preview_callback Output a custom background image div on the custom background administration screen. Optional. + */ +function add_custom_background( $wp_head_callback = '', $admin_head_callback = '', $admin_preview_callback = '' ) { + _deprecated_function( __FUNCTION__, '3.4', 'add_theme_support( \'custom-background\', $args )' ); + $args = array(); + if ( $wp_head_callback ) + $args['wp-head-callback'] = $wp_head_callback; + if ( $admin_head_callback ) + $args['admin-head-callback'] = $admin_head_callback; + if ( $admin_preview_callback ) + $args['admin-preview-callback'] = $admin_preview_callback; + return add_theme_support( 'custom-background', $args ); +} + +/** + * Remove custom background support. + * + * @since 3.1.0 + * @see add_custom_background() + * + * @return null|bool Whether support was removed. + */ +function remove_custom_background() { + _deprecated_function( __FUNCTION__, '3.4', 'remove_theme_support( \'custom-background\' )' ); + return remove_theme_support( 'custom-background' ); +} + +/** + * Retrieve theme data from parsed theme file. + * + * @since 1.5.0 + * @deprecated 3.4.0 + * @deprecated Use wp_get_theme() + * @see wp_get_theme() + * + * @param string $theme_file Theme file path. + * @return array Theme data. + */ +function get_theme_data( $theme_file ) { + _deprecated_function( __FUNCTION__, '3.4', 'wp_get_theme()' ); + $theme = new WP_Theme( basename( dirname( $theme_file ) ), dirname( dirname( $theme_file ) ) ); + + $theme_data = array( + 'Name' => $theme->get('Name'), + 'URI' => $theme->display('ThemeURI', true, false), + 'Description' => $theme->display('Description', true, false), + 'Author' => $theme->display('Author', true, false), + 'AuthorURI' => $theme->display('AuthorURI', true, false), + 'Version' => $theme->get('Version'), + 'Template' => $theme->get('Template'), + 'Status' => $theme->get('Status'), + 'Tags' => $theme->get('Tags'), + 'Title' => $theme->get('Name'), + 'AuthorName' => $theme->get('Author'), + ); + + foreach ( apply_filters( 'extra_theme_headers', array() ) as $extra_header ) { + if ( ! isset( $theme_data[ $extra_header ] ) ) + $theme_data[ $extra_header ] = $theme->get( $extra_header ); + } + + return $theme_data; +} + +/** + * Alias of update_post_cache(). + * + * @see update_post_cache() Posts and pages are the same, alias is intentional + * + * @since 1.5.1 + * @deprecated 3.4.0 + * + * @param array $pages list of page objects + */ +function update_page_cache( &$pages ) { + _deprecated_function( __FUNCTION__, '3.4', 'update_post_cache()' ); + + update_post_cache( $pages ); +} + +/** + * Will clean the page in the cache. + * + * Clean (read: delete) page from cache that matches $id. Will also clean cache + * associated with 'all_page_ids' and 'get_pages'. + * + * @since 2.0.0 + * @deprecated 3.4.0 + * + * @param int $id Page ID to clean + */ +function clean_page_cache( $id ) { + _deprecated_function( __FUNCTION__, '3.4', 'clean_post_cache()' ); + + clean_post_cache( $id ); +} + +/** + * Retrieve nonce action "Are you sure" message. + * + * Deprecated in 3.4.1 and 3.5.0. Backported to 3.3.3. + * + * @since 2.0.4 + * @deprecated 3.4.1 + * @deprecated Use wp_nonce_ays() + * @see wp_nonce_ays() + * + * @param string $action Nonce action. + * @return string Are you sure message. + */ +function wp_explain_nonce( $action ) { + _deprecated_function( __FUNCTION__, '3.4.1', 'wp_nonce_ays()' ); + return __( 'Are you sure you want to do this?' ); +} + +/** + * Display "sticky" CSS class, if a post is sticky. + * + * @since 2.7.0 + * @deprecated 3.5.0 + * @deprecated Use post_class() + * @see post_class() + * + * @param int $post_id An optional post ID. + */ +function sticky_class( $post_id = null ) { + _deprecated_function( __FUNCTION__, '3.5', 'post_class()' ); + if ( is_sticky( $post_id ) ) + echo ' sticky'; +} + +/** + * Retrieve post ancestors. + * + * This is no longer needed as WP_Post lazy-loads the ancestors + * property with get_post_ancestors(). + * + * @since 2.3.4 + * @deprecated 3.5.0 + * @see get_post_ancestors() + */ +function _get_post_ancestors( &$post ) { + _deprecated_function( __FUNCTION__, '3.5' ); +} + +/** + * Load an image from a string, if PHP supports it. + * + * @since 2.1.0 + * @deprecated 3.5.0 + * @see wp_get_image_editor() + * + * @param string $file Filename of the image to load. + * @return resource The resulting image resource on success, Error string on failure. + */ +function wp_load_image( $file ) { + _deprecated_function( __FUNCTION__, '3.5', 'wp_get_image_editor()' ); + + if ( is_numeric( $file ) ) + $file = get_attached_file( $file ); + + if ( ! is_file( $file ) ) + return sprintf(__('File “%s” doesn’t exist?'), $file); + + if ( ! function_exists('imagecreatefromstring') ) + return __('The GD image library is not installed.'); + + // Set artificially high because GD uses uncompressed images in memory + @ini_set( 'memory_limit', apply_filters( 'image_memory_limit', WP_MAX_MEMORY_LIMIT ) ); + $image = imagecreatefromstring( file_get_contents( $file ) ); + + if ( !is_resource( $image ) ) + return sprintf(__('File “%s” is not an image.'), $file); + + return $image; +} + +/** + * Scale down an image to fit a particular size and save a new copy of the image. + * + * The PNG transparency will be preserved using the function, as well as the + * image type. If the file going in is PNG, then the resized image is going to + * be PNG. The only supported image types are PNG, GIF, and JPEG. + * + * Some functionality requires API to exist, so some PHP version may lose out + * support. This is not the fault of WordPress (where functionality is + * downgraded, not actual defects), but of your PHP version. + * + * @since 2.5.0 + * @deprecated 3.5.0 + * @see wp_get_image_editor() + * + * @param string $file Image file path. + * @param int $max_w Maximum width to resize to. + * @param int $max_h Maximum height to resize to. + * @param bool $crop Optional. Whether to crop image or resize. + * @param string $suffix Optional. File suffix. + * @param string $dest_path Optional. New image file path. + * @param int $jpeg_quality Optional, default is 90. Image quality percentage. + * @return mixed WP_Error on failure. String with new destination path. + */ +function image_resize( $file, $max_w, $max_h, $crop = false, $suffix = null, $dest_path = null, $jpeg_quality = 90 ) { + _deprecated_function( __FUNCTION__, '3.5', 'wp_get_image_editor()' ); + + $editor = wp_get_image_editor( $file ); + if ( is_wp_error( $editor ) ) + return $editor; + $editor->set_quality( $jpeg_quality ); + + $resized = $editor->resize( $max_w, $max_h, $crop ); + if ( is_wp_error( $resized ) ) + return $resized; + + $dest_file = $editor->generate_filename( $suffix, $dest_path ); + $saved = $editor->save( $dest_file ); + + if ( is_wp_error( $saved ) ) + return $saved; + + return $dest_file; +} + +/** + * Retrieve a single post, based on post ID. + * + * Has categories in 'post_category' property or key. Has tags in 'tags_input' + * property or key. + * + * @since 1.0.0 + * @deprecated 3.5.0 + * @see get_post() + * + * @param int $postid Post ID. + * @param string $mode How to return result, either OBJECT, ARRAY_N, or ARRAY_A. + * @return WP_Post|null Post object or array holding post contents and information + */ +function wp_get_single_post( $postid = 0, $mode = OBJECT ) { + _deprecated_function( __FUNCTION__, '3.5', 'get_post()' ); + return get_post( $postid, $mode ); +} + +/** + * Check that the user login name and password is correct. + * + * @since 0.71 + * @deprecated 3.5.0 + * @deprecated Use wp_authenticate() + * @see wp_authenticate() + * + * @param string $user_login User name. + * @param string $user_pass User password. + * @return bool False if does not authenticate, true if username and password authenticates. + */ +function user_pass_ok($user_login, $user_pass) { + _deprecated_function( __FUNCTION__, '3.5', 'wp_authenticate()' ); + $user = wp_authenticate( $user_login, $user_pass ); + if ( is_wp_error( $user ) ) + return false; + + return true; +} + +/** + * Callback formerly fired on the save_post hook. No longer needed. + * + * @since 2.3.0 + * @deprecated 3.5.0 + */ +function _save_post_hook() {} + +/** + * Check if the installed version of GD supports particular image type + * + * @since 2.9.0 + * @deprecated 3.5.0 + * @see wp_image_editor_supports() + * + * @param string $mime_type + * @return bool + */ +function gd_edit_image_support($mime_type) { + _deprecated_function( __FUNCTION__, '3.5', 'wp_image_editor_supports()' ); + + if ( function_exists('imagetypes') ) { + switch( $mime_type ) { + case 'image/jpeg': + return (imagetypes() & IMG_JPG) != 0; + case 'image/png': + return (imagetypes() & IMG_PNG) != 0; + case 'image/gif': + return (imagetypes() & IMG_GIF) != 0; + } + } else { + switch( $mime_type ) { + case 'image/jpeg': + return function_exists('imagecreatefromjpeg'); + case 'image/png': + return function_exists('imagecreatefrompng'); + case 'image/gif': + return function_exists('imagecreatefromgif'); + } + } + return false; +} + +/** + * Converts an integer byte value to a shorthand byte value. + * + * @since 2.3.0 + * @deprecated 3.6.0 + * @deprecated Use size_format() + * + * @param int $bytes An integer byte value. + * @return string A shorthand byte value. + */ +function wp_convert_bytes_to_hr( $bytes ) { + _deprecated_function( __FUNCTION__, '3.6', 'size_format()' ); + + $units = array( 0 => 'B', 1 => 'kB', 2 => 'MB', 3 => 'GB', 4 => 'TB' ); + $log = log( $bytes, 1024 ); + $power = (int) $log; + $size = pow( 1024, $log - $power ); + + if ( ! is_nan( $size ) && array_key_exists( $power, $units ) ) { + $unit = $units[ $power ]; + } else { + $size = $bytes; + $unit = $units[0]; + } + + return $size . $unit; +} + +/** + * Formerly used internally to tidy up the search terms. + * + * @access private + * @since 2.9.0 + * @deprecated 3.7.0 + */ +function _search_terms_tidy( $t ) { + _deprecated_function( __FUNCTION__, '3.7' ); + return trim( $t, "\"'\n\r " ); +} + +/** + * Determine if TinyMCE is available. + * + * Checks to see if the user has deleted the tinymce files to slim down + * their WordPress install. + * + * @since 2.1.0 + * @deprecated 3.9.0 + * + * @return bool Whether TinyMCE exists. + */ +function rich_edit_exists() { + global $wp_rich_edit_exists; + _deprecated_function( __FUNCTION__, '3.9' ); + + if ( ! isset( $wp_rich_edit_exists ) ) + $wp_rich_edit_exists = file_exists( ABSPATH . WPINC . '/js/tinymce/tinymce.js' ); + + return $wp_rich_edit_exists; +} + +/** + * Old callback for tag link tooltips. + * + * @since 2.7.0 + * @deprecated 3.9.0 + * @access private + */ +function default_topic_count_text( $count ) { + return $count; +} + +/** + * Formerly used to escape strings before inserting into the DB. + * + * Has not performed this function for many, many years. Use wpdb::prepare() instead. + * + * @since 0.71 + * @deprecated 3.9.0 + * + * @param string $content The text to format. + * @return string The very same text. + */ +function format_to_post( $content ) { + _deprecated_function( __FUNCTION__, '3.9' ); + return $content; +} + +/** + * Formerly used to escape strings before searching the DB. It was poorly documented and never worked as described. + * + * @since 2.5.0 + * @deprecated 4.0.0 + * @deprecated Use wpdb::esc_like() + * + * @param string $text The text to be escaped. + * @return string text, safe for inclusion in LIKE query. + */ +function like_escape($text) { + _deprecated_function( __FUNCTION__, '4.0', 'wpdb::esc_like()' ); + return str_replace( array( "%", "_" ), array( "\\%", "\\_" ), $text ); +} + +/** + * Determines if the URL can be accessed over SSL. + * + * Determines if the URL can be accessed over SSL by using the WordPress HTTP API to access + * the URL using https as the scheme. + * + * @since 2.5.0 + * @deprecated 4.0.0 + * + * @param string $url The URL to test. + * @return bool Whether SSL access is available. + */ +function url_is_accessable_via_ssl( $url ) { + _deprecated_function( __FUNCTION__, '4.0' ); + + $response = wp_remote_get( set_url_scheme( $url, 'https' ) ); + + if ( !is_wp_error( $response ) ) { + $status = wp_remote_retrieve_response_code( $response ); + if ( 200 == $status || 401 == $status ) { + return true; + } + } + + return false; +} diff --git a/wp-includes/feed-atom-comments.php b/wp-includes/feed-atom-comments.php new file mode 100644 index 0000000..d9c8f65 --- /dev/null +++ b/wp-includes/feed-atom-comments.php @@ -0,0 +1,115 @@ +'; + +/** This action is documented in wp-includes/feed-rss2.php */ +do_action( 'rss_tag_pre', 'atom-comments' ); +?> + +> + <?php + if ( is_singular() ) + printf( ent2ncr( __( 'Comments on %s' ) ), get_the_title_rss() ); + elseif ( is_search() ) + printf( ent2ncr( __( 'Comments for %1$s searching on %2$s' ) ), get_bloginfo_rss( 'name' ), get_search_query() ); + else + printf( ent2ncr( __( 'Comments for %s' ) ), get_bloginfo_rss( 'name' ) . get_wp_title_rss() ); + ?> + + + + + + + + + + + + + + + + + + +comment_post_ID ); +?> + + <?php + if ( !is_singular() ) { + $title = get_the_title($comment_post->ID); + /** This filter is documented in wp-includes/feed.php */ + $title = apply_filters( 'the_title_rss', $title ); + printf(ent2ncr(__('Comment on %1$s by %2$s')), $title, get_comment_author_rss()); + } else { + printf(ent2ncr(__('By: %s')), get_comment_author_rss()); + } + ?> + + + + + ' . get_comment_author_url() . ''; ?> + + + + + + + + ]]> + + ]]> +comment_parent == 0 ) : // This comment is top level ?> + +comment_parent); + // The rel attribute below and the id tag above should be GUIDs, but WP doesn't create them for comments (unlike posts). Either way, it's more important that they both use the same system +?> + +comment_ID, $comment_post->ID ); +?> + + + diff --git a/wp-includes/feed-atom.php b/wp-includes/feed-atom.php new file mode 100644 index 0000000..ae4cd79 --- /dev/null +++ b/wp-includes/feed-atom.php @@ -0,0 +1,87 @@ +'; + +/** This action is documented in wp-includes/feed-rss2.php */ +do_action( 'rss_tag_pre', 'atom' ); +?> + + > + <?php bloginfo_rss('name'); wp_title_rss(); ?> + + + + + + + + + + + + + + + + + <![CDATA[<?php the_title_rss() ?>]]> + + + + + + ]]> + + ]]> + + + + + + + + diff --git a/wp-includes/feed-rdf.php b/wp-includes/feed-rdf.php new file mode 100644 index 0000000..1e8327b --- /dev/null +++ b/wp-includes/feed-rdf.php @@ -0,0 +1,85 @@ +'; + +/** This action is documented in wp-includes/feed-rss2.php */ +do_action( 'rss_tag_pre', 'rdf' ); +?> + +> +"> + <?php bloginfo_rss('name'); wp_title_rss(); ?> + + + + + + 2000-01-01T12:00+00:00 + + + + + + + + + + + + <?php the_title_rss() ?> + + post_date_gmt, false); ?> + ]]> + + + ]]> + + ]]> + ]]> + + + + + diff --git a/wp-includes/feed-rss.php b/wp-includes/feed-rss.php new file mode 100644 index 0000000..1650434 --- /dev/null +++ b/wp-includes/feed-rss.php @@ -0,0 +1,46 @@ +'; ?> + + + <?php bloginfo_rss('name'); wp_title_rss(); ?> + + + + http://backend.userland.com/rss092 + + + + + + + <?php the_title_rss() ?> + ]]> + + + + + + diff --git a/wp-includes/feed-rss2-comments.php b/wp-includes/feed-rss2-comments.php new file mode 100644 index 0000000..703fa87 --- /dev/null +++ b/wp-includes/feed-rss2-comments.php @@ -0,0 +1,101 @@ +'; + +/** This action is documented in wp-includes/feed-rss2.php */ +do_action( 'rss_tag_pre', 'rss2-comments' ); +?> + + + +> + + <?php + if ( is_singular() ) + printf( ent2ncr( __( 'Comments on: %s' ) ), get_the_title_rss() ); + elseif ( is_search() ) + printf( ent2ncr( __( 'Comments for %1$s searching on %2$s' ) ), get_bloginfo_rss( 'name' ), get_search_query() ); + else + printf( ent2ncr( __( 'Comments for %s' ) ), get_bloginfo_rss( 'name' ) . get_wp_title_rss() ); + ?> + + + + + + + comment_post_ID ); + ?> + + <?php + if ( !is_singular() ) { + $title = get_the_title($comment_post->ID); + /** This filter is documented in wp-includes/feed.php */ + $title = apply_filters( 'the_title_rss', $title ); + printf(ent2ncr(__('Comment on %1$s by %2$s')), $title, get_comment_author_rss()); + } else { + printf(ent2ncr(__('By: %s')), get_comment_author_rss()); + } + ?> + + ]]> + + + + + ]]> + + ]]> + ]]> +comment_ID The ID of the comment being displayed. + * @param int $comment_post->ID The ID of the post the comment is connected to. + */ + do_action( 'commentrss2_item', $comment->comment_ID, $comment_post->ID ); +?> + + + + diff --git a/wp-includes/feed-rss2.php b/wp-includes/feed-rss2.php new file mode 100644 index 0000000..2390c2d --- /dev/null +++ b/wp-includes/feed-rss2.php @@ -0,0 +1,117 @@ +'; + +/** + * Fires between the xml and rss tags in a feed. + * + * @since 4.0.0 + * + * @param string $context Type of feed. Possible values include 'rss2', 'rss2-comments', + * 'rdf', 'atom', and 'atom-comments'. + */ +do_action( 'rss_tag_pre', 'rss2' ); +?> + +> + + + <?php bloginfo_rss('name'); wp_title_rss(); ?> + + + + + + + + + + <?php the_title_rss() ?> + + + + ]]> + + + + + ]]> + + ]]> + + 0 ) : ?> + ]]> + + ]]> + + + + + + + + + + diff --git a/wp-includes/feed.php b/wp-includes/feed.php new file mode 100644 index 0000000..8638554 --- /dev/null +++ b/wp-includes/feed.php @@ -0,0 +1,651 @@ +get_error_message(); + } + + if ( $title && $sep && ' ' !== substr( $title, 0, 1 ) ) { + $title = " $sep " . $title; + } + + /** + * Filter the blog title for use as the feed title. + * + * @since 2.2.0 + * + * @param string $title The current blog title. + * @param string $sep Separator used by wp_title(). + */ + $title = apply_filters( 'get_wp_title_rss', $title, $sep ); + return $title; +} + +/** + * Display the blog title for display of the feed title. + * + * @since 2.2.0 + * @see wp_title() $sep parameter usage. + * + * @param string $sep Optional. + */ +function wp_title_rss( $sep = '»' ) { + /** + * Filter the blog title for display of the feed title. + * + * @since 2.2.0 + * + * @see get_wp_title_rss() + * + * @param string $wp_title The current blog title. + * @param string $sep Separator used by wp_title(). + */ + echo apply_filters( 'wp_title_rss', get_wp_title_rss( $sep ), $sep ); +} + +/** + * Retrieve the current post title for the feed. + * + * @since 2.0.0 + * + * @return string Current post title. + */ +function get_the_title_rss() { + $title = get_the_title(); + /** + * Filter the post title for use in a feed. + * + * @since 1.2.0 + * + * @param string $title The current post title. + */ + $title = apply_filters( 'the_title_rss', $title ); + return $title; +} + +/** + * Display the post title in the feed. + * + * @since 0.71 + */ +function the_title_rss() { + echo get_the_title_rss(); +} + +/** + * Retrieve the post content for feeds. + * + * @since 2.9.0 + * @see get_the_content() + * + * @param string $feed_type The type of feed. rss2 | atom | rss | rdf + * @return string The filtered content. + */ +function get_the_content_feed($feed_type = null) { + if ( !$feed_type ) + $feed_type = get_default_feed(); + + /** This filter is documented in wp-includes/post-template.php */ + $content = apply_filters( 'the_content', get_the_content() ); + $content = str_replace(']]>', ']]>', $content); + /** + * Filter the post content for use in feeds. + * + * @since 2.9.0 + * + * @param string $content The current post content. + * @param string $feed_type Type of feed. Possible values include 'rss2', 'atom'. + * Default 'rss2'. + */ + return apply_filters( 'the_content_feed', $content, $feed_type ); +} + +/** + * Display the post content for feeds. + * + * @since 2.9.0 + * + * @param string $feed_type The type of feed. rss2 | atom | rss | rdf + */ +function the_content_feed($feed_type = null) { + echo get_the_content_feed($feed_type); +} + +/** + * Display the post excerpt for the feed. + * + * @since 0.71 + */ +function the_excerpt_rss() { + $output = get_the_excerpt(); + /** + * Filter the post excerpt for a feed. + * + * @since 1.2.0 + * + * @param string $output The current post excerpt. + */ + echo apply_filters( 'the_excerpt_rss', $output ); +} + +/** + * Display the permalink to the post for use in feeds. + * + * @since 2.3.0 + */ +function the_permalink_rss() { + /** + * Filter the permalink to the post for use in feeds. + * + * @since 2.3.0 + * + * @param string $post_permalink The current post permalink. + */ + echo esc_url( apply_filters( 'the_permalink_rss', get_permalink() ) ); +} + +/** + * Outputs the link to the comments for the current post in an xml safe way + * + * @since 3.0.0 + * @return none + */ +function comments_link_feed() { + /** + * Filter the comments permalink for the current post. + * + * @since 3.6.0 + * + * @param string $comment_permalink The current comment permalink with + * '#comments' appended. + */ + echo esc_url( apply_filters( 'comments_link_feed', get_comments_link() ) ); +} + +/** + * Display the feed GUID for the current comment. + * + * @since 2.5.0 + * + * @param int|object $comment_id Optional comment object or id. Defaults to global comment object. + */ +function comment_guid($comment_id = null) { + echo esc_url( get_comment_guid($comment_id) ); +} + +/** + * Retrieve the feed GUID for the current comment. + * + * @since 2.5.0 + * + * @param int|object $comment_id Optional comment object or id. Defaults to global comment object. + * @return false|string false on failure or guid for comment on success. + */ +function get_comment_guid($comment_id = null) { + $comment = get_comment($comment_id); + + if ( !is_object($comment) ) + return false; + + return get_the_guid($comment->comment_post_ID) . '#comment-' . $comment->comment_ID; +} + +/** + * Display the link to the comments. + * + * @since 1.5.0 + */ +function comment_link() { + /** + * Filter the current comment's permalink. + * + * @since 3.6.0 + * + * @see get_comment_link() + * + * @param string $comment_permalink The current comment permalink. + */ + echo esc_url( apply_filters( 'comment_link', get_comment_link() ) ); +} + +/** + * Retrieve the current comment author for use in the feeds. + * + * @since 2.0.0 + * + * @return string Comment Author + */ +function get_comment_author_rss() { + /** + * Filter the current comment author for use in a feed. + * + * @since 1.5.0 + * + * @see get_comment_author() + * + * @param string $comment_author The current comment author. + */ + return apply_filters( 'comment_author_rss', get_comment_author() ); +} + +/** + * Display the current comment author in the feed. + * + * @since 1.0.0 + */ +function comment_author_rss() { + echo get_comment_author_rss(); +} + +/** + * Display the current comment content for use in the feeds. + * + * @since 1.0.0 + */ +function comment_text_rss() { + $comment_text = get_comment_text(); + /** + * Filter the current comment content for use in a feed. + * + * @since 1.5.0 + * + * @param string $comment_text The content of the current comment. + */ + $comment_text = apply_filters( 'comment_text_rss', $comment_text ); + echo $comment_text; +} + +/** + * Retrieve all of the post categories, formatted for use in feeds. + * + * All of the categories for the current post in the feed loop, will be + * retrieved and have feed markup added, so that they can easily be added to the + * RSS2, Atom, or RSS1 and RSS0.91 RDF feeds. + * + * @since 2.1.0 + * + * @param string $type Optional, default is the type returned by get_default_feed(). + * @return string All of the post categories for displaying in the feed. + */ +function get_the_category_rss($type = null) { + if ( empty($type) ) + $type = get_default_feed(); + $categories = get_the_category(); + $tags = get_the_tags(); + $the_list = ''; + $cat_names = array(); + + $filter = 'rss'; + if ( 'atom' == $type ) + $filter = 'raw'; + + if ( !empty($categories) ) foreach ( (array) $categories as $category ) { + $cat_names[] = sanitize_term_field('name', $category->name, $category->term_id, 'category', $filter); + } + + if ( !empty($tags) ) foreach ( (array) $tags as $tag ) { + $cat_names[] = sanitize_term_field('name', $tag->name, $tag->term_id, 'post_tag', $filter); + } + + $cat_names = array_unique($cat_names); + + foreach ( $cat_names as $cat_name ) { + if ( 'rdf' == $type ) + $the_list .= "\t\t\n"; + elseif ( 'atom' == $type ) + $the_list .= sprintf( '', esc_attr( get_bloginfo_rss( 'url' ) ), esc_attr( $cat_name ) ); + else + $the_list .= "\t\t\n"; + } + + /** + * Filter all of the post categories for display in a feed. + * + * @since 1.2.0 + * + * @param string $the_list All of the RSS post categories. + * @param string $type Type of feed. Possible values include 'rss2', 'atom'. + * Default 'rss2'. + */ + return apply_filters( 'the_category_rss', $the_list, $type ); +} + +/** + * Display the post categories in the feed. + * + * @since 0.71 + * @see get_the_category_rss() For better explanation. + * + * @param string $type Optional, default is the type returned by get_default_feed(). + */ +function the_category_rss($type = null) { + echo get_the_category_rss($type); +} + +/** + * Display the HTML type based on the blog setting. + * + * The two possible values are either 'xhtml' or 'html'. + * + * @since 2.2.0 + */ +function html_type_rss() { + $type = get_bloginfo('html_type'); + if (strpos($type, 'xhtml') !== false) + $type = 'xhtml'; + else + $type = 'html'; + echo $type; +} + +/** + * Display the rss enclosure for the current post. + * + * Uses the global $post to check whether the post requires a password and if + * the user has the password for the post. If not then it will return before + * displaying. + * + * Also uses the function get_post_custom() to get the post's 'enclosure' + * metadata field and parses the value to display the enclosure(s). The + * enclosure(s) consist of enclosure HTML tag(s) with a URI and other + * attributes. + * + * @since 1.5.0 + */ +function rss_enclosure() { + if ( post_password_required() ) + return; + + foreach ( (array) get_post_custom() as $key => $val) { + if ($key == 'enclosure') { + foreach ( (array) $val as $enc ) { + $enclosure = explode("\n", $enc); + + // only get the first element, e.g. audio/mpeg from 'audio/mpeg mpga mp2 mp3' + $t = preg_split('/[ \t]/', trim($enclosure[2]) ); + $type = $t[0]; + + /** + * Filter the RSS enclosure HTML link tag for the current post. + * + * @since 2.2.0 + * + * @param string $html_link_tag The HTML link tag with a URI and other attributes. + */ + echo apply_filters( 'rss_enclosure', '' . "\n" ); + } + } + } +} + +/** + * Display the atom enclosure for the current post. + * + * Uses the global $post to check whether the post requires a password and if + * the user has the password for the post. If not then it will return before + * displaying. + * + * Also uses the function get_post_custom() to get the post's 'enclosure' + * metadata field and parses the value to display the enclosure(s). The + * enclosure(s) consist of link HTML tag(s) with a URI and other attributes. + * + * @since 2.2.0 + */ +function atom_enclosure() { + if ( post_password_required() ) + return; + + foreach ( (array) get_post_custom() as $key => $val ) { + if ($key == 'enclosure') { + foreach ( (array) $val as $enc ) { + $enclosure = explode("\n", $enc); + /** + * Filter the atom enclosure HTML link tag for the current post. + * + * @since 2.2.0 + * + * @param string $html_link_tag The HTML link tag with a URI and other attributes. + */ + echo apply_filters( 'atom_enclosure', '' . "\n" ); + } + } + } +} + +/** + * Determine the type of a string of data with the data formatted. + * + * Tell whether the type is text, html, or xhtml, per RFC 4287 section 3.1. + * + * In the case of WordPress, text is defined as containing no markup, + * xhtml is defined as "well formed", and html as tag soup (i.e., the rest). + * + * Container div tags are added to xhtml values, per section 3.1.1.3. + * + * @link http://www.atomenabled.org/developers/syndication/atom-format-spec.php#rfc.section.3.1 + * + * @since 2.5.0 + * + * @param string $data Input string + * @return array array(type, value) + */ +function prep_atom_text_construct($data) { + if (strpos($data, '<') === false && strpos($data, '&') === false) { + return array('text', $data); + } + + $parser = xml_parser_create(); + xml_parse($parser, '
        ' . $data . '
        ', true); + $code = xml_get_error_code($parser); + xml_parser_free($parser); + + if (!$code) { + if (strpos($data, '<') === false) { + return array('text', $data); + } else { + $data = "
        $data
        "; + return array('xhtml', $data); + } + } + + if (strpos($data, ']]>') == false) { + return array('html', ""); + } else { + return array('html', htmlspecialchars($data)); + } +} + +/** + * Display the link for the currently displayed feed in a XSS safe way. + * + * Generate a correct link for the atom:self element. + * + * @since 2.5.0 + */ +function self_link() { + $host = @parse_url(home_url()); + /** + * Filter the current feed URL. + * + * @since 3.6.0 + * + * @see set_url_scheme() + * @see wp_unslash() + * + * @param string $feed_link The link for the feed with set URL scheme. + */ + echo esc_url( apply_filters( 'self_link', set_url_scheme( 'http://' . $host['host'] . wp_unslash( $_SERVER['REQUEST_URI'] ) ) ) ); +} + +/** + * Return the content type for specified feed type. + * + * @since 2.8.0 + */ +function feed_content_type( $type = '' ) { + if ( empty($type) ) + $type = get_default_feed(); + + $types = array( + 'rss' => 'application/rss+xml', + 'rss2' => 'application/rss+xml', + 'rss-http' => 'text/xml', + 'atom' => 'application/atom+xml', + 'rdf' => 'application/rdf+xml' + ); + + $content_type = ( !empty($types[$type]) ) ? $types[$type] : 'application/octet-stream'; + + /** + * Filter the content type for a specific feed type. + * + * @since 2.8.0 + * + * @param string $content_type Content type indicating the type of data that a feed contains. + * @param string $type Type of feed. Possible values include 'rss2', 'atom'. + * Default 'rss2'. + */ + return apply_filters( 'feed_content_type', $content_type, $type ); +} + +/** + * Build SimplePie object based on RSS or Atom feed from URL. + * + * @since 2.8.0 + * + * @param mixed $url URL of feed to retrieve. If an array of URLs, the feeds are merged + * using SimplePie's multifeed feature. + * See also {@link ​http://simplepie.org/wiki/faq/typical_multifeed_gotchas} + * + * @return WP_Error|SimplePie WP_Error object on failure or SimplePie object on success + */ +function fetch_feed( $url ) { + require_once( ABSPATH . WPINC . '/class-feed.php' ); + + $feed = new SimplePie(); + + $feed->set_sanitize_class( 'WP_SimplePie_Sanitize_KSES' ); + // We must manually overwrite $feed->sanitize because SimplePie's + // constructor sets it before we have a chance to set the sanitization class + $feed->sanitize = new WP_SimplePie_Sanitize_KSES(); + + $feed->set_cache_class( 'WP_Feed_Cache' ); + $feed->set_file_class( 'WP_SimplePie_File' ); + + $feed->set_feed_url( $url ); + /** This filter is documented in wp-includes/class-feed.php */ + $feed->set_cache_duration( apply_filters( 'wp_feed_cache_transient_lifetime', 12 * HOUR_IN_SECONDS, $url ) ); + /** + * Fires just before processing the SimplePie feed object. + * + * @since 3.0.0 + * + * @param object &$feed SimplePie feed object, passed by reference. + * @param mixed $url URL of feed to retrieve. If an array of URLs, the feeds are merged. + */ + do_action_ref_array( 'wp_feed_options', array( &$feed, $url ) ); + $feed->init(); + $feed->handle_content_type(); + + if ( $feed->error() ) + return new WP_Error( 'simplepie-error', $feed->error() ); + + return $feed; +} diff --git a/wp-includes/fonts/dashicons.eot b/wp-includes/fonts/dashicons.eot new file mode 100644 index 0000000..2e1104a Binary files /dev/null and b/wp-includes/fonts/dashicons.eot differ diff --git a/wp-includes/fonts/dashicons.svg b/wp-includes/fonts/dashicons.svg new file mode 100644 index 0000000..7a6d927 --- /dev/null +++ b/wp-includes/fonts/dashicons.svg @@ -0,0 +1,253 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/wp-includes/fonts/dashicons.ttf b/wp-includes/fonts/dashicons.ttf new file mode 100644 index 0000000..59ea8f7 Binary files /dev/null and b/wp-includes/fonts/dashicons.ttf differ diff --git a/wp-includes/fonts/dashicons.woff b/wp-includes/fonts/dashicons.woff new file mode 100644 index 0000000..7547f9f Binary files /dev/null and b/wp-includes/fonts/dashicons.woff differ diff --git a/wp-includes/formatting.php b/wp-includes/formatting.php new file mode 100644 index 0000000..3d4d1f2 --- /dev/null +++ b/wp-includes/formatting.php @@ -0,0 +1,4005 @@ + array(), 'quote' => array(), 'dash' => array() ); + $dynamic_replacements = array( 'apos' => array(), 'quote' => array(), 'dash' => array() ); + $dynamic = array(); + $spaces = wp_spaces_regexp(); + + // '99' and '99" are ambiguous among other patterns; assume it's an abbreviated year at the end of a quotation. + if ( "'" !== $apos || "'" !== $closing_single_quote ) { + $dynamic[ '/\'(\d\d)\'(?=\Z|[.,)}\-\]]|>|' . $spaces . ')/' ] = $apos . '$1' . $closing_single_quote; + } + if ( "'" !== $apos || '"' !== $closing_quote ) { + $dynamic[ '/\'(\d\d)"(?=\Z|[.,)}\-\]]|>|' . $spaces . ')/' ] = $apos . '$1' . $closing_quote; + } + + // '99 '99s '99's (apostrophe) But never '9 or '99% or '999 or '99.0. + if ( "'" !== $apos ) { + $dynamic[ '/\'(?=\d\d(?:\Z|(?![%\d]|[.,]\d)))/' ] = $apos; + } + + // Quoted Numbers like '0.42' + if ( "'" !== $opening_single_quote && "'" !== $closing_single_quote ) { + $dynamic[ '/(?<=\A|' . $spaces . ')\'(\d[.,\d]*)\'/' ] = $opening_single_quote . '$1' . $closing_single_quote; + } + + // Single quote at start, or preceded by (, {, <, [, ", -, or spaces. + if ( "'" !== $opening_single_quote ) { + $dynamic[ '/(?<=\A|[([{"\-]|<|' . $spaces . ')\'/' ] = $opening_single_quote; + } + + // Apostrophe in a word. No spaces, double apostrophes, or other punctuation. + if ( "'" !== $apos ) { + $dynamic[ '/(? is found. + . '-(?!->)' // Dash not followed by end of comment. + . '[^\-]*+' // Consume non-dashes. + . ')*+' // Loop possessively. + . '(?:-->)?'; // End of comment. If not found, match all input. + + $shortcode_regex = + '\[' // Find start of shortcode. + . '[\/\[]?' // Shortcodes may begin with [/ or [[ + . $tagregexp // Only match registered shortcodes, because performance. + . '(?:' + . '[^\[\]<>]+' // Shortcodes do not contain other shortcodes. Quantifier critical. + . '|' + . '<[^\[\]>]*>' // HTML elements permitted. Prevents matching ] before >. + . ')*+' // Possessive critical. + . '\]' // Find end of shortcode. + . '\]?'; // Shortcodes may end with ]] + + $regex = + '/(' // Capture the entire match. + . '<' // Find start of element. + . '(?(?=!--)' // Is this a comment? + . $comment_regex // Find end of comment. + . '|' + . '[^>]+>' // Find end of element. + . ')' + . '|' + . $shortcode_regex // Find shortcodes. + . ')/s'; + + $textarr = preg_split( $regex, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY ); + + foreach ( $textarr as &$curl ) { + // Only call _wptexturize_pushpop_element if $curl is a delimiter. + $first = $curl[0]; + if ( '<' === $first && ' +> + + + <?php echo $title ?> + + + + + + + + 500 ); + + $r = wp_parse_args($args, $defaults); + + if ( $wp_xmlrpc_server ) { + $error = new IXR_Error( $r['response'] , $message); + $wp_xmlrpc_server->output( $error->getXml() ); + } + die(); +} + +/** + * Kill WordPress ajax execution. + * + * This is the handler for wp_die when processing Ajax requests. + * + * @since 3.4.0 + * @access private + * + * @param string $message Optional. Response to print. Default empty. + */ +function _ajax_wp_die_handler( $message = '' ) { + if ( is_scalar( $message ) ) + die( (string) $message ); + die( '0' ); +} + +/** + * Kill WordPress execution. + * + * This is the handler for wp_die when processing APP requests. + * + * @since 3.4.0 + * @access private + * + * @param string $message Optional. Response to print. Default empty. + */ +function _scalar_wp_die_handler( $message = '' ) { + if ( is_scalar( $message ) ) + die( (string) $message ); + die(); +} + +/** + * Encode a variable into JSON, with some sanity checks. + * + * @since 4.1.0 + * + * @param mixed $data Variable (usually an array or object) to encode as JSON. + * @param int $options Optional. Options to be passed to json_encode(). Default 0. + * @param int $depth Optional. Maximum depth to walk through $data. Must be + * greater than 0. Default 512. + * @return bool|string The JSON encoded string, or false if it cannot be encoded. + */ +function wp_json_encode( $data, $options = 0, $depth = 512 ) { + /* + * json_encode() has had extra params added over the years. + * $options was added in 5.3, and $depth in 5.5. + * We need to make sure we call it with the correct arguments. + */ + if ( version_compare( PHP_VERSION, '5.5', '>=' ) ) { + $args = array( $data, $options, $depth ); + } elseif ( version_compare( PHP_VERSION, '5.3', '>=' ) ) { + $args = array( $data, $options ); + } else { + $args = array( $data ); + } + + $json = call_user_func_array( 'json_encode', $args ); + + // If json_encode() was successful, no need to do more sanity checking. + // ... unless we're in an old version of PHP, and json_encode() returned + // a string containing 'null'. Then we need to do more sanity checking. + if ( false !== $json && ( version_compare( PHP_VERSION, '5.5', '>=' ) || false === strpos( $json, 'null' ) ) ) { + return $json; + } + + try { + $args[0] = _wp_json_sanity_check( $data, $depth ); + } catch ( Exception $e ) { + return false; + } + + return call_user_func_array( 'json_encode', $args ); +} + +/** + * Perform sanity checks on data that shall be encoded to JSON. + * + * @see wp_json_encode() + * + * @since 4.1.0 + * @access private + * @internal + * + * @param mixed $data Variable (usually an array or object) to encode as JSON. + * @param int $depth Maximum depth to walk through $data. Must be greater than 0. + * @return mixed The sanitized data that shall be encoded to JSON. + */ +function _wp_json_sanity_check( $data, $depth ) { + if ( $depth < 0 ) { + throw new Exception( 'Reached depth limit' ); + } + + if ( is_array( $data ) ) { + $output = array(); + foreach ( $data as $id => $el ) { + // Don't forget to sanitize the ID! + if ( is_string( $id ) ) { + $clean_id = _wp_json_convert_string( $id ); + } else { + $clean_id = $id; + } + + // Check the element type, so that we're only recursing if we really have to. + if ( is_array( $el ) || is_object( $el ) ) { + $output[ $clean_id ] = _wp_json_sanity_check( $el, $depth - 1 ); + } elseif ( is_string( $el ) ) { + $output[ $clean_id ] = _wp_json_convert_string( $el ); + } else { + $output[ $clean_id ] = $el; + } + } + } elseif ( is_object( $data ) ) { + $output = new stdClass; + foreach ( $data as $id => $el ) { + if ( is_string( $id ) ) { + $clean_id = _wp_json_convert_string( $id ); + } else { + $clean_id = $id; + } + + if ( is_array( $el ) || is_object( $el ) ) { + $output->$clean_id = _wp_json_sanity_check( $el, $depth - 1 ); + } elseif ( is_string( $el ) ) { + $output->$clean_id = _wp_json_convert_string( $el ); + } else { + $output->$clean_id = $el; + } + } + } elseif ( is_string( $data ) ) { + return _wp_json_convert_string( $data ); + } else { + return $data; + } + + return $output; +} + +/** + * Convert a string to UTF-8, so that it can be safely encoded to JSON. + * + * @see _wp_json_sanity_check() + * + * @since 4.1.0 + * @access private + * @internal + * + * @param string $string The string which is to be converted. + * @return string The checked string. + */ +function _wp_json_convert_string( $string ) { + static $use_mb = null; + if ( is_null( $use_mb ) ) { + $use_mb = function_exists( 'mb_convert_encoding' ); + } + + if ( $use_mb ) { + $encoding = mb_detect_encoding( $string, mb_detect_order(), true ); + if ( $encoding ) { + return mb_convert_encoding( $string, 'UTF-8', $encoding ); + } else { + return mb_convert_encoding( $string, 'UTF-8', 'UTF-8' ); + } + } else { + return wp_check_invalid_utf8( $string, true ); + } +} + +/** + * Send a JSON response back to an Ajax request. + * + * @since 3.5.0 + * + * @param mixed $response Variable (usually an array or object) to encode as JSON, + * then print and die. + */ +function wp_send_json( $response ) { + @header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) ); + echo wp_json_encode( $response ); + if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) + wp_die(); + else + die; +} + +/** + * Send a JSON response back to an Ajax request, indicating success. + * + * @since 3.5.0 + * + * @param mixed $data Data to encode as JSON, then print and die. + */ +function wp_send_json_success( $data = null ) { + $response = array( 'success' => true ); + + if ( isset( $data ) ) + $response['data'] = $data; + + wp_send_json( $response ); +} + +/** + * Send a JSON response back to an Ajax request, indicating failure. + * + * If the `$data` parameter is a {@see WP_Error} object, the errors + * within the object are processed and output as an array of error + * codes and corresponding messages. All other types are output + * without further processing. + * + * @since 3.5.0 + * @since 4.1.0 The `$data` parameter is now processed if a {@see WP_Error} + * object is passed in. + * + * @param mixed $data Data to encode as JSON, then print and die. + */ +function wp_send_json_error( $data = null ) { + $response = array( 'success' => false ); + + if ( isset( $data ) ) { + if ( is_wp_error( $data ) ) { + $result = array(); + foreach ( $data->errors as $code => $messages ) { + foreach ( $messages as $message ) { + $result[] = array( 'code' => $code, 'message' => $message ); + } + } + + $response['data'] = $result; + } else { + $response['data'] = $data; + } + } + + wp_send_json( $response ); +} + +/** + * Retrieve the WordPress home page URL. + * + * If the constant named 'WP_HOME' exists, then it will be used and returned + * by the function. This can be used to counter the redirection on your local + * development environment. + * + * @since 2.2.0 + * @access private + * + * @see WP_HOME + * + * @param string $url URL for the home location. + * @return string Homepage location. + */ +function _config_wp_home( $url = '' ) { + if ( defined( 'WP_HOME' ) ) + return untrailingslashit( WP_HOME ); + return $url; +} + +/** + * Retrieve the WordPress site URL. + * + * If the constant named 'WP_SITEURL' is defined, then the value in that + * constant will always be returned. This can be used for debugging a site + * on your localhost while not having to change the database to your URL. + * + * @since 2.2.0 + * @access private + * + * @see WP_SITEURL + * + * @param string $url URL to set the WordPress site location. + * @return string The WordPress Site URL. + */ +function _config_wp_siteurl( $url = '' ) { + if ( defined( 'WP_SITEURL' ) ) + return untrailingslashit( WP_SITEURL ); + return $url; +} + +/** + * Set the localized direction for MCE plugin. + * + * Will only set the direction to 'rtl', if the WordPress locale has + * the text direction set to 'rtl'. + * + * Fills in the 'directionality' setting, enables the 'directionality' + * plugin, and adds the 'ltr' button to 'toolbar1', formerly + * 'theme_advanced_buttons1' array keys. These keys are then returned + * in the $input (TinyMCE settings) array. + * + * @since 2.1.0 + * @access private + * + * @param array $input MCE settings array. + * @return array Direction set for 'rtl', if needed by locale. + */ +function _mce_set_direction( $input ) { + if ( is_rtl() ) { + $input['directionality'] = 'rtl'; + $input['plugins'] .= ',directionality'; + $input['toolbar1'] .= ',ltr'; + } + + return $input; +} + + +/** + * Convert smiley code to the icon graphic file equivalent. + * + * You can turn off smilies, by going to the write setting screen and unchecking + * the box, or by setting 'use_smilies' option to false or removing the option. + * + * Plugins may override the default smiley list by setting the $wpsmiliestrans + * to an array, with the key the code the blogger types in and the value the + * image file. + * + * The $wp_smiliessearch global is for the regular expression and is set each + * time the function is called. + * + * The full list of smilies can be found in the function and won't be listed in + * the description. Probably should create a Codex page for it, so that it is + * available. + * + * @global array $wpsmiliestrans + * @global array $wp_smiliessearch + * + * @since 2.2.0 + */ +function smilies_init() { + global $wpsmiliestrans, $wp_smiliessearch; + + // don't bother setting up smilies if they are disabled + if ( !get_option( 'use_smilies' ) ) + return; + + if ( !isset( $wpsmiliestrans ) ) { + $wpsmiliestrans = array( + ':mrgreen:' => 'icon_mrgreen.gif', + ':neutral:' => 'icon_neutral.gif', + ':twisted:' => 'icon_twisted.gif', + ':arrow:' => 'icon_arrow.gif', + ':shock:' => 'icon_eek.gif', + ':smile:' => 'icon_smile.gif', + ':???:' => 'icon_confused.gif', + ':cool:' => 'icon_cool.gif', + ':evil:' => 'icon_evil.gif', + ':grin:' => 'icon_biggrin.gif', + ':idea:' => 'icon_idea.gif', + ':oops:' => 'icon_redface.gif', + ':razz:' => 'icon_razz.gif', + ':roll:' => 'icon_rolleyes.gif', + ':wink:' => 'icon_wink.gif', + ':cry:' => 'icon_cry.gif', + ':eek:' => 'icon_surprised.gif', + ':lol:' => 'icon_lol.gif', + ':mad:' => 'icon_mad.gif', + ':sad:' => 'icon_sad.gif', + '8-)' => 'icon_cool.gif', + '8-O' => 'icon_eek.gif', + ':-(' => 'icon_sad.gif', + ':-)' => 'icon_smile.gif', + ':-?' => 'icon_confused.gif', + ':-D' => 'icon_biggrin.gif', + ':-P' => 'icon_razz.gif', + ':-o' => 'icon_surprised.gif', + ':-x' => 'icon_mad.gif', + ':-|' => 'icon_neutral.gif', + ';-)' => 'icon_wink.gif', + // This one transformation breaks regular text with frequency. + // '8)' => 'icon_cool.gif', + '8O' => 'icon_eek.gif', + ':(' => 'icon_sad.gif', + ':)' => 'icon_smile.gif', + ':?' => 'icon_confused.gif', + ':D' => 'icon_biggrin.gif', + ':P' => 'icon_razz.gif', + ':o' => 'icon_surprised.gif', + ':x' => 'icon_mad.gif', + ':|' => 'icon_neutral.gif', + ';)' => 'icon_wink.gif', + ':!:' => 'icon_exclaim.gif', + ':?:' => 'icon_question.gif', + ); + } + + if (count($wpsmiliestrans) == 0) { + return; + } + + /* + * NOTE: we sort the smilies in reverse key order. This is to make sure + * we match the longest possible smilie (:???: vs :?) as the regular + * expression used below is first-match + */ + krsort($wpsmiliestrans); + + $spaces = wp_spaces_regexp(); + + // Begin first "subpattern" + $wp_smiliessearch = '/(?<=' . $spaces . '|^)'; + + $subchar = ''; + foreach ( (array) $wpsmiliestrans as $smiley => $img ) { + $firstchar = substr($smiley, 0, 1); + $rest = substr($smiley, 1); + + // new subpattern? + if ($firstchar != $subchar) { + if ($subchar != '') { + $wp_smiliessearch .= ')(?=' . $spaces . '|$)'; // End previous "subpattern" + $wp_smiliessearch .= '|(?<=' . $spaces . '|^)'; // Begin another "subpattern" + } + $subchar = $firstchar; + $wp_smiliessearch .= preg_quote($firstchar, '/') . '(?:'; + } else { + $wp_smiliessearch .= '|'; + } + $wp_smiliessearch .= preg_quote($rest, '/'); + } + + $wp_smiliessearch .= ')(?=' . $spaces . '|$)/m'; + +} + +/** + * Merge user defined arguments into defaults array. + * + * This function is used throughout WordPress to allow for both string or array + * to be merged into another array. + * + * @since 2.2.0 + * + * @param string|array $args Value to merge with $defaults + * @param array $defaults Optional. Array that serves as the defaults. Default empty. + * @return array Merged user defined values with defaults. + */ +function wp_parse_args( $args, $defaults = '' ) { + if ( is_object( $args ) ) + $r = get_object_vars( $args ); + elseif ( is_array( $args ) ) + $r =& $args; + else + wp_parse_str( $args, $r ); + + if ( is_array( $defaults ) ) + return array_merge( $defaults, $r ); + return $r; +} + +/** + * Clean up an array, comma- or space-separated list of IDs. + * + * @since 3.0.0 + * + * @param array|string $list List of ids. + * @return array Sanitized array of IDs. + */ +function wp_parse_id_list( $list ) { + if ( !is_array($list) ) + $list = preg_split('/[\s,]+/', $list); + + return array_unique(array_map('absint', $list)); +} + +/** + * Extract a slice of an array, given a list of keys. + * + * @since 3.1.0 + * + * @param array $array The original array. + * @param array $keys The list of keys. + * @return array The array slice. + */ +function wp_array_slice_assoc( $array, $keys ) { + $slice = array(); + foreach ( $keys as $key ) + if ( isset( $array[ $key ] ) ) + $slice[ $key ] = $array[ $key ]; + + return $slice; +} + +/** + * Filters a list of objects, based on a set of key => value arguments. + * + * @since 3.0.0 + * + * @param array $list An array of objects to filter + * @param array $args Optional. An array of key => value arguments to match + * against each object. Default empty array. + * @param string $operator Optional. The logical operation to perform. 'or' means + * only one element from the array needs to match; 'and' + * means all elements must match. Default 'and'. + * @param bool|string $field A field from the object to place instead of the entire object. + * Default false. + * @return array A list of objects or object fields. + */ +function wp_filter_object_list( $list, $args = array(), $operator = 'and', $field = false ) { + if ( ! is_array( $list ) ) + return array(); + + $list = wp_list_filter( $list, $args, $operator ); + + if ( $field ) + $list = wp_list_pluck( $list, $field ); + + return $list; +} + +/** + * Filters a list of objects, based on a set of key => value arguments. + * + * @since 3.1.0 + * + * @param array $list An array of objects to filter. + * @param array $args Optional. An array of key => value arguments to match + * against each object. Default empty array. + * @param string $operator Optional. The logical operation to perform. 'AND' means + * all elements from the array must match. 'OR' means only + * one element needs to match. 'NOT' means no elements may + * match. Default 'AND'. + * @return array Array of found values. + */ +function wp_list_filter( $list, $args = array(), $operator = 'AND' ) { + if ( ! is_array( $list ) ) + return array(); + + if ( empty( $args ) ) + return $list; + + $operator = strtoupper( $operator ); + $count = count( $args ); + $filtered = array(); + + foreach ( $list as $key => $obj ) { + $to_match = (array) $obj; + + $matched = 0; + foreach ( $args as $m_key => $m_value ) { + if ( array_key_exists( $m_key, $to_match ) && $m_value == $to_match[ $m_key ] ) + $matched++; + } + + if ( ( 'AND' == $operator && $matched == $count ) + || ( 'OR' == $operator && $matched > 0 ) + || ( 'NOT' == $operator && 0 == $matched ) ) { + $filtered[$key] = $obj; + } + } + + return $filtered; +} + +/** + * Pluck a certain field out of each object in a list. + * + * This has the same functionality and prototype of + * array_column() (PHP 5.5) but also supports objects. + * + * @since 3.1.0 + * @since 4.0.0 $index_key parameter added. + * + * @param array $list List of objects or arrays + * @param int|string $field Field from the object to place instead of the entire object + * @param int|string $index_key Optional. Field from the object to use as keys for the new array. + * Default null. + * @return array Array of found values. If $index_key is set, an array of found values with keys + * corresponding to $index_key. + */ +function wp_list_pluck( $list, $field, $index_key = null ) { + if ( ! $index_key ) { + /* + * This is simple. Could at some point wrap array_column() + * if we knew we had an array of arrays. + */ + foreach ( $list as $key => $value ) { + if ( is_object( $value ) ) { + $list[ $key ] = $value->$field; + } else { + $list[ $key ] = $value[ $field ]; + } + } + return $list; + } + + /* + * When index_key is not set for a particular item, push the value + * to the end of the stack. This is how array_column() behaves. + */ + $newlist = array(); + foreach ( $list as $value ) { + if ( is_object( $value ) ) { + if ( isset( $value->$index_key ) ) { + $newlist[ $value->$index_key ] = $value->$field; + } else { + $newlist[] = $value->$field; + } + } else { + if ( isset( $value[ $index_key ] ) ) { + $newlist[ $value[ $index_key ] ] = $value[ $field ]; + } else { + $newlist[] = $value[ $field ]; + } + } + } + + return $newlist; +} + +/** + * Determines if Widgets library should be loaded. + * + * Checks to make sure that the widgets library hasn't already been loaded. + * If it hasn't, then it will load the widgets library and run an action hook. + * + * @since 2.2.0 + */ +function wp_maybe_load_widgets() { + /** + * Filter whether to load the Widgets library. + * + * Passing a falsey value to the filter will effectively short-circuit + * the Widgets library from loading. + * + * @since 2.8.0 + * + * @param bool $wp_maybe_load_widgets Whether to load the Widgets library. + * Default true. + */ + if ( ! apply_filters( 'load_default_widgets', true ) ) { + return; + } + + require_once( ABSPATH . WPINC . '/default-widgets.php' ); + + add_action( '_admin_menu', 'wp_widgets_add_menu' ); +} + +/** + * Append the Widgets menu to the themes main menu. + * + * @since 2.2.0 + */ +function wp_widgets_add_menu() { + global $submenu; + + if ( ! current_theme_supports( 'widgets' ) ) + return; + + $submenu['themes.php'][7] = array( __( 'Widgets' ), 'edit_theme_options', 'widgets.php' ); + ksort( $submenu['themes.php'], SORT_NUMERIC ); +} + +/** + * Flush all output buffers for PHP 5.2. + * + * Make sure all output buffers are flushed before our singletons are destroyed. + * + * @since 2.2.0 + */ +function wp_ob_end_flush_all() { + $levels = ob_get_level(); + for ($i=0; $i<$levels; $i++) + ob_end_flush(); +} + +/** + * Load custom DB error or display WordPress DB error. + * + * If a file exists in the wp-content directory named db-error.php, then it will + * be loaded instead of displaying the WordPress DB error. If it is not found, + * then the WordPress DB error will be displayed instead. + * + * The WordPress DB error sets the HTTP status header to 500 to try to prevent + * search engines from caching the message. Custom DB messages should do the + * same. + * + * This function was backported to WordPress 2.3.2, but originally was added + * in WordPress 2.5.0. + * + * @since 2.3.2 + * + * @global wpdb $wpdb WordPress database abstraction object. + */ +function dead_db() { + global $wpdb; + + wp_load_translations_early(); + + // Load custom DB error template, if present. + if ( file_exists( WP_CONTENT_DIR . '/db-error.php' ) ) { + require_once( WP_CONTENT_DIR . '/db-error.php' ); + die(); + } + + // If installing or in the admin, provide the verbose message. + if ( defined('WP_INSTALLING') || defined('WP_ADMIN') ) + wp_die($wpdb->error); + + // Otherwise, be terse. + status_header( 500 ); + nocache_headers(); + header( 'Content-Type: text/html; charset=utf-8' ); +?> + +> + + + <?php _e( 'Database Error' ); ?> + + + +

        + + +deprecated since version %2$s! Use %3$s instead.'), $function, $version, $replacement ) ); + else + trigger_error( sprintf( __('%1$s is deprecated since version %2$s with no alternative available.'), $function, $version ) ); + } else { + if ( ! is_null( $replacement ) ) + trigger_error( sprintf( '%1$s is deprecated since version %2$s! Use %3$s instead.', $function, $version, $replacement ) ); + else + trigger_error( sprintf( '%1$s is deprecated since version %2$s with no alternative available.', $function, $version ) ); + } + } +} + +/** + * Mark a file as deprecated and inform when it has been used. + * + * There is a hook deprecated_file_included that will be called that can be used + * to get the backtrace up to what file and function included the deprecated + * file. + * + * The current behavior is to trigger a user error if WP_DEBUG is true. + * + * This function is to be used in every file that is deprecated. + * + * @since 2.5.0 + * @access private + * + * @param string $file The file that was included. + * @param string $version The version of WordPress that deprecated the file. + * @param string $replacement Optional. The file that should have been included based on ABSPATH. + * Default null. + * @param string $message Optional. A message regarding the change. Default empty. + */ +function _deprecated_file( $file, $version, $replacement = null, $message = '' ) { + + /** + * Fires when a deprecated file is called. + * + * @since 2.5.0 + * + * @param string $file The file that was called. + * @param string $replacement The file that should have been included based on ABSPATH. + * @param string $version The version of WordPress that deprecated the file. + * @param string $message A message regarding the change. + */ + do_action( 'deprecated_file_included', $file, $replacement, $version, $message ); + + /** + * Filter whether to trigger an error for deprecated files. + * + * @since 2.5.0 + * + * @param bool $trigger Whether to trigger the error for deprecated files. Default true. + */ + if ( WP_DEBUG && apply_filters( 'deprecated_file_trigger_error', true ) ) { + $message = empty( $message ) ? '' : ' ' . $message; + if ( function_exists( '__' ) ) { + if ( ! is_null( $replacement ) ) + trigger_error( sprintf( __('%1$s is deprecated since version %2$s! Use %3$s instead.'), $file, $version, $replacement ) . $message ); + else + trigger_error( sprintf( __('%1$s is deprecated since version %2$s with no alternative available.'), $file, $version ) . $message ); + } else { + if ( ! is_null( $replacement ) ) + trigger_error( sprintf( '%1$s is deprecated since version %2$s! Use %3$s instead.', $file, $version, $replacement ) . $message ); + else + trigger_error( sprintf( '%1$s is deprecated since version %2$s with no alternative available.', $file, $version ) . $message ); + } + } +} +/** + * Mark a function argument as deprecated and inform when it has been used. + * + * This function is to be used whenever a deprecated function argument is used. + * Before this function is called, the argument must be checked for whether it was + * used by comparing it to its default value or evaluating whether it is empty. + * For example: + * + * if ( ! empty( $deprecated ) ) { + * _deprecated_argument( __FUNCTION__, '3.0' ); + * } + * + * + * There is a hook deprecated_argument_run that will be called that can be used + * to get the backtrace up to what file and function used the deprecated + * argument. + * + * The current behavior is to trigger a user error if WP_DEBUG is true. + * + * @since 3.0.0 + * @access private + * + * @param string $function The function that was called. + * @param string $version The version of WordPress that deprecated the argument used. + * @param string $message Optional. A message regarding the change. Default null. + */ +function _deprecated_argument( $function, $version, $message = null ) { + + /** + * Fires when a deprecated argument is called. + * + * @since 3.0.0 + * + * @param string $function The function that was called. + * @param string $message A message regarding the change. + * @param string $version The version of WordPress that deprecated the argument used. + */ + do_action( 'deprecated_argument_run', $function, $message, $version ); + + /** + * Filter whether to trigger an error for deprecated arguments. + * + * @since 3.0.0 + * + * @param bool $trigger Whether to trigger the error for deprecated arguments. Default true. + */ + if ( WP_DEBUG && apply_filters( 'deprecated_argument_trigger_error', true ) ) { + if ( function_exists( '__' ) ) { + if ( ! is_null( $message ) ) + trigger_error( sprintf( __('%1$s was called with an argument that is deprecated since version %2$s! %3$s'), $function, $version, $message ) ); + else + trigger_error( sprintf( __('%1$s was called with an argument that is deprecated since version %2$s with no alternative available.'), $function, $version ) ); + } else { + if ( ! is_null( $message ) ) + trigger_error( sprintf( '%1$s was called with an argument that is deprecated since version %2$s! %3$s', $function, $version, $message ) ); + else + trigger_error( sprintf( '%1$s was called with an argument that is deprecated since version %2$s with no alternative available.', $function, $version ) ); + } + } +} + +/** + * Mark something as being incorrectly called. + * + * There is a hook doing_it_wrong_run that will be called that can be used + * to get the backtrace up to what file and function called the deprecated + * function. + * + * The current behavior is to trigger a user error if WP_DEBUG is true. + * + * @since 3.1.0 + * @access private + * + * @param string $function The function that was called. + * @param string $message A message explaining what has been done incorrectly. + * @param string $version The version of WordPress where the message was added. + */ +function _doing_it_wrong( $function, $message, $version ) { + + /** + * Fires when the given function is being used incorrectly. + * + * @since 3.1.0 + * + * @param string $function The function that was called. + * @param string $message A message explaining what has been done incorrectly. + * @param string $version The version of WordPress where the message was added. + */ + do_action( 'doing_it_wrong_run', $function, $message, $version ); + + /** + * Filter whether to trigger an error for _doing_it_wrong() calls. + * + * @since 3.1.0 + * + * @param bool $trigger Whether to trigger the error for _doing_it_wrong() calls. Default true. + */ + if ( WP_DEBUG && apply_filters( 'doing_it_wrong_trigger_error', true ) ) { + if ( function_exists( '__' ) ) { + $version = is_null( $version ) ? '' : sprintf( __( '(This message was added in version %s.)' ), $version ); + $message .= ' ' . __( 'Please see Debugging in WordPress for more information.' ); + trigger_error( sprintf( __( '%1$s was called incorrectly. %2$s %3$s' ), $function, $message, $version ) ); + } else { + $version = is_null( $version ) ? '' : sprintf( '(This message was added in version %s.)', $version ); + $message .= ' Please see Debugging in WordPress for more information.'; + trigger_error( sprintf( '%1$s was called incorrectly. %2$s %3$s', $function, $message, $version ) ); + } + } +} + +/** + * Is the server running earlier than 1.5.0 version of lighttpd? + * + * @since 2.5.0 + * + * @return bool Whether the server is running lighttpd < 1.5.0. + */ +function is_lighttpd_before_150() { + $server_parts = explode( '/', isset( $_SERVER['SERVER_SOFTWARE'] )? $_SERVER['SERVER_SOFTWARE'] : '' ); + $server_parts[1] = isset( $server_parts[1] )? $server_parts[1] : ''; + return 'lighttpd' == $server_parts[0] && -1 == version_compare( $server_parts[1], '1.5.0' ); +} + +/** + * Does the specified module exist in the Apache config? + * + * @since 2.5.0 + * + * @param string $mod The module, e.g. mod_rewrite. + * @param bool $default Optional. The default return value if the module is not found. Default false. + * @return bool Whether the specified module is loaded. + */ +function apache_mod_loaded($mod, $default = false) { + global $is_apache; + + if ( !$is_apache ) + return false; + + if ( function_exists( 'apache_get_modules' ) ) { + $mods = apache_get_modules(); + if ( in_array($mod, $mods) ) + return true; + } elseif ( function_exists( 'phpinfo' ) && false === strpos( ini_get( 'disable_functions' ), 'phpinfo' ) ) { + ob_start(); + phpinfo(8); + $phpinfo = ob_get_clean(); + if ( false !== strpos($phpinfo, $mod) ) + return true; + } + return $default; +} + +/** + * Check if IIS 7+ supports pretty permalinks. + * + * @since 2.8.0 + * + * @return bool Whether IIS7 supports permalinks. + */ +function iis7_supports_permalinks() { + global $is_iis7; + + $supports_permalinks = false; + if ( $is_iis7 ) { + /* First we check if the DOMDocument class exists. If it does not exist, then we cannot + * easily update the xml configuration file, hence we just bail out and tell user that + * pretty permalinks cannot be used. + * + * Next we check if the URL Rewrite Module 1.1 is loaded and enabled for the web site. When + * URL Rewrite 1.1 is loaded it always sets a server variable called 'IIS_UrlRewriteModule'. + * Lastly we make sure that PHP is running via FastCGI. This is important because if it runs + * via ISAPI then pretty permalinks will not work. + */ + $supports_permalinks = class_exists('DOMDocument') && isset($_SERVER['IIS_UrlRewriteModule']) && ( php_sapi_name() == 'cgi-fcgi' ); + } + + /** + * Filter whether IIS 7+ supports pretty permalinks. + * + * @since 2.8.0 + * + * @param bool $supports_permalinks Whether IIS7 supports permalinks. Default false. + */ + return apply_filters( 'iis7_supports_permalinks', $supports_permalinks ); +} + +/** + * File validates against allowed set of defined rules. + * + * A return value of '1' means that the $file contains either '..' or './'. A + * return value of '2' means that the $file contains ':' after the first + * character. A return value of '3' means that the file is not in the allowed + * files list. + * + * @since 1.2.0 + * + * @param string $file File path. + * @param array $allowed_files List of allowed files. + * @return int 0 means nothing is wrong, greater than 0 means something was wrong. + */ +function validate_file( $file, $allowed_files = '' ) { + if ( false !== strpos( $file, '..' ) ) + return 1; + + if ( false !== strpos( $file, './' ) ) + return 1; + + if ( ! empty( $allowed_files ) && ! in_array( $file, $allowed_files ) ) + return 3; + + if (':' == substr( $file, 1, 1 ) ) + return 2; + + return 0; +} + +/** + * Determine if SSL is used. + * + * @since 2.6.0 + * + * @return bool True if SSL, false if not used. + */ +function is_ssl() { + if ( isset($_SERVER['HTTPS']) ) { + if ( 'on' == strtolower($_SERVER['HTTPS']) ) + return true; + if ( '1' == $_SERVER['HTTPS'] ) + return true; + } elseif ( isset($_SERVER['SERVER_PORT']) && ( '443' == $_SERVER['SERVER_PORT'] ) ) { + return true; + } + return false; +} + +/** + * Whether SSL login should be forced. + * + * @since 2.6.0 + * + * @see force_ssl_admin() + * + * @param string|bool $force Optional Whether to force SSL login. Default null. + * @return bool True if forced, false if not forced. + */ +function force_ssl_login( $force = null ) { + return force_ssl_admin( $force ); +} + +/** + * Whether to force SSL used for the Administration Screens. + * + * @since 2.6.0 + * + * @param string|bool $force Optional. Whether to force SSL in admin screens. Default null. + * @return bool True if forced, false if not forced. + */ +function force_ssl_admin( $force = null ) { + static $forced = false; + + if ( !is_null( $force ) ) { + $old_forced = $forced; + $forced = $force; + return $old_forced; + } + + return $forced; +} + +/** + * Guess the URL for the site. + * + * Will remove wp-admin links to retrieve only return URLs not in the wp-admin + * directory. + * + * @since 2.6.0 + * + * @return string The guessed URL. + */ +function wp_guess_url() { + if ( defined('WP_SITEURL') && '' != WP_SITEURL ) { + $url = WP_SITEURL; + } else { + $abspath_fix = str_replace( '\\', '/', ABSPATH ); + $script_filename_dir = dirname( $_SERVER['SCRIPT_FILENAME'] ); + + // The request is for the admin + if ( strpos( $_SERVER['REQUEST_URI'], 'wp-admin' ) !== false || strpos( $_SERVER['REQUEST_URI'], 'wp-login.php' ) !== false ) { + $path = preg_replace( '#/(wp-admin/.*|wp-login.php)#i', '', $_SERVER['REQUEST_URI'] ); + + // The request is for a file in ABSPATH + } elseif ( $script_filename_dir . '/' == $abspath_fix ) { + // Strip off any file/query params in the path + $path = preg_replace( '#/[^/]*$#i', '', $_SERVER['PHP_SELF'] ); + + } else { + if ( false !== strpos( $_SERVER['SCRIPT_FILENAME'], $abspath_fix ) ) { + // Request is hitting a file inside ABSPATH + $directory = str_replace( ABSPATH, '', $script_filename_dir ); + // Strip off the sub directory, and any file/query paramss + $path = preg_replace( '#/' . preg_quote( $directory, '#' ) . '/[^/]*$#i', '' , $_SERVER['REQUEST_URI'] ); + } elseif ( false !== strpos( $abspath_fix, $script_filename_dir ) ) { + // Request is hitting a file above ABSPATH + $subdirectory = substr( $abspath_fix, strpos( $abspath_fix, $script_filename_dir ) + strlen( $script_filename_dir ) ); + // Strip off any file/query params from the path, appending the sub directory to the install + $path = preg_replace( '#/[^/]*$#i', '' , $_SERVER['REQUEST_URI'] ) . $subdirectory; + } else { + $path = $_SERVER['REQUEST_URI']; + } + } + + $schema = is_ssl() ? 'https://' : 'http://'; // set_url_scheme() is not defined yet + $url = $schema . $_SERVER['HTTP_HOST'] . $path; + } + + return rtrim($url, '/'); +} + +/** + * Temporarily suspend cache additions. + * + * Stops more data being added to the cache, but still allows cache retrieval. + * This is useful for actions, such as imports, when a lot of data would otherwise + * be almost uselessly added to the cache. + * + * Suspension lasts for a single page load at most. Remember to call this + * function again if you wish to re-enable cache adds earlier. + * + * @since 3.3.0 + * + * @param bool $suspend Optional. Suspends additions if true, re-enables them if false. + * @return bool The current suspend setting + */ +function wp_suspend_cache_addition( $suspend = null ) { + static $_suspend = false; + + if ( is_bool( $suspend ) ) + $_suspend = $suspend; + + return $_suspend; +} + +/** + * Suspend cache invalidation. + * + * Turns cache invalidation on and off. Useful during imports where you don't wont to do + * invalidations every time a post is inserted. Callers must be sure that what they are + * doing won't lead to an inconsistent cache when invalidation is suspended. + * + * @since 2.7.0 + * + * @param bool $suspend Optional. Whether to suspend or enable cache invalidation. Default true. + * @return bool The current suspend setting. + */ +function wp_suspend_cache_invalidation( $suspend = true ) { + global $_wp_suspend_cache_invalidation; + + $current_suspend = $_wp_suspend_cache_invalidation; + $_wp_suspend_cache_invalidation = $suspend; + return $current_suspend; +} + +/** + * Determine whether a site is the main site of the current network. + * + * @since 3.0.0 + * + * @param int $site_id Optional. Site ID to test. Defaults to current site. + * Defaults to current site. + * @return bool True if $site_id is the main site of the network, or if not + * running Multisite. + */ +function is_main_site( $site_id = null ) { + // This is the current network's information; 'site' is old terminology. + global $current_site; + + if ( ! is_multisite() ) + return true; + + if ( ! $site_id ) + $site_id = get_current_blog_id(); + + return (int) $site_id === (int) $current_site->blog_id; +} + +/** + * Determine whether a network is the main network of the Multisite install. + * + * @since 3.7.0 + * + * @param int $network_id Optional. Network ID to test. Defaults to current network. + * @return bool True if $network_id is the main network, or if not running Multisite. + */ +function is_main_network( $network_id = null ) { + global $wpdb; + + if ( ! is_multisite() ) + return true; + + $current_network_id = (int) get_current_site()->id; + + if ( ! $network_id ) + $network_id = $current_network_id; + $network_id = (int) $network_id; + + if ( defined( 'PRIMARY_NETWORK_ID' ) ) + return $network_id === (int) PRIMARY_NETWORK_ID; + + if ( 1 === $current_network_id ) + return $network_id === $current_network_id; + + $primary_network_id = (int) wp_cache_get( 'primary_network_id', 'site-options' ); + + if ( $primary_network_id ) + return $network_id === $primary_network_id; + + $primary_network_id = (int) $wpdb->get_var( "SELECT id FROM $wpdb->site ORDER BY id LIMIT 1" ); + wp_cache_add( 'primary_network_id', $primary_network_id, 'site-options' ); + + return $network_id === $primary_network_id; +} + +/** + * Determine whether global terms are enabled. + * + * @since 3.0.0 + * + * @return bool True if multisite and global terms enabled. + */ +function global_terms_enabled() { + if ( ! is_multisite() ) + return false; + + static $global_terms = null; + if ( is_null( $global_terms ) ) { + + /** + * Filter whether global terms are enabled. + * + * Passing a non-null value to the filter will effectively short-circuit the function, + * returning the value of the 'global_terms_enabled' site option instead. + * + * @since 3.0.0 + * + * @param null $anbled Whether global terms are enabled. + */ + $filter = apply_filters( 'global_terms_enabled', null ); + if ( ! is_null( $filter ) ) + $global_terms = (bool) $filter; + else + $global_terms = (bool) get_site_option( 'global_terms_enabled', false ); + } + return $global_terms; +} + +/** + * gmt_offset modification for smart timezone handling. + * + * Overrides the gmt_offset option if we have a timezone_string available. + * + * @since 2.8.0 + * + * @return float|bool Timezone GMT offset, false otherwise. + */ +function wp_timezone_override_offset() { + if ( !$timezone_string = get_option( 'timezone_string' ) ) { + return false; + } + + $timezone_object = timezone_open( $timezone_string ); + $datetime_object = date_create(); + if ( false === $timezone_object || false === $datetime_object ) { + return false; + } + return round( timezone_offset_get( $timezone_object, $datetime_object ) / HOUR_IN_SECONDS, 2 ); +} + +/** + * Sort-helper for timezones. + * + * @since 2.9.0 + * @access private + * + * @param array $a + * @param array $b + * @return int + */ +function _wp_timezone_choice_usort_callback( $a, $b ) { + // Don't use translated versions of Etc + if ( 'Etc' === $a['continent'] && 'Etc' === $b['continent'] ) { + // Make the order of these more like the old dropdown + if ( 'GMT+' === substr( $a['city'], 0, 4 ) && 'GMT+' === substr( $b['city'], 0, 4 ) ) { + return -1 * ( strnatcasecmp( $a['city'], $b['city'] ) ); + } + if ( 'UTC' === $a['city'] ) { + if ( 'GMT+' === substr( $b['city'], 0, 4 ) ) { + return 1; + } + return -1; + } + if ( 'UTC' === $b['city'] ) { + if ( 'GMT+' === substr( $a['city'], 0, 4 ) ) { + return -1; + } + return 1; + } + return strnatcasecmp( $a['city'], $b['city'] ); + } + if ( $a['t_continent'] == $b['t_continent'] ) { + if ( $a['t_city'] == $b['t_city'] ) { + return strnatcasecmp( $a['t_subcity'], $b['t_subcity'] ); + } + return strnatcasecmp( $a['t_city'], $b['t_city'] ); + } else { + // Force Etc to the bottom of the list + if ( 'Etc' === $a['continent'] ) { + return 1; + } + if ( 'Etc' === $b['continent'] ) { + return -1; + } + return strnatcasecmp( $a['t_continent'], $b['t_continent'] ); + } +} + +/** + * Gives a nicely-formatted list of timezone strings. + * + * @since 2.9.0 + * + * @param string $selected_zone Selected timezone. + * @return string + */ +function wp_timezone_choice( $selected_zone ) { + static $mo_loaded = false; + + $continents = array( 'Africa', 'America', 'Antarctica', 'Arctic', 'Asia', 'Atlantic', 'Australia', 'Europe', 'Indian', 'Pacific'); + + // Load translations for continents and cities + if ( !$mo_loaded ) { + $locale = get_locale(); + $mofile = WP_LANG_DIR . '/continents-cities-' . $locale . '.mo'; + load_textdomain( 'continents-cities', $mofile ); + $mo_loaded = true; + } + + $zonen = array(); + foreach ( timezone_identifiers_list() as $zone ) { + $zone = explode( '/', $zone ); + if ( !in_array( $zone[0], $continents ) ) { + continue; + } + + // This determines what gets set and translated - we don't translate Etc/* strings here, they are done later + $exists = array( + 0 => ( isset( $zone[0] ) && $zone[0] ), + 1 => ( isset( $zone[1] ) && $zone[1] ), + 2 => ( isset( $zone[2] ) && $zone[2] ), + ); + $exists[3] = ( $exists[0] && 'Etc' !== $zone[0] ); + $exists[4] = ( $exists[1] && $exists[3] ); + $exists[5] = ( $exists[2] && $exists[3] ); + + $zonen[] = array( + 'continent' => ( $exists[0] ? $zone[0] : '' ), + 'city' => ( $exists[1] ? $zone[1] : '' ), + 'subcity' => ( $exists[2] ? $zone[2] : '' ), + 't_continent' => ( $exists[3] ? translate( str_replace( '_', ' ', $zone[0] ), 'continents-cities' ) : '' ), + 't_city' => ( $exists[4] ? translate( str_replace( '_', ' ', $zone[1] ), 'continents-cities' ) : '' ), + 't_subcity' => ( $exists[5] ? translate( str_replace( '_', ' ', $zone[2] ), 'continents-cities' ) : '' ) + ); + } + usort( $zonen, '_wp_timezone_choice_usort_callback' ); + + $structure = array(); + + if ( empty( $selected_zone ) ) { + $structure[] = ''; + } + + foreach ( $zonen as $key => $zone ) { + // Build value in an array to join later + $value = array( $zone['continent'] ); + + if ( empty( $zone['city'] ) ) { + // It's at the continent level (generally won't happen) + $display = $zone['t_continent']; + } else { + // It's inside a continent group + + // Continent optgroup + if ( !isset( $zonen[$key - 1] ) || $zonen[$key - 1]['continent'] !== $zone['continent'] ) { + $label = $zone['t_continent']; + $structure[] = ''; + } + + // Add the city to the value + $value[] = $zone['city']; + + $display = $zone['t_city']; + if ( !empty( $zone['subcity'] ) ) { + // Add the subcity to the value + $value[] = $zone['subcity']; + $display .= ' - ' . $zone['t_subcity']; + } + } + + // Build the value + $value = join( '/', $value ); + $selected = ''; + if ( $value === $selected_zone ) { + $selected = 'selected="selected" '; + } + $structure[] = '"; + + // Close continent optgroup + if ( !empty( $zone['city'] ) && ( !isset($zonen[$key + 1]) || (isset( $zonen[$key + 1] ) && $zonen[$key + 1]['continent'] !== $zone['continent']) ) ) { + $structure[] = ''; + } + } + + // Do UTC + $structure[] = ''; + $selected = ''; + if ( 'UTC' === $selected_zone ) + $selected = 'selected="selected" '; + $structure[] = ''; + $structure[] = ''; + + // Do manual UTC offsets + $structure[] = ''; + $offset_range = array (-12, -11.5, -11, -10.5, -10, -9.5, -9, -8.5, -8, -7.5, -7, -6.5, -6, -5.5, -5, -4.5, -4, -3.5, -3, -2.5, -2, -1.5, -1, -0.5, + 0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 5.75, 6, 6.5, 7, 7.5, 8, 8.5, 8.75, 9, 9.5, 10, 10.5, 11, 11.5, 12, 12.75, 13, 13.75, 14); + foreach ( $offset_range as $offset ) { + if ( 0 <= $offset ) + $offset_name = '+' . $offset; + else + $offset_name = (string) $offset; + + $offset_value = $offset_name; + $offset_name = str_replace(array('.25','.5','.75'), array(':15',':30',':45'), $offset_name); + $offset_name = 'UTC' . $offset_name; + $offset_value = 'UTC' . $offset_value; + $selected = ''; + if ( $offset_value === $selected_zone ) + $selected = 'selected="selected" '; + $structure[] = '"; + + } + $structure[] = ''; + + return join( "\n", $structure ); +} + +/** + * Strip close comment and close php tags from file headers used by WP. + * + * @since 2.8.0 + * @access private + * + * @see https://core.trac.wordpress.org/ticket/8497 + * + * @param string $str Header comment to clean up. + * @return string + */ +function _cleanup_header_comment( $str ) { + return trim(preg_replace("/\s*(?:\*\/|\?>).*/", '', $str)); +} + +/** + * Permanently delete posts, pages, attachments, and comments which have been + * in the trash for EMPTY_TRASH_DAYS. + * + * @since 2.9.0 + */ +function wp_scheduled_delete() { + global $wpdb; + + $delete_timestamp = time() - ( DAY_IN_SECONDS * EMPTY_TRASH_DAYS ); + + $posts_to_delete = $wpdb->get_results($wpdb->prepare("SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '_wp_trash_meta_time' AND meta_value < '%d'", $delete_timestamp), ARRAY_A); + + foreach ( (array) $posts_to_delete as $post ) { + $post_id = (int) $post['post_id']; + if ( !$post_id ) + continue; + + $del_post = get_post($post_id); + + if ( !$del_post || 'trash' != $del_post->post_status ) { + delete_post_meta($post_id, '_wp_trash_meta_status'); + delete_post_meta($post_id, '_wp_trash_meta_time'); + } else { + wp_delete_post($post_id); + } + } + + $comments_to_delete = $wpdb->get_results($wpdb->prepare("SELECT comment_id FROM $wpdb->commentmeta WHERE meta_key = '_wp_trash_meta_time' AND meta_value < '%d'", $delete_timestamp), ARRAY_A); + + foreach ( (array) $comments_to_delete as $comment ) { + $comment_id = (int) $comment['comment_id']; + if ( !$comment_id ) + continue; + + $del_comment = get_comment($comment_id); + + if ( !$del_comment || 'trash' != $del_comment->comment_approved ) { + delete_comment_meta($comment_id, '_wp_trash_meta_time'); + delete_comment_meta($comment_id, '_wp_trash_meta_status'); + } else { + wp_delete_comment($comment_id); + } + } +} + +/** + * Retrieve metadata from a file. + * + * Searches for metadata in the first 8kiB of a file, such as a plugin or theme. + * Each piece of metadata must be on its own line. Fields can not span multiple + * lines, the value will get cut at the end of the first line. + * + * If the file data is not within that first 8kiB, then the author should correct + * their plugin file and move the data headers to the top. + * + * @link http://codex.wordpress.org/File_Header + * + * @since 2.9.0 + * + * @param string $file Path to the file. + * @param array $default_headers List of headers, in the format array('HeaderKey' => 'Header Name'). + * @param string $context Optional. If specified adds filter hook "extra_{$context}_headers". + * Default empty. + * @return array Array of file headers in `HeaderKey => Header Value` format. + */ +function get_file_data( $file, $default_headers, $context = '' ) { + // We don't need to write to the file, so just open for reading. + $fp = fopen( $file, 'r' ); + + // Pull only the first 8kiB of the file in. + $file_data = fread( $fp, 8192 ); + + // PHP will close file handle, but we are good citizens. + fclose( $fp ); + + // Make sure we catch CR-only line endings. + $file_data = str_replace( "\r", "\n", $file_data ); + + /** + * Filter extra file headers by context. + * + * The dynamic portion of the hook name, `$context`, refers to + * the context where extra headers might be loaded. + * + * @since 2.9.0 + * + * @param array $extra_context_headers Empty array by default. + */ + if ( $context && $extra_headers = apply_filters( "extra_{$context}_headers", array() ) ) { + $extra_headers = array_combine( $extra_headers, $extra_headers ); // keys equal values + $all_headers = array_merge( $extra_headers, (array) $default_headers ); + } else { + $all_headers = $default_headers; + } + + foreach ( $all_headers as $field => $regex ) { + if ( preg_match( '/^[ \t\/*#@]*' . preg_quote( $regex, '/' ) . ':(.*)$/mi', $file_data, $match ) && $match[1] ) + $all_headers[ $field ] = _cleanup_header_comment( $match[1] ); + else + $all_headers[ $field ] = ''; + } + + return $all_headers; +} + +/** + * Returns true. + * + * Useful for returning true to filters easily. + * + * @since 3.0.0 + * + * @see __return_false() + * + * @return bool True. + */ +function __return_true() { + return true; +} + +/** + * Returns false. + * + * Useful for returning false to filters easily. + * + * @since 3.0.0 + * + * @see __return_true() + * + * @return bool False. + */ +function __return_false() { + return false; +} + +/** + * Returns 0. + * + * Useful for returning 0 to filters easily. + * + * @since 3.0.0 + * + * @return int 0. + */ +function __return_zero() { + return 0; +} + +/** + * Returns an empty array. + * + * Useful for returning an empty array to filters easily. + * + * @since 3.0.0 + * + * @return array Empty array. + */ +function __return_empty_array() { + return array(); +} + +/** + * Returns null. + * + * Useful for returning null to filters easily. + * + * @since 3.4.0 + * + * @return null Null value. + */ +function __return_null() { + return null; +} + +/** + * Returns an empty string. + * + * Useful for returning an empty string to filters easily. + * + * @since 3.7.0 + * + * @see __return_null() + * + * @return string Empty string. + */ +function __return_empty_string() { + return ''; +} + +/** + * Send a HTTP header to disable content type sniffing in browsers which support it. + * + * @since 3.0.0 + * + * @see http://blogs.msdn.com/ie/archive/2008/07/02/ie8-security-part-v-comprehensive-protection.aspx + * @see http://src.chromium.org/viewvc/chrome?view=rev&revision=6985 + */ +function send_nosniff_header() { + @header( 'X-Content-Type-Options: nosniff' ); +} + +/** + * Return a MySQL expression for selecting the week number based on the start_of_week option. + * + * @internal + * @since 3.0.0 + * + * @param string $column Database column. + * @return string SQL clause. + */ +function _wp_mysql_week( $column ) { + switch ( $start_of_week = (int) get_option( 'start_of_week' ) ) { + case 1 : + return "WEEK( $column, 1 )"; + case 2 : + case 3 : + case 4 : + case 5 : + case 6 : + return "WEEK( DATE_SUB( $column, INTERVAL $start_of_week DAY ), 0 )"; + case 0 : + default : + return "WEEK( $column, 0 )"; + } +} + +/** + * Find hierarchy loops using a callback function that maps object IDs to parent IDs. + * + * @since 3.1.0 + * @access private + * + * @param callback $callback Function that accepts ( ID, $callback_args ) and outputs parent_ID. + * @param int $start The ID to start the loop check at. + * @param int $start_parent The parent_ID of $start to use instead of calling $callback( $start ). + * Use null to always use $callback + * @param array $callback_args Optional. Additional arguments to send to $callback. + * @return array IDs of all members of loop. + */ +function wp_find_hierarchy_loop( $callback, $start, $start_parent, $callback_args = array() ) { + $override = is_null( $start_parent ) ? array() : array( $start => $start_parent ); + + if ( !$arbitrary_loop_member = wp_find_hierarchy_loop_tortoise_hare( $callback, $start, $override, $callback_args ) ) + return array(); + + return wp_find_hierarchy_loop_tortoise_hare( $callback, $arbitrary_loop_member, $override, $callback_args, true ); +} + +/** + * Use the "The Tortoise and the Hare" algorithm to detect loops. + * + * For every step of the algorithm, the hare takes two steps and the tortoise one. + * If the hare ever laps the tortoise, there must be a loop. + * + * @since 3.1.0 + * @access private + * + * @param callback $callback Function that accepts ( ID, callback_arg, ... ) and outputs parent_ID. + * @param int $start The ID to start the loop check at. + * @param array $override Optional. An array of ( ID => parent_ID, ... ) to use instead of $callback. + * Default empty array. + * @param array $callback_args Optional. Additional arguments to send to $callback. Default empty array. + * @param bool $_return_loop Optional. Return loop members or just detect presence of loop? Only set + * to true if you already know the given $start is part of a loop (otherwise + * the returned array might include branches). Default false. + * @return mixed Scalar ID of some arbitrary member of the loop, or array of IDs of all members of loop if + * $_return_loop + */ +function wp_find_hierarchy_loop_tortoise_hare( $callback, $start, $override = array(), $callback_args = array(), $_return_loop = false ) { + $tortoise = $hare = $evanescent_hare = $start; + $return = array(); + + // Set evanescent_hare to one past hare + // Increment hare two steps + while ( + $tortoise + && + ( $evanescent_hare = isset( $override[$hare] ) ? $override[$hare] : call_user_func_array( $callback, array_merge( array( $hare ), $callback_args ) ) ) + && + ( $hare = isset( $override[$evanescent_hare] ) ? $override[$evanescent_hare] : call_user_func_array( $callback, array_merge( array( $evanescent_hare ), $callback_args ) ) ) + ) { + if ( $_return_loop ) + $return[$tortoise] = $return[$evanescent_hare] = $return[$hare] = true; + + // tortoise got lapped - must be a loop + if ( $tortoise == $evanescent_hare || $tortoise == $hare ) + return $_return_loop ? $return : $tortoise; + + // Increment tortoise by one step + $tortoise = isset( $override[$tortoise] ) ? $override[$tortoise] : call_user_func_array( $callback, array_merge( array( $tortoise ), $callback_args ) ); + } + + return false; +} + +/** + * Send a HTTP header to limit rendering of pages to same origin iframes. + * + * @since 3.1.3 + * + * @see https://developer.mozilla.org/en/the_x-frame-options_response_header + */ +function send_frame_options_header() { + @header( 'X-Frame-Options: SAMEORIGIN' ); +} + +/** + * Retrieve a list of protocols to allow in HTML attributes. + * + * @since 3.3.0 + * + * @see wp_kses() + * @see esc_url() + * + * @return array Array of allowed protocols. + */ +function wp_allowed_protocols() { + static $protocols; + + if ( empty( $protocols ) ) { + $protocols = array( 'http', 'https', 'ftp', 'ftps', 'mailto', 'news', 'irc', 'gopher', 'nntp', 'feed', 'telnet', 'mms', 'rtsp', 'svn', 'tel', 'fax', 'xmpp' ); + + /** + * Filter the list of protocols allowed in HTML attributes. + * + * @since 3.0.0 + * + * @param array $protocols Array of allowed protocols e.g. 'http', 'ftp', 'tel', and more. + */ + $protocols = apply_filters( 'kses_allowed_protocols', $protocols ); + } + + return $protocols; +} + +/** + * Return a comma-separated string of functions that have been called to get + * to the current point in code. + * + * @since 3.4.0 + * + * @see https://core.trac.wordpress.org/ticket/19589 + * + * @param string $ignore_class Optional. A class to ignore all function calls within - useful + * when you want to just give info about the callee. Default null. + * @param int $skip_frames Optional. A number of stack frames to skip - useful for unwinding + * back to the source of the issue. Default 0. + * @param bool $pretty Optional. Whether or not you want a comma separated string or raw + * array returned. Default true. + * @return string|array Either a string containing a reversed comma separated trace or an array + * of individual calls. + */ +function wp_debug_backtrace_summary( $ignore_class = null, $skip_frames = 0, $pretty = true ) { + if ( version_compare( PHP_VERSION, '5.2.5', '>=' ) ) + $trace = debug_backtrace( false ); + else + $trace = debug_backtrace(); + + $caller = array(); + $check_class = ! is_null( $ignore_class ); + $skip_frames++; // skip this function + + foreach ( $trace as $call ) { + if ( $skip_frames > 0 ) { + $skip_frames--; + } elseif ( isset( $call['class'] ) ) { + if ( $check_class && $ignore_class == $call['class'] ) + continue; // Filter out calls + + $caller[] = "{$call['class']}{$call['type']}{$call['function']}"; + } else { + if ( in_array( $call['function'], array( 'do_action', 'apply_filters' ) ) ) { + $caller[] = "{$call['function']}('{$call['args'][0]}')"; + } elseif ( in_array( $call['function'], array( 'include', 'include_once', 'require', 'require_once' ) ) ) { + $caller[] = $call['function'] . "('" . str_replace( array( WP_CONTENT_DIR, ABSPATH ) , '', $call['args'][0] ) . "')"; + } else { + $caller[] = $call['function']; + } + } + } + if ( $pretty ) + return join( ', ', array_reverse( $caller ) ); + else + return $caller; +} + +/** + * Retrieve ids that are not already present in the cache. + * + * @since 3.4.0 + * @access private + * + * @param array $object_ids ID list. + * @param string $cache_key The cache bucket to check against. + * + * @return array List of ids not present in the cache. + */ +function _get_non_cached_ids( $object_ids, $cache_key ) { + $clean = array(); + foreach ( $object_ids as $id ) { + $id = (int) $id; + if ( !wp_cache_get( $id, $cache_key ) ) { + $clean[] = $id; + } + } + + return $clean; +} + +/** + * Test if the current device has the capability to upload files. + * + * @since 3.4.0 + * @access private + * + * @return bool true|false Whether the device is able to upload files. + */ +function _device_can_upload() { + if ( ! wp_is_mobile() ) + return true; + + $ua = $_SERVER['HTTP_USER_AGENT']; + + if ( strpos($ua, 'iPhone') !== false + || strpos($ua, 'iPad') !== false + || strpos($ua, 'iPod') !== false ) { + return preg_match( '#OS ([\d_]+) like Mac OS X#', $ua, $version ) && version_compare( $version[1], '6', '>=' ); + } + + return true; +} + +/** + * Test if a given path is a stream URL + * + * @param string $path The resource path or URL. + * @return bool True if the path is a stream URL. + */ +function wp_is_stream( $path ) { + $wrappers = stream_get_wrappers(); + $wrappers_re = '(' . join('|', $wrappers) . ')'; + + return preg_match( "!^$wrappers_re://!", $path ) === 1; +} + +/** + * Test if the supplied date is valid for the Gregorian calendar. + * + * @since 3.5.0 + * + * @see checkdate() + * + * @param int $month Month number. + * @param int $day Day number. + * @param int $year Year number. + * @param string $source_date The date to filter. + * @return bool True if valid date, false if not valid date. + */ +function wp_checkdate( $month, $day, $year, $source_date ) { + /** + * Filter whether the given date is valid for the Gregorian calendar. + * + * @since 3.5.0 + * + * @param bool $checkdate Whether the given date is valid. + * @param string $source_date Date to check. + */ + return apply_filters( 'wp_checkdate', checkdate( $month, $day, $year ), $source_date ); +} + +/** + * Load the auth check for monitoring whether the user is still logged in. + * + * Can be disabled with remove_action( 'admin_enqueue_scripts', 'wp_auth_check_load' ); + * + * This is disabled for certain screens where a login screen could cause an + * inconvenient interruption. A filter called wp_auth_check_load can be used + * for fine-grained control. + * + * @since 3.6.0 + */ +function wp_auth_check_load() { + if ( ! is_admin() && ! is_user_logged_in() ) + return; + + if ( defined( 'IFRAME_REQUEST' ) ) + return; + + $screen = get_current_screen(); + $hidden = array( 'update', 'update-network', 'update-core', 'update-core-network', 'upgrade', 'upgrade-network', 'network' ); + $show = ! in_array( $screen->id, $hidden ); + + /** + * Filter whether to load the authentication check. + * + * Passing a falsey value to the filter will effectively short-circuit + * loading the authentication check. + * + * @since 3.6.0 + * + * @param bool $show Whether to load the authentication check. + * @param WP_Screen $screen The current screen object. + */ + if ( apply_filters( 'wp_auth_check_load', $show, $screen ) ) { + wp_enqueue_style( 'wp-auth-check' ); + wp_enqueue_script( 'wp-auth-check' ); + + add_action( 'admin_print_footer_scripts', 'wp_auth_check_html', 5 ); + add_action( 'wp_print_footer_scripts', 'wp_auth_check_html', 5 ); + } +} + +/** + * Output the HTML that shows the wp-login dialog when the user is no longer logged in. + * + * @since 3.6.0 + */ +function wp_auth_check_html() { + $login_url = wp_login_url(); + $current_domain = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST']; + $same_domain = ( strpos( $login_url, $current_domain ) === 0 ); + + /** + * Filter whether the authentication check originated at the same domain. + * + * @since 3.6.0 + * + * @param bool $same_domain Whether the authentication check originated at the same domain. + */ + $same_domain = apply_filters( 'wp_auth_check_same_domain', $same_domain ); + $wrap_class = $same_domain ? 'hidden' : 'hidden fallback'; + + ?> +
        +
        +
        +
        + +
        + +
        +

        +

        +

        +
        +
        +
        + [\s\S]*<\/%1$s>|\s*\/>)', tag_escape( $tag ) ); +} + +/** + * Retrieve a canonical form of the provided charset appropriate for passing to PHP + * functions such as htmlspecialchars() and charset html attributes. + * + * @since 3.6.0 + * @access private + * + * @see https://core.trac.wordpress.org/ticket/23688 + * + * @param string $charset A charset name. + * @return string The canonical form of the charset. + */ +function _canonical_charset( $charset ) { + if ( 'UTF-8' === $charset || 'utf-8' === $charset || 'utf8' === $charset || + 'UTF8' === $charset ) + return 'UTF-8'; + + if ( 'ISO-8859-1' === $charset || 'iso-8859-1' === $charset || + 'iso8859-1' === $charset || 'ISO8859-1' === $charset ) + return 'ISO-8859-1'; + + return $charset; +} + +/** + * Set the mbstring internal encoding to a binary safe encoding when func_overload + * is enabled. + * + * When mbstring.func_overload is in use for multi-byte encodings, the results from + * strlen() and similar functions respect the utf8 characters, causing binary data + * to return incorrect lengths. + * + * This function overrides the mbstring encoding to a binary-safe encoding, and + * resets it to the users expected encoding afterwards through the + * `reset_mbstring_encoding` function. + * + * It is safe to recursively call this function, however each + * `mbstring_binary_safe_encoding()` call must be followed up with an equal number + * of `reset_mbstring_encoding()` calls. + * + * @since 3.7.0 + * + * @see reset_mbstring_encoding() + * + * @param bool $reset Optional. Whether to reset the encoding back to a previously-set encoding. + * Default false. + */ +function mbstring_binary_safe_encoding( $reset = false ) { + static $encodings = array(); + static $overloaded = null; + + if ( is_null( $overloaded ) ) + $overloaded = function_exists( 'mb_internal_encoding' ) && ( ini_get( 'mbstring.func_overload' ) & 2 ); + + if ( false === $overloaded ) + return; + + if ( ! $reset ) { + $encoding = mb_internal_encoding(); + array_push( $encodings, $encoding ); + mb_internal_encoding( 'ISO-8859-1' ); + } + + if ( $reset && $encodings ) { + $encoding = array_pop( $encodings ); + mb_internal_encoding( $encoding ); + } +} + +/** + * Reset the mbstring internal encoding to a users previously set encoding. + * + * @see mbstring_binary_safe_encoding() + * + * @since 3.7.0 + */ +function reset_mbstring_encoding() { + mbstring_binary_safe_encoding( true ); +} + +/** + * Filter/validate a variable as a boolean. + * + * Alternative to `filter_var( $var, FILTER_VALIDATE_BOOLEAN )`. + * + * @since 4.0.0 + * + * @param mixed $var Boolean value to validate. + * @return bool Whether the value is validated. + */ +function wp_validate_boolean( $var ) { + if ( is_bool( $var ) ) { + return $var; + } + + if ( is_string( $var ) && 'false' === strtolower( $var ) ) { + return false; + } + + return (bool) $var; +} diff --git a/wp-includes/functions.wp-scripts.php b/wp-includes/functions.wp-scripts.php new file mode 100644 index 0000000..6eb7a44 --- /dev/null +++ b/wp-includes/functions.wp-scripts.php @@ -0,0 +1,260 @@ +wp_enqueue_scripts', 'admin_enqueue_scripts', 'login_enqueue_scripts' ), '3.3' ); + + if ( !$handles ) + return array(); // No need to instantiate if nothing is there. + else + $wp_scripts = new WP_Scripts(); + } + + return $wp_scripts->do_items( $handles ); +} + +/** + * Register a new script. + * + * Registers a script to be linked later using the wp_enqueue_script() function. + * + * @see WP_Dependencies::add(), WP_Dependencies::add_data() + * @global WP_Scripts $wp_scripts The WP_Scripts object for printing scripts. + * + * @since 2.6.0 + * + * @param string $handle Name of the script. Should be unique. + * @param string $src Path to the script from the WordPress root directory. Example: '/js/myscript.js'. + * @param array $deps Optional. An array of registered script handles this script depends on. Set to false if there + * are no dependencies. Default empty array. + * @param string|bool $ver Optional. String specifying script version number, if it has one, which is concatenated + * to end of path as a query string. If no version is specified or set to false, a version + * number is automatically added equal to current installed WordPress version. + * If set to null, no version is added. Default 'false'. Accepts 'false', 'null', or 'string'. + * @param bool $in_footer Optional. Whether to enqueue the script before or before . + * Default 'false'. Accepts 'false' or 'true'. + */ +function wp_register_script( $handle, $src, $deps = array(), $ver = false, $in_footer = false ) { + global $wp_scripts; + if ( ! is_a( $wp_scripts, 'WP_Scripts' ) ) { + if ( ! did_action( 'init' ) ) + _doing_it_wrong( __FUNCTION__, sprintf( __( 'Scripts and styles should not be registered or enqueued until the %1$s, %2$s, or %3$s hooks.' ), + 'wp_enqueue_scripts', 'admin_enqueue_scripts', 'login_enqueue_scripts' ), '3.3' ); + $wp_scripts = new WP_Scripts(); + } + + $wp_scripts->add( $handle, $src, $deps, $ver ); + if ( $in_footer ) + $wp_scripts->add_data( $handle, 'group', 1 ); +} + +/** + * Localize a script. + * + * Works only if the script has already been added. + * + * Accepts an associative array $l10n and creates a JavaScript object: + * + * "$object_name" = { + * key: value, + * key: value, + * ... + * } + * + * + * @see WP_Dependencies::localize() + * @link https://core.trac.wordpress.org/ticket/11520 + * @global WP_Scripts $wp_scripts The WP_Scripts object for printing scripts. + * + * @since 2.6.0 + * + * @todo Documentation cleanup + * + * @param string $handle Script handle the data will be attached to. + * @param string $object_name Name for the JavaScript object. Passed directly, so it should be qualified JS variable. + * Example: '/[a-zA-Z0-9_]+/'. + * @param array $l10n The data itself. The data can be either a single or multi-dimensional array. + * @return bool True if the script was successfully localized, false otherwise. + */ +function wp_localize_script( $handle, $object_name, $l10n ) { + global $wp_scripts; + if ( ! is_a( $wp_scripts, 'WP_Scripts' ) ) { + if ( ! did_action( 'init' ) ) + _doing_it_wrong( __FUNCTION__, sprintf( __( 'Scripts and styles should not be registered or enqueued until the %1$s, %2$s, or %3$s hooks.' ), + 'wp_enqueue_scripts', 'admin_enqueue_scripts', 'login_enqueue_scripts' ), '3.3' ); + + return false; + } + + return $wp_scripts->localize( $handle, $object_name, $l10n ); +} + +/** + * Remove a registered script. + * + * Note: there are intentional safeguards in place to prevent critical admin scripts, + * such as jQuery core, from being unregistered. + * + * @see WP_Dependencies::remove() + * @global WP_Scripts $wp_scripts The WP_Scripts object for printing scripts. + * + * @since 2.6.0 + * + * @param string $handle Name of the script to be removed. + */ +function wp_deregister_script( $handle ) { + global $wp_scripts; + if ( ! is_a( $wp_scripts, 'WP_Scripts' ) ) { + if ( ! did_action( 'init' ) ) + _doing_it_wrong( __FUNCTION__, sprintf( __( 'Scripts and styles should not be registered or enqueued until the %1$s, %2$s, or %3$s hooks.' ), + 'wp_enqueue_scripts', 'admin_enqueue_scripts', 'login_enqueue_scripts' ), '3.3' ); + $wp_scripts = new WP_Scripts(); + } + + /** + * Do not allow accidental or negligent de-registering of critical scripts in the admin. + * Show minimal remorse if the correct hook is used. + */ + $current_filter = current_filter(); + if ( ( is_admin() && 'admin_enqueue_scripts' !== $current_filter ) || + ( 'wp-login.php' === $GLOBALS['pagenow'] && 'login_enqueue_scripts' !== $current_filter ) + ) { + $no = array( + 'jquery', 'jquery-core', 'jquery-migrate', 'jquery-ui-core', 'jquery-ui-accordion', + 'jquery-ui-autocomplete', 'jquery-ui-button', 'jquery-ui-datepicker', 'jquery-ui-dialog', + 'jquery-ui-draggable', 'jquery-ui-droppable', 'jquery-ui-menu', 'jquery-ui-mouse', + 'jquery-ui-position', 'jquery-ui-progressbar', 'jquery-ui-resizable', 'jquery-ui-selectable', + 'jquery-ui-slider', 'jquery-ui-sortable', 'jquery-ui-spinner', 'jquery-ui-tabs', + 'jquery-ui-tooltip', 'jquery-ui-widget', 'underscore', 'backbone', + ); + + if ( in_array( $handle, $no ) ) { + $message = sprintf( __( 'Do not deregister the %1$s script in the administration area. To target the frontend theme, use the %2$s hook.' ), + "$handle", 'wp_enqueue_scripts' ); + _doing_it_wrong( __FUNCTION__, $message, '3.6' ); + return; + } + } + + $wp_scripts->remove( $handle ); +} + +/** + * Enqueue a script. + * + * Registers the script if $src provided (does NOT overwrite), and enqueues it. + * + * @see WP_Dependencies::add(), WP_Dependencies::add_data(), WP_Dependencies::enqueue() + * @global WP_Scripts $wp_scripts The WP_Scripts object for printing scripts. + * + * @since 2.6.0 + + * @param string $handle Name of the script. + * @param string|bool $src Path to the script from the root directory of WordPress. Example: '/js/myscript.js'. + * @param array $deps An array of registered handles this script depends on. Default empty array. + * @param string|bool $ver Optional. String specifying the script version number, if it has one. This parameter + * is used to ensure that the correct version is sent to the client regardless of caching, + * and so should be included if a version number is available and makes sense for the script. + * @param bool $in_footer Optional. Whether to enqueue the script before or before . + * Default 'false'. Accepts 'false' or 'true'. + */ +function wp_enqueue_script( $handle, $src = false, $deps = array(), $ver = false, $in_footer = false ) { + global $wp_scripts; + if ( ! is_a( $wp_scripts, 'WP_Scripts' ) ) { + if ( ! did_action( 'init' ) ) + _doing_it_wrong( __FUNCTION__, sprintf( __( 'Scripts and styles should not be registered or enqueued until the %1$s, %2$s, or %3$s hooks.' ), + 'wp_enqueue_scripts', 'admin_enqueue_scripts', 'login_enqueue_scripts' ), '3.3' ); + $wp_scripts = new WP_Scripts(); + } + + if ( $src ) { + $_handle = explode('?', $handle); + $wp_scripts->add( $_handle[0], $src, $deps, $ver ); + if ( $in_footer ) + $wp_scripts->add_data( $_handle[0], 'group', 1 ); + } + $wp_scripts->enqueue( $handle ); +} + +/** + * Remove a previously enqueued script. + * + * @see WP_Dependencies::dequeue() + * @global WP_Scripts $wp_scripts The WP_Scripts object for printing scripts. + * + * @since 3.1.0 + * + * @param string $handle Name of the script to be removed. + */ +function wp_dequeue_script( $handle ) { + global $wp_scripts; + if ( ! is_a( $wp_scripts, 'WP_Scripts' ) ) { + if ( ! did_action( 'init' ) ) + _doing_it_wrong( __FUNCTION__, sprintf( __( 'Scripts and styles should not be registered or enqueued until the %1$s, %2$s, or %3$s hooks.' ), + 'wp_enqueue_scripts', 'admin_enqueue_scripts', 'login_enqueue_scripts' ), '3.3' ); + $wp_scripts = new WP_Scripts(); + } + + $wp_scripts->dequeue( $handle ); +} + +/** + * Check whether a script has been added to the queue. + * + * @global WP_Scripts $wp_scripts The WP_Scripts object for printing scripts. + * + * @since 2.8.0 + * @since 3.5.0 'enqueued' added as an alias of the 'queue' list. + * + * @param string $handle Name of the script. + * @param string $list Optional. Status of the script to check. Default 'enqueued'. + * Accepts 'enqueued', 'registered', 'queue', 'to_do', and 'done'. + * @return bool Whether the script script is queued. + */ +function wp_script_is( $handle, $list = 'enqueued' ) { + global $wp_scripts; + if ( ! is_a( $wp_scripts, 'WP_Scripts' ) ) { + if ( ! did_action( 'init' ) ) + _doing_it_wrong( __FUNCTION__, sprintf( __( 'Scripts and styles should not be registered or enqueued until the %1$s, %2$s, or %3$s hooks.' ), + 'wp_enqueue_scripts', 'admin_enqueue_scripts', 'login_enqueue_scripts' ), '3.3' ); + $wp_scripts = new WP_Scripts(); + } + + return (bool) $wp_scripts->query( $handle, $list ); +} diff --git a/wp-includes/functions.wp-styles.php b/wp-includes/functions.wp-styles.php new file mode 100644 index 0000000..ead6296 --- /dev/null +++ b/wp-includes/functions.wp-styles.php @@ -0,0 +1,245 @@ +wp_enqueue_scripts', 'admin_enqueue_scripts', 'login_enqueue_scripts' ), '3.3' ); + + if ( !$handles ) + return array(); // No need to instantiate if nothing is there. + else + $wp_styles = new WP_Styles(); + } + + return $wp_styles->do_items( $handles ); +} + +/** + * Add extra CSS styles to a registered stylesheet. + * + * Styles will only be added if the stylesheet in already in the queue. + * Accepts a string $data containing the CSS. If two or more CSS code blocks + * are added to the same stylesheet $handle, they will be printed in the order + * they were added, i.e. the latter added styles can redeclare the previous. + * + * @see WP_Styles::add_inline_style() + * @global WP_Styles $wp_styles The WP_Styles object for printing styles. + * + * @since 3.3.0 + * + * @param string $handle Name of the stylesheet to add the extra styles to. Must be lowercase. + * @param string $data String containing the CSS styles to be added. + * @return bool True on success, false on failure. + */ +function wp_add_inline_style( $handle, $data ) { + global $wp_styles; + if ( ! is_a( $wp_styles, 'WP_Styles' ) ) { + if ( ! did_action( 'init' ) ) + _doing_it_wrong( __FUNCTION__, sprintf( __( 'Scripts and styles should not be registered or enqueued until the %1$s, %2$s, or %3$s hooks.' ), + 'wp_enqueue_scripts', 'admin_enqueue_scripts', 'login_enqueue_scripts' ), '3.3' ); + $wp_styles = new WP_Styles(); + } + + if ( false !== stripos( $data, '' ) ) { + _doing_it_wrong( __FUNCTION__, __( 'Do not pass style tags to wp_add_inline_style().' ), '3.7' ); + $data = trim( preg_replace( '#]*>(.*)#is', '$1', $data ) ); + } + + return $wp_styles->add_inline_style( $handle, $data ); +} + +/** + * Register a CSS stylesheet. + * + * @see WP_Dependencies::add() + * @link http://www.w3.org/TR/CSS2/media.html#media-types List of CSS media types. + * @global WP_Styles $wp_styles The WP_Styles object for printing styles. + * + * @since 2.6.0 + * + * @param string $handle Name of the stylesheet. + * @param string|bool $src Path to the stylesheet from the WordPress root directory. Example: '/css/mystyle.css'. + * @param array $deps An array of registered style handles this stylesheet depends on. Default empty array. + * @param string|bool $ver String specifying the stylesheet version number. Used to ensure that the correct version + * is sent to the client regardless of caching. Default 'false'. Accepts 'false', 'null', or 'string'. + * @param string $media Optional. The media for which this stylesheet has been defined. + * Default 'all'. Accepts 'all', 'aural', 'braille', 'handheld', 'projection', 'print', + * 'screen', 'tty', or 'tv'. + */ +function wp_register_style( $handle, $src, $deps = array(), $ver = false, $media = 'all' ) { + global $wp_styles; + if ( ! is_a( $wp_styles, 'WP_Styles' ) ) { + if ( ! did_action( 'init' ) ) + _doing_it_wrong( __FUNCTION__, sprintf( __( 'Scripts and styles should not be registered or enqueued until the %1$s, %2$s, or %3$s hooks.' ), + 'wp_enqueue_scripts', 'admin_enqueue_scripts', 'login_enqueue_scripts' ), '3.3' ); + $wp_styles = new WP_Styles(); + } + + $wp_styles->add( $handle, $src, $deps, $ver, $media ); +} + +/** + * Remove a registered stylesheet. + * + * @see WP_Dependencies::remove() + * @global WP_Styles $wp_styles The WP_Styles object for printing styles. + * + * @since 2.1.0 + * + * @param string $handle Name of the stylesheet to be removed. + */ +function wp_deregister_style( $handle ) { + global $wp_styles; + if ( ! is_a( $wp_styles, 'WP_Styles' ) ) { + if ( ! did_action( 'init' ) ) + _doing_it_wrong( __FUNCTION__, sprintf( __( 'Scripts and styles should not be registered or enqueued until the %1$s, %2$s, or %3$s hooks.' ), + 'wp_enqueue_scripts', 'admin_enqueue_scripts', 'login_enqueue_scripts' ), '3.3' ); + $wp_styles = new WP_Styles(); + } + + $wp_styles->remove( $handle ); +} + +/** + * Enqueue a CSS stylesheet. + * + * Registers the style if source provided (does NOT overwrite) and enqueues. + * + * @see WP_Dependencies::add(), WP_Dependencies::enqueue() + * @link http://www.w3.org/TR/CSS2/media.html#media-types List of CSS media types. + * @global WP_Styles $wp_styles The WP_Styles object for printing styles. + * + * @since 2.6.0 + * + * @param string $handle Name of the stylesheet. + * @param string|bool $src Path to the stylesheet from the root directory of WordPress. Example: '/css/mystyle.css'. + * @param array $deps An array of registered style handles this stylesheet depends on. Default empty array. + * @param string|bool $ver String specifying the stylesheet version number, if it has one. This parameter is used + * to ensure that the correct version is sent to the client regardless of caching, and so + * should be included if a version number is available and makes sense for the stylesheet. + * @param string $media Optional. The media for which this stylesheet has been defined. + * Default 'all'. Accepts 'all', 'aural', 'braille', 'handheld', 'projection', 'print', + * 'screen', 'tty', or 'tv'. + */ +function wp_enqueue_style( $handle, $src = false, $deps = array(), $ver = false, $media = 'all' ) { + global $wp_styles; + if ( ! is_a( $wp_styles, 'WP_Styles' ) ) { + if ( ! did_action( 'init' ) ) + _doing_it_wrong( __FUNCTION__, sprintf( __( 'Scripts and styles should not be registered or enqueued until the %1$s, %2$s, or %3$s hooks.' ), + 'wp_enqueue_scripts', 'admin_enqueue_scripts', 'login_enqueue_scripts' ), '3.3' ); + $wp_styles = new WP_Styles(); + } + + if ( $src ) { + $_handle = explode('?', $handle); + $wp_styles->add( $_handle[0], $src, $deps, $ver, $media ); + } + $wp_styles->enqueue( $handle ); +} + +/** + * Remove a previously enqueued CSS stylesheet. + * + * @see WP_Dependencies::dequeue() + * @global WP_Styles $wp_styles The WP_Styles object for printing styles. + * + * @since 3.1.0 + * + * @param string $handle Name of the stylesheet to be removed. + */ +function wp_dequeue_style( $handle ) { + global $wp_styles; + if ( ! is_a( $wp_styles, 'WP_Styles' ) ) { + if ( ! did_action( 'init' ) ) + _doing_it_wrong( __FUNCTION__, sprintf( __( 'Scripts and styles should not be registered or enqueued until the %1$s, %2$s, or %3$s hooks.' ), + 'wp_enqueue_scripts', 'admin_enqueue_scripts', 'login_enqueue_scripts' ), '3.3' ); + $wp_styles = new WP_Styles(); + } + + $wp_styles->dequeue( $handle ); +} + +/** + * Check whether a CSS stylesheet has been added to the queue. + * + * @global WP_Styles $wp_styles The WP_Styles object for printing styles. + * + * @since 2.8.0 + * + * @param string $handle Name of the stylesheet. + * @param string $list Optional. Status of the stylesheet to check. Default 'enqueued'. + * Accepts 'enqueued', 'registered', 'queue', 'to_do', and 'done'. + * @return bool Whether style is queued. + */ +function wp_style_is( $handle, $list = 'enqueued' ) { + global $wp_styles; + if ( ! is_a( $wp_styles, 'WP_Styles' ) ) { + if ( ! did_action( 'init' ) ) + _doing_it_wrong( __FUNCTION__, sprintf( __( 'Scripts and styles should not be registered or enqueued until the %1$s, %2$s, or %3$s hooks.' ), + 'wp_enqueue_scripts', 'admin_enqueue_scripts', 'login_enqueue_scripts' ), '3.3' ); + $wp_styles = new WP_Styles(); + } + + return (bool) $wp_styles->query( $handle, $list ); +} + +/** + * Add metadata to a CSS stylesheet. + * + * Works only if the stylesheet has already been added. + * + * Possible values for $key and $value: + * 'conditional' string Comments for IE 6, lte IE 7 etc. + * 'rtl' bool|string To declare an RTL stylesheet. + * 'suffix' string Optional suffix, used in combination with RTL. + * 'alt' bool For rel="alternate stylesheet". + * 'title' string For preferred/alternate stylesheets. + * + * @see WP_Dependency::add_data() + * + * @since 3.6.0 + * + * @param string $handle Name of the stylesheet. + * @param string $key Name of data point for which we're storing a value. + * Accepts 'conditional', 'rtl' and 'suffix', 'alt' and 'title'. + * @param mixed $value String containing the CSS data to be added. + * @return bool True on success, false on failure. + */ +function wp_style_add_data( $handle, $key, $value ) { + global $wp_styles; + return $wp_styles->add_data( $handle, $key, $value ); +} diff --git a/wp-includes/general-template.php b/wp-includes/general-template.php new file mode 100644 index 0000000..56f4eec --- /dev/null +++ b/wp-includes/general-template.php @@ -0,0 +1,3143 @@ + + + + '; + } else { + $form = ''; + } + } + + /** + * Filter the HTML output of the search form. + * + * @since 2.7.0 + * + * @param string $form The search form HTML output. + */ + $result = apply_filters( 'get_search_form', $form ); + + if ( null === $result ) + $result = $form; + + if ( $echo ) + echo $result; + else + return $result; +} + +/** + * Display the Log In/Out link. + * + * Displays a link, which allows users to navigate to the Log In page to log in + * or log out depending on whether they are currently logged in. + * + * @since 1.5.0 + * + * @param string $redirect Optional path to redirect to on login/logout. + * @param boolean $echo Default to echo and not return the link. + * @return string|null String when retrieving, null when displaying. + */ +function wp_loginout($redirect = '', $echo = true) { + if ( ! is_user_logged_in() ) + $link = '' . __('Log in') . ''; + else + $link = '' . __('Log out') . ''; + + if ( $echo ) { + /** + * Filter the HTML output for the Log In/Log Out link. + * + * @since 1.5.0 + * + * @param string $link The HTML link content. + */ + echo apply_filters( 'loginout', $link ); + } else { + /** This filter is documented in wp-includes/general-template.php */ + return apply_filters( 'loginout', $link ); + } +} + +/** + * Returns the Log Out URL. + * + * Returns the URL that allows the user to log out of the site. + * + * @since 2.7.0 + * + * @param string $redirect Path to redirect to on logout. + * @return string A log out URL. + */ +function wp_logout_url($redirect = '') { + $args = array( 'action' => 'logout' ); + if ( !empty($redirect) ) { + $args['redirect_to'] = urlencode( $redirect ); + } + + $logout_url = add_query_arg($args, site_url('wp-login.php', 'login')); + $logout_url = wp_nonce_url( $logout_url, 'log-out' ); + + /** + * Filter the logout URL. + * + * @since 2.8.0 + * + * @param string $logout_url The Log Out URL. + * @param string $redirect Path to redirect to on logout. + */ + return apply_filters( 'logout_url', $logout_url, $redirect ); +} + +/** + * Returns the Log In URL. + * + * Returns the URL that allows the user to log in to the site. + * + * @since 2.7.0 + * + * @param string $redirect Path to redirect to on login. + * @param bool $force_reauth Whether to force reauthorization, even if a cookie is present. Default is false. + * @return string A log in URL. + */ +function wp_login_url($redirect = '', $force_reauth = false) { + $login_url = site_url('wp-login.php', 'login'); + + if ( !empty($redirect) ) + $login_url = add_query_arg('redirect_to', urlencode($redirect), $login_url); + + if ( $force_reauth ) + $login_url = add_query_arg('reauth', '1', $login_url); + + /** + * Filter the login URL. + * + * @since 2.8.0 + * + * @param string $login_url The login URL. + * @param string $redirect The path to redirect to on login, if supplied. + */ + return apply_filters( 'login_url', $login_url, $redirect ); +} + +/** + * Returns the user registration URL. + * + * Returns the URL that allows the user to register on the site. + * + * @since 3.6.0 + * + * @return string User registration URL. + */ +function wp_registration_url() { + /** + * Filter the user registration URL. + * + * @since 3.6.0 + * + * @param string $register The user registration URL. + */ + return apply_filters( 'register_url', site_url( 'wp-login.php?action=register', 'login' ) ); +} + +/** + * Provides a simple login form for use anywhere within WordPress. By default, it echoes + * the HTML immediately. Pass array('echo'=>false) to return the string instead. + * + * @since 3.0.0 + * + * @param array $args Configuration options to modify the form output. + * @return string|null String when retrieving, null when displaying. + */ +function wp_login_form( $args = array() ) { + $defaults = array( + 'echo' => true, + 'redirect' => ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], // Default redirect is back to the current page + 'form_id' => 'loginform', + 'label_username' => __( 'Username' ), + 'label_password' => __( 'Password' ), + 'label_remember' => __( 'Remember Me' ), + 'label_log_in' => __( 'Log In' ), + 'id_username' => 'user_login', + 'id_password' => 'user_pass', + 'id_remember' => 'rememberme', + 'id_submit' => 'wp-submit', + 'remember' => true, + 'value_username' => '', + 'value_remember' => false, // Set this to true to default the "Remember me" checkbox to checked + ); + + /** + * Filter the default login form output arguments. + * + * @since 3.0.0 + * + * @see wp_login_form() + * + * @param array $defaults An array of default login form arguments. + */ + $args = wp_parse_args( $args, apply_filters( 'login_form_defaults', $defaults ) ); + + /** + * Filter content to display at the top of the login form. + * + * The filter evaluates just following the opening form tag element. + * + * @since 3.0.0 + * + * @param string $content Content to display. Default empty. + * @param array $args Array of login form arguments. + */ + $login_form_top = apply_filters( 'login_form_top', '', $args ); + + /** + * Filter content to display in the middle of the login form. + * + * The filter evaluates just following the location where the 'login-password' + * field is displayed. + * + * @since 3.0.0 + * + * @param string $content Content to display. Default empty. + * @param array $args Array of login form arguments. + */ + $login_form_middle = apply_filters( 'login_form_middle', '', $args ); + + /** + * Filter content to display at the bottom of the login form. + * + * The filter evaluates just preceding the closing form tag element. + * + * @since 3.0.0 + * + * @param string $content Content to display. Default empty. + * @param array $args Array of login form arguments. + */ + $login_form_bottom = apply_filters( 'login_form_bottom', '', $args ); + + $form = ' +
        + ' . $login_form_top . ' + + + ' . $login_form_middle . ' + ' . ( $args['remember'] ? '' : '' ) . ' + + ' . $login_form_bottom . ' +
        '; + + if ( $args['echo'] ) + echo $form; + else + return $form; +} + +/** + * Returns the Lost Password URL. + * + * Returns the URL that allows the user to retrieve the lost password + * + * @since 2.8.0 + * + * @param string $redirect Path to redirect to on login. + * @return string Lost password URL. + */ +function wp_lostpassword_url( $redirect = '' ) { + $args = array( 'action' => 'lostpassword' ); + if ( !empty($redirect) ) { + $args['redirect_to'] = $redirect; + } + + $lostpassword_url = add_query_arg( $args, network_site_url('wp-login.php', 'login') ); + + /** + * Filter the Lost Password URL. + * + * @since 2.8.0 + * + * @param string $lostpassword_url The lost password page URL. + * @param string $redirect The path to redirect to on login. + */ + return apply_filters( 'lostpassword_url', $lostpassword_url, $redirect ); +} + +/** + * Display the Registration or Admin link. + * + * Display a link which allows the user to navigate to the registration page if + * not logged in and registration is enabled or to the dashboard if logged in. + * + * @since 1.5.0 + * + * @param string $before Text to output before the link. Default `
      11. `. + * @param string $after Text to output after the link. Default `
      12. `. + * @param boolean $echo Default to echo and not return the link. + * @return string|null String when retrieving, null when displaying. + */ +function wp_register( $before = '
      13. ', $after = '
      14. ', $echo = true ) { + + if ( ! is_user_logged_in() ) { + if ( get_option('users_can_register') ) + $link = $before . '' . __('Register') . '' . $after; + else + $link = ''; + } else { + $link = $before . '' . __('Site Admin') . '' . $after; + } + + /** + * Filter the HTML link to the Registration or Admin page. + * + * Users are sent to the admin page if logged-in, or the registration page + * if enabled and logged-out. + * + * @since 1.5.0 + * + * @param string $link The HTML code for the link to the Registration or Admin page. + */ + $link = apply_filters( 'register', $link ); + + if ( $echo ) { + echo $link; + } else { + return $link; + } +} + +/** + * Theme container function for the 'wp_meta' action. + * + * The 'wp_meta' action can have several purposes, depending on how you use it, + * but one purpose might have been to allow for theme switching. + * + * @since 1.5.0 + * + * @link https://core.trac.wordpress.org/ticket/1458 Explanation of 'wp_meta' action. + */ +function wp_meta() { + /** + * Fires before displaying echoed content in the sidebar. + * + * @since 1.5.0 + */ + do_action( 'wp_meta' ); +} + +/** + * Display information about the blog. + * + * @see get_bloginfo() For possible values for the parameter. + * @since 0.71 + * + * @param string $show What to display. + */ +function bloginfo( $show='' ) { + echo get_bloginfo( $show, 'display' ); +} + +/** + * Retrieve information about the blog. + * + * Some show parameter values are deprecated and will be removed in future + * versions. These options will trigger the {@see _deprecated_argument()} + * function. The deprecated blog info options are listed in the function + * contents. + * + * The possible values for the 'show' parameter are listed below. + * + * 1. url - Blog URI to homepage. + * 2. wpurl - Blog URI path to WordPress. + * 3. description - Secondary title + * + * The feed URL options can be retrieved from 'rdf_url' (RSS 0.91), + * 'rss_url' (RSS 1.0), 'rss2_url' (RSS 2.0), or 'atom_url' (Atom feed). The + * comment feeds can be retrieved from the 'comments_atom_url' (Atom comment + * feed) or 'comments_rss2_url' (RSS 2.0 comment feed). + * + * @since 0.71 + * + * @param string $show Blog info to retrieve. + * @param string $filter How to filter what is retrieved. + * @return string Mostly string values, might be empty. + */ +function get_bloginfo( $show = '', $filter = 'raw' ) { + + switch( $show ) { + case 'home' : // DEPRECATED + case 'siteurl' : // DEPRECATED + _deprecated_argument( __FUNCTION__, '2.2', sprintf( __('The %s option is deprecated for the family of bloginfo() functions.' ), $show ) . ' ' . sprintf( __( 'Use the %s option instead.' ), 'url' ) ); + case 'url' : + $output = home_url(); + break; + case 'wpurl' : + $output = site_url(); + break; + case 'description': + $output = get_option('blogdescription'); + break; + case 'rdf_url': + $output = get_feed_link('rdf'); + break; + case 'rss_url': + $output = get_feed_link('rss'); + break; + case 'rss2_url': + $output = get_feed_link('rss2'); + break; + case 'atom_url': + $output = get_feed_link('atom'); + break; + case 'comments_atom_url': + $output = get_feed_link('comments_atom'); + break; + case 'comments_rss2_url': + $output = get_feed_link('comments_rss2'); + break; + case 'pingback_url': + $output = site_url( 'xmlrpc.php' ); + break; + case 'stylesheet_url': + $output = get_stylesheet_uri(); + break; + case 'stylesheet_directory': + $output = get_stylesheet_directory_uri(); + break; + case 'template_directory': + case 'template_url': + $output = get_template_directory_uri(); + break; + case 'admin_email': + $output = get_option('admin_email'); + break; + case 'charset': + $output = get_option('blog_charset'); + if ('' == $output) $output = 'UTF-8'; + break; + case 'html_type' : + $output = get_option('html_type'); + break; + case 'version': + global $wp_version; + $output = $wp_version; + break; + case 'language': + $output = get_locale(); + $output = str_replace('_', '-', $output); + break; + case 'text_direction': + //_deprecated_argument( __FUNCTION__, '2.2', sprintf( __('The %s option is deprecated for the family of bloginfo() functions.' ), $show ) . ' ' . sprintf( __( 'Use the %s function instead.' ), 'is_rtl()' ) ); + if ( function_exists( 'is_rtl' ) ) { + $output = is_rtl() ? 'rtl' : 'ltr'; + } else { + $output = 'ltr'; + } + break; + case 'name': + default: + $output = get_option('blogname'); + break; + } + + $url = true; + if (strpos($show, 'url') === false && + strpos($show, 'directory') === false && + strpos($show, 'home') === false) + $url = false; + + if ( 'display' == $filter ) { + if ( $url ) { + /** + * Filter the URL returned by get_bloginfo(). + * + * @since 2.0.5 + * + * @param mixed $output The URL returned by bloginfo(). + * @param mixed $show Type of information requested. + */ + $output = apply_filters( 'bloginfo_url', $output, $show ); + } else { + /** + * Filter the site information returned by get_bloginfo(). + * + * @since 0.71 + * + * @param mixed $output The requested non-URL site information. + * @param mixed $show Type of information requested. + */ + $output = apply_filters( 'bloginfo', $output, $show ); + } + } + + return $output; +} + +/** + * Display title tag with contents. + * + * @since 4.1.0 + * @access private + * @internal + * + * @see wp_title() + */ +function _wp_render_title_tag() { + if ( ! current_theme_supports( 'title-tag' ) ) { + return; + } + + // This can only work internally on wp_head. + if ( ! did_action( 'wp_head' ) && ! doing_action( 'wp_head' ) ) { + return; + } + + echo '' . wp_title( '|', false, 'right' ) . "\n"; +} + +/** + * Display or retrieve page title for all areas of blog. + * + * By default, the page title will display the separator before the page title, + * so that the blog title will be before the page title. This is not good for + * title display, since the blog title shows up on most tabs and not what is + * important, which is the page that the user is looking at. + * + * There are also SEO benefits to having the blog title after or to the 'right' + * or the page title. However, it is mostly common sense to have the blog title + * to the right with most browsers supporting tabs. You can achieve this by + * using the seplocation parameter and setting the value to 'right'. This change + * was introduced around 2.5.0, in case backwards compatibility of themes is + * important. + * + * @since 1.0.0 + * + * @param string $sep Optional, default is '»'. How to separate the various items within the page title. + * @param bool $display Optional, default is true. Whether to display or retrieve title. + * @param string $seplocation Optional. Direction to display title, 'right'. + * @return string|null String on retrieve, null when displaying. + */ +function wp_title($sep = '»', $display = true, $seplocation = '') { + global $wp_locale, $page, $paged; + + $m = get_query_var('m'); + $year = get_query_var('year'); + $monthnum = get_query_var('monthnum'); + $day = get_query_var('day'); + $search = get_query_var('s'); + $title = ''; + + $t_sep = '%WP_TITILE_SEP%'; // Temporary separator, for accurate flipping, if necessary + + // If there is a post + if ( is_single() || ( is_home() && !is_front_page() ) || ( is_page() && !is_front_page() ) ) { + $title = single_post_title( '', false ); + } + + // If there's a post type archive + if ( is_post_type_archive() ) { + $post_type = get_query_var( 'post_type' ); + if ( is_array( $post_type ) ) + $post_type = reset( $post_type ); + $post_type_object = get_post_type_object( $post_type ); + if ( ! $post_type_object->has_archive ) + $title = post_type_archive_title( '', false ); + } + + // If there's a category or tag + if ( is_category() || is_tag() ) { + $title = single_term_title( '', false ); + } + + // If there's a taxonomy + if ( is_tax() ) { + $term = get_queried_object(); + if ( $term ) { + $tax = get_taxonomy( $term->taxonomy ); + $title = single_term_title( $tax->labels->name . $t_sep, false ); + } + } + + // If there's an author + if ( is_author() && ! is_post_type_archive() ) { + $author = get_queried_object(); + if ( $author ) + $title = $author->display_name; + } + + // Post type archives with has_archive should override terms. + if ( is_post_type_archive() && $post_type_object->has_archive ) + $title = post_type_archive_title( '', false ); + + // If there's a month + if ( is_archive() && !empty($m) ) { + $my_year = substr($m, 0, 4); + $my_month = $wp_locale->get_month(substr($m, 4, 2)); + $my_day = intval(substr($m, 6, 2)); + $title = $my_year . ( $my_month ? $t_sep . $my_month : '' ) . ( $my_day ? $t_sep . $my_day : '' ); + } + + // If there's a year + if ( is_archive() && !empty($year) ) { + $title = $year; + if ( !empty($monthnum) ) + $title .= $t_sep . $wp_locale->get_month($monthnum); + if ( !empty($day) ) + $title .= $t_sep . zeroise($day, 2); + } + + // If it's a search + if ( is_search() ) { + /* translators: 1: separator, 2: search phrase */ + $title = sprintf(__('Search Results %1$s %2$s'), $t_sep, strip_tags($search)); + } + + // If it's a 404 page + if ( is_404() ) { + $title = __('Page not found'); + } + + $prefix = ''; + if ( !empty($title) ) + $prefix = " $sep "; + + /** + * Filter the parts of the page title. + * + * @since 4.0.0 + * + * @param array $title_array Parts of the page title. + */ + $title_array = apply_filters( 'wp_title_parts', explode( $t_sep, $title ) ); + + // Determines position of the separator and direction of the breadcrumb + if ( 'right' == $seplocation ) { // sep on right, so reverse the order + $title_array = array_reverse( $title_array ); + $title = implode( " $sep ", $title_array ) . $prefix; + } else { + $title = $prefix . implode( " $sep ", $title_array ); + } + + if ( current_theme_supports( 'title-tag' ) && ! is_feed() ) { + $title .= get_bloginfo( 'name', 'display' ); + + $site_description = get_bloginfo( 'description', 'display' ); + if ( $site_description && ( is_home() || is_front_page() ) ) { + $title .= " $sep $site_description"; + } + + if ( ( $paged >= 2 || $page >= 2 ) && ! is_404() ) { + $title .= " $sep " . sprintf( __( 'Page %s' ), max( $paged, $page ) ); + } + } + + /** + * Filter the text of the page title. + * + * @since 2.0.0 + * + * @param string $title Page title. + * @param string $sep Title separator. + * @param string $seplocation Location of the separator (left or right). + */ + $title = apply_filters( 'wp_title', $title, $sep, $seplocation ); + + // Send it out + if ( $display ) + echo $title; + else + return $title; + +} + +/** + * Display or retrieve page title for post. + * + * This is optimized for single.php template file for displaying the post title. + * + * It does not support placing the separator after the title, but by leaving the + * prefix parameter empty, you can set the title separator manually. The prefix + * does not automatically place a space between the prefix, so if there should + * be a space, the parameter value will need to have it at the end. + * + * @since 0.71 + * + * @param string $prefix Optional. What to display before the title. + * @param bool $display Optional, default is true. Whether to display or retrieve title. + * @return string|null Title when retrieving, null when displaying or failure. + */ +function single_post_title($prefix = '', $display = true) { + $_post = get_queried_object(); + + if ( !isset($_post->post_title) ) + return; + + /** + * Filter the page title for a single post. + * + * @since 0.71 + * + * @param string $_post_title The single post page title. + * @param object $_post The current queried object as returned by get_queried_object(). + */ + $title = apply_filters( 'single_post_title', $_post->post_title, $_post ); + if ( $display ) + echo $prefix . $title; + else + return $prefix . $title; +} + +/** + * Display or retrieve title for a post type archive. + * + * This is optimized for archive.php and archive-{$post_type}.php template files + * for displaying the title of the post type. + * + * @since 3.1.0 + * + * @param string $prefix Optional. What to display before the title. + * @param bool $display Optional, default is true. Whether to display or retrieve title. + * @return string|null Title when retrieving, null when displaying or failure. + */ +function post_type_archive_title( $prefix = '', $display = true ) { + if ( ! is_post_type_archive() ) + return; + + $post_type = get_query_var( 'post_type' ); + if ( is_array( $post_type ) ) + $post_type = reset( $post_type ); + + $post_type_obj = get_post_type_object( $post_type ); + + /** + * Filter the post type archive title. + * + * @since 3.1.0 + * + * @param string $post_type_name Post type 'name' label. + * @param string $post_type Post type. + */ + $title = apply_filters( 'post_type_archive_title', $post_type_obj->labels->name, $post_type ); + + if ( $display ) + echo $prefix . $title; + else + return $prefix . $title; +} + +/** + * Display or retrieve page title for category archive. + * + * This is useful for category template file or files, because it is optimized + * for category page title and with less overhead than {@link wp_title()}. + * + * It does not support placing the separator after the title, but by leaving the + * prefix parameter empty, you can set the title separator manually. The prefix + * does not automatically place a space between the prefix, so if there should + * be a space, the parameter value will need to have it at the end. + * + * @since 0.71 + * + * @param string $prefix Optional. What to display before the title. + * @param bool $display Optional, default is true. Whether to display or retrieve title. + * @return string|null Title when retrieving, null when displaying or failure. + */ +function single_cat_title( $prefix = '', $display = true ) { + return single_term_title( $prefix, $display ); +} + +/** + * Display or retrieve page title for tag post archive. + * + * Useful for tag template files for displaying the tag page title. It has less + * overhead than {@link wp_title()}, because of its limited implementation. + * + * It does not support placing the separator after the title, but by leaving the + * prefix parameter empty, you can set the title separator manually. The prefix + * does not automatically place a space between the prefix, so if there should + * be a space, the parameter value will need to have it at the end. + * + * @since 2.3.0 + * + * @param string $prefix Optional. What to display before the title. + * @param bool $display Optional, default is true. Whether to display or retrieve title. + * @return string|null Title when retrieving, null when displaying or failure. + */ +function single_tag_title( $prefix = '', $display = true ) { + return single_term_title( $prefix, $display ); +} + +/** + * Display or retrieve page title for taxonomy term archive. + * + * Useful for taxonomy term template files for displaying the taxonomy term page title. + * It has less overhead than {@link wp_title()}, because of its limited implementation. + * + * It does not support placing the separator after the title, but by leaving the + * prefix parameter empty, you can set the title separator manually. The prefix + * does not automatically place a space between the prefix, so if there should + * be a space, the parameter value will need to have it at the end. + * + * @since 3.1.0 + * + * @param string $prefix Optional. What to display before the title. + * @param bool $display Optional, default is true. Whether to display or retrieve title. + * @return string|null Title when retrieving, null when displaying or failure. + */ +function single_term_title( $prefix = '', $display = true ) { + $term = get_queried_object(); + + if ( !$term ) + return; + + if ( is_category() ) { + /** + * Filter the category archive page title. + * + * @since 2.0.10 + * + * @param string $term_name Category name for archive being displayed. + */ + $term_name = apply_filters( 'single_cat_title', $term->name ); + } elseif ( is_tag() ) { + /** + * Filter the tag archive page title. + * + * @since 2.3.0 + * + * @param string $term_name Tag name for archive being displayed. + */ + $term_name = apply_filters( 'single_tag_title', $term->name ); + } elseif ( is_tax() ) { + /** + * Filter the custom taxonomy archive page title. + * + * @since 3.1.0 + * + * @param string $term_name Term name for archive being displayed. + */ + $term_name = apply_filters( 'single_term_title', $term->name ); + } else { + return; + } + + if ( empty( $term_name ) ) + return; + + if ( $display ) + echo $prefix . $term_name; + else + return $prefix . $term_name; +} + +/** + * Display or retrieve page title for post archive based on date. + * + * Useful for when the template only needs to display the month and year, if + * either are available. Optimized for just this purpose, so if it is all that + * is needed, should be better than {@link wp_title()}. + * + * It does not support placing the separator after the title, but by leaving the + * prefix parameter empty, you can set the title separator manually. The prefix + * does not automatically place a space between the prefix, so if there should + * be a space, the parameter value will need to have it at the end. + * + * @since 0.71 + * + * @param string $prefix Optional. What to display before the title. + * @param bool $display Optional, default is true. Whether to display or retrieve title. + * @return string|null Title when retrieving, null when displaying or failure. + */ +function single_month_title($prefix = '', $display = true ) { + global $wp_locale; + + $m = get_query_var('m'); + $year = get_query_var('year'); + $monthnum = get_query_var('monthnum'); + + if ( !empty($monthnum) && !empty($year) ) { + $my_year = $year; + $my_month = $wp_locale->get_month($monthnum); + } elseif ( !empty($m) ) { + $my_year = substr($m, 0, 4); + $my_month = $wp_locale->get_month(substr($m, 4, 2)); + } + + if ( empty($my_month) ) + return false; + + $result = $prefix . $my_month . $prefix . $my_year; + + if ( !$display ) + return $result; + echo $result; +} + +/** + * Display the archive title based on the queried object. + * + * @since 4.1.0 + * + * @see get_the_archive_title() + * + * @param string $before Optional. Content to prepend to the title. Default empty. + * @param string $after Optional. Content to append to the title. Default empty. + */ +function the_archive_title( $before = '', $after = '' ) { + $title = get_the_archive_title(); + + if ( ! empty( $title ) ) { + echo $before . $title . $after; + } +} + +/** + * Retrieve the archive title based on the queried object. + * + * @since 4.1.0 + * + * @return string Archive title. + */ +function get_the_archive_title() { + if ( is_category() ) { + $title = sprintf( __( 'Category: %s' ), single_cat_title( '', false ) ); + } elseif ( is_tag() ) { + $title = sprintf( __( 'Tag: %s' ), single_tag_title( '', false ) ); + } elseif ( is_author() ) { + $title = sprintf( __( 'Author: %s' ), '' . get_the_author() . '' ); + } elseif ( is_year() ) { + $title = sprintf( __( 'Year: %s' ), get_the_date( _x( 'Y', 'yearly archives date format' ) ) ); + } elseif ( is_month() ) { + $title = sprintf( __( 'Month: %s' ), get_the_date( _x( 'F Y', 'monthly archives date format' ) ) ); + } elseif ( is_day() ) { + $title = sprintf( __( 'Day: %s' ), get_the_date( _x( 'F j, Y', 'daily archives date format' ) ) ); + } elseif ( is_tax( 'post_format' ) ) { + if ( is_tax( 'post_format', 'post-format-aside' ) ) { + $title = _x( 'Asides', 'post format archive title' ); + } elseif ( is_tax( 'post_format', 'post-format-gallery' ) ) { + $title = _x( 'Galleries', 'post format archive title' ); + } elseif ( is_tax( 'post_format', 'post-format-image' ) ) { + $title = _x( 'Images', 'post format archive title' ); + } elseif ( is_tax( 'post_format', 'post-format-video' ) ) { + $title = _x( 'Videos', 'post format archive title' ); + } elseif ( is_tax( 'post_format', 'post-format-quote' ) ) { + $title = _x( 'Quotes', 'post format archive title' ); + } elseif ( is_tax( 'post_format', 'post-format-link' ) ) { + $title = _x( 'Links', 'post format archive title' ); + } elseif ( is_tax( 'post_format', 'post-format-status' ) ) { + $title = _x( 'Statuses', 'post format archive title' ); + } elseif ( is_tax( 'post_format', 'post-format-audio' ) ) { + $title = _x( 'Audio', 'post format archive title' ); + } elseif ( is_tax( 'post_format', 'post-format-chat' ) ) { + $title = _x( 'Chats', 'post format archive title' ); + } + } elseif ( is_post_type_archive() ) { + $title = sprintf( __( 'Archives: %s' ), post_type_archive_title( '', false ) ); + } elseif ( is_tax() ) { + $tax = get_taxonomy( get_queried_object()->taxonomy ); + /* translators: 1: Taxonomy singular name, 2: Current taxonomy term */ + $title = sprintf( __( '%1$s: %2$s' ), $tax->labels->singular_name, single_term_title( '', false ) ); + } else { + $title = __( 'Archives' ); + } + + /** + * Filter the archive title. + * + * @since 4.1.0 + * + * @param string $title Archive title to be displayed. + */ + return apply_filters( 'get_the_archive_title', $title ); +} + +/** + * Display category, tag, or term description. + * + * @since 4.1.0 + * + * @see get_the_archive_description() + * + * @param string $before Optional. Content to prepend to the description. Default empty. + * @param string $after Optional. Content to append to the description. Default empty. + */ +function the_archive_description( $before = '', $after = '' ) { + $description = get_the_archive_description(); + if ( $description ) { + echo $before . $description . $after; + } +} + +/** + * Retrieve category, tag, or term description. + * + * @since 4.1.0 + * + * @return string Archive description. + */ +function get_the_archive_description() { + /** + * Filter the archive description. + * + * @since 4.1.0 + * + * @see term_description() + * + * @param string $description Archive description to be displayed. + */ + return apply_filters( 'get_the_archive_description', term_description() ); +} + +/** + * Retrieve archive link content based on predefined or custom code. + * + * The format can be one of four styles. The 'link' for head element, 'option' + * for use in the select element, 'html' for use in list (either ol or ul HTML + * elements). Custom content is also supported using the before and after + * parameters. + * + * The 'link' format uses the `` HTML element with the **archives** + * relationship. The before and after parameters are not used. The text + * parameter is used to describe the link. + * + * The 'option' format uses the option HTML element for use in select element. + * The value is the url parameter and the before and after parameters are used + * between the text description. + * + * The 'html' format, which is the default, uses the li HTML element for use in + * the list HTML elements. The before parameter is before the link and the after + * parameter is after the closing link. + * + * The custom format uses the before parameter before the link ('a' HTML + * element) and the after parameter after the closing link tag. If the above + * three values for the format are not used, then custom format is assumed. + * + * @since 1.0.0 + * + * @todo Properly document optional arguments as such + * + * @param string $url URL to archive. + * @param string $text Archive text description. + * @param string $format Optional, default is 'html'. Can be 'link', 'option', 'html', or custom. + * @param string $before Optional. + * @param string $after Optional. + * @return string HTML link content for archive. + */ +function get_archives_link($url, $text, $format = 'html', $before = '', $after = '') { + $text = wptexturize($text); + $url = esc_url($url); + + if ('link' == $format) + $link_html = "\t\n"; + elseif ('option' == $format) + $link_html = "\t\n"; + elseif ('html' == $format) + $link_html = "\t
      15. $before$text$after
      16. \n"; + else // custom + $link_html = "\t$before$text$after\n"; + + /** + * Filter the archive link content. + * + * @since 2.6.0 + * + * @param string $link_html The archive HTML link content. + */ + $link_html = apply_filters( 'get_archives_link', $link_html ); + + return $link_html; +} + +/** + * Display archive links based on type and format. + * + * @since 1.2.0 + * + * @see get_archives_link() + * + * @param string|array $args { + * Default archive links arguments. Optional. + * + * @type string $type Type of archive to retrieve. Accepts 'daily', 'weekly', 'monthly', + * 'yearly', 'postbypost', or 'alpha'. Both 'postbypost' and 'alpha' + * display the same archive link list as well as post titles instead + * of displaying dates. The difference between the two is that 'alpha' + * will order by post title and 'postbypost' will order by post date. + * Default 'monthly'. + * @type string|int $limit Number of links to limit the query to. Default empty (no limit). + * @type string $format Format each link should take using the $before and $after args. + * Accepts 'link' (`` tag), 'option' (`
        ').appendTo(a("body")).hide(),b.container.bind("contextmenu",function(a){return b.isContextMenuEnabled?(a.preventDefault(),b.renderContextMenu(a.clientX-1,a.clientY-1),!1):void 0}),b.container.bind("click",function(){b.contextMenu.hide()}),b.contextMenu.bind("mouseleave",function(){b.startContextMenuTimer()})},cleancontextmenu:function(a){a.contextMenu.remove()},isContextMenuEnabled:!0,enableContextMenu:function(){this.isContextMenuEnabled=!0},disableContextMenu:function(){this.isContextMenuEnabled=!1},contextMenuTimeout:null,startContextMenuTimer:function(){var a=this;a.killContextMenuTimer(),a.contextMenuTimer=setTimeout(function(){a.hideContextMenu(),a.killContextMenuTimer()},750)},killContextMenuTimer:function(){var a=this.contextMenuTimer;null!=a&&(clearTimeout(a),delete a,a=null)},hideContextMenu:function(){this.contextMenu.hide()},renderContextMenu:function(b,c){for(var d=this,e="",f=d.options.contextMenuItems,g=0,h=f.length;h>g;g++)if(f[g].isSeparator)e+='
        ';else{var i=f[g].render(d);null!=i&&(e+='
        '+i+"
        ")}d.contextMenu.empty().append(a(e)).css({top:c,left:b}).show(),d.contextMenu.find(".mejs-contextmenu-item").each(function(){var b=a(this),c=parseInt(b.data("itemindex"),10),e=d.options.contextMenuItems[c];"undefined"!=typeof e.show&&e.show(b,d),b.click(function(){"undefined"!=typeof e.click&&e.click(d),d.contextMenu.hide()})}),setTimeout(function(){d.killControlsTimer("rev3")},100)}})}(mejs.$),function(a){a.extend(mejs.MepDefaults,{postrollCloseText:mejs.i18n.t("Close")}),a.extend(MediaElementPlayer.prototype,{buildpostroll:function(b,c,d){var e=this,f=e.container.find('link[rel="postroll"]').attr("href");"undefined"!=typeof f&&(b.postroll=a('').prependTo(d).hide(),e.media.addEventListener("ended",function(){a.ajax({dataType:"html",url:f,success:function(a){d.find(".mejs-postroll-layer-content").html(a)}}),b.postroll.show()},!1))}})}(mejs.$); \ No newline at end of file diff --git a/wp-includes/js/mediaelement/mediaelementplayer.min.css b/wp-includes/js/mediaelement/mediaelementplayer.min.css new file mode 100644 index 0000000..f71423a --- /dev/null +++ b/wp-includes/js/mediaelement/mediaelementplayer.min.css @@ -0,0 +1 @@ +.mejs-offscreen{position:absolute!important;top:-10000px;overflow:hidden;width:1px;height:1px}.mejs-container{position:relative;background:#000;font-family:Helvetica,Arial;text-align:left;vertical-align:top;text-indent:0}.me-plugin{position:absolute;height:auto;width:auto}.mejs-embed,.mejs-embed body{width:100%;height:100%;margin:0;padding:0;background:#000;overflow:hidden}.mejs-fullscreen{overflow:hidden!important}.mejs-container-fullscreen{position:fixed;left:0;top:0;right:0;bottom:0;overflow:hidden;z-index:1000}.mejs-container-fullscreen .mejs-mediaelement,.mejs-container-fullscreen video{width:100%;height:100%}.mejs-clear{clear:both}.mejs-background{position:absolute;top:0;left:0}.mejs-mediaelement{position:absolute;top:0;left:0;width:100%;height:100%}.mejs-poster{position:absolute;top:0;left:0;background-size:contain;background-position:50% 50%;background-repeat:no-repeat}:root .mejs-poster img{display:none}.mejs-poster img{border:0;padding:0;border:0}.mejs-overlay{position:absolute;top:0;left:0}.mejs-overlay-play{cursor:pointer}.mejs-overlay-button{position:absolute;top:50%;left:50%;width:100px;height:100px;margin:-50px 0 0 -50px;background:url(bigplay.svg) no-repeat}.no-svg .mejs-overlay-button{background-image:url(bigplay.png)}.mejs-overlay:hover .mejs-overlay-button{background-position:0 -100px}.mejs-overlay-loading{position:absolute;top:50%;left:50%;width:80px;height:80px;margin:-40px 0 0 -40px;background:#333;background:url(background.png);background:rgba(0,0,0,.9);background:-webkit-gradient(linear,0 0,0 100%,from(rgba(50,50,50,.9)),to(rgba(0,0,0,.9)));background:-webkit-linear-gradient(top,rgba(50,50,50,.9),rgba(0,0,0,.9));background:-moz-linear-gradient(top,rgba(50,50,50,.9),rgba(0,0,0,.9));background:-o-linear-gradient(top,rgba(50,50,50,.9),rgba(0,0,0,.9));background:-ms-linear-gradient(top,rgba(50,50,50,.9),rgba(0,0,0,.9));background:linear-gradient(rgba(50,50,50,.9),rgba(0,0,0,.9))}.mejs-overlay-loading span{display:block;width:80px;height:80px;background:transparent url(loading.gif) 50% 50% no-repeat}.mejs-container .mejs-controls{position:absolute;list-style-type:none;margin:0;padding:0;bottom:0;left:0;background:url(background.png);background:rgba(0,0,0,.7);background:-webkit-gradient(linear,0 0,0 100%,from(rgba(50,50,50,.7)),to(rgba(0,0,0,.7)));background:-webkit-linear-gradient(top,rgba(50,50,50,.7),rgba(0,0,0,.7));background:-moz-linear-gradient(top,rgba(50,50,50,.7),rgba(0,0,0,.7));background:-o-linear-gradient(top,rgba(50,50,50,.7),rgba(0,0,0,.7));background:-ms-linear-gradient(top,rgba(50,50,50,.7),rgba(0,0,0,.7));background:linear-gradient(rgba(50,50,50,.7),rgba(0,0,0,.7));height:30px;width:100%}.mejs-container .mejs-controls div{list-style-type:none;background-image:none;display:block;float:left;margin:0;padding:0;width:26px;height:26px;font-size:11px;line-height:11px;font-family:Helvetica,Arial;border:0}.mejs-controls .mejs-button button{cursor:pointer;display:block;font-size:0;line-height:0;text-decoration:none;margin:7px 5px;padding:0;position:absolute;height:16px;width:16px;border:0;background:transparent url(controls.svg) no-repeat}.no-svg .mejs-controls .mejs-button button{background-image:url(controls.png)}.mejs-controls .mejs-button button:focus{outline:dotted 1px #999}.mejs-container .mejs-controls .mejs-time{color:#fff;display:block;height:17px;width:auto;padding:8px 3px 0;overflow:hidden;text-align:center;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}.mejs-container .mejs-controls .mejs-time a{color:#fff;font-size:11px;line-height:12px;display:block;float:left;margin:1px 2px 0 0;width:auto}.mejs-controls .mejs-play button{background-position:0 0}.mejs-controls .mejs-pause button{background-position:0 -16px}.mejs-controls .mejs-stop button{background-position:-112px 0}.mejs-controls div.mejs-time-rail{direction:ltr;width:200px;padding-top:5px}.mejs-controls .mejs-time-rail span,.mejs-controls .mejs-time-rail a{display:block;position:absolute;width:180px;height:10px;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px;cursor:pointer}.mejs-controls .mejs-time-rail .mejs-time-total{margin:5px;background:#333;background:rgba(50,50,50,.8);background:-webkit-gradient(linear,0 0,0 100%,from(rgba(30,30,30,.8)),to(rgba(60,60,60,.8)));background:-webkit-linear-gradient(top,rgba(30,30,30,.8),rgba(60,60,60,.8));background:-moz-linear-gradient(top,rgba(30,30,30,.8),rgba(60,60,60,.8));background:-o-linear-gradient(top,rgba(30,30,30,.8),rgba(60,60,60,.8));background:-ms-linear-gradient(top,rgba(30,30,30,.8),rgba(60,60,60,.8));background:linear-gradient(rgba(30,30,30,.8),rgba(60,60,60,.8))}.mejs-controls .mejs-time-rail .mejs-time-buffering{width:100%;background-image:-o-linear-gradient(-45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,.15)),color-stop(0.75,rgba(255,255,255,.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(-45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(-45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-ms-linear-gradient(-45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(-45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:15px 15px;-moz-background-size:15px 15px;-o-background-size:15px 15px;background-size:15px 15px;-webkit-animation:buffering-stripes 2s linear infinite;-moz-animation:buffering-stripes 2s linear infinite;-ms-animation:buffering-stripes 2s linear infinite;-o-animation:buffering-stripes 2s linear infinite;animation:buffering-stripes 2s linear infinite}@-webkit-keyframes buffering-stripes{from{background-position:0 0}to{background-position:30px 0}}@-moz-keyframes buffering-stripes{from{background-position:0 0}to{background-position:30px 0}}@-ms-keyframes buffering-stripes{from{background-position:0 0}to{background-position:30px 0}}@-o-keyframes buffering-stripes{from{background-position:0 0}to{background-position:30px 0}}@keyframes buffering-stripes{from{background-position:0 0}to{background-position:30px 0}}.mejs-controls .mejs-time-rail .mejs-time-loaded{background:#3caac8;background:rgba(60,170,200,.8);background:-webkit-gradient(linear,0 0,0 100%,from(rgba(44,124,145,.8)),to(rgba(78,183,212,.8)));background:-webkit-linear-gradient(top,rgba(44,124,145,.8),rgba(78,183,212,.8));background:-moz-linear-gradient(top,rgba(44,124,145,.8),rgba(78,183,212,.8));background:-o-linear-gradient(top,rgba(44,124,145,.8),rgba(78,183,212,.8));background:-ms-linear-gradient(top,rgba(44,124,145,.8),rgba(78,183,212,.8));background:linear-gradient(rgba(44,124,145,.8),rgba(78,183,212,.8));width:0}.mejs-controls .mejs-time-rail .mejs-time-current{background:#fff;background:rgba(255,255,255,.8);background:-webkit-gradient(linear,0 0,0 100%,from(rgba(255,255,255,.9)),to(rgba(200,200,200,.8)));background:-webkit-linear-gradient(top,rgba(255,255,255,.9),rgba(200,200,200,.8));background:-moz-linear-gradient(top,rgba(255,255,255,.9),rgba(200,200,200,.8));background:-o-linear-gradient(top,rgba(255,255,255,.9),rgba(200,200,200,.8));background:-ms-linear-gradient(top,rgba(255,255,255,.9),rgba(200,200,200,.8));background:linear-gradient(rgba(255,255,255,.9),rgba(200,200,200,.8));width:0}.mejs-controls .mejs-time-rail .mejs-time-handle{display:none;position:absolute;margin:0;width:10px;background:#fff;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;cursor:pointer;border:solid 2px #333;top:-2px;text-align:center}.mejs-controls .mejs-time-rail .mejs-time-float{position:absolute;display:none;background:#eee;width:36px;height:17px;border:solid 1px #333;top:-26px;margin-left:-18px;text-align:center;color:#111}.mejs-controls .mejs-time-rail .mejs-time-float-current{margin:2px;width:30px;display:block;text-align:center;left:0}.mejs-controls .mejs-time-rail .mejs-time-float-corner{position:absolute;display:block;width:0;height:0;line-height:0;border:solid 5px #eee;border-color:#eee transparent transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;top:15px;left:13px}.mejs-long-video .mejs-controls .mejs-time-rail .mejs-time-float{width:48px}.mejs-long-video .mejs-controls .mejs-time-rail .mejs-time-float-current{width:44px}.mejs-long-video .mejs-controls .mejs-time-rail .mejs-time-float-corner{left:18px}.mejs-controls .mejs-fullscreen-button button{background-position:-32px 0}.mejs-controls .mejs-unfullscreen button{background-position:-32px -16px}.mejs-controls .mejs-volume-button{}.mejs-controls .mejs-mute button{background-position:-16px -16px}.mejs-controls .mejs-unmute button{background-position:-16px 0}.mejs-controls .mejs-volume-button{position:relative}.mejs-controls .mejs-volume-button .mejs-volume-slider{display:none;height:115px;width:25px;background:url(background.png);background:rgba(50,50,50,.7);-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;top:-115px;left:0;z-index:1;position:absolute;margin:0}.mejs-controls .mejs-volume-button:hover{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.mejs-controls .mejs-volume-button .mejs-volume-slider .mejs-volume-total{position:absolute;left:11px;top:8px;width:2px;height:100px;background:#ddd;background:rgba(255,255,255,.5);margin:0}.mejs-controls .mejs-volume-button .mejs-volume-slider .mejs-volume-current{position:absolute;left:11px;top:8px;width:2px;height:100px;background:#ddd;background:rgba(255,255,255,.9);margin:0}.mejs-controls .mejs-volume-button .mejs-volume-slider .mejs-volume-handle{position:absolute;left:4px;top:-3px;width:16px;height:6px;background:#ddd;background:rgba(255,255,255,.9);cursor:N-resize;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;margin:0}.mejs-controls a.mejs-horizontal-volume-slider{height:26px;width:60px;position:relative;display:table-cell;vertical-align:middle}.mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-total{position:absolute;left:0;top:11px;width:50px;height:8px;margin:0;padding:0;font-size:1px;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px;background:#333;background:rgba(50,50,50,.8);background:-webkit-gradient(linear,0 0,0 100%,from(rgba(30,30,30,.8)),to(rgba(60,60,60,.8)));background:-webkit-linear-gradient(top,rgba(30,30,30,.8),rgba(60,60,60,.8));background:-moz-linear-gradient(top,rgba(30,30,30,.8),rgba(60,60,60,.8));background:-o-linear-gradient(top,rgba(30,30,30,.8),rgba(60,60,60,.8));background:-ms-linear-gradient(top,rgba(30,30,30,.8),rgba(60,60,60,.8));background:linear-gradient(rgba(30,30,30,.8),rgba(60,60,60,.8))}.mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-current{position:absolute;left:0;top:11px;width:50px;height:8px;margin:0;padding:0;font-size:1px;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px;background:#fff;background:rgba(255,255,255,.8);background:-webkit-gradient(linear,0 0,0 100%,from(rgba(255,255,255,.9)),to(rgba(200,200,200,.8)));background:-webkit-linear-gradient(top,rgba(255,255,255,.9),rgba(200,200,200,.8));background:-moz-linear-gradient(top,rgba(255,255,255,.9),rgba(200,200,200,.8));background:-o-linear-gradient(top,rgba(255,255,255,.9),rgba(200,200,200,.8));background:-ms-linear-gradient(top,rgba(255,255,255,.9),rgba(200,200,200,.8));background:linear-gradient(rgba(255,255,255,.9),rgba(200,200,200,.8))}.mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-handle{display:none}.mejs-controls .mejs-captions-button{position:relative}.mejs-controls .mejs-captions-button button{background-position:-48px 0}.mejs-controls .mejs-captions-button .mejs-captions-selector{visibility:hidden;position:absolute;bottom:26px;right:-51px;width:85px;height:100px;background:url(background.png);background:rgba(50,50,50,.7);border:solid 1px transparent;padding:10px 10px 0;overflow:hidden;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.mejs-controls .mejs-captions-button .mejs-captions-selector ul{margin:0;padding:0;display:block;list-style-type:none!important;overflow:hidden}.mejs-controls .mejs-captions-button .mejs-captions-selector ul li{margin:0 0 6px;padding:0;list-style-type:none!important;display:block;color:#fff;overflow:hidden}.mejs-controls .mejs-captions-button .mejs-captions-selector ul li input{clear:both;float:left;margin:3px 3px 0 5px}.mejs-controls .mejs-captions-button .mejs-captions-selector ul li label{width:55px;float:left;padding:4px 0 0;line-height:15px;font-family:helvetica,arial;font-size:10px}.mejs-controls .mejs-captions-button .mejs-captions-translations{font-size:10px;margin:0 0 5px}.mejs-chapters{position:absolute;top:0;left:0;-xborder-right:solid 1px #fff;width:10000px;z-index:1}.mejs-chapters .mejs-chapter{position:absolute;float:left;background:#222;background:rgba(0,0,0,.7);background:-webkit-gradient(linear,0 0,0 100%,from(rgba(50,50,50,.7)),to(rgba(0,0,0,.7)));background:-webkit-linear-gradient(top,rgba(50,50,50,.7),rgba(0,0,0,.7));background:-moz-linear-gradient(top,rgba(50,50,50,.7),rgba(0,0,0,.7));background:-o-linear-gradient(top,rgba(50,50,50,.7),rgba(0,0,0,.7));background:-ms-linear-gradient(top,rgba(50,50,50,.7),rgba(0,0,0,.7));background:linear-gradient(rgba(50,50,50,.7),rgba(0,0,0,.7));filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0, startColorstr=#323232, endColorstr=#000000);overflow:hidden;border:0}.mejs-chapters .mejs-chapter .mejs-chapter-block{font-size:11px;color:#fff;padding:5px;display:block;border-right:solid 1px #333;border-bottom:solid 1px #333;cursor:pointer}.mejs-chapters .mejs-chapter .mejs-chapter-block-last{border-right:0}.mejs-chapters .mejs-chapter .mejs-chapter-block:hover{background:#666;background:rgba(102,102,102,.7);background:-webkit-gradient(linear,0 0,0 100%,from(rgba(102,102,102,.7)),to(rgba(50,50,50,.6)));background:-webkit-linear-gradient(top,rgba(102,102,102,.7),rgba(50,50,50,.6));background:-moz-linear-gradient(top,rgba(102,102,102,.7),rgba(50,50,50,.6));background:-o-linear-gradient(top,rgba(102,102,102,.7),rgba(50,50,50,.6));background:-ms-linear-gradient(top,rgba(102,102,102,.7),rgba(50,50,50,.6));background:linear-gradient(rgba(102,102,102,.7),rgba(50,50,50,.6));filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0, startColorstr=#666666, endColorstr=#323232)}.mejs-chapters .mejs-chapter .mejs-chapter-block .ch-title{font-size:12px;font-weight:700;display:block;white-space:nowrap;text-overflow:ellipsis;margin:0 0 3px;line-height:12px}.mejs-chapters .mejs-chapter .mejs-chapter-block .ch-timespan{font-size:12px;line-height:12px;margin:3px 0 4px;display:block;white-space:nowrap;text-overflow:ellipsis}.mejs-captions-layer{position:absolute;bottom:0;left:0;text-align:center;line-height:20px;font-size:16px;color:#fff}.mejs-captions-layer a{color:#fff;text-decoration:underline}.mejs-captions-layer[lang=ar]{font-size:20px;font-weight:400}.mejs-captions-position{position:absolute;width:100%;bottom:15px;left:0}.mejs-captions-position-hover{bottom:35px}.mejs-captions-text{padding:3px 5px;background:url(background.png);background:rgba(20,20,20,.5);white-space:pre-wrap}.me-cannotplay{}.me-cannotplay a{color:#fff;font-weight:700}.me-cannotplay span{padding:15px;display:block}.mejs-controls .mejs-loop-off button{background-position:-64px -16px}.mejs-controls .mejs-loop-on button{background-position:-64px 0}.mejs-controls .mejs-backlight-off button{background-position:-80px -16px}.mejs-controls .mejs-backlight-on button{background-position:-80px 0}.mejs-controls .mejs-picturecontrols-button{background-position:-96px 0}.mejs-contextmenu{position:absolute;width:150px;padding:10px;border-radius:4px;top:0;left:0;background:#fff;border:solid 1px #999;z-index:1001}.mejs-contextmenu .mejs-contextmenu-separator{height:1px;font-size:0;margin:5px 6px;background:#333}.mejs-contextmenu .mejs-contextmenu-item{font-family:Helvetica,Arial;font-size:12px;padding:4px 6px;cursor:pointer;color:#333}.mejs-contextmenu .mejs-contextmenu-item:hover{background:#2C7C91;color:#fff}.mejs-controls .mejs-sourcechooser-button{position:relative}.mejs-controls .mejs-sourcechooser-button button{background-position:-128px 0}.mejs-controls .mejs-sourcechooser-button .mejs-sourcechooser-selector{visibility:hidden;position:absolute;bottom:26px;right:-10px;width:130px;height:100px;background:url(background.png);background:rgba(50,50,50,.7);border:solid 1px transparent;padding:10px;overflow:hidden;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.mejs-controls .mejs-sourcechooser-button .mejs-sourcechooser-selector ul{margin:0;padding:0;display:block;list-style-type:none!important;overflow:hidden}.mejs-controls .mejs-sourcechooser-button .mejs-sourcechooser-selector ul li{margin:0 0 6px;padding:0;list-style-type:none!important;display:block;color:#fff;overflow:hidden}.mejs-controls .mejs-sourcechooser-button .mejs-sourcechooser-selector ul li input{clear:both;float:left;margin:3px 3px 0 5px}.mejs-controls .mejs-sourcechooser-button .mejs-sourcechooser-selector ul li label{width:100px;float:left;padding:4px 0 0;line-height:15px;font-family:helvetica,arial;font-size:10px}.mejs-postroll-layer{position:absolute;bottom:0;left:0;width:100%;height:100%;background:url(background.png);background:rgba(50,50,50,.7);z-index:1000;overflow:hidden}.mejs-postroll-layer-content{width:100%;height:100%}.mejs-postroll-close{position:absolute;right:0;top:0;background:url(background.png);background:rgba(50,50,50,.7);color:#fff;padding:4px;z-index:100;cursor:pointer}div.mejs-speed-button{width:46px!important;position:relative}.mejs-controls .mejs-button.mejs-speed-button button{background:transparent;width:36px;font-size:11px;line-height:normal;color:#fff}.mejs-controls .mejs-speed-button .mejs-speed-selector{visibility:hidden;position:absolute;top:-100px;left:-10px;width:60px;height:100px;background:url(background.png);background:rgba(50,50,50,.7);border:solid 1px transparent;padding:0;overflow:hidden;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.mejs-controls .mejs-speed-button:hover>.mejs-speed-selector{visibility:visible}.mejs-controls .mejs-speed-button .mejs-speed-selector ul li label.mejs-speed-selected{color:rgba(33,248,248,1)}.mejs-controls .mejs-speed-button .mejs-speed-selector ul{margin:0;padding:0;display:block;list-style-type:none!important;overflow:hidden}.mejs-controls .mejs-speed-button .mejs-speed-selector ul li{margin:0 0 6px;padding:0 10px;list-style-type:none!important;display:block;color:#fff;overflow:hidden}.mejs-controls .mejs-speed-button .mejs-speed-selector ul li input{clear:both;float:left;margin:3px 3px 0 5px;display:none}.mejs-controls .mejs-speed-button .mejs-speed-selector ul li label{width:60px;float:left;padding:4px 0 0;line-height:15px;font-family:helvetica,arial;font-size:11.5px;color:#fff;margin-left:5px;cursor:pointer}.mejs-controls .mejs-speed-button .mejs-speed-selector ul li:hover{background-color:#c8c8c8!important;background-color:rgba(255,255,255,.4)!important}.mejs-controls .mejs-button.mejs-skip-back-button{background:transparent url(skipback.png) no-repeat;background-position:3px 3px}.mejs-controls .mejs-button.mejs-skip-back-button button{background:transparent;font-size:9px;line-height:normal;color:#fff} \ No newline at end of file diff --git a/wp-includes/js/mediaelement/silverlightmediaelement.xap b/wp-includes/js/mediaelement/silverlightmediaelement.xap new file mode 100644 index 0000000..9d55c2e Binary files /dev/null and b/wp-includes/js/mediaelement/silverlightmediaelement.xap differ diff --git a/wp-includes/js/mediaelement/skipback.png b/wp-includes/js/mediaelement/skipback.png new file mode 100644 index 0000000..04756f9 Binary files /dev/null and b/wp-includes/js/mediaelement/skipback.png differ diff --git a/wp-includes/js/mediaelement/wp-mediaelement.css b/wp-includes/js/mediaelement/wp-mediaelement.css new file mode 100644 index 0000000..e724a15 --- /dev/null +++ b/wp-includes/js/mediaelement/wp-mediaelement.css @@ -0,0 +1,307 @@ +.mejs-container { + clear: both; +} + +.mejs-container * { + font-family: Helvetica, Arial; +} + +.mejs-container, +.mejs-embed, +.mejs-embed body, +.mejs-container .mejs-controls { + background: #222; +} + +.mejs-controls a.mejs-horizontal-volume-slider { + display: table; +} + +.mejs-controls .mejs-time-rail .mejs-time-loaded, +.mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-current { + background: #fff; +} + +.mejs-controls .mejs-time-rail .mejs-time-current { + background: #0074a2; +} + +.mejs-controls .mejs-time-rail .mejs-time-total, +.mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-total { + background: rgba(255, 255, 255, .33); +} + +.mejs-controls .mejs-time-rail span, +.mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-total, +.mejs-controls .mejs-horizontal-volume-slider .mejs-horizontal-volume-current { + border-radius: 0; +} + +.mejs-controls .mejs-offscreen { + clip: rect(1px, 1px, 1px, 1px); + position: absolute; +} + +.mejs-controls a:focus > .mejs-offscreen { + background-color: #f1f1f1; + border-radius: 3px; + box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.6); + clip: auto; + color: #21759b; + display: block; + font-size: 14px; + font-weight: bold; + height: auto; + line-height: normal; + padding: 15px 23px 14px; + position: absolute; + left: 0; + top: 15px; + text-decoration: none; + text-transform: none; + width: auto; +} + +.mejs-overlay-loading { + background: transparent; +} + +/* Override theme styles that may conflict with controls. */ +.mejs-controls button:hover { + border: none; + -webkit-box-shadow: none; + box-shadow: none; +} + +.me-cannotplay { + width: auto !important; +} + +.media-embed-details .wp-audio-shortcode { + display: inline-block; + max-width: 400px; +} + +.audio-details .embed-media-settings { + overflow: visible; +} + +.media-embed-details .embed-media-settings .setting span { + max-width: 400px; + width: auto; +} + +.media-embed-details .embed-media-settings .checkbox-setting span { + display: inline-block; +} + +.media-embed-details .embed-media-settings { + padding-top: 0; + top: 28px; +} + +.media-embed-details .instructions { + padding: 16px 0; + max-width: 600px; +} + +.media-embed-details .setting p, +.media-embed-details .setting a { + color: #a00; + font-size: 10px; + text-transform: uppercase; +} + +.media-embed-details .setting a:hover { + color: #f00; +} + +.media-embed-details .embed-media-settings .checkbox-setting { + float: none; + margin: 0 0 10px; +} + +.wp-video { + max-width: 100%; + height: auto; +} + +.wp_attachment_holder .wp-video { + margin-top: 18px; +} + +video.wp-video-shortcode, +.wp-video-shortcode video { + max-width: 100%; + display: inline-block; +} + +.video-details .wp-video-holder { + width: 100%; + max-width: 640px; +} + +.wp-playlist { + border: 1px solid #ccc; + padding: 10px; + margin: 12px 0 18px; + font-size: 14px; + line-height: 1.5; +} + +.wp-admin .wp-playlist { + margin: 0 0 18px; +} + +.wp-playlist video { + display: inline-block; + max-width: 100%; +} + +.wp-playlist audio { + display: none; + max-width: 100%; + width: 400px; +} + +.wp-playlist .mejs-container { + margin: 0; + width: 100%; +} + +.wp-playlist .mejs-controls .mejs-button button { + outline: 0; +} + +.wp-playlist-light { + background: #fff; + color: #000; +} + +.wp-playlist-dark { + color: #fff; + background: #000; +} + +.wp-playlist-caption { + display: block; + max-width: 88%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 14px; + line-height: 1.5; +} + +.wp-playlist-item .wp-playlist-caption { + text-decoration: none; + color: #000; + max-width: -webkit-calc(100% - 40px); + max-width: calc(100% - 40px); +} + +.wp-playlist-item-meta { + display: block; + font-size: 14px; + line-height: 1.5; +} + +.wp-playlist-item-title { + font-size: 14px; + line-height: 1.5; +} + +.wp-playlist-item-album { + font-style: italic; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.wp-playlist-item-artist { + font-size: 12px; + text-transform: uppercase; +} + +.wp-playlist-item-length { + position: absolute; + right: 3px; + top: 0; + font-size: 14px; + line-height: 1.5; +} + +.rtl .wp-playlist-item-length { + left: 3px; + right: auto; +} + +.wp-playlist-tracks { + margin-top: 10px; +} + +.wp-playlist-item { + position: relative; + cursor: pointer; + padding: 0 3px; + border-bottom: 1px solid #ccc; +} + +.wp-playlist-item:last-child { + border-bottom: 0; +} + +.wp-playlist-light .wp-playlist-caption { + color: #333; +} + +.wp-playlist-dark .wp-playlist-caption { + color: #dedede; +} + +.wp-playlist-playing { + font-weight: bold; + background: #f7f7f7; +} + +.wp-playlist-light .wp-playlist-playing { + background: #fff; + color: #000; +} + +.wp-playlist-dark .wp-playlist-playing { + background: #000; + color: #fff; +} + +.wp-playlist-current-item { + overflow: hidden; + margin-bottom: 10px; + height: 60px; +} + +.wp-playlist .wp-playlist-current-item img { + float: left; + max-width: 60px; + height: auto; + margin-right: 10px; + padding: 0; + border: 0; +} + +.rtl .wp-playlist .wp-playlist-current-item img { + float: right; + margin-left: 10px; + margin-right: 0; +} + +.wp-playlist-current-item .wp-playlist-item-title, +.wp-playlist-current-item .wp-playlist-item-artist { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.wp-audio-playlist .me-cannotplay span { + padding: 5px 15px; +} diff --git a/wp-includes/js/mediaelement/wp-mediaelement.js b/wp-includes/js/mediaelement/wp-mediaelement.js new file mode 100644 index 0000000..8c4364a --- /dev/null +++ b/wp-includes/js/mediaelement/wp-mediaelement.js @@ -0,0 +1,34 @@ +/* global mejs, _wpmejsSettings */ +(function ($) { + // add mime-type aliases to MediaElement plugin support + mejs.plugins.silverlight[0].types.push('video/x-ms-wmv'); + mejs.plugins.silverlight[0].types.push('audio/x-ms-wma'); + + $(function () { + var settings = {}; + + if ( typeof _wpmejsSettings !== 'undefined' ) { + settings = _wpmejsSettings; + } + + settings.success = settings.success || function (mejs) { + var autoplay, loop; + + if ( 'flash' === mejs.pluginType ) { + autoplay = mejs.attributes.autoplay && 'false' !== mejs.attributes.autoplay; + loop = mejs.attributes.loop && 'false' !== mejs.attributes.loop; + + autoplay && mejs.addEventListener( 'canplay', function () { + mejs.play(); + }, false ); + + loop && mejs.addEventListener( 'ended', function () { + mejs.play(); + }, false ); + } + }; + + $('.wp-audio-shortcode, .wp-video-shortcode').mediaelementplayer( settings ); + }); + +}(jQuery)); diff --git a/wp-includes/js/mediaelement/wp-playlist.js b/wp-includes/js/mediaelement/wp-playlist.js new file mode 100644 index 0000000..90839a5 --- /dev/null +++ b/wp-includes/js/mediaelement/wp-playlist.js @@ -0,0 +1,175 @@ +/*globals window, document, jQuery, _, Backbone, _wpmejsSettings */ + +(function ($, _, Backbone) { + "use strict"; + + var WPPlaylistView = Backbone.View.extend({ + initialize : function (options) { + this.index = 0; + this.settings = {}; + this.data = options.metadata || $.parseJSON( this.$('script.wp-playlist-script').html() ); + this.playerNode = this.$( this.data.type ); + + this.tracks = new Backbone.Collection( this.data.tracks ); + this.current = this.tracks.first(); + + if ( 'audio' === this.data.type ) { + this.currentTemplate = wp.template( 'wp-playlist-current-item' ); + this.currentNode = this.$( '.wp-playlist-current-item' ); + } + + this.renderCurrent(); + + if ( this.data.tracklist ) { + this.itemTemplate = wp.template( 'wp-playlist-item' ); + this.playingClass = 'wp-playlist-playing'; + this.renderTracks(); + } + + this.playerNode.attr( 'src', this.current.get( 'src' ) ); + + _.bindAll( this, 'bindPlayer', 'bindResetPlayer', 'setPlayer', 'ended', 'clickTrack' ); + + if ( ! _.isUndefined( window._wpmejsSettings ) ) { + this.settings = _wpmejsSettings; + } + this.settings.success = this.bindPlayer; + this.setPlayer(); + }, + + bindPlayer : function (mejs) { + this.mejs = mejs; + this.mejs.addEventListener( 'ended', this.ended ); + }, + + bindResetPlayer : function (mejs) { + this.bindPlayer( mejs ); + this.playCurrentSrc(); + }, + + setPlayer: function (force) { + if ( this.player ) { + this.player.pause(); + this.player.remove(); + this.playerNode = this.$( this.data.type ); + } + + if (force) { + this.playerNode.attr( 'src', this.current.get( 'src' ) ); + this.settings.success = this.bindResetPlayer; + } + + /** + * This is also our bridge to the outside world + */ + this.player = new MediaElementPlayer( this.playerNode.get(0), this.settings ); + }, + + playCurrentSrc : function () { + this.renderCurrent(); + this.mejs.setSrc( this.playerNode.attr( 'src' ) ); + this.mejs.load(); + this.mejs.play(); + }, + + renderCurrent : function () { + var dimensions, defaultImage = 'wp-includes/images/media/video.png'; + if ( 'video' === this.data.type ) { + if ( this.data.images && this.current.get( 'image' ) && -1 === this.current.get( 'image' ).src.indexOf( defaultImage ) ) { + this.playerNode.attr( 'poster', this.current.get( 'image' ).src ); + } + dimensions = this.current.get( 'dimensions' ).resized; + this.playerNode.attr( dimensions ); + } else { + if ( ! this.data.images ) { + this.current.set( 'image', false ); + } + this.currentNode.html( this.currentTemplate( this.current.toJSON() ) ); + } + }, + + renderTracks : function () { + var self = this, i = 1, tracklist = $( '
        ' ); + this.tracks.each(function (model) { + if ( ! self.data.images ) { + model.set( 'image', false ); + } + model.set( 'artists', self.data.artists ); + model.set( 'index', self.data.tracknumbers ? i : false ); + tracklist.append( self.itemTemplate( model.toJSON() ) ); + i += 1; + }); + this.$el.append( tracklist ); + + this.$( '.wp-playlist-item' ).eq(0).addClass( this.playingClass ); + }, + + events : { + 'click .wp-playlist-item' : 'clickTrack', + 'click .wp-playlist-next' : 'next', + 'click .wp-playlist-prev' : 'prev' + }, + + clickTrack : function (e) { + e.preventDefault(); + + this.index = this.$( '.wp-playlist-item' ).index( e.currentTarget ); + this.setCurrent(); + }, + + ended : function () { + if ( this.index + 1 < this.tracks.length ) { + this.next(); + } else { + this.index = 0; + this.setCurrent(); + } + }, + + next : function () { + this.index = this.index + 1 >= this.tracks.length ? 0 : this.index + 1; + this.setCurrent(); + }, + + prev : function () { + this.index = this.index - 1 < 0 ? this.tracks.length - 1 : this.index - 1; + this.setCurrent(); + }, + + loadCurrent : function () { + var last = this.playerNode.attr( 'src' ) && this.playerNode.attr( 'src' ).split('.').pop(), + current = this.current.get( 'src' ).split('.').pop(); + + this.mejs && this.mejs.pause(); + + if ( last !== current ) { + this.setPlayer( true ); + } else { + this.playerNode.attr( 'src', this.current.get( 'src' ) ); + this.playCurrentSrc(); + } + }, + + setCurrent : function () { + this.current = this.tracks.at( this.index ); + + if ( this.data.tracklist ) { + this.$( '.wp-playlist-item' ) + .removeClass( this.playingClass ) + .eq( this.index ) + .addClass( this.playingClass ); + } + + this.loadCurrent(); + } + }); + + $(document).ready(function () { + $('.wp-playlist').each( function() { + return new WPPlaylistView({ el: this }); + } ); + }); + + window.WPPlaylistView = WPPlaylistView; + +}(jQuery, _, Backbone)); \ No newline at end of file diff --git a/wp-includes/js/plupload/handlers.js b/wp-includes/js/plupload/handlers.js new file mode 100644 index 0000000..d6a9958 --- /dev/null +++ b/wp-includes/js/plupload/handlers.js @@ -0,0 +1,488 @@ +/* global plupload, pluploadL10n, ajaxurl, post_id, wpUploaderInit, deleteUserSetting, setUserSetting, getUserSetting, shortform */ +var topWin = window.dialogArguments || opener || parent || top, uploader, uploader_init; + +// progress and success handlers for media multi uploads +function fileQueued(fileObj) { + // Get rid of unused form + jQuery('.media-blank').remove(); + + var items = jQuery('#media-items').children(), postid = post_id || 0; + + // Collapse a single item + if ( items.length == 1 ) { + items.removeClass('open').find('.slidetoggle').slideUp(200); + } + // Create a progress bar containing the filename + jQuery('
        ') + .attr( 'id', 'media-item-' + fileObj.id ) + .addClass('child-of-' + postid) + .append('
        0%
        ', + jQuery('
        ').text( ' ' + fileObj.name )) + .appendTo( jQuery('#media-items' ) ); + + // Disable submit + jQuery('#insert-gallery').prop('disabled', true); +} + +function uploadStart() { + try { + if ( typeof topWin.tb_remove != 'undefined' ) + topWin.jQuery('#TB_overlay').unbind('click', topWin.tb_remove); + } catch(e){} + + return true; +} + +function uploadProgress(up, file) { + var item = jQuery('#media-item-' + file.id); + + jQuery('.bar', item).width( (200 * file.loaded) / file.size ); + jQuery('.percent', item).html( file.percent + '%' ); +} + +// check to see if a large file failed to upload +function fileUploading( up, file ) { + var hundredmb = 100 * 1024 * 1024, + max = parseInt( up.settings.max_file_size, 10 ); + + if ( max > hundredmb && file.size > hundredmb ) { + setTimeout( function() { + if ( file.status < 3 && file.loaded === 0 ) { // not uploading + wpFileError( file, pluploadL10n.big_upload_failed.replace( '%1$s', '' ).replace( '%2$s', '' ) ); + up.stop(); // stops the whole queue + up.removeFile( file ); + up.start(); // restart the queue + } + }, 10000 ); // wait for 10 sec. for the file to start uploading + } +} + +function updateMediaForm() { + var items = jQuery('#media-items').children(); + + // Just one file, no need for collapsible part + if ( items.length == 1 ) { + items.addClass('open').find('.slidetoggle').show(); + jQuery('.insert-gallery').hide(); + } else if ( items.length > 1 ) { + items.removeClass('open'); + // Only show Gallery/Playlist buttons when there are at least two files. + jQuery('.insert-gallery').show(); + } + + // Only show Save buttons when there is at least one file. + if ( items.not('.media-blank').length > 0 ) + jQuery('.savebutton').show(); + else + jQuery('.savebutton').hide(); +} + +function uploadSuccess(fileObj, serverData) { + var item = jQuery('#media-item-' + fileObj.id); + + // on success serverData should be numeric, fix bug in html4 runtime returning the serverData wrapped in a
         tag
        +	serverData = serverData.replace(/^
        (\d+)<\/pre>$/, '$1');
        +
        +	// if async-upload returned an error message, place it in the media item div and return
        +	if ( serverData.match(/media-upload-error|error-div/) ) {
        +		item.html(serverData);
        +		return;
        +	} else {
        +		jQuery('.percent', item).html( pluploadL10n.crunching );
        +	}
        +
        +	prepareMediaItem(fileObj, serverData);
        +	updateMediaForm();
        +
        +	// Increment the counter.
        +	if ( post_id && item.hasClass('child-of-' + post_id) )
        +		jQuery('#attachments-count').text(1 * jQuery('#attachments-count').text() + 1);
        +}
        +
        +function setResize( arg ) {
        +	if ( arg ) {
        +		if ( window.resize_width && window.resize_height ) {
        +			uploader.settings.resize = {
        +				enabled: true,
        +				width: window.resize_width,
        +				height: window.resize_height,
        +				quality: 100
        +			};
        +		} else {
        +			uploader.settings.multipart_params.image_resize = true;
        +		}
        +	} else {
        +		delete( uploader.settings.multipart_params.image_resize );
        +	}
        +}
        +
        +function prepareMediaItem(fileObj, serverData) {
        +	var f = ( typeof shortform == 'undefined' ) ? 1 : 2, item = jQuery('#media-item-' + fileObj.id);
        +	if ( f == 2 && shortform > 2 )
        +		f = shortform;
        +
        +	try {
        +		if ( typeof topWin.tb_remove != 'undefined' )
        +			topWin.jQuery('#TB_overlay').click(topWin.tb_remove);
        +	} catch(e){}
        +
        +	if ( isNaN(serverData) || !serverData ) { // Old style: Append the HTML returned by the server -- thumbnail and form inputs
        +		item.append(serverData);
        +		prepareMediaItemInit(fileObj);
        +	} else { // New style: server data is just the attachment ID, fetch the thumbnail and form html from the server
        +		item.load('async-upload.php', {attachment_id:serverData, fetch:f}, function(){prepareMediaItemInit(fileObj);updateMediaForm();});
        +	}
        +}
        +
        +function prepareMediaItemInit(fileObj) {
        +	var item = jQuery('#media-item-' + fileObj.id);
        +	// Clone the thumbnail as a "pinkynail" -- a tiny image to the left of the filename
        +	jQuery('.thumbnail', item).clone().attr('class', 'pinkynail toggle').prependTo(item);
        +
        +	// Replace the original filename with the new (unique) one assigned during upload
        +	jQuery('.filename.original', item).replaceWith( jQuery('.filename.new', item) );
        +
        +	// Bind AJAX to the new Delete button
        +	jQuery('a.delete', item).click(function(){
        +		// Tell the server to delete it. TODO: handle exceptions
        +		jQuery.ajax({
        +			url: ajaxurl,
        +			type: 'post',
        +			success: deleteSuccess,
        +			error: deleteError,
        +			id: fileObj.id,
        +			data: {
        +				id : this.id.replace(/[^0-9]/g, ''),
        +				action : 'trash-post',
        +				_ajax_nonce : this.href.replace(/^.*wpnonce=/,'')
        +			}
        +		});
        +		return false;
        +	});
        +
        +	// Bind AJAX to the new Undo button
        +	jQuery('a.undo', item).click(function(){
        +		// Tell the server to untrash it. TODO: handle exceptions
        +		jQuery.ajax({
        +			url: ajaxurl,
        +			type: 'post',
        +			id: fileObj.id,
        +			data: {
        +				id : this.id.replace(/[^0-9]/g,''),
        +				action: 'untrash-post',
        +				_ajax_nonce: this.href.replace(/^.*wpnonce=/,'')
        +			},
        +			success: function( ){
        +				var type,
        +					item = jQuery('#media-item-' + fileObj.id);
        +
        +				if ( type = jQuery('#type-of-' + fileObj.id).val() )
        +					jQuery('#' + type + '-counter').text(jQuery('#' + type + '-counter').text()-0+1);
        +
        +				if ( post_id && item.hasClass('child-of-'+post_id) )
        +					jQuery('#attachments-count').text(jQuery('#attachments-count').text()-0+1);
        +
        +				jQuery('.filename .trashnotice', item).remove();
        +				jQuery('.filename .title', item).css('font-weight','normal');
        +				jQuery('a.undo', item).addClass('hidden');
        +				jQuery('.menu_order_input', item).show();
        +				item.css( {backgroundColor:'#ceb'} ).animate( {backgroundColor: '#fff'}, { queue: false, duration: 500, complete: function(){ jQuery(this).css({backgroundColor:''}); } }).removeClass('undo');
        +			}
        +		});
        +		return false;
        +	});
        +
        +	// Open this item if it says to start open (e.g. to display an error)
        +	jQuery('#media-item-' + fileObj.id + '.startopen').removeClass('startopen').addClass('open').find('slidetoggle').fadeIn();
        +}
        +
        +// generic error message
        +function wpQueueError(message) {
        +	jQuery('#media-upload-error').show().html( '

        ' + message + '

        ' ); +} + +// file-specific error messages +function wpFileError(fileObj, message) { + itemAjaxError(fileObj.id, message); +} + +function itemAjaxError(id, message) { + var item = jQuery('#media-item-' + id), filename = item.find('.filename').text(), last_err = item.data('last-err'); + + if ( last_err == id ) // prevent firing an error for the same file twice + return; + + item.html('
        ' + + '' + pluploadL10n.dismiss + '' + + '' + pluploadL10n.error_uploading.replace('%s', jQuery.trim(filename)) + ' ' + + message + + '
        ').data('last-err', id); +} + +function deleteSuccess(data) { + var type, id, item; + if ( data == '-1' ) + return itemAjaxError(this.id, 'You do not have permission. Has your session expired?'); + + if ( data == '0' ) + return itemAjaxError(this.id, 'Could not be deleted. Has it been deleted already?'); + + id = this.id; + item = jQuery('#media-item-' + id); + + // Decrement the counters. + if ( type = jQuery('#type-of-' + id).val() ) + jQuery('#' + type + '-counter').text( jQuery('#' + type + '-counter').text() - 1 ); + + if ( post_id && item.hasClass('child-of-'+post_id) ) + jQuery('#attachments-count').text( jQuery('#attachments-count').text() - 1 ); + + if ( jQuery('form.type-form #media-items').children().length == 1 && jQuery('.hidden', '#media-items').length > 0 ) { + jQuery('.toggle').toggle(); + jQuery('.slidetoggle').slideUp(200).siblings().removeClass('hidden'); + } + + // Vanish it. + jQuery('.toggle', item).toggle(); + jQuery('.slidetoggle', item).slideUp(200).siblings().removeClass('hidden'); + item.css( {backgroundColor:'#faa'} ).animate( {backgroundColor:'#f4f4f4'}, {queue:false, duration:500} ).addClass('undo'); + + jQuery('.filename:empty', item).remove(); + jQuery('.filename .title', item).css('font-weight','bold'); + jQuery('.filename', item).append(' ' + pluploadL10n.deleted + ' ').siblings('a.toggle').hide(); + jQuery('.filename', item).append( jQuery('a.undo', item).removeClass('hidden') ); + jQuery('.menu_order_input', item).hide(); + + return; +} + +function deleteError() { + // TODO +} + +function uploadComplete() { + jQuery('#insert-gallery').prop('disabled', false); +} + +function switchUploader(s) { + if ( s ) { + deleteUserSetting('uploader'); + jQuery('.media-upload-form').removeClass('html-uploader'); + + if ( typeof(uploader) == 'object' ) + uploader.refresh(); + } else { + setUserSetting('uploader', '1'); // 1 == html uploader + jQuery('.media-upload-form').addClass('html-uploader'); + } +} + +function uploadError(fileObj, errorCode, message, uploader) { + var hundredmb = 100 * 1024 * 1024, max; + + switch (errorCode) { + case plupload.FAILED: + wpFileError(fileObj, pluploadL10n.upload_failed); + break; + case plupload.FILE_EXTENSION_ERROR: + wpFileError(fileObj, pluploadL10n.invalid_filetype); + break; + case plupload.FILE_SIZE_ERROR: + uploadSizeError(uploader, fileObj); + break; + case plupload.IMAGE_FORMAT_ERROR: + wpFileError(fileObj, pluploadL10n.not_an_image); + break; + case plupload.IMAGE_MEMORY_ERROR: + wpFileError(fileObj, pluploadL10n.image_memory_exceeded); + break; + case plupload.IMAGE_DIMENSIONS_ERROR: + wpFileError(fileObj, pluploadL10n.image_dimensions_exceeded); + break; + case plupload.GENERIC_ERROR: + wpQueueError(pluploadL10n.upload_failed); + break; + case plupload.IO_ERROR: + max = parseInt( uploader.settings.filters.max_file_size, 10 ); + + if ( max > hundredmb && fileObj.size > hundredmb ) + wpFileError( fileObj, pluploadL10n.big_upload_failed.replace('%1$s', '').replace('%2$s', '') ); + else + wpQueueError(pluploadL10n.io_error); + break; + case plupload.HTTP_ERROR: + wpQueueError(pluploadL10n.http_error); + break; + case plupload.INIT_ERROR: + jQuery('.media-upload-form').addClass('html-uploader'); + break; + case plupload.SECURITY_ERROR: + wpQueueError(pluploadL10n.security_error); + break; +/* case plupload.UPLOAD_ERROR.UPLOAD_STOPPED: + case plupload.UPLOAD_ERROR.FILE_CANCELLED: + jQuery('#media-item-' + fileObj.id).remove(); + break;*/ + default: + wpFileError(fileObj, pluploadL10n.default_error); + } +} + +function uploadSizeError( up, file, over100mb ) { + var message; + + if ( over100mb ) + message = pluploadL10n.big_upload_queued.replace('%s', file.name) + ' ' + pluploadL10n.big_upload_failed.replace('%1$s', '').replace('%2$s', ''); + else + message = pluploadL10n.file_exceeds_size_limit.replace('%s', file.name); + + jQuery('#media-items').append('

        ' + message + '

        '); + up.removeFile(file); +} + +jQuery(document).ready(function($){ + $('.media-upload-form').bind('click.uploader', function(e) { + var target = $(e.target), tr, c; + + if ( target.is('input[type="radio"]') ) { // remember the last used image size and alignment + tr = target.closest('tr'); + + if ( tr.hasClass('align') ) + setUserSetting('align', target.val()); + else if ( tr.hasClass('image-size') ) + setUserSetting('imgsize', target.val()); + + } else if ( target.is('button.button') ) { // remember the last used image link url + c = e.target.className || ''; + c = c.match(/url([^ '"]+)/); + + if ( c && c[1] ) { + setUserSetting('urlbutton', c[1]); + target.siblings('.urlfield').val( target.data('link-url') ); + } + } else if ( target.is('a.dismiss') ) { + target.parents('.media-item').fadeOut(200, function(){ + $(this).remove(); + }); + } else if ( target.is('.upload-flash-bypass a') || target.is('a.uploader-html') ) { // switch uploader to html4 + $('#media-items, p.submit, span.big-file-warning').css('display', 'none'); + switchUploader(0); + e.preventDefault(); + } else if ( target.is('.upload-html-bypass a') ) { // switch uploader to multi-file + $('#media-items, p.submit, span.big-file-warning').css('display', ''); + switchUploader(1); + e.preventDefault(); + } else if ( target.is('a.describe-toggle-on') ) { // Show + target.parent().addClass('open'); + target.siblings('.slidetoggle').fadeIn(250, function(){ + var S = $(window).scrollTop(), H = $(window).height(), top = $(this).offset().top, h = $(this).height(), b, B; + + if ( H && top && h ) { + b = top + h; + B = S + H; + + if ( b > B ) { + if ( b - B < top - S ) + window.scrollBy(0, (b - B) + 10); + else + window.scrollBy(0, top - S - 40); + } + } + }); + e.preventDefault(); + } else if ( target.is('a.describe-toggle-off') ) { // Hide + target.siblings('.slidetoggle').fadeOut(250, function(){ + target.parent().removeClass('open'); + }); + e.preventDefault(); + } + }); + + // init and set the uploader + uploader_init = function() { + var isIE = navigator.userAgent.indexOf('Trident/') != -1 || navigator.userAgent.indexOf('MSIE ') != -1; + + // Make sure flash sends cookies (seems in IE it does whitout switching to urlstream mode) + if ( ! isIE && 'flash' === plupload.predictRuntime( wpUploaderInit ) && + ( ! wpUploaderInit.required_features || ! wpUploaderInit.required_features.hasOwnProperty( 'send_binary_string' ) ) ) { + + wpUploaderInit.required_features = wpUploaderInit.required_features || {}; + wpUploaderInit.required_features.send_binary_string = true; + } + + uploader = new plupload.Uploader(wpUploaderInit); + + $('#image_resize').bind('change', function() { + var arg = $(this).prop('checked'); + + setResize( arg ); + + if ( arg ) + setUserSetting('upload_resize', '1'); + else + deleteUserSetting('upload_resize'); + }); + + uploader.bind('Init', function(up) { + var uploaddiv = $('#plupload-upload-ui'); + + setResize( getUserSetting('upload_resize', false) ); + + if ( up.features.dragdrop && ! $(document.body).hasClass('mobile') ) { + uploaddiv.addClass('drag-drop'); + $('#drag-drop-area').bind('dragover.wp-uploader', function(){ // dragenter doesn't fire right :( + uploaddiv.addClass('drag-over'); + }).bind('dragleave.wp-uploader, drop.wp-uploader', function(){ + uploaddiv.removeClass('drag-over'); + }); + } else { + uploaddiv.removeClass('drag-drop'); + $('#drag-drop-area').unbind('.wp-uploader'); + } + + if ( up.runtime === 'html4' ) { + $('.upload-flash-bypass').hide(); + } + }); + + uploader.init(); + + uploader.bind('FilesAdded', function( up, files ) { + $('#media-upload-error').html(''); + uploadStart(); + + plupload.each( files, function( file ) { + fileQueued( file ); + }); + + up.refresh(); + up.start(); + }); + + uploader.bind('UploadFile', function(up, file) { + fileUploading(up, file); + }); + + uploader.bind('UploadProgress', function(up, file) { + uploadProgress(up, file); + }); + + uploader.bind('Error', function(up, err) { + uploadError(err.file, err.code, err.message, up); + up.refresh(); + }); + + uploader.bind('FileUploaded', function(up, file, response) { + uploadSuccess(file, response.response); + }); + + uploader.bind('UploadComplete', function() { + uploadComplete(); + }); + }; + + if ( typeof(wpUploaderInit) == 'object' ) { + uploader_init(); + } + +}); diff --git a/wp-includes/js/plupload/handlers.min.js b/wp-includes/js/plupload/handlers.min.js new file mode 100644 index 0000000..d61eca8 --- /dev/null +++ b/wp-includes/js/plupload/handlers.min.js @@ -0,0 +1 @@ +function fileQueued(a){jQuery(".media-blank").remove();var b=jQuery("#media-items").children(),c=post_id||0;1==b.length&&b.removeClass("open").find(".slidetoggle").slideUp(200),jQuery('
        ').attr("id","media-item-"+a.id).addClass("child-of-"+c).append('
        0%
        ',jQuery('
        ').text(" "+a.name)).appendTo(jQuery("#media-items")),jQuery("#insert-gallery").prop("disabled",!0)}function uploadStart(){try{"undefined"!=typeof topWin.tb_remove&&topWin.jQuery("#TB_overlay").unbind("click",topWin.tb_remove)}catch(a){}return!0}function uploadProgress(a,b){var c=jQuery("#media-item-"+b.id);jQuery(".bar",c).width(200*b.loaded/b.size),jQuery(".percent",c).html(b.percent+"%")}function fileUploading(a,b){var c=104857600,d=parseInt(a.settings.max_file_size,10);d>c&&b.size>c&&setTimeout(function(){b.status<3&&0===b.loaded&&(wpFileError(b,pluploadL10n.big_upload_failed.replace("%1$s",'').replace("%2$s","")),a.stop(),a.removeFile(b),a.start())},1e4)}function updateMediaForm(){var a=jQuery("#media-items").children();1==a.length?(a.addClass("open").find(".slidetoggle").show(),jQuery(".insert-gallery").hide()):a.length>1&&(a.removeClass("open"),jQuery(".insert-gallery").show()),a.not(".media-blank").length>0?jQuery(".savebutton").show():jQuery(".savebutton").hide()}function uploadSuccess(a,b){var c=jQuery("#media-item-"+a.id);return b=b.replace(/^
        (\d+)<\/pre>$/,"$1"),b.match(/media-upload-error|error-div/)?void c.html(b):(jQuery(".percent",c).html(pluploadL10n.crunching),prepareMediaItem(a,b),updateMediaForm(),void(post_id&&c.hasClass("child-of-"+post_id)&&jQuery("#attachments-count").text(1*jQuery("#attachments-count").text()+1)))}function setResize(a){a?window.resize_width&&window.resize_height?uploader.settings.resize={enabled:!0,width:window.resize_width,height:window.resize_height,quality:100}:uploader.settings.multipart_params.image_resize=!0:delete uploader.settings.multipart_params.image_resize}function prepareMediaItem(a,b){var c="undefined"==typeof shortform?1:2,d=jQuery("#media-item-"+a.id);2==c&&shortform>2&&(c=shortform);try{"undefined"!=typeof topWin.tb_remove&&topWin.jQuery("#TB_overlay").click(topWin.tb_remove)}catch(e){}isNaN(b)||!b?(d.append(b),prepareMediaItemInit(a)):d.load("async-upload.php",{attachment_id:b,fetch:c},function(){prepareMediaItemInit(a),updateMediaForm()})}function prepareMediaItemInit(a){var b=jQuery("#media-item-"+a.id);jQuery(".thumbnail",b).clone().attr("class","pinkynail toggle").prependTo(b),jQuery(".filename.original",b).replaceWith(jQuery(".filename.new",b)),jQuery("a.delete",b).click(function(){return jQuery.ajax({url:ajaxurl,type:"post",success:deleteSuccess,error:deleteError,id:a.id,data:{id:this.id.replace(/[^0-9]/g,""),action:"trash-post",_ajax_nonce:this.href.replace(/^.*wpnonce=/,"")}}),!1}),jQuery("a.undo",b).click(function(){return jQuery.ajax({url:ajaxurl,type:"post",id:a.id,data:{id:this.id.replace(/[^0-9]/g,""),action:"untrash-post",_ajax_nonce:this.href.replace(/^.*wpnonce=/,"")},success:function(){var b,c=jQuery("#media-item-"+a.id);(b=jQuery("#type-of-"+a.id).val())&&jQuery("#"+b+"-counter").text(jQuery("#"+b+"-counter").text()-0+1),post_id&&c.hasClass("child-of-"+post_id)&&jQuery("#attachments-count").text(jQuery("#attachments-count").text()-0+1),jQuery(".filename .trashnotice",c).remove(),jQuery(".filename .title",c).css("font-weight","normal"),jQuery("a.undo",c).addClass("hidden"),jQuery(".menu_order_input",c).show(),c.css({backgroundColor:"#ceb"}).animate({backgroundColor:"#fff"},{queue:!1,duration:500,complete:function(){jQuery(this).css({backgroundColor:""})}}).removeClass("undo")}}),!1}),jQuery("#media-item-"+a.id+".startopen").removeClass("startopen").addClass("open").find("slidetoggle").fadeIn()}function wpQueueError(a){jQuery("#media-upload-error").show().html('

        '+a+"

        ")}function wpFileError(a,b){itemAjaxError(a.id,b)}function itemAjaxError(a,b){var c=jQuery("#media-item-"+a),d=c.find(".filename").text(),e=c.data("last-err");e!=a&&c.html('
        '+pluploadL10n.dismiss+""+pluploadL10n.error_uploading.replace("%s",jQuery.trim(d))+" "+b+"
        ").data("last-err",a)}function deleteSuccess(a){var b,c,d;return"-1"==a?itemAjaxError(this.id,"You do not have permission. Has your session expired?"):"0"==a?itemAjaxError(this.id,"Could not be deleted. Has it been deleted already?"):(c=this.id,d=jQuery("#media-item-"+c),(b=jQuery("#type-of-"+c).val())&&jQuery("#"+b+"-counter").text(jQuery("#"+b+"-counter").text()-1),post_id&&d.hasClass("child-of-"+post_id)&&jQuery("#attachments-count").text(jQuery("#attachments-count").text()-1),1==jQuery("form.type-form #media-items").children().length&&jQuery(".hidden","#media-items").length>0&&(jQuery(".toggle").toggle(),jQuery(".slidetoggle").slideUp(200).siblings().removeClass("hidden")),jQuery(".toggle",d).toggle(),jQuery(".slidetoggle",d).slideUp(200).siblings().removeClass("hidden"),d.css({backgroundColor:"#faa"}).animate({backgroundColor:"#f4f4f4"},{queue:!1,duration:500}).addClass("undo"),jQuery(".filename:empty",d).remove(),jQuery(".filename .title",d).css("font-weight","bold"),jQuery(".filename",d).append(' '+pluploadL10n.deleted+" ").siblings("a.toggle").hide(),jQuery(".filename",d).append(jQuery("a.undo",d).removeClass("hidden")),void jQuery(".menu_order_input",d).hide())}function deleteError(){}function uploadComplete(){jQuery("#insert-gallery").prop("disabled",!1)}function switchUploader(a){a?(deleteUserSetting("uploader"),jQuery(".media-upload-form").removeClass("html-uploader"),"object"==typeof uploader&&uploader.refresh()):(setUserSetting("uploader","1"),jQuery(".media-upload-form").addClass("html-uploader"))}function uploadError(a,b,c,d){var e,f=104857600;switch(b){case plupload.FAILED:wpFileError(a,pluploadL10n.upload_failed);break;case plupload.FILE_EXTENSION_ERROR:wpFileError(a,pluploadL10n.invalid_filetype);break;case plupload.FILE_SIZE_ERROR:uploadSizeError(d,a);break;case plupload.IMAGE_FORMAT_ERROR:wpFileError(a,pluploadL10n.not_an_image);break;case plupload.IMAGE_MEMORY_ERROR:wpFileError(a,pluploadL10n.image_memory_exceeded);break;case plupload.IMAGE_DIMENSIONS_ERROR:wpFileError(a,pluploadL10n.image_dimensions_exceeded);break;case plupload.GENERIC_ERROR:wpQueueError(pluploadL10n.upload_failed);break;case plupload.IO_ERROR:e=parseInt(d.settings.filters.max_file_size,10),e>f&&a.size>f?wpFileError(a,pluploadL10n.big_upload_failed.replace("%1$s",'').replace("%2$s","")):wpQueueError(pluploadL10n.io_error);break;case plupload.HTTP_ERROR:wpQueueError(pluploadL10n.http_error);break;case plupload.INIT_ERROR:jQuery(".media-upload-form").addClass("html-uploader");break;case plupload.SECURITY_ERROR:wpQueueError(pluploadL10n.security_error);break;default:wpFileError(a,pluploadL10n.default_error)}}function uploadSizeError(a,b,c){var d;d=c?pluploadL10n.big_upload_queued.replace("%s",b.name)+" "+pluploadL10n.big_upload_failed.replace("%1$s",'').replace("%2$s",""):pluploadL10n.file_exceeds_size_limit.replace("%s",b.name),jQuery("#media-items").append('

        '+d+"

        "),a.removeFile(b)}var topWin=window.dialogArguments||opener||parent||top,uploader,uploader_init;jQuery(document).ready(function(a){a(".media-upload-form").bind("click.uploader",function(b){var c,d,e=a(b.target);e.is('input[type="radio"]')?(c=e.closest("tr"),c.hasClass("align")?setUserSetting("align",e.val()):c.hasClass("image-size")&&setUserSetting("imgsize",e.val())):e.is("button.button")?(d=b.target.className||"",d=d.match(/url([^ '"]+)/),d&&d[1]&&(setUserSetting("urlbutton",d[1]),e.siblings(".urlfield").val(e.data("link-url")))):e.is("a.dismiss")?e.parents(".media-item").fadeOut(200,function(){a(this).remove()}):e.is(".upload-flash-bypass a")||e.is("a.uploader-html")?(a("#media-items, p.submit, span.big-file-warning").css("display","none"),switchUploader(0),b.preventDefault()):e.is(".upload-html-bypass a")?(a("#media-items, p.submit, span.big-file-warning").css("display",""),switchUploader(1),b.preventDefault()):e.is("a.describe-toggle-on")?(e.parent().addClass("open"),e.siblings(".slidetoggle").fadeIn(250,function(){var b,c,d=a(window).scrollTop(),e=a(window).height(),f=a(this).offset().top,g=a(this).height();e&&f&&g&&(b=f+g,c=d+e,b>c&&(f-d>b-c?window.scrollBy(0,b-c+10):window.scrollBy(0,f-d-40)))}),b.preventDefault()):e.is("a.describe-toggle-off")&&(e.siblings(".slidetoggle").fadeOut(250,function(){e.parent().removeClass("open")}),b.preventDefault())}),uploader_init=function(){var b=-1!=navigator.userAgent.indexOf("Trident/")||-1!=navigator.userAgent.indexOf("MSIE ");b||"flash"!==plupload.predictRuntime(wpUploaderInit)||wpUploaderInit.required_features&&wpUploaderInit.required_features.hasOwnProperty("send_binary_string")||(wpUploaderInit.required_features=wpUploaderInit.required_features||{},wpUploaderInit.required_features.send_binary_string=!0),uploader=new plupload.Uploader(wpUploaderInit),a("#image_resize").bind("change",function(){var b=a(this).prop("checked");setResize(b),b?setUserSetting("upload_resize","1"):deleteUserSetting("upload_resize")}),uploader.bind("Init",function(b){var c=a("#plupload-upload-ui");setResize(getUserSetting("upload_resize",!1)),b.features.dragdrop&&!a(document.body).hasClass("mobile")?(c.addClass("drag-drop"),a("#drag-drop-area").bind("dragover.wp-uploader",function(){c.addClass("drag-over")}).bind("dragleave.wp-uploader, drop.wp-uploader",function(){c.removeClass("drag-over")})):(c.removeClass("drag-drop"),a("#drag-drop-area").unbind(".wp-uploader")),"html4"===b.runtime&&a(".upload-flash-bypass").hide()}),uploader.init(),uploader.bind("FilesAdded",function(b,c){a("#media-upload-error").html(""),uploadStart(),plupload.each(c,function(a){fileQueued(a)}),b.refresh(),b.start()}),uploader.bind("UploadFile",function(a,b){fileUploading(a,b)}),uploader.bind("UploadProgress",function(a,b){uploadProgress(a,b)}),uploader.bind("Error",function(a,b){uploadError(b.file,b.code,b.message,a),a.refresh()}),uploader.bind("FileUploaded",function(a,b,c){uploadSuccess(b,c.response)}),uploader.bind("UploadComplete",function(){uploadComplete()})},"object"==typeof wpUploaderInit&&uploader_init()}); \ No newline at end of file diff --git a/wp-includes/js/plupload/license.txt b/wp-includes/js/plupload/license.txt new file mode 100644 index 0000000..d511905 --- /dev/null +++ b/wp-includes/js/plupload/license.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/wp-includes/js/plupload/plupload.flash.swf b/wp-includes/js/plupload/plupload.flash.swf new file mode 100644 index 0000000..6493572 Binary files /dev/null and b/wp-includes/js/plupload/plupload.flash.swf differ diff --git a/wp-includes/js/plupload/plupload.full.min.js b/wp-includes/js/plupload/plupload.full.min.js new file mode 100644 index 0000000..69d6ad1 --- /dev/null +++ b/wp-includes/js/plupload/plupload.full.min.js @@ -0,0 +1,28 @@ +/** + * mOxie - multi-runtime File API & XMLHttpRequest L2 Polyfill + * v1.2.0 + * + * Copyright 2013, Moxiecode Systems AB + * Released under GPL License. + * + * License: http://www.plupload.com/license + * Contributing: http://www.plupload.com/contributing + * + * Date: 2014-01-16 + */ +!function(e,t){"use strict";function n(e,t){for(var n,i=[],r=0;r0&&n(o,function(n,o){n!==r&&(e(i[o])===e(n)&&~a(e(n),["array","object"])?t(i[o],n):i[o]=n)})}),i},n=function(e,t){var n,i,r,o;if(e){try{n=e.length}catch(a){n=o}if(n===o){for(i in e)if(e.hasOwnProperty(i)&&t(e[i],i)===!1)return}else for(r=0;n>r;r++)if(t(e[r],r)===!1)return}},i=function(t){var n;if(!t||"object"!==e(t))return!0;for(n in t)return!1;return!0},r=function(t,n){function i(r){"function"===e(t[r])&&t[r](function(e){++rn;n++)if(t[n]===e)return n}return-1},s=function(t,n){var i=[];"array"!==e(t)&&(t=[t]),"array"!==e(n)&&(n=[n]);for(var r in t)-1===a(t[r],n)&&i.push(t[r]);return i.length?i:!1},u=function(e,t){var i=[];return n(e,function(e){-1!==a(e,t)&&i.push(e)}),i.length?i:null},c=function(e){var t,n=[];for(t=0;ti;i++)n+=Math.floor(65535*Math.random()).toString(32);return(t||"o_")+n+(e++).toString(32)}}(),d=function(e){return e?String.prototype.trim?String.prototype.trim.call(e):e.toString().replace(/^\s*/,"").replace(/\s*$/,""):e},f=function(e){if("string"!=typeof e)return e;var t={t:1099511627776,g:1073741824,m:1048576,k:1024},n;return e=/^([0-9]+)([mgk]?)$/.exec(e.toLowerCase().replace(/[^0-9mkg]/g,"")),n=e[2],e=+e[1],t.hasOwnProperty(n)&&(e*=t[n]),e};return{guid:l,typeOf:e,extend:t,each:n,isEmptyObj:i,inSeries:r,inParallel:o,inArray:a,arrayDiff:s,arrayIntersect:u,toArray:c,trim:d,parseSizeStr:f}}),i(c,[u],function(e){var t={};return{addI18n:function(n){return e.extend(t,n)},translate:function(e){return t[e]||e},_:function(e){return this.translate(e)},sprintf:function(t){var n=[].slice.call(arguments,1);return t.replace(/%[a-z]/g,function(){var t=n.shift();return"undefined"!==e.typeOf(t)?t:""})}}}),i(l,[u,c],function(e,t){var n="application/msword,doc dot,application/pdf,pdf,application/pgp-signature,pgp,application/postscript,ps ai eps,application/rtf,rtf,application/vnd.ms-excel,xls xlb,application/vnd.ms-powerpoint,ppt pps pot,application/zip,zip,application/x-shockwave-flash,swf swfl,application/vnd.openxmlformats-officedocument.wordprocessingml.document,docx,application/vnd.openxmlformats-officedocument.wordprocessingml.template,dotx,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,xlsx,application/vnd.openxmlformats-officedocument.presentationml.presentation,pptx,application/vnd.openxmlformats-officedocument.presentationml.template,potx,application/vnd.openxmlformats-officedocument.presentationml.slideshow,ppsx,application/x-javascript,js,application/json,json,audio/mpeg,mp3 mpga mpega mp2,audio/x-wav,wav,audio/x-m4a,m4a,audio/ogg,oga ogg,audio/aiff,aiff aif,audio/flac,flac,audio/aac,aac,audio/ac3,ac3,audio/x-ms-wma,wma,image/bmp,bmp,image/gif,gif,image/jpeg,jpg jpeg jpe,image/photoshop,psd,image/png,png,image/svg+xml,svg svgz,image/tiff,tiff tif,text/plain,asc txt text diff log,text/html,htm html xhtml,text/css,css,text/csv,csv,text/rtf,rtf,video/mpeg,mpeg mpg mpe m2v,video/quicktime,qt mov,video/mp4,mp4,video/x-m4v,m4v,video/x-flv,flv,video/x-ms-wmv,wmv,video/avi,avi,video/webm,webm,video/3gpp,3gpp 3gp,video/3gpp2,3g2,video/vnd.rn-realvideo,rv,video/ogg,ogv,video/x-matroska,mkv,application/vnd.oasis.opendocument.formula-template,otf,application/octet-stream,exe",i={mimes:{},extensions:{},addMimeType:function(e){var t=e.split(/,/),n,i,r;for(n=0;ni;i++)if(e[i]!=t[i]){if(e[i]=u(e[i]),t[i]=u(t[i]),e[i]t[i]){o=1;break}}if(!n)return o;switch(n){case">":case"gt":return o>0;case">=":case"ge":return o>=0;case"<=":case"le":return 0>=o;case"==":case"=":case"eq":return 0===o;case"<>":case"!=":case"ne":return 0!==o;case"":case"<":case"lt":return 0>o;default:return null}}var n=function(e){var t="",n="?",i="function",r="undefined",o="object",a="major",s="model",u="name",c="type",l="vendor",d="version",f="architecture",p="console",h="mobile",m="tablet",g={has:function(e,t){return-1!==t.toLowerCase().indexOf(e.toLowerCase())},lowerize:function(e){return e.toLowerCase()}},v={rgx:function(){for(var t,n=0,a,s,u,c,l,d,f=arguments;n0?2==c.length?t[c[0]]=typeof c[1]==i?c[1].call(this,d):c[1]:3==c.length?t[c[0]]=typeof c[1]!==i||c[1].exec&&c[1].test?d?d.replace(c[1],c[2]):e:d?c[1].call(this,d,c[2]):e:4==c.length&&(t[c[0]]=d?c[3].call(this,d.replace(c[1],c[2])):e):t[c]=d?d:e;break}if(l)break}return t},str:function(t,i){for(var r in i)if(typeof i[r]===o&&i[r].length>0){for(var a=0;a=9)},use_data_uri_of:function(e){return t.use_data_uri&&33e3>e||t.use_data_uri_over32kb()},use_fileinput:function(){var e=document.createElement("input");return e.setAttribute("type","file"),!e.disabled}};return function(n){var i=[].slice.call(arguments);return i.shift(),"function"===e.typeOf(t[n])?t[n].apply(this,i):!!t[n]}}(),r={can:i,browser:n.browser.name,version:parseFloat(n.browser.major),os:n.os.name,osVersion:n.os.version,verComp:t,swf_url:"../flash/Moxie.swf",xap_url:"../silverlight/Moxie.xap",global_event_dispatcher:"moxie.core.EventTarget.instance.dispatchEvent"};return r.OS=r.os,r}),i(f,[d],function(e){var t=function(e){return"string"!=typeof e?e:document.getElementById(e)},n=function(e,t){if(!e.className)return!1;var n=new RegExp("(^|\\s+)"+t+"(\\s+|$)");return n.test(e.className)},i=function(e,t){n(e,t)||(e.className=e.className?e.className.replace(/\s+$/,"")+" "+t:t)},r=function(e,t){if(e.className){var n=new RegExp("(^|\\s+)"+t+"(\\s+|$)");e.className=e.className.replace(n,function(e,t,n){return" "===t&&" "===n?" ":""})}},o=function(e,t){return e.currentStyle?e.currentStyle[t]:window.getComputedStyle?window.getComputedStyle(e,null)[t]:void 0},a=function(t,n){function i(e){var t,n,i=0,r=0;return e&&(n=e.getBoundingClientRect(),t="CSS1Compat"===s.compatMode?s.documentElement:s.body,i=n.left+t.scrollLeft,r=n.top+t.scrollTop),{x:i,y:r}}var r=0,o=0,a,s=document,u,c;if(t=t,n=n||s.body,t&&t.getBoundingClientRect&&"IE"===e.browser&&(!s.documentMode||s.documentMode<8))return u=i(t),c=i(n),{x:u.x-c.x,y:u.y-c.y};for(a=t;a&&a!=n&&a.nodeType;)r+=a.offsetLeft||0,o+=a.offsetTop||0,a=a.offsetParent;for(a=t.parentNode;a&&a!=n&&a.nodeType;)r-=a.scrollLeft||0,o-=a.scrollTop||0,a=a.parentNode;return{x:r,y:o}},s=function(e){return{w:e.offsetWidth||e.clientWidth,h:e.offsetHeight||e.clientHeight}};return{get:t,hasClass:n,addClass:i,removeClass:r,getStyle:o,getPos:a,getSize:s}}),i(p,[u],function(e){function t(e,t){var n;for(n in e)if(e[n]===t)return n;return null}return{RuntimeError:function(){function n(e){this.code=e,this.name=t(i,e),this.message=this.name+": RuntimeError "+this.code}var i={NOT_INIT_ERR:1,NOT_SUPPORTED_ERR:9,JS_ERR:4};return e.extend(n,i),n.prototype=Error.prototype,n}(),OperationNotAllowedException:function(){function t(e){this.code=e,this.name="OperationNotAllowedException"}return e.extend(t,{NOT_ALLOWED_ERR:1}),t.prototype=Error.prototype,t}(),ImageError:function(){function n(e){this.code=e,this.name=t(i,e),this.message=this.name+": ImageError "+this.code}var i={WRONG_FORMAT:1,MAX_RESOLUTION_ERR:2};return e.extend(n,i),n.prototype=Error.prototype,n}(),FileException:function(){function n(e){this.code=e,this.name=t(i,e),this.message=this.name+": FileException "+this.code}var i={NOT_FOUND_ERR:1,SECURITY_ERR:2,ABORT_ERR:3,NOT_READABLE_ERR:4,ENCODING_ERR:5,NO_MODIFICATION_ALLOWED_ERR:6,INVALID_STATE_ERR:7,SYNTAX_ERR:8};return e.extend(n,i),n.prototype=Error.prototype,n}(),DOMException:function(){function n(e){this.code=e,this.name=t(i,e),this.message=this.name+": DOMException "+this.code}var i={INDEX_SIZE_ERR:1,DOMSTRING_SIZE_ERR:2,HIERARCHY_REQUEST_ERR:3,WRONG_DOCUMENT_ERR:4,INVALID_CHARACTER_ERR:5,NO_DATA_ALLOWED_ERR:6,NO_MODIFICATION_ALLOWED_ERR:7,NOT_FOUND_ERR:8,NOT_SUPPORTED_ERR:9,INUSE_ATTRIBUTE_ERR:10,INVALID_STATE_ERR:11,SYNTAX_ERR:12,INVALID_MODIFICATION_ERR:13,NAMESPACE_ERR:14,INVALID_ACCESS_ERR:15,VALIDATION_ERR:16,TYPE_MISMATCH_ERR:17,SECURITY_ERR:18,NETWORK_ERR:19,ABORT_ERR:20,URL_MISMATCH_ERR:21,QUOTA_EXCEEDED_ERR:22,TIMEOUT_ERR:23,INVALID_NODE_TYPE_ERR:24,DATA_CLONE_ERR:25};return e.extend(n,i),n.prototype=Error.prototype,n}(),EventException:function(){function t(e){this.code=e,this.name="EventException"}return e.extend(t,{UNSPECIFIED_EVENT_TYPE_ERR:0}),t.prototype=Error.prototype,t}()}}),i(h,[p,u],function(e,t){function n(){var n={};t.extend(this,{uid:null,init:function(){this.uid||(this.uid=t.guid("uid_"))},addEventListener:function(e,i,r,o){var a=this,s;return e=t.trim(e),/\s/.test(e)?(t.each(e.split(/\s+/),function(e){a.addEventListener(e,i,r,o)}),void 0):(e=e.toLowerCase(),r=parseInt(r,10)||0,s=n[this.uid]&&n[this.uid][e]||[],s.push({fn:i,priority:r,scope:o||this}),n[this.uid]||(n[this.uid]={}),n[this.uid][e]=s,void 0)},hasEventListener:function(e){return e?!(!n[this.uid]||!n[this.uid][e]):!!n[this.uid]},removeEventListener:function(e,i){e=e.toLowerCase();var r=n[this.uid]&&n[this.uid][e],o;if(r){if(i){for(o=r.length-1;o>=0;o--)if(r[o].fn===i){r.splice(o,1);break}}else r=[];r.length||(delete n[this.uid][e],t.isEmptyObj(n[this.uid])&&delete n[this.uid])}},removeAllEventListeners:function(){n[this.uid]&&delete n[this.uid]},dispatchEvent:function(i){var r,o,a,s,u={},c=!0,l;if("string"!==t.typeOf(i)){if(s=i,"string"!==t.typeOf(s.type))throw new e.EventException(e.EventException.UNSPECIFIED_EVENT_TYPE_ERR);i=s.type,s.total!==l&&s.loaded!==l&&(u.total=s.total,u.loaded=s.loaded),u.async=s.async||!1}if(-1!==i.indexOf("::")?function(e){r=e[0],i=e[1]}(i.split("::")):r=this.uid,i=i.toLowerCase(),o=n[r]&&n[r][i]){o.sort(function(e,t){return t.priority-e.priority}),a=[].slice.call(arguments),a.shift(),u.type=i,a.unshift(u);var d=[];t.each(o,function(e){a[0].target=e.scope,u.async?d.push(function(t){setTimeout(function(){t(e.fn.apply(e.scope,a)===!1)},1)}):d.push(function(t){t(e.fn.apply(e.scope,a)===!1)})}),d.length&&t.inSeries(d,function(e){c=!e})}return c},bind:function(){this.addEventListener.apply(this,arguments)},unbind:function(){this.removeEventListener.apply(this,arguments)},unbindAll:function(){this.removeAllEventListeners.apply(this,arguments)},trigger:function(){return this.dispatchEvent.apply(this,arguments)},convertEventPropsToHandlers:function(e){var n;"array"!==t.typeOf(e)&&(e=[e]);for(var i=0;i>16,o=255&d>>8,a=255&d,m[p++]=64==c?String.fromCharCode(r):64==l?String.fromCharCode(r,o):String.fromCharCode(r,o,a);while(f>18,u=63&d>>12,c=63&d>>6,l=63&d,m[p++]=i.charAt(s)+i.charAt(u)+i.charAt(c)+i.charAt(l);while(fa;a++)o+=String.fromCharCode(r[a]);return o}}t.call(this),e.extend(this,{uid:e.guid("uid_"),readAsBinaryString:function(e){return i.call(this,"readAsBinaryString",e)},readAsDataURL:function(e){return i.call(this,"readAsDataURL",e)},readAsText:function(e){return i.call(this,"readAsText",e)}})}}),i(S,[p,u,y],function(e,t,n){function i(){var e,i=[];t.extend(this,{append:function(r,o){var a=this,s=t.typeOf(o);o instanceof n?e={name:r,value:o}:"array"===s?(r+="[]",t.each(o,function(e){a.append(r,e)})):"object"===s?t.each(o,function(e,t){a.append(r+"["+t+"]",e)}):"null"===s||"undefined"===s||"number"===s&&isNaN(o)?a.append(r,"false"):i.push({name:r,value:o.toString()})},hasBlob:function(){return!!this.getBlob()},getBlob:function(){return e&&e.value||null},getBlobName:function(){return e&&e.name||null},each:function(n){t.each(i,function(e){n(e.value,e.name)}),e&&n(e.value,e.name)},destroy:function(){e=null,i=[]}})}return i}),i(A,[u,p,h,m,b,g,x,y,T,S,d,l],function(e,t,n,i,r,o,a,s,u,c,l,d){function f(){this.uid=e.guid("uid_")}function p(){function n(e,t){return y.hasOwnProperty(e)?1===arguments.length?l.can("define_property")?y[e]:v[e]:(l.can("define_property")?y[e]=t:v[e]=t,void 0):void 0}function u(t){function i(){k.destroy(),k=null,s.dispatchEvent("loadend"),s=null}function r(r){k.bind("LoadStart",function(e){n("readyState",p.LOADING),s.dispatchEvent("readystatechange"),s.dispatchEvent(e),I&&s.upload.dispatchEvent(e)}),k.bind("Progress",function(e){n("readyState")!==p.LOADING&&(n("readyState",p.LOADING),s.dispatchEvent("readystatechange")),s.dispatchEvent(e)}),k.bind("UploadProgress",function(e){I&&s.upload.dispatchEvent({type:"progress",lengthComputable:!1,total:e.total,loaded:e.loaded})}),k.bind("Load",function(t){n("readyState",p.DONE),n("status",Number(r.exec.call(k,"XMLHttpRequest","getStatus")||0)),n("statusText",h[n("status")]||""),n("response",r.exec.call(k,"XMLHttpRequest","getResponse",n("responseType"))),~e.inArray(n("responseType"),["text",""])?n("responseText",n("response")):"document"===n("responseType")&&n("responseXML",n("response")),U=r.exec.call(k,"XMLHttpRequest","getAllResponseHeaders"),s.dispatchEvent("readystatechange"),n("status")>0?(I&&s.upload.dispatchEvent(t),s.dispatchEvent(t)):(N=!0,s.dispatchEvent("error")),i()}),k.bind("Abort",function(e){s.dispatchEvent(e),i()}),k.bind("Error",function(e){N=!0,n("readyState",p.DONE),s.dispatchEvent("readystatechange"),D=!0,s.dispatchEvent(e),i()}),r.exec.call(k,"XMLHttpRequest","send",{url:E,method:_,async:w,user:R,password:b,headers:x,mimeType:S,encoding:T,responseType:s.responseType,withCredentials:s.withCredentials,options:P},t)}var s=this;M=(new Date).getTime(),k=new a,"string"==typeof P.required_caps&&(P.required_caps=o.parseCaps(P.required_caps)),P.required_caps=e.extend({},P.required_caps,{return_response_type:s.responseType}),t instanceof c&&(P.required_caps.send_multipart=!0),L||(P.required_caps.do_cors=!0),P.ruid?r(k.connectRuntime(P)):(k.bind("RuntimeInit",function(e,t){r(t)}),k.bind("RuntimeError",function(e,t){s.dispatchEvent("RuntimeError",t)}),k.connectRuntime(P))}function g(){n("responseText",""),n("responseXML",null),n("response",null),n("status",0),n("statusText",""),M=C=null}var v=this,y={timeout:0,readyState:p.UNSENT,withCredentials:!1,status:0,statusText:"",responseType:"",responseXML:null,responseText:null,response:null},w=!0,E,_,x={},R,b,T=null,S=null,A=!1,O=!1,I=!1,D=!1,N=!1,L=!1,M,C,F=null,H=null,P={},k,U="",B;e.extend(this,y,{uid:e.guid("uid_"),upload:new f,open:function(o,a,s,u,c){var l;if(!o||!a)throw new t.DOMException(t.DOMException.SYNTAX_ERR);if(/[\u0100-\uffff]/.test(o)||i.utf8_encode(o)!==o)throw new t.DOMException(t.DOMException.SYNTAX_ERR);if(~e.inArray(o.toUpperCase(),["CONNECT","DELETE","GET","HEAD","OPTIONS","POST","PUT","TRACE","TRACK"])&&(_=o.toUpperCase()),~e.inArray(_,["CONNECT","TRACE","TRACK"]))throw new t.DOMException(t.DOMException.SECURITY_ERR);if(a=i.utf8_encode(a),l=r.parseUrl(a),L=r.hasSameOrigin(l),E=r.resolveUrl(a),(u||c)&&!L)throw new t.DOMException(t.DOMException.INVALID_ACCESS_ERR);if(R=u||l.user,b=c||l.pass,w=s||!0,w===!1&&(n("timeout")||n("withCredentials")||""!==n("responseType")))throw new t.DOMException(t.DOMException.INVALID_ACCESS_ERR);A=!w,O=!1,x={},g.call(this),n("readyState",p.OPENED),this.convertEventPropsToHandlers(["readystatechange"]),this.dispatchEvent("readystatechange")},setRequestHeader:function(r,o){var a=["accept-charset","accept-encoding","access-control-request-headers","access-control-request-method","connection","content-length","cookie","cookie2","content-transfer-encoding","date","expect","host","keep-alive","origin","referer","te","trailer","transfer-encoding","upgrade","user-agent","via"];if(n("readyState")!==p.OPENED||O)throw new t.DOMException(t.DOMException.INVALID_STATE_ERR);if(/[\u0100-\uffff]/.test(r)||i.utf8_encode(r)!==r)throw new t.DOMException(t.DOMException.SYNTAX_ERR);return r=e.trim(r).toLowerCase(),~e.inArray(r,a)||/^(proxy\-|sec\-)/.test(r)?!1:(x[r]?x[r]+=", "+o:x[r]=o,!0)},getAllResponseHeaders:function(){return U||""},getResponseHeader:function(t){return t=t.toLowerCase(),N||~e.inArray(t,["set-cookie","set-cookie2"])?null:U&&""!==U&&(B||(B={},e.each(U.split(/\r\n/),function(t){var n=t.split(/:\s+/);2===n.length&&(n[0]=e.trim(n[0]),B[n[0].toLowerCase()]={header:n[0],value:e.trim(n[1])})})),B.hasOwnProperty(t))?B[t].header+": "+B[t].value:null},overrideMimeType:function(i){var r,o;if(~e.inArray(n("readyState"),[p.LOADING,p.DONE]))throw new t.DOMException(t.DOMException.INVALID_STATE_ERR);if(i=e.trim(i.toLowerCase()),/;/.test(i)&&(r=i.match(/^([^;]+)(?:;\scharset\=)?(.*)$/))&&(i=r[1],r[2]&&(o=r[2])),!d.mimes[i])throw new t.DOMException(t.DOMException.SYNTAX_ERR);F=i,H=o},send:function(n,r){if(P="string"===e.typeOf(r)?{ruid:r}:r?r:{},this.convertEventPropsToHandlers(m),this.upload.convertEventPropsToHandlers(m),this.readyState!==p.OPENED||O)throw new t.DOMException(t.DOMException.INVALID_STATE_ERR);if(n instanceof s)P.ruid=n.ruid,S=n.type||"application/octet-stream";else if(n instanceof c){if(n.hasBlob()){var o=n.getBlob();P.ruid=o.ruid,S=o.type||"application/octet-stream"}}else"string"==typeof n&&(T="UTF-8",S="text/plain;charset=UTF-8",n=i.utf8_encode(n));this.withCredentials||(this.withCredentials=P.required_caps&&P.required_caps.send_browser_cookies&&!L),I=!A&&this.upload.hasEventListener(),N=!1,D=!n,A||(O=!0),u.call(this,n)},abort:function(){if(N=!0,A=!1,~e.inArray(n("readyState"),[p.UNSENT,p.OPENED,p.DONE]))n("readyState",p.UNSENT);else{if(n("readyState",p.DONE),O=!1,!k)throw new t.DOMException(t.DOMException.INVALID_STATE_ERR);k.getRuntime().exec.call(k,"XMLHttpRequest","abort",D),D=!0}},destroy:function(){k&&("function"===e.typeOf(k.destroy)&&k.destroy(),k=null),this.unbindAll(),this.upload&&(this.upload.unbindAll(),this.upload=null)}})}var h={100:"Continue",101:"Switching Protocols",102:"Processing",200:"OK",201:"Created",202:"Accepted",203:"Non-Authoritative Information",204:"No Content",205:"Reset Content",206:"Partial Content",207:"Multi-Status",226:"IM Used",300:"Multiple Choices",301:"Moved Permanently",302:"Found",303:"See Other",304:"Not Modified",305:"Use Proxy",306:"Reserved",307:"Temporary Redirect",400:"Bad Request",401:"Unauthorized",402:"Payment Required",403:"Forbidden",404:"Not Found",405:"Method Not Allowed",406:"Not Acceptable",407:"Proxy Authentication Required",408:"Request Timeout",409:"Conflict",410:"Gone",411:"Length Required",412:"Precondition Failed",413:"Request Entity Too Large",414:"Request-URI Too Long",415:"Unsupported Media Type",416:"Requested Range Not Satisfiable",417:"Expectation Failed",422:"Unprocessable Entity",423:"Locked",424:"Failed Dependency",426:"Upgrade Required",500:"Internal Server Error",501:"Not Implemented",502:"Bad Gateway",503:"Service Unavailable",504:"Gateway Timeout",505:"HTTP Version Not Supported",506:"Variant Also Negotiates",507:"Insufficient Storage",510:"Not Extended"};f.prototype=n.instance;var m=["loadstart","progress","abort","error","load","timeout","loadend"],g=1,v=2;return p.UNSENT=0,p.OPENED=1,p.HEADERS_RECEIVED=2,p.LOADING=3,p.DONE=4,p.prototype=n.instance,p}),i(O,[u,m,v,h],function(e,t,n,i){function r(){function i(){l=d=0,c=this.result=null}function o(t,n){var i=this;u=n,i.bind("TransportingProgress",function(t){d=t.loaded,l>d&&-1===e.inArray(i.state,[r.IDLE,r.DONE])&&a.call(i)},999),i.bind("TransportingComplete",function(){d=l,i.state=r.DONE,c=null,i.result=u.exec.call(i,"Transporter","getAsBlob",t||"")},999),i.state=r.BUSY,i.trigger("TransportingStarted"),a.call(i)}function a(){var e=this,n,i=l-d;f>i&&(f=i),n=t.btoa(c.substr(d,f)),u.exec.call(e,"Transporter","receive",n,l)}var s,u,c,l,d,f;n.call(this),e.extend(this,{uid:e.guid("uid_"),state:r.IDLE,result:null,transport:function(t,n,r){var a=this;if(r=e.extend({chunk_size:204798},r),(s=r.chunk_size%3)&&(r.chunk_size+=3-s),f=r.chunk_size,i.call(this),c=t,l=t.length,"string"===e.typeOf(r)||r.ruid)o.call(a,n,this.connectRuntime(r));else{var u=function(e,t){a.unbind("RuntimeInit",u),o.call(a,n,t)};this.bind("RuntimeInit",u),this.connectRuntime(r)}},abort:function(){var e=this;e.state=r.IDLE,u&&(u.exec.call(e,"Transporter","clear"),e.trigger("TransportingAborted")),i.call(e)},destroy:function(){this.unbindAll(),u=null,this.disconnectRuntime(),i.call(this)}})}return r.IDLE=0,r.BUSY=1,r.DONE=2,r.prototype=i.instance,r}),i(I,[u,f,p,T,A,g,v,O,d,h,y,w,m],function(e,t,n,i,r,o,a,s,u,c,l,d,f){function p(){function i(e){e||(e=this.getRuntime().exec.call(this,"Image","getInfo")),this.size=e.size,this.width=e.width,this.height=e.height,this.type=e.type,this.meta=e.meta,""===this.name&&(this.name=e.name)}function c(t){var i=e.typeOf(t);try{if(t instanceof p){if(!t.size)throw new n.DOMException(n.DOMException.INVALID_STATE_ERR);m.apply(this,arguments)}else if(t instanceof l){if(!~e.inArray(t.type,["image/jpeg","image/png"]))throw new n.ImageError(n.ImageError.WRONG_FORMAT);g.apply(this,arguments)}else if(-1!==e.inArray(i,["blob","file"]))c.call(this,new d(null,t),arguments[1]);else if("string"===i)/^data:[^;]*;base64,/.test(t)?c.call(this,new l(null,{data:t}),arguments[1]):v.apply(this,arguments);else{if("node"!==i||"img"!==t.nodeName.toLowerCase())throw new n.DOMException(n.DOMException.TYPE_MISMATCH_ERR);c.call(this,t.src,arguments[1])}}catch(r){this.trigger("error",r)}}function m(t,n){var i=this.connectRuntime(t.ruid);this.ruid=i.uid,i.exec.call(this,"Image","loadFromImage",t,"undefined"===e.typeOf(n)?!0:n)}function g(t,n){function i(e){r.ruid=e.uid,e.exec.call(r,"Image","loadFromBlob",t)}var r=this;r.name=t.name||"",t.isDetached()?(this.bind("RuntimeInit",function(e,t){i(t)}),n&&"string"==typeof n.required_caps&&(n.required_caps=o.parseCaps(n.required_caps)),this.connectRuntime(e.extend({required_caps:{access_image_binary:!0,resize_image:!0}},n))):i(this.connectRuntime(t.ruid))}function v(e,t){var n=this,i;i=new r,i.open("get",e),i.responseType="blob",i.onprogress=function(e){n.trigger(e)},i.onload=function(){g.call(n,i.response,!0)},i.onerror=function(e){n.trigger(e)},i.onloadend=function(){i.destroy()},i.bind("RuntimeError",function(e,t){n.trigger("RuntimeError",t)}),i.send(null,t)}a.call(this),e.extend(this,{uid:e.guid("uid_"),ruid:null,name:"",size:0,width:0,height:0,type:"",meta:{},clone:function(){this.load.apply(this,arguments)},load:function(){this.bind("Load Resize",function(){i.call(this)},999),this.convertEventPropsToHandlers(h),c.apply(this,arguments)},downsize:function(t,i,r,o){try{if(!this.size)throw new n.DOMException(n.DOMException.INVALID_STATE_ERR);if(this.width>p.MAX_RESIZE_WIDTH||this.height>p.MAX_RESIZE_HEIGHT)throw new n.ImageError(n.ImageError.MAX_RESOLUTION_ERR);(!t&&!i||"undefined"===e.typeOf(r))&&(r=!1),t=t||this.width,i=i||this.height,o="undefined"===e.typeOf(o)?!0:!!o,this.getRuntime().exec.call(this,"Image","downsize",t,i,r,o)}catch(a){this.trigger("error",a)}},crop:function(e,t,n){this.downsize(e,t,!0,n)},getAsCanvas:function(){if(!u.can("create_canvas"))throw new n.RuntimeError(n.RuntimeError.NOT_SUPPORTED_ERR);var e=this.connectRuntime(this.ruid);return e.exec.call(this,"Image","getAsCanvas")},getAsBlob:function(e,t){if(!this.size)throw new n.DOMException(n.DOMException.INVALID_STATE_ERR);return e||(e="image/jpeg"),"image/jpeg"!==e||t||(t=90),this.getRuntime().exec.call(this,"Image","getAsBlob",e,t)},getAsDataURL:function(e,t){if(!this.size)throw new n.DOMException(n.DOMException.INVALID_STATE_ERR);return this.getRuntime().exec.call(this,"Image","getAsDataURL",e,t)},getAsBinaryString:function(e,t){var n=this.getAsDataURL(e,t);return f.atob(n.substring(n.indexOf("base64,")+7))},embed:function(i){function r(){if(u.can("create_canvas")){var t=a.getAsCanvas();if(t)return i.appendChild(t),t=null,a.destroy(),o.trigger("embedded"),void 0}var r=a.getAsDataURL(c,l);if(!r)throw new n.ImageError(n.ImageError.WRONG_FORMAT);if(u.can("use_data_uri_of",r.length))i.innerHTML='',a.destroy(),o.trigger("embedded");else{var d=new s;d.bind("TransportingComplete",function(){v=o.connectRuntime(this.result.ruid),o.bind("Embedded",function(){e.extend(v.getShimContainer().style,{top:"0px",left:"0px",width:a.width+"px",height:a.height+"px"}),v=null},999),v.exec.call(o,"ImageView","display",this.result.uid,m,g),a.destroy()}),d.transport(f.atob(r.substring(r.indexOf("base64,")+7)),c,e.extend({},h,{required_caps:{display_media:!0},runtime_order:"flash,silverlight",container:i}))}}var o=this,a,c,l,d,h=arguments[1]||{},m=this.width,g=this.height,v;try{if(!(i=t.get(i)))throw new n.DOMException(n.DOMException.INVALID_NODE_TYPE_ERR);if(!this.size)throw new n.DOMException(n.DOMException.INVALID_STATE_ERR);if(this.width>p.MAX_RESIZE_WIDTH||this.height>p.MAX_RESIZE_HEIGHT)throw new n.ImageError(n.ImageError.MAX_RESOLUTION_ERR);if(c=h.type||this.type||"image/jpeg",l=h.quality||90,d="undefined"!==e.typeOf(h.crop)?h.crop:!1,h.width)m=h.width,g=h.height||m;else{var y=t.getSize(i);y.w&&y.h&&(m=y.w,g=y.h)}return a=new p,a.bind("Resize",function(){r.call(o)}),a.bind("Load",function(){a.downsize(m,g,d,!1)}),a.clone(this,!1),a}catch(w){this.trigger("error",w)}},destroy:function(){this.ruid&&(this.getRuntime().exec.call(this,"Image","destroy"),this.disconnectRuntime()),this.unbindAll()}})}var h=["progress","load","error","resize","embedded"];return p.MAX_RESIZE_WIDTH=6500,p.MAX_RESIZE_HEIGHT=6500,p.prototype=c.instance,p}),i(D,[u,p,g,d],function(e,t,n,i){function r(t){var r=this,s=n.capTest,u=n.capTrue,c=e.extend({access_binary:s(window.FileReader||window.File&&window.File.getAsDataURL),access_image_binary:function(){return r.can("access_binary")&&!!a.Image},display_media:s(i.can("create_canvas")||i.can("use_data_uri_over32kb")),do_cors:s(window.XMLHttpRequest&&"withCredentials"in new XMLHttpRequest),drag_and_drop:s(function(){var e=document.createElement("div");return("draggable"in e||"ondragstart"in e&&"ondrop"in e)&&("IE"!==i.browser||i.version>9)}()),filter_by_extension:s(function(){return"Chrome"===i.browser&&i.version>=28||"IE"===i.browser&&i.version>=10}()),return_response_headers:u,return_response_type:function(e){return"json"===e&&window.JSON?!0:i.can("return_response_type",e)},return_status_code:u,report_upload_progress:s(window.XMLHttpRequest&&(new XMLHttpRequest).upload),resize_image:function(){return r.can("access_binary")&&i.can("create_canvas")},select_file:function(){return i.can("use_fileinput")&&window.File},select_folder:function(){return r.can("select_file")&&"Chrome"===i.browser&&i.version>=21},select_multiple:function(){return!(!r.can("select_file")||"Safari"===i.browser&&"Windows"===i.os||"iOS"===i.os&&i.verComp(i.osVersion,"7.0.4","<"))},send_binary_string:s(window.XMLHttpRequest&&((new XMLHttpRequest).sendAsBinary||window.Uint8Array&&window.ArrayBuffer)),send_custom_headers:s(window.XMLHttpRequest),send_multipart:function(){return!!(window.XMLHttpRequest&&(new XMLHttpRequest).upload&&window.FormData)||r.can("send_binary_string")},slice_blob:s(window.File&&(File.prototype.mozSlice||File.prototype.webkitSlice||File.prototype.slice)),stream_upload:function(){return r.can("slice_blob")&&r.can("send_multipart")},summon_file_dialog:s(function(){return"Firefox"===i.browser&&i.version>=4||"Opera"===i.browser&&i.version>=12||"IE"===i.browser&&i.version>=10||!!~e.inArray(i.browser,["Chrome","Safari"])}()),upload_filesize:u},arguments[2]);n.call(this,t,arguments[1]||o,c),e.extend(this,{init:function(){this.trigger("Init")},destroy:function(e){return function(){e.call(r),e=r=null}}(this.destroy)}),e.extend(this.getShim(),a)}var o="html5",a={};return n.addConstructor(o,r),a}),i(N,[D,y],function(e,t){function n(){function e(e,t,n){var i;if(!window.File.prototype.slice)return(i=window.File.prototype.webkitSlice||window.File.prototype.mozSlice)?i.call(e,t,n):null;try{return e.slice(),e.slice(t,n)}catch(r){return e.slice(t,n-t)}}this.slice=function(){return new t(this.getRuntime().uid,e.apply(this,arguments))}}return e.Blob=n}),i(L,[u],function(e){function t(){this.returnValue=!1}function n(){this.cancelBubble=!0}var i={},r="moxie_"+e.guid(),o=function(o,a,s,u){var c,l;a=a.toLowerCase(),o.addEventListener?(c=s,o.addEventListener(a,c,!1)):o.attachEvent&&(c=function(){var e=window.event;e.target||(e.target=e.srcElement),e.preventDefault=t,e.stopPropagation=n,s(e)},o.attachEvent("on"+a,c)),o[r]||(o[r]=e.guid()),i.hasOwnProperty(o[r])||(i[o[r]]={}),l=i[o[r]],l.hasOwnProperty(a)||(l[a]=[]),l[a].push({func:c,orig:s,key:u})},a=function(t,n,o){var a,s;if(n=n.toLowerCase(),t[r]&&i[t[r]]&&i[t[r]][n]){a=i[t[r]][n];for(var u=a.length-1;u>=0&&(a[u].orig!==o&&a[u].key!==o||(t.removeEventListener?t.removeEventListener(n,a[u].func,!1):t.detachEvent&&t.detachEvent("on"+n,a[u].func),a[u].orig=null,a[u].func=null,a.splice(u,1),o===s));u--);if(a.length||delete i[t[r]][n],e.isEmptyObj(i[t[r]])){delete i[t[r]];try{delete t[r]}catch(c){t[r]=s}}}},s=function(t,n){t&&t[r]&&e.each(i[t[r]],function(e,i){a(t,i,n)})};return{addEvent:o,removeEvent:a,removeAllEvents:s}}),i(M,[D,u,f,L,l,d],function(e,t,n,i,r,o){function a(){var e=[],a;t.extend(this,{init:function(s){var u=this,c=u.getRuntime(),l,d,f,p,h,m;a=s,e=[],f=a.accept.mimes||r.extList2mimes(a.accept,c.can("filter_by_extension")),d=c.getShimContainer(),d.innerHTML='",l=n.get(c.uid),t.extend(l.style,{position:"absolute",top:0,left:0,width:"100%",height:"100%"}),p=n.get(a.browse_button),c.can("summon_file_dialog")&&("static"===n.getStyle(p,"position")&&(p.style.position="relative"),h=parseInt(n.getStyle(p,"z-index"),10)||1,p.style.zIndex=h,d.style.zIndex=h-1,i.addEvent(p,"click",function(e){var t=n.get(c.uid);t&&!t.disabled&&t.click(),e.preventDefault()},u.uid)),m=c.can("summon_file_dialog")?p:d,i.addEvent(m,"mouseover",function(){u.trigger("mouseenter")},u.uid),i.addEvent(m,"mouseout",function(){u.trigger("mouseleave")},u.uid),i.addEvent(m,"mousedown",function(){u.trigger("mousedown")},u.uid),i.addEvent(n.get(a.container),"mouseup",function(){u.trigger("mouseup")},u.uid),l.onchange=function g(){if(e=[],a.directory?t.each(this.files,function(t){"."!==t.name&&e.push(t)}):e=[].slice.call(this.files),"IE"!==o.browser)this.value="";else{var n=this.cloneNode(!0);this.parentNode.replaceChild(n,this),n.onchange=g}u.trigger("change")},u.trigger({type:"ready",async:!0}),d=null},getFiles:function(){return e},disable:function(e){var t=this.getRuntime(),i;(i=n.get(t.uid))&&(i.disabled=!!e)},destroy:function(){var t=this.getRuntime(),r=t.getShim(),o=t.getShimContainer();i.removeAllEvents(o,this.uid),i.removeAllEvents(a&&n.get(a.container),this.uid),i.removeAllEvents(a&&n.get(a.browse_button),this.uid),o&&(o.innerHTML=""),r.removeInstance(this.uid),e=a=o=r=null}})}return e.FileInput=a}),i(C,[D,u,f,L,l],function(e,t,n,i,r){function o(){function e(e){for(var n=[],i=0;i=4&&u.version<7,f="Android Browser"===u.browser,m=!1;if(h=n.url.replace(/^.+?\/([\w\-\.]+)$/,"$1").toLowerCase(),p=c(),p.open(n.method,n.url,n.async,n.user,n.password),r instanceof o)r.isDetached()&&(m=!0),r=r.getSource();else if(r instanceof a){if(r.hasBlob())if(r.getBlob().isDetached())r=d.call(s,r),m=!0;else if((l||f)&&"blob"===t.typeOf(r.getBlob().getSource())&&window.FileReader)return e.call(s,n,r),void 0;if(r instanceof a){var g=new window.FormData;r.each(function(e,t){e instanceof o?g.append(t,e.getSource()):g.append(t,e)}),r=g}}p.upload?(n.withCredentials&&(p.withCredentials=!0),p.addEventListener("load",function(e){s.trigger(e)}),p.addEventListener("error",function(e){s.trigger(e)}),p.addEventListener("progress",function(e){s.trigger(e)}),p.upload.addEventListener("progress",function(e){s.trigger({type:"UploadProgress",loaded:e.loaded,total:e.total})})):p.onreadystatechange=function v(){switch(p.readyState){case 1:break;case 2:break;case 3:var e,t;try{i.hasSameOrigin(n.url)&&(e=p.getResponseHeader("Content-Length")||0),p.responseText&&(t=p.responseText.length)}catch(r){e=t=0}s.trigger({type:"progress",lengthComputable:!!e,total:parseInt(e,10),loaded:t});break;case 4:p.onreadystatechange=function(){},0===p.status?s.trigger("error"):s.trigger("load")}},t.isEmptyObj(n.headers)||t.each(n.headers,function(e,t){p.setRequestHeader(t,e)}),""!==n.responseType&&"responseType"in p&&(p.responseType="json"!==n.responseType||u.can("return_response_type","json")?n.responseType:"text"),m?p.sendAsBinary?p.sendAsBinary(r):function(){for(var e=new Uint8Array(r.length),t=0;ta;a++)i|=o.charCodeAt(e+a)<s;s++)o+=String.fromCharCode(255&t>>Math.abs(a+8*s));n(o,e,i)}var r=!1,o;return{II:function(e){return e===t?r:(r=e,void 0)},init:function(e){r=!1,o=e},SEGMENT:function(e,t,i){switch(arguments.length){case 1:return o.substr(e,o.length-e-1);case 2:return o.substr(e,t);case 3:n(i,e,t);break;default:return o}},BYTE:function(t){return e(t,1)},SHORT:function(t){return e(t,2)},LONG:function(n,r){return r===t?e(n,4):(i(n,r,4),void 0)},SLONG:function(t){var n=e(t,4);return n>2147483647?n-4294967296:n},STRING:function(t,n){var i="";for(n+=t;n>t;t++)i+=String.fromCharCode(e(t,1));return i}}}}),i(k,[P],function(e){return function t(n){var i=[],r,o,a,s=0;if(r=new e,r.init(n),65496===r.SHORT(0)){for(o=2;o<=n.length;)if(a=r.SHORT(o),a>=65488&&65495>=a)o+=2;else{if(65498===a||65497===a)break;s=r.SHORT(o+2)+2,a>=65505&&65519>=a&&i.push({hex:a,name:"APP"+(15&a),start:o,length:s,segment:r.SEGMENT(o,s)}),o+=s}return r.init(null),{headers:i,restore:function(e){var t,n;for(r.init(e),o=65504==r.SHORT(2)?4+r.SHORT(4):2,n=0,t=i.length;t>n;n++)r.SEGMENT(o,0,i[n].segment),o+=i[n].length;return e=r.SEGMENT(),r.init(null),e},strip:function(e){var n,i,o;for(i=new t(e),n=i.headers,i.purge(),r.init(e),o=n.length;o--;)r.SEGMENT(n[o].start,n[o].length,"");return e=r.SEGMENT(),r.init(null),e},get:function(e){for(var t=[],n=0,r=i.length;r>n;n++)i[n].name===e.toUpperCase()&&t.push(i[n].segment);return t},set:function(e,t){var n=[],r,o,a;for("string"==typeof t?n.push(t):n=t,r=o=0,a=i.length;a>r&&(i[r].name===e.toUpperCase()&&(i[r].segment=n[o],i[r].length=n[o].length,o++),!(o>=n.length));r++);},purge:function(){i=[],r.init(null),r=null}}}}}),i(U,[u,P],function(e,n){return function i(){function i(e,n){var i=a.SHORT(e),r,o,s,u,d,f,p,h,m=[],g={};for(r=0;i>r;r++)if(p=f=e+12*r+2,s=n[a.SHORT(p)],s!==t){switch(u=a.SHORT(p+=2),d=a.LONG(p+=2),p+=4,m=[],u){case 1:case 7:for(d>4&&(p=a.LONG(p)+c.tiffHeader),o=0;d>o;o++)m[o]=a.BYTE(p+o);break;case 2:d>4&&(p=a.LONG(p)+c.tiffHeader),g[s]=a.STRING(p,d-1);continue;case 3:for(d>2&&(p=a.LONG(p)+c.tiffHeader),o=0;d>o;o++)m[o]=a.SHORT(p+2*o);break;case 4:for(d>1&&(p=a.LONG(p)+c.tiffHeader),o=0;d>o;o++)m[o]=a.LONG(p+4*o);break;case 5:for(p=a.LONG(p)+c.tiffHeader,o=0;d>o;o++)m[o]=a.LONG(p+4*o)/a.LONG(p+4*o+4);break;case 9:for(p=a.LONG(p)+c.tiffHeader,o=0;d>o;o++)m[o]=a.SLONG(p+4*o);break;case 10:for(p=a.LONG(p)+c.tiffHeader,o=0;d>o;o++)m[o]=a.SLONG(p+4*o)/a.SLONG(p+4*o+4);break;default:continue}h=1==d?m[0]:m,g[s]=l.hasOwnProperty(s)&&"object"!=typeof h?l[s][h]:h}return g}function r(){var e=c.tiffHeader;return a.II(18761==a.SHORT(e)),42!==a.SHORT(e+=2)?!1:(c.IFD0=c.tiffHeader+a.LONG(e+=2),u=i(c.IFD0,s.tiff),"ExifIFDPointer"in u&&(c.exifIFD=c.tiffHeader+u.ExifIFDPointer,delete u.ExifIFDPointer),"GPSInfoIFDPointer"in u&&(c.gpsIFD=c.tiffHeader+u.GPSInfoIFDPointer,delete u.GPSInfoIFDPointer),!0)}function o(e,t,n){var i,r,o,u=0;if("string"==typeof t){var l=s[e.toLowerCase()];for(var d in l)if(l[d]===t){t=d;break}}i=c[e.toLowerCase()+"IFD"],r=a.SHORT(i);for(var f=0;r>f;f++)if(o=i+12*f+2,a.SHORT(o)==t){u=o+8;break}return u?(a.LONG(u,n),!0):!1}var a,s,u,c={},l;return a=new n,s={tiff:{274:"Orientation",270:"ImageDescription",271:"Make",272:"Model",305:"Software",34665:"ExifIFDPointer",34853:"GPSInfoIFDPointer"},exif:{36864:"ExifVersion",40961:"ColorSpace",40962:"PixelXDimension",40963:"PixelYDimension",36867:"DateTimeOriginal",33434:"ExposureTime",33437:"FNumber",34855:"ISOSpeedRatings",37377:"ShutterSpeedValue",37378:"ApertureValue",37383:"MeteringMode",37384:"LightSource",37385:"Flash",37386:"FocalLength",41986:"ExposureMode",41987:"WhiteBalance",41990:"SceneCaptureType",41988:"DigitalZoomRatio",41992:"Contrast",41993:"Saturation",41994:"Sharpness"},gps:{0:"GPSVersionID",1:"GPSLatitudeRef",2:"GPSLatitude",3:"GPSLongitudeRef",4:"GPSLongitude"}},l={ColorSpace:{1:"sRGB",0:"Uncalibrated"},MeteringMode:{0:"Unknown",1:"Average",2:"CenterWeightedAverage",3:"Spot",4:"MultiSpot",5:"Pattern",6:"Partial",255:"Other"},LightSource:{1:"Daylight",2:"Fliorescent",3:"Tungsten",4:"Flash",9:"Fine weather",10:"Cloudy weather",11:"Shade",12:"Daylight fluorescent (D 5700 - 7100K)",13:"Day white fluorescent (N 4600 -5400K)",14:"Cool white fluorescent (W 3900 - 4500K)",15:"White fluorescent (WW 3200 - 3700K)",17:"Standard light A",18:"Standard light B",19:"Standard light C",20:"D55",21:"D65",22:"D75",23:"D50",24:"ISO studio tungsten",255:"Other"},Flash:{0:"Flash did not fire.",1:"Flash fired.",5:"Strobe return light not detected.",7:"Strobe return light detected.",9:"Flash fired, compulsory flash mode",13:"Flash fired, compulsory flash mode, return light not detected",15:"Flash fired, compulsory flash mode, return light detected",16:"Flash did not fire, compulsory flash mode",24:"Flash did not fire, auto mode",25:"Flash fired, auto mode",29:"Flash fired, auto mode, return light not detected",31:"Flash fired, auto mode, return light detected",32:"No flash function",65:"Flash fired, red-eye reduction mode",69:"Flash fired, red-eye reduction mode, return light not detected",71:"Flash fired, red-eye reduction mode, return light detected",73:"Flash fired, compulsory flash mode, red-eye reduction mode",77:"Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected",79:"Flash fired, compulsory flash mode, red-eye reduction mode, return light detected",89:"Flash fired, auto mode, red-eye reduction mode",93:"Flash fired, auto mode, return light not detected, red-eye reduction mode",95:"Flash fired, auto mode, return light detected, red-eye reduction mode"},ExposureMode:{0:"Auto exposure",1:"Manual exposure",2:"Auto bracket"},WhiteBalance:{0:"Auto white balance",1:"Manual white balance"},SceneCaptureType:{0:"Standard",1:"Landscape",2:"Portrait",3:"Night scene"},Contrast:{0:"Normal",1:"Soft",2:"Hard"},Saturation:{0:"Normal",1:"Low saturation",2:"High saturation"},Sharpness:{0:"Normal",1:"Soft",2:"Hard"},GPSLatitudeRef:{N:"North latitude",S:"South latitude"},GPSLongitudeRef:{E:"East longitude",W:"West longitude"}},{init:function(e){return c={tiffHeader:10},e!==t&&e.length?(a.init(e),65505===a.SHORT(0)&&"EXIF\0"===a.STRING(4,5).toUpperCase()?r():!1):!1 +},TIFF:function(){return u},EXIF:function(){var t;if(t=i(c.exifIFD,s.exif),t.ExifVersion&&"array"===e.typeOf(t.ExifVersion)){for(var n=0,r="";n=65472&&65475>=t)return e+=5,{height:c.SHORT(e),width:c.SHORT(e+=2)};n=c.SHORT(e+=2),e+=n-2}return null}function s(){d&&l&&c&&(d.purge(),l.purge(),c.init(null),u=f=l=d=c=null)}var u,c,l,d,f,p;if(u=o,c=new i,c.init(u),65496!==c.SHORT(0))throw new t.ImageError(t.ImageError.WRONG_FORMAT);l=new n(o),d=new r,p=!!d.init(l.get("app1")[0]),f=a.call(this),e.extend(this,{type:"image/jpeg",size:u.length,width:f&&f.width||0,height:f&&f.height||0,setExif:function(t,n){return p?("object"===e.typeOf(t)?e.each(t,function(e,t){d.setExif(t,e)}):d.setExif(t,n),l.set("app1",d.getBinary()),void 0):!1},writeHeaders:function(){return arguments.length?l.restore(arguments[0]):u=l.restore(u)},stripHeaders:function(e){return l.strip(e)},purge:function(){s.call(this)}}),p&&(this.meta={tiff:d.TIFF(),exif:d.EXIF(),gps:d.GPS()})}return o}),i(z,[p,u,P],function(e,t,n){function i(i){function r(){var e,t;return e=a.call(this,8),"IHDR"==e.type?(t=e.start,{width:u.LONG(t),height:u.LONG(t+=4)}):null}function o(){u&&(u.init(null),s=d=c=l=u=null)}function a(e){var t,n,i,r;return t=u.LONG(e),n=u.STRING(e+=4,4),i=e+=4,r=u.LONG(e+t),{length:t,type:n,start:i,CRC:r}}var s,u,c,l,d;s=i,u=new n,u.init(s),function(){var t=0,n=0,i=[35152,20039,3338,6666];for(n=0;ng;){for(var v=g+f>a?a-g:f,y=0;o>y;){var w=y+f>o?o-y:f;h.clearRect(0,0,f,f),h.drawImage(e,-y,-g);var E=y*s/o+c<<0,_=Math.ceil(w*s/o),x=g*u/a/m+l<<0,R=Math.ceil(v*u/a/m);d.drawImage(p,0,0,w,v,E,x,_,R),y+=f}g+=f}p=h=null}function t(e){var t=e.naturalWidth,n=e.naturalHeight;if(t*n>1048576){var i=document.createElement("canvas");i.width=i.height=1;var r=i.getContext("2d");return r.drawImage(e,-t+1,0),0===r.getImageData(0,0,1,1).data[3]}return!1}function n(e,t,n){var i=document.createElement("canvas");i.width=1,i.height=n;var r=i.getContext("2d");r.drawImage(e,0,0);for(var o=r.getImageData(0,0,1,n).data,a=0,s=n,u=n;u>a;){var c=o[4*(u-1)+3];0===c?s=u:a=u,u=s+a>>1}i=null;var l=u/n;return 0===l?1:l}return{isSubsampled:t,renderTo:e}}),i(X,[D,u,p,m,w,G,q,l,d],function(e,t,n,i,r,o,a,s,u){function c(){function e(){if(!E&&!y)throw new n.ImageError(n.DOMException.INVALID_STATE_ERR);return E||y}function c(e){return i.atob(e.substring(e.indexOf("base64,")+7))}function l(e,t){return"data:"+(t||"")+";base64,"+i.btoa(e)}function d(e){var t=this;y=new Image,y.onerror=function(){g.call(this),t.trigger("error",new n.ImageError(n.ImageError.WRONG_FORMAT))},y.onload=function(){t.trigger("load")},y.src=/^data:[^;]*;base64,/.test(e)?e:l(e,x.type)}function f(e,t){var i=this,r;return window.FileReader?(r=new FileReader,r.onload=function(){t(this.result)},r.onerror=function(){i.trigger("error",new n.FileException(n.FileException.NOT_READABLE_ERR))},r.readAsDataURL(e),void 0):t(e.getAsDataURL())}function p(n,i,r,o){var a=this,s,u,c=0,l=0,d,f,p,g;if(b=o,g=this.meta&&this.meta.tiff&&this.meta.tiff.Orientation||1,-1!==t.inArray(g,[5,6,7,8])){var v=n;n=i,i=v}return d=e(),u=r?Math.max:Math.min,s=u(n/d.width,i/d.height),s>1&&(!r||o)?(this.trigger("Resize"),void 0):(E||(E=document.createElement("canvas")),f=Math.round(d.width*s),p=Math.round(d.height*s),r?(E.width=n,E.height=i,f>n&&(c=Math.round((f-n)/2)),p>i&&(l=Math.round((p-i)/2))):(E.width=f,E.height=p),b||m(E.width,E.height,g),h.call(this,d,E,-c,-l,f,p),this.width=E.width,this.height=E.height,R=!0,a.trigger("Resize"),void 0)}function h(e,t,n,i,r,o){if("iOS"===u.OS)a.renderTo(e,t,{width:r,height:o,x:n,y:i});else{var s=t.getContext("2d");s.drawImage(e,n,i,r,o)}}function m(e,t,n){switch(n){case 5:case 6:case 7:case 8:E.width=t,E.height=e;break;default:E.width=e,E.height=t}var i=E.getContext("2d");switch(n){case 2:i.translate(e,0),i.scale(-1,1);break;case 3:i.translate(e,t),i.rotate(Math.PI);break;case 4:i.translate(0,t),i.scale(1,-1);break;case 5:i.rotate(.5*Math.PI),i.scale(1,-1);break;case 6:i.rotate(.5*Math.PI),i.translate(0,-t);break;case 7:i.rotate(.5*Math.PI),i.translate(e,-t),i.scale(-1,1);break;case 8:i.rotate(-.5*Math.PI),i.translate(-e,0)}}function g(){w&&(w.purge(),w=null),_=y=E=x=null,R=!1}var v=this,y,w,E,_,x,R=!1,b=!0;t.extend(this,{loadFromBlob:function(e){var t=this,i=t.getRuntime(),r=arguments.length>1?arguments[1]:!0;if(!i.can("access_binary"))throw new n.RuntimeError(n.RuntimeError.NOT_SUPPORTED_ERR);return x=e,e.isDetached()?(_=e.getSource(),d.call(this,_),void 0):(f.call(this,e.getSource(),function(e){r&&(_=c(e)),d.call(t,e)}),void 0)},loadFromImage:function(e,t){this.meta=e.meta,x=new r(null,{name:e.name,size:e.size,type:e.type}),d.call(this,t?_=e.getAsBinaryString():e.getAsDataURL())},getInfo:function(){var t=this.getRuntime(),n;return!w&&_&&t.can("access_image_binary")&&(w=new o(_)),n={width:e().width||0,height:e().height||0,type:x.type||s.getFileMime(x.name),size:_&&_.length||x.size||0,name:x.name||"",meta:w&&w.meta||this.meta||{}}},downsize:function(){p.apply(this,arguments)},getAsCanvas:function(){return E&&(E.id=this.uid+"_canvas"),E},getAsBlob:function(e,t){return e!==this.type&&p.call(this,this.width,this.height,!1),new r(null,{name:x.name||"",type:e,data:v.getAsBinaryString.call(this,e,t)})},getAsDataURL:function(e){var t=arguments[1]||90;if(!R)return y.src;if("image/jpeg"!==e)return E.toDataURL("image/png");try{return E.toDataURL("image/jpeg",t/100)}catch(n){return E.toDataURL("image/jpeg")}},getAsBinaryString:function(e,t){if(!R)return _||(_=c(v.getAsDataURL(e,t))),_;if("image/jpeg"!==e)_=c(v.getAsDataURL(e,t));else{var n;t||(t=90);try{n=E.toDataURL("image/jpeg",t/100)}catch(i){n=E.toDataURL("image/jpeg")}_=c(n),w&&(_=w.stripHeaders(_),b&&(w.meta&&w.meta.exif&&w.setExif({PixelXDimension:this.width,PixelYDimension:this.height}),_=w.writeHeaders(_)),w.purge(),w=null)}return R=!1,_},destroy:function(){v=null,g.call(this),this.getRuntime().getShim().removeInstance(this.uid)}})}return e.Image=c}),i(j,[u,d,f,p,g],function(e,t,n,i,r){function o(){var e;try{e=navigator.plugins["Shockwave Flash"],e=e.description}catch(t){try{e=new ActiveXObject("ShockwaveFlash.ShockwaveFlash").GetVariable("$version")}catch(n){e="0.0"}}return e=e.match(/\d+/g),parseFloat(e[0]+"."+e[1])}function a(a){var c=this,l;a=e.extend({swf_url:t.swf_url},a),r.call(this,a,s,{access_binary:function(e){return e&&"browser"===c.mode},access_image_binary:function(e){return e&&"browser"===c.mode},display_media:r.capTrue,do_cors:r.capTrue,drag_and_drop:!1,report_upload_progress:function(){return"client"===c.mode},resize_image:r.capTrue,return_response_headers:!1,return_response_type:function(t){return"json"===t&&window.JSON?!0:!e.arrayDiff(t,["","text","document"])||"browser"===c.mode},return_status_code:function(t){return"browser"===c.mode||!e.arrayDiff(t,[200,404])},select_file:r.capTrue,select_multiple:r.capTrue,send_binary_string:function(e){return e&&"browser"===c.mode},send_browser_cookies:function(e){return e&&"browser"===c.mode},send_custom_headers:function(e){return e&&"browser"===c.mode},send_multipart:r.capTrue,slice_blob:r.capTrue,stream_upload:function(e){return e&&"browser"===c.mode},summon_file_dialog:!1,upload_filesize:function(t){return e.parseSizeStr(t)<=2097152||"client"===c.mode},use_http_method:function(t){return!e.arrayDiff(t,["GET","POST"])}},{access_binary:function(e){return e?"browser":"client"},access_image_binary:function(e){return e?"browser":"client"},report_upload_progress:function(e){return e?"browser":"client"},return_response_type:function(t){return e.arrayDiff(t,["","text","json","document"])?"browser":["client","browser"]},return_status_code:function(t){return e.arrayDiff(t,[200,404])?"browser":["client","browser"]},send_binary_string:function(e){return e?"browser":"client"},send_browser_cookies:function(e){return e?"browser":"client"},send_custom_headers:function(e){return e?"browser":"client"},stream_upload:function(e){return e?"client":"browser"},upload_filesize:function(t){return e.parseSizeStr(t)>=2097152?"client":"browser"}},"client"),o()<10&&(this.mode=!1),e.extend(this,{getShim:function(){return n.get(this.uid)},shimExec:function(e,t){var n=[].slice.call(arguments,2);return c.getShim().exec(this.uid,e,t,n)},init:function(){var n,r,o;o=this.getShimContainer(),e.extend(o.style,{position:"absolute",top:"-8px",left:"-8px",width:"9px",height:"9px",overflow:"hidden"}),n=''+''+''+''+"","IE"===t.browser?(r=document.createElement("div"),o.appendChild(r),r.outerHTML=n,r=o=null):o.innerHTML=n,l=setTimeout(function(){c&&!c.initialized&&c.trigger("Error",new i.RuntimeError(i.RuntimeError.NOT_INIT_ERR))},5e3)},destroy:function(e){return function(){e.call(c),clearTimeout(l),a=l=e=c=null}}(this.destroy)},u)}var s="flash",u={};return r.addConstructor(s,a),u}),i(V,[j,y],function(e,t){var n={slice:function(e,n,i,r){var o=this.getRuntime();return 0>n?n=Math.max(e.size+n,0):n>0&&(n=Math.min(n,e.size)),0>i?i=Math.max(e.size+i,0):i>0&&(i=Math.min(i,e.size)),e=o.shimExec.call(this,"Blob","slice",n,i,r||""),e&&(e=new t(o.uid,e)),e}};return e.Blob=n}),i(W,[j],function(e){var t={init:function(e){this.getRuntime().shimExec.call(this,"FileInput","init",{name:e.name,accept:e.accept,multiple:e.multiple}),this.trigger("ready")}};return e.FileInput=t}),i(Y,[j,m],function(e,t){function n(e,n){switch(n){case"readAsText":return t.atob(e,"utf8");case"readAsBinaryString":return t.atob(e);case"readAsDataURL":return e}return null}var i="",r={read:function(e,t){var r=this,o=r.getRuntime();return"readAsDataURL"===e&&(i="data:"+(t.type||"")+";base64,"),r.bind("Progress",function(t,r){r&&(i+=n(r,e))}),o.shimExec.call(this,"FileReader","readAsBase64",t.uid)},getResult:function(){return i},destroy:function(){i=null}};return e.FileReader=r}),i($,[j,m],function(e,t){function n(e,n){switch(n){case"readAsText":return t.atob(e,"utf8");case"readAsBinaryString":return t.atob(e);case"readAsDataURL":return e}return null}var i={read:function(e,t){var i,r=this.getRuntime();return(i=r.shimExec.call(this,"FileReaderSync","readAsBase64",t.uid))?("readAsDataURL"===e&&(i="data:"+(t.type||"")+";base64,"+i),n(i,e,t.type)):null}};return e.FileReaderSync=i}),i(J,[j,u,y,w,T,S,O],function(e,t,n,i,r,o,a){var s={send:function(e,i){function r(){e.transport=l.mode,l.shimExec.call(c,"XMLHttpRequest","send",e,i)}function s(e,t){l.shimExec.call(c,"XMLHttpRequest","appendBlob",e,t.uid),i=null,r()}function u(e,t){var n=new a;n.bind("TransportingComplete",function(){t(this.result)}),n.transport(e.getSource(),e.type,{ruid:l.uid})}var c=this,l=c.getRuntime();if(t.isEmptyObj(e.headers)||t.each(e.headers,function(e,t){l.shimExec.call(c,"XMLHttpRequest","setRequestHeader",t,e.toString())}),i instanceof o){var d;if(i.each(function(e,t){e instanceof n?d=t:l.shimExec.call(c,"XMLHttpRequest","append",t,e)}),i.hasBlob()){var f=i.getBlob();f.isDetached()?u(f,function(e){f.destroy(),s(d,e)}):s(d,f)}else i=null,r()}else i instanceof n?i.isDetached()?u(i,function(e){i.destroy(),i=e.uid,r()}):(i=i.uid,r()):r()},getResponse:function(e){var n,o,a=this.getRuntime();if(o=a.shimExec.call(this,"XMLHttpRequest","getResponseAsBlob")){if(o=new i(a.uid,o),"blob"===e)return o;try{if(n=new r,~t.inArray(e,["","text"]))return n.readAsText(o);if("json"===e&&window.JSON)return JSON.parse(n.readAsText(o))}finally{o.destroy()}}return null},abort:function(e){var t=this.getRuntime();t.shimExec.call(this,"XMLHttpRequest","abort"),this.dispatchEvent("readystatechange"),this.dispatchEvent("abort")}};return e.XMLHttpRequest=s}),i(Z,[j,y],function(e,t){var n={getAsBlob:function(e){var n=this.getRuntime(),i=n.shimExec.call(this,"Transporter","getAsBlob",e);return i?new t(n.uid,i):null}};return e.Transporter=n}),i(K,[j,u,O,y,T],function(e,t,n,i,r){var o={loadFromBlob:function(e){function t(e){r.shimExec.call(i,"Image","loadFromBlob",e.uid),i=r=null}var i=this,r=i.getRuntime();if(e.isDetached()){var o=new n;o.bind("TransportingComplete",function(){t(o.result.getSource())}),o.transport(e.getSource(),e.type,{ruid:r.uid})}else t(e.getSource())},loadFromImage:function(e){var t=this.getRuntime();return t.shimExec.call(this,"Image","loadFromImage",e.uid)},getAsBlob:function(e,t){var n=this.getRuntime(),r=n.shimExec.call(this,"Image","getAsBlob",e,t);return r?new i(n.uid,r):null},getAsDataURL:function(){var e=this.getRuntime(),t=e.Image.getAsBlob.apply(this,arguments),n;return t?(n=new r,n.readAsDataURL(t)):null}};return e.Image=o}),i(Q,[u,d,f,p,g],function(e,t,n,i,r){function o(e){var t=!1,n=null,i,r,o,a,s,u=0;try{try{n=new ActiveXObject("AgControl.AgControl"),n.IsVersionSupported(e)&&(t=!0),n=null}catch(c){var l=navigator.plugins["Silverlight Plug-In"];if(l){for(i=l.description,"1.0.30226.2"===i&&(i="2.0.30226.2"),r=i.split(".");r.length>3;)r.pop();for(;r.length<4;)r.push(0);for(o=e.split(".");o.length>4;)o.pop();do a=parseInt(o[u],10),s=parseInt(r[u],10),u++;while(u=a&&!isNaN(a)&&(t=!0)}}}catch(d){t=!1}return t}function a(a){var c=this,l;a=e.extend({xap_url:t.xap_url},a),r.call(this,a,s,{access_binary:r.capTrue,access_image_binary:r.capTrue,display_media:r.capTrue,do_cors:r.capTrue,drag_and_drop:!1,report_upload_progress:r.capTrue,resize_image:r.capTrue,return_response_headers:function(e){return e&&"client"===c.mode},return_response_type:function(e){return"json"!==e?!0:!!window.JSON},return_status_code:function(t){return"client"===c.mode||!e.arrayDiff(t,[200,404])},select_file:r.capTrue,select_multiple:r.capTrue,send_binary_string:r.capTrue,send_browser_cookies:function(e){return e&&"browser"===c.mode},send_custom_headers:function(e){return e&&"client"===c.mode},send_multipart:r.capTrue,slice_blob:r.capTrue,stream_upload:!0,summon_file_dialog:!1,upload_filesize:r.capTrue,use_http_method:function(t){return"client"===c.mode||!e.arrayDiff(t,["GET","POST"])}},{return_response_headers:function(e){return e?"client":"browser"},return_status_code:function(t){return e.arrayDiff(t,[200,404])?"client":["client","browser"]},send_browser_cookies:function(e){return e?"browser":"client"},send_custom_headers:function(e){return e?"client":"browser"},use_http_method:function(t){return e.arrayDiff(t,["GET","POST"])?"client":["client","browser"]}}),o("2.0.31005.0")&&"Opera"!==t.browser||(this.mode=!1),e.extend(this,{getShim:function(){return n.get(this.uid).content.Moxie},shimExec:function(e,t){var n=[].slice.call(arguments,2);return c.getShim().exec(this.uid,e,t,n)},init:function(){var e;e=this.getShimContainer(),e.innerHTML=''+''+''+''+''+''+"",l=setTimeout(function(){c&&!c.initialized&&c.trigger("Error",new i.RuntimeError(i.RuntimeError.NOT_INIT_ERR))},"Windows"!==t.OS?1e4:5e3)},destroy:function(e){return function(){e.call(c),clearTimeout(l),a=l=e=c=null}}(this.destroy)},u)}var s="silverlight",u={};return r.addConstructor(s,a),u}),i(et,[Q,u,V],function(e,t,n){return e.Blob=t.extend({},n)}),i(tt,[Q],function(e){var t={init:function(e){function t(e){for(var t="",n=0;no;o++)n=t.keys[o],s=t[n],s&&(/^(\d|[1-9]\d+)$/.test(s)?s=parseInt(s,10):/^\d*\.\d+$/.test(s)&&(s=parseFloat(s)),i.meta[e][n]=s)}),i.width=parseInt(r.width,10),i.height=parseInt(r.height,10),i.size=parseInt(r.size,10),i.type=r.type,i.name=r.name,i}})}),i(ut,[u,p,g,d],function(e,t,n,i){function r(t){var r=this,s=n.capTest,u=n.capTrue;n.call(this,t,o,{access_binary:s(window.FileReader||window.File&&File.getAsDataURL),access_image_binary:!1,display_media:s(a.Image&&(i.can("create_canvas")||i.can("use_data_uri_over32kb"))),do_cors:!1,drag_and_drop:!1,filter_by_extension:s(function(){return"Chrome"===i.browser&&i.version>=28||"IE"===i.browser&&i.version>=10}()),resize_image:function(){return a.Image&&r.can("access_binary")&&i.can("create_canvas")},report_upload_progress:!1,return_response_headers:!1,return_response_type:function(t){return"json"===t&&window.JSON?!0:!!~e.inArray(t,["text","document",""])},return_status_code:function(t){return!e.arrayDiff(t,[200,404])},select_file:function(){return i.can("use_fileinput")},select_multiple:!1,send_binary_string:!1,send_custom_headers:!1,send_multipart:!0,slice_blob:!1,stream_upload:function(){return r.can("select_file")},summon_file_dialog:s(function(){return"Firefox"===i.browser&&i.version>=4||"Opera"===i.browser&&i.version>=12||!!~e.inArray(i.browser,["Chrome","Safari"])}()),upload_filesize:u,use_http_method:function(t){return!e.arrayDiff(t,["GET","POST"])}}),e.extend(this,{init:function(){this.trigger("Init")},destroy:function(e){return function(){e.call(r),e=r=null}}(this.destroy)}),e.extend(this.getShim(),a)}var o="html4",a={};return n.addConstructor(o,r),a}),i(ct,[ut,u,f,L,l,d],function(e,t,n,i,r,o){function a(){function e(){var r=this,l=r.getRuntime(),d,f,p,h,m,g;g=t.guid("uid_"),d=l.getShimContainer(),a&&(p=n.get(a+"_form"),p&&t.extend(p.style,{top:"100%"})),h=document.createElement("form"),h.setAttribute("id",g+"_form"),h.setAttribute("method","post"),h.setAttribute("enctype","multipart/form-data"),h.setAttribute("encoding","multipart/form-data"),t.extend(h.style,{overflow:"hidden",position:"absolute",top:0,left:0,width:"100%",height:"100%"}),m=document.createElement("input"),m.setAttribute("id",g),m.setAttribute("type","file"),m.setAttribute("name",c.name||"Filedata"),m.setAttribute("accept",u.join(",")),t.extend(m.style,{fontSize:"999px",opacity:0}),h.appendChild(m),d.appendChild(h),t.extend(m.style,{position:"absolute",top:0,left:0,width:"100%",height:"100%"}),"IE"===o.browser&&o.version<10&&t.extend(m.style,{filter:"progid:DXImageTransform.Microsoft.Alpha(opacity=0)"}),m.onchange=function(){var t;this.value&&(t=this.files?this.files[0]:{name:this.value},s=[t],this.onchange=function(){},e.call(r),r.bind("change",function i(){var e=n.get(g),t=n.get(g+"_form"),o;r.unbind("change",i),r.files.length&&e&&t&&(o=r.files[0],e.setAttribute("id",o.uid),t.setAttribute("id",o.uid+"_form"),t.setAttribute("target",o.uid+"_iframe")),e=t=null},998),m=h=null,r.trigger("change"))},l.can("summon_file_dialog")&&(f=n.get(c.browse_button),i.removeEvent(f,"click",r.uid),i.addEvent(f,"click",function(e){m&&!m.disabled&&m.click(),e.preventDefault()},r.uid)),a=g,d=p=f=null}var a,s=[],u=[],c;t.extend(this,{init:function(t){var o=this,a=o.getRuntime(),s;c=t,u=t.accept.mimes||r.extList2mimes(t.accept,a.can("filter_by_extension")),s=a.getShimContainer(),function(){var e,r,u;e=n.get(t.browse_button),a.can("summon_file_dialog")&&("static"===n.getStyle(e,"position")&&(e.style.position="relative"),r=parseInt(n.getStyle(e,"z-index"),10)||1,e.style.zIndex=r,s.style.zIndex=r-1),u=a.can("summon_file_dialog")?e:s,i.addEvent(u,"mouseover",function(){o.trigger("mouseenter")},o.uid),i.addEvent(u,"mouseout",function(){o.trigger("mouseleave")},o.uid),i.addEvent(u,"mousedown",function(){o.trigger("mousedown")},o.uid),i.addEvent(n.get(t.container),"mouseup",function(){o.trigger("mouseup")},o.uid),e=null}(),e.call(this),s=null,o.trigger({type:"ready",async:!0})},getFiles:function(){return s},disable:function(e){var t;(t=n.get(a))&&(t.disabled=!!e)},destroy:function(){var e=this.getRuntime(),t=e.getShim(),r=e.getShimContainer();i.removeAllEvents(r,this.uid),i.removeAllEvents(c&&n.get(c.container),this.uid),i.removeAllEvents(c&&n.get(c.browse_button),this.uid),r&&(r.innerHTML=""),t.removeInstance(this.uid),a=s=u=c=r=t=null}})}return e.FileInput=a}),i(lt,[ut,F],function(e,t){return e.FileReader=t}),i(dt,[ut,u,f,b,p,L,y,S],function(e,t,n,i,r,o,a,s){function u(){function e(e){var t=this,i,r,a,s,u=!1;if(l){if(i=l.id.replace(/_iframe$/,""),r=n.get(i+"_form")){for(a=r.getElementsByTagName("input"),s=a.length;s--;)switch(a[s].getAttribute("type")){case"hidden":a[s].parentNode.removeChild(a[s]);break;case"file":u=!0}a=[],u||r.parentNode.removeChild(r),r=null}setTimeout(function(){o.removeEvent(l,"load",t.uid),l.parentNode&&l.parentNode.removeChild(l);var n=t.getRuntime().getShimContainer();n.children.length||n.parentNode.removeChild(n),n=l=null,e()},1)}}var u,c,l;t.extend(this,{send:function(d,f){function p(){var n=m.getShimContainer()||document.body,r=document.createElement("div");r.innerHTML='',l=r.firstChild,n.appendChild(l),o.addEvent(l,"load",function(){var n;try{n=l.contentWindow.document||l.contentDocument||window.frames[l.id].document,/^4(0[0-9]|1[0-7]|2[2346])\s/.test(n.title)?u=n.title.replace(/^(\d+).*$/,"$1"):(u=200,c=t.trim(n.body.innerHTML),h.trigger({type:"progress",loaded:c.length,total:c.length}),w&&h.trigger({type:"uploadprogress",loaded:w.size||1025,total:w.size||1025}))}catch(r){if(!i.hasSameOrigin(d.url))return e.call(h,function(){h.trigger("error")}),void 0;u=404}e.call(h,function(){h.trigger("load")})},h.uid)}var h=this,m=h.getRuntime(),g,v,y,w;if(u=c=null,f instanceof s&&f.hasBlob()){if(w=f.getBlob(),g=w.uid,y=n.get(g),v=n.get(g+"_form"),!v)throw new r.DOMException(r.DOMException.NOT_FOUND_ERR)}else g=t.guid("uid_"),v=document.createElement("form"),v.setAttribute("id",g+"_form"),v.setAttribute("method",d.method),v.setAttribute("enctype","multipart/form-data"),v.setAttribute("encoding","multipart/form-data"),v.setAttribute("target",g+"_iframe"),m.getShimContainer().appendChild(v);f instanceof s&&f.each(function(e,n){if(e instanceof a)y&&y.setAttribute("name",n);else{var i=document.createElement("input");t.extend(i,{type:"hidden",name:n,value:e}),y?v.insertBefore(i,y):v.appendChild(i)}}),v.setAttribute("action",d.url),p(),v.submit(),h.trigger("loadstart")},getStatus:function(){return u},getResponse:function(e){if("json"===e&&"string"===t.typeOf(c)&&window.JSON)try{return JSON.parse(c.replace(/^\s*]*>/,"").replace(/<\/pre>\s*$/,""))}catch(n){return null}return c},abort:function(){var t=this;l&&l.contentWindow&&(l.contentWindow.stop?l.contentWindow.stop():l.contentWindow.document.execCommand?l.contentWindow.document.execCommand("Stop"):l.src="about:blank"),e.call(this,function(){t.dispatchEvent("abort")})}})}return e.XMLHttpRequest=u}),i(ft,[ut,X],function(e,t){return e.Image=t}),a([u,c,l,d,f,p,h,m,g,v,y,w,E,_,x,R,b,T,S,A,O,I,L])}(this);;(function(){"use strict";var e={},t=moxie.core.utils.Basic.inArray;return function n(r){var i,s;for(i in r)s=typeof r[i],s==="object"&&!~t(i,["Exceptions","Env","Mime"])?n(r[i]):s==="function"&&(e[i]=r[i])}(window.moxie),e.Env=window.moxie.core.utils.Env,e.Mime=window.moxie.core.utils.Mime,e.Exceptions=window.moxie.core.Exceptions,window.mOxie=e,window.o||(window.o=e),e})(); +/** + * Plupload - multi-runtime File Uploader + * v2.1.1 + * + * Copyright 2013, Moxiecode Systems AB + * Released under GPL License. + * + * License: http://www.plupload.com/license + * Contributing: http://www.plupload.com/contributing + * + * Date: 2014-01-16 + */ +;(function(e,t,n){function s(e){function r(e,t,r){var i={chunks:"slice_blob",jpgresize:"send_binary_string",pngresize:"send_binary_string",progress:"report_upload_progress",multi_selection:"select_multiple",dragdrop:"drag_and_drop",drop_element:"drag_and_drop",headers:"send_custom_headers",canSendBinary:"send_binary",triggerDialog:"summon_file_dialog"};i[e]?n[i[e]]=t:r||(n[e]=t)}var t=e.required_features,n={};return typeof t=="string"?o.each(t.split(/\s*,\s*/),function(e){r(e,!0)}):typeof t=="object"?o.each(t,function(e,t){r(t,e)}):t===!0&&(e.multipart||(n.send_binary_string=!0),e.chunk_size>0&&(n.slice_blob=!0),e.resize.enabled&&(n.send_binary_string=!0),o.each(e,function(e,t){r(t,!!e,!0)})),n}var r=e.setTimeout,i={},o={VERSION:"2.1.1",STOPPED:1,STARTED:2,QUEUED:1,UPLOADING:2,FAILED:4,DONE:5,GENERIC_ERROR:-100,HTTP_ERROR:-200,IO_ERROR:-300,SECURITY_ERROR:-400,INIT_ERROR:-500,FILE_SIZE_ERROR:-600,FILE_EXTENSION_ERROR:-601,FILE_DUPLICATE_ERROR:-602,IMAGE_FORMAT_ERROR:-700,IMAGE_MEMORY_ERROR:-701,IMAGE_DIMENSIONS_ERROR:-702,mimeTypes:t.mimes,ua:t.ua,typeOf:t.typeOf,extend:t.extend,guid:t.guid,get:function(n){var r=[],i;t.typeOf(n)!=="array"&&(n=[n]);var s=n.length;while(s--)i=t.get(n[s]),i&&r.push(i);return r.length?r:null},each:t.each,getPos:t.getPos,getSize:t.getSize,xmlEncode:function(e){var t={"<":"lt",">":"gt","&":"amp",'"':"quot","'":"#39"},n=/[<>&\"\']/g;return e?(""+e).replace(n,function(e){return t[e]?"&"+t[e]+";":e}):e},toArray:t.toArray,inArray:t.inArray,addI18n:t.addI18n,translate:t.translate,isEmptyObj:t.isEmptyObj,hasClass:t.hasClass,addClass:t.addClass,removeClass:t.removeClass,getStyle:t.getStyle,addEvent:t.addEvent,removeEvent:t.removeEvent,removeAllEvents:t.removeAllEvents,cleanName:function(e){var t,n;n=[/[\300-\306]/g,"A",/[\340-\346]/g,"a",/\307/g,"C",/\347/g,"c",/[\310-\313]/g,"E",/[\350-\353]/g,"e",/[\314-\317]/g,"I",/[\354-\357]/g,"i",/\321/g,"N",/\361/g,"n",/[\322-\330]/g,"O",/[\362-\370]/g,"o",/[\331-\334]/g,"U",/[\371-\374]/g,"u"];for(t=0;t0?"&":"?")+n),e},formatSize:function(e){function t(e,t){return Math.round(e*Math.pow(10,t))/Math.pow(10,t)}if(e===n||/\D/.test(e))return o.translate("N/A");var r=Math.pow(1024,4);return e>r?t(e/r,1)+" "+o.translate("tb"):e>(r/=1024)?t(e/r,1)+" "+o.translate("gb"):e>(r/=1024)?t(e/r,1)+" "+o.translate("mb"):e>1024?Math.round(e/1024)+" "+o.translate("kb"):e+" "+o.translate("b")},parseSize:t.parseSizeStr,predictRuntime:function(e,n){var r,i;return r=new o.Uploader(e),i=t.Runtime.thatCan(r.getOption().required_features,n||e.runtimes),r.destroy(),i},addFileFilter:function(e,t){i[e]=t}};o.addFileFilter("mime_types",function(e,t,n){e.length&&!e.regexp.test(t.name)?(this.trigger("Error",{code:o.FILE_EXTENSION_ERROR,message:o.translate("File extension error."),file:t}),n(!1)):n(!0)}),o.addFileFilter("max_file_size",function(e,t,n){var r;e=o.parseSize(e),t.size!==r&&e&&t.size>e?(this.trigger("Error",{code:o.FILE_SIZE_ERROR,message:o.translate("File size error."),file:t}),n(!1)):n(!0)}),o.addFileFilter("prevent_duplicates",function(e,t,n){if(e){var r=this.files.length;while(r--)if(t.name===this.files[r].name&&t.size===this.files[r].size){this.trigger("Error",{code:o.FILE_DUPLICATE_ERROR,message:o.translate("Duplicate file error."),file:t}),n(!1);return}}n(!0)}),o.Uploader=function(e){function g(){var e,t=0,n;if(this.state==o.STARTED){for(n=0;n0?Math.ceil(e.loaded/e.size*100):100,b()}function b(){var e,t;d.reset();for(e=0;e0?Math.ceil(d.uploaded/f.length*100):0:(d.bytesPerSec=Math.ceil(d.loaded/((+(new Date)-p||1)/1e3)),d.percent=d.size>0?Math.ceil(d.loaded/d.size*100):0)}function w(){var e=c[0]||h[0];return e?e.getRuntime().uid:!1}function E(e,n){if(e.ruid){var r=t.Runtime.getInfo(e.ruid);if(r)return r.can(n)}return!1}function S(){this.bind("FilesAdded",C),this.bind("CancelUpload",M),this.bind("BeforeUpload",k),this.bind("UploadFile",L),this.bind("UploadProgress",A),this.bind("StateChanged",O),this.bind("QueueChanged",b),this.bind("Error",D),this.bind("FileUploaded",_),this.bind("Destroy",P)}function x(e,n){var r=this,i=0,s=[],u={accept:e.filters.mime_types,runtime_order:e.runtimes,required_caps:e.required_features,preferred_caps:l,swf_url:e.flash_swf_url,xap_url:e.silverlight_xap_url};o.each(e.runtimes.split(/\s*,\s*/),function(t){e[t]&&(u[t]=e[t])}),e.browse_button&&o.each(e.browse_button,function(n){s.push(function(s){var a=new t.FileInput(o.extend({},u,{name:e.file_data_name,multiple:e.multi_selection,container:e.container,browse_button:n}));a.onready=function(){var e=t.Runtime.getInfo(this.ruid);t.extend(r.features,{chunks:e.can("slice_blob"),multipart:e.can("send_multipart"),multi_selection:e.can("select_multiple")}),i++,c.push(this),s()},a.onchange=function(){r.addFile(this.files)},a.bind("mouseenter mouseleave mousedown mouseup",function(r){v||(e.browse_button_hover&&("mouseenter"===r.type?t.addClass(n,e.browse_button_hover):"mouseleave"===r.type&&t.removeClass(n,e.browse_button_hover)),e.browse_button_active&&("mousedown"===r.type?t.addClass(n,e.browse_button_active):"mouseup"===r.type&&t.removeClass(n,e.browse_button_active)))}),a.bind("error runtimeerror",function(){a=null,s()}),a.init()})}),e.drop_element&&o.each(e.drop_element,function(e){s.push(function(n){var s=new t.FileDrop(o.extend({},u,{drop_zone:e}));s.onready=function(){var e=t.Runtime.getInfo(this.ruid);r.features.dragdrop=e.can("drag_and_drop"),i++,h.push(this),n()},s.ondrop=function(){r.addFile(this.files)},s.bind("error runtimeerror",function(){s=null,n()}),s.init()})}),t.inSeries(s,function(){typeof n=="function"&&n(i)})}function T(e,n,r){var i=new t.Image;try{i.onload=function(){i.downsize(n.width,n.height,n.crop,n.preserve_headers)},i.onresize=function(){r(this.getAsBlob(e.type,n.quality)),this.destroy()},i.onerror=function(){r(e)},i.load(e)}catch(s){r(e)}}function N(e,n,r){function f(e,t,n){var r=a[e];switch(e){case"max_file_size":e==="max_file_size"&&(a.max_file_size=a.filters.max_file_size=t);break;case"chunk_size":if(t=o.parseSize(t))a[e]=t;break;case"filters":o.typeOf(t)==="array"&&(t={mime_types:t}),n?o.extend(a.filters,t):a.filters=t,t.mime_types&&(a.filters.mime_types.regexp=function(e){var t=[];return o.each(e,function(e){o.each(e.extensions.split(/,/),function(e){/^\s*\*\s*$/.test(e)?t.push("\\.*"):t.push("\\."+e.replace(new RegExp("["+"/^$.*+?|()[]{}\\".replace(/./g,"\\$&")+"]","g"),"\\$&"))})}),new RegExp("("+t.join("|")+")$","i")}(a.filters.mime_types));break;case"resize":n?o.extend(a.resize,t,{enabled:!0}):a.resize=t;break;case"prevent_duplicates":a.prevent_duplicates=a.filters.prevent_duplicates=!!t;break;case"browse_button":case"drop_element":t=o.get(t);case"container":case"runtimes":case"multi_selection":case"flash_swf_url":case"silverlight_xap_url":a[e]=t,n||(u=!0);break;default:a[e]=t}n||i.trigger("OptionChanged",e,t,r)}var i=this,u=!1;typeof e=="object"?o.each(e,function(e,t){f(t,e,r)}):f(e,n,r),r?(a.required_features=s(o.extend({},a)),l=s(o.extend({},a,{required_features:!0}))):u&&(i.trigger("Destroy"),x.call(i,a,function(e){e?(i.runtime=t.Runtime.getInfo(w()).type,i.trigger("Init",{runtime:i.runtime}),i.trigger("PostInit")):i.trigger("Error",{code:o.INIT_ERROR,message:o.translate("Init error.")})}))}function C(e,t){[].push.apply(f,t),e.trigger("QueueChanged"),e.refresh()}function k(e,t){if(a.unique_names){var n=t.name.match(/\.([^.]+)$/),r="part";n&&(r=n[1]),t.target_name=t.id+"."+r}}function L(e,n){function h(){u-->0?r(p,1e3):(n.loaded=f,e.trigger("Error",{code:o.HTTP_ERROR,message:o.translate("HTTP Error."),file:n,response:m.responseText,status:m.status,responseHeaders:m.getAllResponseHeaders()}))}function p(){var d,v,g,y;if(n.status==o.DONE||n.status==o.FAILED||e.state==o.STOPPED)return;g={name:n.target_name||n.name},s&&a.chunks&&c.size>s?(y=Math.min(s,c.size-f),d=c.slice(f,f+y)):(y=c.size,d=c),s&&a.chunks&&(e.settings.send_chunk_number?(g.chunk=Math.ceil(f/s),g.chunks=Math.ceil(c.size/s)):(g.offset=f,g.total=c.size)),m=new t.XMLHttpRequest,m.upload&&(m.upload.onprogress=function(t){n.loaded=Math.min(n.size,f+t.loaded),e.trigger("UploadProgress",n)}),m.onload=function(){if(m.status>=400){h();return}u=e.settings.max_retries,y=c.size?(n.size!=n.origSize&&(c.destroy(),c=null),e.trigger("UploadProgress",n),n.status=o.DONE,e.trigger("FileUploaded",n,{response:m.responseText,status:m.status,responseHeaders:m.getAllResponseHeaders()})):r(p,1)},m.onerror=function(){h()},m.onloadend=function(){this.destroy(),m=null},e.settings.multipart&&a.multipart?(g.name=n.target_name||n.name,m.open("post",i,!0),o.each(e.settings.headers,function(e,t){m.setRequestHeader(t,e)}),v=new t.FormData,o.each(o.extend(g,e.settings.multipart_params),function(e,t){v.append(t,e)}),v.append(e.settings.file_data_name,d),m.send(v,{runtime_order:e.settings.runtimes,required_caps:e.settings.required_features,preferred_caps:l,swf_url:e.settings.flash_swf_url,xap_url:e.settings.silverlight_xap_url})):(i=o.buildUrl(e.settings.url,o.extend(g,e.settings.multipart_params)),m.open("post",i,!0),m.setRequestHeader("Content-Type","application/octet-stream"),o.each(e.settings.headers,function(e,t){m.setRequestHeader(t,e)}),m.send(d,{runtime_order:e.settings.runtimes,required_caps:e.settings.required_features,preferred_caps:l,swf_url:e.settings.flash_swf_url,xap_url:e.settings.silverlight_xap_url}))}var i=e.settings.url,s=e.settings.chunk_size,u=e.settings.max_retries,a=e.features,f=0,c;n.loaded&&(f=n.loaded=s*Math.floor(n.loaded/s)),c=n.getSource(),e.settings.resize.enabled&&E(c,"send_binary_string")&&!!~t.inArray(c.type,["image/jpeg","image/png"])?T.call(this,c,e.settings.resize,function(e){c=e,n.size=e.size,p()}):p()}function A(e,t){y(t)}function O(e){if(e.state==o.STARTED)p=+(new Date);else if(e.state==o.STOPPED)for(var t=e.files.length-1;t>=0;t--)e.files[t].status==o.UPLOADING&&(e.files[t].status=o.QUEUED,b())}function M(){m&&m.abort()}function _(e){b(),r(function(){g.call(e)},1)}function D(e,t){t.file&&(t.file.status=o.FAILED,y(t.file),e.state==o.STARTED&&(e.trigger("CancelUpload"),r(function(){g.call(e)},1)))}function P(e){e.stop(),o.each(f,function(e){e.destroy()}),f=[],c.length&&(o.each(c,function(e){e.destroy()}),c=[]),h.length&&(o.each(h,function(e){e.destroy()}),h=[]),l={},v=!1,p=m=null,d.reset()}var u=o.guid(),a,f=[],l={},c=[],h=[],p,d,v=!1,m;a={runtimes:t.Runtime.order,max_retries:0,chunk_size:0,multipart:!0,multi_selection:!0,file_data_name:"file",flash_swf_url:"js/Moxie.swf",silverlight_xap_url:"js/Moxie.xap",filters:{mime_types:[],prevent_duplicates:!1,max_file_size:0},resize:{enabled:!1,preserve_headers:!0,crop:!1},send_chunk_number:!0},N.call(this,e,null,!0),d=new o.QueueProgress,o.extend(this,{id:u,uid:u,state:o.STOPPED,features:{},runtime:null,files:f,settings:a,total:d,init:function(){var e=this;typeof a.preinit=="function"?a.preinit(e):o.each(a.preinit,function(t,n){e.bind(n,t)});if(!a.browse_button||!a.url){this.trigger("Error",{code:o.INIT_ERROR,message:o.translate("Init error.")});return}S.call(this),x.call(this,a,function(n){typeof a.init=="function"?a.init(e):o.each(a.init,function(t,n){e.bind(n,t)}),n?(e.runtime=t.Runtime.getInfo(w()).type,e.trigger("Init",{runtime:e.runtime}),e.trigger("PostInit")):e.trigger("Error",{code:o.INIT_ERROR,message:o.translate("Init error.")})})},setOption:function(e,t){N.call(this,e,t,!this.runtime)},getOption:function(e){return e?a[e]:a},refresh:function(){c.length&&o.each(c,function(e){e.trigger("Refresh")}),this.trigger("Refresh")},start:function(){this.state!=o.STARTED&&(this.state=o.STARTED,this.trigger("StateChanged"),g.call(this))},stop:function(){this.state!=o.STOPPED&&(this.state=o.STOPPED,this.trigger("StateChanged"),this.trigger("CancelUpload"))},disableBrowse:function(){v=arguments[0]!==n?arguments[0]:!0,c.length&&o.each(c,function(e){e.disable(v)}),this.trigger("DisableBrowse",v)},getFile:function(e){var t;for(t=f.length-1;t>=0;t--)if(f[t].id===e)return f[t]},addFile:function(e,n){function l(e,n){var r=[];t.each(s.settings.filters,function(t,n){i[n]&&r.push(function(r){i[n].call(s,t,e,function(e){r(!e)})})}),t.inSeries(r,n)}function c(e){var i=t.typeOf(e);if(e instanceof t.File){if(!e.ruid&&!e.isDetached()){if(!f)return!1;e.ruid=f,e.connectRuntime(f)}c(new o.File(e))}else e instanceof t.Blob?(c(e.getSource()),e.destroy()):e instanceof o.File?(n&&(e.name=n),u.push(function(t){l(e,function(n){n||(a.push(e),s.trigger("FileFiltered",e)),r(t,1)})})):t.inArray(i,["file","blob"])!==-1?c(new t.File(null,e)):i==="node"&&t.typeOf(e.files)==="filelist"?t.each(e.files,c):i==="array"&&(n=null,t.each(e,c))}var s=this,u=[],a=[],f;f=w(),c(e),u.length&&t.inSeries(u,function(){a.length&&s.trigger("FilesAdded",a)})},removeFile:function(e){var t=typeof e=="string"?e:e.id;for(var n=f.length-1;n>=0;n--)if(f[n].id===t)return this.splice(n,1)[0]},splice:function(e,t){var r=f.splice(e===n?0:e,t===n?f.length:t),i=!1;return this.state==o.STARTED&&(i=!0,this.stop()),this.trigger("FilesRemoved",r),o.each(r,function(e){e.destroy()}),this.trigger("QueueChanged"),this.refresh(),i&&this.start(),r},bind:function(e,t,n){var r=this;o.Uploader.prototype.bind.call(this,e,function(){var e=[].slice.call(arguments);return e.splice(0,1,r),t.apply(this,e)},0,n)},destroy:function(){this.trigger("Destroy"),a=d=null,this.unbindAll()}})},o.Uploader.prototype=t.EventTarget.instance,o.File=function(){function n(n){o.extend(this,{id:o.guid(),name:n.name||n.fileName,type:n.type||"",size:n.size||n.fileSize,origSize:n.size||n.fileSize,loaded:0,percent:0,status:o.QUEUED,lastModifiedDate:n.lastModifiedDate||(new Date).toLocaleString(),getNative:function(){var e=this.getSource().getSource();return t.inArray(t.typeOf(e),["blob","file"])!==-1?e:null},getSource:function(){return e[this.id]?e[this.id]:null},destroy:function(){var t=this.getSource();t&&(t.destroy(),delete e[this.id])}}),e[this.id]=n}var e={};return n}(),o.QueueProgress=function(){var e=this;e.size=0,e.loaded=0,e.uploaded=0,e.failed=0,e.queued=0,e.percent=0,e.bytesPerSec=0,e.reset=function(){e.size=e.loaded=e.uploaded=e.failed=e.queued=e.percent=e.bytesPerSec=0}},e.plupload=o})(window,mOxie); \ No newline at end of file diff --git a/wp-includes/js/plupload/plupload.silverlight.xap b/wp-includes/js/plupload/plupload.silverlight.xap new file mode 100644 index 0000000..3a9f389 Binary files /dev/null and b/wp-includes/js/plupload/plupload.silverlight.xap differ diff --git a/wp-includes/js/plupload/wp-plupload.js b/wp-includes/js/plupload/wp-plupload.js new file mode 100644 index 0000000..09a37ce --- /dev/null +++ b/wp-includes/js/plupload/wp-plupload.js @@ -0,0 +1,424 @@ +/* global pluploadL10n, plupload, _wpPluploadSettings */ + +window.wp = window.wp || {}; + +( function( exports, $ ) { + var Uploader; + + if ( typeof _wpPluploadSettings === 'undefined' ) { + return; + } + + /** + * A WordPress uploader. + * + * The Plupload library provides cross-browser uploader UI integration. + * This object bridges the Plupload API to integrate uploads into the + * WordPress back-end and the WordPress media experience. + * + * @param {object} options The options passed to the new plupload instance. + * @param {object} options.container The id of uploader container. + * @param {object} options.browser The id of button to trigger the file select. + * @param {object} options.dropzone The id of file drop target. + * @param {object} options.plupload An object of parameters to pass to the plupload instance. + * @param {object} options.params An object of parameters to pass to $_POST when uploading the file. + * Extends this.plupload.multipart_params under the hood. + */ + Uploader = function( options ) { + var self = this, + isIE = navigator.userAgent.indexOf('Trident/') != -1 || navigator.userAgent.indexOf('MSIE ') != -1, + elements = { + container: 'container', + browser: 'browse_button', + dropzone: 'drop_element' + }, + key, error; + + this.supports = { + upload: Uploader.browser.supported + }; + + this.supported = this.supports.upload; + + if ( ! this.supported ) { + return; + } + + // Arguments to send to pluplad.Uploader(). + // Use deep extend to ensure that multipart_params and other objects are cloned. + this.plupload = $.extend( true, { multipart_params: {} }, Uploader.defaults ); + this.container = document.body; // Set default container. + + // Extend the instance with options. + // + // Use deep extend to allow options.plupload to override individual + // default plupload keys. + $.extend( true, this, options ); + + // Proxy all methods so this always refers to the current instance. + for ( key in this ) { + if ( $.isFunction( this[ key ] ) ) { + this[ key ] = $.proxy( this[ key ], this ); + } + } + + // Ensure all elements are jQuery elements and have id attributes, + // then set the proper plupload arguments to the ids. + for ( key in elements ) { + if ( ! this[ key ] ) { + continue; + } + + this[ key ] = $( this[ key ] ).first(); + + if ( ! this[ key ].length ) { + delete this[ key ]; + continue; + } + + if ( ! this[ key ].prop('id') ) { + this[ key ].prop( 'id', '__wp-uploader-id-' + Uploader.uuid++ ); + } + + this.plupload[ elements[ key ] ] = this[ key ].prop('id'); + } + + // If the uploader has neither a browse button nor a dropzone, bail. + if ( ! ( this.browser && this.browser.length ) && ! ( this.dropzone && this.dropzone.length ) ) { + return; + } + + // Make sure flash sends cookies (seems in IE it does without switching to urlstream mode) + if ( ! isIE && 'flash' === plupload.predictRuntime( this.plupload ) && + ( ! this.plupload.required_features || ! this.plupload.required_features.hasOwnProperty( 'send_binary_string' ) ) ) { + + this.plupload.required_features = this.plupload.required_features || {}; + this.plupload.required_features.send_binary_string = true; + } + + // Initialize the plupload instance. + this.uploader = new plupload.Uploader( this.plupload ); + delete this.plupload; + + // Set default params and remove this.params alias. + this.param( this.params || {} ); + delete this.params; + + /** + * Custom error callback. + * + * Add a new error to the errors collection, so other modules can track + * and display errors. @see wp.Uploader.errors. + * + * @param {string} message + * @param {object} data + * @param {plupload.File} file File that was uploaded. + */ + error = function( message, data, file ) { + if ( file.attachment ) { + file.attachment.destroy(); + } + + Uploader.errors.unshift({ + message: message || pluploadL10n.default_error, + data: data, + file: file + }); + + self.error( message, data, file ); + }; + + /** + * After the Uploader has been initialized, initialize some behaviors for the dropzone. + * + * @param {plupload.Uploader} uploader Uploader instance. + */ + this.uploader.bind( 'init', function( uploader ) { + var timer, active, dragdrop, + dropzone = self.dropzone; + + dragdrop = self.supports.dragdrop = uploader.features.dragdrop && ! Uploader.browser.mobile; + + // Generate drag/drop helper classes. + if ( ! dropzone ) { + return; + } + + dropzone.toggleClass( 'supports-drag-drop', !! dragdrop ); + + if ( ! dragdrop ) { + return dropzone.unbind('.wp-uploader'); + } + + // 'dragenter' doesn't fire correctly, simulate it with a limited 'dragover'. + dropzone.bind( 'dragover.wp-uploader', function() { + if ( timer ) { + clearTimeout( timer ); + } + + if ( active ) { + return; + } + + dropzone.trigger('dropzone:enter').addClass('drag-over'); + active = true; + }); + + dropzone.bind('dragleave.wp-uploader, drop.wp-uploader', function() { + // Using an instant timer prevents the drag-over class from + // being quickly removed and re-added when elements inside the + // dropzone are repositioned. + // + // @see https://core.trac.wordpress.org/ticket/21705 + timer = setTimeout( function() { + active = false; + dropzone.trigger('dropzone:leave').removeClass('drag-over'); + }, 0 ); + }); + + self.ready = true; + $(self).trigger( 'uploader:ready' ); + }); + + this.uploader.init(); + + if ( this.browser ) { + this.browser.on( 'mouseenter', this.refresh ); + } else { + this.uploader.disableBrowse( true ); + // If HTML5 mode, hide the auto-created file container. + $('#' + this.uploader.id + '_html5_container').hide(); + } + + /** + * After files were filtered and added to the queue, create a model for each. + * + * @event FilesAdded + * @param {plupload.Uploader} uploader Uploader instance. + * @param {Array} files Array of file objects that were added to queue by the user. + */ + this.uploader.bind( 'FilesAdded', function( up, files ) { + _.each( files, function( file ) { + var attributes, image; + + // Ignore failed uploads. + if ( plupload.FAILED === file.status ) { + return; + } + + // Generate attributes for a new `Attachment` model. + attributes = _.extend({ + file: file, + uploading: true, + date: new Date(), + filename: file.name, + menuOrder: 0, + uploadedTo: wp.media.model.settings.post.id + }, _.pick( file, 'loaded', 'size', 'percent' ) ); + + // Handle early mime type scanning for images. + image = /(?:jpe?g|png|gif)$/i.exec( file.name ); + + // For images set the model's type and subtype attributes. + if ( image ) { + attributes.type = 'image'; + + // `jpeg`, `png` and `gif` are valid subtypes. + // `jpg` is not, so map it to `jpeg`. + attributes.subtype = ( 'jpg' === image[0] ) ? 'jpeg' : image[0]; + } + + // Create a model for the attachment, and add it to the Upload queue collection + // so listeners to the upload queue can track and display upload progress. + file.attachment = wp.media.model.Attachment.create( attributes ); + Uploader.queue.add( file.attachment ); + + self.added( file.attachment ); + }); + + up.refresh(); + up.start(); + }); + + this.uploader.bind( 'UploadProgress', function( up, file ) { + file.attachment.set( _.pick( file, 'loaded', 'percent' ) ); + self.progress( file.attachment ); + }); + + /** + * After a file is successfully uploaded, update its model. + * + * @param {plupload.Uploader} uploader Uploader instance. + * @param {plupload.File} file File that was uploaded. + * @param {Object} response Object with response properties. + * @return {mixed} + */ + this.uploader.bind( 'FileUploaded', function( up, file, response ) { + var complete; + + try { + response = JSON.parse( response.response ); + } catch ( e ) { + return error( pluploadL10n.default_error, e, file ); + } + + if ( ! _.isObject( response ) || _.isUndefined( response.success ) ) + return error( pluploadL10n.default_error, null, file ); + else if ( ! response.success ) + return error( response.data && response.data.message, response.data, file ); + + _.each(['file','loaded','size','percent'], function( key ) { + file.attachment.unset( key ); + }); + + file.attachment.set( _.extend( response.data, { uploading: false }) ); + wp.media.model.Attachment.get( response.data.id, file.attachment ); + + complete = Uploader.queue.all( function( attachment ) { + return ! attachment.get('uploading'); + }); + + if ( complete ) + Uploader.queue.reset(); + + self.success( file.attachment ); + }); + + /** + * When plupload surfaces an error, send it to the error handler. + * + * @param {plupload.Uploader} uploader Uploader instance. + * @param {Object} error Contains code, message and sometimes file and other details. + */ + this.uploader.bind( 'Error', function( up, pluploadError ) { + var message = pluploadL10n.default_error, + key; + + // Check for plupload errors. + for ( key in Uploader.errorMap ) { + if ( pluploadError.code === plupload[ key ] ) { + message = Uploader.errorMap[ key ]; + + if ( _.isFunction( message ) ) { + message = message( pluploadError.file, pluploadError ); + } + + break; + } + } + + error( message, pluploadError, pluploadError.file ); + up.refresh(); + }); + + this.uploader.bind( 'PostInit', function() { + self.init(); + }); + }; + + // Adds the 'defaults' and 'browser' properties. + $.extend( Uploader, _wpPluploadSettings ); + + Uploader.uuid = 0; + + // Map Plupload error codes to user friendly error messages. + Uploader.errorMap = { + 'FAILED': pluploadL10n.upload_failed, + 'FILE_EXTENSION_ERROR': pluploadL10n.invalid_filetype, + 'IMAGE_FORMAT_ERROR': pluploadL10n.not_an_image, + 'IMAGE_MEMORY_ERROR': pluploadL10n.image_memory_exceeded, + 'IMAGE_DIMENSIONS_ERROR': pluploadL10n.image_dimensions_exceeded, + 'GENERIC_ERROR': pluploadL10n.upload_failed, + 'IO_ERROR': pluploadL10n.io_error, + 'HTTP_ERROR': pluploadL10n.http_error, + 'SECURITY_ERROR': pluploadL10n.security_error, + + 'FILE_SIZE_ERROR': function( file ) { + return pluploadL10n.file_exceeds_size_limit.replace('%s', file.name); + } + }; + + $.extend( Uploader.prototype, { + /** + * Acts as a shortcut to extending the uploader's multipart_params object. + * + * param( key ) + * Returns the value of the key. + * + * param( key, value ) + * Sets the value of a key. + * + * param( map ) + * Sets values for a map of data. + */ + param: function( key, value ) { + if ( arguments.length === 1 && typeof key === 'string' ) { + return this.uploader.settings.multipart_params[ key ]; + } + + if ( arguments.length > 1 ) { + this.uploader.settings.multipart_params[ key ] = value; + } else { + $.extend( this.uploader.settings.multipart_params, key ); + } + }, + + /** + * Make a few internal event callbacks available on the wp.Uploader object + * to change the Uploader internals if absolutely necessary. + */ + init: function() {}, + error: function() {}, + success: function() {}, + added: function() {}, + progress: function() {}, + complete: function() {}, + refresh: function() { + var node, attached, container, id; + + if ( this.browser ) { + node = this.browser[0]; + + // Check if the browser node is in the DOM. + while ( node ) { + if ( node === document.body ) { + attached = true; + break; + } + node = node.parentNode; + } + + // If the browser node is not attached to the DOM, use a + // temporary container to house it, as the browser button + // shims require the button to exist in the DOM at all times. + if ( ! attached ) { + id = 'wp-uploader-browser-' + this.uploader.id; + + container = $( '#' + id ); + if ( ! container.length ) { + container = $('
        ').css({ + position: 'fixed', + top: '-1000px', + left: '-1000px', + height: 0, + width: 0 + }).attr( 'id', 'wp-uploader-browser-' + this.uploader.id ).appendTo('body'); + } + + container.append( this.browser ); + } + } + + this.uploader.refresh(); + } + }); + + // Create a collection of attachments in the upload queue, + // so that other modules can track and display upload progress. + Uploader.queue = new wp.media.model.Attachments( [], { query: false }); + + // Create a collection to collect errors incurred while attempting upload. + Uploader.errors = new Backbone.Collection(); + + exports.Uploader = Uploader; +})( wp, jQuery ); diff --git a/wp-includes/js/plupload/wp-plupload.min.js b/wp-includes/js/plupload/wp-plupload.min.js new file mode 100644 index 0000000..c1b104f --- /dev/null +++ b/wp-includes/js/plupload/wp-plupload.min.js @@ -0,0 +1 @@ +window.wp=window.wp||{},function(a,b){var c;"undefined"!=typeof _wpPluploadSettings&&(c=function(a){var d,e,f=this,g=-1!=navigator.userAgent.indexOf("Trident/")||-1!=navigator.userAgent.indexOf("MSIE "),h={container:"container",browser:"browse_button",dropzone:"drop_element"};if(this.supports={upload:c.browser.supported},this.supported=this.supports.upload,this.supported){this.plupload=b.extend(!0,{multipart_params:{}},c.defaults),this.container=document.body,b.extend(!0,this,a);for(d in this)b.isFunction(this[d])&&(this[d]=b.proxy(this[d],this));for(d in h)this[d]&&(this[d]=b(this[d]).first(),this[d].length?(this[d].prop("id")||this[d].prop("id","__wp-uploader-id-"+c.uuid++),this.plupload[h[d]]=this[d].prop("id")):delete this[d]);(this.browser&&this.browser.length||this.dropzone&&this.dropzone.length)&&(g||"flash"!==plupload.predictRuntime(this.plupload)||this.plupload.required_features&&this.plupload.required_features.hasOwnProperty("send_binary_string")||(this.plupload.required_features=this.plupload.required_features||{},this.plupload.required_features.send_binary_string=!0),this.uploader=new plupload.Uploader(this.plupload),delete this.plupload,this.param(this.params||{}),delete this.params,e=function(a,b,d){d.attachment&&d.attachment.destroy(),c.errors.unshift({message:a||pluploadL10n.default_error,data:b,file:d}),f.error(a,b,d)},this.uploader.bind("init",function(a){var d,e,g,h=f.dropzone;if(g=f.supports.dragdrop=a.features.dragdrop&&!c.browser.mobile,h){if(h.toggleClass("supports-drag-drop",!!g),!g)return h.unbind(".wp-uploader");h.bind("dragover.wp-uploader",function(){d&&clearTimeout(d),e||(h.trigger("dropzone:enter").addClass("drag-over"),e=!0)}),h.bind("dragleave.wp-uploader, drop.wp-uploader",function(){d=setTimeout(function(){e=!1,h.trigger("dropzone:leave").removeClass("drag-over")},0)}),f.ready=!0,b(f).trigger("uploader:ready")}}),this.uploader.init(),this.browser?this.browser.on("mouseenter",this.refresh):(this.uploader.disableBrowse(!0),b("#"+this.uploader.id+"_html5_container").hide()),this.uploader.bind("FilesAdded",function(a,b){_.each(b,function(a){var b,d;plupload.FAILED!==a.status&&(b=_.extend({file:a,uploading:!0,date:new Date,filename:a.name,menuOrder:0,uploadedTo:wp.media.model.settings.post.id},_.pick(a,"loaded","size","percent")),d=/(?:jpe?g|png|gif)$/i.exec(a.name),d&&(b.type="image",b.subtype="jpg"===d[0]?"jpeg":d[0]),a.attachment=wp.media.model.Attachment.create(b),c.queue.add(a.attachment),f.added(a.attachment))}),a.refresh(),a.start()}),this.uploader.bind("UploadProgress",function(a,b){b.attachment.set(_.pick(b,"loaded","percent")),f.progress(b.attachment)}),this.uploader.bind("FileUploaded",function(a,b,d){var g;try{d=JSON.parse(d.response)}catch(h){return e(pluploadL10n.default_error,h,b)}return!_.isObject(d)||_.isUndefined(d.success)?e(pluploadL10n.default_error,null,b):d.success?(_.each(["file","loaded","size","percent"],function(a){b.attachment.unset(a)}),b.attachment.set(_.extend(d.data,{uploading:!1})),wp.media.model.Attachment.get(d.data.id,b.attachment),g=c.queue.all(function(a){return!a.get("uploading")}),g&&c.queue.reset(),void f.success(b.attachment)):e(d.data&&d.data.message,d.data,b)}),this.uploader.bind("Error",function(a,b){var d,f=pluploadL10n.default_error;for(d in c.errorMap)if(b.code===plupload[d]){f=c.errorMap[d],_.isFunction(f)&&(f=f(b.file,b));break}e(f,b,b.file),a.refresh()}),this.uploader.bind("PostInit",function(){f.init()}))}},b.extend(c,_wpPluploadSettings),c.uuid=0,c.errorMap={FAILED:pluploadL10n.upload_failed,FILE_EXTENSION_ERROR:pluploadL10n.invalid_filetype,IMAGE_FORMAT_ERROR:pluploadL10n.not_an_image,IMAGE_MEMORY_ERROR:pluploadL10n.image_memory_exceeded,IMAGE_DIMENSIONS_ERROR:pluploadL10n.image_dimensions_exceeded,GENERIC_ERROR:pluploadL10n.upload_failed,IO_ERROR:pluploadL10n.io_error,HTTP_ERROR:pluploadL10n.http_error,SECURITY_ERROR:pluploadL10n.security_error,FILE_SIZE_ERROR:function(a){return pluploadL10n.file_exceeds_size_limit.replace("%s",a.name)}},b.extend(c.prototype,{param:function(a,c){return 1===arguments.length&&"string"==typeof a?this.uploader.settings.multipart_params[a]:void(arguments.length>1?this.uploader.settings.multipart_params[a]=c:b.extend(this.uploader.settings.multipart_params,a))},init:function(){},error:function(){},success:function(){},added:function(){},progress:function(){},complete:function(){},refresh:function(){var a,c,d,e;if(this.browser){for(a=this.browser[0];a;){if(a===document.body){c=!0;break}a=a.parentNode}c||(e="wp-uploader-browser-"+this.uploader.id,d=b("#"+e),d.length||(d=b('
        ').css({position:"fixed",top:"-1000px",left:"-1000px",height:0,width:0}).attr("id","wp-uploader-browser-"+this.uploader.id).appendTo("body")),d.append(this.browser))}this.uploader.refresh()}}),c.queue=new wp.media.model.Attachments([],{query:!1}),c.errors=new Backbone.Collection,a.Uploader=c)}(wp,jQuery); \ No newline at end of file diff --git a/wp-includes/js/quicktags.js b/wp-includes/js/quicktags.js new file mode 100644 index 0000000..9b2f8c8 --- /dev/null +++ b/wp-includes/js/quicktags.js @@ -0,0 +1,683 @@ +/* global adminpage, wpActiveEditor, quicktagsL10n, wpLink, prompt */ +/* + * Quicktags + * + * This is the HTML editor in WordPress. It can be attached to any textarea and will + * append a toolbar above it. This script is self-contained (does not require external libraries). + * + * Run quicktags(settings) to initialize it, where settings is an object containing up to 3 properties: + * settings = { + * id : 'my_id', the HTML ID of the textarea, required + * buttons: '' Comma separated list of the names of the default buttons to show. Optional. + * Current list of default button names: 'strong,em,link,block,del,ins,img,ul,ol,li,code,more,close'; + * } + * + * The settings can also be a string quicktags_id. + * + * quicktags_id string The ID of the textarea that will be the editor canvas + * buttons string Comma separated list of the default buttons names that will be shown in that instance. + */ + +// new edit toolbar used with permission +// by Alex King +// http://www.alexking.org/ + +var QTags, edCanvas, + edButtons = []; + +/* jshint ignore:start */ + +/** + * Back-compat + * + * Define all former global functions so plugins that hack quicktags.js directly don't cause fatal errors. + */ +var edAddTag = function(){}, +edCheckOpenTags = function(){}, +edCloseAllTags = function(){}, +edInsertImage = function(){}, +edInsertLink = function(){}, +edInsertTag = function(){}, +edLink = function(){}, +edQuickLink = function(){}, +edRemoveTag = function(){}, +edShowButton = function(){}, +edShowLinks = function(){}, +edSpell = function(){}, +edToolbar = function(){}; + +/** + * Initialize new instance of the Quicktags editor + */ +function quicktags(settings) { + return new QTags(settings); +} + +/** + * Inserts content at the caret in the active editor (textarea) + * + * Added for back compatibility + * @see QTags.insertContent() + */ +function edInsertContent(bah, txt) { + return QTags.insertContent(txt); +} + +/** + * Adds a button to all instances of the editor + * + * Added for back compatibility, use QTags.addButton() as it gives more flexibility like type of button, button placement, etc. + * @see QTags.addButton() + */ +function edButton(id, display, tagStart, tagEnd, access) { + return QTags.addButton( id, display, tagStart, tagEnd, access, '', -1 ); +} + +/* jshint ignore:end */ + +(function(){ + // private stuff is prefixed with an underscore + var _domReady = function(func) { + var t, i, DOMContentLoaded, _tryReady; + + if ( typeof jQuery !== 'undefined' ) { + jQuery(document).ready(func); + } else { + t = _domReady; + t.funcs = []; + + t.ready = function() { + if ( ! t.isReady ) { + t.isReady = true; + for ( i = 0; i < t.funcs.length; i++ ) { + t.funcs[i](); + } + } + }; + + if ( t.isReady ) { + func(); + } else { + t.funcs.push(func); + } + + if ( ! t.eventAttached ) { + if ( document.addEventListener ) { + DOMContentLoaded = function(){document.removeEventListener('DOMContentLoaded', DOMContentLoaded, false);t.ready();}; + document.addEventListener('DOMContentLoaded', DOMContentLoaded, false); + window.addEventListener('load', t.ready, false); + } else if ( document.attachEvent ) { + DOMContentLoaded = function(){if (document.readyState === 'complete'){ document.detachEvent('onreadystatechange', DOMContentLoaded);t.ready();}}; + document.attachEvent('onreadystatechange', DOMContentLoaded); + window.attachEvent('onload', t.ready); + + _tryReady = function() { + try { + document.documentElement.doScroll('left'); + } catch(e) { + setTimeout(_tryReady, 50); + return; + } + + t.ready(); + }; + _tryReady(); + } + + t.eventAttached = true; + } + } + }, + + _datetime = (function() { + var now = new Date(), zeroise; + + zeroise = function(number) { + var str = number.toString(); + + if ( str.length < 2 ) { + str = '0' + str; + } + + return str; + }; + + return now.getUTCFullYear() + '-' + + zeroise( now.getUTCMonth() + 1 ) + '-' + + zeroise( now.getUTCDate() ) + 'T' + + zeroise( now.getUTCHours() ) + ':' + + zeroise( now.getUTCMinutes() ) + ':' + + zeroise( now.getUTCSeconds() ) + + '+00:00'; + })(), + qt; + + qt = QTags = function(settings) { + if ( typeof(settings) === 'string' ) { + settings = {id: settings}; + } else if ( typeof(settings) !== 'object' ) { + return false; + } + + var t = this, + id = settings.id, + canvas = document.getElementById(id), + name = 'qt_' + id, + tb, onclick, toolbar_id; + + if ( !id || !canvas ) { + return false; + } + + t.name = name; + t.id = id; + t.canvas = canvas; + t.settings = settings; + + if ( id === 'content' && typeof(adminpage) === 'string' && ( adminpage === 'post-new-php' || adminpage === 'post-php' ) ) { + // back compat hack :-( + edCanvas = canvas; + toolbar_id = 'ed_toolbar'; + } else { + toolbar_id = name + '_toolbar'; + } + + tb = document.createElement('div'); + tb.id = toolbar_id; + tb.className = 'quicktags-toolbar'; + tb.onclick = function() { + window.wpActiveEditor = id; + }; + + canvas.parentNode.insertBefore(tb, canvas); + t.toolbar = tb; + + // listen for click events + onclick = function(e) { + e = e || window.event; + var target = e.target || e.srcElement, visible = target.clientWidth || target.offsetWidth, i; + + // don't call the callback on pressing the accesskey when the button is not visible + if ( !visible ) { + return; + } + + // as long as it has the class ed_button, execute the callback + if ( / ed_button /.test(' ' + target.className + ' ') ) { + // we have to reassign canvas here + t.canvas = canvas = document.getElementById(id); + i = target.id.replace(name + '_', ''); + + if ( t.theButtons[i] ) { + t.theButtons[i].callback.call(t.theButtons[i], target, canvas, t); + } + } + }; + + if ( tb.addEventListener ) { + tb.addEventListener('click', onclick, false); + } else if ( tb.attachEvent ) { + tb.attachEvent('onclick', onclick); + } + + t.getButton = function(id) { + return t.theButtons[id]; + }; + + t.getButtonElement = function(id) { + return document.getElementById(name + '_' + id); + }; + + qt.instances[id] = t; + + if ( ! qt.instances['0'] ) { + qt.instances['0'] = qt.instances[id]; + _domReady( function(){ qt._buttonsInit(); } ); + } + }; + + qt.instances = {}; + + qt.getInstance = function(id) { + return qt.instances[id]; + }; + + qt._buttonsInit = function() { + var t = this, canvas, name, settings, theButtons, html, inst, ed, id, i, use, + defaults = ',strong,em,link,block,del,ins,img,ul,ol,li,code,more,close,'; + + for ( inst in t.instances ) { + if ( '0' === inst ) { + continue; + } + + ed = t.instances[inst]; + canvas = ed.canvas; + name = ed.name; + settings = ed.settings; + html = ''; + theButtons = {}; + use = ''; + + // set buttons + if ( settings.buttons ) { + use = ','+settings.buttons+','; + } + + for ( i in edButtons ) { + if ( !edButtons[i] ) { + continue; + } + + id = edButtons[i].id; + if ( use && defaults.indexOf( ',' + id + ',' ) !== -1 && use.indexOf( ',' + id + ',' ) === -1 ) { + continue; + } + + if ( !edButtons[i].instance || edButtons[i].instance === inst ) { + theButtons[id] = edButtons[i]; + + if ( edButtons[i].html ) { + html += edButtons[i].html(name + '_'); + } + } + } + + if ( use && use.indexOf(',fullscreen,') !== -1 ) { + theButtons.fullscreen = new qt.FullscreenButton(); + html += theButtons.fullscreen.html(name + '_'); + } + + if ( use && use.indexOf(',dfw,') !== -1 ) { + theButtons.dfw = new qt.DFWButton(); + html += theButtons.dfw.html( name + '_' ); + } + + if ( 'rtl' === document.getElementsByTagName('html')[0].dir ) { + theButtons.textdirection = new qt.TextDirectionButton(); + html += theButtons.textdirection.html(name + '_'); + } + + ed.toolbar.innerHTML = html; + ed.theButtons = theButtons; + + if ( typeof jQuery !== 'undefined' ) { + jQuery( document ).triggerHandler( 'quicktags-init', [ ed ] ); + } + } + t.buttonsInitDone = true; + }; + + /** + * Main API function for adding a button to Quicktags + * + * Adds qt.Button or qt.TagButton depending on the args. The first three args are always required. + * To be able to add button(s) to Quicktags, your script should be enqueued as dependent + * on "quicktags" and outputted in the footer. If you are echoing JS directly from PHP, + * use add_action( 'admin_print_footer_scripts', 'output_my_js', 100 ) or add_action( 'wp_footer', 'output_my_js', 100 ) + * + * Minimum required to add a button that calls an external function: + * QTags.addButton( 'my_id', 'my button', my_callback ); + * function my_callback() { alert('yeah!'); } + * + * Minimum required to add a button that inserts a tag: + * QTags.addButton( 'my_id', 'my button', '', '' ); + * QTags.addButton( 'my_id2', 'my button', '
        ' ); + * + * @param string id Required. Button HTML ID + * @param string display Required. Button's value="..." + * @param string|function arg1 Required. Either a starting tag to be inserted like "" or a callback that is executed when the button is clicked. + * @param string arg2 Optional. Ending tag like "" + * @param string access_key Deprecated Not used + * @param string title Optional. Button's title="..." + * @param int priority Optional. Number representing the desired position of the button in the toolbar. 1 - 9 = first, 11 - 19 = second, 21 - 29 = third, etc. + * @param string instance Optional. Limit the button to a specific instance of Quicktags, add to all instances if not present. + * @return mixed null or the button object that is needed for back-compat. + */ + qt.addButton = function( id, display, arg1, arg2, access_key, title, priority, instance ) { + var btn; + + if ( !id || !display ) { + return; + } + + priority = priority || 0; + arg2 = arg2 || ''; + + if ( typeof(arg1) === 'function' ) { + btn = new qt.Button(id, display, access_key, title, instance); + btn.callback = arg1; + } else if ( typeof(arg1) === 'string' ) { + btn = new qt.TagButton(id, display, arg1, arg2, access_key, title, instance); + } else { + return; + } + + if ( priority === -1 ) { // back-compat + return btn; + } + + if ( priority > 0 ) { + while ( typeof(edButtons[priority]) !== 'undefined' ) { + priority++; + } + + edButtons[priority] = btn; + } else { + edButtons[edButtons.length] = btn; + } + + if ( this.buttonsInitDone ) { + this._buttonsInit(); // add the button HTML to all instances toolbars if addButton() was called too late + } + }; + + qt.insertContent = function(content) { + var sel, startPos, endPos, scrollTop, text, canvas = document.getElementById(wpActiveEditor); + + if ( !canvas ) { + return false; + } + + if ( document.selection ) { //IE + canvas.focus(); + sel = document.selection.createRange(); + sel.text = content; + canvas.focus(); + } else if ( canvas.selectionStart || canvas.selectionStart === 0 ) { // FF, WebKit, Opera + text = canvas.value; + startPos = canvas.selectionStart; + endPos = canvas.selectionEnd; + scrollTop = canvas.scrollTop; + + canvas.value = text.substring(0, startPos) + content + text.substring(endPos, text.length); + + canvas.selectionStart = startPos + content.length; + canvas.selectionEnd = startPos + content.length; + canvas.scrollTop = scrollTop; + canvas.focus(); + } else { + canvas.value += content; + canvas.focus(); + } + return true; + }; + + // a plain, dumb button + qt.Button = function(id, display, access, title, instance) { + var t = this; + t.id = id; + t.display = display; + t.access = ''; + t.title = title || ''; + t.instance = instance || ''; + }; + qt.Button.prototype.html = function(idPrefix) { + var title = this.title ? ' title="' + this.title + '"' : '', + active, on, wp, + dfw = ( wp = window.wp ) && wp.editor && wp.editor.dfw; + + if ( this.id === 'fullscreen' ) { + return ''; + } else if ( this.id === 'dfw' ) { + active = dfw && dfw.isActive() ? '' : ' disabled="disabled"'; + on = dfw && dfw.isOn() ? ' active' : ''; + + return ''; + } + + return ''; + }; + qt.Button.prototype.callback = function(){}; + + // a button that inserts HTML tag + qt.TagButton = function(id, display, tagStart, tagEnd, access, title, instance) { + var t = this; + qt.Button.call(t, id, display, access, title, instance); + t.tagStart = tagStart; + t.tagEnd = tagEnd; + }; + qt.TagButton.prototype = new qt.Button(); + qt.TagButton.prototype.openTag = function(e, ed) { + var t = this; + + if ( ! ed.openTags ) { + ed.openTags = []; + } + if ( t.tagEnd ) { + ed.openTags.push(t.id); + e.value = '/' + e.value; + } + }; + qt.TagButton.prototype.closeTag = function(e, ed) { + var t = this, i = t.isOpen(ed); + + if ( i !== false ) { + ed.openTags.splice(i, 1); + } + + e.value = t.display; + }; + // whether a tag is open or not. Returns false if not open, or current open depth of the tag + qt.TagButton.prototype.isOpen = function (ed) { + var t = this, i = 0, ret = false; + if ( ed.openTags ) { + while ( ret === false && i < ed.openTags.length ) { + ret = ed.openTags[i] === t.id ? i : false; + i ++; + } + } else { + ret = false; + } + return ret; + }; + qt.TagButton.prototype.callback = function(element, canvas, ed) { + var t = this, startPos, endPos, cursorPos, scrollTop, v = canvas.value, l, r, i, sel, endTag = v ? t.tagEnd : ''; + + if ( document.selection ) { // IE + canvas.focus(); + sel = document.selection.createRange(); + if ( sel.text.length > 0 ) { + if ( !t.tagEnd ) { + sel.text = sel.text + t.tagStart; + } else { + sel.text = t.tagStart + sel.text + endTag; + } + } else { + if ( !t.tagEnd ) { + sel.text = t.tagStart; + } else if ( t.isOpen(ed) === false ) { + sel.text = t.tagStart; + t.openTag(element, ed); + } else { + sel.text = endTag; + t.closeTag(element, ed); + } + } + canvas.focus(); + } else if ( canvas.selectionStart || canvas.selectionStart === 0 ) { // FF, WebKit, Opera + startPos = canvas.selectionStart; + endPos = canvas.selectionEnd; + cursorPos = endPos; + scrollTop = canvas.scrollTop; + l = v.substring(0, startPos); // left of the selection + r = v.substring(endPos, v.length); // right of the selection + i = v.substring(startPos, endPos); // inside the selection + if ( startPos !== endPos ) { + if ( !t.tagEnd ) { + canvas.value = l + i + t.tagStart + r; // insert self closing tags after the selection + cursorPos += t.tagStart.length; + } else { + canvas.value = l + t.tagStart + i + endTag + r; + cursorPos += t.tagStart.length + endTag.length; + } + } else { + if ( !t.tagEnd ) { + canvas.value = l + t.tagStart + r; + cursorPos = startPos + t.tagStart.length; + } else if ( t.isOpen(ed) === false ) { + canvas.value = l + t.tagStart + r; + t.openTag(element, ed); + cursorPos = startPos + t.tagStart.length; + } else { + canvas.value = l + endTag + r; + cursorPos = startPos + endTag.length; + t.closeTag(element, ed); + } + } + + canvas.selectionStart = cursorPos; + canvas.selectionEnd = cursorPos; + canvas.scrollTop = scrollTop; + canvas.focus(); + } else { // other browsers? + if ( !endTag ) { + canvas.value += t.tagStart; + } else if ( t.isOpen(ed) !== false ) { + canvas.value += t.tagStart; + t.openTag(element, ed); + } else { + canvas.value += endTag; + t.closeTag(element, ed); + } + canvas.focus(); + } + }; + + // removed + qt.SpellButton = function() {}; + + // the close tags button + qt.CloseButton = function() { + qt.Button.call(this, 'close', quicktagsL10n.closeTags, '', quicktagsL10n.closeAllOpenTags); + }; + + qt.CloseButton.prototype = new qt.Button(); + + qt._close = function(e, c, ed) { + var button, element, tbo = ed.openTags; + + if ( tbo ) { + while ( tbo.length > 0 ) { + button = ed.getButton(tbo[tbo.length - 1]); + element = document.getElementById(ed.name + '_' + button.id); + + if ( e ) { + button.callback.call(button, element, c, ed); + } else { + button.closeTag(element, ed); + } + } + } + }; + + qt.CloseButton.prototype.callback = qt._close; + + qt.closeAllTags = function(editor_id) { + var ed = this.getInstance(editor_id); + qt._close('', ed.canvas, ed); + }; + + // the link button + qt.LinkButton = function() { + qt.TagButton.call(this, 'link', 'link', '', ''); + }; + qt.LinkButton.prototype = new qt.TagButton(); + qt.LinkButton.prototype.callback = function(e, c, ed, defaultValue) { + var URL, t = this; + + if ( typeof wpLink !== 'undefined' ) { + wpLink.open( ed.id ); + return; + } + + if ( ! defaultValue ) { + defaultValue = 'http://'; + } + + if ( t.isOpen(ed) === false ) { + URL = prompt(quicktagsL10n.enterURL, defaultValue); + if ( URL ) { + t.tagStart = ''; + qt.TagButton.prototype.callback.call(t, e, c, ed); + } + } else { + qt.TagButton.prototype.callback.call(t, e, c, ed); + } + }; + + // the img button + qt.ImgButton = function() { + qt.TagButton.call(this, 'img', 'img', '', ''); + }; + qt.ImgButton.prototype = new qt.TagButton(); + qt.ImgButton.prototype.callback = function(e, c, ed, defaultValue) { + if ( ! defaultValue ) { + defaultValue = 'http://'; + } + var src = prompt(quicktagsL10n.enterImageURL, defaultValue), alt; + if ( src ) { + alt = prompt(quicktagsL10n.enterImageDescription, ''); + this.tagStart = '' + alt + ''; + qt.TagButton.prototype.callback.call(this, e, c, ed); + } + }; + + qt.FullscreenButton = function() { + qt.Button.call(this, 'fullscreen', quicktagsL10n.fullscreen, 'f', quicktagsL10n.toggleFullscreen); + }; + qt.FullscreenButton.prototype = new qt.Button(); + qt.FullscreenButton.prototype.callback = function(e, c) { + if ( ! c.id || typeof wp === 'undefined' || ! wp.editor || ! wp.editor.fullscreen ) { + return; + } + + wp.editor.fullscreen.on(); + }; + + qt.DFWButton = function() { + qt.Button.call( this, 'dfw', '', 'f', quicktagsL10n.dfw ); + }; + qt.DFWButton.prototype = new qt.Button(); + qt.DFWButton.prototype.callback = function() { + var wp; + + if ( ! ( wp = window.wp ) || ! wp.editor || ! wp.editor.dfw ) { + return; + } + + window.wp.editor.dfw.toggle(); + }; + + qt.TextDirectionButton = function() { + qt.Button.call(this, 'textdirection', quicktagsL10n.textdirection, '', quicktagsL10n.toggleTextdirection); + }; + qt.TextDirectionButton.prototype = new qt.Button(); + qt.TextDirectionButton.prototype.callback = function(e, c) { + var isRTL = ( 'rtl' === document.getElementsByTagName('html')[0].dir ), + currentDirection = c.style.direction; + + if ( ! currentDirection ) { + currentDirection = ( isRTL ) ? 'rtl' : 'ltr'; + } + + c.style.direction = ( 'rtl' === currentDirection ) ? 'ltr' : 'rtl'; + c.focus(); + }; + + // ensure backward compatibility + edButtons[10] = new qt.TagButton('strong','b','',''); + edButtons[20] = new qt.TagButton('em','i','',''), + edButtons[30] = new qt.LinkButton(), // special case + edButtons[40] = new qt.TagButton('block','b-quote','\n\n
        ','
        \n\n'), + edButtons[50] = new qt.TagButton('del','del','',''), + edButtons[60] = new qt.TagButton('ins','ins','',''), + edButtons[70] = new qt.ImgButton(), // special case + edButtons[80] = new qt.TagButton('ul','ul','
          \n','
        \n\n'), + edButtons[90] = new qt.TagButton('ol','ol','
          \n','
        \n\n'), + edButtons[100] = new qt.TagButton('li','li','\t
      17. ','
      18. \n'), + edButtons[110] = new qt.TagButton('code','code','',''), + edButtons[120] = new qt.TagButton('more','more','\n\n',''), + edButtons[140] = new qt.CloseButton(); + +})(); diff --git a/wp-includes/js/quicktags.min.js b/wp-includes/js/quicktags.min.js new file mode 100644 index 0000000..5a278a3 --- /dev/null +++ b/wp-includes/js/quicktags.min.js @@ -0,0 +1 @@ +function quicktags(a){return new QTags(a)}function edInsertContent(a,b){return QTags.insertContent(b)}function edButton(a,b,c,d,e){return QTags.addButton(a,b,c,d,e,"",-1)}var QTags,edCanvas,edButtons=[],edAddTag=function(){},edCheckOpenTags=function(){},edCloseAllTags=function(){},edInsertImage=function(){},edInsertLink=function(){},edInsertTag=function(){},edLink=function(){},edQuickLink=function(){},edRemoveTag=function(){},edShowButton=function(){},edShowLinks=function(){},edSpell=function(){},edToolbar=function(){};!function(){var a,b=function(a){var c,d,e,f;"undefined"!=typeof jQuery?jQuery(document).ready(a):(c=b,c.funcs=[],c.ready=function(){if(!c.isReady)for(c.isReady=!0,d=0;d0){for(;"undefined"!=typeof edButtons[h];)h++;edButtons[h]=j}else edButtons[edButtons.length]=j;this.buttonsInitDone&&this._buttonsInit()}},a.insertContent=function(a){var b,c,d,e,f,g=document.getElementById(wpActiveEditor);return g?(document.selection?(g.focus(),b=document.selection.createRange(),b.text=a,g.focus()):g.selectionStart||0===g.selectionStart?(f=g.value,c=g.selectionStart,d=g.selectionEnd,e=g.scrollTop,g.value=f.substring(0,c)+a+f.substring(d,f.length),g.selectionStart=c+a.length,g.selectionEnd=c+a.length,g.scrollTop=e,g.focus()):(g.value+=a,g.focus()),!0):!1},a.Button=function(a,b,c,d,e){var f=this;f.id=a,f.display=b,f.access="",f.title=d||"",f.instance=e||""},a.Button.prototype.html=function(a){var b,c,d,e=this.title?' title="'+this.title+'"':"",f=(d=window.wp)&&d.editor&&d.editor.dfw;return"fullscreen"===this.id?'":"dfw"===this.id?(b=f&&f.isActive()?"":' disabled="disabled"',c=f&&f.isOn()?" active":"",'"):''},a.Button.prototype.callback=function(){},a.TagButton=function(b,c,d,e,f,g,h){var i=this;a.Button.call(i,b,c,f,g,h),i.tagStart=d,i.tagEnd=e},a.TagButton.prototype=new a.Button,a.TagButton.prototype.openTag=function(a,b){var c=this;b.openTags||(b.openTags=[]),c.tagEnd&&(b.openTags.push(c.id),a.value="/"+a.value)},a.TagButton.prototype.closeTag=function(a,b){var c=this,d=c.isOpen(b);d!==!1&&b.openTags.splice(d,1),a.value=c.display},a.TagButton.prototype.isOpen=function(a){var b=this,c=0,d=!1;if(a.openTags)for(;d===!1&&c0?k.text=l.tagEnd?l.tagStart+k.text+n:k.text+l.tagStart:l.tagEnd?l.isOpen(c)===!1?(k.text=l.tagStart,l.openTag(a,c)):(k.text=n,l.closeTag(a,c)):k.text=l.tagStart,b.focus()):b.selectionStart||0===b.selectionStart?(d=b.selectionStart,e=b.selectionEnd,f=e,g=b.scrollTop,h=m.substring(0,d),i=m.substring(e,m.length),j=m.substring(d,e),d!==e?l.tagEnd?(b.value=h+l.tagStart+j+n+i,f+=l.tagStart.length+n.length):(b.value=h+j+l.tagStart+i,f+=l.tagStart.length):l.tagEnd?l.isOpen(c)===!1?(b.value=h+l.tagStart+i,l.openTag(a,c),f=d+l.tagStart.length):(b.value=h+n+i,f=d+n.length,l.closeTag(a,c)):(b.value=h+l.tagStart+i,f=d+l.tagStart.length),b.selectionStart=f,b.selectionEnd=f,b.scrollTop=g,b.focus()):(n?l.isOpen(c)!==!1?(b.value+=l.tagStart,l.openTag(a,c)):(b.value+=n,l.closeTag(a,c)):b.value+=l.tagStart,b.focus())},a.SpellButton=function(){},a.CloseButton=function(){a.Button.call(this,"close",quicktagsL10n.closeTags,"",quicktagsL10n.closeAllOpenTags)},a.CloseButton.prototype=new a.Button,a._close=function(a,b,c){var d,e,f=c.openTags;if(f)for(;f.length>0;)d=c.getButton(f[f.length-1]),e=document.getElementById(c.name+"_"+d.id),a?d.callback.call(d,e,b,c):d.closeTag(e,c)},a.CloseButton.prototype.callback=a._close,a.closeAllTags=function(b){var c=this.getInstance(b);a._close("",c.canvas,c)},a.LinkButton=function(){a.TagButton.call(this,"link","link","","
        ")},a.LinkButton.prototype=new a.TagButton,a.LinkButton.prototype.callback=function(b,c,d,e){var f,g=this;return"undefined"!=typeof wpLink?void wpLink.open(d.id):(e||(e="http://"),void(g.isOpen(d)===!1?(f=prompt(quicktagsL10n.enterURL,e),f&&(g.tagStart='',a.TagButton.prototype.callback.call(g,b,c,d))):a.TagButton.prototype.callback.call(g,b,c,d)))},a.ImgButton=function(){a.TagButton.call(this,"img","img","","")},a.ImgButton.prototype=new a.TagButton,a.ImgButton.prototype.callback=function(b,c,d,e){e||(e="http://");var f,g=prompt(quicktagsL10n.enterImageURL,e);g&&(f=prompt(quicktagsL10n.enterImageDescription,""),this.tagStart=''+f+'',a.TagButton.prototype.callback.call(this,b,c,d))},a.FullscreenButton=function(){a.Button.call(this,"fullscreen",quicktagsL10n.fullscreen,"f",quicktagsL10n.toggleFullscreen)},a.FullscreenButton.prototype=new a.Button,a.FullscreenButton.prototype.callback=function(a,b){b.id&&"undefined"!=typeof wp&&wp.editor&&wp.editor.fullscreen&&wp.editor.fullscreen.on()},a.DFWButton=function(){a.Button.call(this,"dfw","","f",quicktagsL10n.dfw)},a.DFWButton.prototype=new a.Button,a.DFWButton.prototype.callback=function(){var a;(a=window.wp)&&a.editor&&a.editor.dfw&&window.wp.editor.dfw.toggle()},a.TextDirectionButton=function(){a.Button.call(this,"textdirection",quicktagsL10n.textdirection,"",quicktagsL10n.toggleTextdirection)},a.TextDirectionButton.prototype=new a.Button,a.TextDirectionButton.prototype.callback=function(a,b){var c="rtl"===document.getElementsByTagName("html")[0].dir,d=b.style.direction;d||(d=c?"rtl":"ltr"),b.style.direction="rtl"===d?"ltr":"rtl",b.focus()},edButtons[10]=new a.TagButton("strong","b","",""),edButtons[20]=new a.TagButton("em","i","",""),edButtons[30]=new a.LinkButton,edButtons[40]=new a.TagButton("block","b-quote","\n\n
        ","
        \n\n"),edButtons[50]=new a.TagButton("del","del",'',""),edButtons[60]=new a.TagButton("ins","ins",'',""),edButtons[70]=new a.ImgButton,edButtons[80]=new a.TagButton("ul","ul","
          \n","
        \n\n"),edButtons[90]=new a.TagButton("ol","ol","
          \n","
        \n\n"),edButtons[100]=new a.TagButton("li","li","
      19. ","
      20. \n"),edButtons[110]=new a.TagButton("code","code","",""),edButtons[120]=new a.TagButton("more","more","\n\n",""),edButtons[140]=new a.CloseButton}(); \ No newline at end of file diff --git a/wp-includes/js/shortcode.js b/wp-includes/js/shortcode.js new file mode 100644 index 0000000..34766fb --- /dev/null +++ b/wp-includes/js/shortcode.js @@ -0,0 +1,356 @@ +// Utility functions for parsing and handling shortcodes in JavaScript. + +// Ensure the global `wp` object exists. +window.wp = window.wp || {}; + +(function(){ + wp.shortcode = { + // ### Find the next matching shortcode + // + // Given a shortcode `tag`, a block of `text`, and an optional starting + // `index`, returns the next matching shortcode or `undefined`. + // + // Shortcodes are formatted as an object that contains the match + // `content`, the matching `index`, and the parsed `shortcode` object. + next: function( tag, text, index ) { + var re = wp.shortcode.regexp( tag ), + match, result; + + re.lastIndex = index || 0; + match = re.exec( text ); + + if ( ! match ) { + return; + } + + // If we matched an escaped shortcode, try again. + if ( '[' === match[1] && ']' === match[7] ) { + return wp.shortcode.next( tag, text, re.lastIndex ); + } + + result = { + index: match.index, + content: match[0], + shortcode: wp.shortcode.fromMatch( match ) + }; + + // If we matched a leading `[`, strip it from the match + // and increment the index accordingly. + if ( match[1] ) { + result.content = result.content.slice( 1 ); + result.index++; + } + + // If we matched a trailing `]`, strip it from the match. + if ( match[7] ) { + result.content = result.content.slice( 0, -1 ); + } + + return result; + }, + + // ### Replace matching shortcodes in a block of text + // + // Accepts a shortcode `tag`, content `text` to scan, and a `callback` + // to process the shortcode matches and return a replacement string. + // Returns the `text` with all shortcodes replaced. + // + // Shortcode matches are objects that contain the shortcode `tag`, + // a shortcode `attrs` object, the `content` between shortcode tags, + // and a boolean flag to indicate if the match was a `single` tag. + replace: function( tag, text, callback ) { + return text.replace( wp.shortcode.regexp( tag ), function( match, left, tag, attrs, slash, content, closing, right ) { + // If both extra brackets exist, the shortcode has been + // properly escaped. + if ( left === '[' && right === ']' ) { + return match; + } + + // Create the match object and pass it through the callback. + var result = callback( wp.shortcode.fromMatch( arguments ) ); + + // Make sure to return any of the extra brackets if they + // weren't used to escape the shortcode. + return result ? left + result + right : match; + }); + }, + + // ### Generate a string from shortcode parameters + // + // Creates a `wp.shortcode` instance and returns a string. + // + // Accepts the same `options` as the `wp.shortcode()` constructor, + // containing a `tag` string, a string or object of `attrs`, a boolean + // indicating whether to format the shortcode using a `single` tag, and a + // `content` string. + string: function( options ) { + return new wp.shortcode( options ).string(); + }, + + // ### Generate a RegExp to identify a shortcode + // + // The base regex is functionally equivalent to the one found in + // `get_shortcode_regex()` in `wp-includes/shortcodes.php`. + // + // Capture groups: + // + // 1. An extra `[` to allow for escaping shortcodes with double `[[]]` + // 2. The shortcode name + // 3. The shortcode argument list + // 4. The self closing `/` + // 5. The content of a shortcode when it wraps some content. + // 6. The closing tag. + // 7. An extra `]` to allow for escaping shortcodes with double `[[]]` + regexp: _.memoize( function( tag ) { + return new RegExp( '\\[(\\[?)(' + tag + ')(?![\\w-])([^\\]\\/]*(?:\\/(?!\\])[^\\]\\/]*)*?)(?:(\\/)\\]|\\](?:([^\\[]*(?:\\[(?!\\/\\2\\])[^\\[]*)*)(\\[\\/\\2\\]))?)(\\]?)', 'g' ); + }), + + + // ### Parse shortcode attributes + // + // Shortcodes accept many types of attributes. These can chiefly be + // divided into named and numeric attributes: + // + // Named attributes are assigned on a key/value basis, while numeric + // attributes are treated as an array. + // + // Named attributes can be formatted as either `name="value"`, + // `name='value'`, or `name=value`. Numeric attributes can be formatted + // as `"value"` or just `value`. + attrs: _.memoize( function( text ) { + var named = {}, + numeric = [], + pattern, match; + + // This regular expression is reused from `shortcode_parse_atts()` + // in `wp-includes/shortcodes.php`. + // + // Capture groups: + // + // 1. An attribute name, that corresponds to... + // 2. a value in double quotes. + // 3. An attribute name, that corresponds to... + // 4. a value in single quotes. + // 5. An attribute name, that corresponds to... + // 6. an unquoted value. + // 7. A numeric attribute in double quotes. + // 8. An unquoted numeric attribute. + pattern = /(\w+)\s*=\s*"([^"]*)"(?:\s|$)|(\w+)\s*=\s*\'([^\']*)\'(?:\s|$)|(\w+)\s*=\s*([^\s\'"]+)(?:\s|$)|"([^"]*)"(?:\s|$)|(\S+)(?:\s|$)/g; + + // Map zero-width spaces to actual spaces. + text = text.replace( /[\u00a0\u200b]/g, ' ' ); + + // Match and normalize attributes. + while ( (match = pattern.exec( text )) ) { + if ( match[1] ) { + named[ match[1].toLowerCase() ] = match[2]; + } else if ( match[3] ) { + named[ match[3].toLowerCase() ] = match[4]; + } else if ( match[5] ) { + named[ match[5].toLowerCase() ] = match[6]; + } else if ( match[7] ) { + numeric.push( match[7] ); + } else if ( match[8] ) { + numeric.push( match[8] ); + } + } + + return { + named: named, + numeric: numeric + }; + }), + + // ### Generate a Shortcode Object from a RegExp match + // Accepts a `match` object from calling `regexp.exec()` on a `RegExp` + // generated by `wp.shortcode.regexp()`. `match` can also be set to the + // `arguments` from a callback passed to `regexp.replace()`. + fromMatch: function( match ) { + var type; + + if ( match[4] ) { + type = 'self-closing'; + } else if ( match[6] ) { + type = 'closed'; + } else { + type = 'single'; + } + + return new wp.shortcode({ + tag: match[2], + attrs: match[3], + type: type, + content: match[5] + }); + } + }; + + + // Shortcode Objects + // ----------------- + // + // Shortcode objects are generated automatically when using the main + // `wp.shortcode` methods: `next()`, `replace()`, and `string()`. + // + // To access a raw representation of a shortcode, pass an `options` object, + // containing a `tag` string, a string or object of `attrs`, a string + // indicating the `type` of the shortcode ('single', 'self-closing', or + // 'closed'), and a `content` string. + wp.shortcode = _.extend( function( options ) { + _.extend( this, _.pick( options || {}, 'tag', 'attrs', 'type', 'content' ) ); + + var attrs = this.attrs; + + // Ensure we have a correctly formatted `attrs` object. + this.attrs = { + named: {}, + numeric: [] + }; + + if ( ! attrs ) { + return; + } + + // Parse a string of attributes. + if ( _.isString( attrs ) ) { + this.attrs = wp.shortcode.attrs( attrs ); + + // Identify a correctly formatted `attrs` object. + } else if ( _.isEqual( _.keys( attrs ), [ 'named', 'numeric' ] ) ) { + this.attrs = attrs; + + // Handle a flat object of attributes. + } else { + _.each( options.attrs, function( value, key ) { + this.set( key, value ); + }, this ); + } + }, wp.shortcode ); + + _.extend( wp.shortcode.prototype, { + // ### Get a shortcode attribute + // + // Automatically detects whether `attr` is named or numeric and routes + // it accordingly. + get: function( attr ) { + return this.attrs[ _.isNumber( attr ) ? 'numeric' : 'named' ][ attr ]; + }, + + // ### Set a shortcode attribute + // + // Automatically detects whether `attr` is named or numeric and routes + // it accordingly. + set: function( attr, value ) { + this.attrs[ _.isNumber( attr ) ? 'numeric' : 'named' ][ attr ] = value; + return this; + }, + + // ### Transform the shortcode match into a string + string: function() { + var text = '[' + this.tag; + + _.each( this.attrs.numeric, function( value ) { + if ( /\s/.test( value ) ) { + text += ' "' + value + '"'; + } else { + text += ' ' + value; + } + }); + + _.each( this.attrs.named, function( value, name ) { + text += ' ' + name + '="' + value + '"'; + }); + + // If the tag is marked as `single` or `self-closing`, close the + // tag and ignore any additional content. + if ( 'single' === this.type ) { + return text + ']'; + } else if ( 'self-closing' === this.type ) { + return text + ' /]'; + } + + // Complete the opening tag. + text += ']'; + + if ( this.content ) { + text += this.content; + } + + // Add the closing tag. + return text + '[/' + this.tag + ']'; + } + }); +}()); + +// HTML utility functions +// ---------------------- +// +// Experimental. These functions may change or be removed in the future. +(function(){ + wp.html = _.extend( wp.html || {}, { + // ### Parse HTML attributes. + // + // Converts `content` to a set of parsed HTML attributes. + // Utilizes `wp.shortcode.attrs( content )`, which is a valid superset of + // the HTML attribute specification. Reformats the attributes into an + // object that contains the `attrs` with `key:value` mapping, and a record + // of the attributes that were entered using `empty` attribute syntax (i.e. + // with no value). + attrs: function( content ) { + var result, attrs; + + // If `content` ends in a slash, strip it. + if ( '/' === content[ content.length - 1 ] ) { + content = content.slice( 0, -1 ); + } + + result = wp.shortcode.attrs( content ); + attrs = result.named; + + _.each( result.numeric, function( key ) { + if ( /\s/.test( key ) ) { + return; + } + + attrs[ key ] = ''; + }); + + return attrs; + }, + + // ### Convert an HTML-representation of an object to a string. + string: function( options ) { + var text = '<' + options.tag, + content = options.content || ''; + + _.each( options.attrs, function( value, attr ) { + text += ' ' + attr; + + // Use empty attribute notation where possible. + if ( '' === value ) { + return; + } + + // Convert boolean values to strings. + if ( _.isBoolean( value ) ) { + value = value ? 'true' : 'false'; + } + + text += '="' + value + '"'; + }); + + // Return the result if it is a self-closing tag. + if ( options.single ) { + return text + ' />'; + } + + // Complete the opening tag. + text += '>'; + + // If `content` is an object, recursively call this function. + text += _.isObject( content ) ? wp.html.string( content ) : content; + + return text + ''; + } + }); +}()); diff --git a/wp-includes/js/shortcode.min.js b/wp-includes/js/shortcode.min.js new file mode 100644 index 0000000..b3a8f52 --- /dev/null +++ b/wp-includes/js/shortcode.min.js @@ -0,0 +1 @@ +window.wp=window.wp||{},function(){wp.shortcode={next:function(a,b,c){var d,e,f=wp.shortcode.regexp(a);return f.lastIndex=c||0,(d=f.exec(b))?"["===d[1]&&"]"===d[7]?wp.shortcode.next(a,b,f.lastIndex):(e={index:d.index,content:d[0],shortcode:wp.shortcode.fromMatch(d)},d[1]&&(e.content=e.content.slice(1),e.index++),d[7]&&(e.content=e.content.slice(0,-1)),e):void 0},replace:function(a,b,c){return b.replace(wp.shortcode.regexp(a),function(a,b,d,e,f,g,h,i){if("["===b&&"]"===i)return a;var j=c(wp.shortcode.fromMatch(arguments));return j?b+j+i:a})},string:function(a){return new wp.shortcode(a).string()},regexp:_.memoize(function(a){return new RegExp("\\[(\\[?)("+a+")(?![\\w-])([^\\]\\/]*(?:\\/(?!\\])[^\\]\\/]*)*?)(?:(\\/)\\]|\\](?:([^\\[]*(?:\\[(?!\\/\\2\\])[^\\[]*)*)(\\[\\/\\2\\]))?)(\\]?)","g")}),attrs:_.memoize(function(a){var b,c,d={},e=[];for(b=/(\w+)\s*=\s*"([^"]*)"(?:\s|$)|(\w+)\s*=\s*\'([^\']*)\'(?:\s|$)|(\w+)\s*=\s*([^\s\'"]+)(?:\s|$)|"([^"]*)"(?:\s|$)|(\S+)(?:\s|$)/g,a=a.replace(/[\u00a0\u200b]/g," ");c=b.exec(a);)c[1]?d[c[1].toLowerCase()]=c[2]:c[3]?d[c[3].toLowerCase()]=c[4]:c[5]?d[c[5].toLowerCase()]=c[6]:c[7]?e.push(c[7]):c[8]&&e.push(c[8]);return{named:d,numeric:e}}),fromMatch:function(a){var b;return b=a[4]?"self-closing":a[6]?"closed":"single",new wp.shortcode({tag:a[2],attrs:a[3],type:b,content:a[5]})}},wp.shortcode=_.extend(function(a){_.extend(this,_.pick(a||{},"tag","attrs","type","content"));var b=this.attrs;this.attrs={named:{},numeric:[]},b&&(_.isString(b)?this.attrs=wp.shortcode.attrs(b):_.isEqual(_.keys(b),["named","numeric"])?this.attrs=b:_.each(a.attrs,function(a,b){this.set(b,a)},this))},wp.shortcode),_.extend(wp.shortcode.prototype,{get:function(a){return this.attrs[_.isNumber(a)?"numeric":"named"][a]},set:function(a,b){return this.attrs[_.isNumber(a)?"numeric":"named"][a]=b,this},string:function(){var a="["+this.tag;return _.each(this.attrs.numeric,function(b){a+=/\s/.test(b)?' "'+b+'"':" "+b}),_.each(this.attrs.named,function(b,c){a+=" "+c+'="'+b+'"'}),"single"===this.type?a+"]":"self-closing"===this.type?a+" /]":(a+="]",this.content&&(a+=this.content),a+"[/"+this.tag+"]")}})}(),function(){wp.html=_.extend(wp.html||{},{attrs:function(a){var b,c;return"/"===a[a.length-1]&&(a=a.slice(0,-1)),b=wp.shortcode.attrs(a),c=b.named,_.each(b.numeric,function(a){/\s/.test(a)||(c[a]="")}),c},string:function(a){var b="<"+a.tag,c=a.content||"";return _.each(a.attrs,function(a,c){b+=" "+c,""!==a&&(_.isBoolean(a)&&(a=a?"true":"false"),b+='="'+a+'"')}),a.single?b+" />":(b+=">",b+=_.isObject(c)?wp.html.string(c):c,b+"")}})}(); \ No newline at end of file diff --git a/wp-includes/js/swfobject.js b/wp-includes/js/swfobject.js new file mode 100644 index 0000000..87e6155 --- /dev/null +++ b/wp-includes/js/swfobject.js @@ -0,0 +1,4 @@ +/* SWFObject v2.2 + is released under the MIT License +*/ +var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y0){for(var af=0;af0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad'}}aa.outerHTML='"+af+"";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab 0 ) { + jQuery('.describe-toggle-on').show(); + jQuery('.describe-toggle-off').hide(); + jQuery('.slidetoggle').slideUp(200).siblings().removeClass('hidden'); + } + // Create a progress bar containing the filename + jQuery('
        ') + .attr( 'id', 'media-item-' + fileObj.id ) + .addClass('child-of-' + post_id) + .append('
        ', + jQuery('
        ').text( ' ' + fileObj.name )) + .appendTo( jQuery('#media-items' ) ); + // Display the progress div + jQuery('.progress', '#media-item-' + fileObj.id).show(); + + // Disable submit and enable cancel + jQuery('#insert-gallery').prop('disabled', true); + jQuery('#cancel-upload').prop('disabled', false); +} + +function uploadStart(fileObj) { + try { + if ( typeof topWin.tb_remove != 'undefined' ) + topWin.jQuery('#TB_overlay').unbind('click', topWin.tb_remove); + } catch(e){} + + return true; +} + +function uploadProgress(fileObj, bytesDone, bytesTotal) { + // Lengthen the progress bar + var w = jQuery('#media-items').width() - 2, item = jQuery('#media-item-' + fileObj.id); + jQuery('.bar', item).width( w * bytesDone / bytesTotal ); + jQuery('.percent', item).html( Math.ceil(bytesDone / bytesTotal * 100) + '%' ); + + if ( bytesDone == bytesTotal ) + jQuery('.bar', item).html('' + swfuploadL10n.crunching + ''); +} + +function prepareMediaItem(fileObj, serverData) { + var f = ( typeof shortform == 'undefined' ) ? 1 : 2, item = jQuery('#media-item-' + fileObj.id); + // Move the progress bar to 100% + jQuery('.bar', item).remove(); + jQuery('.progress', item).hide(); + + try { + if ( typeof topWin.tb_remove != 'undefined' ) + topWin.jQuery('#TB_overlay').click(topWin.tb_remove); + } catch(e){} + + // Old style: Append the HTML returned by the server -- thumbnail and form inputs + if ( isNaN(serverData) || !serverData ) { + item.append(serverData); + prepareMediaItemInit(fileObj); + } + // New style: server data is just the attachment ID, fetch the thumbnail and form html from the server + else { + item.load('async-upload.php', {attachment_id:serverData, fetch:f}, function(){prepareMediaItemInit(fileObj);updateMediaForm()}); + } +} + +function prepareMediaItemInit(fileObj) { + var item = jQuery('#media-item-' + fileObj.id); + // Clone the thumbnail as a "pinkynail" -- a tiny image to the left of the filename + jQuery('.thumbnail', item).clone().attr('class', 'pinkynail toggle').prependTo(item); + + // Replace the original filename with the new (unique) one assigned during upload + jQuery('.filename.original', item).replaceWith( jQuery('.filename.new', item) ); + + // Also bind toggle to the links + jQuery('a.toggle', item).click(function(){ + jQuery(this).siblings('.slidetoggle').slideToggle(350, function(){ + var w = jQuery(window).height(), t = jQuery(this).offset().top, h = jQuery(this).height(), b; + + if ( w && t && h ) { + b = t + h; + + if ( b > w && (h + 48) < w ) + window.scrollBy(0, b - w + 13); + else if ( b > w ) + window.scrollTo(0, t - 36); + } + }); + jQuery(this).siblings('.toggle').andSelf().toggle(); + jQuery(this).siblings('a.toggle').focus(); + return false; + }); + + // Bind AJAX to the new Delete button + jQuery('a.delete', item).click(function(){ + // Tell the server to delete it. TODO: handle exceptions + jQuery.ajax({ + url: ajaxurl, + type: 'post', + success: deleteSuccess, + error: deleteError, + id: fileObj.id, + data: { + id : this.id.replace(/[^0-9]/g, ''), + action : 'trash-post', + _ajax_nonce : this.href.replace(/^.*wpnonce=/,'') + } + }); + return false; + }); + + // Bind AJAX to the new Undo button + jQuery('a.undo', item).click(function(){ + // Tell the server to untrash it. TODO: handle exceptions + jQuery.ajax({ + url: ajaxurl, + type: 'post', + id: fileObj.id, + data: { + id : this.id.replace(/[^0-9]/g,''), + action: 'untrash-post', + _ajax_nonce: this.href.replace(/^.*wpnonce=/,'') + }, + success: function(data, textStatus){ + var item = jQuery('#media-item-' + fileObj.id); + + if ( type = jQuery('#type-of-' + fileObj.id).val() ) + jQuery('#' + type + '-counter').text(jQuery('#' + type + '-counter').text()-0+1); + if ( item.hasClass('child-of-'+post_id) ) + jQuery('#attachments-count').text(jQuery('#attachments-count').text()-0+1); + + jQuery('.filename .trashnotice', item).remove(); + jQuery('.filename .title', item).css('font-weight','normal'); + jQuery('a.undo', item).addClass('hidden'); + jQuery('a.describe-toggle-on, .menu_order_input', item).show(); + item.css( {backgroundColor:'#ceb'} ).animate( {backgroundColor: '#fff'}, { queue: false, duration: 500, complete: function(){ jQuery(this).css({backgroundColor:''}); } }).removeClass('undo'); + } + }); + return false; + }); + + // Open this item if it says to start open (e.g. to display an error) + jQuery('#media-item-' + fileObj.id + '.startopen').removeClass('startopen').slideToggle(500).siblings('.toggle').toggle(); +} + +function itemAjaxError(id, html) { + var item = jQuery('#media-item-' + id); + var filename = jQuery('.filename', item).text(); + + item.html('
        ' + + '' + swfuploadL10n.dismiss + '' + + '' + swfuploadL10n.error_uploading.replace('%s', filename) + '
        ' + + html + + '
        '); + item.find('a.dismiss').click(function(){jQuery(this).parents('.media-item').slideUp(200, function(){jQuery(this).remove();})}); +} + +function deleteSuccess(data, textStatus) { + if ( data == '-1' ) + return itemAjaxError(this.id, 'You do not have permission. Has your session expired?'); + if ( data == '0' ) + return itemAjaxError(this.id, 'Could not be deleted. Has it been deleted already?'); + + var id = this.id, item = jQuery('#media-item-' + id); + + // Decrement the counters. + if ( type = jQuery('#type-of-' + id).val() ) + jQuery('#' + type + '-counter').text( jQuery('#' + type + '-counter').text() - 1 ); + if ( item.hasClass('child-of-'+post_id) ) + jQuery('#attachments-count').text( jQuery('#attachments-count').text() - 1 ); + + if ( jQuery('form.type-form #media-items').children().length == 1 && jQuery('.hidden', '#media-items').length > 0 ) { + jQuery('.toggle').toggle(); + jQuery('.slidetoggle').slideUp(200).siblings().removeClass('hidden'); + } + + // Vanish it. + jQuery('.toggle', item).toggle(); + jQuery('.slidetoggle', item).slideUp(200).siblings().removeClass('hidden'); + item.css( {backgroundColor:'#faa'} ).animate( {backgroundColor:'#f4f4f4'}, {queue:false, duration:500} ).addClass('undo'); + + jQuery('.filename:empty', item).remove(); + jQuery('.filename .title', item).css('font-weight','bold'); + jQuery('.filename', item).append(' ' + swfuploadL10n.deleted + ' ').siblings('a.toggle').hide(); + jQuery('.filename', item).append( jQuery('a.undo', item).removeClass('hidden') ); + jQuery('.menu_order_input', item).hide(); + + return; +} + +function deleteError(X, textStatus, errorThrown) { + // TODO +} + +function updateMediaForm() { + var one = jQuery('form.type-form #media-items').children(), items = jQuery('#media-items').children(); + + // Just one file, no need for collapsible part + if ( one.length == 1 ) { + jQuery('.slidetoggle', one).slideDown(500).siblings().addClass('hidden').filter('.toggle').toggle(); + } + + // Only show Save buttons when there is at least one file. + if ( items.not('.media-blank').length > 0 ) + jQuery('.savebutton').show(); + else + jQuery('.savebutton').hide(); + + // Only show Gallery buttons when there are at least two files. + if ( items.length > 1 ) { + jQuery('.insert-gallery').show(); + } else { + jQuery('.insert-gallery').hide(); + } +} + +function uploadSuccess(fileObj, serverData) { + // if async-upload returned an error message, place it in the media item div and return + if ( serverData.match('media-upload-error') ) { + jQuery('#media-item-' + fileObj.id).html(serverData); + return; + } + + prepareMediaItem(fileObj, serverData); + updateMediaForm(); + + // Increment the counter. + if ( jQuery('#media-item-' + fileObj.id).hasClass('child-of-' + post_id) ) + jQuery('#attachments-count').text(1 * jQuery('#attachments-count').text() + 1); +} + +function uploadComplete(fileObj) { + // If no more uploads queued, enable the submit button + if ( swfu.getStats().files_queued == 0 ) { + jQuery('#cancel-upload').prop('disabled', true); + jQuery('#insert-gallery').prop('disabled', false); + } +} + + +// wp-specific error handlers + +// generic message +function wpQueueError(message) { + jQuery('#media-upload-error').show().text(message); +} + +// file-specific message +function wpFileError(fileObj, message) { + var item = jQuery('#media-item-' + fileObj.id); + var filename = jQuery('.filename', item).text(); + + item.html('
        ' + + '' + swfuploadL10n.dismiss + '' + + '' + swfuploadL10n.error_uploading.replace('%s', filename) + '
        ' + + message + + '
        '); + item.find('a.dismiss').click(function(){jQuery(this).parents('.media-item').slideUp(200, function(){jQuery(this).remove();})}); +} + +function fileQueueError(fileObj, error_code, message) { + // Handle this error separately because we don't want to create a FileProgress element for it. + if ( error_code == SWFUpload.QUEUE_ERROR.QUEUE_LIMIT_EXCEEDED ) { + wpQueueError(swfuploadL10n.queue_limit_exceeded); + } + else if ( error_code == SWFUpload.QUEUE_ERROR.FILE_EXCEEDS_SIZE_LIMIT ) { + fileQueued(fileObj); + wpFileError(fileObj, swfuploadL10n.file_exceeds_size_limit); + } + else if ( error_code == SWFUpload.QUEUE_ERROR.ZERO_BYTE_FILE ) { + fileQueued(fileObj); + wpFileError(fileObj, swfuploadL10n.zero_byte_file); + } + else if ( error_code == SWFUpload.QUEUE_ERROR.INVALID_FILETYPE ) { + fileQueued(fileObj); + wpFileError(fileObj, swfuploadL10n.invalid_filetype); + } + else { + wpQueueError(swfuploadL10n.default_error); + } +} + +function fileDialogComplete(num_files_queued) { + try { + if (num_files_queued > 0) { + this.startUpload(); + } + } catch (ex) { + this.debug(ex); + } +} + +function switchUploader(s) { + var f = document.getElementById(swfu.customSettings.swfupload_element_id), h = document.getElementById(swfu.customSettings.degraded_element_id); + if ( s ) { + f.style.display = 'block'; + h.style.display = 'none'; + } else { + f.style.display = 'none'; + h.style.display = 'block'; + } +} + +function swfuploadPreLoad() { + if ( !uploaderMode ) { + switchUploader(1); + } else { + switchUploader(0); + } +} + +function swfuploadLoadFailed() { + switchUploader(0); + jQuery('.upload-html-bypass').hide(); +} + +function uploadError(fileObj, errorCode, message) { + + switch (errorCode) { + case SWFUpload.UPLOAD_ERROR.MISSING_UPLOAD_URL: + wpFileError(fileObj, swfuploadL10n.missing_upload_url); + break; + case SWFUpload.UPLOAD_ERROR.UPLOAD_LIMIT_EXCEEDED: + wpFileError(fileObj, swfuploadL10n.upload_limit_exceeded); + break; + case SWFUpload.UPLOAD_ERROR.HTTP_ERROR: + wpQueueError(swfuploadL10n.http_error); + break; + case SWFUpload.UPLOAD_ERROR.UPLOAD_FAILED: + wpQueueError(swfuploadL10n.upload_failed); + break; + case SWFUpload.UPLOAD_ERROR.IO_ERROR: + wpQueueError(swfuploadL10n.io_error); + break; + case SWFUpload.UPLOAD_ERROR.SECURITY_ERROR: + wpQueueError(swfuploadL10n.security_error); + break; + case SWFUpload.UPLOAD_ERROR.UPLOAD_STOPPED: + case SWFUpload.UPLOAD_ERROR.FILE_CANCELLED: + jQuery('#media-item-' + fileObj.id).remove(); + break; + default: + wpFileError(fileObj, swfuploadL10n.default_error); + } +} + +function cancelUpload() { + swfu.cancelQueue(); +} + +// remember the last used image size, alignment and url +jQuery(document).ready(function($){ + $('input[type="radio"]', '#media-items').live('click', function(){ + var tr = $(this).closest('tr'); + + if ( $(tr).hasClass('align') ) + setUserSetting('align', $(this).val()); + else if ( $(tr).hasClass('image-size') ) + setUserSetting('imgsize', $(this).val()); + }); + + $('button.button', '#media-items').live('click', function(){ + var c = this.className || ''; + c = c.match(/url([^ '"]+)/); + if ( c && c[1] ) { + setUserSetting('urlbutton', c[1]); + $(this).siblings('.urlfield').val( $(this).attr('title') ); + } + }); +}); diff --git a/wp-includes/js/swfupload/handlers.min.js b/wp-includes/js/swfupload/handlers.min.js new file mode 100644 index 0000000..5aaa8ee --- /dev/null +++ b/wp-includes/js/swfupload/handlers.min.js @@ -0,0 +1 @@ +var topWin=window.dialogArguments||opener||parent||top;function fileDialogStart(){jQuery("#media-upload-error").empty()}function fileQueued(a){jQuery(".media-blank").remove();if(jQuery("form.type-form #media-items").children().length==1&&jQuery(".hidden","#media-items").length>0){jQuery(".describe-toggle-on").show();jQuery(".describe-toggle-off").hide();jQuery(".slidetoggle").slideUp(200).siblings().removeClass("hidden")}jQuery('
        ').attr("id","media-item-"+a.id).addClass("child-of-"+post_id).append('
        ',jQuery('
        ').text(" "+a.name)).appendTo(jQuery("#media-items"));jQuery(".progress","#media-item-"+a.id).show();jQuery("#insert-gallery").prop("disabled",true);jQuery("#cancel-upload").prop("disabled",false)}function uploadStart(a){try{if(typeof topWin.tb_remove!="undefined"){topWin.jQuery("#TB_overlay").unbind("click",topWin.tb_remove)}}catch(b){}return true}function uploadProgress(e,b,d){var a=jQuery("#media-items").width()-2,c=jQuery("#media-item-"+e.id);jQuery(".bar",c).width(a*b/d);jQuery(".percent",c).html(Math.ceil(b/d*100)+"%");if(b==d){jQuery(".bar",c).html(''+swfuploadL10n.crunching+"")}}function prepareMediaItem(c,a){var d=(typeof shortform=="undefined")?1:2,b=jQuery("#media-item-"+c.id);jQuery(".bar",b).remove();jQuery(".progress",b).hide();try{if(typeof topWin.tb_remove!="undefined"){topWin.jQuery("#TB_overlay").click(topWin.tb_remove)}}catch(g){}if(isNaN(a)||!a){b.append(a);prepareMediaItemInit(c)}else{b.load("async-upload.php",{attachment_id:a,fetch:d},function(){prepareMediaItemInit(c);updateMediaForm()})}}function prepareMediaItemInit(b){var a=jQuery("#media-item-"+b.id);jQuery(".thumbnail",a).clone().attr("class","pinkynail toggle").prependTo(a);jQuery(".filename.original",a).replaceWith(jQuery(".filename.new",a));jQuery("a.toggle",a).click(function(){jQuery(this).siblings(".slidetoggle").slideToggle(350,function(){var d=jQuery(window).height(),e=jQuery(this).offset().top,f=jQuery(this).height(),c;if(d&&e&&f){c=e+f;if(c>d&&(f+48)d){window.scrollTo(0,e-36)}}}});jQuery(this).siblings(".toggle").andSelf().toggle();jQuery(this).siblings("a.toggle").focus();return false});jQuery("a.delete",a).click(function(){jQuery.ajax({url:ajaxurl,type:"post",success:deleteSuccess,error:deleteError,id:b.id,data:{id:this.id.replace(/[^0-9]/g,""),action:"trash-post",_ajax_nonce:this.href.replace(/^.*wpnonce=/,"")}});return false});jQuery("a.undo",a).click(function(){jQuery.ajax({url:ajaxurl,type:"post",id:b.id,data:{id:this.id.replace(/[^0-9]/g,""),action:"untrash-post",_ajax_nonce:this.href.replace(/^.*wpnonce=/,"")},success:function(d,e){var c=jQuery("#media-item-"+b.id);if(type=jQuery("#type-of-"+b.id).val()){jQuery("#"+type+"-counter").text(jQuery("#"+type+"-counter").text()-0+1)}if(c.hasClass("child-of-"+post_id)){jQuery("#attachments-count").text(jQuery("#attachments-count").text()-0+1)}jQuery(".filename .trashnotice",c).remove();jQuery(".filename .title",c).css("font-weight","normal");jQuery("a.undo",c).addClass("hidden");jQuery("a.describe-toggle-on, .menu_order_input",c).show();c.css({backgroundColor:"#ceb"}).animate({backgroundColor:"#fff"},{queue:false,duration:500,complete:function(){jQuery(this).css({backgroundColor:""})}}).removeClass("undo")}});return false});jQuery("#media-item-"+b.id+".startopen").removeClass("startopen").slideToggle(500).siblings(".toggle").toggle()}function itemAjaxError(d,b){var c=jQuery("#media-item-"+d);var a=jQuery(".filename",c).text();c.html('
        '+swfuploadL10n.dismiss+""+swfuploadL10n.error_uploading.replace("%s",a)+"
        "+b+"
        ");c.find("a.dismiss").click(function(){jQuery(this).parents(".media-item").slideUp(200,function(){jQuery(this).remove()})})}function deleteSuccess(b,d){if(b=="-1"){return itemAjaxError(this.id,"You do not have permission. Has your session expired?")}if(b=="0"){return itemAjaxError(this.id,"Could not be deleted. Has it been deleted already?")}var c=this.id,a=jQuery("#media-item-"+c);if(type=jQuery("#type-of-"+c).val()){jQuery("#"+type+"-counter").text(jQuery("#"+type+"-counter").text()-1)}if(a.hasClass("child-of-"+post_id)){jQuery("#attachments-count").text(jQuery("#attachments-count").text()-1)}if(jQuery("form.type-form #media-items").children().length==1&&jQuery(".hidden","#media-items").length>0){jQuery(".toggle").toggle();jQuery(".slidetoggle").slideUp(200).siblings().removeClass("hidden")}jQuery(".toggle",a).toggle();jQuery(".slidetoggle",a).slideUp(200).siblings().removeClass("hidden");a.css({backgroundColor:"#faa"}).animate({backgroundColor:"#f4f4f4"},{queue:false,duration:500}).addClass("undo");jQuery(".filename:empty",a).remove();jQuery(".filename .title",a).css("font-weight","bold");jQuery(".filename",a).append(' '+swfuploadL10n.deleted+" ").siblings("a.toggle").hide();jQuery(".filename",a).append(jQuery("a.undo",a).removeClass("hidden"));jQuery(".menu_order_input",a).hide();return}function deleteError(c,b,a){}function updateMediaForm(){var b=jQuery("form.type-form #media-items").children(),a=jQuery("#media-items").children();if(b.length==1){jQuery(".slidetoggle",b).slideDown(500).siblings().addClass("hidden").filter(".toggle").toggle()}if(a.not(".media-blank").length>0){jQuery(".savebutton").show()}else{jQuery(".savebutton").hide()}if(a.length>1){jQuery(".insert-gallery").show()}else{jQuery(".insert-gallery").hide()}}function uploadSuccess(b,a){if(a.match("media-upload-error")){jQuery("#media-item-"+b.id).html(a);return}prepareMediaItem(b,a);updateMediaForm();if(jQuery("#media-item-"+b.id).hasClass("child-of-"+post_id)){jQuery("#attachments-count").text(1*jQuery("#attachments-count").text()+1)}}function uploadComplete(a){if(swfu.getStats().files_queued==0){jQuery("#cancel-upload").prop("disabled",true);jQuery("#insert-gallery").prop("disabled",false)}}function wpQueueError(a){jQuery("#media-upload-error").show().text(a)}function wpFileError(d,c){var b=jQuery("#media-item-"+d.id);var a=jQuery(".filename",b).text();b.html('
        '+swfuploadL10n.dismiss+""+swfuploadL10n.error_uploading.replace("%s",a)+"
        "+c+"
        ");b.find("a.dismiss").click(function(){jQuery(this).parents(".media-item").slideUp(200,function(){jQuery(this).remove()})})}function fileQueueError(c,a,b){if(a==SWFUpload.QUEUE_ERROR.QUEUE_LIMIT_EXCEEDED){wpQueueError(swfuploadL10n.queue_limit_exceeded)}else{if(a==SWFUpload.QUEUE_ERROR.FILE_EXCEEDS_SIZE_LIMIT){fileQueued(c);wpFileError(c,swfuploadL10n.file_exceeds_size_limit)}else{if(a==SWFUpload.QUEUE_ERROR.ZERO_BYTE_FILE){fileQueued(c);wpFileError(c,swfuploadL10n.zero_byte_file)}else{if(a==SWFUpload.QUEUE_ERROR.INVALID_FILETYPE){fileQueued(c);wpFileError(c,swfuploadL10n.invalid_filetype)}else{wpQueueError(swfuploadL10n.default_error)}}}}}function fileDialogComplete(b){try{if(b>0){this.startUpload()}}catch(a){this.debug(a)}}function switchUploader(b){var c=document.getElementById(swfu.customSettings.swfupload_element_id),a=document.getElementById(swfu.customSettings.degraded_element_id);if(b){c.style.display="block";a.style.display="none"}else{c.style.display="none";a.style.display="block"}}function swfuploadPreLoad(){if(!uploaderMode){switchUploader(1)}else{switchUploader(0)}}function swfuploadLoadFailed(){switchUploader(0);jQuery(".upload-html-bypass").hide()}function uploadError(b,c,a){switch(c){case SWFUpload.UPLOAD_ERROR.MISSING_UPLOAD_URL:wpFileError(b,swfuploadL10n.missing_upload_url);break;case SWFUpload.UPLOAD_ERROR.UPLOAD_LIMIT_EXCEEDED:wpFileError(b,swfuploadL10n.upload_limit_exceeded);break;case SWFUpload.UPLOAD_ERROR.HTTP_ERROR:wpQueueError(swfuploadL10n.http_error);break;case SWFUpload.UPLOAD_ERROR.UPLOAD_FAILED:wpQueueError(swfuploadL10n.upload_failed);break;case SWFUpload.UPLOAD_ERROR.IO_ERROR:wpQueueError(swfuploadL10n.io_error);break;case SWFUpload.UPLOAD_ERROR.SECURITY_ERROR:wpQueueError(swfuploadL10n.security_error);break;case SWFUpload.UPLOAD_ERROR.UPLOAD_STOPPED:case SWFUpload.UPLOAD_ERROR.FILE_CANCELLED:jQuery("#media-item-"+b.id).remove();break;default:wpFileError(b,swfuploadL10n.default_error)}}function cancelUpload(){swfu.cancelQueue()}jQuery(document).ready(function(a){a('input[type="radio"]',"#media-items").live("click",function(){var b=a(this).closest("tr");if(a(b).hasClass("align")){setUserSetting("align",a(this).val())}else{if(a(b).hasClass("image-size")){setUserSetting("imgsize",a(this).val())}}});a("button.button","#media-items").live("click",function(){var b=this.className||"";b=b.match(/url([^ '"]+)/);if(b&&b[1]){setUserSetting("urlbutton",b[1]);a(this).siblings(".urlfield").val(a(this).attr("title"))}})}); \ No newline at end of file diff --git a/wp-includes/js/swfupload/license.txt b/wp-includes/js/swfupload/license.txt new file mode 100644 index 0000000..8252ca9 --- /dev/null +++ b/wp-includes/js/swfupload/license.txt @@ -0,0 +1,32 @@ +/** + * SWFUpload: http://www.swfupload.org, http://swfupload.googlecode.com + * + * mmSWFUpload 1.0: Flash upload dialog - http://profandesign.se/swfupload/, http://www.vinterwebb.se/ + * + * SWFUpload is (c) 2006-2007 Lars Huring, Olov Nilzén and Mammon Media and is released under the MIT License: + * http://www.opensource.org/licenses/mit-license.php + * + * SWFUpload 2 is (c) 2007-2008 Jake Roberts and is released under the MIT License: + * http://www.opensource.org/licenses/mit-license.php + * + */ + +The MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/wp-includes/js/swfupload/plugins/swfupload.cookies.js b/wp-includes/js/swfupload/plugins/swfupload.cookies.js new file mode 100644 index 0000000..dd3b78b --- /dev/null +++ b/wp-includes/js/swfupload/plugins/swfupload.cookies.js @@ -0,0 +1,53 @@ +/* + Cookie Plug-in + + This plug in automatically gets all the cookies for this site and adds them to the post_params. + Cookies are loaded only on initialization. The refreshCookies function can be called to update the post_params. + The cookies will override any other post params with the same name. +*/ + +var SWFUpload; +if (typeof(SWFUpload) === "function") { + SWFUpload.prototype.initSettings = function (oldInitSettings) { + return function () { + if (typeof(oldInitSettings) === "function") { + oldInitSettings.call(this); + } + + this.refreshCookies(false); // The false parameter must be sent since SWFUpload has not initialzed at this point + }; + }(SWFUpload.prototype.initSettings); + + // refreshes the post_params and updates SWFUpload. The sendToFlash parameters is optional and defaults to True + SWFUpload.prototype.refreshCookies = function (sendToFlash) { + if (sendToFlash === undefined) { + sendToFlash = true; + } + sendToFlash = !!sendToFlash; + + // Get the post_params object + var postParams = this.settings.post_params; + + // Get the cookies + var i, cookieArray = document.cookie.split(';'), caLength = cookieArray.length, c, eqIndex, name, value; + for (i = 0; i < caLength; i++) { + c = cookieArray[i]; + + // Left Trim spaces + while (c.charAt(0) === " ") { + c = c.substring(1, c.length); + } + eqIndex = c.indexOf("="); + if (eqIndex > 0) { + name = c.substring(0, eqIndex); + value = c.substring(eqIndex + 1); + postParams[name] = value; + } + } + + if (sendToFlash) { + this.setPostParams(postParams); + } + }; + +} diff --git a/wp-includes/js/swfupload/plugins/swfupload.queue.js b/wp-includes/js/swfupload/plugins/swfupload.queue.js new file mode 100644 index 0000000..f09933e --- /dev/null +++ b/wp-includes/js/swfupload/plugins/swfupload.queue.js @@ -0,0 +1,98 @@ +/* + Queue Plug-in + + Features: + *Adds a cancelQueue() method for cancelling the entire queue. + *All queued files are uploaded when startUpload() is called. + *If false is returned from uploadComplete then the queue upload is stopped. + If false is not returned (strict comparison) then the queue upload is continued. + *Adds a QueueComplete event that is fired when all the queued files have finished uploading. + Set the event handler with the queue_complete_handler setting. + + */ + +var SWFUpload; +if (typeof(SWFUpload) === "function") { + SWFUpload.queue = {}; + + SWFUpload.prototype.initSettings = (function (oldInitSettings) { + return function () { + if (typeof(oldInitSettings) === "function") { + oldInitSettings.call(this); + } + + this.queueSettings = {}; + + this.queueSettings.queue_cancelled_flag = false; + this.queueSettings.queue_upload_count = 0; + + this.queueSettings.user_upload_complete_handler = this.settings.upload_complete_handler; + this.queueSettings.user_upload_start_handler = this.settings.upload_start_handler; + this.settings.upload_complete_handler = SWFUpload.queue.uploadCompleteHandler; + this.settings.upload_start_handler = SWFUpload.queue.uploadStartHandler; + + this.settings.queue_complete_handler = this.settings.queue_complete_handler || null; + }; + })(SWFUpload.prototype.initSettings); + + SWFUpload.prototype.startUpload = function (fileID) { + this.queueSettings.queue_cancelled_flag = false; + this.callFlash("StartUpload", [fileID]); + }; + + SWFUpload.prototype.cancelQueue = function () { + this.queueSettings.queue_cancelled_flag = true; + this.stopUpload(); + + var stats = this.getStats(); + while (stats.files_queued > 0) { + this.cancelUpload(); + stats = this.getStats(); + } + }; + + SWFUpload.queue.uploadStartHandler = function (file) { + var returnValue; + if (typeof(this.queueSettings.user_upload_start_handler) === "function") { + returnValue = this.queueSettings.user_upload_start_handler.call(this, file); + } + + // To prevent upload a real "FALSE" value must be returned, otherwise default to a real "TRUE" value. + returnValue = (returnValue === false) ? false : true; + + this.queueSettings.queue_cancelled_flag = !returnValue; + + return returnValue; + }; + + SWFUpload.queue.uploadCompleteHandler = function (file) { + var user_upload_complete_handler = this.queueSettings.user_upload_complete_handler; + var continueUpload; + + if (file.filestatus === SWFUpload.FILE_STATUS.COMPLETE) { + this.queueSettings.queue_upload_count++; + } + + if (typeof(user_upload_complete_handler) === "function") { + continueUpload = (user_upload_complete_handler.call(this, file) === false) ? false : true; + } else if (file.filestatus === SWFUpload.FILE_STATUS.QUEUED) { + // If the file was stopped and re-queued don't restart the upload + continueUpload = false; + } else { + continueUpload = true; + } + + if (continueUpload) { + var stats = this.getStats(); + if (stats.files_queued > 0 && this.queueSettings.queue_cancelled_flag === false) { + this.startUpload(); + } else if (this.queueSettings.queue_cancelled_flag === false) { + this.queueEvent("queue_complete_handler", [this.queueSettings.queue_upload_count]); + this.queueSettings.queue_upload_count = 0; + } else { + this.queueSettings.queue_cancelled_flag = false; + this.queueSettings.queue_upload_count = 0; + } + } + }; +} diff --git a/wp-includes/js/swfupload/plugins/swfupload.speed.js b/wp-includes/js/swfupload/plugins/swfupload.speed.js new file mode 100644 index 0000000..3245c9c --- /dev/null +++ b/wp-includes/js/swfupload/plugins/swfupload.speed.js @@ -0,0 +1,342 @@ +/* + Speed Plug-in + + Features: + *Adds several properties to the 'file' object indicated upload speed, time left, upload time, etc. + - currentSpeed -- String indicating the upload speed, bytes per second + - averageSpeed -- Overall average upload speed, bytes per second + - movingAverageSpeed -- Speed over averaged over the last several measurements, bytes per second + - timeRemaining -- Estimated remaining upload time in seconds + - timeElapsed -- Number of seconds passed for this upload + - percentUploaded -- Percentage of the file uploaded (0 to 100) + - sizeUploaded -- Formatted size uploaded so far, bytes + + *Adds setting 'moving_average_history_size' for defining the window size used to calculate the moving average speed. + + *Adds several Formatting functions for formatting that values provided on the file object. + - SWFUpload.speed.formatBPS(bps) -- outputs string formatted in the best units (Gbps, Mbps, Kbps, bps) + - SWFUpload.speed.formatTime(seconds) -- outputs string formatted in the best units (x Hr y M z S) + - SWFUpload.speed.formatSize(bytes) -- outputs string formatted in the best units (w GB x MB y KB z B ) + - SWFUpload.speed.formatPercent(percent) -- outputs string formatted with a percent sign (x.xx %) + - SWFUpload.speed.formatUnits(baseNumber, divisionArray, unitLabelArray, fractionalBoolean) + - Formats a number using the division array to determine how to apply the labels in the Label Array + - factionalBoolean indicates whether the number should be returned as a single fractional number with a unit (speed) + or as several numbers labeled with units (time) + */ + +var SWFUpload; +if (typeof(SWFUpload) === "function") { + SWFUpload.speed = {}; + + SWFUpload.prototype.initSettings = (function (oldInitSettings) { + return function () { + if (typeof(oldInitSettings) === "function") { + oldInitSettings.call(this); + } + + this.ensureDefault = function (settingName, defaultValue) { + this.settings[settingName] = (this.settings[settingName] == undefined) ? defaultValue : this.settings[settingName]; + }; + + // List used to keep the speed stats for the files we are tracking + this.fileSpeedStats = {}; + this.speedSettings = {}; + + this.ensureDefault("moving_average_history_size", "10"); + + this.speedSettings.user_file_queued_handler = this.settings.file_queued_handler; + this.speedSettings.user_file_queue_error_handler = this.settings.file_queue_error_handler; + this.speedSettings.user_upload_start_handler = this.settings.upload_start_handler; + this.speedSettings.user_upload_error_handler = this.settings.upload_error_handler; + this.speedSettings.user_upload_progress_handler = this.settings.upload_progress_handler; + this.speedSettings.user_upload_success_handler = this.settings.upload_success_handler; + this.speedSettings.user_upload_complete_handler = this.settings.upload_complete_handler; + + this.settings.file_queued_handler = SWFUpload.speed.fileQueuedHandler; + this.settings.file_queue_error_handler = SWFUpload.speed.fileQueueErrorHandler; + this.settings.upload_start_handler = SWFUpload.speed.uploadStartHandler; + this.settings.upload_error_handler = SWFUpload.speed.uploadErrorHandler; + this.settings.upload_progress_handler = SWFUpload.speed.uploadProgressHandler; + this.settings.upload_success_handler = SWFUpload.speed.uploadSuccessHandler; + this.settings.upload_complete_handler = SWFUpload.speed.uploadCompleteHandler; + + delete this.ensureDefault; + }; + })(SWFUpload.prototype.initSettings); + + + SWFUpload.speed.fileQueuedHandler = function (file) { + if (typeof this.speedSettings.user_file_queued_handler === "function") { + file = SWFUpload.speed.extendFile(file); + + return this.speedSettings.user_file_queued_handler.call(this, file); + } + }; + + SWFUpload.speed.fileQueueErrorHandler = function (file, errorCode, message) { + if (typeof this.speedSettings.user_file_queue_error_handler === "function") { + file = SWFUpload.speed.extendFile(file); + + return this.speedSettings.user_file_queue_error_handler.call(this, file, errorCode, message); + } + }; + + SWFUpload.speed.uploadStartHandler = function (file) { + if (typeof this.speedSettings.user_upload_start_handler === "function") { + file = SWFUpload.speed.extendFile(file, this.fileSpeedStats); + return this.speedSettings.user_upload_start_handler.call(this, file); + } + }; + + SWFUpload.speed.uploadErrorHandler = function (file, errorCode, message) { + file = SWFUpload.speed.extendFile(file, this.fileSpeedStats); + SWFUpload.speed.removeTracking(file, this.fileSpeedStats); + + if (typeof this.speedSettings.user_upload_error_handler === "function") { + return this.speedSettings.user_upload_error_handler.call(this, file, errorCode, message); + } + }; + SWFUpload.speed.uploadProgressHandler = function (file, bytesComplete, bytesTotal) { + this.updateTracking(file, bytesComplete); + file = SWFUpload.speed.extendFile(file, this.fileSpeedStats); + + if (typeof this.speedSettings.user_upload_progress_handler === "function") { + return this.speedSettings.user_upload_progress_handler.call(this, file, bytesComplete, bytesTotal); + } + }; + + SWFUpload.speed.uploadSuccessHandler = function (file, serverData) { + if (typeof this.speedSettings.user_upload_success_handler === "function") { + file = SWFUpload.speed.extendFile(file, this.fileSpeedStats); + return this.speedSettings.user_upload_success_handler.call(this, file, serverData); + } + }; + SWFUpload.speed.uploadCompleteHandler = function (file) { + file = SWFUpload.speed.extendFile(file, this.fileSpeedStats); + SWFUpload.speed.removeTracking(file, this.fileSpeedStats); + + if (typeof this.speedSettings.user_upload_complete_handler === "function") { + return this.speedSettings.user_upload_complete_handler.call(this, file); + } + }; + + // Private: extends the file object with the speed plugin values + SWFUpload.speed.extendFile = function (file, trackingList) { + var tracking; + + if (trackingList) { + tracking = trackingList[file.id]; + } + + if (tracking) { + file.currentSpeed = tracking.currentSpeed; + file.averageSpeed = tracking.averageSpeed; + file.movingAverageSpeed = tracking.movingAverageSpeed; + file.timeRemaining = tracking.timeRemaining; + file.timeElapsed = tracking.timeElapsed; + file.percentUploaded = tracking.percentUploaded; + file.sizeUploaded = tracking.bytesUploaded; + + } else { + file.currentSpeed = 0; + file.averageSpeed = 0; + file.movingAverageSpeed = 0; + file.timeRemaining = 0; + file.timeElapsed = 0; + file.percentUploaded = 0; + file.sizeUploaded = 0; + } + + return file; + }; + + // Private: Updates the speed tracking object, or creates it if necessary + SWFUpload.prototype.updateTracking = function (file, bytesUploaded) { + var tracking = this.fileSpeedStats[file.id]; + if (!tracking) { + this.fileSpeedStats[file.id] = tracking = {}; + } + + // Sanity check inputs + bytesUploaded = bytesUploaded || tracking.bytesUploaded || 0; + if (bytesUploaded < 0) { + bytesUploaded = 0; + } + if (bytesUploaded > file.size) { + bytesUploaded = file.size; + } + + var tickTime = (new Date()).getTime(); + if (!tracking.startTime) { + tracking.startTime = (new Date()).getTime(); + tracking.lastTime = tracking.startTime; + tracking.currentSpeed = 0; + tracking.averageSpeed = 0; + tracking.movingAverageSpeed = 0; + tracking.movingAverageHistory = []; + tracking.timeRemaining = 0; + tracking.timeElapsed = 0; + tracking.percentUploaded = bytesUploaded / file.size; + tracking.bytesUploaded = bytesUploaded; + } else if (tracking.startTime > tickTime) { + this.debug("When backwards in time"); + } else { + // Get time and deltas + var now = (new Date()).getTime(); + var lastTime = tracking.lastTime; + var deltaTime = now - lastTime; + var deltaBytes = bytesUploaded - tracking.bytesUploaded; + + if (deltaBytes === 0 || deltaTime === 0) { + return tracking; + } + + // Update tracking object + tracking.lastTime = now; + tracking.bytesUploaded = bytesUploaded; + + // Calculate speeds + tracking.currentSpeed = (deltaBytes * 8 ) / (deltaTime / 1000); + tracking.averageSpeed = (tracking.bytesUploaded * 8) / ((now - tracking.startTime) / 1000); + + // Calculate moving average + tracking.movingAverageHistory.push(tracking.currentSpeed); + if (tracking.movingAverageHistory.length > this.settings.moving_average_history_size) { + tracking.movingAverageHistory.shift(); + } + + tracking.movingAverageSpeed = SWFUpload.speed.calculateMovingAverage(tracking.movingAverageHistory); + + // Update times + tracking.timeRemaining = (file.size - tracking.bytesUploaded) * 8 / tracking.movingAverageSpeed; + tracking.timeElapsed = (now - tracking.startTime) / 1000; + + // Update percent + tracking.percentUploaded = (tracking.bytesUploaded / file.size * 100); + } + + return tracking; + }; + SWFUpload.speed.removeTracking = function (file, trackingList) { + try { + trackingList[file.id] = null; + delete trackingList[file.id]; + } catch (ex) { + } + }; + + SWFUpload.speed.formatUnits = function (baseNumber, unitDivisors, unitLabels, singleFractional) { + var i, unit, unitDivisor, unitLabel; + + if (baseNumber === 0) { + return "0 " + unitLabels[unitLabels.length - 1]; + } + + if (singleFractional) { + unit = baseNumber; + unitLabel = unitLabels.length >= unitDivisors.length ? unitLabels[unitDivisors.length - 1] : ""; + for (i = 0; i < unitDivisors.length; i++) { + if (baseNumber >= unitDivisors[i]) { + unit = (baseNumber / unitDivisors[i]).toFixed(2); + unitLabel = unitLabels.length >= i ? " " + unitLabels[i] : ""; + break; + } + } + + return unit + unitLabel; + } else { + var formattedStrings = []; + var remainder = baseNumber; + + for (i = 0; i < unitDivisors.length; i++) { + unitDivisor = unitDivisors[i]; + unitLabel = unitLabels.length > i ? " " + unitLabels[i] : ""; + + unit = remainder / unitDivisor; + if (i < unitDivisors.length -1) { + unit = Math.floor(unit); + } else { + unit = unit.toFixed(2); + } + if (unit > 0) { + remainder = remainder % unitDivisor; + + formattedStrings.push(unit + unitLabel); + } + } + + return formattedStrings.join(" "); + } + }; + + SWFUpload.speed.formatBPS = function (baseNumber) { + var bpsUnits = [1073741824, 1048576, 1024, 1], bpsUnitLabels = ["Gbps", "Mbps", "Kbps", "bps"]; + return SWFUpload.speed.formatUnits(baseNumber, bpsUnits, bpsUnitLabels, true); + + }; + SWFUpload.speed.formatTime = function (baseNumber) { + var timeUnits = [86400, 3600, 60, 1], timeUnitLabels = ["d", "h", "m", "s"]; + return SWFUpload.speed.formatUnits(baseNumber, timeUnits, timeUnitLabels, false); + + }; + SWFUpload.speed.formatBytes = function (baseNumber) { + var sizeUnits = [1073741824, 1048576, 1024, 1], sizeUnitLabels = ["GB", "MB", "KB", "bytes"]; + return SWFUpload.speed.formatUnits(baseNumber, sizeUnits, sizeUnitLabels, true); + + }; + SWFUpload.speed.formatPercent = function (baseNumber) { + return baseNumber.toFixed(2) + " %"; + }; + + SWFUpload.speed.calculateMovingAverage = function (history) { + var vals = [], size, sum = 0.0, mean = 0.0, varianceTemp = 0.0, variance = 0.0, standardDev = 0.0; + var i; + var mSum = 0, mCount = 0; + + size = history.length; + + // Check for sufficient data + if (size >= 8) { + // Clone the array and Calculate sum of the values + for (i = 0; i < size; i++) { + vals[i] = history[i]; + sum += vals[i]; + } + + mean = sum / size; + + // Calculate variance for the set + for (i = 0; i < size; i++) { + varianceTemp += Math.pow((vals[i] - mean), 2); + } + + variance = varianceTemp / size; + standardDev = Math.sqrt(variance); + + //Standardize the Data + for (i = 0; i < size; i++) { + vals[i] = (vals[i] - mean) / standardDev; + } + + // Calculate the average excluding outliers + var deviationRange = 2.0; + for (i = 0; i < size; i++) { + + if (vals[i] <= deviationRange && vals[i] >= -deviationRange) { + mCount++; + mSum += history[i]; + } + } + + } else { + // Calculate the average (not enough data points to remove outliers) + mCount = size; + for (i = 0; i < size; i++) { + mSum += history[i]; + } + } + + return mSum / mCount; + }; + +} \ No newline at end of file diff --git a/wp-includes/js/swfupload/plugins/swfupload.swfobject.js b/wp-includes/js/swfupload/plugins/swfupload.swfobject.js new file mode 100644 index 0000000..cb7aa80 --- /dev/null +++ b/wp-includes/js/swfupload/plugins/swfupload.swfobject.js @@ -0,0 +1,105 @@ +/* + SWFUpload.SWFObject Plugin + + Summary: + This plugin uses SWFObject to embed SWFUpload dynamically in the page. SWFObject provides accurate Flash Player detection and DOM Ready loading. + This plugin replaces the Graceful Degradation plugin. + + Features: + * swfupload_load_failed_hander event + * swfupload_pre_load_handler event + * minimum_flash_version setting (default: "9.0.28") + * SWFUpload.onload event for early loading + + Usage: + Provide handlers and settings as needed. When using the SWFUpload.SWFObject plugin you should initialize SWFUploading + in SWFUpload.onload rather than in window.onload. When initialized this way SWFUpload can load earlier preventing the UI flicker + that was seen using the Graceful Degradation plugin. + + + + Notes: + You must provide set minimum_flash_version setting to "8" if you are using SWFUpload for Flash Player 8. + The swfuploadLoadFailed event is only fired if the minimum version of Flash Player is not met. Other issues such as missing SWF files, browser bugs + or corrupt Flash Player installations will not trigger this event. + The swfuploadPreLoad event is fired as soon as the minimum version of Flash Player is found. It does not wait for SWFUpload to load and can + be used to prepare the SWFUploadUI and hide alternate content. + swfobject's onDomReady event is cross-browser safe but will default to the window.onload event when DOMReady is not supported by the browser. + Early DOM Loading is supported in major modern browsers but cannot be guaranteed for every browser ever made. +*/ + + +// SWFObject v2.1 must be loaded + +var SWFUpload; +if (typeof(SWFUpload) === "function") { + SWFUpload.onload = function () {}; + + swfobject.addDomLoadEvent(function () { + if (typeof(SWFUpload.onload) === "function") { + setTimeout(function(){SWFUpload.onload.call(window);}, 200); + } + }); + + SWFUpload.prototype.initSettings = (function (oldInitSettings) { + return function () { + if (typeof(oldInitSettings) === "function") { + oldInitSettings.call(this); + } + + this.ensureDefault = function (settingName, defaultValue) { + this.settings[settingName] = (this.settings[settingName] == undefined) ? defaultValue : this.settings[settingName]; + }; + + this.ensureDefault("minimum_flash_version", "9.0.28"); + this.ensureDefault("swfupload_pre_load_handler", null); + this.ensureDefault("swfupload_load_failed_handler", null); + + delete this.ensureDefault; + + }; + })(SWFUpload.prototype.initSettings); + + + SWFUpload.prototype.loadFlash = function (oldLoadFlash) { + return function () { + var hasFlash = swfobject.hasFlashPlayerVersion(this.settings.minimum_flash_version); + + if (hasFlash) { + this.queueEvent("swfupload_pre_load_handler"); + if (typeof(oldLoadFlash) === "function") { + oldLoadFlash.call(this); + } + } else { + this.queueEvent("swfupload_load_failed_handler"); + } + }; + + }(SWFUpload.prototype.loadFlash); + + SWFUpload.prototype.displayDebugInfo = function (oldDisplayDebugInfo) { + return function () { + if (typeof(oldDisplayDebugInfo) === "function") { + oldDisplayDebugInfo.call(this); + } + + this.debug( + [ + "SWFUpload.SWFObject Plugin settings:", "\n", + "\t", "minimum_flash_version: ", this.settings.minimum_flash_version, "\n", + "\t", "swfupload_pre_load_handler assigned: ", (typeof(this.settings.swfupload_pre_load_handler) === "function").toString(), "\n", + "\t", "swfupload_load_failed_handler assigned: ", (typeof(this.settings.swfupload_load_failed_handler) === "function").toString(), "\n", + ].join("") + ); + }; + }(SWFUpload.prototype.displayDebugInfo); +} diff --git a/wp-includes/js/swfupload/swfupload.js b/wp-includes/js/swfupload/swfupload.js new file mode 100644 index 0000000..9c950d4 --- /dev/null +++ b/wp-includes/js/swfupload/swfupload.js @@ -0,0 +1,980 @@ +/** + * SWFUpload: http://www.swfupload.org, http://swfupload.googlecode.com + * + * mmSWFUpload 1.0: Flash upload dialog - http://profandesign.se/swfupload/, http://www.vinterwebb.se/ + * + * SWFUpload is (c) 2006-2007 Lars Huring, Olov Nilz�n and Mammon Media and is released under the MIT License: + * http://www.opensource.org/licenses/mit-license.php + * + * SWFUpload 2 is (c) 2007-2008 Jake Roberts and is released under the MIT License: + * http://www.opensource.org/licenses/mit-license.php + * + */ + + +/* ******************* */ +/* Constructor & Init */ +/* ******************* */ +var SWFUpload; + +if (SWFUpload == undefined) { + SWFUpload = function (settings) { + this.initSWFUpload(settings); + }; +} + +SWFUpload.prototype.initSWFUpload = function (settings) { + try { + this.customSettings = {}; // A container where developers can place their own settings associated with this instance. + this.settings = settings; + this.eventQueue = []; + this.movieName = "SWFUpload_" + SWFUpload.movieCount++; + this.movieElement = null; + + + // Setup global control tracking + SWFUpload.instances[this.movieName] = this; + + // Load the settings. Load the Flash movie. + this.initSettings(); + this.loadFlash(); + this.displayDebugInfo(); + } catch (ex) { + delete SWFUpload.instances[this.movieName]; + throw ex; + } +}; + +/* *************** */ +/* Static Members */ +/* *************** */ +SWFUpload.instances = {}; +SWFUpload.movieCount = 0; +SWFUpload.version = "2.2.0 2009-03-25"; +SWFUpload.QUEUE_ERROR = { + QUEUE_LIMIT_EXCEEDED : -100, + FILE_EXCEEDS_SIZE_LIMIT : -110, + ZERO_BYTE_FILE : -120, + INVALID_FILETYPE : -130 +}; +SWFUpload.UPLOAD_ERROR = { + HTTP_ERROR : -200, + MISSING_UPLOAD_URL : -210, + IO_ERROR : -220, + SECURITY_ERROR : -230, + UPLOAD_LIMIT_EXCEEDED : -240, + UPLOAD_FAILED : -250, + SPECIFIED_FILE_ID_NOT_FOUND : -260, + FILE_VALIDATION_FAILED : -270, + FILE_CANCELLED : -280, + UPLOAD_STOPPED : -290 +}; +SWFUpload.FILE_STATUS = { + QUEUED : -1, + IN_PROGRESS : -2, + ERROR : -3, + COMPLETE : -4, + CANCELLED : -5 +}; +SWFUpload.BUTTON_ACTION = { + SELECT_FILE : -100, + SELECT_FILES : -110, + START_UPLOAD : -120 +}; +SWFUpload.CURSOR = { + ARROW : -1, + HAND : -2 +}; +SWFUpload.WINDOW_MODE = { + WINDOW : "window", + TRANSPARENT : "transparent", + OPAQUE : "opaque" +}; + +// Private: takes a URL, determines if it is relative and converts to an absolute URL +// using the current site. Only processes the URL if it can, otherwise returns the URL untouched +SWFUpload.completeURL = function(url) { + if (typeof(url) !== "string" || url.match(/^https?:\/\//i) || url.match(/^\//)) { + return url; + } + + var currentURL = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ":" + window.location.port : ""); + + var indexSlash = window.location.pathname.lastIndexOf("/"); + if (indexSlash <= 0) { + path = "/"; + } else { + path = window.location.pathname.substr(0, indexSlash) + "/"; + } + + return /*currentURL +*/ path + url; + +}; + + +/* ******************** */ +/* Instance Members */ +/* ******************** */ + +// Private: initSettings ensures that all the +// settings are set, getting a default value if one was not assigned. +SWFUpload.prototype.initSettings = function () { + this.ensureDefault = function (settingName, defaultValue) { + this.settings[settingName] = (this.settings[settingName] == undefined) ? defaultValue : this.settings[settingName]; + }; + + // Upload backend settings + this.ensureDefault("upload_url", ""); + this.ensureDefault("preserve_relative_urls", false); + this.ensureDefault("file_post_name", "Filedata"); + this.ensureDefault("post_params", {}); + this.ensureDefault("use_query_string", false); + this.ensureDefault("requeue_on_error", false); + this.ensureDefault("http_success", []); + this.ensureDefault("assume_success_timeout", 0); + + // File Settings + this.ensureDefault("file_types", "*.*"); + this.ensureDefault("file_types_description", "All Files"); + this.ensureDefault("file_size_limit", 0); // Default zero means "unlimited" + this.ensureDefault("file_upload_limit", 0); + this.ensureDefault("file_queue_limit", 0); + + // Flash Settings + this.ensureDefault("flash_url", "swfupload.swf"); + this.ensureDefault("prevent_swf_caching", true); + + // Button Settings + this.ensureDefault("button_image_url", ""); + this.ensureDefault("button_width", 1); + this.ensureDefault("button_height", 1); + this.ensureDefault("button_text", ""); + this.ensureDefault("button_text_style", "color: #000000; font-size: 16pt;"); + this.ensureDefault("button_text_top_padding", 0); + this.ensureDefault("button_text_left_padding", 0); + this.ensureDefault("button_action", SWFUpload.BUTTON_ACTION.SELECT_FILES); + this.ensureDefault("button_disabled", false); + this.ensureDefault("button_placeholder_id", ""); + this.ensureDefault("button_placeholder", null); + this.ensureDefault("button_cursor", SWFUpload.CURSOR.ARROW); + this.ensureDefault("button_window_mode", SWFUpload.WINDOW_MODE.WINDOW); + + // Debug Settings + this.ensureDefault("debug", false); + this.settings.debug_enabled = this.settings.debug; // Here to maintain v2 API + + // Event Handlers + this.settings.return_upload_start_handler = this.returnUploadStart; + this.ensureDefault("swfupload_loaded_handler", null); + this.ensureDefault("file_dialog_start_handler", null); + this.ensureDefault("file_queued_handler", null); + this.ensureDefault("file_queue_error_handler", null); + this.ensureDefault("file_dialog_complete_handler", null); + + this.ensureDefault("upload_start_handler", null); + this.ensureDefault("upload_progress_handler", null); + this.ensureDefault("upload_error_handler", null); + this.ensureDefault("upload_success_handler", null); + this.ensureDefault("upload_complete_handler", null); + + this.ensureDefault("debug_handler", this.debugMessage); + + this.ensureDefault("custom_settings", {}); + + // Other settings + this.customSettings = this.settings.custom_settings; + + // Update the flash url if needed + if (!!this.settings.prevent_swf_caching) { + this.settings.flash_url = this.settings.flash_url + (this.settings.flash_url.indexOf("?") < 0 ? "?" : "&") + "preventswfcaching=" + new Date().getTime(); + } + + if (!this.settings.preserve_relative_urls) { + //this.settings.flash_url = SWFUpload.completeURL(this.settings.flash_url); // Don't need to do this one since flash doesn't look at it + this.settings.upload_url = SWFUpload.completeURL(this.settings.upload_url); + this.settings.button_image_url = SWFUpload.completeURL(this.settings.button_image_url); + } + + delete this.ensureDefault; +}; + +// Private: loadFlash replaces the button_placeholder element with the flash movie. +SWFUpload.prototype.loadFlash = function () { + var targetElement, tempParent; + + // Make sure an element with the ID we are going to use doesn't already exist + if (document.getElementById(this.movieName) !== null) { + throw "ID " + this.movieName + " is already in use. The Flash Object could not be added"; + } + + // Get the element where we will be placing the flash movie + targetElement = document.getElementById(this.settings.button_placeholder_id) || this.settings.button_placeholder; + + if (targetElement == undefined) { + throw "Could not find the placeholder element: " + this.settings.button_placeholder_id; + } + + // Append the container and load the flash + tempParent = document.createElement("div"); + tempParent.innerHTML = this.getFlashHTML(); // Using innerHTML is non-standard but the only sensible way to dynamically add Flash in IE (and maybe other browsers) + targetElement.parentNode.replaceChild(tempParent.firstChild, targetElement); + + // Fix IE Flash/Form bug + if (window[this.movieName] == undefined) { + window[this.movieName] = this.getMovieElement(); + } + +}; + +// Private: getFlashHTML generates the object tag needed to embed the flash in to the document +SWFUpload.prototype.getFlashHTML = function () { + // Flash Satay object syntax: http://www.alistapart.com/articles/flashsatay + return ['', + '', + '', + '', + '', + '', + '', + ''].join(""); +}; + +// Private: getFlashVars builds the parameter string that will be passed +// to flash in the flashvars param. +SWFUpload.prototype.getFlashVars = function () { + // Build a string from the post param object + var paramString = this.buildParamString(); + var httpSuccessString = this.settings.http_success.join(","); + + // Build the parameter string + return ["movieName=", encodeURIComponent(this.movieName), + "&uploadURL=", encodeURIComponent(this.settings.upload_url), + "&useQueryString=", encodeURIComponent(this.settings.use_query_string), + "&requeueOnError=", encodeURIComponent(this.settings.requeue_on_error), + "&httpSuccess=", encodeURIComponent(httpSuccessString), + "&assumeSuccessTimeout=", encodeURIComponent(this.settings.assume_success_timeout), + "&params=", encodeURIComponent(paramString), + "&filePostName=", encodeURIComponent(this.settings.file_post_name), + "&fileTypes=", encodeURIComponent(this.settings.file_types), + "&fileTypesDescription=", encodeURIComponent(this.settings.file_types_description), + "&fileSizeLimit=", encodeURIComponent(this.settings.file_size_limit), + "&fileUploadLimit=", encodeURIComponent(this.settings.file_upload_limit), + "&fileQueueLimit=", encodeURIComponent(this.settings.file_queue_limit), + "&debugEnabled=", encodeURIComponent(this.settings.debug_enabled), + "&buttonImageURL=", encodeURIComponent(this.settings.button_image_url), + "&buttonWidth=", encodeURIComponent(this.settings.button_width), + "&buttonHeight=", encodeURIComponent(this.settings.button_height), + "&buttonText=", encodeURIComponent(this.settings.button_text), + "&buttonTextTopPadding=", encodeURIComponent(this.settings.button_text_top_padding), + "&buttonTextLeftPadding=", encodeURIComponent(this.settings.button_text_left_padding), + "&buttonTextStyle=", encodeURIComponent(this.settings.button_text_style), + "&buttonAction=", encodeURIComponent(this.settings.button_action), + "&buttonDisabled=", encodeURIComponent(this.settings.button_disabled), + "&buttonCursor=", encodeURIComponent(this.settings.button_cursor) + ].join(""); +}; + +// Public: getMovieElement retrieves the DOM reference to the Flash element added by SWFUpload +// The element is cached after the first lookup +SWFUpload.prototype.getMovieElement = function () { + if (this.movieElement == undefined) { + this.movieElement = document.getElementById(this.movieName); + } + + if (this.movieElement === null) { + throw "Could not find Flash element"; + } + + return this.movieElement; +}; + +// Private: buildParamString takes the name/value pairs in the post_params setting object +// and joins them up in to a string formatted "name=value&name=value" +SWFUpload.prototype.buildParamString = function () { + var postParams = this.settings.post_params; + var paramStringPairs = []; + + if (typeof(postParams) === "object") { + for (var name in postParams) { + if (postParams.hasOwnProperty(name)) { + paramStringPairs.push(encodeURIComponent(name.toString()) + "=" + encodeURIComponent(postParams[name].toString())); + } + } + } + + return paramStringPairs.join("&"); +}; + +// Public: Used to remove a SWFUpload instance from the page. This method strives to remove +// all references to the SWF, and other objects so memory is properly freed. +// Returns true if everything was destroyed. Returns a false if a failure occurs leaving SWFUpload in an inconsistant state. +// Credits: Major improvements provided by steffen +SWFUpload.prototype.destroy = function () { + try { + // Make sure Flash is done before we try to remove it + this.cancelUpload(null, false); + + + // Remove the SWFUpload DOM nodes + var movieElement = null; + movieElement = this.getMovieElement(); + + if (movieElement && typeof(movieElement.CallFunction) === "unknown") { // We only want to do this in IE + // Loop through all the movie's properties and remove all function references (DOM/JS IE 6/7 memory leak workaround) + for (var i in movieElement) { + try { + if (typeof(movieElement[i]) === "function") { + movieElement[i] = null; + } + } catch (ex1) {} + } + + // Remove the Movie Element from the page + try { + movieElement.parentNode.removeChild(movieElement); + } catch (ex) {} + } + + // Remove IE form fix reference + window[this.movieName] = null; + + // Destroy other references + SWFUpload.instances[this.movieName] = null; + delete SWFUpload.instances[this.movieName]; + + this.movieElement = null; + this.settings = null; + this.customSettings = null; + this.eventQueue = null; + this.movieName = null; + + + return true; + } catch (ex2) { + return false; + } +}; + + +// Public: displayDebugInfo prints out settings and configuration +// information about this SWFUpload instance. +// This function (and any references to it) can be deleted when placing +// SWFUpload in production. +SWFUpload.prototype.displayDebugInfo = function () { + this.debug( + [ + "---SWFUpload Instance Info---\n", + "Version: ", SWFUpload.version, "\n", + "Movie Name: ", this.movieName, "\n", + "Settings:\n", + "\t", "upload_url: ", this.settings.upload_url, "\n", + "\t", "flash_url: ", this.settings.flash_url, "\n", + "\t", "use_query_string: ", this.settings.use_query_string.toString(), "\n", + "\t", "requeue_on_error: ", this.settings.requeue_on_error.toString(), "\n", + "\t", "http_success: ", this.settings.http_success.join(", "), "\n", + "\t", "assume_success_timeout: ", this.settings.assume_success_timeout, "\n", + "\t", "file_post_name: ", this.settings.file_post_name, "\n", + "\t", "post_params: ", this.settings.post_params.toString(), "\n", + "\t", "file_types: ", this.settings.file_types, "\n", + "\t", "file_types_description: ", this.settings.file_types_description, "\n", + "\t", "file_size_limit: ", this.settings.file_size_limit, "\n", + "\t", "file_upload_limit: ", this.settings.file_upload_limit, "\n", + "\t", "file_queue_limit: ", this.settings.file_queue_limit, "\n", + "\t", "debug: ", this.settings.debug.toString(), "\n", + + "\t", "prevent_swf_caching: ", this.settings.prevent_swf_caching.toString(), "\n", + + "\t", "button_placeholder_id: ", this.settings.button_placeholder_id.toString(), "\n", + "\t", "button_placeholder: ", (this.settings.button_placeholder ? "Set" : "Not Set"), "\n", + "\t", "button_image_url: ", this.settings.button_image_url.toString(), "\n", + "\t", "button_width: ", this.settings.button_width.toString(), "\n", + "\t", "button_height: ", this.settings.button_height.toString(), "\n", + "\t", "button_text: ", this.settings.button_text.toString(), "\n", + "\t", "button_text_style: ", this.settings.button_text_style.toString(), "\n", + "\t", "button_text_top_padding: ", this.settings.button_text_top_padding.toString(), "\n", + "\t", "button_text_left_padding: ", this.settings.button_text_left_padding.toString(), "\n", + "\t", "button_action: ", this.settings.button_action.toString(), "\n", + "\t", "button_disabled: ", this.settings.button_disabled.toString(), "\n", + + "\t", "custom_settings: ", this.settings.custom_settings.toString(), "\n", + "Event Handlers:\n", + "\t", "swfupload_loaded_handler assigned: ", (typeof this.settings.swfupload_loaded_handler === "function").toString(), "\n", + "\t", "file_dialog_start_handler assigned: ", (typeof this.settings.file_dialog_start_handler === "function").toString(), "\n", + "\t", "file_queued_handler assigned: ", (typeof this.settings.file_queued_handler === "function").toString(), "\n", + "\t", "file_queue_error_handler assigned: ", (typeof this.settings.file_queue_error_handler === "function").toString(), "\n", + "\t", "upload_start_handler assigned: ", (typeof this.settings.upload_start_handler === "function").toString(), "\n", + "\t", "upload_progress_handler assigned: ", (typeof this.settings.upload_progress_handler === "function").toString(), "\n", + "\t", "upload_error_handler assigned: ", (typeof this.settings.upload_error_handler === "function").toString(), "\n", + "\t", "upload_success_handler assigned: ", (typeof this.settings.upload_success_handler === "function").toString(), "\n", + "\t", "upload_complete_handler assigned: ", (typeof this.settings.upload_complete_handler === "function").toString(), "\n", + "\t", "debug_handler assigned: ", (typeof this.settings.debug_handler === "function").toString(), "\n" + ].join("") + ); +}; + +/* Note: addSetting and getSetting are no longer used by SWFUpload but are included + the maintain v2 API compatibility +*/ +// Public: (Deprecated) addSetting adds a setting value. If the value given is undefined or null then the default_value is used. +SWFUpload.prototype.addSetting = function (name, value, default_value) { + if (value == undefined) { + return (this.settings[name] = default_value); + } else { + return (this.settings[name] = value); + } +}; + +// Public: (Deprecated) getSetting gets a setting. Returns an empty string if the setting was not found. +SWFUpload.prototype.getSetting = function (name) { + if (this.settings[name] != undefined) { + return this.settings[name]; + } + + return ""; +}; + + + +// Private: callFlash handles function calls made to the Flash element. +// Calls are made with a setTimeout for some functions to work around +// bugs in the ExternalInterface library. +SWFUpload.prototype.callFlash = function (functionName, argumentArray) { + argumentArray = argumentArray || []; + + var movieElement = this.getMovieElement(); + var returnValue, returnString; + + // Flash's method if calling ExternalInterface methods (code adapted from MooTools). + try { + returnString = movieElement.CallFunction('' + __flash__argumentsToXML(argumentArray, 0) + ''); + returnValue = eval(returnString); + } catch (ex) { + throw "Call to " + functionName + " failed"; + } + + // Unescape file post param values + if (returnValue != undefined && typeof returnValue.post === "object") { + returnValue = this.unescapeFilePostParams(returnValue); + } + + return returnValue; +}; + +/* ***************************** + -- Flash control methods -- + Your UI should use these + to operate SWFUpload + ***************************** */ + +// WARNING: this function does not work in Flash Player 10 +// Public: selectFile causes a File Selection Dialog window to appear. This +// dialog only allows 1 file to be selected. +SWFUpload.prototype.selectFile = function () { + this.callFlash("SelectFile"); +}; + +// WARNING: this function does not work in Flash Player 10 +// Public: selectFiles causes a File Selection Dialog window to appear/ This +// dialog allows the user to select any number of files +// Flash Bug Warning: Flash limits the number of selectable files based on the combined length of the file names. +// If the selection name length is too long the dialog will fail in an unpredictable manner. There is no work-around +// for this bug. +SWFUpload.prototype.selectFiles = function () { + this.callFlash("SelectFiles"); +}; + + +// Public: startUpload starts uploading the first file in the queue unless +// the optional parameter 'fileID' specifies the ID +SWFUpload.prototype.startUpload = function (fileID) { + this.callFlash("StartUpload", [fileID]); +}; + +// Public: cancelUpload cancels any queued file. The fileID parameter may be the file ID or index. +// If you do not specify a fileID the current uploading file or first file in the queue is cancelled. +// If you do not want the uploadError event to trigger you can specify false for the triggerErrorEvent parameter. +SWFUpload.prototype.cancelUpload = function (fileID, triggerErrorEvent) { + if (triggerErrorEvent !== false) { + triggerErrorEvent = true; + } + this.callFlash("CancelUpload", [fileID, triggerErrorEvent]); +}; + +// Public: stopUpload stops the current upload and requeues the file at the beginning of the queue. +// If nothing is currently uploading then nothing happens. +SWFUpload.prototype.stopUpload = function () { + this.callFlash("StopUpload"); +}; + +/* ************************ + * Settings methods + * These methods change the SWFUpload settings. + * SWFUpload settings should not be changed directly on the settings object + * since many of the settings need to be passed to Flash in order to take + * effect. + * *********************** */ + +// Public: getStats gets the file statistics object. +SWFUpload.prototype.getStats = function () { + return this.callFlash("GetStats"); +}; + +// Public: setStats changes the SWFUpload statistics. You shouldn't need to +// change the statistics but you can. Changing the statistics does not +// affect SWFUpload accept for the successful_uploads count which is used +// by the upload_limit setting to determine how many files the user may upload. +SWFUpload.prototype.setStats = function (statsObject) { + this.callFlash("SetStats", [statsObject]); +}; + +// Public: getFile retrieves a File object by ID or Index. If the file is +// not found then 'null' is returned. +SWFUpload.prototype.getFile = function (fileID) { + if (typeof(fileID) === "number") { + return this.callFlash("GetFileByIndex", [fileID]); + } else { + return this.callFlash("GetFile", [fileID]); + } +}; + +// Public: addFileParam sets a name/value pair that will be posted with the +// file specified by the Files ID. If the name already exists then the +// exiting value will be overwritten. +SWFUpload.prototype.addFileParam = function (fileID, name, value) { + return this.callFlash("AddFileParam", [fileID, name, value]); +}; + +// Public: removeFileParam removes a previously set (by addFileParam) name/value +// pair from the specified file. +SWFUpload.prototype.removeFileParam = function (fileID, name) { + this.callFlash("RemoveFileParam", [fileID, name]); +}; + +// Public: setUploadUrl changes the upload_url setting. +SWFUpload.prototype.setUploadURL = function (url) { + this.settings.upload_url = url.toString(); + this.callFlash("SetUploadURL", [url]); +}; + +// Public: setPostParams changes the post_params setting +SWFUpload.prototype.setPostParams = function (paramsObject) { + this.settings.post_params = paramsObject; + this.callFlash("SetPostParams", [paramsObject]); +}; + +// Public: addPostParam adds post name/value pair. Each name can have only one value. +SWFUpload.prototype.addPostParam = function (name, value) { + this.settings.post_params[name] = value; + this.callFlash("SetPostParams", [this.settings.post_params]); +}; + +// Public: removePostParam deletes post name/value pair. +SWFUpload.prototype.removePostParam = function (name) { + delete this.settings.post_params[name]; + this.callFlash("SetPostParams", [this.settings.post_params]); +}; + +// Public: setFileTypes changes the file_types setting and the file_types_description setting +SWFUpload.prototype.setFileTypes = function (types, description) { + this.settings.file_types = types; + this.settings.file_types_description = description; + this.callFlash("SetFileTypes", [types, description]); +}; + +// Public: setFileSizeLimit changes the file_size_limit setting +SWFUpload.prototype.setFileSizeLimit = function (fileSizeLimit) { + this.settings.file_size_limit = fileSizeLimit; + this.callFlash("SetFileSizeLimit", [fileSizeLimit]); +}; + +// Public: setFileUploadLimit changes the file_upload_limit setting +SWFUpload.prototype.setFileUploadLimit = function (fileUploadLimit) { + this.settings.file_upload_limit = fileUploadLimit; + this.callFlash("SetFileUploadLimit", [fileUploadLimit]); +}; + +// Public: setFileQueueLimit changes the file_queue_limit setting +SWFUpload.prototype.setFileQueueLimit = function (fileQueueLimit) { + this.settings.file_queue_limit = fileQueueLimit; + this.callFlash("SetFileQueueLimit", [fileQueueLimit]); +}; + +// Public: setFilePostName changes the file_post_name setting +SWFUpload.prototype.setFilePostName = function (filePostName) { + this.settings.file_post_name = filePostName; + this.callFlash("SetFilePostName", [filePostName]); +}; + +// Public: setUseQueryString changes the use_query_string setting +SWFUpload.prototype.setUseQueryString = function (useQueryString) { + this.settings.use_query_string = useQueryString; + this.callFlash("SetUseQueryString", [useQueryString]); +}; + +// Public: setRequeueOnError changes the requeue_on_error setting +SWFUpload.prototype.setRequeueOnError = function (requeueOnError) { + this.settings.requeue_on_error = requeueOnError; + this.callFlash("SetRequeueOnError", [requeueOnError]); +}; + +// Public: setHTTPSuccess changes the http_success setting +SWFUpload.prototype.setHTTPSuccess = function (http_status_codes) { + if (typeof http_status_codes === "string") { + http_status_codes = http_status_codes.replace(" ", "").split(","); + } + + this.settings.http_success = http_status_codes; + this.callFlash("SetHTTPSuccess", [http_status_codes]); +}; + +// Public: setHTTPSuccess changes the http_success setting +SWFUpload.prototype.setAssumeSuccessTimeout = function (timeout_seconds) { + this.settings.assume_success_timeout = timeout_seconds; + this.callFlash("SetAssumeSuccessTimeout", [timeout_seconds]); +}; + +// Public: setDebugEnabled changes the debug_enabled setting +SWFUpload.prototype.setDebugEnabled = function (debugEnabled) { + this.settings.debug_enabled = debugEnabled; + this.callFlash("SetDebugEnabled", [debugEnabled]); +}; + +// Public: setButtonImageURL loads a button image sprite +SWFUpload.prototype.setButtonImageURL = function (buttonImageURL) { + if (buttonImageURL == undefined) { + buttonImageURL = ""; + } + + this.settings.button_image_url = buttonImageURL; + this.callFlash("SetButtonImageURL", [buttonImageURL]); +}; + +// Public: setButtonDimensions resizes the Flash Movie and button +SWFUpload.prototype.setButtonDimensions = function (width, height) { + this.settings.button_width = width; + this.settings.button_height = height; + + var movie = this.getMovieElement(); + if (movie != undefined) { + movie.style.width = width + "px"; + movie.style.height = height + "px"; + } + + this.callFlash("SetButtonDimensions", [width, height]); +}; +// Public: setButtonText Changes the text overlaid on the button +SWFUpload.prototype.setButtonText = function (html) { + this.settings.button_text = html; + this.callFlash("SetButtonText", [html]); +}; +// Public: setButtonTextPadding changes the top and left padding of the text overlay +SWFUpload.prototype.setButtonTextPadding = function (left, top) { + this.settings.button_text_top_padding = top; + this.settings.button_text_left_padding = left; + this.callFlash("SetButtonTextPadding", [left, top]); +}; + +// Public: setButtonTextStyle changes the CSS used to style the HTML/Text overlaid on the button +SWFUpload.prototype.setButtonTextStyle = function (css) { + this.settings.button_text_style = css; + this.callFlash("SetButtonTextStyle", [css]); +}; +// Public: setButtonDisabled disables/enables the button +SWFUpload.prototype.setButtonDisabled = function (isDisabled) { + this.settings.button_disabled = isDisabled; + this.callFlash("SetButtonDisabled", [isDisabled]); +}; +// Public: setButtonAction sets the action that occurs when the button is clicked +SWFUpload.prototype.setButtonAction = function (buttonAction) { + this.settings.button_action = buttonAction; + this.callFlash("SetButtonAction", [buttonAction]); +}; + +// Public: setButtonCursor changes the mouse cursor displayed when hovering over the button +SWFUpload.prototype.setButtonCursor = function (cursor) { + this.settings.button_cursor = cursor; + this.callFlash("SetButtonCursor", [cursor]); +}; + +/* ******************************* + Flash Event Interfaces + These functions are used by Flash to trigger the various + events. + + All these functions a Private. + + Because the ExternalInterface library is buggy the event calls + are added to a queue and the queue then executed by a setTimeout. + This ensures that events are executed in a determinate order and that + the ExternalInterface bugs are avoided. +******************************* */ + +SWFUpload.prototype.queueEvent = function (handlerName, argumentArray) { + // Warning: Don't call this.debug inside here or you'll create an infinite loop + + if (argumentArray == undefined) { + argumentArray = []; + } else if (!(argumentArray instanceof Array)) { + argumentArray = [argumentArray]; + } + + var self = this; + if (typeof this.settings[handlerName] === "function") { + // Queue the event + this.eventQueue.push(function () { + this.settings[handlerName].apply(this, argumentArray); + }); + + // Execute the next queued event + setTimeout(function () { + self.executeNextEvent(); + }, 0); + + } else if (this.settings[handlerName] !== null) { + throw "Event handler " + handlerName + " is unknown or is not a function"; + } +}; + +// Private: Causes the next event in the queue to be executed. Since events are queued using a setTimeout +// we must queue them in order to garentee that they are executed in order. +SWFUpload.prototype.executeNextEvent = function () { + // Warning: Don't call this.debug inside here or you'll create an infinite loop + + var f = this.eventQueue ? this.eventQueue.shift() : null; + if (typeof(f) === "function") { + f.apply(this); + } +}; + +// Private: unescapeFileParams is part of a workaround for a flash bug where objects passed through ExternalInterface cannot have +// properties that contain characters that are not valid for JavaScript identifiers. To work around this +// the Flash Component escapes the parameter names and we must unescape again before passing them along. +SWFUpload.prototype.unescapeFilePostParams = function (file) { + var reg = /[$]([0-9a-f]{4})/i; + var unescapedPost = {}; + var uk; + + if (file != undefined) { + for (var k in file.post) { + if (file.post.hasOwnProperty(k)) { + uk = k; + var match; + while ((match = reg.exec(uk)) !== null) { + uk = uk.replace(match[0], String.fromCharCode(parseInt("0x" + match[1], 16))); + } + unescapedPost[uk] = file.post[k]; + } + } + + file.post = unescapedPost; + } + + return file; +}; + +// Private: Called by Flash to see if JS can call in to Flash (test if External Interface is working) +SWFUpload.prototype.testExternalInterface = function () { + try { + return this.callFlash("TestExternalInterface"); + } catch (ex) { + return false; + } +}; + +// Private: This event is called by Flash when it has finished loading. Don't modify this. +// Use the swfupload_loaded_handler event setting to execute custom code when SWFUpload has loaded. +SWFUpload.prototype.flashReady = function () { + // Check that the movie element is loaded correctly with its ExternalInterface methods defined + var movieElement = this.getMovieElement(); + + if (!movieElement) { + this.debug("Flash called back ready but the flash movie can't be found."); + return; + } + + this.cleanUp(movieElement); + + this.queueEvent("swfupload_loaded_handler"); +}; + +// Private: removes Flash added fuctions to the DOM node to prevent memory leaks in IE. +// This function is called by Flash each time the ExternalInterface functions are created. +SWFUpload.prototype.cleanUp = function (movieElement) { + // Pro-actively unhook all the Flash functions + try { + if (this.movieElement && typeof(movieElement.CallFunction) === "unknown") { // We only want to do this in IE + this.debug("Removing Flash functions hooks (this should only run in IE and should prevent memory leaks)"); + for (var key in movieElement) { + try { + if (typeof(movieElement[key]) === "function") { + movieElement[key] = null; + } + } catch (ex) { + } + } + } + } catch (ex1) { + + } + + // Fix Flashes own cleanup code so if the SWFMovie was removed from the page + // it doesn't display errors. + window["__flash__removeCallback"] = function (instance, name) { + try { + if (instance) { + instance[name] = null; + } + } catch (flashEx) { + + } + }; + +}; + + +/* This is a chance to do something before the browse window opens */ +SWFUpload.prototype.fileDialogStart = function () { + this.queueEvent("file_dialog_start_handler"); +}; + + +/* Called when a file is successfully added to the queue. */ +SWFUpload.prototype.fileQueued = function (file) { + file = this.unescapeFilePostParams(file); + this.queueEvent("file_queued_handler", file); +}; + + +/* Handle errors that occur when an attempt to queue a file fails. */ +SWFUpload.prototype.fileQueueError = function (file, errorCode, message) { + file = this.unescapeFilePostParams(file); + this.queueEvent("file_queue_error_handler", [file, errorCode, message]); +}; + +/* Called after the file dialog has closed and the selected files have been queued. + You could call startUpload here if you want the queued files to begin uploading immediately. */ +SWFUpload.prototype.fileDialogComplete = function (numFilesSelected, numFilesQueued, numFilesInQueue) { + this.queueEvent("file_dialog_complete_handler", [numFilesSelected, numFilesQueued, numFilesInQueue]); +}; + +SWFUpload.prototype.uploadStart = function (file) { + file = this.unescapeFilePostParams(file); + this.queueEvent("return_upload_start_handler", file); +}; + +SWFUpload.prototype.returnUploadStart = function (file) { + var returnValue; + if (typeof this.settings.upload_start_handler === "function") { + file = this.unescapeFilePostParams(file); + returnValue = this.settings.upload_start_handler.call(this, file); + } else if (this.settings.upload_start_handler != undefined) { + throw "upload_start_handler must be a function"; + } + + // Convert undefined to true so if nothing is returned from the upload_start_handler it is + // interpretted as 'true'. + if (returnValue === undefined) { + returnValue = true; + } + + returnValue = !!returnValue; + + this.callFlash("ReturnUploadStart", [returnValue]); +}; + + + +SWFUpload.prototype.uploadProgress = function (file, bytesComplete, bytesTotal) { + file = this.unescapeFilePostParams(file); + this.queueEvent("upload_progress_handler", [file, bytesComplete, bytesTotal]); +}; + +SWFUpload.prototype.uploadError = function (file, errorCode, message) { + file = this.unescapeFilePostParams(file); + this.queueEvent("upload_error_handler", [file, errorCode, message]); +}; + +SWFUpload.prototype.uploadSuccess = function (file, serverData, responseReceived) { + file = this.unescapeFilePostParams(file); + this.queueEvent("upload_success_handler", [file, serverData, responseReceived]); +}; + +SWFUpload.prototype.uploadComplete = function (file) { + file = this.unescapeFilePostParams(file); + this.queueEvent("upload_complete_handler", file); +}; + +/* Called by SWFUpload JavaScript and Flash functions when debug is enabled. By default it writes messages to the + internal debug console. You can override this event and have messages written where you want. */ +SWFUpload.prototype.debug = function (message) { + this.queueEvent("debug_handler", message); +}; + + +/* ********************************** + Debug Console + The debug console is a self contained, in page location + for debug message to be sent. The Debug Console adds + itself to the body if necessary. + + The console is automatically scrolled as messages appear. + + If you are using your own debug handler or when you deploy to production and + have debug disabled you can remove these functions to reduce the file size + and complexity. +********************************** */ + +// Private: debugMessage is the default debug_handler. If you want to print debug messages +// call the debug() function. When overriding the function your own function should +// check to see if the debug setting is true before outputting debug information. +SWFUpload.prototype.debugMessage = function (message) { + if (this.settings.debug) { + var exceptionMessage, exceptionValues = []; + + // Check for an exception object and print it nicely + if (typeof message === "object" && typeof message.name === "string" && typeof message.message === "string") { + for (var key in message) { + if (message.hasOwnProperty(key)) { + exceptionValues.push(key + ": " + message[key]); + } + } + exceptionMessage = exceptionValues.join("\n") || ""; + exceptionValues = exceptionMessage.split("\n"); + exceptionMessage = "EXCEPTION: " + exceptionValues.join("\nEXCEPTION: "); + SWFUpload.Console.writeLine(exceptionMessage); + } else { + SWFUpload.Console.writeLine(message); + } + } +}; + +SWFUpload.Console = {}; +SWFUpload.Console.writeLine = function (message) { + var console, documentForm; + + try { + console = document.getElementById("SWFUpload_Console"); + + if (!console) { + documentForm = document.createElement("form"); + document.getElementsByTagName("body")[0].appendChild(documentForm); + + console = document.createElement("textarea"); + console.id = "SWFUpload_Console"; + console.style.fontFamily = "monospace"; + console.setAttribute("wrap", "off"); + console.wrap = "off"; + console.style.overflow = "auto"; + console.style.width = "700px"; + console.style.height = "350px"; + console.style.margin = "5px"; + documentForm.appendChild(console); + } + + console.value += message + "\n"; + + console.scrollTop = console.scrollHeight - console.clientHeight; + } catch (ex) { + alert("Exception: " + ex.name + " Message: " + ex.message); + } +}; diff --git a/wp-includes/js/swfupload/swfupload.swf b/wp-includes/js/swfupload/swfupload.swf new file mode 100644 index 0000000..7d94bba Binary files /dev/null and b/wp-includes/js/swfupload/swfupload.swf differ diff --git a/wp-includes/js/thickbox/loadingAnimation.gif b/wp-includes/js/thickbox/loadingAnimation.gif new file mode 100644 index 0000000..030d4de Binary files /dev/null and b/wp-includes/js/thickbox/loadingAnimation.gif differ diff --git a/wp-includes/js/thickbox/macFFBgHack.png b/wp-includes/js/thickbox/macFFBgHack.png new file mode 100644 index 0000000..a032659 Binary files /dev/null and b/wp-includes/js/thickbox/macFFBgHack.png differ diff --git a/wp-includes/js/thickbox/thickbox.css b/wp-includes/js/thickbox/thickbox.css new file mode 100644 index 0000000..45e3f48 --- /dev/null +++ b/wp-includes/js/thickbox/thickbox.css @@ -0,0 +1,138 @@ +#TB_overlay { + background: #000; + opacity: 0.7; + filter: alpha(opacity=70); + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 100050; /* Above DFW. */ +} + +#TB_window { + position: fixed; + background: #fff; + z-index: 100050; /* Above DFW. */ + visibility: hidden; + text-align: left; + top: 50%; + left: 50%; + -webkit-box-shadow: 0 3px 6px rgba( 0, 0, 0, 0.3 ); + box-shadow: 0 3px 6px rgba( 0, 0, 0, 0.3 ); +} + +#TB_window img#TB_Image { + display: block; + margin: 15px 0 0 15px; + border-right: 1px solid #ccc; + border-bottom: 1px solid #ccc; + border-top: 1px solid #666; + border-left: 1px solid #666; +} + +#TB_caption{ + height: 25px; + padding: 7px 30px 10px 25px; + float: left; +} + +#TB_closeWindow { + height: 25px; + padding: 11px 25px 10px 0; + float: right; +} + +#TB_closeAjaxWindow { + float: right; +} + +#TB_closeAjaxWindow a { + text-decoration: none; +} + +#TB_ajaxWindowTitle { + float: left; + font-weight: 600; + line-height: 29px; + overflow: hidden; + padding: 0 29px 0 10px; + text-overflow: ellipsis; + white-space: nowrap; + width: calc( 100% - 39px ); +} + +#TB_title { + background: #fcfcfc; + border-bottom: 1px solid #dfdfdf; + height: 29px; +} + +#TB_ajaxContent { + clear: both; + padding: 2px 15px 15px 15px; + overflow: auto; + text-align: left; + line-height: 1.4em; +} + +#TB_ajaxContent.TB_modal { + padding: 15px; +} + +#TB_ajaxContent p { + padding: 5px 0px 5px 0px; +} + +#TB_load { + position: fixed; + display: none; + z-index: 103; + top: 50%; + left: 50%; + background-color: #E8E8E8; + border: 1px solid #555; + margin: -45px 0 0 -125px; + padding: 40px 15px 15px; +} + +#TB_HideSelect { + z-index: 99; + position: fixed; + top: 0; + left: 0; + background-color: #fff; + border: none; + filter: alpha(opacity=0); + opacity: 0; + height: 100%; + width: 100%; +} + +#TB_iframeContent { + clear: both; + border: none; +} + +.tb-close-icon { + color: #666; + text-align: center; + line-height: 29px; + width: 29px; + height: 29px; + position: absolute; + top: 0; + right: 0; +} + +.tb-close-icon:before { + content: '\f158'; + font: normal 20px/29px 'dashicons'; + speak: none; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.tb-close-icon:hover { + color: #2ea2cc; +} diff --git a/wp-includes/js/thickbox/thickbox.js b/wp-includes/js/thickbox/thickbox.js new file mode 100644 index 0000000..09c8a99 --- /dev/null +++ b/wp-includes/js/thickbox/thickbox.js @@ -0,0 +1,314 @@ +/* + * Thickbox 3.1 - One Box To Rule Them All. + * By Cody Lindley (http://www.codylindley.com) + * Copyright (c) 2007 cody lindley + * Licensed under the MIT License: http://www.opensource.org/licenses/mit-license.php +*/ + +if ( typeof tb_pathToImage != 'string' ) { + var tb_pathToImage = thickboxL10n.loadingAnimation; +} + +/*!!!!!!!!!!!!!!!!! edit below this line at your own risk !!!!!!!!!!!!!!!!!!!!!!!*/ + +//on page load call tb_init +jQuery(document).ready(function(){ + tb_init('a.thickbox, area.thickbox, input.thickbox');//pass where to apply thickbox + imgLoader = new Image();// preload image + imgLoader.src = tb_pathToImage; +}); + +//add thickbox to href & area elements that have a class of .thickbox +function tb_init(domChunk){ + jQuery('body').on('click', domChunk, tb_click); +} + +function tb_click(){ + var t = this.title || this.name || null; + var a = this.href || this.alt; + var g = this.rel || false; + tb_show(t,a,g); + this.blur(); + return false; +} + +function tb_show(caption, url, imageGroup) {//function called when the user clicks on a thickbox link + + try { + if (typeof document.body.style.maxHeight === "undefined") {//if IE 6 + jQuery("body","html").css({height: "100%", width: "100%"}); + jQuery("html").css("overflow","hidden"); + if (document.getElementById("TB_HideSelect") === null) {//iframe to hide select elements in ie6 + jQuery("body").append("
        "); + jQuery("#TB_overlay").click(tb_remove); + } + }else{//all others + if(document.getElementById("TB_overlay") === null){ + jQuery("body").append("
        "); + jQuery("#TB_overlay").click(tb_remove); + jQuery( 'body' ).addClass( 'modal-open' ); + } + } + + if(tb_detectMacXFF()){ + jQuery("#TB_overlay").addClass("TB_overlayMacFFBGHack");//use png overlay so hide flash + }else{ + jQuery("#TB_overlay").addClass("TB_overlayBG");//use background and opacity + } + + if(caption===null){caption="";} + jQuery("body").append("
        ");//add loader to the page + jQuery('#TB_load').show();//show loader + + var baseURL; + if(url.indexOf("?")!==-1){ //ff there is a query string involved + baseURL = url.substr(0, url.indexOf("?")); + }else{ + baseURL = url; + } + + var urlString = /\.jpg$|\.jpeg$|\.png$|\.gif$|\.bmp$/; + var urlType = baseURL.toLowerCase().match(urlString); + + if(urlType == '.jpg' || urlType == '.jpeg' || urlType == '.png' || urlType == '.gif' || urlType == '.bmp'){//code to show images + + TB_PrevCaption = ""; + TB_PrevURL = ""; + TB_PrevHTML = ""; + TB_NextCaption = ""; + TB_NextURL = ""; + TB_NextHTML = ""; + TB_imageCount = ""; + TB_FoundURL = false; + if(imageGroup){ + TB_TempArray = jQuery("a[rel="+imageGroup+"]").get(); + for (TB_Counter = 0; ((TB_Counter < TB_TempArray.length) && (TB_NextHTML === "")); TB_Counter++) { + var urlTypeTemp = TB_TempArray[TB_Counter].href.toLowerCase().match(urlString); + if (!(TB_TempArray[TB_Counter].href == url)) { + if (TB_FoundURL) { + TB_NextCaption = TB_TempArray[TB_Counter].title; + TB_NextURL = TB_TempArray[TB_Counter].href; + TB_NextHTML = "  "+thickboxL10n.next+""; + } else { + TB_PrevCaption = TB_TempArray[TB_Counter].title; + TB_PrevURL = TB_TempArray[TB_Counter].href; + TB_PrevHTML = "  "+thickboxL10n.prev+""; + } + } else { + TB_FoundURL = true; + TB_imageCount = thickboxL10n.image + ' ' + (TB_Counter + 1) + ' ' + thickboxL10n.of + ' ' + (TB_TempArray.length); + } + } + } + + imgPreloader = new Image(); + imgPreloader.onload = function(){ + imgPreloader.onload = null; + + // Resizing large images - original by Christian Montoya edited by me. + var pagesize = tb_getPageSize(); + var x = pagesize[0] - 150; + var y = pagesize[1] - 150; + var imageWidth = imgPreloader.width; + var imageHeight = imgPreloader.height; + if (imageWidth > x) { + imageHeight = imageHeight * (x / imageWidth); + imageWidth = x; + if (imageHeight > y) { + imageWidth = imageWidth * (y / imageHeight); + imageHeight = y; + } + } else if (imageHeight > y) { + imageWidth = imageWidth * (y / imageHeight); + imageHeight = y; + if (imageWidth > x) { + imageHeight = imageHeight * (x / imageWidth); + imageWidth = x; + } + } + // End Resizing + + TB_WIDTH = imageWidth + 30; + TB_HEIGHT = imageHeight + 60; + jQuery("#TB_window").append(""+thickboxL10n.close+""+caption+"" + "
        "+caption+"
        " + TB_imageCount + TB_PrevHTML + TB_NextHTML + "
        "); + + jQuery("#TB_closeWindowButton").click(tb_remove); + + if (!(TB_PrevHTML === "")) { + function goPrev(){ + if(jQuery(document).unbind("click",goPrev)){jQuery(document).unbind("click",goPrev);} + jQuery("#TB_window").remove(); + jQuery("body").append("
        "); + tb_show(TB_PrevCaption, TB_PrevURL, imageGroup); + return false; + } + jQuery("#TB_prev").click(goPrev); + } + + if (!(TB_NextHTML === "")) { + function goNext(){ + jQuery("#TB_window").remove(); + jQuery("body").append("
        "); + tb_show(TB_NextCaption, TB_NextURL, imageGroup); + return false; + } + jQuery("#TB_next").click(goNext); + + } + + jQuery(document).bind('keydown.thickbox', function(e){ + if ( e.which == 27 ){ // close + tb_remove(); + + } else if ( e.which == 190 ){ // display previous image + if(!(TB_NextHTML == "")){ + jQuery(document).unbind('thickbox'); + goNext(); + } + } else if ( e.which == 188 ){ // display next image + if(!(TB_PrevHTML == "")){ + jQuery(document).unbind('thickbox'); + goPrev(); + } + } + return false; + }); + + tb_position(); + jQuery("#TB_load").remove(); + jQuery("#TB_ImageOff").click(tb_remove); + jQuery("#TB_window").css({'visibility':'visible'}); //for safari using css instead of show + }; + + imgPreloader.src = url; + }else{//code to show html + + var queryString = url.replace(/^[^\?]+\??/,''); + var params = tb_parseQuery( queryString ); + + TB_WIDTH = (params['width']*1) + 30 || 630; //defaults to 630 if no parameters were added to URL + TB_HEIGHT = (params['height']*1) + 40 || 440; //defaults to 440 if no parameters were added to URL + ajaxContentW = TB_WIDTH - 30; + ajaxContentH = TB_HEIGHT - 45; + + if(url.indexOf('TB_iframe') != -1){// either iframe or ajax window + urlNoQuery = url.split('TB_'); + jQuery("#TB_iframeContent").remove(); + if(params['modal'] != "true"){//iframe no modal + jQuery("#TB_window").append(""); + }else{//iframe modal + jQuery("#TB_overlay").unbind(); + jQuery("#TB_window").append(""); + } + }else{// not an iframe, ajax + if(jQuery("#TB_window").css("visibility") != "visible"){ + if(params['modal'] != "true"){//ajax no modal + jQuery("#TB_window").append("
        "+caption+"
        "); + }else{//ajax modal + jQuery("#TB_overlay").unbind(); + jQuery("#TB_window").append("
        "); + } + }else{//this means the window is already up, we are just loading new content via ajax + jQuery("#TB_ajaxContent")[0].style.width = ajaxContentW +"px"; + jQuery("#TB_ajaxContent")[0].style.height = ajaxContentH +"px"; + jQuery("#TB_ajaxContent")[0].scrollTop = 0; + jQuery("#TB_ajaxWindowTitle").html(caption); + } + } + + jQuery("#TB_closeWindowButton").click(tb_remove); + + if(url.indexOf('TB_inline') != -1){ + jQuery("#TB_ajaxContent").append(jQuery('#' + params['inlineId']).children()); + jQuery("#TB_window").bind('tb_unload', function () { + jQuery('#' + params['inlineId']).append( jQuery("#TB_ajaxContent").children() ); // move elements back when you're finished + }); + tb_position(); + jQuery("#TB_load").remove(); + jQuery("#TB_window").css({'visibility':'visible'}); + }else if(url.indexOf('TB_iframe') != -1){ + tb_position(); + jQuery("#TB_load").remove(); + jQuery("#TB_window").css({'visibility':'visible'}); + }else{ + jQuery("#TB_ajaxContent").load(url += "&random=" + (new Date().getTime()),function(){//to do a post change this load method + tb_position(); + jQuery("#TB_load").remove(); + tb_init("#TB_ajaxContent a.thickbox"); + jQuery("#TB_window").css({'visibility':'visible'}); + }); + } + + } + + if(!params['modal']){ + jQuery(document).bind('keydown.thickbox', function(e){ + if ( e.which == 27 ){ // close + tb_remove(); + return false; + } + }); + } + + } catch(e) { + //nothing here + } +} + +//helper functions below +function tb_showIframe(){ + jQuery("#TB_load").remove(); + jQuery("#TB_window").css({'visibility':'visible'}); +} + +function tb_remove() { + jQuery("#TB_imageOff").unbind("click"); + jQuery("#TB_closeWindowButton").unbind("click"); + jQuery("#TB_window").fadeOut("fast",function(){jQuery('#TB_window,#TB_overlay,#TB_HideSelect').trigger("tb_unload").unbind().remove();}); + jQuery( 'body' ).removeClass( 'modal-open' ); + jQuery("#TB_load").remove(); + if (typeof document.body.style.maxHeight == "undefined") {//if IE 6 + jQuery("body","html").css({height: "auto", width: "auto"}); + jQuery("html").css("overflow",""); + } + jQuery(document).unbind('.thickbox'); + return false; +} + +function tb_position() { +var isIE6 = typeof document.body.style.maxHeight === "undefined"; +jQuery("#TB_window").css({marginLeft: '-' + parseInt((TB_WIDTH / 2),10) + 'px', width: TB_WIDTH + 'px'}); + if ( ! isIE6 ) { // take away IE6 + jQuery("#TB_window").css({marginTop: '-' + parseInt((TB_HEIGHT / 2),10) + 'px'}); + } +} + +function tb_parseQuery ( query ) { + var Params = {}; + if ( ! query ) {return Params;}// return empty object + var Pairs = query.split(/[;&]/); + for ( var i = 0; i < Pairs.length; i++ ) { + var KeyVal = Pairs[i].split('='); + if ( ! KeyVal || KeyVal.length != 2 ) {continue;} + var key = unescape( KeyVal[0] ); + var val = unescape( KeyVal[1] ); + val = val.replace(/\+/g, ' '); + Params[key] = val; + } + return Params; +} + +function tb_getPageSize(){ + var de = document.documentElement; + var w = window.innerWidth || self.innerWidth || (de&&de.clientWidth) || document.body.clientWidth; + var h = window.innerHeight || self.innerHeight || (de&&de.clientHeight) || document.body.clientHeight; + arrayPageSize = [w,h]; + return arrayPageSize; +} + +function tb_detectMacXFF() { + var userAgent = navigator.userAgent.toLowerCase(); + if (userAgent.indexOf('mac') != -1 && userAgent.indexOf('firefox')!=-1) { + return true; + } +} diff --git a/wp-includes/js/tinymce/langs/wp-langs-en.js b/wp-includes/js/tinymce/langs/wp-langs-en.js new file mode 100644 index 0000000..65d9669 --- /dev/null +++ b/wp-includes/js/tinymce/langs/wp-langs-en.js @@ -0,0 +1,519 @@ +/** + * TinyMCE 3.x language strings + * + * Loaded only when external plugins are added to TinyMCE. + */ +( function() { + var main = {}, lang = 'en'; + + if ( typeof tinyMCEPreInit !== 'undefined' && tinyMCEPreInit.ref.language !== 'en' ) { + lang = tinyMCEPreInit.ref.language; + } + + main[lang] = { + common: { + edit_confirm: "Do you want to use the WYSIWYG mode for this textarea?", + apply: "Apply", + insert: "Insert", + update: "Update", + cancel: "Cancel", + close: "Close", + browse: "Browse", + class_name: "Class", + not_set: "-- Not set --", + clipboard_msg: "Copy/Cut/Paste is not available in Mozilla and Firefox.", + clipboard_no_support: "Currently not supported by your browser, use keyboard shortcuts instead.", + popup_blocked: "Sorry, but we have noticed that your popup-blocker has disabled a window that provides application functionality. You will need to disable popup blocking on this site in order to fully utilize this tool.", + invalid_data: "ERROR: Invalid values entered, these are marked in red.", + invalid_data_number: "{#field} must be a number", + invalid_data_min: "{#field} must be a number greater than {#min}", + invalid_data_size: "{#field} must be a number or percentage", + more_colors: "More colors" + }, + colors: { + "000000": "Black", + "993300": "Burnt orange", + "333300": "Dark olive", + "003300": "Dark green", + "003366": "Dark azure", + "000080": "Navy Blue", + "333399": "Indigo", + "333333": "Very dark gray", + "800000": "Maroon", + "FF6600": "Orange", + "808000": "Olive", + "008000": "Green", + "008080": "Teal", + "0000FF": "Blue", + "666699": "Grayish blue", + "808080": "Gray", + "FF0000": "Red", + "FF9900": "Amber", + "99CC00": "Yellow green", + "339966": "Sea green", + "33CCCC": "Turquoise", + "3366FF": "Royal blue", + "800080": "Purple", + "999999": "Medium gray", + "FF00FF": "Magenta", + "FFCC00": "Gold", + "FFFF00": "Yellow", + "00FF00": "Lime", + "00FFFF": "Aqua", + "00CCFF": "Sky blue", + "993366": "Brown", + "C0C0C0": "Silver", + "FF99CC": "Pink", + "FFCC99": "Peach", + "FFFF99": "Light yellow", + "CCFFCC": "Pale green", + "CCFFFF": "Pale cyan", + "99CCFF": "Light sky blue", + "CC99FF": "Plum", + "FFFFFF": "White" + }, + contextmenu: { + align: "Alignment", + left: "Left", + center: "Center", + right: "Right", + full: "Full" + }, + insertdatetime: { + date_fmt: "%Y-%m-%d", + time_fmt: "%H:%M:%S", + insertdate_desc: "Insert date", + inserttime_desc: "Insert time", + months_long: "January,February,March,April,May,June,July,August,September,October,November,December", + months_short: "Jan_January_abbreviation,Feb_February_abbreviation,Mar_March_abbreviation,Apr_April_abbreviation,May_May_abbreviation,Jun_June_abbreviation,Jul_July_abbreviation,Aug_August_abbreviation,Sep_September_abbreviation,Oct_October_abbreviation,Nov_November_abbreviation,Dec_December_abbreviation", + day_long: "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday", + day_short: "Sun,Mon,Tue,Wed,Thu,Fri,Sat" + }, + print: { + print_desc: "Print" + }, + preview: { + preview_desc: "Preview" + }, + directionality: { + ltr_desc: "Direction left to right", + rtl_desc: "Direction right to left" + }, + layer: { + insertlayer_desc: "Insert new layer", + forward_desc: "Move forward", + backward_desc: "Move backward", + absolute_desc: "Toggle absolute positioning", + content: "New layer..." + }, + save: { + save_desc: "Save", + cancel_desc: "Cancel all changes" + }, + nonbreaking: { + nonbreaking_desc: "Insert non-breaking space character" + }, + iespell: { + iespell_desc: "Run spell checking", + download: "ieSpell not detected. Do you want to install it now?" + }, + advhr: { + advhr_desc: "Horizontal rule" + }, + emotions: { + emotions_desc: "Emotions" + }, + searchreplace: { + search_desc: "Find", + replace_desc: "Find/Replace" + }, + advimage: { + image_desc: "Insert/edit image" + }, + advlink: { + link_desc: "Insert/edit link" + }, + xhtmlxtras: { + cite_desc: "Citation", + abbr_desc: "Abbreviation", + acronym_desc: "Acronym", + del_desc: "Deletion", + ins_desc: "Insertion", + attribs_desc: "Insert/Edit Attributes" + }, + style: { + desc: "Edit CSS Style" + }, + paste: { + paste_text_desc: "Paste as Plain Text", + paste_word_desc: "Paste from Word", + selectall_desc: "Select All", + plaintext_mode_sticky: "Paste is now in plain text mode. Click again to toggle back to regular paste mode. After you paste something you will be returned to regular paste mode.", + plaintext_mode: "Paste is now in plain text mode. Click again to toggle back to regular paste mode." + }, + paste_dlg: { + text_title: "Use CTRL + V on your keyboard to paste the text into the window.", + text_linebreaks: "Keep linebreaks", + word_title: "Use CTRL + V on your keyboard to paste the text into the window." + }, + table: { + desc: "Inserts a new table", + row_before_desc: "Insert row before", + row_after_desc: "Insert row after", + delete_row_desc: "Delete row", + col_before_desc: "Insert column before", + col_after_desc: "Insert column after", + delete_col_desc: "Remove column", + split_cells_desc: "Split merged table cells", + merge_cells_desc: "Merge table cells", + row_desc: "Table row properties", + cell_desc: "Table cell properties", + props_desc: "Table properties", + paste_row_before_desc: "Paste table row before", + paste_row_after_desc: "Paste table row after", + cut_row_desc: "Cut table row", + copy_row_desc: "Copy table row", + del: "Delete table", + row: "Row", + col: "Column", + cell: "Cell" + }, + autosave: { + unload_msg: "The changes you made will be lost if you navigate away from this page." + }, + fullscreen: { + desc: "Toggle fullscreen mode (Alt + Shift + G)" + }, + media: { + desc: "Insert / edit embedded media", + edit: "Edit embedded media" + }, + fullpage: { + desc: "Document properties" + }, + template: { + desc: "Insert predefined template content" + }, + visualchars: { + desc: "Visual control characters on/off." + }, + spellchecker: { + desc: "Toggle spellchecker (Alt + Shift + N)", + menu: "Spellchecker settings", + ignore_word: "Ignore word", + ignore_words: "Ignore all", + langs: "Languages", + wait: "Please wait...", + sug: "Suggestions", + no_sug: "No suggestions", + no_mpell: "No misspellings found.", + learn_word: "Learn word" + }, + pagebreak: { + desc: "Insert Page Break" + }, + advlist:{ + types: "Types", + def: "Default", + lower_alpha: "Lower alpha", + lower_greek: "Lower greek", + lower_roman: "Lower roman", + upper_alpha: "Upper alpha", + upper_roman: "Upper roman", + circle: "Circle", + disc: "Disc", + square: "Square" + }, + aria: { + rich_text_area: "Rich Text Area" + }, + wordcount:{ + words: "Words: " + } + }; + + tinyMCE.addI18n( main ); + + tinyMCE.addI18n( lang + ".advanced", { + style_select: "Styles", + font_size: "Font size", + fontdefault: "Font family", + block: "Format", + paragraph: "Paragraph", + div: "Div", + address: "Address", + pre: "Preformatted", + h1: "Heading 1", + h2: "Heading 2", + h3: "Heading 3", + h4: "Heading 4", + h5: "Heading 5", + h6: "Heading 6", + blockquote: "Blockquote", + code: "Code", + samp: "Code sample", + dt: "Definition term ", + dd: "Definition description", + bold_desc: "Bold (Ctrl + B)", + italic_desc: "Italic (Ctrl + I)", + underline_desc: "Underline", + striketrough_desc: "Strikethrough (Alt + Shift + D)", + justifyleft_desc: "Align Left (Alt + Shift + L)", + justifycenter_desc: "Align Center (Alt + Shift + C)", + justifyright_desc: "Align Right (Alt + Shift + R)", + justifyfull_desc: "Align Full (Alt + Shift + J)", + bullist_desc: "Unordered list (Alt + Shift + U)", + numlist_desc: "Ordered list (Alt + Shift + O)", + outdent_desc: "Outdent", + indent_desc: "Indent", + undo_desc: "Undo (Ctrl + Z)", + redo_desc: "Redo (Ctrl + Y)", + link_desc: "Insert/edit link (Alt + Shift + A)", + unlink_desc: "Unlink (Alt + Shift + S)", + image_desc: "Insert/edit image (Alt + Shift + M)", + cleanup_desc: "Cleanup messy code", + code_desc: "Edit HTML Source", + sub_desc: "Subscript", + sup_desc: "Superscript", + hr_desc: "Insert horizontal ruler", + removeformat_desc: "Remove formatting", + forecolor_desc: "Select text color", + backcolor_desc: "Select background color", + charmap_desc: "Insert custom character", + visualaid_desc: "Toggle guidelines/invisible elements", + anchor_desc: "Insert/edit anchor", + cut_desc: "Cut", + copy_desc: "Copy", + paste_desc: "Paste", + image_props_desc: "Image properties", + newdocument_desc: "New document", + help_desc: "Help", + blockquote_desc: "Blockquote (Alt + Shift + Q)", + clipboard_msg: "Copy/Cut/Paste is not available in Mozilla and Firefox.", + path: "Path", + newdocument: "Are you sure you want to clear all contents?", + toolbar_focus: "Jump to tool buttons - Alt+Q, Jump to editor - Alt-Z, Jump to element path - Alt-X", + more_colors: "More colors", + shortcuts_desc: "Accessibility Help", + help_shortcut: " Press ALT F10 for toolbar. Press ALT 0 for help.", + rich_text_area: "Rich Text Area", + toolbar: "Toolbar" + }); + + tinyMCE.addI18n( lang + ".advanced_dlg", { + about_title: "About TinyMCE", + about_general: "About", + about_help: "Help", + about_license: "License", + about_plugins: "Plugins", + about_plugin: "Plugin", + about_author: "Author", + about_version: "Version", + about_loaded: "Loaded plugins", + anchor_title: "Insert/edit anchor", + anchor_name: "Anchor name", + code_title: "HTML Source Editor", + code_wordwrap: "Word wrap", + colorpicker_title: "Select a color", + colorpicker_picker_tab: "Picker", + colorpicker_picker_title: "Color picker", + colorpicker_palette_tab: "Palette", + colorpicker_palette_title: "Palette colors", + colorpicker_named_tab: "Named", + colorpicker_named_title: "Named colors", + colorpicker_color: "Color: ", + colorpicker_name: "Name: ", + charmap_title: "Select custom character", + charmap_usage: "Use left and right arrows to navigate.", + image_title: "Insert/edit image", + image_src: "Image URL", + image_alt: "Image description", + image_list: "Image list", + image_border: "Border", + image_dimensions: "Dimensions", + image_vspace: "Vertical space", + image_hspace: "Horizontal space", + image_align: "Alignment", + image_align_baseline: "Baseline", + image_align_top: "Top", + image_align_middle: "Middle", + image_align_bottom: "Bottom", + image_align_texttop: "Text top", + image_align_textbottom: "Text bottom", + image_align_left: "Left", + image_align_right: "Right", + link_title: "Insert/edit link", + link_url: "Link URL", + link_target: "Target", + link_target_same: "Open link in the same window", + link_target_blank: "Open link in a new window", + link_titlefield: "Title", + link_is_email: "The URL you entered seems to be an email address, do you want to add the required mailto: prefix?", + link_is_external: "The URL you entered seems to be an external link, do you want to add the required http:// prefix?", + link_list: "Link list", + accessibility_help: "Accessibility Help", + accessibility_usage_title: "General Usage" + }); + + tinyMCE.addI18n( lang + ".media_dlg", { + title: "Insert / edit embedded media", + general: "General", + advanced: "Advanced", + file: "File/URL", + list: "List", + size: "Dimensions", + preview: "Preview", + constrain_proportions: "Constrain proportions", + type: "Type", + id: "Id", + name: "Name", + class_name: "Class", + vspace: "V-Space", + hspace: "H-Space", + play: "Auto play", + loop: "Loop", + menu: "Show menu", + quality: "Quality", + scale: "Scale", + align: "Align", + salign: "SAlign", + wmode: "WMode", + bgcolor: "Background", + base: "Base", + flashvars: "Flashvars", + liveconnect: "SWLiveConnect", + autohref: "AutoHREF", + cache: "Cache", + hidden: "Hidden", + controller: "Controller", + kioskmode: "Kiosk mode", + playeveryframe: "Play every frame", + targetcache: "Target cache", + correction: "No correction", + enablejavascript: "Enable JavaScript", + starttime: "Start time", + endtime: "End time", + href: "href", + qtsrcchokespeed: "Choke speed", + target: "Target", + volume: "Volume", + autostart: "Auto start", + enabled: "Enabled", + fullscreen: "Fullscreen", + invokeurls: "Invoke URLs", + mute: "Mute", + stretchtofit: "Stretch to fit", + windowlessvideo: "Windowless video", + balance: "Balance", + baseurl: "Base URL", + captioningid: "Captioning id", + currentmarker: "Current marker", + currentposition: "Current position", + defaultframe: "Default frame", + playcount: "Play count", + rate: "Rate", + uimode: "UI Mode", + flash_options: "Flash options", + qt_options: "QuickTime options", + wmp_options: "Windows media player options", + rmp_options: "Real media player options", + shockwave_options: "Shockwave options", + autogotourl: "Auto goto URL", + center: "Center", + imagestatus: "Image status", + maintainaspect: "Maintain aspect", + nojava: "No java", + prefetch: "Prefetch", + shuffle: "Shuffle", + console: "Console", + numloop: "Num loops", + controls: "Controls", + scriptcallbacks: "Script callbacks", + swstretchstyle: "Stretch style", + swstretchhalign: "Stretch H-Align", + swstretchvalign: "Stretch V-Align", + sound: "Sound", + progress: "Progress", + qtsrc: "QT Src", + qt_stream_warn: "Streamed rtsp resources should be added to the QT Src field under the advanced tab.", + align_top: "Top", + align_right: "Right", + align_bottom: "Bottom", + align_left: "Left", + align_center: "Center", + align_top_left: "Top left", + align_top_right: "Top right", + align_bottom_left: "Bottom left", + align_bottom_right: "Bottom right", + flv_options: "Flash video options", + flv_scalemode: "Scale mode", + flv_buffer: "Buffer", + flv_startimage: "Start image", + flv_starttime: "Start time", + flv_defaultvolume: "Default volume", + flv_hiddengui: "Hidden GUI", + flv_autostart: "Auto start", + flv_loop: "Loop", + flv_showscalemodes: "Show scale modes", + flv_smoothvideo: "Smooth video", + flv_jscallback: "JS Callback", + html5_video_options: "HTML5 Video Options", + altsource1: "Alternative source 1", + altsource2: "Alternative source 2", + preload: "Preload", + poster: "Poster", + source: "Source" + }); + + tinyMCE.addI18n( lang + ".wordpress", { + wp_adv_desc: "Show/Hide Kitchen Sink (Alt + Shift + Z)", + wp_more_desc: "Insert More Tag (Alt + Shift + T)", + wp_page_desc: "Insert Page break (Alt + Shift + P)", + wp_help_desc: "Help (Alt + Shift + H)", + wp_more_alt: "More...", + wp_page_alt: "Next page...", + add_media: "Add Media", + add_image: "Add an Image", + add_video: "Add Video", + add_audio: "Add Audio", + editgallery: "Edit Gallery", + delgallery: "Delete Gallery", + wp_fullscreen_desc: "Distraction-free writing mode (Alt + Shift + W)" + }); + + tinyMCE.addI18n( lang + ".wpeditimage", { + edit_img: "Edit Image", + del_img: "Delete Image", + adv_settings: "Advanced Settings", + none: "None", + size: "Size", + thumbnail: "Thumbnail", + medium: "Medium", + full_size: "Full Size", + current_link: "Current Link", + link_to_img: "Link to Image", + link_help: "Enter a link URL or click above for presets.", + adv_img_settings: "Advanced Image Settings", + source: "Source", + width: "Width", + height: "Height", + orig_size: "Original Size", + css: "CSS Class", + adv_link_settings: "Advanced Link Settings", + link_rel: "Link Rel", + height: "Height", + orig_size: "Original Size", + css: "CSS Class", + s60: "60%", + s70: "70%", + s80: "80%", + s90: "90%", + s100: "100%", + s110: "110%", + s120: "120%", + s130: "130%", + img_title: "Title", + caption: "Caption", + alt: "Alternative Text" + }); +}()); diff --git a/wp-includes/js/tinymce/license.txt b/wp-includes/js/tinymce/license.txt new file mode 100644 index 0000000..1837b0a --- /dev/null +++ b/wp-includes/js/tinymce/license.txt @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/wp-includes/js/tinymce/plugins/charmap/plugin.js b/wp-includes/js/tinymce/plugins/charmap/plugin.js new file mode 100644 index 0000000..27b6a46 --- /dev/null +++ b/wp-includes/js/tinymce/plugins/charmap/plugin.js @@ -0,0 +1,370 @@ +/** + * plugin.js + * + * Copyright, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/*global tinymce:true */ + +tinymce.PluginManager.add('charmap', function(editor) { + var charmap = [ + ['160', 'no-break space'], + ['38', 'ampersand'], + ['34', 'quotation mark'], + // finance + ['162', 'cent sign'], + ['8364', 'euro sign'], + ['163', 'pound sign'], + ['165', 'yen sign'], + // signs + ['169', 'copyright sign'], + ['174', 'registered sign'], + ['8482', 'trade mark sign'], + ['8240', 'per mille sign'], + ['181', 'micro sign'], + ['183', 'middle dot'], + ['8226', 'bullet'], + ['8230', 'three dot leader'], + ['8242', 'minutes / feet'], + ['8243', 'seconds / inches'], + ['167', 'section sign'], + ['182', 'paragraph sign'], + ['223', 'sharp s / ess-zed'], + // quotations + ['8249', 'single left-pointing angle quotation mark'], + ['8250', 'single right-pointing angle quotation mark'], + ['171', 'left pointing guillemet'], + ['187', 'right pointing guillemet'], + ['8216', 'left single quotation mark'], + ['8217', 'right single quotation mark'], + ['8220', 'left double quotation mark'], + ['8221', 'right double quotation mark'], + ['8218', 'single low-9 quotation mark'], + ['8222', 'double low-9 quotation mark'], + ['60', 'less-than sign'], + ['62', 'greater-than sign'], + ['8804', 'less-than or equal to'], + ['8805', 'greater-than or equal to'], + ['8211', 'en dash'], + ['8212', 'em dash'], + ['175', 'macron'], + ['8254', 'overline'], + ['164', 'currency sign'], + ['166', 'broken bar'], + ['168', 'diaeresis'], + ['161', 'inverted exclamation mark'], + ['191', 'turned question mark'], + ['710', 'circumflex accent'], + ['732', 'small tilde'], + ['176', 'degree sign'], + ['8722', 'minus sign'], + ['177', 'plus-minus sign'], + ['247', 'division sign'], + ['8260', 'fraction slash'], + ['215', 'multiplication sign'], + ['185', 'superscript one'], + ['178', 'superscript two'], + ['179', 'superscript three'], + ['188', 'fraction one quarter'], + ['189', 'fraction one half'], + ['190', 'fraction three quarters'], + // math / logical + ['402', 'function / florin'], + ['8747', 'integral'], + ['8721', 'n-ary sumation'], + ['8734', 'infinity'], + ['8730', 'square root'], + ['8764', 'similar to'], + ['8773', 'approximately equal to'], + ['8776', 'almost equal to'], + ['8800', 'not equal to'], + ['8801', 'identical to'], + ['8712', 'element of'], + ['8713', 'not an element of'], + ['8715', 'contains as member'], + ['8719', 'n-ary product'], + ['8743', 'logical and'], + ['8744', 'logical or'], + ['172', 'not sign'], + ['8745', 'intersection'], + ['8746', 'union'], + ['8706', 'partial differential'], + ['8704', 'for all'], + ['8707', 'there exists'], + ['8709', 'diameter'], + ['8711', 'backward difference'], + ['8727', 'asterisk operator'], + ['8733', 'proportional to'], + ['8736', 'angle'], + // undefined + ['180', 'acute accent'], + ['184', 'cedilla'], + ['170', 'feminine ordinal indicator'], + ['186', 'masculine ordinal indicator'], + ['8224', 'dagger'], + ['8225', 'double dagger'], + // alphabetical special chars + ['192', 'A - grave'], + ['193', 'A - acute'], + ['194', 'A - circumflex'], + ['195', 'A - tilde'], + ['196', 'A - diaeresis'], + ['197', 'A - ring above'], + ['198', 'ligature AE'], + ['199', 'C - cedilla'], + ['200', 'E - grave'], + ['201', 'E - acute'], + ['202', 'E - circumflex'], + ['203', 'E - diaeresis'], + ['204', 'I - grave'], + ['205', 'I - acute'], + ['206', 'I - circumflex'], + ['207', 'I - diaeresis'], + ['208', 'ETH'], + ['209', 'N - tilde'], + ['210', 'O - grave'], + ['211', 'O - acute'], + ['212', 'O - circumflex'], + ['213', 'O - tilde'], + ['214', 'O - diaeresis'], + ['216', 'O - slash'], + ['338', 'ligature OE'], + ['352', 'S - caron'], + ['217', 'U - grave'], + ['218', 'U - acute'], + ['219', 'U - circumflex'], + ['220', 'U - diaeresis'], + ['221', 'Y - acute'], + ['376', 'Y - diaeresis'], + ['222', 'THORN'], + ['224', 'a - grave'], + ['225', 'a - acute'], + ['226', 'a - circumflex'], + ['227', 'a - tilde'], + ['228', 'a - diaeresis'], + ['229', 'a - ring above'], + ['230', 'ligature ae'], + ['231', 'c - cedilla'], + ['232', 'e - grave'], + ['233', 'e - acute'], + ['234', 'e - circumflex'], + ['235', 'e - diaeresis'], + ['236', 'i - grave'], + ['237', 'i - acute'], + ['238', 'i - circumflex'], + ['239', 'i - diaeresis'], + ['240', 'eth'], + ['241', 'n - tilde'], + ['242', 'o - grave'], + ['243', 'o - acute'], + ['244', 'o - circumflex'], + ['245', 'o - tilde'], + ['246', 'o - diaeresis'], + ['248', 'o slash'], + ['339', 'ligature oe'], + ['353', 's - caron'], + ['249', 'u - grave'], + ['250', 'u - acute'], + ['251', 'u - circumflex'], + ['252', 'u - diaeresis'], + ['253', 'y - acute'], + ['254', 'thorn'], + ['255', 'y - diaeresis'], + ['913', 'Alpha'], + ['914', 'Beta'], + ['915', 'Gamma'], + ['916', 'Delta'], + ['917', 'Epsilon'], + ['918', 'Zeta'], + ['919', 'Eta'], + ['920', 'Theta'], + ['921', 'Iota'], + ['922', 'Kappa'], + ['923', 'Lambda'], + ['924', 'Mu'], + ['925', 'Nu'], + ['926', 'Xi'], + ['927', 'Omicron'], + ['928', 'Pi'], + ['929', 'Rho'], + ['931', 'Sigma'], + ['932', 'Tau'], + ['933', 'Upsilon'], + ['934', 'Phi'], + ['935', 'Chi'], + ['936', 'Psi'], + ['937', 'Omega'], + ['945', 'alpha'], + ['946', 'beta'], + ['947', 'gamma'], + ['948', 'delta'], + ['949', 'epsilon'], + ['950', 'zeta'], + ['951', 'eta'], + ['952', 'theta'], + ['953', 'iota'], + ['954', 'kappa'], + ['955', 'lambda'], + ['956', 'mu'], + ['957', 'nu'], + ['958', 'xi'], + ['959', 'omicron'], + ['960', 'pi'], + ['961', 'rho'], + ['962', 'final sigma'], + ['963', 'sigma'], + ['964', 'tau'], + ['965', 'upsilon'], + ['966', 'phi'], + ['967', 'chi'], + ['968', 'psi'], + ['969', 'omega'], + // symbols + ['8501', 'alef symbol'], + ['982', 'pi symbol'], + ['8476', 'real part symbol'], + ['978', 'upsilon - hook symbol'], + ['8472', 'Weierstrass p'], + ['8465', 'imaginary part'], + // arrows + ['8592', 'leftwards arrow'], + ['8593', 'upwards arrow'], + ['8594', 'rightwards arrow'], + ['8595', 'downwards arrow'], + ['8596', 'left right arrow'], + ['8629', 'carriage return'], + ['8656', 'leftwards double arrow'], + ['8657', 'upwards double arrow'], + ['8658', 'rightwards double arrow'], + ['8659', 'downwards double arrow'], + ['8660', 'left right double arrow'], + ['8756', 'therefore'], + ['8834', 'subset of'], + ['8835', 'superset of'], + ['8836', 'not a subset of'], + ['8838', 'subset of or equal to'], + ['8839', 'superset of or equal to'], + ['8853', 'circled plus'], + ['8855', 'circled times'], + ['8869', 'perpendicular'], + ['8901', 'dot operator'], + ['8968', 'left ceiling'], + ['8969', 'right ceiling'], + ['8970', 'left floor'], + ['8971', 'right floor'], + ['9001', 'left-pointing angle bracket'], + ['9002', 'right-pointing angle bracket'], + ['9674', 'lozenge'], + ['9824', 'black spade suit'], + ['9827', 'black club suit'], + ['9829', 'black heart suit'], + ['9830', 'black diamond suit'], + ['8194', 'en space'], + ['8195', 'em space'], + ['8201', 'thin space'], + ['8204', 'zero width non-joiner'], + ['8205', 'zero width joiner'], + ['8206', 'left-to-right mark'], + ['8207', 'right-to-left mark'], + ['173', 'soft hyphen'] + ]; + + function showDialog() { + var gridHtml, x, y, win; + + function getParentTd(elm) { + while (elm) { + if (elm.nodeName == 'TD') { + return elm; + } + + elm = elm.parentNode; + } + } + + gridHtml = ''; + + var width = 25; + for (y = 0; y < 10; y++) { + gridHtml += ''; + + for (x = 0; x < width; x++) { + var chr = charmap[y * width + x]; + + gridHtml += ''; + } + + gridHtml += ''; + } + + gridHtml += ''; + + var charMapPanel = { + type: 'container', + html: gridHtml, + onclick: function(e) { + var target = e.target; + + if (target.tagName == 'TD') { + target = target.firstChild; + } + + if (target.tagName == 'DIV') { + editor.execCommand('mceInsertContent', false, target.firstChild.data); + + if (!e.ctrlKey) { + win.close(); + } + } + }, + onmouseover: function(e) { + var td = getParentTd(e.target); + + if (td) { + win.find('#preview').text(td.firstChild.firstChild.data); + } + } + }; + + win = editor.windowManager.open({ + title: "Special character", + spacing: 10, + padding: 10, + items: [ + charMapPanel, + { + type: 'label', + name: 'preview', + text: ' ', + style: 'font-size: 40px; text-align: center', + border: 1, + minWidth: 100, + minHeight: 80 + } + ], + buttons: [ + {text: "Close", onclick: function() { + win.close(); + }} + ] + }); + } + + editor.addButton('charmap', { + icon: 'charmap', + tooltip: 'Special character', + onclick: showDialog + }); + + editor.addMenuItem('charmap', { + icon: 'charmap', + text: 'Special character', + onclick: showDialog, + context: 'insert' + }); +}); \ No newline at end of file diff --git a/wp-includes/js/tinymce/plugins/charmap/plugin.min.js b/wp-includes/js/tinymce/plugins/charmap/plugin.min.js new file mode 100644 index 0000000..7b4fc97 --- /dev/null +++ b/wp-includes/js/tinymce/plugins/charmap/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("charmap",function(a){function b(){function b(a){for(;a;){if("TD"==a.nodeName)return a;a=a.parentNode}}var d,e,f,g;d='';var h=25;for(f=0;10>f;f++){for(d+="",e=0;h>e;e++){var i=c[f*h+e];d+='"}d+=""}d+="";var j={type:"container",html:d,onclick:function(b){var c=b.target;"TD"==c.tagName&&(c=c.firstChild),"DIV"==c.tagName&&(a.execCommand("mceInsertContent",!1,c.firstChild.data),b.ctrlKey||g.close())},onmouseover:function(a){var c=b(a.target);c&&g.find("#preview").text(c.firstChild.firstChild.data)}};g=a.windowManager.open({title:"Special character",spacing:10,padding:10,items:[j,{type:"label",name:"preview",text:" ",style:"font-size: 40px; text-align: center",border:1,minWidth:100,minHeight:80}],buttons:[{text:"Close",onclick:function(){g.close()}}]})}var c=[["160","no-break space"],["38","ampersand"],["34","quotation mark"],["162","cent sign"],["8364","euro sign"],["163","pound sign"],["165","yen sign"],["169","copyright sign"],["174","registered sign"],["8482","trade mark sign"],["8240","per mille sign"],["181","micro sign"],["183","middle dot"],["8226","bullet"],["8230","three dot leader"],["8242","minutes / feet"],["8243","seconds / inches"],["167","section sign"],["182","paragraph sign"],["223","sharp s / ess-zed"],["8249","single left-pointing angle quotation mark"],["8250","single right-pointing angle quotation mark"],["171","left pointing guillemet"],["187","right pointing guillemet"],["8216","left single quotation mark"],["8217","right single quotation mark"],["8220","left double quotation mark"],["8221","right double quotation mark"],["8218","single low-9 quotation mark"],["8222","double low-9 quotation mark"],["60","less-than sign"],["62","greater-than sign"],["8804","less-than or equal to"],["8805","greater-than or equal to"],["8211","en dash"],["8212","em dash"],["175","macron"],["8254","overline"],["164","currency sign"],["166","broken bar"],["168","diaeresis"],["161","inverted exclamation mark"],["191","turned question mark"],["710","circumflex accent"],["732","small tilde"],["176","degree sign"],["8722","minus sign"],["177","plus-minus sign"],["247","division sign"],["8260","fraction slash"],["215","multiplication sign"],["185","superscript one"],["178","superscript two"],["179","superscript three"],["188","fraction one quarter"],["189","fraction one half"],["190","fraction three quarters"],["402","function / florin"],["8747","integral"],["8721","n-ary sumation"],["8734","infinity"],["8730","square root"],["8764","similar to"],["8773","approximately equal to"],["8776","almost equal to"],["8800","not equal to"],["8801","identical to"],["8712","element of"],["8713","not an element of"],["8715","contains as member"],["8719","n-ary product"],["8743","logical and"],["8744","logical or"],["172","not sign"],["8745","intersection"],["8746","union"],["8706","partial differential"],["8704","for all"],["8707","there exists"],["8709","diameter"],["8711","backward difference"],["8727","asterisk operator"],["8733","proportional to"],["8736","angle"],["180","acute accent"],["184","cedilla"],["170","feminine ordinal indicator"],["186","masculine ordinal indicator"],["8224","dagger"],["8225","double dagger"],["192","A - grave"],["193","A - acute"],["194","A - circumflex"],["195","A - tilde"],["196","A - diaeresis"],["197","A - ring above"],["198","ligature AE"],["199","C - cedilla"],["200","E - grave"],["201","E - acute"],["202","E - circumflex"],["203","E - diaeresis"],["204","I - grave"],["205","I - acute"],["206","I - circumflex"],["207","I - diaeresis"],["208","ETH"],["209","N - tilde"],["210","O - grave"],["211","O - acute"],["212","O - circumflex"],["213","O - tilde"],["214","O - diaeresis"],["216","O - slash"],["338","ligature OE"],["352","S - caron"],["217","U - grave"],["218","U - acute"],["219","U - circumflex"],["220","U - diaeresis"],["221","Y - acute"],["376","Y - diaeresis"],["222","THORN"],["224","a - grave"],["225","a - acute"],["226","a - circumflex"],["227","a - tilde"],["228","a - diaeresis"],["229","a - ring above"],["230","ligature ae"],["231","c - cedilla"],["232","e - grave"],["233","e - acute"],["234","e - circumflex"],["235","e - diaeresis"],["236","i - grave"],["237","i - acute"],["238","i - circumflex"],["239","i - diaeresis"],["240","eth"],["241","n - tilde"],["242","o - grave"],["243","o - acute"],["244","o - circumflex"],["245","o - tilde"],["246","o - diaeresis"],["248","o slash"],["339","ligature oe"],["353","s - caron"],["249","u - grave"],["250","u - acute"],["251","u - circumflex"],["252","u - diaeresis"],["253","y - acute"],["254","thorn"],["255","y - diaeresis"],["913","Alpha"],["914","Beta"],["915","Gamma"],["916","Delta"],["917","Epsilon"],["918","Zeta"],["919","Eta"],["920","Theta"],["921","Iota"],["922","Kappa"],["923","Lambda"],["924","Mu"],["925","Nu"],["926","Xi"],["927","Omicron"],["928","Pi"],["929","Rho"],["931","Sigma"],["932","Tau"],["933","Upsilon"],["934","Phi"],["935","Chi"],["936","Psi"],["937","Omega"],["945","alpha"],["946","beta"],["947","gamma"],["948","delta"],["949","epsilon"],["950","zeta"],["951","eta"],["952","theta"],["953","iota"],["954","kappa"],["955","lambda"],["956","mu"],["957","nu"],["958","xi"],["959","omicron"],["960","pi"],["961","rho"],["962","final sigma"],["963","sigma"],["964","tau"],["965","upsilon"],["966","phi"],["967","chi"],["968","psi"],["969","omega"],["8501","alef symbol"],["982","pi symbol"],["8476","real part symbol"],["978","upsilon - hook symbol"],["8472","Weierstrass p"],["8465","imaginary part"],["8592","leftwards arrow"],["8593","upwards arrow"],["8594","rightwards arrow"],["8595","downwards arrow"],["8596","left right arrow"],["8629","carriage return"],["8656","leftwards double arrow"],["8657","upwards double arrow"],["8658","rightwards double arrow"],["8659","downwards double arrow"],["8660","left right double arrow"],["8756","therefore"],["8834","subset of"],["8835","superset of"],["8836","not a subset of"],["8838","subset of or equal to"],["8839","superset of or equal to"],["8853","circled plus"],["8855","circled times"],["8869","perpendicular"],["8901","dot operator"],["8968","left ceiling"],["8969","right ceiling"],["8970","left floor"],["8971","right floor"],["9001","left-pointing angle bracket"],["9002","right-pointing angle bracket"],["9674","lozenge"],["9824","black spade suit"],["9827","black club suit"],["9829","black heart suit"],["9830","black diamond suit"],["8194","en space"],["8195","em space"],["8201","thin space"],["8204","zero width non-joiner"],["8205","zero width joiner"],["8206","left-to-right mark"],["8207","right-to-left mark"],["173","soft hyphen"]];a.addButton("charmap",{icon:"charmap",tooltip:"Special character",onclick:b}),a.addMenuItem("charmap",{icon:"charmap",text:"Special character",onclick:b,context:"insert"})}); \ No newline at end of file diff --git a/wp-includes/js/tinymce/plugins/colorpicker/plugin.js b/wp-includes/js/tinymce/plugins/colorpicker/plugin.js new file mode 100644 index 0000000..11d0af3 --- /dev/null +++ b/wp-includes/js/tinymce/plugins/colorpicker/plugin.js @@ -0,0 +1,112 @@ +/** + * plugin.js + * + * Copyright, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/*global tinymce:true */ + +tinymce.PluginManager.add('colorpicker', function(editor) { + function colorPickerCallback(callback, value) { + function setColor(value) { + var color = new tinymce.util.Color(value), rgb = color.toRgb(); + + win.fromJSON({ + r: rgb.r, + g: rgb.g, + b: rgb.b, + hex: color.toHex().substr(1) + }); + + showPreview(color.toHex()); + } + + function showPreview(hexColor) { + win.find('#preview')[0].getEl().style.background = hexColor; + } + + var win = editor.windowManager.open({ + title: 'Color', + items: { + type: 'container', + layout: 'flex', + direction: 'row', + align: 'stretch', + padding: 5, + spacing: 10, + items: [ + { + type: 'colorpicker', + value: value, + onchange: function() { + var rgb = this.rgb(); + + if (win) { + win.find('#r').value(rgb.r); + win.find('#g').value(rgb.g); + win.find('#b').value(rgb.b); + win.find('#hex').value(this.value().substr(1)); + showPreview(this.value()); + } + } + }, + { + type: 'form', + padding: 0, + labelGap: 5, + defaults: { + type: 'textbox', + size: 7, + value: '0', + flex: 1, + spellcheck: false, + onchange: function() { + var colorPickerCtrl = win.find('colorpicker')[0]; + var name, value; + + name = this.name(); + value = this.value(); + + if (name == "hex") { + value = '#' + value; + setColor(value); + colorPickerCtrl.value(value); + return; + } + + value = { + r: win.find('#r').value(), + g: win.find('#g').value(), + b: win.find('#b').value() + }; + + colorPickerCtrl.value(value); + setColor(value); + } + }, + items: [ + {name: 'r', label: 'R', autofocus: 1}, + {name: 'g', label: 'G'}, + {name: 'b', label: 'B'}, + {name: 'hex', label: '#', value: '000000'}, + {name: 'preview', type: 'container', border: 1} + ] + } + ] + }, + onSubmit: function() { + callback('#' + this.toJSON().hex); + } + }); + + setColor(value); + } + + if (!editor.settings.color_picker_callback) { + editor.settings.color_picker_callback = colorPickerCallback; + } +}); \ No newline at end of file diff --git a/wp-includes/js/tinymce/plugins/colorpicker/plugin.min.js b/wp-includes/js/tinymce/plugins/colorpicker/plugin.min.js new file mode 100644 index 0000000..66ea69c --- /dev/null +++ b/wp-includes/js/tinymce/plugins/colorpicker/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("colorpicker",function(a){function b(b,c){function d(a){var b=new tinymce.util.Color(a),c=b.toRgb();f.fromJSON({r:c.r,g:c.g,b:c.b,hex:b.toHex().substr(1)}),e(b.toHex())}function e(a){f.find("#preview")[0].getEl().style.background=a}var f=a.windowManager.open({title:"Color",items:{type:"container",layout:"flex",direction:"row",align:"stretch",padding:5,spacing:10,items:[{type:"colorpicker",value:c,onchange:function(){var a=this.rgb();f&&(f.find("#r").value(a.r),f.find("#g").value(a.g),f.find("#b").value(a.b),f.find("#hex").value(this.value().substr(1)),e(this.value()))}},{type:"form",padding:0,labelGap:5,defaults:{type:"textbox",size:7,value:"0",flex:1,spellcheck:!1,onchange:function(){var a,b,c=f.find("colorpicker")[0];return a=this.name(),b=this.value(),"hex"==a?(b="#"+b,d(b),void c.value(b)):(b={r:f.find("#r").value(),g:f.find("#g").value(),b:f.find("#b").value()},c.value(b),void d(b))}},items:[{name:"r",label:"R",autofocus:1},{name:"g",label:"G"},{name:"b",label:"B"},{name:"hex",label:"#",value:"000000"},{name:"preview",type:"container",border:1}]}]},onSubmit:function(){b("#"+this.toJSON().hex)}});d(c)}a.settings.color_picker_callback||(a.settings.color_picker_callback=b)}); \ No newline at end of file diff --git a/wp-includes/js/tinymce/plugins/compat3x/css/dialog.css b/wp-includes/js/tinymce/plugins/compat3x/css/dialog.css new file mode 100644 index 0000000..2b8cb13 --- /dev/null +++ b/wp-includes/js/tinymce/plugins/compat3x/css/dialog.css @@ -0,0 +1,212 @@ +@import url(//fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,300,400,600&subset=latin-ext,latin); + +/* Generic */ +body { +font-family: "Open Sans", sans-serif; +font-size:13px; +background:#fcfcfc; +padding:0; +margin:8px 8px 0 8px; +} + +textarea {resize:none;outline:none;} + +a:link, a:hover { + color: #2B6FB6; +} + +a:visited { + color: #3C2BB6; +} + +.nowrap {white-space: nowrap} + +/* Forms */ +form {margin: 0;} +fieldset {margin:0; padding:4px; border:1px solid #dfdfdf; font-family:Verdana, Arial; font-size:10px;} +legend {color:#2B6FB6; font-weight:bold;} +label.msg {display:none;} +label.invalid {color:#EE0000; display:inline;} +input.invalid {border:1px solid #EE0000;} +input {background:#FFF; border:1px solid #dfdfdf;} +input, select, textarea {font-family:Verdana, Arial, Helvetica, sans-serif; font-size:10px;} +input, select, textarea {border:1px solid #dfdfdf;} +input.radio {border:1px none #000000; background:transparent; vertical-align:middle;} +input.checkbox {border:1px none #000000; background:transparent; vertical-align:middle;} +.input_noborder {border:0;} + +/* Buttons */ +#insert, +#cancel, +#apply, +.mceActionPanel .button, +input.mceButton, +.updateButton { + display: inline-block; + text-decoration: none; + border: 1px solid #adadad; + margin: 0; + padding: 0 10px 1px; + font-size: 13px; + height: 24px; + line-height: 22px; + color: #333; + cursor: pointer; + -webkit-border-radius: 3px; + -webkit-appearance: none; + border-radius: 3px; + white-space: nowrap; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + background: #fafafa; + background-image: -webkit-gradient(linear, left top, left bottom, from(#fafafa), to(#e9e9e9)); + background-image: -webkit-linear-gradient(top, #fafafa, #e9e9e9); + background-image: -moz-linear-gradient(top, #fafafa, #e9e9e9); + background-image: -o-linear-gradient(top, #fafafa, #e9e9e9); + background-image: linear-gradient(to bottom, #fafafa, #e9e9e9); + + text-shadow: 0 1px 0 #fff; + -webkit-box-shadow: inset 0 1px 0 #fff; + -moz-box-shadow: inset 0 1px 0 #fff; + box-shadow: inset 0 1px 0 #fff; +} + +#insert { + background: #2ea2cc; + background: -webkit-gradient(linear, left top, left bottom, from(#2ea2cc), to(#1e8cbe)); + background: -webkit-linear-gradient(top, #2ea2cc 0%,#1e8cbe 100%); + background: linear-gradient(top, #2ea2cc 0%,#1e8cbe 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#2ea2cc', endColorstr='#1e8cbe',GradientType=0 ); + border-color: #0074a2; + -webkit-box-shadow: inset 0 1px 0 rgba(120,200,230,0.5); + box-shadow: inset 0 1px 0 rgba(120,200,230,0.5); + color: #fff; + text-decoration: none; + text-shadow: 0 1px 0 rgba(0,86,132,0.7); +} + +#cancel:hover, +input.mceButton:hover, +.updateButton:hover, +#cancel:focus, +input.mceButton:focus, +.updateButton:focus { + background: #f3f3f3; + background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f3f3f3)); + background-image: -webkit-linear-gradient(top, #fff, #f3f3f3); + background-image: -moz-linear-gradient(top, #fff, #f3f3f3); + background-image: -ms-linear-gradient(top, #fff, #f3f3f3); + background-image: -o-linear-gradient(top, #fff, #f3f3f3); + background-image: linear-gradient(to bottom, #fff, #f3f3f3); + border-color: #999; + color: #222; +} + +#insert:hover, +#insert:focus { + background: #1e8cbe; + background: -webkit-gradient(linear, left top, left bottom, from(#1e8cbe), to(#0074a2)); + background: -webkit-linear-gradient(top, #1e8cbe 0%,#0074a2 100%); + background: linear-gradient(top, #1e8cbe 0%,#0074a2 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#1e8cbe', endColorstr='#0074a2',GradientType=0 ); + border-color: #0074a2; + -webkit-box-shadow: inset 0 1px 0 rgba(120,200,230,0.6); + box-shadow: inset 0 1px 0 rgba(120,200,230,0.6); + color: #fff; +} + +.mceActionPanel #insert { + float: right; +} + +/* Browse */ +a.pickcolor, a.browse {text-decoration:none} +a.browse span {display:block; width:20px; height:18px; border:1px solid #FFF; margin-left:1px;} +.mceOldBoxModel a.browse span {width:22px; height:20px;} +a.browse:hover span {border:1px solid #0A246A; background-color:#B2BBD0;} +a.browse span.disabled {border:1px solid white; opacity:0.3; -ms-filter:'alpha(opacity=30)'; filter:alpha(opacity=30);} +a.browse:hover span.disabled {border:1px solid white; background-color:transparent;} +a.pickcolor span {display:block; width:20px; height:16px; margin-left:2px;} +.mceOldBoxModel a.pickcolor span {width:21px; height:17px;} +a.pickcolor:hover span {background-color:#B2BBD0;} +div.iframecontainer {background: #fff;} + +/* Charmap */ +table.charmap {border:1px solid #AAA; text-align:center} +td.charmap, #charmap a {width:18px; height:18px; color:#000; border:1px solid #AAA; text-align:center; font-size:12px; vertical-align:middle; line-height: 18px;} +#charmap a {display:block; color:#000; text-decoration:none; border:0} +#charmap a:hover {background:#CCC;color:#2B6FB6} +#charmap #codeN {font-size:10px; font-family:Arial,Helvetica,sans-serif; text-align:center} +#charmap #codeV {font-size:40px; height:80px; border:1px solid #AAA; text-align:center} +#charmap #charmapView {background-color:#fff;} + +/* Source */ +.wordWrapCode {vertical-align:middle; border:1px none #000000; background:transparent;} +.mceActionPanel {margin-top:5px;} + +/* Tabs classes */ +.tabs {width:100%; height:19px; line-height:normal; border-bottom: 1px solid #aaa;} +.tabs ul {margin:0; padding:0; list-style:none;} +.tabs li {float:left; border: 1px solid #aaa; margin:0 2px 0 0; padding:0 0 0 10px; line-height:17px; height:18px; display:block;} +.tabs li.current {border-bottom: 1px solid #fff; margin-right:2px;} +.tabs span {float:left; display:block; padding:0px 10px 0 0;} +.tabs a {text-decoration:none; font-family:Verdana, Arial; font-size:10px;} +.tabs a:link, .tabs a:visited, .tabs a:hover {color:black;} + +.wp-core-ui #tabs { + padding-bottom: 5px; + background-color: transparent; +} + +.wp-core-ui #tabs a { + padding: 6px 10px; + margin: 0 2px; +} + +/* Panels */ +.panel_wrapper div.panel {display:none;} +.panel_wrapper div.current {display:block; width:100%; height:300px; overflow:visible;} +.panel_wrapper {border:1px solid #919B9C; border-top:0px; padding:10px; padding-top:5px; clear:both; background:white;} + +/* Columns */ +.column {float:left;} +.properties {width:100%;} +.properties .column1 {} +.properties .column2 {text-align:left;} + +/* Titles */ +h1, h2, h3, h4 {color:#2B6FB6; margin:0; padding:0; padding-top:5px;} +h3 {font-size:14px;} +.title {font-size:12px; font-weight:bold; color:#2B6FB6;} + +/* Dialog specific */ +#link .panel_wrapper, #link div.current {height:125px;} +#image .panel_wrapper, #image div.current {height:200px;} +#plugintable thead {font-weight:bold; background:#DDD;} +#plugintable, #about #plugintable td {border:1px solid #919B9C;} +#plugintable {width:96%; margin-top:10px;} +#pluginscontainer {height:290px; overflow:auto;} +#colorpicker #preview {display:inline-block; padding-left:40px; height:14px; border:1px solid black; margin-left:5px; margin-right: 5px} +#colorpicker #previewblock {position: relative; top: -3px; padding-left:5px; padding-top: 0px; display:inline} +#colorpicker #preview_wrapper {text-align:center; padding-top:4px; white-space: nowrap; float: right;} +#colorpicker #insert, #colorpicker #cancel {width: 90px} +#colorpicker #colors {float:left; border:1px solid gray; cursor:crosshair;} +#colorpicker #light {border:1px solid gray; margin-left:5px; float:left;width:15px; height:150px; cursor:crosshair;} +#colorpicker #light div {overflow:hidden;} +#colorpicker .panel_wrapper div.current {height:175px;} +#colorpicker #namedcolors {width:150px;} +#colorpicker #namedcolors a {display:block; float:left; width:10px; height:10px; margin:1px 1px 0 0; overflow:hidden;} +#colorpicker #colornamecontainer {margin-top:5px;} +#colorpicker #picker_panel fieldset {margin:auto;width:325px;} + + +/* Localization */ + +body[dir="rtl"], +body[dir="rtl"] fieldset, +body[dir="rtl"] input, body[dir="rtl"] select, body[dir="rtl"] textarea, +body[dir="rtl"] #charmap #codeN, +body[dir="rtl"] .tabs a { + font-family: Tahoma, sans-serif; +} diff --git a/wp-includes/js/tinymce/plugins/compat3x/plugin.js b/wp-includes/js/tinymce/plugins/compat3x/plugin.js new file mode 100644 index 0000000..cc6e322 --- /dev/null +++ b/wp-includes/js/tinymce/plugins/compat3x/plugin.js @@ -0,0 +1,304 @@ +/** + * plugin.js + * + * Copyright, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/*global tinymce:true, console:true */ +/*eslint no-console:0, new-cap:0 */ + +/** + * This plugin adds missing events form the 4.x API back. Not every event is + * properly supported but most things should work. + * + * Unsupported things: + * - No editor.onEvent + * - Can't cancel execCommands with beforeExecCommand + */ +(function(tinymce) { + var reported; + + function noop() { + } + + function log(apiCall) { + if (!reported && window && window.console) { + reported = true; + console.log("Deprecated TinyMCE API call: " + apiCall); + } + } + + function Dispatcher(target, newEventName, argsMap, defaultScope) { + target = target || this; + + if (!newEventName) { + this.add = this.addToTop = this.remove = this.dispatch = noop; + return; + } + + this.add = function(callback, scope, prepend) { + log('.on' + newEventName + ".add(..)"); + + // Convert callback({arg1:x, arg2:x}) -> callback(arg1, arg2) + function patchedEventCallback(e) { + var callbackArgs = []; + + if (typeof argsMap == "string") { + argsMap = argsMap.split(" "); + } + + if (argsMap && typeof argsMap != "function") { + for (var i = 0; i < argsMap.length; i++) { + callbackArgs.push(e[argsMap[i]]); + } + } + + if (typeof argsMap == "function") { + callbackArgs = argsMap(newEventName, e, target); + if (!callbackArgs) { + return; + } + } + + if (!argsMap) { + callbackArgs = [e]; + } + + callbackArgs.unshift(defaultScope || target); + + if (callback.apply(scope || defaultScope || target, callbackArgs) === false) { + e.stopImmediatePropagation(); + } + } + + target.on(newEventName, patchedEventCallback, prepend); + + return patchedEventCallback; + }; + + this.addToTop = function(callback, scope) { + this.add(callback, scope, true); + }; + + this.remove = function(callback) { + return target.off(newEventName, callback); + }; + + this.dispatch = function() { + target.fire(newEventName); + + return true; + }; + } + + tinymce.util.Dispatcher = Dispatcher; + tinymce.onBeforeUnload = new Dispatcher(tinymce, "BeforeUnload"); + tinymce.onAddEditor = new Dispatcher(tinymce, "AddEditor", "editor"); + tinymce.onRemoveEditor = new Dispatcher(tinymce, "RemoveEditor", "editor"); + + tinymce.util.Cookie = { + get: noop, getHash: noop, remove: noop, set: noop, setHash: noop + }; + + function patchEditor(editor) { + function patchEditorEvents(oldEventNames, argsMap) { + tinymce.each(oldEventNames.split(" "), function(oldName) { + editor["on" + oldName] = new Dispatcher(editor, oldName, argsMap); + }); + } + + function convertUndoEventArgs(type, event, target) { + return [ + event.level, + target + ]; + } + + function filterSelectionEvents(needsSelection) { + return function(type, e) { + if ((!e.selection && !needsSelection) || e.selection == needsSelection) { + return [e]; + } + }; + } + + if (editor.controlManager) { + return; + } + + function cmNoop() { + var obj = {}, methods = 'add addMenu addSeparator collapse createMenu destroy displayColor expand focus ' + + 'getLength hasMenus hideMenu isActive isCollapsed isDisabled isRendered isSelected mark ' + + 'postRender remove removeAll renderHTML renderMenu renderNode renderTo select selectByIndex ' + + 'setActive setAriaProperty setColor setDisabled setSelected setState showMenu update'; + + log('editor.controlManager.*'); + + function _noop() { + return cmNoop(); + } + + tinymce.each(methods.split(' '), function(method) { + obj[method] = _noop; + }); + + return obj; + } + + editor.controlManager = { + buttons: {}, + + setDisabled: function(name, state) { + log("controlManager.setDisabled(..)"); + + if (this.buttons[name]) { + this.buttons[name].disabled(state); + } + }, + + setActive: function(name, state) { + log("controlManager.setActive(..)"); + + if (this.buttons[name]) { + this.buttons[name].active(state); + } + }, + + onAdd: new Dispatcher(), + onPostRender: new Dispatcher(), + + add: function(obj) { + return obj; + }, + createButton: cmNoop, + createColorSplitButton: cmNoop, + createControl: cmNoop, + createDropMenu: cmNoop, + createListBox: cmNoop, + createMenuButton: cmNoop, + createSeparator: cmNoop, + createSplitButton: cmNoop, + createToolbar: cmNoop, + createToolbarGroup: cmNoop, + destroy: noop, + get: noop, + setControlType: cmNoop + }; + + patchEditorEvents("PreInit BeforeRenderUI PostRender Load Init Remove Activate Deactivate", "editor"); + patchEditorEvents("Click MouseUp MouseDown DblClick KeyDown KeyUp KeyPress ContextMenu Paste Submit Reset"); + patchEditorEvents("BeforeExecCommand ExecCommand", "command ui value args"); // args.terminate not supported + patchEditorEvents("PreProcess PostProcess LoadContent SaveContent Change"); + patchEditorEvents("BeforeSetContent BeforeGetContent SetContent GetContent", filterSelectionEvents(false)); + patchEditorEvents("SetProgressState", "state time"); + patchEditorEvents("VisualAid", "element hasVisual"); + patchEditorEvents("Undo Redo", convertUndoEventArgs); + + patchEditorEvents("NodeChange", function(type, e) { + return [ + editor.controlManager, + e.element, + editor.selection.isCollapsed(), + e + ]; + }); + + var originalAddButton = editor.addButton; + editor.addButton = function(name, settings) { + var originalOnPostRender, string, translated; + + function patchedPostRender() { + editor.controlManager.buttons[name] = this; + + if (originalOnPostRender) { + return originalOnPostRender.call(this); + } + } + + for (var key in settings) { + if (key.toLowerCase() === "onpostrender") { + originalOnPostRender = settings[key]; + settings.onPostRender = patchedPostRender; + } + } + + if (!originalOnPostRender) { + settings.onPostRender = patchedPostRender; + } + + if ( settings.title ) { + // WP + string = (editor.settings.language || "en") + "." + settings.title; + translated = tinymce.i18n.translate(string); + + if ( string !== translated ) { + settings.title = translated; + } + // WP end + } + + return originalAddButton.call(this, name, settings); + }; + + editor.on('init', function() { + var undoManager = editor.undoManager, selection = editor.selection; + + undoManager.onUndo = new Dispatcher(editor, "Undo", convertUndoEventArgs, null, undoManager); + undoManager.onRedo = new Dispatcher(editor, "Redo", convertUndoEventArgs, null, undoManager); + undoManager.onBeforeAdd = new Dispatcher(editor, "BeforeAddUndo", null, undoManager); + undoManager.onAdd = new Dispatcher(editor, "AddUndo", null, undoManager); + + selection.onBeforeGetContent = new Dispatcher(editor, "BeforeGetContent", filterSelectionEvents(true), selection); + selection.onGetContent = new Dispatcher(editor, "GetContent", filterSelectionEvents(true), selection); + selection.onBeforeSetContent = new Dispatcher(editor, "BeforeSetContent", filterSelectionEvents(true), selection); + selection.onSetContent = new Dispatcher(editor, "SetContent", filterSelectionEvents(true), selection); + }); + + editor.on('BeforeRenderUI', function() { + var windowManager = editor.windowManager; + + windowManager.onOpen = new Dispatcher(); + windowManager.onClose = new Dispatcher(); + windowManager.createInstance = function(className, a, b, c, d, e) { + log("windowManager.createInstance(..)"); + + var constr = tinymce.resolve(className); + return new constr(a, b, c, d, e); + }; + }); + } + + tinymce.on('SetupEditor', patchEditor); + tinymce.PluginManager.add("compat3x", patchEditor); + + tinymce.addI18n = function(prefix, o) { + var I18n = tinymce.util.I18n, each = tinymce.each; + + if (typeof(prefix) == "string" && prefix.indexOf('.') === -1) { + I18n.add(prefix, o); + return; + } + + if (!tinymce.is(prefix, 'string')) { + each(prefix, function(o, lc) { + each(o, function(o, g) { + each(o, function(o, k) { + if (g === 'common') { + I18n.data[lc + '.' + k] = o; + } else { + I18n.data[lc + '.' + g + '.' + k] = o; + } + }); + }); + }); + } else { + each(o, function(o, k) { + I18n.data[prefix + '.' + k] = o; + }); + } + }; +})(tinymce); diff --git a/wp-includes/js/tinymce/plugins/compat3x/plugin.min.js b/wp-includes/js/tinymce/plugins/compat3x/plugin.min.js new file mode 100644 index 0000000..42002fb --- /dev/null +++ b/wp-includes/js/tinymce/plugins/compat3x/plugin.min.js @@ -0,0 +1 @@ +!function(a){function b(){}function c(a){!f&&window&&window.console&&(f=!0,console.log("Deprecated TinyMCE API call: "+a))}function d(a,d,e,f){return a=a||this,d?(this.add=function(b,g,h){function i(c){var h=[];if("string"==typeof e&&(e=e.split(" ")),e&&"function"!=typeof e)for(var i=0;i.on"+d+".add(..)"),a.on(d,i,h),i},this.addToTop=function(a,b){this.add(a,b,!0)},this.remove=function(b){return a.off(d,b)},void(this.dispatch=function(){return a.fire(d),!0})):void(this.add=this.addToTop=this.remove=this.dispatch=b)}function e(e){function f(b,c){a.each(b.split(" "),function(a){e["on"+a]=new d(e,a,c)})}function g(a,b,c){return[b.level,c]}function h(a){return function(b,c){return!c.selection&&!a||c.selection==a?[c]:void 0}}function i(){function b(){return i()}var d={},e="add addMenu addSeparator collapse createMenu destroy displayColor expand focus getLength hasMenus hideMenu isActive isCollapsed isDisabled isRendered isSelected mark postRender remove removeAll renderHTML renderMenu renderNode renderTo select selectByIndex setActive setAriaProperty setColor setDisabled setSelected setState showMenu update";return c("editor.controlManager.*"),a.each(e.split(" "),function(a){d[a]=b}),d}if(!e.controlManager){e.controlManager={buttons:{},setDisabled:function(a,b){c("controlManager.setDisabled(..)"),this.buttons[a]&&this.buttons[a].disabled(b)},setActive:function(a,b){c("controlManager.setActive(..)"),this.buttons[a]&&this.buttons[a].active(b)},onAdd:new d,onPostRender:new d,add:function(a){return a},createButton:i,createColorSplitButton:i,createControl:i,createDropMenu:i,createListBox:i,createMenuButton:i,createSeparator:i,createSplitButton:i,createToolbar:i,createToolbarGroup:i,destroy:b,get:b,setControlType:i},f("PreInit BeforeRenderUI PostRender Load Init Remove Activate Deactivate","editor"),f("Click MouseUp MouseDown DblClick KeyDown KeyUp KeyPress ContextMenu Paste Submit Reset"),f("BeforeExecCommand ExecCommand","command ui value args"),f("PreProcess PostProcess LoadContent SaveContent Change"),f("BeforeSetContent BeforeGetContent SetContent GetContent",h(!1)),f("SetProgressState","state time"),f("VisualAid","element hasVisual"),f("Undo Redo",g),f("NodeChange",function(a,b){return[e.controlManager,b.element,e.selection.isCollapsed(),b]});var j=e.addButton;e.addButton=function(b,c){function d(){return e.controlManager.buttons[b]=this,f?f.call(this):void 0}var f,g,h;for(var i in c)"onpostrender"===i.toLowerCase()&&(f=c[i],c.onPostRender=d);return f||(c.onPostRender=d),c.title&&(g=(e.settings.language||"en")+"."+c.title,h=a.i18n.translate(g),g!==h&&(c.title=h)),j.call(this,b,c)},e.on("init",function(){var a=e.undoManager,b=e.selection;a.onUndo=new d(e,"Undo",g,null,a),a.onRedo=new d(e,"Redo",g,null,a),a.onBeforeAdd=new d(e,"BeforeAddUndo",null,a),a.onAdd=new d(e,"AddUndo",null,a),b.onBeforeGetContent=new d(e,"BeforeGetContent",h(!0),b),b.onGetContent=new d(e,"GetContent",h(!0),b),b.onBeforeSetContent=new d(e,"BeforeSetContent",h(!0),b),b.onSetContent=new d(e,"SetContent",h(!0),b)}),e.on("BeforeRenderUI",function(){var b=e.windowManager;b.onOpen=new d,b.onClose=new d,b.createInstance=function(b,d,e,f,g,h){c("windowManager.createInstance(..)");var i=a.resolve(b);return new i(d,e,f,g,h)}})}}var f;a.util.Dispatcher=d,a.onBeforeUnload=new d(a,"BeforeUnload"),a.onAddEditor=new d(a,"AddEditor","editor"),a.onRemoveEditor=new d(a,"RemoveEditor","editor"),a.util.Cookie={get:b,getHash:b,remove:b,set:b,setHash:b},a.on("SetupEditor",e),a.PluginManager.add("compat3x",e),a.addI18n=function(b,c){var d=a.util.I18n,e=a.each;return"string"==typeof b&&-1===b.indexOf(".")?void d.add(b,c):void(a.is(b,"string")?e(c,function(a,c){d.data[b+"."+c]=a}):e(b,function(a,b){e(a,function(a,c){e(a,function(a,e){"common"===c?d.data[b+"."+e]=a:d.data[b+"."+c+"."+e]=a})})}))}}(tinymce); \ No newline at end of file diff --git a/wp-includes/js/tinymce/plugins/directionality/plugin.js b/wp-includes/js/tinymce/plugins/directionality/plugin.js new file mode 100644 index 0000000..3fd0dab --- /dev/null +++ b/wp-includes/js/tinymce/plugins/directionality/plugin.js @@ -0,0 +1,64 @@ +/** + * plugin.js + * + * Copyright, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/*global tinymce:true */ + +tinymce.PluginManager.add('directionality', function(editor) { + function setDir(dir) { + var dom = editor.dom, curDir, blocks = editor.selection.getSelectedBlocks(); + + if (blocks.length) { + curDir = dom.getAttrib(blocks[0], "dir"); + + tinymce.each(blocks, function(block) { + // Add dir to block if the parent block doesn't already have that dir + if (!dom.getParent(block.parentNode, "*[dir='" + dir + "']", dom.getRoot())) { + if (curDir != dir) { + dom.setAttrib(block, "dir", dir); + } else { + dom.setAttrib(block, "dir", null); + } + } + }); + + editor.nodeChanged(); + } + } + + function generateSelector(dir) { + var selector = []; + + tinymce.each('h1 h2 h3 h4 h5 h6 div p'.split(' '), function(name) { + selector.push(name + '[dir=' + dir + ']'); + }); + + return selector.join(','); + } + + editor.addCommand('mceDirectionLTR', function() { + setDir("ltr"); + }); + + editor.addCommand('mceDirectionRTL', function() { + setDir("rtl"); + }); + + editor.addButton('ltr', { + title: 'Left to right', + cmd: 'mceDirectionLTR', + stateSelector: generateSelector('ltr') + }); + + editor.addButton('rtl', { + title: 'Right to left', + cmd: 'mceDirectionRTL', + stateSelector: generateSelector('rtl') + }); +}); \ No newline at end of file diff --git a/wp-includes/js/tinymce/plugins/directionality/plugin.min.js b/wp-includes/js/tinymce/plugins/directionality/plugin.min.js new file mode 100644 index 0000000..43caba6 --- /dev/null +++ b/wp-includes/js/tinymce/plugins/directionality/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("directionality",function(a){function b(b){var c,d=a.dom,e=a.selection.getSelectedBlocks();e.length&&(c=d.getAttrib(e[0],"dir"),tinymce.each(e,function(a){d.getParent(a.parentNode,"*[dir='"+b+"']",d.getRoot())||(c!=b?d.setAttrib(a,"dir",b):d.setAttrib(a,"dir",null))}),a.nodeChanged())}function c(a){var b=[];return tinymce.each("h1 h2 h3 h4 h5 h6 div p".split(" "),function(c){b.push(c+"[dir="+a+"]")}),b.join(",")}a.addCommand("mceDirectionLTR",function(){b("ltr")}),a.addCommand("mceDirectionRTL",function(){b("rtl")}),a.addButton("ltr",{title:"Left to right",cmd:"mceDirectionLTR",stateSelector:c("ltr")}),a.addButton("rtl",{title:"Right to left",cmd:"mceDirectionRTL",stateSelector:c("rtl")})}); \ No newline at end of file diff --git a/wp-includes/js/tinymce/plugins/fullscreen/plugin.js b/wp-includes/js/tinymce/plugins/fullscreen/plugin.js new file mode 100644 index 0000000..e518701 --- /dev/null +++ b/wp-includes/js/tinymce/plugins/fullscreen/plugin.js @@ -0,0 +1,136 @@ +/** + * plugin.js + * + * Copyright, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/*global tinymce:true */ + +tinymce.PluginManager.add('fullscreen', function(editor) { + var fullscreenState = false, DOM = tinymce.DOM, iframeWidth, iframeHeight, resizeHandler; + var containerWidth, containerHeight; + + if (editor.settings.inline) { + return; + } + + function getWindowSize() { + var w, h, win = window, doc = document; + var body = doc.body; + + // Old IE + if (body.offsetWidth) { + w = body.offsetWidth; + h = body.offsetHeight; + } + + // Modern browsers + if (win.innerWidth && win.innerHeight) { + w = win.innerWidth; + h = win.innerHeight; + } + + return {w: w, h: h}; + } + + function toggleFullscreen() { + var body = document.body, documentElement = document.documentElement, editorContainerStyle; + var editorContainer, iframe, iframeStyle; + + function resize() { + DOM.setStyle(iframe, 'height', getWindowSize().h - (editorContainer.clientHeight - iframe.clientHeight)); + } + + fullscreenState = !fullscreenState; + + editorContainer = editor.getContainer(); + editorContainerStyle = editorContainer.style; + iframe = editor.getContentAreaContainer().firstChild; + iframeStyle = iframe.style; + + if (fullscreenState) { + iframeWidth = iframeStyle.width; + iframeHeight = iframeStyle.height; + iframeStyle.width = iframeStyle.height = '100%'; + containerWidth = editorContainerStyle.width; + containerHeight = editorContainerStyle.height; + editorContainerStyle.width = editorContainerStyle.height = ''; + + DOM.addClass(body, 'mce-fullscreen'); + DOM.addClass(documentElement, 'mce-fullscreen'); + DOM.addClass(editorContainer, 'mce-fullscreen'); + + DOM.bind(window, 'resize', resize); + resize(); + resizeHandler = resize; + } else { + iframeStyle.width = iframeWidth; + iframeStyle.height = iframeHeight; + + if (containerWidth) { + editorContainerStyle.width = containerWidth; + } + + if (containerHeight) { + editorContainerStyle.height = containerHeight; + } + + DOM.removeClass(body, 'mce-fullscreen'); + DOM.removeClass(documentElement, 'mce-fullscreen'); + DOM.removeClass(editorContainer, 'mce-fullscreen'); + DOM.unbind(window, 'resize', resizeHandler); + } + + editor.fire('FullscreenStateChanged', {state: fullscreenState}); + } + + editor.on('init', function() { + editor.addShortcut('Ctrl+Alt+F', '', toggleFullscreen); + }); + + editor.on('remove', function() { + if (resizeHandler) { + DOM.unbind(window, 'resize', resizeHandler); + } + }); + + editor.addCommand('mceFullScreen', toggleFullscreen); + + editor.addMenuItem('fullscreen', { + text: 'Fullscreen', + shortcut: 'Ctrl+Alt+F', + selectable: true, + onClick: toggleFullscreen, + onPostRender: function() { + var self = this; + + editor.on('FullscreenStateChanged', function(e) { + self.active(e.state); + }); + }, + context: 'view' + }); + + editor.addButton('fullscreen', { + tooltip: 'Fullscreen', + shortcut: 'Ctrl+Alt+F', + onClick: toggleFullscreen, + onPostRender: function() { + var self = this; + + editor.on('FullscreenStateChanged', function(e) { + self.active(e.state); + }); + } + }); + + return { + isFullscreen: function() { + return fullscreenState; + } + }; +}); \ No newline at end of file diff --git a/wp-includes/js/tinymce/plugins/fullscreen/plugin.min.js b/wp-includes/js/tinymce/plugins/fullscreen/plugin.min.js new file mode 100644 index 0000000..5b128c2 --- /dev/null +++ b/wp-includes/js/tinymce/plugins/fullscreen/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("fullscreen",function(a){function b(){var a,b,c=window,d=document,e=d.body;return e.offsetWidth&&(a=e.offsetWidth,b=e.offsetHeight),c.innerWidth&&c.innerHeight&&(a=c.innerWidth,b=c.innerHeight),{w:a,h:b}}function c(){function c(){j.setStyle(m,"height",b().h-(l.clientHeight-m.clientHeight))}var k,l,m,n,o=document.body,p=document.documentElement;i=!i,l=a.getContainer(),k=l.style,m=a.getContentAreaContainer().firstChild,n=m.style,i?(d=n.width,e=n.height,n.width=n.height="100%",g=k.width,h=k.height,k.width=k.height="",j.addClass(o,"mce-fullscreen"),j.addClass(p,"mce-fullscreen"),j.addClass(l,"mce-fullscreen"),j.bind(window,"resize",c),c(),f=c):(n.width=d,n.height=e,g&&(k.width=g),h&&(k.height=h),j.removeClass(o,"mce-fullscreen"),j.removeClass(p,"mce-fullscreen"),j.removeClass(l,"mce-fullscreen"),j.unbind(window,"resize",f)),a.fire("FullscreenStateChanged",{state:i})}var d,e,f,g,h,i=!1,j=tinymce.DOM;return a.settings.inline?void 0:(a.on("init",function(){a.addShortcut("Ctrl+Alt+F","",c)}),a.on("remove",function(){f&&j.unbind(window,"resize",f)}),a.addCommand("mceFullScreen",c),a.addMenuItem("fullscreen",{text:"Fullscreen",shortcut:"Ctrl+Alt+F",selectable:!0,onClick:c,onPostRender:function(){var b=this;a.on("FullscreenStateChanged",function(a){b.active(a.state)})},context:"view"}),a.addButton("fullscreen",{tooltip:"Fullscreen",shortcut:"Ctrl+Alt+F",onClick:c,onPostRender:function(){var b=this;a.on("FullscreenStateChanged",function(a){b.active(a.state)})}}),{isFullscreen:function(){return i}})}); \ No newline at end of file diff --git a/wp-includes/js/tinymce/plugins/hr/plugin.js b/wp-includes/js/tinymce/plugins/hr/plugin.js new file mode 100644 index 0000000..915a563 --- /dev/null +++ b/wp-includes/js/tinymce/plugins/hr/plugin.js @@ -0,0 +1,30 @@ +/** + * plugin.js + * + * Copyright, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/*global tinymce:true */ + +tinymce.PluginManager.add('hr', function(editor) { + editor.addCommand('InsertHorizontalRule', function() { + editor.execCommand('mceInsertContent', false, '
        '); + }); + + editor.addButton('hr', { + icon: 'hr', + tooltip: 'Horizontal line', + cmd: 'InsertHorizontalRule' + }); + + editor.addMenuItem('hr', { + icon: 'hr', + text: 'Horizontal line', + cmd: 'InsertHorizontalRule', + context: 'insert' + }); +}); diff --git a/wp-includes/js/tinymce/plugins/hr/plugin.min.js b/wp-includes/js/tinymce/plugins/hr/plugin.min.js new file mode 100644 index 0000000..25abb0c --- /dev/null +++ b/wp-includes/js/tinymce/plugins/hr/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("hr",function(a){a.addCommand("InsertHorizontalRule",function(){a.execCommand("mceInsertContent",!1,"
        ")}),a.addButton("hr",{icon:"hr",tooltip:"Horizontal line",cmd:"InsertHorizontalRule"}),a.addMenuItem("hr",{icon:"hr",text:"Horizontal line",cmd:"InsertHorizontalRule",context:"insert"})}); \ No newline at end of file diff --git a/wp-includes/js/tinymce/plugins/image/plugin.js b/wp-includes/js/tinymce/plugins/image/plugin.js new file mode 100644 index 0000000..a478da5 --- /dev/null +++ b/wp-includes/js/tinymce/plugins/image/plugin.js @@ -0,0 +1,459 @@ +/** + * plugin.js + * + * Copyright, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/*global tinymce:true */ + +tinymce.PluginManager.add('image', function(editor) { + function getImageSize(url, callback) { + var img = document.createElement('img'); + + function done(width, height) { + if (img.parentNode) { + img.parentNode.removeChild(img); + } + + callback({width: width, height: height}); + } + + img.onload = function() { + done(img.clientWidth, img.clientHeight); + }; + + img.onerror = function() { + done(); + }; + + var style = img.style; + style.visibility = 'hidden'; + style.position = 'fixed'; + style.bottom = style.left = 0; + style.width = style.height = 'auto'; + + document.body.appendChild(img); + img.src = url; + } + + function buildListItems(inputList, itemCallback, startItems) { + function appendItems(values, output) { + output = output || []; + + tinymce.each(values, function(item) { + var menuItem = {text: item.text || item.title}; + + if (item.menu) { + menuItem.menu = appendItems(item.menu); + } else { + menuItem.value = item.value; + itemCallback(menuItem); + } + + output.push(menuItem); + }); + + return output; + } + + return appendItems(inputList, startItems || []); + } + + function createImageList(callback) { + return function() { + var imageList = editor.settings.image_list; + + if (typeof(imageList) == "string") { + tinymce.util.XHR.send({ + url: imageList, + success: function(text) { + callback(tinymce.util.JSON.parse(text)); + } + }); + } else if (typeof(imageList) == "function") { + imageList(callback); + } else { + callback(imageList); + } + }; + } + + function showDialog(imageList) { + var win, data = {}, dom = editor.dom, imgElm = editor.selection.getNode(); + var width, height, imageListCtrl, classListCtrl, imageDimensions = editor.settings.image_dimensions !== false; + + function recalcSize() { + var widthCtrl, heightCtrl, newWidth, newHeight; + + widthCtrl = win.find('#width')[0]; + heightCtrl = win.find('#height')[0]; + + if (!widthCtrl || !heightCtrl) { + return; + } + + newWidth = widthCtrl.value(); + newHeight = heightCtrl.value(); + + if (win.find('#constrain')[0].checked() && width && height && newWidth && newHeight) { + if (width != newWidth) { + newHeight = Math.round((newWidth / width) * newHeight); + heightCtrl.value(newHeight); + } else { + newWidth = Math.round((newHeight / height) * newWidth); + widthCtrl.value(newWidth); + } + } + + width = newWidth; + height = newHeight; + } + + function onSubmitForm() { + function waitLoad(imgElm) { + function selectImage() { + imgElm.onload = imgElm.onerror = null; + + if (editor.selection) { + editor.selection.select(imgElm); + editor.nodeChanged(); + } + } + + imgElm.onload = function() { + if (!data.width && !data.height && imageDimensions) { + dom.setAttribs(imgElm, { + width: imgElm.clientWidth, + height: imgElm.clientHeight + }); + //WP + editor.fire( 'wpNewImageRefresh', { node: imgElm } ); + } + + selectImage(); + }; + + imgElm.onerror = selectImage; + } + + updateStyle(); + recalcSize(); + + data = tinymce.extend(data, win.toJSON()); + var caption = data.caption; // WP + + if (!data.alt) { + data.alt = ''; + } + + if (data.width === '') { + data.width = null; + } + + if (data.height === '') { + data.height = null; + } + + if (!data.style) { + data.style = null; + } + + // Setup new data excluding style properties + data = { + src: data.src, + alt: data.alt, + width: data.width, + height: data.height, + style: data.style, + "class": data["class"] + }; + + editor.undoManager.transact(function() { + // WP + var eventData = { node: imgElm, data: data, caption: caption }; + + editor.fire( 'wpImageFormSubmit', { imgData: eventData } ); + + if ( eventData.cancel ) { + waitLoad( eventData.node ); + return; + } + // WP end + + if (!data.src) { + if (imgElm) { + dom.remove(imgElm); + editor.focus(); + editor.nodeChanged(); + } + + return; + } + + if (!imgElm) { + data.id = '__mcenew'; + editor.focus(); + editor.selection.setContent(dom.createHTML('img', data)); + imgElm = dom.get('__mcenew'); + dom.setAttrib(imgElm, 'id', null); + } else { + dom.setAttribs(imgElm, data); + } + + waitLoad(imgElm); + }); + } + + function removePixelSuffix(value) { + if (value) { + value = value.replace(/px$/, ''); + } + + return value; + } + + function srcChange(e) { + var meta = e.meta || {}; + + if (imageListCtrl) { + imageListCtrl.value(editor.convertURL(this.value(), 'src')); + } + + tinymce.each(meta, function(value, key) { + win.find('#' + key).value(value); + }); + + if (!meta.width && !meta.height) { + var srcURL = this.value(), + absoluteURLPattern = new RegExp('^(?:[a-z]+:)?//', 'i'), + baseURL = editor.settings.document_base_url; + + //Pattern test the src url and make sure we haven't already prepended the url + if (baseURL && !absoluteURLPattern.test(srcURL) && srcURL.substring(0, baseURL.length) !== baseURL) { + this.value(baseURL + srcURL); + } + + getImageSize(this.value(), function(data) { + if (data.width && data.height && imageDimensions) { + width = data.width; + height = data.height; + + win.find('#width').value(width); + win.find('#height').value(height); + } + }); + } + } + + width = dom.getAttrib(imgElm, 'width'); + height = dom.getAttrib(imgElm, 'height'); + + if (imgElm.nodeName == 'IMG' && !imgElm.getAttribute('data-mce-object') && !imgElm.getAttribute('data-mce-placeholder')) { + data = { + src: dom.getAttrib(imgElm, 'src'), + alt: dom.getAttrib(imgElm, 'alt'), + "class": dom.getAttrib(imgElm, 'class'), + width: width, + height: height + }; + + // WP + editor.fire( 'wpLoadImageData', { imgData: { data: data, node: imgElm } } ); + } else { + imgElm = null; + } + + if (imageList) { + imageListCtrl = { + type: 'listbox', + label: 'Image list', + values: buildListItems( + imageList, + function(item) { + item.value = editor.convertURL(item.value || item.url, 'src'); + }, + [{text: 'None', value: ''}] + ), + value: data.src && editor.convertURL(data.src, 'src'), + onselect: function(e) { + var altCtrl = win.find('#alt'); + + if (!altCtrl.value() || (e.lastControl && altCtrl.value() == e.lastControl.text())) { + altCtrl.value(e.control.text()); + } + + win.find('#src').value(e.control.value()).fire('change'); + }, + onPostRender: function() { + imageListCtrl = this; + } + }; + } + + if (editor.settings.image_class_list) { + classListCtrl = { + name: 'class', + type: 'listbox', + label: 'Class', + values: buildListItems( + editor.settings.image_class_list, + function(item) { + if (item.value) { + item.textStyle = function() { + return editor.formatter.getCssText({inline: 'img', classes: [item.value]}); + }; + } + } + ) + }; + } + + // General settings shared between simple and advanced dialogs + var generalFormItems = [ + { + name: 'src', + type: 'filepicker', + filetype: 'image', + label: 'Source', + autofocus: true, + onchange: srcChange + }, + imageListCtrl + ]; + + if (editor.settings.image_description !== false) { + generalFormItems.push({name: 'alt', type: 'textbox', label: 'Image description'}); + } + + if (imageDimensions) { + generalFormItems.push({ + type: 'container', + label: 'Dimensions', + layout: 'flex', + direction: 'row', + align: 'center', + spacing: 5, + items: [ + {name: 'width', type: 'textbox', maxLength: 5, size: 3, onchange: recalcSize, ariaLabel: 'Width'}, + {type: 'label', text: 'x'}, + {name: 'height', type: 'textbox', maxLength: 5, size: 3, onchange: recalcSize, ariaLabel: 'Height'}, + {name: 'constrain', type: 'checkbox', checked: true, text: 'Constrain proportions'} + ] + }); + } + + generalFormItems.push(classListCtrl); + + // WP + editor.fire( 'wpLoadImageForm', { data: generalFormItems } ); + + function updateStyle() { + function addPixelSuffix(value) { + if (value.length > 0 && /^[0-9]+$/.test(value)) { + value += 'px'; + } + + return value; + } + + if (!editor.settings.image_advtab) { + return; + } + + var data = win.toJSON(); + var css = dom.parseStyle(data.style); + + delete css.margin; + css['margin-top'] = css['margin-bottom'] = addPixelSuffix(data.vspace); + css['margin-left'] = css['margin-right'] = addPixelSuffix(data.hspace); + css['border-width'] = addPixelSuffix(data.border); + + win.find('#style').value(dom.serializeStyle(dom.parseStyle(dom.serializeStyle(css)))); + } + + if (editor.settings.image_advtab) { + // Parse styles from img + if (imgElm) { + data.hspace = removePixelSuffix(imgElm.style.marginLeft || imgElm.style.marginRight); + data.vspace = removePixelSuffix(imgElm.style.marginTop || imgElm.style.marginBottom); + data.border = removePixelSuffix(imgElm.style.borderWidth); + data.style = editor.dom.serializeStyle(editor.dom.parseStyle(editor.dom.getAttrib(imgElm, 'style'))); + } + + // Advanced dialog shows general+advanced tabs + win = editor.windowManager.open({ + title: 'Insert/edit image', + data: data, + bodyType: 'tabpanel', + body: [ + { + title: 'General', + type: 'form', + items: generalFormItems + }, + + { + title: 'Advanced', + type: 'form', + pack: 'start', + items: [ + { + label: 'Style', + name: 'style', + type: 'textbox' + }, + { + type: 'form', + layout: 'grid', + packV: 'start', + columns: 2, + padding: 0, + alignH: ['left', 'right'], + defaults: { + type: 'textbox', + maxWidth: 50, + onchange: updateStyle + }, + items: [ + {label: 'Vertical space', name: 'vspace'}, + {label: 'Horizontal space', name: 'hspace'}, + {label: 'Border', name: 'border'} + ] + } + ] + } + ], + onSubmit: onSubmitForm + }); + } else { + // Simple default dialog + win = editor.windowManager.open({ + title: 'Insert/edit image', + data: data, + body: generalFormItems, + onSubmit: onSubmitForm + }); + } + } + + editor.addButton('image', { + icon: 'image', + tooltip: 'Insert/edit image', + onclick: createImageList(showDialog), + stateSelector: 'img:not([data-mce-object],[data-mce-placeholder])' + }); + + editor.addMenuItem('image', { + icon: 'image', + text: 'Insert image', + onclick: createImageList(showDialog), + context: 'insert', + prependToContext: true + }); + + editor.addCommand('mceImage', createImageList(showDialog)); +}); diff --git a/wp-includes/js/tinymce/plugins/image/plugin.min.js b/wp-includes/js/tinymce/plugins/image/plugin.min.js new file mode 100644 index 0000000..0a9492f --- /dev/null +++ b/wp-includes/js/tinymce/plugins/image/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("image",function(a){function b(a,b){function c(a,c){d.parentNode&&d.parentNode.removeChild(d),b({width:a,height:c})}var d=document.createElement("img");d.onload=function(){c(d.clientWidth,d.clientHeight)},d.onerror=function(){c()};var e=d.style;e.visibility="hidden",e.position="fixed",e.bottom=e.left=0,e.width=e.height="auto",document.body.appendChild(d),d.src=a}function c(a,b,c){function d(a,c){return c=c||[],tinymce.each(a,function(a){var e={text:a.text||a.title};a.menu?e.menu=d(a.menu):(e.value=a.value,b(e)),c.push(e)}),c}return d(a,c||[])}function d(b){return function(){var c=a.settings.image_list;"string"==typeof c?tinymce.util.XHR.send({url:c,success:function(a){b(tinymce.util.JSON.parse(a))}}):"function"==typeof c?c(b):b(c)}}function e(d){function e(){var a,b,c,d;a=j.find("#width")[0],b=j.find("#height")[0],a&&b&&(c=a.value(),d=b.value(),j.find("#constrain")[0].checked()&&k&&l&&c&&d&&(k!=c?(d=Math.round(c/k*d),b.value(d)):(c=Math.round(d/l*c),a.value(c))),k=c,l=d)}function f(){function b(b){function c(){b.onload=b.onerror=null,a.selection&&(a.selection.select(b),a.nodeChanged())}b.onload=function(){o.width||o.height||!r||(p.setAttribs(b,{width:b.clientWidth,height:b.clientHeight}),a.fire("wpNewImageRefresh",{node:b})),c()},b.onerror=c}i(),e(),o=tinymce.extend(o,j.toJSON());var c=o.caption;o.alt||(o.alt=""),""===o.width&&(o.width=null),""===o.height&&(o.height=null),o.style||(o.style=null),o={src:o.src,alt:o.alt,width:o.width,height:o.height,style:o.style,"class":o["class"]},a.undoManager.transact(function(){var d={node:q,data:o,caption:c};return a.fire("wpImageFormSubmit",{imgData:d}),d.cancel?void b(d.node):o.src?(q?p.setAttribs(q,o):(o.id="__mcenew",a.focus(),a.selection.setContent(p.createHTML("img",o)),q=p.get("__mcenew"),p.setAttrib(q,"id",null)),void b(q)):void(q&&(p.remove(q),a.focus(),a.nodeChanged()))})}function g(a){return a&&(a=a.replace(/px$/,"")),a}function h(c){var d=c.meta||{};if(m&&m.value(a.convertURL(this.value(),"src")),tinymce.each(d,function(a,b){j.find("#"+b).value(a)}),!d.width&&!d.height){var e=this.value(),f=new RegExp("^(?:[a-z]+:)?//","i"),g=a.settings.document_base_url;g&&!f.test(e)&&e.substring(0,g.length)!==g&&this.value(g+e),b(this.value(),function(a){a.width&&a.height&&r&&(k=a.width,l=a.height,j.find("#width").value(k),j.find("#height").value(l))})}}function i(){function b(a){return a.length>0&&/^[0-9]+$/.test(a)&&(a+="px"),a}if(a.settings.image_advtab){var c=j.toJSON(),d=p.parseStyle(c.style);delete d.margin,d["margin-top"]=d["margin-bottom"]=b(c.vspace),d["margin-left"]=d["margin-right"]=b(c.hspace),d["border-width"]=b(c.border),j.find("#style").value(p.serializeStyle(p.parseStyle(p.serializeStyle(d))))}}var j,k,l,m,n,o={},p=a.dom,q=a.selection.getNode(),r=a.settings.image_dimensions!==!1;k=p.getAttrib(q,"width"),l=p.getAttrib(q,"height"),"IMG"!=q.nodeName||q.getAttribute("data-mce-object")||q.getAttribute("data-mce-placeholder")?q=null:(o={src:p.getAttrib(q,"src"),alt:p.getAttrib(q,"alt"),"class":p.getAttrib(q,"class"),width:k,height:l},a.fire("wpLoadImageData",{imgData:{data:o,node:q}})),d&&(m={type:"listbox",label:"Image list",values:c(d,function(b){b.value=a.convertURL(b.value||b.url,"src")},[{text:"None",value:""}]),value:o.src&&a.convertURL(o.src,"src"),onselect:function(a){var b=j.find("#alt");(!b.value()||a.lastControl&&b.value()==a.lastControl.text())&&b.value(a.control.text()),j.find("#src").value(a.control.value()).fire("change")},onPostRender:function(){m=this}}),a.settings.image_class_list&&(n={name:"class",type:"listbox",label:"Class",values:c(a.settings.image_class_list,function(b){b.value&&(b.textStyle=function(){return a.formatter.getCssText({inline:"img",classes:[b.value]})})})});var s=[{name:"src",type:"filepicker",filetype:"image",label:"Source",autofocus:!0,onchange:h},m];a.settings.image_description!==!1&&s.push({name:"alt",type:"textbox",label:"Image description"}),r&&s.push({type:"container",label:"Dimensions",layout:"flex",direction:"row",align:"center",spacing:5,items:[{name:"width",type:"textbox",maxLength:5,size:3,onchange:e,ariaLabel:"Width"},{type:"label",text:"x"},{name:"height",type:"textbox",maxLength:5,size:3,onchange:e,ariaLabel:"Height"},{name:"constrain",type:"checkbox",checked:!0,text:"Constrain proportions"}]}),s.push(n),a.fire("wpLoadImageForm",{data:s}),a.settings.image_advtab?(q&&(o.hspace=g(q.style.marginLeft||q.style.marginRight),o.vspace=g(q.style.marginTop||q.style.marginBottom),o.border=g(q.style.borderWidth),o.style=a.dom.serializeStyle(a.dom.parseStyle(a.dom.getAttrib(q,"style")))),j=a.windowManager.open({title:"Insert/edit image",data:o,bodyType:"tabpanel",body:[{title:"General",type:"form",items:s},{title:"Advanced",type:"form",pack:"start",items:[{label:"Style",name:"style",type:"textbox"},{type:"form",layout:"grid",packV:"start",columns:2,padding:0,alignH:["left","right"],defaults:{type:"textbox",maxWidth:50,onchange:i},items:[{label:"Vertical space",name:"vspace"},{label:"Horizontal space",name:"hspace"},{label:"Border",name:"border"}]}]}],onSubmit:f})):j=a.windowManager.open({title:"Insert/edit image",data:o,body:s,onSubmit:f})}a.addButton("image",{icon:"image",tooltip:"Insert/edit image",onclick:d(e),stateSelector:"img:not([data-mce-object],[data-mce-placeholder])"}),a.addMenuItem("image",{icon:"image",text:"Insert image",onclick:d(e),context:"insert",prependToContext:!0}),a.addCommand("mceImage",d(e))}); \ No newline at end of file diff --git a/wp-includes/js/tinymce/plugins/lists/plugin.js b/wp-includes/js/tinymce/plugins/lists/plugin.js new file mode 100644 index 0000000..036ba67 --- /dev/null +++ b/wp-includes/js/tinymce/plugins/lists/plugin.js @@ -0,0 +1,791 @@ +/** + * plugin.js + * + * Copyright, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/*global tinymce:true */ +/*eslint consistent-this:0 */ + +tinymce.PluginManager.add('lists', function(editor) { + var self = this; + + function isListNode(node) { + return node && (/^(OL|UL|DL)$/).test(node.nodeName); + } + + function isFirstChild(node) { + return node.parentNode.firstChild == node; + } + + function isLastChild(node) { + return node.parentNode.lastChild == node; + } + + function isTextBlock(node) { + return node && !!editor.schema.getTextBlockElements()[node.nodeName]; + } + + editor.on('init', function() { + var dom = editor.dom, selection = editor.selection; + + /** + * Returns a range bookmark. This will convert indexed bookmarks into temporary span elements with + * index 0 so that they can be restored properly after the DOM has been modified. Text bookmarks will not have spans + * added to them since they can be restored after a dom operation. + * + * So this:

        ||

        + * becomes:

        ||

        + * + * @param {DOMRange} rng DOM Range to get bookmark on. + * @return {Object} Bookmark object. + */ + function createBookmark(rng) { + var bookmark = {}; + + function setupEndPoint(start) { + var offsetNode, container, offset; + + container = rng[start ? 'startContainer' : 'endContainer']; + offset = rng[start ? 'startOffset' : 'endOffset']; + + if (container.nodeType == 1) { + offsetNode = dom.create('span', {'data-mce-type': 'bookmark'}); + + if (container.hasChildNodes()) { + offset = Math.min(offset, container.childNodes.length - 1); + + if (start) { + container.insertBefore(offsetNode, container.childNodes[offset]); + } else { + dom.insertAfter(offsetNode, container.childNodes[offset]); + } + } else { + container.appendChild(offsetNode); + } + + container = offsetNode; + offset = 0; + } + + bookmark[start ? 'startContainer' : 'endContainer'] = container; + bookmark[start ? 'startOffset' : 'endOffset'] = offset; + } + + setupEndPoint(true); + + if (!rng.collapsed) { + setupEndPoint(); + } + + return bookmark; + } + + /** + * Moves the selection to the current bookmark and removes any selection container wrappers. + * + * @param {Object} bookmark Bookmark object to move selection to. + */ + function moveToBookmark(bookmark) { + function restoreEndPoint(start) { + var container, offset, node; + + function nodeIndex(container) { + var node = container.parentNode.firstChild, idx = 0; + + while (node) { + if (node == container) { + return idx; + } + + // Skip data-mce-type=bookmark nodes + if (node.nodeType != 1 || node.getAttribute('data-mce-type') != 'bookmark') { + idx++; + } + + node = node.nextSibling; + } + + return -1; + } + + container = node = bookmark[start ? 'startContainer' : 'endContainer']; + offset = bookmark[start ? 'startOffset' : 'endOffset']; + + if (!container) { + return; + } + + if (container.nodeType == 1) { + offset = nodeIndex(container); + container = container.parentNode; + dom.remove(node); + } + + bookmark[start ? 'startContainer' : 'endContainer'] = container; + bookmark[start ? 'startOffset' : 'endOffset'] = offset; + } + + restoreEndPoint(true); + restoreEndPoint(); + + var rng = dom.createRng(); + + rng.setStart(bookmark.startContainer, bookmark.startOffset); + + if (bookmark.endContainer) { + rng.setEnd(bookmark.endContainer, bookmark.endOffset); + } + + selection.setRng(rng); + } + + function createNewTextBlock(contentNode, blockName) { + var node, textBlock, fragment = dom.createFragment(), hasContentNode; + var blockElements = editor.schema.getBlockElements(); + + if (editor.settings.forced_root_block) { + blockName = blockName || editor.settings.forced_root_block; + } + + if (blockName) { + textBlock = dom.create(blockName); + + if (textBlock.tagName === editor.settings.forced_root_block) { + dom.setAttribs(textBlock, editor.settings.forced_root_block_attrs); + } + + fragment.appendChild(textBlock); + } + + if (contentNode) { + while ((node = contentNode.firstChild)) { + var nodeName = node.nodeName; + + if (!hasContentNode && (nodeName != 'SPAN' || node.getAttribute('data-mce-type') != 'bookmark')) { + hasContentNode = true; + } + + if (blockElements[nodeName]) { + fragment.appendChild(node); + textBlock = null; + } else { + if (blockName) { + if (!textBlock) { + textBlock = dom.create(blockName); + fragment.appendChild(textBlock); + } + + textBlock.appendChild(node); + } else { + fragment.appendChild(node); + } + } + } + } + + if (!editor.settings.forced_root_block) { + fragment.appendChild(dom.create('br')); + } else { + // BR is needed in empty blocks on non IE browsers + if (!hasContentNode && (!tinymce.Env.ie || tinymce.Env.ie > 10)) { + textBlock.appendChild(dom.create('br', {'data-mce-bogus': '1'})); + } + } + + return fragment; + } + + function getSelectedListItems() { + return tinymce.grep(selection.getSelectedBlocks(), function(block) { + return /^(LI|DT|DD)$/.test(block.nodeName); + }); + } + + function splitList(ul, li, newBlock) { + var tmpRng, fragment; + + var bookmarks = dom.select('span[data-mce-type="bookmark"]', ul); + + newBlock = newBlock || createNewTextBlock(li); + tmpRng = dom.createRng(); + tmpRng.setStartAfter(li); + tmpRng.setEndAfter(ul); + fragment = tmpRng.extractContents(); + + if (!dom.isEmpty(fragment)) { + dom.insertAfter(fragment, ul); + } + + dom.insertAfter(newBlock, ul); + + if (dom.isEmpty(li.parentNode)) { + tinymce.each(bookmarks, function(node) { + li.parentNode.parentNode.insertBefore(node, li.parentNode); + }); + + dom.remove(li.parentNode); + } + + dom.remove(li); + } + + function mergeWithAdjacentLists(listBlock) { + var sibling, node; + + sibling = listBlock.nextSibling; + if (sibling && isListNode(sibling) && sibling.nodeName == listBlock.nodeName) { + while ((node = sibling.firstChild)) { + listBlock.appendChild(node); + } + + dom.remove(sibling); + } + + sibling = listBlock.previousSibling; + if (sibling && isListNode(sibling) && sibling.nodeName == listBlock.nodeName) { + while ((node = sibling.firstChild)) { + listBlock.insertBefore(node, listBlock.firstChild); + } + + dom.remove(sibling); + } + } + + /** + * Normalizes the all lists in the specified element. + */ + function normalizeList(element) { + tinymce.each(tinymce.grep(dom.select('ol,ul', element)), function(ul) { + var sibling, parentNode = ul.parentNode; + + // Move UL/OL to previous LI if it's the only child of a LI + if (parentNode.nodeName == 'LI' && parentNode.firstChild == ul) { + sibling = parentNode.previousSibling; + if (sibling && sibling.nodeName == 'LI') { + sibling.appendChild(ul); + + if (dom.isEmpty(parentNode)) { + dom.remove(parentNode); + } + } + } + + // Append OL/UL to previous LI if it's in a parent OL/UL i.e. old HTML4 + if (isListNode(parentNode)) { + sibling = parentNode.previousSibling; + if (sibling && sibling.nodeName == 'LI') { + sibling.appendChild(ul); + } + } + }); + } + + function outdent(li) { + var ul = li.parentNode, ulParent = ul.parentNode, newBlock; + + function removeEmptyLi(li) { + if (dom.isEmpty(li)) { + dom.remove(li); + } + } + + if (li.nodeName == 'DD') { + dom.rename(li, 'DT'); + return true; + } + + if (isFirstChild(li) && isLastChild(li)) { + if (ulParent.nodeName == "LI") { + dom.insertAfter(li, ulParent); + removeEmptyLi(ulParent); + dom.remove(ul); + } else if (isListNode(ulParent)) { + dom.remove(ul, true); + } else { + ulParent.insertBefore(createNewTextBlock(li), ul); + dom.remove(ul); + } + + return true; + } else if (isFirstChild(li)) { + if (ulParent.nodeName == "LI") { + dom.insertAfter(li, ulParent); + li.appendChild(ul); + removeEmptyLi(ulParent); + } else if (isListNode(ulParent)) { + ulParent.insertBefore(li, ul); + } else { + ulParent.insertBefore(createNewTextBlock(li), ul); + dom.remove(li); + } + + return true; + } else if (isLastChild(li)) { + if (ulParent.nodeName == "LI") { + dom.insertAfter(li, ulParent); + } else if (isListNode(ulParent)) { + dom.insertAfter(li, ul); + } else { + dom.insertAfter(createNewTextBlock(li), ul); + dom.remove(li); + } + + return true; + } else { + if (ulParent.nodeName == 'LI') { + ul = ulParent; + newBlock = createNewTextBlock(li, 'LI'); + } else if (isListNode(ulParent)) { + newBlock = createNewTextBlock(li, 'LI'); + } else { + newBlock = createNewTextBlock(li); + } + + splitList(ul, li, newBlock); + normalizeList(ul.parentNode); + + return true; + } + + return false; + } + + function indent(li) { + var sibling, newList; + + function mergeLists(from, to) { + var node; + + if (isListNode(from)) { + while ((node = li.lastChild.firstChild)) { + to.appendChild(node); + } + + dom.remove(from); + } + } + + if (li.nodeName == 'DT') { + dom.rename(li, 'DD'); + return true; + } + + sibling = li.previousSibling; + + if (sibling && isListNode(sibling)) { + sibling.appendChild(li); + return true; + } + + if (sibling && sibling.nodeName == 'LI' && isListNode(sibling.lastChild)) { + sibling.lastChild.appendChild(li); + mergeLists(li.lastChild, sibling.lastChild); + return true; + } + + sibling = li.nextSibling; + + if (sibling && isListNode(sibling)) { + sibling.insertBefore(li, sibling.firstChild); + return true; + } + + if (sibling && sibling.nodeName == 'LI' && isListNode(li.lastChild)) { + return false; + } + + sibling = li.previousSibling; + if (sibling && sibling.nodeName == 'LI') { + newList = dom.create(li.parentNode.nodeName); + sibling.appendChild(newList); + newList.appendChild(li); + mergeLists(li.lastChild, newList); + return true; + } + + return false; + } + + function indentSelection() { + var listElements = getSelectedListItems(); + + if (listElements.length) { + var bookmark = createBookmark(selection.getRng(true)); + + for (var i = 0; i < listElements.length; i++) { + if (!indent(listElements[i]) && i === 0) { + break; + } + } + + moveToBookmark(bookmark); + editor.nodeChanged(); + + return true; + } + } + + function outdentSelection() { + var listElements = getSelectedListItems(); + + if (listElements.length) { + var bookmark = createBookmark(selection.getRng(true)); + var i, y, root = editor.getBody(); + + i = listElements.length; + while (i--) { + var node = listElements[i].parentNode; + + while (node && node != root) { + y = listElements.length; + while (y--) { + if (listElements[y] === node) { + listElements.splice(i, 1); + break; + } + } + + node = node.parentNode; + } + } + + for (i = 0; i < listElements.length; i++) { + if (!outdent(listElements[i]) && i === 0) { + break; + } + } + + moveToBookmark(bookmark); + editor.nodeChanged(); + + return true; + } + } + + function applyList(listName) { + var rng = selection.getRng(true), bookmark = createBookmark(rng), listItemName = 'LI'; + + listName = listName.toUpperCase(); + + if (listName == 'DL') { + listItemName = 'DT'; + } + + function getSelectedTextBlocks() { + var textBlocks = [], root = editor.getBody(); + + function getEndPointNode(start) { + var container, offset; + + container = rng[start ? 'startContainer' : 'endContainer']; + offset = rng[start ? 'startOffset' : 'endOffset']; + + // Resolve node index + if (container.nodeType == 1) { + container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container; + } + + while (container.parentNode != root) { + if (isTextBlock(container)) { + return container; + } + + if (/^(TD|TH)$/.test(container.parentNode.nodeName)) { + return container; + } + + container = container.parentNode; + } + + return container; + } + + var startNode = getEndPointNode(true); + var endNode = getEndPointNode(); + var block, siblings = []; + + for (var node = startNode; node; node = node.nextSibling) { + siblings.push(node); + + if (node == endNode) { + break; + } + } + + tinymce.each(siblings, function(node) { + if (isTextBlock(node)) { + textBlocks.push(node); + block = null; + return; + } + + if (dom.isBlock(node) || node.nodeName == 'BR') { + if (node.nodeName == 'BR') { + dom.remove(node); + } + + block = null; + return; + } + + var nextSibling = node.nextSibling; + if (tinymce.dom.BookmarkManager.isBookmarkNode(node)) { + if (isTextBlock(nextSibling) || (!nextSibling && node.parentNode == root)) { + block = null; + return; + } + } + + if (!block) { + block = dom.create('p'); + node.parentNode.insertBefore(block, node); + textBlocks.push(block); + } + + block.appendChild(node); + }); + + return textBlocks; + } + + tinymce.each(getSelectedTextBlocks(), function(block) { + var listBlock, sibling; + + sibling = block.previousSibling; + if (sibling && isListNode(sibling) && sibling.nodeName == listName) { + listBlock = sibling; + block = dom.rename(block, listItemName); + sibling.appendChild(block); + } else { + listBlock = dom.create(listName); + block.parentNode.insertBefore(listBlock, block); + listBlock.appendChild(block); + block = dom.rename(block, listItemName); + } + + mergeWithAdjacentLists(listBlock); + }); + + moveToBookmark(bookmark); + } + + function removeList() { + var bookmark = createBookmark(selection.getRng(true)), root = editor.getBody(); + + tinymce.each(getSelectedListItems(), function(li) { + var node, rootList; + + if (dom.isEmpty(li)) { + outdent(li); + return; + } + + for (node = li; node && node != root; node = node.parentNode) { + if (isListNode(node)) { + rootList = node; + } + } + + splitList(rootList, li); + }); + + moveToBookmark(bookmark); + } + + function toggleList(listName) { + var parentList = dom.getParent(selection.getStart(), 'OL,UL,DL'); + + if (parentList) { + if (parentList.nodeName == listName) { + removeList(listName); + } else { + var bookmark = createBookmark(selection.getRng(true)); + mergeWithAdjacentLists(dom.rename(parentList, listName)); + moveToBookmark(bookmark); + } + } else { + applyList(listName); + } + } + + function queryListCommandState(listName) { + return function() { + var parentList = dom.getParent(editor.selection.getStart(), 'UL,OL,DL'); + + return parentList && parentList.nodeName == listName; + }; + } + + self.backspaceDelete = function(isForward) { + function findNextCaretContainer(rng, isForward) { + var node = rng.startContainer, offset = rng.startOffset; + var nonEmptyBlocks, walker; + + if (node.nodeType == 3 && (isForward ? offset < node.data.length : offset > 0)) { + return node; + } + + nonEmptyBlocks = editor.schema.getNonEmptyElements(); + walker = new tinymce.dom.TreeWalker(rng.startContainer); + + while ((node = walker[isForward ? 'next' : 'prev']())) { + if (node.nodeName == 'LI' && !node.hasChildNodes()) { + return node; + } + + if (nonEmptyBlocks[node.nodeName]) { + return node; + } + + if (node.nodeType == 3 && node.data.length > 0) { + return node; + } + } + } + + function mergeLiElements(fromElm, toElm) { + var node, listNode, ul = fromElm.parentNode; + + if (isListNode(toElm.lastChild)) { + listNode = toElm.lastChild; + } + + node = toElm.lastChild; + if (node && node.nodeName == 'BR' && fromElm.hasChildNodes()) { + dom.remove(node); + } + + if (dom.isEmpty(toElm)) { + dom.$(toElm).empty(); + } + + if (!dom.isEmpty(fromElm)) { + while ((node = fromElm.firstChild)) { + toElm.appendChild(node); + } + } + + if (listNode) { + toElm.appendChild(listNode); + } + + dom.remove(fromElm); + + if (dom.isEmpty(ul)) { + dom.remove(ul); + } + } + + if (selection.isCollapsed()) { + var li = dom.getParent(selection.getStart(), 'LI'); + + if (li) { + var rng = selection.getRng(true); + var otherLi = dom.getParent(findNextCaretContainer(rng, isForward), 'LI'); + + if (otherLi && otherLi != li) { + var bookmark = createBookmark(rng); + + if (isForward) { + mergeLiElements(otherLi, li); + } else { + mergeLiElements(li, otherLi); + } + + moveToBookmark(bookmark); + + return true; + } else if (!otherLi) { + if (!isForward && removeList(li.parentNode.nodeName)) { + return true; + } + } + } + } + }; + + editor.addCommand('Indent', function() { + if (!indentSelection()) { + return true; + } + }); + + editor.addCommand('Outdent', function() { + if (!outdentSelection()) { + return true; + } + }); + + editor.addCommand('InsertUnorderedList', function() { + toggleList('UL'); + }); + + editor.addCommand('InsertOrderedList', function() { + toggleList('OL'); + }); + + editor.addCommand('InsertDefinitionList', function() { + toggleList('DL'); + }); + + editor.addQueryStateHandler('InsertUnorderedList', queryListCommandState('UL')); + editor.addQueryStateHandler('InsertOrderedList', queryListCommandState('OL')); + editor.addQueryStateHandler('InsertDefinitionList', queryListCommandState('DL')); + + editor.on('keydown', function(e) { + // Check for tab but not ctrl/cmd+tab since it switches browser tabs + if (e.keyCode != 9 || tinymce.util.VK.metaKeyPressed(e)) { + return; + } + + if (editor.dom.getParent(editor.selection.getStart(), 'LI,DT,DD')) { + e.preventDefault(); + + if (e.shiftKey) { + outdentSelection(); + } else { + indentSelection(); + } + } + }); + }); + + editor.addButton('indent', { + icon: 'indent', + title: 'Increase indent', + cmd: 'Indent', + onPostRender: function() { + var ctrl = this; + + editor.on('nodechange', function() { + var blocks = editor.selection.getSelectedBlocks(); + var disable = false; + + for (var i = 0, l = blocks.length; !disable && i < l; i++) { + var tag = blocks[i].nodeName; + + disable = (tag == 'LI' && isFirstChild(blocks[i]) || tag == 'UL' || tag == 'OL' || tag == 'DD'); + } + + ctrl.disabled(disable); + }); + } + }); + + editor.on('keydown', function(e) { + if (e.keyCode == tinymce.util.VK.BACKSPACE) { + if (self.backspaceDelete()) { + e.preventDefault(); + } + } else if (e.keyCode == tinymce.util.VK.DELETE) { + if (self.backspaceDelete(true)) { + e.preventDefault(); + } + } + }); +}); diff --git a/wp-includes/js/tinymce/plugins/lists/plugin.min.js b/wp-includes/js/tinymce/plugins/lists/plugin.min.js new file mode 100644 index 0000000..c563f5d --- /dev/null +++ b/wp-includes/js/tinymce/plugins/lists/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("lists",function(a){function b(a){return a&&/^(OL|UL|DL)$/.test(a.nodeName)}function c(a){return a.parentNode.firstChild==a}function d(a){return a.parentNode.lastChild==a}function e(b){return b&&!!a.schema.getTextBlockElements()[b.nodeName]}var f=this;a.on("init",function(){function g(a){function b(b){var d,e,f;e=a[b?"startContainer":"endContainer"],f=a[b?"startOffset":"endOffset"],1==e.nodeType&&(d=v.create("span",{"data-mce-type":"bookmark"}),e.hasChildNodes()?(f=Math.min(f,e.childNodes.length-1),b?e.insertBefore(d,e.childNodes[f]):v.insertAfter(d,e.childNodes[f])):e.appendChild(d),e=d,f=0),c[b?"startContainer":"endContainer"]=e,c[b?"startOffset":"endOffset"]=f}var c={};return b(!0),a.collapsed||b(),c}function h(a){function b(b){function c(a){for(var b=a.parentNode.firstChild,c=0;b;){if(b==a)return c;(1!=b.nodeType||"bookmark"!=b.getAttribute("data-mce-type"))&&c++,b=b.nextSibling}return-1}var d,e,f;d=f=a[b?"startContainer":"endContainer"],e=a[b?"startOffset":"endOffset"],d&&(1==d.nodeType&&(e=c(d),d=d.parentNode,v.remove(f)),a[b?"startContainer":"endContainer"]=d,a[b?"startOffset":"endOffset"]=e)}b(!0),b();var c=v.createRng();c.setStart(a.startContainer,a.startOffset),a.endContainer&&c.setEnd(a.endContainer,a.endOffset),w.setRng(c)}function i(b,c){var d,e,f,g=v.createFragment(),h=a.schema.getBlockElements();if(a.settings.forced_root_block&&(c=c||a.settings.forced_root_block),c&&(e=v.create(c),e.tagName===a.settings.forced_root_block&&v.setAttribs(e,a.settings.forced_root_block_attrs),g.appendChild(e)),b)for(;d=b.firstChild;){var i=d.nodeName;f||"SPAN"==i&&"bookmark"==d.getAttribute("data-mce-type")||(f=!0),h[i]?(g.appendChild(d),e=null):c?(e||(e=v.create(c),g.appendChild(e)),e.appendChild(d)):g.appendChild(d)}return a.settings.forced_root_block?f||tinymce.Env.ie&&!(tinymce.Env.ie>10)||e.appendChild(v.create("br",{"data-mce-bogus":"1"})):g.appendChild(v.create("br")),g}function j(){return tinymce.grep(w.getSelectedBlocks(),function(a){return/^(LI|DT|DD)$/.test(a.nodeName)})}function k(a,b,c){var d,e,f=v.select('span[data-mce-type="bookmark"]',a);c=c||i(b),d=v.createRng(),d.setStartAfter(b),d.setEndAfter(a),e=d.extractContents(),v.isEmpty(e)||v.insertAfter(e,a),v.insertAfter(c,a),v.isEmpty(b.parentNode)&&(tinymce.each(f,function(a){b.parentNode.parentNode.insertBefore(a,b.parentNode)}),v.remove(b.parentNode)),v.remove(b)}function l(a){var c,d;if(c=a.nextSibling,c&&b(c)&&c.nodeName==a.nodeName){for(;d=c.firstChild;)a.appendChild(d);v.remove(c)}if(c=a.previousSibling,c&&b(c)&&c.nodeName==a.nodeName){for(;d=c.firstChild;)a.insertBefore(d,a.firstChild);v.remove(c)}}function m(a){tinymce.each(tinymce.grep(v.select("ol,ul",a)),function(a){var c,d=a.parentNode;"LI"==d.nodeName&&d.firstChild==a&&(c=d.previousSibling,c&&"LI"==c.nodeName&&(c.appendChild(a),v.isEmpty(d)&&v.remove(d))),b(d)&&(c=d.previousSibling,c&&"LI"==c.nodeName&&c.appendChild(a))})}function n(a){function e(a){v.isEmpty(a)&&v.remove(a)}var f,g=a.parentNode,h=g.parentNode;return"DD"==a.nodeName?(v.rename(a,"DT"),!0):c(a)&&d(a)?("LI"==h.nodeName?(v.insertAfter(a,h),e(h),v.remove(g)):b(h)?v.remove(g,!0):(h.insertBefore(i(a),g),v.remove(g)),!0):c(a)?("LI"==h.nodeName?(v.insertAfter(a,h),a.appendChild(g),e(h)):b(h)?h.insertBefore(a,g):(h.insertBefore(i(a),g),v.remove(a)),!0):d(a)?("LI"==h.nodeName?v.insertAfter(a,h):b(h)?v.insertAfter(a,g):(v.insertAfter(i(a),g),v.remove(a)),!0):("LI"==h.nodeName?(g=h,f=i(a,"LI")):f=b(h)?i(a,"LI"):i(a),k(g,a,f),m(g.parentNode),!0)}function o(a){function c(c,d){var e;if(b(c)){for(;e=a.lastChild.firstChild;)d.appendChild(e);v.remove(c)}}var d,e;return"DT"==a.nodeName?(v.rename(a,"DD"),!0):(d=a.previousSibling,d&&b(d)?(d.appendChild(a),!0):d&&"LI"==d.nodeName&&b(d.lastChild)?(d.lastChild.appendChild(a),c(a.lastChild,d.lastChild),!0):(d=a.nextSibling,d&&b(d)?(d.insertBefore(a,d.firstChild),!0):d&&"LI"==d.nodeName&&b(a.lastChild)?!1:(d=a.previousSibling,d&&"LI"==d.nodeName?(e=v.create(a.parentNode.nodeName),d.appendChild(e),e.appendChild(a),c(a.lastChild,e),!0):!1)))}function p(){var b=j();if(b.length){for(var c=g(w.getRng(!0)),d=0;d0))return f;for(d=a.schema.getNonEmptyElements(),e=new tinymce.dom.TreeWalker(b.startContainer);f=e[c?"next":"prev"]();){if("LI"==f.nodeName&&!f.hasChildNodes())return f;if(d[f.nodeName])return f;if(3==f.nodeType&&f.data.length>0)return f}}function e(a,c){var d,e,f=a.parentNode;if(b(c.lastChild)&&(e=c.lastChild),d=c.lastChild,d&&"BR"==d.nodeName&&a.hasChildNodes()&&v.remove(d),v.isEmpty(c)&&v.$(c).empty(),!v.isEmpty(a))for(;d=a.firstChild;)c.appendChild(d);e&&c.appendChild(e),v.remove(a),v.isEmpty(f)&&v.remove(f)}if(w.isCollapsed()){var f=v.getParent(w.getStart(),"LI");if(f){var i=w.getRng(!0),j=v.getParent(d(i,c),"LI");if(j&&j!=f){var k=g(i);return c?e(j,f):e(f,j),h(k),!0}if(!j&&!c&&s(f.parentNode.nodeName))return!0}}},a.addCommand("Indent",function(){return p()?void 0:!0}),a.addCommand("Outdent",function(){return q()?void 0:!0}),a.addCommand("InsertUnorderedList",function(){t("UL")}),a.addCommand("InsertOrderedList",function(){t("OL")}),a.addCommand("InsertDefinitionList",function(){t("DL")}),a.addQueryStateHandler("InsertUnorderedList",u("UL")),a.addQueryStateHandler("InsertOrderedList",u("OL")),a.addQueryStateHandler("InsertDefinitionList",u("DL")),a.on("keydown",function(b){9!=b.keyCode||tinymce.util.VK.metaKeyPressed(b)||a.dom.getParent(a.selection.getStart(),"LI,DT,DD")&&(b.preventDefault(),b.shiftKey?q():p())})}),a.addButton("indent",{icon:"indent",title:"Increase indent",cmd:"Indent",onPostRender:function(){var b=this;a.on("nodechange",function(){for(var d=a.selection.getSelectedBlocks(),e=!1,f=0,g=d.length;!e&&g>f;f++){var h=d[f].nodeName;e="LI"==h&&c(d[f])||"UL"==h||"OL"==h||"DD"==h}b.disabled(e)})}}),a.on("keydown",function(a){a.keyCode==tinymce.util.VK.BACKSPACE?f.backspaceDelete()&&a.preventDefault():a.keyCode==tinymce.util.VK.DELETE&&f.backspaceDelete(!0)&&a.preventDefault()})}); \ No newline at end of file diff --git a/wp-includes/js/tinymce/plugins/media/moxieplayer.swf b/wp-includes/js/tinymce/plugins/media/moxieplayer.swf new file mode 100644 index 0000000..19c771b Binary files /dev/null and b/wp-includes/js/tinymce/plugins/media/moxieplayer.swf differ diff --git a/wp-includes/js/tinymce/plugins/media/plugin.js b/wp-includes/js/tinymce/plugins/media/plugin.js new file mode 100644 index 0000000..3739f70 --- /dev/null +++ b/wp-includes/js/tinymce/plugins/media/plugin.js @@ -0,0 +1,774 @@ +/** + * plugin.js + * + * Copyright, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/*jshint maxlen:255 */ +/*eslint max-len:0 */ +/*global tinymce:true */ + +tinymce.PluginManager.add('media', function(editor, url) { + var urlPatterns = [ + {regex: /youtu\.be\/([\w\-.]+)/, type: 'iframe', w: 425, h: 350, url: '//www.youtube.com/embed/$1'}, + {regex: /youtube\.com(.+)v=([^&]+)/, type: 'iframe', w: 425, h: 350, url: '//www.youtube.com/embed/$2'}, + {regex: /vimeo\.com\/([0-9]+)/, type: 'iframe', w: 425, h: 350, url: '//player.vimeo.com/video/$1?title=0&byline=0&portrait=0&color=8dc7dc'}, + {regex: /vimeo\.com\/(.*)\/([0-9]+)/, type: "iframe", w: 425, h: 350, url: "//player.vimeo.com/video/$2?title=0&byline=0"}, + {regex: /maps\.google\.([a-z]{2,3})\/maps\/(.+)msid=(.+)/, type: 'iframe', w: 425, h: 350, url: '//maps.google.com/maps/ms?msid=$2&output=embed"'} + ]; + + var embedChange = (tinymce.Env.ie && tinymce.Env.ie <= 8) ? 'onChange' : 'onInput'; + + function guessMime(url) { + if (url.indexOf('.mp3') != -1) { + return 'audio/mpeg'; + } + + if (url.indexOf('.wav') != -1) { + return 'audio/wav'; + } + + if (url.indexOf('.mp4') != -1) { + return 'video/mp4'; + } + + if (url.indexOf('.webm') != -1) { + return 'video/webm'; + } + + if (url.indexOf('.ogg') != -1) { + return 'video/ogg'; + } + + if (url.indexOf('.swf') != -1) { + return 'application/x-shockwave-flash'; + } + + return ''; + } + + function getVideoScriptMatch(src) { + var prefixes = editor.settings.media_scripts; + + if (prefixes) { + for (var i = 0; i < prefixes.length; i++) { + if (src.indexOf(prefixes[i].filter) !== -1) { + return prefixes[i]; + } + } + } + } + + function showDialog() { + var win, width, height, data; + + var generalFormItems = [ + { + name: 'source1', + type: 'filepicker', + filetype: 'media', + size: 40, + autofocus: true, + label: 'Source', + onchange: function(e) { + tinymce.each(e.meta, function(value, key) { + win.find('#' + key).value(value); + }); + } + } + ]; + + function recalcSize(e) { + var widthCtrl, heightCtrl, newWidth, newHeight; + + widthCtrl = win.find('#width')[0]; + heightCtrl = win.find('#height')[0]; + + newWidth = widthCtrl.value(); + newHeight = heightCtrl.value(); + + if (win.find('#constrain')[0].checked() && width && height && newWidth && newHeight) { + if (e.control == widthCtrl) { + newHeight = Math.round((newWidth / width) * newHeight); + heightCtrl.value(newHeight); + } else { + newWidth = Math.round((newHeight / height) * newWidth); + widthCtrl.value(newWidth); + } + } + + width = newWidth; + height = newHeight; + } + + if (editor.settings.media_alt_source !== false) { + generalFormItems.push({name: 'source2', type: 'filepicker', filetype: 'media', size: 40, label: 'Alternative source'}); + } + + if (editor.settings.media_poster !== false) { + generalFormItems.push({name: 'poster', type: 'filepicker', filetype: 'image', size: 40, label: 'Poster'}); + } + + if (editor.settings.media_dimensions !== false) { + generalFormItems.push({ + type: 'container', + label: 'Dimensions', + layout: 'flex', + align: 'center', + spacing: 5, + items: [ + {name: 'width', type: 'textbox', maxLength: 3, size: 3, onchange: recalcSize}, + {type: 'label', text: 'x'}, + {name: 'height', type: 'textbox', maxLength: 3, size: 3, onchange: recalcSize}, + {name: 'constrain', type: 'checkbox', checked: true, text: 'Constrain proportions'} + ] + }); + } + + data = getData(editor.selection.getNode()); + width = data.width; + height = data.height; + + var embedTextBox = { + id: 'mcemediasource', + type: 'textbox', + flex: 1, + name: 'embed', + value: getSource(), + multiline: true, + label: 'Source' + }; + + function updateValueOnChange() { + data = htmlToData(this.value()); + this.parent().parent().fromJSON(data); + } + + embedTextBox[embedChange] = updateValueOnChange; + + win = editor.windowManager.open({ + title: 'Insert/edit video', + data: data, + bodyType: 'tabpanel', + body: [ + { + title: 'General', + type: "form", + onShowTab: function() { + data = htmlToData(this.next().find('#embed').value()); + this.fromJSON(data); + }, + items: generalFormItems + }, + + { + title: 'Embed', + type: "panel", + layout: 'flex', + direction: 'column', + align: 'stretch', + padding: 10, + spacing: 10, + onShowTab: function() { + this.find('#embed').value(dataToHtml(this.parent().toJSON())); + }, + items: [ + { + type: 'label', + text: 'Paste your embed code below:', + forId: 'mcemediasource' + }, + embedTextBox + ] + } + ], + onSubmit: function() { + var beforeObjects, afterObjects, i, y; + + beforeObjects = editor.dom.select('img[data-mce-object]'); + editor.insertContent(dataToHtml(this.toJSON())); + afterObjects = editor.dom.select('img[data-mce-object]'); + + // Find new image placeholder so we can select it + for (i = 0; i < beforeObjects.length; i++) { + for (y = afterObjects.length - 1; y >= 0; y--) { + if (beforeObjects[i] == afterObjects[y]) { + afterObjects.splice(y, 1); + } + } + } + + editor.selection.select(afterObjects[0]); + editor.nodeChanged(); + } + }); + } + + function getSource() { + var elm = editor.selection.getNode(); + + if (elm.getAttribute('data-mce-object')) { + return editor.selection.getContent(); + } + } + + function dataToHtml(data) { + var html = ''; + + if (!data.source1) { + tinymce.extend(data, htmlToData(data.embed)); + if (!data.source1) { + return ''; + } + } + + if (!data.source2) { + data.source2 = ''; + } + + if (!data.poster) { + data.poster = ''; + } + + data.source1 = editor.convertURL(data.source1, "source"); + data.source2 = editor.convertURL(data.source2, "source"); + data.source1mime = guessMime(data.source1); + data.source2mime = guessMime(data.source2); + data.poster = editor.convertURL(data.poster, "poster"); + data.flashPlayerUrl = editor.convertURL(url + '/moxieplayer.swf', "movie"); + + tinymce.each(urlPatterns, function(pattern) { + var match, i, url; + + if ((match = pattern.regex.exec(data.source1))) { + url = pattern.url; + + for (i = 0; match[i]; i++) { + /*jshint loopfunc:true*/ + /*eslint no-loop-func:0 */ + url = url.replace('$' + i, function() { + return match[i]; + }); + } + + data.source1 = url; + data.type = pattern.type; + data.width = data.width || pattern.w; + data.height = data.height || pattern.h; + } + }); + + if (data.embed) { + html = updateHtml(data.embed, data, true); + } else { + var videoScript = getVideoScriptMatch(data.source1); + if (videoScript) { + data.type = 'script'; + data.width = videoScript.width; + data.height = videoScript.height; + } + + data.width = data.width || 300; + data.height = data.height || 150; + + tinymce.each(data, function(value, key) { + data[key] = editor.dom.encode(value); + }); + + if (data.type == "iframe") { + html += ''; + } else if (data.source1mime == "application/x-shockwave-flash") { + html += ''; + + if (data.poster) { + html += ''; + } + + html += ''; + } else if (data.source1mime.indexOf('audio') != -1) { + if (editor.settings.audio_template_callback) { + html = editor.settings.audio_template_callback(data); + } else { + html += ( + '' + ); + } + } else if (data.type == "script") { + html += ''; + } else { + if (editor.settings.video_template_callback) { + html = editor.settings.video_template_callback(data); + } else { + html = ( + '' + ); + } + } + } + + return html; + } + + function htmlToData(html) { + var data = {}; + + new tinymce.html.SaxParser({ + validate: false, + allow_conditional_comments: true, + special: 'script,noscript', + start: function(name, attrs) { + if (!data.source1 && name == "param") { + data.source1 = attrs.map.movie; + } + + if (name == "iframe" || name == "object" || name == "embed" || name == "video" || name == "audio") { + if (!data.type) { + data.type = name; + } + + data = tinymce.extend(attrs.map, data); + } + + if (name == "script") { + var videoScript = getVideoScriptMatch(attrs.map.src); + if (!videoScript) { + return; + } + + data = { + type: "script", + source1: attrs.map.src, + width: videoScript.width, + height: videoScript.height + }; + } + + if (name == "source") { + if (!data.source1) { + data.source1 = attrs.map.src; + } else if (!data.source2) { + data.source2 = attrs.map.src; + } + } + + if (name == "img" && !data.poster) { + data.poster = attrs.map.src; + } + } + }).parse(html); + + data.source1 = data.source1 || data.src || data.data; + data.source2 = data.source2 || ''; + data.poster = data.poster || ''; + + return data; + } + + function getData(element) { + if (element.getAttribute('data-mce-object')) { + return htmlToData(editor.serializer.serialize(element, {selection: true})); + } + + return {}; + } + + function sanitize(html) { + if (editor.settings.media_filter_html === false) { + return html; + } + + var writer = new tinymce.html.Writer(); + + new tinymce.html.SaxParser({ + validate: false, + allow_conditional_comments: false, + special: 'script,noscript', + + comment: function(text) { + writer.comment(text); + }, + + cdata: function(text) { + writer.cdata(text); + }, + + text: function(text, raw) { + writer.text(text, raw); + }, + + start: function(name, attrs, empty) { + if (name == 'script' || name == 'noscript') { + return; + } + + for (var i = 0; i < attrs.length; i++) { + if (attrs[i].name.indexOf('on') === 0) { + return; + } + } + + writer.start(name, attrs, empty); + }, + + end: function(name) { + if (name == 'script' || name == 'noscript') { + return; + } + + writer.end(name); + } + }, new tinymce.html.Schema({})).parse(html); + + return writer.getContent(); + } + + function updateHtml(html, data, updateAll) { + var writer = new tinymce.html.Writer(); + var sourceCount = 0, hasImage; + + function setAttributes(attrs, updatedAttrs) { + var name, i, value, attr; + + for (name in updatedAttrs) { + value = "" + updatedAttrs[name]; + + if (attrs.map[name]) { + i = attrs.length; + while (i--) { + attr = attrs[i]; + + if (attr.name == name) { + if (value) { + attrs.map[name] = value; + attr.value = value; + } else { + delete attrs.map[name]; + attrs.splice(i, 1); + } + } + } + } else if (value) { + attrs.push({ + name: name, + value: value + }); + + attrs.map[name] = value; + } + } + } + + new tinymce.html.SaxParser({ + validate: false, + allow_conditional_comments: true, + special: 'script,noscript', + + comment: function(text) { + writer.comment(text); + }, + + cdata: function(text) { + writer.cdata(text); + }, + + text: function(text, raw) { + writer.text(text, raw); + }, + + start: function(name, attrs, empty) { + switch (name) { + case "video": + case "object": + case "embed": + case "img": + case "iframe": + setAttributes(attrs, { + width: data.width, + height: data.height + }); + break; + } + + if (updateAll) { + switch (name) { + case "video": + setAttributes(attrs, { + poster: data.poster, + src: "" + }); + + if (data.source2) { + setAttributes(attrs, { + src: "" + }); + } + break; + + case "iframe": + setAttributes(attrs, { + src: data.source1 + }); + break; + + case "source": + sourceCount++; + + if (sourceCount <= 2) { + setAttributes(attrs, { + src: data["source" + sourceCount], + type: data["source" + sourceCount + "mime"] + }); + + if (!data["source" + sourceCount]) { + return; + } + } + break; + + case "img": + if (!data.poster) { + return; + } + + hasImage = true; + break; + } + } + + writer.start(name, attrs, empty); + }, + + end: function(name) { + if (name == "video" && updateAll) { + for (var index = 1; index <= 2; index++) { + if (data["source" + index]) { + var attrs = []; + attrs.map = {}; + + if (sourceCount < index) { + setAttributes(attrs, { + src: data["source" + index], + type: data["source" + index + "mime"] + }); + + writer.start("source", attrs, true); + } + } + } + } + + if (data.poster && name == "object" && updateAll && !hasImage) { + var imgAttrs = []; + imgAttrs.map = {}; + + setAttributes(imgAttrs, { + src: data.poster, + width: data.width, + height: data.height + }); + + writer.start("img", imgAttrs, true); + } + + writer.end(name); + } + }, new tinymce.html.Schema({})).parse(html); + + return writer.getContent(); + } + + editor.on('ResolveName', function(e) { + var name; + + if (e.target.nodeType == 1 && (name = e.target.getAttribute("data-mce-object"))) { + e.name = name; + } + }); + + editor.on('preInit', function() { + // Make sure that any messy HTML is retained inside these + var specialElements = editor.schema.getSpecialElements(); + tinymce.each('video audio iframe object'.split(' '), function(name) { + specialElements[name] = new RegExp('<\/' + name + '[^>]*>', 'gi'); + }); + + // Allow elements + //editor.schema.addValidElements('object[id|style|width|height|classid|codebase|*],embed[id|style|width|height|type|src|*],video[*],audio[*]'); + + // Set allowFullscreen attribs as boolean + var boolAttrs = editor.schema.getBoolAttrs(); + tinymce.each('webkitallowfullscreen mozallowfullscreen allowfullscreen'.split(' '), function(name) { + boolAttrs[name] = {}; + }); + + // Converts iframe, video etc into placeholder images + editor.parser.addNodeFilter('iframe,video,audio,object,embed,script', function(nodes, name) { + var i = nodes.length, ai, node, placeHolder, attrName, attrValue, attribs, innerHtml; + var videoScript; + + while (i--) { + node = nodes[i]; + if (!node.parent) { + continue; + } + + if (node.name == 'script') { + videoScript = getVideoScriptMatch(node.attr('src')); + if (!videoScript) { + continue; + } + } + + placeHolder = new tinymce.html.Node('img', 1); + placeHolder.shortEnded = true; + + if (videoScript) { + if (videoScript.width) { + node.attr('width', videoScript.width.toString()); + } + + if (videoScript.height) { + node.attr('height', videoScript.height.toString()); + } + } + + // Prefix all attributes except width, height and style since we + // will add these to the placeholder + attribs = node.attributes; + ai = attribs.length; + while (ai--) { + attrName = attribs[ai].name; + attrValue = attribs[ai].value; + + if (attrName !== "width" && attrName !== "height" && attrName !== "style") { + if (attrName == "data" || attrName == "src") { + attrValue = editor.convertURL(attrValue, attrName); + } + + placeHolder.attr('data-mce-p-' + attrName, attrValue); + } + } + + // Place the inner HTML contents inside an escaped attribute + // This enables us to copy/paste the fake object + innerHtml = node.firstChild && node.firstChild.value; + if (innerHtml) { + placeHolder.attr("data-mce-html", escape(innerHtml)); + placeHolder.firstChild = null; + } + + placeHolder.attr({ + width: node.attr('width') || "300", + height: node.attr('height') || (name == "audio" ? "30" : "150"), + style: node.attr('style'), + src: tinymce.Env.transparentSrc, + "data-mce-object": name, + "class": "mce-object mce-object-" + name + }); + + node.replace(placeHolder); + } + }); + + // Replaces placeholder images with real elements for video, object, iframe etc + editor.serializer.addAttributeFilter('data-mce-object', function(nodes, name) { + var i = nodes.length, node, realElm, ai, attribs, innerHtml, innerNode, realElmName; + + while (i--) { + node = nodes[i]; + if (!node.parent) { + continue; + } + + realElmName = node.attr(name); + realElm = new tinymce.html.Node(realElmName, 1); + + // Add width/height to everything but audio + if (realElmName != "audio" && realElmName != "script") { + realElm.attr({ + width: node.attr('width'), + height: node.attr('height') + }); + } + + realElm.attr({ + style: node.attr('style') + }); + + // Unprefix all placeholder attributes + attribs = node.attributes; + ai = attribs.length; + while (ai--) { + var attrName = attribs[ai].name; + + if (attrName.indexOf('data-mce-p-') === 0) { + realElm.attr(attrName.substr(11), attribs[ai].value); + } + } + + if (realElmName == "script") { + realElm.attr('type', 'text/javascript'); + } + + // Inject innerhtml + innerHtml = node.attr('data-mce-html'); + if (innerHtml) { + innerNode = new tinymce.html.Node('#text', 3); + innerNode.raw = true; + innerNode.value = sanitize(unescape(innerHtml)); + realElm.append(innerNode); + } + + node.replace(realElm); + } + }); + }); + + editor.on('ObjectSelected', function(e) { + var objectType = e.target.getAttribute('data-mce-object'); + + if (objectType == "audio" || objectType == "script") { + e.preventDefault(); + } + }); + + editor.on('objectResized', function(e) { + var target = e.target, html; + + if (target.getAttribute('data-mce-object')) { + html = target.getAttribute('data-mce-html'); + if (html) { + html = unescape(html); + target.setAttribute('data-mce-html', escape( + updateHtml(html, { + width: e.width, + height: e.height + }) + )); + } + } + }); + + editor.addButton('media', { + tooltip: 'Insert/edit video', + onclick: showDialog, + stateSelector: ['img[data-mce-object=video]', 'img[data-mce-object=iframe]'] + }); + + editor.addMenuItem('media', { + icon: 'media', + text: 'Insert video', + onclick: showDialog, + context: 'insert', + prependToContext: true + }); +}); diff --git a/wp-includes/js/tinymce/plugins/media/plugin.min.js b/wp-includes/js/tinymce/plugins/media/plugin.min.js new file mode 100644 index 0000000..7b12f23 --- /dev/null +++ b/wp-includes/js/tinymce/plugins/media/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("media",function(a,b){function c(a){return-1!=a.indexOf(".mp3")?"audio/mpeg":-1!=a.indexOf(".wav")?"audio/wav":-1!=a.indexOf(".mp4")?"video/mp4":-1!=a.indexOf(".webm")?"video/webm":-1!=a.indexOf(".ogg")?"video/ogg":-1!=a.indexOf(".swf")?"application/x-shockwave-flash":""}function d(b){var c=a.settings.media_scripts;if(c)for(var d=0;d=0;e--)b[d]==c[e]&&c.splice(e,1);a.selection.select(c[0]),a.nodeChanged()}})}function f(){var b=a.selection.getNode();return b.getAttribute("data-mce-object")?a.selection.getContent():void 0}function g(e){var f="";if(!e.source1&&(tinymce.extend(e,h(e.embed)),!e.source1))return"";if(e.source2||(e.source2=""),e.poster||(e.poster=""),e.source1=a.convertURL(e.source1,"source"),e.source2=a.convertURL(e.source2,"source"),e.source1mime=c(e.source1),e.source2mime=c(e.source2),e.poster=a.convertURL(e.poster,"poster"),e.flashPlayerUrl=a.convertURL(b+"/moxieplayer.swf","movie"),tinymce.each(l,function(a){var b,c,d;if(b=a.regex.exec(e.source1)){for(d=a.url,c=0;b[c];c++)d=d.replace("$"+c,function(){return b[c]});e.source1=d,e.type=a.type,e.width=e.width||a.w,e.height=e.height||a.h}}),e.embed)f=k(e.embed,e,!0);else{var g=d(e.source1);g&&(e.type="script",e.width=g.width,e.height=g.height),e.width=e.width||300,e.height=e.height||150,tinymce.each(e,function(b,c){e[c]=a.dom.encode(b)}),"iframe"==e.type?f+='':"application/x-shockwave-flash"==e.source1mime?(f+='',e.poster&&(f+=''),f+=""):-1!=e.source1mime.indexOf("audio")?a.settings.audio_template_callback?f=a.settings.audio_template_callback(e):f+='":"script"==e.type?f+='':f=a.settings.video_template_callback?a.settings.video_template_callback(e):'"}return f}function h(a){var b={};return new tinymce.html.SaxParser({validate:!1,allow_conditional_comments:!0,special:"script,noscript",start:function(a,c){if(b.source1||"param"!=a||(b.source1=c.map.movie),("iframe"==a||"object"==a||"embed"==a||"video"==a||"audio"==a)&&(b.type||(b.type=a),b=tinymce.extend(c.map,b)),"script"==a){var e=d(c.map.src);if(!e)return;b={type:"script",source1:c.map.src,width:e.width,height:e.height}}"source"==a&&(b.source1?b.source2||(b.source2=c.map.src):b.source1=c.map.src),"img"!=a||b.poster||(b.poster=c.map.src)}}).parse(a),b.source1=b.source1||b.src||b.data,b.source2=b.source2||"",b.poster=b.poster||"",b}function i(b){return b.getAttribute("data-mce-object")?h(a.serializer.serialize(b,{selection:!0})):{}}function j(b){if(a.settings.media_filter_html===!1)return b;var c=new tinymce.html.Writer;return new tinymce.html.SaxParser({validate:!1,allow_conditional_comments:!1,special:"script,noscript",comment:function(a){c.comment(a)},cdata:function(a){c.cdata(a)},text:function(a,b){c.text(a,b)},start:function(a,b,d){if("script"!=a&&"noscript"!=a){for(var e=0;e=g&&(d(h,{src:b["source"+g],type:b["source"+g+"mime"]}),!b["source"+g]))return;break;case"img":if(!b.poster)return;e=!0}f.start(a,h,i)},end:function(a){if("video"==a&&c)for(var h=1;2>=h;h++)if(b["source"+h]){var i=[];i.map={},h>g&&(d(i,{src:b["source"+h],type:b["source"+h+"mime"]}),f.start("source",i,!0))}if(b.poster&&"object"==a&&c&&!e){var j=[];j.map={},d(j,{src:b.poster,width:b.width,height:b.height}),f.start("img",j,!0)}f.end(a)}},new tinymce.html.Schema({})).parse(a),f.getContent()}var l=[{regex:/youtu\.be\/([\w\-.]+)/,type:"iframe",w:425,h:350,url:"//www.youtube.com/embed/$1"},{regex:/youtube\.com(.+)v=([^&]+)/,type:"iframe",w:425,h:350,url:"//www.youtube.com/embed/$2"},{regex:/vimeo\.com\/([0-9]+)/,type:"iframe",w:425,h:350,url:"//player.vimeo.com/video/$1?title=0&byline=0&portrait=0&color=8dc7dc"},{regex:/vimeo\.com\/(.*)\/([0-9]+)/,type:"iframe",w:425,h:350,url:"//player.vimeo.com/video/$2?title=0&byline=0"},{regex:/maps\.google\.([a-z]{2,3})\/maps\/(.+)msid=(.+)/,type:"iframe",w:425,h:350,url:'//maps.google.com/maps/ms?msid=$2&output=embed"'}],m=tinymce.Env.ie&&tinymce.Env.ie<=8?"onChange":"onInput";a.on("ResolveName",function(a){var b;1==a.target.nodeType&&(b=a.target.getAttribute("data-mce-object"))&&(a.name=b)}),a.on("preInit",function(){var b=a.schema.getSpecialElements();tinymce.each("video audio iframe object".split(" "),function(a){b[a]=new RegExp("]*>","gi")});var c=a.schema.getBoolAttrs();tinymce.each("webkitallowfullscreen mozallowfullscreen allowfullscreen".split(" "),function(a){c[a]={}}),a.parser.addNodeFilter("iframe,video,audio,object,embed,script",function(b,c){for(var e,f,g,h,i,j,k,l,m=b.length;m--;)if(f=b[m],f.parent&&("script"!=f.name||(l=d(f.attr("src"))))){for(g=new tinymce.html.Node("img",1),g.shortEnded=!0,l&&(l.width&&f.attr("width",l.width.toString()),l.height&&f.attr("height",l.height.toString())),j=f.attributes,e=j.length;e--;)h=j[e].name,i=j[e].value,"width"!==h&&"height"!==h&&"style"!==h&&(("data"==h||"src"==h)&&(i=a.convertURL(i,h)),g.attr("data-mce-p-"+h,i));k=f.firstChild&&f.firstChild.value,k&&(g.attr("data-mce-html",escape(k)),g.firstChild=null),g.attr({width:f.attr("width")||"300",height:f.attr("height")||("audio"==c?"30":"150"),style:f.attr("style"),src:tinymce.Env.transparentSrc,"data-mce-object":c,"class":"mce-object mce-object-"+c}),f.replace(g)}}),a.serializer.addAttributeFilter("data-mce-object",function(a,b){for(var c,d,e,f,g,h,i,k=a.length;k--;)if(c=a[k],c.parent){for(i=c.attr(b),d=new tinymce.html.Node(i,1),"audio"!=i&&"script"!=i&&d.attr({width:c.attr("width"),height:c.attr("height")}),d.attr({style:c.attr("style")}),f=c.attributes,e=f.length;e--;){var l=f[e].name;0===l.indexOf("data-mce-p-")&&d.attr(l.substr(11),f[e].value)}"script"==i&&d.attr("type","text/javascript"),g=c.attr("data-mce-html"),g&&(h=new tinymce.html.Node("#text",3),h.raw=!0,h.value=j(unescape(g)),d.append(h)),c.replace(d)}})}),a.on("ObjectSelected",function(a){var b=a.target.getAttribute("data-mce-object");("audio"==b||"script"==b)&&a.preventDefault()}),a.on("objectResized",function(a){var b,c=a.target;c.getAttribute("data-mce-object")&&(b=c.getAttribute("data-mce-html"),b&&(b=unescape(b),c.setAttribute("data-mce-html",escape(k(b,{width:a.width,height:a.height})))))}),a.addButton("media",{tooltip:"Insert/edit video",onclick:e,stateSelector:["img[data-mce-object=video]","img[data-mce-object=iframe]"]}),a.addMenuItem("media",{icon:"media",text:"Insert video",onclick:e,context:"insert",prependToContext:!0})}); \ No newline at end of file diff --git a/wp-includes/js/tinymce/plugins/paste/plugin.js b/wp-includes/js/tinymce/plugins/paste/plugin.js new file mode 100644 index 0000000..eea165d --- /dev/null +++ b/wp-includes/js/tinymce/plugins/paste/plugin.js @@ -0,0 +1,1625 @@ +/** + * Compiled inline version. (Library mode) + */ + +/*jshint smarttabs:true, undef:true, latedef:true, curly:true, bitwise:true, camelcase:true */ +/*globals $code */ + +(function(exports, undefined) { + "use strict"; + + var modules = {}; + + function require(ids, callback) { + var module, defs = []; + + for (var i = 0; i < ids.length; ++i) { + module = modules[ids[i]] || resolve(ids[i]); + if (!module) { + throw 'module definition dependecy not found: ' + ids[i]; + } + + defs.push(module); + } + + callback.apply(null, defs); + } + + function define(id, dependencies, definition) { + if (typeof id !== 'string') { + throw 'invalid module definition, module id must be defined and be a string'; + } + + if (dependencies === undefined) { + throw 'invalid module definition, dependencies must be specified'; + } + + if (definition === undefined) { + throw 'invalid module definition, definition function must be specified'; + } + + require(dependencies, function() { + modules[id] = definition.apply(null, arguments); + }); + } + + function defined(id) { + return !!modules[id]; + } + + function resolve(id) { + var target = exports; + var fragments = id.split(/[.\/]/); + + for (var fi = 0; fi < fragments.length; ++fi) { + if (!target[fragments[fi]]) { + return; + } + + target = target[fragments[fi]]; + } + + return target; + } + + function expose(ids) { + for (var i = 0; i < ids.length; i++) { + var target = exports; + var id = ids[i]; + var fragments = id.split(/[.\/]/); + + for (var fi = 0; fi < fragments.length - 1; ++fi) { + if (target[fragments[fi]] === undefined) { + target[fragments[fi]] = {}; + } + + target = target[fragments[fi]]; + } + + target[fragments[fragments.length - 1]] = modules[id]; + } + } + +// Included from: js/tinymce/plugins/paste/classes/Utils.js + +/** + * Utils.js + * + * Copyright, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This class contails various utility functions for the paste plugin. + * + * @class tinymce.pasteplugin.Clipboard + * @private + */ +define("tinymce/pasteplugin/Utils", [ + "tinymce/util/Tools", + "tinymce/html/DomParser", + "tinymce/html/Schema" +], function(Tools, DomParser, Schema) { + function filter(content, items) { + Tools.each(items, function(v) { + if (v.constructor == RegExp) { + content = content.replace(v, ''); + } else { + content = content.replace(v[0], v[1]); + } + }); + + return content; + } + + /** + * Gets the innerText of the specified element. It will handle edge cases + * and works better than textContent on Gecko. + * + * @param {String} html HTML string to get text from. + * @return {String} String of text with line feeds. + */ + function innerText(html) { + var schema = new Schema(), domParser = new DomParser({}, schema), text = ''; + var shortEndedElements = schema.getShortEndedElements(); + var ignoreElements = Tools.makeMap('script noscript style textarea video audio iframe object', ' '); + var blockElements = schema.getBlockElements(); + + function walk(node) { + var name = node.name, currentNode = node; + + if (name === 'br') { + text += '\n'; + return; + } + + // img/input/hr + if (shortEndedElements[name]) { + text += ' '; + } + + // Ingore script, video contents + if (ignoreElements[name]) { + text += ' '; + return; + } + + if (node.type == 3) { + text += node.value; + } + + // Walk all children + if (!node.shortEnded) { + if ((node = node.firstChild)) { + do { + walk(node); + } while ((node = node.next)); + } + } + + // Add \n or \n\n for blocks or P + if (blockElements[name] && currentNode.next) { + text += '\n'; + + if (name == 'p') { + text += '\n'; + } + } + } + + html = filter(html, [ + //g // Conditional comments + ]); + + walk(domParser.parse(html)); + + return text; + } + + /** + * Trims the specified HTML by removing all WebKit fragments, all elements wrapping the body trailing BR elements etc. + * + * @param {String} html Html string to trim contents on. + * @return {String} Html contents that got trimmed. + */ + function trimHtml(html) { + function trimSpaces(all, s1, s2) { + // WebKit   meant to preserve multiple spaces but instead inserted around all inline tags, + // including the spans with inline styles created on paste + if (!s1 && !s2) { + return ' '; + } + + return '\u00a0'; + } + + html = filter(html, [ + /^[\s\S]*]*>\s*|\s*<\/body[^>]*>[\s\S]*$/g, // Remove anything but the contents within the BODY element + /|/g, // Inner fragments (tables from excel on mac) + [/( ?)\u00a0<\/span>( ?)/g, trimSpaces], + /
        $/i // Trailing BR elements + ]); + + return html; + } + + return { + filter: filter, + innerText: innerText, + trimHtml: trimHtml + }; +}); + +// Included from: js/tinymce/plugins/paste/classes/Clipboard.js + +/** + * Clipboard.js + * + * Copyright, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This class contains logic for getting HTML contents out of the clipboard. + * + * We need to make a lot of ugly hacks to get the contents out of the clipboard since + * the W3C Clipboard API is broken in all browsers that have it: Gecko/WebKit/Blink. + * We might rewrite this the way those API:s stabilize. Browsers doesn't handle pasting + * from applications like Word the same way as it does when pasting into a contentEditable area + * so we need to do lots of extra work to try to get to this clipboard data. + * + * Current implementation steps: + * 1. On keydown with paste keys Ctrl+V or Shift+Insert create + * a paste bin element and move focus to that element. + * 2. Wait for the browser to fire a "paste" event and get the contents out of the paste bin. + * 3. Check if the paste was successful if true, process the HTML. + * (4). If the paste was unsuccessful use IE execCommand, Clipboard API, document.dataTransfer old WebKit API etc. + * + * @class tinymce.pasteplugin.Clipboard + * @private + */ +define("tinymce/pasteplugin/Clipboard", [ + "tinymce/Env", + "tinymce/util/VK", + "tinymce/pasteplugin/Utils" +], function(Env, VK, Utils) { + return function(editor) { + var self = this, pasteBinElm, lastRng, keyboardPasteTimeStamp = 0, draggingInternally = false; + var pasteBinDefaultContent = '%MCEPASTEBIN%', keyboardPastePlainTextState; + + /** + * Pastes the specified HTML. This means that the HTML is filtered and then + * inserted at the current selection in the editor. It will also fire paste events + * for custom user filtering. + * + * @param {String} html HTML code to paste into the current selection. + */ + function pasteHtml(html) { + var args, dom = editor.dom; + + args = editor.fire('BeforePastePreProcess', {content: html}); // Internal event used by Quirks + args = editor.fire('PastePreProcess', args); + html = args.content; + + if (!args.isDefaultPrevented()) { + // User has bound PastePostProcess events then we need to pass it through a DOM node + // This is not ideal but we don't want to let the browser mess up the HTML for example + // some browsers add   to P tags etc + if (editor.hasEventListeners('PastePostProcess') && !args.isDefaultPrevented()) { + // We need to attach the element to the DOM so Sizzle selectors work on the contents + var tempBody = dom.add(editor.getBody(), 'div', {style: 'display:none'}, html); + args = editor.fire('PastePostProcess', {node: tempBody}); + dom.remove(tempBody); + html = args.node.innerHTML; + } + + if (!args.isDefaultPrevented()) { + editor.insertContent(html, {merge: editor.settings.paste_merge_formats !== false}); + } + } + } + + /** + * Pastes the specified text. This means that the plain text is processed + * and converted into BR and P elements. It will fire paste events for custom filtering. + * + * @param {String} text Text to paste as the current selection location. + */ + function pasteText(text) { + text = editor.dom.encode(text).replace(/\r\n/g, '\n'); + + var startBlock = editor.dom.getParent(editor.selection.getStart(), editor.dom.isBlock); + + // Create start block html for example

        + var forcedRootBlockName = editor.settings.forced_root_block; + var forcedRootBlockStartHtml; + if (forcedRootBlockName) { + forcedRootBlockStartHtml = editor.dom.createHTML(forcedRootBlockName, editor.settings.forced_root_block_attrs); + forcedRootBlockStartHtml = forcedRootBlockStartHtml.substr(0, forcedRootBlockStartHtml.length - 3) + '>'; + } + + if ((startBlock && /^(PRE|DIV)$/.test(startBlock.nodeName)) || !forcedRootBlockName) { + text = Utils.filter(text, [ + [/\n/g, "
        "] + ]); + } else { + text = Utils.filter(text, [ + [/\n\n/g, "

        " + forcedRootBlockStartHtml], + [/^(.*<\/p>)(

        )$/, forcedRootBlockStartHtml + '$1'], + [/\n/g, "
        "] + ]); + + if (text.indexOf('

        ') != -1) { + text = forcedRootBlockStartHtml + text; + } + } + + pasteHtml(text); + } + + /** + * Creates a paste bin element as close as possible to the current caret location and places the focus inside that element + * so that when the real paste event occurs the contents gets inserted into this element + * instead of the current editor selection element. + */ + function createPasteBin() { + var dom = editor.dom, body = editor.getBody(); + var viewport = editor.dom.getViewPort(editor.getWin()), scrollTop = viewport.y, top = 20; + var scrollContainer; + + lastRng = editor.selection.getRng(); + + if (editor.inline) { + scrollContainer = editor.selection.getScrollContainer(); + + // Can't always rely on scrollTop returning a useful value. + // It returns 0 if the browser doesn't support scrollTop for the element or is non-scrollable + if (scrollContainer && scrollContainer.scrollTop > 0) { + scrollTop = scrollContainer.scrollTop; + } + } + + /** + * Returns the rect of the current caret if the caret is in an empty block before a + * BR we insert a temporary invisible character that we get the rect this way we always get a proper rect. + * + * TODO: This might be useful in core. + */ + function getCaretRect(rng) { + var rects, textNode, node, container = rng.startContainer; + + rects = rng.getClientRects(); + if (rects.length) { + return rects[0]; + } + + if (!rng.collapsed || container.nodeType != 1) { + return; + } + + node = container.childNodes[lastRng.startOffset]; + + // Skip empty whitespace nodes + while (node && node.nodeType == 3 && !node.data.length) { + node = node.nextSibling; + } + + if (!node) { + return; + } + + // Check if the location is |
        + // TODO: Might need to expand this to say | + if (node.tagName == 'BR') { + textNode = dom.doc.createTextNode('\uFEFF'); + node.parentNode.insertBefore(textNode, node); + + rng = dom.createRng(); + rng.setStartBefore(textNode); + rng.setEndAfter(textNode); + + rects = rng.getClientRects(); + dom.remove(textNode); + } + + if (rects.length) { + return rects[0]; + } + } + + // Calculate top cordinate this is needed to avoid scrolling to top of document + // We want the paste bin to be as close to the caret as possible to avoid scrolling + if (lastRng.getClientRects) { + var rect = getCaretRect(lastRng); + + if (rect) { + // Client rects gets us closes to the actual + // caret location in for example a wrapped paragraph block + top = scrollTop + (rect.top - dom.getPos(body).y); + } else { + top = scrollTop; + + // Check if we can find a closer location by checking the range element + var container = lastRng.startContainer; + if (container) { + if (container.nodeType == 3 && container.parentNode != body) { + container = container.parentNode; + } + + if (container.nodeType == 1) { + top = dom.getPos(container, scrollContainer || body).y; + } + } + } + } + + // Create a pastebin + pasteBinElm = dom.add(editor.getBody(), 'div', { + id: "mcepastebin", + contentEditable: true, + "data-mce-bogus": "all", + style: 'position: absolute; top: ' + top + 'px;' + + 'width: 10px; height: 10px; overflow: hidden; opacity: 0' + }, pasteBinDefaultContent); + + // Move paste bin out of sight since the controlSelection rect gets displayed otherwise on IE and Gecko + if (Env.ie || Env.gecko) { + dom.setStyle(pasteBinElm, 'left', dom.getStyle(body, 'direction', true) == 'rtl' ? 0xFFFF : -0xFFFF); + } + + // Prevent focus events from bubbeling fixed FocusManager issues + dom.bind(pasteBinElm, 'beforedeactivate focusin focusout', function(e) { + e.stopPropagation(); + }); + + pasteBinElm.focus(); + editor.selection.select(pasteBinElm, true); + } + + /** + * Removes the paste bin if it exists. + */ + function removePasteBin() { + if (pasteBinElm) { + var pasteBinClone; + + // WebKit/Blink might clone the div so + // lets make sure we remove all clones + // TODO: Man o man is this ugly. WebKit is the new IE! Remove this if they ever fix it! + while ((pasteBinClone = editor.dom.get('mcepastebin'))) { + editor.dom.remove(pasteBinClone); + editor.dom.unbind(pasteBinClone); + } + + if (lastRng) { + editor.selection.setRng(lastRng); + } + } + + pasteBinElm = lastRng = null; + } + + /** + * Returns the contents of the paste bin as a HTML string. + * + * @return {String} Get the contents of the paste bin. + */ + function getPasteBinHtml() { + var html = '', pasteBinClones, i, clone, cloneHtml; + + // Since WebKit/Chrome might clone the paste bin when pasting + // for example: we need to check if any of them contains some useful html. + // TODO: Man o man is this ugly. WebKit is the new IE! Remove this if they ever fix it! + pasteBinClones = editor.dom.select('div[id=mcepastebin]'); + for (i = 0; i < pasteBinClones.length; i++) { + clone = pasteBinClones[i]; + + // Pasting plain text produces pastebins in pastebinds makes sence right!? + if (clone.firstChild && clone.firstChild.id == 'mcepastebin') { + clone = clone.firstChild; + } + + cloneHtml = clone.innerHTML; + if (html != pasteBinDefaultContent) { + html += cloneHtml; + } + } + + return html; + } + + /** + * Gets various content types out of a datatransfer object. + * + * @param {DataTransfer} dataTransfer Event fired on paste. + * @return {Object} Object with mime types and data for those mime types. + */ + function getDataTransferItems(dataTransfer) { + var data = {}; + + if (dataTransfer) { + // Use old WebKit/IE API + if (dataTransfer.getData) { + var legacyText = dataTransfer.getData('Text'); + if (legacyText && legacyText.length > 0) { + data['text/plain'] = legacyText; + } + } + + if (dataTransfer.types) { + for (var i = 0; i < dataTransfer.types.length; i++) { + var contentType = dataTransfer.types[i]; + data[contentType] = dataTransfer.getData(contentType); + } + } + } + + return data; + } + + /** + * Gets various content types out of the Clipboard API. It will also get the + * plain text using older IE and WebKit API:s. + * + * @param {ClipboardEvent} clipboardEvent Event fired on paste. + * @return {Object} Object with mime types and data for those mime types. + */ + function getClipboardContent(clipboardEvent) { + return getDataTransferItems(clipboardEvent.clipboardData || editor.getDoc().dataTransfer); + } + + /** + * Checks if the clipboard contains image data if it does it will take that data + * and convert it into a data url image and paste that image at the caret location. + * + * @param {ClipboardEvent} e Paste/drop event object. + * @param {DOMRange} rng Optional rng object to move selection to. + * @return {Boolean} true/false if the image data was found or not. + */ + function pasteImageData(e, rng) { + var dataTransfer = e.clipboardData || e.dataTransfer; + + function processItems(items) { + var i, item, reader; + + function pasteImage() { + if (rng) { + editor.selection.setRng(rng); + rng = null; + } + + pasteHtml(''); + } + + if (items) { + for (i = 0; i < items.length; i++) { + item = items[i]; + + if (/^image\/(jpeg|png|gif)$/.test(item.type)) { + reader = new FileReader(); + reader.onload = pasteImage; + reader.readAsDataURL(item.getAsFile ? item.getAsFile() : item); + + e.preventDefault(); + return true; + } + } + } + } + + if (editor.settings.paste_data_images && dataTransfer) { + return processItems(dataTransfer.items) || processItems(dataTransfer.files); + } + } + + /** + * Chrome on Android doesn't support proper clipboard access so we have no choice but to allow the browser default behavior. + * + * @param {Event} e Paste event object to check if it contains any data. + * @return {Boolean} true/false if the clipboard is empty or not. + */ + function isBrokenAndroidClipboardEvent(e) { + var clipboardData = e.clipboardData; + + return navigator.userAgent.indexOf('Android') != -1 && clipboardData && clipboardData.items && clipboardData.items.length === 0; + } + + function getCaretRangeFromEvent(e) { + var doc = editor.getDoc(), rng, point; + + if (doc.caretPositionFromPoint) { + point = doc.caretPositionFromPoint(e.clientX, e.clientY); + rng = doc.createRange(); + rng.setStart(point.offsetNode, point.offset); + rng.collapse(true); + } else if (doc.caretRangeFromPoint) { + rng = doc.caretRangeFromPoint(e.clientX, e.clientY); + } else if (doc.body.createTextRange) { + rng = doc.body.createTextRange(); + + try { + rng.moveToPoint(e.clientX, e.clientY); + rng.collapse(true); + } catch (ex) { + // Append to top or bottom depending on drop location + rng.collapse(e.clientY < doc.body.clientHeight); + } + } + + return rng; + } + + function hasContentType(clipboardContent, mimeType) { + return mimeType in clipboardContent && clipboardContent[mimeType].length > 0; + } + + function isKeyboardPasteEvent(e) { + return (VK.metaKeyPressed(e) && e.keyCode == 86) || (e.shiftKey && e.keyCode == 45); + } + + function registerEventHandlers() { + editor.on('keydown', function(e) { + function removePasteBinOnKeyUp(e) { + // Ctrl+V or Shift+Insert + if (isKeyboardPasteEvent(e) && !e.isDefaultPrevented()) { + removePasteBin(); + } + } + + // Ctrl+V or Shift+Insert + if (isKeyboardPasteEvent(e) && !e.isDefaultPrevented()) { + keyboardPastePlainTextState = e.shiftKey && e.keyCode == 86; + + // Edge case on Safari on Mac where it doesn't handle Cmd+Shift+V correctly + // it fires the keydown but no paste or keyup so we are left with a paste bin + if (keyboardPastePlainTextState && Env.webkit && navigator.userAgent.indexOf('Version/') != -1) { + return; + } + + // Prevent undoManager keydown handler from making an undo level with the pastebin in it + e.stopImmediatePropagation(); + + keyboardPasteTimeStamp = new Date().getTime(); + + // IE doesn't support Ctrl+Shift+V and it doesn't even produce a paste event + // so lets fake a paste event and let IE use the execCommand/dataTransfer methods + if (Env.ie && keyboardPastePlainTextState) { + e.preventDefault(); + editor.fire('paste', {ieFake: true}); + return; + } + + removePasteBin(); + createPasteBin(); + + // Remove pastebin if we get a keyup and no paste event + // For example pasting a file in IE 11 will not produce a paste event + editor.once('keyup', removePasteBinOnKeyUp); + editor.once('paste', function() { + editor.off('keyup', removePasteBinOnKeyUp); + }); + } + }); + + editor.on('paste', function(e) { + // Getting content from the Clipboard can take some time + var clipboardTimer = new Date().getTime(); + var clipboardContent = getClipboardContent(e); + var clipboardDelay = new Date().getTime() - clipboardTimer; + + var isKeyBoardPaste = (new Date().getTime() - keyboardPasteTimeStamp - clipboardDelay) < 1000; + var plainTextMode = self.pasteFormat == "text" || keyboardPastePlainTextState; + + keyboardPastePlainTextState = false; + + if (e.isDefaultPrevented() || isBrokenAndroidClipboardEvent(e)) { + removePasteBin(); + return; + } + + if (pasteImageData(e)) { + removePasteBin(); + return; + } + + // Not a keyboard paste prevent default paste and try to grab the clipboard contents using different APIs + if (!isKeyBoardPaste) { + e.preventDefault(); + } + + // Try IE only method if paste isn't a keyboard paste + if (Env.ie && (!isKeyBoardPaste || e.ieFake)) { + createPasteBin(); + + editor.dom.bind(pasteBinElm, 'paste', function(e) { + e.stopPropagation(); + }); + + editor.getDoc().execCommand('Paste', false, null); + clipboardContent["text/html"] = getPasteBinHtml(); + } + + setTimeout(function() { + var content; + + // Grab HTML from Clipboard API or paste bin as a fallback + if (hasContentType(clipboardContent, 'text/html')) { + content = clipboardContent['text/html']; + } else { + content = getPasteBinHtml(); + + // If paste bin is empty try using plain text mode + // since that is better than nothing right + if (content == pasteBinDefaultContent) { + plainTextMode = true; + } + } + + content = Utils.trimHtml(content); + + // WebKit has a nice bug where it clones the paste bin if you paste from for example notepad + // so we need to force plain text mode in this case + if (pasteBinElm && pasteBinElm.firstChild && pasteBinElm.firstChild.id === 'mcepastebin') { + plainTextMode = true; + } + + removePasteBin(); + + // If we got nothing from clipboard API and pastebin then we could try the last resort: plain/text + if (!content.length) { + plainTextMode = true; + } + + // Grab plain text from Clipboard API or convert existing HTML to plain text + if (plainTextMode) { + // Use plain text contents from Clipboard API unless the HTML contains paragraphs then + // we should convert the HTML to plain text since works better when pasting HTML/Word contents as plain text + if (hasContentType(clipboardContent, 'text/plain') && content.indexOf('

        ') == -1) { + content = clipboardContent['text/plain']; + } else { + content = Utils.innerText(content); + } + } + + // If the content is the paste bin default HTML then it was + // impossible to get the cliboard data out. + if (content == pasteBinDefaultContent) { + if (!isKeyBoardPaste) { + editor.windowManager.alert('Please use Ctrl+V/Cmd+V keyboard shortcuts to paste contents.'); + } + + return; + } + + if (plainTextMode) { + pasteText(content); + } else { + pasteHtml(content); + } + }, 0); + }); + + editor.on('dragstart dragend', function(e) { + draggingInternally = e.type == 'dragstart'; + }); + + editor.on('drop', function(e) { + var rng = getCaretRangeFromEvent(e); + + if (e.isDefaultPrevented() || draggingInternally) { + return; + } + + if (pasteImageData(e, rng)) { + return; + } + + if (rng && editor.settings.paste_filter_drop !== false) { + var dropContent = getDataTransferItems(e.dataTransfer); + var content = dropContent['mce-internal'] || dropContent['text/html'] || dropContent['text/plain']; + + if (content) { + e.preventDefault(); + + editor.undoManager.transact(function() { + if (dropContent['mce-internal']) { + editor.execCommand('Delete'); + } + + editor.selection.setRng(rng); + + content = Utils.trimHtml(content); + + if (!dropContent['text/html']) { + pasteText(content); + } else { + pasteHtml(content); + } + }); + } + } + }); + + editor.on('dragover dragend', function(e) { + var i, dataTransfer = e.dataTransfer; + + if (editor.settings.paste_data_images && dataTransfer) { + for (i = 0; i < dataTransfer.types.length; i++) { + // Prevent default if we have files dragged into the editor since the pasteImageData handles that + if (dataTransfer.types[i] == "Files") { + e.preventDefault(); + return false; + } + } + } + }); + } + + self.pasteHtml = pasteHtml; + self.pasteText = pasteText; + + editor.on('preInit', function() { + registerEventHandlers(); + + // Remove all data images from paste for example from Gecko + // except internal images like video elements + editor.parser.addNodeFilter('img', function(nodes) { + if (!editor.settings.paste_data_images) { + var i = nodes.length; + + while (i--) { + var src = nodes[i].attributes.map.src; + + // Some browsers automatically produce data uris on paste + // Safari on Mac produces webkit-fake-url see: https://bugs.webkit.org/show_bug.cgi?id=49141 + if (src && /^(data:image|webkit\-fake\-url)/.test(src)) { + if (!nodes[i].attr('data-mce-object') && src !== Env.transparentSrc) { + nodes[i].remove(); + } + } + } + } + }); + }); + }; +}); + +// Included from: js/tinymce/plugins/paste/classes/WordFilter.js + +/** + * WordFilter.js + * + * Copyright, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This class parses word HTML into proper TinyMCE markup. + * + * @class tinymce.pasteplugin.Quirks + * @private + */ +define("tinymce/pasteplugin/WordFilter", [ + "tinymce/util/Tools", + "tinymce/html/DomParser", + "tinymce/html/Schema", + "tinymce/html/Serializer", + "tinymce/html/Node", + "tinymce/pasteplugin/Utils" +], function(Tools, DomParser, Schema, Serializer, Node, Utils) { + /** + * Checks if the specified content is from any of the following sources: MS Word/Office 365/Google docs. + */ + function isWordContent(content) { + return ( + (/ 1) { + currentListNode.attr('start', '' + start); + } + + paragraphNode.wrap(currentListNode); + } else { + currentListNode.append(paragraphNode); + } + + paragraphNode.name = 'li'; + + // Append list to previous list if it exists + if (level > lastLevel && prevListNode) { + prevListNode.lastChild.append(currentListNode); + } + + lastLevel = level; + + // Remove start of list item "1. " or "· " etc + removeIgnoredNodes(paragraphNode); + trimListStart(paragraphNode, /^\u00a0+/); + trimListStart(paragraphNode, /^\s*([\u2022\u00b7\u00a7\u00d8\u25CF]|\w+\.)/); + trimListStart(paragraphNode, /^\u00a0+/); + } + + // Build a list of all root level elements before we start + // altering them in the loop below. + var elements = [], child = node.firstChild; + while (typeof child !== 'undefined' && child !== null) { + elements.push(child); + + child = child.walk(); + if (child !== null) { + while (typeof child !== 'undefined' && child.parent !== node) { + child = child.walk(); + } + } + } + + for (var i = 0; i < elements.length; i++) { + node = elements[i]; + + if (node.name == 'p' && node.firstChild) { + // Find first text node in paragraph + var nodeText = getText(node); + + // Detect unordered lists look for bullets + if (isBulletList(nodeText)) { + convertParagraphToLi(node, 'ul'); + continue; + } + + // Detect ordered lists 1., a. or ixv. + if (isNumericList(nodeText)) { + // Parse OL start number + var matches = /([0-9]+)\./.exec(nodeText); + var start = 1; + if (matches) { + start = parseInt(matches[1], 10); + } + + convertParagraphToLi(node, 'ol', start); + continue; + } + + // Convert paragraphs marked as lists but doesn't look like anything + if (node._listLevel) { + convertParagraphToLi(node, 'ul', 1); + continue; + } + + currentListNode = null; + } else { + // If the root level element isn't a p tag which can be + // processed by convertParagraphToLi, it interrupts the + // lists, causing a new list to start instead of having + // elements from the next list inserted above this tag. + prevListNode = currentListNode; + currentListNode = null; + } + } + } + + function filterStyles(node, styleValue) { + var outputStyles = {}, matches, styles = editor.dom.parseStyle(styleValue); + + Tools.each(styles, function(value, name) { + // Convert various MS styles to W3C styles + switch (name) { + case 'mso-list': + // Parse out list indent level for lists + matches = /\w+ \w+([0-9]+)/i.exec(styleValue); + if (matches) { + node._listLevel = parseInt(matches[1], 10); + } + + // Remove these nodes o + // Since the span gets removed we mark the text node and the span + if (/Ignore/i.test(value) && node.firstChild) { + node._listIgnore = true; + node.firstChild._listIgnore = true; + } + + break; + + case "horiz-align": + name = "text-align"; + break; + + case "vert-align": + name = "vertical-align"; + break; + + case "font-color": + case "mso-foreground": + name = "color"; + break; + + case "mso-background": + case "mso-highlight": + name = "background"; + break; + + case "font-weight": + case "font-style": + if (value != "normal") { + outputStyles[name] = value; + } + return; + + case "mso-element": + // Remove track changes code + if (/^(comment|comment-list)$/i.test(value)) { + node.remove(); + return; + } + + break; + } + + if (name.indexOf('mso-comment') === 0) { + node.remove(); + return; + } + + // Never allow mso- prefixed names + if (name.indexOf('mso-') === 0) { + return; + } + + // Output only valid styles + if (retainStyleProperties == "all" || (validStyles && validStyles[name])) { + outputStyles[name] = value; + } + }); + + // Convert bold style to "b" element + if (/(bold)/i.test(outputStyles["font-weight"])) { + delete outputStyles["font-weight"]; + node.wrap(new Node("b", 1)); + } + + // Convert italic style to "i" element + if (/(italic)/i.test(outputStyles["font-style"])) { + delete outputStyles["font-style"]; + node.wrap(new Node("i", 1)); + } + + // Serialize the styles and see if there is something left to keep + outputStyles = editor.dom.serializeStyle(outputStyles, node.name); + if (outputStyles) { + return outputStyles; + } + + return null; + } + + if (settings.paste_enable_default_filters === false) { + return; + } + + // Detect is the contents is Word junk HTML + if (isWordContent(e.content)) { + e.wordContent = true; // Mark it for other processors + + // Remove basic Word junk + content = Utils.filter(content, [ + // Word comments like conditional comments etc + //gi, + + // Remove comments, scripts (e.g., msoShowComment), XML tag, VML content, + // MS Office namespaced tags, and a few other tags + /<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|img|meta|link|style|\w:\w+)(?=[\s\/>]))[^>]*>/gi, + + // Convert into for line-though + [/<(\/?)s>/gi, "<$1strike>"], + + // Replace nsbp entites to char since it's easier to handle + [/ /gi, "\u00a0"], + + // Convert ___ to string of alternating + // breaking/non-breaking spaces of same length + [/([\s\u00a0]*)<\/span>/gi, + function(str, spaces) { + return (spaces.length > 0) ? + spaces.replace(/./, " ").slice(Math.floor(spaces.length / 2)).split("").join("\u00a0") : ""; + } + ] + ]); + + var validElements = settings.paste_word_valid_elements; + if (!validElements) { + validElements = ( + '-strong/b,-em/i,-u,-span,-p,-ol,-ul,-li,-h1,-h2,-h3,-h4,-h5,-h6,' + + '-p/div,-a[href|name],sub,sup,strike,br,del,table[width],tr,' + + 'td[colspan|rowspan|width],th[colspan|rowspan|width],thead,tfoot,tbody' + ); + } + + // Setup strict schema + var schema = new Schema({ + valid_elements: validElements, + valid_children: '-li[p]' + }); + + // Add style/class attribute to all element rules since the user might have removed them from + // paste_word_valid_elements config option and we need to check them for properties + Tools.each(schema.elements, function(rule) { + if (!rule.attributes["class"]) { + rule.attributes["class"] = {}; + rule.attributesOrder.push("class"); + } + + if (!rule.attributes.style) { + rule.attributes.style = {}; + rule.attributesOrder.push("style"); + } + }); + + // Parse HTML into DOM structure + var domParser = new DomParser({}, schema); + + // Filter styles to remove "mso" specific styles and convert some of them + domParser.addAttributeFilter('style', function(nodes) { + var i = nodes.length, node; + + while (i--) { + node = nodes[i]; + node.attr('style', filterStyles(node, node.attr('style'))); + + // Remove pointess spans + if (node.name == 'span' && node.parent && !node.attributes.length) { + node.unwrap(); + } + } + }); + + // Check the class attribute for comments or del items and remove those + domParser.addAttributeFilter('class', function(nodes) { + var i = nodes.length, node, className; + + while (i--) { + node = nodes[i]; + + className = node.attr('class'); + if (/^(MsoCommentReference|MsoCommentText|msoDel|MsoCaption)$/i.test(className)) { + node.remove(); + } + + node.attr('class', null); + } + }); + + // Remove all del elements since we don't want the track changes code in the editor + domParser.addNodeFilter('del', function(nodes) { + var i = nodes.length; + + while (i--) { + nodes[i].remove(); + } + }); + + // Keep some of the links and anchors + domParser.addNodeFilter('a', function(nodes) { + var i = nodes.length, node, href, name; + + while (i--) { + node = nodes[i]; + href = node.attr('href'); + name = node.attr('name'); + + if (href && href.indexOf('#_msocom_') != -1) { + node.remove(); + continue; + } + + if (href && href.indexOf('file://') === 0) { + href = href.split('#')[1]; + if (href) { + href = '#' + href; + } + } + + if (!href && !name) { + node.unwrap(); + } else { + // Remove all named anchors that aren't specific to TOC, Footnotes or Endnotes + if (name && !/^_?(?:toc|edn|ftn)/i.test(name)) { + node.unwrap(); + continue; + } + + node.attr({ + href: href, + name: name + }); + } + } + }); + + // Parse into DOM structure + var rootNode = domParser.parse(content); + + // Process DOM + if (settings.paste_convert_word_fake_lists !== false) { + convertFakeListsToProperLists(rootNode); + } + + // Serialize DOM back to HTML + e.content = new Serializer({}, schema).serialize(rootNode); + } + }); + } + + WordFilter.isWordContent = isWordContent; + + return WordFilter; +}); + +// Included from: js/tinymce/plugins/paste/classes/Quirks.js + +/** + * Quirks.js + * + * Copyright, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This class contains various fixes for browsers. These issues can not be feature + * detected since we have no direct control over the clipboard. However we might be able + * to remove some of these fixes once the browsers gets updated/fixed. + * + * @class tinymce.pasteplugin.Quirks + * @private + */ +define("tinymce/pasteplugin/Quirks", [ + "tinymce/Env", + "tinymce/util/Tools", + "tinymce/pasteplugin/WordFilter", + "tinymce/pasteplugin/Utils" +], function(Env, Tools, WordFilter, Utils) { + "use strict"; + + return function(editor) { + function addPreProcessFilter(filterFunc) { + editor.on('BeforePastePreProcess', function(e) { + e.content = filterFunc(e.content); + }); + } + + /** + * Removes BR elements after block elements. IE9 has a nasty bug where it puts a BR element after each + * block element when pasting from word. This removes those elements. + * + * This: + *

        a


        b

        + * + * Becomes: + *

        a

        b

        + */ + function removeExplorerBrElementsAfterBlocks(html) { + // Only filter word specific content + if (!WordFilter.isWordContent(html)) { + return html; + } + + // Produce block regexp based on the block elements in schema + var blockElements = []; + + Tools.each(editor.schema.getBlockElements(), function(block, blockName) { + blockElements.push(blockName); + }); + + var explorerBlocksRegExp = new RegExp( + '(?:
         [\\s\\r\\n]+|
        )*(<\\/?(' + blockElements.join('|') + ')[^>]*>)(?:
         [\\s\\r\\n]+|
        )*', + 'g' + ); + + // Remove BR:s from: X
        + html = Utils.filter(html, [ + [explorerBlocksRegExp, '$1'] + ]); + + // IE9 also adds an extra BR element for each soft-linefeed and it also adds a BR for each word wrap break + html = Utils.filter(html, [ + [/

        /g, '

        '], // Replace multiple BR elements with uppercase BR to keep them intact + [/
        /g, ' '], // Replace single br elements with space since they are word wrap BR:s + [/

        /g, '
        '] // Replace back the double brs but into a single BR + ]); + + return html; + } + + /** + * WebKit has a nasty bug where the all computed styles gets added to style attributes when copy/pasting contents. + * This fix solves that by simply removing the whole style attribute. + * + * The paste_webkit_styles option can be set to specify what to keep: + * paste_webkit_styles: "none" // Keep no styles + * paste_webkit_styles: "all", // Keep all of them + * paste_webkit_styles: "font-weight color" // Keep specific ones + * + * @param {String} content Content that needs to be processed. + * @return {String} Processed contents. + */ + function removeWebKitStyles(content) { + // Passthrough all styles from Word and let the WordFilter handle that junk + if (WordFilter.isWordContent(content)) { + return content; + } + + // Filter away styles that isn't matching the target node + var webKitStyles = editor.settings.paste_webkit_styles; + + if (editor.settings.paste_remove_styles_if_webkit === false || webKitStyles == "all") { + return content; + } + + if (webKitStyles) { + webKitStyles = webKitStyles.split(/[, ]/); + } + + // Keep specific styles that doesn't match the current node computed style + if (webKitStyles) { + var dom = editor.dom, node = editor.selection.getNode(); + + content = content.replace(/(<[^>]+) style="([^"]*)"([^>]*>)/gi, function(all, before, value, after) { + var inputStyles = dom.parseStyle(value, 'span'), outputStyles = {}; + + if (webKitStyles === "none") { + return before + after; + } + + for (var i = 0; i < webKitStyles.length; i++) { + var inputValue = inputStyles[webKitStyles[i]], currentValue = dom.getStyle(node, webKitStyles[i], true); + + if (/color/.test(webKitStyles[i])) { + inputValue = dom.toHex(inputValue); + currentValue = dom.toHex(currentValue); + } + + if (currentValue != inputValue) { + outputStyles[webKitStyles[i]] = inputValue; + } + } + + outputStyles = dom.serializeStyle(outputStyles, 'span'); + if (outputStyles) { + return before + ' style="' + outputStyles + '"' + after; + } + + return before + after; + }); + } else { + // Remove all external styles + content = content.replace(/(<[^>]+) style="([^"]*)"([^>]*>)/gi, '$1$3'); + } + + // Keep internal styles + content = content.replace(/(<[^>]+) data-mce-style="([^"]+)"([^>]*>)/gi, function(all, before, value, after) { + return before + ' style="' + value + '"' + after; + }); + + return content; + } + + // Sniff browsers and apply fixes since we can't feature detect + if (Env.webkit) { + addPreProcessFilter(removeWebKitStyles); + } + + if (Env.ie) { + addPreProcessFilter(removeExplorerBrElementsAfterBlocks); + } + }; +}); + +// Included from: js/tinymce/plugins/paste/classes/Plugin.js + +/** + * Plugin.js + * + * Copyright, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This class contains the tinymce plugin logic for the paste plugin. + * + * @class tinymce.pasteplugin.Plugin + * @private + */ +define("tinymce/pasteplugin/Plugin", [ + "tinymce/PluginManager", + "tinymce/pasteplugin/Clipboard", + "tinymce/pasteplugin/WordFilter", + "tinymce/pasteplugin/Quirks" +], function(PluginManager, Clipboard, WordFilter, Quirks) { + var userIsInformed; + + PluginManager.add('paste', function(editor) { + var self = this, clipboard, settings = editor.settings; + + function togglePlainTextPaste() { + if (clipboard.pasteFormat == "text") { + this.active(false); + clipboard.pasteFormat = "html"; + } else { + clipboard.pasteFormat = "text"; + this.active(true); + + if (!userIsInformed) { + editor.windowManager.alert( + 'Paste is now in plain text mode. Contents will now ' + + 'be pasted as plain text until you toggle this option off.' + ); + + userIsInformed = true; + } + } + } + + self.clipboard = clipboard = new Clipboard(editor); + self.quirks = new Quirks(editor); + self.wordFilter = new WordFilter(editor); + + if (editor.settings.paste_as_text) { + self.clipboard.pasteFormat = "text"; + } + + if (settings.paste_preprocess) { + editor.on('PastePreProcess', function(e) { + settings.paste_preprocess.call(self, self, e); + }); + } + + if (settings.paste_postprocess) { + editor.on('PastePostProcess', function(e) { + settings.paste_postprocess.call(self, self, e); + }); + } + + editor.addCommand('mceInsertClipboardContent', function(ui, value) { + if (value.content) { + self.clipboard.pasteHtml(value.content); + } + + if (value.text) { + self.clipboard.pasteText(value.text); + } + }); + + // Block all drag/drop events + if (editor.paste_block_drop) { + editor.on('dragend dragover draggesture dragdrop drop drag', function(e) { + e.preventDefault(); + e.stopPropagation(); + }); + } + + // Prevent users from dropping data images on Gecko + if (!editor.settings.paste_data_images) { + editor.on('drop', function(e) { + var dataTransfer = e.dataTransfer; + + if (dataTransfer && dataTransfer.files && dataTransfer.files.length > 0) { + e.preventDefault(); + } + }); + } + + editor.addButton('pastetext', { + icon: 'pastetext', + tooltip: 'Paste as text', + onclick: togglePlainTextPaste, + active: self.clipboard.pasteFormat == "text" + }); + + editor.addMenuItem('pastetext', { + text: 'Paste as text', + selectable: true, + active: clipboard.pasteFormat, + onclick: togglePlainTextPaste + }); + }); +}); + +expose(["tinymce/pasteplugin/Utils","tinymce/pasteplugin/WordFilter"]); +})(this); \ No newline at end of file diff --git a/wp-includes/js/tinymce/plugins/paste/plugin.min.js b/wp-includes/js/tinymce/plugins/paste/plugin.min.js new file mode 100644 index 0000000..cae7cf6 --- /dev/null +++ b/wp-includes/js/tinymce/plugins/paste/plugin.min.js @@ -0,0 +1 @@ +!function(a,b){"use strict";function c(a,b){for(var c,d=[],f=0;f/g]),f(h.parse(e)),i}function f(a){function b(a,b,c){return b||c?"\xa0":" "}return a=d(a,[/^[\s\S]*]*>\s*|\s*<\/body[^>]*>[\s\S]*$/g,/|/g,[/( ?)\u00a0<\/span>( ?)/g,b],/
        $/i])}return{filter:d,innerText:e,trimHtml:f}}),d("tinymce/pasteplugin/Clipboard",["tinymce/Env","tinymce/util/VK","tinymce/pasteplugin/Utils"],function(a,b,c){return function(d){function e(a){var b,c=d.dom;if(b=d.fire("BeforePastePreProcess",{content:a}),b=d.fire("PastePreProcess",b),a=b.content,!b.isDefaultPrevented()){if(d.hasEventListeners("PastePostProcess")&&!b.isDefaultPrevented()){var e=c.add(d.getBody(),"div",{style:"display:none"},a);b=d.fire("PastePostProcess",{node:e}),c.remove(e),a=b.node.innerHTML}b.isDefaultPrevented()||d.insertContent(a,{merge:d.settings.paste_merge_formats!==!1})}}function f(a){a=d.dom.encode(a).replace(/\r\n/g,"\n");var b,f=d.dom.getParent(d.selection.getStart(),d.dom.isBlock),g=d.settings.forced_root_block;g&&(b=d.dom.createHTML(g,d.settings.forced_root_block_attrs),b=b.substr(0,b.length-3)+">"),f&&/^(PRE|DIV)$/.test(f.nodeName)||!g?a=c.filter(a,[[/\n/g,"
        "]]):(a=c.filter(a,[[/\n\n/g,"

        "+b],[/^(.*<\/p>)(

        )$/,b+"$1"],[/\n/g,"
        "]]),-1!=a.indexOf("

        ")&&(a=b+a)),e(a)}function g(){function b(a){var b,c,d,f=a.startContainer;if(b=a.getClientRects(),b.length)return b[0];if(a.collapsed&&1==f.nodeType){for(d=f.childNodes[s.startOffset];d&&3==d.nodeType&&!d.data.length;)d=d.nextSibling;if(d)return"BR"==d.tagName&&(c=e.doc.createTextNode("\ufeff"),d.parentNode.insertBefore(c,d),a=e.createRng(),a.setStartBefore(c),a.setEndAfter(c),b=a.getClientRects(),e.remove(c)),b.length?b[0]:void 0}}var c,e=d.dom,f=d.getBody(),g=d.dom.getViewPort(d.getWin()),h=g.y,i=20;if(s=d.selection.getRng(),d.inline&&(c=d.selection.getScrollContainer(),c&&c.scrollTop>0&&(h=c.scrollTop)),s.getClientRects){var j=b(s);if(j)i=h+(j.top-e.getPos(f).y);else{i=h;var k=s.startContainer;k&&(3==k.nodeType&&k.parentNode!=f&&(k=k.parentNode),1==k.nodeType&&(i=e.getPos(k,c||f).y))}}r=e.add(d.getBody(),"div",{id:"mcepastebin",contentEditable:!0,"data-mce-bogus":"all",style:"position: absolute; top: "+i+"px;width: 10px; height: 10px; overflow: hidden; opacity: 0"},x),(a.ie||a.gecko)&&e.setStyle(r,"left","rtl"==e.getStyle(f,"direction",!0)?65535:-65535),e.bind(r,"beforedeactivate focusin focusout",function(a){a.stopPropagation()}),r.focus(),d.selection.select(r,!0)}function h(){if(r){for(var a;a=d.dom.get("mcepastebin");)d.dom.remove(a),d.dom.unbind(a);s&&d.selection.setRng(s)}r=s=null}function i(){var a,b,c,e,f="";for(a=d.dom.select("div[id=mcepastebin]"),b=0;b0&&(b["text/plain"]=c)}if(a.types)for(var d=0;d')}var g,h,i;if(c)for(g=0;g0}function p(a){return b.metaKeyPressed(a)&&86==a.keyCode||a.shiftKey&&45==a.keyCode}function q(){d.on("keydown",function(b){function c(a){p(a)&&!a.isDefaultPrevented()&&h()}if(p(b)&&!b.isDefaultPrevented()){if(t=b.shiftKey&&86==b.keyCode,t&&a.webkit&&-1!=navigator.userAgent.indexOf("Version/"))return;if(b.stopImmediatePropagation(),v=(new Date).getTime(),a.ie&&t)return b.preventDefault(),void d.fire("paste",{ieFake:!0});h(),g(),d.once("keyup",c),d.once("paste",function(){d.off("keyup",c)})}}),d.on("paste",function(b){var j=(new Date).getTime(),n=k(b),p=(new Date).getTime()-j,q=(new Date).getTime()-v-p<1e3,s="text"==u.pasteFormat||t;return t=!1,b.isDefaultPrevented()||m(b)?void h():l(b)?void h():(q||b.preventDefault(),!a.ie||q&&!b.ieFake||(g(),d.dom.bind(r,"paste",function(a){a.stopPropagation()}),d.getDoc().execCommand("Paste",!1,null),n["text/html"]=i()),void setTimeout(function(){var a;return o(n,"text/html")?a=n["text/html"]:(a=i(),a==x&&(s=!0)),a=c.trimHtml(a),r&&r.firstChild&&"mcepastebin"===r.firstChild.id&&(s=!0),h(),a.length||(s=!0),s&&(a=o(n,"text/plain")&&-1==a.indexOf("

        ")?n["text/plain"]:c.innerText(a)),a==x?void(q||d.windowManager.alert("Please use Ctrl+V/Cmd+V keyboard shortcuts to paste contents.")):void(s?f(a):e(a))},0))}),d.on("dragstart dragend",function(a){w="dragstart"==a.type}),d.on("drop",function(a){var b=n(a);if(!a.isDefaultPrevented()&&!w&&!l(a,b)&&b&&d.settings.paste_filter_drop!==!1){var g=j(a.dataTransfer),h=g["mce-internal"]||g["text/html"]||g["text/plain"];h&&(a.preventDefault(),d.undoManager.transact(function(){g["mce-internal"]&&d.execCommand("Delete"),d.selection.setRng(b),h=c.trimHtml(h),g["text/html"]?e(h):f(h)}))}}),d.on("dragover dragend",function(a){var b,c=a.dataTransfer;if(d.settings.paste_data_images&&c)for(b=0;bh?g&&(g=g.parent.parent):(j=g,g=null)),g&&g.name==b?g.append(a):(j=j||g,g=new e(b,1),f>1&&g.attr("start",""+f),a.wrap(g)),a.name="li",h>k&&j&&j.lastChild.append(g),k=h,d(a),c(a,/^\u00a0+/),c(a,/^\s*([\u2022\u00b7\u00a7\u00d8\u25CF]|\w+\.)/),c(a,/^\u00a0+/)}for(var g,j,k=1,l=[],m=a.firstChild;"undefined"!=typeof m&&null!==m;)if(l.push(m),m=m.walk(),null!==m)for(;"undefined"!=typeof m&&m.parent!==a;)m=m.walk();for(var n=0;n/gi,/<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|img|meta|link|style|\w:\w+)(?=[\s\/>]))[^>]*>/gi,[/<(\/?)s>/gi,"<$1strike>"],[/ /gi,"\xa0"],[/([\s\u00a0]*)<\/span>/gi,function(a,b){return b.length>0?b.replace(/./," ").slice(Math.floor(b.length/2)).split("").join("\xa0"):""}]]);var r=k.paste_word_valid_elements;r||(r="-strong/b,-em/i,-u,-span,-p,-ol,-ul,-li,-h1,-h2,-h3,-h4,-h5,-h6,-p/div,-a[href|name],sub,sup,strike,br,del,table[width],tr,td[colspan|rowspan|width],th[colspan|rowspan|width],thead,tfoot,tbody");var s=new c({valid_elements:r,valid_children:"-li[p]"});a.each(s.elements,function(a){a.attributes["class"]||(a.attributes["class"]={},a.attributesOrder.push("class")),a.attributes.style||(a.attributes.style={},a.attributesOrder.push("style"))});var t=new b({},s);t.addAttributeFilter("style",function(a){for(var b,c=a.length;c--;)b=a[c],b.attr("style",n(b,b.attr("style"))),"span"==b.name&&b.parent&&!b.attributes.length&&b.unwrap()}),t.addAttributeFilter("class",function(a){for(var b,c,d=a.length;d--;)b=a[d],c=b.attr("class"),/^(MsoCommentReference|MsoCommentText|msoDel|MsoCaption)$/i.test(c)&&b.remove(),b.attr("class",null)}),t.addNodeFilter("del",function(a){for(var b=a.length;b--;)a[b].remove()}),t.addNodeFilter("a",function(a){for(var b,c,d,e=a.length;e--;)if(b=a[e],c=b.attr("href"),d=b.attr("name"),c&&-1!=c.indexOf("#_msocom_"))b.remove();else if(c&&0===c.indexOf("file://")&&(c=c.split("#")[1],c&&(c="#"+c)),c||d){if(d&&!/^_?(?:toc|edn|ftn)/i.test(d)){b.unwrap();continue}b.attr({href:c,name:d})}else b.unwrap()});var u=t.parse(q);k.paste_convert_word_fake_lists!==!1&&m(u),l.content=new d({},s).serialize(u)}})}return j.isWordContent=g,j}),d("tinymce/pasteplugin/Quirks",["tinymce/Env","tinymce/util/Tools","tinymce/pasteplugin/WordFilter","tinymce/pasteplugin/Utils"],function(a,b,c,d){return function(e){function f(a){e.on("BeforePastePreProcess",function(b){b.content=a(b.content)})}function g(a){if(!c.isWordContent(a))return a;var f=[];b.each(e.schema.getBlockElements(),function(a,b){f.push(b)});var g=new RegExp("(?:
         [\\s\\r\\n]+|
        )*(<\\/?("+f.join("|")+")[^>]*>)(?:
         [\\s\\r\\n]+|
        )*","g");return a=d.filter(a,[[g,"$1"]]),a=d.filter(a,[[/

        /g,"

        "],[/
        /g," "],[/

        /g,"
        "]])}function h(a){if(c.isWordContent(a))return a;var b=e.settings.paste_webkit_styles;if(e.settings.paste_remove_styles_if_webkit===!1||"all"==b)return a;if(b&&(b=b.split(/[, ]/)),b){var d=e.dom,f=e.selection.getNode();a=a.replace(/(<[^>]+) style="([^"]*)"([^>]*>)/gi,function(a,c,e,g){var h=d.parseStyle(e,"span"),i={};if("none"===b)return c+g;for(var j=0;j]+) style="([^"]*)"([^>]*>)/gi,"$1$3");return a=a.replace(/(<[^>]+) data-mce-style="([^"]+)"([^>]*>)/gi,function(a,b,c,d){return b+' style="'+c+'"'+d})}a.webkit&&f(h),a.ie&&f(g)}}),d("tinymce/pasteplugin/Plugin",["tinymce/PluginManager","tinymce/pasteplugin/Clipboard","tinymce/pasteplugin/WordFilter","tinymce/pasteplugin/Quirks"],function(a,b,c,d){var e;a.add("paste",function(a){function f(){"text"==g.pasteFormat?(this.active(!1),g.pasteFormat="html"):(g.pasteFormat="text",this.active(!0),e||(a.windowManager.alert("Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off."),e=!0))}var g,h=this,i=a.settings;h.clipboard=g=new b(a),h.quirks=new d(a),h.wordFilter=new c(a),a.settings.paste_as_text&&(h.clipboard.pasteFormat="text"),i.paste_preprocess&&a.on("PastePreProcess",function(a){i.paste_preprocess.call(h,h,a)}),i.paste_postprocess&&a.on("PastePostProcess",function(a){i.paste_postprocess.call(h,h,a)}),a.addCommand("mceInsertClipboardContent",function(a,b){b.content&&h.clipboard.pasteHtml(b.content),b.text&&h.clipboard.pasteText(b.text)}),a.paste_block_drop&&a.on("dragend dragover draggesture dragdrop drop drag",function(a){a.preventDefault(),a.stopPropagation()}),a.settings.paste_data_images||a.on("drop",function(a){var b=a.dataTransfer;b&&b.files&&b.files.length>0&&a.preventDefault()}),a.addButton("pastetext",{icon:"pastetext",tooltip:"Paste as text",onclick:f,active:"text"==h.clipboard.pasteFormat}),a.addMenuItem("pastetext",{text:"Paste as text",selectable:!0,active:g.pasteFormat,onclick:f})})}),f(["tinymce/pasteplugin/Utils","tinymce/pasteplugin/WordFilter"])}(this); \ No newline at end of file diff --git a/wp-includes/js/tinymce/plugins/tabfocus/plugin.js b/wp-includes/js/tinymce/plugins/tabfocus/plugin.js new file mode 100644 index 0000000..9fb89bc --- /dev/null +++ b/wp-includes/js/tinymce/plugins/tabfocus/plugin.js @@ -0,0 +1,120 @@ +/** + * plugin.js + * + * Copyright, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/*global tinymce:true */ + +tinymce.PluginManager.add('tabfocus', function(editor) { + var DOM = tinymce.DOM, each = tinymce.each, explode = tinymce.explode; + + function tabCancel(e) { + if (e.keyCode === 9 && !e.ctrlKey && !e.altKey && !e.metaKey) { + e.preventDefault(); + } + } + + function tabHandler(e) { + var x, el, v, i; + + if (e.keyCode !== 9 || e.ctrlKey || e.altKey || e.metaKey || e.isDefaultPrevented()) { + return; + } + + function find(direction) { + el = DOM.select(':input:enabled,*[tabindex]:not(iframe)'); + + function canSelectRecursive(e) { + return e.nodeName === "BODY" || (e.type != 'hidden' && + e.style.display != "none" && + e.style.visibility != "hidden" && canSelectRecursive(e.parentNode)); + } + + function canSelect(el) { + return /INPUT|TEXTAREA|BUTTON/.test(el.tagName) && tinymce.get(e.id) && el.tabIndex != -1 && canSelectRecursive(el); + } + + each(el, function(e, i) { + if (e.id == editor.id) { + x = i; + return false; + } + }); + if (direction > 0) { + for (i = x + 1; i < el.length; i++) { + if (canSelect(el[i])) { + return el[i]; + } + } + } else { + for (i = x - 1; i >= 0; i--) { + if (canSelect(el[i])) { + return el[i]; + } + } + } + + return null; + } + + v = explode(editor.getParam('tab_focus', editor.getParam('tabfocus_elements', ':prev,:next'))); + + if (v.length == 1) { + v[1] = v[0]; + v[0] = ':prev'; + } + + // Find element to focus + if (e.shiftKey) { + if (v[0] == ':prev') { + el = find(-1); + } else { + el = DOM.get(v[0]); + } + } else { + if (v[1] == ':next') { + el = find(1); + } else { + el = DOM.get(v[1]); + } + } + + if (el) { + var focusEditor = tinymce.get(el.id || el.name); + + if (el.id && focusEditor) { + focusEditor.focus(); + } else { + window.setTimeout(function() { + if (!tinymce.Env.webkit) { + window.focus(); + } + + el.focus(); + }, 10); + } + + e.preventDefault(); + } + } + + editor.on('init', function() { + if (editor.inline) { + // Remove default tabIndex in inline mode + tinymce.DOM.setAttrib(editor.getBody(), 'tabIndex', null); + } + + editor.on('keyup', tabCancel); + + if (tinymce.Env.gecko) { + editor.on('keypress keydown', tabHandler); + } else { + editor.on('keydown', tabHandler); + } + }); +}); diff --git a/wp-includes/js/tinymce/plugins/tabfocus/plugin.min.js b/wp-includes/js/tinymce/plugins/tabfocus/plugin.min.js new file mode 100644 index 0000000..4fb883e --- /dev/null +++ b/wp-includes/js/tinymce/plugins/tabfocus/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("tabfocus",function(a){function b(a){9!==a.keyCode||a.ctrlKey||a.altKey||a.metaKey||a.preventDefault()}function c(b){function c(c){function f(a){return"BODY"===a.nodeName||"hidden"!=a.type&&"none"!=a.style.display&&"hidden"!=a.style.visibility&&f(a.parentNode)}function i(a){return/INPUT|TEXTAREA|BUTTON/.test(a.tagName)&&tinymce.get(b.id)&&-1!=a.tabIndex&&f(a)}if(h=d.select(":input:enabled,*[tabindex]:not(iframe)"),e(h,function(b,c){return b.id==a.id?(g=c,!1):void 0}),c>0){for(j=g+1;j=0;j--)if(i(h[j]))return h[j];return null}var g,h,i,j;if(!(9!==b.keyCode||b.ctrlKey||b.altKey||b.metaKey||b.isDefaultPrevented())&&(i=f(a.getParam("tab_focus",a.getParam("tabfocus_elements",":prev,:next"))),1==i.length&&(i[1]=i[0],i[0]=":prev"),h=b.shiftKey?":prev"==i[0]?c(-1):d.get(i[0]):":next"==i[1]?c(1):d.get(i[1]))){var k=tinymce.get(h.id||h.name);h.id&&k?k.focus():window.setTimeout(function(){tinymce.Env.webkit||window.focus(),h.focus()},10),b.preventDefault()}}var d=tinymce.DOM,e=tinymce.each,f=tinymce.explode;a.on("init",function(){a.inline&&tinymce.DOM.setAttrib(a.getBody(),"tabIndex",null),a.on("keyup",b),tinymce.Env.gecko?a.on("keypress keydown",c):a.on("keydown",c)})}); \ No newline at end of file diff --git a/wp-includes/js/tinymce/plugins/textcolor/plugin.js b/wp-includes/js/tinymce/plugins/textcolor/plugin.js new file mode 100644 index 0000000..b33988b --- /dev/null +++ b/wp-includes/js/tinymce/plugins/textcolor/plugin.js @@ -0,0 +1,272 @@ +/** + * plugin.js + * + * Copyright, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/*global tinymce:true */ +/*eslint consistent-this:0 */ + +tinymce.PluginManager.add('textcolor', function(editor) { + var cols, rows; + + rows = editor.settings.textcolor_rows || 5; + cols = editor.settings.textcolor_cols || 8; + + function getCurrentColor(format) { + var color; + + editor.dom.getParents(editor.selection.getStart(), function(elm) { + var value; + + if ((value = elm.style[format == 'forecolor' ? 'color' : 'background-color'])) { + color = value; + } + }); + + return color; + } + + function mapColors() { + var i, colors = [], colorMap; + + colorMap = editor.settings.textcolor_map || [ + "000000", "Black", + "993300", "Burnt orange", + "333300", "Dark olive", + "003300", "Dark green", + "003366", "Dark azure", + "000080", "Navy Blue", + "333399", "Indigo", + "333333", "Very dark gray", + "800000", "Maroon", + "FF6600", "Orange", + "808000", "Olive", + "008000", "Green", + "008080", "Teal", + "0000FF", "Blue", + "666699", "Grayish blue", + "808080", "Gray", + "FF0000", "Red", + "FF9900", "Amber", + "99CC00", "Yellow green", + "339966", "Sea green", + "33CCCC", "Turquoise", + "3366FF", "Royal blue", + "800080", "Purple", + "999999", "Medium gray", + "FF00FF", "Magenta", + "FFCC00", "Gold", + "FFFF00", "Yellow", + "00FF00", "Lime", + "00FFFF", "Aqua", + "00CCFF", "Sky blue", + "993366", "Red violet", + "FFFFFF", "White", + "FF99CC", "Pink", + "FFCC99", "Peach", + "FFFF99", "Light yellow", + "CCFFCC", "Pale green", + "CCFFFF", "Pale cyan", + "99CCFF", "Light sky blue", + "CC99FF", "Plum" + ]; + + for (i = 0; i < colorMap.length; i += 2) { + colors.push({ + text: colorMap[i + 1], + color: '#' + colorMap[i] + }); + } + + return colors; + } + + function renderColorPicker() { + var ctrl = this, colors, color, html, last, x, y, i, id = ctrl._id, count = 0; + + function getColorCellHtml(color, title) { + var isNoColor = color == 'transparent'; + + return ( + '
        ' + ); + } + + colors = mapColors(); + colors.push({ + text: tinymce.translate("No color"), + color: "transparent" + }); + + html = '
        ' + + '
        ' + + (isNoColor ? '×' : '') + + '
        ' + + '
        '; + last = colors.length - 1; + + for (y = 0; y < rows; y++) { + html += ''; + + for (x = 0; x < cols; x++) { + i = y * cols + x; + + if (i > last) { + html += ''; + } else { + color = colors[i]; + html += getColorCellHtml(color.color, color.text); + } + } + + html += ''; + } + + if (editor.settings.color_picker_callback) { + html += ( + '' + + '' + + '' + ); + + html += ''; + + for (x = 0; x < cols; x++) { + html += getColorCellHtml('', 'Custom color'); + } + + html += ''; + } + + html += '
        ' + + '
        ' + + '' + + '
        ' + + '
        '; + + return html; + } + + function applyFormat(format, value) { + editor.focus(); + editor.formatter.apply(format, {value: value}); + editor.nodeChanged(); + } + + function removeFormat(format) { + editor.focus(); + editor.formatter.remove(format, {value: null}, null, true); + editor.nodeChanged(); + } + + function onPanelClick(e) { + var buttonCtrl = this.parent(), value; + + function selectColor(value) { + buttonCtrl.hidePanel(); + buttonCtrl.color(value); + applyFormat(buttonCtrl.settings.format, value); + } + + function setDivColor(div, value) { + div.style.background = value; + div.setAttribute('data-mce-color', value); + } + + if (tinymce.DOM.getParent(e.target, '.mce-custom-color-btn')) { + buttonCtrl.hidePanel(); + + editor.settings.color_picker_callback.call(editor, function(value) { + var tableElm = buttonCtrl.panel.getEl().getElementsByTagName('table')[0]; + var customColorCells, div, i; + + customColorCells = tinymce.map(tableElm.rows[tableElm.rows.length - 1].childNodes, function(elm) { + return elm.firstChild; + }); + + for (i = 0; i < customColorCells.length; i++) { + div = customColorCells[i]; + if (!div.getAttribute('data-mce-color')) { + break; + } + } + + // Shift colors to the right + // TODO: Might need to be the left on RTL + if (i == cols) { + for (i = 0; i < cols - 1; i++) { + setDivColor(customColorCells[i], customColorCells[i + 1].getAttribute('data-mce-color')); + } + } + + setDivColor(div, value); + selectColor(value); + }, getCurrentColor(buttonCtrl.settings.format)); + } + + value = e.target.getAttribute('data-mce-color'); + if (value) { + if (this.lastId) { + document.getElementById(this.lastId).setAttribute('aria-selected', false); + } + + e.target.setAttribute('aria-selected', true); + this.lastId = e.target.id; + + if (value == 'transparent') { + removeFormat(buttonCtrl.settings.format); + buttonCtrl.hidePanel(); + return; + } + + selectColor(value); + } else if (value !== null) { + buttonCtrl.hidePanel(); + } + } + + function onButtonClick() { + var self = this; + + if (self._color) { + applyFormat(self.settings.format, self._color); + } + } + + editor.addButton('forecolor', { + type: 'colorbutton', + tooltip: 'Text color', + format: 'forecolor', + panel: { + role: 'application', + ariaRemember: true, + html: renderColorPicker, + onclick: onPanelClick + }, + onclick: onButtonClick + }); + + editor.addButton('backcolor', { + type: 'colorbutton', + tooltip: 'Background color', + format: 'hilitecolor', + panel: { + role: 'application', + ariaRemember: true, + html: renderColorPicker, + onclick: onPanelClick + }, + onclick: onButtonClick + }); +}); diff --git a/wp-includes/js/tinymce/plugins/textcolor/plugin.min.js b/wp-includes/js/tinymce/plugins/textcolor/plugin.min.js new file mode 100644 index 0000000..a10cf79 --- /dev/null +++ b/wp-includes/js/tinymce/plugins/textcolor/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("textcolor",function(a){function b(b){var c;return a.dom.getParents(a.selection.getStart(),function(a){var d;(d=a.style["forecolor"==b?"color":"background-color"])&&(c=d)}),c}function c(){var b,c,d=[];for(c=a.settings.textcolor_map||["000000","Black","993300","Burnt orange","333300","Dark olive","003300","Dark green","003366","Dark azure","000080","Navy Blue","333399","Indigo","333333","Very dark gray","800000","Maroon","FF6600","Orange","808000","Olive","008000","Green","008080","Teal","0000FF","Blue","666699","Grayish blue","808080","Gray","FF0000","Red","FF9900","Amber","99CC00","Yellow green","339966","Sea green","33CCCC","Turquoise","3366FF","Royal blue","800080","Purple","999999","Medium gray","FF00FF","Magenta","FFCC00","Gold","FFFF00","Yellow","00FF00","Lime","00FFFF","Aqua","00CCFF","Sky blue","993366","Red violet","FFFFFF","White","FF99CC","Pink","FFCC99","Peach","FFFF99","Light yellow","CCFFCC","Pale green","CCFFFF","Pale cyan","99CCFF","Light sky blue","CC99FF","Plum"],b=0;b

        '+(c?"×":"")+"
        "}var d,e,f,g,h,k,l,m=this,n=m._id,o=0;for(d=c(),d.push({text:tinymce.translate("No color"),color:"transparent"}),f='',g=d.length-1,k=0;j>k;k++){for(f+="",h=0;i>h;h++)l=k*i+h,l>g?f+="":(e=d[l],f+=b(e.color,e.text));f+=""}if(a.settings.color_picker_callback){for(f+='",f+="",h=0;i>h;h++)f+=b("","Custom color");f+=""}return f+="
        "}function e(b,c){a.focus(),a.formatter.apply(b,{value:c}),a.nodeChanged()}function f(b){a.focus(),a.formatter.remove(b,{value:null},null,!0),a.nodeChanged()}function g(c){function d(a){j.hidePanel(),j.color(a),e(j.settings.format,a)}function g(a,b){a.style.background=b,a.setAttribute("data-mce-color",b)}var h,j=this.parent();if(tinymce.DOM.getParent(c.target,".mce-custom-color-btn")&&(j.hidePanel(),a.settings.color_picker_callback.call(a,function(a){var b,c,e,f=j.panel.getEl().getElementsByTagName("table")[0];for(b=tinymce.map(f.rows[f.rows.length-1].childNodes,function(a){return a.firstChild}),e=0;ee;e++)g(b[e],b[e+1].getAttribute("data-mce-color"));g(c,a),d(a)},b(j.settings.format))),h=c.target.getAttribute("data-mce-color")){if(this.lastId&&document.getElementById(this.lastId).setAttribute("aria-selected",!1),c.target.setAttribute("aria-selected",!0),this.lastId=c.target.id,"transparent"==h)return f(j.settings.format),void j.hidePanel();d(h)}else null!==h&&j.hidePanel()}function h(){var a=this;a._color&&e(a.settings.format,a._color)}var i,j;j=a.settings.textcolor_rows||5,i=a.settings.textcolor_cols||8,a.addButton("forecolor",{type:"colorbutton",tooltip:"Text color",format:"forecolor",panel:{role:"application",ariaRemember:!0,html:d,onclick:g},onclick:h}),a.addButton("backcolor",{type:"colorbutton",tooltip:"Background color",format:"hilitecolor",panel:{role:"application",ariaRemember:!0,html:d,onclick:g},onclick:h})}); \ No newline at end of file diff --git a/wp-includes/js/tinymce/plugins/wordpress/plugin.js b/wp-includes/js/tinymce/plugins/wordpress/plugin.js new file mode 100644 index 0000000..f3af649 --- /dev/null +++ b/wp-includes/js/tinymce/plugins/wordpress/plugin.js @@ -0,0 +1,517 @@ +/* global tinymce, getUserSetting, setUserSetting */ + +// Set the minimum value for the modals z-index higher than #wpadminbar (100000) +tinymce.ui.FloatPanel.zIndex = 100100; + +tinymce.PluginManager.add( 'wordpress', function( editor ) { + var DOM = tinymce.DOM, wpAdvButton, modKey, style, + last = 0; + + if ( typeof window.jQuery !== 'undefined' ) { + window.jQuery( document ).triggerHandler( 'tinymce-editor-setup', [ editor ] ); + } + + function toggleToolbars( state ) { + var iframe, initial, toolbars, + pixels = 0; + + initial = ( state === 'hide' ); + + if ( editor.theme.panel ) { + toolbars = editor.theme.panel.find('.toolbar:not(.menubar)'); + } + + if ( ! toolbars || toolbars.length < 2 || ( state === 'hide' && ! toolbars[1].visible() ) ) { + return; + } + + if ( ! state && toolbars[1].visible() ) { + state = 'hide'; + } + + tinymce.each( toolbars, function( toolbar, i ) { + if ( i > 0 ) { + if ( state === 'hide' ) { + toolbar.hide(); + pixels += 30; + } else { + toolbar.show(); + pixels -= 30; + } + } + }); + + if ( pixels && ! initial ) { + // Resize iframe, not needed in iOS + if ( ! tinymce.Env.iOS ) { + iframe = editor.getContentAreaContainer().firstChild; + DOM.setStyle( iframe, 'height', iframe.clientHeight + pixels ); + } + + if ( state === 'hide' ) { + setUserSetting('hidetb', '0'); + wpAdvButton && wpAdvButton.active( false ); + } else { + setUserSetting('hidetb', '1'); + wpAdvButton && wpAdvButton.active( true ); + } + } + + editor.fire( 'wp-toolbar-toggle' ); + } + + // Add the kitchen sink button :) + editor.addButton( 'wp_adv', { + tooltip: 'Toolbar Toggle', + cmd: 'WP_Adv', + onPostRender: function() { + wpAdvButton = this; + wpAdvButton.active( getUserSetting( 'hidetb' ) === '1' ? true : false ); + } + }); + + // Hide the toolbars after loading + editor.on( 'PostRender', function() { + if ( editor.getParam( 'wordpress_adv_hidden', true ) && getUserSetting( 'hidetb', '0' ) === '0' ) { + toggleToolbars( 'hide' ); + } + }); + + editor.addCommand( 'WP_Adv', function() { + toggleToolbars(); + }); + + editor.on( 'focus', function() { + window.wpActiveEditor = editor.id; + }); + + // Replace Read More/Next Page tags with images + editor.on( 'BeforeSetContent', function( e ) { + var title; + + if ( e.content ) { + if ( e.content.indexOf( '/g, function( match, moretext ) { + return ''; + }); + } + + if ( e.content.indexOf( '' ) !== -1 ) { + title = editor.editorManager.i18n.translate( 'Page break' ); + + e.content = e.content.replace( //g, + '' ); + } + } + }); + + // Replace images with tags + editor.on( 'PostProcess', function( e ) { + if ( e.get ) { + e.content = e.content.replace(/]+>/g, function( image ) { + var match, moretext = ''; + + if ( image.indexOf( 'data-wp-more="more"' ) !== -1 ) { + if ( match = image.match( /data-wp-more-text="([^"]+)"/ ) ) { + moretext = match[1]; + } + + image = ''; + } else if ( image.indexOf( 'data-wp-more="nextpage"' ) !== -1 ) { + image = ''; + } + + return image; + }); + } + }); + + // Display the tag name instead of img in element path + editor.on( 'ResolveName', function( event ) { + var attr; + + if ( event.target.nodeName === 'IMG' && ( attr = editor.dom.getAttrib( event.target, 'data-wp-more' ) ) ) { + event.name = attr; + } + }); + + // Register commands + editor.addCommand( 'WP_More', function( tag ) { + var parent, html, title, + classname = 'wp-more-tag', + dom = editor.dom, + node = editor.selection.getNode(); + + tag = tag || 'more'; + classname += ' mce-wp-' + tag; + title = tag === 'more' ? 'Read more...' : 'Next page'; + title = editor.editorManager.i18n.translate( title ); + html = ''; + + // Most common case + if ( node.nodeName === 'BODY' || ( node.nodeName === 'P' && node.parentNode.nodeName === 'BODY' ) ) { + editor.insertContent( html ); + return; + } + + // Get the top level parent node + parent = dom.getParent( node, function( found ) { + if ( found.parentNode && found.parentNode.nodeName === 'BODY' ) { + return true; + } + + return false; + }, editor.getBody() ); + + if ( parent ) { + if ( parent.nodeName === 'P' ) { + parent.appendChild( dom.create( 'p', null, html ).firstChild ); + } else { + dom.insertAfter( dom.create( 'p', null, html ), parent ); + } + + editor.nodeChanged(); + } + }); + + editor.addCommand( 'WP_Code', function() { + editor.formatter.toggle('code'); + }); + + editor.addCommand( 'WP_Page', function() { + editor.execCommand( 'WP_More', 'nextpage' ); + }); + + editor.addCommand( 'WP_Help', function() { + editor.windowManager.open({ + url: tinymce.baseURL + '/wp-mce-help.php', + title: 'Keyboard Shortcuts', + width: 450, + height: 420, + classes: 'wp-help', + buttons: { text: 'Close', onclick: 'close' } + }); + }); + + editor.addCommand( 'WP_Medialib', function() { + if ( typeof wp !== 'undefined' && wp.media && wp.media.editor ) { + wp.media.editor.open( editor.id ); + } + }); + + // Register buttons + editor.addButton( 'wp_more', { + tooltip: 'Insert Read More tag', + onclick: function() { + editor.execCommand( 'WP_More', 'more' ); + } + }); + + editor.addButton( 'wp_page', { + tooltip: 'Page break', + onclick: function() { + editor.execCommand( 'WP_More', 'nextpage' ); + } + }); + + editor.addButton( 'wp_help', { + tooltip: 'Keyboard Shortcuts', + cmd: 'WP_Help' + }); + + editor.addButton( 'wp_code', { + tooltip: 'Code', + cmd: 'WP_Code', + stateSelector: 'code' + }); + + // Menubar + // Insert->Add Media + if ( typeof wp !== 'undefined' && wp.media && wp.media.editor ) { + editor.addMenuItem( 'add_media', { + text: 'Add Media', + icon: 'wp-media-library', + context: 'insert', + cmd: 'WP_Medialib' + }); + } + + // Insert "Read More..." + editor.addMenuItem( 'wp_more', { + text: 'Insert Read More tag', + icon: 'wp_more', + context: 'insert', + onclick: function() { + editor.execCommand( 'WP_More', 'more' ); + } + }); + + // Insert "Next Page" + editor.addMenuItem( 'wp_page', { + text: 'Page break', + icon: 'wp_page', + context: 'insert', + onclick: function() { + editor.execCommand( 'WP_More', 'nextpage' ); + } + }); + + editor.on( 'BeforeExecCommand', function(e) { + if ( tinymce.Env.webkit && ( e.command === 'InsertUnorderedList' || e.command === 'InsertOrderedList' ) ) { + if ( ! style ) { + style = editor.dom.create( 'style', {'type': 'text/css'}, + '#tinymce,#tinymce span,#tinymce li,#tinymce li>span,#tinymce p,#tinymce p>span{font:medium sans-serif;color:#000;line-height:normal;}'); + } + + editor.getDoc().head.appendChild( style ); + } + }); + + editor.on( 'ExecCommand', function( e ) { + if ( tinymce.Env.webkit && style && + ( 'InsertUnorderedList' === e.command || 'InsertOrderedList' === e.command ) ) { + + editor.dom.remove( style ); + } + }); + + editor.on( 'init', function() { + var env = tinymce.Env, + bodyClass = ['mceContentBody'], // back-compat for themes that use this in editor-style.css... + doc = editor.getDoc(), + dom = editor.dom; + + if ( tinymce.Env.iOS ) { + dom.addClass( doc.documentElement, 'ios' ); + } + + if ( editor.getParam( 'directionality' ) === 'rtl' ) { + bodyClass.push('rtl'); + dom.setAttrib( doc.documentElement, 'dir', 'rtl' ); + } + + if ( env.ie ) { + if ( parseInt( env.ie, 10 ) === 9 ) { + bodyClass.push('ie9'); + } else if ( parseInt( env.ie, 10 ) === 8 ) { + bodyClass.push('ie8'); + } else if ( env.ie < 8 ) { + bodyClass.push('ie7'); + } + } else if ( env.webkit ) { + bodyClass.push('webkit'); + } + + bodyClass.push('wp-editor'); + + tinymce.each( bodyClass, function( cls ) { + if ( cls ) { + dom.addClass( doc.body, cls ); + } + }); + + // Remove invalid parent paragraphs when inserting HTML + // TODO: still needed? + editor.on( 'BeforeSetContent', function( e ) { + if ( e.content ) { + e.content = e.content.replace(/

        \s*<(p|div|ul|ol|dl|table|blockquote|h[1-6]|fieldset|pre|address)( [^>]*)?>/gi, '<$1$2>'); + e.content = e.content.replace(/<\/(p|div|ul|ol|dl|table|blockquote|h[1-6]|fieldset|pre|address)>\s*<\/p>/gi, ''); + } + }); + + if ( typeof window.jQuery !== 'undefined' ) { + window.jQuery( document ).triggerHandler( 'tinymce-editor-init', [editor] ); + } + + if ( window.tinyMCEPreInit && window.tinyMCEPreInit.dragDropUpload ) { + dom.bind( doc, 'dragstart dragend dragover drop', function( event ) { + if ( typeof window.jQuery !== 'undefined' ) { + // Trigger the jQuery handlers. + window.jQuery( document ).trigger( new window.jQuery.Event( event ) ); + } + }); + } + + if ( editor.getParam( 'wp_paste_filters', true ) ) { + if ( ! tinymce.Env.webkit ) { + // In WebKit handled by removeWebKitStyles() + editor.on( 'PastePreProcess', function( event ) { + // Remove all inline styles + event.content = event.content.replace( /(<[^>]+) style="[^"]*"([^>]*>)/gi, '$1$2' ); + + // Put back the internal styles + event.content = event.content.replace(/(<[^>]+) data-mce-style=([^>]+>)/gi, '$1 style=$2' ); + }); + } + + editor.on( 'PastePostProcess', function( event ) { + // Remove empty paragraphs + tinymce.each( dom.select( 'p', event.node ), function( node ) { + if ( dom.isEmpty( node ) ) { + dom.remove( node ); + } + }); + }); + } + }); + + // Word count + if ( typeof window.jQuery !== 'undefined' ) { + editor.on( 'keyup', function( e ) { + var key = e.keyCode || e.charCode; + + if ( key === last ) { + return; + } + + if ( 13 === key || 8 === last || 46 === last ) { + window.jQuery( document ).triggerHandler( 'wpcountwords', [ editor.getContent({ format : 'raw' }) ] ); + } + + last = key; + }); + } + + editor.on( 'SaveContent', function( e ) { + // If editor is hidden, we just want the textarea's value to be saved + if ( ! editor.inline && editor.isHidden() ) { + e.content = e.element.value; + return; + } + + // Keep empty paragraphs :( + e.content = e.content.replace( /

        (?:
        |\u00a0|\uFEFF| )*<\/p>/g, '

         

        ' ); + + if ( editor.getParam( 'wpautop', true ) && typeof window.switchEditors !== 'undefined' ) { + e.content = window.switchEditors.pre_wpautop( e.content ); + } + }); + + // Remove spaces from empty paragraphs. + editor.on( 'BeforeSetContent', function( event ) { + if ( event.content ) { + event.content = event.content.replace( /

        (?: |\u00a0|\uFEFF| )+<\/p>/gi, '

        ' ); + } + }); + + editor.on( 'preInit', function() { + // Don't replace with and with and don't remove them when empty + editor.schema.addValidElements( '@[id|accesskey|class|dir|lang|style|tabindex|title|contenteditable|draggable|dropzone|hidden|spellcheck|translate],i,b' ); + + if ( tinymce.Env.iOS ) { + editor.settings.height = 300; + } + }); + + // Add custom shortcuts + modKey = 'alt+shift'; + + editor.addShortcut( modKey + '+c', '', 'JustifyCenter' ); + editor.addShortcut( modKey + '+r', '', 'JustifyRight' ); + editor.addShortcut( modKey + '+l', '', 'JustifyLeft' ); + editor.addShortcut( modKey + '+j', '', 'JustifyFull' ); + editor.addShortcut( modKey + '+q', '', 'mceBlockQuote' ); + editor.addShortcut( modKey + '+u', '', 'InsertUnorderedList' ); + editor.addShortcut( modKey + '+o', '', 'InsertOrderedList' ); + editor.addShortcut( modKey + '+n', '', 'mceSpellCheck' ); + editor.addShortcut( modKey + '+s', '', 'unlink' ); + editor.addShortcut( modKey + '+m', '', 'WP_Medialib' ); + editor.addShortcut( modKey + '+z', '', 'WP_Adv' ); + editor.addShortcut( modKey + '+t', '', 'WP_More' ); + editor.addShortcut( modKey + '+d', '', 'Strikethrough' ); + editor.addShortcut( modKey + '+h', '', 'WP_Help' ); + editor.addShortcut( modKey + '+p', '', 'WP_Page' ); + editor.addShortcut( modKey + '+x', '', 'WP_Code' ); + editor.addShortcut( 'ctrl+s', '', function() { + if ( typeof wp !== 'undefined' && wp.autosave ) { + wp.autosave.server.triggerSave(); + } + }); + + // popup buttons for the gallery, etc. + editor.on( 'init', function() { + editor.dom.bind( editor.getWin(), 'scroll', function() { + _hideButtons(); + }); + + editor.dom.bind( editor.getBody(), 'dragstart', function() { + _hideButtons(); + }); + }); + + editor.on( 'BeforeExecCommand', function() { + _hideButtons(); + }); + + editor.on( 'SaveContent', function() { + _hideButtons(); + }); + + editor.on( 'MouseDown', function( e ) { + if ( e.target.nodeName !== 'IMG' ) { + _hideButtons(); + } + }); + + editor.on( 'keydown', function( e ) { + if ( e.which === tinymce.util.VK.DELETE || e.which === tinymce.util.VK.BACKSPACE ) { + _hideButtons(); + } + }); + + // Internal functions + function _setEmbed( c ) { + return c.replace( /\[embed\]([\s\S]+?)\[\/embed\][\s\u00a0]*/g, function( a, b ) { + return ''; + }); + } + + function _getEmbed( c ) { + return c.replace( /]+>/g, function( a ) { + if ( a.indexOf('class="wp-oembed') !== -1 ) { + var u = a.match( /alt="([^\"]+)"/ ); + + if ( u[1] ) { + a = '[embed]' + u[1] + '[/embed]'; + } + } + + return a; + }); + } + + function _showButtons( n, id ) { + var p1, p2, vp, X, Y; + + vp = editor.dom.getViewPort( editor.getWin() ); + p1 = DOM.getPos( editor.getContentAreaContainer() ); + p2 = editor.dom.getPos( n ); + + X = Math.max( p2.x - vp.x, 0 ) + p1.x; + Y = Math.max( p2.y - vp.y, 0 ) + p1.y; + + DOM.setStyles( id, { + 'top' : Y + 5 + 'px', + 'left' : X + 5 + 'px', + 'display': 'block' + }); + } + + function _hideButtons() { + DOM.hide( DOM.select( '#wp_editbtns, #wp_gallerybtns' ) ); + } + + // Expose some functions (back-compat) + return { + _showButtons: _showButtons, + _hideButtons: _hideButtons, + _setEmbed: _setEmbed, + _getEmbed: _getEmbed + }; +}); diff --git a/wp-includes/js/tinymce/plugins/wordpress/plugin.min.js b/wp-includes/js/tinymce/plugins/wordpress/plugin.min.js new file mode 100644 index 0000000..2575374 --- /dev/null +++ b/wp-includes/js/tinymce/plugins/wordpress/plugin.min.js @@ -0,0 +1 @@ +tinymce.ui.FloatPanel.zIndex=100100,tinymce.PluginManager.add("wordpress",function(a){function b(b){var c,d,e,f=0;d="hide"===b,a.theme.panel&&(e=a.theme.panel.find(".toolbar:not(.menubar)")),!e||e.length<2||"hide"===b&&!e[1].visible()||(!b&&e[1].visible()&&(b="hide"),tinymce.each(e,function(a,c){c>0&&("hide"===b?(a.hide(),f+=30):(a.show(),f-=30))}),f&&!d&&(tinymce.Env.iOS||(c=a.getContentAreaContainer().firstChild,j.setStyle(c,"height",c.clientHeight+f)),"hide"===b?(setUserSetting("hidetb","0"),g&&g.active(!1)):(setUserSetting("hidetb","1"),g&&g.active(!0))),a.fire("wp-toolbar-toggle"))}function c(a){return a.replace(/\[embed\]([\s\S]+?)\[\/embed\][\s\u00a0]*/g,function(a,b){return''+b+''})}function d(a){return a.replace(/]+>/g,function(a){if(-1!==a.indexOf('class="wp-oembed')){var b=a.match(/alt="([^\"]+)"/);b[1]&&(a="[embed]"+b[1]+"[/embed]")}return a})}function e(b,c){var d,e,f,g,h;f=a.dom.getViewPort(a.getWin()),d=j.getPos(a.getContentAreaContainer()),e=a.dom.getPos(b),g=Math.max(e.x-f.x,0)+d.x,h=Math.max(e.y-f.y,0)+d.y,j.setStyles(c,{top:h+5+"px",left:g+5+"px",display:"block"})}function f(){j.hide(j.select("#wp_editbtns, #wp_gallerybtns"))}var g,h,i,j=tinymce.DOM,k=0;return"undefined"!=typeof window.jQuery&&window.jQuery(document).triggerHandler("tinymce-editor-setup",[a]),a.addButton("wp_adv",{tooltip:"Toolbar Toggle",cmd:"WP_Adv",onPostRender:function(){g=this,g.active("1"===getUserSetting("hidetb")?!0:!1)}}),a.on("PostRender",function(){a.getParam("wordpress_adv_hidden",!0)&&"0"===getUserSetting("hidetb","0")&&b("hide")}),a.addCommand("WP_Adv",function(){b()}),a.on("focus",function(){window.wpActiveEditor=a.id}),a.on("BeforeSetContent",function(b){var c;b.content&&(-1!==b.content.indexOf("/g,function(a,b){return''})),-1!==b.content.indexOf("")&&(c=a.editorManager.i18n.translate("Page break"),b.content=b.content.replace(//g,'')))}),a.on("PostProcess",function(a){a.get&&(a.content=a.content.replace(/]+>/g,function(a){var b,c="";return-1!==a.indexOf('data-wp-more="more"')?((b=a.match(/data-wp-more-text="([^"]+)"/))&&(c=b[1]),a=""):-1!==a.indexOf('data-wp-more="nextpage"')&&(a=""),a}))}),a.on("ResolveName",function(b){var c;"IMG"===b.target.nodeName&&(c=a.dom.getAttrib(b.target,"data-wp-more"))&&(b.name=c)}),a.addCommand("WP_More",function(b){var c,d,e,f="wp-more-tag",g=a.dom,h=a.selection.getNode();return b=b||"more",f+=" mce-wp-"+b,e="more"===b?"Read more...":"Next page",e=a.editorManager.i18n.translate(e),d='',"BODY"===h.nodeName||"P"===h.nodeName&&"BODY"===h.parentNode.nodeName?void a.insertContent(d):(c=g.getParent(h,function(a){return a.parentNode&&"BODY"===a.parentNode.nodeName?!0:!1},a.getBody()),void(c&&("P"===c.nodeName?c.appendChild(g.create("p",null,d).firstChild):g.insertAfter(g.create("p",null,d),c),a.nodeChanged())))}),a.addCommand("WP_Code",function(){a.formatter.toggle("code")}),a.addCommand("WP_Page",function(){a.execCommand("WP_More","nextpage")}),a.addCommand("WP_Help",function(){a.windowManager.open({url:tinymce.baseURL+"/wp-mce-help.php",title:"Keyboard Shortcuts",width:450,height:420,classes:"wp-help",buttons:{text:"Close",onclick:"close"}})}),a.addCommand("WP_Medialib",function(){"undefined"!=typeof wp&&wp.media&&wp.media.editor&&wp.media.editor.open(a.id)}),a.addButton("wp_more",{tooltip:"Insert Read More tag",onclick:function(){a.execCommand("WP_More","more")}}),a.addButton("wp_page",{tooltip:"Page break",onclick:function(){a.execCommand("WP_More","nextpage")}}),a.addButton("wp_help",{tooltip:"Keyboard Shortcuts",cmd:"WP_Help"}),a.addButton("wp_code",{tooltip:"Code",cmd:"WP_Code",stateSelector:"code"}),"undefined"!=typeof wp&&wp.media&&wp.media.editor&&a.addMenuItem("add_media",{text:"Add Media",icon:"wp-media-library",context:"insert",cmd:"WP_Medialib"}),a.addMenuItem("wp_more",{text:"Insert Read More tag",icon:"wp_more",context:"insert",onclick:function(){a.execCommand("WP_More","more")}}),a.addMenuItem("wp_page",{text:"Page break",icon:"wp_page",context:"insert",onclick:function(){a.execCommand("WP_More","nextpage")}}),a.on("BeforeExecCommand",function(b){!tinymce.Env.webkit||"InsertUnorderedList"!==b.command&&"InsertOrderedList"!==b.command||(i||(i=a.dom.create("style",{type:"text/css"},"#tinymce,#tinymce span,#tinymce li,#tinymce li>span,#tinymce p,#tinymce p>span{font:medium sans-serif;color:#000;line-height:normal;}")),a.getDoc().head.appendChild(i))}),a.on("ExecCommand",function(b){tinymce.Env.webkit&&i&&("InsertUnorderedList"===b.command||"InsertOrderedList"===b.command)&&a.dom.remove(i)}),a.on("init",function(){var b=tinymce.Env,c=["mceContentBody"],d=a.getDoc(),e=a.dom;tinymce.Env.iOS&&e.addClass(d.documentElement,"ios"),"rtl"===a.getParam("directionality")&&(c.push("rtl"),e.setAttrib(d.documentElement,"dir","rtl")),b.ie?9===parseInt(b.ie,10)?c.push("ie9"):8===parseInt(b.ie,10)?c.push("ie8"):b.ie<8&&c.push("ie7"):b.webkit&&c.push("webkit"),c.push("wp-editor"),tinymce.each(c,function(a){a&&e.addClass(d.body,a)}),a.on("BeforeSetContent",function(a){a.content&&(a.content=a.content.replace(/

        \s*<(p|div|ul|ol|dl|table|blockquote|h[1-6]|fieldset|pre|address)( [^>]*)?>/gi,"<$1$2>"),a.content=a.content.replace(/<\/(p|div|ul|ol|dl|table|blockquote|h[1-6]|fieldset|pre|address)>\s*<\/p>/gi,""))}),"undefined"!=typeof window.jQuery&&window.jQuery(document).triggerHandler("tinymce-editor-init",[a]),window.tinyMCEPreInit&&window.tinyMCEPreInit.dragDropUpload&&e.bind(d,"dragstart dragend dragover drop",function(a){"undefined"!=typeof window.jQuery&&window.jQuery(document).trigger(new window.jQuery.Event(a))}),a.getParam("wp_paste_filters",!0)&&(tinymce.Env.webkit||a.on("PastePreProcess",function(a){a.content=a.content.replace(/(<[^>]+) style="[^"]*"([^>]*>)/gi,"$1$2"),a.content=a.content.replace(/(<[^>]+) data-mce-style=([^>]+>)/gi,"$1 style=$2")}),a.on("PastePostProcess",function(a){tinymce.each(e.select("p",a.node),function(a){e.isEmpty(a)&&e.remove(a)})}))}),"undefined"!=typeof window.jQuery&&a.on("keyup",function(b){var c=b.keyCode||b.charCode;c!==k&&((13===c||8===k||46===k)&&window.jQuery(document).triggerHandler("wpcountwords",[a.getContent({format:"raw"})]),k=c)}),a.on("SaveContent",function(b){return!a.inline&&a.isHidden()?void(b.content=b.element.value):(b.content=b.content.replace(/

        (?:
        |\u00a0|\uFEFF| )*<\/p>/g,"

         

        "),void(a.getParam("wpautop",!0)&&"undefined"!=typeof window.switchEditors&&(b.content=window.switchEditors.pre_wpautop(b.content))))}),a.on("BeforeSetContent",function(a){a.content&&(a.content=a.content.replace(/

        (?: |\u00a0|\uFEFF| )+<\/p>/gi,"

        "))}),a.on("preInit",function(){a.schema.addValidElements("@[id|accesskey|class|dir|lang|style|tabindex|title|contenteditable|draggable|dropzone|hidden|spellcheck|translate],i,b"),tinymce.Env.iOS&&(a.settings.height=300)}),h="alt+shift",a.addShortcut(h+"+c","","JustifyCenter"),a.addShortcut(h+"+r","","JustifyRight"),a.addShortcut(h+"+l","","JustifyLeft"),a.addShortcut(h+"+j","","JustifyFull"),a.addShortcut(h+"+q","","mceBlockQuote"),a.addShortcut(h+"+u","","InsertUnorderedList"),a.addShortcut(h+"+o","","InsertOrderedList"),a.addShortcut(h+"+n","","mceSpellCheck"),a.addShortcut(h+"+s","","unlink"),a.addShortcut(h+"+m","","WP_Medialib"),a.addShortcut(h+"+z","","WP_Adv"),a.addShortcut(h+"+t","","WP_More"),a.addShortcut(h+"+d","","Strikethrough"),a.addShortcut(h+"+h","","WP_Help"),a.addShortcut(h+"+p","","WP_Page"),a.addShortcut(h+"+x","","WP_Code"),a.addShortcut("ctrl+s","",function(){"undefined"!=typeof wp&&wp.autosave&&wp.autosave.server.triggerSave()}),a.on("init",function(){a.dom.bind(a.getWin(),"scroll",function(){f()}),a.dom.bind(a.getBody(),"dragstart",function(){f()})}),a.on("BeforeExecCommand",function(){f()}),a.on("SaveContent",function(){f()}),a.on("MouseDown",function(a){"IMG"!==a.target.nodeName&&f()}),a.on("keydown",function(a){(a.which===tinymce.util.VK.DELETE||a.which===tinymce.util.VK.BACKSPACE)&&f()}),{_showButtons:e,_hideButtons:f,_setEmbed:c,_getEmbed:d}}); \ No newline at end of file diff --git a/wp-includes/js/tinymce/plugins/wpautoresize/plugin.js b/wp-includes/js/tinymce/plugins/wpautoresize/plugin.js new file mode 100644 index 0000000..38ee2f8 --- /dev/null +++ b/wp-includes/js/tinymce/plugins/wpautoresize/plugin.js @@ -0,0 +1,207 @@ +/** + * plugin.js + * + * Copyright, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +// Forked for WordPress so it can be turned on/off after loading. + +/*global tinymce:true */ +/*eslint no-nested-ternary:0 */ + +/** + * Auto Resize + * + * This plugin automatically resizes the content area to fit its content height. + * It will retain a minimum height, which is the height of the content area when + * it's initialized. + */ +tinymce.PluginManager.add( 'wpautoresize', function( editor ) { + var settings = editor.settings, + oldSize = 300, + isActive = false; + + function isFullscreen() { + return editor.plugins.fullscreen && editor.plugins.fullscreen.isFullscreen(); + } + + if ( editor.settings.inline ) { + return; + } + + function getInt( n ) { + return parseInt( n, 10 ) || 0; + } + + /** + * This method gets executed each time the editor needs to resize. + */ + function resize( e ) { + var deltaSize, doc, body, docElm, DOM = tinymce.DOM, resizeHeight, myHeight, + marginTop, marginBottom, paddingTop, paddingBottom, borderTop, borderBottom; + + if ( ! isActive ) { + return; + } + + doc = editor.getDoc(); + if ( ! doc ) { + return; + } + + e = e || {}; + body = doc.body; + docElm = doc.documentElement; + resizeHeight = settings.autoresize_min_height; + + if ( ! body || ( e && e.type === 'setcontent' && e.initial ) || isFullscreen() ) { + if ( body && docElm ) { + body.style.overflowY = 'auto'; + docElm.style.overflowY = 'auto'; // Old IE + } + + return; + } + + // Calculate outer height of the body element using CSS styles + marginTop = editor.dom.getStyle( body, 'margin-top', true ); + marginBottom = editor.dom.getStyle( body, 'margin-bottom', true ); + paddingTop = editor.dom.getStyle( body, 'padding-top', true ); + paddingBottom = editor.dom.getStyle( body, 'padding-bottom', true ); + borderTop = editor.dom.getStyle( body, 'border-top-width', true ); + borderBottom = editor.dom.getStyle( body, 'border-bottom-width', true ); + myHeight = body.offsetHeight + getInt( marginTop ) + getInt( marginBottom ) + + getInt( paddingTop ) + getInt( paddingBottom ) + + getInt( borderTop ) + getInt( borderBottom ); + + // IE < 11, other? + if ( myHeight && myHeight < docElm.offsetHeight ) { + myHeight = docElm.offsetHeight; + } + + // Make sure we have a valid height + if ( isNaN( myHeight ) || myHeight <= 0 ) { + // Get height differently depending on the browser used + myHeight = tinymce.Env.ie ? body.scrollHeight : ( tinymce.Env.webkit && body.clientHeight === 0 ? 0 : body.offsetHeight ); + } + + // Don't make it smaller than the minimum height + if ( myHeight > settings.autoresize_min_height ) { + resizeHeight = myHeight; + } + + // If a maximum height has been defined don't exceed this height + if ( settings.autoresize_max_height && myHeight > settings.autoresize_max_height ) { + resizeHeight = settings.autoresize_max_height; + body.style.overflowY = 'auto'; + docElm.style.overflowY = 'auto'; // Old IE + } else { + body.style.overflowY = 'hidden'; + docElm.style.overflowY = 'hidden'; // Old IE + body.scrollTop = 0; + } + + // Resize content element + if (resizeHeight !== oldSize) { + deltaSize = resizeHeight - oldSize; + DOM.setStyle( editor.iframeElement, 'height', resizeHeight + 'px' ); + oldSize = resizeHeight; + + // WebKit doesn't decrease the size of the body element until the iframe gets resized + // So we need to continue to resize the iframe down until the size gets fixed + if ( tinymce.isWebKit && deltaSize < 0 ) { + resize( e ); + } + + editor.fire( 'wp-autoresize', { height: resizeHeight, deltaHeight: e.type === 'nodechange' ? deltaSize : null } ); + } + } + + /** + * Calls the resize x times in 100ms intervals. We can't wait for load events since + * the CSS files might load async. + */ + function wait( times, interval, callback ) { + setTimeout( function() { + resize(); + + if ( times-- ) { + wait( times, interval, callback ); + } else if ( callback ) { + callback(); + } + }, interval ); + } + + // Define minimum height + settings.autoresize_min_height = parseInt(editor.getParam( 'autoresize_min_height', editor.getElement().offsetHeight), 10 ); + + // Define maximum height + settings.autoresize_max_height = parseInt(editor.getParam( 'autoresize_max_height', 0), 10 ); + + function on() { + if ( ! editor.dom.hasClass( editor.getBody(), 'wp-autoresize' ) ) { + isActive = true; + editor.dom.addClass( editor.getBody(), 'wp-autoresize' ); + // Add appropriate listeners for resizing the content area + editor.on( 'nodechange setcontent keyup FullscreenStateChanged', resize ); + resize(); + } + } + + function off() { + var doc; + + // Don't turn off if the setting is 'on' + if ( ! settings.wp_autoresize_on ) { + isActive = false; + doc = editor.getDoc(); + editor.dom.removeClass( editor.getBody(), 'wp-autoresize' ); + editor.off( 'nodechange setcontent keyup FullscreenStateChanged', resize ); + doc.body.style.overflowY = 'auto'; + doc.documentElement.style.overflowY = 'auto'; // Old IE + oldSize = 0; + } + } + + if ( settings.wp_autoresize_on ) { + // Turn resizing on when the editor loads + isActive = true; + + editor.on( 'init', function() { + editor.dom.addClass( editor.getBody(), 'wp-autoresize' ); + }); + + editor.on( 'nodechange keyup FullscreenStateChanged', resize ); + + editor.on( 'setcontent', function() { + wait( 3, 100 ); + }); + + if ( editor.getParam( 'autoresize_on_init', true ) ) { + editor.on( 'init', function() { + // Hit it 10 times in 200 ms intervals + wait( 10, 200, function() { + // Hit it 5 times in 1 sec intervals + wait( 5, 1000 ); + }); + }); + } + } + + // Reset the stored size + editor.on( 'show', function() { + oldSize = 0; + }); + + // Register the command + editor.addCommand( 'wpAutoResize', resize ); + + // On/off + editor.addCommand( 'wpAutoResizeOn', on ); + editor.addCommand( 'wpAutoResizeOff', off ); +}); diff --git a/wp-includes/js/tinymce/plugins/wpautoresize/plugin.min.js b/wp-includes/js/tinymce/plugins/wpautoresize/plugin.min.js new file mode 100644 index 0000000..a63aa88 --- /dev/null +++ b/wp-includes/js/tinymce/plugins/wpautoresize/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("wpautoresize",function(a){function b(){return a.plugins.fullscreen&&a.plugins.fullscreen.isFullscreen()}function c(a){return parseInt(a,10)||0}function d(e){var f,g,k,l,m,n,o,p,q,r,s,t,u=tinymce.DOM;if(j&&(g=a.getDoc())){if(e=e||{},k=g.body,l=g.documentElement,m=h.autoresize_min_height,!k||e&&"setcontent"===e.type&&e.initial||b())return void(k&&l&&(k.style.overflowY="auto",l.style.overflowY="auto"));o=a.dom.getStyle(k,"margin-top",!0),p=a.dom.getStyle(k,"margin-bottom",!0),q=a.dom.getStyle(k,"padding-top",!0),r=a.dom.getStyle(k,"padding-bottom",!0),s=a.dom.getStyle(k,"border-top-width",!0),t=a.dom.getStyle(k,"border-bottom-width",!0),n=k.offsetHeight+c(o)+c(p)+c(q)+c(r)+c(s)+c(t),n&&n=n)&&(n=tinymce.Env.ie?k.scrollHeight:tinymce.Env.webkit&&0===k.clientHeight?0:k.offsetHeight),n>h.autoresize_min_height&&(m=n),h.autoresize_max_height&&n>h.autoresize_max_height?(m=h.autoresize_max_height,k.style.overflowY="auto",l.style.overflowY="auto"):(k.style.overflowY="hidden",l.style.overflowY="hidden",k.scrollTop=0),m!==i&&(f=m-i,u.setStyle(a.iframeElement,"height",m+"px"),i=m,tinymce.isWebKit&&0>f&&d(e),a.fire("wp-autoresize",{height:m,deltaHeight:"nodechange"===e.type?f:null}))}}function e(a,b,c){setTimeout(function(){d(),a--?e(a,b,c):c&&c()},b)}function f(){a.dom.hasClass(a.getBody(),"wp-autoresize")||(j=!0,a.dom.addClass(a.getBody(),"wp-autoresize"),a.on("nodechange setcontent keyup FullscreenStateChanged",d),d())}function g(){var b;h.wp_autoresize_on||(j=!1,b=a.getDoc(),a.dom.removeClass(a.getBody(),"wp-autoresize"),a.off("nodechange setcontent keyup FullscreenStateChanged",d),b.body.style.overflowY="auto",b.documentElement.style.overflowY="auto",i=0)}var h=a.settings,i=300,j=!1;a.settings.inline||(h.autoresize_min_height=parseInt(a.getParam("autoresize_min_height",a.getElement().offsetHeight),10),h.autoresize_max_height=parseInt(a.getParam("autoresize_max_height",0),10),h.wp_autoresize_on&&(j=!0,a.on("init",function(){a.dom.addClass(a.getBody(),"wp-autoresize")}),a.on("nodechange keyup FullscreenStateChanged",d),a.on("setcontent",function(){e(3,100)}),a.getParam("autoresize_on_init",!0)&&a.on("init",function(){e(10,200,function(){e(5,1e3)})})),a.on("show",function(){i=0}),a.addCommand("wpAutoResize",d),a.addCommand("wpAutoResizeOn",f),a.addCommand("wpAutoResizeOff",g))}); \ No newline at end of file diff --git a/wp-includes/js/tinymce/plugins/wpdialogs/plugin.js b/wp-includes/js/tinymce/plugins/wpdialogs/plugin.js new file mode 100644 index 0000000..8b9229f --- /dev/null +++ b/wp-includes/js/tinymce/plugins/wpdialogs/plugin.js @@ -0,0 +1,92 @@ +/* global tinymce */ +/** + * Included for back-compat. + * The default WindowManager in TinyMCE 4.0 supports three types of dialogs: + * - With HTML created from JS. + * - With inline HTML (like WPWindowManager). + * - Old type iframe based dialogs. + * For examples see the default plugins: https://github.com/tinymce/tinymce/tree/master/js/tinymce/plugins + */ +tinymce.WPWindowManager = tinymce.InlineWindowManager = function( editor ) { + if ( this.wp ) { + return this; + } + + this.wp = {}; + this.parent = editor.windowManager; + this.editor = editor; + + tinymce.extend( this, this.parent ); + + this.open = function( args, params ) { + var $element, + self = this, + wp = this.wp; + + if ( ! args.wpDialog ) { + return this.parent.open.apply( this, arguments ); + } else if ( ! args.id ) { + return; + } + + if ( typeof jQuery === 'undefined' || ! jQuery.wp || ! jQuery.wp.wpdialog ) { + // wpdialog.js is not loaded + if ( window.console && window.console.error ) { + window.console.error('wpdialog.js is not loaded. Please set "wpdialogs" as dependency for your script when calling wp_enqueue_script(). You may also want to enqueue the "wp-jquery-ui-dialog" stylesheet.'); + } + + return; + } + + wp.$element = $element = jQuery( '#' + args.id ); + + if ( ! $element.length ) { + return; + } + + if ( window.console && window.console.log ) { + window.console.log('tinymce.WPWindowManager is deprecated. Use the default editor.windowManager to open dialogs with inline HTML.'); + } + + wp.features = args; + wp.params = params; + + // Store selection. Takes a snapshot in the FocusManager of the selection before focus is moved to the dialog. + editor.nodeChanged(); + + // Create the dialog if necessary + if ( ! $element.data('wpdialog') ) { + $element.wpdialog({ + title: args.title, + width: args.width, + height: args.height, + modal: true, + dialogClass: 'wp-dialog', + zIndex: 300000 + }); + } + + $element.wpdialog('open'); + + $element.on( 'wpdialogclose', function() { + if ( self.wp.$element ) { + self.wp = {}; + } + }); + }; + + this.close = function() { + if ( ! this.wp.features || ! this.wp.features.wpDialog ) { + return this.parent.close.apply( this, arguments ); + } + + this.wp.$element.wpdialog('close'); + }; +}; + +tinymce.PluginManager.add( 'wpdialogs', function( editor ) { + // Replace window manager + editor.on( 'init', function() { + editor.windowManager = new tinymce.WPWindowManager( editor ); + }); +}); diff --git a/wp-includes/js/tinymce/plugins/wpdialogs/plugin.min.js b/wp-includes/js/tinymce/plugins/wpdialogs/plugin.min.js new file mode 100644 index 0000000..12b1df0 --- /dev/null +++ b/wp-includes/js/tinymce/plugins/wpdialogs/plugin.min.js @@ -0,0 +1 @@ +tinymce.WPWindowManager=tinymce.InlineWindowManager=function(a){return this.wp?this:(this.wp={},this.parent=a.windowManager,this.editor=a,tinymce.extend(this,this.parent),this.open=function(b,c){var d,e=this,f=this.wp;if(!b.wpDialog)return this.parent.open.apply(this,arguments);if(b.id){if("undefined"==typeof jQuery||!jQuery.wp||!jQuery.wp.wpdialog)return void(window.console&&window.console.error&&window.console.error('wpdialog.js is not loaded. Please set "wpdialogs" as dependency for your script when calling wp_enqueue_script(). You may also want to enqueue the "wp-jquery-ui-dialog" stylesheet.'));f.$element=d=jQuery("#"+b.id),d.length&&(window.console&&window.console.log&&window.console.log("tinymce.WPWindowManager is deprecated. Use the default editor.windowManager to open dialogs with inline HTML."),f.features=b,f.params=c,a.nodeChanged(),d.data("wpdialog")||d.wpdialog({title:b.title,width:b.width,height:b.height,modal:!0,dialogClass:"wp-dialog",zIndex:3e5}),d.wpdialog("open"),d.on("wpdialogclose",function(){e.wp.$element&&(e.wp={})}))}},void(this.close=function(){return this.wp.features&&this.wp.features.wpDialog?void this.wp.$element.wpdialog("close"):this.parent.close.apply(this,arguments)}))},tinymce.PluginManager.add("wpdialogs",function(a){a.on("init",function(){a.windowManager=new tinymce.WPWindowManager(a)})}); \ No newline at end of file diff --git a/wp-includes/js/tinymce/plugins/wpeditimage/plugin.js b/wp-includes/js/tinymce/plugins/wpeditimage/plugin.js new file mode 100644 index 0000000..acda799 --- /dev/null +++ b/wp-includes/js/tinymce/plugins/wpeditimage/plugin.js @@ -0,0 +1,1227 @@ +/* global tinymce */ +tinymce.PluginManager.add( 'wpeditimage', function( editor ) { + var floatingToolbar, serializer, + DOM = tinymce.DOM, + settings = editor.settings, + Factory = tinymce.ui.Factory, + each = tinymce.each, + iOS = tinymce.Env.iOS, + toolbarIsHidden = true, + editorWrapParent = tinymce.$( '#postdivrich' ); + + function isPlaceholder( node ) { + return !! ( editor.dom.getAttrib( node, 'data-mce-placeholder' ) || editor.dom.getAttrib( node, 'data-mce-object' ) ); + } + + editor.addButton( 'wp_img_remove', { + tooltip: 'Remove', + icon: 'dashicon dashicons-no', + onclick: function() { + removeImage( editor.selection.getNode() ); + } + } ); + + editor.addButton( 'wp_img_edit', { + tooltip: 'Edit ', // trailing space is needed, used for context + icon: 'dashicon dashicons-edit', + onclick: function() { + editImage( editor.selection.getNode() ); + } + } ); + + each( { + alignleft: 'Align left', + aligncenter: 'Align center', + alignright: 'Align right', + alignnone: 'No alignment' + }, function( tooltip, name ) { + var direction = name.slice( 5 ); + + editor.addButton( 'wp_img_' + name, { + tooltip: tooltip, + icon: 'dashicon dashicons-align-' + direction, + cmd: 'alignnone' === name ? 'wpAlignNone' : 'Justify' + direction.slice( 0, 1 ).toUpperCase() + direction.slice( 1 ), + onPostRender: function() { + var self = this; + + editor.on( 'NodeChange', function( event ) { + var node; + + // Don't bother. + if ( event.element.nodeName !== 'IMG' ) { + return; + } + + node = editor.dom.getParent( event.element, '.wp-caption' ) || event.element; + + if ( 'alignnone' === name ) { + self.active( ! /\balign(left|center|right)\b/.test( node.className ) ); + } else { + self.active( editor.dom.hasClass( node, name ) ); + } + } ); + } + } ); + } ); + + function toolbarConfig() { + var toolbarItems = [], + buttonGroup; + + each( [ 'wp_img_alignleft', 'wp_img_aligncenter', 'wp_img_alignright', 'wp_img_alignnone', 'wp_img_edit', 'wp_img_remove' ], function( item ) { + var itemName; + + function bindSelectorChanged() { + var selection = editor.selection; + + if ( item.settings.stateSelector ) { + selection.selectorChanged( item.settings.stateSelector, function( state ) { + item.active( state ); + }, true ); + } + + if ( item.settings.disabledStateSelector ) { + selection.selectorChanged( item.settings.disabledStateSelector, function( state ) { + item.disabled( state ); + } ); + } + } + + if ( item === '|' ) { + buttonGroup = null; + } else { + if ( Factory.has( item ) ) { + item = { + type: item + }; + + if ( settings.toolbar_items_size ) { + item.size = settings.toolbar_items_size; + } + + toolbarItems.push( item ); + + buttonGroup = null; + } else { + if ( ! buttonGroup ) { + buttonGroup = { + type: 'buttongroup', + items: [] + }; + + toolbarItems.push( buttonGroup ); + } + + if ( editor.buttons[ item ] ) { + itemName = item; + item = editor.buttons[ itemName ]; + + if ( typeof item === 'function' ) { + item = item(); + } + + item.type = item.type || 'button'; + + if ( settings.toolbar_items_size ) { + item.size = settings.toolbar_items_size; + } + + item = Factory.create( item ); + buttonGroup.items.push( item ); + + if ( editor.initialized ) { + bindSelectorChanged(); + } else { + editor.on( 'init', bindSelectorChanged ); + } + } + } + } + } ); + + return { + type: 'panel', + layout: 'stack', + classes: 'toolbar-grp inline-toolbar-grp wp-image-toolbar', + ariaRoot: true, + ariaRemember: true, + items: [ + { + type: 'toolbar', + layout: 'flow', + items: toolbarItems + } + ] + }; + } + + floatingToolbar = Factory.create( toolbarConfig() ).renderTo( document.body ).hide(); + + floatingToolbar.reposition = function() { + var top, left, minTop, className, + windowPos, adminbar, mceToolbar, boundary, + boundaryMiddle, boundaryVerticalMiddle, spaceTop, + spaceBottom, windowWidth, toolbarWidth, toolbarHalf, + iframe, iframePos, iframeWidth, iframeHeigth, + toolbarNodeHeight, verticalSpaceNeeded, + toolbarNode = this.getEl(), + buffer = 5, + margin = 8, + adminbarHeight = 0, + imageNode = editor.selection.getNode(); + + if ( ! imageNode || imageNode.nodeName !== 'IMG' ) { + return this; + } + + windowPos = window.pageYOffset || document.documentElement.scrollTop; + adminbar = tinymce.$( '#wpadminbar' )[0]; + mceToolbar = tinymce.$( '.mce-tinymce .mce-toolbar-grp' )[0]; + boundary = imageNode.getBoundingClientRect(); + boundaryMiddle = ( boundary.left + boundary.right ) / 2; + boundaryVerticalMiddle = ( boundary.top + boundary.bottom ) / 2; + spaceTop = boundary.top; + spaceBottom = iframeHeigth - boundary.bottom; + windowWidth = window.innerWidth; + toolbarWidth = toolbarNode.offsetWidth; + toolbarHalf = toolbarWidth / 2; + iframe = document.getElementById( editor.id + '_ifr' ); + iframePos = DOM.getPos( iframe ); + iframeWidth = iframe.offsetWidth; + iframeHeigth = iframe.offsetHeight; + toolbarNodeHeight = toolbarNode.offsetHeight; + verticalSpaceNeeded = toolbarNodeHeight + margin + buffer; + + if ( iOS ) { + top = boundary.top + iframePos.y + margin; + } else { + if ( spaceTop >= verticalSpaceNeeded ) { + className = ' mce-arrow-down'; + top = boundary.top + iframePos.y - toolbarNodeHeight - margin; + } else if ( spaceBottom >= verticalSpaceNeeded ) { + className = ' mce-arrow-up'; + top = boundary.bottom + iframePos.y; + } else { + top = buffer; + + if ( boundaryVerticalMiddle >= verticalSpaceNeeded ) { + className = ' mce-arrow-down'; + } else { + className = ' mce-arrow-up'; + } + } + } + + // Make sure the image toolbar is below the main toolbar. + if ( mceToolbar ) { + minTop = DOM.getPos( mceToolbar ).y + mceToolbar.clientHeight; + } else { + minTop = iframePos.y; + } + + // Make sure the image toolbar is below the adminbar (if visible) or below the top of the window. + if ( windowPos ) { + if ( adminbar && adminbar.getBoundingClientRect().top === 0 ) { + adminbarHeight = adminbar.clientHeight; + } + + if ( windowPos + adminbarHeight > minTop ) { + minTop = windowPos + adminbarHeight; + } + } + + if ( top && minTop && ( minTop + buffer > top ) ) { + top = minTop + buffer; + className = ''; + } + + left = boundaryMiddle - toolbarHalf; + left += iframePos.x; + + if ( boundary.left < 0 || boundary.right > iframeWidth ) { + left = iframePos.x + ( iframeWidth - toolbarWidth ) / 2; + } else if ( toolbarWidth >= windowWidth ) { + className += ' mce-arrow-full'; + left = 0; + } else if ( ( left < 0 && boundary.left + toolbarWidth > windowWidth ) || + ( left + toolbarWidth > windowWidth && boundary.right - toolbarWidth < 0 ) ) { + + left = ( windowWidth - toolbarWidth ) / 2; + } else if ( left < iframePos.x ) { + className += ' mce-arrow-left'; + left = boundary.left + iframePos.x; + } else if ( left + toolbarWidth > iframeWidth + iframePos.x ) { + className += ' mce-arrow-right'; + left = boundary.right - toolbarWidth + iframePos.x; + } + + if ( ! iOS ) { + toolbarNode.className = toolbarNode.className.replace( / ?mce-arrow-[\w]+/g, '' ); + toolbarNode.className += className; + } + + DOM.setStyles( toolbarNode, { 'left': left, 'top': top } ); + + return this; + }; + + if ( iOS ) { + // Safari on iOS fails to select image nodes in contentEditoble mode on touch/click. + // Select them again. + editor.on( 'click', function( event ) { + if ( event.target.nodeName === 'IMG' ) { + var node = event.target; + + window.setTimeout( function() { + editor.selection.select( node ); + }, 200 ); + } else { + floatingToolbar.hide(); + } + }); + } + + editor.on( 'nodechange', function( event ) { + var delay = iOS ? 350 : 100; + + if ( event.element.nodeName !== 'IMG' || isPlaceholder( event.element ) ) { + floatingToolbar.hide(); + return; + } + + setTimeout( function() { + var element = editor.selection.getNode(); + + if ( element.nodeName === 'IMG' && ! isPlaceholder( element ) ) { + if ( floatingToolbar._visible ) { + floatingToolbar.reposition(); + } else { + floatingToolbar.show(); + } + } else { + floatingToolbar.hide(); + } + }, delay ); + } ); + + function hide() { + if ( ! toolbarIsHidden ) { + floatingToolbar.hide(); + } + } + + floatingToolbar.on( 'show', function() { + toolbarIsHidden = false; + + if ( this._visible ) { + this.reposition(); + DOM.addClass( this.getEl(), 'mce-inline-toolbar-grp-active' ); + } + } ); + + floatingToolbar.on( 'hide', function() { + toolbarIsHidden = true; + DOM.removeClass( this.getEl(), 'mce-inline-toolbar-grp-active' ); + } ); + + floatingToolbar.on( 'keydown', function( event ) { + if ( event.keyCode === 27 ) { + hide(); + editor.focus(); + } + } ); + + DOM.bind( window, 'resize scroll', function() { + if ( ! toolbarIsHidden && editorWrapParent.hasClass( 'wp-editor-expand' ) ) { + hide(); + } + }); + + editor.on( 'init', function() { + editor.dom.bind( editor.getWin(), 'scroll', hide ); + }); + + editor.on( 'blur hide', hide ); + + // 119 = F8 + editor.shortcuts.add( 'Alt+119', '', function() { + var node = floatingToolbar.find( 'toolbar' )[0]; + + if ( node ) { + node.focus( true ); + } + }); + + function parseShortcode( content ) { + return content.replace( /(?:

        )?\[(?:wp_)?caption([^\]]+)\]([\s\S]+?)\[\/(?:wp_)?caption\](?:<\/p>)?/g, function( a, b, c ) { + var id, align, classes, caption, img, width, + trim = tinymce.trim; + + id = b.match( /id=['"]([^'"]*)['"] ?/ ); + if ( id ) { + b = b.replace( id[0], '' ); + } + + align = b.match( /align=['"]([^'"]*)['"] ?/ ); + if ( align ) { + b = b.replace( align[0], '' ); + } + + classes = b.match( /class=['"]([^'"]*)['"] ?/ ); + if ( classes ) { + b = b.replace( classes[0], '' ); + } + + width = b.match( /width=['"]([0-9]*)['"] ?/ ); + if ( width ) { + b = b.replace( width[0], '' ); + } + + c = trim( c ); + img = c.match( /((?:]+>)?]+>(?:<\/a>)?)([\s\S]*)/i ); + + if ( img && img[2] ) { + caption = trim( img[2] ); + img = trim( img[1] ); + } else { + // old captions shortcode style + caption = trim( b ).replace( /caption=['"]/, '' ).replace( /['"]$/, '' ); + img = c; + } + + id = ( id && id[1] ) ? id[1].replace( /[<>&]+/g, '' ) : ''; + align = ( align && align[1] ) ? align[1] : 'alignnone'; + classes = ( classes && classes[1] ) ? ' ' + classes[1].replace( /[<>&]+/g, '' ) : ''; + + if ( ! width && img ) { + width = img.match( /width=['"]([0-9]*)['"]/ ); + } + + if ( width && width[1] ) { + width = width[1]; + } + + if ( ! width || ! caption ) { + return c; + } + + width = parseInt( width, 10 ); + if ( ! editor.getParam( 'wpeditimage_html5_captions' ) ) { + width += 10; + } + + return '

        '; + }); + } + + function getShortcode( content ) { + return content.replace( /
        ]*>([\s\S]+?)<\/div>/g, function( a, b ) { + var out = ''; + + if ( b.indexOf(']+>([\s\S]+?)<\/dd>/i ); + + if ( out && out[1] ) { + return '

        ' + out[1] + '

        '; + } + + return ''; + } + + out = b.replace( /\s*
        ]+)>\s*
        ]+>([\s\S]+?)<\/dt>\s*
        ]+>([\s\S]*?)<\/dd>\s*<\/dl>\s*/gi, function( a, b, c, caption ) { + var id, classes, align, width; + + width = c.match( /width="([0-9]*)"/ ); + width = ( width && width[1] ) ? width[1] : ''; + + if ( ! width || ! caption ) { + return c; + } + + id = b.match( /id="([^"]*)"/ ); + id = ( id && id[1] ) ? id[1] : ''; + + classes = b.match( /class="([^"]*)"/ ); + classes = ( classes && classes[1] ) ? classes[1] : ''; + + align = classes.match( /align[a-z]+/i ) || 'alignnone'; + classes = classes.replace( /wp-caption ?|align[a-z]+ ?/gi, '' ); + + if ( classes ) { + classes = ' class="' + classes + '"'; + } + + caption = caption.replace( /\r\n|\r/g, '\n' ).replace( /<[a-zA-Z0-9]+( [^<>]+)?>/g, function( a ) { + // no line breaks inside HTML tags + return a.replace( /[\r\n\t]+/, ' ' ); + }); + + // convert remaining line breaks to
        + caption = caption.replace( /\s*\n\s*/g, '
        ' ); + + return '[caption id="' + id + '" align="' + align + '" width="' + width + '"' + classes + ']' + c + ' ' + caption + '[/caption]'; + }); + + if ( out.indexOf('[caption') === -1 ) { + // the caption html seems broken, try to find the image that may be wrapped in a link + // and may be followed by

        with the caption text. + out = b.replace( /[\s\S]*?((?:]+>)?]+>(?:<\/a>)?)(

        [\s\S]*<\/p>)?[\s\S]*/gi, '

        $1

        $2' ); + } + + return out; + }); + } + + function extractImageData( imageNode ) { + var classes, extraClasses, metadata, captionBlock, caption, link, width, height, + captionClassName = [], + dom = editor.dom, + isIntRegExp = /^\d+$/; + + // default attributes + metadata = { + attachment_id: false, + size: 'custom', + caption: '', + align: 'none', + extraClasses: '', + link: false, + linkUrl: '', + linkClassName: '', + linkTargetBlank: false, + linkRel: '', + title: '' + }; + + metadata.url = dom.getAttrib( imageNode, 'src' ); + metadata.alt = dom.getAttrib( imageNode, 'alt' ); + metadata.title = dom.getAttrib( imageNode, 'title' ); + + width = dom.getAttrib( imageNode, 'width' ); + height = dom.getAttrib( imageNode, 'height' ); + + if ( ! isIntRegExp.test( width ) || parseInt( width, 10 ) < 1 ) { + width = imageNode.naturalWidth || imageNode.width; + } + + if ( ! isIntRegExp.test( height ) || parseInt( height, 10 ) < 1 ) { + height = imageNode.naturalHeight || imageNode.height; + } + + metadata.customWidth = metadata.width = width; + metadata.customHeight = metadata.height = height; + + classes = tinymce.explode( imageNode.className, ' ' ); + extraClasses = []; + + tinymce.each( classes, function( name ) { + + if ( /^wp-image/.test( name ) ) { + metadata.attachment_id = parseInt( name.replace( 'wp-image-', '' ), 10 ); + } else if ( /^align/.test( name ) ) { + metadata.align = name.replace( 'align', '' ); + } else if ( /^size/.test( name ) ) { + metadata.size = name.replace( 'size-', '' ); + } else { + extraClasses.push( name ); + } + + } ); + + metadata.extraClasses = extraClasses.join( ' ' ); + + // Extract caption + captionBlock = dom.getParents( imageNode, '.wp-caption' ); + + if ( captionBlock.length ) { + captionBlock = captionBlock[0]; + + classes = captionBlock.className.split( ' ' ); + tinymce.each( classes, function( name ) { + if ( /^align/.test( name ) ) { + metadata.align = name.replace( 'align', '' ); + } else if ( name && name !== 'wp-caption' ) { + captionClassName.push( name ); + } + } ); + + metadata.captionClassName = captionClassName.join( ' ' ); + + caption = dom.select( 'dd.wp-caption-dd', captionBlock ); + if ( caption.length ) { + caption = caption[0]; + + metadata.caption = editor.serializer.serialize( caption ) + .replace( /]*>/g, '$&\n' ).replace( /^

        /, '' ).replace( /<\/p>$/, '' ); + } + } + + // Extract linkTo + if ( imageNode.parentNode && imageNode.parentNode.nodeName === 'A' ) { + link = imageNode.parentNode; + metadata.linkUrl = dom.getAttrib( link, 'href' ); + metadata.linkTargetBlank = dom.getAttrib( link, 'target' ) === '_blank' ? true : false; + metadata.linkRel = dom.getAttrib( link, 'rel' ); + metadata.linkClassName = link.className; + } + + return metadata; + } + + function hasTextContent( node ) { + return node && !! ( node.textContent || node.innerText ); + } + + // Verify HTML in captions + function verifyHTML( caption ) { + if ( ! caption || ( caption.indexOf( '<' ) === -1 && caption.indexOf( '>' ) === -1 ) ) { + return caption; + } + + if ( ! serializer ) { + serializer = new tinymce.html.Serializer( {}, editor.schema ); + } + + return serializer.serialize( editor.parser.parse( caption, { forced_root_block: false } ) ); + } + + function updateImage( imageNode, imageData ) { + var classes, className, node, html, parent, wrap, linkNode, + captionNode, dd, dl, id, attrs, linkAttrs, width, height, align, + dom = editor.dom; + + classes = tinymce.explode( imageData.extraClasses, ' ' ); + + if ( ! classes ) { + classes = []; + } + + if ( ! imageData.caption ) { + classes.push( 'align' + imageData.align ); + } + + if ( imageData.attachment_id ) { + classes.push( 'wp-image-' + imageData.attachment_id ); + if ( imageData.size && imageData.size !== 'custom' ) { + classes.push( 'size-' + imageData.size ); + } + } + + width = imageData.width; + height = imageData.height; + + if ( imageData.size === 'custom' ) { + width = imageData.customWidth; + height = imageData.customHeight; + } + + attrs = { + src: imageData.url, + width: width || null, + height: height || null, + alt: imageData.alt, + title: imageData.title || null, + 'class': classes.join( ' ' ) || null + }; + + dom.setAttribs( imageNode, attrs ); + + linkAttrs = { + href: imageData.linkUrl, + rel: imageData.linkRel || null, + target: imageData.linkTargetBlank ? '_blank': null, + 'class': imageData.linkClassName || null + }; + + if ( imageNode.parentNode && imageNode.parentNode.nodeName === 'A' && ! hasTextContent( imageNode.parentNode ) ) { + // Update or remove an existing link wrapped around the image + if ( imageData.linkUrl ) { + dom.setAttribs( imageNode.parentNode, linkAttrs ); + } else { + dom.remove( imageNode.parentNode, true ); + } + } else if ( imageData.linkUrl ) { + if ( linkNode = dom.getParent( imageNode, 'a' ) ) { + // The image is inside a link together with other nodes, + // or is nested in another node, move it out + dom.insertAfter( imageNode, linkNode ); + } + + // Add link wrapped around the image + linkNode = dom.create( 'a', linkAttrs ); + imageNode.parentNode.insertBefore( linkNode, imageNode ); + linkNode.appendChild( imageNode ); + } + + captionNode = editor.dom.getParent( imageNode, '.mceTemp' ); + + if ( imageNode.parentNode && imageNode.parentNode.nodeName === 'A' && ! hasTextContent( imageNode.parentNode ) ) { + node = imageNode.parentNode; + } else { + node = imageNode; + } + + if ( imageData.caption ) { + imageData.caption = verifyHTML( imageData.caption ); + + id = imageData.attachment_id ? 'attachment_' + imageData.attachment_id : null; + align = 'align' + ( imageData.align || 'none' ); + className = 'wp-caption ' + align; + + if ( imageData.captionClassName ) { + className += ' ' + imageData.captionClassName.replace( /[<>&]+/g, '' ); + } + + if ( ! editor.getParam( 'wpeditimage_html5_captions' ) ) { + width = parseInt( width, 10 ); + width += 10; + } + + if ( captionNode ) { + dl = dom.select( 'dl.wp-caption', captionNode ); + + if ( dl.length ) { + dom.setAttribs( dl, { + id: id, + 'class': className, + style: 'width: ' + width + 'px' + } ); + } + + dd = dom.select( '.wp-caption-dd', captionNode ); + + if ( dd.length ) { + dom.setHTML( dd[0], imageData.caption ); + } + + } else { + id = id ? 'id="'+ id +'" ' : ''; + + // should create a new function for generating the caption markup + html = '

        ' + + '
        '+ imageData.caption +'
        '; + + wrap = dom.create( 'div', { 'class': 'mceTemp' }, html ); + + if ( parent = dom.getParent( node, 'p' ) ) { + parent.parentNode.insertBefore( wrap, parent ); + + if ( dom.isEmpty( parent ) ) { + dom.remove( parent ); + } + } else { + node.parentNode.insertBefore( wrap, node ); + } + + editor.$( wrap ).find( 'dt.wp-caption-dt' ).append( node ); + } + } else if ( captionNode ) { + // Remove the caption wrapper and place the image in new paragraph + parent = dom.create( 'p' ); + captionNode.parentNode.insertBefore( parent, captionNode ); + parent.appendChild( node ); + dom.remove( captionNode ); + } + + if ( wp.media.events ) { + wp.media.events.trigger( 'editor:image-update', { + editor: editor, + metadata: imageData, + image: imageNode + } ); + } + + editor.nodeChanged(); + } + + function editImage( img ) { + var frame, callback, metadata; + + if ( typeof wp === 'undefined' || ! wp.media ) { + editor.execCommand( 'mceImage' ); + return; + } + + metadata = extractImageData( img ); + + // Manipulate the metadata by reference that is fed into the PostImage model used in the media modal + wp.media.events.trigger( 'editor:image-edit', { + editor: editor, + metadata: metadata, + image: img + } ); + + frame = wp.media({ + frame: 'image', + state: 'image-details', + metadata: metadata + } ); + + wp.media.events.trigger( 'editor:frame-create', { frame: frame } ); + + callback = function( imageData ) { + editor.focus(); + editor.undoManager.transact( function() { + updateImage( img, imageData ); + } ); + frame.detach(); + }; + + frame.state('image-details').on( 'update', callback ); + frame.state('replace-image').on( 'replace', callback ); + frame.on( 'close', function() { + editor.focus(); + frame.detach(); + }); + + frame.open(); + } + + function removeImage( node ) { + var wrap; + + if ( node.nodeName === 'DIV' && editor.dom.hasClass( node, 'mceTemp' ) ) { + wrap = node; + } else if ( node.nodeName === 'IMG' || node.nodeName === 'DT' || node.nodeName === 'A' ) { + wrap = editor.dom.getParent( node, 'div.mceTemp' ); + } + + if ( wrap ) { + if ( wrap.nextSibling ) { + editor.selection.select( wrap.nextSibling ); + } else if ( wrap.previousSibling ) { + editor.selection.select( wrap.previousSibling ); + } else { + editor.selection.select( wrap.parentNode ); + } + + editor.selection.collapse( true ); + editor.dom.remove( wrap ); + } else { + editor.dom.remove( node ); + } + + editor.nodeChanged(); + editor.undoManager.add(); + } + + editor.on( 'init', function() { + var dom = editor.dom, + captionClass = editor.getParam( 'wpeditimage_html5_captions' ) ? 'html5-captions' : 'html4-captions'; + + dom.addClass( editor.getBody(), captionClass ); + + // Add caption field to the default image dialog + editor.on( 'wpLoadImageForm', function( event ) { + if ( editor.getParam( 'wpeditimage_disable_captions' ) ) { + return; + } + + var captionField = { + type: 'textbox', + flex: 1, + name: 'caption', + minHeight: 60, + multiline: true, + scroll: true, + label: 'Image caption' + }; + + event.data.splice( event.data.length - 1, 0, captionField ); + }); + + // Fix caption parent width for images added from URL + editor.on( 'wpNewImageRefresh', function( event ) { + var parent, captionWidth; + + if ( parent = dom.getParent( event.node, 'dl.wp-caption' ) ) { + if ( ! parent.style.width ) { + captionWidth = parseInt( event.node.clientWidth, 10 ) + 10; + captionWidth = captionWidth ? captionWidth + 'px' : '50%'; + dom.setStyle( parent, 'width', captionWidth ); + } + } + }); + + editor.on( 'wpImageFormSubmit', function( event ) { + var data = event.imgData.data, + imgNode = event.imgData.node, + caption = event.imgData.caption, + captionId = '', + captionAlign = '', + captionWidth = '', + wrap, parent, node, html, imgId; + + // Temp image id so we can find the node later + data.id = '__wp-temp-img-id'; + // Cancel the original callback + event.imgData.cancel = true; + + if ( ! data.style ) { + data.style = null; + } + + if ( ! data.src ) { + // Delete the image and the caption + if ( imgNode ) { + if ( wrap = dom.getParent( imgNode, 'div.mceTemp' ) ) { + dom.remove( wrap ); + } else if ( imgNode.parentNode.nodeName === 'A' ) { + dom.remove( imgNode.parentNode ); + } else { + dom.remove( imgNode ); + } + + editor.nodeChanged(); + } + return; + } + + if ( caption ) { + caption = caption.replace( /\r\n|\r/g, '\n' ).replace( /<\/?[a-zA-Z0-9]+( [^<>]+)?>/g, function( a ) { + // No line breaks inside HTML tags + return a.replace( /[\r\n\t]+/, ' ' ); + }); + + // Convert remaining line breaks to
        + caption = caption.replace( /(]*>)\s*\n\s*/g, '$1' ).replace( /\s*\n\s*/g, '
        ' ); + caption = verifyHTML( caption ); + } + + if ( ! imgNode ) { + // New image inserted + html = dom.createHTML( 'img', data ); + + if ( caption ) { + node = editor.selection.getNode(); + + if ( data.width ) { + captionWidth = parseInt( data.width, 10 ); + + if ( ! editor.getParam( 'wpeditimage_html5_captions' ) ) { + captionWidth += 10; + } + + captionWidth = ' style="width: ' + captionWidth + 'px"'; + } + + html = '
        ' + + '
        '+ html +'
        '+ caption +'
        '; + + if ( node.nodeName === 'P' ) { + parent = node; + } else { + parent = dom.getParent( node, 'p' ); + } + + if ( parent && parent.nodeName === 'P' ) { + wrap = dom.create( 'div', { 'class': 'mceTemp' }, html ); + parent.parentNode.insertBefore( wrap, parent ); + editor.selection.select( wrap ); + editor.nodeChanged(); + + if ( dom.isEmpty( parent ) ) { + dom.remove( parent ); + } + } else { + editor.selection.setContent( '
        ' + html + '
        ' ); + } + } else { + editor.selection.setContent( html ); + } + } else { + // Edit existing image + + // Store the original image id if any + imgId = imgNode.id || null; + // Update the image node + dom.setAttribs( imgNode, data ); + wrap = dom.getParent( imgNode, 'dl.wp-caption' ); + + if ( caption ) { + if ( wrap ) { + if ( parent = dom.select( 'dd.wp-caption-dd', wrap )[0] ) { + parent.innerHTML = caption; + } + } else { + if ( imgNode.className ) { + captionId = imgNode.className.match( /wp-image-([0-9]+)/ ); + captionAlign = imgNode.className.match( /align(left|right|center|none)/ ); + } + + if ( captionAlign ) { + captionAlign = captionAlign[0]; + imgNode.className = imgNode.className.replace( /align(left|right|center|none)/g, '' ); + } else { + captionAlign = 'alignnone'; + } + + captionAlign = ' class="wp-caption ' + captionAlign + '"'; + + if ( captionId ) { + captionId = ' id="attachment_' + captionId[1] + '"'; + } + + captionWidth = data.width || imgNode.clientWidth; + + if ( captionWidth ) { + captionWidth = parseInt( captionWidth, 10 ); + + if ( ! editor.getParam( 'wpeditimage_html5_captions' ) ) { + captionWidth += 10; + } + + captionWidth = ' style="width: '+ captionWidth +'px"'; + } + + if ( imgNode.parentNode && imgNode.parentNode.nodeName === 'A' ) { + node = imgNode.parentNode; + } else { + node = imgNode; + } + + html = '
        ' + + '
        '+ caption +'
        '; + + wrap = dom.create( 'div', { 'class': 'mceTemp' }, html ); + + if ( parent = dom.getParent( node, 'p' ) ) { + parent.parentNode.insertBefore( wrap, parent ); + + if ( dom.isEmpty( parent ) ) { + dom.remove( parent ); + } + } else { + node.parentNode.insertBefore( wrap, node ); + } + + editor.$( wrap ).find( 'dt.wp-caption-dt' ).append( node ); + } + } else { + if ( wrap ) { + // Remove the caption wrapper and place the image in new paragraph + if ( imgNode.parentNode.nodeName === 'A' ) { + html = dom.getOuterHTML( imgNode.parentNode ); + } else { + html = dom.getOuterHTML( imgNode ); + } + + parent = dom.create( 'p', {}, html ); + dom.insertAfter( parent, wrap.parentNode ); + editor.selection.select( parent ); + editor.nodeChanged(); + dom.remove( wrap.parentNode ); + } + } + } + + imgNode = dom.get('__wp-temp-img-id'); + dom.setAttrib( imgNode, 'id', imgId ); + event.imgData.node = imgNode; + }); + + editor.on( 'wpLoadImageData', function( event ) { + var parent, + data = event.imgData.data, + imgNode = event.imgData.node; + + if ( parent = dom.getParent( imgNode, 'dl.wp-caption' ) ) { + parent = dom.select( 'dd.wp-caption-dd', parent )[0]; + + if ( parent ) { + data.caption = editor.serializer.serialize( parent ) + .replace( /]*>/g, '$&\n' ).replace( /^

        /, '' ).replace( /<\/p>$/, '' ); + } + } + }); + + dom.bind( editor.getDoc(), 'dragstart', function( event ) { + var node = editor.selection.getNode(); + + // Prevent dragging images out of the caption elements + if ( node.nodeName === 'IMG' && dom.getParent( node, '.wp-caption' ) ) { + event.preventDefault(); + } + }); + + // Prevent IE11 from making dl.wp-caption resizable + if ( tinymce.Env.ie && tinymce.Env.ie > 10 ) { + // The 'mscontrolselect' event is supported only in IE11+ + dom.bind( editor.getBody(), 'mscontrolselect', function( event ) { + if ( event.target.nodeName === 'IMG' && dom.getParent( event.target, '.wp-caption' ) ) { + // Hide the thick border with resize handles around dl.wp-caption + editor.getBody().focus(); // :( + } else if ( event.target.nodeName === 'DL' && dom.hasClass( event.target, 'wp-caption' ) ) { + // Trigger the thick border with resize handles... + // This will make the caption text editable. + event.target.focus(); + } + }); + } + }); + + editor.on( 'ObjectResized', function( event ) { + var node = event.target; + + if ( node.nodeName === 'IMG' ) { + editor.undoManager.transact( function() { + var parent, width, + dom = editor.dom; + + node.className = node.className.replace( /\bsize-[^ ]+/, '' ); + + if ( parent = dom.getParent( node, '.wp-caption' ) ) { + width = event.width || dom.getAttrib( node, 'width' ); + + if ( width ) { + width = parseInt( width, 10 ); + + if ( ! editor.getParam( 'wpeditimage_html5_captions' ) ) { + width += 10; + } + + dom.setStyle( parent, 'width', width + 'px' ); + } + } + }); + } + }); + + editor.on( 'BeforeExecCommand', function( event ) { + var node, p, DL, align, replacement, + cmd = event.command, + dom = editor.dom; + + if ( cmd === 'mceInsertContent' ) { + // When inserting content, if the caret is inside a caption create new paragraph under + // and move the caret there + if ( node = dom.getParent( editor.selection.getNode(), 'div.mceTemp' ) ) { + p = dom.create( 'p' ); + dom.insertAfter( p, node ); + editor.selection.setCursorLocation( p, 0 ); + editor.nodeChanged(); + } + } else if ( cmd === 'JustifyLeft' || cmd === 'JustifyRight' || cmd === 'JustifyCenter' || cmd === 'wpAlignNone' ) { + node = editor.selection.getNode(); + align = 'align' + cmd.slice( 7 ).toLowerCase(); + DL = editor.dom.getParent( node, '.wp-caption' ); + + if ( node.nodeName !== 'IMG' && ! DL ) { + return; + } + + node = DL || node; + + if ( editor.dom.hasClass( node, align ) ) { + replacement = ' alignnone'; + } else { + replacement = ' ' + align; + } + + node.className = node.className.replace( / ?align(left|center|right|none)/g, '' ) + replacement; + + editor.nodeChanged(); + event.preventDefault(); + + if ( floatingToolbar ) { + floatingToolbar.reposition(); + } + + editor.fire( 'ExecCommand', { + command: cmd, + ui: event.ui, + value: event.value + } ); + } + }); + + editor.on( 'keydown', function( event ) { + var node, wrap, P, spacer, + selection = editor.selection, + keyCode = event.keyCode, + dom = editor.dom, + VK = tinymce.util.VK; + + if ( keyCode === VK.ENTER ) { + // When pressing Enter inside a caption move the caret to a new parapraph under it + node = selection.getNode(); + wrap = dom.getParent( node, 'div.mceTemp' ); + + if ( wrap ) { + dom.events.cancel( event ); // Doesn't cancel all :( + + // Remove any extra dt and dd cleated on pressing Enter... + tinymce.each( dom.select( 'dt, dd', wrap ), function( element ) { + if ( dom.isEmpty( element ) ) { + dom.remove( element ); + } + }); + + spacer = tinymce.Env.ie && tinymce.Env.ie < 11 ? '' : '
        '; + P = dom.create( 'p', null, spacer ); + + if ( node.nodeName === 'DD' ) { + dom.insertAfter( P, wrap ); + } else { + wrap.parentNode.insertBefore( P, wrap ); + } + + editor.nodeChanged(); + selection.setCursorLocation( P, 0 ); + } + } else if ( keyCode === VK.DELETE || keyCode === VK.BACKSPACE ) { + node = selection.getNode(); + + if ( node.nodeName === 'DIV' && dom.hasClass( node, 'mceTemp' ) ) { + wrap = node; + } else if ( node.nodeName === 'IMG' || node.nodeName === 'DT' || node.nodeName === 'A' ) { + wrap = dom.getParent( node, 'div.mceTemp' ); + } + + if ( wrap ) { + dom.events.cancel( event ); + removeImage( node ); + return false; + } + } + }); + + // After undo/redo FF seems to set the image height very slowly when it is set to 'auto' in the CSS. + // This causes image.getBoundingClientRect() to return wrong values and the resize handles are shown in wrong places. + // Collapse the selection to remove the resize handles. + if ( tinymce.Env.gecko ) { + editor.on( 'undo redo', function() { + if ( editor.selection.getNode().nodeName === 'IMG' ) { + editor.selection.collapse(); + } + }); + } + + editor.wpSetImgCaption = function( content ) { + return parseShortcode( content ); + }; + + editor.wpGetImgCaption = function( content ) { + return getShortcode( content ); + }; + + editor.on( 'BeforeSetContent', function( event ) { + if ( event.format !== 'raw' ) { + event.content = editor.wpSetImgCaption( event.content ); + } + }); + + editor.on( 'PostProcess', function( event ) { + if ( event.get ) { + event.content = editor.wpGetImgCaption( event.content ); + } + }); + + return { + _do_shcode: parseShortcode, + _get_shcode: getShortcode + }; +}); diff --git a/wp-includes/js/tinymce/plugins/wpeditimage/plugin.min.js b/wp-includes/js/tinymce/plugins/wpeditimage/plugin.min.js new file mode 100644 index 0000000..f152284 --- /dev/null +++ b/wp-includes/js/tinymce/plugins/wpeditimage/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("wpeditimage",function(a){function b(b){return!(!a.dom.getAttrib(b,"data-mce-placeholder")&&!a.dom.getAttrib(b,"data-mce-object"))}function c(){var b,c=[];return r(["wp_img_alignleft","wp_img_aligncenter","wp_img_alignright","wp_img_alignnone","wp_img_edit","wp_img_remove"],function(d){function e(){var b=a.selection;d.settings.stateSelector&&b.selectorChanged(d.settings.stateSelector,function(a){d.active(a)},!0),d.settings.disabledStateSelector&&b.selectorChanged(d.settings.disabledStateSelector,function(a){d.disabled(a)})}var f;"|"===d?b=null:q.has(d)?(d={type:d},p.toolbar_items_size&&(d.size=p.toolbar_items_size),c.push(d),b=null):(b||(b={type:"buttongroup",items:[]},c.push(b)),a.buttons[d]&&(f=d,d=a.buttons[f],"function"==typeof d&&(d=d()),d.type=d.type||"button",p.toolbar_items_size&&(d.size=p.toolbar_items_size),d=q.create(d),b.items.push(d),a.initialized?e():a.on("init",e)))}),{type:"panel",layout:"stack",classes:"toolbar-grp inline-toolbar-grp wp-image-toolbar",ariaRoot:!0,ariaRemember:!0,items:[{type:"toolbar",layout:"flow",items:c}]}}function d(){t||m.hide()}function e(b){return b.replace(/(?:

        )?\[(?:wp_)?caption([^\]]+)\]([\s\S]+?)\[\/(?:wp_)?caption\](?:<\/p>)?/g,function(b,c,d){var e,f,g,h,i,j,k=tinymce.trim;return e=c.match(/id=['"]([^'"]*)['"] ?/),e&&(c=c.replace(e[0],"")),f=c.match(/align=['"]([^'"]*)['"] ?/),f&&(c=c.replace(f[0],"")),g=c.match(/class=['"]([^'"]*)['"] ?/),g&&(c=c.replace(g[0],"")),j=c.match(/width=['"]([0-9]*)['"] ?/),j&&(c=c.replace(j[0],"")),d=k(d),i=d.match(/((?:]+>)?]+>(?:<\/a>)?)([\s\S]*)/i),i&&i[2]?(h=k(i[2]),i=k(i[1])):(h=k(c).replace(/caption=['"]/,"").replace(/['"]$/,""),i=d),e=e&&e[1]?e[1].replace(/[<>&]+/g,""):"",f=f&&f[1]?f[1]:"alignnone",g=g&&g[1]?" "+g[1].replace(/[<>&]+/g,""):"",!j&&i&&(j=i.match(/width=['"]([0-9]*)['"]/)),j&&j[1]&&(j=j[1]),j&&h?(j=parseInt(j,10),a.getParam("wpeditimage_html5_captions")||(j+=10),'

        "):d})}function f(a){return a.replace(/
        ]*>([\s\S]+?)<\/div>/g,function(a,b){var c="";return-1===b.indexOf("]+>([\s\S]+?)<\/dd>/i),c&&c[1]?"

        "+c[1]+"

        ":""):(c=b.replace(/\s*
        ]+)>\s*
        ]+>([\s\S]+?)<\/dt>\s*
        ]+>([\s\S]*?)<\/dd>\s*<\/dl>\s*/gi,function(a,b,c,d){var e,f,g,h;return h=c.match(/width="([0-9]*)"/),h=h&&h[1]?h[1]:"",h&&d?(e=b.match(/id="([^"]*)"/),e=e&&e[1]?e[1]:"",f=b.match(/class="([^"]*)"/),f=f&&f[1]?f[1]:"",g=f.match(/align[a-z]+/i)||"alignnone",f=f.replace(/wp-caption ?|align[a-z]+ ?/gi,""),f&&(f=' class="'+f+'"'),d=d.replace(/\r\n|\r/g,"\n").replace(/<[a-zA-Z0-9]+( [^<>]+)?>/g,function(a){return a.replace(/[\r\n\t]+/," ")}),d=d.replace(/\s*\n\s*/g,"
        "),'[caption id="'+e+'" align="'+g+'" width="'+h+'"'+f+"]"+c+" "+d+"[/caption]"):c}),-1===c.indexOf("[caption")&&(c=b.replace(/[\s\S]*?((?:
        ]+>)?]+>(?:<\/a>)?)(

        [\s\S]*<\/p>)?[\s\S]*/gi,"

        $1

        $2")),c)})}function g(b){var c,d,e,f,g,h,i,j,k=[],l=a.dom,m=/^\d+$/;return e={attachment_id:!1,size:"custom",caption:"",align:"none",extraClasses:"",link:!1,linkUrl:"",linkClassName:"",linkTargetBlank:!1,linkRel:"",title:""},e.url=l.getAttrib(b,"src"),e.alt=l.getAttrib(b,"alt"),e.title=l.getAttrib(b,"title"),i=l.getAttrib(b,"width"),j=l.getAttrib(b,"height"),(!m.test(i)||parseInt(i,10)<1)&&(i=b.naturalWidth||b.width),(!m.test(j)||parseInt(j,10)<1)&&(j=b.naturalHeight||b.height),e.customWidth=e.width=i,e.customHeight=e.height=j,c=tinymce.explode(b.className," "),d=[],tinymce.each(c,function(a){/^wp-image/.test(a)?e.attachment_id=parseInt(a.replace("wp-image-",""),10):/^align/.test(a)?e.align=a.replace("align",""):/^size/.test(a)?e.size=a.replace("size-",""):d.push(a)}),e.extraClasses=d.join(" "),f=l.getParents(b,".wp-caption"),f.length&&(f=f[0],c=f.className.split(" "),tinymce.each(c,function(a){/^align/.test(a)?e.align=a.replace("align",""):a&&"wp-caption"!==a&&k.push(a)}),e.captionClassName=k.join(" "),g=l.select("dd.wp-caption-dd",f),g.length&&(g=g[0],e.caption=a.serializer.serialize(g).replace(/]*>/g,"$&\n").replace(/^

        /,"").replace(/<\/p>$/,""))),b.parentNode&&"A"===b.parentNode.nodeName&&(h=b.parentNode,e.linkUrl=l.getAttrib(h,"href"),e.linkTargetBlank="_blank"===l.getAttrib(h,"target")?!0:!1,e.linkRel=l.getAttrib(h,"rel"),e.linkClassName=h.className),e}function h(a){return a&&!(!a.textContent&&!a.innerText)}function i(b){return!b||-1===b.indexOf("<")&&-1===b.indexOf(">")?b:(n||(n=new tinymce.html.Serializer({},a.schema)),n.serialize(a.parser.parse(b,{forced_root_block:!1})))}function j(b,c){var d,e,f,g,j,k,l,m,n,o,p,q,r,s,t,u,v=a.dom;d=tinymce.explode(c.extraClasses," "),d||(d=[]),c.caption||d.push("align"+c.align),c.attachment_id&&(d.push("wp-image-"+c.attachment_id),c.size&&"custom"!==c.size&&d.push("size-"+c.size)),s=c.width,t=c.height,"custom"===c.size&&(s=c.customWidth,t=c.customHeight),q={src:c.url,width:s||null,height:t||null,alt:c.alt,title:c.title||null,"class":d.join(" ")||null},v.setAttribs(b,q),r={href:c.linkUrl,rel:c.linkRel||null,target:c.linkTargetBlank?"_blank":null,"class":c.linkClassName||null},b.parentNode&&"A"===b.parentNode.nodeName&&!h(b.parentNode)?c.linkUrl?v.setAttribs(b.parentNode,r):v.remove(b.parentNode,!0):c.linkUrl&&((l=v.getParent(b,"a"))&&v.insertAfter(b,l),l=v.create("a",r),b.parentNode.insertBefore(l,b),l.appendChild(b)),m=a.dom.getParent(b,".mceTemp"),f=b.parentNode&&"A"===b.parentNode.nodeName&&!h(b.parentNode)?b.parentNode:b,c.caption?(c.caption=i(c.caption),p=c.attachment_id?"attachment_"+c.attachment_id:null,u="align"+(c.align||"none"),e="wp-caption "+u,c.captionClassName&&(e+=" "+c.captionClassName.replace(/[<>&]+/g,"")),a.getParam("wpeditimage_html5_captions")||(s=parseInt(s,10),s+=10),m?(o=v.select("dl.wp-caption",m),o.length&&v.setAttribs(o,{id:p,"class":e,style:"width: "+s+"px"}),n=v.select(".wp-caption-dd",m),n.length&&v.setHTML(n[0],c.caption)):(p=p?'id="'+p+'" ':"",g="

        '+c.caption+"
        ",k=v.create("div",{"class":"mceTemp"},g),(j=v.getParent(f,"p"))?(j.parentNode.insertBefore(k,j),v.isEmpty(j)&&v.remove(j)):f.parentNode.insertBefore(k,f),a.$(k).find("dt.wp-caption-dt").append(f))):m&&(j=v.create("p"),m.parentNode.insertBefore(j,m),j.appendChild(f),v.remove(m)),wp.media.events&&wp.media.events.trigger("editor:image-update",{editor:a,metadata:c,image:b}),a.nodeChanged()}function k(b){var c,d,e;return"undefined"!=typeof wp&&wp.media?(e=g(b),wp.media.events.trigger("editor:image-edit",{editor:a,metadata:e,image:b}),c=wp.media({frame:"image",state:"image-details",metadata:e}),wp.media.events.trigger("editor:frame-create",{frame:c}),d=function(d){a.focus(),a.undoManager.transact(function(){j(b,d)}),c.detach()},c.state("image-details").on("update",d),c.state("replace-image").on("replace",d),c.on("close",function(){a.focus(),c.detach()}),void c.open()):void a.execCommand("mceImage")}function l(b){var c;"DIV"===b.nodeName&&a.dom.hasClass(b,"mceTemp")?c=b:("IMG"===b.nodeName||"DT"===b.nodeName||"A"===b.nodeName)&&(c=a.dom.getParent(b,"div.mceTemp")),c?(a.selection.select(c.nextSibling?c.nextSibling:c.previousSibling?c.previousSibling:c.parentNode),a.selection.collapse(!0),a.dom.remove(c)):a.dom.remove(b),a.nodeChanged(),a.undoManager.add()}var m,n,o=tinymce.DOM,p=a.settings,q=tinymce.ui.Factory,r=tinymce.each,s=tinymce.Env.iOS,t=!0,u=tinymce.$("#postdivrich");return a.addButton("wp_img_remove",{tooltip:"Remove",icon:"dashicon dashicons-no",onclick:function(){l(a.selection.getNode())}}),a.addButton("wp_img_edit",{tooltip:"Edit ",icon:"dashicon dashicons-edit",onclick:function(){k(a.selection.getNode())}}),r({alignleft:"Align left",aligncenter:"Align center",alignright:"Align right",alignnone:"No alignment"},function(b,c){var d=c.slice(5);a.addButton("wp_img_"+c,{tooltip:b,icon:"dashicon dashicons-align-"+d,cmd:"alignnone"===c?"wpAlignNone":"Justify"+d.slice(0,1).toUpperCase()+d.slice(1),onPostRender:function(){var b=this;a.on("NodeChange",function(d){var e;"IMG"===d.element.nodeName&&(e=a.dom.getParent(d.element,".wp-caption")||d.element,b.active("alignnone"===c?!/\balign(left|center|right)\b/.test(e.className):a.dom.hasClass(e,c)))})}})}),m=q.create(c()).renderTo(document.body).hide(),m.reposition=function(){var b,c,d,e,f,g,h,i,j,k,l,m,n,p,q,r,t,u,v,w,x,y=this.getEl(),z=5,A=8,B=0,C=a.selection.getNode();return C&&"IMG"===C.nodeName?(f=window.pageYOffset||document.documentElement.scrollTop,g=tinymce.$("#wpadminbar")[0],h=tinymce.$(".mce-tinymce .mce-toolbar-grp")[0],i=C.getBoundingClientRect(),j=(i.left+i.right)/2,k=(i.top+i.bottom)/2,l=i.top,m=v-i.bottom,n=window.innerWidth,p=y.offsetWidth,q=p/2,r=document.getElementById(a.id+"_ifr"),t=o.getPos(r),u=r.offsetWidth,v=r.offsetHeight,w=y.offsetHeight,x=w+A+z,s?b=i.top+t.y+A:l>=x?(e=" mce-arrow-down",b=i.top+t.y-w-A):m>=x?(e=" mce-arrow-up",b=i.bottom+t.y):(b=z,e=k>=x?" mce-arrow-down":" mce-arrow-up"),d=h?o.getPos(h).y+h.clientHeight:t.y,f&&(g&&0===g.getBoundingClientRect().top&&(B=g.clientHeight),f+B>d&&(d=f+B)),b&&d&&d+z>b&&(b=d+z,e=""),c=j-q,c+=t.x,i.left<0||i.right>u?c=t.x+(u-p)/2:p>=n?(e+=" mce-arrow-full",c=0):0>c&&i.left+p>n||c+p>n&&i.right-p<0?c=(n-p)/2:cu+t.x&&(e+=" mce-arrow-right",c=i.right-p+t.x),s||(y.className=y.className.replace(/ ?mce-arrow-[\w]+/g,""),y.className+=e),o.setStyles(y,{left:c,top:b}),this):this},s&&a.on("click",function(b){if("IMG"===b.target.nodeName){var c=b.target;window.setTimeout(function(){a.selection.select(c)},200)}else m.hide()}),a.on("nodechange",function(c){var d=s?350:100;return"IMG"!==c.element.nodeName||b(c.element)?void m.hide():void setTimeout(function(){var c=a.selection.getNode();"IMG"!==c.nodeName||b(c)?m.hide():m._visible?m.reposition():m.show()},d)}),m.on("show",function(){t=!1,this._visible&&(this.reposition(),o.addClass(this.getEl(),"mce-inline-toolbar-grp-active"))}),m.on("hide",function(){t=!0,o.removeClass(this.getEl(),"mce-inline-toolbar-grp-active")}),m.on("keydown",function(b){27===b.keyCode&&(d(),a.focus())}),o.bind(window,"resize scroll",function(){!t&&u.hasClass("wp-editor-expand")&&d()}),a.on("init",function(){a.dom.bind(a.getWin(),"scroll",d)}),a.on("blur hide",d),a.shortcuts.add("Alt+119","",function(){var a=m.find("toolbar")[0];a&&a.focus(!0)}),a.on("init",function(){var b=a.dom,c=a.getParam("wpeditimage_html5_captions")?"html5-captions":"html4-captions";b.addClass(a.getBody(),c),a.on("wpLoadImageForm",function(b){if(!a.getParam("wpeditimage_disable_captions")){var c={type:"textbox",flex:1,name:"caption",minHeight:60,multiline:!0,scroll:!0,label:"Image caption"};b.data.splice(b.data.length-1,0,c)}}),a.on("wpNewImageRefresh",function(a){var c,d;(c=b.getParent(a.node,"dl.wp-caption"))&&(c.style.width||(d=parseInt(a.node.clientWidth,10)+10,d=d?d+"px":"50%",b.setStyle(c,"width",d)))}),a.on("wpImageFormSubmit",function(c){var d,e,f,g,h,j=c.imgData.data,k=c.imgData.node,l=c.imgData.caption,m="",n="",o="";return j.id="__wp-temp-img-id",c.imgData.cancel=!0,j.style||(j.style=null),j.src?(l&&(l=l.replace(/\r\n|\r/g,"\n").replace(/<\/?[a-zA-Z0-9]+( [^<>]+)?>/g,function(a){return a.replace(/[\r\n\t]+/," ")}),l=l.replace(/(]*>)\s*\n\s*/g,"$1").replace(/\s*\n\s*/g,"
        "),l=i(l)),k?(h=k.id||null,b.setAttribs(k,j),d=b.getParent(k,"dl.wp-caption"),l?d?(e=b.select("dd.wp-caption-dd",d)[0])&&(e.innerHTML=l):(k.className&&(m=k.className.match(/wp-image-([0-9]+)/),n=k.className.match(/align(left|right|center|none)/)),n?(n=n[0],k.className=k.className.replace(/align(left|right|center|none)/g,"")):n="alignnone",n=' class="wp-caption '+n+'"',m&&(m=' id="attachment_'+m[1]+'"'),o=j.width||k.clientWidth,o&&(o=parseInt(o,10),a.getParam("wpeditimage_html5_captions")||(o+=10),o=' style="width: '+o+'px"'),f=k.parentNode&&"A"===k.parentNode.nodeName?k.parentNode:k,g="
        '+l+"
        ",d=b.create("div",{"class":"mceTemp"},g),(e=b.getParent(f,"p"))?(e.parentNode.insertBefore(d,e),b.isEmpty(e)&&b.remove(e)):f.parentNode.insertBefore(d,f),a.$(d).find("dt.wp-caption-dt").append(f)):d&&(g=b.getOuterHTML("A"===k.parentNode.nodeName?k.parentNode:k),e=b.create("p",{},g),b.insertAfter(e,d.parentNode),a.selection.select(e),a.nodeChanged(),b.remove(d.parentNode))):(g=b.createHTML("img",j),l?(f=a.selection.getNode(),j.width&&(o=parseInt(j.width,10),a.getParam("wpeditimage_html5_captions")||(o+=10),o=' style="width: '+o+'px"'),g='
        '+g+'
        '+l+"
        ",e="P"===f.nodeName?f:b.getParent(f,"p"),e&&"P"===e.nodeName?(d=b.create("div",{"class":"mceTemp"},g),e.parentNode.insertBefore(d,e),a.selection.select(d),a.nodeChanged(),b.isEmpty(e)&&b.remove(e)):a.selection.setContent('
        '+g+"
        ")):a.selection.setContent(g)),k=b.get("__wp-temp-img-id"),b.setAttrib(k,"id",h),void(c.imgData.node=k)):void(k&&(b.remove((d=b.getParent(k,"div.mceTemp"))?d:"A"===k.parentNode.nodeName?k.parentNode:k),a.nodeChanged()))}),a.on("wpLoadImageData",function(c){var d,e=c.imgData.data,f=c.imgData.node;(d=b.getParent(f,"dl.wp-caption"))&&(d=b.select("dd.wp-caption-dd",d)[0],d&&(e.caption=a.serializer.serialize(d).replace(/]*>/g,"$&\n").replace(/^

        /,"").replace(/<\/p>$/,"")))}),b.bind(a.getDoc(),"dragstart",function(c){var d=a.selection.getNode();"IMG"===d.nodeName&&b.getParent(d,".wp-caption")&&c.preventDefault()}),tinymce.Env.ie&&tinymce.Env.ie>10&&b.bind(a.getBody(),"mscontrolselect",function(c){"IMG"===c.target.nodeName&&b.getParent(c.target,".wp-caption")?a.getBody().focus():"DL"===c.target.nodeName&&b.hasClass(c.target,"wp-caption")&&c.target.focus()})}),a.on("ObjectResized",function(b){var c=b.target;"IMG"===c.nodeName&&a.undoManager.transact(function(){var d,e,f=a.dom;c.className=c.className.replace(/\bsize-[^ ]+/,""),(d=f.getParent(c,".wp-caption"))&&(e=b.width||f.getAttrib(c,"width"),e&&(e=parseInt(e,10),a.getParam("wpeditimage_html5_captions")||(e+=10),f.setStyle(d,"width",e+"px")))})}),a.on("BeforeExecCommand",function(b){var c,d,e,f,g,h=b.command,i=a.dom;if("mceInsertContent"===h)(c=i.getParent(a.selection.getNode(),"div.mceTemp"))&&(d=i.create("p"),i.insertAfter(d,c),a.selection.setCursorLocation(d,0),a.nodeChanged());else if("JustifyLeft"===h||"JustifyRight"===h||"JustifyCenter"===h||"wpAlignNone"===h){if(c=a.selection.getNode(),f="align"+h.slice(7).toLowerCase(),e=a.dom.getParent(c,".wp-caption"),"IMG"!==c.nodeName&&!e)return;c=e||c,g=a.dom.hasClass(c,f)?" alignnone":" "+f,c.className=c.className.replace(/ ?align(left|center|right|none)/g,"")+g,a.nodeChanged(),b.preventDefault(),m&&m.reposition(),a.fire("ExecCommand",{command:h,ui:b.ui,value:b.value})}}),a.on("keydown",function(b){var c,d,e,f,g=a.selection,h=b.keyCode,i=a.dom,j=tinymce.util.VK;if(h===j.ENTER)c=g.getNode(),d=i.getParent(c,"div.mceTemp"),d&&(i.events.cancel(b),tinymce.each(i.select("dt, dd",d),function(a){i.isEmpty(a)&&i.remove(a)}),f=tinymce.Env.ie&&tinymce.Env.ie<11?"":'
        ',e=i.create("p",null,f),"DD"===c.nodeName?i.insertAfter(e,d):d.parentNode.insertBefore(e,d),a.nodeChanged(),g.setCursorLocation(e,0));else if((h===j.DELETE||h===j.BACKSPACE)&&(c=g.getNode(),"DIV"===c.nodeName&&i.hasClass(c,"mceTemp")?d=c:("IMG"===c.nodeName||"DT"===c.nodeName||"A"===c.nodeName)&&(d=i.getParent(c,"div.mceTemp")),d))return i.events.cancel(b),l(c),!1}),tinymce.Env.gecko&&a.on("undo redo",function(){"IMG"===a.selection.getNode().nodeName&&a.selection.collapse()}),a.wpSetImgCaption=function(a){return e(a)},a.wpGetImgCaption=function(a){return f(a)},a.on("BeforeSetContent",function(b){"raw"!==b.format&&(b.content=a.wpSetImgCaption(b.content))}),a.on("PostProcess",function(b){b.get&&(b.content=a.wpGetImgCaption(b.content))}),{_do_shcode:e,_get_shcode:f}}); \ No newline at end of file diff --git a/wp-includes/js/tinymce/plugins/wpfullscreen/plugin.js b/wp-includes/js/tinymce/plugins/wpfullscreen/plugin.js new file mode 100644 index 0000000..b06bcb9 --- /dev/null +++ b/wp-includes/js/tinymce/plugins/wpfullscreen/plugin.js @@ -0,0 +1,76 @@ +/* global tinymce */ +/** + * WP Fullscreen (Distraction-Free Writing) TinyMCE plugin + */ +tinymce.PluginManager.add( 'wpfullscreen', function( editor ) { + var settings = editor.settings; + + function fullscreenOn() { + settings.wp_fullscreen = true; + editor.dom.addClass( editor.getDoc().documentElement, 'wp-fullscreen' ); + // Start auto-resizing + editor.execCommand( 'wpAutoResizeOn' ); + } + + function fullscreenOff() { + settings.wp_fullscreen = false; + editor.dom.removeClass( editor.getDoc().documentElement, 'wp-fullscreen' ); + // Stop auto-resizing + editor.execCommand( 'wpAutoResizeOff' ); + } + + // For use from outside the editor. + editor.addCommand( 'wpFullScreenOn', fullscreenOn ); + editor.addCommand( 'wpFullScreenOff', fullscreenOff ); + + function getExtAPI() { + return ( typeof wp !== 'undefined' && wp.editor && wp.editor.fullscreen ); + } + + // Toggle DFW mode. For use from inside the editor. + function toggleFullscreen() { + var fullscreen = getExtAPI(); + + if ( fullscreen ) { + if ( editor.getParam('wp_fullscreen') ) { + fullscreen.off(); + } else { + fullscreen.on(); + } + } + } + + editor.addCommand( 'wpFullScreen', toggleFullscreen ); + + editor.on( 'keydown', function( event ) { + var fullscreen; + + // Turn fullscreen off when Esc is pressed. + if ( event.keyCode === 27 && ( fullscreen = getExtAPI() ) && fullscreen.settings.visible ) { + fullscreen.off(); + } + }); + + editor.on( 'init', function() { + // Set the editor when initializing from whitin DFW + if ( editor.getParam('wp_fullscreen') ) { + fullscreenOn(); + } + }); + + // Register buttons + editor.addButton( 'wp_fullscreen', { + tooltip: 'Distraction-free writing mode', + shortcut: 'Alt+Shift+W', + onclick: toggleFullscreen, + classes: 'wp-fullscreen btn widget' // This overwrites all classes on the container! + }); + + editor.addMenuItem( 'wp_fullscreen', { + text: 'Distraction-free writing mode', + icon: 'wp_fullscreen', + shortcut: 'Alt+Shift+W', + context: 'view', + onclick: toggleFullscreen + }); +}); diff --git a/wp-includes/js/tinymce/plugins/wpfullscreen/plugin.min.js b/wp-includes/js/tinymce/plugins/wpfullscreen/plugin.min.js new file mode 100644 index 0000000..a0db9c7 --- /dev/null +++ b/wp-includes/js/tinymce/plugins/wpfullscreen/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("wpfullscreen",function(a){function b(){f.wp_fullscreen=!0,a.dom.addClass(a.getDoc().documentElement,"wp-fullscreen"),a.execCommand("wpAutoResizeOn")}function c(){f.wp_fullscreen=!1,a.dom.removeClass(a.getDoc().documentElement,"wp-fullscreen"),a.execCommand("wpAutoResizeOff")}function d(){return"undefined"!=typeof wp&&wp.editor&&wp.editor.fullscreen}function e(){var b=d();b&&(a.getParam("wp_fullscreen")?b.off():b.on())}var f=a.settings;a.addCommand("wpFullScreenOn",b),a.addCommand("wpFullScreenOff",c),a.addCommand("wpFullScreen",e),a.on("keydown",function(a){var b;27===a.keyCode&&(b=d())&&b.settings.visible&&b.off()}),a.on("init",function(){a.getParam("wp_fullscreen")&&b()}),a.addButton("wp_fullscreen",{tooltip:"Distraction-free writing mode",shortcut:"Alt+Shift+W",onclick:e,classes:"wp-fullscreen btn widget"}),a.addMenuItem("wp_fullscreen",{text:"Distraction-free writing mode",icon:"wp_fullscreen",shortcut:"Alt+Shift+W",context:"view",onclick:e})}); \ No newline at end of file diff --git a/wp-includes/js/tinymce/plugins/wpgallery/plugin.js b/wp-includes/js/tinymce/plugins/wpgallery/plugin.js new file mode 100644 index 0000000..41dcfe5 --- /dev/null +++ b/wp-includes/js/tinymce/plugins/wpgallery/plugin.js @@ -0,0 +1,112 @@ +/* global tinymce */ +tinymce.PluginManager.add('wpgallery', function( editor ) { + + function replaceGalleryShortcodes( content ) { + return content.replace( /\[gallery([^\]]*)\]/g, function( match ) { + return html( 'wp-gallery', match ); + }); + } + + function html( cls, data ) { + data = window.encodeURIComponent( data ); + return ''; + } + + function restoreMediaShortcodes( content ) { + function getAttr( str, name ) { + name = new RegExp( name + '=\"([^\"]+)\"' ).exec( str ); + return name ? window.decodeURIComponent( name[1] ) : ''; + } + + return content.replace( /(?:]+)?>)*(]+>)(?:<\/p>)*/g, function( match, image ) { + var data = getAttr( image, 'data-wp-media' ); + + if ( data ) { + return '

        ' + data + '

        '; + } + + return match; + }); + } + + function editMedia( node ) { + var gallery, frame, data; + + if ( node.nodeName !== 'IMG' ) { + return; + } + + // Check if the `wp.media` API exists. + if ( typeof wp === 'undefined' || ! wp.media ) { + return; + } + + data = window.decodeURIComponent( editor.dom.getAttrib( node, 'data-wp-media' ) ); + + // Make sure we've selected a gallery node. + if ( editor.dom.hasClass( node, 'wp-gallery' ) && wp.media.gallery ) { + gallery = wp.media.gallery; + frame = gallery.edit( data ); + + frame.state('gallery-edit').on( 'update', function( selection ) { + var shortcode = gallery.shortcode( selection ).string(); + editor.dom.setAttrib( node, 'data-wp-media', window.encodeURIComponent( shortcode ) ); + frame.detach(); + }); + } + } + + // Register the command so that it can be invoked by using tinyMCE.activeEditor.execCommand('...'); + editor.addCommand( 'WP_Gallery', function() { + editMedia( editor.selection.getNode() ); + }); + + editor.on( 'mouseup', function( event ) { + var dom = editor.dom, + node = event.target; + + function unselect() { + dom.removeClass( dom.select( 'img.wp-media-selected' ), 'wp-media-selected' ); + } + + if ( node.nodeName === 'IMG' && dom.getAttrib( node, 'data-wp-media' ) ) { + // Don't trigger on right-click + if ( event.button !== 2 ) { + if ( dom.hasClass( node, 'wp-media-selected' ) ) { + editMedia( node ); + } else { + unselect(); + dom.addClass( node, 'wp-media-selected' ); + } + } + } else { + unselect(); + } + }); + + // Display gallery, audio or video instead of img in the element path + editor.on( 'ResolveName', function( event ) { + var dom = editor.dom, + node = event.target; + + if ( node.nodeName === 'IMG' && dom.getAttrib( node, 'data-wp-media' ) ) { + if ( dom.hasClass( node, 'wp-gallery' ) ) { + event.name = 'gallery'; + } + } + }); + + editor.on( 'BeforeSetContent', function( event ) { + // 'wpview' handles the gallery shortcode when present + if ( ! editor.plugins.wpview || typeof wp === 'undefined' || ! wp.mce ) { + event.content = replaceGalleryShortcodes( event.content ); + } + }); + + editor.on( 'PostProcess', function( event ) { + if ( event.get ) { + event.content = restoreMediaShortcodes( event.content ); + } + }); +}); diff --git a/wp-includes/js/tinymce/plugins/wpgallery/plugin.min.js b/wp-includes/js/tinymce/plugins/wpgallery/plugin.min.js new file mode 100644 index 0000000..1231ac6 --- /dev/null +++ b/wp-includes/js/tinymce/plugins/wpgallery/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("wpgallery",function(a){function b(a){return a.replace(/\[gallery([^\]]*)\]/g,function(a){return c("wp-gallery",a)})}function c(a,b){return b=window.encodeURIComponent(b),''}function d(a){function b(a,b){return b=new RegExp(b+'="([^"]+)"').exec(a),b?window.decodeURIComponent(b[1]):""}return a.replace(/(?:]+)?>)*(]+>)(?:<\/p>)*/g,function(a,c){var d=b(c,"data-wp-media");return d?"

        "+d+"

        ":a})}function e(b){var c,d,e;"IMG"===b.nodeName&&"undefined"!=typeof wp&&wp.media&&(e=window.decodeURIComponent(a.dom.getAttrib(b,"data-wp-media")),a.dom.hasClass(b,"wp-gallery")&&wp.media.gallery&&(c=wp.media.gallery,d=c.edit(e),d.state("gallery-edit").on("update",function(e){var f=c.shortcode(e).string();a.dom.setAttrib(b,"data-wp-media",window.encodeURIComponent(f)),d.detach()})))}a.addCommand("WP_Gallery",function(){e(a.selection.getNode())}),a.on("mouseup",function(b){function c(){d.removeClass(d.select("img.wp-media-selected"),"wp-media-selected")}var d=a.dom,f=b.target;"IMG"===f.nodeName&&d.getAttrib(f,"data-wp-media")?2!==b.button&&(d.hasClass(f,"wp-media-selected")?e(f):(c(),d.addClass(f,"wp-media-selected"))):c()}),a.on("ResolveName",function(b){var c=a.dom,d=b.target;"IMG"===d.nodeName&&c.getAttrib(d,"data-wp-media")&&c.hasClass(d,"wp-gallery")&&(b.name="gallery")}),a.on("BeforeSetContent",function(c){a.plugins.wpview&&"undefined"!=typeof wp&&wp.mce||(c.content=b(c.content))}),a.on("PostProcess",function(a){a.get&&(a.content=d(a.content))})}); \ No newline at end of file diff --git a/wp-includes/js/tinymce/plugins/wplink/plugin.js b/wp-includes/js/tinymce/plugins/wplink/plugin.js new file mode 100644 index 0000000..8c05585 --- /dev/null +++ b/wp-includes/js/tinymce/plugins/wplink/plugin.js @@ -0,0 +1,63 @@ +/* global tinymce */ +tinymce.PluginManager.add( 'wplink', function( editor ) { + var linkButton; + + // Register a command so that it can be invoked by using tinyMCE.activeEditor.execCommand( 'WP_Link' ); + editor.addCommand( 'WP_Link', function() { + if ( ( ! linkButton || ! linkButton.disabled() ) && typeof window.wpLink !== 'undefined' ) { + window.wpLink.open( editor.id ); + } + }); + + // WP default shortcut + editor.addShortcut( 'alt+shift+a', '', 'WP_Link' ); + // The "de-facto standard" shortcut, see #27305 + editor.addShortcut( 'ctrl+k', '', 'WP_Link' ); + + function setState( button, node ) { + var parent = editor.dom.getParent( node, 'a' ), + getView = editor.plugins.wpview ? editor.plugins.wpview.getView : function() { return false; }; + + button.disabled( ( editor.selection.isCollapsed() && ! parent ) || ( parent && ! parent.href ) || getView( node ) ); + button.active( parent && parent.href ); + } + + editor.addButton( 'link', { + icon: 'link', + tooltip: 'Insert/edit link', + shortcut: 'Alt+Shift+A', + cmd: 'WP_Link', + + onPostRender: function() { + linkButton = this; + + editor.on( 'nodechange', function( event ) { + setState( linkButton, event.element ); + }); + } + }); + + editor.addButton( 'unlink', { + icon: 'unlink', + tooltip: 'Remove link', + cmd: 'unlink', + + onPostRender: function() { + var unlinkButton = this; + + editor.on( 'nodechange', function( event ) { + setState( unlinkButton, event.element ); + }); + } + }); + + editor.addMenuItem( 'link', { + icon: 'link', + text: 'Insert link', + shortcut: 'Alt+Shift+A', + cmd: 'WP_Link', + stateSelector: 'a[href]', + context: 'insert', + prependToContext: true + }); +}); diff --git a/wp-includes/js/tinymce/plugins/wplink/plugin.min.js b/wp-includes/js/tinymce/plugins/wplink/plugin.min.js new file mode 100644 index 0000000..32203b7 --- /dev/null +++ b/wp-includes/js/tinymce/plugins/wplink/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("wplink",function(a){function b(b,c){var d=a.dom.getParent(c,"a"),e=a.plugins.wpview?a.plugins.wpview.getView:function(){return!1};b.disabled(a.selection.isCollapsed()&&!d||d&&!d.href||e(c)),b.active(d&&d.href)}var c;a.addCommand("WP_Link",function(){c&&c.disabled()||"undefined"==typeof window.wpLink||window.wpLink.open(a.id)}),a.addShortcut("alt+shift+a","","WP_Link"),a.addShortcut("ctrl+k","","WP_Link"),a.addButton("link",{icon:"link",tooltip:"Insert/edit link",shortcut:"Alt+Shift+A",cmd:"WP_Link",onPostRender:function(){c=this,a.on("nodechange",function(a){b(c,a.element)})}}),a.addButton("unlink",{icon:"unlink",tooltip:"Remove link",cmd:"unlink",onPostRender:function(){var c=this;a.on("nodechange",function(a){b(c,a.element)})}}),a.addMenuItem("link",{icon:"link",text:"Insert link",shortcut:"Alt+Shift+A",cmd:"WP_Link",stateSelector:"a[href]",context:"insert",prependToContext:!0})}); \ No newline at end of file diff --git a/wp-includes/js/tinymce/plugins/wpview/plugin.js b/wp-includes/js/tinymce/plugins/wpview/plugin.js new file mode 100644 index 0000000..6016e3b --- /dev/null +++ b/wp-includes/js/tinymce/plugins/wpview/plugin.js @@ -0,0 +1,718 @@ +/* global tinymce */ + +/** + * WordPress View plugin. + */ +tinymce.PluginManager.add( 'wpview', function( editor ) { + var selected, + Env = tinymce.Env, + VK = tinymce.util.VK, + TreeWalker = tinymce.dom.TreeWalker, + toRemove = false, + firstFocus = true, + _noop = function() { return false; }, + isios = /iPad|iPod|iPhone/.test( navigator.userAgent ), + cursorInterval, lastKeyDownNode, setViewCursorTries, focus, execCommandView, execCommandBefore; + + function getView( node ) { + return getParent( node, 'wpview-wrap' ); + } + + /** + * Returns the node or a parent of the node that has the passed className. + * Doing this directly is about 40% faster + */ + function getParent( node, className ) { + while ( node && node.parentNode ) { + if ( node.className && ( ' ' + node.className + ' ' ).indexOf( ' ' + className + ' ' ) !== -1 ) { + return node; + } + + node = node.parentNode; + } + + return false; + } + + /** + * Get the text/shortcode string for a view. + * + * @param view The view wrapper's node + * @returns string The text/shoercode string of the view + */ + function getViewText( view ) { + if ( view = getView( view ) ) { + return window.decodeURIComponent( editor.dom.getAttrib( view, 'data-wpview-text' ) || '' ); + } + + return ''; + } + + /** + * Set the view's original text/shortcode string + * + * @param view The view wrapper's HTML id or node + * @param text The text string to be set + */ + function setViewText( view, text ) { + view = getView( view ); + + if ( view ) { + editor.dom.setAttrib( view, 'data-wpview-text', window.encodeURIComponent( text || '' ) ); + return true; + } + + return false; + } + + function _stop( event ) { + event.stopPropagation(); + } + + function setViewCursor( before, view ) { + var location = before ? 'before' : 'after', + offset = before ? 0 : 1; + deselect(); + editor.selection.setCursorLocation( editor.dom.select( '.wpview-selection-' + location, view )[0], offset ); + editor.nodeChanged(); + } + + function handleEnter( view, before, key ) { + var dom = editor.dom, + padNode = dom.create( 'p' ); + + if ( ! ( Env.ie && Env.ie < 11 ) ) { + padNode.innerHTML = '
        '; + } + + if ( before ) { + view.parentNode.insertBefore( padNode, view ); + } else { + dom.insertAfter( padNode, view ); + } + + deselect(); + + if ( before && key === VK.ENTER ) { + setViewCursor( before, view ); + } else { + editor.selection.setCursorLocation( padNode, 0 ); + } + + editor.nodeChanged(); + } + + function removeView( view ) { + // TODO: trigger an event to run a clean up function. + // Maybe `jQuery( view ).trigger( 'remove' );`? + editor.undoManager.transact( function() { + handleEnter( view ); + editor.dom.remove( view ); + }); + } + + function select( viewNode ) { + var clipboard, + dom = editor.dom; + + if ( ! viewNode ) { + return; + } + + // Adjust the toolbar position and bail if node is already selected. + if ( viewNode === selected ) { + adjustToolbarPosition( viewNode ); + return; + } + + // Make sure that the editor is focused. + // It is possible that the editor is not focused when the mouse event fires + // without focus, the selection will not work properly. + editor.getBody().focus(); + + deselect(); + selected = viewNode; + dom.setAttrib( viewNode, 'data-mce-selected', 1 ); + adjustToolbarPosition( viewNode ); + + clipboard = dom.create( 'div', { + 'class': 'wpview-clipboard', + 'contenteditable': 'true' + }, getViewText( viewNode ) ); + + editor.dom.select( '.wpview-body', viewNode )[0].appendChild( clipboard ); + + // Both of the following are necessary to prevent manipulating the selection/focus + dom.bind( clipboard, 'beforedeactivate focusin focusout', _stop ); + dom.bind( selected, 'beforedeactivate focusin focusout', _stop ); + + // select the hidden div + if ( isios ) { + editor.selection.select( clipboard ); + } else { + editor.selection.select( clipboard, true ); + } + + editor.nodeChanged(); + editor.fire( 'wpview-selected', viewNode ); + } + + function adjustToolbarPosition( viewNode ) { + var delta = 0, + toolbar = editor.$( viewNode ).find( '.toolbar' ), + editorToolbar = tinymce.$( editor.editorContainer ).find( '.mce-toolbar-grp' )[0], + editorToolbarBottom = ( editorToolbar && editorToolbar.getBoundingClientRect().bottom ) || 0; + + if ( toolbar.length && editor.iframeElement ) { + // 48 = 43 for the toolbar + 5 buffer + delta = viewNode.getBoundingClientRect().top + editor.iframeElement.getBoundingClientRect().top - editorToolbarBottom - 48; + } + + if ( delta < 0 ) { + toolbar.removeClass( 'mce-arrow-down' ).css({ top: ( -43 + delta * -1 ) }); + } else if ( delta > 0 && ! toolbar.hasClass( 'mce-arrow-down' ) ) { + toolbar.addClass( 'mce-arrow-down' ).css({ top: '' }); + } + } + + /** + * Deselect a selected view and remove clipboard + */ + function deselect() { + var clipboard, + dom = editor.dom; + + if ( selected ) { + clipboard = editor.dom.select( '.wpview-clipboard', selected )[0]; + dom.unbind( clipboard ); + dom.remove( clipboard ); + + dom.unbind( selected, 'beforedeactivate focusin focusout click mouseup', _stop ); + dom.setAttrib( selected, 'data-mce-selected', null ); + } + + selected = null; + } + + // Check if the `wp.mce` API exists. + if ( typeof wp === 'undefined' || ! wp.mce ) { + return { + getViewText: _noop, + setViewText: _noop, + getView: _noop + }; + } + + // Remove the content of view wrappers from HTML string + function emptyViews( content ) { + return content.replace(/]+data-wpview-text=\"([^"]+)"[^>]*>[\s\S]+?wpview-selection-after[^>]+>(?: |\u00a0)*<\/p><\/div>/g, '$1' ); + } + + // Prevent adding undo levels on changes inside a view wrapper + editor.on( 'BeforeAddUndo', function( event ) { + if ( event.lastLevel && emptyViews( event.level.content ) === emptyViews( event.lastLevel.content ) ) { + event.preventDefault(); + } + }); + + // When the editor's content changes, scan the new content for + // matching view patterns, and transform the matches into + // view wrappers. + editor.on( 'BeforeSetContent', function( event ) { + var node; + + if ( ! event.content ) { + return; + } + + if ( selected ) { + removeView( selected ); + } + + node = editor.selection.getNode(); + + // When a url is pasted, only try to embed it when pasted in an empty paragrapgh. + if ( event.content.match( /^\s*(https?:\/\/[^\s"]+)\s*$/i ) && + ( node.nodeName !== 'P' || node.parentNode !== editor.getBody() || ! editor.dom.isEmpty( node ) ) ) { + return; + } + + event.content = wp.mce.views.toViews( event.content ); + }); + + // When the editor's content has been updated and the DOM has been + // processed, render the views in the document. + editor.on( 'SetContent', function() { + wp.mce.views.render(); + }); + + // Set the cursor before or after a view when clicking next to it. + editor.on( 'click', function( event ) { + var x = event.clientX, + y = event.clientY, + body = editor.getBody(), + bodyRect = body.getBoundingClientRect(), + first = body.firstChild, + firstRect = first.getBoundingClientRect(), + last = body.lastChild, + lastRect = last.getBoundingClientRect(), + view; + + if ( y < firstRect.top && ( view = getView( first ) ) ) { + setViewCursor( true, view ); + event.preventDefault(); + } else if ( y > lastRect.bottom && ( view = getView( last ) ) ) { + setViewCursor( false, view ); + event.preventDefault(); + } else { + tinymce.each( editor.dom.select( '.wpview-wrap' ), function( view ) { + var rect = view.getBoundingClientRect(); + + if ( y >= rect.top && y <= rect.bottom ) { + if ( x < bodyRect.left ) { + setViewCursor( true, view ); + event.preventDefault(); + } else if ( x > bodyRect.right ) { + setViewCursor( false, view ); + event.preventDefault(); + } + return; + } + }); + } + }); + + editor.on( 'init', function() { + var scrolled = false, + selection = editor.selection, + MutationObserver = window.MutationObserver || window.WebKitMutationObserver; + + // When a view is selected, ensure content that is being pasted + // or inserted is added to a text node (instead of the view). + editor.on( 'BeforeSetContent', function() { + var walker, target, + view = getView( selection.getNode() ); + + // If the selection is not within a view, bail. + if ( ! view ) { + return; + } + + if ( ! view.nextSibling || getView( view.nextSibling ) ) { + // If there are no additional nodes or the next node is a + // view, create a text node after the current view. + target = editor.getDoc().createTextNode(''); + editor.dom.insertAfter( target, view ); + } else { + // Otherwise, find the next text node. + walker = new TreeWalker( view.nextSibling, view.nextSibling ); + target = walker.next(); + } + + // Select the `target` text node. + selection.select( target ); + selection.collapse( true ); + }); + + editor.dom.bind( editor.getDoc(), 'touchmove', function() { + scrolled = true; + }); + + editor.on( 'mousedown mouseup click touchend', function( event ) { + var view = getView( event.target ); + + firstFocus = false; + + // Contain clicks inside the view wrapper + if ( view ) { + event.stopImmediatePropagation(); + event.preventDefault(); + + if ( ( event.type === 'touchend' || event.type === 'mousedown' ) && ! event.metaKey && ! event.ctrlKey ) { + if ( editor.dom.hasClass( event.target, 'edit' ) ) { + wp.mce.views.edit( view ); + editor.focus(); + return false; + } else if ( editor.dom.hasClass( event.target, 'remove' ) ) { + removeView( view ); + return false; + } + } + + if ( event.type === 'touchend' && scrolled ) { + scrolled = false; + } else { + select( view ); + } + + // Returning false stops the ugly bars from appearing in IE11 and stops the view being selected as a range in FF. + // Unfortunately, it also inhibits the dragging of views to a new location. + return false; + } else { + if ( event.type === 'touchend' || event.type === 'mousedown' ) { + deselect(); + } + } + + if ( event.type === 'touchend' && scrolled ) { + scrolled = false; + } + }, true ); + + if ( MutationObserver ) { + new MutationObserver( function() { + editor.fire( 'wp-body-class-change' ); + } ) + .observe( editor.getBody(), { + attributes: true, + attributeFilter: ['class'] + } ); + } + }); + + editor.on( 'PreProcess', function( event ) { + // Empty the wpview wrap nodes + tinymce.each( editor.dom.select( 'div[data-wpview-text]', event.node ), function( node ) { + node.textContent = node.innerText = '\u00a0'; + }); + }); + + editor.on( 'PostProcess', function( event ) { + if ( event.content ) { + event.content = event.content.replace( /
        ]*?data-wpview-text="([^"]*)"[^>]*>[\s\S]*?<\/div>/g, function( match, shortcode ) { + if ( shortcode ) { + return '

        ' + window.decodeURIComponent( shortcode ) + '

        '; + } + return ''; // If error, remove the view wrapper + }); + } + }); + + // Excludes arrow keys, delete, backspace, enter, space bar. + // Ref: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.keyCode + function isSpecialKey( key ) { + return ( ( key <= 47 && key !== VK.SPACEBAR && key !== VK.ENTER && key !== VK.DELETE && key !== VK.BACKSPACE && ( key < 37 || key > 40 ) ) || + key >= 224 || // OEM or non-printable + ( key >= 144 && key <= 150 ) || // Num Lock, Scroll Lock, OEM + ( key >= 91 && key <= 93 ) || // Windows keys + ( key >= 112 && key <= 135 ) ); // F keys + } + + // (De)select views when arrow keys are used to navigate the content of the editor. + editor.on( 'keydown', function( event ) { + var key = event.keyCode, + dom = editor.dom, + selection = editor.selection, + node, view, cursorBefore, cursorAfter, + range, clonedRange, tempRange; + + if ( selected ) { + // Ignore key presses that involve the command or control key, but continue when in combination with backspace or v. + // Also ignore the F# keys. + if ( ( ( event.metaKey || event.ctrlKey ) && key !== VK.BACKSPACE && key !== 86 ) || ( key >= 112 && key <= 123 ) ) { + // Remove the view when pressing cmd/ctrl+x on keyup, otherwise the browser can't copy the content. + if ( ( event.metaKey || event.ctrlKey ) && key === 88 ) { + toRemove = selected; + } + return; + } + + view = getView( selection.getNode() ); + + // If the caret is not within the selected view, deselect the view and bail. + if ( view !== selected ) { + deselect(); + return; + } + + if ( key === VK.LEFT ) { + setViewCursor( true, view ); + event.preventDefault(); + } else if ( key === VK.UP ) { + if ( view.previousSibling ) { + if ( getView( view.previousSibling ) ) { + setViewCursor( true, view.previousSibling ); + } else { + deselect(); + selection.select( view.previousSibling, true ); + selection.collapse(); + } + } else { + setViewCursor( true, view ); + } + event.preventDefault(); + } else if ( key === VK.RIGHT ) { + setViewCursor( false, view ); + event.preventDefault(); + } else if ( key === VK.DOWN ) { + if ( view.nextSibling ) { + if ( getView( view.nextSibling ) ) { + setViewCursor( false, view.nextSibling ); + } else { + deselect(); + selection.setCursorLocation( view.nextSibling, 0 ); + } + } else { + setViewCursor( false, view ); + } + + event.preventDefault(); + // Ignore keys that don't insert anything. + } else if ( ! isSpecialKey( key ) ) { + removeView( selected ); + + if ( key === VK.ENTER || key === VK.DELETE || key === VK.BACKSPACE ) { + event.preventDefault(); + } + } + } else { + if ( event.metaKey || event.ctrlKey || ( key >= 112 && key <= 123 ) ) { + return; + } + + node = selection.getNode(); + lastKeyDownNode = node; + view = getView( node ); + + // Make sure we don't delete part of a view. + // If the range ends or starts with the view, we'll need to trim it. + if ( ! selection.isCollapsed() ) { + range = selection.getRng(); + + if ( view = getView( range.endContainer ) ) { + clonedRange = range.cloneRange(); + selection.select( view.previousSibling, true ); + selection.collapse(); + tempRange = selection.getRng(); + clonedRange.setEnd( tempRange.endContainer, tempRange.endOffset ); + selection.setRng( clonedRange ); + } else if ( view = getView( range.startContainer ) ) { + clonedRange = range.cloneRange(); + clonedRange.setStart( view.nextSibling, 0 ); + selection.setRng( clonedRange ); + } + } + + if ( ! view ) { + // Make sure we don't eat any content. + if ( event.keyCode === VK.BACKSPACE ) { + if ( editor.dom.isEmpty( node ) ) { + if ( view = getView( node.previousSibling ) ) { + setViewCursor( false, view ); + editor.dom.remove( node ); + event.preventDefault(); + } + } else if ( ( range = selection.getRng() ) && + range.startOffset === 0 && + range.endOffset === 0 && + ( view = getView( node.previousSibling ) ) ) { + setViewCursor( false, view ); + event.preventDefault(); + } + } + return; + } + + if ( ! ( ( cursorBefore = dom.hasClass( view, 'wpview-selection-before' ) ) || + ( cursorAfter = dom.hasClass( view, 'wpview-selection-after' ) ) ) ) { + return; + } + + if ( isSpecialKey( key ) ) { + // ignore + return; + } + + if ( ( cursorAfter && key === VK.UP ) || ( cursorBefore && key === VK.BACKSPACE ) ) { + if ( view.previousSibling ) { + if ( getView( view.previousSibling ) ) { + setViewCursor( false, view.previousSibling ); + } else { + if ( dom.isEmpty( view.previousSibling ) && key === VK.BACKSPACE ) { + dom.remove( view.previousSibling ); + } else { + selection.select( view.previousSibling, true ); + selection.collapse(); + } + } + } else { + setViewCursor( true, view ); + } + event.preventDefault(); + } else if ( cursorAfter && ( key === VK.DOWN || key === VK.RIGHT ) ) { + if ( view.nextSibling ) { + if ( getView( view.nextSibling ) ) { + setViewCursor( key === VK.RIGHT, view.nextSibling ); + } else { + selection.setCursorLocation( view.nextSibling, 0 ); + } + } + event.preventDefault(); + } else if ( cursorBefore && ( key === VK.UP || key === VK.LEFT ) ) { + if ( view.previousSibling ) { + if ( getView( view.previousSibling ) ) { + setViewCursor( key === VK.UP, view.previousSibling ); + } else { + selection.select( view.previousSibling, true ); + selection.collapse(); + } + } + event.preventDefault(); + } else if ( cursorBefore && key === VK.DOWN ) { + if ( view.nextSibling ) { + if ( getView( view.nextSibling ) ) { + setViewCursor( true, view.nextSibling ); + } else { + selection.setCursorLocation( view.nextSibling, 0 ); + } + } else { + setViewCursor( false, view ); + } + event.preventDefault(); + } else if ( ( cursorAfter && key === VK.LEFT ) || ( cursorBefore && key === VK.RIGHT ) ) { + select( view ); + event.preventDefault(); + } else if ( cursorAfter && key === VK.BACKSPACE ) { + removeView( view ); + event.preventDefault(); + } else if ( cursorAfter ) { + handleEnter( view ); + } else if ( cursorBefore ) { + handleEnter( view , true, key ); + } + + if ( key === VK.ENTER ) { + event.preventDefault(); + } + } + }); + + editor.on( 'keyup', function() { + if ( toRemove ) { + removeView( toRemove ); + toRemove = false; + } + }); + + editor.on( 'focus', function() { + var view; + + focus = true; + editor.dom.addClass( editor.getBody(), 'has-focus' ); + + // Edge case: show the fake caret when the editor is focused for the first time + // and the first element is a view. + if ( firstFocus && ( view = getView( editor.getBody().firstChild ) ) ) { + setViewCursor( true, view ); + } + + firstFocus = false; + } ); + + editor.on( 'blur', function() { + focus = false; + editor.dom.removeClass( editor.getBody(), 'has-focus' ); + } ); + + editor.on( 'NodeChange', function( event ) { + var dom = editor.dom, + views = editor.dom.select( '.wpview-wrap' ), + className = event.element.className, + view = getView( event.element ), + lKDN = lastKeyDownNode; + + lastKeyDownNode = false; + + clearInterval( cursorInterval ); + + // This runs a lot and is faster than replacing each class separately + tinymce.each( views, function ( view ) { + if ( view.className ) { + view.className = view.className.replace( / ?\bwpview-(?:selection-before|selection-after|cursor-hide)\b/g, '' ); + } + }); + + if ( focus && view ) { + if ( ( className === 'wpview-selection-before' || className === 'wpview-selection-after' ) && + editor.selection.isCollapsed() ) { + + setViewCursorTries = 0; + + deselect(); + + // Make sure the cursor arrived in the right node. + // This is necessary for Firefox. + if ( lKDN === view.previousSibling ) { + setViewCursor( true, view ); + return; + } else if ( lKDN === view.nextSibling ) { + setViewCursor( false, view ); + return; + } + + dom.addClass( view, className ); + + cursorInterval = setInterval( function() { + if ( dom.hasClass( view, 'wpview-cursor-hide' ) ) { + dom.removeClass( view, 'wpview-cursor-hide' ); + } else { + dom.addClass( view, 'wpview-cursor-hide' ); + } + }, 500 ); + // If the cursor lands anywhere else in the view, set the cursor before it. + // Only try this once to prevent a loop. (You never know.) + } else if ( ! getParent( event.element, 'wpview-clipboard' ) && ! setViewCursorTries ) { + deselect(); + setViewCursorTries++; + setViewCursor( true, view ); + } + } + }); + + editor.on( 'BeforeExecCommand', function() { + var node = editor.selection.getNode(), + view; + + if ( node && ( ( execCommandBefore = node.className === 'wpview-selection-before' ) || node.className === 'wpview-selection-after' ) && ( view = getView( node ) ) ) { + handleEnter( view, execCommandBefore ); + execCommandView = view; + } + }); + + editor.on( 'ExecCommand', function() { + var toSelect, node; + + if ( selected ) { + toSelect = selected; + deselect(); + select( toSelect ); + } + + if ( execCommandView ) { + node = execCommandView[ execCommandBefore ? 'previousSibling' : 'nextSibling' ]; + + if ( node && node.nodeName === 'P' && editor.dom.isEmpty( node ) ) { + editor.dom.remove( node ); + setViewCursor( execCommandBefore, execCommandView ); + } + + execCommandView = false; + } + }); + + editor.on( 'ResolveName', function( event ) { + if ( editor.dom.hasClass( event.target, 'wpview-wrap' ) ) { + event.name = editor.dom.getAttrib( event.target, 'data-wpview-type' ) || 'wpview'; + event.stopPropagation(); + } else if ( getView( event.target ) ) { + event.preventDefault(); + event.stopPropagation(); + } + }); + + return { + getViewText: getViewText, + setViewText: setViewText, + getView: getView + }; +}); diff --git a/wp-includes/js/tinymce/plugins/wpview/plugin.min.js b/wp-includes/js/tinymce/plugins/wpview/plugin.min.js new file mode 100644 index 0000000..6cdef61 --- /dev/null +++ b/wp-includes/js/tinymce/plugins/wpview/plugin.min.js @@ -0,0 +1 @@ +tinymce.PluginManager.add("wpview",function(a){function b(a){return c(a,"wpview-wrap")}function c(a,b){for(;a&&a.parentNode;){if(a.className&&-1!==(" "+a.className+" ").indexOf(" "+b+" "))return a;a=a.parentNode}return!1}function d(c){return(c=b(c))?window.decodeURIComponent(a.dom.getAttrib(c,"data-wpview-text")||""):""}function e(c,d){return c=b(c),c?(a.dom.setAttrib(c,"data-wpview-text",window.encodeURIComponent(d||"")),!0):!1}function f(a){a.stopPropagation()}function g(b,c){var d=b?"before":"after",e=b?0:1;l(),a.selection.setCursorLocation(a.dom.select(".wpview-selection-"+d,c)[0],e),a.nodeChanged()}function h(b,c,d){var e=a.dom,f=e.create("p");v.ie&&v.ie<11||(f.innerHTML='
        '),c?b.parentNode.insertBefore(f,b):e.insertAfter(f,b),l(),c&&d===w.ENTER?g(c,b):a.selection.setCursorLocation(f,0),a.nodeChanged()}function i(b){a.undoManager.transact(function(){h(b),a.dom.remove(b)})}function j(b){var c,e=a.dom;if(b){if(b===o)return void k(b);a.getBody().focus(),l(),o=b,e.setAttrib(b,"data-mce-selected",1),k(b),c=e.create("div",{"class":"wpview-clipboard",contenteditable:"true"},d(b)),a.dom.select(".wpview-body",b)[0].appendChild(c),e.bind(c,"beforedeactivate focusin focusout",f),e.bind(o,"beforedeactivate focusin focusout",f),B?a.selection.select(c):a.selection.select(c,!0),a.nodeChanged(),a.fire("wpview-selected",b)}}function k(b){var c=0,d=a.$(b).find(".toolbar"),e=tinymce.$(a.editorContainer).find(".mce-toolbar-grp")[0],f=e&&e.getBoundingClientRect().bottom||0;d.length&&a.iframeElement&&(c=b.getBoundingClientRect().top+a.iframeElement.getBoundingClientRect().top-f-48),0>c?d.removeClass("mce-arrow-down").css({top:-43+-1*c}):c>0&&!d.hasClass("mce-arrow-down")&&d.addClass("mce-arrow-down").css({top:""})}function l(){var b,c=a.dom;o&&(b=a.dom.select(".wpview-clipboard",o)[0],c.unbind(b),c.remove(b),c.unbind(o,"beforedeactivate focusin focusout click mouseup",f),c.setAttrib(o,"data-mce-selected",null)),o=null}function m(a){return a.replace(/]+data-wpview-text=\"([^"]+)"[^>]*>[\s\S]+?wpview-selection-after[^>]+>(?: |\u00a0)*<\/p><\/div>/g,"$1")}function n(a){return 47>=a&&a!==w.SPACEBAR&&a!==w.ENTER&&a!==w.DELETE&&a!==w.BACKSPACE&&(37>a||a>40)||a>=224||a>=144&&150>=a||a>=91&&93>=a||a>=112&&135>=a}var o,p,q,r,s,t,u,v=tinymce.Env,w=tinymce.util.VK,x=tinymce.dom.TreeWalker,y=!1,z=!0,A=function(){return!1},B=/iPad|iPod|iPhone/.test(navigator.userAgent);return"undefined"!=typeof wp&&wp.mce?(a.on("BeforeAddUndo",function(a){a.lastLevel&&m(a.level.content)===m(a.lastLevel.content)&&a.preventDefault()}),a.on("BeforeSetContent",function(b){var c;b.content&&(o&&i(o),c=a.selection.getNode(),(!b.content.match(/^\s*(https?:\/\/[^\s"]+)\s*$/i)||"P"===c.nodeName&&c.parentNode===a.getBody()&&a.dom.isEmpty(c))&&(b.content=wp.mce.views.toViews(b.content)))}),a.on("SetContent",function(){wp.mce.views.render()}),a.on("click",function(c){var d,e=c.clientX,f=c.clientY,h=a.getBody(),i=h.getBoundingClientRect(),j=h.firstChild,k=j.getBoundingClientRect(),l=h.lastChild,m=l.getBoundingClientRect();fm.bottom&&(d=b(l))?(g(!1,d),c.preventDefault()):tinymce.each(a.dom.select(".wpview-wrap"),function(a){var b=a.getBoundingClientRect();return f>=b.top&&f<=b.bottom?void(ei.right&&(g(!1,a),c.preventDefault())):void 0})}),a.on("init",function(){var c=!1,d=a.selection,e=window.MutationObserver||window.WebKitMutationObserver;a.on("BeforeSetContent",function(){var c,e,f=b(d.getNode());f&&(!f.nextSibling||b(f.nextSibling)?(e=a.getDoc().createTextNode(""),a.dom.insertAfter(e,f)):(c=new x(f.nextSibling,f.nextSibling),e=c.next()),d.select(e),d.collapse(!0))}),a.dom.bind(a.getDoc(),"touchmove",function(){c=!0}),a.on("mousedown mouseup click touchend",function(d){var e=b(d.target);if(z=!1,e){if(d.stopImmediatePropagation(),d.preventDefault(),!("touchend"!==d.type&&"mousedown"!==d.type||d.metaKey||d.ctrlKey)){if(a.dom.hasClass(d.target,"edit"))return wp.mce.views.edit(e),a.focus(),!1;if(a.dom.hasClass(d.target,"remove"))return i(e),!1}return"touchend"===d.type&&c?c=!1:j(e),!1}("touchend"===d.type||"mousedown"===d.type)&&l(),"touchend"===d.type&&c&&(c=!1)},!0),e&&new e(function(){a.fire("wp-body-class-change")}).observe(a.getBody(),{attributes:!0,attributeFilter:["class"]})}),a.on("PreProcess",function(b){tinymce.each(a.dom.select("div[data-wpview-text]",b.node),function(a){a.textContent=a.innerText=" "})}),a.on("PostProcess",function(a){a.content&&(a.content=a.content.replace(/
        ]*?data-wpview-text="([^"]*)"[^>]*>[\s\S]*?<\/div>/g,function(a,b){return b?"

        "+window.decodeURIComponent(b)+"

        ":""}))}),a.on("keydown",function(c){var d,e,f,k,m,p,r,s=c.keyCode,t=a.dom,u=a.selection;if(o){if((c.metaKey||c.ctrlKey)&&s!==w.BACKSPACE&&86!==s||s>=112&&123>=s)return void((c.metaKey||c.ctrlKey)&&88===s&&(y=o));if(e=b(u.getNode()),e!==o)return void l();s===w.LEFT?(g(!0,e),c.preventDefault()):s===w.UP?(e.previousSibling?b(e.previousSibling)?g(!0,e.previousSibling):(l(),u.select(e.previousSibling,!0),u.collapse()):g(!0,e),c.preventDefault()):s===w.RIGHT?(g(!1,e),c.preventDefault()):s===w.DOWN?(e.nextSibling?b(e.nextSibling)?g(!1,e.nextSibling):(l(),u.setCursorLocation(e.nextSibling,0)):g(!1,e),c.preventDefault()):n(s)||(i(o),(s===w.ENTER||s===w.DELETE||s===w.BACKSPACE)&&c.preventDefault())}else{if(c.metaKey||c.ctrlKey||s>=112&&123>=s)return;if(d=u.getNode(),q=d,e=b(d),u.isCollapsed()||(m=u.getRng(),(e=b(m.endContainer))?(p=m.cloneRange(),u.select(e.previousSibling,!0),u.collapse(),r=u.getRng(),p.setEnd(r.endContainer,r.endOffset),u.setRng(p)):(e=b(m.startContainer))&&(p=m.cloneRange(),p.setStart(e.nextSibling,0),u.setRng(p))),!e)return void(c.keyCode===w.BACKSPACE&&(a.dom.isEmpty(d)?(e=b(d.previousSibling))&&(g(!1,e),a.dom.remove(d),c.preventDefault()):(m=u.getRng())&&0===m.startOffset&&0===m.endOffset&&(e=b(d.previousSibling))&&(g(!1,e),c.preventDefault())));if(!(f=t.hasClass(e,"wpview-selection-before"))&&!(k=t.hasClass(e,"wpview-selection-after")))return;if(n(s))return;k&&s===w.UP||f&&s===w.BACKSPACE?(e.previousSibling?b(e.previousSibling)?g(!1,e.previousSibling):t.isEmpty(e.previousSibling)&&s===w.BACKSPACE?t.remove(e.previousSibling):(u.select(e.previousSibling,!0),u.collapse()):g(!0,e),c.preventDefault()):!k||s!==w.DOWN&&s!==w.RIGHT?!f||s!==w.UP&&s!==w.LEFT?f&&s===w.DOWN?(e.nextSibling?b(e.nextSibling)?g(!0,e.nextSibling):u.setCursorLocation(e.nextSibling,0):g(!1,e),c.preventDefault()):k&&s===w.LEFT||f&&s===w.RIGHT?(j(e),c.preventDefault()):k&&s===w.BACKSPACE?(i(e),c.preventDefault()):k?h(e):f&&h(e,!0,s):(e.previousSibling&&(b(e.previousSibling)?g(s===w.UP,e.previousSibling):(u.select(e.previousSibling,!0),u.collapse())),c.preventDefault()):(e.nextSibling&&(b(e.nextSibling)?g(s===w.RIGHT,e.nextSibling):u.setCursorLocation(e.nextSibling,0)),c.preventDefault()),s===w.ENTER&&c.preventDefault()}}),a.on("keyup",function(){y&&(i(y),y=!1)}),a.on("focus",function(){var c;s=!0,a.dom.addClass(a.getBody(),"has-focus"),z&&(c=b(a.getBody().firstChild))&&g(!0,c),z=!1}),a.on("blur",function(){s=!1,a.dom.removeClass(a.getBody(),"has-focus")}),a.on("NodeChange",function(d){var e=a.dom,f=a.dom.select(".wpview-wrap"),h=d.element.className,i=b(d.element),j=q;if(q=!1,clearInterval(p),tinymce.each(f,function(a){a.className&&(a.className=a.className.replace(/ ?\bwpview-(?:selection-before|selection-after|cursor-hide)\b/g,""))}),s&&i)if("wpview-selection-before"!==h&&"wpview-selection-after"!==h||!a.selection.isCollapsed())c(d.element,"wpview-clipboard")||r||(l(),r++,g(!0,i));else{if(r=0,l(),j===i.previousSibling)return void g(!0,i);if(j===i.nextSibling)return void g(!1,i);e.addClass(i,h),p=setInterval(function(){e.hasClass(i,"wpview-cursor-hide")?e.removeClass(i,"wpview-cursor-hide"):e.addClass(i,"wpview-cursor-hide")},500)}}),a.on("BeforeExecCommand",function(){var c,d=a.selection.getNode();d&&((u="wpview-selection-before"===d.className)||"wpview-selection-after"===d.className)&&(c=b(d))&&(h(c,u),t=c)}),a.on("ExecCommand",function(){var b,c;o&&(b=o,l(),j(b)),t&&(c=t[u?"previousSibling":"nextSibling"],c&&"P"===c.nodeName&&a.dom.isEmpty(c)&&(a.dom.remove(c),g(u,t)),t=!1)}),a.on("ResolveName",function(c){a.dom.hasClass(c.target,"wpview-wrap")?(c.name=a.dom.getAttrib(c.target,"data-wpview-type")||"wpview",c.stopPropagation()):b(c.target)&&(c.preventDefault(),c.stopPropagation())}),{getViewText:d,setViewText:e,getView:b}):{getViewText:A,setViewText:A,getView:A}}); \ No newline at end of file diff --git a/wp-includes/js/tinymce/skins/lightgray/content.inline.min.css b/wp-includes/js/tinymce/skins/lightgray/content.inline.min.css new file mode 100644 index 0000000..7e9e4e8 --- /dev/null +++ b/wp-includes/js/tinymce/skins/lightgray/content.inline.min.css @@ -0,0 +1 @@ +.mce-object{border:1px dotted #3A3A3A;background:#d5d5d5 url(img/object.gif) no-repeat center}.mce-pagebreak{cursor:default;display:block;border:0;width:100%;height:5px;border:1px dashed #666;margin-top:15px;page-break-before:always}@media print{.mce-pagebreak{border:0}}.mce-item-anchor{cursor:default;display:inline-block;-webkit-user-select:all;-webkit-user-modify:read-only;-moz-user-select:all;-moz-user-modify:read-only;user-select:all;user-modify:read-only;width:9px!important;height:9px!important;border:1px dotted #3A3A3A;background:#d5d5d5 url(img/anchor.gif) no-repeat center}.mce-nbsp{background:#AAA}hr{cursor:default}.mce-match-marker{background:#AAA;color:#fff}.mce-match-marker-selected{background:#39f;color:#fff}.mce-spellchecker-word{border-bottom:2px solid red;cursor:default}.mce-spellchecker-grammar{border-bottom:2px solid green;cursor:default}.mce-item-table,.mce-item-table td,.mce-item-table th,.mce-item-table caption{border:1px dashed #BBB}td.mce-item-selected,th.mce-item-selected{background-color:#39f!important}.mce-edit-focus{outline:1px dotted #333} \ No newline at end of file diff --git a/wp-includes/js/tinymce/skins/lightgray/content.min.css b/wp-includes/js/tinymce/skins/lightgray/content.min.css new file mode 100644 index 0000000..1005b05 --- /dev/null +++ b/wp-includes/js/tinymce/skins/lightgray/content.min.css @@ -0,0 +1 @@ +body{background-color:#FFF;color:#000;font-family:Verdana,Arial,Helvetica,sans-serif;font-size:11px;scrollbar-3dlight-color:#F0F0EE;scrollbar-arrow-color:#676662;scrollbar-base-color:#F0F0EE;scrollbar-darkshadow-color:#DDD;scrollbar-face-color:#E0E0DD;scrollbar-highlight-color:#F0F0EE;scrollbar-shadow-color:#F0F0EE;scrollbar-track-color:#F5F5F5}td,th{font-family:Verdana,Arial,Helvetica,sans-serif;font-size:11px}.mce-object{border:1px dotted #3A3A3A;background:#d5d5d5 url(img/object.gif) no-repeat center}.mce-pagebreak{cursor:default;display:block;border:0;width:100%;height:5px;border:1px dashed #666;margin-top:15px;page-break-before:always}@media print{.mce-pagebreak{border:0}}.mce-item-anchor{cursor:default;display:inline-block;-webkit-user-select:all;-webkit-user-modify:read-only;-moz-user-select:all;-moz-user-modify:read-only;user-select:all;user-modify:read-only;width:9px!important;height:9px!important;border:1px dotted #3A3A3A;background:#d5d5d5 url(img/anchor.gif) no-repeat center}.mce-nbsp{background:#AAA}hr{cursor:default}.mce-match-marker{background:#AAA;color:#fff}.mce-match-marker-selected{background:#39f;color:#fff}.mce-spellchecker-word{border-bottom:2px solid red;cursor:default}.mce-spellchecker-grammar{border-bottom:2px solid green;cursor:default}.mce-item-table,.mce-item-table td,.mce-item-table th,.mce-item-table caption{border:1px dashed #BBB}td.mce-item-selected,th.mce-item-selected{background-color:#39f!important}.mce-edit-focus{outline:1px dotted #333} \ No newline at end of file diff --git a/wp-includes/js/tinymce/skins/lightgray/fonts/readme.md b/wp-includes/js/tinymce/skins/lightgray/fonts/readme.md new file mode 100644 index 0000000..fa5d639 --- /dev/null +++ b/wp-includes/js/tinymce/skins/lightgray/fonts/readme.md @@ -0,0 +1 @@ +Icons are generated and provided by the http://icomoon.io service. diff --git a/wp-includes/js/tinymce/skins/lightgray/fonts/tinymce-small.eot b/wp-includes/js/tinymce/skins/lightgray/fonts/tinymce-small.eot new file mode 100644 index 0000000..60e2d2e Binary files /dev/null and b/wp-includes/js/tinymce/skins/lightgray/fonts/tinymce-small.eot differ diff --git a/wp-includes/js/tinymce/skins/lightgray/fonts/tinymce-small.svg b/wp-includes/js/tinymce/skins/lightgray/fonts/tinymce-small.svg new file mode 100644 index 0000000..930c48d --- /dev/null +++ b/wp-includes/js/tinymce/skins/lightgray/fonts/tinymce-small.svg @@ -0,0 +1,62 @@ + + + +Generated by IcoMoon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/wp-includes/js/tinymce/skins/lightgray/fonts/tinymce-small.ttf b/wp-includes/js/tinymce/skins/lightgray/fonts/tinymce-small.ttf new file mode 100644 index 0000000..afc6ec4 Binary files /dev/null and b/wp-includes/js/tinymce/skins/lightgray/fonts/tinymce-small.ttf differ diff --git a/wp-includes/js/tinymce/skins/lightgray/fonts/tinymce-small.woff b/wp-includes/js/tinymce/skins/lightgray/fonts/tinymce-small.woff new file mode 100644 index 0000000..fa72c74 Binary files /dev/null and b/wp-includes/js/tinymce/skins/lightgray/fonts/tinymce-small.woff differ diff --git a/wp-includes/js/tinymce/skins/lightgray/fonts/tinymce.eot b/wp-includes/js/tinymce/skins/lightgray/fonts/tinymce.eot new file mode 100644 index 0000000..c1085bf Binary files /dev/null and b/wp-includes/js/tinymce/skins/lightgray/fonts/tinymce.eot differ diff --git a/wp-includes/js/tinymce/skins/lightgray/fonts/tinymce.svg b/wp-includes/js/tinymce/skins/lightgray/fonts/tinymce.svg new file mode 100644 index 0000000..feb9ba3 --- /dev/null +++ b/wp-includes/js/tinymce/skins/lightgray/fonts/tinymce.svg @@ -0,0 +1,63 @@ + + + +Generated by IcoMoon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/wp-includes/js/tinymce/skins/lightgray/fonts/tinymce.ttf b/wp-includes/js/tinymce/skins/lightgray/fonts/tinymce.ttf new file mode 100644 index 0000000..58103c2 Binary files /dev/null and b/wp-includes/js/tinymce/skins/lightgray/fonts/tinymce.ttf differ diff --git a/wp-includes/js/tinymce/skins/lightgray/fonts/tinymce.woff b/wp-includes/js/tinymce/skins/lightgray/fonts/tinymce.woff new file mode 100644 index 0000000..ad1ae39 Binary files /dev/null and b/wp-includes/js/tinymce/skins/lightgray/fonts/tinymce.woff differ diff --git a/wp-includes/js/tinymce/skins/lightgray/img/anchor.gif b/wp-includes/js/tinymce/skins/lightgray/img/anchor.gif new file mode 100644 index 0000000..606348c Binary files /dev/null and b/wp-includes/js/tinymce/skins/lightgray/img/anchor.gif differ diff --git a/wp-includes/js/tinymce/skins/lightgray/img/loader.gif b/wp-includes/js/tinymce/skins/lightgray/img/loader.gif new file mode 100644 index 0000000..c69e937 Binary files /dev/null and b/wp-includes/js/tinymce/skins/lightgray/img/loader.gif differ diff --git a/wp-includes/js/tinymce/skins/lightgray/img/object.gif b/wp-includes/js/tinymce/skins/lightgray/img/object.gif new file mode 100644 index 0000000..cccd7f0 Binary files /dev/null and b/wp-includes/js/tinymce/skins/lightgray/img/object.gif differ diff --git a/wp-includes/js/tinymce/skins/lightgray/img/trans.gif b/wp-includes/js/tinymce/skins/lightgray/img/trans.gif new file mode 100644 index 0000000..3884865 Binary files /dev/null and b/wp-includes/js/tinymce/skins/lightgray/img/trans.gif differ diff --git a/wp-includes/js/tinymce/skins/lightgray/skin.ie7.min.css b/wp-includes/js/tinymce/skins/lightgray/skin.ie7.min.css new file mode 100644 index 0000000..f2ca5b9 --- /dev/null +++ b/wp-includes/js/tinymce/skins/lightgray/skin.ie7.min.css @@ -0,0 +1 @@ +.mce-container,.mce-container *,.mce-widget,.mce-widget *,.mce-reset{margin:0;padding:0;border:0;outline:0;vertical-align:top;background:transparent;text-decoration:none;color:#333;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;text-shadow:none;float:none;position:static;width:auto;height:auto;white-space:nowrap;cursor:inherit;-webkit-tap-highlight-color:transparent;line-height:normal;font-weight:normal;text-align:left;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;direction:ltr;max-width:none}.mce-widget button{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}.mce-container *[unselectable]{-moz-user-select:none;-webkit-user-select:none;-o-user-select:none;user-select:none}.mce-fade{opacity:0;-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.mce-fade.mce-in{opacity:1}.mce-tinymce{visibility:inherit !important;position:relative}.mce-fullscreen{border:0;padding:0;margin:0;overflow:hidden;height:100%;z-index:100}div.mce-fullscreen{position:fixed;top:0;left:0;width:100%;height:auto}.mce-tinymce{display:block;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}.mce-wordcount{position:absolute;top:0;right:0;padding:8px}div.mce-edit-area{background:#FFF;filter:none}.mce-statusbar{position:relative}.mce-statusbar .mce-container-body{position:relative}.mce-fullscreen .mce-resizehandle{display:none}.mce-charmap{border-collapse:collapse}.mce-charmap td{cursor:default;border:1px solid #9e9e9e;width:20px;height:20px;line-height:20px;text-align:center;vertical-align:middle;padding:2px}.mce-charmap td div{text-align:center}.mce-charmap td:hover{background:#d9d9d9}.mce-grid td.mce-grid-cell div{border:1px solid #d6d6d6;width:15px;height:15px;margin:0px;cursor:pointer}.mce-grid td.mce-grid-cell div:focus{border-color:#a1a1a1}.mce-grid td.mce-grid-cell div[disabled]{cursor:not-allowed}.mce-grid{border-spacing:2px;border-collapse:separate}.mce-grid a{display:block;border:1px solid transparent}.mce-grid a:hover,.mce-grid a:focus{border-color:#a1a1a1}.mce-grid-border{margin:0 4px 0 4px}.mce-grid-border a{border-color:#d6d6d6;width:13px;height:13px}.mce-grid-border a:hover,.mce-grid-border a.mce-active{border-color:#a1a1a1;background:#c8def4}.mce-text-center{text-align:center}div.mce-tinymce-inline{width:100%;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.mce-colorbtn-trans div{text-align:center;vertical-align:middle;font-weight:bold;font-size:20px;line-height:16px;color:#707070}.mce-toolbar-grp{padding-bottom:2px}.mce-toolbar-grp .mce-flow-layout-item{margin-bottom:0}.mce-rtl .mce-wordcount{left:0;right:auto}.mce-container,.mce-container-body{display:block}.mce-autoscroll{overflow:hidden}.mce-scrollbar{position:absolute;width:7px;height:100%;top:2px;right:2px;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-scrollbar-h{top:auto;right:auto;left:2px;bottom:2px;width:100%;height:7px}.mce-scrollbar-thumb{position:absolute;background-color:#000;border:1px solid #888;border-color:rgba(85,85,85,0.6);width:5px;height:100%;-webkit-border-radius:7px;-moz-border-radius:7px;border-radius:7px}.mce-scrollbar-h .mce-scrollbar-thumb{width:100%;height:5px}.mce-scrollbar:hover,.mce-scrollbar.mce-active{background-color:#AAA;opacity:.6;filter:alpha(opacity=60);zoom:1;-webkit-border-radius:7px;-moz-border-radius:7px;border-radius:7px}.mce-scroll{position:relative}.mce-panel{border:0 solid #9e9e9e;background-color:#f0f0f0;background-image:-moz-linear-gradient(top, #fdfdfd, #ddd);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fdfdfd), to(#ddd));background-image:-webkit-linear-gradient(top, #fdfdfd, #ddd);background-image:-o-linear-gradient(top, #fdfdfd, #ddd);background-image:linear-gradient(to bottom, #fdfdfd, #ddd);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffdfdfd', endColorstr='#ffdddddd', GradientType=0);zoom:1}.mce-floatpanel{position:absolute;-webkit-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);box-shadow:0 5px 10px rgba(0, 0, 0, 0.2)}.mce-floatpanel.mce-fixed{position:fixed}.mce-floatpanel .mce-arrow,.mce-floatpanel .mce-arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.mce-floatpanel .mce-arrow{border-width:11px}.mce-floatpanel .mce-arrow:after{border-width:10px;content:""}.mce-floatpanel.mce-popover{filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background:transparent;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);top:0;left:0;background:#fff;border:1px solid #9e9e9e;border:1px solid rgba(0,0,0,0.25)}.mce-floatpanel.mce-popover.mce-bottom{margin-top:10px;*margin-top:0}.mce-floatpanel.mce-popover.mce-bottom>.mce-arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#9e9e9e;border-bottom-color:rgba(0,0,0,0.25);top:-11px}.mce-floatpanel.mce-popover.mce-bottom>.mce-arrow:after{top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#fff}.mce-floatpanel.mce-popover.mce-bottom.mce-start{margin-left:-22px}.mce-floatpanel.mce-popover.mce-bottom.mce-start>.mce-arrow{left:20px}.mce-floatpanel.mce-popover.mce-bottom.mce-end{margin-left:22px}.mce-floatpanel.mce-popover.mce-bottom.mce-end>.mce-arrow{right:10px;left:auto}.mce-fullscreen{border:0;padding:0;margin:0;overflow:hidden;background:#fff;height:100%}div.mce-fullscreen{position:fixed;top:0;left:0}#mce-modal-block{opacity:0;filter:alpha(opacity=0);zoom:1;position:fixed;left:0;top:0;width:100%;height:100%;background:#000}#mce-modal-block.mce-in{opacity:.3;filter:alpha(opacity=30);zoom:1}.mce-window-move{cursor:move}.mce-window{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background:transparent;background:#fff;position:fixed;top:0;left:0;opacity:0;-webkit-transition:opacity 150ms ease-in;transition:opacity 150ms ease-in}.mce-window.mce-in{opacity:1}.mce-window-head{padding:9px 15px;border-bottom:1px solid #c5c5c5;position:relative}.mce-window-head .mce-close{position:absolute;right:15px;top:9px;font-size:20px;font-weight:bold;line-height:20px;color:#858585;cursor:pointer;height:20px;overflow:hidden}.mce-close:hover{color:#adadad}.mce-window-head .mce-title{line-height:20px;font-size:20px;font-weight:bold;text-rendering:optimizelegibility;padding-right:10px}.mce-window .mce-container-body{display:block}.mce-foot{display:block;background-color:#fff;border-top:1px solid #c5c5c5;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.mce-window-head .mce-dragh{position:absolute;top:0;left:0;cursor:move;width:90%;height:100%}.mce-window iframe{width:100%;height:100%}.mce-window.mce-fullscreen,.mce-window.mce-fullscreen .mce-foot{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.mce-rtl .mce-window-head .mce-close{position:absolute;right:auto;left:15px}.mce-rtl .mce-window-head .mce-dragh{left:auto;right:0}.mce-rtl .mce-window-head .mce-title{direction:rtl;text-align:right}.mce-abs-layout{position:relative}body .mce-abs-layout-item,.mce-abs-end{position:absolute}.mce-abs-end{width:1px;height:1px}.mce-container-body.mce-abs-layout{overflow:hidden}.mce-tooltip{position:absolute;padding:5px;opacity:.8;filter:alpha(opacity=80);zoom:1}.mce-tooltip-inner{font-size:11px;background-color:#000;color:#fff;max-width:200px;padding:5px 8px 4px 8px;text-align:center;white-space:normal}.mce-tooltip-inner{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.mce-tooltip-inner{-webkit-box-shadow:0 0 5px #000000;-moz-box-shadow:0 0 5px #000000;box-shadow:0 0 5px #000000}.mce-tooltip-arrow{position:absolute;width:0;height:0;line-height:0;border:5px dashed #000}.mce-tooltip-arrow-n{border-bottom-color:#000}.mce-tooltip-arrow-s{border-top-color:#000}.mce-tooltip-arrow-e{border-left-color:#000}.mce-tooltip-arrow-w{border-right-color:#000}.mce-tooltip-nw,.mce-tooltip-sw{margin-left:-14px}.mce-tooltip-n .mce-tooltip-arrow{top:0px;left:50%;margin-left:-5px;border-bottom-style:solid;border-top:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-nw .mce-tooltip-arrow{top:0;left:10px;border-bottom-style:solid;border-top:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-ne .mce-tooltip-arrow{top:0;right:10px;border-bottom-style:solid;border-top:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-s .mce-tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-style:solid;border-bottom:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-sw .mce-tooltip-arrow{bottom:0;left:10px;border-top-style:solid;border-bottom:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-se .mce-tooltip-arrow{bottom:0;right:10px;border-top-style:solid;border-bottom:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-e .mce-tooltip-arrow{right:0;top:50%;margin-top:-5px;border-left-style:solid;border-right:none;border-top-color:transparent;border-bottom-color:transparent}.mce-tooltip-w .mce-tooltip-arrow{left:0;top:50%;margin-top:-5px;border-right-style:solid;border-left:none;border-top-color:transparent;border-bottom-color:transparent}.mce-btn{border:1px solid #b1b1b1;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25) rgba(0,0,0,0.25);position:relative;text-shadow:0 1px 1px rgba(255,255,255,0.75);display:inline-block;*display:inline;*zoom:1;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);background-color:#f0f0f0;background-image:-moz-linear-gradient(top, #fff, #d9d9d9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fff), to(#d9d9d9));background-image:-webkit-linear-gradient(top, #fff, #d9d9d9);background-image:-o-linear-gradient(top, #fff, #d9d9d9);background-image:linear-gradient(to bottom, #fff, #d9d9d9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffd9d9d9', GradientType=0);zoom:1}.mce-btn:hover,.mce-btn:focus{color:#333;background-color:#e3e3e3;background-image:-moz-linear-gradient(top, #f2f2f2, #ccc);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#f2f2f2), to(#ccc));background-image:-webkit-linear-gradient(top, #f2f2f2, #ccc);background-image:-o-linear-gradient(top, #f2f2f2, #ccc);background-image:linear-gradient(to bottom, #f2f2f2, #ccc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffcccccc', GradientType=0);zoom:1}.mce-btn.mce-disabled button,.mce-btn.mce-disabled:hover button{cursor:default;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-btn.mce-active,.mce-btn.mce-active:hover{background-color:#d6d6d6;background-image:-moz-linear-gradient(top, #e6e6e6, #c0c0c0);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#e6e6e6), to(#c0c0c0));background-image:-webkit-linear-gradient(top, #e6e6e6, #c0c0c0);background-image:-o-linear-gradient(top, #e6e6e6, #c0c0c0);background-image:linear-gradient(to bottom, #e6e6e6, #c0c0c0);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe6e6e6', endColorstr='#ffc0c0c0', GradientType=0);zoom:1;-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05)}.mce-btn:active{background-color:#d6d6d6;background-image:-moz-linear-gradient(top, #e6e6e6, #c0c0c0);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#e6e6e6), to(#c0c0c0));background-image:-webkit-linear-gradient(top, #e6e6e6, #c0c0c0);background-image:-o-linear-gradient(top, #e6e6e6, #c0c0c0);background-image:linear-gradient(to bottom, #e6e6e6, #c0c0c0);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe6e6e6', endColorstr='#ffc0c0c0', GradientType=0);zoom:1;-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05)}.mce-btn button{padding:4px 10px;font-size:14px;line-height:20px;*line-height:16px;cursor:pointer;color:#333;text-align:center;overflow:visible;-webkit-appearance:none}.mce-btn button::-moz-focus-inner{border:0;padding:0}.mce-btn i{text-shadow:1px 1px #fff}.mce-primary{min-width:50px;color:#fff;border:1px solid #b1b1b1;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25) rgba(0,0,0,0.25);background-color:#006dcc;background-image:-moz-linear-gradient(top, #08c, #04c);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#08c), to(#04c));background-image:-webkit-linear-gradient(top, #08c, #04c);background-image:-o-linear-gradient(top, #08c, #04c);background-image:linear-gradient(to bottom, #08c, #04c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0044cc', GradientType=0);zoom:1}.mce-primary:hover,.mce-primary:focus{background-color:#005fb3;background-image:-moz-linear-gradient(top, #0077b3, #003cb3);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#0077b3), to(#003cb3));background-image:-webkit-linear-gradient(top, #0077b3, #003cb3);background-image:-o-linear-gradient(top, #0077b3, #003cb3);background-image:linear-gradient(to bottom, #0077b3, #003cb3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0077b3', endColorstr='#ff003cb3', GradientType=0);zoom:1}.mce-primary.mce-disabled button,.mce-primary.mce-disabled:hover button{cursor:default;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-primary.mce-active,.mce-primary.mce-active:hover,.mce-primary:not(.mce-disabled):active{background-color:#005299;background-image:-moz-linear-gradient(top, #069, #039);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#069), to(#039));background-image:-webkit-linear-gradient(top, #069, #039);background-image:-o-linear-gradient(top, #069, #039);background-image:linear-gradient(to bottom, #069, #039);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff006699', endColorstr='#ff003399', GradientType=0);zoom:1;-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05)}.mce-primary button,.mce-primary button i{color:#fff;text-shadow:1px 1px #333}.mce-btn-large button{padding:9px 14px;font-size:16px;line-height:normal;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.mce-btn-large i{margin-top:2px}.mce-btn-small button{padding:1px 5px;font-size:12px;*padding-bottom:2px}.mce-btn-small i{line-height:20px;vertical-align:top;*line-height:18px}.mce-btn .mce-caret{margin-top:8px;margin-left:0}.mce-btn-small .mce-caret{margin-top:8px;margin-left:0}.mce-caret{display:inline-block;*display:inline;*zoom:1;width:0;height:0;vertical-align:top;border-top:4px solid #333;border-right:4px solid transparent;border-left:4px solid transparent;content:""}.mce-disabled .mce-caret{border-top-color:#aaa}.mce-caret.mce-up{border-bottom:4px solid #333;border-top:0}.mce-btn-flat{border:0;background:transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;filter:none}.mce-btn-flat:hover,.mce-btn-flat.mce-active,.mce-btn-flat:focus,.mce-btn-flat:active{border:0;background:#e6e6e6;filter:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.mce-rtl .mce-btn button{direction:rtl}.mce-btn-group .mce-btn{border-width:1px 0 1px 0;margin:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.mce-btn-group .mce-first{border-left:1px solid #b1b1b1;border-left:1px solid rgba(0,0,0,0.25);-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.mce-btn-group .mce-last{border-right:1px solid #b1b1b1;border-right:1px solid rgba(0,0,0,0.1);-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.mce-btn-group .mce-first.mce-last{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.mce-btn-group .mce-btn.mce-flow-layout-item{margin:0}.mce-checkbox{cursor:pointer}i.mce-i-checkbox{margin:0 3px 0 0;border:1px solid #c5c5c5;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);background-color:#f0f0f0;background-image:-moz-linear-gradient(top, #fff, #d9d9d9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fff), to(#d9d9d9));background-image:-webkit-linear-gradient(top, #fff, #d9d9d9);background-image:-o-linear-gradient(top, #fff, #d9d9d9);background-image:linear-gradient(to bottom, #fff, #d9d9d9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffd9d9d9', GradientType=0);zoom:1;text-indent:-10em;*font-size:0;*line-height:0;*text-indent:0;overflow:hidden}.mce-checked i.mce-i-checkbox{color:#333;font-size:16px;line-height:16px;text-indent:0}.mce-checkbox:focus i.mce-i-checkbox,.mce-checkbox.mce-focus i.mce-i-checkbox{border:1px solid rgba(82,168,236,0.8);-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.65);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.65);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.65)}.mce-checkbox.mce-disabled .mce-label,.mce-checkbox.mce-disabled i.mce-i-checkbox{color:#acacac}.mce-rtl .mce-checkbox{direction:rtl;text-align:right}.mce-rtl i.mce-i-checkbox{margin:0 0 0 3px}.mce-combobox{display:inline-block;*display:inline;*zoom:1;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);*height:32px}.mce-combobox input{border:1px solid #c5c5c5;border-right-color:#c5c5c5;height:28px}.mce-combobox.mce-disabled input{color:#adadad}.mce-combobox.mce-has-open input{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.mce-combobox .mce-btn{border-left:0;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.mce-combobox button{padding-right:8px;padding-left:8px}.mce-combobox.mce-disabled .mce-btn button{cursor:default;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-colorbox i{border:1px solid #c5c5c5;width:14px;height:14px}.mce-colorbutton .mce-ico{position:relative}.mce-colorbutton-grid{margin:4px}.mce-colorbutton button{padding-right:4px}.mce-colorbutton .mce-preview{padding-right:3px;display:block;position:absolute;left:50%;top:50%;margin-left:-14px;margin-top:7px;background:gray;width:13px;height:2px;overflow:hidden}.mce-colorbutton.mce-btn-small .mce-preview{margin-left:-16px;padding-right:0;width:16px}.mce-colorbutton .mce-open{padding-left:4px;border-left:1px solid transparent;border-right:1px solid transparent}.mce-colorbutton:hover .mce-open{border-left-color:#bdbdbd;border-right-color:#bdbdbd}.mce-colorbutton.mce-btn-small .mce-open{padding:0 3px 0 3px}.mce-rtl .mce-colorbutton{direction:rtl}.mce-rtl .mce-colorbutton .mce-preview{margin-left:0;padding-right:0;padding-left:4px;margin-right:-14px}.mce-rtl .mce-colorbutton.mce-btn-small .mce-preview{margin-left:0;padding-right:0;margin-right:-17px;padding-left:0}.mce-rtl .mce-colorbutton button{padding-right:10px;padding-left:10px}.mce-rtl .mce-colorbutton .mce-open{padding-left:4px;padding-right:4px}.mce-colorpicker{position:relative;width:250px;height:220px}.mce-colorpicker-sv{position:absolute;top:0;left:0;width:90%;height:100%;border:1px solid #c5c5c5;cursor:crosshair;overflow:hidden}.mce-colorpicker-h-chunk{width:100%}.mce-colorpicker-overlay1,.mce-colorpicker-overlay2{width:100%;height:100%;position:absolute;top:0;left:0}.mce-colorpicker-overlay1{filter:progid:DXImageTransform.Microsoft.gradient(GradientType=1, startColorstr='#ffffff', endColorstr='#00ffffff');-ms-filter:"progid:DXImageTransform.Microsoft.gradient(GradientType=1,startColorstr='#ffffff', endColorstr='#00ffffff')";background:linear-gradient(to right, #fff, rgba(255,255,255,0))}.mce-colorpicker-overlay2{filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#00000000', endColorstr='#000000');-ms-filter:"progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#00000000', endColorstr='#000000')";background:linear-gradient(to bottom, rgba(0,0,0,0), #000)}.mce-colorpicker-selector1{background:none;position:absolute;width:12px;height:12px;margin:-8px 0 0 -8px;border:1px solid black;border-radius:50%}.mce-colorpicker-selector2{position:absolute;width:10px;height:10px;border:1px solid white;border-radius:50%}.mce-colorpicker-h{position:absolute;top:0;right:0;width:6.5%;height:100%;border:1px solid #c5c5c5;cursor:crosshair}.mce-colorpicker-h-marker{margin-top:-4px;position:absolute;top:0;left:-1px;width:100%;border:1px solid #333;background:#fff;height:4px;z-index:100}.mce-path{display:inline-block;*display:inline;*zoom:1;padding:8px;white-space:normal}.mce-path .mce-txt{display:inline-block;padding-right:3px}.mce-path .mce-path-body{display:inline-block}.mce-path-item{display:inline-block;*display:inline;*zoom:1;cursor:pointer;color:#333}.mce-path-item:hover{text-decoration:underline}.mce-path-item:focus{background:#666;color:#fff}.mce-path .mce-divider{display:inline}.mce-disabled .mce-path-item{color:#aaa}.mce-rtl .mce-path{direction:rtl}.mce-fieldset{border:0 solid #9E9E9E;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.mce-fieldset>.mce-container-body{margin-top:-15px}.mce-fieldset-title{margin-left:5px;padding:0 5px 0 5px}.mce-fit-layout{display:inline-block;*display:inline;*zoom:1}.mce-fit-layout-item{position:absolute}.mce-flow-layout-item{display:inline-block;*display:inline;*zoom:1}.mce-flow-layout-item{margin:2px 0 2px 2px}.mce-flow-layout-item.mce-last{margin-right:2px}.mce-flow-layout{white-space:normal}.mce-tinymce-inline .mce-flow-layout{white-space:nowrap}.mce-rtl .mce-flow-layout{text-align:right;direction:rtl}.mce-rtl .mce-flow-layout-item{margin:2px 2px 2px 0}.mce-rtl .mce-flow-layout-item.mce-last{margin-left:2px}.mce-iframe{border:0 solid #9e9e9e;width:100%;height:100%}.mce-label{display:inline-block;*display:inline;*zoom:1;text-shadow:0 1px 1px rgba(255,255,255,0.75);overflow:hidden}.mce-label.mce-autoscroll{overflow:auto}.mce-label.mce-disabled{color:#aaa}.mce-label.mce-multiline{white-space:pre-wrap}.mce-label.mce-error{color:#a00}.mce-rtl .mce-label{text-align:right;direction:rtl}.mce-menubar .mce-menubtn{border-color:transparent;background:transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;filter:none}.mce-menubar{border:1px solid #c4c4c4}.mce-menubar .mce-menubtn button span{color:#333}.mce-menubar .mce-caret{border-top-color:#333}.mce-menubar .mce-menubtn:hover,.mce-menubar .mce-menubtn.mce-active,.mce-menubar .mce-menubtn:focus{border-color:transparent;background:#e6e6e6;filter:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.mce-menubtn span{color:#333;margin-right:2px;line-height:20px;*line-height:16px}.mce-menubtn.mce-btn-small span{font-size:12px}.mce-menubtn.mce-fixed-width span{display:inline-block;overflow-x:hidden;text-overflow:ellipsis;width:90px}.mce-menubtn.mce-fixed-width.mce-btn-small span{width:70px}.mce-menubtn .mce-caret{*margin-top:6px}.mce-rtl .mce-menubtn button{direction:rtl;text-align:right}.mce-listbox button{text-align:left;padding-right:20px;position:relative}.mce-listbox .mce-caret{position:absolute;margin-top:-2px;right:8px;top:50%}.mce-rtl .mce-listbox .mce-caret{right:auto;left:8px}.mce-rtl .mce-listbox button{padding-right:10px;padding-left:20px}.mce-menu-item{display:block;padding:6px 15px 6px 12px;clear:both;font-weight:normal;line-height:20px;color:#333;white-space:nowrap;cursor:pointer;line-height:normal;border-left:4px solid transparent;margin-bottom:1px}.mce-menu-item .mce-ico,.mce-menu-item .mce-text{color:#333}.mce-menu-item.mce-disabled .mce-text,.mce-menu-item.mce-disabled .mce-ico{color:#adadad}.mce-menu-item:hover .mce-text,.mce-menu-item.mce-selected .mce-text,.mce-menu-item:focus .mce-text{color:#fff}.mce-menu-item:hover .mce-ico,.mce-menu-item.mce-selected .mce-ico,.mce-menu-item:focus .mce-ico{color:#fff}.mce-menu-item.mce-disabled:hover{background:#ccc}.mce-menu-shortcut{display:inline-block;color:#adadad}.mce-menu-shortcut{display:inline-block;*display:inline;*zoom:1;padding:0 15px 0 20px}.mce-menu-item:hover .mce-menu-shortcut,.mce-menu-item.mce-selected .mce-menu-shortcut,.mce-menu-item:focus .mce-menu-shortcut{color:#fff}.mce-menu-item .mce-caret{margin-top:4px;*margin-top:3px;margin-right:6px;border-top:4px solid transparent;border-bottom:4px solid transparent;border-left:4px solid #333}.mce-menu-item.mce-selected .mce-caret,.mce-menu-item:focus .mce-caret,.mce-menu-item:hover .mce-caret{border-left-color:#fff}.mce-menu-align .mce-menu-shortcut{*margin-top:-2px}.mce-menu-align .mce-menu-shortcut,.mce-menu-align .mce-caret{position:absolute;right:0}.mce-menu-item.mce-active i{visibility:visible}.mce-menu-item-normal.mce-active{background-color:#c8def4}.mce-menu-item-preview.mce-active{border-left:5px solid #aaa}.mce-menu-item-normal.mce-active .mce-text{color:#333}.mce-menu-item-normal.mce-active:hover .mce-text,.mce-menu-item-normal.mce-active:hover .mce-ico{color:#fff}.mce-menu-item-normal.mce-active:focus .mce-text,.mce-menu-item-normal.mce-active:focus .mce-ico{color:#fff}.mce-menu-item:hover,.mce-menu-item.mce-selected,.mce-menu-item:focus{text-decoration:none;color:#fff;background-color:#0081c2;background-image:-moz-linear-gradient(top, #08c, #0077b3);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#08c), to(#0077b3));background-image:-webkit-linear-gradient(top, #08c, #0077b3);background-image:-o-linear-gradient(top, #08c, #0077b3);background-image:linear-gradient(to bottom, #08c, #0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);zoom:1}div.mce-menu .mce-menu-item-sep,.mce-menu-item-sep:hover{border:0;padding:0;height:1px;margin:9px 1px;overflow:hidden;background:#cbcbcb;border-bottom:1px solid #fff;cursor:default;filter:none}.mce-menu.mce-rtl{direction:rtl}.mce-rtl .mce-menu-item{text-align:right;direction:rtl;padding:6px 12px 6px 15px}.mce-menu-align.mce-rtl .mce-menu-shortcut,.mce-menu-align.mce-rtl .mce-caret{right:auto;left:0}.mce-rtl .mce-menu-item .mce-caret{margin-left:6px;margin-right:0;border-right:4px solid #333;border-left:0}.mce-rtl .mce-menu-item.mce-selected .mce-caret,.mce-rtl .mce-menu-item:focus .mce-caret,.mce-rtl .mce-menu-item:hover .mce-caret{border-left-color:transparent;border-right-color:#fff}.mce-menu{position:absolute;left:0;top:0;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background:transparent;z-index:1000;padding:5px 0 5px 0;margin:2px 0 0;min-width:160px;background:#fff;border:1px solid #989898;border:1px solid rgba(0,0,0,0.2);z-index:1002;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);max-height:400px;overflow:auto;overflow-x:hidden}.mce-menu i{display:none}.mce-menu-has-icons i{display:inline-block;*display:inline}.mce-menu-sub-tr-tl{margin:-6px 0 0 -1px}.mce-menu-sub-br-bl{margin:6px 0 0 -1px}.mce-menu-sub-tl-tr{margin:-6px 0 0 1px}.mce-menu-sub-bl-br{margin:6px 0 0 1px}.mce-container-body .mce-resizehandle{position:absolute;right:0;bottom:0;width:16px;height:16px;visibility:visible;cursor:s-resize;margin:0}.mce-container-body .mce-resizehandle-both{cursor:se-resize}i.mce-i-resize{color:#333}.mce-spacer{visibility:hidden}.mce-splitbtn .mce-open{border-left:1px solid transparent;border-right:1px solid transparent}.mce-splitbtn:hover .mce-open{border-left-color:#bdbdbd;border-right-color:#bdbdbd}.mce-splitbtn button{padding-right:4px}.mce-splitbtn .mce-open{padding-left:4px}.mce-splitbtn .mce-open.mce-active{-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05)}.mce-splitbtn.mce-btn-small .mce-open{padding:0 3px 0 3px}.mce-rtl .mce-splitbtn{direction:rtl;text-align:right}.mce-rtl .mce-splitbtn button{padding-right:10px;padding-left:10px}.mce-rtl .mce-splitbtn .mce-open{padding-left:4px;padding-right:4px}.mce-stack-layout-item{display:block}.mce-tabs{display:block;border-bottom:1px solid #c5c5c5}.mce-tab{display:inline-block;*display:inline;*zoom:1;border:1px solid #c5c5c5;border-width:0 1px 0 0;background:#e3e3e3;padding:8px;text-shadow:0 1px 1px rgba(255,255,255,0.75);height:13px;cursor:pointer}.mce-tab:hover{background:#fdfdfd}.mce-tab.mce-active{background:#fdfdfd;border-bottom-color:transparent;margin-bottom:-1px;height:14px}.mce-rtl .mce-tabs{text-align:right;direction:rtl}.mce-rtl .mce-tab{border-width:0 0 0 1px}.mce-textbox{background:#fff;border:1px solid #c5c5c5;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);display:inline-block;-webkit-transition:border linear .2s, box-shadow linear .2s;transition:border linear .2s, box-shadow linear .2s;height:28px;resize:none;padding:0 4px 0 4px;white-space:pre-wrap;*white-space:pre;color:#333}.mce-textbox:focus,.mce-textbox.mce-focus{border-color:rgba(82,168,236,0.8);-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.65);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.65);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.65)}.mce-placeholder .mce-textbox{color:#aaa}.mce-textbox.mce-multiline{padding:4px}.mce-textbox.mce-disabled{color:#adadad}.mce-rtl .mce-textbox{text-align:right;direction:rtl}.mce-throbber{position:absolute;top:0;left:0;width:100%;height:100%;opacity:.6;filter:alpha(opacity=60);zoom:1;background:#fff url('img/loader.gif') no-repeat center center}.mce-throbber-inline{position:static;height:50px}@font-face{font-family:'tinymce';src:url('fonts/tinymce.eot');src:url('fonts/tinymce.eot?#iefix') format('embedded-opentype'),url('fonts/tinymce.woff') format('woff'),url('fonts/tinymce.ttf') format('truetype'),url('fonts/tinymce.svg#tinymce') format('svg');font-weight:normal;font-style:normal}@font-face{font-family:'tinymce-small';src:url('fonts/tinymce-small.eot');src:url('fonts/tinymce-small.eot?#iefix') format('embedded-opentype'),url('fonts/tinymce-small.woff') format('woff'),url('fonts/tinymce-small.ttf') format('truetype'),url('fonts/tinymce-small.svg#tinymce') format('svg');font-weight:normal;font-style:normal}.mce-ico{font-family:'tinymce';font-style:normal;font-weight:normal;font-size:16px;line-height:16px;vertical-align:text-top;-webkit-font-smoothing:antialiased;display:inline-block;background:transparent center center;width:16px;height:16px;color:#333;-ie7-icon:' '}.mce-btn-small .mce-ico{font-family:'tinymce-small'}.mce-ico,i.mce-i-checkbox{zoom:expression(this.runtimeStyle['zoom'] = '1', this.innerHTML = this.currentStyle['-ie7-icon'].substr(1, 1) + ' ')}.mce-i-save{-ie7-icon:"\e000"}.mce-i-newdocument{-ie7-icon:"\e001"}.mce-i-fullpage{-ie7-icon:"\e002"}.mce-i-alignleft{-ie7-icon:"\e003"}.mce-i-aligncenter{-ie7-icon:"\e004"}.mce-i-alignright{-ie7-icon:"\e005"}.mce-i-alignjustify{-ie7-icon:"\e006"}.mce-i-cut{-ie7-icon:"\e007"}.mce-i-paste{-ie7-icon:"\e008"}.mce-i-searchreplace{-ie7-icon:"\e009"}.mce-i-bullist{-ie7-icon:"\e00a"}.mce-i-numlist{-ie7-icon:"\e00b"}.mce-i-indent{-ie7-icon:"\e00c"}.mce-i-outdent{-ie7-icon:"\e00d"}.mce-i-blockquote{-ie7-icon:"\e00e"}.mce-i-undo{-ie7-icon:"\e00f"}.mce-i-redo{-ie7-icon:"\e010"}.mce-i-link{-ie7-icon:"\e011"}.mce-i-unlink{-ie7-icon:"\e012"}.mce-i-anchor{-ie7-icon:"\e013"}.mce-i-image{-ie7-icon:"\e014"}.mce-i-media{-ie7-icon:"\e015"}.mce-i-help{-ie7-icon:"\e016"}.mce-i-code{-ie7-icon:"\e017"}.mce-i-insertdatetime{-ie7-icon:"\e018"}.mce-i-preview{-ie7-icon:"\e019"}.mce-i-forecolor{-ie7-icon:"\e01a"}.mce-i-backcolor{-ie7-icon:"\e01a"}.mce-i-table{-ie7-icon:"\e01b"}.mce-i-hr{-ie7-icon:"\e01c"}.mce-i-removeformat{-ie7-icon:"\e01d"}.mce-i-subscript{-ie7-icon:"\e01e"}.mce-i-superscript{-ie7-icon:"\e01f"}.mce-i-charmap{-ie7-icon:"\e020"}.mce-i-emoticons{-ie7-icon:"\e021"}.mce-i-print{-ie7-icon:"\e022"}.mce-i-fullscreen{-ie7-icon:"\e023"}.mce-i-spellchecker{-ie7-icon:"\e024"}.mce-i-nonbreaking{-ie7-icon:"\e025"}.mce-i-template{-ie7-icon:"\e026"}.mce-i-pagebreak{-ie7-icon:"\e027"}.mce-i-restoredraft{-ie7-icon:"\e028"}.mce-i-untitled{-ie7-icon:"\e029"}.mce-i-bold{-ie7-icon:"\e02a"}.mce-i-italic{-ie7-icon:"\e02b"}.mce-i-underline{-ie7-icon:"\e02c"}.mce-i-strikethrough{-ie7-icon:"\e02d"}.mce-i-visualchars{-ie7-icon:"\e02e"}.mce-i-ltr{-ie7-icon:"\e02f"}.mce-i-rtl{-ie7-icon:"\e030"}.mce-i-copy{-ie7-icon:"\e031"}.mce-i-resize{-ie7-icon:"\e032"}.mce-i-browse{-ie7-icon:"\e034"}.mce-i-pastetext{-ie7-icon:"\e035"}.mce-i-checkbox,.mce-i-selected{-ie7-icon:"\e033"}.mce-i-selected{visibility:hidden}.mce-i-backcolor{background:#BBB} \ No newline at end of file diff --git a/wp-includes/js/tinymce/skins/lightgray/skin.min.css b/wp-includes/js/tinymce/skins/lightgray/skin.min.css new file mode 100644 index 0000000..4f6e167 --- /dev/null +++ b/wp-includes/js/tinymce/skins/lightgray/skin.min.css @@ -0,0 +1 @@ +.mce-container,.mce-container *,.mce-widget,.mce-widget *,.mce-reset{margin:0;padding:0;border:0;outline:0;vertical-align:top;background:0 0;text-decoration:none;color:#333;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;text-shadow:none;float:none;position:static;width:auto;height:auto;white-space:nowrap;cursor:inherit;-webkit-tap-highlight-color:transparent;line-height:normal;font-weight:400;text-align:left;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;direction:ltr;max-width:none}.mce-widget button{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}.mce-container [unselectable]{-moz-user-select:none;-webkit-user-select:none;-o-user-select:none;user-select:none}.mce-fade{opacity:0;-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.mce-fade.mce-in{opacity:1}.mce-tinymce{visibility:inherit!important;position:relative}.mce-fullscreen{border:0;padding:0;margin:0;overflow:hidden;height:100%;z-index:100}div.mce-fullscreen{position:fixed;top:0;left:0;width:100%;height:auto}.mce-tinymce{display:block;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}.mce-wordcount{position:absolute;top:0;right:0;padding:8px}div.mce-edit-area{background:#FFF;filter:none}.mce-statusbar{position:relative}.mce-statusbar .mce-container-body{position:relative}.mce-fullscreen .mce-resizehandle{display:none}.mce-charmap{border-collapse:collapse}.mce-charmap td{cursor:default;border:1px solid #9e9e9e;width:20px;height:20px;line-height:20px;text-align:center;vertical-align:middle;padding:2px}.mce-charmap td div{text-align:center}.mce-charmap td:hover{background:#d9d9d9}.mce-grid td.mce-grid-cell div{border:1px solid #d6d6d6;width:15px;height:15px;margin:0;cursor:pointer}.mce-grid td.mce-grid-cell div:focus{border-color:#a1a1a1}.mce-grid td.mce-grid-cell div[disabled]{cursor:not-allowed}.mce-grid{border-spacing:2px;border-collapse:separate}.mce-grid a{display:block;border:1px solid transparent}.mce-grid a:hover,.mce-grid a:focus{border-color:#a1a1a1}.mce-grid-border{margin:0 4px 0 4px}.mce-grid-border a{border-color:#d6d6d6;width:13px;height:13px}.mce-grid-border a:hover,.mce-grid-border a.mce-active{border-color:#a1a1a1;background:#c8def4}.mce-text-center{text-align:center}div.mce-tinymce-inline{width:100%;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.mce-colorbtn-trans div{text-align:center;vertical-align:middle;font-weight:700;font-size:20px;line-height:16px;color:#707070}.mce-toolbar-grp{padding-bottom:2px}.mce-toolbar-grp .mce-flow-layout-item{margin-bottom:0}.mce-rtl .mce-wordcount{left:0;right:auto}.mce-container,.mce-container-body{display:block}.mce-autoscroll{overflow:hidden}.mce-scrollbar{position:absolute;width:7px;height:100%;top:2px;right:2px;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-scrollbar-h{top:auto;right:auto;left:2px;bottom:2px;width:100%;height:7px}.mce-scrollbar-thumb{position:absolute;background-color:#000;border:1px solid #888;border-color:rgba(85,85,85,.6);width:5px;height:100%;-webkit-border-radius:7px;-moz-border-radius:7px;border-radius:7px}.mce-scrollbar-h .mce-scrollbar-thumb{width:100%;height:5px}.mce-scrollbar:hover,.mce-scrollbar.mce-active{background-color:#AAA;opacity:.6;filter:alpha(opacity=60);zoom:1;-webkit-border-radius:7px;-moz-border-radius:7px;border-radius:7px}.mce-scroll{position:relative}.mce-panel{border:0 solid #9e9e9e;background-color:#f0f0f0;background-image:-moz-linear-gradient(top,#fdfdfd,#ddd);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fdfdfd),to(#ddd));background-image:-webkit-linear-gradient(top,#fdfdfd,#ddd);background-image:-o-linear-gradient(top,#fdfdfd,#ddd);background-image:linear-gradient(to bottom,#fdfdfd,#ddd);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffdfdfd', endColorstr='#ffdddddd', GradientType=0);zoom:1}.mce-floatpanel{position:absolute;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2)}.mce-floatpanel.mce-fixed{position:fixed}.mce-floatpanel .mce-arrow,.mce-floatpanel .mce-arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.mce-floatpanel .mce-arrow{border-width:11px}.mce-floatpanel .mce-arrow:after{border-width:10px;content:""}.mce-floatpanel.mce-popover{filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background:0 0;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);top:0;left:0;background:#fff;border:1px solid #9e9e9e;border:1px solid rgba(0,0,0,.25)}.mce-floatpanel.mce-popover.mce-bottom{margin-top:10px;}.mce-floatpanel.mce-popover.mce-bottom>.mce-arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#9e9e9e;border-bottom-color:rgba(0,0,0,.25);top:-11px}.mce-floatpanel.mce-popover.mce-bottom>.mce-arrow:after{top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#fff}.mce-floatpanel.mce-popover.mce-bottom.mce-start{margin-left:-22px}.mce-floatpanel.mce-popover.mce-bottom.mce-start>.mce-arrow{left:20px}.mce-floatpanel.mce-popover.mce-bottom.mce-end{margin-left:22px}.mce-floatpanel.mce-popover.mce-bottom.mce-end>.mce-arrow{right:10px;left:auto}.mce-fullscreen{border:0;padding:0;margin:0;overflow:hidden;background:#fff;height:100%}div.mce-fullscreen{position:fixed;top:0;left:0}#mce-modal-block{opacity:0;filter:alpha(opacity=0);zoom:1;position:fixed;left:0;top:0;width:100%;height:100%;background:#000}#mce-modal-block.mce-in{opacity:.3;filter:alpha(opacity=30);zoom:1}.mce-window-move{cursor:move}.mce-window{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0,0,0,.3);-moz-box-shadow:0 3px 7px rgba(0,0,0,.3);box-shadow:0 3px 7px rgba(0,0,0,.3);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background:0 0;background:#fff;position:fixed;top:0;left:0;opacity:0;-webkit-transition:opacity 150ms ease-in;transition:opacity 150ms ease-in}.mce-window.mce-in{opacity:1}.mce-window-head{padding:9px 15px;border-bottom:1px solid #c5c5c5;position:relative}.mce-window-head .mce-close{position:absolute;right:15px;top:9px;font-size:20px;font-weight:700;line-height:20px;color:#858585;cursor:pointer;height:20px;overflow:hidden}.mce-close:hover{color:#adadad}.mce-window-head .mce-title{line-height:20px;font-size:20px;font-weight:700;text-rendering:optimizelegibility;padding-right:10px}.mce-window .mce-container-body{display:block}.mce-foot{display:block;background-color:#fff;border-top:1px solid #c5c5c5;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.mce-window-head .mce-dragh{position:absolute;top:0;left:0;cursor:move;width:90%;height:100%}.mce-window iframe{width:100%;height:100%}.mce-window.mce-fullscreen,.mce-window.mce-fullscreen .mce-foot{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.mce-rtl .mce-window-head .mce-close{position:absolute;right:auto;left:15px}.mce-rtl .mce-window-head .mce-dragh{left:auto;right:0}.mce-rtl .mce-window-head .mce-title{direction:rtl;text-align:right}.mce-abs-layout{position:relative}body .mce-abs-layout-item,.mce-abs-end{position:absolute}.mce-abs-end{width:1px;height:1px}.mce-container-body.mce-abs-layout{overflow:hidden}.mce-tooltip{position:absolute;padding:5px;opacity:.8;filter:alpha(opacity=80);zoom:1}.mce-tooltip-inner{font-size:11px;background-color:#000;color:#fff;max-width:200px;padding:5px 8px 4px 8px;text-align:center;white-space:normal}.mce-tooltip-inner{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.mce-tooltip-inner{-webkit-box-shadow:0 0 5px #000;-moz-box-shadow:0 0 5px #000;box-shadow:0 0 5px #000}.mce-tooltip-arrow{position:absolute;width:0;height:0;line-height:0;border:5px dashed #000}.mce-tooltip-arrow-n{border-bottom-color:#000}.mce-tooltip-arrow-s{border-top-color:#000}.mce-tooltip-arrow-e{border-left-color:#000}.mce-tooltip-arrow-w{border-right-color:#000}.mce-tooltip-nw,.mce-tooltip-sw{margin-left:-14px}.mce-tooltip-n .mce-tooltip-arrow{top:0;left:50%;margin-left:-5px;border-bottom-style:solid;border-top:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-nw .mce-tooltip-arrow{top:0;left:10px;border-bottom-style:solid;border-top:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-ne .mce-tooltip-arrow{top:0;right:10px;border-bottom-style:solid;border-top:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-s .mce-tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-style:solid;border-bottom:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-sw .mce-tooltip-arrow{bottom:0;left:10px;border-top-style:solid;border-bottom:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-se .mce-tooltip-arrow{bottom:0;right:10px;border-top-style:solid;border-bottom:none;border-left-color:transparent;border-right-color:transparent}.mce-tooltip-e .mce-tooltip-arrow{right:0;top:50%;margin-top:-5px;border-left-style:solid;border-right:none;border-top-color:transparent;border-bottom-color:transparent}.mce-tooltip-w .mce-tooltip-arrow{left:0;top:50%;margin-top:-5px;border-right-style:solid;border-left:none;border-top-color:transparent;border-bottom-color:transparent}.mce-btn{border:1px solid #b1b1b1;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25) rgba(0,0,0,.25);position:relative;text-shadow:0 1px 1px rgba(255,255,255,.75);display:inline-block;;;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);background-color:#f0f0f0;background-image:-moz-linear-gradient(top,#fff,#d9d9d9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#d9d9d9));background-image:-webkit-linear-gradient(top,#fff,#d9d9d9);background-image:-o-linear-gradient(top,#fff,#d9d9d9);background-image:linear-gradient(to bottom,#fff,#d9d9d9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffd9d9d9', GradientType=0);zoom:1}.mce-btn:hover,.mce-btn:focus{color:#333;background-color:#e3e3e3;background-image:-moz-linear-gradient(top,#f2f2f2,#ccc);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f2f2f2),to(#ccc));background-image:-webkit-linear-gradient(top,#f2f2f2,#ccc);background-image:-o-linear-gradient(top,#f2f2f2,#ccc);background-image:linear-gradient(to bottom,#f2f2f2,#ccc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffcccccc', GradientType=0);zoom:1}.mce-btn.mce-disabled button,.mce-btn.mce-disabled:hover button{cursor:default;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-btn.mce-active,.mce-btn.mce-active:hover{background-color:#d6d6d6;background-image:-moz-linear-gradient(top,#e6e6e6,silver);background-image:-webkit-gradient(linear,0 0,0 100%,from(#e6e6e6),to(silver));background-image:-webkit-linear-gradient(top,#e6e6e6,silver);background-image:-o-linear-gradient(top,#e6e6e6,silver);background-image:linear-gradient(to bottom,#e6e6e6,silver);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe6e6e6', endColorstr='#ffc0c0c0', GradientType=0);zoom:1;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05)}.mce-btn:active{background-color:#d6d6d6;background-image:-moz-linear-gradient(top,#e6e6e6,silver);background-image:-webkit-gradient(linear,0 0,0 100%,from(#e6e6e6),to(silver));background-image:-webkit-linear-gradient(top,#e6e6e6,silver);background-image:-o-linear-gradient(top,#e6e6e6,silver);background-image:linear-gradient(to bottom,#e6e6e6,silver);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe6e6e6', endColorstr='#ffc0c0c0', GradientType=0);zoom:1;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05)}.mce-btn button{padding:4px 10px;font-size:14px;line-height:20px;;cursor:pointer;color:#333;text-align:center;overflow:visible;-webkit-appearance:none}.mce-btn button::-moz-focus-inner{border:0;padding:0}.mce-btn i{text-shadow:1px 1px #fff}.mce-primary{min-width:50px;color:#fff;border:1px solid #b1b1b1;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25) rgba(0,0,0,.25);background-color:#006dcc;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0044cc', GradientType=0);zoom:1}.mce-primary:hover,.mce-primary:focus{background-color:#005fb3;background-image:-moz-linear-gradient(top,#0077b3,#003cb3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#0077b3),to(#003cb3));background-image:-webkit-linear-gradient(top,#0077b3,#003cb3);background-image:-o-linear-gradient(top,#0077b3,#003cb3);background-image:linear-gradient(to bottom,#0077b3,#003cb3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0077b3', endColorstr='#ff003cb3', GradientType=0);zoom:1}.mce-primary.mce-disabled button,.mce-primary.mce-disabled:hover button{cursor:default;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-primary.mce-active,.mce-primary.mce-active:hover,.mce-primary:not(.mce-disabled):active{background-color:#005299;background-image:-moz-linear-gradient(top,#069,#039);background-image:-webkit-gradient(linear,0 0,0 100%,from(#069),to(#039));background-image:-webkit-linear-gradient(top,#069,#039);background-image:-o-linear-gradient(top,#069,#039);background-image:linear-gradient(to bottom,#069,#039);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff006699', endColorstr='#ff003399', GradientType=0);zoom:1;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05)}.mce-primary button,.mce-primary button i{color:#fff;text-shadow:1px 1px #333}.mce-btn-large button{padding:9px 14px;font-size:16px;line-height:normal;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.mce-btn-large i{margin-top:2px}.mce-btn-small button{padding:1px 5px;font-size:12px;}.mce-btn-small i{line-height:20px;vertical-align:top;}.mce-btn .mce-caret{margin-top:8px;margin-left:0}.mce-btn-small .mce-caret{margin-top:8px;margin-left:0}.mce-caret{display:inline-block;;;width:0;height:0;vertical-align:top;border-top:4px solid #333;border-right:4px solid transparent;border-left:4px solid transparent;content:""}.mce-disabled .mce-caret{border-top-color:#aaa}.mce-caret.mce-up{border-bottom:4px solid #333;border-top:0}.mce-btn-flat{border:0;background:0 0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;filter:none}.mce-btn-flat:hover,.mce-btn-flat.mce-active,.mce-btn-flat:focus,.mce-btn-flat:active{border:0;background:#e6e6e6;filter:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.mce-rtl .mce-btn button{direction:rtl}.mce-btn-group .mce-btn{border-width:1px 0 1px 0;margin:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.mce-btn-group .mce-first{border-left:1px solid #b1b1b1;border-left:1px solid rgba(0,0,0,.25);-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.mce-btn-group .mce-last{border-right:1px solid #b1b1b1;border-right:1px solid rgba(0,0,0,.1);-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.mce-btn-group .mce-first.mce-last{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.mce-btn-group .mce-btn.mce-flow-layout-item{margin:0}.mce-checkbox{cursor:pointer}i.mce-i-checkbox{margin:0 3px 0 0;border:1px solid #c5c5c5;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05);background-color:#f0f0f0;background-image:-moz-linear-gradient(top,#fff,#d9d9d9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#d9d9d9));background-image:-webkit-linear-gradient(top,#fff,#d9d9d9);background-image:-o-linear-gradient(top,#fff,#d9d9d9);background-image:linear-gradient(to bottom,#fff,#d9d9d9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffd9d9d9', GradientType=0);zoom:1;text-indent:-10em;;;;overflow:hidden}.mce-checked i.mce-i-checkbox{color:#333;font-size:16px;line-height:16px;text-indent:0}.mce-checkbox:focus i.mce-i-checkbox,.mce-checkbox.mce-focus i.mce-i-checkbox{border:1px solid rgba(82,168,236,.8);-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(82,168,236,.65);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(82,168,236,.65);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(82,168,236,.65)}.mce-checkbox.mce-disabled .mce-label,.mce-checkbox.mce-disabled i.mce-i-checkbox{color:#acacac}.mce-rtl .mce-checkbox{direction:rtl;text-align:right}.mce-rtl i.mce-i-checkbox{margin:0 0 0 3px}.mce-combobox{display:inline-block;;;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);}.mce-combobox input{border:1px solid #c5c5c5;border-right-color:#c5c5c5;height:28px}.mce-combobox.mce-disabled input{color:#adadad}.mce-combobox.mce-has-open input{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.mce-combobox .mce-btn{border-left:0;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.mce-combobox button{padding-right:8px;padding-left:8px}.mce-combobox.mce-disabled .mce-btn button{cursor:default;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;opacity:.4;filter:alpha(opacity=40);zoom:1}.mce-colorbox i{border:1px solid #c5c5c5;width:14px;height:14px}.mce-colorbutton .mce-ico{position:relative}.mce-colorbutton-grid{margin:4px}.mce-colorbutton button{padding-right:4px}.mce-colorbutton .mce-preview{padding-right:3px;display:block;position:absolute;left:50%;top:50%;margin-left:-14px;margin-top:7px;background:gray;width:13px;height:2px;overflow:hidden}.mce-colorbutton.mce-btn-small .mce-preview{margin-left:-16px;padding-right:0;width:16px}.mce-colorbutton .mce-open{padding-left:4px;border-left:1px solid transparent;border-right:1px solid transparent}.mce-colorbutton:hover .mce-open{border-left-color:#bdbdbd;border-right-color:#bdbdbd}.mce-colorbutton.mce-btn-small .mce-open{padding:0 3px 0 3px}.mce-rtl .mce-colorbutton{direction:rtl}.mce-rtl .mce-colorbutton .mce-preview{margin-left:0;padding-right:0;padding-left:4px;margin-right:-14px}.mce-rtl .mce-colorbutton.mce-btn-small .mce-preview{margin-left:0;padding-right:0;margin-right:-17px;padding-left:0}.mce-rtl .mce-colorbutton button{padding-right:10px;padding-left:10px}.mce-rtl .mce-colorbutton .mce-open{padding-left:4px;padding-right:4px}.mce-colorpicker{position:relative;width:250px;height:220px}.mce-colorpicker-sv{position:absolute;top:0;left:0;width:90%;height:100%;border:1px solid #c5c5c5;cursor:crosshair;overflow:hidden}.mce-colorpicker-h-chunk{width:100%}.mce-colorpicker-overlay1,.mce-colorpicker-overlay2{width:100%;height:100%;position:absolute;top:0;left:0}.mce-colorpicker-overlay1{filter:progid:DXImageTransform.Microsoft.gradient(GradientType=1, startColorstr='#ffffff', endColorstr='#00ffffff');-ms-filter:"progid:DXImageTransform.Microsoft.gradient(GradientType=1,startColorstr='#ffffff', endColorstr='#00ffffff')";background:linear-gradient(to right,#fff,rgba(255,255,255,0))}.mce-colorpicker-overlay2{filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#00000000', endColorstr='#000000');-ms-filter:"progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='#00000000', endColorstr='#000000')";background:linear-gradient(to bottom,rgba(0,0,0,0),#000)}.mce-colorpicker-selector1{background:0 0;position:absolute;width:12px;height:12px;margin:-8px 0 0 -8px;border:1px solid #000;border-radius:50%}.mce-colorpicker-selector2{position:absolute;width:10px;height:10px;border:1px solid #fff;border-radius:50%}.mce-colorpicker-h{position:absolute;top:0;right:0;width:6.5%;height:100%;border:1px solid #c5c5c5;cursor:crosshair}.mce-colorpicker-h-marker{margin-top:-4px;position:absolute;top:0;left:-1px;width:100%;border:1px solid #333;background:#fff;height:4px;z-index:100}.mce-path{display:inline-block;;;padding:8px;white-space:normal}.mce-path .mce-txt{display:inline-block;padding-right:3px}.mce-path .mce-path-body{display:inline-block}.mce-path-item{display:inline-block;;;cursor:pointer;color:#333}.mce-path-item:hover{text-decoration:underline}.mce-path-item:focus{background:#666;color:#fff}.mce-path .mce-divider{display:inline}.mce-disabled .mce-path-item{color:#aaa}.mce-rtl .mce-path{direction:rtl}.mce-fieldset{border:0 solid #9E9E9E;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.mce-fieldset>.mce-container-body{margin-top:-15px}.mce-fieldset-title{margin-left:5px;padding:0 5px 0 5px}.mce-fit-layout{display:inline-block;;}.mce-fit-layout-item{position:absolute}.mce-flow-layout-item{display:inline-block;;}.mce-flow-layout-item{margin:2px 0 2px 2px}.mce-flow-layout-item.mce-last{margin-right:2px}.mce-flow-layout{white-space:normal}.mce-tinymce-inline .mce-flow-layout{white-space:nowrap}.mce-rtl .mce-flow-layout{text-align:right;direction:rtl}.mce-rtl .mce-flow-layout-item{margin:2px 2px 2px 0}.mce-rtl .mce-flow-layout-item.mce-last{margin-left:2px}.mce-iframe{border:0 solid #9e9e9e;width:100%;height:100%}.mce-label{display:inline-block;;;text-shadow:0 1px 1px rgba(255,255,255,.75);overflow:hidden}.mce-label.mce-autoscroll{overflow:auto}.mce-label.mce-disabled{color:#aaa}.mce-label.mce-multiline{white-space:pre-wrap}.mce-label.mce-error{color:#a00}.mce-rtl .mce-label{text-align:right;direction:rtl}.mce-menubar .mce-menubtn{border-color:transparent;background:0 0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;filter:none}.mce-menubar{border:1px solid #c4c4c4}.mce-menubar .mce-menubtn button span{color:#333}.mce-menubar .mce-caret{border-top-color:#333}.mce-menubar .mce-menubtn:hover,.mce-menubar .mce-menubtn.mce-active,.mce-menubar .mce-menubtn:focus{border-color:transparent;background:#e6e6e6;filter:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.mce-menubtn span{color:#333;margin-right:2px;line-height:20px;}.mce-menubtn.mce-btn-small span{font-size:12px}.mce-menubtn.mce-fixed-width span{display:inline-block;overflow-x:hidden;text-overflow:ellipsis;width:90px}.mce-menubtn.mce-fixed-width.mce-btn-small span{width:70px}.mce-rtl .mce-menubtn button{direction:rtl;text-align:right}.mce-listbox button{text-align:left;padding-right:20px;position:relative}.mce-listbox .mce-caret{position:absolute;margin-top:-2px;right:8px;top:50%}.mce-rtl .mce-listbox .mce-caret{right:auto;left:8px}.mce-rtl .mce-listbox button{padding-right:10px;padding-left:20px}.mce-menu-item{display:block;padding:6px 15px 6px 12px;clear:both;font-weight:400;line-height:20px;color:#333;white-space:nowrap;cursor:pointer;line-height:normal;border-left:4px solid transparent;margin-bottom:1px}.mce-menu-item .mce-ico,.mce-menu-item .mce-text{color:#333}.mce-menu-item.mce-disabled .mce-text,.mce-menu-item.mce-disabled .mce-ico{color:#adadad}.mce-menu-item:hover .mce-text,.mce-menu-item.mce-selected .mce-text,.mce-menu-item:focus .mce-text{color:#fff}.mce-menu-item:hover .mce-ico,.mce-menu-item.mce-selected .mce-ico,.mce-menu-item:focus .mce-ico{color:#fff}.mce-menu-item.mce-disabled:hover{background:#ccc}.mce-menu-shortcut{display:inline-block;color:#adadad}.mce-menu-shortcut{display:inline-block;;;padding:0 15px 0 20px}.mce-menu-item:hover .mce-menu-shortcut,.mce-menu-item.mce-selected .mce-menu-shortcut,.mce-menu-item:focus .mce-menu-shortcut{color:#fff}.mce-menu-item .mce-caret{margin-top:4px;;margin-right:6px;border-top:4px solid transparent;border-bottom:4px solid transparent;border-left:4px solid #333}.mce-menu-item.mce-selected .mce-caret,.mce-menu-item:focus .mce-caret,.mce-menu-item:hover .mce-caret{border-left-color:#fff}.mce-menu-align .mce-menu-shortcut,.mce-menu-align .mce-caret{position:absolute;right:0}.mce-menu-item.mce-active i{visibility:visible}.mce-menu-item-normal.mce-active{background-color:#c8def4}.mce-menu-item-preview.mce-active{border-left:5px solid #aaa}.mce-menu-item-normal.mce-active .mce-text{color:#333}.mce-menu-item-normal.mce-active:hover .mce-text,.mce-menu-item-normal.mce-active:hover .mce-ico{color:#fff}.mce-menu-item-normal.mce-active:focus .mce-text,.mce-menu-item-normal.mce-active:focus .mce-ico{color:#fff}.mce-menu-item:hover,.mce-menu-item.mce-selected,.mce-menu-item:focus{text-decoration:none;color:#fff;background-color:#0081c2;background-image:-moz-linear-gradient(top,#08c,#0077b3);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#0077b3));background-image:-webkit-linear-gradient(top,#08c,#0077b3);background-image:-o-linear-gradient(top,#08c,#0077b3);background-image:linear-gradient(to bottom,#08c,#0077b3);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);zoom:1}div.mce-menu .mce-menu-item-sep,.mce-menu-item-sep:hover{border:0;padding:0;height:1px;margin:9px 1px;overflow:hidden;background:#cbcbcb;border-bottom:1px solid #fff;cursor:default;filter:none}.mce-menu.mce-rtl{direction:rtl}.mce-rtl .mce-menu-item{text-align:right;direction:rtl;padding:6px 12px 6px 15px}.mce-menu-align.mce-rtl .mce-menu-shortcut,.mce-menu-align.mce-rtl .mce-caret{right:auto;left:0}.mce-rtl .mce-menu-item .mce-caret{margin-left:6px;margin-right:0;border-right:4px solid #333;border-left:0}.mce-rtl .mce-menu-item.mce-selected .mce-caret,.mce-rtl .mce-menu-item:focus .mce-caret,.mce-rtl .mce-menu-item:hover .mce-caret{border-left-color:transparent;border-right-color:#fff}.mce-menu{position:absolute;left:0;top:0;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background:0 0;z-index:1000;padding:5px 0 5px 0;margin:2px 0 0;min-width:160px;background:#fff;border:1px solid #989898;border:1px solid rgba(0,0,0,.2);z-index:1002;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);max-height:400px;overflow:auto;overflow-x:hidden}.mce-menu i{display:none}.mce-menu-has-icons i{display:inline-block;}.mce-menu-sub-tr-tl{margin:-6px 0 0 -1px}.mce-menu-sub-br-bl{margin:6px 0 0 -1px}.mce-menu-sub-tl-tr{margin:-6px 0 0 1px}.mce-menu-sub-bl-br{margin:6px 0 0 1px}.mce-container-body .mce-resizehandle{position:absolute;right:0;bottom:0;width:16px;height:16px;visibility:visible;cursor:s-resize;margin:0}.mce-container-body .mce-resizehandle-both{cursor:se-resize}i.mce-i-resize{color:#333}.mce-spacer{visibility:hidden}.mce-splitbtn .mce-open{border-left:1px solid transparent;border-right:1px solid transparent}.mce-splitbtn:hover .mce-open{border-left-color:#bdbdbd;border-right-color:#bdbdbd}.mce-splitbtn button{padding-right:4px}.mce-splitbtn .mce-open{padding-left:4px}.mce-splitbtn .mce-open.mce-active{-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05)}.mce-splitbtn.mce-btn-small .mce-open{padding:0 3px 0 3px}.mce-rtl .mce-splitbtn{direction:rtl;text-align:right}.mce-rtl .mce-splitbtn button{padding-right:10px;padding-left:10px}.mce-rtl .mce-splitbtn .mce-open{padding-left:4px;padding-right:4px}.mce-stack-layout-item{display:block}.mce-tabs{display:block;border-bottom:1px solid #c5c5c5}.mce-tab{display:inline-block;;;border:1px solid #c5c5c5;border-width:0 1px 0 0;background:#e3e3e3;padding:8px;text-shadow:0 1px 1px rgba(255,255,255,.75);height:13px;cursor:pointer}.mce-tab:hover{background:#fdfdfd}.mce-tab.mce-active{background:#fdfdfd;border-bottom-color:transparent;margin-bottom:-1px;height:14px}.mce-rtl .mce-tabs{text-align:right;direction:rtl}.mce-rtl .mce-tab{border-width:0 0 0 1px}.mce-textbox{background:#fff;border:1px solid #c5c5c5;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);display:inline-block;-webkit-transition:border linear .2s,box-shadow linear .2s;transition:border linear .2s,box-shadow linear .2s;height:28px;resize:none;padding:0 4px 0 4px;white-space:pre-wrap;;color:#333}.mce-textbox:focus,.mce-textbox.mce-focus{border-color:rgba(82,168,236,.8);-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(82,168,236,.65);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(82,168,236,.65);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(82,168,236,.65)}.mce-placeholder .mce-textbox{color:#aaa}.mce-textbox.mce-multiline{padding:4px}.mce-textbox.mce-disabled{color:#adadad}.mce-rtl .mce-textbox{text-align:right;direction:rtl}.mce-throbber{position:absolute;top:0;left:0;width:100%;height:100%;opacity:.6;filter:alpha(opacity=60);zoom:1;background:#fff url(img/loader.gif) no-repeat center center}.mce-throbber-inline{position:static;height:50px}@font-face{font-family:tinymce;src:url(fonts/tinymce.eot);src:url(fonts/tinymce.eot?#iefix) format('embedded-opentype'),url(fonts/tinymce.woff) format('woff'),url(fonts/tinymce.ttf) format('truetype'),url(fonts/tinymce.svg#tinymce) format('svg');font-weight:400;font-style:normal}@font-face{font-family:tinymce-small;src:url(fonts/tinymce-small.eot);src:url(fonts/tinymce-small.eot?#iefix) format('embedded-opentype'),url(fonts/tinymce-small.woff) format('woff'),url(fonts/tinymce-small.ttf) format('truetype'),url(fonts/tinymce-small.svg#tinymce) format('svg');font-weight:400;font-style:normal}.mce-ico{font-family:tinymce,Arial;font-style:normal;font-weight:400;font-variant:normal;font-size:16px;line-height:16px;speak:none;vertical-align:text-top;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;display:inline-block;background:transparent center center;background-size:cover;width:16px;height:16px;color:#333}.mce-btn-small .mce-ico{font-family:tinymce-small,Arial}.mce-i-save:before{content:"\e000"}.mce-i-newdocument:before{content:"\e001"}.mce-i-fullpage:before{content:"\e002"}.mce-i-alignleft:before{content:"\e003"}.mce-i-aligncenter:before{content:"\e004"}.mce-i-alignright:before{content:"\e005"}.mce-i-alignjustify:before{content:"\e006"}.mce-i-cut:before{content:"\e007"}.mce-i-paste:before{content:"\e008"}.mce-i-searchreplace:before{content:"\e009"}.mce-i-bullist:before{content:"\e00a"}.mce-i-numlist:before{content:"\e00b"}.mce-i-indent:before{content:"\e00c"}.mce-i-outdent:before{content:"\e00d"}.mce-i-blockquote:before{content:"\e00e"}.mce-i-undo:before{content:"\e00f"}.mce-i-redo:before{content:"\e010"}.mce-i-link:before{content:"\e011"}.mce-i-unlink:before{content:"\e012"}.mce-i-anchor:before{content:"\e013"}.mce-i-image:before{content:"\e014"}.mce-i-media:before{content:"\e015"}.mce-i-help:before{content:"\e016"}.mce-i-code:before{content:"\e017"}.mce-i-insertdatetime:before{content:"\e018"}.mce-i-preview:before{content:"\e019"}.mce-i-forecolor:before{content:"\e01a"}.mce-i-backcolor:before{content:"\e01a"}.mce-i-table:before{content:"\e01b"}.mce-i-hr:before{content:"\e01c"}.mce-i-removeformat:before{content:"\e01d"}.mce-i-subscript:before{content:"\e01e"}.mce-i-superscript:before{content:"\e01f"}.mce-i-charmap:before{content:"\e020"}.mce-i-emoticons:before{content:"\e021"}.mce-i-print:before{content:"\e022"}.mce-i-fullscreen:before{content:"\e023"}.mce-i-spellchecker:before{content:"\e024"}.mce-i-nonbreaking:before{content:"\e025"}.mce-i-template:before{content:"\e026"}.mce-i-pagebreak:before{content:"\e027"}.mce-i-restoredraft:before{content:"\e028"}.mce-i-untitled:before{content:"\e029"}.mce-i-bold:before{content:"\e02a"}.mce-i-italic:before{content:"\e02b"}.mce-i-underline:before{content:"\e02c"}.mce-i-strikethrough:before{content:"\e02d"}.mce-i-visualchars:before{content:"\e02e"}.mce-i-visualblocks:before{content:"\e02e"}.mce-i-ltr:before{content:"\e02f"}.mce-i-rtl:before{content:"\e030"}.mce-i-copy:before{content:"\e031"}.mce-i-resize:before{content:"\e032"}.mce-i-browse:before{content:"\e034"}.mce-i-pastetext:before{content:"\e035"}.mce-i-checkbox:before,.mce-i-selected:before{content:"\e033"}.mce-i-selected{visibility:hidden}i.mce-i-backcolor{text-shadow:none;background:#bbb} \ No newline at end of file diff --git a/wp-includes/js/tinymce/skins/wordpress/images/audio.png b/wp-includes/js/tinymce/skins/wordpress/images/audio.png new file mode 100644 index 0000000..713e4d2 Binary files /dev/null and b/wp-includes/js/tinymce/skins/wordpress/images/audio.png differ diff --git a/wp-includes/js/tinymce/skins/wordpress/images/dashicon-edit.png b/wp-includes/js/tinymce/skins/wordpress/images/dashicon-edit.png new file mode 100644 index 0000000..7c47b1c Binary files /dev/null and b/wp-includes/js/tinymce/skins/wordpress/images/dashicon-edit.png differ diff --git a/wp-includes/js/tinymce/skins/wordpress/images/dashicon-no.png b/wp-includes/js/tinymce/skins/wordpress/images/dashicon-no.png new file mode 100644 index 0000000..79b6453 Binary files /dev/null and b/wp-includes/js/tinymce/skins/wordpress/images/dashicon-no.png differ diff --git a/wp-includes/js/tinymce/skins/wordpress/images/embedded.png b/wp-includes/js/tinymce/skins/wordpress/images/embedded.png new file mode 100644 index 0000000..e97e38a Binary files /dev/null and b/wp-includes/js/tinymce/skins/wordpress/images/embedded.png differ diff --git a/wp-includes/js/tinymce/skins/wordpress/images/gallery-2x.png b/wp-includes/js/tinymce/skins/wordpress/images/gallery-2x.png new file mode 100644 index 0000000..5b28167 Binary files /dev/null and b/wp-includes/js/tinymce/skins/wordpress/images/gallery-2x.png differ diff --git a/wp-includes/js/tinymce/skins/wordpress/images/gallery.png b/wp-includes/js/tinymce/skins/wordpress/images/gallery.png new file mode 100644 index 0000000..2c21b03 Binary files /dev/null and b/wp-includes/js/tinymce/skins/wordpress/images/gallery.png differ diff --git a/wp-includes/js/tinymce/skins/wordpress/images/more-2x.png b/wp-includes/js/tinymce/skins/wordpress/images/more-2x.png new file mode 100644 index 0000000..c976e50 Binary files /dev/null and b/wp-includes/js/tinymce/skins/wordpress/images/more-2x.png differ diff --git a/wp-includes/js/tinymce/skins/wordpress/images/more.png b/wp-includes/js/tinymce/skins/wordpress/images/more.png new file mode 100644 index 0000000..fca562b Binary files /dev/null and b/wp-includes/js/tinymce/skins/wordpress/images/more.png differ diff --git a/wp-includes/js/tinymce/skins/wordpress/images/pagebreak-2x.png b/wp-includes/js/tinymce/skins/wordpress/images/pagebreak-2x.png new file mode 100644 index 0000000..e5cb33b Binary files /dev/null and b/wp-includes/js/tinymce/skins/wordpress/images/pagebreak-2x.png differ diff --git a/wp-includes/js/tinymce/skins/wordpress/images/pagebreak.png b/wp-includes/js/tinymce/skins/wordpress/images/pagebreak.png new file mode 100644 index 0000000..fc67ca9 Binary files /dev/null and b/wp-includes/js/tinymce/skins/wordpress/images/pagebreak.png differ diff --git a/wp-includes/js/tinymce/skins/wordpress/images/playlist-audio.png b/wp-includes/js/tinymce/skins/wordpress/images/playlist-audio.png new file mode 100644 index 0000000..174c620 Binary files /dev/null and b/wp-includes/js/tinymce/skins/wordpress/images/playlist-audio.png differ diff --git a/wp-includes/js/tinymce/skins/wordpress/images/playlist-video.png b/wp-includes/js/tinymce/skins/wordpress/images/playlist-video.png new file mode 100644 index 0000000..6f19c3a Binary files /dev/null and b/wp-includes/js/tinymce/skins/wordpress/images/playlist-video.png differ diff --git a/wp-includes/js/tinymce/skins/wordpress/images/video.png b/wp-includes/js/tinymce/skins/wordpress/images/video.png new file mode 100644 index 0000000..14ec84b Binary files /dev/null and b/wp-includes/js/tinymce/skins/wordpress/images/video.png differ diff --git a/wp-includes/js/tinymce/skins/wordpress/wp-content.css b/wp-includes/js/tinymce/skins/wordpress/wp-content.css new file mode 100644 index 0000000..47a820b --- /dev/null +++ b/wp-includes/js/tinymce/skins/wordpress/wp-content.css @@ -0,0 +1,640 @@ +/* Additional default styles for the editor */ + +html { + cursor: text; +} + +html.ios { + height: 100%; +} + +.ios body#tinymce { + height: 200%; + max-width: none; +} + +body { + font-family: Georgia, "Times New Roman", "Bitstream Charter", Times, serif; + font-size: 16px; + line-height: 1.5; + color: #333; + margin: 9px 10px; + max-width: 100%; + -webkit-font-smoothing: antialiased !important; +} + +body.rtl { + font-family: Tahoma, "Times New Roman", "Bitstream Charter", Times, serif; +} + +body.locale-he-il { + font-family: Arial, "Times New Roman", "Bitstream Charter", Times, serif; +} + +body.wp-autoresize { + overflow: visible !important; + /* The padding ensures margins of the children are contained in the body. */ + padding-top: 1px !important; + padding-bottom: 1px !important; + padding-left: 0 !important; + padding-right: 0 !important; +} + +/* When font-weight is different than the default browser style, +Chrome and Safari replace and with spans with inline styles on pasting?! */ +body.webkit strong, +body.webkit b { + font-weight: bold !important; +} + +pre { + font-family: Consolas, Monaco, monospace; +} + +td, +th { + font-family: inherit; + font-size: inherit; +} + +/* DFW mode */ +html.wp-fullscreen, +html.wp-fullscreen body#tinymce { + width: auto; + max-width: none; + min-height: 0; + overflow: hidden; + color: #333; + background: transparent; +} + +.aligncenter, +dl.aligncenter, +.html5-captions .wp-caption.aligncenter { + display: block; + margin-left: auto; + margin-right: auto; +} + +.alignleft { + float: left; +} + +.alignright { + float: right; +} + +.wp-caption { + border: 1px solid #ddd; + text-align: center; + background-color: #f3f3f3; + padding-top: 4px; + margin: 10px 0; +} + +.html5-captions .wp-caption { + border: none; + background-color: transparent; + margin: 0; + padding: 0; +} + +.mceIEcenter { + text-align: center; +} + +.wp-caption img { + margin: 0; + padding: 0; + border: 0 none; +} + +div.mceTemp { + -ms-user-select: element; +} + +dl.wp-caption, +dl.wp-caption * { + -webkit-user-drag: none; +} + +.wp-caption-dd { + font-size: 11px; + line-height: 17px; + padding: 0 4px 5px; + margin: 0; +} + +/* Remove blue highlighting of selected images in WebKit */ +img::selection { + background-color: transparent; +} + +/* Styles for the WordPress plugins */ +.mce-content-body img[data-mce-placeholder] { + border-radius: 0; + padding: 0; +} + +.mce-content-body img[data-wp-more] { + border: 0; + -webkit-box-shadow: none; + box-shadow: none; + width: 96%; + height: 16px; + display: block; + margin: 15px auto 0; + outline: 0; + cursor: default; +} + +.mce-content-body img[data-wp-more][data-mce-selected] { + outline: 1px dotted #888; +} + +.mce-content-body img[data-wp-more="more"] { + background: transparent url( images/more.png ) repeat-y scroll center center; +} + +.mce-content-body img[data-wp-more="nextpage"] { + background: transparent url( images/pagebreak.png ) repeat-y scroll center center; +} + +/* Gallery, audio, video placeholders */ +.mce-content-body img.wp-media { + border: 1px solid #aaa; + background-color: #f2f2f2; + background-repeat: no-repeat; + background-position: center center; + width: 99%; + height: 250px; + outline: 0; + cursor: pointer; +} + +.mce-content-body img.wp-media:hover { + background-color: #ededed; + border-color: #777; +} + +.mce-content-body img.wp-media.wp-media-selected { + background-color: #d8d8d8; + border-color: #777; +} + +.mce-content-body img.wp-media.wp-gallery { + background-image: url(images/gallery.png); +} + +/* Image resize handles */ +.mce-content-body div.mce-resizehandle { + border-color: #777; + width: 7px; + height: 7px; +} + +.mce-content-body img[data-mce-selected] { + outline: 1px solid #777; +} + +.mce-content-body img[data-mce-resize="false"] { + outline: 0; +} + +audio, +video, +embed { + display: -moz-inline-stack; + display: inline-block; +} + +audio { + visibility: hidden; +} + +/** + * WP Views + */ + +.wpview-wrap { + width: 99.99%; /* All IE need hasLayout, incl. 11 (ugh, not again!!) */ + position: relative; + clear: both; +} + +/* delegate the handling of the selection to the wpview tinymce plugin */ +.wpview-wrap, +.wpview-wrap * { + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; +} + +/* hide the shortcode content, but allow the content to still be selected */ +.wpview-wrap .wpview-clipboard, +.wpview-wrap > p { + position: absolute; + top: 0; + left: 0; + z-index: -1; + clip: rect(1px 1px 1px 1px); /* IE7 */ + clip: rect(1px, 1px, 1px, 1px); + overflow: hidden; + outline: 0; + padding: 0; + border: 0; + width: 1px; + height: 1px; +} + +/* An ugly box will appear when this is focussed in IE, so we'll move it outside the window. */ +.wpview-wrap.wpview-selection-before > p, +.wpview-wrap.wpview-selection-after > p { + left: -10000px; +} + +.wpview-wrap .wpview-clipboard, +.wpview-wrap .wpview-clipboard *, +.wpview-wrap > p { + -moz-user-select: text; + -webkit-user-select: text; + -ms-user-select: text; + user-select: text; +} + +.has-focus .wpview-wrap.wpview-selection-before:before, +.has-focus .wpview-wrap.wpview-selection-after:before { + content: ''; + margin: 0; + padding: 0; + position: absolute; + top: -2px; + left: -3px; + bottom: -2px; + width: 1px; + background-color: black; + background-color: currentcolor; + opacity: 1; +} + +.has-focus .wpview-wrap.wpview-selection-after:before { + left: auto; + right: -3px; +} + +.has-focus .wpview-wrap.wpview-cursor-hide:before { + opacity: 0; +} + +/** + * Media previews + */ +.wpview-wrap { + position: relative; + margin-bottom: 16px; + border: 1px solid transparent; +} + +.wpview-wrap[data-mce-selected] { + background-color: rgba(0,0,0,0.1); + border-color: rgba(0,0,0,0.3); +} + +.ie8 .wpview-wrap[data-mce-selected], +.ie7 .wpview-wrap[data-mce-selected] { + background-color: #e5e5e5; + border-color: #777; +} + +.wpview-overlay { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; +} + +.wpview-wrap[data-mce-selected] .wpview-overlay { + display: none; +} + +.wpview-wrap .toolbar { + position: absolute; + top: -43px; + left: 45%; + left: calc(50% - 32px); + display: none; + z-index: 100; + background-color: #f5f5f5; + border: 1px solid #aaa; + padding: 1px; + cursor: default; + -webkit-border-radius: 2px; + border-radius: 2px; + -webkit-box-shadow: 0 1px 4px rgba( 0, 0, 0, 0.2 ); + box-shadow: 0 1px 4px rgba( 0, 0, 0, 0.2 ); + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + margin-bottom: 8px; +} + +.wpview-wrap[data-mce-selected] .toolbar { + display: block; +} + +.wpview-wrap .toolbar:before, +.wpview-wrap .toolbar:after { + position: absolute; + left: 50%; + display: block; + width: 0; + height: 0; + border-style: solid; + border-color: transparent; + border-width: 9px; + margin-left: -9px; + content: ''; +} + +.wpview-wrap .toolbar:after { + border-width: 8px; + margin-left: -8px; +} + +.wpview-wrap .toolbar.mce-arrow-down:before { + bottom: -18px; + border-top-color: #aaa; +} + +.wpview-wrap .toolbar.mce-arrow-down:after { + bottom: -16px; + border-top-color: #f5f5f5; +} + +.wpview-wrap .toolbar.mce-arrow-up:before { + top: -18px; + border-bottom-color: #aaa; +} + +.wpview-wrap .toolbar.mce-arrow-up:after { + top: -16px; + border-bottom-color: #f5f5f5; +} + +.wpview-wrap .toolbar div { + margin: 2px; + padding: 2px 3px; + width: 20px; + height: 20px; + color: #777; + cursor: pointer; + font-size: 20px; + border: 1px solid transparent; + border-radius: 2px; +} + +.wpview-wrap .toolbar div:hover { + background-color: #fafafa; + border-color: #999; + color: #222; + -webkit-box-shadow: inset 0 1px 0 #fff, 0 1px 0 rgba( 0, 0, 0, 0.08 ); + box-shadow: inset 0 1px 0 #fff, 0 1px 0 rgba( 0, 0, 0, 0.08 ); + outline: none; +} + +.wpview-wrap .loading-placeholder { + border: 1px dashed #ccc; + padding: 10px; +} + +.wpview-wrap[data-mce-selected] .loading-placeholder { + border-color: transparent; +} + +/* A little "loading" animation, not showing in IE < 10 */ +.wpview-wrap .wpview-loading { + width: 60px; + height: 5px; + overflow: hidden; + background-color: transparent; + margin: 10px auto 0; +} + +.wpview-wrap .wpview-loading ins { + background-color: #333; + margin: 0 0 0 -60px; + width: 60px; + height: 5px; + display: block; + -webkit-animation: wpview-loading 1.3s infinite 1s linear; + animation: wpview-loading 1.3s infinite 1s linear; +} + +@-webkit-keyframes wpview-loading { + 0% { + margin-left: -60px; + } + 100% { + margin-left: 60px; + } +} + +@keyframes wpview-loading { + 0% { + margin-left: -60px; + } + 100% { + margin-left: 60px; + } +} + +.wpview-wrap .wpview-content > iframe { + max-width: 100%; + background: transparent; +} + +.ie8 .wpview-wrap .toolbar div, +.ie7 .wpview-wrap .toolbar div { + display: inline; + padding: 4px; +} + +.ie8 .dashicons-edit, +.ie7 .dashicons-edit { + background-image: url(images/dashicon-edit.png); +} + +.ie8 .dashicons-no, +.ie7 .dashicons-no { + background-image: url(images/dashicon-no.png); +} + +.wpview-error { + border: 1px solid #dedede; + padding: 1em 0; + margin: 0; + word-wrap: break-word; +} + +.wpview-wrap[data-mce-selected] .wpview-error { + border-color: transparent; +} + +.wpview-error .dashicons, +.loading-placeholder .dashicons { + display: block; + margin: 0 auto; + width: 32px; + height: 32px; + font-size: 32px; +} + +.wpview-error p { + margin: 0; + text-align: center; + font-family: 'Open Sans', sans-serif; +} + +.wont-play { + padding: 4px 0; +} + +.wont-play p { + font-size: 13px; + line-height: 1.3; + display: block; + width: 70%; + margin: 0 15%; + text-align: center; +} + +.wpview-type-gallery:after { + content: ''; + display: table; + clear: both; +} + +.gallery img[data-mce-selected]:focus { + outline: none; +} + +.gallery a { + cursor: default; +} + +.gallery { + margin: auto -6px; + padding: 6px 0; + line-height: 1; + overflow-x: hidden; +} + +.ie7 .gallery, +.ie8 .gallery { + margin: auto; +} + + +.gallery .gallery-item { + float: left; + margin: 0; + text-align: center; + padding: 6px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.ie7 .gallery .gallery-item, +.ie8 .gallery .gallery-item { + padding: 6px 0; +} + +.gallery .gallery-caption, +.gallery .gallery-icon { + margin: 0; +} + +.gallery .gallery-caption { + font-size: 13px; + margin: 4px 0; +} + +.gallery-columns-1 .gallery-item { + width: 100%; +} + +.gallery-columns-2 .gallery-item { + width: 50%; +} + +.gallery-columns-3 .gallery-item { + width: 33.333%; +} + +.ie8 .gallery-columns-3 .gallery-item, +.ie7 .gallery-columns-3 .gallery-item { + width: 33%; +} + +.gallery-columns-4 .gallery-item { + width: 25%; +} + +.gallery-columns-5 .gallery-item { + width: 20%; +} + +.gallery-columns-6 .gallery-item { + width: 16.665%; +} + +.gallery-columns-7 .gallery-item { + width: 14.285%; +} + +.gallery-columns-8 .gallery-item { + width: 12.5%; +} + +.gallery-columns-9 .gallery-item { + width: 11.111%; +} + +.gallery img { + max-width: 100%; + height: auto; + border: none; + padding: 0; +} + +img.wp-oembed { + border: 1px dashed #888; + background: #f7f5f2 url(images/embedded.png) no-repeat scroll center center; + width: 300px; + height: 250px; + outline: 0; +} + +/* rtl */ +.rtl .gallery .gallery-item { + float: right; +} + +@media print, + (-o-min-device-pixel-ratio: 5/4), + (-webkit-min-device-pixel-ratio: 1.25), + (min-resolution: 120dpi) { + + .mce-content-body img.mce-wp-more { + background-image: url( images/more-2x.png ); + background-size: 1900px 20px; + } + + .mce-content-body img.mce-wp-nextpage { + background-image: url( images/pagebreak-2x.png ); + background-size: 1900px 20px; + } +} diff --git a/wp-includes/js/tinymce/themes/modern/theme.js b/wp-includes/js/tinymce/themes/modern/theme.js new file mode 100644 index 0000000..db77f1a --- /dev/null +++ b/wp-includes/js/tinymce/themes/modern/theme.js @@ -0,0 +1,617 @@ +/** + * theme.js + * + * Copyright, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/*global tinymce:true */ + +tinymce.ThemeManager.add('modern', function(editor) { + var self = this, settings = editor.settings, Factory = tinymce.ui.Factory, each = tinymce.each, DOM = tinymce.DOM; + + // Default menus + var defaultMenus = { + file: {title: 'File', items: 'newdocument'}, + edit: {title: 'Edit', items: 'undo redo | cut copy paste pastetext | selectall'}, + insert: {title: 'Insert', items: '|'}, + view: {title: 'View', items: 'visualaid |'}, + format: {title: 'Format', items: 'bold italic underline strikethrough superscript subscript | formats | removeformat'}, + table: {title: 'Table'}, + tools: {title: 'Tools'} + }; + + var defaultToolbar = "undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | " + + "bullist numlist outdent indent | link image"; + + /** + * Creates the toolbars from config and returns a toolbar array. + * + * @return {Array} Array with toolbars. + */ + function createToolbars() { + var toolbars = []; + + function addToolbar(items) { + var toolbarItems = [], buttonGroup; + + if (!items) { + return; + } + + each(items.split(/[ ,]/), function(item) { + var itemName; + + function bindSelectorChanged() { + var selection = editor.selection; + + if (itemName == "bullist") { + selection.selectorChanged('ul > li', function(state, args) { + var nodeName, i = args.parents.length; + + while (i--) { + nodeName = args.parents[i].nodeName; + if (nodeName == "OL" || nodeName == "UL") { + break; + } + } + + item.active(state && nodeName == "UL"); + }); + } + + if (itemName == "numlist") { + selection.selectorChanged('ol > li', function(state, args) { + var nodeName, i = args.parents.length; + + while (i--) { + nodeName = args.parents[i].nodeName; + if (nodeName == "OL" || nodeName == "UL") { + break; + } + } + + item.active(state && nodeName == "OL"); + }); + } + + if (item.settings.stateSelector) { + selection.selectorChanged(item.settings.stateSelector, function(state) { + item.active(state); + }, true); + } + + if (item.settings.disabledStateSelector) { + selection.selectorChanged(item.settings.disabledStateSelector, function(state) { + item.disabled(state); + }); + } + } + + if (item == "|") { + buttonGroup = null; + } else { + if (Factory.has(item)) { + item = {type: item}; + + if (settings.toolbar_items_size) { + item.size = settings.toolbar_items_size; + } + + toolbarItems.push(item); + buttonGroup = null; + } else { + if (!buttonGroup) { + buttonGroup = {type: 'buttongroup', items: []}; + toolbarItems.push(buttonGroup); + } + + if (editor.buttons[item]) { + // TODO: Move control creation to some UI class + itemName = item; + item = editor.buttons[itemName]; + + if (typeof(item) == "function") { + item = item(); + } + + item.type = item.type || 'button'; + + if (settings.toolbar_items_size) { + item.size = settings.toolbar_items_size; + } + + item = Factory.create(item); + buttonGroup.items.push(item); + + if (editor.initialized) { + bindSelectorChanged(); + } else { + editor.on('init', bindSelectorChanged); + } + } + } + } + }); + + toolbars.push({type: 'toolbar', layout: 'flow', items: toolbarItems}); + + return true; + } + + // Convert toolbar array to multiple options + if (tinymce.isArray(settings.toolbar)) { + // Empty toolbar array is the same as a disabled toolbar + if (settings.toolbar.length === 0) { + return; + } + + tinymce.each(settings.toolbar, function(toolbar, i) { + settings["toolbar" + (i + 1)] = toolbar; + }); + + delete settings.toolbar; + } + + // Generate toolbar + for (var i = 1; i < 10; i++) { + if (!addToolbar(settings["toolbar" + i])) { + break; + } + } + + // Generate toolbar or default toolbar unless it's disabled + if (!toolbars.length && settings.toolbar !== false) { + addToolbar(settings.toolbar || defaultToolbar); + } + + if (toolbars.length) { + return { + type: 'panel', + layout: 'stack', + classes: "toolbar-grp", + ariaRoot: true, + ariaRemember: true, + items: toolbars + }; + } + } + + /** + * Creates the menu buttons based on config. + * + * @return {Array} Menu buttons array. + */ + function createMenuButtons() { + var name, menuButtons = []; + + function createMenuItem(name) { + var menuItem; + + if (name == '|') { + return {text: '|'}; + } + + menuItem = editor.menuItems[name]; + + return menuItem; + } + + function createMenu(context) { + var menuButton, menu, menuItems, isUserDefined, removedMenuItems; + + removedMenuItems = tinymce.makeMap((settings.removed_menuitems || '').split(/[ ,]/)); + + // User defined menu + if (settings.menu) { + menu = settings.menu[context]; + isUserDefined = true; + } else { + menu = defaultMenus[context]; + } + + if (menu) { + menuButton = {text: menu.title}; + menuItems = []; + + // Default/user defined items + each((menu.items || '').split(/[ ,]/), function(item) { + var menuItem = createMenuItem(item); + + if (menuItem && !removedMenuItems[item]) { + menuItems.push(createMenuItem(item)); + } + }); + + // Added though context + if (!isUserDefined) { + each(editor.menuItems, function(menuItem) { + if (menuItem.context == context) { + if (menuItem.separator == 'before') { + menuItems.push({text: '|'}); + } + + if (menuItem.prependToContext) { + menuItems.unshift(menuItem); + } else { + menuItems.push(menuItem); + } + + if (menuItem.separator == 'after') { + menuItems.push({text: '|'}); + } + } + }); + } + + for (var i = 0; i < menuItems.length; i++) { + if (menuItems[i].text == '|') { + if (i === 0 || i == menuItems.length - 1) { + menuItems.splice(i, 1); + } + } + } + + menuButton.menu = menuItems; + + if (!menuButton.menu.length) { + return null; + } + } + + return menuButton; + } + + var defaultMenuBar = []; + if (settings.menu) { + for (name in settings.menu) { + defaultMenuBar.push(name); + } + } else { + for (name in defaultMenus) { + defaultMenuBar.push(name); + } + } + + var enabledMenuNames = typeof(settings.menubar) == "string" ? settings.menubar.split(/[ ,]/) : defaultMenuBar; + for (var i = 0; i < enabledMenuNames.length; i++) { + var menu = enabledMenuNames[i]; + menu = createMenu(menu); + + if (menu) { + menuButtons.push(menu); + } + } + + return menuButtons; + } + + /** + * Adds accessibility shortcut keys to panel. + * + * @param {tinymce.ui.Panel} panel Panel to add focus to. + */ + function addAccessibilityKeys(panel) { + function focus(type) { + var item = panel.find(type)[0]; + + if (item) { + item.focus(true); + } + } + + editor.shortcuts.add('Alt+F9', '', function() { + focus('menubar'); + }); + + editor.shortcuts.add('Alt+F10', '', function() { + focus('toolbar'); + }); + + editor.shortcuts.add('Alt+F11', '', function() { + focus('elementpath'); + }); + + panel.on('cancel', function() { + editor.focus(); + }); + } + + /** + * Resizes the editor to the specified width, height. + */ + function resizeTo(width, height) { + var containerElm, iframeElm, containerSize, iframeSize; + + function getSize(elm) { + return { + width: elm.clientWidth, + height: elm.clientHeight + }; + } + + containerElm = editor.getContainer(); + iframeElm = editor.getContentAreaContainer().firstChild; + containerSize = getSize(containerElm); + iframeSize = getSize(iframeElm); + + if (width !== null) { + width = Math.max(settings.min_width || 100, width); + width = Math.min(settings.max_width || 0xFFFF, width); + + DOM.setStyle(containerElm, 'width', width + (containerSize.width - iframeSize.width)); + DOM.setStyle(iframeElm, 'width', width); + } + + height = Math.max(settings.min_height || 100, height); + height = Math.min(settings.max_height || 0xFFFF, height); + DOM.setStyle(iframeElm, 'height', height); + + editor.fire('ResizeEditor'); + } + + function resizeBy(dw, dh) { + var elm = editor.getContentAreaContainer(); + self.resizeTo(elm.clientWidth + dw, elm.clientHeight + dh); + } + + /** + * Renders the inline editor UI. + * + * @return {Object} Name/value object with theme data. + */ + function renderInlineUI(args) { + var panel, inlineToolbarContainer; + + if (settings.fixed_toolbar_container) { + inlineToolbarContainer = DOM.select(settings.fixed_toolbar_container)[0]; + } + + function reposition() { + if (panel && panel.moveRel && panel.visible() && !panel._fixed) { + // TODO: This is kind of ugly and doesn't handle multiple scrollable elements + var scrollContainer = editor.selection.getScrollContainer(), body = editor.getBody(); + var deltaX = 0, deltaY = 0; + + if (scrollContainer) { + var bodyPos = DOM.getPos(body), scrollContainerPos = DOM.getPos(scrollContainer); + + deltaX = Math.max(0, scrollContainerPos.x - bodyPos.x); + deltaY = Math.max(0, scrollContainerPos.y - bodyPos.y); + } + + panel.fixed(false).moveRel(body, editor.rtl ? ['tr-br', 'br-tr'] : ['tl-bl', 'bl-tl', 'tr-br']).moveBy(deltaX, deltaY); + } + } + + function show() { + if (panel) { + panel.show(); + reposition(); + DOM.addClass(editor.getBody(), 'mce-edit-focus'); + } + } + + function hide() { + if (panel) { + panel.hide(); + DOM.removeClass(editor.getBody(), 'mce-edit-focus'); + } + } + + function render() { + if (panel) { + if (!panel.visible()) { + show(); + } + + return; + } + + // Render a plain panel inside the inlineToolbarContainer if it's defined + panel = self.panel = Factory.create({ + type: inlineToolbarContainer ? 'panel' : 'floatpanel', + role: 'application', + classes: 'tinymce tinymce-inline', + layout: 'flex', + direction: 'column', + align: 'stretch', + autohide: false, + autofix: true, + fixed: !!inlineToolbarContainer, + border: 1, + items: [ + settings.menubar === false ? null : {type: 'menubar', border: '0 0 1 0', items: createMenuButtons()}, + createToolbars() + ] + }); + + // Add statusbar + /*if (settings.statusbar !== false) { + panel.add({type: 'panel', classes: 'statusbar', layout: 'flow', border: '1 0 0 0', items: [ + {type: 'elementpath'} + ]}); + }*/ + + editor.fire('BeforeRenderUI'); + panel.renderTo(inlineToolbarContainer || document.body).reflow(); + + addAccessibilityKeys(panel); + show(); + + editor.on('nodeChange', reposition); + editor.on('activate', show); + editor.on('deactivate', hide); + + editor.nodeChanged(); + } + + settings.content_editable = true; + + editor.on('focus', function() { + // Render only when the CSS file has been loaded + if (args.skinUiCss) { + tinymce.DOM.styleSheetLoader.load(args.skinUiCss, render, render); + } else { + render(); + } + }); + + editor.on('blur hide', hide); + + // Remove the panel when the editor is removed + editor.on('remove', function() { + if (panel) { + panel.remove(); + panel = null; + } + }); + + // Preload skin css + if (args.skinUiCss) { + tinymce.DOM.styleSheetLoader.load(args.skinUiCss); + } + + return {}; + } + + /** + * Renders the iframe editor UI. + * + * @param {Object} args Details about target element etc. + * @return {Object} Name/value object with theme data. + */ + function renderIframeUI(args) { + var panel, resizeHandleCtrl, startSize; + + if (args.skinUiCss) { + tinymce.DOM.loadCSS(args.skinUiCss); + } + + // Basic UI layout + panel = self.panel = Factory.create({ + type: 'panel', + role: 'application', + classes: 'tinymce', + style: 'visibility: hidden', + layout: 'stack', + border: 1, + items: [ + settings.menubar === false ? null : {type: 'menubar', border: '0 0 1 0', items: createMenuButtons()}, + createToolbars(), + {type: 'panel', name: 'iframe', layout: 'stack', classes: 'edit-area', html: '', border: '1 0 0 0'} + ] + }); + + if (settings.resize !== false) { + resizeHandleCtrl = { + type: 'resizehandle', + direction: settings.resize, + + onResizeStart: function() { + var elm = editor.getContentAreaContainer().firstChild; + + startSize = { + width: elm.clientWidth, + height: elm.clientHeight + }; + }, + + onResize: function(e) { + if (settings.resize == 'both') { + resizeTo(startSize.width + e.deltaX, startSize.height + e.deltaY); + } else { + resizeTo(null, startSize.height + e.deltaY); + } + } + }; + } + + // Add statusbar if needed + if (settings.statusbar !== false) { + panel.add({type: 'panel', name: 'statusbar', classes: 'statusbar', layout: 'flow', border: '1 0 0 0', ariaRoot: true, items: [ + {type: 'elementpath'}, + resizeHandleCtrl + ]}); + } + + if (settings.readonly) { + panel.find('*').disabled(true); + } + + editor.fire('BeforeRenderUI'); + panel.renderBefore(args.targetNode).reflow(); + + if (settings.width) { + tinymce.DOM.setStyle(panel.getEl(), 'width', settings.width); + } + + // Remove the panel when the editor is removed + editor.on('remove', function() { + panel.remove(); + panel = null; + }); + + // Add accesibility shortkuts + addAccessibilityKeys(panel); + + return { + iframeContainer: panel.find('#iframe')[0].getEl(), + editorContainer: panel.getEl() + }; + } + + /** + * Renders the UI for the theme. This gets called by the editor. + * + * @param {Object} args Details about target element etc. + * @return {Object} Theme UI data items. + */ + self.renderUI = function(args) { + var skin = settings.skin !== false ? settings.skin || 'lightgray' : false; + + if (skin) { + var skinUrl = settings.skin_url; + + if (skinUrl) { + skinUrl = editor.documentBaseURI.toAbsolute(skinUrl); + } else { + skinUrl = tinymce.baseURL + '/skins/' + skin; + } + + // Load special skin for IE7 + // TODO: Remove this when we drop IE7 support + if (tinymce.Env.documentMode <= 7) { + args.skinUiCss = skinUrl + '/skin.ie7.min.css'; + } else { + args.skinUiCss = skinUrl + '/skin.min.css'; + } + + // Load content.min.css or content.inline.min.css + editor.contentCSS.push(skinUrl + '/content' + (editor.inline ? '.inline' : '') + '.min.css'); + } + + // Handle editor setProgressState change + editor.on('ProgressState', function(e) { + self.throbber = self.throbber || new tinymce.ui.Throbber(self.panel.getEl('body')); + + if (e.state) { + self.throbber.show(e.time); + } else { + self.throbber.hide(); + } + }); + + if (settings.inline) { + return renderInlineUI(args); + } + + return renderIframeUI(args); + }; + + self.resizeTo = resizeTo; + self.resizeBy = resizeBy; +}); diff --git a/wp-includes/js/tinymce/themes/modern/theme.min.js b/wp-includes/js/tinymce/themes/modern/theme.min.js new file mode 100644 index 0000000..c132ad5 --- /dev/null +++ b/wp-includes/js/tinymce/themes/modern/theme.min.js @@ -0,0 +1 @@ +tinymce.ThemeManager.add("modern",function(a){function b(){function b(b){var d,e=[];if(b)return l(b.split(/[ ,]/),function(b){function c(){var c=a.selection;"bullist"==f&&c.selectorChanged("ul > li",function(a,c){for(var d,e=c.parents.length;e--&&(d=c.parents[e].nodeName,"OL"!=d&&"UL"!=d););b.active(a&&"UL"==d)}),"numlist"==f&&c.selectorChanged("ol > li",function(a,c){for(var d,e=c.parents.length;e--&&(d=c.parents[e].nodeName,"OL"!=d&&"UL"!=d););b.active(a&&"OL"==d)}),b.settings.stateSelector&&c.selectorChanged(b.settings.stateSelector,function(a){b.active(a)},!0),b.settings.disabledStateSelector&&c.selectorChanged(b.settings.disabledStateSelector,function(a){b.disabled(a)})}var f;"|"==b?d=null:k.has(b)?(b={type:b},j.toolbar_items_size&&(b.size=j.toolbar_items_size),e.push(b),d=null):(d||(d={type:"buttongroup",items:[]},e.push(d)),a.buttons[b]&&(f=b,b=a.buttons[f],"function"==typeof b&&(b=b()),b.type=b.type||"button",j.toolbar_items_size&&(b.size=j.toolbar_items_size),b=k.create(b),d.items.push(b),a.initialized?c():a.on("init",c)))}),c.push({type:"toolbar",layout:"flow",items:e}),!0}var c=[];if(tinymce.isArray(j.toolbar)){if(0===j.toolbar.length)return;tinymce.each(j.toolbar,function(a,b){j["toolbar"+(b+1)]=a}),delete j.toolbar}for(var d=1;10>d&&b(j["toolbar"+d]);d++);return c.length||j.toolbar===!1||b(j.toolbar||o),c.length?{type:"panel",layout:"stack",classes:"toolbar-grp",ariaRoot:!0,ariaRemember:!0,items:c}:void 0}function c(){function b(b){var c;return"|"==b?{text:"|"}:c=a.menuItems[b]}function c(c){var d,e,f,g,h;if(h=tinymce.makeMap((j.removed_menuitems||"").split(/[ ,]/)),j.menu?(e=j.menu[c],g=!0):e=n[c],e){d={text:e.title},f=[],l((e.items||"").split(/[ ,]/),function(a){var c=b(a);c&&!h[a]&&f.push(b(a))}),g||l(a.menuItems,function(a){a.context==c&&("before"==a.separator&&f.push({text:"|"}),a.prependToContext?f.unshift(a):f.push(a),"after"==a.separator&&f.push({text:"|"}))});for(var i=0;i/langs/_dlg.js lang pack file. + * + * @method requireLangPack + */ + requireLangPack : function() { + var self = this, url = self.getWindowArg('plugin_url') || self.getWindowArg('theme_url'), settings = self.editor.settings, lang; + + if (settings.language !== false) { + lang = settings.language || "en"; + } + + if (url && lang && self.features.translate_i18n !== false && settings.language_load !== false) { + url += '/langs/' + lang + '_dlg.js'; + + if (!tinymce.ScriptLoader.isDone(url)) { + document.write(''); + tinymce.ScriptLoader.markDone(url); + } + } + }, + + /** + * Executes a color picker on the specified element id. When the user + * then selects a color it will be set as the value of the specified element. + * + * @method pickColor + * @param {DOMEvent} e DOM event object. + * @param {string} element_id Element id to be filled with the color value from the picker. + */ + pickColor : function(e, element_id) { + var el = document.getElementById(element_id), colorPickerCallback = this.editor.settings.color_picker_callback; + if (colorPickerCallback) { + colorPickerCallback.call( + this.editor, + function (value) { + el.value = value; + try { + el.onchange(); + } catch (ex) { + // Try fire event, ignore errors + } + }, + el.value + ); + } + }, + + /** + * Opens a filebrowser/imagebrowser this will set the output value from + * the browser as a value on the specified element. + * + * @method openBrowser + * @param {string} element_id Id of the element to set value in. + * @param {string} type Type of browser to open image/file/flash. + * @param {string} option Option name to get the file_broswer_callback function name from. + */ + openBrowser : function(element_id, type) { + tinyMCEPopup.restoreSelection(); + this.editor.execCallback('file_browser_callback', element_id, document.getElementById(element_id).value, type, window); + }, + + /** + * Creates a confirm dialog. Please don't use the blocking behavior of this + * native version use the callback method instead then it can be extended. + * + * @method confirm + * @param {String} t Title for the new confirm dialog. + * @param {function} cb Callback function to be executed after the user has selected ok or cancel. + * @param {Object} s Optional scope to execute the callback in. + */ + confirm : function(t, cb, s) { + this.editor.windowManager.confirm(t, cb, s, window); + }, + + /** + * Creates a alert dialog. Please don't use the blocking behavior of this + * native version use the callback method instead then it can be extended. + * + * @method alert + * @param {String} tx Title for the new alert dialog. + * @param {function} cb Callback function to be executed after the user has selected ok. + * @param {Object} s Optional scope to execute the callback in. + */ + alert : function(tx, cb, s) { + this.editor.windowManager.alert(tx, cb, s, window); + }, + + /** + * Closes the current window. + * + * @method close + */ + close : function() { + var t = this; + + // To avoid domain relaxing issue in Opera + function close() { + t.editor.windowManager.close(window); + tinymce = tinyMCE = t.editor = t.params = t.dom = t.dom.doc = null; // Cleanup + } + + if (tinymce.isOpera) { + t.getWin().setTimeout(close, 0); + } else { + close(); + } + }, + + // Internal functions + + _restoreSelection : function() { + var e = window.event.srcElement; + + if (e.nodeName == 'INPUT' && (e.type == 'submit' || e.type == 'button')) { + tinyMCEPopup.restoreSelection(); + } + }, + +/* _restoreSelection : function() { + var e = window.event.srcElement; + + // If user focus a non text input or textarea + if ((e.nodeName != 'INPUT' && e.nodeName != 'TEXTAREA') || e.type != 'text') + tinyMCEPopup.restoreSelection(); + },*/ + + _onDOMLoaded : function() { + var t = tinyMCEPopup, ti = document.title, h, nv; + + // Translate page + if (t.features.translate_i18n !== false) { + var map = { + "update": "Ok", + "insert": "Ok", + "cancel": "Cancel", + "not_set": "--", + "class_name": "Class name", + "browse": "Browse" + }; + + var langCode = tinymce.settings.language || 'en'; + for (var key in map) { + tinymce.i18n.data[langCode + "." + key] = tinymce.i18n.translate(map[key]); + } + + h = document.body.innerHTML; + + // Replace a=x with a="x" in IE + if (tinymce.isIE) { + h = h.replace(/ (value|title|alt)=([^"][^\s>]+)/gi, ' $1="$2"'); + } + + document.dir = t.editor.getParam('directionality',''); + + if ((nv = t.editor.translate(h)) && nv != h) { + document.body.innerHTML = nv; + } + + if ((nv = t.editor.translate(ti)) && nv != ti) { + document.title = ti = nv; + } + } + + if (!t.editor.getParam('browser_preferred_colors', false) || !t.isWindow) { + t.dom.addClass(document.body, 'forceColors'); + } + + document.body.style.display = ''; + + // Restore selection in IE when focus is placed on a non textarea or input element of the type text + if (tinymce.Env.ie) { + if (tinymce.Env.ie < 11) { + document.attachEvent('onmouseup', tinyMCEPopup._restoreSelection); + + // Add base target element for it since it would fail with modal dialogs + t.dom.add(t.dom.select('head')[0], 'base', {target: '_self'}); + } else { + document.addEventListener('mouseup', tinyMCEPopup._restoreSelection, false); + } + } + + t.restoreSelection(); + t.resizeToInnerSize(); + + // Set inline title + if (!t.isWindow) { + t.editor.windowManager.setTitle(window, ti); + } else { + window.focus(); + } + + if (!tinymce.isIE && !t.isWindow) { + t.dom.bind(document, 'focus', function() { + t.editor.windowManager.focus(t.id); + }); + } + + // Patch for accessibility + tinymce.each(t.dom.select('select'), function(e) { + e.onkeydown = tinyMCEPopup._accessHandler; + }); + + // Call onInit + // Init must be called before focus so the selection won't get lost by the focus call + tinymce.each(t.listeners, function(o) { + o.func.call(o.scope, t.editor); + }); + + // Move focus to window + if (t.getWindowArg('mce_auto_focus', true)) { + window.focus(); + + // Focus element with mceFocus class + tinymce.each(document.forms, function(f) { + tinymce.each(f.elements, function(e) { + if (t.dom.hasClass(e, 'mceFocus') && !e.disabled) { + e.focus(); + return false; // Break loop + } + }); + }); + } + + document.onkeyup = tinyMCEPopup._closeWinKeyHandler; + + if ('textContent' in document) { + t.uiWindow.getEl('head').firstChild.textContent = document.title; + } else { + t.uiWindow.getEl('head').firstChild.innerText = document.title; + } + }, + + _accessHandler : function(e) { + e = e || window.event; + + if (e.keyCode == 13 || e.keyCode == 32) { + var elm = e.target || e.srcElement; + + if (elm.onchange) { + elm.onchange(); + } + + return tinymce.dom.Event.cancel(e); + } + }, + + _closeWinKeyHandler : function(e) { + e = e || window.event; + + if (e.keyCode == 27) { + tinyMCEPopup.close(); + } + }, + + _eventProxy: function(id) { + return function(evt) { + tinyMCEPopup.dom.events.callNativeHandler(id, evt); + }; + } +}; + +tinyMCEPopup.init(); + +tinymce.util.Dispatcher = function(scope) { + this.scope = scope || this; + this.listeners = []; + + this.add = function(callback, scope) { + this.listeners.push({cb : callback, scope : scope || this.scope}); + + return callback; + }; + + this.addToTop = function(callback, scope) { + var self = this, listener = {cb : callback, scope : scope || self.scope}; + + // Create new listeners if addToTop is executed in a dispatch loop + if (self.inDispatch) { + self.listeners = [listener].concat(self.listeners); + } else { + self.listeners.unshift(listener); + } + + return callback; + }; + + this.remove = function(callback) { + var listeners = this.listeners, output = null; + + tinymce.each(listeners, function(listener, i) { + if (callback == listener.cb) { + output = listener; + listeners.splice(i, 1); + return false; + } + }); + + return output; + }; + + this.dispatch = function() { + var self = this, returnValue, args = arguments, i, listeners = self.listeners, listener; + + self.inDispatch = true; + + // Needs to be a real loop since the listener count might change while looping + // And this is also more efficient + for (i = 0; i < listeners.length; i++) { + listener = listeners[i]; + returnValue = listener.cb.apply(listener.scope, args.length > 0 ? args : [listener.scope]); + + if (returnValue === false) { + break; + } + } + + self.inDispatch = false; + + return returnValue; + }; +}; diff --git a/wp-includes/js/tinymce/tinymce.min.js b/wp-includes/js/tinymce/tinymce.min.js new file mode 100644 index 0000000..e6acb82 --- /dev/null +++ b/wp-includes/js/tinymce/tinymce.min.js @@ -0,0 +1,11 @@ +// 4.1.7 (2014-11-27) +!function(e,t){"use strict";function n(e,t){for(var n,r=[],i=0;ir;r++)if(o=n[r],o&&o.func.call(o.scope,e)===!1&&e.preventDefault(),e.isImmediatePropagationStopped())return}var a=this,s={},l,c,u,d,f;c=o+(+new Date).toString(32),d="onmouseenter"in document.documentElement,u="onfocusin"in document.documentElement,f={mouseenter:"mouseover",mouseleave:"mouseout"},l=1,a.domLoaded=!1,a.events=s,a.bind=function(t,o,p,h){function m(e){i(n(e||_.event),g)}var g,v,y,b,C,x,w,_=window;if(t&&3!==t.nodeType&&8!==t.nodeType){for(t[c]?g=t[c]:(g=l++,t[c]=g,s[g]={}),h=h||t,o=o.split(" "),y=o.length;y--;)b=o[y],x=m,C=w=!1,"DOMContentLoaded"===b&&(b="ready"),a.domLoaded&&"ready"===b&&"complete"==t.readyState?p.call(h,n({type:b})):(d||(C=f[b],C&&(x=function(e){var t,r;if(t=e.currentTarget,r=e.relatedTarget,r&&t.contains)r=t.contains(r);else for(;r&&r!==t;)r=r.parentNode;r||(e=n(e||_.event),e.type="mouseout"===e.type?"mouseleave":"mouseenter",e.target=t,i(e,g))})),u||"focusin"!==b&&"focusout"!==b||(w=!0,C="focusin"===b?"focus":"blur",x=function(e){e=n(e||_.event),e.type="focus"===e.type?"focusin":"focusout",i(e,g)}),v=s[g][b],v?"ready"===b&&a.domLoaded?p({type:b}):v.push({func:p,scope:h}):(s[g][b]=v=[{func:p,scope:h}],v.fakeName=C,v.capture=w,v.nativeHandler=x,"ready"===b?r(t,x,a):e(t,C||b,x,w)));return t=v=0,p}},a.unbind=function(e,n,r){var i,o,l,u,d,f;if(!e||3===e.nodeType||8===e.nodeType)return a;if(i=e[c]){if(f=s[i],n){for(n=n.split(" "),l=n.length;l--;)if(d=n[l],o=f[d]){if(r)for(u=o.length;u--;)if(o[u].func===r){var p=o.nativeHandler,h=o.fakeName,m=o.capture;o=o.slice(0,u).concat(o.slice(u+1)),o.nativeHandler=p,o.fakeName=h,o.capture=m,f[d]=o}r&&0!==o.length||(delete f[d],t(e,o.fakeName||d,o.nativeHandler,o.capture))}}else{for(d in f)o=f[d],t(e,o.fakeName||d,o.nativeHandler,o.capture);f={}}for(d in f)return a;delete s[i];try{delete e[c]}catch(g){e[c]=null}}return a},a.fire=function(e,t,r){var o;if(!e||3===e.nodeType||8===e.nodeType)return a;r=n(null,r),r.type=t,r.target=e;do o=e[c],o&&i(r,o),e=e.parentNode||e.ownerDocument||e.defaultView||e.parentWindow;while(e&&!r.isPropagationStopped());return a},a.clean=function(e){var t,n,r=a.unbind;if(!e||3===e.nodeType||8===e.nodeType)return a;if(e[c]&&r(e),e.getElementsByTagName||(e=e.document),e&&e.getElementsByTagName)for(r(e),n=e.getElementsByTagName("*"),t=n.length;t--;)e=n[t],e[c]&&r(e);return a},a.destroy=function(){s={}},a.cancel=function(e){return e&&(e.preventDefault(),e.stopImmediatePropagation()),!1}}var o="mce-data-",a=/^(?:mouse|contextmenu)|click/,s={keyLocation:1,layerX:1,layerY:1,returnValue:1};return i.Event=new i,i.Event.bind(window,"ready",function(){}),i}),r(c,[],function(){function e(e,t,n,r){var i,o,a,s,l,c,d,p,h,m;if((t?t.ownerDocument||t:z)!==D&&B(t),t=t||D,n=n||[],!e||"string"!=typeof e)return n;if(1!==(s=t.nodeType)&&9!==s)return[];if(H&&!r){if(i=vt.exec(e))if(a=i[1]){if(9===s){if(o=t.getElementById(a),!o||!o.parentNode)return n;if(o.id===a)return n.push(o),n}else if(t.ownerDocument&&(o=t.ownerDocument.getElementById(a))&&I(t,o)&&o.id===a)return n.push(o),n}else{if(i[2])return Z.apply(n,t.getElementsByTagName(e)),n;if((a=i[3])&&x.getElementsByClassName)return Z.apply(n,t.getElementsByClassName(a)),n}if(x.qsa&&(!M||!M.test(e))){if(p=d=F,h=t,m=9===s&&e,1===s&&"object"!==t.nodeName.toLowerCase()){for(c=N(e),(d=t.getAttribute("id"))?p=d.replace(bt,"\\$&"):t.setAttribute("id",p),p="[id='"+p+"'] ",l=c.length;l--;)c[l]=p+f(c[l]);h=yt.test(e)&&u(t.parentNode)||t,m=c.join(",")}if(m)try{return Z.apply(n,h.querySelectorAll(m)),n}catch(g){}finally{d||t.removeAttribute("id")}}}return S(e.replace(st,"$1"),t,n,r)}function n(){function e(n,r){return t.push(n+" ")>w.cacheLength&&delete e[t.shift()],e[n+" "]=r}var t=[];return e}function r(e){return e[F]=!0,e}function i(e){var t=D.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function o(e,t){for(var n=e.split("|"),r=e.length;r--;)w.attrHandle[n[r]]=t}function a(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||K)-(~e.sourceIndex||K);if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function s(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function l(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function c(e){return r(function(t){return t=+t,r(function(n,r){for(var i,o=e([],n.length,t),a=o.length;a--;)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function u(e){return e&&typeof e.getElementsByTagName!==Y&&e}function d(){}function f(e){for(var t=0,n=e.length,r="";n>t;t++)r+=e[t].value;return r}function p(e,t,n){var r=t.dir,i=n&&"parentNode"===r,o=V++;return t.first?function(t,n,o){for(;t=t[r];)if(1===t.nodeType||i)return e(t,n,o)}:function(t,n,a){var s,l,c=[W,o];if(a){for(;t=t[r];)if((1===t.nodeType||i)&&e(t,n,a))return!0}else for(;t=t[r];)if(1===t.nodeType||i){if(l=t[F]||(t[F]={}),(s=l[r])&&s[0]===W&&s[1]===o)return c[2]=s[2];if(l[r]=c,c[2]=e(t,n,a))return!0}}}function h(e){return e.length>1?function(t,n,r){for(var i=e.length;i--;)if(!e[i](t,n,r))return!1;return!0}:e[0]}function m(t,n,r){for(var i=0,o=n.length;o>i;i++)e(t,n[i],r);return r}function g(e,t,n,r,i){for(var o,a=[],s=0,l=e.length,c=null!=t;l>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),c&&t.push(s));return a}function v(e,t,n,i,o,a){return i&&!i[F]&&(i=v(i)),o&&!o[F]&&(o=v(o,a)),r(function(r,a,s,l){var c,u,d,f=[],p=[],h=a.length,v=r||m(t||"*",s.nodeType?[s]:s,[]),y=!e||!r&&t?v:g(v,f,e,s,l),b=n?o||(r?e:h||i)?[]:a:y;if(n&&n(y,b,s,l),i)for(c=g(b,p),i(c,[],s,l),u=c.length;u--;)(d=c[u])&&(b[p[u]]=!(y[p[u]]=d));if(r){if(o||e){if(o){for(c=[],u=b.length;u--;)(d=b[u])&&c.push(y[u]=d);o(null,b=[],c,l)}for(u=b.length;u--;)(d=b[u])&&(c=o?tt.call(r,d):f[u])>-1&&(r[c]=!(a[c]=d))}}else b=g(b===a?b.splice(h,b.length):b),o?o(null,a,b,l):Z.apply(a,b)})}function y(e){for(var t,n,r,i=e.length,o=w.relative[e[0].type],a=o||w.relative[" "],s=o?1:0,l=p(function(e){return e===t},a,!0),c=p(function(e){return tt.call(t,e)>-1},a,!0),u=[function(e,n,r){return!o&&(r||n!==T)||((t=n).nodeType?l(e,n,r):c(e,n,r))}];i>s;s++)if(n=w.relative[e[s].type])u=[p(h(u),n)];else{if(n=w.filter[e[s].type].apply(null,e[s].matches),n[F]){for(r=++s;i>r&&!w.relative[e[r].type];r++);return v(s>1&&h(u),s>1&&f(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace(st,"$1"),n,r>s&&y(e.slice(s,r)),i>r&&y(e=e.slice(r)),i>r&&f(e))}u.push(n)}return h(u)}function b(t,n){var i=n.length>0,o=t.length>0,a=function(r,a,s,l,c){var u,d,f,p=0,h="0",m=r&&[],v=[],y=T,b=r||o&&w.find.TAG("*",c),C=W+=null==y?1:Math.random()||.1,x=b.length;for(c&&(T=a!==D&&a);h!==x&&null!=(u=b[h]);h++){if(o&&u){for(d=0;f=t[d++];)if(f(u,a,s)){l.push(u);break}c&&(W=C)}i&&((u=!f&&u)&&p--,r&&m.push(u))}if(p+=h,i&&h!==p){for(d=0;f=n[d++];)f(m,v,a,s);if(r){if(p>0)for(;h--;)m[h]||v[h]||(v[h]=J.call(l));v=g(v)}Z.apply(l,v),c&&!r&&v.length>0&&p+n.length>1&&e.uniqueSort(l)}return c&&(W=C,T=y),m};return i?r(a):a}var C,x,w,_,E,N,k,S,T,R,A,B,D,L,H,M,P,O,I,F="sizzle"+-new Date,z=window.document,W=0,V=0,U=n(),$=n(),q=n(),j=function(e,t){return e===t&&(A=!0),0},Y=typeof t,K=1<<31,G={}.hasOwnProperty,X=[],J=X.pop,Q=X.push,Z=X.push,et=X.slice,tt=X.indexOf||function(e){for(var t=0,n=this.length;n>t;t++)if(this[t]===e)return t;return-1},nt="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",rt="[\\x20\\t\\r\\n\\f]",it="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",ot="\\["+rt+"*("+it+")(?:"+rt+"*([*^$|!~]?=)"+rt+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+it+"))|)"+rt+"*\\]",at=":("+it+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+ot+")*)|.*)\\)|)",st=new RegExp("^"+rt+"+|((?:^|[^\\\\])(?:\\\\.)*)"+rt+"+$","g"),lt=new RegExp("^"+rt+"*,"+rt+"*"),ct=new RegExp("^"+rt+"*([>+~]|"+rt+")"+rt+"*"),ut=new RegExp("="+rt+"*([^\\]'\"]*?)"+rt+"*\\]","g"),dt=new RegExp(at),ft=new RegExp("^"+it+"$"),pt={ID:new RegExp("^#("+it+")"),CLASS:new RegExp("^\\.("+it+")"),TAG:new RegExp("^("+it+"|[*])"),ATTR:new RegExp("^"+ot),PSEUDO:new RegExp("^"+at),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+rt+"*(even|odd|(([+-]|)(\\d*)n|)"+rt+"*(?:([+-]|)"+rt+"*(\\d+)|))"+rt+"*\\)|)","i"),bool:new RegExp("^(?:"+nt+")$","i"),needsContext:new RegExp("^"+rt+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+rt+"*((?:-\\d)?\\d*)"+rt+"*\\)|)(?=[^-]|$)","i")},ht=/^(?:input|select|textarea|button)$/i,mt=/^h\d$/i,gt=/^[^{]+\{\s*\[native \w/,vt=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,yt=/[+~]/,bt=/'|\\/g,Ct=new RegExp("\\\\([\\da-f]{1,6}"+rt+"?|("+rt+")|.)","ig"),xt=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)};try{Z.apply(X=et.call(z.childNodes),z.childNodes),X[z.childNodes.length].nodeType}catch(wt){Z={apply:X.length?function(e,t){Q.apply(e,et.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}x=e.support={},E=e.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},B=e.setDocument=function(e){var t,n=e?e.ownerDocument||e:z,r=n.defaultView;return n!==D&&9===n.nodeType&&n.documentElement?(D=n,L=n.documentElement,H=!E(n),r&&r!==r.top&&(r.addEventListener?r.addEventListener("unload",function(){B()},!1):r.attachEvent&&r.attachEvent("onunload",function(){B()})),x.attributes=i(function(e){return e.className="i",!e.getAttribute("className")}),x.getElementsByTagName=i(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),x.getElementsByClassName=gt.test(n.getElementsByClassName),x.getById=i(function(e){return L.appendChild(e).id=F,!n.getElementsByName||!n.getElementsByName(F).length}),x.getById?(w.find.ID=function(e,t){if(typeof t.getElementById!==Y&&H){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},w.filter.ID=function(e){var t=e.replace(Ct,xt);return function(e){return e.getAttribute("id")===t}}):(delete w.find.ID,w.filter.ID=function(e){var t=e.replace(Ct,xt);return function(e){var n=typeof e.getAttributeNode!==Y&&e.getAttributeNode("id");return n&&n.value===t}}),w.find.TAG=x.getElementsByTagName?function(e,t){return typeof t.getElementsByTagName!==Y?t.getElementsByTagName(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){for(;n=o[i++];)1===n.nodeType&&r.push(n);return r}return o},w.find.CLASS=x.getElementsByClassName&&function(e,t){return H?t.getElementsByClassName(e):void 0},P=[],M=[],(x.qsa=gt.test(n.querySelectorAll))&&(i(function(e){e.innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&M.push("[*^$]="+rt+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||M.push("\\["+rt+"*(?:value|"+nt+")"),e.querySelectorAll(":checked").length||M.push(":checked")}),i(function(e){var t=n.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&M.push("name"+rt+"*[*^$|!~]?="),e.querySelectorAll(":enabled").length||M.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),M.push(",.*:")})),(x.matchesSelector=gt.test(O=L.matches||L.webkitMatchesSelector||L.mozMatchesSelector||L.oMatchesSelector||L.msMatchesSelector))&&i(function(e){x.disconnectedMatch=O.call(e,"div"),O.call(e,"[s!='']:x"),P.push("!=",at)}),M=M.length&&new RegExp(M.join("|")),P=P.length&&new RegExp(P.join("|")),t=gt.test(L.compareDocumentPosition),I=t||gt.test(L.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return A=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r?r:(r=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1,1&r||!x.sortDetached&&t.compareDocumentPosition(e)===r?e===n||e.ownerDocument===z&&I(z,e)?-1:t===n||t.ownerDocument===z&&I(z,t)?1:R?tt.call(R,e)-tt.call(R,t):0:4&r?-1:1)}:function(e,t){if(e===t)return A=!0,0;var r,i=0,o=e.parentNode,s=t.parentNode,l=[e],c=[t];if(!o||!s)return e===n?-1:t===n?1:o?-1:s?1:R?tt.call(R,e)-tt.call(R,t):0;if(o===s)return a(e,t);for(r=e;r=r.parentNode;)l.unshift(r);for(r=t;r=r.parentNode;)c.unshift(r);for(;l[i]===c[i];)i++;return i?a(l[i],c[i]):l[i]===z?-1:c[i]===z?1:0},n):D},e.matches=function(t,n){return e(t,null,null,n)},e.matchesSelector=function(t,n){if((t.ownerDocument||t)!==D&&B(t),n=n.replace(ut,"='$1']"),!(!x.matchesSelector||!H||P&&P.test(n)||M&&M.test(n)))try{var r=O.call(t,n);if(r||x.disconnectedMatch||t.document&&11!==t.document.nodeType)return r}catch(i){}return e(n,D,null,[t]).length>0},e.contains=function(e,t){return(e.ownerDocument||e)!==D&&B(e),I(e,t)},e.attr=function(e,n){(e.ownerDocument||e)!==D&&B(e);var r=w.attrHandle[n.toLowerCase()],i=r&&G.call(w.attrHandle,n.toLowerCase())?r(e,n,!H):t;return i!==t?i:x.attributes||!H?e.getAttribute(n):(i=e.getAttributeNode(n))&&i.specified?i.value:null},e.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},e.uniqueSort=function(e){var t,n=[],r=0,i=0;if(A=!x.detectDuplicates,R=!x.sortStable&&e.slice(0),e.sort(j),A){for(;t=e[i++];)t===e[i]&&(r=n.push(i));for(;r--;)e.splice(n[r],1)}return R=null,e},_=e.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=_(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r++];)n+=_(t);return n},w=e.selectors={cacheLength:50,createPseudo:r,match:pt,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(Ct,xt),e[3]=(e[3]||e[4]||e[5]||"").replace(Ct,xt),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(t){return t[1]=t[1].toLowerCase(),"nth"===t[1].slice(0,3)?(t[3]||e.error(t[0]),t[4]=+(t[4]?t[5]+(t[6]||1):2*("even"===t[3]||"odd"===t[3])),t[5]=+(t[7]+t[8]||"odd"===t[3])):t[3]&&e.error(t[0]),t},PSEUDO:function(e){var t,n=!e[6]&&e[2];return pt.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&dt.test(n)&&(t=N(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(Ct,xt).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=U[e+" "];return t||(t=new RegExp("(^|"+rt+")"+e+"("+rt+"|$)"))&&U(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==Y&&e.getAttribute("class")||"")})},ATTR:function(t,n,r){return function(i){var o=e.attr(i,t);return null==o?"!="===n:n?(o+="","="===n?o===r:"!="===n?o!==r:"^="===n?r&&0===o.indexOf(r):"*="===n?r&&o.indexOf(r)>-1:"$="===n?r&&o.slice(-r.length)===r:"~="===n?(" "+o+" ").indexOf(r)>-1:"|="===n?o===r||o.slice(0,r.length+1)===r+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var c,u,d,f,p,h,m=o!==a?"nextSibling":"previousSibling",g=t.parentNode,v=s&&t.nodeName.toLowerCase(),y=!l&&!s;if(g){if(o){for(;m;){for(d=t;d=d[m];)if(s?d.nodeName.toLowerCase()===v:1===d.nodeType)return!1;h=m="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?g.firstChild:g.lastChild],a&&y){for(u=g[F]||(g[F]={}),c=u[e]||[],p=c[0]===W&&c[1],f=c[0]===W&&c[2],d=p&&g.childNodes[p];d=++p&&d&&d[m]||(f=p=0)||h.pop();)if(1===d.nodeType&&++f&&d===t){u[e]=[W,p,f];break}}else if(y&&(c=(t[F]||(t[F]={}))[e])&&c[0]===W)f=c[1];else for(;(d=++p&&d&&d[m]||(f=p=0)||h.pop())&&((s?d.nodeName.toLowerCase()!==v:1!==d.nodeType)||!++f||(y&&((d[F]||(d[F]={}))[e]=[W,f]),d!==t)););return f-=i,f===r||f%r===0&&f/r>=0}}},PSEUDO:function(t,n){var i,o=w.pseudos[t]||w.setFilters[t.toLowerCase()]||e.error("unsupported pseudo: "+t);return o[F]?o(n):o.length>1?(i=[t,t,"",n],w.setFilters.hasOwnProperty(t.toLowerCase())?r(function(e,t){for(var r,i=o(e,n),a=i.length;a--;)r=tt.call(e,i[a]),e[r]=!(t[r]=i[a])}):function(e){return o(e,0,i)}):o}},pseudos:{not:r(function(e){var t=[],n=[],i=k(e.replace(st,"$1"));return i[F]?r(function(e,t,n,r){for(var o,a=i(e,null,r,[]),s=e.length;s--;)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,r,o){return t[0]=e,i(t,null,o,n),!n.pop()}}),has:r(function(t){return function(n){return e(t,n).length>0}}),contains:r(function(e){return e=e.replace(Ct,xt),function(t){return(t.textContent||t.innerText||_(t)).indexOf(e)>-1}}),lang:r(function(t){return ft.test(t||"")||e.error("unsupported lang: "+t),t=t.replace(Ct,xt).toLowerCase(),function(e){var n;do if(n=H?e.lang:e.getAttribute("xml:lang")||e.getAttribute("lang"))return n=n.toLowerCase(),n===t||0===n.indexOf(t+"-");while((e=e.parentNode)&&1===e.nodeType);return!1}}),target:function(e){var t=window.location&&window.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===L},focus:function(e){return e===D.activeElement&&(!D.hasFocus||D.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!w.pseudos.empty(e)},header:function(e){return mt.test(e.nodeName)},input:function(e){return ht.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:c(function(){return[0]}),last:c(function(e,t){return[t-1]}),eq:c(function(e,t,n){return[0>n?n+t:n]}),even:c(function(e,t){for(var n=0;t>n;n+=2)e.push(n);return e}),odd:c(function(e,t){for(var n=1;t>n;n+=2)e.push(n);return e}),lt:c(function(e,t,n){for(var r=0>n?n+t:n;--r>=0;)e.push(r);return e}),gt:c(function(e,t,n){for(var r=0>n?n+t:n;++r2&&"ID"===(a=o[0]).type&&x.getById&&9===t.nodeType&&H&&w.relative[o[1].type]){if(t=(w.find.ID(a.matches[0].replace(Ct,xt),t)||[])[0],!t)return n;c&&(t=t.parentNode),e=e.slice(o.shift().value.length)}for(i=pt.needsContext.test(e)?0:o.length;i--&&(a=o[i],!w.relative[s=a.type]);)if((l=w.find[s])&&(r=l(a.matches[0].replace(Ct,xt),yt.test(o[0].type)&&u(t.parentNode)||t))){if(o.splice(i,1),e=r.length&&f(o),!e)return Z.apply(n,r),n;break}}return(c||k(e,d))(r,t,!H,n,yt.test(e)&&u(t.parentNode)||t),n},x.sortStable=F.split("").sort(j).join("")===F,x.detectDuplicates=!!A,B(),x.sortDetached=i(function(e){return 1&e.compareDocumentPosition(D.createElement("div"))}),i(function(e){return e.innerHTML="
        ","#"===e.firstChild.getAttribute("href")})||o("type|href|height|width",function(e,t,n){return n?void 0:e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),x.attributes&&i(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||o("value",function(e,t,n){return n||"input"!==e.nodeName.toLowerCase()?void 0:e.defaultValue}),i(function(e){return null==e.getAttribute("disabled")})||o(nt,function(e,t,n){var r;return n?void 0:e[t]===!0?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),e}),r(u,[],function(){var e=navigator,t=e.userAgent,n,r,i,o,a,s,l;n=window.opera&&window.opera.buildNumber,r=/WebKit/.test(t),i=!r&&!n&&/MSIE/gi.test(t)&&/Explorer/gi.test(e.appName),i=i&&/MSIE (\w+)\./.exec(t)[1],o=-1==t.indexOf("Trident/")||-1==t.indexOf("rv:")&&-1==e.appName.indexOf("Netscape")?!1:11,i=i||o,a=!r&&!o&&/Gecko/.test(t),s=-1!=t.indexOf("Mac"),l=/(iPad|iPhone)/.test(t);var c=!l||t.match(/AppleWebKit\/(\d*)/)[1]>=534;return{opera:n,webkit:r,ie:i,gecko:a,mac:s,iOS:l,contentEditable:c,transparentSrc:"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",caretAfter:8!=i,range:window.getSelection&&"Range"in window,documentMode:i?document.documentMode||7:10}}),r(d,[u],function(e){function n(e){return null===e||e===t?"":(""+e).replace(v,"")}function r(e,n){return n?"array"==n&&y(e)?!0:typeof e==n:e!==t}function i(e){var t=e,n,r;if(!y(e))for(t=[],n=0,r=e.length;r>n;n++)t[n]=e[n];return t}function o(e,t,n){var r;for(e=e||[],t=t||",","string"==typeof e&&(e=e.split(t)),n=n||{},r=e.length;r--;)n[e[r]]={};return n}function a(e,n,r){var i,o;if(!e)return 0;if(r=r||e,e.length!==t){for(i=0,o=e.length;o>i;i++)if(n.call(r,e[i],i,e)===!1)return 0}else for(i in e)if(e.hasOwnProperty(i)&&n.call(r,e[i],i,e)===!1)return 0;return 1}function s(e,t){var n=[];return a(e,function(e){n.push(t(e))}),n}function l(e,t){var n=[];return a(e,function(e){(!t||t(e))&&n.push(e)}),n}function c(e,t,n){var r=this,i,o,a,s,l,c=0;if(e=/^((static) )?([\w.]+)(:([\w.]+))?/.exec(e),a=e[3].match(/(^|\.)(\w+)$/i)[2],o=r.createNS(e[3].replace(/\.\w+$/,""),n),!o[a]){if("static"==e[2])return o[a]=t,void(this.onCreate&&this.onCreate(e[2],e[3],o[a]));t[a]||(t[a]=function(){},c=1),o[a]=t[a],r.extend(o[a].prototype,t),e[5]&&(i=r.resolve(e[5]).prototype,s=e[5].match(/\.(\w+)$/i)[1],l=o[a],o[a]=c?function(){return i[s].apply(this,arguments)}:function(){return this.parent=i[s],l.apply(this,arguments)},o[a].prototype[a]=o[a],r.each(i,function(e,t){o[a].prototype[t]=i[t]}),r.each(t,function(e,t){i[t]?o[a].prototype[t]=function(){return this.parent=i[t],e.apply(this,arguments)}:t!=a&&(o[a].prototype[t]=e)})),r.each(t["static"],function(e,t){o[a][t]=e})}}function u(e,t){var n,r;if(e)for(n=0,r=e.length;r>n;n++)if(e[n]===t)return n;return-1}function d(e,n){var r,i,o,a=arguments,s;for(r=1,i=a.length;i>r;r++){n=a[r];for(o in n)n.hasOwnProperty(o)&&(s=n[o],s!==t&&(e[o]=s))}return e}function f(e,t,n,r){r=r||this,e&&(n&&(e=e[n]),a(e,function(e,i){return t.call(r,e,i,n)===!1?!1:void f(e,t,n,r)}))}function p(e,t){var n,r;for(t=t||window,e=e.split("."),n=0;nn&&(t=t[e[n]],t);n++);return t}function m(e,t){return!e||r(e,"array")?e:s(e.split(t||","),n)}function g(t){var n=e.cacheSuffix;return n&&(t+=(-1===t.indexOf("?")?"?":"&")+n),t}var v=/^\s*|\s*$/g,y=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)};return{trim:n,isArray:y,is:r,toArray:i,makeMap:o,each:a,map:s,grep:l,inArray:u,extend:d,create:c,walk:f,createNS:p,resolve:h,explode:m,_addCacheSuffix:g}}),r(f,[l,c,d,u],function(e,n,r,i){function o(e){return"undefined"!=typeof e}function a(e){return"string"==typeof e}function s(e){return e&&e==e.window}function l(e,t){var n,r,i;for(t=t||w,i=t.createElement("div"),n=t.createDocumentFragment(),i.innerHTML=e;r=i.firstChild;)n.appendChild(r);return n}function c(e,t,n,r){var i;if(a(t))t=l(t,v(e[0]));else if(t.length&&!t.nodeType){if(t=f.makeArray(t),r)for(i=t.length-1;i>=0;i--)c(e,t[i],n,r);else for(i=0;ii&&(a=e[i],t.call(a,i,a)!==!1);i++);return e}function g(e,t){var n=[];return m(e,function(e,r){t(r,e)&&n.push(r)}),n}function v(e){return e?9==e.nodeType?e:e.ownerDocument:w}function y(e,n,r){var i=[],o=e[n];for("string"!=typeof r&&r instanceof f&&(r=r[0]);o&&9!==o.nodeType;){if(r!==t){if(o===r)break;if("string"==typeof r&&f(o).is(r))break}1===o.nodeType&&i.push(o),o=o[n]}return i}function b(e,n,r,i){var o=[];for(i instanceof f&&(i=i[0]);e;e=e[n])if(!r||e.nodeType===r){if(i!==t){if(e===i)break;if("string"==typeof i&&f(e).is(i))break}o.push(e)}return o}function C(e,t,n){for(e=e[t];e;e=e[t])if(e.nodeType==n)return e;return null}function x(e,t,n){m(n,function(n,r){e[n]=e[n]||{},e[n][t]=r})}var w=document,_=Array.prototype.push,E=Array.prototype.slice,N=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,k=e.Event,S,T=r.makeMap("fillOpacity fontWeight lineHeight opacity orphans widows zIndex zoom"," "),R=r.makeMap("checked compact declare defer disabled ismap multiple nohref noshade nowrap readonly selected"," "),A={"for":"htmlFor","class":"className",readonly:"readOnly"},B={"float":"cssFloat"},D={},L={},H=/^\s*|\s*$/g;return f.fn=f.prototype={constructor:f,selector:"",context:null,length:0,init:function(e,t){var n=this,r,i;if(!e)return n;if(e.nodeType)return n.context=n[0]=e,n.length=1,n;if(t&&t.nodeType)n.context=t;else{if(t)return f(e).attr(t);n.context=t=document}if(a(e)){if(n.selector=e,r="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!r)return f(t).find(e);if(r[1])for(i=l(e,v(t)).firstChild;i;)_.call(n,i),i=i.nextSibling;else{if(i=v(t).getElementById(r[2]),!i)return n;if(i.id!==r[2])return n.find(e);n.length=1,n[0]=i}}else this.add(e,!1);return n},toArray:function(){return r.toArray(this)},add:function(e,t){var n=this,r,i;if(a(e))return n.add(f(e));if(t!==!1)for(r=f.unique(n.toArray().concat(f.makeArray(e))),n.length=r.length,i=0;it;t++)f.find(e,this[t],r);return f(r)},filter:function(e){return f("function"==typeof e?g(this.toArray(),function(t,n){return e(n,t)}):f.filter(e,this.toArray()))},closest:function(e){var t=[];return e instanceof f&&(e=e[0]),this.each(function(n,r){for(;r;){if("string"==typeof e&&f(r).is(e)){t.push(r);break}if(r==e){t.push(r);break}r=r.parentNode}}),f(t)},offset:function(e){var t,n,r,i=0,o=0,a;return e?this.css(e):(t=this[0],t&&(n=t.ownerDocument,r=n.documentElement,t.getBoundingClientRect&&(a=t.getBoundingClientRect(),i=a.left+(r.scrollLeft||n.body.scrollLeft)-r.clientLeft,o=a.top+(r.scrollTop||n.body.scrollTop)-r.clientTop)),{left:i,top:o})},push:_,sort:[].sort,splice:[].splice},r.extend(f,{extend:r.extend,makeArray:function(e){return s(e)||e.nodeType?[e]:r.toArray(e)},inArray:p,isArray:r.isArray,each:m,trim:h,grep:g,find:n,expr:n.selectors,unique:n.uniqueSort,text:n.getText,contains:n.contains,filter:function(e,t,n){var r=t.length;for(n&&(e=":not("+e+")");r--;)1!=t[r].nodeType&&t.splice(r,1);return t=1===t.length?f.find.matchesSelector(t[0],e)?[t[0]]:[]:f.find.matches(e,t)}}),m({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return y(e,"parentNode")},next:function(e){return C(e,"nextSibling",1)},prev:function(e){return C(e,"previousSibling",1)},children:function(e){return b(e.firstChild,"nextSibling",1)},contents:function(e){return r.toArray(("iframe"===e.nodeName?e.contentDocument||e.contentWindow.document:e).childNodes)}},function(e,t){f.fn[e]=function(n){var r=this,i=[];return r.each(function(){var e=t.call(i,this,n,i);e&&(f.isArray(e)?i.push.apply(i,e):i.push(e))}),this.length>1&&(i=f.unique(i),0===e.indexOf("parents")&&(i=i.reverse())),i=f(i),n?i.filter(n):i}}),m({parentsUntil:function(e,t){return y(e,"parentNode",t)},nextUntil:function(e,t){return b(e,"nextSibling",1,t).slice(1)},prevUntil:function(e,t){return b(e,"previousSibling",1,t).slice(1)}},function(e,t){f.fn[e]=function(n,r){var i=this,o=[];return i.each(function(){var e=t.call(o,this,n,o);e&&(f.isArray(e)?o.push.apply(o,e):o.push(e))}),this.length>1&&(o=f.unique(o),(0===e.indexOf("parents")||"prevUntil"===e)&&(o=o.reverse())),o=f(o),r?o.filter(r):o}}),f.fn.is=function(e){return!!e&&this.filter(e).length>0},f.fn.init.prototype=f.fn,f.overrideDefaults=function(e){function t(r,i){return n=n||e(),0===arguments.length&&(r=n.element),i||(i=n.context),new t.fn.init(r,i)}var n;return f.extend(t,this),t},i.ie&&i.ie<8&&(x(D,"get",{maxlength:function(e){var t=e.maxLength;return 2147483647===t?S:t},size:function(e){var t=e.size;return 20===t?S:t},"class":function(e){return e.className},style:function(e){var t=e.style.cssText;return 0===t.length?S:t}}),x(D,"set",{"class":function(e,t){e.className=t},style:function(e,t){e.style.cssText=t}})),i.ie&&i.ie<9&&(B["float"]="styleFloat",x(L,"set",{opacity:function(e,t){var n=e.style;null===t||""===t?n.removeAttribute("filter"):(n.zoom=1,n.filter="alpha(opacity="+100*t+")")}})),f.attrHooks=D,f.cssHooks=L,f}),r(p,[],function(){return function(e,t){function n(e,t,n,r){function i(e){return e=parseInt(e,10).toString(16),e.length>1?e:"0"+e}return"#"+i(t)+i(n)+i(r)}var r=/rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi,i=/(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,o=/\s*([^:]+):\s*([^;]+);?/g,a=/\s+$/,s,l,c={},u,d,f,p="\ufeff";for(e=e||{},t&&(d=t.getValidStyles(),f=t.getInvalidStyles()),u=("\\\" \\' \\; \\: ; : "+p).split(" "),l=0;l-1&&n||(m[e+t]=-1==l?s[0]:s.join(" "),delete m[e+"-top"+t],delete m[e+"-right"+t],delete m[e+"-bottom"+t],delete m[e+"-left"+t])}}function u(e){var t=m[e],n;if(t){for(t=t.split(" "),n=t.length;n--;)if(t[n]!==t[0])return!1;return m[e]=t[0],!0}}function d(e,t,n,r){u(t)&&u(n)&&u(r)&&(m[e]=m[t]+" "+m[n]+" "+m[r],delete m[t],delete m[n],delete m[r])}function f(e){return b=!0,c[e]}function p(e,t){return b&&(e=e.replace(/\uFEFF[0-9]/g,function(e){return c[e]})),t||(e=e.replace(/\\([\'\";:])/g,"$1")),e}function h(t,n,r,i,o,a){if(o=o||a)return o=p(o),"'"+o.replace(/\'/g,"\\'")+"'";if(n=p(n||r||i),!e.allow_script_urls){var s=n.replace(/[\s\r\n]+/,"");if(/(java|vb)script:/i.test(s))return"";if(!e.allow_svg_data_urls&&/^data:image\/svg/i.test(s))return""}return C&&(n=C.call(x,n,"style")),"url('"+n.replace(/\'/g,"\\'")+"')"}var m={},g,v,y,b,C=e.url_converter,x=e.url_converter_scope||this;if(t){for(t=t.replace(/[\u0000-\u001F]/g,""),t=t.replace(/\\[\"\';:\uFEFF]/g,f).replace(/\"[^\"]+\"|\'[^\']+\'/g,function(e){return e.replace(/[;:]/g,f)});g=o.exec(t);){if(v=g[1].replace(a,"").toLowerCase(),y=g[2].replace(a,""),y=y.replace(/\\[0-9a-f]+/g,function(e){return String.fromCharCode(parseInt(e.substr(1),16))}),v&&y.length>0){if(!e.allow_script_urls&&("behavior"==v||/expression\s*\(|\/\*|\*\//.test(y)))continue;"font-weight"===v&&"700"===y?y="bold":("color"===v||"background-color"===v)&&(y=y.toLowerCase()),y=y.replace(r,n),y=y.replace(i,h),m[v]=b?p(y,!0):y}o.lastIndex=g.index+g[0].length}s("border","",!0),s("border","-width"),s("border","-color"),s("border","-style"),s("padding",""),s("margin",""),d("border","border-width","border-style","border-color"),"medium none"===m.border&&delete m.border,"none"===m["border-image"]&&delete m["border-image"]}return m},serialize:function(e,t){function n(t){var n,r,o,a;if(n=d[t])for(r=0,o=n.length;o>r;r++)t=n[r],a=e[t],a!==s&&a.length>0&&(i+=(i.length>0?" ":"")+t+": "+a+";")}function r(e,t){var n;return n=f["*"],n&&n[e]?!1:(n=f[t],n&&n[e]?!1:!0)}var i="",o,a;if(t&&d)n("*"),n(t);else for(o in e)a=e[o],a!==s&&a.length>0&&(!f||r(o,t))&&(i+=(i.length>0?" ":"")+o+": "+a+";");return i}}}}),r(h,[],function(){return function(e,t){function n(e,n,r,i){var o,a;if(e){if(!i&&e[n])return e[n];if(e!=t){if(o=e[r])return o;for(a=e.parentNode;a&&a!=t;a=a.parentNode)if(o=a[r])return o}}}var r=e;this.current=function(){return r},this.next=function(e){return r=n(r,"firstChild","nextSibling",e)},this.prev=function(e){return r=n(r,"lastChild","previousSibling",e)}}}),r(m,[d],function(e){function t(n){function r(){return M.createDocumentFragment()}function i(e,t){_(F,e,t)}function o(e,t){_(z,e,t)}function a(e){i(e.parentNode,j(e))}function s(e){i(e.parentNode,j(e)+1)}function l(e){o(e.parentNode,j(e))}function c(e){o(e.parentNode,j(e)+1)}function u(e){e?(H[U]=H[V],H[$]=H[W]):(H[V]=H[U],H[W]=H[$]),H.collapsed=F}function d(e){a(e),c(e)}function f(e){i(e,0),o(e,1===e.nodeType?e.childNodes.length:e.nodeValue.length)}function p(e,t){var n=H[V],r=H[W],i=H[U],o=H[$],a=t.startContainer,s=t.startOffset,l=t.endContainer,c=t.endOffset;return 0===e?w(n,r,a,s):1===e?w(i,o,a,s):2===e?w(i,o,l,c):3===e?w(n,r,l,c):void 0}function h(){E(I)}function m(){return E(P)}function g(){return E(O)}function v(e){var t=this[V],r=this[W],i,o;3!==t.nodeType&&4!==t.nodeType||!t.nodeValue?(t.childNodes.length>0&&(o=t.childNodes[r]),o?t.insertBefore(e,o):3==t.nodeType?n.insertAfter(e,t):t.appendChild(e)):r?r>=t.nodeValue.length?n.insertAfter(e,t):(i=t.splitText(r),t.parentNode.insertBefore(e,i)):t.parentNode.insertBefore(e,t)}function y(e){var t=H.extractContents();H.insertNode(e),e.appendChild(t),H.selectNode(e)}function b(){return q(new t(n),{startContainer:H[V],startOffset:H[W],endContainer:H[U],endOffset:H[$],collapsed:H.collapsed,commonAncestorContainer:H.commonAncestorContainer})}function C(e,t){var n;if(3==e.nodeType)return e;if(0>t)return e;for(n=e.firstChild;n&&t>0;)--t,n=n.nextSibling;return n?n:e}function x(){return H[V]==H[U]&&H[W]==H[$]}function w(e,t,r,i){var o,a,s,l,c,u;if(e==r)return t==i?0:i>t?-1:1;for(o=r;o&&o.parentNode!=e;)o=o.parentNode;if(o){for(a=0,s=e.firstChild;s!=o&&t>a;)a++,s=s.nextSibling;return a>=t?-1:1}for(o=e;o&&o.parentNode!=r;)o=o.parentNode;if(o){for(a=0,s=r.firstChild;s!=o&&i>a;)a++,s=s.nextSibling;return i>a?-1:1}for(l=n.findCommonAncestor(e,r),c=e;c&&c.parentNode!=l;)c=c.parentNode;for(c||(c=l),u=r;u&&u.parentNode!=l;)u=u.parentNode;if(u||(u=l),c==u)return 0;for(s=l.firstChild;s;){if(s==c)return-1;if(s==u)return 1;s=s.nextSibling}}function _(e,t,r){var i,o;for(e?(H[V]=t,H[W]=r):(H[U]=t,H[$]=r),i=H[U];i.parentNode;)i=i.parentNode;for(o=H[V];o.parentNode;)o=o.parentNode;o==i?w(H[V],H[W],H[U],H[$])>0&&H.collapse(e):H.collapse(e),H.collapsed=x(),H.commonAncestorContainer=n.findCommonAncestor(H[V],H[U])}function E(e){var t,n=0,r=0,i,o,a,s,l,c;if(H[V]==H[U])return N(e);for(t=H[U],i=t.parentNode;i;t=i,i=i.parentNode){if(i==H[V])return k(t,e);++n}for(t=H[V],i=t.parentNode;i;t=i,i=i.parentNode){if(i==H[U])return S(t,e);++r}for(o=r-n,a=H[V];o>0;)a=a.parentNode,o--;for(s=H[U];0>o;)s=s.parentNode,o++;for(l=a.parentNode,c=s.parentNode;l!=c;l=l.parentNode,c=c.parentNode)a=l,s=c;return T(a,s,e)}function N(e){var t,n,i,o,a,s,l,c,u;if(e!=I&&(t=r()),H[W]==H[$])return t;if(3==H[V].nodeType){if(n=H[V].nodeValue,i=n.substring(H[W],H[$]),e!=O&&(o=H[V],c=H[W],u=H[$]-H[W],0===c&&u>=o.nodeValue.length-1?o.parentNode.removeChild(o):o.deleteData(c,u),H.collapse(F)),e==I)return;return i.length>0&&t.appendChild(M.createTextNode(i)),t}for(o=C(H[V],H[W]),a=H[$]-H[W];o&&a>0;)s=o.nextSibling,l=D(o,e),t&&t.appendChild(l),--a,o=s;return e!=O&&H.collapse(F),t}function k(e,t){var n,i,o,a,s,l;if(t!=I&&(n=r()),i=R(e,t),n&&n.appendChild(i),o=j(e),a=o-H[W],0>=a)return t!=O&&(H.setEndBefore(e),H.collapse(z)),n;for(i=e.previousSibling;a>0;)s=i.previousSibling,l=D(i,t),n&&n.insertBefore(l,n.firstChild),--a,i=s;return t!=O&&(H.setEndBefore(e),H.collapse(z)),n}function S(e,t){var n,i,o,a,s,l;for(t!=I&&(n=r()),o=A(e,t),n&&n.appendChild(o),i=j(e),++i,a=H[$]-i,o=e.nextSibling;o&&a>0;)s=o.nextSibling,l=D(o,t),n&&n.appendChild(l),--a,o=s;return t!=O&&(H.setStartAfter(e),H.collapse(F)),n}function T(e,t,n){var i,o,a,s,l,c,u;for(n!=I&&(o=r()),i=A(e,n),o&&o.appendChild(i),a=j(e),s=j(t),++a,l=s-a,c=e.nextSibling;l>0;)u=c.nextSibling,i=D(c,n),o&&o.appendChild(i),c=u,--l;return i=R(t,n),o&&o.appendChild(i),n!=O&&(H.setStartAfter(e),H.collapse(F)),o}function R(e,t){var n=C(H[U],H[$]-1),r,i,o,a,s,l=n!=H[U];if(n==e)return B(n,l,z,t);for(r=n.parentNode,i=B(r,z,z,t);r;){for(;n;)o=n.previousSibling,a=B(n,l,z,t),t!=I&&i.insertBefore(a,i.firstChild),l=F,n=o;if(r==e)return i;n=r.previousSibling,r=r.parentNode,s=B(r,z,z,t),t!=I&&s.appendChild(i),i=s}}function A(e,t){var n=C(H[V],H[W]),r=n!=H[V],i,o,a,s,l;if(n==e)return B(n,r,F,t);for(i=n.parentNode,o=B(i,z,F,t);i;){for(;n;)a=n.nextSibling,s=B(n,r,F,t),t!=I&&o.appendChild(s),r=F,n=a;if(i==e)return o;n=i.nextSibling,i=i.parentNode,l=B(i,z,F,t),t!=I&&l.appendChild(o),o=l}}function B(e,t,r,i){var o,a,s,l,c;if(t)return D(e,i);if(3==e.nodeType){if(o=e.nodeValue,r?(l=H[W],a=o.substring(l),s=o.substring(0,l)):(l=H[$],a=o.substring(0,l),s=o.substring(l)),i!=O&&(e.nodeValue=s),i==I)return;return c=n.clone(e,z),c.nodeValue=a,c}if(i!=I)return n.clone(e,z)}function D(e,t){return t!=I?t==O?n.clone(e,F):e:void e.parentNode.removeChild(e)}function L(){return n.create("body",null,g()).outerText}var H=this,M=n.doc,P=0,O=1,I=2,F=!0,z=!1,W="startOffset",V="startContainer",U="endContainer",$="endOffset",q=e.extend,j=n.nodeIndex;return q(H,{startContainer:M,startOffset:0,endContainer:M,endOffset:0,collapsed:F,commonAncestorContainer:M,START_TO_START:0,START_TO_END:1,END_TO_END:2,END_TO_START:3,setStart:i,setEnd:o,setStartBefore:a,setStartAfter:s,setEndBefore:l,setEndAfter:c,collapse:u,selectNode:d,selectNodeContents:f,compareBoundaryPoints:p,deleteContents:h,extractContents:m,cloneContents:g,insertNode:v,surroundContents:y,cloneRange:b,toStringIE:L}),H}return t.prototype.toString=function(){return this.toStringIE()},t}),r(g,[d],function(e){function t(e){var t;return t=document.createElement("div"),t.innerHTML=e,t.textContent||t.innerText||e}function n(e,t){var n,r,i,a={};if(e){for(e=e.split(","),t=t||10,n=0;n\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,l=/[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,c=/[<>&\"\']/g,u=/&(#x|#)?([\w]+);/g,d={128:"\u20ac",130:"\u201a",131:"\u0192",132:"\u201e",133:"\u2026",134:"\u2020",135:"\u2021",136:"\u02c6",137:"\u2030",138:"\u0160",139:"\u2039",140:"\u0152",142:"\u017d",145:"\u2018",146:"\u2019",147:"\u201c",148:"\u201d",149:"\u2022",150:"\u2013",151:"\u2014",152:"\u02dc",153:"\u2122",154:"\u0161",155:"\u203a",156:"\u0153",158:"\u017e",159:"\u0178"};o={'"':""","'":"'","<":"<",">":">","&":"&","`":"`"},a={"<":"<",">":">","&":"&",""":'"',"'":"'"},i=n("50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,t9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4,nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro",32);var f={encodeRaw:function(e,t){return e.replace(t?s:l,function(e){return o[e]||e})},encodeAllRaw:function(e){return(""+e).replace(c,function(e){return o[e]||e})},encodeNumeric:function(e,t){return e.replace(t?s:l,function(e){return e.length>1?"&#"+(1024*(e.charCodeAt(0)-55296)+(e.charCodeAt(1)-56320)+65536)+";":o[e]||"&#"+e.charCodeAt(0)+";"})},encodeNamed:function(e,t,n){return n=n||i,e.replace(t?s:l,function(e){return o[e]||n[e]||e})},getEncodeFunc:function(e,t){function a(e,n){return e.replace(n?s:l,function(e){return o[e]||t[e]||"&#"+e.charCodeAt(0)+";"||e})}function c(e,n){return f.encodeNamed(e,n,t)}return t=n(t)||i,e=r(e.replace(/\+/g,",")),e.named&&e.numeric?a:e.named?t?c:f.encodeNamed:e.numeric?f.encodeNumeric:f.encodeRaw},decode:function(e){return e.replace(u,function(e,n,r){return n?(r=parseInt(r,2===n.length?16:10),r>65535?(r-=65536,String.fromCharCode(55296+(r>>10),56320+(1023&r))):d[r]||String.fromCharCode(r)):a[e]||i[e]||t(e)})}};return f}),r(v,[d],function(e){return function(t,n){function r(e){t.getElementsByTagName("head")[0].appendChild(e)}function i(n,i,l){function c(){for(var e=y.passed,t=e.length;t--;)e[t]();y.status=2,y.passed=[],y.failed=[]}function u(){for(var e=y.failed,t=e.length;t--;)e[t]();y.status=3,y.passed=[],y.failed=[]}function d(){var e=navigator.userAgent.match(/WebKit\/(\d*)/);return!!(e&&e[1]<536)}function f(e,t){e()||((new Date).getTime()-v0)return g=t.createElement("style"),g.textContent='@import "'+n+'"',h(),void r(g);p()}r(m),m.href=n}}var o=0,a={},s;n=n||{},s=n.maxLoadTime||5e3,this.load=i}}),r(y,[c,f,p,l,h,m,g,u,d,v],function(e,n,r,i,o,a,s,l,c,u){function d(e,t){var n={},r=t.keep_values,i;return i={set:function(n,r,i){t.url_converter&&(r=t.url_converter.call(t.url_converter_scope||e,r,i,n[0])),n.attr("data-mce-"+i,r).attr(i,r)},get:function(e,t){return e.attr("data-mce-"+t)||e.attr(t)}},n={style:{set:function(e,t){return null!==t&&"object"==typeof t?void e.css(t):(r&&e.attr("data-mce-style",t),void e.attr("style",t))},get:function(t){var n=t.attr("data-mce-style")||t.attr("style");return n=e.serializeStyle(e.parseStyle(n),t[0].nodeName)}}},r&&(n.href=n.src=i),n}function f(e,t){var o=this,a;o.doc=e,o.win=window,o.files={},o.counter=0,o.stdMode=!v||e.documentMode>=8,o.boxModel=!v||"CSS1Compat"==e.compatMode||o.stdMode,o.styleSheetLoader=new u(e),o.boundEvents=[],o.settings=t=t||{},o.schema=t.schema,o.styles=new r({url_converter:t.url_converter,url_converter_scope:t.url_converter_scope},t.schema),o.fixDoc(e),o.events=t.ownEvents?new i(t.proxy):i.Event,o.attrHooks=d(o,t),a=t.schema?t.schema.getBlockElements():{},o.$=n.overrideDefaults(function(){return{context:e,element:o.getRoot()}}),o.isBlock=function(e){if(!e)return!1;var t=e.nodeType;return t?!(1!==t||!a[e.nodeName]):!!a[e]}}var p=c.each,h=c.is,m=c.grep,g=c.trim,v=l.ie,y=/^([a-z0-9],?)+$/i,b=/^[ \t\r\n]*$/;return f.prototype={$$:function(e){return"string"==typeof e&&(e=this.get(e)),this.$(e)},root:null,fixDoc:function(e){var t=this.settings,n;if(v&&t.schema){"abbr article aside audio canvas details figcaption figure footer header hgroup mark menu meter nav output progress section summary time video".replace(/\w+/g,function(t){e.createElement(t)});for(n in t.schema.getCustomElements())e.createElement(n)}},clone:function(e,t){var n=this,r,i;return!v||1!==e.nodeType||t?e.cloneNode(t):(i=n.doc,t?r.firstChild:(r=i.createElement(e.nodeName),p(n.getAttribs(e),function(t){n.setAttrib(r,t.nodeName,n.getAttrib(e,t.nodeName))}),r))},getRoot:function(){var e=this;return e.settings.root_element||e.doc.body},getViewPort:function(e){var t,n;return e=e?e:this.win,t=e.document,n=this.boxModel?t.documentElement:t.body,{x:e.pageXOffset||n.scrollLeft,y:e.pageYOffset||n.scrollTop,w:e.innerWidth||n.clientWidth,h:e.innerHeight||n.clientHeight}},getRect:function(e){var t=this,n,r;return e=t.get(e),n=t.getPos(e),r=t.getSize(e),{x:n.x,y:n.y,w:r.w,h:r.h}},getSize:function(e){var t=this,n,r;return e=t.get(e),n=t.getStyle(e,"width"),r=t.getStyle(e,"height"),-1===n.indexOf("px")&&(n=0),-1===r.indexOf("px")&&(r=0),{w:parseInt(n,10)||e.offsetWidth||e.clientWidth,h:parseInt(r,10)||e.offsetHeight||e.clientHeight}},getParent:function(e,t,n){return this.getParents(e,t,n,!1)},getParents:function(e,n,r,i){var o=this,a,s=[];for(e=o.get(e),i=i===t,r=r||("BODY"!=o.getRoot().nodeName?o.getRoot().parentNode:null),h(n,"string")&&(a=n,n="*"===n?function(e){return 1==e.nodeType}:function(e){return o.is(e,a)});e&&e!=r&&e.nodeType&&9!==e.nodeType;){if(!n||n(e)){if(!i)return e;s.push(e)}e=e.parentNode}return i?s:null},get:function(e){var t;return e&&this.doc&&"string"==typeof e&&(t=e,e=this.doc.getElementById(e),e&&e.id!==t)?this.doc.getElementsByName(t)[1]:e},getNext:function(e,t){return this._findSib(e,t,"nextSibling")},getPrev:function(e,t){return this._findSib(e,t,"previousSibling")},select:function(t,n){var r=this;return e(t,r.get(n)||r.settings.root_element||r.doc,[])},is:function(n,r){var i;if(n.length===t){if("*"===r)return 1==n.nodeType;if(y.test(r)){for(r=r.toLowerCase().split(/,/),n=n.nodeName.toLowerCase(),i=r.length-1;i>=0;i--)if(r[i]==n)return!0;return!1}}if(n.nodeType&&1!=n.nodeType)return!1;var o=n.nodeType?[n]:n;return e(r,o[0].ownerDocument||o[0],null,o).length>0},add:function(e,t,n,r,i){var o=this;return this.run(e,function(e){var a;return a=h(t,"string")?o.doc.createElement(t):t,o.setAttribs(a,n),r&&(r.nodeType?a.appendChild(r):o.setHTML(a,r)),i?a:e.appendChild(a)})},create:function(e,t,n){return this.add(this.doc.createElement(e),e,t,n,1)},createHTML:function(e,t,n){var r="",i;r+="<"+e;for(i in t)t.hasOwnProperty(i)&&null!==t[i]&&"undefined"!=typeof t[i]&&(r+=" "+i+'="'+this.encode(t[i])+'"');return"undefined"!=typeof n?r+">"+n+"":r+" />"},createFragment:function(e){var t,n,r=this.doc,i;for(i=r.createElement("div"),t=r.createDocumentFragment(),e&&(i.innerHTML=e);n=i.firstChild;)t.appendChild(n);return t},remove:function(e,t){return e=this.$$(e),t?e.each(function(){for(var e;e=this.firstChild;)3==e.nodeType&&0===e.data.length?this.removeChild(e):this.parentNode.insertBefore(e,this)}).remove():e.remove(),e.length>1?e.toArray():e[0]},setStyle:function(e,t,n){e=this.$$(e).css(t,n),this.settings.update_styles&&e.attr("data-mce-style",null)},getStyle:function(e,n,r){return e=this.$$(e),r?e.css(n):(n=n.replace(/-(\D)/g,function(e,t){return t.toUpperCase()}),"float"==n&&(n=v?"styleFloat":"cssFloat"),e[0]&&e[0].style?e[0].style[n]:t)},setStyles:function(e,t){e=this.$$(e).css(t),this.settings.update_styles&&e.attr("data-mce-style",null)},removeAllAttribs:function(e){return this.run(e,function(e){var t,n=e.attributes;for(t=n.length-1;t>=0;t--)e.removeAttributeNode(n.item(t))})},setAttrib:function(e,t,n){var r=this,i,o,a=r.settings;""===n&&(n=null),e=r.$$(e),i=e.attr(t),e.length&&(o=r.attrHooks[t],o&&o.set?o.set(e,n,t):e.attr(t,n),i!=n&&a.onSetAttrib&&a.onSetAttrib({attrElm:e,attrName:t,attrValue:n}))},setAttribs:function(e,t){var n=this;n.$$(e).each(function(e,r){p(t,function(e,t){n.setAttrib(r,t,e)})})},getAttrib:function(e,t,n){var r=this,i,o;return e=r.$$(e),e.length&&(i=r.attrHooks[t],o=i&&i.get?i.get(e,t):e.attr(t)),"undefined"==typeof o&&(o=n||""),o},getPos:function(e,t){var r=this,i=0,o=0,a,s=r.doc,l=s.body,c;if(e=r.get(e),t=t||l,e){if(t===l&&e.getBoundingClientRect&&"static"===n(l).css("position"))return c=e.getBoundingClientRect(),t=r.boxModel?s.documentElement:l,i=c.left+(s.documentElement.scrollLeft||l.scrollLeft)-t.clientLeft,o=c.top+(s.documentElement.scrollTop||l.scrollTop)-t.clientTop,{x:i,y:o};for(a=e;a&&a!=t&&a.nodeType;)i+=a.offsetLeft||0,o+=a.offsetTop||0,a=a.offsetParent;for(a=e.parentNode;a&&a!=t&&a.nodeType;)i-=a.scrollLeft||0,o-=a.scrollTop||0,a=a.parentNode}return{x:i,y:o}},parseStyle:function(e){return this.styles.parse(e)},serializeStyle:function(e,t){return this.styles.serialize(e,t)},addStyle:function(e){var t=this,n=t.doc,r,i;if(t!==f.DOM&&n===document){var o=f.DOM.addedStyles;if(o=o||[],o[e])return;o[e]=!0,f.DOM.addedStyles=o}i=n.getElementById("mceDefaultStyles"),i||(i=n.createElement("style"),i.id="mceDefaultStyles",i.type="text/css",r=n.getElementsByTagName("head")[0],r.firstChild?r.insertBefore(i,r.firstChild):r.appendChild(i)),i.styleSheet?i.styleSheet.cssText+=e:i.appendChild(n.createTextNode(e))},loadCSS:function(e){var t=this,n=t.doc,r;return t!==f.DOM&&n===document?void f.DOM.loadCSS(e):(e||(e=""),r=n.getElementsByTagName("head")[0],void p(e.split(","),function(e){var i;e=c._addCacheSuffix(e),t.files[e]||(t.files[e]=!0,i=t.create("link",{rel:"stylesheet",href:e}),v&&n.documentMode&&n.recalc&&(i.onload=function(){n.recalc&&n.recalc(),i.onload=null}),r.appendChild(i))}))},addClass:function(e,t){this.$$(e).addClass(t)},removeClass:function(e,t){this.toggleClass(e,t,!1)},hasClass:function(e,t){return this.$$(e).hasClass(t)},toggleClass:function(e,t,r){this.$$(e).toggleClass(t,r).each(function(){""===this.className&&n(this).attr("class",null)})},show:function(e){this.$$(e).show()},hide:function(e){this.$$(e).hide()},isHidden:function(e){return"none"==this.$$(e).css("display")},uniqueId:function(e){return(e?e:"mce_")+this.counter++},setHTML:function(e,t){e=this.$$(e),v?e.each(function(e,r){if(r.canHaveHTML!==!1){for(;r.firstChild;)r.removeChild(r.firstChild);try{r.innerHTML="
        "+t,r.removeChild(r.firstChild)}catch(i){n("
        ").html("
        "+t).contents().slice(1).appendTo(r)}return t}}):e.html(t)},getOuterHTML:function(e){return e=this.get(e),1==e.nodeType?e.outerHTML:n("
        ").append(n(e).clone()).html()},setOuterHTML:function(e,t){var r=this;r.$$(e).each(function(){try{this.outerHTML=t}catch(e){r.remove(n(this).html(t),!0)}})},decode:s.decode,encode:s.encodeAllRaw,insertAfter:function(e,t){return t=this.get(t),this.run(e,function(e){var n,r;return n=t.parentNode,r=t.nextSibling,r?n.insertBefore(e,r):n.appendChild(e),e})},replace:function(e,t,n){var r=this;return r.run(t,function(t){return h(t,"array")&&(e=e.cloneNode(!0)),n&&p(m(t.childNodes),function(t){e.appendChild(t)}),t.parentNode.replaceChild(e,t)})},rename:function(e,t){var n=this,r;return e.nodeName!=t.toUpperCase()&&(r=n.create(t),p(n.getAttribs(e),function(t){n.setAttrib(r,t.nodeName,n.getAttrib(e,t.nodeName))}),n.replace(r,e,1)),r||e},findCommonAncestor:function(e,t){for(var n=e,r;n;){for(r=t;r&&n!=r;)r=r.parentNode;if(n==r)break;n=n.parentNode}return!n&&e.ownerDocument?e.ownerDocument.documentElement:n},toHex:function(e){return this.styles.toHex(c.trim(e))},run:function(e,t,n){var r=this,i;return"string"==typeof e&&(e=r.get(e)),e?(n=n||this,e.nodeType||!e.length&&0!==e.length?t.call(n,e):(i=[],p(e,function(e,o){e&&("string"==typeof e&&(e=r.get(e)),i.push(t.call(n,e,o)))}),i)):!1},getAttribs:function(e){var t;if(e=this.get(e),!e)return[];if(v){if(t=[],"OBJECT"==e.nodeName)return e.attributes;"OPTION"===e.nodeName&&this.getAttrib(e,"selected")&&t.push({specified:1,nodeName:"selected"});var n=/<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi;return e.cloneNode(!1).outerHTML.replace(n,"").replace(/[\w:\-]+/gi,function(e){t.push({specified:1,nodeName:e})}),t}return e.attributes},isEmpty:function(e,t){var n=this,r,i,a,s,l,c=0;if(e=e.firstChild){s=new o(e,e.parentNode),t=t||(n.schema?n.schema.getNonEmptyElements():null);do{if(a=e.nodeType,1===a){if(e.getAttribute("data-mce-bogus"))continue;if(l=e.nodeName.toLowerCase(),t&&t[l]){if("br"===l){c++;continue}return!1}for(i=n.getAttribs(e),r=i.length;r--;)if(l=i[r].nodeName,"name"===l||"data-mce-bookmark"===l)return!1}if(8==a)return!1;if(3===a&&!b.test(e.nodeValue))return!1}while(e=s.next())}return 1>=c},createRng:function(){var e=this.doc;return e.createRange?e.createRange():new a(this)},nodeIndex:function(e,t){var n=0,r,i;if(e)for(r=e.nodeType,e=e.previousSibling;e;e=e.previousSibling)i=e.nodeType,(!t||3!=i||i!=r&&e.nodeValue.length)&&(n++,r=i);return n},split:function(e,t,n){function r(e){function t(e){var t=e.previousSibling&&"SPAN"==e.previousSibling.nodeName,n=e.nextSibling&&"SPAN"==e.nextSibling.nodeName;return t&&n}var n,o=e.childNodes,a=e.nodeType;if(1!=a||"bookmark"!=e.getAttribute("data-mce-type")){for(n=o.length-1;n>=0;n--)r(o[n]);if(9!=a){if(3==a&&e.nodeValue.length>0){var s=g(e.nodeValue).length;if(!i.isBlock(e.parentNode)||s>0||0===s&&t(e))return +}else if(1==a&&(o=e.childNodes,1==o.length&&o[0]&&1==o[0].nodeType&&"bookmark"==o[0].getAttribute("data-mce-type")&&e.parentNode.insertBefore(o[0],e),o.length||/^(br|hr|input|img)$/i.test(e.nodeName)))return;i.remove(e)}return e}}var i=this,o=i.createRng(),a,s,l;return e&&t?(o.setStart(e.parentNode,i.nodeIndex(e)),o.setEnd(t.parentNode,i.nodeIndex(t)),a=o.extractContents(),o=i.createRng(),o.setStart(t.parentNode,i.nodeIndex(t)+1),o.setEnd(e.parentNode,i.nodeIndex(e)+1),s=o.extractContents(),l=e.parentNode,l.insertBefore(r(a),e),n?l.replaceChild(n,t):l.insertBefore(t,e),l.insertBefore(r(s),e),i.remove(e),n||t):void 0},bind:function(e,t,n,r){var i=this;if(c.isArray(e)){for(var o=e.length;o--;)e[o]=i.bind(e[o],t,n,r);return e}return!i.settings.collect||e!==i.doc&&e!==i.win||i.boundEvents.push([e,t,n,r]),i.events.bind(e,t,n,r||i)},unbind:function(e,t,n){var r=this,i;if(c.isArray(e)){for(i=e.length;i--;)e[i]=r.unbind(e[i],t,n);return e}if(r.boundEvents&&(e===r.doc||e===r.win))for(i=r.boundEvents.length;i--;){var o=r.boundEvents[i];e!=o[0]||t&&t!=o[1]||n&&n!=o[2]||this.events.unbind(o[0],o[1],o[2])}return this.events.unbind(e,t,n)},fire:function(e,t,n){return this.events.fire(e,t,n)},getContentEditable:function(e){var t;return e&&1==e.nodeType?(t=e.getAttribute("data-mce-contenteditable"),t&&"inherit"!==t?t:"inherit"!==e.contentEditable?e.contentEditable:null):null},getContentEditableParent:function(e){for(var t=this.getRoot(),n=null;e&&e!==t&&(n=this.getContentEditable(e),null===n);e=e.parentNode);return n},destroy:function(){var t=this;if(t.boundEvents){for(var n=t.boundEvents.length;n--;){var r=t.boundEvents[n];this.events.unbind(r[0],r[1],r[2])}t.boundEvents=null}e.setDocument&&e.setDocument(),t.win=t.doc=t.root=t.events=t.frag=null},isChildOf:function(e,t){for(;e;){if(t===e)return!0;e=e.parentNode}return!1},dumpRng:function(e){return"startContainer: "+e.startContainer.nodeName+", startOffset: "+e.startOffset+", endContainer: "+e.endContainer.nodeName+", endOffset: "+e.endOffset},_findSib:function(e,t,n){var r=this,i=t;if(e)for("string"==typeof i&&(i=function(e){return r.is(e,t)}),e=e[n];e;e=e[n])if(i(e))return e;return null}},f.DOM=new f(document),f}),r(b,[y,d],function(e,t){function n(){function e(e,n){function i(){a.remove(l),s&&(s.onreadystatechange=s.onload=s=null),n()}function o(){"undefined"!=typeof console&&console.log&&console.log("Failed to load: "+e)}var a=r,s,l;l=a.uniqueId(),s=document.createElement("script"),s.id=l,s.type="text/javascript",s.src=t._addCacheSuffix(e),"onreadystatechange"in s?s.onreadystatechange=function(){/loaded|complete/.test(s.readyState)&&i()}:s.onload=i,s.onerror=o,(document.getElementsByTagName("head")[0]||document.body).appendChild(s)}var n=0,a=1,s=2,l={},c=[],u={},d=[],f=0,p;this.isDone=function(e){return l[e]==s},this.markDone=function(e){l[e]=s},this.add=this.load=function(e,t,r){var i=l[e];i==p&&(c.push(e),l[e]=n),t&&(u[e]||(u[e]=[]),u[e].push({func:t,scope:r||this}))},this.loadQueue=function(e,t){this.loadScripts(c,e,t)},this.loadScripts=function(t,n,r){function c(e){i(u[e],function(e){e.func.call(e.scope)}),u[e]=p}var h;d.push({func:n,scope:r||this}),(h=function(){var n=o(t);t.length=0,i(n,function(t){return l[t]==s?void c(t):void(l[t]!=a&&(l[t]=a,f++,e(t,function(){l[t]=s,f--,c(t),h()})))}),f||(i(d,function(e){e.func.call(e.scope)}),d.length=0)})()}}var r=e.DOM,i=t.each,o=t.grep;return n.ScriptLoader=new n,n}),r(C,[b,d],function(e,n){function r(){var e=this;e.items=[],e.urls={},e.lookup={}}var i=n.each;return r.prototype={get:function(e){return this.lookup[e]?this.lookup[e].instance:t},dependencies:function(e){var t;return this.lookup[e]&&(t=this.lookup[e].dependencies),t||[]},requireLangPack:function(t,n){var i=r.language;if(i&&r.languageLoad!==!1){if(n)if(n=","+n+",",-1!=n.indexOf(","+i.substr(0,2)+","))i=i.substr(0,2);else if(-1==n.indexOf(","+i+","))return;e.ScriptLoader.add(this.urls[t]+"/langs/"+i+".js")}},add:function(e,t,n){return this.items.push(t),this.lookup[e]={instance:t,dependencies:n},t},createUrl:function(e,t){return"object"==typeof t?t:{prefix:e.prefix,resource:t,suffix:e.suffix}},addComponents:function(t,n){var r=this.urls[t];i(n,function(t){e.ScriptLoader.add(r+"/"+t)})},load:function(n,o,a,s){function l(){var r=c.dependencies(n);i(r,function(e){var n=c.createUrl(o,e);c.load(n.resource,n,t,t)}),a&&a.call(s?s:e)}var c=this,u=o;c.urls[n]||("object"==typeof o&&(u=o.prefix+o.resource+o.suffix),0!==u.indexOf("/")&&-1==u.indexOf("://")&&(u=r.baseURL+"/"+u),c.urls[n]=u.substring(0,u.lastIndexOf("/")),c.lookup[n]?l():e.ScriptLoader.add(u,l,s))}},r.PluginManager=new r,r.ThemeManager=new r,r}),r(x,[d,h],function(e,t){function n(e,t){var n=e.childNodes;return t--,t>n.length-1?t=n.length-1:0>t&&(t=0),n[t]||e}function r(e){this.walk=function(t,r){function o(e){var t;return t=e[0],3===t.nodeType&&t===c&&u>=t.nodeValue.length&&e.splice(0,1),t=e[e.length-1],0===f&&e.length>0&&t===d&&3===t.nodeType&&e.splice(e.length-1,1),e}function a(e,t,n){for(var r=[];e&&e!=n;e=e[t])r.push(e);return r}function s(e,t){do{if(e.parentNode==t)return e;e=e.parentNode}while(e)}function l(e,t,n){var i=n?"nextSibling":"previousSibling";for(g=e,v=g.parentNode;g&&g!=t;g=v)v=g.parentNode,y=a(g==e?g:g[i],i),y.length&&(n||y.reverse(),r(o(y)))}var c=t.startContainer,u=t.startOffset,d=t.endContainer,f=t.endOffset,p,h,m,g,v,y,b;if(b=e.select("td.mce-item-selected,th.mce-item-selected"),b.length>0)return void i(b,function(e){r([e])});if(1==c.nodeType&&c.hasChildNodes()&&(c=c.childNodes[u]),1==d.nodeType&&d.hasChildNodes()&&(d=n(d,f)),c==d)return r(o([c]));for(p=e.findCommonAncestor(c,d),g=c;g;g=g.parentNode){if(g===d)return l(c,p,!0);if(g===p)break}for(g=d;g;g=g.parentNode){if(g===c)return l(d,p);if(g===p)break}h=s(c,p)||c,m=s(d,p)||d,l(c,h,!0),y=a(h==c?h:h.nextSibling,"nextSibling",m==d?m.nextSibling:m),y.length&&r(o(y)),l(d,m)},this.split=function(e){function t(e,t){return e.splitText(t)}var n=e.startContainer,r=e.startOffset,i=e.endContainer,o=e.endOffset;return n==i&&3==n.nodeType?r>0&&rr?(o-=r,n=i=t(i,o).previousSibling,o=i.nodeValue.length,r=0):o=0):(3==n.nodeType&&r>0&&r0&&o0)return c=p,u=n?p.nodeValue.length:0,void(i=!0);if(e.isBlock(p)||h[p.nodeName.toLowerCase()])return;s=p}o&&s&&(c=s,i=!0,u=0)}var c,u,d,f=e.getRoot(),p,h,m,g;if(c=n[(r?"start":"end")+"Container"],u=n[(r?"start":"end")+"Offset"],g=1==c.nodeType&&u===c.childNodes.length,h=e.schema.getNonEmptyElements(),m=r,1==c.nodeType&&u>c.childNodes.length-1&&(m=!1),9===c.nodeType&&(c=e.getRoot(),u=0),c===f){if(m&&(p=c.childNodes[u>0?u-1:0],p&&(h[p.nodeName]||"TABLE"==p.nodeName)))return;if(c.hasChildNodes()&&(u=Math.min(!m&&u>0?u-1:u,c.childNodes.length-1),c=c.childNodes[u],u=0,c.hasChildNodes()&&!/TABLE/.test(c.nodeName))){p=c,d=new t(c,f);do{if(3===p.nodeType&&p.nodeValue.length>0){u=m?0:p.nodeValue.length,c=p,i=!0;break}if(h[p.nodeName.toLowerCase()]){u=e.nodeIndex(p),c=p.parentNode,"IMG"!=p.nodeName||m||u++,i=!0;break}}while(p=m?d.next():d.prev())}}o&&(3===c.nodeType&&0===u&&l(!0),1===c.nodeType&&(p=c.childNodes[u],p||(p=c.childNodes[u-1]),!p||"BR"!==p.nodeName||s(p,"A")||a(p)||a(p,!0)||l(!0,p))),m&&!o&&3===c.nodeType&&u===c.nodeValue.length&&l(!1),i&&n["set"+(r?"Start":"End")](c,u)}var i,o;return o=n.collapsed,r(!0),o||r(),i&&o&&n.collapse(!0),i}}var i=e.each;return r.compareRanges=function(e,t){if(e&&t){if(!e.item&&!e.duplicate)return e.startContainer==t.startContainer&&e.startOffset==t.startOffset;if(e.item&&t.item&&e.item(0)===t.item(0))return!0;if(e.isEqual&&t.isEqual&&t.isEqual(e))return!0}return!1},r}),r(w,[x],function(e){return function(t){function n(e){var n,r;if(r=t.$(e).parentsUntil(t.getBody()).add(e),r.length===i.length){for(n=r.length;n>=0&&r[n]===i[n];n--);if(-1===n)return i=r,!0}return i=r,!1}var r,i=[];"onselectionchange"in t.getDoc()||t.on("NodeChange Click MouseUp KeyUp Focus",function(n){var i,o;i=t.selection.getRng(),o={startContainer:i.startContainer,startOffset:i.startOffset,endContainer:i.endContainer,endOffset:i.endOffset},"nodechange"!=n.type&&e.compareRanges(o,r)||t.fire("SelectionChange"),r=o}),t.on("contextmenu",function(){t.fire("SelectionChange")}),t.on("SelectionChange",function(){var e=t.selection.getStart(!0);t.selection.isCollapsed()||n(e)||!t.dom.isChildOf(e,t.getBody())||t.nodeChanged({selectionChange:!0})}),t.on("MouseUp",function(e){e.isDefaultPrevented()||setTimeout(function(){t.nodeChanged()},0)}),this.nodeChanged=function(e){var n=t.selection,r,i,o;t.initialized&&n&&!t.settings.disable_nodechange&&!t.settings.readonly&&(o=t.getBody(),r=n.getStart()||o,r=r.ownerDocument!=t.getDoc()?t.getBody():r,"IMG"==r.nodeName&&n.isCollapsed()&&(r=r.parentNode),i=[],t.dom.getParent(r,function(e){return e===o?!0:void i.push(e)}),e=e||{},e.element=r,e.parents=i,t.fire("NodeChange",e))}}}),r(_,[],function(){function e(e,t,n){var r,i,o=n?"lastChild":"firstChild",a=n?"prev":"next";if(e[o])return e[o];if(e!==t){if(r=e[a])return r;for(i=e.parent;i&&i!==t;i=i.parent)if(r=i[a])return r}}function t(e,t){this.name=e,this.type=t,1===t&&(this.attributes=[],this.attributes.map={})}var n=/^[ \t\r\n]*$/,r={"#text":3,"#comment":8,"#cdata":4,"#pi":7,"#doctype":10,"#document-fragment":11};return t.prototype={replace:function(e){var t=this;return e.parent&&e.remove(),t.insert(e,t),t.remove(),t},attr:function(e,t){var n=this,r,i,o;if("string"!=typeof e){for(i in e)n.attr(i,e[i]);return n}if(r=n.attributes){if(t!==o){if(null===t){if(e in r.map)for(delete r.map[e],i=r.length;i--;)if(r[i].name===e)return r=r.splice(i,1),n;return n}if(e in r.map){for(i=r.length;i--;)if(r[i].name===e){r[i].value=t;break}}else r.push({name:e,value:t});return r.map[e]=t,n}return r.map[e]}},clone:function(){var e=this,n=new t(e.name,e.type),r,i,o,a,s;if(o=e.attributes){for(s=[],s.map={},r=0,i=o.length;i>r;r++)a=o[r],"id"!==a.name&&(s[s.length]={name:a.name,value:a.value},s.map[a.name]=a.value);n.attributes=s}return n.value=e.value,n.shortEnded=e.shortEnded,n},wrap:function(e){var t=this;return t.parent.insert(e,t),e.append(t),t},unwrap:function(){var e=this,t,n;for(t=e.firstChild;t;)n=t.next,e.insert(t,e,!0),t=n;e.remove()},remove:function(){var e=this,t=e.parent,n=e.next,r=e.prev;return t&&(t.firstChild===e?(t.firstChild=n,n&&(n.prev=null)):r.next=n,t.lastChild===e?(t.lastChild=r,r&&(r.next=null)):n.prev=r,e.parent=e.next=e.prev=null),e},append:function(e){var t=this,n;return e.parent&&e.remove(),n=t.lastChild,n?(n.next=e,e.prev=n,t.lastChild=e):t.lastChild=t.firstChild=e,e.parent=t,e},insert:function(e,t,n){var r;return e.parent&&e.remove(),r=t.parent||this,n?(t===r.firstChild?r.firstChild=e:t.prev.next=e,e.prev=t.prev,e.next=t,t.prev=e):(t===r.lastChild?r.lastChild=e:t.next.prev=e,e.next=t.next,e.prev=t,t.next=e),e.parent=r,e},getAll:function(t){var n=this,r,i=[];for(r=n.firstChild;r;r=e(r,n))r.name===t&&i.push(r);return i},empty:function(){var t=this,n,r,i;if(t.firstChild){for(n=[],i=t.firstChild;i;i=e(i,t))n.push(i);for(r=n.length;r--;)i=n[r],i.parent=i.firstChild=i.lastChild=i.next=i.prev=null}return t.firstChild=t.lastChild=null,t},isEmpty:function(t){var r=this,i=r.firstChild,o,a;if(i)do{if(1===i.type){if(i.attributes.map["data-mce-bogus"])continue;if(t[i.name])return!1;for(o=i.attributes.length;o--;)if(a=i.attributes[o].name,"name"===a||0===a.indexOf("data-mce-bookmark"))return!1}if(8===i.type)return!1;if(3===i.type&&!n.test(i.value))return!1}while(i=e(i,r));return!0},walk:function(t){return e(this,null,t)}},t.create=function(e,n){var i,o;if(i=new t(e,r[e]||1),n)for(o in n)i.attr(o,n[o]);return i},t}),r(E,[d],function(e){function t(e,t){return e?e.split(t||" "):[]}function n(e){function n(e,n,r){function i(e,t){var n={},r,i;for(r=0,i=e.length;i>r;r++)n[e[r]]=t||{};return n}var s,c,u,d=arguments;for(r=r||[],n=n||"","string"==typeof r&&(r=t(r)),c=3;co;o++)i.attributes[n[o]]={},i.attributesOrder.push(n[o])}var a={},l,c,u,d,f,p;return i[e]?i[e]:(l=t("id accesskey class dir lang style tabindex title"),c=t("address blockquote div dl fieldset form h1 h2 h3 h4 h5 h6 hr menu ol p pre table ul"),u=t("a abbr b bdo br button cite code del dfn em embed i iframe img input ins kbd label map noscript object q s samp script select small span strong sub sup textarea u var #text #comment"),"html4"!=e&&(l.push.apply(l,t("contenteditable contextmenu draggable dropzone hidden spellcheck translate")),c.push.apply(c,t("article aside details dialog figure header footer hgroup section nav")),u.push.apply(u,t("audio canvas command datalist mark meter output progress time wbr video ruby bdi keygen"))),"html5-strict"!=e&&(l.push("xml:lang"),p=t("acronym applet basefont big font strike tt"),u.push.apply(u,p),s(p,function(e){n(e,"",u)}),f=t("center dir isindex noframes"),c.push.apply(c,f),d=[].concat(c,u),s(f,function(e){n(e,"",d)})),d=d||[].concat(c,u),n("html","manifest","head body"),n("head","","base command link meta noscript script style title"),n("title hr noscript br"),n("base","href target"),n("link","href rel media hreflang type sizes hreflang"),n("meta","name http-equiv content charset"),n("style","media type scoped"),n("script","src async defer type charset"),n("body","onafterprint onbeforeprint onbeforeunload onblur onerror onfocus onhashchange onload onmessage onoffline ononline onpagehide onpageshow onpopstate onresize onscroll onstorage onunload",d),n("address dt dd div caption","",d),n("h1 h2 h3 h4 h5 h6 pre p abbr code var samp kbd sub sup i b u bdo span legend em strong small s cite dfn","",u),n("blockquote","cite",d),n("ol","reversed start type","li"),n("ul","","li"),n("li","value",d),n("dl","","dt dd"),n("a","href target rel media hreflang type",u),n("q","cite",u),n("ins del","cite datetime",d),n("img","src srcset alt usemap ismap width height"),n("iframe","src name width height",d),n("embed","src type width height"),n("object","data type typemustmatch name usemap form width height",d,"param"),n("param","name value"),n("map","name",d,"area"),n("area","alt coords shape href target rel media hreflang type"),n("table","border","caption colgroup thead tfoot tbody tr"+("html4"==e?" col":"")),n("colgroup","span","col"),n("col","span"),n("tbody thead tfoot","","tr"),n("tr","","td th"),n("td","colspan rowspan headers",d),n("th","colspan rowspan headers scope abbr",d),n("form","accept-charset action autocomplete enctype method name novalidate target",d),n("fieldset","disabled form name",d,"legend"),n("label","form for",u),n("input","accept alt autocomplete checked dirname disabled form formaction formenctype formmethod formnovalidate formtarget height list max maxlength min multiple name pattern readonly required size src step type value width"),n("button","disabled form formaction formenctype formmethod formnovalidate formtarget name type value","html4"==e?d:u),n("select","disabled form multiple name required size","option optgroup"),n("optgroup","disabled label","option"),n("option","disabled label selected value"),n("textarea","cols dirname disabled form maxlength name readonly required rows wrap"),n("menu","type label",d,"li"),n("noscript","",d),"html4"!=e&&(n("wbr"),n("ruby","",u,"rt rp"),n("figcaption","",d),n("mark rt rp summary bdi","",u),n("canvas","width height",d),n("video","src crossorigin poster preload autoplay mediagroup loop muted controls width height buffered",d,"track source"),n("audio","src crossorigin preload autoplay mediagroup loop muted controls buffered volume",d,"track source"),n("picture","","img source"),n("source","src srcset type media sizes"),n("track","kind src srclang label default"),n("datalist","",u,"option"),n("article section nav aside header footer","",d),n("hgroup","","h1 h2 h3 h4 h5 h6"),n("figure","",d,"figcaption"),n("time","datetime",u),n("dialog","open",d),n("command","type label icon disabled checked radiogroup command"),n("output","for form name",u),n("progress","value max",u),n("meter","value min max low high optimum",u),n("details","open",d,"summary"),n("keygen","autofocus challenge disabled form keytype name")),"html5-strict"!=e&&(r("script","language xml:space"),r("style","xml:space"),r("object","declare classid code codebase codetype archive standby align border hspace vspace"),r("embed","align name hspace vspace"),r("param","valuetype type"),r("a","charset name rev shape coords"),r("br","clear"),r("applet","codebase archive code object alt name width height align hspace vspace"),r("img","name longdesc align border hspace vspace"),r("iframe","longdesc frameborder marginwidth marginheight scrolling align"),r("font basefont","size color face"),r("input","usemap align"),r("select","onchange"),r("textarea"),r("h1 h2 h3 h4 h5 h6 div p legend caption","align"),r("ul","type compact"),r("li","type"),r("ol dl menu dir","compact"),r("pre","width xml:space"),r("hr","align noshade size width"),r("isindex","prompt"),r("table","summary width frame rules cellspacing cellpadding align bgcolor"),r("col","width align char charoff valign"),r("colgroup","width align char charoff valign"),r("thead","align char charoff valign"),r("tr","align char charoff valign bgcolor"),r("th","axis align char charoff valign nowrap bgcolor width height"),r("form","accept"),r("td","abbr axis scope align char charoff valign nowrap bgcolor width height"),r("tfoot","align char charoff valign"),r("tbody","align char charoff valign"),r("area","nohref"),r("body","background bgcolor text link vlink alink")),"html4"!=e&&(r("input button select textarea","autofocus"),r("input textarea","placeholder"),r("a","download"),r("link script img","crossorigin"),r("iframe","sandbox seamless allowfullscreen")),s(t("a form meter progress dfn"),function(e){a[e]&&delete a[e].children[e]}),delete a.caption.children.table,i[e]=a,a)}function r(e,t){var n;return e&&(n={},"string"==typeof e&&(e={"*":e}),s(e,function(e,r){n[r]="map"==t?a(e,/[, ]/):c(e,/[, ]/)})),n}var i={},o={},a=e.makeMap,s=e.each,l=e.extend,c=e.explode,u=e.inArray;return function(e){function o(t,n,r){var o=e[t];return o?o=a(o,/[, ]/,a(o.toUpperCase(),/[, ]/)):(o=i[t],o||(o=a(n," ",a(n.toUpperCase()," ")),o=l(o,r),i[t]=o)),o}function d(e){return new RegExp("^"+e.replace(/([?+*])/g,".$1")+"$")}function f(e){var n,r,i,o,s,l,c,f,p,h,m,g,v,b,x,w,_,E,N,k=/^([#+\-])?([^\[!\/]+)(?:\/([^\[!]+))?(?:(!?)\[([^\]]+)\])?$/,S=/^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/,T=/[*?+]/;if(e)for(e=t(e,","),y["@"]&&(w=y["@"].attributes,_=y["@"].attributesOrder),n=0,r=e.length;r>n;n++)if(s=k.exec(e[n])){if(b=s[1],p=s[2],x=s[3],f=s[5],g={},v=[],l={attributes:g,attributesOrder:v},"#"===b&&(l.paddEmpty=!0),"-"===b&&(l.removeEmpty=!0),"!"===s[4]&&(l.removeEmptyAttrs=!0),w){for(E in w)g[E]=w[E];v.push.apply(v,_)}if(f)for(f=t(f,"|"),i=0,o=f.length;o>i;i++)if(s=S.exec(f[i])){if(c={},m=s[1],h=s[2].replace(/::/g,":"),b=s[3],N=s[4],"!"===m&&(l.attributesRequired=l.attributesRequired||[],l.attributesRequired.push(h),c.required=!0),"-"===m){delete g[h],v.splice(u(v,h),1);continue}b&&("="===b&&(l.attributesDefault=l.attributesDefault||[],l.attributesDefault.push({name:h,value:N}),c.defaultValue=N),":"===b&&(l.attributesForced=l.attributesForced||[],l.attributesForced.push({name:h,value:N}),c.forcedValue=N),"<"===b&&(c.validValues=a(N,"?"))),T.test(h)?(l.attributePatterns=l.attributePatterns||[],c.pattern=d(h),l.attributePatterns.push(c)):(g[h]||v.push(h),g[h]=c)}w||"@"!=p||(w=g,_=v),x&&(l.outputName=p,y[x]=l),T.test(p)?(l.pattern=d(p),C.push(l)):y[p]=l}}function p(e){y={},C=[],f(e),s(_,function(e,t){b[t]=e.children})}function h(e){var n=/^(~)?(.+)$/;e&&(i.text_block_elements=i.block_elements=null,s(t(e,","),function(e){var t=n.exec(e),r="~"===t[1],i=r?"span":"div",o=t[2];if(b[o]=b[i],L[o]=i,r||(R[o.toUpperCase()]={},R[o]={}),!y[o]){var a=y[i];a=l({},a),delete a.removeEmptyAttrs,delete a.removeEmpty,y[o]=a}s(b,function(e,t){e[i]&&(b[t]=e=l({},b[t]),e[o]=e[i])})}))}function m(e){var n=/^([+\-]?)(\w+)\[([^\]]+)\]$/;e&&s(t(e,","),function(e){var r=n.exec(e),i,o;r&&(o=r[1],i=o?b[r[2]]:b[r[2]]={"#comment":{}},i=b[r[2]],s(t(r[3],"|"),function(e){"-"===o?(b[r[2]]=i=l({},b[r[2]]),delete i[e]):i[e]={}}))})}function g(e){var t=y[e],n;if(t)return t;for(n=C.length;n--;)if(t=C[n],t.pattern.test(e))return t}var v=this,y={},b={},C=[],x,w,_,E,N,k,S,T,R,A,B,D,L={},H={};e=e||{},_=n(e.schema),e.verify_html===!1&&(e.valid_elements="*[*]"),x=r(e.valid_styles),w=r(e.invalid_styles,"map"),T=r(e.valid_classes,"map"),E=o("whitespace_elements","pre script noscript style textarea video audio iframe object"),N=o("self_closing_elements","colgroup dd dt li option p td tfoot th thead tr"),k=o("short_ended_elements","area base basefont br col frame hr img input isindex link meta param embed source wbr track"),S=o("boolean_attributes","checked compact declare defer disabled ismap multiple nohref noresize noshade nowrap readonly selected autoplay loop controls"),A=o("non_empty_elements","td th iframe video audio object script",k),B=o("text_block_elements","h1 h2 h3 h4 h5 h6 p div address pre form blockquote center dir fieldset header footer article section hgroup aside nav figure"),R=o("block_elements","hr table tbody thead tfoot th tr td li ol ul caption dl dt dd noscript menu isindex option datalist select optgroup",B),D=o("text_inline_elements","span strong b em i font strike u var cite dfn code mark q sup sub samp"),s((e.special||"script noscript style textarea").split(" "),function(e){H[e]=new RegExp("]*>","gi")}),e.valid_elements?p(e.valid_elements):(s(_,function(e,t){y[t]={attributes:e.attributes,attributesOrder:e.attributesOrder},b[t]=e.children}),"html5"!=e.schema&&s(t("strong/b em/i"),function(e){e=t(e,"/"),y[e[1]].outputName=e[0]}),y.img.attributesDefault=[{name:"alt",value:""}],s(t("ol ul sub sup blockquote span font a table tbody tr strong em b i"),function(e){y[e]&&(y[e].removeEmpty=!0)}),s(t("p h1 h2 h3 h4 h5 h6 th td pre div address caption"),function(e){y[e].paddEmpty=!0}),s(t("span"),function(e){y[e].removeEmptyAttrs=!0})),h(e.custom_elements),m(e.valid_children),f(e.extended_valid_elements),m("+ol[ul|ol],+ul[ul|ol]"),e.invalid_elements&&s(c(e.invalid_elements),function(e){y[e]&&delete y[e]}),g("span")||f("span[!data-mce-type|*]"),v.children=b,v.getValidStyles=function(){return x},v.getInvalidStyles=function(){return w},v.getValidClasses=function(){return T},v.getBoolAttrs=function(){return S},v.getBlockElements=function(){return R},v.getTextBlockElements=function(){return B},v.getTextInlineElements=function(){return D},v.getShortEndedElements=function(){return k},v.getSelfClosingElements=function(){return N},v.getNonEmptyElements=function(){return A},v.getWhiteSpaceElements=function(){return E},v.getSpecialElements=function(){return H},v.isValidChild=function(e,t){var n=b[e];return!(!n||!n[t])},v.isValid=function(e,t){var n,r,i=g(e);if(i){if(!t)return!0;if(i.attributes[t])return!0;if(n=i.attributePatterns)for(r=n.length;r--;)if(n[r].pattern.test(e))return!0}return!1},v.getElementRule=g,v.getCustomElements=function(){return L},v.addValidElements=f,v.setValidElements=p,v.addCustomElements=h,v.addValidChildren=m,v.elements=y}}),r(N,[E,g,d],function(e,t,n){function r(e,t,n){var r=1,i,o,a,s;for(s=e.getShortEndedElements(),a=/<([!?\/])?([A-Za-z0-9\-_\:\.]+)((?:\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\/|\s+)>/g,a.lastIndex=i=n;o=a.exec(t);){if(i=a.lastIndex,"/"===o[1])r--;else if(!o[1]){if(o[2]in s)continue;r++}if(0===r)break}return i}function i(i,a){function s(){}var l=this;i=i||{},l.schema=a=a||new e,i.fix_self_closing!==!1&&(i.fix_self_closing=!0),o("comment cdata text start end pi doctype".split(" "),function(e){e&&(l[e]=i[e]||s)}),l.parse=function(e){function o(e){var t,n;for(t=p.length;t--&&p[t].name!==e;);if(t>=0){for(n=p.length-1;n>=t;n--)e=p[n],e.valid&&l.end(e.name);p.length=t}}function s(e,t,n,r,o){var a,s,l=/[\s\u0000-\u001F]+/g;if(t=t.toLowerCase(),n=t in x?t:z(n||r||o||""),_&&!y&&0!==t.indexOf("data-")){if(a=T[t],!a&&R){for(s=R.length;s--&&(a=R[s],!a.pattern.test(t)););-1===s&&(a=null)}if(!a)return;if(a.validValues&&!(n in a.validValues))return}if(V[t]&&!i.allow_script_urls){var c=n.replace(l,"");try{c=decodeURIComponent(c)}catch(u){c=unescape(c)}if(U.test(c))return;if(!i.allow_html_data_urls&&$.test(c)&&!/^data:image\//i.test(c))return}h.map[t]=n,h.push({name:t,value:n})}var l=this,c,u=0,d,f,p=[],h,m,g,v,y,b,C,x,w,_,E,N,k,S,T,R,A,B,D,L,H,M,P,O,I,F=0,z=t.decode,W,V=n.makeMap("src,href,data,background,formaction,poster"),U=/((java|vb)script|mhtml):/i,$=/^data:/i;for(M=new RegExp("<(?:(?:!--([\\w\\W]*?)-->)|(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|(?:!DOCTYPE([\\w\\W]*?)>)|(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|(?:\\/([^>]+)>)|(?:([A-Za-z0-9\\-_\\:\\.]+)((?:\\s+[^\"'>]+(?:(?:\"[^\"]*\")|(?:'[^']*')|[^>]*))*|\\/|\\s+)>))","g"),P=/([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:[^\"])*)\")|(?:\'((?:[^\'])*)\')|([^>\s]+)))?/g,C=a.getShortEndedElements(),H=i.self_closing_elements||a.getSelfClosingElements(),x=a.getBoolAttrs(),_=i.validate,b=i.remove_internals,W=i.fix_self_closing,O=a.getSpecialElements();c=M.exec(e);){if(u0&&p[p.length-1].name===d&&o(d),!_||(E=a.getElementRule(d))){if(N=!0,_&&(T=E.attributes,R=E.attributePatterns),(S=c[8])?(y=-1!==S.indexOf("data-mce-type"),y&&b&&(N=!1),h=[],h.map={},S.replace(P,s)):(h=[],h.map={}),_&&!y){if(A=E.attributesRequired,B=E.attributesDefault,D=E.attributesForced,L=E.removeEmptyAttrs,L&&!h.length&&(N=!1),D)for(m=D.length;m--;)k=D[m],v=k.name,I=k.value,"{$uid}"===I&&(I="mce_"+F++),h.map[v]=I,h.push({name:v,value:I});if(B)for(m=B.length;m--;)k=B[m],v=k.name,v in h.map||(I=k.value,"{$uid}"===I&&(I="mce_"+F++),h.map[v]=I,h.push({name:v,value:I}));if(A){for(m=A.length;m--&&!(A[m]in h.map););-1===m&&(N=!1)}if(k=h.map["data-mce-bogus"]){if("all"===k){u=r(a,e,M.lastIndex),M.lastIndex=u;continue}N=!1}}N&&l.start(d,h,w)}else N=!1;if(f=O[d]){f.lastIndex=u=c.index+c[0].length,(c=f.exec(e))?(N&&(g=e.substr(u,c.index-u)),u=c.index+c[0].length):(g=e.substr(u),u=e.length),N&&(g.length>0&&l.text(g,!0),l.end(d)),M.lastIndex=u;continue}w||(S&&S.indexOf("/")==S.length-1?N&&l.end(d):p.push({name:d,valid:N}))}else(d=c[1])?(">"===d.charAt(0)&&(d=" "+d),i.allow_conditional_comments||"[if"!==d.substr(0,3)||(d=" "+d),l.comment(d)):(d=c[2])?l.cdata(d):(d=c[3])?l.doctype(d):(d=c[4])&&l.pi(d,c[5]);u=c.index+c[0].length}for(u=0;m--)d=p[m],d.valid&&l.end(d.name)}}var o=n.each;return i.findEndTag=r,i}),r(k,[_,E,N,d],function(e,t,n,r){var i=r.makeMap,o=r.each,a=r.explode,s=r.extend;return function(r,l){function c(t){var n,r,o,a,s,c,d,f,p,h,m,g,v,y;for(m=i("tr,td,th,tbody,thead,tfoot,table"),h=l.getNonEmptyElements(),g=l.getTextBlockElements(),n=0;n1){for(a.reverse(),s=c=u.filterNode(a[0].clone()),p=0;p0?(t.value=n,t=t.prev):(r=t.prev,t.remove(),t=r)}function g(e){var t,n={};for(t in e)"li"!==t&&"p"!=t&&(n[t]=e[t]);return n}var v,y,b,C,x,w,_,E,N,k,S,T,R,A=[],B,D,L,H,M,P,O,I;if(o=o||{},p={},h={},T=s(i("script,style,head,html,body,title,meta,param"),l.getBlockElements()),O=l.getNonEmptyElements(),P=l.children,S=r.validate,I="forced_root_block"in o?o.forced_root_block:r.forced_root_block,M=l.getWhiteSpaceElements(),R=/^[ \t\r\n]+/,D=/[ \t\r\n]+$/,L=/[ \t\r\n]+/g,H=/^[ \t\r\n]+$/,v=new n({validate:S,allow_script_urls:r.allow_script_urls,allow_conditional_comments:r.allow_conditional_comments,self_closing_elements:g(l.getSelfClosingElements()),cdata:function(e){b.append(u("#cdata",4)).value=e},text:function(e,t){var n;B||(e=e.replace(L," "),b.lastChild&&T[b.lastChild.name]&&(e=e.replace(R,""))),0!==e.length&&(n=u("#text",3),n.raw=!!t,b.append(n).value=e)},comment:function(e){b.append(u("#comment",8)).value=e},pi:function(e,t){b.append(u(e,7)).value=t,m(b)},doctype:function(e){var t;t=b.append(u("#doctype",10)),t.value=e,m(b)},start:function(e,t,n){var r,i,o,a,s;if(o=S?l.getElementRule(e):{}){for(r=u(o.outputName||e,1),r.attributes=t,r.shortEnded=n,b.append(r),s=P[b.name],s&&P[r.name]&&!s[r.name]&&A.push(r),i=f.length;i--;)a=f[i].name,a in t.map&&(N=h[a],N?N.push(r):h[a]=[r]);T[e]&&m(r),n||(b=r),!B&&M[e]&&(B=!0)}},end:function(t){var n,r,i,o,a;if(r=S?l.getElementRule(t):{}){if(T[t]&&!B){if(n=b.firstChild,n&&3===n.type)if(i=n.value.replace(R,""),i.length>0)n.value=i,n=n.next;else for(o=n.next,n.remove(),n=o;n&&3===n.type;)i=n.value,o=n.next,(0===i.length||H.test(i))&&(n.remove(),n=o),n=o;if(n=b.lastChild,n&&3===n.type)if(i=n.value.replace(D,""),i.length>0)n.value=i,n=n.prev;else for(o=n.prev,n.remove(),n=o;n&&3===n.type;)i=n.value,o=n.prev,(0===i.length||H.test(i))&&(n.remove(),n=o),n=o}if(B&&M[t]&&(B=!1),(r.removeEmpty||r.paddEmpty)&&b.isEmpty(O))if(r.paddEmpty)b.empty().append(new e("#text","3")).value="\xa0";else if(!b.attributes.map.name&&!b.attributes.map.id)return a=b.parent,T[b.name]?b.empty().remove():b.unwrap(),void(b=a); +b=b.parent}}},l),y=b=new e(o.context||r.root_name,11),v.parse(t),S&&A.length&&(o.context?o.invalid=!0:c(A)),I&&("body"==y.name||o.isRootContent)&&a(),!o.invalid){for(k in p){for(N=d[k],C=p[k],_=C.length;_--;)C[_].parent||C.splice(_,1);for(x=0,w=N.length;w>x;x++)N[x](C,k,o)}for(x=0,w=f.length;w>x;x++)if(N=f[x],N.name in h){for(C=h[N.name],_=C.length;_--;)C[_].parent||C.splice(_,1);for(_=0,E=N.callbacks.length;E>_;_++)N.callbacks[_](C,N.name,o)}}return y},r.remove_trailing_brs&&u.addNodeFilter("br",function(t){var n,r=t.length,i,o=s({},l.getBlockElements()),a=l.getNonEmptyElements(),c,u,d,f,p,h;for(o.body=1,n=0;r>n;n++)if(i=t[n],c=i.parent,o[i.parent.name]&&i===c.lastChild){for(d=i.prev;d;){if(f=d.name,"span"!==f||"bookmark"!==d.attr("data-mce-type")){if("br"!==f)break;if("br"===f){i=null;break}}d=d.prev}i&&(i.remove(),c.isEmpty(a)&&(p=l.getElementRule(c.name),p&&(p.removeEmpty?c.remove():p.paddEmpty&&(c.empty().append(new e("#text",3)).value="\xa0"))))}else{for(u=i;c&&c.firstChild===u&&c.lastChild===u&&(u=c,!o[c.name]);)c=c.parent;u===c&&(h=new e("#text",3),h.value="\xa0",i.replace(h))}}),r.allow_html_in_named_anchor||u.addAttributeFilter("id,name",function(e){for(var t=e.length,n,r,i,o;t--;)if(o=e[t],"a"===o.name&&o.firstChild&&!o.attr("href")){i=o.parent,n=o.lastChild;do r=n.prev,i.insert(n,o),n=r;while(n)}}),r.validate&&l.getValidClasses()&&u.addAttributeFilter("class",function(e){for(var t=e.length,n,r,i,o,a,s=l.getValidClasses(),c,u;t--;){for(n=e[t],r=n.attr("class").split(" "),a="",i=0;i0&&(f=r[r.length-1],f.length>0&&"\n"!==f&&r.push("\n")),r.push("<",e),t)for(c=0,u=t.length;u>c;c++)d=t[c],r.push(" ",d.name,'="',s(d.value,!0),'"');r[r.length]=!n||l?">":" />",n&&i&&a[e]&&r.length>0&&(f=r[r.length-1],f.length>0&&"\n"!==f&&r.push("\n"))},end:function(e){var t;r.push(""),i&&a[e]&&r.length>0&&(t=r[r.length-1],t.length>0&&"\n"!==t&&r.push("\n"))},text:function(e,t){e.length>0&&(r[r.length]=t?e:s(e))},cdata:function(e){r.push("")},comment:function(e){r.push("")},pi:function(e,t){t?r.push(""):r.push(""),i&&r.push("\n")},doctype:function(e){r.push("",i?"\n":"")},reset:function(){r.length=0},getContent:function(){return r.join("").replace(/\n$/,"")}}}}),r(T,[S,E],function(e,t){return function(n,r){var i=this,o=new e(n);n=n||{},n.validate="validate"in n?n.validate:!0,i.schema=r=r||new t,i.writer=o,i.serialize=function(e){function t(e){var n=i[e.type],s,l,c,u,d,f,p,h,m;if(n)n(e);else{if(s=e.name,l=e.shortEnded,c=e.attributes,a&&c&&c.length>1){for(f=[],f.map={},m=r.getElementRule(e.name),p=0,h=m.attributesOrder.length;h>p;p++)u=m.attributesOrder[p],u in c.map&&(d=c.map[u],f.map[u]=d,f.push({name:u,value:d}));for(p=0,h=c.length;h>p;p++)u=c[p].name,u in f.map||(d=c.map[u],f.map[u]=d,f.push({name:u,value:d}));c=f}if(o.start(e.name,c,l),!l){if(e=e.firstChild)do t(e);while(e=e.next);o.end(s)}}}var i,a;return a=n.validate,i={3:function(e){o.text(e.value,e.raw)},8:function(e){o.comment(e.value)},7:function(e){o.pi(e.name,e.value)},10:function(e){o.doctype(e.value)},4:function(e){o.cdata(e.value)},11:function(e){if(e=e.firstChild)do t(e);while(e=e.next)}},o.reset(),1!=e.type||n.inner?i[11](e):t(e),o.getContent()}}}),r(R,[y,k,g,T,_,E,u,d],function(e,t,n,r,i,o,a,s){var l=s.each,c=s.trim,u=e.DOM;return function(e,i){var s,d,f;return i&&(s=i.dom,d=i.schema),s=s||u,d=d||new o(e),e.entity_encoding=e.entity_encoding||"named",e.remove_trailing_brs="remove_trailing_brs"in e?e.remove_trailing_brs:!0,f=new t(e,d),f.addAttributeFilter("data-mce-tabindex",function(e,t){for(var n=e.length,r;n--;)r=e[n],r.attr("tabindex",r.attributes.map["data-mce-tabindex"]),r.attr(t,null)}),f.addAttributeFilter("src,href,style",function(t,n){for(var r=t.length,i,o,a="data-mce-"+n,l=e.url_converter,c=e.url_converter_scope,u;r--;)i=t[r],o=i.attributes.map[a],o!==u?(i.attr(n,o.length>0?o:null),i.attr(a,null)):(o=i.attributes.map[n],"style"===n?o=s.serializeStyle(s.parseStyle(o),i.name):l&&(o=l.call(c,o,n,i.name)),i.attr(n,o.length>0?o:null))}),f.addAttributeFilter("class",function(e){for(var t=e.length,n,r;t--;)n=e[t],r=n.attr("class"),r&&(r=n.attr("class").replace(/(?:^|\s)mce-item-\w+(?!\S)/g,""),n.attr("class",r.length>0?r:null))}),f.addAttributeFilter("data-mce-type",function(e,t,n){for(var r=e.length,i;r--;)i=e[r],"bookmark"!==i.attributes.map["data-mce-type"]||n.cleanup||i.remove()}),f.addNodeFilter("noscript",function(e){for(var t=e.length,r;t--;)r=e[t].firstChild,r&&(r.value=n.decode(r.value))}),f.addNodeFilter("script,style",function(e,t){function n(e){return e.replace(/()/g,"\n").replace(/^[\r\n]*|[\r\n]*$/g,"").replace(/^\s*(()?|\s*\/\/\s*\]\]>(-->)?|\/\/\s*(-->)?|\]\]>|\/\*\s*-->\s*\*\/|\s*-->\s*)\s*$/g,"")}for(var r=e.length,i,o,a;r--;)i=e[r],o=i.firstChild?i.firstChild.value:"","script"===t?(a=i.attr("type"),a&&i.attr("type","mce-no/type"==a?null:a.replace(/^mce\-/,"")),o.length>0&&(i.firstChild.value="// ")):o.length>0&&(i.firstChild.value="")}),f.addNodeFilter("#comment",function(e){for(var t=e.length,n;t--;)n=e[t],0===n.value.indexOf("[CDATA[")?(n.name="#cdata",n.type=4,n.value=n.value.replace(/^\[CDATA\[|\]\]$/g,"")):0===n.value.indexOf("mce:protected ")&&(n.name="#text",n.type=3,n.raw=!0,n.value=unescape(n.value).substr(14))}),f.addNodeFilter("xml:namespace,input",function(e,t){for(var n=e.length,r;n--;)r=e[n],7===r.type?r.remove():1===r.type&&("input"!==t||"type"in r.attributes.map||r.attr("type","text"))}),e.fix_list_elements&&f.addNodeFilter("ul,ol",function(e){for(var t=e.length,n,r;t--;)n=e[t],r=n.parent,("ul"===r.name||"ol"===r.name)&&n.prev&&"li"===n.prev.name&&n.prev.append(n)}),f.addAttributeFilter("data-mce-src,data-mce-href,data-mce-style,data-mce-selected,data-mce-expando,data-mce-type,data-mce-resize",function(e,t){for(var n=e.length;n--;)e[n].attr(t,null)}),{schema:d,addNodeFilter:f.addNodeFilter,addAttributeFilter:f.addAttributeFilter,serialize:function(t,n){var i=this,o,u,p,h,m;return a.ie&&s.select("script,style,select,map").length>0?(m=t.innerHTML,t=t.cloneNode(!1),s.setHTML(t,m)):t=t.cloneNode(!0),o=t.ownerDocument.implementation,o.createHTMLDocument&&(u=o.createHTMLDocument(""),l("BODY"==t.nodeName?t.childNodes:[t],function(e){u.body.appendChild(u.importNode(e,!0))}),t="BODY"!=t.nodeName?u.body.firstChild:u.body,p=s.doc,s.doc=u),n=n||{},n.format=n.format||"html",n.selection&&(n.forced_root_block=""),n.no_events||(n.node=t,i.onPreProcess(n)),h=new r(e,d),n.content=h.serialize(f.parse(c(n.getInner?t.innerHTML:s.getOuterHTML(t)),n)),n.cleanup||(n.content=n.content.replace(/\uFEFF/g,"")),n.no_events||i.onPostProcess(n),p&&(s.doc=p),n.node=null,n.content},addRules:function(e){d.addValidElements(e)},setRules:function(e){d.setValidElements(e)},onPreProcess:function(e){i&&i.fire("PreProcess",e)},onPostProcess:function(e){i&&i.fire("PostProcess",e)}}}}),r(A,[],function(){function e(e){function t(t,n){var r,i=0,o,a,s,l,c,u,d=-1,f;if(r=t.duplicate(),r.collapse(n),f=r.parentElement(),f.ownerDocument===e.dom.doc){for(;"false"===f.contentEditable;)f=f.parentNode;if(!f.hasChildNodes())return{node:f,inside:1};for(s=f.children,o=s.length-1;o>=i;)if(u=Math.floor((i+o)/2),l=s[u],r.moveToElementText(l),d=r.compareEndPoints(n?"StartToStart":"EndToEnd",t),d>0)o=u-1;else{if(!(0>d))return{node:l};i=u+1}if(0>d)for(l?r.collapse(!1):(r.moveToElementText(f),r.collapse(!0),l=f,a=!0),c=0;0!==r.compareEndPoints(n?"StartToStart":"StartToEnd",t)&&0!==r.move("character",1)&&f==r.parentElement();)c++;else for(r.collapse(!0),c=0;0!==r.compareEndPoints(n?"StartToStart":"StartToEnd",t)&&0!==r.move("character",-1)&&f==r.parentElement();)c++;return{node:l,position:d,offset:c,inside:a}}}function n(){function n(e){var n=t(o,e),r,i,s=0,l,c,u;if(r=n.node,i=n.offset,n.inside&&!r.hasChildNodes())return void a[e?"setStart":"setEnd"](r,0);if(i===c)return void a[e?"setStartBefore":"setEndAfter"](r);if(n.position<0){if(l=n.inside?r.firstChild:r.nextSibling,!l)return void a[e?"setStartAfter":"setEndAfter"](r);if(!i)return void(3==l.nodeType?a[e?"setStart":"setEnd"](l,0):a[e?"setStartBefore":"setEndBefore"](l));for(;l;){if(3==l.nodeType&&(u=l.nodeValue,s+=u.length,s>=i)){r=l,s-=i,s=u.length-s;break}l=l.nextSibling}}else{if(l=r.previousSibling,!l)return a[e?"setStartBefore":"setEndBefore"](r);if(!i)return void(3==r.nodeType?a[e?"setStart":"setEnd"](l,r.nodeValue.length):a[e?"setStartAfter":"setEndAfter"](l));for(;l;){if(3==l.nodeType&&(s+=l.nodeValue.length,s>=i)){r=l,s-=i;break}l=l.previousSibling}}a[e?"setStart":"setEnd"](r,s)}var o=e.getRng(),a=i.createRng(),s,l,c,u,d;if(s=o.item?o.item(0):o.parentElement(),s.ownerDocument!=i.doc)return a;if(l=e.isCollapsed(),o.item)return a.setStart(s.parentNode,i.nodeIndex(s)),a.setEnd(a.startContainer,a.startOffset+1),a;try{n(!0),l||n()}catch(f){if(-2147024809!=f.number)throw f;d=r.getBookmark(2),c=o.duplicate(),c.collapse(!0),s=c.parentElement(),l||(c=o.duplicate(),c.collapse(!1),u=c.parentElement(),u.innerHTML=u.innerHTML),s.innerHTML=s.innerHTML,r.moveToBookmark(d),o=e.getRng(),n(!0),l||n()}return a}var r=this,i=e.dom,o=!1;this.getBookmark=function(n){function r(e){var t,n,r,o,a=[];for(t=e.parentNode,n=i.getRoot().parentNode;t!=n&&9!==t.nodeType;){for(r=t.children,o=r.length;o--;)if(e===r[o]){a.push(o);break}e=t,t=t.parentNode}return a}function o(e){var n;return n=t(a,e),n?{position:n.position,offset:n.offset,indexes:r(n.node),inside:n.inside}:void 0}var a=e.getRng(),s={};return 2===n&&(a.item?s.start={ctrl:!0,indexes:r(a.item(0))}:(s.start=o(!0),e.isCollapsed()||(s.end=o()))),s},this.moveToBookmark=function(e){function t(e){var t,n,r,o;for(t=i.getRoot(),n=e.length-1;n>=0;n--)o=t.children,r=e[n],r<=o.length-1&&(t=o[r]);return t}function n(n){var i=e[n?"start":"end"],a,s,l,c;i&&(a=i.position>0,s=o.createTextRange(),s.moveToElementText(t(i.indexes)),c=i.offset,c!==l?(s.collapse(i.inside||a),s.moveStart("character",a?-c:c)):s.collapse(n),r.setEndPoint(n?"StartToStart":"EndToStart",s),n&&r.collapse(!0))}var r,o=i.doc.body;e.start&&(e.start.ctrl?(r=o.createControlRange(),r.addElement(t(e.start.indexes)),r.select()):(r=o.createTextRange(),n(!0),n(),r.select()))},this.addRange=function(t){function n(e){var t,n,a,d,h;a=i.create("a"),t=e?s:c,n=e?l:u,d=r.duplicate(),(t==f||t==f.documentElement)&&(t=p,n=0),3==t.nodeType?(t.parentNode.insertBefore(a,t),d.moveToElementText(a),d.moveStart("character",n),i.remove(a),r.setEndPoint(e?"StartToStart":"EndToEnd",d)):(h=t.childNodes,h.length?(n>=h.length?i.insertAfter(a,h[h.length-1]):t.insertBefore(a,h[n]),d.moveToElementText(a)):t.canHaveHTML&&(t.innerHTML="",a=t.firstChild,d.moveToElementText(a),d.collapse(o)),r.setEndPoint(e?"StartToStart":"EndToEnd",d),i.remove(a))}var r,a,s,l,c,u,d,f=e.dom.doc,p=f.body,h,m;if(s=t.startContainer,l=t.startOffset,c=t.endContainer,u=t.endOffset,r=p.createTextRange(),s==c&&1==s.nodeType){if(l==u&&!s.hasChildNodes()){if(s.canHaveHTML)return d=s.previousSibling,d&&!d.hasChildNodes()&&i.isBlock(d)?d.innerHTML="":d=null,s.innerHTML="",r.moveToElementText(s.lastChild),r.select(),i.doc.selection.clear(),s.innerHTML="",void(d&&(d.innerHTML=""));l=i.nodeIndex(s),s=s.parentNode}if(l==u-1)try{if(m=s.childNodes[l],a=p.createControlRange(),a.addElement(m),a.select(),h=e.getRng(),h.item&&m===h.item(0))return}catch(g){}}n(!0),n(),r.select()},this.getRangeAt=n}return e}),r(B,[u],function(e){return{BACKSPACE:8,DELETE:46,DOWN:40,ENTER:13,LEFT:37,RIGHT:39,SPACEBAR:32,TAB:9,UP:38,modifierPressed:function(e){return e.shiftKey||e.ctrlKey||e.altKey||this.metaKeyPressed(e)},metaKeyPressed:function(t){return e.mac?t.metaKey:t.ctrlKey&&!t.altKey}}}),r(D,[B,d,u],function(e,t,n){return function(r,i){function o(e){var t=i.settings.object_resizing;return t===!1||n.iOS?!1:("string"!=typeof t&&(t="table,img,div"),"false"===e.getAttribute("data-mce-resize")?!1:i.dom.is(e,t))}function a(t){var n,r,o,a,s;n=t.screenX-T,r=t.screenY-R,P=n*k[2]+D,O=r*k[3]+L,P=5>P?5:P,O=5>O?5:O,o="IMG"==w.nodeName&&i.settings.resize_img_proportional!==!1?!e.modifierPressed(t):e.modifierPressed(t)||"IMG"==w.nodeName&&k[2]*k[3]!==0,o&&(W(n)>W(r)?(O=V(P*H),P=V(O/H)):(P=V(O/H),O=V(P*H))),C.setStyles(_,{width:P,height:O}),a=k.startPos.x+n,s=k.startPos.y+r,a=a>0?a:0,s=s>0?s:0,C.setStyles(E,{left:a,top:s,display:"block"}),E.innerHTML=P+" × "+O,k[2]<0&&_.clientWidth<=P&&C.setStyle(_,"left",A+(D-P)),k[3]<0&&_.clientHeight<=O&&C.setStyle(_,"top",B+(L-O)),n=U.scrollWidth-$,r=U.scrollHeight-q,n+r!==0&&C.setStyles(E,{left:a-n,top:s-r}),M||(i.fire("ObjectResizeStart",{target:w,width:D,height:L}),M=!0)}function s(){function e(e,t){t&&(w.style[e]||!i.schema.isValid(w.nodeName.toLowerCase(),e)?C.setStyle(w,e,t):C.setAttrib(w,e,t))}M=!1,e("width",P),e("height",O),C.unbind(I,"mousemove",a),C.unbind(I,"mouseup",s),F!=I&&(C.unbind(F,"mousemove",a),C.unbind(F,"mouseup",s)),C.remove(_),C.remove(E),z&&"TABLE"!=w.nodeName||l(w),i.fire("ObjectResized",{target:w,width:P,height:O}),C.setAttrib(w,"style",C.getAttrib(w,"style")),i.nodeChanged()}function l(e,t,r){var l,u,d,f,p;g(),l=C.getPos(e,U),A=l.x,B=l.y,p=e.getBoundingClientRect(),u=p.width||p.right-p.left,d=p.height||p.bottom-p.top,w!=e&&(m(),w=e,P=O=0),f=i.fire("ObjectSelected",{target:e}),o(e)&&!f.isDefaultPrevented()?x(N,function(e,i){function o(t){T=t.screenX,R=t.screenY,D=w.clientWidth,L=w.clientHeight,H=L/D,k=e,e.startPos={x:u*e[0]+A,y:d*e[1]+B},$=U.scrollWidth,q=U.scrollHeight,_=w.cloneNode(!0),C.addClass(_,"mce-clonedresizable"),C.setAttrib(_,"data-mce-bogus","all"),_.contentEditable=!1,_.unSelectabe=!0,C.setStyles(_,{left:A,top:B,margin:0}),_.removeAttribute("data-mce-selected"),U.appendChild(_),C.bind(I,"mousemove",a),C.bind(I,"mouseup",s),F!=I&&(C.bind(F,"mousemove",a),C.bind(F,"mouseup",s)),E=C.add(U,"div",{"class":"mce-resize-helper","data-mce-bogus":"all"},D+" × "+L)}var l,c;return t?void(i==t&&o(r)):(l=C.get("mceResizeHandle"+i),l?C.show(l):(c=U,l=C.add(c,"div",{id:"mceResizeHandle"+i,"data-mce-bogus":"all","class":"mce-resizehandle",unselectable:!0,style:"cursor:"+i+"-resize; margin:0; padding:0"}),n.ie&&(l.contentEditable=!1)),e.elm||(C.bind(l,"mousedown",function(e){e.stopImmediatePropagation(),e.preventDefault(),o(e)}),e.elm=l),void C.setStyles(l,{left:u*e[0]+A-l.offsetWidth/2,top:d*e[1]+B-l.offsetHeight/2}))}):c(),w.setAttribute("data-mce-selected","1")}function c(){var e,t;g(),w&&w.removeAttribute("data-mce-selected");for(e in N)t=C.get("mceResizeHandle"+e),t&&(C.unbind(t),C.remove(t))}function u(e){function t(e,t){if(e)do if(e===t)return!0;while(e=e.parentNode)}var n,i;if(!M)return x(C.select("img[data-mce-selected],hr[data-mce-selected]"),function(e){e.removeAttribute("data-mce-selected")}),i="mousedown"==e.type?e.target:r.getNode(),i=C.$(i).closest(z?"table":"table,img,hr")[0],t(i,U)&&(v(),n=r.getStart(!0),t(n,i)&&t(r.getEnd(!0),i)&&(!z||i!=n&&"IMG"!==n.nodeName))?void l(i):void c()}function d(e,t,n){e&&e.attachEvent&&e.attachEvent("on"+t,n)}function f(e,t,n){e&&e.detachEvent&&e.detachEvent("on"+t,n)}function p(e){var t=e.srcElement,n,r,o,a,s,c,u;n=t.getBoundingClientRect(),c=S.clientX-n.left,u=S.clientY-n.top;for(r in N)if(o=N[r],a=t.offsetWidth*o[0],s=t.offsetHeight*o[1],W(a-c)<8&&W(s-u)<8){k=o;break}M=!0,i.fire("ObjectResizeStart",{target:w,width:w.clientWidth,height:w.clientHeight}),i.getDoc().selection.empty(),l(t,r,S)}function h(e){var t=e.srcElement;if(t!=w){if(i.fire("ObjectSelected",{target:t}),m(),0===t.id.indexOf("mceResizeHandle"))return void(e.returnValue=!1);("IMG"==t.nodeName||"TABLE"==t.nodeName)&&(c(),w=t,d(t,"resizestart",p))}}function m(){f(w,"resizestart",p)}function g(){for(var e in N){var t=N[e];t.elm&&(C.unbind(t.elm),delete t.elm)}}function v(){try{i.getDoc().execCommand("enableObjectResizing",!1,!1)}catch(e){}}function y(e){var t;if(z){t=I.body.createControlRange();try{return t.addElement(e),t.select(),!0}catch(n){}}}function b(){w=_=null,z&&(m(),f(U,"controlselect",h))}var C=i.dom,x=t.each,w,_,E,N,k,S,T,R,A,B,D,L,H,M,P,O,I=i.getDoc(),F=document,z=n.ie&&n.ie<11,W=Math.abs,V=Math.round,U=i.getBody(),$,q;N={n:[.5,0,0,-1],e:[1,.5,1,0],s:[.5,1,0,1],w:[0,.5,-1,0],nw:[0,0,-1,-1],ne:[1,0,1,-1],se:[1,1,1,1],sw:[0,1,-1,1]};var j=".mce-content-body";return i.contentStyles.push(j+" div.mce-resizehandle {position: absolute;border: 1px solid black;background: #FFF;width: 5px;height: 5px;z-index: 10000}"+j+" .mce-resizehandle:hover {background: #000}"+j+" img[data-mce-selected], hr[data-mce-selected] {outline: 1px solid black;resize: none}"+j+" .mce-clonedresizable {position: absolute;"+(n.gecko?"":"outline: 1px dashed black;")+"opacity: .5;filter: alpha(opacity=50);z-index: 10000}"+j+" .mce-resize-helper {background: #555;background: rgba(0,0,0,0.75);border-radius: 3px;border: 1px;color: white;display: none;font-family: sans-serif;font-size: 12px;white-space: nowrap;line-height: 14px;margin: 5px 10px;padding: 5px;position: absolute;z-index: 10001}"),i.on("init",function(){z?(i.on("ObjectResized",function(e){"TABLE"!=e.target.nodeName&&(c(),y(e.target))}),d(U,"controlselect",h),i.on("mousedown",function(e){S=e})):(v(),n.ie>=11&&(i.on("mouseup",function(e){var t=e.target.nodeName;!M&&/^(TABLE|IMG|HR)$/.test(t)&&(i.selection.select(e.target,"TABLE"==t),i.nodeChanged())}),i.dom.bind(U,"mscontrolselect",function(e){/^(TABLE|IMG|HR)$/.test(e.target.nodeName)&&(e.preventDefault(),"IMG"==e.target.tagName&&window.setTimeout(function(){i.selection.select(e.target)},0))}))),i.on("nodechange ResizeEditor",u),i.on("keydown keyup",function(e){w&&"TABLE"==w.nodeName&&u(e)}),i.on("hide",c)}),i.on("remove",g),{isResizable:o,showResizeRect:l,hideResizeRect:c,updateResizeRect:u,controlSelect:y,destroy:b}}}),r(L,[u,d],function(e,t){function n(n){var r=n.dom;this.getBookmark=function(e,i){function o(e,n){var i=0;return t.each(r.select(e),function(e,t){e==n&&(i=t)}),i}function a(e){function t(t){var n,r,i,o=t?"start":"end";n=e[o+"Container"],r=e[o+"Offset"],1==n.nodeType&&"TR"==n.nodeName&&(i=n.childNodes,n=i[Math.min(t?r:r-1,i.length-1)],n&&(r=t?0:n.childNodes.length,e["set"+(t?"Start":"End")](n,r)))}return t(!0),t(),e}function s(){function e(e,t){var n=e[t?"startContainer":"endContainer"],a=e[t?"startOffset":"endOffset"],s=[],l,c,u=0;if(3==n.nodeType){if(i)for(l=n.previousSibling;l&&3==l.nodeType;l=l.previousSibling)a+=l.nodeValue.length;s.push(a)}else c=n.childNodes,a>=c.length&&c.length&&(u=1,a=Math.max(0,c.length-1)),s.push(r.nodeIndex(c[a],i)+u);for(;n&&n!=o;n=n.parentNode)s.push(r.nodeIndex(n,i));return s}var t=n.getRng(!0),o=r.getRoot(),a={};return a.start=e(t,!0),n.isCollapsed()||(a.end=e(t)),a}var l,c,u,d,f,p,h="",m;if(2==e)return p=n.getNode(),f=p?p.nodeName:null,"IMG"==f?{name:f,index:o(f,p)}:n.tridentSel?n.tridentSel.getBookmark(e):s();if(e)return{rng:n.getRng()};if(l=n.getRng(),u=r.uniqueId(),d=n.isCollapsed(),m="overflow:hidden;line-height:0px",l.duplicate||l.item){if(l.item)return p=l.item(0),f=p.nodeName,{name:f,index:o(f,p)};c=l.duplicate();try{l.collapse(),l.pasteHTML(''+h+""),d||(c.collapse(!1),l.moveToElementText(c.parentElement()),0===l.compareEndPoints("StartToEnd",c)&&c.move("character",-1),c.pasteHTML(''+h+""))}catch(g){return null}}else{if(p=n.getNode(),f=p.nodeName,"IMG"==f)return{name:f,index:o(f,p)};c=a(l.cloneRange()),d||(c.collapse(!1),c.insertNode(r.create("span",{"data-mce-type":"bookmark",id:u+"_end",style:m},h))),l=a(l),l.collapse(!0),l.insertNode(r.create("span",{"data-mce-type":"bookmark",id:u+"_start",style:m},h))}return n.moveToBookmark({id:u,keep:1}),{id:u}},this.moveToBookmark=function(i){function o(e){var t=i[e?"start":"end"],n,r,o,a;if(t){for(o=t[0],r=c,n=t.length-1;n>=1;n--){if(a=r.childNodes,t[n]>a.length-1)return;r=a[t[n]]}3===r.nodeType&&(o=Math.min(t[0],r.nodeValue.length)),1===r.nodeType&&(o=Math.min(t[0],r.childNodes.length)),e?l.setStart(r,o):l.setEnd(r,o)}return!0}function a(n){var o=r.get(i.id+"_"+n),a,s,l,c,h=i.keep;if(o&&(a=o.parentNode,"start"==n?(h?(a=o.firstChild,s=1):s=r.nodeIndex(o),u=d=a,f=p=s):(h?(a=o.firstChild,s=1):s=r.nodeIndex(o),d=a,p=s),!h)){for(c=o.previousSibling,l=o.nextSibling,t.each(t.grep(o.childNodes),function(e){3==e.nodeType&&(e.nodeValue=e.nodeValue.replace(/\uFEFF/g,""))});o=r.get(i.id+"_"+n);)r.remove(o,1);c&&l&&c.nodeType==l.nodeType&&3==c.nodeType&&!e.opera&&(s=c.nodeValue.length,c.appendData(l.nodeValue),r.remove(l),"start"==n?(u=d=c,f=p=s):(d=c,p=s))}}function s(t){return!r.isBlock(t)||t.innerHTML||e.ie||(t.innerHTML='
        '),t}var l,c,u,d,f,p;if(i)if(i.start){if(l=r.createRng(),c=r.getRoot(),n.tridentSel)return n.tridentSel.moveToBookmark(i);o(!0)&&o()&&n.setRng(l)}else i.id?(a("start"),a("end"),u&&(l=r.createRng(),l.setStart(s(u),f),l.setEnd(s(d),p),n.setRng(l))):i.name?n.select(r.select(i.name)[i.index]):i.rng&&n.setRng(i.rng)}}return n.isBookmarkNode=function(e){return e&&"SPAN"===e.tagName&&"bookmark"===e.getAttribute("data-mce-type")},n}),r(H,[h,A,D,x,L,u,d],function(e,n,r,i,o,a,s){function l(e,t,i,a){var s=this;s.dom=e,s.win=t,s.serializer=i,s.editor=a,s.bookmarkManager=new o(s),s.controlSelection=new r(s,a),s.win.getSelection||(s.tridentSel=new n(s))}var c=s.each,u=s.trim,d=a.ie;return l.prototype={setCursorLocation:function(e,t){var n=this,r=n.dom.createRng();e?(r.setStart(e,t),r.setEnd(e,t),n.setRng(r),n.collapse(!1)):(n._moveEndPoint(r,n.editor.getBody(),!0),n.setRng(r))},getContent:function(e){var n=this,r=n.getRng(),i=n.dom.create("body"),o=n.getSel(),a,s,l;return e=e||{},a=s="",e.get=!0,e.format=e.format||"html",e.selection=!0,n.editor.fire("BeforeGetContent",e),"text"==e.format?n.isCollapsed()?"":r.text||(o.toString?o.toString():""):(r.cloneContents?(l=r.cloneContents(),l&&i.appendChild(l)):r.item!==t||r.htmlText!==t?(i.innerHTML="
        "+(r.item?r.item(0).outerHTML:r.htmlText),i.removeChild(i.firstChild)):i.innerHTML=r.toString(),/^\s/.test(i.innerHTML)&&(a=" "),/\s+$/.test(i.innerHTML)&&(s=" "),e.getInner=!0,e.content=n.isCollapsed()?"":a+n.serializer.serialize(i,e)+s,n.editor.fire("GetContent",e),e.content)},setContent:function(e,t){var n=this,r=n.getRng(),i,o=n.win.document,a,s;if(t=t||{format:"html"},t.set=!0,t.selection=!0,e=t.content=e,t.no_events||n.editor.fire("BeforeSetContent",t),e=t.content,r.insertNode){e+='_',r.startContainer==o&&r.endContainer==o?o.body.innerHTML=e:(r.deleteContents(),0===o.body.childNodes.length?o.body.innerHTML=e:r.createContextualFragment?r.insertNode(r.createContextualFragment(e)):(a=o.createDocumentFragment(),s=o.createElement("div"),a.appendChild(s),s.outerHTML=e,r.insertNode(a))),i=n.dom.get("__caret"),r=o.createRange(),r.setStartBefore(i),r.setEndBefore(i),n.setRng(r),n.dom.remove("__caret");try{n.setRng(r)}catch(l){}}else r.item&&(o.execCommand("Delete",!1,null),r=n.getRng()),/^\s+/.test(e)?(r.pasteHTML('_'+e),n.dom.remove("__mce_tmp")):r.pasteHTML(e);t.no_events||n.editor.fire("SetContent",t)},getStart:function(e){var t=this,n=t.getRng(),r,i,o,a;if(n.duplicate||n.item){if(n.item)return n.item(0);for(o=n.duplicate(),o.collapse(1),r=o.parentElement(),r.ownerDocument!==t.dom.doc&&(r=t.dom.getRoot()),i=a=n.parentElement();a=a.parentNode;)if(a==r){r=i;break}return r}return r=n.startContainer,1==r.nodeType&&r.hasChildNodes()&&(e&&n.collapsed||(r=r.childNodes[Math.min(r.childNodes.length-1,n.startOffset)])),r&&3==r.nodeType?r.parentNode:r},getEnd:function(e){var t=this,n=t.getRng(),r,i;return n.duplicate||n.item?n.item?n.item(0):(n=n.duplicate(),n.collapse(0),r=n.parentElement(),r.ownerDocument!==t.dom.doc&&(r=t.dom.getRoot()),r&&"BODY"==r.nodeName?r.lastChild||r:r):(r=n.endContainer,i=n.endOffset,1==r.nodeType&&r.hasChildNodes()&&(e&&n.collapsed||(r=r.childNodes[i>0?i-1:i])),r&&3==r.nodeType?r.parentNode:r)},getBookmark:function(e,t){return this.bookmarkManager.getBookmark(e,t)},moveToBookmark:function(e){return this.bookmarkManager.moveToBookmark(e)},select:function(e,t){var n=this,r=n.dom,i=r.createRng(),o;if(n.lastFocusBookmark=null,e){if(!t&&n.controlSelection.controlSelect(e))return;o=r.nodeIndex(e),i.setStart(e.parentNode,o),i.setEnd(e.parentNode,o+1),t&&(n._moveEndPoint(i,e,!0),n._moveEndPoint(i,e)),n.setRng(i)}return e},isCollapsed:function(){var e=this,t=e.getRng(),n=e.getSel();return!t||t.item?!1:t.compareEndPoints?0===t.compareEndPoints("StartToEnd",t):!n||t.collapsed},collapse:function(e){var t=this,n=t.getRng(),r;n.item&&(r=n.item(0),n=t.win.document.body.createTextRange(),n.moveToElementText(r)),n.collapse(!!e),t.setRng(n)},getSel:function(){var e=this.win;return e.getSelection?e.getSelection():e.document.selection},getRng:function(e){function t(e,t,n){try{return t.compareBoundaryPoints(e,n)}catch(r){return-1}}var n=this,r,i,o,a=n.win.document,s;if(!e&&n.lastFocusBookmark){var l=n.lastFocusBookmark;return l.startContainer?(i=a.createRange(),i.setStart(l.startContainer,l.startOffset),i.setEnd(l.endContainer,l.endOffset)):i=l,i}if(e&&n.tridentSel)return n.tridentSel.getRangeAt(0);try{(r=n.getSel())&&(i=r.rangeCount>0?r.getRangeAt(0):r.createRange?r.createRange():a.createRange())}catch(c){}if(d&&i&&i.setStart&&a.selection){try{s=a.selection.createRange()}catch(c){}s&&s.item&&(o=s.item(0),i=a.createRange(),i.setStartBefore(o),i.setEndAfter(o))}return i||(i=a.createRange?a.createRange():a.body.createTextRange()),i.setStart&&9===i.startContainer.nodeType&&i.collapsed&&(o=n.dom.getRoot(),i.setStart(o,0),i.setEnd(o,0)),n.selectedRange&&n.explicitRange&&(0===t(i.START_TO_START,i,n.selectedRange)&&0===t(i.END_TO_END,i,n.selectedRange)?i=n.explicitRange:(n.selectedRange=null,n.explicitRange=null)),i},setRng:function(e,t){var n=this,r;if(e)if(e.select)try{e.select()}catch(i){}else if(n.tridentSel){if(e.cloneRange)try{return void n.tridentSel.addRange(e)}catch(i){}}else if(r=n.getSel()){n.explicitRange=e;try{r.removeAllRanges(),r.addRange(e)}catch(i){}t===!1&&r.extend&&(r.collapse(e.endContainer,e.endOffset),r.extend(e.startContainer,e.startOffset)),n.selectedRange=r.rangeCount>0?r.getRangeAt(0):null}},setNode:function(e){var t=this;return t.setContent(t.dom.getOuterHTML(e)),e},getNode:function(){function e(e,t){for(var n=e;e&&3===e.nodeType&&0===e.length;)e=t?e.nextSibling:e.previousSibling;return e||n}var t=this,n=t.getRng(),r,i=n.startContainer,o=n.endContainer,a=n.startOffset,s=n.endOffset,l=t.dom.getRoot();return n?n.setStart?(r=n.commonAncestorContainer,!n.collapsed&&(i==o&&2>s-a&&i.hasChildNodes()&&(r=i.childNodes[a]),3===i.nodeType&&3===o.nodeType&&(i=i.length===a?e(i.nextSibling,!0):i.parentNode,o=0===s?e(o.previousSibling,!1):o.parentNode,i&&i===o))?i:r&&3==r.nodeType?r.parentNode:r):(r=n.item?n.item(0):n.parentElement(),r.ownerDocument!==t.win.document&&(r=l),r):l},getSelectedBlocks:function(t,n){var r=this,i=r.dom,o,a,s=[];if(a=i.getRoot(),t=i.getParent(t||r.getStart(),i.isBlock),n=i.getParent(n||r.getEnd(),i.isBlock),t&&t!=a&&s.push(t),t&&n&&t!=n){o=t;for(var l=new e(t,a);(o=l.next())&&o!=n;)i.isBlock(o)&&s.push(o)}return n&&t!=n&&n!=a&&s.push(n),s},isForward:function(){var e=this.dom,t=this.getSel(),n,r;return t&&t.anchorNode&&t.focusNode?(n=e.createRng(),n.setStart(t.anchorNode,t.anchorOffset),n.collapse(!0),r=e.createRng(),r.setStart(t.focusNode,t.focusOffset),r.collapse(!0),n.compareBoundaryPoints(n.START_TO_START,r)<=0):!0},normalize:function(){var e=this,t=e.getRng();return!d&&new i(e.dom).normalize(t)&&e.setRng(t,e.isForward()),t},selectorChanged:function(e,t){var n=this,r;return n.selectorChangedData||(n.selectorChangedData={},r={},n.editor.on("NodeChange",function(e){var t=e.element,i=n.dom,o=i.getParents(t,null,i.getRoot()),a={};c(n.selectorChangedData,function(e,t){c(o,function(n){return i.is(n,t)?(r[t]||(c(e,function(e){e(!0,{node:n,selector:t,parents:o})}),r[t]=e),a[t]=e,!1):void 0})}),c(r,function(e,n){a[n]||(delete r[n],c(e,function(e){e(!1,{node:t,selector:n,parents:o})}))})})),n.selectorChangedData[e]||(n.selectorChangedData[e]=[]),n.selectorChangedData[e].push(t),n},getScrollContainer:function(){for(var e,t=this.dom.getRoot();t&&"BODY"!=t.nodeName;){if(t.scrollHeight>t.clientHeight){e=t;break}t=t.parentNode}return e},scrollIntoView:function(e){function t(e){for(var t=0,n=0,r=e;r&&r.nodeType;)t+=r.offsetLeft||0,n+=r.offsetTop||0,r=r.offsetParent;return{x:t,y:n}}var n,r,i=this,o=i.dom,a=o.getRoot(),s,l;if("BODY"!=a.nodeName){var c=i.getScrollContainer();if(c)return n=t(e).y-t(c).y,l=c.clientHeight,s=c.scrollTop,void((s>n||n+25>s+l)&&(c.scrollTop=s>n?n:n-l+25))}r=o.getViewPort(i.editor.getWin()),n=o.getPos(e).y,s=r.y,l=r.h,(ns+l)&&i.editor.getWin().scrollTo(0,s>n?n:n-l+25)},placeCaretAt:function(e,t){var n=this.editor.getDoc(),r,i;if(n.caretPositionFromPoint)i=n.caretPositionFromPoint(e,t),r=n.createRange(),r.setStart(i.offsetNode,i.offset),r.collapse(!0);else if(n.caretRangeFromPoint)r=n.caretRangeFromPoint(e,t);else if(n.body.createTextRange){r=n.body.createTextRange();try{r.moveToPoint(e,t),r.collapse(!0)}catch(o){r.collapse(t=e;e++)a.addShortcut("ctrl+"+e,"",["FormatBlock",!1,"h"+e]);a.addShortcut("ctrl+7","",["FormatBlock",!1,"p"]),a.addShortcut("ctrl+8","",["FormatBlock",!1,"div"]),a.addShortcut("ctrl+9","",["FormatBlock",!1,"address"])}function f(e){return e?V[e]:V}function p(e,t){e&&("string"!=typeof e?at(e,function(e,t){p(t,e)}):(t=t.length?t:[t],at(t,function(e){e.deep===tt&&(e.deep=!e.selector),e.split===tt&&(e.split=!e.selector||e.inline),e.remove===tt&&e.selector&&!e.inline&&(e.remove="none"),e.selector&&e.inline&&(e.mixed=!0,e.block_expand=!0),"string"==typeof e.classes&&(e.classes=e.classes.split(/\s+/))}),V[e]=t))}function h(e){return e&&V[e]&&delete V[e],V}function m(e){var t;return a.dom.getParent(e,function(e){return t=a.dom.getStyle(e,"text-decoration"),t&&"none"!==t}),t}function g(e){var t;1===e.nodeType&&e.parentNode&&1===e.parentNode.nodeType&&(t=m(e.parentNode),a.dom.getStyle(e,"color")&&t?a.dom.setStyle(e,"text-decoration",t):a.dom.getStyle(e,"textdecoration")===t&&a.dom.setStyle(e,"text-decoration",null))}function v(t,n,r){function i(e,t){if(t=t||d,e){if(t.onformat&&t.onformat(e,t,n,r),at(t.styles,function(t,r){U.setStyle(e,r,A(t,n))}),t.styles){var i=U.getAttrib(e,"style");i&&e.setAttribute("data-mce-style",i)}at(t.attributes,function(t,r){U.setAttrib(e,r,A(t,n))}),at(t.classes,function(t){t=A(t,n),U.hasClass(e,t)||U.addClass(e,t)})}}function o(){function t(t,n){var i=new e(n);for(r=i.current();r;r=i.prev())if(r.childNodes.length>1||r==t||"BR"==r.tagName)return r}var n=a.selection.getRng(),i=n.startContainer,o=n.endContainer;if(i!=o&&0===n.endOffset){var s=t(i,o),l=3==s.nodeType?s.length:s.childNodes.length;n.setEnd(s,l)}return n}function l(e,r,o){var a=[],l,f,p=!0;l=d.inline||d.block,f=U.create(l),i(f),q.walk(e,function(e){function r(e){var g,v,y,b,x;return x=p,g=e.nodeName.toLowerCase(),v=e.parentNode.nodeName.toLowerCase(),1===e.nodeType&&nt(e)&&(x=p,p="true"===nt(e),b=!0),S(g,"br")?(h=0,void(d.block&&U.remove(e))):d.wrapper&&C(e,t,n)?void(h=0):p&&!b&&d.block&&!d.wrapper&&s(g)&&j(v,l)?(e=U.rename(e,l),i(e),a.push(e),void(h=0)):d.selector&&(at(u,function(t){"collapsed"in t&&t.collapsed!==m||U.is(e,t.selector)&&!c(e)&&(i(e,t),y=!0)}),!d.inline||y)?void(h=0):void(!p||b||!j(l,g)||!j(v,l)||!o&&3===e.nodeType&&1===e.nodeValue.length&&65279===e.nodeValue.charCodeAt(0)||c(e)||d.inline&&Y(e)?(h=0,at(st(e.childNodes),r),b&&(p=x),h=0):(h||(h=U.clone(f,Q),e.parentNode.insertBefore(h,e),a.push(h)),h.appendChild(e)))}var h;at(e,r)}),d.links===!0&&at(a,function(e){function t(e){"A"===e.nodeName&&i(e,d),at(st(e.childNodes),t)}t(e)}),at(a,function(e){function r(e){var t=0;return at(e.childNodes,function(e){B(e)||ot(e)||t++}),t}function o(e){var t,n;return at(e.childNodes,function(e){return 1!=e.nodeType||ot(e)||c(e)?void 0:(t=e,Q)}),t&&!ot(t)&&k(t,d)&&(n=U.clone(t,Q),i(n),U.replace(n,e,Z),U.remove(t,1)),n||e}var s;if(s=r(e),(a.length>1||!Y(e))&&0===s)return void U.remove(e,1);if(d.inline||d.wrapper){if(d.exact||1!==s||(e=o(e)),at(u,function(t){at(U.select(t.inline,e),function(e){ot(e)||M(t,n,e,t.exact?e:null)})}),C(e.parentNode,t,n))return U.remove(e,1),e=0,Z;d.merge_with_parents&&U.getParent(e.parentNode,function(r){return C(r,t,n)?(U.remove(e,1),e=0,Z):void 0}),e&&d.merge_siblings!==!1&&(e=I(O(e),e),e=I(e,O(e,Z)))}})}var u=f(t),d=u[0],p,h,m=!r&&$.isCollapsed();if(d)if(r)r.nodeType?(h=U.createRng(),h.setStartBefore(r),h.setEndAfter(r),l(L(h,u),null,!0)):l(r,null,!0);else if(m&&d.inline&&!U.select("td.mce-item-selected,th.mce-item-selected").length)z("apply",t,n);else{var y=a.selection.getNode();K||!u[0].defaultBlock||U.getParent(y,U.isBlock)||v(u[0].defaultBlock),a.selection.setRng(o()),p=$.getBookmark(),l(L($.getRng(Z),u),p),d.styles&&(d.styles.color||d.styles.textDecoration)&&(lt(y,g,"childNodes"),g(y)),$.moveToBookmark(p),W($.getRng(Z)),a.nodeChanged()}}function y(e,t,n,r){function i(e){var n,r,o,a,s;if(1===e.nodeType&&nt(e)&&(a=y,y="true"===nt(e),s=!0),n=st(e.childNodes),y&&!s)for(r=0,o=p.length;o>r&&!M(p[r],t,e,e);r++);if(h.deep&&n.length){for(r=0,o=n.length;o>r;r++)i(n[r]);s&&(y=a)}}function o(n){var i;return at(l(n.parentNode).reverse(),function(n){var o;i||"_start"==n.id||"_end"==n.id||(o=C(n,e,t,r),o&&o.split!==!1&&(i=n))}),i}function s(e,n,r,i){var o,a,s,l,c,u;if(e){for(u=e.parentNode,o=n.parentNode;o&&o!=u;o=o.parentNode){for(a=U.clone(o,Q),c=0;c=0;o--){if(a=t[o].selector,!a||t[o].defaultBlock)return Z;for(i=r.length-1;i>=0;i--)if(U.is(r[i],a))return Z}return Q}function E(e,t,n){var r;return et||(et={},r={},a.on("NodeChange",function(e){var t=l(e.element),n={};t=i.grep(t,function(e){return 1==e.nodeType&&!e.getAttribute("data-mce-bogus")}),at(et,function(e,i){at(t,function(o){return C(o,i,{},e.similar)?(r[i]||(at(e,function(e){e(!0,{node:o,format:i,parents:t})}),r[i]=e),n[i]=e,!1):void 0})}),at(r,function(i,o){n[o]||(delete r[o],at(i,function(n){n(!1,{node:e.element,format:o,parents:t})}))})})),at(e.split(","),function(e){et[e]||(et[e]=[],et[e].similar=n),et[e].push(t)}),this}function N(e){return o.getCssText(a,e)}function k(e,t){return S(e,t.inline)?Z:S(e,t.block)?Z:t.selector?1==e.nodeType&&U.is(e,t.selector):void 0}function S(e,t){return e=e||"",t=t||"",e=""+(e.nodeName||e),t=""+(t.nodeName||t),e.toLowerCase()==t.toLowerCase()}function T(e,t){return R(U.getStyle(e,t),t)}function R(e,t){return("color"==t||"backgroundColor"==t)&&(e=U.toHex(e)),"fontWeight"==t&&700==e&&(e="bold"),"fontFamily"==t&&(e=e.replace(/[\'\"]/g,"").replace(/,\s+/g,",")),""+e}function A(e,t){return"string"!=typeof e?e=e(t):t&&(e=e.replace(/%(\w+)/g,function(e,n){return t[n]||e})),e}function B(e){return e&&3===e.nodeType&&/^([\t \r\n]+|)$/.test(e.nodeValue)}function D(e,t,n){var r=U.create(t,n);return e.parentNode.insertBefore(r,e),r.appendChild(e),r}function L(t,n,r){function i(e){function t(e){return"BR"==e.nodeName&&e.getAttribute("data-mce-bogus")&&!e.nextSibling}var r,i,o,a,s;if(r=i=e?g:y,a=e?"previousSibling":"nextSibling",s=U.getRoot(),3==r.nodeType&&!B(r)&&(e?v>0:bo?n:o,-1===n||r||n++):(n=a.indexOf(" ",t),o=a.indexOf("\xa0",t),n=-1!==n&&(-1===o||o>n)?n:o),n}var s,l,c,u;if(3===t.nodeType){if(c=o(t,n),-1!==c)return{container:t,offset:c};u=t}for(s=new e(t,U.getParent(t,Y)||a.getBody());l=s[i?"prev":"next"]();)if(3===l.nodeType){if(u=l,c=o(l),-1!==c)return{container:l,offset:c}}else if(Y(l))break;return u?(n=i?0:u.length,{container:u,offset:n}):void 0}function d(e,r){var i,o,a,s;for(3==e.nodeType&&0===e.nodeValue.length&&e[r]&&(e=e[r]),i=l(e),o=0;op?p:v],3==g.nodeType&&(v=0)),1==y.nodeType&&y.hasChildNodes()&&(p=y.childNodes.length-1,y=y.childNodes[b>p?p:b-1],3==y.nodeType&&(b=y.nodeValue.length)),g=c(g),y=c(y),(ot(g.parentNode)||ot(g))&&(g=ot(g)?g:g.parentNode,g=g.nextSibling||g,3==g.nodeType&&(v=0)),(ot(y.parentNode)||ot(y))&&(y=ot(y)?y:y.parentNode,y=y.previousSibling||y,3==y.nodeType&&(b=y.length)),n[0].inline&&(t.collapsed&&(m=u(g,v,!0),m&&(g=m.container,v=m.offset),m=u(y,b),m&&(y=m.container,b=m.offset)),h=o(y,b),h.node)){for(;h.node&&0===h.offset&&h.node.previousSibling;)h=o(h.node.previousSibling);h.node&&h.offset>0&&3===h.node.nodeType&&" "===h.node.nodeValue.charAt(h.offset-1)&&h.offset>1&&(y=h.node,y.splitText(h.offset-1))}return(n[0].inline||n[0].block_expand)&&(n[0].inline&&3==g.nodeType&&0!==v||(g=i(!0)),n[0].inline&&3==y.nodeType&&b!==y.nodeValue.length||(y=i())),n[0].selector&&n[0].expand!==Q&&!n[0].inline&&(g=d(g,"previousSibling"),y=d(y,"nextSibling")),(n[0].block||n[0].selector)&&(g=f(g,"previousSibling"),y=f(y,"nextSibling"),n[0].block&&(Y(g)||(g=i(!0)),Y(y)||(y=i()))),1==g.nodeType&&(v=G(g),g=g.parentNode),1==y.nodeType&&(b=G(y)+1,y=y.parentNode),{startContainer:g,startOffset:v,endContainer:y,endOffset:b}}function H(e,t){return t.links&&"A"==e.tagName}function M(e,t,n,r){var i,o,a;if(!k(n,e)&&!H(n,e))return Q;if("all"!=e.remove)for(at(e.styles,function(i,o){i=R(A(i,t),o),"number"==typeof o&&(o=i,r=0),(e.remove_similar||!r||S(T(r,o),i))&&U.setStyle(n,o,""),a=1}),a&&""===U.getAttrib(n,"style")&&(n.removeAttribute("style"),n.removeAttribute("data-mce-style")),at(e.attributes,function(e,i){var o;if(e=A(e,t),"number"==typeof i&&(i=e,r=0),!r||S(U.getAttrib(r,i),e)){if("class"==i&&(e=U.getAttrib(n,i),e&&(o="",at(e.split(/\s+/),function(e){/mce\w+/.test(e)&&(o+=(o?" ":"")+e)}),o)))return void U.setAttrib(n,i,o);"class"==i&&n.removeAttribute("className"),J.test(i)&&n.removeAttribute("data-mce-"+i),n.removeAttribute(i)}}),at(e.classes,function(e){e=A(e,t),(!r||U.hasClass(r,e))&&U.removeClass(n,e)}),o=U.getAttribs(n),i=0;io?o:i]),3===r.nodeType&&n&&i>=r.nodeValue.length&&(r=new e(r,a.getBody()).next()||r),3!==r.nodeType||n||0!==i||(r=new e(r,a.getBody()).prev()||r),r}function z(t,n,r,i){function o(e){var t=U.create("span",{id:g,"data-mce-bogus":!0,style:b?"color:red":""});return e&&t.appendChild(a.getDoc().createTextNode(X)),t}function l(e,t){for(;e;){if(3===e.nodeType&&e.nodeValue!==X||e.childNodes.length>1)return!1;t&&1===e.nodeType&&t.push(e),e=e.firstChild}return!0}function c(e){for(;e;){if(e.id===g)return e;e=e.parentNode}}function u(t){var n;if(t)for(n=new e(t,t),t=n.current();t;t=n.next())if(3===t.nodeType)return t}function d(e,t){var n,r;if(e)r=$.getRng(!0),l(e)?(t!==!1&&(r.setStartBefore(e),r.setEndBefore(e)),U.remove(e)):(n=u(e),n.nodeValue.charAt(0)===X&&(n.deleteData(0,1),r.startContainer==n&&r.startOffset>0&&r.setStart(n,r.startOffset-1),r.endContainer==n&&r.endOffset>0&&r.setEnd(n,r.endOffset-1)),U.remove(e,1)),$.setRng(r);else if(e=c($.getStart()),!e)for(;e=U.get(g);)d(e,!1)}function p(){var e,t,i,a,s,l,d;e=$.getRng(!0),a=e.startOffset,l=e.startContainer,d=l.nodeValue,t=c($.getStart()),t&&(i=u(t)),d&&a>0&&a=0;h--)u.appendChild(U.clone(p[h],!1)),u=u.firstChild;u.appendChild(U.doc.createTextNode(X)),u=u.firstChild;var g=U.getParent(d,s);g&&U.isEmpty(g)?d.parentNode.replaceChild(m,d):U.insertAfter(m,d),$.setCursorLocation(u,1),U.isEmpty(d)&&U.remove(d)}}function m(){var e;e=c($.getStart()),e&&!U.isEmpty(e)&<(e,function(e){1!=e.nodeType||e.id===g||U.isEmpty(e)||U.setAttrib(e,"data-mce-bogus",null)},"childNodes")}var g="_mce_caret",b=a.settings.caret_debug;a._hasCaretEvents||(it=function(){var e=[],t;if(l(c($.getStart()),e))for(t=e.length;t--;)U.setAttrib(e[t],"data-mce-bogus","1")},rt=function(e){var t=e.keyCode;d(),(8==t||37==t||39==t)&&d(c($.getStart())),m()},a.on("SetContent",function(e){e.selection&&m()}),a._hasCaretEvents=!0),"apply"==t?p():h()}function W(t){var n=t.startContainer,r=t.startOffset,i,o,a,s,l;if(3==n.nodeType&&r>=n.nodeValue.length&&(r=G(n),n=n.parentNode,i=!0),1==n.nodeType)for(s=n.childNodes,n=s[Math.min(r,s.length-1)],o=new e(n,U.getParent(n,U.isBlock)),(r>s.length-1||i)&&o.next(),a=o.current();a;a=o.next())if(3==a.nodeType&&!B(a))return l=U.create("a",{"data-mce-bogus":"all"},X),a.parentNode.insertBefore(l,a),t.setStart(a,0),$.setRng(t),void U.remove(l)}var V={},U=a.dom,$=a.selection,q=new t(U),j=a.schema.isValidChild,Y=U.isBlock,K=a.settings.forced_root_block,G=U.nodeIndex,X="\ufeff",J=/^(src|href|style)$/,Q=!1,Z=!0,et,tt,nt=U.getContentEditable,rt,it,ot=n.isBookmarkNode,at=i.each,st=i.grep,lt=i.walk,ct=i.extend;ct(this,{get:f,register:p,unregister:h,apply:v,remove:y,toggle:b,match:x,matchAll:w,matchNode:C,canApply:_,formatChanged:E,getCssText:N}),u(),d(),a.on("BeforeGetContent",function(e){it&&"raw"!=e.format&&it()}),a.on("mouseup keydown",function(e){rt&&rt(e)})}}),r(I,[B,u,d,N],function(e,t,n,r){var i=n.trim,o;return o=new RegExp(["]+data-mce-bogus[^>]+>[\u200b\ufeff]+<\\/span>",'\\s?data-mce-selected="[^"]+"'].join("|"),"gi"),function(n){function a(){var e=n.getContent({format:"raw",no_events:1}),t=/<(\w+) [^>]*data-mce-bogus="all"[^>]*>/g,a,s,l,c,u,d=n.schema;for(e=e.replace(o,""),u=d.getShortEndedElements();c=t.exec(e);)s=t.lastIndex,l=c[0].length,a=u[c[1]]?s:r.findEndTag(d,e,s),e=e.substring(0,s-l)+e.substring(a),t.lastIndex=s-l;return i(e)}function s(e){l.typing=!1,l.add({},e)}var l=this,c=0,u=[],d,f,p=0;return n.on("init",function(){l.add()}),n.on("BeforeExecCommand",function(e){var t=e.command;"Undo"!=t&&"Redo"!=t&&"mceRepaint"!=t&&l.beforeChange()}),n.on("ExecCommand",function(e){var t=e.command;"Undo"!=t&&"Redo"!=t&&"mceRepaint"!=t&&s(e)}),n.on("ObjectResizeStart",function(){l.beforeChange()}),n.on("SaveContent ObjectResized blur",s),n.on("DragEnd",s),n.on("KeyUp",function(e){var r=e.keyCode;(r>=33&&36>=r||r>=37&&40>=r||45==r||13==r||e.ctrlKey)&&(s(),n.nodeChanged()),(46==r||8==r||t.mac&&(91==r||93==r))&&n.nodeChanged(),f&&l.typing&&(n.isDirty()||(n.isNotDirty=!u[0]||a()==u[0].content,n.isNotDirty||n.fire("change",{level:u[0],lastLevel:null})),n.fire("TypingUndo"),f=!1,n.nodeChanged())}),n.on("KeyDown",function(t){var n=t.keyCode;if(n>=33&&36>=n||n>=37&&40>=n||45==n)return void(l.typing&&s(t));var r=e.modifierPressed(t);!(16>n||n>20)||224==n||91==n||l.typing||r||(l.beforeChange(),l.typing=!0,l.add({},t),f=!0)}),n.on("MouseDown",function(e){l.typing&&s(e)}),n.addShortcut("ctrl+z","","Undo"),n.addShortcut("ctrl+y,ctrl+shift+z","","Redo"),n.on("AddUndo Undo Redo ClearUndos",function(e){e.isDefaultPrevented()||n.nodeChanged()}),l={data:u,typing:!1,beforeChange:function(){p||(d=n.selection.getBookmark(2,!0))},add:function(e,t){var r,i=n.settings,o;if(e=e||{},e.content=a(),p||n.removed)return null;if(o=u[c],n.fire("BeforeAddUndo",{level:e,lastLevel:o,originalEvent:t}).isDefaultPrevented())return null;if(o&&o.content==e.content)return null;if(u[c]&&(u[c].beforeBookmark=d),i.custom_undo_redo_levels&&u.length>i.custom_undo_redo_levels){for(r=0;r0&&(n.isNotDirty=!1,n.fire("change",s)),e},undo:function(){var e;return l.typing&&(l.add(),l.typing=!1),c>0&&(e=u[--c],0===c&&(n.isNotDirty=!0),n.setContent(e.content,{format:"raw"}),n.selection.moveToBookmark(e.beforeBookmark),n.fire("undo",{level:e})),e},redo:function(){var e;return c0||l.typing&&u[0]&&a()!=u[0].content},hasRedo:function(){return cB)&&(u=a.create("br"),t.parentNode.insertBefore(u,t)),l.setStartBefore(t),l.setEndBefore(t)):(l.setStartAfter(t),l.setEndAfter(t)):(l.setStart(t,0),l.setEnd(t,0));s.setRng(l),a.remove(u),s.scrollIntoView(t)}}function g(e){var t=l.forced_root_block;t&&t.toLowerCase()===e.tagName.toLowerCase()&&a.setAttribs(e,l.forced_root_block_attrs)}function v(e){var t=T,n,i,o,s=u.getTextInlineElements();if(e||"TABLE"==P?(n=a.create(e||I),g(n)):n=A.cloneNode(!1),o=n,l.keep_styles!==!1)do if(s[t.nodeName]){if("_mce_caret"==t.id)continue;i=t.cloneNode(!1),a.setAttrib(i,"id",""),n.hasChildNodes()?(i.appendChild(n.firstChild),n.appendChild(i)):(o=i,n.appendChild(i))}while(t=t.parentNode);return r||(o.innerHTML='
        '),n}function y(t){var n,r,i;if(3==T.nodeType&&(t?R>0:RT.childNodes.length-1,T=T.childNodes[Math.min(R,T.childNodes.length-1)]||T,R=F&&3==T.nodeType?T.nodeValue.length:0),S=_(T)){if(c.beforeChange(),!a.isBlock(S)&&S!=a.getRoot())return void((!I||D)&&x());if((I&&!D||!I&&D)&&(T=b(T,R)),A=a.getParent(T,a.isBlock),M=A?a.getParent(A.parentNode,a.isBlock):null,P=A?A.nodeName.toUpperCase():"",O=M?M.nodeName.toUpperCase():"","LI"!=O||o.ctrlKey||(A=M,P=O),/^(LI|DT|DD)$/.test(P)){if(!I&&D)return void x();if(a.isEmpty(A))return void C()}if("PRE"==P&&l.br_in_pre!==!1){if(!D)return void x()}else if(!I&&!D&&"LI"!=P||I&&D)return void x();I&&A===i.getBody()||(I=I||"P",y()?(L=/^(H[1-6]|PRE|FIGURE)$/.test(P)&&"HGROUP"!=O?v(I):v(),l.end_container_on_empty_block&&f(M)&&a.isEmpty(A)?L=a.split(M,A):a.insertAfter(L,A),m(L)):y(!0)?(L=A.parentNode.insertBefore(v(),A),p(L),m(A)):(k=N.cloneRange(),k.setEndAfter(A),H=k.extractContents(),w(H),L=H.firstChild,a.insertAfter(H,A),h(L),E(A),m(L)),a.setAttrib(L,"id",""),i.fire("NewBlock",{newBlock:L}),c.add())}}}var a=i.dom,s=i.selection,l=i.settings,c=i.undoManager,u=i.schema,d=u.getNonEmptyElements();i.on("keydown",function(e){13==e.keyCode&&o(e)!==!1&&e.preventDefault()})}}),r(z,[],function(){return function(e){function t(){var t=i.getStart(),s=e.getBody(),l,c,u,d,f,p,h,m=-16777215,g,v,y,b,C;if(C=n.forced_root_block,t&&1===t.nodeType&&C){for(;t&&t!=s;){if(a[t.nodeName])return;t=t.parentNode}if(l=i.getRng(),l.setStart){c=l.startContainer,u=l.startOffset,d=l.endContainer,f=l.endOffset;try{v=e.getDoc().activeElement===s}catch(x){}}else l.item&&(t=l.item(0),l=e.getDoc().body.createTextRange(),l.moveToElementText(t)),v=l.parentElement().ownerDocument===e.getDoc(),y=l.duplicate(),y.collapse(!0),u=-1*y.move("character",m),y.collapsed||(y=l.duplicate(),y.collapse(!1),f=-1*y.move("character",m)-u);for(t=s.firstChild,b=s.nodeName.toLowerCase();t;)if((3===t.nodeType||1==t.nodeType&&!a[t.nodeName])&&o.isValidChild(b,C.toLowerCase())){if(3===t.nodeType&&0===t.nodeValue.length){h=t,t=t.nextSibling,r.remove(h);continue}p||(p=r.create(C,e.settings.forced_root_block_attrs),t.parentNode.insertBefore(p,t),g=!0),h=t,t=t.nextSibling,p.appendChild(h)}else p=null,t=t.nextSibling;if(g&&v){if(l.setStart)l.setStart(c,u),l.setEnd(d,f),i.setRng(l);else try{l=e.getDoc().body.createTextRange(),l.moveToElementText(s),l.collapse(!0),l.moveStart("character",u),f>0&&l.moveEnd("character",f),l.select()}catch(x){}e.nodeChanged()}}}var n=e.settings,r=e.dom,i=e.selection,o=e.schema,a=o.getBlockElements();n.forced_root_block&&e.on("NodeChange",t)}}),r(W,[T,u,d,M,x,h],function(e,n,r,i,o,a){var s=r.each,l=r.extend,c=r.map,u=r.inArray,d=r.explode,f=n.gecko,p=n.ie,h=n.ie&&n.ie<11,m=!0,g=!1;return function(r){function v(e,t,n){var r;return e=e.toLowerCase(),(r=T.exec[e])?(r(e,t,n),m):g}function y(e){var t;return e=e.toLowerCase(),(t=T.state[e])?t(e):-1}function b(e){var t;return e=e.toLowerCase(),(t=T.value[e])?t(e):g}function C(e,t){t=t||"exec",s(e,function(e,n){s(n.toLowerCase().split(","),function(n){T[t][n]=e})})}function x(e,n,i){return n===t&&(n=g),i===t&&(i=null),r.getDoc().execCommand(e,n,i)}function w(e){return A.match(e)}function _(e,n){A.toggle(e,n?{value:n}:t),r.nodeChanged()}function E(e){B=S.getBookmark(e)}function N(){S.moveToBookmark(B)}var k=r.dom,S=r.selection,T={state:{},exec:{},value:{}},R=r.settings,A=r.formatter,B;l(this,{execCommand:v,queryCommandState:y,queryCommandValue:b,addCommands:C}),C({"mceResetDesignMode,mceBeginUndoLevel":function(){},"mceEndUndoLevel,mceAddUndoLevel":function(){r.undoManager.add()},"Cut,Copy,Paste":function(e){var t=r.getDoc(),i;try{x(e)}catch(o){i=m}if(i||!t.queryCommandSupported(e)){var a=r.translate("Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.");n.mac&&(a=a.replace(/Ctrl\+/g,"\u2318+")),r.windowManager.alert(a)}},unlink:function(){if(S.isCollapsed()){var e=S.getNode();return void("A"==e.tagName&&r.dom.remove(e,!0))}A.remove("link")},"JustifyLeft,JustifyCenter,JustifyRight,JustifyFull":function(e){var t=e.substring(7);"full"==t&&(t="justify"),s("left,center,right,justify".split(","),function(e){t!=e&&A.remove("align"+e)}),_("align"+t),v("mceRepaint") +},"InsertUnorderedList,InsertOrderedList":function(e){var t,n;x(e),t=k.getParent(S.getNode(),"ol,ul"),t&&(n=t.parentNode,/^(H[1-6]|P|ADDRESS|PRE)$/.test(n.nodeName)&&(E(),k.split(n,t),N()))},"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(e){_(e)},"ForeColor,HiliteColor,FontName":function(e,t,n){_(e,n)},FontSize:function(e,t,n){var r,i;n>=1&&7>=n&&(i=d(R.font_size_style_values),r=d(R.font_size_classes),n=r?r[n-1]||n:i[n-1]||n),_(e,n)},RemoveFormat:function(e){A.remove(e)},mceBlockQuote:function(){_("blockquote")},FormatBlock:function(e,t,n){return _(n||"p")},mceCleanup:function(){var e=S.getBookmark();r.setContent(r.getContent({cleanup:m}),{cleanup:m}),S.moveToBookmark(e)},mceRemoveNode:function(e,t,n){var i=n||S.getNode();i!=r.getBody()&&(E(),r.dom.remove(i,m),N())},mceSelectNodeDepth:function(e,t,n){var i=0;k.getParent(S.getNode(),function(e){return 1==e.nodeType&&i++==n?(S.select(e),g):void 0},r.getBody())},mceSelectNode:function(e,t,n){S.select(n)},mceInsertContent:function(t,n,o){function a(e){function t(e){return r[e]&&3==r[e].nodeType}var n,r,i;return n=S.getRng(!0),r=n.startContainer,i=n.startOffset,3==r.nodeType&&(i>0?e=e.replace(/^ /," "):t("previousSibling")||(e=e.replace(/^ /," ")),i|)$/," "):t("nextSibling")||(e=e.replace(/( | )(
        |)$/," "))),e}function l(e){if(w)for(b=e.firstChild;b;b=b.walk(!0))_[b.name]&&b.attr("data-mce-new","true")}function c(){if(w){var e=r.getBody(),t=new i(k);s(k.select("*[data-mce-new]"),function(n){n.removeAttribute("data-mce-new");for(var r=n.parentNode;r&&r!=e;r=r.parentNode)t.compare(r,n)&&k.remove(n,!0)})}}var u,d,f,h,m,g,v,y,b,C,x,w,_=r.schema.getTextInlineElements();"string"!=typeof o&&(w=o.merge,o=o.content),/^ | $/.test(o)&&(o=a(o)),u=r.parser,d=new e({},r.schema),x='​',g={content:o,format:"html",selection:!0},r.fire("BeforeSetContent",g),o=g.content,-1==o.indexOf("{$caret}")&&(o+="{$caret}"),o=o.replace(/\{\$caret\}/,x),y=S.getRng();var E=y.startContainer||(y.parentElement?y.parentElement():null),N=r.getBody();E===N&&S.isCollapsed()&&k.isBlock(N.firstChild)&&k.isEmpty(N.firstChild)&&(y=k.createRng(),y.setStart(N.firstChild,0),y.setEnd(N.firstChild,0),S.setRng(y)),S.isCollapsed()||r.getDoc().execCommand("Delete",!1,null),f=S.getNode();var T={context:f.nodeName.toLowerCase()};if(m=u.parse(o,T),l(m),b=m.lastChild,"mce_marker"==b.attr("id"))for(v=b,b=b.prev;b;b=b.walk(!0))if(3==b.type||!k.isBlock(b.name)){r.schema.isValidChild(b.parent.name,"span")&&b.parent.insert(v,b,"br"===b.name);break}if(T.invalid){for(S.setContent(x),f=S.getNode(),h=r.getBody(),9==f.nodeType?f=b=h:b=f;b!==h;)f=b,b=b.parentNode;o=f==h?h.innerHTML:k.getOuterHTML(f),o=d.serialize(u.parse(o.replace(//i,function(){return d.serialize(m)}))),f==h?k.setHTML(h,o):k.setOuterHTML(f,o)}else o=d.serialize(m),b=f.firstChild,C=f.lastChild,!b||b===C&&"BR"===b.nodeName?k.setHTML(f,o):S.setContent(o);c(),v=k.get("mce_marker"),S.scrollIntoView(v),y=k.createRng(),b=v.previousSibling,b&&3==b.nodeType?(y.setStart(b,b.nodeValue.length),p||(C=v.nextSibling,C&&3==C.nodeType&&(b.appendData(C.data),C.parentNode.removeChild(C)))):(y.setStartBefore(v),y.setEndBefore(v)),k.remove(v),S.setRng(y),r.fire("SetContent",g),r.addVisual()},mceInsertRawHTML:function(e,t,n){S.setContent("tiny_mce_marker"),r.setContent(r.getContent().replace(/tiny_mce_marker/g,function(){return n}))},mceToggleFormat:function(e,t,n){_(n)},mceSetContent:function(e,t,n){r.setContent(n)},"Indent,Outdent":function(e){var t,n,i;t=R.indentation,n=/[a-z%]+$/i.exec(t),t=parseInt(t,10),y("InsertUnorderedList")||y("InsertOrderedList")?x(e):(R.forced_root_block||k.getParent(S.getNode(),k.isBlock)||A.apply("div"),s(S.getSelectedBlocks(),function(o){if("LI"!=o.nodeName){var a=r.getParam("indent_use_margin",!1)?"margin":"padding";a+="rtl"==k.getStyle(o,"direction",!0)?"Right":"Left","outdent"==e?(i=Math.max(0,parseInt(o.style[a]||0,10)-t),k.setStyle(o,a,i?i+n:"")):(i=parseInt(o.style[a]||0,10)+t+n,k.setStyle(o,a,i))}}))},mceRepaint:function(){if(f)try{E(m),S.getSel()&&S.getSel().selectAllChildren(r.getBody()),S.collapse(m),N()}catch(e){}},InsertHorizontalRule:function(){r.execCommand("mceInsertContent",!1,"
        ")},mceToggleVisualAid:function(){r.hasVisual=!r.hasVisual,r.addVisual()},mceReplaceContent:function(e,t,n){r.execCommand("mceInsertContent",!1,n.replace(/\{\$selection\}/g,S.getContent({format:"text"})))},mceInsertLink:function(e,t,n){var r;"string"==typeof n&&(n={href:n}),r=k.getParent(S.getNode(),"a"),n.href=n.href.replace(" ","%20"),r&&n.href||A.remove("link"),n.href&&A.apply("link",n,r)},selectAll:function(){var e=k.getRoot(),t;S.getRng().setStart?(t=k.createRng(),t.setStart(e,0),t.setEnd(e,e.childNodes.length),S.setRng(t)):(t=S.getRng(),t.item||(t.moveToElementText(e),t.select()))},"delete":function(){x("Delete");var e=r.getBody();k.isEmpty(e)&&(r.setContent(""),e.firstChild&&k.isBlock(e.firstChild)?r.selection.setCursorLocation(e.firstChild,0):r.selection.setCursorLocation(e,0))},mceNewDocument:function(){r.setContent("")},InsertLineBreak:function(e,t,n){function i(){for(var e=new a(p,v),t,n=r.schema.getNonEmptyElements();t=e.next();)if(n[t.nodeName.toLowerCase()]||t.length>0)return!0}var s=n,l,c,u,d=S.getRng(!0);new o(k).normalize(d);var f=d.startOffset,p=d.startContainer;if(1==p.nodeType&&p.hasChildNodes()){var g=f>p.childNodes.length-1;p=p.childNodes[Math.min(f,p.childNodes.length-1)]||p,f=g&&3==p.nodeType?p.nodeValue.length:0}var v=k.getParent(p,k.isBlock),y=v?v.nodeName.toUpperCase():"",b=v?k.getParent(v.parentNode,k.isBlock):null,C=b?b.nodeName.toUpperCase():"",x=s&&s.ctrlKey;"LI"!=C||x||(v=b,y=C),p&&3==p.nodeType&&f>=p.nodeValue.length&&(h||i()||(l=k.create("br"),d.insertNode(l),d.setStartAfter(l),d.setEndAfter(l),c=!0)),l=k.create("br"),d.insertNode(l);var w=k.doc.documentMode;return h&&"PRE"==y&&(!w||8>w)&&l.parentNode.insertBefore(k.doc.createTextNode("\r"),l),u=k.create("span",{}," "),l.parentNode.insertBefore(u,l),S.scrollIntoView(u),k.remove(u),c?(d.setStartBefore(l),d.setEndBefore(l)):(d.setStartAfter(l),d.setEndAfter(l)),S.setRng(d),r.undoManager.add(),m}}),C({"JustifyLeft,JustifyCenter,JustifyRight,JustifyFull":function(e){var t="align"+e.substring(7),n=S.isCollapsed()?[k.getParent(S.getNode(),k.isBlock)]:S.getSelectedBlocks(),r=c(n,function(e){return!!A.matchNode(e,t)});return-1!==u(r,m)},"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(e){return w(e)},mceBlockQuote:function(){return w("blockquote")},Outdent:function(){var e;if(R.inline_styles){if((e=k.getParent(S.getStart(),k.isBlock))&&parseInt(e.style.paddingLeft,10)>0)return m;if((e=k.getParent(S.getEnd(),k.isBlock))&&parseInt(e.style.paddingLeft,10)>0)return m}return y("InsertUnorderedList")||y("InsertOrderedList")||!R.inline_styles&&!!k.getParent(S.getNode(),"BLOCKQUOTE")},"InsertUnorderedList,InsertOrderedList":function(e){var t=k.getParent(S.getNode(),"ul,ol");return t&&("insertunorderedlist"===e&&"UL"===t.tagName||"insertorderedlist"===e&&"OL"===t.tagName)}},"state"),C({"FontSize,FontName":function(e){var t=0,n;return(n=k.getParent(S.getNode(),"span"))&&(t="fontsize"==e?n.style.fontSize:n.style.fontFamily.replace(/, /g,",").replace(/[\'\"]/g,"").toLowerCase()),t}},"value"),C({Undo:function(){r.undoManager.undo()},Redo:function(){r.undoManager.redo()}})}}),r(V,[d],function(e){function t(e,o){var a=this,s,l;if(e=r(e),o=a.settings=o||{},s=o.base_uri,/^([\w\-]+):([^\/]{2})/i.test(e)||/^\s*#/.test(e))return void(a.source=e);var c=0===e.indexOf("//");0!==e.indexOf("/")||c||(e=(s?s.protocol||"http":"http")+"://mce_host"+e),/^[\w\-]*:?\/\//.test(e)||(l=o.base_uri?o.base_uri.path:new t(location.href).directory,""===o.base_uri.protocol?e="//mce_host"+a.toAbsPath(l,e):(e=/([^#?]*)([#?]?.*)/.exec(e),e=(s&&s.protocol||"http")+"://mce_host"+a.toAbsPath(l,e[1])+e[2])),e=e.replace(/@@/g,"(mce_at)"),e=/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(e),n(i,function(t,n){var r=e[n];r&&(r=r.replace(/\(mce_at\)/g,"@@")),a[t]=r}),s&&(a.protocol||(a.protocol=s.protocol),a.userInfo||(a.userInfo=s.userInfo),a.port||"mce_host"!==a.host||(a.port=s.port),a.host&&"mce_host"!==a.host||(a.host=s.host),a.source=""),c&&(a.protocol="")}var n=e.each,r=e.trim,i="source protocol authority userInfo user password host port relative path directory file query anchor".split(" "),o={ftp:21,http:80,https:443,mailto:25};return t.prototype={setPath:function(e){var t=this;e=/^(.*?)\/?(\w+)?$/.exec(e),t.path=e[0],t.directory=e[1],t.file=e[2],t.source="",t.getURI()},toRelative:function(e){var n=this,r;if("./"===e)return e;if(e=new t(e,{base_uri:n}),"mce_host"!=e.host&&n.host!=e.host&&e.host||n.port!=e.port||n.protocol!=e.protocol&&""!==e.protocol)return e.getURI();var i=n.getURI(),o=e.getURI();return i==o||"/"==i.charAt(i.length-1)&&i.substr(0,i.length-1)==o?i:(r=n.toRelPath(n.path,e.path),e.query&&(r+="?"+e.query),e.anchor&&(r+="#"+e.anchor),r)},toAbsolute:function(e,n){return e=new t(e,{base_uri:this}),e.getURI(n&&this.isSameOrigin(e))},isSameOrigin:function(e){if(this.host==e.host&&this.protocol==e.protocol){if(this.port==e.port)return!0;var t=o[this.protocol];if(t&&(this.port||t)==(e.port||t))return!0}return!1},toRelPath:function(e,t){var n,r=0,i="",o,a;if(e=e.substring(0,e.lastIndexOf("/")),e=e.split("/"),n=t.split("/"),e.length>=n.length)for(o=0,a=e.length;a>o;o++)if(o>=n.length||e[o]!=n[o]){r=o+1;break}if(e.lengtho;o++)if(o>=e.length||e[o]!=n[o]){r=o+1;break}if(1===r)return t;for(o=0,a=e.length-(r-1);a>o;o++)i+="../";for(o=r-1,a=n.length;a>o;o++)i+=o!=r-1?"/"+n[o]:n[o];return i},toAbsPath:function(e,t){var r,i=0,o=[],a,s;for(a=/\/$/.test(t)?"/":"",e=e.split("/"),t=t.split("/"),n(e,function(e){e&&o.push(e)}),e=o,r=t.length-1,o=[];r>=0;r--)0!==t[r].length&&"."!==t[r]&&(".."!==t[r]?i>0?i--:o.push(t[r]):i++);return r=e.length-i,s=0>=r?o.reverse().join("/"):e.slice(0,r).join("/")+"/"+o.reverse().join("/"),0!==s.indexOf("/")&&(s="/"+s),a&&s.lastIndexOf("/")!==s.length-1&&(s+=a),s},getURI:function(e){var t,n=this;return(!n.source||e)&&(t="",e||(t+=n.protocol?n.protocol+"://":"//",n.userInfo&&(t+=n.userInfo+"@"),n.host&&(t+=n.host),n.port&&(t+=":"+n.port)),n.path&&(t+=n.path),n.query&&(t+="?"+n.query),n.anchor&&(t+="#"+n.anchor),n.source=t),n.source}},t}),r(U,[d],function(e){function t(){}var n=e.each,r=e.extend,i,o;return t.extend=i=function(e){function t(){var e,t,n,r=this;if(!o&&(r.init&&r.init.apply(r,arguments),t=r.Mixins))for(e=t.length;e--;)n=t[e],n.init&&n.init.apply(r,arguments)}function a(){return this}function s(e,t){return function(){var n=this,r=n._super,i;return n._super=c[e],i=t.apply(n,arguments),n._super=r,i}}var l=this,c=l.prototype,u,d,f;o=!0,u=new l,o=!1,e.Mixins&&(n(e.Mixins,function(t){t=t;for(var n in t)"init"!==n&&(e[n]=t[n])}),c.Mixins&&(e.Mixins=c.Mixins.concat(e.Mixins))),e.Methods&&n(e.Methods.split(","),function(t){e[t]=a}),e.Properties&&n(e.Properties.split(","),function(t){var n="_"+t;e[t]=function(e){var t=this,r;return e!==r?(t[n]=e,t):t[n]}}),e.Statics&&n(e.Statics,function(e,n){t[n]=e}),e.Defaults&&c.Defaults&&(e.Defaults=r({},c.Defaults,e.Defaults));for(d in e)f=e[d],u[d]="function"==typeof f&&c[d]?s(d,f):f;return t.prototype=u,t.constructor=t,t.extend=i,t},t}),r($,[d],function(e){function t(t){function n(){return!1}function r(){return!0}function i(e,i){var o,s,l,c;if(e=e.toLowerCase(),i=i||{},i.type=e,i.target||(i.target=u),i.preventDefault||(i.preventDefault=function(){i.isDefaultPrevented=r},i.stopPropagation=function(){i.isPropagationStopped=r},i.stopImmediatePropagation=function(){i.isImmediatePropagationStopped=r},i.isDefaultPrevented=n,i.isPropagationStopped=n,i.isImmediatePropagationStopped=n),t.beforeFire&&t.beforeFire(i),o=d[e])for(s=0,l=o.length;l>s;s++){if(c=o[s],c.once&&a(e,c.func),i.isImmediatePropagationStopped())return i.stopPropagation(),i;if(c.func.call(u,i)===!1)return i.preventDefault(),i}return i}function o(t,r,i,o){var a,s,l;if(r===!1&&(r=n),r)for(r={func:r},o&&e.extend(r,o),s=t.toLowerCase().split(" "),l=s.length;l--;)t=s[l],a=d[t],a||(a=d[t]=[],f(t,!0)),i?a.unshift(r):a.push(r);return c}function a(e,t){var n,r,i,o,a;if(e)for(o=e.toLowerCase().split(" "),n=o.length;n--;){if(e=o[n],r=d[e],!e){for(i in d)f(i,!1),delete d[i];return c}if(r){if(t)for(a=r.length;a--;)r[a].func===t&&(r=r.slice(0,a).concat(r.slice(a+1)),d[e]=r);else r.length=0;r.length||(f(e,!1),delete d[e])}}else{for(e in d)f(e,!1);d={}}return c}function s(e,t,n){return o(e,t,n,{once:!0})}function l(e){return e=e.toLowerCase(),!(!d[e]||0===d[e].length)}var c=this,u,d={},f;t=t||{},u=t.scope||c,f=t.toggleEvent||n,c.fire=i,c.on=o,c.off=a,c.once=s,c.has=l}var n=e.makeMap("focus blur focusin focusout click dblclick mousedown mouseup mousemove mouseover beforepaste paste cut copy selectionchange mouseout mouseenter mouseleave wheel keydown keypress keyup input contextmenu dragstart dragend dragover draggesture dragdrop drop drag submit compositionstart compositionend compositionupdate touchstart touchend"," ");return t.isNative=function(e){return!!n[e.toLowerCase()]},t}),r(q,[U],function(e){function t(e){for(var t=[],n=e.length,r;n--;)r=e[n],r.__checked||(t.push(r),r.__checked=1);for(n=t.length;n--;)delete t[n].__checked;return t}var n=/^([\w\\*]+)?(?:#([\w\\]+))?(?:\.([\w\\\.]+))?(?:\[\@?([\w\\]+)([\^\$\*!~]?=)([\w\\]+)\])?(?:\:(.+))?/i,r=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,i=/^\s*|\s*$/g,o,a=e.extend({init:function(e){function t(e){return e?(e=e.toLowerCase(),function(t){return"*"===e||t.type===e}):void 0}function o(e){return e?function(t){return t._name===e}:void 0}function a(e){return e?(e=e.split("."),function(t){for(var n=e.length;n--;)if(!t.hasClass(e[n]))return!1;return!0}):void 0}function s(e,t,n){return e?function(r){var i=r[e]?r[e]():"";return t?"="===t?i===n:"*="===t?i.indexOf(n)>=0:"~="===t?(" "+i+" ").indexOf(" "+n+" ")>=0:"!="===t?i!=n:"^="===t?0===i.indexOf(n):"$="===t?i.substr(i.length-n.length)===n:!1:!!n}:void 0}function l(e){var t;return e?(e=/(?:not\((.+)\))|(.+)/i.exec(e),e[1]?(t=u(e[1],[]),function(e){return!d(e,t)}):(e=e[2],function(t,n,r){return"first"===e?0===n:"last"===e?n===r-1:"even"===e?n%2===0:"odd"===e?n%2===1:t[e]?t[e]():!1})):void 0}function c(e,r,c){function u(e){e&&r.push(e)}var d;return d=n.exec(e.replace(i,"")),u(t(d[1])),u(o(d[2])),u(a(d[3])),u(s(d[4],d[5],d[6])),u(l(d[7])),r.psuedo=!!d[7],r.direct=c,r}function u(e,t){var n=[],i,o,a;do if(r.exec(""),o=r.exec(e),o&&(e=o[3],n.push(o[1]),o[2])){i=o[3];break}while(o);for(i&&u(i,t),e=[],a=0;a"!=n[a]&&e.push(c(n[a],[],">"===n[a-1]));return t.push(e),t}var d=this.match;this._selectors=u(e,[])},match:function(e,t){var n,r,i,o,a,s,l,c,u,d,f,p,h;for(t=t||this._selectors,n=0,r=t.length;r>n;n++){for(a=t[n],o=a.length,h=e,p=0,i=o-1;i>=0;i--)for(c=a[i];h;){if(c.psuedo)for(f=h.parent().items(),u=d=f.length;u--&&f[u]!==h;);for(s=0,l=c.length;l>s;s++)if(!c[s](h,u,d)){s=l+1;break}if(s===l){p++;break}if(i===o-1)break;h=h.parent()}if(p===o)return!0}return!1},find:function(e){function n(e,t,i){var o,a,s,l,c,u=t[i];for(o=0,a=e.length;a>o;o++){for(c=e[o],s=0,l=u.length;l>s;s++)if(!u[s](c,o,a)){s=l+1;break}if(s===l)i==t.length-1?r.push(c):c.items&&n(c.items(),t,i+1);else if(u.direct)return;c.items&&n(c.items(),t,i)}}var r=[],i,s,l=this._selectors;if(e.items){for(i=0,s=l.length;s>i;i++)n(e.items(),l[i],0);s>1&&(r=t(r))}return o||(o=a.Collection),new o(r)}});return a}),r(j,[d,q,U],function(e,t,n){var r,i,o=Array.prototype.push,a=Array.prototype.slice;return i={length:0,init:function(e){e&&this.add(e)},add:function(t){var n=this;return e.isArray(t)?o.apply(n,t):t instanceof r?n.add(t.toArray()):o.call(n,t),n},set:function(e){var t=this,n=t.length,r;for(t.length=0,t.add(e),r=t.length;n>r;r++)delete t[r];return t},filter:function(e){var n=this,i,o,a=[],s,l;for("string"==typeof e?(e=new t(e),l=function(t){return e.match(t)}):l=e,i=0,o=n.length;o>i;i++)s=n[i],l(s)&&a.push(s);return new r(a)},slice:function(){return new r(a.apply(this,arguments))},eq:function(e){return-1===e?this.slice(e):this.slice(e,+e+1)},each:function(t){return e.each(this,t),this},toArray:function(){return e.toArray(this)},indexOf:function(e){for(var t=this,n=t.length;n--&&t[n]!==e;);return n},reverse:function(){return new r(e.toArray(this).reverse())},hasClass:function(e){return this[0]?this[0].hasClass(e):!1},prop:function(e,t){var n=this,r,i;return t!==r?(n.each(function(n){n[e]&&n[e](t)}),n):(i=n[0],i&&i[e]?i[e]():void 0)},exec:function(t){var n=this,r=e.toArray(arguments).slice(1);return n.each(function(e){e[t]&&e[t].apply(e,r)}),n},remove:function(){for(var e=this.length;e--;)this[e].remove();return this}},e.each("fire on off show hide addClass removeClass append prepend before after reflow".split(" "),function(t){i[t]=function(){var n=e.toArray(arguments);return this.each(function(e){t in e&&e[t].apply(e,n)}),this}}),e.each("text name disabled active selected checked visible parent value data".split(" "),function(e){i[e]=function(t){return this.prop(e,t)}}),r=n.extend(i),t.Collection=r,r}),r(Y,[d,y],function(e,t){var n=0;return{id:function(){return"mceu_"+n++},createFragment:function(e){return t.DOM.createFragment(e)},getWindowSize:function(){return t.DOM.getViewPort()},getSize:function(e){var t,n;if(e.getBoundingClientRect){var r=e.getBoundingClientRect();t=Math.max(r.width||r.right-r.left,e.offsetWidth),n=Math.max(r.height||r.bottom-r.bottom,e.offsetHeight)}else t=e.offsetWidth,n=e.offsetHeight;return{width:t,height:n}},getPos:function(e,n){return t.DOM.getPos(e,n)},getViewPort:function(e){return t.DOM.getViewPort(e)},get:function(e){return document.getElementById(e)},addClass:function(e,n){return t.DOM.addClass(e,n)},removeClass:function(e,n){return t.DOM.removeClass(e,n)},hasClass:function(e,n){return t.DOM.hasClass(e,n)},toggleClass:function(e,n,r){return t.DOM.toggleClass(e,n,r)},css:function(e,n,r){return t.DOM.setStyle(e,n,r)},getRuntimeStyle:function(e,n){return t.DOM.getStyle(e,n,!0)},on:function(e,n,r,i){return t.DOM.bind(e,n,r,i)},off:function(e,n,r){return t.DOM.unbind(e,n,r)},fire:function(e,n,r){return t.DOM.fire(e,n,r)},innerHtml:function(e,n){t.DOM.setHTML(e,n)}}}),r(K,[U,d,$,j,Y],function(e,t,n,r,i){function o(e){return e._eventDispatcher||(e._eventDispatcher=new n({scope:e,toggleEvent:function(t,r){r&&n.isNative(t)&&(e._nativeEvents||(e._nativeEvents={}),e._nativeEvents[t]=!0,e._rendered&&e.bindPendingEvents())}})),e._eventDispatcher}var a="onmousewheel"in document,s=!1,l="mce-",c=e.extend({Statics:{classPrefix:l},isRtl:function(){return c.rtl},classPrefix:l,init:function(e){var n=this,r,o;if(n.settings=e=t.extend({},n.Defaults,e),n._id=e.id||i.id(),n._text=n._name="",n._width=n._height=0,n._aria={role:e.role},this._elmCache={},r=e.classes)for(r=r.split(" "),r.map={},o=r.length;o--;)r.map[r[o]]=!0;n._classes=r||[],n.visible(!0),t.each("title text width height name classes visible disabled active value".split(" "),function(t){var r=e[t],i;r!==i?n[t](r):n["_"+t]===i&&(n["_"+t]=!1)}),n.on("click",function(){return n.disabled()?!1:void 0}),e.classes&&t.each(e.classes.split(" "),function(e){n.addClass(e)}),n.settings=e,n._borderBox=n.parseBox(e.border),n._paddingBox=n.parseBox(e.padding),n._marginBox=n.parseBox(e.margin),e.hidden&&n.hide()},Properties:"parent,title,text,width,height,disabled,active,name,value",Methods:"renderHtml",getContainerElm:function(){return document.body},getParentCtrl:function(e){for(var t,n=this.getRoot().controlIdLookup;e&&n&&!(t=n[e.id]);)e=e.parentNode;return t},parseBox:function(e){var t,n=10;if(e)return"number"==typeof e?(e=e||0,{top:e,left:e,bottom:e,right:e}):(e=e.split(" "),t=e.length,1===t?e[1]=e[2]=e[3]=e[0]:2===t?(e[2]=e[0],e[3]=e[1]):3===t&&(e[3]=e[1]),{top:parseInt(e[0],n)||0,right:parseInt(e[1],n)||0,bottom:parseInt(e[2],n)||0,left:parseInt(e[3],n)||0})},borderBox:function(){return this._borderBox},paddingBox:function(){return this._paddingBox},marginBox:function(){return this._marginBox},measureBox:function(e,t){function n(t){var n=document.defaultView;return n?(t=t.replace(/[A-Z]/g,function(e){return"-"+e}),n.getComputedStyle(e,null).getPropertyValue(t)):e.currentStyle[t]}function r(e){var t=parseFloat(n(e),10);return isNaN(t)?0:t}return{top:r(t+"TopWidth"),right:r(t+"RightWidth"),bottom:r(t+"BottomWidth"),left:r(t+"LeftWidth")}},initLayoutRect:function(){var e=this,t=e.settings,n,r,o=e.getEl(),a,s,l,c,u,d,f,p;n=e._borderBox=e._borderBox||e.measureBox(o,"border"),e._paddingBox=e._paddingBox||e.measureBox(o,"padding"),e._marginBox=e._marginBox||e.measureBox(o,"margin"),p=i.getSize(o),d=t.minWidth,f=t.minHeight,l=d||p.width,c=f||p.height,a=t.width,s=t.height,u=t.autoResize,u="undefined"!=typeof u?u:!a&&!s,a=a||l,s=s||c;var h=n.left+n.right,m=n.top+n.bottom,g=t.maxWidth||65535,v=t.maxHeight||65535;return e._layoutRect=r={x:t.x||0,y:t.y||0,w:a,h:s,deltaW:h,deltaH:m,contentW:a-h,contentH:s-m,innerW:a-h,innerH:s-m,startMinWidth:d||0,startMinHeight:f||0,minW:Math.min(l,g),minH:Math.min(c,v),maxW:g,maxH:v,autoResize:u,scrollW:0},e._lastLayoutRect={},r},layoutRect:function(e){var t=this,n=t._layoutRect,r,i,o,a,s,l;return n||(n=t.initLayoutRect()),e?(o=n.deltaW,a=n.deltaH,e.x!==s&&(n.x=e.x),e.y!==s&&(n.y=e.y),e.minW!==s&&(n.minW=e.minW),e.minH!==s&&(n.minH=e.minH),i=e.w,i!==s&&(i=in.maxW?n.maxW:i,n.w=i,n.innerW=i-o),i=e.h,i!==s&&(i=in.maxH?n.maxH:i,n.h=i,n.innerH=i-a),i=e.innerW,i!==s&&(i=in.maxW-o?n.maxW-o:i,n.innerW=i,n.w=i+o),i=e.innerH,i!==s&&(i=in.maxH-a?n.maxH-a:i,n.innerH=i,n.h=i+a),e.contentW!==s&&(n.contentW=e.contentW),e.contentH!==s&&(n.contentH=e.contentH),r=t._lastLayoutRect,(r.x!==n.x||r.y!==n.y||r.w!==n.w||r.h!==n.h)&&(l=c.repaintControls,l&&l.map&&!l.map[t._id]&&(l.push(t),l.map[t._id]=!0),r.x=n.x,r.y=n.y,r.w=n.w,r.h=n.h),t):n},repaint:function(){var e=this,t,n,r,i,o=0,a=0,s,l;l=document.createRange?function(e){return e}:Math.round,t=e.getEl().style,r=e._layoutRect,s=e._lastRepaintRect||{},i=e._borderBox,o=i.left+i.right,a=i.top+i.bottom,r.x!==s.x&&(t.left=l(r.x)+"px",s.x=r.x),r.y!==s.y&&(t.top=l(r.y)+"px",s.y=r.y),r.w!==s.w&&(t.width=l(r.w-o)+"px",s.w=r.w),r.h!==s.h&&(t.height=l(r.h-a)+"px",s.h=r.h),e._hasBody&&r.innerW!==s.innerW&&(n=e.getEl("body").style,n.width=l(r.innerW)+"px",s.innerW=r.innerW),e._hasBody&&r.innerH!==s.innerH&&(n=n||e.getEl("body").style,n.height=l(r.innerH)+"px",s.innerH=r.innerH),e._lastRepaintRect=s,e.fire("repaint",{},!1)},on:function(e,t){function n(e){var t,n;return"string"!=typeof e?e:function(i){return t||r.parentsAndSelf().each(function(r){var i=r.settings.callbacks;return i&&(t=i[e])?(n=r,!1):void 0}),t.call(n,i)}}var r=this;return o(r).on(e,n(t)),r},off:function(e,t){return o(this).off(e,t),this},fire:function(e,t,n){var r=this;if(t=t||{},t.control||(t.control=r),t=o(r).fire(e,t),n!==!1&&r.parent)for(var i=r.parent();i&&!t.isPropagationStopped();)i.fire(e,t,!1),i=i.parent();return t},hasEventListeners:function(e){return o(this).has(e)},parents:function(e){var t=this,n,i=new r;for(n=t.parent();n;n=n.parent())i.add(n);return e&&(i=i.filter(e)),i},parentsAndSelf:function(e){return new r(this).add(this.parents(e))},next:function(){var e=this.parent().items();return e[e.indexOf(this)+1]},prev:function(){var e=this.parent().items();return e[e.indexOf(this)-1]},findCommonAncestor:function(e,t){for(var n;e;){for(n=t;n&&e!=n;)n=n.parent();if(e==n)break;e=e.parent()}return e},hasClass:function(e,t){var n=this._classes[t||"control"];return e=this.classPrefix+e,n&&!!n.map[e]},addClass:function(e,t){var n=this,r,i;return e=this.classPrefix+e,r=n._classes[t||"control"],r||(r=[],r.map={},n._classes[t||"control"]=r),r.map[e]||(r.map[e]=e,r.push(e),n._rendered&&(i=n.getEl(t),i&&(i.className=r.join(" ")))),n},removeClass:function(e,t){var n=this,r,i,o;if(e=this.classPrefix+e,r=n._classes[t||"control"],r&&r.map[e])for(delete r.map[e],i=r.length;i--;)r[i]===e&&r.splice(i,1);return n._rendered&&(o=n.getEl(t),o&&(o.className=r.join(" "))),n},toggleClass:function(e,t,n){var r=this;return t?r.addClass(e,n):r.removeClass(e,n),r},classes:function(e){var t=this._classes[e||"control"];return t?t.join(" "):""},innerHtml:function(e){return i.innerHtml(this.getEl(),e),this},getEl:function(e){var t=e?this._id+"-"+e:this._id;return this._elmCache[t]||(this._elmCache[t]=i.get(t)),this._elmCache[t]},visible:function(e){var t=this,n;return"undefined"!=typeof e?(t._visible!==e&&(t._rendered&&(t.getEl().style.display=e?"":"none"),t._visible=e,n=t.parent(),n&&(n._lastRect=null),t.fire(e?"show":"hide")),t):t._visible},show:function(){return this.visible(!0)},hide:function(){return this.visible(!1)},focus:function(){try{this.getEl().focus()}catch(e){}return this},blur:function(){return this.getEl().blur(),this},aria:function(e,t){var n=this,r=n.getEl(n.ariaTarget);return"undefined"==typeof t?n._aria[e]:(n._aria[e]=t,n._rendered&&r.setAttribute("role"==e?e:"aria-"+e,t),n)},encode:function(e,t){return t!==!1&&(e=this.translate(e)),(e||"").replace(/[&<>"]/g,function(e){return"&#"+e.charCodeAt(0)+";"})},translate:function(e){return c.translate?c.translate(e):e},before:function(e){var t=this,n=t.parent();return n&&n.insert(e,n.items().indexOf(t),!0),t},after:function(e){var t=this,n=t.parent();return n&&n.insert(e,n.items().indexOf(t)),t},remove:function(){var e=this,t=e.getEl(),n=e.parent(),r,o;if(e.items){var a=e.items().toArray();for(o=a.length;o--;)a[o].remove()}n&&n.items&&(r=[],n.items().each(function(t){t!==e&&r.push(t)}),n.items().set(r),n._lastRect=null),e._eventsRoot&&e._eventsRoot==e&&i.off(t);var s=e.getRoot().controlIdLookup;return s&&delete s[e._id],t&&t.parentNode&&t.parentNode.removeChild(t),e._rendered=!1,e},renderBefore:function(e){var t=this;return e.parentNode.insertBefore(i.createFragment(t.renderHtml()),e),t.postRender(),t},renderTo:function(e){var t=this;return e=e||t.getContainerElm(),e.appendChild(i.createFragment(t.renderHtml())),t.postRender(),t},postRender:function(){var e=this,t=e.settings,n,r,o,a,s;for(a in t)0===a.indexOf("on")&&e.on(a.substr(2),t[a]);if(e._eventsRoot){for(o=e.parent();!s&&o;o=o.parent())s=o._eventsRoot;if(s)for(a in s._nativeEvents)e._nativeEvents[a]=!0}e.bindPendingEvents(),t.style&&(n=e.getEl(),n&&(n.setAttribute("style",t.style),n.style.cssText=t.style)),e._visible||i.css(e.getEl(),"display","none"),e.settings.border&&(r=e.borderBox(),i.css(e.getEl(),{"border-top-width":r.top,"border-right-width":r.right,"border-bottom-width":r.bottom,"border-left-width":r.left}));var l=e.getRoot();l.controlIdLookup||(l.controlIdLookup={}),l.controlIdLookup[e._id]=e;for(var c in e._aria)e.aria(c,e._aria[c]);e.fire("postrender",{},!1)},scrollIntoView:function(e){function t(e,t){var n,r,i=e;for(n=r=0;i&&i!=t&&i.nodeType;)n+=i.offsetLeft||0,r+=i.offsetTop||0,i=i.offsetParent;return{x:n,y:r}}var n=this.getEl(),r=n.parentNode,i,o,a,s,l,c,u=t(n,r);return i=u.x,o=u.y,a=n.offsetWidth,s=n.offsetHeight,l=r.clientWidth,c=r.clientHeight,"end"==e?(i-=l-a,o-=c-s):"center"==e&&(i-=l/2-a/2,o-=c/2-s/2),r.scrollLeft=i,r.scrollTop=o,this},bindPendingEvents:function(){function e(e){var t=o.getParentCtrl(e.target);t&&t.fire(e.type,e)}function t(){var e=d._lastHoverCtrl;e&&(e.fire("mouseleave",{target:e.getEl()}),e.parents().each(function(e){e.fire("mouseleave",{target:e.getEl()})}),d._lastHoverCtrl=null)}function n(e){var t=o.getParentCtrl(e.target),n=d._lastHoverCtrl,r=0,i,a,s;if(t!==n){if(d._lastHoverCtrl=t,a=t.parents().toArray().reverse(),a.push(t),n){for(s=n.parents().toArray().reverse(),s.push(n),r=0;r=r;i--)n=s[i],n.fire("mouseleave",{target:n.getEl()})}for(i=r;il;l++)d=u[l]._eventsRoot;for(d||(d=u[u.length-1]||o),o._eventsRoot=d,c=l,l=0;c>l;l++)u[l]._eventsRoot=d;var h=d._delegates;h||(h=d._delegates={});for(p in f){if(!f)return!1;"wheel"!==p||s?("mouseenter"===p||"mouseleave"===p?d._hasMouseEnter||(i.on(d.getEl(),"mouseleave",t),i.on(d.getEl(),"mouseover",n),d._hasMouseEnter=1):h[p]||(i.on(d.getEl(),p,e),h[p]=!0),f[p]=!1):a?i.on(o.getEl(),"mousewheel",r):i.on(o.getEl(),"DOMMouseScroll",r)}}},getRoot:function(){for(var e=this,t,n=[];e;){if(e.rootControl){t=e.rootControl;break}n.push(e),t=e,e=e.parent()}t||(t=this);for(var r=n.length;r--;)n[r].rootControl=t;return t},reflow:function(){return this.repaint(),this}});return c}),r(G,[],function(){var e={},t;return{add:function(t,n){e[t.toLowerCase()]=n},has:function(t){return!!e[t.toLowerCase()]},create:function(n,r){var i,o,a;if(!t){a=tinymce.ui;for(o in a)e[o.toLowerCase()]=a[o];t=!0}if("string"==typeof n?(r=r||{},r.type=n):(r=n,n=r.type),n=n.toLowerCase(),i=e[n],!i)throw new Error("Could not find control by type: "+n);return i=new i(r),i.type=n,i}}}),r(X,[],function(){return function(e){function t(e){return e=e||b,e&&e.getAttribute("role")}function n(e){for(var n,r=e||b;r=r.parentNode;)if(n=t(r))return n}function r(e){var t=b;return t?t.getAttribute("aria-"+e):void 0}function i(e){var t=e.tagName.toUpperCase();return"INPUT"==t||"TEXTAREA"==t}function o(e){return i(e)&&!e.hidden?!0:/^(button|menuitem|checkbox|tab|menuitemcheckbox|option|gridcell)$/.test(t(e))?!0:!1}function a(e){function t(e){if(1==e.nodeType&&"none"!=e.style.display){o(e)&&n.push(e);for(var r=0;re?e=t.length-1:e>=t.length&&(e=0),t[e]&&t[e].focus(),e}function u(e,t){var n=-1,r=s();t=t||a(r.getEl());for(var i=0;i=0&&(n=t.getEl(),n&&n.parentNode.removeChild(n),n=e.getEl(),n&&n.parentNode.removeChild(n)),t.parent(this)},create:function(t){var n=this,i,a=[];return o.isArray(t)||(t=[t]),o.each(t,function(t){t&&(t instanceof e||("string"==typeof t&&(t={type:t}),i=o.extend({},n.settings.defaults,t),t.type=i.type=i.type||t.type||n.settings.defaultType||(i.defaults?i.defaults.type:null),t=r.create(i)),a.push(t))}),a},renderNew:function(){var e=this;return e.items().each(function(t,n){var r,i;t.parent(e),t._rendered||(r=e.getEl("body"),i=a.createFragment(t.renderHtml()),r.hasChildNodes()&&n<=r.childNodes.length-1?r.insertBefore(i,r.childNodes[n]):r.appendChild(i),t.postRender())}),e._layout.applyClasses(e),e._lastRect=null,e},append:function(e){return this.add(e).renderNew()},prepend:function(e){var t=this;return t.items().set(t.create(e).concat(t.items().toArray())),t.renderNew()},insert:function(e,t,n){var r=this,i,o,a;return e=r.create(e),i=r.items(),!n&&t=0&&t
        '+(e.settings.html||"")+t.renderHtml(e)+"
        "},postRender:function(){var e=this,t;return e.items().exec("postRender"),e._super(),e._layout.postRender(e),e._rendered=!0,e.settings.style&&a.css(e.getEl(),e.settings.style),e.settings.border&&(t=e.borderBox(),a.css(e.getEl(),{"border-top-width":t.top,"border-right-width":t.right,"border-bottom-width":t.bottom,"border-left-width":t.left})),e.parent()||(e.keyboardNav=new i({root:e})),e},initLayoutRect:function(){var e=this,t=e._super();return e._layout.recalc(e),t},recalc:function(){var e=this,t=e._layoutRect,n=e._lastRect;return n&&n.w==t.w&&n.h==t.h?void 0:(e._layout.recalc(e),t=e.layoutRect(),e._lastRect={x:t.x,y:t.y,w:t.w,h:t.h},!0)},reflow:function(){var t;if(this.visible()){for(e.repaintControls=[],e.repaintControls.map={},this.recalc(),t=e.repaintControls.length;t--;)e.repaintControls[t].repaint();"flow"!==this.settings.layout&&"stack"!==this.settings.layout&&this.repaint(),e.repaintControls=[]}return this}})}),r(Q,[Y],function(e){function t(){var e=document,t,n,r,i,o,a,s,l,c=Math.max;return t=e.documentElement,n=e.body,r=c(t.scrollWidth,n.scrollWidth),i=c(t.clientWidth,n.clientWidth),o=c(t.offsetWidth,n.offsetWidth),a=c(t.scrollHeight,n.scrollHeight),s=c(t.clientHeight,n.clientHeight),l=c(t.offsetHeight,n.offsetHeight),{width:o>r?i:r,height:l>a?s:a}}return function(n,r){function i(){return a.getElementById(r.handle||n)}var o,a=document,s,l,c,u,d,f;r=r||{},l=function(n){var l=t(),p,h;n.preventDefault(),s=n.button,p=i(),d=n.screenX,f=n.screenY,h=window.getComputedStyle?window.getComputedStyle(p,null).getPropertyValue("cursor"):p.runtimeStyle.cursor,o=a.createElement("div"),e.css(o,{position:"absolute",top:0,left:0,width:l.width,height:l.height,zIndex:2147483647,opacity:1e-4,cursor:h}),a.body.appendChild(o),e.on(a,"mousemove",u),e.on(a,"mouseup",c),r.start(n)},u=function(e){return e.button!==s?c(e):(e.deltaX=e.screenX-d,e.deltaY=e.screenY-f,e.preventDefault(),void r.drag(e))},c=function(t){e.off(a,"mousemove",u),e.off(a,"mouseup",c),o.parentNode.removeChild(o),r.stop&&r.stop(t)},this.destroy=function(){e.off(i())},e.on(i(),"mousedown",l)}}),r(Z,[Y,Q],function(e,t){return{init:function(){var e=this;e.on("repaint",e.renderScroll)},renderScroll:function(){function n(){function t(t,a,s,l,c,u){var d,f,p,h,m,g,v,y,b;if(f=i.getEl("scroll"+t)){if(y=a.toLowerCase(),b=s.toLowerCase(),i.getEl("absend")&&e.css(i.getEl("absend"),y,i.layoutRect()[l]-1),!c)return void e.css(f,"display","none");e.css(f,"display","block"),d=i.getEl("body"),p=i.getEl("scroll"+t+"t"),h=d["client"+s]-2*o,h-=n&&r?f["client"+u]:0,m=d["scroll"+s],g=h/m,v={},v[y]=d["offset"+a]+o,v[b]=h,e.css(f,v),v={},v[y]=d["scroll"+a]*g,v[b]=h*g,e.css(p,v)}}var n,r,a;a=i.getEl("body"),n=a.scrollWidth>a.clientWidth,r=a.scrollHeight>a.clientHeight,t("h","Left","Width","contentW",n,"Height"),t("v","Top","Height","contentH",r,"Width")}function r(){function n(n,r,a,s,l){var c,u=i._id+"-scroll"+n,d=i.classPrefix;i.getEl().appendChild(e.createFragment('
        ')),i.draghelper=new t(u+"t",{start:function(){c=i.getEl("body")["scroll"+r],e.addClass(e.get(u),d+"active")},drag:function(e){var t,u,d,f,p=i.layoutRect();u=p.contentW>p.innerW,d=p.contentH>p.innerH,f=i.getEl("body")["client"+a]-2*o,f-=u&&d?i.getEl("scroll"+n)["client"+l]:0,t=f/i.getEl("body")["scroll"+a],i.getEl("body")["scroll"+r]=c+e["delta"+s]/t},stop:function(){e.removeClass(e.get(u),d+"active")}})}i.addClass("scroll"),n("v","Top","Height","Y","Width"),n("h","Left","Width","X","Height")}var i=this,o=2;i.settings.autoScroll&&(i._hasScroll||(i._hasScroll=!0,r(),i.on("wheel",function(e){var t=i.getEl("body");t.scrollLeft+=10*(e.deltaX||0),t.scrollTop+=10*e.deltaY,n()}),e.on(i.getEl("body"),"scroll",n)),n())}}}),r(et,[J,Z],function(e,t){return e.extend({Defaults:{layout:"fit",containerCls:"panel"},Mixins:[t],renderHtml:function(){var e=this,t=e._layout,n=e.settings.html;return e.preRender(),t.preRender(e),"undefined"==typeof n?n='
        '+t.renderHtml(e)+"
        ":("function"==typeof n&&(n=n.call(e)),e._hasBody=!1),'
        '+(e._preBodyHtml||"")+n+"
        "}})}),r(tt,[Y],function(e){function t(t,n,r){var i,o,a,s,l,c,u,d,f,p;return f=e.getViewPort(),o=e.getPos(n),a=o.x,s=o.y,t._fixed&&"static"==e.getRuntimeStyle(document.body,"position")&&(a-=f.x,s-=f.y),i=t.getEl(),p=e.getSize(i),l=p.width,c=p.height,p=e.getSize(n),u=p.width,d=p.height,r=(r||"").split(""),"b"===r[0]&&(s+=d),"r"===r[1]&&(a+=u),"c"===r[0]&&(s+=Math.round(d/2)),"c"===r[1]&&(a+=Math.round(u/2)),"b"===r[3]&&(s-=c),"r"===r[4]&&(a-=l),"c"===r[3]&&(s-=Math.round(c/2)),"c"===r[4]&&(a-=Math.round(l/2)),{x:a,y:s,w:l,h:c}}return{testMoveRel:function(n,r){for(var i=e.getViewPort(),o=0;o0&&a.x+a.w0&&a.y+a.hi.x&&a.x+a.wi.y&&a.y+a.he?0:e+n>t?(e=t-n,0>e?0:e):e}var i=this;if(i.settings.constrainToViewport){var o=e.getViewPort(window),a=i.layoutRect();t=r(t,o.w+o.x,a.w),n=r(n,o.h+o.y,a.h)}return i._rendered?i.layoutRect({x:t,y:n}).repaint():(i.settings.x=t,i.settings.y=n),i.fire("move",{x:t,y:n}),i}}}),r(nt,[Y],function(e){return{resizeToContent:function(){this._layoutRect.autoResize=!0,this._lastRect=null,this.reflow()},resizeTo:function(t,n){if(1>=t||1>=n){var r=e.getWindowSize();t=1>=t?t*r.w:t,n=1>=n?n*r.h:n}return this._layoutRect.autoResize=!1,this.layoutRect({minW:t,minH:n,w:t,h:n}).reflow()},resizeBy:function(e,t){var n=this,r=n.layoutRect();return n.resizeTo(r.w+e,r.h+t)}}}),r(rt,[et,tt,nt,Y],function(e,t,n,r){function i(){function e(e,t){for(;e;){if(e==t)return!0;e=e.parent()}}u||(u=function(t){if(2!=t.button)for(var n=p.length;n--;){var r=p[n],i=r.getParentCtrl(t.target);if(r.settings.autohide){if(i&&(e(i,r)||r.parent()===i))continue;t=r.fire("autohide",{target:t.target}),t.isDefaultPrevented()||r.hide()}}},r.on(document,"click",u))}function o(){d||(d=function(){var e;for(e=p.length;e--;)s(p[e])},r.on(window,"scroll",d))}function a(){if(!f){var e=document.documentElement,t=e.clientWidth,n=e.clientHeight;f=function(){document.all&&t==e.clientWidth&&n==e.clientHeight||(t=e.clientWidth,n=e.clientHeight,g.hideAll())},r.on(window,"resize",f)}}function s(e){function t(t,n){for(var r,i=0;in&&(e.fixed(!1).layoutRect({y:e._autoFixY}).repaint(),t(!1,e._autoFixY-n)):(e._autoFixY=e.layoutRect().y,e._autoFixY
        '),n=n.firstChild,t.getContainerElm().appendChild(n),setTimeout(function(){r.addClass(n,i+"in"),r.addClass(t.getEl(),i+"in")},0),m=!0),l(!0,t)}}),t.on("show",function(){t.parents().each(function(e){return e._fixed?(t.fixed(!0),!1):void 0})}),e.popover&&(t._preBodyHtml='
        ',t.addClass("popover").addClass("bottom").addClass(t.isRtl()?"end":"start"))},fixed:function(e){var t=this;if(t._fixed!=e){if(t._rendered){var n=r.getViewPort();e?t.layoutRect().y-=n.y:t.layoutRect().y+=n.y}t.toggleClass("fixed",e),t._fixed=e}return t},show:function(){var e=this,t,n=e._super();for(t=p.length;t--&&p[t]!==e;);return-1===t&&p.push(e),n},hide:function(){return c(this),l(!1,this),this._super()},hideAll:function(){g.hideAll()},close:function(){var e=this;return e.fire("close").isDefaultPrevented()||(e.remove(),l(!1,e)),e},remove:function(){c(this),this._super()},postRender:function(){var e=this;return e.settings.bodyRole&&this.getEl("body").setAttribute("role",e.settings.bodyRole),e._super()}});return g.hideAll=function(){for(var e=p.length;e--;){var t=p[e];t&&t.settings.autohide&&(t.hide(),p.splice(e,1))}},g}),r(it,[rt,et,Y,Q],function(e,t,n,r){var i=e.extend({modal:!0,Defaults:{border:1,layout:"flex",containerCls:"panel",role:"dialog",callbacks:{submit:function(){this.fire("submit",{data:this.toJSON()})},close:function(){this.close()}}},init:function(e){var n=this;n._super(e),n.isRtl()&&n.addClass("rtl"),n.addClass("window"),n._fixed=!0,e.buttons&&(n.statusbar=new t({layout:"flex",border:"1 0 0 0",spacing:3,padding:10,align:"center",pack:n.isRtl()?"start":"end",defaults:{type:"button"},items:e.buttons}),n.statusbar.addClass("foot"),n.statusbar.parent(n)),n.on("click",function(e){-1!=e.target.className.indexOf(n.classPrefix+"close")&&n.close()}),n.on("cancel",function(){n.close()}),n.aria("describedby",n.describedBy||n._id+"-none"),n.aria("label",e.title),n._fullscreen=!1},recalc:function(){var e=this,t=e.statusbar,r,i,o,a;e._fullscreen&&(e.layoutRect(n.getWindowSize()),e.layoutRect().contentH=e.layoutRect().innerH),e._super(),r=e.layoutRect(),e.settings.title&&!e._fullscreen&&(i=r.headerW,i>r.w&&(o=r.x-Math.max(0,i/2),e.layoutRect({w:i,x:o}),a=!0)),t&&(t.layoutRect({w:e.layoutRect().innerW}).recalc(),i=t.layoutRect().minW+r.deltaW,i>r.w&&(o=r.x-Math.max(0,i-r.w),e.layoutRect({w:i,x:o}),a=!0)),a&&e.recalc()},initLayoutRect:function(){var e=this,t=e._super(),r=0,i;if(e.settings.title&&!e._fullscreen){i=e.getEl("head");var o=n.getSize(i);t.headerW=o.width,t.headerH=o.height,r+=t.headerH}e.statusbar&&(r+=e.statusbar.layoutRect().h),t.deltaH+=r,t.minH+=r,t.h+=r;var a=n.getWindowSize();return t.x=Math.max(0,a.w/2-t.w/2),t.y=Math.max(0,a.h/2-t.h/2),t},renderHtml:function(){var e=this,t=e._layout,n=e._id,r=e.classPrefix,i=e.settings,o="",a="",s=i.html;return e.preRender(),t.preRender(e),i.title&&(o='
        '+e.encode(i.title)+'
        '),i.url&&(s=''),"undefined"==typeof s&&(s=t.renderHtml(e)),e.statusbar&&(a=e.statusbar.renderHtml()),'
        '+o+'
        '+s+"
        "+a+"
        "},fullscreen:function(e){var t=this,r=document.documentElement,i,o=t.classPrefix,a;if(e!=t._fullscreen)if(n.on(window,"resize",function(){var e;if(t._fullscreen)if(i)t._timer||(t._timer=setTimeout(function(){var e=n.getWindowSize();t.moveTo(0,0).resizeTo(e.w,e.h),t._timer=0},50));else{e=(new Date).getTime();var r=n.getWindowSize();t.moveTo(0,0).resizeTo(r.w,r.h),(new Date).getTime()-e>50&&(i=!0)}}),a=t.layoutRect(),t._fullscreen=e,e){t._initial={x:a.x,y:a.y,w:a.w,h:a.h},t._borderBox=t.parseBox("0"),t.getEl("head").style.display="none",a.deltaH-=a.headerH+2,n.addClass(r,o+"fullscreen"),n.addClass(document.body,o+"fullscreen"),t.addClass("fullscreen");var s=n.getWindowSize();t.moveTo(0,0).resizeTo(s.w,s.h)}else t._borderBox=t.parseBox(t.settings.border),t.getEl("head").style.display="",a.deltaH+=a.headerH,n.removeClass(r,o+"fullscreen"),n.removeClass(document.body,o+"fullscreen"),t.removeClass("fullscreen"),t.moveTo(t._initial.x,t._initial.y).resizeTo(t._initial.w,t._initial.h);return t.reflow()},postRender:function(){var e=this,t;setTimeout(function(){e.addClass("in")},0),e._super(),e.statusbar&&e.statusbar.postRender(),e.focus(),this.dragHelper=new r(e._id+"-dragh",{start:function(){t={x:e.layoutRect().x,y:e.layoutRect().y}},drag:function(n){e.moveTo(t.x+n.deltaX,t.y+n.deltaY)}}),e.on("submit",function(t){t.isDefaultPrevented()||e.close()})},submit:function(){return this.fire("submit",{data:this.toJSON()})},remove:function(){var e=this,t=e.classPrefix;e.dragHelper.destroy(),e._super(),e.statusbar&&this.statusbar.remove(),e._fullscreen&&(n.removeClass(document.documentElement,t+"fullscreen"),n.removeClass(document.body,t+"fullscreen"))},getContentWindow:function(){var e=this.getEl().getElementsByTagName("iframe")[0];return e?e.contentWindow:null}});return i}),r(ot,[it],function(e){var t=e.extend({init:function(e){e={border:1,padding:20,layout:"flex",pack:"center",align:"center",containerCls:"panel",autoScroll:!0,buttons:{type:"button",text:"Ok",action:"ok"},items:{type:"label",multiline:!0,maxWidth:500,maxHeight:200}},this._super(e)},Statics:{OK:1,OK_CANCEL:2,YES_NO:3,YES_NO_CANCEL:4,msgBox:function(n){function r(e,t,n){return{type:"button",text:e,subtype:n?"primary":"",onClick:function(e){e.control.parents()[1].close(),o(t)}}}var i,o=n.callback||function(){};switch(n.buttons){case t.OK_CANCEL:i=[r("Ok",!0,!0),r("Cancel",!1)];break;case t.YES_NO:case t.YES_NO_CANCEL:i=[r("Yes",1,!0),r("No",0)],n.buttons==t.YES_NO_CANCEL&&i.push(r("Cancel",-1));break;default:i=[r("Ok",!0,!0)]}return new e({padding:20,x:n.x,y:n.y,minWidth:300,minHeight:100,layout:"flex",pack:"center",align:"center",buttons:i,title:n.title,role:"alertdialog",items:{type:"label",multiline:!0,maxWidth:500,maxHeight:200,text:n.text},onPostRender:function(){this.aria("describedby",this.items()[0]._id)},onClose:n.onClose,onCancel:function(){o(!1)}}).renderTo(document.body).reflow()},alert:function(e,n){return"string"==typeof e&&(e={text:e}),e.callback=n,t.msgBox(e)},confirm:function(e,n){return"string"==typeof e&&(e={text:e}),e.callback=n,e.buttons=t.OK_CANCEL,t.msgBox(e)}}});return t}),r(at,[it,ot],function(e,t){return function(n){function r(){return o.length?o[o.length-1]:void 0}var i=this,o=[];i.windows=o,n.on("remove",function(){for(var e=o.length;e--;)o[e].close()}),i.open=function(t,r){var i;return n.editorManager.setActive(n),t.title=t.title||" ",t.url=t.url||t.file,t.url&&(t.width=parseInt(t.width||320,10),t.height=parseInt(t.height||240,10)),t.body&&(t.items={defaults:t.defaults,type:t.bodyType||"form",items:t.body}),t.url||t.buttons||(t.buttons=[{text:"Ok",subtype:"primary",onclick:function(){i.find("form")[0].submit()}},{text:"Cancel",onclick:function(){i.close()}}]),i=new e(t),o.push(i),i.on("close",function(){for(var e=o.length;e--;)o[e]===i&&o.splice(e,1);o.length||n.focus()}),t.data&&i.on("postRender",function(){this.find("*").each(function(e){var n=e.name();n in t.data&&e.value(t.data[n])})}),i.features=t||{},i.params=r||{},1===o.length&&n.nodeChanged(),i.renderTo().reflow()},i.alert=function(e,r,i){t.alert(e,function(){r?r.call(i||this):n.focus()})},i.confirm=function(e,n,r){t.confirm(e,function(e){n.call(r||this,e)})},i.close=function(){r()&&r().close()},i.getParams=function(){return r()?r().params:null},i.setParams=function(e){r()&&(r().params=e)},i.getWindows=function(){return o}}}),r(st,[B,x,_,g,u,d],function(e,t,n,r,i,o){return function(a){function s(e,t){try{a.getDoc().execCommand(e,!1,t)}catch(n){}}function l(){var e=a.getDoc().documentMode;return e?e:6}function c(e){return e.isDefaultPrevented()}function u(){function t(e){var t=new i(function(){});o.each(a.getBody().getElementsByTagName("*"),function(e){"SPAN"==e.tagName&&e.setAttribute("mce-data-marked",1),!e.hasAttribute("data-mce-style")&&e.hasAttribute("style")&&a.dom.setAttrib(e,"style",a.dom.getAttrib(e,"style"))}),t.observe(a.getDoc(),{childList:!0,attributes:!0,subtree:!0,attributeFilter:["style"]}),a.getDoc().execCommand(e?"ForwardDelete":"Delete",!1,null);var n=a.selection.getRng(),r=n.startContainer.parentNode;o.each(t.takeRecords(),function(e){if(q.isChildOf(e.target,a.getBody())){if("style"==e.attributeName){var t=e.target.getAttribute("data-mce-style");t?e.target.setAttribute("style",t):e.target.removeAttribute("style")}o.each(e.addedNodes,function(e){if("SPAN"==e.nodeName&&!e.getAttribute("mce-data-marked")){var t,i;e==r&&(t=n.startOffset,i=e.firstChild),q.remove(e,!0),i&&(n.setStart(i,t),n.setEnd(i,t),a.selection.setRng(n))}})}}),t.disconnect(),o.each(a.dom.select("span[mce-data-marked]"),function(e){e.removeAttribute("mce-data-marked")})}var n=a.getDoc(),r="data:text/mce-internal,",i=window.MutationObserver,s,l;i||(s=!0,i=function(){function e(e){var t=e.relatedNode||e.target;n.push({target:t,addedNodes:[t]})}function t(e){var t=e.relatedNode||e.target;n.push({target:t,attributeName:e.attrName})}var n=[],r;this.observe=function(n){r=n,r.addEventListener("DOMSubtreeModified",e,!1),r.addEventListener("DOMNodeInsertedIntoDocument",e,!1),r.addEventListener("DOMNodeInserted",e,!1),r.addEventListener("DOMAttrModified",t,!1)},this.disconnect=function(){r.removeEventListener("DOMSubtreeModified",e,!1),r.removeEventListener("DOMNodeInsertedIntoDocument",e,!1),r.removeEventListener("DOMNodeInserted",e,!1),r.removeEventListener("DOMAttrModified",t,!1)},this.takeRecords=function(){return n}}),a.on("keydown",function(n){var r=n.keyCode==$,i=e.metaKeyPressed(n);if(!c(n)&&(r||n.keyCode==U)){var o=a.selection.getRng(),s=o.startContainer,l=o.startOffset;if(!i&&o.collapsed&&3==s.nodeType&&(r?l0))return;n.preventDefault(),i&&a.selection.getSel().modify("extend",r?"forward":"backward","word"),t(r)}}),a.on("keypress",function(n){c(n)||j.isCollapsed()||!n.charCode||e.metaKeyPressed(n)||(n.preventDefault(),t(!0),a.selection.setContent(String.fromCharCode(n.charCode)))}),a.addCommand("Delete",function(){t()}),a.addCommand("ForwardDelete",function(){t(!0)}),s||(a.on("dragstart",function(e){var t;a.selection.isCollapsed()&&"IMG"==e.target.tagName&&j.select(e.target),l=j.getRng(),t=a.selection.getContent(),t.length>0&&e.dataTransfer.setData("URL","data:text/mce-internal,"+escape(t))}),a.on("drop",function(e){if(!c(e)){var i=e.dataTransfer.getData("URL");if(!i||-1==i.indexOf(r)||!n.caretRangeFromPoint)return;i=unescape(i.substr(r.length)),n.caretRangeFromPoint&&(e.preventDefault(),window.setTimeout(function(){var r=n.caretRangeFromPoint(e.x,e.y);l&&(j.setRng(l),l=null),t(),j.setRng(r),a.insertContent(i)},0))}}),a.on("cut",function(e){!c(e)&&e.clipboardData&&(e.preventDefault(),e.clipboardData.clearData(),e.clipboardData.setData("text/html",a.selection.getContent()),e.clipboardData.setData("text/plain",a.selection.getContent({format:"text"})),t(!0))}))}function d(){function e(e){var t=q.create("body"),n=e.cloneContents();return t.appendChild(n),j.serializer.serialize(t,{format:"html"})}function n(n){if(!n.setStart){if(n.item)return!1;var r=n.duplicate();return r.moveToElementText(a.getBody()),t.compareRanges(n,r)}var i=e(n),o=q.createRng();o.selectNode(a.getBody());var s=e(o);return i===s}a.on("keydown",function(e){var t=e.keyCode,r,i;if(!c(e)&&(t==$||t==U)){if(r=a.selection.isCollapsed(),i=a.getBody(),r&&!q.isEmpty(i))return;if(!r&&!n(a.selection.getRng()))return;e.preventDefault(),a.setContent(""),i.firstChild&&q.isBlock(i.firstChild)?a.selection.setCursorLocation(i.firstChild,0):a.selection.setCursorLocation(i,0),a.nodeChanged()}})}function f(){a.shortcuts.add("ctrl+a",null,"SelectAll")}function p(){a.settings.content_editable||(q.bind(a.getDoc(),"focusin",function(){j.setRng(j.getRng())}),q.bind(a.getDoc(),"mousedown mouseup",function(e){e.target==a.getDoc().documentElement&&(a.getBody().focus(),"mousedown"==e.type?j.placeCaretAt(e.clientX,e.clientY):j.setRng(j.getRng()))}))}function h(){a.on("keydown",function(e){if(!c(e)&&e.keyCode===U){if(!a.getBody().getElementsByTagName("hr").length)return;if(j.isCollapsed()&&0===j.getRng(!0).startOffset){var t=j.getNode(),n=t.previousSibling;if("HR"==t.nodeName)return q.remove(t),void e.preventDefault();n&&n.nodeName&&"hr"===n.nodeName.toLowerCase()&&(q.remove(n),e.preventDefault())}}})}function m(){window.Range.prototype.getClientRects||a.on("mousedown",function(e){if(!c(e)&&"HTML"===e.target.nodeName){var t=a.getBody();t.blur(),setTimeout(function(){t.focus()},0)}})}function g(){a.on("click",function(e){var t=e.target;/^(IMG|HR)$/.test(t.nodeName)&&(e.preventDefault(),j.getSel().setBaseAndExtent(t,0,t,1),a.nodeChanged()),"A"==t.nodeName&&q.hasClass(t,"mce-item-anchor")&&(e.preventDefault(),j.select(t))})}function v(){function e(){var e=q.getAttribs(j.getStart().cloneNode(!1));return function(){var t=j.getStart();t!==a.getBody()&&(q.setAttrib(t,"style",null),V(e,function(e){t.setAttributeNode(e.cloneNode(!0))}))}}function t(){return!j.isCollapsed()&&q.getParent(j.getStart(),q.isBlock)!=q.getParent(j.getEnd(),q.isBlock)}a.on("keypress",function(n){var r;return c(n)||8!=n.keyCode&&46!=n.keyCode||!t()?void 0:(r=e(),a.getDoc().execCommand("delete",!1,null),r(),n.preventDefault(),!1)}),q.bind(a.getDoc(),"cut",function(n){var r;!c(n)&&t()&&(r=e(),setTimeout(function(){r()},0))})}function y(){document.body.setAttribute("role","application")}function b(){a.on("keydown",function(e){if(!c(e)&&e.keyCode===U&&j.isCollapsed()&&0===j.getRng(!0).startOffset){var t=j.getNode().previousSibling;if(t&&t.nodeName&&"table"===t.nodeName.toLowerCase())return e.preventDefault(),!1}})}function C(){l()>7||(s("RespectVisibilityInDesign",!0),a.contentStyles.push(".mceHideBrInPre pre br {display: none}"),q.addClass(a.getBody(),"mceHideBrInPre"),K.addNodeFilter("pre",function(e){for(var t=e.length,r,i,o,a;t--;)for(r=e[t].getAll("br"),i=r.length;i--;)o=r[i],a=o.prev,a&&3===a.type&&"\n"!=a.value.charAt(a.value-1)?a.value+="\n":o.parent.insert(new n("#text",3),o,!0).value="\n"}),G.addNodeFilter("pre",function(e){for(var t=e.length,n,r,i,o;t--;)for(n=e[t].getAll("br"),r=n.length;r--;)i=n[r],o=i.prev,o&&3==o.type&&(o.value=o.value.replace(/\r?\n$/,""))}))}function x(){q.bind(a.getBody(),"mouseup",function(){var e,t=j.getNode();"IMG"==t.nodeName&&((e=q.getStyle(t,"width"))&&(q.setAttrib(t,"width",e.replace(/[^0-9%]+/g,"")),q.setStyle(t,"width","")),(e=q.getStyle(t,"height"))&&(q.setAttrib(t,"height",e.replace(/[^0-9%]+/g,"")),q.setStyle(t,"height","")))})}function w(){a.on("keydown",function(t){var n,r,i,o,s;if(!c(t)&&t.keyCode==e.BACKSPACE&&(n=j.getRng(),r=n.startContainer,i=n.startOffset,o=q.getRoot(),s=r,n.collapsed&&0===i)){for(;s&&s.parentNode&&s.parentNode.firstChild==s&&s.parentNode!=o;)s=s.parentNode;"BLOCKQUOTE"===s.tagName&&(a.formatter.toggle("blockquote",null,s),n=q.createRng(),n.setStart(r,0),n.setEnd(r,0),j.setRng(n))}})}function _(){function e(){a._refreshContentEditable(),s("StyleWithCSS",!1),s("enableInlineTableEditing",!1),Y.object_resizing||s("enableObjectResizing",!1)}Y.readonly||a.on("BeforeExecCommand MouseDown",e)}function E(){function e(){V(q.select("a"),function(e){var t=e.parentNode,n=q.getRoot();if(t.lastChild===e){for(;t&&!q.isBlock(t);){if(t.parentNode.lastChild!==t||t===n)return;t=t.parentNode}q.add(t,"br",{"data-mce-bogus":1})}})}a.on("SetContent ExecCommand",function(t){("setcontent"==t.type||"mceInsertLink"===t.command)&&e()})}function N(){Y.forced_root_block&&a.on("init",function(){s("DefaultParagraphSeparator",Y.forced_root_block)})}function k(){a.on("Undo Redo SetContent",function(e){e.initial||a.execCommand("mceRepaint")})}function S(){a.on("keydown",function(e){var t;c(e)||e.keyCode!=U||(t=a.getDoc().selection.createRange(),t&&t.item&&(e.preventDefault(),a.undoManager.beforeChange(),q.remove(t.item(0)),a.undoManager.add()))})}function T(){var e;l()>=10&&(e="",V("p div h1 h2 h3 h4 h5 h6".split(" "),function(t,n){e+=(n>0?",":"")+t+":empty"}),a.contentStyles.push(e+"{padding-right: 1px !important}"))}function R(){l()<9&&(K.addNodeFilter("noscript",function(e){for(var t=e.length,n,r;t--;)n=e[t],r=n.firstChild,r&&n.attr("data-mce-innertext",r.value)}),G.addNodeFilter("noscript",function(e){for(var t=e.length,i,o,a;t--;)i=e[t],o=e[t].firstChild,o?o.value=r.decode(o.value):(a=i.attributes.map["data-mce-innertext"],a&&(i.attr("data-mce-innertext",null),o=new n("#text",3),o.value=a,o.raw=!0,i.append(o)))}))}function A(){function e(e,t){var n=i.createTextRange();try{n.moveToPoint(e,t)}catch(r){n=null}return n}function t(t){var r;t.button?(r=e(t.x,t.y),r&&(r.compareEndPoints("StartToStart",a)>0?r.setEndPoint("StartToStart",a):r.setEndPoint("EndToEnd",a),r.select())):n()}function n(){var e=r.selection.createRange();a&&!e.item&&0===e.compareEndPoints("StartToEnd",e)&&a.select(),q.unbind(r,"mouseup",n),q.unbind(r,"mousemove",t),a=o=0}var r=q.doc,i=r.body,o,a,s;r.documentElement.unselectable=!0,q.bind(r,"mousedown contextmenu",function(i){if("HTML"===i.target.nodeName){if(o&&n(),s=r.documentElement,s.scrollHeight>s.clientHeight)return;o=1,a=e(i.x,i.y),a&&(q.bind(r,"mouseup",n),q.bind(r,"mousemove",t),q.getRoot().focus(),a.select())}})}function B(){a.on("keyup focusin mouseup",function(t){65==t.keyCode&&e.metaKeyPressed(t)||j.normalize()},!0)}function D(){a.contentStyles.push("img:-moz-broken {-moz-force-broken-image-icon:1;min-width:24px;min-height:24px}")}function L(){a.inline||a.on("keydown",function(){document.activeElement==document.body&&a.getWin().focus()})}function H(){a.inline||(a.contentStyles.push("body {min-height: 150px}"),a.on("click",function(e){"HTML"==e.target.nodeName&&(a.getBody().focus(),a.selection.normalize(),a.nodeChanged())}))}function M(){i.mac&&a.on("keydown",function(t){!e.metaKeyPressed(t)||37!=t.keyCode&&39!=t.keyCode||(t.preventDefault(),a.selection.getSel().modify("move",37==t.keyCode?"backward":"forward","word"))})}function P(){s("AutoUrlDetect",!1)}function O(){a.inline||a.on("focus blur beforegetcontent",function(){var e=a.dom.create("br");a.getBody().appendChild(e),e.parentNode.removeChild(e)},!0)}function I(){a.on("click",function(e){var t=e.target;do if("A"===t.tagName)return void e.preventDefault();while(t=t.parentNode)}),a.contentStyles.push(".mce-content-body {-webkit-touch-callout: none}")}function F(){a.on("touchstart",function(e){var t,n,r,i;t=e.target,n=(new Date).getTime(),i=e.changedTouches,!i||i.length>1||(r=i[0],a.once("touchend",function(e){var i=e.changedTouches[0],o;(new Date).getTime()-n>500||Math.abs(r.clientX-i.clientX)>5||Math.abs(r.clientY-i.clientY)>5||(o={target:t},V("pageX pageY clientX clientY screenX screenY".split(" "),function(e){o[e]=i[e]}),o=a.fire("click",o),o.isDefaultPrevented()||(a.selection.placeCaretAt(i.clientX,i.clientY),a.nodeChanged()))}))})}function z(){a.on("init",function(){a.dom.bind(a.getBody(),"submit",function(e){e.preventDefault()})})}function W(){K.addNodeFilter("br",function(e){for(var t=e.length;t--;)"Apple-interchange-newline"==e[t].attr("class")&&e[t].remove()})}var V=o.each,U=e.BACKSPACE,$=e.DELETE,q=a.dom,j=a.selection,Y=a.settings,K=a.parser,G=a.serializer,X=i.gecko,J=i.ie,Q=i.webkit;w(),d(),B(),Q&&(u(),p(),g(),N(),z(),b(),W(),F(),i.iOS?(L(),H(),I()):f()),J&&i.ie<11&&(h(),y(),C(),x(),S(),T(),R(),A()),i.ie>=11&&(H(),O(),b()),i.ie&&(f(),P()),X&&(h(),m(),v(),_(),E(),k(),D(),M(),b())}}),r(lt,[$],function(e){function t(t){return t._eventDispatcher||(t._eventDispatcher=new e({scope:t,toggleEvent:function(n,r){e.isNative(n)&&t.toggleNativeEvent&&t.toggleNativeEvent(n,r)}})),t._eventDispatcher}return{fire:function(e,n,r){var i=this;if(i.removed&&"remove"!==e)return n;if(n=t(i).fire(e,n,r),r!==!1&&i.parent)for(var o=i.parent();o&&!n.isPropagationStopped();)o.fire(e,n,!1),o=o.parent();return n},on:function(e,n,r){return t(this).on(e,n,r)},off:function(e,n){return t(this).off(e,n)},once:function(e,n){return t(this).once(e,n)},hasEventListeners:function(e){return t(this).has(e)}}}),r(ct,[lt,y,d],function(e,t,n){function r(e,t){return"selectionchange"==t?e.getDoc():!e.inline&&/^mouse|click|contextmenu|drop|dragover|dragend/.test(t)?e.getDoc().documentElement:e.settings.event_root?(e.eventRoot||(e.eventRoot=o.select(e.settings.event_root)[0]),e.eventRoot):e.getBody()}function i(e,t){var n=r(e,t),i;if(e.delegates||(e.delegates={}),!e.delegates[t])if(e.settings.event_root){if(a||(a={},e.editorManager.on("removeEditor",function(){var t;if(!e.editorManager.activeEditor&&a){for(t in a)e.dom.unbind(r(e,t));a=null}})),a[t])return;i=function(n){for(var r=n.target,i=e.editorManager.editors,a=i.length;a--;){var s=i[a].getBody();(s===r||o.isChildOf(r,s))&&(i[a].hidden||i[a].fire(t,n))}},a[t]=i,o.bind(n,t,i)}else i=function(n){e.hidden||e.fire(t,n)},o.bind(n,t,i),e.delegates[t]=i}var o=t.DOM,a,s={bindPendingEventDelegates:function(){var e=this;n.each(e._pendingNativeEvents,function(t){i(e,t)})},toggleNativeEvent:function(e,t){var n=this;n.settings.readonly||"focus"!=e&&"blur"!=e&&(t?n.initialized?i(n,e):n._pendingNativeEvents?n._pendingNativeEvents.push(e):n._pendingNativeEvents=[e]:n.initialized&&(n.dom.unbind(r(n,e),e,n.delegates[e]),delete n.delegates[e]))},unbindAllNativeEvents:function(){var e=this,t;if(e.delegates){for(t in e.delegates)e.dom.unbind(r(e,t),t,e.delegates[t]);delete e.delegates}e.inline||(e.getBody().onload=null,e.dom.unbind(e.getWin()),e.dom.unbind(e.getDoc())),e.dom.unbind(e.getBody()),e.dom.unbind(e.getContainer())}};return s=n.extend({},e,s)}),r(ut,[d,u],function(e,t){var n=e.each,r=e.explode,i={f9:120,f10:121,f11:122};return function(o){var a=this,s={};o.on("keyup keypress keydown",function(e){(e.altKey||e.ctrlKey||e.metaKey)&&!e.isDefaultPrevented()&&n(s,function(n){var r=t.mac?e.metaKey:e.ctrlKey;if(n.ctrl==r&&n.alt==e.altKey&&n.shift==e.shiftKey)return e.keyCode==n.keyCode||e.charCode&&e.charCode==n.charCode?(e.preventDefault(),"keydown"==e.type&&n.func.call(n.scope),!0):void 0 +})}),a.add=function(t,a,l,c){var u;return u=l,"string"==typeof l?l=function(){o.execCommand(u,!1,null)}:e.isArray(u)&&(l=function(){o.execCommand(u[0],u[1],u[2])}),n(r(t.toLowerCase()),function(e){var t={func:l,scope:c||o,desc:o.translate(a),alt:!1,ctrl:!1,shift:!1};n(r(e,"+"),function(e){switch(e){case"alt":case"ctrl":case"shift":t[e]=!0;break;default:/^[0-9]{2,}$/.test(e)?t.keyCode=parseInt(e,10):(t.charCode=e.charCodeAt(0),t.keyCode=i[e]||e.toUpperCase().charCodeAt(0))}}),s[(t.ctrl?"ctrl":"")+","+(t.alt?"alt":"")+","+(t.shift?"shift":"")+","+t.keyCode]=t}),!0}}}),r(dt,[y,f,C,w,_,R,T,H,O,I,F,z,W,V,b,l,at,E,k,st,u,d,ct,ut],function(e,n,r,i,o,a,s,l,c,u,d,f,p,h,m,g,v,y,b,C,x,w,_,E){function N(e,t,i){var o=this,a,s;a=o.documentBaseUrl=i.documentBaseURL,s=i.baseURI,o.settings=t=R({id:e,theme:"modern",delta_width:0,delta_height:0,popup_css:"",plugins:"",document_base_url:a,add_form_submit_trigger:!0,submit_patch:!0,add_unload_trigger:!0,convert_urls:!0,relative_urls:!0,remove_script_host:!0,object_resizing:!0,doctype:"",visual:!0,font_size_style_values:"xx-small,x-small,small,medium,large,x-large,xx-large",font_size_legacy_values:"xx-small,small,medium,large,x-large,xx-large,300%",forced_root_block:"p",hidden_input:!0,padd_empty_editor:!0,render_ui:!0,indentation:"30px",inline_styles:!0,convert_fonts_to_spans:!0,indent:"simple",indent_before:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,ol,li,dl,dt,dd,area,table,thead,tfoot,tbody,tr,section,article,hgroup,aside,figure,option,optgroup,datalist",indent_after:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,ol,li,dl,dt,dd,area,table,thead,tfoot,tbody,tr,section,article,hgroup,aside,figure,option,optgroup,datalist",validate:!0,entity_encoding:"named",url_converter:o.convertURL,url_converter_scope:o,ie7_compat:!0},t),r.language=t.language||"en",r.languageLoad=t.language_load,r.baseURL=i.baseURL,o.id=t.id=e,o.isNotDirty=!0,o.plugins={},o.documentBaseURI=new h(t.document_base_url||a,{base_uri:s}),o.baseURI=s,o.contentCSS=[],o.contentStyles=[],o.shortcuts=new E(o),o.execCommands={},o.queryStateCommands={},o.queryValueCommands={},o.loadedCSS={},t.target&&(o.targetElm=t.target),o.suffix=i.suffix,o.editorManager=i,o.inline=t.inline,t.cache_suffix&&(x.cacheSuffix=t.cache_suffix.replace(/^[\?\&]+/,"")),i.fire("SetupEditor",o),o.execCallback("setup",o),o.$=n.overrideDefaults(function(){return{context:o.inline?o.getBody():o.getDoc(),element:o.getBody()}})}var k=e.DOM,S=r.ThemeManager,T=r.PluginManager,R=w.extend,A=w.each,B=w.explode,D=w.inArray,L=w.trim,H=w.resolve,M=g.Event,P=x.gecko,O=x.ie;return N.prototype={render:function(){function e(){k.unbind(window,"ready",e),n.render()}function t(){var e=m.ScriptLoader;if(r.language&&"en"!=r.language&&!r.language_url&&(r.language_url=n.editorManager.baseURL+"/langs/"+r.language+".js"),r.language_url&&e.add(r.language_url),r.theme&&"function"!=typeof r.theme&&"-"!=r.theme.charAt(0)&&!S.urls[r.theme]){var t=r.theme_url;t=t?n.documentBaseURI.toAbsolute(t):"themes/"+r.theme+"/theme"+o+".js",S.load(r.theme,t)}w.isArray(r.plugins)&&(r.plugins=r.plugins.join(" ")),A(r.external_plugins,function(e,t){T.load(t,e),r.plugins+=" "+t}),A(r.plugins.split(/[ ,]/),function(e){if(e=L(e),e&&!T.urls[e])if("-"==e.charAt(0)){e=e.substr(1,e.length);var t=T.dependencies(e);A(t,function(e){var t={prefix:"plugins/",resource:e,suffix:"/plugin"+o+".js"};e=T.createUrl(t,e),T.load(e.resource,e)})}else T.load(e,{prefix:"plugins/",resource:e,suffix:"/plugin"+o+".js"})}),e.loadQueue(function(){n.removed||n.init()})}var n=this,r=n.settings,i=n.id,o=n.suffix;if(!M.domLoaded)return void k.bind(window,"ready",e);if(n.getElement()&&x.contentEditable){r.inline?n.inline=!0:(n.orgVisibility=n.getElement().style.visibility,n.getElement().style.visibility="hidden");var a=n.getElement().form||k.getParent(i,"form");a&&(n.formElement=a,r.hidden_input&&!/TEXTAREA|INPUT/i.test(n.getElement().nodeName)&&(k.insertAfter(k.create("input",{type:"hidden",name:i}),i),n.hasHiddenInput=!0),n.formEventDelegate=function(e){n.fire(e.type,e)},k.bind(a,"submit reset",n.formEventDelegate),n.on("reset",function(){n.setContent(n.startContent,{format:"raw"})}),!r.submit_patch||a.submit.nodeType||a.submit.length||a._mceOldSubmit||(a._mceOldSubmit=a.submit,a.submit=function(){return n.editorManager.triggerSave(),n.isNotDirty=!0,a._mceOldSubmit(a)})),n.windowManager=new v(n),"xml"==r.encoding&&n.on("GetContent",function(e){e.save&&(e.content=k.encode(e.content))}),r.add_form_submit_trigger&&n.on("submit",function(){n.initialized&&n.save()}),r.add_unload_trigger&&(n._beforeUnload=function(){!n.initialized||n.destroyed||n.isHidden()||n.save({format:"raw",no_events:!0,set_dirty:!1})},n.editorManager.on("BeforeUnload",n._beforeUnload)),t()}},init:function(){function e(n){var r=T.get(n),i,o;i=T.urls[n]||t.documentBaseUrl.replace(/\/$/,""),n=L(n),r&&-1===D(m,n)&&(A(T.dependencies(n),function(t){e(t)}),o=new r(t,i,t.$),t.plugins[n]=o,o.init&&(o.init(t,i),m.push(n)))}var t=this,n=t.settings,r=t.getElement(),i,o,a,s,l,c,u,d,f,p,h,m=[];if(t.rtl=this.editorManager.i18n.rtl,t.editorManager.add(t),n.aria_label=n.aria_label||k.getAttrib(r,"aria-label",t.getLang("aria.rich_text_area")),n.theme&&("function"!=typeof n.theme?(n.theme=n.theme.replace(/-/,""),c=S.get(n.theme),t.theme=new c(t,S.urls[n.theme]),t.theme.init&&t.theme.init(t,S.urls[n.theme]||t.documentBaseUrl.replace(/\/$/,""),t.$)):t.theme=n.theme),A(n.plugins.replace(/\-/g,"").split(/[ ,]/),e),n.render_ui&&t.theme&&(t.orgDisplay=r.style.display,"function"!=typeof n.theme?(i=n.width||r.style.width||r.offsetWidth,o=n.height||r.style.height||r.offsetHeight,a=n.min_height||100,p=/^[0-9\.]+(|px)$/i,p.test(""+i)&&(i=Math.max(parseInt(i,10),100)),p.test(""+o)&&(o=Math.max(parseInt(o,10),a)),l=t.theme.renderUI({targetNode:r,width:i,height:o,deltaWidth:n.delta_width,deltaHeight:n.delta_height}),n.content_editable||(o=(l.iframeHeight||o)+("number"==typeof o?l.deltaHeight||0:""),a>o&&(o=a))):(l=n.theme(t,r),l.editorContainer.nodeType&&(l.editorContainer=l.editorContainer.id=l.editorContainer.id||t.id+"_parent"),l.iframeContainer.nodeType&&(l.iframeContainer=l.iframeContainer.id=l.iframeContainer.id||t.id+"_iframecontainer"),o=l.iframeHeight||r.offsetHeight),t.editorContainer=l.editorContainer),n.content_css&&A(B(n.content_css),function(e){t.contentCSS.push(t.documentBaseURI.toAbsolute(e))}),n.content_style&&t.contentStyles.push(n.content_style),n.content_editable)return r=s=l=null,t.initContentBody();for(t.iframeHTML=n.doctype+"",n.document_base_url!=t.documentBaseUrl&&(t.iframeHTML+=''),!x.caretAfter&&n.ie7_compat&&(t.iframeHTML+=''),t.iframeHTML+='',h=0;h',t.loadedCSS[g]=!0}d=n.body_id||"tinymce",-1!=d.indexOf("=")&&(d=t.getParam("body_id","","hash"),d=d[t.id]||d),f=n.body_class||"",-1!=f.indexOf("=")&&(f=t.getParam("body_class","","hash"),f=f[t.id]||""),n.content_security_policy&&(t.iframeHTML+=''),t.iframeHTML+='
        ';var v='javascript:(function(){document.open();document.domain="'+document.domain+'";var ed = window.parent.tinymce.get("'+t.id+'");document.write(ed.iframeHTML);document.close();ed.initContentBody(true);})()';document.domain!=location.hostname&&(u=v);var y=k.create("iframe",{id:t.id+"_ifr",frameBorder:"0",allowTransparency:"true",title:t.editorManager.translate("Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help"),style:{width:"100%",height:o,display:"block"}});if(y.onload=function(){y.onload=null,t.fire("load")},k.setAttrib(y,"src",u||'javascript:""'),t.contentAreaContainer=l.iframeContainer,t.iframeElement=y,s=k.add(l.iframeContainer,y),O)try{t.getDoc()}catch(b){s.src=u=v}l.editorContainer&&(k.get(l.editorContainer).style.display=t.orgDisplay,t.hidden=k.isHidden(l.editorContainer)),t.getElement().style.display="none",k.setAttrib(t.id,"aria-hidden",!0),u||t.initContentBody(),r=s=l=null},initContentBody:function(t){var n=this,r=n.settings,s=n.getElement(),h=n.getDoc(),m,g;r.inline||(n.getElement().style.visibility=n.orgVisibility),t||r.content_editable||(h.open(),h.write(n.iframeHTML),h.close()),r.content_editable&&(n.on("remove",function(){var e=this.getBody();k.removeClass(e,"mce-content-body"),k.removeClass(e,"mce-edit-focus"),k.setAttrib(e,"contentEditable",null)}),k.addClass(s,"mce-content-body"),n.contentDocument=h=r.content_document||document,n.contentWindow=r.content_window||window,n.bodyElement=s,r.content_document=r.content_window=null,r.root_name=s.nodeName.toLowerCase()),m=n.getBody(),m.disabled=!0,r.readonly||(n.inline&&"static"==k.getStyle(m,"position",!0)&&(m.style.position="relative"),m.contentEditable=n.getParam("content_editable_state",!0)),m.disabled=!1,n.schema=new y(r),n.dom=new e(h,{keep_values:!0,url_converter:n.convertURL,url_converter_scope:n,hex_colors:r.force_hex_style_colors,class_filter:r.class_filter,update_styles:!0,root_element:n.inline?n.getBody():null,collect:r.content_editable,schema:n.schema,onSetAttrib:function(e){n.fire("SetAttrib",e)}}),n.parser=new b(r,n.schema),n.parser.addAttributeFilter("src,href,style,tabindex",function(e,t){for(var r=e.length,i,o=n.dom,a,s;r--;)i=e[r],a=i.attr(t),s="data-mce-"+t,i.attributes.map[s]||("style"===t?(a=o.serializeStyle(o.parseStyle(a),i.name),a.length||(a=null),i.attr(s,a),i.attr(t,a)):"tabindex"===t?(i.attr(s,a),i.attr(t,null)):i.attr(s,n.convertURL(a,t,i.name)))}),n.parser.addNodeFilter("script",function(e){for(var t=e.length,n;t--;)n=e[t],n.attr("type","mce-"+(n.attr("type")||"no/type"))}),n.parser.addNodeFilter("#cdata",function(e){for(var t=e.length,n;t--;)n=e[t],n.type=8,n.name="#comment",n.value="[CDATA["+n.value+"]]"}),n.parser.addNodeFilter("p,h1,h2,h3,h4,h5,h6,div",function(e){for(var t=e.length,r,i=n.schema.getNonEmptyElements();t--;)r=e[t],r.isEmpty(i)&&(r.append(new o("br",1)).shortEnded=!0)}),n.serializer=new a(r,n),n.selection=new l(n.dom,n.getWin(),n.serializer,n),n.formatter=new c(n),n.undoManager=new u(n),n.forceBlocks=new f(n),n.enterKey=new d(n),n.editorCommands=new p(n),n._nodeChangeDispatcher=new i(n),n.fire("PreInit"),r.browser_spellcheck||r.gecko_spellcheck||(h.body.spellcheck=!1,k.setAttrib(m,"spellcheck","false")),n.fire("PostRender"),n.quirks=new C(n),r.directionality&&(m.dir=r.directionality),r.nowrap&&(m.style.whiteSpace="nowrap"),r.protect&&n.on("BeforeSetContent",function(e){A(r.protect,function(t){e.content=e.content.replace(t,function(e){return""})})}),n.on("SetContent",function(){n.addVisual(n.getBody())}),r.padd_empty_editor&&n.on("PostProcess",function(e){e.content=e.content.replace(/^(]*>( | |\s|\u00a0|)<\/p>[\r\n]*|
        [\r\n]*)$/,"")}),n.load({initial:!0,format:"html"}),n.startContent=n.getContent({format:"raw"}),n.initialized=!0,n.bindPendingEventDelegates(),n.fire("init"),n.focus(!0),n.nodeChanged({initial:!0}),n.execCallback("init_instance_callback",n),n.contentStyles.length>0&&(g="",A(n.contentStyles,function(e){g+=e+"\r\n"}),n.dom.addStyle(g)),A(n.contentCSS,function(e){n.loadedCSS[e]||(n.dom.loadCSS(e),n.loadedCSS[e]=!0)}),r.auto_focus&&setTimeout(function(){var e;e=r.auto_focus===!0?n:n.editorManager.get(r.auto_focus),e.focus()},100),s=h=m=null},focus:function(e){var t=this,n=t.selection,r=t.settings.content_editable,i,o,a=t.getDoc(),s;if(!e){if(i=n.getRng(),i.item&&(o=i.item(0)),t._refreshContentEditable(),r||(x.opera||t.getBody().focus(),t.getWin().focus()),P||r){if(s=t.getBody(),s.setActive)try{s.setActive()}catch(l){s.focus()}else s.focus();r&&n.normalize()}o&&o.ownerDocument==a&&(i=a.body.createControlRange(),i.addElement(o),i.select())}t.editorManager.setActive(t)},execCallback:function(e){var t=this,n=t.settings[e],r;if(n)return t.callbackLookup&&(r=t.callbackLookup[e])&&(n=r.func,r=r.scope),"string"==typeof n&&(r=n.replace(/\.\w+$/,""),r=r?H(r):0,n=H(n),t.callbackLookup=t.callbackLookup||{},t.callbackLookup[e]={func:n,scope:r}),n.apply(r||t,Array.prototype.slice.call(arguments,1))},translate:function(e){var t=this.settings.language||"en",n=this.editorManager.i18n;return e?n.data[t+"."+e]||e.replace(/\{\#([^\}]+)\}/g,function(e,r){return n.data[t+"."+r]||"{#"+r+"}"}):""},getLang:function(e,n){return this.editorManager.i18n.data[(this.settings.language||"en")+"."+e]||(n!==t?n:"{#"+e+"}")},getParam:function(e,t,n){var r=e in this.settings?this.settings[e]:t,i;return"hash"===n?(i={},"string"==typeof r?A(r.split(r.indexOf("=")>0?/[;,](?![^=;,]*(?:[;,]|$))/:","),function(e){e=e.split("="),i[L(e[0])]=L(e.length>1?e[1]:e)}):i=r,i):r},nodeChanged:function(e){this._nodeChangeDispatcher.nodeChanged(e)},addButton:function(e,t){var n=this;t.cmd&&(t.onclick=function(){n.execCommand(t.cmd)}),t.text||t.icon||(t.icon=e),n.buttons=n.buttons||{},t.tooltip=t.tooltip||t.title,n.buttons[e]=t},addMenuItem:function(e,t){var n=this;t.cmd&&(t.onclick=function(){n.execCommand(t.cmd)}),n.menuItems=n.menuItems||{},n.menuItems[e]=t},addCommand:function(e,t,n){this.execCommands[e]={func:t,scope:n||this}},addQueryStateHandler:function(e,t,n){this.queryStateCommands[e]={func:t,scope:n||this}},addQueryValueHandler:function(e,t,n){this.queryValueCommands[e]={func:t,scope:n||this}},addShortcut:function(e,t,n,r){this.shortcuts.add(e,t,n,r)},execCommand:function(e,t,n,r){var i=this,o=0,a;if(/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint)$/.test(e)||r&&r.skip_focus||i.focus(),r=R({},r),r=i.fire("BeforeExecCommand",{command:e,ui:t,value:n}),r.isDefaultPrevented())return!1;if((a=i.execCommands[e])&&a.func.call(a.scope,t,n)!==!0)return i.fire("ExecCommand",{command:e,ui:t,value:n}),!0;if(A(i.plugins,function(r){return r.execCommand&&r.execCommand(e,t,n)?(i.fire("ExecCommand",{command:e,ui:t,value:n}),o=!0,!1):void 0}),o)return o;if(i.theme&&i.theme.execCommand&&i.theme.execCommand(e,t,n))return i.fire("ExecCommand",{command:e,ui:t,value:n}),!0;if(i.editorCommands.execCommand(e,t,n))return i.fire("ExecCommand",{command:e,ui:t,value:n}),!0;try{o=i.getDoc().execCommand(e,t,n)}catch(s){}return o?(i.fire("ExecCommand",{command:e,ui:t,value:n}),!0):!1},queryCommandState:function(e){var t=this,n,r;if(!t._isHidden()){if((n=t.queryStateCommands[e])&&(r=n.func.call(n.scope),r===!0||r===!1))return r;if(r=t.editorCommands.queryCommandState(e),-1!==r)return r;try{return t.getDoc().queryCommandState(e)}catch(i){}}},queryCommandValue:function(e){var n=this,r,i;if(!n._isHidden()){if((r=n.queryValueCommands[e])&&(i=r.func.call(r.scope),i!==!0))return i;if(i=n.editorCommands.queryCommandValue(e),i!==t)return i;try{return n.getDoc().queryCommandValue(e)}catch(o){}}},show:function(){var e=this;e.hidden&&(e.hidden=!1,e.inline?e.getBody().contentEditable=!0:(k.show(e.getContainer()),k.hide(e.id)),e.load(),e.fire("show"))},hide:function(){var e=this,t=e.getDoc();e.hidden||(O&&t&&!e.inline&&t.execCommand("SelectAll"),e.save(),e.inline?(e.getBody().contentEditable=!1,e==e.editorManager.focusedEditor&&(e.editorManager.focusedEditor=null)):(k.hide(e.getContainer()),k.setStyle(e.id,"display",e.orgDisplay)),e.hidden=!0,e.fire("hide"))},isHidden:function(){return!!this.hidden},setProgressState:function(e,t){this.fire("ProgressState",{state:e,time:t})},load:function(e){var n=this,r=n.getElement(),i;return r?(e=e||{},e.load=!0,i=n.setContent(r.value!==t?r.value:r.innerHTML,e),e.element=r,e.no_events||n.fire("LoadContent",e),e.element=r=null,i):void 0},save:function(e){var t=this,n=t.getElement(),r,i;if(n&&t.initialized)return e=e||{},e.save=!0,e.element=n,r=e.content=t.getContent(e),e.no_events||t.fire("SaveContent",e),r=e.content,/TEXTAREA|INPUT/i.test(n.nodeName)?n.value=r:(t.inline||(n.innerHTML=r),(i=k.getParent(t.id,"form"))&&A(i.elements,function(e){return e.name==t.id?(e.value=r,!1):void 0})),e.element=n=null,e.set_dirty!==!1&&(t.isNotDirty=!0),r},setContent:function(e,t){var n=this,r=n.getBody(),i;return t=t||{},t.format=t.format||"html",t.set=!0,t.content=e,t.no_events||n.fire("BeforeSetContent",t),e=t.content,0===e.length||/^\s+$/.test(e)?(i=n.settings.forced_root_block,i&&n.schema.isValidChild(r.nodeName.toLowerCase(),i.toLowerCase())?(e=O&&11>O?"":'
        ',e=n.dom.createHTML(i,n.settings.forced_root_block_attrs,e)):O||(e='
        '),n.dom.setHTML(r,e),n.fire("SetContent",t)):("raw"!==t.format&&(e=new s({},n.schema).serialize(n.parser.parse(e,{isRootContent:!0}))),t.content=L(e),n.dom.setHTML(r,t.content),t.no_events||n.fire("SetContent",t)),t.content},getContent:function(e){var t=this,n,r=t.getBody();return e=e||{},e.format=e.format||"html",e.get=!0,e.getInner=!0,e.no_events||t.fire("BeforeGetContent",e),n="raw"==e.format?r.innerHTML:"text"==e.format?r.innerText||r.textContent:t.serializer.serialize(r,e),e.content="text"!=e.format?L(n):n,e.no_events||t.fire("GetContent",e),e.content},insertContent:function(e,t){t&&(e=R({content:e},t)),this.execCommand("mceInsertContent",!1,e)},isDirty:function(){return!this.isNotDirty},getContainer:function(){var e=this;return e.container||(e.container=k.get(e.editorContainer||e.id+"_parent")),e.container},getContentAreaContainer:function(){return this.contentAreaContainer},getElement:function(){return this.targetElm||(this.targetElm=k.get(this.id)),this.targetElm},getWin:function(){var e=this,t;return e.contentWindow||(t=e.iframeElement,t&&(e.contentWindow=t.contentWindow)),e.contentWindow},getDoc:function(){var e=this,t;return e.contentDocument||(t=e.getWin(),t&&(e.contentDocument=t.document)),e.contentDocument},getBody:function(){return this.bodyElement||this.getDoc().body},convertURL:function(e,t,n){var r=this,i=r.settings;return i.urlconverter_callback?r.execCallback("urlconverter_callback",e,n,!0,t):!i.convert_urls||n&&"LINK"==n.nodeName||0===e.indexOf("file:")||0===e.length?e:i.relative_urls?r.documentBaseURI.toRelative(e):e=r.documentBaseURI.toAbsolute(e,i.remove_script_host)},addVisual:function(e){var n=this,r=n.settings,i=n.dom,o;e=e||n.getBody(),n.hasVisual===t&&(n.hasVisual=r.visual),A(i.select("table,a",e),function(e){var t;switch(e.nodeName){case"TABLE":return o=r.visual_table_class||"mce-item-table",t=i.getAttrib(e,"border"),void(t&&"0"!=t||!n.hasVisual?i.removeClass(e,o):i.addClass(e,o));case"A":return void(i.getAttrib(e,"href",!1)||(t=i.getAttrib(e,"name")||e.id,o=r.visual_anchor_class||"mce-item-anchor",t&&n.hasVisual?i.addClass(e,o):i.removeClass(e,o)))}}),n.fire("VisualAid",{element:e,hasVisual:n.hasVisual})},remove:function(){var e=this;e.removed||(e.save(),e.removed=1,e.unbindAllNativeEvents(),e.hasHiddenInput&&k.remove(e.getElement().nextSibling),e.inline||(O&&10>O&&e.getDoc().execCommand("SelectAll",!1,null),k.setStyle(e.id,"display",e.orgDisplay),e.getBody().onload=null),e.fire("remove"),e.editorManager.remove(e),k.remove(e.getContainer()),e.destroy())},destroy:function(e){var t=this,n;if(!t.destroyed){if(!e&&!t.removed)return void t.remove();e||(t.editorManager.off("beforeunload",t._beforeUnload),t.theme&&t.theme.destroy&&t.theme.destroy(),t.selection.destroy(),t.dom.destroy()),n=t.formElement,n&&(n._mceOldSubmit&&(n.submit=n._mceOldSubmit,n._mceOldSubmit=null),k.unbind(n,"submit reset",t.formEventDelegate)),t.contentAreaContainer=t.formElement=t.container=t.editorContainer=null,t.bodyElement=t.contentDocument=t.contentWindow=null,t.iframeElement=t.targetElm=null,t.selection&&(t.selection=t.selection.win=t.selection.dom=t.selection.dom.doc=null),t.destroyed=1}},_refreshContentEditable:function(){var e=this,t,n;e._isHidden()&&(t=e.getBody(),n=t.parentNode,n.removeChild(t),n.appendChild(t),t.focus())},_isHidden:function(){var e;return P?(e=this.selection.getSel(),!e||!e.rangeCount||0===e.rangeCount):0}},R(N.prototype,_),N}),r(ft,[],function(){var e={};return{rtl:!1,add:function(t,n){for(var r in n)e[r]=n[r];this.rtl=this.rtl||"rtl"===e._dir},translate:function(t){if("undefined"==typeof t)return t;if("string"!=typeof t&&t.raw)return t.raw;if(t.push){var n=t.slice(1);t=(e[t[0]]||t[0]).replace(/\{([^\}]+)\}/g,function(e,t){return n[t]})}return e[t]||t},data:e}}),r(pt,[y,u],function(e,t){function n(e){function s(){try{return document.activeElement}catch(e){return document.body}}function l(e,t){if(t&&t.startContainer){if(!e.isChildOf(t.startContainer,e.getRoot())||!e.isChildOf(t.endContainer,e.getRoot()))return;return{startContainer:t.startContainer,startOffset:t.startOffset,endContainer:t.endContainer,endOffset:t.endOffset}}return t}function c(e,t){var n;return t.startContainer?(n=e.getDoc().createRange(),n.setStart(t.startContainer,t.startOffset),n.setEnd(t.endContainer,t.endOffset)):n=t,n}function u(e){return!!a.getParent(e,n.isEditorUIElement)}function d(n){var d=n.editor;d.on("init",function(){(d.inline||t.ie)&&("onbeforedeactivate"in document&&t.ie<9?d.dom.bind(d.getBody(),"beforedeactivate",function(e){if(e.target==d.getBody())try{d.lastRng=d.selection.getRng()}catch(t){}}):d.on("nodechange mouseup keyup",function(e){var t=s();"nodechange"==e.type&&e.selectionChange||(t&&t.id==d.id+"_ifr"&&(t=d.getBody()),d.dom.isChildOf(t,d.getBody())&&(d.lastRng=d.selection.getRng()))}),t.webkit&&!r&&(r=function(){var t=e.activeEditor;if(t&&t.selection){var n=t.selection.getRng();n&&!n.collapsed&&(d.lastRng=n)}},a.bind(document,"selectionchange",r)))}),d.on("setcontent",function(){d.lastRng=null}),d.on("mousedown",function(){d.selection.lastFocusBookmark=null}),d.on("focusin",function(){var t=e.focusedEditor;d.selection.lastFocusBookmark&&(d.selection.setRng(c(d,d.selection.lastFocusBookmark)),d.selection.lastFocusBookmark=null),t!=d&&(t&&t.fire("blur",{focusedEditor:d}),e.setActive(d),e.focusedEditor=d,d.fire("focus",{blurredEditor:t}),d.focus(!0)),d.lastRng=null}),d.on("focusout",function(){window.setTimeout(function(){var t=e.focusedEditor;u(s())||t!=d||(d.fire("blur",{focusedEditor:null}),e.focusedEditor=null,d.selection&&(d.selection.lastFocusBookmark=null))},0)}),i||(i=function(t){var n=e.activeEditor;n&&t.target.ownerDocument==document&&(n.selection&&t.target!=n.getBody()&&(n.selection.lastFocusBookmark=l(n.dom,n.lastRng)),t.target==document.body||u(t.target)||e.focusedEditor!=n||(n.fire("blur",{focusedEditor:null}),e.focusedEditor=null))},a.bind(document,"focusin",i)),d.inline&&!o&&(o=function(t){var n=e.activeEditor;if(n.inline&&!n.dom.isChildOf(t.target,n.getBody())){var r=n.selection.getRng();r.collapsed||(n.lastRng=r)}},a.bind(document,"mouseup",o))}function f(t){e.focusedEditor==t.editor&&(e.focusedEditor=null),e.activeEditor||(a.unbind(document,"selectionchange",r),a.unbind(document,"focusin",i),a.unbind(document,"mouseup",o),r=i=o=null)}e.on("AddEditor",d),e.on("RemoveEditor",f)}var r,i,o,a=e.DOM;return n.isEditorUIElement=function(e){return-1!==e.className.toString().indexOf("mce-")},n}),r(ht,[dt,f,y,V,u,d,lt,ft,pt],function(e,t,n,r,i,o,a,s,l){function c(e){var t=v.editors,n;delete t[e.id];for(var r=0;r0&&p(f(e),function(e){var n;(n=d.get(e))?r(e,t,n):p(document.forms,function(n){p(n.elements,function(n){n.name===e&&(e="mce_editor_"+m++,d.setAttrib(n,"id",e),r(e,t,n))})})});break;case"textareas":case"specific_textareas":p(d.select("textarea"),function(e){t.editor_deselector&&o(e,t.editor_deselector)||(!t.editor_selector||o(e,t.editor_selector))&&r(n(e),t,e)})}t.oninit&&(e=s=0,p(l,function(t){s++,t.initialized?e++:t.on("init",function(){e++,e==s&&i("oninit")}),e==s&&i("oninit")}))}var s=this,l=[];s.settings=t,d.bind(window,"ready",a)},get:function(e){return arguments.length?e in this.editors?this.editors[e]:null:this.editors},add:function(e){var t=this,n=t.editors;return n[e.id]=e,n.push(e),t.activeEditor=e,t.fire("AddEditor",{editor:e}),g||(g=function(){t.fire("BeforeUnload")},d.bind(window,"beforeunload",g)),e},createEditor:function(t,n){return this.add(new e(t,n,this))},remove:function(e){var t=this,n,r=t.editors,i;{if(e)return"string"==typeof e?(e=e.selector||e,void p(d.select(e),function(e){i=r[e.id],i&&t.remove(i)})):(i=e,r[i.id]?(c(i)&&t.fire("RemoveEditor",{editor:i}),r.length||d.unbind(window,"beforeunload",g),i.remove(),i):null);for(n=r.length-1;n>=0;n--)t.remove(r[n])}},execCommand:function(t,n,r){var i=this,o=i.get(r);switch(t){case"mceAddEditor":return i.get(r)||new e(r,i.settings,i).render(),!0;case"mceRemoveEditor":return o&&o.remove(),!0;case"mceToggleEditor":return o?(o.isHidden()?o.show():o.hide(),!0):(i.execCommand("mceAddEditor",0,r),!0)}return i.activeEditor?i.activeEditor.execCommand(t,n,r):!1},triggerSave:function(){p(this.editors,function(e){e.save()})},addI18n:function(e,t){s.add(e,t)},translate:function(e){return s.translate(e)},setActive:function(e){var t=this.activeEditor;this.activeEditor!=e&&(t&&t.fire("deactivate",{relatedTarget:e}),e.fire("activate",{relatedTarget:t})),this.activeEditor=e}},h(v,a),v.setup(),window.tinymce=window.tinyMCE=v,v}),r(mt,[ht,d],function(e,t){var n=t.each,r=t.explode;e.on("AddEditor",function(e){var t=e.editor;t.on("preInit",function(){function e(e,t){n(t,function(t,n){t&&s.setStyle(e,n,t)}),s.rename(e,"span")}function i(e){s=t.dom,l.convert_fonts_to_spans&&n(s.select("font,u,strike",e.node),function(e){o[e.nodeName.toLowerCase()](s,e)})}var o,a,s,l=t.settings;l.inline_styles&&(a=r(l.font_size_legacy_values),o={font:function(t,n){e(n,{backgroundColor:n.style.backgroundColor,color:n.color,fontFamily:n.face,fontSize:a[parseInt(n.size,10)-1]})},u:function(t,n){e(n,{textDecoration:"underline"})},strike:function(t,n){e(n,{textDecoration:"line-through"})}},t.on("PreProcess SetContent",i))})})}),r(gt,[lt,d],function(e,t){var n={send:function(e){function t(){!e.async||4==r.readyState||i++>1e4?(e.success&&1e4>i&&200==r.status?e.success.call(e.success_scope,""+r.responseText,r,e):e.error&&e.error.call(e.error_scope,i>1e4?"TIMED_OUT":"GENERAL",r,e),r=null):setTimeout(t,10)}var r,i=0;if(e.scope=e.scope||this,e.success_scope=e.success_scope||e.scope,e.error_scope=e.error_scope||e.scope,e.async=e.async===!1?!1:!0,e.data=e.data||"",r=new XMLHttpRequest){if(r.overrideMimeType&&r.overrideMimeType(e.content_type),r.open(e.type||(e.data?"POST":"GET"),e.url,e.async),e.crossDomain&&(r.withCredentials=!0),e.content_type&&r.setRequestHeader("Content-Type",e.content_type),r.setRequestHeader("X-Requested-With","XMLHttpRequest"),r=n.fire("beforeSend",{xhr:r,settings:e}).xhr,r.send(e.data),!e.async)return t();setTimeout(t,10)}}};return t.extend(n,e),n}),r(vt,[],function(){function e(t,n){var r,i,o,a;if(n=n||'"',null===t)return"null";if(o=typeof t,"string"==o)return i="\bb t\nn\ff\rr\"\"''\\\\",n+t.replace(/([\u0080-\uFFFF\x00-\x1f\"\'\\])/g,function(e,t){return'"'===n&&"'"===e?e:(r=i.indexOf(t),r+1?"\\"+i.charAt(r+1):(e=t.charCodeAt().toString(16),"\\u"+"0000".substring(e.length)+e))})+n;if("object"==o){if(t.hasOwnProperty&&"[object Array]"===Object.prototype.toString.call(t)){for(r=0,i="[";r0?",":"")+e(t[r],n);return i+"]"}i="{";for(a in t)t.hasOwnProperty(a)&&(i+="function"!=typeof t[a]?(i.length>1?","+n:n)+a+n+":"+e(t[a],n):"");return i+"}"}return""+t}return{serialize:e,parse:function(e){try{return window[String.fromCharCode(101)+"val"]("("+e+")")}catch(t){}}}}),r(yt,[vt,gt,d],function(e,t,n){function r(e){this.settings=i({},e),this.count=0}var i=n.extend;return r.sendRPC=function(e){return(new r).send(e)},r.prototype={send:function(n){var r=n.error,o=n.success;n=i(this.settings,n),n.success=function(t,i){t=e.parse(t),"undefined"==typeof t&&(t={error:"JSON Parse error."}),t.error?r.call(n.error_scope||n.scope,t.error,i):o.call(n.success_scope||n.scope,t.result)},n.error=function(e,t){r&&r.call(n.error_scope||n.scope,e,t)},n.data=e.serialize({id:n.id||"c"+this.count++,method:n.method,params:n.params}),n.content_type="application/json",t.send(n)}},r}),r(bt,[y],function(e){return{callbacks:{},count:0,send:function(n){var r=this,i=e.DOM,o=n.count!==t?n.count:r.count,a="tinymce_jsonp_"+o;r.callbacks[o]=function(e){i.remove(a),delete r.callbacks[o],n.callback(e)},i.add(i.doc.body,"script",{id:a,src:n.url,type:"text/javascript"}),r.count++}}}),r(Ct,[],function(){function e(){s=[];for(var e in a)s.push(e);i.length=s.length}function n(){function n(e){var n,r;return r=e!==t?u+e:i.indexOf(",",u),-1===r||r>i.length?null:(n=i.substring(u,r),u=r+1,n)}var r,i,s,u=0;if(a={},c){o.load(l),i=o.getAttribute(l)||"";do{var d=n();if(null===d)break;if(r=n(parseInt(d,32)||0),null!==r){if(d=n(),null===d)break;s=n(parseInt(d,32)||0),r&&(a[r]=s)}}while(null!==r);e()}}function r(){var t,n="";if(c){for(var r in a)t=a[r],n+=(n?",":"")+r.length.toString(32)+","+r+","+t.length.toString(32)+","+t;o.setAttribute(l,n);try{o.save(l)}catch(i){}e()}}var i,o,a,s,l,c;try{if(window.localStorage)return localStorage}catch(u){}return l="tinymce",o=document.documentElement,c=!!o.addBehavior,c&&o.addBehavior("#default#userData"),i={key:function(e){return s[e]},getItem:function(e){return e in a?a[e]:null},setItem:function(e,t){a[e]=""+t,r()},removeItem:function(e){delete a[e],r()},clear:function(){a={},r()}},n(),i}),r(xt,[y,l,b,C,d,u],function(e,t,n,r,i,o){var a=window.tinymce;return a.DOM=e.DOM,a.ScriptLoader=n.ScriptLoader,a.PluginManager=r.PluginManager,a.ThemeManager=r.ThemeManager,a.dom=a.dom||{},a.dom.Event=t.Event,i.each(i,function(e,t){a[t]=e}),i.each("isOpera isWebKit isIE isGecko isMac".split(" "),function(e){a[e]=o[e.substr(2).toLowerCase()]}),{}}),r(wt,[U,d],function(e,t){return e.extend({Defaults:{firstControlClass:"first",lastControlClass:"last"},init:function(e){this.settings=t.extend({},this.Defaults,e)},preRender:function(e){e.addClass(this.settings.containerClass,"body")},applyClasses:function(e){var t=this,n=t.settings,r,i,o;r=e.items().filter(":visible"),i=n.firstControlClass,o=n.lastControlClass,r.each(function(e){e.removeClass(i).removeClass(o),n.controlClass&&e.addClass(n.controlClass)}),r.eq(0).addClass(i),r.eq(-1).addClass(o)},renderHtml:function(e){var t=this,n=t.settings,r,i=""; +return r=e.items(),r.eq(0).addClass(n.firstControlClass),r.eq(-1).addClass(n.lastControlClass),r.each(function(e){n.controlClass&&e.addClass(n.controlClass),i+=e.renderHtml()}),i},recalc:function(){},postRender:function(){}})}),r(_t,[wt],function(e){return e.extend({Defaults:{containerClass:"abs-layout",controlClass:"abs-layout-item"},recalc:function(e){e.items().filter(":visible").each(function(e){var t=e.settings;e.layoutRect({x:t.x,y:t.y,w:t.w,h:t.h}),e.recalc&&e.recalc()})},renderHtml:function(e){return'
        '+this._super(e)}})}),r(Et,[K,tt],function(e,t){return e.extend({Mixins:[t],Defaults:{classes:"widget tooltip tooltip-n"},text:function(e){var t=this;return"undefined"!=typeof e?(t._value=e,t._rendered&&(t.getEl().lastChild.innerHTML=t.encode(e)),t):t._value},renderHtml:function(){var e=this,t=e.classPrefix;return'"},repaint:function(){var e=this,t,n;t=e.getEl().style,n=e._layoutRect,t.left=n.x+"px",t.top=n.y+"px",t.zIndex=131070}})}),r(Nt,[K,Et],function(e,t){var n,r=e.extend({init:function(e){var t=this;t._super(e),e=t.settings,t.canFocus=!0,e.tooltip&&r.tooltips!==!1&&(t.on("mouseenter",function(n){var r=t.tooltip().moveTo(-65535);if(n.control==t){var i=r.text(e.tooltip).show().testMoveRel(t.getEl(),["bc-tc","bc-tl","bc-tr"]);r.toggleClass("tooltip-n","bc-tc"==i),r.toggleClass("tooltip-nw","bc-tl"==i),r.toggleClass("tooltip-ne","bc-tr"==i),r.moveRel(t.getEl(),i)}else r.hide()}),t.on("mouseleave mousedown click",function(){t.tooltip().hide()})),t.aria("label",e.ariaLabel||e.tooltip)},tooltip:function(){return n||(n=new t({type:"tooltip"}),n.renderTo()),n},active:function(e){var t=this,n;return e!==n&&(t.aria("pressed",e),t.toggleClass("active",e)),t._super(e)},disabled:function(e){var t=this,n;return e!==n&&(t.aria("disabled",e),t.toggleClass("disabled",e)),t._super(e)},postRender:function(){var e=this,t=e.settings;e._rendered=!0,e._super(),e.parent()||!t.width&&!t.height||(e.initLayoutRect(),e.repaint()),t.autofocus&&e.focus()},remove:function(){this._super(),n&&(n.remove(),n=null)}});return r}),r(kt,[Nt],function(e){return e.extend({Defaults:{classes:"widget btn",role:"button"},init:function(e){var t=this,n;t.on("click mousedown",function(e){e.preventDefault()}),t._super(e),n=e.size,e.subtype&&t.addClass(e.subtype),n&&t.addClass("btn-"+n)},icon:function(e){var t=this,n=t.classPrefix;if("undefined"==typeof e)return t.settings.icon;if(t.settings.icon=e,e=e?n+"ico "+n+"i-"+t.settings.icon:"",t._rendered){var r=t.getEl().firstChild,i=r.getElementsByTagName("i")[0];e?(i&&i==r.firstChild||(i=document.createElement("i"),r.insertBefore(i,r.firstChild)),i.className=e):i&&r.removeChild(i),t.text(t._text)}return t},repaint:function(){var e=this.getEl().firstChild.style;e.width=e.height="100%",this._super()},text:function(e){var t=this;if(t._rendered){var n=t.getEl().lastChild.lastChild;n&&(n.data=t.translate(e))}return t._super(e)},renderHtml:function(){var e=this,t=e._id,n=e.classPrefix,r=e.settings.icon,i;return i=e.settings.image,i?(r="none","string"!=typeof i&&(i=window.getSelection?i[0]:i[1]),i=" style=\"background-image: url('"+i+"')\""):i="",r=e.settings.icon?n+"ico "+n+"i-"+r:"",'
        "}})}),r(St,[J],function(e){return e.extend({Defaults:{defaultType:"button",role:"group"},renderHtml:function(){var e=this,t=e._layout;return e.addClass("btn-group"),e.preRender(),t.preRender(e),'
        '+(e.settings.html||"")+t.renderHtml(e)+"
        "}})}),r(Tt,[Nt],function(e){return e.extend({Defaults:{classes:"checkbox",role:"checkbox",checked:!1},init:function(e){var t=this;t._super(e),t.on("click mousedown",function(e){e.preventDefault()}),t.on("click",function(e){e.preventDefault(),t.disabled()||t.checked(!t.checked())}),t.checked(t.settings.checked)},checked:function(e){var t=this;return"undefined"!=typeof e?(e?t.addClass("checked"):t.removeClass("checked"),t._checked=e,t.aria("checked",e),t):t._checked},value:function(e){return this.checked(e)},renderHtml:function(){var e=this,t=e._id,n=e.classPrefix;return'
        '+e.encode(e._text)+"
        "}})}),r(Rt,[Nt,G,Y],function(e,t,n){return e.extend({init:function(e){var t=this;t._super(e),t.addClass("combobox"),t.subinput=!0,t.ariaTarget="inp",e=t.settings,e.menu=e.menu||e.values,e.menu&&(e.icon="caret"),t.on("click",function(n){for(var r=n.target,i=t.getEl();r&&r!=i;)r.id&&-1!=r.id.indexOf("-open")&&(t.fire("action"),e.menu&&(t.showMenu(),n.aria&&t.menu.items()[0].focus())),r=r.parentNode}),t.on("keydown",function(e){"INPUT"==e.target.nodeName&&13==e.keyCode&&t.parents().reverse().each(function(n){return e.preventDefault(),t.fire("change"),n.hasEventListeners("submit")&&n.toJSON?(n.fire("submit",{data:n.toJSON()}),!1):void 0})}),e.placeholder&&(t.addClass("placeholder"),t.on("focusin",function(){t._hasOnChange||(n.on(t.getEl("inp"),"change",function(){t.fire("change")}),t._hasOnChange=!0),t.hasClass("placeholder")&&(t.getEl("inp").value="",t.removeClass("placeholder"))}),t.on("focusout",function(){0===t.value().length&&(t.getEl("inp").value=e.placeholder,t.addClass("placeholder"))}))},showMenu:function(){var e=this,n=e.settings,r;e.menu||(r=n.menu||[],r.length?r={type:"menu",items:r}:r.type=r.type||"menu",e.menu=t.create(r).parent(e).renderTo(e.getContainerElm()),e.fire("createmenu"),e.menu.reflow(),e.menu.on("cancel",function(t){t.control===e.menu&&e.focus()}),e.menu.on("show hide",function(t){t.control.items().each(function(t){t.active(t.value()==e.value())})}).fire("show"),e.menu.on("select",function(t){e.value(t.control.value())}),e.on("focusin",function(t){"INPUT"==t.target.tagName.toUpperCase()&&e.menu.hide()}),e.aria("expanded",!0)),e.menu.show(),e.menu.layoutRect({w:e.layoutRect().w}),e.menu.moveRel(e.getEl(),e.isRtl()?["br-tr","tr-br"]:["bl-tl","tl-bl"])},value:function(e){var t=this;return"undefined"!=typeof e?(t._value=e,t.removeClass("placeholder"),t._rendered&&(t.getEl("inp").value=e),t):t._rendered?(e=t.getEl("inp").value,e!=t.settings.placeholder?e:""):t._value},disabled:function(e){var t=this;return t._rendered&&"undefined"!=typeof e&&(t.getEl("inp").disabled=e),t._super(e)},focus:function(){this.getEl("inp").focus()},repaint:function(){var e=this,t=e.getEl(),r=e.getEl("open"),i=e.layoutRect(),o,a;o=r?i.w-n.getSize(r).width-10:i.w-10;var s=document;return s.all&&(!s.documentMode||s.documentMode<=8)&&(a=e.layoutRect().h-2+"px"),n.css(t.firstChild,{width:o,lineHeight:a}),e._super(),e},postRender:function(){var e=this;return n.on(this.getEl("inp"),"change",function(){e.fire("change")}),e._super()},remove:function(){n.off(this.getEl("inp")),this._super()},renderHtml:function(){var e=this,t=e._id,n=e.settings,r=e.classPrefix,i=n.value||n.placeholder||"",o,a,s="",l="";return"spellcheck"in n&&(l+=' spellcheck="'+n.spellcheck+'"'),n.maxLength&&(l+=' maxlength="'+n.maxLength+'"'),n.size&&(l+=' size="'+n.size+'"'),n.subtype&&(l+=' type="'+n.subtype+'"'),e.disabled()&&(l+=' disabled="disabled"'),o=n.icon,o&&"caret"!=o&&(o=r+"ico "+r+"i-"+n.icon),a=e._text,(o||a)&&(s='
        ",e.addClass("has-open")),'
        "+s+"
        "}})}),r(At,[Rt],function(e){return e.extend({init:function(e){var t=this;e.spellcheck=!1,e.onaction&&(e.icon="none"),t._super(e),t.addClass("colorbox"),t.on("change keyup postrender",function(){t.repaintColor(t.value())})},repaintColor:function(e){var t=this.getEl().getElementsByTagName("i")[0];if(t)try{t.style.background=e}catch(n){}},value:function(e){var t=this;return"undefined"!=typeof e&&t._rendered&&t.repaintColor(e),t._super(e)}})}),r(Bt,[kt,rt],function(e,t){return e.extend({showPanel:function(){var e=this,n=e.settings;if(e.active(!0),e.panel)e.panel.show();else{var r=n.panel;r.type&&(r={layout:"grid",items:r}),r.role=r.role||"dialog",r.popover=!0,r.autohide=!0,r.ariaRoot=!0,e.panel=new t(r).on("hide",function(){e.active(!1)}).on("cancel",function(t){t.stopPropagation(),e.focus(),e.hidePanel()}).parent(e).renderTo(e.getContainerElm()),e.panel.fire("show"),e.panel.reflow()}e.panel.moveRel(e.getEl(),n.popoverAlign||(e.isRtl()?["bc-tr","bc-tc"]:["bc-tl","bc-tc"]))},hidePanel:function(){var e=this;e.panel&&e.panel.hide()},postRender:function(){var e=this;return e.aria("haspopup",!0),e.on("click",function(t){t.control===e&&(e.panel&&e.panel.visible()?e.hidePanel():(e.showPanel(),e.panel.focus(!!t.aria)))}),e._super()},remove:function(){return this.panel&&(this.panel.remove(),this.panel=null),this._super()}})}),r(Dt,[Bt,y],function(e,t){var n=t.DOM;return e.extend({init:function(e){this._super(e),this.addClass("colorbutton")},color:function(e){return e?(this._color=e,this.getEl("preview").style.backgroundColor=e,this):this._color},renderHtml:function(){var e=this,t=e._id,n=e.classPrefix,r=e.settings.icon?n+"ico "+n+"i-"+e.settings.icon:"",i=e.settings.image?" style=\"background-image: url('"+e.settings.image+"')\"":"";return'
        '},postRender:function(){var e=this,t=e.settings.onclick;return e.on("click",function(r){r.aria&&"down"==r.aria.key||r.control!=e||n.getParent(r.target,"."+e.classPrefix+"open")||(r.stopImmediatePropagation(),t.call(e,r))}),delete e.settings.onclick,e._super()}})}),r(Lt,[],function(){function e(e){function i(e,i,o){var a,s,l,c,u,d;return a=0,s=0,l=0,e/=255,i/=255,o/=255,u=t(e,t(i,o)),d=n(e,n(i,o)),u==d?(l=u,{h:0,s:0,v:100*l}):(c=e==u?i-o:o==u?e-i:o-e,a=e==u?3:o==u?1:5,a=60*(a-c/(d-u)),s=(d-u)/d,l=d,{h:r(a),s:r(100*s),v:r(100*l)})}function o(e,i,o){var a,s,l,c;if(e=(parseInt(e,10)||0)%360,i=parseInt(i,10)/100,o=parseInt(o,10)/100,i=n(0,t(i,1)),o=n(0,t(o,1)),0===i)return void(d=f=p=r(255*o));switch(a=e/60,s=o*i,l=s*(1-Math.abs(a%2-1)),c=o-s,Math.floor(a)){case 0:d=s,f=l,p=0;break;case 1:d=l,f=s,p=0;break;case 2:d=0,f=s,p=l;break;case 3:d=0,f=l,p=s;break;case 4:d=l,f=0,p=s;break;case 5:d=s,f=0,p=l;break;default:d=f=p=0}d=r(255*(d+c)),f=r(255*(f+c)),p=r(255*(p+c))}function a(){function e(e){return e=parseInt(e,10).toString(16),e.length>1?e:"0"+e}return"#"+e(d)+e(f)+e(p)}function s(){return{r:d,g:f,b:p}}function l(){return i(d,f,p)}function c(e){var t;return"object"==typeof e?"r"in e?(d=e.r,f=e.g,p=e.b):"v"in e&&o(e.h,e.s,e.v):(t=/rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)[^\)]*\)/gi.exec(e))?(d=parseInt(t[1],10),f=parseInt(t[2],10),p=parseInt(t[3],10)):(t=/#([0-F]{2})([0-F]{2})([0-F]{2})/gi.exec(e))?(d=parseInt(t[1],16),f=parseInt(t[2],16),p=parseInt(t[3],16)):(t=/#([0-F])([0-F])([0-F])/gi.exec(e))&&(d=parseInt(t[1]+t[1],16),f=parseInt(t[2]+t[2],16),p=parseInt(t[3]+t[3],16)),d=0>d?0:d>255?255:d,f=0>f?0:f>255?255:f,p=0>p?0:p>255?255:p,u}var u=this,d=0,f=0,p=0;e&&c(e),u.toRgb=s,u.toHsv=l,u.toHex=a,u.parse=c}var t=Math.min,n=Math.max,r=Math.round;return e}),r(Ht,[Nt,Q,Y,Lt],function(e,t,n,r){return e.extend({Defaults:{classes:"widget colorpicker"},init:function(e){this._super(e)},postRender:function(){function e(e,t){var r=n.getPos(e),i,o;return i=t.pageX-r.x,o=t.pageY-r.y,i=Math.max(0,Math.min(i/e.clientWidth,1)),o=Math.max(0,Math.min(o/e.clientHeight,1)),{x:i,y:o}}function i(e,t){var i=(360-e.h)/360;n.css(d,{top:100*i+"%"}),t||n.css(p,{left:e.s+"%",top:100-e.v+"%"}),f.style.background=new r({s:100,v:100,h:e.h}).toHex(),s.color().parse({s:e.s,v:e.v,h:e.h})}function o(t){var n;n=e(f,t),c.s=100*n.x,c.v=100*(1-n.y),i(c),s.fire("change")}function a(t){var n;n=e(u,t),c=l.toHsv(),c.h=360*(1-n.y),i(c,!0),s.fire("change")}var s=this,l=s.color(),c,u,d,f,p;u=s.getEl("h"),d=s.getEl("hp"),f=s.getEl("sv"),p=s.getEl("svp"),s._repaint=function(){c=l.toHsv(),i(c)},s._super(),s._svdraghelper=new t(s._id+"-sv",{start:o,drag:o}),s._hdraghelper=new t(s._id+"-h",{start:a,drag:a}),s._repaint()},rgb:function(){return this.color().toRgb()},value:function(e){var t=this;return arguments.length?(t.color().parse(e),void(t._rendered&&t._repaint())):t.color().toHex()},color:function(){return this._color||(this._color=new r),this._color},renderHtml:function(){function e(){var e,t,n="",i,a;for(i="filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr=",a=o.split(","),e=0,t=a.length-1;t>e;e++)n+='
        ';return n}var t=this,n=t._id,r=t.classPrefix,i,o="#ff0000,#ff0080,#ff00ff,#8000ff,#0000ff,#0080ff,#00ffff,#00ff80,#00ff00,#80ff00,#ffff00,#ff8000,#ff0000",a="background: -ms-linear-gradient(top,"+o+");background: linear-gradient(to bottom,"+o+");";return i='
        '+e()+'
        ','
        '+i+"
        "}})}),r(Mt,[Nt],function(e){return e.extend({init:function(e){var t=this;e.delimiter||(e.delimiter="\xbb"),t._super(e),t.addClass("path"),t.canFocus=!0,t.on("click",function(e){var n,r=e.target;(n=r.getAttribute("data-index"))&&t.fire("select",{value:t.data()[n],index:n})})},focus:function(){var e=this;return e.getEl().firstChild.focus(),e},data:function(e){var t=this;return"undefined"!=typeof e?(t._data=e,t.update(),t):t._data},update:function(){this.innerHtml(this._getPathHtml())},postRender:function(){var e=this;e._super(),e.data(e.settings.data)},renderHtml:function(){var e=this;return'
        '+e._getPathHtml()+"
        "},_getPathHtml:function(){var e=this,t=e._data||[],n,r,i="",o=e.classPrefix;for(n=0,r=t.length;r>n;n++)i+=(n>0?'":"")+'
        '+t[n].name+"
        ";return i||(i='
        \xa0
        '),i}})}),r(Pt,[Mt,ht],function(e,t){return e.extend({postRender:function(){function e(e){if(1===e.nodeType){if("BR"==e.nodeName||e.getAttribute("data-mce-bogus"))return!0;if("bookmark"===e.getAttribute("data-mce-type"))return!0}return!1}var n=this,r=t.activeEditor;return n.on("select",function(e){r.focus(),r.selection.select(this.data()[e.index].element),r.nodeChanged()}),r.on("nodeChange",function(t){for(var i=[],o=t.parents,a=o.length;a--;)if(1==o[a].nodeType&&!e(o[a])){var s=r.fire("ResolveName",{name:o[a].nodeName.toLowerCase(),target:o[a]});if(s.isDefaultPrevented()||i.push({name:s.name,element:o[a]}),s.isPropagationStopped())break}n.data(i)}),n._super()}})}),r(Ot,[J],function(e){return e.extend({Defaults:{layout:"flex",align:"center",defaults:{flex:1}},renderHtml:function(){var e=this,t=e._layout,n=e.classPrefix;return e.addClass("formitem"),t.preRender(e),'
        '+(e.settings.title?'
        '+e.settings.title+"
        ":"")+'
        '+(e.settings.html||"")+t.renderHtml(e)+"
        "}})}),r(It,[J,Ot,d],function(e,t,n){return e.extend({Defaults:{containerCls:"form",layout:"flex",direction:"column",align:"stretch",flex:1,padding:20,labelGap:30,spacing:10,callbacks:{submit:function(){this.submit()}}},preRender:function(){var e=this,r=e.items();e.settings.formItemDefaults||(e.settings.formItemDefaults={layout:"flex",autoResize:"overflow",defaults:{flex:1}}),r.each(function(r){var i,o=r.settings.label;o&&(i=new t(n.extend({items:{type:"label",id:r._id+"-l",text:o,flex:0,forId:r._id,disabled:r.disabled()}},e.settings.formItemDefaults)),i.type="formitem",r.aria("labelledby",r._id+"-l"),"undefined"==typeof r.settings.flex&&(r.settings.flex=1),e.replace(r,i),i.add(r))})},recalcLabels:function(){var e=this,t=0,n=[],r,i,o;if(e.settings.labelGapCalc!==!1)for(o="children"==e.settings.labelGapCalc?e.find("formitem"):e.items(),o.filter("formitem").each(function(e){var r=e.items()[0],i=r.getEl().clientWidth;t=i>t?i:t,n.push(r)}),i=e.settings.labelGap||0,r=n.length;r--;)n[r].settings.minWidth=t+i},visible:function(e){var t=this._super(e);return e===!0&&this._rendered&&this.recalcLabels(),t},submit:function(){return this.fire("submit",{data:this.toJSON()})},postRender:function(){var e=this;e._super(),e.recalcLabels(),e.fromJSON(e.settings.data)}})}),r(Ft,[It],function(e){return e.extend({Defaults:{containerCls:"fieldset",layout:"flex",direction:"column",align:"stretch",flex:1,padding:"25 15 5 15",labelGap:30,spacing:10,border:1},renderHtml:function(){var e=this,t=e._layout,n=e.classPrefix;return e.preRender(),t.preRender(e),'
        '+(e.settings.title?''+e.settings.title+"":"")+'
        '+(e.settings.html||"")+t.renderHtml(e)+"
        "}})}),r(zt,[Rt,d],function(e,t){return e.extend({init:function(e){var n=this,r=tinymce.activeEditor,i=r.settings,o,a,s;e.spellcheck=!1,s=i.file_picker_types||i.file_browser_callback_types,s&&(s=t.makeMap(s,/[, ]/)),(!s||s[e.filetype])&&(a=i.file_picker_callback,!a||s&&!s[e.filetype]?(a=i.file_browser_callback,!a||s&&!s[e.filetype]||(o=function(){a(n.getEl("inp").id,n.value(),e.filetype,window)})):o=function(){var i=n.fire("beforecall").meta;i=t.extend({filetype:e.filetype},i),a.call(r,function(e,t){n.value(e).fire("change",{meta:t})},n.value(),i)}),o&&(e.icon="browse",e.onaction=o),n._super(e)}})}),r(Wt,[_t],function(e){return e.extend({recalc:function(e){var t=e.layoutRect(),n=e.paddingBox();e.items().filter(":visible").each(function(e){e.layoutRect({x:n.left,y:n.top,w:t.innerW-n.right-n.left,h:t.innerH-n.top-n.bottom}),e.recalc&&e.recalc()})}})}),r(Vt,[_t],function(e){return e.extend({recalc:function(e){var t,n,r,i,o,a,s,l,c,u,d,f,p,h,m,g,v=[],y,b,C,x,w,_,E,N,k,S,T,R,A,B,D,L,H,M,P,O,I,F,z=Math.max,W=Math.min;for(r=e.items().filter(":visible"),i=e.layoutRect(),o=e._paddingBox,a=e.settings,f=e.isRtl()?a.direction||"row-reversed":a.direction,s=a.align,l=e.isRtl()?a.pack||"end":a.pack,c=a.spacing||0,("row-reversed"==f||"column-reverse"==f)&&(r=r.set(r.toArray().reverse()),f=f.split("-")[0]),"column"==f?(k="y",E="h",N="minH",S="maxH",R="innerH",T="top",A="deltaH",B="contentH",P="left",H="w",D="x",L="innerW",M="minW",O="right",I="deltaW",F="contentW"):(k="x",E="w",N="minW",S="maxW",R="innerW",T="left",A="deltaW",B="contentW",P="top",H="h",D="y",L="innerH",M="minH",O="bottom",I="deltaH",F="contentH"),d=i[R]-o[T]-o[T],_=u=0,t=0,n=r.length;n>t;t++)p=r[t],h=p.layoutRect(),m=p.settings,g=m.flex,d-=n-1>t?c:0,g>0&&(u+=g,h[S]&&v.push(p),h.flex=g),d-=h[N],y=o[P]+h[M]+o[O],y>_&&(_=y);if(x={},x[N]=0>d?i[N]-d+i[A]:i[R]-d+i[A],x[M]=_+i[I],x[B]=i[R]-d,x[F]=_,x.minW=W(x.minW,i.maxW),x.minH=W(x.minH,i.maxH),x.minW=z(x.minW,i.startMinWidth),x.minH=z(x.minH,i.startMinHeight),!i.autoResize||x.minW==i.minW&&x.minH==i.minH){for(C=d/u,t=0,n=v.length;n>t;t++)p=v[t],h=p.layoutRect(),b=h[S],y=h[N]+h.flex*C,y>b?(d-=h[S]-h[N],u-=h.flex,h.flex=0,h.maxFlexSize=b):h.maxFlexSize=0;for(C=d/u,w=o[T],x={},0===u&&("end"==l?w=d+o[T]:"center"==l?(w=Math.round(i[R]/2-(i[R]-d)/2)+o[T],0>w&&(w=o[T])):"justify"==l&&(w=o[T],c=Math.floor(d/(r.length-1)))),x[D]=o[P],t=0,n=r.length;n>t;t++)p=r[t],h=p.layoutRect(),y=h.maxFlexSize||h[N],"center"===s?x[D]=Math.round(i[L]/2-h[H]/2):"stretch"===s?(x[H]=z(h[M]||0,i[L]-o[P]-o[O]),x[D]=o[P]):"end"===s&&(x[D]=i[L]-h[H]-o.top),h.flex>0&&(y+=h.flex*C),x[E]=y,x[k]=w,p.layoutRect(x),p.recalc&&p.recalc(),w+=y+c}else if(x.w=x.minW,x.h=x.minH,e.layoutRect(x),this.recalc(e),null===e._lastRect){var V=e.parent();V&&(V._lastRect=null,V.recalc())}}})}),r(Ut,[wt],function(e){return e.extend({Defaults:{containerClass:"flow-layout",controlClass:"flow-layout-item",endClass:"break"},recalc:function(e){e.items().filter(":visible").each(function(e){e.recalc&&e.recalc()})}})}),r($t,[K,Nt,rt,d,ht,u],function(e,t,n,r,i,o){function a(e){function t(t,n){return function(){var r=this;e.on("nodeChange",function(i){var o=e.formatter,a=null;s(i.parents,function(e){return s(t,function(t){return n?o.matchNode(e,n,{value:t.value})&&(a=t.value):o.matchNode(e,t.value)&&(a=t.value),a?!1:void 0}),a?!1:void 0}),r.value(a)})}}function r(e){e=e.replace(/;$/,"").split(";");for(var t=e.length;t--;)e[t]=e[t].split("=");return e}function i(){function t(e){var n=[];if(e)return s(e,function(e){var o={text:e.title,icon:e.icon};if(e.items)o.menu=t(e.items);else{var a=e.format||"custom"+r++;e.format||(e.name=a,i.push(e)),o.format=a,o.cmd=e.cmd}n.push(o)}),n}function n(){var n;return n=t(e.settings.style_formats_merge?e.settings.style_formats?o.concat(e.settings.style_formats):o:e.settings.style_formats||o)}var r=0,i=[],o=[{title:"Headings",items:[{title:"Heading 1",format:"h1"},{title:"Heading 2",format:"h2"},{title:"Heading 3",format:"h3"},{title:"Heading 4",format:"h4"},{title:"Heading 5",format:"h5"},{title:"Heading 6",format:"h6"}]},{title:"Inline",items:[{title:"Bold",icon:"bold",format:"bold"},{title:"Italic",icon:"italic",format:"italic"},{title:"Underline",icon:"underline",format:"underline"},{title:"Strikethrough",icon:"strikethrough",format:"strikethrough"},{title:"Superscript",icon:"superscript",format:"superscript"},{title:"Subscript",icon:"subscript",format:"subscript"},{title:"Code",icon:"code",format:"code"}]},{title:"Blocks",items:[{title:"Paragraph",format:"p"},{title:"Blockquote",format:"blockquote"},{title:"Div",format:"div"},{title:"Pre",format:"pre"}]},{title:"Alignment",items:[{title:"Left",icon:"alignleft",format:"alignleft"},{title:"Center",icon:"aligncenter",format:"aligncenter"},{title:"Right",icon:"alignright",format:"alignright"},{title:"Justify",icon:"alignjustify",format:"alignjustify"}]}];return e.on("init",function(){s(i,function(t){e.formatter.register(t.name,t)})}),{type:"menu",items:n(),onPostRender:function(t){e.fire("renderFormatsMenu",{control:t.control})},itemDefaults:{preview:!0,textStyle:function(){return this.settings.format?e.formatter.getCssText(this.settings.format):void 0},onPostRender:function(){var t=this;t.parent().on("show",function(){var n,r;n=t.settings.format,n&&(t.disabled(!e.formatter.canApply(n)),t.active(e.formatter.match(n))),r=t.settings.cmd,r&&t.active(e.queryCommandState(r))})},onclick:function(){this.settings.format&&l(this.settings.format),this.settings.cmd&&e.execCommand(this.settings.cmd)}}}}function o(t){return function(){function n(){return e.undoManager?e.undoManager[t]():!1}var r=this;t="redo"==t?"hasRedo":"hasUndo",r.disabled(!n()),e.on("Undo Redo AddUndo TypingUndo ClearUndos",function(){r.disabled(!n())})}}function a(){var t=this;e.on("VisualAid",function(e){t.active(e.hasVisual)}),t.active(e.hasVisual)}function l(t){t.control&&(t=t.control.value()),t&&e.execCommand("mceToggleFormat",!1,t)}var c;c=i(),s({bold:"Bold",italic:"Italic",underline:"Underline",strikethrough:"Strikethrough",subscript:"Subscript",superscript:"Superscript"},function(t,n){e.addButton(n,{tooltip:t,onPostRender:function(){var t=this;e.formatter?e.formatter.formatChanged(n,function(e){t.active(e)}):e.on("init",function(){e.formatter.formatChanged(n,function(e){t.active(e)})})},onclick:function(){l(n)}})}),s({outdent:["Decrease indent","Outdent"],indent:["Increase indent","Indent"],cut:["Cut","Cut"],copy:["Copy","Copy"],paste:["Paste","Paste"],help:["Help","mceHelp"],selectall:["Select all","SelectAll"],removeformat:["Clear formatting","RemoveFormat"],visualaid:["Visual aids","mceToggleVisualAid"],newdocument:["New document","mceNewDocument"]},function(t,n){e.addButton(n,{tooltip:t[0],cmd:t[1]})}),s({blockquote:["Blockquote","mceBlockQuote"],numlist:["Numbered list","InsertOrderedList"],bullist:["Bullet list","InsertUnorderedList"],subscript:["Subscript","Subscript"],superscript:["Superscript","Superscript"],alignleft:["Align left","JustifyLeft"],aligncenter:["Align center","JustifyCenter"],alignright:["Align right","JustifyRight"],alignjustify:["Justify","JustifyFull"]},function(t,n){e.addButton(n,{tooltip:t[0],cmd:t[1],onPostRender:function(){var t=this;e.formatter?e.formatter.formatChanged(n,function(e){t.active(e)}):e.on("init",function(){e.formatter.formatChanged(n,function(e){t.active(e)})})}})}),e.addButton("undo",{tooltip:"Undo",onPostRender:o("undo"),cmd:"undo"}),e.addButton("redo",{tooltip:"Redo",onPostRender:o("redo"),cmd:"redo"}),e.addMenuItem("newdocument",{text:"New document",shortcut:"Ctrl+N",icon:"newdocument",cmd:"mceNewDocument"}),e.addMenuItem("undo",{text:"Undo",icon:"undo",shortcut:"Ctrl+Z",onPostRender:o("undo"),cmd:"undo"}),e.addMenuItem("redo",{text:"Redo",icon:"redo",shortcut:"Ctrl+Y",onPostRender:o("redo"),cmd:"redo"}),e.addMenuItem("visualaid",{text:"Visual aids",selectable:!0,onPostRender:a,cmd:"mceToggleVisualAid"}),s({cut:["Cut","Cut","Ctrl+X"],copy:["Copy","Copy","Ctrl+C"],paste:["Paste","Paste","Ctrl+V"],selectall:["Select all","SelectAll","Ctrl+A"],bold:["Bold","Bold","Ctrl+B"],italic:["Italic","Italic","Ctrl+I"],underline:["Underline","Underline"],strikethrough:["Strikethrough","Strikethrough"],subscript:["Subscript","Subscript"],superscript:["Superscript","Superscript"],removeformat:["Clear formatting","RemoveFormat"]},function(t,n){e.addMenuItem(n,{text:t[0],icon:n,shortcut:t[2],cmd:t[1]})}),e.on("mousedown",function(){n.hideAll()}),e.addButton("styleselect",{type:"menubutton",text:"Formats",menu:c}),e.addButton("formatselect",function(){var n=[],i=r(e.settings.block_formats||"Paragraph=p;Address=address;Pre=pre;Heading 1=h1;Heading 2=h2;Heading 3=h3;Heading 4=h4;Heading 5=h5;Heading 6=h6");return s(i,function(t){n.push({text:t[0],value:t[1],textStyle:function(){return e.formatter.getCssText(t[1])}})}),{type:"listbox",text:i[0][0],values:n,fixedWidth:!0,onselect:l,onPostRender:t(n)}}),e.addButton("fontselect",function(){var n="Andale Mono=andale mono,times;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier;Georgia=georgia,palatino;Helvetica=helvetica;Impact=impact,chicago;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco;Times New Roman=times new roman,times;Trebuchet MS=trebuchet ms,geneva;Verdana=verdana,geneva;Webdings=webdings;Wingdings=wingdings,zapf dingbats",i=[],o=r(e.settings.font_formats||n);return s(o,function(e){i.push({text:{raw:e[0]},value:e[1],textStyle:-1==e[1].indexOf("dings")?"font-family:"+e[1]:""})}),{type:"listbox",text:"Font Family",tooltip:"Font Family",values:i,fixedWidth:!0,onPostRender:t(i,"fontname"),onselect:function(t){t.control.settings.value&&e.execCommand("FontName",!1,t.control.settings.value)}}}),e.addButton("fontsizeselect",function(){var n=[],r="8pt 10pt 12pt 14pt 18pt 24pt 36pt",i=e.settings.fontsize_formats||r;return s(i.split(" "),function(e){var t=e,r=e,i=e.split("=");i.length>1&&(t=i[0],r=i[1]),n.push({text:t,value:r})}),{type:"listbox",text:"Font Sizes",tooltip:"Font Sizes",values:n,fixedWidth:!0,onPostRender:t(n,"fontsize"),onclick:function(t){t.control.settings.value&&e.execCommand("FontSize",!1,t.control.settings.value)}}}),e.addMenuItem("formats",{text:"Formats",menu:c})}var s=r.each;i.on("AddEditor",function(t){t.editor.rtl&&(e.rtl=!0),a(t.editor)}),e.translate=function(e){return i.translate(e)},t.tooltips=!o.iOS}),r(qt,[_t],function(e){return e.extend({recalc:function(e){var t=e.settings,n,r,i,o,a,s,l,c,u,d,f,p,h,m,g,v,y,b,C,x,w,_,E=[],N=[],k,S,T,R,A,B;t=e.settings,i=e.items().filter(":visible"),o=e.layoutRect(),r=t.columns||Math.ceil(Math.sqrt(i.length)),n=Math.ceil(i.length/r),y=t.spacingH||t.spacing||0,b=t.spacingV||t.spacing||0,C=t.alignH||t.align,x=t.alignV||t.align,g=e._paddingBox,A="reverseRows"in t?t.reverseRows:e.isRtl(),C&&"string"==typeof C&&(C=[C]),x&&"string"==typeof x&&(x=[x]);for(d=0;r>d;d++)E.push(0);for(f=0;n>f;f++)N.push(0);for(f=0;n>f;f++)for(d=0;r>d&&(u=i[f*r+d],u);d++)c=u.layoutRect(),k=c.minW,S=c.minH,E[d]=k>E[d]?k:E[d],N[f]=S>N[f]?S:N[f];for(T=o.innerW-g.left-g.right,w=0,d=0;r>d;d++)w+=E[d]+(d>0?y:0),T-=(d>0?y:0)+E[d];for(R=o.innerH-g.top-g.bottom,_=0,f=0;n>f;f++)_+=N[f]+(f>0?b:0),R-=(f>0?b:0)+N[f];if(w+=g.left+g.right,_+=g.top+g.bottom,l={},l.minW=w+(o.w-o.innerW),l.minH=_+(o.h-o.innerH),l.contentW=l.minW-o.deltaW,l.contentH=l.minH-o.deltaH,l.minW=Math.min(l.minW,o.maxW),l.minH=Math.min(l.minH,o.maxH),l.minW=Math.max(l.minW,o.startMinWidth),l.minH=Math.max(l.minH,o.startMinHeight),!o.autoResize||l.minW==o.minW&&l.minH==o.minH){o.autoResize&&(l=e.layoutRect(l),l.contentW=l.minW-o.deltaW,l.contentH=l.minH-o.deltaH);var D;D="start"==t.packV?0:R>0?Math.floor(R/n):0;var L=0,H=t.flexWidths;if(H)for(d=0;dd;d++)E[d]+=H?H[d]*M:M;for(h=g.top,f=0;n>f;f++){for(p=g.left,s=N[f]+D,d=0;r>d&&(B=A?f*r+r-1-d:f*r+d,u=i[B],u);d++)m=u.settings,c=u.layoutRect(),a=Math.max(E[d],c.startMinWidth),c.x=p,c.y=h,v=m.alignH||(C?C[d]||C[0]:null),"center"==v?c.x=p+a/2-c.w/2:"right"==v?c.x=p+a-c.w:"stretch"==v&&(c.w=a),v=m.alignV||(x?x[d]||x[0]:null),"center"==v?c.y=h+s/2-c.h/2:"bottom"==v?c.y=h+s-c.h:"stretch"==v&&(c.h=s),u.layoutRect(c),p+=a+y,u.recalc&&u.recalc();h+=s+b}}else if(l.w=l.minW,l.h=l.minH,e.layoutRect(l),this.recalc(e),null===e._lastRect){var P=e.parent();P&&(P._lastRect=null,P.recalc())}}})}),r(jt,[Nt],function(e){return e.extend({renderHtml:function(){var e=this;return e.addClass("iframe"),e.canFocus=!1,''},src:function(e){this.getEl().src=e},html:function(e,t){var n=this,r=this.getEl().contentWindow.document.body;return r?(r.innerHTML=e,t&&t()):setTimeout(function(){n.html(e)},0),this}})}),r(Yt,[Nt,Y],function(e,t){return e.extend({init:function(e){var t=this;t._super(e),t.addClass("widget"),t.addClass("label"),t.canFocus=!1,e.multiline&&t.addClass("autoscroll"),e.strong&&t.addClass("strong")},initLayoutRect:function(){var e=this,n=e._super();if(e.settings.multiline){var r=t.getSize(e.getEl());r.width>n.maxW&&(n.minW=n.maxW,e.addClass("multiline")),e.getEl().style.width=n.minW+"px",n.startMinH=n.h=n.minH=Math.min(n.maxH,t.getSize(e.getEl()).height)}return n},repaint:function(){var e=this;return e.settings.multiline||(e.getEl().style.lineHeight=e.layoutRect().h+"px"),e._super()},text:function(e){var t=this;return t._rendered&&e&&this.innerHtml(t.encode(e)),t._super(e)},renderHtml:function(){var e=this,t=e.settings.forId;return'" +}})}),r(Kt,[J],function(e){return e.extend({Defaults:{role:"toolbar",layout:"flow"},init:function(e){var t=this;t._super(e),t.addClass("toolbar")},postRender:function(){var e=this;return e.items().addClass("toolbar-item"),e._super()}})}),r(Gt,[Kt],function(e){return e.extend({Defaults:{role:"menubar",containerCls:"menubar",ariaRoot:!0,defaults:{type:"menubutton"}}})}),r(Xt,[kt,G,Gt],function(e,t,n){function r(e,t){for(;e;){if(t===e)return!0;e=e.parentNode}return!1}var i=e.extend({init:function(e){var t=this;t._renderOpen=!0,t._super(e),t.addClass("menubtn"),e.fixedWidth&&t.addClass("fixed-width"),t.aria("haspopup",!0),t.hasPopup=!0},showMenu:function(){var e=this,n=e.settings,r;return e.menu&&e.menu.visible()?e.hideMenu():(e.menu||(r=n.menu||[],r.length?r={type:"menu",items:r}:r.type=r.type||"menu",e.menu=t.create(r).parent(e).renderTo(),e.fire("createmenu"),e.menu.reflow(),e.menu.on("cancel",function(t){t.control.parent()===e.menu&&(t.stopPropagation(),e.focus(),e.hideMenu())}),e.menu.on("select",function(){e.focus()}),e.menu.on("show hide",function(t){t.control==e.menu&&e.activeMenu("show"==t.type),e.aria("expanded","show"==t.type)}).fire("show")),e.menu.show(),e.menu.layoutRect({w:e.layoutRect().w}),void e.menu.moveRel(e.getEl(),e.isRtl()?["br-tr","tr-br"]:["bl-tl","tl-bl"]))},hideMenu:function(){var e=this;e.menu&&(e.menu.items().each(function(e){e.hideMenu&&e.hideMenu()}),e.menu.hide())},activeMenu:function(e){this.toggleClass("active",e)},renderHtml:function(){var e=this,t=e._id,r=e.classPrefix,i=e.settings.icon,o;return o=e.settings.image,o?(i="none","string"!=typeof o&&(o=window.getSelection?o[0]:o[1]),o=" style=\"background-image: url('"+o+"')\""):o="",i=e.settings.icon?r+"ico "+r+"i-"+i:"",e.aria("role",e.parent()instanceof n?"menuitem":"button"),'
        '},postRender:function(){var e=this;return e.on("click",function(t){t.control===e&&r(t.target,e.getEl())&&(e.showMenu(),t.aria&&e.menu.items()[0].focus())}),e.on("mouseenter",function(t){var n=t.control,r=e.parent(),o;n&&r&&n instanceof i&&n.parent()==r&&(r.items().filter("MenuButton").each(function(e){e.hideMenu&&e!=n&&(e.menu&&e.menu.visible()&&(o=!0),e.hideMenu())}),o&&(n.focus(),n.showMenu()))}),e._super()},text:function(e){var t=this,n,r;if(t._rendered)for(r=t.getEl("open").getElementsByTagName("span"),n=0;n0&&(o=r[0].text,n._value=r[0].value),e.menu=r),e.text=e.text||o||r[0].text,n._super(e),n.addClass("listbox"),n.on("select",function(t){var r=t.control;a&&(t.lastControl=a),e.multiple?r.active(!r.active()):n.value(t.control.settings.value),a=r})},value:function(e){function t(e,n){e.items().each(function(e){i=e.value()===n,i&&(o=o||e.text()),e.active(i),e.menu&&t(e.menu,n)})}function n(t){for(var r=0;r'+("-"!==o?'\xa0":"")+("-"!==o?''+o+"":"")+(l?'
        '+l+"
        ":"")+(r.menu?'
        ':"")+"
        "},postRender:function(){var e=this,t=e.settings,n=t.textStyle;if("function"==typeof n&&(n=n.call(this)),n){var r=e.getEl("text");r&&r.setAttribute("style",n)}return e.on("mouseenter click",function(n){n.control===e&&(t.menu||"click"!==n.type?(e.showMenu(),n.aria&&e.menu.focus(!0)):(e.fire("select"),e.parent().hideAll()))}),e._super(),e},active:function(e){return"undefined"!=typeof e&&this.aria("checked",e),this._super(e)},remove:function(){this._super(),this.menu&&this.menu.remove()}})}),r(Zt,[rt,Qt,d],function(e,t,n){var r=e.extend({Defaults:{defaultType:"menuitem",border:1,layout:"stack",role:"application",bodyRole:"menu",ariaRoot:!0},init:function(e){var t=this;if(e.autohide=!0,e.constrainToViewport=!0,e.itemDefaults)for(var r=e.items,i=r.length;i--;)r[i]=n.extend({},e.itemDefaults,r[i]);t._super(e),t.addClass("menu")},repaint:function(){return this.toggleClass("menu-align",!0),this._super(),this.getEl().style.height="",this.getEl("body").style.height="",this},cancel:function(){var e=this;e.hideAll(),e.fire("select")},hideAll:function(){var e=this;return this.find("menuitem").exec("hideMenu"),e._super()},preRender:function(){var e=this;return e.items().each(function(t){var n=t.settings;return n.icon||n.selectable?(e._hasIcons=!0,!1):void 0}),e._super()}});return r}),r(en,[Tt],function(e){return e.extend({Defaults:{classes:"radio",role:"radio"}})}),r(tn,[Nt,Q],function(e,t){return e.extend({renderHtml:function(){var e=this,t=e.classPrefix;return e.addClass("resizehandle"),"both"==e.settings.direction&&e.addClass("resizehandle-both"),e.canFocus=!1,'
        '},postRender:function(){var e=this;e._super(),e.resizeDragHelper=new t(this._id,{start:function(){e.fire("ResizeStart")},drag:function(t){"both"!=e.settings.direction&&(t.deltaX=0),e.fire("Resize",t)},stop:function(){e.fire("ResizeEnd")}})},remove:function(){return this.resizeDragHelper&&this.resizeDragHelper.destroy(),this._super()}})}),r(nn,[Nt],function(e){return e.extend({renderHtml:function(){var e=this;return e.addClass("spacer"),e.canFocus=!1,'
        '}})}),r(rn,[Xt,Y],function(e,t){return e.extend({Defaults:{classes:"widget btn splitbtn",role:"button"},repaint:function(){var e=this,n=e.getEl(),r=e.layoutRect(),i,o;return e._super(),i=n.firstChild,o=n.lastChild,t.css(i,{width:r.w-t.getSize(o).width,height:r.h-2}),t.css(o,{height:r.h-2}),e},activeMenu:function(e){var n=this;t.toggleClass(n.getEl().lastChild,n.classPrefix+"active",e)},renderHtml:function(){var e=this,t=e._id,n=e.classPrefix,r,i=e.settings.icon;return r=e.settings.image,r?(i="none","string"!=typeof r&&(r=window.getSelection?r[0]:r[1]),r=" style=\"background-image: url('"+r+"')\""):r="",i=e.settings.icon?n+"ico "+n+"i-"+i:"",'
        '},postRender:function(){var e=this,t=e.settings.onclick;return e.on("click",function(e){var n=e.target;if(e.control==this)for(;n;){if(e.aria&&"down"!=e.aria.key||"BUTTON"==n.nodeName&&-1==n.className.indexOf("open"))return e.stopImmediatePropagation(),void t.call(this,e);n=n.parentNode}}),delete e.settings.onclick,e._super()}})}),r(on,[Ut],function(e){return e.extend({Defaults:{containerClass:"stack-layout",controlClass:"stack-layout-item",endClass:"break"}})}),r(an,[et,Y],function(e,t){return e.extend({Defaults:{layout:"absolute",defaults:{type:"panel"}},activateTab:function(e){var n;this.activeTabId&&(n=this.getEl(this.activeTabId),t.removeClass(n,this.classPrefix+"active"),n.setAttribute("aria-selected","false")),this.activeTabId="t"+e,n=this.getEl("t"+e),n.setAttribute("aria-selected","true"),t.addClass(n,this.classPrefix+"active"),this.items()[e].show().fire("showtab"),this.reflow(),this.items().each(function(t,n){e!=n&&t.hide()})},renderHtml:function(){var e=this,t=e._layout,n="",r=e.classPrefix;return e.preRender(),t.preRender(e),e.items().each(function(t,i){var o=e._id+"-t"+i;t.aria("role","tabpanel"),t.aria("labelledby",o),n+='"}),'
        '+n+'
        '+t.renderHtml(e)+"
        "},postRender:function(){var e=this;e._super(),e.settings.activeTab=e.settings.activeTab||0,e.activateTab(e.settings.activeTab),this.on("click",function(t){var n=t.target.parentNode;if(t.target.parentNode.id==e._id+"-head")for(var r=n.childNodes.length;r--;)n.childNodes[r]==t.target&&e.activateTab(r)})},initLayoutRect:function(){var e=this,n,r,i;r=t.getSize(e.getEl("head")).width,r=0>r?0:r,i=0,e.items().each(function(e){r=Math.max(r,e.layoutRect().minW),i=Math.max(i,e.layoutRect().minH)}),e.items().each(function(e){e.settings.x=0,e.settings.y=0,e.settings.w=r,e.settings.h=i,e.layoutRect({x:0,y:0,w:r,h:i})});var o=t.getSize(e.getEl("head")).height;return e.settings.minWidth=r,e.settings.minHeight=i+o,n=e._super(),n.deltaH+=o,n.innerH=n.h-n.deltaH,n}})}),r(sn,[Nt,Y],function(e,t){return e.extend({init:function(e){var t=this;t._super(e),t._value=e.value||"",t.addClass("textbox"),e.multiline?t.addClass("multiline"):t.on("keydown",function(e){13==e.keyCode&&t.parents().reverse().each(function(t){return e.preventDefault(),t.hasEventListeners("submit")&&t.toJSON?(t.fire("submit",{data:t.toJSON()}),!1):void 0})})},disabled:function(e){var t=this;return t._rendered&&"undefined"!=typeof e&&(t.getEl().disabled=e),t._super(e)},value:function(e){var t=this;return"undefined"!=typeof e?(t._value=e,t._rendered&&(t.getEl().value=e),t):t._rendered?t.getEl().value:t._value},repaint:function(){var e=this,t,n,r,i=0,o=0,a;t=e.getEl().style,n=e._layoutRect,a=e._lastRepaintRect||{};var s=document;return!e.settings.multiline&&s.all&&(!s.documentMode||s.documentMode<=8)&&(t.lineHeight=n.h-o+"px"),r=e._borderBox,i=r.left+r.right+8,o=r.top+r.bottom+(e.settings.multiline?8:0),n.x!==a.x&&(t.left=n.x+"px",a.x=n.x),n.y!==a.y&&(t.top=n.y+"px",a.y=n.y),n.w!==a.w&&(t.width=n.w-i+"px",a.w=n.w),n.h!==a.h&&(t.height=n.h-o+"px",a.h=n.h),e._lastRepaintRect=a,e.fire("repaint",{},!1),e},renderHtml:function(){var e=this,t=e._id,n=e.settings,r=e.encode(e._value,!1),i="";return"spellcheck"in n&&(i+=' spellcheck="'+n.spellcheck+'"'),n.maxLength&&(i+=' maxlength="'+n.maxLength+'"'),n.size&&(i+=' size="'+n.size+'"'),n.subtype&&(i+=' type="'+n.subtype+'"'),e.disabled()&&(i+=' disabled="disabled"'),n.multiline?'":'"},postRender:function(){var e=this;return t.on(e.getEl(),"change",function(t){e.fire("change",t)}),e._super()},remove:function(){t.off(this.getEl()),this._super()}})}),r(ln,[Y,K],function(e,t){return function(n,r){var i=this,o,a=t.classPrefix;i.show=function(t){return i.hide(),o=!0,window.setTimeout(function(){o&&n.appendChild(e.createFragment('
        '))},t||0),i},i.hide=function(){var e=n.lastChild;return e&&-1!=e.className.indexOf("throbber")&&e.parentNode.removeChild(e),o=!1,i}}}),a([l,c,u,d,f,p,h,m,g,y,b,C,_,E,N,k,S,T,R,A,B,D,L,H,M,O,I,F,z,W,V,U,$,q,j,Y,K,G,X,J,Q,Z,et,tt,nt,rt,it,ot,at,st,lt,ct,ut,dt,ft,pt,ht,mt,gt,vt,yt,bt,Ct,xt,wt,_t,Et,Nt,kt,St,Tt,Rt,At,Bt,Dt,Lt,Ht,Mt,Pt,Ot,It,Ft,zt,Wt,Vt,Ut,$t,qt,jt,Yt,Kt,Gt,Xt,Jt,Qt,Zt,en,tn,nn,rn,on,an,sn,ln])}(this); \ No newline at end of file diff --git a/wp-includes/js/tinymce/utils/editable_selects.js b/wp-includes/js/tinymce/utils/editable_selects.js new file mode 100644 index 0000000..8d30787 --- /dev/null +++ b/wp-includes/js/tinymce/utils/editable_selects.js @@ -0,0 +1,70 @@ +/** + * editable_selects.js + * + * Copyright, Moxiecode Systems AB + * Released under LGPL License. + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +var TinyMCE_EditableSelects = { + editSelectElm : null, + + init : function() { + var nl = document.getElementsByTagName("select"), i, d = document, o; + + for (i=0; i'; + h += ' '; + + return h; +} + +function updateColor(img_id, form_element_id) { + document.getElementById(img_id).style.backgroundColor = document.forms[0].elements[form_element_id].value; +} + +function setBrowserDisabled(id, state) { + var img = document.getElementById(id); + var lnk = document.getElementById(id + "_link"); + + if (lnk) { + if (state) { + lnk.setAttribute("realhref", lnk.getAttribute("href")); + lnk.removeAttribute("href"); + tinyMCEPopup.dom.addClass(img, 'disabled'); + } else { + if (lnk.getAttribute("realhref")) + lnk.setAttribute("href", lnk.getAttribute("realhref")); + + tinyMCEPopup.dom.removeClass(img, 'disabled'); + } + } +} + +function getBrowserHTML(id, target_form_element, type, prefix) { + var option = prefix + "_" + type + "_browser_callback", cb, html; + + cb = tinyMCEPopup.getParam(option, tinyMCEPopup.getParam("file_browser_callback")); + + if (!cb) + return ""; + + html = ""; + html += ''; + html += ' '; + + return html; +} + +function openBrowser(img_id, target_form_element, type, option) { + var img = document.getElementById(img_id); + + if (img.className != "mceButtonDisabled") + tinyMCEPopup.openBrowser(target_form_element, type, option); +} + +function selectByValue(form_obj, field_name, value, add_custom, ignore_case) { + if (!form_obj || !form_obj.elements[field_name]) + return; + + if (!value) + value = ""; + + var sel = form_obj.elements[field_name]; + + var found = false; + for (var i=0; i parseInt(v)) + st = this.mark(f, n); + } + } + + return st; + }, + + hasClass : function(n, c, d) { + return new RegExp('\\b' + c + (d ? '[0-9]+' : '') + '\\b', 'g').test(n.className); + }, + + getNum : function(n, c) { + c = n.className.match(new RegExp('\\b' + c + '([0-9]+)\\b', 'g'))[0]; + c = c.replace(/[^0-9]/g, ''); + + return c; + }, + + addClass : function(n, c, b) { + var o = this.removeClass(n, c); + n.className = b ? c + (o != '' ? (' ' + o) : '') : (o != '' ? (o + ' ') : '') + c; + }, + + removeClass : function(n, c) { + c = n.className.replace(new RegExp("(^|\\s+)" + c + "(\\s+|$)"), ' '); + return n.className = c != ' ' ? c : ''; + }, + + tags : function(f, s) { + return f.getElementsByTagName(s); + }, + + mark : function(f, n) { + var s = this.settings; + + this.addClass(n, s.invalid_cls); + n.setAttribute('aria-invalid', 'true'); + this.markLabels(f, n, s.invalid_cls); + + return false; + }, + + markLabels : function(f, n, ic) { + var nl, i; + + nl = this.tags(f, "label"); + for (i=0; i + +> + + +<?php _e('Keyboard Shortcuts'); ?> + + + + + + + + + +
        + +
        +

        + + + + + + + + + + + +
        cv
        ax
        zy
        bi
        u1
        23
        45
        6k
        + +

        + + + + + + + + + + + +
        nl
        jc
        dr
        u a
        o1. s
        qm
        wt
        ph
        x  
        + +

        + + + + + + +
        Alt + F8
        Alt + F9
        Alt + F10
        Alt + F11
        + +

        +
        + +
        + + diff --git a/wp-includes/js/tinymce/wp-tinymce.js.gz b/wp-includes/js/tinymce/wp-tinymce.js.gz new file mode 100644 index 0000000..19dd3ed Binary files /dev/null and b/wp-includes/js/tinymce/wp-tinymce.js.gz differ diff --git a/wp-includes/js/tinymce/wp-tinymce.php b/wp-includes/js/tinymce/wp-tinymce.php new file mode 100644 index 0000000..922df9f --- /dev/null +++ b/wp-includes/js/tinymce/wp-tinymce.php @@ -0,0 +1,39 @@ +e;e++)if(b.call(d,a[e],e,a)===c)return}else for(var g=x.keys(a),e=0,f=g.length;f>e;e++)if(b.call(d,a[g[e]],g[e],a)===c)return;return a};x.map=x.collect=function(a,b,c){var d=[];return null==a?d:m&&a.map===m?a.map(b,c):(y(a,function(a,e,f){d.push(b.call(c,a,e,f))}),d)};var z="Reduce of empty array with no initial value";x.reduce=x.foldl=x.inject=function(a,b,c,d){var e=arguments.length>2;if(null==a&&(a=[]),n&&a.reduce===n)return d&&(b=x.bind(b,d)),e?a.reduce(b,c):a.reduce(b);if(y(a,function(a,f,g){e?c=b.call(d,c,a,f,g):(c=a,e=!0)}),!e)throw new TypeError(z);return c},x.reduceRight=x.foldr=function(a,b,c,d){var e=arguments.length>2;if(null==a&&(a=[]),o&&a.reduceRight===o)return d&&(b=x.bind(b,d)),e?a.reduceRight(b,c):a.reduceRight(b);var f=a.length;if(f!==+f){var g=x.keys(a);f=g.length}if(y(a,function(h,i,j){i=g?g[--f]:--f,e?c=b.call(d,c,a[i],i,j):(c=a[i],e=!0)}),!e)throw new TypeError(z);return c},x.find=x.detect=function(a,b,c){var d;return A(a,function(a,e,f){return b.call(c,a,e,f)?(d=a,!0):void 0}),d},x.filter=x.select=function(a,b,c){var d=[];return null==a?d:p&&a.filter===p?a.filter(b,c):(y(a,function(a,e,f){b.call(c,a,e,f)&&d.push(a)}),d)},x.reject=function(a,b,c){return x.filter(a,function(a,d,e){return!b.call(c,a,d,e)},c)},x.every=x.all=function(a,b,d){b||(b=x.identity);var e=!0;return null==a?e:q&&a.every===q?a.every(b,d):(y(a,function(a,f,g){return(e=e&&b.call(d,a,f,g))?void 0:c}),!!e)};var A=x.some=x.any=function(a,b,d){b||(b=x.identity);var e=!1;return null==a?e:r&&a.some===r?a.some(b,d):(y(a,function(a,f,g){return e||(e=b.call(d,a,f,g))?c:void 0}),!!e)};x.contains=x.include=function(a,b){return null==a?!1:s&&a.indexOf===s?-1!=a.indexOf(b):A(a,function(a){return a===b})},x.invoke=function(a,b){var c=h.call(arguments,2),d=x.isFunction(b);return x.map(a,function(a){return(d?b:a[b]).apply(a,c)})},x.pluck=function(a,b){return x.map(a,x.property(b))},x.where=function(a,b){return x.filter(a,x.matches(b))},x.findWhere=function(a,b){return x.find(a,x.matches(b))},x.max=function(a,b,c){if(!b&&x.isArray(a)&&a[0]===+a[0]&&a.length<65535)return Math.max.apply(Math,a);var d=-1/0,e=-1/0;return y(a,function(a,f,g){var h=b?b.call(c,a,f,g):a;h>e&&(d=a,e=h)}),d},x.min=function(a,b,c){if(!b&&x.isArray(a)&&a[0]===+a[0]&&a.length<65535)return Math.min.apply(Math,a);var d=1/0,e=1/0;return y(a,function(a,f,g){var h=b?b.call(c,a,f,g):a;e>h&&(d=a,e=h)}),d},x.shuffle=function(a){var b,c=0,d=[];return y(a,function(a){b=x.random(c++),d[c-1]=d[b],d[b]=a}),d},x.sample=function(a,b,c){return null==b||c?(a.length!==+a.length&&(a=x.values(a)),a[x.random(a.length-1)]):x.shuffle(a).slice(0,Math.max(0,b))};var B=function(a){return null==a?x.identity:x.isFunction(a)?a:x.property(a)};x.sortBy=function(a,b,c){return b=B(b),x.pluck(x.map(a,function(a,d,e){return{value:a,index:d,criteria:b.call(c,a,d,e)}}).sort(function(a,b){var c=a.criteria,d=b.criteria;if(c!==d){if(c>d||void 0===c)return 1;if(d>c||void 0===d)return-1}return a.index-b.index}),"value")};var C=function(a){return function(b,c,d){var e={};return c=B(c),y(b,function(f,g){var h=c.call(d,f,g,b);a(e,h,f)}),e}};x.groupBy=C(function(a,b,c){x.has(a,b)?a[b].push(c):a[b]=[c]}),x.indexBy=C(function(a,b,c){a[b]=c}),x.countBy=C(function(a,b){x.has(a,b)?a[b]++:a[b]=1}),x.sortedIndex=function(a,b,c,d){c=B(c);for(var e=c.call(d,b),f=0,g=a.length;g>f;){var h=f+g>>>1;c.call(d,a[h])b?[]:h.call(a,0,b)},x.initial=function(a,b,c){return h.call(a,0,a.length-(null==b||c?1:b))},x.last=function(a,b,c){return null==a?void 0:null==b||c?a[a.length-1]:h.call(a,Math.max(a.length-b,0))},x.rest=x.tail=x.drop=function(a,b,c){return h.call(a,null==b||c?1:b)},x.compact=function(a){return x.filter(a,x.identity)};var D=function(a,b,c){return b&&x.every(a,x.isArray)?i.apply(c,a):(y(a,function(a){x.isArray(a)||x.isArguments(a)?b?g.apply(c,a):D(a,b,c):c.push(a)}),c)};x.flatten=function(a,b){return D(a,b,[])},x.without=function(a){return x.difference(a,h.call(arguments,1))},x.partition=function(a,b){var c=[],d=[];return y(a,function(a){(b(a)?c:d).push(a)}),[c,d]},x.uniq=x.unique=function(a,b,c,d){x.isFunction(b)&&(d=c,c=b,b=!1);var e=c?x.map(a,c,d):a,f=[],g=[];return y(e,function(c,d){(b?d&&g[g.length-1]===c:x.contains(g,c))||(g.push(c),f.push(a[d]))}),f},x.union=function(){return x.uniq(x.flatten(arguments,!0))},x.intersection=function(a){var b=h.call(arguments,1);return x.filter(x.uniq(a),function(a){return x.every(b,function(b){return x.contains(b,a)})})},x.difference=function(a){var b=i.apply(d,h.call(arguments,1));return x.filter(a,function(a){return!x.contains(b,a)})},x.zip=function(){for(var a=x.max(x.pluck(arguments,"length").concat(0)),b=new Array(a),c=0;a>c;c++)b[c]=x.pluck(arguments,""+c);return b},x.object=function(a,b){if(null==a)return{};for(var c={},d=0,e=a.length;e>d;d++)b?c[a[d]]=b[d]:c[a[d][0]]=a[d][1];return c},x.indexOf=function(a,b,c){if(null==a)return-1;var d=0,e=a.length;if(c){if("number"!=typeof c)return d=x.sortedIndex(a,b),a[d]===b?d:-1;d=0>c?Math.max(0,e+c):c}if(s&&a.indexOf===s)return a.indexOf(b,c);for(;e>d;d++)if(a[d]===b)return d;return-1},x.lastIndexOf=function(a,b,c){if(null==a)return-1;var d=null!=c;if(t&&a.lastIndexOf===t)return d?a.lastIndexOf(b,c):a.lastIndexOf(b);for(var e=d?c:a.length;e--;)if(a[e]===b)return e;return-1},x.range=function(a,b,c){arguments.length<=1&&(b=a||0,a=0),c=arguments[2]||1;for(var d=Math.max(Math.ceil((b-a)/c),0),e=0,f=new Array(d);d>e;)f[e++]=a,a+=c;return f};var E=function(){};x.bind=function(a,b){var c,d;if(w&&a.bind===w)return w.apply(a,h.call(arguments,1));if(!x.isFunction(a))throw new TypeError;return c=h.call(arguments,2),d=function(){if(!(this instanceof d))return a.apply(b,c.concat(h.call(arguments)));E.prototype=a.prototype;var e=new E;E.prototype=null;var f=a.apply(e,c.concat(h.call(arguments)));return Object(f)===f?f:e}},x.partial=function(a){var b=h.call(arguments,1);return function(){for(var c=0,d=b.slice(),e=0,f=d.length;f>e;e++)d[e]===x&&(d[e]=arguments[c++]);for(;c=k?(clearTimeout(g),g=null,h=j,f=a.apply(d,e),d=e=null):g||c.trailing===!1||(g=setTimeout(i,k)),f}},x.debounce=function(a,b,c){var d,e,f,g,h,i=function(){var j=x.now()-g;b>j?d=setTimeout(i,b-j):(d=null,c||(h=a.apply(f,e),f=e=null))};return function(){f=this,e=arguments,g=x.now();var j=c&&!d;return d||(d=setTimeout(i,b)),j&&(h=a.apply(f,e),f=e=null),h}},x.once=function(a){var b,c=!1;return function(){return c?b:(c=!0,b=a.apply(this,arguments),a=null,b)}},x.wrap=function(a,b){return x.partial(b,a)},x.compose=function(){var a=arguments;return function(){for(var b=arguments,c=a.length-1;c>=0;c--)b=[a[c].apply(this,b)];return b[0]}},x.after=function(a,b){return function(){return--a<1?b.apply(this,arguments):void 0}},x.keys=function(a){if(!x.isObject(a))return[];if(v)return v(a);var b=[];for(var c in a)x.has(a,c)&&b.push(c);return b},x.values=function(a){for(var b=x.keys(a),c=b.length,d=new Array(c),e=0;c>e;e++)d[e]=a[b[e]];return d},x.pairs=function(a){for(var b=x.keys(a),c=b.length,d=new Array(c),e=0;c>e;e++)d[e]=[b[e],a[b[e]]];return d},x.invert=function(a){for(var b={},c=x.keys(a),d=0,e=c.length;e>d;d++)b[a[c[d]]]=c[d];return b},x.functions=x.methods=function(a){var b=[];for(var c in a)x.isFunction(a[c])&&b.push(c);return b.sort()},x.extend=function(a){return y(h.call(arguments,1),function(b){if(b)for(var c in b)a[c]=b[c]}),a},x.pick=function(a){var b={},c=i.apply(d,h.call(arguments,1));return y(c,function(c){c in a&&(b[c]=a[c])}),b},x.omit=function(a){var b={},c=i.apply(d,h.call(arguments,1));for(var e in a)x.contains(c,e)||(b[e]=a[e]);return b},x.defaults=function(a){return y(h.call(arguments,1),function(b){if(b)for(var c in b)void 0===a[c]&&(a[c]=b[c])}),a},x.clone=function(a){return x.isObject(a)?x.isArray(a)?a.slice():x.extend({},a):a},x.tap=function(a,b){return b(a),a};var F=function(a,b,c,d){if(a===b)return 0!==a||1/a==1/b;if(null==a||null==b)return a===b;a instanceof x&&(a=a._wrapped),b instanceof x&&(b=b._wrapped);var e=j.call(a);if(e!=j.call(b))return!1;switch(e){case"[object String]":return a==String(b);case"[object Number]":return a!=+a?b!=+b:0==a?1/a==1/b:a==+b;case"[object Date]":case"[object Boolean]":return+a==+b;case"[object RegExp]":return a.source==b.source&&a.global==b.global&&a.multiline==b.multiline&&a.ignoreCase==b.ignoreCase}if("object"!=typeof a||"object"!=typeof b)return!1;for(var f=c.length;f--;)if(c[f]==a)return d[f]==b;var g=a.constructor,h=b.constructor;if(g!==h&&!(x.isFunction(g)&&g instanceof g&&x.isFunction(h)&&h instanceof h)&&"constructor"in a&&"constructor"in b)return!1;c.push(a),d.push(b);var i=0,k=!0;if("[object Array]"==e){if(i=a.length,k=i==b.length)for(;i--&&(k=F(a[i],b[i],c,d)););}else{for(var l in a)if(x.has(a,l)&&(i++,!(k=x.has(b,l)&&F(a[l],b[l],c,d))))break;if(k){for(l in b)if(x.has(b,l)&&!i--)break;k=!i}}return c.pop(),d.pop(),k};x.isEqual=function(a,b){return F(a,b,[],[])},x.isEmpty=function(a){if(null==a)return!0;if(x.isArray(a)||x.isString(a))return 0===a.length;for(var b in a)if(x.has(a,b))return!1;return!0},x.isElement=function(a){return!(!a||1!==a.nodeType)},x.isArray=u||function(a){return"[object Array]"==j.call(a)},x.isObject=function(a){return a===Object(a)},y(["Arguments","Function","String","Number","Date","RegExp"],function(a){x["is"+a]=function(b){return j.call(b)=="[object "+a+"]"}}),x.isArguments(arguments)||(x.isArguments=function(a){return!(!a||!x.has(a,"callee"))}),"function"!=typeof/./&&(x.isFunction=function(a){return"function"==typeof a}),x.isFinite=function(a){return isFinite(a)&&!isNaN(parseFloat(a))},x.isNaN=function(a){return x.isNumber(a)&&a!=+a},x.isBoolean=function(a){return a===!0||a===!1||"[object Boolean]"==j.call(a)},x.isNull=function(a){return null===a},x.isUndefined=function(a){return void 0===a},x.has=function(a,b){return k.call(a,b)},x.noConflict=function(){return a._=b,this},x.identity=function(a){return a},x.constant=function(a){return function(){return a}},x.property=function(a){return function(b){return b[a]}},x.matches=function(a){return function(b){if(b===a)return!0;for(var c in a)if(a[c]!==b[c])return!1;return!0}},x.times=function(a,b,c){for(var d=Array(Math.max(0,a)),e=0;a>e;e++)d[e]=b.call(c,e);return d},x.random=function(a,b){return null==b&&(b=a,a=0),a+Math.floor(Math.random()*(b-a+1))},x.now=Date.now||function(){return(new Date).getTime()};var G={escape:{"&":"&","<":"<",">":">",'"':""","'":"'"}};G.unescape=x.invert(G.escape);var H={escape:new RegExp("["+x.keys(G.escape).join("")+"]","g"),unescape:new RegExp("("+x.keys(G.unescape).join("|")+")","g")};x.each(["escape","unescape"],function(a){x[a]=function(b){return null==b?"":(""+b).replace(H[a],function(b){return G[a][b]})}}),x.result=function(a,b){if(null==a)return void 0;var c=a[b];return x.isFunction(c)?c.call(a):c},x.mixin=function(a){y(x.functions(a),function(b){var c=x[b]=a[b];x.prototype[b]=function(){var a=[this._wrapped];return g.apply(a,arguments),M.call(this,c.apply(x,a))}})};var I=0;x.uniqueId=function(a){var b=++I+"";return a?a+b:b},x.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var J=/(.)^/,K={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},L=/\\|'|\r|\n|\t|\u2028|\u2029/g;x.template=function(a,b,c){var d;c=x.defaults({},c,x.templateSettings);var e=new RegExp([(c.escape||J).source,(c.interpolate||J).source,(c.evaluate||J).source].join("|")+"|$","g"),f=0,g="__p+='";a.replace(e,function(b,c,d,e,h){return g+=a.slice(f,h).replace(L,function(a){return"\\"+K[a]}),c&&(g+="'+\n((__t=("+c+"))==null?'':_.escape(__t))+\n'"),d&&(g+="'+\n((__t=("+d+"))==null?'':__t)+\n'"),e&&(g+="';\n"+e+"\n__p+='"),f=h+b.length,b}),g+="';\n",c.variable||(g="with(obj||{}){\n"+g+"}\n"),g="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n"+g+"return __p;\n";try{d=new Function(c.variable||"obj","_",g)}catch(h){throw h.source=g,h}if(b)return d(b,x);var i=function(a){return d.call(this,a,x)};return i.source="function("+(c.variable||"obj")+"){\n"+g+"}",i},x.chain=function(a){return x(a).chain()};var M=function(a){return this._chain?x(a).chain():a};x.mixin(x),y(["pop","push","reverse","shift","sort","splice","unshift"],function(a){var b=d[a];x.prototype[a]=function(){var c=this._wrapped;return b.apply(c,arguments),"shift"!=a&&"splice"!=a||0!==c.length||delete c[0],M.call(this,c)}}),y(["concat","join","slice"],function(a){var b=d[a];x.prototype[a]=function(){return M.call(this,b.apply(this._wrapped,arguments))}}),x.extend(x.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}}),"function"==typeof define&&define.amd&&define("underscore",[],function(){return x})}).call(this); \ No newline at end of file diff --git a/wp-includes/js/utils.js b/wp-includes/js/utils.js new file mode 100644 index 0000000..d7d8f01 --- /dev/null +++ b/wp-includes/js/utils.js @@ -0,0 +1,197 @@ +/* global userSettings */ +/* exported getUserSetting, setUserSetting, deleteUserSetting */ +// utility functions + +var wpCookies = { +// The following functions are from Cookie.js class in TinyMCE 3, Moxiecode, used under LGPL. + + each: function( obj, cb, scope ) { + var n, l; + + if ( ! obj ) { + return 0; + } + + scope = scope || obj; + + if ( typeof( obj.length ) !== 'undefined' ) { + for ( n = 0, l = obj.length; n < l; n++ ) { + if ( cb.call( scope, obj[n], n, obj ) === false ) { + return 0; + } + } + } else { + for ( n in obj ) { + if ( obj.hasOwnProperty(n) ) { + if ( cb.call( scope, obj[n], n, obj ) === false ) { + return 0; + } + } + } + } + return 1; + }, + + /** + * Get a multi-values cookie. + * Returns a JS object with the name: 'value' pairs. + */ + getHash: function( name ) { + var cookie = this.get( name ), values; + + if ( cookie ) { + this.each( cookie.split('&'), function( pair ) { + pair = pair.split('='); + values = values || {}; + values[pair[0]] = pair[1]; + }); + } + + return values; + }, + + /** + * Set a multi-values cookie. + * + * 'values_obj' is the JS object that is stored. It is encoded as URI in wpCookies.set(). + */ + setHash: function( name, values_obj, expires, path, domain, secure ) { + var str = ''; + + this.each( values_obj, function( val, key ) { + str += ( ! str ? '' : '&' ) + key + '=' + val; + }); + + this.set( name, str, expires, path, domain, secure ); + }, + + /** + * Get a cookie. + */ + get: function( name ) { + var e, b, + cookie = document.cookie, + p = name + '='; + + if ( ! cookie ) { + return; + } + + b = cookie.indexOf( '; ' + p ); + + if ( b === -1 ) { + b = cookie.indexOf(p); + + if ( b !== 0 ) { + return null; + } + } else { + b += 2; + } + + e = cookie.indexOf( ';', b ); + + if ( e === -1 ) { + e = cookie.length; + } + + return decodeURIComponent( cookie.substring( b + p.length, e ) ); + }, + + /** + * Set a cookie. + * + * The 'expires' arg can be either a JS Date() object set to the expiration date (back-compat) + * or the number of seconds until expiration + */ + set: function( name, value, expires, path, domain, secure ) { + var d = new Date(); + + if ( typeof( expires ) === 'object' && expires.toGMTString ) { + expires = expires.toGMTString(); + } else if ( parseInt( expires, 10 ) ) { + d.setTime( d.getTime() + ( parseInt( expires, 10 ) * 1000 ) ); // time must be in miliseconds + expires = d.toGMTString(); + } else { + expires = ''; + } + + document.cookie = name + '=' + encodeURIComponent( value ) + + ( expires ? '; expires=' + expires : '' ) + + ( path ? '; path=' + path : '' ) + + ( domain ? '; domain=' + domain : '' ) + + ( secure ? '; secure' : '' ); + }, + + /** + * Remove a cookie. + * + * This is done by setting it to an empty value and setting the expiration time in the past. + */ + remove: function( name, path, domain, secure ) { + this.set( name, '', -1000, path, domain, secure ); + } +}; + +// Returns the value as string. Second arg or empty string is returned when value is not set. +function getUserSetting( name, def ) { + var settings = getAllUserSettings(); + + if ( settings.hasOwnProperty( name ) ) { + return settings[name]; + } + + if ( typeof def !== 'undefined' ) { + return def; + } + + return ''; +} + +// Both name and value must be only ASCII letters, numbers or underscore +// and the shorter, the better (cookies can store maximum 4KB). Not suitable to store text. +// The value is converted and stored as string. +function setUserSetting( name, value, _del ) { + if ( 'object' !== typeof userSettings ) { + return false; + } + + var uid = userSettings.uid, + settings = wpCookies.getHash( 'wp-settings-' + uid ), + path = userSettings.url, + secure = !! userSettings.secure; + + name = name.toString().replace( /[^A-Za-z0-9_]/, '' ); + + if ( typeof value === 'number' ) { + value = parseInt( value, 10 ); + } else { + value = value.toString().replace( /[^A-Za-z0-9_]/, '' ); + } + + settings = settings || {}; + + if ( _del ) { + delete settings[name]; + } else { + settings[name] = value; + } + + wpCookies.setHash( 'wp-settings-' + uid, settings, 31536000, path, '', secure ); + wpCookies.set( 'wp-settings-time-' + uid, userSettings.time, 31536000, path, '', secure ); + + return name; +} + +function deleteUserSetting( name ) { + return setUserSetting( name, '', 1 ); +} + +// Returns all settings as js object. +function getAllUserSettings() { + if ( 'object' !== typeof userSettings ) { + return {}; + } + + return wpCookies.getHash( 'wp-settings-' + userSettings.uid ) || {}; +} diff --git a/wp-includes/js/utils.min.js b/wp-includes/js/utils.min.js new file mode 100644 index 0000000..4bb7c9a --- /dev/null +++ b/wp-includes/js/utils.min.js @@ -0,0 +1 @@ +function getUserSetting(a,b){var c=getAllUserSettings();return c.hasOwnProperty(a)?c[a]:"undefined"!=typeof b?b:""}function setUserSetting(a,b,c){if("object"!=typeof userSettings)return!1;var d=userSettings.uid,e=wpCookies.getHash("wp-settings-"+d),f=userSettings.url,g=!!userSettings.secure;return a=a.toString().replace(/[^A-Za-z0-9_]/,""),b="number"==typeof b?parseInt(b,10):b.toString().replace(/[^A-Za-z0-9_]/,""),e=e||{},c?delete e[a]:e[a]=b,wpCookies.setHash("wp-settings-"+d,e,31536e3,f,"",g),wpCookies.set("wp-settings-time-"+d,userSettings.time,31536e3,f,"",g),a}function deleteUserSetting(a){return setUserSetting(a,"",1)}function getAllUserSettings(){return"object"!=typeof userSettings?{}:wpCookies.getHash("wp-settings-"+userSettings.uid)||{}}var wpCookies={each:function(a,b,c){var d,e;if(!a)return 0;if(c=c||a,"undefined"!=typeof a.length){for(d=0,e=a.length;e>d;d++)if(b.call(c,a[d],d,a)===!1)return 0}else for(d in a)if(a.hasOwnProperty(d)&&b.call(c,a[d],d,a)===!1)return 0;return 1},getHash:function(a){var b,c=this.get(a);return c&&this.each(c.split("&"),function(a){a=a.split("="),b=b||{},b[a[0]]=a[1]}),b},setHash:function(a,b,c,d,e,f){var g="";this.each(b,function(a,b){g+=(g?"&":"")+b+"="+a}),this.set(a,g,c,d,e,f)},get:function(a){var b,c,d=document.cookie,e=a+"=";if(d){if(c=d.indexOf("; "+e),-1===c){if(c=d.indexOf(e),0!==c)return null}else c+=2;return b=d.indexOf(";",c),-1===b&&(b=d.length),decodeURIComponent(d.substring(c+e.length,b))}},set:function(a,b,c,d,e,f){var g=new Date;"object"==typeof c&&c.toGMTString?c=c.toGMTString():parseInt(c,10)?(g.setTime(g.getTime()+1e3*parseInt(c,10)),c=g.toGMTString()):c="",document.cookie=a+"="+encodeURIComponent(b)+(c?"; expires="+c:"")+(d?"; path="+d:"")+(e?"; domain="+e:"")+(f?"; secure":"")},remove:function(a,b,c,d){this.set(a,"",-1e3,b,c,d)}}; \ No newline at end of file diff --git a/wp-includes/js/wp-ajax-response.js b/wp-includes/js/wp-ajax-response.js new file mode 100644 index 0000000..eb46267 --- /dev/null +++ b/wp-includes/js/wp-ajax-response.js @@ -0,0 +1,64 @@ +var wpAjax = jQuery.extend( { + unserialize: function( s ) { + var r = {}, q, pp, i, p; + if ( !s ) { return r; } + q = s.split('?'); if ( q[1] ) { s = q[1]; } + pp = s.split('&'); + for ( i in pp ) { + if ( jQuery.isFunction(pp.hasOwnProperty) && !pp.hasOwnProperty(i) ) { continue; } + p = pp[i].split('='); + r[p[0]] = p[1]; + } + return r; + }, + parseAjaxResponse: function( x, r, e ) { // 1 = good, 0 = strange (bad data?), -1 = you lack permission + var parsed = {}, re = jQuery('#' + r).html(''), err = ''; + + if ( x && typeof x == 'object' && x.getElementsByTagName('wp_ajax') ) { + parsed.responses = []; + parsed.errors = false; + jQuery('response', x).each( function() { + var th = jQuery(this), child = jQuery(this.firstChild), response; + response = { action: th.attr('action'), what: child.get(0).nodeName, id: child.attr('id'), oldId: child.attr('old_id'), position: child.attr('position') }; + response.data = jQuery( 'response_data', child ).text(); + response.supplemental = {}; + if ( !jQuery( 'supplemental', child ).children().each( function() { + response.supplemental[this.nodeName] = jQuery(this).text(); + } ).size() ) { response.supplemental = false; } + response.errors = []; + if ( !jQuery('wp_error', child).each( function() { + var code = jQuery(this).attr('code'), anError, errorData, formField; + anError = { code: code, message: this.firstChild.nodeValue, data: false }; + errorData = jQuery('wp_error_data[code="' + code + '"]', x); + if ( errorData ) { anError.data = errorData.get(); } + formField = jQuery( 'form-field', errorData ).text(); + if ( formField ) { code = formField; } + if ( e ) { wpAjax.invalidateForm( jQuery('#' + e + ' :input[name="' + code + '"]' ).parents('.form-field:first') ); } + err += '

        ' + anError.message + '

        '; + response.errors.push( anError ); + parsed.errors = true; + } ).size() ) { response.errors = false; } + parsed.responses.push( response ); + } ); + if ( err.length ) { re.html( '
        ' + err + '
        ' ); } + return parsed; + } + if ( isNaN(x) ) { return !re.html('

        ' + x + '

        '); } + x = parseInt(x,10); + if ( -1 == x ) { return !re.html('

        ' + wpAjax.noPerm + '

        '); } + else if ( 0 === x ) { return !re.html('

        ' + wpAjax.broken + '

        '); } + return true; + }, + invalidateForm: function ( selector ) { + return jQuery( selector ).addClass( 'form-invalid' ).find('input:visible').change( function() { jQuery(this).closest('.form-invalid').removeClass( 'form-invalid' ); } ); + }, + validateForm: function( selector ) { + selector = jQuery( selector ); + return !wpAjax.invalidateForm( selector.find('.form-required').filter( function() { return jQuery('input:visible', this).val() === ''; } ) ).size(); + } +}, wpAjax || { noPerm: 'You do not have permission to do that.', broken: 'An unidentified error has occurred.' } ); + +// Basic form validation +jQuery(document).ready( function($){ + $('form.validate').submit( function() { return wpAjax.validateForm( $(this) ); } ); +}); diff --git a/wp-includes/js/wp-ajax-response.min.js b/wp-includes/js/wp-ajax-response.min.js new file mode 100644 index 0000000..32da06b --- /dev/null +++ b/wp-includes/js/wp-ajax-response.min.js @@ -0,0 +1 @@ +var wpAjax=jQuery.extend({unserialize:function(a){var b,c,d,e,f={};if(!a)return f;b=a.split("?"),b[1]&&(a=b[1]),c=a.split("&");for(d in c)(!jQuery.isFunction(c.hasOwnProperty)||c.hasOwnProperty(d))&&(e=c[d].split("="),f[e[0]]=e[1]);return f},parseAjaxResponse:function(a,b,c){var d={},e=jQuery("#"+b).html(""),f="";return a&&"object"==typeof a&&a.getElementsByTagName("wp_ajax")?(d.responses=[],d.errors=!1,jQuery("response",a).each(function(){var b,e=jQuery(this),g=jQuery(this.firstChild);b={action:e.attr("action"),what:g.get(0).nodeName,id:g.attr("id"),oldId:g.attr("old_id"),position:g.attr("position")},b.data=jQuery("response_data",g).text(),b.supplemental={},jQuery("supplemental",g).children().each(function(){b.supplemental[this.nodeName]=jQuery(this).text()}).size()||(b.supplemental=!1),b.errors=[],jQuery("wp_error",g).each(function(){var e,g,h,i=jQuery(this).attr("code");e={code:i,message:this.firstChild.nodeValue,data:!1},g=jQuery('wp_error_data[code="'+i+'"]',a),g&&(e.data=g.get()),h=jQuery("form-field",g).text(),h&&(i=h),c&&wpAjax.invalidateForm(jQuery("#"+c+' :input[name="'+i+'"]').parents(".form-field:first")),f+="

        "+e.message+"

        ",b.errors.push(e),d.errors=!0}).size()||(b.errors=!1),d.responses.push(b)}),f.length&&e.html('
        '+f+"
        "),d):isNaN(a)?!e.html('

        '+a+"

        "):(a=parseInt(a,10),-1==a?!e.html('

        '+wpAjax.noPerm+"

        "):0===a?!e.html('

        '+wpAjax.broken+"

        "):!0)},invalidateForm:function(a){return jQuery(a).addClass("form-invalid").find("input:visible").change(function(){jQuery(this).closest(".form-invalid").removeClass("form-invalid")})},validateForm:function(a){return a=jQuery(a),!wpAjax.invalidateForm(a.find(".form-required").filter(function(){return""===jQuery("input:visible",this).val()})).size()}},wpAjax||{noPerm:"You do not have permission to do that.",broken:"An unidentified error has occurred."});jQuery(document).ready(function(a){a("form.validate").submit(function(){return wpAjax.validateForm(a(this))})}); \ No newline at end of file diff --git a/wp-includes/js/wp-auth-check.js b/wp-includes/js/wp-auth-check.js new file mode 100644 index 0000000..e58a27d --- /dev/null +++ b/wp-includes/js/wp-auth-check.js @@ -0,0 +1,112 @@ +/* global adminpage */ +// Interim login dialog +(function($){ + var wrap, next; + + function show() { + var parent = $('#wp-auth-check'), + form = $('#wp-auth-check-form'), + noframe = wrap.find('.wp-auth-fallback-expired'), + frame, loaded = false; + + if ( form.length ) { + // Add unload confirmation to counter (frame-busting) JS redirects + $(window).on( 'beforeunload.wp-auth-check', function(e) { + e.originalEvent.returnValue = window.authcheckL10n.beforeunload; + }); + + frame = $('